Merge mozilla-central to autoland. a=merge CLOSED TREE

This commit is contained in:
shindli 2018-02-08 12:13:08 +02:00
commit 0fcb667b73
224 changed files with 2772 additions and 1833 deletions

1
.mailmap Normal file
View File

@ -0,0 +1 @@
Nika Layzell <nika@thelayzells.com> Michael Layzell <michael@thelayzells.com>

View File

@ -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,

View File

@ -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.

View File

@ -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,

View File

@ -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);
}
}

View File

@ -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.

View File

@ -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.

View File

@ -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;

View File

@ -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);

View File

@ -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();

View File

@ -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();

View File

@ -779,8 +779,6 @@
completeselectedindex="true"
shrinkdelay="250"
tabscrolling="true"
showcommentcolumn="true"
showimagecolumn="true"
newlines="stripsurroundingwhitespace"
ontextentered="this.handleCommand(param);"
ontextreverted="return this.handleRevert();"

View File

@ -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);

View File

@ -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;

View File

@ -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",

View File

@ -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]

View File

@ -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");

View File

@ -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.

View File

@ -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) {

View File

@ -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

View File

@ -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.");
});

View File

@ -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");
});

View File

@ -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);

View File

@ -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");
});

View File

@ -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"

View File

@ -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:

View File

@ -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";

View File

@ -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

View File

@ -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

View File

@ -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',

View File

@ -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)

View File

@ -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/*

View File

@ -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

View File

@ -0,0 +1,5 @@
%include build/sparse-profiles/mach
[include]
path:build/mozrelease
path:testing/mozharness

View File

@ -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();
}
}
},

View File

@ -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]

View File

@ -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.");
});

View File

@ -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}`);
}

View File

@ -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();
},

View File

@ -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;
}

View File

@ -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 doesnt 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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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]

View File

@ -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>

View File

@ -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>

View File

@ -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

View File

@ -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));

View File

@ -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': [

View File

@ -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);
}

View File

@ -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, &notified, &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;
}

View File

@ -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 {
};

View File

@ -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

View File

@ -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 */

View File

@ -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());
}

160
dom/workers/Worker.cpp Normal file
View File

@ -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

64
dom/workers/Worker.h Normal file
View File

@ -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 */

View File

@ -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;
}
};

View File

@ -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
};

View File

@ -28,6 +28,8 @@
namespace mozilla {
namespace dom {
using namespace workerinternals;
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WorkerNavigator, mStorageManager,
mConnection);

View File

@ -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,

View File

@ -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;

View File

@ -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());

View File

@ -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',

View File

@ -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

View File

@ -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>

View File

@ -0,0 +1 @@
postMessage("Ok!");

View File

@ -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);

View File

@ -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)
{

View File

@ -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.

View File

@ -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.

View File

@ -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;

View File

@ -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();
}

View File

@ -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;

View File

@ -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
*/

View File

@ -360,6 +360,7 @@ DrawTargetRecording::FillGlyphs(ScaledFont *aFont,
}
mRecorder->AddStoredObject(unscaledFont);
}
mRecorder->RecordEvent(RecordedScaledFontCreation(aFont, unscaledFont));
}
RecordingFontUserData *userData = new RecordingFontUserData;
userData->refPtr = aFont;

View File

@ -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;
}

View File

@ -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();

View File

@ -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>

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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)

View File

@ -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

View File

@ -56,7 +56,6 @@ namespace jit {
_(BindName_Fallback) \
\
_(GetIntrinsic_Fallback) \
_(GetIntrinsic_Constant) \
\
_(SetProp_Fallback) \
\

View File

@ -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;
}

View File

@ -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

View File

@ -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();

View File

@ -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()
{

View File

@ -64,6 +64,7 @@ IonIC::scratchRegisterForEntryJump()
case CacheKind::Compare:
case CacheKind::TypeOf:
case CacheKind::ToBool:
case CacheKind::GetIntrinsic:
MOZ_CRASH("Unsupported IC");
}

View File

@ -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");

View File

@ -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);

View File

@ -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);

Some files were not shown because too many files have changed in this diff Show More