mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-21 17:25:36 +00:00
Merge mozilla-central to inbound. a=merge CLOSED TREE
This commit is contained in:
commit
3d78756bdb
@ -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);
|
||||
}
|
||||
});
|
@ -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);
|
||||
}
|
||||
});
|
20
browser/base/content/test/performance/io/browser.ini
Normal file
20
browser/base/content/test/performance/io/browser.ini
Normal 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]
|
@ -34,6 +34,7 @@ BROWSER_CHROME_MANIFESTS += [
|
||||
'content/test/pageinfo/browser.ini',
|
||||
'content/test/performance/browser.ini',
|
||||
'content/test/performance/hidpi/browser.ini',
|
||||
'content/test/performance/io/browser.ini',
|
||||
'content/test/performance/legacyurlbar/browser.ini',
|
||||
'content/test/performance/lowdpi/browser.ini',
|
||||
'content/test/permissions/browser.ini',
|
||||
|
@ -24,7 +24,6 @@ body.config-warning {
|
||||
|
||||
.title {
|
||||
background-image: url("chrome://global/skin/icons/warning.svg");
|
||||
-moz-context-properties: fill;
|
||||
fill: #fcd100;
|
||||
}
|
||||
|
||||
@ -97,6 +96,8 @@ body.config-warning {
|
||||
background-repeat: no-repeat;
|
||||
background-position: 9px center;
|
||||
background-size: 16px 16px;
|
||||
-moz-context-properties: fill;
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
#prefs > tr.locked:dir(rtl) {
|
||||
|
@ -37,7 +37,11 @@ NOTE: The collection and messages can also be created manually using the [admin
|
||||
|
||||
```javascript
|
||||
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.**
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
"name": "__MSG_extensionName__",
|
||||
"description": "__MSG_extensionDescription__",
|
||||
"manifest_version": 2,
|
||||
"version": "1.0",
|
||||
"version": "1.1",
|
||||
"applications": {
|
||||
"gecko": {
|
||||
"id": "amazon@search.mozilla.org"
|
||||
@ -24,4 +24,4 @@
|
||||
"search_url_get_params": "__MSG_searchUrlGetParams__"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
"name": "__MSG_extensionName__",
|
||||
"description": "__MSG_extensionDescription__",
|
||||
"manifest_version": 2,
|
||||
"version": "1.0",
|
||||
"version": "1.1",
|
||||
"applications": {
|
||||
"gecko": {
|
||||
"id": "amazondotcom@search.mozilla.org"
|
||||
@ -25,4 +25,4 @@
|
||||
"search_url_get_params": "__MSG_searchUrlGetParams__"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -214,14 +214,14 @@
|
||||
"de": {
|
||||
"default": {
|
||||
"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": {
|
||||
"default": {
|
||||
"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": {
|
||||
"default": {
|
||||
"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"
|
||||
]
|
||||
}
|
||||
},
|
||||
|
@ -26,18 +26,21 @@ var gSetBackground = {
|
||||
// regular screens seem to be 32:9 (3.56) according to Wikipedia.
|
||||
let screenRatio = Math.min(this._screenWidth / this._screenHeight, 4);
|
||||
this._canvas.width = this._canvas.height * screenRatio;
|
||||
document.getElementById("preview-unavailable").style.width =
|
||||
this._canvas.width + "px";
|
||||
|
||||
if (AppConstants.platform == "macosx") {
|
||||
document.documentElement.getButton("accept").hidden = true;
|
||||
} else {
|
||||
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 monitors = gfxInfo.getMonitors();
|
||||
multiMonitors = monitors.length > 1;
|
||||
} catch (e) {
|
||||
// getMonitors() isn't implemented on Linux
|
||||
multiMonitors = true;
|
||||
}
|
||||
|
||||
if (!multiMonitors || AppConstants.isPlatformAndVersionAtMost("win", 6.1)) {
|
||||
@ -101,6 +104,7 @@ var gSetBackground = {
|
||||
updatePosition() {
|
||||
var ctx = this._canvas.getContext("2d");
|
||||
ctx.clearRect(0, 0, this._screenWidth, this._screenHeight);
|
||||
document.getElementById("preview-unavailable").hidden = true;
|
||||
|
||||
if (AppConstants.platform != "macosx") {
|
||||
this._position = document.getElementById("menuPosition").value;
|
||||
@ -157,18 +161,9 @@ var gSetBackground = {
|
||||
break;
|
||||
}
|
||||
case "SPAN": {
|
||||
ctx.fillStyle = "black";
|
||||
document.getElementById("preview-unavailable").hidden = false;
|
||||
ctx.fillStyle = "#222";
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -64,7 +64,12 @@
|
||||
|
||||
<vbox align="center">
|
||||
<!-- default to 16:9, will be adjusted to match user's actual screen -->
|
||||
<html:canvas id="screen" width="202" height="114" role="presentation"/>
|
||||
<stack>
|
||||
<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"/>
|
||||
</vbox>
|
||||
|
||||
|
@ -63,6 +63,7 @@ startupRecorder.prototype = {
|
||||
if (!Services.prefs.getBoolPref("browser.startup.record", false))
|
||||
return;
|
||||
|
||||
Services.profiler.AddMarker("startupRecorder:" + name);
|
||||
this.data.code[name] = {
|
||||
components: Cu.loadedComponents,
|
||||
modules: Cu.loadedModules,
|
||||
@ -163,8 +164,24 @@ startupRecorder.prototype = {
|
||||
Services.prefs.readStats((key, value) => this.data.prefStats[key] = value);
|
||||
}
|
||||
paints = null;
|
||||
this._resolve();
|
||||
this._resolve = null;
|
||||
|
||||
let env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
|
||||
if (!env.exists("MOZ_PROFILER_STARTUP")) {
|
||||
this._resolve();
|
||||
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 {
|
||||
const topicsToNames = {
|
||||
|
@ -18,3 +18,4 @@
|
||||
<!ENTITY setDesktopBackground.title "Set Desktop Background">
|
||||
<!ENTITY openDesktopPrefs.label "Open Desktop Preferences">
|
||||
<!ENTITY closeWindow.key "w">
|
||||
<!ENTITY previewUnavailable "Preview unavailable">
|
||||
|
@ -14,8 +14,6 @@ body {
|
||||
|
||||
.title {
|
||||
background-image: url("chrome://global/skin/icons/blocked.svg");
|
||||
fill: currentColor;
|
||||
-moz-context-properties: fill;
|
||||
}
|
||||
|
||||
.title-text {
|
||||
|
@ -14,3 +14,10 @@ html|canvas#screen {
|
||||
#monitor-base {
|
||||
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;
|
||||
}
|
||||
|
@ -11,6 +11,8 @@ const Services = require("Services");
|
||||
|
||||
const { l10n } = require("../modules/l10n");
|
||||
|
||||
const { isSupportedDebugTargetPane } = require("../modules/debug-target-support");
|
||||
|
||||
const {
|
||||
openTemporaryExtension,
|
||||
uninstallAddon,
|
||||
@ -23,6 +25,7 @@ const {
|
||||
|
||||
const {
|
||||
DEBUG_TARGETS,
|
||||
DEBUG_TARGET_PANE,
|
||||
REQUEST_EXTENSIONS_FAILURE,
|
||||
REQUEST_EXTENSIONS_START,
|
||||
REQUEST_EXTENSIONS_SUCCESS,
|
||||
@ -162,10 +165,13 @@ function requestTabs() {
|
||||
return async (dispatch, getState) => {
|
||||
dispatch({ type: REQUEST_TABS_START });
|
||||
|
||||
const runtime = getCurrentRuntime(getState().runtimes);
|
||||
const clientWrapper = getCurrentClient(getState().runtimes);
|
||||
|
||||
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 });
|
||||
} catch (e) {
|
||||
|
@ -41,6 +41,24 @@ add_task(async function testThisFirefoxWithoutLocalTab() {
|
||||
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.
|
||||
*/
|
||||
@ -81,4 +99,3 @@ async function checkThisFirefoxTargetPanes(doc, expectedTargetPanes) {
|
||||
`Expected debug target category found: ${ expectedPaneTitle }`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -267,17 +267,20 @@ void ShadowRoot::RemoveSlot(HTMLSlotElement* aSlot) {
|
||||
|
||||
const bool wasFirstSlot = currentSlots->ElementAt(0) == aSlot;
|
||||
currentSlots.RemoveElement(*aSlot);
|
||||
|
||||
// Move assigned nodes from removed slot to the next slot in
|
||||
// tree order with the same name.
|
||||
if (!wasFirstSlot) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Move assigned nodes from removed slot to the next slot in
|
||||
// tree order with the same name.
|
||||
InvalidateStyleAndLayoutOnSubtree(aSlot);
|
||||
HTMLSlotElement* replacementSlot = currentSlots->ElementAt(0);
|
||||
const nsTArray<RefPtr<nsINode>>& assignedNodes = aSlot->AssignedNodes();
|
||||
bool slottedNodesChanged = !assignedNodes.IsEmpty();
|
||||
if (assignedNodes.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
InvalidateStyleAndLayoutOnSubtree(replacementSlot);
|
||||
while (!assignedNodes.IsEmpty()) {
|
||||
nsINode* assignedNode = assignedNodes[0];
|
||||
|
||||
@ -285,10 +288,8 @@ void ShadowRoot::RemoveSlot(HTMLSlotElement* aSlot) {
|
||||
replacementSlot->AppendAssignedNode(assignedNode);
|
||||
}
|
||||
|
||||
if (slottedNodesChanged) {
|
||||
aSlot->EnqueueSlotChangeEvent();
|
||||
replacementSlot->EnqueueSlotChangeEvent();
|
||||
}
|
||||
aSlot->EnqueueSlotChangeEvent();
|
||||
replacementSlot->EnqueueSlotChangeEvent();
|
||||
}
|
||||
|
||||
// FIXME(emilio): There's a bit of code duplication between this and the
|
||||
|
@ -62,13 +62,13 @@ class nsDocumentEncoder : public nsIDocumentEncoder {
|
||||
virtual ~nsDocumentEncoder();
|
||||
|
||||
void Initialize(bool aClearCachedSerializer = true);
|
||||
nsresult SerializeNodeStart(nsINode* aNode, int32_t aStartOffset,
|
||||
nsresult SerializeNodeStart(nsINode& aOriginalNode, int32_t aStartOffset,
|
||||
int32_t aEndOffset, nsAString& aStr,
|
||||
nsINode* aOriginalNode = nullptr);
|
||||
nsINode* aFixupNode = nullptr);
|
||||
nsresult SerializeToStringRecursive(nsINode* aNode, nsAString& aStr,
|
||||
bool aDontSerializeRoot,
|
||||
uint32_t aMaxLength = 0);
|
||||
nsresult SerializeNodeEnd(nsINode* aNode, nsAString& aStr);
|
||||
nsresult SerializeNodeEnd(nsINode& aNode, nsAString& aStr);
|
||||
// This serializes the content of aNode.
|
||||
nsresult SerializeToStringIterative(nsINode* aNode, nsAString& aStr);
|
||||
nsresult SerializeRangeToString(nsRange* aRange, nsAString& aOutputString);
|
||||
@ -296,34 +296,59 @@ nsDocumentEncoder::GetMimeType(nsAString& aMimeType) {
|
||||
|
||||
bool nsDocumentEncoder::IncludeInContext(nsINode* aNode) { return false; }
|
||||
|
||||
nsresult nsDocumentEncoder::SerializeNodeStart(nsINode* aNode,
|
||||
int32_t aStartOffset,
|
||||
int32_t aEndOffset,
|
||||
nsAString& aStr,
|
||||
nsINode* aOriginalNode) {
|
||||
if (mNeedsPreformatScanning && aNode->IsElement()) {
|
||||
mSerializer->ScanElementForPreformat(aNode->AsElement());
|
||||
}
|
||||
|
||||
if (!IsVisibleNode(aNode)) return NS_OK;
|
||||
|
||||
nsINode* node = nullptr;
|
||||
nsCOMPtr<nsINode> fixedNodeKungfuDeathGrip;
|
||||
|
||||
// Caller didn't do fixup, so we'll do it ourselves
|
||||
if (!aOriginalNode) {
|
||||
aOriginalNode = aNode;
|
||||
class FixupNodeDeterminer {
|
||||
public:
|
||||
FixupNodeDeterminer(nsIDocumentEncoderNodeFixup* aNodeFixup,
|
||||
nsINode* aFixupNode, nsINode& aOriginalNode)
|
||||
: mIsSerializationOfFixupChildrenNeeded{false},
|
||||
mNodeFixup(aNodeFixup),
|
||||
mOriginalNode(aOriginalNode) {
|
||||
if (mNodeFixup) {
|
||||
bool dummy;
|
||||
mNodeFixup->FixupNode(aNode, &dummy,
|
||||
getter_AddRefs(fixedNodeKungfuDeathGrip));
|
||||
node = fixedNodeKungfuDeathGrip;
|
||||
if (aFixupNode) {
|
||||
mFixupNode = aFixupNode;
|
||||
} else {
|
||||
mNodeFixup->FixupNode(&mOriginalNode,
|
||||
&mIsSerializationOfFixupChildrenNeeded,
|
||||
getter_AddRefs(mFixupNode));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Either there was no fixed-up node,
|
||||
// or the caller did fixup themselves and aNode is already fixed
|
||||
if (!node) node = aNode;
|
||||
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 aEndOffset,
|
||||
nsAString& aStr,
|
||||
nsINode* aFixupNode) {
|
||||
if (mNeedsPreformatScanning && aOriginalNode.IsElement()) {
|
||||
mSerializer->ScanElementForPreformat(aOriginalNode.AsElement());
|
||||
}
|
||||
|
||||
if (!IsVisibleNode(&aOriginalNode)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
FixupNodeDeterminer fixupNodeDeterminer{mNodeFixup, aFixupNode,
|
||||
aOriginalNode};
|
||||
nsINode* node = &fixupNodeDeterminer.GetFixupNodeFallBackToOriginalNode();
|
||||
|
||||
if (node->IsElement()) {
|
||||
if ((mFlags & (nsIDocumentEncoder::OutputPreformatted |
|
||||
@ -331,7 +356,7 @@ nsresult nsDocumentEncoder::SerializeNodeStart(nsINode* aNode,
|
||||
nsLayoutUtils::IsInvisibleBreak(node)) {
|
||||
return NS_OK;
|
||||
}
|
||||
Element* originalElement = Element::FromNodeOrNull(aOriginalNode);
|
||||
Element* originalElement = aOriginalNode.AsElement();
|
||||
mSerializer->AppendElementStart(node->AsElement(), originalElement, aStr);
|
||||
return NS_OK;
|
||||
}
|
||||
@ -367,15 +392,17 @@ nsresult nsDocumentEncoder::SerializeNodeStart(nsINode* aNode,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult nsDocumentEncoder::SerializeNodeEnd(nsINode* aNode, nsAString& aStr) {
|
||||
if (mNeedsPreformatScanning && aNode->IsElement()) {
|
||||
mSerializer->ForgetElementForPreformat(aNode->AsElement());
|
||||
nsresult nsDocumentEncoder::SerializeNodeEnd(nsINode& aNode, nsAString& aStr) {
|
||||
if (mNeedsPreformatScanning && aNode.IsElement()) {
|
||||
mSerializer->ForgetElementForPreformat(aNode.AsElement());
|
||||
}
|
||||
|
||||
if (!IsVisibleNode(aNode)) return NS_OK;
|
||||
if (!IsVisibleNode(&aNode)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (aNode->IsElement()) {
|
||||
mSerializer->AppendElementEnd(aNode->AsElement(), aStr);
|
||||
if (aNode.IsElement()) {
|
||||
mSerializer->AppendElementEnd(aNode.AsElement(), aStr);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
@ -391,18 +418,11 @@ nsresult nsDocumentEncoder::SerializeToStringRecursive(nsINode* aNode,
|
||||
if (!IsVisibleNode(aNode)) return NS_OK;
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
bool serializeClonedChildren = false;
|
||||
nsINode* maybeFixedNode = nullptr;
|
||||
|
||||
// Keep the node from FixupNode alive.
|
||||
nsCOMPtr<nsINode> fixedNodeKungfuDeathGrip;
|
||||
if (mNodeFixup) {
|
||||
mNodeFixup->FixupNode(aNode, &serializeClonedChildren,
|
||||
getter_AddRefs(fixedNodeKungfuDeathGrip));
|
||||
maybeFixedNode = fixedNodeKungfuDeathGrip;
|
||||
}
|
||||
|
||||
if (!maybeFixedNode) maybeFixedNode = aNode;
|
||||
MOZ_ASSERT(aNode, "aNode shouldn't be nullptr.");
|
||||
FixupNodeDeterminer fixupNodeDeterminer{mNodeFixup, nullptr, *aNode};
|
||||
nsINode* maybeFixedNode =
|
||||
&fixupNodeDeterminer.GetFixupNodeFallBackToOriginalNode();
|
||||
|
||||
if ((mFlags & SkipInvisibleContent) &&
|
||||
!(mFlags & OutputNonTextContentAsPlaceholder)) {
|
||||
@ -421,11 +441,13 @@ nsresult nsDocumentEncoder::SerializeToStringRecursive(nsINode* aNode,
|
||||
MOZ_ASSERT(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);
|
||||
}
|
||||
|
||||
nsINode* node = serializeClonedChildren ? maybeFixedNode : aNode;
|
||||
nsINode* node = fixupNodeDeterminer.IsSerializationOfFixupChildrenNeeded()
|
||||
? maybeFixedNode
|
||||
: aNode;
|
||||
|
||||
for (nsINode* child = nsNodeUtils::GetFirstChildOfTemplateOrNode(node); child;
|
||||
child = child->GetNextSibling()) {
|
||||
@ -434,7 +456,7 @@ nsresult nsDocumentEncoder::SerializeToStringRecursive(nsINode* aNode,
|
||||
}
|
||||
|
||||
if (!aDontSerializeRoot) {
|
||||
rv = SerializeNodeEnd(maybeFixedNode, aStr);
|
||||
rv = SerializeNodeEnd(*maybeFixedNode, aStr);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
@ -448,11 +470,11 @@ nsresult nsDocumentEncoder::SerializeToStringIterative(nsINode* aNode,
|
||||
nsINode* node = nsNodeUtils::GetFirstChildOfTemplateOrNode(aNode);
|
||||
while (node) {
|
||||
nsINode* current = node;
|
||||
rv = SerializeNodeStart(current, 0, -1, aStr, current);
|
||||
rv = SerializeNodeStart(*current, 0, -1, aStr, current);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
node = nsNodeUtils::GetFirstChildOfTemplateOrNode(current);
|
||||
while (!node && current && current != aNode) {
|
||||
rv = SerializeNodeEnd(current, aStr);
|
||||
rv = SerializeNodeEnd(*current, aStr);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
// Check if we have siblings.
|
||||
node = current->GetNextSibling();
|
||||
@ -577,14 +599,14 @@ nsresult nsDocumentEncoder::SerializeRangeNodes(nsRange* const aRange,
|
||||
if (IsTextNode(aNode)) {
|
||||
if (startNode == content) {
|
||||
int32_t startOffset = aRange->StartOffset();
|
||||
rv = SerializeNodeStart(aNode, startOffset, -1, aString);
|
||||
rv = SerializeNodeStart(*aNode, startOffset, -1, aString);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else {
|
||||
int32_t endOffset = aRange->EndOffset();
|
||||
rv = SerializeNodeStart(aNode, 0, endOffset, aString);
|
||||
rv = SerializeNodeStart(*aNode, 0, endOffset, aString);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
rv = SerializeNodeEnd(aNode, aString);
|
||||
rv = SerializeNodeEnd(*aNode, aString);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else {
|
||||
if (aNode != mCommonParent) {
|
||||
@ -597,7 +619,7 @@ nsresult nsDocumentEncoder::SerializeRangeNodes(nsRange* const aRange,
|
||||
if ((endNode == content) && !mHaltRangeHint) mEndDepth++;
|
||||
|
||||
// serialize the start of this node
|
||||
rv = SerializeNodeStart(aNode, 0, -1, aString);
|
||||
rv = SerializeNodeStart(*aNode, 0, -1, aString);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
@ -653,7 +675,7 @@ nsresult nsDocumentEncoder::SerializeRangeNodes(nsRange* const aRange,
|
||||
|
||||
// serialize the end of this node
|
||||
if (aNode != mCommonParent) {
|
||||
rv = SerializeNodeEnd(aNode, aString);
|
||||
rv = SerializeNodeEnd(*aNode, aString);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
}
|
||||
@ -682,7 +704,7 @@ nsresult nsDocumentEncoder::SerializeRangeContextStart(
|
||||
|
||||
// Either a general inclusion or as immediate context
|
||||
if (IncludeInContext(node) || i < j) {
|
||||
rv = SerializeNodeStart(node, 0, -1, aString);
|
||||
rv = SerializeNodeStart(*node, 0, -1, aString);
|
||||
serializedContext->AppendElement(node);
|
||||
if (NS_FAILED(rv)) break;
|
||||
}
|
||||
@ -702,7 +724,7 @@ nsresult nsDocumentEncoder::SerializeRangeContextEnd(nsAString& aString) {
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
for (nsINode* node : Reversed(serializedContext)) {
|
||||
rv = SerializeNodeEnd(node, aString);
|
||||
rv = SerializeNodeEnd(*node, aString);
|
||||
|
||||
if (NS_FAILED(rv)) break;
|
||||
}
|
||||
@ -759,10 +781,10 @@ nsresult nsDocumentEncoder::SerializeRangeToString(nsRange* aRange,
|
||||
if (!parent || !IsVisibleNode(parent)) return NS_OK;
|
||||
}
|
||||
}
|
||||
rv = SerializeNodeStart(startContainer, startOffset, endOffset,
|
||||
rv = SerializeNodeStart(*startContainer, startOffset, endOffset,
|
||||
aOutputString);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = SerializeNodeEnd(startContainer, aOutputString);
|
||||
rv = SerializeNodeEnd(*startContainer, aOutputString);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
} else {
|
||||
rv = SerializeRangeNodes(aRange, mCommonParent, aOutputString, 0);
|
||||
@ -861,7 +883,7 @@ nsDocumentEncoder::EncodeToStringWithMaxLength(uint32_t aMaxLength,
|
||||
NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
|
||||
if (node != prevNode) {
|
||||
if (prevNode) {
|
||||
rv = SerializeNodeEnd(prevNode, output);
|
||||
rv = SerializeNodeEnd(*prevNode, output);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(node);
|
||||
@ -878,7 +900,7 @@ nsDocumentEncoder::EncodeToStringWithMaxLength(uint32_t aMaxLength,
|
||||
mDisableContextSerialize = true;
|
||||
}
|
||||
|
||||
rv = SerializeNodeStart(node, 0, -1, output);
|
||||
rv = SerializeNodeStart(*node, 0, -1, output);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
prevNode = node;
|
||||
} else if (prevNode) {
|
||||
@ -899,7 +921,7 @@ nsDocumentEncoder::EncodeToStringWithMaxLength(uint32_t aMaxLength,
|
||||
mStartDepth = firstRangeStartDepth;
|
||||
|
||||
if (prevNode) {
|
||||
rv = SerializeNodeEnd(prevNode, output);
|
||||
rv = SerializeNodeEnd(*prevNode, output);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
mDisableContextSerialize = false;
|
||||
rv = SerializeRangeContextEnd(output);
|
||||
@ -1266,12 +1288,12 @@ nsHTMLCopyEncoder::EncodeToStringWithContext(nsAString& aContextString,
|
||||
i = count;
|
||||
while (i > 0) {
|
||||
node = mCommonAncestors.ElementAt(--i);
|
||||
SerializeNodeStart(node, 0, -1, aContextString);
|
||||
SerializeNodeStart(*node, 0, -1, aContextString);
|
||||
}
|
||||
// i = 0; guaranteed by above
|
||||
while (i < count) {
|
||||
node = mCommonAncestors.ElementAt(i++);
|
||||
SerializeNodeEnd(node, aContextString);
|
||||
SerializeNodeEnd(*node, aContextString);
|
||||
}
|
||||
|
||||
// encode range info : the start and end depth of the selection, where the
|
||||
|
@ -96,12 +96,7 @@ function check_mp4(v, enabled) {
|
||||
check(codec, "probably");
|
||||
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) {
|
||||
@ -143,10 +138,6 @@ function getPref(name) {
|
||||
return pref;
|
||||
}
|
||||
|
||||
function IsAndroid() {
|
||||
return getAndroidVersion() >= 0;
|
||||
}
|
||||
|
||||
function IsSupportedAndroid() {
|
||||
return getAndroidVersion() >= 14;
|
||||
}
|
||||
|
@ -123,30 +123,6 @@ using namespace widget;
|
||||
* 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()
|
||||
: mEditActionData(nullptr),
|
||||
mPlaceholderName(nullptr),
|
||||
@ -1342,9 +1318,8 @@ EditorBase::SetSpellcheckUserOverride(bool enable) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
template <typename PT, typename CT>
|
||||
already_AddRefed<Element> EditorBase::CreateNodeWithTransaction(
|
||||
nsAtom& aTagName, const EditorDOMPointBase<PT, CT>& aPointToInsert) {
|
||||
nsAtom& aTagName, const EditorDOMPoint& aPointToInsert) {
|
||||
MOZ_ASSERT(IsEditActionDataAvailable());
|
||||
|
||||
MOZ_ASSERT(aPointToInsert.IsSetAndValid());
|
||||
@ -1414,18 +1389,16 @@ EditorBase::InsertNode(nsINode* aNodeToInsert, nsINode* aContainer,
|
||||
aOffset < 0
|
||||
? static_cast<int32_t>(aContainer->Length())
|
||||
: std::min(aOffset, static_cast<int32_t>(aContainer->Length()));
|
||||
nsresult rv = InsertNodeWithTransaction(
|
||||
*contentToInsert, EditorRawDOMPoint(aContainer, offset));
|
||||
nsresult rv = InsertNodeWithTransaction(*contentToInsert,
|
||||
EditorDOMPoint(aContainer, offset));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return EditorBase::ToGenericNSResult(rv);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
template <typename PT, typename CT>
|
||||
nsresult EditorBase::InsertNodeWithTransaction(
|
||||
nsIContent& aContentToInsert,
|
||||
const EditorDOMPointBase<PT, CT>& aPointToInsert) {
|
||||
nsIContent& aContentToInsert, const EditorDOMPoint& aPointToInsert) {
|
||||
MOZ_ASSERT(IsEditActionDataAvailable());
|
||||
|
||||
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()));
|
||||
ErrorResult error;
|
||||
nsCOMPtr<nsIContent> newNode =
|
||||
SplitNodeWithTransaction(EditorRawDOMPoint(aNode, offset), error);
|
||||
SplitNodeWithTransaction(EditorDOMPoint(aNode, offset), error);
|
||||
newNode.forget(aNewLeftNode);
|
||||
if (NS_WARN_IF(error.Failed())) {
|
||||
return EditorBase::ToGenericNSResult(error.StealNSResult());
|
||||
@ -1480,9 +1453,8 @@ EditorBase::SplitNode(nsINode* aNode, int32_t aOffset, nsINode** aNewLeftNode) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
template <typename PT, typename CT>
|
||||
already_AddRefed<nsIContent> EditorBase::SplitNodeWithTransaction(
|
||||
const EditorDOMPointBase<PT, CT>& aStartOfRightNode, ErrorResult& aError) {
|
||||
const EditorDOMPoint& aStartOfRightNode, ErrorResult& aError) {
|
||||
MOZ_ASSERT(IsEditActionDataAvailable());
|
||||
|
||||
if (NS_WARN_IF(!aStartOfRightNode.IsSet()) ||
|
||||
@ -1712,7 +1684,7 @@ already_AddRefed<Element> EditorBase::ReplaceContainerWithTransactionInternal(
|
||||
}
|
||||
|
||||
rv = InsertNodeWithTransaction(
|
||||
*child, EditorRawDOMPoint(newContainer, newContainer->Length()));
|
||||
*child, EditorDOMPoint(newContainer, newContainer->Length()));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -1764,8 +1736,8 @@ nsresult EditorBase::RemoveContainerWithTransaction(Element& aElement) {
|
||||
// use offset here because previous child might have been moved to
|
||||
// container.
|
||||
rv = InsertNodeWithTransaction(
|
||||
*child, EditorRawDOMPoint(pointToInsertChildren.GetContainer(),
|
||||
pointToInsertChildren.Offset()));
|
||||
*child, EditorDOMPoint(pointToInsertChildren.GetContainer(),
|
||||
pointToInsertChildren.Offset()));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
@ -1820,8 +1792,7 @@ already_AddRefed<Element> EditorBase::InsertContainerWithTransactionInternal(
|
||||
|
||||
{
|
||||
AutoTransactionsConserveSelection conserveSelection(*this);
|
||||
rv =
|
||||
InsertNodeWithTransaction(aContent, EditorRawDOMPoint(newContainer, 0));
|
||||
rv = InsertNodeWithTransaction(aContent, EditorDOMPoint(newContainer, 0));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -1836,9 +1807,8 @@ already_AddRefed<Element> EditorBase::InsertContainerWithTransactionInternal(
|
||||
return newContainer.forget();
|
||||
}
|
||||
|
||||
template <typename PT, typename CT>
|
||||
nsresult EditorBase::MoveNodeWithTransaction(
|
||||
nsIContent& aContent, const EditorDOMPointBase<PT, CT>& aPointToInsert) {
|
||||
nsIContent& aContent, const EditorDOMPoint& aPointToInsert) {
|
||||
MOZ_ASSERT(aPointToInsert.IsSetAndValid());
|
||||
|
||||
EditorDOMPoint oldPoint(&aContent);
|
||||
@ -1852,8 +1822,7 @@ nsresult EditorBase::MoveNodeWithTransaction(
|
||||
}
|
||||
|
||||
// Notify our internal selection state listener
|
||||
EditorDOMPoint newPoint(aPointToInsert);
|
||||
AutoMoveNodeSelNotify selNotify(RangeUpdaterRef(), oldPoint, newPoint);
|
||||
AutoMoveNodeSelNotify selNotify(RangeUpdaterRef(), oldPoint, aPointToInsert);
|
||||
|
||||
// Hold a reference so aNode doesn't go away when we remove it (bug 772282)
|
||||
nsresult rv = DeleteNodeWithTransaction(aContent);
|
||||
@ -1862,7 +1831,7 @@ nsresult EditorBase::MoveNodeWithTransaction(
|
||||
}
|
||||
|
||||
// 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())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
@ -3728,10 +3697,9 @@ bool EditorBase::IsPreformatted(nsINode* aNode) {
|
||||
return styleText->WhiteSpaceIsSignificant();
|
||||
}
|
||||
|
||||
template <typename PT, typename CT>
|
||||
SplitNodeResult EditorBase::SplitNodeDeepWithTransaction(
|
||||
nsIContent& aMostAncestorToSplit,
|
||||
const EditorDOMPointBase<PT, CT>& aStartOfDeepestRightNode,
|
||||
const EditorDOMPoint& aStartOfDeepestRightNode,
|
||||
SplitAtEdges aSplitAtEdges) {
|
||||
MOZ_ASSERT(aStartOfDeepestRightNode.IsSetAndValid());
|
||||
MOZ_ASSERT(
|
||||
|
@ -936,10 +936,8 @@ class EditorBase : public nsIEditor,
|
||||
* container. Otherwise, will insert the node
|
||||
* before child node referred by this.
|
||||
*/
|
||||
template <typename PT, typename CT>
|
||||
MOZ_CAN_RUN_SCRIPT nsresult
|
||||
InsertNodeWithTransaction(nsIContent& aContentToInsert,
|
||||
const EditorDOMPointBase<PT, CT>& aPointToInsert);
|
||||
MOZ_CAN_RUN_SCRIPT nsresult InsertNodeWithTransaction(
|
||||
nsIContent& aContentToInsert, const EditorDOMPoint& aPointToInsert);
|
||||
|
||||
/**
|
||||
* ReplaceContainerWithTransaction() creates new element whose name is
|
||||
@ -1071,10 +1069,8 @@ class EditorBase : public nsIEditor,
|
||||
* @param aError If succeed, returns no error. Otherwise, an
|
||||
* error.
|
||||
*/
|
||||
template <typename PT, typename CT>
|
||||
MOZ_CAN_RUN_SCRIPT already_AddRefed<nsIContent> SplitNodeWithTransaction(
|
||||
const EditorDOMPointBase<PT, CT>& aStartOfRightNode,
|
||||
ErrorResult& aResult);
|
||||
const EditorDOMPoint& aStartOfRightNode, ErrorResult& aResult);
|
||||
|
||||
/**
|
||||
* JoinNodesWithTransaction() joins aLeftNode and aRightNode. Content of
|
||||
@ -1093,9 +1089,8 @@ class EditorBase : public nsIEditor,
|
||||
*
|
||||
* @param aContent The node to be moved.
|
||||
*/
|
||||
template <typename PT, typename CT>
|
||||
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.
|
||||
@ -1107,7 +1102,7 @@ class EditorBase : public nsIEditor,
|
||||
MOZ_CAN_RUN_SCRIPT
|
||||
nsresult MoveNodeToEndWithTransaction(nsIContent& aContent,
|
||||
nsINode& aNewContainer) {
|
||||
EditorRawDOMPoint pointToInsert;
|
||||
EditorDOMPoint pointToInsert;
|
||||
pointToInsert.SetToEndOf(&aNewContainer);
|
||||
return MoveNodeWithTransaction(aContent, pointToInsert);
|
||||
}
|
||||
@ -1243,9 +1238,8 @@ class EditorBase : public nsIEditor,
|
||||
* child node referred by this.
|
||||
* @return The created new element node.
|
||||
*/
|
||||
template <typename PT, typename CT>
|
||||
already_AddRefed<Element> CreateNodeWithTransaction(
|
||||
nsAtom& aTag, const EditorDOMPointBase<PT, CT>& aPointToInsert);
|
||||
MOZ_CAN_RUN_SCRIPT already_AddRefed<Element> CreateNodeWithTransaction(
|
||||
nsAtom& aTag, const EditorDOMPoint& aPointToInsert);
|
||||
|
||||
/**
|
||||
* 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 aLength Length of removing text.
|
||||
*/
|
||||
MOZ_CAN_RUN_SCRIPT
|
||||
nsresult DeleteTextWithTransaction(dom::CharacterData& aCharacterData,
|
||||
uint32_t aOffset, uint32_t aLength);
|
||||
|
||||
@ -1389,11 +1384,10 @@ class EditorBase : public nsIEditor,
|
||||
* be good to insert something if the
|
||||
* caller want to do it.
|
||||
*/
|
||||
template <typename PT, typename CT>
|
||||
MOZ_CAN_RUN_SCRIPT SplitNodeResult SplitNodeDeepWithTransaction(
|
||||
nsIContent& aMostAncestorToSplit,
|
||||
const EditorDOMPointBase<PT, CT>& aDeepestStartOfRightNode,
|
||||
SplitAtEdges aSplitAtEdges);
|
||||
MOZ_CAN_RUN_SCRIPT SplitNodeResult
|
||||
SplitNodeDeepWithTransaction(nsIContent& aMostAncestorToSplit,
|
||||
const EditorDOMPoint& aDeepestStartOfRightNode,
|
||||
SplitAtEdges aSplitAtEdges);
|
||||
|
||||
/**
|
||||
* JoinNodesDeepWithTransaction() joins aLeftNode and aRightNode "deeply".
|
||||
|
@ -240,17 +240,17 @@ class MOZ_STACK_CLASS SplitNodeResult final {
|
||||
* by this instance. Therefore, the life time of both container node
|
||||
* and child node are guaranteed while using the result temporarily.
|
||||
*/
|
||||
EditorRawDOMPoint SplitPoint() const {
|
||||
EditorDOMPoint SplitPoint() const {
|
||||
if (Failed()) {
|
||||
return EditorRawDOMPoint();
|
||||
return EditorDOMPoint();
|
||||
}
|
||||
if (mGivenSplitPoint.IsSet()) {
|
||||
return EditorRawDOMPoint(mGivenSplitPoint);
|
||||
return EditorDOMPoint(mGivenSplitPoint);
|
||||
}
|
||||
if (!mPreviousNode) {
|
||||
return EditorRawDOMPoint(mNextNode);
|
||||
return EditorDOMPoint(mNextNode);
|
||||
}
|
||||
EditorRawDOMPoint point(mPreviousNode);
|
||||
EditorDOMPoint point(mPreviousNode);
|
||||
DebugOnly<bool> advanced = point.AdvanceOffset();
|
||||
NS_WARNING_ASSERTION(advanced,
|
||||
"Failed to advance offset to after previous node");
|
||||
|
@ -519,7 +519,7 @@ nsresult HTMLEditor::SetPositionToAbsolute(Element& aElement) {
|
||||
nsINode* parentNode = aElement.GetParentNode();
|
||||
if (parentNode->GetChildCount() == 1) {
|
||||
RefPtr<Element> newBrElement =
|
||||
InsertBrElementWithTransaction(EditorRawDOMPoint(parentNode, 0));
|
||||
InsertBrElementWithTransaction(EditorDOMPoint(parentNode, 0));
|
||||
if (NS_WARN_IF(!newBrElement)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
@ -1833,7 +1833,7 @@ EditActionResult HTMLEditRules::WillInsertParagraphSeparator() {
|
||||
// make block have a line. Then code further below will put in a second br.)
|
||||
if (IsEmptyBlockElement(*blockParent, IgnoreSingleBR::eNo)) {
|
||||
AutoEditorDOMPointChildInvalidator lockOffset(atStartOfSelection);
|
||||
EditorRawDOMPoint endOfBlockParent;
|
||||
EditorDOMPoint endOfBlockParent;
|
||||
endOfBlockParent.SetToEndOf(blockParent);
|
||||
RefPtr<Element> brElement =
|
||||
MOZ_KnownLive(HTMLEditorRef())
|
||||
@ -2060,7 +2060,7 @@ nsresult HTMLEditRules::InsertBRElement(const EditorDOMPoint& aPointToBreak) {
|
||||
EditActionResult HTMLEditRules::SplitMailCites() {
|
||||
MOZ_ASSERT(IsEditorDataAvailable());
|
||||
|
||||
EditorRawDOMPoint pointToSplit(EditorBase::GetStartPoint(*SelectionRefPtr()));
|
||||
EditorDOMPoint pointToSplit(EditorBase::GetStartPoint(*SelectionRefPtr()));
|
||||
if (NS_WARN_IF(!pointToSplit.IsSet())) {
|
||||
return EditActionIgnored(NS_ERROR_FAILURE);
|
||||
}
|
||||
@ -2128,7 +2128,7 @@ EditActionResult HTMLEditRules::SplitMailCites() {
|
||||
nsCOMPtr<nsINode> lastChild = previousNodeOfSplitPoint->GetLastChild();
|
||||
if (lastChild && !lastChild->IsHTMLElement(nsGkAtoms::br)) {
|
||||
// We ignore the result here.
|
||||
EditorRawDOMPoint endOfPreviousNodeOfSplitPoint;
|
||||
EditorDOMPoint endOfPreviousNodeOfSplitPoint;
|
||||
endOfPreviousNodeOfSplitPoint.SetToEndOf(previousNodeOfSplitPoint);
|
||||
RefPtr<Element> invisibleBrElement =
|
||||
MOZ_KnownLive(HTMLEditorRef())
|
||||
@ -2144,7 +2144,7 @@ EditActionResult HTMLEditRules::SplitMailCites() {
|
||||
// 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
|
||||
// cite node, <br> should be inserted before the current cite.
|
||||
EditorRawDOMPoint pointToInsertBrNode(splitCiteNodeResult.SplitPoint());
|
||||
EditorDOMPoint pointToInsertBrNode(splitCiteNodeResult.SplitPoint());
|
||||
RefPtr<Element> brElement =
|
||||
MOZ_KnownLive(HTMLEditorRef())
|
||||
.InsertBrElementWithTransaction(pointToInsertBrNode);
|
||||
@ -2179,8 +2179,8 @@ EditActionResult HTMLEditRules::SplitMailCites() {
|
||||
// then we will need a 2nd br added to achieve blank line that user expects.
|
||||
if (IsInlineNode(*citeNode)) {
|
||||
// Use DOM point which we tried to collapse to.
|
||||
EditorRawDOMPoint pointToCreateNewBrNode(atBrNode.GetContainer(),
|
||||
atBrNode.Offset());
|
||||
EditorDOMPoint pointToCreateNewBrNode(atBrNode.GetContainer(),
|
||||
atBrNode.Offset());
|
||||
|
||||
WSRunObject wsObj(&HTMLEditorRef(), pointToCreateNewBrNode);
|
||||
WSType wsType;
|
||||
@ -2457,8 +2457,9 @@ nsresult HTMLEditRules::WillDeleteSelection(
|
||||
return rv;
|
||||
}
|
||||
*aHandled = true;
|
||||
rv = HTMLEditorRef().DeleteTextWithTransaction(
|
||||
nodeAsText, std::min(so, eo), DeprecatedAbs(eo - so));
|
||||
rv = MOZ_KnownLive(HTMLEditorRef())
|
||||
.DeleteTextWithTransaction(nodeAsText, std::min(so, eo),
|
||||
DeprecatedAbs(eo - so));
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
@ -3053,8 +3054,10 @@ nsresult HTMLEditRules::WillDeleteSelection(
|
||||
// Delete to last character
|
||||
OwningNonNull<CharacterData> dataNode =
|
||||
*static_cast<CharacterData*>(startNode.get());
|
||||
rv = HTMLEditorRef().DeleteTextWithTransaction(
|
||||
dataNode, startOffset, startNode->Length() - startOffset);
|
||||
rv =
|
||||
MOZ_KnownLive(HTMLEditorRef())
|
||||
.DeleteTextWithTransaction(dataNode, startOffset,
|
||||
startNode->Length() - startOffset);
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
@ -3066,8 +3069,8 @@ nsresult HTMLEditRules::WillDeleteSelection(
|
||||
// Delete to first character
|
||||
OwningNonNull<CharacterData> dataNode =
|
||||
*static_cast<CharacterData*>(endNode.get());
|
||||
rv =
|
||||
HTMLEditorRef().DeleteTextWithTransaction(dataNode, 0, endOffset);
|
||||
rv = MOZ_KnownLive(HTMLEditorRef())
|
||||
.DeleteTextWithTransaction(dataNode, 0, endOffset);
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
@ -3165,7 +3168,7 @@ nsresult HTMLEditRules::DeleteNodeIfCollapsedText(nsINode& aNode) {
|
||||
nsresult HTMLEditRules::InsertBRIfNeeded() {
|
||||
MOZ_ASSERT(IsEditorDataAvailable());
|
||||
|
||||
EditorRawDOMPoint atStartOfSelection(
|
||||
EditorDOMPoint atStartOfSelection(
|
||||
EditorBase::GetStartPoint(*SelectionRefPtr()));
|
||||
if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
@ -3684,7 +3687,7 @@ EditActionResult HTMLEditRules::MoveNodeSmart(nsIContent& aNode,
|
||||
return EditActionIgnored(rv);
|
||||
}
|
||||
} else {
|
||||
EditorRawDOMPoint pointToInsert(&aDestElement, *aInOutDestOffset);
|
||||
EditorDOMPoint pointToInsert(&aDestElement, *aInOutDestOffset);
|
||||
nsresult rv = MOZ_KnownLive(HTMLEditorRef())
|
||||
.MoveNodeWithTransaction(aNode, pointToInsert);
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
@ -3958,8 +3961,10 @@ nsresult HTMLEditRules::MakeList(nsAtom& aListType, bool aEntireList,
|
||||
if (NS_WARN_IF(splitAtSelectionStartResult.Failed())) {
|
||||
return splitAtSelectionStartResult.Rv();
|
||||
}
|
||||
RefPtr<Element> theList = HTMLEditorRef().CreateNodeWithTransaction(
|
||||
aListType, splitAtSelectionStartResult.SplitPoint());
|
||||
RefPtr<Element> theList =
|
||||
MOZ_KnownLive(HTMLEditorRef())
|
||||
.CreateNodeWithTransaction(
|
||||
aListType, splitAtSelectionStartResult.SplitPoint());
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
@ -3967,9 +3972,9 @@ nsresult HTMLEditRules::MakeList(nsAtom& aListType, bool aEntireList,
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
EditorRawDOMPoint atFirstListItemToInsertBefore(theList, 0);
|
||||
RefPtr<Element> theListItem = HTMLEditorRef().CreateNodeWithTransaction(
|
||||
aItemType, atFirstListItemToInsertBefore);
|
||||
RefPtr<Element> theListItem =
|
||||
MOZ_KnownLive(HTMLEditorRef())
|
||||
.CreateNodeWithTransaction(aItemType, EditorDOMPoint(theList, 0));
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
@ -4079,7 +4084,7 @@ nsresult HTMLEditRules::MakeList(nsAtom& aListType, bool aEntireList,
|
||||
continue;
|
||||
}
|
||||
|
||||
EditorRawDOMPoint atCurNode(curNode);
|
||||
EditorDOMPoint atCurNode(curNode);
|
||||
if (NS_WARN_IF(!atCurNode.IsSet())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
@ -4104,9 +4109,10 @@ nsresult HTMLEditRules::MakeList(nsAtom& aListType, bool aEntireList,
|
||||
return error.StealNSResult();
|
||||
}
|
||||
newBlock = newLeftNode ? newLeftNode->AsElement() : nullptr;
|
||||
EditorRawDOMPoint atParentOfCurNode(atCurNode.GetContainer());
|
||||
curList = HTMLEditorRef().CreateNodeWithTransaction(
|
||||
aListType, atParentOfCurNode);
|
||||
curList =
|
||||
MOZ_KnownLive(HTMLEditorRef())
|
||||
.CreateNodeWithTransaction(
|
||||
aListType, EditorDOMPoint(atCurNode.GetContainer()));
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
@ -4216,8 +4222,9 @@ nsresult HTMLEditRules::MakeList(nsAtom& aListType, bool aEntireList,
|
||||
if (NS_WARN_IF(splitCurNodeResult.Failed())) {
|
||||
return splitCurNodeResult.Rv();
|
||||
}
|
||||
curList = HTMLEditorRef().CreateNodeWithTransaction(
|
||||
aListType, splitCurNodeResult.SplitPoint());
|
||||
curList = MOZ_KnownLive(HTMLEditorRef())
|
||||
.CreateNodeWithTransaction(aListType,
|
||||
splitCurNodeResult.SplitPoint());
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
@ -4467,7 +4474,7 @@ nsresult HTMLEditRules::MakeBasicBlock(nsAtom& blockType) {
|
||||
if (NS_WARN_IF(splitNodeResult.Failed())) {
|
||||
return splitNodeResult.Rv();
|
||||
}
|
||||
EditorRawDOMPoint pointToInsertBrNode(splitNodeResult.SplitPoint());
|
||||
EditorDOMPoint pointToInsertBrNode(splitNodeResult.SplitPoint());
|
||||
// Put a <br> element at the split point
|
||||
brContent = MOZ_KnownLive(HTMLEditorRef())
|
||||
.InsertBrElementWithTransaction(pointToInsertBrNode);
|
||||
@ -4515,8 +4522,9 @@ nsresult HTMLEditRules::MakeBasicBlock(nsAtom& blockType) {
|
||||
if (NS_WARN_IF(splitNodeResult.Failed())) {
|
||||
return splitNodeResult.Rv();
|
||||
}
|
||||
RefPtr<Element> block = HTMLEditorRef().CreateNodeWithTransaction(
|
||||
blockType, splitNodeResult.SplitPoint());
|
||||
RefPtr<Element> block =
|
||||
MOZ_KnownLive(HTMLEditorRef())
|
||||
.CreateNodeWithTransaction(blockType, splitNodeResult.SplitPoint());
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
@ -4706,8 +4714,10 @@ nsresult HTMLEditRules::IndentAroundSelectionWithCSS() {
|
||||
if (NS_WARN_IF(splitNodeResult.Failed())) {
|
||||
return splitNodeResult.Rv();
|
||||
}
|
||||
RefPtr<Element> theBlock = HTMLEditorRef().CreateNodeWithTransaction(
|
||||
*nsGkAtoms::div, splitNodeResult.SplitPoint());
|
||||
RefPtr<Element> theBlock =
|
||||
MOZ_KnownLive(HTMLEditorRef())
|
||||
.CreateNodeWithTransaction(*nsGkAtoms::div,
|
||||
splitNodeResult.SplitPoint());
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
@ -4779,7 +4789,7 @@ nsresult HTMLEditRules::IndentAroundSelectionWithCSS() {
|
||||
nsresult rv =
|
||||
MOZ_KnownLive(HTMLEditorRef())
|
||||
.MoveNodeWithTransaction(MOZ_KnownLive(*curNode->AsContent()),
|
||||
EditorRawDOMPoint(sibling, 0));
|
||||
EditorDOMPoint(sibling, 0));
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
@ -4827,8 +4837,9 @@ nsresult HTMLEditRules::IndentAroundSelectionWithCSS() {
|
||||
if (NS_WARN_IF(splitNodeResult.Failed())) {
|
||||
return splitNodeResult.Rv();
|
||||
}
|
||||
curList = HTMLEditorRef().CreateNodeWithTransaction(
|
||||
*containerName, splitNodeResult.SplitPoint());
|
||||
curList = MOZ_KnownLive(HTMLEditorRef())
|
||||
.CreateNodeWithTransaction(MOZ_KnownLive(*containerName),
|
||||
splitNodeResult.SplitPoint());
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
@ -4878,8 +4889,9 @@ nsresult HTMLEditRules::IndentAroundSelectionWithCSS() {
|
||||
if (NS_WARN_IF(splitNodeResult.Failed())) {
|
||||
return splitNodeResult.Rv();
|
||||
}
|
||||
curQuote = HTMLEditorRef().CreateNodeWithTransaction(
|
||||
*nsGkAtoms::div, splitNodeResult.SplitPoint());
|
||||
curQuote = MOZ_KnownLive(HTMLEditorRef())
|
||||
.CreateNodeWithTransaction(*nsGkAtoms::div,
|
||||
splitNodeResult.SplitPoint());
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
@ -4985,8 +4997,10 @@ nsresult HTMLEditRules::IndentAroundSelectionWithHTML() {
|
||||
if (NS_WARN_IF(splitNodeResult.Failed())) {
|
||||
return splitNodeResult.Rv();
|
||||
}
|
||||
RefPtr<Element> theBlock = HTMLEditorRef().CreateNodeWithTransaction(
|
||||
*nsGkAtoms::blockquote, splitNodeResult.SplitPoint());
|
||||
RefPtr<Element> theBlock =
|
||||
MOZ_KnownLive(HTMLEditorRef())
|
||||
.CreateNodeWithTransaction(*nsGkAtoms::blockquote,
|
||||
splitNodeResult.SplitPoint());
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
@ -5051,7 +5065,7 @@ nsresult HTMLEditRules::IndentAroundSelectionWithHTML() {
|
||||
sibling->NodeInfo()->NamespaceID()) {
|
||||
rv = MOZ_KnownLive(HTMLEditorRef())
|
||||
.MoveNodeWithTransaction(MOZ_KnownLive(*curNode->AsContent()),
|
||||
EditorRawDOMPoint(sibling, 0));
|
||||
EditorDOMPoint(sibling, 0));
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
@ -5099,8 +5113,9 @@ nsresult HTMLEditRules::IndentAroundSelectionWithHTML() {
|
||||
if (NS_WARN_IF(splitNodeResult.Failed())) {
|
||||
return splitNodeResult.Rv();
|
||||
}
|
||||
curList = HTMLEditorRef().CreateNodeWithTransaction(
|
||||
*containerName, splitNodeResult.SplitPoint());
|
||||
curList = MOZ_KnownLive(HTMLEditorRef())
|
||||
.CreateNodeWithTransaction(MOZ_KnownLive(*containerName),
|
||||
splitNodeResult.SplitPoint());
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
@ -5161,8 +5176,9 @@ nsresult HTMLEditRules::IndentAroundSelectionWithHTML() {
|
||||
if (NS_WARN_IF(splitNodeResult.Failed())) {
|
||||
return splitNodeResult.Rv();
|
||||
}
|
||||
curList = HTMLEditorRef().CreateNodeWithTransaction(
|
||||
*containerName, splitNodeResult.SplitPoint());
|
||||
curList = MOZ_KnownLive(HTMLEditorRef())
|
||||
.CreateNodeWithTransaction(MOZ_KnownLive(*containerName),
|
||||
splitNodeResult.SplitPoint());
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
@ -5207,8 +5223,9 @@ nsresult HTMLEditRules::IndentAroundSelectionWithHTML() {
|
||||
if (NS_WARN_IF(splitNodeResult.Failed())) {
|
||||
return splitNodeResult.Rv();
|
||||
}
|
||||
curQuote = HTMLEditorRef().CreateNodeWithTransaction(
|
||||
*nsGkAtoms::blockquote, splitNodeResult.SplitPoint());
|
||||
curQuote = MOZ_KnownLive(HTMLEditorRef())
|
||||
.CreateNodeWithTransaction(*nsGkAtoms::blockquote,
|
||||
splitNodeResult.SplitPoint());
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
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
|
||||
// list. Be sure to put it after the parent list because this
|
||||
// loop iterates backwards through the parent's list of children.
|
||||
EditorRawDOMPoint afterCurrentList(curParent, offset + 1);
|
||||
EditorDOMPoint afterCurrentList(curParent, offset + 1);
|
||||
rv = MOZ_KnownLive(HTMLEditorRef())
|
||||
.MoveNodeWithTransaction(*child, afterCurrentList);
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
@ -5656,7 +5673,7 @@ SplitRangeOffFromNodeResult HTMLEditRules::SplitRangeOffFromBlock(
|
||||
SplitNodeResult splitAtStartResult =
|
||||
MOZ_KnownLive(HTMLEditorRef())
|
||||
.SplitNodeDeepWithTransaction(
|
||||
aBlockElement, EditorRawDOMPoint(&aStartOfMiddleElement),
|
||||
aBlockElement, EditorDOMPoint(&aStartOfMiddleElement),
|
||||
SplitAtEdges::eDoNotCreateEmptyContainer);
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return SplitRangeOffFromNodeResult(NS_ERROR_EDITOR_DESTROYED);
|
||||
@ -5665,7 +5682,7 @@ SplitRangeOffFromNodeResult HTMLEditRules::SplitRangeOffFromBlock(
|
||||
"Failed to split aBlockElement at start");
|
||||
|
||||
// Split at after the end
|
||||
EditorRawDOMPoint atAfterEnd(&aEndOfMiddleElement);
|
||||
EditorDOMPoint atAfterEnd(&aEndOfMiddleElement);
|
||||
DebugOnly<bool> advanced = atAfterEnd.AdvanceOffset();
|
||||
NS_WARNING_ASSERTION(advanced, "Failed to advance offset after the end node");
|
||||
SplitNodeResult splitAtEndResult =
|
||||
@ -5826,7 +5843,7 @@ nsresult HTMLEditRules::CreateStyleForInsertText(Document& aDocument) {
|
||||
SplitNodeResult splitTextNodeResult =
|
||||
MOZ_KnownLive(HTMLEditorRef())
|
||||
.SplitNodeDeepWithTransaction(
|
||||
*text, EditorRawDOMPoint(text, offset),
|
||||
*text, EditorDOMPoint(text, offset),
|
||||
SplitAtEdges::eAllowToCreateEmptyContainer);
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
@ -5843,9 +5860,9 @@ nsresult HTMLEditRules::CreateStyleForInsertText(Document& aDocument) {
|
||||
}
|
||||
OwningNonNull<Text> newNode =
|
||||
EditorBase::CreateTextNode(aDocument, EmptyString());
|
||||
nsresult rv = MOZ_KnownLive(HTMLEditorRef())
|
||||
.InsertNodeWithTransaction(
|
||||
*newNode, EditorRawDOMPoint(node, offset));
|
||||
nsresult rv =
|
||||
MOZ_KnownLive(HTMLEditorRef())
|
||||
.InsertNodeWithTransaction(*newNode, EditorDOMPoint(node, offset));
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
@ -6054,8 +6071,9 @@ nsresult HTMLEditRules::AlignContentsAtSelection(const nsAString& aAlignType) {
|
||||
}
|
||||
}
|
||||
}
|
||||
RefPtr<Element> div = HTMLEditorRef().CreateNodeWithTransaction(
|
||||
*nsGkAtoms::div, pointToInsertDiv);
|
||||
RefPtr<Element> div =
|
||||
MOZ_KnownLive(HTMLEditorRef())
|
||||
.CreateNodeWithTransaction(*nsGkAtoms::div, pointToInsertDiv);
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
@ -6070,8 +6088,7 @@ nsresult HTMLEditRules::AlignContentsAtSelection(const nsAString& aAlignType) {
|
||||
return rv;
|
||||
}
|
||||
// Put in a moz-br so that it won't get deleted
|
||||
CreateElementResult createMozBrResult =
|
||||
CreateMozBR(EditorRawDOMPoint(div, 0));
|
||||
CreateElementResult createMozBrResult = CreateMozBR(EditorDOMPoint(div, 0));
|
||||
if (NS_WARN_IF(createMozBrResult.Failed())) {
|
||||
return createMozBrResult.Rv();
|
||||
}
|
||||
@ -6193,8 +6210,9 @@ nsresult HTMLEditRules::AlignContentsAtSelection(const nsAString& aAlignType) {
|
||||
if (NS_WARN_IF(splitNodeResult.Failed())) {
|
||||
return splitNodeResult.Rv();
|
||||
}
|
||||
curDiv = HTMLEditorRef().CreateNodeWithTransaction(
|
||||
*nsGkAtoms::div, splitNodeResult.SplitPoint());
|
||||
curDiv = MOZ_KnownLive(HTMLEditorRef())
|
||||
.CreateNodeWithTransaction(*nsGkAtoms::div,
|
||||
splitNodeResult.SplitPoint());
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
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
|
||||
// children
|
||||
EditorRawDOMPoint atStartOfNode(&aNode, 0);
|
||||
RefPtr<Element> divElem =
|
||||
HTMLEditorRef().CreateNodeWithTransaction(*nsGkAtoms::div, atStartOfNode);
|
||||
RefPtr<Element> divElem = MOZ_KnownLive(HTMLEditorRef())
|
||||
.CreateNodeWithTransaction(
|
||||
*nsGkAtoms::div, EditorDOMPoint(&aNode, 0));
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
@ -6300,7 +6318,7 @@ nsresult HTMLEditRules::AlignBlockContents(nsINode& aNode,
|
||||
while (lastChild && (lastChild != divElem)) {
|
||||
nsresult rv =
|
||||
MOZ_KnownLive(HTMLEditorRef())
|
||||
.MoveNodeWithTransaction(*lastChild, EditorRawDOMPoint(divElem, 0));
|
||||
.MoveNodeWithTransaction(*lastChild, EditorDOMPoint(divElem, 0));
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
@ -7471,8 +7489,7 @@ nsresult HTMLEditRules::BustUpInlinesAtRangeEndpoints(RangeItem& aRangeItem) {
|
||||
MOZ_KnownLive(HTMLEditorRef())
|
||||
.SplitNodeDeepWithTransaction(
|
||||
*endInline,
|
||||
EditorRawDOMPoint(aRangeItem.mEndContainer,
|
||||
aRangeItem.mEndOffset),
|
||||
EditorDOMPoint(aRangeItem.mEndContainer, aRangeItem.mEndOffset),
|
||||
SplitAtEdges::eDoNotCreateEmptyContainer);
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
@ -7496,8 +7513,8 @@ nsresult HTMLEditRules::BustUpInlinesAtRangeEndpoints(RangeItem& aRangeItem) {
|
||||
MOZ_KnownLive(HTMLEditorRef())
|
||||
.SplitNodeDeepWithTransaction(
|
||||
*startInline,
|
||||
EditorRawDOMPoint(aRangeItem.mStartContainer,
|
||||
aRangeItem.mStartOffset),
|
||||
EditorDOMPoint(aRangeItem.mStartContainer,
|
||||
aRangeItem.mStartOffset),
|
||||
SplitAtEdges::eDoNotCreateEmptyContainer);
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
@ -7538,7 +7555,7 @@ nsresult HTMLEditRules::BustUpInlinesAtBRs(
|
||||
// Else we need to bust up aNode along all the breaks
|
||||
nsCOMPtr<nsIContent> nextNode = &aNode;
|
||||
for (OwningNonNull<nsINode>& brNode : arrayOfBreaks) {
|
||||
EditorRawDOMPoint atBrNode(brNode);
|
||||
EditorDOMPoint atBrNode(brNode);
|
||||
if (NS_WARN_IF(!atBrNode.IsSet())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
@ -7563,7 +7580,7 @@ nsresult HTMLEditRules::BustUpInlinesAtBRs(
|
||||
}
|
||||
|
||||
// Move break outside of container and also put in node list
|
||||
EditorRawDOMPoint atNextNode(splitNodeResult.GetNextNode());
|
||||
EditorDOMPoint atNextNode(splitNodeResult.GetNextNode());
|
||||
nsresult rv = MOZ_KnownLive(HTMLEditorRef())
|
||||
.MoveNodeWithTransaction(
|
||||
MOZ_KnownLive(*brNode->AsContent()), atNextNode);
|
||||
@ -7741,7 +7758,7 @@ nsresult HTMLEditRules::ReturnInHeader(Element& aHeader, nsINode& aNode,
|
||||
SplitNodeResult splitHeaderResult =
|
||||
MOZ_KnownLive(HTMLEditorRef())
|
||||
.SplitNodeDeepWithTransaction(
|
||||
aHeader, EditorRawDOMPoint(node, aOffset),
|
||||
aHeader, EditorDOMPoint(node, aOffset),
|
||||
SplitAtEdges::eAllowToCreateEmptyContainer);
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
@ -7760,7 +7777,7 @@ nsresult HTMLEditRules::ReturnInHeader(Element& aHeader, nsINode& aNode,
|
||||
}
|
||||
if (isEmptyNode) {
|
||||
CreateElementResult createMozBrResult =
|
||||
CreateMozBR(EditorRawDOMPoint(prevItem, 0));
|
||||
CreateMozBR(EditorDOMPoint(prevItem, 0));
|
||||
if (NS_WARN_IF(createMozBrResult.Failed())) {
|
||||
return createMozBrResult.Rv();
|
||||
}
|
||||
@ -7789,9 +7806,13 @@ nsresult HTMLEditRules::ReturnInHeader(Element& aHeader, nsINode& aNode,
|
||||
// Create a paragraph
|
||||
nsAtom& paraAtom = DefaultParagraphSeparator();
|
||||
// We want a wrapper element even if we separate with <br>
|
||||
EditorRawDOMPoint nextToHeader(headerParent, offset + 1);
|
||||
RefPtr<Element> pNode = HTMLEditorRef().CreateNodeWithTransaction(
|
||||
¶Atom == nsGkAtoms::br ? *nsGkAtoms::p : paraAtom, nextToHeader);
|
||||
EditorDOMPoint nextToHeader(headerParent, offset + 1);
|
||||
RefPtr<Element> pNode =
|
||||
MOZ_KnownLive(HTMLEditorRef())
|
||||
.CreateNodeWithTransaction(¶Atom == nsGkAtoms::br
|
||||
? *nsGkAtoms::p
|
||||
: MOZ_KnownLive(paraAtom),
|
||||
nextToHeader);
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
@ -7802,7 +7823,7 @@ nsresult HTMLEditRules::ReturnInHeader(Element& aHeader, nsINode& aNode,
|
||||
// Append a <br> to it
|
||||
RefPtr<Element> brElement =
|
||||
MOZ_KnownLive(HTMLEditorRef())
|
||||
.InsertBrElementWithTransaction(EditorRawDOMPoint(pNode, 0));
|
||||
.InsertBrElementWithTransaction(EditorDOMPoint(pNode, 0));
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
@ -7938,7 +7959,7 @@ EditActionResult HTMLEditRules::ReturnInParagraph(Element& aParentDivOrP) {
|
||||
|
||||
EditorDOMPoint pointToSplitParentDivOrP(atStartOfSelection);
|
||||
|
||||
EditorRawDOMPoint pointToInsertBR;
|
||||
EditorDOMPoint pointToInsertBR;
|
||||
if (doesCRCreateNewP && atStartOfSelection.GetContainer() == &aParentDivOrP) {
|
||||
// We are at the edges of the block, so, we don't need to create new <br>.
|
||||
brContent = nullptr;
|
||||
@ -8065,7 +8086,7 @@ nsresult HTMLEditRules::SplitParagraph(
|
||||
SplitNodeResult splitDivOrPResult =
|
||||
MOZ_KnownLive(HTMLEditorRef())
|
||||
.SplitNodeDeepWithTransaction(
|
||||
aParentDivOrP, EditorRawDOMPoint(selNode, selOffset),
|
||||
aParentDivOrP, EditorDOMPoint(selNode, selOffset),
|
||||
SplitAtEdges::eAllowToCreateEmptyContainer);
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
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?
|
||||
if (!HTMLEditorRef().IsLastEditableChild(&aListItem)) {
|
||||
// We need to split the list!
|
||||
EditorRawDOMPoint atListItem(&aListItem);
|
||||
EditorDOMPoint atListItem(&aListItem);
|
||||
ErrorResult error;
|
||||
leftListNode = MOZ_KnownLive(HTMLEditorRef())
|
||||
.SplitNodeWithTransaction(atListItem, error);
|
||||
@ -8171,7 +8192,7 @@ nsresult HTMLEditRules::ReturnInListItem(Element& aListItem, nsINode& aNode,
|
||||
}
|
||||
|
||||
// Are we in a sublist?
|
||||
EditorRawDOMPoint atNextSiblingOfLeftList(leftListNode);
|
||||
EditorDOMPoint atNextSiblingOfLeftList(leftListNode);
|
||||
DebugOnly<bool> advanced = atNextSiblingOfLeftList.AdvanceOffset();
|
||||
NS_WARNING_ASSERTION(advanced,
|
||||
"Failed to advance offset after the right list node");
|
||||
@ -8209,9 +8230,12 @@ nsresult HTMLEditRules::ReturnInListItem(Element& aListItem, nsINode& aNode,
|
||||
// Time to insert a paragraph
|
||||
nsAtom& paraAtom = DefaultParagraphSeparator();
|
||||
// We want a wrapper even if we separate with <br>
|
||||
RefPtr<Element> pNode = HTMLEditorRef().CreateNodeWithTransaction(
|
||||
¶Atom == nsGkAtoms::br ? *nsGkAtoms::p : paraAtom,
|
||||
atNextSiblingOfLeftList);
|
||||
RefPtr<Element> pNode =
|
||||
MOZ_KnownLive(HTMLEditorRef())
|
||||
.CreateNodeWithTransaction(¶Atom == nsGkAtoms::br
|
||||
? *nsGkAtoms::p
|
||||
: MOZ_KnownLive(paraAtom),
|
||||
atNextSiblingOfLeftList);
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
@ -8222,7 +8246,7 @@ nsresult HTMLEditRules::ReturnInListItem(Element& aListItem, nsINode& aNode,
|
||||
// Append a <br> to it
|
||||
RefPtr<Element> brElement =
|
||||
MOZ_KnownLive(HTMLEditorRef())
|
||||
.InsertBrElementWithTransaction(EditorRawDOMPoint(pNode, 0));
|
||||
.InsertBrElementWithTransaction(EditorDOMPoint(pNode, 0));
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
@ -8263,7 +8287,7 @@ nsresult HTMLEditRules::ReturnInListItem(Element& aListItem, nsINode& aNode,
|
||||
SplitNodeResult splitListItemResult =
|
||||
MOZ_KnownLive(HTMLEditorRef())
|
||||
.SplitNodeDeepWithTransaction(
|
||||
aListItem, EditorRawDOMPoint(selNode, aOffset),
|
||||
aListItem, EditorDOMPoint(selNode, aOffset),
|
||||
SplitAtEdges::eAllowToCreateEmptyContainer);
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
@ -8284,7 +8308,7 @@ nsresult HTMLEditRules::ReturnInListItem(Element& aListItem, nsINode& aNode,
|
||||
}
|
||||
if (isEmptyNode) {
|
||||
CreateElementResult createMozBrResult =
|
||||
CreateMozBR(EditorRawDOMPoint(prevItem, 0));
|
||||
CreateMozBR(EditorDOMPoint(prevItem, 0));
|
||||
if (NS_WARN_IF(createMozBrResult.Failed())) {
|
||||
return createMozBrResult.Rv();
|
||||
}
|
||||
@ -8302,11 +8326,12 @@ nsresult HTMLEditRules::ReturnInListItem(Element& aListItem, nsINode& aNode,
|
||||
nsAtom* listAtom =
|
||||
nodeAtom == nsGkAtoms::dt ? nsGkAtoms::dd : nsGkAtoms::dt;
|
||||
MOZ_DIAGNOSTIC_ASSERT(itemOffset != -1);
|
||||
EditorRawDOMPoint atNextListItem(list, aListItem.GetNextSibling(),
|
||||
itemOffset + 1);
|
||||
EditorDOMPoint atNextListItem(list, aListItem.GetNextSibling(),
|
||||
itemOffset + 1);
|
||||
RefPtr<Element> newListItem =
|
||||
HTMLEditorRef().CreateNodeWithTransaction(*listAtom,
|
||||
atNextListItem);
|
||||
MOZ_KnownLive(HTMLEditorRef())
|
||||
.CreateNodeWithTransaction(MOZ_KnownLive(*listAtom),
|
||||
atNextListItem);
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
@ -8454,15 +8479,15 @@ nsresult HTMLEditRules::MakeBlockquote(
|
||||
|
||||
// If no curBlock, make one
|
||||
if (!curBlock) {
|
||||
EditorDOMPoint atCurNode(curNode);
|
||||
SplitNodeResult splitNodeResult =
|
||||
MaybeSplitAncestorsForInsertWithTransaction(*nsGkAtoms::blockquote,
|
||||
atCurNode);
|
||||
EditorDOMPoint(curNode));
|
||||
if (NS_WARN_IF(splitNodeResult.Failed())) {
|
||||
return splitNodeResult.Rv();
|
||||
}
|
||||
curBlock = HTMLEditorRef().CreateNodeWithTransaction(
|
||||
*nsGkAtoms::blockquote, splitNodeResult.SplitPoint());
|
||||
curBlock = MOZ_KnownLive(HTMLEditorRef())
|
||||
.CreateNodeWithTransaction(*nsGkAtoms::blockquote,
|
||||
splitNodeResult.SplitPoint());
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
@ -8674,8 +8699,10 @@ nsresult HTMLEditRules::ApplyBlockStyle(
|
||||
if (NS_WARN_IF(splitNodeResult.Failed())) {
|
||||
return splitNodeResult.Rv();
|
||||
}
|
||||
RefPtr<Element> theBlock = HTMLEditorRef().CreateNodeWithTransaction(
|
||||
aBlockTag, splitNodeResult.SplitPoint());
|
||||
RefPtr<Element> theBlock =
|
||||
MOZ_KnownLive(HTMLEditorRef())
|
||||
.CreateNodeWithTransaction(aBlockTag,
|
||||
splitNodeResult.SplitPoint());
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
@ -8711,8 +8738,9 @@ nsresult HTMLEditRules::ApplyBlockStyle(
|
||||
if (NS_WARN_IF(splitNodeResult.Failed())) {
|
||||
return splitNodeResult.Rv();
|
||||
}
|
||||
curBlock = HTMLEditorRef().CreateNodeWithTransaction(
|
||||
aBlockTag, splitNodeResult.SplitPoint());
|
||||
curBlock = MOZ_KnownLive(HTMLEditorRef())
|
||||
.CreateNodeWithTransaction(aBlockTag,
|
||||
splitNodeResult.SplitPoint());
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
@ -8757,8 +8785,9 @@ nsresult HTMLEditRules::ApplyBlockStyle(
|
||||
if (NS_WARN_IF(splitNodeResult.Failed())) {
|
||||
return splitNodeResult.Rv();
|
||||
}
|
||||
curBlock = HTMLEditorRef().CreateNodeWithTransaction(
|
||||
aBlockTag, splitNodeResult.SplitPoint());
|
||||
curBlock = MOZ_KnownLive(HTMLEditorRef())
|
||||
.CreateNodeWithTransaction(aBlockTag,
|
||||
splitNodeResult.SplitPoint());
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
@ -8793,9 +8822,8 @@ nsresult HTMLEditRules::ApplyBlockStyle(
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
template <typename PT, typename CT>
|
||||
SplitNodeResult HTMLEditRules::MaybeSplitAncestorsForInsertWithTransaction(
|
||||
nsAtom& aTag, const EditorDOMPointBase<PT, CT>& aStartOfDeepestRightNode) {
|
||||
nsAtom& aTag, const EditorDOMPoint& aStartOfDeepestRightNode) {
|
||||
MOZ_ASSERT(IsEditorDataAvailable());
|
||||
|
||||
if (NS_WARN_IF(!aStartOfDeepestRightNode.IsSet())) {
|
||||
@ -8873,7 +8901,7 @@ nsresult HTMLEditRules::JoinNearestEditableNodesWithTransaction(
|
||||
int32_t parOffset = parent->ComputeIndexOf(&aNodeLeft);
|
||||
nsresult rv = MOZ_KnownLive(HTMLEditorRef())
|
||||
.MoveNodeWithTransaction(
|
||||
aNodeRight, EditorRawDOMPoint(parent, parOffset));
|
||||
aNodeRight, EditorDOMPoint(parent, parOffset));
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
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
|
||||
// them. Also, we want the br to be after the selection if the selection
|
||||
// is in this node.
|
||||
EditorRawDOMPoint endOfNode;
|
||||
EditorDOMPoint endOfNode;
|
||||
endOfNode.SetToEndOf(node);
|
||||
// XXX This method should return nsreuslt due to may be destroyed by this
|
||||
// CreateMozBr() call.
|
||||
@ -9628,7 +9656,7 @@ nsresult HTMLEditRules::RemoveEmptyNodesInChangedRange() {
|
||||
// but preserve br.
|
||||
RefPtr<Element> brElement =
|
||||
MOZ_KnownLive(HTMLEditorRef())
|
||||
.InsertBrElementWithTransaction(EditorRawDOMPoint(delNode));
|
||||
.InsertBrElementWithTransaction(EditorDOMPoint(delNode));
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
@ -10029,8 +10057,8 @@ nsresult HTMLEditRules::InsertBRIfNeededInternal(nsINode& aNode,
|
||||
}
|
||||
|
||||
CreateElementResult createBrResult =
|
||||
!aInsertMozBR ? CreateBR(EditorRawDOMPoint(&aNode, 0))
|
||||
: CreateMozBR(EditorRawDOMPoint(&aNode, 0));
|
||||
!aInsertMozBR ? CreateBR(EditorDOMPoint(&aNode, 0))
|
||||
: CreateMozBR(EditorDOMPoint(&aNode, 0));
|
||||
if (NS_WARN_IF(createBrResult.Failed())) {
|
||||
return createBrResult.Rv();
|
||||
}
|
||||
@ -10345,7 +10373,7 @@ nsresult HTMLEditRules::MakeSureElemStartsOrEndsOnCR(nsINode& aNode,
|
||||
}
|
||||
}
|
||||
if (!foundCR) {
|
||||
EditorRawDOMPoint pointToInsert;
|
||||
EditorDOMPoint pointToInsert;
|
||||
if (!aStarts) {
|
||||
pointToInsert.SetToEndOf(&aNode);
|
||||
} else {
|
||||
@ -10591,8 +10619,10 @@ nsresult HTMLEditRules::PrepareToMakeElementAbsolutePosition(
|
||||
if (NS_WARN_IF(splitNodeResult.Failed())) {
|
||||
return splitNodeResult.Rv();
|
||||
}
|
||||
RefPtr<Element> positionedDiv = HTMLEditorRef().CreateNodeWithTransaction(
|
||||
*nsGkAtoms::div, splitNodeResult.SplitPoint());
|
||||
RefPtr<Element> positionedDiv =
|
||||
MOZ_KnownLive(HTMLEditorRef())
|
||||
.CreateNodeWithTransaction(*nsGkAtoms::div,
|
||||
splitNodeResult.SplitPoint());
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
@ -10665,8 +10695,10 @@ nsresult HTMLEditRules::PrepareToMakeElementAbsolutePosition(
|
||||
return splitNodeResult.Rv();
|
||||
}
|
||||
if (!curPositionedDiv) {
|
||||
curPositionedDiv = HTMLEditorRef().CreateNodeWithTransaction(
|
||||
*nsGkAtoms::div, splitNodeResult.SplitPoint());
|
||||
curPositionedDiv =
|
||||
MOZ_KnownLive(HTMLEditorRef())
|
||||
.CreateNodeWithTransaction(*nsGkAtoms::div,
|
||||
splitNodeResult.SplitPoint());
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
@ -10675,10 +10707,11 @@ nsresult HTMLEditRules::PrepareToMakeElementAbsolutePosition(
|
||||
"Failed to create current positioned div element");
|
||||
*aTargetElement = curPositionedDiv;
|
||||
}
|
||||
EditorRawDOMPoint atEndOfCurPositionedDiv;
|
||||
EditorDOMPoint atEndOfCurPositionedDiv;
|
||||
atEndOfCurPositionedDiv.SetToEndOf(curPositionedDiv);
|
||||
curList = HTMLEditorRef().CreateNodeWithTransaction(
|
||||
*containerName, atEndOfCurPositionedDiv);
|
||||
curList = MOZ_KnownLive(HTMLEditorRef())
|
||||
.CreateNodeWithTransaction(MOZ_KnownLive(*containerName),
|
||||
atEndOfCurPositionedDiv);
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
@ -10734,9 +10767,10 @@ nsresult HTMLEditRules::PrepareToMakeElementAbsolutePosition(
|
||||
return splitNodeResult.Rv();
|
||||
}
|
||||
if (!curPositionedDiv) {
|
||||
EditorRawDOMPoint atListItemParent(atListItem.GetContainer());
|
||||
curPositionedDiv = HTMLEditorRef().CreateNodeWithTransaction(
|
||||
*nsGkAtoms::div, atListItemParent);
|
||||
curPositionedDiv = MOZ_KnownLive(HTMLEditorRef())
|
||||
.CreateNodeWithTransaction(
|
||||
*nsGkAtoms::div,
|
||||
EditorDOMPoint(atListItem.GetContainer()));
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
@ -10745,10 +10779,11 @@ nsresult HTMLEditRules::PrepareToMakeElementAbsolutePosition(
|
||||
"Failed to create current positioned div element");
|
||||
*aTargetElement = curPositionedDiv;
|
||||
}
|
||||
EditorRawDOMPoint atEndOfCurPositionedDiv;
|
||||
EditorDOMPoint atEndOfCurPositionedDiv;
|
||||
atEndOfCurPositionedDiv.SetToEndOf(curPositionedDiv);
|
||||
curList = HTMLEditorRef().CreateNodeWithTransaction(
|
||||
*containerName, atEndOfCurPositionedDiv);
|
||||
curList = MOZ_KnownLive(HTMLEditorRef())
|
||||
.CreateNodeWithTransaction(MOZ_KnownLive(*containerName),
|
||||
atEndOfCurPositionedDiv);
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
@ -10783,8 +10818,9 @@ nsresult HTMLEditRules::PrepareToMakeElementAbsolutePosition(
|
||||
if (NS_WARN_IF(splitNodeResult.Failed())) {
|
||||
return splitNodeResult.Rv();
|
||||
}
|
||||
curPositionedDiv = HTMLEditorRef().CreateNodeWithTransaction(
|
||||
*nsGkAtoms::div, splitNodeResult.SplitPoint());
|
||||
curPositionedDiv = MOZ_KnownLive(HTMLEditorRef())
|
||||
.CreateNodeWithTransaction(
|
||||
*nsGkAtoms::div, splitNodeResult.SplitPoint());
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
|
@ -1087,10 +1087,9 @@ class HTMLEditRules : public TextEditRules {
|
||||
* @return When succeeded, SplitPoint() returns
|
||||
* the point to insert the element.
|
||||
*/
|
||||
template <typename PT, typename CT>
|
||||
MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE SplitNodeResult
|
||||
MaybeSplitAncestorsForInsertWithTransaction(
|
||||
nsAtom& aTag, const EditorDOMPointBase<PT, CT>& aStartOfDeepestRightNode);
|
||||
nsAtom& aTag, const EditorDOMPoint& aStartOfDeepestRightNode);
|
||||
|
||||
/**
|
||||
* JoinNearestEditableNodesWithTransaction() joins two editable nodes which
|
||||
|
@ -133,13 +133,6 @@ bool HTMLEditorPrefs::sUserWantsToEnableResizingUIByDefault = false;
|
||||
bool HTMLEditorPrefs::sUserWantsToEnableInlineTableEditingUIByDefault = 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()
|
||||
: mCRInParagraphCreatesParagraph(false),
|
||||
mCSSAware(false),
|
||||
@ -1184,7 +1177,7 @@ nsresult HTMLEditor::InsertBrElementAtSelectionWithTransaction() {
|
||||
}
|
||||
}
|
||||
|
||||
EditorRawDOMPoint atStartOfSelection(
|
||||
EditorDOMPoint atStartOfSelection(
|
||||
EditorBase::GetStartPoint(*SelectionRefPtr()));
|
||||
if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
@ -1298,7 +1291,7 @@ nsresult HTMLEditor::ReplaceHeadContentsWithSourceWithTransaction(
|
||||
// Loop over the contents of the fragment and move into the document
|
||||
while (nsCOMPtr<nsIContent> child = documentFragment->GetFirstChild()) {
|
||||
nsresult rv = InsertNodeWithTransaction(
|
||||
*child, EditorRawDOMPoint(headNode, offsetOfNewNode++));
|
||||
*child, EditorDOMPoint(headNode, offsetOfNewNode++));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
@ -1611,7 +1604,7 @@ HTMLEditor::InsertElementAtSelection(Element* aElement, bool aDeleteSelection) {
|
||||
if (SelectionRefPtr()->GetAnchorNode()) {
|
||||
EditorRawDOMPoint atAnchor(SelectionRefPtr()->AnchorRef());
|
||||
// Adjust position based on the node we are going to insert.
|
||||
EditorRawDOMPoint pointToInsert =
|
||||
EditorDOMPoint pointToInsert =
|
||||
GetBetterInsertionPointFor(*aElement, atAnchor);
|
||||
if (NS_WARN_IF(!pointToInsert.IsSet())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
@ -1654,9 +1647,8 @@ HTMLEditor::InsertElementAtSelection(Element* aElement, bool aDeleteSelection) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
template <typename PT, typename CT>
|
||||
EditorDOMPoint HTMLEditor::InsertNodeIntoProperAncestorWithTransaction(
|
||||
nsIContent& aNode, const EditorDOMPointBase<PT, CT>& aPointToInsert,
|
||||
nsIContent& aNode, const EditorDOMPoint& aPointToInsert,
|
||||
SplitAtEdges aSplitAtEdges) {
|
||||
if (NS_WARN_IF(!aPointToInsert.IsSet())) {
|
||||
return EditorDOMPoint();
|
||||
@ -2120,7 +2112,7 @@ HTMLEditor::MakeOrChangeList(const nsAString& aListType, bool entireList,
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
EditorRawDOMPoint atStartOfSelection(firstRange->StartRef());
|
||||
EditorDOMPoint atStartOfSelection(firstRange->StartRef());
|
||||
if (NS_WARN_IF(!atStartOfSelection.IsSet()) ||
|
||||
NS_WARN_IF(!atStartOfSelection.GetContainerAsContent())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
@ -2160,9 +2152,8 @@ HTMLEditor::MakeOrChangeList(const nsAString& aListType, bool entireList,
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
// make a list item
|
||||
EditorRawDOMPoint atStartOfNewList(newList, 0);
|
||||
RefPtr<Element> newItem =
|
||||
CreateNodeWithTransaction(*nsGkAtoms::li, atStartOfNewList);
|
||||
CreateNodeWithTransaction(*nsGkAtoms::li, EditorDOMPoint(newList, 0));
|
||||
if (NS_WARN_IF(!newItem)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
@ -2296,7 +2287,7 @@ nsresult HTMLEditor::InsertBasicBlockWithTransaction(nsAtom& aTagName) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
EditorRawDOMPoint atStartOfSelection(firstRange->StartRef());
|
||||
EditorDOMPoint atStartOfSelection(firstRange->StartRef());
|
||||
if (NS_WARN_IF(!atStartOfSelection.IsSet()) ||
|
||||
NS_WARN_IF(!atStartOfSelection.GetContainerAsContent())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
@ -2436,7 +2427,7 @@ nsresult HTMLEditor::IndentOrOutdentAsSubAction(
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
EditorRawDOMPoint atStartOfSelection(firstRange->StartRef());
|
||||
EditorDOMPoint atStartOfSelection(firstRange->StartRef());
|
||||
if (NS_WARN_IF(!atStartOfSelection.IsSet()) ||
|
||||
NS_WARN_IF(!atStartOfSelection.GetContainerAsContent())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
@ -3834,7 +3825,7 @@ nsresult HTMLEditor::RemoveBlockContainerWithTransaction(Element& aElement) {
|
||||
!sibling->IsHTMLElement(nsGkAtoms::br) && !IsBlockNode(child)) {
|
||||
// Insert br node
|
||||
RefPtr<Element> brElement =
|
||||
InsertBrElementWithTransaction(EditorRawDOMPoint(&aElement, 0));
|
||||
InsertBrElementWithTransaction(EditorDOMPoint(&aElement, 0));
|
||||
if (NS_WARN_IF(!brElement)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
@ -3852,7 +3843,7 @@ nsresult HTMLEditor::RemoveBlockContainerWithTransaction(Element& aElement) {
|
||||
MOZ_ASSERT(child, "aNode has first editable child but not last?");
|
||||
if (!IsBlockNode(child) && !child->IsHTMLElement(nsGkAtoms::br)) {
|
||||
// Insert br node
|
||||
EditorRawDOMPoint endOfNode;
|
||||
EditorDOMPoint endOfNode;
|
||||
endOfNode.SetToEndOf(&aElement);
|
||||
RefPtr<Element> brElement = InsertBrElementWithTransaction(endOfNode);
|
||||
if (NS_WARN_IF(!brElement)) {
|
||||
@ -3875,7 +3866,7 @@ nsresult HTMLEditor::RemoveBlockContainerWithTransaction(Element& aElement) {
|
||||
!sibling->IsHTMLElement(nsGkAtoms::br)) {
|
||||
// Insert br node
|
||||
RefPtr<Element> brElement =
|
||||
InsertBrElementWithTransaction(EditorRawDOMPoint(&aElement, 0));
|
||||
InsertBrElementWithTransaction(EditorDOMPoint(&aElement, 0));
|
||||
if (NS_WARN_IF(!brElement)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
@ -4562,9 +4553,8 @@ nsresult HTMLEditor::CopyLastEditableChildStylesWithTransaction(
|
||||
nsAtom* tagName = elementInPreviousBlock->NodeInfo()->NameAtom();
|
||||
// At first time, just create the most descendant inline container element.
|
||||
if (!firstClonsedElement) {
|
||||
EditorRawDOMPoint atStartOfNewBlock(newBlock, 0);
|
||||
firstClonsedElement = lastClonedElement =
|
||||
CreateNodeWithTransaction(*tagName, atStartOfNewBlock);
|
||||
firstClonsedElement = lastClonedElement = CreateNodeWithTransaction(
|
||||
MOZ_KnownLive(*tagName), EditorDOMPoint(newBlock, 0));
|
||||
if (NS_WARN_IF(!firstClonsedElement)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
@ -4591,7 +4581,7 @@ nsresult HTMLEditor::CopyLastEditableChildStylesWithTransaction(
|
||||
}
|
||||
|
||||
RefPtr<Element> brElement =
|
||||
InsertBrElementWithTransaction(EditorRawDOMPoint(firstClonsedElement, 0));
|
||||
InsertBrElementWithTransaction(EditorDOMPoint(firstClonsedElement, 0));
|
||||
if (NS_WARN_IF(!brElement)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
@ -525,6 +525,7 @@ class HTMLEditor final : public TextEditor,
|
||||
* @param aOffset Start offset of removing text in aCharData.
|
||||
* @param aLength Length of removing text.
|
||||
*/
|
||||
MOZ_CAN_RUN_SCRIPT
|
||||
nsresult DeleteTextWithTransaction(dom::CharacterData& aTextNode,
|
||||
uint32_t aOffset, uint32_t aLength);
|
||||
|
||||
@ -1478,9 +1479,8 @@ class HTMLEditor final : public TextEditor,
|
||||
* @return Returns inserted point if succeeded.
|
||||
* Otherwise, the result is not set.
|
||||
*/
|
||||
template <typename PT, typename CT>
|
||||
MOZ_CAN_RUN_SCRIPT EditorDOMPoint InsertNodeIntoProperAncestorWithTransaction(
|
||||
nsIContent& aNode, const EditorDOMPointBase<PT, CT>& aPointToInsert,
|
||||
nsIContent& aNode, const EditorDOMPoint& aPointToInsert,
|
||||
SplitAtEdges aSplitAtEdges);
|
||||
|
||||
/**
|
||||
|
@ -646,7 +646,7 @@ nsresult HTMLEditor::DoInsertHTMLWithContext(
|
||||
// in a BR that is not visible. If so, the code above just placed
|
||||
// selection inside that. So I split it instead.
|
||||
SplitNodeResult splitLinkResult = SplitNodeDeepWithTransaction(
|
||||
*linkContent, EditorRawDOMPoint(selNode, selOffset),
|
||||
*linkContent, EditorDOMPoint(selNode, selOffset),
|
||||
SplitAtEdges::eDoNotCreateEmptyContainer);
|
||||
NS_WARNING_ASSERTION(splitLinkResult.Succeeded(),
|
||||
"Failed to split the link");
|
||||
|
@ -364,7 +364,7 @@ nsresult HTMLEditor::SetInlinePropertyOnTextNode(
|
||||
nsCOMPtr<nsIContent> textNodeForTheRange = &aText;
|
||||
|
||||
// Split at the end of the range.
|
||||
EditorRawDOMPoint atEnd(textNodeForTheRange, aEndOffset);
|
||||
EditorDOMPoint atEnd(textNodeForTheRange, aEndOffset);
|
||||
if (!atEnd.IsEndOfContainer()) {
|
||||
// We need to split off back of text node
|
||||
ErrorResult error;
|
||||
@ -375,7 +375,7 @@ nsresult HTMLEditor::SetInlinePropertyOnTextNode(
|
||||
}
|
||||
|
||||
// Split at the start of the range.
|
||||
EditorRawDOMPoint atStart(textNodeForTheRange, aStartOffset);
|
||||
EditorDOMPoint atStart(textNodeForTheRange, aStartOffset);
|
||||
if (!atStart.IsStartOfContainer()) {
|
||||
// We need to split off front of text node
|
||||
ErrorResult error;
|
||||
@ -397,7 +397,7 @@ nsresult HTMLEditor::SetInlinePropertyOnTextNode(
|
||||
if (IsSimpleModifiableNode(sibling, &aProperty, aAttribute, &aValue)) {
|
||||
// Following sib is already right kind of inline node; slide this over
|
||||
return MoveNodeWithTransaction(*textNodeForTheRange,
|
||||
EditorRawDOMPoint(sibling, 0));
|
||||
EditorDOMPoint(sibling, 0));
|
||||
}
|
||||
}
|
||||
|
||||
@ -453,7 +453,7 @@ nsresult HTMLEditor::SetInlinePropertyOnNodeImpl(nsIContent& aNode,
|
||||
}
|
||||
if (IsSimpleModifiableNode(nextSibling, &aProperty, aAttribute, &aValue)) {
|
||||
nsresult rv =
|
||||
MoveNodeWithTransaction(aNode, EditorRawDOMPoint(nextSibling, 0));
|
||||
MoveNodeWithTransaction(aNode, EditorDOMPoint(nextSibling, 0));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
@ -641,7 +641,7 @@ nsresult HTMLEditor::SplitStyleAbovePoint(
|
||||
isSet) {
|
||||
// Found a style node we need to split
|
||||
SplitNodeResult splitNodeResult = SplitNodeDeepWithTransaction(
|
||||
*node, EditorRawDOMPoint(*aNode, *aOffset),
|
||||
*node, EditorDOMPoint(*aNode, *aOffset),
|
||||
SplitAtEdges::eAllowToCreateEmptyContainer);
|
||||
NS_WARNING_ASSERTION(splitNodeResult.Succeeded(),
|
||||
"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
|
||||
// if you happen to click at the end of a line.
|
||||
if (savedBR) {
|
||||
rv =
|
||||
MoveNodeWithTransaction(*savedBR, EditorRawDOMPoint(newSelParent, 0));
|
||||
rv = MoveNodeWithTransaction(*savedBR, EditorDOMPoint(newSelParent, 0));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
@ -1641,7 +1640,7 @@ nsresult HTMLEditor::RelativeFontChangeOnTextNode(FontSize aDir,
|
||||
nsCOMPtr<nsIContent> textNodeForTheRange = &aTextNode;
|
||||
|
||||
// Split at the end of the range.
|
||||
EditorRawDOMPoint atEnd(textNodeForTheRange, aEndOffset);
|
||||
EditorDOMPoint atEnd(textNodeForTheRange, aEndOffset);
|
||||
if (!atEnd.IsEndOfContainer()) {
|
||||
// We need to split off back of text node
|
||||
ErrorResult error;
|
||||
@ -1652,7 +1651,7 @@ nsresult HTMLEditor::RelativeFontChangeOnTextNode(FontSize aDir,
|
||||
}
|
||||
|
||||
// Split at the start of the range.
|
||||
EditorRawDOMPoint atStart(textNodeForTheRange, aStartOffset);
|
||||
EditorDOMPoint atStart(textNodeForTheRange, aStartOffset);
|
||||
if (!atStart.IsStartOfContainer()) {
|
||||
// We need to split off front of text node
|
||||
ErrorResult error;
|
||||
@ -1678,7 +1677,7 @@ nsresult HTMLEditor::RelativeFontChangeOnTextNode(FontSize aDir,
|
||||
if (sibling && sibling->IsHTMLElement(nodeType)) {
|
||||
// Following sib is already right kind of inline node; slide this over
|
||||
nsresult rv = MoveNodeWithTransaction(*textNodeForTheRange,
|
||||
EditorRawDOMPoint(sibling, 0));
|
||||
EditorDOMPoint(sibling, 0));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
@ -1789,7 +1788,7 @@ nsresult HTMLEditor::RelativeFontChangeOnNode(int32_t aSizeChange,
|
||||
if (sibling && sibling->IsHTMLElement(atom)) {
|
||||
// following sib is already right kind of inline node; slide this over
|
||||
// into it
|
||||
return MoveNodeWithTransaction(*aNode, EditorRawDOMPoint(sibling, 0));
|
||||
return MoveNodeWithTransaction(*aNode, EditorDOMPoint(sibling, 0));
|
||||
}
|
||||
|
||||
// else insert it above aNode
|
||||
|
@ -2799,8 +2799,8 @@ nsresult HTMLEditor::MergeCells(RefPtr<Element> aTargetCell,
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
rv = InsertNodeWithTransaction(
|
||||
*cellChild, EditorRawDOMPoint(aTargetCell, insertIndex));
|
||||
rv = InsertNodeWithTransaction(*cellChild,
|
||||
EditorDOMPoint(aTargetCell, insertIndex));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
@ -44,11 +44,6 @@ namespace mozilla {
|
||||
|
||||
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 \
|
||||
if (IsReadonly() || IsDisabled()) { \
|
||||
*aCancel = true; \
|
||||
@ -982,7 +977,7 @@ nsresult TextEditRules::WillSetText(bool* aCancel, bool* aHandled,
|
||||
}
|
||||
nsresult rv = MOZ_KnownLive(TextEditorRef())
|
||||
.InsertNodeWithTransaction(
|
||||
*newNode, EditorRawDOMPoint(rootElement, 0));
|
||||
*newNode, EditorDOMPoint(rootElement, 0));
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
@ -1473,7 +1468,7 @@ nsresult TextEditRules::CreateTrailingBRIfNeeded() {
|
||||
|
||||
if (!lastChild->IsHTMLElement(nsGkAtoms::br)) {
|
||||
AutoTransactionsConserveSelection dontChangeMySelection(TextEditorRef());
|
||||
EditorRawDOMPoint endOfRoot;
|
||||
EditorDOMPoint endOfRoot;
|
||||
endOfRoot.SetToEndOf(rootElement);
|
||||
CreateElementResult createMozBrResult = CreateMozBR(endOfRoot);
|
||||
if (NS_WARN_IF(createMozBrResult.Failed())) {
|
||||
@ -1558,8 +1553,8 @@ nsresult TextEditRules::CreateBogusNodeIfNeeded() {
|
||||
|
||||
// Put the node in the document.
|
||||
nsresult rv = MOZ_KnownLive(TextEditorRef())
|
||||
.InsertNodeWithTransaction(
|
||||
*newBrElement, EditorRawDOMPoint(rootElement, 0));
|
||||
.InsertNodeWithTransaction(*newBrElement,
|
||||
EditorDOMPoint(rootElement, 0));
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
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(
|
||||
const EditorDOMPointBase<PT, CT>& aPointToInsert, bool aCreateMozBR) {
|
||||
const EditorDOMPoint& aPointToInsert, bool aCreateMozBR) {
|
||||
MOZ_ASSERT(IsEditorDataAvailable());
|
||||
|
||||
if (NS_WARN_IF(!aPointToInsert.IsSet())) {
|
||||
|
@ -312,9 +312,8 @@ class TextEditRules : public nsITimerCallback, public nsINamed {
|
||||
* @return Returns created <br> element or an error code
|
||||
* if couldn't create new <br> element.
|
||||
*/
|
||||
template <typename PT, typename CT>
|
||||
MOZ_CAN_RUN_SCRIPT CreateElementResult
|
||||
CreateBR(const EditorDOMPointBase<PT, CT>& aPointToInsert) {
|
||||
CreateBR(const EditorDOMPoint& aPointToInsert) {
|
||||
CreateElementResult ret = CreateBRInternal(aPointToInsert, false);
|
||||
#ifdef DEBUG
|
||||
// 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
|
||||
* if couldn't create new <br> element.
|
||||
*/
|
||||
template <typename PT, typename CT>
|
||||
MOZ_CAN_RUN_SCRIPT CreateElementResult
|
||||
CreateMozBR(const EditorDOMPointBase<PT, CT>& aPointToInsert) {
|
||||
CreateMozBR(const EditorDOMPoint& aPointToInsert) {
|
||||
CreateElementResult ret = CreateBRInternal(aPointToInsert, true);
|
||||
#ifdef DEBUG
|
||||
// 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.
|
||||
* If it succeeded, never returns nullptr.
|
||||
*/
|
||||
template <typename PT, typename CT>
|
||||
MOZ_CAN_RUN_SCRIPT CreateElementResult CreateBRInternal(
|
||||
const EditorDOMPointBase<PT, CT>& aPointToInsert, bool aCreateMozBR);
|
||||
MOZ_CAN_RUN_SCRIPT CreateElementResult
|
||||
CreateBRInternal(const EditorDOMPoint& aPointToInsert, bool aCreateMozBR);
|
||||
|
||||
protected:
|
||||
/**
|
||||
|
@ -64,11 +64,6 @@ namespace mozilla {
|
||||
|
||||
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()
|
||||
: mWrapColumn(0),
|
||||
mMaxTextLength(-1),
|
||||
@ -242,9 +237,8 @@ TextEditor::SetDocumentCharacterSet(const nsACString& characterSet) {
|
||||
}
|
||||
|
||||
// Create a new meta charset tag
|
||||
EditorRawDOMPoint atStartOfHeadNode(headNode, 0);
|
||||
RefPtr<Element> metaElement =
|
||||
CreateNodeWithTransaction(*nsGkAtoms::meta, atStartOfHeadNode);
|
||||
CreateNodeWithTransaction(*nsGkAtoms::meta, EditorDOMPoint(headNode, 0));
|
||||
if (NS_WARN_IF(!metaElement)) {
|
||||
return NS_OK;
|
||||
}
|
||||
@ -436,10 +430,8 @@ nsresult TextEditor::InsertLineBreakAsAction() {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
template <typename PT, typename CT>
|
||||
already_AddRefed<Element> TextEditor::InsertBrElementWithTransaction(
|
||||
const EditorDOMPointBase<PT, CT>& aPointToInsert,
|
||||
EDirection aSelect /* = eNone */) {
|
||||
const EditorDOMPoint& aPointToInsert, EDirection aSelect /* = eNone */) {
|
||||
MOZ_ASSERT(IsEditActionDataAvailable());
|
||||
|
||||
if (NS_WARN_IF(!aPointToInsert.IsSet())) {
|
||||
@ -869,7 +861,7 @@ already_AddRefed<Element> TextEditor::DeleteSelectionAndCreateElement(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
EditorRawDOMPoint pointToInsert(SelectionRefPtr()->AnchorRef());
|
||||
EditorDOMPoint pointToInsert(SelectionRefPtr()->AnchorRef());
|
||||
if (!pointToInsert.IsSet()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -354,10 +354,8 @@ class TextEditor : public EditorBase, public nsIPlaintextEditor {
|
||||
* @return The new <br> node. If failed to create new
|
||||
* <br> node, returns nullptr.
|
||||
*/
|
||||
template <typename PT, typename CT>
|
||||
MOZ_CAN_RUN_SCRIPT already_AddRefed<Element> InsertBrElementWithTransaction(
|
||||
const EditorDOMPointBase<PT, CT>& aPointToInsert,
|
||||
EDirection aSelect = eNone);
|
||||
const EditorDOMPoint& aPointToInsert, EDirection aSelect = eNone);
|
||||
|
||||
/**
|
||||
* Extends the selection for given deletion operation
|
||||
|
@ -55,12 +55,6 @@ template void WSRunObject::NextVisibleNode(const EditorRawDOMPoint& aPoint,
|
||||
nsCOMPtr<nsINode>* outVisNode,
|
||||
int32_t* outVisOffset,
|
||||
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(
|
||||
int16_t aDir, const EditorDOMPoint& aPoint, dom::Text** outStartNode,
|
||||
int32_t* outStartOffset, dom::Text** outEndNode, int32_t* outEndOffset);
|
||||
@ -171,9 +165,8 @@ nsresult WSRunObject::PrepareToSplitAcrossBlocks(HTMLEditor* aHTMLEditor,
|
||||
return wsObj.PrepareToSplitAcrossBlocksPriv();
|
||||
}
|
||||
|
||||
template <typename PT, typename CT>
|
||||
already_AddRefed<Element> WSRunObject::InsertBreak(
|
||||
Selection& aSelection, const EditorDOMPointBase<PT, CT>& aPointToInsert,
|
||||
Selection& aSelection, const EditorDOMPoint& aPointToInsert,
|
||||
nsIEditor::EDirection aSelect) {
|
||||
if (NS_WARN_IF(!aPointToInsert.IsSet())) {
|
||||
return nullptr;
|
||||
@ -393,8 +386,8 @@ nsresult WSRunObject::DeleteWSBackward() {
|
||||
// Easy case, preformatted ws.
|
||||
if (mPRE && (nsCRT::IsAsciiSpace(point.mChar) || point.mChar == kNBSP)) {
|
||||
nsresult rv =
|
||||
DeleteRange(EditorRawDOMPoint(point.mTextNode, point.mOffset),
|
||||
EditorRawDOMPoint(point.mTextNode, point.mOffset + 1));
|
||||
DeleteRange(EditorDOMPoint(point.mTextNode, point.mOffset),
|
||||
EditorDOMPoint(point.mTextNode, point.mOffset + 1));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
@ -420,8 +413,8 @@ nsresult WSRunObject::DeleteWSBackward() {
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// finally, delete that ws
|
||||
rv = DeleteRange(EditorRawDOMPoint(startNode, startOffset),
|
||||
EditorRawDOMPoint(endNode, endOffset));
|
||||
rv = DeleteRange(EditorDOMPoint(startNode, startOffset),
|
||||
EditorDOMPoint(endNode, endOffset));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
@ -439,8 +432,8 @@ nsresult WSRunObject::DeleteWSBackward() {
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// finally, delete that ws
|
||||
rv = DeleteRange(EditorRawDOMPoint(node, startOffset),
|
||||
EditorRawDOMPoint(node, endOffset));
|
||||
rv = DeleteRange(EditorDOMPoint(node, startOffset),
|
||||
EditorDOMPoint(node, endOffset));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
@ -457,8 +450,8 @@ nsresult WSRunObject::DeleteWSForward() {
|
||||
// Easy case, preformatted ws.
|
||||
if (mPRE && (nsCRT::IsAsciiSpace(point.mChar) || point.mChar == kNBSP)) {
|
||||
nsresult rv =
|
||||
DeleteRange(EditorRawDOMPoint(point.mTextNode, point.mOffset),
|
||||
EditorRawDOMPoint(point.mTextNode, point.mOffset + 1));
|
||||
DeleteRange(EditorDOMPoint(point.mTextNode, point.mOffset),
|
||||
EditorDOMPoint(point.mTextNode, point.mOffset + 1));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
@ -483,8 +476,8 @@ nsresult WSRunObject::DeleteWSForward() {
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Finally, delete that ws
|
||||
rv = DeleteRange(EditorRawDOMPoint(startNode, startOffset),
|
||||
EditorRawDOMPoint(endNode, endOffset));
|
||||
rv = DeleteRange(EditorDOMPoint(startNode, startOffset),
|
||||
EditorDOMPoint(endNode, endOffset));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
@ -502,8 +495,8 @@ nsresult WSRunObject::DeleteWSForward() {
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Finally, delete that ws
|
||||
rv = DeleteRange(EditorRawDOMPoint(node, startOffset),
|
||||
EditorRawDOMPoint(node, endOffset));
|
||||
rv = DeleteRange(EditorDOMPoint(node, startOffset),
|
||||
EditorDOMPoint(node, endOffset));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
@ -1302,10 +1295,8 @@ nsresult WSRunObject::PrepareToSplitAcrossBlocksPriv() {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
template <typename PT1, typename CT1, typename PT2, typename CT2>
|
||||
nsresult WSRunObject::DeleteRange(
|
||||
const EditorDOMPointBase<PT1, CT1>& aStartPoint,
|
||||
const EditorDOMPointBase<PT2, CT2>& aEndPoint) {
|
||||
nsresult WSRunObject::DeleteRange(const EditorDOMPoint& aStartPoint,
|
||||
const EditorDOMPoint& aEndPoint) {
|
||||
if (NS_WARN_IF(!aStartPoint.IsSet()) || NS_WARN_IF(!aEndPoint.IsSet())) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
@ -1325,8 +1316,9 @@ nsresult WSRunObject::DeleteRange(
|
||||
|
||||
if (aStartPoint.GetContainer() == aEndPoint.GetContainer() &&
|
||||
aStartPoint.IsInTextNode()) {
|
||||
RefPtr<Text> textNode = aStartPoint.GetContainerAsText();
|
||||
return htmlEditor->DeleteTextWithTransaction(
|
||||
*aStartPoint.GetContainerAsText(), aStartPoint.Offset(),
|
||||
*textNode, aStartPoint.Offset(),
|
||||
aEndPoint.Offset() - aStartPoint.Offset());
|
||||
}
|
||||
|
||||
@ -1883,9 +1875,8 @@ nsresult WSRunObject::CheckTrailingNBSPOfRun(WSFragment* aRun) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
template <typename PT, typename CT>
|
||||
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())) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
@ -240,9 +240,8 @@ class MOZ_STACK_CLASS WSRunObject final {
|
||||
* @return The new <br> node. If failed to create new <br>
|
||||
* node, returns nullptr.
|
||||
*/
|
||||
template <typename PT, typename CT>
|
||||
MOZ_CAN_RUN_SCRIPT already_AddRefed<dom::Element> InsertBreak(
|
||||
Selection& aSelection, const EditorDOMPointBase<PT, CT>& aPointToInsert,
|
||||
Selection& aSelection, const EditorDOMPoint& aPointToInsert,
|
||||
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.
|
||||
* Removes any nodes between them.
|
||||
*/
|
||||
template <typename PT1, typename CT1, typename PT2, typename CT2>
|
||||
MOZ_CAN_RUN_SCRIPT nsresult
|
||||
DeleteRange(const EditorDOMPointBase<PT1, CT1>& aStartPoint,
|
||||
const EditorDOMPointBase<PT2, CT2>& aEndPoint);
|
||||
MOZ_CAN_RUN_SCRIPT nsresult DeleteRange(const EditorDOMPoint& aStartPoint,
|
||||
const EditorDOMPoint& aEndPoint);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* unnecessary NBSP will be checked.
|
||||
*/
|
||||
template <typename PT, typename CT>
|
||||
MOZ_CAN_RUN_SCRIPT nsresult ReplacePreviousNBSPIfUnncessary(
|
||||
WSFragment* aRun, const EditorDOMPointBase<PT, CT>& aPoint);
|
||||
WSFragment* aRun, const EditorDOMPoint& aPoint);
|
||||
|
||||
MOZ_CAN_RUN_SCRIPT
|
||||
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();
|
||||
bool IsBlockNode(nsINode* aNode);
|
||||
|
||||
EditorRawDOMPoint StartPoint() const {
|
||||
return EditorRawDOMPoint(mStartNode, mStartOffset);
|
||||
EditorDOMPoint StartPoint() const {
|
||||
return EditorDOMPoint(mStartNode, mStartOffset);
|
||||
}
|
||||
EditorRawDOMPoint EndPoint() const {
|
||||
return EditorRawDOMPoint(mEndNode, mEndOffset);
|
||||
EditorDOMPoint EndPoint() const {
|
||||
return EditorDOMPoint(mEndNode, mEndOffset);
|
||||
}
|
||||
|
||||
// The node passed to our constructor.
|
||||
|
@ -12,6 +12,7 @@ support-files =
|
||||
en-AU/en_AU.aff
|
||||
de-DE/de_DE.dic
|
||||
de-DE/de_DE.aff
|
||||
!/editor/libeditor/tests/spellcheck.js
|
||||
|
||||
[test_async_UpdateCurrentDictionary.html]
|
||||
[test_bug678842.html]
|
||||
@ -24,3 +25,4 @@ support-files =
|
||||
[test_bug1219928.html]
|
||||
skip-if = e10s
|
||||
[test_bug1365383.html]
|
||||
[test_bug1418629.html]
|
||||
|
96
editor/spellchecker/tests/test_bug1418629.html
Normal file
96
editor/spellchecker/tests/test_bug1418629.html
Normal 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>
|
@ -418,8 +418,19 @@ CharClass WordSplitState::ClassifyCharacter(int32_t aIndex,
|
||||
if (mDOMWordText[aIndex - 1] == '.') return CHAR_CLASS_SEPARATOR;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
if (ClassifyCharacter(aIndex + 1, false) != CHAR_CLASS_WORD)
|
||||
return CHAR_CLASS_SEPARATOR;
|
||||
// If the next charatcer is a word-char, make sure that it's not a
|
||||
|
@ -15,7 +15,6 @@
|
||||
#include "mozilla/FloatingPoint.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/PodOperations.h"
|
||||
#include "mozilla/ReverseIterator.h"
|
||||
#include "mozilla/Sprintf.h"
|
||||
#include "mozilla/Variant.h"
|
||||
|
||||
@ -91,24 +90,6 @@ static bool ParseNodeRequiresSpecialLineNumberNotes(ParseNode* pn) {
|
||||
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* parent, SharedContext* sc, HandleScript script,
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
bool BytecodeEmitter::checkStrictOrSloppy(JSOp op) {
|
||||
if (IsCheckStrictOp(op) && !sc->strict()) {
|
||||
@ -9562,127 +9528,6 @@ void BytecodeEmitter::copySrcNotes(jssrcnote* destination, uint32_t nsrcnotes) {
|
||||
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(¬e);
|
||||
|
||||
// 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[] = {
|
||||
#define DEFINE_SRC_NOTE_SPEC(sym, name, arity) {name, arity},
|
||||
FOR_EACH_SRC_NOTE_TYPE(DEFINE_SRC_NOTE_SPEC)
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
#include "ds/InlineTable.h"
|
||||
#include "frontend/BCEParserHandle.h"
|
||||
#include "frontend/BytecodeSection.h" // BytecodeSection, PerScriptData
|
||||
#include "frontend/DestructuringFlavor.h"
|
||||
#include "frontend/EitherParser.h"
|
||||
#include "frontend/JumpList.h"
|
||||
@ -29,80 +30,6 @@
|
||||
namespace js {
|
||||
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 ElemOpEmitter;
|
||||
class EmitterScope;
|
||||
@ -127,198 +54,6 @@ struct MOZ_STACK_CLASS BytecodeEmitter {
|
||||
Rooted<LazyScript*> lazyScript;
|
||||
|
||||
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_;
|
||||
|
||||
public:
|
||||
@ -326,50 +61,6 @@ struct MOZ_STACK_CLASS BytecodeEmitter {
|
||||
const BytecodeSection& bytecodeSection() const { return bytecodeSection_; }
|
||||
|
||||
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_;
|
||||
|
||||
public:
|
||||
|
170
js/src/frontend/BytecodeSection.cpp
Normal file
170
js/src/frontend/BytecodeSection.cpp
Normal 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(¬e);
|
||||
|
||||
// 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); }
|
353
js/src/frontend/BytecodeSection.h
Normal file
353
js/src/frontend/BytecodeSection.h
Normal 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 */
|
@ -27,6 +27,7 @@ UNIFIED_SOURCES += [
|
||||
'BytecodeCompiler.cpp',
|
||||
'BytecodeControlStructures.cpp',
|
||||
'BytecodeEmitter.cpp',
|
||||
'BytecodeSection.cpp',
|
||||
'CallOrNewEmitter.cpp',
|
||||
'CForEmitter.cpp',
|
||||
'DefaultEmitter.cpp',
|
||||
|
@ -241,7 +241,7 @@ void Zone::discardJitCode(FreeOp* fop,
|
||||
// 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
|
||||
// opcodes are setting array holes or accessing getter properties.
|
||||
script->resetWarmUpCounter();
|
||||
script->resetWarmUpCounterForGC();
|
||||
|
||||
// Clear the BaselineScript's control flow graph. The LifoAlloc is purged
|
||||
// below.
|
||||
|
@ -56,8 +56,8 @@ var Opts_IonEagerNoOffthreadCompilation =
|
||||
var Opts_Ion2NoOffthreadCompilation =
|
||||
{
|
||||
'ion.enable': 1,
|
||||
'ion.warmup.trigger': 2,
|
||||
'ion.full.warmup.trigger': 2,
|
||||
'ion.warmup.trigger': 3,
|
||||
'ion.full.warmup.trigger': 3,
|
||||
'baseline.enable': 1,
|
||||
'baseline.warmup.trigger': 1,
|
||||
'offthread-compilation.enable': 0
|
||||
|
@ -518,3 +518,27 @@ wasmAssert(`(module
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
@ -766,21 +766,35 @@ bool BaselineCodeGen<Handler>::emitStackCheck() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static void EmitCallFrameIsDebuggeeCheck(MacroAssembler& masm) {
|
||||
masm.Push(BaselineFrameReg);
|
||||
masm.setupUnalignedABICall(R0.scratchReg());
|
||||
masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
|
||||
masm.passABIArg(R0.scratchReg());
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, jit::FrameIsDebuggeeCheck));
|
||||
masm.Pop(BaselineFrameReg);
|
||||
}
|
||||
|
||||
template <>
|
||||
void BaselineCompilerCodeGen::emitIsDebuggeeCheck() {
|
||||
if (handler.compileDebugInstrumentation()) {
|
||||
masm.Push(BaselineFrameReg);
|
||||
masm.setupUnalignedABICall(R0.scratchReg());
|
||||
masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
|
||||
masm.passABIArg(R0.scratchReg());
|
||||
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, jit::FrameIsDebuggeeCheck));
|
||||
masm.Pop(BaselineFrameReg);
|
||||
EmitCallFrameIsDebuggeeCheck(masm);
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
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 <>
|
||||
@ -1050,7 +1064,44 @@ void BaselineCompilerCodeGen::emitInitFrameFields() {
|
||||
|
||||
template <>
|
||||
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),
|
||||
¬Function);
|
||||
{
|
||||
// CalleeToken_Function or CalleeToken_FunctionConstructing.
|
||||
masm.andPtr(Imm32(uint32_t(CalleeTokenMask)), scratch1);
|
||||
masm.loadPtr(Address(scratch1, JSFunction::offsetOfScript()), scratch1);
|
||||
masm.jump(&done);
|
||||
}
|
||||
masm.bind(¬Function);
|
||||
{
|
||||
// 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 <>
|
||||
@ -6071,7 +6122,27 @@ bool BaselineCompilerCodeGen::emit_JSOP_JUMPTARGET() {
|
||||
|
||||
template <>
|
||||
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>
|
||||
|
@ -655,6 +655,7 @@ class BaselineCompiler final : private BaselineCompilerCodeGen {
|
||||
class BaselineInterpreterHandler {
|
||||
InterpreterFrameInfo frame_;
|
||||
Label interpretOp_;
|
||||
CodeOffset debuggeeCheckOffset_;
|
||||
|
||||
public:
|
||||
using FrameInfoT = InterpreterFrameInfo;
|
||||
@ -671,6 +672,11 @@ class BaselineInterpreterHandler {
|
||||
JSScript* maybeScript() 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
|
||||
// are no-ops.
|
||||
MOZ_MUST_USE bool appendRetAddrEntry(JSContext* cx, RetAddrEntry::Kind kind,
|
||||
|
@ -361,6 +361,8 @@ class ICScript {
|
||||
|
||||
ICEntry& icEntryFromPCOffset(uint32_t pcOffset);
|
||||
ICEntry& icEntryFromPCOffset(uint32_t pcOffset, ICEntry* prevLookedUpEntry);
|
||||
|
||||
static constexpr size_t offsetOfICEntries() { return sizeof(ICScript); }
|
||||
};
|
||||
|
||||
class ICMonitoredStub;
|
||||
|
@ -2252,7 +2252,7 @@ static MethodStatus Compile(JSContext* cx, HandleScript script,
|
||||
}
|
||||
|
||||
if (!CanLikelyAllocateMoreExecutableMemory()) {
|
||||
script->resetWarmUpCounter();
|
||||
script->resetWarmUpCounterToDelayIonCompilation();
|
||||
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: Clear all optimized stubs.
|
||||
// TODO: Add a ion-compilation-disabled checker IC stub
|
||||
script->resetWarmUpCounter();
|
||||
script->resetWarmUpCounterToDelayIonCompilation();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2576,7 +2576,7 @@ bool jit::IonCompileScriptForBaseline(JSContext* cx, BaselineFrame* frame,
|
||||
" Reset WarmUpCounter cantCompile=%s bailoutExpected=%s!",
|
||||
stat == Method_CantCompile ? "yes" : "no",
|
||||
bailoutExpected ? "yes" : "no");
|
||||
script->resetWarmUpCounter();
|
||||
script->resetWarmUpCounterToDelayIonCompilation();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -2799,7 +2799,7 @@ static void ClearIonScriptAfterInvalidation(JSContext* cx, JSScript* script,
|
||||
// compile, unless we are recompiling *because* a script got hot
|
||||
// (resetUses is false).
|
||||
if (resetUses) {
|
||||
script->resetWarmUpCounter();
|
||||
script->resetWarmUpCounterToDelayIonCompilation();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -215,7 +215,7 @@ static void HandleExceptionIon(JSContext* cx, const InlineFrameIterator& frame,
|
||||
// Ion can compile try-catch, but bailing out to catch
|
||||
// exceptions is slow. Reset the warm-up counter so that if we
|
||||
// catch many exceptions we won't Ion-compile the script.
|
||||
script->resetWarmUpCounter();
|
||||
script->resetWarmUpCounterToDelayIonCompilation();
|
||||
|
||||
if (*hitBailoutException) {
|
||||
break;
|
||||
@ -373,7 +373,7 @@ static bool ProcessTryNotesBaseline(JSContext* cx, const JSJitFrameIter& frame,
|
||||
// Ion can compile try-catch, but bailing out to catch
|
||||
// exceptions is slow. Reset the warm-up counter so that if we
|
||||
// catch many exceptions we won't Ion-compile the script.
|
||||
script->resetWarmUpCounter();
|
||||
script->resetWarmUpCounterToDelayIonCompilation();
|
||||
|
||||
// Resume at the start of the catch block.
|
||||
rfe->kind = ResumeFromException::RESUME_CATCH;
|
||||
|
@ -1636,11 +1636,11 @@ class MacroAssembler : public MacroAssemblerSpecific {
|
||||
|
||||
inline void cmp32Load32(Condition cond, Register lhs, const Address& rhs,
|
||||
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,
|
||||
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,
|
||||
Register src, Register dest)
|
||||
|
@ -435,23 +435,6 @@ void LIRGenerator::visitWasmStore(MWasmStore* 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) {
|
||||
MDefinition* lhs = div->getOperand(0);
|
||||
MDefinition* rhs = div->getOperand(1);
|
||||
|
@ -30,6 +30,10 @@ void MacroAssembler::move16SignExtend(Register src, Register dest) {
|
||||
ma_seh(dest, src);
|
||||
}
|
||||
|
||||
void MacroAssembler::loadAbiReturnAddress(Register dest) {
|
||||
movePtr(ra, dest);
|
||||
}
|
||||
|
||||
// ===============================================================
|
||||
// Logical instructions
|
||||
|
||||
@ -797,6 +801,19 @@ void MacroAssembler::cmp32Move32(Condition cond, Register lhs,
|
||||
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,
|
||||
Imm32 mask, const Address& src,
|
||||
Register dest) {
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include "jit/BaselineJIT.h"
|
||||
#include "jit/Ion.h"
|
||||
#include "jit/IonCode.h"
|
||||
#include "jit/JitOptions.h"
|
||||
#include "jit/JitRealm.h"
|
||||
#include "js/CompileOptions.h"
|
||||
#include "js/MemoryMetrics.h"
|
||||
@ -5465,6 +5466,18 @@ bool JSScript::mayReadFrameArgsDirectly() {
|
||||
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) {
|
||||
if (fun) {
|
||||
if (fun->realm()->isSelfHostingRealm()) {
|
||||
|
@ -2662,11 +2662,13 @@ class JSScript : public js::gc::TenuredCell {
|
||||
static size_t offsetOfWarmUpCounter() {
|
||||
return offsetof(JSScript, warmUpCount);
|
||||
}
|
||||
void resetWarmUpCounter() {
|
||||
void resetWarmUpCounterForGC() {
|
||||
incWarmUpResetCounter();
|
||||
warmUpCount = 0;
|
||||
}
|
||||
|
||||
void resetWarmUpCounterToDelayIonCompilation();
|
||||
|
||||
unsigned getWarmUpResetCount() const {
|
||||
constexpr uint32_t MASK = uint32_t(MutableFlags::WarmupResets_MASK);
|
||||
return mutableFlags_ & MASK;
|
||||
|
@ -1569,7 +1569,7 @@ bool js::FinishCompilation(JSContext* cx, HandleScript script,
|
||||
}
|
||||
|
||||
if (!succeeded) {
|
||||
script->resetWarmUpCounter();
|
||||
script->resetWarmUpCounterToDelayIonCompilation();
|
||||
*isValidOut = false;
|
||||
return true;
|
||||
}
|
||||
@ -2679,9 +2679,7 @@ void TypeZone::addPendingRecompile(JSContext* cx, JSScript* script) {
|
||||
CancelOffThreadIonCompile(script);
|
||||
|
||||
// Let the script warm up again before attempting another compile.
|
||||
if (jit::IsBaselineEnabled(cx)) {
|
||||
script->resetWarmUpCounter();
|
||||
}
|
||||
script->resetWarmUpCounterToDelayIonCompilation();
|
||||
|
||||
if (script->hasIonScript()) {
|
||||
addPendingRecompile(
|
||||
|
@ -4454,9 +4454,12 @@ static bool MaybeParseOwnerIndex(WasmParseContext& c) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static AstExpr* ParseInitializerExpression(WasmParseContext& c) {
|
||||
if (!c.ts.match(WasmToken::OpenParen, c.error)) {
|
||||
return nullptr;
|
||||
static AstExpr* ParseInitializerConstExpression(WasmParseContext& c) {
|
||||
bool need_rparen = false;
|
||||
|
||||
// For const initializer expressions, the parens are optional.
|
||||
if (c.ts.getIf(WasmToken::OpenParen)) {
|
||||
need_rparen = true;
|
||||
}
|
||||
|
||||
AstExpr* initExpr = ParseExprInsideParens(c);
|
||||
@ -4464,7 +4467,7 @@ static AstExpr* ParseInitializerExpression(WasmParseContext& c) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!c.ts.match(WasmToken::CloseParen, c.error)) {
|
||||
if (need_rparen && !c.ts.match(WasmToken::CloseParen, c.error)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -4480,11 +4483,19 @@ static bool ParseInitializerExpressionOrPassive(WasmParseContext& c,
|
||||
}
|
||||
#endif
|
||||
|
||||
AstExpr* initExpr = ParseInitializerExpression(c);
|
||||
if (!c.ts.match(WasmToken::OpenParen, c.error)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AstExpr* initExpr = ParseExprInsideParens(c);
|
||||
if (!initExpr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!c.ts.match(WasmToken::CloseParen, c.error)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*maybeInitExpr = initExpr;
|
||||
return true;
|
||||
}
|
||||
@ -5119,7 +5130,7 @@ static bool ParseGlobal(WasmParseContext& c, AstModule* module) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AstExpr* init = ParseInitializerExpression(c);
|
||||
AstExpr* init = ParseInitializerConstExpression(c);
|
||||
if (!init) {
|
||||
return false;
|
||||
}
|
||||
|
@ -186,11 +186,10 @@ function load_cert(cert, trust) {
|
||||
|
||||
function fetch_blocklist() {
|
||||
Services.prefs.setBoolPref("services.settings.load_dump", false);
|
||||
Services.prefs.setBoolPref("services.settings.verify_signature", false);
|
||||
Services.prefs.setCharPref("services.settings.server",
|
||||
`http://localhost:${port}/v1`);
|
||||
|
||||
BlocklistClients.initialize();
|
||||
BlocklistClients.initialize({ verifySignature: false });
|
||||
|
||||
return RemoteSettings.pollChanges();
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ let remoteSecSetting;
|
||||
if (AppConstants.MOZ_NEW_CERT_STORAGE) {
|
||||
const {RemoteSecuritySettings} = ChromeUtils.import("resource://gre/modules/psm/RemoteSecuritySettings.jsm");
|
||||
remoteSecSetting = new RemoteSecuritySettings();
|
||||
remoteSecSetting.client.verifySignature = false;
|
||||
}
|
||||
|
||||
let server;
|
||||
@ -89,7 +90,6 @@ function setupKintoPreloadServer(certGenerator, options = {
|
||||
}) {
|
||||
const dummyServerURL = `http://localhost:${server.identity.primaryPort}/v1`;
|
||||
Services.prefs.setCharPref("services.settings.server", dummyServerURL);
|
||||
Services.prefs.setBoolPref("services.settings.verify_signature", false);
|
||||
|
||||
const configPath = "/v1/";
|
||||
const recordsPath = "/v1/buckets/security-state/collections/intermediates/records";
|
||||
|
@ -110,6 +110,7 @@ skip-if = toolkit == 'android'
|
||||
[test_hmac.js]
|
||||
[test_intermediate_basic_usage_constraints.js]
|
||||
[test_intermediate_preloads.js]
|
||||
tags = blocklist
|
||||
# Bug 1520297 - do something to handle tighter resource constraints on Android
|
||||
skip-if = toolkit == 'android'
|
||||
[test_imminent_distrust.js]
|
||||
|
@ -251,12 +251,15 @@ var OneCRLBlocklistClient;
|
||||
var PinningBlocklistClient;
|
||||
var RemoteSecuritySettingsClient;
|
||||
|
||||
function initialize() {
|
||||
function initialize(options = {}) {
|
||||
const { verifySignature = true } = options;
|
||||
|
||||
OneCRLBlocklistClient = RemoteSettings(Services.prefs.getCharPref(PREF_SECURITY_SETTINGS_ONECRL_COLLECTION), {
|
||||
bucketNamePref: PREF_SECURITY_SETTINGS_ONECRL_BUCKET,
|
||||
lastCheckTimePref: PREF_SECURITY_SETTINGS_ONECRL_CHECKED,
|
||||
signerName: Services.prefs.getCharPref(PREF_SECURITY_SETTINGS_ONECRL_SIGNER),
|
||||
});
|
||||
OneCRLBlocklistClient.verifySignature = verifySignature;
|
||||
OneCRLBlocklistClient.on("sync", updateCertBlocklist);
|
||||
|
||||
PinningBlocklistClient = RemoteSettings(Services.prefs.getCharPref(PREF_BLOCKLIST_PINNING_COLLECTION), {
|
||||
@ -264,6 +267,7 @@ function initialize() {
|
||||
lastCheckTimePref: PREF_BLOCKLIST_PINNING_CHECKED_SECONDS,
|
||||
signerName: Services.prefs.getCharPref(PREF_BLOCKLIST_PINNING_SIGNER),
|
||||
});
|
||||
PinningBlocklistClient.verifySignature = verifySignature;
|
||||
PinningBlocklistClient.on("sync", updatePinningList);
|
||||
|
||||
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
|
||||
// the others.
|
||||
RemoteSecuritySettingsClient = new RemoteSecuritySettings();
|
||||
RemoteSecuritySettingsClient.verifySignature = verifySignature;
|
||||
|
||||
return {
|
||||
OneCRLBlocklistClient,
|
||||
|
@ -19,7 +19,7 @@ add_task(async function test_something() {
|
||||
const dummyServerURL = `http://localhost:${server.identity.primaryPort}/v1`;
|
||||
Services.prefs.setCharPref("services.settings.server", dummyServerURL);
|
||||
|
||||
const {OneCRLBlocklistClient} = BlocklistClients.initialize();
|
||||
const {OneCRLBlocklistClient} = BlocklistClients.initialize({verifySignature: false});
|
||||
|
||||
// register a handler
|
||||
function handleResponse(request, response) {
|
||||
@ -104,10 +104,6 @@ add_task(async function test_something() {
|
||||
});
|
||||
|
||||
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
|
||||
server = new HttpServer();
|
||||
server.start(-1);
|
||||
|
@ -27,7 +27,9 @@ let server;
|
||||
// Some simple tests to demonstrate that the core preload sync operations work
|
||||
// correctly and that simple kinto operations are working as expected.
|
||||
add_task(async function test_something() {
|
||||
const PinningPreloadClient = BlocklistClients.initialize().PinningBlocklistClient;
|
||||
const {
|
||||
PinningBlocklistClient: PinningPreloadClient,
|
||||
} = BlocklistClients.initialize({ verifySignature: false });
|
||||
|
||||
const configPath = "/v1/";
|
||||
const recordsPath = "/v1/buckets/pinning/collections/pins/records";
|
||||
@ -138,10 +140,6 @@ add_task(async function test_something() {
|
||||
});
|
||||
|
||||
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
|
||||
server = new HttpServer();
|
||||
server.start(-1);
|
||||
|
@ -8,7 +8,6 @@ const { UptakeTelemetry } = ChromeUtils.import("resource://services-common/uptak
|
||||
|
||||
let server;
|
||||
|
||||
const PREF_SETTINGS_VERIFY_SIGNATURE = "services.settings.verify_signature";
|
||||
const PREF_SETTINGS_SERVER = "services.settings.server";
|
||||
const PREF_SIGNATURE_ROOT = "security.content.signature.root_hash";
|
||||
|
||||
@ -591,10 +590,8 @@ add_task(async function test_check_signatures() {
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
OneCRLBlocklistClient = BlocklistClients.initialize().OneCRLBlocklistClient;
|
||||
|
||||
// ensure signatures are enforced
|
||||
Services.prefs.setBoolPref(PREF_SETTINGS_VERIFY_SIGNATURE, true);
|
||||
// Signature verification is evabled by default.
|
||||
({OneCRLBlocklistClient} = BlocklistClients.initialize());
|
||||
|
||||
// get a signature verifier to ensure nsNSSComponent is initialized
|
||||
Cc["@mozilla.org/security/contentsignatureverifier;1"]
|
||||
|
@ -35,8 +35,6 @@ XPCOMUtils.defineLazyPreferenceGetter(this, "gServerURL",
|
||||
"services.settings.server");
|
||||
XPCOMUtils.defineLazyPreferenceGetter(this, "gChangesPath",
|
||||
"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.
|
||||
@ -189,6 +187,10 @@ class RemoteSettingsClient extends EventEmitter {
|
||||
this.localFields = localFields;
|
||||
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
|
||||
// to preview the changes to be approved in a real client.
|
||||
this.bucketNamePref = bucketNamePref;
|
||||
@ -328,7 +330,7 @@ class RemoteSettingsClient extends EventEmitter {
|
||||
|
||||
// If signature verification is enabled, then add a synchronization hook
|
||||
// for incoming changes that validates the signature.
|
||||
if (this.signerName && gVerifySignature) {
|
||||
if (this.verifySignature) {
|
||||
kintoCollection.hooks["incoming-changes"] = [async (payload, collection) => {
|
||||
await this._validateCollectionSignature(payload.changes,
|
||||
payload.lastModified,
|
||||
|
@ -44,12 +44,12 @@ function run_test() {
|
||||
// Point the blocklist clients to use this local HTTP server.
|
||||
Services.prefs.setCharPref("services.settings.server",
|
||||
`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.verifySignature = false;
|
||||
|
||||
clientWithDump = RemoteSettings("language-dictionaries");
|
||||
clientWithDump.verifySignature = false;
|
||||
|
||||
// Setup server fake responses.
|
||||
function handleResponse(request, response) {
|
||||
|
@ -769,6 +769,16 @@ def build_generic_worker_payload(config, task, task_def):
|
||||
'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', {})
|
||||
|
||||
if task.get('needs-sccache'):
|
||||
|
@ -1,6 +1,6 @@
|
||||
[payment-allowed-by-feature-policy-attribute-redirect-on-load.https.sub.html]
|
||||
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.]
|
||||
expected:
|
||||
if not e10s: FAIL
|
||||
|
@ -1,6 +1,6 @@
|
||||
[payment-allowed-by-feature-policy-attribute.https.sub.html]
|
||||
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]
|
||||
expected:
|
||||
if not e10s: FAIL
|
||||
|
@ -1,6 +1,6 @@
|
||||
[payment-allowed-by-feature-policy.https.sub.html]
|
||||
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.]
|
||||
expected:
|
||||
if not e10s: FAIL
|
||||
|
@ -1,6 +1,6 @@
|
||||
[payment-default-feature-policy.https.sub.html]
|
||||
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.]
|
||||
expected:
|
||||
if not e10s: FAIL
|
||||
|
@ -1,6 +1,6 @@
|
||||
[payment-disabled-by-feature-policy.https.sub.html]
|
||||
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.]
|
||||
expected:
|
||||
if not e10s: FAIL
|
||||
|
@ -1,4 +1,6 @@
|
||||
[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.]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -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>
|
@ -27,9 +27,6 @@ function run_test() {
|
||||
// Point the blocklist clients to use this local HTTP server.
|
||||
Services.prefs.setCharPref("services.settings.server",
|
||||
`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,
|
||||
// this will be fixed in Bug 1526018
|
||||
@ -42,12 +39,15 @@ function run_test() {
|
||||
BlocklistGlobal.PluginBlocklistRS._ensureInitialized();
|
||||
BlocklistGlobal.GfxBlocklistRS._ensureInitialized();
|
||||
|
||||
|
||||
gBlocklistClients = [
|
||||
{client: BlocklistGlobal.ExtensionBlocklistRS._client, testData: ["i808", "i720", "i539"]},
|
||||
{client: BlocklistGlobal.PluginBlocklistRS._client, testData: ["p1044", "p32", "p28"]},
|
||||
{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.
|
||||
function handleResponse(request, response) {
|
||||
|
@ -12,7 +12,6 @@
|
||||
|
||||
.title {
|
||||
background-image: url("chrome://global/skin/icons/warning.svg");
|
||||
-moz-context-properties: fill;
|
||||
fill: #fcd100;
|
||||
}
|
||||
|
||||
|
@ -45,6 +45,8 @@ body.wide-container {
|
||||
margin-inline-start: -2.3em;
|
||||
padding-inline-start: 2.3em;
|
||||
font-size: 2.2em;
|
||||
-moz-context-properties: fill;
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
.title:-moz-locale-dir(rtl),
|
||||
|
Loading…
Reference in New Issue
Block a user