author | Carsten "Tomcat" Book <cbook@mozilla.com> |
Fri, 02 Dec 2016 09:23:52 +0100 | |
changeset 325059 | f65ad27efe839ce9df0283840a1a40b4bbc9ead0 |
parent 325031 | b1e2d1fe6b070b71661047f94129a1ccd0a7f48f (current diff) |
parent 325058 | 9ff9b1aa382fb49983785d9831b80100a36ecb71 (diff) |
child 325060 | c784f735d981bcb6eec019fd04eb3e66d413fdf3 |
child 325078 | 1451fb639925d5c59389aa5da22781a92bff2e69 |
child 325087 | 7156a06172f4f652d7bfc9240bb89891288aeabe |
push id | 31026 |
push user | cbook@mozilla.com |
push date | Fri, 02 Dec 2016 08:24:04 +0000 |
treeherder | mozilla-central@f65ad27efe83 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 53.0a1 |
first release with | nightly linux32
f65ad27efe83
/
53.0a1
/
20161202030204
/
files
nightly linux64
f65ad27efe83
/
53.0a1
/
20161202030204
/
files
nightly mac
f65ad27efe83
/
53.0a1
/
20161202030204
/
files
nightly win32
f65ad27efe83
/
53.0a1
/
20161202030204
/
files
nightly win64
f65ad27efe83
/
53.0a1
/
20161202030204
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
53.0a1
/
20161202030204
/
pushlog to previous
nightly linux64
53.0a1
/
20161202030204
/
pushlog to previous
nightly mac
53.0a1
/
20161202030204
/
pushlog to previous
nightly win32
53.0a1
/
20161202030204
/
pushlog to previous
nightly win64
53.0a1
/
20161202030204
/
pushlog to previous
|
addon-sdk/source/test/leak/test-leak-event-chrome.js | file | annotate | diff | comparison | revisions | |
testing/web-platform/meta/FileAPI/reading-data-section/FileReader-multiple-reads.html.ini | file | annotate | diff | comparison | revisions | |
testing/web-platform/meta/fetch/api/redirect/redirect-count.html.ini | file | annotate | diff | comparison | revisions |
--- a/addon-sdk/source/lib/sdk/event/chrome.js +++ b/addon-sdk/source/lib/sdk/event/chrome.js @@ -52,14 +52,14 @@ function observe(topic) { // observerChannel (since third argument is `true`). There for if it // will be GC-ed with all it's event listeners once no other references // will be held. addObserver(observerChannel, topic, true); // We need to remove any observer added once the add-on is unloaded; // otherwise we'll get a "dead object" exception. // See: https://bugzilla.mozilla.org/show_bug.cgi?id=1001833 - unload(() => removeObserver(observerChannel, topic), { weak: true }); + unload(() => removeObserver(observerChannel, topic)); return observerChannel; } exports.observe = observe;
--- a/addon-sdk/source/test/leak/jetpack-package.ini +++ b/addon-sdk/source/test/leak/jetpack-package.ini @@ -1,8 +1,7 @@ [DEFAULT] support-files = leak-utils.js [test-leak-window-events.js] [test-leak-event-dom-closed-window.js] [test-leak-tab-events.js] -[test-leak-event-chrome.js]
--- a/addon-sdk/source/test/leak/leak-utils.js +++ b/addon-sdk/source/test/leak/leak-utils.js @@ -26,17 +26,16 @@ function gc() { resolve(); } } } Cu.schedulePreciseGC(genGCCallback()); }); } -exports.gc = gc; // Execute the given test function and verify that we did not leak windows // in the process. The test function must return a promise or be a generator. // If the promise is resolved, or generator completes, with an sdk loader // object then it will be unloaded after the memory measurements. exports.asyncWindowLeakTest = function*(assert, asyncTestFunc) { // SelfSupportBackend periodically tries to open windows. This can
deleted file mode 100644 --- a/addon-sdk/source/test/leak/test-leak-event-chrome.js +++ /dev/null @@ -1,41 +0,0 @@ -/* 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'; - -const { gc } = require("./leak-utils"); -const { Loader } = require("sdk/test/loader"); -const { Cu } = require("chrome"); -const { Services } = Cu.import("resource://gre/modules/Services.jsm", {}); - -exports["test sdk/event/chrome does not leak when not referenced"] = function*(assert) { - let loader = Loader(module); - let { observe } = loader.require("sdk/event/chrome"); - let { on } = loader.require("sdk/event/core"); - - let gotFooEvent = false; - on(observe("test-foo"), "data", function(evt) { - gotFooEvent = true; - }); - - let bar = observe("test-bar"); - let barPromise = new Promise(resolve => { - on(bar, "data", function(evt) { - assert.ok(!gotFooEvent, "should not have gotten test-foo event"); - resolve(); - }); - }); - - // This should clear the test-foo observer channel because we are not - // holding a reference to it above. - yield gc(); - - Services.obs.notifyObservers(null, "test-foo", null); - Services.obs.notifyObservers(null, "test-bar", null); - - yield barPromise; - - loader.unload(); -} - -require("sdk/test").run(exports);
--- a/browser/base/content/test/general/browser_web_channel.js +++ b/browser/base/content/test/general/browser_web_channel.js @@ -32,16 +32,36 @@ var gTests = [ resolve(); }); tab = gBrowser.addTab(HTTP_PATH + HTTP_ENDPOINT + "?generic"); }); } }, { + desc: "WebChannel generic message in a private window.", + run: function* () { + let promiseTestDone = new Promise(function(resolve, reject) { + let channel = new WebChannel("generic", Services.io.newURI(HTTP_PATH, null, null)); + channel.listen(function(id, message, target) { + is(id, "generic"); + is(message.something.nested, "hello"); + channel.stopListening(); + resolve(); + }); + }); + + const url = HTTP_PATH + HTTP_ENDPOINT + "?generic"; + let privateWindow = yield BrowserTestUtils.openNewBrowserWindow({private: true}); + yield BrowserTestUtils.openNewForegroundTab(privateWindow.gBrowser, url); + yield promiseTestDone; + yield BrowserTestUtils.closeWindow(privateWindow); + } + }, + { desc: "WebChannel two way communication", run: function* () { return new Promise(function(resolve, reject) { let tab; let channel = new WebChannel("twoway", Services.io.newURI(HTTP_PATH, null, null)); channel.listen(function(id, message, sender) { is(id, "twoway", "bad id");
--- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -5066,19 +5066,20 @@ nsDocument::UnblockDOMContentLoaded() MOZ_ASSERT(mBlockDOMContentLoaded); if (--mBlockDOMContentLoaded != 0 || mDidFireDOMContentLoaded) { return; } mDidFireDOMContentLoaded = true; MOZ_ASSERT(mReadyState == READYSTATE_INTERACTIVE); if (!mSynchronousDOMContentLoaded) { + MOZ_RELEASE_ASSERT(NS_IsMainThread()); nsCOMPtr<nsIRunnable> ev = NewRunnableMethod(this, &nsDocument::DispatchContentLoadedEvents); - NS_DispatchToCurrentThread(ev); + Dispatch("DispatchContentLoadedEvents", TaskCategory::Other, ev.forget()); } else { DispatchContentLoadedEvents(); } } void nsDocument::ContentStateChanged(nsIContent* aContent, EventStates aStateMask) { @@ -11839,17 +11840,17 @@ nsDocument::GetVisibilityState() const return dom::VisibilityState::Visible; } /* virtual */ void nsDocument::PostVisibilityUpdateEvent() { nsCOMPtr<nsIRunnable> event = NewRunnableMethod(this, &nsDocument::UpdateVisibilityState); - NS_DispatchToMainThread(event); + Dispatch("UpdateVisibility", TaskCategory::Other, event.forget()); } void nsDocument::MaybeActiveMediaComponents() { if (mEverInForeground) { return; }
--- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -8667,17 +8667,17 @@ nsGlobalWindow::PostMessageMozOuter(JSCo JS::Rooted<JS::Value> message(aCx, aMessage); JS::Rooted<JS::Value> transfer(aCx, aTransfer); event->Write(aCx, message, transfer, JS::CloneDataPolicy(), aError); if (NS_WARN_IF(aError.Failed())) { return; } - aError = NS_DispatchToCurrentThread(event); + aError = Dispatch("PostMessage", TaskCategory::Other, event.forget()); } void nsGlobalWindow::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage, const nsAString& aTargetOrigin, JS::Handle<JS::Value> aTransfer, nsIPrincipal& aSubjectPrincipal, ErrorResult& aError)
--- a/dom/base/nsScriptLoader.cpp +++ b/dom/base/nsScriptLoader.cpp @@ -12,18 +12,19 @@ #include "prsystem.h" #include "jsapi.h" #include "jsfriendapi.h" #include "xpcpublic.h" #include "nsCycleCollectionParticipant.h" #include "nsIContent.h" #include "nsJSUtils.h" +#include "mozilla/dom/DocGroup.h" +#include "mozilla/dom/Element.h" #include "mozilla/dom/ScriptSettings.h" -#include "mozilla/dom/Element.h" #include "mozilla/dom/SRILogHelper.h" #include "nsGkAtoms.h" #include "nsNetUtil.h" #include "nsIScriptGlobalObject.h" #include "nsIScriptContext.h" #include "nsIScriptSecurityManager.h" #include "nsIPrincipal.h" #include "nsJSPrincipals.h" @@ -1713,31 +1714,43 @@ nsScriptLoader::ProcessScriptElement(nsI } namespace { class NotifyOffThreadScriptLoadCompletedRunnable : public Runnable { RefPtr<nsScriptLoadRequest> mRequest; RefPtr<nsScriptLoader> mLoader; + RefPtr<DocGroup> mDocGroup; void *mToken; public: NotifyOffThreadScriptLoadCompletedRunnable(nsScriptLoadRequest* aRequest, nsScriptLoader* aLoader) - : mRequest(aRequest), mLoader(aLoader), mToken(nullptr) - {} + : mRequest(aRequest) + , mLoader(aLoader) + , mDocGroup(aLoader->GetDocGroup()) + , mToken(nullptr) + { + MOZ_ASSERT(NS_IsMainThread()); + } virtual ~NotifyOffThreadScriptLoadCompletedRunnable(); void SetToken(void* aToken) { MOZ_ASSERT(aToken && !mToken); mToken = aToken; } + static void Dispatch(already_AddRefed<NotifyOffThreadScriptLoadCompletedRunnable>&& aSelf) { + RefPtr<NotifyOffThreadScriptLoadCompletedRunnable> self = aSelf; + RefPtr<DocGroup> docGroup = self->mDocGroup; + docGroup->Dispatch("OffThreadScriptLoader", TaskCategory::Other, self.forget()); + } + NS_DECL_NSIRUNNABLE }; } /* anonymous namespace */ nsresult nsScriptLoader::ProcessOffThreadRequest(nsScriptLoadRequest* aRequest) { @@ -1805,17 +1818,17 @@ NotifyOffThreadScriptLoadCompletedRunnab } static void OffThreadScriptLoaderCallback(void *aToken, void *aCallbackData) { RefPtr<NotifyOffThreadScriptLoadCompletedRunnable> aRunnable = dont_AddRef(static_cast<NotifyOffThreadScriptLoadCompletedRunnable*>(aCallbackData)); aRunnable->SetToken(aToken); - NS_DispatchToMainThread(aRunnable); + NotifyOffThreadScriptLoadCompletedRunnable::Dispatch(aRunnable.forget()); } nsresult nsScriptLoader::AttemptAsyncScriptCompile(nsScriptLoadRequest* aRequest) { MOZ_ASSERT_IF(!aRequest->IsModuleRequest(), aRequest->IsReadyToRun()); MOZ_ASSERT(!aRequest->mWasCompiledOMT); @@ -2204,18 +2217,23 @@ void nsScriptLoader::ProcessPendingRequestsAsync() { if (mParserBlockingRequest || !mXSLTRequests.isEmpty() || !mLoadedAsyncRequests.isEmpty() || !mNonAsyncExternalScriptInsertedRequests.isEmpty() || !mDeferRequests.isEmpty() || !mPendingChildLoaders.IsEmpty()) { - NS_DispatchToCurrentThread(NewRunnableMethod(this, - &nsScriptLoader::ProcessPendingRequests)); + nsCOMPtr<nsIRunnable> task = NewRunnableMethod(this, + &nsScriptLoader::ProcessPendingRequests); + if (mDocument) { + mDocument->Dispatch("ScriptLoader", TaskCategory::Other, task.forget()); + } else { + NS_DispatchToCurrentThread(task.forget()); + } } } void nsScriptLoader::ProcessPendingRequests() { RefPtr<nsScriptLoadRequest> request;
--- a/dom/base/nsScriptLoader.h +++ b/dom/base/nsScriptLoader.h @@ -464,16 +464,21 @@ public: * off thread. */ nsresult ProcessOffThreadRequest(nsScriptLoadRequest *aRequest); bool AddPendingChildLoader(nsScriptLoader* aChild) { return mPendingChildLoaders.AppendElement(aChild) != nullptr; } + mozilla::dom::DocGroup* GetDocGroup() const + { + return mDocument->GetDocGroup(); + } + private: virtual ~nsScriptLoader(); nsScriptLoadRequest* CreateLoadRequest( nsScriptKind aKind, nsIScriptElement* aElement, uint32_t aVersion, mozilla::CORSMode aCORSMode,
--- a/dom/file/FileReader.cpp +++ b/dom/file/FileReader.cpp @@ -344,31 +344,28 @@ FileReader::DoReadData(uint64_t aCount) // Helper methods void FileReader::ReadFileContent(Blob& aBlob, const nsAString &aCharset, eDataFormat aDataFormat, ErrorResult& aRv) { - //Implicit abort to clear any other activity going on - ErrorResult error; - Abort(error); - error.SuppressException(); - if (mReadyState == LOADING) { - // A nested ReadAsSomething() as been called during one of the events - // dispatched by Abort(). We have to terminate this operation in order to - // continue the nested one. - aRv.Throw(NS_ERROR_ABORT); + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return; } mError = nullptr; + SetDOMStringToNull(mResult); + mResultArrayBuffer = nullptr; + + mAsyncStream = nullptr; + mTransferred = 0; mTotal = 0; mReadyState = EMPTY; FreeFileData(); mBlob = &aBlob; mDataFormat = aDataFormat; CopyUTF16toUTF8(aCharset, mCharset);
--- a/dom/file/tests/test_fileapi.html +++ b/dom/file/tests/test_fileapi.html @@ -351,28 +351,36 @@ function onFilesOpened(message) { r = new FileReader(); r.onabort = function (event) { is(reuseAbortHasRun, false, "abort should only fire once"); reuseAbortHasRun = true; is(event.target.readyState, FileReader.DONE, "should be DONE while firing onabort"); is(event.target.error.name, "AbortError", "error set to AbortError for aborted reads"); is(event.target.result, null, "file data should be null on aborted reads"); } - r.onload = function() { ok(false, "load should not fire for aborted reads") }; + r.onload = function() { ok(false, "load should fire for nested reads"); }; + var abortThrew = false; try { r.abort(); } catch(e) { abortThrew = true; } is(abortThrew, true, "abort() must throw if not loading"); is(reuseAbortHasRun, false, "abort() is a no-op unless loading"); r.readAsText(asciiFile); - r.readAsText(asciiFile); - is(reuseAbortHasRun, true, "abort should fire sync"); + + var readThrew = false; + try { + r.readAsText(asciiFile); + } catch(e) { + readThrew = true; + } + is(readThrew, true, "readAsText() must throw if loading"); + is(reuseAbortHasRun, false, "abort should not fire"); r.onload = getLoadHandler(testASCIIData, testASCIIData.length, "reuse-as-abort reading"); expectedTestCount++; // Test reading from nonexistent files r = new FileReader(); var didThrow = false; r.onerror = function (event) {
--- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -5653,17 +5653,19 @@ nsresult HTMLMediaElement::DispatchAsync // Save events that occur while in the bfcache. These will be dispatched // if the page comes out of the bfcache. if (mEventDeliveryPaused) { mPendingEvents.AppendElement(aName); return NS_OK; } nsCOMPtr<nsIRunnable> event = new nsAsyncEventRunner(aName, this); - NS_DispatchToMainThread(event); + OwnerDoc()->Dispatch("HTMLMediaElement::DispatchAsyncEvent", + TaskCategory::Other, + event.forget()); if ((aName.EqualsLiteral("play") || aName.EqualsLiteral("playing"))) { mPlayTime.Start(); if (IsHidden()) { HiddenVideoStart(); } } else if (aName.EqualsLiteral("waiting")) { mPlayTime.Pause();
--- a/dom/messagechannel/MessagePort.cpp +++ b/dom/messagechannel/MessagePort.cpp @@ -561,16 +561,22 @@ MessagePort::Dispatch() break; } RefPtr<SharedMessagePortMessage> data = mMessages.ElementAt(0); mMessages.RemoveElementAt(0); mPostMessageRunnable = new PostMessageRunnable(this, data); + nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal(); + if (NS_IsMainThread() && global) { + MOZ_ALWAYS_SUCCEEDS(global->Dispatch("MessagePortMessage", TaskCategory::Other, do_AddRef(mPostMessageRunnable))); + return; + } + MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(mPostMessageRunnable)); } void MessagePort::Close() { CloseInternal(true /* aSoftly */); }
--- a/dom/plugins/ipc/PluginModuleParent.cpp +++ b/dom/plugins/ipc/PluginModuleParent.cpp @@ -2751,16 +2751,29 @@ PluginModuleParent::NPP_NewInternal(NPMI } // Release the surrogate reference that was in pdata RefPtr<PluginAsyncSurrogate> surrogate( dont_AddRef(PluginAsyncSurrogate::Cast(instance))); // Now replace it with the instance instance->pdata = static_cast<PluginDataResolver*>(parentInstance); + // Any IPC messages for the PluginInstance actor should be dispatched to the + // DocGroup for the plugin's document. + RefPtr<nsPluginInstanceOwner> owner = parentInstance->GetOwner(); + nsCOMPtr<nsIDOMElement> elt; + owner->GetDOMElement(getter_AddRefs(elt)); + if (nsCOMPtr<nsINode> node = do_QueryInterface(elt)) { + nsCOMPtr<nsIDocument> doc = node->OwnerDoc(); + if (doc) { + nsCOMPtr<nsIEventTarget> eventTarget = doc->EventTargetFor(dom::TaskCategory::Other); + SetEventTargetForActor(parentInstance, eventTarget); + } + } + if (!SendPPluginInstanceConstructor(parentInstance, nsDependentCString(pluginType), mode, names, values)) { // |parentInstance| is automatically deleted. instance->pdata = nullptr; *error = NPERR_GENERIC_ERROR; return NS_ERROR_FAILURE; }
--- a/dom/smil/nsSMILTimeContainer.cpp +++ b/dom/smil/nsSMILTimeContainer.cpp @@ -15,17 +15,19 @@ nsSMILTimeContainer::nsSMILTimeContainer : mParent(nullptr), mCurrentTime(0L), mParentOffset(0L), mPauseStart(0L), mNeedsPauseSample(false), mNeedsRewind(false), mIsSeeking(false), +#ifdef DEBUG mHoldingEntries(false), +#endif mPauseState(PAUSE_BEGIN) { } nsSMILTimeContainer::~nsSMILTimeContainer() { if (mParent) { mParent->RemoveChild(*this); @@ -211,24 +213,24 @@ nsSMILTimeContainer::SetParent(nsSMILTim bool nsSMILTimeContainer::AddMilestone(const nsSMILMilestone& aMilestone, mozilla::dom::SVGAnimationElement& aElement) { // We record the milestone time and store it along with the element but this // time may change (e.g. if attributes are changed on the timed element in // between samples). If this happens, then we may do an unecessary sample // but that's pretty cheap. - MOZ_RELEASE_ASSERT(!mHoldingEntries); + MOZ_ASSERT(!mHoldingEntries); return mMilestoneEntries.Push(MilestoneEntry(aMilestone, aElement)); } void nsSMILTimeContainer::ClearMilestones() { - MOZ_RELEASE_ASSERT(!mHoldingEntries); + MOZ_ASSERT(!mHoldingEntries); mMilestoneEntries.Clear(); } bool nsSMILTimeContainer::GetNextMilestoneInParentTime( nsSMILMilestone& aNextMilestone) const { if (mMilestoneEntries.IsEmpty()) @@ -259,46 +261,48 @@ nsSMILTimeContainer::PopMilestoneElement nsSMILMilestone containerMilestone(containerTime.GetMillis(), aMilestone.mIsEnd); MOZ_ASSERT(mMilestoneEntries.Top().mMilestone >= containerMilestone, "Trying to pop off earliest times but we have earlier ones that " "were overlooked"); - MOZ_RELEASE_ASSERT(!mHoldingEntries); + MOZ_ASSERT(!mHoldingEntries); bool gotOne = false; while (!mMilestoneEntries.IsEmpty() && mMilestoneEntries.Top().mMilestone == containerMilestone) { aMatchedElements.AppendElement(mMilestoneEntries.Pop().mTimebase); gotOne = true; } return gotOne; } void nsSMILTimeContainer::Traverse(nsCycleCollectionTraversalCallback* aCallback) { +#ifdef DEBUG AutoRestore<bool> saveHolding(mHoldingEntries); mHoldingEntries = true; +#endif const MilestoneEntry* p = mMilestoneEntries.Elements(); while (p < mMilestoneEntries.Elements() + mMilestoneEntries.Length()) { NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback, "mTimebase"); aCallback->NoteXPCOMChild(static_cast<nsIContent*>(p->mTimebase.get())); ++p; } } void nsSMILTimeContainer::Unlink() { - MOZ_RELEASE_ASSERT(!mHoldingEntries); + MOZ_ASSERT(!mHoldingEntries); mMilestoneEntries.Clear(); } void nsSMILTimeContainer::UpdateCurrentTime() { nsSMILTime now = IsPaused() ? mPauseStart : GetParentTime(); mCurrentTime = now - mParentOffset; @@ -312,23 +316,30 @@ nsSMILTimeContainer::NotifyTimeChange() // time. When this happens time dependencies in other time containers need to // re-resolve their times because begin and end times are stored in container // time. // // To get the list of timed elements with dependencies we simply re-use the // milestone elements. This is because any timed element with dependents and // with significant transitions yet to fire should have their next milestone // registered. Other timed elements don't matter. - AutoRestore<bool> saveHolding(mHoldingEntries); - mHoldingEntries = true; - const MilestoneEntry* p = mMilestoneEntries.Elements(); -#if DEBUG - uint32_t queueLength = mMilestoneEntries.Length(); + + // Copy the timed elements to a separate array before calling + // HandleContainerTimeChange on each of them in case doing so mutates + // mMilestoneEntries. + nsTArray<RefPtr<mozilla::dom::SVGAnimationElement>> elems; + + { +#ifdef DEBUG + AutoRestore<bool> saveHolding(mHoldingEntries); + mHoldingEntries = true; #endif - while (p < mMilestoneEntries.Elements() + mMilestoneEntries.Length()) { - mozilla::dom::SVGAnimationElement* elem = p->mTimebase.get(); + for (const MilestoneEntry* p = mMilestoneEntries.Elements(); + p < mMilestoneEntries.Elements() + mMilestoneEntries.Length(); + ++p) { + elems.AppendElement(p->mTimebase.get()); + } + } + + for (auto& elem : elems) { elem->TimedElement().HandleContainerTimeChange(); - MOZ_ASSERT(queueLength == mMilestoneEntries.Length(), - "Call to HandleContainerTimeChange resulted in a change to the " - "queue of milestones"); - ++p; } }
--- a/dom/smil/nsSMILTimeContainer.h +++ b/dom/smil/nsSMILTimeContainer.h @@ -261,17 +261,20 @@ protected: nsSMILTime mPauseStart; // Whether or not a pause sample is required bool mNeedsPauseSample; bool mNeedsRewind; // Backwards seek performed bool mIsSeeking; // Currently in the middle of a seek operation - bool mHoldingEntries; // True if there's a raw pointer to mMilestoneEntries on the stack. +#ifdef DEBUG + bool mHoldingEntries; // True if there's a raw pointer to mMilestoneEntries + // on the stack. +#endif // A bitfield of the pause state for all pause requests uint32_t mPauseState; struct MilestoneEntry { MilestoneEntry(nsSMILMilestone aMilestone, mozilla::dom::SVGAnimationElement& aElement)
--- a/dom/workers/ServiceWorkerClients.cpp +++ b/dom/workers/ServiceWorkerClients.cpp @@ -500,17 +500,17 @@ public: MutexAutoLock lock(mPromiseProxy->Lock()); if (mPromiseProxy->CleanedUp()) { return NS_OK; } #ifdef MOZ_WIDGET_ANDROID // This fires an intent that will start launching Fennec and foreground it, // if necessary. - java::GeckoAppShell::OpenWindowForNotification(); + java::GeckoAppShell::LaunchOrBringToFront(); #endif nsCOMPtr<nsPIDOMWindowOuter> window; nsresult rv = OpenWindow(getter_AddRefs(window)); if (NS_SUCCEEDED(rv)) { MOZ_ASSERT(window); rv = nsContentUtils::DispatchFocusChromeEvent(window);
--- a/dom/workers/test/worker_fileReader.js +++ b/dom/workers/test/worker_fileReader.js @@ -299,41 +299,43 @@ onmessage = function(message) { r.abort(); } catch(e) { abortThrew = true; } is(abortThrew, true, "abort() must throw if not loading"); is(abortHasRun, false, "abort() is a no-op unless loading"); r.readAsText(asciiFile); r.abort(); - is(abortHasRun, true, "abort should fire sync"); + is(abortHasRun, true, "1 abort should fire sync"); is(loadEndHasRun, true, "loadend should fire sync"); // Test calling readAsX to cause abort() var reuseAbortHasRun = false; r = new FileReader(); - r.onabort = function (event) { - is(reuseAbortHasRun, false, "abort should only fire once"); - reuseAbortHasRun = true; - is(event.target.readyState, FileReader.DONE, "should be DONE while firing onabort"); - is(event.target.error.name, "AbortError", "error set to AbortError for aborted reads"); - is(event.target.result, null, "file data should be null on aborted reads"); - } - r.onload = function() { ok(false, "load should not fire for aborted reads") }; + r.onabort = function (event) { reuseAbortHasRun = true; } + r.onload = function() { ok(true, "load should fire for aborted reads") }; var abortThrew = false; try { r.abort(); } catch(e) { abortThrew = true; } is(abortThrew, true, "abort() must throw if not loading"); is(reuseAbortHasRun, false, "abort() is a no-op unless loading"); r.readAsText(asciiFile); + + var readThrew = false; + try { r.readAsText(asciiFile); - is(reuseAbortHasRun, true, "abort should fire sync"); + } catch(e) { + readThrew = true; + } + + is(readThrew, true, "readAsText() must throw if loading"); + is(reuseAbortHasRun, false, "2 abort should fire sync"); r.onload = getLoadHandler(testASCIIData, testASCIIData.length, "reuse-as-abort reading"); expectedTestCount++; // Test reading from nonexistent files r = new FileReader(); var didThrow = false; r.onerror = function (event) {
--- a/dom/xhr/XMLHttpRequestMainThread.cpp +++ b/dom/xhr/XMLHttpRequestMainThread.cpp @@ -8,16 +8,17 @@ #include <algorithm> #ifndef XP_WIN #include <unistd.h> #endif #include "mozilla/ArrayUtils.h" #include "mozilla/CheckedInt.h" #include "mozilla/dom/BlobSet.h" +#include "mozilla/dom/DocGroup.h" #include "mozilla/dom/File.h" #include "mozilla/dom/FetchUtil.h" #include "mozilla/dom/FormData.h" #include "mozilla/dom/MutableBlobStorage.h" #include "mozilla/dom/XMLDocument.h" #include "mozilla/dom/URLSearchParams.h" #include "mozilla/EventDispatcher.h" #include "mozilla/EventListenerManager.h" @@ -3144,16 +3145,25 @@ XMLHttpRequestMainThread::SetTimeout(uin mTimeoutMilliseconds = aTimeout; if (mRequestSentTime) { StartTimeoutTimer(); } } void +XMLHttpRequestMainThread::SetTimerEventTarget(nsITimer* aTimer) +{ + if (nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal()) { + nsCOMPtr<nsIEventTarget> target = global->EventTargetFor(TaskCategory::Other); + aTimer->SetTarget(target); + } +} + +void XMLHttpRequestMainThread::StartTimeoutTimer() { MOZ_ASSERT(mRequestSentTime, "StartTimeoutTimer mustn't be called before the request was sent!"); if (mState == State::done) { // do nothing! return; } @@ -3163,16 +3173,17 @@ XMLHttpRequestMainThread::StartTimeoutTi } if (!mTimeoutMilliseconds) { return; } if (!mTimeoutTimer) { mTimeoutTimer = do_CreateInstance(NS_TIMER_CONTRACTID); + SetTimerEventTarget(mTimeoutTimer); } uint32_t elapsed = (uint32_t)((PR_Now() - mRequestSentTime) / PR_USEC_PER_MSEC); mTimeoutTimer->InitWithCallback( this, mTimeoutMilliseconds > elapsed ? mTimeoutMilliseconds - elapsed : 0, nsITimer::TYPE_ONE_SHOT ); @@ -3638,16 +3649,17 @@ XMLHttpRequestMainThread::StopProgressEv } } void XMLHttpRequestMainThread::StartProgressEventTimer() { if (!mProgressNotifier) { mProgressNotifier = do_CreateInstance(NS_TIMER_CONTRACTID); + SetTimerEventTarget(mProgressNotifier); } if (mProgressNotifier) { mProgressTimerIsActive = true; mProgressNotifier->Cancel(); mProgressNotifier->InitWithCallback(this, NS_PROGRESS_EVENT_INTERVAL, nsITimer::TYPE_ONE_SHOT); } } @@ -3664,16 +3676,17 @@ XMLHttpRequestMainThread::MaybeStartSync // If we are in a beforeunload or a unload event, we must force a timeout. TimeDuration diff = (TimeStamp::NowLoRes() - doc->GetPageUnloadingEventTimeStamp()); if (diff.ToMilliseconds() > MAX_SYNC_TIMEOUT_WHEN_UNLOADING) { return eErrorOrExpired; } mSyncTimeoutTimer = do_CreateInstance(NS_TIMER_CONTRACTID); + SetTimerEventTarget(mSyncTimeoutTimer); if (!mSyncTimeoutTimer) { return eErrorOrExpired; } uint32_t timeout = MAX_SYNC_TIMEOUT_WHEN_UNLOADING - diff.ToMilliseconds(); nsresult rv = mSyncTimeoutTimer->InitWithCallback(this, timeout, nsITimer::TYPE_ONE_SHOT); return NS_FAILED(rv) ? eErrorOrExpired : eTimerStarted; @@ -3800,16 +3813,29 @@ XMLHttpRequestMainThread::BlobStoreCompl mLoadTotal = mResponseBlob->GetSize(rv); if (NS_WARN_IF(rv.Failed())) { rv.SuppressException(); } ChangeStateToDone(); } +nsresult +XMLHttpRequestMainThread::GetName(nsACString& aName) +{ + aName.AssignLiteral("XMLHttpRequest"); + return NS_OK; +} + +nsresult +XMLHttpRequestMainThread::SetName(const char* aName) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + // nsXMLHttpRequestXPCOMifier implementation NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXMLHttpRequestXPCOMifier) NS_INTERFACE_MAP_ENTRY(nsIStreamListener) NS_INTERFACE_MAP_ENTRY(nsIRequestObserver) NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink) NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback) NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink) NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
--- a/dom/xhr/XMLHttpRequestMainThread.h +++ b/dom/xhr/XMLHttpRequestMainThread.h @@ -159,16 +159,17 @@ class XMLHttpRequestMainThread final : p public nsIJSXMLHttpRequest, public nsIStreamListener, public nsIChannelEventSink, public nsIProgressEventSink, public nsIInterfaceRequestor, public nsSupportsWeakReference, public nsITimerCallback, public nsISizeOfEventTarget, + public nsINamed, public MutableBlobStorageCallback { friend class nsXHRParseEndListener; friend class nsXMLHttpRequestXPCOMifier; public: enum class ProgressEventType : uint8_t { loadstart, @@ -225,16 +226,19 @@ public: NS_DECL_NSIPROGRESSEVENTSINK // nsIInterfaceRequestor NS_DECL_NSIINTERFACEREQUESTOR // nsITimerCallback NS_DECL_NSITIMERCALLBACK + // nsINamed + NS_DECL_NSINAMED + // nsISizeOfEventTarget virtual size_t SizeOfEventTargetIncludingThis(MallocSizeOf aMallocSizeOf) const override; NS_REALLY_FORWARD_NSIDOMEVENTTARGET(XMLHttpRequestEventTarget) // states virtual uint16_t ReadyState() const override; @@ -588,16 +592,18 @@ protected: void StartProgressEventTimer(); void StopProgressEventTimer(); void MaybeCreateBlobStorage(); nsresult OnRedirectVerifyCallback(nsresult result); + void SetTimerEventTarget(nsITimer* aTimer); + already_AddRefed<nsXMLHttpRequestXPCOMifier> EnsureXPCOMifier(); nsCOMPtr<nsISupports> mContext; nsCOMPtr<nsIPrincipal> mPrincipal; nsCOMPtr<nsIChannel> mChannel; nsCString mRequestMethod; nsCOMPtr<nsIURI> mRequestURL; nsCOMPtr<nsIDocument> mResponseXML;
--- a/gfx/2d/DrawEventRecorder.cpp +++ b/gfx/2d/DrawEventRecorder.cpp @@ -31,18 +31,17 @@ DrawEventRecorderPrivate::RecordEvent(co WriteElement(*mOutputStream, aEvent.mType); aEvent.RecordToStream(*mOutputStream); Flush(); } DrawEventRecorderFile::DrawEventRecorderFile(const char *aFilename) - : DrawEventRecorderPrivate(nullptr) - , mOutputFilename(aFilename) + : DrawEventRecorderPrivate(nullptr) , mOutputFile(aFilename, ofstream::binary) { mOutputStream = &mOutputFile; WriteHeader(); } DrawEventRecorderFile::~DrawEventRecorderFile() @@ -51,35 +50,16 @@ DrawEventRecorderFile::~DrawEventRecorde } void DrawEventRecorderFile::Flush() { mOutputFile.flush(); } -void -DrawEventRecorderFile::OpenAndTruncate() -{ - if (mOutputFile.is_open()) { - return; - } - - mOutputFile.open(mOutputFilename.c_str(), ofstream::binary | ofstream::trunc); - WriteHeader(); -} - -void -DrawEventRecorderFile::Close() -{ - MOZ_ASSERT(mOutputFile.is_open()); - - mOutputFile.close(); -} - DrawEventRecorderMemory::DrawEventRecorderMemory() : DrawEventRecorderPrivate(nullptr) { mOutputStream = &mMemoryStream; WriteHeader(); }
--- a/gfx/2d/DrawEventRecorder.h +++ b/gfx/2d/DrawEventRecorder.h @@ -5,17 +5,16 @@ #ifndef MOZILLA_GFX_DRAWEVENTRECORDER_H_ #define MOZILLA_GFX_DRAWEVENTRECORDER_H_ #include "2D.h" #include "RecordedEvent.h" #include <ostream> #include <fstream> -#include <string> #if defined(_MSC_VER) #include <unordered_set> #else #include <set> #endif namespace mozilla { @@ -74,34 +73,19 @@ protected: class DrawEventRecorderFile : public DrawEventRecorderPrivate { public: MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorderFile) explicit DrawEventRecorderFile(const char *aFilename); ~DrawEventRecorderFile(); - /** - * Re-opens and truncates the file. The recorder does NOT forget which objects - * it has recorded. This can be used with Close, so that a recording can be - * processed in chunks. If the file is already open this does nothing. - */ - void OpenAndTruncate(); - - /** - * Closes the file so that it can be processed. The recorder does NOT forget - * which objects it has recorded. This can be used with OpenAndTruncate, so - * that a recording can be processed in chunks. The file must be open. - */ - void Close(); - private: virtual void Flush(); - std::string mOutputFilename; std::ofstream mOutputFile; }; class DrawEventRecorderMemory final : public DrawEventRecorderPrivate { public: MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorderMemory)
--- a/image/RasterImage.cpp +++ b/image/RasterImage.cpp @@ -1054,16 +1054,17 @@ RasterImage::RequestDecodeForSize(const { MOZ_ASSERT(NS_IsMainThread()); if (mError) { return NS_ERROR_FAILURE; } if (!mHasSize) { + mWantFullDecode = true; return NS_OK; } // Decide whether to sync decode images we can decode quickly. Here we are // explicitly trading off flashing for responsiveness in the case that we're // redecoding an image (see bug 845147). bool shouldSyncDecodeIfFast = !mHasBeenDecoded && (aFlags & FLAG_SYNC_DECODE_IF_FAST);
new file mode 100644 --- /dev/null +++ b/js/src/irregexp/RegExpCharacters-inl.h @@ -0,0 +1,40 @@ +/* Generated by make_unicode.py DO NOT MODIFY */ +/* Unicode version: 9.0.0 */ +#ifndef V8_JSREGEXPCHARACTERS_INL_H_ +#define V8_JSREGEXPCHARACTERS_INL_H_ + +namespace js { + +namespace irregexp { + +static inline bool +RangeContainsLatin1Equivalents(CharacterRange range, bool unicode) +{ + if (unicode) { + // "LATIN SMALL LETTER LONG S" case folds to "LATIN SMALL LETTER S". + if (range.Contains(0x017F)) + return true; + // "LATIN CAPITAL LETTER SHARP S" case folds to "LATIN SMALL LETTER SHARP S". + if (range.Contains(0x1E9E)) + return true; + // "KELVIN SIGN" case folds to "LATIN SMALL LETTER K". + if (range.Contains(0x212A)) + return true; + // "ANGSTROM SIGN" case folds to "LATIN SMALL LETTER A WITH RING ABOVE". + if (range.Contains(0x212B)) + return true; + } + + // "GREEK CAPITAL LETTER MU" case maps to "MICRO SIGN". + // "GREEK SMALL LETTER MU" case maps to "MICRO SIGN". + if (range.Contains(0x039C) || range.Contains(0x03BC)) + return true; + // "LATIN CAPITAL LETTER Y WITH DIAERESIS" case maps to "LATIN SMALL LETTER Y WITH DIAERESIS". + if (range.Contains(0x0178)) + return true; + return false; +} + +} } // namespace js::irregexp + +#endif // V8_JSREGEXPCHARACTERS_INL_H_
new file mode 100644 --- /dev/null +++ b/js/src/irregexp/RegExpCharacters.cpp @@ -0,0 +1,135 @@ +/* Generated by make_unicode.py DO NOT MODIFY */ +/* Unicode version: 9.0.0 */ +#include "irregexp/RegExpCharacters.h" + +#include "mozilla/Assertions.h" + +char16_t +js::irregexp::ConvertNonLatin1ToLatin1(char16_t c, bool unicode) +{ + MOZ_ASSERT(c > 0xFF, "Character mustn't be Latin1"); + if (unicode) { + // "LATIN SMALL LETTER LONG S" case folds to "LATIN SMALL LETTER S". + if (c == 0x017F) + return 0x73; + // "LATIN CAPITAL LETTER SHARP S" case folds to "LATIN SMALL LETTER SHARP S". + if (c == 0x1E9E) + return 0xDF; + // "KELVIN SIGN" case folds to "LATIN SMALL LETTER K". + if (c == 0x212A) + return 0x6B; + // "ANGSTROM SIGN" case folds to "LATIN SMALL LETTER A WITH RING ABOVE". + if (c == 0x212B) + return 0xE5; + } + + // "GREEK CAPITAL LETTER MU" case maps to "MICRO SIGN". + // "GREEK SMALL LETTER MU" case maps to "MICRO SIGN". + if (c == 0x039C || c == 0x03BC) + return 0xB5; + // "LATIN CAPITAL LETTER Y WITH DIAERESIS" case maps to "LATIN SMALL LETTER Y WITH DIAERESIS". + if (c == 0x0178) + return 0xFF; + return 0; +} + +const int js::irregexp::kSpaceRanges[] = { + 0x0009, 0x000D + 1, // CHARACTER TABULATION..CARRIAGE RETURN (CR) + 0x0020, 0x0020 + 1, // SPACE + 0x00A0, 0x00A0 + 1, // NO-BREAK SPACE + 0x1680, 0x1680 + 1, // OGHAM SPACE MARK + 0x2000, 0x200A + 1, // EN QUAD..HAIR SPACE + 0x2028, 0x2029 + 1, // LINE SEPARATOR..PARAGRAPH SEPARATOR + 0x202F, 0x202F + 1, // NARROW NO-BREAK SPACE + 0x205F, 0x205F + 1, // MEDIUM MATHEMATICAL SPACE + 0x3000, 0x3000 + 1, // IDEOGRAPHIC SPACE + 0xFEFF, 0xFEFF + 1, // ZERO WIDTH NO-BREAK SPACE + 0xFFFF + 1 +}; +const int js::irregexp::kSpaceRangeCount = 21; + +const int js::irregexp::kSpaceAndSurrogateRanges[] = { + 0x0009, 0x000D + 1, // CHARACTER TABULATION..CARRIAGE RETURN (CR) + 0x0020, 0x0020 + 1, // SPACE + 0x00A0, 0x00A0 + 1, // NO-BREAK SPACE + 0x1680, 0x1680 + 1, // OGHAM SPACE MARK + 0x2000, 0x200A + 1, // EN QUAD..HAIR SPACE + 0x2028, 0x2029 + 1, // LINE SEPARATOR..PARAGRAPH SEPARATOR + 0x202F, 0x202F + 1, // NARROW NO-BREAK SPACE + 0x205F, 0x205F + 1, // MEDIUM MATHEMATICAL SPACE + 0x3000, 0x3000 + 1, // IDEOGRAPHIC SPACE + 0xD800, 0xDFFF + 1, // <Lead Surrogate Min>..<Trail Surrogate Max> + 0xFEFF, 0xFEFF + 1, // ZERO WIDTH NO-BREAK SPACE + 0xFFFF + 1 +}; +const int js::irregexp::kSpaceAndSurrogateRangeCount = 23; + +const int js::irregexp::kWordRanges[] = { + 0x0030, 0x0039 + 1, // DIGIT ZERO..DIGIT NINE + 0x0041, 0x005A + 1, // LATIN CAPITAL LETTER A..LATIN CAPITAL LETTER Z + 0x005F, 0x005F + 1, // LOW LINE + 0x0061, 0x007A + 1, // LATIN SMALL LETTER A..LATIN SMALL LETTER Z + 0xFFFF + 1 +}; +const int js::irregexp::kWordRangeCount = 9; + +const int js::irregexp::kIgnoreCaseWordRanges[] = { + 0x0030, 0x0039 + 1, // DIGIT ZERO..DIGIT NINE + 0x0041, 0x005A + 1, // LATIN CAPITAL LETTER A..LATIN CAPITAL LETTER Z + 0x005F, 0x005F + 1, // LOW LINE + 0x0061, 0x007A + 1, // LATIN SMALL LETTER A..LATIN SMALL LETTER Z + 0x017F, 0x017F + 1, // LATIN SMALL LETTER LONG S + 0x212A, 0x212A + 1, // KELVIN SIGN + 0xFFFF + 1 +}; +const int js::irregexp::kIgnoreCaseWordRangeCount = 13; + +const int js::irregexp::kWordAndSurrogateRanges[] = { + 0x0030, 0x0039 + 1, // DIGIT ZERO..DIGIT NINE + 0x0041, 0x005A + 1, // LATIN CAPITAL LETTER A..LATIN CAPITAL LETTER Z + 0x005F, 0x005F + 1, // LOW LINE + 0x0061, 0x007A + 1, // LATIN SMALL LETTER A..LATIN SMALL LETTER Z + 0xD800, 0xDFFF + 1, // <Lead Surrogate Min>..<Trail Surrogate Max> + 0xFFFF + 1 +}; +const int js::irregexp::kWordAndSurrogateRangeCount = 11; + +const int js::irregexp::kNegatedIgnoreCaseWordAndSurrogateRanges[] = { + 0x0000, 0x002F + 1, // NULL..SOLIDUS + 0x003A, 0x0040 + 1, // COLON..COMMERCIAL AT + 0x005B, 0x005E + 1, // LEFT SQUARE BRACKET..CIRCUMFLEX ACCENT + 0x0060, 0x0060 + 1, // GRAVE ACCENT + 0x007B, 0x017E + 1, // LEFT CURLY BRACKET..LATIN SMALL LETTER Z WITH CARON + 0x0180, 0x2129 + 1, // LATIN SMALL LETTER B WITH STROKE..TURNED GREEK SMALL LETTER IOTA + 0x212B, 0xD7FF + 1, // ANGSTROM SIGN..<Unused> + 0xE000, 0xFFFF + 1, // Private Use..<Unused> + 0xFFFF + 1 +}; +const int js::irregexp::kNegatedIgnoreCaseWordAndSurrogateRangeCount = 17; + +const int js::irregexp::kDigitRanges[] = { + 0x0030, 0x0039 + 1, // DIGIT ZERO..DIGIT NINE + 0xFFFF + 1 +}; +const int js::irregexp::kDigitRangeCount = 3; + +const int js::irregexp::kDigitAndSurrogateRanges[] = { + 0x0030, 0x0039 + 1, // DIGIT ZERO..DIGIT NINE + 0xD800, 0xDFFF + 1, // <Lead Surrogate Min>..<Trail Surrogate Max> + 0xFFFF + 1 +}; +const int js::irregexp::kDigitAndSurrogateRangeCount = 5; + +const int js::irregexp::kSurrogateRanges[] = { + 0xD800, 0xDFFF + 1, // <Lead Surrogate Min>..<Trail Surrogate Max> + 0xFFFF + 1 +}; +const int js::irregexp::kSurrogateRangeCount = 3; + +const int js::irregexp::kLineTerminatorRanges[] = { + 0x000A, 0x000A + 1, // LINE FEED (LF) + 0x000D, 0x000D + 1, // CARRIAGE RETURN (CR) + 0x2028, 0x2029 + 1, // LINE SEPARATOR..PARAGRAPH SEPARATOR + 0xFFFF + 1 +}; +const int js::irregexp::kLineTerminatorRangeCount = 7;
new file mode 100644 --- /dev/null +++ b/js/src/irregexp/RegExpCharacters.h @@ -0,0 +1,90 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: */ + +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef V8_JSREGEXPCHARACTERS_H_ +#define V8_JSREGEXPCHARACTERS_H_ + +namespace js { + +namespace irregexp { + +char16_t +ConvertNonLatin1ToLatin1(char16_t c, bool unicode); + +// ------------------------------------------------------------------- +// CharacterRange + +// The ranges have inclusive from and exclusive to. + +// This covers \s as defined in ES2016, 21.2.2.12 CharacterClassEscape, +// which includes WhiteSpace (11.2) and LineTerminator (11.3) values. +extern const int kSpaceRanges[]; +extern const int kSpaceRangeCount; + +// Characters in \s and additionally all surrogate characters. +extern const int kSpaceAndSurrogateRanges[]; +extern const int kSpaceAndSurrogateRangeCount; + +// This covers \w as defined in ES2016, 21.2.2.12 CharacterClassEscape. +extern const int kWordRanges[]; +extern const int kWordRangeCount; + +// Characters which case-fold to characters in \w. +extern const int kIgnoreCaseWordRanges[]; +extern const int kIgnoreCaseWordRangeCount; + +// Characters in \w and additionally all surrogate characters. +extern const int kWordAndSurrogateRanges[]; +extern const int kWordAndSurrogateRangeCount; + +// All characters excluding those which case-fold to \w and excluding all +// surrogate characters. +extern const int kNegatedIgnoreCaseWordAndSurrogateRanges[]; +extern const int kNegatedIgnoreCaseWordAndSurrogateRangeCount; + +// This covers \d as defined in ES2016, 21.2.2.12 CharacterClassEscape. +extern const int kDigitRanges[]; +extern const int kDigitRangeCount; + +// Characters in \d and additionally all surrogate characters. +extern const int kDigitAndSurrogateRanges[]; +extern const int kDigitAndSurrogateRangeCount; + +// The range of all surrogate characters. +extern const int kSurrogateRanges[]; +extern const int kSurrogateRangeCount; + +// Line terminators as defined in ES2016, 11.3 LineTerminator. +extern const int kLineTerminatorRanges[]; +extern const int kLineTerminatorRangeCount; + +} } // namespace js::irregexp + +#endif // V8_JSREGEXPCHARACTERS_H_
--- a/js/src/irregexp/RegExpEngine.cpp +++ b/js/src/irregexp/RegExpEngine.cpp @@ -26,19 +26,22 @@ // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "irregexp/RegExpEngine.h" #include "irregexp/NativeRegExpMacroAssembler.h" +#include "irregexp/RegExpCharacters.h" #include "irregexp/RegExpMacroAssembler.h" #include "jit/JitCommon.h" +#include "irregexp/RegExpCharacters-inl.h" + using namespace js; using namespace js::irregexp; using mozilla::ArrayLength; using mozilla::DebugOnly; using mozilla::Maybe; #define DEFINE_ACCEPT(Type) \ @@ -55,71 +58,16 @@ void LoopChoiceNode::Accept(NodeVisitor* static const int kMaxLookaheadForBoyerMoore = 8; RegExpNode::RegExpNode(LifoAlloc* alloc) : replacement_(nullptr), trace_count_(0), alloc_(alloc) { bm_info_[0] = bm_info_[1] = nullptr; } -// ------------------------------------------------------------------- -// CharacterRange - -// The '2' variant has inclusive from and exclusive to. -// This covers \s as defined in ES2016, 21.2.2.12 CharacterClassEscape, -// which include WhiteSpace (11.2) or LineTerminator (11.3) values. -static const int kSpaceRanges[] = { '\t', '\r' + 1, ' ', ' ' + 1, - 0x00A0, 0x00A1, 0x1680, 0x1681, 0x2000, 0x200B, - 0x2028, 0x202A, 0x202F, 0x2030, 0x205F, 0x2060, 0x3000, 0x3001, - 0xFEFF, 0xFF00, 0x10000 }; -static const int kSpaceRangeCount = ArrayLength(kSpaceRanges); - -static const int kSpaceAndSurrogateRanges[] = { '\t', '\r' + 1, ' ', ' ' + 1, - 0x00A0, 0x00A1, 0x1680, 0x1681, 0x2000, 0x200B, - 0x2028, 0x202A, 0x202F, 0x2030, 0x205F, 0x2060, 0x3000, 0x3001, - unicode::LeadSurrogateMin, unicode::TrailSurrogateMax + 1, - 0xFEFF, 0xFF00, 0x10000 }; -static const int kSpaceAndSurrogateRangeCount = ArrayLength(kSpaceAndSurrogateRanges); -static const int kWordRanges[] = { - '0', '9' + 1, 'A', 'Z' + 1, '_', '_' + 1, 'a', 'z' + 1, 0x10000 }; -static const int kWordRangeCount = ArrayLength(kWordRanges); -static const int kIgnoreCaseWordRanges[] = { - '0', '9' + 1, 'A', 'Z' + 1, '_', '_' + 1, 'a', 'z' + 1, - 0x017F, 0x017F + 1, 0x212A, 0x212A + 1, - 0x10000 }; -static const int kIgnoreCaseWordCount = ArrayLength(kIgnoreCaseWordRanges); -static const int kWordAndSurrogateRanges[] = { - '0', '9' + 1, 'A', 'Z' + 1, '_', '_' + 1, 'a', 'z' + 1, - unicode::LeadSurrogateMin, unicode::TrailSurrogateMax + 1, - 0x10000 }; -static const int kWordAndSurrogateRangeCount = ArrayLength(kWordAndSurrogateRanges); -static const int kNegatedIgnoreCaseWordAndSurrogateRanges[] = { - 0, '0', '9' + 1, 'A', - 'Z' + 1, '_', '_' + 1, 'a', - 'z' + 1, 0x017F, - 0x017F + 1, 0x212A, - 0x212A + 1, unicode::LeadSurrogateMin, - unicode::TrailSurrogateMax + 1, 0x10000, - 0x10000 }; -static const int kNegatedIgnoreCaseWordAndSurrogateRangeCount = - ArrayLength(kNegatedIgnoreCaseWordAndSurrogateRanges); -static const int kDigitRanges[] = { '0', '9' + 1, 0x10000 }; -static const int kDigitRangeCount = ArrayLength(kDigitRanges); -static const int kDigitAndSurrogateRanges[] = { - '0', '9' + 1, - unicode::LeadSurrogateMin, unicode::TrailSurrogateMax + 1, - 0x10000 }; -static const int kDigitAndSurrogateRangeCount = ArrayLength(kDigitAndSurrogateRanges); -static const int kSurrogateRanges[] = { - unicode::LeadSurrogateMin, unicode::TrailSurrogateMax + 1, - 0x10000 }; -static const int kSurrogateRangeCount = ArrayLength(kSurrogateRanges); -static const int kLineTerminatorRanges[] = { 0x000A, 0x000B, 0x000D, 0x000E, - 0x2028, 0x202A, 0x10000 }; -static const int kLineTerminatorRangeCount = ArrayLength(kLineTerminatorRanges); static const int kMaxOneByteCharCode = 0xff; static const int kMaxUtf16CodeUnit = 0xffff; static char16_t MaximumCharacter(bool ascii) { return ascii ? kMaxOneByteCharCode : kMaxUtf16CodeUnit; } @@ -207,17 +155,17 @@ CharacterRange::AddClassEscapeUnicode(Li case 'd': return AddClassEscape(alloc, type, ranges); break; case 'S': AddClassNegated(kSpaceAndSurrogateRanges, kSpaceAndSurrogateRangeCount, ranges); break; case 'w': if (ignore_case) - AddClass(kIgnoreCaseWordRanges, kIgnoreCaseWordCount, ranges); + AddClass(kIgnoreCaseWordRanges, kIgnoreCaseWordRangeCount, ranges); else AddClassEscape(alloc, type, ranges); break; case 'W': if (ignore_case) { AddClass(kNegatedIgnoreCaseWordAndSurrogateRanges, kNegatedIgnoreCaseWordAndSurrogateRangeCount, ranges); } else { @@ -227,43 +175,16 @@ CharacterRange::AddClassEscapeUnicode(Li case 'D': AddClassNegated(kDigitAndSurrogateRanges, kDigitAndSurrogateRangeCount, ranges); break; default: MOZ_CRASH("Bad type!"); } } -#define FOR_EACH_NON_ASCII_TO_ASCII_FOLDING(macro) \ - /* LATIN CAPITAL LETTER Y WITH DIAERESIS */ \ - macro(0x0178, 0x00FF) \ - /* LATIN SMALL LETTER LONG S */ \ - macro(0x017F, 0x0073) \ - /* LATIN CAPITAL LETTER SHARP S */ \ - macro(0x1E9E, 0x00DF) \ - /* KELVIN SIGN */ \ - macro(0x212A, 0x006B) \ - /* ANGSTROM SIGN */ \ - macro(0x212B, 0x00E5) - -// We need to check for the following characters: 0x39c 0x3bc 0x178. -static inline bool -RangeContainsLatin1Equivalents(CharacterRange range, bool unicode) -{ - /* TODO(dcarney): this could be a lot more efficient. */ - if (unicode) { -#define CHECK_RANGE(C, F) \ - if (range.Contains(C)) return true; -FOR_EACH_NON_ASCII_TO_ASCII_FOLDING(CHECK_RANGE) -#undef CHECK_RANGE - } - - return range.Contains(0x39c) || range.Contains(0x3bc) || range.Contains(0x178); -} - static bool RangesContainLatin1Equivalents(const CharacterRangeVector& ranges, bool unicode) { for (size_t i = 0; i < ranges.length(); i++) { // TODO(dcarney): this could be a lot more efficient. if (RangeContainsLatin1Equivalents(ranges[i], unicode)) return true; } @@ -330,17 +251,17 @@ GetCaseIndependentLetters(char16_t chara char16_t other1 = others.other1(); char16_t other2 = others.other2(); char16_t other3 = others.other3(); // ES 2017 draft 996af87b7072b3c3dd2b1def856c66f456102215 21.2.4.2 // step 3.g. // The standard requires that non-ASCII characters cannot have ASCII // character codes in their equivalence class, even though this - // situation occurs multiple times in the unicode tables. + // situation occurs multiple times in the Unicode tables. static const unsigned kMaxAsciiCharCode = 127; if (upper <= kMaxAsciiCharCode) { if (character > kMaxAsciiCharCode) { // If Canonicalize(character) == character, all other characters // should be ignored. return GetCaseIndependentLetters(character, ascii_subject, unicode, &character, 1, letters); } @@ -359,41 +280,16 @@ GetCaseIndependentLetters(char16_t chara other1, other2, other3 }; return GetCaseIndependentLetters(character, ascii_subject, unicode, choices, ArrayLength(choices), letters); } -static char16_t -ConvertNonLatin1ToLatin1(char16_t c, bool unicode) -{ - MOZ_ASSERT(c > kMaxOneByteCharCode); - if (unicode) { - switch (c) { -#define CONVERT(C, F) case C: return F; -FOR_EACH_NON_ASCII_TO_ASCII_FOLDING(CONVERT) -#undef CONVERT - } - } - - switch (c) { - // This are equivalent characters in unicode. - case 0x39c: - case 0x3bc: - return 0xb5; - // This is an uppercase of a Latin-1 character - // outside of Latin-1. - case 0x178: - return 0xff; - } - return 0; -} - void CharacterRange::AddCaseEquivalents(bool is_ascii, bool unicode, CharacterRangeVector* ranges) { char16_t bottom = from(); char16_t top = to(); if (is_ascii && !RangeContainsLatin1Equivalents(*this, unicode)) { if (bottom > kMaxOneByteCharCode)
--- a/js/src/moz.build +++ b/js/src/moz.build @@ -192,16 +192,17 @@ UNIFIED_SOURCES += [ 'gc/Nursery.cpp', 'gc/RootMarking.cpp', 'gc/Statistics.cpp', 'gc/Tracer.cpp', 'gc/Verifier.cpp', 'gc/Zone.cpp', 'irregexp/NativeRegExpMacroAssembler.cpp', 'irregexp/RegExpAST.cpp', + 'irregexp/RegExpCharacters.cpp', 'irregexp/RegExpEngine.cpp', 'irregexp/RegExpInterpreter.cpp', 'irregexp/RegExpMacroAssembler.cpp', 'irregexp/RegExpParser.cpp', 'irregexp/RegExpStack.cpp', 'jit/AliasAnalysis.cpp', 'jit/AliasAnalysisShared.cpp', 'jit/AlignmentMaskAnalysis.cpp',
--- a/js/src/vm/make_unicode.py +++ b/js/src/vm/make_unicode.py @@ -128,16 +128,27 @@ def read_derived_core_properties(derived char_property = row[1].strip() if '..' not in char_range: yield (int(char_range, 16), char_property) else: [start, end] = char_range.split('..') for char in range(int(start, 16), int(end, 16) + 1): yield (char, char_property) +def int_ranges(ints): + """ Yields consecutive ranges (inclusive) from integer values. """ + from itertools import tee, izip_longest + + (a, b) = tee(sorted(ints)) + start = next(b) + for (curr, succ) in izip_longest(a, b): + if curr + 1 != succ: + yield (start, curr) + start = succ + def utf16_encode(code): NonBMPMin = 0x10000 LeadSurrogateMin = 0xD800 TrailSurrogateMin = 0xDC00 lead = (code - NonBMPMin) / 1024 + LeadSurrogateMin trail = ((code - NonBMPMin) % 1024) + TrailSurrogateMin @@ -853,16 +864,214 @@ def splitbins(t): dump(t1, t2, shift, bytes) # exhaustively verify that the decomposition is correct mask = 2**shift - 1 for i in range(len(t)): assert t[i] == t2[(t1[i >> shift] << shift) + (i & mask)] return best +def make_irregexp_tables(version, + table, index, + folding_table, folding_index, + test_table): + import string + from functools import partial + from itertools import chain, ifilter, imap + + MAX_ASCII = 0x7F + MAX_LATIN1 = 0xFF + LEAD_SURROGATE_MIN = 0xD800 + TRAIL_SURROGATE_MAX = 0xDFFF + + def hex2(n): + assert 0 <= n and n < 16**2 + return '0x{:02X}'.format(n) + + def hex4(n): + assert 0 <= n and n < 16**4 + return '0x{:04X}'.format(n) + + def uhex4(n): + assert 0 <= n and n < 16**4 + return 'U+{:04X}'.format(n) + + def case_info(code): + assert 0 <= code and code <= MAX_BMP + (upper, lower, flags) = table[index[code]] + return ((code + upper) & 0xffff, (code + lower) & 0xffff, flags) + + def is_space(code): + (_, _, flags) = case_info(code) + return bool(flags & FLAG_SPACE) + + def to_upper(code): + (upper, _, _) = case_info(code) + return upper + + def casefold(code): + assert 0 <= code and code <= MAX_BMP + (folding, _, _, _) = folding_table[folding_index[code]] + return (code + folding) & 0xffff + + def casefolds_to_ascii(code): + return casefold(code) <= MAX_ASCII + + def casefolds_to_latin1(code): + return casefold(code) <= MAX_LATIN1 + + def casemaps_to_nonlatin1(code): + upper = to_upper(code) + return upper > MAX_LATIN1 + + def char_name(code): + assert 0 <= code and code <= MAX_BMP + if code not in test_table: + return '<Unused>' + if code == LEAD_SURROGATE_MIN: + return '<Lead Surrogate Min>' + if code == TRAIL_SURROGATE_MAX: + return '<Trail Surrogate Max>' + (_, _, name, alias) = test_table[code] + return name if not name.startswith('<') else alias + + def write_character_range(println, name, characters): + char_ranges = list(int_ranges(characters)) + println('') + println('const int js::irregexp::k{}Ranges[] = {{'.format(name)) + for (start, end) in char_ranges: + s_name = char_name(start) + e_name = char_name(end) + println(' {}, {} + 1, // {}'.format(hex4(start), hex4(end), + '{}..{}'.format(s_name, e_name) + if start != end else s_name)) + println(' {} + 1'.format(hex4(MAX_BMP))) + println('};') + println('const int js::irregexp::k{}RangeCount = {};'.format(name, + len(char_ranges) * 2 + 1)) + + def write_character_test(println, test, consequent, default): + # Latin1 characters which, when case-mapped through + # String.prototype.toUpperCase(), canonicalize to a non-Latin1 character. + # ES2017, §21.2.2.8.2 Runtime Semantics: Canonicalize + casemapped_to_nonlatin1 = ifilter(casemaps_to_nonlatin1, xrange(0, MAX_LATIN1 + 1)) + + def casemap_closure(ch): + upper = to_upper(ch) + return (ch, [c for c in xrange(MAX_LATIN1 + 1, MAX_BMP + 1) if upper == to_upper(c)]) + + # Mapping from Latin1 characters to the list of case map equivalent + # non-Latin1 characters. + casemap_for_latin1 = dict(chain(imap(casemap_closure, casemapped_to_nonlatin1))) + + # Non-latin1 characters which, when Unicode case-folded, canonicalize to + # a Latin1 character. + # ES2017, §21.2.2.8.2 Runtime Semantics: Canonicalize + casefolded_to_latin1 = ifilter(casefolds_to_latin1, xrange(MAX_LATIN1 + 1, MAX_BMP + 1)) + + println(' if (unicode) {') + for ch in casefolded_to_latin1: + casefolded = casefold(ch) + # Skip if also handled below for case mapping. + if casefolded in casemap_for_latin1 and ch in casemap_for_latin1[casefolded]: + continue + println(' // "{}" case folds to "{}".'.format(char_name(ch), + char_name(casefolded))) + println(' if ({})'.format(test(ch))) + println(' return {};'.format(consequent(casefolded))) + println(' }') + println('') + for (ch, casemapped_chars) in casemap_for_latin1.iteritems(): + for casemapped in casemapped_chars: + println(' // "{}" case maps to "{}".'.format(char_name(casemapped), + char_name(ch))) + println(' if ({})'.format(' || '.join(imap(test, casemapped_chars)))) + println(' return {};'.format(consequent(ch))) + println(' return {};'.format(default)) + + with io.open('../irregexp/RegExpCharacters-inl.h', 'wb') as chars_file: + write = partial(print, file=chars_file, sep='', end='') + println = partial(write, end='\n') + + write(warning_message) + write(unicode_version_message.format(version)) + + println('#ifndef V8_JSREGEXPCHARACTERS_INL_H_') + println('#define V8_JSREGEXPCHARACTERS_INL_H_') + println('') + println('namespace js {') + println('') + println('namespace irregexp {') + println('') + + println('static inline bool') + println('RangeContainsLatin1Equivalents(CharacterRange range, bool unicode)') + println('{') + write_character_test(println, lambda ch: 'range.Contains({})'.format(hex4(ch)), + lambda _: 'true', 'false') + println('}') + + println('') + println('} } // namespace js::irregexp') + println('') + println('#endif // V8_JSREGEXPCHARACTERS_INL_H_') + + with io.open('../irregexp/RegExpCharacters.cpp', 'wb') as chars_file: + write = partial(print, file=chars_file, sep='', end='') + println = partial(write, end='\n') + character_range = partial(write_character_range, println) + + # Characters in \s, 21.2.2.12 CharacterClassEscape. + space_chars = filter(is_space, xrange(0, MAX_BMP + 1)) + + # Characters in \d, 21.2.2.12 CharacterClassEscape. + digit_chars = map(ord, string.digits) + assert all(ch <= MAX_ASCII for ch in digit_chars) + + # Characters in \w, 21.2.2.12 CharacterClassEscape. + word_chars = map(ord, string.digits + string.ascii_letters + '_') + assert all(ch <= MAX_ASCII for ch in word_chars) + + # Characters which case-fold to characters in \w. + ignorecase_word_chars = (word_chars + + filter(casefolds_to_ascii, xrange(MAX_ASCII + 1, MAX_BMP + 1))) + + # Surrogate characters. + surrogate_chars = range(LEAD_SURROGATE_MIN, TRAIL_SURROGATE_MAX + 1) + + write(warning_message) + write(unicode_version_message.format(version)) + println('#include "irregexp/RegExpCharacters.h"') + println('') + println('#include "mozilla/Assertions.h"') + println('') + + println('char16_t') + println('js::irregexp::ConvertNonLatin1ToLatin1(char16_t c, bool unicode)') + println('{') + println(' MOZ_ASSERT(c > {}, "Character mustn\'t be Latin1");'.format(hex2(MAX_LATIN1))) + write_character_test(println, lambda ch: 'c == {}'.format(hex4(ch)), hex2, '0') + println('}') + + character_range('Space', space_chars) + character_range('SpaceAndSurrogate', space_chars + surrogate_chars) + + character_range('Word', word_chars) + character_range('IgnoreCaseWord', ignorecase_word_chars) + character_range('WordAndSurrogate', word_chars + surrogate_chars) + character_range('NegatedIgnoreCaseWordAndSurrogate', + set(xrange(0, MAX_BMP + 1)) - set(ignorecase_word_chars + surrogate_chars)) + + character_range('Digit', digit_chars) + character_range('DigitAndSurrogate', digit_chars + surrogate_chars) + + character_range('Surrogate', surrogate_chars) + + character_range('LineTerminator', line_terminator) + def update_unicode(args): import urllib2 version = args.version if version is not None: baseurl = 'http://unicode.org/Public' if version == 'UNIDATA': url = '%s/%s' % (baseurl, version) @@ -924,16 +1133,20 @@ def update_unicode(args): table, index, same_upper_table, same_upper_index, folding_table, folding_index, non_bmp_space_set, non_bmp_id_start_set, non_bmp_id_cont_set) make_non_bmp_file(unicode_version, non_bmp_lower_map, non_bmp_upper_map, non_bmp_folding_map, non_bmp_rev_folding_map) + make_irregexp_tables(unicode_version, + table, index, + folding_table, folding_index, + test_table) make_bmp_mapping_test(unicode_version, test_table) make_non_bmp_mapping_test(unicode_version, non_bmp_upper_map, non_bmp_lower_map) make_space_test(unicode_version, test_space_table) make_regexp_space_test(unicode_version, test_space_table) make_icase_test(unicode_version, folding_tests) if __name__ == '__main__':
--- a/js/src/wasm/WasmBaselineCompile.cpp +++ b/js/src/wasm/WasmBaselineCompile.cpp @@ -2204,17 +2204,17 @@ class BaseCompiler masm.freeStack(adjustment); if (call.reloadMachineStateAfter) { loadFromFramePtr(WasmTlsReg, frameOffsetFromSlot(tlsSlot_, MIRType::Pointer)); masm.loadWasmPinnedRegsFromTls(); } } - // TODO / OPTIMIZE (Bug 1316820): This is expensive; let's roll the iterator + // TODO / OPTIMIZE (Bug 1316821): This is expensive; let's roll the iterator // walking into the walking done for passArg. See comments in passArg. size_t stackArgAreaSize(const ValTypeVector& args) { ABIArgIter<const ValTypeVector> i(args); while (!i.done()) i++; return AlignBytes(i.stackBytesConsumedSoFar(), 16u); } @@ -2227,17 +2227,17 @@ class BaseCompiler if (adjustment) masm.reserveStack(adjustment); } const ABIArg reservePointerArgument(FunctionCall& call) { return call.abi.next(MIRType::Pointer); } - // TODO / OPTIMIZE (Bug 1316820): Note passArg is used only in one place. + // TODO / OPTIMIZE (Bug 1316821): Note passArg is used only in one place. // (Or it was, until Luke wandered through, but that can be fixed again.) // I'm not saying we should manually inline it, but we could hoist the // dispatch into the caller and have type-specific implementations of // passArg: passArgI32(), etc. Then those might be inlined, at least in PGO // builds. // // The bulk of the work here (60%) is in the next() call, though. //
--- a/layout/printing/ipc/PRemotePrintJob.ipdl +++ b/layout/printing/ipc/PRemotePrintJob.ipdl @@ -19,17 +19,18 @@ both: parent: // Initialize the real print device with the given information. async InitializePrint(nsString aDocumentTitle, nsString aPrintToFile, int32_t aStartPage, int32_t aEndPage); // Translate the stored page recording and play back the events to the real // print device. - async ProcessPage(nsCString aPageFileName); + // This will always deallocate the shared memory. + async ProcessPage(Shmem aStoredPage); // This informs the real print device that we've finished, so it can trigger // the actual print. async FinalizePrint(); // Report a state change to listeners in the parent process. async StateChange(long aStateFlags, nsresult aStatus);
--- a/layout/printing/ipc/RemotePrintJobChild.cpp +++ b/layout/printing/ipc/RemotePrintJobChild.cpp @@ -42,22 +42,22 @@ mozilla::ipc::IPCResult RemotePrintJobChild::RecvPrintInitializationResult(const nsresult& aRv) { mPrintInitialized = true; mInitializationResult = aRv; return IPC_OK(); } void -RemotePrintJobChild::ProcessPage(const nsCString& aPageFileName) +RemotePrintJobChild::ProcessPage(Shmem& aStoredPage) { MOZ_ASSERT(mPagePrintTimer); mPagePrintTimer->WaitForRemotePrint(); - Unused << SendProcessPage(aPageFileName); + Unused << SendProcessPage(aStoredPage); } mozilla::ipc::IPCResult RemotePrintJobChild::RecvPageProcessed() { MOZ_ASSERT(mPagePrintTimer); mPagePrintTimer->RemotePrintFinished();
--- a/layout/printing/ipc/RemotePrintJobChild.h +++ b/layout/printing/ipc/RemotePrintJobChild.h @@ -31,17 +31,17 @@ public: nsresult InitializePrint(const nsString& aDocumentTitle, const nsString& aPrintToFile, const int32_t& aStartPage, const int32_t& aEndPage); mozilla::ipc::IPCResult RecvPrintInitializationResult(const nsresult& aRv) final; - void ProcessPage(const nsCString& aPageFileName); + void ProcessPage(Shmem& aStoredPage); mozilla::ipc::IPCResult RecvPageProcessed() final; mozilla::ipc::IPCResult RecvAbortPrint(const nsresult& aRv) final; void SetPagePrintTimer(nsPagePrintTimer* aPagePrintTimer); void SetPrintEngine(nsPrintEngine* aPrintEngine);
--- a/layout/printing/ipc/RemotePrintJobParent.cpp +++ b/layout/printing/ipc/RemotePrintJobParent.cpp @@ -1,24 +1,22 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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/. */ #include "RemotePrintJobParent.h" -#include <fstream> +#include <istream> #include "gfxContext.h" #include "mozilla/Attributes.h" #include "mozilla/Unused.h" -#include "nsAppDirectoryServiceDefs.h" #include "nsComponentManagerUtils.h" -#include "nsDirectoryServiceUtils.h" #include "nsDeviceContext.h" #include "nsIDeviceContextSpec.h" #include "nsIPrintSettings.h" #include "nsIWebProgressListener.h" #include "PrintTranslator.h" namespace mozilla { namespace layout { @@ -78,73 +76,56 @@ RemotePrintJobParent::InitializePrintDev if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } return NS_OK; } mozilla::ipc::IPCResult -RemotePrintJobParent::RecvProcessPage(const nsCString& aPageFileName) +RemotePrintJobParent::RecvProcessPage(Shmem&& aStoredPage) { - nsresult rv = PrintPage(aPageFileName); + nsresult rv = PrintPage(aStoredPage); + + // Always deallocate the shared memory no matter what the result. + if (!DeallocShmem(aStoredPage)) { + NS_WARNING("Failed to deallocated shared memory, remote print will abort."); + rv = NS_ERROR_FAILURE; + } if (NS_FAILED(rv)) { Unused << SendAbortPrint(rv); } else { Unused << SendPageProcessed(); } return IPC_OK(); } nsresult -RemotePrintJobParent::PrintPage(const nsCString& aPageFileName) +RemotePrintJobParent::PrintPage(const Shmem& aStoredPage) { MOZ_ASSERT(mPrintDeviceContext); nsresult rv = mPrintDeviceContext->BeginPage(); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - nsCOMPtr<nsIFile> recordingFile; - rv = NS_GetSpecialDirectory(NS_APP_CONTENT_PROCESS_TEMP_DIR, - getter_AddRefs(recordingFile)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = recordingFile->AppendNative(aPageFileName); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsAutoCString recordingPath; - rv = recordingFile->GetNativePath(recordingPath); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - std::ifstream recording(recordingPath.get(), std::ifstream::binary); + std::istringstream recording(std::string(aStoredPage.get<char>(), + aStoredPage.Size<char>())); if (!mPrintTranslator->TranslateRecording(recording)) { return NS_ERROR_FAILURE; } rv = mPrintDeviceContext->EndPage(); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - recording.close(); - rv = recordingFile->Remove(/* recursive= */ false); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - return NS_OK; } mozilla::ipc::IPCResult RemotePrintJobParent::RecvFinalizePrint() { // EndDocument is sometimes called in the child even when BeginDocument has // not been called. See bug 1223332.
--- a/layout/printing/ipc/RemotePrintJobParent.h +++ b/layout/printing/ipc/RemotePrintJobParent.h @@ -29,17 +29,17 @@ public: void ActorDestroy(ActorDestroyReason aWhy) final; mozilla::ipc::IPCResult RecvInitializePrint(const nsString& aDocumentTitle, const nsString& aPrintToFile, const int32_t& aStartPage, const int32_t& aEndPage) final; - mozilla::ipc::IPCResult RecvProcessPage(const nsCString& aPageFileName) final; + mozilla::ipc::IPCResult RecvProcessPage(Shmem&& aStoredPage) final; mozilla::ipc::IPCResult RecvFinalizePrint() final; mozilla::ipc::IPCResult RecvAbortPrint(const nsresult& aRv) final; mozilla::ipc::IPCResult RecvStateChange(const long& aStateFlags, const nsresult& aStatus) final; @@ -65,17 +65,17 @@ public: private: ~RemotePrintJobParent() final; nsresult InitializePrintDevice(const nsString& aDocumentTitle, const nsString& aPrintToFile, const int32_t& aStartPage, const int32_t& aEndPage); - nsresult PrintPage(const nsCString& aPageFileName); + nsresult PrintPage(const Shmem& aStoredPage); nsCOMPtr<nsIPrintSettings> mPrintSettings; RefPtr<nsDeviceContext> mPrintDeviceContext; UniquePtr<PrintTranslator> mPrintTranslator; nsCOMArray<nsIWebProgressListener> mPrintProgressListeners; }; } // namespace layout
--- a/layout/reftests/forms/fieldset/fieldset-min-width-2-ref.html +++ b/layout/reftests/forms/fieldset/fieldset-min-width-2-ref.html @@ -1,2 +1,2 @@ <!DOCTYPE html> -<fieldset style="width: -moz-fit-content">‌<!-- To give us the right height --></fieldset> +<fieldset style="width: -moz-fit-content">​<!-- To give us the right height --></fieldset>
new file mode 100644 --- /dev/null +++ b/layout/style/crashtests/1321357-1.html @@ -0,0 +1,12 @@ +<!doctype html> +<html> +<body onload="document.getElementById('containerA').pauseAnimations()"> + <svg id="containerA"> + <animate id="ia" end="50s"></animate> + <animate begin="60s" end="ic.end"></animate> + </svg> + <svg> + <animate id="ic" end="ia.end"></animate> + </svg> +</body> +</html>
--- a/layout/style/crashtests/crashtests.list +++ b/layout/style/crashtests/crashtests.list @@ -159,8 +159,9 @@ pref(dom.animations-api.core.enabled,tru pref(dom.animations-api.core.enabled,true) load 1290994-2.html pref(dom.animations-api.core.enabled,true) load 1290994-3.html load 1290994-4.html load 1314531.html load 1315889-1.html load 1315894-1.html load 1319072-1.html HTTP load 1320423-1.html +load 1321357-1.html
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java +++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java @@ -202,16 +202,18 @@ public abstract class GeckoApp private final HashMap<String, PowerManager.WakeLock> mWakeLocks = new HashMap<String, PowerManager.WakeLock>(); protected boolean mLastSessionCrashed; protected boolean mShouldRestore; private boolean mSessionRestoreParsingFinished = false; private int lastSelectedTabId = -1; + private boolean foregrounded = false; + private static final class LastSessionParser extends SessionParser { private JSONArray tabs; private JSONObject windowObject; private boolean isExternalURL; private boolean selectNextTab; private boolean tabsWereSkipped; private boolean tabsWereProcessed; @@ -2066,25 +2068,32 @@ public abstract class GeckoApp return intent.getDataString(); } protected int getOrientation() { return GeckoScreenOrientation.getInstance().getAndroidOrientation(); } @Override + public boolean isForegrounded() { + return foregrounded; + } + + @Override public void onResume() { // After an onPause, the activity is back in the foreground. // Undo whatever we did in onPause. super.onResume(); if (mIsAbortingAppLaunch) { return; } + foregrounded = true; + GeckoAppShell.setGeckoInterface(this); if (lastSelectedTabId >= 0 && (lastActiveGeckoApp == null || lastActiveGeckoApp.get() != this)) { Tabs.getInstance().selectTab(lastSelectedTabId); } int newOrientation = getResources().getConfiguration().orientation; if (GeckoScreenOrientation.getInstance().update(newOrientation)) { @@ -2155,16 +2164,18 @@ public abstract class GeckoApp @Override public void onPause() { if (mIsAbortingAppLaunch) { super.onPause(); return; } + foregrounded = false; + final Tab selectedTab = Tabs.getInstance().getSelectedTab(); if (selectedTab != null) { lastSelectedTabId = selectedTab.getId(); } lastActiveGeckoApp = new WeakReference<GeckoApp>(this); final HealthRecorder rec = mHealthRecorder; final Context context = this;
--- a/mobile/android/base/java/org/mozilla/gecko/Tabs.java +++ b/mobile/android/base/java/org/mozilla/gecko/Tabs.java @@ -103,16 +103,17 @@ public class Tabs implements GeckoEventL } }; private Tabs() { EventDispatcher.getInstance().registerGeckoThreadListener(this, "Tab:Added", "Tab:Close", "Tab:Select", + "Tab:SelectAndForeground", "Content:LocationChange", "Content:SecurityChange", "Content:StateChange", "Content:LoadError", "Content:PageShow", "DOMTitleChanged", "Link:Favicon", "Link:Touchicon", @@ -519,16 +520,19 @@ public class Tabs implements GeckoEventL // Tab was already closed; abort if (tab == null) return; if (event.equals("Tab:Close")) { closeTab(tab); } else if (event.equals("Tab:Select")) { selectTab(tab.getId()); + } else if (event.equals("Tab:SelectAndForeground")) { + GeckoAppShell.launchOrBringToFront(); + selectTab(tab.getId()); } else if (event.equals("Content:LocationChange")) { tab.handleLocationChange(message); } else if (event.equals("Content:SecurityChange")) { tab.updateIdentityData(message.getJSONObject("identity")); notifyListeners(tab, TabEvents.SECURITY_CHANGE); } else if (event.equals("Content:StateChange")) { int state = message.getInt("state"); if ((state & GeckoAppShell.WPL_STATE_IS_NETWORK) != 0) {
--- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -3526,16 +3526,17 @@ Tab.prototype = { this.browser.addEventListener("DOMAudioPlaybackStopped", this, true); this.browser.addEventListener("DOMWindowClose", this, true); this.browser.addEventListener("DOMWillOpenModalDialog", this, true); this.browser.addEventListener("DOMAutoComplete", this, true); this.browser.addEventListener("blur", this, true); this.browser.addEventListener("pageshow", this, true); this.browser.addEventListener("MozApplicationManifest", this, true); this.browser.addEventListener("TabPreZombify", this, true); + this.browser.addEventListener("DOMServiceWorkerFocusClient", this, true); // Note that the XBL binding is untrusted this.browser.addEventListener("PluginBindingAttached", this, true, true); this.browser.addEventListener("VideoBindingAttached", this, true, true); this.browser.addEventListener("VideoBindingCast", this, true, true); Services.obs.addObserver(this, "before-first-paint", false); Services.obs.addObserver(this, "media-playback", false); @@ -3638,16 +3639,17 @@ Tab.prototype = { this.browser.removeEventListener("DOMAudioPlaybackStopped", this, true); this.browser.removeEventListener("DOMWindowClose", this, true); this.browser.removeEventListener("DOMWillOpenModalDialog", this, true); this.browser.removeEventListener("DOMAutoComplete", this, true); this.browser.removeEventListener("blur", this, true); this.browser.removeEventListener("pageshow", this, true); this.browser.removeEventListener("MozApplicationManifest", this, true); this.browser.removeEventListener("TabPreZombify", this, true); + this.browser.removeEventListener("DOMServiceWorkerFocusClient", this, true); this.browser.removeEventListener("PluginBindingAttached", this, true, true); this.browser.removeEventListener("VideoBindingAttached", this, true, true); this.browser.removeEventListener("VideoBindingCast", this, true, true); Services.obs.removeObserver(this, "before-first-paint"); Services.obs.removeObserver(this, "media-playback", false); Services.obs.removeObserver(this, "media-playback-resumed", false); @@ -4075,16 +4077,24 @@ Tab.prototype = { break; } case "MozApplicationManifest": { OfflineApps.offlineAppRequested(aEvent.originalTarget.defaultView); break; } + case "DOMServiceWorkerFocusClient": { + Messaging.sendRequest({ + type: "Tab:SelectAndForeground", + tabID: this.id + }); + break; + } + case "pageshow": { LoginManagerContent.onPageShow(aEvent, this.browser.contentWindow); // The rest of this only handles pageshow for the top-level document. if (aEvent.originalTarget.defaultView != this.browser.contentWindow) return; let target = aEvent.originalTarget;
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/BaseGeckoInterface.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/BaseGeckoInterface.java @@ -161,9 +161,14 @@ public class BaseGeckoInterface implemen return new String[] {}; } @Override public String getDefaultChromeURI() { // By default, use the GeckoView-specific chrome URI. return "chrome://browser/content/geckoview.xul"; } + + @Override + public boolean isForegrounded() { + return false; + } }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoAppShell.java @@ -318,22 +318,25 @@ public class GeckoAppShell } @WrapForJNI(exceptionMode = "ignore") private static void handleUncaughtException(Throwable e) { CRASH_HANDLER.uncaughtException(null, e); } @WrapForJNI - public static void openWindowForNotification() { - Intent intent = new Intent(Intent.ACTION_MAIN); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); - intent.setClassName(AppConstants.ANDROID_PACKAGE_NAME, AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS); + public static void launchOrBringToFront() { + GeckoInterface gi = getGeckoInterface(); + if (gi == null || !gi.isForegrounded()) { + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); + intent.setClassName(AppConstants.ANDROID_PACKAGE_NAME, AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS); - getApplicationContext().startActivity(intent); + getApplicationContext().startActivity(intent); + } } private static float getLocationAccuracy(Location location) { float radius = location.getAccuracy(); return (location.hasAccuracy() && radius > 0) ? radius : 1001; } @SuppressLint("MissingPermission") // Permissions are explicitly checked for in enableLocation() @@ -1731,16 +1734,17 @@ public class GeckoAppShell public void disableOrientationListener(); public void addAppStateListener(AppStateListener listener); public void removeAppStateListener(AppStateListener listener); public void notifyWakeLockChanged(String topic, String state); public boolean areTabsShown(); public AbsoluteLayout getPluginContainer(); public void notifyCheckUpdateResult(String result); public void invalidateOptionsMenu(); + public boolean isForegrounded(); /** * Create a shortcut -- generally a home-screen icon -- linking the given title to the given URI. * <p> * This method is always invoked on the Gecko thread. * * @param title of URI to link to. * @param URI to link to.
--- a/parser/html/nsHtml5StreamParser.cpp +++ b/parser/html/nsHtml5StreamParser.cpp @@ -1316,17 +1316,20 @@ nsHtml5StreamParser::FlushTreeOpsAndDisa // a mutex mFlushTimer->Cancel(); mFlushTimerArmed = false; } if (mMode == VIEW_SOURCE_HTML || mMode == VIEW_SOURCE_XML) { mTokenizer->FlushViewSource(); } mTreeBuilder->Flush(); - if (NS_FAILED(NS_DispatchToMainThread(mExecutorFlusher))) { + nsCOMPtr<nsIRunnable> runnable(mExecutorFlusher); + if (NS_FAILED(mExecutor->GetDocument()->Dispatch("FlushTreeOpsAndDisarmTimer", + dom::TaskCategory::Other, + runnable.forget()))) { NS_WARNING("failed to dispatch executor flush event"); } } void nsHtml5StreamParser::ParseAvailableData() { NS_ASSERTION(IsParserThread(), "Wrong thread!");
--- a/parser/html/nsHtml5TreeOpExecutor.cpp +++ b/parser/html/nsHtml5TreeOpExecutor.cpp @@ -254,17 +254,19 @@ FlushTimerCallback(nsITimer* aTimer, voi } } void nsHtml5TreeOpExecutor::ContinueInterruptedParsingAsync() { if (!mDocument || !mDocument->IsInBackgroundWindow()) { nsCOMPtr<nsIRunnable> flusher = new nsHtml5ExecutorReflusher(this); - if (NS_FAILED(NS_DispatchToMainThread(flusher))) { + if (NS_FAILED(mDocument->Dispatch("ContinueInterruptedParsingAsync", + dom::TaskCategory::Other, + flusher.forget()))) { NS_WARNING("failed to dispatch executor flush event"); } } else { if (!gBackgroundFlushList) { gBackgroundFlushList = new mozilla::LinkedList<nsHtml5TreeOpExecutor>(); } if (!isInList()) { gBackgroundFlushList->insertBack(this);
deleted file mode 100644 --- a/testing/web-platform/meta/FileAPI/reading-data-section/FileReader-multiple-reads.html.ini +++ /dev/null @@ -1,17 +0,0 @@ -[FileReader-multiple-reads.html] - type: testharness - [test FileReader InvalidStateError exception for readAsText] - expected: FAIL - - [test FileReader InvalidStateError exception for readAsDataURL] - expected: FAIL - - [test FileReader InvalidStateError exception for readAsArrayBuffer] - expected: FAIL - - [test FileReader InvalidStateError exception in onloadstart event for readAsArrayBuffer] - expected: FAIL - - [test FileReader no InvalidStateError exception in onloadstart event for readAsArrayBuffer] - expected: FAIL -
--- a/testing/web-platform/meta/MANIFEST.json +++ b/testing/web-platform/meta/MANIFEST.json @@ -39511,16 +39511,23 @@ ], "fetch/api/redirect/redirect-count-worker.html": [ { "path": "fetch/api/redirect/redirect-count-worker.html", "timeout": "long", "url": "/fetch/api/redirect/redirect-count-worker.html" } ], + "fetch/api/redirect/redirect-count.html": [ + { + "path": "fetch/api/redirect/redirect-count.html", + "timeout": "long", + "url": "/fetch/api/redirect/redirect-count.html" + } + ], "fetch/api/redirect/redirect-referrer-worker.html": [ { "path": "fetch/api/redirect/redirect-referrer-worker.html", "url": "/fetch/api/redirect/redirect-referrer-worker.html" } ], "fetch/api/redirect/redirect-referrer.html": [ {
deleted file mode 100644 --- a/testing/web-platform/meta/fetch/api/redirect/redirect-count.html.ini +++ /dev/null @@ -1,10 +0,0 @@ -[redirect-count.html] - type: testharness - expected: - if not debug and e10s and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86_64") and (bits == 64): TIMEOUT - if not debug and e10s and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86") and (bits == 32): TIMEOUT - [Redirect 308 21 times] - expected: - if not debug and e10s and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86") and (bits == 32): TIMEOUT - if not debug and e10s and (os == "linux") and (version == "Ubuntu 12.04") and (processor == "x86_64") and (bits == 64): TIMEOUT -
--- a/testing/web-platform/mozilla/meta/fetch/api/redirect/redirect-referrer.https.html.ini +++ b/testing/web-platform/mozilla/meta/fetch/api/redirect/redirect-referrer.https.html.ini @@ -1,3 +1,3 @@ [redirect-referrer.https.html] type: testharness - prefs: [security.mixed_content.block_active_content:false, security.mixed_content.block_display_content:false] + prefs: [security.mixed_content.block_active_content:false, security.mixed_content.block_display_content:false, security.mixed_content.send_hsts_priming:false, security.mixed_content.use_hsts:false]
--- a/testing/web-platform/tests/FileAPI/reading-data-section/FileReader-multiple-reads.html +++ b/testing/web-platform/tests/FileAPI/reading-data-section/FileReader-multiple-reads.html @@ -57,17 +57,17 @@ async_test(function() { assert_equals(reader.readyState, FileReader.LOADING, "readyState Must be LOADING") }, 'test FileReader InvalidStateError exception in onloadstart event for readAsArrayBuffer'); async_test(function() { var blob_1 = new Blob(['TEST000000001']) var blob_2 = new Blob(['TEST000000002']) var reader = new FileReader(); reader.onloadend = this.step_func_done(function() { - assert_equals(reader.readyState, FileReader.LOADING, - "readyState must be LOADING") + assert_equals(reader.readyState, FileReader.DONE, + "readyState must be DONE") reader.readAsArrayBuffer(blob_2) assert_equals(reader.readyState, FileReader.LOADING, "readyState Must be LOADING") }); reader.readAsArrayBuffer(blob_1) assert_equals(reader.readyState, FileReader.LOADING, "readyState Must be LOADING") }, 'test FileReader no InvalidStateError exception in onloadstart event for readAsArrayBuffer'); </script>
--- a/testing/web-platform/tests/fetch/api/redirect/redirect-count.html +++ b/testing/web-platform/tests/fetch/api/redirect/redirect-count.html @@ -1,16 +1,17 @@ <!doctype html> <html> <head> <meta charset="utf-8"> <title>Fetch: redirection loop</title> + <meta name="timeout" content="long"> <meta name="author" title="Canon Research France" href="https://www.crf.canon.fr"> <meta name="help" href="https://fetch.spec.whatwg.org/#http-network-or-cache-fetch"> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> </head> <body> <script src="/common/utils.js"></script> <script src="../resources/utils.js"></script> <script src="redirect-count.js"></script> </body> -</html> \ No newline at end of file +</html>
--- a/toolkit/modules/PopupNotifications.jsm +++ b/toolkit/modules/PopupNotifications.jsm @@ -254,20 +254,18 @@ PopupNotifications.prototype = { * @param browser * The browser whose notifications should be searched. If null, the * currently selected browser's notifications will be searched. * * @returns the corresponding Notification object, or null if no such * notification exists. */ getNotification: function PopupNotifications_getNotification(id, browser) { - let n = null; let notifications = this._getNotificationsForBrowser(browser || this.tabbrowser.selectedBrowser); - notifications.some(x => x.id == id && (n = x)); - return n; + return notifications.find(x => x.id == id) || null; }, /** * Adds a new popup notification. * @param browser * The <xul:browser> element associated with the notification. Must not * be null. * @param id
--- a/toolkit/modules/WebChannel.jsm +++ b/toolkit/modules/WebChannel.jsm @@ -171,29 +171,29 @@ this.WebChannel = function(id, originOrP this.id = id; // originOrPermission can be either an nsIURI or a string representing a // permission name. if (typeof originOrPermission == "string") { this._originCheckCallback = requestPrincipal => { // The permission manager operates on domain names rather than true // origins (bug 1066517). To mitigate that, we explicitly check that // the scheme is https://. - let uri = Services.io.newURI(requestPrincipal.origin, null, null); + let uri = Services.io.newURI(requestPrincipal.originNoSuffix, null, null); if (uri.scheme != "https") { return false; } // OK - we have https - now we can check the permission. let perm = Services.perms.testExactPermissionFromPrincipal(requestPrincipal, originOrPermission); return perm == Ci.nsIPermissionManager.ALLOW_ACTION; } } else { // a simple URI, so just check for an exact match. this._originCheckCallback = requestPrincipal => { - return originOrPermission.prePath === requestPrincipal.origin; + return originOrPermission.prePath === requestPrincipal.originNoSuffix; } } this._originOrPermission = originOrPermission; }; this.WebChannel.prototype = { /**
--- a/toolkit/modules/addons/WebRequest.jsm +++ b/toolkit/modules/addons/WebRequest.jsm @@ -717,18 +717,19 @@ HttpObserverManager = { mergeStatus(data, channel, kind); } try { let result = callback(data); if (result && typeof result === "object" && opts.blocking && !AddonManagerPermissions.isHostPermitted(uri.host) - && loadInfo && loadInfo.loadingPrincipal && loadInfo.loadingPrincipal.URI - && !AddonManagerPermissions.isHostPermitted(loadInfo.loadingPrincipal.URI.host)) { + && (!loadInfo || !loadInfo.loadingPrincipal + || !loadInfo.loadingPrincipal.URI + || !AddonManagerPermissions.isHostPermitted(loadInfo.loadingPrincipal.URI.host))) { handlerResults.push({opts, result}); } } catch (e) { Cu.reportError(e); } } } catch (e) { Cu.reportError(e);
--- a/toolkit/modules/addons/WebRequestUpload.jsm +++ b/toolkit/modules/addons/WebRequestUpload.jsm @@ -8,314 +8,525 @@ const EXPORTED_SYMBOLS = ["WebRequestUpl /* exported WebRequestUpload */ const Ci = Components.interfaces; const Cc = Components.classes; const Cu = Components.utils; const Cr = Components.results; +Cu.importGlobalProperties(["TextEncoder"]); + Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/ExtensionUtils.jsm"); + +const { + DefaultMap, +} = ExtensionUtils; + +XPCOMUtils.defineLazyServiceGetter(this, "mimeHeader", "@mozilla.org/network/mime-hdrparam;1", + "nsIMIMEHeaderParam"); + +const BinaryInputStream = Components.Constructor( + "@mozilla.org/binaryinputstream;1", "nsIBinaryInputStream", + "setInputStream"); +const ConverterInputStream = Components.Constructor( + "@mozilla.org/intl/converter-input-stream;1", "nsIConverterInputStream", + "init"); + var WebRequestUpload; -function rewind(stream) { - try { - if (stream instanceof Ci.nsISeekableStream) { - stream.seek(0, 0); +/** + * Parses the given raw header block, and stores the value of each + * lower-cased header name in the resulting map. + */ +class Headers extends Map { + constructor(headerText) { + super(); + + if (headerText) { + this.parseHeaders(headerText); + } + } + + parseHeaders(headerText) { + let lines = headerText.split("\r\n"); + + let lastHeader; + for (let line of lines) { + // The first empty line indicates the end of the header block. + if (line === "") { + return; + } + + // Lines starting with whitespace are appended to the previous + // header. + if (/^\s/.test(line)) { + if (lastHeader) { + let val = this.get(lastHeader); + this.set(lastHeader, `${val}\r\n${line}`); + } + continue; + } + + let match = /^(.*?)\s*:\s+(.*)/.exec(line); + if (match) { + lastHeader = match[1].toLowerCase(); + this.set(lastHeader, match[2]); + } } - } catch (e) { - // It might be already closed, e.g. because of a previous error. + } + + /** + * If the given header exists, and contains the given parameter, + * returns the value of that parameter. + * + * @param {string} name + * The lower-cased header name. + * @param {string} paramName + * The name of the parameter to retrieve, or empty to retrieve + * the first (possibly unnamed) parameter. + * @returns {string | null} + */ + getParam(name, paramName) { + return Headers.getParam(this.get(name), paramName); + } + + /** + * If the given header value is non-null, and contains the given + * parameter, returns the value of that parameter. + * + * @param {string | null} header + * The text of the header from which to retrieve the param. + * @param {string} paramName + * The name of the parameter to retrieve, or empty to retrieve + * the first (possibly unnamed) parameter. + * @returns {string | null} + */ + static getParam(header, paramName) { + if (header) { + // The service expects this to be a raw byte string, so convert to + // UTF-8. + let bytes = new TextEncoder().encode(header); + let binHeader = String.fromCharCode(...bytes); + + return mimeHeader.getParameterHTTP(binHeader, paramName, null, + false, {}); + } + + return null; } } -function parseFormData(stream, channel, lenient = false) { - const BUFFER_SIZE = 8192; // Empirically it seemed a good compromise. +/** + * Creates a new Object with a corresponding property for every + * key-value pair in the given Map. + * + * @param {Map} map + * The map to convert. + * @returns {Object} + */ +function mapToObject(map) { + let result = {}; + for (let [key, value] of map) { + result[key] = value; + } + return result; +} - let mimeStream = null; +/** + * Rewinds the given seekable input stream to its beginning, and catches + * any resulting errors. + * + * @param {nsISeekableStream} stream + * The stream to rewind. + */ +function rewind(stream) { + // Do this outside the try-catch so that we throw if the stream is not + // actually seekable. + stream.QueryInterface(Ci.nsISeekableStream); + + try { + stream.seek(0, 0); + } catch (e) { + // It might be already closed, e.g. because of a previous error. + Cu.reportError(e); + } +} - if (stream instanceof Ci.nsIMIMEInputStream && stream.data) { - mimeStream = stream; - stream = stream.data; +/** + * Iterates over all of the sub-streams that make up the given stream, + * or yields the stream itself if it is not a multi-part stream. + * + * @param {nsIIMultiplexInputStream|nsIStreamBufferAccess<nsIMultiplexInputStream>|nsIInputStream} outerStream + * The outer stream over which to iterate. + */ +function* getStreams(outerStream) { + // If this is a multi-part stream, we need to iterate over its sub-streams, + // rather than treating it as a simple input stream. Since it may be wrapped + // in a buffered input stream, unwrap it before we do any checks. + let unbuffered = outerStream; + if (outerStream instanceof Ci.nsIStreamBufferAccess) { + unbuffered = outerStream.unbufferedStream; } - let multiplexStream = null; - if (stream instanceof Ci.nsIMultiplexInputStream) { - multiplexStream = stream; + + if (unbuffered instanceof Ci.nsIMultiplexInputStream) { + let count = unbuffered.count; + for (let i = 0; i < count; i++) { + yield unbuffered.getStream(i); + } + } else { + yield outerStream; } +} + +/** + * Parses the form data of the given stream as either multipart/form-data or + * x-www-form-urlencoded, and returns a map of its fields. + * + * @param {nsIInputStream} stream + * The input stream from which to parse the form data. + * @param {nsIHttpChannel} channel + * The channel to which the stream belongs. + * @param {boolean} [lenient = false] + * If true, the operation will succeed even if there are UTF-8 + * decoding errors. + * + * @returns {Map<string, Array<string>> | null} + */ +function parseFormData(stream, channel, lenient = false) { + const BUFFER_SIZE = 8192; let touchedStreams = new Set(); + /** + * Creates a converter input stream from the given raw input stream, + * and adds it to the list of streams to be rewound at the end of + * parsing. + * + * Returns null if the given raw stream cannot be rewound. + * + * @param {nsIInputStream} stream + * The base stream from which to create a converter. + * @returns {ConverterInputStream | null} + */ function createTextStream(stream) { - let textStream = Cc["@mozilla.org/intl/converter-input-stream;1"].createInstance(Ci.nsIConverterInputStream); - textStream.init(stream, "UTF-8", 0, lenient ? textStream.DEFAULT_REPLACEMENT_CHARACTER : 0); - if (stream instanceof Ci.nsISeekableStream) { - touchedStreams.add(stream); + if (!(stream instanceof Ci.nsISeekableStream)) { + return null; } - return textStream; - } - let streamIdx = 0; - function nextTextStream() { - for (; streamIdx < multiplexStream.count;) { - let currentStream = multiplexStream.getStream(streamIdx++); - if (currentStream instanceof Ci.nsIStringInputStream) { - touchedStreams.add(multiplexStream); - return createTextStream(currentStream); - } - } - return null; - } - - let textStream; - if (multiplexStream) { - textStream = nextTextStream(); - } else { - textStream = createTextStream(mimeStream || stream); - } - - if (!textStream) { - return null; + touchedStreams.add(stream); + return ConverterInputStream( + stream, "UTF-8", 0, + lenient ? Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER + : 0); } - function readString() { - if (textStream) { - let textBuffer = {}; - textStream.readString(BUFFER_SIZE, textBuffer); - return textBuffer.value; - } - return ""; + /** + * Reads a string of no more than the given length from the given text + * stream. + * + * @param {ConverterInputStream} stream + * The stream to read. + * @param {integer} [length = BUFFER_SIZE] + * The maximum length of data to read. + * @returns {string} + */ + function readString(stream, length = BUFFER_SIZE) { + let data = {}; + stream.readString(length, data); + return data.value; } - function multiplexRead() { - let str = readString(); - if (!str) { - textStream = nextTextStream(); - if (textStream) { - str = multiplexRead(); + /** + * Iterates over all of the sub-streams of the given (possibly multi-part) + * input stream, and yields a ConverterInputStream for each + * nsIStringInputStream among them. + * + * @param {nsIInputStream|nsIMultiplexInputStream} outerStream + * The multi-part stream over which to iterate. + */ + function* getTextStreams(outerStream) { + for (let stream of getStreams(outerStream)) { + if (stream instanceof Ci.nsIStringInputStream) { + touchedStreams.add(outerStream); + yield createTextStream(stream); } } - return str; } - let readChunk; - if (multiplexStream) { - readChunk = multiplexRead; - } else { - readChunk = readString; - } - - function appendFormData(formData, name, value) { - if (name in formData) { - formData[name].push(value); - } else { - formData[name] = [value]; + /** + * Iterates over all of the string streams of the given (possibly + * multi-part) input stream, and yields all of the available data in each as + * chunked strings, each no more than BUFFER_SIZE in length. + * + * @param {nsIInputStream|nsIMultiplexInputStream} outerStream + * The multi-part stream over which to iterate. + */ + function* readAllStrings(outerStream) { + for (let textStream of getTextStreams(outerStream)) { + let str; + while ((str = readString(textStream))) { + yield str; + } } } - function parseMultiPart(firstChunk, boundary = "") { - let formData = Object.create(null); + /** + * Iterates over the text contents of all of the string streams in the given + * (possibly multi-part) input stream, splits them at occurrences of the + * given boundary string, and yields each part. + * + * @param {nsIInputStream|nsIMultiplexInputStream} stream + * The multi-part stream over which to iterate. + * @param {string} boundary + * The boundary at which to split the parts. + * @param {string} [tail = ""] + * Any initial data to prepend to the start of the stream data. + */ + function* getParts(stream, boundary, tail = "") { + for (let chunk of readAllStrings(stream)) { + chunk = tail + chunk; - if (!boundary) { - let match = firstChunk.match(/^--\S+/); - if (!match) { - return null; - } - boundary = match[0]; + let parts = chunk.split(boundary); + tail = parts.pop(); + + yield* parts; } - let unslash = (s) => s.replace(/\\"/g, '"'); - let tail = ""; - for (let chunk = firstChunk; - chunk || tail; - chunk = readChunk()) { - let parts; - if (chunk) { - chunk = tail + chunk; - parts = chunk.split(boundary); - tail = parts.pop(); - } else { - parts = [tail]; - tail = ""; + if (tail) { + yield tail; + } + } + + /** + * Parses the given stream as multipart/form-data and returns a map of its fields. + * + * @param {nsIMultiplexInputStream|nsIInputStream} stream + * The (possibly multi-part) stream to parse. + * @param {string} boundary + * The boundary at which to split the parts. + * @returns {Map<string, Array<string>>} + */ + function parseMultiPart(stream, boundary) { + let formData = new DefaultMap(() => []); + + for (let part of getParts(stream, boundary, "\r\n")) { + if (part === "") { + // The first part will always be empty. + continue; + } + if (part === "--\r\n") { + // This indicates the end of the stream. + break; } - for (let part of parts) { - let match = part.match(/^\r\nContent-Disposition: form-data; name="(.*)"\r\n(?:Content-Type: (\S+))?.*\r\n/i); - if (!match) { - continue; - } - let [header, name, contentType] = match; - if (contentType) { - let fileName; - // Since escaping inside Content-Disposition subfields is still poorly defined and buggy (see Bug 136676), - // currently we always consider backslash-prefixed quotes as escaped even if that's not generally true - // (i.e. in a field whose value actually ends with a backslash). - // Therefore in this edge case we may end coalescing name and filename, which is marginally better than - // potentially truncating the name field at the wrong point, at least from a XSS filter POV. - match = name.match(/^(.*[^\\])"; filename="(.*)/); - if (match) { - [, name, fileName] = match; - } - appendFormData(formData, unslash(name), fileName ? unslash(fileName) : ""); - } else { - appendFormData(formData, unslash(name), part.slice(header.length, -2)); - } + let end = part.indexOf("\r\n\r\n"); + + // All valid parts must begin with \r\n, and we can't process form + // fields without any header block. + if (!part.startsWith("\r\n") || end <= 0) { + throw new Error("Invalid MIME stream"); } + + let content = part.slice(end + 4); + let headerText = part.slice(2, end); + let headers = new Headers(headerText); + + let name = headers.getParam("content-disposition", "name"); + if (!name || headers.getParam("content-disposition", "") !== "form-data") { + throw new Error("Invalid MIME stream: No valid Content-Disposition header"); + } + + if (headers.has("content-type")) { + // For file upload fields, we return the filename, rather than the + // file data. + let filename = headers.getParam("content-disposition", "filename"); + content = filename || ""; + } + formData.get(name).push(content); } return formData; } - function parseUrlEncoded(firstChunk) { - let formData = Object.create(null); + /** + * Parses the given stream as x-www-form-urlencoded, and returns a map of its fields. + * + * @param {nsIInputStream} stream + * The stream to parse. + * @returns {Map<string, Array<string>>} + */ + function parseUrlEncoded(stream) { + let formData = new DefaultMap(() => []); - let tail = ""; - for (let chunk = firstChunk; - chunk || tail; - chunk = readChunk()) { - let pairs; - if (chunk) { - chunk = tail + chunk.trim(); - pairs = chunk.split("&"); - tail = pairs.pop(); - } else { - chunk = tail; - tail = ""; - pairs = [chunk]; - } - for (let pair of pairs) { - let [name, value] = pair.replace(/\+/g, " ").split("=").map(decodeURIComponent); - appendFormData(formData, name, value); - } + for (let part of getParts(stream, "&")) { + let [name, value] = part.replace(/\+/g, " ").split("=").map(decodeURIComponent); + formData.get(name).push(value); } return formData; } try { - let chunk = readChunk(); + let headers; + if (stream instanceof Ci.nsIMIMEInputStream && stream.data) { + // MIME input streams encode additional headers as a block at the + // beginning of their stream. The actual request data comes from a + // sub-stream, which is accessible via their `data` member. The + // difference in available bytes between the outer stream and the + // inner data stream tells us the size of that header block. + // + // Since we need to know at least the value of the Content-Type + // header to properly parse the request body, we need to read and + // parse the header block in order to extract it. - if (multiplexStream) { - touchedStreams.add(multiplexStream); - return parseMultiPart(chunk); - } - let contentType; - if (/^Content-Type:/i.test(chunk)) { - contentType = chunk.replace(/^Content-Type:\s*/i, ""); - chunk = chunk.slice(chunk.indexOf("\r\n\r\n") + 4); - } else { - try { - contentType = channel.getRequestHeader("Content-Type"); - } catch (e) { - Cu.reportError(e); - return null; - } + headers = readString(createTextStream(stream), + stream.available() - stream.data.available()); + + rewind(stream); + stream = stream.data; } - let match = contentType.match(/^(?:multipart\/form-data;\s*boundary=(\S*)|application\/x-www-form-urlencoded\s)/i); - if (match) { - let boundary = match[1]; - if (boundary) { - return parseMultiPart(chunk, boundary); - } - return parseUrlEncoded(chunk); + let contentType; + try { + contentType = channel.getRequestHeader("Content-Type"); + } catch (e) { + contentType = new Headers(headers).get("content-type"); + } + + switch (Headers.getParam(contentType, "")) { + case "multipart/form-data": + let boundary = Headers.getParam(contentType, "boundary"); + return parseMultiPart(stream, `\r\n--${boundary}`); + + case "application/x-www-form-urlencoded": + return parseUrlEncoded(stream); } } finally { for (let stream of touchedStreams) { rewind(stream); } } return null; } -function createFormData(stream, channel) { +/** + * Parses the form data of the given stream as either multipart/form-data or + * x-www-form-urlencoded, and returns a map of its fields. + * + * Returns null if the stream is not seekable. + * + * @param {nsIMultiplexInputStream|nsIInputStream} stream + * The (possibly multi-part) stream from which to create the form data. + * @param {nsIChannel} channel + * The channel to which the stream belongs. + * @param {boolean} [lenient = false] + * If true, the operation will succeed even if there are UTF-8 + * decoding errors. + * @returns {Map<string, Array<string>> | null} + */ +function createFormData(stream, channel, lenient) { + if (!(stream instanceof Ci.nsISeekableStream)) { + return null; + } + try { - rewind(stream); - return parseFormData(stream, channel); + let formData = parseFormData(stream, channel, lenient); + if (formData) { + return mapToObject(formData); + } } catch (e) { Cu.reportError(e); } finally { rewind(stream); } return null; } -function convertRawData(outerStream) { - let raw = []; - let totalBytes = 0; - - // Here we read the stream up to WebRequestUpload.MAX_RAW_BYTES, returning false if we had to truncate the result. - function readAll(stream) { - let unbuffered = stream.unbufferedStream || stream; - if (unbuffered instanceof Ci.nsIFileInputStream) { - raw.push({file: "<file>"}); // Full paths not supported yet for naked files (follow up bug) - return true; +/** + * Iterates over all of the sub-streams of the given (possibly multi-part) + * input stream, and yields an object containing the data for each chunk, up + * to a total of `maxRead` bytes. + * + * @param {nsIMultiplexInputStream|nsIInputStream} outerStream + * The stream for which to return data. + * @param {integer} [maxRead = WebRequestUpload.MAX_RAW_BYTES] + * The maximum total bytes to read. + */ +function* getRawDataChunked(outerStream, maxRead = WebRequestUpload.MAX_RAW_BYTES) { + for (let stream of getStreams(outerStream)) { + // We need to inspect the stream to make sure it's not a file input + // stream. If it's wrapped in a buffered input stream, unwrap it first, + // so we can inspect the inner stream directly. + let unbuffered = stream; + if (stream instanceof Ci.nsIStreamBufferAccess) { + unbuffered = stream.unbufferedStream; } - rewind(stream); - let binaryStream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIBinaryInputStream); - binaryStream.setInputStream(stream); - const MAX_BYTES = WebRequestUpload.MAX_RAW_BYTES; + // For file fields, we return an object containing the full path of + // the file, rather than its data. + if (unbuffered instanceof Ci.nsIFileInputStream) { + // But this is not actually supported yet. + yield {file: "<file>"}; + continue; + } + try { - for (let available; (available = binaryStream.available());) { - let size = Math.min(MAX_BYTES - totalBytes, available); - let bytes = new ArrayBuffer(size); - binaryStream.readArrayBuffer(size, bytes); - let chunk = {bytes}; - raw.push(chunk); - totalBytes += size; + let binaryStream = BinaryInputStream(stream); + let available; + while ((available = binaryStream.available())) { + let buffer = new ArrayBuffer(Math.min(maxRead, available)); + binaryStream.readArrayBuffer(buffer.byteLength, buffer); + + maxRead -= buffer.byteLength; + + let chunk = {bytes: buffer}; - if (totalBytes >= MAX_BYTES) { - if (size < available) { - chunk.truncated = true; - chunk.originalSize = available; - return false; - } - break; + if (buffer.byteLength < available) { + chunk.truncated = true; + chunk.originalSize = available; + } + + yield chunk; + + if (maxRead <= 0) { + return; } } } finally { rewind(stream); } - return true; } - - let unbuffered = outerStream; - if (outerStream instanceof Ci.nsIStreamBufferAccess) { - unbuffered = outerStream.unbufferedStream; - } - - if (unbuffered instanceof Ci.nsIMultiplexInputStream) { - for (let i = 0, count = unbuffered.count; i < count; i++) { - if (!readAll(unbuffered.getStream(i))) { - break; - } - } - } else { - readAll(outerStream); - } - - return raw; } WebRequestUpload = { createRequestBody(channel) { - let requestBody = null; if (channel instanceof Ci.nsIUploadChannel && channel.uploadStream) { try { - let stream = channel.uploadStream.QueryInterface(Ci.nsISeekableStream); + let stream = channel.uploadStream; + let formData = createFormData(stream, channel); if (formData) { - requestBody = {formData}; - } else { - requestBody = {raw: convertRawData(stream), lenientFormData: createFormData(stream, channel, true)}; + return {formData}; } + + // If we failed to parse the stream as form data, return it as a + // sequence of raw data chunks, along with a leniently-parsed form + // data object, which ignores encoding errors. + return { + raw: Array.from(getRawDataChunked(stream)), + lenientFormData: createFormData(stream, channel, true), + }; } catch (e) { Cu.reportError(e); - requestBody = {error: e.message || String(e)}; + return {error: e.message || String(e)}; } - requestBody = Object.freeze(requestBody); } - return requestBody; + + return null; }, }; -XPCOMUtils.defineLazyPreferenceGetter(WebRequestUpload, "MAX_RAW_BYTES", "webextensions.webRequest.requestBodyMaxRawBytes"); +XPCOMUtils.defineLazyPreferenceGetter(WebRequestUpload, "MAX_RAW_BYTES", + "webextensions.webRequest.requestBodyMaxRawBytes");
--- a/widget/android/GeneratedJNIWrappers.cpp +++ b/widget/android/GeneratedJNIWrappers.cpp @@ -522,16 +522,24 @@ auto GeckoAppShell::IsTablet() -> bool constexpr char GeckoAppShell::KillAnyZombies_t::name[]; constexpr char GeckoAppShell::KillAnyZombies_t::signature[]; auto GeckoAppShell::KillAnyZombies() -> void { return mozilla::jni::Method<KillAnyZombies_t>::Call(GeckoAppShell::Context(), nullptr); } +constexpr char GeckoAppShell::LaunchOrBringToFront_t::name[]; +constexpr char GeckoAppShell::LaunchOrBringToFront_t::signature[]; + +auto GeckoAppShell::LaunchOrBringToFront() -> void +{ + return mozilla::jni::Method<LaunchOrBringToFront_t>::Call(GeckoAppShell::Context(), nullptr); +} + constexpr char GeckoAppShell::LoadPluginClass_t::name[]; constexpr char GeckoAppShell::LoadPluginClass_t::signature[]; auto GeckoAppShell::LoadPluginClass(mozilla::jni::String::Param a0, mozilla::jni::String::Param a1) -> mozilla::jni::Class::LocalRef { return mozilla::jni::Method<LoadPluginClass_t>::Call(GeckoAppShell::Context(), nullptr, a0, a1); } @@ -588,24 +596,16 @@ constexpr char GeckoAppShell::OnSensorCh constexpr char GeckoAppShell::OpenUriExternal_t::name[]; constexpr char GeckoAppShell::OpenUriExternal_t::signature[]; auto GeckoAppShell::OpenUriExternal(mozilla::jni::String::Param a0, mozilla::jni::String::Param a1, mozilla::jni::String::Param a2, mozilla::jni::String::Param a3, mozilla::jni::String::Param a4, mozilla::jni::String::Param a5) -> bool { return mozilla::jni::Method<OpenUriExternal_t>::Call(GeckoAppShell::Context(), nullptr, a0, a1, a2, a3, a4, a5); } -constexpr char GeckoAppShell::OpenWindowForNotification_t::name[]; -constexpr char GeckoAppShell::OpenWindowForNotification_t::signature[]; - -auto GeckoAppShell::OpenWindowForNotification() -> void -{ - return mozilla::jni::Method<OpenWindowForNotification_t>::Call(GeckoAppShell::Context(), nullptr); -} - constexpr char GeckoAppShell::PerformHapticFeedback_t::name[]; constexpr char GeckoAppShell::PerformHapticFeedback_t::signature[]; auto GeckoAppShell::PerformHapticFeedback(bool a0) -> void { return mozilla::jni::Method<PerformHapticFeedback_t>::Call(GeckoAppShell::Context(), nullptr, a0); }
--- a/widget/android/GeneratedJNIWrappers.h +++ b/widget/android/GeneratedJNIWrappers.h @@ -1421,16 +1421,35 @@ public: static const mozilla::jni::CallingThread callingThread = mozilla::jni::CallingThread::GECKO; static const mozilla::jni::DispatchTarget dispatchTarget = mozilla::jni::DispatchTarget::CURRENT; }; static auto KillAnyZombies() -> void; + struct LaunchOrBringToFront_t { + typedef GeckoAppShell Owner; + typedef void ReturnType; + typedef void SetterType; + typedef mozilla::jni::Args<> Args; + static constexpr char name[] = "launchOrBringToFront"; + static constexpr char signature[] = + "()V"; + static const bool isStatic = true; + static const mozilla::jni::ExceptionMode exceptionMode = + mozilla::jni::ExceptionMode::ABORT; + static const mozilla::jni::CallingThread callingThread = + mozilla::jni::CallingThread::ANY; + static const mozilla::jni::DispatchTarget dispatchTarget = + mozilla::jni::DispatchTarget::CURRENT; + }; + + static auto LaunchOrBringToFront() -> void; + struct LoadPluginClass_t { typedef GeckoAppShell Owner; typedef mozilla::jni::Class::LocalRef ReturnType; typedef mozilla::jni::Class::Param SetterType; typedef mozilla::jni::Args< mozilla::jni::String::Param, mozilla::jni::String::Param> Args; static constexpr char name[] = "loadPluginClass"; @@ -1670,35 +1689,16 @@ public: static const mozilla::jni::CallingThread callingThread = mozilla::jni::CallingThread::GECKO; static const mozilla::jni::DispatchTarget dispatchTarget = mozilla::jni::DispatchTarget::CURRENT; }; static auto OpenUriExternal(mozilla::jni::String::Param, mozilla::jni::String::Param, mozilla::jni::String::Param, mozilla::jni::String::Param, mozilla::jni::String::Param, mozilla::jni::String::Param) -> bool; - struct OpenWindowForNotification_t { - typedef GeckoAppShell Owner; - typedef void ReturnType; - typedef void SetterType; - typedef mozilla::jni::Args<> Args; - static constexpr char name[] = "openWindowForNotification"; - static constexpr char signature[] = - "()V"; - static const bool isStatic = true; - static const mozilla::jni::ExceptionMode exceptionMode = - mozilla::jni::ExceptionMode::ABORT; - static const mozilla::jni::CallingThread callingThread = - mozilla::jni::CallingThread::ANY; - static const mozilla::jni::DispatchTarget dispatchTarget = - mozilla::jni::DispatchTarget::CURRENT; - }; - - static auto OpenWindowForNotification() -> void; - struct PerformHapticFeedback_t { typedef GeckoAppShell Owner; typedef void ReturnType; typedef void SetterType; typedef mozilla::jni::Args< bool> Args; static constexpr char name[] = "performHapticFeedback"; static constexpr char signature[] =
--- a/widget/nsDeviceContextSpecProxy.cpp +++ b/widget/nsDeviceContextSpecProxy.cpp @@ -9,21 +9,18 @@ #include "gfxASurface.h" #include "gfxPlatform.h" #include "mozilla/gfx/DrawEventRecorder.h" #include "mozilla/gfx/PrintTargetThebes.h" #include "mozilla/layout/RemotePrintJobChild.h" #include "mozilla/RefPtr.h" #include "mozilla/Unused.h" #include "nsComponentManagerUtils.h" -#include "nsAppDirectoryServiceDefs.h" -#include "nsDirectoryServiceUtils.h" #include "nsIPrintSession.h" #include "nsIPrintSettings.h" -#include "nsIUUIDGenerator.h" using mozilla::Unused; using namespace mozilla; using namespace mozilla::gfx; NS_IMPL_ISUPPORTS(nsDeviceContextSpecProxy, nsIDeviceContextSpec) @@ -132,47 +129,17 @@ nsDeviceContextSpecProxy::GetPrintingSca return mRealDeviceContextSpec->GetPrintingScale(); } NS_IMETHODIMP nsDeviceContextSpecProxy::BeginDocument(const nsAString& aTitle, const nsAString& aPrintToFileName, int32_t aStartPage, int32_t aEndPage) { - nsCOMPtr<nsIFile> recordingFile; - nsresult rv = NS_GetSpecialDirectory(NS_APP_CONTENT_PROCESS_TEMP_DIR, - getter_AddRefs(recordingFile)); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsCOMPtr<nsIUUIDGenerator> uuidgen = - do_GetService("@mozilla.org/uuid-generator;1", &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsID uuid; - rv = uuidgen->GenerateUUIDInPlace(&uuid); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - char uuidChars[NSID_LENGTH]; - uuid.ToProvidedString(uuidChars); - mRecorderFile.AssignASCII(uuidChars); - rv = recordingFile->AppendNative(mRecorderFile); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsAutoCString recordingPath; - rv = recordingFile->GetNativePath(recordingPath); - - mRecorder = new mozilla::gfx::DrawEventRecorderFile(recordingPath.get()); + mRecorder = new mozilla::gfx::DrawEventRecorderMemory(); return mRemotePrintJob->InitializePrint(nsString(aTitle), nsString(aPrintToFileName), aStartPage, aEndPage); } NS_IMETHODIMP nsDeviceContextSpecProxy::EndDocument() { @@ -185,23 +152,39 @@ nsDeviceContextSpecProxy::AbortDocument( { Unused << mRemotePrintJob->SendAbortPrint(NS_OK); return NS_OK; } NS_IMETHODIMP nsDeviceContextSpecProxy::BeginPage() { - // Reopen the file, if necessary, ready for the next page. - mRecorder->OpenAndTruncate(); - return NS_OK; } NS_IMETHODIMP nsDeviceContextSpecProxy::EndPage() { + // Save the current page recording to shared memory. + mozilla::ipc::Shmem storedPage; + size_t recordingSize = mRecorder->RecordingSize(); + if (!mRemotePrintJob->AllocShmem(recordingSize, + mozilla::ipc::SharedMemory::TYPE_BASIC, + &storedPage)) { + NS_WARNING("Failed to create shared memory for remote printing."); + return NS_ERROR_FAILURE; + } + + bool success = mRecorder->CopyRecording(storedPage.get<char>(), recordingSize); + if (!success) { + NS_WARNING("Copying recording to shared memory was not succesful."); + return NS_ERROR_FAILURE; + } + + // Wipe the recording to free memory. The recorder does not forget which data + // backed objects that it has stored. + mRecorder->WipeRecording(); + // Send the page recording to the parent. - mRecorder->Close(); - mRemotePrintJob->ProcessPage(mRecorderFile); + mRemotePrintJob->ProcessPage(storedPage); return NS_OK; }
--- a/widget/nsDeviceContextSpecProxy.h +++ b/widget/nsDeviceContextSpecProxy.h @@ -4,23 +4,22 @@ * 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/. */ #ifndef nsDeviceContextSpecProxy_h #define nsDeviceContextSpecProxy_h #include "nsIDeviceContextSpec.h" #include "nsCOMPtr.h" -#include "nsString.h" class nsIPrintSession; namespace mozilla { namespace gfx { -class DrawEventRecorderFile; +class DrawEventRecorderMemory; } namespace layout { class RemotePrintJobChild; } } class nsDeviceContextSpecProxy final : public nsIDeviceContextSpec @@ -53,13 +52,12 @@ public: private: ~nsDeviceContextSpecProxy() {} nsCOMPtr<nsIPrintSettings> mPrintSettings; nsCOMPtr<nsIPrintSession> mPrintSession; nsCOMPtr<nsIDeviceContextSpec> mRealDeviceContextSpec; RefPtr<mozilla::layout::RemotePrintJobChild> mRemotePrintJob; - RefPtr<mozilla::gfx::DrawEventRecorderFile> mRecorder; - nsCString mRecorderFile; + RefPtr<mozilla::gfx::DrawEventRecorderMemory> mRecorder; }; #endif // nsDeviceContextSpecProxy_h
--- a/xpcom/io/nsAppDirectoryServiceDefs.h +++ b/xpcom/io/nsAppDirectoryServiceDefs.h @@ -105,14 +105,11 @@ // // New code should avoid writing to the filesystem from the content process // and should instead proxy through the parent process whenever possible. // // At present, all sandboxed content processes use the same directory for // NS_APP_CONTENT_PROCESS_TEMP_DIR, but that should not be relied upon. // #define NS_APP_CONTENT_PROCESS_TEMP_DIR "ContentTmpD" -#else -// Otherwise NS_APP_CONTENT_PROCESS_TEMP_DIR must match NS_OS_TEMP_DIR. -#define NS_APP_CONTENT_PROCESS_TEMP_DIR "TmpD" #endif // (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX) #endif // nsAppDirectoryServiceDefs_h___