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

This commit is contained in:
Oana Pop Rus 2019-05-09 01:01:06 +03:00
commit 3d78756bdb
82 changed files with 2690 additions and 982 deletions

View File

@ -0,0 +1,419 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* This test records I/O syscalls done on the main thread during startup.
*
* To run this test similar to try server, you need to run:
* ./mach package
* ./mach test --appname=dist <path to test>
*
* If you made changes that cause this test to fail, it's likely because you
* are touching more files or directories during startup.
* Most code has no reason to use main thread I/O.
* If for some reason accessing the file system on the main thread is currently
* unavoidable, consider defering the I/O as long as you can, ideally after
* the end of startup.
*/
"use strict";
/* Set this to true only for debugging purpose; it makes the output noisy. */
const kDumpAllStacks = false;
// Shortcuts for conditions.
const LINUX = AppConstants.platform == "linux";
const WIN = AppConstants.platform == "win";
const MAC = AppConstants.platform == "macosx";
/* Paths in the whitelist can:
* - be a full path, eg. "/etc/mime.types"
* - have a prefix which will be resolved using Services.dirsvc
* eg. "GreD:omni.ja"
* It's possible to have only a prefix, in thise case the directory will
* still be resolved, eg. "UAppData:"
* - use * at the begining and/or end as a wildcard
* The folder separator is '/' even for Windows paths, where it'll be
* automatically converted to '\'.
*
* Specifying 'ignoreIfUnused: true' will make the test ignore unused entries;
* without this the test is strict and will fail if a whitelist entry isn't used.
*
* Each entry specifies the maximum number of times an operation is expected to
* occur.
* The operations currently reported by the I/O interposer are:
* create/open: only supported on Windows currently. The test currently
* ignores these markers to have a shorter initial whitelist.
* Adding Unix support is bug 1533779.
* stat: supported on all platforms when checking the last modified date or
* file size. Supported only on Windows when checking if a file exists;
* fixing this inconsistency is bug 1536109.
* read: supported on all platforms, but unix platforms will only report read
* calls going through NSPR.
* write: supported on all platforms, but Linux will only report write calls
* going through NSPR.
* close: supported only on Unix, and only for close calls going through NSPR.
* Adding Windows support is bug 1524574.
* fsync: supported only on Windows.
*
* If an entry specifies more than one operation, if at least one of them is
* encountered, the test won't report a failure for the entry. This helps when
* whitelisting cases where the reported operations aren't the same on all
* platforms due to the I/O interposer inconsistencies across platforms
* documented above.
*/
const processes = {
"Web Content": [
{
path: "GreD:omni.ja",
condition: !WIN, // Visible on Windows with an open marker
stat: 1,
},
{ // bug 1376994
path: "XCurProcD:omni.ja",
condition: !WIN, // Visible on Windows with an open marker
stat: 1,
},
{ // bug 1543761
path: "GreD:chrome.manifest",
condition: !WIN, // Visible on Windows with an open marker
stat: 1,
close: 1,
},
{ // bug 1376994, bug 1543761
path: "XCurProcD:chrome.manifest",
condition: !WIN, // Visible on Windows with an open marker
stat: 1,
close: 1,
},
{ // Exists call in ScopedXREEmbed::SetAppDir
path: "XCurProcD:",
condition: WIN,
stat: 1,
},
{ // bug 1357205
path: "XREAppFeat:webcompat@mozilla.org.xpi",
condition: !WIN,
stat: 1,
},
{ // bug 1357205
path: "XREAppFeat:formautofill@mozilla.org.xpi",
condition: !WIN,
stat: 1,
},
],
"Privileged Content": [
{
path: "GreD:omni.ja",
condition: !WIN, // Visible on Windows with an open marker
stat: 1,
},
{ // bug 1376994
path: "XCurProcD:omni.ja",
condition: !WIN, // Visible on Windows with an open marker
stat: 1,
},
{ // bug 1543761
path: "GreD:chrome.manifest",
condition: !WIN, // Visible on Windows with an open marker
stat: 1,
close: 1,
},
{ // bug 1376994, bug 1543761
path: "XCurProcD:chrome.manifest",
condition: !WIN, // Visible on Windows with an open marker
stat: 1,
close: 1,
},
{ // Exists call in ScopedXREEmbed::SetAppDir
path: "XCurProcD:",
condition: WIN,
stat: 1,
},
{ // bug 1357205
path: "XREAppFeat:webcompat@mozilla.org.xpi",
condition: !WIN,
stat: 1,
},
],
"WebExtensions": [
{
path: "GreD:omni.ja",
condition: !WIN, // Visible on Windows with an open marker
stat: 1,
},
{ // bug 1376994
path: "XCurProcD:omni.ja",
condition: !WIN, // Visible on Windows with an open marker
stat: 1,
},
{ // bug 1543761
path: "GreD:chrome.manifest",
condition: !WIN, // Visible on Windows with an open marker
stat: 1,
close: 1,
},
{ // bug 1376994, bug 1543761
path: "XCurProcD:chrome.manifest",
condition: !WIN, // Visible on Windows with an open marker
stat: 1,
close: 1,
},
{ // Exists call in ScopedXREEmbed::SetAppDir
path: "XCurProcD:",
condition: WIN,
stat: 1,
},
{ // bug 1357205
path: "XREAppFeat:webcompat@mozilla.org.xpi",
condition: !WIN,
stat: 1,
},
{ // bug 1357205
path: "XREAppFeat:formautofill@mozilla.org.xpi",
condition: !WIN,
stat: 1,
},
{ // bug 1357205
path: "XREAppFeat:screenshots@mozilla.org.xpi",
condition: !WIN,
close: 1,
},
],
};
function expandWhitelistPath(path) {
if (path.includes(":")) {
let [prefix, suffix] = path.split(":");
let [key, property] = prefix.split(".");
let dir = Services.dirsvc.get(key, Ci.nsIFile);
if (property) {
dir = dir[property];
}
// Resolve symLinks.
let dirPath = dir.path;
while (dir && !dir.isSymlink()) {
dir = dir.parent;
}
if (dir) {
dirPath = dirPath.replace(dir.path, dir.target);
}
path = dirPath;
if (suffix) {
path += "/" + suffix;
}
}
if (AppConstants.platform == "win") {
path = path.replace(/\//g, "\\");
}
return path;
}
function getStackFromProfile(profile, stack) {
const stackPrefixCol = profile.stackTable.schema.prefix;
const stackFrameCol = profile.stackTable.schema.frame;
const frameLocationCol = profile.frameTable.schema.location;
let result = [];
while (stack) {
let sp = profile.stackTable.data[stack];
let frame = profile.frameTable.data[sp[stackFrameCol]];
stack = sp[stackPrefixCol];
frame = profile.stringTable[frame[frameLocationCol]];
if (frame != "js::RunScript" && !frame.startsWith("next (self-hosted:")) {
result.push(frame);
}
}
return result;
}
function getIOMarkersFromProfile(profile) {
const nameCol = profile.markers.schema.name;
const dataCol = profile.markers.schema.data;
let markers = [];
for (let m of profile.markers.data) {
let markerName = profile.stringTable[m[nameCol]];
if (markerName != "FileIO")
continue;
let markerData = m[dataCol];
if (markerData.source == "sqlite-mainthread") {
continue;
}
let samples = markerData.stack.samples;
let stack = samples.data[0][samples.schema.stack];
markers.push({operation: markerData.operation,
filename: markerData.filename,
source: markerData.source,
stackId: stack});
}
return markers;
}
function pathMatches(path, filename) {
path = path.toLowerCase();
return path == filename || // Full match
// Wildcard on both sides of the path
(path.startsWith("*") && path.endsWith("*") &&
filename.includes(path.slice(1, -1))) ||
// Wildcard suffix
(path.endsWith("*") && filename.startsWith(path.slice(0, -1))) ||
// Wildcard prefix
(path.startsWith("*") && filename.endsWith(path.slice(1)));
}
add_task(async function() {
if (!AppConstants.NIGHTLY_BUILD && !AppConstants.MOZ_DEV_EDITION && !AppConstants.DEBUG) {
ok(!("@mozilla.org/test/startuprecorder;1" in Cc),
"the startup recorder component shouldn't exist in this non-nightly/non-devedition/" +
"non-debug build.");
return;
}
{
let omniJa = Services.dirsvc.get("XCurProcD", Ci.nsIFile);
omniJa.append("omni.ja");
if (!omniJa.exists()) {
ok(false, "This test requires a packaged build, " +
"run 'mach package' and then use --appname=dist");
return;
}
}
let startupRecorder = Cc["@mozilla.org/test/startuprecorder;1"].getService().wrappedJSObject;
await startupRecorder.done;
for (let process in processes) {
processes[process] =
processes[process].filter(entry => !("condition" in entry) || entry.condition);
processes[process].forEach(entry => {
entry.path = expandWhitelistPath(entry.path, entry.canonicalize);
});
}
let tmpPath = expandWhitelistPath(MAC ? "TmpD:" : "/dev/shm").toLowerCase();
let shouldPass = true;
for (let procName in processes) {
let whitelist = processes[procName];
info(`whitelisted paths for ${procName} process:\n` +
whitelist.map(e => {
let operations = Object.keys(e).filter(k => !["path", "condition"].includes(k))
.map(k => `${k}: ${e[k]}`);
return ` ${e.path} - ${operations.join(", ")}`;
}).join("\n"));
let profile;
for (let process of startupRecorder.data.profile.processes) {
if (process.threads[0].processName == procName) {
profile = process.threads[0];
break;
}
}
if (procName == "Privileged Content" && !profile) {
// The Privileged Content is started from an idle task that may not have
// been executed yet at the time we captured the startup profile in
// startupRecorder.
todo(false, `profile for ${procName} process not found`);
} else {
ok(profile, `Found profile for ${procName} process`);
}
if (!profile) {
continue;
}
let markers = getIOMarkersFromProfile(profile);
for (let marker of markers) {
if (marker.operation == "create/open") {
// TODO: handle these I/O markers once they are supported on
// non-Windows platforms.
continue;
}
// Convert to lower case before comparing because the OS X test slaves
// have the 'Firefox' folder in 'Library/Application Support' created
// as 'firefox' for some reason.
let filename = marker.filename.toLowerCase();
if (!filename) {
// We are still missing the filename on some mainthreadio markers,
// these markers are currently useless for the purpose of this test.
continue;
}
if (!WIN) {
if (filename == "/dev/urandom") {
continue;
}
// Ignore I/O due to IPC. This doesn't really touch the disk.
if (filename.startsWith(tmpPath + "/org.chromium.")) {
continue;
}
}
let expected = false;
for (let entry of whitelist) {
if (pathMatches(entry.path, filename)) {
entry[marker.operation] = (entry[marker.operation] || 0) - 1;
entry._used = true;
expected = true;
break;
}
}
if (!expected) {
record(false,
`unexpected ${marker.operation} on ${marker.filename} in ${procName} process`,
undefined,
" " + getStackFromProfile(profile, marker.stackId).join("\n "));
shouldPass = false;
}
info(`(${marker.source}) ${marker.operation} - ${marker.filename}`);
if (kDumpAllStacks) {
info(getStackFromProfile(profile, marker.stackId).map(f => " " + f)
.join("\n"));
}
}
for (let entry of whitelist) {
for (let op in entry) {
if (["path", "condition", "ignoreIfUnused", "_used"].includes(op)) {
continue;
}
let message = `${op} on ${entry.path} `;
if (entry[op] == 0) {
message += "as many times as expected";
} else if (entry[op] > 0) {
message += `allowed ${entry[op]} more times`;
} else {
message += `${entry[op] * -1} more times than expected`;
}
ok(entry[op] >= 0, `${message} in ${procName} process`);
}
if (!("_used" in entry) && !entry.ignoreIfUnused) {
ok(false, `unused whitelist entry ${procName}: ${entry.path}`);
}
}
}
if (shouldPass) {
ok(shouldPass, "No unexpected main thread I/O during startup");
} else {
const filename = "child-startup-mainthreadio-profile.json";
let path = Cc["@mozilla.org/process/environment;1"]
.getService(Ci.nsIEnvironment)
.get("MOZ_UPLOAD_DIR");
let encoder = new TextEncoder();
let profilePath = OS.Path.join(path, filename);
await OS.File.writeAtomic(profilePath,
encoder.encode(JSON.stringify(startupRecorder.data.profile)));
ok(false,
"Found some unexpected main thread I/O during child process startup; " +
"profile uploaded in " + filename);
}
});

View File

@ -0,0 +1,933 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* This test records I/O syscalls done on the main thread during startup.
*
* To run this test similar to try server, you need to run:
* ./mach package
* ./mach test --appname=dist <path to test>
*
* If you made changes that cause this test to fail, it's likely because you
* are touching more files or directories during startup.
* Most code has no reason to use main thread I/O.
* If for some reason accessing the file system on the main thread is currently
* unavoidable, consider defering the I/O as long as you can, ideally after
* the end of startup.
* If your code isn't strictly required to show the first browser window,
* it shouldn't be loaded before we are done with first paint.
* Finally, if your code isn't really needed during startup, it should not be
* loaded before we have started handling user events.
*/
"use strict";
const { AddonManager } = ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
/* Set this to true only for debugging purpose; it makes the output noisy. */
const kDumpAllStacks = false;
// Shortcuts for conditions.
const LINUX = AppConstants.platform == "linux";
const WIN = AppConstants.platform == "win";
const MAC = AppConstants.platform == "macosx";
/* Paths in the whitelist can:
* - be a full path, eg. "/etc/mime.types"
* - have a prefix which will be resolved using Services.dirsvc
* eg. "GreD:omni.ja"
* It's possible to have only a prefix, in thise case the directory will
* still be resolved, eg. "UAppData:"
* - use * at the begining and/or end as a wildcard
* - For Windows specific entries that require resolving the path to its
* canonical form, ie. the old DOS 8.3 format, use canonicalize: true.
* This is needed for stat calls to non-existent files.
* The folder separator is '/' even for Windows paths, where it'll be
* automatically converted to '\'.
*
* Specifying 'ignoreIfUnused: true' will make the test ignore unused entries;
* without this the test is strict and will fail if a whitelist entry isn't used.
*
* Each entry specifies the maximum number of times an operation is expected to
* occur.
* The operations currently reported by the I/O interposer are:
* create/open: only supported on Windows currently. The test currently
* ignores these markers to have a shorter initial whitelist.
* Adding Unix support is bug 1533779.
* stat: supported on all platforms when checking the last modified date or
* file size. Supported only on Windows when checking if a file exists;
* fixing this inconsistency is bug 1536109.
* read: supported on all platforms, but unix platforms will only report read
* calls going through NSPR.
* write: supported on all platforms, but Linux will only report write calls
* going through NSPR.
* close: supported only on Unix, and only for close calls going through NSPR.
* Adding Windows support is bug 1524574.
* fsync: supported only on Windows.
*
* If an entry specifies more than one operation, if at least one of them is
* encountered, the test won't report a failure for the entry. This helps when
* whitelisting cases where the reported operations aren't the same on all
* platforms due to the I/O interposer inconsistencies across platforms
* documented above.
*/
const startupPhases = {
// Anything done before or during app-startup must have a compelling reason
// to run before we have even selected the user profile.
"before profile selection": [
{ // bug 1541226
path: "UAppData:",
condition: WIN,
stat: 3,
},
{ // bug 1541200
path: "UAppData:Crash Reports/InstallTime20*",
condition: AppConstants.MOZ_CRASHREPORTER,
stat: 1, // only caught on Windows.
read: 1,
write: 2,
close: 1,
},
{ // bug 1541200
path: "UAppData:Crash Reports/LastCrash",
condition: WIN && AppConstants.MOZ_CRASHREPORTER,
stat: 1, // only caught on Windows.
read: 1,
},
{ // bug 1541200
path: "UAppData:Crash Reports/LastCrash",
condition: !WIN && AppConstants.MOZ_CRASHREPORTER,
ignoreIfUnused: true, // only if we ever crashed on this machine
read: 1,
close: 1,
},
{ // bug 1541226
path: "DefProfLRt.parent:",
condition: WIN,
stat: 1,
},
{ // At least the read seems unavoidable for a regular startup.
path: "UAppData:profiles.ini",
condition: MAC,
stat: 1,
read: 1,
close: 1,
},
{ // bug 1546931
path: "UAppData:installs.ini",
condition: WIN || MAC,
ignoreIfUnused: true, // only if a real profile exists on the system.
read: 1,
stat: 2,
close: 1,
},
{ // At least the read seems unavoidable for a regular startup.
path: "UAppData:profiles.ini",
condition: WIN,
ignoreIfUnused: true, // only if a real profile exists on the system.
read: 1,
stat: 1,
},
{ // bug 1541226, bug 1363586, bug 1541593
path: "ProfD:",
condition: WIN,
stat: 3,
},
{
path: "ProfLD:.startup-incomplete",
condition: !WIN, // Visible on Windows with an open marker
close: 1,
},
{ // bug 1541491 to stop using this file, bug 1541494 to write correctly.
path: "ProfLD:compatibility.ini",
condition: !WIN, // Visible on Windows with an open marker
write: 18,
close: 1,
},
{
path: "GreD:omni.ja",
condition: !WIN, // Visible on Windows with an open marker
stat: 1,
},
{ // bug 1376994
path: "XCurProcD:omni.ja",
condition: !WIN, // Visible on Windows with an open marker
stat: 1,
},
{
path: "ProfD:parent.lock",
condition: WIN,
stat: 1,
},
{ // bug 1541603
path: "ProfD:minidumps",
condition: WIN,
stat: 1,
},
{ // bug 1543746
path: "XCurProcD:defaults/preferences",
condition: WIN,
stat: 1,
},
{ // bug 1544034
path: "ProfLDS:startupCache/scriptCache-child-current.bin",
condition: WIN,
stat: 1,
},
{ // bug 1544034
path: "ProfLDS:startupCache/scriptCache-child.bin",
condition: WIN,
stat: 1,
},
{ // bug 1544034
path: "ProfLDS:startupCache/scriptCache-current.bin",
condition: WIN,
stat: 1,
},
{ // bug 1544034
path: "ProfLDS:startupCache/scriptCache.bin",
condition: WIN,
stat: 1,
},
{ // bug 1544037
path: "ProfLDS:startupCache/startupCache." +
(Services.appinfo.is64Bit ? 8 : 4) + ".little",
condition: WIN,
stat: 1,
},
{ // bug 1541601
path: "PrfDef:channel-prefs.js",
stat: 1,
read: 1,
close: 1,
},
{ // bug 1543761
path: "GreD:chrome.manifest",
condition: !WIN, // Visible on Windows with an open marker
stat: 1,
close: 1,
},
{ // bug 1376994, bug 1543761
path: "XCurProcD:chrome.manifest",
condition: !WIN, // Visible on Windows with an open marker
stat: 1,
close: 1,
},
{ // At least the read seems unavoidable
path: "PrefD:prefs.js",
stat: 1,
read: 1,
close: 1,
},
{ // bug 1543752
path: "PrefD:user.js",
stat: 1,
read: 1,
close: 1,
},
{
path: "*ld.so.conf*",
condition: LINUX,
ignoreIfUnused: true,
read: 22,
close: 11,
},
{ // bug 1546838
path: "ProfD:xulstore/data.mdb",
condition: WIN,
write: 1,
fsync: 1,
},
],
"before opening first browser window": [
{ // bug 1541226
path: "ProfD:",
condition: WIN,
stat: 2,
},
{
path: "XCurProcD:blocklist.xml",
condition: WIN,
stat: 1,
},
{ // bug 1534745
path: "ProfD:cookies.sqlite-journal",
condition: !LINUX,
stat: 3,
write: 4,
},
{ // bug 1534745
path: "ProfD:cookies.sqlite",
condition: !LINUX,
stat: 2,
read: 2,
write: 1,
},
{ // bug 1534745
path: "ProfD:cookies.sqlite-wal",
condition: WIN,
stat: 2,
},
{ // bug 975996
path: "ProfD:permissions.sqlite",
condition: WIN || MAC,
fsync: 7,
read: 2,
stat: 1,
write: 10,
},
{ // bug 975996
path: "ProfD:permissions.sqlite-journal",
condition: WIN || MAC,
fsync: 7,
stat: 26,
write: 38,
},
{ // bug 975996
path: "ProfD:permissions.sqlite-wal",
condition: WIN,
stat: 20,
},
{ // Seems done by OS X and outside of our control.
path: "*.savedState/restorecount.plist",
condition: MAC,
ignoreIfUnused: true,
write: 1,
},
{
path: "*ld.so.conf*",
condition: LINUX,
ignoreIfUnused: true,
read: 22,
close: 11,
},
{ // bug 1545167
path: "/etc/mime.types",
condition: LINUX,
read: 3,
close: 3,
},
{
path: "UChrm:userChrome.css",
condition: WIN,
stat: 1,
},
{ // bug 1541233
path: "UChrm:userContent.css",
condition: WIN,
stat: 1,
},
{ // bug 1541246
path: "XREUSysExt:",
condition: WIN,
stat: 1,
},
{ // bug 1541246
path: "XRESysExtDev:",
condition: WIN,
stat: 1,
},
{ // bug 1541246
path: "ProfD:extensions",
condition: WIN,
stat: 1,
},
{ // bug 1541246
path: "XCurProcD:extensions",
condition: WIN,
stat: 1,
},
{ // bug 1541246
path: "UAppData:",
ignoreIfUnused: true, // sometimes before opening first browser window,
// sometimes before first paint
condition: WIN,
stat: 1,
},
{ // bug 1546838
path: "ProfD:xulstore/data.mdb",
condition: WIN,
read: 1,
},
],
// We reach this phase right after showing the first browser window.
// This means that any I/O at this point delayed first paint.
"before first paint": [
{ // bug 1541226
path: "ProfD:",
condition: WIN,
stat: 1,
},
{ // bug 1545119
path: "OldUpdRootD:",
condition: WIN,
stat: 1,
},
{ // bug 1446012
path: "UpdRootD:updates/0/update.status",
condition: WIN,
stat: 1,
},
{ // bug 1545123
path: "ProfD:pluginreg.dat",
condition: WIN,
stat: 1,
},
{ // bug 1545123
path: "ProfD:pluginreg.dat.tmp",
stat: 1,
write: 64,
close: 1,
},
{ // bug 1545123
path: "ProfD:plugins",
condition: WIN,
stat: 1,
},
{ // bug 1545123
path: "APlugns:",
condition: WIN,
stat: 1,
},
{ // bug 1545123
path: "UserPlugins.parent:",
condition: WIN,
stat: 1,
},
{ // bug 1545123
path: "UserPlugins:",
condition: WIN,
stat: 1,
},
{ // bug 1545123
path: "ProfD:plugins/nptest.dll",
condition: WIN,
stat: 1,
},
{ // bug 1545123
path: "ProfD:plugins/npsecondtest.dll",
condition: WIN,
stat: 1,
},
{ // bug 1545123
path: "ProfD:plugins/npthirdtest.dll",
condition: WIN,
stat: 1,
},
{ // bug 1545123
path: "ProfD:plugins/npswftest.dll",
condition: WIN,
stat: 1,
},
{
path: "XREAppFeat:formautofill@mozilla.org.xpi",
condition: !WIN,
stat: 1,
close: 1,
},
{ // bug 1545167
path: "/etc/mime.types",
condition: LINUX,
read: 1,
close: 1,
},
{ // We only hit this for new profiles.
path: "XREAppDist:distribution.ini",
condition: WIN,
stat: 1,
},
{
path: "*WindowsApps/microsoft.windowscommunicationsapps*",
condition: WIN,
ignoreIfUnused: true,
stat: 3,
},
{ // bug 1545139
path: "*Fonts/StaticCache.dat",
condition: WIN,
ignoreIfUnused: true, // Only on Win7
read: 1,
},
{ // bug 1541246
path: "UAppData:",
ignoreIfUnused: true, // sometimes before opening first browser window,
// sometimes before first paint
condition: WIN,
stat: 1,
},
{ // Not in packaged builds; useful for artifact builds.
path: "GreD:ScalarArtifactDefinitions.json",
condition: WIN && !AppConstants.MOZILLA_OFFICIAL,
stat: 1,
},
{ // Not in packaged builds; useful for artifact builds.
path: "GreD:EventArtifactDefinitions.json",
condition: WIN && !AppConstants.MOZILLA_OFFICIAL,
stat: 1,
},
{ // bug 1546838
path: "ProfD:xulstore/data.mdb",
condition: MAC,
write: 3,
},
{ // bug 1543090
path: "GreD:omni.ja",
condition: WIN,
stat: 1,
},
{ // bug 1543090
path: "XCurProcD:omni.ja",
condition: WIN,
stat: 2,
},
],
// We are at this phase once we are ready to handle user events.
// Any IO at this phase or before gets in the way of the user
// interacting with the first browser window.
"before handling user events": [
{
path: "GreD:update.test",
ignoreIfUnused: true,
condition: LINUX,
close: 1,
},
{ // bug 1370516 - NSS should be initialized off main thread.
path: "ProfD:cert9.db",
condition: WIN,
read: 2,
stat: 2,
},
{ // bug 1370516 - NSS should be initialized off main thread.
path: "ProfD:cert9.db",
condition: WIN,
ignoreIfUnused: true, // if canonicalize(ProfD) == ProfD, we'll use the previous entry.
canonicalize: true,
stat: 2,
},
{ // bug 1370516 - NSS should be initialized off main thread.
path: "ProfD:cert9.db-journal",
condition: WIN,
canonicalize: true,
stat: 2,
},
{ // bug 1370516 - NSS should be initialized off main thread.
path: "ProfD:cert9.db-wal",
condition: WIN,
canonicalize: true,
stat: 2,
},
{ // bug 1370516 - NSS should be initialized off main thread.
path: "ProfD:pkcs11.txt",
condition: WIN,
read: 2,
},
{ // bug 1370516 - NSS should be initialized off main thread.
path: "ProfD:key4.db",
condition: WIN,
read: 2,
stat: 2,
},
{ // bug 1370516 - NSS should be initialized off main thread.
path: "ProfD:key4.db",
condition: WIN,
ignoreIfUnused: true, // if canonicalize(ProfD) == ProfD, we'll use the previous entry.
canonicalize: true,
stat: 2,
},
{ // bug 1370516 - NSS should be initialized off main thread.
path: "ProfD:key4.db-journal",
condition: WIN,
canonicalize: true,
stat: 5,
},
{ // bug 1370516 - NSS should be initialized off main thread.
path: "ProfD:key4.db-wal",
condition: WIN,
canonicalize: true,
stat: 5,
},
{
path: "XREAppFeat:webcompat-reporter@mozilla.org.xpi",
condition: !WIN,
ignoreIfUnused: true,
stat: 1,
close: 1,
},
{ // bug 1003968
path: "XREAppDist:searchplugins",
condition: WIN,
stat: 1,
},
{
path: "XCurProcD:extensions",
condition: WIN,
stat: 1,
},
{ // bug 1543090
path: "GreD:omni.ja",
condition: WIN,
stat: 1,
},
{ // bug 1543090
path: "XCurProcD:omni.ja",
condition: WIN,
stat: 2,
},
],
// Things that are expected to be completely out of the startup path
// and loaded lazily when used for the first time by the user should
// be blacklisted here.
"before becoming idle": [
{
path: "XREAppFeat:screenshots@mozilla.org.xpi",
ignoreIfUnused: true,
close: 1,
},
{
path: "XREAppFeat:webcompat-reporter@mozilla.org.xpi",
ignoreIfUnused: true,
stat: 1,
close: 1,
},
{ // bug 1391590
path: "ProfD:places.sqlite-journal",
ignoreIfUnused: true,
fsync: 1,
stat: 4,
write: 2,
},
{ // bug 1391590
path: "ProfD:places.sqlite-wal",
ignoreIfUnused: true,
stat: 4,
fsync: 3,
write: 148,
},
{ // bug 1391590
path: "ProfD:places.sqlite-shm",
condition: WIN,
ignoreIfUnused: true,
stat: 1,
},
{ // bug 1391590
path: "ProfD:places.sqlite",
ignoreIfUnused: true,
fsync: 2,
read: 1,
stat: 3,
write: 1310,
},
{ // bug 1391590
path: "ProfD:favicons.sqlite-journal",
ignoreIfUnused: true,
fsync: 2,
stat: 7,
write: 7,
},
{ // bug 1391590
path: "ProfD:favicons.sqlite-wal",
ignoreIfUnused: true,
fsync: 2,
stat: 7,
write: 15,
},
{ // bug 1391590
path: "ProfD:favicons.sqlite-shm",
condition: WIN,
ignoreIfUnused: true,
stat: 2,
},
{ // bug 1391590
path: "ProfD:favicons.sqlite",
ignoreIfUnused: true,
fsync: 3,
read: 4,
stat: 4,
write: 1300,
},
{
path: "ProfD:key4.db-journal",
condition: WIN,
canonicalize: true,
stat: 2,
},
{
path: "ProfD:key4.db-wal",
condition: WIN,
canonicalize: true,
stat: 2,
},
{
path: "ProfD:",
condition: WIN,
ignoreIfUnused: true,
stat: 3,
},
{ // bug 1543090
path: "XCurProcD:omni.ja",
condition: WIN,
stat: 7,
},
],
};
for (let name of ["d3d11layers", "d3d9video", "glcontext", "d3d11video", "wmfvpxvideo"]) {
startupPhases["before first paint"].push({
path: `ProfD:${name}.guard`,
ignoreIfUnused: true,
stat: 1,
});
}
function expandWhitelistPath(path, canonicalize = false) {
if (path.includes(":")) {
let [prefix, suffix] = path.split(":");
let [key, property] = prefix.split(".");
let dir = Services.dirsvc.get(key, Ci.nsIFile);
if (property) {
dir = dir[property];
}
if (canonicalize) {
path = dir.QueryInterface(Ci.nsILocalFileWin).canonicalPath;
} else {
// Resolve symLinks.
let dirPath = dir.path;
while (dir && !dir.isSymlink()) {
dir = dir.parent;
}
if (dir) {
dirPath = dirPath.replace(dir.path, dir.target);
}
path = dirPath;
}
if (suffix) {
path += "/" + suffix;
}
}
if (AppConstants.platform == "win") {
path = path.replace(/\//g, "\\");
}
return path;
}
function getStackFromProfile(profile, stack) {
const stackPrefixCol = profile.stackTable.schema.prefix;
const stackFrameCol = profile.stackTable.schema.frame;
const frameLocationCol = profile.frameTable.schema.location;
let result = [];
while (stack) {
let sp = profile.stackTable.data[stack];
let frame = profile.frameTable.data[sp[stackFrameCol]];
stack = sp[stackPrefixCol];
frame = profile.stringTable[frame[frameLocationCol]];
if (frame != "js::RunScript" && !frame.startsWith("next (self-hosted:")) {
result.push(frame);
}
}
return result;
}
function pathMatches(path, filename) {
path = path.toLowerCase();
return path == filename || // Full match
// Wildcard on both sides of the path
(path.startsWith("*") && path.endsWith("*") &&
filename.includes(path.slice(1, -1))) ||
// Wildcard suffix
(path.endsWith("*") && filename.startsWith(path.slice(0, -1))) ||
// Wildcard prefix
(path.startsWith("*") && filename.endsWith(path.slice(1)));
}
add_task(async function() {
if (!AppConstants.NIGHTLY_BUILD && !AppConstants.MOZ_DEV_EDITION && !AppConstants.DEBUG) {
ok(!("@mozilla.org/test/startuprecorder;1" in Cc),
"the startup recorder component shouldn't exist in this non-nightly/non-devedition/" +
"non-debug build.");
return;
}
{
let omniJa = Services.dirsvc.get("XCurProcD", Ci.nsIFile);
omniJa.append("omni.ja");
if (!omniJa.exists()) {
ok(false, "This test requires a packaged build, " +
"run 'mach package' and then use --appname=dist");
return;
}
}
let startupRecorder = Cc["@mozilla.org/test/startuprecorder;1"].getService().wrappedJSObject;
await startupRecorder.done;
// Add system add-ons to the whitelist dynamically.
// They should go in the omni.ja file (bug 1357205).
{
let addons = await AddonManager.getAddonsByTypes(["extension"]);
for (let addon of addons) {
if (addon.isSystem) {
startupPhases["before opening first browser window"].push({
path: `XREAppFeat:${addon.id}.xpi`,
stat: 3,
close: 2,
});
startupPhases["before handling user events"].push({
path: `XREAppFeat:${addon.id}.xpi`,
condition: WIN,
stat: 2,
});
}
}
}
// Check for main thread I/O markers in the startup profile.
let profile = startupRecorder.data.profile.threads[0];
let phases = {};
{
const nameCol = profile.markers.schema.name;
const dataCol = profile.markers.schema.data;
let markersForCurrentPhase = [];
for (let m of profile.markers.data) {
let markerName = profile.stringTable[m[nameCol]];
if (markerName.startsWith("startupRecorder:")) {
phases[markerName.split("startupRecorder:")[1]] = markersForCurrentPhase;
markersForCurrentPhase = [];
continue;
}
if (markerName != "FileIO")
continue;
let markerData = m[dataCol];
if (markerData.source == "sqlite-mainthread") {
continue;
}
let samples = markerData.stack.samples;
let stack = samples.data[0][samples.schema.stack];
markersForCurrentPhase.push({operation: markerData.operation,
filename: markerData.filename,
source: markerData.source,
stackId: stack});
}
}
for (let phase in startupPhases) {
startupPhases[phase] =
startupPhases[phase].filter(entry => !("condition" in entry) || entry.condition);
startupPhases[phase].forEach(entry => {
entry.path = expandWhitelistPath(entry.path, entry.canonicalize);
});
}
let tmpPath = expandWhitelistPath(MAC ? "TmpD:" : "/dev/shm").toLowerCase();
let shouldPass = true;
for (let phase in phases) {
let whitelist = startupPhases[phase];
info(`whitelisted paths ${phase}:\n` +
whitelist.map(e => {
let operations = Object.keys(e).filter(k => k != "path")
.map(k => `${k}: ${e[k]}`);
return ` ${e.path} - ${operations.join(", ")}`;
}).join("\n"));
let markers = phases[phase];
for (let marker of markers) {
if (marker.operation == "create/open") {
// TODO: handle these I/O markers once they are supported on
// non-Windows platforms.
continue;
}
// Convert to lower case before comparing because the OS X test slaves
// have the 'Firefox' folder in 'Library/Application Support' created
// as 'firefox' for some reason.
let filename = marker.filename.toLowerCase();
if (!filename) {
// We are still missing the filename on some mainthreadio markers,
// these markers are currently useless for the purpose of this test.
continue;
}
if (!WIN) {
if (filename == "/dev/urandom") {
continue;
}
// Ignore I/O due to IPC. This doesn't really touch the disk.
if (filename.startsWith(tmpPath + "/org.chromium.")) {
continue;
}
}
let expected = false;
for (let entry of whitelist) {
if (pathMatches(entry.path, filename)) {
entry[marker.operation] = (entry[marker.operation] || 0) - 1;
entry._used = true;
expected = true;
break;
}
}
if (!expected) {
record(false,
`unexpected ${marker.operation} on ${marker.filename} ${phase}`,
undefined,
" " + getStackFromProfile(profile, marker.stackId).join("\n "));
shouldPass = false;
}
info(`(${marker.source}) ${marker.operation} - ${marker.filename}`);
if (kDumpAllStacks) {
info(getStackFromProfile(profile, marker.stackId).map(f => " " + f)
.join("\n"));
}
}
for (let entry of whitelist) {
for (let op in entry) {
if (["path", "condition", "canonicalize", "ignoreIfUnused", "_used"].includes(op)) {
continue;
}
let message = `${op} on ${entry.path} `;
if (entry[op] == 0) {
message += "as many times as expected";
} else if (entry[op] > 0) {
message += `allowed ${entry[op]} more times`;
} else {
message += `${entry[op] * -1} more times than expected`;
}
ok(entry[op] >= 0, `${message} ${phase}`);
}
if (!("_used" in entry) && !entry.ignoreIfUnused) {
ok(false, `unused whitelist entry ${phase}: ${entry.path}`);
}
}
}
if (shouldPass) {
ok(shouldPass, "No unexpected main thread I/O during startup");
} else {
const filename = "startup-mainthreadio-profile.json";
let path = Cc["@mozilla.org/process/environment;1"]
.getService(Ci.nsIEnvironment)
.get("MOZ_UPLOAD_DIR");
let encoder = new TextEncoder();
let profilePath = OS.Path.join(path, filename);
await OS.File.writeAtomic(profilePath,
encoder.encode(JSON.stringify(startupRecorder.data.profile)));
ok(false,
"Found some unexpected main thread I/O during startup; profile uploaded in " +
filename);
}
});

View File

@ -0,0 +1,20 @@
[DEFAULT]
# Currently disabled on debug due to debug-only failures, see bug 1549723.
skip-if = debug || (os == "linux" && asan) # bug 1549729
# to avoid overhead when running the browser normally, startupRecorder.js will
# do almost nothing unless browser.startup.record is true.
# gfx.canvas.willReadFrequently.enable is just an optimization, but needs to be
# set during early startup to have an impact as a canvas will be used by
# startupRecorder.js
prefs =
# Skip migration work in BG__migrateUI for browser_startup.js since it isn't
# representative of common startup, and triggers Places I/O.
browser.migration.version=9999999
browser.startup.record=true
gfx.canvas.willReadFrequently.enable=true
environment =
MOZ_PROFILER_STARTUP=1
MOZ_PROFILER_STARTUP_FEATURES=js,mainthreadio
MOZ_PROFILER_STARTUP_ENTRIES=10000000
[../browser_startup_mainthreadio.js]
[../browser_startup_content_mainthreadio.js]

View File

@ -34,6 +34,7 @@ BROWSER_CHROME_MANIFESTS += [
'content/test/pageinfo/browser.ini', 'content/test/pageinfo/browser.ini',
'content/test/performance/browser.ini', 'content/test/performance/browser.ini',
'content/test/performance/hidpi/browser.ini', 'content/test/performance/hidpi/browser.ini',
'content/test/performance/io/browser.ini',
'content/test/performance/legacyurlbar/browser.ini', 'content/test/performance/legacyurlbar/browser.ini',
'content/test/performance/lowdpi/browser.ini', 'content/test/performance/lowdpi/browser.ini',
'content/test/permissions/browser.ini', 'content/test/permissions/browser.ini',

View File

@ -24,7 +24,6 @@ body.config-warning {
.title { .title {
background-image: url("chrome://global/skin/icons/warning.svg"); background-image: url("chrome://global/skin/icons/warning.svg");
-moz-context-properties: fill;
fill: #fcd100; fill: #fcd100;
} }
@ -97,6 +96,8 @@ body.config-warning {
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: 9px center; background-position: 9px center;
background-size: 16px 16px; background-size: 16px 16px;
-moz-context-properties: fill;
fill: currentColor;
} }
#prefs > tr.locked:dir(rtl) { #prefs > tr.locked:dir(rtl) {

View File

@ -37,7 +37,11 @@ NOTE: The collection and messages can also be created manually using the [admin
```javascript ```javascript
Services.prefs.setStringPref("services.settings.server", "https://kinto.dev.mozaws.net/v1"); Services.prefs.setStringPref("services.settings.server", "https://kinto.dev.mozaws.net/v1");
Services.prefs.setBoolPref("services.settings.verify_signature", false);
// Disable signature verification
const { RemoteSettings } = ChromeUtils.import("resource://services-settings/remote-settings.js", {});
RemoteSettings("cfr").verifySignature = false;
``` ```
**3. Set ASRouter CFR pref to use Remote Settings provider and enable asrouter devtools.** **3. Set ASRouter CFR pref to use Remote Settings provider and enable asrouter devtools.**

View File

@ -1,17 +0,0 @@
{
"extensionName": {
"message": "Amazon.com.br"
},
"extensionDescription": {
"message": "Amazon.com.br Search"
},
"searchUrl": {
"message": "https://www.amazon.com.br/exec/obidos/external-search/"
},
"searchForm": {
"message": "https://www.amazon.com.br/exec/obidos/external-search/?field-keywords={searchTerms}&ie={inputEncoding}&mode=blended"
},
"searchUrlGetParams": {
"message": "field-keywords={searchTerms}&ie={inputEncoding}&mode=blended"
}
}

View File

@ -2,7 +2,7 @@
"name": "__MSG_extensionName__", "name": "__MSG_extensionName__",
"description": "__MSG_extensionDescription__", "description": "__MSG_extensionDescription__",
"manifest_version": 2, "manifest_version": 2,
"version": "1.0", "version": "1.1",
"applications": { "applications": {
"gecko": { "gecko": {
"id": "amazon@search.mozilla.org" "id": "amazon@search.mozilla.org"

View File

@ -2,7 +2,7 @@
"name": "__MSG_extensionName__", "name": "__MSG_extensionName__",
"description": "__MSG_extensionDescription__", "description": "__MSG_extensionDescription__",
"manifest_version": 2, "manifest_version": 2,
"version": "1.0", "version": "1.1",
"applications": { "applications": {
"gecko": { "gecko": {
"id": "amazondotcom@search.mozilla.org" "id": "amazondotcom@search.mozilla.org"

View File

@ -214,14 +214,14 @@
"de": { "de": {
"default": { "default": {
"visibleDefaultEngines": [ "visibleDefaultEngines": [
"google-b-d", "amazondotcom-de", "bing", "ddg", "ebay-de", "ecosia", "leo_ende_de", "wikipedia-de" "google-b-d", "amazon-de", "bing", "ddg", "ebay-de", "ecosia", "leo_ende_de", "wikipedia-de"
] ]
} }
}, },
"dsb": { "dsb": {
"default": { "default": {
"visibleDefaultEngines": [ "visibleDefaultEngines": [
"google-b-d", "bing", "amazondotcom-de", "ddg", "ebay-de", "leo_ende_de", "wikipedia-dsb" "google-b-d", "bing", "amazon-de", "ddg", "ebay-de", "leo_ende_de", "wikipedia-dsb"
] ]
} }
}, },
@ -396,7 +396,7 @@
"hsb": { "hsb": {
"default": { "default": {
"visibleDefaultEngines": [ "visibleDefaultEngines": [
"google-b-d", "bing", "amazondotcom-de", "ddg", "ebay-de", "leo_ende_de", "wikipedia-hsb" "google-b-d", "bing", "amazon-de", "ddg", "ebay-de", "leo_ende_de", "wikipedia-hsb"
] ]
} }
}, },

View File

@ -26,18 +26,21 @@ var gSetBackground = {
// regular screens seem to be 32:9 (3.56) according to Wikipedia. // regular screens seem to be 32:9 (3.56) according to Wikipedia.
let screenRatio = Math.min(this._screenWidth / this._screenHeight, 4); let screenRatio = Math.min(this._screenWidth / this._screenHeight, 4);
this._canvas.width = this._canvas.height * screenRatio; this._canvas.width = this._canvas.height * screenRatio;
document.getElementById("preview-unavailable").style.width =
this._canvas.width + "px";
if (AppConstants.platform == "macosx") { if (AppConstants.platform == "macosx") {
document.documentElement.getButton("accept").hidden = true; document.documentElement.getButton("accept").hidden = true;
} else { } else {
let multiMonitors = false; let multiMonitors = false;
try { if (AppConstants.platform == "linux") {
// getMonitors only ever returns the primary monitor on Linux, so just
// always show the option
multiMonitors = true;
} else {
const gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo); const gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
const monitors = gfxInfo.getMonitors(); const monitors = gfxInfo.getMonitors();
multiMonitors = monitors.length > 1; multiMonitors = monitors.length > 1;
} catch (e) {
// getMonitors() isn't implemented on Linux
multiMonitors = true;
} }
if (!multiMonitors || AppConstants.isPlatformAndVersionAtMost("win", 6.1)) { if (!multiMonitors || AppConstants.isPlatformAndVersionAtMost("win", 6.1)) {
@ -101,6 +104,7 @@ var gSetBackground = {
updatePosition() { updatePosition() {
var ctx = this._canvas.getContext("2d"); var ctx = this._canvas.getContext("2d");
ctx.clearRect(0, 0, this._screenWidth, this._screenHeight); ctx.clearRect(0, 0, this._screenWidth, this._screenHeight);
document.getElementById("preview-unavailable").hidden = true;
if (AppConstants.platform != "macosx") { if (AppConstants.platform != "macosx") {
this._position = document.getElementById("menuPosition").value; this._position = document.getElementById("menuPosition").value;
@ -157,18 +161,9 @@ var gSetBackground = {
break; break;
} }
case "SPAN": { case "SPAN": {
ctx.fillStyle = "black"; document.getElementById("preview-unavailable").hidden = false;
ctx.fillStyle = "#222";
ctx.fillRect(0, 0, this._screenWidth, this._screenHeight); ctx.fillRect(0, 0, this._screenWidth, this._screenHeight);
let x = this._screenWidth / 2;
let y = this._screenHeight / 2;
let radius = this._screenHeight * .4;
let delta = Math.sin(.25 * Math.PI) * radius; // opp = sin * hyp
ctx.lineWidth = radius / 4.5;
ctx.strokeStyle = "#9B2423";
ctx.arc(x, y, radius, -.75 * Math.PI, 1.25 * Math.PI);
ctx.stroke();
ctx.lineWidth *= .8;
ctx.lineTo(x + delta, y + delta);
ctx.stroke(); ctx.stroke();
} }
} }

View File

@ -64,7 +64,12 @@
<vbox align="center"> <vbox align="center">
<!-- default to 16:9, will be adjusted to match user's actual screen --> <!-- default to 16:9, will be adjusted to match user's actual screen -->
<stack>
<html:canvas id="screen" width="202" height="114" role="presentation"/> <html:canvas id="screen" width="202" height="114" role="presentation"/>
<vbox pack="center">
<html:p id="preview-unavailable" hidden="">&previewUnavailable;</html:p>
</vbox>
</stack>
<image id="monitor-base"/> <image id="monitor-base"/>
</vbox> </vbox>

View File

@ -63,6 +63,7 @@ startupRecorder.prototype = {
if (!Services.prefs.getBoolPref("browser.startup.record", false)) if (!Services.prefs.getBoolPref("browser.startup.record", false))
return; return;
Services.profiler.AddMarker("startupRecorder:" + name);
this.data.code[name] = { this.data.code[name] = {
components: Cu.loadedComponents, components: Cu.loadedComponents,
modules: Cu.loadedModules, modules: Cu.loadedModules,
@ -163,8 +164,24 @@ startupRecorder.prototype = {
Services.prefs.readStats((key, value) => this.data.prefStats[key] = value); Services.prefs.readStats((key, value) => this.data.prefStats[key] = value);
} }
paints = null; paints = null;
let env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
if (!env.exists("MOZ_PROFILER_STARTUP")) {
this._resolve(); this._resolve();
this._resolve = null; this._resolve = null;
return;
}
Services.profiler.getProfileDataAsync().then(profileData => {
this.data.profile = profileData;
// There's no equivalent StartProfiler call in this file because the
// profiler is started using the MOZ_PROFILER_STARTUP environment
// variable in browser/base/content/test/performance/browser.ini
Services.profiler.StopProfiler();
this._resolve();
this._resolve = null;
});
}); });
} else { } else {
const topicsToNames = { const topicsToNames = {

View File

@ -18,3 +18,4 @@
<!ENTITY setDesktopBackground.title "Set Desktop Background"> <!ENTITY setDesktopBackground.title "Set Desktop Background">
<!ENTITY openDesktopPrefs.label "Open Desktop Preferences"> <!ENTITY openDesktopPrefs.label "Open Desktop Preferences">
<!ENTITY closeWindow.key "w"> <!ENTITY closeWindow.key "w">
<!ENTITY previewUnavailable "Preview unavailable">

View File

@ -14,8 +14,6 @@ body {
.title { .title {
background-image: url("chrome://global/skin/icons/blocked.svg"); background-image: url("chrome://global/skin/icons/blocked.svg");
fill: currentColor;
-moz-context-properties: fill;
} }
.title-text { .title-text {

View File

@ -14,3 +14,10 @@ html|canvas#screen {
#monitor-base { #monitor-base {
list-style-image: url("chrome://browser/skin/monitor-base.png"); list-style-image: url("chrome://browser/skin/monitor-base.png");
} }
html|p#preview-unavailable {
margin: 12px 11px;
text-align: center;
color: #9B2423;
font-weight: bold;
}

View File

@ -11,6 +11,8 @@ const Services = require("Services");
const { l10n } = require("../modules/l10n"); const { l10n } = require("../modules/l10n");
const { isSupportedDebugTargetPane } = require("../modules/debug-target-support");
const { const {
openTemporaryExtension, openTemporaryExtension,
uninstallAddon, uninstallAddon,
@ -23,6 +25,7 @@ const {
const { const {
DEBUG_TARGETS, DEBUG_TARGETS,
DEBUG_TARGET_PANE,
REQUEST_EXTENSIONS_FAILURE, REQUEST_EXTENSIONS_FAILURE,
REQUEST_EXTENSIONS_START, REQUEST_EXTENSIONS_START,
REQUEST_EXTENSIONS_SUCCESS, REQUEST_EXTENSIONS_SUCCESS,
@ -162,10 +165,13 @@ function requestTabs() {
return async (dispatch, getState) => { return async (dispatch, getState) => {
dispatch({ type: REQUEST_TABS_START }); dispatch({ type: REQUEST_TABS_START });
const runtime = getCurrentRuntime(getState().runtimes);
const clientWrapper = getCurrentClient(getState().runtimes); const clientWrapper = getCurrentClient(getState().runtimes);
try { try {
const tabs = await clientWrapper.listTabs({ favicons: true }); const isSupported = isSupportedDebugTargetPane(runtime.runtimeDetails.info.type,
DEBUG_TARGET_PANE.TAB);
const tabs = isSupported ? (await clientWrapper.listTabs({ favicons: true })) : [];
dispatch({ type: REQUEST_TABS_SUCCESS, tabs }); dispatch({ type: REQUEST_TABS_SUCCESS, tabs });
} catch (e) { } catch (e) {

View File

@ -41,6 +41,24 @@ add_task(async function testThisFirefoxWithoutLocalTab() {
await removeTab(tab); await removeTab(tab);
}); });
/**
* Check that the tab which is discarded keeps the state after open the aboutdebugging.
*/
add_task(async function testThisFirefoxKeepDiscardedTab() {
const targetTab = await addTab("https://example.com/");
const blankTab = await addTab("about:blank");
targetTab.ownerGlobal.gBrowser.discardBrowser(targetTab);
const { document, tab, window } = await openAboutDebugging({ enableLocalTabs: false });
await selectThisFirefoxPage(document, window.AboutDebugging.store);
ok(!targetTab.linkedPanel, "The target tab is still discarded");
await removeTab(blankTab);
await removeTab(targetTab);
await removeTab(tab);
});
/** /**
* Check that the Temporary Extensions is hidden if "xpinstall.enabled" is set to false. * Check that the Temporary Extensions is hidden if "xpinstall.enabled" is set to false.
*/ */
@ -81,4 +99,3 @@ async function checkThisFirefoxTargetPanes(doc, expectedTargetPanes) {
`Expected debug target category found: ${ expectedPaneTitle }`); `Expected debug target category found: ${ expectedPaneTitle }`);
} }
} }

View File

@ -267,17 +267,20 @@ void ShadowRoot::RemoveSlot(HTMLSlotElement* aSlot) {
const bool wasFirstSlot = currentSlots->ElementAt(0) == aSlot; const bool wasFirstSlot = currentSlots->ElementAt(0) == aSlot;
currentSlots.RemoveElement(*aSlot); currentSlots.RemoveElement(*aSlot);
// Move assigned nodes from removed slot to the next slot in
// tree order with the same name.
if (!wasFirstSlot) { if (!wasFirstSlot) {
return; return;
} }
// Move assigned nodes from removed slot to the next slot in
// tree order with the same name.
InvalidateStyleAndLayoutOnSubtree(aSlot); InvalidateStyleAndLayoutOnSubtree(aSlot);
HTMLSlotElement* replacementSlot = currentSlots->ElementAt(0); HTMLSlotElement* replacementSlot = currentSlots->ElementAt(0);
const nsTArray<RefPtr<nsINode>>& assignedNodes = aSlot->AssignedNodes(); const nsTArray<RefPtr<nsINode>>& assignedNodes = aSlot->AssignedNodes();
bool slottedNodesChanged = !assignedNodes.IsEmpty(); if (assignedNodes.IsEmpty()) {
return;
}
InvalidateStyleAndLayoutOnSubtree(replacementSlot);
while (!assignedNodes.IsEmpty()) { while (!assignedNodes.IsEmpty()) {
nsINode* assignedNode = assignedNodes[0]; nsINode* assignedNode = assignedNodes[0];
@ -285,11 +288,9 @@ void ShadowRoot::RemoveSlot(HTMLSlotElement* aSlot) {
replacementSlot->AppendAssignedNode(assignedNode); replacementSlot->AppendAssignedNode(assignedNode);
} }
if (slottedNodesChanged) {
aSlot->EnqueueSlotChangeEvent(); aSlot->EnqueueSlotChangeEvent();
replacementSlot->EnqueueSlotChangeEvent(); replacementSlot->EnqueueSlotChangeEvent();
} }
}
// FIXME(emilio): There's a bit of code duplication between this and the // FIXME(emilio): There's a bit of code duplication between this and the
// equivalent ServoStyleSet methods, it'd be nice to not duplicate it... // equivalent ServoStyleSet methods, it'd be nice to not duplicate it...

View File

@ -62,13 +62,13 @@ class nsDocumentEncoder : public nsIDocumentEncoder {
virtual ~nsDocumentEncoder(); virtual ~nsDocumentEncoder();
void Initialize(bool aClearCachedSerializer = true); void Initialize(bool aClearCachedSerializer = true);
nsresult SerializeNodeStart(nsINode* aNode, int32_t aStartOffset, nsresult SerializeNodeStart(nsINode& aOriginalNode, int32_t aStartOffset,
int32_t aEndOffset, nsAString& aStr, int32_t aEndOffset, nsAString& aStr,
nsINode* aOriginalNode = nullptr); nsINode* aFixupNode = nullptr);
nsresult SerializeToStringRecursive(nsINode* aNode, nsAString& aStr, nsresult SerializeToStringRecursive(nsINode* aNode, nsAString& aStr,
bool aDontSerializeRoot, bool aDontSerializeRoot,
uint32_t aMaxLength = 0); uint32_t aMaxLength = 0);
nsresult SerializeNodeEnd(nsINode* aNode, nsAString& aStr); nsresult SerializeNodeEnd(nsINode& aNode, nsAString& aStr);
// This serializes the content of aNode. // This serializes the content of aNode.
nsresult SerializeToStringIterative(nsINode* aNode, nsAString& aStr); nsresult SerializeToStringIterative(nsINode* aNode, nsAString& aStr);
nsresult SerializeRangeToString(nsRange* aRange, nsAString& aOutputString); nsresult SerializeRangeToString(nsRange* aRange, nsAString& aOutputString);
@ -296,34 +296,59 @@ nsDocumentEncoder::GetMimeType(nsAString& aMimeType) {
bool nsDocumentEncoder::IncludeInContext(nsINode* aNode) { return false; } bool nsDocumentEncoder::IncludeInContext(nsINode* aNode) { return false; }
nsresult nsDocumentEncoder::SerializeNodeStart(nsINode* aNode, class FixupNodeDeterminer {
public:
FixupNodeDeterminer(nsIDocumentEncoderNodeFixup* aNodeFixup,
nsINode* aFixupNode, nsINode& aOriginalNode)
: mIsSerializationOfFixupChildrenNeeded{false},
mNodeFixup(aNodeFixup),
mOriginalNode(aOriginalNode) {
if (mNodeFixup) {
if (aFixupNode) {
mFixupNode = aFixupNode;
} else {
mNodeFixup->FixupNode(&mOriginalNode,
&mIsSerializationOfFixupChildrenNeeded,
getter_AddRefs(mFixupNode));
}
}
}
bool IsSerializationOfFixupChildrenNeeded() const {
return mIsSerializationOfFixupChildrenNeeded;
}
/**
* @return The fixup node, if available, otherwise the original node. The
* former is kept alive by this object.
*/
nsINode& GetFixupNodeFallBackToOriginalNode() const {
return mFixupNode ? *mFixupNode : mOriginalNode;
}
private:
bool mIsSerializationOfFixupChildrenNeeded;
nsIDocumentEncoderNodeFixup* mNodeFixup;
nsCOMPtr<nsINode> mFixupNode;
nsINode& mOriginalNode;
};
nsresult nsDocumentEncoder::SerializeNodeStart(nsINode& aOriginalNode,
int32_t aStartOffset, int32_t aStartOffset,
int32_t aEndOffset, int32_t aEndOffset,
nsAString& aStr, nsAString& aStr,
nsINode* aOriginalNode) { nsINode* aFixupNode) {
if (mNeedsPreformatScanning && aNode->IsElement()) { if (mNeedsPreformatScanning && aOriginalNode.IsElement()) {
mSerializer->ScanElementForPreformat(aNode->AsElement()); mSerializer->ScanElementForPreformat(aOriginalNode.AsElement());
} }
if (!IsVisibleNode(aNode)) return NS_OK; if (!IsVisibleNode(&aOriginalNode)) {
return NS_OK;
nsINode* node = nullptr;
nsCOMPtr<nsINode> fixedNodeKungfuDeathGrip;
// Caller didn't do fixup, so we'll do it ourselves
if (!aOriginalNode) {
aOriginalNode = aNode;
if (mNodeFixup) {
bool dummy;
mNodeFixup->FixupNode(aNode, &dummy,
getter_AddRefs(fixedNodeKungfuDeathGrip));
node = fixedNodeKungfuDeathGrip;
}
} }
// Either there was no fixed-up node, FixupNodeDeterminer fixupNodeDeterminer{mNodeFixup, aFixupNode,
// or the caller did fixup themselves and aNode is already fixed aOriginalNode};
if (!node) node = aNode; nsINode* node = &fixupNodeDeterminer.GetFixupNodeFallBackToOriginalNode();
if (node->IsElement()) { if (node->IsElement()) {
if ((mFlags & (nsIDocumentEncoder::OutputPreformatted | if ((mFlags & (nsIDocumentEncoder::OutputPreformatted |
@ -331,7 +356,7 @@ nsresult nsDocumentEncoder::SerializeNodeStart(nsINode* aNode,
nsLayoutUtils::IsInvisibleBreak(node)) { nsLayoutUtils::IsInvisibleBreak(node)) {
return NS_OK; return NS_OK;
} }
Element* originalElement = Element::FromNodeOrNull(aOriginalNode); Element* originalElement = aOriginalNode.AsElement();
mSerializer->AppendElementStart(node->AsElement(), originalElement, aStr); mSerializer->AppendElementStart(node->AsElement(), originalElement, aStr);
return NS_OK; return NS_OK;
} }
@ -367,15 +392,17 @@ nsresult nsDocumentEncoder::SerializeNodeStart(nsINode* aNode,
return NS_OK; return NS_OK;
} }
nsresult nsDocumentEncoder::SerializeNodeEnd(nsINode* aNode, nsAString& aStr) { nsresult nsDocumentEncoder::SerializeNodeEnd(nsINode& aNode, nsAString& aStr) {
if (mNeedsPreformatScanning && aNode->IsElement()) { if (mNeedsPreformatScanning && aNode.IsElement()) {
mSerializer->ForgetElementForPreformat(aNode->AsElement()); mSerializer->ForgetElementForPreformat(aNode.AsElement());
} }
if (!IsVisibleNode(aNode)) return NS_OK; if (!IsVisibleNode(&aNode)) {
return NS_OK;
}
if (aNode->IsElement()) { if (aNode.IsElement()) {
mSerializer->AppendElementEnd(aNode->AsElement(), aStr); mSerializer->AppendElementEnd(aNode.AsElement(), aStr);
} }
return NS_OK; return NS_OK;
} }
@ -391,18 +418,11 @@ nsresult nsDocumentEncoder::SerializeToStringRecursive(nsINode* aNode,
if (!IsVisibleNode(aNode)) return NS_OK; if (!IsVisibleNode(aNode)) return NS_OK;
nsresult rv = NS_OK; nsresult rv = NS_OK;
bool serializeClonedChildren = false;
nsINode* maybeFixedNode = nullptr;
// Keep the node from FixupNode alive. MOZ_ASSERT(aNode, "aNode shouldn't be nullptr.");
nsCOMPtr<nsINode> fixedNodeKungfuDeathGrip; FixupNodeDeterminer fixupNodeDeterminer{mNodeFixup, nullptr, *aNode};
if (mNodeFixup) { nsINode* maybeFixedNode =
mNodeFixup->FixupNode(aNode, &serializeClonedChildren, &fixupNodeDeterminer.GetFixupNodeFallBackToOriginalNode();
getter_AddRefs(fixedNodeKungfuDeathGrip));
maybeFixedNode = fixedNodeKungfuDeathGrip;
}
if (!maybeFixedNode) maybeFixedNode = aNode;
if ((mFlags & SkipInvisibleContent) && if ((mFlags & SkipInvisibleContent) &&
!(mFlags & OutputNonTextContentAsPlaceholder)) { !(mFlags & OutputNonTextContentAsPlaceholder)) {
@ -421,11 +441,13 @@ nsresult nsDocumentEncoder::SerializeToStringRecursive(nsINode* aNode,
MOZ_ASSERT(aMaxLength >= aStr.Length()); MOZ_ASSERT(aMaxLength >= aStr.Length());
endOffset = aMaxLength - aStr.Length(); endOffset = aMaxLength - aStr.Length();
} }
rv = SerializeNodeStart(maybeFixedNode, 0, endOffset, aStr, aNode); rv = SerializeNodeStart(*aNode, 0, endOffset, aStr, maybeFixedNode);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
} }
nsINode* node = serializeClonedChildren ? maybeFixedNode : aNode; nsINode* node = fixupNodeDeterminer.IsSerializationOfFixupChildrenNeeded()
? maybeFixedNode
: aNode;
for (nsINode* child = nsNodeUtils::GetFirstChildOfTemplateOrNode(node); child; for (nsINode* child = nsNodeUtils::GetFirstChildOfTemplateOrNode(node); child;
child = child->GetNextSibling()) { child = child->GetNextSibling()) {
@ -434,7 +456,7 @@ nsresult nsDocumentEncoder::SerializeToStringRecursive(nsINode* aNode,
} }
if (!aDontSerializeRoot) { if (!aDontSerializeRoot) {
rv = SerializeNodeEnd(maybeFixedNode, aStr); rv = SerializeNodeEnd(*maybeFixedNode, aStr);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
} }
@ -448,11 +470,11 @@ nsresult nsDocumentEncoder::SerializeToStringIterative(nsINode* aNode,
nsINode* node = nsNodeUtils::GetFirstChildOfTemplateOrNode(aNode); nsINode* node = nsNodeUtils::GetFirstChildOfTemplateOrNode(aNode);
while (node) { while (node) {
nsINode* current = node; nsINode* current = node;
rv = SerializeNodeStart(current, 0, -1, aStr, current); rv = SerializeNodeStart(*current, 0, -1, aStr, current);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
node = nsNodeUtils::GetFirstChildOfTemplateOrNode(current); node = nsNodeUtils::GetFirstChildOfTemplateOrNode(current);
while (!node && current && current != aNode) { while (!node && current && current != aNode) {
rv = SerializeNodeEnd(current, aStr); rv = SerializeNodeEnd(*current, aStr);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
// Check if we have siblings. // Check if we have siblings.
node = current->GetNextSibling(); node = current->GetNextSibling();
@ -577,14 +599,14 @@ nsresult nsDocumentEncoder::SerializeRangeNodes(nsRange* const aRange,
if (IsTextNode(aNode)) { if (IsTextNode(aNode)) {
if (startNode == content) { if (startNode == content) {
int32_t startOffset = aRange->StartOffset(); int32_t startOffset = aRange->StartOffset();
rv = SerializeNodeStart(aNode, startOffset, -1, aString); rv = SerializeNodeStart(*aNode, startOffset, -1, aString);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
} else { } else {
int32_t endOffset = aRange->EndOffset(); int32_t endOffset = aRange->EndOffset();
rv = SerializeNodeStart(aNode, 0, endOffset, aString); rv = SerializeNodeStart(*aNode, 0, endOffset, aString);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
} }
rv = SerializeNodeEnd(aNode, aString); rv = SerializeNodeEnd(*aNode, aString);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
} else { } else {
if (aNode != mCommonParent) { if (aNode != mCommonParent) {
@ -597,7 +619,7 @@ nsresult nsDocumentEncoder::SerializeRangeNodes(nsRange* const aRange,
if ((endNode == content) && !mHaltRangeHint) mEndDepth++; if ((endNode == content) && !mHaltRangeHint) mEndDepth++;
// serialize the start of this node // serialize the start of this node
rv = SerializeNodeStart(aNode, 0, -1, aString); rv = SerializeNodeStart(*aNode, 0, -1, aString);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
} }
@ -653,7 +675,7 @@ nsresult nsDocumentEncoder::SerializeRangeNodes(nsRange* const aRange,
// serialize the end of this node // serialize the end of this node
if (aNode != mCommonParent) { if (aNode != mCommonParent) {
rv = SerializeNodeEnd(aNode, aString); rv = SerializeNodeEnd(*aNode, aString);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
} }
} }
@ -682,7 +704,7 @@ nsresult nsDocumentEncoder::SerializeRangeContextStart(
// Either a general inclusion or as immediate context // Either a general inclusion or as immediate context
if (IncludeInContext(node) || i < j) { if (IncludeInContext(node) || i < j) {
rv = SerializeNodeStart(node, 0, -1, aString); rv = SerializeNodeStart(*node, 0, -1, aString);
serializedContext->AppendElement(node); serializedContext->AppendElement(node);
if (NS_FAILED(rv)) break; if (NS_FAILED(rv)) break;
} }
@ -702,7 +724,7 @@ nsresult nsDocumentEncoder::SerializeRangeContextEnd(nsAString& aString) {
nsresult rv = NS_OK; nsresult rv = NS_OK;
for (nsINode* node : Reversed(serializedContext)) { for (nsINode* node : Reversed(serializedContext)) {
rv = SerializeNodeEnd(node, aString); rv = SerializeNodeEnd(*node, aString);
if (NS_FAILED(rv)) break; if (NS_FAILED(rv)) break;
} }
@ -759,10 +781,10 @@ nsresult nsDocumentEncoder::SerializeRangeToString(nsRange* aRange,
if (!parent || !IsVisibleNode(parent)) return NS_OK; if (!parent || !IsVisibleNode(parent)) return NS_OK;
} }
} }
rv = SerializeNodeStart(startContainer, startOffset, endOffset, rv = SerializeNodeStart(*startContainer, startOffset, endOffset,
aOutputString); aOutputString);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
rv = SerializeNodeEnd(startContainer, aOutputString); rv = SerializeNodeEnd(*startContainer, aOutputString);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
} else { } else {
rv = SerializeRangeNodes(aRange, mCommonParent, aOutputString, 0); rv = SerializeRangeNodes(aRange, mCommonParent, aOutputString, 0);
@ -861,7 +883,7 @@ nsDocumentEncoder::EncodeToStringWithMaxLength(uint32_t aMaxLength,
NS_ENSURE_TRUE(node, NS_ERROR_FAILURE); NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
if (node != prevNode) { if (node != prevNode) {
if (prevNode) { if (prevNode) {
rv = SerializeNodeEnd(prevNode, output); rv = SerializeNodeEnd(*prevNode, output);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
} }
nsCOMPtr<nsIContent> content = do_QueryInterface(node); nsCOMPtr<nsIContent> content = do_QueryInterface(node);
@ -878,7 +900,7 @@ nsDocumentEncoder::EncodeToStringWithMaxLength(uint32_t aMaxLength,
mDisableContextSerialize = true; mDisableContextSerialize = true;
} }
rv = SerializeNodeStart(node, 0, -1, output); rv = SerializeNodeStart(*node, 0, -1, output);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
prevNode = node; prevNode = node;
} else if (prevNode) { } else if (prevNode) {
@ -899,7 +921,7 @@ nsDocumentEncoder::EncodeToStringWithMaxLength(uint32_t aMaxLength,
mStartDepth = firstRangeStartDepth; mStartDepth = firstRangeStartDepth;
if (prevNode) { if (prevNode) {
rv = SerializeNodeEnd(prevNode, output); rv = SerializeNodeEnd(*prevNode, output);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
mDisableContextSerialize = false; mDisableContextSerialize = false;
rv = SerializeRangeContextEnd(output); rv = SerializeRangeContextEnd(output);
@ -1266,12 +1288,12 @@ nsHTMLCopyEncoder::EncodeToStringWithContext(nsAString& aContextString,
i = count; i = count;
while (i > 0) { while (i > 0) {
node = mCommonAncestors.ElementAt(--i); node = mCommonAncestors.ElementAt(--i);
SerializeNodeStart(node, 0, -1, aContextString); SerializeNodeStart(*node, 0, -1, aContextString);
} }
// i = 0; guaranteed by above // i = 0; guaranteed by above
while (i < count) { while (i < count) {
node = mCommonAncestors.ElementAt(i++); node = mCommonAncestors.ElementAt(i++);
SerializeNodeEnd(node, aContextString); SerializeNodeEnd(*node, aContextString);
} }
// encode range info : the start and end depth of the selection, where the // encode range info : the start and end depth of the selection, where the

View File

@ -96,13 +96,8 @@ function check_mp4(v, enabled) {
check(codec, "probably"); check(codec, "probably");
ok(MediaSource.isTypeSupported(codec), "VP9 in MP4 should be supported in MSE"); ok(MediaSource.isTypeSupported(codec), "VP9 in MP4 should be supported in MSE");
}); });
if (IsAndroid()) {
check("video/mp4; codecs=\"av1\"", "");
} else {
check("video/mp4; codecs=\"av1\"", "probably"); check("video/mp4; codecs=\"av1\"", "probably");
} }
}
function check_mp3(v, enabled) { function check_mp3(v, enabled) {
function check(type, expected) { function check(type, expected) {
@ -143,10 +138,6 @@ function getPref(name) {
return pref; return pref;
} }
function IsAndroid() {
return getAndroidVersion() >= 0;
}
function IsSupportedAndroid() { function IsSupportedAndroid() {
return getAndroidVersion() >= 14; return getAndroidVersion() >= 14;
} }

View File

@ -123,30 +123,6 @@ using namespace widget;
* mozilla::EditorBase * mozilla::EditorBase
*****************************************************************************/ *****************************************************************************/
template already_AddRefed<Element> EditorBase::CreateNodeWithTransaction(
nsAtom& aTag, const EditorDOMPoint& aPointToInsert);
template already_AddRefed<Element> EditorBase::CreateNodeWithTransaction(
nsAtom& aTag, const EditorRawDOMPoint& aPointToInsert);
template nsresult EditorBase::InsertNodeWithTransaction(
nsIContent& aContentToInsert, const EditorDOMPoint& aPointToInsert);
template nsresult EditorBase::InsertNodeWithTransaction(
nsIContent& aContentToInsert, const EditorRawDOMPoint& aPointToInsert);
template already_AddRefed<nsIContent> EditorBase::SplitNodeWithTransaction(
const EditorDOMPoint& aStartOfRightNode, ErrorResult& aError);
template already_AddRefed<nsIContent> EditorBase::SplitNodeWithTransaction(
const EditorRawDOMPoint& aStartOfRightNode, ErrorResult& aError);
template SplitNodeResult EditorBase::SplitNodeDeepWithTransaction(
nsIContent& aMostAncestorToSplit,
const EditorDOMPoint& aStartOfDeepestRightNode, SplitAtEdges aSplitAtEdges);
template SplitNodeResult EditorBase::SplitNodeDeepWithTransaction(
nsIContent& aMostAncestorToSplit,
const EditorRawDOMPoint& aStartOfDeepestRightNode,
SplitAtEdges aSplitAtEdges);
template nsresult EditorBase::MoveNodeWithTransaction(
nsIContent& aContent, const EditorDOMPoint& aPointToInsert);
template nsresult EditorBase::MoveNodeWithTransaction(
nsIContent& aContent, const EditorRawDOMPoint& aPointToInsert);
EditorBase::EditorBase() EditorBase::EditorBase()
: mEditActionData(nullptr), : mEditActionData(nullptr),
mPlaceholderName(nullptr), mPlaceholderName(nullptr),
@ -1342,9 +1318,8 @@ EditorBase::SetSpellcheckUserOverride(bool enable) {
return NS_OK; return NS_OK;
} }
template <typename PT, typename CT>
already_AddRefed<Element> EditorBase::CreateNodeWithTransaction( already_AddRefed<Element> EditorBase::CreateNodeWithTransaction(
nsAtom& aTagName, const EditorDOMPointBase<PT, CT>& aPointToInsert) { nsAtom& aTagName, const EditorDOMPoint& aPointToInsert) {
MOZ_ASSERT(IsEditActionDataAvailable()); MOZ_ASSERT(IsEditActionDataAvailable());
MOZ_ASSERT(aPointToInsert.IsSetAndValid()); MOZ_ASSERT(aPointToInsert.IsSetAndValid());
@ -1414,18 +1389,16 @@ EditorBase::InsertNode(nsINode* aNodeToInsert, nsINode* aContainer,
aOffset < 0 aOffset < 0
? static_cast<int32_t>(aContainer->Length()) ? static_cast<int32_t>(aContainer->Length())
: std::min(aOffset, static_cast<int32_t>(aContainer->Length())); : std::min(aOffset, static_cast<int32_t>(aContainer->Length()));
nsresult rv = InsertNodeWithTransaction( nsresult rv = InsertNodeWithTransaction(*contentToInsert,
*contentToInsert, EditorRawDOMPoint(aContainer, offset)); EditorDOMPoint(aContainer, offset));
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
return EditorBase::ToGenericNSResult(rv); return EditorBase::ToGenericNSResult(rv);
} }
return NS_OK; return NS_OK;
} }
template <typename PT, typename CT>
nsresult EditorBase::InsertNodeWithTransaction( nsresult EditorBase::InsertNodeWithTransaction(
nsIContent& aContentToInsert, nsIContent& aContentToInsert, const EditorDOMPoint& aPointToInsert) {
const EditorDOMPointBase<PT, CT>& aPointToInsert) {
MOZ_ASSERT(IsEditActionDataAvailable()); MOZ_ASSERT(IsEditActionDataAvailable());
if (NS_WARN_IF(!aPointToInsert.IsSet())) { if (NS_WARN_IF(!aPointToInsert.IsSet())) {
@ -1472,7 +1445,7 @@ EditorBase::SplitNode(nsINode* aNode, int32_t aOffset, nsINode** aNewLeftNode) {
std::min(std::max(aOffset, 0), static_cast<int32_t>(aNode->Length())); std::min(std::max(aOffset, 0), static_cast<int32_t>(aNode->Length()));
ErrorResult error; ErrorResult error;
nsCOMPtr<nsIContent> newNode = nsCOMPtr<nsIContent> newNode =
SplitNodeWithTransaction(EditorRawDOMPoint(aNode, offset), error); SplitNodeWithTransaction(EditorDOMPoint(aNode, offset), error);
newNode.forget(aNewLeftNode); newNode.forget(aNewLeftNode);
if (NS_WARN_IF(error.Failed())) { if (NS_WARN_IF(error.Failed())) {
return EditorBase::ToGenericNSResult(error.StealNSResult()); return EditorBase::ToGenericNSResult(error.StealNSResult());
@ -1480,9 +1453,8 @@ EditorBase::SplitNode(nsINode* aNode, int32_t aOffset, nsINode** aNewLeftNode) {
return NS_OK; return NS_OK;
} }
template <typename PT, typename CT>
already_AddRefed<nsIContent> EditorBase::SplitNodeWithTransaction( already_AddRefed<nsIContent> EditorBase::SplitNodeWithTransaction(
const EditorDOMPointBase<PT, CT>& aStartOfRightNode, ErrorResult& aError) { const EditorDOMPoint& aStartOfRightNode, ErrorResult& aError) {
MOZ_ASSERT(IsEditActionDataAvailable()); MOZ_ASSERT(IsEditActionDataAvailable());
if (NS_WARN_IF(!aStartOfRightNode.IsSet()) || if (NS_WARN_IF(!aStartOfRightNode.IsSet()) ||
@ -1712,7 +1684,7 @@ already_AddRefed<Element> EditorBase::ReplaceContainerWithTransactionInternal(
} }
rv = InsertNodeWithTransaction( rv = InsertNodeWithTransaction(
*child, EditorRawDOMPoint(newContainer, newContainer->Length())); *child, EditorDOMPoint(newContainer, newContainer->Length()));
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr; return nullptr;
} }
@ -1764,7 +1736,7 @@ nsresult EditorBase::RemoveContainerWithTransaction(Element& aElement) {
// use offset here because previous child might have been moved to // use offset here because previous child might have been moved to
// container. // container.
rv = InsertNodeWithTransaction( rv = InsertNodeWithTransaction(
*child, EditorRawDOMPoint(pointToInsertChildren.GetContainer(), *child, EditorDOMPoint(pointToInsertChildren.GetContainer(),
pointToInsertChildren.Offset())); pointToInsertChildren.Offset()));
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
return rv; return rv;
@ -1820,8 +1792,7 @@ already_AddRefed<Element> EditorBase::InsertContainerWithTransactionInternal(
{ {
AutoTransactionsConserveSelection conserveSelection(*this); AutoTransactionsConserveSelection conserveSelection(*this);
rv = rv = InsertNodeWithTransaction(aContent, EditorDOMPoint(newContainer, 0));
InsertNodeWithTransaction(aContent, EditorRawDOMPoint(newContainer, 0));
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr; return nullptr;
} }
@ -1836,9 +1807,8 @@ already_AddRefed<Element> EditorBase::InsertContainerWithTransactionInternal(
return newContainer.forget(); return newContainer.forget();
} }
template <typename PT, typename CT>
nsresult EditorBase::MoveNodeWithTransaction( nsresult EditorBase::MoveNodeWithTransaction(
nsIContent& aContent, const EditorDOMPointBase<PT, CT>& aPointToInsert) { nsIContent& aContent, const EditorDOMPoint& aPointToInsert) {
MOZ_ASSERT(aPointToInsert.IsSetAndValid()); MOZ_ASSERT(aPointToInsert.IsSetAndValid());
EditorDOMPoint oldPoint(&aContent); EditorDOMPoint oldPoint(&aContent);
@ -1852,8 +1822,7 @@ nsresult EditorBase::MoveNodeWithTransaction(
} }
// Notify our internal selection state listener // Notify our internal selection state listener
EditorDOMPoint newPoint(aPointToInsert); AutoMoveNodeSelNotify selNotify(RangeUpdaterRef(), oldPoint, aPointToInsert);
AutoMoveNodeSelNotify selNotify(RangeUpdaterRef(), oldPoint, newPoint);
// Hold a reference so aNode doesn't go away when we remove it (bug 772282) // Hold a reference so aNode doesn't go away when we remove it (bug 772282)
nsresult rv = DeleteNodeWithTransaction(aContent); nsresult rv = DeleteNodeWithTransaction(aContent);
@ -1862,7 +1831,7 @@ nsresult EditorBase::MoveNodeWithTransaction(
} }
// Mutation event listener could break insertion point. Let's check it. // Mutation event listener could break insertion point. Let's check it.
EditorRawDOMPoint pointToInsert(selNotify.ComputeInsertionPoint()); EditorDOMPoint pointToInsert(selNotify.ComputeInsertionPoint());
if (NS_WARN_IF(!pointToInsert.IsSet())) { if (NS_WARN_IF(!pointToInsert.IsSet())) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
@ -3728,10 +3697,9 @@ bool EditorBase::IsPreformatted(nsINode* aNode) {
return styleText->WhiteSpaceIsSignificant(); return styleText->WhiteSpaceIsSignificant();
} }
template <typename PT, typename CT>
SplitNodeResult EditorBase::SplitNodeDeepWithTransaction( SplitNodeResult EditorBase::SplitNodeDeepWithTransaction(
nsIContent& aMostAncestorToSplit, nsIContent& aMostAncestorToSplit,
const EditorDOMPointBase<PT, CT>& aStartOfDeepestRightNode, const EditorDOMPoint& aStartOfDeepestRightNode,
SplitAtEdges aSplitAtEdges) { SplitAtEdges aSplitAtEdges) {
MOZ_ASSERT(aStartOfDeepestRightNode.IsSetAndValid()); MOZ_ASSERT(aStartOfDeepestRightNode.IsSetAndValid());
MOZ_ASSERT( MOZ_ASSERT(

View File

@ -936,10 +936,8 @@ class EditorBase : public nsIEditor,
* container. Otherwise, will insert the node * container. Otherwise, will insert the node
* before child node referred by this. * before child node referred by this.
*/ */
template <typename PT, typename CT> MOZ_CAN_RUN_SCRIPT nsresult InsertNodeWithTransaction(
MOZ_CAN_RUN_SCRIPT nsresult nsIContent& aContentToInsert, const EditorDOMPoint& aPointToInsert);
InsertNodeWithTransaction(nsIContent& aContentToInsert,
const EditorDOMPointBase<PT, CT>& aPointToInsert);
/** /**
* ReplaceContainerWithTransaction() creates new element whose name is * ReplaceContainerWithTransaction() creates new element whose name is
@ -1071,10 +1069,8 @@ class EditorBase : public nsIEditor,
* @param aError If succeed, returns no error. Otherwise, an * @param aError If succeed, returns no error. Otherwise, an
* error. * error.
*/ */
template <typename PT, typename CT>
MOZ_CAN_RUN_SCRIPT already_AddRefed<nsIContent> SplitNodeWithTransaction( MOZ_CAN_RUN_SCRIPT already_AddRefed<nsIContent> SplitNodeWithTransaction(
const EditorDOMPointBase<PT, CT>& aStartOfRightNode, const EditorDOMPoint& aStartOfRightNode, ErrorResult& aResult);
ErrorResult& aResult);
/** /**
* JoinNodesWithTransaction() joins aLeftNode and aRightNode. Content of * JoinNodesWithTransaction() joins aLeftNode and aRightNode. Content of
@ -1093,9 +1089,8 @@ class EditorBase : public nsIEditor,
* *
* @param aContent The node to be moved. * @param aContent The node to be moved.
*/ */
template <typename PT, typename CT>
MOZ_CAN_RUN_SCRIPT nsresult MoveNodeWithTransaction( MOZ_CAN_RUN_SCRIPT nsresult MoveNodeWithTransaction(
nsIContent& aContent, const EditorDOMPointBase<PT, CT>& aPointToInsert); nsIContent& aContent, const EditorDOMPoint& aPointToInsert);
/** /**
* MoveNodeToEndWithTransaction() moves aContent to end of aNewContainer. * MoveNodeToEndWithTransaction() moves aContent to end of aNewContainer.
@ -1107,7 +1102,7 @@ class EditorBase : public nsIEditor,
MOZ_CAN_RUN_SCRIPT MOZ_CAN_RUN_SCRIPT
nsresult MoveNodeToEndWithTransaction(nsIContent& aContent, nsresult MoveNodeToEndWithTransaction(nsIContent& aContent,
nsINode& aNewContainer) { nsINode& aNewContainer) {
EditorRawDOMPoint pointToInsert; EditorDOMPoint pointToInsert;
pointToInsert.SetToEndOf(&aNewContainer); pointToInsert.SetToEndOf(&aNewContainer);
return MoveNodeWithTransaction(aContent, pointToInsert); return MoveNodeWithTransaction(aContent, pointToInsert);
} }
@ -1243,9 +1238,8 @@ class EditorBase : public nsIEditor,
* child node referred by this. * child node referred by this.
* @return The created new element node. * @return The created new element node.
*/ */
template <typename PT, typename CT> MOZ_CAN_RUN_SCRIPT already_AddRefed<Element> CreateNodeWithTransaction(
already_AddRefed<Element> CreateNodeWithTransaction( nsAtom& aTag, const EditorDOMPoint& aPointToInsert);
nsAtom& aTag, const EditorDOMPointBase<PT, CT>& aPointToInsert);
/** /**
* Create an aggregate transaction for delete selection. The result may * Create an aggregate transaction for delete selection. The result may
@ -1287,6 +1281,7 @@ class EditorBase : public nsIEditor,
* @param aOffset Start offset of removing text in aCharData. * @param aOffset Start offset of removing text in aCharData.
* @param aLength Length of removing text. * @param aLength Length of removing text.
*/ */
MOZ_CAN_RUN_SCRIPT
nsresult DeleteTextWithTransaction(dom::CharacterData& aCharacterData, nsresult DeleteTextWithTransaction(dom::CharacterData& aCharacterData,
uint32_t aOffset, uint32_t aLength); uint32_t aOffset, uint32_t aLength);
@ -1389,10 +1384,9 @@ class EditorBase : public nsIEditor,
* be good to insert something if the * be good to insert something if the
* caller want to do it. * caller want to do it.
*/ */
template <typename PT, typename CT> MOZ_CAN_RUN_SCRIPT SplitNodeResult
MOZ_CAN_RUN_SCRIPT SplitNodeResult SplitNodeDeepWithTransaction( SplitNodeDeepWithTransaction(nsIContent& aMostAncestorToSplit,
nsIContent& aMostAncestorToSplit, const EditorDOMPoint& aDeepestStartOfRightNode,
const EditorDOMPointBase<PT, CT>& aDeepestStartOfRightNode,
SplitAtEdges aSplitAtEdges); SplitAtEdges aSplitAtEdges);
/** /**

View File

@ -240,17 +240,17 @@ class MOZ_STACK_CLASS SplitNodeResult final {
* by this instance. Therefore, the life time of both container node * by this instance. Therefore, the life time of both container node
* and child node are guaranteed while using the result temporarily. * and child node are guaranteed while using the result temporarily.
*/ */
EditorRawDOMPoint SplitPoint() const { EditorDOMPoint SplitPoint() const {
if (Failed()) { if (Failed()) {
return EditorRawDOMPoint(); return EditorDOMPoint();
} }
if (mGivenSplitPoint.IsSet()) { if (mGivenSplitPoint.IsSet()) {
return EditorRawDOMPoint(mGivenSplitPoint); return EditorDOMPoint(mGivenSplitPoint);
} }
if (!mPreviousNode) { if (!mPreviousNode) {
return EditorRawDOMPoint(mNextNode); return EditorDOMPoint(mNextNode);
} }
EditorRawDOMPoint point(mPreviousNode); EditorDOMPoint point(mPreviousNode);
DebugOnly<bool> advanced = point.AdvanceOffset(); DebugOnly<bool> advanced = point.AdvanceOffset();
NS_WARNING_ASSERTION(advanced, NS_WARNING_ASSERTION(advanced,
"Failed to advance offset to after previous node"); "Failed to advance offset to after previous node");

View File

@ -519,7 +519,7 @@ nsresult HTMLEditor::SetPositionToAbsolute(Element& aElement) {
nsINode* parentNode = aElement.GetParentNode(); nsINode* parentNode = aElement.GetParentNode();
if (parentNode->GetChildCount() == 1) { if (parentNode->GetChildCount() == 1) {
RefPtr<Element> newBrElement = RefPtr<Element> newBrElement =
InsertBrElementWithTransaction(EditorRawDOMPoint(parentNode, 0)); InsertBrElementWithTransaction(EditorDOMPoint(parentNode, 0));
if (NS_WARN_IF(!newBrElement)) { if (NS_WARN_IF(!newBrElement)) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }

View File

@ -1833,7 +1833,7 @@ EditActionResult HTMLEditRules::WillInsertParagraphSeparator() {
// make block have a line. Then code further below will put in a second br.) // make block have a line. Then code further below will put in a second br.)
if (IsEmptyBlockElement(*blockParent, IgnoreSingleBR::eNo)) { if (IsEmptyBlockElement(*blockParent, IgnoreSingleBR::eNo)) {
AutoEditorDOMPointChildInvalidator lockOffset(atStartOfSelection); AutoEditorDOMPointChildInvalidator lockOffset(atStartOfSelection);
EditorRawDOMPoint endOfBlockParent; EditorDOMPoint endOfBlockParent;
endOfBlockParent.SetToEndOf(blockParent); endOfBlockParent.SetToEndOf(blockParent);
RefPtr<Element> brElement = RefPtr<Element> brElement =
MOZ_KnownLive(HTMLEditorRef()) MOZ_KnownLive(HTMLEditorRef())
@ -2060,7 +2060,7 @@ nsresult HTMLEditRules::InsertBRElement(const EditorDOMPoint& aPointToBreak) {
EditActionResult HTMLEditRules::SplitMailCites() { EditActionResult HTMLEditRules::SplitMailCites() {
MOZ_ASSERT(IsEditorDataAvailable()); MOZ_ASSERT(IsEditorDataAvailable());
EditorRawDOMPoint pointToSplit(EditorBase::GetStartPoint(*SelectionRefPtr())); EditorDOMPoint pointToSplit(EditorBase::GetStartPoint(*SelectionRefPtr()));
if (NS_WARN_IF(!pointToSplit.IsSet())) { if (NS_WARN_IF(!pointToSplit.IsSet())) {
return EditActionIgnored(NS_ERROR_FAILURE); return EditActionIgnored(NS_ERROR_FAILURE);
} }
@ -2128,7 +2128,7 @@ EditActionResult HTMLEditRules::SplitMailCites() {
nsCOMPtr<nsINode> lastChild = previousNodeOfSplitPoint->GetLastChild(); nsCOMPtr<nsINode> lastChild = previousNodeOfSplitPoint->GetLastChild();
if (lastChild && !lastChild->IsHTMLElement(nsGkAtoms::br)) { if (lastChild && !lastChild->IsHTMLElement(nsGkAtoms::br)) {
// We ignore the result here. // We ignore the result here.
EditorRawDOMPoint endOfPreviousNodeOfSplitPoint; EditorDOMPoint endOfPreviousNodeOfSplitPoint;
endOfPreviousNodeOfSplitPoint.SetToEndOf(previousNodeOfSplitPoint); endOfPreviousNodeOfSplitPoint.SetToEndOf(previousNodeOfSplitPoint);
RefPtr<Element> invisibleBrElement = RefPtr<Element> invisibleBrElement =
MOZ_KnownLive(HTMLEditorRef()) MOZ_KnownLive(HTMLEditorRef())
@ -2144,7 +2144,7 @@ EditActionResult HTMLEditRules::SplitMailCites() {
// In most cases, <br> should be inserted after current cite. However, if // In most cases, <br> should be inserted after current cite. However, if
// left cite hasn't been created because the split point was start of the // left cite hasn't been created because the split point was start of the
// cite node, <br> should be inserted before the current cite. // cite node, <br> should be inserted before the current cite.
EditorRawDOMPoint pointToInsertBrNode(splitCiteNodeResult.SplitPoint()); EditorDOMPoint pointToInsertBrNode(splitCiteNodeResult.SplitPoint());
RefPtr<Element> brElement = RefPtr<Element> brElement =
MOZ_KnownLive(HTMLEditorRef()) MOZ_KnownLive(HTMLEditorRef())
.InsertBrElementWithTransaction(pointToInsertBrNode); .InsertBrElementWithTransaction(pointToInsertBrNode);
@ -2179,7 +2179,7 @@ EditActionResult HTMLEditRules::SplitMailCites() {
// then we will need a 2nd br added to achieve blank line that user expects. // then we will need a 2nd br added to achieve blank line that user expects.
if (IsInlineNode(*citeNode)) { if (IsInlineNode(*citeNode)) {
// Use DOM point which we tried to collapse to. // Use DOM point which we tried to collapse to.
EditorRawDOMPoint pointToCreateNewBrNode(atBrNode.GetContainer(), EditorDOMPoint pointToCreateNewBrNode(atBrNode.GetContainer(),
atBrNode.Offset()); atBrNode.Offset());
WSRunObject wsObj(&HTMLEditorRef(), pointToCreateNewBrNode); WSRunObject wsObj(&HTMLEditorRef(), pointToCreateNewBrNode);
@ -2457,8 +2457,9 @@ nsresult HTMLEditRules::WillDeleteSelection(
return rv; return rv;
} }
*aHandled = true; *aHandled = true;
rv = HTMLEditorRef().DeleteTextWithTransaction( rv = MOZ_KnownLive(HTMLEditorRef())
nodeAsText, std::min(so, eo), DeprecatedAbs(eo - so)); .DeleteTextWithTransaction(nodeAsText, std::min(so, eo),
DeprecatedAbs(eo - so));
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -3053,8 +3054,10 @@ nsresult HTMLEditRules::WillDeleteSelection(
// Delete to last character // Delete to last character
OwningNonNull<CharacterData> dataNode = OwningNonNull<CharacterData> dataNode =
*static_cast<CharacterData*>(startNode.get()); *static_cast<CharacterData*>(startNode.get());
rv = HTMLEditorRef().DeleteTextWithTransaction( rv =
dataNode, startOffset, startNode->Length() - startOffset); MOZ_KnownLive(HTMLEditorRef())
.DeleteTextWithTransaction(dataNode, startOffset,
startNode->Length() - startOffset);
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -3066,8 +3069,8 @@ nsresult HTMLEditRules::WillDeleteSelection(
// Delete to first character // Delete to first character
OwningNonNull<CharacterData> dataNode = OwningNonNull<CharacterData> dataNode =
*static_cast<CharacterData*>(endNode.get()); *static_cast<CharacterData*>(endNode.get());
rv = rv = MOZ_KnownLive(HTMLEditorRef())
HTMLEditorRef().DeleteTextWithTransaction(dataNode, 0, endOffset); .DeleteTextWithTransaction(dataNode, 0, endOffset);
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -3165,7 +3168,7 @@ nsresult HTMLEditRules::DeleteNodeIfCollapsedText(nsINode& aNode) {
nsresult HTMLEditRules::InsertBRIfNeeded() { nsresult HTMLEditRules::InsertBRIfNeeded() {
MOZ_ASSERT(IsEditorDataAvailable()); MOZ_ASSERT(IsEditorDataAvailable());
EditorRawDOMPoint atStartOfSelection( EditorDOMPoint atStartOfSelection(
EditorBase::GetStartPoint(*SelectionRefPtr())); EditorBase::GetStartPoint(*SelectionRefPtr()));
if (NS_WARN_IF(!atStartOfSelection.IsSet())) { if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
@ -3684,7 +3687,7 @@ EditActionResult HTMLEditRules::MoveNodeSmart(nsIContent& aNode,
return EditActionIgnored(rv); return EditActionIgnored(rv);
} }
} else { } else {
EditorRawDOMPoint pointToInsert(&aDestElement, *aInOutDestOffset); EditorDOMPoint pointToInsert(&aDestElement, *aInOutDestOffset);
nsresult rv = MOZ_KnownLive(HTMLEditorRef()) nsresult rv = MOZ_KnownLive(HTMLEditorRef())
.MoveNodeWithTransaction(aNode, pointToInsert); .MoveNodeWithTransaction(aNode, pointToInsert);
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
@ -3958,7 +3961,9 @@ nsresult HTMLEditRules::MakeList(nsAtom& aListType, bool aEntireList,
if (NS_WARN_IF(splitAtSelectionStartResult.Failed())) { if (NS_WARN_IF(splitAtSelectionStartResult.Failed())) {
return splitAtSelectionStartResult.Rv(); return splitAtSelectionStartResult.Rv();
} }
RefPtr<Element> theList = HTMLEditorRef().CreateNodeWithTransaction( RefPtr<Element> theList =
MOZ_KnownLive(HTMLEditorRef())
.CreateNodeWithTransaction(
aListType, splitAtSelectionStartResult.SplitPoint()); aListType, splitAtSelectionStartResult.SplitPoint());
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
@ -3967,9 +3972,9 @@ nsresult HTMLEditRules::MakeList(nsAtom& aListType, bool aEntireList,
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
EditorRawDOMPoint atFirstListItemToInsertBefore(theList, 0); RefPtr<Element> theListItem =
RefPtr<Element> theListItem = HTMLEditorRef().CreateNodeWithTransaction( MOZ_KnownLive(HTMLEditorRef())
aItemType, atFirstListItemToInsertBefore); .CreateNodeWithTransaction(aItemType, EditorDOMPoint(theList, 0));
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -4079,7 +4084,7 @@ nsresult HTMLEditRules::MakeList(nsAtom& aListType, bool aEntireList,
continue; continue;
} }
EditorRawDOMPoint atCurNode(curNode); EditorDOMPoint atCurNode(curNode);
if (NS_WARN_IF(!atCurNode.IsSet())) { if (NS_WARN_IF(!atCurNode.IsSet())) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
@ -4104,9 +4109,10 @@ nsresult HTMLEditRules::MakeList(nsAtom& aListType, bool aEntireList,
return error.StealNSResult(); return error.StealNSResult();
} }
newBlock = newLeftNode ? newLeftNode->AsElement() : nullptr; newBlock = newLeftNode ? newLeftNode->AsElement() : nullptr;
EditorRawDOMPoint atParentOfCurNode(atCurNode.GetContainer()); curList =
curList = HTMLEditorRef().CreateNodeWithTransaction( MOZ_KnownLive(HTMLEditorRef())
aListType, atParentOfCurNode); .CreateNodeWithTransaction(
aListType, EditorDOMPoint(atCurNode.GetContainer()));
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -4216,8 +4222,9 @@ nsresult HTMLEditRules::MakeList(nsAtom& aListType, bool aEntireList,
if (NS_WARN_IF(splitCurNodeResult.Failed())) { if (NS_WARN_IF(splitCurNodeResult.Failed())) {
return splitCurNodeResult.Rv(); return splitCurNodeResult.Rv();
} }
curList = HTMLEditorRef().CreateNodeWithTransaction( curList = MOZ_KnownLive(HTMLEditorRef())
aListType, splitCurNodeResult.SplitPoint()); .CreateNodeWithTransaction(aListType,
splitCurNodeResult.SplitPoint());
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -4467,7 +4474,7 @@ nsresult HTMLEditRules::MakeBasicBlock(nsAtom& blockType) {
if (NS_WARN_IF(splitNodeResult.Failed())) { if (NS_WARN_IF(splitNodeResult.Failed())) {
return splitNodeResult.Rv(); return splitNodeResult.Rv();
} }
EditorRawDOMPoint pointToInsertBrNode(splitNodeResult.SplitPoint()); EditorDOMPoint pointToInsertBrNode(splitNodeResult.SplitPoint());
// Put a <br> element at the split point // Put a <br> element at the split point
brContent = MOZ_KnownLive(HTMLEditorRef()) brContent = MOZ_KnownLive(HTMLEditorRef())
.InsertBrElementWithTransaction(pointToInsertBrNode); .InsertBrElementWithTransaction(pointToInsertBrNode);
@ -4515,8 +4522,9 @@ nsresult HTMLEditRules::MakeBasicBlock(nsAtom& blockType) {
if (NS_WARN_IF(splitNodeResult.Failed())) { if (NS_WARN_IF(splitNodeResult.Failed())) {
return splitNodeResult.Rv(); return splitNodeResult.Rv();
} }
RefPtr<Element> block = HTMLEditorRef().CreateNodeWithTransaction( RefPtr<Element> block =
blockType, splitNodeResult.SplitPoint()); MOZ_KnownLive(HTMLEditorRef())
.CreateNodeWithTransaction(blockType, splitNodeResult.SplitPoint());
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -4706,8 +4714,10 @@ nsresult HTMLEditRules::IndentAroundSelectionWithCSS() {
if (NS_WARN_IF(splitNodeResult.Failed())) { if (NS_WARN_IF(splitNodeResult.Failed())) {
return splitNodeResult.Rv(); return splitNodeResult.Rv();
} }
RefPtr<Element> theBlock = HTMLEditorRef().CreateNodeWithTransaction( RefPtr<Element> theBlock =
*nsGkAtoms::div, splitNodeResult.SplitPoint()); MOZ_KnownLive(HTMLEditorRef())
.CreateNodeWithTransaction(*nsGkAtoms::div,
splitNodeResult.SplitPoint());
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -4779,7 +4789,7 @@ nsresult HTMLEditRules::IndentAroundSelectionWithCSS() {
nsresult rv = nsresult rv =
MOZ_KnownLive(HTMLEditorRef()) MOZ_KnownLive(HTMLEditorRef())
.MoveNodeWithTransaction(MOZ_KnownLive(*curNode->AsContent()), .MoveNodeWithTransaction(MOZ_KnownLive(*curNode->AsContent()),
EditorRawDOMPoint(sibling, 0)); EditorDOMPoint(sibling, 0));
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -4827,8 +4837,9 @@ nsresult HTMLEditRules::IndentAroundSelectionWithCSS() {
if (NS_WARN_IF(splitNodeResult.Failed())) { if (NS_WARN_IF(splitNodeResult.Failed())) {
return splitNodeResult.Rv(); return splitNodeResult.Rv();
} }
curList = HTMLEditorRef().CreateNodeWithTransaction( curList = MOZ_KnownLive(HTMLEditorRef())
*containerName, splitNodeResult.SplitPoint()); .CreateNodeWithTransaction(MOZ_KnownLive(*containerName),
splitNodeResult.SplitPoint());
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -4878,8 +4889,9 @@ nsresult HTMLEditRules::IndentAroundSelectionWithCSS() {
if (NS_WARN_IF(splitNodeResult.Failed())) { if (NS_WARN_IF(splitNodeResult.Failed())) {
return splitNodeResult.Rv(); return splitNodeResult.Rv();
} }
curQuote = HTMLEditorRef().CreateNodeWithTransaction( curQuote = MOZ_KnownLive(HTMLEditorRef())
*nsGkAtoms::div, splitNodeResult.SplitPoint()); .CreateNodeWithTransaction(*nsGkAtoms::div,
splitNodeResult.SplitPoint());
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -4985,8 +4997,10 @@ nsresult HTMLEditRules::IndentAroundSelectionWithHTML() {
if (NS_WARN_IF(splitNodeResult.Failed())) { if (NS_WARN_IF(splitNodeResult.Failed())) {
return splitNodeResult.Rv(); return splitNodeResult.Rv();
} }
RefPtr<Element> theBlock = HTMLEditorRef().CreateNodeWithTransaction( RefPtr<Element> theBlock =
*nsGkAtoms::blockquote, splitNodeResult.SplitPoint()); MOZ_KnownLive(HTMLEditorRef())
.CreateNodeWithTransaction(*nsGkAtoms::blockquote,
splitNodeResult.SplitPoint());
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -5051,7 +5065,7 @@ nsresult HTMLEditRules::IndentAroundSelectionWithHTML() {
sibling->NodeInfo()->NamespaceID()) { sibling->NodeInfo()->NamespaceID()) {
rv = MOZ_KnownLive(HTMLEditorRef()) rv = MOZ_KnownLive(HTMLEditorRef())
.MoveNodeWithTransaction(MOZ_KnownLive(*curNode->AsContent()), .MoveNodeWithTransaction(MOZ_KnownLive(*curNode->AsContent()),
EditorRawDOMPoint(sibling, 0)); EditorDOMPoint(sibling, 0));
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -5099,8 +5113,9 @@ nsresult HTMLEditRules::IndentAroundSelectionWithHTML() {
if (NS_WARN_IF(splitNodeResult.Failed())) { if (NS_WARN_IF(splitNodeResult.Failed())) {
return splitNodeResult.Rv(); return splitNodeResult.Rv();
} }
curList = HTMLEditorRef().CreateNodeWithTransaction( curList = MOZ_KnownLive(HTMLEditorRef())
*containerName, splitNodeResult.SplitPoint()); .CreateNodeWithTransaction(MOZ_KnownLive(*containerName),
splitNodeResult.SplitPoint());
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -5161,8 +5176,9 @@ nsresult HTMLEditRules::IndentAroundSelectionWithHTML() {
if (NS_WARN_IF(splitNodeResult.Failed())) { if (NS_WARN_IF(splitNodeResult.Failed())) {
return splitNodeResult.Rv(); return splitNodeResult.Rv();
} }
curList = HTMLEditorRef().CreateNodeWithTransaction( curList = MOZ_KnownLive(HTMLEditorRef())
*containerName, splitNodeResult.SplitPoint()); .CreateNodeWithTransaction(MOZ_KnownLive(*containerName),
splitNodeResult.SplitPoint());
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -5207,8 +5223,9 @@ nsresult HTMLEditRules::IndentAroundSelectionWithHTML() {
if (NS_WARN_IF(splitNodeResult.Failed())) { if (NS_WARN_IF(splitNodeResult.Failed())) {
return splitNodeResult.Rv(); return splitNodeResult.Rv();
} }
curQuote = HTMLEditorRef().CreateNodeWithTransaction( curQuote = MOZ_KnownLive(HTMLEditorRef())
*nsGkAtoms::blockquote, splitNodeResult.SplitPoint()); .CreateNodeWithTransaction(*nsGkAtoms::blockquote,
splitNodeResult.SplitPoint());
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -5547,7 +5564,7 @@ SplitRangeOffFromNodeResult HTMLEditRules::OutdentAroundSelection() {
// We have an embedded list, so move it out from under the parent // We have an embedded list, so move it out from under the parent
// list. Be sure to put it after the parent list because this // list. Be sure to put it after the parent list because this
// loop iterates backwards through the parent's list of children. // loop iterates backwards through the parent's list of children.
EditorRawDOMPoint afterCurrentList(curParent, offset + 1); EditorDOMPoint afterCurrentList(curParent, offset + 1);
rv = MOZ_KnownLive(HTMLEditorRef()) rv = MOZ_KnownLive(HTMLEditorRef())
.MoveNodeWithTransaction(*child, afterCurrentList); .MoveNodeWithTransaction(*child, afterCurrentList);
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
@ -5656,7 +5673,7 @@ SplitRangeOffFromNodeResult HTMLEditRules::SplitRangeOffFromBlock(
SplitNodeResult splitAtStartResult = SplitNodeResult splitAtStartResult =
MOZ_KnownLive(HTMLEditorRef()) MOZ_KnownLive(HTMLEditorRef())
.SplitNodeDeepWithTransaction( .SplitNodeDeepWithTransaction(
aBlockElement, EditorRawDOMPoint(&aStartOfMiddleElement), aBlockElement, EditorDOMPoint(&aStartOfMiddleElement),
SplitAtEdges::eDoNotCreateEmptyContainer); SplitAtEdges::eDoNotCreateEmptyContainer);
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return SplitRangeOffFromNodeResult(NS_ERROR_EDITOR_DESTROYED); return SplitRangeOffFromNodeResult(NS_ERROR_EDITOR_DESTROYED);
@ -5665,7 +5682,7 @@ SplitRangeOffFromNodeResult HTMLEditRules::SplitRangeOffFromBlock(
"Failed to split aBlockElement at start"); "Failed to split aBlockElement at start");
// Split at after the end // Split at after the end
EditorRawDOMPoint atAfterEnd(&aEndOfMiddleElement); EditorDOMPoint atAfterEnd(&aEndOfMiddleElement);
DebugOnly<bool> advanced = atAfterEnd.AdvanceOffset(); DebugOnly<bool> advanced = atAfterEnd.AdvanceOffset();
NS_WARNING_ASSERTION(advanced, "Failed to advance offset after the end node"); NS_WARNING_ASSERTION(advanced, "Failed to advance offset after the end node");
SplitNodeResult splitAtEndResult = SplitNodeResult splitAtEndResult =
@ -5826,7 +5843,7 @@ nsresult HTMLEditRules::CreateStyleForInsertText(Document& aDocument) {
SplitNodeResult splitTextNodeResult = SplitNodeResult splitTextNodeResult =
MOZ_KnownLive(HTMLEditorRef()) MOZ_KnownLive(HTMLEditorRef())
.SplitNodeDeepWithTransaction( .SplitNodeDeepWithTransaction(
*text, EditorRawDOMPoint(text, offset), *text, EditorDOMPoint(text, offset),
SplitAtEdges::eAllowToCreateEmptyContainer); SplitAtEdges::eAllowToCreateEmptyContainer);
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
@ -5843,9 +5860,9 @@ nsresult HTMLEditRules::CreateStyleForInsertText(Document& aDocument) {
} }
OwningNonNull<Text> newNode = OwningNonNull<Text> newNode =
EditorBase::CreateTextNode(aDocument, EmptyString()); EditorBase::CreateTextNode(aDocument, EmptyString());
nsresult rv = MOZ_KnownLive(HTMLEditorRef()) nsresult rv =
.InsertNodeWithTransaction( MOZ_KnownLive(HTMLEditorRef())
*newNode, EditorRawDOMPoint(node, offset)); .InsertNodeWithTransaction(*newNode, EditorDOMPoint(node, offset));
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -6054,8 +6071,9 @@ nsresult HTMLEditRules::AlignContentsAtSelection(const nsAString& aAlignType) {
} }
} }
} }
RefPtr<Element> div = HTMLEditorRef().CreateNodeWithTransaction( RefPtr<Element> div =
*nsGkAtoms::div, pointToInsertDiv); MOZ_KnownLive(HTMLEditorRef())
.CreateNodeWithTransaction(*nsGkAtoms::div, pointToInsertDiv);
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -6070,8 +6088,7 @@ nsresult HTMLEditRules::AlignContentsAtSelection(const nsAString& aAlignType) {
return rv; return rv;
} }
// Put in a moz-br so that it won't get deleted // Put in a moz-br so that it won't get deleted
CreateElementResult createMozBrResult = CreateElementResult createMozBrResult = CreateMozBR(EditorDOMPoint(div, 0));
CreateMozBR(EditorRawDOMPoint(div, 0));
if (NS_WARN_IF(createMozBrResult.Failed())) { if (NS_WARN_IF(createMozBrResult.Failed())) {
return createMozBrResult.Rv(); return createMozBrResult.Rv();
} }
@ -6193,8 +6210,9 @@ nsresult HTMLEditRules::AlignContentsAtSelection(const nsAString& aAlignType) {
if (NS_WARN_IF(splitNodeResult.Failed())) { if (NS_WARN_IF(splitNodeResult.Failed())) {
return splitNodeResult.Rv(); return splitNodeResult.Rv();
} }
curDiv = HTMLEditorRef().CreateNodeWithTransaction( curDiv = MOZ_KnownLive(HTMLEditorRef())
*nsGkAtoms::div, splitNodeResult.SplitPoint()); .CreateNodeWithTransaction(*nsGkAtoms::div,
splitNodeResult.SplitPoint());
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -6277,9 +6295,9 @@ nsresult HTMLEditRules::AlignBlockContents(nsINode& aNode,
// else we need to put in a div, set the alignment, and toss in all the // else we need to put in a div, set the alignment, and toss in all the
// children // children
EditorRawDOMPoint atStartOfNode(&aNode, 0); RefPtr<Element> divElem = MOZ_KnownLive(HTMLEditorRef())
RefPtr<Element> divElem = .CreateNodeWithTransaction(
HTMLEditorRef().CreateNodeWithTransaction(*nsGkAtoms::div, atStartOfNode); *nsGkAtoms::div, EditorDOMPoint(&aNode, 0));
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -6300,7 +6318,7 @@ nsresult HTMLEditRules::AlignBlockContents(nsINode& aNode,
while (lastChild && (lastChild != divElem)) { while (lastChild && (lastChild != divElem)) {
nsresult rv = nsresult rv =
MOZ_KnownLive(HTMLEditorRef()) MOZ_KnownLive(HTMLEditorRef())
.MoveNodeWithTransaction(*lastChild, EditorRawDOMPoint(divElem, 0)); .MoveNodeWithTransaction(*lastChild, EditorDOMPoint(divElem, 0));
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -7471,8 +7489,7 @@ nsresult HTMLEditRules::BustUpInlinesAtRangeEndpoints(RangeItem& aRangeItem) {
MOZ_KnownLive(HTMLEditorRef()) MOZ_KnownLive(HTMLEditorRef())
.SplitNodeDeepWithTransaction( .SplitNodeDeepWithTransaction(
*endInline, *endInline,
EditorRawDOMPoint(aRangeItem.mEndContainer, EditorDOMPoint(aRangeItem.mEndContainer, aRangeItem.mEndOffset),
aRangeItem.mEndOffset),
SplitAtEdges::eDoNotCreateEmptyContainer); SplitAtEdges::eDoNotCreateEmptyContainer);
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
@ -7496,7 +7513,7 @@ nsresult HTMLEditRules::BustUpInlinesAtRangeEndpoints(RangeItem& aRangeItem) {
MOZ_KnownLive(HTMLEditorRef()) MOZ_KnownLive(HTMLEditorRef())
.SplitNodeDeepWithTransaction( .SplitNodeDeepWithTransaction(
*startInline, *startInline,
EditorRawDOMPoint(aRangeItem.mStartContainer, EditorDOMPoint(aRangeItem.mStartContainer,
aRangeItem.mStartOffset), aRangeItem.mStartOffset),
SplitAtEdges::eDoNotCreateEmptyContainer); SplitAtEdges::eDoNotCreateEmptyContainer);
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
@ -7538,7 +7555,7 @@ nsresult HTMLEditRules::BustUpInlinesAtBRs(
// Else we need to bust up aNode along all the breaks // Else we need to bust up aNode along all the breaks
nsCOMPtr<nsIContent> nextNode = &aNode; nsCOMPtr<nsIContent> nextNode = &aNode;
for (OwningNonNull<nsINode>& brNode : arrayOfBreaks) { for (OwningNonNull<nsINode>& brNode : arrayOfBreaks) {
EditorRawDOMPoint atBrNode(brNode); EditorDOMPoint atBrNode(brNode);
if (NS_WARN_IF(!atBrNode.IsSet())) { if (NS_WARN_IF(!atBrNode.IsSet())) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
@ -7563,7 +7580,7 @@ nsresult HTMLEditRules::BustUpInlinesAtBRs(
} }
// Move break outside of container and also put in node list // Move break outside of container and also put in node list
EditorRawDOMPoint atNextNode(splitNodeResult.GetNextNode()); EditorDOMPoint atNextNode(splitNodeResult.GetNextNode());
nsresult rv = MOZ_KnownLive(HTMLEditorRef()) nsresult rv = MOZ_KnownLive(HTMLEditorRef())
.MoveNodeWithTransaction( .MoveNodeWithTransaction(
MOZ_KnownLive(*brNode->AsContent()), atNextNode); MOZ_KnownLive(*brNode->AsContent()), atNextNode);
@ -7741,7 +7758,7 @@ nsresult HTMLEditRules::ReturnInHeader(Element& aHeader, nsINode& aNode,
SplitNodeResult splitHeaderResult = SplitNodeResult splitHeaderResult =
MOZ_KnownLive(HTMLEditorRef()) MOZ_KnownLive(HTMLEditorRef())
.SplitNodeDeepWithTransaction( .SplitNodeDeepWithTransaction(
aHeader, EditorRawDOMPoint(node, aOffset), aHeader, EditorDOMPoint(node, aOffset),
SplitAtEdges::eAllowToCreateEmptyContainer); SplitAtEdges::eAllowToCreateEmptyContainer);
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
@ -7760,7 +7777,7 @@ nsresult HTMLEditRules::ReturnInHeader(Element& aHeader, nsINode& aNode,
} }
if (isEmptyNode) { if (isEmptyNode) {
CreateElementResult createMozBrResult = CreateElementResult createMozBrResult =
CreateMozBR(EditorRawDOMPoint(prevItem, 0)); CreateMozBR(EditorDOMPoint(prevItem, 0));
if (NS_WARN_IF(createMozBrResult.Failed())) { if (NS_WARN_IF(createMozBrResult.Failed())) {
return createMozBrResult.Rv(); return createMozBrResult.Rv();
} }
@ -7789,9 +7806,13 @@ nsresult HTMLEditRules::ReturnInHeader(Element& aHeader, nsINode& aNode,
// Create a paragraph // Create a paragraph
nsAtom& paraAtom = DefaultParagraphSeparator(); nsAtom& paraAtom = DefaultParagraphSeparator();
// We want a wrapper element even if we separate with <br> // We want a wrapper element even if we separate with <br>
EditorRawDOMPoint nextToHeader(headerParent, offset + 1); EditorDOMPoint nextToHeader(headerParent, offset + 1);
RefPtr<Element> pNode = HTMLEditorRef().CreateNodeWithTransaction( RefPtr<Element> pNode =
&paraAtom == nsGkAtoms::br ? *nsGkAtoms::p : paraAtom, nextToHeader); MOZ_KnownLive(HTMLEditorRef())
.CreateNodeWithTransaction(&paraAtom == nsGkAtoms::br
? *nsGkAtoms::p
: MOZ_KnownLive(paraAtom),
nextToHeader);
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -7802,7 +7823,7 @@ nsresult HTMLEditRules::ReturnInHeader(Element& aHeader, nsINode& aNode,
// Append a <br> to it // Append a <br> to it
RefPtr<Element> brElement = RefPtr<Element> brElement =
MOZ_KnownLive(HTMLEditorRef()) MOZ_KnownLive(HTMLEditorRef())
.InsertBrElementWithTransaction(EditorRawDOMPoint(pNode, 0)); .InsertBrElementWithTransaction(EditorDOMPoint(pNode, 0));
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -7938,7 +7959,7 @@ EditActionResult HTMLEditRules::ReturnInParagraph(Element& aParentDivOrP) {
EditorDOMPoint pointToSplitParentDivOrP(atStartOfSelection); EditorDOMPoint pointToSplitParentDivOrP(atStartOfSelection);
EditorRawDOMPoint pointToInsertBR; EditorDOMPoint pointToInsertBR;
if (doesCRCreateNewP && atStartOfSelection.GetContainer() == &aParentDivOrP) { if (doesCRCreateNewP && atStartOfSelection.GetContainer() == &aParentDivOrP) {
// We are at the edges of the block, so, we don't need to create new <br>. // We are at the edges of the block, so, we don't need to create new <br>.
brContent = nullptr; brContent = nullptr;
@ -8065,7 +8086,7 @@ nsresult HTMLEditRules::SplitParagraph(
SplitNodeResult splitDivOrPResult = SplitNodeResult splitDivOrPResult =
MOZ_KnownLive(HTMLEditorRef()) MOZ_KnownLive(HTMLEditorRef())
.SplitNodeDeepWithTransaction( .SplitNodeDeepWithTransaction(
aParentDivOrP, EditorRawDOMPoint(selNode, selOffset), aParentDivOrP, EditorDOMPoint(selNode, selOffset),
SplitAtEdges::eAllowToCreateEmptyContainer); SplitAtEdges::eAllowToCreateEmptyContainer);
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
@ -8157,7 +8178,7 @@ nsresult HTMLEditRules::ReturnInListItem(Element& aListItem, nsINode& aNode,
// Are we the last list item in the list? // Are we the last list item in the list?
if (!HTMLEditorRef().IsLastEditableChild(&aListItem)) { if (!HTMLEditorRef().IsLastEditableChild(&aListItem)) {
// We need to split the list! // We need to split the list!
EditorRawDOMPoint atListItem(&aListItem); EditorDOMPoint atListItem(&aListItem);
ErrorResult error; ErrorResult error;
leftListNode = MOZ_KnownLive(HTMLEditorRef()) leftListNode = MOZ_KnownLive(HTMLEditorRef())
.SplitNodeWithTransaction(atListItem, error); .SplitNodeWithTransaction(atListItem, error);
@ -8171,7 +8192,7 @@ nsresult HTMLEditRules::ReturnInListItem(Element& aListItem, nsINode& aNode,
} }
// Are we in a sublist? // Are we in a sublist?
EditorRawDOMPoint atNextSiblingOfLeftList(leftListNode); EditorDOMPoint atNextSiblingOfLeftList(leftListNode);
DebugOnly<bool> advanced = atNextSiblingOfLeftList.AdvanceOffset(); DebugOnly<bool> advanced = atNextSiblingOfLeftList.AdvanceOffset();
NS_WARNING_ASSERTION(advanced, NS_WARNING_ASSERTION(advanced,
"Failed to advance offset after the right list node"); "Failed to advance offset after the right list node");
@ -8209,8 +8230,11 @@ nsresult HTMLEditRules::ReturnInListItem(Element& aListItem, nsINode& aNode,
// Time to insert a paragraph // Time to insert a paragraph
nsAtom& paraAtom = DefaultParagraphSeparator(); nsAtom& paraAtom = DefaultParagraphSeparator();
// We want a wrapper even if we separate with <br> // We want a wrapper even if we separate with <br>
RefPtr<Element> pNode = HTMLEditorRef().CreateNodeWithTransaction( RefPtr<Element> pNode =
&paraAtom == nsGkAtoms::br ? *nsGkAtoms::p : paraAtom, MOZ_KnownLive(HTMLEditorRef())
.CreateNodeWithTransaction(&paraAtom == nsGkAtoms::br
? *nsGkAtoms::p
: MOZ_KnownLive(paraAtom),
atNextSiblingOfLeftList); atNextSiblingOfLeftList);
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
@ -8222,7 +8246,7 @@ nsresult HTMLEditRules::ReturnInListItem(Element& aListItem, nsINode& aNode,
// Append a <br> to it // Append a <br> to it
RefPtr<Element> brElement = RefPtr<Element> brElement =
MOZ_KnownLive(HTMLEditorRef()) MOZ_KnownLive(HTMLEditorRef())
.InsertBrElementWithTransaction(EditorRawDOMPoint(pNode, 0)); .InsertBrElementWithTransaction(EditorDOMPoint(pNode, 0));
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -8263,7 +8287,7 @@ nsresult HTMLEditRules::ReturnInListItem(Element& aListItem, nsINode& aNode,
SplitNodeResult splitListItemResult = SplitNodeResult splitListItemResult =
MOZ_KnownLive(HTMLEditorRef()) MOZ_KnownLive(HTMLEditorRef())
.SplitNodeDeepWithTransaction( .SplitNodeDeepWithTransaction(
aListItem, EditorRawDOMPoint(selNode, aOffset), aListItem, EditorDOMPoint(selNode, aOffset),
SplitAtEdges::eAllowToCreateEmptyContainer); SplitAtEdges::eAllowToCreateEmptyContainer);
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
@ -8284,7 +8308,7 @@ nsresult HTMLEditRules::ReturnInListItem(Element& aListItem, nsINode& aNode,
} }
if (isEmptyNode) { if (isEmptyNode) {
CreateElementResult createMozBrResult = CreateElementResult createMozBrResult =
CreateMozBR(EditorRawDOMPoint(prevItem, 0)); CreateMozBR(EditorDOMPoint(prevItem, 0));
if (NS_WARN_IF(createMozBrResult.Failed())) { if (NS_WARN_IF(createMozBrResult.Failed())) {
return createMozBrResult.Rv(); return createMozBrResult.Rv();
} }
@ -8302,10 +8326,11 @@ nsresult HTMLEditRules::ReturnInListItem(Element& aListItem, nsINode& aNode,
nsAtom* listAtom = nsAtom* listAtom =
nodeAtom == nsGkAtoms::dt ? nsGkAtoms::dd : nsGkAtoms::dt; nodeAtom == nsGkAtoms::dt ? nsGkAtoms::dd : nsGkAtoms::dt;
MOZ_DIAGNOSTIC_ASSERT(itemOffset != -1); MOZ_DIAGNOSTIC_ASSERT(itemOffset != -1);
EditorRawDOMPoint atNextListItem(list, aListItem.GetNextSibling(), EditorDOMPoint atNextListItem(list, aListItem.GetNextSibling(),
itemOffset + 1); itemOffset + 1);
RefPtr<Element> newListItem = RefPtr<Element> newListItem =
HTMLEditorRef().CreateNodeWithTransaction(*listAtom, MOZ_KnownLive(HTMLEditorRef())
.CreateNodeWithTransaction(MOZ_KnownLive(*listAtom),
atNextListItem); atNextListItem);
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
@ -8454,15 +8479,15 @@ nsresult HTMLEditRules::MakeBlockquote(
// If no curBlock, make one // If no curBlock, make one
if (!curBlock) { if (!curBlock) {
EditorDOMPoint atCurNode(curNode);
SplitNodeResult splitNodeResult = SplitNodeResult splitNodeResult =
MaybeSplitAncestorsForInsertWithTransaction(*nsGkAtoms::blockquote, MaybeSplitAncestorsForInsertWithTransaction(*nsGkAtoms::blockquote,
atCurNode); EditorDOMPoint(curNode));
if (NS_WARN_IF(splitNodeResult.Failed())) { if (NS_WARN_IF(splitNodeResult.Failed())) {
return splitNodeResult.Rv(); return splitNodeResult.Rv();
} }
curBlock = HTMLEditorRef().CreateNodeWithTransaction( curBlock = MOZ_KnownLive(HTMLEditorRef())
*nsGkAtoms::blockquote, splitNodeResult.SplitPoint()); .CreateNodeWithTransaction(*nsGkAtoms::blockquote,
splitNodeResult.SplitPoint());
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -8674,8 +8699,10 @@ nsresult HTMLEditRules::ApplyBlockStyle(
if (NS_WARN_IF(splitNodeResult.Failed())) { if (NS_WARN_IF(splitNodeResult.Failed())) {
return splitNodeResult.Rv(); return splitNodeResult.Rv();
} }
RefPtr<Element> theBlock = HTMLEditorRef().CreateNodeWithTransaction( RefPtr<Element> theBlock =
aBlockTag, splitNodeResult.SplitPoint()); MOZ_KnownLive(HTMLEditorRef())
.CreateNodeWithTransaction(aBlockTag,
splitNodeResult.SplitPoint());
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -8711,8 +8738,9 @@ nsresult HTMLEditRules::ApplyBlockStyle(
if (NS_WARN_IF(splitNodeResult.Failed())) { if (NS_WARN_IF(splitNodeResult.Failed())) {
return splitNodeResult.Rv(); return splitNodeResult.Rv();
} }
curBlock = HTMLEditorRef().CreateNodeWithTransaction( curBlock = MOZ_KnownLive(HTMLEditorRef())
aBlockTag, splitNodeResult.SplitPoint()); .CreateNodeWithTransaction(aBlockTag,
splitNodeResult.SplitPoint());
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -8757,8 +8785,9 @@ nsresult HTMLEditRules::ApplyBlockStyle(
if (NS_WARN_IF(splitNodeResult.Failed())) { if (NS_WARN_IF(splitNodeResult.Failed())) {
return splitNodeResult.Rv(); return splitNodeResult.Rv();
} }
curBlock = HTMLEditorRef().CreateNodeWithTransaction( curBlock = MOZ_KnownLive(HTMLEditorRef())
aBlockTag, splitNodeResult.SplitPoint()); .CreateNodeWithTransaction(aBlockTag,
splitNodeResult.SplitPoint());
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -8793,9 +8822,8 @@ nsresult HTMLEditRules::ApplyBlockStyle(
return NS_OK; return NS_OK;
} }
template <typename PT, typename CT>
SplitNodeResult HTMLEditRules::MaybeSplitAncestorsForInsertWithTransaction( SplitNodeResult HTMLEditRules::MaybeSplitAncestorsForInsertWithTransaction(
nsAtom& aTag, const EditorDOMPointBase<PT, CT>& aStartOfDeepestRightNode) { nsAtom& aTag, const EditorDOMPoint& aStartOfDeepestRightNode) {
MOZ_ASSERT(IsEditorDataAvailable()); MOZ_ASSERT(IsEditorDataAvailable());
if (NS_WARN_IF(!aStartOfDeepestRightNode.IsSet())) { if (NS_WARN_IF(!aStartOfDeepestRightNode.IsSet())) {
@ -8873,7 +8901,7 @@ nsresult HTMLEditRules::JoinNearestEditableNodesWithTransaction(
int32_t parOffset = parent->ComputeIndexOf(&aNodeLeft); int32_t parOffset = parent->ComputeIndexOf(&aNodeLeft);
nsresult rv = MOZ_KnownLive(HTMLEditorRef()) nsresult rv = MOZ_KnownLive(HTMLEditorRef())
.MoveNodeWithTransaction( .MoveNodeWithTransaction(
aNodeRight, EditorRawDOMPoint(parent, parOffset)); aNodeRight, EditorDOMPoint(parent, parOffset));
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -9110,7 +9138,7 @@ HTMLEditRules::InsertBRElementToEmptyListItemsAndTableCellsInChangedRange() {
// still pass the "IsEmptyNode" test, and we want the br's to be after // still pass the "IsEmptyNode" test, and we want the br's to be after
// them. Also, we want the br to be after the selection if the selection // them. Also, we want the br to be after the selection if the selection
// is in this node. // is in this node.
EditorRawDOMPoint endOfNode; EditorDOMPoint endOfNode;
endOfNode.SetToEndOf(node); endOfNode.SetToEndOf(node);
// XXX This method should return nsreuslt due to may be destroyed by this // XXX This method should return nsreuslt due to may be destroyed by this
// CreateMozBr() call. // CreateMozBr() call.
@ -9628,7 +9656,7 @@ nsresult HTMLEditRules::RemoveEmptyNodesInChangedRange() {
// but preserve br. // but preserve br.
RefPtr<Element> brElement = RefPtr<Element> brElement =
MOZ_KnownLive(HTMLEditorRef()) MOZ_KnownLive(HTMLEditorRef())
.InsertBrElementWithTransaction(EditorRawDOMPoint(delNode)); .InsertBrElementWithTransaction(EditorDOMPoint(delNode));
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -10029,8 +10057,8 @@ nsresult HTMLEditRules::InsertBRIfNeededInternal(nsINode& aNode,
} }
CreateElementResult createBrResult = CreateElementResult createBrResult =
!aInsertMozBR ? CreateBR(EditorRawDOMPoint(&aNode, 0)) !aInsertMozBR ? CreateBR(EditorDOMPoint(&aNode, 0))
: CreateMozBR(EditorRawDOMPoint(&aNode, 0)); : CreateMozBR(EditorDOMPoint(&aNode, 0));
if (NS_WARN_IF(createBrResult.Failed())) { if (NS_WARN_IF(createBrResult.Failed())) {
return createBrResult.Rv(); return createBrResult.Rv();
} }
@ -10345,7 +10373,7 @@ nsresult HTMLEditRules::MakeSureElemStartsOrEndsOnCR(nsINode& aNode,
} }
} }
if (!foundCR) { if (!foundCR) {
EditorRawDOMPoint pointToInsert; EditorDOMPoint pointToInsert;
if (!aStarts) { if (!aStarts) {
pointToInsert.SetToEndOf(&aNode); pointToInsert.SetToEndOf(&aNode);
} else { } else {
@ -10591,8 +10619,10 @@ nsresult HTMLEditRules::PrepareToMakeElementAbsolutePosition(
if (NS_WARN_IF(splitNodeResult.Failed())) { if (NS_WARN_IF(splitNodeResult.Failed())) {
return splitNodeResult.Rv(); return splitNodeResult.Rv();
} }
RefPtr<Element> positionedDiv = HTMLEditorRef().CreateNodeWithTransaction( RefPtr<Element> positionedDiv =
*nsGkAtoms::div, splitNodeResult.SplitPoint()); MOZ_KnownLive(HTMLEditorRef())
.CreateNodeWithTransaction(*nsGkAtoms::div,
splitNodeResult.SplitPoint());
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -10665,8 +10695,10 @@ nsresult HTMLEditRules::PrepareToMakeElementAbsolutePosition(
return splitNodeResult.Rv(); return splitNodeResult.Rv();
} }
if (!curPositionedDiv) { if (!curPositionedDiv) {
curPositionedDiv = HTMLEditorRef().CreateNodeWithTransaction( curPositionedDiv =
*nsGkAtoms::div, splitNodeResult.SplitPoint()); MOZ_KnownLive(HTMLEditorRef())
.CreateNodeWithTransaction(*nsGkAtoms::div,
splitNodeResult.SplitPoint());
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -10675,10 +10707,11 @@ nsresult HTMLEditRules::PrepareToMakeElementAbsolutePosition(
"Failed to create current positioned div element"); "Failed to create current positioned div element");
*aTargetElement = curPositionedDiv; *aTargetElement = curPositionedDiv;
} }
EditorRawDOMPoint atEndOfCurPositionedDiv; EditorDOMPoint atEndOfCurPositionedDiv;
atEndOfCurPositionedDiv.SetToEndOf(curPositionedDiv); atEndOfCurPositionedDiv.SetToEndOf(curPositionedDiv);
curList = HTMLEditorRef().CreateNodeWithTransaction( curList = MOZ_KnownLive(HTMLEditorRef())
*containerName, atEndOfCurPositionedDiv); .CreateNodeWithTransaction(MOZ_KnownLive(*containerName),
atEndOfCurPositionedDiv);
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -10734,9 +10767,10 @@ nsresult HTMLEditRules::PrepareToMakeElementAbsolutePosition(
return splitNodeResult.Rv(); return splitNodeResult.Rv();
} }
if (!curPositionedDiv) { if (!curPositionedDiv) {
EditorRawDOMPoint atListItemParent(atListItem.GetContainer()); curPositionedDiv = MOZ_KnownLive(HTMLEditorRef())
curPositionedDiv = HTMLEditorRef().CreateNodeWithTransaction( .CreateNodeWithTransaction(
*nsGkAtoms::div, atListItemParent); *nsGkAtoms::div,
EditorDOMPoint(atListItem.GetContainer()));
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -10745,10 +10779,11 @@ nsresult HTMLEditRules::PrepareToMakeElementAbsolutePosition(
"Failed to create current positioned div element"); "Failed to create current positioned div element");
*aTargetElement = curPositionedDiv; *aTargetElement = curPositionedDiv;
} }
EditorRawDOMPoint atEndOfCurPositionedDiv; EditorDOMPoint atEndOfCurPositionedDiv;
atEndOfCurPositionedDiv.SetToEndOf(curPositionedDiv); atEndOfCurPositionedDiv.SetToEndOf(curPositionedDiv);
curList = HTMLEditorRef().CreateNodeWithTransaction( curList = MOZ_KnownLive(HTMLEditorRef())
*containerName, atEndOfCurPositionedDiv); .CreateNodeWithTransaction(MOZ_KnownLive(*containerName),
atEndOfCurPositionedDiv);
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -10783,7 +10818,8 @@ nsresult HTMLEditRules::PrepareToMakeElementAbsolutePosition(
if (NS_WARN_IF(splitNodeResult.Failed())) { if (NS_WARN_IF(splitNodeResult.Failed())) {
return splitNodeResult.Rv(); return splitNodeResult.Rv();
} }
curPositionedDiv = HTMLEditorRef().CreateNodeWithTransaction( curPositionedDiv = MOZ_KnownLive(HTMLEditorRef())
.CreateNodeWithTransaction(
*nsGkAtoms::div, splitNodeResult.SplitPoint()); *nsGkAtoms::div, splitNodeResult.SplitPoint());
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;

View File

@ -1087,10 +1087,9 @@ class HTMLEditRules : public TextEditRules {
* @return When succeeded, SplitPoint() returns * @return When succeeded, SplitPoint() returns
* the point to insert the element. * the point to insert the element.
*/ */
template <typename PT, typename CT>
MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE SplitNodeResult MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE SplitNodeResult
MaybeSplitAncestorsForInsertWithTransaction( MaybeSplitAncestorsForInsertWithTransaction(
nsAtom& aTag, const EditorDOMPointBase<PT, CT>& aStartOfDeepestRightNode); nsAtom& aTag, const EditorDOMPoint& aStartOfDeepestRightNode);
/** /**
* JoinNearestEditableNodesWithTransaction() joins two editable nodes which * JoinNearestEditableNodesWithTransaction() joins two editable nodes which

View File

@ -133,13 +133,6 @@ bool HTMLEditorPrefs::sUserWantsToEnableResizingUIByDefault = false;
bool HTMLEditorPrefs::sUserWantsToEnableInlineTableEditingUIByDefault = false; bool HTMLEditorPrefs::sUserWantsToEnableInlineTableEditingUIByDefault = false;
bool HTMLEditorPrefs::sUserWantsToEnableAbsolutePositioningUIByDefault = false; bool HTMLEditorPrefs::sUserWantsToEnableAbsolutePositioningUIByDefault = false;
template EditorDOMPoint HTMLEditor::InsertNodeIntoProperAncestorWithTransaction(
nsIContent& aNode, const EditorDOMPoint& aPointToInsert,
SplitAtEdges aSplitAtEdges);
template EditorDOMPoint HTMLEditor::InsertNodeIntoProperAncestorWithTransaction(
nsIContent& aNode, const EditorRawDOMPoint& aPointToInsert,
SplitAtEdges aSplitAtEdges);
HTMLEditor::HTMLEditor() HTMLEditor::HTMLEditor()
: mCRInParagraphCreatesParagraph(false), : mCRInParagraphCreatesParagraph(false),
mCSSAware(false), mCSSAware(false),
@ -1184,7 +1177,7 @@ nsresult HTMLEditor::InsertBrElementAtSelectionWithTransaction() {
} }
} }
EditorRawDOMPoint atStartOfSelection( EditorDOMPoint atStartOfSelection(
EditorBase::GetStartPoint(*SelectionRefPtr())); EditorBase::GetStartPoint(*SelectionRefPtr()));
if (NS_WARN_IF(!atStartOfSelection.IsSet())) { if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
@ -1298,7 +1291,7 @@ nsresult HTMLEditor::ReplaceHeadContentsWithSourceWithTransaction(
// Loop over the contents of the fragment and move into the document // Loop over the contents of the fragment and move into the document
while (nsCOMPtr<nsIContent> child = documentFragment->GetFirstChild()) { while (nsCOMPtr<nsIContent> child = documentFragment->GetFirstChild()) {
nsresult rv = InsertNodeWithTransaction( nsresult rv = InsertNodeWithTransaction(
*child, EditorRawDOMPoint(headNode, offsetOfNewNode++)); *child, EditorDOMPoint(headNode, offsetOfNewNode++));
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
return rv; return rv;
} }
@ -1611,7 +1604,7 @@ HTMLEditor::InsertElementAtSelection(Element* aElement, bool aDeleteSelection) {
if (SelectionRefPtr()->GetAnchorNode()) { if (SelectionRefPtr()->GetAnchorNode()) {
EditorRawDOMPoint atAnchor(SelectionRefPtr()->AnchorRef()); EditorRawDOMPoint atAnchor(SelectionRefPtr()->AnchorRef());
// Adjust position based on the node we are going to insert. // Adjust position based on the node we are going to insert.
EditorRawDOMPoint pointToInsert = EditorDOMPoint pointToInsert =
GetBetterInsertionPointFor(*aElement, atAnchor); GetBetterInsertionPointFor(*aElement, atAnchor);
if (NS_WARN_IF(!pointToInsert.IsSet())) { if (NS_WARN_IF(!pointToInsert.IsSet())) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
@ -1654,9 +1647,8 @@ HTMLEditor::InsertElementAtSelection(Element* aElement, bool aDeleteSelection) {
return NS_OK; return NS_OK;
} }
template <typename PT, typename CT>
EditorDOMPoint HTMLEditor::InsertNodeIntoProperAncestorWithTransaction( EditorDOMPoint HTMLEditor::InsertNodeIntoProperAncestorWithTransaction(
nsIContent& aNode, const EditorDOMPointBase<PT, CT>& aPointToInsert, nsIContent& aNode, const EditorDOMPoint& aPointToInsert,
SplitAtEdges aSplitAtEdges) { SplitAtEdges aSplitAtEdges) {
if (NS_WARN_IF(!aPointToInsert.IsSet())) { if (NS_WARN_IF(!aPointToInsert.IsSet())) {
return EditorDOMPoint(); return EditorDOMPoint();
@ -2120,7 +2112,7 @@ HTMLEditor::MakeOrChangeList(const nsAString& aListType, bool entireList,
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
EditorRawDOMPoint atStartOfSelection(firstRange->StartRef()); EditorDOMPoint atStartOfSelection(firstRange->StartRef());
if (NS_WARN_IF(!atStartOfSelection.IsSet()) || if (NS_WARN_IF(!atStartOfSelection.IsSet()) ||
NS_WARN_IF(!atStartOfSelection.GetContainerAsContent())) { NS_WARN_IF(!atStartOfSelection.GetContainerAsContent())) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
@ -2160,9 +2152,8 @@ HTMLEditor::MakeOrChangeList(const nsAString& aListType, bool entireList,
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
// make a list item // make a list item
EditorRawDOMPoint atStartOfNewList(newList, 0);
RefPtr<Element> newItem = RefPtr<Element> newItem =
CreateNodeWithTransaction(*nsGkAtoms::li, atStartOfNewList); CreateNodeWithTransaction(*nsGkAtoms::li, EditorDOMPoint(newList, 0));
if (NS_WARN_IF(!newItem)) { if (NS_WARN_IF(!newItem)) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
@ -2296,7 +2287,7 @@ nsresult HTMLEditor::InsertBasicBlockWithTransaction(nsAtom& aTagName) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
EditorRawDOMPoint atStartOfSelection(firstRange->StartRef()); EditorDOMPoint atStartOfSelection(firstRange->StartRef());
if (NS_WARN_IF(!atStartOfSelection.IsSet()) || if (NS_WARN_IF(!atStartOfSelection.IsSet()) ||
NS_WARN_IF(!atStartOfSelection.GetContainerAsContent())) { NS_WARN_IF(!atStartOfSelection.GetContainerAsContent())) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
@ -2436,7 +2427,7 @@ nsresult HTMLEditor::IndentOrOutdentAsSubAction(
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
EditorRawDOMPoint atStartOfSelection(firstRange->StartRef()); EditorDOMPoint atStartOfSelection(firstRange->StartRef());
if (NS_WARN_IF(!atStartOfSelection.IsSet()) || if (NS_WARN_IF(!atStartOfSelection.IsSet()) ||
NS_WARN_IF(!atStartOfSelection.GetContainerAsContent())) { NS_WARN_IF(!atStartOfSelection.GetContainerAsContent())) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
@ -3834,7 +3825,7 @@ nsresult HTMLEditor::RemoveBlockContainerWithTransaction(Element& aElement) {
!sibling->IsHTMLElement(nsGkAtoms::br) && !IsBlockNode(child)) { !sibling->IsHTMLElement(nsGkAtoms::br) && !IsBlockNode(child)) {
// Insert br node // Insert br node
RefPtr<Element> brElement = RefPtr<Element> brElement =
InsertBrElementWithTransaction(EditorRawDOMPoint(&aElement, 0)); InsertBrElementWithTransaction(EditorDOMPoint(&aElement, 0));
if (NS_WARN_IF(!brElement)) { if (NS_WARN_IF(!brElement)) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
@ -3852,7 +3843,7 @@ nsresult HTMLEditor::RemoveBlockContainerWithTransaction(Element& aElement) {
MOZ_ASSERT(child, "aNode has first editable child but not last?"); MOZ_ASSERT(child, "aNode has first editable child but not last?");
if (!IsBlockNode(child) && !child->IsHTMLElement(nsGkAtoms::br)) { if (!IsBlockNode(child) && !child->IsHTMLElement(nsGkAtoms::br)) {
// Insert br node // Insert br node
EditorRawDOMPoint endOfNode; EditorDOMPoint endOfNode;
endOfNode.SetToEndOf(&aElement); endOfNode.SetToEndOf(&aElement);
RefPtr<Element> brElement = InsertBrElementWithTransaction(endOfNode); RefPtr<Element> brElement = InsertBrElementWithTransaction(endOfNode);
if (NS_WARN_IF(!brElement)) { if (NS_WARN_IF(!brElement)) {
@ -3875,7 +3866,7 @@ nsresult HTMLEditor::RemoveBlockContainerWithTransaction(Element& aElement) {
!sibling->IsHTMLElement(nsGkAtoms::br)) { !sibling->IsHTMLElement(nsGkAtoms::br)) {
// Insert br node // Insert br node
RefPtr<Element> brElement = RefPtr<Element> brElement =
InsertBrElementWithTransaction(EditorRawDOMPoint(&aElement, 0)); InsertBrElementWithTransaction(EditorDOMPoint(&aElement, 0));
if (NS_WARN_IF(!brElement)) { if (NS_WARN_IF(!brElement)) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
@ -4562,9 +4553,8 @@ nsresult HTMLEditor::CopyLastEditableChildStylesWithTransaction(
nsAtom* tagName = elementInPreviousBlock->NodeInfo()->NameAtom(); nsAtom* tagName = elementInPreviousBlock->NodeInfo()->NameAtom();
// At first time, just create the most descendant inline container element. // At first time, just create the most descendant inline container element.
if (!firstClonsedElement) { if (!firstClonsedElement) {
EditorRawDOMPoint atStartOfNewBlock(newBlock, 0); firstClonsedElement = lastClonedElement = CreateNodeWithTransaction(
firstClonsedElement = lastClonedElement = MOZ_KnownLive(*tagName), EditorDOMPoint(newBlock, 0));
CreateNodeWithTransaction(*tagName, atStartOfNewBlock);
if (NS_WARN_IF(!firstClonsedElement)) { if (NS_WARN_IF(!firstClonsedElement)) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
@ -4591,7 +4581,7 @@ nsresult HTMLEditor::CopyLastEditableChildStylesWithTransaction(
} }
RefPtr<Element> brElement = RefPtr<Element> brElement =
InsertBrElementWithTransaction(EditorRawDOMPoint(firstClonsedElement, 0)); InsertBrElementWithTransaction(EditorDOMPoint(firstClonsedElement, 0));
if (NS_WARN_IF(!brElement)) { if (NS_WARN_IF(!brElement)) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }

View File

@ -525,6 +525,7 @@ class HTMLEditor final : public TextEditor,
* @param aOffset Start offset of removing text in aCharData. * @param aOffset Start offset of removing text in aCharData.
* @param aLength Length of removing text. * @param aLength Length of removing text.
*/ */
MOZ_CAN_RUN_SCRIPT
nsresult DeleteTextWithTransaction(dom::CharacterData& aTextNode, nsresult DeleteTextWithTransaction(dom::CharacterData& aTextNode,
uint32_t aOffset, uint32_t aLength); uint32_t aOffset, uint32_t aLength);
@ -1478,9 +1479,8 @@ class HTMLEditor final : public TextEditor,
* @return Returns inserted point if succeeded. * @return Returns inserted point if succeeded.
* Otherwise, the result is not set. * Otherwise, the result is not set.
*/ */
template <typename PT, typename CT>
MOZ_CAN_RUN_SCRIPT EditorDOMPoint InsertNodeIntoProperAncestorWithTransaction( MOZ_CAN_RUN_SCRIPT EditorDOMPoint InsertNodeIntoProperAncestorWithTransaction(
nsIContent& aNode, const EditorDOMPointBase<PT, CT>& aPointToInsert, nsIContent& aNode, const EditorDOMPoint& aPointToInsert,
SplitAtEdges aSplitAtEdges); SplitAtEdges aSplitAtEdges);
/** /**

View File

@ -646,7 +646,7 @@ nsresult HTMLEditor::DoInsertHTMLWithContext(
// in a BR that is not visible. If so, the code above just placed // in a BR that is not visible. If so, the code above just placed
// selection inside that. So I split it instead. // selection inside that. So I split it instead.
SplitNodeResult splitLinkResult = SplitNodeDeepWithTransaction( SplitNodeResult splitLinkResult = SplitNodeDeepWithTransaction(
*linkContent, EditorRawDOMPoint(selNode, selOffset), *linkContent, EditorDOMPoint(selNode, selOffset),
SplitAtEdges::eDoNotCreateEmptyContainer); SplitAtEdges::eDoNotCreateEmptyContainer);
NS_WARNING_ASSERTION(splitLinkResult.Succeeded(), NS_WARNING_ASSERTION(splitLinkResult.Succeeded(),
"Failed to split the link"); "Failed to split the link");

View File

@ -364,7 +364,7 @@ nsresult HTMLEditor::SetInlinePropertyOnTextNode(
nsCOMPtr<nsIContent> textNodeForTheRange = &aText; nsCOMPtr<nsIContent> textNodeForTheRange = &aText;
// Split at the end of the range. // Split at the end of the range.
EditorRawDOMPoint atEnd(textNodeForTheRange, aEndOffset); EditorDOMPoint atEnd(textNodeForTheRange, aEndOffset);
if (!atEnd.IsEndOfContainer()) { if (!atEnd.IsEndOfContainer()) {
// We need to split off back of text node // We need to split off back of text node
ErrorResult error; ErrorResult error;
@ -375,7 +375,7 @@ nsresult HTMLEditor::SetInlinePropertyOnTextNode(
} }
// Split at the start of the range. // Split at the start of the range.
EditorRawDOMPoint atStart(textNodeForTheRange, aStartOffset); EditorDOMPoint atStart(textNodeForTheRange, aStartOffset);
if (!atStart.IsStartOfContainer()) { if (!atStart.IsStartOfContainer()) {
// We need to split off front of text node // We need to split off front of text node
ErrorResult error; ErrorResult error;
@ -397,7 +397,7 @@ nsresult HTMLEditor::SetInlinePropertyOnTextNode(
if (IsSimpleModifiableNode(sibling, &aProperty, aAttribute, &aValue)) { if (IsSimpleModifiableNode(sibling, &aProperty, aAttribute, &aValue)) {
// Following sib is already right kind of inline node; slide this over // Following sib is already right kind of inline node; slide this over
return MoveNodeWithTransaction(*textNodeForTheRange, return MoveNodeWithTransaction(*textNodeForTheRange,
EditorRawDOMPoint(sibling, 0)); EditorDOMPoint(sibling, 0));
} }
} }
@ -453,7 +453,7 @@ nsresult HTMLEditor::SetInlinePropertyOnNodeImpl(nsIContent& aNode,
} }
if (IsSimpleModifiableNode(nextSibling, &aProperty, aAttribute, &aValue)) { if (IsSimpleModifiableNode(nextSibling, &aProperty, aAttribute, &aValue)) {
nsresult rv = nsresult rv =
MoveNodeWithTransaction(aNode, EditorRawDOMPoint(nextSibling, 0)); MoveNodeWithTransaction(aNode, EditorDOMPoint(nextSibling, 0));
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
return rv; return rv;
} }
@ -641,7 +641,7 @@ nsresult HTMLEditor::SplitStyleAbovePoint(
isSet) { isSet) {
// Found a style node we need to split // Found a style node we need to split
SplitNodeResult splitNodeResult = SplitNodeDeepWithTransaction( SplitNodeResult splitNodeResult = SplitNodeDeepWithTransaction(
*node, EditorRawDOMPoint(*aNode, *aOffset), *node, EditorDOMPoint(*aNode, *aOffset),
SplitAtEdges::eAllowToCreateEmptyContainer); SplitAtEdges::eAllowToCreateEmptyContainer);
NS_WARNING_ASSERTION(splitNodeResult.Succeeded(), NS_WARNING_ASSERTION(splitNodeResult.Succeeded(),
"Failed to split the node"); "Failed to split the node");
@ -732,8 +732,7 @@ nsresult HTMLEditor::ClearStyle(nsCOMPtr<nsINode>* aNode, int32_t* aOffset,
// leftNode. This is so we you don't revert back to the previous style // leftNode. This is so we you don't revert back to the previous style
// if you happen to click at the end of a line. // if you happen to click at the end of a line.
if (savedBR) { if (savedBR) {
rv = rv = MoveNodeWithTransaction(*savedBR, EditorDOMPoint(newSelParent, 0));
MoveNodeWithTransaction(*savedBR, EditorRawDOMPoint(newSelParent, 0));
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
return rv; return rv;
} }
@ -1641,7 +1640,7 @@ nsresult HTMLEditor::RelativeFontChangeOnTextNode(FontSize aDir,
nsCOMPtr<nsIContent> textNodeForTheRange = &aTextNode; nsCOMPtr<nsIContent> textNodeForTheRange = &aTextNode;
// Split at the end of the range. // Split at the end of the range.
EditorRawDOMPoint atEnd(textNodeForTheRange, aEndOffset); EditorDOMPoint atEnd(textNodeForTheRange, aEndOffset);
if (!atEnd.IsEndOfContainer()) { if (!atEnd.IsEndOfContainer()) {
// We need to split off back of text node // We need to split off back of text node
ErrorResult error; ErrorResult error;
@ -1652,7 +1651,7 @@ nsresult HTMLEditor::RelativeFontChangeOnTextNode(FontSize aDir,
} }
// Split at the start of the range. // Split at the start of the range.
EditorRawDOMPoint atStart(textNodeForTheRange, aStartOffset); EditorDOMPoint atStart(textNodeForTheRange, aStartOffset);
if (!atStart.IsStartOfContainer()) { if (!atStart.IsStartOfContainer()) {
// We need to split off front of text node // We need to split off front of text node
ErrorResult error; ErrorResult error;
@ -1678,7 +1677,7 @@ nsresult HTMLEditor::RelativeFontChangeOnTextNode(FontSize aDir,
if (sibling && sibling->IsHTMLElement(nodeType)) { if (sibling && sibling->IsHTMLElement(nodeType)) {
// Following sib is already right kind of inline node; slide this over // Following sib is already right kind of inline node; slide this over
nsresult rv = MoveNodeWithTransaction(*textNodeForTheRange, nsresult rv = MoveNodeWithTransaction(*textNodeForTheRange,
EditorRawDOMPoint(sibling, 0)); EditorDOMPoint(sibling, 0));
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
return rv; return rv;
} }
@ -1789,7 +1788,7 @@ nsresult HTMLEditor::RelativeFontChangeOnNode(int32_t aSizeChange,
if (sibling && sibling->IsHTMLElement(atom)) { if (sibling && sibling->IsHTMLElement(atom)) {
// following sib is already right kind of inline node; slide this over // following sib is already right kind of inline node; slide this over
// into it // into it
return MoveNodeWithTransaction(*aNode, EditorRawDOMPoint(sibling, 0)); return MoveNodeWithTransaction(*aNode, EditorDOMPoint(sibling, 0));
} }
// else insert it above aNode // else insert it above aNode

View File

@ -2799,8 +2799,8 @@ nsresult HTMLEditor::MergeCells(RefPtr<Element> aTargetCell,
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
return rv; return rv;
} }
rv = InsertNodeWithTransaction( rv = InsertNodeWithTransaction(*cellChild,
*cellChild, EditorRawDOMPoint(aTargetCell, insertIndex)); EditorDOMPoint(aTargetCell, insertIndex));
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
return rv; return rv;
} }

View File

@ -44,11 +44,6 @@ namespace mozilla {
using namespace dom; using namespace dom;
template CreateElementResult TextEditRules::CreateBRInternal(
const EditorDOMPoint& aPointToInsert, bool aCreateMozBR);
template CreateElementResult TextEditRules::CreateBRInternal(
const EditorRawDOMPoint& aPointToInsert, bool aCreateMozBR);
#define CANCEL_OPERATION_IF_READONLY_OR_DISABLED \ #define CANCEL_OPERATION_IF_READONLY_OR_DISABLED \
if (IsReadonly() || IsDisabled()) { \ if (IsReadonly() || IsDisabled()) { \
*aCancel = true; \ *aCancel = true; \
@ -982,7 +977,7 @@ nsresult TextEditRules::WillSetText(bool* aCancel, bool* aHandled,
} }
nsresult rv = MOZ_KnownLive(TextEditorRef()) nsresult rv = MOZ_KnownLive(TextEditorRef())
.InsertNodeWithTransaction( .InsertNodeWithTransaction(
*newNode, EditorRawDOMPoint(rootElement, 0)); *newNode, EditorDOMPoint(rootElement, 0));
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -1473,7 +1468,7 @@ nsresult TextEditRules::CreateTrailingBRIfNeeded() {
if (!lastChild->IsHTMLElement(nsGkAtoms::br)) { if (!lastChild->IsHTMLElement(nsGkAtoms::br)) {
AutoTransactionsConserveSelection dontChangeMySelection(TextEditorRef()); AutoTransactionsConserveSelection dontChangeMySelection(TextEditorRef());
EditorRawDOMPoint endOfRoot; EditorDOMPoint endOfRoot;
endOfRoot.SetToEndOf(rootElement); endOfRoot.SetToEndOf(rootElement);
CreateElementResult createMozBrResult = CreateMozBR(endOfRoot); CreateElementResult createMozBrResult = CreateMozBR(endOfRoot);
if (NS_WARN_IF(createMozBrResult.Failed())) { if (NS_WARN_IF(createMozBrResult.Failed())) {
@ -1558,8 +1553,8 @@ nsresult TextEditRules::CreateBogusNodeIfNeeded() {
// Put the node in the document. // Put the node in the document.
nsresult rv = MOZ_KnownLive(TextEditorRef()) nsresult rv = MOZ_KnownLive(TextEditorRef())
.InsertNodeWithTransaction( .InsertNodeWithTransaction(*newBrElement,
*newBrElement, EditorRawDOMPoint(rootElement, 0)); EditorDOMPoint(rootElement, 0));
if (NS_WARN_IF(!CanHandleEditAction())) { if (NS_WARN_IF(!CanHandleEditAction())) {
return NS_ERROR_EDITOR_DESTROYED; return NS_ERROR_EDITOR_DESTROYED;
} }
@ -1770,9 +1765,8 @@ void TextEditRules::FillBufWithPWChars(nsAString* aOutString, int32_t aLength) {
} }
} }
template <typename PT, typename CT>
CreateElementResult TextEditRules::CreateBRInternal( CreateElementResult TextEditRules::CreateBRInternal(
const EditorDOMPointBase<PT, CT>& aPointToInsert, bool aCreateMozBR) { const EditorDOMPoint& aPointToInsert, bool aCreateMozBR) {
MOZ_ASSERT(IsEditorDataAvailable()); MOZ_ASSERT(IsEditorDataAvailable());
if (NS_WARN_IF(!aPointToInsert.IsSet())) { if (NS_WARN_IF(!aPointToInsert.IsSet())) {

View File

@ -312,9 +312,8 @@ class TextEditRules : public nsITimerCallback, public nsINamed {
* @return Returns created <br> element or an error code * @return Returns created <br> element or an error code
* if couldn't create new <br> element. * if couldn't create new <br> element.
*/ */
template <typename PT, typename CT>
MOZ_CAN_RUN_SCRIPT CreateElementResult MOZ_CAN_RUN_SCRIPT CreateElementResult
CreateBR(const EditorDOMPointBase<PT, CT>& aPointToInsert) { CreateBR(const EditorDOMPoint& aPointToInsert) {
CreateElementResult ret = CreateBRInternal(aPointToInsert, false); CreateElementResult ret = CreateBRInternal(aPointToInsert, false);
#ifdef DEBUG #ifdef DEBUG
// If editor is destroyed, it must return NS_ERROR_EDITOR_DESTROYED. // If editor is destroyed, it must return NS_ERROR_EDITOR_DESTROYED.
@ -333,9 +332,8 @@ class TextEditRules : public nsITimerCallback, public nsINamed {
* @return Returns created <br> element or an error code * @return Returns created <br> element or an error code
* if couldn't create new <br> element. * if couldn't create new <br> element.
*/ */
template <typename PT, typename CT>
MOZ_CAN_RUN_SCRIPT CreateElementResult MOZ_CAN_RUN_SCRIPT CreateElementResult
CreateMozBR(const EditorDOMPointBase<PT, CT>& aPointToInsert) { CreateMozBR(const EditorDOMPoint& aPointToInsert) {
CreateElementResult ret = CreateBRInternal(aPointToInsert, true); CreateElementResult ret = CreateBRInternal(aPointToInsert, true);
#ifdef DEBUG #ifdef DEBUG
// If editor is destroyed, it must return NS_ERROR_EDITOR_DESTROYED. // If editor is destroyed, it must return NS_ERROR_EDITOR_DESTROYED.
@ -391,9 +389,8 @@ class TextEditRules : public nsITimerCallback, public nsINamed {
* @return Returns created <br> element and error code. * @return Returns created <br> element and error code.
* If it succeeded, never returns nullptr. * If it succeeded, never returns nullptr.
*/ */
template <typename PT, typename CT> MOZ_CAN_RUN_SCRIPT CreateElementResult
MOZ_CAN_RUN_SCRIPT CreateElementResult CreateBRInternal( CreateBRInternal(const EditorDOMPoint& aPointToInsert, bool aCreateMozBR);
const EditorDOMPointBase<PT, CT>& aPointToInsert, bool aCreateMozBR);
protected: protected:
/** /**

View File

@ -64,11 +64,6 @@ namespace mozilla {
using namespace dom; using namespace dom;
template already_AddRefed<Element> TextEditor::InsertBrElementWithTransaction(
const EditorDOMPoint& aPointToInsert, EDirection aSelect);
template already_AddRefed<Element> TextEditor::InsertBrElementWithTransaction(
const EditorRawDOMPoint& aPointToInsert, EDirection aSelect);
TextEditor::TextEditor() TextEditor::TextEditor()
: mWrapColumn(0), : mWrapColumn(0),
mMaxTextLength(-1), mMaxTextLength(-1),
@ -242,9 +237,8 @@ TextEditor::SetDocumentCharacterSet(const nsACString& characterSet) {
} }
// Create a new meta charset tag // Create a new meta charset tag
EditorRawDOMPoint atStartOfHeadNode(headNode, 0);
RefPtr<Element> metaElement = RefPtr<Element> metaElement =
CreateNodeWithTransaction(*nsGkAtoms::meta, atStartOfHeadNode); CreateNodeWithTransaction(*nsGkAtoms::meta, EditorDOMPoint(headNode, 0));
if (NS_WARN_IF(!metaElement)) { if (NS_WARN_IF(!metaElement)) {
return NS_OK; return NS_OK;
} }
@ -436,10 +430,8 @@ nsresult TextEditor::InsertLineBreakAsAction() {
return NS_OK; return NS_OK;
} }
template <typename PT, typename CT>
already_AddRefed<Element> TextEditor::InsertBrElementWithTransaction( already_AddRefed<Element> TextEditor::InsertBrElementWithTransaction(
const EditorDOMPointBase<PT, CT>& aPointToInsert, const EditorDOMPoint& aPointToInsert, EDirection aSelect /* = eNone */) {
EDirection aSelect /* = eNone */) {
MOZ_ASSERT(IsEditActionDataAvailable()); MOZ_ASSERT(IsEditActionDataAvailable());
if (NS_WARN_IF(!aPointToInsert.IsSet())) { if (NS_WARN_IF(!aPointToInsert.IsSet())) {
@ -869,7 +861,7 @@ already_AddRefed<Element> TextEditor::DeleteSelectionAndCreateElement(
return nullptr; return nullptr;
} }
EditorRawDOMPoint pointToInsert(SelectionRefPtr()->AnchorRef()); EditorDOMPoint pointToInsert(SelectionRefPtr()->AnchorRef());
if (!pointToInsert.IsSet()) { if (!pointToInsert.IsSet()) {
return nullptr; return nullptr;
} }

View File

@ -354,10 +354,8 @@ class TextEditor : public EditorBase, public nsIPlaintextEditor {
* @return The new <br> node. If failed to create new * @return The new <br> node. If failed to create new
* <br> node, returns nullptr. * <br> node, returns nullptr.
*/ */
template <typename PT, typename CT>
MOZ_CAN_RUN_SCRIPT already_AddRefed<Element> InsertBrElementWithTransaction( MOZ_CAN_RUN_SCRIPT already_AddRefed<Element> InsertBrElementWithTransaction(
const EditorDOMPointBase<PT, CT>& aPointToInsert, const EditorDOMPoint& aPointToInsert, EDirection aSelect = eNone);
EDirection aSelect = eNone);
/** /**
* Extends the selection for given deletion operation * Extends the selection for given deletion operation

View File

@ -55,12 +55,6 @@ template void WSRunObject::NextVisibleNode(const EditorRawDOMPoint& aPoint,
nsCOMPtr<nsINode>* outVisNode, nsCOMPtr<nsINode>* outVisNode,
int32_t* outVisOffset, int32_t* outVisOffset,
WSType* outType) const; WSType* outType) const;
template already_AddRefed<Element> WSRunObject::InsertBreak(
Selection& aSelection, const EditorDOMPoint& aPointToInsert,
nsIEditor::EDirection aSelect);
template already_AddRefed<Element> WSRunObject::InsertBreak(
Selection& aSelection, const EditorRawDOMPoint& aPointToInsert,
nsIEditor::EDirection aSelect);
template void WSRunObject::GetASCIIWhitespacesBounds( template void WSRunObject::GetASCIIWhitespacesBounds(
int16_t aDir, const EditorDOMPoint& aPoint, dom::Text** outStartNode, int16_t aDir, const EditorDOMPoint& aPoint, dom::Text** outStartNode,
int32_t* outStartOffset, dom::Text** outEndNode, int32_t* outEndOffset); int32_t* outStartOffset, dom::Text** outEndNode, int32_t* outEndOffset);
@ -171,9 +165,8 @@ nsresult WSRunObject::PrepareToSplitAcrossBlocks(HTMLEditor* aHTMLEditor,
return wsObj.PrepareToSplitAcrossBlocksPriv(); return wsObj.PrepareToSplitAcrossBlocksPriv();
} }
template <typename PT, typename CT>
already_AddRefed<Element> WSRunObject::InsertBreak( already_AddRefed<Element> WSRunObject::InsertBreak(
Selection& aSelection, const EditorDOMPointBase<PT, CT>& aPointToInsert, Selection& aSelection, const EditorDOMPoint& aPointToInsert,
nsIEditor::EDirection aSelect) { nsIEditor::EDirection aSelect) {
if (NS_WARN_IF(!aPointToInsert.IsSet())) { if (NS_WARN_IF(!aPointToInsert.IsSet())) {
return nullptr; return nullptr;
@ -393,8 +386,8 @@ nsresult WSRunObject::DeleteWSBackward() {
// Easy case, preformatted ws. // Easy case, preformatted ws.
if (mPRE && (nsCRT::IsAsciiSpace(point.mChar) || point.mChar == kNBSP)) { if (mPRE && (nsCRT::IsAsciiSpace(point.mChar) || point.mChar == kNBSP)) {
nsresult rv = nsresult rv =
DeleteRange(EditorRawDOMPoint(point.mTextNode, point.mOffset), DeleteRange(EditorDOMPoint(point.mTextNode, point.mOffset),
EditorRawDOMPoint(point.mTextNode, point.mOffset + 1)); EditorDOMPoint(point.mTextNode, point.mOffset + 1));
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
return rv; return rv;
} }
@ -420,8 +413,8 @@ nsresult WSRunObject::DeleteWSBackward() {
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
// finally, delete that ws // finally, delete that ws
rv = DeleteRange(EditorRawDOMPoint(startNode, startOffset), rv = DeleteRange(EditorDOMPoint(startNode, startOffset),
EditorRawDOMPoint(endNode, endOffset)); EditorDOMPoint(endNode, endOffset));
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
return rv; return rv;
} }
@ -439,8 +432,8 @@ nsresult WSRunObject::DeleteWSBackward() {
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
// finally, delete that ws // finally, delete that ws
rv = DeleteRange(EditorRawDOMPoint(node, startOffset), rv = DeleteRange(EditorDOMPoint(node, startOffset),
EditorRawDOMPoint(node, endOffset)); EditorDOMPoint(node, endOffset));
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
return rv; return rv;
} }
@ -457,8 +450,8 @@ nsresult WSRunObject::DeleteWSForward() {
// Easy case, preformatted ws. // Easy case, preformatted ws.
if (mPRE && (nsCRT::IsAsciiSpace(point.mChar) || point.mChar == kNBSP)) { if (mPRE && (nsCRT::IsAsciiSpace(point.mChar) || point.mChar == kNBSP)) {
nsresult rv = nsresult rv =
DeleteRange(EditorRawDOMPoint(point.mTextNode, point.mOffset), DeleteRange(EditorDOMPoint(point.mTextNode, point.mOffset),
EditorRawDOMPoint(point.mTextNode, point.mOffset + 1)); EditorDOMPoint(point.mTextNode, point.mOffset + 1));
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
return rv; return rv;
} }
@ -483,8 +476,8 @@ nsresult WSRunObject::DeleteWSForward() {
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
// Finally, delete that ws // Finally, delete that ws
rv = DeleteRange(EditorRawDOMPoint(startNode, startOffset), rv = DeleteRange(EditorDOMPoint(startNode, startOffset),
EditorRawDOMPoint(endNode, endOffset)); EditorDOMPoint(endNode, endOffset));
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
return rv; return rv;
} }
@ -502,8 +495,8 @@ nsresult WSRunObject::DeleteWSForward() {
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
// Finally, delete that ws // Finally, delete that ws
rv = DeleteRange(EditorRawDOMPoint(node, startOffset), rv = DeleteRange(EditorDOMPoint(node, startOffset),
EditorRawDOMPoint(node, endOffset)); EditorDOMPoint(node, endOffset));
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
return rv; return rv;
} }
@ -1302,10 +1295,8 @@ nsresult WSRunObject::PrepareToSplitAcrossBlocksPriv() {
return NS_OK; return NS_OK;
} }
template <typename PT1, typename CT1, typename PT2, typename CT2> nsresult WSRunObject::DeleteRange(const EditorDOMPoint& aStartPoint,
nsresult WSRunObject::DeleteRange( const EditorDOMPoint& aEndPoint) {
const EditorDOMPointBase<PT1, CT1>& aStartPoint,
const EditorDOMPointBase<PT2, CT2>& aEndPoint) {
if (NS_WARN_IF(!aStartPoint.IsSet()) || NS_WARN_IF(!aEndPoint.IsSet())) { if (NS_WARN_IF(!aStartPoint.IsSet()) || NS_WARN_IF(!aEndPoint.IsSet())) {
return NS_ERROR_INVALID_ARG; return NS_ERROR_INVALID_ARG;
} }
@ -1325,8 +1316,9 @@ nsresult WSRunObject::DeleteRange(
if (aStartPoint.GetContainer() == aEndPoint.GetContainer() && if (aStartPoint.GetContainer() == aEndPoint.GetContainer() &&
aStartPoint.IsInTextNode()) { aStartPoint.IsInTextNode()) {
RefPtr<Text> textNode = aStartPoint.GetContainerAsText();
return htmlEditor->DeleteTextWithTransaction( return htmlEditor->DeleteTextWithTransaction(
*aStartPoint.GetContainerAsText(), aStartPoint.Offset(), *textNode, aStartPoint.Offset(),
aEndPoint.Offset() - aStartPoint.Offset()); aEndPoint.Offset() - aStartPoint.Offset());
} }
@ -1883,9 +1875,8 @@ nsresult WSRunObject::CheckTrailingNBSPOfRun(WSFragment* aRun) {
return NS_OK; return NS_OK;
} }
template <typename PT, typename CT>
nsresult WSRunObject::ReplacePreviousNBSPIfUnncessary( nsresult WSRunObject::ReplacePreviousNBSPIfUnncessary(
WSFragment* aRun, const EditorDOMPointBase<PT, CT>& aPoint) { WSFragment* aRun, const EditorDOMPoint& aPoint) {
if (NS_WARN_IF(!aRun) || NS_WARN_IF(!aPoint.IsSet())) { if (NS_WARN_IF(!aRun) || NS_WARN_IF(!aPoint.IsSet())) {
return NS_ERROR_INVALID_ARG; return NS_ERROR_INVALID_ARG;
} }

View File

@ -240,9 +240,8 @@ class MOZ_STACK_CLASS WSRunObject final {
* @return The new <br> node. If failed to create new <br> * @return The new <br> node. If failed to create new <br>
* node, returns nullptr. * node, returns nullptr.
*/ */
template <typename PT, typename CT>
MOZ_CAN_RUN_SCRIPT already_AddRefed<dom::Element> InsertBreak( MOZ_CAN_RUN_SCRIPT already_AddRefed<dom::Element> InsertBreak(
Selection& aSelection, const EditorDOMPointBase<PT, CT>& aPointToInsert, Selection& aSelection, const EditorDOMPoint& aPointToInsert,
nsIEditor::EDirection aSelect); nsIEditor::EDirection aSelect);
/** /**
@ -387,10 +386,8 @@ class MOZ_STACK_CLASS WSRunObject final {
* When aEndPoint is in a text node, removes the text data before the point. * When aEndPoint is in a text node, removes the text data before the point.
* Removes any nodes between them. * Removes any nodes between them.
*/ */
template <typename PT1, typename CT1, typename PT2, typename CT2> MOZ_CAN_RUN_SCRIPT nsresult DeleteRange(const EditorDOMPoint& aStartPoint,
MOZ_CAN_RUN_SCRIPT nsresult const EditorDOMPoint& aEndPoint);
DeleteRange(const EditorDOMPointBase<PT1, CT1>& aStartPoint,
const EditorDOMPointBase<PT2, CT2>& aEndPoint);
/** /**
* GetNextCharPoint() returns next character's point of aPoint. If there is * GetNextCharPoint() returns next character's point of aPoint. If there is
@ -492,9 +489,8 @@ class MOZ_STACK_CLASS WSRunObject final {
* @param aPoint Current insertion point. Its previous character is * @param aPoint Current insertion point. Its previous character is
* unnecessary NBSP will be checked. * unnecessary NBSP will be checked.
*/ */
template <typename PT, typename CT>
MOZ_CAN_RUN_SCRIPT nsresult ReplacePreviousNBSPIfUnncessary( MOZ_CAN_RUN_SCRIPT nsresult ReplacePreviousNBSPIfUnncessary(
WSFragment* aRun, const EditorDOMPointBase<PT, CT>& aPoint); WSFragment* aRun, const EditorDOMPoint& aPoint);
MOZ_CAN_RUN_SCRIPT MOZ_CAN_RUN_SCRIPT
nsresult CheckLeadingNBSP(WSFragment* aRun, nsINode* aNode, int32_t aOffset); nsresult CheckLeadingNBSP(WSFragment* aRun, nsINode* aNode, int32_t aOffset);
@ -502,11 +498,11 @@ class MOZ_STACK_CLASS WSRunObject final {
MOZ_CAN_RUN_SCRIPT nsresult Scrub(); MOZ_CAN_RUN_SCRIPT nsresult Scrub();
bool IsBlockNode(nsINode* aNode); bool IsBlockNode(nsINode* aNode);
EditorRawDOMPoint StartPoint() const { EditorDOMPoint StartPoint() const {
return EditorRawDOMPoint(mStartNode, mStartOffset); return EditorDOMPoint(mStartNode, mStartOffset);
} }
EditorRawDOMPoint EndPoint() const { EditorDOMPoint EndPoint() const {
return EditorRawDOMPoint(mEndNode, mEndOffset); return EditorDOMPoint(mEndNode, mEndOffset);
} }
// The node passed to our constructor. // The node passed to our constructor.

View File

@ -12,6 +12,7 @@ support-files =
en-AU/en_AU.aff en-AU/en_AU.aff
de-DE/de_DE.dic de-DE/de_DE.dic
de-DE/de_DE.aff de-DE/de_DE.aff
!/editor/libeditor/tests/spellcheck.js
[test_async_UpdateCurrentDictionary.html] [test_async_UpdateCurrentDictionary.html]
[test_bug678842.html] [test_bug678842.html]
@ -24,3 +25,4 @@ support-files =
[test_bug1219928.html] [test_bug1219928.html]
skip-if = e10s skip-if = e10s
[test_bug1365383.html] [test_bug1365383.html]
[test_bug1418629.html]

View File

@ -0,0 +1,96 @@
<!DOCTYPE html>
<html>
<head>
<title>Mozilla bug 1418629</title>
<link rel=stylesheet href="/tests/SimpleTest/test.css">
<script src="/tests/SimpleTest/EventUtils.js"></script>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/AddTask.js"></script>
<script src="/tests/editor/libeditor/tests/spellcheck.js"></script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1418629">Mozilla Bug 1418629</a>
<p id="display"></p>
<div id="content" style="display: none;">
</div>
<input id="input1" autofocus spellcheck="true">
<script>
const {onSpellCheck} = SpecialPowers.Cu.import("resource://testing-common/AsyncSpellCheckTestHelper.jsm", {});
SimpleTest.waitForExplicitFinish();
add_task(async function() {
await new Promise((resolve) => {
SimpleTest.waitForFocus(() => {
SimpleTest.executeSoon(resolve);
}, window);
});
let misspeltWords = [];
let input = document.getElementById("input1");
input.focus();
input.value = "";
synthesizeKey("d");
synthesizeKey("o");
synthesizeKey("e");
synthesizeKey("s");
await new Promise((resolve) => { onSpellCheck(input, resolve); });
// isSpellingCheckOk is defined in spellcheck.js
// eslint-disable-next-line no-undef
ok(isSpellingCheckOk(SpecialPowers.wrap(input).editor, misspeltWords),
"no misspelt words");
synthesizeKey("n");
synthesizeKey("\'");
is(input.value, "doesn\'", "");
await new Promise((resolve) => { onSpellCheck(input, resolve); });
// isSpellingCheckOk is defined in spellcheck.js
// eslint-disable-next-line no-undef
ok(isSpellingCheckOk(SpecialPowers.wrap(input).editor, misspeltWords),
"don't run spellchecker during inputting word");
synthesizeKey(" ");
is(input.value, "doesn\' ", "");
await new Promise((resolve) => { onSpellCheck(input, resolve); });
misspeltWords.push("doesn\'");
// isSpellingCheckOk is defined in spellcheck.js
// eslint-disable-next-line no-undef
ok(isSpellingCheckOk(SpecialPowers.wrap(input).editor, misspeltWords),
"should run spellchecker");
});
async function test_with_twice_characters(ch) {
let misspeltWords = [];
let input = document.getElementById("input1");
input.focus();
input.value = "";
synthesizeKey("d");
synthesizeKey("o");
synthesizeKey("e");
synthesizeKey("s");
synthesizeKey("n");
synthesizeKey(ch);
synthesizeKey(ch);
is(input.value, "doesn" + ch + ch, "");
await new Promise((resolve) => { onSpellCheck(input, resolve); });
misspeltWords.push("doesn");
// isSpellingCheckOk is defined in spellcheck.js
// eslint-disable-next-line no-undef
ok(isSpellingCheckOk(SpecialPowers.wrap(input).editor, misspeltWords),
"should run spellchecker");
}
add_task(test_with_twice_characters.bind(null, "\'"));
add_task(test_with_twice_characters.bind(null, String.fromCharCode(0x2019)));
</script>
</body>
</html>

View File

@ -418,8 +418,19 @@ CharClass WordSplitState::ClassifyCharacter(int32_t aIndex,
if (mDOMWordText[aIndex - 1] == '.') return CHAR_CLASS_SEPARATOR; if (mDOMWordText[aIndex - 1] == '.') return CHAR_CLASS_SEPARATOR;
// now we know left char is a word-char, check the right-hand character // now we know left char is a word-char, check the right-hand character
if (aIndex == int32_t(mDOMWordText.Length()) - 1) if (aIndex == int32_t(mDOMWordText.Length() - 1)) {
if (mDOMWordText[aIndex] == '\'' || mDOMWordText[aIndex] == 0x2019) {
nsUGenCategory prevCategory =
mozilla::unicode::GetGenCategory(mDOMWordText[aIndex - 1]);
if (prevCategory == nsUGenCategory::kLetter ||
prevCategory == nsUGenCategory::kNumber) {
// If single quotation mark is last, we don't return separator yet.
return CHAR_CLASS_WORD;
}
}
return CHAR_CLASS_SEPARATOR; return CHAR_CLASS_SEPARATOR;
}
if (ClassifyCharacter(aIndex + 1, false) != CHAR_CLASS_WORD) if (ClassifyCharacter(aIndex + 1, false) != CHAR_CLASS_WORD)
return CHAR_CLASS_SEPARATOR; return CHAR_CLASS_SEPARATOR;
// If the next charatcer is a word-char, make sure that it's not a // If the next charatcer is a word-char, make sure that it's not a

View File

@ -15,7 +15,6 @@
#include "mozilla/FloatingPoint.h" #include "mozilla/FloatingPoint.h"
#include "mozilla/Maybe.h" #include "mozilla/Maybe.h"
#include "mozilla/PodOperations.h" #include "mozilla/PodOperations.h"
#include "mozilla/ReverseIterator.h"
#include "mozilla/Sprintf.h" #include "mozilla/Sprintf.h"
#include "mozilla/Variant.h" #include "mozilla/Variant.h"
@ -91,24 +90,6 @@ static bool ParseNodeRequiresSpecialLineNumberNotes(ParseNode* pn) {
kind == ParseNodeKind::Function; kind == ParseNodeKind::Function;
} }
BytecodeEmitter::BytecodeSection::BytecodeSection(JSContext* cx,
uint32_t lineNum)
: code_(cx),
notes_(cx),
tryNoteList_(cx),
scopeNoteList_(cx),
resumeOffsetList_(cx),
currentLine_(lineNum) {}
BytecodeEmitter::PerScriptData::PerScriptData(JSContext* cx)
: scopeList_(cx),
numberList_(cx),
atomIndices_(cx->frontendCollectionPool()) {}
bool BytecodeEmitter::PerScriptData::init(JSContext* cx) {
return atomIndices_.acquire(cx);
}
BytecodeEmitter::BytecodeEmitter( BytecodeEmitter::BytecodeEmitter(
BytecodeEmitter* parent, SharedContext* sc, HandleScript script, BytecodeEmitter* parent, SharedContext* sc, HandleScript script,
Handle<LazyScript*> lazyScript, uint32_t lineNum, EmitterMode emitterMode, Handle<LazyScript*> lazyScript, uint32_t lineNum, EmitterMode emitterMode,
@ -260,21 +241,6 @@ bool BytecodeEmitter::emitCheck(JSOp op, ptrdiff_t delta, ptrdiff_t* offset) {
return true; return true;
} }
void BytecodeEmitter::BytecodeSection::updateDepth(ptrdiff_t target) {
jsbytecode* pc = code(target);
int nuses = StackUses(pc);
int ndefs = StackDefs(pc);
stackDepth_ -= nuses;
MOZ_ASSERT(stackDepth_ >= 0);
stackDepth_ += ndefs;
if ((uint32_t)stackDepth_ > maxStackDepth_) {
maxStackDepth_ = stackDepth_;
}
}
#ifdef DEBUG #ifdef DEBUG
bool BytecodeEmitter::checkStrictOrSloppy(JSOp op) { bool BytecodeEmitter::checkStrictOrSloppy(JSOp op) {
if (IsCheckStrictOp(op) && !sc->strict()) { if (IsCheckStrictOp(op) && !sc->strict()) {
@ -9562,127 +9528,6 @@ void BytecodeEmitter::copySrcNotes(jssrcnote* destination, uint32_t nsrcnotes) {
SN_MAKE_TERMINATOR(&destination[count]); SN_MAKE_TERMINATOR(&destination[count]);
} }
void CGNumberList::finish(mozilla::Span<GCPtrValue> array) {
MOZ_ASSERT(length() == array.size());
for (unsigned i = 0; i < length(); i++) {
array[i].init(vector[i]);
}
}
/*
* Find the index of the given object for code generator.
*
* Since the emitter refers to each parsed object only once, for the index we
* use the number of already indexed objects. We also add the object to a list
* to convert the list to a fixed-size array when we complete code generation,
* see js::CGObjectList::finish below.
*/
unsigned CGObjectList::add(ObjectBox* objbox) {
MOZ_ASSERT(objbox->isObjectBox());
MOZ_ASSERT(!objbox->emitLink);
objbox->emitLink = lastbox;
lastbox = objbox;
return length++;
}
void CGObjectList::finish(mozilla::Span<GCPtrObject> array) {
MOZ_ASSERT(length <= INDEX_LIMIT);
MOZ_ASSERT(length == array.size());
ObjectBox* objbox = lastbox;
for (GCPtrObject& obj : mozilla::Reversed(array)) {
MOZ_ASSERT(obj == nullptr);
MOZ_ASSERT(objbox->object()->isTenured());
obj.init(objbox->object());
objbox = objbox->emitLink;
}
}
void CGObjectList::finishInnerFunctions() {
ObjectBox* objbox = lastbox;
while (objbox) {
if (objbox->isFunctionBox()) {
objbox->asFunctionBox()->finish();
}
objbox = objbox->emitLink;
}
}
void CGScopeList::finish(mozilla::Span<GCPtrScope> array) {
MOZ_ASSERT(length() <= INDEX_LIMIT);
MOZ_ASSERT(length() == array.size());
for (uint32_t i = 0; i < length(); i++) {
array[i].init(vector[i]);
}
}
bool CGTryNoteList::append(JSTryNoteKind kind, uint32_t stackDepth,
size_t start, size_t end) {
MOZ_ASSERT(start <= end);
MOZ_ASSERT(size_t(uint32_t(start)) == start);
MOZ_ASSERT(size_t(uint32_t(end)) == end);
// Offsets are given relative to sections, but we only expect main-section
// to have TryNotes. In finish() we will fixup base offset.
JSTryNote note;
note.kind = kind;
note.stackDepth = stackDepth;
note.start = uint32_t(start);
note.length = uint32_t(end - start);
return list.append(note);
}
void CGTryNoteList::finish(mozilla::Span<JSTryNote> array) {
MOZ_ASSERT(length() == array.size());
for (unsigned i = 0; i < length(); i++) {
array[i] = list[i];
}
}
bool CGScopeNoteList::append(uint32_t scopeIndex, uint32_t offset,
uint32_t parent) {
CGScopeNote note;
mozilla::PodZero(&note);
// Offsets are given relative to sections. In finish() we will fixup base
// offset if needed.
note.index = scopeIndex;
note.start = offset;
note.parent = parent;
return list.append(note);
}
void CGScopeNoteList::recordEnd(uint32_t index, uint32_t offset) {
MOZ_ASSERT(index < length());
MOZ_ASSERT(list[index].length == 0);
list[index].end = offset;
}
void CGScopeNoteList::finish(mozilla::Span<ScopeNote> array) {
MOZ_ASSERT(length() == array.size());
for (unsigned i = 0; i < length(); i++) {
MOZ_ASSERT(list[i].end >= list[i].start);
list[i].length = list[i].end - list[i].start;
array[i] = list[i];
}
}
void CGResumeOffsetList::finish(mozilla::Span<uint32_t> array) {
MOZ_ASSERT(length() == array.size());
for (unsigned i = 0; i < length(); i++) {
array[i] = list[i];
}
}
const JSSrcNoteSpec js_SrcNoteSpec[] = { const JSSrcNoteSpec js_SrcNoteSpec[] = {
#define DEFINE_SRC_NOTE_SPEC(sym, name, arity) {name, arity}, #define DEFINE_SRC_NOTE_SPEC(sym, name, arity) {name, arity},
FOR_EACH_SRC_NOTE_TYPE(DEFINE_SRC_NOTE_SPEC) FOR_EACH_SRC_NOTE_TYPE(DEFINE_SRC_NOTE_SPEC)

View File

@ -14,6 +14,7 @@
#include "ds/InlineTable.h" #include "ds/InlineTable.h"
#include "frontend/BCEParserHandle.h" #include "frontend/BCEParserHandle.h"
#include "frontend/BytecodeSection.h" // BytecodeSection, PerScriptData
#include "frontend/DestructuringFlavor.h" #include "frontend/DestructuringFlavor.h"
#include "frontend/EitherParser.h" #include "frontend/EitherParser.h"
#include "frontend/JumpList.h" #include "frontend/JumpList.h"
@ -29,80 +30,6 @@
namespace js { namespace js {
namespace frontend { namespace frontend {
class CGNumberList {
Rooted<ValueVector> vector;
public:
explicit CGNumberList(JSContext* cx) : vector(cx, ValueVector(cx)) {}
MOZ_MUST_USE bool append(const Value& v) { return vector.append(v); }
size_t length() const { return vector.length(); }
void finish(mozilla::Span<GCPtrValue> array);
};
struct CGObjectList {
uint32_t length; /* number of emitted so far objects */
ObjectBox* lastbox; /* last emitted object */
CGObjectList() : length(0), lastbox(nullptr) {}
unsigned add(ObjectBox* objbox);
void finish(mozilla::Span<GCPtrObject> array);
void finishInnerFunctions();
};
struct MOZ_STACK_CLASS CGScopeList {
Rooted<GCVector<Scope*>> vector;
explicit CGScopeList(JSContext* cx) : vector(cx, GCVector<Scope*>(cx)) {}
bool append(Scope* scope) { return vector.append(scope); }
uint32_t length() const { return vector.length(); }
void finish(mozilla::Span<GCPtrScope> array);
};
struct CGTryNoteList {
Vector<JSTryNote> list;
explicit CGTryNoteList(JSContext* cx) : list(cx) {}
MOZ_MUST_USE bool append(JSTryNoteKind kind, uint32_t stackDepth,
size_t start, size_t end);
size_t length() const { return list.length(); }
void finish(mozilla::Span<JSTryNote> array);
};
struct CGScopeNote : public ScopeNote {
// The end offset. Used to compute the length.
uint32_t end;
};
struct CGScopeNoteList {
Vector<CGScopeNote> list;
explicit CGScopeNoteList(JSContext* cx) : list(cx) {}
MOZ_MUST_USE bool append(uint32_t scopeIndex, uint32_t offset,
uint32_t parent);
void recordEnd(uint32_t index, uint32_t offse);
size_t length() const { return list.length(); }
void finish(mozilla::Span<ScopeNote> array);
};
struct CGResumeOffsetList {
Vector<uint32_t> list;
explicit CGResumeOffsetList(JSContext* cx) : list(cx) {}
MOZ_MUST_USE bool append(uint32_t offset) { return list.append(offset); }
size_t length() const { return list.length(); }
void finish(mozilla::Span<uint32_t> array);
};
static constexpr size_t MaxBytecodeLength = INT32_MAX;
static constexpr size_t MaxSrcNotesLength = INT32_MAX;
// Have a few inline elements, so as to avoid heap allocation for tiny
// sequences. See bug 1390526.
typedef Vector<jsbytecode, 64> BytecodeVector;
typedef Vector<jssrcnote, 64> SrcNotesVector;
class CallOrNewEmitter; class CallOrNewEmitter;
class ElemOpEmitter; class ElemOpEmitter;
class EmitterScope; class EmitterScope;
@ -127,198 +54,6 @@ struct MOZ_STACK_CLASS BytecodeEmitter {
Rooted<LazyScript*> lazyScript; Rooted<LazyScript*> lazyScript;
private: private:
// Bytecode and all data directly associated with specific opcode/index inside
// bytecode is stored in this class.
class BytecodeSection {
public:
BytecodeSection(JSContext* cx, uint32_t lineNum);
// ---- Bytecode ----
BytecodeVector& code() { return code_; }
const BytecodeVector& code() const { return code_; }
jsbytecode* code(ptrdiff_t offset) { return code_.begin() + offset; }
ptrdiff_t offset() const { return code_.end() - code_.begin(); }
// ---- Source notes ----
SrcNotesVector& notes() { return notes_; }
const SrcNotesVector& notes() const { return notes_; }
ptrdiff_t lastNoteOffset() const { return lastNoteOffset_; }
void setLastNoteOffset(ptrdiff_t offset) { lastNoteOffset_ = offset; }
// ---- Jump ----
ptrdiff_t lastTargetOffset() const { return lastTarget_.offset; }
void setLastTargetOffset(ptrdiff_t offset) { lastTarget_.offset = offset; }
// Check if the last emitted opcode is a jump target.
bool lastOpcodeIsJumpTarget() const {
return offset() - lastTarget_.offset == ptrdiff_t(JSOP_JUMPTARGET_LENGTH);
}
// JumpTarget should not be part of the emitted statement, as they can be
// aliased by multiple statements. If we included the jump target as part of
// the statement we might have issues where the enclosing statement might
// not contain all the opcodes of the enclosed statements.
ptrdiff_t lastNonJumpTargetOffset() const {
return lastOpcodeIsJumpTarget() ? lastTarget_.offset : offset();
}
// ---- Stack ----
int32_t stackDepth() const { return stackDepth_; }
void setStackDepth(int32_t depth) { stackDepth_ = depth; }
uint32_t maxStackDepth() const { return maxStackDepth_; }
void updateDepth(ptrdiff_t target);
// ---- Try notes ----
CGTryNoteList& tryNoteList() { return tryNoteList_; };
const CGTryNoteList& tryNoteList() const { return tryNoteList_; };
// ---- Scope ----
CGScopeNoteList& scopeNoteList() { return scopeNoteList_; };
const CGScopeNoteList& scopeNoteList() const { return scopeNoteList_; };
// ---- Generator ----
CGResumeOffsetList& resumeOffsetList() { return resumeOffsetList_; }
const CGResumeOffsetList& resumeOffsetList() const {
return resumeOffsetList_;
}
uint32_t numYields() const { return numYields_; }
void addNumYields() { numYields_++; }
// ---- Line and column ----
uint32_t currentLine() const { return currentLine_; }
uint32_t lastColumn() const { return lastColumn_; }
void setCurrentLine(uint32_t line) {
currentLine_ = line;
lastColumn_ = 0;
}
void setLastColumn(uint32_t column) { lastColumn_ = column; }
void updateSeparatorPosition() {
lastSeparatorOffet_ = code().length();
lastSeparatorLine_ = currentLine_;
lastSeparatorColumn_ = lastColumn_;
}
void updateSeparatorPositionIfPresent() {
if (lastSeparatorOffet_ == code().length()) {
lastSeparatorLine_ = currentLine_;
lastSeparatorColumn_ = lastColumn_;
}
}
bool isDuplicateLocation() const {
return lastSeparatorLine_ == currentLine_ &&
lastSeparatorColumn_ == lastColumn_;
}
// ---- JIT ----
uint32_t numICEntries() const { return numICEntries_; }
void incrementNumICEntries() {
MOZ_ASSERT(numICEntries_ != UINT32_MAX, "Shouldn't overflow");
numICEntries_++;
}
void setNumICEntries(uint32_t entries) { numICEntries_ = entries; }
uint32_t numTypeSets() const { return numTypeSets_; }
void incrementNumTypeSets() {
MOZ_ASSERT(numTypeSets_ != UINT32_MAX, "Shouldn't overflow");
numTypeSets_++;
}
private:
// ---- Bytecode ----
// Bytecode.
BytecodeVector code_;
// ---- Source notes ----
// Source notes
SrcNotesVector notes_;
// Code offset for last source note
ptrdiff_t lastNoteOffset_ = 0;
// ---- Jump ----
// Last jump target emitted.
JumpTarget lastTarget_ = {-1 - ptrdiff_t(JSOP_JUMPTARGET_LENGTH)};
// ---- Stack ----
// Maximum number of expression stack slots so far.
uint32_t maxStackDepth_ = 0;
// Current stack depth in script frame.
int32_t stackDepth_ = 0;
// ---- Try notes ----
// List of emitted try notes.
CGTryNoteList tryNoteList_;
// ---- Scope ----
// List of emitted block scope notes.
CGScopeNoteList scopeNoteList_;
// ---- Generator ----
// Certain ops (yield, await, gosub) have an entry in the script's
// resumeOffsets list. This can be used to map from the op's resumeIndex to
// the bytecode offset of the next pc. This indirection makes it easy to
// resume in the JIT (because BaselineScript stores a resumeIndex => native
// code array).
CGResumeOffsetList resumeOffsetList_;
// Number of yield instructions emitted. Does not include JSOP_AWAIT.
uint32_t numYields_ = 0;
// ---- Line and column ----
// Line number for srcnotes.
//
// WARNING: If this becomes out of sync with already-emitted srcnotes,
// we can get undefined behavior.
uint32_t currentLine_;
// Zero-based column index on currentLine_ of last SRC_COLSPAN-annotated
// opcode.
//
// WARNING: If this becomes out of sync with already-emitted srcnotes,
// we can get undefined behavior.
uint32_t lastColumn_ = 0;
// The offset, line and column numbers of the last opcode for the
// breakpoint for step execution.
uint32_t lastSeparatorOffet_ = 0;
uint32_t lastSeparatorLine_ = 0;
uint32_t lastSeparatorColumn_ = 0;
// ---- JIT ----
// Number of ICEntries in the script. There's one ICEntry for each JOF_IC op
// and, if the script is a function, for |this| and each formal argument.
uint32_t numICEntries_ = 0;
// Number of JOF_TYPESET opcodes generated.
uint32_t numTypeSets_ = 0;
};
BytecodeSection bytecodeSection_; BytecodeSection bytecodeSection_;
public: public:
@ -326,50 +61,6 @@ struct MOZ_STACK_CLASS BytecodeEmitter {
const BytecodeSection& bytecodeSection() const { return bytecodeSection_; } const BytecodeSection& bytecodeSection() const { return bytecodeSection_; }
private: private:
// Data that is not directly associated with specific opcode/index inside
// bytecode, but referred from bytecode is stored in this class.
class PerScriptData {
public:
explicit PerScriptData(JSContext* cx);
MOZ_MUST_USE bool init(JSContext* cx);
// ---- Scope ----
CGScopeList& scopeList() { return scopeList_; }
const CGScopeList& scopeList() const { return scopeList_; }
// ---- Literals ----
CGNumberList& numberList() { return numberList_; }
const CGNumberList& numberList() const { return numberList_; }
CGObjectList& objectList() { return objectList_; }
const CGObjectList& objectList() const { return objectList_; }
PooledMapPtr<AtomIndexMap>& atomIndices() { return atomIndices_; }
const PooledMapPtr<AtomIndexMap>& atomIndices() const {
return atomIndices_;
}
private:
// ---- Scope ----
// List of emitted scopes.
CGScopeList scopeList_;
// ---- Literals ----
// List of double and bigint values used by script.
CGNumberList numberList_;
// List of emitted objects.
CGObjectList objectList_;
// Map from atom to index.
PooledMapPtr<AtomIndexMap> atomIndices_;
};
PerScriptData perScriptData_; PerScriptData perScriptData_;
public: public:

View File

@ -0,0 +1,170 @@
/* -*- 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 "frontend/BytecodeSection.h"
#include "mozilla/Assertions.h" // MOZ_ASSERT
#include "mozilla/PodOperations.h" // PodZero
#include "mozilla/ReverseIterator.h" // mozilla::Reversed
#include "frontend/ParseNode.h" // ObjectBox
#include "frontend/SharedContext.h" // FunctionBox
#include "vm/BytecodeUtil.h" // INDEX_LIMIT, StackUses, StackDefs
#include "vm/JSContext.h" // JSContext
using namespace js;
using namespace js::frontend;
void CGNumberList::finish(mozilla::Span<GCPtrValue> array) {
MOZ_ASSERT(length() == array.size());
for (unsigned i = 0; i < length(); i++) {
array[i].init(vector[i]);
}
}
/*
* Find the index of the given object for code generator.
*
* Since the emitter refers to each parsed object only once, for the index we
* use the number of already indexed objects. We also add the object to a list
* to convert the list to a fixed-size array when we complete code generation,
* see js::CGObjectList::finish below.
*/
unsigned CGObjectList::add(ObjectBox* objbox) {
MOZ_ASSERT(objbox->isObjectBox());
MOZ_ASSERT(!objbox->emitLink);
objbox->emitLink = lastbox;
lastbox = objbox;
return length++;
}
void CGObjectList::finish(mozilla::Span<GCPtrObject> array) {
MOZ_ASSERT(length <= INDEX_LIMIT);
MOZ_ASSERT(length == array.size());
ObjectBox* objbox = lastbox;
for (GCPtrObject& obj : mozilla::Reversed(array)) {
MOZ_ASSERT(obj == nullptr);
MOZ_ASSERT(objbox->object()->isTenured());
obj.init(objbox->object());
objbox = objbox->emitLink;
}
}
void CGObjectList::finishInnerFunctions() {
ObjectBox* objbox = lastbox;
while (objbox) {
if (objbox->isFunctionBox()) {
objbox->asFunctionBox()->finish();
}
objbox = objbox->emitLink;
}
}
void CGScopeList::finish(mozilla::Span<GCPtrScope> array) {
MOZ_ASSERT(length() <= INDEX_LIMIT);
MOZ_ASSERT(length() == array.size());
for (uint32_t i = 0; i < length(); i++) {
array[i].init(vector[i]);
}
}
bool CGTryNoteList::append(JSTryNoteKind kind, uint32_t stackDepth,
size_t start, size_t end) {
MOZ_ASSERT(start <= end);
MOZ_ASSERT(size_t(uint32_t(start)) == start);
MOZ_ASSERT(size_t(uint32_t(end)) == end);
// Offsets are given relative to sections, but we only expect main-section
// to have TryNotes. In finish() we will fixup base offset.
JSTryNote note;
note.kind = kind;
note.stackDepth = stackDepth;
note.start = uint32_t(start);
note.length = uint32_t(end - start);
return list.append(note);
}
void CGTryNoteList::finish(mozilla::Span<JSTryNote> array) {
MOZ_ASSERT(length() == array.size());
for (unsigned i = 0; i < length(); i++) {
array[i] = list[i];
}
}
bool CGScopeNoteList::append(uint32_t scopeIndex, uint32_t offset,
uint32_t parent) {
CGScopeNote note;
mozilla::PodZero(&note);
// Offsets are given relative to sections. In finish() we will fixup base
// offset if needed.
note.index = scopeIndex;
note.start = offset;
note.parent = parent;
return list.append(note);
}
void CGScopeNoteList::recordEnd(uint32_t index, uint32_t offset) {
MOZ_ASSERT(index < length());
MOZ_ASSERT(list[index].length == 0);
list[index].end = offset;
}
void CGScopeNoteList::finish(mozilla::Span<ScopeNote> array) {
MOZ_ASSERT(length() == array.size());
for (unsigned i = 0; i < length(); i++) {
MOZ_ASSERT(list[i].end >= list[i].start);
list[i].length = list[i].end - list[i].start;
array[i] = list[i];
}
}
void CGResumeOffsetList::finish(mozilla::Span<uint32_t> array) {
MOZ_ASSERT(length() == array.size());
for (unsigned i = 0; i < length(); i++) {
array[i] = list[i];
}
}
BytecodeSection::BytecodeSection(JSContext* cx, uint32_t lineNum)
: code_(cx),
notes_(cx),
tryNoteList_(cx),
scopeNoteList_(cx),
resumeOffsetList_(cx),
currentLine_(lineNum) {}
void BytecodeSection::updateDepth(ptrdiff_t target) {
jsbytecode* pc = code(target);
int nuses = StackUses(pc);
int ndefs = StackDefs(pc);
stackDepth_ -= nuses;
MOZ_ASSERT(stackDepth_ >= 0);
stackDepth_ += ndefs;
if (uint32_t(stackDepth_) > maxStackDepth_) {
maxStackDepth_ = stackDepth_;
}
}
PerScriptData::PerScriptData(JSContext* cx)
: scopeList_(cx),
numberList_(cx),
atomIndices_(cx->frontendCollectionPool()) {}
bool PerScriptData::init(JSContext* cx) { return atomIndices_.acquire(cx); }

View File

@ -0,0 +1,353 @@
/* -*- 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 frontend_BytecodeSection_h
#define frontend_BytecodeSection_h
#include "mozilla/Attributes.h" // MOZ_MUST_USE, MOZ_STACK_CLASS
#include "mozilla/Span.h" // mozilla::Span
#include <stddef.h> // ptrdiff_t, size_t
#include <stdint.h> // uint16_t, int32_t, uint32_t
#include "NamespaceImports.h" // ValueVector
#include "frontend/JumpList.h" // JumpTarget
#include "frontend/NameCollections.h" // AtomIndexMap, PooledMapPtr
#include "frontend/SourceNotes.h" // jssrcnote
#include "gc/Barrier.h" // GCPtrObject, GCPtrScope, GCPtrValue
#include "gc/Rooting.h" // JS::Rooted
#include "js/GCVector.h" // GCVector
#include "js/TypeDecls.h" // jsbytecode
#include "js/Value.h" // JS::Vector
#include "js/Vector.h" // Vector
#include "vm/JSScript.h" // JSTryNote, JSTryNoteKind, ScopeNote
#include "vm/Opcodes.h" // JSOP_*
struct JSContext;
namespace js {
class Scope;
namespace frontend {
class ObjectBox;
class CGNumberList {
JS::Rooted<ValueVector> vector;
public:
explicit CGNumberList(JSContext* cx) : vector(cx, ValueVector(cx)) {}
MOZ_MUST_USE bool append(const JS::Value& v) { return vector.append(v); }
size_t length() const { return vector.length(); }
void finish(mozilla::Span<GCPtrValue> array);
};
struct CGObjectList {
// Number of emitted so far objects.
uint32_t length;
// Last emitted object.
ObjectBox* lastbox;
CGObjectList() : length(0), lastbox(nullptr) {}
unsigned add(ObjectBox* objbox);
void finish(mozilla::Span<GCPtrObject> array);
void finishInnerFunctions();
};
struct MOZ_STACK_CLASS CGScopeList {
JS::Rooted<GCVector<Scope*>> vector;
explicit CGScopeList(JSContext* cx) : vector(cx, GCVector<Scope*>(cx)) {}
bool append(Scope* scope) { return vector.append(scope); }
uint32_t length() const { return vector.length(); }
void finish(mozilla::Span<GCPtrScope> array);
};
struct CGTryNoteList {
Vector<JSTryNote> list;
explicit CGTryNoteList(JSContext* cx) : list(cx) {}
MOZ_MUST_USE bool append(JSTryNoteKind kind, uint32_t stackDepth,
size_t start, size_t end);
size_t length() const { return list.length(); }
void finish(mozilla::Span<JSTryNote> array);
};
struct CGScopeNote : public ScopeNote {
// The end offset. Used to compute the length.
uint32_t end;
};
struct CGScopeNoteList {
Vector<CGScopeNote> list;
explicit CGScopeNoteList(JSContext* cx) : list(cx) {}
MOZ_MUST_USE bool append(uint32_t scopeIndex, uint32_t offset,
uint32_t parent);
void recordEnd(uint32_t index, uint32_t offse);
size_t length() const { return list.length(); }
void finish(mozilla::Span<ScopeNote> array);
};
struct CGResumeOffsetList {
Vector<uint32_t> list;
explicit CGResumeOffsetList(JSContext* cx) : list(cx) {}
MOZ_MUST_USE bool append(uint32_t offset) { return list.append(offset); }
size_t length() const { return list.length(); }
void finish(mozilla::Span<uint32_t> array);
};
static constexpr size_t MaxBytecodeLength = INT32_MAX;
static constexpr size_t MaxSrcNotesLength = INT32_MAX;
// Have a few inline elements, so as to avoid heap allocation for tiny
// sequences. See bug 1390526.
typedef Vector<jsbytecode, 64> BytecodeVector;
typedef Vector<jssrcnote, 64> SrcNotesVector;
// Bytecode and all data directly associated with specific opcode/index inside
// bytecode is stored in this class.
class BytecodeSection {
public:
BytecodeSection(JSContext* cx, uint32_t lineNum);
// ---- Bytecode ----
BytecodeVector& code() { return code_; }
const BytecodeVector& code() const { return code_; }
jsbytecode* code(ptrdiff_t offset) { return code_.begin() + offset; }
ptrdiff_t offset() const { return code_.end() - code_.begin(); }
// ---- Source notes ----
SrcNotesVector& notes() { return notes_; }
const SrcNotesVector& notes() const { return notes_; }
ptrdiff_t lastNoteOffset() const { return lastNoteOffset_; }
void setLastNoteOffset(ptrdiff_t offset) { lastNoteOffset_ = offset; }
// ---- Jump ----
ptrdiff_t lastTargetOffset() const { return lastTarget_.offset; }
void setLastTargetOffset(ptrdiff_t offset) { lastTarget_.offset = offset; }
// Check if the last emitted opcode is a jump target.
bool lastOpcodeIsJumpTarget() const {
return offset() - lastTarget_.offset == ptrdiff_t(JSOP_JUMPTARGET_LENGTH);
}
// JumpTarget should not be part of the emitted statement, as they can be
// aliased by multiple statements. If we included the jump target as part of
// the statement we might have issues where the enclosing statement might
// not contain all the opcodes of the enclosed statements.
ptrdiff_t lastNonJumpTargetOffset() const {
return lastOpcodeIsJumpTarget() ? lastTarget_.offset : offset();
}
// ---- Stack ----
int32_t stackDepth() const { return stackDepth_; }
void setStackDepth(int32_t depth) { stackDepth_ = depth; }
uint32_t maxStackDepth() const { return maxStackDepth_; }
void updateDepth(ptrdiff_t target);
// ---- Try notes ----
CGTryNoteList& tryNoteList() { return tryNoteList_; };
const CGTryNoteList& tryNoteList() const { return tryNoteList_; };
// ---- Scope ----
CGScopeNoteList& scopeNoteList() { return scopeNoteList_; };
const CGScopeNoteList& scopeNoteList() const { return scopeNoteList_; };
// ---- Generator ----
CGResumeOffsetList& resumeOffsetList() { return resumeOffsetList_; }
const CGResumeOffsetList& resumeOffsetList() const {
return resumeOffsetList_;
}
uint32_t numYields() const { return numYields_; }
void addNumYields() { numYields_++; }
// ---- Line and column ----
uint32_t currentLine() const { return currentLine_; }
uint32_t lastColumn() const { return lastColumn_; }
void setCurrentLine(uint32_t line) {
currentLine_ = line;
lastColumn_ = 0;
}
void setLastColumn(uint32_t column) { lastColumn_ = column; }
void updateSeparatorPosition() {
lastSeparatorOffet_ = code().length();
lastSeparatorLine_ = currentLine_;
lastSeparatorColumn_ = lastColumn_;
}
void updateSeparatorPositionIfPresent() {
if (lastSeparatorOffet_ == code().length()) {
lastSeparatorLine_ = currentLine_;
lastSeparatorColumn_ = lastColumn_;
}
}
bool isDuplicateLocation() const {
return lastSeparatorLine_ == currentLine_ &&
lastSeparatorColumn_ == lastColumn_;
}
// ---- JIT ----
uint32_t numICEntries() const { return numICEntries_; }
void incrementNumICEntries() {
MOZ_ASSERT(numICEntries_ != UINT32_MAX, "Shouldn't overflow");
numICEntries_++;
}
void setNumICEntries(uint32_t entries) { numICEntries_ = entries; }
uint32_t numTypeSets() const { return numTypeSets_; }
void incrementNumTypeSets() {
MOZ_ASSERT(numTypeSets_ != UINT32_MAX, "Shouldn't overflow");
numTypeSets_++;
}
private:
// ---- Bytecode ----
// Bytecode.
BytecodeVector code_;
// ---- Source notes ----
// Source notes
SrcNotesVector notes_;
// Code offset for last source note
ptrdiff_t lastNoteOffset_ = 0;
// ---- Jump ----
// Last jump target emitted.
JumpTarget lastTarget_ = {-1 - ptrdiff_t(JSOP_JUMPTARGET_LENGTH)};
// ---- Stack ----
// Maximum number of expression stack slots so far.
uint32_t maxStackDepth_ = 0;
// Current stack depth in script frame.
int32_t stackDepth_ = 0;
// ---- Try notes ----
// List of emitted try notes.
CGTryNoteList tryNoteList_;
// ---- Scope ----
// List of emitted block scope notes.
CGScopeNoteList scopeNoteList_;
// ---- Generator ----
// Certain ops (yield, await, gosub) have an entry in the script's
// resumeOffsets list. This can be used to map from the op's resumeIndex to
// the bytecode offset of the next pc. This indirection makes it easy to
// resume in the JIT (because BaselineScript stores a resumeIndex => native
// code array).
CGResumeOffsetList resumeOffsetList_;
// Number of yield instructions emitted. Does not include JSOP_AWAIT.
uint32_t numYields_ = 0;
// ---- Line and column ----
// Line number for srcnotes.
//
// WARNING: If this becomes out of sync with already-emitted srcnotes,
// we can get undefined behavior.
uint32_t currentLine_;
// Zero-based column index on currentLine_ of last SRC_COLSPAN-annotated
// opcode.
//
// WARNING: If this becomes out of sync with already-emitted srcnotes,
// we can get undefined behavior.
uint32_t lastColumn_ = 0;
// The offset, line and column numbers of the last opcode for the
// breakpoint for step execution.
uint32_t lastSeparatorOffet_ = 0;
uint32_t lastSeparatorLine_ = 0;
uint32_t lastSeparatorColumn_ = 0;
// ---- JIT ----
// Number of ICEntries in the script. There's one ICEntry for each JOF_IC op
// and, if the script is a function, for |this| and each formal argument.
uint32_t numICEntries_ = 0;
// Number of JOF_TYPESET opcodes generated.
uint32_t numTypeSets_ = 0;
};
// Data that is not directly associated with specific opcode/index inside
// bytecode, but referred from bytecode is stored in this class.
class PerScriptData {
public:
explicit PerScriptData(JSContext* cx);
MOZ_MUST_USE bool init(JSContext* cx);
// ---- Scope ----
CGScopeList& scopeList() { return scopeList_; }
const CGScopeList& scopeList() const { return scopeList_; }
// ---- Literals ----
CGNumberList& numberList() { return numberList_; }
const CGNumberList& numberList() const { return numberList_; }
CGObjectList& objectList() { return objectList_; }
const CGObjectList& objectList() const { return objectList_; }
PooledMapPtr<AtomIndexMap>& atomIndices() { return atomIndices_; }
const PooledMapPtr<AtomIndexMap>& atomIndices() const { return atomIndices_; }
private:
// ---- Scope ----
// List of emitted scopes.
CGScopeList scopeList_;
// ---- Literals ----
// List of double and bigint values used by script.
CGNumberList numberList_;
// List of emitted objects.
CGObjectList objectList_;
// Map from atom to index.
PooledMapPtr<AtomIndexMap> atomIndices_;
};
} /* namespace frontend */
} /* namespace js */
#endif /* frontend_BytecodeSection_h */

View File

@ -27,6 +27,7 @@ UNIFIED_SOURCES += [
'BytecodeCompiler.cpp', 'BytecodeCompiler.cpp',
'BytecodeControlStructures.cpp', 'BytecodeControlStructures.cpp',
'BytecodeEmitter.cpp', 'BytecodeEmitter.cpp',
'BytecodeSection.cpp',
'CallOrNewEmitter.cpp', 'CallOrNewEmitter.cpp',
'CForEmitter.cpp', 'CForEmitter.cpp',
'DefaultEmitter.cpp', 'DefaultEmitter.cpp',

View File

@ -241,7 +241,7 @@ void Zone::discardJitCode(FreeOp* fop,
// Warm-up counter for scripts are reset on GC. After discarding code we // Warm-up counter for scripts are reset on GC. After discarding code we
// need to let it warm back up to get information such as which // need to let it warm back up to get information such as which
// opcodes are setting array holes or accessing getter properties. // opcodes are setting array holes or accessing getter properties.
script->resetWarmUpCounter(); script->resetWarmUpCounterForGC();
// Clear the BaselineScript's control flow graph. The LifoAlloc is purged // Clear the BaselineScript's control flow graph. The LifoAlloc is purged
// below. // below.

View File

@ -56,8 +56,8 @@ var Opts_IonEagerNoOffthreadCompilation =
var Opts_Ion2NoOffthreadCompilation = var Opts_Ion2NoOffthreadCompilation =
{ {
'ion.enable': 1, 'ion.enable': 1,
'ion.warmup.trigger': 2, 'ion.warmup.trigger': 3,
'ion.full.warmup.trigger': 2, 'ion.full.warmup.trigger': 3,
'baseline.enable': 1, 'baseline.enable': 1,
'baseline.warmup.trigger': 1, 'baseline.warmup.trigger': 1,
'offthread-compilation.enable': 0 'offthread-compilation.enable': 0

View File

@ -518,3 +518,27 @@ wasmAssert(`(module
// ADD NO MORE TESTS HERE! // ADD NO MORE TESTS HERE!
} }
// Standard wat syntax: the parens around the initializer expression are
// optional.
{
let i1 = wasmEvalText(
`(module
(global $g i32 i32.const 37)
(func (export "f") (result i32) (global.get $g)))`);
assertEq(i1.exports.f(), 37);
let i2 = wasmEvalText(
`(module
(global $g (mut f64) f64.const 42.0)
(func (export "f") (result f64) (global.get $g)))`);
assertEq(i2.exports.f(), 42);
let i3 = wasmEvalText(
`(module
(global $x (import "m" "x") i32)
(global $g i32 global.get $x)
(func (export "f") (result i32) (global.get $g)))`,
{m:{x:86}});
assertEq(i3.exports.f(), 86);
}

View File

@ -766,9 +766,7 @@ bool BaselineCodeGen<Handler>::emitStackCheck() {
return true; return true;
} }
template <> static void EmitCallFrameIsDebuggeeCheck(MacroAssembler& masm) {
void BaselineCompilerCodeGen::emitIsDebuggeeCheck() {
if (handler.compileDebugInstrumentation()) {
masm.Push(BaselineFrameReg); masm.Push(BaselineFrameReg);
masm.setupUnalignedABICall(R0.scratchReg()); masm.setupUnalignedABICall(R0.scratchReg());
masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg()); masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
@ -776,11 +774,27 @@ void BaselineCompilerCodeGen::emitIsDebuggeeCheck() {
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, jit::FrameIsDebuggeeCheck)); masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, jit::FrameIsDebuggeeCheck));
masm.Pop(BaselineFrameReg); masm.Pop(BaselineFrameReg);
} }
template <>
void BaselineCompilerCodeGen::emitIsDebuggeeCheck() {
if (handler.compileDebugInstrumentation()) {
EmitCallFrameIsDebuggeeCheck(masm);
}
} }
template <> template <>
void BaselineInterpreterCodeGen::emitIsDebuggeeCheck() { void BaselineInterpreterCodeGen::emitIsDebuggeeCheck() {
MOZ_CRASH("NYI: interpreter emitIsDebuggeeCheck"); // Use a toggled jump to call FrameIsDebuggeeCheck only if the debugger is
// enabled.
//
// TODO(bug 1522394): consider having a cx->realm->isDebuggee guard before the
// call. Consider moving the callWithABI out-of-line.
Label skipCheck;
CodeOffset toggleOffset = masm.toggledJump(&skipCheck);
EmitCallFrameIsDebuggeeCheck(masm);
masm.bind(&skipCheck);
handler.setDebuggeeCheckOffset(toggleOffset);
} }
template <> template <>
@ -1050,7 +1064,44 @@ void BaselineCompilerCodeGen::emitInitFrameFields() {
template <> template <>
void BaselineInterpreterCodeGen::emitInitFrameFields() { void BaselineInterpreterCodeGen::emitInitFrameFields() {
MOZ_CRASH("NYI: interpreter emitInitFrameFields"); Register scratch1 = R0.scratchReg();
Register scratch2 = R2.scratchReg();
masm.store32(Imm32(BaselineFrame::RUNNING_IN_INTERPRETER),
frame.addressOfFlags());
// Initialize interpreterScript.
Label notFunction, done;
masm.loadPtr(frame.addressOfCalleeToken(), scratch1);
masm.branchTestPtr(Assembler::NonZero, scratch1, Imm32(CalleeTokenScriptBit),
&notFunction);
{
// CalleeToken_Function or CalleeToken_FunctionConstructing.
masm.andPtr(Imm32(uint32_t(CalleeTokenMask)), scratch1);
masm.loadPtr(Address(scratch1, JSFunction::offsetOfScript()), scratch1);
masm.jump(&done);
}
masm.bind(&notFunction);
{
// CalleeToken_Script.
masm.andPtr(Imm32(uint32_t(CalleeTokenMask)), scratch1);
}
masm.bind(&done);
masm.storePtr(scratch1, frame.addressOfInterpreterScript());
// Initialize interpreterICEntry.
masm.loadPtr(Address(scratch1, JSScript::offsetOfTypes()), scratch2);
masm.loadPtr(Address(scratch2, TypeScript::offsetOfICScript()), scratch2);
masm.computeEffectiveAddress(Address(scratch2, ICScript::offsetOfICEntries()),
scratch2);
masm.storePtr(scratch2, frame.addressOfInterpreterICEntry());
// Initialize interpreterPC.
masm.loadPtr(Address(scratch1, JSScript::offsetOfScriptData()), scratch1);
masm.load32(Address(scratch1, SharedScriptData::offsetOfCodeOffset()),
scratch2);
masm.addPtr(scratch2, scratch1);
masm.storePtr(scratch1, frame.addressOfInterpreterPC());
} }
template <> template <>
@ -6071,7 +6122,27 @@ bool BaselineCompilerCodeGen::emit_JSOP_JUMPTARGET() {
template <> template <>
bool BaselineInterpreterCodeGen::emit_JSOP_JUMPTARGET() { bool BaselineInterpreterCodeGen::emit_JSOP_JUMPTARGET() {
MOZ_CRASH("NYI: interpreter JSOP_JUMPTARGET"); Register scratch1 = R0.scratchReg();
Register scratch2 = R1.scratchReg();
// Load icIndex in scratch1.
LoadInt32Operand(masm, PCRegAtStart, scratch1);
// scratch1 := scratch1 * sizeof(ICEntry)
static_assert(sizeof(ICEntry) == 8 || sizeof(ICEntry) == 16,
"shift below depends on ICEntry size");
uint32_t shift = (sizeof(ICEntry) == 16) ? 4 : 3;
masm.lshiftPtr(Imm32(shift), scratch1);
// Compute ICEntry* and store to frame->interpreterICEntry.
loadScript(scratch2);
masm.loadPtr(Address(scratch2, JSScript::offsetOfTypes()), scratch2);
masm.loadPtr(Address(scratch2, TypeScript::offsetOfICScript()), scratch2);
masm.computeEffectiveAddress(
BaseIndex(scratch2, scratch1, TimesOne, ICScript::offsetOfICEntries()),
scratch2);
masm.storePtr(scratch2, frame.addressOfInterpreterICEntry());
return true;
} }
template <typename Handler> template <typename Handler>

View File

@ -655,6 +655,7 @@ class BaselineCompiler final : private BaselineCompilerCodeGen {
class BaselineInterpreterHandler { class BaselineInterpreterHandler {
InterpreterFrameInfo frame_; InterpreterFrameInfo frame_;
Label interpretOp_; Label interpretOp_;
CodeOffset debuggeeCheckOffset_;
public: public:
using FrameInfoT = InterpreterFrameInfo; using FrameInfoT = InterpreterFrameInfo;
@ -671,6 +672,11 @@ class BaselineInterpreterHandler {
JSScript* maybeScript() const { return nullptr; } JSScript* maybeScript() const { return nullptr; }
JSFunction* maybeFunction() const { return nullptr; } JSFunction* maybeFunction() const { return nullptr; }
void setDebuggeeCheckOffset(CodeOffset offset) {
debuggeeCheckOffset_ = offset;
}
CodeOffset debuggeeCheckOffset() const { return debuggeeCheckOffset_; }
// Interpreter doesn't need to keep track of RetAddrEntries, so these methods // Interpreter doesn't need to keep track of RetAddrEntries, so these methods
// are no-ops. // are no-ops.
MOZ_MUST_USE bool appendRetAddrEntry(JSContext* cx, RetAddrEntry::Kind kind, MOZ_MUST_USE bool appendRetAddrEntry(JSContext* cx, RetAddrEntry::Kind kind,

View File

@ -361,6 +361,8 @@ class ICScript {
ICEntry& icEntryFromPCOffset(uint32_t pcOffset); ICEntry& icEntryFromPCOffset(uint32_t pcOffset);
ICEntry& icEntryFromPCOffset(uint32_t pcOffset, ICEntry* prevLookedUpEntry); ICEntry& icEntryFromPCOffset(uint32_t pcOffset, ICEntry* prevLookedUpEntry);
static constexpr size_t offsetOfICEntries() { return sizeof(ICScript); }
}; };
class ICMonitoredStub; class ICMonitoredStub;

View File

@ -2252,7 +2252,7 @@ static MethodStatus Compile(JSContext* cx, HandleScript script,
} }
if (!CanLikelyAllocateMoreExecutableMemory()) { if (!CanLikelyAllocateMoreExecutableMemory()) {
script->resetWarmUpCounter(); script->resetWarmUpCounterToDelayIonCompilation();
return Method_Skipped; return Method_Skipped;
} }
@ -2513,7 +2513,7 @@ bool jit::IonCompileScriptForBaseline(JSContext* cx, BaselineFrame* frame,
// TODO: ASSERT that ion-compilation-disabled checker stub doesn't exist. // TODO: ASSERT that ion-compilation-disabled checker stub doesn't exist.
// TODO: Clear all optimized stubs. // TODO: Clear all optimized stubs.
// TODO: Add a ion-compilation-disabled checker IC stub // TODO: Add a ion-compilation-disabled checker IC stub
script->resetWarmUpCounter(); script->resetWarmUpCounterToDelayIonCompilation();
return true; return true;
} }
@ -2576,7 +2576,7 @@ bool jit::IonCompileScriptForBaseline(JSContext* cx, BaselineFrame* frame,
" Reset WarmUpCounter cantCompile=%s bailoutExpected=%s!", " Reset WarmUpCounter cantCompile=%s bailoutExpected=%s!",
stat == Method_CantCompile ? "yes" : "no", stat == Method_CantCompile ? "yes" : "no",
bailoutExpected ? "yes" : "no"); bailoutExpected ? "yes" : "no");
script->resetWarmUpCounter(); script->resetWarmUpCounterToDelayIonCompilation();
} }
return true; return true;
} }
@ -2799,7 +2799,7 @@ static void ClearIonScriptAfterInvalidation(JSContext* cx, JSScript* script,
// compile, unless we are recompiling *because* a script got hot // compile, unless we are recompiling *because* a script got hot
// (resetUses is false). // (resetUses is false).
if (resetUses) { if (resetUses) {
script->resetWarmUpCounter(); script->resetWarmUpCounterToDelayIonCompilation();
} }
} }

View File

@ -215,7 +215,7 @@ static void HandleExceptionIon(JSContext* cx, const InlineFrameIterator& frame,
// Ion can compile try-catch, but bailing out to catch // Ion can compile try-catch, but bailing out to catch
// exceptions is slow. Reset the warm-up counter so that if we // exceptions is slow. Reset the warm-up counter so that if we
// catch many exceptions we won't Ion-compile the script. // catch many exceptions we won't Ion-compile the script.
script->resetWarmUpCounter(); script->resetWarmUpCounterToDelayIonCompilation();
if (*hitBailoutException) { if (*hitBailoutException) {
break; break;
@ -373,7 +373,7 @@ static bool ProcessTryNotesBaseline(JSContext* cx, const JSJitFrameIter& frame,
// Ion can compile try-catch, but bailing out to catch // Ion can compile try-catch, but bailing out to catch
// exceptions is slow. Reset the warm-up counter so that if we // exceptions is slow. Reset the warm-up counter so that if we
// catch many exceptions we won't Ion-compile the script. // catch many exceptions we won't Ion-compile the script.
script->resetWarmUpCounter(); script->resetWarmUpCounterToDelayIonCompilation();
// Resume at the start of the catch block. // Resume at the start of the catch block.
rfe->kind = ResumeFromException::RESUME_CATCH; rfe->kind = ResumeFromException::RESUME_CATCH;

View File

@ -1636,11 +1636,11 @@ class MacroAssembler : public MacroAssemblerSpecific {
inline void cmp32Load32(Condition cond, Register lhs, const Address& rhs, inline void cmp32Load32(Condition cond, Register lhs, const Address& rhs,
const Address& src, Register dest) const Address& src, Register dest)
DEFINED_ON(arm, arm64, x86_shared); DEFINED_ON(arm, arm64, mips_shared, x86_shared);
inline void cmp32Load32(Condition cond, Register lhs, Register rhs, inline void cmp32Load32(Condition cond, Register lhs, Register rhs,
const Address& src, Register dest) const Address& src, Register dest)
DEFINED_ON(arm, arm64, x86_shared); DEFINED_ON(arm, arm64, mips_shared, x86_shared);
inline void cmp32MovePtr(Condition cond, Register lhs, Imm32 rhs, inline void cmp32MovePtr(Condition cond, Register lhs, Imm32 rhs,
Register src, Register dest) Register src, Register dest)

View File

@ -435,23 +435,6 @@ void LIRGenerator::visitWasmStore(MWasmStore* ins) {
add(lir, ins); add(lir, ins);
} }
void LIRGenerator::visitWasmSelect(MWasmSelect* ins) {
if (ins->type() == MIRType::Int64) {
auto* lir = new (alloc()) LWasmSelectI64(
useInt64RegisterAtStart(ins->trueExpr()), useInt64(ins->falseExpr()),
useRegister(ins->condExpr()));
defineInt64ReuseInput(lir, ins, LWasmSelectI64::TrueExprIndex);
return;
}
auto* lir = new (alloc())
LWasmSelect(useRegisterAtStart(ins->trueExpr()), use(ins->falseExpr()),
useRegister(ins->condExpr()));
defineReuseInput(lir, ins, LWasmSelect::TrueExprIndex);
}
void LIRGeneratorMIPSShared::lowerUDiv(MDiv* div) { void LIRGeneratorMIPSShared::lowerUDiv(MDiv* div) {
MDefinition* lhs = div->getOperand(0); MDefinition* lhs = div->getOperand(0);
MDefinition* rhs = div->getOperand(1); MDefinition* rhs = div->getOperand(1);

View File

@ -30,6 +30,10 @@ void MacroAssembler::move16SignExtend(Register src, Register dest) {
ma_seh(dest, src); ma_seh(dest, src);
} }
void MacroAssembler::loadAbiReturnAddress(Register dest) {
movePtr(ra, dest);
}
// =============================================================== // ===============================================================
// Logical instructions // Logical instructions
@ -797,6 +801,19 @@ void MacroAssembler::cmp32Move32(Condition cond, Register lhs,
MOZ_CRASH(); MOZ_CRASH();
} }
void MacroAssembler::cmp32Load32(Condition cond, Register lhs,
const Address& rhs, const Address& src,
Register dest) {
// This is never used, but must be present to facilitate linking on mips(64).
MOZ_CRASH("No known use cases");
}
void MacroAssembler::cmp32Load32(Condition cond, Register lhs, Register rhs,
const Address& src, Register dest) {
// This is never used, but must be present to facilitate linking on mips(64).
MOZ_CRASH("No known use cases");
}
void MacroAssembler::test32LoadPtr(Condition cond, const Address& addr, void MacroAssembler::test32LoadPtr(Condition cond, const Address& addr,
Imm32 mask, const Address& src, Imm32 mask, const Address& src,
Register dest) { Register dest) {

View File

@ -36,6 +36,7 @@
#include "jit/BaselineJIT.h" #include "jit/BaselineJIT.h"
#include "jit/Ion.h" #include "jit/Ion.h"
#include "jit/IonCode.h" #include "jit/IonCode.h"
#include "jit/JitOptions.h"
#include "jit/JitRealm.h" #include "jit/JitRealm.h"
#include "js/CompileOptions.h" #include "js/CompileOptions.h"
#include "js/MemoryMetrics.h" #include "js/MemoryMetrics.h"
@ -5465,6 +5466,18 @@ bool JSScript::mayReadFrameArgsDirectly() {
return argumentsHasVarBinding() || hasRest(); return argumentsHasVarBinding() || hasRest();
} }
void JSScript::resetWarmUpCounterToDelayIonCompilation() {
// Reset the warm-up count only if it's greater than the BaselineCompiler
// threshold. We do this to ensure this has no effect on Baseline compilation
// because we don't want scripts to get stuck in the (Baseline) interpreter in
// pathological cases.
if (warmUpCount > jit::JitOptions.baselineWarmUpThreshold) {
incWarmUpResetCounter();
warmUpCount = jit::JitOptions.baselineWarmUpThreshold;
}
}
void JSScript::AutoDelazify::holdScript(JS::HandleFunction fun) { void JSScript::AutoDelazify::holdScript(JS::HandleFunction fun) {
if (fun) { if (fun) {
if (fun->realm()->isSelfHostingRealm()) { if (fun->realm()->isSelfHostingRealm()) {

View File

@ -2662,11 +2662,13 @@ class JSScript : public js::gc::TenuredCell {
static size_t offsetOfWarmUpCounter() { static size_t offsetOfWarmUpCounter() {
return offsetof(JSScript, warmUpCount); return offsetof(JSScript, warmUpCount);
} }
void resetWarmUpCounter() { void resetWarmUpCounterForGC() {
incWarmUpResetCounter(); incWarmUpResetCounter();
warmUpCount = 0; warmUpCount = 0;
} }
void resetWarmUpCounterToDelayIonCompilation();
unsigned getWarmUpResetCount() const { unsigned getWarmUpResetCount() const {
constexpr uint32_t MASK = uint32_t(MutableFlags::WarmupResets_MASK); constexpr uint32_t MASK = uint32_t(MutableFlags::WarmupResets_MASK);
return mutableFlags_ & MASK; return mutableFlags_ & MASK;

View File

@ -1569,7 +1569,7 @@ bool js::FinishCompilation(JSContext* cx, HandleScript script,
} }
if (!succeeded) { if (!succeeded) {
script->resetWarmUpCounter(); script->resetWarmUpCounterToDelayIonCompilation();
*isValidOut = false; *isValidOut = false;
return true; return true;
} }
@ -2679,9 +2679,7 @@ void TypeZone::addPendingRecompile(JSContext* cx, JSScript* script) {
CancelOffThreadIonCompile(script); CancelOffThreadIonCompile(script);
// Let the script warm up again before attempting another compile. // Let the script warm up again before attempting another compile.
if (jit::IsBaselineEnabled(cx)) { script->resetWarmUpCounterToDelayIonCompilation();
script->resetWarmUpCounter();
}
if (script->hasIonScript()) { if (script->hasIonScript()) {
addPendingRecompile( addPendingRecompile(

View File

@ -4454,9 +4454,12 @@ static bool MaybeParseOwnerIndex(WasmParseContext& c) {
return true; return true;
} }
static AstExpr* ParseInitializerExpression(WasmParseContext& c) { static AstExpr* ParseInitializerConstExpression(WasmParseContext& c) {
if (!c.ts.match(WasmToken::OpenParen, c.error)) { bool need_rparen = false;
return nullptr;
// For const initializer expressions, the parens are optional.
if (c.ts.getIf(WasmToken::OpenParen)) {
need_rparen = true;
} }
AstExpr* initExpr = ParseExprInsideParens(c); AstExpr* initExpr = ParseExprInsideParens(c);
@ -4464,7 +4467,7 @@ static AstExpr* ParseInitializerExpression(WasmParseContext& c) {
return nullptr; return nullptr;
} }
if (!c.ts.match(WasmToken::CloseParen, c.error)) { if (need_rparen && !c.ts.match(WasmToken::CloseParen, c.error)) {
return nullptr; return nullptr;
} }
@ -4480,11 +4483,19 @@ static bool ParseInitializerExpressionOrPassive(WasmParseContext& c,
} }
#endif #endif
AstExpr* initExpr = ParseInitializerExpression(c); if (!c.ts.match(WasmToken::OpenParen, c.error)) {
return false;
}
AstExpr* initExpr = ParseExprInsideParens(c);
if (!initExpr) { if (!initExpr) {
return false; return false;
} }
if (!c.ts.match(WasmToken::CloseParen, c.error)) {
return false;
}
*maybeInitExpr = initExpr; *maybeInitExpr = initExpr;
return true; return true;
} }
@ -5119,7 +5130,7 @@ static bool ParseGlobal(WasmParseContext& c, AstModule* module) {
return false; return false;
} }
AstExpr* init = ParseInitializerExpression(c); AstExpr* init = ParseInitializerConstExpression(c);
if (!init) { if (!init) {
return false; return false;
} }

View File

@ -186,11 +186,10 @@ function load_cert(cert, trust) {
function fetch_blocklist() { function fetch_blocklist() {
Services.prefs.setBoolPref("services.settings.load_dump", false); Services.prefs.setBoolPref("services.settings.load_dump", false);
Services.prefs.setBoolPref("services.settings.verify_signature", false);
Services.prefs.setCharPref("services.settings.server", Services.prefs.setCharPref("services.settings.server",
`http://localhost:${port}/v1`); `http://localhost:${port}/v1`);
BlocklistClients.initialize(); BlocklistClients.initialize({ verifySignature: false });
return RemoteSettings.pollChanges(); return RemoteSettings.pollChanges();
} }

View File

@ -15,6 +15,7 @@ let remoteSecSetting;
if (AppConstants.MOZ_NEW_CERT_STORAGE) { if (AppConstants.MOZ_NEW_CERT_STORAGE) {
const {RemoteSecuritySettings} = ChromeUtils.import("resource://gre/modules/psm/RemoteSecuritySettings.jsm"); const {RemoteSecuritySettings} = ChromeUtils.import("resource://gre/modules/psm/RemoteSecuritySettings.jsm");
remoteSecSetting = new RemoteSecuritySettings(); remoteSecSetting = new RemoteSecuritySettings();
remoteSecSetting.client.verifySignature = false;
} }
let server; let server;
@ -89,7 +90,6 @@ function setupKintoPreloadServer(certGenerator, options = {
}) { }) {
const dummyServerURL = `http://localhost:${server.identity.primaryPort}/v1`; const dummyServerURL = `http://localhost:${server.identity.primaryPort}/v1`;
Services.prefs.setCharPref("services.settings.server", dummyServerURL); Services.prefs.setCharPref("services.settings.server", dummyServerURL);
Services.prefs.setBoolPref("services.settings.verify_signature", false);
const configPath = "/v1/"; const configPath = "/v1/";
const recordsPath = "/v1/buckets/security-state/collections/intermediates/records"; const recordsPath = "/v1/buckets/security-state/collections/intermediates/records";

View File

@ -110,6 +110,7 @@ skip-if = toolkit == 'android'
[test_hmac.js] [test_hmac.js]
[test_intermediate_basic_usage_constraints.js] [test_intermediate_basic_usage_constraints.js]
[test_intermediate_preloads.js] [test_intermediate_preloads.js]
tags = blocklist
# Bug 1520297 - do something to handle tighter resource constraints on Android # Bug 1520297 - do something to handle tighter resource constraints on Android
skip-if = toolkit == 'android' skip-if = toolkit == 'android'
[test_imminent_distrust.js] [test_imminent_distrust.js]

View File

@ -251,12 +251,15 @@ var OneCRLBlocklistClient;
var PinningBlocklistClient; var PinningBlocklistClient;
var RemoteSecuritySettingsClient; var RemoteSecuritySettingsClient;
function initialize() { function initialize(options = {}) {
const { verifySignature = true } = options;
OneCRLBlocklistClient = RemoteSettings(Services.prefs.getCharPref(PREF_SECURITY_SETTINGS_ONECRL_COLLECTION), { OneCRLBlocklistClient = RemoteSettings(Services.prefs.getCharPref(PREF_SECURITY_SETTINGS_ONECRL_COLLECTION), {
bucketNamePref: PREF_SECURITY_SETTINGS_ONECRL_BUCKET, bucketNamePref: PREF_SECURITY_SETTINGS_ONECRL_BUCKET,
lastCheckTimePref: PREF_SECURITY_SETTINGS_ONECRL_CHECKED, lastCheckTimePref: PREF_SECURITY_SETTINGS_ONECRL_CHECKED,
signerName: Services.prefs.getCharPref(PREF_SECURITY_SETTINGS_ONECRL_SIGNER), signerName: Services.prefs.getCharPref(PREF_SECURITY_SETTINGS_ONECRL_SIGNER),
}); });
OneCRLBlocklistClient.verifySignature = verifySignature;
OneCRLBlocklistClient.on("sync", updateCertBlocklist); OneCRLBlocklistClient.on("sync", updateCertBlocklist);
PinningBlocklistClient = RemoteSettings(Services.prefs.getCharPref(PREF_BLOCKLIST_PINNING_COLLECTION), { PinningBlocklistClient = RemoteSettings(Services.prefs.getCharPref(PREF_BLOCKLIST_PINNING_COLLECTION), {
@ -264,6 +267,7 @@ function initialize() {
lastCheckTimePref: PREF_BLOCKLIST_PINNING_CHECKED_SECONDS, lastCheckTimePref: PREF_BLOCKLIST_PINNING_CHECKED_SECONDS,
signerName: Services.prefs.getCharPref(PREF_BLOCKLIST_PINNING_SIGNER), signerName: Services.prefs.getCharPref(PREF_BLOCKLIST_PINNING_SIGNER),
}); });
PinningBlocklistClient.verifySignature = verifySignature;
PinningBlocklistClient.on("sync", updatePinningList); PinningBlocklistClient.on("sync", updatePinningList);
if (AppConstants.MOZ_NEW_CERT_STORAGE) { if (AppConstants.MOZ_NEW_CERT_STORAGE) {
@ -272,6 +276,7 @@ function initialize() {
// In Bug 1526018 this will move into its own service, as it's not quite like // In Bug 1526018 this will move into its own service, as it's not quite like
// the others. // the others.
RemoteSecuritySettingsClient = new RemoteSecuritySettings(); RemoteSecuritySettingsClient = new RemoteSecuritySettings();
RemoteSecuritySettingsClient.verifySignature = verifySignature;
return { return {
OneCRLBlocklistClient, OneCRLBlocklistClient,

View File

@ -19,7 +19,7 @@ add_task(async function test_something() {
const dummyServerURL = `http://localhost:${server.identity.primaryPort}/v1`; const dummyServerURL = `http://localhost:${server.identity.primaryPort}/v1`;
Services.prefs.setCharPref("services.settings.server", dummyServerURL); Services.prefs.setCharPref("services.settings.server", dummyServerURL);
const {OneCRLBlocklistClient} = BlocklistClients.initialize(); const {OneCRLBlocklistClient} = BlocklistClients.initialize({verifySignature: false});
// register a handler // register a handler
function handleResponse(request, response) { function handleResponse(request, response) {
@ -104,10 +104,6 @@ add_task(async function test_something() {
}); });
function run_test() { function run_test() {
// Ensure that signature verification is disabled to prevent interference
// with basic certificate sync tests
Services.prefs.setBoolPref("services.settings.verify_signature", false);
// Set up an HTTP Server // Set up an HTTP Server
server = new HttpServer(); server = new HttpServer();
server.start(-1); server.start(-1);

View File

@ -27,7 +27,9 @@ let server;
// Some simple tests to demonstrate that the core preload sync operations work // Some simple tests to demonstrate that the core preload sync operations work
// correctly and that simple kinto operations are working as expected. // correctly and that simple kinto operations are working as expected.
add_task(async function test_something() { add_task(async function test_something() {
const PinningPreloadClient = BlocklistClients.initialize().PinningBlocklistClient; const {
PinningBlocklistClient: PinningPreloadClient,
} = BlocklistClients.initialize({ verifySignature: false });
const configPath = "/v1/"; const configPath = "/v1/";
const recordsPath = "/v1/buckets/pinning/collections/pins/records"; const recordsPath = "/v1/buckets/pinning/collections/pins/records";
@ -138,10 +140,6 @@ add_task(async function test_something() {
}); });
function run_test() { function run_test() {
// Ensure that signature verification is disabled to prevent interference
// with basic certificate sync tests
Services.prefs.setBoolPref("services.settings.verify_signature", false);
// Set up an HTTP Server // Set up an HTTP Server
server = new HttpServer(); server = new HttpServer();
server.start(-1); server.start(-1);

View File

@ -8,7 +8,6 @@ const { UptakeTelemetry } = ChromeUtils.import("resource://services-common/uptak
let server; let server;
const PREF_SETTINGS_VERIFY_SIGNATURE = "services.settings.verify_signature";
const PREF_SETTINGS_SERVER = "services.settings.server"; const PREF_SETTINGS_SERVER = "services.settings.server";
const PREF_SIGNATURE_ROOT = "security.content.signature.root_hash"; const PREF_SIGNATURE_ROOT = "security.content.signature.root_hash";
@ -591,10 +590,8 @@ add_task(async function test_check_signatures() {
}); });
function run_test() { function run_test() {
OneCRLBlocklistClient = BlocklistClients.initialize().OneCRLBlocklistClient; // Signature verification is evabled by default.
({OneCRLBlocklistClient} = BlocklistClients.initialize());
// ensure signatures are enforced
Services.prefs.setBoolPref(PREF_SETTINGS_VERIFY_SIGNATURE, true);
// get a signature verifier to ensure nsNSSComponent is initialized // get a signature verifier to ensure nsNSSComponent is initialized
Cc["@mozilla.org/security/contentsignatureverifier;1"] Cc["@mozilla.org/security/contentsignatureverifier;1"]

View File

@ -35,8 +35,6 @@ XPCOMUtils.defineLazyPreferenceGetter(this, "gServerURL",
"services.settings.server"); "services.settings.server");
XPCOMUtils.defineLazyPreferenceGetter(this, "gChangesPath", XPCOMUtils.defineLazyPreferenceGetter(this, "gChangesPath",
"services.settings.changes.path"); "services.settings.changes.path");
XPCOMUtils.defineLazyPreferenceGetter(this, "gVerifySignature",
"services.settings.verify_signature", true);
/** /**
* cacheProxy returns an object Proxy that will memoize properties of the target. * cacheProxy returns an object Proxy that will memoize properties of the target.
@ -189,6 +187,10 @@ class RemoteSettingsClient extends EventEmitter {
this.localFields = localFields; this.localFields = localFields;
this._lastCheckTimePref = lastCheckTimePref; this._lastCheckTimePref = lastCheckTimePref;
// This attribute allows signature verification to be disabled, when running tests
// or when pulling data from a dev server.
this.verifySignature = true;
// The bucket preference value can be changed (eg. `main` to `main-preview`) in order // The bucket preference value can be changed (eg. `main` to `main-preview`) in order
// to preview the changes to be approved in a real client. // to preview the changes to be approved in a real client.
this.bucketNamePref = bucketNamePref; this.bucketNamePref = bucketNamePref;
@ -328,7 +330,7 @@ class RemoteSettingsClient extends EventEmitter {
// If signature verification is enabled, then add a synchronization hook // If signature verification is enabled, then add a synchronization hook
// for incoming changes that validates the signature. // for incoming changes that validates the signature.
if (this.signerName && gVerifySignature) { if (this.verifySignature) {
kintoCollection.hooks["incoming-changes"] = [async (payload, collection) => { kintoCollection.hooks["incoming-changes"] = [async (payload, collection) => {
await this._validateCollectionSignature(payload.changes, await this._validateCollectionSignature(payload.changes,
payload.lastModified, payload.lastModified,

View File

@ -44,12 +44,12 @@ function run_test() {
// Point the blocklist clients to use this local HTTP server. // Point the blocklist clients to use this local HTTP server.
Services.prefs.setCharPref("services.settings.server", Services.prefs.setCharPref("services.settings.server",
`http://localhost:${server.identity.primaryPort}/v1`); `http://localhost:${server.identity.primaryPort}/v1`);
// Ensure that signature verification is disabled to prevent interference
// with basic certificate sync tests
Services.prefs.setBoolPref("services.settings.verify_signature", false);
client = RemoteSettings("password-fields"); client = RemoteSettings("password-fields");
client.verifySignature = false;
clientWithDump = RemoteSettings("language-dictionaries"); clientWithDump = RemoteSettings("language-dictionaries");
clientWithDump.verifySignature = false;
// Setup server fake responses. // Setup server fake responses.
function handleResponse(request, response) { function handleResponse(request, response) {

View File

@ -769,6 +769,16 @@ def build_generic_worker_payload(config, task, task_def):
'maxRunTime': worker['max-run-time'], 'maxRunTime': worker['max-run-time'],
} }
if worker['os'] == 'windows':
task_def['payload']['onExitStatus'] = {
'retry': [
# These codes (on windows) indicate a process interruption,
# rather than a task run failure. See bug 1544403.
1073807364, # process force-killed due to system shutdown
3221225786, # sigint (any interrupt)
]
}
env = worker.get('env', {}) env = worker.get('env', {})
if task.get('needs-sccache'): if task.get('needs-sccache'):

View File

@ -1,6 +1,6 @@
[payment-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html] [payment-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html]
disabled: disabled:
if not nightly_build: https://bugzilla.mozilla.org/show_bug.cgi?id=1495301 if (os == "android") and not nightly_build: https://bugzilla.mozilla.org/show_bug.cgi?id=1549241
[Feature-Policy allow="payment" allows same-origin relocation.] [Feature-Policy allow="payment" allows same-origin relocation.]
expected: expected:
if not e10s: FAIL if not e10s: FAIL

View File

@ -1,6 +1,6 @@
[payment-allowed-by-feature-policy-attribute.https.sub.html] [payment-allowed-by-feature-policy-attribute.https.sub.html]
disabled: disabled:
if not nightly_build: https://bugzilla.mozilla.org/show_bug.cgi?id=1495301 if (os == "android") or not nightly_build: https://bugzilla.mozilla.org/show_bug.cgi?id=1549241
[Feature policy "payment" can be enabled in same-origin iframe using allow="payment" attribute] [Feature policy "payment" can be enabled in same-origin iframe using allow="payment" attribute]
expected: expected:
if not e10s: FAIL if not e10s: FAIL

View File

@ -1,6 +1,6 @@
[payment-allowed-by-feature-policy.https.sub.html] [payment-allowed-by-feature-policy.https.sub.html]
disabled: disabled:
if not nightly_build: https://bugzilla.mozilla.org/show_bug.cgi?id=1495301 if (os == "android") or not nightly_build: https://bugzilla.mozilla.org/show_bug.cgi?id=1549241
[Feature-Policy header {"payment" : ["*"\]} allows the top-level document.] [Feature-Policy header {"payment" : ["*"\]} allows the top-level document.]
expected: expected:
if not e10s: FAIL if not e10s: FAIL

View File

@ -1,6 +1,6 @@
[payment-default-feature-policy.https.sub.html] [payment-default-feature-policy.https.sub.html]
disabled: disabled:
if not nightly_build: https://bugzilla.mozilla.org/show_bug.cgi?id=1495301 if (os == "android") or not nightly_build: https://bugzilla.mozilla.org/show_bug.cgi?id=1549241
[Default "payment" feature policy ["self"\] allows the top-level document.] [Default "payment" feature policy ["self"\] allows the top-level document.]
expected: expected:
if not e10s: FAIL if not e10s: FAIL

View File

@ -1,6 +1,6 @@
[payment-disabled-by-feature-policy.https.sub.html] [payment-disabled-by-feature-policy.https.sub.html]
disabled: disabled:
if not nightly_build: https://bugzilla.mozilla.org/show_bug.cgi?id=1495301 if (os == "android") or not nightly_build: https://bugzilla.mozilla.org/show_bug.cgi?id=1549241
[Feature-Policy header {"payment" : [\]} disallows the top-level document.] [Feature-Policy header {"payment" : [\]} disallows the top-level document.]
expected: expected:
if not e10s: FAIL if not e10s: FAIL

View File

@ -1,4 +1,6 @@
[payment-supported-by-feature-policy.tentative.html] [payment-supported-by-feature-policy.tentative.html]
disabled:
if (os == "android") or not nightly_build: https://bugzilla.mozilla.org/show_bug.cgi?id=1549241
[document.featurePolicy.features should advertise payment.] [document.featurePolicy.features should advertise payment.]
expected: FAIL expected: FAIL

View File

@ -0,0 +1,26 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>CSS Scoping: Dynamic reassignment of a slot.</title>
<link rel="author" title="Edgar Chen" href="mailto:echen@mozilla.com">
<link rel="help" href="https://drafts.csswg.org/css-scoping/#selectors-data-model">
<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1548848">
<link rel="match" href="reference/green-box.html"/>
<div id="host">
<div id="green" style="background: green"></div>
<div>
<script>
let root = host.attachShadow({mode: 'open'});
root.innerHTML = `
<style>::slotted(div),div { width: 100px; height: 100px }</style>
<p>Test passes if you see a single 100px by 100px green box below.</p>
<slot id="slot"></slot>
<slot>
<div style="background: red"></div>
</slot>
`;
onload = function () {
root.offsetTop; // Update layout
root.getElementById('slot').remove();
};
</script>

View File

@ -27,9 +27,6 @@ function run_test() {
// Point the blocklist clients to use this local HTTP server. // Point the blocklist clients to use this local HTTP server.
Services.prefs.setCharPref("services.settings.server", Services.prefs.setCharPref("services.settings.server",
`http://localhost:${server.identity.primaryPort}/v1`); `http://localhost:${server.identity.primaryPort}/v1`);
// Ensure that signature verification is disabled to prevent interference
// with basic certificate sync tests
Services.prefs.setBoolPref("services.settings.verify_signature", false);
// Unfortunately security settings are coupled with blocklists clients, // Unfortunately security settings are coupled with blocklists clients,
// this will be fixed in Bug 1526018 // this will be fixed in Bug 1526018
@ -42,12 +39,15 @@ function run_test() {
BlocklistGlobal.PluginBlocklistRS._ensureInitialized(); BlocklistGlobal.PluginBlocklistRS._ensureInitialized();
BlocklistGlobal.GfxBlocklistRS._ensureInitialized(); BlocklistGlobal.GfxBlocklistRS._ensureInitialized();
gBlocklistClients = [ gBlocklistClients = [
{client: BlocklistGlobal.ExtensionBlocklistRS._client, testData: ["i808", "i720", "i539"]}, {client: BlocklistGlobal.ExtensionBlocklistRS._client, testData: ["i808", "i720", "i539"]},
{client: BlocklistGlobal.PluginBlocklistRS._client, testData: ["p1044", "p32", "p28"]}, {client: BlocklistGlobal.PluginBlocklistRS._client, testData: ["p1044", "p32", "p28"]},
{client: BlocklistGlobal.GfxBlocklistRS._client, testData: ["g204", "g200", "g36"]}, {client: BlocklistGlobal.GfxBlocklistRS._client, testData: ["g204", "g200", "g36"]},
]; ];
// Disable signature verification in these tests.
for (const c of gBlocklistClients) {
c.verifySignature = false;
}
// Setup server fake responses. // Setup server fake responses.
function handleResponse(request, response) { function handleResponse(request, response) {

View File

@ -12,7 +12,6 @@
.title { .title {
background-image: url("chrome://global/skin/icons/warning.svg"); background-image: url("chrome://global/skin/icons/warning.svg");
-moz-context-properties: fill;
fill: #fcd100; fill: #fcd100;
} }

View File

@ -45,6 +45,8 @@ body.wide-container {
margin-inline-start: -2.3em; margin-inline-start: -2.3em;
padding-inline-start: 2.3em; padding-inline-start: 2.3em;
font-size: 2.2em; font-size: 2.2em;
-moz-context-properties: fill;
fill: currentColor;
} }
.title:-moz-locale-dir(rtl), .title:-moz-locale-dir(rtl),