author | Carsten "Tomcat" Book <cbook@mozilla.com> |
Wed, 26 Jul 2017 11:11:40 +0200 | |
changeset 371046 | e8400551c2e39f24c75a009ebed496c7acd7bf47 |
parent 371045 | 4f821dab4306fd0aff947ce55a1304acd7e4cf95 (current diff) |
parent 370956 | 5697f69b1426ffc358a8b9f7f1b209057a9a7782 (diff) |
child 371047 | e9224528ba04e008087766d6bc2d0e10eea4c195 |
child 371077 | 0d4a575f389cbc978679ef9001a079c6d9944eb6 |
child 371154 | 360af4185a25d3bb4d7bea43f61a24814ec3c7c4 |
push id | 92990 |
push user | cbook@mozilla.com |
push date | Wed, 26 Jul 2017 09:16:20 +0000 |
treeherder | mozilla-inbound@e9224528ba04 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 56.0a1 |
first release with | nightly linux32
e8400551c2e3
/
56.0a1
/
20170726100241
/
files
nightly linux64
e8400551c2e3
/
56.0a1
/
20170726100241
/
files
nightly mac
e8400551c2e3
/
56.0a1
/
20170726100322
/
files
nightly win32
e8400551c2e3
/
56.0a1
/
20170726030207
/
files
nightly win64
e8400551c2e3
/
56.0a1
/
20170726030207
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
56.0a1
/
20170726100241
/
pushlog to previous
nightly linux64
56.0a1
/
20170726100241
/
pushlog to previous
nightly mac
56.0a1
/
20170726100322
/
pushlog to previous
nightly win32
56.0a1
/
20170726030207
/
pushlog to previous
nightly win64
56.0a1
/
20170726030207
/
pushlog to previous
|
--- a/accessible/generic/DocAccessible.cpp +++ b/accessible/generic/DocAccessible.cpp @@ -2102,21 +2102,41 @@ DocAccessible::DoARIAOwnsRelocation(Acce } #ifdef A11Y_LOG logging::TreeInfo("aria owns traversal", logging::eVerbose, "candidate", child, nullptr); #endif // Same child on same position, no change. - if (child->Parent() == aOwner && - child->IndexInParent() == static_cast<int32_t>(insertIdx)) { - MOZ_ASSERT(owned->ElementAt(idx) == child, "Not in sync!"); - idx++; - continue; + if (child->Parent() == aOwner) { + int32_t indexInParent = child->IndexInParent(); + + // The child is being placed in its current index, + // eg. aria-owns='id1 id2 id3' is changed to aria-owns='id3 id2 id1'. + if (indexInParent == static_cast<int32_t>(insertIdx)) { + MOZ_ASSERT(child->IsRelocated(), + "A child, having an index in parent from aria ownded indices range, has to be aria owned"); + MOZ_ASSERT(owned->ElementAt(idx) == child, + "Unexpected child in ARIA owned array"); + idx++; + continue; + } + + // The child is being inserted directly after its current index, + // resulting in a no-move case. This will happen when a parent aria-owns + // its last ordinal child: + // <ul aria-owns='id2'><li id='id1'></li><li id='id2'></li></ul> + if (indexInParent == static_cast<int32_t>(insertIdx) - 1) { + MOZ_ASSERT(!child->IsRelocated(), "Child should be in its ordinal position"); + child->SetRelocated(true); + owned->InsertElementAt(idx, child); + idx++; + continue; + } } MOZ_ASSERT(owned->SafeElementAt(idx) != child, "Already in place!"); if (owned->IndexOf(child) < idx) { continue; // ignore second entry of same ID } @@ -2185,17 +2205,33 @@ DocAccessible::PutChildrenBack(nsTArray< MOZ_DIAGNOSTIC_ASSERT(origContainer == prevChild->Parent(), "Broken tree"); origContainer = prevChild->Parent(); } else { idxInParent = 0; } } } - MoveChild(child, origContainer, idxInParent); + + // The child may have already be in its ordinal place for 2 reasons: + // 1. It was the last ordinal child, and the first aria-owned child. + // given: <ul id="list" aria-owns="b"><li id="a"></li><li id="b"></li></ul> + // after load: $("list").setAttribute("aria-owns", ""); + // 2. The preceding adopted children were just reclaimed, eg: + // given: <ul id="list"><li id="b"></li></ul> + // after load: $("list").setAttribute("aria-owns", "a b"); + // later: $("list").setAttribute("aria-owns", ""); + if (origContainer != owner || child->IndexInParent() != idxInParent) { + MoveChild(child, origContainer, idxInParent); + } else { + MOZ_ASSERT(!child->PrevSibling() || !child->PrevSibling()->IsRelocated(), + "No relocated child should appear before this one"); + MOZ_ASSERT(!child->NextSibling() || child->NextSibling()->IsRelocated(), + "No ordinal child should appear after this one"); + } } aChildren->RemoveElementsAt(aStartIdx, aChildren->Length() - aStartIdx); } bool DocAccessible::MoveChild(Accessible* aChild, Accessible* aNewParent, int32_t aIdxInParent)
--- a/accessible/moz.build +++ b/accessible/moz.build @@ -31,13 +31,14 @@ if CONFIG['MOZ_XUL']: TEST_DIRS += ['tests/mochitest'] BROWSER_CHROME_MANIFESTS += [ 'tests/browser/bounds/browser.ini', 'tests/browser/browser.ini', 'tests/browser/e10s/browser.ini', 'tests/browser/events/browser.ini', 'tests/browser/scroll/browser.ini', - 'tests/browser/states/browser.ini' + 'tests/browser/states/browser.ini', + 'tests/browser/tree/browser.ini' ] with Files("**"): BUG_COMPONENT = ("Core", "Disability Access APIs")
--- a/accessible/tests/browser/events.js +++ b/accessible/tests/browser/events.js @@ -155,32 +155,34 @@ class UnexpectedEvents { } /** * A helper function that waits for a sequence of accessible events in * specified order. * @param {Array} events a list of events to wait (same format as * waitForEvent arguments) */ -function waitForEvents(events, unexpected = [], ordered = false) { +function waitForEvents(events, ordered = false) { + let expected = events.expected || events; + let unexpected = events.unexpected || []; // Next expected event index. let currentIdx = 0; let unexpectedListener = new UnexpectedEvents(unexpected); - return Promise.all(events.map((evt, idx) => { + return Promise.all(expected.map((evt, idx) => { let promise = evt instanceof Array ? waitForEvent(...evt) : evt; return promise.then(result => { if (ordered) { is(idx, currentIdx++, `Unexpected event order: ${result}`); } return result; }); })).then(results => { unexpectedListener.stop(); return results; }); } -function waitForOrderedEvents(events, unexpected = []) { - return waitForEvents(events, unexpected, true); +function waitForOrderedEvents(events) { + return waitForEvents(events, true); }
--- a/accessible/tests/browser/shared-head.js +++ b/accessible/tests/browser/shared-head.js @@ -6,17 +6,17 @@ /* import-globals-from ../mochitest/common.js */ /* import-globals-from events.js */ /* exported Logger, MOCHITESTS_DIR, invokeSetAttribute, invokeFocus, invokeSetStyle, getAccessibleDOMNodeID, getAccessibleTagName, addAccessibleTask, findAccessibleChildByID, isDefunct, CURRENT_CONTENT_DIR, loadScripts, loadFrameScripts, snippetToURL, - Cc, Cu */ + Cc, Cu, arrayFromChildren */ const { interfaces: Ci, utils: Cu, classes: Cc } = Components; /** * Current browser test directory path used to load subscripts. */ const CURRENT_DIR = 'chrome://mochitests/content/browser/accessible/tests/browser/'; @@ -353,8 +353,13 @@ function queryInterfaces(accessible, int accessible.QueryInterface(iface); } catch (e) { ok(false, "Can't query " + iface); } } return accessible; } + +function arrayFromChildren(accessible) { + return Array.from({ length: accessible.childCount }, (c, i) => + accessible.getChildAt(i)); +}
new file mode 100644 --- /dev/null +++ b/accessible/tests/browser/tree/browser.ini @@ -0,0 +1,9 @@ +[DEFAULT] +skip-if = e10s && os == 'win' && release_or_beta +support-files = + head.js + !/accessible/tests/browser/events.js + !/accessible/tests/browser/shared-head.js + !/accessible/tests/mochitest/*.js + +[browser_test_aria_owns.js]
new file mode 100644 --- /dev/null +++ b/accessible/tests/browser/tree/browser_test_aria_owns.js @@ -0,0 +1,96 @@ +/* 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"; + +function testChildrenIds(acc, expectedIds) { + let ids = arrayFromChildren(acc).map(child => getAccessibleDOMNodeID(child)); + Assert.deepEqual(ids, expectedIds, + `Children for ${getAccessibleDOMNodeID(acc)} are wrong.`); +} + +async function runTests(browser, accDoc) { + let one = findAccessibleChildByID(accDoc, "one"); + let two = findAccessibleChildByID(accDoc, "two"); + let three = findAccessibleChildByID(accDoc, "three"); + let four = findAccessibleChildByID(accDoc, "four"); + + testChildrenIds(one, ["a"]); + testChildrenIds(two, ["b", "c", "d"]); + testChildrenIds(three, []); + + let onReorders = waitForEvents({ + expected: [ + [EVENT_REORDER, "two"]], // children will be reordered via aria-owns + unexpected: [ + [EVENT_REORDER, "one"], // child will remain in place + [EVENT_REORDER, "three"], // none of its children will be reclaimed + [EVENT_REORDER, "four"]] // child will remain in place + }); + + await ContentTask.spawn(browser, null, async function() { + // aria-own ordinal child in place, should be a no-op. + document.getElementById("one").setAttribute("aria-owns", "a"); + // remove aria-owned child that is already ordinal, should be no-op. + document.getElementById("four").removeAttribute("aria-owns"); + // shuffle aria-owns with markup child. + document.getElementById("two").setAttribute("aria-owns", "d c"); + }); + + await onReorders; + + testChildrenIds(one, ["a"]); + testChildrenIds(two, ["b", "d", "c"]); + testChildrenIds(three, []); + testChildrenIds(four, ["e"]); + + onReorders = waitForEvent(EVENT_REORDER, "one"); + + await ContentTask.spawn(browser, null, async function() { + let aa = document.createElement("li"); + aa.id = "aa"; + document.getElementById("one").appendChild(aa); + }); + + await onReorders; + + testChildrenIds(one, ["aa", "a"]); + + onReorders = waitForEvents([ + [EVENT_REORDER, "two"], // "b" will go to "three" + [EVENT_REORDER, "three"], // some children will be reclaimed and acquired + [EVENT_REORDER, "one"]]); // removing aria-owns will reorder native children + + await ContentTask.spawn(browser, null, async function() { + // removing aria-owns should reorder the children + document.getElementById("one").removeAttribute("aria-owns"); + // child order will be overridden by aria-owns + document.getElementById("three").setAttribute("aria-owns", "b d"); + }); + + await onReorders; + + testChildrenIds(one, ["a", "aa"]); + testChildrenIds(two, ["c"]); + testChildrenIds(three, ["b", "d"]); +} + +/** + * Test caching of accessible object states + */ +addAccessibleTask(` + <ul id="one"> + <li id="a">Test</li> + </ul> + <ul id="two" aria-owns="d"> + <li id="b">Test 2</li> + <li id="c">Test 3</li> + </ul> + <ul id="three"> + <li id="d">Test 4</li> + </ul> + <ul id="four" aria-owns="e"> + <li id="e">Test 5</li> + </ul> + `, runTests);
new file mode 100644 --- /dev/null +++ b/accessible/tests/browser/tree/head.js @@ -0,0 +1,15 @@ +/* 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'; + +// Load the shared-head file first. +/* import-globals-from ../shared-head.js */ +Services.scriptloader.loadSubScript( + 'chrome://mochitests/content/browser/accessible/tests/browser/shared-head.js', + this); + +// Loading and common.js from accessible/tests/mochitest/ for all tests, as +// well as events.js. +loadScripts({ name: 'common.js', dir: MOCHITESTS_DIR }, 'events.js', 'layout.js');
--- a/accessible/windows/msaa/AccessibleWrap.cpp +++ b/accessible/windows/msaa/AccessibleWrap.cpp @@ -1440,18 +1440,16 @@ AccessibleWrap::GetIAccessibleFor(const // If the MSAA ID is not a chrome id then we already know that we won't // find it here and should look remotely instead. This handles the case when // accessible is part of the chrome process and is part of the xul browser // window and the child id points in the content documents. Thus we need to // make sure that it is never called on proxies. if (XRE_IsParentProcess() && !IsProxy() && !sIDGen.IsChromeID(varChild.lVal)) { return GetRemoteIAccessibleFor(varChild); } - MOZ_ASSERT(XRE_IsParentProcess() || - sIDGen.IsIDForThisContentProcess(varChild.lVal)); if (varChild.lVal > 0) { // Gecko child indices are 0-based in contrast to indices used in MSAA. MOZ_ASSERT(!IsProxy()); Accessible* xpAcc = GetChildAt(varChild.lVal - 1); if (!xpAcc) { return nullptr; }
--- a/browser/base/content/tab-content.js +++ b/browser/base/content/tab-content.js @@ -906,16 +906,17 @@ var RefreshBlocker = { } } }, enable() { this._filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"] .createInstance(Ci.nsIWebProgress); this._filter.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_ALL); + this._filter.target = tabEventTarget; let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIWebProgress); webProgress.addProgressListener(this._filter, Ci.nsIWebProgress.NOTIFY_ALL); addMessageListener("RefreshBlocker:Refresh", this); },
--- a/browser/extensions/onboarding/content/onboarding.css +++ b/browser/extensions/onboarding/content/onboarding.css @@ -308,16 +308,28 @@ border: 1px solid transparent; border-radius: 0; color: #fff; float: inline-end; margin-inline-end: 26px; margin-top: -32px; } +/* Remove default dotted outline around buttons' text */ +.onboarding-tour-action-button::-moz-focus-inner { + border: 0; +} + +/* Keyboard focus specific outline */ +.onboarding-tour-action-button:-moz-focusring { + outline: 2px solid rgba(0,149,221,0.5); + outline-offset: 1px; + -moz-outline-radius: 2px; +} + .onboarding-tour-action-button:hover:not([disabled]) , #onboarding-notification-action-btn:hover { background: #0060df; cursor: pointer; } .onboarding-tour-action-button:active:not([disabled]), #onboarding-notification-action-btn:active {
--- a/devtools/client/inspector/boxmodel/components/BoxModelInfo.js +++ b/devtools/client/inspector/boxmodel/components/BoxModelInfo.js @@ -30,20 +30,20 @@ module.exports = createClass({ onToggleGeometryEditor(e) { this.props.onToggleGeometryEditor(); }, render() { let { boxModel } = this.props; let { geometryEditorEnabled, layout } = boxModel; let { - height, + height = "-", isPositionEditable, position, - width, + width = "-", } = layout; let buttonClass = "layout-geometry-editor devtools-button"; if (geometryEditorEnabled) { buttonClass += " checked"; } return dom.div(
--- a/devtools/client/inspector/boxmodel/components/BoxModelMain.js +++ b/devtools/client/inspector/boxmodel/components/BoxModelMain.js @@ -115,38 +115,38 @@ module.exports = createClass({ * Returns true if the position is displayed and false otherwise. */ getDisplayPosition() { let { layout } = this.props.boxModel; return layout.position && layout.position != "static"; }, getHeightValue(property) { - let { layout } = this.props.boxModel; - if (property == undefined) { return "-"; } + let { layout } = this.props.boxModel; + property -= parseFloat(layout["border-top-width"]) + parseFloat(layout["border-bottom-width"]) + parseFloat(layout["padding-top"]) + parseFloat(layout["padding-bottom"]); property = parseFloat(property.toPrecision(6)); return property; }, getWidthValue(property) { - let { layout } = this.props.boxModel; - if (property == undefined) { return "-"; } + let { layout } = this.props.boxModel; + property -= parseFloat(layout["border-left-width"]) + parseFloat(layout["border-right-width"]) + parseFloat(layout["padding-left"]) + parseFloat(layout["padding-right"]); property = parseFloat(property.toPrecision(6)); return property; },
--- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -2985,18 +2985,18 @@ Element::List(FILE* out, int32_t aIndent fprintf(out, "@%p", (void *)this); ListAttributes(out); fprintf(out, " state=[%llx]", static_cast<unsigned long long>(State().GetInternalValue())); fprintf(out, " flags=[%08x]", static_cast<unsigned int>(GetFlags())); if (IsCommonAncestorForRangeInSelection()) { - nsRange::RangeHashTable* ranges = - static_cast<nsRange::RangeHashTable*>(GetProperty(nsGkAtoms::range)); + const nsTHashtable<nsPtrHashKey<nsRange>>* ranges = + GetExistingCommonAncestorRanges(); fprintf(out, " ranges:%d", ranges ? ranges->Count() : 0); } fprintf(out, " primaryframe=%p", static_cast<void*>(GetPrimaryFrame())); fprintf(out, " refcount=%" PRIuPTR "<", mRefCnt.get()); nsIContent* child = GetFirstChild(); if (child) { fputs("\n", out);
--- a/dom/base/FragmentOrElement.cpp +++ b/dom/base/FragmentOrElement.cpp @@ -653,16 +653,29 @@ nsNodeSupportsWeakRefTearoff::GetWeakRef } NS_ADDREF(*aInstancePtr = slots->mWeakReference); return NS_OK; } //---------------------------------------------------------------------- + +static const size_t MaxDOMSlotSizeAllowed = +#ifdef HAVE_64BIT_BUILD +128; +#else +64; +#endif + +static_assert(sizeof(nsINode::nsSlots) <= MaxDOMSlotSizeAllowed, + "DOM slots cannot be grown without consideration"); +static_assert(sizeof(FragmentOrElement::nsDOMSlots) <= MaxDOMSlotSizeAllowed, + "DOM slots cannot be grown without consideration"); + FragmentOrElement::nsDOMSlots::nsDOMSlots() : nsINode::nsSlots(), mDataset(nullptr) { } FragmentOrElement::nsDOMSlots::~nsDOMSlots() {
--- a/dom/base/StructuredCloneBlob.cpp +++ b/dom/base/StructuredCloneBlob.cpp @@ -17,27 +17,32 @@ #include "mozilla/dom/StructuredCloneTags.h" namespace mozilla { namespace dom { StructuredCloneBlob::StructuredCloneBlob() : StructuredCloneHolder(CloningSupported, TransferringNotSupported, StructuredCloneScope::DifferentProcess) -{}; +{} + +StructuredCloneBlob::~StructuredCloneBlob() +{ + UnregisterWeakMemoryReporter(this); +} /* static */ already_AddRefed<StructuredCloneBlob> StructuredCloneBlob::Constructor(GlobalObject& aGlobal, JS::HandleValue aValue, JS::HandleObject aTargetGlobal, ErrorResult& aRv) { JSContext* cx = aGlobal.Context(); - RefPtr<StructuredCloneBlob> holder = new StructuredCloneBlob(); + RefPtr<StructuredCloneBlob> holder = StructuredCloneBlob::Create(); Maybe<JSAutoCompartment> ac; JS::RootedValue value(cx, aValue); if (aTargetGlobal) { JS::RootedObject targetGlobal(cx, js::CheckedUnwrap(aTargetGlobal)); if (!targetGlobal) { js::ReportAccessDenied(cx); @@ -99,17 +104,17 @@ StructuredCloneBlob::Deserialize(JSConte /* static */ JSObject* StructuredCloneBlob::ReadStructuredClone(JSContext* aCx, JSStructuredCloneReader* aReader, StructuredCloneHolder* aHolder) { JS::RootedObject obj(aCx); { - RefPtr<StructuredCloneBlob> holder = new StructuredCloneBlob(); + RefPtr<StructuredCloneBlob> holder = StructuredCloneBlob::Create(); if (!holder->ReadStructuredCloneInternal(aCx, aReader, aHolder) || !holder->WrapObject(aCx, nullptr, &obj)) { return nullptr; } } return obj.get(); } @@ -176,10 +181,25 @@ StructuredCloneBlob::WriteStructuredClon } bool StructuredCloneBlob::WrapObject(JSContext* aCx, JS::HandleObject aGivenProto, JS::MutableHandleObject aResult) { return StructuredCloneHolderBinding::Wrap(aCx, this, aGivenProto, aResult); } + +NS_IMETHODIMP +StructuredCloneBlob::CollectReports(nsIHandleReportCallback* aHandleReport, + nsISupports* aData, bool aAnonymize) +{ + MOZ_COLLECT_REPORT( + "explicit/dom/structured-clone-holder", KIND_HEAP, UNITS_BYTES, + MallocSizeOf(this) + SizeOfExcludingThis(MallocSizeOf), + "Memory used by StructuredCloneHolder DOM objects."); + + return NS_OK; +} + +NS_IMPL_ISUPPORTS(StructuredCloneBlob, nsIMemoryReporter) + } // namespace dom } // namespace mozilla
--- a/dom/base/StructuredCloneBlob.h +++ b/dom/base/StructuredCloneBlob.h @@ -4,32 +4,33 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_dom_StructuredCloneBlob_h #define mozilla_dom_StructuredCloneBlob_h #include "mozilla/dom/BindingDeclarations.h" #include "mozilla/dom/StructuredCloneHolder.h" #include "mozilla/dom/StructuredCloneHolderBinding.h" -#include "mozilla/RefCounted.h" #include "jsapi.h" +#include "nsIMemoryReporter.h" #include "nsISupports.h" namespace mozilla { namespace dom { -class StructuredCloneBlob : public StructuredCloneHolder - , public RefCounted<StructuredCloneBlob> +class StructuredCloneBlob final : public nsIMemoryReporter + , public StructuredCloneHolder { + MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf) + public: - MOZ_DECLARE_REFCOUNTED_TYPENAME(StructuredCloneBlob) - - explicit StructuredCloneBlob(); + NS_DECL_ISUPPORTS + NS_DECL_NSIMEMORYREPORTER static JSObject* ReadStructuredClone(JSContext* aCx, JSStructuredCloneReader* aReader, StructuredCloneHolder* aHolder); bool WriteStructuredClone(JSContext* aCx, JSStructuredCloneWriter* aWriter, StructuredCloneHolder* aHolder); static already_AddRefed<StructuredCloneBlob> Constructor(GlobalObject& aGlobal, JS::HandleValue aValue, JS::HandleObject aTargetGlobal, ErrorResult& aRv); @@ -38,22 +39,28 @@ public: JS::MutableHandleValue aResult, ErrorResult& aRv); nsISupports* GetParentObject() const { return nullptr; } JSObject* GetWrapper() const { return nullptr; } bool WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto, JS::MutableHandleObject aResult); protected: - template <typename T, detail::RefCountAtomicity> - friend class detail::RefCounted; - - ~StructuredCloneBlob() = default; + virtual ~StructuredCloneBlob(); private: + explicit StructuredCloneBlob(); + + static already_AddRefed<StructuredCloneBlob> Create() + { + RefPtr<StructuredCloneBlob> holder = new StructuredCloneBlob(); + RegisterWeakMemoryReporter(holder); + return holder.forget(); + } + bool ReadStructuredCloneInternal(JSContext* aCx, JSStructuredCloneReader* aReader, StructuredCloneHolder* aHolder); }; } // namespace dom } // namespace mozilla #endif // mozilla_dom_StructuredCloneBlob_h
--- a/dom/base/StructuredCloneHolder.h +++ b/dom/base/StructuredCloneHolder.h @@ -3,16 +3,17 @@ * 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 mozilla_dom_StructuredCloneHolder_h #define mozilla_dom_StructuredCloneHolder_h #include "jsapi.h" #include "js/StructuredClone.h" +#include "mozilla/MemoryReporting.h" #include "mozilla/Move.h" #include "mozilla/UniquePtr.h" #include "mozilla/dom/BindingDeclarations.h" #include "nsISupports.h" #include "nsTArray.h" #ifdef DEBUG #include "nsIThread.h" @@ -111,16 +112,25 @@ public: } JSStructuredCloneData& BufferData() const { MOZ_ASSERT(mBuffer, "Write() has never been called."); return mBuffer->data(); } + size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) + { + size_t size = 0; + if (HasData()) { + size += mBuffer->sizeOfIncludingThis(aMallocSizeOf); + } + return size; + } + protected: UniquePtr<JSAutoStructuredCloneBuffer> mBuffer; StructuredCloneScope mStructuredCloneScope; #ifdef DEBUG bool mClearCalled; #endif @@ -301,16 +311,22 @@ protected: JSStructuredCloneData& aBuffer, uint32_t aAlgorithmVersion, JS::MutableHandle<JS::Value> aValue, ErrorResult &aRv); bool mSupportsCloning; bool mSupportsTransferring; + // SizeOfExcludingThis is inherited from StructuredCloneHolderBase. It doesn't + // account for objects in the following arrays because a) they're not expected + // to be stored in long-lived StructuredCloneHolder objects, and b) in the + // case of BlobImpl objects, MemoryBlobImpls have their own memory reporters, + // and the other types do not hold significant amounts of memory alive. + // Used for cloning blobs in the structured cloning algorithm. nsTArray<RefPtr<BlobImpl>> mBlobImplArray; // Used for cloning JS::WasmModules in the structured cloning algorithm. nsTArray<RefPtr<JS::WasmModule>> mWasmModuleArray; // Used for cloning InputStream in the structured cloning algorithm. nsTArray<nsCOMPtr<nsIInputStream>> mInputStreamArray;
--- a/dom/base/TabGroup.h +++ b/dom/base/TabGroup.h @@ -2,16 +2,17 @@ /* 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/. */ #ifndef TabGroup_h #define TabGroup_h +#include "nsHashKeys.h" #include "nsISupportsImpl.h" #include "nsIPrincipal.h" #include "nsTHashtable.h" #include "nsString.h" #include "mozilla/Atomics.h" #include "mozilla/SchedulerGroup.h" #include "mozilla/RefPtr.h" @@ -38,16 +39,17 @@ class TabChild; // A TabGroup is a set of browsing contexts which are all "related". Within a // TabGroup, browsing contexts are broken into "similar-origin" DocGroups. In // more detail, a DocGroup is actually a collection of documents, and a // TabGroup is a collection of DocGroups. A TabGroup typically will contain // (through its DocGroups) the documents from one or more tabs related by // window.opener. A DocGroup is a member of exactly one TabGroup. class DocGroup; +class TabChild; class TabGroup final : public SchedulerGroup { private: class HashEntry : public nsCStringHashKey { public: // NOTE: Weak reference. The DocGroup destructor removes itself from its
--- a/dom/base/TimeoutManager.cpp +++ b/dom/base/TimeoutManager.cpp @@ -1286,16 +1286,18 @@ TimeoutManager::MaybeStartThrottleTimeou do_CreateInstance("@mozilla.org/timer;1"); if (!mThrottleTimeoutsTimer) { return; } nsCOMPtr<nsITimerCallback> callback = new ThrottleTimeoutsCallback(&mWindow); + mThrottleTimeoutsTimer->SetTarget(EventTarget()); + mThrottleTimeoutsTimer->InitWithCallback( callback, gTimeoutThrottlingDelay, nsITimer::TYPE_ONE_SHOT); } void TimeoutManager::BeginSyncOperation() { // If we're beginning a sync operation, the currently running
--- a/dom/base/nsFrameMessageManager.cpp +++ b/dom/base/nsFrameMessageManager.cpp @@ -845,16 +845,23 @@ nsFrameMessageManager::GetContent(mozIDO NS_IMETHODIMP nsFrameMessageManager::GetDocShell(nsIDocShell** aDocShell) { *aDocShell = nullptr; return NS_OK; } NS_IMETHODIMP +nsFrameMessageManager::GetTabEventTarget(nsIEventTarget** aTarget) +{ + *aTarget = nullptr; + return NS_OK; +} + +NS_IMETHODIMP nsFrameMessageManager::Btoa(const nsAString& aBinaryData, nsAString& aAsciiBase64String) { return nsContentUtils::Btoa(aBinaryData, aAsciiBase64String); } NS_IMETHODIMP nsFrameMessageManager::Atob(const nsAString& aAsciiString,
--- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -1028,17 +1028,17 @@ nsPIDOMWindow<T>::nsPIDOMWindow(nsPIDOMW mAudioMuted(false), mAudioVolume(1.0), mAudioCaptured(false), mDesktopModeViewport(false), mIsRootOuterWindow(false), mInnerWindow(nullptr), mOuterWindow(aOuterWindow), // Make sure no actual window ends up with mWindowID == 0 mWindowID(NextWindowID()), mHasNotifiedGlobalCreated(false), mMarkedCCGeneration(0), mServiceWorkersTestingEnabled(false), mLargeAllocStatus(LargeAllocStatus::NONE), mHasTriedToCacheTopInnerWindow(false), - mNumOfIndexedDBDatabases(0), mCleanedUp(false) + mNumOfIndexedDBDatabases(0) { if (aOuterWindow) { mTimeoutManager = MakeUnique<mozilla::dom::TimeoutManager>(*nsGlobalWindow::Cast(AsInner())); } } template<class T> @@ -1615,16 +1615,17 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalW mIdleRequestExecutor(nullptr), #ifdef DEBUG mSetOpenerWindowCalled(false), #endif #ifdef MOZ_B2G mNetworkUploadObserverEnabled(false), mNetworkDownloadObserverEnabled(false), #endif + mCleanedUp(false), mDialogAbuseCount(0), mAreDialogsEnabled(true), #ifdef DEBUG mIsValidatingTabGroup(false), #endif mCanSkipCCGeneration(0), mAutoActivateVRDisplayID(0), mBeforeUnloadListenerCount(0) @@ -2000,20 +2001,17 @@ nsGlobalWindow::CleanUp() mAudioWorklet = nullptr; mPaintWorklet = nullptr; mExternal = nullptr; mMozSelfSupport = nullptr; - if (mPerformance) { - mPerformance->Shutdown(); - mPerformance = nullptr; - } + mPerformance = nullptr; #ifdef MOZ_WEBSPEECH mSpeechSynthesis = nullptr; #endif #if defined(MOZ_WIDGET_ANDROID) mOrientationChangeObserver = nullptr; #endif @@ -4358,20 +4356,19 @@ nsGlobalWindow::GetPerformance() return AsInner()->GetPerformance(); } void nsPIDOMWindowInner::CreatePerformanceObjectIfNeeded() { MOZ_ASSERT(IsInnerWindow()); - if (mPerformance || !mDoc || mCleanedUp) { - return; - } - + if (mPerformance || !mDoc) { + return; + } RefPtr<nsDOMNavigationTiming> timing = mDoc->GetNavigationTiming(); nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(mDoc->GetChannel())); bool timingEnabled = false; if (!timedChannel || !NS_SUCCEEDED(timedChannel->GetTimingEnabled(&timingEnabled)) || !timingEnabled) { timedChannel = nullptr; }
--- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -2005,16 +2005,18 @@ protected: nsCOMPtr<nsIURI> mLastOpenedURI; #endif #ifdef MOZ_B2G bool mNetworkUploadObserverEnabled; bool mNetworkDownloadObserverEnabled; #endif // MOZ_B2G + bool mCleanedUp; + nsCOMPtr<nsIDOMOfflineResourceList> mApplicationCache; using XBLPrototypeHandlerTable = nsJSThingHashtable<nsPtrHashKey<nsXBLPrototypeHandler>, JSObject*>; nsAutoPtr<XBLPrototypeHandlerTable> mCachedXBLPrototypeHandlers; // mSuspendedDoc is only set on outer windows. It's useful when we get matched // EnterModalState/LeaveModalState calls, in which case the outer window is // responsible for unsuspending events on the document. If we don't (for
--- a/dom/base/nsIMessageManager.idl +++ b/dom/base/nsIMessageManager.idl @@ -3,16 +3,17 @@ * 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 "nsISupports.idl" interface mozIDOMWindowProxy; interface nsIDocShell; interface nsIContent; +interface nsIEventTarget; interface nsIFrameLoader; interface nsIPrincipal; /** * Message managers provide a way for chrome-privileged JS code to * communicate with each other, even across process boundaries. * * Message managers are separated into "parent side" and "child side". @@ -393,16 +394,22 @@ interface nsIContentFrameMessageManager * The current top level window in the frame or null. */ readonly attribute mozIDOMWindowProxy content; /** * The top level docshell or null. */ readonly attribute nsIDocShell docShell; + + /** + * Returns the SchedulerEventTarget corresponding to the TabGroup + * for this frame. + */ + readonly attribute nsIEventTarget tabEventTarget; }; [uuid(b39a3324-b574-4f85-8cdb-274d04f807ef)] interface nsIInProcessContentFrameMessageManager : nsIContentFrameMessageManager { [notxpcom] nsIContent getOwnerContent(); [notxpcom] void cacheFrameLoader(in nsIFrameLoader aFrameLoader); };
--- a/dom/base/nsINode.h +++ b/dom/base/nsINode.h @@ -18,16 +18,17 @@ #include "nsPropertyTable.h" // for typedefs #include "nsTObserverArray.h" // for member #include "mozilla/ErrorResult.h" #include "mozilla/MemoryReporting.h" #include "mozilla/dom/EventTarget.h" // for base class #include "js/TypeDecls.h" // for Handle, Value, JSObject, JSContext #include "mozilla/dom/DOMString.h" #include "mozilla/dom/BindingDeclarations.h" +#include "nsTHashtable.h" #include <iosfwd> // Including 'windows.h' will #define GetClassInfo to something else. #ifdef XP_WIN #ifdef GetClassInfo #undef GetClassInfo #endif #endif @@ -46,16 +47,17 @@ class nsIMutationObserver; class nsINode; class nsINodeList; class nsIPresShell; class nsIPrincipal; class nsIURI; class nsNodeSupportsWeakRefTearoff; class nsNodeWeakReference; class nsDOMMutationObserver; +class nsRange; namespace mozilla { class EventListenerManager; class TextEditor; namespace dom { /** * @return true if aChar is what the WHATWG defines as a 'ascii whitespace'. * https://infra.spec.whatwg.org/#ascii-whitespace @@ -1104,16 +1106,22 @@ public: /** * Weak reference to this node. This is cleared by the destructor of * nsNodeWeakReference. */ nsNodeWeakReference* MOZ_NON_OWNING_REF mWeakReference; /** + * A set of ranges in the common ancestor for the selection to which + * this node belongs to. + */ + mozilla::UniquePtr<nsTHashtable<nsPtrHashKey<nsRange>>> mCommonAncestorRanges; + + /** * Number of descendant nodes in the uncomposed document that have been * explicitly set as editable. */ uint32_t mEditableDescendantCount; }; /** * Functions for managing flags and slots @@ -1893,16 +1901,37 @@ public: CallerType aCallerType, ErrorResult& aRv); already_AddRefed<DOMPoint> ConvertPointFromNode(const DOMPointInit& aPoint, const TextOrElementOrDocument& aFrom, const ConvertCoordinateOptions& aOptions, CallerType aCallerType, ErrorResult& aRv); + const nsTHashtable<nsPtrHashKey<nsRange>>* GetExistingCommonAncestorRanges() const + { + if (!HasSlots()) { + return nullptr; + } + mozilla::UniquePtr<nsTHashtable<nsPtrHashKey<nsRange>>>& ranges = + GetExistingSlots()->mCommonAncestorRanges; + return ranges.get(); + } + + nsTHashtable<nsPtrHashKey<nsRange>>* GetExistingCommonAncestorRanges() + { + nsINode::nsSlots* slots = GetExistingSlots(); + return slots ? slots->mCommonAncestorRanges.get() : nullptr; + } + + mozilla::UniquePtr<nsTHashtable<nsPtrHashKey<nsRange>>>& GetCommonAncestorRangesPtr() + { + return Slots()->mCommonAncestorRanges; + } + protected: // Override this function to create a custom slots class. // Must not return null. virtual nsINode::nsSlots* CreateSlots(); bool HasSlots() const {
--- a/dom/base/nsInProcessTabChildGlobal.cpp +++ b/dom/base/nsInProcessTabChildGlobal.cpp @@ -194,16 +194,24 @@ nsInProcessTabChildGlobal::GetContent(mo NS_IMETHODIMP nsInProcessTabChildGlobal::GetDocShell(nsIDocShell** aDocShell) { NS_IF_ADDREF(*aDocShell = mDocShell); return NS_OK; } +NS_IMETHODIMP +nsInProcessTabChildGlobal::GetTabEventTarget(nsIEventTarget** aTarget) +{ + nsCOMPtr<nsIEventTarget> target = GetMainThreadEventTarget(); + target.forget(aTarget); + return NS_OK; +} + void nsInProcessTabChildGlobal::FireUnloadEvent() { // We're called from nsDocument::MaybeInitializeFinalizeFrameLoaders, so it // should be safe to run script. MOZ_ASSERT(nsContentUtils::IsSafeToRunScript()); // Don't let the unload event propagate to chrome event handlers.
--- a/dom/base/nsInProcessTabChildGlobal.h +++ b/dom/base/nsInProcessTabChildGlobal.h @@ -72,16 +72,17 @@ public: { return mMessageManager ? mMessageManager->SendRpcMessage(aMessageName, aObject, aRemote, aPrincipal, aCx, aArgc, aRetval) : NS_ERROR_NULL_POINTER; } NS_IMETHOD GetContent(mozIDOMWindowProxy** aContent) override; NS_IMETHOD GetDocShell(nsIDocShell** aDocShell) override; + NS_IMETHOD GetTabEventTarget(nsIEventTarget** aTarget) override; NS_DECL_NSIINPROCESSCONTENTFRAMEMESSAGEMANAGER /** * MessageManagerCallback methods that we override. */ virtual bool DoSendBlockingMessage(JSContext* aCx, const nsAString& aMessage,
--- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -2085,20 +2085,25 @@ CCRunnerFired(TimeStamp aDeadline, void* if (isLateTimerFire && ShouldTriggerCC(suspected)) { if (sCCRunnerFireCount == numEarlyTimerFires + 1) { FireForgetSkippable(suspected, true, aDeadline); didDoWork = true; if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) { // Our efforts to avoid a CC have failed, so we return to let the // timer fire once more to trigger a CC. - // Clear content unbinder before the first CC slice. - Element::ClearContentUnbinder(); - // And trigger deferred deletion too. - nsCycleCollector_doDeferredDeletion(); + if (!aDeadline.IsNull() && TimeStamp::Now() < aDeadline) { + // Clear content unbinder before the first CC slice. + Element::ClearContentUnbinder(); + + if (TimeStamp::Now() < aDeadline) { + // And trigger deferred deletion too. + nsCycleCollector_doDeferredDeletion(); + } + } return didDoWork; } } else { // We are in the final timer fire and still meet the conditions for // triggering a CC. Let RunCycleCollectorSlice finish the current IGC, if // any because that will allow us to include the GC time in the CC pause. nsJSContext::RunCycleCollectorSlice(aDeadline); didDoWork = true;
--- a/dom/base/nsPIDOMWindow.h +++ b/dom/base/nsPIDOMWindow.h @@ -767,18 +767,16 @@ protected: // The evidence that we have tried to cache mTopInnerWindow only once from // SetNewDocument(). Note: We need this extra flag because mTopInnerWindow // could be null and we don't want it to be set multiple times. bool mHasTriedToCacheTopInnerWindow; // The number of active IndexedDB databases. Inner window only. uint32_t mNumOfIndexedDBDatabases; - - bool mCleanedUp; }; #define NS_PIDOMWINDOWINNER_IID \ { 0x775dabc9, 0x8f43, 0x4277, \ { 0x9a, 0xdb, 0xf1, 0x99, 0x0d, 0x77, 0xcf, 0xfb } } #define NS_PIDOMWINDOWOUTER_IID \ { 0x769693d4, 0xb009, 0x4fe2, \
--- a/dom/base/nsRange.cpp +++ b/dom/base/nsRange.cpp @@ -200,18 +200,21 @@ nsRange::IsNodeSelected(nsINode* aNode, NS_ASSERTION(n || !aNode->IsSelectionDescendant(), "orphan selection descendant"); // Collect the potential ranges and their selection objects. RangeHashTable ancestorSelectionRanges; nsTHashtable<nsPtrHashKey<Selection>> ancestorSelections; uint32_t maxRangeCount = 0; for (; n; n = GetNextRangeCommonAncestor(n->GetParentNode())) { - RangeHashTable* ranges = - static_cast<RangeHashTable*>(n->GetProperty(nsGkAtoms::range)); + nsTHashtable<nsPtrHashKey<nsRange>>* ranges = + n->GetExistingCommonAncestorRanges(); + if (!ranges) { + continue; + } for (auto iter = ranges->ConstIter(); !iter.Done(); iter.Next()) { nsRange* range = iter.Get()->GetKey(); if (range->IsInSelection() && !range->Collapsed()) { ancestorSelectionRanges.PutEntry(range); Selection* selection = range->mSelection; ancestorSelections.PutEntry(selection); maxRangeCount = std::max(maxRangeCount, selection->RangeCount()); } @@ -403,39 +406,38 @@ static void UnmarkDescendants(nsINode* a void nsRange::RegisterCommonAncestor(nsINode* aNode) { NS_PRECONDITION(aNode, "bad arg"); NS_ASSERTION(IsInSelection(), "registering range not in selection"); MarkDescendants(aNode); - RangeHashTable* ranges = - static_cast<RangeHashTable*>(aNode->GetProperty(nsGkAtoms::range)); + UniquePtr<nsTHashtable<nsPtrHashKey<nsRange>>>& ranges = + aNode->GetCommonAncestorRangesPtr(); if (!ranges) { - ranges = new RangeHashTable; - aNode->SetProperty(nsGkAtoms::range, ranges, - nsINode::DeleteProperty<nsRange::RangeHashTable>, true); + ranges = MakeUnique<nsRange::RangeHashTable>(); } ranges->PutEntry(this); aNode->SetCommonAncestorForRangeInSelection(); } void nsRange::UnregisterCommonAncestor(nsINode* aNode) { NS_PRECONDITION(aNode, "bad arg"); NS_ASSERTION(aNode->IsCommonAncestorForRangeInSelection(), "wrong node"); - RangeHashTable* ranges = - static_cast<RangeHashTable*>(aNode->GetProperty(nsGkAtoms::range)); + nsTHashtable<nsPtrHashKey<nsRange>>* ranges = + aNode->GetExistingCommonAncestorRanges(); + MOZ_ASSERT(ranges); NS_ASSERTION(ranges->GetEntry(this), "unknown range"); if (ranges->Count() == 1) { aNode->ClearCommonAncestorForRangeInSelection(); - aNode->DeleteProperty(nsGkAtoms::range); + aNode->GetCommonAncestorRangesPtr().reset(); UnmarkDescendants(aNode); } else { ranges->RemoveEntry(this); } } /****************************************************** * nsIMutationObserver implementation @@ -3391,18 +3393,18 @@ nsRange::GetUsedFontFaces(nsIDOMFontFace nsINode* nsRange::GetRegisteredCommonAncestor() { NS_ASSERTION(IsInSelection(), "GetRegisteredCommonAncestor only valid for range in selection"); nsINode* ancestor = GetNextRangeCommonAncestor(mStartContainer); while (ancestor) { - RangeHashTable* ranges = - static_cast<RangeHashTable*>(ancestor->GetProperty(nsGkAtoms::range)); + nsTHashtable<nsPtrHashKey<nsRange>>* ranges = + ancestor->GetExistingCommonAncestorRanges(); if (ranges->GetEntry(this)) { break; } ancestor = GetNextRangeCommonAncestor(ancestor->GetParentNode()); } NS_ASSERTION(ancestor, "can't find common ancestor for selected range"); return ancestor; }
--- a/dom/base/nsRange.h +++ b/dom/base/nsRange.h @@ -388,20 +388,20 @@ protected: // CharacterDataChanged does the re-registering when needed. void DoSetRange(nsINode* aStartN, uint32_t aStartOffset, nsINode* aEndN, uint32_t aEndOffset, nsINode* aRoot, bool aNotInsertedYet = false); /** * For a range for which IsInSelection() is true, return the common * ancestor for the range. This method uses the selection bits and - * nsGkAtoms::range property on the nodes to quickly find the ancestor. - * That is, it's a faster version of GetCommonAncestor that only works - * for ranges in a Selection. The method will assert and the behavior - * is undefined if called on a range where IsInSelection() is false. + * node slots to quickly find the ancestor. That is, it's a faster + * version of GetCommonAncestor that only works for ranges in a + * Selection. The method will assert and the behavior is undefined if + * called on a range where IsInSelection() is false. */ nsINode* GetRegisteredCommonAncestor(); // Helper to IsNodeSelected. static bool IsNodeInSortedRanges(nsINode* aNode, uint32_t aStartOffset, uint32_t aEndOffset, const nsTArray<const nsRange*>& aRanges,
--- a/dom/base/nsTextNode.cpp +++ b/dom/base/nsTextNode.cpp @@ -160,19 +160,18 @@ void nsTextNode::List(FILE* out, int32_t aIndent) const { int32_t index; for (index = aIndent; --index >= 0; ) fputs(" ", out); fprintf(out, "Text@%p", static_cast<const void*>(this)); fprintf(out, " flags=[%08x]", static_cast<unsigned int>(GetFlags())); if (IsCommonAncestorForRangeInSelection()) { - typedef nsTHashtable<nsPtrHashKey<nsRange> > RangeHashTable; - RangeHashTable* ranges = - static_cast<RangeHashTable*>(GetProperty(nsGkAtoms::range)); + const nsTHashtable<nsPtrHashKey<nsRange>>* ranges = + GetExistingCommonAncestorRanges(); fprintf(out, " ranges:%d", ranges ? ranges->Count() : 0); } fprintf(out, " primaryframe=%p", static_cast<void*>(GetPrimaryFrame())); fprintf(out, " refcount=%" PRIuPTR "<", mRefCnt.get()); nsAutoString tmp; ToCString(tmp, 0, mText.GetLength()); fputs(NS_LossyConvertUTF16toASCII(tmp).get(), out);
--- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -4103,19 +4103,19 @@ public: WidgetMouseEvent* mMouseEvent; EventMessage mEventMessage; }; void EventStateManager::NotifyMouseOut(WidgetMouseEvent* aMouseEvent, nsIContent* aMovingInto) { - OverOutElementsWrapper* wrapper = GetWrapperByEventID(aMouseEvent); - - if (!wrapper->mLastOverElement) + RefPtr<OverOutElementsWrapper> wrapper = GetWrapperByEventID(aMouseEvent); + + if (!wrapper || !wrapper->mLastOverElement) return; // Before firing mouseout, check for recursion if (wrapper->mLastOverElement == wrapper->mFirstOutEventElement) return; if (wrapper->mLastOverFrame) { // if the frame is associated with a subdocument, // tell the subdocument that we're moving out of it @@ -4171,19 +4171,19 @@ EventStateManager::NotifyMouseOut(Widget } void EventStateManager::NotifyMouseOver(WidgetMouseEvent* aMouseEvent, nsIContent* aContent) { NS_ASSERTION(aContent, "Mouse must be over something"); - OverOutElementsWrapper* wrapper = GetWrapperByEventID(aMouseEvent); - - if (wrapper->mLastOverElement == aContent) + RefPtr<OverOutElementsWrapper> wrapper = GetWrapperByEventID(aMouseEvent); + + if (!wrapper || wrapper->mLastOverElement == aContent) return; // Before firing mouseover, check for recursion if (aContent == wrapper->mFirstOverEventElement) return; // Check to see if we're a subdocument and if so update the parent // document's ESM state to indicate that the mouse is over the @@ -4343,33 +4343,33 @@ EventStateManager::GenerateMouseEnterExi nsCOMPtr<nsIContent> targetElement = GetEventTargetContent(aMouseEvent); if (!targetElement) { // We're always over the document root, even if we're only // over dead space in a page (whose frame is not associated with // any content) or in print preview dead space targetElement = mDocument->GetRootElement(); } if (targetElement) { - OverOutElementsWrapper* helper = GetWrapperByEventID(aMouseEvent); + RefPtr<OverOutElementsWrapper> helper = GetWrapperByEventID(aMouseEvent); if (helper) { helper->mLastOverElement = targetElement; } NotifyMouseOut(aMouseEvent, nullptr); } } break; case ePointerLeave: case ePointerCancel: case eMouseExitFromWidget: { // This is actually the window mouse exit or pointer leave event. We're not moving // into any new element. - OverOutElementsWrapper* helper = GetWrapperByEventID(aMouseEvent); - if (helper->mLastOverFrame && + RefPtr<OverOutElementsWrapper> helper = GetWrapperByEventID(aMouseEvent); + if (helper && helper->mLastOverFrame && nsContentUtils::GetTopLevelWidget(aMouseEvent->mWidget) != nsContentUtils::GetTopLevelWidget(helper->mLastOverFrame->GetNearestWidget())) { // the Mouse/PointerOut event widget doesn't have same top widget with // mLastOverFrame, it's a spurious event for mLastOverFrame break; } // Reset sLastRefPoint, so that we'll know not to report any
--- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -3515,16 +3515,24 @@ TabChildGlobal::GetDocShell(nsIDocShell* *aDocShell = nullptr; if (!mTabChild) return NS_ERROR_NULL_POINTER; nsCOMPtr<nsIDocShell> docShell = do_GetInterface(mTabChild->WebNavigation()); docShell.swap(*aDocShell); return NS_OK; } +NS_IMETHODIMP +TabChildGlobal::GetTabEventTarget(nsIEventTarget** aTarget) +{ + nsCOMPtr<nsIEventTarget> target = EventTargetFor(TaskCategory::Other); + target.forget(aTarget); + return NS_OK; +} + nsIPrincipal* TabChildGlobal::GetPrincipal() { if (!mTabChild) return nullptr; return mTabChild->GetPrincipal(); }
--- a/dom/ipc/TabChild.h +++ b/dom/ipc/TabChild.h @@ -109,16 +109,17 @@ public: { return mMessageManager ? mMessageManager->SendRpcMessage(aMessageName, aObject, aRemote, aPrincipal, aCx, aArgc, aRetval) : NS_ERROR_NULL_POINTER; } NS_IMETHOD GetContent(mozIDOMWindowProxy** aContent) override; NS_IMETHOD GetDocShell(nsIDocShell** aDocShell) override; + NS_IMETHOD GetTabEventTarget(nsIEventTarget** aTarget) override; nsresult AddEventListener(const nsAString& aType, nsIDOMEventListener* aListener, bool aUseCapture) { // By default add listeners only for trusted events! return DOMEventTargetHelper::AddEventListener(aType, aListener, aUseCapture, false, 2);
--- a/dom/payments/PaymentRequest.cpp +++ b/dom/payments/PaymentRequest.cpp @@ -379,17 +379,21 @@ PaymentRequest::Show(ErrorResult& aRv) RefPtr<PaymentRequestManager> manager = PaymentRequestManager::GetSingleton(); if (NS_WARN_IF(!manager)) { mState = eClosed; aRv.Throw(NS_ERROR_FAILURE); return nullptr; } nsresult rv = manager->ShowPayment(mInternalId); if (NS_WARN_IF(NS_FAILED(rv))) { - promise->MaybeReject(NS_ERROR_FAILURE); + if (rv == NS_ERROR_ABORT) { + promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR); + } else { + promise->MaybeReject(NS_ERROR_DOM_NOT_ALLOWED_ERR); + } mState = eClosed; return promise.forget(); } mAcceptPromise = promise; mState = eInteractive; return promise.forget(); }
--- a/dom/payments/PaymentRequestManager.cpp +++ b/dom/payments/PaymentRequestManager.cpp @@ -485,25 +485,29 @@ PaymentRequestManager::CanMakePayment(co IPCPaymentCanMakeActionRequest action(requestId); return SendRequestPayment(request, action); } nsresult PaymentRequestManager::ShowPayment(const nsAString& aRequestId) { + if (mShowingRequest) { + return NS_ERROR_ABORT; + } RefPtr<PaymentRequest> request = GetPaymentRequestById(aRequestId); if (!request) { return NS_ERROR_FAILURE; } nsAutoString requestId(aRequestId); IPCPaymentShowActionRequest action(requestId); - - return SendRequestPayment(request, action); + nsresult rv = SendRequestPayment(request, action); + mShowingRequest = request; + return rv; } nsresult PaymentRequestManager::AbortPayment(const nsAString& aRequestId) { RefPtr<PaymentRequest> request = GetPaymentRequestById(aRequestId); if (!request) { return NS_ERROR_FAILURE; @@ -588,47 +592,53 @@ PaymentRequestManager::RespondPayment(co } request->RespondShowPayment(response.isAccepted(), response.methodName(), response.data(), response.payerName(), response.payerEmail(), response.payerPhone()); if (!response.isAccepted()) { + MOZ_ASSERT(mShowingRequest == request); + mShowingRequest = nullptr; mRequestQueue.RemoveElement(request); nsresult rv = ReleasePaymentChild(request); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } } break; } case IPCPaymentActionResponse::TIPCPaymentAbortActionResponse: { const IPCPaymentAbortActionResponse& response = aResponse; RefPtr<PaymentRequest> request = GetPaymentRequestById(response.requestId()); if (NS_WARN_IF(!request)) { return NS_ERROR_FAILURE; } request->RespondAbortPayment(response.isSucceeded()); if (response.isSucceeded()) { + MOZ_ASSERT(mShowingRequest == request); + mShowingRequest = nullptr; mRequestQueue.RemoveElement(request); nsresult rv = ReleasePaymentChild(request); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } } break; } case IPCPaymentActionResponse::TIPCPaymentCompleteActionResponse: { const IPCPaymentCompleteActionResponse& response = aResponse; RefPtr<PaymentRequest> request = GetPaymentRequestById(response.requestId()); if (NS_WARN_IF(!request)) { return NS_ERROR_FAILURE; } request->RespondComplete(); + MOZ_ASSERT(mShowingRequest == request); + mShowingRequest = nullptr; mRequestQueue.RemoveElement(request); nsresult rv = ReleasePaymentChild(request); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } break; } default: {
--- a/dom/payments/PaymentRequestManager.h +++ b/dom/payments/PaymentRequestManager.h @@ -74,14 +74,15 @@ private: nsresult SendRequestPayment(PaymentRequest* aRequest, const IPCPaymentActionRequest& action, bool aReleaseAfterSend = false); // The container for the created PaymentRequests nsTArray<RefPtr<PaymentRequest>> mRequestQueue; nsRefPtrHashtable<nsRefPtrHashKey<PaymentRequest>, PaymentRequestChild> mPaymentChildHash; + RefPtr<PaymentRequest> mShowingRequest; }; } // end of namespace dom } // end of namespace mozilla #endif
--- a/dom/payments/PaymentRequestService.cpp +++ b/dom/payments/PaymentRequestService.cpp @@ -301,21 +301,56 @@ PaymentRequestService::RequestPayment(ns } /* * TODO: 1. Check basic card support once the Basic Card Payment spec is * implemented. * https://www.w3.org/TR/payment-method-basic-card/ * 2. Check third party payment app support by traversing all * registered third party payment apps. */ - case nsIPaymentActionRequest::CANMAKE_ACTION: + case nsIPaymentActionRequest::CANMAKE_ACTION: { + rv = CallTestingUIAction(requestId, type); + if (NS_WARN_IF(NS_FAILED(rv))) { + return NS_ERROR_FAILURE; + } + break; + } /* * TODO: Launch/inform payment UI here once the UI module is implemented. */ - case nsIPaymentActionRequest::SHOW_ACTION: + case nsIPaymentActionRequest::SHOW_ACTION: { + if (mShowingRequest) { + nsCOMPtr<nsIPaymentShowActionResponse> showResponse = + do_CreateInstance(NS_PAYMENT_SHOW_ACTION_RESPONSE_CONTRACT_ID); + MOZ_ASSERT(showResponse); + rv = showResponse->Init(requestId, + nsIPaymentActionResponse::PAYMENT_REJECTED, + EmptyString(), + EmptyString(), + EmptyString(), + EmptyString(), + EmptyString()); + nsCOMPtr<nsIPaymentActionResponse> response = do_QueryInterface(showResponse); + MOZ_ASSERT(response); + rv = RespondPayment(response); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } else { + rv = GetPaymentRequestById(requestId, getter_AddRefs(mShowingRequest)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return NS_ERROR_FAILURE; + } + rv = CallTestingUIAction(requestId, type); + if (NS_WARN_IF(NS_FAILED(rv))) { + return NS_ERROR_FAILURE; + } + } + break; + } case nsIPaymentActionRequest::ABORT_ACTION: case nsIPaymentActionRequest::COMPLETE_ACTION: { rv = CallTestingUIAction(requestId, type); if (NS_WARN_IF(NS_FAILED(rv))) { return NS_ERROR_FAILURE; } break; } @@ -386,21 +421,36 @@ PaymentRequestService::RespondPayment(ns case nsIPaymentActionResponse::ABORT_ACTION: { nsCOMPtr<nsIPaymentAbortActionResponse> response = do_QueryInterface(aResponse); MOZ_ASSERT(response); bool isSucceeded; rv = response->IsSucceeded(&isSucceeded); NS_ENSURE_SUCCESS(rv, rv); if (isSucceeded) { + mShowingRequest = nullptr; + mRequestQueue.RemoveElement(request); + } + break; + } + case nsIPaymentActionResponse::SHOW_ACTION: { + nsCOMPtr<nsIPaymentShowActionResponse> response = + do_QueryInterface(aResponse); + MOZ_ASSERT(response); + bool isAccepted; + rv = response->IsAccepted(&isAccepted); + NS_ENSURE_SUCCESS(rv, rv); + if (!isAccepted) { + mShowingRequest = nullptr; mRequestQueue.RemoveElement(request); } break; } case nsIPaymentActionResponse::COMPLETE_ACTION: { + mShowingRequest = nullptr; mRequestQueue.RemoveElement(request); break; } default: { break; } } return NS_OK;
--- a/dom/payments/PaymentRequestService.h +++ b/dom/payments/PaymentRequestService.h @@ -46,14 +46,16 @@ private: nsresult CallTestingUIAction(const nsAString& aRequestId, uint32_t aActionType); FallibleTArray<nsCOMPtr<nsIPaymentRequest>> mRequestQueue; nsInterfaceHashtable<nsStringHashKey, nsIPaymentActionCallback> mCallbackHashtable; nsCOMPtr<nsIPaymentUIService> mTestingUIService; + + nsCOMPtr<nsIPaymentRequest> mShowingRequest; }; } // end of namespace dom } // end of namespace mozilla #endif
--- a/dom/performance/Performance.cpp +++ b/dom/performance/Performance.cpp @@ -101,17 +101,17 @@ NS_IMPL_RELEASE_INHERITED(Performance, D /* static */ already_AddRefed<Performance> Performance::CreateForMainThread(nsPIDOMWindowInner* aWindow, nsDOMNavigationTiming* aDOMTiming, nsITimedChannel* aChannel) { MOZ_ASSERT(NS_IsMainThread()); RefPtr<Performance> performance = - PerformanceMainThread::Create(aWindow, aDOMTiming, aChannel); + new PerformanceMainThread(aWindow, aDOMTiming, aChannel); return performance.forget(); } /* static */ already_AddRefed<Performance> Performance::CreateForWorker(workers::WorkerPrivate* aWorkerPrivate) { MOZ_ASSERT(aWorkerPrivate); aWorkerPrivate->AssertIsOnWorkerThread(); @@ -272,23 +272,28 @@ Performance::RoundTime(double aTime) con void Performance::Mark(const nsAString& aName, ErrorResult& aRv) { // We add nothing when 'privacy.resistFingerprinting' is on. if (nsContentUtils::ShouldResistFingerprinting()) { return; } + // Don't add the entry if the buffer is full. XXX should be removed by bug 1159003. + if (mUserEntries.Length() >= mResourceTimingBufferSize) { + return; + } + if (IsPerformanceTimingAttribute(aName)) { aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); return; } RefPtr<PerformanceMark> performanceMark = - new PerformanceMark(GetParentObject(), aName, Now()); + new PerformanceMark(GetAsISupports(), aName, Now()); InsertUserEntry(performanceMark); if (profiler_is_active()) { profiler_add_marker( "UserTiming", MakeUnique<UserTimingMarkerPayload>(aName, TimeStamp::Now())); } } @@ -334,16 +339,22 @@ Performance::Measure(const nsAString& aN const Optional<nsAString>& aEndMark, ErrorResult& aRv) { // We add nothing when 'privacy.resistFingerprinting' is on. if (nsContentUtils::ShouldResistFingerprinting()) { return; } + // Don't add the entry if the buffer is full. XXX should be removed by bug + // 1159003. + if (mUserEntries.Length() >= mResourceTimingBufferSize) { + return; + } + DOMHighResTimeStamp startTime; DOMHighResTimeStamp endTime; if (IsPerformanceTimingAttribute(aName)) { aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); return; } @@ -364,17 +375,17 @@ Performance::Measure(const nsAString& aN if (NS_WARN_IF(aRv.Failed())) { return; } } else { endTime = Now(); } RefPtr<PerformanceMeasure> performanceMeasure = - new PerformanceMeasure(GetParentObject(), aName, startTime, endTime); + new PerformanceMeasure(GetAsISupports(), aName, startTime, endTime); InsertUserEntry(performanceMeasure); if (profiler_is_active()) { TimeStamp startTimeStamp = CreationTimeStamp() + TimeDuration::FromMilliseconds(startTime); TimeStamp endTimeStamp = CreationTimeStamp() + TimeDuration::FromMilliseconds(endTime); profiler_add_marker( @@ -562,16 +573,10 @@ Performance::IsObserverEnabled(JSContext RefPtr<PrefEnabledRunnable> runnable = new PrefEnabledRunnable(workerPrivate, NS_LITERAL_CSTRING("dom.enable_performance_observer")); return runnable->Dispatch() && runnable->IsEnabled(); } -void -Performance::MemoryPressure() -{ - mUserEntries.Clear(); -} - } // dom namespace } // mozilla namespace
--- a/dom/performance/Performance.h +++ b/dom/performance/Performance.h @@ -96,36 +96,33 @@ public: virtual void GetMozMemory(JSContext *aCx, JS::MutableHandle<JSObject*> aObj) = 0; virtual nsDOMNavigationTiming* GetDOMTiming() const = 0; virtual nsITimedChannel* GetChannel() const = 0; - void MemoryPressure(); - - // This method is currently called only on the main-thread. - virtual void Shutdown() {} - protected: Performance(); explicit Performance(nsPIDOMWindowInner* aWindow); virtual ~Performance(); virtual void InsertUserEntry(PerformanceEntry* aEntry); void InsertResourceEntry(PerformanceEntry* aEntry); void ClearUserEntries(const Optional<nsAString>& aEntryName, const nsAString& aEntryType); DOMHighResTimeStamp ResolveTimestampFromName(const nsAString& aName, ErrorResult& aRv); + virtual nsISupports* GetAsISupports() = 0; + virtual void DispatchBufferFullEvent() = 0; virtual TimeStamp CreationTimeStamp() const = 0; virtual DOMHighResTimeStamp CreationTime() const = 0; virtual bool IsPerformanceTimingAttribute(const nsAString& aName) {
--- a/dom/performance/PerformanceMainThread.cpp +++ b/dom/performance/PerformanceMainThread.cpp @@ -2,17 +2,16 @@ /* 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 "PerformanceMainThread.h" #include "PerformanceNavigation.h" #include "nsICacheInfoChannel.h" -#include "nsISupportsPrimitives.h" namespace mozilla { namespace dom { NS_IMPL_CYCLE_COLLECTION_CLASS(PerformanceMainThread) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PerformanceMainThread, Performance) @@ -34,50 +33,28 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INH NS_IMPL_CYCLE_COLLECTION_TRACE_END NS_IMPL_ADDREF_INHERITED(PerformanceMainThread, Performance) NS_IMPL_RELEASE_INHERITED(PerformanceMainThread, Performance) // QueryInterface implementation for PerformanceMainThread NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PerformanceMainThread) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY - NS_INTERFACE_MAP_ENTRY(nsIObserver) + NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END_INHERITING(Performance) -already_AddRefed<PerformanceMainThread> -PerformanceMainThread::Create(nsPIDOMWindowInner* aWindow, - nsDOMNavigationTiming* aDOMTiming, - nsITimedChannel* aChannel) -{ - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aWindow, "Parent window object should be provided"); - - RefPtr<PerformanceMainThread> performance = - new PerformanceMainThread(aWindow, aDOMTiming, aChannel); - - nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); - if (NS_WARN_IF(!obs)) { - return nullptr; - } - - nsresult rv = obs->AddObserver(performance, "memory-pressure", false); - if (NS_WARN_IF(NS_FAILED(rv))) { - return nullptr; - } - - return performance.forget(); -} - PerformanceMainThread::PerformanceMainThread(nsPIDOMWindowInner* aWindow, nsDOMNavigationTiming* aDOMTiming, nsITimedChannel* aChannel) : Performance(aWindow) , mDOMTiming(aDOMTiming) , mChannel(aChannel) -{} +{ + MOZ_ASSERT(aWindow, "Parent window object should be provided"); +} PerformanceMainThread::~PerformanceMainThread() { mozilla::DropJSObjects(this); } void PerformanceMainThread::GetMozMemory(JSContext *aCx, @@ -355,33 +332,10 @@ PerformanceMainThread::CreationTimeStamp } DOMHighResTimeStamp PerformanceMainThread::CreationTime() const { return GetDOMTiming()->GetNavigationStart(); } -NS_IMETHODIMP -PerformanceMainThread::Observe(nsISupports* aSubject, const char* aTopic, - const char16_t* aData) -{ - AssertIsOnMainThread(); - - if (!strcmp(aTopic, "memory-pressure")) { - MemoryPressure(); - return NS_OK; - } - - return NS_OK; -} - -void -PerformanceMainThread::Shutdown() -{ - nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); - if (obs) { - obs->RemoveObserver(this, "memory-pressure"); - } -} - } // dom namespace } // mozilla namespace
--- a/dom/performance/PerformanceMainThread.h +++ b/dom/performance/PerformanceMainThread.h @@ -8,28 +8,26 @@ #define mozilla_dom_PerformanceMainThread_h #include "Performance.h" namespace mozilla { namespace dom { class PerformanceMainThread final : public Performance - , public nsIObserver { public: + PerformanceMainThread(nsPIDOMWindowInner* aWindow, + nsDOMNavigationTiming* aDOMTiming, + nsITimedChannel* aChannel); + NS_DECL_ISUPPORTS_INHERITED - NS_DECL_NSIOBSERVER NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(PerformanceMainThread, Performance) - static already_AddRefed<PerformanceMainThread> - Create(nsPIDOMWindowInner* aWindow, nsDOMNavigationTiming* aDOMTiming, - nsITimedChannel* aChannel); - virtual PerformanceTiming* Timing() override; virtual PerformanceNavigation* Navigation() override; virtual void AddEntry(nsIHttpChannel* channel, nsITimedChannel* timedChannel) override; TimeStamp CreationTimeStamp() const override; @@ -44,24 +42,23 @@ public: return mDOMTiming; } virtual nsITimedChannel* GetChannel() const override { return mChannel; } - void Shutdown() override; +protected: + ~PerformanceMainThread(); -protected: - PerformanceMainThread(nsPIDOMWindowInner* aWindow, - nsDOMNavigationTiming* aDOMTiming, - nsITimedChannel* aChannel); - - ~PerformanceMainThread(); + nsISupports* GetAsISupports() override + { + return this; + } void InsertUserEntry(PerformanceEntry* aEntry) override; bool IsPerformanceTimingAttribute(const nsAString& aName) override; DOMHighResTimeStamp GetPerformanceTimingFromString(const nsAString& aTimingName) override;
--- a/dom/performance/PerformanceWorker.h +++ b/dom/performance/PerformanceWorker.h @@ -59,16 +59,21 @@ public: { MOZ_CRASH("This should not be called on workers."); return nullptr; } protected: ~PerformanceWorker(); + nsISupports* GetAsISupports() override + { + return nullptr; + } + void InsertUserEntry(PerformanceEntry* aEntry) override; void DispatchBufferFullEvent() override { MOZ_CRASH("This should not be called on workers."); } private:
--- a/dom/plugins/base/nsPluginsDirWin.cpp +++ b/dom/plugins/base/nsPluginsDirWin.cpp @@ -419,14 +419,14 @@ nsresult nsPluginFile::FreePluginInfo(ns if (info.fFullPath) PL_strfree(info.fFullPath); if (info.fFileName) PL_strfree(info.fFileName); if (info.fVersion) - mozilla::SmprintfFree(info.fVersion); + free(info.fVersion); ZeroMemory((void *)&info, sizeof(info)); return NS_OK; }
--- a/dom/power/PowerManagerService.cpp +++ b/dom/power/PowerManagerService.cpp @@ -28,17 +28,17 @@ #include <android/log.h> extern "C" char* PrintJSStack(); static void LogFunctionAndJSStack(const char* funcname) { char *jsstack = PrintJSStack(); __android_log_print(ANDROID_LOG_INFO, "PowerManagerService", \ "Call to %s. The JS stack is:\n%s\n", funcname, jsstack ? jsstack : "<no JS stack>"); - JS_smprintf_free(jsstack); + js_free(jsstack); } // bug 839452 #define LOG_FUNCTION_AND_JS_STACK() \ LogFunctionAndJSStack(__PRETTY_FUNCTION__); #else #define LOG_FUNCTION_AND_JS_STACK() #endif
--- a/dom/tests/mochitest/chrome/chrome.ini +++ b/dom/tests/mochitest/chrome/chrome.ini @@ -17,16 +17,17 @@ support-files = file_bug1224790-2_nonmodal.xul file_subscript_bindings.js focus_frameset.html focus_window2.xul fullscreen.xul queryCaretRectUnix.html queryCaretRectWin.html selectAtPoint.html + selectAtPoint-innerframe.html sizemode_attribute.xul window_activation.xul window_callback_wrapping.xul window_docshell_swap.xul window_focus.xul window_focus_docnav.xul !/dom/tests/mochitest/general/file_clonewrapper.html !/dom/tests/mochitest/general/file_moving_nodeList.html
new file mode 100644 --- /dev/null +++ b/dom/tests/mochitest/chrome/selectAtPoint-innerframe.html @@ -0,0 +1,6 @@ +<html> +<body style='margin: 0; padding: 0; font-family: monospace;' onload='window.parent.onFrameLoad();'> +<div id='sel2'>ttestselection2 Lorem ipsum dolor sit amet, at duo debet graeci, vivendum vulputate per ut.</div> +<br/><br/> +</body> +</html>">
--- a/dom/tests/mochitest/chrome/selectAtPoint.html +++ b/dom/tests/mochitest/chrome/selectAtPoint.html @@ -262,17 +262,17 @@ body { </style> </head> <body id="body" onload="onPageLoad();"> <div id="div1">ttestselection1 Lorem ipsum dolor sit amet, at duo debet graeci, vivendum vulputate per ut. Ne labore incorrupte vix. Cu copiosae postulant tincidunt ius, in illud appetere contentiones eos. Ei munere officiis assentior pro, nibh decore ius at.</div> <br /> -<iframe id="frame1" src="data:text/html,<html><body style='margin: 0; padding: 0; font-family: monospace;' onload='window.parent.onFrameLoad();'><div id='sel2'>ttestselection2 Lorem ipsum dolor sit amet, at duo debet graeci, vivendum vulputate per ut.</div><br/><br/></body></html>"></iframe> +<iframe id="frame1" src="selectAtPoint-innerframe.html"></iframe> <br/> <div id="div2">Lorem ipsum dolor sit amet, at duo debet graeci, vivendum vulputate per ut. Ne labore incorrupte vix. Cu copiosae postulant tincidunt ius, in illud appetere contentiones eos.</div> <br /> <span id="measure">t</span>
--- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -6770,33 +6770,24 @@ WorkerPrivate::CycleCollectInternal(bool } } void WorkerPrivate::MemoryPressureInternal() { AssertIsOnWorkerThread(); - if (mScope) { - RefPtr<Console> console = mScope->GetConsoleIfExists(); - if (console) { - console->ClearStorage(); - } - - RefPtr<Performance> performance = mScope->GetPerformanceIfExists(); - if (performance) { - performance->MemoryPressure(); - } - } - - if (mDebuggerScope) { - RefPtr<Console> console = mDebuggerScope->GetConsoleIfExists(); - if (console) { - console->ClearStorage(); - } + RefPtr<Console> console = mScope ? mScope->GetConsoleIfExists() : nullptr; + if (console) { + console->ClearStorage(); + } + + console = mDebuggerScope ? mDebuggerScope->GetConsoleIfExists() : nullptr; + if (console) { + console->ClearStorage(); } for (uint32_t index = 0; index < mChildWorkers.Length(); index++) { mChildWorkers[index]->MemoryPressure(false); } } void
--- a/dom/workers/WorkerScope.h +++ b/dom/workers/WorkerScope.h @@ -151,21 +151,16 @@ public: IMPL_EVENT_HANDLER(online) IMPL_EVENT_HANDLER(offline) void Dump(const Optional<nsAString>& aString) const; Performance* GetPerformance(); - Performance* GetPerformanceIfExists() const - { - return mPerformance; - } - already_AddRefed<Promise> Fetch(const RequestOrUSVString& aInput, const RequestInit& aInit, CallerType aCallerType, ErrorResult& aRv); already_AddRefed<IDBFactory> GetIndexedDB(ErrorResult& aErrorResult); already_AddRefed<cache::CacheStorage>
--- a/gfx/layers/d3d11/CompositorD3D11.cpp +++ b/gfx/layers/d3d11/CompositorD3D11.cpp @@ -1100,16 +1100,23 @@ CompositorD3D11::NormalDrawingDone() void CompositorD3D11::EndFrame() { if (!mDefaultRT) { Compositor::EndFrame(); return; } + if (XRE_IsParentProcess() && mDevice->GetDeviceRemovedReason() != S_OK) { + gfxCriticalNote << "GFX: D3D11 skip EndFrame with device-removed."; + Compositor::EndFrame(); + mCurrentRT = nullptr; + return; + } + LayoutDeviceIntSize oldSize = mSize; EnsureSize(); if (mSize.width <= 0 || mSize.height <= 0) { Compositor::EndFrame(); return; } RefPtr<ID3D11Query> query;
--- a/gfx/layers/d3d11/MLGDeviceD3D11.cpp +++ b/gfx/layers/d3d11/MLGDeviceD3D11.cpp @@ -1646,47 +1646,47 @@ MLGDeviceD3D11::DrawInstanced(uint32_t a uint32_t aVertexOffset, uint32_t aInstanceOffset) { mCtx->DrawInstanced(aVertexCountPerInstance, aInstanceCount, aVertexOffset, aInstanceOffset); } void MLGDeviceD3D11::SetPSTextures(uint32_t aSlot, uint32_t aNumTextures, TextureSource* const* aTextures) { - StackArray<ID3D11ShaderResourceView*, 2> textures(aNumTextures); + // TextureSource guarantees that the ID3D11ShaderResourceView will be cached, + // so we don't hold a RefPtr here. + StackArray<ID3D11ShaderResourceView*, 3> views(aNumTextures); for (size_t i = 0; i < aNumTextures; i++) { - if (!aTextures[i]) { - gfxWarning() << "Null TextureRef in SetPSTextures"; - continue; + views[i] = ResolveTextureSourceForShader(aTextures[i]); + } + + mCtx->PSSetShaderResources(aSlot, aNumTextures, views.data()); +} + +ID3D11ShaderResourceView* +MLGDeviceD3D11::ResolveTextureSourceForShader(TextureSource* aTexture) +{ + if (!aTexture) { + return nullptr; + } + + if (TextureSourceD3D11* source = aTexture->AsSourceD3D11()) { + ID3D11Texture2D* texture = source->GetD3D11Texture(); + if (!texture) { + gfxWarning() << "No D3D11 texture present in SetPSTextures"; + return nullptr; } - ID3D11ShaderResourceView* view = nullptr; - if (TextureSourceD3D11* source = aTextures[i]->AsSourceD3D11()) { - ID3D11Texture2D* texture = source->GetD3D11Texture(); - if (!texture) { - gfxWarning() << "No D3D11 texture present in SetPSTextures"; - continue; - } - MaybeLockTexture(texture); - - view = source->GetShaderResourceView(); - } else { - gfxWarning() << "Unknown texture type in SetPSTextures"; - continue; - } - - if (!view) { - gfxWarning() << "Failed to get shader resource view for texture"; - continue; - } - textures[i] = view; + MaybeLockTexture(texture); + return source->GetShaderResourceView(); } - mCtx->PSSetShaderResources(aSlot, aNumTextures, textures.data()); + gfxWarning() << "Unknown texture type in SetPSTextures"; + return nullptr; } void MLGDeviceD3D11::SetPSTexture(uint32_t aSlot, MLGTexture* aTexture) { RefPtr<ID3D11ShaderResourceView> view; if (aTexture) { MLGTextureD3D11* texture = aTexture->AsD3D11();
--- a/gfx/layers/d3d11/MLGDeviceD3D11.h +++ b/gfx/layers/d3d11/MLGDeviceD3D11.h @@ -272,16 +272,20 @@ private: bool InitSamplerStates(); bool InitBlendStates(); bool InitDepthStencilState(); bool VerifyConstantBufferOffsetting() override; void SetInputLayout(ID3D11InputLayout* aLayout); void SetVertexShader(ID3D11VertexShader* aShader); + // Resolve a TextureSource to an ID3D11ShaderResourceView, locking the + // texture if needed. The lock is released at the end of the frame. + ID3D11ShaderResourceView* ResolveTextureSourceForShader(TextureSource* aSource); + private: RefPtr<ID3D11Device> mDevice; RefPtr<ID3D11DeviceContext> mCtx; RefPtr<ID3D11DeviceContext1> mCtx1; UniquePtr<DiagnosticsD3D11> mDiagnostics; typedef EnumeratedArray<PixelShaderID, PixelShaderID::MaxShaders, RefPtr<ID3D11PixelShader>> PixelShaderArray; typedef EnumeratedArray<VertexShaderID, VertexShaderID::MaxShaders, RefPtr<ID3D11VertexShader>> VertexShaderArray;
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp +++ b/gfx/layers/ipc/CompositorBridgeParent.cpp @@ -1657,17 +1657,17 @@ CompositorBridgeParent::RecvAdoptChild(c sIndirectLayerTrees[child].mWrBridge->UpdateWebRender(mWrBridge->CompositorScheduler(), mWrBridge->GetWebRenderAPI(), mWrBridge->CompositableHolder(), GetAnimationStorage()); // Pretend we composited, since parent CompositorBridgeParent was replaced. CrossProcessCompositorBridgeParent* cpcp = sIndirectLayerTrees[child].mCrossProcessParent; if (cpcp) { TimeStamp now = TimeStamp::Now(); - cpcp->DidComposite(child, now, now); + cpcp->DidCompositeLocked(child, now, now); } } parent = sIndirectLayerTrees[child].mApzcTreeManagerParent; } if (mApzcTreeManager && parent) { parent->ChildAdopted(mApzcTreeManager); } @@ -2000,17 +2000,17 @@ CompositorBridgeParent::NotifyDidComposi Unused << ImageBridgeParent::NotifyImageComposites(notifications); } } MonitorAutoLock lock(*sIndirectLayerTreesLock); ForEachIndirectLayerTree([&] (LayerTreeState* lts, const uint64_t& aLayersId) -> void { if (lts->mCrossProcessParent && lts->mParent == this) { CrossProcessCompositorBridgeParent* cpcp = lts->mCrossProcessParent; - cpcp->DidComposite(aLayersId, aCompositeStart, aCompositeEnd); + cpcp->DidCompositeLocked(aLayersId, aCompositeStart, aCompositeEnd); } }); } void CompositorBridgeParent::InvalidateRemoteLayers() { MOZ_ASSERT(CompositorLoop() == MessageLoop::current());
--- a/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp +++ b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp @@ -357,16 +357,26 @@ CrossProcessCompositorBridgeParent::Shad } void CrossProcessCompositorBridgeParent::DidComposite( uint64_t aId, TimeStamp& aCompositeStart, TimeStamp& aCompositeEnd) { + MonitorAutoLock lock(*sIndirectLayerTreesLock); + DidCompositeLocked(aId, aCompositeStart, aCompositeEnd); +} + +void +CrossProcessCompositorBridgeParent::DidCompositeLocked( + uint64_t aId, + TimeStamp& aCompositeStart, + TimeStamp& aCompositeEnd) +{ sIndirectLayerTreesLock->AssertCurrentThreadOwns(); if (LayerTransactionParent *layerTree = sIndirectLayerTrees[aId].mLayerTree) { uint64_t transactionId = layerTree->GetPendingTransactionId(); if (transactionId) { Unused << SendDidComposite(aId, transactionId, aCompositeStart, aCompositeEnd); layerTree->SetPendingTransactionId(0); } } else if (WebRenderBridgeParent* wrbridge = sIndirectLayerTrees[aId].mWrBridge) {
--- a/gfx/layers/ipc/CrossProcessCompositorBridgeParent.h +++ b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.h @@ -102,16 +102,22 @@ public: APZTestData* aOutData) override; virtual void SetConfirmedTargetAPZC(const uint64_t& aLayersId, const uint64_t& aInputBlockId, const nsTArray<ScrollableLayerGuid>& aTargets) override; virtual AsyncCompositionManager* GetCompositionManager(LayerTransactionParent* aParent) override; virtual mozilla::ipc::IPCResult RecvRemotePluginsReady() override { return IPC_FAIL_NO_REASON(this); } + // Use DidCompositeLocked if you already hold a lock on + // sIndirectLayerTreesLock; Otherwise use DidComposite, which would request + // the lock automatically. + void DidCompositeLocked(uint64_t aId, + TimeStamp& aCompositeStart, + TimeStamp& aCompositeEnd); virtual void DidComposite(uint64_t aId, TimeStamp& aCompositeStart, TimeStamp& aCompositeEnd) override; virtual PTextureParent* AllocPTextureParent(const SurfaceDescriptor& aSharedData, const LayersBackend& aLayersBackend, const TextureFlags& aFlags, const uint64_t& aId,
--- a/image/imgRequest.cpp +++ b/image/imgRequest.cpp @@ -64,17 +64,19 @@ imgRequest::imgRequest(imgLoader* aLoade , mMutex("imgRequest") , mProgressTracker(new ProgressTracker()) , mIsMultiPartChannel(false) , mGotData(false) , mIsInCache(false) , mDecodeRequested(false) , mNewPartPending(false) , mHadInsecureRedirect(false) -{ } +{ + LOG_FUNC(gImgLog, "imgRequest::imgRequest()"); +} imgRequest::~imgRequest() { if (mLoader) { mLoader->RemoveFromUncachedImages(this); } if (mURI) { nsAutoCString spec;
--- a/image/imgRequestProxy.cpp +++ b/image/imgRequestProxy.cpp @@ -117,17 +117,17 @@ imgRequestProxy::imgRequestProxy() : mIsInLoadGroup(false), mListenerIsStrongRef(false), mDecodeRequested(false), mDeferNotifications(false), mHadListener(false), mHadDispatch(false) { /* member initializers and constructor code */ - + LOG_FUNC(gImgLog, "imgRequestProxy::imgRequestProxy"); } imgRequestProxy::~imgRequestProxy() { /* destructor code */ NS_PRECONDITION(!mListener, "Someone forgot to properly cancel this request!"); @@ -164,16 +164,18 @@ imgRequestProxy::~imgRequestProxy() /* Call RemoveProxy with a successful status. This will keep the channel, if still downloading data, from being canceled if 'this' is the last observer. This allows the image to continue to download and be cached even if no one is using it currently. */ mCanceled = true; GetOwner()->RemoveProxy(this, NS_OK); } + + LOG_FUNC(gImgLog, "imgRequestProxy::~imgRequestProxy"); } nsresult imgRequestProxy::Init(imgRequest* aOwner, nsILoadGroup* aLoadGroup, nsIDocument* aLoadingDocument, ImageURL* aURI, imgINotificationObserver* aObserver)
--- a/ipc/chromium/src/base/message_loop.cc +++ b/ipc/chromium/src/base/message_loop.cc @@ -163,16 +163,21 @@ MessageLoop::EventTarget::DelayedDispatc //------------------------------------------------------------------------------ // static MessageLoop* MessageLoop::current() { return get_tls_ptr().Get(); } +// static +void MessageLoop::set_current(MessageLoop* loop) { + get_tls_ptr().Set(loop); +} + static mozilla::Atomic<int32_t> message_loop_id_seq(0); MessageLoop::MessageLoop(Type type, nsIThread* aThread) : type_(type), id_(++message_loop_id_seq), nestable_tasks_allowed_(true), exception_restoration_(false), state_(NULL),
--- a/ipc/chromium/src/base/message_loop.h +++ b/ipc/chromium/src/base/message_loop.h @@ -214,16 +214,18 @@ public: DCHECK(thread_name_.empty()) << "Should not rename this thread!"; thread_name_ = aThreadName; } const std::string& thread_name() const { return thread_name_; } // Returns the MessageLoop object for the current thread, or null if none. static MessageLoop* current(); + static void set_current(MessageLoop* loop); + // Enables or disables the recursive task processing. This happens in the case // of recursive message loops. Some unwanted message loop may occurs when // using common controls or printer functions. By default, recursive task // processing is disabled. // // The specific case where tasks get queued is: // - The thread is running a message loop. // - It receives a task #1 and execute it.
--- a/ipc/chromium/src/base/pickle.h +++ b/ipc/chromium/src/base/pickle.h @@ -12,28 +12,24 @@ #include "base/basictypes.h" #include "base/logging.h" #include "base/string16.h" #include "mozilla/Attributes.h" #include "mozilla/BufferList.h" #include "mozilla/mozalloc.h" #include "mozilla/TimeStamp.h" - #ifdef FUZZING #include "base/singleton.h" #include "mozilla/ipc/Faulty.h" #endif - -#if !defined(RELEASE_OR_BETA) || defined(DEBUG) +#if (!defined(RELEASE_OR_BETA) && !defined(FUZZING)) || defined(DEBUG) #define MOZ_PICKLE_SENTINEL_CHECKING #endif - class Pickle; - class PickleIterator { public: explicit PickleIterator(const Pickle& pickle); private: friend class Pickle; mozilla::BufferList<InfallibleAllocPolicy>::IterImpl iter_;
--- a/ipc/glue/MessageLink.cpp +++ b/ipc/glue/MessageLink.cpp @@ -67,16 +67,18 @@ ProcessLink::~ProcessLink() mIOLoop = nullptr; mExistingListener = nullptr; #endif } void ProcessLink::Open(mozilla::ipc::Transport* aTransport, MessageLoop *aIOLoop, Side aSide) { + mChan->AssertWorkerThread(); + NS_PRECONDITION(aTransport, "need transport layer"); // FIXME need to check for valid channel mTransport = aTransport; // FIXME figure out whether we're in parent or child, grab IO loop // appropriately @@ -125,18 +127,22 @@ ProcessLink::Open(mozilla::ipc::Transpor // over the channel from the previous listener and process // any queued messages. mIOLoop->PostTask(NewNonOwningRunnableMethod( "ipc::ProcessLink::OnTakeConnectedChannel", this, &ProcessLink::OnTakeConnectedChannel)); } - // Should not wait here if something goes wrong with the channel. - while (!mChan->Connected() && mChan->mChannelState != ChannelError) { + // Wait until one of the runnables above changes the state of the + // channel. Note that the state could be changed again after that (to + // ChannelClosing, for example, by the IO thread). We can rely on it not + // changing back to Closed: only the worker thread changes it to closed, + // and we're on the worker thread, blocked. + while (mChan->mChannelState == ChannelClosed) { mChan->mMonitor->Wait(); } } } void ProcessLink::EchoMessage(Message *msg) {
--- a/js/public/StructuredClone.h +++ b/js/public/StructuredClone.h @@ -4,16 +4,17 @@ * 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 js_StructuredClone_h #define js_StructuredClone_h #include "mozilla/Attributes.h" #include "mozilla/BufferList.h" +#include "mozilla/MemoryReporting.h" #include <stdint.h> #include "jstypes.h" #include "js/RootingAPI.h" #include "js/TypeDecls.h" #include "js/Value.h" @@ -334,16 +335,26 @@ class JS_PUBLIC_API(JSAutoStructuredClon bool write(JSContext* cx, JS::HandleValue v, const JSStructuredCloneCallbacks* optionalCallbacks=nullptr, void* closure=nullptr); bool write(JSContext* cx, JS::HandleValue v, JS::HandleValue transferable, JS::CloneDataPolicy cloneDataPolicy, const JSStructuredCloneCallbacks* optionalCallbacks=nullptr, void* closure=nullptr); + size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) + { + return data_.SizeOfExcludingThis(mallocSizeOf); + } + + size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) + { + return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf); + } + private: // Copy and assignment are not supported. JSAutoStructuredCloneBuffer(const JSAutoStructuredCloneBuffer& other) = delete; JSAutoStructuredCloneBuffer& operator=(const JSAutoStructuredCloneBuffer& other) = delete; }; // The range of tag values the application may use for its own custom object types. #define JS_SCTAG_USER_MIN ((uint32_t) 0xFFFF8000)
--- a/js/public/TrackedOptimizationInfo.h +++ b/js/public/TrackedOptimizationInfo.h @@ -18,17 +18,16 @@ namespace JS { _(GetProp_Constant) \ _(GetProp_NotDefined) \ _(GetProp_StaticName) \ _(GetProp_SimdGetter) \ _(GetProp_TypedObject) \ _(GetProp_DefiniteSlot) \ _(GetProp_Unboxed) \ _(GetProp_CommonGetter) \ - _(GetProp_Static) \ _(GetProp_InlineAccess) \ _(GetProp_Innerize) \ _(GetProp_InlineCache) \ _(GetProp_SharedCache) \ _(GetProp_ModuleNamespace) \ \ _(SetProp_CommonSetter) \ _(SetProp_TypedObject) \
--- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -9511,25 +9511,16 @@ BytecodeEmitter::emitCallOrNew(ParseNode if (!emitGetName(pn2, callop)) return false; break; case PNK_DOT: MOZ_ASSERT(emitterMode != BytecodeEmitter::SelfHosting); if (pn2->as<PropertyAccess>().isSuper()) { if (!emitSuperPropOp(pn2, JSOP_GETPROP_SUPER, /* isCall = */ callop)) return false; - } else if ((pn->getOp() == JSOP_FUNCALL || pn->getOp() == JSOP_FUNAPPLY) && - pn2->expr()->getKind() == PNK_FUNCTION && - checkRunOnceContext()) { - // Top level lambdas whose .call or .apply methods are immediately - // invoked should be treated as run once lambdas. - emittingRunOnceLambda = true; - if (!emitPropOp(pn2, callop ? JSOP_CALLPROP : JSOP_GETPROP)) - return false; - emittingRunOnceLambda = false; } else { if (!emitPropOp(pn2, callop ? JSOP_CALLPROP : JSOP_GETPROP)) return false; } break; case PNK_ELEM: MOZ_ASSERT(emitterMode != BytecodeEmitter::SelfHosting);
--- a/js/src/gc/RootMarking.cpp +++ b/js/src/gc/RootMarking.cpp @@ -357,19 +357,16 @@ js::gc::GCRuntime::traceRuntimeCommon(JS for (const CooperatingContext& target : rt->cooperatingContexts()) target.context()->trace(trc); // Trace all compartment roots, but not the compartment itself; it is // traced via the parent pointer if traceRoots actually traces anything. for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) c->traceRoots(trc, traceOrMark); - // Trace the Gecko Profiler. - rt->geckoProfiler().trace(trc); - // Trace helper thread roots. HelperThreadState().trace(trc); // Trace the embedding's black and gray roots. if (!JS::CurrentThreadIsHeapMinorCollecting()) { gcstats::AutoPhase ap(stats(), gcstats::PhaseKind::MARK_EMBEDDING); /*
--- a/js/src/gc/ZoneGroup.cpp +++ b/js/src/gc/ZoneGroup.cpp @@ -6,16 +6,18 @@ #include "gc/ZoneGroup.h" #include "jscntxt.h" #include "jit/IonBuilder.h" #include "jit/JitCompartment.h" +using namespace js; + namespace js { ZoneGroup::ZoneGroup(JSRuntime* runtime) : runtime(runtime), ownerContext_(TlsContext.get()), enterCount(1), zones_(this), usedByHelperThread(false), @@ -142,8 +144,31 @@ ZoneGroup::deleteEmptyZone(Zone* zone) zone->destroy(runtime->defaultFreeOp()); return; } } MOZ_CRASH("Zone not found"); } } // namespace js + +JS::AutoRelinquishZoneGroups::AutoRelinquishZoneGroups(JSContext* cx) + : cx(cx) +{ + MOZ_ASSERT(cx == TlsContext.get()); + + AutoEnterOOMUnsafeRegion oomUnsafe; + for (ZoneGroupsIter group(cx->runtime()); !group.done(); group.next()) { + while (group->ownerContext().context() == cx) { + group->leave(); + if (!enterList.append(group)) + oomUnsafe.crash("AutoRelinquishZoneGroups"); + } + } +} + +JS::AutoRelinquishZoneGroups::~AutoRelinquishZoneGroups() +{ + for (size_t i = 0; i < enterList.length(); i++) { + ZoneGroup* group = static_cast<ZoneGroup*>(enterList[i]); + group->enter(cx); + } +}
deleted file mode 100644 --- a/js/src/jit-test/tests/ion/bailoutStaticObject.js +++ /dev/null @@ -1,28 +0,0 @@ -// Test bailouts from loading particular non-singleton static objects. - -function wrap(fun) { - function wrapper() { - return fun.apply(this, arguments); - } - return wrapper; -} - -var adder = wrap(function(a, b) { return a + b; }); -var subber = wrap(function(a, b) { return a - b; }); -var tmp = adder; -adder = subber; -adder = tmp; - -function foo() { - var i = 0; - var a = 0; - for (var i = 0; i < 10000; i++) { - a = adder(a, 1); - a = subber(a, 1); - } - return a; -} - -assertEq(foo(), 0); -adder = subber; -assertEq(foo(), -20000);
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/ion/idempotentCache.js @@ -0,0 +1,34 @@ +// Test that we don't attach ICs to idempotent caches that are incompatible +// with the cache result type. + +var missingObjs = [{a:1},Object.create({a:2}),{}]; +function testMissing(limit) +{ + var res = 0; + for (var i = 0; i < 1000; i++) { + for (var j = 0; j < missingObjs.length; j++) { + var obj = missingObjs[j]; + if (j < limit) + res += obj.a; + } + } + return res; +} +assertEq(testMissing(2), 3000); +assertEq(testMissing(3), NaN); + +var lengthObjs = [{length:{a:1}},Object.create({length:{a:2}}),[0,1]]; +function testArrayLength(limit) +{ + var res = 0; + for (var i = 0; i < 1000; i++) { + for (var j = 0; j < lengthObjs.length; j++) { + var obj = lengthObjs[j]; + if (j < limit) + res += obj.length.a; + } + } + return res; +} +assertEq(testArrayLength(2), 3000); +assertEq(testArrayLength(3), NaN);
--- a/js/src/jit/BaselineBailouts.cpp +++ b/js/src/jit/BaselineBailouts.cpp @@ -1998,17 +1998,16 @@ jit::FinishBailoutToBaseline(BaselineBai case Bailout_NonObjectInput: case Bailout_NonStringInput: case Bailout_NonSymbolInput: case Bailout_UnexpectedSimdInput: case Bailout_NonSharedTypedArrayInput: case Bailout_Debugger: case Bailout_UninitializedThis: case Bailout_BadDerivedConstructorReturn: - case Bailout_LoadStaticObject: // Do nothing. break; case Bailout_FirstExecution: // Do not return directly, as this was not frequent in the first place, // thus rely on the check for frequent bailouts to recompile the current // script. break;
--- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -4803,17 +4803,18 @@ BaselineCompiler::emit_JSOP_RESUME() // If profiler instrumentation is on, update lastProfilingFrame on // current JitActivation { Register scratchReg = scratch2; Label skip; AbsoluteAddress addressOfEnabled(cx->runtime()->geckoProfiler().addressOfEnabled()); masm.branch32(Assembler::Equal, addressOfEnabled, Imm32(0), &skip); - masm.loadPtr(AbsoluteAddress(cx->addressOfProfilingActivation()), scratchReg); + masm.loadJSContext(scratchReg); + masm.loadPtr(Address(scratchReg, JSContext::offsetOfProfilingActivation()), scratchReg); masm.storePtr(masm.getStackPointer(), Address(scratchReg, JitActivation::offsetOfLastProfilingFrame())); masm.bind(&skip); } // Construct BaselineFrame. masm.push(BaselineFrameReg); masm.moveStackPtrTo(BaselineFrameReg);
--- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -805,18 +805,20 @@ DoGetElemFallback(JSContext* cx, Baselin bool attached = false; bool isTemporarilyUnoptimizable = false; if (stub->state().maybeTransition()) stub->discardStubs(cx); if (stub->state().canAttachStub()) { ICStubEngine engine = ICStubEngine::Baseline; - GetPropIRGenerator gen(cx, script, pc, CacheKind::GetElem, stub->state().mode(), - &isTemporarilyUnoptimizable, lhs, rhs, lhs, CanAttachGetter::Yes); + GetPropIRGenerator gen(cx, script, pc, + CacheKind::GetElem, stub->state().mode(), + &isTemporarilyUnoptimizable, lhs, rhs, lhs, + GetPropertyResultFlags::All); if (gen.tryAttachStub()) { ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), BaselineCacheIRStubKind::Monitored, engine, script, stub, &attached); if (newStub) { JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub"); if (gen.shouldNotePreliminaryObjectStub()) newStub->toCacheIR_Monitored()->notePreliminaryObject(); @@ -879,17 +881,17 @@ DoGetElemSuperFallback(JSContext* cx, Ba if (stub->state().maybeTransition()) stub->discardStubs(cx); if (stub->state().canAttachStub()) { ICStubEngine engine = ICStubEngine::Baseline; GetPropIRGenerator gen(cx, script, pc, CacheKind::GetElemSuper, stub->state().mode(), &isTemporarilyUnoptimizable, lhs, rhs, receiver, - CanAttachGetter::Yes); + GetPropertyResultFlags::All); if (gen.tryAttachStub()) { ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), BaselineCacheIRStubKind::Monitored, engine, script, stub, &attached); if (newStub) { JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub"); if (gen.shouldNotePreliminaryObjectStub()) newStub->toCacheIR_Monitored()->notePreliminaryObject();
--- a/js/src/jit/BaselineInspector.cpp +++ b/js/src/jit/BaselineInspector.cpp @@ -1035,17 +1035,17 @@ GetMegamorphicGetterSetterFunction(ICStu JSObject* obj = isGetter ? propShape->getterObject() : propShape->setterObject(); return &obj->as<JSFunction>(); } bool BaselineInspector::megamorphicGetterSetterFunction(jsbytecode* pc, bool isGetter, JSFunction** getterOrSetter) { - if (!hasBaselineScript() || *pc == JSOP_SETALIASEDVAR) + if (!hasBaselineScript()) return false; *getterOrSetter = nullptr; const ICEntry& entry = icEntryFromPC(pc); for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) { if (stub->isCacheIR_Monitored()) { MOZ_ASSERT(isGetter); @@ -1186,17 +1186,17 @@ AddCacheIRSetPropFunction(ICCacheIR_Upda } bool BaselineInspector::commonSetPropFunction(jsbytecode* pc, JSObject** holder, Shape** holderShape, JSFunction** commonSetter, bool* isOwnProperty, ReceiverVector& receivers, ObjectGroupVector& convertUnboxedGroups) { - if (!hasBaselineScript() || *pc == JSOP_SETALIASEDVAR) + if (!hasBaselineScript()) return false; MOZ_ASSERT(receivers.empty()); MOZ_ASSERT(convertUnboxedGroups.empty()); *commonSetter = nullptr; const ICEntry& entry = icEntryFromPC(pc);
--- a/js/src/jit/BaselineJIT.cpp +++ b/js/src/jit/BaselineJIT.cpp @@ -1261,13 +1261,15 @@ MarkActiveBaselineScripts(JSContext* cx, } void jit::MarkActiveBaselineScripts(Zone* zone) { if (zone->isAtomsZone()) return; JSContext* cx = TlsContext.get(); - for (JitActivationIterator iter(cx, zone->group()->ownerContext()); !iter.done(); ++iter) { - if (iter->compartment()->zone() == zone) - MarkActiveBaselineScripts(cx, iter); + for (const CooperatingContext& target : cx->runtime()->cooperatingContexts()) { + for (JitActivationIterator iter(cx, target); !iter.done(); ++iter) { + if (iter->compartment()->zone() == zone) + MarkActiveBaselineScripts(cx, iter); + } } }
--- a/js/src/jit/CacheIR.cpp +++ b/js/src/jit/CacheIR.cpp @@ -43,23 +43,23 @@ IRGenerator::IRGenerator(JSContext* cx, cacheKind_(cacheKind), mode_(mode) {} GetPropIRGenerator::GetPropIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, CacheKind cacheKind, ICState::Mode mode, bool* isTemporarilyUnoptimizable, HandleValue val, HandleValue idVal, HandleValue receiver, - CanAttachGetter canAttachGetter) + GetPropertyResultFlags resultFlags) : IRGenerator(cx, script, pc, cacheKind, mode), val_(val), idVal_(idVal), receiver_(receiver), isTemporarilyUnoptimizable_(isTemporarilyUnoptimizable), - canAttachGetter_(canAttachGetter), + resultFlags_(resultFlags), preliminaryObjectAction_(PreliminaryObjectAction::None) {} static void EmitLoadSlotResult(CacheIRWriter& writer, ObjOperandId holderOp, NativeObject* holder, Shape* shape) { if (holder->isFixedSlot(shape->slot())) { @@ -242,72 +242,75 @@ GetPropIRGenerator::tryAttachStub() trackNotAttached(); return false; } bool GetPropIRGenerator::tryAttachIdempotentStub() { - // For idempotent ICs, only attach stubs for plain data properties. - // This ensures (1) the lookup has no side-effects and (2) Ion has complete - // static type information and we don't have to monitor the result. Because - // of (2), we don't support for instance missing properties or array - // lengths, as TI does not account for these cases. + // For idempotent ICs, only attach stubs which we can be sure have no side + // effects and produce a result which the MIR in the calling code is able + // to handle, since we do not have a pc to explicitly monitor the result. MOZ_ASSERT(idempotent()); RootedObject obj(cx_, &val_.toObject()); RootedId id(cx_, NameToId(idVal_.toString()->asAtom().asPropertyName())); ValOperandId valId(writer.setInputOperandId(0)); ObjOperandId objId = writer.guardIsObject(valId); if (tryAttachNative(obj, objId, id)) return true; + // Object lengths are supported only if int32 results are allowed. + if ((resultFlags_ & GetPropertyResultFlags::AllowInt32) && tryAttachObjectLength(obj, objId, id)) + return true; + // Also support native data properties on DOMProxy prototypes. if (GetProxyStubType(cx_, obj, id) == ProxyStubType::DOMUnshadowed) return tryAttachDOMProxyUnshadowed(obj, objId, id); return false; } static bool IsCacheableNoProperty(JSContext* cx, JSObject* obj, JSObject* holder, Shape* shape, jsid id, - jsbytecode* pc) + jsbytecode* pc, GetPropertyResultFlags resultFlags) { if (shape) return false; MOZ_ASSERT(!holder); - if (!pc) { - // This is an idempotent IC, don't attach a missing-property stub. - // See tryAttachStub. + // Idempotent ICs may only attach missing-property stubs if undefined + // results are explicitly allowed, since no monitoring is done of the + // cache result. + if (!pc && !(resultFlags & GetPropertyResultFlags::AllowUndefined)) return false; - } // If we're doing a name lookup, we have to throw a ReferenceError. If // extra warnings are enabled, we may have to report a warning. - if (*pc == JSOP_GETBOUNDNAME || cx->compartment()->behaviors().extraWarnings(cx)) + // Note that Ion does not generate idempotent caches for JSOP_GETBOUNDNAME. + if ((pc && *pc == JSOP_GETBOUNDNAME) || cx->compartment()->behaviors().extraWarnings(cx)) return false; return CheckHasNoSuchProperty(cx, obj, id); } enum NativeGetPropCacheability { CanAttachNone, CanAttachReadSlot, CanAttachCallGetter, }; static NativeGetPropCacheability CanAttachNativeGetProp(JSContext* cx, HandleObject obj, HandleId id, MutableHandleNativeObject holder, MutableHandleShape shape, - jsbytecode* pc, CanAttachGetter canAttachGetter, + jsbytecode* pc, GetPropertyResultFlags resultFlags, bool* isTemporarilyUnoptimizable) { MOZ_ASSERT(JSID_IS_STRING(id) || JSID_IS_SYMBOL(id)); // The lookup needs to be universally pure, otherwise we risk calling hooks out // of turn. We don't mind doing this even when purity isn't required, because we // only miss out on shape hashification, which is only a temporary perf cost. // The limits were arbitrarily set, anyways. @@ -322,32 +325,27 @@ CanAttachNativeGetProp(JSContext* cx, Ha return CanAttachNone; holder.set(&baseHolder->as<NativeObject>()); } shape.set(prop.maybeShape()); if (IsCacheableGetPropReadSlotForIonOrCacheIR(obj, holder, prop)) return CanAttachReadSlot; - // Idempotent ICs only support plain data properties, see - // tryAttachIdempotentStub. - if (!pc) - return CanAttachNone; - - if (IsCacheableNoProperty(cx, obj, holder, shape, id, pc)) + if (IsCacheableNoProperty(cx, obj, holder, shape, id, pc, resultFlags)) return CanAttachReadSlot; - if (canAttachGetter == CanAttachGetter::No) - return CanAttachNone; - - if (IsCacheableGetPropCallScripted(obj, holder, shape, isTemporarilyUnoptimizable)) - return CanAttachCallGetter; - - if (IsCacheableGetPropCallNative(obj, holder, shape)) - return CanAttachCallGetter; + // Idempotent ICs cannot call getters, see tryAttachIdempotentStub. + if (pc && (resultFlags & GetPropertyResultFlags::Monitored)) { + if (IsCacheableGetPropCallScripted(obj, holder, shape, isTemporarilyUnoptimizable)) + return CanAttachCallGetter; + + if (IsCacheableGetPropCallNative(obj, holder, shape)) + return CanAttachCallGetter; + } return CanAttachNone; } static void GeneratePrototypeGuards(CacheIRWriter& writer, JSObject* obj, JSObject* holder, ObjOperandId objId) { // The guards here protect against the effects of JSObject::swap(). If the @@ -577,20 +575,18 @@ GetPropIRGenerator::attachMegamorphicNat bool GetPropIRGenerator::tryAttachNative(HandleObject obj, ObjOperandId objId, HandleId id) { RootedShape shape(cx_); RootedNativeObject holder(cx_); NativeGetPropCacheability type = CanAttachNativeGetProp(cx_, obj, id, &holder, &shape, pc_, - canAttachGetter_, + resultFlags_, isTemporarilyUnoptimizable_); - MOZ_ASSERT_IF(idempotent(), - type == CanAttachNone || (type == CanAttachReadSlot && holder)); switch (type) { case CanAttachNone: return false; case CanAttachReadSlot: if (mode_ == ICState::Mode::Megamorphic) { attachMegamorphicNativeSlot(objId, id, holder == nullptr); return true; } @@ -608,16 +604,17 @@ GetPropIRGenerator::tryAttachNative(Hand } EmitReadSlotResult(writer, obj, holder, shape, objId); EmitReadSlotReturn(writer, obj, holder, shape); trackAttached("NativeSlot"); return true; case CanAttachCallGetter: { // |super.prop| accesses use a |this| value that differs from lookup object + MOZ_ASSERT(!idempotent()); ObjOperandId receiverId = isSuper() ? writer.guardIsObject(getSuperReceiverValueId()) : objId; maybeEmitIdGuard(id); EmitCallGetterResult(writer, obj, holder, shape, objId, receiverId, mode_); trackAttached("NativeGetter"); return true; } @@ -646,17 +643,17 @@ GetPropIRGenerator::tryAttachWindowProxy MOZ_ASSERT(obj->getClass() == cx_->runtime()->maybeWindowProxyClass()); MOZ_ASSERT(ToWindowIfWindowProxy(obj) == cx_->global()); // Now try to do the lookup on the Window (the current global). HandleObject windowObj = cx_->global(); RootedShape shape(cx_); RootedNativeObject holder(cx_); NativeGetPropCacheability type = CanAttachNativeGetProp(cx_, windowObj, id, &holder, &shape, pc_, - canAttachGetter_, + resultFlags_, isTemporarilyUnoptimizable_); switch (type) { case CanAttachNone: return false; case CanAttachReadSlot: { maybeEmitIdGuard(id); writer.guardClass(objId, GuardClassKind::WindowProxy); @@ -733,18 +730,18 @@ GetPropIRGenerator::tryAttachCrossCompar MOZ_ASSERT(ToWindowIfWindowProxy(unwrapped) == unwrapped->compartment()->maybeGlobal()); unwrapped = cx_->global(); MOZ_ASSERT(unwrapped); } RootedShape shape(cx_); RootedNativeObject holder(cx_); NativeGetPropCacheability canCache = - CanAttachNativeGetProp(cx_, unwrapped, id, &holder, &shape, pc_, canAttachGetter_, - isTemporarilyUnoptimizable_); + CanAttachNativeGetProp(cx_, unwrapped, id, &holder, &shape, pc_, + resultFlags_, isTemporarilyUnoptimizable_); if (canCache != CanAttachReadSlot) return false; if (holder) { EnsureTrackPropertyTypes(cx_, holder, id); if (unwrapped == holder) { // See the comment in StripPreliminaryObjectStubs. if (IsPreliminaryObject(unwrapped)) @@ -958,17 +955,17 @@ GetPropIRGenerator::tryAttachDOMProxyExp expandoObj = &expandoAndGeneration->expando.toObject(); } // Try to do the lookup on the expando object. RootedNativeObject holder(cx_); RootedShape propShape(cx_); NativeGetPropCacheability canCache = CanAttachNativeGetProp(cx_, expandoObj, id, &holder, &propShape, pc_, - canAttachGetter_, isTemporarilyUnoptimizable_); + resultFlags_, isTemporarilyUnoptimizable_); if (canCache != CanAttachReadSlot && canCache != CanAttachCallGetter) return false; if (!holder) return false; MOZ_ASSERT(holder == expandoObj); maybeEmitIdGuard(id); @@ -1049,20 +1046,18 @@ GetPropIRGenerator::tryAttachDOMProxyUns RootedObject checkObj(cx_, obj->staticPrototype()); if (!checkObj) return false; RootedNativeObject holder(cx_); RootedShape shape(cx_); NativeGetPropCacheability canCache = CanAttachNativeGetProp(cx_, checkObj, id, &holder, &shape, - pc_, canAttachGetter_, + pc_, resultFlags_, isTemporarilyUnoptimizable_); - MOZ_ASSERT_IF(idempotent(), - canCache == CanAttachNone || (canCache == CanAttachReadSlot && holder)); if (canCache == CanAttachNone) return false; maybeEmitIdGuard(id); writer.guardShape(objId, obj->maybeShape()); // Guard that our expando object hasn't started shadowing this property. CheckDOMProxyExpandoDoesNotShadow(writer, obj, id, objId); @@ -1389,17 +1384,17 @@ GetPropIRGenerator::tryAttachPrimitive(V return false; } if (!proto) return false; RootedShape shape(cx_); RootedNativeObject holder(cx_); NativeGetPropCacheability type = CanAttachNativeGetProp(cx_, proto, id, &holder, &shape, pc_, - canAttachGetter_, + resultFlags_, isTemporarilyUnoptimizable_); if (type != CanAttachReadSlot) return false; if (holder) { // Instantiate this property, for use during Ion compilation. if (IsIonEnabled(cx_)) EnsureTrackPropertyTypes(cx_, holder, id);
--- a/js/src/jit/CacheIR.h +++ b/js/src/jit/CacheIR.h @@ -1142,26 +1142,57 @@ class MOZ_RAII IRGenerator public: explicit IRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, CacheKind cacheKind, ICState::Mode mode); const CacheIRWriter& writerRef() const { return writer; } CacheKind cacheKind() const { return cacheKind_; } }; -enum class CanAttachGetter { Yes, No }; +// Flags used to describe what values a GetProperty cache may produce. +enum class GetPropertyResultFlags { + None = 0, + + // Values produced by this cache will go through a type barrier, + // so the cache may produce any type of value that is compatible with its + // result operand. + Monitored = 1 << 0, + + // Whether particular primitives may be produced by this cache. + AllowUndefined = 1 << 1, + AllowInt32 = 1 << 2, + AllowDouble = 1 << 3, + + All = Monitored | AllowUndefined | AllowInt32 | AllowDouble +}; + +static inline bool operator&(GetPropertyResultFlags a, GetPropertyResultFlags b) +{ + return static_cast<int>(a) & static_cast<int>(b); +} + +static inline GetPropertyResultFlags operator|(GetPropertyResultFlags a, GetPropertyResultFlags b) +{ + return static_cast<GetPropertyResultFlags>(static_cast<int>(a) | static_cast<int>(b)); +} + +static inline GetPropertyResultFlags& operator|=(GetPropertyResultFlags& lhs, GetPropertyResultFlags b) +{ + lhs = lhs | b; + return lhs; +} // GetPropIRGenerator generates CacheIR for a GetProp IC. class MOZ_RAII GetPropIRGenerator : public IRGenerator { HandleValue val_; HandleValue idVal_; HandleValue receiver_; bool* isTemporarilyUnoptimizable_; - CanAttachGetter canAttachGetter_; + GetPropertyResultFlags resultFlags_; enum class PreliminaryObjectAction { None, Unlink, NotePreliminary }; PreliminaryObjectAction preliminaryObjectAction_; bool tryAttachNative(HandleObject obj, ObjOperandId objId, HandleId id); bool tryAttachUnboxed(HandleObject obj, ObjOperandId objId, HandleId id); bool tryAttachUnboxedExpando(HandleObject obj, ObjOperandId objId, HandleId id); bool tryAttachTypedObject(HandleObject obj, ObjOperandId objId, HandleId id); @@ -1228,17 +1259,18 @@ class MOZ_RAII GetPropIRGenerator : publ void maybeEmitIdGuard(jsid id); void trackAttached(const char* name); void trackNotAttached(); public: GetPropIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, CacheKind cacheKind, ICState::Mode mode, bool* isTemporarilyUnoptimizable, HandleValue val, - HandleValue idVal, HandleValue receiver, CanAttachGetter canAttachGetter); + HandleValue idVal, HandleValue receiver, + GetPropertyResultFlags resultFlags); bool tryAttachStub(); bool tryAttachIdempotentStub(); bool shouldUnlinkPreliminaryObjectStubs() const { return preliminaryObjectAction_ == PreliminaryObjectAction::Unlink; } bool shouldNotePreliminaryObjectStub() const {
--- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -10280,28 +10280,27 @@ CodeGenerator::visitGetNameCache(LGetNam IonGetNameIC ic(liveRegs, envChain, output, temp); addIC(ins, allocateIC(ic)); } void CodeGenerator::addGetPropertyCache(LInstruction* ins, LiveRegisterSet liveRegs, TypedOrValueRegister value, const ConstantOrRegister& id, TypedOrValueRegister output, Register maybeTemp, - bool monitoredResult, bool allowDoubleResult, + GetPropertyResultFlags resultFlags, jsbytecode* profilerLeavePc) { CacheKind kind = CacheKind::GetElem; if (id.constant() && id.value().isString()) { JSString* idString = id.value().toString(); uint32_t dummy; if (idString->isAtom() && !idString->asAtom().isIndex(&dummy)) kind = CacheKind::GetProp; } - IonGetPropertyIC cache(kind, liveRegs, value, id, output, maybeTemp, monitoredResult, - allowDoubleResult); + IonGetPropertyIC cache(kind, liveRegs, value, id, output, maybeTemp, resultFlags); addIC(ins, allocateIC(cache)); } void CodeGenerator::addSetPropertyCache(LInstruction* ins, LiveRegisterSet liveRegs, Register objReg, Register temp, FloatRegister tempDouble, FloatRegister tempF32, const ConstantOrRegister& id, const ConstantOrRegister& value, @@ -10328,44 +10327,71 @@ CodeGenerator::toConstantOrRegister(LIns const LAllocation* value = lir->getOperand(n); if (value->isConstant()) return ConstantOrRegister(value->toConstant()->toJSValue()); return TypedOrValueRegister(type, ToAnyRegister(value)); } +static GetPropertyResultFlags +IonGetPropertyICFlags(const MGetPropertyCache* mir) +{ + GetPropertyResultFlags flags = GetPropertyResultFlags::None; + if (mir->monitoredResult()) + flags |= GetPropertyResultFlags::Monitored; + + if (mir->type() == MIRType::Value) { + if (TemporaryTypeSet* types = mir->resultTypeSet()) { + if (types->hasType(TypeSet::UndefinedType())) + flags |= GetPropertyResultFlags::AllowUndefined; + if (types->hasType(TypeSet::Int32Type())) + flags |= GetPropertyResultFlags::AllowInt32; + if (types->hasType(TypeSet::DoubleType())) + flags |= GetPropertyResultFlags::AllowDouble; + } else { + flags |= GetPropertyResultFlags::AllowUndefined + | GetPropertyResultFlags::AllowInt32 + | GetPropertyResultFlags::AllowDouble; + } + } else if (mir->type() == MIRType::Int32) { + flags |= GetPropertyResultFlags::AllowInt32; + } else if (mir->type() == MIRType::Double) { + flags |= GetPropertyResultFlags::AllowInt32 | GetPropertyResultFlags::AllowDouble; + } + + return flags; +} + void CodeGenerator::visitGetPropertyCacheV(LGetPropertyCacheV* ins) { LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); TypedOrValueRegister value = toConstantOrRegister(ins, LGetPropertyCacheV::Value, ins->mir()->value()->type()).reg(); ConstantOrRegister id = toConstantOrRegister(ins, LGetPropertyCacheV::Id, ins->mir()->idval()->type()); - bool monitoredResult = ins->mir()->monitoredResult(); TypedOrValueRegister output = TypedOrValueRegister(GetValueOutput(ins)); Register maybeTemp = ins->temp()->isBogusTemp() ? InvalidReg : ToRegister(ins->temp()); - addGetPropertyCache(ins, liveRegs, value, id, output, maybeTemp, monitoredResult, - ins->mir()->allowDoubleResult(), ins->mir()->profilerLeavePc()); + addGetPropertyCache(ins, liveRegs, value, id, output, maybeTemp, + IonGetPropertyICFlags(ins->mir()), ins->mir()->profilerLeavePc()); } void CodeGenerator::visitGetPropertyCacheT(LGetPropertyCacheT* ins) { LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); TypedOrValueRegister value = toConstantOrRegister(ins, LGetPropertyCacheV::Value, ins->mir()->value()->type()).reg(); ConstantOrRegister id = toConstantOrRegister(ins, LGetPropertyCacheT::Id, ins->mir()->idval()->type()); - bool monitoredResult = ins->mir()->monitoredResult(); TypedOrValueRegister output(ins->mir()->type(), ToAnyRegister(ins->getDef(0))); Register maybeTemp = ins->temp()->isBogusTemp() ? InvalidReg : ToRegister(ins->temp()); - addGetPropertyCache(ins, liveRegs, value, id, output, maybeTemp, monitoredResult, - ins->mir()->allowDoubleResult(), ins->mir()->profilerLeavePc()); + addGetPropertyCache(ins, liveRegs, value, id, output, maybeTemp, + IonGetPropertyICFlags(ins->mir()), ins->mir()->profilerLeavePc()); } void CodeGenerator::visitBindNameCache(LBindNameCache* ins) { LiveRegisterSet liveRegs = ins->safepoint()->liveRegs(); Register envChain = ToRegister(ins->environmentChain()); Register output = ToRegister(ins->output());
--- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -2,16 +2,17 @@ * vim: set ts=8 sts=4 et sw=4 tw=99: * 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/. */ #ifndef jit_CodeGenerator_h #define jit_CodeGenerator_h +#include "jit/CacheIR.h" #include "jit/IonCaches.h" #if defined(JS_ION_PERF) # include "jit/PerfSpewer.h" #endif #if defined(JS_CODEGEN_X86) # include "jit/x86/CodeGenerator-x86.h" #elif defined(JS_CODEGEN_X64) @@ -463,18 +464,18 @@ class CodeGenerator final : public CodeG IonScriptCounts* counts = scriptCounts_; scriptCounts_ = nullptr; // prevent delete in dtor return counts; } private: void addGetPropertyCache(LInstruction* ins, LiveRegisterSet liveRegs, TypedOrValueRegister value, const ConstantOrRegister& id, - TypedOrValueRegister output, Register maybeTemp, bool monitoredResult, - bool allowDoubleResult, jsbytecode* profilerLeavePc); + TypedOrValueRegister output, Register maybeTemp, + GetPropertyResultFlags flags, jsbytecode* profilerLeavePc); void addSetPropertyCache(LInstruction* ins, LiveRegisterSet liveRegs, Register objReg, Register temp, FloatRegister tempDouble, FloatRegister tempF32, const ConstantOrRegister& id, const ConstantOrRegister& value, bool strict, bool needsPostBarrier, bool needsTypeBarrier, bool guardHoles, jsbytecode* profilerLeavePc); MOZ_MUST_USE bool generateBranchV(const ValueOperand& value, Label* ifTrue, Label* ifFalse,
--- a/js/src/jit/CompileWrappers.cpp +++ b/js/src/jit/CompileWrappers.cpp @@ -32,17 +32,17 @@ CompileRuntime::addressOfGCZealModeBits( #endif const JitRuntime* CompileRuntime::jitRuntime() { return runtime()->jitRuntime(); } -GeckoProfiler& +GeckoProfilerRuntime& CompileRuntime::geckoProfiler() { return runtime()->geckoProfiler(); } bool CompileRuntime::jitSupportsFloatingPoint() {
--- a/js/src/jit/CompileWrappers.h +++ b/js/src/jit/CompileWrappers.h @@ -29,17 +29,17 @@ class CompileRuntime #ifdef JS_GC_ZEAL const void* addressOfGCZealModeBits(); #endif const JitRuntime* jitRuntime(); // Compilation does not occur off thread when the Gecko Profiler is enabled. - GeckoProfiler& geckoProfiler(); + GeckoProfilerRuntime& geckoProfiler(); bool jitSupportsFloatingPoint(); bool hadOutOfMemory(); bool profilingScripts(); const JSAtomState& names(); const PropertyName* emptyString(); const StaticStrings& staticStrings();
--- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -3120,20 +3120,22 @@ jit::InvalidateAll(FreeOp* fop, Zone* zo // The caller should previously have cancelled off thread compilation. #ifdef DEBUG for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) MOZ_ASSERT(!HasOffThreadIonCompile(comp)); #endif if (zone->isAtomsZone()) return; JSContext* cx = TlsContext.get(); - for (JitActivationIterator iter(cx, zone->group()->ownerContext()); !iter.done(); ++iter) { - if (iter->compartment()->zone() == zone) { - JitSpew(JitSpew_IonInvalidate, "Invalidating all frames for GC"); - InvalidateActivation(fop, iter, true); + for (const CooperatingContext& target : cx->runtime()->cooperatingContexts()) { + for (JitActivationIterator iter(cx, target); !iter.done(); ++iter) { + if (iter->compartment()->zone() == zone) { + JitSpew(JitSpew_IonInvalidate, "Invalidating all frames for GC"); + InvalidateActivation(fop, iter, true); + } } } } void jit::Invalidate(TypeZone& types, FreeOp* fop, const RecompileInfoVector& invalid, bool resetUses, @@ -3174,18 +3176,20 @@ jit::Invalidate(TypeZone& types, FreeOp* // This method can be called both during GC and during the course of normal // script execution. In the former case this class will already be on the // stack, and in the latter case the invalidations will all be on the // current thread's stack, but the assertion under ActivationIterator can't // tell that this is a thread local use of the iterator. JSRuntime::AutoProhibitActiveContextChange apacc(fop->runtime()); JSContext* cx = TlsContext.get(); - for (JitActivationIterator iter(cx, types.zone()->group()->ownerContext()); !iter.done(); ++iter) - InvalidateActivation(fop, iter, false); + for (const CooperatingContext& target : cx->runtime()->cooperatingContexts()) { + for (JitActivationIterator iter(cx, target); !iter.done(); ++iter) + InvalidateActivation(fop, iter, false); + } // Drop the references added above. If a script was never active, its // IonScript will be immediately destroyed. Otherwise, it will be held live // until its last invalidated frame is destroyed. for (size_t i = 0; i < invalid.length(); i++) { CompilerOutput* co = invalid[i].compilerOutput(types); if (!co) continue;
--- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -2,16 +2,17 @@ * vim: set ts=8 sts=4 et sw=4 tw=99: * 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 "jit/IonBuilder.h" #include "mozilla/DebugOnly.h" +#include "mozilla/ScopeExit.h" #include "mozilla/SizePrintfMacros.h" #include "builtin/Eval.h" #include "builtin/TypedObject.h" #include "frontend/SourceNotes.h" #include "jit/BaselineFrame.h" #include "jit/BaselineInspector.h" #include "jit/Ion.h" @@ -831,21 +832,23 @@ IonBuilder::build() #ifdef DEBUG // lazyArguments should never be accessed in |argsObjAliasesFormals| scripts. if (info().hasArguments() && !info().argsObjAliasesFormals()) hasLazyArguments_ = true; #endif insertRecompileCheck(); + auto clearLastPriorResumePoint = mozilla::MakeScopeExit([&] { + // Discard unreferenced & pre-allocated resume points. + replaceMaybeFallbackFunctionGetter(nullptr); + }); + MOZ_TRY(traverseBytecode()); - // Discard unreferenced & pre-allocated resume points. - replaceMaybeFallbackFunctionGetter(nullptr); - if (script_->hasBaselineScript() && inlinedBytecodeLength_ > script_->baselineScript()->inlinedBytecodeLength()) { script_->baselineScript()->setInlinedBytecodeLength(inlinedBytecodeLength_); } MOZ_TRY(maybeAddOsrTypeBarriers()); MOZ_TRY(processIterators()); @@ -993,20 +996,23 @@ IonBuilder::buildInline(IonBuilder* call #endif insertRecompileCheck(); // Initialize the env chain now that all resume points operands are // initialized. MOZ_TRY(initEnvironmentChain(callInfo.fun())); + auto clearLastPriorResumePoint = mozilla::MakeScopeExit([&] { + // Discard unreferenced & pre-allocated resume points. + replaceMaybeFallbackFunctionGetter(nullptr); + }); + MOZ_TRY(traverseBytecode()); - // Discard unreferenced & pre-allocated resume points. - replaceMaybeFallbackFunctionGetter(nullptr); MOZ_ASSERT(iterators_.empty(), "Iterators should be added to outer builder"); if (!info().isAnalysis() && !abortedPreliminaryGroups().empty()) return abort(AbortReason::PreliminaryObjects); return Ok(); } @@ -7270,16 +7276,27 @@ IonBuilder::ensureDefiniteTypeSet(MDefin } // Create a NOP mir instruction to filter the typeset. MFilterTypeSet* filter = MFilterTypeSet::New(alloc(), def, types); current->add(filter); return filter; } +static size_t +NumFixedSlots(JSObject* object) +{ + // Note: we can't use object->numFixedSlots() here, as this will read the + // shape and can race with the active thread if we are building off thread. + // The allocation kind and object class (which goes through the type) can + // be read freely, however. + gc::AllocKind kind = object->asTenured().getAllocKind(); + return gc::GetGCKindSlots(kind, object->getClass()); +} + static bool IsUninitializedGlobalLexicalSlot(JSObject* obj, PropertyName* name) { LexicalEnvironmentObject &globalLexical = obj->as<LexicalEnvironmentObject>(); MOZ_ASSERT(globalLexical.isGlobal()); Shape* shape = globalLexical.lookupPure(name); if (!shape) return false; @@ -7291,58 +7308,42 @@ IonBuilder::getStaticName(bool* emitted, MDefinition* lexicalCheck) { MOZ_ASSERT(*emitted == false); jsid id = NameToId(name); bool isGlobalLexical = staticObject->is<LexicalEnvironmentObject>() && staticObject->as<LexicalEnvironmentObject>().isGlobal(); + MOZ_ASSERT(isGlobalLexical || + staticObject->is<GlobalObject>() || + staticObject->is<CallObject>() || + staticObject->is<ModuleEnvironmentObject>()); + MOZ_ASSERT(staticObject->isSingleton()); // Always emit the lexical check. This could be optimized, but is // currently not for simplicity's sake. if (lexicalCheck) return Ok(); - // Only optimize accesses on native objects. - if (!staticObject->isNative()) - return Ok(); - - // Only optimize accesses on own data properties. - Shape* propertyShape = staticObject->as<NativeObject>().lastProperty()->searchLinear(NameToId(name)); - if (!propertyShape || !propertyShape->isDataDescriptor() || !propertyShape->hasSlot()) - return Ok(); - uint32_t slot = propertyShape->slot(); - TypeSet::ObjectKey* staticKey = TypeSet::ObjectKey::get(staticObject); if (analysisContext) staticKey->ensureTrackedProperty(analysisContext, NameToId(name)); - // Make sure the property is a normal data property. This is not done for - // call objects, as they are not tracked by TI and their data properties - // cannot be dynamically reconfigured. - Maybe<HeapTypeSetKey> property; - if (!staticObject->is<CallObject>()) { - if (staticKey->unknownProperties()) - return Ok(); - - property.emplace(staticKey->property(id)); - - if (property.ref().nonData(constraints()) || - !property.ref().maybeTypes() || - !property.ref().maybeTypes()->definiteProperty()) - { - // We can't be sure the slot will match at runtime, so include a - // shape guard on the object. - MInstruction* obj = MConstant::NewConstraintlessObject(alloc(), staticObject); - current->add(obj); - addShapeGuard(obj, staticObject->as<NativeObject>().lastProperty(), Bailout_ShapeGuard); - } else { - MOZ_ASSERT(slot == property.ref().maybeTypes()->definiteSlot()); - } + if (staticKey->unknownProperties()) + return Ok(); + + HeapTypeSetKey property = staticKey->property(id); + if (!property.maybeTypes() || + !property.maybeTypes()->definiteProperty() || + property.nonData(constraints())) + { + // The property has been reconfigured as non-configurable, non-enumerable + // or non-writable. + return Ok(); } // Don't optimize global lexical bindings if they aren't initialized at // compile time. if (isGlobalLexical && IsUninitializedGlobalLexicalSlot(staticObject, name)) return Ok(); *emitted = true; @@ -7358,47 +7359,23 @@ IonBuilder::getStaticName(bool* emitted, if (testSingletonProperty(staticObject, id) == singleton) { pushConstant(ObjectValue(*singleton)); return Ok(); } } // Try to inline properties that have never been overwritten. Value constantValue; - if (property.isSome() && property.ref().constant(constraints(), &constantValue)) { + if (property.constant(constraints(), &constantValue)) { pushConstant(constantValue); return Ok(); } } - MOZ_TRY(loadStaticSlot(staticObject, barrier, types, slot)); - - // If the static object has a function object stored in this property, - // test that the result is that specific function. This is yet another - // technique for trying to force a property load to be a specific value, - // and is included because other mechanisms (property types and observed - // types) do not always work, especially in polymorphic framework code. - // We restrict this optimization to function properties, as they are less - // likely to change over time and are more likely to require precise - // information for inlining decisions. - if (!outermostBuilder()->script()->hadFrequentBailouts()) { - Value v = staticObject->as<NativeObject>().getSlot(slot); - if (v.isObject() && - v.toObject().is<JSFunction>() && - v.toObject().as<JSFunction>().isInterpreted()) - { - JSObject* result = checkNurseryObject(&v.toObject().as<JSFunction>()); - MDefinition* load = current->pop(); - MInstruction* expected = MConstant::NewConstraintlessObject(alloc(), result); - expected->setResultTypeSet(MakeSingletonTypeSet(constraints(), result)); - current->add(expected); - current->add(MGuardObjectIdentity::New(alloc(), load, expected, false, Bailout_LoadStaticObject)); - current->push(expected); - } - } + MOZ_TRY(loadStaticSlot(staticObject, barrier, types, property.maybeTypes()->definiteSlot())); return Ok(); } AbortReasonOr<Ok> IonBuilder::loadStaticSlot(JSObject* staticObject, BarrierKind barrier, TemporaryTypeSet* types, uint32_t slot) { @@ -7416,17 +7393,17 @@ IonBuilder::loadStaticSlot(JSObject* sta } MInstruction* obj = constant(ObjectValue(*staticObject)); MIRType rvalType = types->getKnownMIRType(); if (barrier != BarrierKind::NoBarrier) rvalType = MIRType::Value; - return loadSlot(obj, slot, staticObject->as<NativeObject>().numFixedSlots(), rvalType, barrier, types); + return loadSlot(obj, slot, NumFixedSlots(staticObject), rvalType, barrier, types); } // Whether a write of the given value may need a post-write barrier for GC purposes. bool jit::NeedsPostBarrier(MDefinition* value) { if (!GetJitContext()->compartment->zone()->nurseryExists()) return false; @@ -7481,18 +7458,17 @@ IonBuilder::setStaticName(JSObject* stat // If the property has a known type, we may be able to optimize typed stores by not // storing the type tag. MIRType slotType = MIRType::None; MIRType knownType = property.knownMIRType(constraints()); if (knownType != MIRType::Value) slotType = knownType; bool needsPreBarrier = property.needsBarrier(constraints()); - return storeSlot(obj, property.maybeTypes()->definiteSlot(), - staticObject->as<NativeObject>().numFixedSlots(), + return storeSlot(obj, property.maybeTypes()->definiteSlot(), NumFixedSlots(staticObject), value, needsPreBarrier, slotType); } JSObject* IonBuilder::testGlobalLexicalBinding(PropertyName* name) { MOZ_ASSERT(JSOp(*pc) == JSOP_BINDGNAME || JSOp(*pc) == JSOP_GETGNAME || @@ -10423,22 +10399,16 @@ IonBuilder::jsop_getprop(PropertyName* n return Ok(); // Try to inline a common property getter, or make a call. trackOptimizationAttempt(TrackedStrategy::GetProp_CommonGetter); MOZ_TRY(getPropTryCommonGetter(&emitted, obj, name, types)); if (emitted) return Ok(); - // Try to optimize for loads from a specific object. - trackOptimizationAttempt(TrackedStrategy::GetProp_Static); - MOZ_TRY(getPropTryStaticAccess(&emitted, obj, name, barrier, types)); - if (emitted) - return Ok(); - // Try to emit a monomorphic/polymorphic access based on baseline caches. trackOptimizationAttempt(TrackedStrategy::GetProp_InlineAccess); MOZ_TRY(getPropTryInlineAccess(&emitted, obj, name, barrier, types)); if (emitted) return Ok(); // Try to emit loads from a module namespace. trackOptimizationAttempt(TrackedStrategy::GetProp_ModuleNamespace); @@ -11229,27 +11199,16 @@ PropertyShapesHaveSameSlot(const Baselin return nullptr; } } return firstShape; } AbortReasonOr<Ok> -IonBuilder::getPropTryStaticAccess(bool* emitted, MDefinition* obj, PropertyName* name, - BarrierKind barrier, TemporaryTypeSet* types) -{ - if (!obj->isConstant() || obj->type() != MIRType::Object) - return Ok(); - - obj->setImplicitlyUsedUnchecked(); - return getStaticName(emitted, &obj->toConstant()->toObject(), name); -} - -AbortReasonOr<Ok> IonBuilder::getPropTryInlineAccess(bool* emitted, MDefinition* obj, PropertyName* name, BarrierKind barrier, TemporaryTypeSet* types) { MOZ_ASSERT(*emitted == false); BaselineInspector::ReceiverVector receivers(alloc()); BaselineInspector::ObjectGroupVector convertUnboxedGroups(alloc()); if (!inspector->maybeInfoForPropertyOp(pc, receivers, convertUnboxedGroups)) @@ -12672,59 +12631,21 @@ IonBuilder::walkEnvironmentChain(unsigne MInstruction* ins = MEnclosingEnvironment::New(alloc(), env); current->add(ins); env = ins; } return env; } -static bool -SearchEnvironmentChainForCallObject(JSObject* environment, JSScript* script, JSObject** pcall) -{ - while (environment && !environment->is<GlobalObject>()) { - if (environment->is<CallObject>() && - environment->as<CallObject>().callee().nonLazyScript() == script) - { - *pcall = environment; - return true; - } - environment = environment->enclosingEnvironment(); - } - return false; -} - bool IonBuilder::hasStaticEnvironmentObject(EnvironmentCoordinate ec, JSObject** pcall) { JSScript* outerScript = EnvironmentCoordinateFunctionScript(script(), pc); - if (!outerScript) - return false; - - // JSOP_SETALIASEDVAR only emits a cache when the outer script is a run - // once script. To avoid problems with the generic jsop_setprop() paths, - // only use static environment objects when a baseline cache exists. - if (*pc == JSOP_SETALIASEDVAR && !outerScript->treatAsRunOnce()) - return false; - - // If the callee is a specific JSFunction then there is a specific - // environment object on its chain we can use. - if (inlineCallInfo_) { - MDefinition* calleeDef = inlineCallInfo_->fun(); - if (calleeDef->isConstant()) { - JSFunction* callee = &calleeDef->toConstant()->toObject().template as<JSFunction>(); - JSObject* environment = callee->environment(); - if (SearchEnvironmentChainForCallObject(environment, outerScript, pcall)) - return true; - } - } - - // Otherwise, if the outer script will only run once then we can go looking - // for its call object. - if (!outerScript->treatAsRunOnce()) + if (!outerScript || !outerScript->treatAsRunOnce()) return false; TypeSet::ObjectKey* funKey = TypeSet::ObjectKey::get(outerScript->functionNonDelazifying()); if (funKey->hasFlags(constraints(), OBJECT_FLAG_RUNONCE_INVALIDATED)) return false; // The script this aliased var operation is accessing will run only once, @@ -12735,28 +12656,42 @@ IonBuilder::hasStaticEnvironmentObject(E // Look for the call object on the current script's function's env chain. // If the current script is inner to the outer script and the function has // singleton type then it should show up here. MDefinition* envDef = current->getSlot(info().environmentChainSlot()); envDef->setImplicitlyUsedUnchecked(); JSObject* environment = script()->functionNonDelazifying()->environment(); - if (SearchEnvironmentChainForCallObject(environment, outerScript, pcall)) - return true; + while (environment && !environment->is<GlobalObject>()) { + if (environment->is<CallObject>() && + environment->as<CallObject>().callee().nonLazyScript() == outerScript) + { + MOZ_ASSERT(environment->isSingleton()); + *pcall = environment; + return true; + } + environment = environment->enclosingEnvironment(); + } // Look for the call object on the current frame, if we are compiling the // outer script itself. Don't do this if we are at entry to the outer // script, as the call object we see will not be the real one --- after // entering the Ion code a different call object will be created. if (script() == outerScript && baselineFrame_ && info().osrPc()) { JSObject* singletonScope = baselineFrame_->singletonEnvChain; - if (SearchEnvironmentChainForCallObject(singletonScope, outerScript, pcall)) + if (singletonScope && + singletonScope->is<CallObject>() && + singletonScope->as<CallObject>().callee().nonLazyScript() == outerScript) + { + MOZ_ASSERT(singletonScope->isSingleton()); + *pcall = singletonScope; return true; + } } return true; } MDefinition* IonBuilder::getAliasedVar(EnvironmentCoordinate ec) {
--- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -241,18 +241,16 @@ class IonBuilder AbortReasonOr<Ok> getPropTryDefiniteSlot(bool* emitted, MDefinition* obj, PropertyName* name, BarrierKind barrier, TemporaryTypeSet* types); AbortReasonOr<Ok> getPropTryModuleNamespace(bool* emitted, MDefinition* obj, PropertyName* name, BarrierKind barrier, TemporaryTypeSet* types); AbortReasonOr<Ok> getPropTryUnboxed(bool* emitted, MDefinition* obj, PropertyName* name, BarrierKind barrier, TemporaryTypeSet* types); AbortReasonOr<Ok> getPropTryCommonGetter(bool* emitted, MDefinition* obj, PropertyName* name, TemporaryTypeSet* types, bool innerized = false); - AbortReasonOr<Ok> getPropTryStaticAccess(bool* emitted, MDefinition* obj, PropertyName* name, - BarrierKind barrier, TemporaryTypeSet* types); AbortReasonOr<Ok> getPropTryInlineAccess(bool* emitted, MDefinition* obj, PropertyName* name, BarrierKind barrier, TemporaryTypeSet* types); AbortReasonOr<Ok> getPropTryTypedObject(bool* emitted, MDefinition* obj, PropertyName* name); AbortReasonOr<Ok> getPropTryScalarPropOfTypedObject(bool* emitted, MDefinition* typedObj, int32_t fieldOffset, TypedObjectPrediction fieldTypeReprs); AbortReasonOr<Ok> getPropTryReferencePropOfTypedObject(bool* emitted, MDefinition* typedObj, int32_t fieldOffset,
--- a/js/src/jit/IonIC.cpp +++ b/js/src/jit/IonIC.cpp @@ -130,22 +130,21 @@ IonGetPropertyIC::update(JSContext* cx, ic->discardStubs(cx->zone()); bool attached = false; if (ic->state().canAttachStub()) { // IonBuilder calls PropertyReadNeedsTypeBarrier to determine if it // needs a type barrier. Unfortunately, PropertyReadNeedsTypeBarrier // does not account for getters, so we should only attach a getter // stub if we inserted a type barrier. - CanAttachGetter canAttachGetter = - ic->monitoredResult() ? CanAttachGetter::Yes : CanAttachGetter::No; jsbytecode* pc = ic->idempotent() ? nullptr : ic->pc(); bool isTemporarilyUnoptimizable = false; GetPropIRGenerator gen(cx, outerScript, pc, ic->kind(), ic->state().mode(), - &isTemporarilyUnoptimizable, val, idVal, val, canAttachGetter); + &isTemporarilyUnoptimizable, val, idVal, val, + ic->resultFlags()); if (ic->idempotent() ? gen.tryAttachIdempotentStub() : gen.tryAttachStub()) ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript, &attached); if (!attached && !isTemporarilyUnoptimizable) ic->state().trackNotAttached(); } if (!attached && ic->idempotent()) {
--- a/js/src/jit/IonIC.h +++ b/js/src/jit/IonIC.h @@ -173,47 +173,47 @@ class IonIC void attachCacheIRStub(JSContext* cx, const CacheIRWriter& writer, CacheKind kind, IonScript* ionScript, bool* attached, const PropertyTypeCheckInfo* typeCheckInfo = nullptr); }; class IonGetPropertyIC : public IonIC { + private: LiveRegisterSet liveRegs_; TypedOrValueRegister value_; ConstantOrRegister id_; TypedOrValueRegister output_; Register maybeTemp_; // Might be InvalidReg. - bool monitoredResult_ : 1; - bool allowDoubleResult_ : 1; + GetPropertyResultFlags resultFlags_; public: IonGetPropertyIC(CacheKind kind, LiveRegisterSet liveRegs, TypedOrValueRegister value, const ConstantOrRegister& id, TypedOrValueRegister output, Register maybeTemp, - bool monitoredResult, bool allowDoubleResult) + GetPropertyResultFlags resultFlags) : IonIC(kind), liveRegs_(liveRegs), value_(value), id_(id), output_(output), maybeTemp_(maybeTemp), - monitoredResult_(monitoredResult), - allowDoubleResult_(allowDoubleResult) + resultFlags_(resultFlags) { } - bool monitoredResult() const { return monitoredResult_; } TypedOrValueRegister value() const { return value_; } ConstantOrRegister id() const { return id_; } TypedOrValueRegister output() const { return output_; } Register maybeTemp() const { return maybeTemp_; } LiveRegisterSet liveRegs() const { return liveRegs_; } - bool allowDoubleResult() const { return allowDoubleResult_; } + GetPropertyResultFlags resultFlags() const { return resultFlags_; } + bool monitoredResult() const { return resultFlags_ & GetPropertyResultFlags::Monitored; } + bool allowDoubleResult() const { return resultFlags_ & GetPropertyResultFlags::AllowDouble; } static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript, IonGetPropertyIC* ic, HandleValue val, HandleValue idVal, MutableHandleValue res); }; class IonSetPropertyIC : public IonIC { LiveRegisterSet liveRegs_;
--- a/js/src/jit/IonInstrumentation.h +++ b/js/src/jit/IonInstrumentation.h @@ -4,28 +4,28 @@ * 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 jit_IonInstrumentatjit_h #define jit_IonInstrumentatjit_h namespace js { -class GeckoProfiler; +class GeckoProfilerRuntime; namespace jit { class MacroAssembler; typedef GeckoProfilerInstrumentation<MacroAssembler, Register> BaseInstrumentation; class IonInstrumentation : public BaseInstrumentation { public: - IonInstrumentation(GeckoProfiler* profiler, jsbytecode** pc) + IonInstrumentation(GeckoProfilerRuntime* profiler, jsbytecode** pc) : BaseInstrumentation(profiler) { MOZ_ASSERT(pc != nullptr); } }; } // namespace jit } // namespace js
--- a/js/src/jit/IonTypes.h +++ b/js/src/jit/IonTypes.h @@ -137,19 +137,16 @@ enum BailoutKind Bailout_NonStringInputInvalidate, // Used for integer division, multiplication and modulo. // If there's a remainder, bails to return a double. // Can also signal overflow or result of -0. // Can also signal division by 0 (returns inf, a double). Bailout_DoubleOutput, - // Load of a value from a static object retrieved an unexpected value. - Bailout_LoadStaticObject, - // END Invalid assumptions bailouts // A bailout at the very start of a function indicates that there may be // a type mismatch in the arguments that necessitates a reflow. Bailout_ArgumentCheck, // A bailout triggered by a bounds-check failure. @@ -231,18 +228,16 @@ BailoutKindString(BailoutKind kind) // Bailouts caused by invalid assumptions. case Bailout_OverflowInvalidate: return "Bailout_OverflowInvalidate"; case Bailout_NonStringInputInvalidate: return "Bailout_NonStringInputInvalidate"; case Bailout_DoubleOutput: return "Bailout_DoubleOutput"; - case Bailout_LoadStaticObject: - return "Bailout_LoadStaticObject"; // Other bailouts. case Bailout_ArgumentCheck: return "Bailout_ArgumentCheck"; case Bailout_BoundsCheck: return "Bailout_BoundsCheck"; case Bailout_Detached: return "Bailout_Detached";
--- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -3895,17 +3895,17 @@ LIRGenerator::visitCallBindVar(MCallBind define(lir, ins); } void LIRGenerator::visitGuardObjectIdentity(MGuardObjectIdentity* ins) { LGuardObjectIdentity* guard = new(alloc()) LGuardObjectIdentity(useRegister(ins->object()), useRegister(ins->expected())); - assignSnapshot(guard, ins->bailoutKind()); + assignSnapshot(guard, Bailout_ObjectIdentityOrTypeGuard); add(guard, ins); redefine(ins, ins->object()); } void LIRGenerator::visitGuardClass(MGuardClass* ins) { LDefinition t = temp();
--- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -6192,28 +6192,19 @@ jit::PropertyReadNeedsTypeBarrier(JSCont if (obj->unknownProperties()) break; HeapTypeSetKey property = obj->property(NameToId(name)); if (property.maybeTypes()) { TypeSet::TypeList types; if (!property.maybeTypes()->enumerateTypes(&types)) break; - // If there is a single possible type for the property, - // optimistically add it to the observed set. Don't do this - // for the special uninitialized lexical type, which will - // never actually be observed here and will cause problems - // downstream during compilation. - if (types.length() == 1 && - (!types[0].isPrimitive() || - types[0].primitive() != JSVAL_TYPE_MAGIC)) - { + if (types.length() == 1) { // Note: the return value here is ignored. observed->addType(types[0], GetJitContext()->temp->lifoAlloc()); - break; } break; } if (!obj->proto().isObject()) break; obj = TypeSet::ObjectKey::get(obj->proto().toObject()); } while (obj);
--- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -11485,47 +11485,39 @@ class MGuardObjectGroup }; // Guard on an object's identity, inclusively or exclusively. class MGuardObjectIdentity : public MBinaryInstruction, public SingleObjectPolicy::Data { bool bailOnEquality_; - BailoutKind bailoutKind_; - - MGuardObjectIdentity(MDefinition* obj, MDefinition* expected, bool bailOnEquality, - BailoutKind bailoutKind = Bailout_ObjectIdentityOrTypeGuard) + + MGuardObjectIdentity(MDefinition* obj, MDefinition* expected, bool bailOnEquality) : MBinaryInstruction(obj, expected), - bailOnEquality_(bailOnEquality), - bailoutKind_(bailoutKind) + bailOnEquality_(bailOnEquality) { setGuard(); setMovable(); setResultType(MIRType::Object); } public: INSTRUCTION_HEADER(GuardObjectIdentity) TRIVIAL_NEW_WRAPPERS NAMED_OPERANDS((0, object), (1, expected)) bool bailOnEquality() const { return bailOnEquality_; } - BailoutKind bailoutKind() const { - return bailoutKind_; - } bool congruentTo(const MDefinition* ins) const override { if (!ins->isGuardObjectIdentity()) return false; if (bailOnEquality() != ins->toGuardObjectIdentity()->bailOnEquality()) return false; - if (bailoutKind() != ins->toGuardObjectIdentity()->bailoutKind()) - return false; return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const override { return AliasSet::Load(AliasSet::ObjectFields); } }; // Guard on an object's class.
--- a/js/src/jit/SharedIC.cpp +++ b/js/src/jit/SharedIC.cpp @@ -2064,17 +2064,18 @@ DoGetPropFallback(JSContext* cx, Baselin if (stub->state().maybeTransition()) stub->discardStubs(cx); bool attached = false; if (stub->state().canAttachStub()) { RootedValue idVal(cx, StringValue(name)); GetPropIRGenerator gen(cx, script, pc, CacheKind::GetProp, stub->state().mode(), - &isTemporarilyUnoptimizable, val, idVal, val, CanAttachGetter::Yes); + &isTemporarilyUnoptimizable, val, idVal, val, + GetPropertyResultFlags::All); if (gen.tryAttachStub()) { ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), BaselineCacheIRStubKind::Monitored, ICStubEngine::Baseline, script, stub, &attached); if (newStub) { JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub"); if (gen.shouldNotePreliminaryObjectStub()) @@ -2135,17 +2136,17 @@ DoGetPropSuperFallback(JSContext* cx, Ba if (stub->state().maybeTransition()) stub->discardStubs(cx); bool attached = false; if (stub->state().canAttachStub()) { RootedValue idVal(cx, StringValue(name)); GetPropIRGenerator gen(cx, script, pc, CacheKind::GetPropSuper, stub->state().mode(), &isTemporarilyUnoptimizable, val, idVal, receiver, - CanAttachGetter::Yes); + GetPropertyResultFlags::All); if (gen.tryAttachStub()) { ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), BaselineCacheIRStubKind::Monitored, ICStubEngine::Baseline, script, stub, &attached); if (newStub) { JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub"); if (gen.shouldNotePreliminaryObjectStub())
--- a/js/src/jit/arm/Trampoline-arm.cpp +++ b/js/src/jit/arm/Trampoline-arm.cpp @@ -28,17 +28,17 @@ static const FloatRegisterSet NonVolatil (1ULL << FloatRegisters::d10) | (1ULL << FloatRegisters::d11) | (1ULL << FloatRegisters::d12) | (1ULL << FloatRegisters::d13) | (1ULL << FloatRegisters::d14) | (1ULL << FloatRegisters::d15)); static void -GenerateReturn(MacroAssembler& masm, int returnCode, GeckoProfiler* prof) +GenerateReturn(MacroAssembler& masm, int returnCode) { // Restore non-volatile floating point registers. masm.transferMultipleByRuns(NonVolatileFloatRegs, IsLoad, StackPointer, IA); // Get rid of padding word. masm.addPtr(Imm32(sizeof(void*)), sp); // Set up return value @@ -372,17 +372,17 @@ JitRuntime::generateEnterJIT(JSContext* // :TODO: Optimize storeValue with: // We're using a load-double here. In order for that to work, the data needs // to be stored in two consecutive registers, make sure this is the case // MOZ_ASSERT(JSReturnReg_Type.code() == JSReturnReg_Data.code()+1); // aasm->as_extdtr(IsStore, 64, true, Offset, // JSReturnReg_Data, EDtrAddr(r5, EDtrOffImm(0))); // Restore non-volatile registers and return. - GenerateReturn(masm, true, &cx->runtime()->geckoProfiler()); + GenerateReturn(masm, true); Linker linker(masm); AutoFlushICache afc("EnterJIT"); JitCode* code = linker.newCode<NoGC>(cx, OTHER_CODE); #ifdef JS_ION_PERF writePerfSpewerJitCodeProfile(code, "EnterJIT"); #endif
--- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -1025,16 +1025,33 @@ JS_ResumeCooperativeContext(JSContext* c // Create a new context on this thread for cooperative multithreading in the // same runtime as siblingContext. Called on a runtime (as indicated by // siblingContet) which has no active context, on success the new context will // become the runtime's active context. extern JS_PUBLIC_API(JSContext*) JS_NewCooperativeContext(JSContext* siblingContext); +namespace JS { + +// Class to relinquish exclusive access to all zone groups in use by this +// thread. This allows other cooperative threads to enter the zone groups +// and modify their contents. +struct AutoRelinquishZoneGroups +{ + explicit AutoRelinquishZoneGroups(JSContext* cx); + ~AutoRelinquishZoneGroups(); + + private: + JSContext* cx; + mozilla::Vector<void*> enterList; +}; + +} // namespace JS + // Destroy a context allocated with JS_NewContext or JS_NewCooperativeContext. // The context must be the current active context in the runtime, and after // this call the runtime will have no active context. extern JS_PUBLIC_API(void) JS_DestroyContext(JSContext* cx); JS_PUBLIC_API(void*) JS_GetContextPrivate(JSContext* cx);
--- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -1318,17 +1318,17 @@ ArrayJoinKernel(JSContext* cx, Separator // ES2017 draft rev 1b0184bc17fc09a8ddcf4aeec9b6d9fcac4eafce // 22.1.3.13 Array.prototype.join ( separator ) bool js::array_join(JSContext* cx, unsigned argc, Value* vp) { if (!CheckRecursionLimit(cx)) return false; - AutoGeckoProfilerEntry pseudoFrame(cx->runtime(), "Array.prototype.join"); + AutoGeckoProfilerEntry pseudoFrame(cx, "Array.prototype.join"); CallArgs args = CallArgsFromVp(argc, vp); // Step 1. RootedObject obj(cx, ToObject(cx, args.thisv())); if (!obj) return false; AutoCycleDetector detector(cx, obj); @@ -1578,17 +1578,17 @@ ArrayReverseDenseKernel(JSContext* cx, H DefineBoxedOrUnboxedFunctor3(ArrayReverseDenseKernel, JSContext*, HandleObject, uint32_t); // ES2017 draft rev 1b0184bc17fc09a8ddcf4aeec9b6d9fcac4eafce // 22.1.3.21 Array.prototype.reverse ( ) bool js::array_reverse(JSContext* cx, unsigned argc, Value* vp) { - AutoGeckoProfilerEntry pseudoFrame(cx->runtime(), "Array.prototype.reverse"); + AutoGeckoProfilerEntry pseudoFrame(cx, "Array.prototype.reverse"); CallArgs args = CallArgsFromVp(argc, vp); // Step 1. RootedObject obj(cx, ToObject(cx, args.thisv())); if (!obj) return false; // Step 2. @@ -2283,17 +2283,17 @@ js::NewbornArrayPush(JSContext* cx, Hand return true; } // ES2017 draft rev 1b0184bc17fc09a8ddcf4aeec9b6d9fcac4eafce // 22.1.3.18 Array.prototype.push ( ...items ) bool js::array_push(JSContext* cx, unsigned argc, Value* vp) { - AutoGeckoProfilerEntry pseudoFrame(cx->runtime(), "Array.prototype.push"); + AutoGeckoProfilerEntry pseudoFrame(cx, "Array.prototype.push"); CallArgs args = CallArgsFromVp(argc, vp); // Step 1. RootedObject obj(cx, ToObject(cx, args.thisv())); if (!obj) return false; // Step 2. @@ -2340,17 +2340,17 @@ js::array_push(JSContext* cx, unsigned a return SetLengthProperty(cx, obj, newlength); } // ES2017 draft rev 1b0184bc17fc09a8ddcf4aeec9b6d9fcac4eafce // 22.1.3.17 Array.prototype.pop ( ) bool js::array_pop(JSContext* cx, unsigned argc, Value* vp) { - AutoGeckoProfilerEntry pseudoFrame(cx->runtime(), "Array.prototype.pop"); + AutoGeckoProfilerEntry pseudoFrame(cx, "Array.prototype.pop"); CallArgs args = CallArgsFromVp(argc, vp); // Step 1. RootedObject obj(cx, ToObject(cx, args.thisv())); if (!obj) return false; // Step 2. @@ -2445,17 +2445,17 @@ ArrayShiftDenseKernel(JSContext* cx, Han DefineBoxedOrUnboxedFunctor3(ArrayShiftDenseKernel, JSContext*, HandleObject, MutableHandleValue); // ES2017 draft rev 1b0184bc17fc09a8ddcf4aeec9b6d9fcac4eafce // 22.1.3.22 Array.prototype.shift ( ) bool js::array_shift(JSContext* cx, unsigned argc, Value* vp) { - AutoGeckoProfilerEntry pseudoFrame(cx->runtime(), "Array.prototype.shift"); + AutoGeckoProfilerEntry pseudoFrame(cx, "Array.prototype.shift"); CallArgs args = CallArgsFromVp(argc, vp); // Step 1. RootedObject obj(cx, ToObject(cx, args.thisv())); if (!obj) return false; // Step 2. @@ -2521,17 +2521,17 @@ js::array_shift(JSContext* cx, unsigned return SetLengthProperty(cx, obj, newlen); } // ES2017 draft rev 1b0184bc17fc09a8ddcf4aeec9b6d9fcac4eafce // 22.1.3.29 Array.prototype.unshift ( ...items ) bool js::array_unshift(JSContext* cx, unsigned argc, Value* vp) { - AutoGeckoProfilerEntry pseudoFrame(cx->runtime(), "Array.prototype.unshift"); + AutoGeckoProfilerEntry pseudoFrame(cx, "Array.prototype.unshift"); CallArgs args = CallArgsFromVp(argc, vp); // Step 1. RootedObject obj(cx, ToObject(cx, args.thisv())); if (!obj) return false; // Step 2. @@ -2764,17 +2764,17 @@ CopyArrayElements(JSContext* cx, HandleO return false; } return true; } static bool array_splice_impl(JSContext* cx, unsigned argc, Value* vp, bool returnValueIsUsed) { - AutoGeckoProfilerEntry pseudoFrame(cx->runtime(), "Array.prototype.splice"); + AutoGeckoProfilerEntry pseudoFrame(cx, "Array.prototype.splice"); CallArgs args = CallArgsFromVp(argc, vp); /* Step 1. */ RootedObject obj(cx, ToObject(cx, args.thisv())); if (!obj) return false; /* Step 2. */ @@ -3272,17 +3272,17 @@ ArraySliceOrdinary(JSContext* cx, Handle rval.setObject(*narr); return true; } /* ES 2016 draft Mar 25, 2016 22.1.3.23. */ bool js::array_slice(JSContext* cx, unsigned argc, Value* vp) { - AutoGeckoProfilerEntry pseudoFrame(cx->runtime(), "Array.prototype.slice"); + AutoGeckoProfilerEntry pseudoFrame(cx, "Array.prototype.slice"); CallArgs args = CallArgsFromVp(argc, vp); /* Step 1. */ RootedObject obj(cx, ToObject(cx, args.thisv())); if (!obj) return false; /* Step 2. */
--- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -1585,16 +1585,17 @@ JSContext::sizeOfExcludingThis(mozilla:: */ return cycleDetectorVector().sizeOfExcludingThis(mallocSizeOf); } void JSContext::trace(JSTracer* trc) { cycleDetectorVector().trace(trc); + geckoProfiler().trace(trc); if (trc->isMarkingTracer() && compartment_) compartment_->mark(); } void* JSContext::stackLimitAddressForJitCode(JS::StackKind kind) {
--- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -385,19 +385,16 @@ struct JSContext : public JS::RootingCon } static size_t offsetOfActivation() { return offsetof(JSContext, activation_); } js::Activation* profilingActivation() const { return profilingActivation_; } - void* addressOfProfilingActivation() { - return (void*) &profilingActivation_; - } static size_t offsetOfProfilingActivation() { return offsetof(JSContext, profilingActivation_); } private: /* Space for interpreter frames. */ js::ThreadLocalData<js::InterpreterStack> interpreterStack_; @@ -598,16 +595,22 @@ struct JSContext : public JS::RootingCon } void disableProfilerSampling() { suppressProfilerSampling = true; } void enableProfilerSampling() { suppressProfilerSampling = false; } + private: + /* Gecko profiling metadata */ + js::UnprotectedData<js::GeckoProfilerThread> geckoProfiler_; + public: + js::GeckoProfilerThread& geckoProfiler() { return geckoProfiler_.ref(); } + #if defined(XP_DARWIN) js::wasm::MachExceptionHandler wasmMachExceptionHandler; #endif /* Temporary arena pool used while compiling and decompiling. */ static const size_t TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 4 * 1024; private: js::ThreadLocalData<js::LifoAlloc> tempLifoAlloc_;
--- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -6211,17 +6211,17 @@ AllNurseriesAreEmpty(JSRuntime* rt) } #endif /* Start a new heap session. */ AutoTraceSession::AutoTraceSession(JSRuntime* rt, JS::HeapState heapState) : lock(rt), runtime(rt), prevState(TlsContext.get()->heapState), - pseudoFrame(rt, HeapStateToLabel(heapState), ProfileEntry::Category::GC) + pseudoFrame(TlsContext.get(), HeapStateToLabel(heapState), ProfileEntry::Category::GC) { MOZ_ASSERT(prevState == JS::HeapState::Idle); MOZ_ASSERT(heapState != JS::HeapState::Idle); MOZ_ASSERT_IF(heapState == JS::HeapState::MajorCollecting, AllNurseriesAreEmpty(rt)); TlsContext.get()->heapState = heapState; } AutoTraceSession::~AutoTraceSession()
--- a/js/src/jsprf.cpp +++ b/js/src/jsprf.cpp @@ -24,21 +24,16 @@ JS_PUBLIC_API(JS::UniqueChars) JS_smprin { va_list ap; va_start(ap, fmt); JSSmprintfPointer result = mozilla::Vsmprintf<js::SystemAllocPolicy>(fmt, ap); va_end(ap); return JS::UniqueChars(result.release()); } -JS_PUBLIC_API(void) JS_smprintf_free(char* mem) -{ - mozilla::SmprintfFree<js::SystemAllocPolicy>(mem); -} - JS_PUBLIC_API(JS::UniqueChars) JS_sprintf_append(JS::UniqueChars&& last, const char* fmt, ...) { va_list ap; va_start(ap, fmt); JSSmprintfPointer lastPtr(last.release()); JSSmprintfPointer result = mozilla::VsmprintfAppend<js::SystemAllocPolicy>(Move(lastPtr), fmt, ap); va_end(ap);
--- a/js/src/jsprf.h +++ b/js/src/jsprf.h @@ -15,18 +15,16 @@ #include "js/Utility.h" /* Wrappers for mozilla::Smprintf and friends that are used throughout JS. */ extern JS_PUBLIC_API(JS::UniqueChars) JS_smprintf(const char* fmt, ...) MOZ_FORMAT_PRINTF(1, 2); -extern JS_PUBLIC_API(void) JS_smprintf_free(char* mem); - extern JS_PUBLIC_API(JS::UniqueChars) JS_sprintf_append(JS::UniqueChars&& last, const char* fmt, ...) MOZ_FORMAT_PRINTF(2, 3); extern JS_PUBLIC_API(JS::UniqueChars) JS_vsmprintf(const char* fmt, va_list ap) MOZ_FORMAT_PRINTF(1, 0); extern JS_PUBLIC_API(JS::UniqueChars) JS_vsprintf_append(JS::UniqueChars&& last, const char* fmt, va_list ap)
--- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -4194,34 +4194,37 @@ JSScript::argumentsOptimizationFailed(JS * MagicValue(JS_OPTIMIZED_ARGUMENTS) on the stack. However, there are * three things that need fixup: * - there may be any number of activations of this script that don't have * an argsObj that now need one. * - jit code compiled (and possible active on the stack) with the static * assumption of !script->needsArgsObj(); * - type inference data for the script assuming script->needsArgsObj */ - for (AllScriptFramesIter i(cx); !i.done(); ++i) { - /* - * We cannot reliably create an arguments object for Ion activations of - * this script. To maintain the invariant that "script->needsArgsObj - * implies fp->hasArgsObj", the Ion bail mechanism will create an - * arguments object right after restoring the BaselineFrame and before - * entering Baseline code (in jit::FinishBailoutToBaseline). - */ - if (i.isIon()) - continue; - AbstractFramePtr frame = i.abstractFramePtr(); - if (frame.isFunctionFrame() && frame.script() == script) { - /* We crash on OOM since cleaning up here would be complicated. */ - AutoEnterOOMUnsafeRegion oomUnsafe; - ArgumentsObject* argsobj = ArgumentsObject::createExpected(cx, frame); - if (!argsobj) - oomUnsafe.crash("JSScript::argumentsOptimizationFailed"); - SetFrameArgumentsObject(cx, frame, script, argsobj); + JSRuntime::AutoProhibitActiveContextChange apacc(cx->runtime()); + for (const CooperatingContext& target : cx->runtime()->cooperatingContexts()) { + for (AllScriptFramesIter i(cx, target); !i.done(); ++i) { + /* + * We cannot reliably create an arguments object for Ion activations of + * this script. To maintain the invariant that "script->needsArgsObj + * implies fp->hasArgsObj", the Ion bail mechanism will create an + * arguments object right after restoring the BaselineFrame and before + * entering Baseline code (in jit::FinishBailoutToBaseline). + */ + if (i.isIon()) + continue; + AbstractFramePtr frame = i.abstractFramePtr(); + if (frame.isFunctionFrame() && frame.script() == script) { + /* We crash on OOM since cleaning up here would be complicated. */ + AutoEnterOOMUnsafeRegion oomUnsafe; + ArgumentsObject* argsobj = ArgumentsObject::createExpected(cx, frame); + if (!argsobj) + oomUnsafe.crash("JSScript::argumentsOptimizationFailed"); + SetFrameArgumentsObject(cx, frame, script, argsobj); + } } } return true; } bool JSScript::formalIsAliased(unsigned argSlot)
--- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -1483,18 +1483,17 @@ Evaluate(JSContext* cx, unsigned argc, V bool saveIncrementalBytecode = false; bool assertEqBytecode = false; RootedObject callerGlobal(cx, cx->global()); options.setIntroductionType("js shell evaluate") .setFileAndLine("@evaluate", 1); global = JS_GetGlobalForObject(cx, &args.callee()); - if (!global) - return false; + MOZ_ASSERT(global); if (args.length() == 2) { RootedObject opts(cx, &args[1].toObject()); RootedValue v(cx); if (!ParseCompileOptions(cx, options, opts, fileNameBytes)) return false; @@ -1524,16 +1523,51 @@ Evaluate(JSContext* cx, unsigned argc, V } if (!global || !(JS_GetClass(global)->flags & JSCLASS_IS_GLOBAL)) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE, "\"global\" passed to evaluate()", "not a global object"); return false; } } + if (!JS_GetProperty(cx, opts, "zoneGroup", &v)) + return false; + if (!v.isUndefined()) { + if (global != JS_GetGlobalForObject(cx, &args.callee())) { + JS_ReportErrorASCII(cx, "zoneGroup and global cannot both be specified."); + return false; + } + + // Find all eligible globals to execute in: any global in another + // zone group which has not been entered by a cooperative thread. + JS::AutoObjectVector eligibleGlobals(cx); + for (CompartmentsIter c(cx->runtime(), SkipAtoms); !c.done(); c.next()) { + if (!c->zone()->group()->ownerContext().context() && + c->maybeGlobal() && + !cx->runtime()->isSelfHostingGlobal(c->maybeGlobal())) + { + if (!eligibleGlobals.append(c->maybeGlobal())) + return false; + } + } + + if (eligibleGlobals.empty()) { + JS_ReportErrorASCII(cx, "zoneGroup can only be used if another" + " cooperative thread has called cooperativeYield(true)."); + return false; + } + + // Pick an eligible global to use based on the value of the zoneGroup property. + int32_t which; + if (!ToInt32(cx, v, &which)) + return false; + which = Min<int32_t>(Max(which, 0), eligibleGlobals.length() - 1); + global = eligibleGlobals[which]; + } + if (!JS_GetProperty(cx, opts, "catchTermination", &v)) return false; if (!v.isUndefined()) catchTermination = ToBoolean(v); if (!JS_GetProperty(cx, opts, "loadBytecode", &v)) return false; if (!v.isUndefined()) @@ -3290,19 +3324,39 @@ CooperativeYieldThread(JSContext* cx, un return false; } if (cooperationState->singleThreaded) { JS_ReportErrorASCII(cx, "Yielding is not allowed while single threaded"); return false; } - CooperativeBeginWait(cx); - CooperativeYield(); - CooperativeEndWait(cx); + // To avoid contention issues between threads, yields are not allowed while + // a thread has access to zone groups other than its original one, i.e. if + // the thread is inside an evaluate() call with a different zone group. + // This is not a limit which the browser has, but is necessary in the + // shell: the shell can have arbitrary interleavings between cooperative + // threads, whereas the browser has more control over which threads are + // running at different times. + for (ZoneGroupsIter group(cx->runtime()); !group.done(); group.next()) { + if (group->ownerContext().context() == cx && group != cx->zone()->group()) { + JS_ReportErrorASCII(cx, "Yielding is not allowed while owning multiple zone groups"); + return false; + } + } + + { + Maybe<JS::AutoRelinquishZoneGroups> artzg; + if ((args.length() > 0) && ToBoolean(args[0])) + artzg.emplace(cx); + + CooperativeBeginWait(cx); + CooperativeYield(); + CooperativeEndWait(cx); + } args.rval().setUndefined(); return true; } static void CooperativeBeginSingleThreadedExecution(JSContext* cx) { @@ -3327,16 +3381,35 @@ CooperativeBeginSingleThreadedExecution( static void CooperativeEndSingleThreadedExecution(JSContext* cx) { if (cooperationState) cooperationState->singleThreaded = false; } +static bool +EnsureGeckoProfilingStackInstalled(JSContext* cx, ShellContext* sc) +{ + if (cx->geckoProfiler().installed()) { + MOZ_ASSERT(sc->geckoProfilingStack); + return true; + } + + MOZ_ASSERT(!sc->geckoProfilingStack); + sc->geckoProfilingStack = MakeUnique<PseudoStack>(); + if (!sc->geckoProfilingStack) { + JS_ReportOutOfMemory(cx); + return false; + } + + SetContextProfilingStack(cx, sc->geckoProfilingStack.get()); + return true; +} + struct WorkerInput { JSRuntime* parentRuntime; JSContext* siblingContext; char16_t* chars; size_t length; WorkerInput(JSRuntime* parentRuntime, char16_t* chars, size_t length) @@ -3395,16 +3468,20 @@ WorkerMain(void* arg) js::UseInternalJobQueues(cx); if (!JS::InitSelfHostedCode(cx)) return; environmentPreparer.emplace(cx); } else { JS_AddInterruptCallback(cx, ShellInterruptCallback); + + // The Gecko Profiler requires that all cooperating contexts have + // profiling stacks installed. + MOZ_ALWAYS_TRUE(EnsureGeckoProfilingStackInstalled(cx, sc.get())); } do { JSAutoRequest ar(cx); JS::CompartmentOptions compartmentOptions; SetStandardCompartmentOptions(compartmentOptions); if (input->siblingContext) @@ -3425,21 +3502,16 @@ WorkerMain(void* arg) if (!JS::Compile(cx, options, input->chars, input->length, &script)) break; RootedValue result(cx); JS_ExecuteScript(cx, script, &result); } while (0); KillWatchdog(cx); JS_SetGrayGCRootsTracer(cx, nullptr, nullptr); - - if (sc->geckoProfilingStack) { - MOZ_ALWAYS_TRUE(cx->runtime()->geckoProfiler().enable(false)); - SetContextProfilingStack(cx, nullptr); - } } // Workers can spawn other workers, so we need a lock to access workerThreads. static Mutex* workerThreadsLock = nullptr; static Vector<js::Thread*, 0, SystemAllocPolicy> workerThreads; class MOZ_RAII AutoLockWorkerThreads : public LockGuard<Mutex> { @@ -5205,116 +5277,80 @@ static bool IsLatin1(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); bool isLatin1 = args.get(0).isString() && args[0].toString()->hasLatin1Chars(); args.rval().setBoolean(isLatin1); return true; } -static bool -EnsureGeckoProfilingStackInstalled(JSContext* cx, ShellContext* sc) -{ - if (cx->runtime()->geckoProfiler().installed()) { - if (!sc->geckoProfilingStack) { - JS_ReportErrorASCII(cx, "Profiler already installed by another context"); - return false; - } - - return true; - } - - MOZ_ASSERT(!sc->geckoProfilingStack); - sc->geckoProfilingStack = MakeUnique<PseudoStack>(); - if (!sc->geckoProfilingStack) { - JS_ReportOutOfMemory(cx); - return false; - } - - SetContextProfilingStack(cx, sc->geckoProfilingStack.get()); +// Set the profiling stack for each cooperating context in a runtime. +static bool +EnsureAllContextProfilingStacks(JSContext* cx) +{ + for (const CooperatingContext& target : cx->runtime()->cooperatingContexts()) { + ShellContext* sc = GetShellContext(target.context()); + if (!EnsureGeckoProfilingStackInstalled(target.context(), sc)) + return false; + } + return true; } static bool EnableGeckoProfiling(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); - ShellContext* sc = GetShellContext(cx); - - if (!EnsureGeckoProfilingStackInstalled(cx, sc)) - return false; - - // Disable before re-enabling; see the assertion in - // |GeckoProfiler::setProfilingStack|. - if (cx->runtime()->geckoProfiler().installed()) - MOZ_ALWAYS_TRUE(cx->runtime()->geckoProfiler().enable(false)); + if (!EnsureAllContextProfilingStacks(cx)) + return false; cx->runtime()->geckoProfiler().enableSlowAssertions(false); - if (!cx->runtime()->geckoProfiler().enable(true)) { - JS_ReportErrorASCII(cx, "Cannot ensure single threaded execution in profiler"); - return false; - } + cx->runtime()->geckoProfiler().enable(true); args.rval().setUndefined(); return true; } static bool EnableGeckoProfilingWithSlowAssertions(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); args.rval().setUndefined(); - ShellContext* sc = GetShellContext(cx); - - if (!EnsureGeckoProfilingStackInstalled(cx, sc)) - return false; - if (cx->runtime()->geckoProfiler().enabled()) { // If profiling already enabled with slow assertions disabled, // this is a no-op. if (cx->runtime()->geckoProfiler().slowAssertionsEnabled()) return true; // Slow assertions are off. Disable profiling before re-enabling // with slow assertions on. - MOZ_ALWAYS_TRUE(cx->runtime()->geckoProfiler().enable(false)); - } - - // Disable before re-enabling; see the assertion in |GeckoProfiler::setProfilingStack|. - if (cx->runtime()->geckoProfiler().installed()) - MOZ_ALWAYS_TRUE(cx->runtime()->geckoProfiler().enable(false)); + cx->runtime()->geckoProfiler().enable(false); + } + + if (!EnsureAllContextProfilingStacks(cx)) + return false; cx->runtime()->geckoProfiler().enableSlowAssertions(true); - if (!cx->runtime()->geckoProfiler().enable(true)) { - JS_ReportErrorASCII(cx, "Cannot ensure single threaded execution in profiler"); - return false; - } + cx->runtime()->geckoProfiler().enable(true); return true; } static bool DisableGeckoProfiling(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); args.rval().setUndefined(); - ShellContext* sc = GetShellContext(cx); - - if (!cx->runtime()->geckoProfiler().installed()) + if (!cx->runtime()->geckoProfiler().enabled()) return true; - if (!sc->geckoProfilingStack) { - JS_ReportErrorASCII(cx, "Profiler was not installed by this context"); - return false; - } - - MOZ_ALWAYS_TRUE(cx->runtime()->geckoProfiler().enable(false)); + cx->runtime()->geckoProfiler().enable(false); return true; } // Global mailbox that is used to communicate a SharedArrayBuffer // value from one worker to another. // // For simplicity we store only the SharedArrayRawBuffer; retaining // the SAB object would require per-runtime storage, and would have no @@ -6148,16 +6184,18 @@ static const JSFunctionSpecWithHelp shel " Evaluate code as though it were the contents of a file.\n" " options is an optional object that may have these properties:\n" " isRunOnce: use the isRunOnce compiler option (default: false)\n" " noScriptRval: use the no-script-rval compiler option (default: false)\n" " fileName: filename for error messages and debug info\n" " lineNumber: starting line number for error messages and debug info\n" " columnNumber: starting column number for error messages and debug info\n" " global: global in which to execute the code\n" +" zoneGroup: pick a global from another zone group with no current context\n" +" to execute the code in\n" " newContext: if true, create and use a new cx (default: false)\n" " catchTermination: if true, catch termination (failure without\n" " an exception value, as for slow scripts or out-of-memory)\n" " and return 'terminated'\n" " element: if present with value |v|, convert |v| to an object |o| and\n" " mark the source as being attached to the DOM element |o|. If the\n" " property is omitted or |v| is null, don't attribute the source to\n" " any DOM element.\n" @@ -6286,19 +6324,21 @@ static const JSFunctionSpecWithHelp shel JS_FN_HELP("evalInWorker", EvalInWorker, 1, 0, "evalInWorker(str)", " Evaluate 'str' in a separate thread with its own runtime.\n"), JS_FN_HELP("evalInCooperativeThread", EvalInCooperativeThread, 1, 0, "evalInCooperativeThread(str)", " Evaluate 'str' in a separate cooperatively scheduled thread using the same runtime.\n"), - JS_FN_HELP("cooperativeYield", CooperativeYieldThread, 0, 0, -"evalInCooperativeThread()", -" Yield execution to another cooperatively scheduled thread using the same runtime.\n"), + JS_FN_HELP("cooperativeYield", CooperativeYieldThread, 1, 0, +"cooperativeYield(leaveZoneGroup)", +" Yield execution to another cooperatively scheduled thread using the same runtime.\n" +" If leaveZoneGroup is specified then other threads may execute code in the\n" +" current thread's zone group via evaluate(..., {zoneGroup:N}).\n"), JS_FN_HELP("getSharedArrayBuffer", GetSharedArrayBuffer, 0, 0, "getSharedArrayBuffer()", " Retrieve the SharedArrayBuffer object from the cross-worker mailbox.\n" " The object retrieved may not be identical to the object that was\n" " installed, but it references the same shared memory.\n" " getSharedArrayBuffer performs an ordering memory barrier.\n"),
--- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -2590,31 +2590,33 @@ UpdateExecutionObservabilityOfScriptsInZ } } // Code below this point must be infallible to ensure the active bit of // BaselineScripts is in a consistent state. // // Mark active baseline scripts in the observable set so that they don't // get discarded. They will be recompiled. - for (JitActivationIterator actIter(cx, zone->group()->ownerContext()); !actIter.done(); ++actIter) { - if (actIter->compartment()->zone() != zone) - continue; - - for (JitFrameIterator iter(actIter); !iter.done(); ++iter) { - switch (iter.type()) { - case JitFrame_BaselineJS: - MarkBaselineScriptActiveIfObservable(iter.script(), obs); - break; - case JitFrame_IonJS: - MarkBaselineScriptActiveIfObservable(iter.script(), obs); - for (InlineFrameIterator inlineIter(cx, &iter); inlineIter.more(); ++inlineIter) - MarkBaselineScriptActiveIfObservable(inlineIter.script(), obs); - break; - default:; + for (const CooperatingContext& target : cx->runtime()->cooperatingContexts()) { + for (JitActivationIterator actIter(cx, target); !actIter.done(); ++actIter) { + if (actIter->compartment()->zone() != zone) + continue; + + for (JitFrameIterator iter(actIter); !iter.done(); ++iter) { + switch (iter.type()) { + case JitFrame_BaselineJS: + MarkBaselineScriptActiveIfObservable(iter.script(), obs); + break; + case JitFrame_IonJS: + MarkBaselineScriptActiveIfObservable(iter.script(), obs); + for (InlineFrameIterator inlineIter(cx, &iter); inlineIter.more(); ++inlineIter) + MarkBaselineScriptActiveIfObservable(inlineIter.script(), obs); + break; + default:; + } } } } // Iterate through the scripts again and finish discarding // BaselineScripts. This must be done as a separate phase as we can only // discard the BaselineScript on scripts that have no IonScript. for (size_t i = 0; i < scripts.length(); i++) {
--- a/js/src/vm/GeckoProfiler-inl.h +++ b/js/src/vm/GeckoProfiler-inl.h @@ -4,20 +4,36 @@ * 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 vm_GeckoProfiler_inl_h #define vm_GeckoProfiler_inl_h #include "vm/GeckoProfiler.h" +#include "jscntxt.h" + #include "vm/Runtime.h" namespace js { +inline void +GeckoProfilerThread::updatePC(JSContext* cx, JSScript* script, jsbytecode* pc) +{ + if (!cx->runtime()->geckoProfiler().enabled()) + return; + + uint32_t sp = pseudoStack_->stackPointer; + if (sp - 1 < PseudoStack::MaxEntries) { + MOZ_ASSERT(sp > 0); + MOZ_ASSERT(pseudoStack_->entries[sp - 1].rawScript() == script); + pseudoStack_->entries[sp - 1].setPC(pc); + } +} + /* * This class is used to suppress profiler sampling during * critical sections where stack state is not valid. */ class MOZ_RAII AutoSuppressProfilerSampling { public: explicit AutoSuppressProfilerSampling(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
--- a/js/src/vm/GeckoProfiler.cpp +++ b/js/src/vm/GeckoProfiler.cpp @@ -20,48 +20,49 @@ #include "vm/StringBuffer.h" #include "jsgcinlines.h" using namespace js; using mozilla::DebugOnly; -GeckoProfiler::GeckoProfiler(JSRuntime* rt) +GeckoProfilerThread::GeckoProfilerThread() + : pseudoStack_(nullptr) +{ +} + +GeckoProfilerRuntime::GeckoProfilerRuntime(JSRuntime* rt) : rt(rt), strings(mutexid::GeckoProfilerStrings), - pseudoStack_(nullptr), slowAssertions(false), enabled_(false), eventMarker_(nullptr) { MOZ_ASSERT(rt != nullptr); } bool -GeckoProfiler::init() +GeckoProfilerRuntime::init() { auto locked = strings.lock(); if (!locked->init()) return false; return true; } void -GeckoProfiler::setProfilingStack(PseudoStack* pseudoStack) +GeckoProfilerThread::setProfilingStack(PseudoStack* pseudoStack) { - MOZ_ASSERT_IF(pseudoStack_, !enabled()); - MOZ_ASSERT(strings.lock()->initialized()); - pseudoStack_ = pseudoStack; } void -GeckoProfiler::setEventMarker(void (*fn)(const char*)) +GeckoProfilerRuntime::setEventMarker(void (*fn)(const char*)) { eventMarker_ = fn; } /* Get a pointer to the top-most profiling frame, given the exit frame pointer. */ static void* GetTopProfilingJitFrame(Activation* act) { @@ -73,34 +74,29 @@ GetTopProfilingJitFrame(Activation* act) if (!exitFP) return nullptr; jit::JitProfilingFrameIterator iter(exitFP); MOZ_ASSERT(!iter.done()); return iter.fp(); } -bool -GeckoProfiler::enable(bool enabled) +void +GeckoProfilerRuntime::enable(bool enabled) { - MOZ_ASSERT(installed()); +#ifdef DEBUG + // All cooperating contexts must have profile stacks installed before the + // profiler can be enabled. Cooperating threads created while the profiler + // is enabled must have stacks set before they execute any JS. + for (const CooperatingContext& target : rt->cooperatingContexts()) + MOZ_ASSERT(target.context()->geckoProfiler().installed()); +#endif if (enabled_ == enabled) - return true; - - // Execution in the runtime must be single threaded if the Gecko profiler - // is enabled. There is only a single profiler stack in the runtime, from - // which entries must be added/removed in a LIFO fashion. - JSContext* cx = rt->activeContextFromOwnThread(); - if (enabled) { - if (!rt->beginSingleThreadedExecution(cx)) - return false; - } else { - rt->endSingleThreadedExecution(cx); - } + return; /* * Ensure all future generated code will be instrumented, or that all * currently instrumented code is discarded */ ReleaseAllJITCode(rt->defaultFreeOp()); // This function is called when the Gecko profiler makes a new Sampler @@ -158,69 +154,67 @@ GeckoProfiler::enable(bool enabled) } } // WebAssembly code does not need to be released, but profiling string // labels have to be generated so that they are available during async // profiling stack iteration. for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) c->wasm.ensureProfilingLabels(enabled); - - return true; } /* Lookup the string for the function/script, creating one if necessary */ const char* -GeckoProfiler::profileString(JSScript* script, JSFunction* maybeFun) +GeckoProfilerRuntime::profileString(JSScript* script, JSFunction* maybeFun) { auto locked = strings.lock(); MOZ_ASSERT(locked->initialized()); ProfileStringMap::AddPtr s = locked->lookupForAdd(script); if (!s) { auto str = allocProfileString(script, maybeFun); if (!str || !locked->add(s, script, mozilla::Move(str))) return nullptr; } return s->value().get(); } void -GeckoProfiler::onScriptFinalized(JSScript* script) +GeckoProfilerRuntime::onScriptFinalized(JSScript* script) { /* * This function is called whenever a script is destroyed, regardless of * whether profiling has been turned on, so don't invoke a function on an * invalid hash set. Also, even if profiling was enabled but then turned * off, we still want to remove the string, so no check of enabled() is * done. */ auto locked = strings.lock(); if (!locked->initialized()) return; if (ProfileStringMap::Ptr entry = locked->lookup(script)) locked->remove(entry); } void -GeckoProfiler::markEvent(const char* event) +GeckoProfilerRuntime::markEvent(const char* event) { MOZ_ASSERT(enabled()); if (eventMarker_) { JS::AutoSuppressGCAnalysis nogc; eventMarker_(event); } } bool -GeckoProfiler::enter(JSContext* cx, JSScript* script, JSFunction* maybeFun) +GeckoProfilerThread::enter(JSContext* cx, JSScript* script, JSFunction* maybeFun) { - const char* dynamicString = profileString(script, maybeFun); + const char* dynamicString = cx->runtime()->geckoProfiler().profileString(script, maybeFun); if (dynamicString == nullptr) { ReportOutOfMemory(cx); return false; } #ifdef DEBUG // In debug builds, assert the JS pseudo frames already on the stack // have a non-null pc. Only look at the top frames to avoid quadratic @@ -233,25 +227,26 @@ GeckoProfiler::enter(JSContext* cx, JSSc } #endif pseudoStack_->pushJsFrame("", dynamicString, script, script->code()); return true; } void -GeckoProfiler::exit(JSScript* script, JSFunction* maybeFun) +GeckoProfilerThread::exit(JSScript* script, JSFunction* maybeFun) { pseudoStack_->pop(); #ifdef DEBUG /* Sanity check to make sure push/pop balanced */ uint32_t sp = pseudoStack_->stackPointer; if (sp < PseudoStack::MaxEntries) { - const char* dynamicString = profileString(script, maybeFun); + JSRuntime* rt = script->runtimeFromActiveCooperatingThread(); + const char* dynamicString = rt->geckoProfiler().profileString(script, maybeFun); /* Can't fail lookup because we should already be in the set */ MOZ_ASSERT(dynamicString); // Bug 822041 if (!pseudoStack_->entries[sp].isJs()) { fprintf(stderr, "--- ABOUT TO FAIL ASSERTION ---\n"); fprintf(stderr, " entries=%p size=%u/%u\n", (void*) pseudoStack_->entries, @@ -276,17 +271,17 @@ GeckoProfiler::exit(JSScript* script, JS /* * Serializes the script/function pair into a "descriptive string" which is * allowed to fail. This function cannot trigger a GC because it could finalize * some scripts, resize the hash table of profile strings, and invalidate the * AddPtr held while invoking allocProfileString. */ UniqueChars -GeckoProfiler::allocProfileString(JSScript* script, JSFunction* maybeFun) +GeckoProfilerRuntime::allocProfileString(JSScript* script, JSFunction* maybeFun) { // Note: this profiler string is regexp-matched by // devtools/client/profiler/cleopatra/js/parserWorker.js. // Get the function name, if any. JSAtom* atom = maybeFun ? maybeFun->displayAtom() : nullptr; // Get the script filename, if any, and its length. @@ -324,44 +319,44 @@ GeckoProfiler::allocProfileString(JSScri } MOZ_ASSERT(ret == len, "Computed length should match actual length!"); return cstr; } void -GeckoProfiler::trace(JSTracer* trc) +GeckoProfilerThread::trace(JSTracer* trc) { if (pseudoStack_) { size_t size = pseudoStack_->stackSize(); for (size_t i = 0; i < size; i++) pseudoStack_->entries[i].trace(trc); } } void -GeckoProfiler::fixupStringsMapAfterMovingGC() +GeckoProfilerRuntime::fixupStringsMapAfterMovingGC() { auto locked = strings.lock(); if (!locked->initialized()) return; for (ProfileStringMap::Enum e(locked.get()); !e.empty(); e.popFront()) { JSScript* script = e.front().key(); if (IsForwarded(script)) { script = Forwarded(script); e.rekeyFront(script); } } } #ifdef JSGC_HASH_TABLE_CHECKS void -GeckoProfiler::checkStringsMapAfterMovingGC() +GeckoProfilerRuntime::checkStringsMapAfterMovingGC() { auto locked = strings.lock(); if (!locked->initialized()) return; for (auto r = locked->all(); !r.empty(); r.popFront()) { JSScript* script = r.front().key(); CheckGCThingAfterMovingGC(script); @@ -376,20 +371,20 @@ ProfileEntry::trace(JSTracer* trc) { if (isJs()) { JSScript* s = rawScript(); TraceNullableRoot(trc, &s, "ProfileEntry script"); spOrScript = s; } } -GeckoProfilerEntryMarker::GeckoProfilerEntryMarker(JSRuntime* rt, +GeckoProfilerEntryMarker::GeckoProfilerEntryMarker(JSContext* cx, JSScript* script MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL) - : profiler(&rt->geckoProfiler()) + : profiler(&cx->geckoProfiler()) { MOZ_GUARD_OBJECT_NOTIFIER_INIT; if (!profiler->installed()) { profiler = nullptr; return; } spBefore_ = profiler->stackPointer(); @@ -408,20 +403,20 @@ GeckoProfilerEntryMarker::~GeckoProfiler if (profiler == nullptr) return; profiler->pseudoStack_->pop(); // the JS frame profiler->pseudoStack_->pop(); // the BEGIN_PSEUDO_JS frame MOZ_ASSERT(spBefore_ == profiler->stackPointer()); } -AutoGeckoProfilerEntry::AutoGeckoProfilerEntry(JSRuntime* rt, const char* label, +AutoGeckoProfilerEntry::AutoGeckoProfilerEntry(JSContext* cx, const char* label, ProfileEntry::Category category MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL) - : profiler_(&rt->geckoProfiler()) + : profiler_(&cx->geckoProfiler()) { MOZ_GUARD_OBJECT_NOTIFIER_INIT; if (!profiler_->installed()) { profiler_ = nullptr; return; } spBefore_ = profiler_->stackPointer(); @@ -434,22 +429,22 @@ AutoGeckoProfilerEntry::~AutoGeckoProfil { if (!profiler_) return; profiler_->pseudoStack_->pop(); MOZ_ASSERT(spBefore_ == profiler_->stackPointer()); } -GeckoProfilerBaselineOSRMarker::GeckoProfilerBaselineOSRMarker(JSRuntime* rt, bool hasProfilerFrame +GeckoProfilerBaselineOSRMarker::GeckoProfilerBaselineOSRMarker(JSContext* cx, bool hasProfilerFrame MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL) - : profiler(&rt->geckoProfiler()) + : profiler(&cx->geckoProfiler()) { MOZ_GUARD_OBJECT_NOTIFIER_INIT; - if (!hasProfilerFrame || !profiler->enabled()) { + if (!hasProfilerFrame || !cx->runtime()->geckoProfiler().enabled()) { profiler = nullptr; return; } uint32_t sp = profiler->pseudoStack_->stackPointer; if (sp >= PseudoStack::MaxEntries) { profiler = nullptr; return; @@ -525,24 +520,23 @@ ProfileEntry::setPC(jsbytecode* pc) JSScript* script = this->script(); MOZ_ASSERT(script); // This should not be called while profiling is suppressed. lineOrPcOffset = pcToOffset(script, pc); } JS_FRIEND_API(void) js::SetContextProfilingStack(JSContext* cx, PseudoStack* pseudoStack) { - cx->runtime()->geckoProfiler().setProfilingStack(pseudoStack); + cx->geckoProfiler().setProfilingStack(pseudoStack); } JS_FRIEND_API(void) js::EnableContextProfilingStack(JSContext* cx, bool enabled) { - if (!cx->runtime()->geckoProfiler().enable(enabled)) - MOZ_CRASH("Execution in this runtime should already be single threaded"); + cx->runtime()->geckoProfiler().enable(enabled); } JS_FRIEND_API(void) js::RegisterContextProfilingEventMarker(JSContext* cx, void (*fn)(const char*)) { MOZ_ASSERT(cx->runtime()->geckoProfiler().enabled()); cx->runtime()->geckoProfiler().setEventMarker(fn); }
--- a/js/src/vm/GeckoProfiler.h +++ b/js/src/vm/GeckoProfiler.h @@ -109,155 +109,155 @@ using ProfileStringMap = HashMap<JSScrip UniqueChars, DefaultHasher<JSScript*>, SystemAllocPolicy>; class AutoGeckoProfilerEntry; class GeckoProfilerEntryMarker; class GeckoProfilerBaselineOSRMarker; -class GeckoProfiler +class GeckoProfilerThread { friend class AutoGeckoProfilerEntry; friend class GeckoProfilerEntryMarker; friend class GeckoProfilerBaselineOSRMarker; - JSRuntime* rt; - ExclusiveData<ProfileStringMap> strings; PseudoStack* pseudoStack_; - bool slowAssertions; - uint32_t enabled_; - void (*eventMarker_)(const char*); - - UniqueChars allocProfileString(JSScript* script, JSFunction* function); public: - explicit GeckoProfiler(JSRuntime* rt); - - bool init(); + GeckoProfilerThread(); uint32_t stackPointer() { MOZ_ASSERT(installed()); return pseudoStack_->stackPointer; } ProfileEntry* stack() { return pseudoStack_->entries; } /* management of whether instrumentation is on or off */ - bool enabled() { MOZ_ASSERT_IF(enabled_, installed()); return enabled_; } bool installed() { return pseudoStack_ != nullptr; } - MOZ_MUST_USE bool enable(bool enabled); - void enableSlowAssertions(bool enabled) { slowAssertions = enabled; } - bool slowAssertionsEnabled() { return slowAssertions; } + + void setProfilingStack(PseudoStack* pseudoStack); + void trace(JSTracer* trc); /* * Functions which are the actual instrumentation to track run information * * - enter: a function has started to execute * - updatePC: updates the pc information about where a function * is currently executing * - exit: this function has ceased execution, and no further * entries/exits will be made */ bool enter(JSContext* cx, JSScript* script, JSFunction* maybeFun); void exit(JSScript* script, JSFunction* maybeFun); - void updatePC(JSScript* script, jsbytecode* pc) { - if (!enabled()) - return; + inline void updatePC(JSContext* cx, JSScript* script, jsbytecode* pc); +}; + +class GeckoProfilerRuntime +{ + JSRuntime* rt; + ExclusiveData<ProfileStringMap> strings; + bool slowAssertions; + uint32_t enabled_; + void (*eventMarker_)(const char*); - uint32_t sp = pseudoStack_->stackPointer; - if (sp - 1 < PseudoStack::MaxEntries) { - MOZ_ASSERT(sp > 0); - MOZ_ASSERT(pseudoStack_->entries[sp - 1].rawScript() == script); - pseudoStack_->entries[sp - 1].setPC(pc); - } - } + UniqueChars allocProfileString(JSScript* script, JSFunction* function); + + public: + explicit GeckoProfilerRuntime(JSRuntime* rt); + + bool init(); - void setProfilingStack(PseudoStack* pseudoStack); + /* management of whether instrumentation is on or off */ + bool enabled() { return enabled_; } + void enable(bool enabled); + void enableSlowAssertions(bool enabled) { slowAssertions = enabled; } + bool slowAssertionsEnabled() { return slowAssertions; } + void setEventMarker(void (*fn)(const char*)); const char* profileString(JSScript* script, JSFunction* maybeFun); void onScriptFinalized(JSScript* script); void markEvent(const char* event); /* meant to be used for testing, not recommended to call in normal code */ size_t stringsCount(); void stringsReset(); uint32_t* addressOfEnabled() { return &enabled_; } - void trace(JSTracer* trc); void fixupStringsMapAfterMovingGC(); #ifdef JSGC_HASH_TABLE_CHECKS void checkStringsMapAfterMovingGC(); #endif }; inline size_t -GeckoProfiler::stringsCount() +GeckoProfilerRuntime::stringsCount() { return strings.lock()->count(); } inline void -GeckoProfiler::stringsReset() +GeckoProfilerRuntime::stringsReset() { strings.lock()->clear(); } /* * This class is used in RunScript() to push the marker onto the sampling stack * that we're about to enter JS function calls. This is the only time in which a * valid stack pointer is pushed to the sampling stack. */ class MOZ_RAII GeckoProfilerEntryMarker { public: - explicit GeckoProfilerEntryMarker(JSRuntime* rt, + explicit GeckoProfilerEntryMarker(JSContext* cx, JSScript* script MOZ_GUARD_OBJECT_NOTIFIER_PARAM); ~GeckoProfilerEntryMarker(); private: - GeckoProfiler* profiler; + GeckoProfilerThread* profiler; mozilla::DebugOnly<uint32_t> spBefore_; MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER }; /* * RAII class to automatically add Gecko Profiler pseudo frame entries. * * NB: The `label` string must be statically allocated. */ class MOZ_NONHEAP_CLASS AutoGeckoProfilerEntry { public: - explicit AutoGeckoProfilerEntry(JSRuntime* rt, const char* label, + explicit AutoGeckoProfilerEntry(JSContext* cx, const char* label, ProfileEntry::Category category = ProfileEntry::Category::JS MOZ_GUARD_OBJECT_NOTIFIER_PARAM); ~AutoGeckoProfilerEntry(); private: - GeckoProfiler* profiler_; + GeckoProfilerThread* profiler_; mozilla::DebugOnly<uint32_t> spBefore_; MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER }; /* * This class is used in the interpreter to bound regions where the baseline JIT * being entered via OSR. It marks the current top pseudostack entry as * OSR-ed */ class MOZ_RAII GeckoProfilerBaselineOSRMarker { public: - explicit GeckoProfilerBaselineOSRMarker(JSRuntime* rt, bool hasProfilerFrame + explicit GeckoProfilerBaselineOSRMarker(JSContext* cx, bool hasProfilerFrame MOZ_GUARD_OBJECT_NOTIFIER_PARAM); ~GeckoProfilerBaselineOSRMarker(); private: - GeckoProfiler* profiler; + GeckoProfilerThread* profiler; mozilla::DebugOnly<uint32_t> spBefore_; MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER }; /* * This class manages the instrumentation portion of the profiling for JIT * code. * @@ -267,26 +267,26 @@ class MOZ_RAII GeckoProfilerBaselineOSRM * manages the instrumentation which needs to be attached to them as well. * * The basic methods which emit instrumentation are at the end of this class, * and the management functions are all described in the middle. */ template<class Assembler, class Register> class GeckoProfilerInstrumentation { - GeckoProfiler* profiler_; // Instrumentation location management + GeckoProfilerRuntime* profiler_; // Instrumentation location management public: /* * Creates instrumentation which writes information out the the specified * profiler's stack and constituent fields. */ - explicit GeckoProfilerInstrumentation(GeckoProfiler* profiler) : profiler_(profiler) {} + explicit GeckoProfilerInstrumentation(GeckoProfilerRuntime* profiler) : profiler_(profiler) {} /* Small proxies around GeckoProfiler */ bool enabled() { return profiler_ && profiler_->enabled(); } - GeckoProfiler* profiler() { MOZ_ASSERT(enabled()); return profiler_; } + GeckoProfilerRuntime* profiler() { MOZ_ASSERT(enabled()); return profiler_; } void disable() { profiler_ = nullptr; } }; } /* namespace js */ #endif /* vm_GeckoProfiler_h */
--- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -51,16 +51,17 @@ #include "jsatominlines.h" #include "jsboolinlines.h" #include "jsfuninlines.h" #include "jsscriptinlines.h" #include "jit/JitFrames-inl.h" #include "vm/Debugger-inl.h" #include "vm/EnvironmentObject-inl.h" +#include "vm/GeckoProfiler-inl.h" #include "vm/NativeObject-inl.h" #include "vm/Probes-inl.h" #include "vm/Stack-inl.h" using namespace js; using namespace js::gc; using mozilla::ArrayLength; @@ -371,17 +372,17 @@ js::RunScript(JSContext* cx, RunState& s if (!Debugger::checkNoExecute(cx, state.script())) return false; #if defined(MOZ_HAVE_RDTSC) js::AutoStopwatch stopwatch(cx); #endif // defined(MOZ_HAVE_RDTSC) - GeckoProfilerEntryMarker marker(cx->runtime(), state.script()); + GeckoProfilerEntryMarker marker(cx, state.script()); state.script()->ensureNonLazyCanonicalFunction(); if (jit::IsIonEnabled(cx)) { jit::MethodStatus status = jit::CanEnter(cx, state); if (status == jit::Method_Error) return false; if (status == jit::Method_Compiled) { @@ -2010,31 +2011,31 @@ CASE(JSOP_LOOPENTRY) jit::MethodStatus status = jit::CanEnterBaselineAtBranch(cx, REGS.fp(), false); if (status == jit::Method_Error) goto error; if (status == jit::Method_Compiled) { bool wasProfiler = REGS.fp()->hasPushedGeckoProfilerFrame(); jit::JitExecStatus maybeOsr; { - GeckoProfilerBaselineOSRMarker osr(cx->runtime(), wasProfiler); + GeckoProfilerBaselineOSRMarker osr(cx, wasProfiler); maybeOsr = jit::EnterBaselineAtBranch(cx, REGS.fp(), REGS.pc); } // We failed to call into baseline at all, so treat as an error. if (maybeOsr == jit::JitExec_Aborted) goto error; interpReturnOK = (maybeOsr == jit::JitExec_Ok); // Pop the profiler frame pushed by the interpreter. (The compiled // version of the function popped a copy of the frame pushed by the // OSR trampoline.) if (wasProfiler) - cx->runtime()->geckoProfiler().exit(script, script->functionNonDelazifying()); + cx->geckoProfiler().exit(script, script->functionNonDelazifying()); if (activation.entryFrame() != REGS.fp()) goto jit_return_pop_frame; goto leave_on_safe_point; } } END_CASE(JSOP_LOOPENTRY) @@ -2984,17 +2985,17 @@ CASE(JSOP_STRICTEVAL) TypeScript::Monitor(cx, script, REGS.pc, REGS.sp[-1]); } END_CASE(JSOP_EVAL) CASE(JSOP_SPREADNEW) CASE(JSOP_SPREADCALL) CASE(JSOP_SPREADSUPERCALL) if (REGS.fp()->hasPushedGeckoProfilerFrame()) - cx->runtime()->geckoProfiler().updatePC(script, REGS.pc); + cx->geckoProfiler().updatePC(cx, script, REGS.pc); /* FALL THROUGH */ CASE(JSOP_SPREADEVAL) CASE(JSOP_STRICTSPREADEVAL) { static_assert(JSOP_SPREADEVAL_LENGTH == JSOP_STRICTSPREADEVAL_LENGTH, "spreadeval and strictspreadeval must be the same size"); bool construct = JSOp(*REGS.pc) == JSOP_SPREADNEW || JSOp(*REGS.pc) == JSOP_SPREADSUPERCALL;; @@ -3030,17 +3031,17 @@ CASE(JSOP_FUNAPPLY) CASE(JSOP_NEW) CASE(JSOP_CALL) CASE(JSOP_CALL_IGNORES_RV) CASE(JSOP_CALLITER) CASE(JSOP_SUPERCALL) CASE(JSOP_FUNCALL) { if (REGS.fp()->hasPushedGeckoProfilerFrame()) - cx->runtime()->geckoProfiler().updatePC(script, REGS.pc); + cx->geckoProfiler().updatePC(cx, script, REGS.pc); MaybeConstruct construct = MaybeConstruct(*REGS.pc == JSOP_NEW || *REGS.pc == JSOP_SUPERCALL); bool ignoresReturnValue = *REGS.pc == JSOP_CALL_IGNORES_RV; unsigned argStackSlots = GET_ARGC(REGS.pc) + construct; MOZ_ASSERT(REGS.stackDepth() >= 2u + GET_ARGC(REGS.pc)); CallArgs args = CallArgsFromSp(argStackSlots, REGS.sp, construct, ignoresReturnValue);
--- a/js/src/vm/Probes-inl.h +++ b/js/src/vm/Probes-inl.h @@ -36,17 +36,17 @@ probes::EnterScript(JSContext* cx, JSScr { #ifdef INCLUDE_MOZILLA_DTRACE if (JAVASCRIPT_FUNCTION_ENTRY_ENABLED()) DTraceEnterJSFun(cx, maybeFun, script); #endif JSRuntime* rt = cx->runtime(); if (rt->geckoProfiler().enabled()) { - if (!rt->geckoProfiler().enter(cx, script, maybeFun)) + if (!cx->geckoProfiler().enter(cx, script, maybeFun)) return false; MOZ_ASSERT_IF(!fp->script()->isStarGenerator() && !fp->script()->isLegacyGenerator() && !fp->script()->isAsync(), !fp->hasPushedGeckoProfilerFrame()); fp->setPushedGeckoProfilerFrame(); } @@ -57,17 +57,17 @@ inline void probes::ExitScript(JSContext* cx, JSScript* script, JSFunction* maybeFun, bool popProfilerFrame) { #ifdef INCLUDE_MOZILLA_DTRACE if (JAVASCRIPT_FUNCTION_RETURN_ENABLED()) DTraceExitJSFun(cx, maybeFun, script); #endif if (popProfilerFrame) - cx->runtime()->geckoProfiler().exit(script, maybeFun); + cx->geckoProfiler().exit(script, maybeFun); } inline bool probes::StartExecution(JSScript* script) { bool ok = true; #ifdef INCLUDE_MOZILLA_DTRACE
--- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -312,18 +312,16 @@ JSRuntime::destroyRuntime() profilerSampleBufferGen_ = UINT32_MAX; JS::PrepareForFullGC(cx); gc.gc(GC_NORMAL, JS::gcreason::DESTROY_RUNTIME); } AutoNoteSingleThreadedRegion anstr; - MOZ_ASSERT_IF(!geckoProfiler().enabled(), !singleThreadedExecutionRequired_); - MOZ_ASSERT(!hasHelperThreadZones()); AutoLockForExclusiveAccess lock(this); /* * Even though all objects in the compartment are dead, we may have keep * some filenames around because of gcKeepAtoms. */ FreeScriptData(this, lock);
--- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -504,19 +504,19 @@ struct JSRuntime : public js::MallocProv js::ActiveThreadData<JSDestroyPrincipalsOp> destroyPrincipals; js::ActiveThreadData<JSReadPrincipalsOp> readPrincipals; /* Optional warning reporter. */ js::ActiveThreadData<JS::WarningReporter> warningReporter; private: /* Gecko profiling metadata */ - js::UnprotectedData<js::GeckoProfiler> geckoProfiler_; + js::UnprotectedData<js::GeckoProfilerRuntime> geckoProfiler_; public: - js::GeckoProfiler& geckoProfiler() { return geckoProfiler_.ref(); } + js::GeckoProfilerRuntime& geckoProfiler() { return geckoProfiler_.ref(); } // Heap GC roots for PersistentRooted pointers. js::ActiveThreadData<mozilla::EnumeratedArray<JS::RootKind, JS::RootKind::Limit, mozilla::LinkedList<JS::PersistentRooted<void*>>>> heapRoots; void tracePersistentRoots(JSTracer* trc); void finishPersistentRoots();
--- a/js/src/vm/SavedStacks.cpp +++ b/js/src/vm/SavedStacks.cpp @@ -1168,17 +1168,17 @@ SavedStacks::saveCurrentStack(JSContext* cx->isExceptionPending() || !cx->global() || !cx->global()->isStandardClassResolved(JSProto_Object)) { frame.set(nullptr); return true; } - AutoGeckoProfilerEntry psuedoFrame(cx->runtime(), "js::SavedStacks::saveCurrentStack"); + AutoGeckoProfilerEntry psuedoFrame(cx, "js::SavedStacks::saveCurrentStack"); FrameIter iter(cx); return insertFrames(cx, iter, frame, mozilla::Move(capture)); } bool SavedStacks::copyAsyncStack(JSContext* cx, HandleObject asyncStack, HandleString asyncCause, MutableHandleSavedFrame adoptedStack, uint32_t maxFrameCount) {
--- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -592,30 +592,55 @@ FrameIter::Data::Data(JSContext* cx, Deb interpFrames_(nullptr), activations_(cx), jitFrames_(), ionInlineFrameNo_(0), wasmFrames_() { } +FrameIter::Data::Data(JSContext* cx, const CooperatingContext& target, + DebuggerEvalOption debuggerEvalOption) + : cx_(cx), + debuggerEvalOption_(debuggerEvalOption), + principals_(nullptr), + state_(DONE), + pc_(nullptr), + interpFrames_(nullptr), + activations_(cx, target), + jitFrames_(), + ionInlineFrameNo_(0), + wasmFrames_() +{ +} + FrameIter::Data::Data(const FrameIter::Data& other) : cx_(other.cx_), debuggerEvalOption_(other.debuggerEvalOption_), principals_(other.principals_), state_(other.state_), pc_(other.pc_), interpFrames_(other.interpFrames_), activations_(other.activations_), jitFrames_(other.jitFrames_), ionInlineFrameNo_(other.ionInlineFrameNo_), wasmFrames_(other.wasmFrames_) { } +FrameIter::FrameIter(JSContext* cx, const CooperatingContext& target, + DebuggerEvalOption debuggerEvalOption) + : data_(cx, target, debuggerEvalOption), + ionInlineFrames_(cx, (js::jit::JitFrameIterator*) nullptr) +{ + // settleOnActivation can only GC if principals are given. + JS::AutoSuppressGCAnalysis nogc; + settleOnActivation(); +} + FrameIter::FrameIter(JSContext* cx, DebuggerEvalOption debuggerEvalOption) : data_(cx, debuggerEvalOption, nullptr), ionInlineFrames_(cx, (js::jit::JitFrameIterator*) nullptr) { // settleOnActivation can only GC if principals are given. JS::AutoSuppressGCAnalysis nogc; settleOnActivation(); }
--- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -47,17 +47,17 @@ class JS_PUBLIC_API(AutoEntryMonitor); namespace js { class InterpreterRegs; class CallObject; class FrameIter; class EnvironmentObject; class ScriptFrameIter; -class GeckoProfiler; +class GeckoProfilerRuntime; class InterpreterFrame; class LexicalEnvironmentObject; class EnvironmentIter; class EnvironmentCoordinate; class SavedFrame; namespace jit { @@ -1810,21 +1810,23 @@ class FrameIter InterpreterFrameIterator interpFrames_; ActivationIterator activations_; jit::JitFrameIterator jitFrames_; unsigned ionInlineFrameNo_; wasm::FrameIterator wasmFrames_; Data(JSContext* cx, DebuggerEvalOption debuggerEvalOption, JSPrincipals* principals); + Data(JSContext* cx, const CooperatingContext& target, DebuggerEvalOption debuggerEvalOption); Data(const Data& other); }; explicit FrameIter(JSContext* cx, DebuggerEvalOption = FOLLOW_DEBUGGER_EVAL_PREV_LINK); + FrameIter(JSContext* cx, const CooperatingContext&, DebuggerEvalOption); FrameIter(JSContext* cx, DebuggerEvalOption, JSPrincipals*); FrameIter(const FrameIter& iter); MOZ_IMPLICIT FrameIter(const Data& data); MOZ_IMPLICIT FrameIter(AbstractFramePtr frame); bool done() const { return data_.state_ == DONE; } // ------------------------------------------------------- @@ -1970,16 +1972,24 @@ class ScriptFrameIter : public FrameIter explicit ScriptFrameIter(JSContext* cx, DebuggerEvalOption debuggerEvalOption = FOLLOW_DEBUGGER_EVAL_PREV_LINK) : FrameIter(cx, debuggerEvalOption) { settle(); } ScriptFrameIter(JSContext* cx, + const CooperatingContext& target, + DebuggerEvalOption debuggerEvalOption) + : FrameIter(cx, target, debuggerEvalOption) + { + settle(); + } + + ScriptFrameIter(JSContext* cx, DebuggerEvalOption debuggerEvalOption, JSPrincipals* prin) : FrameIter(cx, debuggerEvalOption, prin) { settle(); } ScriptFrameIter(const ScriptFrameIter& iter) : FrameIter(iter) { settle(); } @@ -2091,16 +2101,20 @@ class AllFramesIter : public FrameIter * See also AllFramesIter and ScriptFrameIter. */ class AllScriptFramesIter : public ScriptFrameIter { public: explicit AllScriptFramesIter(JSContext* cx) : ScriptFrameIter(cx, ScriptFrameIter::IGNORE_DEBUGGER_EVAL_PREV_LINK) {} + + explicit AllScriptFramesIter(JSContext* cx, const CooperatingContext& target) + : ScriptFrameIter(cx, target, ScriptFrameIter::IGNORE_DEBUGGER_EVAL_PREV_LINK) + {} }; /* Popular inline definitions. */ inline JSScript* FrameIter::script() const { MOZ_ASSERT(!done());
--- a/js/src/vm/String.cpp +++ b/js/src/vm/String.cpp @@ -580,17 +580,17 @@ JSRope::flattenInternal(JSContext* maybe return flattenInternal<b, Latin1Char>(maybecx); } JSFlatString* JSRope::flatten(JSContext* maybecx) { mozilla::Maybe<AutoGeckoProfilerEntry> entry; if (maybecx && !maybecx->helperThread()) - entry.emplace(maybecx->runtime(), "JSRope::flatten"); + entry.emplace(maybecx, "JSRope::flatten"); if (zone()->needsIncrementalBarrier()) return flattenInternal<WithIncrementalBarrier>(maybecx); return flattenInternal<NoBarrier>(maybecx); } template <AllowGC allowGC> static JSLinearString*
--- a/js/src/vm/TypeInference.cpp +++ b/js/src/vm/TypeInference.cpp @@ -2106,17 +2106,17 @@ class ConstraintDataConstantProperty JSCompartment* maybeCompartment() { return nullptr; } }; } /* anonymous namespace */ bool HeapTypeSetKey::constant(CompilerConstraintList* constraints, Value* valOut) { - if (nonData(constraints) || !object()->isSingleton()) + if (nonData(constraints)) return false; // Only singleton object properties can be marked as constants. JSObject* obj = object()->singleton(); if (!obj || !obj->isNative()) return false; if (maybeTypes() && maybeTypes()->nonConstantProperty()) @@ -3994,99 +3994,102 @@ TypeNewScript::rollbackPartiallyInitiali if (!initializerList) return false; bool found = false; RootedFunction function(cx, this->function()); Vector<uint32_t, 32> pcOffsets(cx); - for (ScriptFrameIter iter(cx); !iter.done(); ++iter) { - { - AutoEnterOOMUnsafeRegion oomUnsafe; - if (!pcOffsets.append(iter.script()->pcToOffset(iter.pc()))) - oomUnsafe.crash("rollbackPartiallyInitializedObjects"); - } - - if (!iter.isConstructing() || !iter.matchCallee(cx, function)) - continue; - - // Derived class constructors initialize their this-binding later and - // we shouldn't run the definite properties analysis on them. - MOZ_ASSERT(!iter.script()->isDerivedClassConstructor()); - - Value thisv = iter.thisArgument(cx); - if (!thisv.isObject() || - thisv.toObject().hasLazyGroup() || - thisv.toObject().group() != group) - { - continue; - } - - if (thisv.toObject().is<UnboxedPlainObject>()) { - AutoEnterOOMUnsafeRegion oomUnsafe; - if (!UnboxedPlainObject::convertToNative(cx, &thisv.toObject())) - oomUnsafe.crash("rollbackPartiallyInitializedObjects"); - } - - // Found a matching frame. - RootedPlainObject obj(cx, &thisv.toObject().as<PlainObject>()); - - // Whether all identified 'new' properties have been initialized. - bool finished = false; - - // If not finished, number of properties that have been added. - uint32_t numProperties = 0; - - // Whether the current SETPROP is within an inner frame which has - // finished entirely. - bool pastProperty = false; - - // Index in pcOffsets of the outermost frame. - int callDepth = pcOffsets.length() - 1; - - // Index in pcOffsets of the frame currently being checked for a SETPROP. - int setpropDepth = callDepth; - - for (Initializer* init = initializerList;; init++) { - if (init->kind == Initializer::SETPROP) { - if (!pastProperty && pcOffsets[setpropDepth] < init->offset) { - // Have not yet reached this setprop. + JSRuntime::AutoProhibitActiveContextChange apacc(cx->runtime()); + for (const CooperatingContext& target : cx->runtime()->cooperatingContexts()) { + for (AllScriptFramesIter iter(cx, target); !iter.done(); ++iter) { + { + AutoEnterOOMUnsafeRegion oomUnsafe; + if (!pcOffsets.append(iter.script()->pcToOffset(iter.pc()))) + oomUnsafe.crash("rollbackPartiallyInitializedObjects"); + } + + if (!iter.isConstructing() || !iter.matchCallee(cx, function)) + continue; + + // Derived class constructors initialize their this-binding later and + // we shouldn't run the definite properties analysis on them. + MOZ_ASSERT(!iter.script()->isDerivedClassConstructor()); + + Value thisv = iter.thisArgument(cx); + if (!thisv.isObject() || + thisv.toObject().hasLazyGroup() || + thisv.toObject().group() != group) + { + continue; + } + + if (thisv.toObject().is<UnboxedPlainObject>()) { + AutoEnterOOMUnsafeRegion oomUnsafe; + if (!UnboxedPlainObject::convertToNative(cx, &thisv.toObject())) + oomUnsafe.crash("rollbackPartiallyInitializedObjects"); + } + + // Found a matching frame. + RootedPlainObject obj(cx, &thisv.toObject().as<PlainObject>()); + + // Whether all identified 'new' properties have been initialized. + bool finished = false; + + // If not finished, number of properties that have been added. + uint32_t numProperties = 0; + + // Whether the current SETPROP is within an inner frame which has + // finished entirely. + bool pastProperty = false; + + // Index in pcOffsets of the outermost frame. + int callDepth = pcOffsets.length() - 1; + + // Index in pcOffsets of the frame currently being checked for a SETPROP. + int setpropDepth = callDepth; + + for (Initializer* init = initializerList;; init++) { + if (init->kind == Initializer::SETPROP) { + if (!pastProperty && pcOffsets[setpropDepth] < init->offset) { + // Have not yet reached this setprop. + break; + } + // This setprop has executed, reset state for the next one. + numProperties++; + pastProperty = false; + setpropDepth = callDepth; + } else if (init->kind == Initializer::SETPROP_FRAME) { + if (!pastProperty) { + if (pcOffsets[setpropDepth] < init->offset) { + // Have not yet reached this inner call. + break; + } else if (pcOffsets[setpropDepth] > init->offset) { + // Have advanced past this inner call. + pastProperty = true; + } else if (setpropDepth == 0) { + // Have reached this call but not yet in it. + break; + } else { + // Somewhere inside this inner call. + setpropDepth--; + } + } + } else { + MOZ_ASSERT(init->kind == Initializer::DONE); + finished = true; break; } - // This setprop has executed, reset state for the next one. - numProperties++; - pastProperty = false; - setpropDepth = callDepth; - } else if (init->kind == Initializer::SETPROP_FRAME) { - if (!pastProperty) { - if (pcOffsets[setpropDepth] < init->offset) { - // Have not yet reached this inner call. - break; - } else if (pcOffsets[setpropDepth] > init->offset) { - // Have advanced past this inner call. - pastProperty = true; - } else if (setpropDepth == 0) { - // Have reached this call but not yet in it. - break; - } else { - // Somewhere inside this inner call. - setpropDepth--; - } - } - } else { - MOZ_ASSERT(init->kind == Initializer::DONE); - finished = true; - break; } - } - - if (!finished) { - (void) NativeObject::rollbackProperties(cx, obj, numProperties); - found = true; + + if (!finished) { + (void) NativeObject::rollbackProperties(cx, obj, numProperties); + found = true; + } } } return found; } void TypeNewScript::trace(JSTracer* trc)
--- a/js/xpconnect/loader/ScriptPreloader.cpp +++ b/js/xpconnect/loader/ScriptPreloader.cpp @@ -261,35 +261,51 @@ ScriptPreloader::Cleanup() // the main thread, or we will deadlock. MOZ_RELEASE_ASSERT(!mBlockedOnSyncDispatch); while (!mSaveComplete && mSaveThread) { mal.Wait(); } } - mScripts.Clear(); + // Wait for any pending parses to finish before clearing the mScripts + // hashtable, since the parse tasks depend on memory allocated by those + // scripts. + { + MonitorAutoLock mal(mMonitor); + FinishPendingParses(mal); + + mScripts.Clear(); + } AutoSafeJSAPI jsapi; JS_RemoveExtraGCRootsTracer(jsapi.cx(), TraceOp, this); UnregisterWeakMemoryReporter(this); } void ScriptPreloader::InvalidateCache() { mMonitor.AssertNotCurrentThreadOwns(); MonitorAutoLock mal(mMonitor); mCacheInvalidated = true; - mParsingScripts.clearAndFree(); - while (auto script = mPendingScripts.getFirst()) - script->remove(); + // Wait for pending off-thread parses to finish, since they depend on the + // memory allocated by our CachedScripts, and can't be canceled + // asynchronously. + FinishPendingParses(mal); + + // Pending scripts should have been cleared by the above, and new parses + // should not have been queued. + MOZ_ASSERT(mParsingScripts.empty()); + MOZ_ASSERT(mParsingSources.empty()); + MOZ_ASSERT(mPendingScripts.isEmpty()); + for (auto& script : IterHash(mScripts)) script.Remove(); // If we've already finished saving the cache at this point, start a new // delayed save operation. This will write out an empty cache file in place // of any cache file we've already written out this session, which will // prevent us from falling back to the current session's cache file on the // next startup. @@ -543,18 +559,16 @@ ScriptPreloader::PrepareCacheWriteIntern if (!(script->mProcessTypes == script->mOriginalProcessTypes)) { // Note: EnumSet doesn't support operator!=, hence the weird form above. found = true; } if (!script->mSize && !script->XDREncode(jsapi.cx())) { script.Remove(); - } else { - script->mSize = script->Range().length(); } } if (!found) { mSaveComplete = true; return; } @@ -617,16 +631,21 @@ ScriptPreloader::WriteCache() if (exists) { NS_TRY(cacheFile->Remove(false)); } { AutoFDClose fd; NS_TRY(cacheFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE, 0644, &fd.rwget())); + // We also need to hold mMonitor while we're touching scripts in + // mScripts, or they may be freed before we're done with them. + mMonitor.AssertNotCurrentThreadOwns(); + MonitorAutoLock mal(mMonitor); + nsTArray<CachedScript*> scripts; for (auto& script : IterHash(mScripts, Match<ScriptStatus::Saved>())) { scripts.AppendElement(script); } // Sort scripts by load time, with async loaded scripts before sync scripts. // Since async scripts are always loaded immediately at startup, it helps to // have them stored contiguously. @@ -691,17 +710,17 @@ ScriptPreloader::Run() } void ScriptPreloader::NoteScript(const nsCString& url, const nsCString& cachePath, JS::HandleScript jsscript) { // Don't bother trying to cache any URLs with cache-busting query // parameters. - if (mStartupFinished || !mCacheInitialized || cachePath.FindChar('?') >= 0) { + if (!Active() || cachePath.FindChar('?') >= 0) { return; } // Don't bother caching files that belong to the mochitest harness. NS_NAMED_LITERAL_CSTRING(mochikitPrefix, "chrome://mochikit/"); if (StringHead(url, mochikitPrefix.Length()) == mochikitPrefix) { return; } @@ -709,16 +728,31 @@ ScriptPreloader::NoteScript(const nsCStr auto script = mScripts.LookupOrAdd(cachePath, *this, url, cachePath, jsscript); if (!script->mScript) { MOZ_ASSERT(jsscript); script->mScript = jsscript; script->mReadyToExecute = true; } + // If we don't already have bytecode for this script, and it doesn't already + // exist in the child cache, encode it now, before it's ever executed. + // + // Ideally, we would like to do the encoding lazily, during idle slices. + // There are subtle issues with encoding scripts which have already been + // executed, though, which makes that somewhat risky. So until that + // situation is improved, and thoroughly tested, we need to encode eagerly. + // + // (See also the TranscodeResult_Failure_RunOnceNotSupported failure case in + // js::XDRScript) + if (!script->mSize && !(mChildCache && mChildCache->mScripts.Get(cachePath))) { + AutoSafeJSAPI jsapi; + Unused << script->XDREncode(jsapi.cx()); + } + script->UpdateLoadTime(TimeStamp::Now()); script->mProcessTypes += CurrentProcessType(); } void ScriptPreloader::NoteScript(const nsCString& url, const nsCString& cachePath, ProcessType processType, nsTArray<uint8_t>&& xdrData, TimeStamp loadTime) @@ -777,40 +811,40 @@ ScriptPreloader::GetCachedScript(JSConte JSScript* ScriptPreloader::WaitForCachedScript(JSContext* cx, CachedScript* script) { // Check for finished operations before locking so that we can move onto // decoding the next batch as soon as possible after the pending batch is // ready. If we wait until we hit an unfinished script, we wind up having at // most one batch of buffered scripts, and occasionally under-running that // buffer. - FinishOffThreadDecode(); + MaybeFinishOffThreadDecode(); if (!script->mReadyToExecute) { LOG(Info, "Must wait for async script load: %s\n", script->mURL.get()); auto start = TimeStamp::Now(); mMonitor.AssertNotCurrentThreadOwns(); MonitorAutoLock mal(mMonitor); // Check for finished operations again *after* locking, or we may race // against mToken being set between our last check and the time we // entered the mutex. - FinishOffThreadDecode(); + MaybeFinishOffThreadDecode(); if (!script->mReadyToExecute && script->mSize < MAX_MAINTHREAD_DECODE_SIZE) { LOG(Info, "Script is small enough to recompile on main thread\n"); script->mReadyToExecute = true; } else { while (!script->mReadyToExecute) { mal.Wait(); MonitorAutoUnlock mau(mMonitor); - FinishOffThreadDecode(); + MaybeFinishOffThreadDecode(); } } LOG(Debug, "Waited %fms\n", (TimeStamp::Now() - start).ToMilliseconds()); } return script->GetJSScript(cx); } @@ -838,24 +872,40 @@ ScriptPreloader::OffThreadDecodeCallback NS_DispatchToMainThread( NewRunnableMethod("ScriptPreloader::DoFinishOffThreadDecode", cache, &ScriptPreloader::DoFinishOffThreadDecode)); } } void +ScriptPreloader::FinishPendingParses(MonitorAutoLock& aMal) +{ + mMonitor.AssertCurrentThreadOwns(); + + mPendingScripts.clear(); + + MaybeFinishOffThreadDecode(); + + // Loop until all pending decode operations finish. + while (!mParsingScripts.empty()) { + aMal.Wait(); + MaybeFinishOffThreadDecode(); + } +} + +void ScriptPreloader::DoFinishOffThreadDecode() { mFinishDecodeRunnablePending = false; - FinishOffThreadDecode(); + MaybeFinishOffThreadDecode(); } void -ScriptPreloader::FinishOffThreadDecode() +ScriptPreloader::MaybeFinishOffThreadDecode() { if (!mToken) { return; } auto cleanup = MakeScopeExit([&] () { mToken = nullptr; mParsingSources.clear(); @@ -935,17 +985,18 @@ ScriptPreloader::DecodeNextBatch(size_t return; } AutoJSAPI jsapi; MOZ_RELEASE_ASSERT(jsapi.Init(xpc::CompilationScope())); JSContext* cx = jsapi.cx(); JS::CompileOptions options(cx, JSVERSION_LATEST); - options.setNoScriptRval(true); + options.setNoScriptRval(true) + .setSourceIsLazy(true); if (!JS::CanCompileOffThread(cx, options, size) || !JS::DecodeMultiOffThreadScripts(cx, options, mParsingSources, OffThreadDecodeCallback, static_cast<void*>(this))) { // If we fail here, we don't move on to process the next batch, so make // sure we don't have any other scripts left to process. MOZ_ASSERT(mPendingScripts.isEmpty()); @@ -985,18 +1036,20 @@ ScriptPreloader::CachedScript::XDREncode JSAutoCompartment ac(cx, mScript); JS::RootedScript jsscript(cx, mScript); mXDRData.construct<JS::TranscodeBuffer>(); JS::TranscodeResult code = JS::EncodeScript(cx, Buffer(), jsscript); if (code == JS::TranscodeResult_Ok) { mXDRRange.emplace(Buffer().begin(), Buffer().length()); + mSize = Range().length(); return true; } + mXDRData.destroy(); JS_ClearPendingException(cx); return false; } JSScript* ScriptPreloader::CachedScript::GetJSScript(JSContext* cx) { MOZ_ASSERT(mReadyToExecute);
--- a/js/xpconnect/loader/ScriptPreloader.h +++ b/js/xpconnect/loader/ScriptPreloader.h @@ -86,16 +86,21 @@ public: ProcessType processType, nsTArray<uint8_t>&& xdrData, TimeStamp loadTime); // Initializes the script cache from the startup script cache file. Result<Ok, nsresult> InitCache(const nsAString& = NS_LITERAL_STRING("scriptCache")); Result<Ok, nsresult> InitCache(const Maybe<ipc::FileDescriptor>& cacheFile, ScriptCacheChild* cacheChild); + bool Active() + { + return mCacheInitialized && !mStartupFinished; + } + private: Result<Ok, nsresult> InitCacheInternal(); public: void Trace(JSTracer* trc); static ProcessType CurrentProcessType() { @@ -350,16 +355,17 @@ private: // thread for quite as long. static constexpr int MAX_MAINTHREAD_DECODE_SIZE = 50 * 1024; ScriptPreloader(); void ForceWriteCacheFile(); void Cleanup(); + void FinishPendingParses(MonitorAutoLock& aMal); void InvalidateCache(); // Opens the cache file for reading. Result<Ok, nsresult> OpenCache(); // Writes a new cache file to disk. Must not be called on the main thread. Result<Ok, nsresult> WriteCache(); @@ -376,17 +382,17 @@ private: // Waits for the given cached script to finish compiling off-thread, or // decodes it synchronously on the main thread, as appropriate. JSScript* WaitForCachedScript(JSContext* cx, CachedScript* script); void DecodeNextBatch(size_t chunkSize); static void OffThreadDecodeCallback(void* token, void* context); - void FinishOffThreadDecode(); + void MaybeFinishOffThreadDecode(); void DoFinishOffThreadDecode(); size_t ShallowHeapSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) { return (mallocSizeOf(this) + mScripts.ShallowSizeOfExcludingThis(mallocSizeOf) + mallocSizeOf(mSaveThread.get()) + mallocSizeOf(mProfD.get())); }
--- a/js/xpconnect/loader/mozJSComponentLoader.cpp +++ b/js/xpconnect/loader/mozJSComponentLoader.cpp @@ -643,17 +643,17 @@ mozJSComponentLoader::ObjectForLocation( // XDR encoded modules in omni.ja). Also, XDR decoding is relatively // fast. When we're not using the startup cache, we want to use non-lazy // source code so that we can use lazy parsing. // See bug 1303754. CompileOptions options(cx); options.setNoScriptRval(true) .setVersion(JSVERSION_LATEST) .setFileAndLine(nativePath.get(), 1) - .setSourceIsLazy(!!cache); + .setSourceIsLazy(cache || ScriptPreloader::GetSingleton().Active()); if (realFile) { AutoMemMap map; MOZ_TRY(map.init(aComponentFile)); // Note: exceptions will get handled further down; // don't early return for them here. auto buf = map.get<char>();
--- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -135,17 +135,17 @@ public: mActive = false; } return NS_OK; } nsresult Dispatch() { nsCOMPtr<nsIRunnable> self(this); - return NS_IdleDispatchToCurrentThread(self.forget(), 1000); + return NS_IdleDispatchToCurrentThread(self.forget(), 2500); } void Start(bool aContinuation = false, bool aPurge = false) { if (mContinuation) { mContinuation = aContinuation; } mPurge = aPurge;
--- a/js/xpconnect/src/XPCThrower.cpp +++ b/js/xpconnect/src/XPCThrower.cpp @@ -80,17 +80,17 @@ XPCThrower::Throw(nsresult rv, XPCCallCo NS_ENSURE_TRUE_VOID(sz); if (sz && sVerbose) Verbosify(ccx, &sz, false); dom::Throw(ccx, rv, nsDependentCString(sz)); if (sz && sz != format) - JS_smprintf_free(sz); + js_free(sz); } // static void XPCThrower::ThrowBadResult(nsresult rv, nsresult result, XPCCallContext& ccx) { char* sz; @@ -122,17 +122,17 @@ XPCThrower::ThrowBadResult(nsresult rv, NS_ENSURE_TRUE_VOID(sz); if (sz && sVerbose) Verbosify(ccx, &sz, true); dom::Throw(ccx, result, nsDependentCString(sz)); if (sz) - JS_smprintf_free(sz); + js_free(sz); } // static void XPCThrower::ThrowBadParam(nsresult rv, unsigned paramNum, XPCCallContext& ccx) { char* sz; const char* format; @@ -144,17 +144,17 @@ XPCThrower::ThrowBadParam(nsresult rv, u NS_ENSURE_TRUE_VOID(sz); if (sz && sVerbose) Verbosify(ccx, &sz, true); dom::Throw(ccx, rv, nsDependentCString(sz)); if (sz) - JS_smprintf_free(sz); + js_free(sz); } // static void XPCThrower::Verbosify(XPCCallContext& ccx, char** psz, bool own) { @@ -168,12 +168,12 @@ XPCThrower::Verbosify(XPCCallContext& cc if (!name) { name = ""; } sz = JS_smprintf("%s [%s.%s]", *psz, iface->GetNameString(), name).release(); } if (sz) { if (own) - JS_smprintf_free(*psz); + js_free(*psz); *psz = sz; } }
--- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -1725,17 +1725,18 @@ public: static void Trace(JSTracer* trc, JSObject* obj); void AutoTrace(JSTracer* trc) { TraceSelf(trc); } inline void SweepTearOffs(); - // Returns a string that shuld be free'd using JS_smprintf_free (or null). + // Returns a string that should be freed with js_free, or nullptr on + // failure. char* ToString(XPCWrappedNativeTearOff* to = nullptr) const; static nsIXPCScriptable* GatherProtoScriptable(nsIClassInfo* classInfo); bool HasExternalReference() const {return mRefCnt > 1;} void Suspect(nsCycleCollectionNoteRootCallback& cb); void NoteTearoffs(nsCycleCollectionTraversalCallback& cb);
--- a/js/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/xpconnect/wrappers/XrayWrapper.cpp @@ -2048,17 +2048,17 @@ XrayToString(JSContext* cx, unsigned arg XPCCallContext ccx(cx, obj); XPCWrappedNative* wn = XPCWrappedNativeXrayTraits::getWN(wrapper); char* wrapperStr = wn->ToString(); if (!wrapperStr) { JS_ReportOutOfMemory(cx); return false; } result.AppendASCII(wrapperStr); - JS_smprintf_free(wrapperStr); + js_free(wrapperStr); result.AppendASCII(end); JSString* str = JS_NewUCStringCopyN(cx, result.get(), result.Length()); if (!str) return false; args.rval().setString(str);
--- a/layout/painting/nsCSSRendering.cpp +++ b/layout/painting/nsCSSRendering.cpp @@ -2199,19 +2199,25 @@ nsCSSRendering::GetImageLayerClip(const aForFrame->IsSVGOuterSVGFrame()); // Compute the outermost boundary of the area that might be painted. // Same coordinate space as aBorderArea. Sides skipSides = aForFrame->GetSkipSides(); nsRect clipBorderArea = ::BoxDecorationRectForBorder(aForFrame, aBorderArea, skipSides, &aBorder); - bool haveRoundedCorners = GetRadii(aForFrame, aBorder, aBorderArea, - clipBorderArea, aClipState->mRadii); - + bool haveRoundedCorners = false; + LayoutFrameType fType = aForFrame->Type(); + if (fType != LayoutFrameType::TableColGroup && + fType != LayoutFrameType::TableCol && + fType != LayoutFrameType::TableRow && + fType != LayoutFrameType::TableRowGroup) { + haveRoundedCorners = GetRadii(aForFrame, aBorder, aBorderArea, + clipBorderArea, aClipState->mRadii); + } bool isSolidBorder = aWillPaintBorder && IsOpaqueBorder(aBorder); if (isSolidBorder && layerClip == StyleGeometryBox::BorderBox) { // If we have rounded corners, we need to inflate the background // drawing area a bit to avoid seams between the border and // background. layerClip = haveRoundedCorners ? StyleGeometryBox::MozAlmostPadding
--- a/layout/reftests/bugs/212563-2-inner.html +++ b/layout/reftests/bugs/212563-2-inner.html @@ -1,9 +1,9 @@ <html> <head> </head> <frameset resizable="yes" rows="100%" onload="parent.changeInnermost(document.getElementById("innermost"))"> - <frame id="innermost" src="data:text/html,<font color=red>old innermost"></frame> + <frame id="innermost" src="212563-2-innermost-b.html"></frame> </frameset> </html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bugs/212563-2-innermost-a.html @@ -0,0 +1,13 @@ +<body onload="w(parent.document, top);"> +<font color=blue>new innermost +<script type="text/javascript"> +function w(md, t) +{ + t.p(0); + md.write(t.replacementForMiddleFrame); + t.p(3); + t.p(md.documentElement.innerHTML); + md.close(); + t.p(4); +} +</script>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bugs/212563-2-innermost-b.html @@ -0,0 +1,1 @@ +<font color=red>old innermost
--- a/layout/reftests/bugs/212563-2.html +++ b/layout/reftests/bugs/212563-2.html @@ -5,32 +5,20 @@ function p(n) { dump("Test 212563-2 says: " + n + "\n"); } // Step 1: replace the innermost frame function changeInnermost(iframeElement) { - iframeElement.setAttribute("src", - "data:text/html,<body onload='" + w + "w(parent.document, top);'><font color=blue>new innermost"); + iframeElement.setAttribute("src", "212563-2-innermost-a.html"); } // Step 2: replace the middle iframe (from the new innermost iframe's onload handler) var replacementForMiddleFrame = "<body onload=top.p(5);parent.document.documentElement.removeAttribute('class');>replacement for middle frame<script>top.p(2);<\/script><\/body>"; -function w(md, t) -{ - t.p(0); - md.write(t.replacementForMiddleFrame); - t.p(3); - t.p(md.documentElement.innerHTML); - md.close(); - t.p(4); -} - - </script> </head> <body> <iframe src="212563-2-inner.html"></iframe> </body> </html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/table-bordercollapse/bug1375518-2.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<html> +<head> +<title>Table border collapse</title> +<style> + div > span { + display: table-cell; + background-color: black; + height: 100px; + width: 100px; + border-radius: 50px; + } + div { + display: table; + border-collapse: collapse; + } +</style> +</head> +<body> + <div><span></span></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/table-bordercollapse/bug1375518-3.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<html> +<head> +<title>Separated border model table</title> +<style> + div > span { + display: table-cell; + background-color: black; + height: 100px; + width: 100px; + border-radius: 50px; + } + div { + display: table; + border-collapse: separate; + } +</style> +</head> +<body> + <div><span></span></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/table-bordercollapse/bug1375518-4-ref.html @@ -0,0 +1,44 @@ +<!DOCTYPE html> +<title>border-radius and separated border model tables</title> +<style> + +body { background: white; color: black } + +table { border-collapse: separate; margin: 1em 2px; } +table, td { border: 1px solid black; } + +.radius { border: 3px solid teal; background: aqua; color: black; } + +</style> + +<h1>border-radius and separated border model tables</h1> + +<table> + <tbody> + <tr><td>xx</td><td>xx</td><td>xx + </td></tr></tbody> + <tbody class="radius"> + <tr><td>xx</td><td>xx</td><td>xx + </td></tr><tr><td>xx</td><td>xx</td><td>xx + </td></tr></tbody> + <tbody> + <tr><td>xx</td><td>xx</td><td>xx + </td></tr></tbody> +</table> + +<table> + <tbody><tr class="radius"><td>xx</td><td>xx</td><td>xx + </td></tr><tr><td>xx</td><td>xx</td><td>xx +</td></tr></tbody></table> + +<table> + <colgroup class="radius"><col><col></colgroup><colgroup><col> + </colgroup><tbody><tr><td>xx</td><td>xx</td><td>xx + </td></tr><tr><td>xx</td><td>xx</td><td>xx +</td></tr></tbody></table> + +<table> + <colgroup><col><col class="radius"><col> + </colgroup><tbody><tr><td>xx</td><td>xx</td><td>xx + </td></tr><tr><td>xx</td><td>xx</td><td>xx +</td></tr></tbody></table>
new file mode 100644 --- /dev/null +++ b/layout/reftests/table-bordercollapse/bug1375518-4.html @@ -0,0 +1,44 @@ +<!DOCTYPE html> +<title>border-radius and separated border model tables</title> +<style> + +body { background: white; color: black } + +table { border-collapse: separate; margin: 1em 2px; } +table, td { border: 1px solid black; } + +.radius { border: 3px solid teal; background: aqua; color: black; border-radius: 12px } + +</style> + +<h1>border-radius and separated border model tables</h1> + +<table> + <tbody> + <tr><td>xx</td><td>xx</td><td>xx + </td></tr></tbody> + <tbody class="radius"> + <tr><td>xx</td><td>xx</td><td>xx + </td></tr><tr><td>xx</td><td>xx</td><td>xx + </td></tr></tbody> + <tbody> + <tr><td>xx</td><td>xx</td><td>xx + </td></tr></tbody> +</table> + +<table> + <tbody><tr class="radius"><td>xx</td><td>xx</td><td>xx + </td></tr><tr><td>xx</td><td>xx</td><td>xx +</td></tr></tbody></table> + +<table> + <colgroup class="radius"><col><col></colgroup><colgroup><col> + </colgroup><tbody><tr><td>xx</td><td>xx</td><td>xx + </td></tr><tr><td>xx</td><td>xx</td><td>xx +</td></tr></tbody></table> + +<table> + <colgroup><col><col class="radius"><col> + </colgroup><tbody><tr><td>xx</td><td>xx</td><td>xx + </td></tr><tr><td>xx</td><td>xx</td><td>xx +</td></tr></tbody></table>
new file mode 100644 --- /dev/null +++ b/layout/reftests/table-bordercollapse/bug1375518-5-ref.html @@ -0,0 +1,45 @@ +<!DOCTYPE HTML> +<title>border-radius and border-collapse tables</title> +<style> + +body { background: white; color: black } + +table { border-collapse: collapse; margin: 1em 2px; } +td { border: 1px solid black; } + +.radius { border: 3px solid teal; background: aqua; color: black; } + +</style> + +<h1>border-radius and border-collapse tables</h1> + +<table> + <tbody> + <tr><td>xx<td>xx<td>xx + </tbody> + <tbody class="radius"> + <tr><td>xx<td>xx<td>xx + <tr><td>xx<td>xx<td>xx + </tbody> + <tbody> + <tr><td>xx<td>xx<td>xx + </tbody> +</table> + +<table> + <tr class="radius"><td>xx<td>xx<td>xx + <tr><td>xx<td>xx<td>xx +</table> + +<table> + <colgroup class="radius"><col><col><colgroup><col> + <tr><td>xx<td>xx<td>xx + <tr><td>xx<td>xx<td>xx +</table> + +<table> + <col><col class="radius"><col> + <tr><td>xx<td>xx<td>xx + <tr><td>xx<td>xx<td>xx +</table> +
new file mode 100644 --- /dev/null +++ b/layout/reftests/table-bordercollapse/bug1375518-5.html @@ -0,0 +1,45 @@ +<!DOCTYPE HTML> +<title>border-radius and border-collapse tables</title> +<style> + +body { background: white; color: black } + +table { border-collapse: collapse; margin: 1em 2px; } +td { border: 1px solid black; } + +.radius { border: 3px solid teal; background: aqua; color: black; border-radius: 12px } + +</style> + +<h1>border-radius and border-collapse tables</h1> + +<table> + <tbody> + <tr><td>xx<td>xx<td>xx + </tbody> + <tbody class="radius"> + <tr><td>xx<td>xx<td>xx + <tr><td>xx<td>xx<td>xx + </tbody> + <tbody> + <tr><td>xx<td>xx<td>xx + </tbody> +</table> + +<table> + <tr class="radius"><td>xx<td>xx<td>xx + <tr><td>xx<td>xx<td>xx +</table> + +<table> + <colgroup class="radius"><col><col><colgroup><col> + <tr><td>xx<td>xx<td>xx + <tr><td>xx<td>xx<td>xx +</table> + +<table> + <col><col class="radius"><col> + <tr><td>xx<td>xx<td>xx + <tr><td>xx<td>xx<td>xx +</table> +
new file mode 100644 --- /dev/null +++ b/layout/reftests/table-bordercollapse/bug1375518-ref.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<html> +<head> +<title>Table border collapse</title> +<style> + div { + background-color: black; + height: 100px; + width: 100px; + border-radius: 50px; + } +</style> +</head> +<body> + <div></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/table-bordercollapse/bug1375518.html @@ -0,0 +1,24 @@ +<!DOCTYPE html> +<html> +<head> +<title>Table border collapse</title> +<style> + table { + border-collapse: collapse; + height: 100px; + width: 100px; + } + td { + background-color: black; + border-radius: 50px; + } +</style> +</head> +<body> + <table> + <tr> + <td></td> + </tr> + </table> +</body> +</html>
--- a/layout/reftests/table-bordercollapse/reftest.list +++ b/layout/reftests/table-bordercollapse/reftest.list @@ -1,8 +1,13 @@ +== bug1375518.html bug1375518-ref.html +== bug1375518-2.html bug1375518-ref.html +== bug1375518-3.html bug1375518-ref.html +== bug1375518-4.html bug1375518-4-ref.html +== bug1375518-5.html bug1375518-5-ref.html == bc_dyn_cell1.html bc_dyn_cell1_ref.html == bc_dyn_cell2.html bc_dyn_cell2_ref.html == bc_dyn_cell3.html bc_dyn_cell3_ref.html == bc_dyn_cell4.html bc_dyn_cell4_ref.html == bc_dyn_cell5.html bc_dyn_cell5_ref.html == bc_dyn_row1.html bc_dyn_rg1_ref.html == bc_dyn_row2.html bc_dyn_rg2_ref.html == bc_dyn_row3.html bc_dyn_rg3_ref.html
new file mode 100644 --- /dev/null +++ b/layout/style/crashtests/1383975.html @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="UTF-8"> +<style> +color: url(9 +Û° +</style> +</head> +</html>
--- a/layout/style/crashtests/crashtests.list +++ b/layout/style/crashtests/crashtests.list @@ -134,24 +134,20 @@ load 1226400-1.html load 1227501-1.html load 1230408-1.html load 1233135-1.html load 1233135-2.html load 1238660-1.html load 1245260-1.html load 1247865-1.html asserts-if(stylo,0-1) load 1264396-1.html # bug 1324677 +load 1264949.html # The following test relies on -webkit-text-fill-color being behind the # layout.css.prefixes.webkit pref pref(layout.css.prefixes.webkit,false) load 1265611-1.html -load border-image-visited-link.html -load font-face-truncated-src.html -load large_border_image_width.html -load long-url-list-stack-overflow.html -load 1264949.html load 1270795.html load 1275026.html load 1278463-1.html pref(dom.animations-api.core.enabled,true) load 1277908-1.html # bug 1323652 load 1277908-2.html load 1282076-1.html pref(dom.animations-api.core.enabled,true) load 1282076-2.html pref(dom.animations-api.core.enabled,true) load 1290994-1.html @@ -168,25 +164,31 @@ load 1328535-1.html load 1331272.html HTTP load 1333001-1.html pref(dom.animations-api.core.enabled,true) load 1340344.html load 1342316-1.html load 1356601-1.html load 1370793-1.xhtml load 1371450-1.html load 1374175-1.html -load content-only-on-link-before.html -load content-only-on-visited-before.html load 1375812-1.html load 1377053-1.html load 1377256-1.html load 1378064-1.html load 1378814.html load 1380800.html load link-transition-before.html load 1381420-1.html load 1381682.html load 1382672.html load 1382710.html -pref(dom.animations-api.core.enabled,true) load 1383589-1.html load 1383001.html load 1383001-2.html load 1383319.html +pref(dom.animations-api.core.enabled,true) load 1383589-1.html +load 1383975.html +load border-image-visited-link.html +load content-only-on-link-before.html +load content-only-on-visited-before.html +load font-face-truncated-src.html +load large_border_image_width.html +load link-transition-before.html +load long-url-list-stack-overflow.html
--- a/layout/tables/nsTableCellFrame.cpp +++ b/layout/tables/nsTableCellFrame.cpp @@ -1102,28 +1102,16 @@ nsBCTableCellFrame::~nsBCTableCellFrame( /* virtual */ nsMargin nsBCTableCellFrame::GetUsedBorder() const { WritingMode wm = GetWritingMode(); return GetBorderWidth(wm).GetPhysicalMargin(wm); } -/* virtual */ bool -nsBCTableCellFrame::GetBorderRadii(const nsSize& aFrameSize, - const nsSize& aBorderArea, - Sides aSkipSides, - nscoord aRadii[8]) const -{ - NS_FOR_CSS_HALF_CORNERS(corner) { - aRadii[corner] = 0; - } - return false; -} - #ifdef DEBUG_FRAME_DUMP nsresult nsBCTableCellFrame::GetFrameName(nsAString& aResult) const { return MakeFrameName(NS_LITERAL_STRING("BCTableCell"), aResult); } #endif
--- a/layout/tables/nsTableCellFrame.h +++ b/layout/tables/nsTableCellFrame.h @@ -300,20 +300,16 @@ class nsBCTableCellFrame final : public public: NS_DECL_FRAMEARENA_HELPERS(nsBCTableCellFrame) nsBCTableCellFrame(nsStyleContext* aContext, nsTableFrame* aTableFrame); ~nsBCTableCellFrame(); virtual nsMargin GetUsedBorder() const override; - virtual bool GetBorderRadii(const nsSize& aFrameSize, - const nsSize& aBorderArea, - Sides aSkipSides, - nscoord aRadii[8]) const override; // Get the *inner half of the border only*, in twips. virtual LogicalMargin GetBorderWidth(WritingMode aWM) const override; // Get the *inner half of the border only*, in pixels. BCPixelSize GetBorderWidth(LogicalSide aSide) const; // Set the full (both halves) width of the border
--- a/layout/tables/nsTableFrame.cpp +++ b/layout/tables/nsTableFrame.cpp @@ -1426,17 +1426,17 @@ PaintRowGroupBackgroundByColIdx(nsTableR if (aColIdx.Contains(curColIdx)) { auto cellPos = cell->GetNormalPosition() + rowPos; auto cellRect = nsRect(cellPos, cell->GetSize()); if (!aDirtyRect.Intersects(cellRect)) { continue; } nsDisplayBackgroundImage::AppendBackgroundItemsToTop(aBuilder, aFrame, cellRect, aLists.BorderBackground(), - true, nullptr, + false, nullptr, aFrame->GetRectRelativeToSelf(), cell); } } } } static inline bool FrameHasBorder(nsIFrame* f)
--- a/media/webrtc/trunk/webrtc/modules/utility/source/jvm_android.cc +++ b/media/webrtc/trunk/webrtc/modules/utility/source/jvm_android.cc @@ -162,25 +162,25 @@ jmethodID JavaClass::GetMethodId( jmethodID JavaClass::GetStaticMethodId( const char* name, const char* signature) { return GetStaticMethodID(jni_, j_class_, name, signature); } jobject JavaClass::CallStaticObjectMethod(jmethodID methodID, ...) { va_list args; va_start(args, methodID); - jobject res = jni_->CallStaticObjectMethod(j_class_, methodID, args); + jobject res = jni_->CallStaticObjectMethodV(j_class_, methodID, args); CHECK_EXCEPTION(jni_) << "Error during CallStaticObjectMethod"; return res; } jint JavaClass::CallStaticIntMethod(jmethodID methodID, ...) { va_list args; va_start(args, methodID); - jint res = jni_->CallStaticIntMethod(j_class_, methodID, args); + jint res = jni_->CallStaticIntMethodV(j_class_, methodID, args); CHECK_EXCEPTION(jni_) << "Error during CallStaticIntMethod"; return res; } // JNIEnvironment implementation. JNIEnvironment::JNIEnvironment(JNIEnv* jni) : jni_(jni) { ALOGD("JNIEnvironment::ctor%s", GetThreadInfo().c_str()); }
--- a/mfbt/BufferList.h +++ b/mfbt/BufferList.h @@ -4,16 +4,17 @@ * 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 mozilla_BufferList_h #define mozilla_BufferList_h #include <algorithm> #include "mozilla/AllocPolicy.h" +#include "mozilla/MemoryReporting.h" #include "mozilla/Move.h" #include "mozilla/ScopeExit.h" #include "mozilla/Types.h" #include "mozilla/TypeTraits.h" #include "mozilla/Vector.h" #include <string.h> // BufferList represents a sequence of buffers of data. A BufferList can choose @@ -131,16 +132,25 @@ class BufferList : private AllocPolicy MOZ_ASSERT(aInitialCapacity % kSegmentAlignment == 0); return AllocateSegment(aInitialSize, aInitialCapacity); } // Returns the sum of the sizes of all the buffers. size_t Size() const { return mSize; } + size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) + { + size_t size = mSegments.sizeOfExcludingThis(aMallocSizeOf); + for (Segment& segment : mSegments) { + size += aMallocSizeOf(segment.Start()); + } + return size; + } + void Clear() { if (mOwning) { for (Segment& segment : mSegments) { this->free_(segment.mData); } } mSegments.clear();
--- a/mobile/android/app/src/main/res/layout/gecko_app.xml +++ b/mobile/android/app/src/main/res/layout/gecko_app.xml @@ -21,21 +21,20 @@ android:background="@android:color/transparent"> <RelativeLayout android:id="@+id/gecko_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@+id/tablet_tab_strip" android:layout_above="@+id/find_in_page"> - <fragment class="org.mozilla.gecko.GeckoViewFragment" - android:id="@+id/layer_view" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:scrollbars="none"/> + <org.mozilla.gecko.GeckoView android:id="@+id/layer_view" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:scrollbars="none"/> <AbsoluteLayout android:id="@+id/plugin_container" android:background="@android:color/transparent" android:layout_width="match_parent" android:layout_height="match_parent"/> <org.mozilla.gecko.FormAssistPopup android:id="@+id/form_assist_popup" android:layout_width="match_parent"
--- a/mobile/android/base/moz.build +++ b/mobile/android/base/moz.build @@ -378,17 +378,16 @@ gvjar.sources += [geckoview_source_dir + 'GeckoInputConnection.java', 'GeckoNetworkManager.java', 'GeckoProfile.java', 'GeckoProfileDirectories.java', 'GeckoScreenOrientation.java', 'GeckoSharedPrefs.java', 'GeckoThread.java', 'GeckoView.java', - 'GeckoViewFragment.java', 'GeckoViewHandler.java', 'GeckoViewSettings.java', 'gfx/BitmapUtils.java', 'gfx/BufferedImage.java', 'gfx/BufferedImageGLInfo.java', 'gfx/DynamicToolbarAnimator.java', 'gfx/FloatSize.java', 'gfx/FullScreenState.java',
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoView.java @@ -297,17 +297,16 @@ public class GeckoView extends LayerView mPermissionHandler.setListener(delegate, this); } private PromptDelegate mPromptDelegate; private InputConnectionListener mInputConnectionListener; private GeckoViewSettings mSettings; - protected boolean mOnAttachedToWindowCalled; protected String mChromeUri; protected int mScreenId = 0; // default to the primary screen @WrapForJNI(dispatchTo = "proxy") protected static final class Window extends JNIObject { @WrapForJNI(skip = true) public final String chromeUri; @@ -504,20 +503,16 @@ public class GeckoView extends LayerView protected void onRestoreInstanceState(final Parcelable state) { final StateBinder stateBinder = (StateBinder) state; if (stateBinder.window != null) { mWindow = stateBinder.window; } mStateSaved = false; - if (mOnAttachedToWindowCalled) { - reattachWindow(); - } - // We have to always call super.onRestoreInstanceState because View keeps // track of these calls and throws an exception when we don't call it. super.onRestoreInstanceState(stateBinder.superState); } /** * Return the URI of the underlying chrome window opened or to be opened, or null if * using the default GeckoView URI. @@ -582,18 +577,16 @@ public class GeckoView extends LayerView if (mWindow == null) { // Open a new nsWindow if we didn't have one from before. openWindow(); } else { reattachWindow(); } super.onAttachedToWindow(); - - mOnAttachedToWindowCalled = true; } @Override public void onDetachedFromWindow() { super.onDetachedFromWindow(); super.destroy(); if (mStateSaved) { @@ -605,18 +598,16 @@ public class GeckoView extends LayerView mWindow.close(); mWindow.disposeNative(); } else { GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY, mWindow, "close"); GeckoThread.queueNativeCallUntil(GeckoThread.State.PROFILE_READY, mWindow, "disposeNative"); } - - mOnAttachedToWindowCalled = false; } @WrapForJNI public static final int LOAD_DEFAULT = 0; @WrapForJNI public static final int LOAD_NEW_TAB = 1; @WrapForJNI public static final int LOAD_SWITCH_TAB = 2; /** * Load the given URI.
deleted file mode 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoViewFragment.java +++ /dev/null @@ -1,52 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * 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/. */ - -package org.mozilla.gecko; - -import android.support.v4.app.Fragment; -import android.os.Bundle; -import android.os.Parcelable; -import android.util.Log; -import android.util.SparseArray; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -public class GeckoViewFragment extends android.support.v4.app.Fragment { - private static final String LOGTAG = "GeckoViewFragment"; - - private static Parcelable mSavedState = null; - private static GeckoViewFragment mLastUsed = null; - private GeckoView mGeckoView = null; - - @Override - public void onCreate(Bundle savedInstanceState) { - setRetainInstance(true); - super.onCreate(savedInstanceState); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - mGeckoView = new GeckoView(getContext()); - return mGeckoView; - } - - @Override - public void onResume() { - if (mSavedState != null && mLastUsed != this) { - // "Restore" the window from the previously used GeckoView to this GeckoView and attach it - mGeckoView.onRestoreInstanceState(mSavedState); - mSavedState = null; - } - super.onResume(); - } - - @Override - public void onPause() { - mSavedState = mGeckoView.onSaveInstanceState(); - mLastUsed = this; - super.onPause(); - } -}
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoLayerClient.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/GeckoLayerClient.java @@ -45,19 +45,19 @@ class GeckoLayerClient implements LayerV * 3) whenever reading multiple fields from mViewportMetrics without synchronization (i.e. in * case 1 above) you should always first grab a local copy of the reference, and then use * that because mViewportMetrics might get reassigned in between reading the different * fields. */ private volatile ImmutableViewportMetrics mViewportMetrics; private volatile boolean mGeckoIsReady; - /* package */ final PanZoomController mPanZoomController; + private final PanZoomController mPanZoomController; private final DynamicToolbarAnimator mToolbarAnimator; - /* package */ final LayerView mView; + private final LayerView mView; /* This flag is true from the time that browser.js detects a first-paint is about to start, * to the time that we receive the first-paint composite notification from the compositor. * Note that there is a small race condition with this; if there are two paints that both * have the first-paint flag set, and the second paint happens concurrently with the * composite for the first paint, then this flag may be set to true prematurely. Fixing this * is possible but risky; see https://bugzilla.mozilla.org/show_bug.cgi?id=797615#c751 */ @@ -106,18 +106,17 @@ class GeckoLayerClient implements LayerV // Gecko being ready is one of the two conditions (along with having an available // surface) that cause us to create the compositor. So here, now that we know gecko // is ready, call updateCompositor() to see if we can actually do the creation. // This needs to run on the UI thread so that the surface validity can't change on // us while we're in the middle of creating the compositor. mView.post(new Runnable() { @Override public void run() { - mPanZoomController.attach(); - mView.updateCompositor(); + getView().updateCompositor(); } }); } public void destroy() { mPanZoomController.destroy(); mGeckoIsReady = false; } @@ -403,17 +402,17 @@ class GeckoLayerClient implements LayerV /*yPrecision*/ 0, /*deviceId*/ 0, /*edgeFlags*/ 0, /*source*/ source, /*flags*/ 0); mView.post(new Runnable() { @Override public void run() { - mView.dispatchTouchEvent(event); + getView().dispatchTouchEvent(event); } }); // Forget about removed pointers if (eventType == MotionEvent.ACTION_POINTER_UP || eventType == MotionEvent.ACTION_UP || eventType == MotionEvent.ACTION_CANCEL || eventType == MotionEvent.ACTION_HOVER_MOVE)
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerView.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerView.java @@ -21,17 +21,16 @@ import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.SurfaceTexture; -import android.os.Parcelable; import android.support.v4.util.SimpleArrayMap; import android.util.AttributeSet; import android.util.Log; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.TextureView; @@ -60,17 +59,16 @@ public class LayerView extends FrameLayo private Listener mListener; /* This should only be modified on the Java UI thread. */ private final Overscroll mOverscroll; private boolean mServerSurfaceValid; private int mWidth, mHeight; - private boolean onAttachedToWindowCalled; private int mDefaultClearColor = Color.WHITE; /* package */ GetPixelsResult mGetPixelsResult; private final List<DrawListener> mDrawListeners; /* This is written by the Gecko thread and the UI thread, and read by the UI thread. */ /* package */ volatile boolean mCompositorCreated; /* package */ volatile boolean mCompositorControllerOpen; @@ -416,24 +414,16 @@ public class LayerView extends FrameLayo } if (mPanZoomController != null && mPanZoomController.onMotionEvent(event)) { return true; } return false; } @Override - protected void onRestoreInstanceState(final Parcelable state) { - if (onAttachedToWindowCalled) { - attachCompositor(); - } - super.onRestoreInstanceState(state); - } - - @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); // We are adding descendants to this LayerView, but we don't want the // descendants to affect the way LayerView retains its focus. setDescendantFocusability(FOCUS_BLOCK_DESCENDANTS); // This check should not be done before the view is attached to a window @@ -459,25 +449,16 @@ public class LayerView extends FrameLayo mSurfaceView.setBackgroundColor(Color.WHITE); addView(mSurfaceView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); SurfaceHolder holder = mSurfaceView.getHolder(); holder.addCallback(new SurfaceListener()); } attachCompositor(); - - onAttachedToWindowCalled = true; - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - - onAttachedToWindowCalled = false; } // Don't expose GeckoLayerClient to things outside this package; only expose it as an Object GeckoLayerClient getLayerClient() { return mLayerClient; } /* package */ boolean isGeckoReady() { return mLayerClient.isGeckoReady(); }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/NativePanZoomController.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/NativePanZoomController.java @@ -134,17 +134,16 @@ class NativePanZoomController extends JN final float y = coords.y; return handleMouseEvent(event.getActionMasked(), event.getEventTime(), event.getMetaState(), x, y, event.getButtonState()); } NativePanZoomController(View view) { mView = (LayerView) view; - mDestroyed = true; String[] prefs = { "ui.scrolling.negate_wheel_scroll" }; mPrefsObserver = new PrefsHelper.PrefHandlerBase() { @Override public void prefValue(String pref, boolean value) { if (pref.equals("ui.scrolling.negate_wheel_scroll")) { mNegateWheelScroll = value; } } @@ -201,21 +200,16 @@ class NativePanZoomController extends JN } if (mDestroyed || !mView.isGeckoReady()) { return; } mDestroyed = true; disposeNative(); } - @Override - public void attach() { - mDestroyed = false; - } - @WrapForJNI(calledFrom = "ui", dispatchTo = "gecko_priority") @Override // JNIObject protected native void disposeNative(); @Override public void setOverscrollHandler(final Overscroll handler) { mOverscroll = handler; }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/PanZoomController.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/PanZoomController.java @@ -18,17 +18,16 @@ public interface PanZoomController { static class Factory { static PanZoomController create(View view) { return new NativePanZoomController(view); } } public void destroy(); - public void attach(); public boolean onTouchEvent(MotionEvent event); public boolean onMotionEvent(MotionEvent event); public void setOverscrollHandler(final Overscroll controller); public void setIsLongpressEnabled(boolean isLongpressEnabled); }
--- a/mozglue/misc/Printf.h +++ b/mozglue/misc/Printf.h @@ -249,21 +249,11 @@ SmprintfPolicyPointer<AllocPolicy> Vsmpr const char* fmt, va_list ap) { SprintfState<AllocPolicy> ss(last.release()); if (!ss.vprint(fmt, ap)) return nullptr; return ss.release(); } -/* -** Free the memory allocated, for the caller, by Smprintf. -*/ -template<typename AllocPolicy = mozilla::MallocAllocPolicy> -void SmprintfFree(char* mem) -{ - AllocPolicy allocator; - allocator.free_(mem); -} - } // namespace mozilla #endif /* mozilla_Printf_h */
--- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -7355,31 +7355,48 @@ nsHttpChannel::OnStopRequest(nsIRequest // authentication request over it or use it for an upgrade // to another protocol. // // this code relies on the code in nsHttpTransaction::Close, which // tests for NS_HTTP_STICKY_CONNECTION to determine whether or not to // keep the connection around after the transaction is finished. // RefPtr<nsAHttpConnection> conn; - LOG((" authRetry=%d, sticky conn cap=%d", authRetry, mCaps & NS_HTTP_STICKY_CONNECTION)); + LOG((" mAuthRetryPending=%d, status=%" PRIx32 ", sticky conn cap=%d", + mAuthRetryPending, static_cast<uint32_t>(status), + mCaps & NS_HTTP_STICKY_CONNECTION)); // We must check caps for stickinness also on the transaction because it // might have been updated by the transaction itself during inspection of // the reposnse headers yet on the socket thread (found connection based // auth schema). - if (authRetry && (mCaps & NS_HTTP_STICKY_CONNECTION || - mTransaction->Caps() & NS_HTTP_STICKY_CONNECTION)) { + if ((mAuthRetryPending || NS_FAILED(status)) && + (mCaps & NS_HTTP_STICKY_CONNECTION || + mTransaction->Caps() & NS_HTTP_STICKY_CONNECTION)) { + conn = mTransaction->GetConnectionReference(); LOG((" transaction %p provides connection %p", mTransaction.get(), conn.get())); - // This is so far a workaround to fix leak when reusing unpersistent - // connection for authentication retry. See bug 459620 comment 4 - // for details. - if (conn && !conn->IsPersistent()) { - LOG((" connection is not persistent, not reusing it")); - conn = nullptr; + + if (conn) { + if (NS_FAILED(status)) { + // Close (don't reuse) the sticky connection if it's in the middle + // of an NTLM negotiation and this channel has been cancelled. + // There are proxy servers known to get confused when we send + // a new request over such a half-stated connection. + if (!mAuthConnectionRestartable) { + LOG((" not reusing a half-authenticated sticky connection")); + conn->DontReuse(); + } + conn = nullptr; + } else if (!conn->IsPersistent()) { + // This is so far a workaround to fix leak when reusing unpersistent + // connection for authentication retry. See bug 459620 comment 4 + // for details. + LOG((" connection is not persistent, not reusing it")); + conn = nullptr; + } } } RefPtr<nsAHttpConnection> stickyConn; if (mCaps & NS_HTTP_STICKY_CONNECTION) { stickyConn = mTransaction->GetConnectionReference(); }
--- a/parser/htmlparser/nsExpatDriver.cpp +++ b/parser/htmlparser/nsExpatDriver.cpp @@ -831,17 +831,17 @@ CreateErrorText(const char16_t* aDescrip char16_t *message = nsTextFormatter::smprintf(msg.get(), aDescription, aSourceURL, aLineNumber, aColNumber); if (!message) { return NS_ERROR_OUT_OF_MEMORY; } aErrorString.Assign(message); - nsTextFormatter::smprintf_free(message); + free(message); return NS_OK; } static nsresult AppendErrorPointer(const int32_t aColNumber, const char16_t *aSourceLine, nsString& aSourceString) @@ -909,30 +909,30 @@ nsExpatDriver::HandleError() nsAutoString tagName; if (uriEnd && nameEnd) { // We have a prefix. tagName.Append(nameEnd + 1, pos - nameEnd - 1); tagName.Append(char16_t(':')); } const char16_t *nameStart = uriEnd ? uriEnd + 1 : mismatch; tagName.Append(nameStart, (nameEnd ? nameEnd : pos) - nameStart); - + nsAutoString msg; nsParserMsgUtils::GetLocalizedStringByName(XMLPARSER_PROPERTIES, "Expected", msg); // . Expected: </%S>. char16_t *message = nsTextFormatter::smprintf(msg.get(), tagName.get()); if (!message) { return NS_ERROR_OUT_OF_MEMORY; } description.Append(message); - nsTextFormatter::smprintf_free(message); + free(message); } // Adjust the column number so that it is one based rather than zero based. uint32_t colNumber = XML_GetCurrentColumnNumber(mExpatParser) + 1; uint32_t lineNumber = XML_GetCurrentLineNumber(mExpatParser); nsAutoString errorText; CreateErrorText(description.get(), XML_GetBase(mExpatParser), lineNumber,
--- a/python/mozbuild/mozpack/manifests.py +++ b/python/mozbuild/mozpack/manifests.py @@ -405,8 +405,15 @@ class InstallManifestNoSymlinks(InstallM """ def add_symlink(self, source, dest): """A wrapper that accept symlink entries and install file copies. source will be copied to dest. """ self.add_copy(source, dest) + + def add_pattern_symlink(self, base, pattern, dest): + """A wrapper that accepts symlink patterns and installs file copies. + + Files discovered with ``pattern`` will be copied to ``dest``. + """ + self.add_pattern_copy(base, pattern, dest)
--- a/toolkit/components/extensions/Schemas.jsm +++ b/toolkit/components/extensions/Schemas.jsm @@ -62,19 +62,55 @@ function readJSON(url) { resolve(JSON.parse(text)); } catch (e) { reject(e); } }); }); } +function stripDescriptions(json, stripThis = true) { + if (Array.isArray(json)) { + for (let i = 0; i < json.length; i++) { + if (typeof json[i] === "object" && json[i] !== null) { + json[i] = stripDescriptions(json[i]); + } + } + return json; + } + + let result = {}; + + // Objects are handled much more efficiently, both in terms of memory and + // CPU, if they have the same shape as other objects that serve the same + // purpose. So, normalize the order of properties to increase the chances + // that the majority of schema objects wind up in large shape groups. + for (let key of Object.keys(json).sort()) { + if (stripThis && key === "description" && typeof json[key] === "string") { + continue; + } + + if (typeof json[key] === "object" && json[key] !== null) { + result[key] = stripDescriptions(json[key], key !== "properties"); + } else { + result[key] = json[key]; + } + } + + return result; +} + async function readJSONAndBlobbify(url) { let json = await readJSON(url); + // We don't actually use descriptions at runtime, and they make up about a + // third of the size of our structured clone data, so strip them before + // blobbifying. + json = stripDescriptions(json); + return new StructuredCloneHolder(json); } /** * Defines a lazy getter for the given property on the given object. Any * security wrappers are waived on the object before the property is * defined, and the getter and setter methods are wrapped for the target * scope. @@ -2681,19 +2717,31 @@ this.Schemas = { this._needFlush = true; Object.defineProperty(this, "rootNamespace", { enumerable: true, configurable: true, value: new Namespace("", []), }); - for (let blob of this.schemaJSON.values()) { + for (let [key, schema] of this.schemaJSON.entries()) { try { - this.loadSchema(blob.deserialize(global)); + if (typeof schema.deserialize === "function") { + schema = schema.deserialize(global); + + // If we're in the parent process, we need to keep the + // StructuredCloneHolder blob around in order to send to future child + // processes. If we're in a child, we have no further use for it, so + // just store the deserialized schema data in its place. + if (!isParentProcess) { + this.schemaJSON.set(key, schema); + } + } + + this.loadSchema(schema); } catch (e) { Cu.reportError(e); } } return this.rootNamespace; },
--- a/toolkit/components/extensions/test/xpcshell/test_ext_schemas.js +++ b/toolkit/components/extensions/test/xpcshell/test_ext_schemas.js @@ -623,40 +623,40 @@ add_task(async function() { tallied = null; Assert.throws(() => root.testing.pattern("DEADcow"), /String "DEADcow" must match \/\^\[0-9a-f\]\+\$\/i/, "should throw for non-match"); root.testing.format({hostname: "foo"}); verify("call", "testing", "format", [{hostname: "foo", - url: null, relativeUrl: null, - strictRelativeUrl: null}]); + strictRelativeUrl: null, + url: null}]); tallied = null; for (let invalid of ["", " ", "http://foo", "foo/bar", "foo.com/", "foo?"]) { Assert.throws(() => root.testing.format({hostname: invalid}), /Invalid hostname/, "should throw for invalid hostname"); } root.testing.format({url: "http://foo/bar", relativeUrl: "http://foo/bar"}); verify("call", "testing", "format", [{hostname: null, - url: "http://foo/bar", relativeUrl: "http://foo/bar", - strictRelativeUrl: null}]); + strictRelativeUrl: null, + url: "http://foo/bar"}]); tallied = null; root.testing.format({relativeUrl: "foo.html", strictRelativeUrl: "foo.html"}); verify("call", "testing", "format", [{hostname: null, - url: null, relativeUrl: `${wrapper.url}foo.html`, - strictRelativeUrl: `${wrapper.url}foo.html`}]); + strictRelativeUrl: `${wrapper.url}foo.html`, + url: null}]); tallied = null; for (let format of ["url", "relativeUrl"]) { Assert.throws(() => root.testing.format({[format]: "chrome://foo/content/"}), /Access denied/, "should throw for access denied"); } @@ -699,40 +699,40 @@ add_task(async function() { ]; badDates.forEach(str => { Assert.throws(() => root.testing.formatDate({date: str}), /Invalid date string/, "should throw for invalid iso date string"); }); root.testing.deep({foo: {bar: [{baz: {required: 12, optional: "42"}}]}}); - verify("call", "testing", "deep", [{foo: {bar: [{baz: {required: 12, optional: "42"}}]}}]); + verify("call", "testing", "deep", [{foo: {bar: [{baz: {optional: "42", required: 12}}]}}]); tallied = null; Assert.throws(() => root.testing.deep({foo: {bar: [{baz: {optional: "42"}}]}}), /Type error for parameter arg \(Error processing foo\.bar\.0\.baz: Property "required" is required\) for testing\.deep/, "should throw with the correct object path"); - Assert.throws(() => root.testing.deep({foo: {bar: [{baz: {required: 12, optional: 42}}]}}), + Assert.throws(() => root.testing.deep({foo: {bar: [{baz: {optional: 42, required: 12}}]}}), /Type error for parameter arg \(Error processing foo\.bar\.0\.baz\.optional: Expected string instead of 42\) for testing\.deep/, "should throw with the correct object path"); talliedErrors.length = 0; - root.testing.errors({warn: "0123", ignore: "0123", default: "0123"}); - verify("call", "testing", "errors", [{warn: "0123", ignore: "0123", default: "0123"}]); + root.testing.errors({default: "0123", ignore: "0123", warn: "0123"}); + verify("call", "testing", "errors", [{default: "0123", ignore: "0123", warn: "0123"}]); checkErrors([]); - root.testing.errors({warn: "0123", ignore: "x123", default: "0123"}); - verify("call", "testing", "errors", [{warn: "0123", ignore: null, default: "0123"}]); + root.testing.errors({default: "0123", ignore: "x123", warn: "0123"}); + verify("call", "testing", "errors", [{default: "0123", ignore: null, warn: "0123"}]); checkErrors([]); - root.testing.errors({warn: "x123", ignore: "0123", default: "0123"}); - verify("call", "testing", "errors", [{warn: null, ignore: "0123", default: "0123"}]); + root.testing.errors({default: "0123", ignore: "0123", warn: "x123"}); + verify("call", "testing", "errors", [{default: "0123", ignore: "0123", warn: null}]); checkErrors([ 'String "x123" must match /^\\d+$/', ]); root.testing.onFoo.addListener(f); do_check_eq(JSON.stringify(tallied.slice(0, -1)), JSON.stringify(["addListener", "testing", "onFoo"])); do_check_eq(tallied[3][0], f); @@ -781,17 +781,17 @@ add_task(async function() { let stringProxy = new Proxy(stringTarget, {}); Assert.throws(() => root.testing.quack(stringProxy), /Expected a plain JavaScript object, got a Proxy/, "should throw when passing a Proxy"); } root.testing.localize({foo: "__MSG_foo__", bar: "__MSG_foo__", url: "__MSG_http://example.com/__"}); - verify("call", "testing", "localize", [{foo: "FOO", bar: "__MSG_foo__", url: "http://example.com/"}]); + verify("call", "testing", "localize", [{bar: "__MSG_foo__", foo: "FOO", url: "http://example.com/"}]); tallied = null; Assert.throws(() => root.testing.localize({url: "__MSG_/foo/bar__"}), /\/FOO\/BAR is not a valid URL\./, "should throw for invalid URL");
--- a/toolkit/components/jsdownloads/src/DownloadIntegration.jsm +++ b/toolkit/components/jsdownloads/src/DownloadIntegration.jsm @@ -41,20 +41,18 @@ XPCOMUtils.defineLazyModuleGetter(this, XPCOMUtils.defineLazyModuleGetter(this, "DownloadUIHelper", "resource://gre/modules/DownloadUIHelper.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", "resource://gre/modules/FileUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm"); -#ifdef MOZ_PLACES XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", "resource://gre/modules/PlacesUtils.jsm"); -#endif XPCOMUtils.defineLazyModuleGetter(this, "Services", "resource://gre/modules/Services.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm"); XPCOMUtils.defineLazyServiceGetter(this, "gDownloadPlatform", "@mozilla.org/toolkit/download-platform;1", "mozIDownloadPlatform"); @@ -62,20 +60,18 @@ XPCOMUtils.defineLazyServiceGetter(this, "@mozilla.org/process/environment;1", "nsIEnvironment"); XPCOMUtils.defineLazyServiceGetter(this, "gMIMEService", "@mozilla.org/mime;1", "nsIMIMEService"); XPCOMUtils.defineLazyServiceGetter(this, "gExternalProtocolService", "@mozilla.org/uriloader/external-protocol-service;1", "nsIExternalProtocolService"); -#ifdef MOZ_WIDGET_ANDROID XPCOMUtils.defineLazyModuleGetter(this, "RuntimePermissions", "resource://gre/modules/RuntimePermissions.jsm"); -#endif XPCOMUtils.defineLazyGetter(this, "gParentalControlsService", function() { if ("@mozilla.org/parental-controls-service;1" in Cc) { return Cc["@mozilla.org/parental-controls-service;1"] .createInstance(Ci.nsIParentalControlsService); } return null; }); @@ -188,20 +184,22 @@ this.DownloadIntegration = { */ async initializePublicDownloadList(list) { try { await this.loadPublicDownloadListFromStore(list); } catch (ex) { Cu.reportError(ex); } - // After the list of persistent downloads has been loaded, we can add the - // history observers, even if the load operation failed. This object is kept - // alive by the history service. - new DownloadHistoryObserver(list); + if (AppConstants.MOZ_PLACES) { + // After the list of persistent downloads has been loaded, we can add the + // history observers, even if the load operation failed. This object is kept + // alive by the history service. + new DownloadHistoryObserver(list); + } }, /** * Called by initializePublicDownloadList to load the list of persistent * downloads, before its first use by the host application. This function may * be called only once during the entire lifetime of the application. * * @param list @@ -253,81 +251,50 @@ this.DownloadIntegration = { * the save operation will be repeated later. * * @return True to save the download, false otherwise. */ shouldPersistDownload(aDownload) { // On all platforms, we save all the downloads currently in progress, as // well as stopped downloads for which we retained partially downloaded // data or we have blocked data. - if (!aDownload.stopped || aDownload.hasPartialData || - aDownload.hasBlockedData) { - return true; - } -#ifdef MOZ_B2G - // On B2G we keep a few days of history. - let maxTime = Date.now() - - Services.prefs.getIntPref("dom.downloads.max_retention_days") * 24 * 60 * 60 * 1000; - return aDownload.startTime > maxTime; -#elif defined(MOZ_WIDGET_ANDROID) - // On Android we store all history. - return true; -#else - // On Desktop, stopped downloads for which we don't need to track the - // presence of a ".part" file are only retained in the browser history. - return false; -#endif + // On Android we store all history; on Desktop, stopped downloads for which + // we don't need to track the presence of a ".part" file are only retained + // in the browser history. + return !aDownload.stopped || aDownload.hasPartialData || + aDownload.hasBlockedData || AppConstants.platform == "android"; }, /** * Returns the system downloads directory asynchronously. * * @return {Promise} * @resolves The downloads directory string path. */ async getSystemDownloadsDirectory() { if (this._downloadsDirectory) { return this._downloadsDirectory; } - let directoryPath = null; -#ifdef XP_MACOSX - directoryPath = this._getDirectory("DfltDwnld"); -#elifdef XP_WIN - // For XP/2K, use My Documents/Downloads. Other version uses - // the default Downloads directory. - let version = parseFloat(Services.sysinfo.getProperty("version")); - if (version < 6) { - directoryPath = await this._createDownloadsDirectory("Pers"); + if (AppConstants.platform == "android") { + // Android doesn't have a $HOME directory, and by default we only have + // write access to /data/data/org.mozilla.{$APP} and /sdcard + this._downloadsDirectory = gEnvironment.get("DOWNLOADS_DIRECTORY"); + if (!this._downloadsDirectory) { + throw new Components.Exception("DOWNLOADS_DIRECTORY is not set.", + Cr.NS_ERROR_FILE_UNRECOGNIZED_PATH); + } } else { - directoryPath = this._getDirectory("DfltDwnld"); + try { + this._downloadsDirectory = this._getDirectory("DfltDwnld"); + } catch(e) { + this._downloadsDirectory = await this._createDownloadsDirectory("Home"); + } } -#elifdef XP_UNIX -#ifdef MOZ_WIDGET_ANDROID - // Android doesn't have a $HOME directory, and by default we only have - // write access to /data/data/org.mozilla.{$APP} and /sdcard - directoryPath = gEnvironment.get("DOWNLOADS_DIRECTORY"); - if (!directoryPath) { - throw new Components.Exception("DOWNLOADS_DIRECTORY is not set.", - Cr.NS_ERROR_FILE_UNRECOGNIZED_PATH); - } -#else - // For Linux, use XDG download dir, with a fallback to Home/Downloads - // if the XDG user dirs are disabled. - try { - directoryPath = this._getDirectory("DfltDwnld"); - } catch(e) { - directoryPath = await this._createDownloadsDirectory("Home"); - } -#endif -#else - directoryPath = await this._createDownloadsDirectory("Home"); -#endif - this._downloadsDirectory = directoryPath; return this._downloadsDirectory; }, _downloadsDirectory: null, /** * Returns the user downloads directory asynchronously. * * @return {Promise} @@ -364,23 +331,23 @@ this.DownloadIntegration = { /** * Returns the temporary downloads directory asynchronously. * * @return {Promise} * @resolves The downloads directory string path. */ async getTemporaryDownloadsDirectory() { let directoryPath = null; -#ifdef XP_MACOSX - directoryPath = await this.getPreferredDownloadsDirectory(); -#elifdef MOZ_WIDGET_ANDROID - directoryPath = await this.getSystemDownloadsDirectory(); -#else - directoryPath = this._getDirectory("TmpD"); -#endif + if (AppConstants.platform == "macosx") { + directoryPath = await this.getPreferredDownloadsDirectory(); + } else if (AppConstants.platform == "android") { + directoryPath = await this.getSystemDownloadsDirectory(); + } else { + directoryPath = this._getDirectory("TmpD"); + } return directoryPath; }, /** * Checks to determine whether to block downloads for parental controls. * * aParam aDownload * The download object. @@ -405,23 +372,20 @@ this.DownloadIntegration = { }, /** * Checks to determine whether to block downloads for not granted runtime permissions. * * @return {Promise} * @resolves The boolean indicates to block downloads or not. */ - shouldBlockForRuntimePermissions() { -#ifdef MOZ_WIDGET_ANDROID - return RuntimePermissions.waitForPermissions(RuntimePermissions.WRITE_EXTERNAL_STORAGE) - .then(permissionGranted => !permissionGranted); -#else - return Promise.resolve(false); -#endif + async shouldBlockForRuntimePermissions() { + return AppConstants.platform == "android" && + !(await RuntimePermissions.waitForPermissions( + RuntimePermissions.WRITE_EXTERNAL_STORAGE)); }, /** * Checks to determine whether to block downloads because they might be * malware, based on application reputation checks. * * aParam aDownload * The download object. @@ -473,17 +437,16 @@ this.DownloadIntegration = { resolve({ shouldBlock: aShouldBlock, verdict: (aShouldBlock && kVerdictMap[aVerdict]) || "", }); }); }); }, -#ifdef XP_WIN /** * Checks whether downloaded files should be marked as coming from * Internet Zone. * * @return true if files should be marked */ _shouldSaveZoneInformation() { let key = Cc["@mozilla.org/windows-registry-key;1"] @@ -497,39 +460,37 @@ this.DownloadIntegration = { } finally { key.close(); } } catch (ex) { // If the key is not present, files should be marked by default. return true; } }, -#endif /** * Performs platform-specific operations when a download is done. * * aParam aDownload * The Download object. * * @return {Promise} * @resolves When all the operations completed successfully. * @rejects JavaScript exception if any of the operations failed. */ async downloadDone(aDownload) { -#ifdef XP_WIN // On Windows, we mark any file saved to the NTFS file system as coming // from the Internet security zone unless Group Policy disables the // feature. We do this by writing to the "Zone.Identifier" Alternate // Data Stream directly, because the Save method of the // IAttachmentExecute interface would trigger operations that may cause // the application to hang, or other performance issues. // The stream created in this way is forward-compatible with all the // current and future versions of Windows. - if (this._shouldSaveZoneInformation()) { + if (AppConstants.platform == "win" && this._shouldSaveZoneInformation()) { let zone; try { zone = gDownloadPlatform.mapUrlToZone(aDownload.source.url); } catch (e) { // Default to Internet Zone if mapUrlToZone failed for // whatever reason. zone = Ci.mozIDownloadPlatform.ZONE_INTERNET; } @@ -555,17 +516,16 @@ this.DownloadIntegration = { // occur when working on a file system that does not support // Alternate Data Streams, like FAT32, thus we don't report this // specific error. if (!(ex instanceof OS.File.Error) || ex.winLastError != 123) { Cu.reportError(ex); } } } -#endif // The file with the partially downloaded data has restrictive permissions // that don't allow other users on the system to access it. Now that the // download is completed, we need to adjust permissions based on whether // this is a permanently downloaded file or a temporary download to be // opened read-only with an external application. try { // The following logic to determine whether this is a temporary download @@ -1041,17 +1001,16 @@ this.DownloadObserver = { //// nsISupports QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]) }; //////////////////////////////////////////////////////////////////////////////// //// DownloadHistoryObserver -#ifdef MOZ_PLACES /** * Registers a Places observer so that operations on download history are * reflected on the provided list of downloads. * * You do not need to keep a reference to this object in order to keep it alive, * because the history service already keeps a strong reference to it. * * @param aList @@ -1088,22 +1047,16 @@ this.DownloadHistoryObserver.prototype = onTitleChanged: function () {}, onBeginUpdateBatch: function () {}, onEndUpdateBatch: function () {}, onVisit: function () {}, onPageChanged: function () {}, onDeleteVisits: function () {}, }; -#else -/** - * Empty implementation when we have no Places support, for example on B2G. - */ -this.DownloadHistoryObserver = function (aList) {} -#endif //////////////////////////////////////////////////////////////////////////////// //// DownloadAutoSaveView /** * This view can be added to a DownloadList object to trigger a save operation * in the given DownloadStore object when a relevant change occurs. You should * call the "initialize" method in order to register the view and load the
--- a/toolkit/components/jsdownloads/src/moz.build +++ b/toolkit/components/jsdownloads/src/moz.build @@ -10,21 +10,18 @@ SOURCES += [ EXTRA_COMPONENTS += [ 'DownloadLegacy.js', 'Downloads.manifest', ] EXTRA_JS_MODULES += [ 'DownloadCore.jsm', + 'DownloadIntegration.jsm', 'DownloadList.jsm', 'Downloads.jsm', 'DownloadStore.jsm', 'DownloadUIHelper.jsm', ] -EXTRA_PP_JS_MODULES += [ - 'DownloadIntegration.jsm', -] - FINAL_LIBRARY = 'xul' CXXFLAGS += CONFIG['TK_CFLAGS']
--- a/toolkit/components/osfile/moz.build +++ b/toolkit/components/osfile/moz.build @@ -20,16 +20,16 @@ XPIDL_MODULE = 'toolkit_osfile' XPIDL_SOURCES += [ 'nsINativeOSFileInternals.idl', ] EXPORTS.mozilla += [ 'NativeOSFileInternals.h', ] -EXTRA_PP_JS_MODULES += [ +EXTRA_JS_MODULES += [ 'osfile.jsm', ] FINAL_LIBRARY = 'xul' with Files('**'): BUG_COMPONENT = ('Toolkit', 'OS.File')
--- a/toolkit/components/osfile/osfile.jsm +++ b/toolkit/components/osfile/osfile.jsm @@ -5,28 +5,31 @@ /** * Common front for various implementations of OS.File */ if (typeof Components != "undefined") { this.EXPORTED_SYMBOLS = ["OS"]; Components.utils.import("resource://gre/modules/osfile/osfile_async_front.jsm", this); } else { + importScripts("resource://gre/modules/workers/require.js"); + + var SharedAll = require("resource://gre/modules/osfile/osfile_shared_allthreads.jsm"); + // At this stage, we need to import all sources at once to avoid // a unique failure on tbpl + talos that seems caused by a // what looks like a nested event loop bug (see bug 794091). -#ifdef XP_WIN - importScripts( - "resource://gre/modules/workers/require.js", - "resource://gre/modules/osfile/osfile_win_back.jsm", - "resource://gre/modules/osfile/osfile_shared_front.jsm", - "resource://gre/modules/osfile/osfile_win_front.jsm" - ); -#else - importScripts( - "resource://gre/modules/workers/require.js", - "resource://gre/modules/osfile/osfile_unix_back.jsm", - "resource://gre/modules/osfile/osfile_shared_front.jsm", - "resource://gre/modules/osfile/osfile_unix_front.jsm" - ); -#endif + if (SharedAll.Constants.Win) { + importScripts( + "resource://gre/modules/osfile/osfile_win_back.jsm", + "resource://gre/modules/osfile/osfile_shared_front.jsm", + "resource://gre/modules/osfile/osfile_win_front.jsm" + ); + } else { + importScripts( + "resource://gre/modules/osfile/osfile_unix_back.jsm", + "resource://gre/modules/osfile/osfile_shared_front.jsm", + "resource://gre/modules/osfile/osfile_unix_front.jsm" + ); + } + OS.Path = require("resource://gre/modules/osfile/ospath.jsm"); }
--- a/toolkit/components/statusfilter/nsBrowserStatusFilter.cpp +++ b/toolkit/components/statusfilter/nsBrowserStatusFilter.cpp @@ -4,25 +4,27 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsBrowserStatusFilter.h" #include "mozilla/SystemGroup.h" #include "nsIChannel.h" #include "nsITimer.h" #include "nsIServiceManager.h" #include "nsString.h" +#include "nsThreadUtils.h" using namespace mozilla; //----------------------------------------------------------------------------- // nsBrowserStatusFilter <public> //----------------------------------------------------------------------------- nsBrowserStatusFilter::nsBrowserStatusFilter() - : mCurProgress(0) + : mTarget(GetMainThreadEventTarget()) + , mCurProgress(0) , mMaxProgress(0) , mStatusIsDirty(true) , mCurrentPercentage(0) , mTotalRequests(0) , mFinishedRequests(0) , mUseRealProgressFlag(false) , mDelayedStatus(false) , mDelayedProgress(false) @@ -107,16 +109,31 @@ nsBrowserStatusFilter::GetIsLoadingDocum NS_IMETHODIMP nsBrowserStatusFilter::GetLoadType(uint32_t *aLoadType) { *aLoadType = 0; NS_NOTREACHED("nsBrowserStatusFilter::GetLoadType"); return NS_ERROR_NOT_IMPLEMENTED; } +NS_IMETHODIMP +nsBrowserStatusFilter::GetTarget(nsIEventTarget** aTarget) +{ + nsCOMPtr<nsIEventTarget> target = mTarget; + target.forget(aTarget); + return NS_OK; +} + +NS_IMETHODIMP +nsBrowserStatusFilter::SetTarget(nsIEventTarget* aTarget) +{ + mTarget = aTarget; + return NS_OK; +} + //----------------------------------------------------------------------------- // nsBrowserStatusFilter::nsIWebProgressListener //----------------------------------------------------------------------------- NS_IMETHODIMP nsBrowserStatusFilter::OnStateChange(nsIWebProgress *aWebProgress, nsIRequest *aRequest, uint32_t aStateFlags, @@ -357,19 +374,17 @@ nsresult nsBrowserStatusFilter::StartDelayTimer() { NS_ASSERTION(!DelayInEffect(), "delay should not be in effect"); mTimer = do_CreateInstance("@mozilla.org/timer;1"); if (!mTimer) return NS_ERROR_FAILURE; - // Use the system group. The browser status filter is always used by chrome - // code. - mTimer->SetTarget(SystemGroup::EventTargetFor(TaskCategory::Other)); + mTimer->SetTarget(mTarget); return mTimer->InitWithNamedFuncCallback( TimeoutHandler, this, 160, nsITimer::TYPE_ONE_SHOT, "nsBrowserStatusFilter::TimeoutHandler"); } void nsBrowserStatusFilter::ProcessTimeout() {
--- a/toolkit/components/statusfilter/nsBrowserStatusFilter.h +++ b/toolkit/components/statusfilter/nsBrowserStatusFilter.h @@ -41,16 +41,17 @@ private: void MaybeSendStatus(); void ResetMembers(); bool DelayInEffect() { return mDelayedStatus || mDelayedProgress; } static void TimeoutHandler(nsITimer *aTimer, void *aClosure); private: nsCOMPtr<nsIWebProgressListener> mListener; + nsCOMPtr<nsIEventTarget> mTarget; nsCOMPtr<nsITimer> mTimer; // delayed values nsString mStatusMsg; int64_t mCurProgress; int64_t mMaxProgress; nsString mCurrentStatusMsg;
--- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -1,33 +1,41 @@ { "A11Y_INSTANTIATED_FLAG": { - "record_in_processes": ["main", "content"], + "record_in_processes": ["main"], "expires_in_version": "never", "kind": "flag", - "description": "has accessibility support been instantiated" + "releaseChannelCollection": "opt-out", + "bug_numbers": [1382820], + "description": "Flag indicating accessibility support has been instantiated.", + "alert_emails": ["accessibility@mozilla.com"] }, "A11Y_CONSUMERS": { - "record_in_processes": ["main", "content"], + "record_in_processes": ["main"], "expires_in_version": "default", "kind": "enumerated", "n_values": 11, - "description": "Accessibility client by enum id" + "releaseChannelCollection": "opt-out", + "bug_numbers": [1382820], + "description": "A list of known accessibility clients that inject into Firefox process space (see https://dxr.mozilla.org/mozilla-central/source/accessible/windows/msaa/Compatibility.h).", + "alert_emails": ["accessibility@mozilla.com"] }, "A11Y_ISIMPLEDOM_USAGE_FLAG": { - "record_in_processes": ["main", "content"], + "record_in_processes": ["main"], "expires_in_version": "default", "kind": "flag", - "description": "have the ISimpleDOM* accessibility interfaces been used" + "description": "Flag indicating the ISimpleDOM* accessibility interfaces has been used.", + "alert_emails": ["accessibility@mozilla.com"] }, "A11Y_IATABLE_USAGE_FLAG": { - "record_in_processes": ["main", "content"], + "record_in_processes": ["main"], "expires_in_version": "default", "kind": "flag", - "description": "has the IAccessibleTable accessibility interface been used" + "description": "Flag indicating the IAccessibleTable accessibility interface has been used.", + "alert_emails": ["accessibility@mozilla.com"] }, "ADDON_CONTENT_POLICY_SHIM_BLOCKING_LOADING_MS": { "record_in_processes": ["main", "content"], "expires_in_version": "58", "kind": "exponential", "high": 60000, "n_buckets": 20, "keyed": true,
--- a/toolkit/components/telemetry/histogram-whitelists.json +++ b/toolkit/components/telemetry/histogram-whitelists.json @@ -1,14 +1,10 @@ { "alert_emails": [ - "A11Y_CONSUMERS", - "A11Y_IATABLE_USAGE_FLAG", - "A11Y_INSTANTIATED_FLAG", - "A11Y_ISIMPLEDOM_USAGE_FLAG", "ADDON_SHIM_USAGE", "AUDIOSTREAM_FIRST_OPEN_MS", "AUDIOSTREAM_LATER_OPEN_MS", "AUTO_REJECTED_TRANSLATION_OFFERS", "BACKGROUNDFILESAVER_THREAD_COUNT", "BAD_FALLBACK_FONT", "BROWSERPROVIDER_XUL_IMPORT_BOOKMARKS", "BROWSER_IS_ASSIST_DEFAULT", @@ -611,19 +607,17 @@ "WEBCRYPTO_EXTRACTABLE_SIG", "WEBCRYPTO_METHOD", "WEBCRYPTO_RESOLVED", "WEBSOCKETS_HANDSHAKE_TYPE", "XMLHTTPREQUEST_ASYNC_OR_SYNC", "XUL_CACHE_DISABLED" ], "bug_numbers": [ - "A11Y_CONSUMERS", "A11Y_IATABLE_USAGE_FLAG", - "A11Y_INSTANTIATED_FLAG", "A11Y_ISIMPLEDOM_USAGE_FLAG", "ADDON_SHIM_USAGE", "APPLICATION_REPUTATION_COUNT", "APPLICATION_REPUTATION_LOCAL", "APPLICATION_REPUTATION_SERVER", "APPLICATION_REPUTATION_SHOULD_BLOCK", "AUDIOSTREAM_FIRST_OPEN_MS", "AUDIOSTREAM_LATER_OPEN_MS",
--- a/toolkit/content/browser-child.js +++ b/toolkit/content/browser-child.js @@ -28,16 +28,17 @@ if (AppConstants.MOZ_CRASHREPORTER) { "nsICrashReporter"); } var WebProgressListener = { init() { this._filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"] .createInstance(Ci.nsIWebProgress); this._filter.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_ALL); + this._filter.target = tabEventTarget; let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIWebProgress); webProgress.addProgressListener(this._filter, Ci.nsIWebProgress.NOTIFY_ALL); }, uninit() { let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
--- a/toolkit/crashreporter/nsExceptionHandler.cpp +++ b/toolkit/crashreporter/nsExceptionHandler.cpp @@ -3574,17 +3574,17 @@ OOPDeinit() delete dumpMapLock; dumpMapLock = nullptr; delete pidToMinidump; pidToMinidump = nullptr; #if defined(XP_WIN) || defined(XP_MACOSX) - mozilla::SmprintfFree(childCrashNotifyPipe); + free(childCrashNotifyPipe); childCrashNotifyPipe = nullptr; #endif } #if defined(XP_WIN) || defined(XP_MACOSX) // Parent-side API for children const char* GetChildNotificationPipe()
--- a/tools/lint/eslint/eslint-plugin-mozilla/lib/environments/frame-script.js +++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/environments/frame-script.js @@ -17,11 +17,12 @@ module.exports = { content: false, docShell: false, privateNoteIntentionalCrash: false, processMessageManager: false, removeMessageListener: false, removeWeakMessageListener: false, sendAsyncMessage: false, sendSyncMessage: false, - sendRpcMessage: false + sendRpcMessage: false, + tabEventTarget: false } };
--- a/uriloader/base/nsDocLoader.cpp +++ b/uriloader/base/nsDocLoader.cpp @@ -1,14 +1,15 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nspr.h" +#include "mozilla/dom/TabGroup.h" #include "mozilla/Logging.h" #include "mozilla/IntegerPrintfMacros.h" #include "nsDocLoader.h" #include "nsCURILoader.h" #include "nsNetUtil.h" #include "nsIHttpChannel.h" #include "nsIWebProgressListener2.h" @@ -975,16 +976,37 @@ nsDocLoader::GetIsLoadingDocument(bool * NS_IMETHODIMP nsDocLoader::GetLoadType(uint32_t *aLoadType) { *aLoadType = 0; return NS_ERROR_NOT_IMPLEMENTED; } +NS_IMETHODIMP +nsDocLoader::GetTarget(nsIEventTarget** aTarget) +{ + nsCOMPtr<mozIDOMWindowProxy> window; + nsresult rv = GetDOMWindow(getter_AddRefs(window)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsPIDOMWindowOuter> piwindow = nsPIDOMWindowOuter::From(window); + NS_ENSURE_STATE(piwindow); + + nsCOMPtr<nsIEventTarget> target = piwindow->TabGroup()->EventTargetFor(mozilla::TaskCategory::Other); + target.forget(aTarget); + return NS_OK; +} + +NS_IMETHODIMP +nsDocLoader::SetTarget(nsIEventTarget* aTarget) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + int64_t nsDocLoader::GetMaxTotalProgress() { int64_t newMaxTotal = 0; uint32_t count = mChildList.Length(); for (uint32_t i=0; i < count; i++) { int64_t individualProgress = 0;
--- a/uriloader/base/nsIWebProgress.idl +++ b/uriloader/base/nsIWebProgress.idl @@ -2,16 +2,17 @@ * * 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 "nsISupports.idl" interface mozIDOMWindowProxy; +interface nsIEventTarget; interface nsIWebProgressListener; /** * The nsIWebProgress interface is used to add or remove nsIWebProgressListener * instances to observe the loading of asynchronous requests (usually in the * context of a DOM window). * * nsIWebProgress instances may be arranged in a parent-child configuration, @@ -146,9 +147,16 @@ interface nsIWebProgress : nsISupports */ readonly attribute boolean isLoadingDocument; /** * Contains a load type as specified by the load* constants in * nsIDocShellLoadInfo.idl. */ readonly attribute unsigned long loadType; + + /** + * Main thread event target to which progress updates should be + * dispatched. This typically will be a SchedulerEventTarget + * corresponding to the tab requesting updates. + */ + attribute nsIEventTarget target; };
--- a/widget/android/GeckoEditableSupport.cpp +++ b/widget/android/GeckoEditableSupport.cpp @@ -659,17 +659,21 @@ GeckoEditableSupport::FlushIMEChanges(Fl if (aFlags != FLUSH_FLAG_RECOVER) { // First time seeing an exception; try flushing text. env->ExceptionClear(); __android_log_print(ANDROID_LOG_WARN, "GeckoEditableSupport", "Recovering from IME exception"); FlushIMEText(FLUSH_FLAG_RECOVER); } else { // Give up because we've already tried. +#ifdef RELEASE_OR_BETA + env->ExceptionClear(); +#else MOZ_CATCH_JNI_EXCEPTION(env); +#endif } return true; }; // Commit the text change and selection change transaction. mIMETextChanges.Clear(); for (const TextRecord& record : textTransaction) {
--- a/widget/android/fennec/ThumbnailHelper.h +++ b/widget/android/fennec/ThumbnailHelper.h @@ -189,34 +189,47 @@ public: static void Init() { java::ThumbnailHelper::Natives<ThumbnailHelper>::Init(); } template<class Functor> static void OnNativeCall(Functor&& aCall) { - class IdleEvent : public nsAppShell::LambdaEvent<Functor> + class IdleEvent : public Runnable { - using Base = nsAppShell::LambdaEvent<Functor>; + Functor mLambda; + bool mIdlePass; public: IdleEvent(Functor&& aCall) - : Base(Forward<Functor>(aCall)) + : Runnable("ThumbnailHelperIdle") + , mLambda(Move(aCall)) + , mIdlePass(false) {} - void Run() override + NS_IMETHOD Run() override { + // Because we can only post to the idle queue from the main + // queue, we must first post to the main queue and then to the + // idle queue. However, we use the same runnable object for + // both queues, and use mIdlePass to track our progress. + if (mIdlePass) { + mLambda(); + return NS_OK; + } + + mIdlePass = true; MessageLoop::current()->PostIdleTask( - NS_NewRunnableFunction("OnNativeCall", Move(Base::lambda))); + nsCOMPtr<nsIRunnable>(this).forget()); + return NS_OK; } }; - // Invoke RequestThumbnail on the main thread when the thread is idle. - nsAppShell::PostEvent(MakeUnique<IdleEvent>(Forward<Functor>(aCall))); + NS_DispatchToMainThread(new IdleEvent(Move(aCall))); } static void RequestThumbnail(jni::ByteBuffer::Param aData, jni::Object::Param aTab, int32_t aTabId, int32_t aWidth, int32_t aHeight) { nsCOMPtr<mozIDOMWindowProxy> window = GetWindowForTab(aTabId); if (!window || !aData) {
--- a/widget/android/jni/Natives.h +++ b/widget/android/jni/Natives.h @@ -347,17 +347,17 @@ struct ProxyArg<Ref<C, T>> template<typename C> struct ProxyArg<const C&> : ProxyArg<C> {}; template<> struct ProxyArg<StringParam> : ProxyArg<String::Ref> {}; template<class C> struct ProxyArg<LocalRef<C>> : ProxyArg<typename C::Ref> {}; // ProxyNativeCall implements the functor object that is passed to OnNativeCall template<class Impl, class Owner, bool IsStatic, bool HasThisArg /* has instance/class local ref in the call */, typename... Args> -class ProxyNativeCall : public AbstractCall +class ProxyNativeCall { // "this arg" refers to the Class::LocalRef (for static methods) or // Owner::LocalRef (for instance methods) that we optionally (as indicated // by HasThisArg) pass into the destination C++ function. typedef typename mozilla::Conditional<IsStatic, Class, Owner>::Type ThisArgClass; typedef typename mozilla::Conditional<IsStatic, jclass, jobject>::Type ThisArgJNIType; @@ -470,17 +470,17 @@ public: bool IsTarget(NativeCallType call) const { return call == mNativeCall; } template<typename T> bool IsTarget(T&&) const { return false; } // Redirect the call to another function / class member with the same // signature as the original target. Crash if given a wrong signature. void SetTarget(NativeCallType call) { mNativeCall = call; } template<typename T> void SetTarget(T&&) const { MOZ_CRASH(); } - void operator()() override + void operator()() { JNIEnv* const env = GetEnvForThread(); typename ThisArgClass::LocalRef thisArg(env, mThisArg); Call<IsStatic, HasThisArg>( thisArg, typename IndexSequenceFor<Args...>::Type()); // Clear all saved global refs. We do this after the call is invoked, // and not inside the destructor because we already have a JNIEnv here, @@ -509,35 +509,39 @@ struct Dispatcher typename ThisArg, typename... ProxyArgs> static typename EnableIf< Traits::dispatchTarget == DispatchTarget::GECKO_PRIORITY, void>::Type Run(ThisArg thisArg, ProxyArgs&&... args) { // For a static method, do not forward the "this arg" (i.e. the class // local ref) if the implementation does not request it. This saves us // a pair of calls to add/delete global ref. - DispatchToGeckoPriorityQueue(MakeUnique<ProxyNativeCall< - Impl, typename Traits::Owner, IsStatic, HasThisArg, - Args...>>(HasThisArg || !IsStatic ? thisArg : nullptr, - Forward<ProxyArgs>(args)...)); + auto proxy = ProxyNativeCall<Impl, typename Traits::Owner, IsStatic, + HasThisArg, Args...>( + (HasThisArg || !IsStatic) ? thisArg : nullptr, + Forward<ProxyArgs>(args)...); + DispatchToGeckoPriorityQueue( + NS_NewRunnableFunction("PriorityNativeCall", Move(proxy))); } template<class Traits, bool IsStatic = Traits::isStatic, typename ThisArg, typename... ProxyArgs> static typename EnableIf< Traits::dispatchTarget == DispatchTarget::GECKO, void>::Type Run(ThisArg thisArg, ProxyArgs&&... args) { // For a static method, do not forward the "this arg" (i.e. the class // local ref) if the implementation does not request it. This saves us // a pair of calls to add/delete global ref. - NS_DispatchToMainThread(NS_NewRunnableFunction("ProxyNativeCall", ProxyNativeCall< - Impl, typename Traits::Owner, IsStatic, HasThisArg, - Args...>(HasThisArg || !IsStatic ? thisArg : nullptr, - Forward<ProxyArgs>(args)...))); + auto proxy = ProxyNativeCall<Impl, typename Traits::Owner, IsStatic, + HasThisArg, Args...>( + (HasThisArg || !IsStatic) ? thisArg : nullptr, + Forward<ProxyArgs>(args)...); + NS_DispatchToMainThread( + NS_NewRunnableFunction("GeckoNativeCall", Move(proxy))); } template<class Traits, bool IsStatic = false, typename... ProxyArgs> static typename EnableIf< Traits::dispatchTarget == DispatchTarget::CURRENT, void>::Type Run(ProxyArgs&&... args) { MOZ_CRASH("Unreachable code");
--- a/widget/android/jni/Utils.cpp +++ b/widget/android/jni/Utils.cpp @@ -283,34 +283,27 @@ jclass GetClassRef(JNIEnv* aEnv, const c "Does the class require a newer API version? " "Or did ProGuard optimize away something it shouldn't have?", aClassName); aEnv->ExceptionDescribe(); MOZ_CRASH("Cannot find JNI class"); return nullptr; } -void DispatchToGeckoPriorityQueue(UniquePtr<AbstractCall>&& aCall) +void DispatchToGeckoPriorityQueue(already_AddRefed<nsIRunnable> aCall) { - class AbstractCallEvent : public nsAppShell::Event + class RunnableEvent : public nsAppShell::Event { - UniquePtr<AbstractCall> mCall; - + nsCOMPtr<nsIRunnable> mCall; public: - AbstractCallEvent(UniquePtr<AbstractCall>&& aCall) - : mCall(Move(aCall)) - {} - - void Run() override - { - (*mCall)(); - } + RunnableEvent(already_AddRefed<nsIRunnable> aCall) : mCall(aCall) {} + void Run() override { NS_ENSURE_SUCCESS_VOID(mCall->Run()); } }; - nsAppShell::PostEvent(MakeUnique<AbstractCallEvent>(Move(aCall))); + nsAppShell::PostEvent(MakeUnique<RunnableEvent>(Move(aCall))); } bool IsFennec() { return sIsFennec; } int GetAPIVersion() {
--- a/widget/android/jni/Utils.h +++ b/widget/android/jni/Utils.h @@ -1,13 +1,15 @@ #ifndef mozilla_jni_Utils_h__ #define mozilla_jni_Utils_h__ #include <jni.h> +#include "nsIRunnable.h" + #include "mozilla/UniquePtr.h" #if defined(DEBUG) || !defined(RELEASE_OR_BETA) #define MOZ_CHECK_JNI #endif #ifdef MOZ_CHECK_JNI #include <pthread.h> @@ -127,23 +129,17 @@ bool ReportException(JNIEnv* aEnv, jthro uintptr_t GetNativeHandle(JNIEnv* env, jobject instance); void SetNativeHandle(JNIEnv* env, jobject instance, uintptr_t handle); jclass GetClassRef(JNIEnv* aEnv, const char* aClassName); -struct AbstractCall -{ - virtual ~AbstractCall() {} - virtual void operator()() = 0; -}; - -void DispatchToGeckoPriorityQueue(UniquePtr<AbstractCall>&& aCall); +void DispatchToGeckoPriorityQueue(already_AddRefed<nsIRunnable> aCall); /** * Returns whether Gecko is running in a Fennec environment, as determined by * the presence of the GeckoApp class. */ bool IsFennec(); int GetAPIVersion();
--- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -111,21 +111,18 @@ static nsTArray<nsWindow*> gTopLevelWind static bool sFailedToCreateGLContext = false; // Multitouch swipe thresholds in inches static const double SWIPE_MAX_PINCH_DELTA_INCHES = 0.4; static const double SWIPE_MIN_DISTANCE_INCHES = 0.6; template<typename Lambda, bool IsStatic, typename InstanceType, class Impl> -class nsWindow::WindowEvent : public nsAppShell::LambdaEvent<Lambda> +class nsWindow::WindowEvent : public Runnable { - typedef nsAppShell::Event Event; - typedef nsAppShell::LambdaEvent<Lambda> Base; - bool IsStaleCall() { if (IsStatic) { // Static calls are never stale. return false; } JNIEnv* const env = mozilla::jni::GetEnvForThread(); @@ -137,45 +134,39 @@ class nsWindow::WindowEvent : public nsA // The call is stale if the nsWindow has been destroyed on the // Gecko side, but the Java object is still attached to it through // a weak pointer. Stale calls should be discarded. Note that it's // an error if natives is nullptr here; we return false but the // native call will throw an error. return natives && !natives->get(); } + Lambda mLambda; const InstanceType mInstance; - const Event::Type mEventType; public: WindowEvent(Lambda&& aLambda, - InstanceType&& aInstance, - Event::Type aEventType = Event::Type::kGeneralActivity) - : Base(mozilla::Move(aLambda)) - , mInstance(mozilla::Move(aInstance)) - , mEventType(aEventType) + InstanceType&& aInstance) + : Runnable("nsWindowEvent") + , mLambda(mozilla::Move(aLambda)) + , mInstance(Forward<InstanceType>(aInstance)) {} - WindowEvent(Lambda&& aLambda, - Event::Type aEventType = Event::Type::kGeneralActivity) - : Base(mozilla::Move(aLambda)) - , mInstance(Base::lambda.GetThisArg()) - , mEventType(aEventType) + WindowEvent(Lambda&& aLambda) + : Runnable("nsWindowEvent") + , mLambda(mozilla::Move(aLambda)) + , mInstance(mLambda.GetThisArg()) {} - void Run() override + NS_IMETHOD Run() override { if (!IsStaleCall()) { - return Base::Run(); + mLambda(); } - } - - Event::Type ActivityType() const override - { - return mEventType; + return NS_OK; } }; namespace { template<class Instance, class Impl> typename EnableIf< jni::detail::NativePtrPicker<Impl>::value == jni::detail::REFPTR, void>::Type CallAttachNative(Instance aInstance, Impl* aImpl) @@ -259,18 +250,17 @@ public: { if (aCall.IsTarget(&Open) && NS_IsMainThread()) { // Gecko state probably just switched to PROFILE_READY, and the // event loop is not running yet. Skip the event loop here so we // can get a head start on opening our window. return aCall(); } - nsAppShell::PostEvent(mozilla::MakeUnique<WindowEvent<Functor>>( - mozilla::Move(aCall))); + NS_DispatchToMainThread(new WindowEvent<Functor>(mozilla::Move(aCall))); } GeckoViewSupport(nsWindow* aWindow, const GeckoView::Window::LocalRef& aInstance) : window(*aWindow) , mGeckoViewWindow(aInstance) { Base::AttachNative(aInstance, static_cast<SupportsWeakPtr*>(this)); @@ -358,16 +348,17 @@ class nsWindow::NPZCSupport final { return nsAppShell::Event::Type::kUIActivity; } }; template<typename Lambda> void PostInputEvent(Lambda&& aLambda) { + // Use priority queue for input events. nsAppShell::PostEvent(MakeUnique<InputEvent<Lambda>>( this, mozilla::Move(aLambda))); } public: typedef NativePanZoomController::Natives<NPZCSupport> Base; NPZCSupport(NativePtr<NPZCSupport>* aPtr, nsWindow* aWindow, @@ -992,16 +983,17 @@ public: LayerView::Compositor::LocalRef(env, mCompositor))) { MOZ_CATCH_JNI_EXCEPTION(env); lvs->OnResumedCompositor(); } } }; + // Use priority queue for timing-sensitive event. nsAppShell::PostEvent(MakeUnique<LayerViewEvent>( MakeUnique<OnResumedEvent>(aObj))); } void SyncInvalidateAndScheduleComposite() { RefPtr<UiCompositorControllerChild> child = GetUiCompositorControllerChild(); if (!child) {
--- a/xpcom/string/nsTextFormatter.cpp +++ b/xpcom/string/nsTextFormatter.cpp @@ -1375,17 +1375,8 @@ nsTextFormatter::vsnprintf(char16_t* aOu if ((ss.cur != ss.base) && (*(ss.cur - 1) != '\0')) { *(--ss.cur) = '\0'; } n = ss.cur - ss.base; return n ? n - 1 : n; } -/* - * Free memory allocated, for the caller, by smprintf - */ -void -nsTextFormatter::smprintf_free(char16_t* aMem) -{ - free(aMem); -} -
--- a/xpcom/string/nsTextFormatter.h +++ b/xpcom/string/nsTextFormatter.h @@ -49,32 +49,24 @@ public: * terminated. Returns the length of the written output, NOT including the * null terminator, or (uint32_t)-1 if an error occurs. */ static uint32_t snprintf(char16_t* aOut, uint32_t aOutLen, const char16_t* aFmt, ...); /* * sprintf into a moz_xmalloc'd buffer. Return a pointer to - * buffer on success, nullptr on failure. + * buffer on success, nullptr on failure. Use free() to free the buffer. */ static char16_t* smprintf(const char16_t* aFmt, ...); static uint32_t ssprintf(nsAString& aOut, const char16_t* aFmt, ...); /* * va_list forms of the above. */ static uint32_t vsnprintf(char16_t* aOut, uint32_t aOutLen, const char16_t* aFmt, va_list aAp); static char16_t* vsmprintf(const char16_t* aFmt, va_list aAp); static uint32_t vssprintf(nsAString& aOut, const char16_t* aFmt, va_list aAp); - - /* - * Free the memory allocated, for the caller, by smprintf. - * -- Deprecated -- - * Callers can substitute calling smprintf_free with free - */ - static void smprintf_free(char16_t* aMem); - }; #endif /* nsTextFormatter_h___ */
--- a/xpcom/threads/nsEnvironment.cpp +++ b/xpcom/threads/nsEnvironment.cpp @@ -149,15 +149,15 @@ nsEnvironment::Set(const nsAString& aNam nativeName.get(), nativeVal.get()); if (!newData) { return NS_ERROR_OUT_OF_MEMORY; } PR_SetEnv(newData.get()); if (entry->mData) { - mozilla::SmprintfFree(entry->mData); + free(entry->mData); } entry->mData = newData.release(); return NS_OK; }
--- a/xpcom/threads/nsIThreadManager.idl +++ b/xpcom/threads/nsIThreadManager.idl @@ -3,16 +3,17 @@ /* 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 "nsISupports.idl" [ptr] native PRThread(PRThread); +interface nsIEventTarget; interface nsIRunnable; interface nsIThread; [scriptable, function, uuid(039a227d-0cb7-44a5-a8f9-dbb7071979f2)] interface nsINestedEventLoopCondition : nsISupports { /** * Returns true if the current nested event loop should stop spinning. @@ -121,9 +122,14 @@ interface nsIThreadManager : nsISupports /** * Spin the current thread's event loop until there are no more pending * events. This could be done with spinEventLoopUntil, but that would * require access to the current thread from JavaScript, which we are * moving away from. */ void spinEventLoopUntilEmpty(); + + /** + * Return the SchedulerEventTarget for the SystemGroup. + */ + readonly attribute nsIEventTarget systemGroupEventTarget; };
--- a/xpcom/threads/nsThreadManager.cpp +++ b/xpcom/threads/nsThreadManager.cpp @@ -7,16 +7,17 @@ #include "nsThreadManager.h" #include "nsThread.h" #include "nsThreadUtils.h" #include "nsIClassInfoImpl.h" #include "nsTArray.h" #include "nsAutoPtr.h" #include "mozilla/AbstractThread.h" #include "mozilla/InputEventStatistics.h" +#include "mozilla/SystemGroup.h" #include "mozilla/ThreadLocal.h" #include "mozilla/Preferences.h" #ifdef MOZ_CANARY #include <fcntl.h> #include <unistd.h> #endif #include "MainThreadIdlePeriod.h" @@ -373,16 +374,24 @@ nsThreadManager::SpinEventLoopUntilEmpty while (NS_HasPendingEvents(thread)) { (void)NS_ProcessNextEvent(thread, false); } return NS_OK; } +NS_IMETHODIMP +nsThreadManager::GetSystemGroupEventTarget(nsIEventTarget** aTarget) +{ + nsCOMPtr<nsIEventTarget> target = SystemGroup::EventTargetFor(TaskCategory::Other); + target.forget(aTarget); + return NS_OK; +} + uint32_t nsThreadManager::GetHighestNumberOfThreads() { OffTheBooksMutexAutoLock lock(mLock); return mHighestNumberOfThreads; } NS_IMETHODIMP