mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-04 21:18:35 +00:00
Merge mozilla-central to autoland on a CLOSED TREE
--HG-- extra : amend_source : 5026c19958cdc458c0f0887582fd00b8038fe93e
This commit is contained in:
commit
74e36e1adb
@ -6,8 +6,26 @@ requestLongerTimeout(5);
|
||||
|
||||
var isDevtools = SimpleTest.harnessParameters.subsuite == "devtools";
|
||||
|
||||
var gExceptionPaths = ["chrome://browser/content/defaultthemes/",
|
||||
"chrome://browser/locale/searchplugins/"];
|
||||
var gExceptionPaths = [
|
||||
"chrome://browser/content/defaultthemes/",
|
||||
"chrome://browser/locale/searchplugins/",
|
||||
"resource://app/defaults/blocklists/",
|
||||
"resource://app/defaults/preferences/",
|
||||
"resource://gre/modules/commonjs/",
|
||||
"resource://gre/defaults/pref/",
|
||||
"resource://shield-recipe-client/node_modules/jexl/lib/",
|
||||
|
||||
// https://github.com/mozilla/normandy/issues/577
|
||||
"resource://shield-recipe-client/test/",
|
||||
|
||||
// browser/extensions/pdfjs/content/build/pdf.js#1999
|
||||
"resource://pdf.js/web/images/",
|
||||
];
|
||||
|
||||
// These are not part of the omni.ja file, so we find them only when running
|
||||
// the test on a non-packaged build.
|
||||
if (AppConstants.platform == "macosx")
|
||||
gExceptionPaths.push("resource://gre/res/cursors/");
|
||||
|
||||
var whitelist = new Set([
|
||||
// browser/extensions/pdfjs/content/PdfStreamConverter.jsm
|
||||
@ -49,6 +67,50 @@ var whitelist = new Set([
|
||||
{file: "chrome://devtools/content/inspector/markup/markup.xhtml",
|
||||
isFromDevTools: true},
|
||||
|
||||
// extensions/pref/autoconfig/src/nsReadConfig.cpp
|
||||
{file: "resource://gre/defaults/autoconfig/prefcalls.js"},
|
||||
|
||||
// modules/libpref/Preferences.cpp
|
||||
{file: "resource://gre/greprefs.js"},
|
||||
|
||||
// browser/extensions/pdfjs/content/web/viewer.js
|
||||
{file: "resource://pdf.js/build/pdf.worker.js"},
|
||||
|
||||
// Add-on API introduced in bug 1118285
|
||||
{file: "resource://app/modules/NewTabURL.jsm"},
|
||||
|
||||
// layout/mathml/nsMathMLChar.cpp
|
||||
{file: "resource://gre/res/fonts/mathfontSTIXGeneral.properties"},
|
||||
{file: "resource://gre/res/fonts/mathfontUnicode.properties"},
|
||||
|
||||
// toolkit/components/places/ColorAnalyzer_worker.js
|
||||
{file: "resource://gre/modules/ClusterLib.js"},
|
||||
{file: "resource://gre/modules/ColorConversion.js"},
|
||||
|
||||
// The l10n build system can't package string files only for some platforms.
|
||||
{file: "resource://gre/chrome/en-US/locale/en-US/global-platform/mac/accessible.properties",
|
||||
platforms: ["linux", "win"]},
|
||||
{file: "resource://gre/chrome/en-US/locale/en-US/global-platform/mac/intl.properties",
|
||||
platforms: ["linux", "win"]},
|
||||
{file: "resource://gre/chrome/en-US/locale/en-US/global-platform/mac/platformKeys.properties",
|
||||
platforms: ["linux", "win"]},
|
||||
{file: "resource://gre/chrome/en-US/locale/en-US/global-platform/unix/accessible.properties",
|
||||
platforms: ["macosx", "win"]},
|
||||
{file: "resource://gre/chrome/en-US/locale/en-US/global-platform/unix/intl.properties",
|
||||
platforms: ["macosx", "win"]},
|
||||
{file: "resource://gre/chrome/en-US/locale/en-US/global-platform/unix/platformKeys.properties",
|
||||
platforms: ["macosx", "win"]},
|
||||
{file: "resource://gre/chrome/en-US/locale/en-US/global-platform/win/accessible.properties",
|
||||
platforms: ["linux", "macosx"]},
|
||||
{file: "resource://gre/chrome/en-US/locale/en-US/global-platform/win/intl.properties",
|
||||
platforms: ["linux", "macosx"]},
|
||||
{file: "resource://gre/chrome/en-US/locale/en-US/global-platform/win/platformKeys.properties",
|
||||
platforms: ["linux", "macosx"]},
|
||||
|
||||
// browser/extensions/pdfjs/content/web/viewer.js#7450
|
||||
{file: "resource://pdf.js/web/debugger.js"},
|
||||
|
||||
|
||||
// Starting from here, files in the whitelist are bugs that need fixing.
|
||||
// Bug 1339420
|
||||
{file: "chrome://branding/content/icon128.png"},
|
||||
@ -167,6 +229,41 @@ var whitelist = new Set([
|
||||
// Bug 1348559
|
||||
{file: "chrome://pippki/content/resetpassword.xul"},
|
||||
|
||||
// Bug 1344257
|
||||
{file: "resource://gre-resources/checkmark.svg"},
|
||||
{file: "resource://gre-resources/indeterminate-checkmark.svg"},
|
||||
{file: "resource://gre-resources/radio.svg"},
|
||||
// Bug 1351074
|
||||
{file: "resource://gre/modules/AsyncSpellCheckTestHelper.jsm"},
|
||||
// Bug 1351078
|
||||
{file: "resource://gre/modules/Battery.jsm"},
|
||||
// Bug 1351070
|
||||
{file: "resource://gre/modules/ContentPrefInstance.jsm"},
|
||||
// Bug 1351079
|
||||
{file: "resource://gre/modules/ISO8601DateUtils.jsm"},
|
||||
// Bug 1337345
|
||||
{file: "resource://gre/modules/Manifest.jsm"},
|
||||
// Bug 1351089
|
||||
{file: "resource://gre/modules/PresentationDeviceInfoManager.jsm"},
|
||||
// Bug 1351091
|
||||
{file: "resource://gre/modules/Profiler.jsm"},
|
||||
// Bug 1351658
|
||||
{file: "resource://gre/modules/PropertyListUtils.jsm", platforms: ["linux", "win"]},
|
||||
// Bug 1351093
|
||||
{file: "resource://gre/modules/Sntp.jsm"},
|
||||
// Bug 1351980
|
||||
{file: "resource://gre/modules/UserAgentOverrides.jsm"},
|
||||
// Bug 1351097
|
||||
{file: "resource://gre/modules/accessibility/AccessFu.jsm"},
|
||||
// Bug 1351099
|
||||
{file: "resource://gre/modules/addons/AddonLogging.jsm"},
|
||||
// Bug 1351604
|
||||
{file: "resource://gre/modules/psm/X509.jsm"},
|
||||
// Bug 1351637
|
||||
{file: "resource://gre/modules/sdk/bootstrap.js"},
|
||||
// Bug 1351657
|
||||
{file: "resource://gre/res/langGroups.properties", platforms: ["macosx"]},
|
||||
|
||||
].filter(item =>
|
||||
("isFromDevTools" in item) == isDevtools &&
|
||||
(!item.platforms || item.platforms.includes(AppConstants.platform))
|
||||
@ -186,12 +283,45 @@ const ignorableWhitelist = new Set([
|
||||
// reporter (eg. Linux x64 asan builds on treeherder)
|
||||
"chrome://global/locale/crashes.dtd",
|
||||
"chrome://global/locale/crashes.properties",
|
||||
|
||||
// The following files are outside of the omni.ja file, so we only catch them
|
||||
// when testing on a non-packaged build.
|
||||
|
||||
// toolkit/mozapps/extensions/nsBlocklistService.js
|
||||
"resource://app/blocklist.xml",
|
||||
|
||||
// dom/media/gmp/GMPParent.cpp
|
||||
"resource://gre/gmp-clearkey/0.1/manifest.json",
|
||||
|
||||
// Bug 1351675 - should this file be packaged?
|
||||
"resource://app/defaults/pinning/pins.json",
|
||||
|
||||
// Bug 1351682 - should be removed?
|
||||
"resource://app/defaults/profile/prefs.js",
|
||||
|
||||
// Bug 1351669 - obsolete test file
|
||||
"resource://gre/res/test.properties",
|
||||
]);
|
||||
for (let entry of ignorableWhitelist)
|
||||
for (let entry of ignorableWhitelist) {
|
||||
whitelist.add(entry);
|
||||
}
|
||||
|
||||
if (!isDevtools) {
|
||||
// services/sync/modules/service.js
|
||||
for (let module of ["addons.js", "bookmarks.js", "forms.js", "history.js",
|
||||
"passwords.js", "prefs.js", "tabs.js",
|
||||
"extension-storage.js"]) {
|
||||
whitelist.add("resource://services-sync/engines/" + module);
|
||||
}
|
||||
|
||||
// intl/unicharutil/nsEntityConverter.h
|
||||
for (let name of ["html40Latin1", "html40Symbols", "html40Special", "mathml20"]) {
|
||||
whitelist.add("resource://gre/res/entityTables/" + name + ".properties");
|
||||
}
|
||||
}
|
||||
|
||||
const gInterestingCategories = new Set([
|
||||
"agent-style-sheets", "webextension-scripts",
|
||||
"agent-style-sheets", "addon-provider-module", "webextension-scripts",
|
||||
"webextension-schemas", "webextension-scripts-addon",
|
||||
"webextension-scripts-content", "webextension-scripts-devtools"
|
||||
]);
|
||||
@ -201,6 +331,17 @@ var gChromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"]
|
||||
var gChromeMap = new Map();
|
||||
var gOverrideMap = new Map();
|
||||
var gReferencesFromCode = new Set();
|
||||
var gComponentsSet = new Set();
|
||||
|
||||
var resHandler = Services.io.getProtocolHandler("resource")
|
||||
.QueryInterface(Ci.nsIResProtocolHandler);
|
||||
var gResourceMap = [];
|
||||
function trackResourcePrefix(prefix) {
|
||||
let uri = Services.io.newURI("resource://" + prefix + "/");
|
||||
gResourceMap.unshift([prefix, resHandler.resolveURI(uri)]);
|
||||
}
|
||||
trackResourcePrefix("gre");
|
||||
trackResourcePrefix("app");
|
||||
|
||||
function getBaseUriForChromeUri(chromeUri) {
|
||||
let chromeFile = chromeUri + "gobbledygooknonexistentfile.reallynothere";
|
||||
@ -227,6 +368,10 @@ function parseManifest(manifestUri) {
|
||||
}
|
||||
} else if (type == "category" && gInterestingCategories.has(argv[0])) {
|
||||
gReferencesFromCode.add(argv[2]);
|
||||
} else if (type == "resource") {
|
||||
trackResourcePrefix(argv[0]);
|
||||
} else if (type == "component") {
|
||||
gComponentsSet.add(argv[1]);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -241,7 +386,7 @@ function parseCSSFile(fileUri) {
|
||||
let importMatch = line.match(/@import ['"]?([^'"]*)['"]?/);
|
||||
if (importMatch && importMatch[1]) {
|
||||
let url = Services.io.newURI(importMatch[1], null, fileUri).spec;
|
||||
gReferencesFromCode.add(convertToChromeUri(url));
|
||||
gReferencesFromCode.add(convertToCodeURI(url));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@ -256,7 +401,7 @@ function parseCSSFile(fileUri) {
|
||||
|
||||
try {
|
||||
url = Services.io.newURI(url, null, fileUri).specIgnoringRef;
|
||||
gReferencesFromCode.add(convertToChromeUri(url));
|
||||
gReferencesFromCode.add(convertToCodeURI(url));
|
||||
} catch (e) {
|
||||
ok(false, "unexpected error while resolving this URI: " + url);
|
||||
}
|
||||
@ -267,30 +412,64 @@ function parseCSSFile(fileUri) {
|
||||
|
||||
function parseCodeFile(fileUri) {
|
||||
return fetchFile(fileUri.spec).then(data => {
|
||||
let baseUri;
|
||||
for (let line of data.split("\n")) {
|
||||
let urls =
|
||||
line.match(/["']chrome:\/\/[a-zA-Z0-9 -]+\/(content|skin|locale)\/[^"' ]*["']/g);
|
||||
if (!urls) {
|
||||
urls = line.match(/["']resource:\/\/[^"']+["']/g);
|
||||
if (urls && isDevtools &&
|
||||
/baseURI: "resource:\/\/devtools\//.test(line)) {
|
||||
baseUri = Services.io.newURI(urls[0].slice(1, -1));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!urls) {
|
||||
// If there's no absolute chrome URL, look for relative ones in
|
||||
// src and href attributes.
|
||||
let match = line.match("(?:src|href)=[\"']([^$&\"']+)");
|
||||
if (match && match[1]) {
|
||||
let url = Services.io.newURI(match[1], null, fileUri).spec;
|
||||
gReferencesFromCode.add(convertToChromeUri(url));
|
||||
gReferencesFromCode.add(convertToCodeURI(url));
|
||||
}
|
||||
|
||||
if (isDevtools) {
|
||||
// Handle usage of devtools' LocalizationHelper object
|
||||
match = line.match('"devtools/client/locales/([^/.]+).properties"');
|
||||
let rules = [
|
||||
["gcli", "resource://devtools/shared/gcli/source/lib/gcli"],
|
||||
["devtools/client/locales", "chrome://devtools/locale"],
|
||||
["devtools/shared/locales", "chrome://devtools-shared/locale"],
|
||||
["devtools/shared/platform", "resource://devtools/shared/platform/chrome"],
|
||||
["devtools", "resource://devtools"]
|
||||
];
|
||||
|
||||
match = line.match(/["']((?:devtools|gcli)\/[^\\#"']+)["']/);
|
||||
if (match && match[1]) {
|
||||
gReferencesFromCode.add("chrome://devtools/locale/" +
|
||||
match[1] + ".properties");
|
||||
let path = match[1];
|
||||
for (let rule of rules) {
|
||||
if (path.startsWith(rule[0] + "/")) {
|
||||
path = path.replace(rule[0], rule[1]);
|
||||
if (!/\.(properties|js|jsm|json|css)$/.test(path))
|
||||
path += ".js";
|
||||
gReferencesFromCode.add(path);
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match = line.match('"devtools/shared/locales/([^/.]+).properties"');
|
||||
match = line.match(/require\(['"](\.[^'"]+)['"]\)/);
|
||||
if (match && match[1]) {
|
||||
gReferencesFromCode.add("chrome://devtools-shared/locale/" +
|
||||
match[1] + ".properties");
|
||||
let url = match[1];
|
||||
url = Services.io.newURI(url, null, baseUri || fileUri).spec;
|
||||
url = convertToCodeURI(url);
|
||||
if (!/\.(properties|js|jsm|json|css)$/.test(url))
|
||||
url += ".js";
|
||||
if (url.startsWith("resource://")) {
|
||||
gReferencesFromCode.add(url);
|
||||
} else {
|
||||
// if we end up with a chrome:// url here, it's likely because
|
||||
// a baseURI to a resource:// path has been defined in another
|
||||
// .js file that is loaded in the same scope, we can't detect it.
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
@ -307,28 +486,34 @@ function parseCodeFile(fileUri) {
|
||||
// and remove the ref if any.
|
||||
url = Services.io.newURI(url).specIgnoringRef;
|
||||
|
||||
if (isDevtools && line.includes("require(") &&
|
||||
!/\.(properties|js|jsm|json|css)$/.test(url))
|
||||
url += ".js";
|
||||
|
||||
gReferencesFromCode.add(url);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function convertToChromeUri(fileUri) {
|
||||
function convertToCodeURI(fileUri) {
|
||||
let baseUri = fileUri;
|
||||
let path = "";
|
||||
while (true) {
|
||||
let slashPos = baseUri.lastIndexOf("/", baseUri.length - 2);
|
||||
if (slashPos <= 0) {
|
||||
// File not accessible from chrome protocol,
|
||||
// TODO: bug 1349005 handle resource:// urls.
|
||||
// File not accessible from chrome protocol, try resource://
|
||||
for (let res of gResourceMap) {
|
||||
if (fileUri.startsWith(res[1]))
|
||||
return fileUri.replace(res[1], "resource://" + res[0] + "/");
|
||||
}
|
||||
// Give up and return the original URL.
|
||||
return fileUri;
|
||||
}
|
||||
path = baseUri.slice(slashPos + 1) + path;
|
||||
baseUri = baseUri.slice(0, slashPos + 1);
|
||||
if (gChromeMap.has(baseUri)) {
|
||||
let chromeBaseUri = gChromeMap.get(baseUri);
|
||||
return `${chromeBaseUri}${path}`;
|
||||
}
|
||||
if (gChromeMap.has(baseUri))
|
||||
return gChromeMap.get(baseUri) + path;
|
||||
}
|
||||
}
|
||||
|
||||
@ -343,21 +528,20 @@ function chromeFileExists(aURI) {
|
||||
available = sstream.available();
|
||||
sstream.close();
|
||||
} catch (e) {
|
||||
if (e.result != Components.results.NS_ERROR_FILE_NOT_FOUND) {
|
||||
dump("Checking " + aURI + ": " + e + "\n");
|
||||
Cu.reportError(e);
|
||||
if (e.result != Components.results.NS_ERROR_FILE_NOT_FOUND &&
|
||||
e.result != Components.results.NS_ERROR_NOT_AVAILABLE) {
|
||||
todo(false, "Failed to check if " + aURI + "exists: " + e);
|
||||
}
|
||||
}
|
||||
return available > 0;
|
||||
}
|
||||
|
||||
function findChromeUrlsFromArray(array) {
|
||||
const prefix = "chrome://";
|
||||
// Find the 'c' character...
|
||||
function findChromeUrlsFromArray(array, prefix) {
|
||||
// Find the first character of the prefix...
|
||||
for (let index = 0;
|
||||
(index = array.indexOf(prefix.charCodeAt(0), index)) != -1;
|
||||
++index) {
|
||||
// Then ensure we actually have the whole chrome:// prefix.
|
||||
// Then ensure we actually have the whole prefix.
|
||||
let found = true;
|
||||
for (let i = 1; i < prefix.length; ++i) {
|
||||
if (array[index + i] != prefix.charCodeAt(i)) {
|
||||
@ -373,13 +557,16 @@ function findChromeUrlsFromArray(array) {
|
||||
// Let's also terminate the string on the # character to skip references.
|
||||
let end = Math.min(array.indexOf(0, index),
|
||||
array.indexOf('"'.charCodeAt(0), index),
|
||||
array.indexOf(")".charCodeAt(0), index),
|
||||
array.indexOf("#".charCodeAt(0), index));
|
||||
let string = "";
|
||||
for ( ; index < end; ++index)
|
||||
for ( ; index < end; ++index) {
|
||||
string += String.fromCharCode(array[index]);
|
||||
}
|
||||
|
||||
// Only keep strings that look like real chrome urls.
|
||||
if (/chrome:\/\/[a-zA-Z09 -]+\/(content|skin|locale)\//.test(string))
|
||||
// Only keep strings that look like real chrome or resource urls.
|
||||
if (/chrome:\/\/[a-zA-Z09 -]+\/(content|skin|locale)\//.test(string) ||
|
||||
/resource:\/\/gre.*\.[a-z]+/.test(string))
|
||||
gReferencesFromCode.add(string);
|
||||
}
|
||||
}
|
||||
@ -389,9 +576,12 @@ add_task(function* checkAllTheFiles() {
|
||||
if (AppConstants.platform != "macosx")
|
||||
libxulPath = OS.Constants.Path.libDir + "/" + libxulPath;
|
||||
let libxul = yield OS.File.read(libxulPath);
|
||||
findChromeUrlsFromArray(libxul);
|
||||
findChromeUrlsFromArray(libxul, "chrome://");
|
||||
findChromeUrlsFromArray(libxul, "resource://");
|
||||
// Handle NS_LITERAL_STRING.
|
||||
findChromeUrlsFromArray(new Uint16Array(libxul.buffer));
|
||||
let uint16 = new Uint16Array(libxul.buffer);
|
||||
findChromeUrlsFromArray(uint16, "chrome://");
|
||||
findChromeUrlsFromArray(uint16, "resource://");
|
||||
|
||||
const kCodeExtensions = [".xul", ".xml", ".xsl", ".js", ".jsm", ".html", ".xhtml"];
|
||||
|
||||
@ -435,11 +625,18 @@ add_task(function* checkAllTheFiles() {
|
||||
|
||||
// Keep only chrome:// files, and filter out either the devtools paths or
|
||||
// the non-devtools paths:
|
||||
let devtoolsPrefixes = ["chrome://webide/", "chrome://devtools"];
|
||||
let chromeFiles =
|
||||
uris.map(uri => convertToChromeUri(uri.spec))
|
||||
.filter(u => u.startsWith("chrome://"))
|
||||
.filter(u => isDevtools == devtoolsPrefixes.some(prefix => u.startsWith(prefix)));
|
||||
let devtoolsPrefixes = ["chrome://webide/",
|
||||
"chrome://devtools",
|
||||
"resource://devtools/",
|
||||
"resource://app/modules/devtools",
|
||||
"resource://gre/modules/devtools"];
|
||||
let chromeFiles = [];
|
||||
for (let uri of uris) {
|
||||
uri = convertToCodeURI(uri.spec);
|
||||
if ((uri.startsWith("chrome://") || uri.startsWith("resource://")) &&
|
||||
isDevtools == devtoolsPrefixes.some(prefix => uri.startsWith(prefix)))
|
||||
chromeFiles.push(uri);
|
||||
}
|
||||
|
||||
let isUnreferenced =
|
||||
file => !gReferencesFromCode.has(file) &&
|
||||
@ -453,12 +650,47 @@ add_task(function* checkAllTheFiles() {
|
||||
return false;
|
||||
};
|
||||
|
||||
let unreferencedFiles =
|
||||
chromeFiles.filter(isUnreferenced).filter(notWhitelisted).sort();
|
||||
let unreferencedFiles = chromeFiles.filter(f => {
|
||||
let rv = isUnreferenced(f);
|
||||
if (rv && f.startsWith("resource://app/"))
|
||||
rv = isUnreferenced(f.replace("resource://app/", "resource:///"));
|
||||
if (rv && /^resource:\/\/(?:app|gre)\/components\/[^/]+\.js$/.test(f))
|
||||
rv = !gComponentsSet.has(f.replace(/.*\//, ""));
|
||||
return rv;
|
||||
}).filter(notWhitelisted).sort();
|
||||
|
||||
if (isDevtools) {
|
||||
// Bug 1351878 - handle devtools resource files
|
||||
unreferencedFiles = unreferencedFiles.filter(file => {
|
||||
if (file.startsWith("resource://")) {
|
||||
info("unreferenced devtools resource file: " + file);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
unreferencedFiles = unreferencedFiles.filter(file => {
|
||||
// resource://app/features/ will only contain .xpi files when the test runs
|
||||
// on a packaged build, so the following two exceptions only matter when
|
||||
// running the test on a local non-packaged build.
|
||||
|
||||
if (/resource:\/\/app\/features\/[^/]+\/bootstrap\.js/.test(file)) {
|
||||
info("not reporting feature boostrap file: " + file)
|
||||
return false;
|
||||
}
|
||||
// Bug 1351892 - can stop shipping these?
|
||||
if (/resource:\/\/app\/features\/[^/]+\/chrome\/skin\//.test(file)) {
|
||||
info("not reporting feature skin file that may be for another platform: " + file)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
is(unreferencedFiles.length, 0, "there should be no unreferenced files");
|
||||
for (let file of unreferencedFiles)
|
||||
ok(false, "unreferenced chrome file: " + file);
|
||||
for (let file of unreferencedFiles) {
|
||||
ok(false, "unreferenced file: " + file);
|
||||
}
|
||||
|
||||
for (let file of whitelist) {
|
||||
if (ignorableWhitelist.has(file))
|
||||
@ -471,7 +703,8 @@ add_task(function* checkAllTheFiles() {
|
||||
if (isDevtools != devtoolsPrefixes.some(prefix => file.startsWith(prefix)))
|
||||
continue;
|
||||
|
||||
if (file.startsWith("chrome://") && !chromeFileExists(file)) {
|
||||
if ((file.startsWith("chrome://") || file.startsWith("resource://")) &&
|
||||
!chromeFileExists(file)) {
|
||||
// Ignore chrome prefixes that have been automatically expanded.
|
||||
let pathParts =
|
||||
file.match("chrome://([^/]+)/content/([^/.]+)\.xul") ||
|
||||
|
@ -242,7 +242,7 @@
|
||||
.boxmodel-position.boxmodel-top,
|
||||
.boxmodel-position.boxmodel-bottom {
|
||||
border-left: 1px solid var(--theme-highlight-purple);
|
||||
left: calc(50% - 1px);
|
||||
left: calc(50% - 2px);
|
||||
padding-left: 1px;
|
||||
}
|
||||
|
||||
@ -324,6 +324,14 @@
|
||||
padding: 0 9px;
|
||||
}
|
||||
|
||||
.boxmodel-properties-wrapper .property-name-container {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.boxmodel-properties-wrapper .property-value-container {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* Box Model Main - Offset Parent */
|
||||
|
||||
.boxmodel-offset-parent {
|
||||
|
@ -2653,6 +2653,10 @@ var WalkerActor = protocol.ActorClassWithSpec(walkerSpec, {
|
||||
* returns null.
|
||||
*/
|
||||
getOffsetParent: function (node) {
|
||||
if (isNodeDead(node)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let offsetParent = node.rawNode.offsetParent;
|
||||
|
||||
if (!offsetParent) {
|
||||
|
@ -40,6 +40,14 @@ addTest(function setup() {
|
||||
});
|
||||
});
|
||||
|
||||
addTest(function () {
|
||||
info("Try to get the offset parent for a dead node (null)");
|
||||
gWalker.getOffsetParent(null).then(offsetParent => {
|
||||
ok(!offsetParent, "No offset parent found");
|
||||
runNextTest();
|
||||
});
|
||||
});
|
||||
|
||||
addTest(function () {
|
||||
info("Try to get the offset parent for a node that is absolutely positioned inside a " +
|
||||
"relative node");
|
||||
|
@ -377,7 +377,7 @@ const walkerSpec = generateActorSpec({
|
||||
},
|
||||
getOffsetParent: {
|
||||
request: {
|
||||
node: Arg(0, "domnode")
|
||||
node: Arg(0, "nullable:domnode")
|
||||
},
|
||||
response: {
|
||||
node: RetVal("nullable:domnode")
|
||||
|
@ -291,6 +291,25 @@ TexUnpackBlob::TexUnpackBlob(const WebGLContext* webgl, TexImageTarget target,
|
||||
MOZ_ASSERT_IF(!IsTarget3D(target), mDepth == 1);
|
||||
}
|
||||
|
||||
static bool
|
||||
HasColorAndAlpha(const WebGLTexelFormat format)
|
||||
{
|
||||
switch (format) {
|
||||
case WebGLTexelFormat::RA8:
|
||||
case WebGLTexelFormat::RA16F:
|
||||
case WebGLTexelFormat::RA32F:
|
||||
case WebGLTexelFormat::RGBA8:
|
||||
case WebGLTexelFormat::RGBA5551:
|
||||
case WebGLTexelFormat::RGBA4444:
|
||||
case WebGLTexelFormat::RGBA16F:
|
||||
case WebGLTexelFormat::RGBA32F:
|
||||
case WebGLTexelFormat::BGRA8:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
TexUnpackBlob::ConvertIfNeeded(WebGLContext* webgl, const char* funcName,
|
||||
const uint32_t rowLength, const uint32_t rowCount,
|
||||
@ -314,16 +333,18 @@ TexUnpackBlob::ConvertIfNeeded(WebGLContext* webgl, const char* funcName,
|
||||
const auto dstOrigin = gl::OriginPos::BottomLeft;
|
||||
|
||||
if (srcFormat != dstFormat) {
|
||||
webgl->GeneratePerfWarning("%s: Conversion requires pixel reformatting.",
|
||||
funcName);
|
||||
} else if (mSrcIsPremult != dstIsPremult) {
|
||||
webgl->GeneratePerfWarning("%s: Conversion requires pixel reformatting. (%u->%u)",
|
||||
funcName, uint32_t(srcFormat),
|
||||
uint32_t(dstFormat));
|
||||
} else if (mSrcIsPremult != dstIsPremult && HasColorAndAlpha(srcFormat)) {
|
||||
webgl->GeneratePerfWarning("%s: Conversion requires change in"
|
||||
"alpha-premultiplication.",
|
||||
" alpha-premultiplication.",
|
||||
funcName);
|
||||
} else if (srcOrigin != dstOrigin) {
|
||||
webgl->GeneratePerfWarning("%s: Conversion requires y-flip.", funcName);
|
||||
} else if (srcStride != dstStride) {
|
||||
webgl->GeneratePerfWarning("%s: Conversion requires change in stride.", funcName);
|
||||
webgl->GeneratePerfWarning("%s: Conversion requires change in stride. (%u->%u)",
|
||||
funcName, uint32_t(srcStride), uint32_t(dstStride));
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
@ -598,19 +619,32 @@ TexUnpackImage::TexOrSubImage(bool isSubImage, bool needsRespec, const char* fun
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* fallbackReason;
|
||||
do {
|
||||
if (mDepth != 1)
|
||||
if (mDepth != 1) {
|
||||
fallbackReason = "depth is not 1";
|
||||
break;
|
||||
}
|
||||
|
||||
const auto& dstIsPremult = webgl->mPixelStore_PremultiplyAlpha;
|
||||
if (mSrcIsPremult != dstIsPremult)
|
||||
if (mSrcIsPremult != dstIsPremult) {
|
||||
if (dstIsPremult) {
|
||||
fallbackReason = "UNPACK_PREMULTIPLY_ALPHA_WEBGL is not true";
|
||||
} else {
|
||||
fallbackReason = "UNPACK_PREMULTIPLY_ALPHA_WEBGL is not false";
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (dui->unpackFormat != LOCAL_GL_RGB && dui->unpackFormat != LOCAL_GL_RGBA)
|
||||
if (dui->unpackFormat != LOCAL_GL_RGB && dui->unpackFormat != LOCAL_GL_RGBA) {
|
||||
fallbackReason = "`format` is not RGB or RGBA";
|
||||
break;
|
||||
}
|
||||
|
||||
if (dui->unpackType != LOCAL_GL_UNSIGNED_BYTE)
|
||||
if (dui->unpackType != LOCAL_GL_UNSIGNED_BYTE) {
|
||||
fallbackReason = "`type` is not UNSIGNED_BYTE";
|
||||
break;
|
||||
}
|
||||
|
||||
gl::ScopedFramebuffer scopedFB(gl);
|
||||
gl::ScopedBindFramebuffer bindFB(gl, scopedFB.FB());
|
||||
@ -621,13 +655,17 @@ TexUnpackImage::TexOrSubImage(bool isSubImage, bool needsRespec, const char* fun
|
||||
gl->fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
|
||||
target.get(), tex->mGLName, level);
|
||||
|
||||
if (errorScope.GetError())
|
||||
if (errorScope.GetError()) {
|
||||
fallbackReason = "bug: failed to attach to FB for blit";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const GLenum status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
|
||||
if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE)
|
||||
if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
|
||||
fallbackReason = "bug: failed to confirm FB for blit";
|
||||
break;
|
||||
}
|
||||
|
||||
const gfx::IntSize destSize(mWidth, mHeight);
|
||||
const auto dstOrigin = (webgl->mPixelStore_FlipY ? gl::OriginPos::TopLeft
|
||||
@ -635,6 +673,7 @@ TexUnpackImage::TexOrSubImage(bool isSubImage, bool needsRespec, const char* fun
|
||||
if (!gl->BlitHelper()->BlitImageToFramebuffer(mImage, destSize, scopedFB.FB(),
|
||||
dstOrigin))
|
||||
{
|
||||
fallbackReason = "likely bug: failed to blit";
|
||||
break;
|
||||
}
|
||||
|
||||
@ -643,9 +682,9 @@ TexUnpackImage::TexOrSubImage(bool isSubImage, bool needsRespec, const char* fun
|
||||
return true;
|
||||
} while (false);
|
||||
|
||||
webgl->GeneratePerfWarning("%s: Failed to hit GPU-copy fast-path. Falling back to CPU"
|
||||
" upload.",
|
||||
funcName);
|
||||
webgl->GeneratePerfWarning("%s: Failed to hit GPU-copy fast-path. (src type %u)"
|
||||
" Falling back to CPU upload. (%s)",
|
||||
funcName, uint32_t(mImage->GetFormat()), fallbackReason);
|
||||
|
||||
const RefPtr<gfx::SourceSurface> surf = mImage->GetAsSourceSurface();
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "AbstractMediaDecoder.h"
|
||||
#include "gfx2DGlue.h"
|
||||
#include "VideoFrameContainer.h"
|
||||
#include "mozilla/CheckedInt.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -381,17 +382,30 @@ AndroidMediaReader::ImageBufferCallback::CreateI420Image(size_t aWidth,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t frameSize = aWidth * aHeight;
|
||||
// Use uint32_t throughout to match AllocateAndGetNewBuffer's param
|
||||
const auto checkedFrameSize =
|
||||
CheckedInt<uint32_t>(aWidth) * aHeight;
|
||||
|
||||
// Allocate enough for one full resolution Y plane
|
||||
// and two quarter resolution Cb/Cr planes.
|
||||
uint8_t *buffer = yuvImage->AllocateAndGetNewBuffer(frameSize * 3 / 2);
|
||||
const auto checkedBufferSize =
|
||||
checkedFrameSize + checkedFrameSize / 2;
|
||||
|
||||
if (!checkedBufferSize.isValid()) { // checks checkedFrameSize too
|
||||
NS_WARNING("Could not create I420 image");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto frameSize = checkedFrameSize.value();
|
||||
|
||||
uint8_t *buffer =
|
||||
yuvImage->AllocateAndGetNewBuffer(checkedBufferSize.value());
|
||||
|
||||
mozilla::layers::PlanarYCbCrData frameDesc;
|
||||
|
||||
frameDesc.mYChannel = buffer;
|
||||
frameDesc.mCbChannel = buffer + frameSize;
|
||||
frameDesc.mCrChannel = buffer + frameSize * 5 / 4;
|
||||
frameDesc.mCrChannel = frameDesc.mCbChannel + frameSize / 4;
|
||||
|
||||
frameDesc.mYSize = IntSize(aWidth, aHeight);
|
||||
frameDesc.mCbCrSize = IntSize(aWidth / 2, aHeight / 2);
|
||||
|
@ -17,6 +17,7 @@ function testScript(script) {
|
||||
"set": [["dom.requestcontext.enabled", true],
|
||||
["dom.serviceWorkers.enabled", true],
|
||||
["dom.serviceWorkers.testing.enabled", true],
|
||||
["dom.serviceWorkers.idle_timeout", 0],
|
||||
["dom.serviceWorkers.exemptFromPerDomainMax", true]]
|
||||
}, resolve);
|
||||
});
|
||||
|
@ -16,15 +16,19 @@ addEventListener('message', function workerWrapperOnMessage(e) {
|
||||
removeEventListener('message', workerWrapperOnMessage);
|
||||
var data = e.data;
|
||||
|
||||
function loadTest() {
|
||||
var done = function() {
|
||||
function loadTest(event) {
|
||||
var done = function(res) {
|
||||
client.postMessage({ type: 'finish', context: context });
|
||||
return res;
|
||||
}
|
||||
|
||||
try {
|
||||
importScripts(data.script);
|
||||
// runTest() is provided by the test.
|
||||
runTest().then(done, done);
|
||||
var result = runTest().then(done, done);
|
||||
if ('waitUntil' in event) {
|
||||
event.waitUntil(result);
|
||||
}
|
||||
} catch(e) {
|
||||
client.postMessage({
|
||||
type: 'status',
|
||||
@ -37,7 +41,10 @@ addEventListener('message', function workerWrapperOnMessage(e) {
|
||||
}
|
||||
|
||||
if ("ServiceWorker" in self) {
|
||||
self.clients.matchAll().then(function(clients) {
|
||||
// Fetch requests from a service worker are not intercepted.
|
||||
self.isSWPresent = false;
|
||||
|
||||
e.waitUntil(self.clients.matchAll().then(function(clients) {
|
||||
for (var i = 0; i < clients.length; ++i) {
|
||||
if (clients[i].url.indexOf("message_receiver.html") > -1) {
|
||||
client = clients[i];
|
||||
@ -48,11 +55,11 @@ addEventListener('message', function workerWrapperOnMessage(e) {
|
||||
dump("We couldn't find the message_receiver window, the test will fail\n");
|
||||
}
|
||||
context = "ServiceWorker";
|
||||
loadTest();
|
||||
});
|
||||
loadTest(e);
|
||||
}));
|
||||
} else {
|
||||
client = self;
|
||||
context = "Worker";
|
||||
loadTest();
|
||||
loadTest(e);
|
||||
}
|
||||
});
|
||||
|
@ -9,7 +9,6 @@
|
||||
#include "Logging.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "ScaledFontWin.h"
|
||||
#include "SFNTData.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
@ -19,27 +18,6 @@ already_AddRefed<NativeFontResourceGDI>
|
||||
NativeFontResourceGDI::Create(uint8_t *aFontData, uint32_t aDataLength,
|
||||
bool aNeedsCairo)
|
||||
{
|
||||
UniquePtr<SFNTData> sfntData = SFNTData::Create(aFontData, aDataLength);
|
||||
if (!sfntData) {
|
||||
gfxWarning() << "Failed to create SFNTData for ScaledFontWin.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Vector<mozilla::u16string> fontNames;
|
||||
if (!sfntData->GetU16FullNames(fontNames)) {
|
||||
gfxWarning() << "Failed to get font names from font.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// lfFaceName has a maximum length including null.
|
||||
for (size_t i = 0; i < fontNames.length(); ++i) {
|
||||
if (fontNames[i].size() > LF_FACESIZE - 1) {
|
||||
fontNames[i].resize(LF_FACESIZE - 1);
|
||||
}
|
||||
// Add null to end for easy copying later.
|
||||
fontNames[i].append(1, '\0');
|
||||
}
|
||||
|
||||
DWORD numberOfFontsAdded;
|
||||
HANDLE fontResourceHandle = ::AddFontMemResourceEx(aFontData, aDataLength,
|
||||
0, &numberOfFontsAdded);
|
||||
@ -48,13 +26,8 @@ NativeFontResourceGDI::Create(uint8_t *aFontData, uint32_t aDataLength,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (numberOfFontsAdded != fontNames.length()) {
|
||||
gfxWarning() <<
|
||||
"Number of fonts added doesn't match number of names extracted.";
|
||||
}
|
||||
|
||||
RefPtr<NativeFontResourceGDI> fontResouce =
|
||||
new NativeFontResourceGDI(fontResourceHandle, Move(fontNames), aNeedsCairo);
|
||||
new NativeFontResourceGDI(fontResourceHandle, aNeedsCairo);
|
||||
|
||||
return fontResouce.forget();
|
||||
}
|
||||
@ -68,41 +41,18 @@ already_AddRefed<ScaledFont>
|
||||
NativeFontResourceGDI::CreateScaledFont(uint32_t aIndex, Float aGlyphSize,
|
||||
const uint8_t* aInstanceData, uint32_t aInstanceDataLength)
|
||||
{
|
||||
if (aIndex >= mFontNames.length()) {
|
||||
gfxWarning() << "Font index is too high for font resource.";
|
||||
if (aInstanceDataLength < sizeof(LOGFONT)) {
|
||||
gfxWarning() << "GDI scaled font instance data is truncated.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (mFontNames[aIndex].empty()) {
|
||||
gfxWarning() << "Font name for index is empty.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LOGFONT logFont;
|
||||
logFont.lfHeight = 0;
|
||||
logFont.lfWidth = 0;
|
||||
logFont.lfEscapement = 0;
|
||||
logFont.lfOrientation = 0;
|
||||
logFont.lfWeight = FW_DONTCARE;
|
||||
logFont.lfItalic = FALSE;
|
||||
logFont.lfUnderline = FALSE;
|
||||
logFont.lfStrikeOut = FALSE;
|
||||
logFont.lfCharSet = DEFAULT_CHARSET;
|
||||
logFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
|
||||
logFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
|
||||
logFont.lfQuality = DEFAULT_QUALITY;
|
||||
logFont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
|
||||
|
||||
// Copy name to mLogFont (null already included in font name). We cast here
|
||||
// because for VS2015 char16_t != wchar_t, even though they are both 16 bit.
|
||||
mFontNames[aIndex].copy(reinterpret_cast<char16_t*>(logFont.lfFaceName),
|
||||
mFontNames[aIndex].length());
|
||||
const LOGFONT* logFont = reinterpret_cast<const LOGFONT*>(aInstanceData);
|
||||
|
||||
// Constructor for ScaledFontWin dereferences and copies the LOGFONT, so we
|
||||
// are safe to pass this reference.
|
||||
RefPtr<ScaledFontBase> scaledFont = new ScaledFontWin(&logFont, aGlyphSize);
|
||||
RefPtr<ScaledFontBase> scaledFont = new ScaledFontWin(logFont, aGlyphSize);
|
||||
if (mNeedsCairo && !scaledFont->PopulateCairoScaledFont()) {
|
||||
gfxWarning() << "Unable to create cairo scaled font DWrite font.";
|
||||
gfxWarning() << "Unable to create cairo scaled font GDI font.";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include "2D.h"
|
||||
#include "mozilla/AlreadyAddRefed.h"
|
||||
#include "mozilla/Vector.h"
|
||||
#include "u16string.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
@ -41,14 +40,12 @@ public:
|
||||
|
||||
private:
|
||||
NativeFontResourceGDI(HANDLE aFontResourceHandle,
|
||||
Vector<mozilla::u16string>&& aFontNames,
|
||||
bool aNeedsCairo)
|
||||
: mFontResourceHandle(aFontResourceHandle), mFontNames(Move(aFontNames))
|
||||
: mFontResourceHandle(aFontResourceHandle)
|
||||
, mNeedsCairo(aNeedsCairo)
|
||||
{}
|
||||
|
||||
HANDLE mFontResourceHandle;
|
||||
Vector<mozilla::u16string> mFontNames;
|
||||
bool mNeedsCairo;
|
||||
};
|
||||
|
||||
|
@ -24,7 +24,7 @@ const uint32_t kMagicInt = 0xc001feed;
|
||||
// loss of backwards compatibility. Old streams will not work in a player
|
||||
// using a newer major revision. And new streams will not work in a player
|
||||
// using an older major revision.
|
||||
const uint16_t kMajorRevision = 7;
|
||||
const uint16_t kMajorRevision = 8;
|
||||
// A change in minor revision means additions of new events. New streams will
|
||||
// not play in older players.
|
||||
const uint16_t kMinorRevision = 0;
|
||||
|
@ -278,6 +278,7 @@ ScaledFontFontconfig::GetFontDescriptor(FontDescriptorOutput aCb, void* aBaton)
|
||||
memcpy(data + sizeof(FontDescriptor), pathname, pathLength);
|
||||
|
||||
aCb(data, dataLength, mSize, aBaton);
|
||||
delete[] data;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,6 @@
|
||||
#include "AutoHelpersWin.h"
|
||||
#include "Logging.h"
|
||||
#include "nsString.h"
|
||||
#include "SFNTData.h"
|
||||
|
||||
#ifdef USE_SKIA
|
||||
#include "skia/include/ports/SkTypeface_win.h"
|
||||
@ -23,7 +22,7 @@
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
ScaledFontWin::ScaledFontWin(LOGFONT* aFont, Float aSize)
|
||||
ScaledFontWin::ScaledFontWin(const LOGFONT* aFont, Float aSize)
|
||||
: ScaledFontBase(aSize)
|
||||
, mLogFont(*aFont)
|
||||
{
|
||||
@ -55,29 +54,14 @@ ScaledFontWin::GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton)
|
||||
return false;
|
||||
}
|
||||
|
||||
// If it's a font collection then attempt to get the index.
|
||||
uint32_t index = 0;
|
||||
if (table != 0) {
|
||||
UniquePtr<SFNTData> sfntData = SFNTData::Create(fontData.get(),
|
||||
tableSize);
|
||||
if (!sfntData) {
|
||||
gfxWarning() << "Failed to create SFNTData for GetFontFileData.";
|
||||
return false;
|
||||
}
|
||||
aDataCallback(fontData.get(), tableSize, 0, mSize, 0, nullptr, aBaton);
|
||||
return true;
|
||||
}
|
||||
|
||||
// We cast here because for VS2015 char16_t != wchar_t, even though they are
|
||||
// both 16 bit.
|
||||
if (!sfntData->GetIndexForU16Name(
|
||||
reinterpret_cast<char16_t*>(mLogFont.lfFaceName), &index, LF_FACESIZE - 1)) {
|
||||
gfxWarning() << "Failed to get index for face name.";
|
||||
gfxDevCrash(LogReason::GetFontFileDataFailed) <<
|
||||
"Failed to get index for face name |" <<
|
||||
NS_ConvertUTF16toUTF8(mLogFont.lfFaceName).get() << "|.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
aDataCallback(fontData.get(), tableSize, index, mSize, 0, nullptr, aBaton);
|
||||
bool
|
||||
ScaledFontWin::GetFontInstanceData(FontInstanceDataOutput aCb, void* aBaton)
|
||||
{
|
||||
aCb(reinterpret_cast<uint8_t*>(&mLogFont), sizeof(mLogFont), aBaton);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -16,12 +16,14 @@ class ScaledFontWin : public ScaledFontBase
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFontWin)
|
||||
ScaledFontWin(LOGFONT* aFont, Float aSize);
|
||||
ScaledFontWin(const LOGFONT* aFont, Float aSize);
|
||||
|
||||
virtual FontType GetType() const { return FontType::GDI; }
|
||||
|
||||
bool GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton) override;
|
||||
|
||||
bool GetFontInstanceData(FontInstanceDataOutput aCb, void* aBaton) override;
|
||||
|
||||
virtual bool GetFontDescriptor(FontDescriptorOutput aCb, void* aBaton) override;
|
||||
|
||||
static already_AddRefed<ScaledFont>
|
||||
|
@ -80,19 +80,22 @@ GPUChild::OnVarChanged(const GfxVarUpdate& aVar)
|
||||
SendUpdateVar(aVar);
|
||||
}
|
||||
|
||||
void
|
||||
bool
|
||||
GPUChild::EnsureGPUReady()
|
||||
{
|
||||
if (mGPUReady) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
GPUDeviceData data;
|
||||
SendGetDeviceStatus(&data);
|
||||
if (!SendGetDeviceStatus(&data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
gfxPlatform::GetPlatform()->ImportGPUDeviceData(data);
|
||||
Telemetry::AccumulateTimeDelta(Telemetry::GPU_PROCESS_LAUNCH_TIME_MS_2, mHost->GetLaunchTime());
|
||||
mGPUReady = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
|
@ -41,7 +41,7 @@ public:
|
||||
|
||||
void Init();
|
||||
|
||||
void EnsureGPUReady();
|
||||
bool EnsureGPUReady();
|
||||
|
||||
// gfxVarReceiver overrides.
|
||||
void OnVarChanged(const GfxVarUpdate& aVar) override;
|
||||
|
@ -163,7 +163,7 @@ GPUProcessManager::DisableGPUProcess(const char* aMessage)
|
||||
ShutdownVsyncIOThread();
|
||||
}
|
||||
|
||||
void
|
||||
bool
|
||||
GPUProcessManager::EnsureGPUReady()
|
||||
{
|
||||
if (mProcess && !mProcess->IsConnected()) {
|
||||
@ -171,13 +171,15 @@ GPUProcessManager::EnsureGPUReady()
|
||||
// If this fails, we should have fired OnProcessLaunchComplete and
|
||||
// removed the process.
|
||||
MOZ_ASSERT(!mProcess && !mGPUChild);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (mGPUChild) {
|
||||
mGPUChild->EnsureGPUReady();
|
||||
if (mGPUChild && mGPUChild->EnsureGPUReady()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
@ -187,9 +189,7 @@ GPUProcessManager::EnsureImageBridgeChild()
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureGPUReady();
|
||||
|
||||
if (!mGPUChild) {
|
||||
if (!EnsureGPUReady()) {
|
||||
ImageBridgeChild::InitSameProcess();
|
||||
return;
|
||||
}
|
||||
@ -217,9 +217,7 @@ GPUProcessManager::EnsureVRManager()
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureGPUReady();
|
||||
|
||||
if (!mGPUChild) {
|
||||
if (!EnsureGPUReady()) {
|
||||
VRManagerChild::InitSameProcess();
|
||||
return;
|
||||
}
|
||||
@ -248,15 +246,13 @@ GPUProcessManager::EnsureUiCompositorController()
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureGPUReady();
|
||||
|
||||
RefPtr<nsThread> uiThread;
|
||||
|
||||
uiThread = GetAndroidUiThread();
|
||||
|
||||
MOZ_ASSERT(uiThread);
|
||||
|
||||
if (!mGPUChild) {
|
||||
if (!EnsureGPUReady()) {
|
||||
UiCompositorControllerChild::InitSameProcess(uiThread);
|
||||
return;
|
||||
}
|
||||
@ -546,14 +542,13 @@ GPUProcessManager::CreateTopLevelCompositor(nsBaseWidget* aWidget,
|
||||
{
|
||||
uint64_t layerTreeId = AllocateLayerTreeId();
|
||||
|
||||
EnsureGPUReady();
|
||||
EnsureImageBridgeChild();
|
||||
EnsureVRManager();
|
||||
#if defined(MOZ_WIDGET_ANDROID)
|
||||
EnsureUiCompositorController();
|
||||
#endif // defined(MOZ_WIDGET_ANDROID)
|
||||
|
||||
if (mGPUChild) {
|
||||
if (EnsureGPUReady()) {
|
||||
RefPtr<CompositorSession> session = CreateRemoteSession(
|
||||
aWidget,
|
||||
aLayerManager,
|
||||
@ -682,12 +677,10 @@ bool
|
||||
GPUProcessManager::CreateContentCompositorBridge(base::ProcessId aOtherProcess,
|
||||
ipc::Endpoint<PCompositorBridgeChild>* aOutEndpoint)
|
||||
{
|
||||
EnsureGPUReady();
|
||||
|
||||
ipc::Endpoint<PCompositorBridgeParent> parentPipe;
|
||||
ipc::Endpoint<PCompositorBridgeChild> childPipe;
|
||||
|
||||
base::ProcessId gpuPid = mGPUChild
|
||||
base::ProcessId gpuPid = EnsureGPUReady()
|
||||
? mGPUChild->OtherPid()
|
||||
: base::GetCurrentProcId();
|
||||
|
||||
@ -701,7 +694,7 @@ GPUProcessManager::CreateContentCompositorBridge(base::ProcessId aOtherProcess,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mGPUChild) {
|
||||
if (EnsureGPUReady()) {
|
||||
mGPUChild->SendNewContentCompositorBridge(Move(parentPipe));
|
||||
} else {
|
||||
if (!CompositorBridgeParent::CreateForContent(Move(parentPipe))) {
|
||||
@ -735,7 +728,7 @@ GPUProcessManager::CreateContentImageBridge(base::ProcessId aOtherProcess,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mGPUChild) {
|
||||
if (EnsureGPUReady()) {
|
||||
mGPUChild->SendNewContentImageBridge(Move(parentPipe));
|
||||
} else {
|
||||
if (!ImageBridgeParent::CreateForContent(Move(parentPipe))) {
|
||||
@ -778,7 +771,7 @@ GPUProcessManager::CreateContentVRManager(base::ProcessId aOtherProcess,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mGPUChild) {
|
||||
if (EnsureGPUReady()) {
|
||||
mGPUChild->SendNewContentVRManager(Move(parentPipe));
|
||||
} else {
|
||||
if (!VRManagerParent::CreateForContent(Move(parentPipe))) {
|
||||
@ -794,7 +787,7 @@ void
|
||||
GPUProcessManager::CreateContentVideoDecoderManager(base::ProcessId aOtherProcess,
|
||||
ipc::Endpoint<dom::PVideoDecoderManagerChild>* aOutEndpoint)
|
||||
{
|
||||
if (!mGPUChild || !MediaPrefs::PDMUseGPUDecoder()) {
|
||||
if (!EnsureGPUReady() || !MediaPrefs::PDMUseGPUDecoder()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -828,7 +821,7 @@ GPUProcessManager::MapLayerTreeId(uint64_t aLayersId, base::ProcessId aOwningId)
|
||||
{
|
||||
LayerTreeOwnerTracker::Get()->Map(aLayersId, aOwningId);
|
||||
|
||||
if (mGPUChild) {
|
||||
if (EnsureGPUReady()) {
|
||||
AutoTArray<LayerTreeIdMapping, 1> mappings;
|
||||
mappings.AppendElement(LayerTreeIdMapping(aLayersId, aOwningId));
|
||||
mGPUChild->SendAddLayerTreeIdMapping(mappings);
|
||||
@ -840,7 +833,7 @@ GPUProcessManager::UnmapLayerTreeId(uint64_t aLayersId, base::ProcessId aOwningI
|
||||
{
|
||||
LayerTreeOwnerTracker::Get()->Unmap(aLayersId, aOwningId);
|
||||
|
||||
if (mGPUChild) {
|
||||
if (EnsureGPUReady()) {
|
||||
mGPUChild->SendRemoveLayerTreeIdMapping(LayerTreeIdMapping(aLayersId, aOwningId));
|
||||
return;
|
||||
}
|
||||
@ -904,7 +897,7 @@ GPUProcessManager::RemoveListener(GPUProcessListener* aListener)
|
||||
bool
|
||||
GPUProcessManager::NotifyGpuObservers(const char* aTopic)
|
||||
{
|
||||
if (!mGPUChild) {
|
||||
if (!EnsureGPUReady()) {
|
||||
return false;
|
||||
}
|
||||
nsCString topic(aTopic);
|
||||
@ -962,7 +955,7 @@ protected:
|
||||
RefPtr<MemoryReportingProcess>
|
||||
GPUProcessManager::GetProcessMemoryReporter()
|
||||
{
|
||||
if (!mGPUChild) {
|
||||
if (!EnsureGPUReady()) {
|
||||
return nullptr;
|
||||
}
|
||||
return new GPUMemoryReporter();
|
||||
|
@ -80,7 +80,7 @@ public:
|
||||
// Ensure that GPU-bound methods can be used. If no GPU process is being
|
||||
// used, or one is launched and ready, this function returns immediately.
|
||||
// Otherwise it blocks until the GPU process has finished launching.
|
||||
void EnsureGPUReady();
|
||||
bool EnsureGPUReady();
|
||||
|
||||
RefPtr<CompositorSession> CreateTopLevelCompositor(
|
||||
nsBaseWidget* aWidget,
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "YCbCrUtils.h" // for YCbCr conversions
|
||||
#include "gfx2DGlue.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "mozilla/CheckedInt.h"
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
#include "mozilla/gfx/QuartzSupport.h"
|
||||
@ -499,8 +500,15 @@ RecyclingPlanarYCbCrImage::CopyData(const Data& aData)
|
||||
mData = aData;
|
||||
|
||||
// update buffer size
|
||||
size_t size = mData.mCbCrStride * mData.mCbCrSize.height * 2 +
|
||||
mData.mYStride * mData.mYSize.height;
|
||||
// Use uint32_t throughout to match AllocateBuffer's param and mBufferSize
|
||||
const auto checkedSize =
|
||||
CheckedInt<uint32_t>(mData.mCbCrStride) * mData.mCbCrSize.height * 2 +
|
||||
CheckedInt<uint32_t>(mData.mYStride) * mData.mYSize.height;
|
||||
|
||||
if (!checkedSize.isValid())
|
||||
return false;
|
||||
|
||||
const auto size = checkedSize.value();
|
||||
|
||||
// get new buffer
|
||||
mBuffer = AllocateBuffer(size);
|
||||
@ -703,8 +711,15 @@ NVImage::SetData(const Data& aData)
|
||||
MOZ_ASSERT((int)std::abs(aData.mCbChannel - aData.mCrChannel) == 1);
|
||||
|
||||
// Calculate buffer size
|
||||
const uint32_t size = aData.mYSize.height * aData.mYStride +
|
||||
aData.mCbCrSize.height * aData.mCbCrStride;
|
||||
// Use uint32_t throughout to match AllocateBuffer's param and mBufferSize
|
||||
const auto checkedSize =
|
||||
CheckedInt<uint32_t>(aData.mYSize.height) * aData.mYStride +
|
||||
CheckedInt<uint32_t>(aData.mCbCrSize.height) * aData.mCbCrStride;
|
||||
|
||||
if (!checkedSize.isValid())
|
||||
return false;
|
||||
|
||||
const auto size = checkedSize.value();
|
||||
|
||||
// Allocate a new buffer.
|
||||
mBuffer = AllocateBuffer(size);
|
||||
|
@ -28,7 +28,7 @@ var lastExc = null;
|
||||
var todo = [];
|
||||
var activeTask;
|
||||
var options = { 'pretty': true,
|
||||
'emacs': (os.getenv('EMACS') == 't') };
|
||||
'emacs': !!os.getenv('INSIDE_EMACS') };
|
||||
var rerun = true;
|
||||
|
||||
// Cleanup functions to run when we next re-enter the repl.
|
||||
|
@ -741,6 +741,7 @@ GCPreserveCode(JSContext* cx, unsigned argc, Value* vp)
|
||||
}
|
||||
|
||||
#ifdef JS_GC_ZEAL
|
||||
|
||||
static bool
|
||||
GCZeal(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
@ -902,6 +903,16 @@ DeterministicGC(JSContext* cx, unsigned argc, Value* vp)
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
DumpGCArenaInfo(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
js::gc::DumpArenaInfo();
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif /* JS_GC_ZEAL */
|
||||
|
||||
static bool
|
||||
@ -4451,6 +4462,11 @@ gc::ZealModeHelpText),
|
||||
JS_FN_HELP("deterministicgc", DeterministicGC, 1, 0,
|
||||
"deterministicgc(true|false)",
|
||||
" If true, only allow determinstic GCs to run."),
|
||||
|
||||
JS_FN_HELP("dumpGCArenaInfo", DumpGCArenaInfo, 0, 0,
|
||||
"dumpGCArenaInfo()",
|
||||
" Prints information about the different GC things and how they are arranged\n"
|
||||
" in arenas.\n"),
|
||||
#endif
|
||||
|
||||
JS_FN_HELP("startgc", StartGC, 1, 0,
|
||||
|
@ -30,8 +30,6 @@
|
||||
|
||||
#if defined(XP_UNIX)
|
||||
#include <errno.h>
|
||||
#elif defined(XP_WIN)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "jscntxt.h"
|
||||
@ -39,6 +37,7 @@
|
||||
#include "jsfun.h"
|
||||
#include "jsnum.h"
|
||||
#include "jsprf.h"
|
||||
#include "jswin.h"
|
||||
|
||||
#include "builtin/TypedObject.h"
|
||||
#include "ctypes/Library.h"
|
||||
|
@ -9,7 +9,7 @@
|
||||
// in the same process.
|
||||
|
||||
#ifdef WIN32
|
||||
#include "windows.h"
|
||||
#include "jswin.h"
|
||||
#else
|
||||
#define __cdecl
|
||||
#include <stdarg.h>
|
||||
|
@ -37,6 +37,12 @@ class MarkingValidator;
|
||||
class AutoTraceSession;
|
||||
struct MovingTracer;
|
||||
|
||||
enum IncrementalProgress
|
||||
{
|
||||
NotFinished = 0,
|
||||
Finished
|
||||
};
|
||||
|
||||
class ChunkPool
|
||||
{
|
||||
Chunk* head_;
|
||||
@ -771,6 +777,8 @@ class GCRuntime
|
||||
|
||||
bool isShrinkingGC() const { return invocationKind == GC_SHRINK; }
|
||||
|
||||
static bool initializeSweepActions();
|
||||
|
||||
void setGrayRootsTracer(JSTraceDataOp traceOp, void* data);
|
||||
MOZ_MUST_USE bool addBlackRootsTracer(JSTraceDataOp traceOp, void* data);
|
||||
void removeBlackRootsTracer(JSTraceDataOp traceOp, void* data);
|
||||
@ -892,12 +900,6 @@ class GCRuntime
|
||||
static TenuredCell* refillFreeListInGC(Zone* zone, AllocKind thingKind);
|
||||
|
||||
private:
|
||||
enum IncrementalProgress
|
||||
{
|
||||
NotFinished = 0,
|
||||
Finished
|
||||
};
|
||||
|
||||
enum IncrementalResult
|
||||
{
|
||||
Reset = 0,
|
||||
@ -987,7 +989,16 @@ class GCRuntime
|
||||
void beginSweepingSweepGroup(AutoLockForExclusiveAccess& lock);
|
||||
bool shouldReleaseObservedTypes();
|
||||
void endSweepingSweepGroup();
|
||||
IncrementalProgress sweepPhase(SliceBudget& sliceBudget, AutoLockForExclusiveAccess& lock);
|
||||
IncrementalProgress performSweepActions(SliceBudget& sliceBudget,
|
||||
AutoLockForExclusiveAccess& lock);
|
||||
static IncrementalProgress sweepTypeInformation(GCRuntime* gc, FreeOp* fop, Zone* zone,
|
||||
SliceBudget& budget, AllocKind kind);
|
||||
static IncrementalProgress mergeSweptObjectArenas(GCRuntime* gc, FreeOp* fop, Zone* zone,
|
||||
SliceBudget& budget, AllocKind kind);
|
||||
static IncrementalProgress finalizeAllocKind(GCRuntime* gc, FreeOp* fop, Zone* zone,
|
||||
SliceBudget& budget, AllocKind kind);
|
||||
static IncrementalProgress sweepShapeTree(GCRuntime* gc, FreeOp* fop, Zone* zone,
|
||||
SliceBudget& budget, AllocKind kind);
|
||||
void endSweepPhase(bool lastGC, AutoLockForExclusiveAccess& lock);
|
||||
bool allCCVisibleZonesWereCollected() const;
|
||||
void sweepZones(FreeOp* fop, ZoneGroup* group, bool lastGC);
|
||||
@ -1206,10 +1217,9 @@ class GCRuntime
|
||||
*/
|
||||
ActiveThreadData<JS::Zone*> sweepGroups;
|
||||
ActiveThreadOrGCTaskData<JS::Zone*> currentSweepGroup;
|
||||
ActiveThreadData<bool> sweepingTypes;
|
||||
ActiveThreadData<unsigned> finalizePhase;
|
||||
ActiveThreadData<size_t> sweepPhaseIndex;
|
||||
ActiveThreadData<JS::Zone*> sweepZone;
|
||||
ActiveThreadData<AllocKind> sweepKind;
|
||||
ActiveThreadData<size_t> sweepActionIndex;
|
||||
ActiveThreadData<bool> abortSweepAfterCurrentGroup;
|
||||
|
||||
/*
|
||||
|
@ -190,12 +190,6 @@ ExecutableAllocator::poolForSize(size_t n)
|
||||
/* static */ size_t
|
||||
ExecutableAllocator::roundUpAllocationSize(size_t request, size_t granularity)
|
||||
{
|
||||
// Something included via windows.h defines a macro with this name,
|
||||
// which causes the function below to fail to compile.
|
||||
#ifdef _MSC_VER
|
||||
# undef max
|
||||
#endif
|
||||
|
||||
if ((std::numeric_limits<size_t>::max() - granularity) <= request)
|
||||
return OVERSIZE_ALLOCATION;
|
||||
|
||||
|
@ -33,9 +33,6 @@
|
||||
#include "vm/TypedArrayObject.h"
|
||||
#include "vm/UnboxedObject.h"
|
||||
|
||||
// Undo windows.h damage on Win64
|
||||
#undef MemoryBarrier
|
||||
|
||||
namespace js {
|
||||
|
||||
class StringObject;
|
||||
|
@ -13,10 +13,6 @@
|
||||
#include "jit/IonAnalysis.h"
|
||||
#include "jit/MIR.h"
|
||||
|
||||
// windows.h defines those, which messes with the definitions below.
|
||||
#undef min
|
||||
#undef max
|
||||
|
||||
namespace js {
|
||||
namespace jit {
|
||||
|
||||
|
@ -25,7 +25,6 @@
|
||||
#endif // ANDROID
|
||||
#ifdef XP_WIN
|
||||
#include <processthreadsapi.h>
|
||||
#include <windows.h>
|
||||
#endif // XP_WIN
|
||||
|
||||
#include "jsatom.h"
|
||||
@ -44,6 +43,7 @@
|
||||
#include "jsstr.h"
|
||||
#include "jstypes.h"
|
||||
#include "jswatchpoint.h"
|
||||
#include "jswin.h"
|
||||
|
||||
#include "gc/Marking.h"
|
||||
#include "jit/Ion.h"
|
||||
|
306
js/src/jsgc.cpp
306
js/src/jsgc.cpp
@ -193,6 +193,7 @@
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/ScopeExit.h"
|
||||
#include "mozilla/SizePrintfMacros.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
|
||||
#include <ctype.h>
|
||||
@ -394,6 +395,42 @@ static const FinalizePhase BackgroundFinalizePhases[] = {
|
||||
}
|
||||
};
|
||||
|
||||
// Incremental sweeping is controlled by a list of actions that describe what
|
||||
// happens and in what order. Due to the incremental nature of sweeping an
|
||||
// action does not necessarily run to completion so the current state is tracked
|
||||
// in the GCRuntime by the performSweepActions() method.
|
||||
//
|
||||
// Actions are performed in phases run per sweep group, and each action is run
|
||||
// for every zone in the group, i.e. as if by the following pseudocode:
|
||||
//
|
||||
// for each sweep group:
|
||||
// for each phase:
|
||||
// for each zone in sweep group:
|
||||
// for each action in phase:
|
||||
// perform_action
|
||||
|
||||
struct SweepAction
|
||||
{
|
||||
using Func = IncrementalProgress (*)(GCRuntime* gc, FreeOp* fop, Zone* zone,
|
||||
SliceBudget& budget, AllocKind kind);
|
||||
|
||||
Func func;
|
||||
AllocKind kind;
|
||||
|
||||
SweepAction(Func func, AllocKind kind) : func(func), kind(kind) {}
|
||||
};
|
||||
|
||||
using SweepActionVector = Vector<SweepAction, 0, SystemAllocPolicy>;
|
||||
using SweepPhaseVector = Vector<SweepActionVector, 0, SystemAllocPolicy>;
|
||||
|
||||
static SweepPhaseVector SweepPhases;
|
||||
|
||||
bool
|
||||
js::gc::InitializeStaticData()
|
||||
{
|
||||
return GCRuntime::initializeSweepActions();
|
||||
}
|
||||
|
||||
template<>
|
||||
JSObject*
|
||||
ArenaCellIterImpl::get<JSObject>() const
|
||||
@ -846,8 +883,9 @@ GCRuntime::GCRuntime(JSRuntime* rt) :
|
||||
sweepGroupIndex(0),
|
||||
sweepGroups(nullptr),
|
||||
currentSweepGroup(nullptr),
|
||||
sweepPhaseIndex(0),
|
||||
sweepZone(nullptr),
|
||||
sweepKind(AllocKind::FIRST),
|
||||
sweepActionIndex(0),
|
||||
abortSweepAfterCurrentGroup(false),
|
||||
arenasAllocatedDuringSweep(nullptr),
|
||||
startedCompacting(false),
|
||||
@ -1024,7 +1062,41 @@ GCRuntime::parseAndSetZeal(const char* str)
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
static const char*
|
||||
AllocKindName(AllocKind kind)
|
||||
{
|
||||
static const char* names[] = {
|
||||
#define EXPAND_THING_NAME(allocKind, _1, _2, _3) \
|
||||
#allocKind,
|
||||
FOR_EACH_ALLOCKIND(EXPAND_THING_NAME)
|
||||
#undef EXPAND_THING_NAME
|
||||
};
|
||||
static_assert(ArrayLength(names) == size_t(AllocKind::LIMIT),
|
||||
"names array should have an entry for every AllocKind");
|
||||
|
||||
size_t i = size_t(kind);
|
||||
MOZ_ASSERT(i < ArrayLength(names));
|
||||
return names[i];
|
||||
}
|
||||
|
||||
void
|
||||
js::gc::DumpArenaInfo()
|
||||
{
|
||||
fprintf(stderr, "Arena header size: %" PRIuSIZE "\n\n", ArenaHeaderSize);
|
||||
|
||||
fprintf(stderr, "GC thing kinds:\n");
|
||||
fprintf(stderr, "%25s %8s %8s %8s\n", "AllocKind:", "Size:", "Count:", "Padding:");
|
||||
for (auto kind : AllAllocKinds()) {
|
||||
fprintf(stderr,
|
||||
"%25s %8" PRIuSIZE " %8" PRIuSIZE " %8" PRIuSIZE "\n",
|
||||
AllocKindName(kind),
|
||||
Arena::thingSize(kind),
|
||||
Arena::thingsPerArena(kind),
|
||||
Arena::firstThingOffset(kind) - ArenaHeaderSize);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // JS_GC_ZEAL
|
||||
|
||||
/*
|
||||
* Lifetime in number of major GCs for type sets attached to scripts containing
|
||||
@ -1035,7 +1107,7 @@ static const uint64_t JIT_SCRIPT_RELEASE_TYPES_PERIOD = 20;
|
||||
bool
|
||||
GCRuntime::init(uint32_t maxbytes, uint32_t maxNurseryBytes)
|
||||
{
|
||||
InitMemorySubsystem();
|
||||
MOZ_ASSERT(SystemPageSize());
|
||||
|
||||
if (!rootsHash.ref().init(256))
|
||||
return false;
|
||||
@ -5200,11 +5272,9 @@ GCRuntime::beginSweepingSweepGroup(AutoLockForExclusiveAccess& lock)
|
||||
zone->arenas.queueForegroundThingsForSweep(&fop);
|
||||
}
|
||||
|
||||
sweepingTypes = true;
|
||||
|
||||
finalizePhase = 0;
|
||||
sweepPhaseIndex = 0;
|
||||
sweepZone = currentSweepGroup;
|
||||
sweepKind = AllocKind::FIRST;
|
||||
sweepActionIndex = 0;
|
||||
|
||||
{
|
||||
gcstats::AutoPhase ap(stats(), gcstats::PHASE_FINALIZE_END);
|
||||
@ -5300,7 +5370,7 @@ ArenaLists::foregroundFinalize(FreeOp* fop, AllocKind thingKind, SliceBudget& sl
|
||||
return true;
|
||||
}
|
||||
|
||||
GCRuntime::IncrementalProgress
|
||||
IncrementalProgress
|
||||
GCRuntime::drainMarkStack(SliceBudget& sliceBudget, gcstats::Phase phase)
|
||||
{
|
||||
/* Run a marking slice and return whether the stack is now empty. */
|
||||
@ -5345,104 +5415,154 @@ SweepArenaList(Arena** arenasToSweep, SliceBudget& sliceBudget, Args... args)
|
||||
return true;
|
||||
}
|
||||
|
||||
GCRuntime::IncrementalProgress
|
||||
GCRuntime::sweepPhase(SliceBudget& sliceBudget, AutoLockForExclusiveAccess& lock)
|
||||
/* static */ IncrementalProgress
|
||||
GCRuntime::sweepTypeInformation(GCRuntime* gc, FreeOp* fop, Zone* zone, SliceBudget& budget,
|
||||
AllocKind kind)
|
||||
{
|
||||
// Sweep dead type information stored in scripts and object groups, but
|
||||
// don't finalize them yet. We have to sweep dead information from both live
|
||||
// and dead scripts and object groups, so that no dead references remain in
|
||||
// them. Type inference can end up crawling these zones again, such as for
|
||||
// TypeCompartment::markSetsUnknown, and if this happens after sweeping for
|
||||
// the sweep group finishes we won't be able to determine which things in
|
||||
// the zone are live.
|
||||
|
||||
MOZ_ASSERT(kind == AllocKind::LIMIT);
|
||||
|
||||
gcstats::AutoPhase ap1(gc->stats(), gcstats::PHASE_SWEEP_COMPARTMENTS);
|
||||
gcstats::AutoPhase ap2(gc->stats(), gcstats::PHASE_SWEEP_TYPES);
|
||||
|
||||
ArenaLists& al = zone->arenas;
|
||||
|
||||
AutoClearTypeInferenceStateOnOOM oom(zone);
|
||||
|
||||
if (!SweepArenaList<JSScript>(&al.gcScriptArenasToUpdate.ref(), budget, &oom))
|
||||
return NotFinished;
|
||||
|
||||
if (!SweepArenaList<ObjectGroup>(&al.gcObjectGroupArenasToUpdate.ref(), budget, &oom))
|
||||
return NotFinished;
|
||||
|
||||
// Finish sweeping type information in the zone.
|
||||
{
|
||||
gcstats::AutoPhase ap(gc->stats(), gcstats::PHASE_SWEEP_TYPES_END);
|
||||
zone->types.endSweep(gc->rt);
|
||||
}
|
||||
|
||||
return Finished;
|
||||
}
|
||||
|
||||
/* static */ IncrementalProgress
|
||||
GCRuntime::mergeSweptObjectArenas(GCRuntime* gc, FreeOp* fop, Zone* zone, SliceBudget& budget,
|
||||
AllocKind kind)
|
||||
{
|
||||
// Foreground finalized objects have already been finalized, and now their
|
||||
// arenas can be reclaimed by freeing empty ones and making non-empty ones
|
||||
// available for allocation.
|
||||
|
||||
MOZ_ASSERT(kind == AllocKind::LIMIT);
|
||||
zone->arenas.mergeForegroundSweptObjectArenas();
|
||||
return Finished;
|
||||
}
|
||||
|
||||
/* static */ IncrementalProgress
|
||||
GCRuntime::finalizeAllocKind(GCRuntime* gc, FreeOp* fop, Zone* zone, SliceBudget& budget,
|
||||
AllocKind kind)
|
||||
{
|
||||
// Set the number of things per arena for this AllocKind.
|
||||
size_t thingsPerArena = Arena::thingsPerArena(kind);
|
||||
auto& sweepList = gc->incrementalSweepList.ref();
|
||||
sweepList.setThingsPerArena(thingsPerArena);
|
||||
|
||||
if (!zone->arenas.foregroundFinalize(fop, kind, budget, sweepList))
|
||||
return NotFinished;
|
||||
|
||||
// Reset the slots of the sweep list that we used.
|
||||
sweepList.reset(thingsPerArena);
|
||||
|
||||
return Finished;
|
||||
}
|
||||
|
||||
/* static */ IncrementalProgress
|
||||
GCRuntime::sweepShapeTree(GCRuntime* gc, FreeOp* fop, Zone* zone, SliceBudget& budget,
|
||||
AllocKind kind)
|
||||
{
|
||||
// Remove dead shapes from the shape tree, but don't finalize them yet.
|
||||
|
||||
MOZ_ASSERT(kind == AllocKind::LIMIT);
|
||||
|
||||
gcstats::AutoPhase ap(gc->stats(), gcstats::PHASE_SWEEP_SHAPE);
|
||||
|
||||
ArenaLists& al = zone->arenas;
|
||||
|
||||
if (!SweepArenaList<Shape>(&al.gcShapeArenasToUpdate.ref(), budget))
|
||||
return NotFinished;
|
||||
|
||||
if (!SweepArenaList<AccessorShape>(&al.gcAccessorShapeArenasToUpdate.ref(), budget))
|
||||
return NotFinished;
|
||||
|
||||
return Finished;
|
||||
}
|
||||
|
||||
static void
|
||||
AddSweepPhase(bool* ok)
|
||||
{
|
||||
if (*ok)
|
||||
*ok = SweepPhases.emplaceBack();
|
||||
}
|
||||
|
||||
static void
|
||||
AddSweepAction(bool* ok, SweepAction::Func func, AllocKind kind = AllocKind::LIMIT)
|
||||
{
|
||||
if (*ok)
|
||||
*ok = SweepPhases.back().emplaceBack(func, kind);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
GCRuntime::initializeSweepActions()
|
||||
{
|
||||
bool ok = true;
|
||||
|
||||
AddSweepPhase(&ok);
|
||||
AddSweepAction(&ok, GCRuntime::sweepTypeInformation);
|
||||
AddSweepAction(&ok, GCRuntime::mergeSweptObjectArenas);
|
||||
|
||||
for (const auto& finalizePhase : IncrementalFinalizePhases) {
|
||||
AddSweepPhase(&ok);
|
||||
for (auto kind : finalizePhase.kinds)
|
||||
AddSweepAction(&ok, GCRuntime::finalizeAllocKind, kind);
|
||||
}
|
||||
|
||||
AddSweepPhase(&ok);
|
||||
AddSweepAction(&ok, GCRuntime::sweepShapeTree);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
IncrementalProgress
|
||||
GCRuntime::performSweepActions(SliceBudget& budget, AutoLockForExclusiveAccess& lock)
|
||||
{
|
||||
AutoSetThreadIsSweeping threadIsSweeping;
|
||||
|
||||
gcstats::AutoPhase ap(stats(), gcstats::PHASE_SWEEP);
|
||||
FreeOp fop(rt);
|
||||
|
||||
if (drainMarkStack(sliceBudget, gcstats::PHASE_SWEEP_MARK) == NotFinished)
|
||||
if (drainMarkStack(budget, gcstats::PHASE_SWEEP_MARK) == NotFinished)
|
||||
return NotFinished;
|
||||
|
||||
|
||||
for (;;) {
|
||||
// Sweep dead type information stored in scripts and object groups, but
|
||||
// don't finalize them yet. We have to sweep dead information from both
|
||||
// live and dead scripts and object groups, so that no dead references
|
||||
// remain in them. Type inference can end up crawling these zones again,
|
||||
// such as for TypeCompartment::markSetsUnknown, and if this happens
|
||||
// after sweeping for the sweep group finishes we won't be able to
|
||||
// determine which things in the zone are live.
|
||||
if (sweepingTypes) {
|
||||
gcstats::AutoPhase ap1(stats(), gcstats::PHASE_SWEEP_COMPARTMENTS);
|
||||
gcstats::AutoPhase ap2(stats(), gcstats::PHASE_SWEEP_TYPES);
|
||||
|
||||
for (; sweepPhaseIndex < SweepPhases.length(); sweepPhaseIndex++) {
|
||||
const auto& actions = SweepPhases[sweepPhaseIndex];
|
||||
for (; sweepZone; sweepZone = sweepZone->nextNodeInGroup()) {
|
||||
ArenaLists& al = sweepZone->arenas;
|
||||
|
||||
AutoClearTypeInferenceStateOnOOM oom(sweepZone);
|
||||
|
||||
if (!SweepArenaList<JSScript>(&al.gcScriptArenasToUpdate.ref(), sliceBudget, &oom))
|
||||
return NotFinished;
|
||||
|
||||
if (!SweepArenaList<ObjectGroup>(
|
||||
&al.gcObjectGroupArenasToUpdate.ref(), sliceBudget, &oom))
|
||||
{
|
||||
return NotFinished;
|
||||
}
|
||||
|
||||
// Finish sweeping type information in the zone.
|
||||
{
|
||||
gcstats::AutoPhase ap(stats(), gcstats::PHASE_SWEEP_TYPES_END);
|
||||
sweepZone->types.endSweep(rt);
|
||||
}
|
||||
|
||||
// Foreground finalized objects have already been finalized,
|
||||
// and now their arenas can be reclaimed by freeing empty ones
|
||||
// and making non-empty ones available for allocation.
|
||||
al.mergeForegroundSweptObjectArenas();
|
||||
}
|
||||
|
||||
sweepZone = currentSweepGroup;
|
||||
sweepingTypes = false;
|
||||
}
|
||||
|
||||
/* Finalize foreground finalized things. */
|
||||
for (; finalizePhase < ArrayLength(IncrementalFinalizePhases) ; ++finalizePhase) {
|
||||
gcstats::AutoPhase ap(stats(), IncrementalFinalizePhases[finalizePhase].statsPhase);
|
||||
|
||||
for (; sweepZone; sweepZone = sweepZone->nextNodeInGroup()) {
|
||||
Zone* zone = sweepZone;
|
||||
|
||||
for (auto kind : SomeAllocKinds(sweepKind, AllocKind::LIMIT)) {
|
||||
if (!IncrementalFinalizePhases[finalizePhase].kinds.contains(kind))
|
||||
continue;
|
||||
|
||||
/* Set the number of things per arena for this AllocKind. */
|
||||
size_t thingsPerArena = Arena::thingsPerArena(kind);
|
||||
incrementalSweepList.ref().setThingsPerArena(thingsPerArena);
|
||||
|
||||
if (!zone->arenas.foregroundFinalize(&fop, kind, sliceBudget,
|
||||
incrementalSweepList.ref()))
|
||||
{
|
||||
sweepKind = kind;
|
||||
for (; sweepActionIndex < actions.length(); sweepActionIndex++) {
|
||||
const auto& action = actions[sweepActionIndex];
|
||||
if (action.func(this, &fop, sweepZone, budget, action.kind) == NotFinished)
|
||||
return NotFinished;
|
||||
}
|
||||
|
||||
/* Reset the slots of the sweep list that we used. */
|
||||
incrementalSweepList.ref().reset(thingsPerArena);
|
||||
}
|
||||
sweepKind = AllocKind::FIRST;
|
||||
sweepActionIndex = 0;
|
||||
}
|
||||
sweepZone = currentSweepGroup;
|
||||
}
|
||||
|
||||
/* Remove dead shapes from the shape tree, but don't finalize them yet. */
|
||||
{
|
||||
gcstats::AutoPhase ap(stats(), gcstats::PHASE_SWEEP_SHAPE);
|
||||
|
||||
for (; sweepZone; sweepZone = sweepZone->nextNodeInGroup()) {
|
||||
ArenaLists& al = sweepZone->arenas;
|
||||
|
||||
if (!SweepArenaList<Shape>(&al.gcShapeArenasToUpdate.ref(), sliceBudget))
|
||||
return NotFinished;
|
||||
|
||||
if (!SweepArenaList<AccessorShape>(&al.gcAccessorShapeArenasToUpdate.ref(), sliceBudget))
|
||||
return NotFinished;
|
||||
}
|
||||
}
|
||||
sweepPhaseIndex = 0;
|
||||
|
||||
endSweepingSweepGroup();
|
||||
getNextSweepGroup();
|
||||
@ -5565,7 +5685,7 @@ GCRuntime::beginCompactPhase()
|
||||
startedCompacting = true;
|
||||
}
|
||||
|
||||
GCRuntime::IncrementalProgress
|
||||
IncrementalProgress
|
||||
GCRuntime::compactPhase(JS::gcreason::Reason reason, SliceBudget& sliceBudget,
|
||||
AutoLockForExclusiveAccess& lock)
|
||||
{
|
||||
@ -6039,7 +6159,7 @@ GCRuntime::incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason rea
|
||||
MOZ_FALLTHROUGH;
|
||||
|
||||
case State::Sweep:
|
||||
if (sweepPhase(budget, lock) == NotFinished)
|
||||
if (performSweepActions(budget, lock) == NotFinished)
|
||||
break;
|
||||
|
||||
endSweepPhase(destroyingRuntime, lock);
|
||||
|
@ -828,6 +828,9 @@ class ArenaLists
|
||||
/* The number of GC cycles an empty chunk can survive before been released. */
|
||||
const size_t MAX_EMPTY_CHUNK_AGE = 4;
|
||||
|
||||
extern bool
|
||||
InitializeStaticData();
|
||||
|
||||
} /* namespace gc */
|
||||
|
||||
class InterpreterFrame;
|
||||
@ -1204,6 +1207,8 @@ VerifyBarriers(JSRuntime* rt, VerifierType type);
|
||||
void
|
||||
MaybeVerifyBarriers(JSContext* cx, bool always = false);
|
||||
|
||||
void DumpArenaInfo();
|
||||
|
||||
#else
|
||||
|
||||
static inline void
|
||||
|
@ -18,6 +18,7 @@
|
||||
# undef min
|
||||
# undef max
|
||||
# undef GetProp
|
||||
# undef MemoryBarrier
|
||||
# undef SetProp
|
||||
# undef CONST
|
||||
# undef STRICT
|
||||
|
@ -74,8 +74,10 @@
|
||||
#include "jit/OptimizationTracking.h"
|
||||
#include "js/Debug.h"
|
||||
#include "js/GCAPI.h"
|
||||
#include "js/GCVector.h"
|
||||
#include "js/Initialization.h"
|
||||
#include "js/StructuredClone.h"
|
||||
#include "js/SweepingAPI.h"
|
||||
#include "js/TrackedOptimizationInfo.h"
|
||||
#include "perf/jsperf.h"
|
||||
#include "shell/jsoptparse.h"
|
||||
@ -286,11 +288,27 @@ class OffThreadState {
|
||||
typedef Vector<char16_t, 0, SystemAllocPolicy> StackChars;
|
||||
#endif
|
||||
|
||||
class NonshrinkingGCObjectVector : public GCVector<JSObject*, 0, SystemAllocPolicy>
|
||||
{
|
||||
public:
|
||||
void sweep() {
|
||||
for (uint32_t i = 0; i < this->length(); i++) {
|
||||
if (JS::GCPolicy<JSObject*>::needsSweep(&(*this)[i]))
|
||||
(*this)[i] = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
using MarkBitObservers = JS::WeakCache<NonshrinkingGCObjectVector>;
|
||||
|
||||
struct ShellCompartmentPrivate {
|
||||
JS::Heap<JSObject*> grayRoot;
|
||||
};
|
||||
|
||||
// Per-context shell state.
|
||||
struct ShellContext
|
||||
{
|
||||
explicit ShellContext(JSContext* cx);
|
||||
|
||||
bool isWorker;
|
||||
double timeoutInterval;
|
||||
double startTime;
|
||||
@ -330,6 +348,7 @@ struct ShellContext
|
||||
OffThreadState offThreadState;
|
||||
|
||||
UniqueChars moduleLoadPath;
|
||||
UniquePtr<MarkBitObservers> markObservers;
|
||||
};
|
||||
|
||||
struct MOZ_STACK_CLASS EnvironmentPreparer : public js::ScriptEnvironmentPreparer {
|
||||
@ -486,6 +505,19 @@ GetShellContext(JSContext* cx)
|
||||
return sc;
|
||||
}
|
||||
|
||||
static void
|
||||
TraceGrayRoots(JSTracer* trc, void* data)
|
||||
{
|
||||
JSRuntime* rt = trc->runtime();
|
||||
for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
|
||||
for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) {
|
||||
auto priv = static_cast<ShellCompartmentPrivate*>(JS_GetCompartmentPrivate(comp.get()));
|
||||
if (priv)
|
||||
JS::TraceEdge(trc, &priv->grayRoot, "test gray root");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static char*
|
||||
GetLine(FILE* file, const char * prompt)
|
||||
{
|
||||
@ -3525,7 +3557,6 @@ WorkerMain(void* arg)
|
||||
MOZ_ASSERT(!!input->parentRuntime != !!input->siblingContext);
|
||||
|
||||
JSContext* cx = nullptr;
|
||||
ShellContext* sc = nullptr;
|
||||
|
||||
auto guard = mozilla::MakeScopeExit([&] {
|
||||
if (cx)
|
||||
@ -3535,7 +3566,6 @@ WorkerMain(void* arg)
|
||||
CooperativeYield();
|
||||
}
|
||||
js_delete(input);
|
||||
js_delete(sc);
|
||||
});
|
||||
|
||||
cx = input->parentRuntime
|
||||
@ -3544,13 +3574,14 @@ WorkerMain(void* arg)
|
||||
if (!cx)
|
||||
return;
|
||||
|
||||
sc = js_new<ShellContext>(cx);
|
||||
UniquePtr<ShellContext> sc = MakeUnique<ShellContext>(cx);
|
||||
if (!sc)
|
||||
return;
|
||||
|
||||
if (input->parentRuntime)
|
||||
sc->isWorker = true;
|
||||
JS_SetContextPrivate(cx, sc);
|
||||
JS_SetContextPrivate(cx, sc.get());
|
||||
JS_SetGrayGCRootsTracer(cx, TraceGrayRoots, nullptr);
|
||||
SetWorkerContextOptions(cx);
|
||||
sc->jobQueue.init(cx, JobQueue(SystemAllocPolicy()));
|
||||
|
||||
@ -3611,6 +3642,7 @@ WorkerMain(void* arg)
|
||||
sc->jobQueue.reset();
|
||||
|
||||
KillWatchdog(cx);
|
||||
JS_SetGrayGCRootsTracer(cx, nullptr, nullptr);
|
||||
}
|
||||
|
||||
// Workers can spawn other workers, so we need a lock to access workerThreads.
|
||||
@ -5814,6 +5846,149 @@ DumpScopeChain(JSContext* cx, unsigned argc, Value* vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
// For testing gray marking, grayRoot() will heap-allocate an address
|
||||
// where we can store a JSObject*, and create a new object if one doesn't
|
||||
// already exist.
|
||||
//
|
||||
// Note that ensureGrayRoot() will automatically blacken the returned object,
|
||||
// so it will not actually end up marked gray until the following GC clears the
|
||||
// black bit (assuming nothing is holding onto it.)
|
||||
//
|
||||
// The idea is that you can set up a whole graph of objects to be marked gray,
|
||||
// hanging off of the object returned from grayRoot(). Then you GC to clear the
|
||||
// black bits and set the gray bits.
|
||||
//
|
||||
// To test grayness, register the objects of interest with addMarkObservers(),
|
||||
// which takes an Array of objects (which will be marked black at the time
|
||||
// they're passed in). Their mark bits may be retrieved at any time with
|
||||
// getMarks(), in the form of an array of strings with each index corresponding
|
||||
// to the original objects passed to addMarkObservers().
|
||||
|
||||
static ShellCompartmentPrivate*
|
||||
EnsureShellCompartmentPrivate(JSContext* cx)
|
||||
{
|
||||
auto priv = static_cast<ShellCompartmentPrivate*>(JS_GetCompartmentPrivate(cx->compartment()));
|
||||
if (!priv) {
|
||||
priv = cx->new_<ShellCompartmentPrivate>();
|
||||
JS_SetCompartmentPrivate(cx->compartment(), priv);
|
||||
}
|
||||
return priv;
|
||||
}
|
||||
|
||||
static bool
|
||||
EnsureGrayRoot(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
auto priv = EnsureShellCompartmentPrivate(cx);
|
||||
if (!priv->grayRoot) {
|
||||
if (!(priv->grayRoot = NewDenseEmptyArray(cx, nullptr, TenuredObject)))
|
||||
return false;
|
||||
}
|
||||
|
||||
args.rval().setObject(*priv->grayRoot);
|
||||
return true;
|
||||
}
|
||||
|
||||
static MarkBitObservers*
|
||||
EnsureMarkBitObservers(JSContext* cx)
|
||||
{
|
||||
ShellContext* sc = GetShellContext(cx);
|
||||
if (!sc->markObservers) {
|
||||
sc->markObservers.reset(cx->new_<MarkBitObservers>(cx->runtime(),
|
||||
NonshrinkingGCObjectVector()));
|
||||
}
|
||||
return sc->markObservers.get();
|
||||
}
|
||||
|
||||
static bool
|
||||
ClearMarkObservers(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
auto markObservers = EnsureMarkBitObservers(cx);
|
||||
markObservers->get().clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
AddMarkObservers(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
auto markObservers = EnsureMarkBitObservers(cx);
|
||||
|
||||
if (!args.get(0).isObject()) {
|
||||
JS_ReportErrorASCII(cx, "argument must be an Array of objects");
|
||||
return false;
|
||||
}
|
||||
|
||||
// WeakCaches are not swept during a minor GC. To prevent nursery-allocated
|
||||
// contents from having the mark bits be deceptively black until the second
|
||||
// GC, they would need to be marked weakly (cf NurseryAwareHashMap). It is
|
||||
// simpler to evict the nursery to prevent nursery objects from being
|
||||
// observed.
|
||||
cx->runtime()->gc.evictNursery();
|
||||
|
||||
RootedObject observersArg(cx, &args[0].toObject());
|
||||
RootedValue v(cx);
|
||||
uint32_t length;
|
||||
if (!GetLengthProperty(cx, observersArg, &length))
|
||||
return false;
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
if (!JS_GetElement(cx, observersArg, i, &v))
|
||||
return false;
|
||||
if (!v.isObject()) {
|
||||
JS_ReportErrorASCII(cx, "argument must be an Array of objects");
|
||||
return false;
|
||||
}
|
||||
if (!markObservers->get().append(&v.toObject()))
|
||||
return false;
|
||||
}
|
||||
|
||||
args.rval().setInt32(length);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
GetMarks(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
auto& observers = GetShellContext(cx)->markObservers;
|
||||
if (!observers) {
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t length = observers->get().length();
|
||||
Rooted<ArrayObject*> ret(cx, js::NewDenseEmptyArray(cx));
|
||||
if (!ret)
|
||||
return false;
|
||||
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
const char* color;
|
||||
JSObject* obj = observers->get()[i];
|
||||
if (!obj) {
|
||||
color = "dead";
|
||||
} else {
|
||||
gc::TenuredCell* cell = &obj->asTenured();
|
||||
if (cell->isMarked(gc::GRAY))
|
||||
color = "gray";
|
||||
else if (cell->isMarked(gc::BLACK))
|
||||
color = "black";
|
||||
else
|
||||
color = "unmarked";
|
||||
}
|
||||
JSString* s = JS_NewStringCopyZ(cx, color);
|
||||
if (!s)
|
||||
return false;
|
||||
if (!NewbornArrayPush(cx, ret, StringValue(s)))
|
||||
return false;
|
||||
}
|
||||
|
||||
args.rval().setObject(*ret);
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace js {
|
||||
namespace shell {
|
||||
|
||||
@ -6644,6 +6819,30 @@ TestAssertRecoveredOnBailout,
|
||||
"dumpScopeChain(obj)",
|
||||
" Prints the scope chain of an interpreted function or a module."),
|
||||
|
||||
JS_FN_HELP("grayRoot", EnsureGrayRoot, 0, 0,
|
||||
"grayRoot()",
|
||||
" Create a gray root Array, if needed, for the current compartment, and\n"
|
||||
" return it."),
|
||||
|
||||
JS_FN_HELP("addMarkObservers", AddMarkObservers, 1, 0,
|
||||
"addMarkObservers(array_of_objects)",
|
||||
" Register an array of objects whose mark bits will be tested by calls to\n"
|
||||
" getMarks. The objects will be in calling compartment. Objects from\n"
|
||||
" multiple compartments may be monitored by calling this function in\n"
|
||||
" different compartments."),
|
||||
|
||||
JS_FN_HELP("clearMarkObservers", ClearMarkObservers, 1, 0,
|
||||
"clearMarkObservers()",
|
||||
" Clear out the list of objects whose mark bits will be tested.\n"),
|
||||
|
||||
JS_FN_HELP("getMarks", GetMarks, 0, 0,
|
||||
"getMarks()",
|
||||
" Return an array of strings representing the current state of the mark\n"
|
||||
" bits ('gray' or 'black', or 'dead' if the object has been collected)\n"
|
||||
" for the objects registered via addMarkObservers. Note that some of the\n"
|
||||
" objects tested may be from different compartments than the one in which\n"
|
||||
" this function runs."),
|
||||
|
||||
JS_FN_HELP("crash", Crash, 0, 0,
|
||||
"crash([message])",
|
||||
" Crashes the process with a MOZ_CRASH."),
|
||||
@ -8405,6 +8604,7 @@ main(int argc, char** argv, char** envp)
|
||||
return 1;
|
||||
|
||||
JS_SetContextPrivate(cx, sc.get());
|
||||
JS_SetGrayGCRootsTracer(cx, TraceGrayRoots, nullptr);
|
||||
// Waiting is allowed on the shell's main thread, for now.
|
||||
JS_SetFutexCanWait(cx);
|
||||
JS::SetWarningReporter(cx, WarningReporter);
|
||||
@ -8472,6 +8672,10 @@ main(int argc, char** argv, char** envp)
|
||||
|
||||
JS::SetGetIncumbentGlobalCallback(cx, nullptr);
|
||||
JS::SetEnqueuePromiseJobCallback(cx, nullptr);
|
||||
JS_SetGrayGCRootsTracer(cx, nullptr, nullptr);
|
||||
|
||||
// Must clear out some of sc's pointer containers before JS_DestroyContext.
|
||||
sc->markObservers.reset();
|
||||
sc->jobQueue.reset();
|
||||
|
||||
KillWatchdog(cx);
|
||||
|
150
js/src/tests/js1_8_5/extensions/collect-gray.js
Normal file
150
js/src/tests/js1_8_5/extensions/collect-gray.js
Normal file
@ -0,0 +1,150 @@
|
||||
// |reftest| skip-if(!xulRuntime.shell)
|
||||
// Any copyright is dedicated to the Public Domain.
|
||||
// http://creativecommons.org/licenses/publicdomain/
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
var BUGNUMBER = 1337209;
|
||||
var summary =
|
||||
"Test gray marking";
|
||||
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
grayRoot().x = Object.create(null);
|
||||
addMarkObservers([grayRoot(), grayRoot().x, this, Object.create(null)]);
|
||||
gc();
|
||||
let marks = getMarks();
|
||||
assertEq(marks[0], 'gray', 'gray root');
|
||||
assertEq(marks[1], 'gray', 'object reachable from gray root');
|
||||
assertEq(marks[2], 'black', 'global');
|
||||
assertEq(marks[3], 'dead', 'dead object should have been collected');
|
||||
|
||||
grayRoot().x = 7; // Overwrite the object
|
||||
gc();
|
||||
marks = getMarks();
|
||||
assertEq(marks[0], 'gray', 'gray root');
|
||||
assertEq(marks[1], 'dead', 'object no longer reachable from gray root');
|
||||
assertEq(marks[2], 'black', 'global');
|
||||
assertEq(marks[3], 'dead', 'dead object should have been collected');
|
||||
|
||||
var wm = new WeakMap();
|
||||
var global = newGlobal();
|
||||
|
||||
var wrapper1 = global.eval("Object.create(null)");
|
||||
wrapper1.name = "wrapper1";
|
||||
var value1 = Object.create(null);
|
||||
wm.set(wrapper1, value1);
|
||||
|
||||
var wrapper2 = global.eval("Object.create(null)");
|
||||
wrapper2.name = "wrapper2";
|
||||
var value2 = global.eval("Object.create(null)");
|
||||
wm.set(wrapper2, value2);
|
||||
|
||||
grayRoot().root1 = wrapper1;
|
||||
grayRoot().root2 = wrapper2;
|
||||
clearMarkObservers();
|
||||
addMarkObservers([wrapper1, value1, wrapper2, value2]);
|
||||
wrapper1 = wrapper2 = null;
|
||||
value1 = value2 = null;
|
||||
gc();
|
||||
marks = getMarks();
|
||||
assertEq(marks[0], 'gray', 'gray key 1');
|
||||
assertEq(marks[1], 'gray', 'black map, gray key => gray value');
|
||||
assertEq(marks[2], 'gray', 'gray key 2');
|
||||
assertEq(marks[3], 'gray', 'black map, gray key => gray value');
|
||||
|
||||
// Blacken one of the keys
|
||||
wrapper1 = grayRoot().root1;
|
||||
gc();
|
||||
marks = getMarks();
|
||||
assertEq(marks[0], 'black', 'black key 1');
|
||||
assertEq(marks[1], 'black', 'black map, black key => black value');
|
||||
assertEq(marks[2], 'gray', 'gray key 2');
|
||||
assertEq(marks[3], 'gray', 'black map, gray key => gray value');
|
||||
|
||||
// Test edges from map&delegate => key and map&key => value.
|
||||
//
|
||||
// In general, when a&b => x, then if both a and b are black, then x must be
|
||||
// black. If either is gray and the other is marked (gray or black), then x
|
||||
// must be gray (unless otherwise reachable from black.) If neither a nor b is
|
||||
// marked at all, then they will not keep x alive.
|
||||
|
||||
clearMarkObservers();
|
||||
|
||||
// Black map, gray delegate => gray key
|
||||
|
||||
// wm is in a variable, so is black.
|
||||
wm = new WeakMap();
|
||||
|
||||
let key = Object.create(null);
|
||||
// delegate unwraps key in the 'global' compartment
|
||||
global.grayRoot().delegate = key;
|
||||
|
||||
// Create a value and map to it from a gray key, then make the value a gray
|
||||
// root.
|
||||
let value = Object.create(null);
|
||||
wm.set(key, value);
|
||||
grayRoot().value = value;
|
||||
|
||||
// We are interested in the mark bits of the map, key, and value, as well as
|
||||
// the mark bits of the wrapped versions in the other compartment. Note that
|
||||
// the other-compartment key is the known as the key's delegate with respect to
|
||||
// the weakmap.
|
||||
global.addMarkObservers([wm, key, value]);
|
||||
addMarkObservers([wm, key, value]);
|
||||
|
||||
// Key is otherwise dead in main compartment.
|
||||
key = null;
|
||||
// Don't want value to be marked black.
|
||||
value = null;
|
||||
|
||||
gc(); // Update mark bits.
|
||||
let [
|
||||
other_map_mark, other_key_mark, other_value_mark,
|
||||
map_mark, key_mark, value_mark
|
||||
] = getMarks();
|
||||
assertEq(other_map_mark, 'dead', 'nothing points to wm in other compartment');
|
||||
assertEq(other_key_mark, 'gray', 'delegate should be gray');
|
||||
assertEq(other_value_mark, 'dead', 'nothing points to value wrapper in other compartment');
|
||||
assertEq(map_mark, 'black', 'map in var => black');
|
||||
assertEq(key_mark, 'gray', 'black map, gray delegate => gray key');
|
||||
assertEq(value_mark, 'gray', 'black map, gray delegate/key => gray value');
|
||||
|
||||
// Black map, black delegate => black key
|
||||
|
||||
// Blacken the delegate by pointing to it from the other global.
|
||||
global.delegate = global.grayRoot().delegate;
|
||||
|
||||
gc();
|
||||
[
|
||||
other_map_mark, other_key_mark, other_value_mark,
|
||||
map_mark, key_mark, value_mark
|
||||
] = getMarks();
|
||||
assertEq(other_map_mark, 'dead', 'nothing points to wm in other compartment');
|
||||
assertEq(other_key_mark, 'black', 'delegate held in global.delegate');
|
||||
assertEq(other_value_mark, 'dead', 'nothing points to value wrapper in other compartment');
|
||||
assertEq(map_mark, 'black', 'map in var => black');
|
||||
assertEq(key_mark, 'black', 'black map, black delegate => black key');
|
||||
assertEq(value_mark, 'black', 'black map, black key => black value');
|
||||
|
||||
// Gray map, black delegate => gray key. Unfortunately, there's no way to test
|
||||
// this, because a WeakMap key's delegate is its wrapper, and there is a strong
|
||||
// edge from wrappers to wrappees. The jsapi-test in testGCGrayMarking, inside
|
||||
// TestWeakMaps, *does* test this case.
|
||||
|
||||
grayRoot().map = wm;
|
||||
wm = null;
|
||||
|
||||
gc();
|
||||
[
|
||||
other_map_mark, other_key_mark, other_value_mark,
|
||||
map_mark, key_mark, value_mark
|
||||
] = getMarks();
|
||||
assertEq(other_map_mark, 'dead', 'nothing points to wm in other compartment');
|
||||
assertEq(other_key_mark, 'black', 'delegate held in global.delegate');
|
||||
assertEq(other_value_mark, 'dead', 'nothing points to value wrapper in other compartment');
|
||||
assertEq(map_mark, 'gray', 'map is a gray root');
|
||||
assertEq(key_mark, 'black', 'black delegate marks its key black');
|
||||
assertEq(value_mark, 'gray', 'gray map, black key => gray value');
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
@ -300,18 +300,22 @@ class JitTest:
|
||||
quotechar = '"'
|
||||
else:
|
||||
quotechar = "'"
|
||||
expr = "const platform={}; const libdir={}; const scriptdir={}".format(
|
||||
js_quote(quotechar, sys.platform),
|
||||
js_quote(quotechar, libdir),
|
||||
js_quote(quotechar, scriptdir_var))
|
||||
|
||||
# Don't merge the expressions: We want separate -e arguments to avoid
|
||||
# semicolons in the command line, bug 1351607.
|
||||
exprs = ["const platform={}".format(js_quote(quotechar, sys.platform)),
|
||||
"const libdir={}".format(js_quote(quotechar, libdir)),
|
||||
"const scriptdir={}".format(js_quote(quotechar, scriptdir_var))];
|
||||
|
||||
if self.need_for_each:
|
||||
expr += "; enableForEach()"
|
||||
exprs += ["enableForEach()"]
|
||||
|
||||
# We may have specified '-a' or '-d' twice: once via --jitflags, once
|
||||
# via the "|jit-test|" line. Remove dups because they are toggles.
|
||||
cmd = prefix + ['--js-cache', JitTest.CacheDir]
|
||||
cmd += list(set(self.jitflags)) + ['-e', expr]
|
||||
cmd += list(set(self.jitflags))
|
||||
for expr in exprs:
|
||||
cmd += ['-e', expr]
|
||||
for inc in self.other_includes:
|
||||
cmd += ['-f', libdir + inc]
|
||||
if self.is_module:
|
||||
|
@ -8,8 +8,7 @@
|
||||
#include <new.h>
|
||||
#include <process.h>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "jswin.h"
|
||||
#include "threading/Thread.h"
|
||||
|
||||
class js::Thread::Id::PlatformData
|
||||
|
@ -106,6 +106,8 @@ JS::detail::InitWithFailureDiagnostic(bool isDebugBuild)
|
||||
RETURN_IF_FAIL(js::wasm::InitInstanceStaticData());
|
||||
|
||||
js::gc::InitMemorySubsystem(); // Ensure gc::SystemPageSize() works.
|
||||
RETURN_IF_FAIL(js::gc::InitializeStaticData());
|
||||
|
||||
RETURN_IF_FAIL(js::jit::InitProcessExecutableMemory());
|
||||
|
||||
MOZ_ALWAYS_TRUE(js::MemoryProtectionExceptionHandler::install());
|
||||
|
@ -15,8 +15,6 @@
|
||||
#include <mach/mach.h>
|
||||
#elif defined(XP_UNIX)
|
||||
#include <sys/resource.h>
|
||||
#elif defined(XP_WIN)
|
||||
#include <windows.h>
|
||||
#endif // defined(XP_DARWIN) || defined(XP_UNIX) || defined(XP_WIN)
|
||||
|
||||
#include <locale.h>
|
||||
|
@ -12,10 +12,10 @@
|
||||
|
||||
#if defined(XP_WIN)
|
||||
#include <processthreadsapi.h>
|
||||
#include <windows.h>
|
||||
#endif // defined(XP_WIN)
|
||||
|
||||
#include "jscompartment.h"
|
||||
#include "jswin.h"
|
||||
|
||||
#include "gc/Zone.h"
|
||||
#include "vm/Runtime.h"
|
||||
|
@ -244,7 +244,7 @@
|
||||
|
||||
/* OS communication functions */
|
||||
#if ITT_PLATFORM==ITT_PLATFORM_WIN
|
||||
#include <windows.h>
|
||||
#include "jswin.h"
|
||||
typedef HMODULE lib_t;
|
||||
typedef DWORD TIDT;
|
||||
typedef CRITICAL_SECTION mutex_t;
|
||||
|
@ -7384,10 +7384,6 @@ nsLayoutUtils::SurfaceFromElement(HTMLVideoElement* aElement,
|
||||
{
|
||||
SurfaceFromElementResult result;
|
||||
|
||||
NS_WARNING_ASSERTION(
|
||||
(aSurfaceFlags & SFE_PREFER_NO_PREMULTIPLY_ALPHA) == 0,
|
||||
"We can't support non-premultiplied alpha for video!");
|
||||
|
||||
if (aElement->ContainsRestrictedContent()) {
|
||||
return result;
|
||||
}
|
||||
|
@ -51,7 +51,10 @@ class SVGContextPaint : public RefCounted<SVGContextPaint>
|
||||
protected:
|
||||
typedef mozilla::gfx::DrawTarget DrawTarget;
|
||||
|
||||
SVGContextPaint() {}
|
||||
SVGContextPaint()
|
||||
: mDashOffset(0.0f)
|
||||
, mStrokeWidth(0.0f)
|
||||
{}
|
||||
|
||||
public:
|
||||
typedef image::DrawResult DrawResult;
|
||||
|
@ -40,7 +40,9 @@ WyciwygChannelParent::ActorDestroy(ActorDestroyReason why)
|
||||
mIPCClosed = true;
|
||||
|
||||
// We need to force the cycle to break here
|
||||
mChannel->SetNotificationCallbacks(nullptr);
|
||||
if (mChannel) {
|
||||
mChannel->SetNotificationCallbacks(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -161,9 +161,8 @@ PathBuildingStep::Check(Input potentialIssuerDER,
|
||||
// Loop prevention, done as recommended by RFC4158 Section 5.2
|
||||
// TODO: this doesn't account for subjectAltNames!
|
||||
// TODO(perf): This probably can and should be optimized in some way.
|
||||
bool loopDetected = false;
|
||||
for (const BackCert* prev = potentialIssuer.childCert;
|
||||
!loopDetected && prev != nullptr; prev = prev->childCert) {
|
||||
for (const BackCert* prev = potentialIssuer.childCert; prev;
|
||||
prev = prev->childCert) {
|
||||
if (InputsAreEqual(potentialIssuer.GetSubjectPublicKeyInfo(),
|
||||
prev->GetSubjectPublicKeyInfo()) &&
|
||||
InputsAreEqual(potentialIssuer.GetSubject(), prev->GetSubject())) {
|
||||
|
@ -4,7 +4,7 @@
|
||||
"use strict"
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "jwcrypto",
|
||||
"resource://gre/modules/services-crypto/jwcrypto.jsm");
|
||||
"resource://services-crypto/jwcrypto.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this,
|
||||
"CryptoService",
|
||||
|
@ -28,7 +28,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "FxAccountsConfig",
|
||||
"resource://gre/modules/FxAccountsConfig.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "jwcrypto",
|
||||
"resource://gre/modules/services-crypto/jwcrypto.jsm");
|
||||
"resource://services-crypto/jwcrypto.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "FxAccountsOAuthGrantClient",
|
||||
"resource://gre/modules/FxAccountsOAuthGrantClient.jsm");
|
||||
|
@ -80,9 +80,8 @@ class GeckoProfile(object):
|
||||
# itself, not by Talos JS code.
|
||||
env.update({
|
||||
'MOZ_PROFILER_STARTUP': '1',
|
||||
'MOZ_PROFILER_INTERVAL': str(self.option('interval')),
|
||||
'MOZ_PROFILER_ENTRIES': str(self.option('entries')),
|
||||
"MOZ_PROFILER_THREADS": str(self.option('threads'))
|
||||
'MOZ_PROFILER_STARTUP_INTERVAL': str(self.option('interval')),
|
||||
'MOZ_PROFILER_STARTUP_ENTRIES': str(self.option('entries'))
|
||||
})
|
||||
|
||||
def _save_gecko_profile(self, cycle, symbolicator, missing_symbols_zip,
|
||||
|
@ -1,6 +0,0 @@
|
||||
[serviceworker-message-event-historical.https.html]
|
||||
type: testharness
|
||||
expected: TIMEOUT
|
||||
[Test MessageEvent supplants ServiceWorkerMessageEvent.]
|
||||
expected: TIMEOUT
|
||||
|
@ -1,6 +0,0 @@
|
||||
[skip-waiting-installed.https.html]
|
||||
type: testharness
|
||||
expected: TIMEOUT
|
||||
[Test skipWaiting when a installed worker is waiting]
|
||||
expected: TIMEOUT
|
||||
|
@ -1,4 +0,0 @@
|
||||
[skip-waiting-using-registration.https.html]
|
||||
type: testharness
|
||||
[Test skipWaiting while a client is using the registration]
|
||||
expected: FAIL
|
@ -1,3 +0,0 @@
|
||||
[skip-waiting-without-using-registration.https.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1347497
|
@ -1,3 +0,0 @@
|
||||
[skip-waiting.https.html]
|
||||
type: testharness
|
||||
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1347497
|
@ -7,7 +7,7 @@
|
||||
var t = async_test('Request: end-to-end');
|
||||
t.step(function() {
|
||||
var url = 'resources/request-end-to-end-worker.js';
|
||||
var scope = 'resources/blank.html';
|
||||
var scope = 'resources/blank.html?request-end-to-end';
|
||||
var frames = [];
|
||||
|
||||
service_worker_unregister_and_register(t, url, scope)
|
||||
|
@ -7,7 +7,7 @@
|
||||
<script>
|
||||
|
||||
promise_test(function(t) {
|
||||
var scope = 'resources/blank.html';
|
||||
var scope = 'resources/blank.html?skip-waiting-installed';
|
||||
var url1 = 'resources/empty.js';
|
||||
var url2 = 'resources/skip-waiting-installed-worker.js';
|
||||
var frame, frame_sw, service_worker, registration, onmessage, oncontrollerchanged;
|
||||
|
@ -7,7 +7,7 @@
|
||||
<script>
|
||||
|
||||
promise_test(function(t) {
|
||||
var scope = 'resources/blank.html';
|
||||
var scope = 'resources/blank.html?skip-waiting-using-registration';
|
||||
var url1 = 'resources/empty.js';
|
||||
var url2 = 'resources/skip-waiting-worker.js';
|
||||
var frame, frame_sw, sw_registration, oncontrollerchanged;
|
||||
|
@ -7,7 +7,7 @@
|
||||
<script>
|
||||
|
||||
promise_test(function(t) {
|
||||
var scope = 'resources/blank.html';
|
||||
var scope = 'resources/blank.html?skip-waiting-without-using-registration';
|
||||
var url = 'resources/skip-waiting-worker.js';
|
||||
var frame, frame_sw, sw_registration;
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
<script>
|
||||
|
||||
promise_test(function(t) {
|
||||
var scope = 'resources/blank.html';
|
||||
var scope = 'resources/blank.html?skip-waiting';
|
||||
var url1 = 'resources/empty.js';
|
||||
var url2 = 'resources/empty-worker.js';
|
||||
var url3 = 'resources/skip-waiting-worker.js';
|
||||
|
@ -1483,6 +1483,7 @@
|
||||
"description": "Did the browser start after a successful shutdown"
|
||||
},
|
||||
"IMAGE_DECODE_LATENCY_US": {
|
||||
"alert_emails": ["gfx-telemetry-alerts@mozilla.com"],
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"low": 50,
|
||||
@ -1491,6 +1492,7 @@
|
||||
"description": "Time spent decoding an image chunk (us)"
|
||||
},
|
||||
"IMAGE_DECODE_TIME": {
|
||||
"alert_emails": ["gfx-telemetry-alerts@mozilla.com"],
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"low": 50,
|
||||
@ -1509,6 +1511,7 @@
|
||||
"description": "Time spent decoding an animated image (us)"
|
||||
},
|
||||
"IMAGE_DECODE_ON_DRAW_LATENCY": {
|
||||
"alert_emails": ["gfx-telemetry-alerts@mozilla.com"],
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"low": 50,
|
||||
@ -1527,6 +1530,7 @@
|
||||
"description": "Time from starting a decode of an animated image to it showing up on the screen (us)"
|
||||
},
|
||||
"IMAGE_DECODE_CHUNKS": {
|
||||
"alert_emails": ["gfx-telemetry-alerts@mozilla.com"],
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"high": 500,
|
||||
@ -1534,6 +1538,7 @@
|
||||
"description": "Number of chunks per decode attempt"
|
||||
},
|
||||
"IMAGE_DECODE_COUNT": {
|
||||
"alert_emails": ["gfx-telemetry-alerts@mozilla.com"],
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"high": 500,
|
||||
@ -1550,6 +1555,7 @@
|
||||
"description": "Decode count of animated images"
|
||||
},
|
||||
"IMAGE_DECODE_SPEED_JPEG": {
|
||||
"alert_emails": ["gfx-telemetry-alerts@mozilla.com"],
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"low": 500,
|
||||
@ -1558,6 +1564,7 @@
|
||||
"description": "JPEG image decode speed (Kbytes/sec)"
|
||||
},
|
||||
"IMAGE_DECODE_SPEED_GIF": {
|
||||
"alert_emails": ["gfx-telemetry-alerts@mozilla.com"],
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"low": 500,
|
||||
@ -1566,7 +1573,7 @@
|
||||
"description": "GIF image decode speed (Kbytes/sec)"
|
||||
},
|
||||
"IMAGE_LOAD_TRIGGER_LATENCY_MS": {
|
||||
"alert_emails": ["ehsan@mozilla.com"],
|
||||
"alert_emails": ["ehsan@mozilla.com", "gfx-telemetry-alerts@mozilla.com"],
|
||||
"expires_in_version": "60",
|
||||
"bug_numbers": [1347376],
|
||||
"kind": "exponential",
|
||||
@ -1576,6 +1583,7 @@
|
||||
"description": "Measures the number of milliseconds we spend inside imgLoader::LoadImage(). Note: only calls that take more than 500 microseconds and happen in the content process are included in this probe."
|
||||
},
|
||||
"IMAGE_DECODE_SPEED_PNG": {
|
||||
"alert_emails": ["gfx-telemetry-alerts@mozilla.com"],
|
||||
"expires_in_version": "never",
|
||||
"kind": "exponential",
|
||||
"low": 500,
|
||||
|
@ -356,14 +356,6 @@
|
||||
"HTTP_SUB_TCP_CONNECTION",
|
||||
"HTTP_TRANSACTION_USE_ALTSVC",
|
||||
"HTTP_TRANSACTION_USE_ALTSVC_OE",
|
||||
"IMAGE_DECODE_CHUNKS",
|
||||
"IMAGE_DECODE_COUNT",
|
||||
"IMAGE_DECODE_LATENCY_US",
|
||||
"IMAGE_DECODE_ON_DRAW_LATENCY",
|
||||
"IMAGE_DECODE_SPEED_GIF",
|
||||
"IMAGE_DECODE_SPEED_JPEG",
|
||||
"IMAGE_DECODE_SPEED_PNG",
|
||||
"IMAGE_DECODE_TIME",
|
||||
"INNERWINDOWS_WITH_MUTATION_LISTENERS",
|
||||
"IPC_SAME_PROCESS_MESSAGE_COPY_OOM_KB",
|
||||
"IPV4_AND_IPV6_ADDRESS_CONNECTIVITY",
|
||||
|
@ -477,42 +477,6 @@ readCSVArray(char* aCsvList, const char** aBuffer)
|
||||
return count;
|
||||
}
|
||||
|
||||
// Support some of the env variables reported in ReadProfilerEnvVars, plus some
|
||||
// extra stuff.
|
||||
static void
|
||||
ReadProfilerVars(const char* aFileName,
|
||||
const char** aFeatures, uint32_t* aFeatureCount,
|
||||
const char** aThreadNames, uint32_t* aThreadCount)
|
||||
{
|
||||
FILE* file = fopen(aFileName, "r");
|
||||
const int bufferSize = 1024;
|
||||
char line[bufferSize];
|
||||
char* feature;
|
||||
char* value;
|
||||
char* savePtr;
|
||||
|
||||
if (file) {
|
||||
PS::AutoLock lock(gPSMutex);
|
||||
|
||||
while (fgets(line, bufferSize, file) != nullptr) {
|
||||
feature = strtok_r(line, "=", &savePtr);
|
||||
value = strtok_r(nullptr, "", &savePtr);
|
||||
|
||||
if (strncmp(feature, "MOZ_PROFILER_INTERVAL", bufferSize) == 0) {
|
||||
set_profiler_interval(lock, value);
|
||||
} else if (strncmp(feature, "MOZ_PROFILER_ENTRIES", bufferSize) == 0) {
|
||||
set_profiler_entries(lock, value);
|
||||
} else if (strncmp(feature, "MOZ_PROFILER_FEATURES", bufferSize) == 0) {
|
||||
*aFeatureCount = readCSVArray(value, aFeatures);
|
||||
} else if (strncmp(feature, "threads", bufferSize) == 0) {
|
||||
*aThreadCount = readCSVArray(value, aThreadNames);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
DoStartTask()
|
||||
{
|
||||
@ -527,11 +491,39 @@ DoStartTask()
|
||||
const char* features[10];
|
||||
const char* profilerConfigFile = "/data/local/tmp/profiler.options";
|
||||
|
||||
ReadProfilerVars(profilerConfigFile, features, &featureCount, threadNames, &threadCount);
|
||||
// Support some of the usual env variables, plus some extra stuff.
|
||||
FILE* file = fopen(profilerConfigFile, "r");
|
||||
int entries = PROFILE_DEFAULT_ENTRIES;
|
||||
int interval = PROFILE_DEFAULT_INTERVAL;
|
||||
|
||||
if (file) {
|
||||
const int bufferSize = 1024;
|
||||
char line[bufferSize];
|
||||
while (fgets(line, bufferSize, file) != nullptr) {
|
||||
char* savePtr;
|
||||
char* feature = strtok_r(line, "=", &savePtr);
|
||||
char* value = strtok_r(nullptr, "", &savePtr);
|
||||
|
||||
if (strncmp(feature, "MOZ_PROFILER_STARTUP_ENTRIES", bufferSize) == 0) {
|
||||
GetEntries(value, &entries);
|
||||
} else if (strncmp(feature, "MOZ_PROFILER_STARTUP_INTERVAL",
|
||||
bufferSize) == 0) {
|
||||
GetInterval(value, &interval);
|
||||
} else if (strncmp(feature, "MOZ_PROFILER_STARTUP_FEATURES",
|
||||
bufferSize) == 0) {
|
||||
featureCount = readCSVArray(value, features);
|
||||
} else if (strncmp(feature, "threads", bufferSize) == 0) {
|
||||
threadCount = readCSVArray(value, threadNames);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(featureCount < 10);
|
||||
MOZ_ASSERT(threadCount < 10);
|
||||
|
||||
profiler_start(PROFILE_DEFAULT_ENTRIES, /* interval */ 1,
|
||||
profiler_start(entries, interval,
|
||||
features, featureCount, threadNames, threadCount);
|
||||
|
||||
freeArray(threadNames, threadCount);
|
||||
|
@ -138,9 +138,7 @@ public:
|
||||
typedef std::vector<ThreadInfo*> ThreadVector;
|
||||
|
||||
ProfilerState()
|
||||
: mEnvVarEntries(0)
|
||||
, mEntries(0)
|
||||
, mEnvVarInterval(0)
|
||||
: mEntries(0)
|
||||
, mInterval(0)
|
||||
, mFeatureDisplayListDump(false)
|
||||
, mFeatureGPU(false)
|
||||
@ -175,10 +173,8 @@ public:
|
||||
|
||||
GET_AND_SET(TimeStamp, StartTime)
|
||||
|
||||
GET_AND_SET(int, EnvVarEntries)
|
||||
GET_AND_SET(int, Entries)
|
||||
|
||||
GET_AND_SET(int, EnvVarInterval)
|
||||
GET_AND_SET(double, Interval)
|
||||
|
||||
Vector<std::string>& Features(LockRef) { return mFeatures; }
|
||||
@ -239,16 +235,11 @@ private:
|
||||
// When profiler_init() or profiler_start() was most recently called.
|
||||
mozilla::TimeStamp mStartTime;
|
||||
|
||||
// The number of entries in mBuffer. mEnvVarEntries comes from an environment
|
||||
// variable and can override the value passed in to profiler_start(). Zeroed
|
||||
// when the profiler is inactive.
|
||||
int mEnvVarEntries;
|
||||
// The number of entries in mBuffer. Zeroed when the profiler is inactive.
|
||||
int mEntries;
|
||||
|
||||
// The interval between samples, measured in milliseconds. mEnvVarInterval
|
||||
// comes from an environment variable and can override the value passed in to
|
||||
// profiler_start(). Zeroed when the profiler is inactive.
|
||||
int mEnvVarInterval;
|
||||
// The interval between samples, measured in milliseconds. Zeroed when the
|
||||
// profiler is inactive.
|
||||
double mInterval;
|
||||
|
||||
// The profile features that are enabled. Cleared when the profiler is
|
||||
@ -1430,40 +1421,36 @@ void ProfilerMarker::StreamJSON(SpliceableJSONWriter& aWriter,
|
||||
aWriter.EndArray();
|
||||
}
|
||||
|
||||
// Only fills in aOut if aStr contains a valid value.
|
||||
static bool
|
||||
set_profiler_interval(PS::LockRef aLock, const char* aInterval)
|
||||
GetEntries(const char* aStr, int* aOut)
|
||||
{
|
||||
if (aInterval) {
|
||||
errno = 0;
|
||||
long int n = strtol(aInterval, nullptr, 10);
|
||||
if (errno == 0 && 1 <= n && n <= 1000) {
|
||||
gPS->SetEnvVarInterval(aLock, n);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
MOZ_ASSERT(aStr);
|
||||
errno = 0;
|
||||
int entries = strtol(aStr, nullptr, 10);
|
||||
if (errno == 0 && entries > 0) {
|
||||
*aOut = entries;
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only fills in aOut if aStr contains a valid value.
|
||||
static bool
|
||||
set_profiler_entries(PS::LockRef aLock, const char* aEntries)
|
||||
GetInterval(const char* aStr, int* aOut)
|
||||
{
|
||||
if (aEntries) {
|
||||
errno = 0;
|
||||
long int n = strtol(aEntries, nullptr, 10);
|
||||
if (errno == 0 && n > 0) {
|
||||
gPS->SetEnvVarEntries(aLock, n);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
MOZ_ASSERT(aStr);
|
||||
errno = 0;
|
||||
int interval = strtol(aStr, nullptr, 10);
|
||||
if (errno == 0 && 1 <= interval && interval <= 1000) {
|
||||
*aOut = interval;
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
profiler_usage(int aExitCode)
|
||||
PrintUsageThenExit(int aExitCode)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(NS_IsMainThread());
|
||||
MOZ_RELEASE_ASSERT(gPS);
|
||||
@ -1475,14 +1462,6 @@ profiler_usage(int aExitCode)
|
||||
" MOZ_PROFILER_HELP\n"
|
||||
" If set to any value, prints this message.\n"
|
||||
"\n"
|
||||
" MOZ_PROFILER_ENTRIES=<1..>\n"
|
||||
" The number of entries in the profiler's circular buffer.\n"
|
||||
" If unset, the platform default is used.\n"
|
||||
"\n"
|
||||
" MOZ_PROFILER_INTERVAL=<1..1000>\n"
|
||||
" The interval between samples, measured in milliseconds.\n"
|
||||
" If unset, platform default is used.\n"
|
||||
"\n"
|
||||
" MOZ_LOG\n"
|
||||
" Enables logging. The levels of logging available are\n"
|
||||
" 'prof:3' (least verbose), 'prof:4', 'prof:5' (most verbose).\n"
|
||||
@ -1491,6 +1470,16 @@ profiler_usage(int aExitCode)
|
||||
" If set to any value, starts the profiler immediately on start-up.\n"
|
||||
" Useful if you want profile code that runs very early.\n"
|
||||
"\n"
|
||||
" MOZ_PROFILER_STARTUP_ENTRIES=<1..>\n"
|
||||
" If MOZ_PROFILER_STARTUP is set, specifies the number of entries in\n"
|
||||
" the profiler's circular buffer when the profiler is first started.\n"
|
||||
" If unset, the platform default is used.\n"
|
||||
"\n"
|
||||
" MOZ_PROFILER_STARTUP_INTERVAL=<1..1000>\n"
|
||||
" If MOZ_PROFILER_STARTUP is set, specifies the sample interval,\n"
|
||||
" measured in milliseconds, when the profiler is first started.\n"
|
||||
" If unset, the platform default is used.\n"
|
||||
"\n"
|
||||
" MOZ_PROFILER_SHUTDOWN\n"
|
||||
" If set, the profiler saves a profile to the named file on shutdown.\n"
|
||||
"\n"
|
||||
@ -1509,30 +1498,6 @@ profiler_usage(int aExitCode)
|
||||
exit(aExitCode);
|
||||
}
|
||||
|
||||
// Read env vars at startup, so as to set:
|
||||
// gPS->mEnvVarEntries, gPS->mEnvVarInterval
|
||||
static void
|
||||
ReadProfilerEnvVars(PS::LockRef aLock)
|
||||
{
|
||||
const char* help = getenv("MOZ_PROFILER_HELP");
|
||||
const char* entries = getenv("MOZ_PROFILER_ENTRIES");
|
||||
const char* interval = getenv("MOZ_PROFILER_INTERVAL");
|
||||
|
||||
if (help) {
|
||||
profiler_usage(0); // terminates execution
|
||||
}
|
||||
|
||||
if (!set_profiler_entries(aLock, entries) ||
|
||||
!set_profiler_interval(aLock, interval)) {
|
||||
profiler_usage(1); // terminates execution
|
||||
}
|
||||
|
||||
LOG("entries = %d (zero means \"platform default\")",
|
||||
gPS->EnvVarEntries(aLock));
|
||||
LOG("interval = %d ms (zero means \"platform default\")",
|
||||
gPS->EnvVarInterval(aLock));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// BEGIN SamplerThread
|
||||
|
||||
@ -1947,6 +1912,10 @@ profiler_init(void* aStackTop)
|
||||
|
||||
const char* threadFilters[] = { "GeckoMain", "Compositor" };
|
||||
|
||||
if (getenv("MOZ_PROFILER_HELP")) {
|
||||
PrintUsageThenExit(0); // terminates execution
|
||||
}
|
||||
|
||||
{
|
||||
PS::AutoLock lock(gPSMutex);
|
||||
|
||||
@ -1957,9 +1926,6 @@ profiler_init(void* aStackTop)
|
||||
bool ignore;
|
||||
gPS->SetStartTime(lock, mozilla::TimeStamp::ProcessCreation(ignore));
|
||||
|
||||
// Read settings from environment variables.
|
||||
ReadProfilerEnvVars(lock);
|
||||
|
||||
locked_register_thread(lock, kMainThreadName, aStackTop);
|
||||
|
||||
// Platform-specific initialization.
|
||||
@ -1981,18 +1947,31 @@ profiler_init(void* aStackTop)
|
||||
// startup, even if no profiling is actually to be done. So, instead, it is
|
||||
// created on demand at the first call to PlatformStart().
|
||||
|
||||
// We can't open pref so we use an environment variable to know if we
|
||||
// should trigger the profiler on startup.
|
||||
// NOTE: Default
|
||||
const char *val = getenv("MOZ_PROFILER_STARTUP");
|
||||
if (!val || !*val) {
|
||||
if (!getenv("MOZ_PROFILER_STARTUP")) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG("MOZ_PROFILER_STARTUP is set");
|
||||
LOG("- MOZ_PROFILER_STARTUP is set");
|
||||
|
||||
locked_profiler_start(lock, PROFILE_DEFAULT_ENTRIES,
|
||||
PROFILE_DEFAULT_INTERVAL,
|
||||
int entries = PROFILE_DEFAULT_ENTRIES;
|
||||
const char* startupEntries = getenv("MOZ_PROFILER_STARTUP_ENTRIES");
|
||||
if (startupEntries) {
|
||||
if (!GetEntries(startupEntries, &entries)) {
|
||||
PrintUsageThenExit(1);
|
||||
}
|
||||
LOG("- MOZ_PROFILER_STARTUP_ENTRIES = %d", entries);
|
||||
}
|
||||
|
||||
int interval = PROFILE_DEFAULT_INTERVAL;
|
||||
const char* startupInterval = getenv("MOZ_PROFILER_STARTUP_INTERVAL");
|
||||
if (startupInterval) {
|
||||
if (!GetInterval(startupInterval, &interval)) {
|
||||
PrintUsageThenExit(1);
|
||||
}
|
||||
LOG("- MOZ_PROFILER_STARTUP_INTERVAL = %d", interval);
|
||||
}
|
||||
|
||||
locked_profiler_start(lock, entries, interval,
|
||||
features, MOZ_ARRAY_LENGTH(features),
|
||||
threadFilters, MOZ_ARRAY_LENGTH(threadFilters));
|
||||
}
|
||||
@ -2383,25 +2362,12 @@ locked_profiler_start(PS::LockRef aLock, int aEntries, double aInterval,
|
||||
bool ignore;
|
||||
gPS->SetStartTime(aLock, mozilla::TimeStamp::ProcessCreation(ignore));
|
||||
|
||||
// Start with the default value. Then override with the passed-in value, if
|
||||
// reasonable. Then override with the env var value, if reasonable.
|
||||
int entries = PROFILE_DEFAULT_ENTRIES;
|
||||
if (aEntries > 0) {
|
||||
entries = aEntries;
|
||||
}
|
||||
if (gPS->EnvVarEntries(aLock) > 0) {
|
||||
entries = gPS->EnvVarEntries(aLock);
|
||||
}
|
||||
// Fall back to the default value if the passed-in value is unreasonable.
|
||||
int entries = aEntries > 0 ? aEntries : PROFILE_DEFAULT_ENTRIES;
|
||||
gPS->SetEntries(aLock, entries);
|
||||
|
||||
// Ditto.
|
||||
double interval = PROFILE_DEFAULT_INTERVAL;
|
||||
if (aInterval > 0) {
|
||||
interval = aInterval;
|
||||
}
|
||||
if (gPS->EnvVarInterval(aLock) > 0) {
|
||||
interval = gPS->EnvVarInterval(aLock);
|
||||
}
|
||||
double interval = aInterval > 0 ? aInterval : PROFILE_DEFAULT_INTERVAL;
|
||||
gPS->SetInterval(aLock, interval);
|
||||
|
||||
// Deep copy aFeatures. Must precede the MaybeSetProfile() call below.
|
||||
|
Loading…
x
Reference in New Issue
Block a user