зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to fx-team. a=merge
This commit is contained in:
Коммит
531a5d3749
|
@ -165,7 +165,7 @@ nsCoreUtils::DispatchTouchEvent(EventMessage aMessage, int32_t aX, int32_t aY,
|
|||
RefPtr<dom::Touch> 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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
},
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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});
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -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<nsIPrincipal> appPrin = BasePrincipal::CreateCodebasePrincipal(appURI, attrs);
|
||||
NS_ENSURE_TRUE(appPrin, nsIPrincipal::APP_STATUS_NOT_INSTALLED);
|
||||
return aPrin->Equals(appPrin) ? status
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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']],
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,<script>${script}</script>`
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
|
|
|
@ -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];
|
|
@ -25,6 +25,7 @@ DevToolsModules(
|
|||
'director-manager.js',
|
||||
'director-registry.js',
|
||||
'environment.js',
|
||||
'errordocs.js',
|
||||
'eventlooplag.js',
|
||||
'frame.js',
|
||||
'framerate.js',
|
||||
|
|
|
@ -1392,6 +1392,7 @@ WebConsoleActor.prototype =
|
|||
|
||||
return {
|
||||
errorMessage: this._createStringGrip(aPageError.errorMessage),
|
||||
errorMessageName: aPageError.errorMessageName,
|
||||
sourceName: aPageError.sourceName,
|
||||
lineText: lineText,
|
||||
lineNumber: aPageError.lineNumber,
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -774,7 +774,11 @@ nsContentIterator::NextNode(nsINode* aNode, nsTArray<int32_t>* 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<int32_t>* 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;
|
||||
|
||||
|
|
|
@ -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<Touch> t =
|
||||
new Touch(aIdentifiers[i], pt, radius, aRotationAngles[i], aForces[i]);
|
||||
|
||||
event.touches.AppendElement(t);
|
||||
event.mTouches.AppendElement(t);
|
||||
}
|
||||
|
||||
nsEventStatus status;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
<title>Test for D3E WheelEvent</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -39,7 +40,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
@ -55,7 +56,12 @@ var gHorizontalLine = 0;
|
|||
var gPageHeight = 0;
|
||||
var gPageWidth = 0;
|
||||
|
||||
function prepareScrollUnits()
|
||||
function sendWheelAndWait(aX, aY, aEvent)
|
||||
{
|
||||
sendWheelAndPaint(gScrollableElement, aX, aY, aEvent, continueTest);
|
||||
}
|
||||
|
||||
function* prepareScrollUnits()
|
||||
{
|
||||
var result = -1;
|
||||
function handler(aEvent)
|
||||
|
@ -65,32 +71,32 @@ function prepareScrollUnits()
|
|||
}
|
||||
window.addEventListener("MozMousePixelScroll", handler, true);
|
||||
|
||||
synthesizeWheel(gScrollableElement, 10, 10,
|
||||
{ deltaMode: WheelEvent.DOM_DELTA_LINE,
|
||||
deltaY: 1.0, lineOrPageDeltaY: 1 });
|
||||
yield sendWheelAndWait(10, 10,
|
||||
{ deltaMode: WheelEvent.DOM_DELTA_LINE,
|
||||
deltaY: 1.0, lineOrPageDeltaY: 1 });
|
||||
gLineHeight = result;
|
||||
ok(gLineHeight > 10 && gLineHeight < 25, "prepareScrollUnits: gLineHeight may be illegal value, got " + gLineHeight);
|
||||
|
||||
result = -1;
|
||||
synthesizeWheel(gScrollableElement, 10, 10,
|
||||
{ deltaMode: WheelEvent.DOM_DELTA_LINE,
|
||||
deltaX: 1.0, lineOrPageDeltaX: 1 });
|
||||
yield sendWheelAndWait(10, 10,
|
||||
{ deltaMode: WheelEvent.DOM_DELTA_LINE,
|
||||
deltaX: 1.0, lineOrPageDeltaX: 1 });
|
||||
gHorizontalLine = result;
|
||||
ok(gHorizontalLine > 5 && gHorizontalLine < 16, "prepareScrollUnits: gHorizontalLine may be illegal value, got " + gHorizontalLine);
|
||||
|
||||
result = -1;
|
||||
synthesizeWheel(gScrollableElement, 10, 10,
|
||||
{ deltaMode: WheelEvent.DOM_DELTA_PAGE,
|
||||
deltaY: 1.0, lineOrPageDeltaY: 1 });
|
||||
yield sendWheelAndWait(10, 10,
|
||||
{ deltaMode: WheelEvent.DOM_DELTA_PAGE,
|
||||
deltaY: 1.0, lineOrPageDeltaY: 1 });
|
||||
gPageHeight = result;
|
||||
// XXX Cannot we know the actual scroll port size?
|
||||
ok(gPageHeight >= 150 && gPageHeight <= 200,
|
||||
"prepareScrollUnits: gPageHeight is strange value, got " + gPageHeight);
|
||||
|
||||
result = -1;
|
||||
synthesizeWheel(gScrollableElement, 10, 10,
|
||||
{ deltaMode: WheelEvent.DOM_DELTA_PAGE,
|
||||
deltaX: 1.0, lineOrPageDeltaX: 1 });
|
||||
yield sendWheelAndWait(10, 10,
|
||||
{ deltaMode: WheelEvent.DOM_DELTA_PAGE,
|
||||
deltaX: 1.0, lineOrPageDeltaX: 1 });
|
||||
gPageWidth = result;
|
||||
ok(gPageWidth >= 150 && gPageWidth <= 200,
|
||||
"prepareScrollUnits: gPageWidth is strange value, got " + gPageWidth);
|
||||
|
@ -102,7 +108,7 @@ function prepareScrollUnits()
|
|||
// legacy mouse scroll events when its lineOrPageDelta value is not zero or
|
||||
// accumulated delta values of pixel scroll events of pixel only device
|
||||
// become over the line height.
|
||||
function testContinuousTrustedEvents()
|
||||
function* testContinuousTrustedEvents()
|
||||
{
|
||||
const kSynthesizedWheelEventTests = [
|
||||
{ description: "Simple horizontal wheel event by pixels (16.0 - 1) #1",
|
||||
|
@ -2661,8 +2667,9 @@ function testContinuousTrustedEvents()
|
|||
// Tests for accumulation delta when delta_multiplier_is customized.
|
||||
{ description: "lineOrPageDelta should be recomputed by ESM (pixel) #1",
|
||||
prepare: function () {
|
||||
SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_x", 200);
|
||||
SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_y", 300);
|
||||
SpecialPowers.pushPrefEnv({"set": [["mousewheel.default.delta_multiplier_x", 200],
|
||||
["mousewheel.default.delta_multiplier_y", 300]]},
|
||||
continueTest);
|
||||
},
|
||||
event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
|
||||
deltaX: gHorizontalLine / 4, deltaY: gLineHeight / 8, deltaZ: 0,
|
||||
|
@ -2714,15 +2721,17 @@ function testContinuousTrustedEvents()
|
|||
horizontal: { expected: true, preventDefault: false, detail: Math.floor((gHorizontalLine / 4 + 1) * 2) },
|
||||
vertical: { expected: true, preventDefault: false, detail: Math.floor((gLineHeight / 8 + 1) * 3) } },
|
||||
finished: function () {
|
||||
SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_x", 100);
|
||||
SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_y", 100);
|
||||
SpecialPowers.pushPrefEnv({"set": [["mousewheel.default.delta_multiplier_x", 100],
|
||||
["mousewheel.default.delta_multiplier_y", 100]]},
|
||||
continueTest);
|
||||
},
|
||||
},
|
||||
|
||||
{ description: "lineOrPageDelta should be recomputed by ESM (pixel, negative, shift) #1",
|
||||
prepare: function () {
|
||||
SpecialPowers.setIntPref("mousewheel.with_shift.delta_multiplier_x", 200);
|
||||
SpecialPowers.setIntPref("mousewheel.with_shift.delta_multiplier_y", 300);
|
||||
SpecialPowers.pushPrefEnv({"set": [["mousewheel.with_shift.delta_multiplier_x", 200],
|
||||
["mousewheel.with_shift.delta_multiplier_y", 300]]},
|
||||
continueTest);
|
||||
},
|
||||
event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
|
||||
deltaX: -gHorizontalLine / 4, deltaY: -gLineHeight / 8, deltaZ: 0,
|
||||
|
@ -2774,15 +2783,17 @@ function testContinuousTrustedEvents()
|
|||
horizontal: { expected: true, preventDefault: false, detail: Math.ceil(-(gHorizontalLine / 4 + 1) * 2) },
|
||||
vertical: { expected: true, preventDefault: false, detail: Math.ceil(-(gLineHeight / 8 + 1) * 3) } },
|
||||
finished: function () {
|
||||
SpecialPowers.setIntPref("mousewheel.with_shift.delta_multiplier_x", 100);
|
||||
SpecialPowers.setIntPref("mousewheel.with_shift.delta_multiplier_y", 100);
|
||||
SpecialPowers.pushPrefEnv({"set": [["mousewheel.with_shift.delta_multiplier_x", 100],
|
||||
["mousewheel.with_shift.delta_multiplier_y", 100]]},
|
||||
continueTest);
|
||||
},
|
||||
},
|
||||
|
||||
{ description: "lineOrPageDelta should be recomputed by ESM (line) #1",
|
||||
prepare: function () {
|
||||
SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_x", 200);
|
||||
SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_y", 100);
|
||||
SpecialPowers.pushPrefEnv({"set": [["mousewheel.default.delta_multiplier_x", 200],
|
||||
["mousewheel.default.delta_multiplier_y", 100]]},
|
||||
continueTest);
|
||||
},
|
||||
event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
|
||||
deltaX: 0.3, deltaY: 0.4, deltaZ: 0,
|
||||
|
@ -2834,15 +2845,17 @@ function testContinuousTrustedEvents()
|
|||
horizontal: { expected: true, preventDefault: false, detail: Math.floor(gHorizontalLine * 0.6) },
|
||||
vertical: { expected: true, preventDefault: false, detail: Math.floor(gLineHeight * 0.4) } },
|
||||
finished: function () {
|
||||
SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_x", 100);
|
||||
SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_y", 100);
|
||||
SpecialPowers.pushPrefEnv({"set": [["mousewheel.default.delta_multiplier_x", 100],
|
||||
["mousewheel.default.delta_multiplier_y", 100]]},
|
||||
continueTest);
|
||||
},
|
||||
},
|
||||
|
||||
{ description: "lineOrPageDelta should be recomputed by ESM (line, negative) #1",
|
||||
prepare: function () {
|
||||
SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_x", 200);
|
||||
SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_y", -100);
|
||||
SpecialPowers.pushPrefEnv({"set": [["mousewheel.default.delta_multiplier_x", 200],
|
||||
["mousewheel.default.delta_multiplier_y", -100]]},
|
||||
continueTest);
|
||||
},
|
||||
event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
|
||||
deltaX: -0.3, deltaY: -0.4, deltaZ: 0,
|
||||
|
@ -2894,15 +2907,17 @@ function testContinuousTrustedEvents()
|
|||
horizontal: { expected: true, preventDefault: false, detail: Math.ceil(gHorizontalLine * -0.6) },
|
||||
vertical: { expected: true, preventDefault: false, detail: Math.floor(gLineHeight * 0.4) } },
|
||||
finished: function () {
|
||||
SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_x", 100);
|
||||
SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_y", 100);
|
||||
SpecialPowers.pushPrefEnv({"set": [["mousewheel.default.delta_multiplier_x", 100],
|
||||
["mousewheel.default.delta_multiplier_y", 100]]},
|
||||
continueTest);
|
||||
},
|
||||
},
|
||||
|
||||
{ description: "lineOrPageDelta should be recomputed by ESM (page) #1",
|
||||
prepare: function () {
|
||||
SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_x", 100);
|
||||
SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_y", 200);
|
||||
SpecialPowers.pushPrefEnv({"set": [["mousewheel.default.delta_multiplier_x", 100],
|
||||
["mousewheel.default.delta_multiplier_y", 200]]},
|
||||
continueTest);
|
||||
},
|
||||
event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
|
||||
deltaX: 0.3, deltaY: 0.4, deltaZ: 0,
|
||||
|
@ -2954,15 +2969,17 @@ function testContinuousTrustedEvents()
|
|||
horizontal: { expected: true, preventDefault: false, detail: Math.floor(gPageWidth * 0.4) },
|
||||
vertical: { expected: true, preventDefault: false, detail: Math.floor(gPageHeight * 0.8) } },
|
||||
finished: function () {
|
||||
SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_x", 100);
|
||||
SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_y", 100);
|
||||
SpecialPowers.pushPrefEnv({"set": [["mousewheel.default.delta_multiplier_x", 100],
|
||||
["mousewheel.default.delta_multiplier_y", 100]]},
|
||||
continueTest);
|
||||
},
|
||||
},
|
||||
|
||||
{ description: "lineOrPageDelta should be recomputed by ESM (page, negative) #1",
|
||||
prepare: function () {
|
||||
SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_x", 100);
|
||||
SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_y", 200);
|
||||
SpecialPowers.pushPrefEnv({"set": [["mousewheel.default.delta_multiplier_x", 100],
|
||||
["mousewheel.default.delta_multiplier_y", 200]]},
|
||||
continueTest);
|
||||
},
|
||||
event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
|
||||
deltaX: -0.3, deltaY: -0.4, deltaZ: 0,
|
||||
|
@ -3014,8 +3031,9 @@ function testContinuousTrustedEvents()
|
|||
horizontal: { expected: true, preventDefault: false, detail: Math.ceil(gPageWidth * -0.4) },
|
||||
vertical: { expected: true, preventDefault: false, detail: Math.ceil(gPageHeight * -0.8) } },
|
||||
finished: function () {
|
||||
SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_x", 100);
|
||||
SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_y", 100);
|
||||
SpecialPowers.pushPrefEnv({"set": [["mousewheel.default.delta_multiplier_x", 100],
|
||||
["mousewheel.default.delta_multiplier_y", 100]]},
|
||||
continueTest);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
@ -3145,13 +3163,13 @@ function testContinuousTrustedEvents()
|
|||
currentWheelEventTest = kSynthesizedWheelEventTests[i];
|
||||
|
||||
if (currentWheelEventTest.prepare) {
|
||||
currentWheelEventTest.prepare();
|
||||
yield currentWheelEventTest.prepare();
|
||||
}
|
||||
|
||||
synthesizeWheel(gScrollableElement, 10, 10, currentWheelEventTest.event);
|
||||
yield sendWheelAndWait(10, 10, currentWheelEventTest.event);
|
||||
|
||||
if (currentWheelEventTest.finished) {
|
||||
currentWheelEventTest.finished();
|
||||
yield currentWheelEventTest.finished();
|
||||
}
|
||||
|
||||
var description = "testContinuousTrustedEvents, " +
|
||||
|
@ -3181,60 +3199,49 @@ function testContinuousTrustedEvents()
|
|||
window.removeEventListener("MozMousePixelScroll", legacyEventHandler, true);
|
||||
}
|
||||
|
||||
var gTestContinuation = null;
|
||||
|
||||
function continueTest()
|
||||
{
|
||||
if (!gTestContinuation) {
|
||||
gTestContinuation = testBody();
|
||||
}
|
||||
var ret = gTestContinuation.next();
|
||||
if (ret.done) {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
function* testBody()
|
||||
{
|
||||
yield* prepareScrollUnits();
|
||||
yield* testContinuousTrustedEvents();
|
||||
}
|
||||
|
||||
function runTests()
|
||||
{
|
||||
SpecialPowers.setIntPref("mousewheel.transaction.timeout", 100000);
|
||||
|
||||
SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_x", 100);
|
||||
SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_y", 100);
|
||||
SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_z", 100);
|
||||
SpecialPowers.setIntPref("mousewheel.with_alt.delta_multiplier_x", 100);
|
||||
SpecialPowers.setIntPref("mousewheel.with_alt.delta_multiplier_y", 100);
|
||||
SpecialPowers.setIntPref("mousewheel.with_alt.delta_multiplier_z", 100);
|
||||
SpecialPowers.setIntPref("mousewheel.with_control.delta_multiplier_x", 100);
|
||||
SpecialPowers.setIntPref("mousewheel.with_control.delta_multiplier_y", 100);
|
||||
SpecialPowers.setIntPref("mousewheel.with_control.delta_multiplier_z", 100);
|
||||
SpecialPowers.setIntPref("mousewheel.with_meta.delta_multiplier_x", 100);
|
||||
SpecialPowers.setIntPref("mousewheel.with_meta.delta_multiplier_y", 100);
|
||||
SpecialPowers.setIntPref("mousewheel.with_meta.delta_multiplier_z", 100);
|
||||
SpecialPowers.setIntPref("mousewheel.with_shift.delta_multiplier_x", 100);
|
||||
SpecialPowers.setIntPref("mousewheel.with_shift.delta_multiplier_y", 100);
|
||||
SpecialPowers.setIntPref("mousewheel.with_shift.delta_multiplier_z", 100);
|
||||
SpecialPowers.setIntPref("mousewheel.with_win.delta_multiplier_x", 100);
|
||||
SpecialPowers.setIntPref("mousewheel.with_win.delta_multiplier_y", 100);
|
||||
SpecialPowers.setIntPref("mousewheel.with_win.delta_multiplier_z", 100);
|
||||
|
||||
prepareScrollUnits();
|
||||
testContinuousTrustedEvents();
|
||||
|
||||
clearPrefs();
|
||||
SimpleTest.finish();
|
||||
SpecialPowers.pushPrefEnv({"set": [
|
||||
["mousewheel.transaction.timeout", 100000],
|
||||
["mousewheel.default.delta_multiplier_x", 100],
|
||||
["mousewheel.default.delta_multiplier_y", 100],
|
||||
["mousewheel.default.delta_multiplier_z", 100],
|
||||
["mousewheel.with_alt.delta_multiplier_x", 100],
|
||||
["mousewheel.with_alt.delta_multiplier_y", 100],
|
||||
["mousewheel.with_alt.delta_multiplier_z", 100],
|
||||
["mousewheel.with_control.delta_multiplier_x", 100],
|
||||
["mousewheel.with_control.delta_multiplier_y", 100],
|
||||
["mousewheel.with_control.delta_multiplier_z", 100],
|
||||
["mousewheel.with_meta.delta_multiplier_x", 100],
|
||||
["mousewheel.with_meta.delta_multiplier_y", 100],
|
||||
["mousewheel.with_meta.delta_multiplier_z", 100],
|
||||
["mousewheel.with_shift.delta_multiplier_x", 100],
|
||||
["mousewheel.with_shift.delta_multiplier_y", 100],
|
||||
["mousewheel.with_shift.delta_multiplier_z", 100],
|
||||
["mousewheel.with_win.delta_multiplier_x", 100],
|
||||
["mousewheel.with_win.delta_multiplier_y", 100],
|
||||
["mousewheel.with_win.delta_multiplier_z", 100]
|
||||
]}, continueTest);
|
||||
}
|
||||
|
||||
function clearPrefs()
|
||||
{
|
||||
SpecialPowers.clearUserPref("mousewheel.transaction.timeout");
|
||||
|
||||
SpecialPowers.clearUserPref("mousewheel.default.delta_multiplier_x");
|
||||
SpecialPowers.clearUserPref("mousewheel.default.delta_multiplier_y");
|
||||
SpecialPowers.clearUserPref("mousewheel.default.delta_multiplier_z");
|
||||
SpecialPowers.clearUserPref("mousewheel.with_alt.delta_multiplier_x");
|
||||
SpecialPowers.clearUserPref("mousewheel.with_alt.delta_multiplier_y");
|
||||
SpecialPowers.clearUserPref("mousewheel.with_alt.delta_multiplier_z");
|
||||
SpecialPowers.clearUserPref("mousewheel.with_control.delta_multiplier_x");
|
||||
SpecialPowers.clearUserPref("mousewheel.with_control.delta_multiplier_y");
|
||||
SpecialPowers.clearUserPref("mousewheel.with_control.delta_multiplier_z");
|
||||
SpecialPowers.clearUserPref("mousewheel.with_meta.delta_multiplier_x");
|
||||
SpecialPowers.clearUserPref("mousewheel.with_meta.delta_multiplier_y");
|
||||
SpecialPowers.clearUserPref("mousewheel.with_meta.delta_multiplier_z");
|
||||
SpecialPowers.clearUserPref("mousewheel.with_shift.delta_multiplier_x");
|
||||
SpecialPowers.clearUserPref("mousewheel.with_shift.delta_multiplier_y");
|
||||
SpecialPowers.clearUserPref("mousewheel.with_shift.delta_multiplier_z");
|
||||
SpecialPowers.clearUserPref("mousewheel.with_win.delta_multiplier_x");
|
||||
SpecialPowers.clearUserPref("mousewheel.with_win.delta_multiplier_y");
|
||||
SpecialPowers.clearUserPref("mousewheel.with_win.delta_multiplier_z");
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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<nsIURI> uri;
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(uri), originNoSuffix);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -5025,7 +5025,7 @@ nsHTMLEditRules::CheckForEmptyBlock(nsINode* aStartNode,
|
|||
// Move to the start of the next node if it's a text.
|
||||
nsCOMPtr<nsIContent> 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<nsIContent> 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 {
|
||||
|
|
|
@ -713,6 +713,7 @@ struct ParamTraits<mozilla::layers::FrameMetrics>
|
|||
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<mozilla::layers::FrameMetrics>
|
|||
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));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -162,21 +162,6 @@ public:
|
|||
bool UpdateAnimation(const TimeStamp& aSampleTime,
|
||||
Vector<Task*>* 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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<ScrollableLayerGuid> 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<TouchBehaviorFlags> 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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -624,7 +624,7 @@ inline ClassObjectCreationOp DELEGATED_CLASSSPEC(const ClassSpec* spec) {
|
|||
return reinterpret_cast<ClassObjectCreationOp>(const_cast<ClassSpec*>(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; }
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -275,7 +275,6 @@ enum class Expr
|
|||
F64Atan2,
|
||||
|
||||
// Atomics
|
||||
AtomicsFence,
|
||||
I32AtomicsCompareExchange,
|
||||
I32AtomicsExchange,
|
||||
I32AtomicsLoad,
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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<TypedArrayObject*> view, uint32_t* offset)
|
||||
{
|
||||
RootedId id(cx);
|
||||
if (!ValueToId<CanGC>(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<int32_t*>(addr) = view->viewDataShared().cast<int32_t*>() + 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<double>();
|
||||
} 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<TypedArrayObject*> 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<int32_t*> addr = view->viewDataShared().cast<int32_t*>() + offset1;
|
||||
if (jit::AtomicOperations::loadSafeWhenRacy(addr) != value) {
|
||||
r.setInt32(AtomicsObject::FutexNotequal);
|
||||
return true;
|
||||
}
|
||||
|
||||
Rooted<SharedArrayBufferObject*> 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<GlobalObject*> global)
|
||||
{
|
||||
|
@ -1253,8 +1138,6 @@ AtomicsObject::initClass(JSContext* cx, Handle<GlobalObject*> global)
|
|||
|
||||
if (!JS_DefineFunctions(cx, Atomics, AtomicsMethods))
|
||||
return nullptr;
|
||||
if (!JS_DefineConstDoubles(cx, Atomics, AtomicsConstants))
|
||||
return nullptr;
|
||||
|
||||
RootedValue AtomicsValue(cx, ObjectValue(*Atomics));
|
||||
|
||||
|
|
|
@ -18,31 +18,20 @@ class AtomicsObject : public JSObject
|
|||
static const Class class_;
|
||||
static JSObject* initClass(JSContext* cx, Handle<GlobalObject*> 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_;
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -397,6 +397,14 @@ CreatePromisePrototype(JSContext* cx, JSProtoKey key)
|
|||
return cx->global()->createBlankPrototype(cx, &PromiseObject::protoClass_);
|
||||
}
|
||||
|
||||
static const ClassSpec PromiseObjectClassSpec = {
|
||||
GenericCreateConstructor<PromiseConstructor, 1, gc::AllocKind::FUNCTION>,
|
||||
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<PromiseConstructor, 1, gc::AllocKind::FUNCTION>,
|
||||
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
|
||||
};
|
||||
|
|
|
@ -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<V>(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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
_(AtomicsExchange) \
|
||||
_(AtomicsLoad) \
|
||||
_(AtomicsStore) \
|
||||
_(AtomicsFence) \
|
||||
_(AtomicsAdd) \
|
||||
_(AtomicsSub) \
|
||||
_(AtomicsAnd) \
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -280,7 +280,6 @@ namespace jit {
|
|||
_(AsmReinterpret) \
|
||||
_(NewDerivedTypedObject) \
|
||||
_(RecompileCheck) \
|
||||
_(MemoryBarrier) \
|
||||
_(AsmJSCompareExchangeHeap) \
|
||||
_(AsmJSAtomicExchangeHeap) \
|
||||
_(AsmJSAtomicBinopHeap) \
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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}'")
|
||||
|
|
|
@ -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<ArrayConstructor, 1, AllocKind::FUNCTION, &jit::JitInfo_Array>,
|
||||
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<ArrayConstructor, 1, AllocKind::FUNCTION, &jit::JitInfo_Array>,
|
||||
CreateArrayPrototype,
|
||||
array_static_methods,
|
||||
nullptr,
|
||||
array_methods,
|
||||
nullptr,
|
||||
array_proto_finish
|
||||
}
|
||||
&ArrayObjectClassSpec
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -3272,6 +3272,16 @@ FinishDateClassInit(JSContext* cx, HandleObject ctor, HandleObject proto)
|
|||
nullptr, nullptr, 0);
|
||||
}
|
||||
|
||||
static const ClassSpec DateObjectClassSpec = {
|
||||
GenericCreateConstructor<DateConstructor, 7, gc::AllocKind::FUNCTION>,
|
||||
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<DateConstructor, 7, gc::AllocKind::FUNCTION>,
|
||||
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*
|
||||
|
|
|
@ -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*
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -1748,6 +1748,55 @@ js::ToLengthClamped<JSContext>(JSContext*, HandleValue, uint32_t*, bool*);
|
|||
template bool
|
||||
js::ToLengthClamped<ExclusiveContext>(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 <typename CharT>
|
||||
bool
|
||||
js_strtod(ExclusiveContext* cx, const CharT* begin, const CharT* end, const CharT** dEnd,
|
||||
|
|
|
@ -275,6 +275,26 @@ ToInteger(JSContext* cx, HandleValue v, double* dp)
|
|||
template<typename T>
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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") \
|
||||
|
|
|
@ -41,6 +41,10 @@ class ErrorObject : public NativeObject
|
|||
static bool checkAndUnwrapThis(JSContext* cx, CallArgs& args, const char* fnName,
|
||||
MutableHandle<ErrorObject*> 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;
|
||||
|
|
|
@ -162,7 +162,7 @@ GlobalObject::resolveConstructor(JSContext* cx, Handle<GlobalObject*> 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<GlobalObject*> 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<GlobalObject*> 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<GlobalObject*> 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<GlobalObject*> 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<GlobalObject*> 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;
|
||||
|
||||
|
|
|
@ -185,6 +185,15 @@ RegExpObject::trace(JSTracer* trc, JSObject* obj)
|
|||
}
|
||||
}
|
||||
|
||||
static const ClassSpec RegExpObjectClassSpec = {
|
||||
GenericCreateConstructor<js::regexp_construct, 2, gc::AllocKind::FUNCTION>,
|
||||
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<js::regexp_construct, 2, gc::AllocKind::FUNCTION>,
|
||||
CreateRegExpPrototype,
|
||||
nullptr,
|
||||
js::regexp_static_props,
|
||||
js::regexp_methods,
|
||||
js::regexp_properties
|
||||
}
|
||||
&RegExpObjectClassSpec
|
||||
};
|
||||
|
||||
RegExpObject*
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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[];
|
||||
|
|
|
@ -289,6 +289,17 @@ SavedFrame::finishSavedFrameInit(JSContext* cx, HandleObject ctor, HandleObject
|
|||
return FreezeObject(cx, proto);
|
||||
}
|
||||
|
||||
const ClassSpec SavedFrame::classSpec_ = {
|
||||
GenericCreateConstructor<SavedFrame::construct, 0, gc::AllocKind::FUNCTION>,
|
||||
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<SavedFrame::construct, 0, gc::AllocKind::FUNCTION>,
|
||||
GenericCreatePrototype,
|
||||
SavedFrame::staticFunctions,
|
||||
nullptr,
|
||||
SavedFrame::protoFunctions,
|
||||
SavedFrame::protoAccessors,
|
||||
SavedFrame::finishSavedFrameInit,
|
||||
ClassSpec::DontDefineConstructor
|
||||
}
|
||||
&SavedFrame::classSpec_
|
||||
};
|
||||
|
||||
/* static */ const JSFunctionSpec
|
||||
|
|
|
@ -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<XDRMode mode>
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -861,6 +861,18 @@ TypedArrayObject::staticFunctions[] = {
|
|||
JS_FS_END
|
||||
};
|
||||
|
||||
static const ClassSpec
|
||||
TypedArrayObjectSharedTypedArrayPrototypeClassSpec = {
|
||||
GenericCreateConstructor<TypedArrayConstructor, 3, gc::AllocKind::FUNCTION>,
|
||||
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<TypedArrayConstructor, 3, gc::AllocKind::FUNCTION>,
|
||||
GenericCreatePrototype,
|
||||
TypedArrayObject::staticFunctions,
|
||||
nullptr,
|
||||
TypedArrayObject::protoFunctions,
|
||||
TypedArrayObject::protoAccessors,
|
||||
nullptr,
|
||||
ClassSpec::DontDefineConstructor
|
||||
}
|
||||
&TypedArrayObjectSharedTypedArrayPrototypeClassSpec
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -2943,6 +2943,7 @@ protected:
|
|||
InitializeOnMainThread();
|
||||
|
||||
nsString mMessage;
|
||||
nsString mMessageName;
|
||||
nsString mSourceName;
|
||||
uint32_t mLineNumber;
|
||||
nsString mSourceLine;
|
||||
|
|
|
@ -520,6 +520,7 @@ class ErrorReport {
|
|||
public:
|
||||
|
||||
nsCString mCategory;
|
||||
nsString mErrorMsgName;
|
||||
nsString mErrorMsg;
|
||||
nsString mFileName;
|
||||
nsString mSourceLine;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ EvictTouchPoint(RefPtr<dom::Touch>& 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) {
|
||||
|
|
|
@ -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<dom::Touch> touch(
|
||||
new dom::Touch(identifier, point, radius, rotationAngle, force));
|
||||
event->touches.AppendElement(touch);
|
||||
event->mTouches.AppendElement(touch);
|
||||
|
||||
return Move(event);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче