diff --git a/accessible/base/nsCoreUtils.cpp b/accessible/base/nsCoreUtils.cpp index 3437e8052339..37cb814c2d14 100644 --- a/accessible/base/nsCoreUtils.cpp +++ b/accessible/base/nsCoreUtils.cpp @@ -165,7 +165,7 @@ nsCoreUtils::DispatchTouchEvent(EventMessage aMessage, int32_t aX, int32_t aY, RefPtr t = new dom::Touch(-1, LayoutDeviceIntPoint(aX, aY), LayoutDeviceIntPoint(1, 1), 0.0f, 1.0f); t->SetTarget(aContent); - event.touches.AppendElement(t); + event.mTouches.AppendElement(t); nsEventStatus status = nsEventStatus_eIgnore; aPresShell->HandleEventWithTarget(&event, aFrame, aContent, &status); } diff --git a/browser/components/sessionstore/SessionStore.jsm b/browser/components/sessionstore/SessionStore.jsm index 7c64d10c9b66..dff3910de930 100644 --- a/browser/components/sessionstore/SessionStore.jsm +++ b/browser/components/sessionstore/SessionStore.jsm @@ -838,7 +838,7 @@ var SessionStoreInternal = { SessionStoreInternal._resetLocalTabRestoringState(tab); SessionStoreInternal.restoreNextTab(); - this._sendTabRestoredNotification(tab); + this._sendTabRestoredNotification(tab, data.isRemotenessUpdate); break; case "SessionStore:crashedTabRevived": // The browser was revived by navigating to a different page @@ -3286,6 +3286,9 @@ var SessionStoreInternal = { * the tab to restore * @param aLoadArguments * optional load arguments used for loadURI() + * @param aRemotenessSwitch + * true if we're restoring a tab's content because we flipped + * its remoteness (out-of-process) state. */ restoreTabContent: function (aTab, aLoadArguments = null) { if (aTab.hasAttribute("customizemode")) { @@ -3309,7 +3312,8 @@ var SessionStoreInternal = { // flip the remoteness of any browser that is not being displayed. this.markTabAsRestoring(aTab); - if (tabbrowser.updateBrowserRemotenessByURL(browser, uri)) { + let isRemotenessUpdate = tabbrowser.updateBrowserRemotenessByURL(browser, uri); + if (isRemotenessUpdate) { // We updated the remoteness, so we need to send the history down again. // // Start a new epoch to discard all frame script messages relating to a @@ -3332,7 +3336,7 @@ var SessionStoreInternal = { } browser.messageManager.sendAsyncMessage("SessionStore:restoreTabContent", - {loadArguments: aLoadArguments}); + {loadArguments: aLoadArguments, isRemotenessUpdate}); }, /** @@ -3977,11 +3981,17 @@ var SessionStoreInternal = { /** * Dispatch the SSTabRestored event for the given tab. - * @param aTab the which has been restored + * @param aTab + * The tab which has been restored + * @param aIsRemotenessUpdate + * True if this tab was restored due to flip from running from + * out-of-main-process to in-main-process or vice-versa. */ - _sendTabRestoredNotification: function ssi_sendTabRestoredNotification(aTab) { - let event = aTab.ownerDocument.createEvent("Events"); - event.initEvent("SSTabRestored", true, false); + _sendTabRestoredNotification(aTab, aIsRemotenessUpdate) { + let event = aTab.ownerDocument.createEvent("CustomEvent"); + event.initCustomEvent("SSTabRestored", true, false, { + isRemotenessUpdate: aIsRemotenessUpdate, + }); aTab.dispatchEvent(event); }, diff --git a/browser/components/sessionstore/StartupPerformance.jsm b/browser/components/sessionstore/StartupPerformance.jsm index bdce5852f836..d1b77a237e05 100644 --- a/browser/components/sessionstore/StartupPerformance.jsm +++ b/browser/components/sessionstore/StartupPerformance.jsm @@ -199,9 +199,16 @@ this.StartupPerformance = { // to reach that point. let win = subject; - let observer = () => { - this._latestRestoredTimeStamp = Date.now(); - this._totalNumberOfEagerTabs += 1; + let observer = (event) => { + // We don't care about tab restorations that are due to + // a browser flipping from out-of-main-process to in-main-process + // or vice-versa. We only care about restorations that are due + // to the user switching to a lazily restored tab, or for tabs + // that are restoring eagerly. + if (!event.detail.isRemotenessUpdate) { + this._latestRestoredTimeStamp = Date.now(); + this._totalNumberOfEagerTabs += 1; + } }; win.gBrowser.tabContainer.addEventListener("SSTabRestored", observer); this._totalNumberOfTabs += win.gBrowser.tabContainer.itemCount; diff --git a/browser/components/sessionstore/content/content-sessionStore.js b/browser/components/sessionstore/content/content-sessionStore.js index 7fedd998a6e6..8d25fea80461 100644 --- a/browser/components/sessionstore/content/content-sessionStore.js +++ b/browser/components/sessionstore/content/content-sessionStore.js @@ -167,21 +167,21 @@ var MessageListener = { sendSyncMessage("SessionStore:restoreHistoryComplete", {epoch}); }, - restoreTabContent({loadArguments}) { + restoreTabContent({loadArguments, isRemotenessUpdate}) { let epoch = gCurrentEpoch; // We need to pass the value of didStartLoad back to SessionStore.jsm. let didStartLoad = gContentRestore.restoreTabContent(loadArguments, () => { // Tell SessionStore.jsm that it may want to restore some more tabs, // since it restores a max of MAX_CONCURRENT_TAB_RESTORES at a time. - sendAsyncMessage("SessionStore:restoreTabContentComplete", {epoch}); + sendAsyncMessage("SessionStore:restoreTabContentComplete", {epoch, isRemotenessUpdate}); }); sendAsyncMessage("SessionStore:restoreTabContentStarted", {epoch}); if (!didStartLoad) { // Pretend that the load succeeded so that event handlers fire correctly. - sendAsyncMessage("SessionStore:restoreTabContentComplete", {epoch}); + sendAsyncMessage("SessionStore:restoreTabContentComplete", {epoch, isRemotenessUpdate}); } }, diff --git a/caps/nsScriptSecurityManager.cpp b/caps/nsScriptSecurityManager.cpp index f0b342c27ba8..dcb26b21a4cf 100644 --- a/caps/nsScriptSecurityManager.cpp +++ b/caps/nsScriptSecurityManager.cpp @@ -304,7 +304,12 @@ nsScriptSecurityManager::AppStatusForPrincipal(nsIPrincipal *aPrin) // The app could contain a cross-origin iframe - make sure that the content // is actually same-origin with the app. MOZ_ASSERT(inIsolatedMozBrowser == false, "Checked this above"); - PrincipalOriginAttributes attrs(appId, false); + nsAutoCString suffix; + PrincipalOriginAttributes attrs; + NS_ENSURE_TRUE(attrs.PopulateFromOrigin(NS_ConvertUTF16toUTF8(appOrigin), suffix), + nsIPrincipal::APP_STATUS_NOT_INSTALLED); + attrs.mAppId = appId; + attrs.mInIsolatedMozBrowser = false; nsCOMPtr appPrin = BasePrincipal::CreateCodebasePrincipal(appURI, attrs); NS_ENSURE_TRUE(appPrin, nsIPrincipal::APP_STATUS_NOT_INSTALLED); return aPrin->Equals(appPrin) ? status diff --git a/devtools/client/inspector/rules/test/browser_rules_completion-new-property_02.js b/devtools/client/inspector/rules/test/browser_rules_completion-new-property_02.js index 13c8c3e4a4c8..6eb1f24b6d3c 100644 --- a/devtools/client/inspector/rules/test/browser_rules_completion-new-property_02.js +++ b/devtools/client/inspector/rules/test/browser_rules_completion-new-property_02.js @@ -23,8 +23,9 @@ var testData = [ ["n", {}, "none", -1, 0, true], ["VK_TAB", {shiftKey: true}, "display", -1, 0, true], ["VK_BACK_SPACE", {}, "", -1, 0, false], - ["c", {}, "color", 5, 10, false], - ["o", {}, "color", 0, 7, false], + ["o", {}, "opacity", 6, 10, false], + ["u", {}, "outline", 0, 5, false], + ["VK_DOWN", {}, "outline-color", 1, 5, false], ["VK_TAB", {}, "none", -1, 0, true], ["r", {}, "rebeccapurple", 0, 6, true], ["VK_DOWN", {}, "red", 1, 6, true], diff --git a/devtools/client/sourceeditor/test/css_autocompletion_tests.json b/devtools/client/sourceeditor/test/css_autocompletion_tests.json index 070165e2f0f9..a455b2b601c1 100644 --- a/devtools/client/sourceeditor/test/css_autocompletion_tests.json +++ b/devtools/client/sourceeditor/test/css_autocompletion_tests.json @@ -18,8 +18,11 @@ [[12, 20], ['none', 'number-input']], [[12, 22], ['none']], [[17, 22], ['hsl', 'hsla']], + [[19, 10], ['background', 'background-attachment', 'background-blend-mode', + 'background-clip', 'background-color', 'background-image', + 'background-origin', 'background-position', 'background-repeat', + 'background-size']], [[21, 9], ["-moz-calc", "auto", "calc", "inherit", "initial","unset"]], - [[22, 5], ['color', 'color-interpolation', 'color-interpolation-filters']], [[25, 26], ['.devtools-toolbarbutton > tab', '.devtools-toolbarbutton > hbox', '.devtools-toolbarbutton > .toolbarbutton-menubutton-button']], diff --git a/devtools/client/webconsole/test/browser.ini b/devtools/client/webconsole/test/browser.ini index 621d1262bba4..eeb7176d3e31 100644 --- a/devtools/client/webconsole/test/browser.ini +++ b/devtools/client/webconsole/test/browser.ini @@ -193,6 +193,8 @@ skip-if = e10s && debug && (os == 'win' || os == 'mac') # Bug 1243966 [browser_warn_user_about_replaced_api.js] [browser_webconsole_allow_mixedcontent_securityerrors.js] tags = mcb +[browser_webconsole_script_errordoc_urls.js] +skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s [browser_webconsole_assert.js] [browser_webconsole_block_mixedcontent_securityerrors.js] tags = mcb diff --git a/devtools/client/webconsole/test/browser_webconsole_script_errordoc_urls.js b/devtools/client/webconsole/test/browser_webconsole_script_errordoc_urls.js new file mode 100644 index 000000000000..7c4ef4225044 --- /dev/null +++ b/devtools/client/webconsole/test/browser_webconsole_script_errordoc_urls.js @@ -0,0 +1,67 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Ensure that [Learn More] links appear alongside any errors listed +// in "errordocs.js". Note: this only tests script execution. + +"use strict"; + +const ErrorDocs = require("devtools/server/actors/errordocs"); + +function makeURIData(script) { + return `data:text/html;charset=utf8,` +} + +const TestData = [ + { + jsmsg: "JSMSG_READ_ONLY", + script: "'use strict'; (Object.freeze({name: 'Elsa', score: 157})).score = 0;", + isException: true, + }, + { + jsmsg: "JSMSG_STMT_AFTER_RETURN", + script: "function a() { return; 1 + 1; };", + isException: false, + } +] + +add_task(function* () { + yield loadTab("data:text/html;charset=utf8,errordoc tests"); + + let hud = yield openConsole(); + + for (let i = 0; i < TestData.length; i++) { + yield testScriptError(hud, TestData[i]); + } +}); + +function* testScriptError(hud, testData) { + if (testData.isException === true) { + expectUncaughtException(); + } + + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, makeURIData(testData.script)); + + yield waitForMessages({ + webconsole: hud, + messages: [ + { + category: CATEGORY_JS + } + ] + }); + + // grab the most current error doc URL + let url = ErrorDocs.GetURL(testData.jsmsg); + + let hrefs = {}; + for (let link of hud.jsterm.outputNode.querySelectorAll("a")) { + hrefs[link.href] = true; + } + + ok(url in hrefs, `Expected a link to ${url}.`); + + hud.jsterm.clearOutput(); +} diff --git a/devtools/client/webconsole/webconsole.js b/devtools/client/webconsole/webconsole.js index e21381914b25..d56d8f33b8b1 100644 --- a/devtools/client/webconsole/webconsole.js +++ b/devtools/client/webconsole/webconsole.js @@ -16,6 +16,7 @@ Cu.import("resource://devtools/client/shared/browser-loader.js", BrowserLoaderMo const promise = require("promise"); const Services = require("Services"); +const ErrorDocs = require("devtools/server/actors/errordocs"); loader.lazyServiceGetter(this, "clipboardHelper", "@mozilla.org/widget/clipboardhelper;1", @@ -1489,6 +1490,7 @@ WebConsoleFrame.prototype = { // Select the body of the message node that is displayed in the console let msgBody = node.getElementsByClassName("message-body")[0]; + // Add the more info link node to messages that belong to certain categories this.addMoreInfoLink(msgBody, scriptError); @@ -1697,11 +1699,14 @@ WebConsoleFrame.prototype = { url = TRACKING_PROTECTION_LEARN_MORE; break; default: - // Unknown category. Return without adding more info node. - return; + // If all else fails check for an error doc URL. + url = ErrorDocs.GetURL(scriptError.errorMessageName); + break; } - this.addLearnMoreWarningNode(node, url); + if (url) { + this.addLearnMoreWarningNode(node, url); + } }, /* diff --git a/devtools/server/actors/errordocs.js b/devtools/server/actors/errordocs.js new file mode 100644 index 000000000000..caaedb427e87 --- /dev/null +++ b/devtools/server/actors/errordocs.js @@ -0,0 +1,23 @@ +/* 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/. */ + +/** + * A mapping of error message names to external documentation. Any error message + * included here will be displayed alongside its link in the web console. + */ + +"use strict"; + +const ErrorDocs = { + JSMSG_READ_ONLY: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Read-only", + JSMSG_BAD_ARRAY_LENGTH: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Invalid_array_length", + JSMSG_NEGATIVE_REPETITION_COUNT: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Negative_repetition_count", + JSMSG_RESULTING_STRING_TOO_LARGE: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Resulting_string_too_large", + JSMSG_BAD_RADIX: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Bad_radix", + JSMSG_PRECISION_RANGE: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Precision_range", + JSMSG_BAD_FORMAL: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Malformed_formal_parameter", + JSMSG_STMT_AFTER_RETURN: "https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Stmt_after_return", +} + +exports.GetURL = (errorName) => ErrorDocs[errorName]; diff --git a/devtools/server/actors/moz.build b/devtools/server/actors/moz.build index 4d2d8ab9fbd0..0dc85698f3bd 100644 --- a/devtools/server/actors/moz.build +++ b/devtools/server/actors/moz.build @@ -25,6 +25,7 @@ DevToolsModules( 'director-manager.js', 'director-registry.js', 'environment.js', + 'errordocs.js', 'eventlooplag.js', 'frame.js', 'framerate.js', diff --git a/devtools/server/actors/webconsole.js b/devtools/server/actors/webconsole.js index 4b43bf414f56..1c404f324226 100644 --- a/devtools/server/actors/webconsole.js +++ b/devtools/server/actors/webconsole.js @@ -1392,6 +1392,7 @@ WebConsoleActor.prototype = return { errorMessage: this._createStringGrip(aPageError.errorMessage), + errorMessageName: aPageError.errorMessageName, sourceName: aPageError.sourceName, lineText: lineText, lineNumber: aPageError.lineNumber, diff --git a/devtools/shared/webconsole/test/test_page_errors.html b/devtools/shared/webconsole/test/test_page_errors.html index 7a924667fc77..c7fb167b1e2c 100644 --- a/devtools/shared/webconsole/test/test_page_errors.html +++ b/devtools/shared/webconsole/test/test_page_errors.html @@ -18,9 +18,10 @@ let expectedPageErrors = []; function doPageErrors() { - expectedPageErrors = [ - { + expectedPageErrors = { + "document.body.style.color = 'fooColor';": { errorMessage: /fooColor/, + errorMessageName: undefined, sourceName: /test_page_errors/, category: "CSS Parser", timeStamp: /^\d+$/, @@ -29,8 +30,9 @@ function doPageErrors() exception: false, strict: false, }, - { + "document.doTheImpossible();": { errorMessage: /doTheImpossible/, + errorMessageName: undefined, sourceName: /test_page_errors/, category: "chrome javascript", timeStamp: /^\d+$/, @@ -39,19 +41,101 @@ function doPageErrors() exception: true, strict: false, }, - ]; + "(42).toString(0);": { + errorMessage: /radix/, + errorMessageName: "JSMSG_BAD_RADIX", + sourceName: /test_page_errors/, + category: "chrome javascript", + timeStamp: /^\d+$/, + error: false, + warning: false, + exception: true, + strict: false, + }, + "'use strict'; (Object.freeze({name: 'Elsa', score: 157})).score = 0;": { + errorMessage: /read.only/, + errorMessageName: "JSMSG_READ_ONLY", + sourceName: /test_page_errors/, + category: "chrome javascript", + timeStamp: /^\d+$/, + error: false, + warning: false, + exception: true, + }, + "([]).length = -1": { + errorMessage: /array length/, + errorMessageName: "JSMSG_BAD_ARRAY_LENGTH", + sourceName: /test_page_errors/, + category: "chrome javascript", + timeStamp: /^\d+$/, + error: false, + warning: false, + exception: true, + }, + "'abc'.repeat(-1);": { + errorMessage: /repeat count.*non-negative/, + errorMessageName: "JSMSG_NEGATIVE_REPETITION_COUNT", + sourceName: /test_page_errors/, + category: "chrome javascript", + timeStamp: /^\d+$/, + error: false, + warning: false, + exception: true, + }, + "'a'.repeat(2**28);": { + errorMessage: /repeat count.*less than infinity/, + errorMessageName: "JSMSG_RESULTING_STRING_TOO_LARGE", + sourceName: /test_page_errors/, + category: "chrome javascript", + timeStamp: /^\d+$/, + error: false, + warning: false, + exception: true, + }, + "77.1234.toExponential(-1);": { + errorMessage: /out of range/, + errorMessageName: "JSMSG_PRECISION_RANGE", + sourceName: /test_page_errors/, + category: "chrome javascript", + timeStamp: /^\d+$/, + error: false, + warning: false, + exception: true, + }, + "var f = Function('x y', 'return x + y;');": { + errorMessage: /malformed formal/, + errorMessageName: "JSMSG_BAD_FORMAL", + sourceName: /test_page_errors/, + category: "chrome javascript", + timeStamp: /^\d+$/, + error: false, + warning: false, + exception: true, + }, + "function a() { return; 1 + 1; }": { + errorMessage: /unreachable code/, + errorMessageName: "JSMSG_STMT_AFTER_RETURN", + sourceName: /test_page_errors/, + category: "chrome javascript", + timeStamp: /^\d+$/, + error: false, + warning: true, + exception: false, + }, + }; let container = document.createElement("script"); - document.body.appendChild(container); - container.textContent = "document.body.style.color = 'fooColor';"; - document.body.removeChild(container); - - SimpleTest.expectUncaughtException(); - - container = document.createElement("script"); - document.body.appendChild(container); - container.textContent = "document.doTheImpossible();"; - document.body.removeChild(container); + for (let stmt of Object.keys(expectedPageErrors)) { + if (expectedPageErrors[stmt].exception) { + SimpleTest.expectUncaughtException(); + } + info("starting stmt: " + stmt); + container = document.createElement("script"); + document.body.appendChild(container); + container.textContent = stmt; + document.body.removeChild(container); + info("ending stmt: " + stmt); + } } function startTest() @@ -80,15 +164,15 @@ function onPageError(aState, aType, aPacket) is(aPacket.from, aState.actor, "page error actor"); pageErrors.push(aPacket.pageError); - if (pageErrors.length != expectedPageErrors.length) { + if (pageErrors.length != Object.keys(expectedPageErrors).length) { return; } aState.dbgClient.removeListener("pageError", onPageError); - expectedPageErrors.forEach(function(aMessage, aIndex) { + Object.values(expectedPageErrors).forEach(function(aMessage, aIndex) { info("checking received page error #" + aIndex); - checkObject(pageErrors[aIndex], expectedPageErrors[aIndex]); + checkObject(pageErrors[aIndex], Object.values(expectedPageErrors)[aIndex]); }); closeDebugger(aState, function() { diff --git a/dom/base/nsContentIterator.cpp b/dom/base/nsContentIterator.cpp index 9f3f8daf806a..092d9a46add2 100644 --- a/dom/base/nsContentIterator.cpp +++ b/dom/base/nsContentIterator.cpp @@ -774,7 +774,11 @@ nsContentIterator::NextNode(nsINode* aNode, nsTArray* aIndexes) // post-order nsINode* parent = node->GetParentNode(); - NS_WARN_IF(!parent); + if (NS_WARN_IF(!parent)) { + MOZ_ASSERT(parent, "The node is the root node but not the last node"); + mIsDone = true; + return node; + } nsIContent* sibling = nullptr; int32_t indx = 0; @@ -839,7 +843,11 @@ nsContentIterator::PrevNode(nsINode* aNode, nsTArray* aIndexes) // if we are a Pre-order iterator, use pre-order if (mPre) { nsINode* parent = node->GetParentNode(); - NS_WARN_IF(!parent); + if (NS_WARN_IF(!parent)) { + MOZ_ASSERT(parent, "The node is the root node but not the first node"); + mIsDone = true; + return aNode; + } nsIContent* sibling = nullptr; int32_t indx = 0; diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index 993b896a921d..b407238f49bb 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -945,7 +945,7 @@ nsDOMWindowUtils::SendTouchEventCommon(const nsAString& aType, if (!presContext) { return NS_ERROR_FAILURE; } - event.touches.SetCapacity(aCount); + event.mTouches.SetCapacity(aCount); for (uint32_t i = 0; i < aCount; ++i) { LayoutDeviceIntPoint pt = nsContentUtils::ToWidgetPoint(CSSPoint(aXs[i], aYs[i]), offset, presContext); @@ -957,7 +957,7 @@ nsDOMWindowUtils::SendTouchEventCommon(const nsAString& aType, RefPtr t = new Touch(aIdentifiers[i], pt, radius, aRotationAngles[i], aForces[i]); - event.touches.AppendElement(t); + event.mTouches.AppendElement(t); } nsEventStatus status; diff --git a/dom/base/nsXMLHttpRequest.cpp b/dom/base/nsXMLHttpRequest.cpp index 77c465ca012a..f4d08979683c 100644 --- a/dom/base/nsXMLHttpRequest.cpp +++ b/dom/base/nsXMLHttpRequest.cpp @@ -1566,8 +1566,13 @@ nsXMLHttpRequest::Open(const nsACString& inMethod, const nsACString& url, } rv = NS_NewURI(getter_AddRefs(uri), url, nullptr, baseURI); - if (NS_FAILED(rv)) return rv; + if (NS_FAILED(rv)) { + if (rv == NS_ERROR_MALFORMED_URI) { + return NS_ERROR_DOM_SYNTAX_ERR; + } + return rv; + } rv = CheckInnerWindowCorrectness(); NS_ENSURE_SUCCESS(rv, rv); diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp index f8e1ee6e9432..ad1bf5ffad84 100644 --- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -1314,7 +1314,7 @@ EventStateManager::HandleCrossProcessEvent(WidgetEvent* aEvent, // This loop is similar to the one used in // PresShell::DispatchTouchEvent(). const WidgetTouchEvent::TouchArray& touches = - aEvent->AsTouchEvent()->touches; + aEvent->AsTouchEvent()->mTouches; for (uint32_t i = 0; i < touches.Length(); ++i) { Touch* touch = touches[i]; // NB: the |mChanged| check is an optimization, subprocesses can diff --git a/dom/events/TouchEvent.cpp b/dom/events/TouchEvent.cpp index 7743babf5689..47c23b475d33 100644 --- a/dom/events/TouchEvent.cpp +++ b/dom/events/TouchEvent.cpp @@ -69,8 +69,8 @@ TouchEvent::TouchEvent(EventTarget* aOwner, if (aEvent) { mEventIsInternal = false; - for (uint32_t i = 0; i < aEvent->touches.Length(); ++i) { - Touch* touch = aEvent->touches[i]; + for (uint32_t i = 0; i < aEvent->mTouches.Length(); ++i) { + Touch* touch = aEvent->mTouches[i]; touch->InitializePoints(mPresContext, aEvent); } } else { @@ -118,9 +118,9 @@ TouchEvent::Touches() if (!mTouches) { WidgetTouchEvent* touchEvent = mEvent->AsTouchEvent(); if (mEvent->mMessage == eTouchEnd || mEvent->mMessage == eTouchCancel) { - // for touchend events, remove any changed touches from the touches array + // for touchend events, remove any changed touches from mTouches WidgetTouchEvent::AutoTouchArray unchangedTouches; - const WidgetTouchEvent::TouchArray& touches = touchEvent->touches; + const WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches; for (uint32_t i = 0; i < touches.Length(); ++i) { if (!touches[i]->mChanged) { unchangedTouches.AppendElement(touches[i]); @@ -128,7 +128,7 @@ TouchEvent::Touches() } mTouches = new TouchList(ToSupports(this), unchangedTouches); } else { - mTouches = new TouchList(ToSupports(this), touchEvent->touches); + mTouches = new TouchList(ToSupports(this), touchEvent->mTouches); } } return mTouches; @@ -140,7 +140,7 @@ TouchEvent::TargetTouches() if (!mTargetTouches) { WidgetTouchEvent::AutoTouchArray targetTouches; WidgetTouchEvent* touchEvent = mEvent->AsTouchEvent(); - const WidgetTouchEvent::TouchArray& touches = touchEvent->touches; + const WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches; for (uint32_t i = 0; i < touches.Length(); ++i) { // for touchend/cancel events, don't append to the target list if this is a // touch that is ending @@ -162,7 +162,7 @@ TouchEvent::ChangedTouches() if (!mChangedTouches) { WidgetTouchEvent::AutoTouchArray changedTouches; WidgetTouchEvent* touchEvent = mEvent->AsTouchEvent(); - const WidgetTouchEvent::TouchArray& touches = touchEvent->touches; + const WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches; for (uint32_t i = 0; i < touches.Length(); ++i) { if (touches[i]->mChanged) { changedTouches.AppendElement(touches[i]); diff --git a/dom/events/test/mochitest.ini b/dom/events/test/mochitest.ini index 9360f98af791..e35f80953f24 100644 --- a/dom/events/test/mochitest.ini +++ b/dom/events/test/mochitest.ini @@ -151,7 +151,7 @@ support-files = bug1017086_inner.html [test_clickevent_on_input.html] skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM [test_continuous_wheel_events.html] -skip-if = buildapp == 'b2g' || e10s # b2g(5535 passed, 108 failed - more tests running than desktop) b2g-debug(5535 passed, 108 failed - more tests running than desktop) b2g-desktop(5535 passed, 108 failed - more tests running than desktop) +skip-if = buildapp == 'b2g' # b2g(5535 passed, 108 failed - more tests running than desktop) b2g-debug(5535 passed, 108 failed - more tests running than desktop) b2g-desktop(5535 passed, 108 failed - more tests running than desktop) [test_dblclick_explicit_original_target.html] [test_dom_keyboard_event.html] skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM diff --git a/dom/events/test/test_continuous_wheel_events.html b/dom/events/test/test_continuous_wheel_events.html index 74d5b9ba5611..fc8c69390c8d 100644 --- a/dom/events/test/test_continuous_wheel_events.html +++ b/dom/events/test/test_continuous_wheel_events.html @@ -4,6 +4,7 @@ Test for D3E WheelEvent + @@ -39,7 +40,7 @@
 
 
diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp index 7dc93870461c..961bc4884b98 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -4242,7 +4242,7 @@ HTMLInputElement::PostHandleEventForRangeThumb(EventChainPostVisitor& aVisitor) CancelRangeThumbDrag(); } } else { - if (aVisitor.mEvent->AsTouchEvent()->touches.Length() == 1) { + if (aVisitor.mEvent->AsTouchEvent()->mTouches.Length() == 1) { StartRangeThumbDrag(inputEvent); } else if (mIsDraggingRange) { CancelRangeThumbDrag(); diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index a40b8da85c59..75f5aedc8b61 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -2492,6 +2492,10 @@ ContentChild::RecvAddPermission(const IPC::Permission& permission) nsAutoCString originNoSuffix; PrincipalOriginAttributes attrs; attrs.PopulateFromOrigin(permission.origin, originNoSuffix); + // we're doing this because we currently don't support isolating permissions + // by userContextId. + MOZ_ASSERT(attrs.mUserContextId == nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID, + "permission user context should be set to default!"); nsCOMPtr uri; nsresult rv = NS_NewURI(getter_AddRefs(uri), originNoSuffix); diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index c6779b0a062e..a700293ac9b5 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -1658,9 +1658,9 @@ bool TabParent::SendRealTouchEvent(WidgetTouchEvent& event) // that the added touches are part of the touchend/cancel, when actually // they're not. if (event.mMessage == eTouchEnd || event.mMessage == eTouchCancel) { - for (int i = event.touches.Length() - 1; i >= 0; i--) { - if (!event.touches[i]->mChanged) { - event.touches.RemoveElementAt(i); + for (int i = event.mTouches.Length() - 1; i >= 0; i--) { + if (!event.mTouches[i]->mChanged) { + event.mTouches.RemoveElementAt(i); } } } @@ -1675,8 +1675,8 @@ bool TabParent::SendRealTouchEvent(WidgetTouchEvent& event) } LayoutDeviceIntPoint offset = GetChildProcessOffset(); - for (uint32_t i = 0; i < event.touches.Length(); i++) { - event.touches[i]->mRefPoint += offset; + for (uint32_t i = 0; i < event.mTouches.Length(); i++) { + event.mTouches[i]->mRefPoint += offset; } return (event.mMessage == eTouchMove) ? @@ -2778,7 +2778,7 @@ TabParent::InjectTouchEvent(const nsAString& aType, } nsPresContext* presContext = doc->GetShell()->GetPresContext(); - event.touches.SetCapacity(aCount); + event.mTouches.SetCapacity(aCount); for (uint32_t i = 0; i < aCount; ++i) { LayoutDeviceIntPoint pt = LayoutDeviceIntPoint::FromAppUnitsRounded( @@ -2797,7 +2797,7 @@ TabParent::InjectTouchEvent(const nsAString& aType, // about the meaning of changedTouches for each event, see // https://developer.mozilla.org/docs/Web/API/TouchEvent.changedTouches t->mChanged = true; - event.touches.AppendElement(t); + event.mTouches.AppendElement(t); } SendRealTouchEvent(event); diff --git a/dom/media/GraphDriver.cpp b/dom/media/GraphDriver.cpp index 3d8ec285758b..46041f00bcb2 100644 --- a/dom/media/GraphDriver.cpp +++ b/dom/media/GraphDriver.cpp @@ -620,7 +620,9 @@ AudioCallbackDriver::Init() // We have to translate the deviceID values to cubeb devid's since those can be // freed whenever enumerate is called. { +#ifdef MOZ_WEBRTC StaticMutexAutoLock lock(AudioInputCubeb::Mutex()); +#endif if ((!mGraphImpl->mInputWanted #ifdef MOZ_WEBRTC || AudioInputCubeb::GetDeviceID(mGraphImpl->mInputDeviceID, input_id) @@ -645,7 +647,9 @@ AudioCallbackDriver::Init() DataCallback_s, StateCallback_s, this) == CUBEB_OK) { mAudioStream.own(stream); } else { +#ifdef MOZ_WEBRTC StaticMutexAutoUnlock unlock(AudioInputCubeb::Mutex()); +#endif NS_WARNING("Could not create a cubeb stream for MediaStreamGraph, falling back to a SystemClockDriver"); // Fall back to a driver using a normal thread. MonitorAutoLock lock(GraphImpl()->GetMonitor()); diff --git a/dom/media/webspeech/synth/nsSpeechTask.cpp b/dom/media/webspeech/synth/nsSpeechTask.cpp index 11ddd7d84976..ee300fbeb2bf 100644 --- a/dom/media/webspeech/synth/nsSpeechTask.cpp +++ b/dom/media/webspeech/synth/nsSpeechTask.cpp @@ -351,8 +351,6 @@ nsSpeechTask::DispatchStart() nsresult nsSpeechTask::DispatchStartInner() { - CreateAudioChannelAgent(); - nsSynthVoiceRegistry::GetInstance()->SetIsSpeaking(true); return DispatchStartImpl(); } @@ -373,6 +371,8 @@ nsSpeechTask::DispatchStartImpl(const nsAString& aUri) return NS_ERROR_NOT_AVAILABLE; } + CreateAudioChannelAgent(); + mUtterance->mState = SpeechSynthesisUtterance::STATE_SPEAKING; mUtterance->mChosenVoiceURI = aUri; mUtterance->DispatchSpeechSynthesisEvent(NS_LITERAL_STRING("start"), 0, 0, @@ -395,8 +395,6 @@ nsSpeechTask::DispatchEnd(float aElapsedTime, uint32_t aCharIndex) nsresult nsSpeechTask::DispatchEndInner(float aElapsedTime, uint32_t aCharIndex) { - DestroyAudioChannelAgent(); - if (!mPreCanceled) { nsSynthVoiceRegistry::GetInstance()->SpeakNext(); } @@ -409,6 +407,8 @@ nsSpeechTask::DispatchEndImpl(float aElapsedTime, uint32_t aCharIndex) { LOG(LogLevel::Debug, ("nsSpeechTask::DispatchEnd\n")); + DestroyAudioChannelAgent(); + MOZ_ASSERT(mUtterance); if(NS_WARN_IF(mUtterance->mState == SpeechSynthesisUtterance::STATE_ENDED)) { return NS_ERROR_NOT_AVAILABLE; diff --git a/editor/libeditor/nsHTMLEditRules.cpp b/editor/libeditor/nsHTMLEditRules.cpp index c4c5fd4d5c01..938b24393b46 100644 --- a/editor/libeditor/nsHTMLEditRules.cpp +++ b/editor/libeditor/nsHTMLEditRules.cpp @@ -5025,7 +5025,7 @@ nsHTMLEditRules::CheckForEmptyBlock(nsINode* aStartNode, // Move to the start of the next node if it's a text. nsCOMPtr nextNode = mHTMLEditor->GetNextNode(blockParent, offset + 1, true); - if (mHTMLEditor->IsTextNode(nextNode)) { + if (nextNode && mHTMLEditor->IsTextNode(nextNode)) { res = aSelection->Collapse(nextNode, 0); NS_ENSURE_SUCCESS(res, res); } @@ -5034,7 +5034,7 @@ nsHTMLEditRules::CheckForEmptyBlock(nsINode* aStartNode, nsCOMPtr priorNode = mHTMLEditor->GetPriorNode(blockParent, offset, true); - if (mHTMLEditor->IsTextNode(priorNode)) { + if (priorNode && mHTMLEditor->IsTextNode(priorNode)) { res = aSelection->Collapse(priorNode, priorNode->TextLength()); NS_ENSURE_SUCCESS(res, res); } else { diff --git a/gfx/ipc/GfxMessageUtils.h b/gfx/ipc/GfxMessageUtils.h index 857211830633..be42676c8259 100644 --- a/gfx/ipc/GfxMessageUtils.h +++ b/gfx/ipc/GfxMessageUtils.h @@ -713,6 +713,7 @@ struct ParamTraits WriteParam(aMsg, aParam.mIsLayersIdRoot); WriteParam(aMsg, aParam.mUsesContainerScrolling); WriteParam(aMsg, aParam.mIsScrollInfoLayer); + WriteParam(aMsg, aParam.mForceDisableApz); } static bool ReadContentDescription(const Message* aMsg, void** aIter, paramType* aResult) @@ -772,7 +773,8 @@ struct ParamTraits ReadBoolForBitfield(aMsg, aIter, aResult, ¶mType::SetAllowVerticalScrollWithWheel) && ReadBoolForBitfield(aMsg, aIter, aResult, ¶mType::SetIsLayersIdRoot) && ReadBoolForBitfield(aMsg, aIter, aResult, ¶mType::SetUsesContainerScrolling) && - ReadBoolForBitfield(aMsg, aIter, aResult, ¶mType::SetIsScrollInfoLayer)); + ReadBoolForBitfield(aMsg, aIter, aResult, ¶mType::SetIsScrollInfoLayer) && + ReadBoolForBitfield(aMsg, aIter, aResult, ¶mType::SetForceDisableApz)); } }; diff --git a/gfx/layers/FrameMetrics.h b/gfx/layers/FrameMetrics.h index b034cb231cb2..234af27ad152 100644 --- a/gfx/layers/FrameMetrics.h +++ b/gfx/layers/FrameMetrics.h @@ -74,6 +74,7 @@ public: , mIsLayersIdRoot(false) , mUsesContainerScrolling(false) , mIsScrollInfoLayer(false) + , mForceDisableApz(false) { } @@ -113,7 +114,8 @@ public: mAllowVerticalScrollWithWheel == aOther.mAllowVerticalScrollWithWheel && mIsLayersIdRoot == aOther.mIsLayersIdRoot && mUsesContainerScrolling == aOther.mUsesContainerScrolling && - mIsScrollInfoLayer == aOther.mIsScrollInfoLayer; + mIsScrollInfoLayer == aOther.mIsScrollInfoLayer && + mForceDisableApz == aOther.mForceDisableApz; } bool operator!=(const FrameMetrics& aOther) const @@ -554,6 +556,13 @@ public: return mIsScrollInfoLayer; } + void SetForceDisableApz(bool aForceDisable) { + mForceDisableApz = aForceDisable; + } + bool IsApzForceDisabled() const { + return mForceDisableApz; + } + private: // A unique ID assigned to each scrollable frame. ViewID mScrollId; @@ -742,6 +751,10 @@ private: // Whether or not this frame has a "scroll info layer" to capture events. bool mIsScrollInfoLayer:1; + // Whether or not the compositor should actually do APZ-scrolling on this + // scrollframe. + bool mForceDisableApz:1; + // WARNING!!!! // // When adding new fields to FrameMetrics, the following places should be diff --git a/gfx/layers/apz/src/APZCTreeManager.cpp b/gfx/layers/apz/src/APZCTreeManager.cpp index 0eac8dac3e26..e720d67b03f1 100644 --- a/gfx/layers/apz/src/APZCTreeManager.cpp +++ b/gfx/layers/apz/src/APZCTreeManager.cpp @@ -1152,10 +1152,11 @@ APZCTreeManager::ReceiveInputEvent(WidgetInputEvent& aEvent, // touch points (if we are overscrolled), and the coordinates were // modified using the APZ untransform. We need to copy these changes // back into the WidgetInputEvent. - touchEvent.touches.Clear(); - touchEvent.touches.SetCapacity(touchInput.mTouches.Length()); + touchEvent.mTouches.Clear(); + touchEvent.mTouches.SetCapacity(touchInput.mTouches.Length()); for (size_t i = 0; i < touchInput.mTouches.Length(); i++) { - *touchEvent.touches.AppendElement() = touchInput.mTouches[i].ToNewDOMTouch(); + *touchEvent.mTouches.AppendElement() = + touchInput.mTouches[i].ToNewDOMTouch(); } touchEvent.mFlags.mHandledByAPZ = touchInput.mHandledByAPZ; return result; @@ -1934,7 +1935,7 @@ APZCTreeManager::GetScreenToApzcTransform(const AsyncPanZoomController *aApzc) c // ancestorUntransform is updated to RC.Inverse() * QC.Inverse() when parent == P ancestorUntransform = parent->GetAncestorTransform().Inverse(); // asyncUntransform is updated to PA.Inverse() when parent == P - Matrix4x4 asyncUntransform = parent->GetCurrentAsyncTransformWithOverscroll().Inverse().ToUnknownMatrix(); + Matrix4x4 asyncUntransform = parent->GetCurrentAsyncTransformWithOverscroll(AsyncPanZoomController::NORMAL).Inverse().ToUnknownMatrix(); // untransformSinceLastApzc is RC.Inverse() * QC.Inverse() * PA.Inverse() Matrix4x4 untransformSinceLastApzc = ancestorUntransform * asyncUntransform; @@ -1966,7 +1967,7 @@ APZCTreeManager::GetApzcToGeckoTransform(const AsyncPanZoomController *aApzc) co // leftmost matrix in a multiplication is applied first. // asyncUntransform is LA.Inverse() - Matrix4x4 asyncUntransform = aApzc->GetCurrentAsyncTransformWithOverscroll().Inverse().ToUnknownMatrix(); + Matrix4x4 asyncUntransform = aApzc->GetCurrentAsyncTransformWithOverscroll(AsyncPanZoomController::NORMAL).Inverse().ToUnknownMatrix(); // aTransformToGeckoOut is initialized to LA.Inverse() * LD * MC * NC * OC * PC result = asyncUntransform * aApzc->GetTransformToLastDispatchedPaint() * aApzc->GetAncestorTransform(); diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp index bee08b48bcf8..c6dcabc7b3d4 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -156,6 +156,14 @@ using mozilla::gfx::PointTyped; * pixels would make us drop to low-res at y=490...990.\n * This value is in layer pixels. * + * \li\b apz.disable_for_scroll_linked_effects + * Setting this pref to true will disable APZ scrolling on documents where + * scroll-linked effects are detected. A scroll linked effect is detected if + * positioning or transform properties are updated inside a scroll event + * dispatch; we assume that such an update is in response to the scroll event + * and is therefore a scroll-linked effect which will be laggy with APZ + * scrolling. + * * \li\b apz.displayport_expiry_ms * While a scrollable frame is scrolling async, we set a displayport on it * to make sure it is layerized. However this takes up memory, so once the @@ -3030,8 +3038,15 @@ bool AsyncPanZoomController::UpdateAnimation(const TimeStamp& aSampleTime, return false; } -AsyncTransformComponentMatrix AsyncPanZoomController::GetOverscrollTransform() const { +AsyncTransformComponentMatrix +AsyncPanZoomController::GetOverscrollTransform(AsyncMode aMode) const +{ ReentrantMonitorAutoEnter lock(mMonitor); + + if (aMode == RESPECT_FORCE_DISABLE && mFrameMetrics.IsApzForceDisabled()) { + return AsyncTransformComponentMatrix(); + } + if (!IsOverscrolled()) { return AsyncTransformComponentMatrix(); } @@ -3124,18 +3139,28 @@ bool AsyncPanZoomController::AdvanceAnimations(const TimeStamp& aSampleTime) return requestAnimationFrame; } -void AsyncPanZoomController::SampleContentTransformForFrame(AsyncTransform* aOutTransform, - ParentLayerPoint& aScrollOffset) +ParentLayerPoint +AsyncPanZoomController::GetCurrentAsyncScrollOffset(AsyncMode aMode) const { ReentrantMonitorAutoEnter lock(mMonitor); - aScrollOffset = mFrameMetrics.GetScrollOffset() * mFrameMetrics.GetZoom(); - *aOutTransform = GetCurrentAsyncTransform(); + if (aMode == RESPECT_FORCE_DISABLE && mFrameMetrics.IsApzForceDisabled()) { + return mLastContentPaintMetrics.GetScrollOffset() * mLastContentPaintMetrics.GetZoom(); + } + + return (mFrameMetrics.GetScrollOffset() + mTestAsyncScrollOffset) + * mFrameMetrics.GetZoom() * mTestAsyncZoom.scale; } -AsyncTransform AsyncPanZoomController::GetCurrentAsyncTransform() const { +AsyncTransform +AsyncPanZoomController::GetCurrentAsyncTransform(AsyncMode aMode) const +{ ReentrantMonitorAutoEnter lock(mMonitor); + if (aMode == RESPECT_FORCE_DISABLE && mFrameMetrics.IsApzForceDisabled()) { + return AsyncTransform(); + } + CSSPoint lastPaintScrollOffset; if (mLastContentPaintMetrics.IsScrollable()) { lastPaintScrollOffset = mLastContentPaintMetrics.GetScrollOffset(); @@ -3170,9 +3195,11 @@ AsyncTransform AsyncPanZoomController::GetCurrentAsyncTransform() const { -translation); } -AsyncTransformComponentMatrix AsyncPanZoomController::GetCurrentAsyncTransformWithOverscroll() const { - return AsyncTransformComponentMatrix(GetCurrentAsyncTransform()) - * GetOverscrollTransform(); +AsyncTransformComponentMatrix +AsyncPanZoomController::GetCurrentAsyncTransformWithOverscroll(AsyncMode aMode) const +{ + return AsyncTransformComponentMatrix(GetCurrentAsyncTransform(aMode)) + * GetOverscrollTransform(aMode); } Matrix4x4 AsyncPanZoomController::GetTransformToLastDispatchedPaint() const { @@ -3436,6 +3463,7 @@ void AsyncPanZoomController::NotifyLayersUpdated(const ScrollMetadata& aScrollMe mFrameMetrics.SetIsLayersIdRoot(aLayerMetrics.IsLayersIdRoot()); mFrameMetrics.SetUsesContainerScrolling(aLayerMetrics.UsesContainerScrolling()); mFrameMetrics.SetIsScrollInfoLayer(aLayerMetrics.IsScrollInfoLayer()); + mFrameMetrics.SetForceDisableApz(aLayerMetrics.IsApzForceDisabled()); if (scrollOffsetUpdated) { APZC_LOG("%p updating scroll offset from %s to %s\n", this, diff --git a/gfx/layers/apz/src/AsyncPanZoomController.h b/gfx/layers/apz/src/AsyncPanZoomController.h index 1ebed90d5f26..b9944b3b2a75 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.h +++ b/gfx/layers/apz/src/AsyncPanZoomController.h @@ -162,21 +162,6 @@ public: bool UpdateAnimation(const TimeStamp& aSampleTime, Vector* aOutDeferredTasks); - /** - * Query the transforms that should be applied to the layer corresponding - * to this APZC due to asynchronous panning and zooming. - * This function returns the async transform via the |aOutTransform| - * out parameter. - */ - void SampleContentTransformForFrame(AsyncTransform* aOutTransform, - ParentLayerPoint& aScrollOffset); - - /** - * Return a visual effect that reflects this apzc's - * overscrolled state, if any. - */ - AsyncTransformComponentMatrix GetOverscrollTransform() const; - /** * A shadow layer update has arrived. |aScrollMetdata| is the new ScrollMetadata * for the container layer corresponding to this APZC. @@ -222,20 +207,6 @@ public: */ bool IsDestroyed() const; - /** - * Returns the incremental transformation corresponding to the async pan/zoom - * in progress. That is, when this transform is multiplied with the layer's - * existing transform, it will make the layer appear with the desired pan/zoom - * amount. - */ - AsyncTransform GetCurrentAsyncTransform() const; - - /** - * Returns the same transform as GetCurrentAsyncTransform(), but includes - * any transform due to axis over-scroll. - */ - AsyncTransformComponentMatrix GetCurrentAsyncTransformWithOverscroll() const; - /** * Returns the transform to take something from the coordinate space of the * last thing we know gecko painted, to the coordinate space of the last thing @@ -737,6 +708,53 @@ private: friend class Axis; + /* =================================================================== + * The functions and members in this section are used to expose + * the current async transform state to callers. + */ +public: + /** + * Allows callers to specify which type of async transform they want: + * NORMAL provides the actual async transforms of the APZC, whereas + * RESPECT_FORCE_DISABLE will provide empty async transforms if and only if + * the metrics has the mForceDisableApz flag set. In general the latter should + * only be used by call sites that are applying the transform to update + * a layer's position. + */ + enum AsyncMode { + NORMAL, + RESPECT_FORCE_DISABLE, + }; + + /** + * Query the transforms that should be applied to the layer corresponding + * to this APZC due to asynchronous panning and zooming. + * This function returns the async transform via the |aOutTransform| + * out parameter. + */ + ParentLayerPoint GetCurrentAsyncScrollOffset(AsyncMode aMode) const; + + /** + * Return a visual effect that reflects this apzc's + * overscrolled state, if any. + */ + AsyncTransformComponentMatrix GetOverscrollTransform(AsyncMode aMode) const; + + /** + * Returns the incremental transformation corresponding to the async pan/zoom + * in progress. That is, when this transform is multiplied with the layer's + * existing transform, it will make the layer appear with the desired pan/zoom + * amount. + */ + AsyncTransform GetCurrentAsyncTransform(AsyncMode aMode) const; + + /** + * Returns the same transform as GetCurrentAsyncTransform(), but includes + * any transform due to axis over-scroll. + */ + AsyncTransformComponentMatrix GetCurrentAsyncTransformWithOverscroll(AsyncMode aMode) const; + + /* =================================================================== * The functions and members in this section are used to manage @@ -1106,9 +1124,9 @@ public: } private: - // Extra offset to add in SampleContentTransformForFrame for testing + // Extra offset to add to the async scroll position for testing CSSPoint mTestAsyncScrollOffset; - // Extra zoom to include in SampleContentTransformForFrame for testing + // Extra zoom to include in the aync zoom for testing LayerToParentLayerScale mTestAsyncZoom; // Flag to track whether or not the APZ transform is not used. This // flag is recomputed for every composition frame. diff --git a/gfx/layers/apz/src/HitTestingTreeNode.cpp b/gfx/layers/apz/src/HitTestingTreeNode.cpp index ce392a98c3fc..fc8b599dfc2b 100644 --- a/gfx/layers/apz/src/HitTestingTreeNode.cpp +++ b/gfx/layers/apz/src/HitTestingTreeNode.cpp @@ -241,7 +241,7 @@ HitTestingTreeNode::Untransform(const ParentLayerPoint& aPoint) const LayerToParentLayerMatrix4x4 transform = mTransform * CompleteAsyncTransform( mApzc - ? mApzc->GetCurrentAsyncTransformWithOverscroll() + ? mApzc->GetCurrentAsyncTransformWithOverscroll(AsyncPanZoomController::NORMAL) : AsyncTransformComponentMatrix()); return UntransformBy(transform.Inverse(), aPoint); } diff --git a/gfx/layers/apz/test/gtest/APZCBasicTester.h b/gfx/layers/apz/test/gtest/APZCBasicTester.h index 9debd9106aed..57b28da24517 100644 --- a/gfx/layers/apz/test/gtest/APZCBasicTester.h +++ b/gfx/layers/apz/test/gtest/APZCBasicTester.h @@ -96,7 +96,7 @@ protected: // Trigger computation of the overscroll tranform, to make sure // no assetions fire during the calculation. - apzc->GetOverscrollTransform(); + apzc->GetOverscrollTransform(AsyncPanZoomController::NORMAL); if (!apzc->IsOverscrolled()) { recoveredFromOverscroll = true; diff --git a/gfx/layers/apz/test/gtest/APZTestCommon.h b/gfx/layers/apz/test/gtest/APZTestCommon.h index 07932e4081c6..e8ae260ab05d 100644 --- a/gfx/layers/apz/test/gtest/APZTestCommon.h +++ b/gfx/layers/apz/test/gtest/APZTestCommon.h @@ -252,8 +252,10 @@ public: const TimeDuration& aIncrement = TimeDuration::FromMilliseconds(0)) { mcc->AdvanceBy(aIncrement); bool ret = AdvanceAnimations(mcc->Time()); - AsyncPanZoomController::SampleContentTransformForFrame( - aOutTransform, aScrollOffset); + if (aOutTransform) { + *aOutTransform = GetCurrentAsyncTransform(AsyncPanZoomController::NORMAL); + } + aScrollOffset = GetCurrentAsyncScrollOffset(AsyncPanZoomController::NORMAL); return ret; } diff --git a/gfx/layers/apz/test/gtest/TestHitTesting.cpp b/gfx/layers/apz/test/gtest/TestHitTesting.cpp index de2b17deb5f0..aa46a59b51b5 100644 --- a/gfx/layers/apz/test/gtest/TestHitTesting.cpp +++ b/gfx/layers/apz/test/gtest/TestHitTesting.cpp @@ -58,6 +58,12 @@ protected: SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID + 2, CSSRect(0, 0, 80, 80)); } + void DisableApzOn(Layer* aLayer) { + ScrollMetadata m = aLayer->GetScrollMetadata(0); + m.GetMetrics().SetForceDisableApz(true); + aLayer->SetScrollMetadata(m); + } + void CreateComplexMultiLayerTree() { const char* layerTreeSyntax = "c(tc(t)tc(c(t)tt))"; // LayerID 0 12 3 45 6 7 89 @@ -453,6 +459,48 @@ TEST_F(APZHitTestingTester, TestRepaintFlushOnWheelEvents) { } } +TEST_F(APZHitTestingTester, TestForceDisableApz) { + CreateSimpleScrollingLayer(); + DisableApzOn(root); + ScopedLayerTreeRegistration registration(manager, 0, root, mcc); + manager->UpdateHitTestingTree(nullptr, root, false, 0, 0); + TestAsyncPanZoomController* apzcroot = ApzcOf(root); + + ScreenPoint origin(100, 50); + ScrollWheelInput swi(MillisecondsSinceStartup(mcc->Time()), mcc->Time(), 0, + ScrollWheelInput::SCROLLMODE_INSTANT, ScrollWheelInput::SCROLLDELTA_PIXEL, + origin, 0, 10, false); + EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(swi, nullptr, nullptr)); + EXPECT_EQ(origin, swi.mOrigin); + + AsyncTransform viewTransform; + ParentLayerPoint point; + apzcroot->SampleContentTransformForFrame(&viewTransform, point); + // Since APZ is force-disabled, we expect to see the async transform via + // the NORMAL AsyncMode, but not via the RESPECT_FORCE_DISABLE AsyncMode. + EXPECT_EQ(0, point.x); + EXPECT_EQ(10, point.y); + EXPECT_EQ(0, viewTransform.mTranslation.x); + EXPECT_EQ(-10, viewTransform.mTranslation.y); + viewTransform = apzcroot->GetCurrentAsyncTransform(AsyncPanZoomController::RESPECT_FORCE_DISABLE); + point = apzcroot->GetCurrentAsyncScrollOffset(AsyncPanZoomController::RESPECT_FORCE_DISABLE); + EXPECT_EQ(0, point.x); + EXPECT_EQ(0, point.y); + EXPECT_EQ(0, viewTransform.mTranslation.x); + EXPECT_EQ(0, viewTransform.mTranslation.y); + + mcc->AdvanceByMillis(10); + + // With untransforming events we should get normal behaviour (in this case, + // no noticeable untransform, because the repaint request already got + // flushed). + swi = ScrollWheelInput(MillisecondsSinceStartup(mcc->Time()), mcc->Time(), 0, + ScrollWheelInput::SCROLLMODE_INSTANT, ScrollWheelInput::SCROLLDELTA_PIXEL, + origin, 0, 0, false); + EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(swi, nullptr, nullptr)); + EXPECT_EQ(origin, swi.mOrigin); +} + TEST_F(APZHitTestingTester, Bug1148350) { CreateBug1148350LayerTree(); ScopedLayerTreeRegistration registration(manager, 0, root, mcc); diff --git a/gfx/layers/apz/util/APZCCallbackHelper.cpp b/gfx/layers/apz/util/APZCCallbackHelper.cpp index 87b82b31b065..d26e0d2b4846 100644 --- a/gfx/layers/apz/util/APZCCallbackHelper.cpp +++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp @@ -501,9 +501,9 @@ APZCCallbackHelper::ApplyCallbackTransform(WidgetEvent& aEvent, { if (aEvent.AsTouchEvent()) { WidgetTouchEvent& event = *(aEvent.AsTouchEvent()); - for (size_t i = 0; i < event.touches.Length(); i++) { - event.touches[i]->mRefPoint = ApplyCallbackTransform( - event.touches[i]->mRefPoint, aGuid, aScale); + for (size_t i = 0; i < event.mTouches.Length(); i++) { + event.mTouches[i]->mRefPoint = ApplyCallbackTransform( + event.mTouches[i]->mRefPoint, aGuid, aScale); } } else { aEvent.refPoint = ApplyCallbackTransform( @@ -806,9 +806,9 @@ APZCCallbackHelper::SendSetTargetAPZCNotification(nsIWidget* aWidget, nsTArray targets; if (const WidgetTouchEvent* touchEvent = aEvent.AsTouchEvent()) { - for (size_t i = 0; i < touchEvent->touches.Length(); i++) { + for (size_t i = 0; i < touchEvent->mTouches.Length(); i++) { waitForRefresh |= PrepareForSetTargetAPZCNotification(aWidget, aGuid, - rootFrame, touchEvent->touches[i]->mRefPoint, &targets); + rootFrame, touchEvent->mTouches[i]->mRefPoint, &targets); } } else if (const WidgetWheelEvent* wheelEvent = aEvent.AsWheelEvent()) { waitForRefresh = PrepareForSetTargetAPZCNotification(aWidget, aGuid, @@ -836,8 +836,10 @@ APZCCallbackHelper::SendSetAllowedTouchBehaviorNotification( const SetAllowedTouchBehaviorCallback& aCallback) { nsTArray flags; - for (uint32_t i = 0; i < aEvent.touches.Length(); i++) { - flags.AppendElement(widget::ContentHelper::GetAllowedTouchBehavior(aWidget, aEvent.touches[i]->mRefPoint)); + for (uint32_t i = 0; i < aEvent.mTouches.Length(); i++) { + flags.AppendElement( + widget::ContentHelper::GetAllowedTouchBehavior( + aWidget, aEvent.mTouches[i]->mRefPoint)); } aCallback(aInputBlockId, flags); } diff --git a/gfx/layers/apz/util/APZEventState.cpp b/gfx/layers/apz/util/APZEventState.cpp index b9b82562608d..a7d7ceb3c9e7 100644 --- a/gfx/layers/apz/util/APZEventState.cpp +++ b/gfx/layers/apz/util/APZEventState.cpp @@ -260,8 +260,8 @@ APZEventState::ProcessTouchEvent(const WidgetTouchEvent& aEvent, nsEventStatus aApzResponse, nsEventStatus aContentResponse) { - if (aEvent.mMessage == eTouchStart && aEvent.touches.Length() > 0) { - mActiveElementManager->SetTargetElement(aEvent.touches[0]->GetTarget()); + if (aEvent.mMessage == eTouchStart && aEvent.mTouches.Length() > 0) { + mActiveElementManager->SetTargetElement(aEvent.mTouches[0]->GetTarget()); } bool isTouchPrevented = aContentResponse == nsEventStatus_eConsumeNoDefault; @@ -322,8 +322,8 @@ APZEventState::ProcessTouchEvent(const WidgetTouchEvent& aEvent, WidgetTouchEvent cancelEvent(aEvent); cancelEvent.mMessage = eTouchCancel; cancelEvent.mFlags.mCancelable = false; // mMessage != eTouchCancel; - for (uint32_t i = 0; i < cancelEvent.touches.Length(); ++i) { - if (mozilla::dom::Touch* touch = cancelEvent.touches[i]) { + for (uint32_t i = 0; i < cancelEvent.mTouches.Length(); ++i) { + if (mozilla::dom::Touch* touch = cancelEvent.mTouches[i]) { touch->convertToPointer = true; } } diff --git a/gfx/layers/composite/AsyncCompositionManager.cpp b/gfx/layers/composite/AsyncCompositionManager.cpp index 7bb9b52c29c7..0c5c729036e0 100644 --- a/gfx/layers/composite/AsyncCompositionManager.cpp +++ b/gfx/layers/composite/AsyncCompositionManager.cpp @@ -845,11 +845,10 @@ AsyncCompositionManager::ApplyAsyncContentTransformToTree(Layer *aLayer, hasAsyncTransform = true; - AsyncTransform asyncTransformWithoutOverscroll; - ParentLayerPoint scrollOffset; - controller->SampleContentTransformForFrame(&asyncTransformWithoutOverscroll, - scrollOffset); - AsyncTransformComponentMatrix overscrollTransform = controller->GetOverscrollTransform(); + AsyncTransform asyncTransformWithoutOverscroll = + controller->GetCurrentAsyncTransform(AsyncPanZoomController::RESPECT_FORCE_DISABLE); + AsyncTransformComponentMatrix overscrollTransform = + controller->GetOverscrollTransform(AsyncPanZoomController::RESPECT_FORCE_DISABLE); AsyncTransformComponentMatrix asyncTransform = AsyncTransformComponentMatrix(asyncTransformWithoutOverscroll) * overscrollTransform; @@ -882,6 +881,8 @@ AsyncCompositionManager::ApplyAsyncContentTransformToTree(Layer *aLayer, geckoZoom, mContentRect); } else { + ParentLayerPoint scrollOffset = controller->GetCurrentAsyncScrollOffset( + AsyncPanZoomController::RESPECT_FORCE_DISABLE); // Compute the painted displayport in document-relative CSS pixels. CSSRect displayPort(metrics.GetCriticalDisplayPort().IsEmpty() ? metrics.GetDisplayPort() : @@ -1032,7 +1033,8 @@ ApplyAsyncTransformToScrollbarForContent(Layer* aScrollbar, const FrameMetrics& metrics = aContent.Metrics(); AsyncPanZoomController* apzc = aContent.GetApzc(); - AsyncTransformComponentMatrix asyncTransform = apzc->GetCurrentAsyncTransform(); + AsyncTransformComponentMatrix asyncTransform = + apzc->GetCurrentAsyncTransform(AsyncPanZoomController::RESPECT_FORCE_DISABLE); // |asyncTransform| represents the amount by which we have scrolled and // zoomed since the last paint. Because the scrollbar was sized and positioned based @@ -1147,7 +1149,9 @@ ApplyAsyncTransformToScrollbarForContent(Layer* aScrollbar, // the same coordinate space. This requires applying the content transform // and then unapplying it after unapplying the async transform. if (aScrollbarIsDescendant) { - Matrix4x4 asyncUntransform = (asyncTransform * apzc->GetOverscrollTransform()).Inverse().ToUnknownMatrix(); + AsyncTransformComponentMatrix overscroll = + apzc->GetOverscrollTransform(AsyncPanZoomController::RESPECT_FORCE_DISABLE); + Matrix4x4 asyncUntransform = (asyncTransform * overscroll).Inverse().ToUnknownMatrix(); Matrix4x4 contentTransform = aContent.GetTransform(); Matrix4x4 contentUntransform = contentTransform.Inverse(); diff --git a/gfx/layers/composite/ContainerLayerComposite.cpp b/gfx/layers/composite/ContainerLayerComposite.cpp index 82b3d66f356c..2aa0afe6e2a5 100755 --- a/gfx/layers/composite/ContainerLayerComposite.cpp +++ b/gfx/layers/composite/ContainerLayerComposite.cpp @@ -488,10 +488,7 @@ RenderMinimap(ContainerT* aContainer, LayerManagerComposite* aManager, return; } - AsyncTransform asyncTransformWithoutOverscroll; - ParentLayerPoint scrollOffset; - controller->SampleContentTransformForFrame(&asyncTransformWithoutOverscroll, - scrollOffset); + ParentLayerPoint scrollOffset = controller->GetCurrentAsyncScrollOffset(AsyncPanZoomController::RESPECT_FORCE_DISABLE); // Options const int verticalPadding = 10; @@ -665,8 +662,9 @@ RenderLayers(ContainerT* aContainer, gfx::Rect(aClipRect.ToUnknownRect()), asyncTransform * aContainer->GetEffectiveTransform()); if (AsyncPanZoomController* apzc = layer->GetAsyncPanZoomController(i - 1)) { - asyncTransform = apzc->GetCurrentAsyncTransformWithOverscroll().ToUnknownMatrix() - * asyncTransform; + asyncTransform = + apzc->GetCurrentAsyncTransformWithOverscroll(AsyncPanZoomController::RESPECT_FORCE_DISABLE).ToUnknownMatrix() + * asyncTransform; } } } @@ -815,7 +813,7 @@ ContainerRender(ContainerT* aContainer, for (LayerMetricsWrapper i(aContainer); i; i = i.GetFirstChild()) { if (AsyncPanZoomController* apzc = i.GetApzc()) { if (!apzc->GetAsyncTransformAppliedToContent() - && !AsyncTransformComponentMatrix(apzc->GetCurrentAsyncTransform()).IsIdentity()) { + && !AsyncTransformComponentMatrix(apzc->GetCurrentAsyncTransform(AsyncPanZoomController::NORMAL)).IsIdentity()) { aManager->UnusedApzTransformWarning(); break; } diff --git a/gfx/thebes/gfxPrefs.h b/gfx/thebes/gfxPrefs.h index ab42c38e957e..853b18912fc7 100644 --- a/gfx/thebes/gfxPrefs.h +++ b/gfx/thebes/gfxPrefs.h @@ -148,6 +148,7 @@ private: DECL_GFX_PREF(Live, "apz.content_response_timeout", APZContentResponseTimeout, int32_t, 300); DECL_GFX_PREF(Live, "apz.danger_zone_x", APZDangerZoneX, int32_t, 50); DECL_GFX_PREF(Live, "apz.danger_zone_y", APZDangerZoneY, int32_t, 100); + DECL_GFX_PREF(Live, "apz.disable_for_scroll_linked_effects", APZDisableForScrollLinkedEffects, bool, false); DECL_GFX_PREF(Live, "apz.displayport_expiry_ms", APZDisplayPortExpiryTime, uint32_t, 15000); DECL_GFX_PREF(Live, "apz.drag.enabled", APZDragEnabled, bool, false); DECL_GFX_PREF(Live, "apz.enlarge_displayport_when_clipped", APZEnlargeDisplayPortWhenClipped, bool, false); diff --git a/image/imgLoader.cpp b/image/imgLoader.cpp index ac4a8f8589a1..e1f8589e202e 100644 --- a/image/imgLoader.cpp +++ b/image/imgLoader.cpp @@ -8,6 +8,7 @@ #include "mozilla/ClearOnShutdown.h" #include "mozilla/Move.h" #include "mozilla/Preferences.h" + #include "mozilla/ChaosMode.h" #include "ImageLogging.h" #include "nsPrintfCString.h" @@ -1772,6 +1773,12 @@ imgLoader::ValidateEntry(imgCacheEntry* aEntry, return false; } + if (MOZ_UNLIKELY(ChaosMode::isActive(ChaosFeature::ImageCache))) { + if (ChaosMode::randomUint32LessThan(4) < 1) { + return false; + } + } + // Determine whether the cache aEntry must be revalidated... validateRequest = ShouldRevalidateEntry(aEntry, aLoadFlags, hasExpired); diff --git a/js/public/Class.h b/js/public/Class.h index fe8687b29ced..abf27e5369df 100644 --- a/js/public/Class.h +++ b/js/public/Class.h @@ -624,7 +624,7 @@ inline ClassObjectCreationOp DELEGATED_CLASSSPEC(const ClassSpec* spec) { return reinterpret_cast(const_cast(spec)); } -#define JS_NULL_CLASS_SPEC {nullptr,nullptr,nullptr,nullptr,nullptr,nullptr} +#define JS_NULL_CLASS_SPEC nullptr #define JS_NULL_CLASS_EXT {false,nullptr} struct ObjectOps @@ -654,7 +654,7 @@ typedef void (*JSClassInternal)(); struct JSClass { JS_CLASS_MEMBERS(JSFinalizeOp); - void* reserved[12]; + void* reserved[5]; }; #define JSCLASS_HAS_PRIVATE (1<<0) // objects have private slot @@ -756,7 +756,7 @@ namespace js { struct Class { JS_CLASS_MEMBERS(FinalizeOp); - ClassSpec spec; + const ClassSpec* spec; ClassExtension ext; const ObjectOps* ops; @@ -804,6 +804,26 @@ struct Class static size_t offsetOfFlags() { return offsetof(Class, flags); } + bool specDefined() const { return spec ? spec->defined() : false; } + bool specDependent() const { return spec ? spec->dependent() : false; } + JSProtoKey specParentKey() const { return spec ? spec->parentKey() : JSProto_Null; } + bool specShouldDefineConstructor() + const { return spec ? spec->shouldDefineConstructor() : true; } + ClassObjectCreationOp specCreateConstructorHook() + const { return spec ? spec->createConstructorHook() : nullptr; } + ClassObjectCreationOp specCreatePrototypeHook() + const { return spec ? spec->createPrototypeHook() : nullptr; } + const JSFunctionSpec* specConstructorFunctions() + const { return spec ? spec->constructorFunctions() : nullptr; } + const JSPropertySpec* specConstructorProperties() + const { return spec ? spec->constructorProperties() : nullptr; } + const JSFunctionSpec* specPrototypeFunctions() + const { return spec ? spec->prototypeFunctions() : nullptr; } + const JSPropertySpec* specPrototypeProperties() + const { return spec ? spec->prototypeProperties() : nullptr; } + FinishClassInitOp specFinishInitHook() + const { return spec ? spec->finishInitHook() : nullptr; } + LookupPropertyOp getOpsLookupProperty() const { return ops ? ops->lookupProperty : nullptr; } DefinePropertyOp getOpsDefineProperty() const { return ops ? ops->defineProperty : nullptr; } HasPropertyOp getOpsHasProperty() const { return ops ? ops->hasProperty : nullptr; } diff --git a/js/src/asmjs/AsmJS.cpp b/js/src/asmjs/AsmJS.cpp index 6f8a178c9245..c3e900d7ebfb 100644 --- a/js/src/asmjs/AsmJS.cpp +++ b/js/src/asmjs/AsmJS.cpp @@ -96,7 +96,6 @@ enum AsmJSAtomicsBuiltinFunction AsmJSAtomicsBuiltin_exchange, AsmJSAtomicsBuiltin_load, AsmJSAtomicsBuiltin_store, - AsmJSAtomicsBuiltin_fence, AsmJSAtomicsBuiltin_add, AsmJSAtomicsBuiltin_sub, AsmJSAtomicsBuiltin_and, @@ -1735,7 +1734,6 @@ class MOZ_STACK_CLASS ModuleValidator !addStandardLibraryAtomicsName("exchange", AsmJSAtomicsBuiltin_exchange) || !addStandardLibraryAtomicsName("load", AsmJSAtomicsBuiltin_load) || !addStandardLibraryAtomicsName("store", AsmJSAtomicsBuiltin_store) || - !addStandardLibraryAtomicsName("fence", AsmJSAtomicsBuiltin_fence) || !addStandardLibraryAtomicsName("add", AsmJSAtomicsBuiltin_add) || !addStandardLibraryAtomicsName("sub", AsmJSAtomicsBuiltin_sub) || !addStandardLibraryAtomicsName("and", AsmJSAtomicsBuiltin_and) || @@ -4106,16 +4104,6 @@ CheckSharedArrayAtomicAccess(FunctionValidator& f, ParseNode* viewName, ParseNod return true; } -static bool -CheckAtomicsFence(FunctionValidator& f, ParseNode* call, Type* type) -{ - if (CallArgListLength(call) != 0) - return f.fail(call, "Atomics.fence must be passed 0 arguments"); - - *type = Type::Void; - return f.encoder().writeExpr(Expr::AtomicsFence); -} - static bool WriteAtomicOperator(FunctionValidator& f, Expr opcode, size_t* viewTypeAt) { @@ -4309,8 +4297,6 @@ CheckAtomicsBuiltinCall(FunctionValidator& f, ParseNode* callNode, AsmJSAtomicsB return CheckAtomicsLoad(f, callNode, type); case AsmJSAtomicsBuiltin_store: return CheckAtomicsStore(f, callNode, type); - case AsmJSAtomicsBuiltin_fence: - return CheckAtomicsFence(f, callNode, type); case AsmJSAtomicsBuiltin_add: return CheckAtomicsBinop(f, callNode, type, AtomicFetchAddOp); case AsmJSAtomicsBuiltin_sub: @@ -7371,7 +7357,6 @@ ValidateAtomicsBuiltinFunction(JSContext* cx, const AsmJSGlobal& global, HandleV case AsmJSAtomicsBuiltin_exchange: native = atomics_exchange; break; case AsmJSAtomicsBuiltin_load: native = atomics_load; break; case AsmJSAtomicsBuiltin_store: native = atomics_store; break; - case AsmJSAtomicsBuiltin_fence: native = atomics_fence; break; case AsmJSAtomicsBuiltin_add: native = atomics_add; break; case AsmJSAtomicsBuiltin_sub: native = atomics_sub; break; case AsmJSAtomicsBuiltin_and: native = atomics_and; break; diff --git a/js/src/asmjs/WasmBinary.h b/js/src/asmjs/WasmBinary.h index 159b2818157e..b5a5416fdb6f 100644 --- a/js/src/asmjs/WasmBinary.h +++ b/js/src/asmjs/WasmBinary.h @@ -275,7 +275,6 @@ enum class Expr F64Atan2, // Atomics - AtomicsFence, I32AtomicsCompareExchange, I32AtomicsExchange, I32AtomicsLoad, diff --git a/js/src/asmjs/WasmIonCompile.cpp b/js/src/asmjs/WasmIonCompile.cpp index d54c8313a24f..0854892483eb 100644 --- a/js/src/asmjs/WasmIonCompile.cpp +++ b/js/src/asmjs/WasmIonCompile.cpp @@ -577,14 +577,6 @@ class FunctionCompiler curBlock_->add(store); } - void memoryBarrier(MemoryBarrierBits type) - { - if (inDeadCode()) - return; - MMemoryBarrier* ins = MMemoryBarrier::New(alloc(), type); - curBlock_->add(ins); - } - MDefinition* atomicLoadHeap(MDefinition* base, const MAsmJSHeapAccess& access) { if (inDeadCode()) @@ -3073,10 +3065,6 @@ EmitExpr(FunctionCompiler& f, MDefinition** def) SimdSign::Unsigned, def); // Atomics - case Expr::AtomicsFence: - *def = nullptr; - f.memoryBarrier(MembarFull); - return true; case Expr::I32AtomicsCompareExchange: return EmitAtomicsCompareExchange(f, def); case Expr::I32AtomicsExchange: diff --git a/js/src/builtin/AtomicsObject.cpp b/js/src/builtin/AtomicsObject.cpp index 038a886ec0bc..739bef672021 100644 --- a/js/src/builtin/AtomicsObject.cpp +++ b/js/src/builtin/AtomicsObject.cpp @@ -52,6 +52,7 @@ #include "jsapi.h" #include "jsfriendapi.h" +#include "jsnum.h" #include "asmjs/WasmModule.h" #include "jit/AtomicOperations.h" @@ -80,7 +81,10 @@ ReportBadArrayType(JSContext* cx) static bool ReportOutOfRange(JSContext* cx) { - JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_ATOMICS_BAD_INDEX); + // Use JSMSG_BAD_INDEX here even if it is generic, since that is + // the message used by ToIntegerIndex for its initial range + // checking. + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX); return false; } @@ -108,22 +112,12 @@ GetSharedTypedArray(JSContext* cx, HandleValue v, static bool GetTypedArrayIndex(JSContext* cx, HandleValue v, Handle view, uint32_t* offset) { - RootedId id(cx); - if (!ValueToId(cx, v, &id)) - return false; uint64_t index; - if (!IsTypedArrayIndex(id, &index) || index >= view->length()) + if (!js::ToIntegerIndex(cx, v, &index)) + return false; + if (index >= view->length()) return ReportOutOfRange(cx); - *offset = (uint32_t)index; - return true; -} - -bool -js::atomics_fence(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - jit::AtomicOperations::fenceSeqCst(); - args.rval().setUndefined(); + *offset = uint32_t(index); return true; } @@ -754,7 +748,7 @@ class AutoUnlockFutexAPI } // namespace js bool -js::atomics_futexWait(JSContext* cx, unsigned argc, Value* vp) +js::atomics_wait(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); HandleValue objv = args.get(0); @@ -797,7 +791,7 @@ js::atomics_futexWait(JSContext* cx, unsigned argc, Value* vp) SharedMem(addr) = view->viewDataShared().cast() + offset; if (jit::AtomicOperations::loadSafeWhenRacy(addr) != value) { - r.setInt32(AtomicsObject::FutexNotequal); + r.setString(cx->names().futexNotEqual); return true; } @@ -815,10 +809,18 @@ js::atomics_futexWait(JSContext* cx, unsigned argc, Value* vp) sarb->setWaiters(&w); } - AtomicsObject::FutexWaitResult result = AtomicsObject::FutexOK; + FutexRuntime::WaitResult result = FutexRuntime::FutexOK; bool retval = rt->fx.wait(cx, timeout_ms, &result); - if (retval) - r.setInt32(result); + if (retval) { + switch (result) { + case FutexRuntime::FutexOK: + r.setString(cx->names().futexOK); + break; + case FutexRuntime::FutexTimedOut: + r.setString(cx->names().futexTimedOut); + break; + } + } if (w.lower_pri == &w) { sarb->setWaiters(nullptr); @@ -832,7 +834,7 @@ js::atomics_futexWait(JSContext* cx, unsigned argc, Value* vp) } bool -js::atomics_futexWake(JSContext* cx, unsigned argc, Value* vp) +js::atomics_wake(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); HandleValue objv = args.get(0); @@ -849,10 +851,14 @@ js::atomics_futexWake(JSContext* cx, unsigned argc, Value* vp) if (!GetTypedArrayIndex(cx, idxv, view, &offset)) return false; double count; - if (!ToInteger(cx, countv, &count)) - return false; - if (count < 0) - count = 0; + if (countv.isUndefined()) { + count = mozilla::PositiveInfinity(); + } else { + if (!ToInteger(cx, countv, &count)) + return false; + if (count < 0.0) + count = 0.0; + } AutoLockFutexAPI lock; @@ -878,120 +884,6 @@ js::atomics_futexWake(JSContext* cx, unsigned argc, Value* vp) return true; } -bool -js::atomics_futexWakeOrRequeue(JSContext* cx, unsigned argc, Value* vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - HandleValue objv = args.get(0); - HandleValue idx1v = args.get(1); - HandleValue countv = args.get(2); - HandleValue idx2v = args.get(3); - HandleValue valv = args.get(4); - MutableHandleValue r = args.rval(); - - Rooted view(cx, nullptr); - if (!GetSharedTypedArray(cx, objv, &view)) - return false; - if (view->type() != Scalar::Int32) - return ReportBadArrayType(cx); - uint32_t offset1; - if (!GetTypedArrayIndex(cx, idx1v, view, &offset1)) - return false; - double count; - if (!ToInteger(cx, countv, &count)) - return false; - if (count < 0) - count = 0; - int32_t value; - if (!ToInt32(cx, valv, &value)) - return false; - uint32_t offset2; - if (!GetTypedArrayIndex(cx, idx2v, view, &offset2)) - return false; - - AutoLockFutexAPI lock; - - SharedMem addr = view->viewDataShared().cast() + offset1; - if (jit::AtomicOperations::loadSafeWhenRacy(addr) != value) { - r.setInt32(AtomicsObject::FutexNotequal); - return true; - } - - Rooted sab(cx, view->bufferShared()); - SharedArrayRawBuffer* sarb = sab->rawBufferObject(); - - // Walk the list of waiters looking for those waiting on offset1. - // Wake some and requeue the others. There may already be other - // waiters on offset2, so those that are requeued must be moved to - // the back of the list. Offset1 may equal offset2. The list's - // first node may change, and the list may be emptied out by the - // operation. - - FutexWaiter* waiters = sarb->waiters(); - if (!waiters) { - r.setInt32(0); - return true; - } - - int32_t woken = 0; - FutexWaiter whead((uint32_t)-1, nullptr); // Header node for waiters - FutexWaiter* first = waiters; - FutexWaiter* last = waiters->back; - whead.lower_pri = first; - whead.back = last; - first->back = &whead; - last->lower_pri = &whead; - - FutexWaiter rhead((uint32_t)-1, nullptr); // Header node for requeued - rhead.lower_pri = rhead.back = &rhead; - - FutexWaiter* iter = whead.lower_pri; - while (iter != &whead) { - FutexWaiter* c = iter; - iter = iter->lower_pri; - if (c->offset != offset1 || !c->rt->fx.isWaiting()) - continue; - if (count > 0) { - c->rt->fx.wake(FutexRuntime::WakeExplicit); - ++woken; - --count; - } else { - c->offset = offset2; - - // Remove the node from the waiters list. - c->back->lower_pri = c->lower_pri; - c->lower_pri->back = c->back; - - // Insert the node at the back of the requeuers list. - c->lower_pri = &rhead; - c->back = rhead.back; - rhead.back->lower_pri = c; - rhead.back = c; - } - } - - // If there are any requeuers, append them to the waiters. - if (rhead.lower_pri != &rhead) { - whead.back->lower_pri = rhead.lower_pri; - rhead.lower_pri->back = whead.back; - - whead.back = rhead.back; - rhead.back->lower_pri = &whead; - } - - // Make the final list and install it. - waiters = nullptr; - if (whead.lower_pri != &whead) { - whead.back->lower_pri = whead.lower_pri; - whead.lower_pri->back = whead.back; - waiters = whead.lower_pri; - } - sarb->setWaiters(waiters); - - r.setInt32(woken); - return true; -} - /* static */ bool js::FutexRuntime::initialize() { @@ -1070,7 +962,7 @@ js::FutexRuntime::isWaiting() } bool -js::FutexRuntime::wait(JSContext* cx, double timeout_ms, AtomicsObject::FutexWaitResult* result) +js::FutexRuntime::wait(JSContext* cx, double timeout_ms, WaitResult* result) { MOZ_ASSERT(&cx->runtime()->fx == this); MOZ_ASSERT(cx->runtime()->fx.canWait()); @@ -1127,14 +1019,14 @@ js::FutexRuntime::wait(JSContext* cx, double timeout_ms, AtomicsObject::FutexWai if (timed) { uint64_t now = PRMJ_Now(); if (now >= finalEnd) { - *result = AtomicsObject::FutexTimedout; + *result = FutexTimedOut; goto finished; } } break; case FutexRuntime::Woken: - *result = AtomicsObject::FutexOK; + *result = FutexOK; goto finished; case FutexRuntime::WaitingNotifiedForInterrupt: @@ -1146,10 +1038,10 @@ js::FutexRuntime::wait(JSContext* cx, double timeout_ms, AtomicsObject::FutexWai // should be woken when the interrupt handler returns. // To that end, we flag the thread as interrupted around // the interrupt and check state_ when the interrupt - // handler returns. A futexWake() call that reaches the + // handler returns. A wake() call that reaches the // runtime during the interrupt sets state_ to Woken. // - // - It is in principle possible for futexWait() to be + // - It is in principle possible for wait() to be // reentered on the same thread/runtime and waiting on the // same location and to yet again be interrupted and enter // the interrupt handler. In this case, it is important @@ -1175,7 +1067,7 @@ js::FutexRuntime::wait(JSContext* cx, double timeout_ms, AtomicsObject::FutexWai if (!retval) goto finished; if (state_ == Woken) { - *result = AtomicsObject::FutexOK; + *result = FutexOK; goto finished; } break; @@ -1219,26 +1111,19 @@ const JSFunctionSpec AtomicsMethods[] = { JS_INLINABLE_FN("load", atomics_load, 2,0, AtomicsLoad), JS_INLINABLE_FN("store", atomics_store, 3,0, AtomicsStore), JS_INLINABLE_FN("exchange", atomics_exchange, 3,0, AtomicsExchange), - JS_INLINABLE_FN("fence", atomics_fence, 0,0, AtomicsFence), JS_INLINABLE_FN("add", atomics_add, 3,0, AtomicsAdd), JS_INLINABLE_FN("sub", atomics_sub, 3,0, AtomicsSub), JS_INLINABLE_FN("and", atomics_and, 3,0, AtomicsAnd), JS_INLINABLE_FN("or", atomics_or, 3,0, AtomicsOr), JS_INLINABLE_FN("xor", atomics_xor, 3,0, AtomicsXor), JS_INLINABLE_FN("isLockFree", atomics_isLockFree, 1,0, AtomicsIsLockFree), - JS_FN("futexWait", atomics_futexWait, 4,0), - JS_FN("futexWake", atomics_futexWake, 3,0), - JS_FN("futexWakeOrRequeue", atomics_futexWakeOrRequeue, 5,0), + JS_FN("wait", atomics_wait, 4,0), + JS_FN("futexWait", atomics_wait, 4,0), + JS_FN("wake", atomics_wake, 3,0), + JS_FN("futexWake", atomics_wake, 3,0), JS_FS_END }; -static const JSConstDoubleSpec AtomicsConstants[] = { - {"OK", AtomicsObject::FutexOK}, - {"TIMEDOUT", AtomicsObject::FutexTimedout}, - {"NOTEQUAL", AtomicsObject::FutexNotequal}, - {0, 0} -}; - JSObject* AtomicsObject::initClass(JSContext* cx, Handle global) { @@ -1253,8 +1138,6 @@ AtomicsObject::initClass(JSContext* cx, Handle global) if (!JS_DefineFunctions(cx, Atomics, AtomicsMethods)) return nullptr; - if (!JS_DefineConstDoubles(cx, Atomics, AtomicsConstants)) - return nullptr; RootedValue AtomicsValue(cx, ObjectValue(*Atomics)); diff --git a/js/src/builtin/AtomicsObject.h b/js/src/builtin/AtomicsObject.h index 64745a379c02..82e5272bfcc4 100644 --- a/js/src/builtin/AtomicsObject.h +++ b/js/src/builtin/AtomicsObject.h @@ -18,31 +18,20 @@ class AtomicsObject : public JSObject static const Class class_; static JSObject* initClass(JSContext* cx, Handle global); static bool toString(JSContext* cx, unsigned int argc, Value* vp); - - // Defined return values for futexWait. - // The error values must be negative because APIs such as futexWaitOrRequeue - // return a value that is either the number of tasks woken or an error code. - enum FutexWaitResult : int32_t { - FutexOK = 0, - FutexNotequal = -1, - FutexTimedout = -2 - }; }; bool atomics_compareExchange(JSContext* cx, unsigned argc, Value* vp); bool atomics_exchange(JSContext* cx, unsigned argc, Value* vp); bool atomics_load(JSContext* cx, unsigned argc, Value* vp); bool atomics_store(JSContext* cx, unsigned argc, Value* vp); -bool atomics_fence(JSContext* cx, unsigned argc, Value* vp); bool atomics_add(JSContext* cx, unsigned argc, Value* vp); bool atomics_sub(JSContext* cx, unsigned argc, Value* vp); bool atomics_and(JSContext* cx, unsigned argc, Value* vp); bool atomics_or(JSContext* cx, unsigned argc, Value* vp); bool atomics_xor(JSContext* cx, unsigned argc, Value* vp); bool atomics_isLockFree(JSContext* cx, unsigned argc, Value* vp); -bool atomics_futexWait(JSContext* cx, unsigned argc, Value* vp); -bool atomics_futexWake(JSContext* cx, unsigned argc, Value* vp); -bool atomics_futexWakeOrRequeue(JSContext* cx, unsigned argc, Value* vp); +bool atomics_wait(JSContext* cx, unsigned argc, Value* vp); +bool atomics_wake(JSContext* cx, unsigned argc, Value* vp); /* asm.js callouts */ int32_t atomics_add_asm_callout(int32_t vt, int32_t offset, int32_t value); @@ -72,6 +61,12 @@ public: WakeForJSInterrupt // Interrupt requested }; + // Result code from wait(). + enum WaitResult { + FutexOK, + FutexTimedOut + }; + // Block the calling thread and wait. // // The futex lock must be held around this call. @@ -81,30 +76,30 @@ public: // // wait() will not wake up spuriously. It will return true and // set *result to a return code appropriate for - // Atomics.futexWait() on success, and return false on error. - bool wait(JSContext* cx, double timeout, AtomicsObject::FutexWaitResult* result); + // Atomics.wait() on success, and return false on error. + bool wait(JSContext* cx, double timeout, WaitResult* result); // Wake the thread represented by this Runtime. // // The futex lock must be held around this call. (The sleeping - // thread will not wake up until the caller of futexWake() + // thread will not wake up until the caller of Atomics.wake() // releases the lock.) // // If the thread is not waiting then this method does nothing. // - // If the thread is waiting in a call to futexWait() and the - // reason is WakeExplicit then the futexWait() call will return + // If the thread is waiting in a call to wait() and the + // reason is WakeExplicit then the wait() call will return // with Woken. // - // If the thread is waiting in a call to futexWait() and the - // reason is WakeForJSInterrupt then the futexWait() will return + // If the thread is waiting in a call to wait() and the + // reason is WakeForJSInterrupt then the wait() will return // with WaitingNotifiedForInterrupt; in the latter case the caller - // of futexWait() must handle the interrupt. + // of wait() must handle the interrupt. void wake(WakeReason reason); bool isWaiting(); - // If canWait() returns false (the default) then futexWait is disabled + // If canWait() returns false (the default) then wait() is disabled // on the runtime to which the FutexRuntime belongs. bool canWait() { return canWait_; diff --git a/js/src/builtin/Object.cpp b/js/src/builtin/Object.cpp index ce1e50d7213c..973deb6239ea 100644 --- a/js/src/builtin/Object.cpp +++ b/js/src/builtin/Object.cpp @@ -1211,6 +1211,16 @@ FinishObjectClassInit(JSContext* cx, JS::HandleObject ctor, JS::HandleObject pro return true; } +static const ClassSpec PlainObjectClassSpec = { + CreateObjectConstructor, + CreateObjectPrototype, + object_static_methods, + nullptr, + object_methods, + object_properties, + FinishObjectClassInit +}; + const Class PlainObject::class_ = { js_Object_str, JSCLASS_HAS_CACHED_PROTO(JSProto_Object), @@ -1226,15 +1236,7 @@ const Class PlainObject::class_ = { nullptr, /* hasInstance */ nullptr, /* construct */ nullptr, /* trace */ - { - CreateObjectConstructor, - CreateObjectPrototype, - object_static_methods, - nullptr, - object_methods, - object_properties, - FinishObjectClassInit - } + &PlainObjectClassSpec }; const Class* const js::ObjectClassPtr = &PlainObject::class_; diff --git a/js/src/builtin/Promise.cpp b/js/src/builtin/Promise.cpp index c34d7fbb81ab..fe0c83237bee 100644 --- a/js/src/builtin/Promise.cpp +++ b/js/src/builtin/Promise.cpp @@ -397,6 +397,14 @@ CreatePromisePrototype(JSContext* cx, JSProtoKey key) return cx->global()->createBlankPrototype(cx, &PromiseObject::protoClass_); } +static const ClassSpec PromiseObjectClassSpec = { + GenericCreateConstructor, + CreatePromisePrototype, + promise_static_methods, + promise_static_properties, + promise_methods +}; + const Class PromiseObject::class_ = { "Promise", JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) | JSCLASS_HAS_CACHED_PROTO(JSProto_Promise) | @@ -413,13 +421,18 @@ const Class PromiseObject::class_ = { nullptr, /* hasInstance */ nullptr, /* construct */ nullptr, /* trace */ - { - GenericCreateConstructor, - CreatePromisePrototype, - promise_static_methods, - promise_static_properties, - promise_methods - } + &PromiseObjectClassSpec +}; + +static const ClassSpec PromiseObjectProtoClassSpec = { + DELEGATED_CLASSSPEC(PromiseObject::class_.spec), + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + ClassSpec::IsDelegated }; const Class PromiseObject::protoClass_ = { @@ -437,14 +450,5 @@ const Class PromiseObject::protoClass_ = { nullptr, /* hasInstance */ nullptr, /* construct */ nullptr, /* trace */ - { - DELEGATED_CLASSSPEC(&PromiseObject::class_.spec), - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - ClassSpec::IsDelegated - } + &PromiseObjectProtoClassSpec }; diff --git a/js/src/builtin/SIMD.cpp b/js/src/builtin/SIMD.cpp index 9e1cd7e8e5f1..d833b83a7e89 100644 --- a/js/src/builtin/SIMD.cpp +++ b/js/src/builtin/SIMD.cpp @@ -18,6 +18,7 @@ #include "jsapi.h" #include "jsfriendapi.h" +#include "jsnum.h" #include "jsprf.h" #include "builtin/TypedObject.h" @@ -1254,70 +1255,6 @@ Select(JSContext* cx, unsigned argc, Value* vp) return StoreResult(cx, args, result); } -// Get an integer array index from a function argument. Coerce if necessary. -// -// When a JS function argument represents an integer index into an array, it is -// laundered like this: -// -// 1. numericIndex = ToNumber(argument) (may throw TypeError) -// 2. intIndex = ToInteger(numericIndex) -// 3. if intIndex != numericIndex throw RangeError -// -// This function additionally bounds the range to the non-negative contiguous -// integers: -// -// 4. if intIndex < 0 or intIndex > 2^53 throw RangeError -// -// Return true and set |*index| to the integer value if |argument| is a valid -// array index argument. Otherwise report an TypeError or RangeError and return -// false. -// -// The returned index will always be in the range 0 <= *index <= 2^53. -static bool -ArgumentToIntegerIndex(JSContext* cx, JS::HandleValue v, uint64_t* index) -{ - // Fast common case. - if (v.isInt32()) { - int32_t i = v.toInt32(); - if (i >= 0) { - *index = i; - return true; - } - } - - // Slow case. Use ToNumber() to coerce. This may throw a TypeError. - double d; - if (!ToNumber(cx, v, &d)) - return false; - - // Check that |d| is an integer in the valid range. - // - // Not all floating point integers fit in the range of a uint64_t, so we - // need a rough range check before the real range check in our caller. We - // could limit indexes to UINT64_MAX, but this would mean that our callers - // have to be very careful about integer overflow. The contiguous integer - // floating point numbers end at 2^53, so make that our upper limit. If we - // ever support arrays with more than 2^53 elements, this will need to - // change. - // - // Reject infinities, NaNs, and numbers outside the contiguous integer range - // with a RangeError. - - // Write relation so NaNs throw a RangeError. - if (!(0 <= d && d <= (uint64_t(1) << 53))) - return ErrorBadIndex(cx); - - // Check that d is an integer, throw a RangeError if not. - // Note that this conversion could invoke undefined behaviour without the - // range check above. - uint64_t i(d); - if (d != double(i)) - return ErrorBadIndex(cx); - - *index = i; - return true; -} - // Extract an integer lane index from a function argument. // // Register an exception and return false if the argument is not suitable. @@ -1325,7 +1262,7 @@ static bool ArgumentToLaneIndex(JSContext* cx, JS::HandleValue v, unsigned limit, unsigned* lane) { uint64_t arg; - if (!ArgumentToIntegerIndex(cx, v, &arg)) + if (!ToIntegerIndex(cx, v, &arg)) return false; if (arg >= limit) return ErrorBadIndex(cx); @@ -1352,7 +1289,7 @@ TypedArrayFromArgs(JSContext* cx, const CallArgs& args, uint32_t accessBytes, typedArray.set(&argobj); uint64_t index; - if (!ArgumentToIntegerIndex(cx, args[1], &index)) + if (!ToIntegerIndex(cx, args[1], &index)) return false; // Do the range check in 64 bits even when size_t is 32 bits. diff --git a/js/src/jit-test/tests/asm.js/testAtomics.js b/js/src/jit-test/tests/asm.js/testAtomics.js index db50e0279158..3b09525b83a2 100644 --- a/js/src/jit-test/tests/asm.js/testAtomics.js +++ b/js/src/jit-test/tests/asm.js/testAtomics.js @@ -11,7 +11,6 @@ load(libdir + "asserts.js"); var loadModule_int32_code = USE_ASM + ` - var atomic_fence = stdlib.Atomics.fence; var atomic_load = stdlib.Atomics.load; var atomic_store = stdlib.Atomics.store; var atomic_cmpxchg = stdlib.Atomics.compareExchange; @@ -24,10 +23,6 @@ var loadModule_int32_code = var i32a = new stdlib.Int32Array(heap); - function do_fence() { - atomic_fence(); - } - // Load element 0 function do_load() { var v = 0; @@ -204,8 +199,7 @@ var loadModule_int32_code = return v|0; } - return { fence: do_fence, - load: do_load, + return { load: do_load, load_i: do_load_i, store: do_store, store_i: do_store_i, @@ -238,8 +232,6 @@ function test_int32(heap) { var size = Int32Array.BYTES_PER_ELEMENT; - i32m.fence(); - i32a[0] = 12345; assertEq(i32m.load(), 12345); assertEq(i32m.load_i(size*0), 12345); @@ -328,7 +320,6 @@ function test_int32(heap) { var loadModule_uint32_code = USE_ASM + ` - var atomic_fence = stdlib.Atomics.fence; var atomic_load = stdlib.Atomics.load; var atomic_store = stdlib.Atomics.store; var atomic_cmpxchg = stdlib.Atomics.compareExchange; @@ -609,7 +600,6 @@ function test_uint32(heap) { var loadModule_int16_code = USE_ASM + ` - var atomic_fence = stdlib.Atomics.fence; var atomic_load = stdlib.Atomics.load; var atomic_store = stdlib.Atomics.store; var atomic_cmpxchg = stdlib.Atomics.compareExchange; @@ -622,10 +612,6 @@ var loadModule_int16_code = var i16a = new stdlib.Int16Array(heap); - function do_fence() { - atomic_fence(); - } - // Load element 0 function do_load() { var v = 0; @@ -776,8 +762,7 @@ var loadModule_int16_code = return v|0; } - return { fence: do_fence, - load: do_load, + return { load: do_load, load_i: do_load_i, store: do_store, store_i: do_store_i, @@ -807,8 +792,6 @@ function test_int16(heap) { var size = Int16Array.BYTES_PER_ELEMENT; - i16m.fence(); - i16a[0] = 12345; assertEq(i16m.load(), 12345); assertEq(i16m.load_i(size*0), 12345); diff --git a/js/src/jit-test/tests/atomics/basic-tests.js b/js/src/jit-test/tests/atomics/basic-tests.js index 1a5235fe5d83..710822982eef 100644 --- a/js/src/jit-test/tests/atomics/basic-tests.js +++ b/js/src/jit-test/tests/atomics/basic-tests.js @@ -3,7 +3,7 @@ // These do not test atomicity, just that calling and coercions and // indexing and exception behavior all work right. // -// These do not test the futex operations. +// These do not test the wait/wake operations. load(libdir + "asserts.js"); @@ -66,8 +66,6 @@ function testMethod(a, ...indices) { Atomics.store(a, x, 0); // val = 0 - Atomics.fence(); - // val = 0 assertEq(Atomics.add(a, x, 3), 0); // val = 3 @@ -137,8 +135,6 @@ function testFunction(a, ...indices) { gAtomics_store(a, x, 0); // val = 0 - gAtomics_fence(); - // val = 0 assertEq(gAtomics_add(a, x, 3), 0); // val = 3 @@ -211,7 +207,7 @@ var globlength = 0; // Will be set later function testRangeCAS(a) { dprint("Range: " + a.constructor.name); - var msg = /out-of-range index for atomic access/; + var msg = /out-of-range index/; // A generic message assertErrorMessage(() => Atomics.compareExchange(a, -1, 0, 1), RangeError, msg); assertEq(a[0], 0); @@ -380,7 +376,7 @@ function adHocExchange() { // ie, it must return false for n=8. // // SpiderMonkey has isLockFree(1), isLockFree(2), isLockFree(4) on all -// supported platforms, though this is not guaranteed by the spec. +// supported platforms, only the last is guaranteed by the spec. var sizes = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; @@ -456,6 +452,13 @@ function testUint8Clamped(sab) { assertEq(thrown, true); } +function testWeirdIndices() { + var a = new Int8Array(new SharedArrayBuffer(16)); + a[3] = 10; + assertEq(Atomics.load(a, "0x03"), 10); + assertEq(Atomics.load(a, {valueOf: () => 3}), 10); +} + function isLittleEndian() { var xxx = new ArrayBuffer(2); var xxa = new Int16Array(xxx); @@ -497,7 +500,6 @@ function runTests() { gAtomics_exchange = Atomics.exchange; gAtomics_load = Atomics.load; gAtomics_store = Atomics.store; - gAtomics_fence = Atomics.fence; gAtomics_add = Atomics.add; gAtomics_sub = Atomics.sub; gAtomics_and = Atomics.and; @@ -551,6 +553,7 @@ function runTests() { // Misc testIsLockFree(); testIsLockFree2(); + testWeirdIndices(); } if (this.Atomics && this.SharedArrayBuffer) diff --git a/js/src/jit-test/tests/atomics/inline-fence.js b/js/src/jit-test/tests/atomics/inline-fence.js deleted file mode 100644 index 73628028115e..000000000000 --- a/js/src/jit-test/tests/atomics/inline-fence.js +++ /dev/null @@ -1,31 +0,0 @@ -// |jit-test| slow; -// -// This is intended to be run manually with IONFLAGS=logs and -// postprocessing by iongraph to verify manually (by inspecting the -// MIR) that: -// -// - the fence operation is inlined as it should be -// - loads and stores are not moved across the fence -// -// Be sure to run with --ion-eager --ion-offthread-compile=off. - -function fence(ta) { - var x = ta[0]; - Atomics.fence(); - var y = ta[1]; - var z = y + 1; - var w = x + z; - return w; -} - -if (!this.SharedArrayBuffer || !this.Atomics) - quit(0); - -var sab = new SharedArrayBuffer(4096); -var ia = new Int32Array(sab); -for ( var i=0, limit=ia.length ; i < limit ; i++ ) - ia[i] = 37; -var v = 0; -for ( var i=0 ; i < 1000 ; i++ ) - v += fence(ia); -//print(v); diff --git a/js/src/jit/AtomicOperations.h b/js/src/jit/AtomicOperations.h index d9edafa7e265..16196342a282 100644 --- a/js/src/jit/AtomicOperations.h +++ b/js/src/jit/AtomicOperations.h @@ -299,8 +299,11 @@ AtomicOperations::isLockfree(int32_t size) switch (size) { case 1: + return true; case 2: + return true; case 4: + // The spec requires Atomics.isLockFree(4) to return true. return true; case 8: // The spec requires Atomics.isLockFree(n) to return false diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 649665ca0bb0..bc6da000f3b2 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -10007,7 +10007,10 @@ CodeGenerator::visitAtomicIsLockFree(LAtomicIsLockFree* lir) Register output = ToRegister(lir->output()); // Keep this in sync with isLockfree() in jit/AtomicOperations.h. - MOZ_ASSERT(!AtomicOperations::isLockfree(8)); + MOZ_ASSERT(AtomicOperations::isLockfree(1)); // Implementation artifact + MOZ_ASSERT(AtomicOperations::isLockfree(2)); // Implementation artifact + MOZ_ASSERT(AtomicOperations::isLockfree(4)); // Spec requirement + MOZ_ASSERT(!AtomicOperations::isLockfree(8)); // Implementation invariant, for now Label Ldone, Lfailed; masm.move32(Imm32(1), output); diff --git a/js/src/jit/InlinableNatives.h b/js/src/jit/InlinableNatives.h index 3364bd512bf6..44eac9565ba6 100644 --- a/js/src/jit/InlinableNatives.h +++ b/js/src/jit/InlinableNatives.h @@ -22,7 +22,6 @@ _(AtomicsExchange) \ _(AtomicsLoad) \ _(AtomicsStore) \ - _(AtomicsFence) \ _(AtomicsAdd) \ _(AtomicsSub) \ _(AtomicsAnd) \ diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index 2f09796ffd05..56287b5c08b3 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -832,7 +832,6 @@ class IonBuilder InliningStatus inlineAtomicsExchange(CallInfo& callInfo); InliningStatus inlineAtomicsLoad(CallInfo& callInfo); InliningStatus inlineAtomicsStore(CallInfo& callInfo); - InliningStatus inlineAtomicsFence(CallInfo& callInfo); InliningStatus inlineAtomicsBinop(CallInfo& callInfo, InlinableNative target); InliningStatus inlineAtomicsIsLockFree(CallInfo& callInfo); diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 937b44a8aa54..face9c0ec3a4 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -4204,13 +4204,6 @@ LIRGenerator::visitRecompileCheck(MRecompileCheck* ins) assignSafepoint(lir, ins); } -void -LIRGenerator::visitMemoryBarrier(MMemoryBarrier* ins) -{ - LMemoryBarrier* lir = new(alloc()) LMemoryBarrier(ins->type()); - add(lir, ins); -} - void LIRGenerator::visitSimdBox(MSimdBox* ins) { diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index f4a4a4d0acfd..3b76bc688c76 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -286,7 +286,6 @@ class LIRGenerator : public LIRGeneratorSpecific void visitGetDOMProperty(MGetDOMProperty* ins); void visitGetDOMMember(MGetDOMMember* ins); void visitRecompileCheck(MRecompileCheck* ins); - void visitMemoryBarrier(MMemoryBarrier* ins); void visitSimdBox(MSimdBox* ins); void visitSimdUnbox(MSimdUnbox* ins); void visitSimdExtractElement(MSimdExtractElement* ins); diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index a5c736fdf3c8..8796be1204c5 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -93,8 +93,6 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target) return inlineAtomicsLoad(callInfo); case InlinableNative::AtomicsStore: return inlineAtomicsStore(callInfo); - case InlinableNative::AtomicsFence: - return inlineAtomicsFence(callInfo); case InlinableNative::AtomicsAdd: case InlinableNative::AtomicsSub: case InlinableNative::AtomicsAnd: @@ -2915,30 +2913,6 @@ IonBuilder::inlineAtomicsStore(CallInfo& callInfo) return InliningStatus_Inlined; } -IonBuilder::InliningStatus -IonBuilder::inlineAtomicsFence(CallInfo& callInfo) -{ - if (callInfo.argc() != 0 || callInfo.constructing()) { - trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); - return InliningStatus_NotInlined; - } - - if (!JitSupportsAtomics()) - return InliningStatus_NotInlined; - - callInfo.setImplicitlyUsedUnchecked(); - - MMemoryBarrier* fence = MMemoryBarrier::New(alloc()); - current->add(fence); - pushConstant(UndefinedValue()); - - // Fences are considered effectful (they execute a memory barrier). - if (!resumeAfter(fence)) - return InliningStatus_Error; - - return InliningStatus_Inlined; -} - IonBuilder::InliningStatus IonBuilder::inlineAtomicsBinop(CallInfo& callInfo, InlinableNative target) { diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index f972dc8b5afd..e3303a232e02 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -9995,7 +9995,21 @@ class MArrayJoin MDefinition* foldsTo(TempAllocator& alloc) override; }; -// See comments above MMemoryBarrier, below. +// All barriered operations - MCompareExchangeTypedArrayElement, +// MExchangeTypedArrayElement, and MAtomicTypedArrayElementBinop, as +// well as MLoadUnboxedScalar and MStoreUnboxedScalar when they are +// marked as requiring a memory barrer - have the following +// attributes: +// +// - Not movable +// - Not removable +// - Not congruent with any other instruction +// - Effectful (they alias every TypedArray store) +// +// The intended effect of those constraints is to prevent all loads +// and stores preceding the barriered operation from being moved to +// after the barriered operation, and vice versa, and to prevent the +// barriered operation from being removed or hoisted. enum MemoryBarrierRequirement { @@ -10003,7 +10017,7 @@ enum MemoryBarrierRequirement DoesRequireMemoryBarrier }; -// Also see comments above MMemoryBarrier, below. +// Also see comments at MMemoryBarrierRequirement, above. // Load an unboxed scalar value from a typed array or other object. class MLoadUnboxedScalar @@ -13648,49 +13662,6 @@ class MRecompileCheck : public MNullaryInstruction } }; -// All barriered operations - MMemoryBarrier, MCompareExchangeTypedArrayElement, -// MExchangeTypedArrayElement, and MAtomicTypedArrayElementBinop, as well as -// MLoadUnboxedScalar and MStoreUnboxedScalar when they are marked as requiring -// a memory barrer - have the following attributes: -// -// - Not movable -// - Not removable -// - Not congruent with any other instruction -// - Effectful (they alias every TypedArray store) -// -// The intended effect of those constraints is to prevent all loads -// and stores preceding the barriered operation from being moved to -// after the barriered operation, and vice versa, and to prevent the -// barriered operation from being removed or hoisted. - -class MMemoryBarrier - : public MNullaryInstruction -{ - // The type is a combination of the memory barrier types in AtomicOp.h. - const MemoryBarrierBits type_; - - explicit MMemoryBarrier(MemoryBarrierBits type) - : type_(type) - { - MOZ_ASSERT((type_ & ~MembarAllbits) == MembarNobits); - setGuard(); // Not removable - } - - public: - INSTRUCTION_HEADER(MemoryBarrier) - - static MMemoryBarrier* New(TempAllocator& alloc, MemoryBarrierBits type = MembarFull) { - return new(alloc) MMemoryBarrier(type); - } - MemoryBarrierBits type() const { - return type_; - } - - AliasSet getAliasSet() const override { - return AliasSet::Store(AliasSet::UnboxedElement); - } -}; - class MAtomicIsLockFree : public MUnaryInstruction, public ConvertToInt32Policy<0>::Data diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index 45ee12768dbe..a0e69f6e37da 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -280,7 +280,6 @@ namespace jit { _(AsmReinterpret) \ _(NewDerivedTypedObject) \ _(RecompileCheck) \ - _(MemoryBarrier) \ _(AsmJSCompareExchangeHeap) \ _(AsmJSAtomicExchangeHeap) \ _(AsmJSAtomicBinopHeap) \ diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index debddd4716f4..51353b5643ce 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -7963,10 +7963,6 @@ class LMemoryBarrier : public LInstructionHelper<0, 0, 0> MemoryBarrierBits type() const { return type_; } - - const MMemoryBarrier* mir() const { - return mir_->toMemoryBarrier(); - } }; class LDebugger : public LCallInstructionHelper<0, 0, 2> diff --git a/js/src/js.msg b/js/src/js.msg index 99ef8d2c88fa..c64fca37daf2 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -497,7 +497,6 @@ MSG_DEF(JSMSG_SYMBOL_TO_NUMBER, 0, JSEXN_TYPEERR, "can't convert symbol t MSG_DEF(JSMSG_ATOMICS_BAD_ARRAY, 0, JSEXN_TYPEERR, "invalid array type for the operation") MSG_DEF(JSMSG_ATOMICS_TOO_LONG, 0, JSEXN_RANGEERR, "timeout value too large") MSG_DEF(JSMSG_ATOMICS_WAIT_NOT_ALLOWED, 0, JSEXN_ERR, "waiting is not allowed on this thread") -MSG_DEF(JSMSG_ATOMICS_BAD_INDEX, 0, JSEXN_RANGEERR, "out-of-range index for atomic access") // XPConnect wrappers and DOM bindings MSG_DEF(JSMSG_CANT_SET_INTERPOSED, 1, JSEXN_TYPEERR, "unable to set interposed data property '{0}'") diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index d2c6e096c013..e16c35579084 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -3262,6 +3262,16 @@ array_proto_finish(JSContext* cx, JS::HandleObject ctor, JS::HandleObject proto) return DefineProperty(cx, proto, id, value, nullptr, nullptr, JSPROP_READONLY); } +static const ClassSpec ArrayObjectClassSpec = { + GenericCreateConstructor, + CreateArrayPrototype, + array_static_methods, + nullptr, + array_methods, + nullptr, + array_proto_finish +}; + const Class ArrayObject::class_ = { "Array", JSCLASS_HAS_CACHED_PROTO(JSProto_Array) | JSCLASS_DELAY_METADATA_CALLBACK, @@ -3277,15 +3287,7 @@ const Class ArrayObject::class_ = { nullptr, /* hasInstance */ nullptr, /* construct */ nullptr, /* trace */ - { - GenericCreateConstructor, - CreateArrayPrototype, - array_static_methods, - nullptr, - array_methods, - nullptr, - array_proto_finish - } + &ArrayObjectClassSpec }; /* diff --git a/js/src/jsdate.cpp b/js/src/jsdate.cpp index dd74f989ccf7..1f3ae9654f9e 100644 --- a/js/src/jsdate.cpp +++ b/js/src/jsdate.cpp @@ -3272,6 +3272,16 @@ FinishDateClassInit(JSContext* cx, HandleObject ctor, HandleObject proto) nullptr, nullptr, 0); } +static const ClassSpec DateObjectClassSpec = { + GenericCreateConstructor, + CreateDatePrototype, + date_static_methods, + nullptr, + date_methods, + nullptr, + FinishDateClassInit +}; + const Class DateObject::class_ = { js_Date_str, JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) | @@ -3288,15 +3298,18 @@ const Class DateObject::class_ = { nullptr, /* hasInstance */ nullptr, /* construct */ nullptr, /* trace */ - { - GenericCreateConstructor, - CreateDatePrototype, - date_static_methods, - nullptr, - date_methods, - nullptr, - FinishDateClassInit - } + &DateObjectClassSpec +}; + +static const ClassSpec DateObjectProtoClassSpec = { + DELEGATED_CLASSSPEC(DateObject::class_.spec), + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + ClassSpec::IsDelegated }; const Class DateObject::protoClass_ = { @@ -3314,16 +3327,7 @@ const Class DateObject::protoClass_ = { nullptr, /* hasInstance */ nullptr, /* construct */ nullptr, /* trace */ - { - DELEGATED_CLASSSPEC(&DateObject::class_.spec), - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - ClassSpec::IsDelegated - } + &DateObjectProtoClassSpec }; JSObject* diff --git a/js/src/jsexn.cpp b/js/src/jsexn.cpp index 7d5627748bbb..aa5d56392419 100644 --- a/js/src/jsexn.cpp +++ b/js/src/jsexn.cpp @@ -65,7 +65,7 @@ static const JSFunctionSpec exception_methods[] = { JS_FS_END }; -#define IMPLEMENT_ERROR_SUBCLASS_EXTRA_FLAGS(name, extraClassSpecFlags) \ +#define IMPLEMENT_ERROR_CLASS(name, classSpecPtr) \ { \ js_Error_str, /* yes, really */ \ JSCLASS_HAS_CACHED_PROTO(JSProto_##name) | \ @@ -82,60 +82,59 @@ static const JSFunctionSpec exception_methods[] = { nullptr, /* hasInstance */ \ nullptr, /* construct */ \ nullptr, /* trace */ \ - { \ - ErrorObject::createConstructor, \ - ErrorObject::createProto, \ - nullptr, \ - nullptr, \ - exception_methods, \ - exception_properties, \ - nullptr, \ - JSProto_Error | extraClassSpecFlags \ - } \ + classSpecPtr \ } -#define IMPLEMENT_ERROR_SUBCLASS(name) \ - IMPLEMENT_ERROR_SUBCLASS_EXTRA_FLAGS(name, 0) +const ClassSpec +ErrorObject::errorClassSpec_ = { + ErrorObject::createConstructor, + ErrorObject::createProto, + nullptr, + nullptr, + exception_methods, + exception_properties, + nullptr, + 0 +}; + +const ClassSpec +ErrorObject::subErrorClassSpec_ = { + ErrorObject::createConstructor, + ErrorObject::createProto, + nullptr, + nullptr, + exception_methods, + exception_properties, + nullptr, + JSProto_Error +}; + +const ClassSpec +ErrorObject::debuggeeWouldRunClassSpec_ = { + ErrorObject::createConstructor, + ErrorObject::createProto, + nullptr, + nullptr, + exception_methods, + exception_properties, + nullptr, + JSProto_Error | ClassSpec::DontDefineConstructor +}; const Class ErrorObject::classes[JSEXN_LIMIT] = { - { - js_Error_str, - JSCLASS_HAS_CACHED_PROTO(JSProto_Error) | - JSCLASS_HAS_RESERVED_SLOTS(ErrorObject::RESERVED_SLOTS), - nullptr, /* addProperty */ - nullptr, /* delProperty */ - nullptr, /* getProperty */ - nullptr, /* setProperty */ - nullptr, /* enumerate */ - nullptr, /* resolve */ - nullptr, /* mayResolve */ - exn_finalize, - nullptr, /* call */ - nullptr, /* hasInstance */ - nullptr, /* construct */ - nullptr, /* trace */ - { - ErrorObject::createConstructor, - ErrorObject::createProto, - nullptr, - nullptr, - exception_methods, - exception_properties, - nullptr - } - }, - IMPLEMENT_ERROR_SUBCLASS(InternalError), - IMPLEMENT_ERROR_SUBCLASS(EvalError), - IMPLEMENT_ERROR_SUBCLASS(RangeError), - IMPLEMENT_ERROR_SUBCLASS(ReferenceError), - IMPLEMENT_ERROR_SUBCLASS(SyntaxError), - IMPLEMENT_ERROR_SUBCLASS(TypeError), - IMPLEMENT_ERROR_SUBCLASS(URIError), + IMPLEMENT_ERROR_CLASS(Error, &ErrorObject::errorClassSpec_), + IMPLEMENT_ERROR_CLASS(InternalError, &ErrorObject::subErrorClassSpec_), + IMPLEMENT_ERROR_CLASS(EvalError, &ErrorObject::subErrorClassSpec_), + IMPLEMENT_ERROR_CLASS(RangeError, &ErrorObject::subErrorClassSpec_), + IMPLEMENT_ERROR_CLASS(ReferenceError, &ErrorObject::subErrorClassSpec_), + IMPLEMENT_ERROR_CLASS(SyntaxError, &ErrorObject::subErrorClassSpec_), + IMPLEMENT_ERROR_CLASS(TypeError, &ErrorObject::subErrorClassSpec_), + IMPLEMENT_ERROR_CLASS(URIError, &ErrorObject::subErrorClassSpec_), // DebuggeeWouldRun is a subclass of Error but is accessible via the // Debugger constructor, not the global. - IMPLEMENT_ERROR_SUBCLASS_EXTRA_FLAGS(DebuggeeWouldRun, ClassSpec::DontDefineConstructor), + IMPLEMENT_ERROR_CLASS(DebuggeeWouldRun, &ErrorObject::debuggeeWouldRunClassSpec_) }; JSErrorReport* diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 6a57d806d13e..f20f5611629d 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -643,7 +643,7 @@ inline bool StandardClassIsDependent(JSProtoKey key) { const Class* clasp = ProtoKeyToClass(key); - return clasp && clasp->spec.defined() && clasp->spec.dependent(); + return clasp && clasp->specDefined() && clasp->specDependent(); } // Returns the key for the class inherited by a given standard class (that @@ -662,7 +662,7 @@ ParentKeyForStandardClass(JSProtoKey key) // If we're dependent, return the key of the class we depend on. if (StandardClassIsDependent(key)) - return ProtoKeyToClass(key)->spec.parentKey(); + return ProtoKeyToClass(key)->specParentKey(); // Otherwise, we inherit [Object]. return JSProto_Object; diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 11ad95926703..606830435afa 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -834,6 +834,15 @@ CreateFunctionPrototype(JSContext* cx, JSProtoKey key) return functionProto; } +static const ClassSpec JSFunctionClassSpec = { + CreateFunctionConstructor, + CreateFunctionPrototype, + nullptr, + nullptr, + function_methods, + function_properties +}; + const Class JSFunction::class_ = { js_Function_str, JSCLASS_HAS_CACHED_PROTO(JSProto_Function), @@ -849,14 +858,7 @@ const Class JSFunction::class_ = { fun_hasInstance, nullptr, /* construct */ fun_trace, - { - CreateFunctionConstructor, - CreateFunctionPrototype, - nullptr, - nullptr, - function_methods, - function_properties - } + &JSFunctionClassSpec }; const Class* const js::FunctionClassPtr = &JSFunction::class_; diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp index 285d5dfa5775..4c95d20a8cea 100644 --- a/js/src/jsnum.cpp +++ b/js/src/jsnum.cpp @@ -1748,6 +1748,55 @@ js::ToLengthClamped(JSContext*, HandleValue, uint32_t*, bool*); template bool js::ToLengthClamped(ExclusiveContext*, HandleValue, uint32_t*, bool*); +bool +js::ToIntegerIndex(JSContext* cx, JS::HandleValue v, uint64_t* index) +{ + // Fast common case. + if (v.isInt32()) { + int32_t i = v.toInt32(); + if (i >= 0) { + *index = i; + return true; + } + } + + // Slow case. Use ToNumber() to coerce. This may throw a TypeError. + double d; + if (!ToNumber(cx, v, &d)) + return false; + + // Check that |d| is an integer in the valid range. + // + // Not all floating point integers fit in the range of a uint64_t, so we + // need a rough range check before the real range check in our caller. We + // could limit indexes to UINT64_MAX, but this would mean that our callers + // have to be very careful about integer overflow. The contiguous integer + // floating point numbers end at 2^53, so make that our upper limit. If we + // ever support arrays with more than 2^53 elements, this will need to + // change. + // + // Reject infinities, NaNs, and numbers outside the contiguous integer range + // with a RangeError. + + // Write relation so NaNs throw a RangeError. + if (!(0 <= d && d <= (uint64_t(1) << 53))) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX); + return false; + } + + // Check that d is an integer, throw a RangeError if not. + // Note that this conversion could invoke undefined behaviour without the + // range check above. + uint64_t i(d); + if (d != double(i)) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX); + return false; + } + + *index = i; + return true; +} + template bool js_strtod(ExclusiveContext* cx, const CharT* begin, const CharT* end, const CharT** dEnd, diff --git a/js/src/jsnum.h b/js/src/jsnum.h index f94bf085b3a7..ee5d4b9fe74e 100644 --- a/js/src/jsnum.h +++ b/js/src/jsnum.h @@ -275,6 +275,26 @@ ToInteger(JSContext* cx, HandleValue v, double* dp) template bool ToLengthClamped(T* cx, HandleValue v, uint32_t* out, bool* overflow); +/* Convert and range check an index value as for DataView, SIMD, and Atomics + * operations, eg ES7 24.2.1.1, DataView's GetViewValue(): + * + * 1. numericIndex = ToNumber(argument) (may throw TypeError) + * 2. intIndex = ToInteger(numericIndex) + * 3. if intIndex != numericIndex throw RangeError + * + * This function additionally bounds the range to the non-negative contiguous + * integers: + * + * 4. if intIndex < 0 or intIndex > 2^53 throw RangeError + * + * Return true and set |*index| to the integer value if |argument| is a valid + * array index argument. Otherwise report an TypeError or RangeError and return + * false. + * + * The returned index will always be in the range 0 <= *index <= 2^53. + */ +bool ToIntegerIndex(JSContext* cx, JS::HandleValue v, uint64_t* index); + inline bool SafeAdd(int32_t one, int32_t two, int32_t* res) { diff --git a/js/src/tests/shell/futex-apis.js b/js/src/tests/shell/futex-apis.js index e05deb718302..b3e0acd55488 100644 --- a/js/src/tests/shell/futex-apis.js +++ b/js/src/tests/shell/futex-apis.js @@ -10,7 +10,7 @@ if (!(this.SharedArrayBuffer && this.Atomics)) { quit(0); } -// Checks for parameter validation of futex API. ALl of these test +// Checks for parameter validation of wait/wake API. All of these test // cases should throw exceptions during parameter validation, before // we check whether any waiting should be done. @@ -57,9 +57,8 @@ let sab = new SharedArrayBuffer(16); for ( let i=0 ; i < values.length ; i++ ) { let view = values[i]; - assertThrowsInstanceOf(() => Atomics.futexWait(view, 0, 0), TypeError); - assertThrowsInstanceOf(() => Atomics.futexWake(view, 0), TypeError); - assertThrowsInstanceOf(() => Atomics.futexWakeOrRequeue(view, 0, 0, 1, 0), TypeError); + assertThrowsInstanceOf(() => Atomics.wait(view, 0, 0), TypeError); + assertThrowsInstanceOf(() => Atomics.wake(view, 0), TypeError); } } @@ -71,9 +70,8 @@ let sab = new SharedArrayBuffer(16); for ( let View of views ) { let view = new View(ab); - assertThrowsInstanceOf(() => Atomics.futexWait(view, 0, 0), TypeError); - assertThrowsInstanceOf(() => Atomics.futexWake(view, 0), TypeError); - assertThrowsInstanceOf(() => Atomics.futexWakeOrRequeue(view, 0, 0, 1, 0), TypeError); + assertThrowsInstanceOf(() => Atomics.wait(view, 0, 0), TypeError); + assertThrowsInstanceOf(() => Atomics.wake(view, 0), TypeError); } } @@ -86,9 +84,8 @@ let sab = new SharedArrayBuffer(16); for ( let View of views ) { let view = new View(sab); - assertThrowsInstanceOf(() => Atomics.futexWait(view, 0, 0), TypeError); - assertThrowsInstanceOf(() => Atomics.futexWake(view, 0), TypeError); - assertThrowsInstanceOf(() => Atomics.futexWakeOrRequeue(view, 0, 0, 1, 0), TypeError); + assertThrowsInstanceOf(() => Atomics.wait(view, 0, 0), TypeError); + assertThrowsInstanceOf(() => Atomics.wake(view, 0), TypeError); } } @@ -108,10 +105,8 @@ let sab = new SharedArrayBuffer(16); for ( let iidx=0 ; iidx < indices.length ; iidx++ ) { let Idx = indices[iidx](view); - assertThrowsInstanceOf(() => Atomics.futexWait(view, Idx, 10), RangeError); - assertThrowsInstanceOf(() => Atomics.futexWake(view, Idx), RangeError); - assertThrowsInstanceOf(() => Atomics.futexWakeOrRequeue(view, Idx, 5, 0, 0), RangeError); - assertThrowsInstanceOf(() => Atomics.futexWakeOrRequeue(view, 0, 5, Idx, 0), RangeError); + assertThrowsInstanceOf(() => Atomics.wait(view, Idx, 10), RangeError); + assertThrowsInstanceOf(() => Atomics.wake(view, Idx), RangeError); } } diff --git a/js/src/tests/shell/futex.js b/js/src/tests/shell/futex.js index bd3f1b0c4fd7..3800a396d186 100644 --- a/js/src/tests/shell/futex.js +++ b/js/src/tests/shell/futex.js @@ -17,7 +17,7 @@ function dprint(s) { } // Tests the SharedArrayBuffer mailbox in the shell. -// Tests the futex functionality in the shell. +// Tests the wait/wake functionality in the shell. var sab = new SharedArrayBuffer(12); var mem = new Int32Array(sab); @@ -61,19 +61,34 @@ assertThrowsInstanceOf(() => setSharedArrayBuffer(() => 37), Error); // Futex test +if (helperThreadCount() === 0) { + // Abort if there is no helper thread. + reportCompare(true,true); + quit(); +} + +//////////////////////////////////////////////////////////// + +// wait() returns "not-equal" if the value is not the expected one. + +mem[0] = 42; + +assertEq(Atomics.wait(mem, 0, 33), "not-equal"); + +// wait() returns "timed-out" if it times out + +assertEq(Atomics.wait(mem, 0, 42, 100), "timed-out"); + +//////////////////////////////////////////////////////////// + // Main is sharing the buffer with the worker; the worker is clearing // the buffer. mem[0] = 42; mem[1] = 37; mem[2] = DEBUG; -setSharedArrayBuffer(mem.buffer); -if (helperThreadCount() === 0) { - // Abort if there is no helper thread. - reportCompare(true,true); - quit(); -} +setSharedArrayBuffer(mem.buffer); evalInWorker(` var mem = new Int32Array(getSharedArrayBuffer()); @@ -83,40 +98,61 @@ function dprint(s) { assertEq(mem[0], 42); // what was written in the main thread assertEq(mem[1], 37); // is read in the worker mem[1] = 1337; -dprint("Sleeping for 3 seconds"); -sleep(3); +dprint("Sleeping for 2 seconds"); +sleep(2); dprint("Waking the main thread now"); setSharedArrayBuffer(null); -Atomics.futexWake(mem, 0, 1); +assertEq(Atomics.wake(mem, 0, 1), 1); // Can fail spuriously but very unlikely `); var then = Date.now(); -assertEq(Atomics.futexWait(mem, 0, 42), Atomics.OK); +assertEq(Atomics.wait(mem, 0, 42), "ok"); dprint("Woke up as I should have in " + (Date.now() - then)/1000 + "s"); assertEq(mem[1], 1337); // what was written in the worker is read in the main thread assertEq(getSharedArrayBuffer(), null); // The worker's clearing of the mbx is visible +//////////////////////////////////////////////////////////// + +// Test the default argument to atomics.wake() + +setSharedArrayBuffer(mem.buffer); + +evalInWorker(` +var mem = new Int32Array(getSharedArrayBuffer()); +sleep(2); // Probably long enough to avoid a spurious error next +assertEq(Atomics.wake(mem, 0), 1); // Last argument to wake should default to +Infinity +`); + +var then = Date.now(); +dprint("Main thread waiting on wakeup (2s)"); +assertEq(Atomics.wait(mem, 0, 42), "ok"); +dprint("Woke up as I should have in " + (Date.now() - then)/1000 + "s"); + +//////////////////////////////////////////////////////////// + // A tricky case: while in the wait there will be an interrupt, and in -// the interrupt handler we will execute a futexWait. This is +// the interrupt handler we will execute a wait. This is // explicitly prohibited (for now), so there should be a catchable exception. timeout(2, function () { - dprint("In the interrupt, starting inner wait"); - Atomics.futexWait(mem, 0, 42); // Should throw and propagate all the way out + dprint("In the interrupt, starting inner wait with timeout 2s"); + Atomics.wait(mem, 0, 42); // Should throw and propagate all the way out }); var exn = false; try { dprint("Starting outer wait"); - assertEq(Atomics.futexWait(mem, 0, 42, 5000), Atomics.OK); + assertEq(Atomics.wait(mem, 0, 42, 5000), "ok"); } catch (e) { - dprint("Got the exception!"); + dprint("Got the timeout exception!"); exn = true; } finally { timeout(-1); } assertEq(exn, true); -dprint("Done"); +//////////////////////////////////////////////////////////// + +dprint("Done"); reportCompare(true,true); diff --git a/js/src/vm/ArrayBufferObject.cpp b/js/src/vm/ArrayBufferObject.cpp index 8526a3d0c00c..0dcea906f9dc 100644 --- a/js/src/vm/ArrayBufferObject.cpp +++ b/js/src/vm/ArrayBufferObject.cpp @@ -1017,6 +1017,7 @@ ArrayBufferViewObject::trace(JSTracer* trc, JSObject* objArg) ArrayBufferObject& buf = AsArrayBuffer(MaybeForwarded(&bufSlot.toObject())); uint32_t offset = uint32_t(obj->getFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT).toInt32()); MOZ_ASSERT(buf.dataPointer() != nullptr); + MOZ_ASSERT(offset <= INT32_MAX); if (buf.forInlineTypedObject()) { // The data is inline with an InlineTypedObject associated with the diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h index 683227940155..7fa17e3913d2 100644 --- a/js/src/vm/CommonPropertyNames.h +++ b/js/src/vm/CommonPropertyNames.h @@ -112,6 +112,9 @@ macro(formatToParts, formatToParts, "formatToParts") \ macro(frame, frame, "frame") \ macro(from, from, "from") \ + macro(futexOK, futexOK, "ok") \ + macro(futexNotEqual, futexNotEqual, "not-equal") \ + macro(futexTimedOut, futexTimedOut, "timed-out") \ macro(gcCycleNumber, gcCycleNumber, "gcCycleNumber") \ macro(GeneratorFunction, GeneratorFunction, "GeneratorFunction") \ macro(get, get, "get") \ diff --git a/js/src/vm/ErrorObject.h b/js/src/vm/ErrorObject.h index 39e2c37f5dd6..75d5c95cb7a2 100644 --- a/js/src/vm/ErrorObject.h +++ b/js/src/vm/ErrorObject.h @@ -41,6 +41,10 @@ class ErrorObject : public NativeObject static bool checkAndUnwrapThis(JSContext* cx, CallArgs& args, const char* fnName, MutableHandle error); + static const ClassSpec errorClassSpec_; + static const ClassSpec subErrorClassSpec_; + static const ClassSpec debuggeeWouldRunClassSpec_; + protected: static const uint32_t EXNTYPE_SLOT = 0; static const uint32_t STACK_SLOT = EXNTYPE_SLOT + 1; diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp index 620c99290308..71207a380245 100644 --- a/js/src/vm/GlobalObject.cpp +++ b/js/src/vm/GlobalObject.cpp @@ -162,7 +162,7 @@ GlobalObject::resolveConstructor(JSContext* cx, Handle global, JS // GlobalObject::initStandardClasses that want to just carpet-bomb-call // ensureConstructor with every JSProtoKey. So it's easier to just handle // it here. - bool haveSpec = clasp && clasp->spec.defined(); + bool haveSpec = clasp && clasp->specDefined(); if (!init && !haveSpec) return true; @@ -195,8 +195,8 @@ GlobalObject::resolveConstructor(JSContext* cx, Handle global, JS // |createPrototype|, |prototypeFunctions|, and |prototypeProperties| // should all be null. RootedObject proto(cx); - if (clasp->spec.createPrototypeHook()) { - proto = clasp->spec.createPrototypeHook()(cx, key); + if (ClassObjectCreationOp createPrototype = clasp->specCreatePrototypeHook()) { + proto = createPrototype(cx, key); if (!proto) return false; @@ -211,12 +211,12 @@ GlobalObject::resolveConstructor(JSContext* cx, Handle global, JS } // Create the constructor. - RootedObject ctor(cx, clasp->spec.createConstructorHook()(cx, key)); + RootedObject ctor(cx, clasp->specCreateConstructorHook()(cx, key)); if (!ctor) return false; RootedId id(cx, NameToId(ClassName(key, cx))); - if (clasp->spec.shouldDefineConstructor()) { + if (clasp->specShouldDefineConstructor()) { if (!global->addDataProperty(cx, id, constructorPropertySlot(key), 0)) return false; } @@ -229,19 +229,19 @@ GlobalObject::resolveConstructor(JSContext* cx, Handle global, JS // operating on the self-hosting global, in which case we don't want any // functions and properties on the builtins and their prototypes. if (!StandardClassIsDependent(key) && !cx->runtime()->isSelfHostingGlobal(global)) { - if (const JSFunctionSpec* funs = clasp->spec.prototypeFunctions()) { + if (const JSFunctionSpec* funs = clasp->specPrototypeFunctions()) { if (!JS_DefineFunctions(cx, proto, funs)) return false; } - if (const JSPropertySpec* props = clasp->spec.prototypeProperties()) { + if (const JSPropertySpec* props = clasp->specPrototypeProperties()) { if (!JS_DefineProperties(cx, proto, props)) return false; } - if (const JSFunctionSpec* funs = clasp->spec.constructorFunctions()) { + if (const JSFunctionSpec* funs = clasp->specConstructorFunctions()) { if (!JS_DefineFunctions(cx, ctor, funs)) return false; } - if (const JSPropertySpec* props = clasp->spec.constructorProperties()) { + if (const JSPropertySpec* props = clasp->specConstructorProperties()) { if (!JS_DefineProperties(cx, ctor, props)) return false; } @@ -252,10 +252,12 @@ GlobalObject::resolveConstructor(JSContext* cx, Handle global, JS return false; // Call the post-initialization hook, if provided. - if (clasp->spec.finishInitHook() && !clasp->spec.finishInitHook()(cx, ctor, proto)) - return false; + if (FinishClassInitOp finishInit = clasp->specFinishInitHook()) { + if (!finishInit(cx, ctor, proto)) + return false; + } - if (clasp->spec.shouldDefineConstructor()) { + if (clasp->specShouldDefineConstructor()) { // Stash type information, so that what we do here is equivalent to // initBuiltinConstructor. AddTypePropertyId(cx, global, id, ObjectValue(*ctor)); @@ -416,11 +418,11 @@ InitBareBuiltinCtor(JSContext* cx, Handle global, JSProtoKey prot MOZ_ASSERT(cx->runtime()->isSelfHostingGlobal(global)); const Class* clasp = ProtoKeyToClass(protoKey); RootedObject proto(cx); - proto = clasp->spec.createPrototypeHook()(cx, protoKey); + proto = clasp->specCreatePrototypeHook()(cx, protoKey); if (!proto) return false; - RootedObject ctor(cx, clasp->spec.createConstructorHook()(cx, protoKey)); + RootedObject ctor(cx, clasp->specCreateConstructorHook()(cx, protoKey)); if (!ctor) return false; diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp index 54bc25b601d2..d3fbf7e178d2 100644 --- a/js/src/vm/RegExpObject.cpp +++ b/js/src/vm/RegExpObject.cpp @@ -185,6 +185,15 @@ RegExpObject::trace(JSTracer* trc, JSObject* obj) } } +static const ClassSpec RegExpObjectClassSpec = { + GenericCreateConstructor, + CreateRegExpPrototype, + nullptr, + js::regexp_static_props, + js::regexp_methods, + js::regexp_properties +}; + const Class RegExpObject::class_ = { js_RegExp_str, JSCLASS_HAS_PRIVATE | @@ -202,16 +211,7 @@ const Class RegExpObject::class_ = { nullptr, /* hasInstance */ nullptr, /* construct */ RegExpObject::trace, - - // ClassSpec - { - GenericCreateConstructor, - CreateRegExpPrototype, - nullptr, - js::regexp_static_props, - js::regexp_methods, - js::regexp_properties - } + &RegExpObjectClassSpec }; RegExpObject* diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index 765d21daeee4..a634085d9226 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -652,7 +652,7 @@ JSRuntime::requestInterrupt(InterruptMode mode) // collection among others), take additional steps to // interrupt corner cases where the above fields are not // regularly polled. Wake both ilooping JIT code and - // futexWait. + // Atomics.wait(). fx.lock(); if (fx.isWaiting()) fx.wake(FutexRuntime::WakeForJSInterrupt); diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 4dbb89e0ac39..3e586066de14 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -961,7 +961,7 @@ struct JSRuntime : public JS::shadow::Runtime, /* Default JSVersion. */ JSVersion defaultVersion_; - /* Futex state, used by futexWait and futexWake on the Atomics object */ + /* Futex state, used by Atomics.wait() and Atomics.wake() on the Atomics object */ js::FutexRuntime fx; private: diff --git a/js/src/vm/SavedFrame.h b/js/src/vm/SavedFrame.h index 484ba0feac34..905e8f2f0104 100644 --- a/js/src/vm/SavedFrame.h +++ b/js/src/vm/SavedFrame.h @@ -18,6 +18,8 @@ class SavedFrame : public NativeObject { friend class SavedStacks; friend struct ::JSStructuredCloneReader; + static const ClassSpec classSpec_; + public: static const Class class_; static const JSPropertySpec protoAccessors[]; diff --git a/js/src/vm/SavedStacks.cpp b/js/src/vm/SavedStacks.cpp index 0f3332fb57ef..82eeab93face 100644 --- a/js/src/vm/SavedStacks.cpp +++ b/js/src/vm/SavedStacks.cpp @@ -289,6 +289,17 @@ SavedFrame::finishSavedFrameInit(JSContext* cx, HandleObject ctor, HandleObject return FreezeObject(cx, proto); } +const ClassSpec SavedFrame::classSpec_ = { + GenericCreateConstructor, + GenericCreatePrototype, + SavedFrame::staticFunctions, + nullptr, + SavedFrame::protoFunctions, + SavedFrame::protoAccessors, + SavedFrame::finishSavedFrameInit, + ClassSpec::DontDefineConstructor +}; + /* static */ const Class SavedFrame::class_ = { "SavedFrame", JSCLASS_HAS_PRIVATE | @@ -307,18 +318,7 @@ SavedFrame::finishSavedFrameInit(JSContext* cx, HandleObject ctor, HandleObject nullptr, // hasInstance nullptr, // construct nullptr, // trace - - // ClassSpec - { - GenericCreateConstructor, - GenericCreatePrototype, - SavedFrame::staticFunctions, - nullptr, - SavedFrame::protoFunctions, - SavedFrame::protoAccessors, - SavedFrame::finishSavedFrameInit, - ClassSpec::DontDefineConstructor - } + &SavedFrame::classSpec_ }; /* static */ const JSFunctionSpec diff --git a/js/src/vm/ScopeObject.cpp b/js/src/vm/ScopeObject.cpp index b00714ef8f22..352cd8b000ce 100644 --- a/js/src/vm/ScopeObject.cpp +++ b/js/src/vm/ScopeObject.cpp @@ -868,7 +868,7 @@ with_DeleteProperty(JSContext* cx, HandleObject obj, HandleId id, ObjectOpResult return DeleteProperty(cx, actual, id, result); } -const ObjectOps DynamicWithObject::objectOps_ = { +static const ObjectOps DynamicWithObjectObjectOps = { with_LookupProperty, with_DefineProperty, with_HasProperty, @@ -900,7 +900,7 @@ const Class DynamicWithObject::class_ = { nullptr, /* trace */ JS_NULL_CLASS_SPEC, JS_NULL_CLASS_EXT, - &DynamicWithObject::objectOps_ + &DynamicWithObjectObjectOps }; /* static */ StaticEvalScope* @@ -1124,20 +1124,6 @@ ClonedBlockObject::thisValue() const static_assert(StaticBlockScope::RESERVED_SLOTS == ClonedBlockObject::RESERVED_SLOTS, "static block scopes and dynamic block environments share a Class"); -const ObjectOps ClonedBlockObject::objectOps_ = { - nullptr, /* lookupProperty */ - nullptr, /* defineProperty */ - nullptr, /* hasProperty */ - nullptr, /* getProperty */ - nullptr, /* setProperty */ - nullptr, /* getOwnPropertyDescriptor */ - nullptr, /* deleteProperty */ - nullptr, nullptr, /* watch/unwatch */ - nullptr, /* getElements */ - nullptr, /* enumerate (native enumeration of target doesn't work) */ - nullptr, -}; - const Class ClonedBlockObject::class_ = { "Block", JSCLASS_HAS_RESERVED_SLOTS(ClonedBlockObject::RESERVED_SLOTS) | @@ -1156,7 +1142,7 @@ const Class ClonedBlockObject::class_ = { nullptr, /* trace */ JS_NULL_CLASS_SPEC, JS_NULL_CLASS_EXT, - &ClonedBlockObject::objectOps_ + JS_NULL_OBJECT_OPS }; template @@ -1397,7 +1383,7 @@ lexicalError_DeleteProperty(JSContext* cx, HandleObject obj, HandleId id, Object return false; } -const ObjectOps RuntimeLexicalErrorObject::objectOps_ = { +static const ObjectOps RuntimeLexicalErrorObjectObjectOps = { lexicalError_LookupProperty, nullptr, /* defineProperty */ lexicalError_HasProperty, @@ -1429,7 +1415,7 @@ const Class RuntimeLexicalErrorObject::class_ = { nullptr, /* trace */ JS_NULL_CLASS_SPEC, JS_NULL_CLASS_EXT, - &RuntimeLexicalErrorObject::objectOps_ + &RuntimeLexicalErrorObjectObjectOps }; /*****************************************************************************/ diff --git a/js/src/vm/ScopeObject.h b/js/src/vm/ScopeObject.h index 956ba1a0ee9f..5438a6bc853c 100644 --- a/js/src/vm/ScopeObject.h +++ b/js/src/vm/ScopeObject.h @@ -816,8 +816,9 @@ class ModuleEnvironmentObject : public LexicalScopeBase { static const uint32_t MODULE_SLOT = 1; - public: static const ObjectOps objectOps_; + + public: static const Class class_; static const uint32_t RESERVED_SLOTS = 2; @@ -912,7 +913,6 @@ class DynamicWithObject : public NestedScopeObject public: static const unsigned RESERVED_SLOTS = 4; - static const ObjectOps objectOps_; static const Class class_; enum WithKind { @@ -1075,7 +1075,6 @@ class RuntimeLexicalErrorObject : public ScopeObject public: static const unsigned RESERVED_SLOTS = 2; - static const ObjectOps objectOps_; static const Class class_; static RuntimeLexicalErrorObject* create(JSContext* cx, HandleObject enclosing, diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp index e1f4ef6c67d7..827df0437569 100644 --- a/js/src/vm/TypedArrayObject.cpp +++ b/js/src/vm/TypedArrayObject.cpp @@ -861,6 +861,18 @@ TypedArrayObject::staticFunctions[] = { JS_FS_END }; +static const ClassSpec +TypedArrayObjectSharedTypedArrayPrototypeClassSpec = { + GenericCreateConstructor, + GenericCreatePrototype, + TypedArrayObject::staticFunctions, + nullptr, + TypedArrayObject::protoFunctions, + TypedArrayObject::protoAccessors, + nullptr, + ClassSpec::DontDefineConstructor +}; + /* static */ const Class TypedArrayObject::sharedTypedArrayPrototypeClass = { // Actually ({}).toString.call(%TypedArray%.prototype) should throw, @@ -883,16 +895,7 @@ TypedArrayObject::sharedTypedArrayPrototypeClass = { nullptr, /* hasInstance */ nullptr, /* construct */ nullptr, /* trace */ - { - GenericCreateConstructor, - GenericCreatePrototype, - TypedArrayObject::staticFunctions, - nullptr, - TypedArrayObject::protoFunctions, - TypedArrayObject::protoAccessors, - nullptr, - ClassSpec::DontDefineConstructor - } + &TypedArrayObjectSharedTypedArrayPrototypeClassSpec }; template @@ -1905,24 +1908,36 @@ IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Uint32, uint32_t, uint32_t) IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Float32, float, float) IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Float64, double, double) -#define TYPED_ARRAY_CLASS_SPEC(_typedArray) \ +#define IMPL_TYPED_ARRAY_CLASS_SPEC(_type) \ { \ - _typedArray::createConstructor, \ - _typedArray::createPrototype, \ + _type##Array::createConstructor, \ + _type##Array::createPrototype, \ nullptr, \ nullptr, \ nullptr, \ nullptr, \ - _typedArray::finishClassInit, \ + _type##Array::finishClassInit, \ JSProto_TypedArray \ } -#define IMPL_TYPED_ARRAY_CLASS(_typedArray) \ +static const ClassSpec TypedArrayObjectClassSpecs[Scalar::MaxTypedArrayViewType] = { + IMPL_TYPED_ARRAY_CLASS_SPEC(Int8), + IMPL_TYPED_ARRAY_CLASS_SPEC(Uint8), + IMPL_TYPED_ARRAY_CLASS_SPEC(Int16), + IMPL_TYPED_ARRAY_CLASS_SPEC(Uint16), + IMPL_TYPED_ARRAY_CLASS_SPEC(Int32), + IMPL_TYPED_ARRAY_CLASS_SPEC(Uint32), + IMPL_TYPED_ARRAY_CLASS_SPEC(Float32), + IMPL_TYPED_ARRAY_CLASS_SPEC(Float64), + IMPL_TYPED_ARRAY_CLASS_SPEC(Uint8Clamped) +}; + +#define IMPL_TYPED_ARRAY_CLASS(_type) \ { \ - #_typedArray, \ + #_type "Array", \ JSCLASS_HAS_RESERVED_SLOTS(TypedArrayObject::RESERVED_SLOTS) | \ JSCLASS_HAS_PRIVATE | \ - JSCLASS_HAS_CACHED_PROTO(JSProto_##_typedArray) | \ + JSCLASS_HAS_CACHED_PROTO(JSProto_##_type##Array) | \ JSCLASS_DELAY_METADATA_CALLBACK, \ nullptr, /* addProperty */ \ nullptr, /* delProperty */ \ @@ -1936,19 +1951,43 @@ IMPL_TYPED_ARRAY_COMBINED_UNWRAPPERS(Float64, double, double) nullptr, /* hasInstance */ \ nullptr, /* construct */ \ TypedArrayObject::trace, /* trace */ \ - TYPED_ARRAY_CLASS_SPEC(_typedArray) \ + &TypedArrayObjectClassSpecs[Scalar::Type::_type] \ } const Class TypedArrayObject::classes[Scalar::MaxTypedArrayViewType] = { - IMPL_TYPED_ARRAY_CLASS(Int8Array), - IMPL_TYPED_ARRAY_CLASS(Uint8Array), - IMPL_TYPED_ARRAY_CLASS(Int16Array), - IMPL_TYPED_ARRAY_CLASS(Uint16Array), - IMPL_TYPED_ARRAY_CLASS(Int32Array), - IMPL_TYPED_ARRAY_CLASS(Uint32Array), - IMPL_TYPED_ARRAY_CLASS(Float32Array), - IMPL_TYPED_ARRAY_CLASS(Float64Array), - IMPL_TYPED_ARRAY_CLASS(Uint8ClampedArray) + IMPL_TYPED_ARRAY_CLASS(Int8), + IMPL_TYPED_ARRAY_CLASS(Uint8), + IMPL_TYPED_ARRAY_CLASS(Int16), + IMPL_TYPED_ARRAY_CLASS(Uint16), + IMPL_TYPED_ARRAY_CLASS(Int32), + IMPL_TYPED_ARRAY_CLASS(Uint32), + IMPL_TYPED_ARRAY_CLASS(Float32), + IMPL_TYPED_ARRAY_CLASS(Float64), + IMPL_TYPED_ARRAY_CLASS(Uint8Clamped) +}; + +#define IMPL_TYPED_ARRAY_PROTO_CLASS_SPEC(_type) \ +{ \ + DELEGATED_CLASSSPEC(TypedArrayObject::classes[Scalar::Type::_type].spec), \ + nullptr, \ + nullptr, \ + nullptr, \ + nullptr, \ + nullptr, \ + nullptr, \ + JSProto_TypedArray | ClassSpec::IsDelegated \ +} + +static const ClassSpec TypedArrayObjectProtoClassSpecs[Scalar::MaxTypedArrayViewType] = { + IMPL_TYPED_ARRAY_PROTO_CLASS_SPEC(Int8), + IMPL_TYPED_ARRAY_PROTO_CLASS_SPEC(Uint8), + IMPL_TYPED_ARRAY_PROTO_CLASS_SPEC(Int16), + IMPL_TYPED_ARRAY_PROTO_CLASS_SPEC(Uint16), + IMPL_TYPED_ARRAY_PROTO_CLASS_SPEC(Int32), + IMPL_TYPED_ARRAY_PROTO_CLASS_SPEC(Uint32), + IMPL_TYPED_ARRAY_PROTO_CLASS_SPEC(Float32), + IMPL_TYPED_ARRAY_PROTO_CLASS_SPEC(Float64), + IMPL_TYPED_ARRAY_PROTO_CLASS_SPEC(Uint8Clamped) }; // The various typed array prototypes are supposed to 1) be normal objects, @@ -1958,7 +1997,7 @@ const Class TypedArrayObject::classes[Scalar::MaxTypedArrayViewType] = { // prototype's class have the relevant typed array's cached JSProtoKey in them. // Thus we need one class with cached prototype per kind of typed array, with a // delegated ClassSpec. -#define IMPL_TYPED_ARRAY_PROTO_CLASS(typedArray, i) \ +#define IMPL_TYPED_ARRAY_PROTO_CLASS(_type) \ { \ /* * Actually ({}).toString.call(Uint8Array.prototype) should throw, because @@ -1967,8 +2006,8 @@ const Class TypedArrayObject::classes[Scalar::MaxTypedArrayViewType] = { * above), but it's what we've always done, so keep doing it till we * implement @@toStringTag or ES6 changes. */ \ - #typedArray "Prototype", \ - JSCLASS_HAS_CACHED_PROTO(JSProto_##typedArray), \ + #_type "ArrayPrototype", \ + JSCLASS_HAS_CACHED_PROTO(JSProto_##_type##Array), \ nullptr, /* addProperty */ \ nullptr, /* delProperty */ \ nullptr, /* getProperty */ \ @@ -1981,28 +2020,19 @@ const Class TypedArrayObject::classes[Scalar::MaxTypedArrayViewType] = { nullptr, /* hasInstance */ \ nullptr, /* construct */ \ nullptr, /* trace */ \ - { \ - DELEGATED_CLASSSPEC(&TypedArrayObject::classes[i].spec), \ - nullptr, \ - nullptr, \ - nullptr, \ - nullptr, \ - nullptr, \ - nullptr, \ - JSProto_TypedArray | ClassSpec::IsDelegated \ - } \ + &TypedArrayObjectProtoClassSpecs[Scalar::Type::_type] \ } const Class TypedArrayObject::protoClasses[Scalar::MaxTypedArrayViewType] = { - IMPL_TYPED_ARRAY_PROTO_CLASS(Int8Array, 0), - IMPL_TYPED_ARRAY_PROTO_CLASS(Uint8Array, 1), - IMPL_TYPED_ARRAY_PROTO_CLASS(Int16Array, 2), - IMPL_TYPED_ARRAY_PROTO_CLASS(Uint16Array, 3), - IMPL_TYPED_ARRAY_PROTO_CLASS(Int32Array, 4), - IMPL_TYPED_ARRAY_PROTO_CLASS(Uint32Array, 5), - IMPL_TYPED_ARRAY_PROTO_CLASS(Float32Array, 6), - IMPL_TYPED_ARRAY_PROTO_CLASS(Float64Array, 7), - IMPL_TYPED_ARRAY_PROTO_CLASS(Uint8ClampedArray, 8) + IMPL_TYPED_ARRAY_PROTO_CLASS(Int8), + IMPL_TYPED_ARRAY_PROTO_CLASS(Uint8), + IMPL_TYPED_ARRAY_PROTO_CLASS(Int16), + IMPL_TYPED_ARRAY_PROTO_CLASS(Uint16), + IMPL_TYPED_ARRAY_PROTO_CLASS(Int32), + IMPL_TYPED_ARRAY_PROTO_CLASS(Uint32), + IMPL_TYPED_ARRAY_PROTO_CLASS(Float32), + IMPL_TYPED_ARRAY_PROTO_CLASS(Float64), + IMPL_TYPED_ARRAY_PROTO_CLASS(Uint8Clamped) }; /* static */ bool diff --git a/js/src/vm/TypedArrayObject.h b/js/src/vm/TypedArrayObject.h index 619188e7e305..5e2419332c13 100644 --- a/js/src/vm/TypedArrayObject.h +++ b/js/src/vm/TypedArrayObject.h @@ -120,7 +120,9 @@ class TypedArrayObject : public NativeObject return tarr->getFixedSlot(BUFFER_SLOT); } static Value byteOffsetValue(TypedArrayObject* tarr) { - return tarr->getFixedSlot(BYTEOFFSET_SLOT); + Value v = tarr->getFixedSlot(BYTEOFFSET_SLOT); + MOZ_ASSERT(v.toInt32() >= 0); + return v; } static Value byteLengthValue(TypedArrayObject* tarr) { return Int32Value(tarr->getFixedSlot(LENGTH_SLOT).toInt32() * tarr->bytesPerElement()); diff --git a/js/src/vm/UnboxedObject.cpp b/js/src/vm/UnboxedObject.cpp index cb6c5baf6747..a71e429eaa04 100644 --- a/js/src/vm/UnboxedObject.cpp +++ b/js/src/vm/UnboxedObject.cpp @@ -905,7 +905,7 @@ const Class UnboxedExpandoObject::class_ = { 0 }; -const ObjectOps UnboxedPlainObject::objectOps_ = { +static const ObjectOps UnboxedPlainObjectObjectOps = { UnboxedPlainObject::obj_lookupProperty, UnboxedPlainObject::obj_defineProperty, UnboxedPlainObject::obj_hasProperty, @@ -939,7 +939,7 @@ const Class UnboxedPlainObject::class_ = { UnboxedPlainObject::trace, JS_NULL_CLASS_SPEC, JS_NULL_CLASS_EXT, - &UnboxedPlainObject::objectOps_ + &UnboxedPlainObjectObjectOps }; ///////////////////////////////////////////////////////////////////// @@ -1591,7 +1591,7 @@ UnboxedArrayObject::obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& return true; } -const ObjectOps UnboxedArrayObject::objectOps_ = { +static const ObjectOps UnboxedArrayObjectObjectOps = { UnboxedArrayObject::obj_lookupProperty, UnboxedArrayObject::obj_defineProperty, UnboxedArrayObject::obj_hasProperty, @@ -1629,7 +1629,7 @@ const Class UnboxedArrayObject::class_ = { nullptr, /* weakmapKeyDelegateOp */ UnboxedArrayObject::objectMoved }, - &UnboxedArrayObject::objectOps_ + &UnboxedArrayObjectObjectOps }; ///////////////////////////////////////////////////////////////////// diff --git a/js/src/vm/UnboxedObject.h b/js/src/vm/UnboxedObject.h index 00aab20dfbf4..6f86c0912503 100644 --- a/js/src/vm/UnboxedObject.h +++ b/js/src/vm/UnboxedObject.h @@ -238,7 +238,6 @@ class UnboxedPlainObject : public JSObject uint8_t data_[1]; public: - static const ObjectOps objectOps_; static const Class class_; static bool obj_lookupProperty(JSContext* cx, HandleObject obj, @@ -375,7 +374,6 @@ class UnboxedArrayObject : public JSObject static uint32_t exactCapacityIndex(uint32_t capacity); public: - static const ObjectOps objectOps_; static const Class class_; static bool obj_lookupProperty(JSContext* cx, HandleObject obj, diff --git a/js/xpconnect/idl/nsIScriptError.idl b/js/xpconnect/idl/nsIScriptError.idl index 8968a8e19de7..468ca682fa75 100644 --- a/js/xpconnect/idl/nsIScriptError.idl +++ b/js/xpconnect/idl/nsIScriptError.idl @@ -15,7 +15,7 @@ #include "nsStringGlue.h" // for nsDependentCString %} -[scriptable, uuid(18bdefde-e57b-11e4-832a-000c29a57fff)] +[scriptable, uuid(361be358-76f0-47aa-b37b-6ad833599e8d)] interface nsIScriptError : nsIConsoleMessage { /** pseudo-flag for default case */ @@ -68,6 +68,13 @@ interface nsIScriptError : nsIConsoleMessage attribute jsval stack; + /** + * The name of a template string, as found in js.msg, associated with the + * error message. + */ + attribute AString errorMessageName; + + void init(in AString message, in AString sourceName, in AString sourceLine, diff --git a/js/xpconnect/src/nsScriptError.cpp b/js/xpconnect/src/nsScriptError.cpp index fad6fc091d59..d8ba08ac5158 100644 --- a/js/xpconnect/src/nsScriptError.cpp +++ b/js/xpconnect/src/nsScriptError.cpp @@ -21,6 +21,7 @@ nsScriptErrorBase::nsScriptErrorBase() : mMessage(), + mMessageName(), mSourceName(), mLineNumber(0), mSourceLine(), @@ -152,6 +153,18 @@ nsScriptErrorBase::SetStack(JS::HandleValue aStack) { return NS_OK; } +NS_IMETHODIMP +nsScriptErrorBase::GetErrorMessageName(nsAString& aErrorMessageName) { + aErrorMessageName = mMessageName; + return NS_OK; +} + +NS_IMETHODIMP +nsScriptErrorBase::SetErrorMessageName(const nsAString& aErrorMessageName) { + mMessageName = aErrorMessageName; + return NS_OK; +} + NS_IMETHODIMP nsScriptErrorBase::Init(const nsAString& message, const nsAString& sourceName, diff --git a/js/xpconnect/src/nsXPConnect.cpp b/js/xpconnect/src/nsXPConnect.cpp index 52faf1ea58f4..8c96607921fa 100644 --- a/js/xpconnect/src/nsXPConnect.cpp +++ b/js/xpconnect/src/nsXPConnect.cpp @@ -31,6 +31,8 @@ #include "nsIObjectOutputStream.h" #include "nsScriptSecurityManager.h" +#include "jsfriendapi.h" + using namespace mozilla; using namespace mozilla::dom; using namespace xpc; @@ -178,7 +180,6 @@ xpc::ErrorReport::Init(JSErrorReport* aReport, const char* aFallbackMessage, mWindowID = aWindowID; ErrorReportToMessageString(aReport, mErrorMsg); - if (mErrorMsg.IsEmpty() && aFallbackMessage) { mErrorMsg.AssignWithConversion(aFallbackMessage); } @@ -190,6 +191,13 @@ xpc::ErrorReport::Init(JSErrorReport* aReport, const char* aFallbackMessage, } mSourceLine.Assign(aReport->linebuf(), aReport->linebufLength()); + const JSErrorFormatString* efs = js::GetErrorMessage(nullptr, aReport->errorNumber); + + if (efs == nullptr) { + mErrorMsgName.AssignASCII(""); + } else { + mErrorMsgName.AssignASCII(efs->name); + } mLineNumber = aReport->lineno; mColumn = aReport->column; @@ -248,6 +256,7 @@ xpc::ErrorReport::LogToConsoleWithStack(JS::HandleObject aStack) } else { errorObject = new nsScriptError(); } + errorObject->SetErrorMessageName(mErrorMsgName); NS_ENSURE_TRUE_VOID(consoleService && errorObject); nsresult rv = errorObject->InitWithWindowID(mErrorMsg, mFileName, mSourceLine, diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index 87dbf22a32c6..06543787a00e 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -2943,6 +2943,7 @@ protected: InitializeOnMainThread(); nsString mMessage; + nsString mMessageName; nsString mSourceName; uint32_t mLineNumber; nsString mSourceLine; diff --git a/js/xpconnect/src/xpcpublic.h b/js/xpconnect/src/xpcpublic.h index a2c0e363314c..fd8d37ce326c 100644 --- a/js/xpconnect/src/xpcpublic.h +++ b/js/xpconnect/src/xpcpublic.h @@ -520,6 +520,7 @@ class ErrorReport { public: nsCString mCategory; + nsString mErrorMsgName; nsString mErrorMsg; nsString mFileName; nsString mSourceLine; diff --git a/js/xpconnect/wrappers/XrayWrapper.cpp b/js/xpconnect/wrappers/XrayWrapper.cpp index 2543f9411349..149d513fcd7f 100644 --- a/js/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/xpconnect/wrappers/XrayWrapper.cpp @@ -573,11 +573,11 @@ JSXrayTraits::resolveOwnProperty(JSContext* cx, const Wrapper& jsWrapper, if (ShouldResolveStaticProperties(standardConstructor)) { const js::Class* clasp = js::ProtoKeyToClass(standardConstructor); - MOZ_ASSERT(clasp->spec.defined()); + MOZ_ASSERT(clasp->specDefined()); if (!TryResolvePropertyFromSpecs(cx, id, holder, - clasp->spec.constructorFunctions(), - clasp->spec.constructorProperties(), desc)) { + clasp->specConstructorFunctions(), + clasp->specConstructorProperties(), desc)) { return false; } @@ -656,13 +656,13 @@ JSXrayTraits::resolveOwnProperty(JSContext* cx, const Wrapper& jsWrapper, // Grab the JSClass. We require all Xrayable classes to have a ClassSpec. const js::Class* clasp = js::GetObjectClass(target); - MOZ_ASSERT(clasp->spec.defined()); + MOZ_ASSERT(clasp->specDefined()); // Indexed array properties are handled above, so we can just work with the // class spec here. if (!TryResolvePropertyFromSpecs(cx, id, holder, - clasp->spec.prototypeFunctions(), - clasp->spec.prototypeProperties(), + clasp->specPrototypeFunctions(), + clasp->specPrototypeProperties(), desc)) { return false; } @@ -864,11 +864,11 @@ JSXrayTraits::enumerateNames(JSContext* cx, HandleObject wrapper, unsigned flags if (ShouldResolveStaticProperties(standardConstructor)) { const js::Class* clasp = js::ProtoKeyToClass(standardConstructor); - MOZ_ASSERT(clasp->spec.defined()); + MOZ_ASSERT(clasp->specDefined()); if (!AppendNamesFromFunctionAndPropertySpecs( - cx, clasp->spec.constructorFunctions(), - clasp->spec.constructorProperties(), flags, props)) { + cx, clasp->specConstructorFunctions(), + clasp->specConstructorProperties(), flags, props)) { return false; } } @@ -905,11 +905,11 @@ JSXrayTraits::enumerateNames(JSContext* cx, HandleObject wrapper, unsigned flags // Grab the JSClass. We require all Xrayable classes to have a ClassSpec. const js::Class* clasp = js::GetObjectClass(target); - MOZ_ASSERT(clasp->spec.defined()); + MOZ_ASSERT(clasp->specDefined()); return AppendNamesFromFunctionAndPropertySpecs( - cx, clasp->spec.prototypeFunctions(), - clasp->spec.prototypeProperties(), flags, props); + cx, clasp->specPrototypeFunctions(), + clasp->specPrototypeProperties(), flags, props); } bool diff --git a/layout/base/AccessibleCaretEventHub.cpp b/layout/base/AccessibleCaretEventHub.cpp index 5044b6d5a5a9..4d08bb9e22d3 100644 --- a/layout/base/AccessibleCaretEventHub.cpp +++ b/layout/base/AccessibleCaretEventHub.cpp @@ -545,7 +545,7 @@ AccessibleCaretEventHub::HandleMouseEvent(WidgetMouseEvent* aEvent) nsEventStatus AccessibleCaretEventHub::HandleTouchEvent(WidgetTouchEvent* aEvent) { - if (aEvent->touches.IsEmpty()) { + if (aEvent->mTouches.IsEmpty()) { AC_LOG("%s: Receive a touch event without any touch data!", __FUNCTION__); return nsEventStatus_eIgnore; } @@ -553,7 +553,7 @@ AccessibleCaretEventHub::HandleTouchEvent(WidgetTouchEvent* aEvent) nsEventStatus rv = nsEventStatus_eIgnore; int32_t id = - (mActiveTouchId == kInvalidTouchId ? aEvent->touches[0]->Identifier() + (mActiveTouchId == kInvalidTouchId ? aEvent->mTouches[0]->Identifier() : mActiveTouchId); nsPoint point = GetTouchEventPosition(aEvent, id); @@ -781,7 +781,7 @@ nsPoint AccessibleCaretEventHub::GetTouchEventPosition(WidgetTouchEvent* aEvent, int32_t aIdentifier) const { - for (dom::Touch* touch : aEvent->touches) { + for (dom::Touch* touch : aEvent->mTouches) { if (touch->Identifier() == aIdentifier) { LayoutDeviceIntPoint touchIntPoint = touch->mRefPoint; diff --git a/layout/base/TouchManager.cpp b/layout/base/TouchManager.cpp index aaf67d9b41d3..821dc6428ad7 100644 --- a/layout/base/TouchManager.cpp +++ b/layout/base/TouchManager.cpp @@ -67,7 +67,7 @@ EvictTouchPoint(RefPtr& aTouch, WidgetTouchEvent event(true, eTouchEnd, widget); event.widget = widget; event.mTime = PR_IntervalNow(); - event.touches.AppendElement(aTouch); + event.mTouches.AppendElement(aTouch); nsEventStatus status; widget->DispatchEvent(&event, status); return; @@ -118,7 +118,7 @@ TouchManager::PreHandleEvent(WidgetEvent* aEvent, // if there is only one touch in this touchstart event, assume that it is // the start of a new touch session and evict any old touches in the // queue - if (touchEvent->touches.Length() == 1) { + if (touchEvent->mTouches.Length() == 1) { WidgetTouchEvent::AutoTouchArray touches; AppendToTouchList(&touches); for (uint32_t i = 0; i < touches.Length(); ++i) { @@ -126,8 +126,8 @@ TouchManager::PreHandleEvent(WidgetEvent* aEvent, } } // Add any new touches to the queue - for (uint32_t i = 0; i < touchEvent->touches.Length(); ++i) { - dom::Touch* touch = touchEvent->touches[i]; + for (uint32_t i = 0; i < touchEvent->mTouches.Length(); ++i) { + dom::Touch* touch = touchEvent->mTouches[i]; int32_t id = touch->Identifier(); if (!gCaptureTouchList->Get(id, nullptr)) { // If it is not already in the queue, it is a new touch @@ -141,7 +141,7 @@ TouchManager::PreHandleEvent(WidgetEvent* aEvent, case eTouchMove: { // Check for touches that changed. Mark them add to queue WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent(); - WidgetTouchEvent::TouchArray& touches = touchEvent->touches; + WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches; bool haveChanged = false; for (int32_t i = touches.Length(); i; ) { --i; @@ -185,9 +185,9 @@ TouchManager::PreHandleEvent(WidgetEvent* aEvent, // arbitrarily pick the first touch point to be the "changed" // touch because firing an event with no changed events doesn't // work. - for (uint32_t i = 0; i < touchEvent->touches.Length(); ++i) { - if (touchEvent->touches[i]) { - touchEvent->touches[i]->mChanged = true; + for (uint32_t i = 0; i < touchEvent->mTouches.Length(); ++i) { + if (touchEvent->mTouches[i]) { + touchEvent->mTouches[i]->mChanged = true; break; } } @@ -205,7 +205,7 @@ TouchManager::PreHandleEvent(WidgetEvent* aEvent, // Remove the changed touches // need to make sure we only remove touches that are ending here WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent(); - WidgetTouchEvent::TouchArray& touches = touchEvent->touches; + WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches; for (uint32_t i = 0; i < touches.Length(); ++i) { dom::Touch* touch = touches[i]; if (!touch) { diff --git a/layout/base/gtest/TestAccessibleCaretEventHub.cpp b/layout/base/gtest/TestAccessibleCaretEventHub.cpp index 7e447992fb35..14ca675aaf82 100644 --- a/layout/base/gtest/TestAccessibleCaretEventHub.cpp +++ b/layout/base/gtest/TestAccessibleCaretEventHub.cpp @@ -74,7 +74,7 @@ public: int32_t aIdentifier) const override { // Return the device point directly. - LayoutDeviceIntPoint touchIntPoint = aEvent->touches[0]->mRefPoint; + LayoutDeviceIntPoint touchIntPoint = aEvent->mTouches[0]->mRefPoint; return nsPoint(touchIntPoint.x, touchIntPoint.y); } @@ -163,7 +163,7 @@ public: RefPtr touch( new dom::Touch(identifier, point, radius, rotationAngle, force)); - event->touches.AppendElement(touch); + event->mTouches.AppendElement(touch); return Move(event); } diff --git a/layout/base/nsCSSRendering.cpp b/layout/base/nsCSSRendering.cpp index 0e7fec1ba679..d90569d90806 100644 --- a/layout/base/nsCSSRendering.cpp +++ b/layout/base/nsCSSRendering.cpp @@ -1985,7 +1985,10 @@ nsCSSRendering::DetermineBackgroundColor(nsPresContext* aPresContext, aDrawBackgroundImage = true; aDrawBackgroundColor = true; - if (aFrame->HonorPrintBackgroundSettings()) { + const nsStyleVisibility* visibility = aStyleContext->StyleVisibility(); + + if (visibility->mColorAdjust != NS_STYLE_COLOR_ADJUST_EXACT && + aFrame->HonorPrintBackgroundSettings()) { aDrawBackgroundImage = aPresContext->GetBackgroundImageDraw(); aDrawBackgroundColor = aPresContext->GetBackgroundColorDraw(); } diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 0b891f47d36b..86444efb3499 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -1060,6 +1060,16 @@ GetDisplayPortFromMarginsData(nsIContent* aContent, return result; } +static bool +ShouldDisableApzForElement(nsIContent* aContent) +{ + if (gfxPrefs::APZDisableForScrollLinkedEffects() && aContent) { + nsIDocument* doc = aContent->GetComposedDoc(); + return (doc && doc->HasScrollLinkedEffect()); + } + return false; +} + static bool GetDisplayPortImpl(nsIContent* aContent, nsRect *aResult, float aMultiplier) { @@ -1094,7 +1104,7 @@ GetDisplayPortImpl(nsIContent* aContent, nsRect *aResult, float aMultiplier) nsRect result; if (rectData) { result = GetDisplayPortFromRectData(aContent, rectData, aMultiplier); - } else if (APZCCallbackHelper::IsDisplayportSuppressed()) { + } else if (APZCCallbackHelper::IsDisplayportSuppressed() || ShouldDisableApzForElement(aContent)) { DisplayPortMarginsPropertyData noMargins(ScreenMargin(), 1); result = GetDisplayPortFromMarginsData(aContent, &noMargins, aMultiplier); } else { @@ -8801,6 +8811,10 @@ nsLayoutUtils::ComputeScrollMetadata(nsIFrame* aForFrame, } } + if (ShouldDisableApzForElement(aContent)) { + metrics.SetForceDisableApz(true); + } + return metadata; } diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 68af917664b4..a43efac30b00 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -6891,8 +6891,8 @@ DispatchPointerFromMouseOrTouch(PresShell* aShell, return NS_OK; } - for (uint32_t i = 0; i < touchEvent->touches.Length(); ++i) { - mozilla::dom::Touch* touch = touchEvent->touches[i]; + for (uint32_t i = 0; i < touchEvent->mTouches.Length(); ++i) { + mozilla::dom::Touch* touch = touchEvent->mTouches[i]; if (!touch || !touch->convertToPointer) { continue; } @@ -7544,7 +7544,8 @@ PresShell::HandleEvent(nsIFrame* aFrame, // in the same document by taking the target of the events already in // the capture list nsCOMPtr anyTarget; - if (TouchManager::gCaptureTouchList->Count() > 0 && touchEvent->touches.Length() > 1) { + if (TouchManager::gCaptureTouchList->Count() > 0 && + touchEvent->mTouches.Length() > 1) { for (auto iter = TouchManager::gCaptureTouchList->Iter(); !iter.Done(); iter.Next()) { @@ -7559,9 +7560,9 @@ PresShell::HandleEvent(nsIFrame* aFrame, } } - for (int32_t i = touchEvent->touches.Length(); i; ) { + for (int32_t i = touchEvent->mTouches.Length(); i; ) { --i; - dom::Touch* touch = touchEvent->touches[i]; + dom::Touch* touch = touchEvent->mTouches[i]; int32_t id = touch->Identifier(); if (!TouchManager::gCaptureTouchList->Get(id, nullptr)) { @@ -7595,10 +7596,10 @@ PresShell::HandleEvent(nsIFrame* aFrame, // if we couldn't find a target frame in the same document as // anyTarget, remove the touch from the capture touch list, as - // well as the event->touches array. touchmove events that aren't + // well as the event->mTouches array. touchmove events that aren't // in the captured touch list will be discarded if (!newTargetFrame) { - touchEvent->touches.RemoveElementAt(i); + touchEvent->mTouches.RemoveElementAt(i); } else { target = newTargetFrame; nsCOMPtr targetContent; @@ -7728,7 +7729,7 @@ PresShell::HandleEvent(nsIFrame* aFrame, case eTouchEnd: { // get the correct shell to dispatch to WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent(); - for (dom::Touch* touch : touchEvent->touches) { + for (dom::Touch* touch : touchEvent->mTouches) { if (!touch) { break; } @@ -8341,7 +8342,7 @@ PresShell::DispatchTouchEventToDOM(WidgetEvent* aEvent, WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent(); // loop over all touches and dispatch events on any that have changed - for (dom::Touch* touch : touchEvent->touches) { + for (dom::Touch* touch : touchEvent->mTouches) { if (!touch || !touch->mChanged) { continue; } diff --git a/layout/forms/nsRangeFrame.cpp b/layout/forms/nsRangeFrame.cpp index df00b48e5ba1..d9949473f818 100644 --- a/layout/forms/nsRangeFrame.cpp +++ b/layout/forms/nsRangeFrame.cpp @@ -535,9 +535,9 @@ nsRangeFrame::GetValueAtEventPoint(WidgetGUIEvent* aEvent) LayoutDeviceIntPoint absPoint; if (aEvent->mClass == eTouchEventClass) { - MOZ_ASSERT(aEvent->AsTouchEvent()->touches.Length() == 1, - "Unexpected number of touches"); - absPoint = aEvent->AsTouchEvent()->touches[0]->mRefPoint; + MOZ_ASSERT(aEvent->AsTouchEvent()->mTouches.Length() == 1, + "Unexpected number of mTouches"); + absPoint = aEvent->AsTouchEvent()->mTouches[0]->mRefPoint; } else { absPoint = aEvent->refPoint; } diff --git a/layout/reftests/async-scrolling/disable-apz-for-sle-pages-ref.html b/layout/reftests/async-scrolling/disable-apz-for-sle-pages-ref.html new file mode 100644 index 000000000000..8e603fbc5ab8 --- /dev/null +++ b/layout/reftests/async-scrolling/disable-apz-for-sle-pages-ref.html @@ -0,0 +1,19 @@ + + + + Check that the apz.disable_for_sle_pages pref behaves as expected + + + +
+ + diff --git a/layout/reftests/async-scrolling/disable-apz-for-sle-pages.html b/layout/reftests/async-scrolling/disable-apz-for-sle-pages.html new file mode 100644 index 000000000000..5c091d836ad3 --- /dev/null +++ b/layout/reftests/async-scrolling/disable-apz-for-sle-pages.html @@ -0,0 +1,32 @@ + + + + Check that the apz.disable_for_sle_pages pref behaves as expected + + + +
+ + diff --git a/layout/reftests/async-scrolling/reftest.list b/layout/reftests/async-scrolling/reftest.list index 1901ad04cf37..c0449cc77aa4 100644 --- a/layout/reftests/async-scrolling/reftest.list +++ b/layout/reftests/async-scrolling/reftest.list @@ -39,6 +39,7 @@ fuzzy-if(Android,6,4) skip-if(!asyncPan) == offscreen-clipped-blendmode-4.html o fuzzy-if(Android,7,4) skip-if(!asyncPan) == perspective-scrolling-1.html perspective-scrolling-1-ref.html fuzzy-if(Android,7,4) skip-if(!asyncPan) == perspective-scrolling-2.html perspective-scrolling-2-ref.html fuzzy-if(Android,7,4) skip-if(!asyncPan) == perspective-scrolling-3.html perspective-scrolling-3-ref.html +pref(apz.disable_for_scroll_linked_effects,true) skip-if(!asyncPan) == disable-apz-for-sle-pages.html disable-apz-for-sle-pages-ref.html # for the following tests, we want to disable the low-precision buffer # as it will expand the displayport beyond what the test specifies in diff --git a/layout/style/nsCSSKeywordList.h b/layout/style/nsCSSKeywordList.h index f09b0b987a13..b99c8d28412b 100644 --- a/layout/style/nsCSSKeywordList.h +++ b/layout/style/nsCSSKeywordList.h @@ -248,6 +248,7 @@ CSS_KEY(ease, ease) CSS_KEY(ease-in, ease_in) CSS_KEY(ease-in-out, ease_in_out) CSS_KEY(ease-out, ease_out) +CSS_KEY(economy, economy) CSS_KEY(element, element) CSS_KEY(elements, elements) CSS_KEY(ellipse, ellipse) @@ -258,6 +259,7 @@ CSS_KEY(enabled, enabled) CSS_KEY(end, end) CSS_KEY(ethiopic-numeric, ethiopic_numeric) CSS_KEY(ex, ex) +CSS_KEY(exact, exact) CSS_KEY(exclude, exclude) CSS_KEY(exclusion, exclusion) CSS_KEY(expanded, expanded) diff --git a/layout/style/nsCSSPropList.h b/layout/style/nsCSSPropList.h index a53a38c7e14d..c0253df47615 100644 --- a/layout/style/nsCSSPropList.h +++ b/layout/style/nsCSSPropList.h @@ -1388,6 +1388,16 @@ CSS_PROP_COLOR( nullptr, offsetof(nsStyleColor, mColor), eStyleAnimType_Color) +CSS_PROP_VISIBILITY( + color-adjust, + color_adjust, + ColorAdjust, + CSS_PROPERTY_PARSE_VALUE, + "layout.css.color-adjust.enabled", + VARIANT_HK, + kColorAdjustKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) CSS_PROP_SHORTHAND( -moz-columns, _moz_columns, diff --git a/layout/style/nsCSSProps.cpp b/layout/style/nsCSSProps.cpp index 012171eeec26..e52e897933ad 100644 --- a/layout/style/nsCSSProps.cpp +++ b/layout/style/nsCSSProps.cpp @@ -2366,6 +2366,12 @@ const KTableEntry nsCSSProps::kVectorEffectKTable[] = { { eCSSKeyword_UNKNOWN, -1 } }; +const KTableEntry nsCSSProps::kColorAdjustKTable[] = { + { eCSSKeyword_economy, NS_STYLE_COLOR_ADJUST_ECONOMY }, + { eCSSKeyword_exact, NS_STYLE_COLOR_ADJUST_EXACT }, + { eCSSKeyword_UNKNOWN, -1 } +}; + const KTableEntry nsCSSProps::kColorInterpolationKTable[] = { { eCSSKeyword_auto, NS_STYLE_COLOR_INTERPOLATION_AUTO }, { eCSSKeyword_srgb, NS_STYLE_COLOR_INTERPOLATION_SRGB }, diff --git a/layout/style/nsCSSProps.h b/layout/style/nsCSSProps.h index af096af57367..7f22b1a90dd0 100644 --- a/layout/style/nsCSSProps.h +++ b/layout/style/nsCSSProps.h @@ -707,6 +707,7 @@ public: static const KTableEntry kVectorEffectKTable[]; static const KTableEntry kTextAnchorKTable[]; static const KTableEntry kTextRenderingKTable[]; + static const KTableEntry kColorAdjustKTable[]; static const KTableEntry kColorInterpolationKTable[]; static const KTableEntry kColumnFillKTable[]; static const KTableEntry kBoxPropSourceKTable[]; diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp index 28265802a805..ffcf9a7626c5 100644 --- a/layout/style/nsComputedDOMStyle.cpp +++ b/layout/style/nsComputedDOMStyle.cpp @@ -979,6 +979,16 @@ nsComputedDOMStyle::DoGetColor() return val.forget(); } +already_AddRefed +nsComputedDOMStyle::DoGetColorAdjust() +{ + RefPtr val = new nsROCSSPrimitiveValue; + val->SetIdent( + nsCSSProps::ValueToKeywordEnum(StyleVisibility()->mColorAdjust, + nsCSSProps::kColorAdjustKTable)); + return val.forget(); +} + already_AddRefed nsComputedDOMStyle::DoGetOpacity() { diff --git a/layout/style/nsComputedDOMStyle.h b/layout/style/nsComputedDOMStyle.h index dbd1a89a1043..f12bfc528079 100644 --- a/layout/style/nsComputedDOMStyle.h +++ b/layout/style/nsComputedDOMStyle.h @@ -428,6 +428,7 @@ private: already_AddRefed DoGetWebkitTextFillColor(); /* Visibility properties */ + already_AddRefed DoGetColorAdjust(); already_AddRefed DoGetOpacity(); already_AddRefed DoGetPointerEvents(); already_AddRefed DoGetVisibility(); diff --git a/layout/style/nsComputedDOMStylePropertyList.h b/layout/style/nsComputedDOMStylePropertyList.h index bb9773bcf449..e2b8b73ffa5b 100644 --- a/layout/style/nsComputedDOMStylePropertyList.h +++ b/layout/style/nsComputedDOMStylePropertyList.h @@ -102,6 +102,7 @@ COMPUTED_STYLE_PROP(caption_side, CaptionSide) COMPUTED_STYLE_PROP(clear, Clear) COMPUTED_STYLE_PROP(clip, Clip) COMPUTED_STYLE_PROP(color, Color) +COMPUTED_STYLE_PROP(color_adjust, ColorAdjust) COMPUTED_STYLE_PROP(contain, Contain) COMPUTED_STYLE_PROP(content, Content) COMPUTED_STYLE_PROP(counter_increment, CounterIncrement) diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp index 2f7131a80950..71b933e04289 100644 --- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -6461,6 +6461,12 @@ nsRuleNode::ComputeVisibilityData(void* aStartStruct, MOZ_ASSERT(orientation->GetUnit() == eCSSUnit_Null, "Should be null unit"); } + SetDiscrete(*aRuleData->ValueForColorAdjust(), visibility->mColorAdjust, + conditions, + SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, + parentVisibility->mColorAdjust, + NS_STYLE_COLOR_ADJUST_ECONOMY, 0, 0, 0, 0); + COMPUTE_END_INHERITED(Visibility, visibility) } diff --git a/layout/style/nsStyleConsts.h b/layout/style/nsStyleConsts.h index fab8bc9d3607..c684007b9bd9 100644 --- a/layout/style/nsStyleConsts.h +++ b/layout/style/nsStyleConsts.h @@ -1121,6 +1121,10 @@ enum class FillMode : uint32_t; #define NS_STYLE_TEXT_RENDERING_OPTIMIZELEGIBILITY 2 #define NS_STYLE_TEXT_RENDERING_GEOMETRICPRECISION 3 +// adjust-color +#define NS_STYLE_COLOR_ADJUST_ECONOMY 0 +#define NS_STYLE_COLOR_ADJUST_EXACT 1 + // color-interpolation and color-interpolation-filters #define NS_STYLE_COLOR_INTERPOLATION_AUTO 0 #define NS_STYLE_COLOR_INTERPOLATION_SRGB 1 diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index c23700ed0d12..d106f915340e 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -3172,6 +3172,7 @@ nsStyleVisibility::nsStyleVisibility(nsPresContext* aPresContext) mPointerEvents = NS_STYLE_POINTER_EVENTS_AUTO; mWritingMode = NS_STYLE_WRITING_MODE_HORIZONTAL_TB; mTextOrientation = NS_STYLE_TEXT_ORIENTATION_MIXED; + mColorAdjust = NS_STYLE_COLOR_ADJUST_ECONOMY; } nsStyleVisibility::nsStyleVisibility(const nsStyleVisibility& aSource) @@ -3183,6 +3184,7 @@ nsStyleVisibility::nsStyleVisibility(const nsStyleVisibility& aSource) mPointerEvents = aSource.mPointerEvents; mWritingMode = aSource.mWritingMode; mTextOrientation = aSource.mTextOrientation; + mColorAdjust = aSource.mColorAdjust; } nsChangeHint nsStyleVisibility::CalcDifference(const nsStyleVisibility& aOther) const @@ -3217,6 +3219,10 @@ nsChangeHint nsStyleVisibility::CalcDifference(const nsStyleVisibility& aOther) NS_UpdateHint(hint, nsChangeHint_NeedReflow); NS_UpdateHint(hint, nsChangeHint_NeedDirtyReflow); // XXX remove me: bug 876085 } + if (mColorAdjust != aOther.mColorAdjust) { + // color-adjust only affects media where dynamic changes can't happen. + NS_UpdateHint(hint, nsChangeHint_NeutralChange); + } } return hint; } diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index bc545aa34102..49349db3e127 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -2109,7 +2109,8 @@ struct nsStyleVisibility nsChangeHint CalcDifference(const nsStyleVisibility& aOther) const; static nsChangeHint MaxDifference() { - return NS_STYLE_HINT_FRAMECHANGE; + return NS_STYLE_HINT_FRAMECHANGE | + nsChangeHint_NeutralChange; } static nsChangeHint DifferenceAlwaysHandledForDescendants() { // CalcDifference never returns the reflow hints that are sometimes @@ -2125,6 +2126,7 @@ struct nsStyleVisibility uint8_t mPointerEvents; // [inherited] see nsStyleConsts.h uint8_t mWritingMode; // [inherited] see nsStyleConsts.h uint8_t mTextOrientation; // [inherited] see nsStyleConsts.h + uint8_t mColorAdjust; // [inherited] see nsStyleConsts.h bool IsVisible() const { return (mVisible == NS_STYLE_VISIBILITY_VISIBLE); diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js index e1ad7078b0ba..1909997b086c 100644 --- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -1368,6 +1368,14 @@ var gCSSProperties = { alias_for: "box-sizing", subproperties: [ "box-sizing" ], }, + "color-adjust": { + domProp: "colorAdjust", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "economy" ], + other_values: [ "exact" ], + invalid_values: [] + }, "-moz-columns": { domProp: "MozColumns", inherited: false, diff --git a/layout/xul/nsBoxFrame.cpp b/layout/xul/nsBoxFrame.cpp index 1c4883824540..63c3ad197bda 100644 --- a/layout/xul/nsBoxFrame.cpp +++ b/layout/xul/nsBoxFrame.cpp @@ -2093,11 +2093,11 @@ nsBoxFrame::GetEventPoint(WidgetGUIEvent* aEvent, LayoutDeviceIntPoint& aPoint) if (touchEvent) { // return false if there is more than one touch on the page, or if // we can't find a touch point - if (touchEvent->touches.Length() != 1) { + if (touchEvent->mTouches.Length() != 1) { return false; } - dom::Touch* touch = touchEvent->touches.SafeElementAt(0); + dom::Touch* touch = touchEvent->mTouches.SafeElementAt(0); if (!touch) { return false; } diff --git a/mfbt/ChaosMode.h b/mfbt/ChaosMode.h index b5a0b65d0aaf..94833c398078 100644 --- a/mfbt/ChaosMode.h +++ b/mfbt/ChaosMode.h @@ -27,6 +27,8 @@ enum ChaosFeature { IOAmounts = 0x8, // Iterate over hash tables in random order. HashTableIteration = 0x10, + // Randomly refuse to use cached version of image (when allowed by spec). + ImageCache = 0x20, Any = 0xffffffff, }; diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index e472cff1bac0..7d78e7dbb993 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -567,6 +567,7 @@ pref("apz.content_response_timeout", 300); pref("apz.drag.enabled", false); pref("apz.danger_zone_x", 50); pref("apz.danger_zone_y", 100); +pref("apz.disable_for_scroll_linked_effects", false); pref("apz.displayport_expiry_ms", 15000); pref("apz.enlarge_displayport_when_clipped", false); pref("apz.fling_accel_base_mult", "1.0"); @@ -5267,3 +5268,6 @@ pref("plugins.rewrite_youtube_embeds", true); // Disable browser frames by default pref("dom.mozBrowserFramesEnabled", false); + +// Is support for 'color-adjust' CSS property enabled? +pref("layout.css.color-adjust.enabled", true); diff --git a/testing/firefox-ui/mach_commands.py b/testing/firefox-ui/mach_commands.py index d16e35296249..bfa31ddd0e53 100644 --- a/testing/firefox-ui/mach_commands.py +++ b/testing/firefox-ui/mach_commands.py @@ -29,11 +29,14 @@ def setup_argument_parser_update(): def run_firefox_ui_test(testtype=None, topsrcdir=None, **kwargs): - import argparse - from mozlog.structured import commandline import firefox_ui_harness + if testtype == 'functional': + parser = setup_argument_parser_functional() + else: + parser = setup_argument_parser_update() + test_types = { 'functional': { 'default_tests': [ @@ -57,14 +60,24 @@ def run_firefox_ui_test(testtype=None, topsrcdir=None, **kwargs): kwargs['server_root'] = os.path.join(fxui_dir, 'resources') # If no tests have been selected, set default ones - if not kwargs.get('tests'): - kwargs['tests'] = [os.path.join(fxui_dir, 'tests', test) + if kwargs.get('tests'): + tests = kwargs.get('tests') + else: + tests = [os.path.join(fxui_dir, 'tests', test) for test in test_types[testtype]['default_tests']] kwargs['logger'] = commandline.setup_logging('Firefox UI - {} Tests'.format(testtype), {"mach": sys.stdout}) - failed = test_types[testtype]['cli_module'].cli(args=kwargs) + args = parser.parse_args(args=tests) + + for k, v in kwargs.iteritems(): + setattr(args, k, v) + + parser.verify_usage(args) + + failed = test_types[testtype]['cli_module'].cli(args=vars(args)) + if failed > 0: return 1 else: diff --git a/testing/mochitest/tests/SimpleTest/EventUtils.js b/testing/mochitest/tests/SimpleTest/EventUtils.js index 81128ccf85c8..2aa549826602 100644 --- a/testing/mochitest/tests/SimpleTest/EventUtils.js +++ b/testing/mochitest/tests/SimpleTest/EventUtils.js @@ -577,7 +577,7 @@ function sendWheelAndPaint(aTarget, aOffsetX, aOffsetY, aEvent, aCallback, aWind } var onwheel = function() { - window.removeEventListener("wheel", onwheel); + SpecialPowers.removeSystemEventListener(window, "wheel", onwheel); // Wait one frame since the wheel event has not caused a refresh observer // to be added yet. @@ -604,7 +604,9 @@ function sendWheelAndPaint(aTarget, aOffsetX, aOffsetY, aEvent, aCallback, aWind }, 0); }; - aWindow.addEventListener("wheel", onwheel); + // Listen for the system wheel event, because it happens after all of + // the other wheel events, including legacy events. + SpecialPowers.addSystemEventListener(aWindow, "wheel", onwheel); synthesizeWheel(aTarget, aOffsetX, aOffsetY, aEvent, aWindow); } diff --git a/testing/web-platform/meta/XMLHttpRequest/open-url-bogus.htm.ini b/testing/web-platform/meta/XMLHttpRequest/open-url-bogus.htm.ini index 254cd0bc82b3..5ec4b29710a8 100644 --- a/testing/web-platform/meta/XMLHttpRequest/open-url-bogus.htm.ini +++ b/testing/web-platform/meta/XMLHttpRequest/open-url-bogus.htm.ini @@ -3,6 +3,3 @@ [XMLHttpRequest: open() - bogus URLs (http:)] expected: FAIL - [XMLHttpRequest: open() - bogus URLs (http://a a/)] - expected: FAIL - diff --git a/widget/InputData.cpp b/widget/InputData.cpp index 6243d77c8659..4528a832e502 100644 --- a/widget/InputData.cpp +++ b/widget/InputData.cpp @@ -194,8 +194,8 @@ MultiTouchInput::MultiTouchInput(const WidgetTouchEvent& aTouchEvent) break; } - for (size_t i = 0; i < aTouchEvent.touches.Length(); i++) { - const Touch* domTouch = aTouchEvent.touches[i]; + for (size_t i = 0; i < aTouchEvent.mTouches.Length(); i++) { + const Touch* domTouch = aTouchEvent.mTouches[i]; // Extract data from weird interfaces. int32_t identifier = domTouch->Identifier(); @@ -251,7 +251,7 @@ MultiTouchInput::ToWidgetTouchEvent(nsIWidget* aWidget) const event.mFlags.mHandledByAPZ = mHandledByAPZ; for (size_t i = 0; i < mTouches.Length(); i++) { - *event.touches.AppendElement() = mTouches[i].ToNewDOMTouch(); + *event.mTouches.AppendElement() = mTouches[i].ToNewDOMTouch(); } return event; diff --git a/widget/TouchEvents.h b/widget/TouchEvents.h index 2332cd39c7ea..367baa293089 100644 --- a/widget/TouchEvents.h +++ b/widget/TouchEvents.h @@ -174,7 +174,7 @@ public: mModifiers = aOther.mModifiers; mTime = aOther.mTime; mTimeStamp = aOther.mTimeStamp; - touches.AppendElements(aOther.touches); + mTouches.AppendElements(aOther.mTouches); mFlags.mCancelable = mMessage != eTouchCancel; mFlags.mHandledByAPZ = aOther.mFlags.mHandledByAPZ; } @@ -202,15 +202,15 @@ public: return result; } - TouchArray touches; + TouchArray mTouches; void AssignTouchEventData(const WidgetTouchEvent& aEvent, bool aCopyTargets) { AssignInputEventData(aEvent, aCopyTargets); // Assign*EventData() assume that they're called only new instance. - MOZ_ASSERT(touches.IsEmpty()); - touches.AppendElements(aEvent.touches); + MOZ_ASSERT(mTouches.IsEmpty()); + mTouches.AppendElements(aEvent.mTouches); } }; diff --git a/widget/android/AndroidJavaWrappers.cpp b/widget/android/AndroidJavaWrappers.cpp index ebc6ad5ce42f..05c8932b01f8 100644 --- a/widget/android/AndroidJavaWrappers.cpp +++ b/widget/android/AndroidJavaWrappers.cpp @@ -611,7 +611,7 @@ AndroidGeckoEvent::MakeTouchEvent(nsIWidget* widget) event.mTime = Time(); const LayoutDeviceIntPoint& offset = widget->WidgetToScreenOffset(); - event.touches.SetCapacity(endIndex - startIndex); + event.mTouches.SetCapacity(endIndex - startIndex); for (int i = startIndex; i < endIndex; i++) { // In this code branch, we are dispatching this event directly // into Gecko (as opposed to going through the AsyncPanZoomController), @@ -629,7 +629,7 @@ AndroidGeckoEvent::MakeTouchEvent(nsIWidget* widget) radius, Orientations()[i], Pressures()[i]); - event.touches.AppendElement(t); + event.mTouches.AppendElement(t); } return event; diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp index 0c198f3f5364..fd215f61da61 100644 --- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -2007,14 +2007,14 @@ nsWindow::OnLongTapEvent(AndroidGeckoEvent *ae) void nsWindow::DispatchHitTest(const WidgetTouchEvent& aEvent) { - if (aEvent.mMessage == eTouchStart && aEvent.touches.Length() == 1) { + if (aEvent.mMessage == eTouchStart && aEvent.mTouches.Length() == 1) { // Since touch events don't get retargeted by PositionedEventTargeting.cpp // code on Fennec, we dispatch a dummy mouse event that *does* get // retargeted. The Fennec browser.js code can use this to activate the // highlight element in case the this touchstart is the start of a tap. WidgetMouseEvent hittest(true, eMouseHitTest, this, WidgetMouseEvent::eReal); - hittest.refPoint = aEvent.touches[0]->mRefPoint; + hittest.refPoint = aEvent.mTouches[0]->mRefPoint; hittest.ignoreRootScrollFrame = true; hittest.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; nsEventStatus status; diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp index b9ab6c1039cd..9f513e47d652 100644 --- a/widget/gtk/nsWindow.cpp +++ b/widget/gtk/nsWindow.cpp @@ -3446,11 +3446,11 @@ nsWindow::OnTouchEvent(GdkEventTouch* aEvent) mTouches.Put(aEvent->sequence, touch.forget()); // add all touch points to event object for (auto iter = mTouches.Iter(); !iter.Done(); iter.Next()) { - event.touches.AppendElement(new dom::Touch(*iter.UserData())); + event.mTouches.AppendElement(new dom::Touch(*iter.UserData())); } } else if (aEvent->type == GDK_TOUCH_END || aEvent->type == GDK_TOUCH_CANCEL) { - *event.touches.AppendElement() = touch.forget(); + *event.mTouches.AppendElement() = touch.forget(); } DispatchInputEvent(&event); diff --git a/widget/nsGUIEventIPC.h b/widget/nsGUIEventIPC.h index 9a6de2839a25..f69a2f48729f 100644 --- a/widget/nsGUIEventIPC.h +++ b/widget/nsGUIEventIPC.h @@ -303,8 +303,8 @@ struct ParamTraits { WriteParam(aMsg, static_cast(aParam)); // Sigh, Touch bites us again! We want to be able to do - // WriteParam(aMsg, aParam.touches); - const paramType::TouchArray& touches = aParam.touches; + // WriteParam(aMsg, aParam.mTouches); + const paramType::TouchArray& touches = aParam.mTouches; WriteParam(aMsg, touches.Length()); for (uint32_t i = 0; i < touches.Length(); ++i) { mozilla::dom::Touch* touch = touches[i]; @@ -337,7 +337,7 @@ struct ParamTraits !ReadParam(aMsg, aIter, &force)) { return false; } - aResult->touches.AppendElement( + aResult->mTouches.AppendElement( new mozilla::dom::Touch( identifier, refPoint, radius, rotationAngle, force)); } diff --git a/widget/uikit/nsWindow.mm b/widget/uikit/nsWindow.mm index 47bd35281e69..c7b521843107 100644 --- a/widget/uikit/nsWindow.mm +++ b/widget/uikit/nsWindow.mm @@ -170,7 +170,7 @@ private: //XXX: I think nativeEvent.timestamp * 1000 is probably usable here but // I don't care that much right now. event.mTime = PR_IntervalNow(); - event.touches.SetCapacity(aTouches.count); + event.mTouches.SetCapacity(aTouches.count); for (UITouch* touch in aTouches) { LayoutDeviceIntPoint loc = UIKitPointsToDevPixels([touch locationInView:self], [self contentScaleFactor]); LayoutDeviceIntPoint radius = UIKitPointsToDevPixels([touch majorRadius], [touch majorRadius]); @@ -183,7 +183,7 @@ private: int id = reinterpret_cast(value); RefPtr t = new Touch(id, loc, radius, 0.0f, 1.0f); event.refPoint = loc; - event.touches.AppendElement(t); + event.mTouches.AppendElement(t); } aWindow->DispatchInputEvent(&event); }