import changes from `dev' branch of rmottola/Arctic-Fox:

- Bug 1257611 - Fix wrong CondVar::Wait() and Monitor::Wait() usage in netwerk/cache2, r=honzab (54f1a79f43)
- Bug 1239687 - Leak invalid/doomed file handles immediately after HTTP cache shutdown. r=michal (a20bcf662b)
- Bug 996836 - Merge CLOSE and WRITE priority in cache2 I/O thread. r=michal (8b0af50593)
- Bug 1265243 - Do not initialize CacheObserver in the child process, r=honzab (87f69529d2)
- Bug 1247432 - Don't do any unnecessary I/O in cache2 after shutdown. r=michal (7c5a2b89ac)
- Bug 1220272 - Fix signalling in HTTP cache test code suspender. r=michal (293a16731d)
- Bug 1160368 - Part 2: Clean up some cruft in nsCookie.h from PRBool/bool conversions. r=jdm (aa873f2dcc)
- Bug 1160368 - Part 5: Clean up some confusing browser_storage_listings.js checks. r=jdm (b27f5930a9)
- Bug 866380 - Null check for amlogic libc implementation. r=valentin (c3a487af9c)
- Bug 1257320 - Disable C4577 to unblock compilation on VS2015; r=mayhemer (2f800ca85c)
- Bug 1229726 - fix the data copy from sockaddr to NetAddr on OSX/FreeBSD. r=mcmanus. (fde11e004e)
- Bug 1241368 - provide JS implemented MDNS service as a fallback. r=nwgh,nalexander. (afe1445eb8)
- Bug 571126: Remove NECKO_SEPARATE_STACKS support, r=jduell (fb5b87bb3a)
- Bug 1057689 - Add xpcshell test checking correct notifications and app-offline state r=jduell (89946b44ba)
- Bug 1259459 - h2 0 length options puts end-stream on headers r=hurley (618480a609)
- Bug 1234251 - Remove HttpChannelChild::mSynthesizedResponseHead; r=jdm (20981affea)
- Bug 904559 - Veto redirect when target channel doesn't implement nsIParentRedirectingChannel. r=jduell (b3da2fae91)
- Bug 1261784 Make sure InterceptedChannel body streams are always closed. r=jdm (cd50b1a52b)
- Bug 1224508 - Use URI path without reference as the callback key. r=valentin. (b8a953f23a)
- Bug 1226760 - ViewSource doesn't work for packaged app resources r=mcmanus (ec9b0297de)
- Bug 1254061 - Rewrite nsHttp::ParseInt64 using strtoll r=mcmanus (2125b8ae6f)
- Bug 1241565 - nsIHttpChannelInternal should be a builtinclass, r=honzab.moz (d24da6a95a)
- Bug 1252386 - Removed debugging printf,r=me (c5d89f353c)
- bug 277813 - autogenerated expires needs max r=mayhemer (6aa7c255a5)
- Bug 1225384 - Change how the default resource "host names" are handled. r=michal (868732baab)
- Bug 719905: Fix resolution of resource: URLs containing : and / characters. r=valentin (f60f4baacf)
- Bug 1195173 - Convert ExtensionProtocolHandler to use channel->Open2() (r=maglione) (063f5d5d10)
- Bug 1226909 followup to fix bustage. Make sure that the channel returned by NS_NewChannel doesn't have a loadinfo that indicates that the channel has already been opened (42ebe0f44e)
- Bug 241698 - Fixed init and use of nsDirIndex::mLastModified (-1LL) + built in nullptr checks where they were missing. r=dragana (e9c2277a3d)
- Bug 1261318 - make sure brotli context is created in onstoprequest r=bagder (6646fed64d)
- Bug 1212223 - Update |bufLen| as well when we probed the multipart preamble. r=valentin. (ec878c5b0f)
- Bug 1259561: Increase CRAZY_COORD (threshold for debug build layout warnings) by an order of magnitude. r=mats (c05c16dd85)
- Bug 1261698. Make ReparentFrameViewTo return void because it always returns NS_OK. r=mats (e806d6abcb)
- Bug 1261698. Remove comment that is not relevant in nsContainerFrame.cpp. (f570189d15)
- Bug 1261698. Don't descend into child frames looking for views in ReparentFrameViewTo if the frame doesn't have the NS_FRAME_HAS_CHILD_WITH_VIEW bit set. r=mats (0d42befd46)
- Bug 1265154 - Fix compile error in MSVC 2013 caused by ArrayLength; r=heycam (1c962f2840)
- Bug 1260351 - Image: Enable ConvolveVertically/Horizontally in LS3 MMI. r=tnikkel (576b6bbdb3)
- Bug 1209780 (Part 1) - Mark DrawResult MOZ_MUST_USE. r=tn (e6c113bef2)
- Bug 1253753 - Remove unnecessary switch fallthrough to avoid -Wimplicit-fallthrough warning. r=karlt (96bd93fc5c)
- bug 1260178 null check pattern from -unico-border-gradient r=acomminos (f63c9c7ffb)
- Bug 1247796. Use keyboardFocusIndicatorColor for ActiveBorder system color keyword. r=mstange (7ed133de97)
- Bug 1248675 - Update the cached mBounds in nsChildView when its backing scale factor (display DPI) changes. r=mstange (8197274118)
- Bug 1256576 - Make sure texture is (re)initialized if the size changed. r=snorp (2c56790ca9)
- Bug 1242449 - Fix confusion among CSS, desktop and device pixel units in nsXULWindow position/size and window staggering so as to work consistently across mixed resolution displays. r=emk (f73d2fd41d)
- Bug 1255645 - Ensure nsXULWindow constrains the window to the bounds of its screen after applying intrinsic sizing (if appropriate), by re-doing positioning after the window has been sized properly. r=emk (e87e0cea81)
- Bug 1259492 - Ensure window position is constrained to the screen after it has been sized properly in nsXULWindow::OnChromeLoaded. r=emk (7cf599b39b)
- Bug 1832708 - Disable std::__throw_* wrapping on libc++ >= 14.0. r=firefox-build-system-reviewers,andi (29e0cc9319)
This commit is contained in:
2024-08-09 14:38:26 +08:00
parent 4b8580917e
commit fe3519e975
58 changed files with 1573 additions and 288 deletions
+1 -1
View File
@@ -477,7 +477,7 @@ HardwareKeyHandler::GetCurrentTarget()
return nullptr;
}
nsCOMPtr<mozIDOMWindowProxy> focusedWindow;
nsCOMPtr<nsIDOMWindow> domWindow;
fm->GetFocusedWindow(getter_AddRefs(focusedWindow));
if (NS_WARN_IF(!focusedWindow)) {
return nullptr;
+6 -4
View File
@@ -1,7 +1,9 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
let test_generator = do_run_test();
"use strict";
var test_generator = do_run_test();
function run_test()
{
@@ -31,10 +33,10 @@ function repeat_test()
}
// Purge threshold, in seconds.
let gPurgeAge = 1;
var gPurgeAge = 1;
// Short expiry age, in seconds.
let gShortExpiry = 2;
var gShortExpiry = 2;
// Required delay to ensure a purge occurs, in milliseconds. This must be at
// least gPurgeAge + 10%, and includes a little fuzz to account for timer
@@ -231,7 +233,7 @@ function get_creationTime(i)
function check_remaining_cookies(aNumberTotal, aNumberOld, aNumberToExpect) {
var enumerator = Services.cookiemgr.enumerator;
i = 0;
let i = 0;
while (enumerator.hasMoreElements()) {
var cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
++i;
+3 -2
View File
@@ -11,6 +11,7 @@
#include "gfxPrefs.h"
#include "image_operations.h"
#include "mozilla/SSE.h"
#include "mozilla/mips.h"
#include "convolver.h"
#include "skia/include/core/SkTypes.h"
@@ -228,7 +229,7 @@ Downscaler::CommitRow()
if (mCurrentInLine == inLineToRead) {
skia::ConvolveHorizontally(mRowBuffer.get(), *mXFilter,
mWindow[mLinesInBuffer++], mHasAlpha,
supports_sse2());
supports_sse2() || supports_mmi());
}
MOZ_ASSERT(mCurrentOutLine < mTargetSize.height,
@@ -316,7 +317,7 @@ Downscaler::DownscaleInputLine()
&mOutputBuffer[currentOutLine * mTargetSize.width * sizeof(uint32_t)];
skia::ConvolveVertically(static_cast<const FilterValue*>(filterValues),
filterLength, mWindow.get(), mXFilter->num_values(),
outputLine, mHasAlpha, supports_sse2());
outputLine, mHasAlpha, supports_sse2() || supports_mmi());
mCurrentOutLine += 1;
+3 -2
View File
@@ -23,6 +23,7 @@
#include "mozilla/Maybe.h"
#include "mozilla/SSE.h"
#include "mozilla/mips.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/gfx/2D.h"
#include "gfxPrefs.h"
@@ -236,7 +237,7 @@ public:
if (mInputRow == inputRowToRead) {
skia::ConvolveHorizontally(mRowBuffer.get(), *mXFilter,
mWindow[mRowsInWindow++], mHasAlpha,
supports_sse2());
supports_sse2() || supports_mmi());
}
MOZ_ASSERT(mOutputRow < mNext.InputSize().height,
@@ -311,7 +312,7 @@ private:
skia::ConvolveVertically(static_cast<const FilterValue*>(filterValues),
filterLength, mWindow.get(), mXFilter->num_values(),
reinterpret_cast<uint8_t*>(aRow), mHasAlpha,
supports_sse2());
supports_sse2() || supports_mmi());
return Some(WriteState::NEED_MORE_DATA);
});
+1 -1
View File
@@ -45,7 +45,7 @@ namespace image {
*
* BAD_ARGS: We failed to draw because bad arguments were passed to draw().
*/
enum class DrawResult : uint8_t
enum class MOZ_MUST_USE DrawResult : uint8_t
{
SUCCESS,
INCOMPLETE,
+5 -12
View File
@@ -372,22 +372,17 @@ nsContainerFrame::PeekOffsetCharacter(bool aForward, int32_t* aOffset,
/////////////////////////////////////////////////////////////////////////////
// Helper member functions
static nsresult
static void
ReparentFrameViewTo(nsIFrame* aFrame,
nsViewManager* aViewManager,
nsView* aNewParentView,
nsView* aOldParentView)
{
// XXX What to do about placeholder views for "position: fixed" elements?
// They should be reparented too.
// Does aFrame have a view?
if (aFrame->HasView()) {
#ifdef MOZ_XUL
if (aFrame->GetType() == nsGkAtoms::menuPopupFrame) {
// This view must be parented by the root view, don't reparent it.
return NS_OK;
return;
}
#endif
nsView* view = aFrame->GetView();
@@ -400,7 +395,7 @@ ReparentFrameViewTo(nsIFrame* aFrame,
// The view will remember the Z-order and other attributes that have been set on it.
nsView* insertBefore = nsLayoutUtils::FindSiblingViewFor(aNewParentView, aFrame);
aViewManager->InsertChild(aNewParentView, view, insertBefore, insertBefore != nullptr);
} else {
} else if (aFrame->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW) {
nsIFrame::ChildListIterator lists(aFrame);
for (; !lists.IsDone(); lists.Next()) {
// Iterate the child frames, and check each child frame to see if it has
@@ -412,8 +407,6 @@ ReparentFrameViewTo(nsIFrame* aFrame,
}
}
}
return NS_OK;
}
void
@@ -545,8 +538,8 @@ nsContainerFrame::ReparentFrameView(nsIFrame* aChildFrame,
// anything
if (oldParentView != newParentView) {
// They're not so we need to reparent any child views
return ReparentFrameViewTo(aChildFrame, oldParentView->GetViewManager(), newParentView,
oldParentView);
ReparentFrameViewTo(aChildFrame, oldParentView->GetViewManager(), newParentView,
oldParentView);
}
return NS_OK;
+4 -1
View File
@@ -34,7 +34,10 @@ class FramePropertyTable;
// dependency on nsDeviceContext.h. It doesn't matter if it's a
// little off.
#ifdef DEBUG
#define CRAZY_COORD (1000000*60)
// 10 million pixels, converted to app units. Note that this a bit larger
// than 1/4 of nscoord_MAX. So, if any content gets to be this large, we're
// definitely in danger of grazing up against nscoord_MAX; hence, it's CRAZY.
#define CRAZY_COORD (10000000*60)
#define CRAZY_SIZE(_x) (((_x) < -CRAZY_COORD) || ((_x) > CRAZY_COORD))
#endif
+2 -4
View File
@@ -910,12 +910,10 @@ KTableEntry nsCSSProps::kBackgroundClipKTable[] = {
{ eCSSKeyword_UNKNOWN, -1 }
};
#if 0
static_assert(ArrayLength(nsCSSProps::kImageLayerOriginKTable) ==
ArrayLength(nsCSSProps::kBackgroundClipKTable) - 1,
static_assert(MOZ_ARRAY_LENGTH(nsCSSProps::kImageLayerOriginKTable) ==
MOZ_ARRAY_LENGTH(nsCSSProps::kBackgroundClipKTable) - 1,
"background-clip has one extra value, which is text, compared"
"to {background,mask}-origin");
#endif
// Note: Don't change this table unless you update
// ParseImageLayerPosition!
+7
View File
@@ -8,6 +8,8 @@
#ifndef mozilla_throw_gcc_h
#define mozilla_throw_gcc_h
#if !defined(_LIBCPP_VERSION) || _LIBCPP_VERSION < 14000
#include "mozilla/Attributes.h"
#include <stdio.h> // snprintf
@@ -151,4 +153,9 @@ __throw_system_error(int err)
} // namespace std
#undef MOZ_THROW_NORETURN
#undef MOZ_THROW_INLINE
#endif
#endif // mozilla_throw_gcc_h
+3
View File
@@ -2217,6 +2217,9 @@ pref("network.stricttransportsecurity.enabled", true);
// Use the HSTS preload list by default
pref("network.stricttransportsecurity.preloadlist", true);
// Use JS mDNS as a fallback
pref("network.mdns.use_js_fallback", false);
pref("converter.html2txt.structs", true); // Output structured phrases (strong, em, code, sub, sup, b, i, u)
pref("converter.html2txt.header_strategy", 1); // 0 = no indention; 1 = indention, increased with header level; 2 = numbering and slight indention
// Whether we include ruby annotation in the text despite whether it
+44 -25
View File
@@ -532,9 +532,9 @@ CacheFileHandles::SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
class ShutdownEvent : public nsRunnable {
public:
ShutdownEvent(mozilla::Mutex *aLock, mozilla::CondVar *aCondVar)
: mLock(aLock)
, mCondVar(aCondVar)
ShutdownEvent()
: mMonitor("ShutdownEvent.mMonitor")
, mNotified(false)
, mPrepare(true)
{
MOZ_COUNT_CTOR(ShutdownEvent);
@@ -564,18 +564,35 @@ public:
return NS_OK;
}
MutexAutoLock lock(*mLock);
MonitorAutoLock mon(mMonitor);
CacheFileIOManager::gInstance->ShutdownInternal();
mCondVar->Notify();
mNotified = true;
mon.Notify();
return NS_OK;
}
void PostAndWait()
{
MonitorAutoLock mon(mMonitor);
DebugOnly<nsresult> rv;
nsCOMPtr<nsIEventTarget> ioTarget =
CacheFileIOManager::gInstance->mIOThread->Target();
MOZ_ASSERT(ioTarget);
rv = ioTarget->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
MOZ_ASSERT(NS_SUCCEEDED(rv));
while (!mNotified) {
mon.Wait();
}
}
protected:
mozilla::Mutex *mLock;
mozilla::CondVar *mCondVar;
bool mPrepare;
mozilla::Monitor mMonitor;
bool mNotified;
bool mPrepare;
};
class OpenFileEvent : public nsRunnable {
@@ -1157,23 +1174,14 @@ CacheFileIOManager::Shutdown()
return NS_ERROR_NOT_INITIALIZED;
}
gInstance->mShutdownDemanded = true;
CacheIndex::PreShutdown();
ShutdownMetadataWriteScheduling();
{
mozilla::Mutex lock("CacheFileIOManager::Shutdown() lock");
mozilla::CondVar condVar(lock, "CacheFileIOManager::Shutdown() condVar");
MutexAutoLock autoLock(lock);
RefPtr<ShutdownEvent> ev = new ShutdownEvent(&lock, &condVar);
DebugOnly<nsresult> rv;
nsCOMPtr<nsIEventTarget> ioTarget = gInstance->mIOThread->Target();
MOZ_ASSERT(ioTarget);
rv = ioTarget->Dispatch(ev, nsIEventTarget::DISPATCH_NORMAL);
MOZ_ASSERT(NS_SUCCEEDED(rv));
condVar.Wait();
}
RefPtr<ShutdownEvent> ev = new ShutdownEvent();
ev->PostAndWait();
MOZ_ASSERT(gInstance->mHandles.HandleCount() == 0);
MOZ_ASSERT(gInstance->mHandlesByLastUsed.Length() == 0);
@@ -2289,15 +2297,21 @@ CacheFileIOManager::ReleaseNSPRHandleInternal(CacheFileHandle *aHandle,
found = mHandlesByLastUsed.RemoveElement(aHandle);
MOZ_ASSERT(found);
if (aIgnoreShutdownLag || !IsPastShutdownIOLag()) {
PR_Close(aHandle->mFD);
} else {
// Leak invalid (w/o metadata) and doomed handles immediately after shutdown.
// Leak other handles when past the shutdown time maximum lag.
if (
#ifndef DEBUG
((aHandle->mInvalid || aHandle->mIsDoomed) && MOZ_UNLIKELY(mShutdownDemanded)) ||
#endif
MOZ_UNLIKELY(!aIgnoreShutdownLag && IsPastShutdownIOLag())) {
// Pretend this file has been validated (the metadata has been written)
// to prevent removal I/O on this apparently used file. The entry will
// never be used, since it doesn't have correct metadata, thus we don't
// need to worry about removing it.
aHandle->mInvalid = false;
LOG((" past the shutdown I/O lag, leaking file handle"));
} else {
PR_Close(aHandle->mFD);
}
aHandle->mFD = nullptr;
@@ -4033,13 +4047,16 @@ public:
}
mozilla::MonitorAutoLock mon(mMonitor);
mMonitorNotified = false;
nsresult rv = target->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
if (NS_FAILED(rv)) {
NS_ERROR("Dispatch failed, cannot do memory report of CacheFileHandles");
return 0;
}
mon.Wait();
while (!mMonitorNotified) {
mon.Wait();
}
return mSize;
}
@@ -4053,12 +4070,14 @@ public:
mSize += mSpecialHandles[i]->SizeOfIncludingThis(mMallocSizeOf);
}
mMonitorNotified = true;
mon.Notify();
return NS_OK;
}
private:
mozilla::Monitor mMonitor;
bool mMonitorNotified;
mozilla::MallocSizeOf mMallocSizeOf;
CacheFileHandles const &mHandles;
nsTArray<CacheFileHandle *> const &mSpecialHandles;
+4
View File
@@ -444,6 +444,10 @@ private:
// Shutdown time stamp, accessed only on the I/O thread. Used to bypass
// I/O after a certain time pass the shutdown has been demanded.
TimeStamp mShutdownDemandedTime;
// Set true on the main thread when cache shutdown is first demanded.
Atomic<bool, Relaxed> mShutdownDemanded;
// Set true on the IO thread, CLOSE level as part of the internal shutdown
// procedure.
bool mShuttingDown;
RefPtr<CacheIOThread> mIOThread;
nsCOMPtr<nsIFile> mCacheDirectory;
+2 -1
View File
@@ -157,8 +157,9 @@ already_AddRefed<nsIEventTarget> CacheIOThread::Target()
if (!target && mThread)
{
MonitorAutoLock lock(mMonitor);
if (!mXPCOMThread)
while (!mXPCOMThread) {
lock.Wait();
}
target = mXPCOMThread;
}
+1 -1
View File
@@ -36,7 +36,7 @@ public:
READ,
MANAGEMENT,
WRITE,
CLOSE,
CLOSE = WRITE,
INDEX,
EVICT,
LAST_LEVEL,
+12 -11
View File
@@ -13,6 +13,7 @@
#include "mozilla/Preferences.h"
#include "mozilla/TimeStamp.h"
#include "nsServiceManagerUtils.h"
#include "mozilla/net/NeckoCommon.h"
#include "prsystem.h"
#include <time.h>
#include <math.h>
@@ -98,6 +99,10 @@ NS_IMPL_ISUPPORTS(CacheObserver,
nsresult
CacheObserver::Init()
{
if (IsNeckoChild()) {
return NS_OK;
}
if (sSelf) {
return NS_OK;
}
@@ -446,18 +451,13 @@ CacheObserver::Observe(nsISupports* aSubject,
return NS_OK;
}
if (!strcmp(aTopic, "profile-before-change")) {
if (!strcmp(aTopic, "profile-change-net-teardown") ||
!strcmp(aTopic, "profile-before-change") ||
!strcmp(aTopic, "xpcom-shutdown")) {
RefPtr<CacheStorageService> service = CacheStorageService::Self();
if (service)
service->Shutdown();
return NS_OK;
}
if (!strcmp(aTopic, "xpcom-shutdown")) {
RefPtr<CacheStorageService> service = CacheStorageService::Self();
if (service)
if (service) {
service->Shutdown();
}
CacheFileIOManager::Shutdown();
return NS_OK;
@@ -465,8 +465,9 @@ CacheObserver::Observe(nsISupports* aSubject,
if (!strcmp(aTopic, "last-pb-context-exited")) {
RefPtr<CacheStorageService> service = CacheStorageService::Self();
if (service)
if (service) {
service->DropPrivateBrowsingEntries();
}
return NS_OK;
}
+4 -1
View File
@@ -2084,7 +2084,9 @@ NS_IMETHODIMP
CacheStorageService::IOThreadSuspender::Run()
{
MonitorAutoLock mon(mMon);
mon.Wait();
while (!mSignaled) {
mon.Wait();
}
return NS_OK;
}
@@ -2092,6 +2094,7 @@ void
CacheStorageService::IOThreadSuspender::Notify()
{
MonitorAutoLock mon(mMon);
mSignaled = true;
mon.Notify();
}
+2 -1
View File
@@ -366,13 +366,14 @@ private:
class IOThreadSuspender : public nsRunnable
{
public:
IOThreadSuspender() : mMon("IOThreadSuspender") { }
IOThreadSuspender() : mMon("IOThreadSuspender"), mSignaled(false) { }
void Notify();
private:
virtual ~IOThreadSuspender() { }
NS_IMETHOD Run() override;
Monitor mMon;
bool mSignaled;
};
RefPtr<IOThreadSuspender> mActiveIOSuspender;
+5 -4
View File
@@ -56,9 +56,9 @@ class nsCookie : public nsICookie2
, mExpiry(aExpiry)
, mLastAccessed(aLastAccessed)
, mCreationTime(aCreationTime)
, mIsSession(aIsSession != false)
, mIsSecure(aIsSecure != false)
, mIsHttpOnly(aIsHttpOnly != false)
, mIsSession(aIsSession)
, mIsSecure(aIsSecure)
, mIsHttpOnly(aIsHttpOnly)
, mOriginAttributes(aOriginAttributes)
{
}
@@ -101,7 +101,7 @@ class nsCookie : public nsICookie2
// setters
inline void SetExpiry(int64_t aExpiry) { mExpiry = aExpiry; }
inline void SetLastAccessed(int64_t aTime) { mLastAccessed = aTime; }
inline void SetIsSession(bool aIsSession) { mIsSession = (bool) aIsSession; }
inline void SetIsSession(bool aIsSession) { mIsSession = aIsSession; }
// Set the creation time manually, overriding the monotonicity checks in
// Create(). Use with caution!
inline void SetCreationTime(int64_t aTime) { mCreationTime = aTime; }
@@ -111,6 +111,7 @@ class nsCookie : public nsICookie2
protected:
virtual ~nsCookie() {}
private:
// member variables
// we use char* ptrs to store the strings in a contiguous block,
// so we save on the overhead of using nsCStrings. However, we
@@ -281,7 +281,11 @@ GetAddrInfoReplyRunnable::Reply(DNSServiceRef aSdRef,
}
NetAddr address;
memcpy(&address, aAddress, sizeof(*aAddress));
address.raw.family = aAddress->sa_family;
static_assert(sizeof(address.raw.data) >= sizeof(aAddress->sa_data),
"size of sockaddr.sa_data is too big");
memcpy(&address.raw.data, aAddress->sa_data, sizeof(aAddress->sa_data));
thread->Dispatch(new GetAddrInfoReplyRunnable(aSdRef,
aFlags,
@@ -0,0 +1,244 @@
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
this.EXPORTED_SYMBOLS = ["MulticastDNS"];
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/Messaging.jsm");
Cu.import("resource://gre/modules/Services.jsm");
var log = Cu.import("resource://gre/modules/AndroidLog.jsm", {}).AndroidLog.d.bind(null, "MulticastDNS");
const FAILURE_INTERNAL_ERROR = -65537;
// Helper function for sending commands to Java.
function send(type, data, callback) {
let msg = {
type: type
};
for (let i in data) {
try {
msg[i] = data[i];
} catch (e) {
}
}
Messaging.sendRequestForResult(msg)
.then(result => callback(result, null),
err => callback(null, typeof err === "number" ? err : FAILURE_INTERNAL_ERROR));
}
// Receives service found/lost event from NsdManager
function ServiceManager() {
}
ServiceManager.prototype = {
listeners: {},
numListeners: 0,
registerEvent: function() {
log("registerEvent");
Messaging.addListener(this.onServiceFound.bind(this), "NsdManager:ServiceFound");
Messaging.addListener(this.onServiceLost.bind(this), "NsdManager:ServiceLost");
},
unregisterEvent: function() {
log("unregisterEvent");
Messaging.removeListener("NsdManager:ServiceFound");
Messaging.removeListener("NsdManager:ServiceLost");
},
addListener: function(aServiceType, aListener) {
log("addListener: " + aServiceType + ", " + aListener);
if (!this.listeners[aServiceType]) {
this.listeners[aServiceType] = [];
}
if (this.listeners[aServiceType].includes(aListener)) {
log("listener already exists");
return;
}
this.listeners[aServiceType].push(aListener);
++this.numListeners;
if (this.numListeners === 1) {
this.registerEvent();
}
log("listener added: " + this);
},
removeListener: function(aServiceType, aListener) {
log("removeListener: " + aServiceType + ", " + aListener);
if (!this.listeners[aServiceType]) {
log("listener doesn't exist");
return;
}
let index = this.listeners[aServiceType].indexOf(aListener);
if (index < 0) {
log("listener doesn't exist");
return;
}
this.listeners[aServiceType].splice(index, 1);
--this.numListeners;
if (this.numListeners === 0) {
this.unregisterEvent();
}
log("listener removed" + this);
},
onServiceFound: function(aServiceInfo) {
let listeners = this.listeners[aServiceInfo.serviceType];
if (listeners) {
for (let listener of listeners) {
listener.onServiceFound(aServiceInfo);
}
} else {
log("no listener");
}
return {};
},
onServiceLost: function(aServiceInfo) {
let listeners = this.listeners[aServiceInfo.serviceType];
if (listeners) {
for (let listener of listeners) {
listener.onServiceLost(aServiceInfo);
}
} else {
log("no listener");
}
return {};
}
};
// make an object from nsIPropertyBag2
function parsePropertyBag2(bag) {
if (!bag || !(bag instanceof Ci.nsIPropertyBag2)) {
throw new TypeError("Not a property bag");
}
let attributes = [];
let enumerator = bag.enumerator;
while (enumerator.hasMoreElements()) {
let name = enumerator.getNext().QueryInterface(Ci.nsIProperty).name;
let value = bag.getPropertyAsACString(name);
attributes.push({
"name": name,
"value": value
});
}
return attributes;
}
function MulticastDNS() {
this.serviceManager = new ServiceManager();
}
MulticastDNS.prototype = {
startDiscovery: function(aServiceType, aListener) {
this.serviceManager.addListener(aServiceType, aListener);
let serviceInfo = {
serviceType: aServiceType,
uniqueId: aListener.uuid
};
send("NsdManager:DiscoverServices", serviceInfo, (result, err) => {
if (err) {
log("onStartDiscoveryFailed: " + aServiceType + " (" + err + ")");
this.serviceManager.removeListener(aServiceType, aListener);
aListener.onStartDiscoveryFailed(aServiceType, err);
} else {
aListener.onDiscoveryStarted(result);
}
});
},
stopDiscovery: function(aServiceType, aListener) {
this.serviceManager.removeListener(aServiceType, aListener);
let serviceInfo = {
uniqueId: aListener.uuid
};
send("NsdManager:StopServiceDiscovery", serviceInfo, (result, err) => {
if (err) {
log("onStopDiscoveryFailed: " + aServiceType + " (" + err + ")");
aListener.onStopDiscoveryFailed(aServiceType, err);
} else {
aListener.onDiscoveryStopped(aServiceType);
}
});
},
registerService: function(aServiceInfo, aListener) {
let serviceInfo = {
port: aServiceInfo.port,
serviceType: aServiceInfo.serviceType,
uniqueId: aListener.uuid
};
try {
serviceInfo.host = aServiceInfo.host;
} catch(e) {
// host unspecified
}
try {
serviceInfo.serviceName = aServiceInfo.serviceName;
} catch(e) {
// serviceName unspecified
}
try {
serviceInfo.attributes = parsePropertyBag2(aServiceInfo.attributes);
} catch(e) {
// attributes unspecified
}
send("NsdManager:RegisterService", serviceInfo, (result, err) => {
if (err) {
log("onRegistrationFailed: (" + err + ")");
aListener.onRegistrationFailed(aServiceInfo, err);
} else {
aListener.onServiceRegistered(result);
}
});
},
unregisterService: function(aServiceInfo, aListener) {
let serviceInfo = {
uniqueId: aListener.uuid
};
send("NsdManager:UnregisterService", serviceInfo, (result, err) => {
if (err) {
log("onUnregistrationFailed: (" + err + ")");
aListener.onUnregistrationFailed(aServiceInfo, err);
} else {
aListener.onServiceUnregistered(aServiceInfo);
}
});
},
resolveService: function(aServiceInfo, aListener) {
send("NsdManager:ResolveService", aServiceInfo, (result, err) => {
if (err) {
log("onResolveFailed: (" + err + ")");
aListener.onResolveFailed(aServiceInfo, err);
} else {
aListener.onServiceResolved(result);
}
});
}
};
@@ -0,0 +1,577 @@
/* -*- Mode: js; js-indent-level: 2; indent-tabs-mode: nil; tab-width: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
/* jshint esnext: true, moz: true */
'use strict';
this.EXPORTED_SYMBOLS = ['MulticastDNS'];
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
Cu.import('resource://gre/modules/Services.jsm');
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
const MDNS_PORT = 5353;
const MDNS_ADDRESS = '224.0.0.251';
const DNS_REC_TYPE_PTR = 12;
const DNS_REC_TYPE_TXT = 16;
const DNS_REC_TYPE_SRV = 33;
const DNS_REC_TYPE_A = 1;
const DNS_REC_TYPE_NSEC= 47;
const DNS_CLASS_QU = 0x8000;
const DNS_CLASS_IN = 0x0001;
const DNS_SECTION_QD = 'qd';
const DNS_SECTION_AN = 'an';
const DNS_SECTION_NS = 'ns';
const DNS_SECTION_AR = 'ar';
const DEBUG = false;
function debug(msg) {
Services.console.logStringMessage('MulticastDNSFallback: ' + msg);
}
/* The following was taken from https://raw.githubusercontent.com/GoogleChrome/chrome-app-samples/master/mdns-browser/dns.js */
/**
* DataWriter writes data to an ArrayBuffer, presenting it as the instance
* variable 'buffer'.
*
* @constructor
*/
let DataWriter = function(opt_size) {
let loc = 0;
let view = new Uint8Array(new ArrayBuffer(opt_size || 512));
this.byte_ = function(v) {
view[loc] = v;
++loc;
this.buffer = view.buffer.slice(0, loc);
}.bind(this);
};
DataWriter.prototype.byte = function(v) {
this.byte_(v);
return this;
};
DataWriter.prototype.short = function(v) {
return this.byte((v >> 8) & 0xff).byte(v & 0xff);
};
DataWriter.prototype.long = function(v) {
return this.short((v >> 16) & 0xffff).short(v & 0xffff);
};
/**
* Writes a DNS name. If opt_ref is specified, will finish this name with a
* suffix reference (i.e., 0xc0 <ref>). If not, then will terminate with a NULL
* byte.
*/
DataWriter.prototype.name = function(v, opt_ref) {
let parts = v.split('.');
parts.forEach(function(part) {
this.byte(part.length);
for (let i = 0; i < part.length; ++i) {
this.byte(part.charCodeAt(i));
}
}.bind(this));
if (opt_ref) {
this.byte(0xc0).byte(opt_ref);
} else {
this.byte(0);
}
return this;
};
/**
* DataConsumer consumes data from an ArrayBuffer.
*
* @constructor
*/
let DataConsumer = function(arg) {
if (arg instanceof Uint8Array) {
this.view_ = arg;
} else {
this.view_ = new Uint8Array(arg);
}
this.loc_ = 0;
};
/**
* @return whether this DataConsumer has consumed all its data
*/
DataConsumer.prototype.isEOF = function() {
return this.loc_ >= this.view_.byteLength;
};
/**
* @param length {integer} number of bytes to return from the front of the view
* @return a Uint8Array
*/
DataConsumer.prototype.slice = function(length) {
let view = this.view_.subarray(this.loc_, this.loc_ + length);
this.loc_ += length;
return view;
};
DataConsumer.prototype.byte = function() {
this.loc_ += 1;
return this.view_[this.loc_ - 1];
};
DataConsumer.prototype.short = function() {
return (this.byte() << 8) + this.byte();
};
DataConsumer.prototype.long = function() {
return (this.short() << 16) + this.short();
};
/**
* Consumes a DNS name, which will finish with a NULL byte.
*/
DataConsumer.prototype.name = function() {
let parts = [];
for (;;) {
let len = this.byte();
if (!len) {
break;
} else if (len == 0xc0) {
// concat suffix reference (i.e., 0xc0 <ref>).
let ref = this.byte();
let refConsumer = new DataConsumer(new Uint8Array(this.view_.buffer, ref));
parts.push(refConsumer.name());
break;
}
// Otherwise, consume a string!
let v = '';
while (len-- > 0) {
v += String.fromCharCode(this.byte());
}
parts.push(v);
}
return parts.join('.');
};
/**
* Consumes a string according to length.
*/
DataConsumer.prototype.string = function() {
let len = this.byte();
if (!len) {
return;
}
let v = '';
while (len-- > 0) {
v += String.fromCharCode(this.byte());
}
return v;
};
/**
* DNSPacket holds the state of a DNS packet. It can be modified or serialized
* in-place.
*
* @constructor
*/
let DNSPacket = function(opt_flags) {
this.flags_ = opt_flags || 0; /* uint16 */
this.data_ = {};
this.data_[DNS_SECTION_QD] = [];
this.data_[DNS_SECTION_AN] = [];
this.data_[DNS_SECTION_NS] = [];
this.data_[DNS_SECTION_AR] = [];
};
/**
* Parse a DNSPacket from an ArrayBuffer (or Uint8Array).
*/
DNSPacket.parse = function(buffer) {
let consumer = new DataConsumer(buffer);
if (consumer.short()) {
throw new Error('DNS packet must start with 00 00');
}
let flags = consumer.short();
let count = {};
count[DNS_SECTION_QD] = consumer.short();
count[DNS_SECTION_AN] = consumer.short();
count[DNS_SECTION_NS] = consumer.short();
count[DNS_SECTION_AR] = consumer.short();
let packet = new DNSPacket(flags);
// Parse the QUESTION section.
for (let i = 0; i < count[DNS_SECTION_QD]; ++i) {
let part = new DNSRecord(
consumer.name(),
consumer.short(), // type
consumer.short()); // class
packet.push(DNS_SECTION_QD, part);
}
// Parse the ANSWER, AUTHORITY and ADDITIONAL sections.
[DNS_SECTION_AN, DNS_SECTION_NS, DNS_SECTION_AR].forEach(function(section) {
for (let i = 0; i < count[section]; ++i) {
let part = new DNSRecord(
consumer.name(),
consumer.short(),
consumer.short(), // class
consumer.long(), // ttl
consumer.slice(consumer.short()));
packet.push(section, part);
}
});
if (consumer.isEOF()) {
DEBUG && debug('was not EOF on incoming packet');
}
return packet;
};
DNSPacket.prototype.push = function(section, record) {
this.data_[section].push(record);
};
DNSPacket.prototype.each = function(section) {
let filter = false;
let call;
if (arguments.length == 2) {
call = arguments[1];
} else {
filter = arguments[1];
call = arguments[2];
}
this.data_[section].forEach(function(rec) {
if (!filter || rec.type == filter) {
call(rec);
}
});
};
/**
* Serialize this DNSPacket into an ArrayBuffer for sending over UDP.
*/
DNSPacket.prototype.serialize = function() {
let out = new DataWriter();
let s = [DNS_SECTION_QD, DNS_SECTION_AN, DNS_SECTION_NS, DNS_SECTION_AR];
out.short(0).short(this.flags_);
s.forEach(function(section) {
out.short(this.data_[section].length);
}.bind(this));
s.forEach(function(section) {
this.data_[section].forEach(function(rec) {
out.name(rec.name).short(rec.type).short(rec.cl);
if (section != DNS_SECTION_QD) {
// TODO: implement .bytes()
throw new Error('can\'t yet serialize non-QD records');
// out.long(rec.ttl).bytes(rec.data_);
}
});
}.bind(this));
return out.buffer;
};
/**
* DNSRecord is a record inside a DNS packet; e.g. a QUESTION, or an ANSWER,
* AUTHORITY, or ADDITIONAL record. Note that QUESTION records are special,
* and do not have ttl or data.
*/
let DNSRecord = function(name, type, cl, opt_ttl, opt_data) {
this.name = name;
this.type = type;
this.cl = cl;
this.isQD = (arguments.length == 3);
if (!this.isQD) {
this.ttl = opt_ttl;
this.data_ = opt_data;
}
};
DNSRecord.prototype.asName = function() {
return new DataConsumer(this.data_).name();
};
DNSRecord.prototype.asSRV = function() {
if (this.type !== DNS_REC_TYPE_SRV) {
return null;
}
let consumer = new DataConsumer(this.data_);
let data_length = this.data_.length;
return {
priority: consumer.short(),
weight: consumer.short(),
port: consumer.short(),
target: consumer.name() + consumer.name(),
};
};
DNSRecord.prototype.asTXT = function() {
if (this.type !== DNS_REC_TYPE_TXT) {
return null;
}
let consumer = new DataConsumer(this.data_);
let attributes = [];
while(!consumer.isEOF()) {
attributes.push(consumer.string());
}
return attributes;
};
/* end https://raw.githubusercontent.com/GoogleChrome/chrome-app-samples/master/mdns-browser/dns.js */
/**
* Parse fully qualified domain name to service name, instance name,
* and domain name. See https://tools.ietf.org/html/rfc6763#section-7.
*
* example: The Server._http._tcp.example.com
* instance name = "The Server"
* service type = "_http._tcp"
* domain = "example.com"
* @private
*/
function _parseDomainName(str) {
let items = str.split('.');
let idx = items.findIndex(function(element) {
return element === '_tcp' || element === '_udp';
});
return {
instanceName: items.splice(0, idx - 1).join('.'),
serviceType: items.splice(0, 2). join('.'),
domainName: items.join('.')
};
}
function _createPropertyBag(map) {
let bag = Cc['@mozilla.org/hash-property-bag;1']
.createInstance(Ci.nsIWritablePropertyBag);
for (let entry of map.entries()) {
bag.setProperty(entry[0], entry[1]);
}
return bag;
}
let MulticastDNS = function() {
this._targets = new Map();
};
MulticastDNS.prototype = {
socket: null,
//public API
startDiscovery: function(aServiceType, aListener) {
DEBUG && debug('startDiscovery for ' + aServiceType);
let { serviceType } = _parseDomainName(aServiceType);
this._addServiceListener(serviceType, aListener);
try {
this._ensureSocket();
this._query(serviceType + '.local', DNS_REC_TYPE_PTR);
aListener.onDiscoveryStarted(serviceType);
} catch (e) {
DEBUG && debug('onStartDiscoveryFailed: ' + serviceType + ' (' + e + ')');
this._removeServiceListener(serviceType, aListener);
aListener.onStartDiscoveryFailed(serviceType, Cr.NS_ERROR_FAILURE);
}
},
stopDiscovery: function(aServiceType, aListener) {
DEBUG && debug('stopDiscovery for ' + aServiceType);
let { serviceType } = _parseDomainName(aServiceType);
this._removeServiceListener(serviceType, aListener);
aListener.onDiscoveryStopped(serviceType);
if (this._targets.size === 0) {
DEBUG && debug('close current socket');
this.socket.close();
delete this.socket;
}
},
registerService: function(aServiceInfo, aListener) {
DEBUG && debug('service registration is not supported');
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
},
unregisterService: function(aServiceInfo, aListener) {
DEBUG && debug('service registration is not supported');
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
},
resolveService: function(aServiceInfo, aListener) {
DEBUG && debug('address info is already resolve during discovery phase');
aListener.onServiceResolved(aServiceInfo);
},
//private API
onReceive: function(info) {
let packet = DNSPacket.parse(info.rawData);
let serviceRecords = {};
packet.each(DNS_SECTION_AR, DNS_REC_TYPE_SRV, (rec) => {
DEBUG && debug('recieve SRV: ' + rec.name);
let srv = rec.asSRV();
serviceRecords[rec.name] = {
port: srv.port,
host: srv.target,
};
});
packet.each(DNS_SECTION_AR, DNS_REC_TYPE_TXT, (rec) => {
DEBUG && debug('recieve TXT: ' + rec.name);
if (!serviceRecords[rec.name]) {
return;
}
let txt = rec.asTXT();
let attributes = new Map();
for(let x in txt) {
let idx = x.indexOf('=');
if (idx < 0) {
attributes.set(txt[x], true);
continue;
}
let key = txt[x].substring(0, idx);
let value = txt[x].substring(idx + 1);
attributes.set(key, value);
}
serviceRecords[rec.name].attributes = attributes;
});
packet.each(DNS_SECTION_AN, DNS_REC_TYPE_PTR, (rec) => {
DEBUG && debug('recieve PTR: ' + rec.name);
let { serviceType: answerType } = _parseDomainName(rec.name);
if (this._targets.has(answerType)) {
let name = rec.asName();
DEBUG && debug('>> for ' + name);
let {instanceName, serviceType, domainName} = _parseDomainName(name);
let serviceInfo = {
host: serviceRecords[name].host,
address: info.fromAddr.address,
port: serviceRecords[name].port,
serviceType: serviceType,
serviceName: instanceName,
domainName: domainName,
attributes: _createPropertyBag(serviceRecords[name].attributes),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceInfo]),
};
this._targets.get(serviceType).forEach(function(listener) {
listener.onServiceFound(serviceInfo);
});
}
});
},
/**
* Handles network error occured while waiting for data.
* @private
*/
onReceiveError: function(socket, status) {
DEBUG && debug('receiver socket error' + status);
return true;
},
/**
* Broadcasts for services on the given socket/address.
* @private
*/
_query: function(search, type) {
DEBUG && debug('query for service: ' + search);
let packet = new DNSPacket();
packet.push(DNS_SECTION_QD, new DNSRecord(search, type, DNS_CLASS_IN | DNS_CLASS_QU));
this._broadcast(this.socket, packet);
},
/**
* Broadcasts a MDNS packet on the given socket/address.
* @private
*/
_broadcast: function(sock, packet) {
let raw = new DataView(packet.serialize());
let length = raw.byteLength;
let buf = [];
for (let x = 0; x < length; x++) {
let charcode = raw.getUint8(x);
buf[x] = charcode;
}
sock.send(MDNS_ADDRESS, MDNS_PORT, buf, buf.length);
},
_addServiceListener: function(serviceType, listener) {
let listeners = this._targets.get(serviceType);
if (!listeners) {
listeners = [];
this._targets.set(serviceType, listeners);
}
if (!listeners.find((element) => {
return element === listener;
})) {
DEBUG && debug('insert new listener');
listeners.push(listener);
}
},
_removeServiceListener: function(serviceType, listener) {
if (!this._targets.has(serviceType)) {
DEBUG && debug('listener doesnt exist');
return;
}
let listeners = this._targets.get(serviceType);
let idx = listeners.findIndex(function(element) {
return element === listener;
});
if (idx >= 0) {
listeners.splice(idx, 1);
}
if (listeners.length === 0) {
this._targets.delete(serviceType);
}
},
_ensureSocket: function() {
if (this.socket) {
DEBUG && debug('reuse current socket');
return;
}
this.socket = Cc['@mozilla.org/network/udp-socket;1']
.createInstance(Ci.nsIUDPSocket);
let self = this;
this.socket.init(MDNS_PORT, false,
Services.scriptSecurityManager.getSystemPrincipal());
this.socket.asyncListen({
onPacketReceived: function(aSocket, aMessage) {
self.onReceive(aMessage);
},
onStopListening: function(aSocket, aStatus) {
self.onReceiveError(aSocket, aStatus);
},
});
this.socket.joinMulticast(MDNS_ADDRESS);
},
};
+5
View File
@@ -10,6 +10,11 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
'nsDNSServiceDiscovery.manifest',
]
EXTRA_JS_MODULES += [
'MulticastDNSAndroid.jsm',
'MulticastDNSFallback.jsm',
]
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa' or \
(CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' and CONFIG['ANDROID_VERSION'] >= '16'):
UNIFIED_SOURCES += [
@@ -5,9 +5,15 @@
const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
Cu.import("resource://gre/modules/MulticastDNS.jsm");
Cu.import('resource://gre/modules/Services.jsm');
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
if (Services.prefs.getBoolPref("network.mdns.use_js_fallback")) {
Cu.import("resource://gre/modules/MulticastDNSFallback.jsm");
} else {
Cu.import("resource://gre/modules/MulticastDNSAndroid.jsm");
}
const DNSSERVICEDISCOVERY_CID = Components.ID("{f9346d98-f27a-4e89-b744-493843416480}");
const DNSSERVICEDISCOVERY_CONTRACT_ID = "@mozilla.org/toolkit/components/mdnsresponder/dns-sd;1";
const DNSSERVICEINFO_CONTRACT_ID = "@mozilla.org/toolkit/components/mdnsresponder/dns-info;1";
@@ -135,7 +141,7 @@ nsDNSServiceDiscovery.prototype = {
this.stopDiscovery = true;
return;
}
this.mdns.stopDiscovery(aServiceType, this);
this.mdns.stopDiscovery(aServiceType, listener);
}).bind(listener)
};
},
@@ -153,7 +159,7 @@ nsDNSServiceDiscovery.prototype = {
this.stopRegistration = true;
return;
}
this.mdns.unregisterService(aServiceInfo, this);
this.mdns.unregisterService(aServiceInfo, listener);
}).bind(listener)
};
},
+7
View File
@@ -79,3 +79,10 @@ else:
UNIFIED_SOURCES += [
'nameprep.c',
]
if CONFIG['_MSC_VER']:
# This is intended as a temporary hack to support building with VS2015.
# icu\source\common\unicode/ucasemap.h(93): warning C4577:
# 'noexcept' used with no exception handling mode specified;
# termination on exception is not guaranteed. Specify /EHsc from unified dns
CXXFLAGS += ['-wd4577']
+2 -1
View File
@@ -406,7 +406,8 @@ HostDB_MatchEntry(const PLDHashEntryHdr *entry,
const nsHostDBEnt *he = static_cast<const nsHostDBEnt *>(entry);
const nsHostKey *hk = static_cast<const nsHostKey *>(key);
return !strcmp(he->rec->host, hk->host) &&
return !strcmp(he->rec->host ? he->rec->host : "",
hk->host ? hk->host : "") &&
RES_KEY_FLAGS (he->rec->flags) == RES_KEY_FLAGS(hk->flags) &&
he->rec->af == hk->af &&
!strcmp(he->rec->netInterface, hk->netInterface);
+1 -6
View File
@@ -90,13 +90,8 @@ IsNeckoChild()
static bool amChild = false;
if (!didCheck) {
// This allows independent necko-stacks (instead of single stack in chrome)
// to still be run.
// TODO: Remove eventually when no longer supported (bug 571126)
const char * e = PR_GetEnv("NECKO_SEPARATE_STACKS");
if (!e)
amChild = XRE_IsContentProcess();
didCheck = true;
amChild = (XRE_GetProcessType() == GeckoProcessType_Content);
}
return amChild;
}
+11
View File
@@ -1005,6 +1005,17 @@ NeckoParent::OfflineNotification(nsISupports *aSubject)
}
// XPCShells don't have any TabParents
// Just send the ipdl message to the child process.
if (!UsingNeckoIPCSecurity()) {
bool offline = false;
gIOService->IsAppOffline(targetAppId, &offline);
if (!SendAppOfflineStatus(targetAppId, offline)) {
printf_stderr("NeckoParent: "
"SendAppOfflineStatus failed for targetAppId: %u\n", targetAppId);
}
}
return NS_OK;
}
+1 -2
View File
@@ -564,8 +564,7 @@ Http2Stream::GenerateOpen()
firstFrameFlags |= Http2Session::kFlag_END_STREAM;
} else if (head->IsPost() ||
head->IsPut() ||
head->IsConnect() ||
head->IsOptions()) {
head->IsConnect()) {
// place fin in a data frame even for 0 length messages for iterop
} else if (!mRequestBodyLenRemaining) {
// for other HTTP extension methods, rely on the content-length
-1
View File
@@ -184,7 +184,6 @@ private:
nsCOMPtr<nsIChildChannel> mRedirectChannelChild;
RefPtr<InterceptStreamListener> mInterceptListener;
RefPtr<nsInputStreamPump> mSynthesizedResponsePump;
nsAutoPtr<nsHttpResponseHead> mSynthesizedResponseHead;
nsCOMPtr<nsIInputStream> mSynthesizedInput;
int64_t mSynthesizedStreamLength;
@@ -148,6 +148,14 @@ HttpChannelParentListener::AsyncOnChannelRedirect(
{
nsresult rv;
nsCOMPtr<nsIParentRedirectingChannel> activeRedirectingChannel =
do_QueryInterface(mNextListener);
if (!activeRedirectingChannel) {
NS_ERROR("Channel got a redirect response, but doesn't implement "
"nsIParentRedirectingChannel to handle it.");
return NS_ERROR_NOT_IMPLEMENTED;
}
// Register the new channel and obtain id for it
nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
do_GetService("@mozilla.org/redirectchannelregistrar;1", &rv);
@@ -158,13 +166,6 @@ HttpChannelParentListener::AsyncOnChannelRedirect(
LOG(("Registered %p channel under id=%d", newChannel, mRedirectChannelId));
nsCOMPtr<nsIParentRedirectingChannel> activeRedirectingChannel =
do_QueryInterface(mNextListener);
if (!activeRedirectingChannel) {
NS_RUNTIMEABORT("Channel got a redirect response, but doesn't implement "
"nsIParentRedirectingChannel to handle it.");
}
return activeRedirectingChannel->StartRedirect(mRedirectChannelId,
newChannel,
redirectFlags,
@@ -198,6 +198,9 @@ InterceptedChannelChrome::ResetInterception()
nsresult rv = mChannel->StartRedirectChannelToURI(uri, nsIChannelEventSink::REDIRECT_INTERNAL);
NS_ENSURE_SUCCESS(rv, rv);
mResponseBody->Close();
mResponseBody = nullptr;
mReleaseHandle = nullptr;
mChannel = nullptr;
return NS_OK;
@@ -230,6 +233,11 @@ InterceptedChannelChrome::FinishSynthesizedResponse(const nsACString& aFinalURLS
return NS_ERROR_NOT_AVAILABLE;
}
// Make sure the cache entry's output stream is always closed. If the
// channel was intercepted with a null-body response then its possible
// the synthesis completed without a stream copy operation.
mResponseBody->Close();
mReportCollector->FlushConsoleReports(mChannel);
EnsureSynthesizedResponse();
@@ -382,6 +390,7 @@ InterceptedChannelContent::ResetInterception()
mReportCollector->FlushConsoleReports(mChannel);
mResponseBody->Close();
mResponseBody = nullptr;
mSynthesizedInput = nullptr;
@@ -418,6 +427,11 @@ InterceptedChannelContent::FinishSynthesizedResponse(const nsACString& aFinalURL
return NS_ERROR_NOT_AVAILABLE;
}
// Make sure the body output stream is always closed. If the channel was
// intercepted with a null-body response then its possible the synthesis
// completed without a stream copy operation.
mResponseBody->Close();
mReportCollector->FlushConsoleReports(mChannel);
EnsureSynthesizedResponse();
+2 -2
View File
@@ -727,7 +727,7 @@ PackagedAppService::PackagedAppDownloader::AddCallback(nsIURI *aURI,
{
MOZ_RELEASE_ASSERT(NS_IsMainThread(), "mCallbacks hashtable is not thread safe");
nsAutoCString spec;
aURI->GetAsciiSpec(spec);
aURI->GetSpecIgnoringRef(spec);
LogURI("PackagedAppDownloader::AddCallback", this, aURI);
LOG(("[%p] > callback: %p\n", this, aCallback));
@@ -1037,7 +1037,7 @@ PackagedAppService::GetResource(nsIChannel *aChannel,
nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
nsCOMPtr<nsIURI> uri;
rv = principal->GetURI(getter_AddRefs(uri));
rv = aChannel->GetURI(getter_AddRefs(uri));
if (NS_WARN_IF(NS_FAILED(rv))) {
LOG(("[%p] > Error calling GetURI rv=%X\n", this, rv));
return rv;
+19 -12
View File
@@ -12,6 +12,7 @@
#include "mozilla/Mutex.h"
#include "mozilla/HashFunctions.h"
#include "nsCRT.h"
#include <errno.h>
namespace mozilla {
namespace net {
@@ -294,19 +295,25 @@ nsHttp::FindToken(const char *input, const char *token, const char *seps)
bool
nsHttp::ParseInt64(const char *input, const char **next, int64_t *r)
{
const char *start = input;
*r = 0;
while (*input >= '0' && *input <= '9') {
int64_t next = 10 * (*r) + (*input - '0');
if (next < *r) // overflow?
return false;
*r = next;
++input;
}
if (input == start) // nothing parsed?
MOZ_ASSERT(input);
MOZ_ASSERT(r);
char *end = nullptr;
errno = 0; // Clear errno to make sure its value is set by strtoll
int64_t value = strtoll(input, &end, /* base */ 10);
// Fail if: - the parsed number overflows.
// - the end points to the start of the input string.
// - we parsed a negative value. Consumers don't expect that.
if (errno != 0 || end == input || value < 0) {
LOG(("nsHttp::ParseInt64 value=%ld errno=%d", value, errno));
return false;
if (next)
*next = input;
}
if (next) {
*next = end;
}
*r = value;
return true;
}
@@ -824,9 +824,7 @@ nsHttpChannelAuthProvider::BlockPrompt()
nsCOMPtr<nsIHttpChannelInternal> chanInternal = do_QueryInterface(mAuthChannel);
MOZ_ASSERT(chanInternal);
bool skipAuthentication = false;
nsresult rv = chanInternal->GetBlockAuthPrompt(&skipAuthentication);
if (NS_SUCCEEDED(rv) && skipAuthentication) {
if (chanInternal->GetBlockAuthPrompt()) {
return true;
}
@@ -185,7 +185,6 @@ nsHttpConnectionMgr::Shutdown()
// wait for shutdown event to complete
while (!shutdownWrapper->mBool) {
fprintf(stderr, "nsHttpConnectionMgr::Shutdown() ProcessNextEvent\n");
NS_ProcessNextEvent(NS_GetCurrentThread());
}
+3 -1
View File
@@ -415,7 +415,7 @@ nsHttpResponseHead::ComputeCurrentAge(uint32_t now,
// <or>
// freshnessLifetime = expires_value - date_value
// <or>
// freshnessLifetime = (date_value - last_modified_value) * 0.10
// freshnessLifetime = min(one-week,(date_value - last_modified_value) * 0.10)
// <or>
// freshnessLifetime = 0
//
@@ -463,6 +463,8 @@ nsHttpResponseHead::ComputeFreshnessLifetime(uint32_t *result) const
if (date2 <= date) {
// this only makes sense if last-modified is actually in the past
*result = (date - date2) / 10;
const uint32_t kOneWeek = 60 * 60 * 24 * 7;
*result = std::min(kOneWeek, *result);
return NS_OK;
}
}
@@ -39,7 +39,7 @@ interface nsIHttpUpgradeListener : nsISupports
* using any feature exposed by this interface, be aware that this interface
* will change and you will be broken. You have been warned.
*/
[scriptable, uuid(01b8296a-e206-4e5f-acab-82bd8b6a900c)]
[builtinclass, scriptable, uuid(4e28263d-1e03-46f4-aa5c-9512f91957f9)]
interface nsIHttpChannelInternal : nsISupports
{
/**
@@ -283,5 +283,6 @@ interface nsIHttpChannelInternal : nsISupports
* authentication failure, that failure will be propagated to the channel
* listener. Must be called before opening the channel, otherwise throws.
*/
[infallible]
attribute boolean blockAuthPrompt;
};
@@ -18,6 +18,7 @@
#include "nsIStreamConverterService.h"
#include "nsIPipe.h"
#include "nsNetUtil.h"
#include "LoadInfo.h"
namespace mozilla {
@@ -103,8 +104,9 @@ ExtensionProtocolHandler::SubstituteChannel(nsIURI* aURI,
const char* kToType = "text/css";
nsCOMPtr<nsIInputStream> inputStream;
if (aLoadInfo && aLoadInfo->GetSecurityMode()) {
// Certain security checks require an async channel.
if (aLoadInfo &&
aLoadInfo->GetSecurityMode() == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) {
// If the channel needs to enforce CORS, we need to open the channel async.
nsCOMPtr<nsIOutputStream> outputStream;
rv = NS_NewPipe(getter_AddRefs(inputStream), getter_AddRefs(outputStream),
@@ -121,13 +123,20 @@ ExtensionProtocolHandler::SubstituteChannel(nsIURI* aURI,
aURI, getter_AddRefs(converter));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsILoadInfo> loadInfo =
static_cast<mozilla::LoadInfo*>(aLoadInfo)->CloneForNewRequest();
(*result)->SetLoadInfo(loadInfo);
rv = (*result)->AsyncOpen2(converter);
} else {
// Stylesheet loads for extension content scripts require a sync channel,
// but fortunately do not invoke security checks.
// Stylesheet loads for extension content scripts require a sync channel.
nsCOMPtr<nsIInputStream> sourceStream;
rv = (*result)->Open(getter_AddRefs(sourceStream));
if (aLoadInfo && aLoadInfo->GetEnforceSecurity()) {
rv = (*result)->Open2(getter_AddRefs(sourceStream));
} else {
rv = (*result)->Open(getter_AddRefs(sourceStream));
}
NS_ENSURE_SUCCESS(rv, rv);
rv = convService->Convert(sourceStream, kFromType, kToType,
@@ -352,28 +352,40 @@ SubstitutingProtocolHandler::ResolveURI(nsIURI *uri, nsACString &result)
return NS_OK;
}
// Unescape the path so we can perform some checks on it.
nsAutoCString unescapedPath(path);
NS_UnescapeURL(unescapedPath);
// Don't misinterpret the filepath as an absolute URI.
if (unescapedPath.FindChar(':') != -1)
return NS_ERROR_MALFORMED_URI;
if (unescapedPath.FindChar('\\') != -1)
return NS_ERROR_MALFORMED_URI;
const char *p = path.get() + 1; // path always starts with a slash
NS_ASSERTION(*(p-1) == '/', "Path did not begin with a slash!");
if (*p == '/')
return NS_ERROR_MALFORMED_URI;
nsCOMPtr<nsIURI> baseURI;
rv = GetSubstitution(host, getter_AddRefs(baseURI));
if (NS_FAILED(rv)) return rv;
rv = baseURI->Resolve(nsDependentCString(p, path.Length()-1), result);
// Unescape the path so we can perform some checks on it.
nsCOMPtr<nsIURL> url = do_QueryInterface(uri);
if (!url) {
return NS_ERROR_MALFORMED_URI;
}
nsAutoCString unescapedPath;
rv = url->GetFilePath(unescapedPath);
if (NS_FAILED(rv)) return rv;
NS_UnescapeURL(unescapedPath);
if (unescapedPath.FindChar('\\') != -1) {
return NS_ERROR_MALFORMED_URI;
}
// Some code relies on an empty path resolving to a file rather than a
// directory.
NS_ASSERTION(path.CharAt(0) == '/', "Path must begin with '/'");
if (path.Length() == 1) {
rv = baseURI->GetSpec(result);
} else {
// Make sure we always resolve the path as file-relative to our target URI.
path.InsertLiteral(".", 0);
rv = baseURI->Resolve(path, result);
}
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (MOZ_LOG_TEST(gResLog, LogLevel::Debug)) {
nsAutoCString spec;
+40 -43
View File
@@ -21,46 +21,27 @@ using mozilla::dom::ContentParent;
using mozilla::LogLevel;
using mozilla::Unused;
#define kAPP NS_LITERAL_CSTRING("app")
#define kGRE NS_LITERAL_CSTRING("gre")
#define kAPP "app"
#define kGRE "gre"
nsresult
nsResProtocolHandler::Init()
{
nsresult rv;
nsAutoCString appURI, greURI;
rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::APP, appURI);
rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::APP, mAppURI);
NS_ENSURE_SUCCESS(rv, rv);
rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::GRE, greURI);
rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::GRE, mGREURI);
NS_ENSURE_SUCCESS(rv, rv);
//
// make resource:/// point to the application directory or omnijar
//
nsCOMPtr<nsIURI> uri;
rv = NS_NewURI(getter_AddRefs(uri), appURI.Length() ? appURI : greURI);
NS_ENSURE_SUCCESS(rv, rv);
rv = SetSubstitution(EmptyCString(), uri);
NS_ENSURE_SUCCESS(rv, rv);
//
// make resource://app/ point to the application directory or omnijar
//
rv = SetSubstitution(kAPP, uri);
NS_ENSURE_SUCCESS(rv, rv);
//
// make resource://gre/ point to the GRE directory
//
if (appURI.Length()) { // We already have greURI in uri if appURI.Length() is 0.
rv = NS_NewURI(getter_AddRefs(uri), greURI);
NS_ENSURE_SUCCESS(rv, rv);
// mozilla::Omnijar::GetURIString always returns a string ending with /,
// and we want to remove it.
mGREURI.Truncate(mGREURI.Length() - 1);
if (mAppURI.Length()) {
mAppURI.Truncate(mAppURI.Length() - 1);
} else {
mAppURI = mGREURI;
}
rv = SetSubstitution(kGRE, uri);
NS_ENSURE_SUCCESS(rv, rv);
//XXXbsmedberg Neil wants a resource://pchrome/ for the profile chrome dir...
// but once I finish multiple chrome registration I'm not sure that it is needed
@@ -83,20 +64,36 @@ NS_IMPL_RELEASE_INHERITED(nsResProtocolHandler, SubstitutingProtocolHandler)
nsresult
nsResProtocolHandler::GetSubstitutionInternal(const nsACString& root, nsIURI **result)
{
// try invoking the directory service for "resource:root"
nsAutoCString uri;
nsAutoCString key;
key.AssignLiteral("resource:");
key.Append(root);
nsCOMPtr<nsIFile> file;
nsresult rv = NS_GetSpecialDirectory(key.get(), getter_AddRefs(file));
if (NS_FAILED(rv))
return NS_ERROR_NOT_AVAILABLE;
rv = IOService()->NewFileURI(file, result);
if (NS_FAILED(rv))
if (!ResolveSpecialCases(root, NS_LITERAL_CSTRING("/"), uri)) {
return NS_ERROR_NOT_AVAILABLE;
}
return NS_OK;
return NS_NewURI(result, uri);
}
bool
nsResProtocolHandler::ResolveSpecialCases(const nsACString& aHost,
const nsACString& aPath,
nsACString& aResult)
{
if (aHost.Equals("") || aHost.Equals(kAPP)) {
aResult.Assign(mAppURI);
} else if (aHost.Equals(kGRE)) {
aResult.Assign(mGREURI);
} else {
return false;
}
aResult.Append(aPath);
return true;
}
nsresult
nsResProtocolHandler::SetSubstitution(const nsACString& aRoot, nsIURI* aBaseURI)
{
MOZ_ASSERT(!aRoot.Equals(""));
MOZ_ASSERT(!aRoot.Equals(kAPP));
MOZ_ASSERT(!aRoot.Equals(kGRE));
return SubstitutingProtocolHandler::SetSubstitution(aRoot, aBaseURI);
}
+24 -1
View File
@@ -23,7 +23,6 @@ public:
NS_DECL_NSIRESPROTOCOLHANDLER
NS_FORWARD_NSIPROTOCOLHANDLER(mozilla::SubstitutingProtocolHandler::)
NS_FORWARD_NSISUBSTITUTINGPROTOCOLHANDLER(mozilla::SubstitutingProtocolHandler::)
nsResProtocolHandler()
: SubstitutingProtocolHandler("resource", URI_STD | URI_IS_UI_RESOURCE | URI_IS_LOCAL_RESOURCE,
@@ -32,9 +31,33 @@ public:
nsresult Init();
NS_IMETHOD SetSubstitution(const nsACString& aRoot, nsIURI* aBaseURI) override;
NS_IMETHOD GetSubstitution(const nsACString& aRoot, nsIURI** aResult) override
{
return mozilla::SubstitutingProtocolHandler::GetSubstitution(aRoot, aResult);
}
NS_IMETHOD HasSubstitution(const nsACString& aRoot, bool* aResult) override
{
return mozilla::SubstitutingProtocolHandler::HasSubstitution(aRoot, aResult);
}
NS_IMETHOD ResolveURI(nsIURI *aResURI, nsACString& aResult) override
{
return mozilla::SubstitutingProtocolHandler::ResolveURI(aResURI, aResult);
}
protected:
nsresult GetSubstitutionInternal(const nsACString& aRoot, nsIURI** aResult) override;
virtual ~nsResProtocolHandler() {}
bool ResolveSpecialCases(const nsACString& aHost, const nsACString& aPath,
nsACString& aResult) override;
private:
nsCString mAppURI;
nsCString mGREURI;
};
#endif /* nsResProtocolHandler_h___ */
+23 -16
View File
@@ -10,7 +10,8 @@ NS_IMPL_ISUPPORTS(nsDirIndex,
nsDirIndex::nsDirIndex() : mType(TYPE_UNKNOWN),
mSize(UINT64_MAX),
mLastModified(-1) {
mLastModified(-1LL)
{
}
nsDirIndex::~nsDirIndex() {}
@@ -18,9 +19,7 @@ nsDirIndex::~nsDirIndex() {}
NS_IMETHODIMP
nsDirIndex::GetType(uint32_t* aType)
{
if (!aType) {
return NS_ERROR_NULL_POINTER;
}
NS_ENSURE_ARG_POINTER(aType);
*aType = mType;
return NS_OK;
@@ -34,7 +33,10 @@ nsDirIndex::SetType(uint32_t aType)
}
NS_IMETHODIMP
nsDirIndex::GetContentType(char* *aContentType) {
nsDirIndex::GetContentType(char* *aContentType)
{
NS_ENSURE_ARG_POINTER(aContentType);
*aContentType = ToNewCString(mContentType);
if (!*aContentType)
return NS_ERROR_OUT_OF_MEMORY;
@@ -43,13 +45,17 @@ nsDirIndex::GetContentType(char* *aContentType) {
}
NS_IMETHODIMP
nsDirIndex::SetContentType(const char* aContentType) {
nsDirIndex::SetContentType(const char* aContentType)
{
mContentType = aContentType;
return NS_OK;
}
NS_IMETHODIMP
nsDirIndex::GetLocation(char* *aLocation) {
nsDirIndex::GetLocation(char* *aLocation)
{
NS_ENSURE_ARG_POINTER(aLocation);
*aLocation = ToNewCString(mLocation);
if (!*aLocation)
return NS_ERROR_OUT_OF_MEMORY;
@@ -58,13 +64,17 @@ nsDirIndex::GetLocation(char* *aLocation) {
}
NS_IMETHODIMP
nsDirIndex::SetLocation(const char* aLocation) {
nsDirIndex::SetLocation(const char* aLocation)
{
mLocation = aLocation;
return NS_OK;
}
NS_IMETHODIMP
nsDirIndex::GetDescription(char16_t* *aDescription) {
nsDirIndex::GetDescription(char16_t* *aDescription)
{
NS_ENSURE_ARG_POINTER(aDescription);
*aDescription = ToNewUnicode(mDescription);
if (!*aDescription)
return NS_ERROR_OUT_OF_MEMORY;
@@ -73,7 +83,8 @@ nsDirIndex::GetDescription(char16_t* *aDescription) {
}
NS_IMETHODIMP
nsDirIndex::SetDescription(const char16_t* aDescription) {
nsDirIndex::SetDescription(const char16_t* aDescription)
{
mDescription.Assign(aDescription);
return NS_OK;
}
@@ -81,9 +92,7 @@ nsDirIndex::SetDescription(const char16_t* aDescription) {
NS_IMETHODIMP
nsDirIndex::GetSize(int64_t* aSize)
{
if (!aSize) {
return NS_ERROR_NULL_POINTER;
}
NS_ENSURE_ARG_POINTER(aSize);
*aSize = mSize;
return NS_OK;
@@ -99,9 +108,7 @@ nsDirIndex::SetSize(int64_t aSize)
NS_IMETHODIMP
nsDirIndex::GetLastModified(PRTime* aLastModified)
{
if (!aLastModified) {
return NS_ERROR_NULL_POINTER;
}
NS_ENSURE_ARG_POINTER(aLastModified);
*aLastModified = mLastModified;
return NS_OK;
@@ -134,7 +134,7 @@ nsHTTPCompressConv::OnStopRequest(nsIRequest* request, nsISupports *aContext,
if (fpChannel && !isPending) {
fpChannel->ForcePending(true);
}
if (mBrotli && mBrotli->mTotalOut == 0 && !BrotliDecoderIsFinished(&mBrotli->mState)) {
if (mBrotli && (mBrotli->mTotalOut == 0) && !BrotliDecoderIsFinished(&mBrotli->mState)) {
status = NS_ERROR_INVALID_CONTENT_ENCODING;
}
if (fpChannel && !isPending) {
@@ -800,7 +800,7 @@ nsIndexedToHTML::OnIndexAvailable(nsIRequest *aRequest,
PRTime t;
aIndex->GetLastModified(&t);
if (t == -1) {
if (t == -1LL) {
pushBuffer.AppendLiteral("></td>\n <td>");
} else {
pushBuffer.AppendLiteral(" sortable-data=\"");
@@ -666,6 +666,10 @@ nsMultiMixedConv::OnDataAvailable(nsIRequest *request, nsISupports *context,
// Push the cursor to the token so that the while loop below will
// find token from the right position.
cursor = tokenPos;
// Update bufLen to exlude the preamble. Otherwise, the first
// |SendData| would claim longer buffer length.
bufLen -= mPreamble.Length();
}
} else {
// If the boundary was set in the header,
+95 -22
View File
@@ -1,41 +1,114 @@
/* verify that certain invalid URIs are not parsed by the resource
protocol handler */
Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://gre/modules/Services.jsm");
const specs = [
"resource:////",
"resource:///http://www.mozilla.org/",
"resource:///file:///",
"resource:///..\\",
"resource:///..\\..\\",
"resource:///..%5C",
"resource:///..%5c"
"resource://res-test//",
"resource://res-test/?foo=http:",
"resource://res-test/?foo=" + encodeURIComponent("http://example.com/"),
"resource://res-test/?foo=" + encodeURIComponent("x\\y"),
"resource://res-test/..%2F",
"resource://res-test/..%2f",
"resource://res-test/..%2F..",
"resource://res-test/..%2f..",
"resource://res-test/../../",
"resource://res-test/http://www.mozilla.org/",
"resource://res-test/file:///",
];
function check_for_exception(spec)
const error_specs = [
"resource://res-test/..\\",
"resource://res-test/..\\..\\",
"resource://res-test/..%5C",
"resource://res-test/..%5c",
];
// Create some fake principal that has not enough
// privileges to access any resource: uri.
var uri = NetUtil.newURI("http://www.example.com", null, null);
var principal = Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
function get_channel(spec)
{
var ios =
Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
var channelURI = NetUtil.newURI(spec, null, null);
var channel = NetUtil.newChannel({
uri: NetUtil.newURI(spec, null, null),
loadingPrincipal: principal,
securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER
});
try {
var channel = ios.newChannel2(spec,
null,
null,
null, // aLoadingNode
Services.scriptSecurityManager.getSystemPrincipal(),
null, // aTriggeringPrincipal
Ci.nsILoadInfo.SEC_NORMAL,
Ci.nsIContentPolicy.TYPE_OTHER);
channel.asyncOpen2(null);
ok(false, "asyncOpen2() of URI: " + spec + "should throw");
}
catch (e) {
return;
// make sure we get the right error code in the exception
// ERROR code for NS_ERROR_DOM_BAD_URI is 1012
equal(e.code, 1012);
}
do_throw("Successfully opened invalid URI: '" + spec + "'");
try {
channel.open2();
ok(false, "Open2() of uri: " + spec + "should throw");
}
catch (e) {
// make sure we get the right error code in the exception
// ERROR code for NS_ERROR_DOM_BAD_URI is 1012
equal(e.code, 1012);
}
return channel;
}
function check_safe_resolution(spec, rootURI)
{
do_print(`Testing URL "${spec}"`);
let channel = get_channel(spec);
ok(channel.name.startsWith(rootURI), `URL resolved safely to ${channel.name}`);
ok(!/%2f/i.test(channel.name), `URL contains no escaped / characters`);
}
function check_resolution_error(spec)
{
try {
get_channel(spec);
ok(false, "Expected an error");
} catch (e) {
equal(e.result, Components.results.NS_ERROR_MALFORMED_URI,
"Expected a malformed URI error");
}
}
function run_test() {
// resource:/// and resource://gre/ are resolved specially, so we need
// to create a temporary resource package to test the standard logic
// with.
let resProto = Cc['@mozilla.org/network/protocol;1?name=resource'].getService(Ci.nsIResProtocolHandler);
let rootFile = Services.dirsvc.get("GreD", Ci.nsIFile);
let rootURI = Services.io.newFileURI(rootFile);
resProto.setSubstitution("res-test", rootURI);
do_register_cleanup(() => {
resProto.setSubstitution("res-test", null);
});
let baseRoot = resProto.resolveURI(Services.io.newURI("resource:///", null, null));
let greRoot = resProto.resolveURI(Services.io.newURI("resource://gre/", null, null));
for (var spec of specs) {
check_for_exception(spec);
check_safe_resolution(spec, rootURI.spec);
check_safe_resolution(spec.replace("res-test", ""), baseRoot);
check_safe_resolution(spec.replace("res-test", "gre"), greRoot);
}
for (var spec of error_specs) {
check_resolution_error(spec);
}
}
@@ -4,7 +4,7 @@ This test exercises the CacheFileContextEvictor::WasEvicted API and code using i
- We store 10+10 (pinned and non-pinned) entries to the cache, wait for them being written.
- Then we purge the memory pools.
- Now the IO thread is suspended on the EVICT (8) level to prevent actual deletion of the files.
- Now the IO thread is suspended on the EVICT (7) level to prevent actual deletion of the files.
- Index is disabled.
- We do clear() of the cache, this creates the "ce_*" file and posts to the EVICT level
the eviction loop mechanics.
@@ -47,9 +47,9 @@ function run_test()
// (1), here we start
log_("first set of opens");
var i;
for (i = 0; i < kENTRYCOUNT; ++i) {
log_("first set of opens");
// Callbacks 1-20
mc.add();
@@ -72,7 +72,7 @@ function run_test()
log_("after purge");
// Prevent the I/O thread from evicting physically the data. We first want to re-open the entries.
// This deterministically emulates a slow hard drive.
testingInterface.suspendCacheIOThread(8);
testingInterface.suspendCacheIOThread(7);
log_("clearing");
// Now clear everything except pinned. Stores the "ce_*" file and schedules background eviction.
@@ -96,7 +96,9 @@ function run_test()
// an early check on CacheIOThread::YieldAndRerun() in that method.
// CacheFileIOManager::OpenFileInternal should now run and CacheFileContextEvictor::WasEvicted
// should be checked on.
log_("resuming");
testingInterface.resumeCacheIOThread();
log_("resumed");
mc.fired(); // Finishes this test
}
@@ -76,11 +76,17 @@ function contentHandler_type_missing(metadata, response)
response.bodyOutputStream.write(body, body.length);
}
function contentHandler_with_package_header(metadata, response)
function contentHandler_with_package_header(chunkSize, metadata, response)
{
response.setHeader("Content-Type", 'application/package');
var body = testData.packageHeader + testData.getData();
response.bodyOutputStream.write(body, body.length);
response.bodyOutputStream.write(body.substring(0,chunkSize), chunkSize);
response.processAsync();
do_timeout(5, function() {
response.bodyOutputStream.write(body.substring(chunkSize), body.length-chunkSize);
response.finish();
});
}
var testData = {
@@ -244,7 +250,7 @@ function test_multipart_content_type_other() {
chan.asyncOpen(conv, null);
}
function test_multipart_package_header() {
function test_multipart_package_header(aChunkSize) {
var streamConv = Cc["@mozilla.org/streamConverters;1"]
.getService(Ci.nsIStreamConverterService);
@@ -253,10 +259,29 @@ function test_multipart_package_header() {
new multipartListener(testData, false, true),
null);
var chan = make_channel(uri + "/multipart5");
var chan = make_channel(uri + "/multipart5_" + aChunkSize);
chan.asyncOpen(conv, null);
}
// Bug 1212223 - Test multipart with package header and different chunk size.
// Use explict function name to make the test case log more readable.
function test_multipart_package_header_50() {
return test_multipart_package_header(50);
}
function test_multipart_package_header_100() {
return test_multipart_package_header(100);
}
function test_multipart_package_header_150() {
return test_multipart_package_header(150);
}
function test_multipart_package_header_200() {
return test_multipart_package_header(200);
}
function run_test()
{
httpserver = new HttpServer();
@@ -264,7 +289,13 @@ function run_test()
httpserver.registerPathHandler("/multipart2", contentHandler_with_boundary);
httpserver.registerPathHandler("/multipart3", contentHandler_chunked_headers);
httpserver.registerPathHandler("/multipart4", contentHandler_type_missing);
httpserver.registerPathHandler("/multipart5", contentHandler_with_package_header);
// Bug 1212223 - Test multipart with package header and different chunk size.
httpserver.registerPathHandler("/multipart5_50", contentHandler_with_package_header.bind(null, 50));
httpserver.registerPathHandler("/multipart5_100", contentHandler_with_package_header.bind(null, 100));
httpserver.registerPathHandler("/multipart5_150", contentHandler_with_package_header.bind(null, 150));
httpserver.registerPathHandler("/multipart5_200", contentHandler_with_package_header.bind(null, 200));
httpserver.start(-1);
run_next_test();
@@ -274,4 +305,9 @@ add_test(test_multipart);
add_test(test_multipart_with_boundary);
add_test(test_multipart_chunked_headers);
add_test(test_multipart_content_type_other);
add_test(test_multipart_package_header);
// Bug 1212223 - Test multipart with package header and different chunk size.
add_test(test_multipart_package_header_50);
add_test(test_multipart_package_header_100);
add_test(test_multipart_package_header_150);
add_test(test_multipart_package_header_200);
@@ -247,6 +247,8 @@ function run_test()
add_test(test_worse_package_4);
add_test(test_worse_package_5);
add_test(test_request_has_ref);
// run tests
run_next_test();
}
@@ -334,6 +336,13 @@ function test_updated_package() {
new packagedResourceListener(testData.content[0].data.replace(/\.\.\./g, 'xxx')));
}
// This tests that requested URI with reference should still work.
function test_request_has_ref() {
packagePath = "/package";
let url = uri + packagePath + "!//index.html#Ref";
paservice.getResource(getChannelForURL(url), cacheListener);
}
// ----------------------------------------------------------------------------
// This listener checks that the requested resources are not returned
@@ -0,0 +1,43 @@
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
function is_app_offline(appId) {
let ioservice = Cc['@mozilla.org/network/io-service;1'].
getService(Ci.nsIIOService);
return ioservice.isAppOffline(appId);
}
var events_observed_no = 0;
// Holds the last observed app-offline event
var info = null;
function observer(aSubject, aTopic, aData) {
events_observed_no++;
info = aSubject.QueryInterface(Ci.nsIAppOfflineInfo);
dump("ChildObserver - subject: {" + aSubject.appId + ", " + aSubject.mode + "} ");
}
// Add observer for the app-offline notification
function run_test() {
Services.obs.addObserver(observer, "network:app-offline-status-changed", false);
}
// Chech that the app has the proper offline status
function check_status(appId, status)
{
do_check_eq(is_app_offline(appId), status == Ci.nsIAppOfflineInfo.OFFLINE);
}
// Check that the app has the proper offline status
// and that the correct notification has been received
function check_notification_and_status(appId, status) {
do_check_eq(info.appId, appId);
do_check_eq(info.mode, status);
do_check_eq(is_app_offline(appId), status == Ci.nsIAppOfflineInfo.OFFLINE);
}
// Remove the observer from the child process
function finished() {
Services.obs.removeObserver(observer, "network:app-offline-status-changed");
do_check_eq(events_observed_no, 2);
}
@@ -0,0 +1,102 @@
// Checks that app-offline notifications are received in both the parent
// and the child process, and that after receiving the notification
// isAppOffline returns the correct value
Cu.import("resource://testing-common/httpd.js");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
var test_index = 0;
var APP_ID = 42;
var events_observed_no = 0;
function set_app_offline(appId, offline) {
let ioservice = Cc['@mozilla.org/network/io-service;1'].
getService(Ci.nsIIOService);
ioservice.setAppOffline(appId, offline);
}
function is_app_offline(appId) {
let ioservice = Cc['@mozilla.org/network/io-service;1'].
getService(Ci.nsIIOService);
return ioservice.isAppOffline(appId);
}
// The expected offline status after running each function,
// and the next function that should be run.
// test0 test1 test2 test3 test4
let expected_offline = [ false, true, true, false, false];
let callbacks = [ test1, test2, test3, test4, finished];
function observer(aSubject, aTopic, aData) {
events_observed_no++;
let info = aSubject.QueryInterface(Ci.nsIAppOfflineInfo);
dump("ParentObserver - subject: {" + aSubject.appId + ", " + aSubject.mode + "} " +
"topic: " + aTopic + "\n");
// Check that the correct offline status is in place
do_check_eq(is_app_offline(APP_ID), expected_offline[test_index]);
// Execute the callback for the current test
do_execute_soon(callbacks[test_index]);
}
function run_test() {
Services.obs.addObserver(observer, "network:app-offline-status-changed", false);
test_index = 0;
do_check_eq(is_app_offline(APP_ID), expected_offline[test_index]) // The app should be online at first
run_test_in_child("child_app_offline_notifications.js", test0);
}
// Check that the app is online by default in the child
function test0() {
dump("parent: RUNNING: test0\n");
test_index = 0;
sendCommand('check_status('+APP_ID+','+Ci.nsIAppOfflineInfo.ONLINE+');\n', test1);
}
// Set the app OFFLINE
// Check that the notification is emmited in the parent process
// The observer function will execute test2 which does the check in the child
function test1() {
dump("parent: RUNNING: test1\n");
test_index = 1;
set_app_offline(APP_ID, Ci.nsIAppOfflineInfo.OFFLINE);
}
// Checks that child process sees the app OFFLINE
function test2() {
dump("parent: RUNNING: test2\n");
test_index = 2;
sendCommand('check_notification_and_status('+APP_ID+','+Ci.nsIAppOfflineInfo.OFFLINE+');\n', test3);
}
// Set the app ONLINE
// Chech that the notification is received in the parent
// The observer function will execute test3 and do the check in the child
function test3() {
dump("parent: RUNNING: test3\n");
test_index = 3;
set_app_offline(APP_ID, Ci.nsIAppOfflineInfo.ONLINE);
}
// Chech that the app is back online
function test4() {
dump("parent: RUNNING: test4\n");
test_index = 4;
sendCommand('check_notification_and_status('+APP_ID+','+Ci.nsIAppOfflineInfo.ONLINE+');\n', function() {
// Send command to unregister observer on the child
sendCommand('finished();\n', finished);
});
}
// Remove observer and end test
function finished() {
dump("parent: RUNNING: finished\n");
Services.obs.removeObserver(observer, "network:app-offline-status-changed");
do_check_eq(events_observed_no, 2);
do_test_finished();
}
+1
View File
@@ -92,3 +92,4 @@ skip-if = true
[test_reply_without_content_type_wrap.js]
[test_app_offline_http.js]
[test_getHost_wrap.js]
[test_app_offline_notifications.js]
+3
View File
@@ -991,6 +991,8 @@ nsChildView::BackingScaleFactorChanged()
}
mBackingScaleFactor = newScale;
NSRect frame = [mView frame];
mBounds = nsCocoaUtils::CocoaRectToGeckoRectDevPix(frame, newScale);
if (mWidgetListener && !mWidgetListener->GetXULWindow()) {
nsIPresShell* presShell = mWidgetListener->GetPresShell();
@@ -2968,6 +2970,7 @@ RectTextureImage::EndUpdate(bool aKeepSurface)
LayoutDeviceIntRegion updateRegion = mUpdateRegion;
if (mTextureSize != mBufferSize) {
mTextureSize = mBufferSize;
needInit = true;
}
if (needInit || !CanUploadSubtextures()) {
+2 -2
View File
@@ -169,7 +169,7 @@ nsLookAndFeel::NativeGetColor(ColorID aID, nscolor &aColor)
aColor = GetColorFromNSColor([NSColor gridColor]);
break;
case eColorID_activeborder:
aColor = NS_RGB(0x00,0x00,0x00);
aColor = GetColorFromNSColor([NSColor keyboardFocusIndicatorColor]);
break;
case eColorID_appworkspace:
aColor = NS_RGB(0xFF,0xFF,0xFF);
@@ -601,4 +601,4 @@ nsLookAndFeel::RefreshImpl()
mUseOverlayScrollbarsCached = false;
mAllowOverlayScrollbarsOverlapCached = false;
}
}
}
+1 -1
View File
@@ -226,7 +226,7 @@ NativeKeyBindings::GetInstance(NativeKeyBindingsType aType)
default:
// fallback to multiline editor case in release build
MOZ_ASSERT(false, "aType is invalid or not yet implemented");
MOZ_FALLTHROUGH_ASSERT("aType is invalid or not yet implemented");
case nsIWidget::NativeKeyBindingsForMultiLineEditor:
case nsIWidget::NativeKeyBindingsForRichTextEditor:
if (!sInstanceForMultiLineEditor) {
+2
View File
@@ -83,6 +83,8 @@ GetGradientColors(const GValue* aValue,
return false;
auto pattern = static_cast<cairo_pattern_t*>(g_value_get_boxed(aValue));
if (!pattern)
return false;
// Just picking the lightest and darkest colors as simple samples rather
// than trying to blend, which could get messy if there are many stops.
+108 -55
View File
@@ -1032,6 +1032,13 @@ void nsXULWindow::OnChromeLoaded()
ApplyChromeFlags();
SyncAttributesToWidget();
int32_t specWidth = -1, specHeight = -1;
bool gotSize = false;
if (!mIgnoreXULSize) {
gotSize = LoadSizeFromXUL(specWidth, specHeight);
}
bool positionSet = !mIgnoreXULPosition;
nsCOMPtr<nsIXULWindow> parentWindow(do_QueryReferent(mParentWindow));
#if defined(XP_UNIX) && !defined(XP_MACOSX)
@@ -1041,11 +1048,18 @@ void nsXULWindow::OnChromeLoaded()
if (!parentWindow)
positionSet = false;
#endif
if (positionSet)
positionSet = LoadPositionFromXUL();
if (positionSet) {
// We have to do this before sizing the window, because sizing depends
// on the resolution of the screen we're on. But positioning needs to
// know the size so that it can constrain to screen bounds.... as an
// initial guess here, we'll use the specified size (if any).
positionSet = LoadPositionFromXUL(specWidth, specHeight);
}
if (gotSize) {
SetSpecifiedSize(specWidth, specHeight);
}
if (!mIgnoreXULSize)
LoadSizeFromXUL();
if (mIntrinsicallySized) {
// (if LoadSizeFromXUL set the size, mIntrinsicallySized will be false)
nsCOMPtr<nsIContentViewer> cv;
@@ -1060,15 +1074,25 @@ void nsXULWindow::OnChromeLoaded()
int32_t width = 0, height = 0;
if (NS_SUCCEEDED(cv->GetContentSize(&width, &height))) {
treeOwner->SizeShellTo(docShellAsItem, width, height);
// Update specified size for the final LoadPositionFromXUL call.
specWidth = width;
specHeight = height;
}
}
}
}
// Now that we have set the window's final size, we can re-do its
// positioning so that it is properly constrained to the screen.
if (positionSet) {
LoadPositionFromXUL(specWidth, specHeight);
}
LoadMiscPersistentAttributesFromXUL();
if (mCenterAfterLoad && !positionSet)
if (mCenterAfterLoad && !positionSet) {
Center(parentWindow, parentWindow ? false : true, false);
}
if (mShowAfterLoad) {
SetVisibility(true);
@@ -1079,7 +1103,10 @@ void nsXULWindow::OnChromeLoaded()
mPersistentAttributesMask |= PAD_POSITION | PAD_SIZE | PAD_MISC;
}
bool nsXULWindow::LoadPositionFromXUL()
// If aSpecWidth and/or aSpecHeight are > 0, we will use these CSS px sizes
// to fit to the screen when staggering windows; if they're negative,
// we use the window's current size instead.
bool nsXULWindow::LoadPositionFromXUL(int32_t aSpecWidth, int32_t aSpecHeight)
{
bool gotPosition = false;
@@ -1102,11 +1129,16 @@ bool nsXULWindow::LoadPositionFromXUL()
// Convert to global display pixels for consistent window management across
// screens with diverse resolutions
double scale = mWindow->GetDesktopToDeviceScale().scale;
currX = NSToIntRound(currX / scale);
currY = NSToIntRound(currY / scale);
currWidth = NSToIntRound(currWidth / scale);
currHeight = NSToIntRound(currHeight / scale);
double devToDesktopScale = 1.0 / mWindow->GetDesktopToDeviceScale().scale;
currX = NSToIntRound(currX * devToDesktopScale);
currY = NSToIntRound(currY * devToDesktopScale);
// For size, use specified value if > 0, else current value
double devToCSSScale = 1.0 / mWindow->GetDefaultScale().scale;
int32_t cssWidth =
aSpecWidth > 0 ? aSpecWidth : NSToIntRound(currWidth * devToCSSScale);
int32_t cssHeight =
aSpecHeight > 0 ? aSpecHeight : NSToIntRound(currHeight * devToCSSScale);
// Obtain the position information from the <xul:window> element.
int32_t specX = currX;
@@ -1142,7 +1174,7 @@ bool nsXULWindow::LoadPositionFromXUL()
}
}
else {
StaggerPosition(specX, specY, currWidth, currHeight);
StaggerPosition(specX, specY, cssWidth, cssHeight);
}
}
mWindow->ConstrainPosition(false, &specX, &specY);
@@ -1153,76 +1185,81 @@ bool nsXULWindow::LoadPositionFromXUL()
return gotPosition;
}
bool nsXULWindow::LoadSizeFromXUL()
bool
nsXULWindow::LoadSizeFromXUL(int32_t& aSpecWidth, int32_t& aSpecHeight)
{
bool gotSize = false;
// if we're the hidden window, don't try to validate our size/position. We're
// special.
if (mIsHiddenWindow)
if (mIsHiddenWindow) {
return false;
}
nsCOMPtr<dom::Element> windowElement = GetWindowDOMElement();
NS_ENSURE_TRUE(windowElement, false);
int32_t currWidth = 0;
int32_t currHeight = 0;
nsresult errorCode;
int32_t temp;
NS_ASSERTION(mWindow, "we expected to have a window already");
GetSize(&currWidth, &currHeight);
double displayToDevPx =
mWindow ? mWindow->GetDesktopToDeviceScale().scale : 1.0;
double cssToDevPx = mWindow ? mWindow->GetDefaultScale().scale : 1.0;
currWidth = NSToIntRound(currWidth * displayToDevPx / cssToDevPx);
currHeight = NSToIntRound(currHeight * displayToDevPx / cssToDevPx);
// Obtain the position and sizing information from the <xul:window> element.
int32_t specWidth = currWidth;
int32_t specHeight = currHeight;
// Obtain the sizing information from the <xul:window> element.
aSpecWidth = 100;
aSpecHeight = 100;
nsAutoString sizeString;
windowElement->GetAttribute(WIDTH_ATTRIBUTE, sizeString);
temp = sizeString.ToInteger(&errorCode);
if (NS_SUCCEEDED(errorCode) && temp > 0) {
specWidth = std::max(temp, 100);
aSpecWidth = std::max(temp, 100);
gotSize = true;
}
windowElement->GetAttribute(HEIGHT_ATTRIBUTE, sizeString);
temp = sizeString.ToInteger(&errorCode);
if (NS_SUCCEEDED(errorCode) && temp > 0) {
specHeight = std::max(temp, 100);
aSpecHeight = std::max(temp, 100);
gotSize = true;
}
if (gotSize) {
// constrain to screen size
nsCOMPtr<nsIDOMWindow> domWindow;
GetWindowDOMWindow(getter_AddRefs(domWindow));
if (domWindow) {
nsCOMPtr<nsIDOMScreen> screen;
domWindow->GetScreen(getter_AddRefs(screen));
if (screen) {
int32_t screenWidth;
int32_t screenHeight;
screen->GetAvailWidth(&screenWidth); // CSS pixels
screen->GetAvailHeight(&screenHeight);
if (specWidth > screenWidth)
specWidth = screenWidth;
if (specHeight > screenHeight)
specHeight = screenHeight;
}
}
return gotSize;
}
mIntrinsicallySized = false;
if (specWidth != currWidth || specHeight != currHeight) {
SetSize(specWidth * cssToDevPx, specHeight * cssToDevPx, false);
void
nsXULWindow::SetSpecifiedSize(int32_t aSpecWidth, int32_t aSpecHeight)
{
// constrain to screen size
nsCOMPtr<nsIDOMWindow> domWindow;
GetWindowDOMWindow(getter_AddRefs(domWindow));
if (domWindow) {
nsCOMPtr<nsIDOMScreen> screen;
domWindow->GetScreen(getter_AddRefs(screen));
if (screen) {
int32_t screenWidth;
int32_t screenHeight;
screen->GetAvailWidth(&screenWidth); // CSS pixels
screen->GetAvailHeight(&screenHeight);
if (aSpecWidth > screenWidth) {
aSpecWidth = screenWidth;
}
if (aSpecHeight > screenHeight) {
aSpecHeight = screenHeight;
}
}
}
return gotSize;
NS_ASSERTION(mWindow, "we expected to have a window already");
int32_t currWidth = 0;
int32_t currHeight = 0;
GetSize(&currWidth, &currHeight); // returns device pixels
// convert specified values to device pixels, and resize if needed
double cssToDevPx = mWindow ? mWindow->GetDefaultScale().scale : 1.0;
aSpecWidth = NSToIntRound(aSpecWidth * cssToDevPx);
aSpecHeight = NSToIntRound(aSpecHeight * cssToDevPx);
mIntrinsicallySized = false;
if (aSpecWidth != currWidth || aSpecHeight != currHeight) {
SetSize(aSpecWidth, aSpecHeight, false);
}
}
/* Miscellaneous persistent attributes are attributes named in the
@@ -1308,12 +1345,17 @@ bool nsXULWindow::LoadMiscPersistentAttributesFromXUL()
This code does have a scary double loop -- it'll keep passing through
the entire list of open windows until it finds a non-collision. Doesn't
seem to be a problem, but it deserves watching.
The aRequested{X,Y} parameters here are in desktop pixels;
the aSpec{Width,Height} parameters are CSS pixel dimensions.
*/
void nsXULWindow::StaggerPosition(int32_t &aRequestedX, int32_t &aRequestedY,
int32_t aSpecWidth, int32_t aSpecHeight)
{
const int32_t kOffset = 22;
const uint32_t kSlop = 4;
// These "constants" will be converted from CSS to desktop pixels
// for the appropriate screen, assuming we find a screen to use...
// hence they're not actually declared const here.
int32_t kOffset = 22;
uint32_t kSlop = 4;
bool keepTrying;
int bouncedX = 0, // bounced off vertical edge of screen
@@ -1354,6 +1396,17 @@ void nsXULWindow::StaggerPosition(int32_t &aRequestedX, int32_t &aRequestedY,
&screenWidth, &screenHeight);
screenBottom = screenTop + screenHeight;
screenRight = screenLeft + screenWidth;
// Get the screen's scaling factors and convert staggering constants
// from CSS px to desktop pixel units
double desktopToDeviceScale = 1.0, cssToDeviceScale = 1.0;
ourScreen->GetContentsScaleFactor(&desktopToDeviceScale);
ourScreen->GetDefaultCSSScaleFactor(&cssToDeviceScale);
double cssToDesktopFactor = cssToDeviceScale / desktopToDeviceScale;
kOffset = NSToIntRound(kOffset * cssToDesktopFactor);
kSlop = NSToIntRound(kSlop * cssToDesktopFactor);
// Convert dimensions from CSS to desktop pixels
aSpecWidth = NSToIntRound(aSpecWidth * cssToDesktopFactor);
aSpecHeight = NSToIntRound(aSpecHeight * cssToDesktopFactor);
gotScreen = true;
}
}
@@ -1384,7 +1437,7 @@ void nsXULWindow::StaggerPosition(int32_t &aRequestedX, int32_t &aRequestedY,
nsCOMPtr<nsIBaseWindow> listBaseWindow(do_QueryInterface(supportsWindow));
listBaseWindow->GetPosition(&listX, &listY);
double scale;
if (NS_SUCCEEDED(listBaseWindow->GetUnscaledDevicePixelsPerCSSPixel(&scale))) {
if (NS_SUCCEEDED(listBaseWindow->GetDevicePixelsPerDesktopPixel(&scale))) {
listX = NSToIntRound(listX / scale);
listY = NSToIntRound(listY / scale);
}
+3 -2
View File
@@ -93,8 +93,9 @@ protected:
void OnChromeLoaded();
void StaggerPosition(int32_t &aRequestedX, int32_t &aRequestedY,
int32_t aSpecWidth, int32_t aSpecHeight);
bool LoadPositionFromXUL();
bool LoadSizeFromXUL();
bool LoadPositionFromXUL(int32_t aSpecWidth, int32_t aSpecHeight);
bool LoadSizeFromXUL(int32_t& aSpecWidth, int32_t& aSpecHeight);
void SetSpecifiedSize(int32_t aSpecWidth, int32_t aSpecHeight);
bool LoadMiscPersistentAttributesFromXUL();
void SyncAttributesToWidget();
NS_IMETHOD SavePersistentAttributes();