Merge mozilla-central to autoland a=merge on a CLOSED TREE

This commit is contained in:
Cosmin Sabou 2018-01-17 11:49:21 +02:00
commit 75435dc3b2
83 changed files with 1934 additions and 2251 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

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

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

View File

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

View File

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

View File

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

View File

@ -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=[],

View File

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

View File

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

View File

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

View File

@ -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'] = [{

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -81,6 +81,7 @@ function runTest() {
} }
} }
SimpleTest.requestCompleteLog(); // Bug 1407296
runTest(); runTest();
</script> </script>

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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