author | Phil Ringnalda <philringnalda@gmail.com> |
Fri, 25 Nov 2016 21:26:21 -0800 | |
changeset 371573 | f8f4eaac1701107f794b48891bcca2c95d39d503 |
parent 371552 | 180c7af5922ee89f848c25c3847bd1fb22a71e37 (current diff) |
parent 371572 | 5b45f7938182ea8bf814ab0bb6373bcf803755b5 (diff) |
child 371574 | a8f717430769a9e2b79f244569b0a4613a924f7f |
child 371577 | f485eaccbb98034a1111eaf4f4871de1868bcb70 |
child 371588 | 95a822efc877b26b46b348aa334fd482377267ab |
push id | 1419 |
push user | jlund@mozilla.com |
push date | Mon, 10 Apr 2017 20:44:07 +0000 |
treeherder | mozilla-release@5e6801b73ef6 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 53.0a1 |
first release with | nightly linux32
f8f4eaac1701
/
53.0a1
/
20161126030207
/
files
nightly linux64
f8f4eaac1701
/
53.0a1
/
20161126030207
/
files
nightly mac
f8f4eaac1701
/
53.0a1
/
20161126030207
/
files
nightly win32
f8f4eaac1701
/
53.0a1
/
20161126030207
/
files
nightly win64
f8f4eaac1701
/
53.0a1
/
20161126030207
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
53.0a1
/
20161126030207
/
pushlog to previous
nightly linux64
53.0a1
/
20161126030207
/
pushlog to previous
nightly mac
53.0a1
/
20161126030207
/
pushlog to previous
nightly win32
53.0a1
/
20161126030207
/
pushlog to previous
nightly win64
53.0a1
/
20161126030207
/
pushlog to previous
|
--- a/browser/base/content/test/general/browser_remoteTroubleshoot.js +++ b/browser/base/content/test/general/browser_remoteTroubleshoot.js @@ -32,18 +32,18 @@ function promiseNewChannelResponse(uri) gBrowser.removeTab(tab); return data; }); } add_task(function*() { // We haven't set a permission yet - so even the "good" URI should fail. let got = yield promiseNewChannelResponse(TEST_URI_GOOD); - // Should have no data. - Assert.ok(got.message === undefined, "should have failed to get any data"); + // Should return an error. + Assert.ok(got.message.errno === 2, "should have failed with errno 2, no such channel"); // Add a permission manager entry for our URI. Services.perms.add(TEST_URI_GOOD, "remote-troubleshooting", Services.perms.ALLOW_ACTION); registerCleanupFunction(() => { Services.perms.remove(TEST_URI_GOOD, "remote-troubleshooting"); }); @@ -71,19 +71,19 @@ add_task(function*() { "should have correct update channel."); } // And check some keys we know we decline to return. Assert.ok(!got.message.modifiedPreferences, "should not have a modifiedPreferences key"); Assert.ok(!got.message.crashes, "should not have crash info"); - // Now a http:// URI - should get nothing even with the permission setup. + // Now a http:// URI - should receive an error got = yield promiseNewChannelResponse(TEST_URI_BAD); - Assert.ok(got.message === undefined, "should have failed to get any data"); + Assert.ok(got.message.errno === 2, "should have failed with errno 2, no such channel"); // Check that the page can send an object as well if it's in the whitelist let webchannelWhitelistPref = "webchannel.allowObject.urlWhitelist"; let origWhitelist = Services.prefs.getCharPref(webchannelWhitelistPref); let newWhitelist = origWhitelist + " https://example.com"; Services.prefs.setCharPref(webchannelWhitelistPref, newWhitelist); registerCleanupFunction(() => { Services.prefs.clearUserPref(webchannelWhitelistPref);
--- a/browser/base/content/test/general/browser_web_channel.html +++ b/browser/base/content/test/general/browser_web_channel.html @@ -31,16 +31,22 @@ test_unsolicited(); break; case "bubbles": test_bubbles(); break; case "object": test_object(); break; + case "error_thrown": + test_error_thrown(); + break; + case "error_invalid_channel": + test_error_invalid_channel(); + break; default: throw new Error(`INVALID TEST NAME ${testName}`); } }; function test_generic() { var event = new window.CustomEvent("WebChannelMessageToChrome", { detail: JSON.stringify({ @@ -167,16 +173,54 @@ }) }); // Test fails if objectMessage is received, we send stringMessage to know // when we should stop listening for objectMessage window.dispatchEvent(objectMessage); window.dispatchEvent(stringMessage); } + function test_error_thrown() { + var event = new window.CustomEvent("WebChannelMessageToChrome", { + detail: JSON.stringify({ + id: "error", + message: { + command: "oops" + } + }) + }); + + // echo the response back to chrome - chrome will check it is the + // expected error. + window.addEventListener("WebChannelMessageToContent", function(e) { + echoEventToChannel(e, "echo"); + }, true); + + window.dispatchEvent(event); + } + + function test_error_invalid_channel() { + var event = new window.CustomEvent("WebChannelMessageToChrome", { + detail: JSON.stringify({ + id: "invalid-channel", + message: { + command: "oops" + } + }) + }); + + // echo the response back to chrome - chrome will check it is the + // expected error. + window.addEventListener("WebChannelMessageToContent", function(e) { + echoEventToChannel(e, "echo"); + }, true); + + window.dispatchEvent(event); + } + function echoEventToChannel(e, channelId) { var echoedEvent = new window.CustomEvent("WebChannelMessageToChrome", { detail: JSON.stringify({ id: channelId, message: e.detail.message, }) });
--- a/browser/base/content/test/general/browser_web_channel.js +++ b/browser/base/content/test/general/browser_web_channel.js @@ -391,17 +391,78 @@ var gTests = [ gBrowser, url: HTTP_PATH + HTTP_ENDPOINT + "?object" }, function* () { yield testDonePromise; Services.prefs.setCharPref(webchannelWhitelistPref, origWhitelist); channel.stopListening(); }); } - } + }, + { + desc: "WebChannel errors handling the message are delivered back to content", + run: function* () { + const ERRNO_UNKNOWN_ERROR = 999; // WebChannel.jsm doesn't export this. + + // The channel where we purposely fail responding to a command. + let channel = new WebChannel("error", Services.io.newURI(HTTP_PATH, null, null)); + // The channel where we see the response when the content sees the error + let echoChannel = new WebChannel("echo", Services.io.newURI(HTTP_PATH, null, null)); + + let testDonePromise = new Promise((resolve, reject) => { + // listen for the confirmation that content saw the error. + echoChannel.listen((id, message, sender) => { + is(id, "echo"); + is(message.error, "oh no"); + is(message.errno, ERRNO_UNKNOWN_ERROR); + resolve(); + }); + + // listen for a message telling us to simulate an error. + channel.listen((id, message, sender) => { + is(id, "error"); + is(message.command, "oops"); + throw new Error("oh no"); + }); + }); + yield BrowserTestUtils.withNewTab({ + gBrowser, + url: HTTP_PATH + HTTP_ENDPOINT + "?error_thrown" + }, function* () { + yield testDonePromise; + channel.stopListening(); + echoChannel.stopListening(); + }); + } + }, + { + desc: "WebChannel errors due to an invalid channel are delivered back to content", + run: function* () { + const ERRNO_NO_SUCH_CHANNEL = 2; // WebChannel.jsm doesn't export this. + // The channel where we see the response when the content sees the error + let echoChannel = new WebChannel("echo", Services.io.newURI(HTTP_PATH, null, null)); + + let testDonePromise = new Promise((resolve, reject) => { + // listen for the confirmation that content saw the error. + echoChannel.listen((id, message, sender) => { + is(id, "echo"); + is(message.error, "No Such Channel"); + is(message.errno, ERRNO_NO_SUCH_CHANNEL); + resolve(); + }); + }); + yield BrowserTestUtils.withNewTab({ + gBrowser, + url: HTTP_PATH + HTTP_ENDPOINT + "?error_invalid_channel" + }, function* () { + yield testDonePromise; + echoChannel.stopListening(); + }); + } + }, ]; // gTests function test() { waitForExplicitFinish(); Task.spawn(function* () { for (let testCase of gTests) { info("Running: " + testCase.desc);
--- a/browser/themes/shared/aboutNetError.css +++ b/browser/themes/shared/aboutNetError.css @@ -105,20 +105,16 @@ body.certerror #advancedButton { } span#hostname { font-weight: bold; } #automaticallyReportInFuture { cursor: pointer; - display: inline-block; - padding-inline-start: 2.3em; - text-indent: -2.3em; - line-height: 16px } #errorCode:not([href]) { color: var(--in-content-page-color); cursor: text; text-decoration: none; }
--- a/browser/themes/shared/downloads/allDownloadsViewOverlay.inc.css +++ b/browser/themes/shared/downloads/allDownloadsViewOverlay.inc.css @@ -91,17 +91,16 @@ padding: 5px; color: inherit; } .downloadButton > .button-box { -moz-appearance: none; padding: 2px !important; border-radius: 50%; - color: graytext; } .downloadButton > .button-box > .button-icon { width: 16px; height: 16px; margin: 0; filter: url("chrome://global/skin/filters.svg#fill"); fill: currentColor; @@ -115,20 +114,16 @@ background-color: graytext; color: -moz-field; } .downloadButton:hover:active > .button-box { background-color: -moz-fieldtext; } -@itemFocused@ > .downloadButtonArea > .downloadButton > .button-box { - color: inherit; -} - @itemFocused@ > .downloadButtonArea > .downloadButton:hover > .button-box { background-color: HighlightText; color: Highlight; } @itemFocused@ > .downloadButtonArea > .downloadButton:hover:active > .button-box { background-color: -moz-field; color: -moz-fieldtext;
--- a/browser/themes/shared/downloads/downloads.inc.css +++ b/browser/themes/shared/downloads/downloads.inc.css @@ -255,17 +255,17 @@ richlistitem[type="download"] > .downloa .downloadButton { -moz-appearance: none; min-width: 58px; margin: 0; border: none; background: transparent; padding: 8px; - color: graytext; + color: inherit; } .downloadButton > .button-box > .button-icon { width: 16px; height: 16px; margin: 1px; filter: url("chrome://global/skin/filters.svg#fill"); fill: currentColor; @@ -302,20 +302,16 @@ richlistitem[type="download"] > .downloa @item@[verdict="Malware"]:hover, @item@[verdict="Malware"]:hover:active, @item@[verdict="Malware"][showingsubview] { background-color: #aa1b08; color: white; } -@item@[verdict="Malware"]:hover > .downloadButtonArea > .downloadButton { - color: inherit; -} - /*** Button icons ***/ .downloadIconCancel > .button-box > .button-icon { list-style-image: url("chrome://browser/skin/panel-icons.svg#cancel"); } .downloadIconShow > .button-box > .button-icon { %ifdef XP_MACOSX
--- a/devtools/client/inspector/markup/test/browser_markup_html_edit_01.js +++ b/devtools/client/inspector/markup/test/browser_markup_html_edit_01.js @@ -1,16 +1,18 @@ /* vim: set ts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ /* import-globals-from helper_outerhtml_test_runner.js */ "use strict"; // Test outerHTML edition via the markup-view +requestLongerTimeout(2); + loadHelperScript("helper_outerhtml_test_runner.js"); const TEST_DATA = [{ selector: "#one", oldHTML: '<div id="one">First <em>Div</em></div>', newHTML: '<div id="one">First Div</div>', validate: function* ({pageNodeFront, selectedNodeFront, testActor}) { let text = yield testActor.getProperty("#one", "textContent");
--- a/devtools/client/preferences/devtools.js +++ b/devtools/client/preferences/devtools.js @@ -65,16 +65,19 @@ pref("devtools.inspector.showAllAnonymou pref("devtools.inspector.mdnDocsTooltip.enabled", true); // Enable the Font Inspector pref("devtools.fontinspector.enabled", true); // Enable the Layout View pref("devtools.layoutview.enabled", false); +// Grid highlighter preferences +pref("devtools.gridinspector.showInfiniteLines", false); + // By how many times eyedropper will magnify pixels pref("devtools.eyedropper.zoom", 6); // Enable to collapse attributes that are too long. pref("devtools.markup.collapseAttributes", true); // Length to collapse attributes pref("devtools.markup.collapseAttributeLength", 120);
--- a/devtools/client/responsive.html/browser/tunnel.js +++ b/devtools/client/responsive.html/browser/tunnel.js @@ -309,17 +309,18 @@ function copyPermanentKey(outer, inner) // outer browser (that we're hiding the inner browser within) as part of its history. // We want SessionStore's view of the history for our tab to only have the page content // of the inner browser, so we want to hide this message from SessionStore, but we have // no direct mechanism to do so. As a workaround, we wait until the one errant message // has gone by, and then we copy the permanentKey after that, since the permanentKey is // what SessionStore uses to identify each browser. let outerMM = outer[FRAME_LOADER].messageManager; let onHistoryEntry = message => { - let history = message.data.data.history; + let data = message.data.data; + let history = data.history || data.historychange; if (!history || !history.entries) { // Wait for a message that contains history data return; } outerMM.removeMessageListener("SessionStore:update", onHistoryEntry); debug("Got session update for outer browser"); DevToolsUtils.executeSoon(() => { debug("Copy inner permanentKey to outer browser");
--- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -1731,28 +1731,16 @@ Element::BindToTree(nsIDocument* aDocume child = child->GetNextSibling()) { rv = child->BindToTree(nullptr, shadowRoot, shadowRoot->GetBindingParent(), aCompileEventHandlers); NS_ENSURE_SUCCESS(rv, rv); } } - // It would be cleanest to mark nodes as dirty when (a) they're created and - // (b) they're unbound from a tree. However, we can't easily do (a) right now, - // because IsStyledByServo() is not always easy to check at node creation time, - // and the bits have different meaning in the non-IsStyledByServo case. - // - // So for now, we just mark nodes as dirty when they're inserted into a - // document or shadow tree. - if (IsStyledByServo() && IsInComposedDoc()) { - MOZ_ASSERT(!HasServoData()); - SetIsDirtyForServo(); - } - // XXXbz script execution during binding can trigger some of these // postcondition asserts.... But we do want that, since things will // generally be quite broken when that happens. NS_POSTCONDITION(aDocument == GetUncomposedDoc(), "Bound to wrong document"); NS_POSTCONDITION(aParent == GetParent(), "Bound to wrong parent"); NS_POSTCONDITION(aBindingParent == GetBindingParent(), "Bound to wrong binding parent"); @@ -3917,8 +3905,17 @@ Element::UpdateIntersectionObservation(D for (auto& reg : *observers) { if (reg.observer == aObserver && reg.previousThreshold != aThreshold) { reg.previousThreshold = aThreshold; return true; } } return false; } + +void +Element::ClearServoData() { +#ifdef MOZ_STYLO + Servo_Element_ClearData(this); +#else + MOZ_CRASH("Accessing servo node data in non-stylo build"); +#endif +}
--- a/dom/base/Element.h +++ b/dom/base/Element.h @@ -11,16 +11,17 @@ */ #ifndef mozilla_dom_Element_h__ #define mozilla_dom_Element_h__ #include "mozilla/dom/FragmentOrElement.h" // for base class #include "nsChangeHint.h" // for enum #include "mozilla/EventStates.h" // for member +#include "mozilla/ServoTypes.h" #include "mozilla/dom/DirectionalityUtils.h" #include "nsIDOMElement.h" #include "nsILinkHandler.h" #include "nsINodeList.h" #include "nsNodeUtils.h" #include "nsAttrAndChildArray.h" #include "mozFlushType.h" #include "nsDOMAttributeMap.h" @@ -159,16 +160,24 @@ public: explicit Element(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) : FragmentOrElement(aNodeInfo), mState(NS_EVENT_STATE_MOZ_READONLY) { MOZ_ASSERT(mNodeInfo->NodeType() == nsIDOMNode::ELEMENT_NODE, "Bad NodeType in aNodeInfo"); SetIsElement(); } + + ~Element() + { +#ifdef MOZ_STYLO + NS_ASSERTION(!HasServoData(), "expected ServoData to be cleared earlier"); +#endif + } + #endif // MOZILLA_INTERNAL_API NS_DECLARE_STATIC_IID_ACCESSOR(NS_ELEMENT_IID) NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override; /** * Method to get the full state of this element. See mozilla/EventStates.h @@ -386,16 +395,50 @@ public: // false inline bool HasDirAuto() const { return (!HasFixedDir() && (HasValidDir() || IsHTMLElement(nsGkAtoms::bdi))); } Directionality GetComputedDirectionality() const; + inline Element* GetFlattenedTreeParentElement() const; + + bool HasDirtyDescendantsForServo() const + { + MOZ_ASSERT(IsStyledByServo()); + return HasFlag(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO); + } + + void SetHasDirtyDescendantsForServo() { + MOZ_ASSERT(IsStyledByServo()); + SetFlags(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO); + } + + void UnsetHasDirtyDescendantsForServo() { + MOZ_ASSERT(IsStyledByServo()); + UnsetFlags(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO); + } + + inline void NoteDirtyDescendantsForServo(); + +#ifdef DEBUG + inline bool DirtyDescendantsBitIsPropagatedForServo(); +#endif + + bool HasServoData() { +#ifdef MOZ_STYLO + return !!mServoData.Get(); +#else + MOZ_CRASH("Accessing servo node data in non-stylo build"); +#endif + } + + void ClearServoData(); + protected: /** * Method to get the _intrinsic_ content state of this element. This is the * state that is independent of the element's presentation. To get the full * content state, use State(). See mozilla/EventStates.h for * the possible bits that could be set here. */ virtual EventStates IntrinsicState() const; @@ -1386,16 +1429,20 @@ private: */ nsRect GetClientAreaRect(); nsIScrollableFrame* GetScrollFrame(nsIFrame **aStyledFrame = nullptr, bool aFlushLayout = true); // Data members EventStates mState; +#ifdef MOZ_STYLO + // Per-node data managed by Servo. + mozilla::ServoCell<ServoNodeData*> mServoData; +#endif }; class RemoveFromBindingManagerRunnable : public mozilla::Runnable { public: RemoveFromBindingManagerRunnable(nsBindingManager* aManager, nsIContent* aContent, nsIDocument* aDoc); @@ -1483,17 +1530,17 @@ inline mozilla::dom::Element* nsINode::A inline const mozilla::dom::Element* nsINode::AsElement() const { MOZ_ASSERT(IsElement()); return static_cast<const mozilla::dom::Element*>(this); } inline void nsINode::UnsetRestyleFlagsIfGecko() { - if (IsElement() && !IsStyledByServo()) { + if (IsElement() && !AsElement()->IsStyledByServo()) { UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS); } } /** * Macros to implement Clone(). _elementName is the class for which to implement * Clone. */
--- a/dom/base/ElementInlines.h +++ b/dom/base/ElementInlines.h @@ -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/. */ #ifndef mozilla_dom_ElementInlines_h #define mozilla_dom_ElementInlines_h #include "mozilla/dom/Element.h" +#include "nsIContentInlines.h" #include "nsIDocument.h" namespace mozilla { namespace dom { inline void Element::RegisterActivityObserver() { @@ -20,12 +21,52 @@ Element::RegisterActivityObserver() } inline void Element::UnregisterActivityObserver() { OwnerDoc()->UnregisterActivityObserver(this); } +inline Element* +Element::GetFlattenedTreeParentElement() const +{ + nsINode* parentNode = GetFlattenedTreeParentNode(); + if MOZ_LIKELY(parentNode && parentNode->IsElement()) { + return parentNode->AsElement(); + } + + return nullptr; +} + +inline void +Element::NoteDirtyDescendantsForServo() +{ + Element* curr = this; + while (curr && !curr->HasDirtyDescendantsForServo()) { + curr->SetHasDirtyDescendantsForServo(); + curr = curr->GetFlattenedTreeParentElement(); + } + + MOZ_ASSERT(DirtyDescendantsBitIsPropagatedForServo()); +} + +#ifdef DEBUG +inline bool +Element::DirtyDescendantsBitIsPropagatedForServo() +{ + Element* curr = this; + while (curr) { + if (!curr->HasDirtyDescendantsForServo()) { + return false; + } + nsINode* parentNode = curr->GetParentNode(); + curr = curr->GetFlattenedTreeParentElement(); + MOZ_ASSERT_IF(!curr, parentNode == OwnerDoc()); + } + return true; +} +#endif + } // namespace dom } // namespace mozilla #endif // mozilla_dom_ElementInlines_h
--- a/dom/base/FragmentOrElement.cpp +++ b/dom/base/FragmentOrElement.cpp @@ -2339,56 +2339,8 @@ FragmentOrElement::SetIsElementInStyleSc NS_ASSERTION(IsElement(), "calling SetIsElementInStyleScopeFlagOnShadowTree " "on a non-Element is useless"); ShadowRoot* shadowRoot = GetShadowRoot(); while (shadowRoot) { shadowRoot->SetIsElementInStyleScopeFlagOnSubtree(aInStyleScope); shadowRoot = shadowRoot->GetOlderShadowRoot(); } } - -#ifdef DEBUG -static void -AssertDirtyDescendantsBitPropagated(nsINode* aNode) -{ - MOZ_ASSERT(aNode->HasDirtyDescendantsForServo()); - nsINode* parent = aNode->GetFlattenedTreeParentNode(); - if (!parent->IsContent()) { - MOZ_ASSERT(parent == aNode->OwnerDoc()); - MOZ_ASSERT(parent->HasDirtyDescendantsForServo()); - } else { - AssertDirtyDescendantsBitPropagated(parent); - } -} -#else -static void AssertDirtyDescendantsBitPropagated(nsINode* aNode) {} -#endif - -void -nsIContent::MarkAncestorsAsHavingDirtyDescendantsForServo() -{ - MOZ_ASSERT(IsInComposedDoc()); - - // Get the parent in the flattened tree. - nsINode* parent = GetFlattenedTreeParentNode(); - - // Loop until we hit a base case. - while (true) { - - // Base case: the document. - if (!parent->IsContent()) { - MOZ_ASSERT(parent == OwnerDoc()); - parent->SetHasDirtyDescendantsForServo(); - return; - } - - // Base case: the parent is already marked, and therefore - // so are all its ancestors. - if (parent->HasDirtyDescendantsForServo()) { - AssertDirtyDescendantsBitPropagated(parent); - return; - } - - // Mark the parent and iterate. - parent->SetHasDirtyDescendantsForServo(); - parent = parent->GetFlattenedTreeParentNode(); - } -}
--- a/dom/base/nsGenericDOMDataNode.cpp +++ b/dom/base/nsGenericDOMDataNode.cpp @@ -547,28 +547,16 @@ nsGenericDOMDataNode::BindToTree(nsIDocu nsNodeUtils::ParentChainChanged(this); if (!hadParent && IsRootOfNativeAnonymousSubtree()) { nsNodeUtils::NativeAnonymousChildListChange(this, false); } UpdateEditableState(false); - // It would be cleanest to mark nodes as dirty when (a) they're created and - // (b) they're unbound from a tree. However, we can't easily do (a) right now, - // because IsStyledByServo() is not always easy to check at node creation time, - // and the bits have different meaning in the non-IsStyledByServo case. - // - // So for now, we just mark nodes as dirty when they're inserted into a - // document or shadow tree. - if (IsStyledByServo() && IsInComposedDoc()) { - MOZ_ASSERT(!HasServoData()); - SetIsDirtyForServo(); - } - NS_POSTCONDITION(aDocument == GetUncomposedDoc(), "Bound to wrong document"); NS_POSTCONDITION(aParent == GetParent(), "Bound to wrong parent"); NS_POSTCONDITION(aBindingParent == GetBindingParent(), "Bound to wrong binding parent"); return NS_OK; } @@ -590,26 +578,16 @@ nsGenericDOMDataNode::UnbindFromTree(boo NS_RELEASE(mParent); } else { mParent = nullptr; } SetParentIsContent(false); } ClearInDocument(); - // Computed styled data isn't useful for detached nodes, and we'll need to - // recomputed it anyway if we ever insert the nodes back into a document. - if (IsStyledByServo()) { - ClearServoData(); - } else { -#ifdef MOZ_STYLO - MOZ_ASSERT(!HasServoData()); -#endif - } - if (aNullParent || !mParent->IsInShadowTree()) { UnsetFlags(NODE_IS_IN_SHADOW_TREE); // Begin keeping track of our subtree root. SetSubtreeRootPointer(aNullParent ? this : mParent->SubtreeRoot()); } if (document && !GetContainingShadow()) {
--- a/dom/base/nsIContent.h +++ b/dom/base/nsIContent.h @@ -933,24 +933,16 @@ public: /** * If the content is a part of HTML editor, this returns editing * host content. When the content is in designMode, this returns its body * element. Also, when the content isn't editable, this returns null. */ mozilla::dom::Element* GetEditingHost(); - - /** - * Set NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO all the way up the flattened - * parent chain to the document. If an ancestor is found with the bit already - * set, this method asserts that all of its ancestors also have the bit set. - */ - void MarkAncestorsAsHavingDirtyDescendantsForServo(); - /** * Determining language. Look at the nearest ancestor element that has a lang * attribute in the XML namespace or is an HTML/SVG element and has a lang in * no namespace attribute. Returns false if no language was specified. */ bool GetLang(nsAString& aResult) const { for (const nsIContent* content = this; content; content = content->GetParent()) { if (content->GetAttrCount() > 0) {
--- a/dom/base/nsINode.cpp +++ b/dom/base/nsINode.cpp @@ -147,20 +147,16 @@ nsINode::nsSlots::Unlink() } //---------------------------------------------------------------------- nsINode::~nsINode() { MOZ_ASSERT(!HasSlots(), "nsNodeUtils::LastRelease was not called?"); MOZ_ASSERT(mSubtreeRoot == this, "Didn't restore state properly?"); -#ifdef MOZ_STYLO - NS_ASSERTION(!HasServoData(), "expected ServoNodeData to be cleared earlier"); - ClearServoData(); -#endif } void* nsINode::GetProperty(uint16_t aCategory, nsIAtom *aPropertyName, nsresult *aStatus) const { return OwnerDoc()->PropertyTable(aCategory)->GetProperty(this, aPropertyName, aStatus); @@ -1395,25 +1391,16 @@ nsINode::UnoptimizableCCNode() const NODE_IS_IN_SHADOW_TREE); return HasFlag(problematicFlags) || NodeType() == nsIDOMNode::ATTRIBUTE_NODE || // For strange cases like xbl:content/xbl:children (IsElement() && AsElement()->IsInNamespace(kNameSpaceID_XBL)); } -void -nsINode::ClearServoData() { -#ifdef MOZ_STYLO - Servo_Node_ClearNodeData(this); -#else - MOZ_CRASH("Accessing servo node data in non-stylo build"); -#endif -} - /* static */ bool nsINode::Traverse(nsINode *tmp, nsCycleCollectionTraversalCallback &cb) { if (MOZ_LIKELY(!cb.WantAllTraces())) { nsIDocument *currentDoc = tmp->GetUncomposedDoc(); if (currentDoc && nsCCUncollectableMarker::InGeneration(currentDoc->GetMarkedCCGeneration())) {
--- a/dom/base/nsINode.h +++ b/dom/base/nsINode.h @@ -3,17 +3,16 @@ /* 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 nsINode_h___ #define nsINode_h___ #include "mozilla/Likely.h" -#include "mozilla/ServoTypes.h" #include "mozilla/UniquePtr.h" #include "nsCOMPtr.h" // for member, local #include "nsGkAtoms.h" // for nsGkAtoms::baseURIProperty #include "nsIDOMNode.h" #include "mozilla/dom/NodeInfo.h" // member (in nsCOMPtr) #include "nsIVariant.h" // for use in GetUserData() #include "nsNodeInfoManager.h" // for use in NodePrincipal() #include "nsPropertyTable.h" // for typedefs @@ -181,24 +180,23 @@ enum { NODE_CHROME_ONLY_ACCESS = NODE_FLAG_BIT(19), NODE_IS_ROOT_OF_CHROME_ONLY_ACCESS = NODE_FLAG_BIT(20), // These two bits are shared by Gecko's and Servo's restyle systems for // different purposes. They should not be accessed directly, and access to // them should be properly guarded by asserts. + // + // FIXME(bholley): These should move to Element, and we only need one now. NODE_SHARED_RESTYLE_BIT_1 = NODE_FLAG_BIT(21), NODE_SHARED_RESTYLE_BIT_2 = NODE_FLAG_BIT(22), - // Whether this node is dirty for Servo's style system. - NODE_IS_DIRTY_FOR_SERVO = NODE_SHARED_RESTYLE_BIT_1, - // Whether this node has dirty descendants for Servo's style system. - NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO = NODE_SHARED_RESTYLE_BIT_2, + NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO = NODE_SHARED_RESTYLE_BIT_1, // Remaining bits are node type specific. NODE_TYPE_SPECIFIC_BITS_OFFSET = 23 }; // Make sure we have space for our bits #define ASSERT_NODE_FLAGS_SPACE(n) \ static_assert(WRAPPER_CACHE_FLAGS_BITS_USED + (n) <= \ @@ -976,58 +974,16 @@ public: * style system. */ #ifdef MOZ_STYLO bool IsStyledByServo() const; #else bool IsStyledByServo() const { return false; } #endif - bool IsDirtyForServo() const - { - MOZ_ASSERT(IsStyledByServo()); - return HasFlag(NODE_IS_DIRTY_FOR_SERVO); - } - - bool HasDirtyDescendantsForServo() const - { - MOZ_ASSERT(IsStyledByServo()); - return HasFlag(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO); - } - - void SetIsDirtyForServo() { - MOZ_ASSERT(IsStyledByServo()); - SetFlags(NODE_IS_DIRTY_FOR_SERVO); - } - - void SetHasDirtyDescendantsForServo() { - MOZ_ASSERT(IsStyledByServo()); - SetFlags(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO); - } - - void SetIsDirtyAndHasDirtyDescendantsForServo() { - MOZ_ASSERT(IsStyledByServo()); - SetFlags(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO | NODE_IS_DIRTY_FOR_SERVO); - } - - void UnsetIsDirtyForServo() { - MOZ_ASSERT(IsStyledByServo()); - UnsetFlags(NODE_IS_DIRTY_FOR_SERVO); - } - - void UnsetHasDirtyDescendantsForServo() { - MOZ_ASSERT(IsStyledByServo()); - UnsetFlags(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO); - } - - void UnsetIsDirtyAndHasDirtyDescendantsForServo() { - MOZ_ASSERT(IsStyledByServo()); - UnsetFlags(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO | NODE_IS_DIRTY_FOR_SERVO); - } - inline void UnsetRestyleFlagsIfGecko(); /** * Adds a mutation observer to be notified when this node, or any of its * descendants, are modified. The node will hold a weak reference to the * observer, which means that it is the responsibility of the observer to * remove itself in case it dies before the node. If an observer is added * while observers are being notified, it may also be notified. In general, @@ -2056,26 +2012,16 @@ public: void SetOn##name_(mozilla::dom::EventHandlerNonNull* listener); #define TOUCH_EVENT EVENT #define DOCUMENT_ONLY_EVENT EVENT #include "mozilla/EventNameList.h" #undef DOCUMENT_ONLY_EVENT #undef TOUCH_EVENT #undef EVENT - bool HasServoData() { -#ifdef MOZ_STYLO - return !!mServoData.Get(); -#else - MOZ_CRASH("Accessing servo node data in non-stylo build"); -#endif - } - - void ClearServoData(); - protected: static bool Traverse(nsINode *tmp, nsCycleCollectionTraversalCallback &cb); static void Unlink(nsINode *tmp); RefPtr<mozilla::dom::NodeInfo> mNodeInfo; // mParent is an owning ref most of the time, except for the case of document // nodes, so it cannot be represented by nsCOMPtr, so mark is as @@ -2103,21 +2049,16 @@ protected: // Pointer to the root of our subtree. Might be null. // This reference is non-owning and safe, since it either points to the // object itself, or is reset by ClearSubtreeRootPointer. nsINode* MOZ_NON_OWNING_REF mSubtreeRoot; }; // Storage for more members that are usually not needed; allocated lazily. nsSlots* mSlots; - -#ifdef MOZ_STYLO - // Per-node data managed by Servo. - mozilla::ServoCell<ServoNodeData*> mServoData; -#endif }; inline nsIDOMNode* GetAsDOMNode(nsINode* aNode) { return aNode ? aNode->AsDOMNode() : nullptr; } // Useful inline function for getting a node given an nsIContent and an
--- a/dom/html/nsGenericHTMLElement.cpp +++ b/dom/html/nsGenericHTMLElement.cpp @@ -2937,21 +2937,24 @@ IsOrHasAncestorWithDisplayNone(Element* } elementsToCheck.AppendElement(e); } if (elementsToCheck.IsEmpty()) { return false; } + // XXXbholley: This could be done more directly with Servo's style system. StyleSetHandle styleSet = aPresShell->StyleSet(); RefPtr<nsStyleContext> sc; for (int32_t i = elementsToCheck.Length() - 1; i >= 0; --i) { if (sc) { - sc = styleSet->ResolveStyleFor(elementsToCheck[i], sc); + sc = styleSet->ResolveStyleFor(elementsToCheck[i], sc, + ConsumeStyleBehavior::DontConsume, + LazyComputeBehavior::Assert); } else { sc = nsComputedDOMStyle::GetStyleContextForElementNoFlush(elementsToCheck[i], nullptr, aPresShell); } if (sc->StyleDisplay()->mDisplay == StyleDisplay::None) { return true; } }
--- a/dom/workers/test/sharedWorker_sharedWorker.js +++ b/dom/workers/test/sharedWorker_sharedWorker.js @@ -67,20 +67,19 @@ onconnect = function(event) { } if (!(event.ports[0] == event.source)) { throw new Error("'connect' event source property is incorrect!"); } if (event.data) { throw new Error("'connect' event has data: " + event.data); } - // The expression closures should trigger a warning in debug builds, but NOT - // fire error events at us. If we ever actually remove expression closures - // (in bug 1083458), we'll need something else to test this case. - (function() "Expected console warning: expression closures are deprecated"); + // Statement after return should trigger a warning, but NOT fire error events + // at us. + (function() { return; 1; }); event.ports[0].onmessage = function(event) { if (!(event instanceof MessageEvent)) { throw new Error("'message' event is not a MessageEvent!"); } if (!("ports" in event)) { throw new Error("'message' event doesn't have a 'ports' property!"); }
--- a/dom/workers/test/test_sharedWorker.html +++ b/dom/workers/test/test_sharedWorker.html @@ -17,17 +17,17 @@ <script class="testbody"> "use strict"; const href = window.location.href; const filename = "sharedWorker_sharedWorker.js"; const sentMessage = "ping"; const errorFilename = href.substring(0, href.lastIndexOf("/") + 1) + filename; - const errorLine = 91; + const errorLine = 90; const errorColumn = 0; var worker = new SharedWorker(filename); ok(worker instanceof SharedWorker, "Got SharedWorker instance"); ok(!("postMessage" in worker), "SharedWorker has no 'postMessage'"); ok(worker.port instanceof MessagePort, "Shared worker has MessagePort");
--- a/dom/xbl/nsXBLBinding.cpp +++ b/dom/xbl/nsXBLBinding.cpp @@ -421,17 +421,16 @@ nsXBLBinding::GenerateAnonymousContent() mContent->UnsetAttr(namespaceID, name, false); } // Now that we've finished shuffling the tree around, go ahead and restyle it // since frame construction is about to happen. nsIPresShell* presShell = mBoundElement->OwnerDoc()->GetShell(); ServoStyleSet* servoSet = presShell->StyleSet()->GetAsServo(); if (servoSet) { - mBoundElement->SetHasDirtyDescendantsForServo(); servoSet->StyleNewChildren(mBoundElement); } } nsIURI* nsXBLBinding::GetSourceDocURI() { nsIContent* targetContent =
--- a/image/ImageCacheKey.cpp +++ b/image/ImageCacheKey.cpp @@ -136,31 +136,18 @@ ImageCacheKey::ComputeHash(ImageURL* aUR { // Since we frequently call Hash() several times in a row on the same // ImageCacheKey, as an optimization we compute our hash once and store it. nsPrintfCString ptr("%p", aControlledDocument); nsAutoCString suffix; aAttrs.CreateSuffix(suffix); - if (aBlobSerial) { - // For blob URIs, we hash the serial number of the underlying blob, so that - // different blob URIs which point to the same blob share a cache entry. We - // also include the ref portion of the URI to support media fragments which - // requires us to create different Image objects even if the source data is - // the same. - nsAutoCString ref; - aURI->GetRef(ref); - return HashGeneric(*aBlobSerial, HashString(ref + suffix + ptr)); - } - - // For non-blob URIs, we hash the URI spec. - nsAutoCString spec; - aURI->GetSpec(spec); - return HashString(spec + suffix + ptr); + return AddToHash(0, aURI->ComputeHash(aBlobSerial), + HashString(suffix), HashString(ptr)); } /* static */ void* ImageCacheKey::GetControlledDocumentToken(nsIDocument* aDocument) { // For non-controlled documents, we just return null. For controlled // documents, we cast the pointer into a void* to avoid dereferencing // it (since we only use it for comparisons), and return it.
--- a/image/ImageURL.h +++ b/image/ImageURL.h @@ -4,20 +4,24 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_image_ImageURL_h #define mozilla_image_ImageURL_h #include "nsIURI.h" #include "MainThreadUtils.h" #include "nsNetUtil.h" +#include "mozilla/HashFunctions.h" +#include "nsHashKeys.h" namespace mozilla { namespace image { +class ImageCacheKey; + /** ImageURL * * nsStandardURL is not threadsafe, so this class is created to hold only the * necessary URL data required for image loading and decoding. * * Note: Although several APIs have the same or similar prototypes as those * found in nsIURI/nsStandardURL, the class does not implement nsIURI. This is * intentional; functionality is limited, and is only useful for imagelib code. @@ -108,16 +112,32 @@ public: } bool HasSameRef(const ImageURL& aOther) const { return mRef == aOther.mRef; } private: + friend class ImageCacheKey; + + uint32_t ComputeHash(const Maybe<uint64_t>& aBlobSerial) const + { + if (aBlobSerial) { + // For blob URIs, we hash the serial number of the underlying blob, so that + // different blob URIs which point to the same blob share a cache entry. We + // also include the ref portion of the URI to support media fragments which + // requires us to create different Image objects even if the source data is + // the same. + return HashGeneric(*aBlobSerial, HashString(mRef)); + } + // For non-blob URIs, we hash the URI spec. + return HashString(mSpec); + } + // Since this is a basic storage class, no duplication of spec parsing is // included in the functionality. Instead, the class depends upon the // parsing implementation in the nsIURI class used in object construction. // This means each field is stored separately, but since only a few are // required, this small memory tradeoff for threadsafe usage should be ok. nsAutoCString mSpec; nsAutoCString mScheme; nsAutoCString mRef;
--- a/js/src/jit-test/tests/SIMD/bug1273483.js +++ b/js/src/jit-test/tests/SIMD/bug1273483.js @@ -1,9 +1,9 @@ if (typeof SIMD === 'undefined') quit(); Int8x16 = SIMD.Int8x16; var Int32x4 = SIMD.Int32x4; -function testSwizzleForType(type) type(); +function testSwizzleForType(type) { return type(); } testSwizzleForType(Int8x16); -function testSwizzleInt32x4() testSwizzleForType(Int32x4); +function testSwizzleInt32x4() { return testSwizzleForType(Int32x4); } testSwizzleInt32x4();
--- a/js/src/jit-test/tests/TypedObject/jit-write-references-2.js +++ b/js/src/jit-test/tests/TypedObject/jit-write-references-2.js @@ -5,12 +5,13 @@ try { gczeal(4) } catch (exc) {} var T = TypedObject; var ValueStruct = new T.StructType({ f: T.Any }) var v = new ValueStruct; new class get extends Number {}; -function writeValue(o, v) - o.f = v +function writeValue(o, v) { + return o.f = v; +} for (var i = 0; i < 5; i++) writeValue(v, {}, "helo")
--- a/js/src/jit-test/tests/arguments/defaults-destructuring-expression-closure.js +++ b/js/src/jit-test/tests/arguments/defaults-destructuring-expression-closure.js @@ -1,14 +1,14 @@ -function f1(a, bIs, cIs, dIs, {b}={b: 3}, c=4, [d]=[5]) ( +function f1(a, bIs, cIs, dIs, {b}={b: 3}, c=4, [d]=[5]) { assertEq(a, 1), assertEq(b, bIs), assertEq(c, cIs), assertEq(d, dIs) -); +} assertEq(f1.length, 4); f1(1, 3, 4, 5); f1(1, 42, 43, 44, {b: 42}, 43, [44]); let f2 = (a, bIs, cIs, dIs, {b}={b: 3}, c=4, [d]=[5]) => ( assertEq(a, 1), assertEq(b, bIs), assertEq(c, cIs),
--- a/js/src/jit-test/tests/arguments/destructuring-exprbody.js +++ b/js/src/jit-test/tests/arguments/destructuring-exprbody.js @@ -1,8 +1,8 @@ // See bug 763313 load(libdir + "iteration.js"); -function f([a]) a +function f([a]) { return a; } var i = 0; var o = {[Symbol.iterator]: function () { i++; return { next: function () { i++; return {value: 42, done: false}; }}}}; assertEq(f(o), 42); assertEq(i, 2);
--- a/js/src/jit-test/tests/asm.js/testCall.js +++ b/js/src/jit-test/tests/asm.js/testCall.js @@ -53,17 +53,17 @@ assertEq(asmLink(asmCompile(USE_ASM+"fun assertEq(asmLink(asmCompile(USE_ASM+"function e() { return 42 } function f(i) { i=i|0; switch(i|0) { case 0: return e()|0; default: return 13 } return 0 } function g() { return f(0)|0 } return g"))(), 42); var rec = asmLink(asmCompile(USE_ASM+"function rec() { rec() } return rec")); assertThrowsInstanceOf(rec, InternalError); var rec = asmLink(asmCompile(USE_ASM+"function rec(i) { i=i|0; if (!i) return 0; return ((rec((i-1)|0)|0)+1)|0 } return rec")); assertEq(rec(100), 100); assertEq(rec(1000), 1000); -assertThrowsInstanceOf(function() rec(100000000000), InternalError); +assertThrowsInstanceOf(() => rec(100000000000), InternalError); assertEq(rec(2000), 2000); assertAsmTypeFail(USE_ASM+"function f(){return 0} function g() { var i=0; i=f() } return g"); assertAsmTypeFail(USE_ASM+"function f(){return 0.0} function g() { var i=0.0; i=f() } return g"); assertAsmTypeFail(USE_ASM+"function f(){return 0} function g() { return (f()+1)|0 } return g"); assertAsmTypeFail(USE_ASM+"function f(){return 0.0} function g() { return +(f()+1.0) } return g"); assertEq(asmLink(asmCompile(USE_ASM + "const M = 42; function f() { return M } function g() { return f()|0 } return f"))(), 42);
--- a/js/src/jit-test/tests/auto-regress/bug1264823.js +++ b/js/src/jit-test/tests/auto-regress/bug1264823.js @@ -1,11 +1,13 @@ if (!('oomTest' in this)) quit(); loadFile(""); loadFile(""); loadFile(` function lalala() {} new Map([[1, 2]]).forEach(lalala) `); -function loadFile(lfVarx) oomTest(function() { - eval(lfVarx) -}) +function loadFile(lfVarx) { + return oomTest(function() { + eval(lfVarx) + }); +}
--- a/js/src/jit-test/tests/auto-regress/bug1317460.js +++ b/js/src/jit-test/tests/auto-regress/bug1317460.js @@ -1,11 +1,11 @@ // |jit-test| error:TypeError g = newGlobal(); g.parent = this; g.eval("(" + function() { - Debugger(parent).onExceptionUnwind = function() 0; + Debugger(parent).onExceptionUnwind = function() { return 0; }; } + ")()"); async function f() { t; } f();
--- a/js/src/jit-test/tests/auto-regress/bug488034.js +++ b/js/src/jit-test/tests/auto-regress/bug488034.js @@ -1,7 +1,7 @@ // Binary: cache/js-dbg-64-e1257570fef8-linux // Flags: // (function(){ var x; - eval("var x; ((function ()x)())"); + eval("var x; ((function () { return x; })())"); })()
--- a/js/src/jit-test/tests/auto-regress/bug488421.js +++ b/js/src/jit-test/tests/auto-regress/bug488421.js @@ -1,12 +1,12 @@ // Binary: cache/js-dbg-64-862693caa320-linux // Flags: // function f(foo) { var x; - eval("this.__defineGetter__(\"y\", function ()x)"); + eval("this.__defineGetter__(\"y\", function () { return x; })"); } f(""); try { ((function(){ throw y })()) } catch(exc1) {}
--- a/js/src/jit-test/tests/auto-regress/bug490191.js +++ b/js/src/jit-test/tests/auto-regress/bug490191.js @@ -1,15 +1,15 @@ // Binary: cache/js-dbg-64-daefd30072a6-linux // Flags: // function f(param) { var w; return eval("\ (function(){\ - this.__defineGetter__(\"y\", function()({\ + this.__defineGetter__(\"y\", function() { return ({\ x: function(){ return w }()\ - }))\ + }); })\ });\ "); } (f())(); (new Function("eval(\"y\")"))();
--- a/js/src/jit-test/tests/auto-regress/bug563034.js +++ b/js/src/jit-test/tests/auto-regress/bug563034.js @@ -1,11 +1,11 @@ // Binary: cache/js-dbg-64-c9212eb6175b-linux // Flags: // function f(a) { function g() { - yield function () a; + yield function () { return a; }; } return g(); } var x; f(7).next()();
--- a/js/src/jit-test/tests/auto-regress/bug630770.js +++ b/js/src/jit-test/tests/auto-regress/bug630770.js @@ -1,5 +1,5 @@ // Binary: cache/js-dbg-64-3b3710520c0e-linux // Flags: // options('strict') -Function("function y(x,x)d") +Function("function y(x,x) { return d; }")
--- a/js/src/jit-test/tests/auto-regress/bug650658.js +++ b/js/src/jit-test/tests/auto-regress/bug650658.js @@ -1,9 +1,10 @@ // |jit-test| error:TypeError // Binary: cache/js-dbg-64-ac0989a03bf1-linux // Flags: -m -n -a // AddRegExpCases(/a*b/, "xxx", 0, null ); AddRegExpCases(/x\d\dy/, "abcx45ysss235", 3,[] ); -function AddRegExpCases(regexp, pattern, index, matches_array ) - (matches_array.length, regexp) +function AddRegExpCases(regexp, pattern, index, matches_array ) { + return (matches_array.length, regexp); +}
--- a/js/src/jit-test/tests/auto-regress/bug659779.js +++ b/js/src/jit-test/tests/auto-regress/bug659779.js @@ -2,18 +2,18 @@ // Flags: -m -n -a // var gTestcases = new Array; function TestCase(n, d, e, a) { this.description=d gTestcases[gTc++]=this } -TestCase.prototype.dump=function () + toPrinted(this.description) -function toPrinted(value) value=value; +TestCase.prototype.dump=function () { return + toPrinted(this.description); }; +function toPrinted(value) { return value=value; } function reportCompare (expected, actual, description) { new TestCase("unknown-test-name", description, expected, actual) } function enterFunc (funcName) { try { expectCompile = 'No Error' var actualCompile; reportCompare(expectCompile, actualCompile, ': compile actual')
--- a/js/src/jit-test/tests/auto-regress/bug730806.js +++ b/js/src/jit-test/tests/auto-regress/bug730806.js @@ -1,15 +1,16 @@ // Binary: cache/js-opt-32-2dc40eb83023-linux // Flags: -m -n -a // -function toPrinted(value) - value = value.replace(/\\n/g, 'NL') - .replace(/\\r/g, 'CR') - .replace(/[^\x20-\x7E]+/g, escapeString); +function toPrinted(value) { + return value = value.replace(/\\n/g, 'NL') + .replace(/\\r/g, 'CR') + .replace(/[^\x20-\x7E]+/g, escapeString); +} function escapeString (str) { var a, b, c, d; var len = str.length; var result = ""; var digits = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"]; for (var i=0; i<len; i++)
--- a/js/src/jit-test/tests/auto-regress/bug756236.js +++ b/js/src/jit-test/tests/auto-regress/bug756236.js @@ -1,14 +1,15 @@ // Binary: cache/js-dbg-64-14735b4dbccc-linux // Flags: --ion-eager // gczeal(4); function startTest() {} -function TestCase(n, d, e, a) - dump = (function () {}); +function TestCase(n, d, e, a) { + return dump = (function () {}); +} if (typeof document != "object" || !document.location.href.match(/jsreftest.html/)) {} function writeHeaderToLog( string ) {} var SECTION = "11.4.5"; new TestCase( SECTION, "var MYVAR= void 0; --MYVAR", NaN, eval("var MYVAR=void 0; --MYVAR") ); new TestCase( SECTION, "var MYVAR=0;--MYVAR;MYVAR", -1, eval("var MYVAR=0;--MYVAR;MYVAR") ); new TestCase( SECTION, "var MYVAR=0;--MYVAR;MYVAR", -1, eval("var MYVAR=0;--MYVAR;MYVAR") );
--- a/js/src/jit-test/tests/auto-regress/bug778557.js +++ b/js/src/jit-test/tests/auto-regress/bug778557.js @@ -1,7 +1,7 @@ // Binary: cache/js-dbg-64-90828ac18dcf-linux // Flags: // x = Set; -eval("function y()(Iterator)", this); +eval("function y() { return Iterator; }", this); x.__iterator__ = y; new Iterator(x)
--- a/js/src/jit-test/tests/baseline/bug1216140.js +++ b/js/src/jit-test/tests/baseline/bug1216140.js @@ -1,6 +1,6 @@ -function newFunc(x) Function(x)() +function newFunc(x) { return Function(x)(); } newFunc(` var BUGNUMBER = 8[ anonymous = true ]--; () => BUGNUMBER; `);
--- a/js/src/jit-test/tests/baseline/bug1238815.js +++ b/js/src/jit-test/tests/baseline/bug1238815.js @@ -1,13 +1,15 @@ // This program crashes the ARM code generator because the machine code is // longer than the 32MB range of ARM branch instructions. // // Baseline should not attempt to compile the script. i = 1; -function test(s) eval("line0 = Error.lineNumber\ndebugger\n" + s); +function test(s) { + return eval("line0 = Error.lineNumber\ndebugger\n" + s); +} function repeat(s) { return Array(65 << 13).join(s) } long_expr = repeat(" + i") long_throw_stmt = long_expr; test(long_throw_stmt);
--- a/js/src/jit-test/tests/baseline/bug852801.js +++ b/js/src/jit-test/tests/baseline/bug852801.js @@ -9,65 +9,68 @@ TestCase.prototype.dump = function () {} TestCase.prototype.testPassed = (function TestCase_testPassed() { return this.passed; }); TestCase.prototype.testFailed = (function TestCase_testFailed() { return !this.passed; }); function printStatus (msg) { var lines = msg.split ("\n"); for (var i=0; i<lines.length; i++) print (STATUS + lines[i]); } function printBugNumber (num) {} -function toPrinted(value) -function escapeString (str) {} +function toPrinted(value) { + return function escapeString (str) {}; +} function reportCompare (expected, actual, description) { var actual_t = typeof actual; var output = ""; printStatus ( "Expected value '" + toPrinted(expected) + toPrinted(actual) ); var testcase = new TestCase("unknown-test-name", description, expected, actual); testcase.reason = output; if (typeof document != "object" || !document.location.href.match(/jsreftest.html/)) { if (testcase.passed) { } } return testcase.passed; } function reportMatch (expectedRegExp, actual, description) {} -function enterFunc (funcName) -function BigO(data) { - function LinearRegression(data) { } +function enterFunc (funcName) { + return function BigO(data) { + function LinearRegression(data) { } + }; } function compareSource(expect, actual, summary) {} function optionsInit() { var optionNames = options().split(','); } function optionsClear() {} function optionsPush() {} optionsInit(); optionsClear(); -function getTestCaseResult(expected, actual) -function test() { - for ( gTc=0; gTc < gTestcases.length; gTc++ ) {} +function getTestCaseResult(expected, actual) { + return function test() { + for ( gTc=0; gTc < gTestcases.length; gTc++ ) {} + }; } var lfcode = new Array(); lfcode.push("4"); lfcode.push("gcparam(\"maxBytes\", gcparam(\"gcBytes\") + 1024);"); lfcode.push(""); lfcode.push("\ var UBound = 0;\n\ var BUGNUMBER = 74474;\n\ var actual = '';\n\ var actualvalues = [ ];\n\ var expectedvalues = [ ];\n\ addThis();\n\ addThis();\n\ tryThis(1);\n\ function tryThis(x)\n\ -addThis();\n\ +{ return addThis(); }\n\ test();\n\ function addThis() {\n\ actualvalues[UBound] = actual;\n\ UBound++;\n\ }\n\ function test() {\n\ enterFunc ('test');\n\ printBugNumber(BUGNUMBER);\n\
--- a/js/src/jit-test/tests/basic/bug1135718.js +++ b/js/src/jit-test/tests/basic/bug1135718.js @@ -1,12 +1,13 @@ setJitCompilerOption("ion.warmup.trigger", 30); -function ArrayCallback(state) - this.state = state; +function ArrayCallback(state) { + return this.state = state; +} ArrayCallback.prototype.isUpperCase = function(v, index, array) { return this.state ? true : (v == v.toUpperCase()); }; strings = ['hello', 'Array', 'WORLD']; obj = new ArrayCallback(false); strings.filter(obj.isUpperCase, obj) obj = new ArrayCallback(true); strings.filter(obj.isUpperCase, obj)
--- a/js/src/jit-test/tests/basic/bug560234b.js +++ b/js/src/jit-test/tests/basic/bug560234b.js @@ -1,11 +1,11 @@ function f(a) { function g() { - yield function () a; + yield function () { return a; }; } if (a == 8) return g(); a = 3; } var x; for (var i = 0; i < 9; i++) x = f(i);
--- a/js/src/jit-test/tests/basic/bug650148.js +++ b/js/src/jit-test/tests/basic/bug650148.js @@ -3,11 +3,12 @@ summary=/(?!AB+D)AB/.exec("AB") + ''; try { var s = "throw 42"; } catch (e) {} test(); function test() { [ {0xBe: /l/|| 'Error' ? s++ : summary } ] } -function foo(code) - Function(code)(); +function foo(code) { + return Function(code)(); +} foo("for each (y in this);");
--- a/js/src/jit-test/tests/basic/bug651966.js +++ b/js/src/jit-test/tests/basic/bug651966.js @@ -21,17 +21,17 @@ f(); f(); try { f("function x(){}(x())"); } catch (e) {} function f2() { a = { x } = x, (x._) function - x()({}) + x(){ return ({}); } } try { f2(); } catch (e) {} function f3() { var x = 0; with ({}) { x = 'three'; } return x; }
--- a/js/src/jit-test/tests/basic/bug657225.js +++ b/js/src/jit-test/tests/basic/bug657225.js @@ -1,9 +1,9 @@ -function reportCompare(expected, actual, description) + ++actual + "'"; +function reportCompare(expected, actual, description) { return + ++actual + "'"; } var summary = 'Object.prototype.toLocaleString() should track Object.prototype.toString() '; var o = { toString: function () {} }; expect = o; actual = o.toLocaleString(); reportCompare(expect, actual, summary);
--- a/js/src/jit-test/tests/basic/bug673766.js +++ b/js/src/jit-test/tests/basic/bug673766.js @@ -8,13 +8,13 @@ function reportCompare(expected, actual, var actual = ''; var expect = ''; for (var i = 0; i < 2; ++i) reportCompare(expect, actual, ': 2'); try { ({ valueOf: gc } - []) } catch (prop) {} -function addThis() reportCompare(expect, actual, 'ok'); +function addThis() { return reportCompare(expect, actual, 'ok'); } Object.defineProperty(Object.prototype, "name", { set: function (newValue) {} }); addThis()
--- a/js/src/jit-test/tests/basic/bug709634.js +++ b/js/src/jit-test/tests/basic/bug709634.js @@ -1,6 +1,6 @@ -Function.prototype.toString = function () f(this, true); +Function.prototype.toString = function () { return f(this, true); }; function f(obj) { f.caller.p } decodeURI + 3;
--- a/js/src/jit-test/tests/basic/bug728086.js +++ b/js/src/jit-test/tests/basic/bug728086.js @@ -8,18 +8,19 @@ function toPrinted(value) { function escapeString (str) {} function reportCompare (expected, actual, description) { var expected_t = typeof expected; var actual_t = typeof actual; var output = ""; var testcase = new TestCase("unknown-test-name", description, expected, actual); reportFailure (description + " : " + output); } -function enterFunc (funcName) - callStack.push(funcName); +function enterFunc (funcName) { + return callStack.push(funcName); +} try { reportCompare(expectCompile, actualCompile, summary + ': compile actual'); } catch(ex) { } var lfcode = new Array(); lfcode.push("gczeal(2);\ enterFunc ('test');\ ");
--- a/js/src/jit-test/tests/basic/bug738841.js +++ b/js/src/jit-test/tests/basic/bug738841.js @@ -35,15 +35,15 @@ try { }()) } catch (e) {} try { m } catch (e) {} try { var f = function() { { - print(new function(q)("", s)) + print(new function(q) { return ("", s); }) let u } }; dis(f); f(); } catch (e) {}
--- a/js/src/jit-test/tests/basic/bug744285.js +++ b/js/src/jit-test/tests/basic/bug744285.js @@ -42,17 +42,17 @@ function check(b, desc) { } function isCloneable(pair) { } function assertIsCloneOf(a, b, path) { ca = classOf(a) assertEq(ca, classOf(b), path) assertEq(Object.getPrototypeOf(a), ca == "[object Object]" ? Object.prototype : Array.prototype, path) pb = ownProperties(b).filter(isCloneable) pa = ownProperties(a) - function byName(a, b) 0 + function byName(a, b) { return 0; } byName (pa.length, pb.length, "should see the same number of properties " + path) for (var i = 0; i < pa.length; i++) { gczeal(4) } } banner = desc || uneval() a = deserialize(serialize(b))
--- a/js/src/jit-test/tests/basic/bug808067.js +++ b/js/src/jit-test/tests/basic/bug808067.js @@ -1,10 +1,11 @@ -function TestCase(n, d, e, a) - this.reason = ''; +function TestCase(n, d, e, a) { + return this.reason = ''; +} function reportCompare (expected, actual, description) { var output = ""; var testcase = new TestCase("unknown-test-name", description, expected, actual); testcase.reason = output; } gcPreserveCode(); var summary = 'return with argument and lazy generator detection'; expect = "generator function foo returns a value";
--- a/js/src/jit-test/tests/basic/bug832203.js +++ b/js/src/jit-test/tests/basic/bug832203.js @@ -1,11 +1,11 @@ // Don't assert. gczeal(2,1); eval("(function() { " + "\ var g1 = newGlobal('same-compartment');\ function test(str, f) {\ var x = f(eval(str));\ assertEq(x, f(g1.eval(str)));\ }\ -test('new RegExp(\"1\")', function(r) assertEq('a1'.search(r), 1));\ +test('new RegExp(\"1\")', function(r) { return assertEq('a1'.search(r), 1); });\ " + " })();"); eval("(function() { " + "" + " })();");
--- a/js/src/jit-test/tests/basic/bug951213.js +++ b/js/src/jit-test/tests/basic/bug951213.js @@ -1,8 +1,8 @@ enableShellAllocationMetadataBuilder(); function foo(x, y) { this.g = x + y; } var a = 0; -var b = { valueOf: function() Object.defineProperty(Object.prototype, 'g', {}) }; +var b = { valueOf: function() { return Object.defineProperty(Object.prototype, 'g', {}); } }; var c = new foo(a, b);
--- a/js/src/jit-test/tests/basic/decompile-script.js +++ b/js/src/jit-test/tests/basic/decompile-script.js @@ -1,6 +1,6 @@ function example(a, b, c) { var complicated = 3; perform_some_operations(); } -var myfun = function (a, b) a + b; +var myfun = function (a, b) { return a + b; } assertEq(decompileThis(), snarf(thisFilename()));
--- a/js/src/jit-test/tests/basic/function-tosource-getset.js +++ b/js/src/jit-test/tests/basic/function-tosource-getset.js @@ -1,7 +1,7 @@ -var o = {get prop() a + b, set prop(x) a + b}; +var o = {get prop() { a + b; }, set prop(x) { a + b; }}; var prop = Object.getOwnPropertyDescriptor(o, "prop"); -assertEq(prop.get.toString(), "function get prop() a + b"); -assertEq(prop.get.toSource(), "(function get prop() a + b)"); -assertEq(prop.set.toString(), "function set prop(x) a + b"); -assertEq(prop.set.toSource(), "(function set prop(x) a + b)"); -assertEq(o.toSource(), "({get prop () a + b, set prop (x) a + b})"); +assertEq(prop.get.toString(), "function get prop() { a + b; }"); +assertEq(prop.get.toSource(), "(function get prop() { a + b; })"); +assertEq(prop.set.toString(), "function set prop(x) { a + b; }"); +assertEq(prop.set.toSource(), "(function set prop(x) { a + b; })"); +assertEq(o.toSource(), "({get prop () { a + b; }, set prop (x) { a + b; }})");
--- a/js/src/jit-test/tests/basic/shapelessCalleeTest.js +++ b/js/src/jit-test/tests/basic/shapelessCalleeTest.js @@ -38,27 +38,27 @@ function shapelessUnknownCalleeLoop(n, f g = h; } } function shapelessCalleeTest() { var a = []; - var helper = function (i, a) a[i] = i; - shapelessArgCalleeLoop(helper, helper, function (i, a) a[i] = -i, a); + var helper = function (i, a) { a[i] = i; }; + shapelessArgCalleeLoop(helper, helper, function (i, a) { a[i] = -i; }, a); - helper = function (i, a) a[10 + i] = i; - shapelessVarCalleeLoop(helper, helper, function (i, a) a[10 + i] = -i, a); + helper = function (i, a) { a[10 + i] = i; }; + shapelessVarCalleeLoop(helper, helper, function (i, a) { a[10 + i] = -i; }, a); - helper = function (i, a) a[20 + i] = i; - shapelessLetCalleeLoop(helper, helper, function (i, a) a[20 + i] = -i, a); + helper = function (i, a) { a[20 + i] = i; }; + shapelessLetCalleeLoop(helper, helper, function (i, a) { a[20 + i] = -i; }, a); - helper = function (i, a) a[30 + i] = i; - shapelessUnknownCalleeLoop(null, helper, helper, function (i, a) a[30 + i] = -i, a); + helper = function (i, a) { a[30 + i] = i; }; + shapelessUnknownCalleeLoop(null, helper, helper, function (i, a) { a[30 + i] = -i; }, a); try { helper = {hack: 42}; shapelessUnknownCalleeLoop(null, helper, helper, helper, a); } catch (e) { if (e + "" != "TypeError: f is not a function") print("shapelessUnknownCalleeLoop: unexpected exception " + e); }
--- a/js/src/jit-test/tests/basic/spread-call-funapply.js +++ b/js/src/jit-test/tests/basic/spread-call-funapply.js @@ -48,41 +48,41 @@ function checkNormal(f) { assertEqArray(f.apply(null, ...[[]]), [undefined, undefined, undefined]); assertEqArray(f.apply(null, ...[[1]]), [1, undefined, undefined]); assertEqArray(f.apply(null, ...[[1, 2]]), [1, 2, undefined]); assertEqArray(f.apply(null, ...[[1, 2, 3, 4]]), [1, 2, 3]); assertEqArray(f.apply(null, ...[[undefined]]), [undefined, undefined, undefined]); } -checkNormal(function(a, b, c) [a, b, c]); +checkNormal(function(a, b, c) { return [a, b, c]; }); checkNormal((a, b, c) => [a, b, c]); function checkDefault(f) { checkCommon(f); assertEqArray(f.apply(null, ...[[]]), [-1, -2, -3]); assertEqArray(f.apply(null, ...[[1]]), [1, -2, -3]); assertEqArray(f.apply(null, ...[[1, 2]]), [1, 2, -3]); assertEqArray(f.apply(null, ...[[1, 2, 3, 4]]), [1, 2, 3]); assertEqArray(f.apply(null, ...[[undefined]]), [-1, -2, -3]); } -checkDefault(function(a = -1, b = -2, c = -3) [a, b, c]); +checkDefault(function(a = -1, b = -2, c = -3) { return [a, b, c]; }); checkDefault((a = -1, b = -2, c = -3) => [a, b, c]); function checkRest(f) { checkCommon(f); assertEqArray(f.apply(null, ...[[]]), []); assertEqArray(f.apply(null, ...[[1]]), [1]); assertEqArray(f.apply(null, ...[[1, 2]]), [1, 2]); assertEqArray(f.apply(null, ...[[1, 2, 3, 4]]), [1, 2, 3, 4]); assertEqArray(f.apply(null, ...[[undefined]]), [undefined]); // other iterable objects assertEqArray(f.apply(null, ...new Map([[["a", "A"], ["b", "B"]]])).map(([k, v]) => k + v), ["aA", "bB"]); } -checkRest(function(...x) x); +checkRest(function(...x) { return x; }); checkRest((...x) => x);
--- a/js/src/jit-test/tests/basic/spread-call-funcall.js +++ b/js/src/jit-test/tests/basic/spread-call-funcall.js @@ -2,10 +2,10 @@ load(libdir + "eqArrayHelper.js"); function check(f) { assertEqArray(f.call(...[null], 1, 2, 3), [1, 2, 3]); assertEqArray(f.call(...[null], 1, ...[2, 3], 4, ...[5, 6]), [1, 2, 3, 4, 5, 6]); assertEqArray(f.call(...[null, 1], ...[2, 3], 4, ...[5, 6]), [1, 2, 3, 4, 5, 6]); assertEqArray(f.call(...[null, 1, ...[2, 3], 4, ...[5, 6]]), [1, 2, 3, 4, 5, 6]); } -check(function(...x) x); +check(function(...x) { return x; }); check((...x) => x);
--- a/js/src/jit-test/tests/basic/spread-call-length.js +++ b/js/src/jit-test/tests/basic/spread-call-length.js @@ -39,14 +39,14 @@ function checkLength(f, makeFn) { assertEq(makeFn("...arg")(f, itr), 3); function* gen() { for (let i = 1; i < 4; i ++) yield i; } assertEq(makeFn("...arg")(f, gen()), 3); } -checkLength(function(x) arguments.length, makeCall); -checkLength(function(x) arguments.length, makeFunCall); +checkLength(function(x) { return arguments.length; }, makeCall); +checkLength(function(x) { return arguments.length; }, makeFunCall); function lengthClass(x) { this.length = arguments.length; } checkLength(lengthClass, makeNew);
--- a/js/src/jit-test/tests/basic/spread-call-not-iterable.js +++ b/js/src/jit-test/tests/basic/spread-call-not-iterable.js @@ -9,20 +9,20 @@ assertThrowsInstanceOf(() => Math.sin(.. assertThrowsInstanceOf(() => Math.sin(...(x => x)), TypeError); assertThrowsInstanceOf(() => Math.sin(...1), TypeError); assertThrowsInstanceOf(() => Math.sin(...{}), TypeError); var foo = {} foo[Symbol.iterator] = 10; assertThrowsInstanceOf(() => Math.sin(...foo), TypeError); -foo[Symbol.iterator] = function() undefined; +foo[Symbol.iterator] = function() { return undefined; }; assertThrowsInstanceOf(() => Math.sin(...foo), TypeError); -foo[Symbol.iterator] = function() this; +foo[Symbol.iterator] = function() { return this; }; assertThrowsInstanceOf(() => Math.sin(...foo), TypeError); -foo[Symbol.iterator] = function() this; +foo[Symbol.iterator] = function() { return this; }; foo.next = function() { throw 10; }; assertThrowsValue(() => Math.sin(...foo), 10); assertThrowsInstanceOf(() => Math.sin(.../a/), TypeError); assertThrowsInstanceOf(() => Math.sin(...new Error()), TypeError);
--- a/js/src/jit-test/tests/basic/spread-call-recursion.js +++ b/js/src/jit-test/tests/basic/spread-call-recursion.js @@ -4,15 +4,15 @@ a.length = 30; function check(f) { try { f(); } catch (e) { assertEq(e.message, "too much recursion"); } } -let f = function() f(...a) + 1; +let f = function() { return f(...a) + 1; }; let g = () => g(...a) + 1; -let h = function() new h(...a) + 1; +let h = function() { return new h(...a) + 1; }; check(f); check(g); check(h);
--- a/js/src/jit-test/tests/basic/spread-call.js +++ b/js/src/jit-test/tests/basic/spread-call.js @@ -52,18 +52,18 @@ function checkNormal(f, makeFn) { assertEqArray(makeFn("...[]")(f), [undefined, undefined, undefined]); assertEqArray(makeFn("...[1]")(f), [1, undefined, undefined]); assertEqArray(makeFn("...[1, 2]")(f), [1, 2, undefined]); assertEqArray(makeFn("...[1, 2, 3, 4]")(f), [1, 2, 3]); assertEqArray(makeFn("...[undefined]")(f), [undefined, undefined, undefined]); } -checkNormal(function(a, b, c) [a, b, c], makeCall); -checkNormal(function(a, b, c) [a, b, c], makeFunCall); +checkNormal(function(a, b, c) { return [a, b, c]; }, makeCall); +checkNormal(function(a, b, c) { return [a, b, c]; }, makeFunCall); checkNormal((a, b, c) => [a, b, c], makeCall); checkNormal((a, b, c) => [a, b, c], makeFunCall); function normalClass(a, b, c) { this.value = [a, b, c]; assertEq(Object.getPrototypeOf(this), normalClass.prototype); } checkNormal(normalClass, makeNew); @@ -73,18 +73,18 @@ function checkDefault(f, makeFn) { assertEqArray(makeFn("...[]")(f), [-1, -2, -3]); assertEqArray(makeFn("...[1]")(f), [1, -2, -3]); assertEqArray(makeFn("...[1, 2]")(f), [1, 2, -3]); assertEqArray(makeFn("...[1, 2, 3, 4]")(f), [1, 2, 3]); assertEqArray(makeFn("...[undefined]")(f), [-1, -2, -3]); } -checkDefault(function(a = -1, b = -2, c = -3) [a, b, c], makeCall); -checkDefault(function(a = -1, b = -2, c = -3) [a, b, c], makeFunCall); +checkDefault(function(a = -1, b = -2, c = -3) { return [a, b, c]; }, makeCall); +checkDefault(function(a = -1, b = -2, c = -3) { return [a, b, c]; }, makeFunCall); checkDefault((a = -1, b = -2, c = -3) => [a, b, c], makeCall); checkDefault((a = -1, b = -2, c = -3) => [a, b, c], makeFunCall); function defaultClass(a = -1, b = -2, c = -3) { this.value = [a, b, c]; assertEq(Object.getPrototypeOf(this), defaultClass.prototype); } checkDefault(defaultClass, makeNew); @@ -94,17 +94,17 @@ function checkRest(f, makeFn) { assertEqArray(makeFn("...[]")(f), []); assertEqArray(makeFn("1, ...[2, 3, 4], 5")(f), [1, 2, 3, 4, 5]); assertEqArray(makeFn("1, ...[], 2")(f), [1, 2]); assertEqArray(makeFn("1, ...[2, 3], 4, ...[5, 6]")(f), [1, 2, 3, 4, 5, 6]); assertEqArray(makeFn("...[undefined]")(f), [undefined]); } -checkRest(function(...x) x, makeCall); -checkRest(function(...x) x, makeFunCall); +checkRest(function(...x) { return x; }, makeCall); +checkRest(function(...x) { return x; }, makeFunCall); checkRest((...x) => x, makeCall); checkRest((...x) => x, makeFunCall); function restClass(...x) { this.value = x; assertEq(Object.getPrototypeOf(this), restClass.prototype); } checkRest(restClass, makeNew);
--- a/js/src/jit-test/tests/basic/testBrandedVsGeneric.js +++ b/js/src/jit-test/tests/basic/testBrandedVsGeneric.js @@ -1,17 +1,17 @@ const C = function (a, b, c) { return function C() { - this.m1 = function () a; - this.m2 = function () b; - this.m3 = function () c; + this.m1 = function () { return a; }; + this.m2 = function () { return b; }; + this.m3 = function () { return c; }; } }(2,3,4); var c = new C(); -var d = function (e) {return {m0: function () e}}(5); +var d = function (e) {return {m0: function () { return e; }}}(5); for (var i = 0; i < 5; i++) d.m0(); C.call(d); d.__iterator__ = function() {yield 55}; for (i = 0; i < 5; i++) { for (j in d) print(j); }
--- a/js/src/jit-test/tests/basic/testBug740442.js +++ b/js/src/jit-test/tests/basic/testBug740442.js @@ -1,10 +1,11 @@ function g1() {} -function g2() -function Int8Array () {} +function g2() { + return function Int8Array () {}; +} function f1(other) { eval("gc(); h = g1"); for(var i=0; i<20; i++) { i = i.name; } } f1(g2);
--- a/js/src/jit-test/tests/basic/testBug783441.js +++ b/js/src/jit-test/tests/basic/testBug783441.js @@ -1,1 +1,1 @@ -assertEq((function(x, y, x) { return (function() x+y)(); })(1,2,5), 7); +assertEq((function(x, y, x) { return (function() { return x+y; })(); })(1,2,5), 7);
--- a/js/src/jit-test/tests/basic/testCallProtoMethod.js +++ b/js/src/jit-test/tests/basic/testCallProtoMethod.js @@ -1,14 +1,14 @@ function testCallProtoMethod() { function X() { this.x = 1; } X.prototype.getName = function () { return "X"; } function Y() { this.x = 2; } - Y.prototype.getName = function() "Y"; + Y.prototype.getName = function() { return "Y"; }; var a = [new X, new X, new X, new X, new Y]; var s = ''; for (var i = 0; i < a.length; i++) s += a[i].getName(); return s; } assertEq(testCallProtoMethod(), 'XXXXY');
--- a/js/src/jit-test/tests/basic/testDynamicUsage.js +++ b/js/src/jit-test/tests/basic/testDynamicUsage.js @@ -1,17 +1,17 @@ assertEq((function() { var x = 3; return (function() { return x } )() })(), 3); assertEq((function() { var g = function() { return x }; var x = 3; return g()})(), 3); assertEq((function() { var x; x = 3; return (function() { return x } )() })(), 3); assertEq((function() { x = 3; var x; return (function() { return x } )() })(), 3); assertEq((function() { var x; var g = function() { return x }; x = 3; return g() })(), 3); -assertEq((function() { function f() { return 3 }; assertEq(f(), 3); return (function() f())() })(), 3); +assertEq((function() { function f() { return 3 }; assertEq(f(), 3); return (function() { return f(); })(); })(), 3); assertEq((function() { function f() { return 3 }; assertEq(f(), 3); return eval('f()') })(), 3); -assertEq((function() { function f() { return 3 }; (function() f())(); return f() })(), 3); +assertEq((function() { function f() { return 3 }; (function() { return f(); })(); return f() })(), 3); assertEq((function() { var x = 3; return eval("x") })(), 3); assertEq((function() { var x; x = 3; return eval("x") })(), 3); assertEq((function() { x = 3; var x; return eval("x") })(), 3); assertEq((function() { var x; (function() {x = 2})(); return ++x })(), 3); assertEq((function() { var x; (function() {x = 2})(); x++; return x })(), 3); assertEq((function() { var x; (function() {x = 4})(); return --x })(), 3); @@ -21,60 +21,60 @@ assertEq((function(x) { return (function assertEq((function(x) { var x = 3; return (function() { return x } )() })(4), 3); assertEq((function(x) { x = 3; return (function() { return x } )() })(4), 3); assertEq((function(x) { var g = function() { return x }; x = 3; return g()})(3), 3); assertEq((function(x) { return eval("x") })(3), 3); assertEq((function(x) { x = 3; return eval("x") })(4), 3); assertEq((function(a) { var [x,y] = a; (function() { x += y })(); return x })([1,2]), 3); -assertEq((function(a) { var [x,y] = a; x += y; return (function() x)() })([1,2]), 3); -assertEq((function(a) { var [[l, x],[m, y]] = a; x += y; return (function() x)() })([[0,1],[0,2]]), 3); +assertEq((function(a) { var [x,y] = a; x += y; return (function() { return x; })() })([1,2]), 3); +assertEq((function(a) { var [[l, x],[m, y]] = a; x += y; return (function() { return x; })() })([[0,1],[0,2]]), 3); assertEq((function(a) { var [x,y] = a; eval('x += y'); return x })([1,2]), 3); assertEq((function(a) { var [x,y] = a; x += y; return eval('x') })([1,2]), 3); assertEq((function(a) { var [x,y] = a; (function() { x += y })(); return x })([1,2]), 3); -assertEq((function(a) { var [x,y] = a; x += y; return (function() x)() })([1,2]), 3); +assertEq((function(a) { var [x,y] = a; x += y; return (function() { return x; })() })([1,2]), 3); assertEq((function(a,x,y) { [x,y] = a; (function() { eval('x += y') })(); return x })([1,2]), 3); -assertEq((function(a,x,y) { [x,y] = a; x += y; return (function() eval('x'))() })([1,2]), 3); +assertEq((function(a,x,y) { [x,y] = a; x += y; return (function() { return eval('x'); })() })([1,2]), 3); -assertEq((function() { var [x,y] = [1,2]; x += y; return (function() x)() })(), 3); -assertEq((function() { var [x,y] = [1,2]; (function() x += y)(); return x })(), 3); -assertEq((function() { { let [x,y] = [1,2]; x += y; return (function() x)() } })(), 3); -assertEq((function() { { let [x,y] = [1,2]; (function() x += y)(); return x } })(), 3); +assertEq((function() { var [x,y] = [1,2]; x += y; return (function() { return x; })() })(), 3); +assertEq((function() { var [x,y] = [1,2]; (function() { return x += y; })(); return x })(), 3); +assertEq((function() { { let [x,y] = [1,2]; x += y; return (function() { return x; })() } })(), 3); +assertEq((function() { { let [x,y] = [1,2]; (function() { return x += y; })(); return x } })(), 3); assertEq((function([x,y]) { (function() { x += y })(); return x })([1,2]), 3); -assertEq((function([x,y]) { x += y; return (function() x)() })([1,2]), 3); +assertEq((function([x,y]) { x += y; return (function() { return x; })() })([1,2]), 3); assertEq((function([[l,x],[m,y]]) { (function() { x += y })(); return x })([[0,1],[0,2]]), 3); -assertEq((function([[l,x],[m,y]]) { x += y; return (function() x)() })([[0,1],[0,2]]), 3); +assertEq((function([[l,x],[m,y]]) { x += y; return (function() { return x; })() })([[0,1],[0,2]]), 3); assertEq((function([x,y]) { (function() { eval('x += y') })(); return x })([1,2]), 3); -assertEq((function([x,y]) { x += y; return (function() eval('x'))() })([1,2]), 3); +assertEq((function([x,y]) { x += y; return (function() { return eval('x'); })() })([1,2]), 3); assertEq((function() { try { throw [1,2] } catch([x,y]) { eval('x += y'); return x }})(), 3); assertEq((function() { try { throw [1,2] } catch([x,y]) { x += y; return eval('x') }})(), 3); assertEq((function() { try { throw [1,2] } catch([x,y]) { (function() { x += y })(); return x }})(), 3); -assertEq((function() { try { throw [1,2] } catch([x,y]) { x += y; return (function() x)() }})(), 3); +assertEq((function() { try { throw [1,2] } catch([x,y]) { x += y; return (function() { return x; })() }})(), 3); assertEq((function() { try { throw [1,2] } catch([x,y]) { (function() { eval('x += y') })(); return x }})(), 3); -assertEq((function() { try { throw [1,2] } catch([x,y]) { x += y; return (function() eval('x'))() }})(), 3); +assertEq((function() { try { throw [1,2] } catch([x,y]) { x += y; return (function() { return eval('x'); })() }})(), 3); assertEq((function(a) { let [x,y] = a; (function() { x += y })(); return x })([1,2]), 3); -assertEq((function(a) { let [x,y] = a; x += y; return (function() x)() })([1,2]), 3); +assertEq((function(a) { let [x,y] = a; x += y; return (function() { return x; })() })([1,2]), 3); assertEq((function(a) { { let [x,y] = a; (function() { x += y })(); return x } })([1,2]), 3); -assertEq((function(a) { { let [x,y] = a; x += y; return (function() x)() } })([1,2]), 3); +assertEq((function(a) { { let [x,y] = a; x += y; return (function() { return x; })() } })([1,2]), 3); assertEq((function(a) { { let [[l, x],[m, y]] = a; (function() { x += y })(); return x } })([[0,1],[0,2]]), 3); -assertEq((function(a) { { let [[l, x],[m, y]] = a; x += y; return (function() x)() } })([[0,1],[0,2]]), 3); +assertEq((function(a) { { let [[l, x],[m, y]] = a; x += y; return (function() { return x; })() } })([[0,1],[0,2]]), 3); assertEq((function() { let x = 3; return (function() { return x })() })(), 3); assertEq((function() { let g = function() { return x }; let x = 3; return g() })(), 3); assertEq((function() { { let x = 3; return (function() { return x })() } })(), 3); assertEq((function() { { let x = 3; (function() { assertEq(x, 3) })(); return x } })(), 3); assertEq((function() { { let x = 2; x = 3; return (function() { return x })() } })(), 3); assertEq((function() { { let x = 1; { let x = 3; (function() { assertEq(x,3) })() } return x } })(), 1); assertEq((function() { try { throw 3 } catch (e) { (function(){assertEq(e,3)})(); return e } })(), 3); -assertEq((function() { try { throw 3 } catch (e) { assertEq(e, 3); return (function() e)() } })(), 3); +assertEq((function() { try { throw 3 } catch (e) { assertEq(e, 3); return (function() { return e; })() } })(), 3); assertEq((function() { try { throw 3 } catch (e) { (function(){eval('assertEq(e,3)')})(); return e } })(), 3); assertEq((function() { var x; function f() { return x } function f() { return 3 }; return f() })(), 3); assertEq((function() { var x = 3; function f() { return 3 } function f() { return x }; return f() })(), 3); assertEq((function() { function f(x,y) { (function() { assertEq(f.length, 2) })(); }; return f.length })(), 2); assertEq((function f(x,y) { (function() { assertEq(f.length, 2) })(); return f.length})(), 2); function f1(x,y) { (function() { assertEq(f1.length, 2) })(); assertEq(f1.length, 2) }; f1();
--- a/js/src/jit-test/tests/debug/Debugger-findScripts-12.js +++ b/js/src/jit-test/tests/debug/Debugger-findScripts-12.js @@ -15,18 +15,19 @@ g2.load(url1); g2.load(url2); var g3 = newGlobal(); var dbg = new Debugger(g1, g2, g3); function script(func) { var gw = dbg.addDebuggee(func.global); var script = gw.makeDebuggeeValue(func).script; - script.toString = function () - "[Debugger.Script for " + func.name + " in " + uneval(func.global) + "]"; + script.toString = function () { + return "[Debugger.Script for " + func.name + " in " + uneval(func.global) + "]"; + }; return script; } // The function scripts we know of. There may be random eval scripts involved, but // we don't care about those. var allScripts = ([g1.f, g1.f(), g1.g, g1.h, g1.h(), g1.i, g2.f, g2.f(), g2.g, g2.h, g2.h(), g2.i].map(script));
--- a/js/src/jit-test/tests/debug/bug-1240090.js +++ b/js/src/jit-test/tests/debug/bug-1240090.js @@ -1,12 +1,12 @@ gczeal(2); g = newGlobal(); dbg = Debugger(g); -dbg.onNewScript = function() function() this; +dbg.onNewScript = function() { return function() { return this; } }; schedulegc(10); g.eval("setLazyParsingDisabled(true)"); g.evaluate("function one() {}"); g.evaluate(` function target () {} function two2() {} `, {}); g.evaluate(`
--- a/js/src/jit-test/tests/debug/bug1240803.js +++ b/js/src/jit-test/tests/debug/bug1240803.js @@ -10,15 +10,15 @@ if (!('oomAfterAllocations' in this)) g.toggle = function(d) { if (d) { dbg.addDebuggee(g); dbg.getNewestFrame(); oomAfterAllocations(2); setBreakpoint; } } - g.eval("" + function f(d) toggle(d)) + g.eval("" + function f(d) { return toggle(d); }) g.eval("(" + function() { f(false); f(true); } + ")()") })();
--- a/js/src/jit-test/tests/debug/bug1252453.js +++ b/js/src/jit-test/tests/debug/bug1252453.js @@ -8,14 +8,14 @@ const dbg = new Debugger; const wrappedRoot = dbg.addDebuggee(root); dbg.memory.trackingAllocationSites = 1; root.eval("(" + function immediate() { '_' << foo } + "())"); `); file = lfcode.shift(); loadFile(file); function loadFile(lfVarx) { try { - function newFunc(x) Function(x)(); + function newFunc(x) { return Function(x)(); } newFunc(lfVarx)(); } catch (lfVare) { print(lfVare) } }
--- a/js/src/jit-test/tests/debug/bug1252464.js +++ b/js/src/jit-test/tests/debug/bug1252464.js @@ -1,14 +1,14 @@ // |jit-test| error: ReferenceError g = newGlobal(); dbg = Debugger(g); hits = 0; -dbg.onNewScript = function () hits++; +dbg.onNewScript = function () { return hits++; }; assertEq(g.eval("eval('2 + 3')"), 5); this.gczeal(hits,1); dbg = Debugger(g); g.h = function () { var env = dbg.getNewestFrame().environment; dbg = 0; assertThrowsInstanceOf; }
--- a/js/src/jit-test/tests/debug/bug1275001.js +++ b/js/src/jit-test/tests/debug/bug1275001.js @@ -15,16 +15,16 @@ function check_one(expected, f, err) { } } ieval = eval function check(expr, expected = expr) { var end, err for ([end, err] of[[".random_prop", " is undefined" ]]) statement = "o = {};" + expr + end; cases = [ - function() ieval("var undef;" + statement), + function() { return ieval("var undef;" + statement); }, Function(statement) ] for (f of cases) check_one(expected, f, err) } check("undef"); check("o.b");
--- a/js/src/jit-test/tests/gc/bug-1016016.js +++ b/js/src/jit-test/tests/gc/bug-1016016.js @@ -1,12 +1,13 @@ // |jit-test| error:ReferenceError toPrinted(this.reason); -function toPrinted(value) - value = String(value); +function toPrinted(value) { + return value = String(value); +} var lfcode = new Array(); lfcode.push = loadFile; lfcode.push("enableTrackAllocations();"); lfcode.push("\ gczeal(9, 2);\ newGlobal();\ ''.addDebuggee(g1);\ ");
--- a/js/src/jit-test/tests/gc/bug-1136597.js +++ b/js/src/jit-test/tests/gc/bug-1136597.js @@ -3,18 +3,19 @@ var evalInFrame = (function (global) { var dbgGlobal = newGlobal(); var dbg = new dbgGlobal.Debugger(); return function evalInFrame(upCount, code) { dbg.addDebuggee(global); }; })(this); var gTestcases = new Array(); var gTc = gTestcases.length; -function TestCase() - gTestcases[gTc++] = this; +function TestCase() { + return gTestcases[gTc++] = this; +} function checkCollation(extensionCoValue, usageValue) { var collator = new Intl.Collator(["de-DE"]); collator.resolvedOptions().collation; } checkCollation(undefined, "sort"); checkCollation(); for ( addpow = 0; addpow < 33; addpow++ ) { new TestCase();
--- a/js/src/jit-test/tests/gc/bug-1155455.js +++ b/js/src/jit-test/tests/gc/bug-1155455.js @@ -1,17 +1,18 @@ // |jit-test| error: TypeError if (!("gczeal" in this)) quit(); var g = newGlobal(); gczeal(10, 2) var dbg = Debugger(g); dbg.onDebuggerStatement = function (frame1) { - function hit(frame2) - hit[0] = "mutated"; + function hit(frame2) { + return hit[0] = "mutated"; + } var s = frame1.script; var offs = s.getLineOffsets(g.line0 + 2); for (var i = 0; i < offs.length; i++) s.setBreakpoint(offs[i], {hit: hit}); return; }; var lfGlobal = newGlobal(); g.eval("var line0 = Error().lineNumber;\n debugger;\nx = 1;\n");
--- a/js/src/jit-test/tests/gc/bug-1252103.js +++ b/js/src/jit-test/tests/gc/bug-1252103.js @@ -10,13 +10,13 @@ function foo() { TO = TypedObject; PointType = new TO.StructType({ y: TO.float64, name: TO.string }) LineType = new TO.StructType({ PointType }) - function testBasic() new LineType; + function testBasic() { return new LineType; } testBasic(); } evaluate("foo()");
--- a/js/src/jit-test/tests/gc/bug-1261329.js +++ b/js/src/jit-test/tests/gc/bug-1261329.js @@ -1,10 +1,10 @@ if (!('oomTest' in this)) quit(); print = function() {} -function k() dissrc(print); -function j() k(); -function h() j(); -function f() h(); +function k() { return dissrc(print); } +function j() { return k(); } +function h() { return j(); } +function f() { return h(); } f(); oomTest(() => f())
--- a/js/src/jit-test/tests/gc/bug-1263871.js +++ b/js/src/jit-test/tests/gc/bug-1263871.js @@ -1,8 +1,9 @@ if (!('oomTest' in this)) quit(); lfLogBuffer = `this[''] = function() {}`; loadFile(lfLogBuffer); loadFile(lfLogBuffer); -function loadFile(lfVarx) - oomTest(function() parseModule(lfVarx)) +function loadFile(lfVarx) { + return oomTest(function() { return parseModule(lfVarx); }); +}
--- a/js/src/jit-test/tests/gc/bug-1305220.js +++ b/js/src/jit-test/tests/gc/bug-1305220.js @@ -10,14 +10,14 @@ evalcx("\ ", s); gczeal(0); evalcx("\ var g = newGlobal();\ b = new Debugger;\ g.h = function() {\ g.oomAfterAllocations(1);\ };\ - g.eval(\"\" + function f() g());\ - g.eval(\"\" + function g() h());\ + g.eval(\"\" + function f() { return g(); });\ + g.eval(\"\" + function g() { return h(); });\ g.eval(\"(\" + function() {\ f();\ } + \")()\");\ ", s);
--- a/js/src/jit-test/tests/gc/bug-1310589.js +++ b/js/src/jit-test/tests/gc/bug-1310589.js @@ -7,17 +7,17 @@ e2 = Set v2 = b2 = new ArrayBuffer t2 = new Uint8ClampedArray minorgc() x = /x/ for (var i = 0; i < 4; ++i) { function f1() {} } Object.defineProperty(a, 12, {}).push(1); -toString = (function() a.reverse()) +toString = (function() { return a.reverse(); }) oomTest(Date.prototype.toJSON) function f1000(){} function f1001(){} function f1002(){} function f1003(){} function f1004(){} function f1005(){} function f1006(){}
--- a/js/src/jit-test/tests/gc/bug-906243.js +++ b/js/src/jit-test/tests/gc/bug-906243.js @@ -1,12 +1,12 @@ // |jit-test| need-for-each a2 = [] -g = function() r +g = function() { return r; }; Object.defineProperty(a2, 0, { set: function() {} }) for (var x = 0; x < 70; ++x) { Array.prototype.unshift.call(a2, g) } a2.length = 8 for each(e in [0, 0]) {
--- a/js/src/jit-test/tests/gc/bug-945275.js +++ b/js/src/jit-test/tests/gc/bug-945275.js @@ -1,11 +1,11 @@ function TestCase(n) { this.name = undefined; this.description = undefined; } gczeal(7,1); eval("\ -function reportCompare() new TestCase;\ +function reportCompare() { return new TestCase; };\ reportCompare();\ Object.defineProperty(Object.prototype, 'name', {});\ reportCompare();\ ");
--- a/js/src/jit-test/tests/gc/bug-957114.js +++ b/js/src/jit-test/tests/gc/bug-957114.js @@ -2,12 +2,12 @@ gczeal(7,1); function TestCase(n) { this.name = ''; this.description = ''; this.expect = ''; this.actual = ''; this.reason = ''; this.passed = ''; } -function test() new TestCase; +function test() { return new TestCase; } test(); Object.defineProperty(Object.prototype, "name", {}); test();
--- a/js/src/jit-test/tests/gc/bug-961877.js +++ b/js/src/jit-test/tests/gc/bug-961877.js @@ -1,14 +1,14 @@ g = Function("", "for (var i = 0; i < 0; ++i) { eval('this.arg'+0 +'=arg'+0); }"); Math.abs(undefined); gczeal(2,300); evaluate("\ var toFloat32 = (function() {\ var f32 = new Float32Array(1);\ - function f(x) f32[0] = x;\ + function f(x) { return f32[0] = x; }\ return f;\ })();\ for (var i = 0; i < 64; ++i) {\ var p = Math.pow(2, i) + 1;\ g(toFloat32(p));\ toFloat32(-p);\ }");
--- a/js/src/jit-test/tests/gc/bug-981295.js +++ b/js/src/jit-test/tests/gc/bug-981295.js @@ -1,9 +1,9 @@ var NotEarlyErrorString = "NotEarlyError"; var NotEarlyError = new Error(NotEarlyErrorString); var juneDate = new Date(2000, 5, 20, 0, 0, 0, 0); -for (var i = 0; i < function(x) myObj(Date.prototype.toString.apply(x)); void i) { +for (var i = 0; i < function(x) { return myObj(Date.prototype.toString.apply(x)); }; void i) { eval(a.text.replace(/@/g, "")) } gcslice(2601); function testcase() {} new Uint16Array(testcase);
--- a/js/src/jit-test/tests/gc/oomInDtoa.js +++ b/js/src/jit-test/tests/gc/oomInDtoa.js @@ -1,4 +1,4 @@ if (!('oomTest' in this)) quit(); -oomTest(function() 1e300) +oomTest(function() { return 1e300; })
--- a/js/src/jit-test/tests/ion/bug1215600.js +++ b/js/src/jit-test/tests/ion/bug1215600.js @@ -7,17 +7,17 @@ for (let i = 0; i < 10; i++) { file = lfcode.shift() loadFile(file) } function loadFile(lfVarx) { try { if (lfVarx.length != 1) switch (lfRunTypeId) { case 3: - function newFunc(x) Function(x)() + function newFunc(x) { return Function(x)(); } newFunc(lfVarx) case 5: for (lfLocal in this); } isNaN(); lfRunTypeId = parseInt(lfVarx); } catch (lfVare) {} }
--- a/js/src/jit-test/tests/ion/bug1264948.js +++ b/js/src/jit-test/tests/ion/bug1264948.js @@ -5,17 +5,18 @@ loadFile(` T = TypedObject ObjectStruct = new T.StructType({f: T.Object}) var o = new ObjectStruct function testGC(p) { for (; i < 5; i++) whatever.push; } testGC(o) - function writeObject() - o.f = v + function writeObject() { + return o.f = v; + } writeObject({function() { } }) for (var i ; i < 5 ; ++i) try {} catch (StringStruct) {} `); function loadFile(lfVarx) { oomTest(Function(lfVarx)); }
--- a/js/src/jit-test/tests/ion/bug1284491.js +++ b/js/src/jit-test/tests/ion/bug1284491.js @@ -6,10 +6,10 @@ loadFile(` switch(value) { case 0:break case isNaN: break } } SwitchTest(); `) function loadFile(lfVarx) { - oomTest(function() eval(lfVarx)) + oomTest(function() { return eval(lfVarx); }) }
--- a/js/src/jit-test/tests/ion/bug1299007.js +++ b/js/src/jit-test/tests/ion/bug1299007.js @@ -11,18 +11,18 @@ evalInFrame = function(global) { } }(this); function h() { evalInFrame(0, "") evalInFrame(0, "i") evalInFrame(0, "a.push") evalInFrame(1, "a.pushy") } -function g() h() -function f() g() +function g() { return h(); } +function f() { return g(); } f() evaluate(` g() g() g() g() g() g()
--- a/js/src/jit-test/tests/ion/bug691747.js +++ b/js/src/jit-test/tests/ion/bug691747.js @@ -1,9 +1,9 @@ -function reportCompare(actual) - ++actual + "'"; +function reportCompare(actual) { return - ++actual + "'"; } var UBound = 0; var actualvalues = []; for (var li = 0; li < 6; ++li) addThis(); function addThis() { UBound++; for (var i=0; i<UBound; i++) { reportCompare(actualvalues[i]); }
--- a/js/src/jit-test/tests/ion/bug724944.js +++ b/js/src/jit-test/tests/ion/bug724944.js @@ -1,10 +1,11 @@ -function TestCase(n, d, e, a) -function writeHeaderToLog( string ) {} +function TestCase(n, d, e, a) { + return function writeHeaderToLog( string ) {}; +} var SECTION = "15.1.2.5-2"; for ( var CHARCODE = 0; CHARCODE < 256; CHARCODE += 16 ) { new TestCase( SECTION, unescape( "%" + (ToHexString(CHARCODE)).substring(0,1) ) ); } function ToHexString( n ) { var hex = new Array(); for ( var mag = 1; Math.pow(16,mag) <= n ; mag++ ) { } for ( index = 0, mag -= 1; mag > 0; index++, mag-- ) { }
--- a/js/src/jit-test/tests/ion/bug729573.js +++ b/js/src/jit-test/tests/ion/bug729573.js @@ -1,10 +1,11 @@ -function TestCase(n, d, e, a) -function writeHeaderToLog( string ) {} +function TestCase(n, d, e, a) { + return function writeHeaderToLog( string ) {}; +} var SECTION = "11.7.2"; for ( power = 0; power <= 32; power++ ) { shiftexp = Math.pow( 2, power ); for ( addexp = 0; addexp <= 32; addexp++ ) { new TestCase( SECTION, SignedRightShift( shiftexp, addexp ), shiftexp >> addexp ); } } function ToInt32BitString( n ) {
--- a/js/src/jit-test/tests/ion/bug729788.js +++ b/js/src/jit-test/tests/ion/bug729788.js @@ -3,17 +3,17 @@ function Day(t) { return Math.floor(t / msPerDay); } function YearFromTime(t) { sign = 1 year = sign < 0 } function MonthFromTime(t) { DayWithinYear(t) - function DayWithinYear(t) Day(t) - YearFromTime() + function DayWithinYear(t) { return Day(t) - YearFromTime(); } function WeekDay(t) { weekday = Day(t) + 4 return (weekday < 0 ? weekday : weekday); } time = year for (var last_sunday = time; WeekDay(last_sunday) == 0;) {} } addTestCase(0, 946684800000);
--- a/js/src/jit-test/tests/ion/bug732851.js +++ b/js/src/jit-test/tests/ion/bug732851.js @@ -1,10 +1,11 @@ var OMIT = {}; var WRITABLES = [true, false, OMIT]; { var desc = {}; - function put(field, value) - desc[field] = value; + function put(field, value) { + return desc[field] = value; + } WRITABLES.forEach(function(writable) { put("writable", writable) }); };
--- a/js/src/jit-test/tests/ion/bug732859.js +++ b/js/src/jit-test/tests/ion/bug732859.js @@ -1,10 +1,11 @@ -function TestCase(n, d, e, a) -function writeHeaderToLog( string ) {} +function TestCase(n, d, e, a) { + return function writeHeaderToLog( string ) {}; +} var SECTION = "15.1.2.4"; for ( var CHARCODE = 128; CHARCODE < 256; CHARCODE++ ) { new TestCase( SECTION, "%"+ToHexString(CHARCODE), escape(String.fromCharCode(CHARCODE))); } function ToHexString( n ) { var hex = new Array(); hex[hex.length] = n % 16; var string ="";
--- a/js/src/jit-test/tests/ion/bug756780.js +++ b/js/src/jit-test/tests/ion/bug756780.js @@ -1,8 +1,9 @@ gczeal(4); var i = (29); var status = ''; var statusmessages = new Array(); addThis(); addThis(); -function addThis() - statusmessages[i] = status; +function addThis() { + return statusmessages[i] = status; +}
--- a/js/src/jit-test/tests/ion/bug819865.js +++ b/js/src/jit-test/tests/ion/bug819865.js @@ -1,2 +1,2 @@ -(function x() (x == x))(); +(function x() { return (x == x); })();
--- a/js/src/jit-test/tests/ion/bug852140.js +++ b/js/src/jit-test/tests/ion/bug852140.js @@ -1,7 +1,8 @@ function reportCompare (expected, actual) { if (expected != actual) {} } -function exitFunc (funcName) - reportCompare(undefined, ''); +function exitFunc (funcName) { + return reportCompare(undefined, ''); +} reportCompare('', ''); exitFunc();
--- a/js/src/jit-test/tests/ion/bug852174.js +++ b/js/src/jit-test/tests/ion/bug852174.js @@ -1,9 +1,12 @@ -function eval() - isPrototypeOf[Iterator.length] -function DoWhile_3() - eval(); +function eval() { + return isPrototypeOf[Iterator.length]; +} +function DoWhile_3() { + return eval(); +} DoWhile_3(); -function f() - DoWhile_3(f - 0); +function f() { + return DoWhile_3(f - 0); +} for (var i in f());
--- a/js/src/jit-test/tests/ion/bug862100.js +++ b/js/src/jit-test/tests/ion/bug862100.js @@ -4,11 +4,11 @@ function reportCompare (expected, actual new TestCase("", description, expected, actual); } new TestCase( "", "", 0, Number(new Number()) ); reportCompare(true, true); evaluate("\ function TestCase(n, d, e, a) {}\ test_negation(-2147483648, 2147483648);\ test_negation(2147483647, -2147483647);\ -function test_negation(value, expected)\ +function test_negation(value, expected) {\ reportCompare(expected, '', '-(' + value + ') == ' + expected);\ -"); +}");
--- a/js/src/jit-test/tests/ion/bug862357.js +++ b/js/src/jit-test/tests/ion/bug862357.js @@ -1,11 +1,12 @@ // |jit-test| error: ReferenceError -function TestCase(e, a) - this.passed = (e == a); +function TestCase(e, a) { + return this.passed = (e == a); +} function reportCompare (expected, actual) { var expected_t = typeof expected; var actual_t = typeof actual; if (expected_t != actual_t) printStatus(); new TestCase(expected, actual); } var expect = '';
--- a/js/src/jit-test/tests/ion/recover-lambdas-bug1118911.js +++ b/js/src/jit-test/tests/ion/recover-lambdas-bug1118911.js @@ -1,9 +1,10 @@ function test() { - function f() - k.apply(this, arguments); + function f() { + return k.apply(this, arguments); + } if (undefined >> undefined !== 0) {} for (var [ v , c ] in this.tracemonkey) { } } try { test(); } catch(exc1) {} try { test(); } catch(exc1) {}
--- a/js/src/jit-test/tests/jaeger/bug549396.js +++ b/js/src/jit-test/tests/jaeger/bug549396.js @@ -1,1 +1,1 @@ -x = this.__defineSetter__("x", function(z) function() { z }) +x = this.__defineSetter__("x", function(z) { return function() { z }; })
--- a/js/src/jit-test/tests/jaeger/bug819035.js +++ b/js/src/jit-test/tests/jaeger/bug819035.js @@ -3,17 +3,17 @@ (function (a, u) { var sum = function (array, callback) { for (var i = 0; i < array.length; i++) callback(array[i]); }; (function () { (function r(t) { t !== u, - sum(t, function (v) r(v) ); + sum(t, function (v) { return r(v); } ); })(arguments); })(a); }) ( [ [ [1], [1], [1], [1], [1], [1], [1], [1], [1], [1], [1], [1], [1], [1], [1], [1], [1], [1], [1], [1], [1], [1], [1], [1], [1], [1], [1], [1], [1], [1],
--- a/js/src/jit-test/tests/jaeger/loops/bug659452.js +++ b/js/src/jit-test/tests/jaeger/loops/bug659452.js @@ -1,5 +1,5 @@ test(); function test() { - var t = function () function printStatus() {}; + var t = function () { return function printStatus() {}; }; for (var j = 0; j < 10; j++) t["-1"] }
--- a/js/src/jit-test/tests/jaeger/recompile/bug647199.js +++ b/js/src/jit-test/tests/jaeger/recompile/bug647199.js @@ -1,11 +1,11 @@ TryInWhile( new TryObject( "hello", ThrowException, true ) ); function TryObject( value, throwFunction, result ) { this.thrower=throwFunction } -function ThrowException() TryInWhile(1); +function ThrowException() { return TryInWhile(1); } function TryInWhile( object ) { try { object.thrower() } catch ( e ) { } }
--- a/js/src/jit-test/tests/jaeger/recompile/bug658212.js +++ b/js/src/jit-test/tests/jaeger/recompile/bug658212.js @@ -1,17 +1,20 @@ var gTestcases = Array; function TestCase(n, d, e, a) { this.description = d gTestcases[gTc] = this } -TestCase.prototype.dump=function () + + + +TestCase.prototype.dump=function () { return + + + + this.description + + - + + '\n';function printStatus (msg) -function toPrinted(value) { + + + '\n'; +}; +function printStatus (msg) { + return function toPrinted(value) { + }; } function reportCompare(expected, actual, description) { new TestCase("unknown-test-name", description, expected, actual) } gTc = 0;; function jsTestDriverEnd() { for (var i = 0; i < gTestcases.length; i++) gTestcases[i].dump()
--- a/js/src/jit-test/tests/jaeger/recompile/bug658561.js +++ b/js/src/jit-test/tests/jaeger/recompile/bug658561.js @@ -1,5 +1,5 @@ var s1 = 'xx'; for (var x = 0; x < 10 ; ++x ) { - new function() s1++; + new function() { return s1++; }; gc(); }
--- a/js/src/jit-test/tests/jaeger/recompile/bug658777.js +++ b/js/src/jit-test/tests/jaeger/recompile/bug658777.js @@ -1,9 +1,9 @@ -function Employee(name, dept) this.name = name || ""; +function Employee(name, dept) { return this.name = name || ""; } function WorkerBee(name, dept, projs) { this.base = Employee this.base(name, dept) } function Engineer(name, projs, machine) { this.base = WorkerBee this.base(name, "engineering", projs) __proto__["a" + constructor] = 1
--- a/js/src/jit-test/tests/jaeger/recompile/bug659766.js +++ b/js/src/jit-test/tests/jaeger/recompile/bug659766.js @@ -1,29 +1,29 @@ var gTestcases = new Array; var gTc = gTestcases; function TestCase(n, d, e, a) { this.description=d this.reason='' gTestcases[gTc++]=this } -TestCase.prototype.dump=function () + toPrinted(this.description) + toPrinted(this.reason) + '\n'; -function toPrinted(value) value=value.replace(/\\n/g, 'NL').replace(/[^\x20-\x7E]+/g, escapeString); +TestCase.prototype.dump=function () { return + toPrinted(this.description) + toPrinted(this.reason) + '\n'; }; +function toPrinted(value) { return value=value.replace(/\\n/g, 'NL').replace(/[^\x20-\x7E]+/g, escapeString); } function escapeString (str) { try { err } catch(ex) { } } function jsTestDriverEnd() { for (var i = 0; i < gTestcases.length; i++) gTestcases[i].dump() } var SECTION = "dowhile-007"; DoWhile(); -function DoWhile( object ) result1=false; +function DoWhile( object ) { return result1=false; } new TestCase( SECTION, "break one: ", result1 ); jsTestDriverEnd(); new TestCase( SECTION, "'�O� �:i��'.match(new RegExp('.+'))", [], '�O� �:i��'); jsTestDriverEnd();
--- a/js/src/jit-test/tests/jaeger/recompile/bug661859.js +++ b/js/src/jit-test/tests/jaeger/recompile/bug661859.js @@ -1,24 +1,24 @@ -function TestCase(n, d, e, a) this.expect = e; +function TestCase(n, d, e, a) { return this.expect = e; } function reportCompare(expected, actual, description) { typeof actual } expect = 1; var summary = 'Do not assert: top < ss->printer->script->depth'; var actual = 'No Crash'; var expect = 'No Crash'; test(); function notInlined(f) { // prevent inlining this function, as a consequence, it prevent inlining // Array.prototype.some (Bug 1087468) with ({}) {} return f; } function test(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z) { try { - p = [1].some(notInlined(function (y) test())) ? 4 : 0x0041; + p = [1].some(notInlined(function (y) { return test(); })) ? 4 : 0x0041; } catch (ex) {} reportCompare(expect, actual, summary) } test(); TestCase(); test()
--- a/js/src/jit-test/tests/modules/bug-1233915.js +++ b/js/src/jit-test/tests/modules/bug-1233915.js @@ -1,10 +1,11 @@ g = newGlobal(); g.parent = this; g.eval("(" + function() { Debugger(parent) - .onExceptionUnwind = function(frame) - frame.eval("") + .onExceptionUnwind = function(frame) { + return frame.eval(""); + }; } + ")()"); m = parseModule(` s1 `); m.declarationInstantiation(); m.evaluation();
--- a/js/src/jit-test/tests/modules/bug-1245518.js +++ b/js/src/jit-test/tests/modules/bug-1245518.js @@ -3,13 +3,13 @@ evalInFrame = function(global) { dbg = new dbgGlobal.Debugger(); return function(upCount, code) { dbg.addDebuggee(global); frame = dbg.getNewestFrame().older; frame.eval(code); } }(this); m = parseModule(` - function g() this.hours = 0; + function g() { return this.hours = 0; } evalInFrame.call(0, 0, "g()") `); m.declarationInstantiation(); m.evaluation();
--- a/js/src/jit-test/tests/parser/bug-1263355-16.js +++ b/js/src/jit-test/tests/parser/bug-1263355-16.js @@ -1,10 +1,11 @@ // |jit-test| error: ReferenceError let m = parseModule(` var i = 0; addThis(); -function addThis() - statusmessages[i] = Number; +function addThis() { + return statusmessages[i] = Number; +} `); m.declarationInstantiation(); m.evaluation();
--- a/js/src/jit-test/tests/pic/arguments.js +++ b/js/src/jit-test/tests/pic/arguments.js @@ -13,11 +13,11 @@ assertEq(f.apply(null, [1, 2, 3, 4]), 4) function g(arg) { var r; for (var i = 0; i < arg.length; i++) r = arg[i]; return r; } -assertEq(g((function () arguments).call(null, 1, 2, 3)), 3); +assertEq(g((function () { return arguments; }).call(null, 1, 2, 3)), 3); assertEq(g(new Float32Array(3)), 0.0); assertEq(g([1, 2, 3, 4]), 4);
--- a/js/src/jit-test/tests/pic/bug558099.js +++ b/js/src/jit-test/tests/pic/bug558099.js @@ -1,11 +1,11 @@ // |jit-test| need-for-each -(function()[function() function() function() function() function() function() {}]); +(function() { return [function() { return function() { return function() { return function() { return function() { return function() {}; }; }; }; }; }]; }); foo = [{ text: "(function(){if(d){(1)}})", s: function() {}, test: function() { try { f } catch(e) {} } @@ -42,19 +42,19 @@ foo = [{ }]; (function() { for (i = 0; i < foo.length; ++i) { a = foo[i] text = a.text eval(text.replace(/@/, "")); if (a.test()) {} } } ()); -s = [function() function() function() function() function() function() {}] -[function() function() function() function() {}]; -(function() { [function() function() {}] }); +s = [function() { return function() { return function() { return function() { return function() { return function() {}; }; }; }; }; }] +[function() { return function() { return function() { return function() {}; }; }; }]; +(function() { [function() { return function() {}; }] }); (function() {}); (eval("\ (function(){\ for each(d in[\ 0,0,0,0,0,0,0,0,0,0,0,0,0,null,NaN,1,Boolean(false),Boolean(false)\ ]){\ [].filter(new Function,gczeal(2))\ }\
--- a/js/src/jit-test/tests/profiler/bug1164448.js +++ b/js/src/jit-test/tests/profiler/bug1164448.js @@ -1,14 +1,15 @@ // |jit-test| error: TypeError print = function(s) { return s.toString(); } var gTestcases = new Array(); -function TestCase(n, d, e, a) - gTestcases[gTc++] = this; +function TestCase(n, d, e, a) { + return gTestcases[gTc++] = this; +} dump = print; for ( gTc=0; gTc < gTestcases.length; gTc++ ) {} function jsTestDriverEnd() { for (var i = 0; i < gTestcases.length; i++) gTestcases[i].dump(); } TestCase(); var g = newGlobal();
--- a/js/src/jit-test/tests/profiler/bug1242840.js +++ b/js/src/jit-test/tests/profiler/bug1242840.js @@ -5,12 +5,12 @@ enableSPSProfiling(); oomTest(() => { try { for (quit of ArrayBuffer); } catch (e) { switch (1) { case 0: let x case 1: - (function() x)() + (function() { return x; })() } } })
--- a/js/src/jit-test/tests/profiler/bug1261324.js +++ b/js/src/jit-test/tests/profiler/bug1261324.js @@ -13,12 +13,12 @@ try { function assertThrowsInstanceOf(f) { try { f() } catch (exc) {} } function testThrow(thunk) { for (i = 0; i < 20; i++) { iter = thunk() - assertThrowsInstanceOf(function() iter.throw()) + assertThrowsInstanceOf(function() { return iter.throw(); }) } } testThrow(function*() {})
--- a/js/src/jit-test/tests/profiler/test-bug1026485.js +++ b/js/src/jit-test/tests/profiler/test-bug1026485.js @@ -1,11 +1,12 @@ -function TestCase(n, d, e, a) - TestCase.prototype.dump = function () {} +function TestCase(n, d, e, a) { + return TestCase.prototype.dump = function () {}; +} enableSPSProfiling(); new TestCase(typeof Number(new Number())); new TestCase(typeof Number(new Number(Number.NaN))); test(); function test() { try { test(); } catch (e) {
--- a/js/src/jit/BaselineCacheIR.cpp +++ b/js/src/jit/BaselineCacheIR.cpp @@ -507,24 +507,16 @@ class MOZ_RAII BaselineCacheIRCompiler : } if (!failurePaths.append(Move(newFailure))) return false; *failure = &failurePaths.back(); return true; } - void emitEnterTypeMonitorIC() { - allocator.discardStack(masm); - EmitEnterTypeMonitorIC(masm); - } - void emitReturnFromIC() { - allocator.discardStack(masm); - EmitReturnFromIC(masm); - } }; void BaselineCacheIRCompiler::enterStubFrame(MacroAssembler& masm, Register scratch) { if (engine_ == ICStubEngine::Baseline) { EmitBaselineEnterStubFrame(masm, scratch); #ifdef DEBUG @@ -599,16 +591,18 @@ BaselineCacheIRCompiler::compile() default: MOZ_CRASH("Invalid op"); } allocator.nextOp(); } while (reader.more()); + masm.assumeUnreachable("Should have returned from IC"); + // Done emitting the main IC code. Now emit the failure paths. for (size_t i = 0; i < failurePaths.length(); i++) { emitFailurePath(i); EmitStubGuardFailure(masm); } Linker linker(masm); AutoFlushICache afc("getStubCode"); @@ -1052,31 +1046,29 @@ BaselineCacheIRCompiler::emitGuardAndLoa bool BaselineCacheIRCompiler::emitLoadFixedSlotResult() { Register obj = allocator.useRegister(masm, reader.objOperandId()); AutoScratchRegister scratch(allocator, masm); masm.load32(stubAddress(reader.stubOffset()), scratch); masm.loadValue(BaseIndex(obj, scratch, TimesOne), R0); - emitEnterTypeMonitorIC(); return true; } bool BaselineCacheIRCompiler::emitLoadDynamicSlotResult() { Register obj = allocator.useRegister(masm, reader.objOperandId()); AutoScratchRegister scratch(allocator, masm); // We're about to return, so it's safe to clobber obj now. masm.load32(stubAddress(reader.stubOffset()), scratch); masm.loadPtr(Address(obj, NativeObject::offsetOfSlots()), obj); masm.loadValue(BaseIndex(obj, scratch, TimesOne), R0); - emitEnterTypeMonitorIC(); return true; } bool BaselineCacheIRCompiler::emitCallScriptedGetterResult() { MOZ_ASSERT(engine_ == ICStubEngine::Baseline); @@ -1137,18 +1129,16 @@ BaselineCacheIRCompiler::emitCallScripte masm.loadPtr(Address(code, JitCode::offsetOfCode()), code); masm.movePtr(ImmWord(0), ArgumentsRectifierReg); } masm.bind(&noUnderflow); masm.callJit(code); leaveStubFrame(masm, true); - - emitEnterTypeMonitorIC(); return true; } typedef bool (*DoCallNativeGetterFn)(JSContext*, HandleFunction, HandleObject, MutableHandleValue); static const VMFunction DoCallNativeGetterInfo = FunctionInfo<DoCallNativeGetterFn>(DoCallNativeGetter, "DoCallNativeGetter"); bool @@ -1175,38 +1165,29 @@ BaselineCacheIRCompiler::emitCallNativeG masm.Push(obj); masm.Push(scratch); if (!callVM(masm, DoCallNativeGetterInfo)) return false; leaveStubFrame(masm); - - emitEnterTypeMonitorIC(); return true; } bool BaselineCacheIRCompiler::emitLoadUnboxedPropertyResult() { Register obj = allocator.useRegister(masm, reader.objOperandId()); AutoScratchRegister scratch(allocator, masm); JSValueType fieldType = reader.valueType(); - Address fieldOffset(stubAddress(reader.stubOffset())); masm.load32(fieldOffset, scratch); masm.loadUnboxedProperty(BaseIndex(obj, scratch, TimesOne), fieldType, R0); - - if (fieldType == JSVAL_TYPE_OBJECT) - emitEnterTypeMonitorIC(); - else - emitReturnFromIC(); - return true; } bool BaselineCacheIRCompiler::emitGuardNoDetachedTypedObjects() { FailurePath* failure; if (!addFailurePath(&failure)) @@ -1229,28 +1210,22 @@ BaselineCacheIRCompiler::emitLoadTypedOb // Get the object's data pointer. LoadTypedThingData(masm, layout, obj, scratch1); // Get the address being written to. masm.load32(fieldOffset, scratch2); masm.addPtr(scratch2, scratch1); - // Only monitor the result if the type produced by this stub might vary. - bool monitorLoad; if (SimpleTypeDescrKeyIsScalar(typeDescr)) { Scalar::Type type = ScalarTypeFromSimpleTypeDescrKey(typeDescr); - monitorLoad = type == Scalar::Uint32; - masm.loadFromTypedArray(type, Address(scratch1, 0), R0, /* allowDouble = */ true, scratch2, nullptr); } else { ReferenceTypeDescr::Type type = ReferenceTypeFromSimpleTypeDescrKey(typeDescr); - monitorLoad = type != ReferenceTypeDescr::TYPE_STRING; - switch (type) { case ReferenceTypeDescr::TYPE_ANY: masm.loadValue(Address(scratch1, 0), R0); break; case ReferenceTypeDescr::TYPE_OBJECT: { Label notNull, done; masm.loadPtr(Address(scratch1, 0), scratch1); @@ -1268,32 +1243,23 @@ BaselineCacheIRCompiler::emitLoadTypedOb masm.tagValue(JSVAL_TYPE_STRING, scratch1, R0); break; default: MOZ_CRASH("Invalid ReferenceTypeDescr"); } } - if (monitorLoad) - emitEnterTypeMonitorIC(); - else - emitReturnFromIC(); return true; } bool BaselineCacheIRCompiler::emitLoadUndefinedResult() { masm.moveValue(UndefinedValue(), R0); - - // Normally for this op, the result would have to be monitored by TI. - // However, since this stub ALWAYS returns UndefinedValue(), and we can be sure - // that undefined is already registered with the type-set, this can be avoided. - emitReturnFromIC(); return true; } bool BaselineCacheIRCompiler::emitLoadInt32ArrayLengthResult() { Register obj = allocator.useRegister(masm, reader.objOperandId()); AutoScratchRegister scratch(allocator, masm); @@ -1303,33 +1269,25 @@ BaselineCacheIRCompiler::emitLoadInt32Ar return false; masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch); masm.load32(Address(scratch, ObjectElements::offsetOfLength()), scratch); // Guard length fits in an int32. masm.branchTest32(Assembler::Signed, scratch, scratch, failure->label()); masm.tagValue(JSVAL_TYPE_INT32, scratch, R0); - - // The int32 type was monitored when attaching the stub, so we can - // just return. - emitReturnFromIC(); return true; } bool BaselineCacheIRCompiler::emitLoadUnboxedArrayLengthResult() { Register obj = allocator.useRegister(masm, reader.objOperandId()); masm.load32(Address(obj, UnboxedArrayObject::offsetOfLength()), R0.scratchReg()); masm.tagValue(JSVAL_TYPE_INT32, R0.scratchReg(), R0); - - // The int32 type was monitored when attaching the stub, so we can - // just return. - emitReturnFromIC(); return true; } bool BaselineCacheIRCompiler::emitLoadArgumentsObjectLengthResult() { Register obj = allocator.useRegister(masm, reader.objOperandId()); AutoScratchRegister scratch(allocator, masm); @@ -1346,17 +1304,32 @@ BaselineCacheIRCompiler::emitLoadArgumen scratch, Imm32(ArgumentsObject::LENGTH_OVERRIDDEN_BIT), failure->label()); // Shift out arguments length and return it. No need to type monitor // because this stub always returns int32. masm.rshiftPtr(Imm32(ArgumentsObject::PACKED_BITS_COUNT), scratch); masm.tagValue(JSVAL_TYPE_INT32, scratch, R0); - emitReturnFromIC(); + return true; +} + +bool +BaselineCacheIRCompiler::emitTypeMonitorResult() +{ + allocator.discardStack(masm); + EmitEnterTypeMonitorIC(masm); + return true; +} + +bool +BaselineCacheIRCompiler::emitReturnFromIC() +{ + allocator.discardStack(masm); + EmitReturnFromIC(masm); return true; } bool BaselineCacheIRCompiler::emitLoadObject() { Register reg = allocator.defineRegister(masm, reader.objOperandId()); masm.loadPtr(stubAddress(reader.stubOffset()), reg);
--- a/js/src/jit/CacheIR.cpp +++ b/js/src/jit/CacheIR.cpp @@ -241,16 +241,31 @@ EmitReadSlotResult(CacheIRWriter& writer EmitLoadSlotResult(writer, holderId, &holder->as<NativeObject>(), shape); } else { MOZ_ASSERT(!holderId.valid()); writer.loadUndefinedResult(); } } static void +EmitReadSlotReturn(CacheIRWriter& writer, JSObject*, JSObject* holder, Shape* shape) +{ + // Slot access. + if (holder) { + MOZ_ASSERT(shape); + writer.typeMonitorResult(); + } else { + // Normally for this op, the result would have to be monitored by TI. + // However, since this stub ALWAYS returns UndefinedValue(), and we can be sure + // that undefined is already registered with the type-set, this can be avoided. + writer.returnFromIC(); + } +} + +static void EmitCallGetterResult(CacheIRWriter& writer, JSObject* obj, JSObject* holder, Shape* shape, ObjOperandId objId) { Maybe<ObjOperandId> expandoId; TestMatchingReceiver(writer, obj, shape, objId, &expandoId); if (obj != holder) { GeneratePrototypeGuards(writer, obj, holder, objId); @@ -259,24 +274,26 @@ EmitCallGetterResult(CacheIRWriter& writ ObjOperandId holderId = writer.loadObject(holder); writer.guardShape(holderId, holder->as<NativeObject>().lastProperty()); } if (IsCacheableGetPropCallNative(obj, holder, shape)) { JSFunction* target = &shape->getterValue().toObject().as<JSFunction>(); MOZ_ASSERT(target->isNative()); writer.callNativeGetterResult(objId, target); + writer.typeMonitorResult(); return; } MOZ_ASSERT(IsCacheableGetPropCallScripted(obj, holder, shape)); JSFunction* target = &shape->getterValue().toObject().as<JSFunction>(); MOZ_ASSERT(target->hasJITCode()); writer.callScriptedGetterResult(objId, target); + writer.typeMonitorResult(); } bool GetPropIRGenerator::tryAttachNative(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId) { MOZ_ASSERT(!emitted_); RootedShape shape(cx_); @@ -298,16 +315,17 @@ GetPropIRGenerator::tryAttachNative(Cach // See the comment in StripPreliminaryObjectStubs. if (IsPreliminaryObject(obj)) preliminaryObjectAction_ = PreliminaryObjectAction::NotePreliminary; else preliminaryObjectAction_ = PreliminaryObjectAction::Unlink; } } EmitReadSlotResult(writer, obj, holder, shape, objId); + EmitReadSlotReturn(writer, obj, holder, shape); break; case CanAttachCallGetter: EmitCallGetterResult(writer, obj, holder, shape, objId); break; default: MOZ_CRASH("Bad NativeGetPropCacheability"); } @@ -376,16 +394,21 @@ GetPropIRGenerator::tryAttachUnboxed(Cac return true; if (!cx_->runtime()->jitSupportsFloatingPoint) return true; writer.guardGroup(objId, obj->group()); writer.loadUnboxedPropertyResult(objId, property->type, UnboxedPlainObject::offsetOfData() + property->offset); + if (property->type == JSVAL_TYPE_OBJECT) + writer.typeMonitorResult(); + else + writer.returnFromIC(); + emitted_ = true; preliminaryObjectAction_ = PreliminaryObjectAction::Unlink; return true; } bool GetPropIRGenerator::tryAttachUnboxedExpando(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId) { @@ -400,16 +423,17 @@ GetPropIRGenerator::tryAttachUnboxedExpa Shape* shape = expando->lookup(cx_, NameToId(name_)); if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot()) return true; emitted_ = true; EmitReadSlotResult(writer, obj, obj, shape, objId); + EmitReadSlotReturn(writer, obj, obj, shape); return true; } bool GetPropIRGenerator::tryAttachTypedObject(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId) { MOZ_ASSERT(!emitted_); @@ -437,16 +461,32 @@ GetPropIRGenerator::tryAttachTypedObject TypedThingLayout layout = GetTypedThingLayout(shape->getObjectClass()); uint32_t fieldOffset = structDescr->fieldOffset(fieldIndex); uint32_t typeDescr = SimpleTypeDescrKey(&fieldDescr->as<SimpleTypeDescr>()); writer.guardNoDetachedTypedObjects(); writer.guardShape(objId, shape); writer.loadTypedObjectResult(objId, fieldOffset, layout, typeDescr); + + // Only monitor the result if the type produced by this stub might vary. + bool monitorLoad = false; + if (SimpleTypeDescrKeyIsScalar(typeDescr)) { + Scalar::Type type = ScalarTypeFromSimpleTypeDescrKey(typeDescr); + monitorLoad = type == Scalar::Uint32; + } else { + ReferenceTypeDescr::Type type = ReferenceTypeFromSimpleTypeDescrKey(typeDescr); + monitorLoad = type != ReferenceTypeDescr::TYPE_STRING; + } + + if (monitorLoad) + writer.typeMonitorResult(); + else + writer.returnFromIC(); + emitted_ = true; return true; } bool GetPropIRGenerator::tryAttachObjectLength(CacheIRWriter& writer, HandleObject obj, ObjOperandId objId) { MOZ_ASSERT(!emitted_); @@ -457,35 +497,38 @@ GetPropIRGenerator::tryAttachObjectLengt if (obj->is<ArrayObject>()) { // Make sure int32 is added to the TypeSet before we attach a stub, so // the stub can return int32 values without monitoring the result. if (obj->as<ArrayObject>().length() > INT32_MAX) return true; writer.guardClass(objId, GuardClassKind::Array); writer.loadInt32ArrayLengthResult(objId); + writer.returnFromIC(); emitted_ = true; return true; } if (obj->is<UnboxedArrayObject>()) { writer.guardClass(objId, GuardClassKind::UnboxedArray); writer.loadUnboxedArrayLengthResult(objId); + writer.returnFromIC(); emitted_ = true; return true; } if (obj->is<ArgumentsObject>() && !obj->as<ArgumentsObject>().hasOverriddenLength()) { if (obj->is<MappedArgumentsObject>()) { writer.guardClass(objId, GuardClassKind::MappedArguments); } else { MOZ_ASSERT(obj->is<UnmappedArgumentsObject>()); writer.guardClass(objId, GuardClassKind::UnmappedArguments); } writer.loadArgumentsObjectLengthResult(objId); + writer.returnFromIC(); emitted_ = true; return true; } return true; } bool @@ -512,16 +555,17 @@ GetPropIRGenerator::tryAttachModuleNames emitted_ = true; // Check for the specific namespace object. writer.guardSpecificObject(objId, ns); ObjOperandId envId = writer.loadObject(env); EmitLoadSlotResult(writer, envId, env, shape); + writer.typeMonitorResult(); return true; } bool GetPropIRGenerator::tryAttachPrimitive(CacheIRWriter& writer, ValOperandId valId) { MOZ_ASSERT(!emitted_); @@ -560,12 +604,13 @@ GetPropIRGenerator::tryAttachPrimitive(C if (!shape || !shape->hasSlot() || !shape->hasDefaultGetter()) return true; writer.guardType(valId, primitiveType); ObjOperandId protoId = writer.loadObject(proto); writer.guardShape(protoId, proto->lastProperty()); EmitLoadSlotResult(writer, protoId, proto, shape); + writer.typeMonitorResult(); emitted_ = true; return true; }
--- a/js/src/jit/CacheIR.h +++ b/js/src/jit/CacheIR.h @@ -97,17 +97,20 @@ class ObjOperandId : public OperandId _(LoadDynamicSlotResult) \ _(LoadUnboxedPropertyResult) \ _(LoadTypedObjectResult) \ _(LoadInt32ArrayLengthResult) \ _(LoadUnboxedArrayLengthResult) \ _(LoadArgumentsObjectLengthResult) \ _(CallScriptedGetterResult) \ _(CallNativeGetterResult) \ - _(LoadUndefinedResult) + _(LoadUndefinedResult) \ + \ + _(TypeMonitorResult) \ + _(ReturnFromIC) enum class CacheOp { #define DEFINE_OP(op) op, CACHE_IR_OPS(DEFINE_OP) #undef DEFINE_OP }; struct StubField { @@ -342,16 +345,23 @@ class MOZ_RAII CacheIRWriter void callScriptedGetterResult(ObjOperandId obj, JSFunction* getter) { writeOpWithOperandId(CacheOp::CallScriptedGetterResult, obj); addStubWord(uintptr_t(getter), StubField::GCType::JSObject); } void callNativeGetterResult(ObjOperandId obj, JSFunction* getter) { writeOpWithOperandId(CacheOp::CallNativeGetterResult, obj); addStubWord(uintptr_t(getter), StubField::GCType::JSObject); } + + void typeMonitorResult() { + writeOp(CacheOp::TypeMonitorResult); + } + void returnFromIC() { + writeOp(CacheOp::ReturnFromIC); + } }; class CacheIRStubInfo; // Helper class for reading CacheIR bytecode. class MOZ_RAII CacheIRReader { CompactBufferReader buffer_;
--- a/js/src/jit/Ion.h +++ b/js/src/jit/Ion.h @@ -25,16 +25,17 @@ enum MethodStatus Method_Error, Method_CantCompile, Method_Skipped, Method_Compiled }; enum AbortReason { AbortReason_Alloc, + AbortReason_Inlining, AbortReason_PreliminaryObjects, AbortReason_Disable, AbortReason_Error, AbortReason_NoAbort }; // A JIT context is needed to enter into either an JIT method or an instance // of a JIT compiler. It points to a temporary allocator and the active
--- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -5251,18 +5251,23 @@ IonBuilder::inlineScriptedCall(CallInfo& abortReason_ = AbortReason_Error; return InliningStatus_Error; } // Inlining the callee failed. Mark the callee as uninlineable only if // the inlining was aborted for a non-exception reason. if (inlineBuilder.abortReason_ == AbortReason_Disable) { calleeScript->setUninlineable(); - current = backup.restore(); - return InliningStatus_NotInlined; + if (!JitOptions.disableInlineBacktracking) { + current = backup.restore(); + return InliningStatus_NotInlined; + } + abortReason_ = AbortReason_Inlining; + } else if (inlineBuilder.abortReason_ == AbortReason_Inlining) { + abortReason_ = AbortReason_Inlining; } else if (inlineBuilder.abortReason_ == AbortReason_Alloc) { abortReason_ = AbortReason_Alloc; } else if (inlineBuilder.abortReason_ == AbortReason_PreliminaryObjects) { const ObjectGroupVector& groups = inlineBuilder.abortedPreliminaryGroups(); MOZ_ASSERT(!groups.empty()); for (size_t i = 0; i < groups.length(); i++) addAbortedPreliminaryGroup(groups[i]); abortReason_ = AbortReason_PreliminaryObjects; @@ -5281,18 +5286,22 @@ IonBuilder::inlineScriptedCall(CallInfo& // Inherit the slots from current and pop |fun|. returnBlock->inheritSlots(current); returnBlock->pop(); // Accumulate return values. if (returns.empty()) { // Inlining of functions that have no exit is not supported. calleeScript->setUninlineable(); - current = backup.restore(); - return InliningStatus_NotInlined; + if (!JitOptions.disableInlineBacktracking) { + current = backup.restore(); + return InliningStatus_NotInlined; + } + abortReason_ = AbortReason_Inlining; + return InliningStatus_Error; } MDefinition* retvalDefn = patchInlinedReturns(callInfo, returns, returnBlock); if (!retvalDefn) return InliningStatus_Error; returnBlock->push(retvalDefn); // Initialize entry slots now that the stack has been fixed up. if (!returnBlock->initEntrySlots(alloc()))
--- a/js/src/jit/JitOptions.cpp +++ b/js/src/jit/JitOptions.cpp @@ -70,16 +70,19 @@ DefaultJitOptions::DefaultJitOptions() // are not modified before its OsiPoint. SET_DEFAULT(checkOsiPointRegisters, false); #endif // Whether to enable extra code to perform dynamic validation of // RangeAnalysis results. SET_DEFAULT(checkRangeAnalysis, false); + // Toggles whether IonBuilder fallbacks to a call if we fail to inline. + SET_DEFAULT(disableInlineBacktracking, true); + // Toggles whether Alignment Mask Analysis is globally disabled. SET_DEFAULT(disableAma, false); // Toggles whether Effective Address Analysis is globally disabled. SET_DEFAULT(disableEaa, false); // Toggle whether eager simd unboxing is globally disabled. SET_DEFAULT(disableEagerSimdUnbox, false);
--- a/js/src/jit/JitOptions.h +++ b/js/src/jit/JitOptions.h @@ -42,16 +42,17 @@ LookupRegisterAllocator(const char* name struct DefaultJitOptions { bool checkGraphConsistency; #ifdef CHECK_OSIPOINT_REGISTERS bool checkOsiPointRegisters; #endif bool checkRangeAnalysis; bool runExtraChecks; + bool disableInlineBacktracking; bool disableAma; bool disableEaa; bool disableEagerSimdUnbox; bool disableEdgeCaseAnalysis; bool disableFlowAA; bool disableGvn; bool disableInlining; bool disableLicm;
--- a/js/src/tests/ecma_3/FunExpr/regress-545980.js +++ b/js/src/tests/ecma_3/FunExpr/regress-545980.js @@ -22,17 +22,17 @@ function run_test() { (function doSearch(query) { ac.startSearch(query, "", null, { onSearchResult: function() { var num = tagIds.length; var timer = new Timer; var next = query.slice(1); - timer.initWithCallback({ notify: function() doSearch(next) }); + timer.initWithCallback({ notify: function() { return doSearch(next); } }); } }); })("title"); } run_test(); later.onSearchResult(); for (var i in Timer.q)
--- a/js/src/tests/ecma_5/strict/13.1.js +++ b/js/src/tests/ecma_5/strict/13.1.js @@ -81,21 +81,21 @@ assertEq(testLenientAndStrict('Function( completesNormally), true); /* * The parameter lists of function expressions should not contain * duplicate identifiers. */ -assertEq(testLenientAndStrict('(function (x,x) 2)', +assertEq(testLenientAndStrict('(function (x,x) {})', parsesSuccessfully, parseRaisesException(SyntaxError)), true); -assertEq(testLenientAndStrict('(function (x,y) 2)', +assertEq(testLenientAndStrict('(function (x,y) {})', parsesSuccessfully, parsesSuccessfully), true); /* * All permutations of: * - For the two magic identifiers 'arguments' or 'eval' * - For function definitions, function expressions, expression closures, @@ -168,29 +168,29 @@ assertEq(testLenientAndStrict('(function assertEq(testLenientAndStrict('(function f({x:eval}){"use strict";})', parseRaisesException(SyntaxError), parseRaisesException(SyntaxError)), true); assertEq(testLenientAndStrict('(function eval(){"use strict";})', parseRaisesException(SyntaxError), parseRaisesException(SyntaxError)), true); -assertEq(testLenientAndStrict('(function f(eval) 2)', +assertEq(testLenientAndStrict('(function f(eval) {})', parsesSuccessfully, parseRaisesException(SyntaxError)), true); -assertEq(testLenientAndStrict('(function f([eval]) 2)', +assertEq(testLenientAndStrict('(function f([eval]) {})', parsesSuccessfully, parseRaisesException(SyntaxError)), true); -assertEq(testLenientAndStrict('(function f({x:eval}) 2)', +assertEq(testLenientAndStrict('(function f({x:eval}) {})', parsesSuccessfully, parseRaisesException(SyntaxError)), true); -assertEq(testLenientAndStrict('(function eval() 2)', +assertEq(testLenientAndStrict('(function eval() {})', parsesSuccessfully, parseRaisesException(SyntaxError)), true); assertEq(testLenientAndStrict('({set x(eval){}})', parsesSuccessfully, parseRaisesException(SyntaxError)), true); assertEq(testLenientAndStrict('({set x([eval]){}})', @@ -272,29 +272,29 @@ assertEq(testLenientAndStrict('(function assertEq(testLenientAndStrict('(function f({x:arguments}){"use strict";})', parseRaisesException(SyntaxError), parseRaisesException(SyntaxError)), true); assertEq(testLenientAndStrict('(function arguments(){"use strict";})', parseRaisesException(SyntaxError), parseRaisesException(SyntaxError)), true); -assertEq(testLenientAndStrict('(function f(arguments) 2)', +assertEq(testLenientAndStrict('(function f(arguments) {})', parsesSuccessfully, parseRaisesException(SyntaxError)), true); -assertEq(testLenientAndStrict('(function f([arguments]) 2)', +assertEq(testLenientAndStrict('(function f([arguments]) {})', parsesSuccessfully, parseRaisesException(SyntaxError)), true); -assertEq(testLenientAndStrict('(function f({x:arguments}) 2)', +assertEq(testLenientAndStrict('(function f({x:arguments}) {})', parsesSuccessfully, parseRaisesException(SyntaxError)), true); -assertEq(testLenientAndStrict('(function arguments() 2)', +assertEq(testLenientAndStrict('(function arguments() {})', parsesSuccessfully, parseRaisesException(SyntaxError)), true); assertEq(testLenientAndStrict('({set x(arguments){}})', parsesSuccessfully, parseRaisesException(SyntaxError)), true); assertEq(testLenientAndStrict('({set x([arguments]){}})',
--- a/js/src/tests/ecma_6/Class/newTargetEval.js +++ b/js/src/tests/ecma_6/Class/newTargetEval.js @@ -5,17 +5,17 @@ try { } catch (e if e instanceof SyntaxError) { } // new.target is invalid inside eval inside top-level arrow functions assertThrowsInstanceOf(() => eval('new.target'), SyntaxError); // new.target is invalid inside indirect eval. let ieval = eval; try { - (function () ieval('new.target'))(); + (function () { return ieval('new.target'); })(); assertEq(false, true); } catch (e if e instanceof SyntaxError) { } function assertNewTarget(expected) { assertEq(eval('new.target'), expected); assertEq((()=>eval('new.target'))(), expected); // Also test nestings "by induction"
--- a/js/src/tests/js1_8/extensions/regress-452913.js +++ b/js/src/tests/js1_8/extensions/regress-452913.js @@ -7,12 +7,12 @@ var BUGNUMBER = 452913; var summary = 'Do not crash with defined getter and for (let)'; var actual = ''; var expect = ''; printBugNumber(BUGNUMBER); printStatus (summary); -(this.__defineGetter__("x", function (x)'foo'.replace(/o/g, [1].push))); +(this.__defineGetter__("x", function (x) { return 'foo'.replace(/o/g, [1].push); })); for(let y in [,,,]) for(let y in [,,,]) x = x; reportCompare(expect, actual, summary);
--- a/js/src/tests/js1_8/extensions/regress-454744.js +++ b/js/src/tests/js1_8/extensions/regress-454744.js @@ -17,17 +17,17 @@ function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); try { - this.__defineGetter__('x', function() 2); for (var j=0;j<4;++j) { x=1; } + this.__defineGetter__('x', function() { return 2; }); for (var j=0;j<4;++j) { x=1; } } catch(ex) { } reportCompare(expect, actual, summary);
--- a/js/src/tests/js1_8/extensions/regress-469625.js +++ b/js/src/tests/js1_8/extensions/regress-469625.js @@ -20,17 +20,17 @@ function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = 'TypeError: [].__proto__ is not a function'; - Array.prototype.__proto__ = function () 3; + Array.prototype.__proto__ = function () { return 3; }; try { [].__proto__(); } catch(ex) { print(actual = ex + '');
--- a/js/src/tests/js1_8/regress/regress-452491.js +++ b/js/src/tests/js1_8/regress/regress-452491.js @@ -15,15 +15,15 @@ test(); function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); - for (var j=0;j<5;++j) (new (function(q) q)).a; + for (var j=0;j<5;++j) (new (function(q) { return q; })).a; reportCompare(expect, actual, summary); exitFunc ('test'); }
--- a/js/src/tests/js1_8/regress/regress-459389.js +++ b/js/src/tests/js1_8/regress/regress-459389.js @@ -48,18 +48,19 @@ var p=new SNI.MetaData.Parameter(); this.addParameter=p.addParameter; this.getParameter=p.getParameter; }; function Ad() { var url=new SNI.Ads.Url(); this.addParameter=url.addParameter; this.getParameter=url.getParameter; } -function DartAd() -AdUrl.prototype=new Ad(); +function DartAd() { + return AdUrl.prototype=new Ad(); +} function AdUrl() { } function AdRestriction() { var p=new SNI.MetaData.Parameter(); this.addParameter=p.addParameter; this.getParameter=p.getParameter; this.getKeys=p.getKeys; } function AdRestrictionManager(){
--- a/js/src/tests/js1_8/regress/regress-464096.js +++ b/js/src/tests/js1_8/regress/regress-464096.js @@ -17,16 +17,16 @@ test(); function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); for (let f in [1,1]); - Object.prototype.__defineGetter__('x', function() gc()); + Object.prototype.__defineGetter__('x', function() { return gc(); }); (function() { for each (let j in [1,1,1,1,1]) { var y = .2; } })(); reportCompare(expect, actual, summary); exitFunc ('test'); }
--- a/js/src/tests/js1_8/regress/regress-465220.js +++ b/js/src/tests/js1_8/regress/regress-465220.js @@ -20,17 +20,17 @@ function test() printBugNumber(BUGNUMBER); printStatus (summary); expect = 'TypeError: can\'t convert o to primitive type'; try { - var o = {toString: function()(i > 2) ? this : "foo"}; + var o = {toString: function() { return (i > 2) ? this : "foo"; }}; var s = ""; for (var i = 0; i < 5; i++) s += o + o; print(s); actual = 'No Exception'; } catch(ex) {
--- a/js/src/tests/js1_8/regress/regress-465460-01.js +++ b/js/src/tests/js1_8/regress/regress-465460-01.js @@ -18,15 +18,15 @@ function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = '11111'; - (function(d) { for (let j = 0; j < 5; ++j) { actual += ('' + d); } })({valueOf: function()1}); + (function(d) { for (let j = 0; j < 5; ++j) { actual += ('' + d); } })({valueOf: function() { return 1; }}); reportCompare(expect, actual, summary); exitFunc ('test'); }
--- a/js/src/tests/js1_8/regress/regress-467495-01.js +++ b/js/src/tests/js1_8/regress/regress-467495-01.js @@ -15,14 +15,14 @@ test(); //----------------------------------------------------------------------------- function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); - (function() { x = 0; function x() 4; var x; const y = 1; })(); + (function() { x = 0; function x() { return 4; }; var x; const y = 1; })(); reportCompare(expect, actual, summary); exitFunc ('test'); }
--- a/js/src/tests/js1_8_1/extensions/regress-452498-196.js +++ b/js/src/tests/js1_8_1/extensions/regress-452498-196.js @@ -29,16 +29,16 @@ function test() x+=NaN; reportCompare(expect, actual, summary + ': 1'); // Assertion failure: lexdep->isLet(), at ../jsparse.cpp:1900 (function (){ var x; - eval("var x; (function ()x)"); + eval("var x; (function () { return x; })"); } )(); reportCompare(expect, actual, summary + ': 2'); exitFunc ('test'); }
--- a/js/src/tests/js1_8_1/regress/regress-452498-027.js +++ b/js/src/tests/js1_8_1/regress/regress-452498-027.js @@ -19,17 +19,17 @@ function test() enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); expect = '5'; // ------- Comment #27 From Brendan Eich - function f(x){function g(y)x+y;return g} + function f(x){function g(y) { return x+y; } return g} g = f(2); actual = String(g(3)); reportCompare(expect, actual, summary); exitFunc ('test'); }
--- a/js/src/tests/js1_8_1/regress/regress-452498-079.js +++ b/js/src/tests/js1_8_1/regress/regress-452498-079.js @@ -19,16 +19,16 @@ test(); function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #79 From Jason Orendorff - x; var x; function x() 0 + x; var x; function x() { return 0; } // Assertion failure: !(pn->pn_dflags & flag), at ../jsparse.h:635 reportCompare(expect, actual, summary); exitFunc ('test'); }
--- a/js/src/tests/js1_8_1/regress/regress-452498-102.js +++ b/js/src/tests/js1_8_1/regress/regress-452498-102.js @@ -19,17 +19,17 @@ function test() enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #102 From Gary Kwong [:nth10sd] // ===== - (function(){function x(){} function x()y})(); + (function(){function x(){} function x() { return y; }})(); // Assertion failure: JOF_OPTYPE(op) == JOF_ATOM, at ../jsemit.cpp:1710 // ===== function f() { "" + (function(){ for( ; [function(){}] ; x = 0) with({x: ""}) {
--- a/js/src/tests/js1_8_1/regress/regress-452498-121.js +++ b/js/src/tests/js1_8_1/regress/regress-452498-121.js @@ -18,16 +18,16 @@ function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // ------- Comment #121 From Gary Kwong [:nth10sd] // without -j - x = function()x; + x = function() { return x; }; // Assertion failure: !(pn->pn_dflags & flag), at ../jsparse.h:651 reportCompare(expect, actual, summary); exitFunc ('test'); }
--- a/js/src/tests/js1_8_1/regress/regress-452498-160.js +++ b/js/src/tests/js1_8_1/regress/regress-452498-160.js @@ -17,18 +17,18 @@ test(); function test() { enterFunc ('test'); printBugNumber(BUGNUMBER); printStatus (summary); // crash [@ js_Interpret] - (eval("(function(){ this.watch(\"x\", function () { new function ()y } ); const y = undefined });"))(); + (eval("(function(){ this.watch(\"x\", function () { new function () { return y; } } ); const y = undefined });"))(); x = NaN; reportCompare(expect, actual, summary + ': 2'); // Assertion failure: JOF_OPTYPE(op) == JOF_ATOM, at ../jsemit.cpp:5916 - ({ set z(v){}, set y(v)--x, set w(v)--w }); + ({ set z(v){}, set y(v) { return --x; }, set w(v) { return --w; } }); reportCompare(expect, actual, summary + ': 3'); exitFunc ('test'); }
--- a/js/src/tests/js1_8_5/regress/regress-607863.js +++ b/js/src/tests/js1_8_5/regress/regress-607863.js @@ -1,14 +1,14 @@ // |reftest| fails-if(!xulRuntime.shell) /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * Any copyright is dedicated to the Public Domain. * http://creativecommons.org/licenses/publicdomain/ */ var sandbox = evalcx(''); -var foreign = evalcx('({ get f() this, set x(v) { result = this } })', sandbox); +var foreign = evalcx('({ get f() { return this; }, set x(v) { result = this } })', sandbox); var local = Object.create(foreign); reportCompare(local, local.f, "this should be set correctly in getters"); local.x = 42; reportCompare(local, sandbox.result, "this should be set correctly in setters");
--- a/js/src/tests/js1_8_5/regress/regress-646820-1.js +++ b/js/src/tests/js1_8_5/regress/regress-646820-1.js @@ -1,9 +1,9 @@ // Any copyright is dedicated to the Public Domain. // http://creativecommons.org/licenses/publicdomain/ (function () { - var [x, y] = [1, function () x]; + var [x, y] = [1, function () { return x; }]; assertEq(y(), 1); })(); reportCompare(0, 0, 'ok');
--- a/js/src/tests/js1_8_5/regress/regress-646820-2.js +++ b/js/src/tests/js1_8_5/regress/regress-646820-2.js @@ -1,11 +1,11 @@ // Any copyright is dedicated to the Public Domain. // http://creativecommons.org/licenses/publicdomain/ (function () { var obj = {prop: 1}; - var [x, {prop: y}] = [function () y, obj]; + var [x, {prop: y}] = [function () { return y; }, obj]; assertEq(y, 1); assertEq(x(), 1); })(); reportCompare(0, 0, 'ok');
--- a/js/src/tests/js1_8_5/regress/regress-646820-3.js +++ b/js/src/tests/js1_8_5/regress/regress-646820-3.js @@ -1,9 +1,9 @@ // Any copyright is dedicated to the Public Domain. // http://creativecommons.org/licenses/publicdomain/ (function () { - var [x, y] = [function () y, 13]; + var [x, y] = [function () { return y; }, 13]; assertEq(x(), 13); })(); reportCompare(0, 0, 'ok');
--- a/layout/base/RestyleManagerBase.h +++ b/layout/base/RestyleManagerBase.h @@ -76,16 +76,18 @@ public: // Note: It's the caller's responsibility to make sure to wrap a // ProcessRestyledFrames call in a view update batch and a script blocker. // This function does not call ProcessAttachedQueue() on the binding manager. // If the caller wants that to happen synchronously, it needs to handle that // itself. nsresult ProcessRestyledFrames(nsStyleChangeList& aChangeList); + bool IsInStyleRefresh() const { return mInStyleRefresh; } + protected: void ContentStateChangedInternal(Element* aElement, EventStates aStateMask, nsChangeHint* aOutChangeHint, nsRestyleHint* aOutRestyleHint); bool IsDisconnected() { return mPresContext == nullptr; }
--- a/layout/base/RestyleManagerHandle.h +++ b/layout/base/RestyleManagerHandle.h @@ -12,16 +12,17 @@ #include "mozilla/HandleRefPtr.h" #include "mozilla/RefCountType.h" #include "mozilla/StyleBackendType.h" #include "nsChangeHint.h" namespace mozilla { class RestyleManager; class ServoRestyleManager; +class RestyleManagerBase; namespace dom { class Element; } // namespace dom } // namespace mozilla class nsAttrValue; class nsIAtom; class nsIContent; class nsIFrame; @@ -90,16 +91,24 @@ public: { MOZ_ASSERT(IsServo()); return const_cast<Ptr*>(this)->AsServo(); } const RestyleManager* GetAsGecko() const { return IsGecko() ? AsGecko() : nullptr; } const ServoRestyleManager* GetAsServo() const { return IsServo() ? AsServo() : nullptr; } + const mozilla::RestyleManagerBase* AsBase() const { + return reinterpret_cast<const RestyleManagerBase*>(mValue & ~SERVO_BIT); + } + + mozilla::RestyleManagerBase* AsBase() { + return reinterpret_cast<RestyleManagerBase*>(mValue & ~SERVO_BIT); + } + // These inline methods are defined in RestyleManagerHandleInlines.h. inline MozExternalRefCountType AddRef(); inline MozExternalRefCountType Release(); // Restyle manager interface. These inline methods are defined in // RestyleManagerHandleInlines.h and just forward to the underlying // RestyleManager or ServoRestyleManager. See corresponding comments in // RestyleManager.h for descriptions of these methods.
--- a/layout/base/ServoRestyleManager.cpp +++ b/layout/base/ServoRestyleManager.cpp @@ -13,16 +13,17 @@ #include "nsStyleChangeList.h" using namespace mozilla::dom; namespace mozilla { ServoRestyleManager::ServoRestyleManager(nsPresContext* aPresContext) : RestyleManagerBase(aPresContext) + , mReentrantChanges(nullptr) { } void ServoRestyleManager::PostRestyleEvent(Element* aElement, nsRestyleHint aRestyleHint, nsChangeHint aMinChangeHint) { @@ -30,29 +31,38 @@ ServoRestyleManager::PostRestyleEvent(El MOZ_UNLIKELY(PresContext()->PresShell()->IsDestroying())) { return; } if (aRestyleHint == 0 && !aMinChangeHint && !HasPendingRestyles()) { return; // Nothing to do. } + // We allow posting change hints during restyling, but not restyle hints + // themselves, since those would require us to re-traverse the tree. + MOZ_ASSERT_IF(mInStyleRefresh, aRestyleHint == 0); + + // Processing change hints sometimes causes new change hints to be generated. + // Doing this after the gecko post-traversal is problematic, so instead we just + // queue them up for special handling. + if (mReentrantChanges) { + MOZ_ASSERT(aRestyleHint == 0); + mReentrantChanges->AppendElement(ReentrantChange { aElement, aMinChangeHint }); + return; + } + // XXX This is a temporary hack to make style attribute change works. // In the future, we should be able to use this hint directly. if (aRestyleHint & eRestyle_StyleAttribute) { aRestyleHint &= ~eRestyle_StyleAttribute; aRestyleHint |= eRestyle_Self | eRestyle_Subtree; } - // Note that unlike in Servo, we don't mark elements as dirty until we process - // the restyle hints in ProcessPendingRestyles. if (aRestyleHint || aMinChangeHint) { - ServoElementSnapshot* snapshot = SnapshotForElement(aElement); - snapshot->AddExplicitRestyleHint(aRestyleHint); - snapshot->AddExplicitChangeHint(aMinChangeHint); + Servo_NoteExplicitHints(aElement, aRestyleHint, aMinChangeHint); } PostRestyleEventInternal(false); } void ServoRestyleManager::PostRestyleEventForLazyConstruction() { @@ -68,133 +78,77 @@ ServoRestyleManager::RebuildAllStyleData void ServoRestyleManager::PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint, nsRestyleHint aRestyleHint) { NS_WARNING("stylo: ServoRestyleManager::PostRebuildAllStyleDataEvent not implemented"); } -static void -MarkSelfAndDescendantsAsNotDirtyForServo(nsIContent* aContent) +/* static */ void +ServoRestyleManager::ClearServoDataFromSubtree(Element* aElement) { - aContent->UnsetIsDirtyForServo(); + aElement->ClearServoData(); - if (aContent->HasDirtyDescendantsForServo()) { - aContent->UnsetHasDirtyDescendantsForServo(); - - StyleChildrenIterator it(aContent); - for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) { - MarkSelfAndDescendantsAsNotDirtyForServo(n); + StyleChildrenIterator it(aElement); + for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) { + if (n->IsElement()) { + ClearServoDataFromSubtree(n->AsElement()); } } + + aElement->UnsetHasDirtyDescendantsForServo(); } /* static */ void -ServoRestyleManager::ClearServoDataFromSubtree(nsIContent* aContent) +ServoRestyleManager::ClearDirtyDescendantsFromSubtree(Element* aElement) { - aContent->ClearServoData(); - aContent->SetIsDirtyForServo(); - aContent->UnsetHasDirtyDescendantsForServo(); + StyleChildrenIterator it(aElement); + for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) { + if (n->IsElement()) { + ClearDirtyDescendantsFromSubtree(n->AsElement()); + } + } - AllChildrenIterator it(aContent, nsIContent::eAllChildren); - for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) { - ClearServoDataFromSubtree(n); - } + aElement->UnsetHasDirtyDescendantsForServo(); } void -ServoRestyleManager::RecreateStyleContexts(nsIContent* aContent, +ServoRestyleManager::RecreateStyleContexts(Element* aElement, nsStyleContext* aParentContext, ServoStyleSet* aStyleSet, nsStyleChangeList& aChangeListToProcess) { - MOZ_ASSERT(aContent->IsElement() || aContent->IsNodeOfType(nsINode::eTEXT)); + nsIFrame* primaryFrame = aElement->GetPrimaryFrame(); - nsIFrame* primaryFrame = aContent->GetPrimaryFrame(); - if (!primaryFrame && !aContent->IsDirtyForServo()) { - // This happens when, for example, a display: none child of a - // HAS_DIRTY_DESCENDANTS content is reached as part of the traversal. - MarkSelfAndDescendantsAsNotDirtyForServo(aContent); - return; + // FIXME(bholley): Once we transfer ownership of the styles to the frame, we + // can fast-reject without the FFI call by checking mServoData for null. + nsChangeHint changeHint = Servo_CheckChangeHint(aElement); + if (changeHint) { + aChangeListToProcess.AppendChange(primaryFrame, aElement, changeHint); } - // Work on text before. - if (!aContent->IsElement()) { - if (primaryFrame) { - RefPtr<nsStyleContext> oldStyleContext = primaryFrame->StyleContext(); - RefPtr<nsStyleContext> newContext = - aStyleSet->ResolveStyleForText(aContent, aParentContext); - - for (nsIFrame* f = primaryFrame; f; - f = GetNextContinuationWithSameStyle(f, oldStyleContext)) { - f->SetStyleContext(newContext); - } - } - - aContent->UnsetIsDirtyForServo(); + // If our change hint is reconstruct, we delegate to the frame constructor, + // which consumes the new style and expects the old style to be on the frame. + // + // XXXbholley: We should teach the frame constructor how to clear the dirty + // descendants bit to avoid the traversal here. + if (changeHint & nsChangeHint_ReconstructFrame) { + ClearDirtyDescendantsFromSubtree(aElement); return; } - Element* element = aContent->AsElement(); - if (element->IsDirtyForServo()) { - RefPtr<ServoComputedValues> computedValues = - Servo_ComputedValues_Get(aContent).Consume(); - MOZ_ASSERT(computedValues); - - nsChangeHint changeHint = nsChangeHint(0); - - // Add an explicit change hint if appropriate. - ServoElementSnapshot* snapshot; - if (mModifiedElements.Get(element, &snapshot)) { - changeHint |= snapshot->ExplicitChangeHint(); - } - - // Add the stored change hint if there's a frame. If there isn't a frame, - // generate a ReconstructFrame change hint if the new display value - // (which we can get from the ComputedValues stored on the node) is not - // none. - if (primaryFrame) { - changeHint |= primaryFrame->StyleContext()->ConsumeStoredChangeHint(); - } else { - const nsStyleDisplay* currentDisplay = - Servo_GetStyleDisplay(computedValues); - if (currentDisplay->mDisplay != StyleDisplay::None) { - changeHint |= nsChangeHint_ReconstructFrame; - } - } - - // Add the new change hint to the list of elements to process if - // we need to do any work. - if (changeHint) { - aChangeListToProcess.AppendChange(primaryFrame, element, changeHint); - } - - // The frame reconstruction step (if needed) will ask for the descendants' - // style correctly. If not needed, we're done too. - // - // Note that we must leave the old style on an existing frame that is - // about to be reframed, since some frame constructor code wants to - // inspect the old style to work out what to do. - if (changeHint & nsChangeHint_ReconstructFrame) { - // Since we might still have some dirty bits set on descendants, - // inconsistent with the clearing of HasDirtyDescendants we will do as - // we return from these recursive RecreateStyleContexts calls, we - // explicitly clear them here. Otherwise we will trigger assertions - // when we soon process the frame reconstruction. - MarkSelfAndDescendantsAsNotDirtyForServo(element); - return; - } - - // If there is no frame, and we didn't generate a ReconstructFrame change - // hint, then we don't need to do any more work. - if (!primaryFrame) { - aContent->UnsetIsDirtyForServo(); - return; - } + // If we have a frame and a non-zero + non-reconstruct change hint, we need to + // attach a new style context. + bool recreateContext = primaryFrame && changeHint; + if (recreateContext) { + RefPtr<ServoComputedValues> computedValues + = Servo_ResolveStyle(aElement, aStyleSet->mRawSet.get(), + ConsumeStyleBehavior::Consume, + LazyComputeBehavior::Assert).Consume(); // Hold the old style context alive, because it could become a dangling // pointer during the replacement. In practice it's not a huge deal (on // GetNextContinuationWithSameStyle the pointer is not dereferenced, only // compared), but better not playing with dangling pointers if not needed. RefPtr<nsStyleContext> oldStyleContext = primaryFrame->StyleContext(); MOZ_ASSERT(oldStyleContext); @@ -214,79 +168,78 @@ ServoRestyleManager::RecreateStyleContex const static CSSPseudoElementType pseudosToRestyle[] = { CSSPseudoElementType::before, CSSPseudoElementType::after, }; for (CSSPseudoElementType pseudoType : pseudosToRestyle) { nsIAtom* pseudoTag = nsCSSPseudoElements::GetPseudoAtom(pseudoType); - if (nsIFrame* pseudoFrame = FrameForPseudoElement(element, pseudoTag)) { + if (nsIFrame* pseudoFrame = FrameForPseudoElement(aElement, pseudoTag)) { // TODO: we could maybe make this more performant via calling into // Servo just once to know which pseudo-elements we've got to restyle? RefPtr<nsStyleContext> pseudoContext = - aStyleSet->ProbePseudoElementStyle(element, pseudoType, newContext); - - // If pseudoContext is null here, it means the frame is going away, so - // our change hint computation should have already indicated we need - // to reframe. - MOZ_ASSERT_IF(!pseudoContext, - changeHint & nsChangeHint_ReconstructFrame); - if (pseudoContext) { - pseudoFrame->SetStyleContext(pseudoContext); + aStyleSet->ProbePseudoElementStyle(aElement, pseudoType, newContext); + MOZ_ASSERT(pseudoContext, "should have taken the ReconstructFrame path above"); + pseudoFrame->SetStyleContext(pseudoContext); - // We only care restyling text nodes, since other type of nodes - // (images), are still not supported. If that eventually changes, we - // may have to write more code here... Or not, I don't think too - // many inherited properties can affect those other frames. - StyleChildrenIterator it(pseudoFrame->GetContent()); - for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) { - if (n->IsNodeOfType(nsINode::eTEXT)) { - RefPtr<nsStyleContext> childContext = - aStyleSet->ResolveStyleForText(n, pseudoContext); - MOZ_ASSERT(n->GetPrimaryFrame(), - "How? This node is created at FC time!"); - n->GetPrimaryFrame()->SetStyleContext(childContext); - } + // We only care restyling text nodes, since other type of nodes + // (images), are still not supported. If that eventually changes, we + // may have to write more code here... Or not, I don't think too + // many inherited properties can affect those other frames. + StyleChildrenIterator it(pseudoFrame->GetContent()); + for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) { + if (n->IsNodeOfType(nsINode::eTEXT)) { + RefPtr<nsStyleContext> childContext = + aStyleSet->ResolveStyleForText(n, pseudoContext); + MOZ_ASSERT(n->GetPrimaryFrame(), + "How? This node is created at FC time!"); + n->GetPrimaryFrame()->SetStyleContext(childContext); } } } } - - aContent->UnsetIsDirtyForServo(); } - if (aContent->HasDirtyDescendantsForServo()) { - MOZ_ASSERT(primaryFrame, - "Frame construction should be scheduled, and it takes the " - "correct style for the children, so no need to be here."); - StyleChildrenIterator it(aContent); + bool traverseElementChildren = aElement->HasDirtyDescendantsForServo(); + bool traverseTextChildren = recreateContext; + if (traverseElementChildren || traverseTextChildren) { + StyleChildrenIterator it(aElement); for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) { - if (n->IsElement() || n->IsNodeOfType(nsINode::eTEXT)) { - RecreateStyleContexts(n, primaryFrame->StyleContext(), + if (traverseElementChildren && n->IsElement()) { + MOZ_ASSERT(primaryFrame, + "Frame construction should be scheduled, and it takes the " + "correct style for the children, so no need to be here."); + RecreateStyleContexts(n->AsElement(), primaryFrame->StyleContext(), aStyleSet, aChangeListToProcess); + } else if (traverseTextChildren && n->IsNodeOfType(nsINode::eTEXT)) { + RecreateStyleContextsForText(n, primaryFrame->StyleContext(), + aStyleSet); } } - aContent->UnsetHasDirtyDescendantsForServo(); } + + aElement->UnsetHasDirtyDescendantsForServo(); } -static void -MarkChildrenAsDirtyForServo(nsIContent* aContent) +void +ServoRestyleManager::RecreateStyleContextsForText(nsIContent* aTextNode, + nsStyleContext* aParentContext, + ServoStyleSet* aStyleSet) { - StyleChildrenIterator it(aContent); + nsIFrame* primaryFrame = aTextNode->GetPrimaryFrame(); + if (primaryFrame) { + RefPtr<nsStyleContext> oldStyleContext = primaryFrame->StyleContext(); + RefPtr<nsStyleContext> newContext = + aStyleSet->ResolveStyleForText(aTextNode, aParentContext); - nsIContent* n = it.GetNextChild(); - bool hadChildren = bool(n); - for (; n; n = it.GetNextChild()) { - n->SetIsDirtyForServo(); - } - - if (hadChildren) { - aContent->SetHasDirtyDescendantsForServo(); + for (nsIFrame* f = primaryFrame; f; + f = GetNextContinuationWithSameStyle(f, oldStyleContext)) { + f->SetStyleContext(newContext); + } } } /* static */ nsIFrame* ServoRestyleManager::FrameForPseudoElement(const nsIContent* aContent, nsIAtom* aPseudoTagOrNull) { MOZ_ASSERT_IF(aPseudoTagOrNull, aContent->IsElement()); @@ -310,53 +263,16 @@ ServoRestyleManager::FrameForPseudoEleme return nsLayoutUtils::GetAfterFrameForContent(primaryFrame, aContent); } MOZ_CRASH("Unkown pseudo-element given to " "ServoRestyleManager::FrameForPseudoElement"); return nullptr; } -/* static */ void -ServoRestyleManager::NoteRestyleHint(Element* aElement, nsRestyleHint aHint) -{ - const nsRestyleHint HANDLED_RESTYLE_HINTS = eRestyle_Self | - eRestyle_Subtree | - eRestyle_LaterSiblings | - eRestyle_SomeDescendants; - // NB: For Servo, at least for now, restyling and running selector-matching - // against the subtree is necessary as part of restyling the element, so - // processing eRestyle_Self will perform at least as much work as - // eRestyle_Subtree. - if (aHint & (eRestyle_Self | eRestyle_Subtree)) { - aElement->SetIsDirtyForServo(); - aElement->MarkAncestorsAsHavingDirtyDescendantsForServo(); - // NB: Servo gives us a eRestyle_SomeDescendants when it expects us to run - // selector matching on all the descendants. There's a bug on Servo to align - // meanings here (#12710) to avoid this potential source of confusion. - } else if (aHint & eRestyle_SomeDescendants) { - MarkChildrenAsDirtyForServo(aElement); - aElement->MarkAncestorsAsHavingDirtyDescendantsForServo(); - } - - if (aHint & eRestyle_LaterSiblings) { - aElement->MarkAncestorsAsHavingDirtyDescendantsForServo(); - for (nsIContent* cur = aElement->GetNextSibling(); cur; - cur = cur->GetNextSibling()) { - cur->SetIsDirtyForServo(); - } - } - - // TODO: Handle all other nsRestyleHint values. - if (aHint & ~HANDLED_RESTYLE_HINTS) { - NS_WARNING(nsPrintfCString("stylo: Unhandled restyle hint %s", - RestyleManagerBase::RestyleHintToString(aHint).get()).get()); - } -} - void ServoRestyleManager::ProcessPendingRestyles() { MOZ_ASSERT(PresContext()->Document(), "No document? Pshaw!"); MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(), "Missing a script blocker!"); if (MOZ_UNLIKELY(!PresContext()->PresShell()->DidInitialize())) { // PresShell::FlushPendingNotifications doesn't early-return in the case @@ -369,77 +285,59 @@ ServoRestyleManager::ProcessPendingResty if (!HasPendingRestyles()) { return; } ServoStyleSet* styleSet = StyleSet(); nsIDocument* doc = PresContext()->Document(); Element* root = doc->GetRootElement(); - if (root) { - // ProcessPendingRestyles can generate new restyles (e.g. from the - // frame constructor if it decides that a ReconstructFrame change must - // apply to the parent of the element that generated that hint). So - // we loop while mModifiedElements still has some restyles in it, clearing - // it after each RecreateStyleContexts call below. - while (!mModifiedElements.IsEmpty()) { - for (auto iter = mModifiedElements.Iter(); !iter.Done(); iter.Next()) { - ServoElementSnapshot* snapshot = iter.UserData(); - Element* element = iter.Key(); + + // XXXbholley: Should this be while() per bug 1316247? + if (HasPendingRestyles()) { + MOZ_ASSERT(root); + mInStyleRefresh = true; + styleSet->StyleDocument(); - // The element is no longer in the document, so don't bother computing - // a final restyle hint for it. - // - // XXXheycam RestyleTracker checks that the element's GetComposedDoc() - // matches the document we're restyling. Do we need to do that too? - if (!element->IsInComposedDoc()) { - continue; - } + // First do any queued-up frame creation. (see bugs 827239 and 997506). + // + // XXXEmilio I'm calling this to avoid random behavior changes, since we + // delay frame construction after styling we should re-check once our + // model is more stable whether we can skip this call. + // + // Note this has to be *after* restyling, because otherwise frame + // construction will find unstyled nodes, and that's not funny. + PresContext()->FrameConstructor()->CreateNeededFrames(); - // TODO: avoid the ComputeRestyleHint call if we already have the highest - // explicit restyle hint? - nsRestyleHint hint = styleSet->ComputeRestyleHint(element, snapshot); - hint |= snapshot->ExplicitRestyleHint(); - - if (hint) { - NoteRestyleHint(element, hint); - } - } + // Recreate style contexts and queue up change hints. + nsStyleChangeList currentChanges; + RecreateStyleContexts(root, nullptr, styleSet, currentChanges); - if (!root->IsDirtyForServo() && !root->HasDirtyDescendantsForServo()) { - mModifiedElements.Clear(); - break; + // Process the change hints. + // + // Unfortunately, the frame constructor can generate new change hints while + // processing existing ones. We redirect those into a secondary queue and + // iterate until there's nothing left. + ReentrantChangeList newChanges; + mReentrantChanges = &newChanges; + while (!currentChanges.IsEmpty()) { + ProcessRestyledFrames(currentChanges); + MOZ_ASSERT(currentChanges.IsEmpty()); + for (ReentrantChange& change: newChanges) { + currentChanges.AppendChange(change.mContent->GetPrimaryFrame(), + change.mContent, change.mHint); } - - mInStyleRefresh = true; - styleSet->StyleDocument(/* aLeaveDirtyBits = */ true); + newChanges.Clear(); + } + mReentrantChanges = nullptr; - // First do any queued-up frame creation. (see bugs 827239 and 997506). - // - // XXXEmilio I'm calling this to avoid random behavior changes, since we - // delay frame construction after styling we should re-check once our - // model is more stable whether we can skip this call. - // - // Note this has to be *after* restyling, because otherwise frame - // construction will find unstyled nodes, and that's not funny. - PresContext()->FrameConstructor()->CreateNeededFrames(); - - nsStyleChangeList changeList; - RecreateStyleContexts(root, nullptr, styleSet, changeList); - - mModifiedElements.Clear(); - ProcessRestyledFrames(changeList); - - mInStyleRefresh = false; - } + styleSet->AssertTreeIsClean(); + mInStyleRefresh = false; } - MOZ_ASSERT(!doc->IsDirtyForServo()); - doc->UnsetHasDirtyDescendantsForServo(); - IncrementRestyleGeneration(); } void ServoRestyleManager::RestyleForInsertOrChange(nsINode* aContainer, nsIContent* aChild) { // @@ -449,41 +347,16 @@ ServoRestyleManager::RestyleForInsertOrC // // Bug 1297899 tracks this work. // } void ServoRestyleManager::ContentInserted(nsINode* aContainer, nsIContent* aChild) { - if (aContainer == aContainer->OwnerDoc()) { - // If we're getting this notification for the insertion of a root element, - // that means either: - // (a) We initialized the PresShell before the root element existed, or - // (b) The root element was removed and it or another root is being - // inserted. - // - // Either way the whole tree is dirty, so we should style the document. - MOZ_ASSERT(aChild == aChild->OwnerDoc()->GetRootElement()); - MOZ_ASSERT(aChild->IsDirtyForServo()); - StyleSet()->StyleDocument(/* aLeaveDirtyBits = */ false); - return; - } - - if (!aContainer->HasServoData()) { - // This can happen with display:none. Bug 1297249 tracks more investigation - // and assertions here. - return; - } - - // Style the new subtree because we will most likely need it during subsequent - // frame construction. Bug 1298281 tracks deferring this work in the lazy - // frame construction case. - StyleSet()->StyleNewSubtree(aChild); - RestyleForInsertOrChange(aContainer, aChild); } void ServoRestyleManager::RestyleForAppend(nsIContent* aContainer, nsIContent* aFirstNewContent) { // @@ -494,32 +367,16 @@ ServoRestyleManager::RestyleForAppend(ns // Bug 1297899 tracks this work. // } void ServoRestyleManager::ContentAppended(nsIContent* aContainer, nsIContent* aFirstNewContent) { - if (!aContainer->HasServoData()) { - // This can happen with display:none. Bug 1297249 tracks more investigation - // and assertions here. - return; - } - - // Style the new subtree because we will most likely need it during subsequent - // frame construction. Bug 1298281 tracks deferring this work in the lazy - // frame construction case. - if (aFirstNewContent->GetNextSibling()) { - aContainer->SetHasDirtyDescendantsForServo(); - StyleSet()->StyleNewChildren(aContainer); - } else { - StyleSet()->StyleNewSubtree(aFirstNewContent); - } - RestyleForAppend(aContainer, aFirstNewContent); } void ServoRestyleManager::ContentRemoved(nsINode* aContainer, nsIContent* aOldChild, nsIContent* aFollowingSibling) { @@ -556,52 +413,51 @@ ServoRestyleManager::ContentStateChanged // PostRestyleEvent. // // If we definitely take the snapshot approach, we should take rid of // HasStateDependentStyle, etc (though right now they're no-ops). ContentStateChangedInternal(aElement, aChangedBits, &changeHint, &restyleHint); EventStates previousState = aElement->StyleState() ^ aChangedBits; - ServoElementSnapshot* snapshot = SnapshotForElement(aElement); - snapshot->AddState(previousState); + ServoElementSnapshot* snapshot = Servo_Element_GetSnapshot(aElement); + if (snapshot) { + snapshot->AddState(previousState); + PostRestyleEvent(aElement, restyleHint, changeHint); + } - PostRestyleEvent(aElement, restyleHint, changeHint); return NS_OK; } void ServoRestyleManager::AttributeWillChange(Element* aElement, int32_t aNameSpaceID, nsIAtom* aAttribute, int32_t aModType, const nsAttrValue* aNewValue) { - ServoElementSnapshot* snapshot = SnapshotForElement(aElement); - snapshot->AddAttrs(aElement); + ServoElementSnapshot* snapshot = Servo_Element_GetSnapshot(aElement); + if (snapshot) { + snapshot->AddAttrs(aElement); + } } void ServoRestyleManager::AttributeChanged(Element* aElement, int32_t aNameSpaceID, nsIAtom* aAttribute, int32_t aModType, const nsAttrValue* aOldValue) { - MOZ_ASSERT(SnapshotForElement(aElement)->HasAttrs()); +#ifdef DEBUG + ServoElementSnapshot* snapshot = Servo_Element_GetSnapshot(aElement); + MOZ_ASSERT_IF(snapshot, snapshot->HasAttrs()); +#endif if (aAttribute == nsGkAtoms::style) { PostRestyleEvent(aElement, eRestyle_StyleAttribute, nsChangeHint(0)); } } nsresult ServoRestyleManager::ReparentStyleContext(nsIFrame* aFrame) { NS_WARNING("stylo: ServoRestyleManager::ReparentStyleContext not implemented"); return NS_OK; } -ServoElementSnapshot* -ServoRestyleManager::SnapshotForElement(Element* aElement) -{ - // NB: aElement is the argument for the construction of the snapshot in the - // not found case. - return mModifiedElements.LookupOrAdd(aElement, aElement); -} - } // namespace mozilla
--- a/layout/base/ServoRestyleManager.h +++ b/layout/base/ServoRestyleManager.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_ServoRestyleManager_h #define mozilla_ServoRestyleManager_h #include "mozilla/EventStates.h" #include "mozilla/RestyleManagerBase.h" +#include "mozilla/ServoBindings.h" #include "mozilla/ServoElementSnapshot.h" #include "nsChangeHint.h" #include "nsHashKeys.h" #include "nsINode.h" #include "nsISupportsImpl.h" #include "nsPresContext.h" namespace mozilla { @@ -74,67 +75,76 @@ public: void AttributeChanged(dom::Element* aElement, int32_t aNameSpaceID, nsIAtom* aAttribute, int32_t aModType, const nsAttrValue* aOldValue); nsresult ReparentStyleContext(nsIFrame* aFrame); bool HasPendingRestyles() { - return !mModifiedElements.IsEmpty() || - PresContext()->Document()->HasDirtyDescendantsForServo(); + Element* root = PresContext()->Document()->GetRootElement(); + return root && root->HasDirtyDescendantsForServo(); } /** * Gets the appropriate frame given a content and a pseudo-element tag. * * Right now only supports a null tag, before or after. If the pseudo-element * is not null, the content needs to be an element. */ static nsIFrame* FrameForPseudoElement(const nsIContent* aContent, nsIAtom* aPseudoTagOrNull); /** - * Clears the ServoNodeData from all content nodes in the subtree rooted - * at aContent, and sets the IsDirtyForServo bit and clears the - * HasDirtyDescendantsForServo bit on them too. + * Clears the ServoElementData and HasDirtyDescendants from all elements + * in the subtree rooted at aElement. */ - static void ClearServoDataFromSubtree(nsIContent* aContent); + static void ClearServoDataFromSubtree(Element* aElement); + + /** + * Clears HasDirtyDescendants from all elements in the subtree rooted at + * aElement. + */ + static void ClearDirtyDescendantsFromSubtree(Element* aElement); protected: - ~ServoRestyleManager() {} + ~ServoRestyleManager() { MOZ_ASSERT(!mReentrantChanges); } private: - ServoElementSnapshot* SnapshotForElement(Element* aElement); - - /** - * The element-to-element snapshot table to compute restyle hints. - */ - nsClassHashtable<nsRefPtrHashKey<Element>, ServoElementSnapshot> - mModifiedElements; - /** * Traverses a tree of content that Servo has just restyled, recreating style * contexts for their frames with the new style data. */ - void RecreateStyleContexts(nsIContent* aContent, + void RecreateStyleContexts(Element* aElement, nsStyleContext* aParentContext, ServoStyleSet* aStyleSet, nsStyleChangeList& aChangeList); - /** - * Marks the tree with the appropriate dirty flags for the given restyle hint. - */ - static void NoteRestyleHint(Element* aElement, nsRestyleHint aRestyleHint); + void RecreateStyleContextsForText(nsIContent* aTextNode, + nsStyleContext* aParentContext, + ServoStyleSet* aStyleSet); inline ServoStyleSet* StyleSet() const { MOZ_ASSERT(PresContext()->StyleSet()->IsServo(), "ServoRestyleManager should only be used with a Servo-flavored " "style backend"); return PresContext()->StyleSet()->AsServo(); } + + // We use a separate data structure from nsStyleChangeList because we need a + // frame to create nsStyleChangeList entries, and the primary frame may not be + // attached yet. + struct ReentrantChange { + nsCOMPtr<nsIContent> mContent; + nsChangeHint mHint; + }; + typedef AutoTArray<ReentrantChange, 10> ReentrantChangeList; + + // Only non-null while processing change hints. See the comment in + // ProcessPendingRestyles. + ReentrantChangeList* mReentrantChanges; }; } // namespace mozilla #endif // mozilla_ServoRestyleManager_h
--- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -2424,17 +2424,19 @@ nsCSSFrameConstructor::ConstructDocEleme aDocElement->UnsetRestyleFlagsIfGecko(); // --------- CREATE AREA OR BOX FRAME ------- // FIXME: Should this use ResolveStyleContext? (The calls in this // function are the only case in nsCSSFrameConstructor where we don't // do so for the construction of a style context for an element.) RefPtr<nsStyleContext> styleContext; styleContext = mPresShell->StyleSet()->ResolveStyleFor(aDocElement, - nullptr); + nullptr, + ConsumeStyleBehavior::Consume, + LazyComputeBehavior::Allow); const nsStyleDisplay* display = styleContext->StyleDisplay(); // Ensure that our XBL bindings are installed. if (display->mBinding) { // Get the XBL loader. nsresult rv; bool resolveStyle; @@ -2459,21 +2461,29 @@ nsCSSFrameConstructor::ConstructDocEleme } if (resolveStyle) { // FIXME: Should this use ResolveStyleContext? (The calls in this // function are the only case in nsCSSFrameConstructor where we // don't do so for the construction of a style context for an // element.) styleContext = mPresShell->StyleSet()->ResolveStyleFor(aDocElement, - nullptr); + nullptr, + ConsumeStyleBehavior::Consume, + LazyComputeBehavior::Allow); display = styleContext->StyleDisplay(); } } + // We delay traversing the entire document until here, since we per above we + // may invalidate the root style when we load doc stylesheets. + if (ServoStyleSet* set = mPresShell->StyleSet()->GetAsServo()) { + set->StyleDocument(); + } + // --------- IF SCROLLABLE WRAP IN SCROLLFRAME -------- NS_ASSERTION(!display->IsScrollableOverflow() || state.mPresContext->IsPaginated() || propagatedScrollFrom == aDocElement, "Scrollbars should have been propagated to the viewport"); if (MOZ_UNLIKELY(display->mDisplay == StyleDisplay::None)) { @@ -5017,20 +5027,24 @@ nsCSSFrameConstructor::ResolveStyleConte StyleSetHandle styleSet = mPresShell->StyleSet(); aContent->OwnerDoc()->FlushPendingLinkUpdates(); RefPtr<nsStyleContext> result; if (aContent->IsElement()) { if (aState) { result = styleSet->ResolveStyleFor(aContent->AsElement(), aParentStyleContext, + ConsumeStyleBehavior::Consume, + LazyComputeBehavior::Assert, aState->mTreeMatchContext); } else { result = styleSet->ResolveStyleFor(aContent->AsElement(), - aParentStyleContext); + aParentStyleContext, + ConsumeStyleBehavior::Consume, + LazyComputeBehavior::Assert); } } else { NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT), "shouldn't waste time creating style contexts for " "comments and processing instructions"); result = styleSet->ResolveStyleForText(aContent, aParentStyleContext); } @@ -5708,16 +5722,21 @@ nsCSSFrameConstructor::AddFrameConstruct return; if (newPendingBinding->mBinding) { pendingBinding = newPendingBinding; // aState takes over owning newPendingBinding aState.AddPendingBinding(newPendingBinding.forget()); } + if (aContent->IsStyledByServo()) { + NS_WARNING("stylo: Skipping Unsupported binding re-resolve. This needs fixing."); + resolveStyle = false; + } + if (resolveStyle) { styleContext = ResolveStyleContext(styleContext->GetParent(), aContent, &aState); display = styleContext->StyleDisplay(); aStyleContext = styleContext; } aTag = mDocument->BindingManager()->ResolveTag(aContent, &aNameSpaceID); @@ -7337,19 +7356,38 @@ nsCSSFrameConstructor::ContentAppended(n // See comment in ContentRangeInserted for why this is necessary. if (!GetContentInsertionFrameFor(aContainer) && !aContainer->IsActiveChildrenElement()) { return NS_OK; } if (aAllowLazyConstruction && MaybeConstructLazily(CONTENTAPPEND, aContainer, aFirstNewContent)) { + if (aContainer->IsStyledByServo()) { + aContainer->AsElement()->NoteDirtyDescendantsForServo(); + } return NS_OK; } + // We couldn't construct lazily. Make Servo eagerly traverse the subtree. + if (ServoStyleSet* set = mPresShell->StyleSet()->GetAsServo()) { + // We use the same codepaths to handle both of the following cases: + // (a) Newly-appended content for which lazy frame construction is disallowed. + // (b) Lazy frame construction driven by the restyle manager. + // We need the styles for (a). In the case of (b), the Servo traversal has + // already happened, so we don't need to do it again. + if (!RestyleManager()->AsBase()->IsInStyleRefresh()) { + if (aFirstNewContent->GetNextSibling()) { + set->StyleNewChildren(aContainer); + } else { + set->StyleNewSubtree(aFirstNewContent); + } + } + } + LAYOUT_PHASE_TEMP_EXIT(); InsertionPoint insertion = GetRangeInsertionPoint(aContainer, aFirstNewContent, nullptr, aAllowLazyConstruction); nsContainerFrame*& parentFrame = insertion.mParentFrame; LAYOUT_PHASE_TEMP_REENTER(); if (!parentFrame) { return NS_OK; @@ -7782,20 +7820,35 @@ nsCSSFrameConstructor::ContentRangeInser } // Otherwise, we've got parent content. Find its frame. NS_ASSERTION(!parentFrame || parentFrame->GetContent() == aContainer || GetDisplayContentsStyleFor(aContainer), "New XBL code is possibly wrong!"); if (aAllowLazyConstruction && MaybeConstructLazily(CONTENTINSERT, aContainer, aStartChild)) { + if (aContainer->IsStyledByServo()) { + aContainer->AsElement()->NoteDirtyDescendantsForServo(); + } return NS_OK; } } + // We couldn't construct lazily. Make Servo eagerly traverse the subtree. + if (ServoStyleSet* set = mPresShell->StyleSet()->GetAsServo()) { + // We use the same codepaths to handle both of the following cases: + // (a) Newly-appended content for which lazy frame construction is disallowed. + // (b) Lazy frame construction driven by the restyle manager. + // We need the styles for (a). In the case of (b), the Servo traversal has + // already happened, so we don't need to do it again. + if (!RestyleManager()->AsBase()->IsInStyleRefresh()) { + set->StyleNewSubtree(aStartChild); + } + } + InsertionPoint insertion; if (isSingleInsert) { // See if we have an XBL insertion point. If so, then that's our // real parent frame; if not, then the frame hasn't been built yet // and we just bail. insertion = GetInsertionPoint(aContainer, aStartChild); } else { // Get our insertion point. If we need to issue single ContentInserted's @@ -9250,17 +9303,19 @@ nsCSSFrameConstructor::MaybeRecreateFram if (!oldContext) { return nullptr; } oldDisplay = StyleDisplay::Contents; } // The parent has a frame, so try resolving a new context. RefPtr<nsStyleContext> newContext = mPresShell->StyleSet()-> - ResolveStyleFor(aElement, oldContext->GetParent()); + ResolveStyleFor(aElement, oldContext->GetParent(), + ConsumeStyleBehavior::Consume, + LazyComputeBehavior::Assert); if (oldDisplay == StyleDisplay::None) { ChangeUndisplayedContent(aElement, newContext); } else { ChangeDisplayContents(aElement, newContext); } const nsStyleDisplay* disp = newContext->StyleDisplay(); @@ -10622,24 +10677,25 @@ nsCSSFrameConstructor::AddFCItemsForAnon parentDisplayBasedStyleFixupSkipper(aState.mTreeMatchContext); if (aAnonymousItems[i].mStyleContext) { // If we have an explicit style context, that means that the anonymous // content creator had its own plan for the style, and doesn't need the // computed style obtained by cascading this content as a normal node. // This happens when a native anonymous node is used to implement a // pseudo-element. Allowing Servo to traverse these nodes would be wasted // work, so assert that we didn't do that. - MOZ_ASSERT_IF(content->IsStyledByServo(), !content->HasServoData()); + MOZ_ASSERT_IF(content->IsStyledByServo(), + !content->IsElement() || !content->AsElement()->HasServoData()); styleContext = aAnonymousItems[i].mStyleContext.forget(); } else { // If we don't have an explicit style context, that means we need the // ordinary computed values. Make sure we eagerly cascaded them when the // anonymous nodes were created. MOZ_ASSERT_IF(content->IsStyledByServo() && content->IsElement(), - content->HasServoData()); + content->AsElement()->HasServoData()); styleContext = ResolveStyleContext(aFrame, content, &aState); } nsTArray<nsIAnonymousContentCreator::ContentInfo>* anonChildren = nullptr; if (!aAnonymousItems[i].mChildren.IsEmpty()) { anonChildren = &aAnonymousItems[i].mChildren; }
--- a/layout/base/nsChangeHint.h +++ b/layout/base/nsChangeHint.h @@ -12,16 +12,18 @@ #include "nsDebug.h" #include "nsTArray.h" struct nsCSSSelector; // Defines for various style related constants enum nsChangeHint { + nsChangeHint_Empty = 0, + // change was visual only (e.g., COLOR=) // Invalidates all descendant frames (including following // placeholders to out-of-flow frames). nsChangeHint_RepaintFrame = 1 << 0, // For reflow, we want flags to give us arbitrary FrameNeedsReflow behavior. // just do a FrameNeedsReflow. nsChangeHint_NeedReflow = 1 << 1,
--- a/layout/base/nsDocumentViewer.cpp +++ b/layout/base/nsDocumentViewer.cpp @@ -1,16 +1,17 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 sw=2 et 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/. */ /* container for a document and its presentation */ +#include "mozilla/ServoRestyleManager.h" #include "mozilla/ServoStyleSet.h" #include "nsAutoPtr.h" #include "nscore.h" #include "nsCOMPtr.h" #include "nsCRT.h" #include "nsString.h" #include "nsReadableUtils.h" #include "nsIContent.h" @@ -4464,16 +4465,25 @@ NS_IMETHODIMP nsDocumentViewer::SetPageM if (mPresContext) { DestroyPresContext(); } mViewManager = nullptr; mWindow = nullptr; + // We're creating a new presentation context for an existing document. + // Drop any associated Servo data. +#ifdef MOZ_STYLO + Element* root = mDocument->GetRootElement(); + if (root && root->IsStyledByServo()) { + ServoRestyleManager::ClearServoDataFromSubtree(root); + } +#endif + NS_ENSURE_STATE(mDocument); if (aPageMode) { mPresContext = CreatePresContext(mDocument, nsPresContext::eContext_PageLayout, FindContainerView()); NS_ENSURE_TRUE(mPresContext, NS_ERROR_OUT_OF_MEMORY); mPresContext->SetPaginatedScrolling(true); mPresContext->SetPrintSettings(aPrintSettings);
--- a/layout/base/nsPresContext.cpp +++ b/layout/base/nsPresContext.cpp @@ -1379,17 +1379,19 @@ GetPropagatedScrollbarStylesForViewport( // docElement might be null if we're doing this after removing it. if (!docElement) { return nullptr; } // Check the style on the document root element StyleSetHandle styleSet = aPresContext->StyleSet(); RefPtr<nsStyleContext> rootStyle; - rootStyle = styleSet->ResolveStyleFor(docElement, nullptr); + rootStyle = styleSet->ResolveStyleFor(docElement, nullptr, + ConsumeStyleBehavior::DontConsume, + LazyComputeBehavior::Allow); if (CheckOverflow(rootStyle->StyleDisplay(), aStyles)) { // tell caller we stole the overflow style from the root element return docElement; } // Don't look in the BODY for non-HTML documents or HTML documents // with non-HTML roots // XXX this should be earlier; we shouldn't even look at the document root @@ -1407,17 +1409,19 @@ GetPropagatedScrollbarStylesForViewport( if (!bodyElement || !bodyElement->NodeInfo()->Equals(nsGkAtoms::body)) { // The body is not a <body> tag, it's a <frameset>. return nullptr; } RefPtr<nsStyleContext> bodyStyle; - bodyStyle = styleSet->ResolveStyleFor(bodyElement->AsElement(), rootStyle); + bodyStyle = styleSet->ResolveStyleFor(bodyElement->AsElement(), rootStyle, + ConsumeStyleBehavior::DontConsume, + LazyComputeBehavior::Allow); if (CheckOverflow(bodyStyle->StyleDisplay(), aStyles)) { // tell caller we stole the overflow style from the body element return bodyElement; } return nullptr; }
--- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -1687,27 +1687,16 @@ PresShell::Initialize(nscoord aWidth, ns } #endif // XXX Do a full invalidate at the beginning so that invalidates along // the way don't have region accumulation issues? mPresContext->SetVisibleArea(nsRect(0, 0, aWidth, aHeight)); - if (mStyleSet->IsServo() && mDocument->GetRootElement()) { - // If we have the root element already, go ahead style it along with any - // descendants. - // - // Some things, like nsDocumentViewer::GetPageMode, recreate the PresShell - // while keeping the content tree alive (see bug 1292280) - so we - // unconditionally mark the root as dirty. - mDocument->GetRootElement()->SetIsDirtyForServo(); - mStyleSet->AsServo()->StyleDocument(/* aLeaveDirtyBits = */ false); - } - // Get the root frame from the frame manager // XXXbz it would be nice to move this somewhere else... like frame manager // Init(), say. But we need to make sure our views are all set up by the // time we do this! nsIFrame* rootFrame = mFrameConstructor->GetRootFrame(); NS_ASSERTION(!rootFrame, "How did that happen, exactly?"); if (!rootFrame) { nsAutoScriptBlocker scriptBlocker;
--- a/layout/generic/nsFrameSetFrame.cpp +++ b/layout/generic/nsFrameSetFrame.cpp @@ -297,17 +297,19 @@ nsHTMLFramesetFrame::Init(nsIContent* // nsCSSFrameConstructor::ContentAppended/Inserted/Removed if (!child->IsHTMLElement()) continue; if (child->IsAnyOfHTMLElements(nsGkAtoms::frameset, nsGkAtoms::frame)) { RefPtr<nsStyleContext> kidSC; kidSC = shell->StyleSet()->ResolveStyleFor(child->AsElement(), - mStyleContext); + mStyleContext, + ConsumeStyleBehavior::DontConsume, + LazyComputeBehavior::Allow); if (child->IsHTMLElement(nsGkAtoms::frameset)) { frame = NS_NewHTMLFramesetFrame(shell, kidSC); nsHTMLFramesetFrame* childFrame = (nsHTMLFramesetFrame*)frame; childFrame->SetParentFrameborder(frameborder); childFrame->SetParentBorderWidth(borderWidth); childFrame->SetParentBorderColor(borderColor); frame->Init(child, this, nullptr);
--- a/layout/generic/nsImageFrame.cpp +++ b/layout/generic/nsImageFrame.cpp @@ -2053,17 +2053,19 @@ nsImageFrame::GetCursor(const nsPoint& a if (area) { // Use the cursor from the style of the *area* element. // XXX Using the image as the parent style context isn't // technically correct, but it's probably the right thing to do // here, since it means that areas on which the cursor isn't // specified will inherit the style from the image. RefPtr<nsStyleContext> areaStyle = PresContext()->PresShell()->StyleSet()-> - ResolveStyleFor(area->AsElement(), StyleContext()); + ResolveStyleFor(area->AsElement(), StyleContext(), + ConsumeStyleBehavior::DontConsume, + LazyComputeBehavior::Allow); FillCursorInformationFromStyle(areaStyle->StyleUserInterface(), aCursor); if (NS_STYLE_CURSOR_AUTO == aCursor.mCursor) { aCursor.mCursor = NS_STYLE_CURSOR_DEFAULT; } return NS_OK; } }
--- a/layout/style/ServoBindingList.h +++ b/layout/style/ServoBindingList.h @@ -13,18 +13,18 @@ * - 'return_' the return type of the binding function * and the parameter list of the function. * * Users of this list should define a macro * SERVO_BINDING_FUNC(name_, return_, ...) * before including this file. */ -// Node data -SERVO_BINDING_FUNC(Servo_Node_ClearNodeData, void, RawGeckoNodeBorrowed node) +// Element data +SERVO_BINDING_FUNC(Servo_Element_ClearData, void, RawGeckoElementBorrowed node) // Styleset and Stylesheet management SERVO_BINDING_FUNC(Servo_StyleSheet_Empty, RawServoStyleSheetStrong, mozilla::css::SheetParsingMode parsing_mode) SERVO_BINDING_FUNC(Servo_StyleSheet_FromUTF8Bytes, RawServoStyleSheetStrong, const nsACString* data, mozilla::css::SheetParsingMode parsing_mode, const nsACString* base_url, @@ -128,18 +128,16 @@ SERVO_BINDING_FUNC(Servo_DeclarationBloc RawServoDeclarationBlockBorrowed declarations, nsIAtom* property, bool is_custom) // CSS supports() SERVO_BINDING_FUNC(Servo_CSSSupports, bool, const nsACString* name, const nsACString* value) // Computed style data -SERVO_BINDING_FUNC(Servo_ComputedValues_Get, ServoComputedValuesStrong, - RawGeckoNodeBorrowed node) SERVO_BINDING_FUNC(Servo_ComputedValues_GetForAnonymousBox, ServoComputedValuesStrong, ServoComputedValuesBorrowedOrNull parent_style_or_null, nsIAtom* pseudoTag, RawServoStyleSetBorrowed set) SERVO_BINDING_FUNC(Servo_ComputedValues_GetForPseudoElement, ServoComputedValuesStrong, ServoComputedValuesBorrowed parent_style, RawGeckoElementBorrowed match_element, nsIAtom* pseudo_tag, @@ -151,24 +149,36 @@ SERVO_BINDING_FUNC(Servo_ComputedValues_ SERVO_BINDING_FUNC(Servo_ComputedValues_Release, void, ServoComputedValuesBorrowed computed_values) // Initialize Servo components. Should be called exactly once at startup. SERVO_BINDING_FUNC(Servo_Initialize, void) // Shut down Servo components. Should be called exactly once at shutdown. SERVO_BINDING_FUNC(Servo_Shutdown, void) -// Restyle hints -SERVO_BINDING_FUNC(Servo_ComputeRestyleHint, nsRestyleHint, - RawGeckoElementBorrowed element, ServoElementSnapshot* snapshot, - RawServoStyleSetBorrowed set) +// Gets the snapshot for the element. This will return null if the element +// has never been styled, since snapshotting in that case is wasted work. +SERVO_BINDING_FUNC(Servo_Element_GetSnapshot, ServoElementSnapshot*, + RawGeckoElementBorrowed element) + +// Restyle and change hints. +SERVO_BINDING_FUNC(Servo_NoteExplicitHints, void, RawGeckoElementBorrowed element, + nsRestyleHint restyle_hint, nsChangeHint change_hint) +SERVO_BINDING_FUNC(Servo_CheckChangeHint, nsChangeHint, RawGeckoElementBorrowed element) +SERVO_BINDING_FUNC(Servo_ResolveStyle, ServoComputedValuesStrong, + RawGeckoElementBorrowed element, RawServoStyleSetBorrowed set, + mozilla::ConsumeStyleBehavior consume, mozilla::LazyComputeBehavior compute) // Restyle the given subtree. -SERVO_BINDING_FUNC(Servo_RestyleSubtree, void, - RawGeckoNodeBorrowed node, RawServoStyleSetBorrowed set) +SERVO_BINDING_FUNC(Servo_TraverseSubtree, void, + RawGeckoElementBorrowed root, RawServoStyleSetBorrowed set, + mozilla::SkipRootBehavior skip_root) + +// Assert that the tree has no pending or unconsumed restyles. +SERVO_BINDING_FUNC(Servo_AssertTreeIsClean, void, RawGeckoElementBorrowed root) // Style-struct management. #define STYLE_STRUCT(name, checkdata_cb) \ struct nsStyle##name; \ SERVO_BINDING_FUNC(Servo_GetStyle##name, const nsStyle##name*, \ ServoComputedValuesBorrowedOrNull computed_values) #include "nsStyleStructList.h" #undef STYLE_STRUCT
--- a/layout/style/ServoBindingTypes.h +++ b/layout/style/ServoBindingTypes.h @@ -13,27 +13,29 @@ struct ServoComputedValues; struct ServoCssRules; struct RawServoStyleSheet; struct RawServoStyleSet; struct RawServoDeclarationBlock; struct RawServoStyleRule; namespace mozilla { + class ServoElementSnapshot; namespace dom { class Element; class StyleChildrenIterator; } // namespace dom } // namespace mozilla class nsCSSValue; class nsIDocument; class nsINode; using mozilla::dom::StyleChildrenIterator; +using mozilla::ServoElementSnapshot; typedef nsINode RawGeckoNode; typedef mozilla::dom::Element RawGeckoElement; typedef nsIDocument RawGeckoDocument; // We have these helper types so that we can directly generate // things like &T or Borrowed<T> on the Rust side in the function, providing // additional safety benefits. @@ -80,29 +82,31 @@ DECL_ARC_REF_TYPE_FOR(RawServoDeclaratio DECL_ARC_REF_TYPE_FOR(RawServoStyleRule) // This is a reference to a reference of RawServoDeclarationBlock, which // corresponds to Option<&Arc<RawServoDeclarationBlock>> in Servo side. DECL_NULLABLE_BORROWED_REF_TYPE_FOR(RawServoDeclarationBlockStrong) DECL_OWNED_REF_TYPE_FOR(RawServoStyleSet) DECL_NULLABLE_OWNED_REF_TYPE_FOR(StyleChildrenIterator) DECL_OWNED_REF_TYPE_FOR(StyleChildrenIterator) +DECL_OWNED_REF_TYPE_FOR(ServoElementSnapshot) // We don't use BorrowedMut because the nodes may alias // Servo itself doesn't directly read or mutate these; // it only asks Gecko to do so. In case we wish to in // the future, we should ensure that things being mutated // are protected from noalias violations by a cell type DECL_BORROWED_REF_TYPE_FOR(RawGeckoNode) DECL_NULLABLE_BORROWED_REF_TYPE_FOR(RawGeckoNode) DECL_BORROWED_REF_TYPE_FOR(RawGeckoElement) DECL_NULLABLE_BORROWED_REF_TYPE_FOR(RawGeckoElement) DECL_BORROWED_REF_TYPE_FOR(RawGeckoDocument) DECL_NULLABLE_BORROWED_REF_TYPE_FOR(RawGeckoDocument) DECL_BORROWED_MUT_REF_TYPE_FOR(StyleChildrenIterator) +DECL_BORROWED_MUT_REF_TYPE_FOR(ServoElementSnapshot) DECL_BORROWED_REF_TYPE_FOR(nsCSSValue) DECL_BORROWED_MUT_REF_TYPE_FOR(nsCSSValue) #undef DECL_ARC_REF_TYPE_FOR #undef DECL_OWNED_REF_TYPE_FOR #undef DECL_NULLABLE_OWNED_REF_TYPE_FOR #undef DECL_BORROWED_REF_TYPE_FOR #undef DECL_NULLABLE_BORROWED_REF_TYPE_FOR
--- a/layout/style/ServoBindings.cpp +++ b/layout/style/ServoBindings.cpp @@ -92,18 +92,17 @@ RawGeckoNodeBorrowedOrNull Gecko_GetNextSibling(RawGeckoNodeBorrowed aNode) { return aNode->GetNextSibling(); } RawGeckoElementBorrowedOrNull Gecko_GetParentElement(RawGeckoElementBorrowed aElement) { - nsINode* parentNode = aElement->GetFlattenedTreeParentNode(); - return parentNode->IsElement() ? parentNode->AsElement() : nullptr; + return aElement->GetFlattenedTreeParentElement(); } RawGeckoElementBorrowedOrNull Gecko_GetFirstChildElement(RawGeckoElementBorrowed aElement) { return aElement->GetFirstElementChild(); } @@ -270,55 +269,28 @@ Gecko_CalcStyleDifference(nsStyleContext aOldStyleContext->CalcStyleDifference(aComputedValues, forDescendants, &equalStructs, &samePointerStructs); return result; } -void -Gecko_StoreStyleDifference(RawGeckoNodeBorrowed aNode, nsChangeHint aChangeHintToStore) +ServoElementSnapshotOwned +Gecko_CreateElementSnapshot(RawGeckoElementBorrowed aElement) { -#ifdef MOZ_STYLO - MOZ_ASSERT(aNode->IsElement()); - MOZ_ASSERT(aNode->IsDirtyForServo(), - "Change hint stored in a not-dirty node"); + return new ServoElementSnapshot(aElement); +} - const Element* aElement = aNode->AsElement(); - nsIFrame* primaryFrame = aElement->GetPrimaryFrame(); - if (!primaryFrame) { - // If there's no primary frame, that means that either this content is - // undisplayed (so we only need to check at the restyling phase for the - // display value on the element), or is a display: contents element. - // - // In this second case, we should store it in the frame constructor display - // contents map. Note that while this operation looks hairy, this would be - // thread-safe because the content should be there already (we'd only need - // to read the map and modify our entry). - // - // That being said, we still don't support display: contents anyway, so it's - // probably not worth it to do all the roundtrip just yet until we have a - // more concrete plan. - return; - } - - if ((aChangeHintToStore & nsChangeHint_ReconstructFrame) && - aNode->IsInNativeAnonymousSubtree()) - { - NS_WARNING("stylo: Removing forbidden frame reconstruction hint on native " - "anonymous content. Fix this in bug 1297857!"); - aChangeHintToStore &= ~nsChangeHint_ReconstructFrame; - } - - primaryFrame->StyleContext()->StoreChangeHint(aChangeHintToStore); -#else - MOZ_CRASH("stylo: Shouldn't call Gecko_StoreStyleDifference in " - "non-stylo build"); -#endif +void +Gecko_DropElementSnapshot(ServoElementSnapshotOwned aSnapshot) +{ + MOZ_ASSERT(NS_IsMainThread(), + "ServoAttrSnapshots can only be dropped on the main thread"); + delete aSnapshot; } RawServoDeclarationBlockStrongBorrowedOrNull Gecko_GetServoDeclarationBlock(RawGeckoElementBorrowed aElement) { const nsAttrValue* attr = aElement->GetParsedAttr(nsGkAtoms::style); if (!attr || attr->Type() != nsAttrValue::eCSSDeclaration) { return nullptr; @@ -576,17 +548,17 @@ ClassOrClassList(Implementor* aElement, } \ uint32_t prefix_##ClassOrClassList(implementor_ aElement, nsIAtom** aClass, \ nsIAtom*** aClassList) \ { \ return ClassOrClassList(aElement, aClass, aClassList); \ } SERVO_IMPL_ELEMENT_ATTR_MATCHING_FUNCTIONS(Gecko_, RawGeckoElementBorrowed) -SERVO_IMPL_ELEMENT_ATTR_MATCHING_FUNCTIONS(Gecko_Snapshot, ServoElementSnapshot*) +SERVO_IMPL_ELEMENT_ATTR_MATCHING_FUNCTIONS(Gecko_Snapshot, const ServoElementSnapshot*) #undef SERVO_IMPL_ELEMENT_ATTR_MATCHING_FUNCTIONS nsIAtom* Gecko_Atomize(const char* aString, uint32_t aLength) { return NS_Atomize(nsDependentCSubstring(aString, aLength)).take(); }
--- a/layout/style/ServoBindings.h +++ b/layout/style/ServoBindings.h @@ -133,17 +133,17 @@ nsIAtom* Gecko_GetElementId(RawGeckoElem nsIAtom* name, nsIAtom* str); \ bool prefix_##AttrHasSuffix(implementor_ element, nsIAtom* ns, \ nsIAtom* name, nsIAtom* str); \ uint32_t prefix_##ClassOrClassList(implementor_ element, nsIAtom** class_, \ nsIAtom*** classList); SERVO_DECLARE_ELEMENT_ATTR_MATCHING_FUNCTIONS(Gecko_, RawGeckoElementBorrowed) SERVO_DECLARE_ELEMENT_ATTR_MATCHING_FUNCTIONS(Gecko_Snapshot, - ServoElementSnapshot*) + const ServoElementSnapshot*) #undef SERVO_DECLARE_ELEMENT_ATTR_MATCHING_FUNCTIONS // Style attributes. RawServoDeclarationBlockStrongBorrowedOrNull Gecko_GetServoDeclarationBlock(RawGeckoElementBorrowed element); // Atoms. @@ -215,27 +215,26 @@ void Gecko_SetMozBinding(nsStyleDisplay* void Gecko_CopyMozBindingFrom(nsStyleDisplay* des, const nsStyleDisplay* src); // Dirtiness tracking. uint32_t Gecko_GetNodeFlags(RawGeckoNodeBorrowed node); void Gecko_SetNodeFlags(RawGeckoNodeBorrowed node, uint32_t flags); void Gecko_UnsetNodeFlags(RawGeckoNodeBorrowed node, uint32_t flags); // Incremental restyle. -// TODO: We would avoid a few ffi calls if we decide to make an API like the -// former CalcAndStoreStyleDifference, but that would effectively mean breaking -// some safety guarantees in the servo side. -// // Also, we might want a ComputedValues to ComputedValues API for animations? // Not if we do them in Gecko... nsStyleContext* Gecko_GetStyleContext(RawGeckoNodeBorrowed node, nsIAtom* aPseudoTagOrNull); nsChangeHint Gecko_CalcStyleDifference(nsStyleContext* oldstyle, ServoComputedValuesBorrowed newstyle); -void Gecko_StoreStyleDifference(RawGeckoNodeBorrowed node, nsChangeHint change); + +// Element snapshot. +ServoElementSnapshotOwned Gecko_CreateElementSnapshot(RawGeckoElementBorrowed element); +void Gecko_DropElementSnapshot(ServoElementSnapshotOwned snapshot); // `array` must be an nsTArray // If changing this signature, please update the // friend function declaration in nsTArray.h void Gecko_EnsureTArrayCapacity(void* array, size_t capacity, size_t elem_size); // Same here, `array` must be an nsTArray<T>, for some T. //
--- a/layout/style/ServoElementSnapshot.cpp +++ b/layout/style/ServoElementSnapshot.cpp @@ -6,28 +6,32 @@ #include "mozilla/ServoElementSnapshot.h" #include "mozilla/dom/Element.h" #include "nsIContentInlines.h" #include "nsContentUtils.h" namespace mozilla { -ServoElementSnapshot::ServoElementSnapshot(Element* aElement) +ServoElementSnapshot::ServoElementSnapshot(const Element* aElement) : mContains(Flags(0)) , mState(0) - , mExplicitRestyleHint(nsRestyleHint(0)) - , mExplicitChangeHint(nsChangeHint(0)) { + MOZ_COUNT_CTOR(ServoElementSnapshot); mIsHTMLElementInHTMLDocument = aElement->IsHTMLElement() && aElement->IsInHTMLDocument(); mIsInChromeDocument = nsContentUtils::IsChromeDoc(aElement->OwnerDoc()); } +ServoElementSnapshot::~ServoElementSnapshot() +{ + MOZ_COUNT_DTOR(ServoElementSnapshot); +} + void ServoElementSnapshot::AddAttrs(Element* aElement) { MOZ_ASSERT(aElement); if (HasAny(Flags::Attributes)) { return; }
--- a/layout/style/ServoElementSnapshot.h +++ b/layout/style/ServoElementSnapshot.h @@ -62,17 +62,18 @@ class ServoElementSnapshot { typedef dom::BorrowedAttrInfo BorrowedAttrInfo; typedef dom::Element Element; typedef EventStates::ServoType ServoStateType; public: typedef ServoElementSnapshotFlags Flags; - explicit ServoElementSnapshot(Element* aElement); + explicit ServoElementSnapshot(const Element* aElement); + ~ServoElementSnapshot(); bool HasAttrs() { return HasAny(Flags::Attributes); } bool HasState() { return HasAny(Flags::State); } /** * Captures the given state (if not previously captured). */ @@ -84,30 +85,16 @@ public: } } /** * Captures the given element attributes (if not previously captured). */ void AddAttrs(Element* aElement); - void AddExplicitChangeHint(nsChangeHint aMinChangeHint) - { - mExplicitChangeHint |= aMinChangeHint; - } - - void AddExplicitRestyleHint(nsRestyleHint aRestyleHint) - { - mExplicitRestyleHint |= aRestyleHint; - } - - nsRestyleHint ExplicitRestyleHint() { return mExplicitRestyleHint; } - - nsChangeHint ExplicitChangeHint() { return mExplicitChangeHint; } - /** * Needed methods for attribute matching. */ BorrowedAttrInfo GetAttrInfoAt(uint32_t aIndex) const { if (aIndex >= mAttrs.Length()) { return BorrowedAttrInfo(nullptr, nullptr); } @@ -153,17 +140,15 @@ public: private: // TODO: Profile, a 1 or 2 element AutoTArray could be worth it, given we know // we're dealing with attribute changes when we take snapshots of attributes, // though it can be wasted space if we deal with a lot of state-only // snapshots. Flags mContains; nsTArray<ServoAttrSnapshot> mAttrs; ServoStateType mState; - nsRestyleHint mExplicitRestyleHint; - nsChangeHint mExplicitChangeHint; bool mIsHTMLElementInHTMLDocument; bool mIsInChromeDocument; }; } // namespace mozilla #endif
--- a/layout/style/ServoStyleSet.cpp +++ b/layout/style/ServoStyleSet.cpp @@ -84,30 +84,36 @@ ServoStyleSet::EndUpdate() } // ... do something ... return NS_OK; } already_AddRefed<nsStyleContext> ServoStyleSet::ResolveStyleFor(Element* aElement, - nsStyleContext* aParentContext) + nsStyleContext* aParentContext, + ConsumeStyleBehavior aConsume, + LazyComputeBehavior aMayCompute) { return GetContext(aElement, aParentContext, nullptr, - CSSPseudoElementType::NotPseudo); + CSSPseudoElementType::NotPseudo, aConsume, aMayCompute); } already_AddRefed<nsStyleContext> ServoStyleSet::GetContext(nsIContent* aContent, nsStyleContext* aParentContext, nsIAtom* aPseudoTag, - CSSPseudoElementType aPseudoType) + CSSPseudoElementType aPseudoType, + ConsumeStyleBehavior aConsume, + LazyComputeBehavior aMayCompute) { + MOZ_ASSERT(aContent->IsElement()); + Element* element = aContent->AsElement(); RefPtr<ServoComputedValues> computedValues = - Servo_ComputedValues_Get(aContent).Consume(); + Servo_ResolveStyle(element, mRawSet.get(), aConsume, aMayCompute).Consume(); MOZ_ASSERT(computedValues); return GetContext(computedValues.forget(), aParentContext, aPseudoTag, aPseudoType); } already_AddRefed<nsStyleContext> ServoStyleSet::GetContext(already_AddRefed<ServoComputedValues> aComputedValues, nsStyleContext* aParentContext, nsIAtom* aPseudoTag, @@ -121,22 +127,24 @@ ServoStyleSet::GetContext(already_AddRef return NS_NewStyleContext(aParentContext, mPresContext, aPseudoTag, aPseudoType, Move(aComputedValues), skipFixup); } already_AddRefed<nsStyleContext> ServoStyleSet::ResolveStyleFor(Element* aElement, nsStyleContext* aParentContext, + ConsumeStyleBehavior aConsume, + LazyComputeBehavior aMayCompute, TreeMatchContext& aTreeMatchContext) { // aTreeMatchContext is used to speed up selector matching, // but if the element already has a ServoComputedValues computed in // advance, then we shouldn't need to use it. - return ResolveStyleFor(aElement, aParentContext); + return ResolveStyleFor(aElement, aParentContext, aConsume, aMayCompute); } already_AddRefed<nsStyleContext> ServoStyleSet::ResolveStyleForText(nsIContent* aTextNode, nsStyleContext* aParentContext) { MOZ_ASSERT(aTextNode && aTextNode->IsNodeOfType(nsINode::eTEXT)); MOZ_ASSERT(aTextNode->GetParent()); @@ -432,63 +440,44 @@ ServoStyleSet::HasStateDependentStyle(do CSSPseudoElementType aPseudoType, dom::Element* aPseudoElement, EventStates aStateMask) { NS_WARNING("stylo: HasStateDependentStyle always returns zero!"); return nsRestyleHint(0); } -nsRestyleHint -ServoStyleSet::ComputeRestyleHint(dom::Element* aElement, - ServoElementSnapshot* aSnapshot) -{ - return Servo_ComputeRestyleHint(aElement, aSnapshot, mRawSet.get()); -} - -static void -ClearDirtyBits(nsIContent* aContent) -{ - bool traverseDescendants = aContent->HasDirtyDescendantsForServo(); - aContent->UnsetIsDirtyAndHasDirtyDescendantsForServo(); - if (!traverseDescendants) { - return; - } - - StyleChildrenIterator it(aContent); - for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) { - ClearDirtyBits(n); - } -} - void -ServoStyleSet::StyleDocument(bool aLeaveDirtyBits) +ServoStyleSet::StyleDocument() { // Grab the root. nsIDocument* doc = mPresContext->Document(); - nsIContent* root = doc->GetRootElement(); + Element* root = doc->GetRootElement(); MOZ_ASSERT(root); - // Restyle the document, clearing the dirty bits if requested. - Servo_RestyleSubtree(root, mRawSet.get()); - if (!aLeaveDirtyBits) { - ClearDirtyBits(root); - doc->UnsetHasDirtyDescendantsForServo(); - } + // Restyle the document. + Servo_TraverseSubtree(root, mRawSet.get(), SkipRootBehavior::DontSkip); } void ServoStyleSet::StyleNewSubtree(nsIContent* aContent) { - MOZ_ASSERT(aContent->IsDirtyForServo()); - if (aContent->IsElement() || aContent->IsNodeOfType(nsINode::eTEXT)) { - Servo_RestyleSubtree(aContent, mRawSet.get()); + if (aContent->IsElement()) { + Servo_TraverseSubtree(aContent->AsElement(), mRawSet.get(), SkipRootBehavior::DontSkip); } - ClearDirtyBits(aContent); } void ServoStyleSet::StyleNewChildren(nsIContent* aParent) { - MOZ_ASSERT(aParent->HasDirtyDescendantsForServo()); - Servo_RestyleSubtree(aParent, mRawSet.get()); - ClearDirtyBits(aParent); + MOZ_ASSERT(aParent->IsElement()); + Servo_TraverseSubtree(aParent->AsElement(), mRawSet.get(), SkipRootBehavior::Skip); } + +#ifdef DEBUG +void +ServoStyleSet::AssertTreeIsClean() +{ + if (Element* root = mPresContext->Document()->GetRootElement()) { + Servo_AssertTreeIsClean(root); + } +} +#endif
--- a/layout/style/ServoStyleSet.h +++ b/layout/style/ServoStyleSet.h @@ -51,21 +51,25 @@ public: bool GetAuthorStyleDisabled() const; nsresult SetAuthorStyleDisabled(bool aStyleDisabled); void BeginUpdate(); nsresult EndUpdate(); already_AddRefed<nsStyleContext> ResolveStyleFor(dom::Element* aElement, - nsStyleContext* aParentContext); + nsStyleContext* aParentContext, + ConsumeStyleBehavior aConsume, + LazyComputeBehavior aMayCompute); already_AddRefed<nsStyleContext> ResolveStyleFor(dom::Element* aElement, nsStyleContext* aParentContext, + ConsumeStyleBehavior aConsume, + LazyComputeBehavior aMayCompute, TreeMatchContext& aTreeMatchContext); already_AddRefed<nsStyleContext> ResolveStyleForText(nsIContent* aTextNode, nsStyleContext* aParentContext); already_AddRefed<nsStyleContext> ResolveStyleForOtherNonElement(nsStyleContext* aParentContext); @@ -113,29 +117,20 @@ public: // Test if style is dependent on content state nsRestyleHint HasStateDependentStyle(dom::Element* aElement, EventStates aStateMask); nsRestyleHint HasStateDependentStyle( dom::Element* aElement, mozilla::CSSPseudoElementType aPseudoType, dom::Element* aPseudoElement, EventStates aStateMask); /** - * Computes a restyle hint given a element and a previous element snapshot. - */ - nsRestyleHint ComputeRestyleHint(dom::Element* aElement, - ServoElementSnapshot* aSnapshot); - - /** * Performs a Servo traversal to compute style for all dirty nodes in the * document. The root element must be non-null. - * - * If aLeaveDirtyBits is true, the dirty/dirty-descendant bits are not - * cleared. */ - void StyleDocument(bool aLeaveDirtyBits); + void StyleDocument(); /** * Eagerly styles a subtree of dirty nodes that were just appended to the * tree. This is used in situations where we need the style immediately and * cannot wait for a future batch restyle. * * The subtree must have the root dirty bit set, which currently gets * propagated to all descendants. The dirty bits are cleared before @@ -147,26 +142,34 @@ public: * Like the above, but does not assume that the root node is dirty. When * appending multiple children to a potentially-non-dirty node, it's * preferable to call StyleNewChildren on the node rather than making multiple * calls to StyleNewSubtree on each child, since it allows for more * parallelism. */ void StyleNewChildren(nsIContent* aParent); +#ifdef DEBUG + void AssertTreeIsClean(); +#else + void AssertTreeIsClean() {} +#endif + private: already_AddRefed<nsStyleContext> GetContext(already_AddRefed<ServoComputedValues>, nsStyleContext* aParentContext, nsIAtom* aPseudoTag, CSSPseudoElementType aPseudoType); already_AddRefed<nsStyleContext> GetContext(nsIContent* aContent, nsStyleContext* aParentContext, nsIAtom* aPseudoTag, - CSSPseudoElementType aPseudoType); + CSSPseudoElementType aPseudoType, + ConsumeStyleBehavior aConsume, + LazyComputeBehavior aMayCompute); nsPresContext* mPresContext; UniquePtr<RawServoStyleSet> mRawSet; EnumeratedArray<SheetType, SheetType::Count, nsTArray<RefPtr<ServoStyleSheet>>> mSheets; int32_t mBatching; };
--- a/layout/style/ServoTypes.h +++ b/layout/style/ServoTypes.h @@ -30,11 +30,34 @@ struct ServoUnsafeCell { template<typename T> struct ServoCell { ServoUnsafeCell<T> value; T Get() const { return value.value; } void Set(T arg) { value.value = arg; } ServoCell() : value() {}; }; +// Indicates whether a style resolution should be considered to consume the style +// for the construction of a layout frame, or whether the caller is just +// peeking. +enum class ConsumeStyleBehavior { + Consume, + DontConsume, +}; + +// Indicates whether the Servo style system should expect the style on an element +// to have already been resolved (i.e. via a parallel traversal), or whether it +// may be lazily computed. +enum class LazyComputeBehavior { + Allow, + Assert, +}; + +// Indicates whether the Servo style system should skip processing on the root +// element and start processing with its children. +enum class SkipRootBehavior { + Skip, + DontSkip, +}; + } // namespace mozilla #endif // mozilla_ServoTypes_h
--- a/layout/style/StyleSetHandle.h +++ b/layout/style/StyleSetHandle.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_StyleSetHandle_h #define mozilla_StyleSetHandle_h #include "mozilla/EventStates.h" #include "mozilla/RefPtr.h" +#include "mozilla/ServoTypes.h" #include "mozilla/SheetType.h" #include "mozilla/StyleBackendType.h" #include "mozilla/StyleSheet.h" #include "nsChangeHint.h" #include "nsCSSPseudoElements.h" #include "nsTArray.h" namespace mozilla { @@ -109,20 +110,24 @@ public: inline void BeginShutdown(); inline void Shutdown(); inline bool GetAuthorStyleDisabled() const; inline nsresult SetAuthorStyleDisabled(bool aStyleDisabled); inline void BeginUpdate(); inline nsresult EndUpdate(); inline already_AddRefed<nsStyleContext> ResolveStyleFor(dom::Element* aElement, - nsStyleContext* aParentContext); + nsStyleContext* aParentContext, + ConsumeStyleBehavior aConsume, + LazyComputeBehavior aMayCompute); inline already_AddRefed<nsStyleContext> ResolveStyleFor(dom::Element* aElement, nsStyleContext* aParentContext, + ConsumeStyleBehavior aConsume, + LazyComputeBehavior aMayCompute, TreeMatchContext& aTreeMatchContext); inline already_AddRefed<nsStyleContext> ResolveStyleForText(nsIContent* aTextNode, nsStyleContext* aParentContext); inline already_AddRefed<nsStyleContext> ResolveStyleForOtherNonElement(nsStyleContext* aParentContext); inline already_AddRefed<nsStyleContext> ResolvePseudoElementStyle(dom::Element* aParentElement,
--- a/layout/style/StyleSetHandleInlines.h +++ b/layout/style/StyleSetHandleInlines.h @@ -74,27 +74,31 @@ nsresult StyleSetHandle::Ptr::EndUpdate() { FORWARD(EndUpdate, ()); } // resolve a style context already_AddRefed<nsStyleContext> StyleSetHandle::Ptr::ResolveStyleFor(dom::Element* aElement, - nsStyleContext* aParentContext) + nsStyleContext* aParentContext, + ConsumeStyleBehavior aConsume, + LazyComputeBehavior aMayCompute) { - FORWARD(ResolveStyleFor, (aElement, aParentContext)); + FORWARD(ResolveStyleFor, (aElement, aParentContext, aConsume, aMayCompute)); } already_AddRefed<nsStyleContext> StyleSetHandle::Ptr::ResolveStyleFor(dom::Element* aElement, nsStyleContext* aParentContext, + ConsumeStyleBehavior aConsume, + LazyComputeBehavior aMayCompute, TreeMatchContext& aTreeMatchContext) { - FORWARD(ResolveStyleFor, (aElement, aParentContext, aTreeMatchContext)); + FORWARD(ResolveStyleFor, (aElement, aParentContext, aConsume, aMayCompute, aTreeMatchContext)); } already_AddRefed<nsStyleContext> StyleSetHandle::Ptr::ResolveStyleForText(nsIContent* aTextNode, nsStyleContext* aParentContext) { FORWARD(ResolveStyleForText, (aTextNode, aParentContext)); }
--- a/layout/style/nsComputedDOMStyle.cpp +++ b/layout/style/nsComputedDOMStyle.cpp @@ -490,17 +490,18 @@ nsComputedDOMStyle::GetStyleContextForEl return nullptr; } nsIFrame* frame = nsLayoutUtils::GetStyleFrame(aElement); Element* pseudoElement = frame && inDocWithShell ? frame->GetPseudoElement(type) : nullptr; sc = styleSet->ResolvePseudoElementStyle(aElement, type, parentContext, pseudoElement); } else { - sc = styleSet->ResolveStyleFor(aElement, parentContext); + sc = styleSet->ResolveStyleFor(aElement, parentContext, ConsumeStyleBehavior::DontConsume, + LazyComputeBehavior::Allow); } if (aStyleType == eDefaultOnly) { if (styleSet->IsServo()) { NS_ERROR("stylo: ServoStyleSets cannot supply UA-only styles yet"); return nullptr; }
--- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -558,18 +558,18 @@ static nscoord CalcLengthWith(const nsCS const nsStyleFont *rootStyleFont = styleFont; Element* docElement = aPresContext->Document()->GetRootElement(); if (docElement) { nsIFrame* rootFrame = docElement->GetPrimaryFrame(); if (rootFrame) { rootStyle = rootFrame->StyleContext(); } else { - rootStyle = aPresContext->StyleSet()->ResolveStyleFor(docElement, - nullptr); + rootStyle = aPresContext->StyleSet()->AsGecko()->ResolveStyleFor(docElement, + nullptr); } rootStyleFont = rootStyle->StyleFont(); } rootFontSize = rootStyleFont->mFont.size; } return ScaleCoordRound(aValue, float(rootFontSize));
--- a/layout/style/nsStyleContext.cpp +++ b/layout/style/nsStyleContext.cpp @@ -86,22 +86,16 @@ nsStyleContext::nsStyleContext(nsStyleCo , mPseudoTag(aPseudoTag) , mSource(Move(aSource)) #ifdef MOZ_STYLO , mPresContext(nullptr) #endif , mCachedResetData(nullptr) , mBits(((uint64_t)aPseudoType) << NS_STYLE_CONTEXT_TYPE_SHIFT) , mRefCnt(0) -#ifdef MOZ_STYLO - , mStoredChangeHint(nsChangeHint(0)) -#ifdef DEBUG - , mConsumedChangeHint(false) -#endif -#endif #ifdef DEBUG , mFrameRefCnt(0) , mComputingStruct(nsStyleStructID_None) #endif { MOZ_COUNT_CTOR(nsStyleContext); } @@ -791,17 +785,17 @@ nsStyleContext::ApplyStyleFixups(bool aS // CSS 2.1 10.1: Propagate the root element's 'direction' to the ICB. // (PageContentFrame/CanvasFrame etc will inherit 'direction') if (mPseudoTag == nsCSSAnonBoxes::viewport) { nsPresContext* presContext = PresContext(); mozilla::dom::Element* docElement = presContext->Document()->GetRootElement(); if (docElement) { RefPtr<nsStyleContext> rootStyle = - presContext->StyleSet()->ResolveStyleFor(docElement, nullptr); + presContext->StyleSet()->AsGecko()->ResolveStyleFor(docElement, nullptr); auto dir = rootStyle->StyleVisibility()->mDirection; if (dir != StyleVisibility()->mDirection) { nsStyleVisibility* uniqueVisibility = GET_UNIQUE_STYLE_DATA(Visibility); uniqueVisibility->mDirection = dir; } } }
--- a/layout/style/nsStyleContext.h +++ b/layout/style/nsStyleContext.h @@ -507,55 +507,16 @@ public: } else { cachedData = mCachedInheritedData.mStyleStructs[aSID]; } return cachedData; } mozilla::NonOwningStyleContextSource StyleSource() const { return mSource.AsRaw(); } -#ifdef MOZ_STYLO - // NOTE: It'd be great to assert here that the previous change hint is always - // consumed. - // - // This is not the case right now, since the changes of childs of frames that - // go through frame construction are not consumed. - void StoreChangeHint(nsChangeHint aHint) - { - MOZ_ASSERT(!IsShared()); - mStoredChangeHint = aHint; -#ifdef DEBUG - mConsumedChangeHint = false; -#endif - } - - nsChangeHint ConsumeStoredChangeHint() - { - MOZ_ASSERT(!mConsumedChangeHint, "Re-consuming the same change hint!"); - nsChangeHint result = mStoredChangeHint; - mStoredChangeHint = nsChangeHint(0); -#ifdef DEBUG - mConsumedChangeHint = true; -#endif - return result; - } -#else - void StoreChangeHint(nsChangeHint aHint) - { - MOZ_CRASH("stylo: Called nsStyleContext::StoreChangeHint in a non MOZ_STYLO " - "build."); - } - - nsChangeHint ConsumeStoredChangeHint() - { - MOZ_CRASH("stylo: Called nsStyleContext::ComsumeStoredChangeHint in a non " - "MOZ_STYLO build."); - } -#endif - private: // Private destructor, to discourage deletion outside of Release(): ~nsStyleContext(); // Delegated Helper constructor. nsStyleContext(nsStyleContext* aParent, mozilla::OwningStyleContextSource&& aSource, nsIAtom* aPseudoTag, @@ -804,25 +765,16 @@ private: // inherited from the parent context or owned by the rule node (i.e., // not owned by the style context). // - It also stores the additional bits listed at the top of // nsStyleStruct.h. uint64_t mBits; uint32_t mRefCnt; - // For now we store change hints on the style context during parallel traversal. - // We should improve this - see bug 1289861. -#ifdef MOZ_STYLO - nsChangeHint mStoredChangeHint; -#ifdef DEBUG - bool mConsumedChangeHint; -#endif -#endif - #ifdef DEBUG uint32_t mFrameRefCnt; // number of frames that use this // as their style context nsStyleStructID mComputingStruct; static bool DependencyAllowed(nsStyleStructID aOuterSID, nsStyleStructID aInnerSID)
--- a/layout/style/nsStyleSet.h +++ b/layout/style/nsStyleSet.h @@ -12,16 +12,17 @@ #ifndef nsStyleSet_h_ #define nsStyleSet_h_ #include "mozilla/Attributes.h" #include "mozilla/StyleSheetInlines.h" #include "mozilla/EnumeratedArray.h" #include "mozilla/LinkedList.h" #include "mozilla/MemoryReporting.h" +#include "mozilla/ServoTypes.h" #include "mozilla/SheetType.h" #include "nsIStyleRuleProcessor.h" #include "nsBindingManager.h" #include "nsRuleNode.h" #include "nsTArray.h" #include "nsCOMArray.h" #include "nsIStyleRule.h" @@ -110,18 +111,37 @@ class nsStyleSet final // get a style context for a non-pseudo frame. already_AddRefed<nsStyleContext> ResolveStyleFor(mozilla::dom::Element* aElement, nsStyleContext* aParentContext); already_AddRefed<nsStyleContext> ResolveStyleFor(mozilla::dom::Element* aElement, nsStyleContext* aParentContext, + mozilla::ConsumeStyleBehavior, + mozilla::LazyComputeBehavior) + { + return ResolveStyleFor(aElement, aParentContext); + } + + already_AddRefed<nsStyleContext> + ResolveStyleFor(mozilla::dom::Element* aElement, + nsStyleContext* aParentContext, TreeMatchContext& aTreeMatchContext); + already_AddRefed<nsStyleContext> + ResolveStyleFor(mozilla::dom::Element* aElement, + nsStyleContext* aParentContext, + mozilla::ConsumeStyleBehavior aConsume, + mozilla::LazyComputeBehavior aMayCompute, + TreeMatchContext& aTreeMatchContext) + { + return ResolveStyleFor(aElement, aParentContext, aTreeMatchContext); + } + // Get a style context (with the given parent) for the // sequence of style rules in the |aRules| array. already_AddRefed<nsStyleContext> ResolveStyleForRules(nsStyleContext* aParentContext, const nsTArray< nsCOMPtr<nsIStyleRule> > &aRules); // Get a style context that represents aBaseContext, but as though // it additionally matched the rules in the aRules array (in that
--- a/layout/xul/nsListBoxBodyFrame.cpp +++ b/layout/xul/nsListBoxBodyFrame.cpp @@ -697,17 +697,19 @@ nsListBoxBodyFrame::ComputeIntrinsicISiz nsCOMPtr<nsIDOMElement> firstRowEl; GetItemAtIndex(index, getter_AddRefs(firstRowEl)); nsCOMPtr<nsIContent> firstRowContent(do_QueryInterface(firstRowEl)); if (firstRowContent) { RefPtr<nsStyleContext> styleContext; nsPresContext *presContext = aBoxLayoutState.PresContext(); styleContext = presContext->StyleSet()-> - ResolveStyleFor(firstRowContent->AsElement(), nullptr); + ResolveStyleFor(firstRowContent->AsElement(), nullptr, + ConsumeStyleBehavior::DontConsume, + LazyComputeBehavior::Allow); nscoord width = 0; nsMargin margin(0,0,0,0); if (styleContext->StylePadding()->GetPadding(margin)) width += margin.LeftRight(); width += styleContext->StyleBorder()->GetComputedBorder().LeftRight(); if (styleContext->StyleMargin()->GetMargin(margin))
--- a/layout/xul/nsSplitterFrame.cpp +++ b/layout/xul/nsSplitterFrame.cpp @@ -281,17 +281,19 @@ nsSplitterFrame::Init(nsIContent* if (aParent && aParent->IsXULBoxFrame()) { if (!aParent->IsXULHorizontal()) { if (!nsContentUtils::HasNonEmptyAttr(aContent, kNameSpaceID_None, nsGkAtoms::orient)) { aContent->SetAttr(kNameSpaceID_None, nsGkAtoms::orient, NS_LITERAL_STRING("vertical"), false); nsStyleContext* parentStyleContext = StyleContext()->GetParent(); RefPtr<nsStyleContext> newContext = PresContext()->StyleSet()-> - ResolveStyleFor(aContent->AsElement(), parentStyleContext); + ResolveStyleFor(aContent->AsElement(), parentStyleContext, + ConsumeStyleBehavior::Consume, + LazyComputeBehavior::Allow); SetStyleContextWithoutNotification(newContext); } } } nsBoxFrame::Init(aContent, aParent, aPrevInFlow); mInner->mState = nsSplitterFrameInner::Open;
--- a/netwerk/base/PrivateBrowsingChannel.h +++ b/netwerk/base/PrivateBrowsingChannel.h @@ -9,16 +9,17 @@ #include "nsIPrivateBrowsingChannel.h" #include "nsCOMPtr.h" #include "nsILoadGroup.h" #include "nsILoadContext.h" #include "nsIInterfaceRequestorUtils.h" #include "nsIInterfaceRequestor.h" #include "nsNetUtil.h" +#include "mozilla/Unused.h" namespace mozilla { namespace net { template <class Channel> class PrivateBrowsingChannel : public nsIPrivateBrowsingChannel { public: @@ -66,20 +67,30 @@ public: // Must be called every time the channel's callbacks or loadGroup is updated void UpdatePrivateBrowsing() { // once marked as private we never go un-private if (mPrivateBrowsing) { return; } + auto channel = static_cast<Channel*>(this); + nsCOMPtr<nsILoadContext> loadContext; - NS_QueryNotificationCallbacks(static_cast<Channel*>(this), loadContext); + NS_QueryNotificationCallbacks(channel, loadContext); if (loadContext) { mPrivateBrowsing = loadContext->UsePrivateBrowsing(); + return; + } + + nsCOMPtr<nsILoadInfo> loadInfo; + Unused << channel->GetLoadInfo(getter_AddRefs(loadInfo)); + if (loadInfo) { + NeckoOriginAttributes attrs = loadInfo->GetOriginAttributes(); + mPrivateBrowsing = attrs.mPrivateBrowsingId > 0; } } bool CanSetCallbacks(nsIInterfaceRequestor* aCallbacks) const { // Make sure that the private bit override flag is not set. // This is a fatal error in debug builds, and a runtime error in release // builds.
--- a/netwerk/ipc/NeckoParent.cpp +++ b/netwerk/ipc/NeckoParent.cpp @@ -154,26 +154,30 @@ void CrashWithReason(const char * reason } const char* NeckoParent::GetValidatedOriginAttributes(const SerializedLoadContext& aSerialized, PContentParent* aContent, nsIPrincipal* aRequestingPrincipal, DocShellOriginAttributes& aAttrs) { - if (!aSerialized.IsNotNull()) { - if (UsingNeckoIPCSecurity()) { - CrashWithReason("GetValidatedOriginAttributes | SerializedLoadContext from child is null"); - return "SerializedLoadContext from child is null"; + if (!UsingNeckoIPCSecurity()) { + if (!aSerialized.IsNotNull()) { + // If serialized is null, we cannot validate anything. We have to assume + // that this requests comes from a SystemPrincipal. + aAttrs = DocShellOriginAttributes(NECKO_NO_APP_ID, false); + } else { + aAttrs = aSerialized.mOriginAttributes; } + return nullptr; + } - // If serialized is null, we cannot validate anything. We have to assume - // that this requests comes from a SystemPrincipal. - aAttrs = DocShellOriginAttributes(NECKO_NO_APP_ID, false); - return nullptr; + if (!aSerialized.IsNotNull()) { + CrashWithReason("GetValidatedOriginAttributes | SerializedLoadContext from child is null"); + return "SerializedLoadContext from child is null"; } nsTArray<TabContext> contextArray = static_cast<ContentParent*>(aContent)->GetManagedTabContext(); nsAutoCString serializedSuffix; aSerialized.mOriginAttributes.CreateAnonymizedSuffix(serializedSuffix); @@ -208,22 +212,16 @@ NeckoParent::GetValidatedOriginAttribute if (swm && swm->MayHaveActiveServiceWorkerInstance(static_cast<ContentParent*>(aContent), aRequestingPrincipal)) { aAttrs = aSerialized.mOriginAttributes; return nullptr; } } - if (!UsingNeckoIPCSecurity()) { - // We are running some tests - aAttrs = aSerialized.mOriginAttributes; - return nullptr; - } - nsAutoCString errorString; errorString.Append("GetValidatedOriginAttributes | App does not have permission -"); errorString.Append(debugString); // Leak the buffer on the heap to make sure that it lives long enough, as // MOZ_CRASH_ANNOTATE expects the pointer passed to it to live to the end of // the program. char * error = strdup(errorString.BeginReading());
--- a/python/mozboot/mozboot/base.py +++ b/python/mozboot/mozboot/base.py @@ -70,18 +70,19 @@ We recommend the following tools for ins pyenv -- https://github.com/yyuu/pyenv) pythonz -- https://github.com/saghul/pythonz official installers -- http://www.python.org/ ''' RUST_NOT_IN_PATH = ''' You have some rust files in %(cargo_bin)s, but they're not part of the -standard PATH. +standard PATH.''' +RUST_PATH_ADVICE = ''' To make these available, please add this directory to the PATH variable in your shell initialization script, which may be called ~/.bashrc or ~/.bash_profile or ~/.profile. Edit this and add the following line: source %(cargo_home)s/env Then restart your shell and run the bootstrap script again. ''' @@ -509,35 +510,39 @@ class BaseBootstrapper(object): cargo = self.which('cargo') our = self._parse_version(rustc) if not our: return False, None return our >= MODERN_RUST_VERSION, our + def cargo_home(self): + cargo_home = os.environ.get('CARGO_HOME', + os.path.expanduser(os.path.join('~', '.cargo'))) + cargo_bin = os.path.join(cargo_home, 'bin') + return cargo_home, cargo_bin + def ensure_rust_modern(self): modern, version = self.is_rust_modern() if modern: print('Your version of Rust (%s) is new enough.' % version) return if not version: # Rust wasn't in PATH. Try the standard path. - cargo_home = os.environ.get('CARGO_HOME', - os.path.expanduser(os.path.join('~', '.cargo'))) - cargo_bin = os.path.join(cargo_home, 'bin') + cargo_home, cargo_bin = self.cargo_home() try_rustc = os.path.join(cargo_bin, 'rustc' + rust.exe_suffix()) try_cargo = os.path.join(cargo_bin, 'cargo' + rust.exe_suffix()) have_rustc = os.path.exists(try_rustc) have_cargo = os.path.exists(try_cargo) if have_rustc or have_cargo: - print(RUST_NOT_IN_PATH % { 'cargo_bin': cargo_bin, - 'cargo_home': cargo_home }) + print(RUST_NOT_IN_PATH % { 'cargo_bin': cargo_bin }) + print(RUST_PATH_ADVICE % { 'cargo_home': cargo_home }) sys.exit(1) rustup = self.which('rustup') if rustup: rustup_version = self._parse_version(rustup) if not rustup_version: print(RUSTUP_OLD) sys.exit(1) @@ -545,27 +550,26 @@ class BaseBootstrapper(object): # We have rustup but no rustc. # Try running rustup; maybe it will fix things. print('Found rustup. Will try to upgrade.') else: # We have both rustup and rustc. print('Your version of Rust (%s) is too old. Will try to upgrade.' % version) self.upgrade_rust(rustup) + + modern, after = self.is_rust_modern() + if not modern: + print(RUST_UPGRADE_FAILED % (MODERN_RUST_VERSION, after)) + sys.exit(1) else: # No rustc or rustup. print('Will try to install Rust.') self.install_rust() - modern, after = self.is_rust_modern() - - if not modern: - print(RUST_UPGRADE_FAILED % (MODERN_RUST_VERSION, after)) - sys.exit(1) - def upgrade_rust(self, rustup): """Upgrade Rust. Invoke rustup from the given path to update the rust install.""" subprocess.check_call([rustup, 'update']) def install_rust(self): """Download and run the rustup installer.""" @@ -586,17 +590,20 @@ class BaseBootstrapper(object): mode = os.stat(rustup_init).st_mode os.chmod(rustup_init, mode | stat.S_IRWXU) print('Ok') print('Running rustup-init...') subprocess.check_call([rustup_init, '-y', '--default-toolchain', 'stable', '--default-host', platform, ]) + cargo_home, cargo_bin = self.cargo_home() print('Rust installation complete.') + print('You should now have rustc and cargo in %s' % cargo_bin) + print(RUST_PATH_ADVICE % { 'cargo_home': cargo_home }) finally: try: os.remove(rustup_init) except OSError as e: if e.errno != errno.ENOENT: raise def http_download_and_save(self, url, dest, sha256hexhash):
--- a/toolkit/components/extensions/test/mochitest/mochitest-common.ini +++ b/toolkit/components/extensions/test/mochitest/mochitest-common.ini @@ -46,16 +46,18 @@ skip-if = os == 'android' # Android does [test_ext_background_canvas.html] [test_ext_content_security_policy.html] [test_ext_contentscript.html] [test_ext_contentscript_api_injection.html] [test_ext_contentscript_context.html] [test_ext_contentscript_create_iframe.html] [test_ext_contentscript_devtools_metadata.html] [test_ext_contentscript_exporthelpers.html] +[test_ext_contentscript_incognito.html] +skip-if = os == 'android' # Android does not multiple windows. [test_ext_contentscript_css.html] [test_ext_contentscript_about_blank.html] [test_ext_contentscript_teardown.html] skip-if = (os == 'android') # Android does not support tabs API. Bug 1260250 [test_ext_exclude_include_globs.html] [test_ext_i18n_css.html] [test_ext_generate.html] [test_ext_notifications.html]
new file mode 100644 --- /dev/null +++ b/toolkit/components/extensions/test/mochitest/test_ext_contentscript_incognito.html @@ -0,0 +1,89 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test for content script private browsing ID</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script> + <script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script> + <script type="text/javascript" src="head.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> + +<script type="text/javascript"> +"use strict"; + +add_task(function* test_contentscript_incognito() { + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + content_scripts: [ + { + "matches": ["http://mochi.test/*/file_sample.html"], + "js": ["content_script.js"], + }, + ], + }, + + background() { + let windowId; + + browser.test.onMessage.addListener(([msg, url]) => { + if (msg === "open-window") { + browser.windows.create({url, incognito: true}).then(window => { + windowId = window.id; + }); + } else if (msg === "close-window") { + browser.windows.remove(windowId).then(() => { + browser.test.sendMessage("done"); + }); + } + }); + }, + + files: { + "content_script.js": async () => { + const COOKIE = "foo=florgheralzps"; + document.cookie = COOKIE; + + let url = new URL("return_headers.sjs", location.href); + + let responses = [ + new Promise(resolve => { + let xhr = new XMLHttpRequest(); + xhr.open("GET", url); + xhr.onload = () => resolve(JSON.parse(xhr.responseText)); + xhr.send(); + }), + + fetch(url, {credentials: "include"}).then(body => body.json()), + ]; + + try { + for (let response of await Promise.all(responses)) { + browser.test.assertEq(COOKIE, response.cookie, "Got expected cookie header"); + } + browser.test.notifyPass("cookies"); + } catch (e) { + browser.test.fail(`Error: ${e}`); + browser.test.notifyFail("cookies"); + } + }, + }, + }); + + yield extension.startup(); + + extension.sendMessage(["open-window", SimpleTest.getTestFileURL("file_sample.html")]); + + yield extension.awaitFinish("cookies"); + + extension.sendMessage(["close-window"]); + yield extension.awaitMessage("done"); + + yield extension.unload(); +}); +</script> + +</body> +</html> +
--- a/toolkit/components/extensions/test/mochitest/test_ext_webrequest_suspend.html +++ b/toolkit/components/extensions/test/mochitest/test_ext_webrequest_suspend.html @@ -59,11 +59,81 @@ add_task(function* test_suspend() { let headers = JSON.parse(yield result.text()); is(headers.foo, "Bar", "Request header was correctly set on suspended request"); yield extension.unload(); }); + +// Test that requests that were canceled while suspended for a blocking +// listener are correctly resumed. +add_task(function* test_error_resume() { + let chromeScript = SpecialPowers.loadChromeScript(() => { + const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; + Cu.import("resource://gre/modules/Services.jsm"); + + let observer = channel => { + if (channel instanceof Ci.nsIHttpChannel && channel.URI.spec === "http://example.com/") { + Services.obs.removeObserver(observer, "http-on-modify-request"); + + // Wait until the next tick to make sure this runs after WebRequest observers. + Promise.resolve().then(() => { + channel.cancel(Cr.NS_BINDING_ABORTED); + }); + } + }; + + Services.obs.addObserver(observer, "http-on-modify-request", false); + }); + + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + permissions: [ + "webRequest", + "webRequestBlocking", + ], + }, + + background() { + browser.webRequest.onBeforeSendHeaders.addListener( + details => { + browser.test.log(`onBeforeSendHeaders({url: ${details.url}})`); + + if (details.url === "http://example.com/") { + browser.test.sendMessage("got-before-send-headers"); + } + }, + {urls: ["<all_urls>"]}, + ["blocking"]); + + browser.webRequest.onErrorOccurred.addListener( + details => { + browser.test.log(`onErrorOccurred({url: ${details.url}})`); + + if (details.url === "http://example.com/") { + browser.test.sendMessage("got-error-occurred"); + } + }, + {urls: ["<all_urls>"]}); + }, + }); + + yield extension.startup(); + + try { + yield fetch("http://example.com/"); + ok(false, "Fetch should have failed."); + } catch (e) { + ok(true, "Got expected error."); + } + + yield extension.awaitMessage("got-before-send-headers"); + yield extension.awaitMessage("got-error-occurred"); + + yield extension.unload(); + chromeScript.destroy(); +}); + </script> </body> </html>
--- a/toolkit/modules/WebChannel.jsm +++ b/toolkit/modules/WebChannel.jsm @@ -4,16 +4,18 @@ /** * WebChannel is an abstraction that uses the Message Manager and Custom Events * to create a two-way communication channel between chrome and content code. */ this.EXPORTED_SYMBOLS = ["WebChannel", "WebChannelBroker"]; +const ERRNO_MISSING_PRINCIPAL = 1; +const ERRNO_NO_SUCH_CHANNEL = 2; const ERRNO_UNKNOWN_ERROR = 999; const ERROR_UNKNOWN = "UNKNOWN_ERROR"; const {classes: Cc, interfaces: Ci, utils: Cu} = Components; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); @@ -78,32 +80,32 @@ var WebChannelBroker = Object.create({ } catch (e) { Cu.reportError("Failed to parse WebChannel data as a JSON object"); return; } } if (data && data.id) { if (!event.principal) { - this._sendErrorEventToContent(data.id, sendingContext, "Message principal missing"); + this._sendErrorEventToContent(data.id, sendingContext, ERRNO_MISSING_PRINCIPAL, "Message principal missing"); } else { let validChannelFound = false; data.message = data.message || {}; for (var channel of this._channelMap.keys()) { if (channel.id === data.id && channel._originCheckCallback(event.principal)) { validChannelFound = true; channel.deliver(data, sendingContext); } } // if no valid origins send an event that there is no such valid channel if (!validChannelFound) { - this._sendErrorEventToContent(data.id, sendingContext, "No Such Channel"); + this._sendErrorEventToContent(data.id, sendingContext, ERRNO_NO_SUCH_CHANNEL, "No Such Channel"); } } } else { Cu.reportError("WebChannel channel id missing"); } }, /** * The global message manager operates on every <browser> @@ -122,25 +124,28 @@ var WebChannelBroker = Object.create({ * @param id {String} * The WebChannel id to include in the message * @param sendingContext {Object} * Message sending context * @param [errorMsg] {String} * Error message * @private */ - _sendErrorEventToContent: function(id, sendingContext, errorMsg) { + _sendErrorEventToContent: function(id, sendingContext, errorNo, errorMsg) { let { browser: targetBrowser, eventTarget, principal: targetPrincipal } = sendingContext; errorMsg = errorMsg || "Web Channel Broker error"; if (targetBrowser && targetBrowser.messageManager) { targetBrowser.messageManager.sendAsyncMessage("WebChannelMessageToContent", { id: id, - error: errorMsg, + message: { + errno: errorNo, + error: errorMsg, + }, }, { eventTarget: eventTarget }, targetPrincipal); } else { Cu.reportError("Failed to send a WebChannel error. Target invalid."); } Cu.reportError(id.toString() + " error message. " + errorMsg); }, });
--- a/toolkit/modules/addons/WebRequest.jsm +++ b/toolkit/modules/addons/WebRequest.jsm @@ -775,32 +775,28 @@ HttpObserverManager = { if (opts.requestHeaders && result.requestHeaders && requestHeaders) { requestHeaders.applyChanges(result.requestHeaders); } if (opts.responseHeaders && result.responseHeaders && responseHeaders) { responseHeaders.applyChanges(result.responseHeaders); } } + + if (kind === "opening") { + yield this.runChannelListener(channel, loadContext, "modify"); + } else if (kind === "modify") { + yield this.runChannelListener(channel, loadContext, "afterModify"); + } } catch (e) { Cu.reportError(e); } - if (kind === "opening") { - return this.runChannelListener(channel, loadContext, "modify"); - } else if (kind === "modify") { - return this.runChannelListener(channel, loadContext, "afterModify"); - } - - // Only resume the channel if either it was suspended by this call, - // and this callback is not part of the opening-modify-afterModify - // chain, or if this is an afterModify callback. This allows us to - // chain the aforementioned handlers without repeatedly suspending - // and resuming the request. - if (shouldResume || kind === "afterModify") { + // Only resume the channel if either it was suspended by this call. + if (shouldResume) { this.maybeResume(channel); } }), examine(channel, topic, data) { let loadContext = this.getLoadContext(channel); if (this.needTracing) {
--- a/toolkit/themes/linux/global/notification.css +++ b/toolkit/themes/linux/global/notification.css @@ -63,12 +63,12 @@ notification[type="critical"] { .messageCloseButton { padding-left: 11px; padding-right: 11px; } %include ../../shared/popupnotification.inc.css -.popup-notification-button:focus { +.popup-notification-button:-moz-focusring { outline: 1px -moz-dialogtext dotted; outline-offset: -5px; }
--- a/toolkit/themes/osx/global/notification.css +++ b/toolkit/themes/osx/global/notification.css @@ -100,16 +100,16 @@ notification[type="info"]:not([value="tr @media (min-resolution: 2dppx) { .messageCloseButton > .toolbarbutton-icon { width: 16px; } } %include ../../shared/popupnotification.inc.css -.popup-notification-button:focus { +.popup-notification-button:-moz-focusring { outline: 2px -moz-mac-focusring solid; outline-offset: -2px; } .popup-notification-warning { color: #aa1b08; }
--- a/toolkit/themes/windows/global/notification.css +++ b/toolkit/themes/windows/global/notification.css @@ -63,17 +63,17 @@ notification[type="critical"] { } .messageCloseButton > .toolbarbutton-icon { margin-inline-end: 5px; } %include ../../shared/popupnotification.inc.css -.popup-notification-button:focus { +.popup-notification-button:-moz-focusring { outline: 1px -moz-dialogtext dotted; outline-offset: -1px; } /* Override default icon size which is too small for this dropdown */ .popup-notification-dropmarker > .button-box > .button-menu-dropmarker { width: 16px; height: 16px;