Merge mozilla-central to inbound. a=merge CLOSED TREE

This commit is contained in:
Gurzau Raul 2019-02-14 19:06:35 +02:00
commit 7ca067da6d
104 changed files with 1892 additions and 843 deletions

View File

@ -335,8 +335,15 @@ uint64_t Accessible::VisibilityState() const {
nsIFrame* parentFrame = curFrame->GetParent();
nsDeckFrame* deckFrame = do_QueryFrame(parentFrame);
if (deckFrame && deckFrame->GetSelectedBox() != curFrame) {
#if defined(ANDROID)
// In Fennec instead of a <tabpanels> container there is a <deck>
// with direct <browser> children.
if (curFrame->GetContent()->IsXULElement(nsGkAtoms::browser))
return states::OFFSCREEN;
#else
if (deckFrame->GetContent()->IsXULElement(nsGkAtoms::tabpanels))
return states::OFFSCREEN;
#endif
MOZ_ASSERT_UNREACHABLE(
"Children of not selected deck panel are not accessible.");

View File

@ -63,6 +63,7 @@ tags = blocklist
[browser_CTP_hide_overlay.js]
tags = blocklist
[browser_CTP_iframe.js]
skip-if = os == 'win' && !debug # Bug 1519868
tags = blocklist
[browser_CTP_nonplugins.js]
skip-if = verify

View File

@ -177,6 +177,7 @@ skip-if = !e10s || !crashreporter # the tab's process is killed during the test.
[browser_ext_sidebarAction_browser_style.js]
[browser_ext_sidebarAction_click.js]
[browser_ext_sidebarAction_context.js]
skip-if = true # 1349892
[browser_ext_sidebarAction_contextMenu.js]
[browser_ext_sidebarAction_incognito.js]
[browser_ext_sidebarAction_runtime.js]

View File

@ -36,21 +36,7 @@ function debugTargetListenerMiddleware(store) {
clientWrapper.addListener("addonListChanged", onExtensionsUpdated);
// Workers
clientWrapper.addListener("workerListChanged", onWorkersUpdated);
clientWrapper.onFront("contentProcessTarget", front => {
clientWrapper.contentProcessFronts.push(front);
front.on("workerListChanged", onWorkersUpdated);
});
clientWrapper.onFront("serviceWorkerRegistration", front => {
clientWrapper.serviceWorkerRegistrationFronts.push(front);
front.on("push-subscription-modified", onWorkersUpdated);
front.on("registration-changed", onWorkersUpdated);
});
clientWrapper.addListener("serviceWorkerRegistrationListChanged",
onWorkersUpdated);
clientWrapper.addListener("processListChanged", onWorkersUpdated);
clientWrapper.addListener("workersUpdated", onWorkersUpdated);
break;
}
@ -65,22 +51,7 @@ function debugTargetListenerMiddleware(store) {
clientWrapper.removeListener("addonListChanged", onExtensionsUpdated);
// Workers
clientWrapper.removeListener("workerListChanged", onWorkersUpdated);
clientWrapper.removeListener("serviceWorkerRegistrationListChanged",
onWorkersUpdated);
for (const front of clientWrapper.contentProcessFronts) {
front.off("workerListChanged", onWorkersUpdated);
}
clientWrapper.contentProcessFronts = [];
for (const front of clientWrapper.serviceWorkerRegistrationFronts) {
front.off("push-subscription-modified", onWorkersUpdated);
front.off("registration-changed", onWorkersUpdated);
}
clientWrapper.serviceWorkerRegistrationFronts = [];
clientWrapper.removeListener("processListChanged", onWorkersUpdated);
clientWrapper.removeListener("workersUpdated", onWorkersUpdated);
break;
}

View File

@ -5,6 +5,7 @@
"use strict";
const { RUNTIME_PREFERENCE } = require("../constants");
const { WorkersListener } = require("./workers-listener");
const PREF_TYPES = {
BOOL: "BOOL",
@ -20,10 +21,7 @@ const PREF_TO_TYPE = {
// Some events are fired by mainRoot rather than client.
const MAIN_ROOT_EVENTS = [
"addonListChanged",
"processListChanged",
"serviceWorkerRegistrationListChanged",
"tabListChanged",
"workerListChanged",
];
/**
@ -33,9 +31,7 @@ const MAIN_ROOT_EVENTS = [
class ClientWrapper {
constructor(client) {
this.client = client;
// Array of contentProcessTarget fronts on which we will listen for worker events.
this.contentProcessFronts = [];
this.serviceWorkerRegistrationFronts = [];
this.workersListener = new WorkersListener(client.mainRoot);
}
addOneTimeListener(evt, listener) {
@ -47,7 +43,9 @@ class ClientWrapper {
}
addListener(evt, listener) {
if (MAIN_ROOT_EVENTS.includes(evt)) {
if (evt === "workersUpdated") {
this.workersListener.addListener(listener);
} else if (MAIN_ROOT_EVENTS.includes(evt)) {
this.client.mainRoot.on(evt, listener);
} else {
this.client.addListener(evt, listener);
@ -55,7 +53,9 @@ class ClientWrapper {
}
removeListener(evt, listener) {
if (MAIN_ROOT_EVENTS.includes(evt)) {
if (evt === "workersUpdated") {
this.workersListener.removeListener(listener);
} else if (MAIN_ROOT_EVENTS.includes(evt)) {
this.client.mainRoot.off(evt, listener);
} else {
this.client.removeListener(evt, listener);

View File

@ -12,4 +12,5 @@ DevToolsModules(
'runtime-client-factory.js',
'runtimes-state-helper.js',
'usb-runtimes.js',
'workers-listener.js',
)

View File

@ -0,0 +1,70 @@
/* 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/. */
"use strict";
/**
* Listening to worker updates requires watching various sources. This class provides
* a single addListener/removeListener API that will aggregate all possible worker update
* events.
*
* Only supports one listener at a time.
*/
class WorkersListener {
constructor(rootFront) {
this.rootFront = rootFront;
// Array of contentProcessTarget fronts on which we will listen for worker events.
this._contentProcessFronts = [];
this._serviceWorkerRegistrationFronts = [];
this._listener = null;
}
addListener(listener) {
if (this._listener) {
throw new Error("WorkersListener addListener called twice.");
}
this._listener = listener;
this.rootFront.on("workerListChanged", this._listener);
this.rootFront.onFront("contentProcessTarget", front => {
this._contentProcessFronts.push(front);
front.on("workerListChanged", this._listener);
});
this.rootFront.onFront("serviceWorkerRegistration", front => {
this._serviceWorkerRegistrationFronts.push(front);
front.on("push-subscription-modified", this._listener);
front.on("registration-changed", this._listener);
});
this.rootFront.on("serviceWorkerRegistrationListChanged", this._listener);
this.rootFront.on("processListChanged", this._listener);
}
removeListener(listener) {
if (!this._listener) {
return;
}
this.rootFront.off("workerListChanged", this._listener);
for (const front of this._contentProcessFronts) {
front.off("workerListChanged", this._listener);
}
for (const front of this._serviceWorkerRegistrationFronts) {
front.off("push-subscription-modified", this._listener);
front.off("registration-changed", this._listener);
}
this.rootFront.off("serviceWorkerRegistrationListChanged", this._listener);
this.rootFront.off("processListChanged", this._listener);
this._contentProcessFronts = [];
this._serviceWorkerRegistrationFronts = [];
this._listener = null;
}
}
exports.WorkersListener = WorkersListener;

View File

@ -9,9 +9,6 @@ const USB_RUNTIME_ID = "test-runtime-id";
const USB_RUNTIME_DEVICE_NAME = "test device name";
const USB_RUNTIME_APP_NAME = "TestUsbApp";
/* import-globals-from helper-mocks.js */
Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "helper-mocks.js", this);
// Test that addons are displayed and updated for USB runtimes when expected.
add_task(async function() {
const mocks = new Mocks();

View File

@ -3,9 +3,6 @@
"use strict";
/* import-globals-from helper-mocks.js */
Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "helper-mocks.js", this);
/**
* Check whether can toggle enable/disable connection prompt setting.
*/

View File

@ -5,8 +5,6 @@
/* import-globals-from helper-collapsibilities.js */
Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "helper-collapsibilities.js", this);
/* import-globals-from helper-mocks.js */
Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "helper-mocks.js", this);
const RUNTIME_ID = "test-runtime-id";
const RUNTIME_DEVICE_NAME = "test device name";

View File

@ -26,7 +26,6 @@ add_task(async function() {
info("DevTools starts workers, wait for requests to settle");
const store = window.AboutDebugging.store;
await waitForDispatch(store, "REQUEST_WORKERS_SUCCESS");
await waitForRequestsToSettle(store);
info("Click on the Connect item in the sidebar");

View File

@ -9,9 +9,6 @@ const USB_RUNTIME_ID = "test-runtime-id";
const USB_DEVICE_NAME = "test device name";
const USB_APP_NAME = "TestApp";
/* import-globals-from helper-mocks.js */
Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "helper-mocks.js", this);
// Test that remote runtime connections are persisted across about:debugging reloads.
add_task(async function() {
const mocks = new Mocks();

View File

@ -3,9 +3,6 @@
"use strict";
/* import-globals-from helper-mocks.js */
Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "helper-mocks.js", this);
/**
* Test opening and closing the profiler dialog.
*/

View File

@ -3,9 +3,6 @@
"use strict";
/* import-globals-from helper-mocks.js */
Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "helper-mocks.js", this);
/**
* Test that the initial route is /runtime/this-firefox
*/

View File

@ -3,10 +3,6 @@
"use strict";
/* import-globals-from helper-mocks.js */
Services.scriptloader.loadSubScript(
CHROME_URL_ROOT + "helper-mocks.js", this);
/**
* Test that remote runtimes show action buttons that are hidden for 'This Firefox'.
*/

View File

@ -3,9 +3,6 @@
"use strict";
/* import-globals-from helper-mocks.js */
Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "helper-mocks.js", this);
const NETWORK_RUNTIME_HOST = "localhost:6080";
const NETWORK_RUNTIME_APP_NAME = "TestNetworkApp";
const USB_RUNTIME_ID = "test-runtime-id";

View File

@ -3,9 +3,6 @@
"use strict";
/* import-globals-from helper-mocks.js */
Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "helper-mocks.js", this);
const NETWORK_RUNTIME_HOST = "localhost:6080";
const NETWORK_RUNTIME_APP_NAME = "TestNetworkApp";
const NETWORK_RUNTIME_CHANNEL = "SomeChannel";

View File

@ -3,9 +3,6 @@
"use strict";
/* import-globals-from helper-mocks.js */
Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "helper-mocks.js", this);
const NETWORK_RUNTIME_HOST = "localhost:6080";
const NETWORK_RUNTIME_APP_NAME = "TestNetworkApp";
const WORKER_NAME = "testserviceworker";
@ -18,7 +15,8 @@ const WORKER_NAME = "testserviceworker";
add_task(async function() {
const mocks = new Mocks();
const { document, tab, window } = await openAboutDebugging();
const { document, tab, window } =
await openAboutDebugging({ enableWorkerUpdates: true });
info("Prepare Network client mock");
const networkClient = mocks.createNetworkRuntime(NETWORK_RUNTIME_HOST, {
@ -39,7 +37,7 @@ add_task(async function() {
sharedWorkers: [],
};
networkClient.listWorkers = () => workers;
networkClient._eventEmitter.emit("workerListChanged");
networkClient._eventEmitter.emit("workersUpdated");
info("Wait until the service worker is displayed");
await waitUntil(() => findDebugTargetByText(WORKER_NAME, document));

View File

@ -17,7 +17,7 @@ const EMPTY_SW_HTML = URL_ROOT + "resources/service-workers/empty-sw.html";
*/
add_task(async function() {
await enableServiceWorkerDebugging();
const { document, tab } = await openAboutDebugging();
const { document, tab } = await openAboutDebugging({ enableWorkerUpdates: true });
info("Test fetch status for a service worker listening to fetch events");
await testServiceWorkerFetchStatus(document, FETCH_SW_HTML, FETCH_SW_JS, true);

View File

@ -35,7 +35,7 @@ async function testDebuggingSW(enableMultiE10sFn, disableMultiE10sFn) {
// enable service workers
await pushPref("dom.serviceWorkers.testing.enabled", true);
const { document, tab } = await openAboutDebugging();
const { document, tab } = await openAboutDebugging({ enableWorkerUpdates: true });
// disable multi e10s
info("Disabling multi e10s");

View File

@ -3,9 +3,6 @@
"use strict";
/* import-globals-from helper-mocks.js */
Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "helper-mocks.js", this);
// Test migrated from
// devtools/client/aboutdebugging/test/browser_service_workers_not_compatible.js
@ -47,7 +44,7 @@ add_task(async function testLocalRuntime() {
await pushPref("dom.serviceWorkers.enabled", serviceWorkersEnabled);
await pushPref("browser.privatebrowsing.autostart", privateBrowsingEnabled);
const { document, tab } = await openAboutDebugging();
const { document, tab } = await openAboutDebugging({ enableWorkerUpdates: true });
assertWarningMessage(document, expectedMessage);
await removeTab(tab);
}
@ -74,7 +71,7 @@ add_task(async function testRemoteRuntime() {
client.setPreference("dom.serviceWorkers.enabled", serviceWorkersEnabled);
client.setPreference("browser.privatebrowsing.autostart", privateBrowsingEnabled);
const { document, tab } = await openAboutDebugging();
const { document, tab } = await openAboutDebugging({ enableWorkerUpdates: true });
info("Checking a USB runtime");
mocks.emitUSBUpdate();

View File

@ -13,7 +13,7 @@ const TAB_URL = URL_ROOT + "resources/service-workers/push-sw.html";
// It should trigger a "push" notification in the worker.
add_task(async function() {
await enableServiceWorkerDebugging();
const { document, tab } = await openAboutDebugging();
const { document, tab } = await openAboutDebugging({ enableWorkerUpdates: true });
// Open a tab that registers a push service worker.
const swTab = await addTab(TAB_URL);

View File

@ -19,7 +19,7 @@ add_task(async function() {
info("Mock the push service");
mockPushService(FAKE_ENDPOINT);
const { document, tab } = await openAboutDebugging();
const { document, tab } = await openAboutDebugging({ enableWorkerUpdates: true });
// Open a tab that registers a push service worker.
const swTab = await addTab(TAB_URL);

View File

@ -21,7 +21,8 @@ const SW_URL = URL_ROOT + "resources/service-workers/push-sw.js";
add_task(async function() {
prepareCollapsibilitiesTest();
await enableServiceWorkerDebugging();
const { document, tab, window } = await openAboutDebugging();
const { document, tab, window } =
await openAboutDebugging({ enableWorkerUpdates: true });
const store = window.AboutDebugging.store;
await selectThisFirefoxPage(document, store);

View File

@ -25,7 +25,7 @@ add_task(async function() {
await pushPref("dom.serviceWorkers.idle_timeout", 1000);
await pushPref("dom.serviceWorkers.idle_extended_timeout", 1000);
const { document, tab } = await openAboutDebugging();
const { document, tab } = await openAboutDebugging({ enableWorkerUpdates: true });
// Open a tab that registers a basic service worker.
const swTab = await addTab(SW_TAB_URL);

View File

@ -16,7 +16,7 @@ const SW_URL = URL_ROOT + "resources/service-workers/controlled-sw.js";
add_task(async function() {
await enableServiceWorkerDebugging();
const { document, tab } = await openAboutDebugging();
const { document, tab } = await openAboutDebugging({ enableWorkerUpdates: true });
info("Open tab with a service worker that never leaves `registering` status");
const swTab = await addTab(SW_TAB_URL);

View File

@ -31,7 +31,7 @@ add_task(async function() {
await pushPref("dom.serviceWorkers.idle_timeout", SW_TIMEOUT);
await pushPref("dom.serviceWorkers.idle_extended_timeout", SW_TIMEOUT);
const { document, tab } = await openAboutDebugging();
const { document, tab } = await openAboutDebugging({ enableWorkerUpdates: true });
// Open a tab that registers a basic service worker.
const swTab = await addTab(SW_TAB_URL);

View File

@ -18,7 +18,7 @@ const SW_URL = URL_ROOT + "resources/service-workers/empty-sw.js";
add_task(async function() {
await enableServiceWorkerDebugging();
const { document, tab } = await openAboutDebugging();
const { document, tab } = await openAboutDebugging({ enableWorkerUpdates: true });
// Open a tab that registers a basic service worker.
const swTab = await addTab(SW_TAB_URL);

View File

@ -3,9 +3,6 @@
"use strict";
/* import-globals-from helper-mocks.js */
Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "helper-mocks.js", this);
// Test that USB runtimes appear and disappear from the sidebar.
add_task(async function() {
const mocks = new Mocks();

View File

@ -3,9 +3,6 @@
"use strict";
/* import-globals-from helper-mocks.js */
Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "helper-mocks.js", this);
const RUNTIME_ID = "test-runtime-id";
const RUNTIME_DEVICE_NAME = "test device name";

View File

@ -3,9 +3,6 @@
"use strict";
/* import-globals-from helper-mocks.js */
Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "helper-mocks.js", this);
const RUNTIME_ID = "test-runtime-id";
const RUNTIME_DEVICE_NAME = "test device name";
const RUNTIME_APP_NAME = "TestApp";

View File

@ -3,9 +3,6 @@
"use strict";
/* import-globals-from helper-mocks.js */
Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "helper-mocks.js", this);
const RUNTIME_DEVICE_ID = "1234";
const RUNTIME_DEVICE_NAME = "A device";

View File

@ -3,9 +3,6 @@
"use strict";
/* import-globals-from helper-mocks.js */
Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "helper-mocks.js", this);
const RUNTIME_NAME = "Firefox 123";
// Test that unknown runtimes:

View File

@ -3,13 +3,6 @@
"use strict";
/* import-globals-from mocks/helper-client-wrapper-mock.js */
Services.scriptloader.loadSubScript(
CHROME_URL_ROOT + "mocks/helper-client-wrapper-mock.js", this);
/* import-globals-from mocks/helper-runtime-client-factory-mock.js */
Services.scriptloader.loadSubScript(
CHROME_URL_ROOT + "mocks/helper-runtime-client-factory-mock.js", this);
// Test that system addons are only displayed when the showSystemAddons preference is
// true.

View File

@ -2,9 +2,6 @@
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/* import-globals-from helper-mocks.js */
Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "helper-mocks.js", this);
/* import-globals-from helper-telemetry.js */
Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "helper-telemetry.js", this);

View File

@ -3,13 +3,6 @@
"use strict";
/* import-globals-from mocks/helper-client-wrapper-mock.js */
Services.scriptloader.loadSubScript(
CHROME_URL_ROOT + "mocks/helper-client-wrapper-mock.js", this);
/* import-globals-from mocks/helper-runtime-client-factory-mock.js */
Services.scriptloader.loadSubScript(
CHROME_URL_ROOT + "mocks/helper-runtime-client-factory-mock.js", this);
/**
* Check that the runtime info is correctly displayed for ThisFirefox.
* Also acts as basic sanity check for the default mock of the this-firefox client.

View File

@ -3,13 +3,6 @@
"use strict";
/* import-globals-from mocks/helper-client-wrapper-mock.js */
Services.scriptloader.loadSubScript(
CHROME_URL_ROOT + "mocks/helper-client-wrapper-mock.js", this);
/* import-globals-from mocks/helper-runtime-client-factory-mock.js */
Services.scriptloader.loadSubScript(
CHROME_URL_ROOT + "mocks/helper-runtime-client-factory-mock.js", this);
const { gDevToolsBrowser } = require("devtools/client/framework/devtools-browser");
add_task(async function() {

View File

@ -3,9 +3,6 @@
"use strict";
/* import-globals-from helper-mocks.js */
Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "helper-mocks.js", this);
const NETWORK_RUNTIME_HOST = "localhost:6080";
const NETWORK_RUNTIME_APP_NAME = "TestNetworkApp";
const USB_RUNTIME_ID = "test-runtime-id";
@ -38,7 +35,7 @@ const EMPTY_WORKERS_RESPONSE = {
add_task(async function() {
const mocks = new Mocks();
const { document, tab } = await openAboutDebugging();
const { document, tab } = await openAboutDebugging({ enableWorkerUpdates: true });
info("Prepare USB client mock");
const usbClient = mocks.createUSBRuntime(USB_RUNTIME_ID, {
@ -95,7 +92,7 @@ async function testWorkerOnMockedRemoteClient(testData, remoteClient, firefoxCli
}],
});
remoteClient.listWorkers = () => workers;
remoteClient._eventEmitter.emit("workerListChanged");
remoteClient._eventEmitter.emit("workersUpdated");
info("Wait until the worker appears");
await waitUntil(() => !workersPane.querySelector(".js-debug-target-list-empty"));
@ -104,12 +101,12 @@ async function testWorkerOnMockedRemoteClient(testData, remoteClient, firefoxCli
ok(workerTarget, "Worker target appeared for the remote runtime");
// Check that the list of REMOTE workers are NOT updated when the local this-firefox
// emits a workerListChanged event.
// emits a workersUpdated event.
info("Remove the worker from the remote client WITHOUT sending an event");
remoteClient.listWorkers = () => EMPTY_WORKERS_RESPONSE;
info("Simulate a worker update on the ThisFirefox client");
firefoxClient._eventEmitter.emit("workerListChanged");
firefoxClient._eventEmitter.emit("workersUpdated");
// To avoid wait for a set period of time we trigger another async update, adding a new
// tab. We assume that if the worker update mechanism had started, it would also be done
@ -123,7 +120,7 @@ async function testWorkerOnMockedRemoteClient(testData, remoteClient, firefoxCli
ok(findDebugTargetByText(workerName, document),
"The test worker is still visible");
info("Emit `workerListChanged` on remoteClient and wait for the target list to update");
remoteClient._eventEmitter.emit("workerListChanged");
info("Emit `workersUpdated` on remoteClient and wait for the target list to update");
remoteClient._eventEmitter.emit("workersUpdated");
await waitUntil(() => !findDebugTargetByText(workerName, document));
}

View File

@ -17,6 +17,9 @@ Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/shared/test/shared-redux-head.js",
this);
/* import-globals-from helper-mocks.js */
Services.scriptloader.loadSubScript(CHROME_URL_ROOT + "helper-mocks.js", this);
// Make sure the ADB addon is removed and ADB is stopped when the test ends.
registerCleanupFunction(async function() {
try {
@ -41,12 +44,16 @@ async function enableNewAboutDebugging() {
await pushPref("devtools.aboutdebugging.network", true);
}
async function openAboutDebugging(page, win) {
async function openAboutDebugging({ enableWorkerUpdates } = {}) {
if (!enableWorkerUpdates) {
silenceWorkerUpdates();
}
await enableNewAboutDebugging();
info("opening about:debugging");
const tab = await addTab("about:debugging", { window: win });
const tab = await addTab("about:debugging");
const browser = tab.linkedBrowser;
const document = browser.contentDocument;
const window = browser.contentWindow;

View File

@ -163,3 +163,23 @@ class Mocks {
}
/* exported Mocks */
const silenceWorkerUpdates = function() {
const { removeMockedModule, setMockedModule } =
require("devtools/client/shared/browser-loader-mocks");
const mock = {
WorkersListener: () => {
return {
addListener: () => {},
removeListener: () => {},
};
},
};
setMockedModule(mock,
"devtools/client/aboutdebugging-new/src/modules/workers-listener");
registerCleanupFunction(() => {
removeMockedModule("devtools/client/aboutdebugging-new/src/modules/workers-listener");
});
};
/* exported silenceWorkerUpdates */

View File

@ -39,7 +39,6 @@ function createClientMock() {
removeListener: (evt, listener) => {
eventEmitter.off(evt, listener);
},
client: {
addOneTimeListener: (evt, listener) => {
eventEmitter.once(evt, listener);

View File

@ -0,0 +1,21 @@
/* 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/. */
.error-page {
--base-unit: 4px; /* from photon */
padding: calc(var(--base-unit) * 4);
font-size: 15px; /* from photon */
min-height: 100vh;
}
.error-page__title {
margin: 0;
font-size: 36px; /* from photon */
font-weight: 200; /* from photon */
}
.error-page__details {
font-family: monospace;
}

View File

@ -0,0 +1,50 @@
/* 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/. */
"use strict";
const { PureComponent } = require("devtools/client/shared/vendor/react");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
/**
* This component is displayed when the about:devtools-toolbox fails to load
* properly due to wrong parameters or debug targets that don't exist.
*/
class DebugTargetErrorPage extends PureComponent {
static get propTypes() {
return {
errorMessage: PropTypes.string.isRequired,
L10N: PropTypes.object.isRequired,
};
}
render() {
const { errorMessage, L10N } = this.props;
return dom.article(
{
className: "error-page js-error-page",
},
dom.h1(
{
className: "error-page__title",
},
L10N.getStr("toolbox.debugTargetErrorPage.title"),
),
dom.p(
{},
L10N.getStr("toolbox.debugTargetErrorPage.description"),
),
dom.output(
{
className: "error-page__details",
},
errorMessage,
),
);
}
}
module.exports = DebugTargetErrorPage;

View File

@ -6,6 +6,8 @@
DevToolsModules(
'DebugTargetErrorPage.css',
'DebugTargetErrorPage.js',
'DebugTargetInfo.js',
'MeatballMenu.js',
'ToolboxController.js',

View File

@ -137,7 +137,11 @@ async function clientFromURL(url) {
// If a remote id was provided we should already have a connected client available.
const remoteId = params.get("remoteId");
if (remoteId) {
return remoteClientManager.getClientByRemoteId(remoteId);
const client = remoteClientManager.getClientByRemoteId(remoteId);
if (!client) {
throw new Error(`Could not find client with remote id: ${remoteId}`);
}
return client;
}
const host = params.get("host");

View File

@ -54,6 +54,7 @@ support-files =
!/devtools/client/shared/test/shared-redux-head.js
!/devtools/client/shared/test/telemetry-test-helpers.js
[browser_about-devtools-toolbox_load.js]
[browser_browser_toolbox.js]
skip-if = coverage # Bug 1387827
[browser_browser_toolbox_debugger.js]

View File

@ -0,0 +1,42 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Test that about:devtools-toolbox shows error an page when opened with invalid
* paramters
*/
add_task(async function() {
// test that error is shown when missing `type` param
let { document, tab } = await openAboutToolbox({ invalid: "invalid" });
await assertErrorIsShown(document);
await removeTab(tab);
// test that error is shown if `id` is not provided
({ document, tab } = await openAboutToolbox({ type: "tab" }));
await assertErrorIsShown(document);
await removeTab(tab);
// test that error is shown if `remoteId` refers to an unexisting target
({ document, tab } = await openAboutToolbox({ type: "tab", remoteId: "13371337" }));
await assertErrorIsShown(document);
await removeTab(tab);
async function assertErrorIsShown(doc) {
await waitUntil(() => doc.querySelector(".js-error-page"));
ok(doc.querySelector(".js-error-page"), "Error page is rendered");
}
});
async function openAboutToolbox(params) {
info("opening about:devtools-toolbox");
const querystring = new URLSearchParams();
Object.keys(params).forEach(x => querystring.append(x, params[x]));
const tab = await addTab(`about:devtools-toolbox?${querystring}`);
const browser = tab.linkedBrowser;
return {
tab,
document: browser.contentDocument,
};
}

View File

@ -7,43 +7,78 @@
"use strict";
const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm");
// URL constructor doesn't support about: scheme
const href = window.location.href.replace("about:", "http://");
const url = new window.URL(href);
// Only use this method to attach the toolbox if some query parameters are given
if (url.search.length > 1) {
const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm");
// `host` is the frame element loading the toolbox.
let host = window.windowUtils.containerElement;
// If there's no containerElement (which happens when loading about:devtools-toolbox as
// a top level document), use the current window.
if (!host) {
host = {
contentWindow: window,
contentDocument: document,
// toolbox-host-manager.js wants to set attributes on the frame that contains it,
// but that is fine to skip and doesn't make sense when using the current window.
setAttribute() {},
ownerDocument: document,
// toolbox-host-manager.js wants to listen for unload events from outside the frame,
// but this is fine to skip since the toolbox code listens inside the frame as well,
// and there is no outer document in this case.
addEventListener() {},
};
}
const onLoad = new Promise(r => {
host.contentWindow.addEventListener("DOMContentLoaded", r, { once: true });
});
async function showErrorPage(doc, errorMessage) {
const win = doc.defaultView;
const { BrowserLoader } =
ChromeUtils.import("resource://devtools/client/shared/browser-loader.js");
const browserRequire = BrowserLoader({
window: win,
useOnlyShared: true,
}).require;
const React = browserRequire("devtools/client/shared/vendor/react");
const ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom");
const DebugTargetErrorPage = React.createFactory(
require("devtools/client/framework/components/DebugTargetErrorPage"));
const { LocalizationHelper } = browserRequire("devtools/shared/l10n");
const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
// mount the React component into our XUL container once the DOM is ready
await onLoad;
const mountEl = doc.querySelector("#toolbox-error-mount");
const element = DebugTargetErrorPage({
errorMessage,
L10N,
});
ReactDOM.render(element, mountEl);
// make sure we unmount the component when the page is destroyed
win.addEventListener("unload", () => {
ReactDOM.unmountComponentAtNode(mountEl);
}, { once: true });
}
async function initToolbox(url, host) {
const { gDevTools } = require("devtools/client/framework/devtools");
const { targetFromURL } = require("devtools/client/framework/target-from-url");
const { Toolbox } = require("devtools/client/framework/toolbox");
const { DebuggerServer } = require("devtools/server/main");
const { DebuggerClient } = require("devtools/shared/client/debugger-client");
// `host` is the frame element loading the toolbox.
let host = window.windowUtils.containerElement;
// If there's no containerElement (which happens when loading about:devtools-toolbox as
// a top level document), use the current window.
if (!host) {
host = {
contentWindow: window,
contentDocument: document,
// toolbox-host-manager.js wants to set attributes on the frame that contains it,
// but that is fine to skip and doesn't make sense when using the current window.
setAttribute() {},
ownerDocument: document,
// toolbox-host-manager.js wants to listen for unload events from outside the frame,
// but this is fine to skip since the toolbox code listens inside the frame as well,
// and there is no outer document in this case.
addEventListener() {},
};
}
// Specify the default tool to open
const tool = url.searchParams.get("tool");
(async function() {
try {
let target;
if (url.searchParams.has("target")) {
// Attach toolbox to a given browser iframe (<xul:browser> or <html:iframe
@ -76,7 +111,16 @@ if (url.search.length > 1) {
}
const options = { customIframe: host };
await gDevTools.showToolbox(target, tool, Toolbox.HostType.PAGE, options);
})().catch(error => {
} catch (error) {
// When an error occurs, show error page with message.
console.error("Exception while loading the toolbox", error);
});
showErrorPage(host.contentDocument, `${error}`);
}
}
// Only use this method to attach the toolbox if some query parameters are given
if (url.search.length > 1) {
initToolbox(url, host);
}
// TODO: handle no params in about:devtool-toolbox
// https://bugzilla.mozilla.org/show_bug.cgi?id=1526996

View File

@ -5,6 +5,7 @@
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://devtools/skin/toolbox.css" type="text/css"?>
<?xml-stylesheet href="resource://devtools/client/shared/components/NotificationBox.css" type="text/css"?>
<?xml-stylesheet href="resource://devtools/client/framework/components/DebugTargetErrorPage.css" type="text/css"?>
<!DOCTYPE window [
<!ENTITY % toolboxDTD SYSTEM "chrome://devtools/locale/toolbox.dtd" >
@ -27,6 +28,7 @@
src="chrome://devtools/content/framework/toolbox-init.js"/>
<vbox id="toolbox-container" flex="1">
<div xmlns="http://www.w3.org/1999/xhtml" id="toolbox-error-mount"/>
<div xmlns="http://www.w3.org/1999/xhtml" id="toolbox-notificationbox"/>
<div xmlns="http://www.w3.org/1999/xhtml" id="toolbox-toolbar-mount"
role="toolbar" />

View File

@ -123,8 +123,4 @@ async function waitUntilDebuggerReady(debuggerContext) {
// We have to wait until the debugger has fully loaded the source otherwise
// we will get unhandled promise rejections.
await waitForLoadedSource(debuggerContext, "data:");
// Have to wait until https://github.com/devtools-html/debugger.html/pull/6189
// is released to avoid unhandled promise rejections.
await waitForTime(1000);
}

View File

@ -244,3 +244,13 @@ browserToolbox.statusMessage=Browser Toolbox connection status:
# LOCALIZATION NOTE (toolbox.replay.jumpMessage): This is the label
# shown in the web replay timeline marker
toolbox.replay.jumpMessage=Jump to message %1$S
# LOCALIZATION NOTE (toolbox.debugTargetErrorPage.title): This is the title
# for the Error view shown by the toolbox when a connection to a debug target
# could not be made
toolbox.debugTargetErrorPage.title = Error
# LOCALIZATION NOTE (toolbox.debugTargetErrorPage.description): This is the
# text that appears in the Error view and explains to the user that an error
# has happened while trying to connect to a debug target
toolbox.debugTargetErrorPage.description = Cannot connect to the debug target. See error details below:

View File

@ -9,9 +9,6 @@ loader.lazyRequireGetter(this, "extend", "devtools/shared/extend", true);
loader.lazyRequireGetter(this, "Telemetry", "devtools/client/shared/telemetry");
loader.lazyRequireGetter(this, "WebConsole", "devtools/client/webconsole/webconsole");
// The preference prefix for all of the Browser Console filters.
const BC_FILTER_PREFS_PREFIX = "devtools.browserconsole.filter.";
/**
* A BrowserConsole instance is an interactive console initialized *per target*
* that displays console log data as well as provides an interactive terminal to
@ -59,8 +56,6 @@ BrowserConsole.prototype = extend(WebConsole.prototype, {
// Only add the shutdown observer if we've opened a Browser Console window.
ShutdownObserver.init(this.hudService);
this.ui._filterPrefsPrefix = BC_FILTER_PREFS_PREFIX;
const window = this.iframeWindow;
// Make sure that the closing of the Browser Console window destroys this

View File

@ -40,6 +40,9 @@ nsIContent* ExplicitChildIterator::GetNextChild() {
mChild = (mIndexInInserted < assignedNodes.Length())
? assignedNodes[mIndexInInserted++]->AsContent()
: nullptr;
if (!mChild) {
mIndexInInserted = 0;
}
return mChild;
}

View File

@ -3146,6 +3146,8 @@ nsIContent* nsFocusManager::GetNextTabbableContentInAncestorScopes(
int32_t tabIndex = 0;
if (IsHostOrSlot(startContent)) {
tabIndex = HostOrSlotTabIndexValue(startContent);
} else if (nsIFrame* frame = startContent->GetPrimaryFrame()) {
frame->IsFocusable(&tabIndex);
} else {
startContent->IsFocusable(&tabIndex);
}

View File

@ -653,6 +653,127 @@
input1.remove();
}
function testTabbingThroughScrollableShadowDOM() {
opener.is(document.activeElement, document.body.firstChild, "body's first child should have focus.");
var host0 = document.createElement("div");
host0.setAttribute("style", "height: 50px; overflow: auto;");
host0.onfocus = focusLogger;
document.body.appendChild(host0);
var sr0 = host0.attachShadow({mode: "open"});
sr0.innerHTML = `
<style>
div,slot {
height: 30px;
display: block;
overflow: auto;
}
input {
display: block;
}
</style>
`;
var input00 = document.createElement("input");
input00.setAttribute("style", "background-color: red;");
input00.onfocus = focusLogger;
sr0.appendChild(input00);
var container01 = document.createElement("div");
container01.onfocus = focusLogger;
sr0.appendChild(container01);
var input010 = document.createElement("input");
input010.onfocus = focusLogger;
container01.appendChild(input010);
var input011 = document.createElement("input");
input011.onfocus = focusLogger;
container01.appendChild(input011);
var slot02 = document.createElement("slot");
slot02.onfocus = focusLogger;
sr0.appendChild(slot02);
var input020 = document.createElement("input");
input020.setAttribute("style", "display: block;");
input020.onfocus = focusLogger;
host0.appendChild(input020);
var input021 = document.createElement("input");
input021.setAttribute("style", "display: block;");
input021.onfocus = focusLogger;
host0.appendChild(input021);
var input1 = document.createElement("input");
input1.onfocus = focusLogger;
document.body.appendChild(input1);
document.body.offsetLeft;
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, host0, "Should have focused shadow host element. (1)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, input00, "Should have focused input element in shadow dom. (2)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, container01, "Should have focused scrollable element in shadow dom. (3)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, input010, "Should have focused input element in shadow dom. (4)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, input011, "Should have focused input element in shadow dom. (5)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, slot02, "Should have focused slot element in shadow dom. (6)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, input020, "Should have focused input element in slot. (7)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, input021, "Should have focused input element in slot. (8)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, input1, "Should have focused input element in light dom. (9)");
// Backwards
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, input021, "Should have focused input element in slot. (10)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, input020, "Should have focused input element in slot. (11)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, slot02, "Should have focused slot element in shadow dom. (12)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, input011, "Should have focused input element in shadow dom. (13)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, input010, "Should have focused input element in shadow dom. (14)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, container01, "Should have focused scrollable element in shadow dom. (15)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, input00, "Should have focused input element in shadow dom. (16)");
synthesizeKey("KEY_Tab", {shiftKey: true});
// focus is already on host
opener.is(sr0.activeElement, null,
"Focus should have left input element in shadow DOM. (7)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(document.activeElement, document.body.firstChild,
"body's first child should have focus.");
host0.remove();
input1.remove();
}
function runTest() {
testTabbingThroughShadowDOMWithTabIndexes();
@ -665,6 +786,7 @@
testTabbingThroughNestedSlot();
testTabbingThroughSlotInLightDOM();
testTabbingThroughFocusableSlotInLightDOM();
testTabbingThroughScrollableShadowDOM();
opener.didRunTests();
window.close();

View File

@ -397,12 +397,11 @@ nsresult AudioStream::OpenCubeb(cubeb* aContext, cubeb_stream_params& aParams,
void AudioStream::SetVolume(double aVolume) {
MOZ_ASSERT(aVolume >= 0.0 && aVolume <= 1.0, "Invalid volume");
#ifdef DEBUG
{
MonitorAutoLock mon(mMonitor);
MOZ_ASSERT(mState != SHUTDOWN, "Don't set volume after shutdown.");
MOZ_DIAGNOSTIC_ASSERT(mState != ERRORED, "Don't set volume if stream got error.");
}
#endif
if (cubeb_stream_set_volume(mCubebStream.get(),
aVolume * CubebUtils::GetVolumeScale()) !=

View File

@ -1384,11 +1384,15 @@ class RTCPeerConnection {
}
getSenders() {
return this.getTransceivers().map(transceiver => transceiver.sender);
return this.getTransceivers()
.filter(transceiver => !transceiver.stopped)
.map(transceiver => transceiver.sender);
}
getReceivers() {
return this.getTransceivers().map(transceiver => transceiver.receiver);
return this.getTransceivers()
.filter(transceiver => !transceiver.stopped)
.map(transceiver => transceiver.receiver);
}
// test-only: get the current time using the webrtc clock

View File

@ -2572,11 +2572,16 @@ nsresult ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsPIDOMWindowOuter> window = mDocument->GetWindow();
nsIDocShell* docShell = window ? window->GetDocShell() : nullptr;
nsAutoCString profilerLabelString;
GetProfilerLabelForRequest(aRequest, profilerLabelString);
AUTO_PROFILER_TEXT_MARKER_DOCSHELL("Script", profilerLabelString, JS,
docShell);
// New script entry point required, due to the "Create a script" sub-step of
// http://www.whatwg.org/specs/web-apps/current-work/#execute-the-script-block
nsAutoMicroTask mt;
nsAutoCString profilerLabelString;
GetProfilerLabelForRequest(aRequest, profilerLabelString);
AutoEntryScript aes(globalObject, profilerLabelString.get(), true);
JSContext* cx = aes.cx();
JS::Rooted<JSObject*> global(cx, globalObject->GetGlobalJSObject());

View File

@ -59,6 +59,14 @@ void VsyncSource::Display::NotifyVsync(TimeStamp aVsyncTimestamp) {
// Called on the vsync thread
MutexAutoLock lock(mDispatcherLock);
// mRefreshTimerVsyncDispatcher might be null here if MoveListenersToNewSource
// was called concurrently with this function and won the race to acquire
// mDispatcherLock. In this case the new VsyncSource that is replacing this
// one will handle notifications from now on, so we can abort.
if (!mRefreshTimerVsyncDispatcher) {
return;
}
mVsyncId = mVsyncId.Next();
VsyncEvent event(mVsyncId, aVsyncTimestamp);

View File

@ -19,6 +19,7 @@
#include "vm/RegExpStatics.h"
#include "vm/SelfHosting.h"
#include "vm/EnvironmentObject-inl.h"
#include "vm/JSObject-inl.h"
#include "vm/NativeObject-inl.h"
#include "vm/ObjectOperations-inl.h"

View File

@ -61,11 +61,9 @@ namespace frontend {
*/
#define FOR_EACH_BIN_KIND(F) \
F(_Null, "") \
F(Arguments, "Arguments") \
F(ArrayAssignmentTarget, "ArrayAssignmentTarget") \
F(ArrayBinding, "ArrayBinding") \
F(ArrayExpression, "ArrayExpression") \
F(ArrowExpression, "ArrowExpression") \
F(ArrowExpressionContentsWithExpression, \
"ArrowExpressionContentsWithExpression") \
F(ArrowExpressionContentsWithFunctionBody, \
@ -73,10 +71,7 @@ namespace frontend {
F(AssertedBlockScope, "AssertedBlockScope") \
F(AssertedBoundName, "AssertedBoundName") \
F(AssertedBoundNamesScope, "AssertedBoundNamesScope") \
F(AssertedDeclaredKind, "AssertedDeclaredKind") \
F(AssertedDeclaredName, "AssertedDeclaredName") \
F(AssertedMaybePositionalParameterName, \
"AssertedMaybePositionalParameterName") \
F(AssertedParameterName, "AssertedParameterName") \
F(AssertedParameterScope, "AssertedParameterScope") \
F(AssertedPositionalParameterName, "AssertedPositionalParameterName") \
@ -84,23 +79,13 @@ namespace frontend {
F(AssertedScriptGlobalScope, "AssertedScriptGlobalScope") \
F(AssertedVarScope, "AssertedVarScope") \
F(AssignmentExpression, "AssignmentExpression") \
F(AssignmentTarget, "AssignmentTarget") \
F(AssignmentTargetIdentifier, "AssignmentTargetIdentifier") \
F(AssignmentTargetOrAssignmentTargetWithInitializer, \
"AssignmentTargetOrAssignmentTargetWithInitializer") \
F(AssignmentTargetPattern, "AssignmentTargetPattern") \
F(AssignmentTargetProperty, "AssignmentTargetProperty") \
F(AssignmentTargetPropertyIdentifier, "AssignmentTargetPropertyIdentifier") \
F(AssignmentTargetPropertyProperty, "AssignmentTargetPropertyProperty") \
F(AssignmentTargetWithInitializer, "AssignmentTargetWithInitializer") \
F(AwaitExpression, "AwaitExpression") \
F(BinaryExpression, "BinaryExpression") \
F(BinaryOperator, "BinaryOperator") \
F(Binding, "Binding") \
F(BindingIdentifier, "BindingIdentifier") \
F(BindingOrBindingWithInitializer, "BindingOrBindingWithInitializer") \
F(BindingPattern, "BindingPattern") \
F(BindingProperty, "BindingProperty") \
F(BindingPropertyIdentifier, "BindingPropertyIdentifier") \
F(BindingPropertyProperty, "BindingPropertyProperty") \
F(BindingWithInitializer, "BindingWithInitializer") \
@ -112,7 +97,6 @@ namespace frontend {
F(ClassElement, "ClassElement") \
F(ClassExpression, "ClassExpression") \
F(CompoundAssignmentExpression, "CompoundAssignmentExpression") \
F(CompoundAssignmentOperator, "CompoundAssignmentOperator") \
F(ComputedMemberAssignmentTarget, "ComputedMemberAssignmentTarget") \
F(ComputedMemberExpression, "ComputedMemberExpression") \
F(ComputedPropertyName, "ComputedPropertyName") \
@ -133,45 +117,25 @@ namespace frontend {
F(EmptyStatement, "EmptyStatement") \
F(Export, "Export") \
F(ExportAllFrom, "ExportAllFrom") \
F(ExportDeclaration, "ExportDeclaration") \
F(ExportDefault, "ExportDefault") \
F(ExportFrom, "ExportFrom") \
F(ExportFromSpecifier, "ExportFromSpecifier") \
F(ExportLocalSpecifier, "ExportLocalSpecifier") \
F(ExportLocals, "ExportLocals") \
F(Expression, "Expression") \
F(ExpressionOrSuper, "ExpressionOrSuper") \
F(ExpressionOrTemplateElement, "ExpressionOrTemplateElement") \
F(ExpressionStatement, "ExpressionStatement") \
F(ForInOfBinding, "ForInOfBinding") \
F(ForInOfBindingOrAssignmentTarget, "ForInOfBindingOrAssignmentTarget") \
F(ForInStatement, "ForInStatement") \
F(ForOfStatement, "ForOfStatement") \
F(ForStatement, "ForStatement") \
F(FormalParameters, "FormalParameters") \
F(FunctionBody, "FunctionBody") \
F(FunctionDeclaration, "FunctionDeclaration") \
F(FunctionDeclarationOrClassDeclarationOrExpression, \
"FunctionDeclarationOrClassDeclarationOrExpression") \
F(FunctionDeclarationOrClassDeclarationOrVariableDeclaration, \
"FunctionDeclarationOrClassDeclarationOrVariableDeclaration") \
F(FunctionExpression, "FunctionExpression") \
F(FunctionExpressionContents, "FunctionExpressionContents") \
F(FunctionOrMethodContents, "FunctionOrMethodContents") \
F(Getter, "Getter") \
F(GetterContents, "GetterContents") \
F(Identifier, "Identifier") \
F(IdentifierExpression, "IdentifierExpression") \
F(IdentifierName, "IdentifierName") \
F(IfStatement, "IfStatement") \
F(Import, "Import") \
F(ImportDeclaration, "ImportDeclaration") \
F(ImportDeclarationOrExportDeclarationOrStatement, \
"ImportDeclarationOrExportDeclarationOrStatement") \
F(ImportNamespace, "ImportNamespace") \
F(ImportSpecifier, "ImportSpecifier") \
F(IterationStatement, "IterationStatement") \
F(Label, "Label") \
F(LabelledStatement, "LabelledStatement") \
F(LazyArrowExpressionWithExpression, "LazyArrowExpressionWithExpression") \
F(LazyArrowExpressionWithFunctionBody, \
@ -181,32 +145,6 @@ namespace frontend {
F(LazyGetter, "LazyGetter") \
F(LazyMethod, "LazyMethod") \
F(LazySetter, "LazySetter") \
F(ListOfAssertedBoundName, "ListOfAssertedBoundName") \
F(ListOfAssertedDeclaredName, "ListOfAssertedDeclaredName") \
F(ListOfAssertedMaybePositionalParameterName, \
"ListOfAssertedMaybePositionalParameterName") \
F(ListOfAssignmentTargetOrAssignmentTargetWithInitializer, \
"ListOfAssignmentTargetOrAssignmentTargetWithInitializer") \
F(ListOfAssignmentTargetProperty, "ListOfAssignmentTargetProperty") \
F(ListOfBindingProperty, "ListOfBindingProperty") \
F(ListOfClassElement, "ListOfClassElement") \
F(ListOfDirective, "ListOfDirective") \
F(ListOfExportFromSpecifier, "ListOfExportFromSpecifier") \
F(ListOfExportLocalSpecifier, "ListOfExportLocalSpecifier") \
F(ListOfExpressionOrTemplateElement, "ListOfExpressionOrTemplateElement") \
F(ListOfImportDeclarationOrExportDeclarationOrStatement, \
"ListOfImportDeclarationOrExportDeclarationOrStatement") \
F(ListOfImportSpecifier, "ListOfImportSpecifier") \
F(ListOfObjectProperty, "ListOfObjectProperty") \
F(ListOfOptionalBindingOrBindingWithInitializer, \
"ListOfOptionalBindingOrBindingWithInitializer") \
F(ListOfOptionalSpreadElementOrExpression, \
"ListOfOptionalSpreadElementOrExpression") \
F(ListOfParameter, "ListOfParameter") \
F(ListOfStatement, "ListOfStatement") \
F(ListOfSwitchCase, "ListOfSwitchCase") \
F(ListOfVariableDeclarator, "ListOfVariableDeclarator") \
F(Literal, "Literal") \
F(LiteralBooleanExpression, "LiteralBooleanExpression") \
F(LiteralInfinityExpression, "LiteralInfinityExpression") \
F(LiteralNullExpression, "LiteralNullExpression") \
@ -214,42 +152,17 @@ namespace frontend {
F(LiteralPropertyName, "LiteralPropertyName") \
F(LiteralRegExpExpression, "LiteralRegExpExpression") \
F(LiteralStringExpression, "LiteralStringExpression") \
F(Method, "Method") \
F(MethodDefinition, "MethodDefinition") \
F(Module, "Module") \
F(NewExpression, "NewExpression") \
F(NewTargetExpression, "NewTargetExpression") \
F(ObjectAssignmentTarget, "ObjectAssignmentTarget") \
F(ObjectBinding, "ObjectBinding") \
F(ObjectExpression, "ObjectExpression") \
F(ObjectProperty, "ObjectProperty") \
F(OptionalAssignmentTarget, "OptionalAssignmentTarget") \
F(OptionalBinding, "OptionalBinding") \
F(OptionalBindingIdentifier, "OptionalBindingIdentifier") \
F(OptionalBindingOrBindingWithInitializer, \
"OptionalBindingOrBindingWithInitializer") \
F(OptionalCatchClause, "OptionalCatchClause") \
F(OptionalExpression, "OptionalExpression") \
F(OptionalIdentifierName, "OptionalIdentifierName") \
F(OptionalLabel, "OptionalLabel") \
F(OptionalPropertyKey, "OptionalPropertyKey") \
F(OptionalSpreadElementOrExpression, "OptionalSpreadElementOrExpression") \
F(OptionalStatement, "OptionalStatement") \
F(OptionalVariableDeclarationOrExpression, \
"OptionalVariableDeclarationOrExpression") \
F(Parameter, "Parameter") \
F(Program, "Program") \
F(PropertyKey, "PropertyKey") \
F(PropertyName, "PropertyName") \
F(ReturnStatement, "ReturnStatement") \
F(Script, "Script") \
F(Setter, "Setter") \
F(SetterContents, "SetterContents") \
F(ShorthandProperty, "ShorthandProperty") \
F(SimpleAssignmentTarget, "SimpleAssignmentTarget") \
F(SpreadElement, "SpreadElement") \
F(SpreadElementOrExpression, "SpreadElementOrExpression") \
F(Statement, "Statement") \
F(StaticMemberAssignmentTarget, "StaticMemberAssignmentTarget") \
F(StaticMemberExpression, "StaticMemberExpression") \
F(Super, "Super") \
@ -264,18 +177,13 @@ namespace frontend {
F(TryCatchStatement, "TryCatchStatement") \
F(TryFinallyStatement, "TryFinallyStatement") \
F(UnaryExpression, "UnaryExpression") \
F(UnaryOperator, "UnaryOperator") \
F(UpdateExpression, "UpdateExpression") \
F(UpdateOperator, "UpdateOperator") \
F(VariableDeclaration, "VariableDeclaration") \
F(VariableDeclarationKind, "VariableDeclarationKind") \
F(VariableDeclarationOrExpression, "VariableDeclarationOrExpression") \
F(VariableDeclarator, "VariableDeclarator") \
F(WhileStatement, "WhileStatement") \
F(WithStatement, "WithStatement") \
F(YieldExpression, "YieldExpression") \
F(YieldStarExpression, "YieldStarExpression") \
F(String, "string")
F(YieldStarExpression, "YieldStarExpression")
enum class BinKind {
#define EMIT_ENUM(name, _) name,
@ -284,7 +192,7 @@ enum class BinKind {
};
// The number of distinct values of BinKind.
const size_t BINKIND_LIMIT = 200;
const size_t BINKIND_LIMIT = 120;
/**
* The different fields of Binary AST nodes, as per the specifications of

View File

@ -933,9 +933,11 @@ impl CPPExporter {
buffer.push_str(&self.rules.hpp_tokens_kind_doc.reindent(""));
}
let node_names = self.syntax.node_names()
let node_names = self.syntax.interfaces_by_name()
.keys()
.map(|n| n.to_string())
.sorted();
let kind_limit = node_names.len();
buffer.push_str(&format!("\n#define FOR_EACH_BIN_KIND(F) \\\n{nodes}\n",
nodes = node_names.iter()
.map(|name| format!(" F({enum_name}, \"{spec_name}\")",
@ -950,7 +952,7 @@ enum class BinKind {
};
");
buffer.push_str(&format!("\n// The number of distinct values of BinKind.\nconst size_t BINKIND_LIMIT = {};\n\n\n", self.syntax.node_names().len()));
buffer.push_str(&format!("\n// The number of distinct values of BinKind.\nconst size_t BINKIND_LIMIT = {};\n\n\n", kind_limit));
buffer.push_str("\n\n");
if self.rules.hpp_tokens_field_doc.is_some() {
buffer.push_str(&self.rules.hpp_tokens_field_doc.reindent(""));
@ -959,6 +961,7 @@ enum class BinKind {
let field_names = self.syntax.field_names()
.keys()
.sorted();
let field_limit = field_names.len();
buffer.push_str(&format!("\n#define FOR_EACH_BIN_FIELD(F) \\\n{nodes}\n",
nodes = field_names.iter()
.map(|name| format!(" F({enum_name}, \"{spec_name}\")",
@ -972,7 +975,7 @@ enum class BinField {
#undef EMIT_ENUM
};
");
buffer.push_str(&format!("\n// The number of distinct values of BinField.\nconst size_t BINFIELD_LIMIT = {};\n\n\n", self.syntax.field_names().len()));
buffer.push_str(&format!("\n// The number of distinct values of BinField.\nconst size_t BINFIELD_LIMIT = {};\n\n\n", field_limit));
if self.rules.hpp_tokens_variants_doc.is_some() {
buffer.push_str(&self.rules.hpp_tokens_variants_doc.reindent(""));
@ -983,6 +986,7 @@ enum class BinField {
Ord::cmp(name_1, name_2)
.then_with(|| Ord::cmp(symbol_1, symbol_2))
});
let variants_limit = enum_variants.len();
buffer.push_str(&format!("\n#define FOR_EACH_BIN_VARIANT(F) \\\n{nodes}\n",
nodes = enum_variants.into_iter()
@ -999,7 +1003,7 @@ enum class BinVariant {
};
");
buffer.push_str(&format!("\n// The number of distinct values of BinVariant.\nconst size_t BINVARIANT_LIMIT = {};\n\n\n",
self.variants_by_symbol.len()));
variants_limit));
buffer.push_str(&self.rules.hpp_tokens_footer.reindent(""));
buffer.push_str("\n");

View File

@ -3132,16 +3132,15 @@ void BaselineInterpreterCodeGen::getEnvironmentCoordinateObject(Register reg) {
template <>
Address BaselineCompilerCodeGen::getEnvironmentCoordinateAddressFromObject(
Register objReg, Register reg) {
JSScript* script = handler.script();
EnvironmentCoordinate ec(handler.pc());
Shape* shape = EnvironmentCoordinateToEnvironmentShape(script, handler.pc());
if (shape->numFixedSlots() <= ec.slot()) {
masm.loadPtr(Address(objReg, NativeObject::offsetOfSlots()), reg);
return Address(reg, (ec.slot() - shape->numFixedSlots()) * sizeof(Value));
if (EnvironmentObject::nonExtensibleIsFixedSlot(ec)) {
return Address(objReg, NativeObject::getFixedSlotOffset(ec.slot()));
}
return Address(objReg, NativeObject::getFixedSlotOffset(ec.slot()));
uint32_t slot = EnvironmentObject::nonExtensibleDynamicSlotIndex(ec);
masm.loadPtr(Address(objReg, NativeObject::offsetOfSlots()), reg);
return Address(reg, slot * sizeof(Value));
}
template <>

View File

@ -13155,16 +13155,15 @@ MDefinition* IonBuilder::walkEnvironmentChain(unsigned hops) {
MDefinition* IonBuilder::getAliasedVar(EnvironmentCoordinate ec) {
MDefinition* obj = walkEnvironmentChain(ec.hops());
Shape* shape = EnvironmentCoordinateToEnvironmentShape(script(), pc);
MInstruction* load;
if (shape->numFixedSlots() <= ec.slot()) {
if (EnvironmentObject::nonExtensibleIsFixedSlot(ec)) {
load = MLoadFixedSlot::New(alloc(), obj, ec.slot());
} else {
MInstruction* slots = MSlots::New(alloc(), obj);
current->add(slots);
load = MLoadSlot::New(alloc(), slots, ec.slot() - shape->numFixedSlots());
} else {
load = MLoadFixedSlot::New(alloc(), obj, ec.slot());
uint32_t slot = EnvironmentObject::nonExtensibleDynamicSlotIndex(ec);
load = MLoadSlot::New(alloc(), slots, slot);
}
current->add(load);
@ -13187,21 +13186,19 @@ AbortReasonOr<Ok> IonBuilder::jsop_setaliasedvar(EnvironmentCoordinate ec) {
MDefinition* rval = current->peek(-1);
MDefinition* obj = walkEnvironmentChain(ec.hops());
Shape* shape = EnvironmentCoordinateToEnvironmentShape(script(), pc);
if (needsPostBarrier(rval)) {
current->add(MPostWriteBarrier::New(alloc(), obj, rval));
}
MInstruction* store;
if (shape->numFixedSlots() <= ec.slot()) {
if (EnvironmentObject::nonExtensibleIsFixedSlot(ec)) {
store = MStoreFixedSlot::NewBarriered(alloc(), obj, ec.slot(), rval);
} else {
MInstruction* slots = MSlots::New(alloc(), obj);
current->add(slots);
store = MStoreSlot::NewBarriered(alloc(), slots,
ec.slot() - shape->numFixedSlots(), rval);
} else {
store = MStoreFixedSlot::NewBarriered(alloc(), obj, ec.slot(), rval);
uint32_t slot = EnvironmentObject::nonExtensibleDynamicSlotIndex(ec);
store = MStoreSlot::NewBarriered(alloc(), slots, slot, rval);
}
current->add(store);

View File

@ -34,6 +34,14 @@ inline JSObject& GetVariablesObject(JSObject* envChain) {
return *envChain;
}
inline const Value& EnvironmentObject::aliasedBinding(
EnvironmentCoordinate ec) {
MOZ_ASSERT(!IsExtensibleLexicalEnvironment(this));
MOZ_ASSERT(nonExtensibleIsFixedSlot(ec) ==
NativeObject::isFixedSlot(ec.slot()));
return getSlot(ec.slot());
}
inline void EnvironmentObject::setAliasedBinding(JSContext* cx, uint32_t slot,
const Value& v) {
MOZ_ASSERT(!isSingleton());
@ -43,6 +51,9 @@ inline void EnvironmentObject::setAliasedBinding(JSContext* cx, uint32_t slot,
inline void EnvironmentObject::setAliasedBinding(JSContext* cx,
EnvironmentCoordinate ec,
const Value& v) {
MOZ_ASSERT(!IsExtensibleLexicalEnvironment(this));
MOZ_ASSERT(nonExtensibleIsFixedSlot(ec) ==
NativeObject::isFixedSlot(ec.slot()));
setAliasedBinding(cx, ec.slot(), v);
}

View File

@ -278,10 +278,18 @@ class EnvironmentObject : public NativeObject {
initReservedSlot(ENCLOSING_ENV_SLOT, ObjectOrNullValue(enclosing));
}
// Get or set a name contained in this environment.
const Value& aliasedBinding(EnvironmentCoordinate ec) {
return getSlot(ec.slot());
static bool nonExtensibleIsFixedSlot(EnvironmentCoordinate ec) {
// For non-extensible environment objects isFixedSlot(slot) is equivalent to
// slot < MAX_FIXED_SLOTS.
return ec.slot() < MAX_FIXED_SLOTS;
}
static size_t nonExtensibleDynamicSlotIndex(EnvironmentCoordinate ec) {
MOZ_ASSERT(!nonExtensibleIsFixedSlot(ec));
return ec.slot() - MAX_FIXED_SLOTS;
}
// Get or set a name contained in this environment.
inline const Value& aliasedBinding(EnvironmentCoordinate ec);
const Value& aliasedBinding(const BindingIter& bi) {
MOZ_ASSERT(bi.location().kind() == BindingLocation::Kind::Environment);

View File

@ -395,6 +395,8 @@ const mozilla::Module* mozJSComponentLoader::LoadModule(FileLocation& aFile) {
}
}
AUTO_PROFILER_TEXT_MARKER_CAUSE("JS XPCOM", spec, JS,
profiler_get_backtrace());
AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("mozJSComponentLoader::LoadModule",
OTHER, spec);
@ -1268,8 +1270,9 @@ nsresult mozJSComponentLoader::Import(JSContext* aCx,
NS_ENSURE_SUCCESS(rv, rv);
}
AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING("mozJSComponentLoader::Import", JS,
aLocation);
AUTO_PROFILER_TEXT_MARKER_CAUSE("ChromeUtils.import", aLocation, JS,
profiler_get_backtrace());
ComponentLoaderInfo info(aLocation);
rv = info.EnsureKey();

View File

@ -590,6 +590,8 @@ nsresult mozJSSubScriptLoader::DoLoadSubScriptWithOptions(
}
NS_LossyConvertUTF16toASCII asciiUrl(url);
AUTO_PROFILER_TEXT_MARKER_CAUSE("SubScript", asciiUrl, JS,
profiler_get_backtrace());
AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING(
"mozJSSubScriptLoader::DoLoadSubScriptWithOptions", OTHER, asciiUrl);

View File

@ -19,5 +19,5 @@ origin:
license: "ISC"
# update.sh will update this value
release: "feec7e2e893c6dc4188fcb95e15dcf8c19890f46 (2019-01-23 17:15:35 +0200)"
release: "3afc3350063ce1d68bbd0287fde4f70b4803d2eb (2019-02-13 10:28:58 +1300)"

View File

@ -146,9 +146,23 @@ static std::unique_ptr<wchar_t const []> utf8_to_wstr(char const * str);
}
class wasapi_collection_notification_client;
class monitor_device_notifications;
struct cubeb {
cubeb_ops const * ops = &wasapi_ops;
cubeb_strings * device_ids;
/* Device enumerator to get notifications when the
device collection change. */
com_ptr<IMMDeviceEnumerator> device_collection_enumerator;
com_ptr<wasapi_collection_notification_client> collection_notification_client;
/* Collection changed for input (capture) devices. */
cubeb_device_collection_changed_callback input_collection_changed_callback = nullptr;
void * input_collection_changed_user_ptr = nullptr;
/* Collection changed for output (render) devices. */
cubeb_device_collection_changed_callback output_collection_changed_callback = nullptr;
void * output_collection_changed_user_ptr = nullptr;
std::unique_ptr<monitor_device_notifications> monitor_notifications;
};
class wasapi_endpoint_notification_client;
@ -270,6 +284,251 @@ struct cubeb_stream {
std::atomic<std::atomic<bool>*> emergency_bailout;
};
class monitor_device_notifications {
public:
monitor_device_notifications(cubeb * context)
: cubeb_context(context)
{
create_thread();
}
~monitor_device_notifications()
{
SetEvent(shutdown);
WaitForSingleObject(thread, 5000);
CloseHandle(thread);
CloseHandle(input_changed);
CloseHandle(output_changed);
CloseHandle(shutdown);
}
void notify(EDataFlow flow)
{
if (flow == eCapture && cubeb_context->input_collection_changed_callback) {
bool res = SetEvent(input_changed);
if (!res) {
LOG("Failed to set input changed event");
}
return;
}
if (flow == eRender && cubeb_context->output_collection_changed_callback) {
bool res = SetEvent(output_changed);
if (!res) {
LOG("Failed to set output changed event");
}
}
}
private:
static unsigned int __stdcall
thread_proc(LPVOID args)
{
XASSERT(args);
static_cast<monitor_device_notifications*>(args)
->notification_thread_loop();
return 0;
}
void notification_thread_loop()
{
struct auto_com {
auto_com() {
HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
XASSERT(SUCCEEDED(hr));
}
~auto_com() {
CoUninitialize();
}
} com;
HANDLE wait_array[3] = {
input_changed,
output_changed,
shutdown,
};
while (true) {
Sleep(200);
DWORD wait_result = WaitForMultipleObjects(ARRAY_LENGTH(wait_array),
wait_array,
FALSE,
INFINITE);
if (wait_result == WAIT_OBJECT_0) { // input changed
cubeb_context->input_collection_changed_callback(cubeb_context,
cubeb_context->input_collection_changed_user_ptr);
} else if (wait_result == WAIT_OBJECT_0 + 1) { // output changed
cubeb_context->output_collection_changed_callback(cubeb_context,
cubeb_context->output_collection_changed_user_ptr);
} else if (wait_result == WAIT_OBJECT_0 + 2) { // shutdown
break;
} else {
LOG("Unexpected result %lu", wait_result);
}
} // loop
}
void create_thread()
{
output_changed = CreateEvent(nullptr, 0, 0, nullptr);
if (!output_changed) {
LOG("Failed to create output changed event.");
return;
}
input_changed = CreateEvent(nullptr, 0, 0, nullptr);
if (!input_changed) {
LOG("Failed to create input changed event.");
return;
}
shutdown = CreateEvent(nullptr, 0, 0, nullptr);
if (!shutdown) {
LOG("Failed to create shutdown event.");
return;
}
thread = (HANDLE) _beginthreadex(nullptr,
256 * 1024,
thread_proc,
this,
STACK_SIZE_PARAM_IS_A_RESERVATION,
nullptr);
if (!thread) {
LOG("Failed to create thread.");
return;
}
}
HANDLE thread = INVALID_HANDLE_VALUE;
HANDLE output_changed = INVALID_HANDLE_VALUE;
HANDLE input_changed = INVALID_HANDLE_VALUE;
HANDLE shutdown = INVALID_HANDLE_VALUE;
cubeb * cubeb_context = nullptr;
};
class wasapi_collection_notification_client : public IMMNotificationClient
{
public:
/* The implementation of MSCOM was copied from MSDN. */
ULONG STDMETHODCALLTYPE
AddRef()
{
return InterlockedIncrement(&ref_count);
}
ULONG STDMETHODCALLTYPE
Release()
{
ULONG ulRef = InterlockedDecrement(&ref_count);
if (0 == ulRef) {
delete this;
}
return ulRef;
}
HRESULT STDMETHODCALLTYPE
QueryInterface(REFIID riid, VOID **ppvInterface)
{
if (__uuidof(IUnknown) == riid) {
AddRef();
*ppvInterface = (IUnknown*)this;
} else if (__uuidof(IMMNotificationClient) == riid) {
AddRef();
*ppvInterface = (IMMNotificationClient*)this;
} else {
*ppvInterface = NULL;
return E_NOINTERFACE;
}
return S_OK;
}
wasapi_collection_notification_client(cubeb * context)
: ref_count(1)
, cubeb_context(context)
{
XASSERT(cubeb_context);
}
virtual ~wasapi_collection_notification_client()
{ }
HRESULT STDMETHODCALLTYPE
OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR device_id)
{
LOG("collection: Audio device default changed, id = %S.", device_id);
return S_OK;
}
/* The remaining methods are not implemented, they simply log when called (if
log is enabled), for debugging. */
HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR device_id)
{
LOG("collection: Audio device added.");
return S_OK;
};
HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR device_id)
{
LOG("collection: Audio device removed.");
return S_OK;
}
HRESULT STDMETHODCALLTYPE
OnDeviceStateChanged(LPCWSTR device_id, DWORD new_state)
{
XASSERT(cubeb_context->output_collection_changed_callback ||
cubeb_context->input_collection_changed_callback);
LOG("collection: Audio device state changed, id = %S, state = %lu.", device_id, new_state);
if (new_state == DEVICE_STATE_ACTIVE ||
new_state == DEVICE_STATE_NOTPRESENT ||
new_state == DEVICE_STATE_UNPLUGGED) {
EDataFlow flow;
HRESULT hr = GetDataFlow(device_id, &flow);
if (FAILED(hr)) {
return hr;
}
cubeb_context->monitor_notifications->notify(flow);
}
return S_OK;
}
HRESULT STDMETHODCALLTYPE
OnPropertyValueChanged(LPCWSTR device_id, const PROPERTYKEY key)
{
//Audio device property value changed.
return S_OK;
}
private:
HRESULT GetDataFlow(LPCWSTR device_id, EDataFlow * flow)
{
com_ptr<IMMDevice> device;
com_ptr<IMMEndpoint> endpoint;
HRESULT hr = cubeb_context->device_collection_enumerator
->GetDevice(device_id, device.receive());
if (FAILED(hr)) {
LOG("collection: Could not get device: %lx", hr);
return hr;
}
hr = device->QueryInterface(IID_PPV_ARGS(endpoint.receive()));
if (FAILED(hr)) {
LOG("collection: Could not get endpoint: %lx", hr);
return hr;
}
return endpoint->GetDataFlow(flow);
}
/* refcount for this instance, necessary to implement MSCOM semantics. */
LONG ref_count;
cubeb * cubeb_context = nullptr;
};
class wasapi_endpoint_notification_client : public IMMNotificationClient
{
public:
@ -317,7 +576,7 @@ public:
HRESULT STDMETHODCALLTYPE
OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR device_id)
{
LOG("Audio device default changed.");
LOG("endpoint: Audio device default changed.");
/* we only support a single stream type for now. */
if (flow != eRender && role != eConsole) {
@ -326,7 +585,7 @@ public:
BOOL ok = SetEvent(reconfigure_event);
if (!ok) {
LOG("SetEvent on reconfigure_event failed: %lx", GetLastError());
LOG("endpoint: SetEvent on reconfigure_event failed: %lx", GetLastError());
}
return S_OK;
@ -336,27 +595,27 @@ public:
log is enabled), for debugging. */
HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR device_id)
{
LOG("Audio device added.");
LOG("endpoint: Audio device added.");
return S_OK;
};
HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR device_id)
{
LOG("Audio device removed.");
LOG("endpoint: Audio device removed.");
return S_OK;
}
HRESULT STDMETHODCALLTYPE
OnDeviceStateChanged(LPCWSTR device_id, DWORD new_state)
{
LOG("Audio device state changed.");
LOG("endpoint: Audio device state changed.");
return S_OK;
}
HRESULT STDMETHODCALLTYPE
OnPropertyValueChanged(LPCWSTR device_id, const PROPERTYKEY key)
{
LOG("Audio device property value changed.");
//Audio device property value changed.
return S_OK;
}
private:
@ -1052,6 +1311,46 @@ HRESULT get_endpoint(com_ptr<IMMDevice> & device, LPCWSTR devid)
return S_OK;
}
HRESULT register_collection_notification_client(cubeb * context)
{
HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(context->device_collection_enumerator.receive()));
if (FAILED(hr)) {
LOG("Could not get device enumerator: %lx", hr);
return hr;
}
context->collection_notification_client.reset(new wasapi_collection_notification_client(context));
hr = context->device_collection_enumerator->RegisterEndpointNotificationCallback(
context->collection_notification_client.get());
if (FAILED(hr)) {
LOG("Could not register endpoint notification callback: %lx", hr);
context->collection_notification_client.reset();
context->device_collection_enumerator.reset();
}
context->monitor_notifications.reset(new monitor_device_notifications(context));
return hr;
}
HRESULT unregister_collection_notification_client(cubeb * context)
{
HRESULT hr = context->device_collection_enumerator->
UnregisterEndpointNotificationCallback(context->collection_notification_client.get());
if (FAILED(hr)) {
return hr;
}
context->collection_notification_client = nullptr;
context->device_collection_enumerator = nullptr;
context->monitor_notifications.reset();
return hr;
}
HRESULT get_default_endpoint(com_ptr<IMMDevice> & device, EDataFlow direction)
{
com_ptr<IMMDeviceEnumerator> enumerator;
@ -2375,6 +2674,79 @@ wasapi_device_collection_destroy(cubeb * /*ctx*/, cubeb_device_collection * coll
return CUBEB_OK;
}
static int
wasapi_register_device_collection_changed(cubeb * context,
cubeb_device_type devtype,
cubeb_device_collection_changed_callback collection_changed_callback,
void * user_ptr)
{
if (devtype == CUBEB_DEVICE_TYPE_UNKNOWN) {
return CUBEB_ERROR_INVALID_PARAMETER;
}
if (collection_changed_callback) {
// Make sure it has been unregistered first.
XASSERT(((devtype & CUBEB_DEVICE_TYPE_INPUT) &&
!context->input_collection_changed_callback) ||
((devtype & CUBEB_DEVICE_TYPE_OUTPUT) &&
!context->output_collection_changed_callback));
// Stop the notification client. Notifications arrive on
// a separate thread. We stop them here to avoid
// synchronization issues during the update.
if (context->device_collection_enumerator.get()) {
HRESULT hr = unregister_collection_notification_client(context);
if (FAILED(hr)) {
return CUBEB_ERROR;
}
}
if (devtype & CUBEB_DEVICE_TYPE_INPUT) {
context->input_collection_changed_callback = collection_changed_callback;
context->input_collection_changed_user_ptr = user_ptr;
}
if (devtype & CUBEB_DEVICE_TYPE_OUTPUT) {
context->output_collection_changed_callback = collection_changed_callback;
context->output_collection_changed_user_ptr = user_ptr;
}
HRESULT hr = register_collection_notification_client(context);
if (FAILED(hr)) {
return CUBEB_ERROR;
}
} else {
if (!context->device_collection_enumerator.get()) {
// Already unregistered, ignore it.
return CUBEB_OK;
}
HRESULT hr = unregister_collection_notification_client(context);
if (FAILED(hr)) {
return CUBEB_ERROR;
}
if (devtype & CUBEB_DEVICE_TYPE_INPUT) {
context->input_collection_changed_callback = nullptr;
context->input_collection_changed_user_ptr = nullptr;
}
if (devtype & CUBEB_DEVICE_TYPE_OUTPUT) {
context->output_collection_changed_callback = nullptr;
context->output_collection_changed_user_ptr = nullptr;
}
// If after the updates we still have registered
// callbacks restart the notification client.
if (context->input_collection_changed_callback ||
context->output_collection_changed_callback) {
hr = register_collection_notification_client(context);
if (FAILED(hr)) {
return CUBEB_ERROR;
}
}
}
return CUBEB_OK;
}
cubeb_ops const wasapi_ops = {
/*.init =*/ wasapi_init,
/*.get_backend_id =*/ wasapi_get_backend_id,
@ -2396,6 +2768,6 @@ cubeb_ops const wasapi_ops = {
/*.stream_get_current_device =*/ NULL,
/*.stream_device_destroy =*/ NULL,
/*.stream_register_device_changed_callback =*/ NULL,
/*.register_device_collection_changed =*/ NULL
/*.register_device_collection_changed =*/ wasapi_register_device_collection_changed,
};
} // namespace anonymous

View File

@ -207,6 +207,7 @@ package org.mozilla.geckoview {
@android.support.annotation.AnyThread public final class GeckoRuntimeSettings extends org.mozilla.geckoview.RuntimeSettings {
method @android.support.annotation.NonNull public java.lang.String[] getArguments();
method public int getAutoplayDefault();
method public boolean getConsoleOutputEnabled();
method @android.support.annotation.NonNull public org.mozilla.geckoview.ContentBlocking.Settings getContentBlocking();
method @android.support.annotation.Nullable public java.lang.Class<?> getCrashHandler();
@ -222,17 +223,21 @@ package org.mozilla.geckoview {
method public boolean getUseContentProcessHint();
method public boolean getUseMaxScreenDepth();
method public boolean getWebFontsEnabled();
method @android.support.annotation.NonNull public org.mozilla.geckoview.GeckoRuntimeSettings setAutoplayDefault(int);
method @android.support.annotation.NonNull public org.mozilla.geckoview.GeckoRuntimeSettings setConsoleOutputEnabled(boolean);
method @android.support.annotation.NonNull public org.mozilla.geckoview.GeckoRuntimeSettings setJavaScriptEnabled(boolean);
method public void setLocales(@android.support.annotation.Nullable java.lang.String[]);
method @android.support.annotation.NonNull public org.mozilla.geckoview.GeckoRuntimeSettings setRemoteDebuggingEnabled(boolean);
method @android.support.annotation.NonNull public org.mozilla.geckoview.GeckoRuntimeSettings setWebFontsEnabled(boolean);
field public static final int AUTOPLAY_DEFAULT_ALLOWED = 0;
field public static final int AUTOPLAY_DEFAULT_BLOCKED = 1;
field public static final android.os.Parcelable.Creator<org.mozilla.geckoview.GeckoRuntimeSettings> CREATOR;
}
@android.support.annotation.AnyThread public static final class GeckoRuntimeSettings.Builder extends org.mozilla.geckoview.RuntimeSettings.Builder<Settings extends org.mozilla.geckoview.RuntimeSettings> {
ctor public Builder();
method @android.support.annotation.NonNull public org.mozilla.geckoview.GeckoRuntimeSettings.Builder arguments(@android.support.annotation.NonNull java.lang.String[]);
method @android.support.annotation.NonNull public org.mozilla.geckoview.GeckoRuntimeSettings.Builder autoplayDefault(int);
method @android.support.annotation.NonNull public org.mozilla.geckoview.GeckoRuntimeSettings.Builder consoleOutput(boolean);
method @android.support.annotation.NonNull public org.mozilla.geckoview.GeckoRuntimeSettings.Builder contentBlocking(@android.support.annotation.NonNull org.mozilla.geckoview.ContentBlocking.Settings);
method @android.support.annotation.NonNull public org.mozilla.geckoview.GeckoRuntimeSettings.Builder crashHandler(java.lang.Class<?>);
@ -416,7 +421,6 @@ package org.mozilla.geckoview {
method @android.support.annotation.UiThread public void onAndroidPermissionsRequest(@android.support.annotation.NonNull org.mozilla.geckoview.GeckoSession, @android.support.annotation.Nullable java.lang.String[], @android.support.annotation.NonNull org.mozilla.geckoview.GeckoSession.PermissionDelegate.Callback);
method @android.support.annotation.UiThread public void onContentPermissionRequest(@android.support.annotation.NonNull org.mozilla.geckoview.GeckoSession, @android.support.annotation.Nullable java.lang.String, int, @android.support.annotation.NonNull org.mozilla.geckoview.GeckoSession.PermissionDelegate.Callback);
method @android.support.annotation.UiThread public void onMediaPermissionRequest(@android.support.annotation.NonNull org.mozilla.geckoview.GeckoSession, @android.support.annotation.NonNull java.lang.String, @android.support.annotation.Nullable org.mozilla.geckoview.GeckoSession.PermissionDelegate.MediaSource[], @android.support.annotation.Nullable org.mozilla.geckoview.GeckoSession.PermissionDelegate.MediaSource[], @android.support.annotation.NonNull org.mozilla.geckoview.GeckoSession.PermissionDelegate.MediaCallback);
field public static final int PERMISSION_AUTOPLAY_MEDIA = 2;
field public static final int PERMISSION_DESKTOP_NOTIFICATION = 1;
field public static final int PERMISSION_GEOLOCATION = 0;
}

View File

@ -0,0 +1,8 @@
<html>
<head><title>WEBM Video</title></head>
<body>
<video preload autoplay>
<source src="videos/gizmo.webm"></source>
</video>
</body>
</html>

View File

@ -51,6 +51,7 @@ open class BaseSessionTest(noErrorCollector: Boolean = false) {
const val VIEWPORT_PATH = "/assets/www/viewport.html"
const val IFRAME_REDIRECT_LOCAL = "/assets/www/iframe_redirect_local.html"
const val IFRAME_REDIRECT_AUTOMATION = "/assets/www/iframe_redirect_automation.html"
const val AUTOPLAY_PATH = "/assets/www/autoplay.html"
}
@get:Rule val sessionRule = GeckoSessionTestRule()

View File

@ -18,6 +18,8 @@ import org.junit.Assume.assumeThat
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.geckoview.GeckoRuntimeSettings
import org.mozilla.geckoview.test.rule.GeckoSessionTestRule
@RunWith(AndroidJUnit4::class)
@TimeoutMillis(45000)
@ -36,12 +38,15 @@ class MediaElementTest : BaseSessionTest() {
override fun onError(mediaElement: MediaElement, errorCode: Int) {}
}
private fun setupPrefsAndDelegates(path: String) {
private fun setupPrefs() {
sessionRule.setPrefsUntilTestEnd(mapOf(
"media.autoplay.enabled.user-gestures-needed" to false,
"media.autoplay.default" to 0,
"full-screen-api.allow-trusted-requests-only" to false))
}
private fun setupDelegate(path: String) {
sessionRule.session.loadTestPath(path)
sessionRule.waitUntilCalled(object : Callbacks.MediaDelegate {
@AssertCalled
@ -55,8 +60,12 @@ class MediaElementTest : BaseSessionTest() {
})
}
private fun waitUntilVideoReady(path: String, waitState: Int = MediaElement.MEDIA_READY_STATE_HAVE_ENOUGH_DATA): MediaElement {
setupPrefsAndDelegates(path)
private fun setupPrefsAndDelegates(path: String) {
setupPrefs()
setupDelegate(path)
}
private fun waitUntilState(waitState: Int = MediaElement.MEDIA_READY_STATE_HAVE_ENOUGH_DATA): MediaElement {
var ready = false
var result: MediaElement? = null
while (!ready) {
@ -76,6 +85,16 @@ class MediaElementTest : BaseSessionTest() {
return result!!
}
private fun waitUntilVideoReady(path: String, waitState: Int = MediaElement.MEDIA_READY_STATE_HAVE_ENOUGH_DATA): MediaElement {
setupPrefsAndDelegates(path)
return waitUntilState(waitState)
}
private fun waitUntilVideoReadyNoPrefs(path: String, waitState: Int = MediaElement.MEDIA_READY_STATE_HAVE_ENOUGH_DATA): MediaElement {
setupDelegate(path)
return waitUntilState(waitState)
}
private fun waitForPlaybackStateChange(waitState: Int, lambda: (element: MediaElement, state: Int) -> Unit = { _: MediaElement, _: Int -> }) {
var waiting = true
while (waiting) {
@ -409,4 +428,54 @@ class MediaElementTest : BaseSessionTest() {
}
})
}
@WithDevToolsAPI
@Test fun autoplayBlocked() {
sessionRule.runtime.settings.autoplayDefault = GeckoRuntimeSettings.AUTOPLAY_DEFAULT_BLOCKED
val media = waitUntilVideoReadyNoPrefs(AUTOPLAY_PATH)
val promise = sessionRule.evaluateJS(mainSession, "$('video').play()").asJSPromise()
var exceptionCaught = false
try {
val result = promise.value as Boolean
assertThat("Promise should not resolve", result, equalTo(false))
} catch (e: GeckoSessionTestRule.RejectedPromiseException) {
exceptionCaught = true;
}
assertThat("video.play() failed with exception", exceptionCaught, equalTo(true))
media.play()
/*
// Fails due to bug 1524092
waitForPlaybackStateChange(MediaElement.MEDIA_STATE_PLAY)
waitForPlaybackStateChange(MediaElement.MEDIA_STATE_PLAYING)
*/
}
@WithDevToolsAPI
@Test fun autoplayAllowed() {
sessionRule.runtime.settings.autoplayDefault = GeckoRuntimeSettings.AUTOPLAY_DEFAULT_ALLOWED
val media = waitUntilVideoReadyNoPrefs(VIDEO_WEBM_PATH)
val promise = sessionRule.evaluateJS(mainSession, "$('video').play()").asJSPromise()
var exceptionCaught = false
try {
promise.value
} catch (e: GeckoSessionTestRule.RejectedPromiseException) {
exceptionCaught = true;
}
assertThat("video.play() did not fail", exceptionCaught, equalTo(false))
waitForPlaybackStateChange(MediaElement.MEDIA_STATE_PLAY)
waitForPlaybackStateChange(MediaElement.MEDIA_STATE_PLAYING)
media.pause()
media.play()
waitForPlaybackStateChange(MediaElement.MEDIA_STATE_PLAY)
waitForPlaybackStateChange(MediaElement.MEDIA_STATE_PLAYING)
// Restore default runtime settings
sessionRule.runtime.settings.autoplayDefault = GeckoRuntimeSettings.AUTOPLAY_DEFAULT_BLOCKED
}
}

View File

@ -168,7 +168,8 @@ public class TestRunnerActivity extends Activity {
runtimeSettingsBuilder
.arguments(new String[] { "-purgecaches" })
.displayDpiOverride(160)
.displayDensityOverride(1.0f);
.displayDensityOverride(1.0f)
.autoplayDefault(GeckoRuntimeSettings.AUTOPLAY_DEFAULT_ALLOWED);
final Bundle extras = intent.getExtras();
if (extras != null) {

View File

@ -243,6 +243,17 @@ public final class GeckoRuntimeSettings extends RuntimeSettings {
getSettings().mContentBlocking = cb;
return this;
}
/**
* Sets video autoplay mode.
* May be either {@link GeckoRuntimeSettings#AUTOPLAY_DEFAULT_ALLOWED} or {@link GeckoRuntimeSettings#AUTOPLAY_DEFAULT_BLOCKED}
* @param autoplay Allows or blocks video autoplay.
* @return This Builder instance.
*/
public @NonNull Builder autoplayDefault(@AutoplayDefault int autoplay) {
getSettings().mAutoplayDefault.set(autoplay);
return this;
}
}
private GeckoRuntime mRuntime;
@ -264,6 +275,8 @@ public final class GeckoRuntimeSettings extends RuntimeSettings {
"browser.display.use_document_fonts", 1);
/* package */ final Pref<Boolean> mConsoleOutput = new Pref<Boolean>(
"geckoview.console.enabled", false);
/* package */ final Pref<Integer> mAutoplayDefault = new Pref<Integer>(
"media.autoplay.default", AUTOPLAY_DEFAULT_BLOCKED);
/* package */ boolean mDebugPause;
/* package */ boolean mUseMaxScreenDepth;
@ -525,6 +538,41 @@ public final class GeckoRuntimeSettings extends RuntimeSettings {
return mConsoleOutput.get();
}
// Sync values with dom/media/nsIAutoplay.idl.
@Retention(RetentionPolicy.SOURCE)
@IntDef({ AUTOPLAY_DEFAULT_ALLOWED, AUTOPLAY_DEFAULT_BLOCKED })
/* package */ @interface AutoplayDefault {}
/**
* Autoplay video is allowed.
*/
public static final int AUTOPLAY_DEFAULT_ALLOWED = 0;
/**
* Autoplay video is blocked.
*/
public static final int AUTOPLAY_DEFAULT_BLOCKED = 1;
/**
* Sets video autoplay mode.
* May be either {@link GeckoRuntimeSettings#AUTOPLAY_DEFAULT_ALLOWED} or {@link GeckoRuntimeSettings#AUTOPLAY_DEFAULT_BLOCKED}
* @param autoplay Allows or blocks video autoplay.
* @return This GeckoRuntimeSettings instance.
*/
public @NonNull GeckoRuntimeSettings setAutoplayDefault(@AutoplayDefault int autoplay) {
mAutoplayDefault.commit(autoplay);
return this;
}
/**
* Gets the current video autoplay mode.
* @return The current video autoplay mode. Will be either {@link GeckoRuntimeSettings#AUTOPLAY_DEFAULT_ALLOWED}
* or {@link GeckoRuntimeSettings#AUTOPLAY_DEFAULT_BLOCKED}
*/
public @AutoplayDefault int getAutoplayDefault() {
return mAutoplayDefault.get();
}
@Override // Parcelable
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);

View File

@ -652,8 +652,6 @@ public class GeckoSession implements Parcelable {
type = PermissionDelegate.PERMISSION_GEOLOCATION;
} else if ("desktop-notification".equals(typeString)) {
type = PermissionDelegate.PERMISSION_DESKTOP_NOTIFICATION;
} else if ("autoplay-media".equals(typeString)) {
type = PermissionDelegate.PERMISSION_AUTOPLAY_MEDIA;
} else {
throw new IllegalArgumentException("Unknown permission request: " + typeString);
}
@ -3811,7 +3809,7 @@ public class GeckoSession implements Parcelable {
**/
public interface PermissionDelegate {
@Retention(RetentionPolicy.SOURCE)
@IntDef({PERMISSION_GEOLOCATION, PERMISSION_DESKTOP_NOTIFICATION, PERMISSION_AUTOPLAY_MEDIA})
@IntDef({PERMISSION_GEOLOCATION, PERMISSION_DESKTOP_NOTIFICATION})
/* package */ @interface Permission {}
/**
@ -3826,11 +3824,6 @@ public class GeckoSession implements Parcelable {
*/
public static final int PERMISSION_DESKTOP_NOTIFICATION = 1;
/**
* Permission for allowing auto-playing media.
*/
public static final int PERMISSION_AUTOPLAY_MEDIA = 2;
/**
* Callback interface for notifying the result of a permission request.
*/
@ -3874,7 +3867,6 @@ public class GeckoSession implements Parcelable {
* @param type The type of the requested permission; possible values are,
* PERMISSION_GEOLOCATION
* PERMISSION_DESKTOP_NOTIFICATION
* PERMISSION_AUTOPLAY_MEDIA
* @param callback Callback interface.
*/
@UiThread

View File

@ -9,6 +9,10 @@ exclude: true
<h1> GeckoView API Changelog. </h1>
## v67
- Updated video autoplay API to reflect changes in Gecko. Instead of being a per-video
permission in the PermissionDelegate, it is a runtime setting that either allows or
blocks autoplay videos.
- Change `ContentBlocking.AT_ALL` and `ContentBlocking.SB_ALL` values to mirror
the actual constants they encompass.
@ -128,4 +132,4 @@ exclude: true
[65.24]: ../CrashReporter.html#sendCrashReport-android.content.Context-android.os.Bundle-java.lang.String-
[65.25]: ../GeckoResult.html
[api-version]: 1a31e792d4802cb18219b7ac5281a63a89a1df75
[api-version]: a42a6f4481dd690ac46f14d8e692785bb00e8b04

View File

@ -619,8 +619,6 @@ public class GeckoViewActivity extends AppCompatActivity {
resId = R.string.request_geolocation;
} else if (PERMISSION_DESKTOP_NOTIFICATION == type) {
resId = R.string.request_notification;
} else if (PERMISSION_AUTOPLAY_MEDIA == type) {
resId = R.string.request_autoplay;
} else {
Log.w(LOGTAG, "Unknown permission: " + type);
callback.reject();

View File

@ -5,7 +5,6 @@
<string name="username">Username</string>
<string name="password">Password</string>
<string name="clear_field">Clear</string>
<string name="request_autoplay">Allow media to play on "%1$s"?</string>
<string name="request_geolocation">Share location with "%1$s"?</string>
<string name="request_notification">Allow notifications for "%1$s"?</string>
<string name="request_video">Share video with "%1$s"</string>

View File

@ -1171,4 +1171,4 @@ static const TransportSecurityPreload kPublicKeyPinningPreloadList[] = {
static const int32_t kUnknownId = -1;
static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1558354645894000);
static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1558613934969000);

File diff suppressed because it is too large Load Diff

View File

@ -1,14 +1,2 @@
[RTCPeerConnection-connectionState.html]
expected: TIMEOUT
[Initial connectionState should be new]
expected: FAIL
[connection with one data channel should eventually have connected connection state]
expected: TIMEOUT
[connection with one data channel should eventually have transports in connected state]
expected: TIMEOUT
[Closing the connection should set connectionState to closed]
expected: FAIL
disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1265827

View File

@ -1,11 +1,5 @@
[RTCPeerConnection-remote-track-mute.https.html]
expected: TIMEOUT
[Changing transceiver direction to 'sendrecv' unmutes the remote track]
expected: NOTRUN
[pc.close() mutes remote tracks]
expected: NOTRUN
[Changing transceiver direction to 'inactive' mutes the remote track]
expected: TIMEOUT

View File

@ -1,4 +0,0 @@
[RTCPeerConnection-setDescription-transceiver.html]
[setRemoteDescription should stop the transceiver if its corresponding m section is rejected]
expected: FAIL

View File

@ -1,5 +1,4 @@
[RTCPeerConnection-setRemoteDescription-tracks.https.html]
expected: TIMEOUT
[addTrack() with a track and no stream makes ontrack fire with a track and no stream.]
expected: FAIL
@ -15,18 +14,6 @@
[addTrack() with a track and a stream makes ontrack fire with a track and a stream.]
expected: FAIL
[removeTrack() causes onremovetrack and the track to be removed from the stream.]
expected: TIMEOUT
[removeTrack() causes onmute and the track to be muted.]
[removeTrack() twice is safe.]
expected: FAIL
[removeTrack() makes track.onmute fire and the track to be muted.]
expected: TIMEOUT
[track.onmute fires before setRemoteDescription resolves.]
expected: NOTRUN
[removeTrack() twice is safe.]
expected: NOTRUN

View File

@ -1,121 +1,18 @@
[RTCPeerConnection-transceivers.https.html]
disabled: Bug 1515043
expected:
if e10s and (os == "linux"): TIMEOUT
[addTransceiver(track, init): initialize sendEncodings[0\].active to false]
expected:
if e10s and (os == "linux"): NOTRUN
FAIL
[addTrack(0 streams): ontrack fires with no stream]
expected:
if e10s and (os == "linux"): NOTRUN
FAIL
[addTrack(2 streams): ontrack fires with corresponding two streams]
expected:
if e10s and (os == "linux"): NOTRUN
FAIL
[setRemoteDescription(offer): ontrack's track.id is the same as track.id]
expected:
if not debug and e10s and (os == "linux") and (processor == "x86"): NOTRUN
if not debug and webrender and (os == "linux"): NOTRUN
FAIL
expected: FAIL
[addTransceiver(track, init): initialize sendEncodings\[0\].active to false]
expected: FAIL
[addTransceiver(2 streams): ontrack fires with corresponding two streams]
expected:
if e10s and (os == "linux"): NOTRUN
FAIL
expected: FAIL
[addTrack(0 streams): ontrack fires with no stream]
expected: FAIL
[addTrack(2 streams): ontrack fires with corresponding two streams]
expected: FAIL
[Closing the PC stops the transceivers]
expected:
if e10s and (os == "linux"): NOTRUN
FAIL
[setLocalDescription(answer): transceiver.currentDirection is recvonly]
expected:
if e10s and (os == "linux"): NOTRUN
[Can setup two-way call using a single transceiver]
expected:
if e10s and (os == "linux"): NOTRUN
[addTransceiver('video'): transceiver.receiver.track.kind == 'video']
expected:
if e10s and (os == "linux"): NOTRUN
[addTransceiver('audio'): transceiver.stopped is false]
expected:
if e10s and (os == "linux"): NOTRUN
[setLocalDescription(answer): transceiver.currentDirection is sendonly]
expected:
if e10s and (os == "linux"): NOTRUN
[Changing transceiver direction to 'sendrecv' makes ontrack fire]
expected:
if e10s and (os == "linux"): NOTRUN
[addTrack(1 stream): ontrack fires with corresponding stream]
expected:
if e10s and (os == "linux"): NOTRUN
[addTransceiver(track, init): initialize direction to inactive]
expected:
if e10s and (os == "linux"): NOTRUN
[addTransceiver(0 streams): ontrack fires with no stream]
expected:
if e10s and (os == "linux"): NOTRUN
[addTransceiver(1 stream): ontrack fires with corresponding stream]
expected:
if e10s and (os == "linux"): NOTRUN
[addTransceiver does not reuse reusable transceivers]
expected:
if e10s and (os == "linux"): NOTRUN
[addTransceiver(track): "transceiver == {sender,receiver}"]
expected:
if e10s and (os == "linux"): NOTRUN
[addTransceiver('audio'): transceiver.sender.track == null]
expected:
if e10s and (os == "linux"): NOTRUN
[addTransceiver('audio'): creates a transceiver with direction sendrecv]
expected:
if e10s and (os == "linux"): NOTRUN
[addTransceiver('audio'): transceiver.currentDirection is null]
expected:
if e10s and (os == "linux"): NOTRUN
[addTransceiver('audio'): transceiver.receiver.track.kind == 'audio']
expected:
if e10s and (os == "linux"): NOTRUN
[setRemoteDescription(offer): transceiver.stopped is false]
expected:
if e10s and (os == "linux"): NOTRUN
[addTransceiver(track): creates a transceiver for the track]
expected:
if e10s and (os == "linux"): NOTRUN
[addTrack reuses reusable transceivers]
expected:
if e10s and (os == "linux"): NOTRUN
[setRemoteDescription(offer): transceiver.direction is recvonly]
expected:
if e10s and (os == "linux") and (processor == "x86"): NOTRUN
if webrender and (os == "linux"): NOTRUN
if not debug and not webrender and e10s and (os == "linux") and (processor == "x86_64"): NOTRUN
[setRemoteDescription(offer): transceiver.currentDirection is null]
expected:
if e10s and (os == "linux"): NOTRUN
expected: FAIL

View File

@ -1,62 +0,0 @@
[RTCRtpTransceiver.https.html]
disabled: Bug 1515043
expected:
if e10s and (os == "linux"): TIMEOUT
[checkStop]
expected:
if e10s and (os == "linux"): NOTRUN
FAIL
[checkMsectionReuse]
expected:
if e10s and (os == "linux"): NOTRUN
[checkRemoteRollback]
expected:
if e10s and (os == "linux"): NOTRUN
[checkAddTransceiverNoTrackDoesntPair]
expected:
if not debug and e10s and (os == "linux") and (processor == "x86"): NOTRUN
[checkStopAfterClose]
expected:
if e10s and (os == "linux"): NOTRUN
[checkStopAfterCreateOffer]
expected:
if e10s and (os == "linux"): NOTRUN
[checkLocalRollback]
expected:
if e10s and (os == "linux"): NOTRUN
[checkSendrecvWithTracklessStream]
expected:
if not debug and e10s and (os == "linux") and (processor == "x86"): NOTRUN
[checkStopAfterCreateAnswer]
expected:
if e10s and (os == "linux"): NOTRUN
[checkStopAfterSetLocalAnswer]
expected:
if e10s and (os == "linux"): NOTRUN
[checkStopAfterSetRemoteOffer]
expected:
if e10s and (os == "linux"): NOTRUN
[checkSendrecvWithNoSendTrack]
expected:
if not debug and webrender and (os == "linux"): NOTRUN
if not debug and e10s and (os == "linux") and (processor == "x86"): NOTRUN
[checkStopAfterSetLocalOffer]
expected:
if e10s and (os == "linux"): NOTRUN
[checkRollbackAndSetRemoteOfferWithDifferentType]
expected:
if e10s and (os == "linux"): NOTRUN

View File

@ -552,7 +552,7 @@ function createPeerConnectionWithCleanup(t) {
async function createTrackAndStreamWithCleanup(t, kind = 'audio') {
let constraints = {};
constraints[kind] = true;
const stream = await navigator.mediaDevices.getUserMedia(constraints);
const stream = await getNoiseStream(constraints);
const [track] = stream.getTracks();
t.add_cleanup(() => track.stop());
return [track, stream];

View File

@ -50,9 +50,13 @@ promise_test(async t => {
exchangeIceCandidates(pc1, pc2);
const e = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
// Need to wait for the initial unmute event before renegotiating, otherwise
// there will be no transition from unmuted->muted.
const muteWatcher = new EventWatcher(t, e.track, ['mute', 'unmute']);
const unmutePromise = muteWatcher.wait_for('unmute');
await exchangeAnswer(pc1, pc2);
await unmutePromise;
const muteWatcher = new EventWatcher(t, e.track, ['mute']);
const mutePromise = muteWatcher.wait_for('mute');
localTransceiver.direction = 'inactive';
await exchangeOfferAnswer(pc1, pc2);
@ -88,8 +92,13 @@ promise_test(async t => {
exchangeIceCandidates(pc1, pc2);
const e = await exchangeOfferAndListenToOntrack(t, pc1, pc2);
// Need to wait for the initial unmute event before closing, otherwise
// there will be no transition from unmuted->muted.
const muteWatcher = new EventWatcher(t, e.track, ['mute', 'unmute']);
const unmutePromise = muteWatcher.wait_for('unmute');
await exchangeAnswer(pc1, pc2);
const muteWatcher = new EventWatcher(t, e.track, ['mute']);
await unmutePromise;
const mutePromise = muteWatcher.wait_for('mute');
pc2.close();
await mutePromise;

View File

@ -61,6 +61,7 @@
promise_test(async t => {
const pc = new RTCPeerConnection();
t.add_cleanup(() => pc.close());
const stream = await navigator.mediaDevices.getUserMedia({audio: true});
t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
const [track] = stream.getTracks();
@ -92,6 +93,7 @@
*/
promise_test(async t => {
const pc = new RTCPeerConnection();
t.add_cleanup(() => pc.close());
const stream = await navigator.mediaDevices.getUserMedia({audio: true});
t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
const [track] = stream.getTracks();
@ -99,6 +101,7 @@
const { sender } = transceiver;
const pc2 = new RTCPeerConnection();
t.add_cleanup(() => pc2.close());
assert_throws('InvalidAccessError', () => pc2.removeTrack(sender));
}, 'addTransceiver - Calling removeTrack on different connection should throw InvalidAccessError');
@ -112,6 +115,7 @@
const sender = pc.addTrack(track, stream);
const pc2 = new RTCPeerConnection();
t.add_cleanup(() => pc2.close());
assert_throws('InvalidAccessError', () => pc2.removeTrack(sender));
}, 'addTrack - Calling removeTrack on different connection should throw InvalidAccessError')
@ -121,6 +125,7 @@
*/
promise_test(async t => {
const pc = new RTCPeerConnection();
t.add_cleanup(() => pc.close());
const stream = await navigator.mediaDevices.getUserMedia({audio: true});
t.add_cleanup(() => stream.getTracks().forEach(track => track.stop()));
const [track] = stream.getTracks();

View File

@ -142,6 +142,7 @@
if (!remoteStreams.includes(e.streams[0]))
remoteStreams.push(e.streams[0]);
};
exchangeIceCandidates(caller, callee);
await exchangeOfferAnswer(caller, callee);
assert_equals(remoteStreams.length, 1, 'One remote stream created.');
assert_equals(remoteStreams[0].getTracks()[0].id,
@ -176,6 +177,7 @@
if (!remoteStreams.includes(e.streams[0]))
remoteStreams.push(e.streams[0]);
};
exchangeIceCandidates(caller, callee);
await exchangeOfferAnswer(caller, callee);
assert_equals(remoteStreams.length, 1, 'One remote stream created.');
const onaddtrackPromise =
@ -247,6 +249,7 @@
t.add_cleanup(() => localStream.getTracks().forEach(track => track.stop()));
const sender = caller.addTrack(localStream.getTracks()[0], localStream);
const ontrackPromise = addEventListenerPromise(t, callee, 'track');
exchangeIceCandidates(caller, callee);
await exchangeOfferAnswer(caller, callee);
await ontrackPromise;
assert_equals(callee.getReceivers().length, 1, 'One receiver created.');
@ -269,6 +272,7 @@
assert_equals(e.streams.length, 1);
return e.streams[0];
});
exchangeIceCandidates(caller, callee);
await exchangeOfferAnswer(caller, callee);
const remoteStream = await ontrackPromise;
const remoteTrack = remoteStream.getTracks()[0];
@ -297,6 +301,7 @@
assert_equals(e.streams.length, 1);
return e.streams[0];
});
exchangeIceCandidates(caller, callee);
await exchangeOfferAnswer(caller, callee);
const remoteStream = await ontrackPromise;
const remoteTrack = remoteStream.getTracks()[0];
@ -320,13 +325,19 @@
await getNoiseStream({audio: true});
t.add_cleanup(() => localStream.getTracks().forEach(track => track.stop()));
const sender = caller.addTrack(localStream.getTracks()[0], localStream);
const ontrackPromise = addEventListenerPromise(t, callee, 'track', e => {
assert_equals(e.streams.length, 1);
return e.streams[0];
exchangeIceCandidates(caller, callee);
const e = await exchangeOfferAndListenToOntrack(t, caller, callee);
const remoteTrack = e.track;
// Need to wait for unmute, otherwise there's no event for the transition
// back to muted.
const onunmutePromise =
addEventListenerPromise(t, remoteTrack, 'unmute', () => {
assert_false(remoteTrack.muted);
});
await exchangeOfferAnswer(caller, callee);
const remoteStream = await ontrackPromise;
const remoteTrack = remoteStream.getTracks()[0];
await exchangeAnswer(caller, callee);
await onunmutePromise;
const onmutePromise =
addEventListenerPromise(t, remoteTrack, 'mute', () => {
assert_true(remoteTrack.muted);
@ -350,9 +361,19 @@
assert_equals(e.streams.length, 1);
return e.streams[0];
});
await exchangeOfferAnswer(caller, callee);
const remoteStream = await ontrackPromise;
const remoteTrack = remoteStream.getTracks()[0];
exchangeIceCandidates(caller, callee);
const e = await exchangeOfferAndListenToOntrack(t, caller, callee);
const remoteTrack = e.track;
// Need to wait for unmute, otherwise there's no event for the transition
// back to muted.
const onunmutePromise =
addEventListenerPromise(t, remoteTrack, 'unmute', () => {
assert_false(remoteTrack.muted);
});
await exchangeAnswer(caller, callee);
await onunmutePromise;
const onmutePromise =
addEventListenerPromise(t, remoteTrack, 'mute', () => {
eventSequence += 'track.onmute;';

View File

@ -25,6 +25,7 @@
let track;
return getUserMediaTracksAndStreams(1)
.then(t.step_func(([tracks, streams]) => {
t.add_cleanup(() => tracks.forEach(track => track.stop()));
track = tracks[0];
pc.addTrack(track, streams[0]);
return pc.getStats();
@ -50,6 +51,7 @@
let stream;
return getUserMediaTracksAndStreams(1)
.then(t.step_func(([tracks, streams]) => {
t.add_cleanup(() => tracks.forEach(track => track.stop()));
let track = tracks[0];
stream = streams[0];
pc.addTrack(track, stream);
@ -72,6 +74,7 @@
let track;
return getUserMediaTracksAndStreams(1)
.then(t.step_func(([tracks, streams]) => {
t.add_cleanup(() => tracks.forEach(track => track.stop()));
track = tracks[0];
pc.addTrack(track, streams[0]);
return pc.createOffer();
@ -99,6 +102,7 @@
let stream;
return getUserMediaTracksAndStreams(1)
.then(t.step_func(([tracks, streams]) => {
t.add_cleanup(() => tracks.forEach(track => track.stop()));
let track = tracks[0];
stream = streams[0];
pc.addTrack(track, stream);
@ -128,6 +132,7 @@
let stream;
return getUserMediaTracksAndStreams(1)
.then(t.step_func(([tracks, streams]) => {
t.add_cleanup(() => tracks.forEach(track => track.stop()));
track = tracks[0];
stream = streams[0];
pc.addTrack(track, stream);
@ -162,6 +167,7 @@
let stream;
return getUserMediaTracksAndStreams(1)
.then(t.step_func(([tracks, streams]) => {
t.add_cleanup(() => tracks.forEach(track => track.stop()));
track = tracks[0];
stream = streams[0];
stream.addTrack(track);
@ -199,6 +205,7 @@
let sendingTrack;
return getUserMediaTracksAndStreams(1)
.then(t.step_func(([tracks, streams]) => {
t.add_cleanup(() => tracks.forEach(track => track.stop()));
sendingTrack = tracks[0];
caller.addTrack(sendingTrack, streams[0]);
return doSignalingHandshake(caller, callee);
@ -233,6 +240,7 @@
};
return getUserMediaTracksAndStreams(1)
.then(t.step_func(([tracks, streams]) => {
t.add_cleanup(() => tracks.forEach(track => track.stop()));
caller.addTrack(tracks[0], streams[0]);
return doSignalingHandshake(caller, callee);
}))
@ -265,6 +273,7 @@
let sender;
return getUserMediaTracksAndStreams(2)
.then(t.step_func(([tracks, streams]) => {
t.add_cleanup(() => tracks.forEach(track => track.stop()));
sendingTrack1 = tracks[0];
sendingTrack2 = tracks[1];
sender = caller.addTrack(sendingTrack1, streams[0]);
@ -294,6 +303,7 @@
let sender;
return getUserMediaTracksAndStreams(2)
.then(t.step_func(([tracks, streams]) => {
t.add_cleanup(() => tracks.forEach(track => track.stop()));
sendingTrack1 = tracks[0];
sendingTrack2 = tracks[1];
sender = caller.addTrack(sendingTrack1, streams[0]);
@ -331,6 +341,7 @@
let sender;
return getUserMediaTracksAndStreams(2)
.then(t.step_func(([tracks, streams]) => {
t.add_cleanup(() => tracks.forEach(track => track.stop()));
sendingTrack1 = tracks[0];
sendingTrack2 = tracks[1];
sender = caller.addTrack(sendingTrack1, streams[0]);
@ -367,6 +378,7 @@
let sender;
return getUserMediaTracksAndStreams(2)
.then(t.step_func(([tracks, streams]) => {
t.add_cleanup(() => tracks.forEach(track => track.stop()));
sendingTrack1 = tracks[0];
sendingTrack2 = tracks[1];
sender = caller.addTrack(sendingTrack1, streams[0]);
@ -400,6 +412,7 @@
const callee = new RTCPeerConnection();
t.add_cleanup(() => callee.close());
let [tracks, streams] = await getUserMediaTracksAndStreams(2);
t.add_cleanup(() => tracks.forEach(track => track.stop()));
let sender = caller.addTrack(tracks[0], streams[0]);
callee.addTrack(tracks[1], streams[1]);
exchangeIceCandidates(caller, callee);
@ -448,6 +461,7 @@
const callee = new RTCPeerConnection();
t.add_cleanup(() => callee.close());
let [tracks, streams] = await getUserMediaTracksAndStreams(2);
t.add_cleanup(() => tracks.forEach(track => track.stop()));
let sender = caller.addTrack(tracks[0], streams[0]);
callee.addTrack(tracks[1], streams[1]);
exchangeIceCandidates(caller, callee);
@ -496,6 +510,7 @@
const callee = new RTCPeerConnection();
t.add_cleanup(() => callee.close());
let [tracks, streams] = await getUserMediaTracksAndStreams(2);
t.add_cleanup(() => tracks.forEach(track => track.stop()));
let sender = caller.addTrack(tracks[0], streams[0]);
callee.addTrack(tracks[1], streams[1]);
exchangeIceCandidates(caller, callee);
@ -527,6 +542,7 @@
const callee = new RTCPeerConnection();
t.add_cleanup(() => callee.close());
let [tracks, streams] = await getUserMediaTracksAndStreams(2);
t.add_cleanup(() => tracks.forEach(track => track.stop()));
let sender = caller.addTrack(tracks[0], streams[0]);
callee.addTrack(tracks[1], streams[1]);
exchangeIceCandidates(caller, callee);
@ -557,6 +573,7 @@
const pc = new RTCPeerConnection();
t.add_cleanup(() => pc.close());
let [tracks, streams] = await getUserMediaTracksAndStreams(1);
t.add_cleanup(() => tracks.forEach(track => track.stop()));
await promise_rejects(t, 'InvalidAccessError', pc.getStats(tracks[0]));
}, 'RTCPeerConnection.getStats(track) throws InvalidAccessError when there ' +
'are zero senders or receivers for the track');
@ -565,6 +582,7 @@
const pc = new RTCPeerConnection();
t.add_cleanup(() => pc.close());
let [tracks, streams] = await getUserMediaTracksAndStreams(2);
t.add_cleanup(() => tracks.forEach(track => track.stop()));
let sender1 = pc.addTrack(tracks[0], streams[0]);
let sender2 = pc.addTrack(tracks[1], streams[1]);
await sender2.replaceTrack(sender1.track);

View File

@ -1,4 +1,5 @@
<!doctype html>
<meta name="timeout" content="long"/>
<meta charset=utf-8>
<title>RTCPeerConnection-transceivers.https.html</title>
<script src="/resources/testharness.js"></script>

View File

@ -215,7 +215,7 @@
const pc = new RTCPeerConnection();
t.add_cleanup(() => pc.close());
const stream = await navigator.mediaDevices.getUserMedia({audio: true, video: true});
const stream = await getNoiseStream({audio: true, video: true});
t.add_cleanup(() => stopTracks(stream));
const audio = stream.getAudioTracks()[0];
const video = stream.getVideoTracks()[0];
@ -248,7 +248,7 @@
const pc = new RTCPeerConnection();
t.add_cleanup(() => pc.close());
const stream = await navigator.mediaDevices.getUserMedia({audio: true, video: true});
const stream = await getNoiseStream({audio: true, video: true});
t.add_cleanup(() => stopTracks(stream));
const audio = stream.getAudioTracks()[0];
const video = stream.getVideoTracks()[0];
@ -311,7 +311,7 @@
t.add_cleanup(() => pc1.close());
t.add_cleanup(() => pc2.close());
const stream = await navigator.mediaDevices.getUserMedia({audio: true});
const stream = await getNoiseStream({audio: true});
t.add_cleanup(() => stopTracks(stream));
const track = stream.getAudioTracks()[0];
pc1.addTransceiver(track, {streams: [stream]});
@ -346,7 +346,7 @@
t.add_cleanup(() => pc1.close());
t.add_cleanup(() => pc2.close());
const stream = await navigator.mediaDevices.getUserMedia({audio: true});
const stream = await getNoiseStream({audio: true});
t.add_cleanup(() => stopTracks(stream));
const track = stream.getAudioTracks()[0];
pc1.addTransceiver(track);
@ -392,7 +392,7 @@
const pc2 = new RTCPeerConnection();
t.add_cleanup(() => pc1.close());
t.add_cleanup(() => pc2.close());
const stream = await navigator.mediaDevices.getUserMedia({audio: true});
const stream = await getNoiseStream({audio: true});
t.add_cleanup(() => stopTracks(stream));
const track = stream.getAudioTracks()[0];
pc1.addTrack(track, stream);
@ -413,7 +413,7 @@
t.add_cleanup(() => pc1.close());
t.add_cleanup(() => pc2.close());
const stream = await navigator.mediaDevices.getUserMedia({audio: true});
const stream = await getNoiseStream({audio: true});
t.add_cleanup(() => stopTracks(stream));
const track = stream.getAudioTracks()[0];
pc1.addTrack(track, stream);
@ -474,7 +474,7 @@
t.add_cleanup(() => pc2.close());
pc1.addTransceiver("audio");
const stream = await navigator.mediaDevices.getUserMedia({audio: true});
const stream = await getNoiseStream({audio: true});
t.add_cleanup(() => stopTracks(stream));
const track = stream.getAudioTracks()[0];
pc2.addTransceiver(track);
@ -504,7 +504,7 @@
pc1.addTransceiver("audio");
pc2.addTransceiver("audio");
const stream = await navigator.mediaDevices.getUserMedia({audio: true});
const stream = await getNoiseStream({audio: true});
t.add_cleanup(() => stopTracks(stream));
const track = stream.getAudioTracks()[0];
pc2.getTransceivers()[0].sender.replaceTrack(track);
@ -534,7 +534,7 @@
pc1.addTransceiver("audio");
pc2.addTransceiver("audio");
const stream = await navigator.mediaDevices.getUserMedia({audio: true});
const stream = await getNoiseStream({audio: true});
t.add_cleanup(() => stopTracks(stream));
const track = stream.getAudioTracks()[0];
pc2.addTrack(track, stream);
@ -562,7 +562,7 @@
t.add_cleanup(() => pc2.close());
pc1.addTransceiver("audio");
const stream = await navigator.mediaDevices.getUserMedia({audio: true});
const stream = await getNoiseStream({audio: true});
t.add_cleanup(() => stopTracks(stream));
const track = stream.getAudioTracks()[0];
pc2.addTrack(track, stream);
@ -590,7 +590,7 @@
t.add_cleanup(() => pc2.close());
pc1.addTransceiver("audio");
const stream = await navigator.mediaDevices.getUserMedia({audio: true});
const stream = await getNoiseStream({audio: true});
t.add_cleanup(() => stopTracks(stream));
const track = stream.getAudioTracks()[0];
pc2.addTrack(track, stream);
@ -617,7 +617,7 @@
const pc2 = new RTCPeerConnection();
t.add_cleanup(() => pc1.close());
t.add_cleanup(() => pc2.close());
const stream = await navigator.mediaDevices.getUserMedia({audio: true});
const stream = await getNoiseStream({audio: true});
t.add_cleanup(() => stopTracks(stream));
const track = stream.getAudioTracks()[0];
pc1.addTrack(track, stream);
@ -685,7 +685,7 @@
const pc = new RTCPeerConnection();
t.add_cleanup(() => pc.close());
pc.addTransceiver("audio");
const stream = await navigator.mediaDevices.getUserMedia({audio: true});
const stream = await getNoiseStream({audio: true});
const audio = stream.getAudioTracks()[0];
let sender = pc.addTrack(audio, stream);
pc.removeTrack(sender);
@ -705,7 +705,7 @@
const pc2 = new RTCPeerConnection();
t.add_cleanup(() => pc1.close());
t.add_cleanup(() => pc2.close());
const stream = await navigator.mediaDevices.getUserMedia({audio: true, video: true});
const stream = await getNoiseStream({audio: true, video: true});
t.add_cleanup(() => stopTracks(stream));
const audio = stream.getAudioTracks()[0];
pc1.addTrack(audio, stream);
@ -860,7 +860,7 @@
t.add_cleanup(() => pc1.close());
t.add_cleanup(() => pc2.close());
const stream = await navigator.mediaDevices.getUserMedia({audio: true});
const stream = await getNoiseStream({audio: true});
t.add_cleanup(() => stopTracks(stream));
const track = stream.getAudioTracks()[0];
pc1.addTrack(track, stream);
@ -962,7 +962,7 @@
t.add_cleanup(() => pc1.close());
t.add_cleanup(() => pc2.close());
const stream = await navigator.mediaDevices.getUserMedia({audio: true});
const stream = await getNoiseStream({audio: true});
t.add_cleanup(() => stopTracks(stream));
const track = stream.getAudioTracks()[0];
pc1.addTransceiver("audio");
@ -1026,7 +1026,7 @@
const checkMute = async t => {
const pc1 = new RTCPeerConnection();
t.add_cleanup(() => pc1.close());
const stream1 = await navigator.mediaDevices.getUserMedia({audio: true, video: true});
const stream1 = await getNoiseStream({audio: true, video: true});
t.add_cleanup(() => stopTracks(stream1));
const audio1 = stream1.getAudioTracks()[0];
pc1.addTrack(audio1, stream1);
@ -1040,7 +1040,7 @@
const pc2 = new RTCPeerConnection();
t.add_cleanup(() => pc2.close());
const stream2 = await navigator.mediaDevices.getUserMedia({audio: true, video: true});
const stream2 = await getNoiseStream({audio: true, video: true});
t.add_cleanup(() => stopTracks(stream2));
const audio2 = stream2.getAudioTracks()[0];
pc2.addTrack(audio2, stream2);
@ -1152,7 +1152,7 @@
const checkStop = async t => {
const pc1 = new RTCPeerConnection();
t.add_cleanup(() => pc1.close());
const stream = await navigator.mediaDevices.getUserMedia({audio: true});
const stream = await getNoiseStream({audio: true});
t.add_cleanup(() => stopTracks(stream));
const track = stream.getAudioTracks()[0];
pc1.addTrack(track, stream);
@ -1197,7 +1197,7 @@
transceiver.sender.getParameters()),
"InvalidStateError", "setParameters on stopped transceiver");
const stream2 = await navigator.mediaDevices.getUserMedia({audio: true});
const stream2 = await getNoiseStream({audio: true});
const track2 = stream.getAudioTracks()[0];
checkThrows(() => transceiver.sender.replaceTrack(track2),
"InvalidStateError", "replaceTrack on stopped transceiver");
@ -1251,7 +1251,7 @@
t.add_cleanup(() => pc1.close());
t.add_cleanup(() => pc2.close());
const stream = await navigator.mediaDevices.getUserMedia({audio: true});
const stream = await getNoiseStream({audio: true});
t.add_cleanup(() => stopTracks(stream));
const track = stream.getAudioTracks()[0];
pc1.addTrack(track, stream);
@ -1321,7 +1321,7 @@
t.add_cleanup(() => pc1.close());
t.add_cleanup(() => pc2.close());
const stream = await navigator.mediaDevices.getUserMedia({audio: true});
const stream = await getNoiseStream({audio: true});
t.add_cleanup(() => stopTracks(stream));
const track = stream.getAudioTracks()[0];
pc1.addTrack(track, stream);
@ -1391,7 +1391,7 @@
t.add_cleanup(() => pc1.close());
t.add_cleanup(() => pc2.close());
const stream = await navigator.mediaDevices.getUserMedia({audio: true});
const stream = await getNoiseStream({audio: true});
t.add_cleanup(() => stopTracks(stream));
const track = stream.getAudioTracks()[0];
pc1.addTrack(track, stream);
@ -1426,7 +1426,7 @@
t.add_cleanup(() => pc1.close());
t.add_cleanup(() => pc2.close());
const stream = await navigator.mediaDevices.getUserMedia({audio: true});
const stream = await getNoiseStream({audio: true});
t.add_cleanup(() => stopTracks(stream));
const track = stream.getAudioTracks()[0];
pc1.addTrack(track, stream);
@ -1496,7 +1496,7 @@
t.add_cleanup(() => pc1.close());
t.add_cleanup(() => pc2.close());
const stream = await navigator.mediaDevices.getUserMedia({audio: true});
const stream = await getNoiseStream({audio: true});
t.add_cleanup(() => stopTracks(stream));
const track = stream.getAudioTracks()[0];
pc1.addTrack(track, stream);
@ -1566,7 +1566,7 @@
t.add_cleanup(() => pc1.close());
t.add_cleanup(() => pc2.close());
const stream = await navigator.mediaDevices.getUserMedia({audio: true});
const stream = await getNoiseStream({audio: true});
t.add_cleanup(() => stopTracks(stream));
const track = stream.getAudioTracks()[0];
pc1.addTrack(track, stream);
@ -1589,7 +1589,7 @@
const pc = new RTCPeerConnection();
t.add_cleanup(() => pc.close());
const stream = await navigator.mediaDevices.getUserMedia({audio: true});
const stream = await getNoiseStream({audio: true});
t.add_cleanup(() => stopTracks(stream));
const track = stream.getAudioTracks()[0];
pc.addTrack(track, stream);
@ -1610,7 +1610,7 @@
// Verify that rollback doesn't stomp things it should not
pc.getTransceivers()[0].direction = "sendonly";
const stream2 = await navigator.mediaDevices.getUserMedia({audio: true});
const stream2 = await getNoiseStream({audio: true});
const track2 = stream2.getAudioTracks()[0];
await pc.getTransceivers()[0].sender.replaceTrack(track2);
@ -1641,7 +1641,7 @@
const pc1 = new RTCPeerConnection();
t.add_cleanup(() => pc1.close());
const audioStream = await navigator.mediaDevices.getUserMedia({audio: true});
const audioStream = await getNoiseStream({audio: true});
t.add_cleanup(() => stopTracks(audioStream));
const audioTrack = audioStream.getAudioTracks()[0];
pc1.addTrack(audioTrack, audioStream);
@ -1649,7 +1649,7 @@
const pc2 = new RTCPeerConnection();
t.add_cleanup(() => pc2.close());
const videoStream = await navigator.mediaDevices.getUserMedia({video: true});
const videoStream = await getNoiseStream({video: true});
t.add_cleanup(() => stopTracks(videoStream));
const videoTrack = videoStream.getVideoTracks()[0];
pc2.addTrack(videoTrack, videoStream);
@ -1720,7 +1720,7 @@
const pc1 = new RTCPeerConnection();
t.add_cleanup(() => pc1.close());
const stream = await navigator.mediaDevices.getUserMedia({audio: true});
const stream = await getNoiseStream({audio: true});
t.add_cleanup(() => stopTracks(stream));
const track = stream.getAudioTracks()[0];
pc1.addTrack(track, stream);
@ -1777,7 +1777,7 @@
const mid0 = pc2.getTransceivers()[0].mid;
// Give pc2 a track with replaceTrack
const stream2 = await navigator.mediaDevices.getUserMedia({audio: true});
const stream2 = await getNoiseStream({audio: true});
t.add_cleanup(() => stopTracks(stream2));
const track2 = stream2.getAudioTracks()[0];
await pc2.getTransceivers()[0].sender.replaceTrack(track2);
@ -1937,7 +1937,7 @@
t.add_cleanup(() => pc1.close());
t.add_cleanup(() => pc2.close());
const stream = await navigator.mediaDevices.getUserMedia({audio: true});
const stream = await getNoiseStream({audio: true});
t.add_cleanup(() => stopTracks(stream));
const track = stream.getAudioTracks()[0];
pc1.addTrack(track, stream);
@ -1973,7 +1973,7 @@
]);
// Check that m-section is reused on both ends
const stream2 = await navigator.mediaDevices.getUserMedia({audio: true});
const stream2 = await getNoiseStream({audio: true});
t.add_cleanup(() => stopTracks(stream2));
const track2 = stream2.getAudioTracks()[0];
@ -2044,7 +2044,7 @@
// new one for the new track)
const stoppedMid1 = pc1.getTransceivers()[1].mid;
pc1.getTransceivers()[1].stop();
const stream3 = await navigator.mediaDevices.getUserMedia({audio: true});
const stream3 = await getNoiseStream({audio: true});
t.add_cleanup(() => stopTracks(stream3));
const track3 = stream3.getAudioTracks()[0];
pc1.addTrack(track3, stream3);
@ -2114,7 +2114,7 @@
]);
// Add _another_ track; this should reuse the disabled m-section
const stream4 = await navigator.mediaDevices.getUserMedia({audio: true});
const stream4 = await getNoiseStream({audio: true});
t.add_cleanup(() => stopTracks(stream4));
const track4 = stream4.getAudioTracks()[0];
pc2.addTrack(track4, stream4);

View File

@ -24,7 +24,7 @@ async function checkIconInView(view, name, findIcon) {
const icon = findIcon(manager.document);
const size = Number(icon.src.match(/icon(\d+)\.png/)[1]);
is(icon.clientWidth, icon.clientHeight, `The icon should be square in ${name}`);
is(size, icon.clientWidth, `The correct icon size should have been chosen in ${name}`);
is(size, icon.clientWidth * window.devicePixelRatio, `The correct icon size should have been chosen in ${name}`);
await close_manager(manager);
}

View File

@ -3832,6 +3832,19 @@ void profiler_tracing(const char* aCategoryString, const char* aMarkerName,
racy_profiler_add_marker(aMarkerName, aCategory, std::move(payload));
}
void profiler_add_text_marker(
const char* aMarkerName, const nsACString& aText,
js::ProfilingStackFrame::Category aCategory,
const mozilla::TimeStamp& aStartTime, const mozilla::TimeStamp& aEndTime,
const mozilla::Maybe<nsID>& aDocShellId,
const mozilla::Maybe<uint32_t>& aDocShellHistoryId,
UniqueProfilerBacktrace aCause) {
profiler_add_marker(
aMarkerName, aCategory,
MakeUnique<TextMarkerPayload>(aText, aStartTime, aEndTime, aDocShellId,
aDocShellHistoryId, std::move(aCause)));
}
void profiler_set_js_context(JSContext* aCx) {
MOZ_ASSERT(aCx);

View File

@ -60,6 +60,11 @@
# define AUTO_PROFILER_TRACING(categoryString, markerName, category)
# define AUTO_PROFILER_TRACING_DOCSHELL(categoryString, markerName, category, \
docShell)
# define AUTO_PROFILER_TEXT_MARKER_CAUSE(markerName, text, category, cause)
# define AUTO_PROFILER_TEXT_MARKER_DOCSHELL(markerName, text, category, \
docShell)
# define AUTO_PROFILER_TEXT_MARKER_DOCSHELL_CAUSE(markerName, text, category, \
docShell, cause)
#else // !MOZ_GECKO_PROFILER
@ -695,6 +700,76 @@ void profiler_tracing(
js::ProfilingStackFrame::Category::category, docShellId, \
docShellHistoryId)
// Add a text marker. Text markers are similar to tracing markers, with the
// difference that text markers have their "text" separate from the marker name;
// multiple text markers with the same name can have different text, and these
// markers will still be displayed in the same "row" in the UI.
// Another difference is that text markers combine the start and end markers
// into one marker.
void profiler_add_text_marker(
const char* aMarkerName, const nsACString& aText,
js::ProfilingStackFrame::Category aCategory,
const mozilla::TimeStamp& aStartTime, const mozilla::TimeStamp& aEndTime,
const mozilla::Maybe<nsID>& aDocShellId = mozilla::Nothing(),
const mozilla::Maybe<uint32_t>& aDocShellHistoryId = mozilla::Nothing(),
UniqueProfilerBacktrace aCause = nullptr);
class MOZ_RAII AutoProfilerTextMarker {
public:
AutoProfilerTextMarker(const char* aMarkerName, const nsACString& aText,
js::ProfilingStackFrame::Category aCategory,
const mozilla::Maybe<nsID>& aDocShellId,
const mozilla::Maybe<uint32_t>& aDocShellHistoryId,
UniqueProfilerBacktrace&& aCause =
nullptr MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: mMarkerName(aMarkerName),
mText(aText),
mCategory(aCategory),
mStartTime(mozilla::TimeStamp::Now()),
mCause(std::move(aCause)),
mDocShellId(aDocShellId),
mDocShellHistoryId(aDocShellHistoryId) {
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
}
~AutoProfilerTextMarker() {
profiler_add_text_marker(mMarkerName, mText,
js::ProfilingStackFrame::Category::LAYOUT,
mStartTime, mozilla::TimeStamp::Now(), mDocShellId,
mDocShellHistoryId, std::move(mCause));
}
protected:
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
const char* mMarkerName;
nsCString mText;
const js::ProfilingStackFrame::Category mCategory;
mozilla::TimeStamp mStartTime;
UniqueProfilerBacktrace mCause;
const mozilla::Maybe<nsID> mDocShellId;
const mozilla::Maybe<uint32_t> mDocShellHistoryId;
};
# define AUTO_PROFILER_TEXT_MARKER_CAUSE(markerName, text, category, \
cause) \
AutoProfilerTextMarker PROFILER_RAII( \
markerName, text, js::ProfilingStackFrame::Category::category, \
Nothing(), Nothing(), cause)
# define AUTO_PROFILER_TEXT_MARKER_DOCSHELL(markerName, text, category, \
docShell) \
DECLARE_DOCSHELL_AND_HISTORY_ID(docShell); \
AutoProfilerTextMarker PROFILER_RAII( \
markerName, text, js::ProfilingStackFrame::Category::category, \
docShellId, docShellHistoryId)
# define AUTO_PROFILER_TEXT_MARKER_DOCSHELL_CAUSE(markerName, text, category, \
docShell, cause) \
DECLARE_DOCSHELL_AND_HISTORY_ID(docShell); \
AutoProfilerTextMarker PROFILER_RAII( \
markerName, text, js::ProfilingStackFrame::Category::category, \
docShellId, docShellHistoryId, cause)
//---------------------------------------------------------------------------
// Output profiles
//---------------------------------------------------------------------------

View File

@ -385,9 +385,10 @@ class TextMarkerPayload : public ProfilerMarkerPayload {
const mozilla::TimeStamp& aStartTime,
const mozilla::TimeStamp& aEndTime,
const mozilla::Maybe<nsID>& aDocShellId,
const mozilla::Maybe<uint32_t>& aDocShellHistoryId)
const mozilla::Maybe<uint32_t>& aDocShellHistoryId,
UniqueProfilerBacktrace aCause = nullptr)
: ProfilerMarkerPayload(aStartTime, aEndTime, aDocShellId,
aDocShellHistoryId),
aDocShellHistoryId, std::move(aCause)),
mText(aText) {}
DECL_STREAM_PAYLOAD

View File

@ -72,14 +72,6 @@ class VibrancyManager {
bool HasVibrantRegions() { return !mVibrantRegions.IsEmpty(); }
/**
* Clear the vibrant areas that we know about.
* The clearing happens in the current NSGraphicsContext. If you call this
* from within an -[NSView drawRect:] implementation, the currrent
* NSGraphicsContext is already correctly set to the window drawing context.
*/
void ClearVibrantAreas() const;
/**
* Return the fill color that should be drawn on top of the cleared window
* parts. Usually this would be drawn by -[NSVisualEffectView drawRect:].
@ -108,8 +100,6 @@ class VibrancyManager {
static NSView* CreateEffectView(VibrancyType aType, BOOL aIsContainer = NO);
protected:
void ClearVibrantRegion(const LayoutDeviceIntRegion& aVibrantRegion) const;
const nsChildView& mCoordinateConverter;
NSView* mContainerView;
nsClassHashtable<nsUint32HashKey, ViewRegion> mVibrantRegions;

Some files were not shown because too many files have changed in this diff Show More