зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to autoland. a=merge CLOSED TREE
This commit is contained in:
Коммит
0fcb667b73
|
@ -0,0 +1 @@
|
|||
Nika Layzell <nika@thelayzells.com> Michael Layzell <michael@thelayzells.com>
|
|
@ -578,7 +578,7 @@ static const nsRoleMapEntry sWAIRoleMaps[] =
|
|||
},
|
||||
{ // document
|
||||
&nsGkAtoms::document,
|
||||
roles::DOCUMENT,
|
||||
roles::NON_NATIVE_DOCUMENT,
|
||||
kUseMapRole,
|
||||
eNoValue,
|
||||
eNoAction,
|
||||
|
@ -619,7 +619,7 @@ static const nsRoleMapEntry sWAIRoleMaps[] =
|
|||
},
|
||||
{ // graphics-document
|
||||
&nsGkAtoms::graphicsDocument,
|
||||
roles::DOCUMENT,
|
||||
roles::NON_NATIVE_DOCUMENT,
|
||||
kUseMapRole,
|
||||
eNoValue,
|
||||
eNoAction,
|
||||
|
|
|
@ -111,7 +111,7 @@ enum Role {
|
|||
|
||||
/**
|
||||
* Represents a document window. A document window is always contained within
|
||||
* an application window. It is used for role="document".
|
||||
* an application window. For role="document", see NON_NATIVE_DOCUMENT.
|
||||
*/
|
||||
DOCUMENT = 15,
|
||||
|
||||
|
@ -643,13 +643,12 @@ enum Role {
|
|||
CAPTION = 103,
|
||||
|
||||
/**
|
||||
* A visual frame or container which contains a view of document content.
|
||||
* Document frames may occur within another Document instance, in which case
|
||||
* the second document may be said to be embedded in the containing instance.
|
||||
* HTML frames are often DOCUMENT_FRAME. Either this object, or a
|
||||
* singleton descendant, should implement the Document interface.
|
||||
* An element containing content that assistive technology users may want to
|
||||
* browse in a reading mode, rather than a focus/interactive/application mode.
|
||||
* This role is used for role="document". For the container which holds the
|
||||
* content of a web page, see DOCUMENT.
|
||||
*/
|
||||
DOCUMENT_FRAME = 104,
|
||||
NON_NATIVE_DOCUMENT = 104,
|
||||
|
||||
/**
|
||||
* Heading.
|
||||
|
|
|
@ -129,7 +129,7 @@ ROLE(APPLICATION,
|
|||
|
||||
ROLE(DOCUMENT,
|
||||
"document",
|
||||
ATK_ROLE_DOCUMENT_FRAME,
|
||||
ATK_ROLE_DOCUMENT_WEB,
|
||||
@"AXWebArea",
|
||||
ROLE_SYSTEM_DOCUMENT,
|
||||
ROLE_SYSTEM_DOCUMENT,
|
||||
|
@ -849,12 +849,12 @@ ROLE(CAPTION,
|
|||
IA2_ROLE_CAPTION,
|
||||
eNameFromSubtreeIfReqRule)
|
||||
|
||||
ROLE(DOCUMENT_FRAME,
|
||||
"document frame",
|
||||
ROLE(NON_NATIVE_DOCUMENT,
|
||||
"non-native document",
|
||||
ATK_ROLE_DOCUMENT_FRAME,
|
||||
NSAccessibilityScrollAreaRole,
|
||||
NSAccessibilityGroupRole,
|
||||
USE_ROLE_STRING,
|
||||
IA2_ROLE_UNKNOWN,
|
||||
ROLE_SYSTEM_DOCUMENT,
|
||||
eNoNameRule)
|
||||
|
||||
ROLE(HEADING,
|
||||
|
|
|
@ -2351,7 +2351,7 @@ DocAccessible::CacheChildrenInSubtree(Accessible* aRoot,
|
|||
// XXX: we should delay document load complete event if the ARIA document
|
||||
// has aria-busy.
|
||||
roles::Role role = aRoot->ARIARole();
|
||||
if (!aRoot->IsDoc() && (role == roles::DIALOG || role == roles::DOCUMENT)) {
|
||||
if (!aRoot->IsDoc() && (role == roles::DIALOG || role == roles::NON_NATIVE_DOCUMENT)) {
|
||||
FireDelayedEvent(nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE, aRoot);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -104,7 +104,7 @@ interface nsIAccessibleRole : nsISupports
|
|||
|
||||
/**
|
||||
* Represents a document window. A document window is always contained within
|
||||
* an application window. It is used for role="document".
|
||||
* an application window. For role="document", see NON_NATIVE_DOCUMENT.
|
||||
*/
|
||||
const unsigned long ROLE_DOCUMENT = 15;
|
||||
|
||||
|
@ -637,13 +637,12 @@ interface nsIAccessibleRole : nsISupports
|
|||
const unsigned long ROLE_CAPTION = 103;
|
||||
|
||||
/**
|
||||
* A visual frame or container which contains a view of document content.
|
||||
* Document frames may occur within another Document instance, in which case
|
||||
* the second document may be said to be embedded in the containing instance.
|
||||
* HTML frames are often ROLE_DOCUMENT_FRAME. Either this object, or a
|
||||
* singleton descendant, should implement the Document interface.
|
||||
* An element containing content that assistive technology users may want to
|
||||
* browse in a reading mode, rather than a focus/interactive/application mode.
|
||||
* This role is used for role="document". For the container which holds the
|
||||
* content of a web page, see ROLE_DOCUMENT.
|
||||
*/
|
||||
const unsigned long ROLE_DOCUMENT_FRAME = 104;
|
||||
const unsigned long ROLE_NON_NATIVE_DOCUMENT = 104;
|
||||
|
||||
/**
|
||||
* Heading.
|
||||
|
|
|
@ -917,6 +917,9 @@ ConvertToNSArray(nsTArray<ProxyAccessible*>& aArray)
|
|||
case roles::ARTICLE:
|
||||
return @"AXDocumentArticle";
|
||||
|
||||
case roles::NON_NATIVE_DOCUMENT:
|
||||
return @"AXDocument";
|
||||
|
||||
// macOS added an AXSubrole value to distinguish generic AXGroup objects
|
||||
// from those which are AXGroups as a result of an explicit ARIA role,
|
||||
// such as the non-landmark, non-listitem text containers in DPub ARIA.
|
||||
|
|
|
@ -85,6 +85,7 @@ const ROLE_MENUBAR = nsIAccessibleRole.ROLE_MENUBAR;
|
|||
const ROLE_MENUITEM = nsIAccessibleRole.ROLE_MENUITEM;
|
||||
const ROLE_MENUPOPUP = nsIAccessibleRole.ROLE_MENUPOPUP;
|
||||
const ROLE_NAVIGATION = nsIAccessibleRole.ROLE_NAVIGATION;
|
||||
const ROLE_NON_NATIVE_DOCUMENT = nsIAccessibleRole.ROLE_NON_NATIVE_DOCUMENT;
|
||||
const ROLE_NOTHING = nsIAccessibleRole.ROLE_NOTHING;
|
||||
const ROLE_NOTE = nsIAccessibleRole.ROLE_NOTE;
|
||||
const ROLE_OPTION = nsIAccessibleRole.ROLE_OPTION;
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
testRole("aria_combobox", ROLE_EDITCOMBOBOX);
|
||||
testRole("aria_dialog", ROLE_DIALOG);
|
||||
testRole("aria_directory", ROLE_LIST);
|
||||
testRole("aria_document", ROLE_DOCUMENT);
|
||||
testRole("aria_document", ROLE_NON_NATIVE_DOCUMENT);
|
||||
testRole("aria_form", ROLE_FORM);
|
||||
testRole("aria_feed", ROLE_GROUPING);
|
||||
testRole("aria_figure", ROLE_FIGURE);
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
function doTest() {
|
||||
// Graphics ARIA role map.
|
||||
testRole("graphics-document", ROLE_DOCUMENT);
|
||||
testRole("graphics-document", ROLE_NON_NATIVE_DOCUMENT);
|
||||
testRole("graphics-object", ROLE_GROUPING);
|
||||
testRole("graphics-symbol", ROLE_GRAPHIC);
|
||||
SimpleTest.finish();
|
||||
|
|
|
@ -116,39 +116,25 @@
|
|||
// xul:menupopup
|
||||
role: ROLE_COMBOBOX_LIST,
|
||||
children: []
|
||||
},
|
||||
{
|
||||
// xul:richlistbox
|
||||
role: ROLE_COMBOBOX_LIST,
|
||||
children: []
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
function test_AutocompleteControl() {
|
||||
testAccessibleTree("txc_autocomplete", accTree);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
var txc = document.getElementById("txc_autocomplete");
|
||||
SimpleTest.ok(txc, "Testing (New) Toolkit autocomplete widget.");
|
||||
|
||||
// Dumb access to trigger popup lazy creation.
|
||||
dump("Trigget popup lazy creation");
|
||||
waitForEvent(EVENT_REORDER, txc, test_AutocompleteControl);
|
||||
waitForEvent(EVENT_REORDER, txc, () => {
|
||||
testAccessibleTree("txc_autocomplete", accTree);
|
||||
SimpleTest.finish();
|
||||
});
|
||||
txc.popup;
|
||||
|
||||
accTree.children.push(
|
||||
{
|
||||
role: ROLE_LIST,
|
||||
children: [
|
||||
{
|
||||
role: ROLE_LIST,
|
||||
children: [
|
||||
{
|
||||
role: ROLE_COLUMNHEADER,
|
||||
children: []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
|
|
@ -779,8 +779,6 @@
|
|||
completeselectedindex="true"
|
||||
shrinkdelay="250"
|
||||
tabscrolling="true"
|
||||
showcommentcolumn="true"
|
||||
showimagecolumn="true"
|
||||
newlines="stripsurroundingwhitespace"
|
||||
ontextentered="this.handleCommand(param);"
|
||||
ontextreverted="return this.handleRevert();"
|
||||
|
|
|
@ -95,6 +95,14 @@ this.Policies = {
|
|||
}
|
||||
},
|
||||
|
||||
"DisableFormHistory": {
|
||||
onBeforeUIStartup(manager, param) {
|
||||
if (param == true) {
|
||||
setAndLockPref("browser.formfill.enable", false);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"dont_check_default_browser": {
|
||||
onBeforeUIStartup(manager, param) {
|
||||
setAndLockPref("browser.shell.checkDefaultBrowser", false);
|
||||
|
|
|
@ -43,10 +43,19 @@ function validateAndParseParamRecursive(param, properties) {
|
|||
case "integer":
|
||||
case "string":
|
||||
case "URL":
|
||||
case "URLorEmpty":
|
||||
case "origin":
|
||||
return validateAndParseSimpleParam(param, properties.type);
|
||||
|
||||
case "array":
|
||||
if (param === undefined) {
|
||||
// Accept a missing array as valid. Policies that use
|
||||
// arrays should still check before iterating through
|
||||
// the array, unless it is marked as "required" in the
|
||||
// schema (but "required" is not implemented yet).
|
||||
return [true, undefined];
|
||||
}
|
||||
|
||||
if (!Array.isArray(param)) {
|
||||
log.error("Array expected but not received");
|
||||
return [false, null];
|
||||
|
@ -95,6 +104,15 @@ function validateAndParseSimpleParam(param, type) {
|
|||
|
||||
switch (type) {
|
||||
case "boolean":
|
||||
if (typeof(param) == "boolean") {
|
||||
valid = true;
|
||||
} else if (typeof(param) == "number" &&
|
||||
(param == 0 || param == 1)) {
|
||||
valid = true;
|
||||
parsedParam = !!param;
|
||||
}
|
||||
break;
|
||||
|
||||
case "number":
|
||||
case "string":
|
||||
valid = (typeof(param) == type);
|
||||
|
@ -126,10 +144,16 @@ function validateAndParseSimpleParam(param, type) {
|
|||
break;
|
||||
|
||||
case "URL":
|
||||
case "URLorEmpty":
|
||||
if (typeof(param) != "string") {
|
||||
break;
|
||||
}
|
||||
|
||||
if (type == "URLorEmpty" && param === "") {
|
||||
valid = true;
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
parsedParam = Services.io.newURI(param);
|
||||
valid = true;
|
||||
|
|
|
@ -50,6 +50,14 @@
|
|||
"enum": [true]
|
||||
},
|
||||
|
||||
"DisableFormHistory": {
|
||||
"description": "Don't remember search and form history.",
|
||||
"first_available": "60.0",
|
||||
|
||||
"type": "boolean",
|
||||
"enum": [true]
|
||||
},
|
||||
|
||||
"dont_check_default_browser": {
|
||||
"description": "Don't check for the default browser on startup.",
|
||||
"first_available": "60.0",
|
||||
|
|
|
@ -15,6 +15,7 @@ support-files =
|
|||
[browser_policy_default_browser_check.js]
|
||||
[browser_policy_disable_fxscreenshots.js]
|
||||
[browser_policy_display_bookmarks.js]
|
||||
[browser_policy_disable_formhistory.js]
|
||||
[browser_policy_display_menu.js]
|
||||
[browser_policy_disable_shield.js]
|
||||
|
||||
|
|
|
@ -3,10 +3,6 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
add_task(async function test_clean_slate() {
|
||||
await startWithCleanSlate();
|
||||
});
|
||||
|
||||
add_task(async function test_broken_json() {
|
||||
await setupPolicyEngineWithJson("config_broken_json.json");
|
||||
|
||||
|
|
|
@ -7,10 +7,6 @@ function URI(str) {
|
|||
return Services.io.newURI(str);
|
||||
}
|
||||
|
||||
add_task(async function test_start_with_disabled_engine() {
|
||||
await startWithCleanSlate();
|
||||
});
|
||||
|
||||
add_task(async function test_setup_preexisting_permissions() {
|
||||
// Pre-existing ALLOW permissions that should be overriden
|
||||
// with DENY.
|
||||
|
|
|
@ -3,10 +3,6 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
add_task(async function test_clean_slate() {
|
||||
await startWithCleanSlate();
|
||||
});
|
||||
|
||||
let { Policies, setAndLockPref } = ChromeUtils.import("resource:///modules/policies/Policies.jsm", {});
|
||||
|
||||
function checkPref(prefName, expectedValue) {
|
||||
|
|
|
@ -3,10 +3,6 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
add_task(async function test_clean_slate() {
|
||||
await startWithCleanSlate();
|
||||
});
|
||||
|
||||
add_task(async function test_simple_policies() {
|
||||
await ContentTask.spawn(gBrowser.selectedBrowser, null, async function() {
|
||||
// Initialize the service in the content process, in case it hasn't
|
||||
|
|
|
@ -21,9 +21,16 @@ add_task(async function test_boolean_values() {
|
|||
[valid, parsed] = PoliciesValidator.validateAndParseParameters(false, schema);
|
||||
ok(valid && parsed === false, "Parsed boolean value correctly");
|
||||
|
||||
[valid, parsed] = PoliciesValidator.validateAndParseParameters(0, schema);
|
||||
ok(valid && parsed === false, "0 parsed as false correctly");
|
||||
|
||||
[valid, parsed] = PoliciesValidator.validateAndParseParameters(1, schema);
|
||||
ok(valid && parsed === true, "1 parsed as true correctly");
|
||||
|
||||
// Invalid values:
|
||||
ok(!PoliciesValidator.validateAndParseParameters("0", schema)[0], "No type coercion");
|
||||
ok(!PoliciesValidator.validateAndParseParameters("true", schema)[0], "No type coercion");
|
||||
ok(!PoliciesValidator.validateAndParseParameters(2, schema)[0], "Other number values are not valid");
|
||||
ok(!PoliciesValidator.validateAndParseParameters(undefined, schema)[0], "Invalid value");
|
||||
ok(!PoliciesValidator.validateAndParseParameters({}, schema)[0], "Invalid value");
|
||||
ok(!PoliciesValidator.validateAndParseParameters(null, schema)[0], "Invalid value");
|
||||
|
@ -92,11 +99,39 @@ add_task(async function test_URL_values() {
|
|||
is(parsed.pathQueryRef, "/foo#bar", "pathQueryRef is correct");
|
||||
|
||||
// Invalid values:
|
||||
ok(!PoliciesValidator.validateAndParseParameters("", schema)[0], "Empty string is not accepted for URL");
|
||||
ok(!PoliciesValidator.validateAndParseParameters("www.example.com", schema)[0], "Scheme is required for URL");
|
||||
ok(!PoliciesValidator.validateAndParseParameters("https://:!$%", schema)[0], "Invalid URL");
|
||||
ok(!PoliciesValidator.validateAndParseParameters({}, schema)[0], "Invalid value");
|
||||
});
|
||||
|
||||
add_task(async function test_URLorEmpty_values() {
|
||||
let schema = {
|
||||
type: "URLorEmpty"
|
||||
};
|
||||
|
||||
let valid, parsed;
|
||||
[valid, parsed] = PoliciesValidator.validateAndParseParameters("https://www.example.com/foo#bar", schema);
|
||||
ok(valid, "URL is valid");
|
||||
ok(parsed instanceof Ci.nsIURI, "parsed is a nsIURI");
|
||||
is(parsed.prePath, "https://www.example.com", "prePath is correct");
|
||||
is(parsed.pathQueryRef, "/foo#bar", "pathQueryRef is correct");
|
||||
|
||||
// Test that this type also accept empty strings
|
||||
[valid, parsed] = PoliciesValidator.validateAndParseParameters("", schema);
|
||||
ok(valid, "URLorEmpty is valid");
|
||||
ok(!parsed, "parsed value is falsy");
|
||||
is(typeof(parsed), "string", "parsed is a string");
|
||||
is(parsed, "", "parsed is an empty string");
|
||||
|
||||
// Invalid values:
|
||||
ok(!PoliciesValidator.validateAndParseParameters(" ", schema)[0], "Non-empty string is not accepted");
|
||||
ok(!PoliciesValidator.validateAndParseParameters("www.example.com", schema)[0], "Scheme is required for URL");
|
||||
ok(!PoliciesValidator.validateAndParseParameters("https://:!$%", schema)[0], "Invalid URL");
|
||||
ok(!PoliciesValidator.validateAndParseParameters({}, schema)[0], "Invalid value");
|
||||
});
|
||||
|
||||
|
||||
add_task(async function test_origin_values() {
|
||||
// Origin is a URL that doesn't contain a path/query string (i.e., it's only scheme + host + port)
|
||||
let schema = {
|
||||
|
@ -228,3 +263,34 @@ add_task(async function test_array_of_objects() {
|
|||
is(parsed[0].title, "Foo", "Correct title for bookmark 1");
|
||||
is(parsed[1].title, "Bar", "Correct title for bookmark 2");
|
||||
});
|
||||
|
||||
add_task(async function test_missing_arrays_inside_objects() {
|
||||
let schema = {
|
||||
type: "object",
|
||||
properties: {
|
||||
allow: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "boolean"
|
||||
}
|
||||
},
|
||||
block: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "boolean"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
let valid, parsed;
|
||||
[valid, parsed] = PoliciesValidator.validateAndParseParameters({
|
||||
allow: [true, true, true]
|
||||
}, schema);
|
||||
|
||||
ok(valid, "Object is valid");
|
||||
is(parsed.allow.length, 3, "Allow array is correct.");
|
||||
is(parsed.block, undefined, "Block array is undefined, as expected.");
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
add_task(async function test_policy_disable_formhistory() {
|
||||
await setupPolicyEngineWithJson({
|
||||
"policies": {
|
||||
"DisableFormHistory": true
|
||||
}
|
||||
});
|
||||
|
||||
is(Services.prefs.getBoolPref("browser.formfill.enable"), false, "FormHistory has been disabled");
|
||||
is(Services.prefs.prefIsLocked("browser.formfill.enable"), true, "FormHistory pref has been locked");
|
||||
});
|
|
@ -13,9 +13,6 @@ async function checkScreenshots(shouldBeEnabled) {
|
|||
|
||||
add_task(async function test_disable_firefox_screenshots() {
|
||||
await BrowserTestUtils.withNewTab("data:text/html,Test", async function() {
|
||||
await setupPolicyEngineWithJson("");
|
||||
is(Services.policies.status, Services.policies.INACTIVE, "Start with no policies");
|
||||
|
||||
// Firefox Screenshots are disabled in tests, so make sure we enable
|
||||
// it first to ensure that the test is valid.
|
||||
Services.prefs.setBoolPref(PREF_DISABLE_FX_SCREENSHOTS, false);
|
||||
|
|
|
@ -43,7 +43,16 @@ async function setupPolicyEngineWithJson(json, customSchema) {
|
|||
return promise;
|
||||
}
|
||||
|
||||
async function startWithCleanSlate() {
|
||||
await setupPolicyEngineWithJson("");
|
||||
is(Services.policies.status, Ci.nsIEnterprisePolicies.INACTIVE, "Engine is inactive");
|
||||
}
|
||||
add_task(async function policies_headjs_startWithCleanSlate() {
|
||||
if (Services.policies.status != Ci.nsIEnterprisePolicies.INACTIVE) {
|
||||
await setupPolicyEngineWithJson("");
|
||||
}
|
||||
is(Services.policies.status, Ci.nsIEnterprisePolicies.INACTIVE, "Engine is inactive at the start of the test");
|
||||
});
|
||||
|
||||
registerCleanupFunction(async function policies_headjs_finishWithCleanSlate() {
|
||||
if (Services.policies.status != Ci.nsIEnterprisePolicies.INACTIVE) {
|
||||
await setupPolicyEngineWithJson("");
|
||||
}
|
||||
is(Services.policies.status, Ci.nsIEnterprisePolicies.INACTIVE, "Engine is inactive at the end of the test");
|
||||
});
|
||||
|
|
|
@ -122,10 +122,10 @@
|
|||
<textbox id="editBMPanel_tagsField"
|
||||
type="autocomplete"
|
||||
flex="1"
|
||||
autocompletesearch="places-tag-autocomplete"
|
||||
autocompletesearch="places-tag-autocomplete"
|
||||
autocompletepopup="PopupAutoComplete"
|
||||
completedefaultindex="true"
|
||||
tabscrolling="true"
|
||||
showcommentcolumn="true"
|
||||
placeholder="&editBookmarkOverlay.tagsEmptyDesc.label;"
|
||||
onchange="gEditItemOverlay.onTagsFieldChange();"/>
|
||||
<button id="editBMPanel_tagsSelectorExpander"
|
||||
|
|
|
@ -104,21 +104,18 @@ gTests.push({
|
|||
self.window.document.documentElement.cancelDialog();
|
||||
break;
|
||||
case "popupshown":
|
||||
(async function() {
|
||||
tagsField.popup.removeEventListener("popupshown", this, true);
|
||||
// In case this test fails the window will close, the test will fail
|
||||
// since we didn't set _cleanShutdown.
|
||||
var tree = tagsField.popup.tree;
|
||||
// Focus and select first result.
|
||||
Assert.notEqual(tree, null, "Autocomplete results tree exists");
|
||||
Assert.equal(tree.view.rowCount, 1, "We have 1 autocomplete result");
|
||||
tagsField.popup.selectedIndex = 0;
|
||||
Assert.equal(tree.view.selection.count, 1,
|
||||
"We have selected a tag from the autocomplete popup");
|
||||
info("About to focus the autocomplete results tree");
|
||||
tree.focus();
|
||||
EventUtils.synthesizeKey("VK_RETURN", {}, self.window);
|
||||
})();
|
||||
tagsField.popup.removeEventListener("popupshown", this, true);
|
||||
// In case this test fails the window will close, the test will fail
|
||||
// since we didn't set _cleanShutdown.
|
||||
let richlistbox = tagsField.popup.richlistbox;
|
||||
// Focus and select first result.
|
||||
Assert.equal(richlistbox.itemCount, 1, "We have 1 autocomplete result");
|
||||
tagsField.popup.selectedIndex = 0;
|
||||
Assert.equal(richlistbox.selectedItems.length, 1,
|
||||
"We have selected a tag from the autocomplete popup");
|
||||
info("About to focus the autocomplete results");
|
||||
richlistbox.focus();
|
||||
EventUtils.synthesizeKey("VK_RETURN", {}, self.window);
|
||||
break;
|
||||
default:
|
||||
Assert.ok(false, "unknown event: " + aEvent.type);
|
||||
|
@ -214,15 +211,14 @@ gTests.push({
|
|||
tagsField.popup.removeEventListener("popupshown", this, true);
|
||||
// In case this test fails the window will close, the test will fail
|
||||
// since we didn't set _cleanShutdown.
|
||||
var tree = tagsField.popup.tree;
|
||||
let richlistbox = tagsField.popup.richlistbox;
|
||||
// Focus and select first result.
|
||||
Assert.notEqual(tree, null, "Autocomplete results tree exists");
|
||||
Assert.ok(tree.view.rowCount, 1, "We have 1 autocomplete result");
|
||||
Assert.ok(richlistbox.itemCount, 1, "We have 1 autocomplete result");
|
||||
tagsField.popup.selectedIndex = 0;
|
||||
Assert.ok(tree.view.selection.count, 1,
|
||||
Assert.ok(richlistbox.selectedItems.length, 1,
|
||||
"We have selected a tag from the autocomplete popup");
|
||||
info("About to focus the autocomplete results tree");
|
||||
tree.focus();
|
||||
info("About to focus the autocomplete results");
|
||||
richlistbox.focus();
|
||||
EventUtils.synthesizeKey("VK_ESCAPE", {}, self.window);
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -742,9 +742,6 @@
|
|||
// clear any previous selection, see bugs 400671 and 488357
|
||||
popup.selectedIndex = -1;
|
||||
|
||||
popup.showCommentColumn = this.showCommentColumn;
|
||||
popup.showImageColumn = this.showImageColumn;
|
||||
|
||||
document.popupNode = null;
|
||||
|
||||
const isRTL = getComputedStyle(this, "").direction == "rtl";
|
||||
|
|
|
@ -80,43 +80,44 @@ dnl ========================================================
|
|||
dnl = Use UndefinedBehavior Sanitizer to find integer overflows
|
||||
dnl ========================================================
|
||||
|
||||
MOZ_ARG_ENABLE_BOOL(ubsan-int-overflow,
|
||||
[ --enable-ubsan-int-overflow Enable UndefinedBehavior Sanitizer (Signed Integer Overflow Parts, default=no)],
|
||||
MOZ_UBSAN_INT_OVERFLOW=1,
|
||||
MOZ_UBSAN_INT_OVERFLOW= )
|
||||
MOZ_ARG_ENABLE_BOOL(ubsan-uint-overflow,
|
||||
[ --enable-ubsan-uint-overflow Enable UndefinedBehavior Sanitizer (Unsigned Integer Overflow Parts, default=no)],
|
||||
MOZ_UBSAN_UINT_OVERFLOW=1,
|
||||
MOZ_UBSAN_UINT_OVERFLOW= )
|
||||
MOZ_ARG_ENABLE_BOOL(signed-overflow-sanitizer,
|
||||
[ --enable-signed-overflow-sanitizer Enable UndefinedBehavior Sanitizer (Signed Integer Overflow Parts, default=no)],
|
||||
MOZ_SIGNED_OVERFLOW_SANITIZE=1,
|
||||
MOZ_SIGNED_OVERFLOW_SANITIZE= )
|
||||
MOZ_ARG_ENABLE_BOOL(unsigned-overflow-sanitizer,
|
||||
[ --enable-unsigned-overflow-sanitizer Enable UndefinedBehavior Sanitizer (Unsigned Integer Overflow Parts, default=no)],
|
||||
MOZ_UNSIGNED_OVERFLOW_SANITIZE=1,
|
||||
MOZ_UNSIGNED_OVERFLOW_SANITIZE= )
|
||||
|
||||
if test -n "$MOZ_UBSAN_INT_OVERFLOW$MOZ_UBSAN_UINT_OVERFLOW"; then
|
||||
if test -n "$MOZ_SIGNED_OVERFLOW_SANITIZE$MOZ_UNSIGNED_OVERFLOW_SANITIZE"; then
|
||||
MOZ_LLVM_HACKS=1
|
||||
MOZ_UBSAN=1
|
||||
# The blacklist really should be split into separate signed/unsigned
|
||||
# blacklists, but we leave that task for another day.
|
||||
CFLAGS="-fsanitize-blacklist=$_topsrcdir/build/sanitizers/ubsan_blacklist_int.txt $CFLAGS"
|
||||
CXXFLAGS="-fsanitize-blacklist=$_topsrcdir/build/sanitizers/ubsan_blacklist_int.txt $CXXFLAGS"
|
||||
if test -n "$MOZ_UBSAN_INT_OVERFLOW"; then
|
||||
SANITIZER_BLACKLISTS=""
|
||||
if test -n "$MOZ_SIGNED_OVERFLOW_SANITIZE"; then
|
||||
SANITIZER_BLACKLISTS="-fsanitize-blacklist=$_topsrcdir/build/sanitizers/ubsan_signed_overflow_blacklist.txt $SANITIZER_BLACKLISTS"
|
||||
CFLAGS="-fsanitize=signed-integer-overflow $CFLAGS"
|
||||
CXXFLAGS="-fsanitize=signed-integer-overflow $CXXFLAGS"
|
||||
if test -z "$CLANG_CL"; then
|
||||
LDFLAGS="-fsanitize=signed-integer-overflow $LDFLAGS"
|
||||
fi
|
||||
AC_DEFINE(MOZ_UBSAN_INT_OVERFLOW)
|
||||
AC_DEFINE(MOZ_SIGNED_OVERFLOW_SANITIZE)
|
||||
fi
|
||||
if test -n "$MOZ_UBSAN_UINT_OVERFLOW"; then
|
||||
if test -n "$MOZ_UNSIGNED_OVERFLOW_SANITIZE"; then
|
||||
SANITIZER_BLACKLISTS="-fsanitize-blacklist=$_topsrcdir/build/sanitizers/ubsan_unsigned_overflow_blacklist.txt $SANITIZER_BLACKLISTS"
|
||||
CFLAGS="-fsanitize=unsigned-integer-overflow $CFLAGS"
|
||||
CXXFLAGS="-fsanitize=unsigned-integer-overflow $CXXFLAGS"
|
||||
if test -z "$CLANG_CL"; then
|
||||
LDFLAGS="-fsanitize=unsigned-integer-overflow $LDFLAGS"
|
||||
fi
|
||||
AC_DEFINE(MOZ_UBSAN_UINT_OVERFLOW)
|
||||
AC_DEFINE(MOZ_UNSIGNED_OVERFLOW_SANITIZE)
|
||||
fi
|
||||
CFLAGS="$SANITIZER_BLACKLISTS $CFLAGS"
|
||||
CXXFLAGS="$SANITIZER_BLACKLISTS $CXXFLAGS"
|
||||
AC_DEFINE(MOZ_UBSAN)
|
||||
MOZ_PATH_PROG(LLVM_SYMBOLIZER, llvm-symbolizer)
|
||||
fi
|
||||
AC_SUBST(MOZ_UBSAN_INT_OVERFLOW)
|
||||
AC_SUBST(MOZ_UBSAN_UINT_OVERFLOW)
|
||||
AC_SUBST(MOZ_SIGNED_OVERFLOW_SANITIZE)
|
||||
AC_SUBST(MOZ_UNSIGNED_OVERFLOW_SANITIZE)
|
||||
AC_SUBST(MOZ_UBSAN)
|
||||
|
||||
# The LLVM symbolizer is used by all sanitizers
|
||||
|
|
|
@ -278,7 +278,7 @@ class RemoteAutomation(Automation):
|
|||
# device manager process
|
||||
dm = None
|
||||
def __init__(self, dm, cmd, stdout=None, stderr=None, env=None, cwd=None, app=None,
|
||||
messageLogger=None):
|
||||
messageLogger=None, counts=None):
|
||||
self.dm = dm
|
||||
self.stdoutlen = 0
|
||||
self.lastTestSeen = "remoteautomation.py"
|
||||
|
@ -286,6 +286,12 @@ class RemoteAutomation(Automation):
|
|||
self.messageLogger = messageLogger
|
||||
self.utilityPath = None
|
||||
|
||||
self.counts = counts
|
||||
if self.counts is not None:
|
||||
self.counts['pass'] = 0
|
||||
self.counts['fail'] = 0
|
||||
self.counts['todo'] = 0
|
||||
|
||||
if (self.proc is None):
|
||||
if cmd[0] == 'am':
|
||||
self.proc = stdout
|
||||
|
@ -360,6 +366,22 @@ class RemoteAutomation(Automation):
|
|||
for message in parsed_messages:
|
||||
if isinstance(message, dict) and message.get('action') == 'test_start':
|
||||
self.lastTestSeen = message['test']
|
||||
if isinstance(message, dict) and message.get('action') == 'log':
|
||||
line = message['message'].strip()
|
||||
if self.counts:
|
||||
m = re.match(".*:\s*(\d*)", line)
|
||||
if m:
|
||||
try:
|
||||
val = int(m.group(1))
|
||||
if "Passed:" in line:
|
||||
self.counts['pass'] += val
|
||||
elif "Failed:" in line:
|
||||
self.counts['fail'] += val
|
||||
elif "Todo:" in line:
|
||||
self.counts['todo'] += val
|
||||
except:
|
||||
pass
|
||||
|
||||
return True
|
||||
|
||||
@property
|
||||
|
@ -393,6 +415,8 @@ class RemoteAutomation(Automation):
|
|||
slowLog = True
|
||||
if hasOutput:
|
||||
noOutputTimer = 0
|
||||
if self.counts and 'pass' in self.counts and self.counts['pass'] > 0:
|
||||
interval = 0.5
|
||||
time.sleep(interval)
|
||||
timer += interval
|
||||
noOutputTimer += interval
|
||||
|
|
|
@ -222,9 +222,9 @@ def old_configure_options(*options):
|
|||
'--enable-system-sqlite',
|
||||
'--enable-tasktracer',
|
||||
'--enable-thread-sanitizer',
|
||||
'--enable-ubsan-int-overflow',
|
||||
'--enable-ubsan-uint-overflow',
|
||||
'--enable-signed-overflow-sanitizer',
|
||||
'--enable-universalchardet',
|
||||
'--enable-unsigned-overflow-sanitizer',
|
||||
'--enable-updater',
|
||||
'--enable-valgrind',
|
||||
'--enable-verify-mar',
|
||||
|
|
|
@ -429,7 +429,7 @@ def valid_mt(path):
|
|||
set_config('MSMANIFEST_TOOL', depends(valid_mt)(lambda x: bool(x)))
|
||||
|
||||
|
||||
link = check_prog('LINKER', ('link.exe',), paths=vc_compiler_path)
|
||||
link = check_prog('LINKER', ('link.exe',), paths=toolchain_search_path)
|
||||
|
||||
add_old_configure_assignment('LINKER', link)
|
||||
|
||||
|
|
|
@ -0,0 +1,285 @@
|
|||
# This file contains an extensive compile-time blacklist for silencing highly
|
||||
# frequent signed integer overflows in our codebase, found by the use of
|
||||
# -fsanitize=signed-integer-overflow. C/C++ say signed integer overflow is
|
||||
# undefined behavior, so instances of this need to be fixed. But not all code
|
||||
# has been properly written to not overflow, and overflow-checking can have
|
||||
# significant compile time and runtime costs, so we will sometimes disable
|
||||
# signed overflow checking.
|
||||
#
|
||||
# The rules in this file are applied at compile time; changes to this list
|
||||
# usually require a full rebuild to apply. If you can modify the source in
|
||||
# question to exempt individual functions using MOZ_NO_SANITIZE_SINT_OVERFLOW,
|
||||
# do that instead.
|
||||
#
|
||||
# The extensive number of entries below is for two reasons.
|
||||
#
|
||||
# First, compiler instrumentation for signed integer overflows has a cost, at
|
||||
# compile time and at runtime. In performance-critical code proven to have no
|
||||
# signed overflow, it makes sense to turn off overflow detection to avoid both
|
||||
# costs. (Indeed, -fsanitize=signed-integer-overflow is unusably slow without
|
||||
# this.)
|
||||
#
|
||||
# Second, many entries here are overly aggressive to get the build into a state
|
||||
# that allows any testing to happen at all. Some of the entries here are for
|
||||
# issues that are highly frequent in our test suites -- over 500 times per run.
|
||||
# Aggressive entries now let us start using this mode, without having to first
|
||||
# fix wide swaths of existing code.
|
||||
#
|
||||
# Entries should be removed 1) as issues are fixed; and 2) as blacklist entries
|
||||
# can be moved out of this centralized file, into source-level blacklist
|
||||
# attributes on individual functions.
|
||||
|
||||
# All entries in this file are to suppress signed-integer-overflow problems.
|
||||
# Blacklists for other reasons should go in separate blacklist files.
|
||||
[signed-integer-overflow]
|
||||
|
||||
# Overflows in the C++ std headers aren't necessarily bugs, because code inside
|
||||
# a language implementation can depend on compiler-specific behavior where C/C++
|
||||
# leave the behavior undefined.
|
||||
src:*bits/basic_string.h
|
||||
|
||||
# Assume everything running through CheckedInt.h is ok. Signed overflows here
|
||||
# should generally have been guarded by safe overflow checks, so it's likely
|
||||
# safe to exempt it from overflow checking. (This should eventually be verified
|
||||
# and functions individually tagged safe so this entry can be removed.)
|
||||
src:*/CheckedInt.h
|
||||
|
||||
# Exclude bignum
|
||||
src:*/mfbt/double-conversion/source/bignum.cc
|
||||
|
||||
# Exclude anything within gtests
|
||||
src:*/gtest/*
|
||||
|
||||
# The JS engine has a lot of code doing all sorts of overflows. This code
|
||||
# is pretty well tested though and excluding it here will allow us to go
|
||||
# for other, less tested code. Ideally, we would include the JS engine here
|
||||
# at some point.
|
||||
src:*/js/src/*
|
||||
src:*/js/public/*
|
||||
src:*/js/*.h
|
||||
src:*/jsfriendapi.h
|
||||
|
||||
# Atomics can overflow, but without a full stack we can't trace these back
|
||||
# to what is actually causing the overflow. Ignoring these for now, as it will
|
||||
# be too much effort to determine every single source here.
|
||||
src:*/mfbt/Atomics.h
|
||||
|
||||
# No reason to instrument certain parts of NSS that explicitely deal with
|
||||
# arithmetics and crypto.
|
||||
src:*/security/nss/lib/freebl/mpi/*
|
||||
src:*/security/nss/lib/freebl/ecl/*
|
||||
|
||||
# nsTArray_base<Alloc, Copy>::ShiftData performs overflows
|
||||
fun:*nsTArray_base*ShiftData*
|
||||
|
||||
### Frequent 0 - 1 overflows
|
||||
#
|
||||
# We have several code patterns in our codebase that cause these overflows,
|
||||
# but they are typically all harmless and could be filtered easily at runtime.
|
||||
# However, some of them are so frequent that suppressing them at compile-time
|
||||
# makes sense to increase runtime performance.
|
||||
#
|
||||
src:*/netwerk/base/nsSocketTransportService2.cpp
|
||||
src:*/dom/xul/XULDocument.cpp
|
||||
src:*/nsCharTraits.h
|
||||
# Code in xpcom/base/CycleCollectedJSContext.cpp
|
||||
fun:*CycleCollectedJSContext*ProcessMetastableStateQueue*
|
||||
# Code in layout/painting/nsDisplayList.cpp
|
||||
fun:*nsDisplayOpacity*ShouldFlattenAway*
|
||||
# Code in modules/libpref/Preferences.cpp
|
||||
fun:*pref_InitInitialObjects*
|
||||
# Code in netwerk/base/nsIOService.cpp
|
||||
fun:*nsIOService*GetCachedProtocolHandler*
|
||||
# Code in layout/style/nsCSSRuleProcessor.cpp
|
||||
fun:*0nsCSSRuleProcessor@@*
|
||||
fun:*nsCSSRuleProcessor*ClearSheets*
|
||||
fun:*TreeMatchContext*InitAncestors*
|
||||
fun:*TreeMatchContext*InitStyleScopes*
|
||||
# Code in layout/xul/nsXULPopupManager.cpp
|
||||
fun:*nsXULPopupManager*AdjustPopupsOnWindowChange*
|
||||
# Code in dom/base/nsDocument.cpp
|
||||
fun:*1nsDocument@@*
|
||||
# Code in gfx/layers/ipc/CompositorBridgeChild.cpp
|
||||
fun:*CompositorBridgeChild*Destroy*
|
||||
# Code in gfx/layers/ipc/ImageBridgeChild.cpp
|
||||
fun:*ImageBridgeChild*ShutdownStep1*
|
||||
# Code in dom/base/nsGlobalWindow.cpp
|
||||
fun:*nsGlobalWindow*ClearControllers*
|
||||
# Code in layout/style/AnimationCollection.cpp
|
||||
fun:*AnimationCollection*PropertyDtor*
|
||||
# Code in layout/style/nsStyleSet.cpp
|
||||
fun:*nsStyleSet*AddImportantRules*
|
||||
fun:*nsStyleSet*CounterStyleRuleForName*
|
||||
|
||||
|
||||
### Misc overflows
|
||||
|
||||
# Hot function in protobuf producing overflows
|
||||
fun:*CodedInputStream*ReadTagWithCutoff*
|
||||
|
||||
|
||||
# SQLite3 is full of overflows :/
|
||||
src:*/db/sqlite3/src/sqlite3.c
|
||||
|
||||
# zlib has some overflows, we can't deal with them right now
|
||||
src:*/modules/zlib/src/*
|
||||
|
||||
# Our LZ4 implementation uses overflows. By listing it here we might
|
||||
# miss some unintended overflows in that implementation, but we can't
|
||||
# check for it right now.
|
||||
src:*/mfbt/lz4.c
|
||||
|
||||
# Apparently this overflows a lot, because it contains some allocators
|
||||
# that keep overflowing, not sure why. Disabling by function didn't seem
|
||||
# to work here for operator new.
|
||||
src:*/xpcom/ds/nsArrayEnumerator.cpp
|
||||
|
||||
# Memory usage reporting code in gfx/thebes/gfxASurface.cpp
|
||||
# We probably don't care about the frequent overflows there.
|
||||
fun:*SurfaceMemoryReporter*AdjustUsedMemory*
|
||||
|
||||
# Frequent overflower in gfx/thebes/gfxFontEntry.cpp
|
||||
fun:*WeightDistance*
|
||||
|
||||
# Another frequent overflower
|
||||
fun:*nsTObserverArray_base*AdjustIterators*
|
||||
|
||||
# Overflows in Skia
|
||||
fun:*SkPathRef*makeSpace*
|
||||
fun:*SkPathRef*resetToSize*
|
||||
|
||||
# Expat Parser has some overflows
|
||||
fun:*nsExpatDriver*ConsumeToken*
|
||||
|
||||
# Frequent overflowers in harfbuzz
|
||||
fun:*hb_in_range*
|
||||
fun:*OT*collect_glyphs*
|
||||
|
||||
# These look like harmless layouting-related overflows
|
||||
src:*/gfx/cairo/libpixman/src/pixman-region.c
|
||||
|
||||
# Sorting code in layout/style/nsCSSProps.cpp that probably doesn't
|
||||
# care about overflows.
|
||||
fun:*SortPropertyAndCount*
|
||||
|
||||
# Code in ipc/chromium/src/base/file_path.cc where a function returns -1
|
||||
# being cast to unsigned and then overflowed.
|
||||
fun:*FilePath*Append*
|
||||
fun:*FilePath*StripTrailingSeparatorsInternal*
|
||||
|
||||
# Code in dom/base/nsJSEnvironment.cpp
|
||||
fun:*FireForgetSkippable*
|
||||
|
||||
# Code in gfx/thebes/gfxSkipChars.h
|
||||
fun:*gfxSkipCharsIterator*AdvanceSkipped*
|
||||
|
||||
# Code in gfx/thebes/gfxScriptItemizer.cpp
|
||||
fun:*gfxScriptItemizer*fixup*
|
||||
fun:*gfxScriptItemizer*push*
|
||||
|
||||
# Code in dom/base/nsDocument.cpp
|
||||
fun:*nsDocument*BlockOnload*
|
||||
|
||||
# Code in layout/base/nsCSSFrameConstructor.cpp
|
||||
fun:*nsCSSFrameConstructor*FrameConstructionItemList*AdjustCountsForItem*
|
||||
|
||||
# Code in nsprpub/lib/ds/plarena.c doing ptrdiffs
|
||||
fun:*PL_ArenaRelease*
|
||||
|
||||
# This file contains a bunch of arithmetic operations on timestamps that
|
||||
# apparently are allowed to overflow.
|
||||
src:*/src/widget/SystemTimeConverter.h
|
||||
|
||||
# Code in dom/media/flac/FlacDemuxer.cpp purposely uses overflowing arithmetics
|
||||
fun:*Frame*FindNext*
|
||||
|
||||
# Code in netwerk/base/nsStandardURL.cpp,
|
||||
# these methods return signed but the subtraction is first performed unsigned
|
||||
fun:*nsStandardURL*ReplaceSegment*
|
||||
|
||||
# Code in netwerk/protocol/http/nsHttpChannel.cpp
|
||||
# same as previous with the previous entry.
|
||||
fun:*nsHttpChannel*ReportNetVSCacheTelemetry*
|
||||
|
||||
# Code in layout/tables/nsCellMap.cpp
|
||||
# again subtraction then cast to signed.
|
||||
fun:*nsTableCellMap*GetColInfoAt*
|
||||
|
||||
# Code in layout/generic/nsTextFrame.cpp
|
||||
# again subtraction then cast to signed.
|
||||
fun:*nsTextFrame*CharacterDataChanged*
|
||||
|
||||
# Not sure what is going on in this file, but it doesn't look
|
||||
# related to what we are looking for.
|
||||
src:*/xpcom/base/CountingAllocatorBase.h
|
||||
|
||||
# Code in dom/base/nsDOMNavigationTiming.cpp
|
||||
# Timestamp related, probably expecting the overflow
|
||||
fun:*nsDOMNavigationTiming*TimeStampToDOM*
|
||||
|
||||
# Several unsigned arithmetic operations with -1
|
||||
src:*/hal/HalWakeLock.cpp
|
||||
|
||||
# Code in layout/generic/nsGfxScrollFrame.cpp that produces
|
||||
# somewhat frequent signed integer overflows. Probably harmless
|
||||
# because it's layout code.
|
||||
fun:*ClampAndAlignWithPixels*
|
||||
|
||||
# Likely benign overflow in mozglue/misc/TimeStamp_posix.cpp
|
||||
fun:*ClockResolutionNs*
|
||||
|
||||
# This header has all sorts of operators that do post-operation
|
||||
# overflow and underflow checking, triggering frequent reports
|
||||
src:*/mozglue/misc/TimeStamp.h
|
||||
|
||||
#
|
||||
# Various hashing functions, both regular and cryptographic ones
|
||||
#
|
||||
src:*/dom/canvas/MurmurHash3.cpp
|
||||
src:*/gfx/skia/skia/include/private/SkChecksum.h
|
||||
src:*/HashFunctions.h
|
||||
src:*/intl/icu/source/common/unifiedcache.h
|
||||
src:*/mfbt/SHA1.cpp
|
||||
src:*/modules/zlib/src/adler32.c
|
||||
src:*/netwerk/cache/nsDiskCacheDevice.cpp
|
||||
src:*/netwerk/cache2/CacheHashUtils.cpp
|
||||
src:*/netwerk/sctp/src/netinet/sctp_sha1.c
|
||||
src:*/netwerk/srtp/src/crypto/hash/sha1.c
|
||||
src:*/netwerk/sctp/src/netinet/sctp_sha1.c
|
||||
src:*/nsprpub/lib/ds/plhash.c
|
||||
src:*/security/manager/ssl/md4.c
|
||||
src:*/security/nss/lib/dbm/src/h_func.c
|
||||
src:*/security/nss/lib/freebl/sha512.c
|
||||
src:*/security/nss/lib/freebl/md5.c
|
||||
src:*/XorShift128PlusRNG.h
|
||||
src:*/xpcom/ds/PLDHashTable.cpp
|
||||
|
||||
# Hash/Cache function in Skia
|
||||
fun:*GradientShaderCache*Build32bitCache*
|
||||
|
||||
# Hash function in js/public/Utility.h
|
||||
fun:ScrambleHashCode*
|
||||
|
||||
# Hashing functions in Cairo
|
||||
fun:*_hash_matrix_fnv*
|
||||
fun:*_hash_mix_bits*
|
||||
fun:*_cairo_hash_string*
|
||||
fun:*_cairo_hash_bytes*
|
||||
|
||||
# Hash function in modules/libjar/nsZipArchive.cpp
|
||||
fun:*HashName*
|
||||
|
||||
# intl code hashing functions
|
||||
fun:*ustr_hash*CharsN*
|
||||
fun:*hashEntry*
|
||||
|
||||
# harfbuzz hash/digest functions
|
||||
fun:*hb_set_digest_lowest_bits_t*
|
||||
|
||||
# Hash function in gfx
|
||||
fun:*gfxFontStyle*Hash*
|
||||
|
||||
# expat uses a CHAR_HASH macro in several places that causes
|
||||
# a high amount of overflows. We should try finding a better
|
||||
# way to disable this rather than blacklisting the whole thing.
|
||||
src:*/parser/expat/*
|
|
@ -1,20 +1,40 @@
|
|||
# This file contains an extensive compile-time blacklist for silencing highly
|
||||
# frequent signed and unsigned integer overflows in our codebase, found by the
|
||||
# use of -fsanitize=integer. All of the overflows that caused an entry in this
|
||||
# list are highly frequent in our test suites (> 500 times per run) and therefore
|
||||
# unlikely to be bugs. Nevertheless, the slow down this test mode significantly
|
||||
# if left active. Without this list, the -fsanitize=integer test mode is unusable
|
||||
# both because of performance and the large number of results to check.
|
||||
# frequent *un*signed integer overflows in our codebase, found by the use of
|
||||
# -fsanitize=unsigned-integer-overflow. Such overflows are not necessarily
|
||||
# bugs -- unsigned integer overflow has well-defined semantics in C/C++. But
|
||||
# overflow may still be *unexpected* and incorrectly handled, so we try to
|
||||
# annotate those places where unsigned overflow is correct and desired.
|
||||
#
|
||||
# Some of the entries on this list are more aggressive to get the build into a
|
||||
# state that allows any testing to happen at all. This is not an optimal solution
|
||||
# and it would be good if we could refine the tool and shorten this list over
|
||||
# the time. Source code annotations can also help with this.
|
||||
# The rules in this file are applied at compile time; changes to this list
|
||||
# usually require a full rebuild to apply. If you can modify the source in
|
||||
# question to exempt individual functions using MOZ_NO_SANITIZE_UINT_OVERFLOW,
|
||||
# do that instead.
|
||||
#
|
||||
# The rules in this file are only applied at compile time. If you can modify the
|
||||
# source in question, consider function attributes to disable instrumentation.
|
||||
# The extensive number of entries below is for two reasons.
|
||||
#
|
||||
# First, compiler instrumentation for unsigned integer overflows has a cost, at
|
||||
# compile time and at runtime. In places where code expects and depends upon
|
||||
# overflow behavior -- and especially in performance-critical code -- it makes
|
||||
# sense to turn off overflow detection to avoid both costs. (Indeed,
|
||||
# -fsanitize=signed-integer-overflow is unusably slow without this.)
|
||||
#
|
||||
# Second, many entries here are overly aggressive to get the build into a state
|
||||
# that allows any testing to happen at all. Some of the entries here are for
|
||||
# issues that are highly frequent in our test suites -- over 500 times per run.
|
||||
# Aggressive entries now let us start using this mode, without having to first
|
||||
# fix wide swaths of existing code.
|
||||
#
|
||||
# Entries should be removed 1) as issues are fixed; and 2) as blacklist entries
|
||||
# can be moved out of this centralized file, into source-level blacklist
|
||||
# attributes on individual functions.
|
||||
|
||||
# Ignore common overflows in the C++ std headers
|
||||
# All entries in this file are to suppress unsigned-integer-overflow problems.
|
||||
# Blacklists for other reasons should go in separate blacklist files.
|
||||
[unsigned-integer-overflow]
|
||||
|
||||
# Overflows in the C++ std headers aren't necessarily bugs, because code inside
|
||||
# a language implementation can depend on compiler-specific behavior where C/C++
|
||||
# leave the behavior undefined.
|
||||
src:*bits/basic_string.h
|
||||
|
||||
# Assume everything running through CheckedInt.h is ok. The CheckedInt class
|
|
@ -0,0 +1,5 @@
|
|||
%include build/sparse-profiles/mach
|
||||
|
||||
[include]
|
||||
path:build/mozrelease
|
||||
path:testing/mozharness
|
|
@ -1015,9 +1015,6 @@ MarkupView.prototype = {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (type === "attributes" && mutation.attributeName === "class") {
|
||||
container.updateIsDisplayed();
|
||||
}
|
||||
if (type === "attributes" || type === "characterData"
|
||||
|| type === "events" || type === "pseudoClassLock") {
|
||||
container.update();
|
||||
|
@ -1062,7 +1059,7 @@ MarkupView.prototype = {
|
|||
for (let node of nodes) {
|
||||
let container = this.getContainer(node);
|
||||
if (container) {
|
||||
container.updateIsDisplayed();
|
||||
container.update();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -95,6 +95,8 @@ skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32
|
|||
[browser_markup_css_completion_style_attribute_01.js]
|
||||
[browser_markup_css_completion_style_attribute_02.js]
|
||||
[browser_markup_css_completion_style_attribute_03.js]
|
||||
[browser_markup_display_node_01.js]
|
||||
[browser_markup_display_node_02.js]
|
||||
[browser_markup_dragdrop_autoscroll_01.js]
|
||||
[browser_markup_dragdrop_autoscroll_02.js]
|
||||
[browser_markup_dragdrop_distance.js]
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Tests that markup display node shows for only for grid and flex containers.
|
||||
|
||||
const TEST_URI = `
|
||||
<style type="text/css">
|
||||
#grid {
|
||||
display: grid;
|
||||
}
|
||||
#flex {
|
||||
display: flex;
|
||||
}
|
||||
#block {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
<div id="grid">Grid</div>
|
||||
<div id="flex">Flex</div>
|
||||
<div id="block">Block</div>
|
||||
<span>HELLO WORLD</span>
|
||||
`;
|
||||
|
||||
add_task(function* () {
|
||||
let {inspector} = yield openInspectorForURL("data:text/html;charset=utf-8," +
|
||||
encodeURIComponent(TEST_URI));
|
||||
|
||||
info("Check the display node is shown and the value of #grid.");
|
||||
yield selectNode("#grid", inspector);
|
||||
let gridContainer = yield getContainerForSelector("#grid", inspector);
|
||||
let gridDisplayNode = gridContainer.elt.querySelector(".markupview-display");
|
||||
is(gridDisplayNode.textContent, "grid", "Got the correct display type for #grid.");
|
||||
is(gridDisplayNode.style.display, "inline-block", "#grid display node is shown.");
|
||||
|
||||
info("Check the display node is shown and the value of #flex.");
|
||||
yield selectNode("#flex", inspector);
|
||||
let flexContainer = yield getContainerForSelector("#flex", inspector);
|
||||
let flexDisplayNode = flexContainer.elt.querySelector(".markupview-display");
|
||||
is(flexDisplayNode.textContent, "flex", "Got the correct display type for #flex");
|
||||
is(flexDisplayNode.style.display, "inline-block", "#flex display node is shown.");
|
||||
|
||||
info("Check the display node is shown and the value of #block.");
|
||||
yield selectNode("#block", inspector);
|
||||
let blockContainer = yield getContainerForSelector("#block", inspector);
|
||||
let blockDisplayNode = blockContainer.elt.querySelector(".markupview-display");
|
||||
is(blockDisplayNode.textContent, "block", "Got the correct display type for #block");
|
||||
is(blockDisplayNode.style.display, "none", "#block display node is hidden.");
|
||||
|
||||
info("Check the display node is shown and the value of span.");
|
||||
yield selectNode("span", inspector);
|
||||
let spanContainer = yield getContainerForSelector("span", inspector);
|
||||
let spanDisplayNode = spanContainer.elt.querySelector(".markupview-display");
|
||||
is(spanDisplayNode.textContent, "inline", "Got the correct display type for #span");
|
||||
is(spanDisplayNode.style.display, "none", "span display node is hidden.");
|
||||
});
|
|
@ -0,0 +1,129 @@
|
|||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Tests that markup display node are updated when their display changes.
|
||||
|
||||
const TEST_URI = `
|
||||
<style type="text/css">
|
||||
#grid {
|
||||
display: grid;
|
||||
}
|
||||
#flex {
|
||||
display: flex;
|
||||
}
|
||||
#flex[hidden] {
|
||||
display: none;
|
||||
}
|
||||
#block {
|
||||
display: block;
|
||||
}
|
||||
#flex
|
||||
</style>
|
||||
<div id="grid">Grid</div>
|
||||
<div id="flex" hidden="">Flex</div>
|
||||
<div id="block">Block</div>
|
||||
`;
|
||||
|
||||
const TEST_DATA = [
|
||||
{
|
||||
desc: "Hiding the #grid display node by changing its style property",
|
||||
selector: "#grid",
|
||||
before: {
|
||||
textContent: "grid",
|
||||
display: "inline-block"
|
||||
},
|
||||
changeStyle: function* (testActor) {
|
||||
yield testActor.eval(`
|
||||
let node = document.getElementById("grid");
|
||||
node.style.display = "block";
|
||||
`);
|
||||
},
|
||||
after: {
|
||||
textContent: "block",
|
||||
display: "none"
|
||||
}
|
||||
},
|
||||
{
|
||||
desc: "Showing a 'grid' node by changing its style property",
|
||||
selector: "#block",
|
||||
before: {
|
||||
textContent: "block",
|
||||
display: "none"
|
||||
},
|
||||
changeStyle: function* (testActor) {
|
||||
yield testActor.eval(`
|
||||
let node = document.getElementById("block");
|
||||
node.style.display = "grid";
|
||||
`);
|
||||
},
|
||||
after: {
|
||||
textContent: "grid",
|
||||
display: "inline-block"
|
||||
}
|
||||
},
|
||||
{
|
||||
desc: "Showing a 'flex' node by removing its hidden attribute",
|
||||
selector: "#flex",
|
||||
before: {
|
||||
textContent: "none",
|
||||
display: "none"
|
||||
},
|
||||
changeStyle: function* (testActor) {
|
||||
yield testActor.eval(`
|
||||
document.getElementById("flex").removeAttribute("hidden");
|
||||
`);
|
||||
},
|
||||
after: {
|
||||
textContent: "flex",
|
||||
display: "inline-block"
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
add_task(function* () {
|
||||
let {inspector, testActor} = yield openInspectorForURL("data:text/html;charset=utf-8," +
|
||||
encodeURIComponent(TEST_URI));
|
||||
|
||||
for (let data of TEST_DATA) {
|
||||
info("Running test case: " + data.desc);
|
||||
yield runTestData(inspector, testActor, data);
|
||||
}
|
||||
});
|
||||
|
||||
function* runTestData(inspector, testActor,
|
||||
{selector, before, changeStyle, after}) {
|
||||
yield selectNode(selector, inspector);
|
||||
let container = yield getContainerForSelector(selector, inspector);
|
||||
let displayNode = container.elt.querySelector(".markupview-display");
|
||||
|
||||
is(displayNode.textContent, before.textContent,
|
||||
`Got the correct before display type for ${selector}: ${displayNode.textContent}`);
|
||||
is(displayNode.style.display, before.display,
|
||||
`Got the correct before display style for ${selector}: ${displayNode.style.display}`);
|
||||
|
||||
info("Listening for the display-change event");
|
||||
let onDisplayChanged = inspector.markup.walker.once("display-change");
|
||||
info("Making style changes");
|
||||
yield changeStyle(testActor);
|
||||
let nodes = yield onDisplayChanged;
|
||||
|
||||
info("Verifying that the list of changed nodes include our container");
|
||||
ok(nodes.length, "The display-change event was received with a nodes");
|
||||
let foundContainer = false;
|
||||
for (let node of nodes) {
|
||||
if (getContainerForNodeFront(node, inspector) === container) {
|
||||
foundContainer = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ok(foundContainer, "Container is part of the list of changed nodes");
|
||||
|
||||
is(displayNode.textContent, after.textContent,
|
||||
`Got the correct after display type for ${selector}: ${displayNode.textContent}`);
|
||||
is(displayNode.style.display, after.display,
|
||||
`Got the correct after display style for ${selector}: ${displayNode.style.display}`);
|
||||
}
|
||||
|
|
@ -19,6 +19,11 @@ const {parseAttribute} =
|
|||
require("devtools/client/shared/node-attribute-parser");
|
||||
const {getCssProperties} = require("devtools/shared/fronts/css-properties");
|
||||
|
||||
// Global tooltip inspector
|
||||
const {LocalizationHelper} = require("devtools/shared/l10n");
|
||||
const INSPECTOR_L10N =
|
||||
new LocalizationHelper("devtools/client/locales/inspector.properties");
|
||||
|
||||
// Page size for pageup/pagedown
|
||||
const COLLAPSE_DATA_URL_REGEX = /^data.+base64/;
|
||||
const COLLAPSE_DATA_URL_LENGTH = 60;
|
||||
|
@ -27,12 +32,19 @@ const COLLAPSE_DATA_URL_LENGTH = 60;
|
|||
const HTML_VOID_ELEMENTS = [
|
||||
"area", "base", "br", "col", "command", "embed",
|
||||
"hr", "img", "input", "keygen", "link", "meta", "param", "source",
|
||||
"track", "wbr" ];
|
||||
"track", "wbr"
|
||||
];
|
||||
|
||||
// Global tooltip inspector
|
||||
const {LocalizationHelper} = require("devtools/shared/l10n");
|
||||
const INSPECTOR_L10N =
|
||||
new LocalizationHelper("devtools/client/locales/inspector.properties");
|
||||
// Contains only valid computed display property types of the node to display in the
|
||||
// element markup and their respective title tooltip text.
|
||||
const DISPLAY_TYPES = {
|
||||
"flex": INSPECTOR_L10N.getStr("markupView.display.flex.tooltiptext"),
|
||||
"inline-flex": INSPECTOR_L10N.getStr("markupView.display.flex.tooltiptext"),
|
||||
"grid": INSPECTOR_L10N.getStr("markupView.display.grid.tooltiptext"),
|
||||
"inline-grid": INSPECTOR_L10N.getStr("markupView.display.inlineGrid.tooltiptext"),
|
||||
"flow-root": INSPECTOR_L10N.getStr("markupView.display.flowRoot.tooltiptext"),
|
||||
"contents": INSPECTOR_L10N.getStr("markupView.display.contents.tooltiptext"),
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates an editor for an Element node.
|
||||
|
@ -162,6 +174,10 @@ ElementEditor.prototype = {
|
|||
this.eventNode.textContent = "ev";
|
||||
this.eventNode.title = INSPECTOR_L10N.getStr("markupView.event.tooltiptext");
|
||||
this.elt.appendChild(this.eventNode);
|
||||
|
||||
this.displayNode = this.doc.createElement("div");
|
||||
this.displayNode.classList.add("markupview-display");
|
||||
this.elt.appendChild(this.displayNode);
|
||||
},
|
||||
|
||||
set selected(value) {
|
||||
|
@ -253,8 +269,14 @@ ElementEditor.prototype = {
|
|||
}
|
||||
|
||||
// Update the event bubble display
|
||||
this.eventNode.style.display = this.node.hasEventListeners ?
|
||||
"inline-block" : "none";
|
||||
this.eventNode.style.display = this.node.hasEventListeners ? "inline-block" : "none";
|
||||
|
||||
// Update the display type node
|
||||
let showDisplayNode = this.node.displayType in DISPLAY_TYPES;
|
||||
this.displayNode.textContent = this.node.displayType;
|
||||
this.displayNode.dataset.display = showDisplayNode ? this.node.displayType : "";
|
||||
this.displayNode.style.display = showDisplayNode ? "inline-block" : "none";
|
||||
this.displayNode.title = showDisplayNode ? DISPLAY_TYPES[this.node.displayType] : "";
|
||||
|
||||
this.updateTextEditor();
|
||||
},
|
||||
|
|
|
@ -718,6 +718,8 @@ MarkupContainer.prototype = {
|
|||
this.elt.classList.remove("pseudoclass-locked");
|
||||
}
|
||||
|
||||
this.updateIsDisplayed();
|
||||
|
||||
if (this.editor.update) {
|
||||
this.editor.update();
|
||||
}
|
||||
|
@ -735,8 +737,9 @@ MarkupContainer.prototype = {
|
|||
},
|
||||
|
||||
_onToggle: function (event) {
|
||||
// Prevent the html tree from expanding when an event bubble is clicked.
|
||||
if (event.target.dataset.event) {
|
||||
// Prevent the html tree from expanding when an event bubble or display node is
|
||||
// clicked.
|
||||
if (event.target.dataset.event || event.target.dataset.display) {
|
||||
event.stopPropagation();
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -38,9 +38,39 @@ markupView.more.showAll2=Show one more node;Show all #1 nodes
|
|||
# the inspector.
|
||||
markupView.whitespaceOnly=Whitespace-only text node: %S
|
||||
|
||||
# LOCALIZATION NOTE (markupView.display.flex.tooltiptext)
|
||||
# Used in a tooltip that appears when the user hovers over the display type button in
|
||||
# the markup view.
|
||||
markupView.display.flex.tooltiptext=This element behaves like a block element and lays out its content according to the flexbox model.
|
||||
|
||||
# LOCALIZATION NOTE (markupView.display.inlineFlex.tooltiptext)
|
||||
# Used in a tooltip that appears when the user hovers over the display type button in
|
||||
# the markup view.
|
||||
markupView.display.inlineFlex.tooltiptext=This element behaves like an inline element and lays out its content according to the flexbox model.
|
||||
|
||||
# LOCALIZATION NOTE (markupView.display.grid.tooltiptext)
|
||||
# Used in a tooltip that appears when the user hovers over the display type button in
|
||||
# the markup view.
|
||||
markupView.display.grid.tooltiptext=This element behaves like a block element and lays out its content according to the grid model.
|
||||
|
||||
# LOCALIZATION NOTE (markupView.display.flex.tooltiptext)
|
||||
# Used in a tooltip that appears when the user hovers over the display type button in
|
||||
# the markup view.
|
||||
markupView.display.inlineGrid.tooltiptext=This element behaves like an inline element and lays out its content according to the grid model.
|
||||
|
||||
# LOCALIZATION NOTE (markupView.display.flowRoot.tooltiptext)
|
||||
# Used in a tooltip that appears when the user hovers over the display type button in
|
||||
# the markup view.
|
||||
markupView.display.flowRoot.tooltiptext=This element generates a block element box that establishes a new block formatting context.
|
||||
|
||||
# LOCALIZATION NOTE (markupView.display.contents.tooltiptext)
|
||||
# Used in a tooltip that appears when the user hovers over the display type button in
|
||||
# the markup view.
|
||||
markupView.display.contents.tooltiptext=This element doesn’t produce a specific box by themselves, but renders its contents.
|
||||
|
||||
# LOCALIZATION NOTE (markupView.event.tooltiptext)
|
||||
# Used in a tooltip that appears when the user hovers over 'ev' button in
|
||||
# the inspector.
|
||||
# the markup view.
|
||||
markupView.event.tooltiptext=Event listener
|
||||
|
||||
#LOCALIZATION NOTE: Used in the image preview tooltip when the image could not be loaded
|
||||
|
|
|
@ -263,11 +263,6 @@ ul.children + .tag-line::before {
|
|||
transition: background .5s;
|
||||
}
|
||||
|
||||
.markupview-events {
|
||||
display: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.editor {
|
||||
/* Make sure the editor still appears above the tag-state */
|
||||
position: relative;
|
||||
|
@ -373,6 +368,7 @@ ul.children + .tag-line::before {
|
|||
color: var(--theme-highlight-red);
|
||||
}
|
||||
|
||||
.theme-firebug .markupview-display,
|
||||
.theme-firebug .markupview-events {
|
||||
font-size: var(--theme-toolbar-font-size);
|
||||
}
|
||||
|
@ -414,8 +410,10 @@ ul.children + .tag-line::before {
|
|||
color: #787878;
|
||||
}
|
||||
|
||||
/* Events */
|
||||
/* Display and Events Bubble */
|
||||
.markupview-display,
|
||||
.markupview-events {
|
||||
display: none;
|
||||
font-size: 8px;
|
||||
font-weight: bold;
|
||||
line-height: 10px;
|
||||
|
@ -423,9 +421,10 @@ ul.children + .tag-line::before {
|
|||
padding: 0px 2px;
|
||||
margin-inline-start: 5px;
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
.markupview-events {
|
||||
background-color: var(--theme-body-color-alt);
|
||||
color: var(--theme-body-background);
|
||||
}
|
||||
|
||||
.markupview-events {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
|
|
@ -368,6 +368,7 @@
|
|||
}
|
||||
|
||||
.ruleview-ruleclose {
|
||||
clear: both;
|
||||
cursor: text;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
@ -557,7 +558,7 @@
|
|||
|
||||
.ruleview-property {
|
||||
border-left: 3px solid transparent;
|
||||
clear: right;
|
||||
clear: both;
|
||||
padding-left: 28px;
|
||||
}
|
||||
|
||||
|
|
|
@ -44,8 +44,9 @@ const NodeActor = protocol.ActorClassWithSpec(nodeSpec, {
|
|||
this.rawNode = node;
|
||||
this._eventParsers = new EventParsers().parsers;
|
||||
|
||||
// Storing the original display of the node, to track changes when reflows
|
||||
// occur
|
||||
// Store the original display type and whether or not the node is displayed to
|
||||
// track changes when reflows occur.
|
||||
this.currentDisplayType = this.displayType;
|
||||
this.wasDisplayed = this.isDisplayed;
|
||||
},
|
||||
|
||||
|
@ -100,6 +101,7 @@ const NodeActor = protocol.ActorClassWithSpec(nodeSpec, {
|
|||
displayName: InspectorActorUtils.getNodeDisplayName(this.rawNode),
|
||||
numChildren: this.numChildren,
|
||||
inlineTextChild: inlineTextChild ? inlineTextChild.form() : undefined,
|
||||
displayType: this.displayType,
|
||||
|
||||
// doctype attributes
|
||||
name: this.rawNode.name,
|
||||
|
@ -204,7 +206,30 @@ const NodeActor = protocol.ActorClassWithSpec(nodeSpec, {
|
|||
},
|
||||
|
||||
get computedStyle() {
|
||||
return CssLogic.getComputedStyle(this.rawNode);
|
||||
if (!this._computedStyle) {
|
||||
this._computedStyle = CssLogic.getComputedStyle(this.rawNode);
|
||||
}
|
||||
return this._computedStyle;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the computed display style property value of the node.
|
||||
*/
|
||||
get displayType() {
|
||||
// Consider all non-element nodes as displayed.
|
||||
if (InspectorActorUtils.isNodeDead(this) ||
|
||||
this.rawNode.nodeType !== Ci.nsIDOMNode.ELEMENT_NODE ||
|
||||
this.isAfterPseudoElement ||
|
||||
this.isBeforePseudoElement) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let style = this.computedStyle;
|
||||
if (!style) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return style.display;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -213,9 +238,7 @@ const NodeActor = protocol.ActorClassWithSpec(nodeSpec, {
|
|||
get isDisplayed() {
|
||||
// Consider all non-element nodes as displayed.
|
||||
if (InspectorActorUtils.isNodeDead(this) ||
|
||||
this.rawNode.nodeType !== Ci.nsIDOMNode.ELEMENT_NODE ||
|
||||
this.isAfterPseudoElement ||
|
||||
this.isBeforePseudoElement) {
|
||||
this.rawNode.nodeType !== Ci.nsIDOMNode.ELEMENT_NODE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -311,10 +311,15 @@ var WalkerActor = protocol.ActorClassWithSpec(walkerSpec, {
|
|||
continue;
|
||||
}
|
||||
|
||||
let displayType = actor.displayType;
|
||||
let isDisplayed = actor.isDisplayed;
|
||||
if (isDisplayed !== actor.wasDisplayed) {
|
||||
|
||||
if (displayType !== actor.currentDisplayType ||
|
||||
isDisplayed !== actor.wasDisplayed) {
|
||||
changes.push(actor);
|
||||
|
||||
// Updating the original value
|
||||
actor.currentDisplayType = displayType;
|
||||
actor.wasDisplayed = isDisplayed;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ support-files =
|
|||
iframe1_makeGlobalObjectReference.html
|
||||
iframe2_makeGlobalObjectReference.html
|
||||
inspector_css-properties.html
|
||||
inspector_display-type.html
|
||||
inspector_getImageData.html
|
||||
inspector_getOffsetParent.html
|
||||
inspector-delay-image-response.sjs
|
||||
|
@ -53,6 +54,7 @@ support-files =
|
|||
[test_inspector-changeattrs.html]
|
||||
[test_inspector-changevalue.html]
|
||||
[test_inspector-dead-nodes.html]
|
||||
[test_inspector-display-type.html]
|
||||
[test_inspector-duplicate-node.html]
|
||||
[test_inspector_getImageData.html]
|
||||
[test_inspector_getImageDataFromURL.html]
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
<html>
|
||||
<head>
|
||||
<body>
|
||||
<div id="inline-block" style="display: inline-block">
|
||||
HELLO WORLD
|
||||
</div>
|
||||
<div id="grid" style="display: grid"></div>
|
||||
<div id="block" style="position: fixed"></div>
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
window.onload = () => {
|
||||
window.opener.postMessage("ready", "*");
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,85 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1431900
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 1431900</title>
|
||||
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
<script type="application/javascript" src="inspector-helpers.js"></script>
|
||||
<script type="application/javascript">
|
||||
"use strict";
|
||||
|
||||
window.onload = function () {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
runNextTest();
|
||||
};
|
||||
|
||||
var gWalker;
|
||||
|
||||
addTest(function setup() {
|
||||
let url = document.getElementById("inspectorContent").href;
|
||||
attachURL(url, function (err, client, tab, doc) {
|
||||
let {InspectorFront} = require("devtools/shared/fronts/inspector");
|
||||
let inspector = InspectorFront(client, tab);
|
||||
|
||||
promiseDone(inspector.getWalker().then(walker => {
|
||||
gWalker = walker;
|
||||
}).then(runNextTest));
|
||||
});
|
||||
});
|
||||
|
||||
addAsyncTest(function* testInlineBlockDisplayType() {
|
||||
info("Test getting the display type of an inline block element.");
|
||||
let node = yield gWalker.querySelector(gWalker.rootNode, "#inline-block");
|
||||
let displayType = node.displayType;
|
||||
is(displayType, "inline-block", "The node has a display type of 'inline-block'.");
|
||||
runNextTest();
|
||||
});
|
||||
|
||||
addAsyncTest(function* testInlineTextChildDisplayType() {
|
||||
info("Test getting the display type of an inline text child.");
|
||||
let node = yield gWalker.querySelector(gWalker.rootNode, "#inline-block");
|
||||
let children = yield gWalker.children(node);
|
||||
let inlineTextChild = children.nodes[0];
|
||||
let displayType = inlineTextChild.displayType;
|
||||
ok(!displayType, "No display type for inline text child.");
|
||||
runNextTest();
|
||||
});
|
||||
|
||||
addAsyncTest(function* testGridDisplayType() {
|
||||
info("Test getting the display type of an grid container.");
|
||||
let node = yield gWalker.querySelector(gWalker.rootNode, "#grid");
|
||||
let displayType = node.displayType;
|
||||
is(displayType, "grid", "The node has a display type of 'grid'.");
|
||||
runNextTest();
|
||||
});
|
||||
|
||||
addAsyncTest(function* testBlockDisplayType() {
|
||||
info("Test getting the display type of a block element.");
|
||||
let node = yield gWalker.querySelector(gWalker.rootNode, "#block");
|
||||
let displayType = yield node.displayType;
|
||||
is(displayType, "block", "The node has a display type of 'block'.");
|
||||
runNextTest();
|
||||
});
|
||||
|
||||
addTest(function () {
|
||||
gWalker = null;
|
||||
runNextTest();
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1431900">Mozilla Bug 1431900</a>
|
||||
<a id="inspectorContent" target="_blank" href="inspector_display-type.html">Test Document</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -331,6 +331,10 @@ const NodeFront = FrontClassWithSpec(nodeSpec, {
|
|||
return this.pseudoClassLocks.some(locked => locked === pseudo);
|
||||
},
|
||||
|
||||
get displayType() {
|
||||
return this._form.displayType;
|
||||
},
|
||||
|
||||
get isDisplayed() {
|
||||
// The NodeActor's form contains the isDisplayed information as a boolean
|
||||
// starting from FF32. Before that, the property is missing
|
||||
|
|
|
@ -698,7 +698,7 @@ nsPlainTextSerializer::DoOpenContainer(nsAtom* aTag)
|
|||
|
||||
}
|
||||
else {
|
||||
static char bulletCharArray[] = "*o+#";
|
||||
static const char bulletCharArray[] = "*o+#";
|
||||
uint32_t index = mULCount > 0 ? (mULCount - 1) : 3;
|
||||
char bulletChar = bulletCharArray[index % 4];
|
||||
mInIndentString.Append(char16_t(bulletChar));
|
||||
|
|
|
@ -144,11 +144,6 @@ DOMInterfaces = {
|
|||
'concrete': False
|
||||
},
|
||||
|
||||
'ChromeWorker': {
|
||||
'headerFile': 'mozilla/dom/WorkerPrivate.h',
|
||||
'nativeType': 'mozilla::dom::ChromeWorkerPrivate',
|
||||
},
|
||||
|
||||
'console': {
|
||||
'nativeType': 'mozilla::dom::Console',
|
||||
},
|
||||
|
@ -1351,11 +1346,6 @@ DOMInterfaces = {
|
|||
'nativeType': 'nsWindowRoot'
|
||||
},
|
||||
|
||||
'Worker': {
|
||||
'headerFile': 'mozilla/dom/WorkerPrivate.h',
|
||||
'nativeType': 'mozilla::dom::WorkerPrivate',
|
||||
},
|
||||
|
||||
'WorkerDebuggerGlobalScope': {
|
||||
'headerFile': 'mozilla/dom/WorkerScope.h',
|
||||
'implicitJSContext': [
|
||||
|
|
|
@ -1387,9 +1387,10 @@ nsTextEditorState::PrepareEditor(const nsAString *aValue)
|
|||
// editor's Init() call.
|
||||
|
||||
// Get the DOM document
|
||||
nsCOMPtr<nsIDOMDocument> domdoc = do_QueryInterface(shell->GetDocument());
|
||||
if (!domdoc)
|
||||
nsCOMPtr<nsIDocument> doc = shell->GetDocument();
|
||||
if (NS_WARN_IF(!doc)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// What follows is a bit of a hack. The editor uses the public DOM APIs
|
||||
// for its content manipulations, and it causes it to fail some security
|
||||
|
@ -1400,7 +1401,7 @@ nsTextEditorState::PrepareEditor(const nsAString *aValue)
|
|||
// already does the relevant security checks.
|
||||
AutoNoJSAPI nojsapi;
|
||||
|
||||
rv = newTextEditor->Init(domdoc, GetRootNode(), mSelCon, editorFlags,
|
||||
rv = newTextEditor->Init(*doc, GetRootNode(), mSelCon, editorFlags,
|
||||
defaultValue);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
|
|
@ -1264,18 +1264,24 @@ protected:
|
|||
bool PostToDispatchThread(uint32_t& aWinError, ResultType& aRet, ParamTypes&... aParameters) const;
|
||||
|
||||
static void
|
||||
PostToDispatchHelper(const SelfType* bmhi, Monitor* monitor, bool* ok, uint32_t* winErr, ResultType* r, ParamTypes*... p)
|
||||
PostToDispatchHelper(const SelfType* bmhi, Monitor* monitor, bool* notified,
|
||||
bool* ok, uint32_t* winErr, ResultType* r, ParamTypes*... p)
|
||||
{
|
||||
// Note: p is also non-null... its just hard to assert that.
|
||||
MOZ_ASSERT(bmhi && monitor && ok && winErr && r);
|
||||
MOZ_ASSERT(bmhi && monitor && notified && ok && winErr && r);
|
||||
MOZ_ASSERT(*notified == false);
|
||||
*ok = bmhi->BrokerCallClient(*winErr, *r, *p...);
|
||||
{
|
||||
// By grabbing (and freeing) the lock, we make sure that Wait() has been
|
||||
// called in PostToDispatchThread. We need that since we wake it with
|
||||
// Notify().
|
||||
MonitorAutoLock lock(*monitor);
|
||||
}
|
||||
*ok &= NS_SUCCEEDED(monitor->Notify());
|
||||
|
||||
// We need to grab the lock to make sure that Wait() has been
|
||||
// called in PostToDispatchThread. We need that since we wake it with
|
||||
// Notify().
|
||||
// We also need to keep the lock until _after_ Notify() has been called
|
||||
// since, after we set notified to true, a spurious wakeup could lead
|
||||
// the other thread to wake and proceed -- and one of its first acts would
|
||||
// be to destroy the Monitor.
|
||||
MonitorAutoLock lock(*monitor);
|
||||
*notified = true;
|
||||
monitor->Notify();
|
||||
};
|
||||
|
||||
template<typename ... VarParams>
|
||||
|
@ -1446,11 +1452,17 @@ PostToDispatchThread(uint32_t& aWinError, ResultType& aRet,
|
|||
Monitor monitor("FunctionDispatchThread Lock");
|
||||
MonitorAutoLock lock(monitor);
|
||||
bool success = false;
|
||||
bool notified = false;
|
||||
FunctionBrokerChild::GetInstance()->PostToDispatchThread(
|
||||
NewRunnableFunction("FunctionDispatchThreadRunnable", &PostToDispatchHelper,
|
||||
this, &monitor, &success, &aWinError, &aRet,
|
||||
this, &monitor, ¬ified, &success, &aWinError, &aRet,
|
||||
&aParameters...));
|
||||
monitor.Wait();
|
||||
|
||||
// We wait to be notified, testing that notified was actually set to make
|
||||
// sure this isn't a spurious wakeup.
|
||||
while (!notified) {
|
||||
monitor.Wait();
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* The origin of this IDL file is
|
||||
* http://www.whatwg.org/specs/web-apps/current-work/multipage/workers.html
|
||||
* https://html.spec.whatwg.org/multipage/workers.html
|
||||
*
|
||||
* © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and Opera
|
||||
* Software ASA.
|
||||
|
@ -33,7 +33,7 @@ dictionary WorkerOptions {
|
|||
};
|
||||
|
||||
[Constructor(USVString scriptURL),
|
||||
Func="mozilla::dom::ChromeWorkerPrivate::WorkerAvailable",
|
||||
Func="mozilla::dom::ChromeWorker::WorkerAvailable",
|
||||
Exposed=(Window,DedicatedWorker,SharedWorker,System)]
|
||||
interface ChromeWorker : Worker {
|
||||
};
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ChromeWorker.h"
|
||||
|
||||
#include "mozilla/dom/WorkerBinding.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "WorkerPrivate.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
/* static */ already_AddRefed<ChromeWorker>
|
||||
ChromeWorker::Constructor(const GlobalObject& aGlobal,
|
||||
const nsAString& aScriptURL,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
JSContext* cx = aGlobal.Context();
|
||||
|
||||
RefPtr<WorkerPrivate> workerPrivate =
|
||||
WorkerPrivate::Constructor(cx, aScriptURL, true /* aIsChromeWorker */,
|
||||
WorkerTypeDedicated, EmptyString(),
|
||||
VoidCString(), nullptr /*aLoadInfo */, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> globalObject =
|
||||
do_QueryInterface(aGlobal.GetAsSupports());
|
||||
|
||||
RefPtr<ChromeWorker> worker =
|
||||
new ChromeWorker(globalObject, workerPrivate.forget());
|
||||
return worker.forget();
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
ChromeWorker::WorkerAvailable(JSContext* aCx, JSObject* /* unused */)
|
||||
{
|
||||
// Chrome is always allowed to use workers, and content is never
|
||||
// allowed to use ChromeWorker, so all we have to check is the
|
||||
// caller. However, chrome workers apparently might not have a
|
||||
// system principal, so we have to check for them manually.
|
||||
if (NS_IsMainThread()) {
|
||||
return nsContentUtils::IsSystemCaller(aCx);
|
||||
}
|
||||
|
||||
return GetWorkerPrivateFromContext(aCx)->IsChromeWorker();
|
||||
}
|
||||
|
||||
ChromeWorker::ChromeWorker(nsIGlobalObject* aGlobalObject,
|
||||
already_AddRefed<WorkerPrivate> aWorkerPrivate)
|
||||
: Worker(aGlobalObject, Move(aWorkerPrivate))
|
||||
{}
|
||||
|
||||
ChromeWorker::~ChromeWorker() = default;
|
||||
|
||||
JSObject*
|
||||
ChromeWorker::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
JS::Rooted<JSObject*> wrapper(aCx,
|
||||
ChromeWorkerBinding::Wrap(aCx, this, aGivenProto));
|
||||
if (wrapper) {
|
||||
// Most DOM objects don't assume they have a reflector. If they don't have
|
||||
// one and need one, they create it. But in workers code, we assume that the
|
||||
// reflector is always present. In order to guarantee that it's always
|
||||
// present, we have to preserve it. Otherwise the GC will happily collect it
|
||||
// as needed.
|
||||
MOZ_ALWAYS_TRUE(TryPreserveWrapper(wrapper));
|
||||
}
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
|
@ -0,0 +1,37 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_ChromeWorker_h
|
||||
#define mozilla_dom_ChromeWorker_h
|
||||
|
||||
#include "mozilla/dom/Worker.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class ChromeWorker final : public Worker
|
||||
{
|
||||
public:
|
||||
static already_AddRefed<ChromeWorker>
|
||||
Constructor(const GlobalObject& aGlobal, const nsAString& aScriptURL,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static bool
|
||||
WorkerAvailable(JSContext* aCx, JSObject* /* unused */);
|
||||
|
||||
JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
private:
|
||||
ChromeWorker(nsIGlobalObject* aGlobalObject,
|
||||
already_AddRefed<WorkerPrivate> aWorkerPrivate);
|
||||
~ChromeWorker();
|
||||
};
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
||||
|
||||
#endif /* mozilla_dom_ChromeWorker_h */
|
|
@ -126,7 +126,8 @@ MessageEventRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
|||
|
||||
aWorkerPrivate->AssertInnerWindowIsCorrect();
|
||||
|
||||
return DispatchDOMEvent(aCx, aWorkerPrivate, aWorkerPrivate,
|
||||
return DispatchDOMEvent(aCx, aWorkerPrivate,
|
||||
aWorkerPrivate->ParentEventTargetRef(),
|
||||
!aWorkerPrivate->GetParent());
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "Worker.h"
|
||||
|
||||
#include "MessageEventRunnable.h"
|
||||
#include "mozilla/dom/WorkerBinding.h"
|
||||
#include "mozilla/TimelineConsumers.h"
|
||||
#include "mozilla/WorkerTimelineMarker.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "WorkerPrivate.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
/* static */ already_AddRefed<Worker>
|
||||
Worker::Constructor(const GlobalObject& aGlobal, const nsAString& aScriptURL,
|
||||
const WorkerOptions& aOptions, ErrorResult& aRv)
|
||||
{
|
||||
JSContext* cx = aGlobal.Context();
|
||||
|
||||
RefPtr<WorkerPrivate> workerPrivate =
|
||||
WorkerPrivate::Constructor(cx, aScriptURL, false /* aIsChromeWorker */,
|
||||
WorkerTypeDedicated, aOptions.mName,
|
||||
VoidCString(), nullptr /*aLoadInfo */, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> globalObject =
|
||||
do_QueryInterface(aGlobal.GetAsSupports());
|
||||
|
||||
RefPtr<Worker> worker = new Worker(globalObject, workerPrivate.forget());
|
||||
return worker.forget();
|
||||
}
|
||||
|
||||
Worker::Worker(nsIGlobalObject* aGlobalObject,
|
||||
already_AddRefed<WorkerPrivate> aWorkerPrivate)
|
||||
: DOMEventTargetHelper(aGlobalObject)
|
||||
, mWorkerPrivate(Move(aWorkerPrivate))
|
||||
{
|
||||
MOZ_ASSERT(mWorkerPrivate);
|
||||
mWorkerPrivate->SetParentEventTargetRef(this);
|
||||
}
|
||||
|
||||
Worker::~Worker()
|
||||
{
|
||||
Terminate();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
Worker::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
JS::Rooted<JSObject*> wrapper(aCx,
|
||||
WorkerBinding::Wrap(aCx, this, aGivenProto));
|
||||
if (wrapper) {
|
||||
// Most DOM objects don't assume they have a reflector. If they don't have
|
||||
// one and need one, they create it. But in workers code, we assume that the
|
||||
// reflector is always present. In order to guarantee that it's always
|
||||
// present, we have to preserve it. Otherwise the GC will happily collect it
|
||||
// as needed.
|
||||
MOZ_ALWAYS_TRUE(TryPreserveWrapper(wrapper));
|
||||
}
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
void
|
||||
Worker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
const Sequence<JSObject*>& aTransferable,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Worker);
|
||||
|
||||
if (!mWorkerPrivate ||
|
||||
mWorkerPrivate->ParentStatusProtected() > Running) {
|
||||
return;
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
|
||||
|
||||
aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransferable,
|
||||
&transferable);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<MessageEventRunnable> runnable =
|
||||
new MessageEventRunnable(mWorkerPrivate,
|
||||
WorkerRunnable::WorkerThreadModifyBusyCount);
|
||||
|
||||
UniquePtr<AbstractTimelineMarker> start;
|
||||
UniquePtr<AbstractTimelineMarker> end;
|
||||
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
|
||||
bool isTimelineRecording = timelines && !timelines->IsEmpty();
|
||||
|
||||
if (isTimelineRecording) {
|
||||
start = MakeUnique<WorkerTimelineMarker>(NS_IsMainThread()
|
||||
? ProfileTimelineWorkerOperationType::SerializeDataOnMainThread
|
||||
: ProfileTimelineWorkerOperationType::SerializeDataOffMainThread,
|
||||
MarkerTracingType::START);
|
||||
}
|
||||
|
||||
runnable->Write(aCx, aMessage, transferable, JS::CloneDataPolicy(), aRv);
|
||||
|
||||
if (isTimelineRecording) {
|
||||
end = MakeUnique<WorkerTimelineMarker>(NS_IsMainThread()
|
||||
? ProfileTimelineWorkerOperationType::SerializeDataOnMainThread
|
||||
: ProfileTimelineWorkerOperationType::SerializeDataOffMainThread,
|
||||
MarkerTracingType::END);
|
||||
timelines->AddMarkerForAllObservedDocShells(start);
|
||||
timelines->AddMarkerForAllObservedDocShells(end);
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!runnable->Dispatch()) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Worker::Terminate()
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(Worker);
|
||||
|
||||
if (mWorkerPrivate) {
|
||||
mWorkerPrivate->Terminate();
|
||||
mWorkerPrivate = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(Worker)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(Worker, DOMEventTargetHelper)
|
||||
if (tmp->mWorkerPrivate) {
|
||||
tmp->mWorkerPrivate->Traverse(cb);
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(Worker, DOMEventTargetHelper)
|
||||
tmp->Terminate();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(Worker, DOMEventTargetHelper)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Worker)
|
||||
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(Worker, DOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(Worker, DOMEventTargetHelper)
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
|
@ -0,0 +1,64 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_Worker_h
|
||||
#define mozilla_dom_Worker_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/WeakPtr.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
#undef PostMessage
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
struct WorkerOptions;
|
||||
class WorkerPrivate;
|
||||
|
||||
class Worker : public DOMEventTargetHelper
|
||||
, public SupportsWeakPtr<Worker>
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(Worker,
|
||||
DOMEventTargetHelper)
|
||||
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(Worker)
|
||||
|
||||
static already_AddRefed<Worker>
|
||||
Constructor(const GlobalObject& aGlobal, const nsAString& aScriptURL,
|
||||
const WorkerOptions& aOptions, ErrorResult& aRv);
|
||||
|
||||
JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
void
|
||||
PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
const Sequence<JSObject*>& aTransferable,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void
|
||||
Terminate();
|
||||
|
||||
IMPL_EVENT_HANDLER(error)
|
||||
IMPL_EVENT_HANDLER(message)
|
||||
IMPL_EVENT_HANDLER(messageerror)
|
||||
|
||||
protected:
|
||||
Worker(nsIGlobalObject* aGlobalObject,
|
||||
already_AddRefed<WorkerPrivate> aWorkerPrivate);
|
||||
~Worker();
|
||||
|
||||
RefPtr<WorkerPrivate> mWorkerPrivate;
|
||||
};
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
||||
|
||||
#endif /* mozilla_dom_Worker_h */
|
|
@ -35,7 +35,7 @@ public:
|
|||
// (if any).
|
||||
static void
|
||||
ReportError(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
bool aFireAtScope, WorkerPrivate* aTarget,
|
||||
bool aFireAtScope, DOMEventTargetHelper* aTarget,
|
||||
const WorkerErrorReport& aReport, uint64_t aInnerWindowId,
|
||||
JS::Handle<JS::Value> aException = JS::NullHandleValue)
|
||||
{
|
||||
|
@ -177,8 +177,6 @@ private:
|
|||
virtual bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
||||
{
|
||||
JS::Rooted<JSObject*> target(aCx, aWorkerPrivate->GetWrapper());
|
||||
|
||||
uint64_t innerWindowId;
|
||||
bool fireAtScope = true;
|
||||
|
||||
|
@ -236,8 +234,9 @@ private:
|
|||
return true;
|
||||
}
|
||||
|
||||
ReportError(aCx, parent, fireAtScope, aWorkerPrivate, mReport,
|
||||
innerWindowId);
|
||||
ReportError(aCx, parent, fireAtScope,
|
||||
aWorkerPrivate->ParentEventTargetRef(),
|
||||
mReport, innerWindowId);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -18,19 +18,19 @@ class WorkerPrivate;
|
|||
* Use this chart to help figure out behavior during each of the closing
|
||||
* statuses. Details below.
|
||||
*
|
||||
* +==============================================================+
|
||||
* | Closing Statuses |
|
||||
* +=============+=============+=================+================+
|
||||
* | status | clear queue | abort execution | close handler |
|
||||
* +=============+=============+=================+================+
|
||||
* | Closing | yes | no | no timeout |
|
||||
* +-------------+-------------+-----------------+----------------+
|
||||
* | Terminating | yes | yes | no timeout |
|
||||
* +-------------+-------------+-----------------+----------------+
|
||||
* | Canceling | yes | yes | short duration |
|
||||
* +-------------+-------------+-----------------+----------------+
|
||||
* | Killing | yes | yes | doesn't run |
|
||||
* +-------------+-------------+-----------------+----------------+
|
||||
* +=============================================+
|
||||
* | Closing Statuses |
|
||||
* +=============+=============+=================+
|
||||
* | status | clear queue | abort execution |
|
||||
* +=============+=============+=================+
|
||||
* | Closing | yes | no |
|
||||
* +-------------+-------------+-----------------+
|
||||
* | Terminating | yes | yes |
|
||||
* +-------------+-------------+-----------------+
|
||||
* | Canceling | yes | yes |
|
||||
* +-------------+-------------+-----------------+
|
||||
* | Killing | yes | yes |
|
||||
* +-------------+-------------+-----------------+
|
||||
*/
|
||||
|
||||
#ifdef Status
|
||||
|
@ -43,32 +43,29 @@ enum WorkerStatus
|
|||
// Not yet scheduled.
|
||||
Pending = 0,
|
||||
|
||||
// This status means that the close handler has not yet been scheduled.
|
||||
// This status means that the worker is active.
|
||||
Running,
|
||||
|
||||
// Inner script called close() on the worker global scope. Setting this
|
||||
// status causes the worker to clear its queue of events but does not abort
|
||||
// the currently running script. The close handler is also scheduled with
|
||||
// no expiration time.
|
||||
// the currently running script.
|
||||
Closing,
|
||||
|
||||
// Outer script called terminate() on the worker or the worker object was
|
||||
// garbage collected in its outer script. Setting this status causes the
|
||||
// worker to abort immediately, clear its queue of events, and schedules the
|
||||
// close handler with no expiration time.
|
||||
// worker to abort immediately and clear its queue of events.
|
||||
Terminating,
|
||||
|
||||
// Either the user navigated away from the owning page or the owning page fell
|
||||
// out of bfcache. Setting this status causes the worker to abort immediately
|
||||
// and schedules the close handler with a short expiration time. Since the
|
||||
// page has gone away the worker may not post any messages.
|
||||
// out of bfcache. Setting this status causes the worker to abort immediately.
|
||||
// Since the page has gone away the worker may not post any messages.
|
||||
Canceling,
|
||||
|
||||
// The application is shutting down. Setting this status causes the worker to
|
||||
// abort immediately and the close handler is never scheduled.
|
||||
// abort immediately.
|
||||
Killing,
|
||||
|
||||
// The close handler has run and the worker is effectively dead.
|
||||
// The worker is effectively dead.
|
||||
Dead
|
||||
};
|
||||
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
using namespace workerinternals;
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WorkerNavigator, mStorageManager,
|
||||
mConnection);
|
||||
|
||||
|
|
|
@ -249,7 +249,7 @@ private:
|
|||
|
||||
runtime->UnregisterWorker(mFinishedWorker);
|
||||
|
||||
mFinishedWorker->ClearSelfRef();
|
||||
mFinishedWorker->ClearSelfAndParentEventTargetRef();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
@ -287,7 +287,7 @@ private:
|
|||
NS_WARNING("Failed to dispatch, going to leak!");
|
||||
}
|
||||
|
||||
mFinishedWorker->ClearSelfRef();
|
||||
mFinishedWorker->ClearSelfAndParentEventTargetRef();
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
@ -384,13 +384,15 @@ private:
|
|||
return true;
|
||||
}
|
||||
|
||||
RefPtr<mozilla::dom::EventTarget> parentEventTarget =
|
||||
aWorkerPrivate->ParentEventTargetRef();
|
||||
RefPtr<Event> event =
|
||||
Event::Constructor(aWorkerPrivate, NS_LITERAL_STRING("error"),
|
||||
Event::Constructor(parentEventTarget, NS_LITERAL_STRING("error"),
|
||||
EventInit());
|
||||
event->SetTrusted(true);
|
||||
|
||||
bool dummy;
|
||||
aWorkerPrivate->DispatchEvent(event, &dummy);
|
||||
parentEventTarget->DispatchEvent(event, &dummy);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
@ -1546,14 +1548,6 @@ WorkerPrivateParent<Derived>::SetReferrerPolicyFromHeaderValue(
|
|||
SetReferrerPolicy(policy);
|
||||
}
|
||||
|
||||
|
||||
// Can't use NS_IMPL_CYCLE_COLLECTION_CLASS(WorkerPrivateParent) because of the
|
||||
// templates.
|
||||
template <class Derived>
|
||||
typename WorkerPrivateParent<Derived>::cycleCollection
|
||||
WorkerPrivateParent<Derived>::_cycleCollectorGlobal =
|
||||
WorkerPrivateParent<Derived>::cycleCollection();
|
||||
|
||||
template <class Derived>
|
||||
WorkerPrivateParent<Derived>::WorkerPrivateParent(
|
||||
WorkerPrivate* aParent,
|
||||
|
@ -1576,11 +1570,6 @@ WorkerPrivateParent<Derived>::WorkerPrivateParent(
|
|||
{
|
||||
MOZ_ASSERT_IF(!IsDedicatedWorker(), NS_IsMainThread());
|
||||
|
||||
if (aLoadInfo.mWindow) {
|
||||
AssertIsOnMainThread();
|
||||
BindToOwner(aLoadInfo.mWindow);
|
||||
}
|
||||
|
||||
mLoadInfo.StealFrom(aLoadInfo);
|
||||
|
||||
if (aParent) {
|
||||
|
@ -1644,23 +1633,23 @@ WorkerPrivateParent<Derived>::~WorkerPrivateParent()
|
|||
}
|
||||
|
||||
template <class Derived>
|
||||
JSObject*
|
||||
WorkerPrivateParent<Derived>::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
void
|
||||
WorkerPrivateParent<Derived>::Traverse(nsCycleCollectionTraversalCallback& aCb)
|
||||
{
|
||||
MOZ_ASSERT(!IsSharedWorker(),
|
||||
"We should never wrap a WorkerPrivate for a SharedWorker");
|
||||
|
||||
AssertIsOnParentThread();
|
||||
|
||||
// XXXkhuey this should not need to be rooted, the analysis is dumb.
|
||||
// See bug 980181.
|
||||
JS::Rooted<JSObject*> wrapper(aCx,
|
||||
WorkerBinding::Wrap(aCx, ParentAsWorkerPrivate(), aGivenProto));
|
||||
if (wrapper) {
|
||||
MOZ_ALWAYS_TRUE(TryPreserveWrapper(wrapper));
|
||||
// The WorkerPrivate::mParentEventTargetRef has a reference to the exposed
|
||||
// Worker object, which is really held by the worker thread. We traverse this
|
||||
// reference if and only if our busy count is zero and we have not released
|
||||
// the main thread reference. We do not unlink it. This allows the CC to
|
||||
// break cycles involving the Worker and begin shutting it down (which does
|
||||
// happen in unlink) but ensures that the WorkerPrivate won't be deleted
|
||||
// before we're done shutting down the thread.
|
||||
if (!mBusyCount && !mMainThreadObjectsForgotten) {
|
||||
nsCycleCollectionTraversalCallback& cb = aCb;
|
||||
WorkerPrivateParent<Derived>* tmp = this;
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentEventTargetRef);
|
||||
}
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
template <class Derived>
|
||||
|
@ -2145,76 +2134,6 @@ WorkerPrivateParent<Derived>::ProxyReleaseMainThreadObjects()
|
|||
return result;
|
||||
}
|
||||
|
||||
template <class Derived>
|
||||
void
|
||||
WorkerPrivateParent<Derived>::PostMessageInternal(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aMessage,
|
||||
const Sequence<JSObject*>& aTransferable,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
AssertIsOnParentThread();
|
||||
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
if (mParentStatus > Running) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
|
||||
|
||||
aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransferable,
|
||||
&transferable);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<MessageEventRunnable> runnable =
|
||||
new MessageEventRunnable(ParentAsWorkerPrivate(),
|
||||
WorkerRunnable::WorkerThreadModifyBusyCount);
|
||||
|
||||
UniquePtr<AbstractTimelineMarker> start;
|
||||
UniquePtr<AbstractTimelineMarker> end;
|
||||
RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
|
||||
bool isTimelineRecording = timelines && !timelines->IsEmpty();
|
||||
|
||||
if (isTimelineRecording) {
|
||||
start = MakeUnique<WorkerTimelineMarker>(NS_IsMainThread()
|
||||
? ProfileTimelineWorkerOperationType::SerializeDataOnMainThread
|
||||
: ProfileTimelineWorkerOperationType::SerializeDataOffMainThread,
|
||||
MarkerTracingType::START);
|
||||
}
|
||||
|
||||
runnable->Write(aCx, aMessage, transferable, JS::CloneDataPolicy(), aRv);
|
||||
|
||||
if (isTimelineRecording) {
|
||||
end = MakeUnique<WorkerTimelineMarker>(NS_IsMainThread()
|
||||
? ProfileTimelineWorkerOperationType::SerializeDataOnMainThread
|
||||
: ProfileTimelineWorkerOperationType::SerializeDataOffMainThread,
|
||||
MarkerTracingType::END);
|
||||
timelines->AddMarkerForAllObservedDocShells(start);
|
||||
timelines->AddMarkerForAllObservedDocShells(end);
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!runnable->Dispatch()) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
template <class Derived>
|
||||
void
|
||||
WorkerPrivateParent<Derived>::PostMessage(
|
||||
JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
const Sequence<JSObject*>& aTransferable,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
PostMessageInternal(aCx, aMessage, aTransferable, aRv);
|
||||
}
|
||||
|
||||
template <class Derived>
|
||||
void
|
||||
WorkerPrivateParent<Derived>::UpdateContextOptions(
|
||||
|
@ -2780,48 +2699,6 @@ WorkerPrivateParent<Derived>::FlushReportsToSharedWorkers(
|
|||
aReporter->ClearConsoleReports();
|
||||
}
|
||||
|
||||
template <class Derived>
|
||||
NS_IMPL_ADDREF_INHERITED(WorkerPrivateParent<Derived>, DOMEventTargetHelper)
|
||||
|
||||
template <class Derived>
|
||||
NS_IMPL_RELEASE_INHERITED(WorkerPrivateParent<Derived>, DOMEventTargetHelper)
|
||||
|
||||
template <class Derived>
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WorkerPrivateParent<Derived>)
|
||||
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||
|
||||
template <class Derived>
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WorkerPrivateParent<Derived>,
|
||||
DOMEventTargetHelper)
|
||||
tmp->AssertIsOnParentThread();
|
||||
|
||||
// The WorkerPrivate::mSelfRef has a reference to itself, which is really
|
||||
// held by the worker thread. We traverse this reference if and only if our
|
||||
// busy count is zero and we have not released the main thread reference.
|
||||
// We do not unlink it. This allows the CC to break cycles involving the
|
||||
// WorkerPrivate and begin shutting it down (which does happen in unlink) but
|
||||
// ensures that the WorkerPrivate won't be deleted before we're done shutting
|
||||
// down the thread.
|
||||
|
||||
if (!tmp->mBusyCount && !tmp->mMainThreadObjectsForgotten) {
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelfRef)
|
||||
}
|
||||
|
||||
// The various strong references in LoadInfo are managed manually and cannot
|
||||
// be cycle collected.
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
template <class Derived>
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WorkerPrivateParent<Derived>,
|
||||
DOMEventTargetHelper)
|
||||
tmp->Terminate();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
template <class Derived>
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(WorkerPrivateParent<Derived>,
|
||||
DOMEventTargetHelper)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
template <class Derived>
|
||||
|
@ -2954,58 +2831,6 @@ WorkerPrivate::~WorkerPrivate()
|
|||
mWorkerHybridEventTarget->ForgetWorkerPrivate(this);
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<WorkerPrivate>
|
||||
WorkerPrivate::Constructor(const GlobalObject& aGlobal,
|
||||
const nsAString& aScriptURL,
|
||||
const WorkerOptions& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
return WorkerPrivate::Constructor(aGlobal, aScriptURL, false,
|
||||
WorkerTypeDedicated,
|
||||
aOptions.mName, nullptr, aRv);
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<ChromeWorkerPrivate>
|
||||
ChromeWorkerPrivate::Constructor(const GlobalObject& aGlobal,
|
||||
const nsAString& aScriptURL,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
return WorkerPrivate::Constructor(aGlobal, aScriptURL, true,
|
||||
WorkerTypeDedicated, EmptyString(),
|
||||
nullptr, aRv)
|
||||
.downcast<ChromeWorkerPrivate>();
|
||||
}
|
||||
|
||||
// static
|
||||
bool
|
||||
ChromeWorkerPrivate::WorkerAvailable(JSContext* aCx, JSObject* /* unused */)
|
||||
{
|
||||
// Chrome is always allowed to use workers, and content is never
|
||||
// allowed to use ChromeWorker, so all we have to check is the
|
||||
// caller. However, chrome workers apparently might not have a
|
||||
// system principal, so we have to check for them manually.
|
||||
if (NS_IsMainThread()) {
|
||||
return nsContentUtils::IsSystemCaller(aCx);
|
||||
}
|
||||
|
||||
return GetWorkerPrivateFromContext(aCx)->IsChromeWorker();
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<WorkerPrivate>
|
||||
WorkerPrivate::Constructor(const GlobalObject& aGlobal,
|
||||
const nsAString& aScriptURL,
|
||||
bool aIsChromeWorker, WorkerType aWorkerType,
|
||||
const nsAString& aWorkerName,
|
||||
WorkerLoadInfo* aLoadInfo, ErrorResult& aRv)
|
||||
{
|
||||
JSContext* cx = aGlobal.Context();
|
||||
return Constructor(cx, aScriptURL, aIsChromeWorker, aWorkerType,
|
||||
aWorkerName, VoidCString(), aLoadInfo, aRv);
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<WorkerPrivate>
|
||||
WorkerPrivate::Constructor(JSContext* aCx,
|
||||
|
|
|
@ -14,15 +14,12 @@
|
|||
#include "nsIEventTarget.h"
|
||||
#include "nsTObserverArray.h"
|
||||
|
||||
#include "mozilla/dom/Worker.h"
|
||||
#include "mozilla/dom/WorkerHolder.h"
|
||||
#include "mozilla/dom/WorkerLoadInfo.h"
|
||||
#include "mozilla/dom/workerinternals/JSSettings.h"
|
||||
#include "mozilla/dom/workerinternals/Queue.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
#undef PostMessage
|
||||
#endif
|
||||
|
||||
class nsIConsoleReportCollector;
|
||||
class nsIThreadInternal;
|
||||
|
||||
|
@ -51,7 +48,6 @@ class WorkerDebuggerGlobalScope;
|
|||
class WorkerErrorReport;
|
||||
class WorkerEventTarget;
|
||||
class WorkerGlobalScope;
|
||||
struct WorkerOptions;
|
||||
class WorkerRunnable;
|
||||
class WorkerThread;
|
||||
|
||||
|
@ -105,7 +101,7 @@ public:
|
|||
};
|
||||
|
||||
template <class Derived>
|
||||
class WorkerPrivateParent : public DOMEventTargetHelper
|
||||
class WorkerPrivateParent
|
||||
{
|
||||
protected:
|
||||
class EventTarget;
|
||||
|
@ -184,8 +180,19 @@ private:
|
|||
|
||||
protected:
|
||||
// The worker is owned by its thread, which is represented here. This is set
|
||||
// in Construct() and emptied by WorkerFinishedRunnable, and conditionally
|
||||
// in Constructor() and emptied by WorkerFinishedRunnable, and conditionally
|
||||
// traversed by the cycle collector if the busy count is zero.
|
||||
//
|
||||
// There are 4 ways a worker can be terminated:
|
||||
// 1. GC/CC - When the worker is in idle state (busycount == 0), it allows to
|
||||
// traverse the 'hidden' mParentEventTargetRef pointer. This is the exposed
|
||||
// Worker webidl object. Doing this, CC will be able to detect a cycle and
|
||||
// Unlink is called. In Unlink, Worker calls Terminate().
|
||||
// 2. Worker::Terminate() is called - the shutdown procedure starts
|
||||
// immediately.
|
||||
// 3. WorkerScope::Close() is called - Similar to point 2.
|
||||
// 4. xpcom-shutdown notification - We call Kill().
|
||||
RefPtr<Worker> mParentEventTargetRef;
|
||||
RefPtr<WorkerPrivate> mSelfRef;
|
||||
|
||||
WorkerPrivateParent(WorkerPrivate* aParent,
|
||||
|
@ -195,7 +202,7 @@ protected:
|
|||
const nsACString& aServiceWorkerScope,
|
||||
WorkerLoadInfo& aLoadInfo);
|
||||
|
||||
~WorkerPrivateParent();
|
||||
virtual ~WorkerPrivateParent();
|
||||
|
||||
private:
|
||||
Derived*
|
||||
|
@ -213,21 +220,14 @@ private:
|
|||
return NotifyPrivate(Terminating);
|
||||
}
|
||||
|
||||
void
|
||||
PostMessageInternal(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
const Sequence<JSObject*>& aTransferable,
|
||||
ErrorResult& aRv);
|
||||
|
||||
nsresult
|
||||
DispatchPrivate(already_AddRefed<WorkerRunnable> aRunnable, nsIEventTarget* aSyncLoopTarget);
|
||||
|
||||
public:
|
||||
virtual JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
NS_INLINE_DECL_REFCOUNTING(WorkerPrivateParent)
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(WorkerPrivateParent,
|
||||
DOMEventTargetHelper)
|
||||
void
|
||||
Traverse(nsCycleCollectionTraversalCallback& aCb);
|
||||
|
||||
void
|
||||
EnableDebugger();
|
||||
|
@ -236,10 +236,11 @@ public:
|
|||
DisableDebugger();
|
||||
|
||||
void
|
||||
ClearSelfRef()
|
||||
ClearSelfAndParentEventTargetRef()
|
||||
{
|
||||
AssertIsOnParentThread();
|
||||
MOZ_ASSERT(mSelfRef);
|
||||
mParentEventTargetRef = nullptr;
|
||||
mSelfRef = nullptr;
|
||||
}
|
||||
|
||||
|
@ -318,11 +319,6 @@ public:
|
|||
bool
|
||||
ProxyReleaseMainThreadObjects();
|
||||
|
||||
void
|
||||
PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
const Sequence<JSObject*>& aTransferable,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void
|
||||
UpdateContextOptions(const JS::ContextOptions& aContextOptions);
|
||||
|
||||
|
@ -394,7 +390,15 @@ public:
|
|||
|
||||
MutexAutoLock lock(mMutex);
|
||||
return mParentStatus < Terminating;
|
||||
}
|
||||
}
|
||||
|
||||
WorkerStatus
|
||||
ParentStatusProtected()
|
||||
{
|
||||
AssertIsOnParentThread();
|
||||
MutexAutoLock lock(mMutex);
|
||||
return mParentStatus;
|
||||
}
|
||||
|
||||
WorkerStatus
|
||||
ParentStatus() const
|
||||
|
@ -829,10 +833,6 @@ public:
|
|||
void
|
||||
FlushReportsToSharedWorkers(nsIConsoleReportCollector* aReporter);
|
||||
|
||||
IMPL_EVENT_HANDLER(message)
|
||||
IMPL_EVENT_HANDLER(messageerror)
|
||||
IMPL_EVENT_HANDLER(error)
|
||||
|
||||
// Check whether this worker is a secure context. For use from the parent
|
||||
// thread only; the canonical "is secure context" boolean is stored on the
|
||||
// compartment of the worker global. The only reason we don't
|
||||
|
@ -968,17 +968,6 @@ protected:
|
|||
~WorkerPrivate();
|
||||
|
||||
public:
|
||||
static already_AddRefed<WorkerPrivate>
|
||||
Constructor(const GlobalObject& aGlobal, const nsAString& aScriptURL,
|
||||
const WorkerOptions& aOptions,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<WorkerPrivate>
|
||||
Constructor(const GlobalObject& aGlobal, const nsAString& aScriptURL,
|
||||
bool aIsChromeWorker, WorkerType aWorkerType,
|
||||
const nsAString& aWorkerName,
|
||||
WorkerLoadInfo* aLoadInfo, ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<WorkerPrivate>
|
||||
Constructor(JSContext* aCx, const nsAString& aScriptURL, bool aIsChromeWorker,
|
||||
WorkerType aWorkerType, const nsAString& aWorkerName,
|
||||
|
@ -1389,6 +1378,21 @@ public:
|
|||
PerformanceStorage*
|
||||
GetPerformanceStorage();
|
||||
|
||||
Worker*
|
||||
ParentEventTargetRef() const
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(mParentEventTargetRef);
|
||||
return mParentEventTargetRef;
|
||||
}
|
||||
|
||||
void
|
||||
SetParentEventTargetRef(Worker* aParentEventTargetRef)
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(aParentEventTargetRef);
|
||||
MOZ_DIAGNOSTIC_ASSERT(!mParentEventTargetRef);
|
||||
mParentEventTargetRef = aParentEventTargetRef;
|
||||
}
|
||||
|
||||
private:
|
||||
WorkerPrivate(WorkerPrivate* aParent,
|
||||
const nsAString& aScriptURL, bool aIsChromeWorker,
|
||||
|
@ -1491,25 +1495,6 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
// This class is only used to trick the DOM bindings. We never create
|
||||
// instances of it, and static_casting to it is fine since it doesn't add
|
||||
// anything to WorkerPrivate.
|
||||
class ChromeWorkerPrivate : public WorkerPrivate
|
||||
{
|
||||
public:
|
||||
static already_AddRefed<ChromeWorkerPrivate>
|
||||
Constructor(const GlobalObject& aGlobal, const nsAString& aScriptURL,
|
||||
ErrorResult& rv);
|
||||
|
||||
static bool
|
||||
WorkerAvailable(JSContext* aCx, JSObject* /* unused */);
|
||||
|
||||
private:
|
||||
ChromeWorkerPrivate() = delete;
|
||||
ChromeWorkerPrivate(const ChromeWorkerPrivate& aRHS) = delete;
|
||||
ChromeWorkerPrivate& operator =(const ChromeWorkerPrivate& aRHS) = delete;
|
||||
};
|
||||
|
||||
class AutoSyncLoopHolder
|
||||
{
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
|
|
|
@ -330,7 +330,8 @@ WorkerRunnable::Run()
|
|||
cx = jsapi->cx();
|
||||
}
|
||||
|
||||
// Note that we can't assert anything about mWorkerPrivate->GetWrapper()
|
||||
// Note that we can't assert anything about
|
||||
// mWorkerPrivate->ParentEventTargetRef()->GetWrapper()
|
||||
// existing, since it may in fact have been GCed (and we may be one of the
|
||||
// runnables cleaning up the worker as a result).
|
||||
|
||||
|
@ -348,27 +349,31 @@ WorkerRunnable::Run()
|
|||
// the compartment of the worker's reflector if there is one. There might
|
||||
// not be one if we're just starting to compile the script for this worker.
|
||||
Maybe<JSAutoCompartment> ac;
|
||||
if (!targetIsWorkerThread && mWorkerPrivate->GetWrapper()) {
|
||||
if (!targetIsWorkerThread &&
|
||||
mWorkerPrivate->IsDedicatedWorker() &&
|
||||
mWorkerPrivate->ParentEventTargetRef()->GetWrapper()) {
|
||||
JSObject* wrapper = mWorkerPrivate->ParentEventTargetRef()->GetWrapper();
|
||||
|
||||
// If we're on the parent thread and have a reflector and a globalObject,
|
||||
// then the compartments of cx, globalObject, and the worker's reflector
|
||||
// should all match.
|
||||
MOZ_ASSERT_IF(globalObject,
|
||||
js::GetObjectCompartment(mWorkerPrivate->GetWrapper()) ==
|
||||
js::GetObjectCompartment(wrapper) ==
|
||||
js::GetContextCompartment(cx));
|
||||
MOZ_ASSERT_IF(globalObject,
|
||||
js::GetObjectCompartment(mWorkerPrivate->GetWrapper()) ==
|
||||
js::GetObjectCompartment(wrapper) ==
|
||||
js::GetObjectCompartment(globalObject->GetGlobalJSObject()));
|
||||
|
||||
// If we're on the parent thread and have a reflector, then our
|
||||
// JSContext had better be either in the null compartment (and hence
|
||||
// have no globalObject) or in the compartment of our reflector.
|
||||
MOZ_ASSERT(!js::GetContextCompartment(cx) ||
|
||||
js::GetObjectCompartment(mWorkerPrivate->GetWrapper()) ==
|
||||
js::GetObjectCompartment(wrapper) ==
|
||||
js::GetContextCompartment(cx),
|
||||
"Must either be in the null compartment or in our reflector "
|
||||
"compartment");
|
||||
|
||||
ac.emplace(cx, mWorkerPrivate->GetWrapper());
|
||||
ac.emplace(cx, wrapper);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!jsapi->HasException());
|
||||
|
|
|
@ -9,7 +9,9 @@ with Files("**"):
|
|||
|
||||
# Public stuff.
|
||||
EXPORTS.mozilla.dom += [
|
||||
'ChromeWorker.h',
|
||||
'SharedWorker.h',
|
||||
'Worker.h',
|
||||
'WorkerCommon.h',
|
||||
'WorkerDebugger.h',
|
||||
'WorkerDebuggerManager.h',
|
||||
|
@ -38,6 +40,7 @@ XPIDL_SOURCES += [
|
|||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'ChromeWorker.cpp',
|
||||
'ChromeWorkerScope.cpp',
|
||||
'MessageEventRunnable.cpp',
|
||||
'Principal.cpp',
|
||||
|
@ -45,6 +48,7 @@ UNIFIED_SOURCES += [
|
|||
'RuntimeService.cpp',
|
||||
'ScriptLoader.cpp',
|
||||
'SharedWorker.cpp',
|
||||
'Worker.cpp',
|
||||
'WorkerDebugger.cpp',
|
||||
'WorkerDebuggerManager.cpp',
|
||||
'WorkerError.cpp',
|
||||
|
|
|
@ -82,3 +82,5 @@ skip-if = (os == 'linux') # Bug 1244409
|
|||
[test_fileSubWorker.xul]
|
||||
[test_bug1062920.xul]
|
||||
[test_sharedWorker_privateBrowsing.html]
|
||||
[test_shutdownCheck.xul]
|
||||
support-files = worker_shutdownCheck.js
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
|
||||
|
||||
<window title="Worker shutdown check"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
|
||||
|
||||
<!-- test results are displayed in the html:body -->
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
</body>
|
||||
|
||||
<!-- test code goes here -->
|
||||
<script type="application/javascript">
|
||||
<![CDATA[
|
||||
|
||||
SimpleTest.waitForExplicitFinish()
|
||||
|
||||
const URL = "worker_shutdownCheck.js";
|
||||
|
||||
function checkWorker() {
|
||||
const wdm = Cc["@mozilla.org/dom/workers/workerdebuggermanager;1"].
|
||||
getService(Ci.nsIWorkerDebuggerManager);
|
||||
|
||||
let e = wdm.getWorkerDebuggerEnumerator();
|
||||
while (e.hasMoreElements()) {
|
||||
let dbg = e.getNext().QueryInterface(Ci.nsIWorkerDebugger);
|
||||
if (dbg.url == URL) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
new Promise(resolve => {
|
||||
var w = new Worker(URL);
|
||||
ok(checkWorker(), "We have the worker");
|
||||
w.onmessage = () => { resolve(); }
|
||||
}).then(() => {
|
||||
info("Waiting...");
|
||||
|
||||
// We don't know if the worker thread is able to shutdown when calling
|
||||
// CC/GC. Better to check again in case.
|
||||
function checkGC() {
|
||||
Cu.forceCC();
|
||||
Cu.forceGC();
|
||||
if (!checkWorker()) {
|
||||
ok(true, "We don't have the worker");
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
setTimeout(checkGC, 200);
|
||||
}
|
||||
|
||||
checkGC();
|
||||
});
|
||||
|
||||
]]>
|
||||
</script>
|
||||
</window>
|
||||
|
|
@ -0,0 +1 @@
|
|||
postMessage("Ok!");
|
|
@ -448,16 +448,17 @@ nsEditingSession::SetupEditorOnWindow(mozIDOMWindowProxy* aWindow)
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_TRUE(contentViewer, NS_ERROR_FAILURE);
|
||||
|
||||
nsCOMPtr<nsIDOMDocument> domDoc =
|
||||
do_QueryInterface(contentViewer->GetDocument());
|
||||
NS_ENSURE_TRUE(domDoc, NS_ERROR_FAILURE);
|
||||
nsCOMPtr<nsIDocument> doc = contentViewer->GetDocument();
|
||||
if (NS_WARN_IF(!doc)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Set up as a doc state listener
|
||||
// Important! We must have this to broadcast the "obs_documentCreated" message
|
||||
rv = htmlEditor->AddDocumentStateListener(mComposerCommandsUpdater);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = htmlEditor->Init(domDoc, nullptr /* root content */,
|
||||
rv = htmlEditor->Init(*doc, nullptr /* root content */,
|
||||
nullptr, mEditorFlags, EmptyString());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
|
|
@ -216,19 +216,15 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(EditorBase)
|
|||
NS_IMPL_CYCLE_COLLECTING_RELEASE(EditorBase)
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
EditorBase::Init(nsIDOMDocument* aDOMDocument,
|
||||
nsIContent* aRoot,
|
||||
nsresult
|
||||
EditorBase::Init(nsIDocument& aDocument,
|
||||
Element* aRoot,
|
||||
nsISelectionController* aSelectionController,
|
||||
uint32_t aFlags,
|
||||
const nsAString& aValue)
|
||||
{
|
||||
MOZ_ASSERT(mAction == EditAction::none,
|
||||
"Initializing during an edit action is an error");
|
||||
MOZ_ASSERT(aDOMDocument);
|
||||
if (!aDOMDocument) {
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
}
|
||||
|
||||
// First only set flags, but other stuff shouldn't be initialized now.
|
||||
// Don't move this call after initializing mDocument.
|
||||
|
@ -240,7 +236,7 @@ EditorBase::Init(nsIDOMDocument* aDOMDocument,
|
|||
SetFlags(aFlags);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "SetFlags() failed");
|
||||
|
||||
mDocument = do_QueryInterface(aDOMDocument);
|
||||
mDocument = &aDocument;
|
||||
// HTML editors currently don't have their own selection controller,
|
||||
// so they'll pass null as aSelCon, and we'll get the selection controller
|
||||
// off of the presshell.
|
||||
|
@ -256,8 +252,9 @@ EditorBase::Init(nsIDOMDocument* aDOMDocument,
|
|||
"Selection controller should be available at this point");
|
||||
|
||||
//set up root element if we are passed one.
|
||||
if (aRoot)
|
||||
mRootElement = do_QueryInterface(aRoot);
|
||||
if (aRoot) {
|
||||
mRootElement = aRoot;
|
||||
}
|
||||
|
||||
mUpdateCount=0;
|
||||
|
||||
|
@ -1353,14 +1350,6 @@ EditorBase::RemoveAttribute(Element* aElement,
|
|||
return DoTransaction(transaction);
|
||||
}
|
||||
|
||||
bool
|
||||
EditorBase::OutputsMozDirty()
|
||||
{
|
||||
// Return true for Composer (!IsInteractionAllowed()) or mail
|
||||
// (IsMailEditor()), but false for webpages.
|
||||
return !IsInteractionAllowed() || IsMailEditor();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
EditorBase::MarkNodeDirty(nsIDOMNode* aNode)
|
||||
{
|
||||
|
@ -2514,7 +2503,7 @@ EditorBase::CommitComposition()
|
|||
IMEStateManager::NotifyIME(REQUEST_TO_COMMIT_COMPOSITION, pc) : NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsresult
|
||||
EditorBase::GetPreferredIMEState(IMEState* aState)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aState);
|
||||
|
@ -5252,7 +5241,7 @@ private:
|
|||
nsCOMPtr<nsISelectionController> mSelectionController;
|
||||
};
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsresult
|
||||
EditorBase::FinalizeSelection()
|
||||
{
|
||||
nsCOMPtr<nsISelectionController> selectionController =
|
||||
|
@ -5419,12 +5408,6 @@ EditorBase::SwitchTextDirectionTo(uint32_t aDirection)
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
EditorBase::IsModifiableNode(nsIDOMNode* aNode)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
EditorBase::IsModifiableNode(nsINode* aNode)
|
||||
{
|
||||
|
@ -5572,16 +5555,6 @@ EditorBase::SetSuppressDispatchingInputEvent(bool aSuppress)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
EditorBase::GetIsInEditAction(bool* aIsInEditAction)
|
||||
{
|
||||
// NOTE: If you need to override this method, you need to make
|
||||
// IsInEditAction() virtual.
|
||||
MOZ_ASSERT(aIsInEditAction, "aIsInEditAction must not be null");
|
||||
*aIsInEditAction = IsInEditAction();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
int32_t
|
||||
EditorBase::GetIMESelectionStartOffsetIn(nsINode* aTextNode)
|
||||
{
|
||||
|
|
|
@ -220,6 +220,23 @@ public:
|
|||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(EditorBase, nsIEditor)
|
||||
|
||||
/**
|
||||
* Init is to tell the implementation of nsIEditor to begin its services
|
||||
* @param aDoc The dom document interface being observed
|
||||
* @param aRoot This is the root of the editable section of this
|
||||
* document. If it is null then we get root
|
||||
* from document body.
|
||||
* @param aSelCon this should be used to get the selection location
|
||||
* (will be null for HTML editors)
|
||||
* @param aFlags A bitmask of flags for specifying the behavior
|
||||
* of the editor.
|
||||
*/
|
||||
virtual nsresult Init(nsIDocument& doc,
|
||||
Element* aRoot,
|
||||
nsISelectionController* aSelCon,
|
||||
uint32_t aFlags,
|
||||
const nsAString& aInitialValue);
|
||||
|
||||
bool IsInitialized() const { return !!mDocument; }
|
||||
already_AddRefed<nsIDOMDocument> GetDOMDocument();
|
||||
already_AddRefed<nsIDocument> GetDocument();
|
||||
|
@ -457,6 +474,11 @@ public:
|
|||
WidgetCompositionEvent* aCompositionChangeEvet) = 0;
|
||||
void EndIMEComposition();
|
||||
|
||||
/**
|
||||
* Get preferred IME status of current widget.
|
||||
*/
|
||||
virtual nsresult GetPreferredIMEState(widget::IMEState* aState);
|
||||
|
||||
/**
|
||||
* Commit composition if there is.
|
||||
* Note that when there is a composition, this requests to commit composition
|
||||
|
@ -470,6 +492,11 @@ public:
|
|||
|
||||
RangeUpdater& RangeUpdaterRef() { return mRangeUpdater; }
|
||||
|
||||
/**
|
||||
* Finalizes selection and caret for the editor.
|
||||
*/
|
||||
nsresult FinalizeSelection();
|
||||
|
||||
protected:
|
||||
nsresult DetermineCurrentDirection();
|
||||
void FireInputEvent();
|
||||
|
@ -1289,6 +1316,17 @@ public:
|
|||
return mDidPreDestroy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if markNodeDirty() has any effect. Returns false if
|
||||
* markNodeDirty() is a no-op.
|
||||
*/
|
||||
bool OutputsMozDirty() const
|
||||
{
|
||||
// Return true for Composer (!IsInteractionAllowed()) or mail
|
||||
// (IsMailEditor()), but false for webpages.
|
||||
return !IsInteractionAllowed() || IsMailEditor();
|
||||
}
|
||||
|
||||
/**
|
||||
* GetTransactionManager() returns transaction manager associated with the
|
||||
* editor. This may return nullptr if undo/redo hasn't been enabled.
|
||||
|
|
|
@ -231,15 +231,13 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(HTMLEditor)
|
|||
NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
|
||||
NS_INTERFACE_MAP_END_INHERITING(TextEditor)
|
||||
|
||||
NS_IMETHODIMP
|
||||
HTMLEditor::Init(nsIDOMDocument* aDoc,
|
||||
nsIContent* aRoot,
|
||||
nsresult
|
||||
HTMLEditor::Init(nsIDocument& aDoc,
|
||||
Element* aRoot,
|
||||
nsISelectionController* aSelCon,
|
||||
uint32_t aFlags,
|
||||
const nsAString& aInitialValue)
|
||||
{
|
||||
NS_PRECONDITION(aDoc && !aSelCon, "bad arg");
|
||||
NS_ENSURE_TRUE(aDoc, NS_ERROR_NULL_POINTER);
|
||||
MOZ_ASSERT(aInitialValue.IsEmpty(), "Non-empty initial values not supported");
|
||||
|
||||
nsresult rulesRv = NS_OK;
|
||||
|
@ -255,8 +253,7 @@ HTMLEditor::Init(nsIDOMDocument* aDoc,
|
|||
}
|
||||
|
||||
// Init mutation observer
|
||||
nsCOMPtr<nsINode> document = do_QueryInterface(aDoc);
|
||||
document->AddMutationObserverUnlessExists(this);
|
||||
aDoc.AddMutationObserverUnlessExists(this);
|
||||
|
||||
if (!mRootElement) {
|
||||
UpdateRootElement();
|
||||
|
@ -3089,7 +3086,8 @@ HTMLEditor::DeleteNode(nsIDOMNode* aNode)
|
|||
{
|
||||
// do nothing if the node is read-only
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
|
||||
if (NS_WARN_IF(!IsModifiableNode(aNode) && !IsMozEditorBogusNode(content))) {
|
||||
if (NS_WARN_IF(!IsModifiableNode(content) &&
|
||||
!IsMozEditorBogusNode(content))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
@ -3249,13 +3247,6 @@ HTMLEditor::ContentRemoved(nsIDocument* aDocument,
|
|||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP_(bool)
|
||||
HTMLEditor::IsModifiableNode(nsIDOMNode* aNode)
|
||||
{
|
||||
nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
|
||||
return IsModifiableNode(node);
|
||||
}
|
||||
|
||||
bool
|
||||
HTMLEditor::IsModifiableNode(nsINode* aNode)
|
||||
{
|
||||
|
@ -4836,7 +4827,7 @@ HTMLEditor::IsAcceptableInputEvent(WidgetGUIEvent* aGUIEvent)
|
|||
return IsActiveInDOMWindow();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsresult
|
||||
HTMLEditor::GetPreferredIMEState(IMEState* aState)
|
||||
{
|
||||
// HTML editor don't prefer the CSS ime-mode because IE didn't do so too.
|
||||
|
|
|
@ -106,15 +106,15 @@ public:
|
|||
bool GetReturnInParagraphCreatesNewParagraph();
|
||||
Element* GetSelectionContainer();
|
||||
|
||||
// nsIEditor overrides
|
||||
NS_IMETHOD GetPreferredIMEState(widget::IMEState* aState) override;
|
||||
|
||||
// nsISelectionListener overrides
|
||||
NS_IMETHOD NotifySelectionChanged(nsIDOMDocument* aDOMDocument,
|
||||
nsISelection* aSelection,
|
||||
int16_t aReason) override;
|
||||
|
||||
// TextEditor overrides
|
||||
virtual nsresult Init(nsIDocument& aDoc, Element* aRoot,
|
||||
nsISelectionController* aSelCon, uint32_t aFlags,
|
||||
const nsAString& aValue) override;
|
||||
NS_IMETHOD BeginningOfDocument() override;
|
||||
virtual nsresult HandleKeyPressEvent(
|
||||
WidgetKeyboardEvent* aKeyboardEvent) override;
|
||||
|
@ -221,11 +221,10 @@ public:
|
|||
// Overrides of EditorBase interface methods
|
||||
virtual nsresult EndUpdateViewBatch() override;
|
||||
|
||||
NS_IMETHOD Init(nsIDOMDocument* aDoc, nsIContent* aRoot,
|
||||
nsISelectionController* aSelCon, uint32_t aFlags,
|
||||
const nsAString& aValue) override;
|
||||
NS_IMETHOD PreDestroy(bool aDestroyingFrames) override;
|
||||
|
||||
virtual nsresult GetPreferredIMEState(widget::IMEState* aState) override;
|
||||
|
||||
/**
|
||||
* @param aElement Must not be null.
|
||||
*/
|
||||
|
@ -391,7 +390,6 @@ public:
|
|||
const EditorRawDOMPoint& aPointToInsert,
|
||||
EditorRawDOMPoint* aPointAfterInsertedString =
|
||||
nullptr) override;
|
||||
NS_IMETHOD_(bool) IsModifiableNode(nsIDOMNode* aNode) override;
|
||||
virtual bool IsModifiableNode(nsINode* aNode) override;
|
||||
|
||||
NS_IMETHOD SelectAll() override;
|
||||
|
|
|
@ -113,16 +113,13 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TextEditor)
|
|||
NS_INTERFACE_MAP_END_INHERITING(EditorBase)
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
TextEditor::Init(nsIDOMDocument* aDoc,
|
||||
nsIContent* aRoot,
|
||||
nsresult
|
||||
TextEditor::Init(nsIDocument& aDoc,
|
||||
Element* aRoot,
|
||||
nsISelectionController* aSelCon,
|
||||
uint32_t aFlags,
|
||||
const nsAString& aInitialValue)
|
||||
{
|
||||
NS_PRECONDITION(aDoc, "bad arg");
|
||||
NS_ENSURE_TRUE(aDoc, NS_ERROR_NULL_POINTER);
|
||||
|
||||
if (mRules) {
|
||||
mRules->DetachEditor();
|
||||
}
|
||||
|
|
|
@ -74,9 +74,9 @@ public:
|
|||
using EditorBase::RemoveAttributeOrEquivalent;
|
||||
using EditorBase::SetAttributeOrEquivalent;
|
||||
|
||||
NS_IMETHOD Init(nsIDOMDocument* aDoc, nsIContent* aRoot,
|
||||
nsISelectionController* aSelCon, uint32_t aFlags,
|
||||
const nsAString& aValue) override;
|
||||
virtual nsresult Init(nsIDocument& aDoc, Element* aRoot,
|
||||
nsISelectionController* aSelCon, uint32_t aFlags,
|
||||
const nsAString& aValue) override;
|
||||
|
||||
nsresult DocumentIsEmpty(bool* aIsEmpty);
|
||||
NS_IMETHOD GetDocumentIsEmpty(bool* aDocumentIsEmpty) override;
|
||||
|
|
|
@ -25,14 +25,9 @@ namespace mozilla {
|
|||
class EditorBase;
|
||||
class HTMLEditor;
|
||||
class TextEditor;
|
||||
namespace widget {
|
||||
struct IMEState;
|
||||
} // namespace widget
|
||||
} // namespace mozilla
|
||||
%}
|
||||
|
||||
native IMEState(mozilla::widget::IMEState);
|
||||
|
||||
[scriptable, builtinclass, uuid(094be624-f0bf-400f-89e2-6a84baab9474)]
|
||||
interface nsIEditor : nsISupports
|
||||
{
|
||||
|
@ -53,28 +48,6 @@ interface nsIEditor : nsISupports
|
|||
|
||||
readonly attribute nsISelection selection;
|
||||
|
||||
/**
|
||||
* Finalizes selection and caret for the editor.
|
||||
*/
|
||||
[noscript] void finalizeSelection();
|
||||
|
||||
/**
|
||||
* Init is to tell the implementation of nsIEditor to begin its services
|
||||
* @param aDoc The dom document interface being observed
|
||||
* @param aRoot This is the root of the editable section of this
|
||||
* document. If it is null then we get root
|
||||
* from document body.
|
||||
* @param aSelCon this should be used to get the selection location
|
||||
* (will be null for HTML editors)
|
||||
* @param aFlags A bitmask of flags for specifying the behavior
|
||||
* of the editor.
|
||||
*/
|
||||
[noscript] void init(in nsIDOMDocument doc,
|
||||
in nsIContent aRoot,
|
||||
in nsISelectionController aSelCon,
|
||||
in unsigned long aFlags,
|
||||
in AString initialValue);
|
||||
|
||||
void setAttributeOrEquivalent(in nsIDOMElement element,
|
||||
in AString sourceAttrName,
|
||||
in AString sourceAttrValue,
|
||||
|
@ -478,12 +451,6 @@ interface nsIEditor : nsISupports
|
|||
*/
|
||||
void deleteNode(in nsIDOMNode child);
|
||||
|
||||
/**
|
||||
* Returns true if markNodeDirty() has any effect. Returns false if
|
||||
* markNodeDirty() is a no-op.
|
||||
*/
|
||||
[notxpcom] boolean outputsMozDirty();
|
||||
|
||||
/**
|
||||
* markNodeDirty() sets a special dirty attribute on the node.
|
||||
* Usually this will be called immediately after creating a new node.
|
||||
|
@ -550,29 +517,14 @@ interface nsIEditor : nsISupports
|
|||
/* Run unit tests. Noop in optimized builds */
|
||||
void debugUnitTests(out long outNumTests, out long outNumTestsFailed);
|
||||
|
||||
/* checks if a node is read-only or not */
|
||||
[notxpcom] boolean isModifiableNode(in nsIDOMNode aNode);
|
||||
|
||||
/* Set true if you want to suppress dispatching input event. */
|
||||
attribute boolean suppressDispatchingInputEvent;
|
||||
|
||||
/**
|
||||
* True if an edit action is being handled (in other words, between calls of
|
||||
* nsIEditorObserver::BeforeEditAction() and nsIEditorObserver::EditAction()
|
||||
* or nsIEditorObserver::CancelEditAction(). Otherwise, false.
|
||||
*/
|
||||
[noscript] readonly attribute boolean isInEditAction;
|
||||
|
||||
/**
|
||||
* forceCompositionEnd() force the composition end
|
||||
*/
|
||||
void forceCompositionEnd();
|
||||
|
||||
/**
|
||||
* Get preferred IME status of current widget.
|
||||
*/
|
||||
[noscript] IMEState getPreferredIMEState();
|
||||
|
||||
/**
|
||||
* whether this editor has active IME transaction
|
||||
*/
|
||||
|
|
|
@ -360,6 +360,7 @@ DrawTargetRecording::FillGlyphs(ScaledFont *aFont,
|
|||
}
|
||||
mRecorder->AddStoredObject(unscaledFont);
|
||||
}
|
||||
mRecorder->RecordEvent(RecordedScaledFontCreation(aFont, unscaledFont));
|
||||
}
|
||||
RecordingFontUserData *userData = new RecordingFontUserData;
|
||||
userData->refPtr = aFont;
|
||||
|
|
|
@ -124,13 +124,14 @@ ScrollingLayersHelper::BeginItem(nsDisplayItem* aItem,
|
|||
// nested ScrollingLayersHelper may rely on things like TopmostScrollId and
|
||||
// TopmostClipId, so now we need to push at most two things onto the stack.
|
||||
|
||||
Maybe<wr::WrScrollId> leafmostId = ids.first;
|
||||
wr::WrScrollId rootId = wr::WrScrollId { 0 };
|
||||
wr::WrScrollId leafmostId = ids.first.valueOr(rootId);
|
||||
|
||||
FrameMetrics::ViewID viewId = aItem->GetActiveScrolledRoot()
|
||||
? aItem->GetActiveScrolledRoot()->GetViewId()
|
||||
: FrameMetrics::NULL_SCROLL_ID;
|
||||
Maybe<wr::WrScrollId> scrollId =
|
||||
mBuilder->GetScrollIdForDefinedScrollLayer(viewId);
|
||||
MOZ_ASSERT(scrollId.isSome());
|
||||
wr::WrScrollId scrollId =
|
||||
mBuilder->GetScrollIdForDefinedScrollLayer(viewId).valueOr(rootId);
|
||||
|
||||
// If the leafmost ASR is not the same as the item's ASR then we are dealing
|
||||
// with a case where the item's clip chain is scrolled by something other than
|
||||
|
@ -146,7 +147,7 @@ ScrollingLayersHelper::BeginItem(nsDisplayItem* aItem,
|
|||
mBuilder->TopmostScrollId() == scrollId &&
|
||||
!mBuilder->TopmostIsClip()) {
|
||||
if (auto cs = EnclosingClipAndScroll()) {
|
||||
MOZ_ASSERT(cs->first == *scrollId);
|
||||
MOZ_ASSERT(cs->first == scrollId);
|
||||
needClipAndScroll = true;
|
||||
}
|
||||
}
|
||||
|
@ -155,7 +156,7 @@ ScrollingLayersHelper::BeginItem(nsDisplayItem* aItem,
|
|||
// the scroll stack
|
||||
if (!needClipAndScroll && mBuilder->TopmostScrollId() != scrollId) {
|
||||
MOZ_ASSERT(leafmostId == scrollId); // because !needClipAndScroll
|
||||
clips.mScrollId = scrollId;
|
||||
clips.mScrollId = Some(scrollId);
|
||||
}
|
||||
// And ensure the leafmost clip, if scrolled by that ASR, is at the top of the
|
||||
// stack.
|
||||
|
@ -174,7 +175,7 @@ ScrollingLayersHelper::BeginItem(nsDisplayItem* aItem,
|
|||
clipId = mBuilder->TopmostClipId();
|
||||
}
|
||||
|
||||
clips.mClipAndScroll = Some(std::make_pair(*scrollId, clipId));
|
||||
clips.mClipAndScroll = Some(std::make_pair(scrollId, clipId));
|
||||
}
|
||||
|
||||
clips.Apply(mBuilder);
|
||||
|
@ -299,7 +300,9 @@ ScrollingLayersHelper::RecurseAndDefineClip(nsDisplayItem* aItem,
|
|||
} else {
|
||||
MOZ_ASSERT(!ancestorIds.second);
|
||||
FrameMetrics::ViewID viewId = aChain->mASR ? aChain->mASR->GetViewId() : FrameMetrics::NULL_SCROLL_ID;
|
||||
auto scrollId = mBuilder->GetScrollIdForDefinedScrollLayer(viewId);
|
||||
|
||||
wr::WrScrollId rootId = wr::WrScrollId { 0 };
|
||||
auto scrollId = mBuilder->GetScrollIdForDefinedScrollLayer(viewId).valueOr(rootId);
|
||||
if (mBuilder->TopmostScrollId() == scrollId) {
|
||||
if (mBuilder->TopmostIsClip()) {
|
||||
// If aChain->mASR is already the topmost scroll layer on the stack, but
|
||||
|
@ -328,7 +331,7 @@ ScrollingLayersHelper::RecurseAndDefineClip(nsDisplayItem* aItem,
|
|||
// (S, D) for this item. This hunk of code ensures that we define D
|
||||
// as a child of C, and when we set the needClipAndScroll flag elsewhere
|
||||
// in this file we make sure to set it for this scenario.
|
||||
MOZ_ASSERT(Some(cs->first) == scrollId);
|
||||
MOZ_ASSERT(cs->first == scrollId);
|
||||
ancestorIds.first = Nothing();
|
||||
ancestorIds.second = cs->second;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,10 @@
|
|||
#include "nsDisplayList.h"
|
||||
#include "WebRenderCanvasRenderer.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
#include "gfxDWriteFonts.h"
|
||||
#endif
|
||||
|
||||
// Useful for debugging, it dumps the Gecko display list *before* we try to
|
||||
// build WR commands from it, and dumps the WR display list after building it.
|
||||
#define DUMP_LISTS 0
|
||||
|
@ -258,6 +262,10 @@ WebRenderLayerManager::EndTransactionWithoutLayer(nsDisplayList* aDisplayList,
|
|||
if (XRE_IsContentProcess()) nsFrame::PrintDisplayList(aDisplayListBuilder, *aDisplayList);
|
||||
#endif
|
||||
|
||||
#ifdef XP_WIN
|
||||
gfxDWriteFont::UpdateClearTypeUsage();
|
||||
#endif
|
||||
|
||||
// Since we don't do repeat transactions right now, just set the time
|
||||
mAnimationReadyTime = TimeStamp::Now();
|
||||
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
#green {
|
||||
position: absolute;
|
||||
background: green;
|
||||
border-radius: 1px;
|
||||
transform: translateX(100px);
|
||||
}
|
||||
#text {
|
||||
visibility: hidden;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="header">
|
||||
<div id="green"><span id="text">Text.</span></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
#header {
|
||||
position: fixed;
|
||||
}
|
||||
#green {
|
||||
position: absolute;
|
||||
background: green;
|
||||
border-radius: 1px;
|
||||
transform: translateX(100px);
|
||||
}
|
||||
#text {
|
||||
visibility: hidden;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="header">
|
||||
<div id="green"><span id="text">Text.</span></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -10,3 +10,4 @@ fuzzy(100,30) == 1149923.html 1149923-ref.html # use fuzzy due to few distorted
|
|||
== 1419528.html 1419528-ref.html
|
||||
== 1424673.html 1424673-ref.html
|
||||
== 1429411.html 1429411-ref.html
|
||||
== 1435143.html 1435143-ref.html
|
||||
|
|
|
@ -1311,15 +1311,15 @@ DisplayListBuilder::TopmostClipId()
|
|||
return Nothing();
|
||||
}
|
||||
|
||||
Maybe<wr::WrScrollId>
|
||||
wr::WrScrollId
|
||||
DisplayListBuilder::TopmostScrollId()
|
||||
{
|
||||
for (auto it = mClipStack.crbegin(); it != mClipStack.crend(); it++) {
|
||||
if (it->is<wr::WrScrollId>()) {
|
||||
return Some(it->as<wr::WrScrollId>());
|
||||
return it->as<wr::WrScrollId>();
|
||||
}
|
||||
}
|
||||
return Nothing();
|
||||
return wr::WrScrollId { 0 };
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -435,7 +435,7 @@ public:
|
|||
// is empty.
|
||||
Maybe<wr::WrClipId> TopmostClipId();
|
||||
// Same as TopmostClipId() but for scroll layers.
|
||||
Maybe<wr::WrScrollId> TopmostScrollId();
|
||||
wr::WrScrollId TopmostScrollId();
|
||||
// If the topmost item on the stack is a clip or a scroll layer
|
||||
bool TopmostIsClip();
|
||||
|
||||
|
|
|
@ -771,6 +771,10 @@ struct WrScrollId {
|
|||
bool operator==(const WrScrollId& other) const {
|
||||
return id == other.id;
|
||||
}
|
||||
|
||||
bool operator!=(const WrScrollId& other) const {
|
||||
return id != other.id;
|
||||
}
|
||||
};
|
||||
|
||||
// Corresponds to a clip id for a position:sticky clip in webrender. Similar
|
||||
|
|
|
@ -122,7 +122,7 @@ DecodedSurfaceProvider::LogicalSizeInBytes() const
|
|||
{
|
||||
// Single frame images are always 32bpp.
|
||||
IntSize size = GetSurfaceKey().Size();
|
||||
return size.width * size.height * sizeof(uint32_t);
|
||||
return size_t(size.width) * size_t(size.height) * sizeof(uint32_t);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -880,7 +880,8 @@ nsGIFDecoder2::FinishImageDescriptor(const char* aData)
|
|||
}
|
||||
|
||||
// Clear state from last image.
|
||||
mGIFStruct.pixels_remaining = frameRect.Width() * frameRect.Height();
|
||||
mGIFStruct.pixels_remaining =
|
||||
int64_t(frameRect.Width()) * int64_t(frameRect.Height());
|
||||
|
||||
if (haveLocalColorTable) {
|
||||
// We have a local color table, so prepare to read it into the palette of
|
||||
|
|
|
@ -477,6 +477,14 @@ BaselineCacheIRCompiler::emitGuardFunctionPrototype()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCacheIRCompiler::emitLoadValueResult()
|
||||
{
|
||||
AutoOutputRegister output(*this);
|
||||
masm.loadValue(stubAddress(reader.stubOffset()), output.valueReg());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
BaselineCacheIRCompiler::emitLoadFixedSlotResult()
|
||||
{
|
||||
|
@ -2092,6 +2100,9 @@ BaselineCacheIRCompiler::init(CacheKind kind)
|
|||
AllocatableGeneralRegisterSet available(ICStubCompiler::availableGeneralRegs(numInputsInRegs));
|
||||
|
||||
switch (kind) {
|
||||
case CacheKind::GetIntrinsic:
|
||||
MOZ_ASSERT(numInputs == 0);
|
||||
break;
|
||||
case CacheKind::GetProp:
|
||||
case CacheKind::TypeOf:
|
||||
case CacheKind::GetIterator:
|
||||
|
|
|
@ -1392,13 +1392,25 @@ DoGetIntrinsicFallback(JSContext* cx, BaselineFrame* frame, ICGetIntrinsic_Fallb
|
|||
if (stub.invalid())
|
||||
return true;
|
||||
|
||||
JitSpew(JitSpew_BaselineIC, " Generating GetIntrinsic optimized stub");
|
||||
ICGetIntrinsic_Constant::Compiler compiler(cx, res);
|
||||
ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
|
||||
if (!newStub)
|
||||
return false;
|
||||
if (stub->state().maybeTransition())
|
||||
stub->discardStubs(cx);
|
||||
|
||||
if (stub->state().canAttachStub()) {
|
||||
bool attached = false;
|
||||
RootedScript script(cx, frame->script());
|
||||
GetIntrinsicIRGenerator gen(cx, script, pc, stub->state().mode(), res);
|
||||
if (gen.tryAttachStub()) {
|
||||
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
||||
BaselineCacheIRStubKind::Regular,
|
||||
ICStubEngine::Baseline, script, stub,
|
||||
&attached);
|
||||
if (newStub)
|
||||
JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub");
|
||||
}
|
||||
if (!attached)
|
||||
stub->state().trackNotAttached();
|
||||
}
|
||||
|
||||
stub->addNewStub(newStub);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1421,17 +1433,6 @@ ICGetIntrinsic_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
|
|||
return tailCallVM(DoGetIntrinsicFallbackInfo, masm);
|
||||
}
|
||||
|
||||
bool
|
||||
ICGetIntrinsic_Constant::Compiler::generateStubCode(MacroAssembler& masm)
|
||||
{
|
||||
MOZ_ASSERT(engine_ == Engine::Baseline);
|
||||
|
||||
masm.loadValue(Address(ICStubReg, ICGetIntrinsic_Constant::offsetOfValue()), R0);
|
||||
|
||||
EmitReturnFromIC(masm);
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// SetProp_Fallback
|
||||
//
|
||||
|
@ -4382,14 +4383,6 @@ ICTypeUpdate_ObjectGroup::ICTypeUpdate_ObjectGroup(JitCode* stubCode, ObjectGrou
|
|||
group_(group)
|
||||
{ }
|
||||
|
||||
ICGetIntrinsic_Constant::ICGetIntrinsic_Constant(JitCode* stubCode, const Value& value)
|
||||
: ICStub(GetIntrinsic_Constant, stubCode),
|
||||
value_(value)
|
||||
{ }
|
||||
|
||||
ICGetIntrinsic_Constant::~ICGetIntrinsic_Constant()
|
||||
{ }
|
||||
|
||||
ICCall_Scripted::ICCall_Scripted(JitCode* stubCode, ICStub* firstMonitorStub,
|
||||
JSFunction* callee, JSObject* templateObject,
|
||||
uint32_t pcOffset)
|
||||
|
|
|
@ -499,41 +499,6 @@ class ICGetIntrinsic_Fallback : public ICMonitoredFallbackStub
|
|||
};
|
||||
};
|
||||
|
||||
// Stub that loads the constant result of a GETINTRINSIC operation.
|
||||
class ICGetIntrinsic_Constant : public ICStub
|
||||
{
|
||||
friend class ICStubSpace;
|
||||
|
||||
GCPtrValue value_;
|
||||
|
||||
ICGetIntrinsic_Constant(JitCode* stubCode, const Value& value);
|
||||
~ICGetIntrinsic_Constant();
|
||||
|
||||
public:
|
||||
GCPtrValue& value() {
|
||||
return value_;
|
||||
}
|
||||
static size_t offsetOfValue() {
|
||||
return offsetof(ICGetIntrinsic_Constant, value_);
|
||||
}
|
||||
|
||||
class Compiler : public ICStubCompiler {
|
||||
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
|
||||
|
||||
HandleValue value_;
|
||||
|
||||
public:
|
||||
Compiler(JSContext* cx, HandleValue value)
|
||||
: ICStubCompiler(cx, ICStub::GetIntrinsic_Constant, Engine::Baseline),
|
||||
value_(value)
|
||||
{}
|
||||
|
||||
ICStub* getStub(ICStubSpace* space) override {
|
||||
return newStub<ICGetIntrinsic_Constant>(space, getStubCode(), value_);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// SetProp
|
||||
// JSOP_SETPROP
|
||||
// JSOP_SETNAME
|
||||
|
|
|
@ -56,7 +56,6 @@ namespace jit {
|
|||
_(BindName_Fallback) \
|
||||
\
|
||||
_(GetIntrinsic_Fallback) \
|
||||
_(GetIntrinsic_Constant) \
|
||||
\
|
||||
_(SetProp_Fallback) \
|
||||
\
|
||||
|
|
|
@ -4832,4 +4832,48 @@ ToBoolIRGenerator::tryAttachObject()
|
|||
writer.returnFromIC();
|
||||
trackAttached("ToBoolObject");
|
||||
return true;
|
||||
}
|
||||
|
||||
GetIntrinsicIRGenerator::GetIntrinsicIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, ICState::Mode mode,
|
||||
HandleValue val)
|
||||
: IRGenerator(cx, script, pc, CacheKind::GetIntrinsic, mode)
|
||||
, val_(val)
|
||||
{}
|
||||
|
||||
void
|
||||
GetIntrinsicIRGenerator::trackAttached(const char* name)
|
||||
{
|
||||
#ifdef JS_CACHEIR_SPEW
|
||||
CacheIRSpewer& sp = CacheIRSpewer::singleton();
|
||||
if (sp.enabled()) {
|
||||
LockGuard<Mutex> guard(sp.lock());
|
||||
sp.beginCache(guard, *this);
|
||||
sp.valueProperty(guard, "val", val_);
|
||||
sp.attached(guard, name);
|
||||
sp.endCache(guard);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
GetIntrinsicIRGenerator::trackNotAttached()
|
||||
{
|
||||
#ifdef JS_CACHEIR_SPEW
|
||||
CacheIRSpewer& sp = CacheIRSpewer::singleton();
|
||||
if (sp.enabled()) {
|
||||
LockGuard<Mutex> guard(sp.lock());
|
||||
sp.beginCache(guard, *this);
|
||||
sp.valueProperty(guard, "val", val_);
|
||||
sp.endCache(guard);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
GetIntrinsicIRGenerator::tryAttachStub()
|
||||
{
|
||||
writer.loadValueResult(val_);
|
||||
writer.returnFromIC();
|
||||
trackAttached("GetIntrinsic");
|
||||
return true;
|
||||
}
|
|
@ -142,6 +142,7 @@ class TypedOperandId : public OperandId
|
|||
_(GetName) \
|
||||
_(GetPropSuper) \
|
||||
_(GetElemSuper) \
|
||||
_(GetIntrinsic) \
|
||||
_(SetProp) \
|
||||
_(SetElem) \
|
||||
_(BindName) \
|
||||
|
@ -275,6 +276,7 @@ extern const char* CacheKindNames[];
|
|||
_(LoadDoubleTruthyResult) \
|
||||
_(LoadStringTruthyResult) \
|
||||
_(LoadObjectTruthyResult) \
|
||||
_(LoadValueResult) \
|
||||
\
|
||||
_(CallStringSplitResult) \
|
||||
\
|
||||
|
@ -1034,6 +1036,10 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter
|
|||
void loadObjectTruthyResult(ObjOperandId obj) {
|
||||
writeOpWithOperandId(CacheOp::LoadObjectTruthyResult, obj);
|
||||
}
|
||||
void loadValueResult(const Value& val) {
|
||||
writeOp(CacheOp::LoadValueResult);
|
||||
addStubField(val.asRawBits(), StubField::Type::Value);
|
||||
}
|
||||
void callStringSplitResult(StringOperandId str, StringOperandId sep, ObjectGroup* group) {
|
||||
writeOp(CacheOp::CallStringSplitResult);
|
||||
writeOperandId(str);
|
||||
|
@ -1642,6 +1648,20 @@ class MOZ_RAII ToBoolIRGenerator : public IRGenerator
|
|||
bool tryAttachStub();
|
||||
};
|
||||
|
||||
class MOZ_RAII GetIntrinsicIRGenerator : public IRGenerator
|
||||
{
|
||||
HandleValue val_;
|
||||
|
||||
void trackAttached(const char* name);
|
||||
void trackNotAttached();
|
||||
|
||||
public:
|
||||
GetIntrinsicIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc, ICState::Mode,
|
||||
HandleValue val);
|
||||
|
||||
bool tryAttachStub();
|
||||
};
|
||||
|
||||
} // namespace jit
|
||||
} // namespace js
|
||||
|
||||
|
|
|
@ -359,6 +359,7 @@ CodeGenerator::visitOutOfLineICFallback(OutOfLineICFallback* ool)
|
|||
case CacheKind::Compare:
|
||||
case CacheKind::TypeOf:
|
||||
case CacheKind::ToBool:
|
||||
case CacheKind::GetIntrinsic:
|
||||
MOZ_CRASH("Unsupported IC");
|
||||
}
|
||||
MOZ_CRASH();
|
||||
|
|
|
@ -547,6 +547,7 @@ IonCacheIRCompiler::init()
|
|||
case CacheKind::Compare:
|
||||
case CacheKind::TypeOf:
|
||||
case CacheKind::ToBool:
|
||||
case CacheKind::GetIntrinsic:
|
||||
MOZ_CRASH("Unsupported IC");
|
||||
}
|
||||
|
||||
|
@ -887,6 +888,13 @@ IonCacheIRCompiler::emitGuardFunctionPrototype()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
IonCacheIRCompiler::emitLoadValueResult()
|
||||
{
|
||||
MOZ_CRASH("Baseline-specific op");
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
IonCacheIRCompiler::emitLoadFixedSlotResult()
|
||||
{
|
||||
|
|
|
@ -64,6 +64,7 @@ IonIC::scratchRegisterForEntryJump()
|
|||
case CacheKind::Compare:
|
||||
case CacheKind::TypeOf:
|
||||
case CacheKind::ToBool:
|
||||
case CacheKind::GetIntrinsic:
|
||||
MOZ_CRASH("Unsupported IC");
|
||||
}
|
||||
|
||||
|
|
|
@ -292,11 +292,6 @@ ICStub::trace(JSTracer* trc)
|
|||
TraceEdge(trc, &updateStub->group(), "baseline-update-group");
|
||||
break;
|
||||
}
|
||||
case ICStub::GetIntrinsic_Constant: {
|
||||
ICGetIntrinsic_Constant* constantStub = toGetIntrinsic_Constant();
|
||||
TraceEdge(trc, &constantStub->value(), "baseline-getintrinsic-constant-value");
|
||||
break;
|
||||
}
|
||||
case ICStub::NewArray_Fallback: {
|
||||
ICNewArray_Fallback* stub = toNewArray_Fallback();
|
||||
TraceNullableEdge(trc, &stub->templateObject(), "baseline-newarray-template");
|
||||
|
|
|
@ -427,7 +427,11 @@ void MacroAssembler::LogicalMacro(const Register& rd,
|
|||
} else {
|
||||
// Immediate can't be encoded: synthesize using move immediate.
|
||||
Register temp = temps.AcquireSameSizeAs(rn);
|
||||
Operand imm_operand = MoveImmediateForShiftedOp(temp, immediate);
|
||||
|
||||
// If the left-hand input is the stack pointer, we can't pre-shift the
|
||||
// immediate, as the encoding won't allow the subsequent post shift.
|
||||
PreShiftImmMode mode = rn.IsSP() ? kNoShift : kAnyShift;
|
||||
Operand imm_operand = MoveImmediateForShiftedOp(temp, immediate, mode);
|
||||
|
||||
// VIXL can acquire temp registers. Assert that the caller is aware.
|
||||
VIXL_ASSERT(!temp.Is(rd) && !temp.Is(rn));
|
||||
|
@ -965,7 +969,8 @@ bool MacroAssembler::TryOneInstrMoveImmediate(const Register& dst,
|
|||
|
||||
|
||||
Operand MacroAssembler::MoveImmediateForShiftedOp(const Register& dst,
|
||||
int64_t imm) {
|
||||
int64_t imm,
|
||||
PreShiftImmMode mode) {
|
||||
int reg_size = dst.size();
|
||||
|
||||
// Encode the immediate in a single move instruction, if possible.
|
||||
|
@ -974,6 +979,14 @@ Operand MacroAssembler::MoveImmediateForShiftedOp(const Register& dst,
|
|||
} else {
|
||||
// Pre-shift the immediate to the least-significant bits of the register.
|
||||
int shift_low = CountTrailingZeros(imm, reg_size);
|
||||
if (mode == kLimitShiftForSP) {
|
||||
// When applied to the stack pointer, the subsequent arithmetic operation
|
||||
// can use the extend form to shift left by a maximum of four bits. Right
|
||||
// shifts are not allowed, so we filter them out later before the new
|
||||
// immediate is tested.
|
||||
shift_low = std::min(shift_low, 4);
|
||||
}
|
||||
|
||||
int64_t imm_low = imm >> shift_low;
|
||||
|
||||
// Pre-shift the immediate to the most-significant bits of the register,
|
||||
|
@ -981,11 +994,11 @@ Operand MacroAssembler::MoveImmediateForShiftedOp(const Register& dst,
|
|||
int shift_high = CountLeadingZeros(imm, reg_size);
|
||||
int64_t imm_high = (imm << shift_high) | ((INT64_C(1) << shift_high) - 1);
|
||||
|
||||
if (TryOneInstrMoveImmediate(dst, imm_low)) {
|
||||
if ((mode != kNoShift) && TryOneInstrMoveImmediate(dst, imm_low)) {
|
||||
// The new immediate has been moved into the destination's low bits:
|
||||
// return a new leftward-shifting operand.
|
||||
return Operand(dst, LSL, shift_low);
|
||||
} else if (TryOneInstrMoveImmediate(dst, imm_high)) {
|
||||
} else if ((mode == kAnyShift) && TryOneInstrMoveImmediate(dst, imm_high)) {
|
||||
// The new immediate has been moved into the destination's high bits:
|
||||
// return a new rightward-shifting operand.
|
||||
return Operand(dst, LSR, shift_high);
|
||||
|
@ -1037,19 +1050,27 @@ void MacroAssembler::AddSubMacro(const Register& rd,
|
|||
}
|
||||
|
||||
if ((operand.IsImmediate() && !IsImmAddSub(operand.immediate())) ||
|
||||
(rn.IsZero() && !operand.IsShiftedRegister()) ||
|
||||
(rn.IsZero() && !operand.IsShiftedRegister()) ||
|
||||
(operand.IsShiftedRegister() && (operand.shift() == ROR))) {
|
||||
UseScratchRegisterScope temps(this);
|
||||
Register temp = temps.AcquireSameSizeAs(rn);
|
||||
|
||||
// VIXL can acquire temp registers. Assert that the caller is aware.
|
||||
VIXL_ASSERT(!temp.Is(rd) && !temp.Is(rn));
|
||||
VIXL_ASSERT(!temp.Is(operand.maybeReg()));
|
||||
|
||||
if (operand.IsImmediate()) {
|
||||
PreShiftImmMode mode = kAnyShift;
|
||||
|
||||
// If the destination or source register is the stack pointer, we can
|
||||
// only pre-shift the immediate right by values supported in the add/sub
|
||||
// extend encoding.
|
||||
if (rd.IsSP()) {
|
||||
// If the destination is SP and flags will be set, we can't pre-shift
|
||||
// the immediate at all.
|
||||
mode = (S == SetFlags) ? kNoShift : kLimitShiftForSP;
|
||||
} else if (rn.IsSP()) {
|
||||
mode = kLimitShiftForSP;
|
||||
}
|
||||
|
||||
Operand imm_operand =
|
||||
MoveImmediateForShiftedOp(temp, operand.immediate());
|
||||
AddSub(rd, rn, imm_operand, S, op);
|
||||
MoveImmediateForShiftedOp(temp, operand.immediate(), mode);
|
||||
AddSub(rd, rn, imm_operand, S, op);
|
||||
} else {
|
||||
Mov(temp, operand);
|
||||
AddSub(rd, rn, temp, S, op);
|
||||
|
|
|
@ -142,6 +142,21 @@ enum BranchType {
|
|||
|
||||
enum DiscardMoveMode { kDontDiscardForSameWReg, kDiscardForSameWReg };
|
||||
|
||||
// The macro assembler supports moving automatically pre-shifted immediates for
|
||||
// arithmetic and logical instructions, and then applying a post shift in the
|
||||
// instruction to undo the modification, in order to reduce the code emitted for
|
||||
// an operation. For example:
|
||||
//
|
||||
// Add(x0, x0, 0x1f7de) => movz x16, 0xfbef; add x0, x0, x16, lsl #1.
|
||||
//
|
||||
// This optimisation can be only partially applied when the stack pointer is an
|
||||
// operand or destination, so this enumeration is used to control the shift.
|
||||
enum PreShiftImmMode {
|
||||
kNoShift, // Don't pre-shift.
|
||||
kLimitShiftForSP, // Limit pre-shift for add/sub extend use.
|
||||
kAnyShift // Allow any pre-shift.
|
||||
};
|
||||
|
||||
|
||||
class MacroAssembler : public js::jit::Assembler {
|
||||
public:
|
||||
|
@ -272,7 +287,9 @@ class MacroAssembler : public js::jit::Assembler {
|
|||
// into dst is not necessarily equal to imm; it may have had a shifting
|
||||
// operation applied to it that will be subsequently undone by the shift
|
||||
// applied in the Operand.
|
||||
Operand MoveImmediateForShiftedOp(const Register& dst, int64_t imm);
|
||||
Operand MoveImmediateForShiftedOp(const Register& dst,
|
||||
int64_t imm,
|
||||
PreShiftImmMode mode);
|
||||
|
||||
// Synthesises the address represented by a MemOperand into a register.
|
||||
void ComputeAddress(const Register& dst, const MemOperand& mem_op);
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче