mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-22 17:55:50 +00:00
Merge mozilla-central to autoland a=merge on a CLOSED TREE
This commit is contained in:
commit
75435dc3b2
@ -75,5 +75,11 @@ add_task(async function() {
|
||||
// should be switched back
|
||||
ok(openedTab.selected, "Ta-dah, the other tab should now be selected again!");
|
||||
|
||||
// In e10s, with the conformant promise scheduling, we have to wait for next tick
|
||||
// to ensure that the prompt is open before removing the opened tab, because the
|
||||
// promise callback of 'openedTabSelectedPromise' could be done at the middle of
|
||||
// RemotePrompt.openTabPrompt() while 'DOMModalDialogClosed' event is fired.
|
||||
await TestUtils.waitForTick();
|
||||
|
||||
await BrowserTestUtils.removeTab(openedTab);
|
||||
});
|
||||
|
@ -4,6 +4,7 @@ MOZ_AUTOMATION_L10N_CHECK=0
|
||||
|
||||
. "$topsrcdir/build/mozconfig.win-common"
|
||||
. "$topsrcdir/browser/config/mozconfigs/common"
|
||||
. "$topsrcdir/browser/config/mozconfigs/win64/common-win64"
|
||||
|
||||
ac_add_options --enable-optimize
|
||||
ac_add_options --enable-debug
|
||||
@ -11,7 +12,7 @@ ac_add_options --enable-debug
|
||||
ac_add_options --enable-clang-plugin
|
||||
ac_add_options --enable-mozsearch-plugin
|
||||
|
||||
. $topsrcdir/build/win32/mozconfig.vs-latest
|
||||
. $topsrcdir/build/win64/mozconfig.vs-latest
|
||||
|
||||
. "$topsrcdir/build/mozconfig.common.override"
|
||||
. "$topsrcdir/build/mozconfig.clang-cl"
|
@ -279,6 +279,22 @@ this.TestRunner = {
|
||||
return {bounds, rects};
|
||||
},
|
||||
|
||||
_do_skip(reason, combo, config, func) {
|
||||
const { todo } = reason;
|
||||
if (todo) {
|
||||
this.mochitestScope.todo(
|
||||
false,
|
||||
`Skipped configuration ` +
|
||||
`[ ${combo.map((e) => e.name).join(", ")} ] for failure in ` +
|
||||
`${config.name}.${func}: ${todo}`);
|
||||
} else {
|
||||
this.mochitestScope.info(
|
||||
`\tSkipped configuration ` +
|
||||
`[ ${combo.map((e) => e.name).join(", ")} ] ` +
|
||||
`for "${reason}" in ${config.name}.${func}`);
|
||||
}
|
||||
},
|
||||
|
||||
async _performCombo(combo) {
|
||||
let paddedComboIndex = padLeft(this.currentComboIndex + 1, String(this.combos.length).length);
|
||||
this.mochitestScope.info(
|
||||
@ -298,9 +314,9 @@ this.TestRunner = {
|
||||
this.mochitestScope.info("called " + config.name);
|
||||
// Add a default timeout of 500ms to avoid conflicts when configurations
|
||||
// try to apply at the same time. e.g WindowSize and TabsInTitlebar
|
||||
return Promise.race([applyPromise, timeoutPromise]).then(() => {
|
||||
return Promise.race([applyPromise, timeoutPromise]).then(result => {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(resolve, 500);
|
||||
setTimeout(() => resolve(result), 500);
|
||||
});
|
||||
});
|
||||
};
|
||||
@ -311,7 +327,11 @@ this.TestRunner = {
|
||||
let config = combo[i];
|
||||
if (!this._lastCombo || config !== this._lastCombo[i]) {
|
||||
this.mochitestScope.info(`promising ${config.name}`);
|
||||
await changeConfig(config);
|
||||
const reason = await changeConfig(config);
|
||||
if (reason) {
|
||||
this._do_skip(reason, combo, config, "applyConfig");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -330,18 +350,22 @@ this.TestRunner = {
|
||||
// have invalidated it.
|
||||
if (config.verifyConfig) {
|
||||
this.mochitestScope.info(`checking if the combo is valid with ${config.name}`);
|
||||
await config.verifyConfig();
|
||||
const reason = await config.verifyConfig();
|
||||
if (reason) {
|
||||
this._do_skip(reason, combo, config, "applyConfig");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (ex) {
|
||||
this.mochitestScope.info(`\tskipped configuration [ ${combo.map((e) => e.name).join(", ")} ]`);
|
||||
this.mochitestScope.info(`\treason: ${ex.toString()}`);
|
||||
// Don't set lastCombo here so that we properly know which configurations
|
||||
// need to be applied since the last screenshot
|
||||
|
||||
// Return so we don't take a screenshot.
|
||||
this.mochitestScope.ok(false, `Unexpected exception in [ ${combo.map(({ name }) => name).join(", ")} ]: ${ex.toString()}`);
|
||||
this.mochitestScope.info(`\t${ex}`);
|
||||
if (ex.stack) {
|
||||
this.mochitestScope.info(`\t${ex.stack}`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
this.mochitestScope.info(`Configured UI for [ ${combo.map(({ name }) => name).join(", ")} ] successfully`);
|
||||
|
||||
// Collect selectors from combo configs for cropping region
|
||||
let windowType;
|
||||
|
@ -35,17 +35,17 @@ this.AppMenu = {
|
||||
|
||||
appMenuHistorySubview: {
|
||||
selectors: ["#appMenu-popup"],
|
||||
applyConfig() {
|
||||
async applyConfig() {
|
||||
// History has a footer
|
||||
if (isCustomizing()) {
|
||||
return Promise.reject("Can't show subviews while customizing");
|
||||
return "Can't show subviews while customizing";
|
||||
}
|
||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
let promise = browserWindow.PanelUI.show();
|
||||
return promise.then(() => {
|
||||
await browserWindow.PanelUI.show();
|
||||
browserWindow.PanelUI.showMainView();
|
||||
browserWindow.document.getElementById("history-panelmenu").click();
|
||||
});
|
||||
|
||||
return undefined;
|
||||
},
|
||||
|
||||
verifyConfig: verifyConfigHelper,
|
||||
@ -53,16 +53,16 @@ this.AppMenu = {
|
||||
|
||||
appMenuHelpSubview: {
|
||||
selectors: ["#appMenu-popup"],
|
||||
applyConfig() {
|
||||
async applyConfig() {
|
||||
if (isCustomizing()) {
|
||||
return Promise.reject("Can't show subviews while customizing");
|
||||
return "Can't show subviews while customizing";
|
||||
}
|
||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
let promise = browserWindow.PanelUI.show();
|
||||
return promise.then(() => {
|
||||
await browserWindow.PanelUI.show();
|
||||
browserWindow.PanelUI.showMainView();
|
||||
browserWindow.document.getElementById("PanelUI-help").click();
|
||||
});
|
||||
|
||||
return undefined;
|
||||
},
|
||||
|
||||
verifyConfig: verifyConfigHelper,
|
||||
@ -73,9 +73,9 @@ this.AppMenu = {
|
||||
|
||||
function verifyConfigHelper() {
|
||||
if (isCustomizing()) {
|
||||
return Promise.reject("AppMenu verifyConfigHelper");
|
||||
return "navigator:browser has the customizing attribute";
|
||||
}
|
||||
return Promise.resolve("AppMenu verifyConfigHelper");
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function isCustomizing() {
|
||||
|
@ -38,12 +38,12 @@ this.Buttons = {
|
||||
CustomizableUI.addWidgetToArea("screenshot-widget", CustomizableUI.AREA_FIXED_OVERFLOW_PANEL);
|
||||
},
|
||||
|
||||
verifyConfig() {
|
||||
async verifyConfig() {
|
||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
if (browserWindow.PanelUI.panel.state == "closed") {
|
||||
return Promise.reject("The button isn't shown when the panel isn't open.");
|
||||
return "The button isn't shown when the panel isn't open.";
|
||||
}
|
||||
return Promise.resolve("menuPanelButtons.verifyConfig");
|
||||
return undefined;
|
||||
},
|
||||
},
|
||||
|
||||
@ -53,12 +53,12 @@ this.Buttons = {
|
||||
CustomizableUI.removeWidgetFromArea("screenshot-widget");
|
||||
},
|
||||
|
||||
verifyConfig() {
|
||||
async verifyConfig() {
|
||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
if (browserWindow.document.documentElement.getAttribute("customizing") != "true") {
|
||||
return Promise.reject("The button isn't shown when we're not in customize mode.");
|
||||
return "The button isn't shown when we're not in customize mode.";
|
||||
}
|
||||
return Promise.resolve("custPaletteButtons.verifyConfig");
|
||||
return undefined;
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -55,7 +55,7 @@ this.ControlCenter = {
|
||||
},
|
||||
|
||||
async verifyConfig() {
|
||||
return Promise.reject("Bug 1373563: intermittent controlCenter_localFile on Taskcluster");
|
||||
return { todo: "Bug 1373563: intermittent controlCenter_localFile on Taskcluster" };
|
||||
},
|
||||
},
|
||||
|
||||
|
@ -112,8 +112,10 @@ this.PermissionPrompts = {
|
||||
|
||||
// We want to skip the progress-notification, so we wait for
|
||||
// the install-confirmation screen to be "not hidden" = shown.
|
||||
await BrowserTestUtils.waitForCondition(() => !notification.hasAttribute("hidden"),
|
||||
"addon install confirmation did not show", 200);
|
||||
return BrowserTestUtils.waitForCondition(() => !notification.hasAttribute("hidden"),
|
||||
"addon install confirmation did not show", 200).catch((msg) => {
|
||||
return Promise.resolve({todo: msg});
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -36,8 +36,14 @@ this.Preferences = {
|
||||
}
|
||||
this.configurations[configName] = {};
|
||||
this.configurations[configName].selectors = ["#browser"];
|
||||
if (primary == "panePrivacy" && customFn) {
|
||||
this.configurations[configName].applyConfig = async () => {
|
||||
return {todo: `${configName} times out on the try server`};
|
||||
};
|
||||
} else {
|
||||
this.configurations[configName].applyConfig = prefHelper.bind(null, primary, customFn);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
configurations: {},
|
||||
@ -49,7 +55,9 @@ let prefHelper = async function(primary, customFn = null) {
|
||||
|
||||
// close any dialog that might still be open
|
||||
await ContentTask.spawn(selectedBrowser, null, async function() {
|
||||
if (!content.window.gSubDialog) {
|
||||
// Check that gSubDialog is defined on the content window
|
||||
// and that there is an open dialog to close
|
||||
if (!content.window.gSubDialog || !content.window.gSubDialog._topDialog) {
|
||||
return;
|
||||
}
|
||||
content.window.gSubDialog.close();
|
||||
@ -73,9 +81,11 @@ let prefHelper = async function(primary, customFn = null) {
|
||||
|
||||
if (customFn) {
|
||||
let customPaintPromise = paintPromise(browserWindow);
|
||||
await customFn(selectedBrowser);
|
||||
let result = await customFn(selectedBrowser);
|
||||
await customPaintPromise;
|
||||
return result;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
function paintPromise(browserWindow) {
|
||||
@ -99,8 +109,13 @@ async function cacheGroup(aBrowser) {
|
||||
}
|
||||
|
||||
async function DNTDialog(aBrowser) {
|
||||
await ContentTask.spawn(aBrowser, null, async function() {
|
||||
content.document.getElementById("doNotTrackSettings").click();
|
||||
return ContentTask.spawn(aBrowser, null, async function() {
|
||||
const button = content.document.getElementById("doNotTrackSettings");
|
||||
if (!button) {
|
||||
return {todo: "The dialog may have exited before we could click the button"};
|
||||
}
|
||||
button.click();
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,7 @@ this.Tabs = {
|
||||
hoverTab(browserWindow.gBrowser.tabs[2]);
|
||||
// also hover the new tab button
|
||||
let newTabButton = browserWindow.document.getAnonymousElementByAttribute(browserWindow.
|
||||
gBrowser.tabContainer, "class", "tabs-newtab-button");
|
||||
gBrowser.tabContainer, "anonid", "tabs-newtab-button");
|
||||
hoverTab(newTabButton);
|
||||
browserWindow.gBrowser.tabs[browserWindow.gBrowser.tabs.length - 1].
|
||||
setAttribute("beforehovered", true);
|
||||
@ -134,6 +134,7 @@ async function allTabTitlesDisplayed(browserWindow) {
|
||||
"about:home": "New Tab",
|
||||
"about:newtab": "New Tab",
|
||||
"about:addons": "Add-ons Manager",
|
||||
"about:privatebrowsing": "Open a private window?"
|
||||
};
|
||||
specToTitleMap[PREFS_TAB] = "browser/skin/settings.svg";
|
||||
specToTitleMap[CUST_TAB] = "browser/skin/customize.svg";
|
||||
|
@ -21,7 +21,7 @@ this.TabsInTitlebar = {
|
||||
selectors: ["#navigator-toolbox"],
|
||||
async applyConfig() {
|
||||
if (Services.appinfo.OS == "Linux") {
|
||||
return Promise.reject("TabsInTitlebar isn't supported on Linux");
|
||||
return "TabsInTitlebar isn't supported on Linux";
|
||||
}
|
||||
Services.prefs.setBoolPref(PREF_TABS_IN_TITLEBAR, true);
|
||||
return undefined;
|
||||
|
@ -36,7 +36,7 @@ this.Toolbars = {
|
||||
async verifyConfig() {
|
||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
if (browserWindow.fullScreen) {
|
||||
return Promise.reject("The bookmark toolbar and menubar are not shown in fullscreen.");
|
||||
return "The bookmark toolbar and menubar are not shown in fullscreen.";
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
|
@ -11,6 +11,10 @@ add_task(async function capture() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (AppConstants.platform == "macosx") {
|
||||
// Bug 1425394 - Generate output so mozprocess knows we're still alive for the long session.
|
||||
SimpleTest.requestCompleteLog();
|
||||
}
|
||||
let sets = ["TabsInTitlebar", "Tabs", "WindowSize", "Toolbars", "LightweightThemes", "UIDensities"];
|
||||
await TestRunner.start(sets, "primaryUI");
|
||||
});
|
||||
|
@ -1,9 +1,9 @@
|
||||
This is the debugger.html project output.
|
||||
See https://github.com/devtools-html/debugger.html
|
||||
|
||||
Version 8.0
|
||||
commit https://github.com/devtools-html/debugger.html/commit/9be8e9d9e7a70730bc02e50ae019028f1f06b14e
|
||||
comparison https://github.com/devtools-html/debugger.html/compare/release-7...release-8
|
||||
Version 9.0
|
||||
commit: https://github.com/devtools-html/debugger.html/commit/0de8d3f673ee0f0030d666f1827380e17bef8036
|
||||
comparison: https://github.com/devtools-html/debugger.html/compare/release-8...release-9
|
||||
|
||||
Packages:
|
||||
- babel-plugin-transform-es2015-modules-commonjs @6.26.0
|
||||
|
@ -1568,7 +1568,7 @@ html[dir="rtl"] .managed-tree .tree .node > div {
|
||||
.outline-list {
|
||||
list-style-type: none;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
padding: 10px 0px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@ -1581,7 +1581,7 @@ html[dir="rtl"] .managed-tree .tree .node > div {
|
||||
.outline-list h2 {
|
||||
font-weight: normal;
|
||||
font-size: 1em;
|
||||
margin: 10px 0;
|
||||
margin: 10px 0 10px 10px;
|
||||
color: var(--theme-body-color);
|
||||
}
|
||||
|
||||
@ -1596,6 +1596,10 @@ html[dir="rtl"] .managed-tree .tree .node > div {
|
||||
.outline-list__element:hover {
|
||||
background: var(--theme-toolbar-background-hover);
|
||||
}
|
||||
|
||||
.outline-list__element .function {
|
||||
padding-left: 10px;
|
||||
}
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
@ -2849,16 +2853,40 @@ html .breakpoints-list .breakpoint.paused {
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
.expression-input-form {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.input-expression {
|
||||
width: 100%;
|
||||
margin: 0px;
|
||||
margin: 0;
|
||||
border: 1px;
|
||||
background-color: var(--theme-sidebar-background);
|
||||
font-size: 12px;
|
||||
padding: 0px 20px;
|
||||
padding: 0.5em 2.16em;
|
||||
color: var(--theme-body-color);
|
||||
}
|
||||
|
||||
@keyframes shake {
|
||||
0%,
|
||||
100% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
20%,
|
||||
60% {
|
||||
transform: translateX(-10px);
|
||||
}
|
||||
40%,
|
||||
80% {
|
||||
transform: translateX(10px);
|
||||
}
|
||||
}
|
||||
|
||||
.input-expression.error {
|
||||
border: 1px solid red;
|
||||
animation: 150ms cubic-bezier(0.07, 0.95, 0, 1) shake;
|
||||
}
|
||||
|
||||
.input-expression::placeholder {
|
||||
text-align: center;
|
||||
font-style: italic;
|
||||
@ -2877,7 +2905,6 @@ html .breakpoints-list .breakpoint.paused {
|
||||
padding: 0;
|
||||
}
|
||||
.expression-input-container {
|
||||
padding: 0.5em;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@ -3367,6 +3394,7 @@ html[dir="rtl"] .dropdown {
|
||||
padding: 0px 2px;
|
||||
position: relative;
|
||||
align-self: center;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.dropdown-button {
|
||||
@ -3376,6 +3404,8 @@ html[dir="rtl"] .dropdown {
|
||||
padding: 0;
|
||||
font-weight: 100;
|
||||
font-size: 14px;
|
||||
height: 100%;
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
.dropdown li {
|
||||
@ -3396,6 +3426,7 @@ html[dir="rtl"] .dropdown {
|
||||
height: var(--icon-size);
|
||||
margin-right: 5px;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.dropdown-icon.prettyPrint {
|
||||
|
File diff suppressed because one or more lines are too long
@ -27,6 +27,7 @@ add_task(async function() {
|
||||
"Breakpoint has correct line"
|
||||
);
|
||||
|
||||
await addBreakpoint(dbg, entrySrc, 5);
|
||||
await addBreakpoint(dbg, entrySrc, 15);
|
||||
await disableBreakpoint(dbg, entrySrc, 15);
|
||||
|
||||
@ -34,7 +35,10 @@ add_task(async function() {
|
||||
await reload(dbg, "opts.js");
|
||||
await waitForDispatch(dbg, "LOAD_SOURCE_TEXT");
|
||||
|
||||
is(getBreakpoints(getState()).size, 2, "One breakpoint exists");
|
||||
await waitForPaused(dbg);
|
||||
assertPausedLocation(dbg);
|
||||
|
||||
is(getBreakpoints(getState()).size, 3, "Three breakpoints exist");
|
||||
|
||||
ok(
|
||||
getBreakpoint(getState(), { sourceId: entrySrc.id, line: 13 }),
|
||||
|
@ -439,6 +439,9 @@ original=original
|
||||
# LOCALIZATION NOTE (expressions.placeholder): Placeholder text for expression
|
||||
# input element
|
||||
expressions.placeholder=Add watch expression
|
||||
# LOCALIZATION NOTE (expressions.errorMsg): Error text for expression
|
||||
# input element
|
||||
expressions.errorMsg=Invalid expression…
|
||||
expressions.label=Add watch expression
|
||||
expressions.accesskey=e
|
||||
|
||||
|
@ -5273,32 +5273,37 @@ nsContentUtils::SetNodeTextContent(nsIContent* aContent,
|
||||
UPDATE_CONTENT_MODEL, true);
|
||||
nsAutoMutationBatch mb;
|
||||
|
||||
uint32_t childCount = aContent->GetChildCount();
|
||||
|
||||
if (aTryReuse && !aValue.IsEmpty()) {
|
||||
uint32_t removeIndex = 0;
|
||||
// Let's remove nodes until we find a eTEXT.
|
||||
while (aContent->HasChildren()) {
|
||||
nsIContent* child = aContent->GetFirstChild();
|
||||
if (child->IsNodeOfType(nsINode::eTEXT)) {
|
||||
break;
|
||||
}
|
||||
aContent->RemoveChildNode(child, true);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < childCount; ++i) {
|
||||
nsIContent* child = aContent->GetChildAt_Deprecated(removeIndex);
|
||||
if (removeIndex == 0 && child && child->IsNodeOfType(nsINode::eTEXT)) {
|
||||
// If we have a node, it must be a eTEXT and we reuse it.
|
||||
if (aContent->HasChildren()) {
|
||||
nsIContent* child = aContent->GetFirstChild();
|
||||
MOZ_ASSERT(child->IsNodeOfType(nsINode::eTEXT));
|
||||
nsresult rv = child->SetText(aValue, true);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
removeIndex = 1;
|
||||
}
|
||||
else {
|
||||
aContent->RemoveChildAt_Deprecated(removeIndex, true);
|
||||
// All the following nodes, if they exist, must be deleted.
|
||||
while (nsIContent* nextChild = child->GetNextSibling()) {
|
||||
aContent->RemoveChildNode(nextChild, true);
|
||||
}
|
||||
}
|
||||
|
||||
if (removeIndex == 1) {
|
||||
if (aContent->HasChildren()) {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
else {
|
||||
mb.Init(aContent, true, false);
|
||||
for (uint32_t i = 0; i < childCount; ++i) {
|
||||
aContent->RemoveChildAt_Deprecated(0, true);
|
||||
while (aContent->HasChildren()) {
|
||||
aContent->RemoveChildNode(aContent->GetFirstChild(), true);
|
||||
}
|
||||
}
|
||||
mb.RemovalDone();
|
||||
|
@ -804,19 +804,9 @@ WebGLContext::AssertCachedGlobalState() const
|
||||
gl->fIsEnabled(LOCAL_GL_RASTERIZER_DISCARD) == mRasterizerDiscardEnabled);
|
||||
MOZ_ASSERT(gl->fIsEnabled(LOCAL_GL_SCISSOR_TEST) == mScissorTestEnabled);
|
||||
|
||||
GLfloat colorClearValue[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
gl->fGetFloatv(LOCAL_GL_COLOR_CLEAR_VALUE, colorClearValue);
|
||||
const bool ok = IsCacheCorrect(mColorClearValue[0], colorClearValue[0]) &&
|
||||
IsCacheCorrect(mColorClearValue[1], colorClearValue[1]) &&
|
||||
IsCacheCorrect(mColorClearValue[2], colorClearValue[2]) &&
|
||||
IsCacheCorrect(mColorClearValue[3], colorClearValue[3]);
|
||||
if (!ok) {
|
||||
gfxCriticalNote << mColorClearValue[0] << " - " << colorClearValue[0] << " = " << (mColorClearValue[0] - colorClearValue[0]) << "\n"
|
||||
<< mColorClearValue[1] << " - " << colorClearValue[1] << " = " << (mColorClearValue[1] - colorClearValue[1]) << "\n"
|
||||
<< mColorClearValue[2] << " - " << colorClearValue[2] << " = " << (mColorClearValue[2] - colorClearValue[2]) << "\n"
|
||||
<< mColorClearValue[3] << " - " << colorClearValue[3] << " = " << (mColorClearValue[3] - colorClearValue[3]);
|
||||
}
|
||||
MOZ_ASSERT(ok);
|
||||
// Cannot trivially check COLOR_CLEAR_VALUE, since in old GL versions glGet may clamp
|
||||
// based on whether the current framebuffer is floating-point or not.
|
||||
// This also means COLOR_CLEAR_VALUE save+restore is dangerous!
|
||||
|
||||
realGLboolean depthWriteMask = 0;
|
||||
gl->fGetBooleanv(LOCAL_GL_DEPTH_WRITEMASK, &depthWriteMask);
|
||||
|
@ -34,27 +34,6 @@ WebGLExtensionColorBufferFloat::WebGLExtensionColorBufferFloat(WebGLContext* web
|
||||
FOO(RGBA32F);
|
||||
|
||||
#undef FOO
|
||||
|
||||
#ifdef DEBUG
|
||||
const auto gl = webgl->gl;
|
||||
float was[4] = {};
|
||||
gl->fGetFloatv(LOCAL_GL_COLOR_CLEAR_VALUE, was);
|
||||
|
||||
const float test[4] = {-1.0, 0, 2.0, 255.0};
|
||||
gl->fClearColor(test[0], test[1], test[2], test[3]);
|
||||
|
||||
float now[4] = {};
|
||||
gl->fGetFloatv(LOCAL_GL_COLOR_CLEAR_VALUE, now);
|
||||
const bool ok = now[0] == test[0] && now[1] == test[1] &&
|
||||
now[2] == test[2] && now[3] == test[3];
|
||||
if (!ok) {
|
||||
printf_stderr("COLOR_CLEAR_VALUE: now{%f,%f,%f,%f} != test{%f,%f,%f,%f}\n",
|
||||
test[0], test[1], test[2], test[3],
|
||||
now[0], now[1], now[2], now[3]);
|
||||
MOZ_ASSERT(false);
|
||||
}
|
||||
gl->fClearColor(was[0], was[1], was[2], was[3]);
|
||||
#endif
|
||||
}
|
||||
|
||||
WebGLExtensionColorBufferFloat::~WebGLExtensionColorBufferFloat()
|
||||
|
@ -7596,7 +7596,7 @@ fail-if = (os == 'android')
|
||||
[generated/test_conformance__extensions__oes-texture-half-float-with-video.html]
|
||||
fail-if = (os == 'mac') || (os == 'android') || (os == 'linux') || (os == 'win')
|
||||
[generated/test_conformance__extensions__oes-texture-half-float.html]
|
||||
fail-if = (os == 'mac') || (os == 'win') || (os == 'android')
|
||||
fail-if = (os == 'mac') || (os == 'win') || (os == 'android') || (os == 'linux')
|
||||
[generated/test_conformance__extensions__oes-vertex-array-object-bufferData.html]
|
||||
[generated/test_conformance__extensions__oes-vertex-array-object.html]
|
||||
skip-if = (os == 'mac' && os_version == '10.6')
|
||||
|
@ -132,7 +132,7 @@ skip-if = (os == 'android') || (os == 'win')
|
||||
[generated/test_2_conformance2__vertex_arrays__vertex-array-object.html]
|
||||
fail-if = (os == 'mac') || (os == 'win')
|
||||
[generated/test_conformance__extensions__oes-texture-half-float.html]
|
||||
fail-if = (os == 'mac') || (os == 'win') || (os == 'android')
|
||||
fail-if = (os == 'mac') || (os == 'win') || (os == 'android') || (os == 'linux')
|
||||
[generated/test_conformance__attribs__gl-vertexattribpointer.html]
|
||||
fail-if = (os == 'android')
|
||||
[generated/test_conformance__ogles__GL__biuDepthRange__biuDepthRange_001_to_002.html]
|
||||
|
@ -17,7 +17,7 @@ fail-if = (os == 'android') || (os == 'mac' && os_version == '10.6')
|
||||
[ensure-exts/test_EXT_blend_minmax.html]
|
||||
fail-if = (os == 'android')
|
||||
[ensure-exts/test_EXT_color_buffer_half_float.html]
|
||||
fail-if = (os == 'android') || (os == 'linux')
|
||||
fail-if = (os == 'android')
|
||||
[ensure-exts/test_EXT_disjoint_timer_query.html]
|
||||
fail-if = (os == 'android') || (os == 'win' && os_version == '5.1')
|
||||
[ensure-exts/test_EXT_frag_depth.html]
|
||||
@ -31,7 +31,7 @@ fail-if = (os == 'android') || (os == 'linux')
|
||||
[ensure-exts/test_OES_standard_derivatives.html]
|
||||
fail-if = (os == 'android')
|
||||
[ensure-exts/test_WEBGL_color_buffer_float.html]
|
||||
fail-if = (os == 'android') || (os == 'linux')
|
||||
fail-if = (os == 'android')
|
||||
[ensure-exts/test_WEBGL_compressed_texture_atc.html]
|
||||
fail-if = (os == 'android') || (os == 'linux') || (os == 'mac') || (os == 'win')
|
||||
[ensure-exts/test_WEBGL_compressed_texture_es3.html]
|
||||
|
@ -73,6 +73,7 @@
|
||||
#include "mozilla/media/MediaChild.h"
|
||||
#include "mozilla/BasePrincipal.h"
|
||||
#include "mozilla/WebBrowserPersistDocumentChild.h"
|
||||
#include "mozilla/HangDetails.h"
|
||||
#include "imgLoader.h"
|
||||
#include "GMPServiceChild.h"
|
||||
#include "NullPrincipal.h"
|
||||
|
@ -100,6 +100,7 @@
|
||||
#include "mozilla/WebBrowserPersistDocumentParent.h"
|
||||
#include "mozilla/widget/ScreenManager.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/HangDetails.h"
|
||||
#include "nsAnonymousTemporaryFile.h"
|
||||
#include "nsAppRunner.h"
|
||||
#include "nsCDefaultURIFixup.h"
|
||||
|
@ -60,6 +60,7 @@ include ServiceWorkerConfiguration;
|
||||
include GraphicsMessages;
|
||||
include MemoryReportTypes;
|
||||
include ClientIPCTypes;
|
||||
include HangTypes;
|
||||
|
||||
// Workaround to prevent error if PContentChild.cpp & PContentBridgeParent.cpp
|
||||
// are put into different UnifiedProtocolsXX.cpp files.
|
||||
@ -98,7 +99,6 @@ using mozilla::Telemetry::DynamicScalarDefinition from "mozilla/TelemetryComms.h
|
||||
using mozilla::Telemetry::ChildEventData from "mozilla/TelemetryComms.h";
|
||||
using mozilla::Telemetry::DiscardedData from "mozilla/TelemetryComms.h";
|
||||
using mozilla::CrossProcessMutexHandle from "mozilla/ipc/CrossProcessMutex.h";
|
||||
using mozilla::HangDetails from "mozilla/HangDetails.h";
|
||||
|
||||
union ChromeRegistryItem
|
||||
{
|
||||
|
@ -73,7 +73,7 @@ namespace {
|
||||
void* sServerHandle = nullptr;
|
||||
|
||||
// Initialized during early startup, protected by sMutex.
|
||||
ipc::FileDescriptor sIPCConnection;
|
||||
StaticAutoPtr<ipc::FileDescriptor> sIPCConnection;
|
||||
|
||||
static bool
|
||||
StartSoundServer()
|
||||
@ -204,6 +204,7 @@ static const uint32_t CUBEB_NORMAL_LATENCY_MS = 100;
|
||||
static const uint32_t CUBEB_NORMAL_LATENCY_FRAMES = 1024;
|
||||
|
||||
namespace CubebUtils {
|
||||
cubeb* GetCubebContextUnlocked();
|
||||
|
||||
void PrefChanged(const char* aPref, void* aClosure)
|
||||
{
|
||||
@ -405,12 +406,12 @@ void InitAudioIPCConnection()
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
auto contentChild = dom::ContentChild::GetSingleton();
|
||||
auto promise = contentChild->SendCreateAudioIPCConnection();
|
||||
promise->Then(AbstractThread::GetCurrent(),
|
||||
promise->Then(AbstractThread::MainThread(),
|
||||
__func__,
|
||||
[](ipc::FileDescriptor aFD) {
|
||||
StaticMutexAutoLock lock(sMutex);
|
||||
MOZ_ASSERT(!sIPCConnection.IsValid());
|
||||
sIPCConnection = aFD;
|
||||
MOZ_ASSERT(!sIPCConnection);
|
||||
sIPCConnection = new ipc::FileDescriptor(aFD);
|
||||
},
|
||||
[](mozilla::ipc::ResponseRejectReason aReason) {
|
||||
MOZ_LOG(gCubebLog, LogLevel::Error, ("SendCreateAudioIPCConnection failed: %d",
|
||||
@ -452,10 +453,10 @@ cubeb* GetCubebContextUnlocked()
|
||||
if (sCubebSandbox) {
|
||||
if (XRE_IsParentProcess()) {
|
||||
// TODO: Don't use audio IPC when within the same process.
|
||||
MOZ_ASSERT(!sIPCConnection.IsValid());
|
||||
sIPCConnection = CreateAudioIPCConnection();
|
||||
MOZ_ASSERT(!sIPCConnection);
|
||||
sIPCConnection = new ipc::FileDescriptor(CreateAudioIPCConnection());
|
||||
} else {
|
||||
MOZ_DIAGNOSTIC_ASSERT(sIPCConnection.IsValid());
|
||||
MOZ_DIAGNOSTIC_ASSERT(sIPCConnection);
|
||||
}
|
||||
}
|
||||
|
||||
@ -463,8 +464,9 @@ cubeb* GetCubebContextUnlocked()
|
||||
|
||||
int rv = sCubebSandbox
|
||||
? audioipc_client_init(&sCubebContext, sBrandName,
|
||||
sIPCConnection.ClonePlatformHandle().release())
|
||||
sIPCConnection->ClonePlatformHandle().release())
|
||||
: cubeb_init(&sCubebContext, sBrandName, sCubebBackendName.get());
|
||||
sIPCConnection = nullptr;
|
||||
#else // !MOZ_CUBEB_REMOTING
|
||||
int rv = cubeb_init(&sCubebContext, sBrandName, sCubebBackendName.get());
|
||||
#endif // MOZ_CUBEB_REMOTING
|
||||
@ -559,8 +561,7 @@ void InitLibrary()
|
||||
#endif
|
||||
#ifdef MOZ_CUBEB_REMOTING
|
||||
if (sCubebSandbox && XRE_IsContentProcess()) {
|
||||
AbstractThread::MainThread()->Dispatch(
|
||||
NS_NewRunnableFunction("CubebUtils::InitLibrary", &InitAudioIPCConnection));
|
||||
InitAudioIPCConnection();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@ -585,7 +586,7 @@ void ShutdownLibrary()
|
||||
sCubebState = CubebState::Shutdown;
|
||||
|
||||
#ifdef MOZ_CUBEB_REMOTING
|
||||
sIPCConnection = ipc::FileDescriptor();
|
||||
sIPCConnection = nullptr;
|
||||
ShutdownSoundServer();
|
||||
#endif
|
||||
}
|
||||
|
@ -38,11 +38,9 @@ enum Side {
|
||||
Output
|
||||
};
|
||||
|
||||
void PrefChanged(const char* aPref, void* aClosure);
|
||||
double GetVolumeScale();
|
||||
bool GetFirstStream();
|
||||
cubeb* GetCubebContext();
|
||||
cubeb* GetCubebContextUnlocked();
|
||||
void ReportCubebStreamInitFailure(bool aIsFirstStream);
|
||||
void ReportCubebBackendUsed();
|
||||
uint32_t GetCubebPlaybackLatencyInMilliseconds();
|
||||
|
@ -53,11 +53,6 @@ public:
|
||||
static const int DEFAULT_169_VIDEO_WIDTH = 1280;
|
||||
static const int DEFAULT_169_VIDEO_HEIGHT = 720;
|
||||
|
||||
// This allows using whatever rate the graph is using for the
|
||||
// MediaStreamTrack. This is useful for microphone data, we know it's already
|
||||
// at the correct rate for insertion in the MSG.
|
||||
static const int USE_GRAPH_RATE = -1;
|
||||
|
||||
/* Populate an array of video sources in the nsTArray. Also include devices
|
||||
* that are currently unavailable. */
|
||||
virtual void EnumerateVideoDevices(dom::MediaSourceEnum,
|
||||
|
@ -543,7 +543,6 @@ private:
|
||||
nsString mDeviceName;
|
||||
nsCString mDeviceUUID;
|
||||
|
||||
int32_t mSampleFrequency;
|
||||
uint64_t mTotalFrames;
|
||||
uint64_t mLastLogFrames;
|
||||
|
||||
|
@ -70,7 +70,6 @@ MediaEngineWebRTCMicrophoneSource::MediaEngineWebRTCMicrophoneSource(
|
||||
, mExtendedFilter(aExtendedFilter)
|
||||
, mTrackID(TRACK_NONE)
|
||||
, mStarted(false)
|
||||
, mSampleFrequency(MediaEngine::USE_GRAPH_RATE)
|
||||
, mTotalFrames(0)
|
||||
, mLastLogFrames(0)
|
||||
, mSkipProcessing(false)
|
||||
@ -481,9 +480,7 @@ MediaEngineWebRTCMicrophoneSource::Start(SourceMediaStream *aStream,
|
||||
}
|
||||
|
||||
AudioSegment* segment = new AudioSegment();
|
||||
if (mSampleFrequency == MediaEngine::USE_GRAPH_RATE) {
|
||||
mSampleFrequency = aStream->GraphRate();
|
||||
}
|
||||
|
||||
aStream->AddAudioTrack(aID, aStream->GraphRate(), 0, segment, SourceMediaStream::ADDTRACK_QUEUED);
|
||||
|
||||
// XXX Make this based on the pref.
|
||||
@ -773,7 +770,7 @@ MediaEngineWebRTCMicrophoneSource::InsertInGraph(const T* aBuffer,
|
||||
|
||||
if (MOZ_LOG_TEST(AudioLogModule(), LogLevel::Debug)) {
|
||||
mTotalFrames += aFrames;
|
||||
if (mTotalFrames > mLastLogFrames + mSampleFrequency) { // ~ 1 second
|
||||
if (mTotalFrames > mLastLogFrames + mSources[0]->GraphRate()) { // ~ 1 second
|
||||
MOZ_LOG(AudioLogModule(), LogLevel::Debug,
|
||||
("%p: Inserting %zu samples into graph, total frames = %" PRIu64,
|
||||
(void*)this, aFrames, mTotalFrames));
|
||||
@ -894,9 +891,6 @@ MediaEngineWebRTCMicrophoneSource::FreeChannel()
|
||||
bool
|
||||
MediaEngineWebRTCMicrophoneSource::AllocChannel()
|
||||
{
|
||||
mSampleFrequency = MediaEngine::USE_GRAPH_RATE;
|
||||
LOG(("%s: sampling rate %u", __FUNCTION__, mSampleFrequency));
|
||||
|
||||
mState = kAllocated;
|
||||
sChannelsOpen++;
|
||||
return true;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -9,7 +9,7 @@
|
||||
// properties which qualify the exposure of that interface. For example:
|
||||
//
|
||||
// [
|
||||
// "AGlobalInterface",
|
||||
// "AGlobalInterface", // secure context only
|
||||
// { name: "ExperimentalThing", release: false },
|
||||
// { name: "ReallyExperimentalThing", nightly: true },
|
||||
// { name: "DesktopOnlyThing", desktop: true },
|
||||
@ -23,54 +23,54 @@
|
||||
// a JavaScript Engine peer!
|
||||
var ecmaGlobals =
|
||||
[
|
||||
"Array",
|
||||
"ArrayBuffer",
|
||||
{name: "Atomics", disabled: true},
|
||||
"Boolean",
|
||||
{name: "ByteLengthQueuingStrategy", optional: true},
|
||||
{name: "CountQueuingStrategy", optional: true},
|
||||
"DataView",
|
||||
"Date",
|
||||
"Error",
|
||||
"EvalError",
|
||||
"Float32Array",
|
||||
"Float64Array",
|
||||
"Function",
|
||||
"Infinity",
|
||||
"Int16Array",
|
||||
"Int32Array",
|
||||
"Int8Array",
|
||||
"InternalError",
|
||||
"Intl",
|
||||
"JSON",
|
||||
"Map",
|
||||
"Math",
|
||||
"NaN",
|
||||
"Number",
|
||||
"Object",
|
||||
"Promise",
|
||||
"Proxy",
|
||||
"RangeError",
|
||||
{name: "ReadableStream", optional: true},
|
||||
"ReferenceError",
|
||||
"Reflect",
|
||||
"RegExp",
|
||||
"Set",
|
||||
{name: "SharedArrayBuffer", disabled: true},
|
||||
{name: "SIMD", nightly: true},
|
||||
"String",
|
||||
"Symbol",
|
||||
"SyntaxError",
|
||||
{name: "TypedObject", nightly: true},
|
||||
"TypeError",
|
||||
"Uint16Array",
|
||||
"Uint32Array",
|
||||
"Uint8Array",
|
||||
"Uint8ClampedArray",
|
||||
"URIError",
|
||||
"WeakMap",
|
||||
"WeakSet",
|
||||
{name: "WebAssembly", optional: true}
|
||||
{name: "Array", insecureContext: true},
|
||||
{name: "ArrayBuffer", insecureContext: true},
|
||||
{name: "Atomics", insecureContext: true, disabled: true},
|
||||
{name: "Boolean", insecureContext: true},
|
||||
{name: "ByteLengthQueuingStrategy", insecureContext: true, optional: true},
|
||||
{name: "CountQueuingStrategy", insecureContext: true, optional: true},
|
||||
{name: "DataView", insecureContext: true},
|
||||
{name: "Date", insecureContext: true},
|
||||
{name: "Error", insecureContext: true},
|
||||
{name: "EvalError", insecureContext: true},
|
||||
{name: "Float32Array", insecureContext: true},
|
||||
{name: "Float64Array", insecureContext: true},
|
||||
{name: "Function", insecureContext: true},
|
||||
{name: "Infinity", insecureContext: true},
|
||||
{name: "Int16Array", insecureContext: true},
|
||||
{name: "Int32Array", insecureContext: true},
|
||||
{name: "Int8Array", insecureContext: true},
|
||||
{name: "InternalError", insecureContext: true},
|
||||
{name: "Intl", insecureContext: true},
|
||||
{name: "JSON", insecureContext: true},
|
||||
{name: "Map", insecureContext: true},
|
||||
{name: "Math", insecureContext: true},
|
||||
{name: "NaN", insecureContext: true},
|
||||
{name: "Number", insecureContext: true},
|
||||
{name: "Object", insecureContext: true},
|
||||
{name: "Promise", insecureContext: true},
|
||||
{name: "Proxy", insecureContext: true},
|
||||
{name: "RangeError", insecureContext: true},
|
||||
{name: "ReadableStream", insecureContext: true, optional: true},
|
||||
{name: "ReferenceError", insecureContext: true},
|
||||
{name: "Reflect", insecureContext: true},
|
||||
{name: "RegExp", insecureContext: true},
|
||||
{name: "Set", insecureContext: true},
|
||||
{name: "SharedArrayBuffer", insecureContext: true, disabled: true},
|
||||
{name: "SIMD", insecureContext: true, nightly: true},
|
||||
{name: "String", insecureContext: true},
|
||||
{name: "Symbol", insecureContext: true},
|
||||
{name: "SyntaxError", insecureContext: true},
|
||||
{name: "TypedObject", insecureContext: true, nightly: true},
|
||||
{name: "TypeError", insecureContext: true},
|
||||
{name: "Uint16Array", insecureContext: true},
|
||||
{name: "Uint32Array", insecureContext: true},
|
||||
{name: "Uint8Array", insecureContext: true},
|
||||
{name: "Uint8ClampedArray", insecureContext: true},
|
||||
{name: "URIError", insecureContext: true},
|
||||
{name: "WeakMap", insecureContext: true},
|
||||
{name: "WeakSet", insecureContext: true},
|
||||
{name: "WebAssembly", insecureContext: true, optional: true}
|
||||
];
|
||||
// IMPORTANT: Do not change the list above without review from
|
||||
// a JavaScript Engine peer!
|
||||
@ -79,173 +79,173 @@ var ecmaGlobals =
|
||||
var interfaceNamesInGlobalScope =
|
||||
[
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"AbortController",
|
||||
{name: "AbortController", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"AbortSignal",
|
||||
{name: "AbortSignal", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"Blob",
|
||||
{name: "Blob", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"BroadcastChannel",
|
||||
{name: "BroadcastChannel", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"Cache",
|
||||
{name: "Cache", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"CacheStorage",
|
||||
{name: "CacheStorage", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"CloseEvent",
|
||||
{name: "CloseEvent", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"Crypto",
|
||||
{name: "Crypto", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"CustomEvent",
|
||||
{name: "CustomEvent", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"DedicatedWorkerGlobalScope",
|
||||
{name: "DedicatedWorkerGlobalScope", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"Directory",
|
||||
{name: "Directory", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"DOMCursor",
|
||||
{name: "DOMCursor", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"DOMError",
|
||||
{name: "DOMError", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"DOMException",
|
||||
{name: "DOMException", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"DOMRequest",
|
||||
{name: "DOMRequest", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"DOMStringList",
|
||||
{name: "DOMStringList", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"ErrorEvent",
|
||||
{name: "ErrorEvent", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"Event",
|
||||
{name: "Event", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"EventSource",
|
||||
{name: "EventSource", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"EventTarget",
|
||||
{name: "EventTarget", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"File",
|
||||
{name: "File", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"FileList",
|
||||
{name: "FileList", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"FileReader",
|
||||
{name: "FileReader", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"FileReaderSync",
|
||||
{name: "FileReaderSync", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"FormData",
|
||||
{name: "FormData", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"Headers",
|
||||
{name: "Headers", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"IDBCursor",
|
||||
{name: "IDBCursor", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"IDBCursorWithValue",
|
||||
{name: "IDBCursorWithValue", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"IDBDatabase",
|
||||
{name: "IDBDatabase", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"IDBFactory",
|
||||
{name: "IDBFactory", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"IDBIndex",
|
||||
{name: "IDBIndex", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"IDBKeyRange",
|
||||
{name: "IDBKeyRange", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"IDBObjectStore",
|
||||
{name: "IDBObjectStore", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"IDBOpenDBRequest",
|
||||
{name: "IDBOpenDBRequest", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"IDBRequest",
|
||||
{name: "IDBRequest", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"IDBTransaction",
|
||||
{name: "IDBTransaction", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"IDBVersionChangeEvent",
|
||||
{name: "IDBVersionChangeEvent", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"ImageBitmap",
|
||||
{name: "ImageBitmap", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"ImageBitmapRenderingContext",
|
||||
{name: "ImageBitmapRenderingContext", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"ImageData",
|
||||
{name: "ImageData", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"MessageChannel",
|
||||
{name: "MessageChannel", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"MessageEvent",
|
||||
{name: "MessageEvent", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"MessagePort",
|
||||
{name: "MessagePort", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{ name: "NetworkInformation", android: true },
|
||||
{ name: "NetworkInformation", insecureContext: true, android: true },
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"Notification",
|
||||
{name: "Notification", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{ name: "OffscreenCanvas", disabled: true },
|
||||
{ name: "OffscreenCanvas", insecureContext: true, disabled: true },
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"Performance",
|
||||
{name: "Performance", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"PerformanceEntry",
|
||||
{name: "PerformanceEntry", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"PerformanceMark",
|
||||
{name: "PerformanceMark", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"PerformanceMeasure",
|
||||
{name: "PerformanceMeasure", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"PerformanceObserver",
|
||||
{name: "PerformanceObserver", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"PerformanceObserverEntryList",
|
||||
{name: "PerformanceObserverEntryList", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"ProgressEvent",
|
||||
{name: "ProgressEvent", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"PushManager",
|
||||
{name: "PushManager", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"PushSubscription",
|
||||
{name: "PushSubscription", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"PushSubscriptionOptions",
|
||||
{name: "PushSubscriptionOptions", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"Request",
|
||||
{name: "Request", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"Response",
|
||||
{name: "Response", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"ServiceWorkerRegistration",
|
||||
{name: "ServiceWorkerRegistration", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "StorageManager", isSecureContext: true, android: false},
|
||||
{name: "StorageManager", android: false},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"SubtleCrypto",
|
||||
{name: "SubtleCrypto", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"TextDecoder",
|
||||
{name: "TextDecoder", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"TextEncoder",
|
||||
{name: "TextEncoder", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"XMLHttpRequest",
|
||||
{name: "XMLHttpRequest", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"XMLHttpRequestEventTarget",
|
||||
{name: "XMLHttpRequestEventTarget", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"XMLHttpRequestUpload",
|
||||
{name: "XMLHttpRequestUpload", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"URL",
|
||||
{name: "URL", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"URLSearchParams",
|
||||
{name: "URLSearchParams", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{ name: "WebGLActiveInfo", disabled: true },
|
||||
{ name: "WebGLActiveInfo", insecureContext: true, disabled: true },
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{ name: "WebGLBuffer", disabled: true },
|
||||
{ name: "WebGLBuffer", insecureContext: true, disabled: true },
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{ name: "WebGLContextEvent", disabled: true },
|
||||
{ name: "WebGLContextEvent", insecureContext: true, disabled: true },
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{ name: "WebGLFramebuffer", disabled: true },
|
||||
{ name: "WebGLFramebuffer", insecureContext: true, disabled: true },
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{ name: "WebGLProgram", disabled: true },
|
||||
{ name: "WebGLProgram", insecureContext: true, disabled: true },
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{ name: "WebGLRenderbuffer", disabled: true },
|
||||
{ name: "WebGLRenderbuffer", insecureContext: true, disabled: true },
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{ name: "WebGLRenderingContext", disabled: true },
|
||||
{ name: "WebGLRenderingContext", insecureContext: true, disabled: true },
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{ name: "WebGLShader", disabled: true },
|
||||
{ name: "WebGLShader", insecureContext: true, disabled: true },
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{ name: "WebGLShaderPrecisionFormat", disabled: true },
|
||||
{ name: "WebGLShaderPrecisionFormat", insecureContext: true, disabled: true },
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{ name: "WebGLTexture", disabled: true },
|
||||
{ name: "WebGLTexture", insecureContext: true, disabled: true },
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{ name: "WebGLUniformLocation", disabled: true },
|
||||
{ name: "WebGLUniformLocation", insecureContext: true, disabled: true },
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"WebSocket",
|
||||
{name: "WebSocket", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"Worker",
|
||||
{name: "Worker", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"WorkerGlobalScope",
|
||||
{name: "WorkerGlobalScope", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"WorkerLocation",
|
||||
{name: "WorkerLocation", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"WorkerNavigator",
|
||||
{name: "WorkerNavigator", insecureContext: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
];
|
||||
// IMPORTANT: Do not change the list above without review from a DOM peer!
|
||||
@ -255,6 +255,7 @@ function createInterfaceMap(version, userAgent) {
|
||||
var isRelease = !version.includes("a");
|
||||
var isDesktop = !/Mobile|Tablet/.test(userAgent);
|
||||
var isAndroid = !!navigator.userAgent.includes("Android");
|
||||
var isInsecureContext = !self.isSecureContext;
|
||||
|
||||
var interfaceMap = {};
|
||||
|
||||
@ -262,7 +263,7 @@ function createInterfaceMap(version, userAgent) {
|
||||
{
|
||||
for (var entry of interfaces) {
|
||||
if (typeof(entry) === "string") {
|
||||
interfaceMap[entry] = true;
|
||||
interfaceMap[entry] = !isInsecureContext;
|
||||
} else {
|
||||
ok(!("pref" in entry), "Bogus pref annotation for " + entry.name);
|
||||
if ((entry.nightly === !isNightly) ||
|
||||
@ -270,7 +271,12 @@ function createInterfaceMap(version, userAgent) {
|
||||
(entry.desktop === !isDesktop) ||
|
||||
(entry.android === !isAndroid && !entry.nightlyAndroid) ||
|
||||
(entry.release === !isRelease) ||
|
||||
(entry.isSecureContext === !isSecureContext) ||
|
||||
// The insecureContext test is very purposefully converting
|
||||
// entry.insecureContext to boolean, so undefined will convert to
|
||||
// false. That way entries without an insecureContext annotation
|
||||
// will get treated as "insecureContext: false", which means exposed
|
||||
// only in secure contexts.
|
||||
(isInsecureContext && !Boolean(entry.insecureContext)) ||
|
||||
entry.disabled) {
|
||||
interfaceMap[entry.name] = false;
|
||||
} else if (entry.optional) {
|
||||
|
@ -625,11 +625,10 @@ txMozillaXMLOutput::createTxWrapper()
|
||||
mDocument->CreateElem(nsDependentAtomString(nsGkAtoms::result),
|
||||
nsGkAtoms::transformiix, namespaceID);
|
||||
|
||||
uint32_t j = 0;
|
||||
#ifdef DEBUG
|
||||
// Keep track of the location of the current documentElement, if there is
|
||||
// one, so we can verify later
|
||||
uint32_t rootLocation = 0;
|
||||
uint32_t j = 0, rootLocation = 0;
|
||||
#endif
|
||||
for (nsCOMPtr<nsIContent> childContent = mDocument->GetFirstChild();
|
||||
childContent; childContent = childContent->GetNextSibling()) {
|
||||
@ -645,11 +644,11 @@ txMozillaXMLOutput::createTxWrapper()
|
||||
// This is needed for cases when there is no existing
|
||||
// documentElement in the document.
|
||||
rootLocation = std::max(rootLocation, j + 1);
|
||||
#endif
|
||||
++j;
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
mDocument->RemoveChildAt_Deprecated(j, true);
|
||||
mDocument->RemoveChildNode(childContent, true);
|
||||
|
||||
rv = wrapper->AppendChildTo(childContent, true);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
@ -754,30 +754,6 @@ GLContext::InitWithPrefixImpl(const char* prefix, bool trygl)
|
||||
MarkUnsupported(GLFeature::depth_texture);
|
||||
}
|
||||
#endif
|
||||
if (IsSupported(GLFeature::frag_color_float)) {
|
||||
float was[4] = {};
|
||||
fGetFloatv(LOCAL_GL_COLOR_CLEAR_VALUE, was);
|
||||
|
||||
const float test[4] = {-1.0, 0, 2.0, 255.0};
|
||||
fClearColor(test[0], test[1], test[2], test[3]);
|
||||
|
||||
float now[4] = {};
|
||||
fGetFloatv(LOCAL_GL_COLOR_CLEAR_VALUE, now);
|
||||
|
||||
fClearColor(was[0], was[1], was[2], was[3]);
|
||||
|
||||
const bool unclamped = now[0] == test[0] && now[1] == test[1] &&
|
||||
now[2] == test[2] && now[3] == test[3];
|
||||
if (!unclamped) {
|
||||
printf_stderr("COLOR_CLEAR_VALUE: now{%f,%f,%f,%f} != test{%f,%f,%f,%f}\n",
|
||||
test[0], test[1], test[2], test[3],
|
||||
now[0], now[1], now[2], now[3]);
|
||||
gfxCriticalNote << "GLFeature::frag_color_float failed support probe,"
|
||||
<< " disabling. (RENDERER: "
|
||||
<< (const char*)fGetString(LOCAL_GL_RENDERER) << ")";
|
||||
MarkUnsupported(GLFeature::frag_color_float);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (IsExtensionSupported(GLContext::ARB_pixel_buffer_object)) {
|
||||
@ -2071,86 +2047,6 @@ GLContext::AssembleOffscreenFBs(const GLuint colorMSRB,
|
||||
return isComplete;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
GLContext::ClearSafely()
|
||||
{
|
||||
// bug 659349 --- we must be very careful here: clearing a GL framebuffer is nontrivial, relies on a lot of state,
|
||||
// and in the case of the backbuffer of a WebGL context, state is exposed to scripts.
|
||||
//
|
||||
// The code here is taken from WebGLContext::ForceClearFramebufferWithDefaultValues, but I didn't find a good way of
|
||||
// sharing code with it. WebGL's code is somewhat performance-critical as it is typically called on every frame, so
|
||||
// WebGL keeps track of GL state to avoid having to query it everytime, and also tries to only do work for actually
|
||||
// present buffers (e.g. stencil buffer). Doing that here seems like premature optimization,
|
||||
// as ClearSafely() is called only when e.g. a canvas is resized, not on every animation frame.
|
||||
|
||||
realGLboolean scissorTestEnabled;
|
||||
realGLboolean ditherEnabled;
|
||||
realGLboolean colorWriteMask[4];
|
||||
realGLboolean depthWriteMask;
|
||||
GLint stencilWriteMaskFront, stencilWriteMaskBack;
|
||||
GLfloat colorClearValue[4];
|
||||
GLfloat depthClearValue;
|
||||
GLint stencilClearValue;
|
||||
|
||||
// save current GL state
|
||||
fGetBooleanv(LOCAL_GL_SCISSOR_TEST, &scissorTestEnabled);
|
||||
fGetBooleanv(LOCAL_GL_DITHER, &ditherEnabled);
|
||||
fGetBooleanv(LOCAL_GL_COLOR_WRITEMASK, colorWriteMask);
|
||||
fGetBooleanv(LOCAL_GL_DEPTH_WRITEMASK, &depthWriteMask);
|
||||
fGetIntegerv(LOCAL_GL_STENCIL_WRITEMASK, &stencilWriteMaskFront);
|
||||
fGetIntegerv(LOCAL_GL_STENCIL_BACK_WRITEMASK, &stencilWriteMaskBack);
|
||||
fGetFloatv(LOCAL_GL_COLOR_CLEAR_VALUE, colorClearValue);
|
||||
fGetFloatv(LOCAL_GL_DEPTH_CLEAR_VALUE, &depthClearValue);
|
||||
fGetIntegerv(LOCAL_GL_STENCIL_CLEAR_VALUE, &stencilClearValue);
|
||||
|
||||
// prepare GL state for clearing
|
||||
fDisable(LOCAL_GL_SCISSOR_TEST);
|
||||
fDisable(LOCAL_GL_DITHER);
|
||||
|
||||
fColorMask(1, 1, 1, 1);
|
||||
fClearColor(0.f, 0.f, 0.f, 0.f);
|
||||
|
||||
fDepthMask(1);
|
||||
fClearDepth(1.0f);
|
||||
|
||||
fStencilMask(0xffffffff);
|
||||
fClearStencil(0);
|
||||
|
||||
// do clear
|
||||
fClear(LOCAL_GL_COLOR_BUFFER_BIT |
|
||||
LOCAL_GL_DEPTH_BUFFER_BIT |
|
||||
LOCAL_GL_STENCIL_BUFFER_BIT);
|
||||
|
||||
// restore GL state after clearing
|
||||
fColorMask(colorWriteMask[0],
|
||||
colorWriteMask[1],
|
||||
colorWriteMask[2],
|
||||
colorWriteMask[3]);
|
||||
fClearColor(colorClearValue[0],
|
||||
colorClearValue[1],
|
||||
colorClearValue[2],
|
||||
colorClearValue[3]);
|
||||
|
||||
fDepthMask(depthWriteMask);
|
||||
fClearDepth(depthClearValue);
|
||||
|
||||
fStencilMaskSeparate(LOCAL_GL_FRONT, stencilWriteMaskFront);
|
||||
fStencilMaskSeparate(LOCAL_GL_BACK, stencilWriteMaskBack);
|
||||
fClearStencil(stencilClearValue);
|
||||
|
||||
if (ditherEnabled)
|
||||
fEnable(LOCAL_GL_DITHER);
|
||||
else
|
||||
fDisable(LOCAL_GL_DITHER);
|
||||
|
||||
if (scissorTestEnabled)
|
||||
fEnable(LOCAL_GL_SCISSOR_TEST);
|
||||
else
|
||||
fDisable(LOCAL_GL_SCISSOR_TEST);
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
GLContext::MarkDestroyed()
|
||||
{
|
||||
|
@ -3576,12 +3576,6 @@ public:
|
||||
return mScreen.get();
|
||||
}
|
||||
|
||||
/* Clear to transparent black, with 0 depth and stencil,
|
||||
* while preserving current ClearColor etc. values.
|
||||
* Useful for resizing offscreen buffers.
|
||||
*/
|
||||
void ClearSafely();
|
||||
|
||||
bool WorkAroundDriverBugs() const { return mWorkAroundDriverBugs; }
|
||||
|
||||
bool IsDrawingToDefaultFramebuffer();
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "mozilla/ipc/CrashReporterHost.h"
|
||||
#include "mozilla/layers/LayerTreeOwnerTracker.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/HangDetails.h"
|
||||
#include "nsIObserverService.h"
|
||||
|
||||
#ifdef MOZ_GECKO_PROFILER
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "mozilla/layers/MemoryReportingMLGPU.h"
|
||||
#include "mozilla/webrender/RenderThread.h"
|
||||
#include "mozilla/webrender/WebRenderAPI.h"
|
||||
#include "mozilla/HangDetails.h"
|
||||
#include "nsDebugImpl.h"
|
||||
#include "nsThreadManager.h"
|
||||
#include "prenv.h"
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
include GraphicsMessages;
|
||||
include MemoryReportTypes;
|
||||
include HangTypes;
|
||||
include protocol PCompositorManager;
|
||||
include protocol PImageBridge;
|
||||
include protocol PProfiler;
|
||||
@ -23,7 +24,6 @@ using mozilla::Telemetry::ChildEventData from "mozilla/TelemetryComms.h";
|
||||
using mozilla::Telemetry::DiscardedData from "mozilla/TelemetryComms.h";
|
||||
using mozilla::gfx::Feature from "gfxFeature.h";
|
||||
using mozilla::gfx::Fallback from "gfxFallback.h";
|
||||
using mozilla::HangDetails from "mozilla/HangDetails.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
@ -55,6 +55,10 @@ public:
|
||||
layers::AutoCompleteTask complete(mTask);
|
||||
|
||||
UniquePtr<RenderCompositor> compositor = RenderCompositor::Create(Move(mCompositorWidget));
|
||||
if (!compositor) {
|
||||
// RenderCompositor::Create puts a message into gfxCriticalNote if it is nullptr
|
||||
return;
|
||||
}
|
||||
|
||||
*mUseANGLE = compositor->UseANGLE();
|
||||
|
||||
|
@ -306,6 +306,8 @@ private:
|
||||
bool aStartAtTop);
|
||||
#endif // NS_PRINTING
|
||||
|
||||
void ReturnToGalleyPresentation();
|
||||
|
||||
// Whether we should attach to the top level widget. This is true if we
|
||||
// are sharing/recycling a single base widget and not creating multiple
|
||||
// child widgets.
|
||||
@ -356,7 +358,7 @@ protected:
|
||||
int mMinFontSize;
|
||||
|
||||
int16_t mNumURLStarts;
|
||||
int16_t mDestroyRefCount; // a second "refcount" for the document viewer's "destroy"
|
||||
int16_t mDestroyBlockedCount;
|
||||
|
||||
unsigned mStopped : 1;
|
||||
unsigned mLoaded : 1;
|
||||
@ -502,7 +504,7 @@ nsDocumentViewer::nsDocumentViewer()
|
||||
mOverrideDPPX(0.0),
|
||||
mMinFontSize(0),
|
||||
mNumURLStarts(0),
|
||||
mDestroyRefCount(0),
|
||||
mDestroyBlockedCount(0),
|
||||
mStopped(false),
|
||||
mLoaded(false),
|
||||
mDeferredWindowClose(false),
|
||||
@ -554,7 +556,7 @@ nsDocumentViewer::~nsDocumentViewer()
|
||||
mPrintJob = nullptr;
|
||||
}
|
||||
|
||||
MOZ_RELEASE_ASSERT(mDestroyRefCount == 0);
|
||||
MOZ_RELEASE_ASSERT(mDestroyBlockedCount == 0);
|
||||
NS_ASSERTION(!mPresShell && !mPresContext,
|
||||
"User did not call nsIContentViewer::Destroy");
|
||||
if (mPresShell || mPresContext) {
|
||||
@ -1653,6 +1655,14 @@ nsDocumentViewer::Destroy()
|
||||
{
|
||||
NS_ASSERTION(mDocument, "No document in Destroy()!");
|
||||
|
||||
// Don't let the document get unloaded while we are printing.
|
||||
// this could happen if we hit the back button during printing.
|
||||
// We also keep the viewer from being cached in session history, since
|
||||
// we require all documents there to be sanitized.
|
||||
if (mDestroyBlockedCount != 0) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef NS_PRINTING
|
||||
// Here is where we check to see if the document was still being prepared
|
||||
// for printing when it was asked to be destroy from someone externally
|
||||
@ -1670,14 +1680,6 @@ nsDocumentViewer::Destroy()
|
||||
mAutoBeforeAndAfterPrint = nullptr;
|
||||
#endif
|
||||
|
||||
// Don't let the document get unloaded while we are printing.
|
||||
// this could happen if we hit the back button during printing.
|
||||
// We also keep the viewer from being cached in session history, since
|
||||
// we require all documents there to be sanitized.
|
||||
if (mDestroyRefCount != 0) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// If we were told to put ourselves into session history instead of destroy
|
||||
// the presentation, do that now.
|
||||
if (mSHEntry) {
|
||||
@ -4466,15 +4468,15 @@ nsDocumentViewer::SetIsPrintPreview(bool aIsPrintPreview)
|
||||
|
||||
//------------------------------------------------------------
|
||||
void
|
||||
nsDocumentViewer::IncrementDestroyRefCount()
|
||||
nsDocumentViewer::IncrementDestroyBlockedCount()
|
||||
{
|
||||
++mDestroyRefCount;
|
||||
++mDestroyBlockedCount;
|
||||
}
|
||||
|
||||
void
|
||||
nsDocumentViewer::DecrementDestroyRefCount()
|
||||
nsDocumentViewer::DecrementDestroyBlockedCount()
|
||||
{
|
||||
--mDestroyRefCount;
|
||||
--mDestroyBlockedCount;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------
|
||||
|
@ -41,10 +41,13 @@ public:
|
||||
// Callers should call EndUpdate() on it when ready to use.
|
||||
virtual mozilla::StyleSetHandle CreateStyleSet(nsIDocument* aDocument) = 0;
|
||||
|
||||
virtual void IncrementDestroyRefCount() = 0;
|
||||
virtual void DecrementDestroyRefCount() = 0;
|
||||
|
||||
virtual void ReturnToGalleyPresentation() = 0;
|
||||
/**
|
||||
* This is used by nsPagePrintTimer to make nsDocumentViewer::Destroy()
|
||||
* a no-op until printing is finished. That prevents the nsDocumentViewer
|
||||
* and its document, presshell and prescontext from going away.
|
||||
*/
|
||||
virtual void IncrementDestroyBlockedCount() = 0;
|
||||
virtual void DecrementDestroyBlockedCount() = 0;
|
||||
|
||||
virtual void OnDonePrinting() = 0;
|
||||
|
||||
@ -76,9 +79,8 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocumentViewerPrint,
|
||||
void SetIsPrintPreview(bool aIsPrintPreview) override; \
|
||||
bool GetIsPrintPreview() override; \
|
||||
mozilla::StyleSetHandle CreateStyleSet(nsIDocument* aDocument) override; \
|
||||
void IncrementDestroyRefCount() override; \
|
||||
void DecrementDestroyRefCount() override; \
|
||||
void ReturnToGalleyPresentation() override; \
|
||||
void IncrementDestroyBlockedCount() override; \
|
||||
void DecrementDestroyBlockedCount() override; \
|
||||
void OnDonePrinting() override; \
|
||||
bool IsInitializedForPrintPreview() override; \
|
||||
void InitializeForPrintPreview() override; \
|
||||
|
@ -306,13 +306,13 @@ void UpdateASR(nsDisplayItem* aItem,
|
||||
*
|
||||
* The basic algorithm is:
|
||||
*
|
||||
* For-each item in the new list:
|
||||
* For-each item i in the new list:
|
||||
* If the item has a matching item in the old list:
|
||||
* Remove items from the bottom of the old list until we reach the matching item:
|
||||
* Remove items from the start of the old list up until we reach an item that also exists in the new list (leaving the matched item in place):
|
||||
* Add valid items to the merged list, destroy invalid items.
|
||||
* Destroy the matching item from the old list.
|
||||
* Add the item from the new list into the merged list.
|
||||
* Add all remaining valid items from the old list into the merged list.
|
||||
* Add i into the merged list.
|
||||
* If the start of the old list matches i, remove and destroy it, otherwise mark the old version of i as used.
|
||||
* Add all remaining valid items from the old list into the merged list, skipping over (and destroying) any that are marked as used.
|
||||
*
|
||||
* If any item has a child display list, then we recurse into the merge
|
||||
* algorithm once we match up the new/old versions (if present).
|
||||
@ -320,7 +320,7 @@ void UpdateASR(nsDisplayItem* aItem,
|
||||
* Example 1:
|
||||
*
|
||||
* Old List: A,B,C,D
|
||||
* New List: A,D
|
||||
* Modified List: A,D
|
||||
* Invalidations: C,D
|
||||
*
|
||||
* We first match the A items, and add the new one to the merged list.
|
||||
@ -330,10 +330,23 @@ void UpdateASR(nsDisplayItem* aItem,
|
||||
*
|
||||
* Merged List: A,B,D
|
||||
*
|
||||
* Example 2:
|
||||
* Example 2 (layout/reftests/retained-dl-zindex-1.html):
|
||||
*
|
||||
* Old List: A, B
|
||||
* New List, B, A
|
||||
* Modified List: B, A
|
||||
* Invalidations: A
|
||||
*
|
||||
* In this example A has been explicitly moved to the back.
|
||||
*
|
||||
* We match the B items, but don't copy A since it's invalid, and then add the
|
||||
* new B into the merged list. We then add A, and we're done.
|
||||
*
|
||||
* Merged List: B, A
|
||||
*
|
||||
* Example 3:
|
||||
*
|
||||
* Old List: A, B
|
||||
* Modified List: B, A
|
||||
* Invalidations: -
|
||||
*
|
||||
* This can happen because a prior merge might have changed the ordering
|
||||
@ -343,6 +356,28 @@ void UpdateASR(nsDisplayItem* aItem,
|
||||
* and then add the new B into the merged list. We then add A, and we're done.
|
||||
*
|
||||
* Merged List: B, A
|
||||
*
|
||||
* Example 4 (layout/reftests/retained-dl-zindex-2.html):
|
||||
*
|
||||
* Element A has two elements covering it (B and C), that don't intersect each
|
||||
* other. We then move C to the back.
|
||||
*
|
||||
* The correct initial ordering has B and C after A, in any order.
|
||||
*
|
||||
* Old List: A, B, C
|
||||
* Modified List: C, A
|
||||
* Invalidations: C
|
||||
*
|
||||
* We match the C items, but don't add anything from the old list because A is present
|
||||
* in both lists. We add C to the merged list, and mark the old version of C as reused.
|
||||
*
|
||||
* We then match A, add the new version the merged list and delete the old version.
|
||||
*
|
||||
* We then process the remainder of the old list, B is added (since it is valid,
|
||||
* and hasn't been mark as reused), C is destroyed since it's marked as reused and
|
||||
* is already present in the merged list.
|
||||
*
|
||||
* Merged List: C, A, B
|
||||
*/
|
||||
void
|
||||
RetainedDisplayListBuilder::MergeDisplayLists(nsDisplayList* aNewList,
|
||||
@ -407,18 +442,17 @@ RetainedDisplayListBuilder::MergeDisplayLists(nsDisplayList* aNewList,
|
||||
// The new item has a matching counterpart in the old list that we haven't yet reached,
|
||||
// so copy all valid items from the old list into the merged list until we get to the
|
||||
// matched item.
|
||||
if (!oldItem->IsReused()) {
|
||||
nsDisplayItem* old = nullptr;
|
||||
while ((old = aOldList->RemoveBottom()) && !IsSameItem(newItem, old)) {
|
||||
while ((old = aOldList->GetBottom()) && old != oldItem) {
|
||||
if (IsAnyAncestorModified(old->FrameForInvalidation())) {
|
||||
// The old item is invalid, discard it.
|
||||
oldListLookup.Remove({ old->Frame(), old->GetPerFrameKey() });
|
||||
aOldList->RemoveBottom();
|
||||
old->Destroy(&mBuilder);
|
||||
} else if (newListLookup.Get({ old->Frame(), old->GetPerFrameKey() })) {
|
||||
// The old item is also in the new list, but we haven't got to it yet.
|
||||
// Mark that we've found it, and we'll deal with it when we get to the new
|
||||
// entry.
|
||||
old->SetReused(true);
|
||||
// This old item is also in the new list, but we haven't got to it yet.
|
||||
// Stop now, and we'll deal with it when we get to the new entry.
|
||||
break;
|
||||
} else {
|
||||
// Recurse into the child list (without a matching new list) to
|
||||
// ensure that we find and remove any invalidated items.
|
||||
@ -430,16 +464,27 @@ RetainedDisplayListBuilder::MergeDisplayLists(nsDisplayList* aNewList,
|
||||
UpdateASR(old, containerASRForChildren);
|
||||
old->UpdateBounds(&mBuilder);
|
||||
}
|
||||
aOldList->RemoveBottom();
|
||||
ReuseItem(old);
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(old && IsSameItem(newItem, old));
|
||||
MOZ_ASSERT(old == oldItem);
|
||||
bool destroy = false;
|
||||
if (old == oldItem) {
|
||||
// If we advanced the old list until the matching item then we can pop
|
||||
// the matching item off the old list and make sure we clean it up.
|
||||
aOldList->RemoveBottom();
|
||||
destroy = true;
|
||||
} else {
|
||||
// If we didn't get to the matching item, then mark the old item
|
||||
// as being reused (since we're adding the new version to the new
|
||||
// list now) so that we don't add it twice at the end.
|
||||
oldItem->SetReused(true);
|
||||
}
|
||||
|
||||
// Recursively merge any child lists, destroy the old item and add
|
||||
// the new one to the list.
|
||||
if (oldItem->GetType() == DisplayItemType::TYPE_LAYER_EVENT_REGIONS &&
|
||||
if (!destroy &&
|
||||
oldItem->GetType() == DisplayItemType::TYPE_LAYER_EVENT_REGIONS &&
|
||||
!IsAnyAncestorModified(oldItem->FrameForInvalidation())) {
|
||||
// Event regions items don't have anything interesting other than
|
||||
// the lists of regions and frames, so we have no need to use the
|
||||
@ -459,7 +504,9 @@ RetainedDisplayListBuilder::MergeDisplayLists(nsDisplayList* aNewList,
|
||||
newItem->UpdateBounds(&mBuilder);
|
||||
}
|
||||
|
||||
if (destroy) {
|
||||
oldItem->Destroy(&mBuilder);
|
||||
}
|
||||
UseItem(newItem);
|
||||
}
|
||||
} else {
|
||||
@ -471,7 +518,8 @@ RetainedDisplayListBuilder::MergeDisplayLists(nsDisplayList* aNewList,
|
||||
|
||||
// Reuse the remaining valid items from the old display list.
|
||||
while (nsDisplayItem* old = aOldList->RemoveBottom()) {
|
||||
if (!IsAnyAncestorModified(old->FrameForInvalidation())) {
|
||||
if (!IsAnyAncestorModified(old->FrameForInvalidation()) &&
|
||||
!old->IsReused()) {
|
||||
if (old->GetChildren()) {
|
||||
// We are calling MergeDisplayLists() to ensure that the display items
|
||||
// with modified or deleted children will be correctly handled.
|
||||
@ -550,7 +598,7 @@ SubDocEnumCb(nsIDocument* aDocument, void* aData)
|
||||
MOZ_ASSERT(subDocView);
|
||||
|
||||
nsIFrame* subDocFrame = subDocView->GetFrame();
|
||||
MOZ_ASSERT(subDocFrame);
|
||||
if (subDocFrame) {
|
||||
nsSubDocumentFrame* subdocumentFrame = do_QueryFrame(subDocFrame);
|
||||
MOZ_ASSERT(subdocumentFrame);
|
||||
|
||||
@ -562,6 +610,7 @@ SubDocEnumCb(nsIDocument* aDocument, void* aData)
|
||||
TakeAndAddModifiedFramesFromRootFrame(data->modifiedFrames, rootFrame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
aDocument->EnumerateSubDocuments(SubDocEnumCb, aData);
|
||||
return true;
|
||||
|
@ -17,8 +17,8 @@ NS_IMPL_ISUPPORTS_INHERITED(nsPagePrintTimer, mozilla::Runnable, nsITimerCallbac
|
||||
|
||||
nsPagePrintTimer::~nsPagePrintTimer()
|
||||
{
|
||||
// This matches the IncrementDestroyRefCount call in the constructor.
|
||||
mDocViewerPrint->DecrementDestroyRefCount();
|
||||
// This matches the IncrementDestroyBlockedCount call in the constructor.
|
||||
mDocViewerPrint->DecrementDestroyBlockedCount();
|
||||
}
|
||||
|
||||
nsresult
|
||||
@ -140,7 +140,6 @@ nsPagePrintTimer::Notify(nsITimer *timer)
|
||||
}
|
||||
}
|
||||
|
||||
if (mDocViewerPrint) {
|
||||
bool donePrePrint = true;
|
||||
// Don't start to pre-print if we're waiting on the parent still.
|
||||
if (mPrintJob && !mWaitingForRemotePrint) {
|
||||
@ -157,7 +156,6 @@ nsPagePrintTimer::Notify(nsITimer *timer)
|
||||
StartWatchDogTimer();
|
||||
}
|
||||
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "nsIDocumentViewerPrint.h"
|
||||
#include "nsPrintObject.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/OwningNonNull.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
class nsPrintJob;
|
||||
@ -33,7 +34,7 @@ public:
|
||||
uint32_t aDelay)
|
||||
: Runnable("nsPagePrintTimer")
|
||||
, mPrintJob(aPrintJob)
|
||||
, mDocViewerPrint(aDocViewerPrint)
|
||||
, mDocViewerPrint(*aDocViewerPrint)
|
||||
, mDocument(aDocument)
|
||||
, mDelay(aDelay)
|
||||
, mFiringCount(0)
|
||||
@ -41,8 +42,8 @@ public:
|
||||
, mWatchDogCount(0)
|
||||
, mDone(false)
|
||||
{
|
||||
MOZ_ASSERT(aDocument);
|
||||
mDocViewerPrint->IncrementDestroyRefCount();
|
||||
MOZ_ASSERT(aDocViewerPrint && aDocument);
|
||||
mDocViewerPrint->IncrementDestroyBlockedCount();
|
||||
}
|
||||
|
||||
NS_DECL_NSITIMERCALLBACK
|
||||
@ -71,7 +72,7 @@ private:
|
||||
void Fail();
|
||||
|
||||
nsPrintJob* mPrintJob;
|
||||
nsCOMPtr<nsIDocumentViewerPrint> mDocViewerPrint;
|
||||
const mozilla::OwningNonNull<nsIDocumentViewerPrint> mDocViewerPrint;
|
||||
nsCOMPtr<nsIDocument> mDocument;
|
||||
nsCOMPtr<nsITimer> mTimer;
|
||||
nsCOMPtr<nsITimer> mWatchDogTimer;
|
||||
|
@ -38,7 +38,6 @@ nsPrintData::nsPrintData(ePrintDataType aType)
|
||||
, mNumPrintablePages(0)
|
||||
, mNumPagesPrinted(0)
|
||||
, mShrinkRatio(1.0)
|
||||
, mOrigDCScale(1.0)
|
||||
, mPPEventListeners(nullptr)
|
||||
{
|
||||
nsCOMPtr<nsIStringBundle> brandBundle;
|
||||
|
@ -82,7 +82,6 @@ public:
|
||||
int32_t mNumPrintablePages;
|
||||
int32_t mNumPagesPrinted;
|
||||
float mShrinkRatio;
|
||||
float mOrigDCScale;
|
||||
|
||||
nsCOMPtr<nsIPrintSettings> mPrintSettings;
|
||||
nsPrintPreviewListener* mPPEventListeners;
|
||||
|
@ -8,6 +8,8 @@ skip-if(!retainedDisplayList) == retained-dl-scroll-out-of-view-1.html retained-
|
||||
skip-if(!retainedDisplayList) == retained-dl-displayport-1.html retained-dl-displayport-1-ref.html
|
||||
skip-if(!retainedDisplayList) == retained-dl-prerender-transform-1.html retained-dl-prerender-transform-1-ref.html
|
||||
== retained-dl-wrap-list.html retained-dl-wrap-list-ref.html
|
||||
== retained-dl-zindex-1.html retained-dl-zindex-1-ref.html
|
||||
== retained-dl-zindex-2.html retained-dl-zindex-2-ref.html
|
||||
fuzzy(1,235200) == 1413073.html 1413073-ref.html
|
||||
== 1416291.html 1416291-ref.html
|
||||
== 1417601-1.html 1417601-1-ref.html
|
||||
|
27
layout/reftests/display-list/retained-dl-zindex-1-ref.html
Normal file
27
layout/reftests/display-list/retained-dl-zindex-1-ref.html
Normal file
@ -0,0 +1,27 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
div {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
position:relative;
|
||||
will-change: transform;
|
||||
}
|
||||
#one {
|
||||
top:120px;
|
||||
background-color:blue;
|
||||
}
|
||||
#two {
|
||||
top: -200px;
|
||||
left: 100px;
|
||||
background-color:green;
|
||||
z-index: -1;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="one"></div>
|
||||
<div id="two"></div>
|
||||
</body>
|
||||
</html>
|
35
layout/reftests/display-list/retained-dl-zindex-1.html
Normal file
35
layout/reftests/display-list/retained-dl-zindex-1.html
Normal file
@ -0,0 +1,35 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="reftest-wait">
|
||||
<head>
|
||||
<style>
|
||||
div {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
position:relative;
|
||||
will-change: transform;
|
||||
}
|
||||
#one {
|
||||
top:120px;
|
||||
background-color:blue;
|
||||
}
|
||||
#two {
|
||||
top: -200px;
|
||||
left: 100px;
|
||||
background-color:green;
|
||||
z-index: 1;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="one"></div>
|
||||
<div id="two"></div>
|
||||
</body>
|
||||
<script>
|
||||
function doTest() {
|
||||
document.getElementById("two").style.zIndex = -1;
|
||||
document.documentElement.removeAttribute("class");
|
||||
}
|
||||
|
||||
window.addEventListener("MozReftestInvalidate", doTest);
|
||||
</script>
|
||||
</html>
|
33
layout/reftests/display-list/retained-dl-zindex-2-ref.html
Normal file
33
layout/reftests/display-list/retained-dl-zindex-2-ref.html
Normal file
@ -0,0 +1,33 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
div {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
position:relative;
|
||||
will-change:transform;
|
||||
}
|
||||
#one {
|
||||
top:120px;
|
||||
background-color:blue;
|
||||
}
|
||||
#two {
|
||||
top: -200px;
|
||||
left: 100px;
|
||||
background-color:green;
|
||||
}
|
||||
#three {
|
||||
top: -180px;
|
||||
left: 100px;
|
||||
background-color:red;
|
||||
z-index: -1;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="one"></div>
|
||||
<div id="two"></div>
|
||||
<div id="three"></div>
|
||||
</body>
|
||||
</html>
|
41
layout/reftests/display-list/retained-dl-zindex-2.html
Normal file
41
layout/reftests/display-list/retained-dl-zindex-2.html
Normal file
@ -0,0 +1,41 @@
|
||||
<!DOCTYPE html>
|
||||
<html class="reftest-wait">
|
||||
<head>
|
||||
<style>
|
||||
div {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
position:relative;
|
||||
will-change:transform;
|
||||
}
|
||||
#one {
|
||||
top:120px;
|
||||
background-color:blue;
|
||||
}
|
||||
#two {
|
||||
top: -200px;
|
||||
left: 100px;
|
||||
background-color:green;
|
||||
}
|
||||
#three {
|
||||
top: -180px;
|
||||
left: 100px;
|
||||
background-color:red;
|
||||
z-index: 1;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="one"></div>
|
||||
<div id="two"></div>
|
||||
<div id="three"></div>
|
||||
</body>
|
||||
<script>
|
||||
function doTest() {
|
||||
document.getElementById("three").style.zIndex = -1;
|
||||
document.documentElement.removeAttribute("class");
|
||||
}
|
||||
|
||||
window.addEventListener("MozReftestInvalidate", doTest);
|
||||
</script>
|
||||
</html>
|
@ -631,8 +631,12 @@ pref("media.cubeb.logging_level", "");
|
||||
|
||||
#ifdef NIGHTLY_BUILD
|
||||
// Cubeb sandbox (remoting) control
|
||||
#ifdef XP_MACOSX
|
||||
pref("media.cubeb.sandbox", false);
|
||||
#else
|
||||
pref("media.cubeb.sandbox", true);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Set to true to force demux/decode warnings to be treated as errors.
|
||||
pref("media.playback.warnings-as-errors", false);
|
||||
|
@ -79,24 +79,27 @@ jobs:
|
||||
|
||||
# NOTE: Windows Searchfox jobs aren't working quite yet (bug 1418415).
|
||||
|
||||
win32-searchfox/debug:
|
||||
description: "Win32 Searchfox Debug (clang-cl)"
|
||||
win64-searchfox/debug:
|
||||
description: "Win64 Searchfox Debug (clang-cl)"
|
||||
index:
|
||||
product: firefox
|
||||
job-name: win32-searchfox-debug
|
||||
job-name: win64-searchfox-debug
|
||||
treeherder:
|
||||
platform: windows2012-32/debug
|
||||
platform: windows2012-64/debug
|
||||
worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
|
||||
worker:
|
||||
max-run-time: 7200
|
||||
env:
|
||||
TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/win32/releng.manifest"
|
||||
TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/win64/releng.manifest"
|
||||
run:
|
||||
using: mozharness
|
||||
options: [append-env-variables-from-configs]
|
||||
script: mozharness/scripts/fx_desktop_build.py
|
||||
config:
|
||||
- builds/releng_base_firefox.py
|
||||
- builds/taskcluster_firefox_win32_clang_searchfox_debug.py
|
||||
- builds/taskcluster_base_windows.py
|
||||
- builds/taskcluster_base_win64.py
|
||||
- builds/taskcluster_sub_win64/searchfox_debug.py
|
||||
toolchains:
|
||||
- win32-clang-cl
|
||||
- win32-rust
|
||||
- win64-clang-cl
|
||||
- win64-rust
|
||||
|
@ -166,6 +166,8 @@ def register_callback_action(name, title, symbol, description, order=10000,
|
||||
def register_callback(cb):
|
||||
assert isinstance(cb, FunctionType), 'callback must be a function'
|
||||
assert isinstance(symbol, basestring), 'symbol must be a string'
|
||||
# Allow for json-e > 25 chars in the symbol.
|
||||
if '$' not in symbol:
|
||||
assert 1 <= len(symbol) <= 25, 'symbol must be between 1 and 25 characters'
|
||||
assert not mem['registered'], 'register_callback_action must be used as decorator'
|
||||
assert cb.__name__ not in callbacks, 'callback name {} is not unique'.format(cb.__name__)
|
||||
|
@ -89,7 +89,7 @@ def is_release_promotion_available(parameters):
|
||||
@register_callback_action(
|
||||
name='release-promotion',
|
||||
title='Release Promotion',
|
||||
symbol='Relpro',
|
||||
symbol='${input.release_promotion_flavor}',
|
||||
description="Promote a release.",
|
||||
order=10000,
|
||||
context=[],
|
||||
|
@ -120,6 +120,31 @@ class Parameters(ReadOnlyDict):
|
||||
except KeyError:
|
||||
raise KeyError("taskgraph parameter {!r} not found".format(k))
|
||||
|
||||
def is_try(self):
|
||||
"""
|
||||
Determine whether this graph is being built on a try project.
|
||||
"""
|
||||
return 'try' in self['project']
|
||||
|
||||
def file_url(self, path):
|
||||
"""
|
||||
Determine the VCS URL for viewing a file in the tree, suitable for
|
||||
viewing by a human.
|
||||
|
||||
:param basestring path: The path, relative to the root of the repository.
|
||||
|
||||
:return basestring: The URL displaying the given path.
|
||||
"""
|
||||
if path.startswith('comm/'):
|
||||
path = path[len('comm/'):]
|
||||
repo = self['comm_head_repository']
|
||||
rev = self['comm_head_rev']
|
||||
else:
|
||||
repo = self['head_repository']
|
||||
rev = self['head_rev']
|
||||
|
||||
return '{}/file/{}/{}'.format(repo, rev, path)
|
||||
|
||||
|
||||
def load_parameters_file(filename, strict=True):
|
||||
"""
|
||||
|
@ -113,7 +113,7 @@ def fill_template(config, tasks):
|
||||
'label': 'build-docker-image-' + image_name,
|
||||
'description': description,
|
||||
'attributes': {'image_name': image_name},
|
||||
'expires-after': '28 days' if config.params['project'] == 'try' else '1 year',
|
||||
'expires-after': '28 days' if config.params.is_try() else '1 year',
|
||||
'scopes': ['secrets:get:project/taskcluster/gecko/hgfingerprint'],
|
||||
'treeherder': {
|
||||
'symbol': job_symbol,
|
||||
|
@ -174,7 +174,7 @@ def mozharness_on_docker_worker_setup(config, job, taskdesc):
|
||||
if 'job-script' in run:
|
||||
env['JOB_SCRIPT'] = run['job-script']
|
||||
|
||||
if 'try' in config.params['project']:
|
||||
if config.params.is_try():
|
||||
env['TRY_COMMIT_MSG'] = config.params['message']
|
||||
|
||||
# if we're not keeping artifacts, set some env variables to empty values
|
||||
@ -257,7 +257,7 @@ def mozharness_on_generic_worker(config, job, taskdesc):
|
||||
# to commands. Setting a variable to empty in a batch file unsets, so if
|
||||
# there is no `TRY_COMMIT_MESSAGE`, pass a space instead, so that
|
||||
# mozharness doesn't try to find the commit message on its own.
|
||||
if 'try' in config.params['project']:
|
||||
if config.params.is_try():
|
||||
env['TRY_COMMIT_MSG'] = config.params['message'] or 'no commit message'
|
||||
|
||||
if not job['attributes']['build_platform'].startswith('win'):
|
||||
|
@ -147,7 +147,7 @@ def mozharness_test_on_docker(config, job, taskdesc):
|
||||
if 'actions' in mozharness:
|
||||
env['MOZHARNESS_ACTIONS'] = ' '.join(mozharness['actions'])
|
||||
|
||||
if 'try' in config.params['project']:
|
||||
if config.params.is_try():
|
||||
env['TRY_COMMIT_MSG'] = config.params['message']
|
||||
|
||||
# handle some of the mozharness-specific options
|
||||
@ -315,7 +315,7 @@ def mozharness_test_on_generic_worker(config, job, taskdesc):
|
||||
if isinstance(c, basestring) and c.startswith('--test-suite'):
|
||||
mh_command[i] += suffix
|
||||
|
||||
if 'try' in config.params['project']:
|
||||
if config.params.is_try():
|
||||
env['TRY_COMMIT_MSG'] = config.params['message']
|
||||
|
||||
worker['mounts'] = [{
|
||||
|
@ -875,7 +875,7 @@ def build_docker_worker_payload(config, task, task_def):
|
||||
else:
|
||||
suffix = ''
|
||||
|
||||
skip_untrusted = config.params['project'] == 'try' or level == 1
|
||||
skip_untrusted = config.params.is_try() or level == 1
|
||||
|
||||
for cache in worker['caches']:
|
||||
# Some caches aren't enabled in environments where we can't
|
||||
@ -1454,10 +1454,7 @@ def build_task(config, tasks):
|
||||
'description': task['description'],
|
||||
'name': task['label'],
|
||||
'owner': config.params['owner'],
|
||||
'source': '{}/file/{}/{}'.format(
|
||||
config.params['head_repository'],
|
||||
config.params['head_rev'],
|
||||
config.path),
|
||||
'source': config.params.file_url(config.path),
|
||||
},
|
||||
'extra': extra,
|
||||
'tags': tags,
|
||||
|
@ -28,7 +28,7 @@ class EasyServer(ThreadingMixIn, BaseHTTPServer.HTTPServer):
|
||||
acceptable_errors = (errno.EPIPE, errno.ECONNABORTED)
|
||||
|
||||
def handle_error(self, request, client_address):
|
||||
error = sys.exc_value
|
||||
error = sys.exc_info()[1]
|
||||
|
||||
if ((isinstance(error, socket.error) and
|
||||
isinstance(error.args, tuple) and
|
||||
|
@ -13,6 +13,7 @@ config = {
|
||||
'--upload', 'mozilla/geckoview',
|
||||
'--upload-branch', 'gh-pages/javadoc/{project}',
|
||||
'--upload-message', 'Update {project} javadoc to rev {revision}',
|
||||
'--variant', 'officialWithGeckoBinariesNoMinApiRelease',
|
||||
],
|
||||
],
|
||||
'artifact_flag_build_variant_in_try': None, # There's no artifact equivalent.
|
||||
|
@ -1,76 +0,0 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
config = {
|
||||
#########################################################################
|
||||
######## WINDOWS GENERIC CONFIG KEYS/VAlUES
|
||||
# if you are updating this with custom 32 bit keys/values please add them
|
||||
# below under the '32 bit specific' code block otherwise, update in this
|
||||
# code block and also make sure this is synced between:
|
||||
# - taskcluster_firefox_win32_debug
|
||||
# - taskcluster_firefox_win32_opt
|
||||
# - taskcluster_firefox_win64_debug
|
||||
# - taskcluster_firefox_win64_opt
|
||||
# - taskcluster_firefox_win32_clang
|
||||
# - taskcluster_firefox_win32_clang_debug
|
||||
# - taskcluster_firefox_win64_clang
|
||||
# - taskcluster_firefox_win64_clang_debug
|
||||
|
||||
'default_actions': [
|
||||
'clone-tools',
|
||||
'build',
|
||||
'check-test',
|
||||
],
|
||||
'app_ini_path': '%(obj_dir)s/dist/bin/application.ini',
|
||||
# decides whether we want to use moz_sign_cmd in env
|
||||
'enable_signing': True,
|
||||
'vcs_share_base': os.path.join('y:', os.sep, 'hg-shared'),
|
||||
'tooltool_script': [
|
||||
sys.executable,
|
||||
os.path.join(os.environ['MOZILLABUILD'], 'tooltool.py')
|
||||
],
|
||||
'tooltool_bootstrap': 'setup.sh',
|
||||
'enable_count_ctors': False,
|
||||
'max_build_output_timeout': 60 * 80,
|
||||
'perfherder_extra_options': ['static-analysis'],
|
||||
#########################################################################
|
||||
|
||||
|
||||
#########################################################################
|
||||
###### 32 bit specific ######
|
||||
'base_name': 'WINNT_5.2_%(branch)s',
|
||||
'platform': 'win32',
|
||||
'stage_platform': 'win32-st-an-debug',
|
||||
'debug_build': True,
|
||||
'publish_nightly_en_US_routes': True,
|
||||
'env': {
|
||||
'BINSCOPE': os.path.join(
|
||||
os.environ['ProgramFiles(x86)'], 'Microsoft', 'SDL BinScope', 'BinScope.exe'
|
||||
),
|
||||
'HG_SHARE_BASE_DIR': os.path.join('y:', os.sep, 'hg-shared'),
|
||||
'MOZBUILD_STATE_PATH': os.path.join(os.getcwd(), '.mozbuild'),
|
||||
'MOZ_CRASHREPORTER_NO_REPORT': '1',
|
||||
'MOZ_OBJDIR': '%(abs_obj_dir)s',
|
||||
'PDBSTR_PATH': 'C:/Program Files (x86)/Windows Kits/10/Debuggers/x86/srcsrv/pdbstr.exe',
|
||||
'TINDERBOX_OUTPUT': '1',
|
||||
'TOOLTOOL_CACHE': 'c:/builds/tooltool_cache',
|
||||
'TOOLTOOL_HOME': '/c/builds',
|
||||
'XPCOM_DEBUG_BREAK': 'stack-and-abort',
|
||||
'MSYSTEM': 'MINGW32',
|
||||
# Disable sccache because otherwise we won't index the files that
|
||||
# sccache optimizes away compilation for
|
||||
'SCCACHE_DISABLE': '1',
|
||||
},
|
||||
'upload_env': {
|
||||
'UPLOAD_HOST': 'localhost',
|
||||
'UPLOAD_PATH': os.path.join(os.getcwd(), 'public', 'build'),
|
||||
},
|
||||
"check_test_env": {
|
||||
'MINIDUMP_STACKWALK': '%(abs_tools_dir)s\\breakpad\\win32\\minidump_stackwalk.exe',
|
||||
'MINIDUMP_SAVE_PATH': os.path.join(os.getcwd(), 'public', 'build'),
|
||||
},
|
||||
'mozconfig_platform': 'win32',
|
||||
'mozconfig_variant': 'debug-searchfox',
|
||||
'artifact_flag_build_variant_in_try': None,
|
||||
#########################################################################
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
config = {
|
||||
'perfherder_extra_options': ['static-analysis'],
|
||||
'stage_platform': 'win64-st-an-debug',
|
||||
'debug_build': True,
|
||||
'env': {
|
||||
'XPCOM_DEBUG_BREAK': 'stack-and-abort',
|
||||
# Disable sccache because otherwise we won't index the files that
|
||||
# sccache optimizes away compilation for
|
||||
'SCCACHE_DISABLE': '1',
|
||||
},
|
||||
'mozconfig_variant': 'debug-searchfox',
|
||||
'artifact_flag_build_variant_in_try': None,
|
||||
}
|
@ -7,7 +7,7 @@ support-files =
|
||||
# Synchronous tests like test_alerts.html must come before
|
||||
# asynchronous tests like test_alerts_noobserve.html!
|
||||
[test_alerts.html]
|
||||
skip-if = toolkit == 'android'
|
||||
skip-if = toolkit == 'android' || (os == "win" && debug) # Bug 1407296
|
||||
[test_alerts_noobserve.html]
|
||||
[test_alerts_requireinteraction.html]
|
||||
[test_image.html]
|
||||
|
@ -81,6 +81,7 @@ function runTest() {
|
||||
}
|
||||
}
|
||||
|
||||
SimpleTest.requestCompleteLog(); // Bug 1407296
|
||||
runTest();
|
||||
|
||||
</script>
|
||||
|
@ -26,13 +26,13 @@
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
#include "GeckoProfiler.h"
|
||||
#ifdef MOZ_GECKO_PROFILER
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "ProfilerMarkerPayload.h"
|
||||
#endif
|
||||
#include "nsNetCID.h"
|
||||
#include "HangDetails.h"
|
||||
|
||||
#ifdef MOZ_GECKO_PROFILER
|
||||
#include "ProfilerMarkerPayload.h"
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
// Activate BHR only for one every BHR_BETA_MOD users.
|
||||
@ -218,7 +218,7 @@ public:
|
||||
|
||||
// Report a hang; aManager->mLock IS locked. The hang will be processed
|
||||
// off-main-thread, and will then be submitted back.
|
||||
void ReportHang(PRIntervalTime aHangTime, TimeStamp aHangEndTime);
|
||||
void ReportHang(PRIntervalTime aHangTime);
|
||||
// Report a permanent hang; aManager->mLock IS locked
|
||||
void ReportPermaHang();
|
||||
// Called by BackgroundHangMonitor::NotifyActivity
|
||||
@ -390,7 +390,7 @@ BackgroundHangManager::RunMonitorThread()
|
||||
} else {
|
||||
if (MOZ_LIKELY(interval != currentThread->mHangStart)) {
|
||||
// A hang ended
|
||||
currentThread->ReportHang(intervalNow - currentThread->mHangStart, TimeStamp::Now());
|
||||
currentThread->ReportHang(intervalNow - currentThread->mHangStart);
|
||||
currentThread->mHanging = false;
|
||||
}
|
||||
}
|
||||
@ -468,18 +468,27 @@ BackgroundHangThread::~BackgroundHangThread()
|
||||
}
|
||||
|
||||
void
|
||||
BackgroundHangThread::ReportHang(PRIntervalTime aHangTime, TimeStamp aHangEndTime)
|
||||
BackgroundHangThread::ReportHang(PRIntervalTime aHangTime)
|
||||
{
|
||||
// Recovered from a hang; called on the monitor thread
|
||||
// mManager->mLock IS locked
|
||||
|
||||
HangDetails hangDetails(aHangTime,
|
||||
aHangEndTime,
|
||||
XRE_GetProcessType(),
|
||||
nsTArray<HangAnnotation> annotations;
|
||||
for (auto& annotation : mAnnotations) {
|
||||
HangAnnotation annot(annotation.mName, annotation.mValue);
|
||||
annotations.AppendElement(mozilla::Move(annot));
|
||||
}
|
||||
|
||||
HangDetails hangDetails(
|
||||
aHangTime,
|
||||
nsDependentCString(XRE_ChildProcessTypeToString(XRE_GetProcessType())),
|
||||
VoidString(),
|
||||
mThreadName,
|
||||
mRunnableName,
|
||||
Move(mHangStack),
|
||||
Move(mAnnotations));
|
||||
Move(annotations)
|
||||
);
|
||||
|
||||
// If we have the stream transport service avaliable, we can process the
|
||||
// native stack on it. Otherwise, we are unable to report a native stack, so
|
||||
// we just report without one.
|
||||
@ -492,6 +501,18 @@ BackgroundHangThread::ReportHang(PRIntervalTime aHangTime, TimeStamp aHangEndTim
|
||||
RefPtr<nsHangDetails> hd = new nsHangDetails(Move(hangDetails));
|
||||
hd->Submit();
|
||||
}
|
||||
|
||||
// If the profiler is enabled, add a marker.
|
||||
#ifdef MOZ_GECKO_PROFILER
|
||||
if (profiler_is_active()) {
|
||||
TimeStamp endTime = TimeStamp::Now();
|
||||
TimeStamp startTime = endTime - TimeDuration::FromMilliseconds(aHangTime);
|
||||
profiler_add_marker_for_thread(
|
||||
mStackHelper.GetThreadId(),
|
||||
"BHR-detected hang",
|
||||
MakeUnique<HangMarkerPayload>(startTime, endTime));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
@ -506,7 +527,7 @@ BackgroundHangThread::ReportPermaHang()
|
||||
//
|
||||
// We currently don't look at hang reports outside of nightly, and already
|
||||
// collect native stacks eagerly on nightly, so this should be OK.
|
||||
ReportHang(mMaxTimeout, TimeStamp::Now());
|
||||
ReportHang(mMaxTimeout);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE void
|
||||
|
@ -5,8 +5,9 @@
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/GfxMessageUtils.h" // For ParamTraits<GeckoProcessType>
|
||||
|
||||
#ifdef MOZ_GECKO_PROFILER
|
||||
#include "ProfilerMarkerPayload.h"
|
||||
#include "shared-libraries.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
@ -14,35 +15,35 @@ namespace mozilla {
|
||||
NS_IMETHODIMP
|
||||
nsHangDetails::GetDuration(uint32_t* aDuration)
|
||||
{
|
||||
*aDuration = mDetails.mDuration;
|
||||
*aDuration = mDetails.duration();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHangDetails::GetThread(nsACString& aName)
|
||||
{
|
||||
aName.Assign(mDetails.mThreadName);
|
||||
aName.Assign(mDetails.threadName());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHangDetails::GetRunnableName(nsACString& aRunnableName)
|
||||
{
|
||||
aRunnableName.Assign(mDetails.mRunnableName);
|
||||
aRunnableName.Assign(mDetails.runnableName());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHangDetails::GetProcess(nsACString& aName)
|
||||
{
|
||||
aName.AssignASCII(XRE_ChildProcessTypeToString(mDetails.mProcess));
|
||||
aName.Assign(mDetails.process());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHangDetails::GetRemoteType(nsAString& aName)
|
||||
{
|
||||
aName.Assign(mDetails.mRemoteType);
|
||||
aName.Assign(mDetails.remoteType());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -56,14 +57,14 @@ nsHangDetails::GetAnnotations(JSContext* aCx, JS::MutableHandleValue aVal)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
for (auto& annot : mDetails.mAnnotations) {
|
||||
JSString* jsString = JS_NewUCStringCopyN(aCx, annot.mValue.get(), annot.mValue.Length());
|
||||
for (auto& annot : mDetails.annotations()) {
|
||||
JSString* jsString = JS_NewUCStringCopyN(aCx, annot.value().get(), annot.value().Length());
|
||||
if (!jsString) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
JS::RootedValue jsValue(aCx);
|
||||
jsValue.setString(jsString);
|
||||
if (!JS_DefineUCProperty(aCx, jsAnnotation, annot.mName.get(), annot.mName.Length(),
|
||||
if (!JS_DefineUCProperty(aCx, jsAnnotation, annot.name().get(), annot.name().Length(),
|
||||
jsValue, JSPROP_ENUMERATE)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
@ -98,32 +99,63 @@ StringFrame(JSContext* aCx,
|
||||
} // anonymous namespace
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHangDetails::GetStack(JSContext* aCx, JS::MutableHandle<JS::Value> aVal)
|
||||
nsHangDetails::GetStack(JSContext* aCx, JS::MutableHandleValue aStack)
|
||||
{
|
||||
JS::RootedObject ret(aCx, JS_NewArrayObject(aCx, mDetails.mStack.length()));
|
||||
auto& stack = mDetails.stack();
|
||||
uint32_t length = stack.stack().Length();
|
||||
JS::RootedObject ret(aCx, JS_NewArrayObject(aCx, length));
|
||||
if (!ret) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
for (size_t i = 0; i < mDetails.mStack.length(); ++i) {
|
||||
const HangStack::Frame& frame = mDetails.mStack[i];
|
||||
switch (frame.GetKind()) {
|
||||
case HangStack::Frame::Kind::STRING: {
|
||||
nsresult rv = StringFrame(aCx, ret, i, frame.AsString());
|
||||
|
||||
for (uint32_t i = 0; i < length; ++i) {
|
||||
auto& entry = stack.stack()[i];
|
||||
switch (entry.type()) {
|
||||
case HangEntry::TnsCString: {
|
||||
nsresult rv = StringFrame(aCx, ret, i, entry.get_nsCString().get());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
break;
|
||||
}
|
||||
case HangEntry::THangEntryBufOffset: {
|
||||
uint32_t offset = entry.get_HangEntryBufOffset().index();
|
||||
|
||||
// NOTE: We can't trust the offset we got, as we might have gotten it
|
||||
// from a compromised content process. Validate that it is in bounds.
|
||||
if (NS_WARN_IF(stack.strbuffer().IsEmpty() ||
|
||||
offset >= stack.strbuffer().Length())) {
|
||||
MOZ_ASSERT_UNREACHABLE("Corrupted offset data");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// NOTE: If our content process is compromised, it could send us back a
|
||||
// strbuffer() which didn't have a null terminator. If the last byte in
|
||||
// the buffer is not '\0', we abort, to make sure we don't read out of
|
||||
// bounds.
|
||||
if (stack.strbuffer().LastElement() != '\0') {
|
||||
MOZ_ASSERT_UNREACHABLE("Corrupted strbuffer data");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// We know this offset is safe because of the previous checks.
|
||||
const int8_t* start = stack.strbuffer().Elements() + offset;
|
||||
nsresult rv = StringFrame(aCx, ret, i,
|
||||
reinterpret_cast<const char*>(start));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
break;
|
||||
}
|
||||
case HangEntry::THangEntryModOffset: {
|
||||
const HangEntryModOffset& mo = entry.get_HangEntryModOffset();
|
||||
|
||||
case HangStack::Frame::Kind::MODOFFSET: {
|
||||
JS::RootedObject jsFrame(aCx, JS_NewArrayObject(aCx, 2));
|
||||
if (!jsFrame) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
if (!JS_DefineElement(aCx, jsFrame, 0, frame.AsModOffset().mModule, JSPROP_ENUMERATE)) {
|
||||
if (!JS_DefineElement(aCx, jsFrame, 0, mo.module(), JSPROP_ENUMERATE)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
nsPrintfCString hexString("%" PRIxPTR, (uintptr_t)frame.AsModOffset().mOffset);
|
||||
nsPrintfCString hexString("%" PRIxPTR, (uintptr_t)mo.offset());
|
||||
JS::RootedString hex(aCx, JS_NewStringCopyZ(aCx, hexString.get()));
|
||||
if (!hex || !JS_DefineElement(aCx, jsFrame, 1, hex, JSPROP_ENUMERATE)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
@ -134,67 +166,49 @@ nsHangDetails::GetStack(JSContext* aCx, JS::MutableHandle<JS::Value> aVal)
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case HangStack::Frame::Kind::PC: {
|
||||
JS::RootedObject jsFrame(aCx, JS_NewArrayObject(aCx, 2));
|
||||
if (!jsFrame) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
if (!JS_DefineElement(aCx, jsFrame, 0, -1, JSPROP_ENUMERATE)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
nsPrintfCString hexString("%" PRIxPTR, frame.AsPC());
|
||||
JS::RootedString hex(aCx, JS_NewStringCopyZ(aCx, hexString.get()));
|
||||
if (!hex || !JS_DefineElement(aCx, jsFrame, 1, hex, JSPROP_ENUMERATE)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
if (!JS_DefineElement(aCx, ret, i, jsFrame, JSPROP_ENUMERATE)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
case HangEntry::THangEntryProgCounter: {
|
||||
// Don't bother recording fixed program counters to JS
|
||||
nsresult rv = StringFrame(aCx, ret, i, "(unresolved)");
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
break;
|
||||
}
|
||||
|
||||
case HangStack::Frame::Kind::CONTENT: {
|
||||
case HangEntry::THangEntryContent: {
|
||||
nsresult rv = StringFrame(aCx, ret, i, "(content script)");
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
break;
|
||||
}
|
||||
|
||||
case HangStack::Frame::Kind::JIT: {
|
||||
case HangEntry::THangEntryJit: {
|
||||
nsresult rv = StringFrame(aCx, ret, i, "(jit frame)");
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
break;
|
||||
}
|
||||
|
||||
case HangStack::Frame::Kind::WASM: {
|
||||
case HangEntry::THangEntryWasm: {
|
||||
nsresult rv = StringFrame(aCx, ret, i, "(wasm)");
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
break;
|
||||
}
|
||||
|
||||
case HangStack::Frame::Kind::SUPPRESSED: {
|
||||
case HangEntry::THangEntryChromeScript: {
|
||||
nsresult rv = StringFrame(aCx, ret, i, "(chrome script)");
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
break;
|
||||
}
|
||||
case HangEntry::THangEntrySuppressed: {
|
||||
nsresult rv = StringFrame(aCx, ret, i, "(profiling suppressed)");
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Invalid variant");
|
||||
break;
|
||||
default: MOZ_CRASH("Unsupported HangEntry type?");
|
||||
}
|
||||
}
|
||||
|
||||
aVal.setObject(*ret);
|
||||
aStack.setObject(*ret);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHangDetails::GetModules(JSContext* aCx, JS::MutableHandleValue aVal)
|
||||
{
|
||||
auto& modules = mDetails.mStack.GetModules();
|
||||
auto& modules = mDetails.stack().modules();
|
||||
size_t length = modules.Length();
|
||||
JS::RootedObject retObj(aCx, JS_NewArrayObject(aCx, length));
|
||||
if (!retObj) {
|
||||
@ -202,18 +216,22 @@ nsHangDetails::GetModules(JSContext* aCx, JS::MutableHandleValue aVal)
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
const HangStack::Module& module = modules[i];
|
||||
const HangModule& module = modules[i];
|
||||
JS::RootedObject jsModule(aCx, JS_NewArrayObject(aCx, 2));
|
||||
if (!jsModule) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
JS::RootedString name(aCx, JS_NewUCStringCopyN(aCx, module.mName.BeginReading(), module.mName.Length()));
|
||||
JS::RootedString name(aCx, JS_NewUCStringCopyN(aCx,
|
||||
module.name().BeginReading(),
|
||||
module.name().Length()));
|
||||
if (!JS_DefineElement(aCx, jsModule, 0, name, JSPROP_ENUMERATE)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
JS::RootedString breakpadId(aCx, JS_NewStringCopyN(aCx, module.mBreakpadId.BeginReading(), module.mBreakpadId.Length()));
|
||||
JS::RootedString breakpadId(aCx, JS_NewStringCopyN(aCx,
|
||||
module.breakpadId().BeginReading(),
|
||||
module.breakpadId().Length()));
|
||||
if (!JS_DefineElement(aCx, jsModule, 1, breakpadId, JSPROP_ENUMERATE)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
@ -247,7 +265,7 @@ nsHangDetails::Submit()
|
||||
case GeckoProcessType_Content: {
|
||||
auto cc = dom::ContentChild::GetSingleton();
|
||||
if (cc) {
|
||||
hangDetails->mDetails.mRemoteType.Assign(cc->GetRemoteType());
|
||||
hangDetails->mDetails.remoteType().Assign(cc->GetRemoteType());
|
||||
Unused << cc->SendBHRThreadHang(hangDetails->mDetails);
|
||||
}
|
||||
break;
|
||||
@ -272,16 +290,6 @@ nsHangDetails::Submit()
|
||||
NS_WARNING("Unsupported BHR process type - discarding hang.");
|
||||
break;
|
||||
}
|
||||
#ifdef MOZ_GECKO_PROFILER
|
||||
if (profiler_is_active()) {
|
||||
TimeStamp endTime = hangDetails->mDetails.mEndTime;
|
||||
TimeStamp startTime = endTime -
|
||||
TimeDuration::FromMilliseconds(hangDetails->mDetails.mDuration);
|
||||
profiler_add_marker(
|
||||
"BHR-detected hang",
|
||||
MakeUnique<HangMarkerPayload>(startTime, endTime));
|
||||
}
|
||||
#endif
|
||||
});
|
||||
|
||||
nsresult rv = SystemGroup::Dispatch(TaskCategory::Other,
|
||||
@ -291,12 +299,88 @@ nsHangDetails::Submit()
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsHangDetails, nsIHangDetails)
|
||||
|
||||
namespace {
|
||||
|
||||
// Sorting comparator used by ReadModuleInformation. Sorts PC Frames by their
|
||||
// PC.
|
||||
struct PCFrameComparator {
|
||||
bool LessThan(HangEntry* const& a, HangEntry* const& b) const {
|
||||
return a->get_HangEntryProgCounter().pc() < b->get_HangEntryProgCounter().pc();
|
||||
}
|
||||
bool Equals(HangEntry* const& a, HangEntry* const& b) const {
|
||||
return a->get_HangEntryProgCounter().pc() == b->get_HangEntryProgCounter().pc();
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void
|
||||
ReadModuleInformation(HangStack& stack)
|
||||
{
|
||||
// modules() should be empty when we start filling it.
|
||||
stack.modules().Clear();
|
||||
|
||||
#ifdef MOZ_GECKO_PROFILER
|
||||
// Create a sorted list of the PCs in the current stack.
|
||||
AutoTArray<HangEntry*, 100> frames;
|
||||
for (auto& frame : stack.stack()) {
|
||||
if (frame.type() == HangEntry::THangEntryProgCounter) {
|
||||
frames.AppendElement(&frame);
|
||||
}
|
||||
}
|
||||
PCFrameComparator comparator;
|
||||
frames.Sort(comparator);
|
||||
|
||||
SharedLibraryInfo rawModules = SharedLibraryInfo::GetInfoForSelf();
|
||||
rawModules.SortByAddress();
|
||||
|
||||
size_t frameIdx = 0;
|
||||
for (size_t i = 0; i < rawModules.GetSize(); ++i) {
|
||||
const SharedLibrary& info = rawModules.GetEntry(i);
|
||||
uintptr_t moduleStart = info.GetStart();
|
||||
uintptr_t moduleEnd = info.GetEnd() - 1;
|
||||
// the interval is [moduleStart, moduleEnd)
|
||||
|
||||
bool moduleReferenced = false;
|
||||
for (; frameIdx < frames.Length(); ++frameIdx) {
|
||||
auto& frame = frames[frameIdx];
|
||||
uint64_t pc = frame->get_HangEntryProgCounter().pc();
|
||||
// We've moved past this frame, let's go to the next one.
|
||||
if (pc >= moduleEnd) {
|
||||
break;
|
||||
}
|
||||
if (pc >= moduleStart) {
|
||||
uint64_t offset = pc - moduleStart;
|
||||
if (NS_WARN_IF(offset > UINT32_MAX)) {
|
||||
continue; // module/offset can only hold 32-bit offsets into shared libraries.
|
||||
}
|
||||
|
||||
// If we found the module, rewrite the Frame entry to instead be a
|
||||
// ModOffset one. mModules.Length() will be the index of the module when
|
||||
// we append it below, and we set moduleReferenced to true to ensure
|
||||
// that we do.
|
||||
moduleReferenced = true;
|
||||
uint32_t module = stack.modules().Length();
|
||||
HangEntryModOffset modOffset(module, static_cast<uint32_t>(offset));
|
||||
*frame = modOffset;
|
||||
}
|
||||
}
|
||||
|
||||
if (moduleReferenced) {
|
||||
nsDependentCString cstr(info.GetBreakpadId().c_str());
|
||||
HangModule module(info.GetDebugName(), cstr);
|
||||
stack.modules().AppendElement(module);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ProcessHangStackRunnable::Run()
|
||||
{
|
||||
// NOTE: Reading module information can take a long time, which is why we do
|
||||
// it off-main-thread.
|
||||
mHangDetails.mStack.ReadModuleInformation();
|
||||
ReadModuleInformation(mHangDetails.stack());
|
||||
|
||||
RefPtr<nsHangDetails> hangDetails = new nsHangDetails(Move(mHangDetails));
|
||||
hangDetails->Submit();
|
||||
@ -305,53 +389,3 @@ ProcessHangStackRunnable::Run()
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
|
||||
/**
|
||||
* IPC Serialization / Deserialization logic
|
||||
*/
|
||||
namespace IPC {
|
||||
|
||||
void
|
||||
ParamTraits<mozilla::HangDetails>::Write(Message* aMsg, const mozilla::HangDetails& aParam)
|
||||
{
|
||||
WriteParam(aMsg, aParam.mDuration);
|
||||
WriteParam(aMsg, aParam.mProcess);
|
||||
WriteParam(aMsg, aParam.mRemoteType);
|
||||
WriteParam(aMsg, aParam.mThreadName);
|
||||
WriteParam(aMsg, aParam.mRunnableName);
|
||||
WriteParam(aMsg, aParam.mStack);
|
||||
WriteParam(aMsg, aParam.mAnnotations);
|
||||
}
|
||||
|
||||
bool
|
||||
ParamTraits<mozilla::HangDetails>::Read(const Message* aMsg,
|
||||
PickleIterator* aIter,
|
||||
mozilla::HangDetails* aResult)
|
||||
{
|
||||
if (!ReadParam(aMsg, aIter, &aResult->mDuration)) {
|
||||
return false;
|
||||
}
|
||||
if (!ReadParam(aMsg, aIter, &aResult->mProcess)) {
|
||||
return false;
|
||||
}
|
||||
if (!ReadParam(aMsg, aIter, &aResult->mRemoteType)) {
|
||||
return false;
|
||||
}
|
||||
if (!ReadParam(aMsg, aIter, &aResult->mThreadName)) {
|
||||
return false;
|
||||
}
|
||||
if (!ReadParam(aMsg, aIter, &aResult->mRunnableName)) {
|
||||
return false;
|
||||
}
|
||||
if (!ReadParam(aMsg, aIter, &aResult->mStack)) {
|
||||
return false;
|
||||
}
|
||||
if (!ReadParam(aMsg, aIter, &aResult->mAnnotations)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace IPC
|
||||
|
@ -11,7 +11,7 @@
|
||||
#include "mozilla/ProcessedStack.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/HangStack.h"
|
||||
#include "mozilla/HangTypes.h"
|
||||
#include "mozilla/HangAnnotations.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsIHangDetails.h"
|
||||
@ -19,55 +19,6 @@
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/**
|
||||
* HangDetails is a POD struct which contains the information collected from the
|
||||
* hang. It can be wrapped in a nsHangDetails to provide an XPCOM interface for
|
||||
* extracting information from it easily.
|
||||
*
|
||||
* This type is separate, as it can be sent over IPC while nsHangDetails is an
|
||||
* XPCOM interface which is harder to serialize over IPC.
|
||||
*/
|
||||
class HangDetails
|
||||
{
|
||||
public:
|
||||
HangDetails()
|
||||
: mDuration(0)
|
||||
, mEndTime(TimeStamp::Now())
|
||||
, mProcess(GeckoProcessType_Invalid)
|
||||
, mRemoteType(VoidString())
|
||||
{}
|
||||
|
||||
HangDetails(const HangDetails& aOther) = default;
|
||||
HangDetails(HangDetails&& aOther) = default;
|
||||
HangDetails(uint32_t aDuration,
|
||||
TimeStamp aEndTime,
|
||||
GeckoProcessType aProcess,
|
||||
const nsACString& aThreadName,
|
||||
const nsACString& aRunnableName,
|
||||
HangStack&& aStack,
|
||||
HangMonitor::HangAnnotations&& aAnnotations)
|
||||
: mDuration(aDuration)
|
||||
, mEndTime(aEndTime)
|
||||
, mProcess(aProcess)
|
||||
, mRemoteType(VoidString())
|
||||
, mThreadName(aThreadName)
|
||||
, mRunnableName(aRunnableName)
|
||||
, mStack(Move(aStack))
|
||||
, mAnnotations(Move(aAnnotations))
|
||||
{}
|
||||
|
||||
uint32_t mDuration;
|
||||
TimeStamp mEndTime;
|
||||
GeckoProcessType mProcess;
|
||||
// NOTE: mRemoteType is set in nsHangDetails::Submit before the HangDetails
|
||||
// object is sent to the parent process.
|
||||
nsString mRemoteType;
|
||||
nsCString mThreadName;
|
||||
nsCString mRunnableName;
|
||||
HangStack mStack;
|
||||
HangMonitor::HangAnnotations mAnnotations;
|
||||
};
|
||||
|
||||
/**
|
||||
* HangDetails is the concrete implementaion of nsIHangDetails, and contains the
|
||||
* infromation which we want to expose to observers of the bhr-thread-hang
|
||||
|
@ -1,325 +0,0 @@
|
||||
#include "HangStack.h"
|
||||
|
||||
#ifdef MOZ_GECKO_PROFILER
|
||||
#include "shared-libraries.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
HangStack::HangStack(const HangStack& aOther)
|
||||
: mModules(aOther.mModules)
|
||||
{
|
||||
if (NS_WARN_IF(!mBuffer.reserve(aOther.mBuffer.length()) ||
|
||||
!mImpl.reserve(aOther.mImpl.length()))) {
|
||||
return;
|
||||
}
|
||||
// XXX: I should be able to just memcpy the other stack's mImpl and mBuffer,
|
||||
// and then just re-offset pointers.
|
||||
for (size_t i = 0; i < aOther.length(); ++i) {
|
||||
const Frame& frame = aOther[i];
|
||||
|
||||
// If the source string is a reference to aOther's buffer, we have to append
|
||||
// via buffer, otherwise we can just copy the entry over.
|
||||
if (frame.GetKind() == Frame::Kind::STRING) {
|
||||
const char* s = frame.AsString();
|
||||
if (aOther.IsInBuffer(s)) {
|
||||
InfallibleAppendViaBuffer(s, strlen(s));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
infallibleAppend(frame);
|
||||
}
|
||||
MOZ_ASSERT(mImpl.length() == aOther.mImpl.length());
|
||||
MOZ_ASSERT(mBuffer.length() == aOther.mBuffer.length());
|
||||
}
|
||||
|
||||
void
|
||||
HangStack::InfallibleAppendViaBuffer(const char* aText, size_t aLength)
|
||||
{
|
||||
MOZ_ASSERT(this->canAppendWithoutRealloc(1));
|
||||
// Include null-terminator in length count.
|
||||
MOZ_ASSERT(mBuffer.canAppendWithoutRealloc(aLength + 1));
|
||||
|
||||
const char* const entry = mBuffer.end();
|
||||
mBuffer.infallibleAppend(aText, aLength);
|
||||
mBuffer.infallibleAppend('\0'); // Explicitly append null-terminator
|
||||
|
||||
this->infallibleAppend(Frame(entry));
|
||||
}
|
||||
|
||||
bool
|
||||
HangStack::AppendViaBuffer(const char* aText, size_t aLength)
|
||||
{
|
||||
if (!this->reserve(this->length() + 1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Keep track of the previous buffer in case we need to adjust pointers later.
|
||||
const char* const prevStart = mBuffer.begin();
|
||||
const char* const prevEnd = mBuffer.end();
|
||||
|
||||
// Include null-terminator in length count.
|
||||
if (!mBuffer.reserve(mBuffer.length() + aLength + 1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (prevStart != mBuffer.begin()) {
|
||||
// The buffer has moved; we have to adjust pointers in the stack.
|
||||
for (auto & frame : *this) {
|
||||
if (frame.GetKind() == Frame::Kind::STRING) {
|
||||
const char*& entry = frame.AsString();
|
||||
if (entry >= prevStart && entry < prevEnd) {
|
||||
// Move from old buffer to new buffer.
|
||||
entry += mBuffer.begin() - prevStart;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InfallibleAppendViaBuffer(aText, aLength);
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Sorting comparator used by ReadModuleInformation. Sorts PC Frames by their
|
||||
// PC.
|
||||
struct PCFrameComparator {
|
||||
bool LessThan(HangStack::Frame* const& a, HangStack::Frame* const& b) const {
|
||||
return a->AsPC() < b->AsPC();
|
||||
}
|
||||
bool Equals(HangStack::Frame* const& a, HangStack::Frame* const& b) const {
|
||||
return a->AsPC() == b->AsPC();
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void
|
||||
HangStack::ReadModuleInformation()
|
||||
{
|
||||
// mModules should be empty when we start filling it.
|
||||
mModules.Clear();
|
||||
|
||||
#ifdef MOZ_GECKO_PROFILER
|
||||
// Create a sorted list of the PCs in the current stack.
|
||||
AutoTArray<Frame*, 100> frames;
|
||||
for (auto& frame : *this) {
|
||||
if (frame.GetKind() == Frame::Kind::PC) {
|
||||
frames.AppendElement(&frame);
|
||||
}
|
||||
}
|
||||
PCFrameComparator comparator;
|
||||
frames.Sort(comparator);
|
||||
|
||||
SharedLibraryInfo rawModules = SharedLibraryInfo::GetInfoForSelf();
|
||||
rawModules.SortByAddress();
|
||||
|
||||
size_t frameIdx = 0;
|
||||
for (size_t i = 0; i < rawModules.GetSize(); ++i) {
|
||||
const SharedLibrary& info = rawModules.GetEntry(i);
|
||||
uintptr_t moduleStart = info.GetStart();
|
||||
uintptr_t moduleEnd = info.GetEnd() - 1;
|
||||
// the interval is [moduleStart, moduleEnd)
|
||||
|
||||
bool moduleReferenced = false;
|
||||
for (; frameIdx < frames.Length(); ++frameIdx) {
|
||||
auto& frame = frames[frameIdx];
|
||||
// We've moved past this frame, let's go to the next one.
|
||||
if (frame->AsPC() >= moduleEnd) {
|
||||
break;
|
||||
}
|
||||
if (frame->AsPC() >= moduleStart) {
|
||||
uint64_t offset = frame->AsPC() - moduleStart;
|
||||
if (NS_WARN_IF(offset > UINT32_MAX)) {
|
||||
continue; // module/offset can only hold 32-bit offsets into shared libraries.
|
||||
}
|
||||
|
||||
// If we found the module, rewrite the Frame entry to instead be a
|
||||
// ModOffset one. mModules.Length() will be the index of the module when
|
||||
// we append it below, and we set moduleReferenced to true to ensure
|
||||
// that we do.
|
||||
moduleReferenced = true;
|
||||
uint32_t module = mModules.Length();
|
||||
ModOffset modOffset = {
|
||||
module,
|
||||
static_cast<uint32_t>(offset)
|
||||
};
|
||||
*frame = Frame(modOffset);
|
||||
}
|
||||
}
|
||||
|
||||
if (moduleReferenced) {
|
||||
nsDependentCString cstr(info.GetBreakpadId().c_str());
|
||||
Module module = {
|
||||
info.GetDebugName(),
|
||||
cstr
|
||||
};
|
||||
mModules.AppendElement(module);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
namespace IPC {
|
||||
|
||||
void
|
||||
ParamTraits<mozilla::HangStack::ModOffset>::Write(Message* aMsg, const mozilla::HangStack::ModOffset& aParam)
|
||||
{
|
||||
WriteParam(aMsg, aParam.mModule);
|
||||
WriteParam(aMsg, aParam.mOffset);
|
||||
}
|
||||
|
||||
bool
|
||||
ParamTraits<mozilla::HangStack::ModOffset>::Read(const Message* aMsg,
|
||||
PickleIterator* aIter,
|
||||
mozilla::HangStack::ModOffset* aResult)
|
||||
{
|
||||
if (!ReadParam(aMsg, aIter, &aResult->mModule)) {
|
||||
return false;
|
||||
}
|
||||
if (!ReadParam(aMsg, aIter, &aResult->mOffset)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ParamTraits<mozilla::HangStack::Module>::Write(Message* aMsg, const mozilla::HangStack::Module& aParam)
|
||||
{
|
||||
WriteParam(aMsg, aParam.mName);
|
||||
WriteParam(aMsg, aParam.mBreakpadId);
|
||||
}
|
||||
|
||||
bool
|
||||
ParamTraits<mozilla::HangStack::Module>::Read(const Message* aMsg, PickleIterator* aIter,
|
||||
mozilla::HangStack::Module* aResult)
|
||||
{
|
||||
if (!ReadParam(aMsg, aIter, &aResult->mName)) {
|
||||
return false;
|
||||
}
|
||||
if (!ReadParam(aMsg, aIter, &aResult->mBreakpadId)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ParamTraits<mozilla::HangStack>::Write(Message* aMsg, const mozilla::HangStack& aParam)
|
||||
{
|
||||
typedef mozilla::HangStack::Frame Frame;
|
||||
|
||||
size_t length = aParam.length();
|
||||
WriteParam(aMsg, length);
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
const Frame& frame = aParam[i];
|
||||
WriteParam(aMsg, frame.GetKind());
|
||||
|
||||
switch (frame.GetKind()) {
|
||||
case Frame::Kind::STRING: {
|
||||
nsDependentCString str(frame.AsString());
|
||||
WriteParam(aMsg, static_cast<nsACString&>(str));
|
||||
break;
|
||||
}
|
||||
case Frame::Kind::MODOFFSET: {
|
||||
WriteParam(aMsg, frame.AsModOffset());
|
||||
break;
|
||||
}
|
||||
case Frame::Kind::PC: {
|
||||
WriteParam(aMsg, frame.AsPC());
|
||||
break;
|
||||
}
|
||||
case Frame::Kind::CONTENT:
|
||||
case Frame::Kind::WASM:
|
||||
case Frame::Kind::JIT:
|
||||
case Frame::Kind::SUPPRESSED: {
|
||||
// NOTE: no associated data.
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
MOZ_RELEASE_ASSERT(false, "Invalid kind for HangStack Frame");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WriteParam(aMsg, aParam.GetModules());
|
||||
}
|
||||
|
||||
bool
|
||||
ParamTraits<mozilla::HangStack>::Read(const Message* aMsg,
|
||||
PickleIterator* aIter,
|
||||
mozilla::HangStack* aResult)
|
||||
{
|
||||
// Shorten the name of Frame
|
||||
typedef mozilla::HangStack::Frame Frame;
|
||||
|
||||
size_t length;
|
||||
if (!ReadParam(aMsg, aIter, &length)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!aResult->reserve(length)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
Frame::Kind kind;
|
||||
if (!ReadParam(aMsg, aIter, &kind)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (kind) {
|
||||
case Frame::Kind::STRING: {
|
||||
nsAutoCString str;
|
||||
if (!ReadParam(aMsg, aIter, static_cast<nsACString*>(&str))) {
|
||||
return false;
|
||||
}
|
||||
aResult->AppendViaBuffer(str.get(), str.Length());
|
||||
break;
|
||||
}
|
||||
case Frame::Kind::MODOFFSET: {
|
||||
mozilla::HangStack::ModOffset modOff;
|
||||
if (!ReadParam(aMsg, aIter, &modOff)) {
|
||||
return false;
|
||||
}
|
||||
aResult->infallibleAppend(Frame(modOff));
|
||||
break;
|
||||
}
|
||||
case Frame::Kind::PC: {
|
||||
uintptr_t pc;
|
||||
if (!ReadParam(aMsg, aIter, &pc)) {
|
||||
return false;
|
||||
}
|
||||
aResult->infallibleAppend(Frame(pc));
|
||||
break;
|
||||
}
|
||||
case Frame::Kind::CONTENT:
|
||||
aResult->infallibleAppend(Frame::Content());
|
||||
break;
|
||||
case Frame::Kind::WASM:
|
||||
aResult->infallibleAppend(Frame::Wasm());
|
||||
break;
|
||||
case Frame::Kind::JIT:
|
||||
aResult->infallibleAppend(Frame::Jit());
|
||||
break;
|
||||
case Frame::Kind::SUPPRESSED:
|
||||
aResult->infallibleAppend(Frame::Suppressed());
|
||||
break;
|
||||
default:
|
||||
// We can't deserialize other kinds!
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ReadParam(aMsg, aIter, &aResult->GetModules())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace IPC
|
@ -1,340 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_HangStack_h
|
||||
#define mozilla_HangStack_h
|
||||
|
||||
#include "ipc/IPCMessageUtils.h"
|
||||
#include "mozilla/ProcessedStack.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsIHangDetails.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
/* A native stack is a simple list of pointers, so rather than building a
|
||||
wrapper type, we typdef the type here. */
|
||||
typedef std::vector<uintptr_t> NativeHangStack;
|
||||
|
||||
/* HangStack stores an array of const char pointers,
|
||||
with optional internal storage for strings. */
|
||||
class HangStack
|
||||
{
|
||||
public:
|
||||
static const size_t sMaxInlineStorage = 8;
|
||||
|
||||
// The maximum depth for the native stack frames that we might collect.
|
||||
// XXX: Consider moving this to a different object?
|
||||
static const size_t sMaxNativeFrames = 150;
|
||||
|
||||
struct ModOffset {
|
||||
uint32_t mModule;
|
||||
uint32_t mOffset;
|
||||
|
||||
bool operator==(const ModOffset& aOther) const {
|
||||
return mModule == aOther.mModule && mOffset == aOther.mOffset;
|
||||
}
|
||||
};
|
||||
|
||||
// A HangStack frame is one of the following types:
|
||||
// * Kind::STRING(const char*) : A string representing a pseudostack or chrome JS stack frame.
|
||||
// * Kind::MODOFFSET(ModOffset) : A module index and offset into that module.
|
||||
// * Kind::PC(uintptr_t) : A raw program counter which has not been mapped to a module.
|
||||
// * Kind::CONTENT: A hidden "(content script)" frame.
|
||||
// * Kind::JIT : An unprocessed "(jit frame)".
|
||||
// * Kind::WASM : An unprocessed "(wasm)" frame.
|
||||
// * Kind::SUPPRESSED : A JS frame while profiling was suppressed.
|
||||
//
|
||||
// NOTE: A manually rolled tagged enum is used instead of mozilla::Variant
|
||||
// here because we cannot use mozilla::Variant's IPC serialization directly.
|
||||
// Unfortunately the const char* variant needs the context of the HangStack
|
||||
// which it is in order to correctly deserialize. For this reason, a Frame by
|
||||
// itself does not have a ParamTraits implementation.
|
||||
class Frame
|
||||
{
|
||||
public:
|
||||
enum class Kind {
|
||||
STRING,
|
||||
MODOFFSET,
|
||||
PC,
|
||||
CONTENT,
|
||||
JIT,
|
||||
WASM,
|
||||
SUPPRESSED,
|
||||
END // Marker
|
||||
};
|
||||
|
||||
Frame()
|
||||
: mKind(Kind::STRING)
|
||||
, mString("")
|
||||
{}
|
||||
|
||||
explicit Frame(const char* aString)
|
||||
: mKind(Kind::STRING)
|
||||
, mString(aString)
|
||||
{}
|
||||
|
||||
explicit Frame(ModOffset aModOffset)
|
||||
: mKind(Kind::MODOFFSET)
|
||||
, mModOffset(aModOffset)
|
||||
{}
|
||||
|
||||
explicit Frame(uintptr_t aPC)
|
||||
: mKind(Kind::PC)
|
||||
, mPC(aPC)
|
||||
{}
|
||||
|
||||
Kind GetKind() const {
|
||||
return mKind;
|
||||
}
|
||||
|
||||
const char*& AsString() {
|
||||
MOZ_ASSERT(mKind == Kind::STRING);
|
||||
return mString;
|
||||
}
|
||||
|
||||
const char* const& AsString() const {
|
||||
MOZ_ASSERT(mKind == Kind::STRING);
|
||||
return mString;
|
||||
}
|
||||
|
||||
const ModOffset& AsModOffset() const {
|
||||
MOZ_ASSERT(mKind == Kind::MODOFFSET);
|
||||
return mModOffset;
|
||||
}
|
||||
|
||||
const uintptr_t& AsPC() const {
|
||||
MOZ_ASSERT(mKind == Kind::PC);
|
||||
return mPC;
|
||||
}
|
||||
|
||||
// Public constant frames copies of each of the data-less frames.
|
||||
static Frame Content() {
|
||||
return Frame(Kind::CONTENT);
|
||||
}
|
||||
static Frame Jit() {
|
||||
return Frame(Kind::JIT);
|
||||
}
|
||||
static Frame Wasm() {
|
||||
return Frame(Kind::WASM);
|
||||
}
|
||||
static Frame Suppressed() {
|
||||
return Frame(Kind::SUPPRESSED);
|
||||
}
|
||||
|
||||
private:
|
||||
explicit Frame(Kind aKind)
|
||||
: mKind(aKind)
|
||||
{
|
||||
MOZ_ASSERT(aKind == Kind::CONTENT ||
|
||||
aKind == Kind::JIT ||
|
||||
aKind == Kind::WASM ||
|
||||
aKind == Kind::SUPPRESSED,
|
||||
"Kind must only be one of CONTENT, JIT, WASM or SUPPRESSED "
|
||||
"for the data-free constructor.");
|
||||
}
|
||||
|
||||
Kind mKind;
|
||||
union {
|
||||
const char* mString;
|
||||
ModOffset mModOffset;
|
||||
uintptr_t mPC;
|
||||
};
|
||||
};
|
||||
|
||||
struct Module {
|
||||
// The file name, /foo/bar/libxul.so for example.
|
||||
// It can contain unicode characters.
|
||||
nsString mName;
|
||||
nsCString mBreakpadId;
|
||||
|
||||
bool operator==(const Module& aOther) const {
|
||||
return mName == aOther.mName && mBreakpadId == aOther.mBreakpadId;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
typedef mozilla::Vector<Frame, sMaxInlineStorage> Impl;
|
||||
Impl mImpl;
|
||||
|
||||
// Stack entries can either be a static const char*
|
||||
// or a pointer to within this buffer.
|
||||
mozilla::Vector<char, 0> mBuffer;
|
||||
nsTArray<Module> mModules;
|
||||
|
||||
public:
|
||||
HangStack() {}
|
||||
|
||||
HangStack(const HangStack& aOther);
|
||||
HangStack(HangStack&& aOther)
|
||||
: mImpl(mozilla::Move(aOther.mImpl))
|
||||
, mBuffer(mozilla::Move(aOther.mBuffer))
|
||||
, mModules(mozilla::Move(aOther.mModules))
|
||||
{
|
||||
}
|
||||
|
||||
HangStack& operator=(HangStack&& aOther) {
|
||||
mImpl = mozilla::Move(aOther.mImpl);
|
||||
mBuffer = mozilla::Move(aOther.mBuffer);
|
||||
mModules = mozilla::Move(aOther.mModules);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const HangStack& aOther) const {
|
||||
for (size_t i = 0; i < length(); i++) {
|
||||
if (!IsSameAsEntry(operator[](i), aOther[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator!=(const HangStack& aOther) const {
|
||||
return !operator==(aOther);
|
||||
}
|
||||
|
||||
Frame& operator[](size_t aIndex) {
|
||||
return mImpl[aIndex];
|
||||
}
|
||||
|
||||
Frame const& operator[](size_t aIndex) const {
|
||||
return mImpl[aIndex];
|
||||
}
|
||||
|
||||
size_t capacity() const { return mImpl.capacity(); }
|
||||
size_t length() const { return mImpl.length(); }
|
||||
bool empty() const { return mImpl.empty(); }
|
||||
bool canAppendWithoutRealloc(size_t aNeeded) const {
|
||||
return mImpl.canAppendWithoutRealloc(aNeeded);
|
||||
}
|
||||
void infallibleAppend(Frame aEntry) { mImpl.infallibleAppend(aEntry); }
|
||||
MOZ_MUST_USE bool reserve(size_t aRequest) { return mImpl.reserve(aRequest); }
|
||||
Frame* begin() { return mImpl.begin(); }
|
||||
Frame const* begin() const { return mImpl.begin(); }
|
||||
Frame* end() { return mImpl.end(); }
|
||||
Frame const* end() const { return mImpl.end(); }
|
||||
Frame& back() { return mImpl.back(); }
|
||||
void erase(Frame* aEntry) { mImpl.erase(aEntry); }
|
||||
void erase(Frame* aBegin, Frame* aEnd) {
|
||||
mImpl.erase(aBegin, aEnd);
|
||||
}
|
||||
|
||||
void clear() {
|
||||
mImpl.clear();
|
||||
mBuffer.clear();
|
||||
mModules.Clear();
|
||||
}
|
||||
|
||||
bool IsInBuffer(const char* aEntry) const {
|
||||
return aEntry >= mBuffer.begin() && aEntry < mBuffer.end();
|
||||
}
|
||||
|
||||
bool IsSameAsEntry(const Frame& aFrame, const Frame& aOther) const {
|
||||
if (aFrame.GetKind() != aOther.GetKind()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (aFrame.GetKind()) {
|
||||
case Frame::Kind::STRING:
|
||||
// If the entry came from the buffer, we need to compare its content;
|
||||
// otherwise we only need to compare its pointer.
|
||||
return IsInBuffer(aFrame.AsString()) ?
|
||||
!strcmp(aFrame.AsString(), aOther.AsString()) :
|
||||
(aFrame.AsString() == aOther.AsString());
|
||||
case Frame::Kind::MODOFFSET:
|
||||
return aFrame.AsModOffset() == aOther.AsModOffset();
|
||||
case Frame::Kind::PC:
|
||||
return aFrame.AsPC() == aOther.AsPC();
|
||||
default:
|
||||
MOZ_CRASH();
|
||||
}
|
||||
}
|
||||
|
||||
bool IsSameAsEntry(const char* aEntry, const char* aOther) const {
|
||||
// If the entry came from the buffer, we need to compare its content;
|
||||
// otherwise we only need to compare its pointer.
|
||||
return IsInBuffer(aEntry) ? !strcmp(aEntry, aOther) : (aEntry == aOther);
|
||||
}
|
||||
|
||||
size_t AvailableBufferSize() const {
|
||||
return mBuffer.capacity() - mBuffer.length();
|
||||
}
|
||||
|
||||
MOZ_MUST_USE bool EnsureBufferCapacity(size_t aCapacity) {
|
||||
// aCapacity is the minimal capacity and Vector may make the actual
|
||||
// capacity larger, in which case we want to use up all the space.
|
||||
return mBuffer.reserve(aCapacity) &&
|
||||
mBuffer.reserve(mBuffer.capacity());
|
||||
}
|
||||
|
||||
void InfallibleAppendViaBuffer(const char* aText, size_t aLength);
|
||||
bool AppendViaBuffer(const char* aText, size_t aLength);
|
||||
|
||||
const nsTArray<Module>& GetModules() const {
|
||||
return mModules;
|
||||
}
|
||||
nsTArray<Module>& GetModules() {
|
||||
return mModules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current list of loaded modules, and use it to transform Kind::PC
|
||||
* stack frames from within these modules into Kind::MODOFFSET stack entries.
|
||||
*
|
||||
* This method also populates the mModules list, which should be empty when
|
||||
* this method is called.
|
||||
*/
|
||||
void ReadModuleInformation();
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
namespace IPC {
|
||||
|
||||
template<>
|
||||
class ParamTraits<mozilla::HangStack::ModOffset>
|
||||
{
|
||||
public:
|
||||
typedef mozilla::HangStack::ModOffset paramType;
|
||||
static void Write(Message* aMsg, const paramType& aParam);
|
||||
static bool Read(const Message* aMsg,
|
||||
PickleIterator* aIter,
|
||||
paramType* aResult);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ParamTraits<mozilla::HangStack::Frame::Kind>
|
||||
: public ContiguousEnumSerializer<
|
||||
mozilla::HangStack::Frame::Kind,
|
||||
mozilla::HangStack::Frame::Kind::STRING,
|
||||
mozilla::HangStack::Frame::Kind::END>
|
||||
{};
|
||||
|
||||
template<>
|
||||
struct ParamTraits<mozilla::HangStack::Module>
|
||||
{
|
||||
public:
|
||||
typedef mozilla::HangStack::Module paramType;
|
||||
static void Write(Message* aMsg, const paramType& aParam);
|
||||
static bool Read(const Message* aMsg,
|
||||
PickleIterator* aIter,
|
||||
paramType* aResult);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ParamTraits<mozilla::HangStack>
|
||||
{
|
||||
typedef mozilla::HangStack paramType;
|
||||
static void Write(Message* aMsg, const paramType& aParam);
|
||||
static bool Read(const Message* aMsg,
|
||||
PickleIterator* aIter,
|
||||
paramType* aResult);
|
||||
};
|
||||
|
||||
} // namespace IPC
|
||||
|
||||
#endif // mozilla_HangStack_h
|
93
toolkit/components/backgroundhangmonitor/HangTypes.ipdlh
Normal file
93
toolkit/components/backgroundhangmonitor/HangTypes.ipdlh
Normal file
@ -0,0 +1,93 @@
|
||||
/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
|
||||
/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// The different kinds of hang entries which we're going to need to handle in
|
||||
// our HangStacks.
|
||||
|
||||
struct HangEntryBufOffset
|
||||
{
|
||||
// NOTE: Don't trust this index without checking it is a valid index into
|
||||
// the strbuffer, and that the buffer's last byte is a '\0'.
|
||||
uint32_t index;
|
||||
};
|
||||
|
||||
struct HangEntryModOffset
|
||||
{
|
||||
uint32_t module;
|
||||
uint32_t offset;
|
||||
};
|
||||
|
||||
struct HangEntryProgCounter
|
||||
{
|
||||
uintptr_t pc;
|
||||
};
|
||||
|
||||
// Singleton structs for the union type.
|
||||
struct HangEntryContent {};
|
||||
struct HangEntryJit {};
|
||||
struct HangEntryWasm {};
|
||||
struct HangEntryChromeScript {};
|
||||
struct HangEntrySuppressed {};
|
||||
|
||||
union HangEntry
|
||||
{
|
||||
// String representing a pseudostack or chrome JS stack.
|
||||
nsCString;
|
||||
// The index of the start of a string in the associated buffer.
|
||||
HangEntryBufOffset;
|
||||
// A module index and offset into that module.
|
||||
HangEntryModOffset;
|
||||
// A raw program counter which has not been mapped into a module.
|
||||
HangEntryProgCounter;
|
||||
// A hidden "(content script)" frame.
|
||||
HangEntryContent;
|
||||
// An unprocessed "(jit frame)"
|
||||
HangEntryJit;
|
||||
// An unprocessed "(wasm)" frame.
|
||||
HangEntryWasm;
|
||||
// A chrome script which didn't fit in the buffer.
|
||||
HangEntryChromeScript;
|
||||
// A JS frame while profiling was suppressed.
|
||||
HangEntrySuppressed;
|
||||
};
|
||||
|
||||
struct HangModule
|
||||
{
|
||||
// The file name, /foo/bar/libxul.so for example.
|
||||
// It can contain unicode characters.
|
||||
nsString name;
|
||||
nsCString breakpadId;
|
||||
};
|
||||
|
||||
struct HangStack
|
||||
{
|
||||
HangEntry[] stack;
|
||||
int8_t[] strbuffer;
|
||||
HangModule[] modules;
|
||||
};
|
||||
|
||||
// Hang annotation information.
|
||||
struct HangAnnotation
|
||||
{
|
||||
nsString name;
|
||||
nsString value;
|
||||
};
|
||||
|
||||
// The information about an individual hang which is sent over IPC.
|
||||
struct HangDetails
|
||||
{
|
||||
uint32_t duration;
|
||||
nsCString process;
|
||||
nsString remoteType;
|
||||
nsCString threadName;
|
||||
nsCString runnableName;
|
||||
HangStack stack;
|
||||
HangAnnotation[] annotations;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
@ -22,7 +22,7 @@
|
||||
#include "mozilla/MemoryChecking.h"
|
||||
#include "mozilla/Sprintf.h"
|
||||
#include "nsThread.h"
|
||||
#include "mozilla/HangStack.h"
|
||||
#include "mozilla/HangTypes.h"
|
||||
|
||||
#ifdef __GNUC__
|
||||
# pragma GCC diagnostic push
|
||||
@ -68,7 +68,7 @@ namespace mozilla {
|
||||
|
||||
ThreadStackHelper::ThreadStackHelper()
|
||||
: mStackToFill(nullptr)
|
||||
, mMaxStackSize(HangStack::sMaxInlineStorage)
|
||||
, mMaxStackSize(16)
|
||||
, mMaxBufferSize(512)
|
||||
, mDesiredStackSize(0)
|
||||
, mDesiredBufferSize(0)
|
||||
@ -90,12 +90,16 @@ ThreadStackHelper::PrepareStackBuffer(HangStack& aStack)
|
||||
mDesiredBufferSize = 0;
|
||||
mDesiredStackSize = 0;
|
||||
|
||||
// Return false to skip getting the stack and return an empty stack
|
||||
aStack.clear();
|
||||
// Clear all of the stack entries.
|
||||
aStack.stack().ClearAndRetainStorage();
|
||||
aStack.strbuffer().ClearAndRetainStorage();
|
||||
aStack.modules().Clear();
|
||||
|
||||
#ifdef MOZ_THREADSTACKHELPER_PSEUDO
|
||||
if (!aStack.reserve(mMaxStackSize) ||
|
||||
!aStack.reserve(aStack.capacity()) || // reserve up to the capacity
|
||||
!aStack.EnsureBufferCapacity(mMaxBufferSize)) {
|
||||
// Ensure we have enough space in our stack and string buffers for the data we
|
||||
// want to collect.
|
||||
if (!aStack.stack().SetCapacity(mMaxStackSize, fallible) ||
|
||||
!aStack.strbuffer().SetCapacity(mMaxBufferSize, fallible)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -161,17 +165,19 @@ ThreadStackHelper::SetIsMainThread()
|
||||
}
|
||||
|
||||
void
|
||||
ThreadStackHelper::TryAppendFrame(HangStack::Frame aFrame)
|
||||
ThreadStackHelper::TryAppendFrame(HangEntry aFrame)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(mStackToFill);
|
||||
|
||||
// We deduplicate identical frames of kind CONTENT, JIT, WASM, and SUPPRESSED.
|
||||
switch (aFrame.GetKind()) {
|
||||
case HangStack::Frame::Kind::CONTENT:
|
||||
case HangStack::Frame::Kind::JIT:
|
||||
case HangStack::Frame::Kind::WASM:
|
||||
case HangStack::Frame::Kind::SUPPRESSED:
|
||||
if (!mStackToFill->empty() && mStackToFill->back().GetKind() == aFrame.GetKind()) {
|
||||
// We deduplicate identical Content, Jit, Wasm, ChromeScript and Suppressed frames.
|
||||
switch (aFrame.type()) {
|
||||
case HangEntry::THangEntryContent:
|
||||
case HangEntry::THangEntryJit:
|
||||
case HangEntry::THangEntryWasm:
|
||||
case HangEntry::THangEntryChromeScript:
|
||||
case HangEntry::THangEntrySuppressed:
|
||||
if (!mStackToFill->stack().IsEmpty() &&
|
||||
mStackToFill->stack().LastElement().type() == aFrame.type()) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@ -184,8 +190,8 @@ ThreadStackHelper::TryAppendFrame(HangStack::Frame aFrame)
|
||||
mDesiredStackSize += 1;
|
||||
|
||||
// Perform the append if we have enough space to do so.
|
||||
if (mStackToFill->canAppendWithoutRealloc(1)) {
|
||||
mStackToFill->infallibleAppend(aFrame);
|
||||
if (mStackToFill->stack().Capacity() > mStackToFill->stack().Length()) {
|
||||
mStackToFill->stack().AppendElement(mozilla::Move(aFrame));
|
||||
}
|
||||
}
|
||||
|
||||
@ -193,14 +199,14 @@ void
|
||||
ThreadStackHelper::CollectNativeLeafAddr(void* aAddr)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(mStackToFill);
|
||||
TryAppendFrame(HangStack::Frame(reinterpret_cast<uintptr_t>(aAddr)));
|
||||
TryAppendFrame(HangEntryProgCounter(reinterpret_cast<uintptr_t>(aAddr)));
|
||||
}
|
||||
|
||||
void
|
||||
ThreadStackHelper::CollectJitReturnAddr(void* aAddr)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(mStackToFill);
|
||||
TryAppendFrame(HangStack::Frame::Jit());
|
||||
TryAppendFrame(HangEntryJit());
|
||||
}
|
||||
|
||||
void
|
||||
@ -209,7 +215,7 @@ ThreadStackHelper::CollectWasmFrame(const char* aLabel)
|
||||
MOZ_RELEASE_ASSERT(mStackToFill);
|
||||
// We don't want to collect WASM frames, as they are probably for content, so
|
||||
// we just add a "(content wasm)" frame.
|
||||
TryAppendFrame(HangStack::Frame::Wasm());
|
||||
TryAppendFrame(HangEntryWasm());
|
||||
}
|
||||
|
||||
namespace {
|
||||
@ -263,18 +269,39 @@ ThreadStackHelper::CollectPseudoEntry(const js::ProfileEntry& aEntry)
|
||||
{
|
||||
// For non-js frames we just include the raw label.
|
||||
if (!aEntry.isJs()) {
|
||||
const char* label = aEntry.label();
|
||||
TryAppendFrame(HangStack::Frame(label));
|
||||
const char* entryLabel = aEntry.label();
|
||||
|
||||
// entryLabel is a statically allocated string, so we want to store a
|
||||
// reference to it without performing any allocations. This is important, as
|
||||
// we aren't allowed to allocate within this function.
|
||||
//
|
||||
// The variant for this kind of label in our HangStack object is a
|
||||
// `nsCString`, which normally contains heap allocated string data. However,
|
||||
// `nsCString` has an optimization for literal strings which causes the
|
||||
// backing data to not be copied when being copied between nsCString
|
||||
// objects.
|
||||
//
|
||||
// We take advantage of that optimization by creating a nsCString object
|
||||
// which has the LITERAL flag set. Without this optimization, this code
|
||||
// would be incorrect.
|
||||
nsCString label;
|
||||
label.AssignLiteral(entryLabel, strlen(entryLabel));
|
||||
|
||||
// Let's make sure we don't deadlock here, by asserting that `label`'s
|
||||
// backing data matches.
|
||||
MOZ_RELEASE_ASSERT(label.BeginReading() == entryLabel,
|
||||
"String copy performed during ThreadStackHelper::CollectPseudoEntry");
|
||||
TryAppendFrame(label);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!aEntry.script()) {
|
||||
TryAppendFrame(HangStack::Frame::Suppressed());
|
||||
TryAppendFrame(HangEntrySuppressed());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsChromeJSScript(aEntry.script())) {
|
||||
TryAppendFrame(HangStack::Frame::Content());
|
||||
TryAppendFrame(HangEntryContent());
|
||||
return;
|
||||
}
|
||||
|
||||
@ -313,21 +340,25 @@ ThreadStackHelper::CollectPseudoEntry(const js::ProfileEntry& aEntry)
|
||||
}
|
||||
}
|
||||
|
||||
mDesiredStackSize += 1;
|
||||
char buffer[128]; // Enough to fit longest js file name from the tree
|
||||
size_t len = SprintfLiteral(buffer, "%s:%u", basename, lineno);
|
||||
if (len < sizeof(buffer)) {
|
||||
mDesiredBufferSize += len + 1;
|
||||
if (mStackToFill->canAppendWithoutRealloc(1) &&
|
||||
mStackToFill->AvailableBufferSize() >= len + 1) {
|
||||
mStackToFill->InfallibleAppendViaBuffer(buffer, len);
|
||||
|
||||
if (mStackToFill->stack().Capacity() > mStackToFill->stack().Length() &&
|
||||
(mStackToFill->strbuffer().Capacity() -
|
||||
mStackToFill->strbuffer().Length()) > len + 1) {
|
||||
// NOTE: We only increment this if we're going to successfully append.
|
||||
mDesiredStackSize += 1;
|
||||
uint32_t start = mStackToFill->strbuffer().Length();
|
||||
mStackToFill->strbuffer().AppendElements(buffer, len);
|
||||
mStackToFill->strbuffer().AppendElement('\0');
|
||||
mStackToFill->stack().AppendElement(HangEntryBufOffset(start));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (mStackToFill->canAppendWithoutRealloc(1)) {
|
||||
mStackToFill->infallibleAppend(HangStack::Frame("(chrome script)"));
|
||||
}
|
||||
TryAppendFrame(HangEntryChromeScript());
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -53,13 +53,6 @@ namespace mozilla {
|
||||
*/
|
||||
class ThreadStackHelper : public ProfilerStackCollector
|
||||
{
|
||||
public:
|
||||
// When a native stack is gathered, this vector holds the raw program counter
|
||||
// values that FramePointerStackWalk will return to us after it walks the
|
||||
// stack. When gathering the Telemetry payload, Telemetry will take care of
|
||||
// mapping these program counters to proper addresses within modules.
|
||||
typedef NativeHangStack NativeStack;
|
||||
|
||||
private:
|
||||
HangStack* mStackToFill;
|
||||
Array<char, nsThread::kRunnableNameBufSize>* mRunnableNameBuffer;
|
||||
@ -87,6 +80,11 @@ public:
|
||||
*/
|
||||
void GetStack(HangStack& aStack, nsACString& aRunnableName, bool aStackWalk);
|
||||
|
||||
/**
|
||||
* Retrieve the thread's profiler thread ID.
|
||||
*/
|
||||
int GetThreadId() const { return mThreadId; }
|
||||
|
||||
protected:
|
||||
/**
|
||||
* ProfilerStackCollector
|
||||
@ -98,7 +96,7 @@ protected:
|
||||
virtual void CollectPseudoEntry(const js::ProfileEntry& aEntry) override;
|
||||
|
||||
private:
|
||||
void TryAppendFrame(mozilla::HangStack::Frame aFrame);
|
||||
void TryAppendFrame(mozilla::HangEntry aFrame);
|
||||
|
||||
// The profiler's unique thread identifier for the target thread.
|
||||
int mThreadId;
|
||||
|
@ -32,13 +32,15 @@ XPIDL_MODULE = 'backgroundhangmonitor'
|
||||
EXPORTS.mozilla += [
|
||||
'BackgroundHangMonitor.h',
|
||||
'HangDetails.h',
|
||||
'HangStack.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'BackgroundHangMonitor.cpp',
|
||||
'HangDetails.cpp',
|
||||
'HangStack.cpp',
|
||||
]
|
||||
|
||||
IPDL_SOURCES += [
|
||||
'HangTypes.ipdlh',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_GECKO_PROFILER']:
|
||||
|
@ -732,7 +732,7 @@ ProfileBuffer::StreamSamplesToJSON(SpliceableJSONWriter& aWriter, int aThreadId,
|
||||
//
|
||||
// - We skip samples that don't have an appropriate ThreadId or Time.
|
||||
//
|
||||
// - We skip range Pause, Resume, CollectionStart, and CollectionEnd
|
||||
// - We skip range Pause, Resume, CollectionStart, Marker, and CollectionEnd
|
||||
// entries between samples.
|
||||
while (e.Has()) {
|
||||
if (e.Get().IsThreadId()) {
|
||||
@ -918,16 +918,17 @@ ProfileBuffer::StreamMarkersToJSON(SpliceableJSONWriter& aWriter,
|
||||
{
|
||||
EntryGetter e(*this);
|
||||
|
||||
int currentThreadID = -1;
|
||||
|
||||
// Stream all markers whose threadId matches aThreadId. All other entries are
|
||||
// skipped, because we process them in StreamSamplesToJSON().
|
||||
// Stream all markers whose threadId matches aThreadId. We skip other entries,
|
||||
// because we process them in StreamSamplesToJSON().
|
||||
//
|
||||
// NOTE: The ThreadId of a marker is determined by its GetThreadId() method,
|
||||
// rather than ThreadId buffer entries, as markers can be added outside of
|
||||
// samples.
|
||||
while (e.Has()) {
|
||||
if (e.Get().IsThreadId()) {
|
||||
currentThreadID = e.Get().u.mInt;
|
||||
} else if (currentThreadID == aThreadId && e.Get().IsMarker()) {
|
||||
if (e.Get().IsMarker()) {
|
||||
const ProfilerMarker* marker = e.Get().u.mMarker;
|
||||
if (marker->GetTime() >= aSinceTime) {
|
||||
if (marker->GetTime() >= aSinceTime &&
|
||||
marker->GetThreadId() == aThreadId) {
|
||||
marker->StreamJSON(aWriter, aProcessStartTime, aUniqueStacks);
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ class ProfilerMarker
|
||||
|
||||
public:
|
||||
explicit ProfilerMarker(const char* aMarkerName,
|
||||
int aThreadId,
|
||||
mozilla::UniquePtr<ProfilerMarkerPayload>
|
||||
aPayload = nullptr,
|
||||
double aTime = 0)
|
||||
@ -30,6 +31,7 @@ public:
|
||||
, mNext{nullptr}
|
||||
, mTime(aTime)
|
||||
, mGenID{0}
|
||||
, mThreadId{aThreadId}
|
||||
{}
|
||||
|
||||
void SetGeneration(uint32_t aGenID) { mGenID = aGenID; }
|
||||
@ -38,6 +40,8 @@ public:
|
||||
|
||||
double GetTime() const { return mTime; }
|
||||
|
||||
int GetThreadId() const { return mThreadId; }
|
||||
|
||||
void StreamJSON(SpliceableJSONWriter& aWriter,
|
||||
const mozilla::TimeStamp& aProcessStartTime,
|
||||
UniqueStacks& aUniqueStacks) const
|
||||
@ -69,6 +73,7 @@ private:
|
||||
ProfilerMarker* mNext;
|
||||
double mTime;
|
||||
uint32_t mGenID;
|
||||
int mThreadId;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
|
@ -25,9 +25,8 @@ ThreadInfo::ThreadInfo(const char* aName,
|
||||
void* aStackTop)
|
||||
: mName(strdup(aName))
|
||||
, mRegisterTime(TimeStamp::Now())
|
||||
, mThreadId(aThreadId)
|
||||
, mIsMainThread(aIsMainThread)
|
||||
, mRacyInfo(mozilla::MakeNotNull<RacyThreadInfo*>())
|
||||
, mRacyInfo(mozilla::MakeNotNull<RacyThreadInfo*>(aThreadId))
|
||||
, mPlatformData(AllocPlatformData(aThreadId))
|
||||
, mStackTop(aStackTop)
|
||||
, mIsBeingProfiled(false)
|
||||
@ -260,7 +259,7 @@ ThreadInfo::FlushSamplesAndMarkers(const TimeStamp& aProcessStartTime,
|
||||
SpliceableChunkedJSONWriter b;
|
||||
b.StartBareList();
|
||||
{
|
||||
aBuffer.StreamSamplesToJSON(b, mThreadId, /* aSinceTime = */ 0,
|
||||
aBuffer.StreamSamplesToJSON(b, ThreadId(), /* aSinceTime = */ 0,
|
||||
&mFirstSavedStreamedSampleTime,
|
||||
mContext, *mUniqueStacks);
|
||||
}
|
||||
@ -272,7 +271,7 @@ ThreadInfo::FlushSamplesAndMarkers(const TimeStamp& aProcessStartTime,
|
||||
SpliceableChunkedJSONWriter b;
|
||||
b.StartBareList();
|
||||
{
|
||||
aBuffer.StreamMarkersToJSON(b, mThreadId, aProcessStartTime,
|
||||
aBuffer.StreamMarkersToJSON(b, ThreadId(), aProcessStartTime,
|
||||
/* aSinceTime = */ 0, *mUniqueStacks);
|
||||
}
|
||||
b.EndBareList();
|
||||
|
@ -23,8 +23,9 @@
|
||||
class RacyThreadInfo final : public PseudoStack
|
||||
{
|
||||
public:
|
||||
RacyThreadInfo()
|
||||
explicit RacyThreadInfo(int aThreadId)
|
||||
: PseudoStack()
|
||||
, mThreadId(aThreadId)
|
||||
, mSleep(AWAKE)
|
||||
{
|
||||
MOZ_COUNT_CTOR(RacyThreadInfo);
|
||||
@ -57,7 +58,7 @@ public:
|
||||
double aTime)
|
||||
{
|
||||
ProfilerMarker* marker =
|
||||
new ProfilerMarker(aMarkerName, Move(aPayload), aTime);
|
||||
new ProfilerMarker(aMarkerName, mThreadId, Move(aPayload), aTime);
|
||||
mPendingMarkers.insert(marker);
|
||||
}
|
||||
|
||||
@ -113,10 +114,16 @@ public:
|
||||
|
||||
bool IsSleeping() { return mSleep != AWAKE; }
|
||||
|
||||
int ThreadId() const { return mThreadId; }
|
||||
|
||||
private:
|
||||
// A list of pending markers that must be moved to the circular buffer.
|
||||
ProfilerSignalSafeLinkedList<ProfilerMarker> mPendingMarkers;
|
||||
|
||||
// mThreadId contains the thread ID of the current thread. It is safe to read
|
||||
// this from multiple threads concurrently, as it will never be mutated.
|
||||
const int mThreadId;
|
||||
|
||||
// mSleep tracks whether the thread is sleeping, and if so, whether it has
|
||||
// been previously observed. This is used for an optimization: in some cases,
|
||||
// when a thread is asleep, we duplicate the previous sample, which is
|
||||
@ -173,7 +180,10 @@ public:
|
||||
~ThreadInfo();
|
||||
|
||||
const char* Name() const { return mName.get(); }
|
||||
int ThreadId() const { return mThreadId; }
|
||||
|
||||
// This is a safe read even when the target thread is not blocked, as this
|
||||
// thread id is never mutated.
|
||||
int ThreadId() const { return RacyInfo()->ThreadId(); }
|
||||
|
||||
bool IsMainThread() const { return mIsMainThread; }
|
||||
|
||||
@ -196,7 +206,6 @@ private:
|
||||
mozilla::UniqueFreePtr<char> mName;
|
||||
mozilla::TimeStamp mRegisterTime;
|
||||
mozilla::TimeStamp mUnregisterTime;
|
||||
int mThreadId;
|
||||
const bool mIsMainThread;
|
||||
|
||||
// The thread's RacyThreadInfo. This is an owning pointer. It could be an
|
||||
|
@ -3283,6 +3283,46 @@ profiler_add_marker(const char* aMarkerName)
|
||||
profiler_add_marker(aMarkerName, nullptr);
|
||||
}
|
||||
|
||||
// This logic needs to add a marker for a different thread, so we actually need
|
||||
// to lock here.
|
||||
void
|
||||
profiler_add_marker_for_thread(int aThreadId,
|
||||
const char* aMarkerName,
|
||||
UniquePtr<ProfilerMarkerPayload> aPayload)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(CorePS::Exists());
|
||||
|
||||
// Create the ProfilerMarker which we're going to store.
|
||||
TimeStamp origin = (aPayload && !aPayload->GetStartTime().IsNull())
|
||||
? aPayload->GetStartTime()
|
||||
: TimeStamp::Now();
|
||||
TimeDuration delta = origin - CorePS::ProcessStartTime();
|
||||
ProfilerMarker* marker =
|
||||
new ProfilerMarker(aMarkerName, aThreadId, Move(aPayload),
|
||||
delta.ToMilliseconds());
|
||||
|
||||
PSAutoLock lock(gPSMutex);
|
||||
|
||||
#ifdef DEBUG
|
||||
// Assert that our thread ID makes sense
|
||||
bool realThread = false;
|
||||
const CorePS::ThreadVector& liveThreads = CorePS::LiveThreads(lock);
|
||||
for (uint32_t i = 0; i < liveThreads.size(); i++) {
|
||||
ThreadInfo* info = liveThreads.at(i);
|
||||
if (info->ThreadId() == aThreadId) {
|
||||
realThread = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT(realThread, "Invalid thread id");
|
||||
#endif
|
||||
|
||||
// Insert the marker into the buffer
|
||||
ProfileBuffer& buffer = ActivePS::Buffer(lock);
|
||||
buffer.AddStoredMarker(marker);
|
||||
buffer.AddEntry(ProfileBufferEntry::Marker(marker));
|
||||
}
|
||||
|
||||
void
|
||||
profiler_tracing(const char* aCategory, const char* aMarkerName,
|
||||
TracingKind aKind)
|
||||
|
@ -462,6 +462,11 @@ void profiler_add_marker(const char* aMarkerName);
|
||||
void profiler_add_marker(const char* aMarkerName,
|
||||
mozilla::UniquePtr<ProfilerMarkerPayload> aPayload);
|
||||
|
||||
// Insert a marker in the profile timeline for a specified thread.
|
||||
void profiler_add_marker_for_thread(int aThreadId,
|
||||
const char* aMarkerName,
|
||||
mozilla::UniquePtr<ProfilerMarkerPayload> aPayload);
|
||||
|
||||
enum TracingKind {
|
||||
TRACING_EVENT,
|
||||
TRACING_INTERVAL_START,
|
||||
|
@ -168,7 +168,9 @@ private:
|
||||
{
|
||||
MOZ_ASSERT(aSize <= Available());
|
||||
char* p = reinterpret_cast<char*>(header.offset);
|
||||
MOZ_RELEASE_ASSERT(p);
|
||||
header.offset += aSize;
|
||||
canary.Check();
|
||||
MOZ_MAKE_MEM_UNDEFINED(p, aSize);
|
||||
return p;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user