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

View File

@ -4,6 +4,7 @@ MOZ_AUTOMATION_L10N_CHECK=0
. "$topsrcdir/build/mozconfig.win-common"
. "$topsrcdir/browser/config/mozconfigs/common"
. "$topsrcdir/browser/config/mozconfigs/win64/common-win64"
ac_add_options --enable-optimize
ac_add_options --enable-debug
@ -11,7 +12,7 @@ ac_add_options --enable-debug
ac_add_options --enable-clang-plugin
ac_add_options --enable-mozsearch-plugin
. $topsrcdir/build/win32/mozconfig.vs-latest
. $topsrcdir/build/win64/mozconfig.vs-latest
. "$topsrcdir/build/mozconfig.common.override"
. "$topsrcdir/build/mozconfig.clang-cl"

View File

@ -279,6 +279,22 @@ this.TestRunner = {
return {bounds, rects};
},
_do_skip(reason, combo, config, func) {
const { todo } = reason;
if (todo) {
this.mochitestScope.todo(
false,
`Skipped configuration ` +
`[ ${combo.map((e) => e.name).join(", ")} ] for failure in ` +
`${config.name}.${func}: ${todo}`);
} else {
this.mochitestScope.info(
`\tSkipped configuration ` +
`[ ${combo.map((e) => e.name).join(", ")} ] ` +
`for "${reason}" in ${config.name}.${func}`);
}
},
async _performCombo(combo) {
let paddedComboIndex = padLeft(this.currentComboIndex + 1, String(this.combos.length).length);
this.mochitestScope.info(
@ -298,9 +314,9 @@ this.TestRunner = {
this.mochitestScope.info("called " + config.name);
// Add a default timeout of 500ms to avoid conflicts when configurations
// try to apply at the same time. e.g WindowSize and TabsInTitlebar
return Promise.race([applyPromise, timeoutPromise]).then(() => {
return Promise.race([applyPromise, timeoutPromise]).then(result => {
return new Promise((resolve) => {
setTimeout(resolve, 500);
setTimeout(() => resolve(result), 500);
});
});
};
@ -311,7 +327,11 @@ this.TestRunner = {
let config = combo[i];
if (!this._lastCombo || config !== this._lastCombo[i]) {
this.mochitestScope.info(`promising ${config.name}`);
await changeConfig(config);
const reason = await changeConfig(config);
if (reason) {
this._do_skip(reason, combo, config, "applyConfig");
return;
}
}
}
@ -330,18 +350,22 @@ this.TestRunner = {
// have invalidated it.
if (config.verifyConfig) {
this.mochitestScope.info(`checking if the combo is valid with ${config.name}`);
await config.verifyConfig();
const reason = await config.verifyConfig();
if (reason) {
this._do_skip(reason, combo, config, "applyConfig");
return;
}
}
}
} catch (ex) {
this.mochitestScope.info(`\tskipped configuration [ ${combo.map((e) => e.name).join(", ")} ]`);
this.mochitestScope.info(`\treason: ${ex.toString()}`);
// Don't set lastCombo here so that we properly know which configurations
// need to be applied since the last screenshot
// Return so we don't take a screenshot.
this.mochitestScope.ok(false, `Unexpected exception in [ ${combo.map(({ name }) => name).join(", ")} ]: ${ex.toString()}`);
this.mochitestScope.info(`\t${ex}`);
if (ex.stack) {
this.mochitestScope.info(`\t${ex.stack}`);
}
return;
}
this.mochitestScope.info(`Configured UI for [ ${combo.map(({ name }) => name).join(", ")} ] successfully`);
// Collect selectors from combo configs for cropping region
let windowType;

View File

@ -35,17 +35,17 @@ this.AppMenu = {
appMenuHistorySubview: {
selectors: ["#appMenu-popup"],
applyConfig() {
async applyConfig() {
// History has a footer
if (isCustomizing()) {
return Promise.reject("Can't show subviews while customizing");
return "Can't show subviews while customizing";
}
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
let promise = browserWindow.PanelUI.show();
return promise.then(() => {
await browserWindow.PanelUI.show();
browserWindow.PanelUI.showMainView();
browserWindow.document.getElementById("history-panelmenu").click();
});
return undefined;
},
verifyConfig: verifyConfigHelper,
@ -53,16 +53,16 @@ this.AppMenu = {
appMenuHelpSubview: {
selectors: ["#appMenu-popup"],
applyConfig() {
async applyConfig() {
if (isCustomizing()) {
return Promise.reject("Can't show subviews while customizing");
return "Can't show subviews while customizing";
}
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
let promise = browserWindow.PanelUI.show();
return promise.then(() => {
await browserWindow.PanelUI.show();
browserWindow.PanelUI.showMainView();
browserWindow.document.getElementById("PanelUI-help").click();
});
return undefined;
},
verifyConfig: verifyConfigHelper,
@ -73,9 +73,9 @@ this.AppMenu = {
function verifyConfigHelper() {
if (isCustomizing()) {
return Promise.reject("AppMenu verifyConfigHelper");
return "navigator:browser has the customizing attribute";
}
return Promise.resolve("AppMenu verifyConfigHelper");
return undefined;
}
function isCustomizing() {

View File

@ -38,12 +38,12 @@ this.Buttons = {
CustomizableUI.addWidgetToArea("screenshot-widget", CustomizableUI.AREA_FIXED_OVERFLOW_PANEL);
},
verifyConfig() {
async verifyConfig() {
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
if (browserWindow.PanelUI.panel.state == "closed") {
return Promise.reject("The button isn't shown when the panel isn't open.");
return "The button isn't shown when the panel isn't open.";
}
return Promise.resolve("menuPanelButtons.verifyConfig");
return undefined;
},
},
@ -53,12 +53,12 @@ this.Buttons = {
CustomizableUI.removeWidgetFromArea("screenshot-widget");
},
verifyConfig() {
async verifyConfig() {
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
if (browserWindow.document.documentElement.getAttribute("customizing") != "true") {
return Promise.reject("The button isn't shown when we're not in customize mode.");
return "The button isn't shown when we're not in customize mode.";
}
return Promise.resolve("custPaletteButtons.verifyConfig");
return undefined;
},
},
},

View File

@ -55,7 +55,7 @@ this.ControlCenter = {
},
async verifyConfig() {
return Promise.reject("Bug 1373563: intermittent controlCenter_localFile on Taskcluster");
return { todo: "Bug 1373563: intermittent controlCenter_localFile on Taskcluster" };
},
},

View File

@ -112,8 +112,10 @@ this.PermissionPrompts = {
// We want to skip the progress-notification, so we wait for
// the install-confirmation screen to be "not hidden" = shown.
await BrowserTestUtils.waitForCondition(() => !notification.hasAttribute("hidden"),
"addon install confirmation did not show", 200);
return BrowserTestUtils.waitForCondition(() => !notification.hasAttribute("hidden"),
"addon install confirmation did not show", 200).catch((msg) => {
return Promise.resolve({todo: msg});
});
},
},
},

View File

@ -36,8 +36,14 @@ this.Preferences = {
}
this.configurations[configName] = {};
this.configurations[configName].selectors = ["#browser"];
if (primary == "panePrivacy" && customFn) {
this.configurations[configName].applyConfig = async () => {
return {todo: `${configName} times out on the try server`};
};
} else {
this.configurations[configName].applyConfig = prefHelper.bind(null, primary, customFn);
}
}
},
configurations: {},
@ -49,7 +55,9 @@ let prefHelper = async function(primary, customFn = null) {
// close any dialog that might still be open
await ContentTask.spawn(selectedBrowser, null, async function() {
if (!content.window.gSubDialog) {
// Check that gSubDialog is defined on the content window
// and that there is an open dialog to close
if (!content.window.gSubDialog || !content.window.gSubDialog._topDialog) {
return;
}
content.window.gSubDialog.close();
@ -73,9 +81,11 @@ let prefHelper = async function(primary, customFn = null) {
if (customFn) {
let customPaintPromise = paintPromise(browserWindow);
await customFn(selectedBrowser);
let result = await customFn(selectedBrowser);
await customPaintPromise;
return result;
}
return undefined;
};
function paintPromise(browserWindow) {
@ -99,8 +109,13 @@ async function cacheGroup(aBrowser) {
}
async function DNTDialog(aBrowser) {
await ContentTask.spawn(aBrowser, null, async function() {
content.document.getElementById("doNotTrackSettings").click();
return ContentTask.spawn(aBrowser, null, async function() {
const button = content.document.getElementById("doNotTrackSettings");
if (!button) {
return {todo: "The dialog may have exited before we could click the button"};
}
button.click();
return undefined;
});
}

View File

@ -53,7 +53,7 @@ this.Tabs = {
hoverTab(browserWindow.gBrowser.tabs[2]);
// also hover the new tab button
let newTabButton = browserWindow.document.getAnonymousElementByAttribute(browserWindow.
gBrowser.tabContainer, "class", "tabs-newtab-button");
gBrowser.tabContainer, "anonid", "tabs-newtab-button");
hoverTab(newTabButton);
browserWindow.gBrowser.tabs[browserWindow.gBrowser.tabs.length - 1].
setAttribute("beforehovered", true);
@ -134,6 +134,7 @@ async function allTabTitlesDisplayed(browserWindow) {
"about:home": "New Tab",
"about:newtab": "New Tab",
"about:addons": "Add-ons Manager",
"about:privatebrowsing": "Open a private window?"
};
specToTitleMap[PREFS_TAB] = "browser/skin/settings.svg";
specToTitleMap[CUST_TAB] = "browser/skin/customize.svg";

View File

@ -21,7 +21,7 @@ this.TabsInTitlebar = {
selectors: ["#navigator-toolbox"],
async applyConfig() {
if (Services.appinfo.OS == "Linux") {
return Promise.reject("TabsInTitlebar isn't supported on Linux");
return "TabsInTitlebar isn't supported on Linux";
}
Services.prefs.setBoolPref(PREF_TABS_IN_TITLEBAR, true);
return undefined;

View File

@ -36,7 +36,7 @@ this.Toolbars = {
async verifyConfig() {
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
if (browserWindow.fullScreen) {
return Promise.reject("The bookmark toolbar and menubar are not shown in fullscreen.");
return "The bookmark toolbar and menubar are not shown in fullscreen.";
}
return undefined;
},

View File

@ -11,6 +11,10 @@ add_task(async function capture() {
return;
}
if (AppConstants.platform == "macosx") {
// Bug 1425394 - Generate output so mozprocess knows we're still alive for the long session.
SimpleTest.requestCompleteLog();
}
let sets = ["TabsInTitlebar", "Tabs", "WindowSize", "Toolbars", "LightweightThemes", "UIDensities"];
await TestRunner.start(sets, "primaryUI");
});

View File

@ -1,9 +1,9 @@
This is the debugger.html project output.
See https://github.com/devtools-html/debugger.html
Version 8.0
commit https://github.com/devtools-html/debugger.html/commit/9be8e9d9e7a70730bc02e50ae019028f1f06b14e
comparison https://github.com/devtools-html/debugger.html/compare/release-7...release-8
Version 9.0
commit: https://github.com/devtools-html/debugger.html/commit/0de8d3f673ee0f0030d666f1827380e17bef8036
comparison: https://github.com/devtools-html/debugger.html/compare/release-8...release-9
Packages:
- babel-plugin-transform-es2015-modules-commonjs @6.26.0

View File

@ -1568,7 +1568,7 @@ html[dir="rtl"] .managed-tree .tree .node > div {
.outline-list {
list-style-type: none;
width: 100%;
padding: 10px;
padding: 10px 0px;
margin: 0;
}
@ -1581,7 +1581,7 @@ html[dir="rtl"] .managed-tree .tree .node > div {
.outline-list h2 {
font-weight: normal;
font-size: 1em;
margin: 10px 0;
margin: 10px 0 10px 10px;
color: var(--theme-body-color);
}
@ -1596,6 +1596,10 @@ html[dir="rtl"] .managed-tree .tree .node > div {
.outline-list__element:hover {
background: var(--theme-toolbar-background-hover);
}
.outline-list__element .function {
padding-left: 10px;
}
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
@ -2849,16 +2853,40 @@ html .breakpoints-list .breakpoint.paused {
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
.expression-input-form {
width: 100%;
}
.input-expression {
width: 100%;
margin: 0px;
margin: 0;
border: 1px;
background-color: var(--theme-sidebar-background);
font-size: 12px;
padding: 0px 20px;
padding: 0.5em 2.16em;
color: var(--theme-body-color);
}
@keyframes shake {
0%,
100% {
transform: translateX(0);
}
20%,
60% {
transform: translateX(-10px);
}
40%,
80% {
transform: translateX(10px);
}
}
.input-expression.error {
border: 1px solid red;
animation: 150ms cubic-bezier(0.07, 0.95, 0, 1) shake;
}
.input-expression::placeholder {
text-align: center;
font-style: italic;
@ -2877,7 +2905,6 @@ html .breakpoints-list .breakpoint.paused {
padding: 0;
}
.expression-input-container {
padding: 0.5em;
display: flex;
}
@ -3367,6 +3394,7 @@ html[dir="rtl"] .dropdown {
padding: 0px 2px;
position: relative;
align-self: center;
height: 100%;
}
.dropdown-button {
@ -3376,6 +3404,8 @@ html[dir="rtl"] .dropdown {
padding: 0;
font-weight: 100;
font-size: 14px;
height: 100%;
width: 24px;
}
.dropdown li {
@ -3396,6 +3426,7 @@ html[dir="rtl"] .dropdown {
height: var(--icon-size);
margin-right: 5px;
vertical-align: middle;
display: inline-block;
}
.dropdown-icon.prettyPrint {

File diff suppressed because one or more lines are too long

View File

@ -27,6 +27,7 @@ add_task(async function() {
"Breakpoint has correct line"
);
await addBreakpoint(dbg, entrySrc, 5);
await addBreakpoint(dbg, entrySrc, 15);
await disableBreakpoint(dbg, entrySrc, 15);
@ -34,7 +35,10 @@ add_task(async function() {
await reload(dbg, "opts.js");
await waitForDispatch(dbg, "LOAD_SOURCE_TEXT");
is(getBreakpoints(getState()).size, 2, "One breakpoint exists");
await waitForPaused(dbg);
assertPausedLocation(dbg);
is(getBreakpoints(getState()).size, 3, "Three breakpoints exist");
ok(
getBreakpoint(getState(), { sourceId: entrySrc.id, line: 13 }),

View File

@ -439,6 +439,9 @@ original=original
# LOCALIZATION NOTE (expressions.placeholder): Placeholder text for expression
# input element
expressions.placeholder=Add watch expression
# LOCALIZATION NOTE (expressions.errorMsg): Error text for expression
# input element
expressions.errorMsg=Invalid expression…
expressions.label=Add watch expression
expressions.accesskey=e

View File

@ -5273,32 +5273,37 @@ nsContentUtils::SetNodeTextContent(nsIContent* aContent,
UPDATE_CONTENT_MODEL, true);
nsAutoMutationBatch mb;
uint32_t childCount = aContent->GetChildCount();
if (aTryReuse && !aValue.IsEmpty()) {
uint32_t removeIndex = 0;
// Let's remove nodes until we find a eTEXT.
while (aContent->HasChildren()) {
nsIContent* child = aContent->GetFirstChild();
if (child->IsNodeOfType(nsINode::eTEXT)) {
break;
}
aContent->RemoveChildNode(child, true);
}
for (uint32_t i = 0; i < childCount; ++i) {
nsIContent* child = aContent->GetChildAt_Deprecated(removeIndex);
if (removeIndex == 0 && child && child->IsNodeOfType(nsINode::eTEXT)) {
// If we have a node, it must be a eTEXT and we reuse it.
if (aContent->HasChildren()) {
nsIContent* child = aContent->GetFirstChild();
MOZ_ASSERT(child->IsNodeOfType(nsINode::eTEXT));
nsresult rv = child->SetText(aValue, true);
NS_ENSURE_SUCCESS(rv, rv);
removeIndex = 1;
}
else {
aContent->RemoveChildAt_Deprecated(removeIndex, true);
// All the following nodes, if they exist, must be deleted.
while (nsIContent* nextChild = child->GetNextSibling()) {
aContent->RemoveChildNode(nextChild, true);
}
}
if (removeIndex == 1) {
if (aContent->HasChildren()) {
return NS_OK;
}
}
else {
mb.Init(aContent, true, false);
for (uint32_t i = 0; i < childCount; ++i) {
aContent->RemoveChildAt_Deprecated(0, true);
while (aContent->HasChildren()) {
aContent->RemoveChildNode(aContent->GetFirstChild(), true);
}
}
mb.RemovalDone();

View File

@ -804,19 +804,9 @@ WebGLContext::AssertCachedGlobalState() const
gl->fIsEnabled(LOCAL_GL_RASTERIZER_DISCARD) == mRasterizerDiscardEnabled);
MOZ_ASSERT(gl->fIsEnabled(LOCAL_GL_SCISSOR_TEST) == mScissorTestEnabled);
GLfloat colorClearValue[4] = {0.0f, 0.0f, 0.0f, 0.0f};
gl->fGetFloatv(LOCAL_GL_COLOR_CLEAR_VALUE, colorClearValue);
const bool ok = IsCacheCorrect(mColorClearValue[0], colorClearValue[0]) &&
IsCacheCorrect(mColorClearValue[1], colorClearValue[1]) &&
IsCacheCorrect(mColorClearValue[2], colorClearValue[2]) &&
IsCacheCorrect(mColorClearValue[3], colorClearValue[3]);
if (!ok) {
gfxCriticalNote << mColorClearValue[0] << " - " << colorClearValue[0] << " = " << (mColorClearValue[0] - colorClearValue[0]) << "\n"
<< mColorClearValue[1] << " - " << colorClearValue[1] << " = " << (mColorClearValue[1] - colorClearValue[1]) << "\n"
<< mColorClearValue[2] << " - " << colorClearValue[2] << " = " << (mColorClearValue[2] - colorClearValue[2]) << "\n"
<< mColorClearValue[3] << " - " << colorClearValue[3] << " = " << (mColorClearValue[3] - colorClearValue[3]);
}
MOZ_ASSERT(ok);
// Cannot trivially check COLOR_CLEAR_VALUE, since in old GL versions glGet may clamp
// based on whether the current framebuffer is floating-point or not.
// This also means COLOR_CLEAR_VALUE save+restore is dangerous!
realGLboolean depthWriteMask = 0;
gl->fGetBooleanv(LOCAL_GL_DEPTH_WRITEMASK, &depthWriteMask);

View File

@ -34,27 +34,6 @@ WebGLExtensionColorBufferFloat::WebGLExtensionColorBufferFloat(WebGLContext* web
FOO(RGBA32F);
#undef FOO
#ifdef DEBUG
const auto gl = webgl->gl;
float was[4] = {};
gl->fGetFloatv(LOCAL_GL_COLOR_CLEAR_VALUE, was);
const float test[4] = {-1.0, 0, 2.0, 255.0};
gl->fClearColor(test[0], test[1], test[2], test[3]);
float now[4] = {};
gl->fGetFloatv(LOCAL_GL_COLOR_CLEAR_VALUE, now);
const bool ok = now[0] == test[0] && now[1] == test[1] &&
now[2] == test[2] && now[3] == test[3];
if (!ok) {
printf_stderr("COLOR_CLEAR_VALUE: now{%f,%f,%f,%f} != test{%f,%f,%f,%f}\n",
test[0], test[1], test[2], test[3],
now[0], now[1], now[2], now[3]);
MOZ_ASSERT(false);
}
gl->fClearColor(was[0], was[1], was[2], was[3]);
#endif
}
WebGLExtensionColorBufferFloat::~WebGLExtensionColorBufferFloat()

View File

@ -7596,7 +7596,7 @@ fail-if = (os == 'android')
[generated/test_conformance__extensions__oes-texture-half-float-with-video.html]
fail-if = (os == 'mac') || (os == 'android') || (os == 'linux') || (os == 'win')
[generated/test_conformance__extensions__oes-texture-half-float.html]
fail-if = (os == 'mac') || (os == 'win') || (os == 'android')
fail-if = (os == 'mac') || (os == 'win') || (os == 'android') || (os == 'linux')
[generated/test_conformance__extensions__oes-vertex-array-object-bufferData.html]
[generated/test_conformance__extensions__oes-vertex-array-object.html]
skip-if = (os == 'mac' && os_version == '10.6')

View File

@ -132,7 +132,7 @@ skip-if = (os == 'android') || (os == 'win')
[generated/test_2_conformance2__vertex_arrays__vertex-array-object.html]
fail-if = (os == 'mac') || (os == 'win')
[generated/test_conformance__extensions__oes-texture-half-float.html]
fail-if = (os == 'mac') || (os == 'win') || (os == 'android')
fail-if = (os == 'mac') || (os == 'win') || (os == 'android') || (os == 'linux')
[generated/test_conformance__attribs__gl-vertexattribpointer.html]
fail-if = (os == 'android')
[generated/test_conformance__ogles__GL__biuDepthRange__biuDepthRange_001_to_002.html]

View File

@ -17,7 +17,7 @@ fail-if = (os == 'android') || (os == 'mac' && os_version == '10.6')
[ensure-exts/test_EXT_blend_minmax.html]
fail-if = (os == 'android')
[ensure-exts/test_EXT_color_buffer_half_float.html]
fail-if = (os == 'android') || (os == 'linux')
fail-if = (os == 'android')
[ensure-exts/test_EXT_disjoint_timer_query.html]
fail-if = (os == 'android') || (os == 'win' && os_version == '5.1')
[ensure-exts/test_EXT_frag_depth.html]
@ -31,7 +31,7 @@ fail-if = (os == 'android') || (os == 'linux')
[ensure-exts/test_OES_standard_derivatives.html]
fail-if = (os == 'android')
[ensure-exts/test_WEBGL_color_buffer_float.html]
fail-if = (os == 'android') || (os == 'linux')
fail-if = (os == 'android')
[ensure-exts/test_WEBGL_compressed_texture_atc.html]
fail-if = (os == 'android') || (os == 'linux') || (os == 'mac') || (os == 'win')
[ensure-exts/test_WEBGL_compressed_texture_es3.html]

View File

@ -73,6 +73,7 @@
#include "mozilla/media/MediaChild.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/WebBrowserPersistDocumentChild.h"
#include "mozilla/HangDetails.h"
#include "imgLoader.h"
#include "GMPServiceChild.h"
#include "NullPrincipal.h"

View File

@ -100,6 +100,7 @@
#include "mozilla/WebBrowserPersistDocumentParent.h"
#include "mozilla/widget/ScreenManager.h"
#include "mozilla/Unused.h"
#include "mozilla/HangDetails.h"
#include "nsAnonymousTemporaryFile.h"
#include "nsAppRunner.h"
#include "nsCDefaultURIFixup.h"

View File

@ -60,6 +60,7 @@ include ServiceWorkerConfiguration;
include GraphicsMessages;
include MemoryReportTypes;
include ClientIPCTypes;
include HangTypes;
// Workaround to prevent error if PContentChild.cpp & PContentBridgeParent.cpp
// are put into different UnifiedProtocolsXX.cpp files.
@ -98,7 +99,6 @@ using mozilla::Telemetry::DynamicScalarDefinition from "mozilla/TelemetryComms.h
using mozilla::Telemetry::ChildEventData from "mozilla/TelemetryComms.h";
using mozilla::Telemetry::DiscardedData from "mozilla/TelemetryComms.h";
using mozilla::CrossProcessMutexHandle from "mozilla/ipc/CrossProcessMutex.h";
using mozilla::HangDetails from "mozilla/HangDetails.h";
union ChromeRegistryItem
{

View File

@ -73,7 +73,7 @@ namespace {
void* sServerHandle = nullptr;
// Initialized during early startup, protected by sMutex.
ipc::FileDescriptor sIPCConnection;
StaticAutoPtr<ipc::FileDescriptor> sIPCConnection;
static bool
StartSoundServer()
@ -204,6 +204,7 @@ static const uint32_t CUBEB_NORMAL_LATENCY_MS = 100;
static const uint32_t CUBEB_NORMAL_LATENCY_FRAMES = 1024;
namespace CubebUtils {
cubeb* GetCubebContextUnlocked();
void PrefChanged(const char* aPref, void* aClosure)
{
@ -405,12 +406,12 @@ void InitAudioIPCConnection()
MOZ_ASSERT(NS_IsMainThread());
auto contentChild = dom::ContentChild::GetSingleton();
auto promise = contentChild->SendCreateAudioIPCConnection();
promise->Then(AbstractThread::GetCurrent(),
promise->Then(AbstractThread::MainThread(),
__func__,
[](ipc::FileDescriptor aFD) {
StaticMutexAutoLock lock(sMutex);
MOZ_ASSERT(!sIPCConnection.IsValid());
sIPCConnection = aFD;
MOZ_ASSERT(!sIPCConnection);
sIPCConnection = new ipc::FileDescriptor(aFD);
},
[](mozilla::ipc::ResponseRejectReason aReason) {
MOZ_LOG(gCubebLog, LogLevel::Error, ("SendCreateAudioIPCConnection failed: %d",
@ -452,10 +453,10 @@ cubeb* GetCubebContextUnlocked()
if (sCubebSandbox) {
if (XRE_IsParentProcess()) {
// TODO: Don't use audio IPC when within the same process.
MOZ_ASSERT(!sIPCConnection.IsValid());
sIPCConnection = CreateAudioIPCConnection();
MOZ_ASSERT(!sIPCConnection);
sIPCConnection = new ipc::FileDescriptor(CreateAudioIPCConnection());
} else {
MOZ_DIAGNOSTIC_ASSERT(sIPCConnection.IsValid());
MOZ_DIAGNOSTIC_ASSERT(sIPCConnection);
}
}
@ -463,8 +464,9 @@ cubeb* GetCubebContextUnlocked()
int rv = sCubebSandbox
? audioipc_client_init(&sCubebContext, sBrandName,
sIPCConnection.ClonePlatformHandle().release())
sIPCConnection->ClonePlatformHandle().release())
: cubeb_init(&sCubebContext, sBrandName, sCubebBackendName.get());
sIPCConnection = nullptr;
#else // !MOZ_CUBEB_REMOTING
int rv = cubeb_init(&sCubebContext, sBrandName, sCubebBackendName.get());
#endif // MOZ_CUBEB_REMOTING
@ -559,8 +561,7 @@ void InitLibrary()
#endif
#ifdef MOZ_CUBEB_REMOTING
if (sCubebSandbox && XRE_IsContentProcess()) {
AbstractThread::MainThread()->Dispatch(
NS_NewRunnableFunction("CubebUtils::InitLibrary", &InitAudioIPCConnection));
InitAudioIPCConnection();
}
#endif
}
@ -585,7 +586,7 @@ void ShutdownLibrary()
sCubebState = CubebState::Shutdown;
#ifdef MOZ_CUBEB_REMOTING
sIPCConnection = ipc::FileDescriptor();
sIPCConnection = nullptr;
ShutdownSoundServer();
#endif
}

View File

@ -38,11 +38,9 @@ enum Side {
Output
};
void PrefChanged(const char* aPref, void* aClosure);
double GetVolumeScale();
bool GetFirstStream();
cubeb* GetCubebContext();
cubeb* GetCubebContextUnlocked();
void ReportCubebStreamInitFailure(bool aIsFirstStream);
void ReportCubebBackendUsed();
uint32_t GetCubebPlaybackLatencyInMilliseconds();

View File

@ -53,11 +53,6 @@ public:
static const int DEFAULT_169_VIDEO_WIDTH = 1280;
static const int DEFAULT_169_VIDEO_HEIGHT = 720;
// This allows using whatever rate the graph is using for the
// MediaStreamTrack. This is useful for microphone data, we know it's already
// at the correct rate for insertion in the MSG.
static const int USE_GRAPH_RATE = -1;
/* Populate an array of video sources in the nsTArray. Also include devices
* that are currently unavailable. */
virtual void EnumerateVideoDevices(dom::MediaSourceEnum,

View File

@ -543,7 +543,6 @@ private:
nsString mDeviceName;
nsCString mDeviceUUID;
int32_t mSampleFrequency;
uint64_t mTotalFrames;
uint64_t mLastLogFrames;

View File

@ -70,7 +70,6 @@ MediaEngineWebRTCMicrophoneSource::MediaEngineWebRTCMicrophoneSource(
, mExtendedFilter(aExtendedFilter)
, mTrackID(TRACK_NONE)
, mStarted(false)
, mSampleFrequency(MediaEngine::USE_GRAPH_RATE)
, mTotalFrames(0)
, mLastLogFrames(0)
, mSkipProcessing(false)
@ -481,9 +480,7 @@ MediaEngineWebRTCMicrophoneSource::Start(SourceMediaStream *aStream,
}
AudioSegment* segment = new AudioSegment();
if (mSampleFrequency == MediaEngine::USE_GRAPH_RATE) {
mSampleFrequency = aStream->GraphRate();
}
aStream->AddAudioTrack(aID, aStream->GraphRate(), 0, segment, SourceMediaStream::ADDTRACK_QUEUED);
// XXX Make this based on the pref.
@ -773,7 +770,7 @@ MediaEngineWebRTCMicrophoneSource::InsertInGraph(const T* aBuffer,
if (MOZ_LOG_TEST(AudioLogModule(), LogLevel::Debug)) {
mTotalFrames += aFrames;
if (mTotalFrames > mLastLogFrames + mSampleFrequency) { // ~ 1 second
if (mTotalFrames > mLastLogFrames + mSources[0]->GraphRate()) { // ~ 1 second
MOZ_LOG(AudioLogModule(), LogLevel::Debug,
("%p: Inserting %zu samples into graph, total frames = %" PRIu64,
(void*)this, aFrames, mTotalFrames));
@ -894,9 +891,6 @@ MediaEngineWebRTCMicrophoneSource::FreeChannel()
bool
MediaEngineWebRTCMicrophoneSource::AllocChannel()
{
mSampleFrequency = MediaEngine::USE_GRAPH_RATE;
LOG(("%s: sampling rate %u", __FUNCTION__, mSampleFrequency));
mState = kAllocated;
sChannelsOpen++;
return true;

File diff suppressed because it is too large Load Diff

View File

@ -9,7 +9,7 @@
// properties which qualify the exposure of that interface. For example:
//
// [
// "AGlobalInterface",
// "AGlobalInterface", // secure context only
// { name: "ExperimentalThing", release: false },
// { name: "ReallyExperimentalThing", nightly: true },
// { name: "DesktopOnlyThing", desktop: true },
@ -23,54 +23,54 @@
// a JavaScript Engine peer!
var ecmaGlobals =
[
"Array",
"ArrayBuffer",
{name: "Atomics", disabled: true},
"Boolean",
{name: "ByteLengthQueuingStrategy", optional: true},
{name: "CountQueuingStrategy", optional: true},
"DataView",
"Date",
"Error",
"EvalError",
"Float32Array",
"Float64Array",
"Function",
"Infinity",
"Int16Array",
"Int32Array",
"Int8Array",
"InternalError",
"Intl",
"JSON",
"Map",
"Math",
"NaN",
"Number",
"Object",
"Promise",
"Proxy",
"RangeError",
{name: "ReadableStream", optional: true},
"ReferenceError",
"Reflect",
"RegExp",
"Set",
{name: "SharedArrayBuffer", disabled: true},
{name: "SIMD", nightly: true},
"String",
"Symbol",
"SyntaxError",
{name: "TypedObject", nightly: true},
"TypeError",
"Uint16Array",
"Uint32Array",
"Uint8Array",
"Uint8ClampedArray",
"URIError",
"WeakMap",
"WeakSet",
{name: "WebAssembly", optional: true}
{name: "Array", insecureContext: true},
{name: "ArrayBuffer", insecureContext: true},
{name: "Atomics", insecureContext: true, disabled: true},
{name: "Boolean", insecureContext: true},
{name: "ByteLengthQueuingStrategy", insecureContext: true, optional: true},
{name: "CountQueuingStrategy", insecureContext: true, optional: true},
{name: "DataView", insecureContext: true},
{name: "Date", insecureContext: true},
{name: "Error", insecureContext: true},
{name: "EvalError", insecureContext: true},
{name: "Float32Array", insecureContext: true},
{name: "Float64Array", insecureContext: true},
{name: "Function", insecureContext: true},
{name: "Infinity", insecureContext: true},
{name: "Int16Array", insecureContext: true},
{name: "Int32Array", insecureContext: true},
{name: "Int8Array", insecureContext: true},
{name: "InternalError", insecureContext: true},
{name: "Intl", insecureContext: true},
{name: "JSON", insecureContext: true},
{name: "Map", insecureContext: true},
{name: "Math", insecureContext: true},
{name: "NaN", insecureContext: true},
{name: "Number", insecureContext: true},
{name: "Object", insecureContext: true},
{name: "Promise", insecureContext: true},
{name: "Proxy", insecureContext: true},
{name: "RangeError", insecureContext: true},
{name: "ReadableStream", insecureContext: true, optional: true},
{name: "ReferenceError", insecureContext: true},
{name: "Reflect", insecureContext: true},
{name: "RegExp", insecureContext: true},
{name: "Set", insecureContext: true},
{name: "SharedArrayBuffer", insecureContext: true, disabled: true},
{name: "SIMD", insecureContext: true, nightly: true},
{name: "String", insecureContext: true},
{name: "Symbol", insecureContext: true},
{name: "SyntaxError", insecureContext: true},
{name: "TypedObject", insecureContext: true, nightly: true},
{name: "TypeError", insecureContext: true},
{name: "Uint16Array", insecureContext: true},
{name: "Uint32Array", insecureContext: true},
{name: "Uint8Array", insecureContext: true},
{name: "Uint8ClampedArray", insecureContext: true},
{name: "URIError", insecureContext: true},
{name: "WeakMap", insecureContext: true},
{name: "WeakSet", insecureContext: true},
{name: "WebAssembly", insecureContext: true, optional: true}
];
// IMPORTANT: Do not change the list above without review from
// a JavaScript Engine peer!
@ -79,173 +79,173 @@ var ecmaGlobals =
var interfaceNamesInGlobalScope =
[
// IMPORTANT: Do not change this list without review from a DOM peer!
"AbortController",
{name: "AbortController", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"AbortSignal",
{name: "AbortSignal", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"Blob",
{name: "Blob", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"BroadcastChannel",
{name: "BroadcastChannel", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"Cache",
{name: "Cache", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"CacheStorage",
{name: "CacheStorage", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"CloseEvent",
{name: "CloseEvent", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"Crypto",
{name: "Crypto", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"CustomEvent",
{name: "CustomEvent", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"DedicatedWorkerGlobalScope",
{name: "DedicatedWorkerGlobalScope", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"Directory",
{name: "Directory", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"DOMCursor",
{name: "DOMCursor", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"DOMError",
{name: "DOMError", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"DOMException",
{name: "DOMException", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"DOMRequest",
{name: "DOMRequest", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"DOMStringList",
{name: "DOMStringList", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"ErrorEvent",
{name: "ErrorEvent", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"Event",
{name: "Event", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"EventSource",
{name: "EventSource", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"EventTarget",
{name: "EventTarget", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"File",
{name: "File", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"FileList",
{name: "FileList", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"FileReader",
{name: "FileReader", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"FileReaderSync",
{name: "FileReaderSync", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"FormData",
{name: "FormData", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"Headers",
{name: "Headers", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"IDBCursor",
{name: "IDBCursor", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"IDBCursorWithValue",
{name: "IDBCursorWithValue", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"IDBDatabase",
{name: "IDBDatabase", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"IDBFactory",
{name: "IDBFactory", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"IDBIndex",
{name: "IDBIndex", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"IDBKeyRange",
{name: "IDBKeyRange", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"IDBObjectStore",
{name: "IDBObjectStore", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"IDBOpenDBRequest",
{name: "IDBOpenDBRequest", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"IDBRequest",
{name: "IDBRequest", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"IDBTransaction",
{name: "IDBTransaction", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"IDBVersionChangeEvent",
{name: "IDBVersionChangeEvent", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"ImageBitmap",
{name: "ImageBitmap", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"ImageBitmapRenderingContext",
{name: "ImageBitmapRenderingContext", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"ImageData",
{name: "ImageData", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"MessageChannel",
{name: "MessageChannel", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"MessageEvent",
{name: "MessageEvent", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"MessagePort",
{name: "MessagePort", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "NetworkInformation", android: true },
{ name: "NetworkInformation", insecureContext: true, android: true },
// IMPORTANT: Do not change this list without review from a DOM peer!
"Notification",
{name: "Notification", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "OffscreenCanvas", disabled: true },
{ name: "OffscreenCanvas", insecureContext: true, disabled: true },
// IMPORTANT: Do not change this list without review from a DOM peer!
"Performance",
{name: "Performance", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"PerformanceEntry",
{name: "PerformanceEntry", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"PerformanceMark",
{name: "PerformanceMark", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"PerformanceMeasure",
{name: "PerformanceMeasure", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"PerformanceObserver",
{name: "PerformanceObserver", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"PerformanceObserverEntryList",
{name: "PerformanceObserverEntryList", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"ProgressEvent",
{name: "ProgressEvent", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"PushManager",
{name: "PushManager", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"PushSubscription",
{name: "PushSubscription", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"PushSubscriptionOptions",
{name: "PushSubscriptionOptions", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"Request",
{name: "Request", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"Response",
{name: "Response", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"ServiceWorkerRegistration",
{name: "ServiceWorkerRegistration", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "StorageManager", isSecureContext: true, android: false},
{name: "StorageManager", android: false},
// IMPORTANT: Do not change this list without review from a DOM peer!
"SubtleCrypto",
{name: "SubtleCrypto", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"TextDecoder",
{name: "TextDecoder", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"TextEncoder",
{name: "TextEncoder", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"XMLHttpRequest",
{name: "XMLHttpRequest", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"XMLHttpRequestEventTarget",
{name: "XMLHttpRequestEventTarget", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"XMLHttpRequestUpload",
{name: "XMLHttpRequestUpload", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"URL",
{name: "URL", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"URLSearchParams",
{name: "URLSearchParams", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "WebGLActiveInfo", disabled: true },
{ name: "WebGLActiveInfo", insecureContext: true, disabled: true },
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "WebGLBuffer", disabled: true },
{ name: "WebGLBuffer", insecureContext: true, disabled: true },
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "WebGLContextEvent", disabled: true },
{ name: "WebGLContextEvent", insecureContext: true, disabled: true },
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "WebGLFramebuffer", disabled: true },
{ name: "WebGLFramebuffer", insecureContext: true, disabled: true },
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "WebGLProgram", disabled: true },
{ name: "WebGLProgram", insecureContext: true, disabled: true },
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "WebGLRenderbuffer", disabled: true },
{ name: "WebGLRenderbuffer", insecureContext: true, disabled: true },
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "WebGLRenderingContext", disabled: true },
{ name: "WebGLRenderingContext", insecureContext: true, disabled: true },
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "WebGLShader", disabled: true },
{ name: "WebGLShader", insecureContext: true, disabled: true },
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "WebGLShaderPrecisionFormat", disabled: true },
{ name: "WebGLShaderPrecisionFormat", insecureContext: true, disabled: true },
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "WebGLTexture", disabled: true },
{ name: "WebGLTexture", insecureContext: true, disabled: true },
// IMPORTANT: Do not change this list without review from a DOM peer!
{ name: "WebGLUniformLocation", disabled: true },
{ name: "WebGLUniformLocation", insecureContext: true, disabled: true },
// IMPORTANT: Do not change this list without review from a DOM peer!
"WebSocket",
{name: "WebSocket", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"Worker",
{name: "Worker", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"WorkerGlobalScope",
{name: "WorkerGlobalScope", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"WorkerLocation",
{name: "WorkerLocation", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"WorkerNavigator",
{name: "WorkerNavigator", insecureContext: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
];
// IMPORTANT: Do not change the list above without review from a DOM peer!
@ -255,6 +255,7 @@ function createInterfaceMap(version, userAgent) {
var isRelease = !version.includes("a");
var isDesktop = !/Mobile|Tablet/.test(userAgent);
var isAndroid = !!navigator.userAgent.includes("Android");
var isInsecureContext = !self.isSecureContext;
var interfaceMap = {};
@ -262,7 +263,7 @@ function createInterfaceMap(version, userAgent) {
{
for (var entry of interfaces) {
if (typeof(entry) === "string") {
interfaceMap[entry] = true;
interfaceMap[entry] = !isInsecureContext;
} else {
ok(!("pref" in entry), "Bogus pref annotation for " + entry.name);
if ((entry.nightly === !isNightly) ||
@ -270,7 +271,12 @@ function createInterfaceMap(version, userAgent) {
(entry.desktop === !isDesktop) ||
(entry.android === !isAndroid && !entry.nightlyAndroid) ||
(entry.release === !isRelease) ||
(entry.isSecureContext === !isSecureContext) ||
// The insecureContext test is very purposefully converting
// entry.insecureContext to boolean, so undefined will convert to
// false. That way entries without an insecureContext annotation
// will get treated as "insecureContext: false", which means exposed
// only in secure contexts.
(isInsecureContext && !Boolean(entry.insecureContext)) ||
entry.disabled) {
interfaceMap[entry.name] = false;
} else if (entry.optional) {

View File

@ -625,11 +625,10 @@ txMozillaXMLOutput::createTxWrapper()
mDocument->CreateElem(nsDependentAtomString(nsGkAtoms::result),
nsGkAtoms::transformiix, namespaceID);
uint32_t j = 0;
#ifdef DEBUG
// Keep track of the location of the current documentElement, if there is
// one, so we can verify later
uint32_t rootLocation = 0;
uint32_t j = 0, rootLocation = 0;
#endif
for (nsCOMPtr<nsIContent> childContent = mDocument->GetFirstChild();
childContent; childContent = childContent->GetNextSibling()) {
@ -645,11 +644,11 @@ txMozillaXMLOutput::createTxWrapper()
// This is needed for cases when there is no existing
// documentElement in the document.
rootLocation = std::max(rootLocation, j + 1);
#endif
++j;
#endif
}
else {
mDocument->RemoveChildAt_Deprecated(j, true);
mDocument->RemoveChildNode(childContent, true);
rv = wrapper->AppendChildTo(childContent, true);
NS_ENSURE_SUCCESS(rv, rv);

View File

@ -754,30 +754,6 @@ GLContext::InitWithPrefixImpl(const char* prefix, bool trygl)
MarkUnsupported(GLFeature::depth_texture);
}
#endif
if (IsSupported(GLFeature::frag_color_float)) {
float was[4] = {};
fGetFloatv(LOCAL_GL_COLOR_CLEAR_VALUE, was);
const float test[4] = {-1.0, 0, 2.0, 255.0};
fClearColor(test[0], test[1], test[2], test[3]);
float now[4] = {};
fGetFloatv(LOCAL_GL_COLOR_CLEAR_VALUE, now);
fClearColor(was[0], was[1], was[2], was[3]);
const bool unclamped = now[0] == test[0] && now[1] == test[1] &&
now[2] == test[2] && now[3] == test[3];
if (!unclamped) {
printf_stderr("COLOR_CLEAR_VALUE: now{%f,%f,%f,%f} != test{%f,%f,%f,%f}\n",
test[0], test[1], test[2], test[3],
now[0], now[1], now[2], now[3]);
gfxCriticalNote << "GLFeature::frag_color_float failed support probe,"
<< " disabling. (RENDERER: "
<< (const char*)fGetString(LOCAL_GL_RENDERER) << ")";
MarkUnsupported(GLFeature::frag_color_float);
}
}
}
if (IsExtensionSupported(GLContext::ARB_pixel_buffer_object)) {
@ -2071,86 +2047,6 @@ GLContext::AssembleOffscreenFBs(const GLuint colorMSRB,
return isComplete;
}
void
GLContext::ClearSafely()
{
// bug 659349 --- we must be very careful here: clearing a GL framebuffer is nontrivial, relies on a lot of state,
// and in the case of the backbuffer of a WebGL context, state is exposed to scripts.
//
// The code here is taken from WebGLContext::ForceClearFramebufferWithDefaultValues, but I didn't find a good way of
// sharing code with it. WebGL's code is somewhat performance-critical as it is typically called on every frame, so
// WebGL keeps track of GL state to avoid having to query it everytime, and also tries to only do work for actually
// present buffers (e.g. stencil buffer). Doing that here seems like premature optimization,
// as ClearSafely() is called only when e.g. a canvas is resized, not on every animation frame.
realGLboolean scissorTestEnabled;
realGLboolean ditherEnabled;
realGLboolean colorWriteMask[4];
realGLboolean depthWriteMask;
GLint stencilWriteMaskFront, stencilWriteMaskBack;
GLfloat colorClearValue[4];
GLfloat depthClearValue;
GLint stencilClearValue;
// save current GL state
fGetBooleanv(LOCAL_GL_SCISSOR_TEST, &scissorTestEnabled);
fGetBooleanv(LOCAL_GL_DITHER, &ditherEnabled);
fGetBooleanv(LOCAL_GL_COLOR_WRITEMASK, colorWriteMask);
fGetBooleanv(LOCAL_GL_DEPTH_WRITEMASK, &depthWriteMask);
fGetIntegerv(LOCAL_GL_STENCIL_WRITEMASK, &stencilWriteMaskFront);
fGetIntegerv(LOCAL_GL_STENCIL_BACK_WRITEMASK, &stencilWriteMaskBack);
fGetFloatv(LOCAL_GL_COLOR_CLEAR_VALUE, colorClearValue);
fGetFloatv(LOCAL_GL_DEPTH_CLEAR_VALUE, &depthClearValue);
fGetIntegerv(LOCAL_GL_STENCIL_CLEAR_VALUE, &stencilClearValue);
// prepare GL state for clearing
fDisable(LOCAL_GL_SCISSOR_TEST);
fDisable(LOCAL_GL_DITHER);
fColorMask(1, 1, 1, 1);
fClearColor(0.f, 0.f, 0.f, 0.f);
fDepthMask(1);
fClearDepth(1.0f);
fStencilMask(0xffffffff);
fClearStencil(0);
// do clear
fClear(LOCAL_GL_COLOR_BUFFER_BIT |
LOCAL_GL_DEPTH_BUFFER_BIT |
LOCAL_GL_STENCIL_BUFFER_BIT);
// restore GL state after clearing
fColorMask(colorWriteMask[0],
colorWriteMask[1],
colorWriteMask[2],
colorWriteMask[3]);
fClearColor(colorClearValue[0],
colorClearValue[1],
colorClearValue[2],
colorClearValue[3]);
fDepthMask(depthWriteMask);
fClearDepth(depthClearValue);
fStencilMaskSeparate(LOCAL_GL_FRONT, stencilWriteMaskFront);
fStencilMaskSeparate(LOCAL_GL_BACK, stencilWriteMaskBack);
fClearStencil(stencilClearValue);
if (ditherEnabled)
fEnable(LOCAL_GL_DITHER);
else
fDisable(LOCAL_GL_DITHER);
if (scissorTestEnabled)
fEnable(LOCAL_GL_SCISSOR_TEST);
else
fDisable(LOCAL_GL_SCISSOR_TEST);
}
void
GLContext::MarkDestroyed()
{

View File

@ -3576,12 +3576,6 @@ public:
return mScreen.get();
}
/* Clear to transparent black, with 0 depth and stencil,
* while preserving current ClearColor etc. values.
* Useful for resizing offscreen buffers.
*/
void ClearSafely();
bool WorkAroundDriverBugs() const { return mWorkAroundDriverBugs; }
bool IsDrawingToDefaultFramebuffer();

View File

@ -19,6 +19,7 @@
#include "mozilla/ipc/CrashReporterHost.h"
#include "mozilla/layers/LayerTreeOwnerTracker.h"
#include "mozilla/Unused.h"
#include "mozilla/HangDetails.h"
#include "nsIObserverService.h"
#ifdef MOZ_GECKO_PROFILER

View File

@ -32,6 +32,7 @@
#include "mozilla/layers/MemoryReportingMLGPU.h"
#include "mozilla/webrender/RenderThread.h"
#include "mozilla/webrender/WebRenderAPI.h"
#include "mozilla/HangDetails.h"
#include "nsDebugImpl.h"
#include "nsThreadManager.h"
#include "prenv.h"

View File

@ -5,6 +5,7 @@
include GraphicsMessages;
include MemoryReportTypes;
include HangTypes;
include protocol PCompositorManager;
include protocol PImageBridge;
include protocol PProfiler;
@ -23,7 +24,6 @@ using mozilla::Telemetry::ChildEventData from "mozilla/TelemetryComms.h";
using mozilla::Telemetry::DiscardedData from "mozilla/TelemetryComms.h";
using mozilla::gfx::Feature from "gfxFeature.h";
using mozilla::gfx::Fallback from "gfxFallback.h";
using mozilla::HangDetails from "mozilla/HangDetails.h";
namespace mozilla {
namespace gfx {

View File

@ -55,6 +55,10 @@ public:
layers::AutoCompleteTask complete(mTask);
UniquePtr<RenderCompositor> compositor = RenderCompositor::Create(Move(mCompositorWidget));
if (!compositor) {
// RenderCompositor::Create puts a message into gfxCriticalNote if it is nullptr
return;
}
*mUseANGLE = compositor->UseANGLE();

View File

@ -306,6 +306,8 @@ private:
bool aStartAtTop);
#endif // NS_PRINTING
void ReturnToGalleyPresentation();
// Whether we should attach to the top level widget. This is true if we
// are sharing/recycling a single base widget and not creating multiple
// child widgets.
@ -356,7 +358,7 @@ protected:
int mMinFontSize;
int16_t mNumURLStarts;
int16_t mDestroyRefCount; // a second "refcount" for the document viewer's "destroy"
int16_t mDestroyBlockedCount;
unsigned mStopped : 1;
unsigned mLoaded : 1;
@ -502,7 +504,7 @@ nsDocumentViewer::nsDocumentViewer()
mOverrideDPPX(0.0),
mMinFontSize(0),
mNumURLStarts(0),
mDestroyRefCount(0),
mDestroyBlockedCount(0),
mStopped(false),
mLoaded(false),
mDeferredWindowClose(false),
@ -554,7 +556,7 @@ nsDocumentViewer::~nsDocumentViewer()
mPrintJob = nullptr;
}
MOZ_RELEASE_ASSERT(mDestroyRefCount == 0);
MOZ_RELEASE_ASSERT(mDestroyBlockedCount == 0);
NS_ASSERTION(!mPresShell && !mPresContext,
"User did not call nsIContentViewer::Destroy");
if (mPresShell || mPresContext) {
@ -1653,6 +1655,14 @@ nsDocumentViewer::Destroy()
{
NS_ASSERTION(mDocument, "No document in Destroy()!");
// Don't let the document get unloaded while we are printing.
// this could happen if we hit the back button during printing.
// We also keep the viewer from being cached in session history, since
// we require all documents there to be sanitized.
if (mDestroyBlockedCount != 0) {
return NS_OK;
}
#ifdef NS_PRINTING
// Here is where we check to see if the document was still being prepared
// for printing when it was asked to be destroy from someone externally
@ -1670,14 +1680,6 @@ nsDocumentViewer::Destroy()
mAutoBeforeAndAfterPrint = nullptr;
#endif
// Don't let the document get unloaded while we are printing.
// this could happen if we hit the back button during printing.
// We also keep the viewer from being cached in session history, since
// we require all documents there to be sanitized.
if (mDestroyRefCount != 0) {
return NS_OK;
}
// If we were told to put ourselves into session history instead of destroy
// the presentation, do that now.
if (mSHEntry) {
@ -4466,15 +4468,15 @@ nsDocumentViewer::SetIsPrintPreview(bool aIsPrintPreview)
//------------------------------------------------------------
void
nsDocumentViewer::IncrementDestroyRefCount()
nsDocumentViewer::IncrementDestroyBlockedCount()
{
++mDestroyRefCount;
++mDestroyBlockedCount;
}
void
nsDocumentViewer::DecrementDestroyRefCount()
nsDocumentViewer::DecrementDestroyBlockedCount()
{
--mDestroyRefCount;
--mDestroyBlockedCount;
}
//------------------------------------------------------------

View File

@ -41,10 +41,13 @@ public:
// Callers should call EndUpdate() on it when ready to use.
virtual mozilla::StyleSetHandle CreateStyleSet(nsIDocument* aDocument) = 0;
virtual void IncrementDestroyRefCount() = 0;
virtual void DecrementDestroyRefCount() = 0;
virtual void ReturnToGalleyPresentation() = 0;
/**
* This is used by nsPagePrintTimer to make nsDocumentViewer::Destroy()
* a no-op until printing is finished. That prevents the nsDocumentViewer
* and its document, presshell and prescontext from going away.
*/
virtual void IncrementDestroyBlockedCount() = 0;
virtual void DecrementDestroyBlockedCount() = 0;
virtual void OnDonePrinting() = 0;
@ -76,9 +79,8 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocumentViewerPrint,
void SetIsPrintPreview(bool aIsPrintPreview) override; \
bool GetIsPrintPreview() override; \
mozilla::StyleSetHandle CreateStyleSet(nsIDocument* aDocument) override; \
void IncrementDestroyRefCount() override; \
void DecrementDestroyRefCount() override; \
void ReturnToGalleyPresentation() override; \
void IncrementDestroyBlockedCount() override; \
void DecrementDestroyBlockedCount() override; \
void OnDonePrinting() override; \
bool IsInitializedForPrintPreview() override; \
void InitializeForPrintPreview() override; \

View File

@ -306,13 +306,13 @@ void UpdateASR(nsDisplayItem* aItem,
*
* The basic algorithm is:
*
* For-each item in the new list:
* For-each item i in the new list:
* If the item has a matching item in the old list:
* Remove items from the bottom of the old list until we reach the matching item:
* Remove items from the start of the old list up until we reach an item that also exists in the new list (leaving the matched item in place):
* Add valid items to the merged list, destroy invalid items.
* Destroy the matching item from the old list.
* Add the item from the new list into the merged list.
* Add all remaining valid items from the old list into the merged list.
* Add i into the merged list.
* If the start of the old list matches i, remove and destroy it, otherwise mark the old version of i as used.
* Add all remaining valid items from the old list into the merged list, skipping over (and destroying) any that are marked as used.
*
* If any item has a child display list, then we recurse into the merge
* algorithm once we match up the new/old versions (if present).
@ -320,7 +320,7 @@ void UpdateASR(nsDisplayItem* aItem,
* Example 1:
*
* Old List: A,B,C,D
* New List: A,D
* Modified List: A,D
* Invalidations: C,D
*
* We first match the A items, and add the new one to the merged list.
@ -330,10 +330,23 @@ void UpdateASR(nsDisplayItem* aItem,
*
* Merged List: A,B,D
*
* Example 2:
* Example 2 (layout/reftests/retained-dl-zindex-1.html):
*
* Old List: A, B
* New List, B, A
* Modified List: B, A
* Invalidations: A
*
* In this example A has been explicitly moved to the back.
*
* We match the B items, but don't copy A since it's invalid, and then add the
* new B into the merged list. We then add A, and we're done.
*
* Merged List: B, A
*
* Example 3:
*
* Old List: A, B
* Modified List: B, A
* Invalidations: -
*
* This can happen because a prior merge might have changed the ordering
@ -343,6 +356,28 @@ void UpdateASR(nsDisplayItem* aItem,
* and then add the new B into the merged list. We then add A, and we're done.
*
* Merged List: B, A
*
* Example 4 (layout/reftests/retained-dl-zindex-2.html):
*
* Element A has two elements covering it (B and C), that don't intersect each
* other. We then move C to the back.
*
* The correct initial ordering has B and C after A, in any order.
*
* Old List: A, B, C
* Modified List: C, A
* Invalidations: C
*
* We match the C items, but don't add anything from the old list because A is present
* in both lists. We add C to the merged list, and mark the old version of C as reused.
*
* We then match A, add the new version the merged list and delete the old version.
*
* We then process the remainder of the old list, B is added (since it is valid,
* and hasn't been mark as reused), C is destroyed since it's marked as reused and
* is already present in the merged list.
*
* Merged List: C, A, B
*/
void
RetainedDisplayListBuilder::MergeDisplayLists(nsDisplayList* aNewList,
@ -407,18 +442,17 @@ RetainedDisplayListBuilder::MergeDisplayLists(nsDisplayList* aNewList,
// The new item has a matching counterpart in the old list that we haven't yet reached,
// so copy all valid items from the old list into the merged list until we get to the
// matched item.
if (!oldItem->IsReused()) {
nsDisplayItem* old = nullptr;
while ((old = aOldList->RemoveBottom()) && !IsSameItem(newItem, old)) {
while ((old = aOldList->GetBottom()) && old != oldItem) {
if (IsAnyAncestorModified(old->FrameForInvalidation())) {
// The old item is invalid, discard it.
oldListLookup.Remove({ old->Frame(), old->GetPerFrameKey() });
aOldList->RemoveBottom();
old->Destroy(&mBuilder);
} else if (newListLookup.Get({ old->Frame(), old->GetPerFrameKey() })) {
// The old item is also in the new list, but we haven't got to it yet.
// Mark that we've found it, and we'll deal with it when we get to the new
// entry.
old->SetReused(true);
// This old item is also in the new list, but we haven't got to it yet.
// Stop now, and we'll deal with it when we get to the new entry.
break;
} else {
// Recurse into the child list (without a matching new list) to
// ensure that we find and remove any invalidated items.
@ -430,16 +464,27 @@ RetainedDisplayListBuilder::MergeDisplayLists(nsDisplayList* aNewList,
UpdateASR(old, containerASRForChildren);
old->UpdateBounds(&mBuilder);
}
aOldList->RemoveBottom();
ReuseItem(old);
}
}
MOZ_ASSERT(old && IsSameItem(newItem, old));
MOZ_ASSERT(old == oldItem);
bool destroy = false;
if (old == oldItem) {
// If we advanced the old list until the matching item then we can pop
// the matching item off the old list and make sure we clean it up.
aOldList->RemoveBottom();
destroy = true;
} else {
// If we didn't get to the matching item, then mark the old item
// as being reused (since we're adding the new version to the new
// list now) so that we don't add it twice at the end.
oldItem->SetReused(true);
}
// Recursively merge any child lists, destroy the old item and add
// the new one to the list.
if (oldItem->GetType() == DisplayItemType::TYPE_LAYER_EVENT_REGIONS &&
if (!destroy &&
oldItem->GetType() == DisplayItemType::TYPE_LAYER_EVENT_REGIONS &&
!IsAnyAncestorModified(oldItem->FrameForInvalidation())) {
// Event regions items don't have anything interesting other than
// the lists of regions and frames, so we have no need to use the
@ -459,7 +504,9 @@ RetainedDisplayListBuilder::MergeDisplayLists(nsDisplayList* aNewList,
newItem->UpdateBounds(&mBuilder);
}
if (destroy) {
oldItem->Destroy(&mBuilder);
}
UseItem(newItem);
}
} else {
@ -471,7 +518,8 @@ RetainedDisplayListBuilder::MergeDisplayLists(nsDisplayList* aNewList,
// Reuse the remaining valid items from the old display list.
while (nsDisplayItem* old = aOldList->RemoveBottom()) {
if (!IsAnyAncestorModified(old->FrameForInvalidation())) {
if (!IsAnyAncestorModified(old->FrameForInvalidation()) &&
!old->IsReused()) {
if (old->GetChildren()) {
// We are calling MergeDisplayLists() to ensure that the display items
// with modified or deleted children will be correctly handled.
@ -550,7 +598,7 @@ SubDocEnumCb(nsIDocument* aDocument, void* aData)
MOZ_ASSERT(subDocView);
nsIFrame* subDocFrame = subDocView->GetFrame();
MOZ_ASSERT(subDocFrame);
if (subDocFrame) {
nsSubDocumentFrame* subdocumentFrame = do_QueryFrame(subDocFrame);
MOZ_ASSERT(subdocumentFrame);
@ -562,6 +610,7 @@ SubDocEnumCb(nsIDocument* aDocument, void* aData)
TakeAndAddModifiedFramesFromRootFrame(data->modifiedFrames, rootFrame);
}
}
}
aDocument->EnumerateSubDocuments(SubDocEnumCb, aData);
return true;

View File

@ -17,8 +17,8 @@ NS_IMPL_ISUPPORTS_INHERITED(nsPagePrintTimer, mozilla::Runnable, nsITimerCallbac
nsPagePrintTimer::~nsPagePrintTimer()
{
// This matches the IncrementDestroyRefCount call in the constructor.
mDocViewerPrint->DecrementDestroyRefCount();
// This matches the IncrementDestroyBlockedCount call in the constructor.
mDocViewerPrint->DecrementDestroyBlockedCount();
}
nsresult
@ -140,7 +140,6 @@ nsPagePrintTimer::Notify(nsITimer *timer)
}
}
if (mDocViewerPrint) {
bool donePrePrint = true;
// Don't start to pre-print if we're waiting on the parent still.
if (mPrintJob && !mWaitingForRemotePrint) {
@ -157,7 +156,6 @@ nsPagePrintTimer::Notify(nsITimer *timer)
StartWatchDogTimer();
}
}
return NS_OK;
}

View File

@ -12,6 +12,7 @@
#include "nsIDocumentViewerPrint.h"
#include "nsPrintObject.h"
#include "mozilla/Attributes.h"
#include "mozilla/OwningNonNull.h"
#include "nsThreadUtils.h"
class nsPrintJob;
@ -33,7 +34,7 @@ public:
uint32_t aDelay)
: Runnable("nsPagePrintTimer")
, mPrintJob(aPrintJob)
, mDocViewerPrint(aDocViewerPrint)
, mDocViewerPrint(*aDocViewerPrint)
, mDocument(aDocument)
, mDelay(aDelay)
, mFiringCount(0)
@ -41,8 +42,8 @@ public:
, mWatchDogCount(0)
, mDone(false)
{
MOZ_ASSERT(aDocument);
mDocViewerPrint->IncrementDestroyRefCount();
MOZ_ASSERT(aDocViewerPrint && aDocument);
mDocViewerPrint->IncrementDestroyBlockedCount();
}
NS_DECL_NSITIMERCALLBACK
@ -71,7 +72,7 @@ private:
void Fail();
nsPrintJob* mPrintJob;
nsCOMPtr<nsIDocumentViewerPrint> mDocViewerPrint;
const mozilla::OwningNonNull<nsIDocumentViewerPrint> mDocViewerPrint;
nsCOMPtr<nsIDocument> mDocument;
nsCOMPtr<nsITimer> mTimer;
nsCOMPtr<nsITimer> mWatchDogTimer;

View File

@ -38,7 +38,6 @@ nsPrintData::nsPrintData(ePrintDataType aType)
, mNumPrintablePages(0)
, mNumPagesPrinted(0)
, mShrinkRatio(1.0)
, mOrigDCScale(1.0)
, mPPEventListeners(nullptr)
{
nsCOMPtr<nsIStringBundle> brandBundle;

View File

@ -82,7 +82,6 @@ public:
int32_t mNumPrintablePages;
int32_t mNumPagesPrinted;
float mShrinkRatio;
float mOrigDCScale;
nsCOMPtr<nsIPrintSettings> mPrintSettings;
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-prerender-transform-1.html retained-dl-prerender-transform-1-ref.html
== retained-dl-wrap-list.html retained-dl-wrap-list-ref.html
== retained-dl-zindex-1.html retained-dl-zindex-1-ref.html
== retained-dl-zindex-2.html retained-dl-zindex-2-ref.html
fuzzy(1,235200) == 1413073.html 1413073-ref.html
== 1416291.html 1416291-ref.html
== 1417601-1.html 1417601-1-ref.html

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
// Cubeb sandbox (remoting) control
#ifdef XP_MACOSX
pref("media.cubeb.sandbox", false);
#else
pref("media.cubeb.sandbox", true);
#endif
#endif
// Set to true to force demux/decode warnings to be treated as errors.
pref("media.playback.warnings-as-errors", false);

View File

@ -79,24 +79,27 @@ jobs:
# NOTE: Windows Searchfox jobs aren't working quite yet (bug 1418415).
win32-searchfox/debug:
description: "Win32 Searchfox Debug (clang-cl)"
win64-searchfox/debug:
description: "Win64 Searchfox Debug (clang-cl)"
index:
product: firefox
job-name: win32-searchfox-debug
job-name: win64-searchfox-debug
treeherder:
platform: windows2012-32/debug
platform: windows2012-64/debug
worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
worker:
max-run-time: 7200
env:
TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/win32/releng.manifest"
TOOLTOOL_MANIFEST: "browser/config/tooltool-manifests/win64/releng.manifest"
run:
using: mozharness
options: [append-env-variables-from-configs]
script: mozharness/scripts/fx_desktop_build.py
config:
- builds/releng_base_firefox.py
- builds/taskcluster_firefox_win32_clang_searchfox_debug.py
- builds/taskcluster_base_windows.py
- builds/taskcluster_base_win64.py
- builds/taskcluster_sub_win64/searchfox_debug.py
toolchains:
- win32-clang-cl
- win32-rust
- win64-clang-cl
- win64-rust

View File

@ -166,6 +166,8 @@ def register_callback_action(name, title, symbol, description, order=10000,
def register_callback(cb):
assert isinstance(cb, FunctionType), 'callback must be a function'
assert isinstance(symbol, basestring), 'symbol must be a string'
# Allow for json-e > 25 chars in the symbol.
if '$' not in symbol:
assert 1 <= len(symbol) <= 25, 'symbol must be between 1 and 25 characters'
assert not mem['registered'], 'register_callback_action must be used as decorator'
assert cb.__name__ not in callbacks, 'callback name {} is not unique'.format(cb.__name__)

View File

@ -89,7 +89,7 @@ def is_release_promotion_available(parameters):
@register_callback_action(
name='release-promotion',
title='Release Promotion',
symbol='Relpro',
symbol='${input.release_promotion_flavor}',
description="Promote a release.",
order=10000,
context=[],

View File

@ -120,6 +120,31 @@ class Parameters(ReadOnlyDict):
except KeyError:
raise KeyError("taskgraph parameter {!r} not found".format(k))
def is_try(self):
"""
Determine whether this graph is being built on a try project.
"""
return 'try' in self['project']
def file_url(self, path):
"""
Determine the VCS URL for viewing a file in the tree, suitable for
viewing by a human.
:param basestring path: The path, relative to the root of the repository.
:return basestring: The URL displaying the given path.
"""
if path.startswith('comm/'):
path = path[len('comm/'):]
repo = self['comm_head_repository']
rev = self['comm_head_rev']
else:
repo = self['head_repository']
rev = self['head_rev']
return '{}/file/{}/{}'.format(repo, rev, path)
def load_parameters_file(filename, strict=True):
"""

View File

@ -113,7 +113,7 @@ def fill_template(config, tasks):
'label': 'build-docker-image-' + image_name,
'description': description,
'attributes': {'image_name': image_name},
'expires-after': '28 days' if config.params['project'] == 'try' else '1 year',
'expires-after': '28 days' if config.params.is_try() else '1 year',
'scopes': ['secrets:get:project/taskcluster/gecko/hgfingerprint'],
'treeherder': {
'symbol': job_symbol,

View File

@ -174,7 +174,7 @@ def mozharness_on_docker_worker_setup(config, job, taskdesc):
if 'job-script' in run:
env['JOB_SCRIPT'] = run['job-script']
if 'try' in config.params['project']:
if config.params.is_try():
env['TRY_COMMIT_MSG'] = config.params['message']
# if we're not keeping artifacts, set some env variables to empty values
@ -257,7 +257,7 @@ def mozharness_on_generic_worker(config, job, taskdesc):
# to commands. Setting a variable to empty in a batch file unsets, so if
# there is no `TRY_COMMIT_MESSAGE`, pass a space instead, so that
# mozharness doesn't try to find the commit message on its own.
if 'try' in config.params['project']:
if config.params.is_try():
env['TRY_COMMIT_MSG'] = config.params['message'] or 'no commit message'
if not job['attributes']['build_platform'].startswith('win'):

View File

@ -147,7 +147,7 @@ def mozharness_test_on_docker(config, job, taskdesc):
if 'actions' in mozharness:
env['MOZHARNESS_ACTIONS'] = ' '.join(mozharness['actions'])
if 'try' in config.params['project']:
if config.params.is_try():
env['TRY_COMMIT_MSG'] = config.params['message']
# handle some of the mozharness-specific options
@ -315,7 +315,7 @@ def mozharness_test_on_generic_worker(config, job, taskdesc):
if isinstance(c, basestring) and c.startswith('--test-suite'):
mh_command[i] += suffix
if 'try' in config.params['project']:
if config.params.is_try():
env['TRY_COMMIT_MSG'] = config.params['message']
worker['mounts'] = [{

View File

@ -875,7 +875,7 @@ def build_docker_worker_payload(config, task, task_def):
else:
suffix = ''
skip_untrusted = config.params['project'] == 'try' or level == 1
skip_untrusted = config.params.is_try() or level == 1
for cache in worker['caches']:
# Some caches aren't enabled in environments where we can't
@ -1454,10 +1454,7 @@ def build_task(config, tasks):
'description': task['description'],
'name': task['label'],
'owner': config.params['owner'],
'source': '{}/file/{}/{}'.format(
config.params['head_repository'],
config.params['head_rev'],
config.path),
'source': config.params.file_url(config.path),
},
'extra': extra,
'tags': tags,

View File

@ -28,7 +28,7 @@ class EasyServer(ThreadingMixIn, BaseHTTPServer.HTTPServer):
acceptable_errors = (errno.EPIPE, errno.ECONNABORTED)
def handle_error(self, request, client_address):
error = sys.exc_value
error = sys.exc_info()[1]
if ((isinstance(error, socket.error) and
isinstance(error.args, tuple) and

View File

@ -13,6 +13,7 @@ config = {
'--upload', 'mozilla/geckoview',
'--upload-branch', 'gh-pages/javadoc/{project}',
'--upload-message', 'Update {project} javadoc to rev {revision}',
'--variant', 'officialWithGeckoBinariesNoMinApiRelease',
],
],
'artifact_flag_build_variant_in_try': None, # There's no artifact equivalent.

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
# asynchronous tests like test_alerts_noobserve.html!
[test_alerts.html]
skip-if = toolkit == 'android'
skip-if = toolkit == 'android' || (os == "win" && debug) # Bug 1407296
[test_alerts_noobserve.html]
[test_alerts_requireinteraction.html]
[test_image.html]

View File

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

View File

@ -26,13 +26,13 @@
#include "nsThreadUtils.h"
#include "nsXULAppAPI.h"
#include "GeckoProfiler.h"
#ifdef MOZ_GECKO_PROFILER
#include "mozilla/TimeStamp.h"
#include "ProfilerMarkerPayload.h"
#endif
#include "nsNetCID.h"
#include "HangDetails.h"
#ifdef MOZ_GECKO_PROFILER
#include "ProfilerMarkerPayload.h"
#endif
#include <algorithm>
// Activate BHR only for one every BHR_BETA_MOD users.
@ -218,7 +218,7 @@ public:
// Report a hang; aManager->mLock IS locked. The hang will be processed
// off-main-thread, and will then be submitted back.
void ReportHang(PRIntervalTime aHangTime, TimeStamp aHangEndTime);
void ReportHang(PRIntervalTime aHangTime);
// Report a permanent hang; aManager->mLock IS locked
void ReportPermaHang();
// Called by BackgroundHangMonitor::NotifyActivity
@ -390,7 +390,7 @@ BackgroundHangManager::RunMonitorThread()
} else {
if (MOZ_LIKELY(interval != currentThread->mHangStart)) {
// A hang ended
currentThread->ReportHang(intervalNow - currentThread->mHangStart, TimeStamp::Now());
currentThread->ReportHang(intervalNow - currentThread->mHangStart);
currentThread->mHanging = false;
}
}
@ -468,18 +468,27 @@ BackgroundHangThread::~BackgroundHangThread()
}
void
BackgroundHangThread::ReportHang(PRIntervalTime aHangTime, TimeStamp aHangEndTime)
BackgroundHangThread::ReportHang(PRIntervalTime aHangTime)
{
// Recovered from a hang; called on the monitor thread
// mManager->mLock IS locked
HangDetails hangDetails(aHangTime,
aHangEndTime,
XRE_GetProcessType(),
nsTArray<HangAnnotation> annotations;
for (auto& annotation : mAnnotations) {
HangAnnotation annot(annotation.mName, annotation.mValue);
annotations.AppendElement(mozilla::Move(annot));
}
HangDetails hangDetails(
aHangTime,
nsDependentCString(XRE_ChildProcessTypeToString(XRE_GetProcessType())),
VoidString(),
mThreadName,
mRunnableName,
Move(mHangStack),
Move(mAnnotations));
Move(annotations)
);
// If we have the stream transport service avaliable, we can process the
// native stack on it. Otherwise, we are unable to report a native stack, so
// we just report without one.
@ -492,6 +501,18 @@ BackgroundHangThread::ReportHang(PRIntervalTime aHangTime, TimeStamp aHangEndTim
RefPtr<nsHangDetails> hd = new nsHangDetails(Move(hangDetails));
hd->Submit();
}
// If the profiler is enabled, add a marker.
#ifdef MOZ_GECKO_PROFILER
if (profiler_is_active()) {
TimeStamp endTime = TimeStamp::Now();
TimeStamp startTime = endTime - TimeDuration::FromMilliseconds(aHangTime);
profiler_add_marker_for_thread(
mStackHelper.GetThreadId(),
"BHR-detected hang",
MakeUnique<HangMarkerPayload>(startTime, endTime));
}
#endif
}
void
@ -506,7 +527,7 @@ BackgroundHangThread::ReportPermaHang()
//
// We currently don't look at hang reports outside of nightly, and already
// collect native stacks eagerly on nightly, so this should be OK.
ReportHang(mMaxTimeout, TimeStamp::Now());
ReportHang(mMaxTimeout);
}
MOZ_ALWAYS_INLINE void

View File

@ -5,8 +5,9 @@
#include "mozilla/dom/ContentChild.h"
#include "mozilla/Unused.h"
#include "mozilla/GfxMessageUtils.h" // For ParamTraits<GeckoProcessType>
#ifdef MOZ_GECKO_PROFILER
#include "ProfilerMarkerPayload.h"
#include "shared-libraries.h"
#endif
namespace mozilla {
@ -14,35 +15,35 @@ namespace mozilla {
NS_IMETHODIMP
nsHangDetails::GetDuration(uint32_t* aDuration)
{
*aDuration = mDetails.mDuration;
*aDuration = mDetails.duration();
return NS_OK;
}
NS_IMETHODIMP
nsHangDetails::GetThread(nsACString& aName)
{
aName.Assign(mDetails.mThreadName);
aName.Assign(mDetails.threadName());
return NS_OK;
}
NS_IMETHODIMP
nsHangDetails::GetRunnableName(nsACString& aRunnableName)
{
aRunnableName.Assign(mDetails.mRunnableName);
aRunnableName.Assign(mDetails.runnableName());
return NS_OK;
}
NS_IMETHODIMP
nsHangDetails::GetProcess(nsACString& aName)
{
aName.AssignASCII(XRE_ChildProcessTypeToString(mDetails.mProcess));
aName.Assign(mDetails.process());
return NS_OK;
}
NS_IMETHODIMP
nsHangDetails::GetRemoteType(nsAString& aName)
{
aName.Assign(mDetails.mRemoteType);
aName.Assign(mDetails.remoteType());
return NS_OK;
}
@ -56,14 +57,14 @@ nsHangDetails::GetAnnotations(JSContext* aCx, JS::MutableHandleValue aVal)
return NS_ERROR_OUT_OF_MEMORY;
}
for (auto& annot : mDetails.mAnnotations) {
JSString* jsString = JS_NewUCStringCopyN(aCx, annot.mValue.get(), annot.mValue.Length());
for (auto& annot : mDetails.annotations()) {
JSString* jsString = JS_NewUCStringCopyN(aCx, annot.value().get(), annot.value().Length());
if (!jsString) {
return NS_ERROR_OUT_OF_MEMORY;
}
JS::RootedValue jsValue(aCx);
jsValue.setString(jsString);
if (!JS_DefineUCProperty(aCx, jsAnnotation, annot.mName.get(), annot.mName.Length(),
if (!JS_DefineUCProperty(aCx, jsAnnotation, annot.name().get(), annot.name().Length(),
jsValue, JSPROP_ENUMERATE)) {
return NS_ERROR_OUT_OF_MEMORY;
}
@ -98,32 +99,63 @@ StringFrame(JSContext* aCx,
} // anonymous namespace
NS_IMETHODIMP
nsHangDetails::GetStack(JSContext* aCx, JS::MutableHandle<JS::Value> aVal)
nsHangDetails::GetStack(JSContext* aCx, JS::MutableHandleValue aStack)
{
JS::RootedObject ret(aCx, JS_NewArrayObject(aCx, mDetails.mStack.length()));
auto& stack = mDetails.stack();
uint32_t length = stack.stack().Length();
JS::RootedObject ret(aCx, JS_NewArrayObject(aCx, length));
if (!ret) {
return NS_ERROR_OUT_OF_MEMORY;
}
for (size_t i = 0; i < mDetails.mStack.length(); ++i) {
const HangStack::Frame& frame = mDetails.mStack[i];
switch (frame.GetKind()) {
case HangStack::Frame::Kind::STRING: {
nsresult rv = StringFrame(aCx, ret, i, frame.AsString());
for (uint32_t i = 0; i < length; ++i) {
auto& entry = stack.stack()[i];
switch (entry.type()) {
case HangEntry::TnsCString: {
nsresult rv = StringFrame(aCx, ret, i, entry.get_nsCString().get());
NS_ENSURE_SUCCESS(rv, rv);
break;
}
case HangEntry::THangEntryBufOffset: {
uint32_t offset = entry.get_HangEntryBufOffset().index();
// NOTE: We can't trust the offset we got, as we might have gotten it
// from a compromised content process. Validate that it is in bounds.
if (NS_WARN_IF(stack.strbuffer().IsEmpty() ||
offset >= stack.strbuffer().Length())) {
MOZ_ASSERT_UNREACHABLE("Corrupted offset data");
return NS_ERROR_FAILURE;
}
// NOTE: If our content process is compromised, it could send us back a
// strbuffer() which didn't have a null terminator. If the last byte in
// the buffer is not '\0', we abort, to make sure we don't read out of
// bounds.
if (stack.strbuffer().LastElement() != '\0') {
MOZ_ASSERT_UNREACHABLE("Corrupted strbuffer data");
return NS_ERROR_FAILURE;
}
// We know this offset is safe because of the previous checks.
const int8_t* start = stack.strbuffer().Elements() + offset;
nsresult rv = StringFrame(aCx, ret, i,
reinterpret_cast<const char*>(start));
NS_ENSURE_SUCCESS(rv, rv);
break;
}
case HangEntry::THangEntryModOffset: {
const HangEntryModOffset& mo = entry.get_HangEntryModOffset();
case HangStack::Frame::Kind::MODOFFSET: {
JS::RootedObject jsFrame(aCx, JS_NewArrayObject(aCx, 2));
if (!jsFrame) {
return NS_ERROR_OUT_OF_MEMORY;
}
if (!JS_DefineElement(aCx, jsFrame, 0, frame.AsModOffset().mModule, JSPROP_ENUMERATE)) {
if (!JS_DefineElement(aCx, jsFrame, 0, mo.module(), JSPROP_ENUMERATE)) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsPrintfCString hexString("%" PRIxPTR, (uintptr_t)frame.AsModOffset().mOffset);
nsPrintfCString hexString("%" PRIxPTR, (uintptr_t)mo.offset());
JS::RootedString hex(aCx, JS_NewStringCopyZ(aCx, hexString.get()));
if (!hex || !JS_DefineElement(aCx, jsFrame, 1, hex, JSPROP_ENUMERATE)) {
return NS_ERROR_OUT_OF_MEMORY;
@ -134,67 +166,49 @@ nsHangDetails::GetStack(JSContext* aCx, JS::MutableHandle<JS::Value> aVal)
}
break;
}
case HangStack::Frame::Kind::PC: {
JS::RootedObject jsFrame(aCx, JS_NewArrayObject(aCx, 2));
if (!jsFrame) {
return NS_ERROR_OUT_OF_MEMORY;
}
if (!JS_DefineElement(aCx, jsFrame, 0, -1, JSPROP_ENUMERATE)) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsPrintfCString hexString("%" PRIxPTR, frame.AsPC());
JS::RootedString hex(aCx, JS_NewStringCopyZ(aCx, hexString.get()));
if (!hex || !JS_DefineElement(aCx, jsFrame, 1, hex, JSPROP_ENUMERATE)) {
return NS_ERROR_OUT_OF_MEMORY;
}
if (!JS_DefineElement(aCx, ret, i, jsFrame, JSPROP_ENUMERATE)) {
return NS_ERROR_OUT_OF_MEMORY;
}
case HangEntry::THangEntryProgCounter: {
// Don't bother recording fixed program counters to JS
nsresult rv = StringFrame(aCx, ret, i, "(unresolved)");
NS_ENSURE_SUCCESS(rv, rv);
break;
}
case HangStack::Frame::Kind::CONTENT: {
case HangEntry::THangEntryContent: {
nsresult rv = StringFrame(aCx, ret, i, "(content script)");
NS_ENSURE_SUCCESS(rv, rv);
break;
}
case HangStack::Frame::Kind::JIT: {
case HangEntry::THangEntryJit: {
nsresult rv = StringFrame(aCx, ret, i, "(jit frame)");
NS_ENSURE_SUCCESS(rv, rv);
break;
}
case HangStack::Frame::Kind::WASM: {
case HangEntry::THangEntryWasm: {
nsresult rv = StringFrame(aCx, ret, i, "(wasm)");
NS_ENSURE_SUCCESS(rv, rv);
break;
}
case HangStack::Frame::Kind::SUPPRESSED: {
case HangEntry::THangEntryChromeScript: {
nsresult rv = StringFrame(aCx, ret, i, "(chrome script)");
NS_ENSURE_SUCCESS(rv, rv);
break;
}
case HangEntry::THangEntrySuppressed: {
nsresult rv = StringFrame(aCx, ret, i, "(profiling suppressed)");
NS_ENSURE_SUCCESS(rv, rv);
break;
}
default:
MOZ_ASSERT_UNREACHABLE("Invalid variant");
break;
default: MOZ_CRASH("Unsupported HangEntry type?");
}
}
aVal.setObject(*ret);
aStack.setObject(*ret);
return NS_OK;
}
NS_IMETHODIMP
nsHangDetails::GetModules(JSContext* aCx, JS::MutableHandleValue aVal)
{
auto& modules = mDetails.mStack.GetModules();
auto& modules = mDetails.stack().modules();
size_t length = modules.Length();
JS::RootedObject retObj(aCx, JS_NewArrayObject(aCx, length));
if (!retObj) {
@ -202,18 +216,22 @@ nsHangDetails::GetModules(JSContext* aCx, JS::MutableHandleValue aVal)
}
for (size_t i = 0; i < length; ++i) {
const HangStack::Module& module = modules[i];
const HangModule& module = modules[i];
JS::RootedObject jsModule(aCx, JS_NewArrayObject(aCx, 2));
if (!jsModule) {
return NS_ERROR_OUT_OF_MEMORY;
}
JS::RootedString name(aCx, JS_NewUCStringCopyN(aCx, module.mName.BeginReading(), module.mName.Length()));
JS::RootedString name(aCx, JS_NewUCStringCopyN(aCx,
module.name().BeginReading(),
module.name().Length()));
if (!JS_DefineElement(aCx, jsModule, 0, name, JSPROP_ENUMERATE)) {
return NS_ERROR_OUT_OF_MEMORY;
}
JS::RootedString breakpadId(aCx, JS_NewStringCopyN(aCx, module.mBreakpadId.BeginReading(), module.mBreakpadId.Length()));
JS::RootedString breakpadId(aCx, JS_NewStringCopyN(aCx,
module.breakpadId().BeginReading(),
module.breakpadId().Length()));
if (!JS_DefineElement(aCx, jsModule, 1, breakpadId, JSPROP_ENUMERATE)) {
return NS_ERROR_OUT_OF_MEMORY;
}
@ -247,7 +265,7 @@ nsHangDetails::Submit()
case GeckoProcessType_Content: {
auto cc = dom::ContentChild::GetSingleton();
if (cc) {
hangDetails->mDetails.mRemoteType.Assign(cc->GetRemoteType());
hangDetails->mDetails.remoteType().Assign(cc->GetRemoteType());
Unused << cc->SendBHRThreadHang(hangDetails->mDetails);
}
break;
@ -272,16 +290,6 @@ nsHangDetails::Submit()
NS_WARNING("Unsupported BHR process type - discarding hang.");
break;
}
#ifdef MOZ_GECKO_PROFILER
if (profiler_is_active()) {
TimeStamp endTime = hangDetails->mDetails.mEndTime;
TimeStamp startTime = endTime -
TimeDuration::FromMilliseconds(hangDetails->mDetails.mDuration);
profiler_add_marker(
"BHR-detected hang",
MakeUnique<HangMarkerPayload>(startTime, endTime));
}
#endif
});
nsresult rv = SystemGroup::Dispatch(TaskCategory::Other,
@ -291,12 +299,88 @@ nsHangDetails::Submit()
NS_IMPL_ISUPPORTS(nsHangDetails, nsIHangDetails)
namespace {
// Sorting comparator used by ReadModuleInformation. Sorts PC Frames by their
// PC.
struct PCFrameComparator {
bool LessThan(HangEntry* const& a, HangEntry* const& b) const {
return a->get_HangEntryProgCounter().pc() < b->get_HangEntryProgCounter().pc();
}
bool Equals(HangEntry* const& a, HangEntry* const& b) const {
return a->get_HangEntryProgCounter().pc() == b->get_HangEntryProgCounter().pc();
}
};
} // anonymous namespace
void
ReadModuleInformation(HangStack& stack)
{
// modules() should be empty when we start filling it.
stack.modules().Clear();
#ifdef MOZ_GECKO_PROFILER
// Create a sorted list of the PCs in the current stack.
AutoTArray<HangEntry*, 100> frames;
for (auto& frame : stack.stack()) {
if (frame.type() == HangEntry::THangEntryProgCounter) {
frames.AppendElement(&frame);
}
}
PCFrameComparator comparator;
frames.Sort(comparator);
SharedLibraryInfo rawModules = SharedLibraryInfo::GetInfoForSelf();
rawModules.SortByAddress();
size_t frameIdx = 0;
for (size_t i = 0; i < rawModules.GetSize(); ++i) {
const SharedLibrary& info = rawModules.GetEntry(i);
uintptr_t moduleStart = info.GetStart();
uintptr_t moduleEnd = info.GetEnd() - 1;
// the interval is [moduleStart, moduleEnd)
bool moduleReferenced = false;
for (; frameIdx < frames.Length(); ++frameIdx) {
auto& frame = frames[frameIdx];
uint64_t pc = frame->get_HangEntryProgCounter().pc();
// We've moved past this frame, let's go to the next one.
if (pc >= moduleEnd) {
break;
}
if (pc >= moduleStart) {
uint64_t offset = pc - moduleStart;
if (NS_WARN_IF(offset > UINT32_MAX)) {
continue; // module/offset can only hold 32-bit offsets into shared libraries.
}
// If we found the module, rewrite the Frame entry to instead be a
// ModOffset one. mModules.Length() will be the index of the module when
// we append it below, and we set moduleReferenced to true to ensure
// that we do.
moduleReferenced = true;
uint32_t module = stack.modules().Length();
HangEntryModOffset modOffset(module, static_cast<uint32_t>(offset));
*frame = modOffset;
}
}
if (moduleReferenced) {
nsDependentCString cstr(info.GetBreakpadId().c_str());
HangModule module(info.GetDebugName(), cstr);
stack.modules().AppendElement(module);
}
}
#endif
}
NS_IMETHODIMP
ProcessHangStackRunnable::Run()
{
// NOTE: Reading module information can take a long time, which is why we do
// it off-main-thread.
mHangDetails.mStack.ReadModuleInformation();
ReadModuleInformation(mHangDetails.stack());
RefPtr<nsHangDetails> hangDetails = new nsHangDetails(Move(mHangDetails));
hangDetails->Submit();
@ -305,53 +389,3 @@ ProcessHangStackRunnable::Run()
}
} // namespace mozilla
/**
* IPC Serialization / Deserialization logic
*/
namespace IPC {
void
ParamTraits<mozilla::HangDetails>::Write(Message* aMsg, const mozilla::HangDetails& aParam)
{
WriteParam(aMsg, aParam.mDuration);
WriteParam(aMsg, aParam.mProcess);
WriteParam(aMsg, aParam.mRemoteType);
WriteParam(aMsg, aParam.mThreadName);
WriteParam(aMsg, aParam.mRunnableName);
WriteParam(aMsg, aParam.mStack);
WriteParam(aMsg, aParam.mAnnotations);
}
bool
ParamTraits<mozilla::HangDetails>::Read(const Message* aMsg,
PickleIterator* aIter,
mozilla::HangDetails* aResult)
{
if (!ReadParam(aMsg, aIter, &aResult->mDuration)) {
return false;
}
if (!ReadParam(aMsg, aIter, &aResult->mProcess)) {
return false;
}
if (!ReadParam(aMsg, aIter, &aResult->mRemoteType)) {
return false;
}
if (!ReadParam(aMsg, aIter, &aResult->mThreadName)) {
return false;
}
if (!ReadParam(aMsg, aIter, &aResult->mRunnableName)) {
return false;
}
if (!ReadParam(aMsg, aIter, &aResult->mStack)) {
return false;
}
if (!ReadParam(aMsg, aIter, &aResult->mAnnotations)) {
return false;
}
return true;
}
} // namespace IPC

View File

@ -11,7 +11,7 @@
#include "mozilla/ProcessedStack.h"
#include "mozilla/RefPtr.h"
#include "mozilla/Move.h"
#include "mozilla/HangStack.h"
#include "mozilla/HangTypes.h"
#include "mozilla/HangAnnotations.h"
#include "nsTArray.h"
#include "nsIHangDetails.h"
@ -19,55 +19,6 @@
namespace mozilla {
/**
* HangDetails is a POD struct which contains the information collected from the
* hang. It can be wrapped in a nsHangDetails to provide an XPCOM interface for
* extracting information from it easily.
*
* This type is separate, as it can be sent over IPC while nsHangDetails is an
* XPCOM interface which is harder to serialize over IPC.
*/
class HangDetails
{
public:
HangDetails()
: mDuration(0)
, mEndTime(TimeStamp::Now())
, mProcess(GeckoProcessType_Invalid)
, mRemoteType(VoidString())
{}
HangDetails(const HangDetails& aOther) = default;
HangDetails(HangDetails&& aOther) = default;
HangDetails(uint32_t aDuration,
TimeStamp aEndTime,
GeckoProcessType aProcess,
const nsACString& aThreadName,
const nsACString& aRunnableName,
HangStack&& aStack,
HangMonitor::HangAnnotations&& aAnnotations)
: mDuration(aDuration)
, mEndTime(aEndTime)
, mProcess(aProcess)
, mRemoteType(VoidString())
, mThreadName(aThreadName)
, mRunnableName(aRunnableName)
, mStack(Move(aStack))
, mAnnotations(Move(aAnnotations))
{}
uint32_t mDuration;
TimeStamp mEndTime;
GeckoProcessType mProcess;
// NOTE: mRemoteType is set in nsHangDetails::Submit before the HangDetails
// object is sent to the parent process.
nsString mRemoteType;
nsCString mThreadName;
nsCString mRunnableName;
HangStack mStack;
HangMonitor::HangAnnotations mAnnotations;
};
/**
* HangDetails is the concrete implementaion of nsIHangDetails, and contains the
* infromation which we want to expose to observers of the bhr-thread-hang

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/Sprintf.h"
#include "nsThread.h"
#include "mozilla/HangStack.h"
#include "mozilla/HangTypes.h"
#ifdef __GNUC__
# pragma GCC diagnostic push
@ -68,7 +68,7 @@ namespace mozilla {
ThreadStackHelper::ThreadStackHelper()
: mStackToFill(nullptr)
, mMaxStackSize(HangStack::sMaxInlineStorage)
, mMaxStackSize(16)
, mMaxBufferSize(512)
, mDesiredStackSize(0)
, mDesiredBufferSize(0)
@ -90,12 +90,16 @@ ThreadStackHelper::PrepareStackBuffer(HangStack& aStack)
mDesiredBufferSize = 0;
mDesiredStackSize = 0;
// Return false to skip getting the stack and return an empty stack
aStack.clear();
// Clear all of the stack entries.
aStack.stack().ClearAndRetainStorage();
aStack.strbuffer().ClearAndRetainStorage();
aStack.modules().Clear();
#ifdef MOZ_THREADSTACKHELPER_PSEUDO
if (!aStack.reserve(mMaxStackSize) ||
!aStack.reserve(aStack.capacity()) || // reserve up to the capacity
!aStack.EnsureBufferCapacity(mMaxBufferSize)) {
// Ensure we have enough space in our stack and string buffers for the data we
// want to collect.
if (!aStack.stack().SetCapacity(mMaxStackSize, fallible) ||
!aStack.strbuffer().SetCapacity(mMaxBufferSize, fallible)) {
return false;
}
return true;
@ -161,17 +165,19 @@ ThreadStackHelper::SetIsMainThread()
}
void
ThreadStackHelper::TryAppendFrame(HangStack::Frame aFrame)
ThreadStackHelper::TryAppendFrame(HangEntry aFrame)
{
MOZ_RELEASE_ASSERT(mStackToFill);
// We deduplicate identical frames of kind CONTENT, JIT, WASM, and SUPPRESSED.
switch (aFrame.GetKind()) {
case HangStack::Frame::Kind::CONTENT:
case HangStack::Frame::Kind::JIT:
case HangStack::Frame::Kind::WASM:
case HangStack::Frame::Kind::SUPPRESSED:
if (!mStackToFill->empty() && mStackToFill->back().GetKind() == aFrame.GetKind()) {
// We deduplicate identical Content, Jit, Wasm, ChromeScript and Suppressed frames.
switch (aFrame.type()) {
case HangEntry::THangEntryContent:
case HangEntry::THangEntryJit:
case HangEntry::THangEntryWasm:
case HangEntry::THangEntryChromeScript:
case HangEntry::THangEntrySuppressed:
if (!mStackToFill->stack().IsEmpty() &&
mStackToFill->stack().LastElement().type() == aFrame.type()) {
return;
}
break;
@ -184,8 +190,8 @@ ThreadStackHelper::TryAppendFrame(HangStack::Frame aFrame)
mDesiredStackSize += 1;
// Perform the append if we have enough space to do so.
if (mStackToFill->canAppendWithoutRealloc(1)) {
mStackToFill->infallibleAppend(aFrame);
if (mStackToFill->stack().Capacity() > mStackToFill->stack().Length()) {
mStackToFill->stack().AppendElement(mozilla::Move(aFrame));
}
}
@ -193,14 +199,14 @@ void
ThreadStackHelper::CollectNativeLeafAddr(void* aAddr)
{
MOZ_RELEASE_ASSERT(mStackToFill);
TryAppendFrame(HangStack::Frame(reinterpret_cast<uintptr_t>(aAddr)));
TryAppendFrame(HangEntryProgCounter(reinterpret_cast<uintptr_t>(aAddr)));
}
void
ThreadStackHelper::CollectJitReturnAddr(void* aAddr)
{
MOZ_RELEASE_ASSERT(mStackToFill);
TryAppendFrame(HangStack::Frame::Jit());
TryAppendFrame(HangEntryJit());
}
void
@ -209,7 +215,7 @@ ThreadStackHelper::CollectWasmFrame(const char* aLabel)
MOZ_RELEASE_ASSERT(mStackToFill);
// We don't want to collect WASM frames, as they are probably for content, so
// we just add a "(content wasm)" frame.
TryAppendFrame(HangStack::Frame::Wasm());
TryAppendFrame(HangEntryWasm());
}
namespace {
@ -263,18 +269,39 @@ ThreadStackHelper::CollectPseudoEntry(const js::ProfileEntry& aEntry)
{
// For non-js frames we just include the raw label.
if (!aEntry.isJs()) {
const char* label = aEntry.label();
TryAppendFrame(HangStack::Frame(label));
const char* entryLabel = aEntry.label();
// entryLabel is a statically allocated string, so we want to store a
// reference to it without performing any allocations. This is important, as
// we aren't allowed to allocate within this function.
//
// The variant for this kind of label in our HangStack object is a
// `nsCString`, which normally contains heap allocated string data. However,
// `nsCString` has an optimization for literal strings which causes the
// backing data to not be copied when being copied between nsCString
// objects.
//
// We take advantage of that optimization by creating a nsCString object
// which has the LITERAL flag set. Without this optimization, this code
// would be incorrect.
nsCString label;
label.AssignLiteral(entryLabel, strlen(entryLabel));
// Let's make sure we don't deadlock here, by asserting that `label`'s
// backing data matches.
MOZ_RELEASE_ASSERT(label.BeginReading() == entryLabel,
"String copy performed during ThreadStackHelper::CollectPseudoEntry");
TryAppendFrame(label);
return;
}
if (!aEntry.script()) {
TryAppendFrame(HangStack::Frame::Suppressed());
TryAppendFrame(HangEntrySuppressed());
return;
}
if (!IsChromeJSScript(aEntry.script())) {
TryAppendFrame(HangStack::Frame::Content());
TryAppendFrame(HangEntryContent());
return;
}
@ -313,21 +340,25 @@ ThreadStackHelper::CollectPseudoEntry(const js::ProfileEntry& aEntry)
}
}
mDesiredStackSize += 1;
char buffer[128]; // Enough to fit longest js file name from the tree
size_t len = SprintfLiteral(buffer, "%s:%u", basename, lineno);
if (len < sizeof(buffer)) {
mDesiredBufferSize += len + 1;
if (mStackToFill->canAppendWithoutRealloc(1) &&
mStackToFill->AvailableBufferSize() >= len + 1) {
mStackToFill->InfallibleAppendViaBuffer(buffer, len);
if (mStackToFill->stack().Capacity() > mStackToFill->stack().Length() &&
(mStackToFill->strbuffer().Capacity() -
mStackToFill->strbuffer().Length()) > len + 1) {
// NOTE: We only increment this if we're going to successfully append.
mDesiredStackSize += 1;
uint32_t start = mStackToFill->strbuffer().Length();
mStackToFill->strbuffer().AppendElements(buffer, len);
mStackToFill->strbuffer().AppendElement('\0');
mStackToFill->stack().AppendElement(HangEntryBufOffset(start));
return;
}
}
if (mStackToFill->canAppendWithoutRealloc(1)) {
mStackToFill->infallibleAppend(HangStack::Frame("(chrome script)"));
}
TryAppendFrame(HangEntryChromeScript());
}
} // namespace mozilla

View File

@ -53,13 +53,6 @@ namespace mozilla {
*/
class ThreadStackHelper : public ProfilerStackCollector
{
public:
// When a native stack is gathered, this vector holds the raw program counter
// values that FramePointerStackWalk will return to us after it walks the
// stack. When gathering the Telemetry payload, Telemetry will take care of
// mapping these program counters to proper addresses within modules.
typedef NativeHangStack NativeStack;
private:
HangStack* mStackToFill;
Array<char, nsThread::kRunnableNameBufSize>* mRunnableNameBuffer;
@ -87,6 +80,11 @@ public:
*/
void GetStack(HangStack& aStack, nsACString& aRunnableName, bool aStackWalk);
/**
* Retrieve the thread's profiler thread ID.
*/
int GetThreadId() const { return mThreadId; }
protected:
/**
* ProfilerStackCollector
@ -98,7 +96,7 @@ protected:
virtual void CollectPseudoEntry(const js::ProfileEntry& aEntry) override;
private:
void TryAppendFrame(mozilla::HangStack::Frame aFrame);
void TryAppendFrame(mozilla::HangEntry aFrame);
// The profiler's unique thread identifier for the target thread.
int mThreadId;

View File

@ -32,13 +32,15 @@ XPIDL_MODULE = 'backgroundhangmonitor'
EXPORTS.mozilla += [
'BackgroundHangMonitor.h',
'HangDetails.h',
'HangStack.h',
]
UNIFIED_SOURCES += [
'BackgroundHangMonitor.cpp',
'HangDetails.cpp',
'HangStack.cpp',
]
IPDL_SOURCES += [
'HangTypes.ipdlh',
]
if CONFIG['MOZ_GECKO_PROFILER']:

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 range Pause, Resume, CollectionStart, and CollectionEnd
// - We skip range Pause, Resume, CollectionStart, Marker, and CollectionEnd
// entries between samples.
while (e.Has()) {
if (e.Get().IsThreadId()) {
@ -918,16 +918,17 @@ ProfileBuffer::StreamMarkersToJSON(SpliceableJSONWriter& aWriter,
{
EntryGetter e(*this);
int currentThreadID = -1;
// Stream all markers whose threadId matches aThreadId. All other entries are
// skipped, because we process them in StreamSamplesToJSON().
// Stream all markers whose threadId matches aThreadId. We skip other entries,
// because we process them in StreamSamplesToJSON().
//
// NOTE: The ThreadId of a marker is determined by its GetThreadId() method,
// rather than ThreadId buffer entries, as markers can be added outside of
// samples.
while (e.Has()) {
if (e.Get().IsThreadId()) {
currentThreadID = e.Get().u.mInt;
} else if (currentThreadID == aThreadId && e.Get().IsMarker()) {
if (e.Get().IsMarker()) {
const ProfilerMarker* marker = e.Get().u.mMarker;
if (marker->GetTime() >= aSinceTime) {
if (marker->GetTime() >= aSinceTime &&
marker->GetThreadId() == aThreadId) {
marker->StreamJSON(aWriter, aProcessStartTime, aUniqueStacks);
}
}

View File

@ -22,6 +22,7 @@ class ProfilerMarker
public:
explicit ProfilerMarker(const char* aMarkerName,
int aThreadId,
mozilla::UniquePtr<ProfilerMarkerPayload>
aPayload = nullptr,
double aTime = 0)
@ -30,6 +31,7 @@ public:
, mNext{nullptr}
, mTime(aTime)
, mGenID{0}
, mThreadId{aThreadId}
{}
void SetGeneration(uint32_t aGenID) { mGenID = aGenID; }
@ -38,6 +40,8 @@ public:
double GetTime() const { return mTime; }
int GetThreadId() const { return mThreadId; }
void StreamJSON(SpliceableJSONWriter& aWriter,
const mozilla::TimeStamp& aProcessStartTime,
UniqueStacks& aUniqueStacks) const
@ -69,6 +73,7 @@ private:
ProfilerMarker* mNext;
double mTime;
uint32_t mGenID;
int mThreadId;
};
template<typename T>

View File

@ -25,9 +25,8 @@ ThreadInfo::ThreadInfo(const char* aName,
void* aStackTop)
: mName(strdup(aName))
, mRegisterTime(TimeStamp::Now())
, mThreadId(aThreadId)
, mIsMainThread(aIsMainThread)
, mRacyInfo(mozilla::MakeNotNull<RacyThreadInfo*>())
, mRacyInfo(mozilla::MakeNotNull<RacyThreadInfo*>(aThreadId))
, mPlatformData(AllocPlatformData(aThreadId))
, mStackTop(aStackTop)
, mIsBeingProfiled(false)
@ -260,7 +259,7 @@ ThreadInfo::FlushSamplesAndMarkers(const TimeStamp& aProcessStartTime,
SpliceableChunkedJSONWriter b;
b.StartBareList();
{
aBuffer.StreamSamplesToJSON(b, mThreadId, /* aSinceTime = */ 0,
aBuffer.StreamSamplesToJSON(b, ThreadId(), /* aSinceTime = */ 0,
&mFirstSavedStreamedSampleTime,
mContext, *mUniqueStacks);
}
@ -272,7 +271,7 @@ ThreadInfo::FlushSamplesAndMarkers(const TimeStamp& aProcessStartTime,
SpliceableChunkedJSONWriter b;
b.StartBareList();
{
aBuffer.StreamMarkersToJSON(b, mThreadId, aProcessStartTime,
aBuffer.StreamMarkersToJSON(b, ThreadId(), aProcessStartTime,
/* aSinceTime = */ 0, *mUniqueStacks);
}
b.EndBareList();

View File

@ -23,8 +23,9 @@
class RacyThreadInfo final : public PseudoStack
{
public:
RacyThreadInfo()
explicit RacyThreadInfo(int aThreadId)
: PseudoStack()
, mThreadId(aThreadId)
, mSleep(AWAKE)
{
MOZ_COUNT_CTOR(RacyThreadInfo);
@ -57,7 +58,7 @@ public:
double aTime)
{
ProfilerMarker* marker =
new ProfilerMarker(aMarkerName, Move(aPayload), aTime);
new ProfilerMarker(aMarkerName, mThreadId, Move(aPayload), aTime);
mPendingMarkers.insert(marker);
}
@ -113,10 +114,16 @@ public:
bool IsSleeping() { return mSleep != AWAKE; }
int ThreadId() const { return mThreadId; }
private:
// A list of pending markers that must be moved to the circular buffer.
ProfilerSignalSafeLinkedList<ProfilerMarker> mPendingMarkers;
// mThreadId contains the thread ID of the current thread. It is safe to read
// this from multiple threads concurrently, as it will never be mutated.
const int mThreadId;
// mSleep tracks whether the thread is sleeping, and if so, whether it has
// been previously observed. This is used for an optimization: in some cases,
// when a thread is asleep, we duplicate the previous sample, which is
@ -173,7 +180,10 @@ public:
~ThreadInfo();
const char* Name() const { return mName.get(); }
int ThreadId() const { return mThreadId; }
// This is a safe read even when the target thread is not blocked, as this
// thread id is never mutated.
int ThreadId() const { return RacyInfo()->ThreadId(); }
bool IsMainThread() const { return mIsMainThread; }
@ -196,7 +206,6 @@ private:
mozilla::UniqueFreePtr<char> mName;
mozilla::TimeStamp mRegisterTime;
mozilla::TimeStamp mUnregisterTime;
int mThreadId;
const bool mIsMainThread;
// The thread's RacyThreadInfo. This is an owning pointer. It could be an

View File

@ -3283,6 +3283,46 @@ profiler_add_marker(const char* aMarkerName)
profiler_add_marker(aMarkerName, nullptr);
}
// This logic needs to add a marker for a different thread, so we actually need
// to lock here.
void
profiler_add_marker_for_thread(int aThreadId,
const char* aMarkerName,
UniquePtr<ProfilerMarkerPayload> aPayload)
{
MOZ_RELEASE_ASSERT(CorePS::Exists());
// Create the ProfilerMarker which we're going to store.
TimeStamp origin = (aPayload && !aPayload->GetStartTime().IsNull())
? aPayload->GetStartTime()
: TimeStamp::Now();
TimeDuration delta = origin - CorePS::ProcessStartTime();
ProfilerMarker* marker =
new ProfilerMarker(aMarkerName, aThreadId, Move(aPayload),
delta.ToMilliseconds());
PSAutoLock lock(gPSMutex);
#ifdef DEBUG
// Assert that our thread ID makes sense
bool realThread = false;
const CorePS::ThreadVector& liveThreads = CorePS::LiveThreads(lock);
for (uint32_t i = 0; i < liveThreads.size(); i++) {
ThreadInfo* info = liveThreads.at(i);
if (info->ThreadId() == aThreadId) {
realThread = true;
break;
}
}
MOZ_ASSERT(realThread, "Invalid thread id");
#endif
// Insert the marker into the buffer
ProfileBuffer& buffer = ActivePS::Buffer(lock);
buffer.AddStoredMarker(marker);
buffer.AddEntry(ProfileBufferEntry::Marker(marker));
}
void
profiler_tracing(const char* aCategory, const char* aMarkerName,
TracingKind aKind)

View File

@ -462,6 +462,11 @@ void profiler_add_marker(const char* aMarkerName);
void profiler_add_marker(const char* aMarkerName,
mozilla::UniquePtr<ProfilerMarkerPayload> aPayload);
// Insert a marker in the profile timeline for a specified thread.
void profiler_add_marker_for_thread(int aThreadId,
const char* aMarkerName,
mozilla::UniquePtr<ProfilerMarkerPayload> aPayload);
enum TracingKind {
TRACING_EVENT,
TRACING_INTERVAL_START,

View File

@ -168,7 +168,9 @@ private:
{
MOZ_ASSERT(aSize <= Available());
char* p = reinterpret_cast<char*>(header.offset);
MOZ_RELEASE_ASSERT(p);
header.offset += aSize;
canary.Check();
MOZ_MAKE_MEM_UNDEFINED(p, aSize);
return p;
}