Merge mozilla-central to autoland on a CLOSED TREE

--HG--
extra : amend_source : 5026c19958cdc458c0f0887582fd00b8038fe93e
This commit is contained in:
Carsten "Tomcat" Book 2017-03-30 16:48:10 +02:00
commit 74e36e1adb
61 changed files with 1200 additions and 511 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -377,7 +377,7 @@ const walkerSpec = generateActorSpec({
},
getOffsetParent: {
request: {
node: Arg(0, "domnode")
node: Arg(0, "nullable:domnode")
},
response: {
node: RetVal("nullable:domnode")

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -41,7 +41,7 @@ public:
void Init();
void EnsureGPUReady();
bool EnsureGPUReady();
// gfxVarReceiver overrides.
void OnVarChanged(const GfxVarUpdate& aVar) override;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -9,7 +9,7 @@
// in the same process.
#ifdef WIN32
#include "windows.h"
#include "jswin.h"
#else
#define __cdecl
#include <stdarg.h>

View File

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

View File

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

View File

@ -33,9 +33,6 @@
#include "vm/TypedArrayObject.h"
#include "vm/UnboxedObject.h"
// Undo windows.h damage on Win64
#undef MemoryBarrier
namespace js {
class StringObject;

View File

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

View File

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

View File

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

View File

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

View File

@ -18,6 +18,7 @@
# undef min
# undef max
# undef GetProp
# undef MemoryBarrier
# undef SetProp
# undef CONST
# undef STRICT

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +0,0 @@
[serviceworker-message-event-historical.https.html]
type: testharness
expected: TIMEOUT
[Test MessageEvent supplants ServiceWorkerMessageEvent.]
expected: TIMEOUT

View File

@ -1,6 +0,0 @@
[skip-waiting-installed.https.html]
type: testharness
expected: TIMEOUT
[Test skipWaiting when a installed worker is waiting]
expected: TIMEOUT

View File

@ -1,4 +0,0 @@
[skip-waiting-using-registration.https.html]
type: testharness
[Test skipWaiting while a client is using the registration]
expected: FAIL

View File

@ -1,3 +0,0 @@
[skip-waiting-without-using-registration.https.html]
type: testharness
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1347497

View File

@ -1,3 +0,0 @@
[skip-waiting.https.html]
type: testharness
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1347497

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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