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
|
// should be switched back
|
||||||
ok(openedTab.selected, "Ta-dah, the other tab should now be selected again!");
|
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);
|
await BrowserTestUtils.removeTab(openedTab);
|
||||||
});
|
});
|
||||||
|
@ -4,6 +4,7 @@ MOZ_AUTOMATION_L10N_CHECK=0
|
|||||||
|
|
||||||
. "$topsrcdir/build/mozconfig.win-common"
|
. "$topsrcdir/build/mozconfig.win-common"
|
||||||
. "$topsrcdir/browser/config/mozconfigs/common"
|
. "$topsrcdir/browser/config/mozconfigs/common"
|
||||||
|
. "$topsrcdir/browser/config/mozconfigs/win64/common-win64"
|
||||||
|
|
||||||
ac_add_options --enable-optimize
|
ac_add_options --enable-optimize
|
||||||
ac_add_options --enable-debug
|
ac_add_options --enable-debug
|
||||||
@ -11,7 +12,7 @@ ac_add_options --enable-debug
|
|||||||
ac_add_options --enable-clang-plugin
|
ac_add_options --enable-clang-plugin
|
||||||
ac_add_options --enable-mozsearch-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.common.override"
|
||||||
. "$topsrcdir/build/mozconfig.clang-cl"
|
. "$topsrcdir/build/mozconfig.clang-cl"
|
@ -279,6 +279,22 @@ this.TestRunner = {
|
|||||||
return {bounds, rects};
|
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) {
|
async _performCombo(combo) {
|
||||||
let paddedComboIndex = padLeft(this.currentComboIndex + 1, String(this.combos.length).length);
|
let paddedComboIndex = padLeft(this.currentComboIndex + 1, String(this.combos.length).length);
|
||||||
this.mochitestScope.info(
|
this.mochitestScope.info(
|
||||||
@ -298,9 +314,9 @@ this.TestRunner = {
|
|||||||
this.mochitestScope.info("called " + config.name);
|
this.mochitestScope.info("called " + config.name);
|
||||||
// Add a default timeout of 500ms to avoid conflicts when configurations
|
// Add a default timeout of 500ms to avoid conflicts when configurations
|
||||||
// try to apply at the same time. e.g WindowSize and TabsInTitlebar
|
// 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) => {
|
return new Promise((resolve) => {
|
||||||
setTimeout(resolve, 500);
|
setTimeout(() => resolve(result), 500);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -311,7 +327,11 @@ this.TestRunner = {
|
|||||||
let config = combo[i];
|
let config = combo[i];
|
||||||
if (!this._lastCombo || config !== this._lastCombo[i]) {
|
if (!this._lastCombo || config !== this._lastCombo[i]) {
|
||||||
this.mochitestScope.info(`promising ${config.name}`);
|
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.
|
// have invalidated it.
|
||||||
if (config.verifyConfig) {
|
if (config.verifyConfig) {
|
||||||
this.mochitestScope.info(`checking if the combo is valid with ${config.name}`);
|
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) {
|
} catch (ex) {
|
||||||
this.mochitestScope.info(`\tskipped configuration [ ${combo.map((e) => e.name).join(", ")} ]`);
|
this.mochitestScope.ok(false, `Unexpected exception in [ ${combo.map(({ name }) => name).join(", ")} ]: ${ex.toString()}`);
|
||||||
this.mochitestScope.info(`\treason: ${ex.toString()}`);
|
this.mochitestScope.info(`\t${ex}`);
|
||||||
// Don't set lastCombo here so that we properly know which configurations
|
if (ex.stack) {
|
||||||
// need to be applied since the last screenshot
|
this.mochitestScope.info(`\t${ex.stack}`);
|
||||||
|
}
|
||||||
// Return so we don't take a screenshot.
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this.mochitestScope.info(`Configured UI for [ ${combo.map(({ name }) => name).join(", ")} ] successfully`);
|
||||||
|
|
||||||
// Collect selectors from combo configs for cropping region
|
// Collect selectors from combo configs for cropping region
|
||||||
let windowType;
|
let windowType;
|
||||||
|
@ -35,17 +35,17 @@ this.AppMenu = {
|
|||||||
|
|
||||||
appMenuHistorySubview: {
|
appMenuHistorySubview: {
|
||||||
selectors: ["#appMenu-popup"],
|
selectors: ["#appMenu-popup"],
|
||||||
applyConfig() {
|
async applyConfig() {
|
||||||
// History has a footer
|
// History has a footer
|
||||||
if (isCustomizing()) {
|
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 browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||||
let promise = browserWindow.PanelUI.show();
|
await browserWindow.PanelUI.show();
|
||||||
return promise.then(() => {
|
|
||||||
browserWindow.PanelUI.showMainView();
|
browserWindow.PanelUI.showMainView();
|
||||||
browserWindow.document.getElementById("history-panelmenu").click();
|
browserWindow.document.getElementById("history-panelmenu").click();
|
||||||
});
|
|
||||||
|
return undefined;
|
||||||
},
|
},
|
||||||
|
|
||||||
verifyConfig: verifyConfigHelper,
|
verifyConfig: verifyConfigHelper,
|
||||||
@ -53,16 +53,16 @@ this.AppMenu = {
|
|||||||
|
|
||||||
appMenuHelpSubview: {
|
appMenuHelpSubview: {
|
||||||
selectors: ["#appMenu-popup"],
|
selectors: ["#appMenu-popup"],
|
||||||
applyConfig() {
|
async applyConfig() {
|
||||||
if (isCustomizing()) {
|
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 browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||||
let promise = browserWindow.PanelUI.show();
|
await browserWindow.PanelUI.show();
|
||||||
return promise.then(() => {
|
|
||||||
browserWindow.PanelUI.showMainView();
|
browserWindow.PanelUI.showMainView();
|
||||||
browserWindow.document.getElementById("PanelUI-help").click();
|
browserWindow.document.getElementById("PanelUI-help").click();
|
||||||
});
|
|
||||||
|
return undefined;
|
||||||
},
|
},
|
||||||
|
|
||||||
verifyConfig: verifyConfigHelper,
|
verifyConfig: verifyConfigHelper,
|
||||||
@ -73,9 +73,9 @@ this.AppMenu = {
|
|||||||
|
|
||||||
function verifyConfigHelper() {
|
function verifyConfigHelper() {
|
||||||
if (isCustomizing()) {
|
if (isCustomizing()) {
|
||||||
return Promise.reject("AppMenu verifyConfigHelper");
|
return "navigator:browser has the customizing attribute";
|
||||||
}
|
}
|
||||||
return Promise.resolve("AppMenu verifyConfigHelper");
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isCustomizing() {
|
function isCustomizing() {
|
||||||
|
@ -38,12 +38,12 @@ this.Buttons = {
|
|||||||
CustomizableUI.addWidgetToArea("screenshot-widget", CustomizableUI.AREA_FIXED_OVERFLOW_PANEL);
|
CustomizableUI.addWidgetToArea("screenshot-widget", CustomizableUI.AREA_FIXED_OVERFLOW_PANEL);
|
||||||
},
|
},
|
||||||
|
|
||||||
verifyConfig() {
|
async verifyConfig() {
|
||||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||||
if (browserWindow.PanelUI.panel.state == "closed") {
|
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");
|
CustomizableUI.removeWidgetFromArea("screenshot-widget");
|
||||||
},
|
},
|
||||||
|
|
||||||
verifyConfig() {
|
async verifyConfig() {
|
||||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||||
if (browserWindow.document.documentElement.getAttribute("customizing") != "true") {
|
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() {
|
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
|
// We want to skip the progress-notification, so we wait for
|
||||||
// the install-confirmation screen to be "not hidden" = shown.
|
// the install-confirmation screen to be "not hidden" = shown.
|
||||||
await BrowserTestUtils.waitForCondition(() => !notification.hasAttribute("hidden"),
|
return BrowserTestUtils.waitForCondition(() => !notification.hasAttribute("hidden"),
|
||||||
"addon install confirmation did not show", 200);
|
"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] = {};
|
||||||
this.configurations[configName].selectors = ["#browser"];
|
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);
|
this.configurations[configName].applyConfig = prefHelper.bind(null, primary, customFn);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
configurations: {},
|
configurations: {},
|
||||||
@ -49,7 +55,9 @@ let prefHelper = async function(primary, customFn = null) {
|
|||||||
|
|
||||||
// close any dialog that might still be open
|
// close any dialog that might still be open
|
||||||
await ContentTask.spawn(selectedBrowser, null, async function() {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
content.window.gSubDialog.close();
|
content.window.gSubDialog.close();
|
||||||
@ -73,9 +81,11 @@ let prefHelper = async function(primary, customFn = null) {
|
|||||||
|
|
||||||
if (customFn) {
|
if (customFn) {
|
||||||
let customPaintPromise = paintPromise(browserWindow);
|
let customPaintPromise = paintPromise(browserWindow);
|
||||||
await customFn(selectedBrowser);
|
let result = await customFn(selectedBrowser);
|
||||||
await customPaintPromise;
|
await customPaintPromise;
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
function paintPromise(browserWindow) {
|
function paintPromise(browserWindow) {
|
||||||
@ -99,8 +109,13 @@ async function cacheGroup(aBrowser) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function DNTDialog(aBrowser) {
|
async function DNTDialog(aBrowser) {
|
||||||
await ContentTask.spawn(aBrowser, null, async function() {
|
return ContentTask.spawn(aBrowser, null, async function() {
|
||||||
content.document.getElementById("doNotTrackSettings").click();
|
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]);
|
hoverTab(browserWindow.gBrowser.tabs[2]);
|
||||||
// also hover the new tab button
|
// also hover the new tab button
|
||||||
let newTabButton = browserWindow.document.getAnonymousElementByAttribute(browserWindow.
|
let newTabButton = browserWindow.document.getAnonymousElementByAttribute(browserWindow.
|
||||||
gBrowser.tabContainer, "class", "tabs-newtab-button");
|
gBrowser.tabContainer, "anonid", "tabs-newtab-button");
|
||||||
hoverTab(newTabButton);
|
hoverTab(newTabButton);
|
||||||
browserWindow.gBrowser.tabs[browserWindow.gBrowser.tabs.length - 1].
|
browserWindow.gBrowser.tabs[browserWindow.gBrowser.tabs.length - 1].
|
||||||
setAttribute("beforehovered", true);
|
setAttribute("beforehovered", true);
|
||||||
@ -134,6 +134,7 @@ async function allTabTitlesDisplayed(browserWindow) {
|
|||||||
"about:home": "New Tab",
|
"about:home": "New Tab",
|
||||||
"about:newtab": "New Tab",
|
"about:newtab": "New Tab",
|
||||||
"about:addons": "Add-ons Manager",
|
"about:addons": "Add-ons Manager",
|
||||||
|
"about:privatebrowsing": "Open a private window?"
|
||||||
};
|
};
|
||||||
specToTitleMap[PREFS_TAB] = "browser/skin/settings.svg";
|
specToTitleMap[PREFS_TAB] = "browser/skin/settings.svg";
|
||||||
specToTitleMap[CUST_TAB] = "browser/skin/customize.svg";
|
specToTitleMap[CUST_TAB] = "browser/skin/customize.svg";
|
||||||
|
@ -21,7 +21,7 @@ this.TabsInTitlebar = {
|
|||||||
selectors: ["#navigator-toolbox"],
|
selectors: ["#navigator-toolbox"],
|
||||||
async applyConfig() {
|
async applyConfig() {
|
||||||
if (Services.appinfo.OS == "Linux") {
|
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);
|
Services.prefs.setBoolPref(PREF_TABS_IN_TITLEBAR, true);
|
||||||
return undefined;
|
return undefined;
|
||||||
|
@ -36,7 +36,7 @@ this.Toolbars = {
|
|||||||
async verifyConfig() {
|
async verifyConfig() {
|
||||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||||
if (browserWindow.fullScreen) {
|
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;
|
return undefined;
|
||||||
},
|
},
|
||||||
|
@ -11,6 +11,10 @@ add_task(async function capture() {
|
|||||||
return;
|
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"];
|
let sets = ["TabsInTitlebar", "Tabs", "WindowSize", "Toolbars", "LightweightThemes", "UIDensities"];
|
||||||
await TestRunner.start(sets, "primaryUI");
|
await TestRunner.start(sets, "primaryUI");
|
||||||
});
|
});
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
This is the debugger.html project output.
|
This is the debugger.html project output.
|
||||||
See https://github.com/devtools-html/debugger.html
|
See https://github.com/devtools-html/debugger.html
|
||||||
|
|
||||||
Version 8.0
|
Version 9.0
|
||||||
commit https://github.com/devtools-html/debugger.html/commit/9be8e9d9e7a70730bc02e50ae019028f1f06b14e
|
commit: https://github.com/devtools-html/debugger.html/commit/0de8d3f673ee0f0030d666f1827380e17bef8036
|
||||||
comparison https://github.com/devtools-html/debugger.html/compare/release-7...release-8
|
comparison: https://github.com/devtools-html/debugger.html/compare/release-8...release-9
|
||||||
|
|
||||||
Packages:
|
Packages:
|
||||||
- babel-plugin-transform-es2015-modules-commonjs @6.26.0
|
- babel-plugin-transform-es2015-modules-commonjs @6.26.0
|
||||||
|
@ -1568,7 +1568,7 @@ html[dir="rtl"] .managed-tree .tree .node > div {
|
|||||||
.outline-list {
|
.outline-list {
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 10px;
|
padding: 10px 0px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1581,7 +1581,7 @@ html[dir="rtl"] .managed-tree .tree .node > div {
|
|||||||
.outline-list h2 {
|
.outline-list h2 {
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
margin: 10px 0;
|
margin: 10px 0 10px 10px;
|
||||||
color: var(--theme-body-color);
|
color: var(--theme-body-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1596,6 +1596,10 @@ html[dir="rtl"] .managed-tree .tree .node > div {
|
|||||||
.outline-list__element:hover {
|
.outline-list__element:hover {
|
||||||
background: var(--theme-toolbar-background-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
|
/* 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
|
* 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/>. */
|
* 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
|
* 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/>. */
|
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||||
|
|
||||||
|
.expression-input-form {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.input-expression {
|
.input-expression {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 0px;
|
margin: 0;
|
||||||
border: 1px;
|
border: 1px;
|
||||||
background-color: var(--theme-sidebar-background);
|
background-color: var(--theme-sidebar-background);
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
padding: 0px 20px;
|
padding: 0.5em 2.16em;
|
||||||
color: var(--theme-body-color);
|
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 {
|
.input-expression::placeholder {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
@ -2877,7 +2905,6 @@ html .breakpoints-list .breakpoint.paused {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
.expression-input-container {
|
.expression-input-container {
|
||||||
padding: 0.5em;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3367,6 +3394,7 @@ html[dir="rtl"] .dropdown {
|
|||||||
padding: 0px 2px;
|
padding: 0px 2px;
|
||||||
position: relative;
|
position: relative;
|
||||||
align-self: center;
|
align-self: center;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-button {
|
.dropdown-button {
|
||||||
@ -3376,6 +3404,8 @@ html[dir="rtl"] .dropdown {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
font-weight: 100;
|
font-weight: 100;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
height: 100%;
|
||||||
|
width: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown li {
|
.dropdown li {
|
||||||
@ -3396,6 +3426,7 @@ html[dir="rtl"] .dropdown {
|
|||||||
height: var(--icon-size);
|
height: var(--icon-size);
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-icon.prettyPrint {
|
.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"
|
"Breakpoint has correct line"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await addBreakpoint(dbg, entrySrc, 5);
|
||||||
await addBreakpoint(dbg, entrySrc, 15);
|
await addBreakpoint(dbg, entrySrc, 15);
|
||||||
await disableBreakpoint(dbg, entrySrc, 15);
|
await disableBreakpoint(dbg, entrySrc, 15);
|
||||||
|
|
||||||
@ -34,7 +35,10 @@ add_task(async function() {
|
|||||||
await reload(dbg, "opts.js");
|
await reload(dbg, "opts.js");
|
||||||
await waitForDispatch(dbg, "LOAD_SOURCE_TEXT");
|
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(
|
ok(
|
||||||
getBreakpoint(getState(), { sourceId: entrySrc.id, line: 13 }),
|
getBreakpoint(getState(), { sourceId: entrySrc.id, line: 13 }),
|
||||||
|
@ -439,6 +439,9 @@ original=original
|
|||||||
# LOCALIZATION NOTE (expressions.placeholder): Placeholder text for expression
|
# LOCALIZATION NOTE (expressions.placeholder): Placeholder text for expression
|
||||||
# input element
|
# input element
|
||||||
expressions.placeholder=Add watch expression
|
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.label=Add watch expression
|
||||||
expressions.accesskey=e
|
expressions.accesskey=e
|
||||||
|
|
||||||
|
@ -5273,32 +5273,37 @@ nsContentUtils::SetNodeTextContent(nsIContent* aContent,
|
|||||||
UPDATE_CONTENT_MODEL, true);
|
UPDATE_CONTENT_MODEL, true);
|
||||||
nsAutoMutationBatch mb;
|
nsAutoMutationBatch mb;
|
||||||
|
|
||||||
uint32_t childCount = aContent->GetChildCount();
|
|
||||||
|
|
||||||
if (aTryReuse && !aValue.IsEmpty()) {
|
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) {
|
// If we have a node, it must be a eTEXT and we reuse it.
|
||||||
nsIContent* child = aContent->GetChildAt_Deprecated(removeIndex);
|
if (aContent->HasChildren()) {
|
||||||
if (removeIndex == 0 && child && child->IsNodeOfType(nsINode::eTEXT)) {
|
nsIContent* child = aContent->GetFirstChild();
|
||||||
|
MOZ_ASSERT(child->IsNodeOfType(nsINode::eTEXT));
|
||||||
nsresult rv = child->SetText(aValue, true);
|
nsresult rv = child->SetText(aValue, true);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
removeIndex = 1;
|
// All the following nodes, if they exist, must be deleted.
|
||||||
}
|
while (nsIContent* nextChild = child->GetNextSibling()) {
|
||||||
else {
|
aContent->RemoveChildNode(nextChild, true);
|
||||||
aContent->RemoveChildAt_Deprecated(removeIndex, true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (removeIndex == 1) {
|
if (aContent->HasChildren()) {
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
mb.Init(aContent, true, false);
|
mb.Init(aContent, true, false);
|
||||||
for (uint32_t i = 0; i < childCount; ++i) {
|
while (aContent->HasChildren()) {
|
||||||
aContent->RemoveChildAt_Deprecated(0, true);
|
aContent->RemoveChildNode(aContent->GetFirstChild(), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mb.RemovalDone();
|
mb.RemovalDone();
|
||||||
|
@ -804,19 +804,9 @@ WebGLContext::AssertCachedGlobalState() const
|
|||||||
gl->fIsEnabled(LOCAL_GL_RASTERIZER_DISCARD) == mRasterizerDiscardEnabled);
|
gl->fIsEnabled(LOCAL_GL_RASTERIZER_DISCARD) == mRasterizerDiscardEnabled);
|
||||||
MOZ_ASSERT(gl->fIsEnabled(LOCAL_GL_SCISSOR_TEST) == mScissorTestEnabled);
|
MOZ_ASSERT(gl->fIsEnabled(LOCAL_GL_SCISSOR_TEST) == mScissorTestEnabled);
|
||||||
|
|
||||||
GLfloat colorClearValue[4] = {0.0f, 0.0f, 0.0f, 0.0f};
|
// Cannot trivially check COLOR_CLEAR_VALUE, since in old GL versions glGet may clamp
|
||||||
gl->fGetFloatv(LOCAL_GL_COLOR_CLEAR_VALUE, colorClearValue);
|
// based on whether the current framebuffer is floating-point or not.
|
||||||
const bool ok = IsCacheCorrect(mColorClearValue[0], colorClearValue[0]) &&
|
// This also means COLOR_CLEAR_VALUE save+restore is dangerous!
|
||||||
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);
|
|
||||||
|
|
||||||
realGLboolean depthWriteMask = 0;
|
realGLboolean depthWriteMask = 0;
|
||||||
gl->fGetBooleanv(LOCAL_GL_DEPTH_WRITEMASK, &depthWriteMask);
|
gl->fGetBooleanv(LOCAL_GL_DEPTH_WRITEMASK, &depthWriteMask);
|
||||||
|
@ -34,27 +34,6 @@ WebGLExtensionColorBufferFloat::WebGLExtensionColorBufferFloat(WebGLContext* web
|
|||||||
FOO(RGBA32F);
|
FOO(RGBA32F);
|
||||||
|
|
||||||
#undef FOO
|
#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()
|
WebGLExtensionColorBufferFloat::~WebGLExtensionColorBufferFloat()
|
||||||
|
@ -7596,7 +7596,7 @@ fail-if = (os == 'android')
|
|||||||
[generated/test_conformance__extensions__oes-texture-half-float-with-video.html]
|
[generated/test_conformance__extensions__oes-texture-half-float-with-video.html]
|
||||||
fail-if = (os == 'mac') || (os == 'android') || (os == 'linux') || (os == 'win')
|
fail-if = (os == 'mac') || (os == 'android') || (os == 'linux') || (os == 'win')
|
||||||
[generated/test_conformance__extensions__oes-texture-half-float.html]
|
[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-bufferData.html]
|
||||||
[generated/test_conformance__extensions__oes-vertex-array-object.html]
|
[generated/test_conformance__extensions__oes-vertex-array-object.html]
|
||||||
skip-if = (os == 'mac' && os_version == '10.6')
|
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]
|
[generated/test_2_conformance2__vertex_arrays__vertex-array-object.html]
|
||||||
fail-if = (os == 'mac') || (os == 'win')
|
fail-if = (os == 'mac') || (os == 'win')
|
||||||
[generated/test_conformance__extensions__oes-texture-half-float.html]
|
[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]
|
[generated/test_conformance__attribs__gl-vertexattribpointer.html]
|
||||||
fail-if = (os == 'android')
|
fail-if = (os == 'android')
|
||||||
[generated/test_conformance__ogles__GL__biuDepthRange__biuDepthRange_001_to_002.html]
|
[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]
|
[ensure-exts/test_EXT_blend_minmax.html]
|
||||||
fail-if = (os == 'android')
|
fail-if = (os == 'android')
|
||||||
[ensure-exts/test_EXT_color_buffer_half_float.html]
|
[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]
|
[ensure-exts/test_EXT_disjoint_timer_query.html]
|
||||||
fail-if = (os == 'android') || (os == 'win' && os_version == '5.1')
|
fail-if = (os == 'android') || (os == 'win' && os_version == '5.1')
|
||||||
[ensure-exts/test_EXT_frag_depth.html]
|
[ensure-exts/test_EXT_frag_depth.html]
|
||||||
@ -31,7 +31,7 @@ fail-if = (os == 'android') || (os == 'linux')
|
|||||||
[ensure-exts/test_OES_standard_derivatives.html]
|
[ensure-exts/test_OES_standard_derivatives.html]
|
||||||
fail-if = (os == 'android')
|
fail-if = (os == 'android')
|
||||||
[ensure-exts/test_WEBGL_color_buffer_float.html]
|
[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]
|
[ensure-exts/test_WEBGL_compressed_texture_atc.html]
|
||||||
fail-if = (os == 'android') || (os == 'linux') || (os == 'mac') || (os == 'win')
|
fail-if = (os == 'android') || (os == 'linux') || (os == 'mac') || (os == 'win')
|
||||||
[ensure-exts/test_WEBGL_compressed_texture_es3.html]
|
[ensure-exts/test_WEBGL_compressed_texture_es3.html]
|
||||||
|
@ -73,6 +73,7 @@
|
|||||||
#include "mozilla/media/MediaChild.h"
|
#include "mozilla/media/MediaChild.h"
|
||||||
#include "mozilla/BasePrincipal.h"
|
#include "mozilla/BasePrincipal.h"
|
||||||
#include "mozilla/WebBrowserPersistDocumentChild.h"
|
#include "mozilla/WebBrowserPersistDocumentChild.h"
|
||||||
|
#include "mozilla/HangDetails.h"
|
||||||
#include "imgLoader.h"
|
#include "imgLoader.h"
|
||||||
#include "GMPServiceChild.h"
|
#include "GMPServiceChild.h"
|
||||||
#include "NullPrincipal.h"
|
#include "NullPrincipal.h"
|
||||||
|
@ -100,6 +100,7 @@
|
|||||||
#include "mozilla/WebBrowserPersistDocumentParent.h"
|
#include "mozilla/WebBrowserPersistDocumentParent.h"
|
||||||
#include "mozilla/widget/ScreenManager.h"
|
#include "mozilla/widget/ScreenManager.h"
|
||||||
#include "mozilla/Unused.h"
|
#include "mozilla/Unused.h"
|
||||||
|
#include "mozilla/HangDetails.h"
|
||||||
#include "nsAnonymousTemporaryFile.h"
|
#include "nsAnonymousTemporaryFile.h"
|
||||||
#include "nsAppRunner.h"
|
#include "nsAppRunner.h"
|
||||||
#include "nsCDefaultURIFixup.h"
|
#include "nsCDefaultURIFixup.h"
|
||||||
|
@ -60,6 +60,7 @@ include ServiceWorkerConfiguration;
|
|||||||
include GraphicsMessages;
|
include GraphicsMessages;
|
||||||
include MemoryReportTypes;
|
include MemoryReportTypes;
|
||||||
include ClientIPCTypes;
|
include ClientIPCTypes;
|
||||||
|
include HangTypes;
|
||||||
|
|
||||||
// Workaround to prevent error if PContentChild.cpp & PContentBridgeParent.cpp
|
// Workaround to prevent error if PContentChild.cpp & PContentBridgeParent.cpp
|
||||||
// are put into different UnifiedProtocolsXX.cpp files.
|
// 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::ChildEventData from "mozilla/TelemetryComms.h";
|
||||||
using mozilla::Telemetry::DiscardedData from "mozilla/TelemetryComms.h";
|
using mozilla::Telemetry::DiscardedData from "mozilla/TelemetryComms.h";
|
||||||
using mozilla::CrossProcessMutexHandle from "mozilla/ipc/CrossProcessMutex.h";
|
using mozilla::CrossProcessMutexHandle from "mozilla/ipc/CrossProcessMutex.h";
|
||||||
using mozilla::HangDetails from "mozilla/HangDetails.h";
|
|
||||||
|
|
||||||
union ChromeRegistryItem
|
union ChromeRegistryItem
|
||||||
{
|
{
|
||||||
|
@ -73,7 +73,7 @@ namespace {
|
|||||||
void* sServerHandle = nullptr;
|
void* sServerHandle = nullptr;
|
||||||
|
|
||||||
// Initialized during early startup, protected by sMutex.
|
// Initialized during early startup, protected by sMutex.
|
||||||
ipc::FileDescriptor sIPCConnection;
|
StaticAutoPtr<ipc::FileDescriptor> sIPCConnection;
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
StartSoundServer()
|
StartSoundServer()
|
||||||
@ -204,6 +204,7 @@ static const uint32_t CUBEB_NORMAL_LATENCY_MS = 100;
|
|||||||
static const uint32_t CUBEB_NORMAL_LATENCY_FRAMES = 1024;
|
static const uint32_t CUBEB_NORMAL_LATENCY_FRAMES = 1024;
|
||||||
|
|
||||||
namespace CubebUtils {
|
namespace CubebUtils {
|
||||||
|
cubeb* GetCubebContextUnlocked();
|
||||||
|
|
||||||
void PrefChanged(const char* aPref, void* aClosure)
|
void PrefChanged(const char* aPref, void* aClosure)
|
||||||
{
|
{
|
||||||
@ -405,12 +406,12 @@ void InitAudioIPCConnection()
|
|||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
auto contentChild = dom::ContentChild::GetSingleton();
|
auto contentChild = dom::ContentChild::GetSingleton();
|
||||||
auto promise = contentChild->SendCreateAudioIPCConnection();
|
auto promise = contentChild->SendCreateAudioIPCConnection();
|
||||||
promise->Then(AbstractThread::GetCurrent(),
|
promise->Then(AbstractThread::MainThread(),
|
||||||
__func__,
|
__func__,
|
||||||
[](ipc::FileDescriptor aFD) {
|
[](ipc::FileDescriptor aFD) {
|
||||||
StaticMutexAutoLock lock(sMutex);
|
StaticMutexAutoLock lock(sMutex);
|
||||||
MOZ_ASSERT(!sIPCConnection.IsValid());
|
MOZ_ASSERT(!sIPCConnection);
|
||||||
sIPCConnection = aFD;
|
sIPCConnection = new ipc::FileDescriptor(aFD);
|
||||||
},
|
},
|
||||||
[](mozilla::ipc::ResponseRejectReason aReason) {
|
[](mozilla::ipc::ResponseRejectReason aReason) {
|
||||||
MOZ_LOG(gCubebLog, LogLevel::Error, ("SendCreateAudioIPCConnection failed: %d",
|
MOZ_LOG(gCubebLog, LogLevel::Error, ("SendCreateAudioIPCConnection failed: %d",
|
||||||
@ -452,10 +453,10 @@ cubeb* GetCubebContextUnlocked()
|
|||||||
if (sCubebSandbox) {
|
if (sCubebSandbox) {
|
||||||
if (XRE_IsParentProcess()) {
|
if (XRE_IsParentProcess()) {
|
||||||
// TODO: Don't use audio IPC when within the same process.
|
// TODO: Don't use audio IPC when within the same process.
|
||||||
MOZ_ASSERT(!sIPCConnection.IsValid());
|
MOZ_ASSERT(!sIPCConnection);
|
||||||
sIPCConnection = CreateAudioIPCConnection();
|
sIPCConnection = new ipc::FileDescriptor(CreateAudioIPCConnection());
|
||||||
} else {
|
} else {
|
||||||
MOZ_DIAGNOSTIC_ASSERT(sIPCConnection.IsValid());
|
MOZ_DIAGNOSTIC_ASSERT(sIPCConnection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -463,8 +464,9 @@ cubeb* GetCubebContextUnlocked()
|
|||||||
|
|
||||||
int rv = sCubebSandbox
|
int rv = sCubebSandbox
|
||||||
? audioipc_client_init(&sCubebContext, sBrandName,
|
? audioipc_client_init(&sCubebContext, sBrandName,
|
||||||
sIPCConnection.ClonePlatformHandle().release())
|
sIPCConnection->ClonePlatformHandle().release())
|
||||||
: cubeb_init(&sCubebContext, sBrandName, sCubebBackendName.get());
|
: cubeb_init(&sCubebContext, sBrandName, sCubebBackendName.get());
|
||||||
|
sIPCConnection = nullptr;
|
||||||
#else // !MOZ_CUBEB_REMOTING
|
#else // !MOZ_CUBEB_REMOTING
|
||||||
int rv = cubeb_init(&sCubebContext, sBrandName, sCubebBackendName.get());
|
int rv = cubeb_init(&sCubebContext, sBrandName, sCubebBackendName.get());
|
||||||
#endif // MOZ_CUBEB_REMOTING
|
#endif // MOZ_CUBEB_REMOTING
|
||||||
@ -559,8 +561,7 @@ void InitLibrary()
|
|||||||
#endif
|
#endif
|
||||||
#ifdef MOZ_CUBEB_REMOTING
|
#ifdef MOZ_CUBEB_REMOTING
|
||||||
if (sCubebSandbox && XRE_IsContentProcess()) {
|
if (sCubebSandbox && XRE_IsContentProcess()) {
|
||||||
AbstractThread::MainThread()->Dispatch(
|
InitAudioIPCConnection();
|
||||||
NS_NewRunnableFunction("CubebUtils::InitLibrary", &InitAudioIPCConnection));
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -585,7 +586,7 @@ void ShutdownLibrary()
|
|||||||
sCubebState = CubebState::Shutdown;
|
sCubebState = CubebState::Shutdown;
|
||||||
|
|
||||||
#ifdef MOZ_CUBEB_REMOTING
|
#ifdef MOZ_CUBEB_REMOTING
|
||||||
sIPCConnection = ipc::FileDescriptor();
|
sIPCConnection = nullptr;
|
||||||
ShutdownSoundServer();
|
ShutdownSoundServer();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -38,11 +38,9 @@ enum Side {
|
|||||||
Output
|
Output
|
||||||
};
|
};
|
||||||
|
|
||||||
void PrefChanged(const char* aPref, void* aClosure);
|
|
||||||
double GetVolumeScale();
|
double GetVolumeScale();
|
||||||
bool GetFirstStream();
|
bool GetFirstStream();
|
||||||
cubeb* GetCubebContext();
|
cubeb* GetCubebContext();
|
||||||
cubeb* GetCubebContextUnlocked();
|
|
||||||
void ReportCubebStreamInitFailure(bool aIsFirstStream);
|
void ReportCubebStreamInitFailure(bool aIsFirstStream);
|
||||||
void ReportCubebBackendUsed();
|
void ReportCubebBackendUsed();
|
||||||
uint32_t GetCubebPlaybackLatencyInMilliseconds();
|
uint32_t GetCubebPlaybackLatencyInMilliseconds();
|
||||||
|
@ -53,11 +53,6 @@ public:
|
|||||||
static const int DEFAULT_169_VIDEO_WIDTH = 1280;
|
static const int DEFAULT_169_VIDEO_WIDTH = 1280;
|
||||||
static const int DEFAULT_169_VIDEO_HEIGHT = 720;
|
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
|
/* Populate an array of video sources in the nsTArray. Also include devices
|
||||||
* that are currently unavailable. */
|
* that are currently unavailable. */
|
||||||
virtual void EnumerateVideoDevices(dom::MediaSourceEnum,
|
virtual void EnumerateVideoDevices(dom::MediaSourceEnum,
|
||||||
|
@ -543,7 +543,6 @@ private:
|
|||||||
nsString mDeviceName;
|
nsString mDeviceName;
|
||||||
nsCString mDeviceUUID;
|
nsCString mDeviceUUID;
|
||||||
|
|
||||||
int32_t mSampleFrequency;
|
|
||||||
uint64_t mTotalFrames;
|
uint64_t mTotalFrames;
|
||||||
uint64_t mLastLogFrames;
|
uint64_t mLastLogFrames;
|
||||||
|
|
||||||
|
@ -70,7 +70,6 @@ MediaEngineWebRTCMicrophoneSource::MediaEngineWebRTCMicrophoneSource(
|
|||||||
, mExtendedFilter(aExtendedFilter)
|
, mExtendedFilter(aExtendedFilter)
|
||||||
, mTrackID(TRACK_NONE)
|
, mTrackID(TRACK_NONE)
|
||||||
, mStarted(false)
|
, mStarted(false)
|
||||||
, mSampleFrequency(MediaEngine::USE_GRAPH_RATE)
|
|
||||||
, mTotalFrames(0)
|
, mTotalFrames(0)
|
||||||
, mLastLogFrames(0)
|
, mLastLogFrames(0)
|
||||||
, mSkipProcessing(false)
|
, mSkipProcessing(false)
|
||||||
@ -481,9 +480,7 @@ MediaEngineWebRTCMicrophoneSource::Start(SourceMediaStream *aStream,
|
|||||||
}
|
}
|
||||||
|
|
||||||
AudioSegment* segment = new AudioSegment();
|
AudioSegment* segment = new AudioSegment();
|
||||||
if (mSampleFrequency == MediaEngine::USE_GRAPH_RATE) {
|
|
||||||
mSampleFrequency = aStream->GraphRate();
|
|
||||||
}
|
|
||||||
aStream->AddAudioTrack(aID, aStream->GraphRate(), 0, segment, SourceMediaStream::ADDTRACK_QUEUED);
|
aStream->AddAudioTrack(aID, aStream->GraphRate(), 0, segment, SourceMediaStream::ADDTRACK_QUEUED);
|
||||||
|
|
||||||
// XXX Make this based on the pref.
|
// XXX Make this based on the pref.
|
||||||
@ -773,7 +770,7 @@ MediaEngineWebRTCMicrophoneSource::InsertInGraph(const T* aBuffer,
|
|||||||
|
|
||||||
if (MOZ_LOG_TEST(AudioLogModule(), LogLevel::Debug)) {
|
if (MOZ_LOG_TEST(AudioLogModule(), LogLevel::Debug)) {
|
||||||
mTotalFrames += aFrames;
|
mTotalFrames += aFrames;
|
||||||
if (mTotalFrames > mLastLogFrames + mSampleFrequency) { // ~ 1 second
|
if (mTotalFrames > mLastLogFrames + mSources[0]->GraphRate()) { // ~ 1 second
|
||||||
MOZ_LOG(AudioLogModule(), LogLevel::Debug,
|
MOZ_LOG(AudioLogModule(), LogLevel::Debug,
|
||||||
("%p: Inserting %zu samples into graph, total frames = %" PRIu64,
|
("%p: Inserting %zu samples into graph, total frames = %" PRIu64,
|
||||||
(void*)this, aFrames, mTotalFrames));
|
(void*)this, aFrames, mTotalFrames));
|
||||||
@ -894,9 +891,6 @@ MediaEngineWebRTCMicrophoneSource::FreeChannel()
|
|||||||
bool
|
bool
|
||||||
MediaEngineWebRTCMicrophoneSource::AllocChannel()
|
MediaEngineWebRTCMicrophoneSource::AllocChannel()
|
||||||
{
|
{
|
||||||
mSampleFrequency = MediaEngine::USE_GRAPH_RATE;
|
|
||||||
LOG(("%s: sampling rate %u", __FUNCTION__, mSampleFrequency));
|
|
||||||
|
|
||||||
mState = kAllocated;
|
mState = kAllocated;
|
||||||
sChannelsOpen++;
|
sChannelsOpen++;
|
||||||
return true;
|
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:
|
// properties which qualify the exposure of that interface. For example:
|
||||||
//
|
//
|
||||||
// [
|
// [
|
||||||
// "AGlobalInterface",
|
// "AGlobalInterface", // secure context only
|
||||||
// { name: "ExperimentalThing", release: false },
|
// { name: "ExperimentalThing", release: false },
|
||||||
// { name: "ReallyExperimentalThing", nightly: true },
|
// { name: "ReallyExperimentalThing", nightly: true },
|
||||||
// { name: "DesktopOnlyThing", desktop: true },
|
// { name: "DesktopOnlyThing", desktop: true },
|
||||||
@ -23,54 +23,54 @@
|
|||||||
// a JavaScript Engine peer!
|
// a JavaScript Engine peer!
|
||||||
var ecmaGlobals =
|
var ecmaGlobals =
|
||||||
[
|
[
|
||||||
"Array",
|
{name: "Array", insecureContext: true},
|
||||||
"ArrayBuffer",
|
{name: "ArrayBuffer", insecureContext: true},
|
||||||
{name: "Atomics", disabled: true},
|
{name: "Atomics", insecureContext: true, disabled: true},
|
||||||
"Boolean",
|
{name: "Boolean", insecureContext: true},
|
||||||
{name: "ByteLengthQueuingStrategy", optional: true},
|
{name: "ByteLengthQueuingStrategy", insecureContext: true, optional: true},
|
||||||
{name: "CountQueuingStrategy", optional: true},
|
{name: "CountQueuingStrategy", insecureContext: true, optional: true},
|
||||||
"DataView",
|
{name: "DataView", insecureContext: true},
|
||||||
"Date",
|
{name: "Date", insecureContext: true},
|
||||||
"Error",
|
{name: "Error", insecureContext: true},
|
||||||
"EvalError",
|
{name: "EvalError", insecureContext: true},
|
||||||
"Float32Array",
|
{name: "Float32Array", insecureContext: true},
|
||||||
"Float64Array",
|
{name: "Float64Array", insecureContext: true},
|
||||||
"Function",
|
{name: "Function", insecureContext: true},
|
||||||
"Infinity",
|
{name: "Infinity", insecureContext: true},
|
||||||
"Int16Array",
|
{name: "Int16Array", insecureContext: true},
|
||||||
"Int32Array",
|
{name: "Int32Array", insecureContext: true},
|
||||||
"Int8Array",
|
{name: "Int8Array", insecureContext: true},
|
||||||
"InternalError",
|
{name: "InternalError", insecureContext: true},
|
||||||
"Intl",
|
{name: "Intl", insecureContext: true},
|
||||||
"JSON",
|
{name: "JSON", insecureContext: true},
|
||||||
"Map",
|
{name: "Map", insecureContext: true},
|
||||||
"Math",
|
{name: "Math", insecureContext: true},
|
||||||
"NaN",
|
{name: "NaN", insecureContext: true},
|
||||||
"Number",
|
{name: "Number", insecureContext: true},
|
||||||
"Object",
|
{name: "Object", insecureContext: true},
|
||||||
"Promise",
|
{name: "Promise", insecureContext: true},
|
||||||
"Proxy",
|
{name: "Proxy", insecureContext: true},
|
||||||
"RangeError",
|
{name: "RangeError", insecureContext: true},
|
||||||
{name: "ReadableStream", optional: true},
|
{name: "ReadableStream", insecureContext: true, optional: true},
|
||||||
"ReferenceError",
|
{name: "ReferenceError", insecureContext: true},
|
||||||
"Reflect",
|
{name: "Reflect", insecureContext: true},
|
||||||
"RegExp",
|
{name: "RegExp", insecureContext: true},
|
||||||
"Set",
|
{name: "Set", insecureContext: true},
|
||||||
{name: "SharedArrayBuffer", disabled: true},
|
{name: "SharedArrayBuffer", insecureContext: true, disabled: true},
|
||||||
{name: "SIMD", nightly: true},
|
{name: "SIMD", insecureContext: true, nightly: true},
|
||||||
"String",
|
{name: "String", insecureContext: true},
|
||||||
"Symbol",
|
{name: "Symbol", insecureContext: true},
|
||||||
"SyntaxError",
|
{name: "SyntaxError", insecureContext: true},
|
||||||
{name: "TypedObject", nightly: true},
|
{name: "TypedObject", insecureContext: true, nightly: true},
|
||||||
"TypeError",
|
{name: "TypeError", insecureContext: true},
|
||||||
"Uint16Array",
|
{name: "Uint16Array", insecureContext: true},
|
||||||
"Uint32Array",
|
{name: "Uint32Array", insecureContext: true},
|
||||||
"Uint8Array",
|
{name: "Uint8Array", insecureContext: true},
|
||||||
"Uint8ClampedArray",
|
{name: "Uint8ClampedArray", insecureContext: true},
|
||||||
"URIError",
|
{name: "URIError", insecureContext: true},
|
||||||
"WeakMap",
|
{name: "WeakMap", insecureContext: true},
|
||||||
"WeakSet",
|
{name: "WeakSet", insecureContext: true},
|
||||||
{name: "WebAssembly", optional: true}
|
{name: "WebAssembly", insecureContext: true, optional: true}
|
||||||
];
|
];
|
||||||
// IMPORTANT: Do not change the list above without review from
|
// IMPORTANT: Do not change the list above without review from
|
||||||
// a JavaScript Engine peer!
|
// a JavaScript Engine peer!
|
||||||
@ -79,173 +79,173 @@ var ecmaGlobals =
|
|||||||
var interfaceNamesInGlobalScope =
|
var interfaceNamesInGlobalScope =
|
||||||
[
|
[
|
||||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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!
|
// 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 this list without review from a DOM peer!
|
||||||
];
|
];
|
||||||
// IMPORTANT: Do not change the list above 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 isRelease = !version.includes("a");
|
||||||
var isDesktop = !/Mobile|Tablet/.test(userAgent);
|
var isDesktop = !/Mobile|Tablet/.test(userAgent);
|
||||||
var isAndroid = !!navigator.userAgent.includes("Android");
|
var isAndroid = !!navigator.userAgent.includes("Android");
|
||||||
|
var isInsecureContext = !self.isSecureContext;
|
||||||
|
|
||||||
var interfaceMap = {};
|
var interfaceMap = {};
|
||||||
|
|
||||||
@ -262,7 +263,7 @@ function createInterfaceMap(version, userAgent) {
|
|||||||
{
|
{
|
||||||
for (var entry of interfaces) {
|
for (var entry of interfaces) {
|
||||||
if (typeof(entry) === "string") {
|
if (typeof(entry) === "string") {
|
||||||
interfaceMap[entry] = true;
|
interfaceMap[entry] = !isInsecureContext;
|
||||||
} else {
|
} else {
|
||||||
ok(!("pref" in entry), "Bogus pref annotation for " + entry.name);
|
ok(!("pref" in entry), "Bogus pref annotation for " + entry.name);
|
||||||
if ((entry.nightly === !isNightly) ||
|
if ((entry.nightly === !isNightly) ||
|
||||||
@ -270,7 +271,12 @@ function createInterfaceMap(version, userAgent) {
|
|||||||
(entry.desktop === !isDesktop) ||
|
(entry.desktop === !isDesktop) ||
|
||||||
(entry.android === !isAndroid && !entry.nightlyAndroid) ||
|
(entry.android === !isAndroid && !entry.nightlyAndroid) ||
|
||||||
(entry.release === !isRelease) ||
|
(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) {
|
entry.disabled) {
|
||||||
interfaceMap[entry.name] = false;
|
interfaceMap[entry.name] = false;
|
||||||
} else if (entry.optional) {
|
} else if (entry.optional) {
|
||||||
|
@ -625,11 +625,10 @@ txMozillaXMLOutput::createTxWrapper()
|
|||||||
mDocument->CreateElem(nsDependentAtomString(nsGkAtoms::result),
|
mDocument->CreateElem(nsDependentAtomString(nsGkAtoms::result),
|
||||||
nsGkAtoms::transformiix, namespaceID);
|
nsGkAtoms::transformiix, namespaceID);
|
||||||
|
|
||||||
uint32_t j = 0;
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
// Keep track of the location of the current documentElement, if there is
|
// Keep track of the location of the current documentElement, if there is
|
||||||
// one, so we can verify later
|
// one, so we can verify later
|
||||||
uint32_t rootLocation = 0;
|
uint32_t j = 0, rootLocation = 0;
|
||||||
#endif
|
#endif
|
||||||
for (nsCOMPtr<nsIContent> childContent = mDocument->GetFirstChild();
|
for (nsCOMPtr<nsIContent> childContent = mDocument->GetFirstChild();
|
||||||
childContent; childContent = childContent->GetNextSibling()) {
|
childContent; childContent = childContent->GetNextSibling()) {
|
||||||
@ -645,11 +644,11 @@ txMozillaXMLOutput::createTxWrapper()
|
|||||||
// This is needed for cases when there is no existing
|
// This is needed for cases when there is no existing
|
||||||
// documentElement in the document.
|
// documentElement in the document.
|
||||||
rootLocation = std::max(rootLocation, j + 1);
|
rootLocation = std::max(rootLocation, j + 1);
|
||||||
#endif
|
|
||||||
++j;
|
++j;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
mDocument->RemoveChildAt_Deprecated(j, true);
|
mDocument->RemoveChildNode(childContent, true);
|
||||||
|
|
||||||
rv = wrapper->AppendChildTo(childContent, true);
|
rv = wrapper->AppendChildTo(childContent, true);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
@ -754,30 +754,6 @@ GLContext::InitWithPrefixImpl(const char* prefix, bool trygl)
|
|||||||
MarkUnsupported(GLFeature::depth_texture);
|
MarkUnsupported(GLFeature::depth_texture);
|
||||||
}
|
}
|
||||||
#endif
|
#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)) {
|
if (IsExtensionSupported(GLContext::ARB_pixel_buffer_object)) {
|
||||||
@ -2071,86 +2047,6 @@ GLContext::AssembleOffscreenFBs(const GLuint colorMSRB,
|
|||||||
return isComplete;
|
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
|
void
|
||||||
GLContext::MarkDestroyed()
|
GLContext::MarkDestroyed()
|
||||||
{
|
{
|
||||||
|
@ -3576,12 +3576,6 @@ public:
|
|||||||
return mScreen.get();
|
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 WorkAroundDriverBugs() const { return mWorkAroundDriverBugs; }
|
||||||
|
|
||||||
bool IsDrawingToDefaultFramebuffer();
|
bool IsDrawingToDefaultFramebuffer();
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "mozilla/ipc/CrashReporterHost.h"
|
#include "mozilla/ipc/CrashReporterHost.h"
|
||||||
#include "mozilla/layers/LayerTreeOwnerTracker.h"
|
#include "mozilla/layers/LayerTreeOwnerTracker.h"
|
||||||
#include "mozilla/Unused.h"
|
#include "mozilla/Unused.h"
|
||||||
|
#include "mozilla/HangDetails.h"
|
||||||
#include "nsIObserverService.h"
|
#include "nsIObserverService.h"
|
||||||
|
|
||||||
#ifdef MOZ_GECKO_PROFILER
|
#ifdef MOZ_GECKO_PROFILER
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
#include "mozilla/layers/MemoryReportingMLGPU.h"
|
#include "mozilla/layers/MemoryReportingMLGPU.h"
|
||||||
#include "mozilla/webrender/RenderThread.h"
|
#include "mozilla/webrender/RenderThread.h"
|
||||||
#include "mozilla/webrender/WebRenderAPI.h"
|
#include "mozilla/webrender/WebRenderAPI.h"
|
||||||
|
#include "mozilla/HangDetails.h"
|
||||||
#include "nsDebugImpl.h"
|
#include "nsDebugImpl.h"
|
||||||
#include "nsThreadManager.h"
|
#include "nsThreadManager.h"
|
||||||
#include "prenv.h"
|
#include "prenv.h"
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
include GraphicsMessages;
|
include GraphicsMessages;
|
||||||
include MemoryReportTypes;
|
include MemoryReportTypes;
|
||||||
|
include HangTypes;
|
||||||
include protocol PCompositorManager;
|
include protocol PCompositorManager;
|
||||||
include protocol PImageBridge;
|
include protocol PImageBridge;
|
||||||
include protocol PProfiler;
|
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::Telemetry::DiscardedData from "mozilla/TelemetryComms.h";
|
||||||
using mozilla::gfx::Feature from "gfxFeature.h";
|
using mozilla::gfx::Feature from "gfxFeature.h";
|
||||||
using mozilla::gfx::Fallback from "gfxFallback.h";
|
using mozilla::gfx::Fallback from "gfxFallback.h";
|
||||||
using mozilla::HangDetails from "mozilla/HangDetails.h";
|
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace gfx {
|
namespace gfx {
|
||||||
|
@ -55,6 +55,10 @@ public:
|
|||||||
layers::AutoCompleteTask complete(mTask);
|
layers::AutoCompleteTask complete(mTask);
|
||||||
|
|
||||||
UniquePtr<RenderCompositor> compositor = RenderCompositor::Create(Move(mCompositorWidget));
|
UniquePtr<RenderCompositor> compositor = RenderCompositor::Create(Move(mCompositorWidget));
|
||||||
|
if (!compositor) {
|
||||||
|
// RenderCompositor::Create puts a message into gfxCriticalNote if it is nullptr
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
*mUseANGLE = compositor->UseANGLE();
|
*mUseANGLE = compositor->UseANGLE();
|
||||||
|
|
||||||
|
@ -306,6 +306,8 @@ private:
|
|||||||
bool aStartAtTop);
|
bool aStartAtTop);
|
||||||
#endif // NS_PRINTING
|
#endif // NS_PRINTING
|
||||||
|
|
||||||
|
void ReturnToGalleyPresentation();
|
||||||
|
|
||||||
// Whether we should attach to the top level widget. This is true if we
|
// 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
|
// are sharing/recycling a single base widget and not creating multiple
|
||||||
// child widgets.
|
// child widgets.
|
||||||
@ -356,7 +358,7 @@ protected:
|
|||||||
int mMinFontSize;
|
int mMinFontSize;
|
||||||
|
|
||||||
int16_t mNumURLStarts;
|
int16_t mNumURLStarts;
|
||||||
int16_t mDestroyRefCount; // a second "refcount" for the document viewer's "destroy"
|
int16_t mDestroyBlockedCount;
|
||||||
|
|
||||||
unsigned mStopped : 1;
|
unsigned mStopped : 1;
|
||||||
unsigned mLoaded : 1;
|
unsigned mLoaded : 1;
|
||||||
@ -502,7 +504,7 @@ nsDocumentViewer::nsDocumentViewer()
|
|||||||
mOverrideDPPX(0.0),
|
mOverrideDPPX(0.0),
|
||||||
mMinFontSize(0),
|
mMinFontSize(0),
|
||||||
mNumURLStarts(0),
|
mNumURLStarts(0),
|
||||||
mDestroyRefCount(0),
|
mDestroyBlockedCount(0),
|
||||||
mStopped(false),
|
mStopped(false),
|
||||||
mLoaded(false),
|
mLoaded(false),
|
||||||
mDeferredWindowClose(false),
|
mDeferredWindowClose(false),
|
||||||
@ -554,7 +556,7 @@ nsDocumentViewer::~nsDocumentViewer()
|
|||||||
mPrintJob = nullptr;
|
mPrintJob = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_RELEASE_ASSERT(mDestroyRefCount == 0);
|
MOZ_RELEASE_ASSERT(mDestroyBlockedCount == 0);
|
||||||
NS_ASSERTION(!mPresShell && !mPresContext,
|
NS_ASSERTION(!mPresShell && !mPresContext,
|
||||||
"User did not call nsIContentViewer::Destroy");
|
"User did not call nsIContentViewer::Destroy");
|
||||||
if (mPresShell || mPresContext) {
|
if (mPresShell || mPresContext) {
|
||||||
@ -1653,6 +1655,14 @@ nsDocumentViewer::Destroy()
|
|||||||
{
|
{
|
||||||
NS_ASSERTION(mDocument, "No document in 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
|
#ifdef NS_PRINTING
|
||||||
// Here is where we check to see if the document was still being prepared
|
// 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
|
// for printing when it was asked to be destroy from someone externally
|
||||||
@ -1670,14 +1680,6 @@ nsDocumentViewer::Destroy()
|
|||||||
mAutoBeforeAndAfterPrint = nullptr;
|
mAutoBeforeAndAfterPrint = nullptr;
|
||||||
#endif
|
#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
|
// If we were told to put ourselves into session history instead of destroy
|
||||||
// the presentation, do that now.
|
// the presentation, do that now.
|
||||||
if (mSHEntry) {
|
if (mSHEntry) {
|
||||||
@ -4466,15 +4468,15 @@ nsDocumentViewer::SetIsPrintPreview(bool aIsPrintPreview)
|
|||||||
|
|
||||||
//------------------------------------------------------------
|
//------------------------------------------------------------
|
||||||
void
|
void
|
||||||
nsDocumentViewer::IncrementDestroyRefCount()
|
nsDocumentViewer::IncrementDestroyBlockedCount()
|
||||||
{
|
{
|
||||||
++mDestroyRefCount;
|
++mDestroyBlockedCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsDocumentViewer::DecrementDestroyRefCount()
|
nsDocumentViewer::DecrementDestroyBlockedCount()
|
||||||
{
|
{
|
||||||
--mDestroyRefCount;
|
--mDestroyBlockedCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------
|
//------------------------------------------------------------
|
||||||
|
@ -41,10 +41,13 @@ public:
|
|||||||
// Callers should call EndUpdate() on it when ready to use.
|
// Callers should call EndUpdate() on it when ready to use.
|
||||||
virtual mozilla::StyleSetHandle CreateStyleSet(nsIDocument* aDocument) = 0;
|
virtual mozilla::StyleSetHandle CreateStyleSet(nsIDocument* aDocument) = 0;
|
||||||
|
|
||||||
virtual void IncrementDestroyRefCount() = 0;
|
/**
|
||||||
virtual void DecrementDestroyRefCount() = 0;
|
* This is used by nsPagePrintTimer to make nsDocumentViewer::Destroy()
|
||||||
|
* a no-op until printing is finished. That prevents the nsDocumentViewer
|
||||||
virtual void ReturnToGalleyPresentation() = 0;
|
* and its document, presshell and prescontext from going away.
|
||||||
|
*/
|
||||||
|
virtual void IncrementDestroyBlockedCount() = 0;
|
||||||
|
virtual void DecrementDestroyBlockedCount() = 0;
|
||||||
|
|
||||||
virtual void OnDonePrinting() = 0;
|
virtual void OnDonePrinting() = 0;
|
||||||
|
|
||||||
@ -76,9 +79,8 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocumentViewerPrint,
|
|||||||
void SetIsPrintPreview(bool aIsPrintPreview) override; \
|
void SetIsPrintPreview(bool aIsPrintPreview) override; \
|
||||||
bool GetIsPrintPreview() override; \
|
bool GetIsPrintPreview() override; \
|
||||||
mozilla::StyleSetHandle CreateStyleSet(nsIDocument* aDocument) override; \
|
mozilla::StyleSetHandle CreateStyleSet(nsIDocument* aDocument) override; \
|
||||||
void IncrementDestroyRefCount() override; \
|
void IncrementDestroyBlockedCount() override; \
|
||||||
void DecrementDestroyRefCount() override; \
|
void DecrementDestroyBlockedCount() override; \
|
||||||
void ReturnToGalleyPresentation() override; \
|
|
||||||
void OnDonePrinting() override; \
|
void OnDonePrinting() override; \
|
||||||
bool IsInitializedForPrintPreview() override; \
|
bool IsInitializedForPrintPreview() override; \
|
||||||
void InitializeForPrintPreview() override; \
|
void InitializeForPrintPreview() override; \
|
||||||
|
@ -306,13 +306,13 @@ void UpdateASR(nsDisplayItem* aItem,
|
|||||||
*
|
*
|
||||||
* The basic algorithm is:
|
* 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:
|
* 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.
|
* Add valid items to the merged list, destroy invalid items.
|
||||||
* Destroy the matching item from the old list.
|
* Add i into the merged list.
|
||||||
* Add the item from the new list 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.
|
* 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
|
* 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).
|
* algorithm once we match up the new/old versions (if present).
|
||||||
@ -320,7 +320,7 @@ void UpdateASR(nsDisplayItem* aItem,
|
|||||||
* Example 1:
|
* Example 1:
|
||||||
*
|
*
|
||||||
* Old List: A,B,C,D
|
* Old List: A,B,C,D
|
||||||
* New List: A,D
|
* Modified List: A,D
|
||||||
* Invalidations: C,D
|
* Invalidations: C,D
|
||||||
*
|
*
|
||||||
* We first match the A items, and add the new one to the merged list.
|
* 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
|
* Merged List: A,B,D
|
||||||
*
|
*
|
||||||
* Example 2:
|
* Example 2 (layout/reftests/retained-dl-zindex-1.html):
|
||||||
*
|
*
|
||||||
* Old List: A, B
|
* 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: -
|
* Invalidations: -
|
||||||
*
|
*
|
||||||
* This can happen because a prior merge might have changed the ordering
|
* 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.
|
* and then add the new B into the merged list. We then add A, and we're done.
|
||||||
*
|
*
|
||||||
* Merged List: B, A
|
* 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
|
void
|
||||||
RetainedDisplayListBuilder::MergeDisplayLists(nsDisplayList* aNewList,
|
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,
|
// 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
|
// so copy all valid items from the old list into the merged list until we get to the
|
||||||
// matched item.
|
// matched item.
|
||||||
if (!oldItem->IsReused()) {
|
|
||||||
nsDisplayItem* old = nullptr;
|
nsDisplayItem* old = nullptr;
|
||||||
while ((old = aOldList->RemoveBottom()) && !IsSameItem(newItem, old)) {
|
while ((old = aOldList->GetBottom()) && old != oldItem) {
|
||||||
if (IsAnyAncestorModified(old->FrameForInvalidation())) {
|
if (IsAnyAncestorModified(old->FrameForInvalidation())) {
|
||||||
// The old item is invalid, discard it.
|
// The old item is invalid, discard it.
|
||||||
oldListLookup.Remove({ old->Frame(), old->GetPerFrameKey() });
|
oldListLookup.Remove({ old->Frame(), old->GetPerFrameKey() });
|
||||||
|
aOldList->RemoveBottom();
|
||||||
old->Destroy(&mBuilder);
|
old->Destroy(&mBuilder);
|
||||||
} else if (newListLookup.Get({ old->Frame(), old->GetPerFrameKey() })) {
|
} 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.
|
// This 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
|
// Stop now, and we'll deal with it when we get to the new entry.
|
||||||
// entry.
|
break;
|
||||||
old->SetReused(true);
|
|
||||||
} else {
|
} else {
|
||||||
// Recurse into the child list (without a matching new list) to
|
// Recurse into the child list (without a matching new list) to
|
||||||
// ensure that we find and remove any invalidated items.
|
// ensure that we find and remove any invalidated items.
|
||||||
@ -430,16 +464,27 @@ RetainedDisplayListBuilder::MergeDisplayLists(nsDisplayList* aNewList,
|
|||||||
UpdateASR(old, containerASRForChildren);
|
UpdateASR(old, containerASRForChildren);
|
||||||
old->UpdateBounds(&mBuilder);
|
old->UpdateBounds(&mBuilder);
|
||||||
}
|
}
|
||||||
|
aOldList->RemoveBottom();
|
||||||
ReuseItem(old);
|
ReuseItem(old);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MOZ_ASSERT(old && IsSameItem(newItem, old));
|
bool destroy = false;
|
||||||
MOZ_ASSERT(old == oldItem);
|
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
|
// Recursively merge any child lists, destroy the old item and add
|
||||||
// the new one to the list.
|
// 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())) {
|
!IsAnyAncestorModified(oldItem->FrameForInvalidation())) {
|
||||||
// Event regions items don't have anything interesting other than
|
// Event regions items don't have anything interesting other than
|
||||||
// the lists of regions and frames, so we have no need to use the
|
// 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);
|
newItem->UpdateBounds(&mBuilder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (destroy) {
|
||||||
oldItem->Destroy(&mBuilder);
|
oldItem->Destroy(&mBuilder);
|
||||||
|
}
|
||||||
UseItem(newItem);
|
UseItem(newItem);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -471,7 +518,8 @@ RetainedDisplayListBuilder::MergeDisplayLists(nsDisplayList* aNewList,
|
|||||||
|
|
||||||
// Reuse the remaining valid items from the old display list.
|
// Reuse the remaining valid items from the old display list.
|
||||||
while (nsDisplayItem* old = aOldList->RemoveBottom()) {
|
while (nsDisplayItem* old = aOldList->RemoveBottom()) {
|
||||||
if (!IsAnyAncestorModified(old->FrameForInvalidation())) {
|
if (!IsAnyAncestorModified(old->FrameForInvalidation()) &&
|
||||||
|
!old->IsReused()) {
|
||||||
if (old->GetChildren()) {
|
if (old->GetChildren()) {
|
||||||
// We are calling MergeDisplayLists() to ensure that the display items
|
// We are calling MergeDisplayLists() to ensure that the display items
|
||||||
// with modified or deleted children will be correctly handled.
|
// with modified or deleted children will be correctly handled.
|
||||||
@ -550,7 +598,7 @@ SubDocEnumCb(nsIDocument* aDocument, void* aData)
|
|||||||
MOZ_ASSERT(subDocView);
|
MOZ_ASSERT(subDocView);
|
||||||
|
|
||||||
nsIFrame* subDocFrame = subDocView->GetFrame();
|
nsIFrame* subDocFrame = subDocView->GetFrame();
|
||||||
MOZ_ASSERT(subDocFrame);
|
if (subDocFrame) {
|
||||||
nsSubDocumentFrame* subdocumentFrame = do_QueryFrame(subDocFrame);
|
nsSubDocumentFrame* subdocumentFrame = do_QueryFrame(subDocFrame);
|
||||||
MOZ_ASSERT(subdocumentFrame);
|
MOZ_ASSERT(subdocumentFrame);
|
||||||
|
|
||||||
@ -562,6 +610,7 @@ SubDocEnumCb(nsIDocument* aDocument, void* aData)
|
|||||||
TakeAndAddModifiedFramesFromRootFrame(data->modifiedFrames, rootFrame);
|
TakeAndAddModifiedFramesFromRootFrame(data->modifiedFrames, rootFrame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
aDocument->EnumerateSubDocuments(SubDocEnumCb, aData);
|
aDocument->EnumerateSubDocuments(SubDocEnumCb, aData);
|
||||||
return true;
|
return true;
|
||||||
|
@ -17,8 +17,8 @@ NS_IMPL_ISUPPORTS_INHERITED(nsPagePrintTimer, mozilla::Runnable, nsITimerCallbac
|
|||||||
|
|
||||||
nsPagePrintTimer::~nsPagePrintTimer()
|
nsPagePrintTimer::~nsPagePrintTimer()
|
||||||
{
|
{
|
||||||
// This matches the IncrementDestroyRefCount call in the constructor.
|
// This matches the IncrementDestroyBlockedCount call in the constructor.
|
||||||
mDocViewerPrint->DecrementDestroyRefCount();
|
mDocViewerPrint->DecrementDestroyBlockedCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
@ -140,7 +140,6 @@ nsPagePrintTimer::Notify(nsITimer *timer)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mDocViewerPrint) {
|
|
||||||
bool donePrePrint = true;
|
bool donePrePrint = true;
|
||||||
// Don't start to pre-print if we're waiting on the parent still.
|
// Don't start to pre-print if we're waiting on the parent still.
|
||||||
if (mPrintJob && !mWaitingForRemotePrint) {
|
if (mPrintJob && !mWaitingForRemotePrint) {
|
||||||
@ -157,7 +156,6 @@ nsPagePrintTimer::Notify(nsITimer *timer)
|
|||||||
StartWatchDogTimer();
|
StartWatchDogTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include "nsIDocumentViewerPrint.h"
|
#include "nsIDocumentViewerPrint.h"
|
||||||
#include "nsPrintObject.h"
|
#include "nsPrintObject.h"
|
||||||
#include "mozilla/Attributes.h"
|
#include "mozilla/Attributes.h"
|
||||||
|
#include "mozilla/OwningNonNull.h"
|
||||||
#include "nsThreadUtils.h"
|
#include "nsThreadUtils.h"
|
||||||
|
|
||||||
class nsPrintJob;
|
class nsPrintJob;
|
||||||
@ -33,7 +34,7 @@ public:
|
|||||||
uint32_t aDelay)
|
uint32_t aDelay)
|
||||||
: Runnable("nsPagePrintTimer")
|
: Runnable("nsPagePrintTimer")
|
||||||
, mPrintJob(aPrintJob)
|
, mPrintJob(aPrintJob)
|
||||||
, mDocViewerPrint(aDocViewerPrint)
|
, mDocViewerPrint(*aDocViewerPrint)
|
||||||
, mDocument(aDocument)
|
, mDocument(aDocument)
|
||||||
, mDelay(aDelay)
|
, mDelay(aDelay)
|
||||||
, mFiringCount(0)
|
, mFiringCount(0)
|
||||||
@ -41,8 +42,8 @@ public:
|
|||||||
, mWatchDogCount(0)
|
, mWatchDogCount(0)
|
||||||
, mDone(false)
|
, mDone(false)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(aDocument);
|
MOZ_ASSERT(aDocViewerPrint && aDocument);
|
||||||
mDocViewerPrint->IncrementDestroyRefCount();
|
mDocViewerPrint->IncrementDestroyBlockedCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_DECL_NSITIMERCALLBACK
|
NS_DECL_NSITIMERCALLBACK
|
||||||
@ -71,7 +72,7 @@ private:
|
|||||||
void Fail();
|
void Fail();
|
||||||
|
|
||||||
nsPrintJob* mPrintJob;
|
nsPrintJob* mPrintJob;
|
||||||
nsCOMPtr<nsIDocumentViewerPrint> mDocViewerPrint;
|
const mozilla::OwningNonNull<nsIDocumentViewerPrint> mDocViewerPrint;
|
||||||
nsCOMPtr<nsIDocument> mDocument;
|
nsCOMPtr<nsIDocument> mDocument;
|
||||||
nsCOMPtr<nsITimer> mTimer;
|
nsCOMPtr<nsITimer> mTimer;
|
||||||
nsCOMPtr<nsITimer> mWatchDogTimer;
|
nsCOMPtr<nsITimer> mWatchDogTimer;
|
||||||
|
@ -38,7 +38,6 @@ nsPrintData::nsPrintData(ePrintDataType aType)
|
|||||||
, mNumPrintablePages(0)
|
, mNumPrintablePages(0)
|
||||||
, mNumPagesPrinted(0)
|
, mNumPagesPrinted(0)
|
||||||
, mShrinkRatio(1.0)
|
, mShrinkRatio(1.0)
|
||||||
, mOrigDCScale(1.0)
|
|
||||||
, mPPEventListeners(nullptr)
|
, mPPEventListeners(nullptr)
|
||||||
{
|
{
|
||||||
nsCOMPtr<nsIStringBundle> brandBundle;
|
nsCOMPtr<nsIStringBundle> brandBundle;
|
||||||
|
@ -82,7 +82,6 @@ public:
|
|||||||
int32_t mNumPrintablePages;
|
int32_t mNumPrintablePages;
|
||||||
int32_t mNumPagesPrinted;
|
int32_t mNumPagesPrinted;
|
||||||
float mShrinkRatio;
|
float mShrinkRatio;
|
||||||
float mOrigDCScale;
|
|
||||||
|
|
||||||
nsCOMPtr<nsIPrintSettings> mPrintSettings;
|
nsCOMPtr<nsIPrintSettings> mPrintSettings;
|
||||||
nsPrintPreviewListener* mPPEventListeners;
|
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-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
|
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-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
|
fuzzy(1,235200) == 1413073.html 1413073-ref.html
|
||||||
== 1416291.html 1416291-ref.html
|
== 1416291.html 1416291-ref.html
|
||||||
== 1417601-1.html 1417601-1-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
|
#ifdef NIGHTLY_BUILD
|
||||||
// Cubeb sandbox (remoting) control
|
// Cubeb sandbox (remoting) control
|
||||||
|
#ifdef XP_MACOSX
|
||||||
|
pref("media.cubeb.sandbox", false);
|
||||||
|
#else
|
||||||
pref("media.cubeb.sandbox", true);
|
pref("media.cubeb.sandbox", true);
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
// Set to true to force demux/decode warnings to be treated as errors.
|
// Set to true to force demux/decode warnings to be treated as errors.
|
||||||
pref("media.playback.warnings-as-errors", false);
|
pref("media.playback.warnings-as-errors", false);
|
||||||
|
@ -79,24 +79,27 @@ jobs:
|
|||||||
|
|
||||||
# NOTE: Windows Searchfox jobs aren't working quite yet (bug 1418415).
|
# NOTE: Windows Searchfox jobs aren't working quite yet (bug 1418415).
|
||||||
|
|
||||||
win32-searchfox/debug:
|
win64-searchfox/debug:
|
||||||
description: "Win32 Searchfox Debug (clang-cl)"
|
description: "Win64 Searchfox Debug (clang-cl)"
|
||||||
index:
|
index:
|
||||||
product: firefox
|
product: firefox
|
||||||
job-name: win32-searchfox-debug
|
job-name: win64-searchfox-debug
|
||||||
treeherder:
|
treeherder:
|
||||||
platform: windows2012-32/debug
|
platform: windows2012-64/debug
|
||||||
worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
|
worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
|
||||||
worker:
|
worker:
|
||||||
max-run-time: 7200
|
max-run-time: 7200
|
||||||
env:
|
env:
|
||||||
TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/win32/releng.manifest"
|
TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/win64/releng.manifest"
|
||||||
run:
|
run:
|
||||||
using: mozharness
|
using: mozharness
|
||||||
|
options: [append-env-variables-from-configs]
|
||||||
script: mozharness/scripts/fx_desktop_build.py
|
script: mozharness/scripts/fx_desktop_build.py
|
||||||
config:
|
config:
|
||||||
- builds/releng_base_firefox.py
|
- 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:
|
toolchains:
|
||||||
- win32-clang-cl
|
- win64-clang-cl
|
||||||
- win32-rust
|
- win64-rust
|
||||||
|
@ -166,6 +166,8 @@ def register_callback_action(name, title, symbol, description, order=10000,
|
|||||||
def register_callback(cb):
|
def register_callback(cb):
|
||||||
assert isinstance(cb, FunctionType), 'callback must be a function'
|
assert isinstance(cb, FunctionType), 'callback must be a function'
|
||||||
assert isinstance(symbol, basestring), 'symbol must be a string'
|
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 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 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__)
|
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(
|
@register_callback_action(
|
||||||
name='release-promotion',
|
name='release-promotion',
|
||||||
title='Release Promotion',
|
title='Release Promotion',
|
||||||
symbol='Relpro',
|
symbol='${input.release_promotion_flavor}',
|
||||||
description="Promote a release.",
|
description="Promote a release.",
|
||||||
order=10000,
|
order=10000,
|
||||||
context=[],
|
context=[],
|
||||||
|
@ -120,6 +120,31 @@ class Parameters(ReadOnlyDict):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
raise KeyError("taskgraph parameter {!r} not found".format(k))
|
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):
|
def load_parameters_file(filename, strict=True):
|
||||||
"""
|
"""
|
||||||
|
@ -113,7 +113,7 @@ def fill_template(config, tasks):
|
|||||||
'label': 'build-docker-image-' + image_name,
|
'label': 'build-docker-image-' + image_name,
|
||||||
'description': description,
|
'description': description,
|
||||||
'attributes': {'image_name': image_name},
|
'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'],
|
'scopes': ['secrets:get:project/taskcluster/gecko/hgfingerprint'],
|
||||||
'treeherder': {
|
'treeherder': {
|
||||||
'symbol': job_symbol,
|
'symbol': job_symbol,
|
||||||
|
@ -174,7 +174,7 @@ def mozharness_on_docker_worker_setup(config, job, taskdesc):
|
|||||||
if 'job-script' in run:
|
if 'job-script' in run:
|
||||||
env['JOB_SCRIPT'] = run['job-script']
|
env['JOB_SCRIPT'] = run['job-script']
|
||||||
|
|
||||||
if 'try' in config.params['project']:
|
if config.params.is_try():
|
||||||
env['TRY_COMMIT_MSG'] = config.params['message']
|
env['TRY_COMMIT_MSG'] = config.params['message']
|
||||||
|
|
||||||
# if we're not keeping artifacts, set some env variables to empty values
|
# 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
|
# 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
|
# there is no `TRY_COMMIT_MESSAGE`, pass a space instead, so that
|
||||||
# mozharness doesn't try to find the commit message on its own.
|
# 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'
|
env['TRY_COMMIT_MSG'] = config.params['message'] or 'no commit message'
|
||||||
|
|
||||||
if not job['attributes']['build_platform'].startswith('win'):
|
if not job['attributes']['build_platform'].startswith('win'):
|
||||||
|
@ -147,7 +147,7 @@ def mozharness_test_on_docker(config, job, taskdesc):
|
|||||||
if 'actions' in mozharness:
|
if 'actions' in mozharness:
|
||||||
env['MOZHARNESS_ACTIONS'] = ' '.join(mozharness['actions'])
|
env['MOZHARNESS_ACTIONS'] = ' '.join(mozharness['actions'])
|
||||||
|
|
||||||
if 'try' in config.params['project']:
|
if config.params.is_try():
|
||||||
env['TRY_COMMIT_MSG'] = config.params['message']
|
env['TRY_COMMIT_MSG'] = config.params['message']
|
||||||
|
|
||||||
# handle some of the mozharness-specific options
|
# 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'):
|
if isinstance(c, basestring) and c.startswith('--test-suite'):
|
||||||
mh_command[i] += suffix
|
mh_command[i] += suffix
|
||||||
|
|
||||||
if 'try' in config.params['project']:
|
if config.params.is_try():
|
||||||
env['TRY_COMMIT_MSG'] = config.params['message']
|
env['TRY_COMMIT_MSG'] = config.params['message']
|
||||||
|
|
||||||
worker['mounts'] = [{
|
worker['mounts'] = [{
|
||||||
|
@ -875,7 +875,7 @@ def build_docker_worker_payload(config, task, task_def):
|
|||||||
else:
|
else:
|
||||||
suffix = ''
|
suffix = ''
|
||||||
|
|
||||||
skip_untrusted = config.params['project'] == 'try' or level == 1
|
skip_untrusted = config.params.is_try() or level == 1
|
||||||
|
|
||||||
for cache in worker['caches']:
|
for cache in worker['caches']:
|
||||||
# Some caches aren't enabled in environments where we can't
|
# Some caches aren't enabled in environments where we can't
|
||||||
@ -1454,10 +1454,7 @@ def build_task(config, tasks):
|
|||||||
'description': task['description'],
|
'description': task['description'],
|
||||||
'name': task['label'],
|
'name': task['label'],
|
||||||
'owner': config.params['owner'],
|
'owner': config.params['owner'],
|
||||||
'source': '{}/file/{}/{}'.format(
|
'source': config.params.file_url(config.path),
|
||||||
config.params['head_repository'],
|
|
||||||
config.params['head_rev'],
|
|
||||||
config.path),
|
|
||||||
},
|
},
|
||||||
'extra': extra,
|
'extra': extra,
|
||||||
'tags': tags,
|
'tags': tags,
|
||||||
|
@ -28,7 +28,7 @@ class EasyServer(ThreadingMixIn, BaseHTTPServer.HTTPServer):
|
|||||||
acceptable_errors = (errno.EPIPE, errno.ECONNABORTED)
|
acceptable_errors = (errno.EPIPE, errno.ECONNABORTED)
|
||||||
|
|
||||||
def handle_error(self, request, client_address):
|
def handle_error(self, request, client_address):
|
||||||
error = sys.exc_value
|
error = sys.exc_info()[1]
|
||||||
|
|
||||||
if ((isinstance(error, socket.error) and
|
if ((isinstance(error, socket.error) and
|
||||||
isinstance(error.args, tuple) and
|
isinstance(error.args, tuple) and
|
||||||
|
@ -13,6 +13,7 @@ config = {
|
|||||||
'--upload', 'mozilla/geckoview',
|
'--upload', 'mozilla/geckoview',
|
||||||
'--upload-branch', 'gh-pages/javadoc/{project}',
|
'--upload-branch', 'gh-pages/javadoc/{project}',
|
||||||
'--upload-message', 'Update {project} javadoc to rev {revision}',
|
'--upload-message', 'Update {project} javadoc to rev {revision}',
|
||||||
|
'--variant', 'officialWithGeckoBinariesNoMinApiRelease',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'artifact_flag_build_variant_in_try': None, # There's no artifact equivalent.
|
'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
|
# Synchronous tests like test_alerts.html must come before
|
||||||
# asynchronous tests like test_alerts_noobserve.html!
|
# asynchronous tests like test_alerts_noobserve.html!
|
||||||
[test_alerts.html]
|
[test_alerts.html]
|
||||||
skip-if = toolkit == 'android'
|
skip-if = toolkit == 'android' || (os == "win" && debug) # Bug 1407296
|
||||||
[test_alerts_noobserve.html]
|
[test_alerts_noobserve.html]
|
||||||
[test_alerts_requireinteraction.html]
|
[test_alerts_requireinteraction.html]
|
||||||
[test_image.html]
|
[test_image.html]
|
||||||
|
@ -81,6 +81,7 @@ function runTest() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SimpleTest.requestCompleteLog(); // Bug 1407296
|
||||||
runTest();
|
runTest();
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -26,13 +26,13 @@
|
|||||||
#include "nsThreadUtils.h"
|
#include "nsThreadUtils.h"
|
||||||
#include "nsXULAppAPI.h"
|
#include "nsXULAppAPI.h"
|
||||||
#include "GeckoProfiler.h"
|
#include "GeckoProfiler.h"
|
||||||
#ifdef MOZ_GECKO_PROFILER
|
|
||||||
#include "mozilla/TimeStamp.h"
|
|
||||||
#include "ProfilerMarkerPayload.h"
|
|
||||||
#endif
|
|
||||||
#include "nsNetCID.h"
|
#include "nsNetCID.h"
|
||||||
#include "HangDetails.h"
|
#include "HangDetails.h"
|
||||||
|
|
||||||
|
#ifdef MOZ_GECKO_PROFILER
|
||||||
|
#include "ProfilerMarkerPayload.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
// Activate BHR only for one every BHR_BETA_MOD users.
|
// 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
|
// Report a hang; aManager->mLock IS locked. The hang will be processed
|
||||||
// off-main-thread, and will then be submitted back.
|
// 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
|
// Report a permanent hang; aManager->mLock IS locked
|
||||||
void ReportPermaHang();
|
void ReportPermaHang();
|
||||||
// Called by BackgroundHangMonitor::NotifyActivity
|
// Called by BackgroundHangMonitor::NotifyActivity
|
||||||
@ -390,7 +390,7 @@ BackgroundHangManager::RunMonitorThread()
|
|||||||
} else {
|
} else {
|
||||||
if (MOZ_LIKELY(interval != currentThread->mHangStart)) {
|
if (MOZ_LIKELY(interval != currentThread->mHangStart)) {
|
||||||
// A hang ended
|
// A hang ended
|
||||||
currentThread->ReportHang(intervalNow - currentThread->mHangStart, TimeStamp::Now());
|
currentThread->ReportHang(intervalNow - currentThread->mHangStart);
|
||||||
currentThread->mHanging = false;
|
currentThread->mHanging = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -468,18 +468,27 @@ BackgroundHangThread::~BackgroundHangThread()
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
BackgroundHangThread::ReportHang(PRIntervalTime aHangTime, TimeStamp aHangEndTime)
|
BackgroundHangThread::ReportHang(PRIntervalTime aHangTime)
|
||||||
{
|
{
|
||||||
// Recovered from a hang; called on the monitor thread
|
// Recovered from a hang; called on the monitor thread
|
||||||
// mManager->mLock IS locked
|
// mManager->mLock IS locked
|
||||||
|
|
||||||
HangDetails hangDetails(aHangTime,
|
nsTArray<HangAnnotation> annotations;
|
||||||
aHangEndTime,
|
for (auto& annotation : mAnnotations) {
|
||||||
XRE_GetProcessType(),
|
HangAnnotation annot(annotation.mName, annotation.mValue);
|
||||||
|
annotations.AppendElement(mozilla::Move(annot));
|
||||||
|
}
|
||||||
|
|
||||||
|
HangDetails hangDetails(
|
||||||
|
aHangTime,
|
||||||
|
nsDependentCString(XRE_ChildProcessTypeToString(XRE_GetProcessType())),
|
||||||
|
VoidString(),
|
||||||
mThreadName,
|
mThreadName,
|
||||||
mRunnableName,
|
mRunnableName,
|
||||||
Move(mHangStack),
|
Move(mHangStack),
|
||||||
Move(mAnnotations));
|
Move(annotations)
|
||||||
|
);
|
||||||
|
|
||||||
// If we have the stream transport service avaliable, we can process the
|
// 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
|
// native stack on it. Otherwise, we are unable to report a native stack, so
|
||||||
// we just report without one.
|
// we just report without one.
|
||||||
@ -492,6 +501,18 @@ BackgroundHangThread::ReportHang(PRIntervalTime aHangTime, TimeStamp aHangEndTim
|
|||||||
RefPtr<nsHangDetails> hd = new nsHangDetails(Move(hangDetails));
|
RefPtr<nsHangDetails> hd = new nsHangDetails(Move(hangDetails));
|
||||||
hd->Submit();
|
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
|
void
|
||||||
@ -506,7 +527,7 @@ BackgroundHangThread::ReportPermaHang()
|
|||||||
//
|
//
|
||||||
// We currently don't look at hang reports outside of nightly, and already
|
// We currently don't look at hang reports outside of nightly, and already
|
||||||
// collect native stacks eagerly on nightly, so this should be OK.
|
// collect native stacks eagerly on nightly, so this should be OK.
|
||||||
ReportHang(mMaxTimeout, TimeStamp::Now());
|
ReportHang(mMaxTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_ALWAYS_INLINE void
|
MOZ_ALWAYS_INLINE void
|
||||||
|
@ -5,8 +5,9 @@
|
|||||||
#include "mozilla/dom/ContentChild.h"
|
#include "mozilla/dom/ContentChild.h"
|
||||||
#include "mozilla/Unused.h"
|
#include "mozilla/Unused.h"
|
||||||
#include "mozilla/GfxMessageUtils.h" // For ParamTraits<GeckoProcessType>
|
#include "mozilla/GfxMessageUtils.h" // For ParamTraits<GeckoProcessType>
|
||||||
|
|
||||||
#ifdef MOZ_GECKO_PROFILER
|
#ifdef MOZ_GECKO_PROFILER
|
||||||
#include "ProfilerMarkerPayload.h"
|
#include "shared-libraries.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
@ -14,35 +15,35 @@ namespace mozilla {
|
|||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsHangDetails::GetDuration(uint32_t* aDuration)
|
nsHangDetails::GetDuration(uint32_t* aDuration)
|
||||||
{
|
{
|
||||||
*aDuration = mDetails.mDuration;
|
*aDuration = mDetails.duration();
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsHangDetails::GetThread(nsACString& aName)
|
nsHangDetails::GetThread(nsACString& aName)
|
||||||
{
|
{
|
||||||
aName.Assign(mDetails.mThreadName);
|
aName.Assign(mDetails.threadName());
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsHangDetails::GetRunnableName(nsACString& aRunnableName)
|
nsHangDetails::GetRunnableName(nsACString& aRunnableName)
|
||||||
{
|
{
|
||||||
aRunnableName.Assign(mDetails.mRunnableName);
|
aRunnableName.Assign(mDetails.runnableName());
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsHangDetails::GetProcess(nsACString& aName)
|
nsHangDetails::GetProcess(nsACString& aName)
|
||||||
{
|
{
|
||||||
aName.AssignASCII(XRE_ChildProcessTypeToString(mDetails.mProcess));
|
aName.Assign(mDetails.process());
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsHangDetails::GetRemoteType(nsAString& aName)
|
nsHangDetails::GetRemoteType(nsAString& aName)
|
||||||
{
|
{
|
||||||
aName.Assign(mDetails.mRemoteType);
|
aName.Assign(mDetails.remoteType());
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,14 +57,14 @@ nsHangDetails::GetAnnotations(JSContext* aCx, JS::MutableHandleValue aVal)
|
|||||||
return NS_ERROR_OUT_OF_MEMORY;
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& annot : mDetails.mAnnotations) {
|
for (auto& annot : mDetails.annotations()) {
|
||||||
JSString* jsString = JS_NewUCStringCopyN(aCx, annot.mValue.get(), annot.mValue.Length());
|
JSString* jsString = JS_NewUCStringCopyN(aCx, annot.value().get(), annot.value().Length());
|
||||||
if (!jsString) {
|
if (!jsString) {
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
JS::RootedValue jsValue(aCx);
|
JS::RootedValue jsValue(aCx);
|
||||||
jsValue.setString(jsString);
|
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)) {
|
jsValue, JSPROP_ENUMERATE)) {
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
@ -98,32 +99,63 @@ StringFrame(JSContext* aCx,
|
|||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
NS_IMETHODIMP
|
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) {
|
if (!ret) {
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < mDetails.mStack.length(); ++i) {
|
|
||||||
const HangStack::Frame& frame = mDetails.mStack[i];
|
for (uint32_t i = 0; i < length; ++i) {
|
||||||
switch (frame.GetKind()) {
|
auto& entry = stack.stack()[i];
|
||||||
case HangStack::Frame::Kind::STRING: {
|
switch (entry.type()) {
|
||||||
nsresult rv = StringFrame(aCx, ret, i, frame.AsString());
|
case HangEntry::TnsCString: {
|
||||||
|
nsresult rv = StringFrame(aCx, ret, i, entry.get_nsCString().get());
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
break;
|
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));
|
JS::RootedObject jsFrame(aCx, JS_NewArrayObject(aCx, 2));
|
||||||
if (!jsFrame) {
|
if (!jsFrame) {
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
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;
|
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()));
|
JS::RootedString hex(aCx, JS_NewStringCopyZ(aCx, hexString.get()));
|
||||||
if (!hex || !JS_DefineElement(aCx, jsFrame, 1, hex, JSPROP_ENUMERATE)) {
|
if (!hex || !JS_DefineElement(aCx, jsFrame, 1, hex, JSPROP_ENUMERATE)) {
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
@ -134,67 +166,49 @@ nsHangDetails::GetStack(JSContext* aCx, JS::MutableHandle<JS::Value> aVal)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case HangEntry::THangEntryProgCounter: {
|
||||||
case HangStack::Frame::Kind::PC: {
|
// Don't bother recording fixed program counters to JS
|
||||||
JS::RootedObject jsFrame(aCx, JS_NewArrayObject(aCx, 2));
|
nsresult rv = StringFrame(aCx, ret, i, "(unresolved)");
|
||||||
if (!jsFrame) {
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
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;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case HangEntry::THangEntryContent: {
|
||||||
case HangStack::Frame::Kind::CONTENT: {
|
|
||||||
nsresult rv = StringFrame(aCx, ret, i, "(content script)");
|
nsresult rv = StringFrame(aCx, ret, i, "(content script)");
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case HangEntry::THangEntryJit: {
|
||||||
case HangStack::Frame::Kind::JIT: {
|
|
||||||
nsresult rv = StringFrame(aCx, ret, i, "(jit frame)");
|
nsresult rv = StringFrame(aCx, ret, i, "(jit frame)");
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case HangEntry::THangEntryWasm: {
|
||||||
case HangStack::Frame::Kind::WASM: {
|
|
||||||
nsresult rv = StringFrame(aCx, ret, i, "(wasm)");
|
nsresult rv = StringFrame(aCx, ret, i, "(wasm)");
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case HangEntry::THangEntryChromeScript: {
|
||||||
case HangStack::Frame::Kind::SUPPRESSED: {
|
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)");
|
nsresult rv = StringFrame(aCx, ret, i, "(profiling suppressed)");
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
default: MOZ_CRASH("Unsupported HangEntry type?");
|
||||||
default:
|
|
||||||
MOZ_ASSERT_UNREACHABLE("Invalid variant");
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
aVal.setObject(*ret);
|
aStack.setObject(*ret);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsHangDetails::GetModules(JSContext* aCx, JS::MutableHandleValue aVal)
|
nsHangDetails::GetModules(JSContext* aCx, JS::MutableHandleValue aVal)
|
||||||
{
|
{
|
||||||
auto& modules = mDetails.mStack.GetModules();
|
auto& modules = mDetails.stack().modules();
|
||||||
size_t length = modules.Length();
|
size_t length = modules.Length();
|
||||||
JS::RootedObject retObj(aCx, JS_NewArrayObject(aCx, length));
|
JS::RootedObject retObj(aCx, JS_NewArrayObject(aCx, length));
|
||||||
if (!retObj) {
|
if (!retObj) {
|
||||||
@ -202,18 +216,22 @@ nsHangDetails::GetModules(JSContext* aCx, JS::MutableHandleValue aVal)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < length; ++i) {
|
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));
|
JS::RootedObject jsModule(aCx, JS_NewArrayObject(aCx, 2));
|
||||||
if (!jsModule) {
|
if (!jsModule) {
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
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)) {
|
if (!JS_DefineElement(aCx, jsModule, 0, name, JSPROP_ENUMERATE)) {
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
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)) {
|
if (!JS_DefineElement(aCx, jsModule, 1, breakpadId, JSPROP_ENUMERATE)) {
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
}
|
}
|
||||||
@ -247,7 +265,7 @@ nsHangDetails::Submit()
|
|||||||
case GeckoProcessType_Content: {
|
case GeckoProcessType_Content: {
|
||||||
auto cc = dom::ContentChild::GetSingleton();
|
auto cc = dom::ContentChild::GetSingleton();
|
||||||
if (cc) {
|
if (cc) {
|
||||||
hangDetails->mDetails.mRemoteType.Assign(cc->GetRemoteType());
|
hangDetails->mDetails.remoteType().Assign(cc->GetRemoteType());
|
||||||
Unused << cc->SendBHRThreadHang(hangDetails->mDetails);
|
Unused << cc->SendBHRThreadHang(hangDetails->mDetails);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -272,16 +290,6 @@ nsHangDetails::Submit()
|
|||||||
NS_WARNING("Unsupported BHR process type - discarding hang.");
|
NS_WARNING("Unsupported BHR process type - discarding hang.");
|
||||||
break;
|
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,
|
nsresult rv = SystemGroup::Dispatch(TaskCategory::Other,
|
||||||
@ -291,12 +299,88 @@ nsHangDetails::Submit()
|
|||||||
|
|
||||||
NS_IMPL_ISUPPORTS(nsHangDetails, nsIHangDetails)
|
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
|
NS_IMETHODIMP
|
||||||
ProcessHangStackRunnable::Run()
|
ProcessHangStackRunnable::Run()
|
||||||
{
|
{
|
||||||
// NOTE: Reading module information can take a long time, which is why we do
|
// NOTE: Reading module information can take a long time, which is why we do
|
||||||
// it off-main-thread.
|
// it off-main-thread.
|
||||||
mHangDetails.mStack.ReadModuleInformation();
|
ReadModuleInformation(mHangDetails.stack());
|
||||||
|
|
||||||
RefPtr<nsHangDetails> hangDetails = new nsHangDetails(Move(mHangDetails));
|
RefPtr<nsHangDetails> hangDetails = new nsHangDetails(Move(mHangDetails));
|
||||||
hangDetails->Submit();
|
hangDetails->Submit();
|
||||||
@ -305,53 +389,3 @@ ProcessHangStackRunnable::Run()
|
|||||||
}
|
}
|
||||||
|
|
||||||
} // namespace mozilla
|
} // 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/ProcessedStack.h"
|
||||||
#include "mozilla/RefPtr.h"
|
#include "mozilla/RefPtr.h"
|
||||||
#include "mozilla/Move.h"
|
#include "mozilla/Move.h"
|
||||||
#include "mozilla/HangStack.h"
|
#include "mozilla/HangTypes.h"
|
||||||
#include "mozilla/HangAnnotations.h"
|
#include "mozilla/HangAnnotations.h"
|
||||||
#include "nsTArray.h"
|
#include "nsTArray.h"
|
||||||
#include "nsIHangDetails.h"
|
#include "nsIHangDetails.h"
|
||||||
@ -19,55 +19,6 @@
|
|||||||
|
|
||||||
namespace mozilla {
|
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
|
* HangDetails is the concrete implementaion of nsIHangDetails, and contains the
|
||||||
* infromation which we want to expose to observers of the bhr-thread-hang
|
* 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/MemoryChecking.h"
|
||||||
#include "mozilla/Sprintf.h"
|
#include "mozilla/Sprintf.h"
|
||||||
#include "nsThread.h"
|
#include "nsThread.h"
|
||||||
#include "mozilla/HangStack.h"
|
#include "mozilla/HangTypes.h"
|
||||||
|
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
# pragma GCC diagnostic push
|
# pragma GCC diagnostic push
|
||||||
@ -68,7 +68,7 @@ namespace mozilla {
|
|||||||
|
|
||||||
ThreadStackHelper::ThreadStackHelper()
|
ThreadStackHelper::ThreadStackHelper()
|
||||||
: mStackToFill(nullptr)
|
: mStackToFill(nullptr)
|
||||||
, mMaxStackSize(HangStack::sMaxInlineStorage)
|
, mMaxStackSize(16)
|
||||||
, mMaxBufferSize(512)
|
, mMaxBufferSize(512)
|
||||||
, mDesiredStackSize(0)
|
, mDesiredStackSize(0)
|
||||||
, mDesiredBufferSize(0)
|
, mDesiredBufferSize(0)
|
||||||
@ -90,12 +90,16 @@ ThreadStackHelper::PrepareStackBuffer(HangStack& aStack)
|
|||||||
mDesiredBufferSize = 0;
|
mDesiredBufferSize = 0;
|
||||||
mDesiredStackSize = 0;
|
mDesiredStackSize = 0;
|
||||||
|
|
||||||
// Return false to skip getting the stack and return an empty stack
|
// Clear all of the stack entries.
|
||||||
aStack.clear();
|
aStack.stack().ClearAndRetainStorage();
|
||||||
|
aStack.strbuffer().ClearAndRetainStorage();
|
||||||
|
aStack.modules().Clear();
|
||||||
|
|
||||||
#ifdef MOZ_THREADSTACKHELPER_PSEUDO
|
#ifdef MOZ_THREADSTACKHELPER_PSEUDO
|
||||||
if (!aStack.reserve(mMaxStackSize) ||
|
// Ensure we have enough space in our stack and string buffers for the data we
|
||||||
!aStack.reserve(aStack.capacity()) || // reserve up to the capacity
|
// want to collect.
|
||||||
!aStack.EnsureBufferCapacity(mMaxBufferSize)) {
|
if (!aStack.stack().SetCapacity(mMaxStackSize, fallible) ||
|
||||||
|
!aStack.strbuffer().SetCapacity(mMaxBufferSize, fallible)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -161,17 +165,19 @@ ThreadStackHelper::SetIsMainThread()
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ThreadStackHelper::TryAppendFrame(HangStack::Frame aFrame)
|
ThreadStackHelper::TryAppendFrame(HangEntry aFrame)
|
||||||
{
|
{
|
||||||
MOZ_RELEASE_ASSERT(mStackToFill);
|
MOZ_RELEASE_ASSERT(mStackToFill);
|
||||||
|
|
||||||
// We deduplicate identical frames of kind CONTENT, JIT, WASM, and SUPPRESSED.
|
// We deduplicate identical Content, Jit, Wasm, ChromeScript and Suppressed frames.
|
||||||
switch (aFrame.GetKind()) {
|
switch (aFrame.type()) {
|
||||||
case HangStack::Frame::Kind::CONTENT:
|
case HangEntry::THangEntryContent:
|
||||||
case HangStack::Frame::Kind::JIT:
|
case HangEntry::THangEntryJit:
|
||||||
case HangStack::Frame::Kind::WASM:
|
case HangEntry::THangEntryWasm:
|
||||||
case HangStack::Frame::Kind::SUPPRESSED:
|
case HangEntry::THangEntryChromeScript:
|
||||||
if (!mStackToFill->empty() && mStackToFill->back().GetKind() == aFrame.GetKind()) {
|
case HangEntry::THangEntrySuppressed:
|
||||||
|
if (!mStackToFill->stack().IsEmpty() &&
|
||||||
|
mStackToFill->stack().LastElement().type() == aFrame.type()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -184,8 +190,8 @@ ThreadStackHelper::TryAppendFrame(HangStack::Frame aFrame)
|
|||||||
mDesiredStackSize += 1;
|
mDesiredStackSize += 1;
|
||||||
|
|
||||||
// Perform the append if we have enough space to do so.
|
// Perform the append if we have enough space to do so.
|
||||||
if (mStackToFill->canAppendWithoutRealloc(1)) {
|
if (mStackToFill->stack().Capacity() > mStackToFill->stack().Length()) {
|
||||||
mStackToFill->infallibleAppend(aFrame);
|
mStackToFill->stack().AppendElement(mozilla::Move(aFrame));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,14 +199,14 @@ void
|
|||||||
ThreadStackHelper::CollectNativeLeafAddr(void* aAddr)
|
ThreadStackHelper::CollectNativeLeafAddr(void* aAddr)
|
||||||
{
|
{
|
||||||
MOZ_RELEASE_ASSERT(mStackToFill);
|
MOZ_RELEASE_ASSERT(mStackToFill);
|
||||||
TryAppendFrame(HangStack::Frame(reinterpret_cast<uintptr_t>(aAddr)));
|
TryAppendFrame(HangEntryProgCounter(reinterpret_cast<uintptr_t>(aAddr)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ThreadStackHelper::CollectJitReturnAddr(void* aAddr)
|
ThreadStackHelper::CollectJitReturnAddr(void* aAddr)
|
||||||
{
|
{
|
||||||
MOZ_RELEASE_ASSERT(mStackToFill);
|
MOZ_RELEASE_ASSERT(mStackToFill);
|
||||||
TryAppendFrame(HangStack::Frame::Jit());
|
TryAppendFrame(HangEntryJit());
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -209,7 +215,7 @@ ThreadStackHelper::CollectWasmFrame(const char* aLabel)
|
|||||||
MOZ_RELEASE_ASSERT(mStackToFill);
|
MOZ_RELEASE_ASSERT(mStackToFill);
|
||||||
// We don't want to collect WASM frames, as they are probably for content, so
|
// We don't want to collect WASM frames, as they are probably for content, so
|
||||||
// we just add a "(content wasm)" frame.
|
// we just add a "(content wasm)" frame.
|
||||||
TryAppendFrame(HangStack::Frame::Wasm());
|
TryAppendFrame(HangEntryWasm());
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@ -263,18 +269,39 @@ ThreadStackHelper::CollectPseudoEntry(const js::ProfileEntry& aEntry)
|
|||||||
{
|
{
|
||||||
// For non-js frames we just include the raw label.
|
// For non-js frames we just include the raw label.
|
||||||
if (!aEntry.isJs()) {
|
if (!aEntry.isJs()) {
|
||||||
const char* label = aEntry.label();
|
const char* entryLabel = aEntry.label();
|
||||||
TryAppendFrame(HangStack::Frame(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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!aEntry.script()) {
|
if (!aEntry.script()) {
|
||||||
TryAppendFrame(HangStack::Frame::Suppressed());
|
TryAppendFrame(HangEntrySuppressed());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsChromeJSScript(aEntry.script())) {
|
if (!IsChromeJSScript(aEntry.script())) {
|
||||||
TryAppendFrame(HangStack::Frame::Content());
|
TryAppendFrame(HangEntryContent());
|
||||||
return;
|
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
|
char buffer[128]; // Enough to fit longest js file name from the tree
|
||||||
size_t len = SprintfLiteral(buffer, "%s:%u", basename, lineno);
|
size_t len = SprintfLiteral(buffer, "%s:%u", basename, lineno);
|
||||||
if (len < sizeof(buffer)) {
|
if (len < sizeof(buffer)) {
|
||||||
mDesiredBufferSize += len + 1;
|
mDesiredBufferSize += len + 1;
|
||||||
if (mStackToFill->canAppendWithoutRealloc(1) &&
|
|
||||||
mStackToFill->AvailableBufferSize() >= len + 1) {
|
if (mStackToFill->stack().Capacity() > mStackToFill->stack().Length() &&
|
||||||
mStackToFill->InfallibleAppendViaBuffer(buffer, len);
|
(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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mStackToFill->canAppendWithoutRealloc(1)) {
|
TryAppendFrame(HangEntryChromeScript());
|
||||||
mStackToFill->infallibleAppend(HangStack::Frame("(chrome script)"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
@ -53,13 +53,6 @@ namespace mozilla {
|
|||||||
*/
|
*/
|
||||||
class ThreadStackHelper : public ProfilerStackCollector
|
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:
|
private:
|
||||||
HangStack* mStackToFill;
|
HangStack* mStackToFill;
|
||||||
Array<char, nsThread::kRunnableNameBufSize>* mRunnableNameBuffer;
|
Array<char, nsThread::kRunnableNameBufSize>* mRunnableNameBuffer;
|
||||||
@ -87,6 +80,11 @@ public:
|
|||||||
*/
|
*/
|
||||||
void GetStack(HangStack& aStack, nsACString& aRunnableName, bool aStackWalk);
|
void GetStack(HangStack& aStack, nsACString& aRunnableName, bool aStackWalk);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the thread's profiler thread ID.
|
||||||
|
*/
|
||||||
|
int GetThreadId() const { return mThreadId; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/**
|
/**
|
||||||
* ProfilerStackCollector
|
* ProfilerStackCollector
|
||||||
@ -98,7 +96,7 @@ protected:
|
|||||||
virtual void CollectPseudoEntry(const js::ProfileEntry& aEntry) override;
|
virtual void CollectPseudoEntry(const js::ProfileEntry& aEntry) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void TryAppendFrame(mozilla::HangStack::Frame aFrame);
|
void TryAppendFrame(mozilla::HangEntry aFrame);
|
||||||
|
|
||||||
// The profiler's unique thread identifier for the target thread.
|
// The profiler's unique thread identifier for the target thread.
|
||||||
int mThreadId;
|
int mThreadId;
|
||||||
|
@ -32,13 +32,15 @@ XPIDL_MODULE = 'backgroundhangmonitor'
|
|||||||
EXPORTS.mozilla += [
|
EXPORTS.mozilla += [
|
||||||
'BackgroundHangMonitor.h',
|
'BackgroundHangMonitor.h',
|
||||||
'HangDetails.h',
|
'HangDetails.h',
|
||||||
'HangStack.h',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
UNIFIED_SOURCES += [
|
UNIFIED_SOURCES += [
|
||||||
'BackgroundHangMonitor.cpp',
|
'BackgroundHangMonitor.cpp',
|
||||||
'HangDetails.cpp',
|
'HangDetails.cpp',
|
||||||
'HangStack.cpp',
|
]
|
||||||
|
|
||||||
|
IPDL_SOURCES += [
|
||||||
|
'HangTypes.ipdlh',
|
||||||
]
|
]
|
||||||
|
|
||||||
if CONFIG['MOZ_GECKO_PROFILER']:
|
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 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.
|
// entries between samples.
|
||||||
while (e.Has()) {
|
while (e.Has()) {
|
||||||
if (e.Get().IsThreadId()) {
|
if (e.Get().IsThreadId()) {
|
||||||
@ -918,16 +918,17 @@ ProfileBuffer::StreamMarkersToJSON(SpliceableJSONWriter& aWriter,
|
|||||||
{
|
{
|
||||||
EntryGetter e(*this);
|
EntryGetter e(*this);
|
||||||
|
|
||||||
int currentThreadID = -1;
|
// Stream all markers whose threadId matches aThreadId. We skip other entries,
|
||||||
|
// because we process them in StreamSamplesToJSON().
|
||||||
// Stream all markers whose threadId matches aThreadId. All other entries are
|
//
|
||||||
// skipped, 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()) {
|
while (e.Has()) {
|
||||||
if (e.Get().IsThreadId()) {
|
if (e.Get().IsMarker()) {
|
||||||
currentThreadID = e.Get().u.mInt;
|
|
||||||
} else if (currentThreadID == aThreadId && e.Get().IsMarker()) {
|
|
||||||
const ProfilerMarker* marker = e.Get().u.mMarker;
|
const ProfilerMarker* marker = e.Get().u.mMarker;
|
||||||
if (marker->GetTime() >= aSinceTime) {
|
if (marker->GetTime() >= aSinceTime &&
|
||||||
|
marker->GetThreadId() == aThreadId) {
|
||||||
marker->StreamJSON(aWriter, aProcessStartTime, aUniqueStacks);
|
marker->StreamJSON(aWriter, aProcessStartTime, aUniqueStacks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ class ProfilerMarker
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ProfilerMarker(const char* aMarkerName,
|
explicit ProfilerMarker(const char* aMarkerName,
|
||||||
|
int aThreadId,
|
||||||
mozilla::UniquePtr<ProfilerMarkerPayload>
|
mozilla::UniquePtr<ProfilerMarkerPayload>
|
||||||
aPayload = nullptr,
|
aPayload = nullptr,
|
||||||
double aTime = 0)
|
double aTime = 0)
|
||||||
@ -30,6 +31,7 @@ public:
|
|||||||
, mNext{nullptr}
|
, mNext{nullptr}
|
||||||
, mTime(aTime)
|
, mTime(aTime)
|
||||||
, mGenID{0}
|
, mGenID{0}
|
||||||
|
, mThreadId{aThreadId}
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void SetGeneration(uint32_t aGenID) { mGenID = aGenID; }
|
void SetGeneration(uint32_t aGenID) { mGenID = aGenID; }
|
||||||
@ -38,6 +40,8 @@ public:
|
|||||||
|
|
||||||
double GetTime() const { return mTime; }
|
double GetTime() const { return mTime; }
|
||||||
|
|
||||||
|
int GetThreadId() const { return mThreadId; }
|
||||||
|
|
||||||
void StreamJSON(SpliceableJSONWriter& aWriter,
|
void StreamJSON(SpliceableJSONWriter& aWriter,
|
||||||
const mozilla::TimeStamp& aProcessStartTime,
|
const mozilla::TimeStamp& aProcessStartTime,
|
||||||
UniqueStacks& aUniqueStacks) const
|
UniqueStacks& aUniqueStacks) const
|
||||||
@ -69,6 +73,7 @@ private:
|
|||||||
ProfilerMarker* mNext;
|
ProfilerMarker* mNext;
|
||||||
double mTime;
|
double mTime;
|
||||||
uint32_t mGenID;
|
uint32_t mGenID;
|
||||||
|
int mThreadId;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
@ -25,9 +25,8 @@ ThreadInfo::ThreadInfo(const char* aName,
|
|||||||
void* aStackTop)
|
void* aStackTop)
|
||||||
: mName(strdup(aName))
|
: mName(strdup(aName))
|
||||||
, mRegisterTime(TimeStamp::Now())
|
, mRegisterTime(TimeStamp::Now())
|
||||||
, mThreadId(aThreadId)
|
|
||||||
, mIsMainThread(aIsMainThread)
|
, mIsMainThread(aIsMainThread)
|
||||||
, mRacyInfo(mozilla::MakeNotNull<RacyThreadInfo*>())
|
, mRacyInfo(mozilla::MakeNotNull<RacyThreadInfo*>(aThreadId))
|
||||||
, mPlatformData(AllocPlatformData(aThreadId))
|
, mPlatformData(AllocPlatformData(aThreadId))
|
||||||
, mStackTop(aStackTop)
|
, mStackTop(aStackTop)
|
||||||
, mIsBeingProfiled(false)
|
, mIsBeingProfiled(false)
|
||||||
@ -260,7 +259,7 @@ ThreadInfo::FlushSamplesAndMarkers(const TimeStamp& aProcessStartTime,
|
|||||||
SpliceableChunkedJSONWriter b;
|
SpliceableChunkedJSONWriter b;
|
||||||
b.StartBareList();
|
b.StartBareList();
|
||||||
{
|
{
|
||||||
aBuffer.StreamSamplesToJSON(b, mThreadId, /* aSinceTime = */ 0,
|
aBuffer.StreamSamplesToJSON(b, ThreadId(), /* aSinceTime = */ 0,
|
||||||
&mFirstSavedStreamedSampleTime,
|
&mFirstSavedStreamedSampleTime,
|
||||||
mContext, *mUniqueStacks);
|
mContext, *mUniqueStacks);
|
||||||
}
|
}
|
||||||
@ -272,7 +271,7 @@ ThreadInfo::FlushSamplesAndMarkers(const TimeStamp& aProcessStartTime,
|
|||||||
SpliceableChunkedJSONWriter b;
|
SpliceableChunkedJSONWriter b;
|
||||||
b.StartBareList();
|
b.StartBareList();
|
||||||
{
|
{
|
||||||
aBuffer.StreamMarkersToJSON(b, mThreadId, aProcessStartTime,
|
aBuffer.StreamMarkersToJSON(b, ThreadId(), aProcessStartTime,
|
||||||
/* aSinceTime = */ 0, *mUniqueStacks);
|
/* aSinceTime = */ 0, *mUniqueStacks);
|
||||||
}
|
}
|
||||||
b.EndBareList();
|
b.EndBareList();
|
||||||
|
@ -23,8 +23,9 @@
|
|||||||
class RacyThreadInfo final : public PseudoStack
|
class RacyThreadInfo final : public PseudoStack
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
RacyThreadInfo()
|
explicit RacyThreadInfo(int aThreadId)
|
||||||
: PseudoStack()
|
: PseudoStack()
|
||||||
|
, mThreadId(aThreadId)
|
||||||
, mSleep(AWAKE)
|
, mSleep(AWAKE)
|
||||||
{
|
{
|
||||||
MOZ_COUNT_CTOR(RacyThreadInfo);
|
MOZ_COUNT_CTOR(RacyThreadInfo);
|
||||||
@ -57,7 +58,7 @@ public:
|
|||||||
double aTime)
|
double aTime)
|
||||||
{
|
{
|
||||||
ProfilerMarker* marker =
|
ProfilerMarker* marker =
|
||||||
new ProfilerMarker(aMarkerName, Move(aPayload), aTime);
|
new ProfilerMarker(aMarkerName, mThreadId, Move(aPayload), aTime);
|
||||||
mPendingMarkers.insert(marker);
|
mPendingMarkers.insert(marker);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,10 +114,16 @@ public:
|
|||||||
|
|
||||||
bool IsSleeping() { return mSleep != AWAKE; }
|
bool IsSleeping() { return mSleep != AWAKE; }
|
||||||
|
|
||||||
|
int ThreadId() const { return mThreadId; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// A list of pending markers that must be moved to the circular buffer.
|
// A list of pending markers that must be moved to the circular buffer.
|
||||||
ProfilerSignalSafeLinkedList<ProfilerMarker> mPendingMarkers;
|
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
|
// 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,
|
// been previously observed. This is used for an optimization: in some cases,
|
||||||
// when a thread is asleep, we duplicate the previous sample, which is
|
// when a thread is asleep, we duplicate the previous sample, which is
|
||||||
@ -173,7 +180,10 @@ public:
|
|||||||
~ThreadInfo();
|
~ThreadInfo();
|
||||||
|
|
||||||
const char* Name() const { return mName.get(); }
|
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; }
|
bool IsMainThread() const { return mIsMainThread; }
|
||||||
|
|
||||||
@ -196,7 +206,6 @@ private:
|
|||||||
mozilla::UniqueFreePtr<char> mName;
|
mozilla::UniqueFreePtr<char> mName;
|
||||||
mozilla::TimeStamp mRegisterTime;
|
mozilla::TimeStamp mRegisterTime;
|
||||||
mozilla::TimeStamp mUnregisterTime;
|
mozilla::TimeStamp mUnregisterTime;
|
||||||
int mThreadId;
|
|
||||||
const bool mIsMainThread;
|
const bool mIsMainThread;
|
||||||
|
|
||||||
// The thread's RacyThreadInfo. This is an owning pointer. It could be an
|
// 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);
|
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
|
void
|
||||||
profiler_tracing(const char* aCategory, const char* aMarkerName,
|
profiler_tracing(const char* aCategory, const char* aMarkerName,
|
||||||
TracingKind aKind)
|
TracingKind aKind)
|
||||||
|
@ -462,6 +462,11 @@ void profiler_add_marker(const char* aMarkerName);
|
|||||||
void profiler_add_marker(const char* aMarkerName,
|
void profiler_add_marker(const char* aMarkerName,
|
||||||
mozilla::UniquePtr<ProfilerMarkerPayload> aPayload);
|
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 {
|
enum TracingKind {
|
||||||
TRACING_EVENT,
|
TRACING_EVENT,
|
||||||
TRACING_INTERVAL_START,
|
TRACING_INTERVAL_START,
|
||||||
|
@ -168,7 +168,9 @@ private:
|
|||||||
{
|
{
|
||||||
MOZ_ASSERT(aSize <= Available());
|
MOZ_ASSERT(aSize <= Available());
|
||||||
char* p = reinterpret_cast<char*>(header.offset);
|
char* p = reinterpret_cast<char*>(header.offset);
|
||||||
|
MOZ_RELEASE_ASSERT(p);
|
||||||
header.offset += aSize;
|
header.offset += aSize;
|
||||||
|
canary.Check();
|
||||||
MOZ_MAKE_MEM_UNDEFINED(p, aSize);
|
MOZ_MAKE_MEM_UNDEFINED(p, aSize);
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user