Merge mozilla-central to mozilla-inbound

This commit is contained in:
Dorel Luca 2018-11-09 19:41:24 +02:00
Родитель 437a5e1375 4e638efc71
Коммит 17253c6f93
1482 изменённых файлов: 3658 добавлений и 6032 удалений

Просмотреть файл

@ -16,6 +16,8 @@ js/src/builtin/intl/TimeZoneDataGenerated.h
js/src/jsapi-tests/.*
# See bug 1395584
js/src/vm/Opcodes.h
# Ignored because of bug 1506117
layout/style/nsCSSKeywordList.h
# Ignored because of bug 1342657
layout/style/nsCSSPropAliasList.h
# Ignored because of bug 1342657

Просмотреть файл

@ -14,9 +14,7 @@ obj*/**
# We ignore all these directories by default, until we get them enabled.
# If you are enabling a directory, please add directory specific exclusions
# below.
docshell/resources/**
docshell/test/browser/**
docshell/test/chrome/**
docshell/test/iframesandbox/**
docshell/test/mochitest/**
extensions/cookie/**

Просмотреть файл

@ -104,23 +104,15 @@ var gDragSpaceObserver = {
pref: "browser.tabs.extraDragSpace",
init() {
this._update();
Services.prefs.addObserver(this.pref, this);
this.observe();
},
uninit() {
Services.prefs.removeObserver(this.pref, this);
},
observe(aSubject, aTopic, aPrefName) {
if (aTopic != "nsPref:changed" || aPrefName != this.pref) {
return;
}
this._update();
},
_update() {
observe() {
if (Services.prefs.getBoolPref(this.pref)) {
document.documentElement.setAttribute("extradragspace", "true");
} else {

Просмотреть файл

@ -35,6 +35,7 @@ Preferences.addAll([
{ id: "network.proxy.backup.socks_port", type: "int" },
{ id: "network.trr.mode", type: "int" },
{ id: "network.trr.uri", type: "string" },
{ id: "network.trr.custom_uri", "type": "string" },
]);
window.addEventListener("DOMContentLoaded", () => {
@ -55,6 +56,10 @@ window.addEventListener("DOMContentLoaded", () => {
var gConnectionsDialog = {
beforeAccept() {
if (document.getElementById("customDnsOverHttpsUrlRadio").selected) {
Services.prefs.setStringPref("network.trr.uri", document.getElementById("customDnsOverHttpsInput").value);
}
var proxyTypePref = Preferences.get("network.proxy.type");
if (proxyTypePref.value == 2) {
this.doAutoconfigURLFixup();
@ -304,16 +309,25 @@ var gConnectionsDialog = {
return trrModeCheckbox.checked ? 2 : 0;
},
writeDnsOverHttpsUri() {
// called to update pref with user input
let input = document.getElementById("networkDnsOverHttpsUrl");
let uriString = input.value.trim();
// turn an empty string into `undefined` to clear the pref back to the default
return uriString.length ? uriString : undefined;
updateDnsOverHttpsUI() {
// Disable the custom url input box if the parent checkbox and custom radio button attached to it is not selected.
// Disable the custom radio button if the parent checkbox is not selected.
let parentCheckbox = document.getElementById("networkDnsOverHttps");
let customDnsOverHttpsUrlRadio = document.getElementById("customDnsOverHttpsUrlRadio");
let customDnsOverHttpsInput = document.getElementById("customDnsOverHttpsInput");
customDnsOverHttpsInput.disabled = !parentCheckbox.checked || !customDnsOverHttpsUrlRadio.selected;
customDnsOverHttpsUrlRadio.disabled = !parentCheckbox.checked;
},
initDnsOverHttpsUI() {
let input = document.getElementById("networkDnsOverHttpsUrl");
input.placeholder = Preferences.get("network.trr.uri").defaultValue;
let defaultDnsOverHttpsUrlRadio = document.getElementById("defaultDnsOverHttpsUrlRadio");
let defaultPrefUrl = Preferences.get("network.trr.uri").defaultValue;
document.l10n.setAttributes(defaultDnsOverHttpsUrlRadio, "connection-dns-over-https-url-default", {
url: defaultPrefUrl,
});
defaultDnsOverHttpsUrlRadio.value = defaultPrefUrl;
let radioGroup = document.getElementById("DnsOverHttpsUrlRadioGroup");
radioGroup.selectedIndex = Preferences.get("network.trr.uri").hasUserValue ? 1 : 0;
this.updateDnsOverHttpsUI();
},
};

Просмотреть файл

@ -152,11 +152,20 @@
data-l10n-id="connection-dns-over-https"
preference="network.trr.mode"
onsyncfrompreference="return gConnectionsDialog.readDnsOverHttpsMode();"
onsynctopreference="return gConnectionsDialog.writeDnsOverHttpsMode()" />
<hbox class="indent" flex="1" align="center">
<label control="networkDnsOverHttpsUrl" data-l10n-id="connection-dns-over-https-url"
data-l10n-attrs="tooltiptext"/>
<textbox id="networkDnsOverHttpsUrl" flex="1" preference="network.trr.uri"
onsynctopreference="return gConnectionsDialog.writeDnsOverHttpsUri()" />
</hbox>
onsynctopreference="return gConnectionsDialog.writeDnsOverHttpsMode();"
oncommand="gConnectionsDialog.updateDnsOverHttpsUI();"/>
<vbox class="indent" flex="1">
<radiogroup id="DnsOverHttpsUrlRadioGroup" orient="vertical">
<radio id="defaultDnsOverHttpsUrlRadio"
data-l10n-id="connection-dns-over-https-url-default"
preference="network.trr.uri"
oncommand="gConnectionsDialog.updateDnsOverHttpsUI();"/>
<hbox>
<radio id="customDnsOverHttpsUrlRadio"
data-l10n-id="connection-dns-over-https-url-custom"
oncommand="gConnectionsDialog.updateDnsOverHttpsUI();"/>
<textbox id="customDnsOverHttpsInput" flex="1" preference="network.trr.custom_uri"/>
</hbox>
</radiogroup>
</vbox>
</dialog>

Просмотреть файл

@ -709,7 +709,8 @@
connection-proxy-autologin.label,
connection-proxy-socks-remote-dns.label,
connection-dns-over-https,
connection-dns-over-https-url
connection-dns-over-https-url-custom,
connection-dns-over-https-url-default,
" />
</hbox>
</hbox>

Просмотреть файл

@ -3,17 +3,20 @@ ChromeUtils.import("resource://gre/modules/Services.jsm");
const SUBDIALOG_URL = "chrome://browser/content/preferences/connection.xul";
const TRR_MODE_PREF = "network.trr.mode";
const TRR_URI_PREF = "network.trr.uri";
const TRR_CUSTOM_URI_PREF = "network.trr.custom_uri";
const modeCheckboxSelector = "#networkDnsOverHttps";
const uriTextboxSelector = "#networkDnsOverHttpsUrl";
const uriTextboxSelector = "#customDnsOverHttpsInput";
const defaultPrefValues = Object.freeze({
[TRR_MODE_PREF]: 0,
[TRR_URI_PREF]: "https://mozilla.cloudflare-dns.com/dns-query",
[TRR_CUSTOM_URI_PREF]: "",
});
function resetPrefs() {
Services.prefs.clearUserPref(TRR_MODE_PREF);
Services.prefs.clearUserPref(TRR_URI_PREF);
Services.prefs.clearUserPref(TRR_CUSTOM_URI_PREF);
}
let preferencesOpen = new Promise(res => open_preferences(res));
@ -53,6 +56,9 @@ async function testWithProperties(props, startTime) {
if (props.hasOwnProperty(TRR_MODE_PREF)) {
Services.prefs.setIntPref(TRR_MODE_PREF, props[TRR_MODE_PREF]);
}
if (props.hasOwnProperty(TRR_CUSTOM_URI_PREF)) {
Services.prefs.setStringPref(TRR_CUSTOM_URI_PREF, props[TRR_CUSTOM_URI_PREF]);
}
if (props.hasOwnProperty(TRR_URI_PREF)) {
Services.prefs.setStringPref(TRR_URI_PREF, props[TRR_URI_PREF]);
}
@ -84,7 +90,7 @@ async function testWithProperties(props, startTime) {
}
if (props.hasOwnProperty("inputUriKeys")) {
info((Date.now() - startTime) + ": testWithProperties: inputUriKeys, waiting for the pref observer");
uriPrefChangedPromise = waitForPrefObserver(TRR_URI_PREF);
uriPrefChangedPromise = waitForPrefObserver(TRR_CUSTOM_URI_PREF);
info((Date.now() - startTime) + ": testWithProperties: inputUriKeys, pref changed, now enter the new value");
uriTextbox.focus();
uriTextbox.value = props.inputUriKeys;
@ -117,17 +123,20 @@ async function testWithProperties(props, startTime) {
}
add_task(async function default_values() {
let customUriPref = Services.prefs.getStringPref(TRR_CUSTOM_URI_PREF);
let uriPref = Services.prefs.getStringPref(TRR_URI_PREF);
let modePref = Services.prefs.getIntPref(TRR_MODE_PREF);
is(modePref, defaultPrefValues[TRR_MODE_PREF],
`Actual value of ${TRR_MODE_PREF} matches expected default value`);
is(uriPref, defaultPrefValues[TRR_URI_PREF],
`Actual value of ${TRR_MODE_PREF} matches expected default value`);
`Actual value of ${TRR_URI_PREF} matches expected default value`);
is(customUriPref, defaultPrefValues[TRR_CUSTOM_URI_PREF],
`Actual value of ${TRR_CUSTOM_URI_PREF} matches expected default value`);
});
let testVariations = [
// verify state with defaults
{ expectedModePref: 0, expectedUriValue: "https://mozilla.cloudflare-dns.com/dns-query" },
{ expectedModePref: 0, expectedUriValue: "" },
// verify each of the modes maps to the correct checked state
{ [TRR_MODE_PREF]: 0, expectedModeChecked: false },
@ -140,31 +149,27 @@ let testVariations = [
{ [TRR_MODE_PREF]: 77, expectedModeChecked: false },
// verify toggling the checkbox gives the right outcomes
{ clickMode: true, expectedModeValue: 2, expectedUriValue: "https://mozilla.cloudflare-dns.com/dns-query" },
{ clickMode: true, expectedModeValue: 2, expectedUriValue: "" },
{
[TRR_MODE_PREF]: 4,
expectedModeChecked: true, clickMode: true, expectedModePref: 0,
},
// test that setting TRR_CUSTOM_URI_PREF subsequently changes TRR_URI_PREF
{
[TRR_MODE_PREF]: 2, [TRR_URI_PREF]: "https://example.com",
[TRR_MODE_PREF]: 2, [TRR_CUSTOM_URI_PREF]: "https://example.com",
expectedModeValue: true, expectedUriValue: "https://example.com",
},
{
[TRR_URI_PREF]: "",
clickMode: true, inputUriKeys: "https://example.com",
expectedModePref: 2, expectedFinalUriPref: "https://example.com",
},
// verify the uri can be cleared
{
[TRR_MODE_PREF]: 2, [TRR_URI_PREF]: "https://example.com",
[TRR_MODE_PREF]: 2, [TRR_URI_PREF]: "https://example.com", [TRR_CUSTOM_URI_PREF]: "https://example.com",
expectedUriValue: "https://example.com", inputUriKeys: "", expectedFinalUriPref: "",
},
// verify uri gets sanitized
{
clickMode: true, inputUriKeys: " https://example.com ",
expectedModePref: 2, expectedFinalUriPref: "https://example.com",
},
];
for (let props of testVariations) {

Просмотреть файл

@ -86,6 +86,14 @@ connection-dns-over-https =
.label = Enable DNS over HTTPS
.accesskey = b
connection-dns-over-https-url = URL
# Variables:
# $url (String) - URL for the DNS over HTTPS provider
connection-dns-over-https-url-default =
.label = Use default ({ $url })
.accesskey = U
.tooltiptext = URL for resolving DNS over HTTPS
.tooltiptext = Use the default URL for resolving DNS over HTTPS
connection-dns-over-https-url-custom =
.label = Custom
.accesskey = C
.tooltiptext = Enter your preferred URL for resolving DNS over HTTPS

Просмотреть файл

@ -632,12 +632,19 @@ notification[value="translation"] menulist > .menulist-dropmarker {
}
}
:root[tabsintitlebar] > #navigator-toolbox > #titlebar:not(:-moz-lwtheme) {
:root[tabsintitlebar] > #navigator-toolbox > #titlebar {
-moz-appearance: -moz-window-titlebar-maximized;
}
:root[tabsintitlebar][sizemode="normal"] > #navigator-toolbox > #titlebar:not(:-moz-lwtheme) {
:root[tabsintitlebar][sizemode="normal"] > #navigator-toolbox > #titlebar {
-moz-appearance: -moz-window-titlebar;
}
:root[tabsintitlebar] > #navigator-toolbox > #titlebar:-moz-lwtheme {
visibility: hidden;
}
:root[tabsintitlebar] #toolbar-menubar:-moz-lwtheme,
:root[tabsintitlebar] #TabsToolbar:-moz-lwtheme {
visibility: visible;
}
/* Add extra space to titlebar for dragging */
:root[sizemode="normal"][chromehidden~="menubar"] #TabsToolbar,

Просмотреть файл

@ -93,7 +93,6 @@ toolbar[brighttext] {
.findbar-button {
-moz-appearance: none;
padding: 0;
color: inherit;
}
@ -113,7 +112,7 @@ toolbar .toolbarbutton-1 > menupopup {
margin-top: -3px;
}
.findbar-button > .toolbarbutton-text,
.findbar-button,
toolbarbutton.bookmark-item:not(.subviewbutton),
toolbar .toolbarbutton-1 > .toolbarbutton-icon,
toolbar .toolbarbutton-1 > .toolbarbutton-text,
@ -173,7 +172,7 @@ toolbar[brighttext] .toolbaritem-combined-buttons > separator {
#PersonalToolbar .toolbarbutton-1:not([disabled=true]):not([checked]):not([open]):not(:active):hover,
.tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled=true]):hover,
.tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled=true]):hover,
.findbar-button:not(:-moz-any([checked="true"],[disabled="true"])):hover > .toolbarbutton-text,
.findbar-button:not(:-moz-any([checked="true"],[disabled="true"])):hover,
toolbarbutton.bookmark-item:not(.subviewbutton):hover:not([disabled="true"]):not([open]),
toolbar .toolbarbutton-1:not([disabled=true]):not([checked]):not([open]):not(:active):hover > .toolbarbutton-icon,
toolbar .toolbarbutton-1:not([disabled=true]):not([checked]):not([open]):not(:active):hover > .toolbarbutton-text,
@ -183,7 +182,7 @@ toolbar .toolbarbutton-1:not([disabled=true]):not([checked]):not([open]):not(:ac
}
#PersonalToolbar .toolbarbutton-1:not([disabled=true]):-moz-any([open],[checked],:hover:active),
.findbar-button:not([disabled=true]):-moz-any([checked="true"],:hover:active) > .toolbarbutton-text,
.findbar-button:not([disabled=true]):-moz-any([checked="true"],:hover:active),
toolbarbutton.bookmark-item:not(.subviewbutton):hover:active:not([disabled="true"]),
toolbarbutton.bookmark-item[open="true"],
toolbar .toolbarbutton-1:not([disabled=true]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-icon,

Просмотреть файл

@ -72,7 +72,7 @@ def rust_compiler(rustc_info, cargo_info):
You can install rust by running './mach bootstrap'
or by directly running the installer from https://rustup.rs/
'''))
rustc_min_version = Version('1.29.0')
rustc_min_version = Version('1.30.0')
cargo_min_version = rustc_min_version
version = rustc_info.version

Просмотреть файл

@ -166,11 +166,7 @@ function requestWorkers() {
const client = getCurrentClient(getState().runtimes);
try {
const {
other: otherWorkers,
service: serviceWorkers,
shared: sharedWorkers,
} = await client.mainRoot.listAllWorkers();
const { otherWorkers, serviceWorkers, sharedWorkers } = await client.listWorkers();
dispatch({
type: REQUEST_WORKERS_SUCCESS,

Просмотреть файл

@ -4,8 +4,6 @@
"use strict";
const { ADB } = require("devtools/shared/adb/adb");
const { DebuggerClient } = require("devtools/shared/client/debugger-client");
const { DebuggerServer } = require("devtools/server/main");
const Actions = require("./index");
@ -16,6 +14,8 @@ const {
} = require("../modules/runtimes-state-helper");
const { isSupportedDebugTarget } = require("../modules/debug-target-support");
const { createClientForRuntime } = require("../modules/runtime-client-factory");
const {
CONNECT_RUNTIME_FAILURE,
CONNECT_RUNTIME_START,
@ -38,47 +38,9 @@ const {
WATCH_RUNTIME_SUCCESS,
} = require("../constants");
async function createLocalClient() {
DebuggerServer.init();
DebuggerServer.registerAllActors();
const client = new DebuggerClient(DebuggerServer.connectPipe());
await client.connect();
return { client };
}
async function createNetworkClient(host, port) {
const transportDetails = { host, port };
const transport = await DebuggerClient.socketConnect(transportDetails);
const client = new DebuggerClient(transport);
await client.connect();
return { client, transportDetails };
}
async function createUSBClient(socketPath) {
const port = await ADB.prepareTCPConnection(socketPath);
return createNetworkClient("localhost", port);
}
async function createClientForRuntime(runtime) {
const { extra, type } = runtime;
if (type === RUNTIMES.THIS_FIREFOX) {
return createLocalClient();
} else if (type === RUNTIMES.NETWORK) {
const { host, port } = extra.connectionParameters;
return createNetworkClient(host, port);
} else if (type === RUNTIMES.USB) {
const { socketPath } = extra.connectionParameters;
return createUSBClient(socketPath);
}
return null;
}
async function getRuntimeInfo(runtime, client) {
const { extra, type } = runtime;
const deviceFront = await client.mainRoot.getFront("device");
const { brandName: name, channel, version } = await deviceFront.getDescription();
const { name, channel, version } = await client.getDeviceDescription();
const icon =
(channel === "release" || channel === "beta" || channel === "aurora")
? `chrome://devtools/skin/images/aboutdebugging-firefox-${ channel }.svg`
@ -107,9 +69,9 @@ function connectRuntime(id) {
const runtime = findRuntimeById(id, getState().runtimes);
const { client, transportDetails } = await createClientForRuntime(runtime);
const info = await getRuntimeInfo(runtime, client);
const preferenceFront = await client.mainRoot.getFront("preference");
const connectionPromptEnabled =
await preferenceFront.getBoolPref(RUNTIME_PREFERENCE.CONNECTION_PROMPT);
const promptPrefName = RUNTIME_PREFERENCE.CONNECTION_PROMPT;
const connectionPromptEnabled = await client.getPreference(promptPrefName);
const runtimeDetails = { connectionPromptEnabled, client, info, transportDetails };
if (runtime.type === RUNTIMES.USB) {
@ -168,12 +130,10 @@ function updateConnectionPromptSetting(connectionPromptEnabled) {
try {
const runtime = getCurrentRuntime(getState().runtimes);
const client = runtime.runtimeDetails.client;
const preferenceFront = await client.mainRoot.getFront("preference");
await preferenceFront.setBoolPref(RUNTIME_PREFERENCE.CONNECTION_PROMPT,
connectionPromptEnabled);
const promptPrefName = RUNTIME_PREFERENCE.CONNECTION_PROMPT;
await client.setPreference(promptPrefName, connectionPromptEnabled);
// Re-get actual value from the runtime.
connectionPromptEnabled =
await preferenceFront.getBoolPref(RUNTIME_PREFERENCE.CONNECTION_PROMPT);
connectionPromptEnabled = await client.getPreference(promptPrefName);
dispatch({ type: UPDATE_CONNECTION_PROMPT_SETTING_SUCCESS,
runtime, connectionPromptEnabled });

Просмотреть файл

@ -40,7 +40,7 @@ class SidebarRuntimeItem extends PureComponent {
},
dom.button(
{
className: "default-button default-button--micro",
className: "default-button default-button--micro js-connect-button",
onClick: () => {
const { dispatch, runtimeId } = this.props;
dispatch(Actions.connectRuntime(runtimeId));

Просмотреть файл

@ -60,7 +60,7 @@ function debugTargetListenerMiddleware(store) {
const { client } = runtime.runtimeDetails;
if (isSupportedDebugTarget(runtime.type, DEBUG_TARGETS.TAB)) {
client.mainRoot.on("tabListChanged", onTabsUpdated);
client.addListener("tabListChanged", onTabsUpdated);
}
if (isSupportedDebugTarget(runtime.type, DEBUG_TARGETS.EXTENSION)) {
@ -68,9 +68,9 @@ function debugTargetListenerMiddleware(store) {
}
if (isSupportedDebugTarget(runtime.type, DEBUG_TARGETS.WORKER)) {
client.mainRoot.on("workerListChanged", onWorkersUpdated);
client.mainRoot.on("serviceWorkerRegistrationListChanged", onWorkersUpdated);
client.mainRoot.on("processListChanged", onWorkersUpdated);
client.addListener("workerListChanged", onWorkersUpdated);
client.addListener("serviceWorkerRegistrationListChanged", onWorkersUpdated);
client.addListener("processListChanged", onWorkersUpdated);
client.addListener("registration-changed", onWorkersUpdated);
client.addListener("push-subscription-modified", onWorkersUpdated);
}
@ -81,7 +81,7 @@ function debugTargetListenerMiddleware(store) {
const { client } = runtime.runtimeDetails;
if (isSupportedDebugTarget(runtime.type, DEBUG_TARGETS.TAB)) {
client.mainRoot.off("tabListChanged", onTabsUpdated);
client.removeListener("tabListChanged", onTabsUpdated);
}
if (isSupportedDebugTarget(runtime.type, DEBUG_TARGETS.EXTENSION)) {
@ -89,9 +89,9 @@ function debugTargetListenerMiddleware(store) {
}
if (isSupportedDebugTarget(runtime.type, DEBUG_TARGETS.WORKER)) {
client.mainRoot.off("workerListChanged", onWorkersUpdated);
client.mainRoot.off("serviceWorkerRegistrationListChanged", onWorkersUpdated);
client.mainRoot.off("processListChanged", onWorkersUpdated);
client.removeListener("workerListChanged", onWorkersUpdated);
client.removeListener("serviceWorkerRegistrationListChanged", onWorkersUpdated);
client.removeListener("processListChanged", onWorkersUpdated);
client.removeListener("registration-changed", onWorkersUpdated);
client.removeListener("push-subscription-modified", onWorkersUpdated);
}

Просмотреть файл

@ -0,0 +1,114 @@
/* 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 { RUNTIME_PREFERENCE } = require("../constants");
const PREF_TYPES = {
BOOL: "BOOL",
};
// Map of preference to preference type.
const PREF_TO_TYPE = {
[RUNTIME_PREFERENCE.CONNECTION_PROMPT]: PREF_TYPES.BOOL,
};
// Some events are fired by mainRoot rather than client.
const MAIN_ROOT_EVENTS = [
"processListChanged",
"serviceWorkerRegistrationListChanged",
"tabListChanged",
"workerListChanged",
];
/**
* The ClientWrapper class is used to isolate aboutdebugging from the DevTools client API
* The modules of about:debugging should never call DevTools client APIs directly.
*/
class ClientWrapper {
constructor(client) {
this.client = client;
}
addOneTimeListener(evt, listener) {
if (MAIN_ROOT_EVENTS.includes(evt)) {
this.client.mainRoot.once(evt, listener);
} else {
this.client.addOneTimeListener(evt, listener);
}
}
addListener(evt, listener) {
if (MAIN_ROOT_EVENTS.includes(evt)) {
this.client.mainRoot.on(evt, listener);
} else {
this.client.addListener(evt, listener);
}
}
removeListener(evt, listener) {
if (MAIN_ROOT_EVENTS.includes(evt)) {
this.client.mainRoot.off(evt, listener);
} else {
this.client.removeListener(evt, listener);
}
}
async getDeviceDescription() {
const deviceFront = await this.client.mainRoot.getFront("device");
const { brandName, channel, version } = await deviceFront.getDescription();
return { name: brandName, channel, version };
}
async setPreference(prefName, value) {
const prefType = PREF_TO_TYPE[prefName];
const preferenceFront = await this.client.mainRoot.getFront("preference");
switch (prefType) {
case PREF_TYPES.BOOL:
return preferenceFront.setBoolPref(prefName, value);
default:
throw new Error("Unsupported preference" + prefName);
}
}
async getPreference(prefName) {
const prefType = PREF_TO_TYPE[prefName];
const preferenceFront = await this.client.mainRoot.getFront("preference");
switch (prefType) {
case PREF_TYPES.BOOL:
return preferenceFront.getBoolPref(prefName);
default:
throw new Error("Unsupported preference:" + prefName);
}
}
async listTabs(options) {
return this.client.listTabs(options);
}
async listAddons() {
return this.client.listAddons();
}
async listWorkers() {
const { other, service, shared } = await this.client.mainRoot.listAllWorkers();
return {
otherWorkers: other,
serviceWorkers: service,
sharedWorkers: shared,
};
}
async request(options) {
return this.client.request(options);
}
async close() {
return this.client.close();
}
}
exports.ClientWrapper = ClientWrapper;

Просмотреть файл

@ -3,11 +3,14 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DevToolsModules(
'client-wrapper.js',
'debug-target-collapsibilities.js',
'debug-target-support.js',
'extensions-helper.js',
'l10n.js',
'network-locations.js',
'runtime-client-factory.js',
'runtimes-state-helper.js',
'test-helper.js',
'usb-runtimes.js',
)

Просмотреть файл

@ -0,0 +1,53 @@
/* 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 { ADB } = require("devtools/shared/adb/adb");
const { DebuggerClient } = require("devtools/shared/client/debugger-client");
const { DebuggerServer } = require("devtools/server/main");
const { ClientWrapper } = require("./client-wrapper");
const { RUNTIMES } = require("../constants");
async function createLocalClient() {
DebuggerServer.init();
DebuggerServer.registerAllActors();
const client = new DebuggerClient(DebuggerServer.connectPipe());
await client.connect();
return { client: new ClientWrapper(client) };
}
async function createNetworkClient(host, port) {
const transportDetails = { host, port };
const transport = await DebuggerClient.socketConnect(transportDetails);
const client = new DebuggerClient(transport);
await client.connect();
return { client: new ClientWrapper(client), transportDetails };
}
async function createUSBClient(socketPath) {
const port = await ADB.prepareTCPConnection(socketPath);
return createNetworkClient("localhost", port);
}
async function createClientForRuntime(runtime) {
const { extra, type } = runtime;
if (type === RUNTIMES.THIS_FIREFOX) {
return createLocalClient();
} else if (type === RUNTIMES.NETWORK) {
const { host, port } = extra.connectionParameters;
return createNetworkClient(host, port);
} else if (type === RUNTIMES.USB) {
const { socketPath } = extra.connectionParameters;
return createUSBClient(socketPath);
}
return null;
}
exports.createClientForRuntime = createClientForRuntime;
require("./test-helper").enableMocks(module, "modules/runtime-client-factory");

Просмотреть файл

@ -0,0 +1,84 @@
/* 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 flags = require("devtools/shared/flags");
/**
* The methods from this module are going to be called from both aboutdebugging
* BrowserLoader modules (enableMocks) and from tests (setMockedModules,
* removeMockedModule). We use the main devtools loader to communicate between the
* different sandboxes involved.
*/
const { Cu } = require("chrome");
const { loader } = Cu.import("resource://devtools/shared/Loader.jsm", {});
const ROOT_PATH = "resource://devtools/client/aboutdebugging-new/src/";
/**
* Allows to load a mock instead of an about:debugging module.
* To use it, the module to mock needs to call
* require("devtools/client/aboutdebugging-new/src/modules/test-helper").
* enableMocks(module, "path/to/module.js");
*
* From the tests, the devtools loader should be provided with the mocked module using
* setMockedModule().
*
* At the end of the test, the mocks should be discarded with removeMockedModule().
*
* @param {Object} moduleToMock
* The `module` global from the module we want to mock, in order to override its
* `module.exports`.
* @param {String} modulePath
* Path to the module, relative to `devtools/client/aboutdebugging-new/src/`
*/
function enableMocks(moduleToMock, modulePath) {
if (flags.testing) {
const path = ROOT_PATH + modulePath + ".js";
if (loader.mocks && loader.mocks[path]) {
moduleToMock.exports = loader.mocks[path];
}
}
}
exports.enableMocks = enableMocks;
/**
* Assign a mock object to the provided module path, relative to
* `devtools/client/aboutdebugging-new/src/`.
*/
function setMockedModule(mock, modulePath) {
loader.mocks = loader.mocks || {};
const mockedModule = {};
Object.keys(mock).forEach(key => {
if (typeof mock[key] === "function") {
// All the mock methods are wrapped again here so that if the test replaces the
// methods dynamically and call sites used
// `const { someMethod } = require("./runtime-client-factory")`, someMethod will
// still internally call the current method defined on the mock, and not the one
// that was defined at the moment of the import.
mockedModule[key] = function() {
return mock[key].apply(mock, arguments);
};
} else {
mockedModule[key] = mock[key];
}
});
const path = ROOT_PATH + modulePath + ".js";
loader.mocks[path] = mockedModule;
}
exports.setMockedModule = setMockedModule;
/**
* Remove any mock object defined for the provided module path, relative to
* `devtools/client/aboutdebugging-new/src/`.
*/
function removeMockedModule(modulePath) {
const path = ROOT_PATH + modulePath + ".js";
if (loader.mocks && loader.mocks[path]) {
delete loader.mocks[path];
}
}
exports.removeMockedModule = removeMockedModule;

Просмотреть файл

@ -42,3 +42,5 @@ function refreshUSBRuntimes() {
return adbScanner.scan();
}
exports.refreshUSBRuntimes = refreshUSBRuntimes;
require("./test-helper").enableMocks(module, "modules/usb-runtimes");

Просмотреть файл

@ -5,6 +5,7 @@
"use strict";
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const { ClientWrapper } = require("./modules/client-wrapper");
const runtimeInfo = {
// device name which is running the runtime,
@ -30,8 +31,8 @@ const runtimeTransportDetails = {
};
const runtimeDetails = {
// debugger client instance
client: PropTypes.object.isRequired,
// ClientWrapper built using a DebuggerClient for the runtime
client: PropTypes.instanceOf(ClientWrapper).isRequired,
// reflect devtools.debugger.prompt-connection preference of this runtime
connectionPromptEnabled: PropTypes.bool.isRequired,

Просмотреть файл

@ -5,6 +5,7 @@ support-files =
debug-target-pane_collapsibilities_head.js
head-addons-script.js
head.js
mocks/*
resources/test-adb-extension/*
resources/test-temporary-extension/*
!/devtools/client/shared/test/shared-head.js
@ -20,6 +21,8 @@ skip-if = (os == 'linux' && bits == 32) # ADB start() fails on linux 32, see Bug
[browser_aboutdebugging_debug-target-pane_empty.js]
[browser_aboutdebugging_navigate.js]
[browser_aboutdebugging_sidebar_network_runtimes.js]
[browser_aboutdebugging_sidebar_usb_runtime.js]
[browser_aboutdebugging_sidebar_usb_runtime_connect.js]
[browser_aboutdebugging_sidebar_usb_status.js]
skip-if = (os == 'linux' && bits == 32) # ADB start() fails on linux 32, see Bug 1499638
[browser_aboutdebugging_thisfirefox.js]

Просмотреть файл

@ -0,0 +1,50 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* import-globals-from mocks/head-usb-runtimes-mock.js */
"use strict";
// Load USB Runtimes mock module
Services.scriptloader.loadSubScript(
CHROME_URL_ROOT + "mocks/head-usb-runtimes-mock.js", this);
// Test that USB runtimes appear and disappear from the sidebar.
add_task(async function() {
const usbRuntimesMock = createUsbRuntimesMock();
const observerMock = addObserverMock(usbRuntimesMock);
enableUsbRuntimesMock(usbRuntimesMock);
// Disable our mock when the test ends.
registerCleanupFunction(() => {
disableUsbRuntimesMock();
});
const { document, tab } = await openAboutDebugging();
usbRuntimesMock.getUSBRuntimes = function() {
return [{
id: "test_device_id",
_socketPath: "test/path",
deviceName: "test device name",
shortName: "testshort",
}];
};
observerMock.emit("runtime-list-updated");
info("Wait until the USB sidebar item appears");
await waitUntil(() => findSidebarItemByText("test device name", document));
const usbRuntimeSidebarItem = findSidebarItemByText("test device name", document);
ok(usbRuntimeSidebarItem.textContent.includes("testshort"),
"The short name of the usb runtime is visible");
usbRuntimesMock.getUSBRuntimes = function() {
return [];
};
observerMock.emit("runtime-list-updated");
info("Wait until the USB sidebar item disappears");
await waitUntil(() => !findSidebarItemByText("test device name", document));
await removeTab(tab);
});

Просмотреть файл

@ -0,0 +1,88 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const MOCKS_ROOT = CHROME_URL_ROOT + "mocks/";
/* import-globals-from mocks/head-client-wrapper-mock.js */
Services.scriptloader.loadSubScript(MOCKS_ROOT + "head-client-wrapper-mock.js", this);
/* import-globals-from mocks/head-runtime-client-factory-mock.js */
Services.scriptloader.loadSubScript(MOCKS_ROOT + "head-runtime-client-factory-mock.js",
this);
/* import-globals-from mocks/head-usb-runtimes-mock.js */
Services.scriptloader.loadSubScript(MOCKS_ROOT + "head-usb-runtimes-mock.js", this);
const RUNTIME_ID = "test-runtime-id";
const { RUNTIMES } = require("devtools/client/aboutdebugging-new/src/constants");
// Test that USB runtimes appear and disappear from the sidebar.
add_task(async function() {
const usbRuntimesMock = createUsbRuntimesMock();
const observerMock = addObserverMock(usbRuntimesMock);
enableUsbRuntimesMock(usbRuntimesMock);
// Create a basic mocked ClientWrapper that only defines a custom description.
const mockUsbClient = createClientMock();
mockUsbClient.getDeviceDescription = () => {
return {
name: "TestBrand",
channel: "release",
version: "1.0",
};
};
// Mock the runtime-helper module to return mocked ClientWrappers for our test runtime
// ids.
const RuntimeClientFactoryMock = createRuntimeClientFactoryMock();
enableRuntimeClientFactoryMock(RuntimeClientFactoryMock);
RuntimeClientFactoryMock.createClientForRuntime = runtime => {
let client = null;
if (runtime.id === RUNTIMES.THIS_FIREFOX) {
client = createThisFirefoxClientMock();
} else if (runtime.id === RUNTIME_ID) {
client = mockUsbClient;
}
return { client };
};
// Disable mocks when the test ends.
registerCleanupFunction(() => {
disableRuntimeClientFactoryMock();
disableUsbRuntimesMock();
});
const { document, tab } = await openAboutDebugging();
usbRuntimesMock.getUSBRuntimes = function() {
return [{
id: RUNTIME_ID,
_socketPath: "test/path",
deviceName: "test device name",
shortName: "testshort",
}];
};
observerMock.emit("runtime-list-updated");
info("Wait until the USB sidebar item appears");
await waitUntil(() => findSidebarItemByText("test device name", document));
const usbRuntimeSidebarItem = findSidebarItemByText("test device name", document);
const connectButton = usbRuntimeSidebarItem.querySelector(".js-connect-button");
ok(connectButton, "Connect button is displayed for the USB runtime");
info("Click on the connect button and wait until it disappears");
connectButton.click();
await waitUntil(() => usbRuntimeSidebarItem.querySelector(".js-connect-button"));
info("Remove all USB runtimes");
usbRuntimesMock.getUSBRuntimes = function() {
return [];
};
observerMock.emit("runtime-list-updated");
info("Wait until the USB sidebar item disappears");
await waitUntil(() => !findSidebarItemByText("test device name", document));
await removeTab(tab);
});

Просмотреть файл

@ -0,0 +1,74 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* import-globals-from ../../../../shared/test/shared-head.js */
"use strict";
// This head file contains helpers to create mock versions of the ClientWrapper class
// defined at devtools/client/aboutdebugging-new/src/modules/client-wrapper.js .
const { RUNTIME_PREFERENCE } =
require("devtools/client/aboutdebugging-new/src/constants");
// Sensible default values for runtime preferences that should be usable in most
// situations
const DEFAULT_PREFERENCES = {
[RUNTIME_PREFERENCE.CONNECTION_PROMPT]: true,
};
// Creates a simple mock ClientWrapper.
function createClientMock() {
return {
// no-op
addListener: () => {},
// no-op
close: () => {},
// no-op
connect: () => {},
// no-op
getDeviceDescription: () => {},
// Return default preference value or null if no match.
getPreference: (prefName) => {
if (prefName in DEFAULT_PREFERENCES) {
return DEFAULT_PREFERENCES[prefName];
}
return null;
},
// Empty array of addons
listAddons: () => ({ addons: [] }),
// Empty array of tabs
listTabs: () => ({ tabs: []}),
// Empty arrays of workers
listWorkers: () => ({
otherWorkers: [],
serviceWorkers: [],
sharedWorkers: [],
}),
// no-op
removeListener: () => {},
// no-op
setPreference: () => {},
};
}
// Create a ClientWrapper mock that can be used to replace the this-firefox runtime.
function createThisFirefoxClientMock() {
const mockThisFirefoxDescription = {
name: "Firefox",
channel: "nightly",
version: "63.0",
};
// Create a fake about:debugging tab because our test helper openAboutDebugging
// waits until about:debugging is displayed in the list of tabs.
const mockAboutDebuggingTab = {
outerWindowID: 0,
url: "about:debugging",
};
const mockThisFirefoxClient = createClientMock();
mockThisFirefoxClient.listTabs = () => ({ tabs: [mockAboutDebuggingTab] });
mockThisFirefoxClient.getDeviceDescription = () => mockThisFirefoxDescription;
return mockThisFirefoxClient;
}

Просмотреть файл

@ -0,0 +1,39 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* import-globals-from ../../../../shared/test/shared-head.js */
"use strict";
/**
* Setup the loader to return the provided mock object instead of the regular
* runtime-client-factory module.
*
* @param {Object}
* mock should implement the following methods:
* - createClientForRuntime(runtime)
*/
function enableRuntimeClientFactoryMock(mock) {
const { setMockedModule } = require("devtools/client/aboutdebugging-new/src/modules/test-helper");
setMockedModule(mock, "modules/runtime-client-factory");
}
/**
* Update the loader to clear the mock entry for the runtime-client-factory module.
*/
function disableRuntimeClientFactoryMock() {
const { removeMockedModule } = require("devtools/client/aboutdebugging-new/src/modules/test-helper");
removeMockedModule("modules/runtime-client-factory");
}
/**
* Creates a simple mock version for runtime-client-factory, implementing all the expected
* methods with empty placeholders.
*/
function createRuntimeClientFactoryMock() {
const RuntimeClientFactoryMock = {};
RuntimeClientFactoryMock.createClientForRuntime = function(runtime) {
console.log("MOCKED METHOD createClientForRuntime");
};
return RuntimeClientFactoryMock;
}

Просмотреть файл

@ -0,0 +1,86 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* import-globals-from ../../../../shared/test/shared-head.js */
"use strict";
/**
* Setup the loader to return the provided mock object instead of the regular
* usb-runtimes module.
*
* @param {Object}
* mock should implement the following methods:
* - addUSBRuntimesObserver(listener)
* - disableUSBRuntimes()
* - enableUSBRuntimes()
* - getUSBRuntimes()
* - removeUSBRuntimesObserver(listener)
*/
function enableUsbRuntimesMock(mock) {
const { setMockedModule } = require("devtools/client/aboutdebugging-new/src/modules/test-helper");
setMockedModule(mock, "modules/usb-runtimes");
}
/**
* Update the loader to clear the mock entry for the usb-runtimes module.
*/
function disableUsbRuntimesMock() {
const { removeMockedModule } = require("devtools/client/aboutdebugging-new/src/modules/test-helper");
removeMockedModule("modules/usb-runtimes");
}
/**
* Creates a simple mock version for usb-runtimes, implementing all the expected methods
* with empty placeholders.
*/
function createUsbRuntimesMock() {
const usbRuntimesMock = {};
usbRuntimesMock.addUSBRuntimesObserver = function(listener) {
console.log("MOCKED METHOD addUSBRuntimesObserver");
};
usbRuntimesMock.disableUSBRuntimes = function() {
console.log("MOCKED METHOD disableUSBRuntimes");
};
usbRuntimesMock.enableUSBRuntimes = function() {
console.log("MOCKED METHOD enableUSBRuntimes");
};
usbRuntimesMock.getUSBRuntimes = function() {
console.log("MOCKED METHOD getUSBRuntimes");
};
usbRuntimesMock.removeUSBRuntimesObserver = function(listener) {
console.log("MOCKED METHOD removeUSBRuntimesObserver");
};
return usbRuntimesMock;
}
/**
* The usb-runtimes module allows to observer runtime updates. To simulate this behaviour
* the easiest is to use an EventEmitter-decorated object that can accept listeners and
* can emit events from the test.
*
* This method will update the addUSBRuntimesObserver method of the provided
* usbRuntimesMock in order to add listeners to a mockObserver, and returns said observer
* so that the test can emit "runtime-list-updated" when needed.
*/
function addObserverMock(usbRuntimesMock) {
const EventEmitter = require("devtools/shared/event-emitter");
const observerMock = {};
EventEmitter.decorate(observerMock);
usbRuntimesMock.addUSBRuntimesObserver = function(listener) {
console.log("MOCKED METHOD addUSBRuntimesObserver with mock scanner");
observerMock.on("runtime-list-updated", listener);
};
// NOTE FOR REVIEW: Instead of emitting "runtime-list-updated" events in the test,
// this mock could have a emitObservedEvent method, that would just emit the correct
// event. This way if the event name changes, everything remains contained in this
// method.
return observerMock;
}

Просмотреть файл

@ -388,6 +388,16 @@ Target.prototype = {
return this._inspector;
},
// Run callback on every front of this type that currently exists, and on every
// instantiation of front type in the future.
onFront(typeName, callback) {
const front = this.fronts.get(typeName);
if (front) {
return callback(front);
}
return this.on(typeName, callback);
},
// Get a Front for a target-scoped actor.
// i.e. an actor served by RootActor.listTabs or RootActorActor.getTab requests
getFront(typeName) {
@ -397,6 +407,7 @@ Target.prototype = {
return front;
}
front = getFront(this.client, typeName, this.form);
this.emit(typeName, front);
this.fronts.set(typeName, front);
return front;
},

Просмотреть файл

@ -77,6 +77,7 @@ skip-if = os == 'win' || debug # Bug 1282269, 1448084
[browser_source_map-late-script.js]
[browser_target_from_url.js]
[browser_target_events.js]
[browser_target_listeners.js]
[browser_target_remote.js]
[browser_target_support.js]
[browser_toolbox_custom_host.js]

Просмотреть файл

@ -0,0 +1,26 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
add_task(async function() {
gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
const target = await TargetFactory.forTab(gBrowser.selectedTab);
await target.attach();
info("Test applying onFront to a front that will be created");
const promise = new Promise(resolve => {
target.onFront("accessibility", resolve);
});
const getFrontFront = target.getFront("accessibility");
const onFrontFront = await promise;
is(getFrontFront, onFrontFront, "got the front instantiated in the future and it's the same");
info("Test applying onFront to an existing front");
await new Promise(resolve => {
target.onFront("accessibility", front => {
is(front, getFrontFront, "got the already instantiated front and it's the same");
resolve();
});
});
});

Просмотреть файл

@ -284,6 +284,25 @@ BrowsingContext::GetChildren(nsTArray<RefPtr<BrowsingContext>>& aChildren)
}
}
void
BrowsingContext::SetOpener(BrowsingContext* aOpener)
{
if (mOpener == aOpener) {
return;
}
mOpener = aOpener;
if (!XRE_IsContentProcess()) {
return;
}
auto cc = ContentChild::GetSingleton();
MOZ_DIAGNOSTIC_ASSERT(cc);
cc->SendSetOpenerBrowsingContext(BrowsingContextId(Id()),
BrowsingContextId(aOpener ? aOpener->Id() : 0));
}
/* static */ void
BrowsingContext::GetRootBrowsingContexts(nsTArray<RefPtr<BrowsingContext>>& aBrowsingContexts)
{

Просмотреть файл

@ -111,6 +111,13 @@ public:
void GetChildren(nsTArray<RefPtr<BrowsingContext>>& aChildren);
BrowsingContext* GetOpener()
{
return mOpener;
}
void SetOpener(BrowsingContext* aOpener);
static void GetRootBrowsingContexts(
nsTArray<RefPtr<BrowsingContext>>& aBrowsingContexts);
@ -141,6 +148,7 @@ private:
WeakPtr<BrowsingContext> mParent;
Children mChildren;
WeakPtr<BrowsingContext> mOpener;
nsCOMPtr<nsIDocShell> mDocShell;
nsString mName;
};

Просмотреть файл

@ -12,16 +12,14 @@
// which is the URL displayed in the location bar, i.e.
// the URI that the user attempted to load.
function getErrorCode()
{
function getErrorCode() {
var url = document.documentURI;
var error = url.search(/e\=/);
var duffUrl = url.search(/\&u\=/);
return decodeURIComponent(url.slice(error + 2, duffUrl));
}
function getCSSClass()
{
function getCSSClass() {
var url = document.documentURI;
var matches = url.match(/s\=([^&]+)\&/);
// s is optional, if no match just return nothing
@ -32,8 +30,7 @@
return decodeURIComponent(matches[1]);
}
function getDescription()
{
function getDescription() {
var url = document.documentURI;
var desc = url.search(/d\=/);
@ -45,8 +42,7 @@
return decodeURIComponent(url.slice(desc + 2));
}
function retryThis(buttonEl)
{
function retryThis(buttonEl) {
// Note: The application may wish to handle switching off "offline mode"
// before this event handler runs, but using a capturing event handler.
@ -63,23 +59,20 @@
buttonEl.disabled = true;
}
function initPage()
{
function initPage() {
var err = getErrorCode();
// if it's an unknown error or there's no title or description
// defined, get the generic message
var errTitle = document.getElementById("et_" + err);
var errDesc = document.getElementById("ed_" + err);
if (!errTitle || !errDesc)
{
if (!errTitle || !errDesc) {
errTitle = document.getElementById("et_generic");
errDesc = document.getElementById("ed_generic");
}
var title = document.getElementById("errorTitleText");
if (title)
{
if (title) {
title.parentNode.replaceChild(errTitle, title);
// change id to the replaced child's id so styling works
errTitle.id = "errorTitleText";
@ -90,8 +83,7 @@
sd.textContent = getDescription();
var ld = document.getElementById("errorLongDesc");
if (ld)
{
if (ld) {
ld.parentNode.replaceChild(errDesc, ld);
// change id to the replaced child's id so styling works
errDesc.id = "errorLongDesc";
@ -139,8 +131,7 @@
document.getElementById("errorTryAgain").style.display = "none";
document.getElementById("errorPageContainer").setAttribute("class", "certerror");
addDomainErrorLink();
}
else {
} else {
// Remove the override block for non-certificate errors. CSS-hiding
// isn't good enough here, because of bug 39098
var secOverride = document.getElementById("securityOverrideDiv");
@ -165,8 +156,8 @@
function showSecuritySection() {
// Swap link out, content in
document.getElementById('securityOverrideContent').style.display = '';
document.getElementById('securityOverrideLink').style.display = 'none';
document.getElementById("securityOverrideContent").style.display = "";
document.getElementById("securityOverrideLink").style.display = "none";
}
/* In the case of SSL error pages about domain mismatch, see if
@ -187,7 +178,7 @@
// use an over-greedy regex
var re = /<a id="cert_domain_link" title="([^"]+)">/;
var result = re.exec(desc);
if(!result)
if (!result)
return;
// Remove sd's existing children
@ -207,7 +198,7 @@
sd.appendChild(document.createTextNode(desc.slice(desc.indexOf("</a>") + "</a>".length)));
}
var link = document.getElementById('cert_domain_link');
var link = document.getElementById("cert_domain_link");
if (!link)
return;

Просмотреть файл

@ -7,16 +7,14 @@
<body onpageshow="showpageshowcount()">
<script>
var pageshowcount = 0;
function showpageshowcount()
{
pageshowcount++;
var div1 = document.getElementById("div1");
while (div1.firstChild)
{
div1.firstChild.remove();
}
div1.appendChild(document.createTextNode(
"pageshowcount: " + pageshowcount));
function showpageshowcount() {
pageshowcount++;
var div1 = document.getElementById("div1");
while (div1.firstChild) {
div1.firstChild.remove();
}
div1.appendChild(document.createTextNode(
"pageshowcount: " + pageshowcount));
}
</script>
<div id="div1">

Просмотреть файл

@ -7,6 +7,10 @@ for (var name of imports) {
window[name] = window.opener.wrappedJSObject[name];
}
ChromeUtils.import("resource://testing-common/BrowserTestUtils.jsm");
ChromeUtils.import("resource://gre/modules/Services.jsm");
// Some functions assume chrome-harness.js has been loaded.
/* import-globals-from ../../../testing/mochitest/chrome-harness.js */
/**
* Define global constants and variables.
@ -17,19 +21,19 @@ const NAV_FORWARD = 2;
const NAV_URI = 3;
const NAV_RELOAD = 4;
var gExpectedEvents; // an array of events which are expected to
var gExpectedEvents; // an array of events which are expected to
// be triggered by this navigation
var gUnexpectedEvents; // an array of event names which are NOT expected
var gUnexpectedEvents; // an array of event names which are NOT expected
// to be triggered by this navigation
var gFinalEvent; // true if the last expected event has fired
var gUrisNotInBFCache = []; // an array of uri's which shouldn't be stored
var gFinalEvent; // true if the last expected event has fired
var gUrisNotInBFCache = []; // an array of uri's which shouldn't be stored
// in the bfcache
var gNavType = NAV_NONE; // defines the most recent navigation type
var gNavType = NAV_NONE; // defines the most recent navigation type
// executed by doPageNavigation
var gOrigMaxTotalViewers = // original value of max_total_viewers,
undefined; // to be restored at end of test
undefined; // to be restored at end of test
var gExtractedPath = null; //used to cache file path for extracting files from a .jar file
var gExtractedPath = null; // used to cache file path for extracting files from a .jar file
/**
* The doPageNavigation() function performs page navigations asynchronously,
@ -88,6 +92,7 @@ var gExtractedPath = null; //used to cache file path for extracting files fro
* must contain an object for each pagehide and pageshow event which occurs as
* a result of this navigation.
*/
// eslint-disable-next-line complexity
function doPageNavigation(params) {
// Parse the parameters.
let back = params.back ? params.back : false;
@ -149,7 +154,7 @@ function doPageNavigation(params) {
// If the test explicitly sets .eventsToListenFor to [], don't wait for any
// events.
gFinalEvent = eventsToListenFor.length == 0 ? true : false;
gFinalEvent = eventsToListenFor.length == 0;
// Add an event listener for each type of event in the .eventsToListenFor
// property of the input parameters.
@ -163,30 +168,24 @@ function doPageNavigation(params) {
if (back) {
gNavType = NAV_BACK;
TestWindow.getBrowser().goBack();
}
else if (forward) {
} else if (forward) {
gNavType = NAV_FORWARD;
TestWindow.getBrowser().goForward();
}
else if (uri) {
} else if (uri) {
gNavType = NAV_URI;
BrowserTestUtils.loadURI(TestWindow.getBrowser(), uri);
}
else if (reload) {
} else if (reload) {
gNavType = NAV_RELOAD;
TestWindow.getBrowser().reload();
}
else if (waitOnly) {
} else if (waitOnly) {
gNavType = NAV_NONE;
}
else {
} else {
throw "No valid navigation type passed to doPageNavigation!";
}
// If we're listening for events and there is an .onNavComplete callback,
// wait for all events to occur, and then call doPageNavigation_complete().
if (eventsToListenFor.length > 0 && params.onNavComplete)
{
if (eventsToListenFor.length > 0 && params.onNavComplete) {
waitForTrue(
function() { return gFinalEvent; },
function() {
@ -257,7 +256,7 @@ function pageEventListener(event) {
try {
dump("TEST: eventListener received a " + event.type + " event for page " +
event.originalTarget.title + ", persisted=" + event.persisted + "\n");
} catch(e) {
} catch (e) {
// Ignore any exception.
}
@ -282,8 +281,7 @@ function pageEventListener(event) {
// If no expected events were specified, mark the final event as having been
// triggered when a pageshow event is fired; this will allow
// doPageNavigation() to return.
if ((typeof(gExpectedEvents) == "undefined") && event.type == "pageshow")
{
if ((typeof(gExpectedEvents) == "undefined") && event.type == "pageshow") {
waitForNextPaint(function() { gFinalEvent = true; });
return;
}
@ -347,9 +345,7 @@ function finish() {
// If the test changed the value of max_total_viewers via a call to
// enableBFCache(), then restore it now.
if (typeof(gOrigMaxTotalViewers) != "undefined") {
var prefs = Cc["@mozilla.org/preferences-service;1"]
.getService(Ci.nsIPrefBranch);
prefs.setIntPref("browser.sessionhistory.max_total_viewers",
Services.prefs.setIntPref("browser.sessionhistory.max_total_viewers",
gOrigMaxTotalViewers);
}
@ -358,11 +354,9 @@ function finish() {
let SimpleTest = opener.wrappedJSObject.SimpleTest;
// Wait for the window to be closed before finishing the test
let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"]
.getService(Ci.nsIWindowWatcher);
ww.registerNotification(function(subject, topic, data) {
Services.ww.registerNotification(function observer(subject, topic, data) {
if (topic == "domwindowclosed") {
ww.unregisterNotification(arguments.callee);
Services.ww.unregisterNotification(observer);
SimpleTest.waitForFocus(SimpleTest.finish, opener);
}
});
@ -405,7 +399,7 @@ function waitForTrue(fn, onWaitComplete, timeout) {
var timeoutHit = false;
if (typeof(timeout) != "undefined") {
timeoutHit = new Date().valueOf() - start >=
timeout ? true : false;
timeout;
if (timeoutHit) {
ok(false, "Timed out waiting for condition");
}
@ -431,25 +425,21 @@ function waitForNextPaint(cb) {
* to 0 (disabled), if a number, set it to that specific number
*/
function enableBFCache(enable) {
var prefs = Cc["@mozilla.org/preferences-service;1"]
.getService(Ci.nsIPrefBranch);
// If this is the first time the test called enableBFCache(),
// store the original value of max_total_viewers, so it can
// be restored at the end of the test.
if (typeof(gOrigMaxTotalViewers) == "undefined") {
gOrigMaxTotalViewers =
prefs.getIntPref("browser.sessionhistory.max_total_viewers");
Services.prefs.getIntPref("browser.sessionhistory.max_total_viewers");
}
if (typeof(enable) == "boolean") {
if (enable)
prefs.setIntPref("browser.sessionhistory.max_total_viewers", -1);
Services.prefs.setIntPref("browser.sessionhistory.max_total_viewers", -1);
else
prefs.setIntPref("browser.sessionhistory.max_total_viewers", 0);
}
else if (typeof(enable) == "number") {
prefs.setIntPref("browser.sessionhistory.max_total_viewers", enable);
Services.prefs.setIntPref("browser.sessionhistory.max_total_viewers", 0);
} else if (typeof(enable) == "number") {
Services.prefs.setIntPref("browser.sessionhistory.max_total_viewers", enable);
}
}
@ -470,7 +460,7 @@ function getHttpRoot() {
} else {
return null;
}
return "file://" + gExtractedPath + '/';
return "file://" + gExtractedPath + "/";
}
/**
@ -490,12 +480,12 @@ function getHttpUrl(filename) {
* browser, and document.
*/
var TestWindow = {};
TestWindow.getWindow = function () {
TestWindow.getWindow = function() {
return document.getElementById("content").contentWindow;
}
TestWindow.getBrowser = function () {
};
TestWindow.getBrowser = function() {
return document.getElementById("content");
}
TestWindow.getDocument = function () {
};
TestWindow.getDocument = function() {
return document.getElementById("content").contentDocument;
}
};

Просмотреть файл

@ -55,7 +55,7 @@ function loadIframe(iframe) {
}
var progressListener = {
onStateChange: function (webProgress, req, flags, status) {
onStateChange(webProgress, req, flags, status) {
if (!(flags & Ci.nsIWebProgressListener.STATE_STOP))
return;
is(Components.isSuccessCode(status), false,
@ -64,16 +64,7 @@ var progressListener = {
runNextTest();
},
QueryInterface: function (iid) {
var iids = [
Ci.nsIWebProgressListener,
Ci.nsISupportsWeakReference,
Ci.nsISupports,
];
if (iids.some(function (i) { return iid.equals(i); }))
return this;
throw Cr.NS_ERROR_NO_INTERFACE;
},
QueryInterface: ChromeUtils.generateQI(["nsIWebProgressListener", "nsISupportsWeakReference"]),
};
</script>

Просмотреть файл

@ -22,7 +22,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=428288
function makeClick() {
var event = document.createEvent("MouseEvents");
event.initMouseEvent("click", true, true, window, 0, 0,0,0,0,
event.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0,
false, false, false, false, 0, null);
document.getElementById("crashy").dispatchEvent(event);
return true;

Просмотреть файл

@ -9,7 +9,7 @@
SimpleTest.waitForExplicitFinish();
addLoadEvent(test);
ChromeUtils.import('resource://gre/modules/XPCOMUtils.jsm');
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
// The default flags we will stick on the docShell - every request made by the
// docShell should include those flags.
@ -41,8 +41,8 @@ function test() {
// an nsIWebProgressListener that checks all requests made by the docShell
// have the flags we expect.
RequestWatcher = {
init: function(docShell, callback) {
var RequestWatcher = {
init(docShell, callback) {
this.callback = callback;
this.docShell = docShell;
docShell.
@ -60,7 +60,7 @@ RequestWatcher = {
"http://mochi.test:8888/tests/SimpleTest/test.css",
"http://mochi.test:8888/tests/docshell/test/chrome/red.png",
// the content of an iframe in the test html.
"http://mochi.test:8888/chrome/docshell/test/chrome/generic.html"
"http://mochi.test:8888/chrome/docshell/test/chrome/generic.html",
]) {
this.requestCounts[url] = 0;
}
@ -68,7 +68,7 @@ RequestWatcher = {
// Finalize the test after we detect a completed load. We check we saw the
// correct requests and make a callback to exit.
finalize: function() {
finalize() {
ok(Object.keys(this.requestCounts).length, "we expected some requests");
for (var url in this.requestCounts) {
var count = this.requestCounts[url];
@ -86,7 +86,7 @@ RequestWatcher = {
this.callback();
},
onStateChange: function (webProgress, req, flags, status) {
onStateChange(webProgress, req, flags, status) {
// We are checking requests - if there isn't one, ignore it.
if (!req) {
return;
@ -112,8 +112,8 @@ RequestWatcher = {
QueryInterface: ChromeUtils.generateQI([
Ci.nsIWebProgressListener,
Ci.nsISupportsWeakReference,
])
}
]),
};
</script>
</head>

Просмотреть файл

@ -40,8 +40,8 @@ if (isNotLoaded()) {
}
function onHiddenPrivateWindowReady() {
var iframe = hidden.document.createElement('iframe');
iframe.src = 'generic.html';
var iframe = hidden.document.createElement("iframe");
iframe.src = "generic.html";
hidden.document.body.appendChild(iframe);
var win = mainWindow.OpenBrowserWindow({private: true});

Просмотреть файл

@ -2243,6 +2243,9 @@ nsGlobalWindowOuter::SetOpenerWindow(nsPIDOMWindowOuter* aOpener,
{
nsWeakPtr opener = do_GetWeakReference(aOpener);
if (opener == mOpener) {
MOZ_DIAGNOSTIC_ASSERT(
!aOpener || (GetBrowsingContext() && GetBrowsingContext()->GetOpener() ==
aOpener->GetBrowsingContext()));
return;
}
@ -2255,6 +2258,13 @@ nsGlobalWindowOuter::SetOpenerWindow(nsPIDOMWindowOuter* aOpener,
mOpener = opener.forget();
NS_ASSERTION(mOpener || !aOpener, "Opener must support weak references!");
if (mDocShell && aOpener) {
// TODO(farre): Here we really wish to only consider the case
// where 'aOriginalOpener' is false, and we also really want to
// move opener entirely to BrowsingContext. See bug 1502330.
GetBrowsingContext()->SetOpener(aOpener->GetBrowsingContext());
}
// Check that the js visible opener matches! We currently don't depend on this
// being true outside of nightly, so we disable the assertion in optimized
// release / beta builds.
@ -7868,3 +7878,9 @@ nsAutoPopupStatePusherInternal::~nsAutoPopupStatePusherInternal()
{
nsContentUtils::PopPopupControlState(mOldState);
}
mozilla::dom::BrowsingContext*
nsPIDOMWindowOuter::GetBrowsingContext() const
{
return mDocShell ? nsDocShell::Cast(mDocShell)->GetBrowsingContext() : nullptr;
}

Просмотреть файл

@ -48,6 +48,7 @@ namespace mozilla {
class AutoplayPermissionManager;
namespace dom {
class AudioContext;
class BrowsingContext;
class ClientInfo;
class ClientState;
class ContentFrameMessageManager;
@ -929,6 +930,8 @@ public:
*/
inline nsIDocShell *GetDocShell() const;
mozilla::dom::BrowsingContext* GetBrowsingContext() const;
/**
* Set a new document in the window. Calling this method will in most cases
* create a new inner window. This may be called with a pointer to the current

Просмотреть файл

@ -14,4 +14,6 @@ interface BrowsingContext {
readonly attribute nsIDocShell? docShell;
readonly attribute unsigned long long id;
readonly attribute BrowsingContext? opener;
};

Просмотреть файл

@ -6080,3 +6080,39 @@ ContentParent::RecvDetachBrowsingContext(const BrowsingContextId& aContextId,
return IPC_OK();
}
mozilla::ipc::IPCResult
ContentParent::RecvSetOpenerBrowsingContext(
const BrowsingContextId& aContextId,
const BrowsingContextId& aOpenerContextId)
{
RefPtr<ChromeBrowsingContext> context = ChromeBrowsingContext::Get(aContextId);
if (!context) {
MOZ_LOG(BrowsingContext::GetLog(),
LogLevel::Debug,
("ParentIPC: Trying to set opener already detached 0x%08" PRIx64,
(uint64_t)aContextId));
return IPC_OK();
}
if (!context->IsOwnedByProcess(ChildID())) {
// Where trying to set opener on a child BrowsingContext in
// another child process. This is illegal since the owner of the
// BrowsingContext is the proccess with the in-process docshell,
// which is tracked by OwnerProcessId.
// TODO(farre): To crash or not to crash. Same reasoning as in
// above TODO. [Bug 1471598]
MOZ_LOG(BrowsingContext::GetLog(),
LogLevel::Warning,
("ParentIPC: Trying to set opener on out of process context 0x%08" PRIx64,
context->Id()));
return IPC_OK();
}
RefPtr<BrowsingContext> opener = BrowsingContext::Get(aOpenerContextId);
context->SetOpener(opener);
return IPC_OK();
}

Просмотреть файл

@ -685,6 +685,10 @@ public:
const BrowsingContextId& aContextId,
const bool& aMoveToBFCache) override;
virtual mozilla::ipc::IPCResult RecvSetOpenerBrowsingContext(
const BrowsingContextId& aContextId,
const BrowsingContextId& aOpenerContextId) override;
protected:
void OnChannelConnected(int32_t pid) override;

Просмотреть файл

@ -1187,6 +1187,14 @@ parent:
*/
async DetachBrowsingContext(BrowsingContextId aContextId,
bool aMoveToBFCache);
/**
* Set the opener of browsing context with id 'aContextId' to the
* browsing context with id 'aOpenerId'.
*/
async SetOpenerBrowsingContext(BrowsingContextId aContextId,
BrowsingContextId aOpenerContextId);
both:
async AsyncMessage(nsString aMessage, CpowEntry[] aCpows,
Principal aPrincipal, ClonedMessageData aData);

Просмотреть файл

@ -60,6 +60,10 @@ skip-if = !e10s || verify # This is a test of e10s functionality.
support-files =
set-samesite-cookies-and-redirect.sjs
mimeme.sjs
[browser_persist_mixed_content_image.js]
support-files =
test_mixed_content_image.html
dummy.png
[browser_test_focus_after_modal_state.js]
skip-if = verify
support-files =

Просмотреть файл

@ -0,0 +1,102 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const TEST_PATH = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://example.org");
var MockFilePicker = SpecialPowers.MockFilePicker;
MockFilePicker.init(window);
registerCleanupFunction(async function() {
info("Running the cleanup code");
MockFilePicker.cleanup();
if (gTestDir && gTestDir.exists()) {
// On Windows, sometimes nsIFile.remove() throws, probably because we're
// still writing to the directory we're trying to remove, despite
// waiting for the download to complete. Just retry a bit later...
let succeeded = false;
while (!succeeded) {
try {
gTestDir.remove(true);
succeeded = true;
} catch (ex) {
await new Promise(requestAnimationFrame);
}
}
}
});
let gTestDir = null;
function createTemporarySaveDirectory() {
var saveDir = Services.dirsvc.get("TmpD", Ci.nsIFile);
saveDir.append("testsavedir");
if (!saveDir.exists()) {
info("create testsavedir!");
saveDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
}
info("return from createTempSaveDir: " + saveDir.path);
return saveDir;
}
add_task(async function test_image_download() {
await BrowserTestUtils.withNewTab(TEST_PATH + "test_mixed_content_image.html", async (browser) => {
// Add the image, and wait for it to load.
await ContentTask.spawn(browser, null, function() {
let loc = content.document.location.href;
let httpRoot = loc.replace("https", "http");
let imgloc = new content.URL("dummy.png", httpRoot);
let img = content.document.createElement("img");
img.src = imgloc;
return new Promise(resolve => {
img.onload = resolve;
content.document.body.appendChild(img);
});
});
gTestDir = createTemporarySaveDirectory();
let destFile = gTestDir.clone();
MockFilePicker.displayDirectory = gTestDir;
let fileName;
MockFilePicker.showCallback = function(fp) {
info("showCallback");
fileName = fp.defaultString;
info("fileName: " + fileName);
destFile.append(fileName);
info("path: " + destFile.path);
MockFilePicker.setFiles([destFile]);
MockFilePicker.filterIndex = 0; // just save the file
info("done showCallback");
};
let downloadFinishedPromise = new Promise(async (resolve) => {
let dls = await Downloads.getList(Downloads.PUBLIC);
dls.addView({
onDownloadChanged(download) {
info("Download changed!");
if (download.succeeded || download.error) {
info("Download succeeded or errored");
dls.removeView(this);
dls.removeFinished();
resolve(download);
}
}
});
});
// open the context menu.
let popup = document.getElementById("contentAreaContextMenu");
let popupShown = BrowserTestUtils.waitForEvent(popup, "popupshown");
BrowserTestUtils.synthesizeMouseAtCenter("img", {type: "contextmenu", button: 2}, browser);
await popupShown;
let popupHidden = BrowserTestUtils.waitForEvent(popup, "popuphidden");
popup.querySelector("#context-saveimage").click();
popup.hidePopup();
await popupHidden;
info("Context menu hidden, waiting for download to finish");
let imageDownload = await downloadFinishedPromise;
ok(imageDownload.succeeded, "Image should have downloaded successfully");
});
});

Двоичные данные
dom/tests/browser/dummy.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 703 B

Просмотреть файл

@ -0,0 +1 @@
<body></body>

Просмотреть файл

@ -1358,7 +1358,7 @@ nsresult nsWebBrowserPersist::SaveURIInternal(
aURI,
aTriggeringPrincipal,
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
nsIContentPolicy::TYPE_OTHER,
nsIContentPolicy::TYPE_SAVEAS_DOWNLOAD,
nullptr, // aPerformanceStorage
nullptr, // aLoadGroup
static_cast<nsIInterfaceRequestor*>(this),

Просмотреть файл

@ -431,7 +431,7 @@ pub struct PicturePrimitive {
/// The spatial node index of this picture when it is
/// composited into the parent picture.
spatial_node_index: SpatialNodeIndex,
pub spatial_node_index: SpatialNodeIndex,
/// The local rect of this picture. It is built
/// dynamically during the first picture traversal.
@ -670,31 +670,29 @@ impl PicturePrimitive {
pub fn add_split_plane(
splitter: &mut PlaneSplitter,
transforms: &TransformPalette,
prim_instance: &PrimitiveInstance,
original_local_rect: LayoutRect,
local_rect: LayoutRect,
spatial_node_index: SpatialNodeIndex,
plane_split_anchor: usize,
world_bounds: WorldRect,
) -> bool {
let transform = transforms
.get_world_transform(prim_instance.spatial_node_index);
let matrix = transform.cast();
// If the picture isn't visible, then ensure it's not added
// to the plane splitter, to avoid assertions during batching
// about each split plane having a surface.
if local_rect.size.width <= 0.0 ||
local_rect.size.height <= 0.0 {
return false;
}
// Apply the local clip rect here, before splitting. This is
// because the local clip rect can't be applied in the vertex
// shader for split composites, since we are drawing polygons
// rather that rectangles. The interpolation still works correctly
// since we determine the UVs by doing a bilerp with a factor
// from the original local rect.
let local_rect = match original_local_rect
.intersection(&prim_instance.combined_local_clip_rect)
{
Some(rect) => rect.cast(),
None => return false,
};
let transform = transforms
.get_world_transform(spatial_node_index);
let matrix = transform.cast();
let local_rect = local_rect.cast();
let world_bounds = world_bounds.cast();
match transform.transform_kind() {
TransformedRectKind::AxisAligned => {
let inv_transform = transforms
.get_world_inv_transform(prim_instance.spatial_node_index);
.get_world_inv_transform(spatial_node_index);
let polygon = Polygon::from_transformed_rect_with_inverse(
local_rect,
&matrix,
@ -711,7 +709,7 @@ impl PicturePrimitive {
plane_split_anchor,
),
&matrix,
prim_instance.clipped_world_rect.map(|r| r.to_f64()),
Some(world_bounds),
);
if let Ok(results) = results {
for poly in results {

Просмотреть файл

@ -2341,9 +2341,10 @@ impl PrimitiveStore {
PicturePrimitive::add_split_plane(
splitter,
frame_state.transforms,
prim_instance,
prim_local_rect,
pic.spatial_node_index,
plane_split_anchor,
frame_context.world_rect,
);
}
} else {

Просмотреть файл

@ -9,8 +9,9 @@ use serde::de::Deserializer;
#[cfg(feature = "serialize")]
use serde::ser::{Serializer, SerializeSeq};
use serde::{Deserialize, Serialize};
use std::io::{Read, Write};
use std::io::{Read, stdout, Write};
use std::marker::PhantomData;
use std::ops::Range;
use std::{io, mem, ptr, slice};
use time::precise_time_ns;
use {AlphaType, BorderDetails, BorderDisplayItem, BorderRadius, BoxShadowClipMode};
@ -924,20 +925,31 @@ impl DisplayListBuilder {
self.save_state.take().expect("No save to clear in DisplayListBuilder");
}
/// Print the display items in the list to stderr. If the start parameter
/// is specified, only display items starting at that index (inclusive) will
/// be printed. If the end parameter is specified, only display items before
/// that index (exclusive) will be printed. Calling this function with
/// end <= start is allowed but is just a waste of CPU cycles.
/// This function returns the total number of items in the display list, which
/// allows the caller to subsequently invoke this function to only dump the
/// newly-added items.
pub fn print_display_list(
/// Print the display items in the list to stdout.
pub fn print_display_list(&mut self) {
self.emit_display_list(0, Range { start: None, end: None }, stdout());
}
/// Emits a debug representation of display items in the list, for debugging
/// purposes. If the range's start parameter is specified, only display
/// items starting at that index (inclusive) will be printed. If the range's
/// end parameter is specified, only display items before that index
/// (exclusive) will be printed. Calling this function with end <= start is
/// allowed but is just a waste of CPU cycles. The function emits the
/// debug representation of the selected display items, one per line, with
/// the given indent, to the provided sink object. The return value is
/// the total number of items in the display list, which allows the
/// caller to subsequently invoke this function to only dump the newly-added
/// items.
pub fn emit_display_list<W>(
&mut self,
indent: usize,
start: Option<usize>,
end: Option<usize>,
) -> usize {
range: Range<Option<usize>>,
mut sink: W,
) -> usize
where
W: Write
{
let mut temp = BuiltDisplayList::default();
mem::swap(&mut temp.data, &mut self.data);
@ -945,8 +957,8 @@ impl DisplayListBuilder {
{
let mut iter = BuiltDisplayListIter::new(&temp);
while let Some(item) = iter.next_raw() {
if index >= start.unwrap_or(0) && end.map_or(true, |e| index < e) {
eprintln!("{}{:?}", " ".repeat(indent), item.display_item());
if index >= range.start.unwrap_or(0) && range.end.map_or(true, |e| index < e) {
writeln!(sink, "{}{:?}", " ".repeat(indent), item.display_item());
}
index += 1;
}

Просмотреть файл

@ -39,4 +39,4 @@ derive_helper_methods = true
[defines]
"target_os = windows" = "XP_WIN"
"target_os = macos" = "XP_MACOSX"
"target_os = android" = "ANDROID"

Просмотреть файл

@ -1 +1 @@
b8829189cfc1769550c9ab4a4bb994e28621f009
874d782a891db37707364be2071d91117c6e255a

Просмотреть файл

@ -1,11 +1,15 @@
use std::ffi::{CStr, CString};
use std::io::Cursor;
use std::{mem, slice, ptr, env};
use std::path::PathBuf;
use std::rc::Rc;
use std::cell::RefCell;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::ops::Range;
use std::os::raw::{c_void, c_char, c_float};
#[cfg(target_os = "android")]
use std::os::raw::{c_int};
use gleam::gl;
use webrender::api::*;
@ -32,6 +36,11 @@ use core_foundation::string::CFString;
#[cfg(target_os = "macos")]
use core_graphics::font::CGFont;
extern "C" {
#[cfg(target_os = "android")]
fn __android_log_write(prio: c_int, tag: *const c_char, text: *const c_char) -> c_int;
}
/// The unique id for WR resource identification.
static NEXT_NAMESPACE_ID: AtomicUsize = AtomicUsize::new(1);
@ -2563,9 +2572,26 @@ pub extern "C" fn wr_dump_display_list(state: &mut WrState,
end: *const usize) -> usize {
let start = unsafe { start.as_ref().cloned() };
let end = unsafe { end.as_ref().cloned() };
state.frame_builder
.dl_builder
.print_display_list(indent, start, end)
let range = Range { start, end };
let mut sink = Cursor::new(Vec::new());
let index = state.frame_builder
.dl_builder
.emit_display_list(indent, range, &mut sink);
// For Android, dump to logcat instead of stderr. This is the same as
// what printf_stderr does on the C++ side.
#[cfg(target_os = "android")]
unsafe {
__android_log_write(4 /* info */,
CString::new("Gecko").unwrap().as_ptr(),
CString::new(sink.into_inner()).unwrap().as_ptr());
}
#[cfg(not(target_os = "android"))]
eprint!("{}", String::from_utf8(sink.into_inner()).unwrap());
index
}
#[no_mangle]

Просмотреть файл

@ -1108,6 +1108,12 @@ extern void DeleteBlobFont(WrFontInstanceKey aKey);
extern void DeleteFontData(WrFontKey aKey);
#if defined(ANDROID)
extern int __android_log_write(int aPrio,
const char *aTag,
const char *aText);
#endif
extern void apz_deregister_sampler(WrWindowId aWindowId);
extern void apz_deregister_updater(WrWindowId aWindowId);

Просмотреть файл

@ -664,6 +664,25 @@ class LifoAlloc
return allocImpl(n);
}
// Allocates |n| bytes if we can guarantee that we will have
// |needed| unused bytes remaining. Returns nullptr if we can't.
// This is useful for maintaining our ballast invariants while
// attempting fallible allocations.
MOZ_ALWAYS_INLINE
void* allocEnsureUnused(size_t n, size_t needed) {
JS_OOM_POSSIBLY_FAIL();
MOZ_ASSERT(fallibleScope_);
detail::BumpChunk::Mark m = mark();
void *result = allocImpl(n);
if (!ensureUnusedApproximate(needed)) {
release(m);
return nullptr;
}
cancelMark(m);
return result;
}
template<typename T, typename... Args>
MOZ_ALWAYS_INLINE T*
allocInSize(size_t n, Args&&... args)
@ -802,6 +821,12 @@ class LifoAlloc
}
}
private:
void cancelMark(Mark mark) {
markCount--;
}
public:
void releaseAll() {
MOZ_ASSERT(!markCount);
for (detail::BumpChunk& bc : chunks_) {

Просмотреть файл

@ -2196,6 +2196,9 @@ BytecodeEmitter::allocateResumeIndex(ptrdiff_t offset, uint32_t* resumeIndex)
static_assert(MaxResumeIndex < uint32_t(GeneratorObject::RESUME_INDEX_CLOSING),
"resumeIndex should not include magic GeneratorObject resumeIndex values");
static_assert(MaxResumeIndex <= INT32_MAX / sizeof(uintptr_t),
"resumeIndex * sizeof(uintptr_t) must fit in an int32. JIT code relies "
"on this when loading resume entries from BaselineScript");
*resumeIndex = resumeOffsetList.length();
if (*resumeIndex > MaxResumeIndex) {

Просмотреть файл

@ -3949,6 +3949,18 @@ BaselineCompiler::emit_JSOP_GOSUB()
return true;
}
static void
LoadBaselineScriptResumeEntries(MacroAssembler& masm, JSScript* script, Register dest,
Register scratch)
{
MOZ_ASSERT(dest != scratch);
masm.movePtr(ImmGCPtr(script), dest);
masm.loadPtr(Address(dest, JSScript::offsetOfBaselineScript()), dest);
masm.load32(Address(dest, BaselineScript::offsetOfResumeEntriesOffset()), scratch);
masm.addPtr(scratch, dest);
}
bool
BaselineCompiler::emit_JSOP_RETSUB()
{
@ -3969,10 +3981,7 @@ BaselineCompiler::emit_JSOP_RETSUB()
// R0 is |false|. R1 contains the resumeIndex to jump to.
Register scratch1 = R2.scratchReg();
Register scratch2 = R0.scratchReg();
masm.movePtr(ImmGCPtr(script), scratch1);
masm.loadPtr(Address(scratch1, JSScript::offsetOfBaselineScript()), scratch1);
masm.load32(Address(scratch1, BaselineScript::offsetOfResumeEntriesOffset()), scratch2);
masm.addPtr(scratch2, scratch1);
LoadBaselineScriptResumeEntries(masm, script, scratch1, scratch2);
masm.unboxInt32(R1, scratch2);
masm.loadPtr(BaseIndex(scratch1, scratch2, ScaleFromElemWidth(sizeof(uintptr_t))), scratch1);
masm.jump(scratch1);
@ -4488,9 +4497,44 @@ BaselineCompiler::emit_JSOP_TABLESWITCH()
{
frame.popRegsAndSync(1);
// Call IC.
ICTableSwitch::Compiler compiler(cx, script, pc);
return emitOpIC(compiler.getStub(&stubSpace_));
jsbytecode* defaultpc = pc + GET_JUMP_OFFSET(pc);
Label* defaultLabel = labelOf(defaultpc);
int32_t low = GET_JUMP_OFFSET(pc + 1 * JUMP_OFFSET_LEN);
int32_t high = GET_JUMP_OFFSET(pc + 2 * JUMP_OFFSET_LEN);
uint32_t firstResumeIndex = GET_RESUMEINDEX(pc + 3 * JUMP_OFFSET_LEN);
int32_t length = high - low + 1;
Register key = R0.scratchReg();
Register scratch1 = R1.scratchReg();
Register scratch2 = R2.scratchReg();
// Call a stub to convert R0 from double to int32 if needed.
// Note: this stub may clobber scratch1.
masm.call(cx->runtime()->jitRuntime()->getDoubleToInt32ValueStub());
// Jump to the 'default' pc if not int32 (tableswitch is only used when
// all cases are int32).
masm.branchTestInt32(Assembler::NotEqual, R0, defaultLabel);
masm.unboxInt32(R0, key);
// Subtract 'low'. Bounds check.
if (low != 0) {
masm.sub32(Imm32(low), key);
}
masm.branch32(Assembler::AboveOrEqual, key, Imm32(length), defaultLabel);
// Jump to resumeEntries[firstResumeIndex + key].
//
// Note: BytecodeEmitter::allocateResumeIndex static_asserts
// |firstResumeIndex * sizeof(uintptr_t)| fits in int32_t.
LoadBaselineScriptResumeEntries(masm, script, scratch1, scratch2);
masm.loadPtr(BaseIndex(scratch1, key, ScaleFromElemWidth(sizeof(uintptr_t)),
firstResumeIndex * sizeof(uintptr_t)), scratch1);
masm.jump(scratch1);
return true;
}
bool

Просмотреть файл

@ -5142,91 +5142,6 @@ ICCall_ScriptedFunCall::Compiler::generateStubCode(MacroAssembler& masm)
return true;
}
bool
ICTableSwitch::Compiler::generateStubCode(MacroAssembler& masm)
{
Label isInt32, notInt32, outOfRange;
Register scratch = R1.scratchReg();
masm.branchTestInt32(Assembler::NotEqual, R0, &notInt32);
Register key = masm.extractInt32(R0, ExtractTemp0);
masm.bind(&isInt32);
masm.load32(Address(ICStubReg, offsetof(ICTableSwitch, min_)), scratch);
masm.sub32(scratch, key);
masm.branch32(Assembler::BelowOrEqual,
Address(ICStubReg, offsetof(ICTableSwitch, length_)), key, &outOfRange);
masm.loadPtr(Address(ICStubReg, offsetof(ICTableSwitch, table_)), scratch);
masm.loadPtr(BaseIndex(scratch, key, ScalePointer), scratch);
EmitChangeICReturnAddress(masm, scratch);
EmitReturnFromIC(masm);
masm.bind(&notInt32);
masm.branchTestDouble(Assembler::NotEqual, R0, &outOfRange);
masm.unboxDouble(R0, FloatReg0);
// N.B. -0 === 0, so convert -0 to a 0 int32.
masm.convertDoubleToInt32(FloatReg0, key, &outOfRange, /* negativeZeroCheck = */ false);
masm.jump(&isInt32);
masm.bind(&outOfRange);
masm.loadPtr(Address(ICStubReg, offsetof(ICTableSwitch, defaultTarget_)), scratch);
EmitChangeICReturnAddress(masm, scratch);
EmitReturnFromIC(masm);
return true;
}
ICStub*
ICTableSwitch::Compiler::getStub(ICStubSpace* space)
{
JitCode* code = getStubCode();
if (!code) {
return nullptr;
}
jsbytecode* pc = pc_;
pc += JUMP_OFFSET_LEN;
int32_t low = GET_JUMP_OFFSET(pc);
pc += JUMP_OFFSET_LEN;
int32_t high = GET_JUMP_OFFSET(pc);
int32_t length = high - low + 1;
pc += JUMP_OFFSET_LEN;
void** table = (void**) space->alloc(sizeof(void*) * length);
if (!table) {
ReportOutOfMemory(cx);
return nullptr;
}
jsbytecode* defaultpc = pc_ + GET_JUMP_OFFSET(pc_);
for (int32_t i = 0; i < length; i++) {
table[i] = script_->tableSwitchCasePC(pc_, i);
}
return newStub<ICTableSwitch>(space, code, table, low, length, defaultpc);
}
void
ICTableSwitch::fixupJumpTable(JSScript* script, BaselineScript* baseline)
{
PCMappingSlotInfo slotInfo;
defaultTarget_ = baseline->nativeCodeForPC(script, (jsbytecode*) defaultTarget_, &slotInfo);
MOZ_ASSERT(slotInfo.isStackSynced());
for (int32_t i = 0; i < length_; i++) {
table_[i] = baseline->nativeCodeForPC(script, (jsbytecode*) table_[i], &slotInfo);
MOZ_ASSERT(slotInfo.isStackSynced());
}
}
//
// GetIterator_Fallback
//

Просмотреть файл

@ -2614,42 +2614,6 @@ class ICCall_IsSuspendedGenerator : public ICStub
};
};
// Stub for performing a TableSwitch, updating the IC's return address to jump
// to whatever point the switch is branching to.
class ICTableSwitch : public ICStub
{
friend class ICStubSpace;
protected: // Protected to silence Clang warning.
void** table_;
int32_t min_;
int32_t length_;
void* defaultTarget_;
ICTableSwitch(JitCode* stubCode, void** table,
int32_t min, int32_t length, void* defaultTarget)
: ICStub(TableSwitch, stubCode), table_(table),
min_(min), length_(length), defaultTarget_(defaultTarget)
{}
public:
void fixupJumpTable(JSScript* script, BaselineScript* baseline);
class Compiler : public ICStubCompiler {
MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
JSScript* script_;
jsbytecode* pc_;
public:
Compiler(JSContext* cx, JSScript* script, jsbytecode* pc)
: ICStubCompiler(cx, ICStub::TableSwitch), script_(script), pc_(pc)
{}
ICStub* getStub(ICStubSpace* space) override;
};
};
// IC for constructing an iterator from an input value.
class ICGetIterator_Fallback : public ICFallbackStub
{

Просмотреть файл

@ -61,8 +61,6 @@ namespace jit {
\
_(SetProp_Fallback) \
\
_(TableSwitch) \
\
_(GetIterator_Fallback) \
_(IteratorMore_Fallback) \
_(IteratorMore_Native) \

Просмотреть файл

@ -820,11 +820,6 @@ BaselineScript::copyICEntries(JSScript* script, const ICEntry* entries)
ICTypeMonitor_Fallback* stub = realEntry.firstStub()->toTypeMonitor_Fallback();
stub->fixupICEntry(&realEntry);
}
if (realEntry.firstStub()->isTableSwitch()) {
ICTableSwitch* stub = realEntry.firstStub()->toTableSwitch();
stub->fixupJumpTable(script, this);
}
}
}
@ -1150,7 +1145,7 @@ BaselineScript::purgeOptimizedStubs(Zone* zone)
} else if (lastStub->isTypeMonitor_Fallback()) {
lastStub->toTypeMonitor_Fallback()->resetMonitorStubChain(zone);
} else {
MOZ_ASSERT(lastStub->isTableSwitch());
MOZ_CRASH("Unknown fallback stub");
}
}

Просмотреть файл

@ -9008,6 +9008,23 @@ JitRuntime::generateInterpreterStub(MacroAssembler& masm)
masm.ret();
}
void
JitRuntime::generateDoubleToInt32ValueStub(MacroAssembler& masm)
{
doubleToInt32ValueStubOffset_ = startTrampolineCode(masm);
Label done;
masm.branchTestDouble(Assembler::NotEqual, R0, &done);
masm.unboxDouble(R0, FloatReg0);
masm.convertDoubleToInt32(FloatReg0, R1.scratchReg(), &done,
/* negativeZeroCheck = */ false);
masm.tagValue(JSVAL_TYPE_INT32, R1.scratchReg(), R0);
masm.bind(&done);
masm.abiret();
}
bool
JitRuntime::generateTLEventVM(MacroAssembler& masm, const VMFunction& f, bool enter)
{

Просмотреть файл

@ -179,6 +179,7 @@ JitRuntime::JitRuntime()
invalidatorOffset_(0),
lazyLinkStubOffset_(0),
interpreterStubOffset_(0),
doubleToInt32ValueStubOffset_(0),
debugTrapHandler_(nullptr),
baselineDebugModeOSRHandler_(nullptr),
trampolineCode_(nullptr),
@ -302,6 +303,9 @@ JitRuntime::initialize(JSContext* cx)
JitSpew(JitSpew_Codegen, "# Emitting interpreter stub");
generateInterpreterStub(masm);
JitSpew(JitSpew_Codegen, "# Emitting double-to-int32-value stub");
generateDoubleToInt32ValueStub(masm);
JitSpew(JitSpew_Codegen, "# Emitting VM function wrappers");
for (VMFunction* fun = VMFunction::functions; fun; fun = fun->next) {
if (functionWrappers_->has(fun)) {

Просмотреть файл

@ -48,20 +48,7 @@ class TempAllocator
MOZ_MUST_USE void* allocate(size_t bytes)
{
LifoAlloc::AutoFallibleScope fallibleAllocator(lifoAlloc());
void* p = lifoScope_.alloc().alloc(bytes);
// The above allocation will allocate memory out of the
// lifo alloc's ballast, so we call ensureBallast to
// replenish it. If we fail to replenish the ballast, then
// future "infallible" allocations could fail. (Returning
// nullptr is insufficient because of cases like
// CompilerConstraintList::add, where we delay the OOM
// failure until later.)
AutoEnterOOMUnsafeRegion oomUnsafe;
if (!ensureBallast()) {
oomUnsafe.crash("Failed to replenish ballast in TempAllocator::allocate");
}
return p;
return lifoScope_.alloc().allocEnsureUnused(bytes, BallastSize);
}
template <typename T>
@ -72,12 +59,7 @@ class TempAllocator
if (MOZ_UNLIKELY(!CalculateAllocSize<T>(n, &bytes))) {
return nullptr;
}
T* p = static_cast<T*>(lifoScope_.alloc().alloc(bytes));
AutoEnterOOMUnsafeRegion oomUnsafe;
if (!ensureBallast()) {
oomUnsafe.crash("Failed to replenish ballast in TempAllocator::allocateArray");
}
return p;
return static_cast<T*>(lifoScope_.alloc().allocEnsureUnused(bytes, BallastSize));
}
// View this allocator as a fallible allocator.

Просмотреть файл

@ -127,6 +127,10 @@ class JitRuntime
// Thunk to enter the interpreter from JIT code.
WriteOnceData<uint32_t> interpreterStubOffset_;
// Thunk to convert the value in R0 to int32 if it's a double.
// Note: this stub treats -0 as +0 and may clobber R1.scratchReg().
WriteOnceData<uint32_t> doubleToInt32ValueStubOffset_;
// Thunk used by the debugger for breakpoint and step mode.
WriteOnceData<JitCode*> debugTrapHandler_;
@ -169,6 +173,7 @@ class JitRuntime
private:
void generateLazyLinkStub(MacroAssembler& masm);
void generateInterpreterStub(MacroAssembler& masm);
void generateDoubleToInt32ValueStub(MacroAssembler& masm);
void generateProfilerExitFrameTailStub(MacroAssembler& masm, Label* profilerExitTail);
void generateExceptionTailStub(MacroAssembler& masm, void* handler, Label* profilerExitTail);
void generateBailoutTailStub(MacroAssembler& masm, Label* bailoutTail);
@ -290,6 +295,10 @@ class JitRuntime
return trampolineCode(interpreterStubOffset_);
}
TrampolinePtr getDoubleToInt32ValueStub() const {
return trampolineCode(doubleToInt32ValueStubOffset_);
}
bool hasJitcodeGlobalTable() const {
return jitcodeGlobalTable_ != nullptr;
}

Просмотреть файл

@ -73,12 +73,6 @@ EmitReturnFromIC(MacroAssembler& masm)
masm.ma_mov(lr, pc);
}
inline void
EmitChangeICReturnAddress(MacroAssembler& masm, Register reg)
{
masm.ma_mov(reg, lr);
}
inline void
EmitBaselineLeaveStubFrame(MacroAssembler& masm, bool calledIntoIon = false)
{

Просмотреть файл

@ -73,12 +73,6 @@ EmitReturnFromIC(MacroAssembler& masm)
masm.abiret(); // Defaults to lr.
}
inline void
EmitChangeICReturnAddress(MacroAssembler& masm, Register reg)
{
masm.movePtr(reg, lr);
}
inline void
EmitBaselineLeaveStubFrame(MacroAssembler& masm, bool calledIntoIon = false)
{

Просмотреть файл

@ -83,12 +83,6 @@ EmitReturnFromIC(MacroAssembler& masm)
masm.branch(ra);
}
inline void
EmitChangeICReturnAddress(MacroAssembler& masm, Register reg)
{
masm.movePtr(reg, ra);
}
inline void
EmitBaselineLeaveStubFrame(MacroAssembler& masm, bool calledIntoIon = false)
{

Просмотреть файл

@ -17,7 +17,6 @@ inline void EmitRepushTailCallReg(MacroAssembler&) { MOZ_CRASH(); }
inline void EmitCallIC(MacroAssembler&, CodeOffset*, CodeOffset*) { MOZ_CRASH(); }
inline void EmitEnterTypeMonitorIC(MacroAssembler&, size_t v = 0) { MOZ_CRASH(); }
inline void EmitReturnFromIC(MacroAssembler&) { MOZ_CRASH(); }
inline void EmitChangeICReturnAddress(MacroAssembler&, Register) { MOZ_CRASH(); }
inline void EmitBaselineLeaveStubFrame(MacroAssembler&, bool v = false) { MOZ_CRASH(); }
inline void EmitStubGuardFailure(MacroAssembler&) { MOZ_CRASH(); }

Просмотреть файл

@ -64,12 +64,6 @@ EmitReturnFromIC(MacroAssembler& masm)
masm.ret();
}
inline void
EmitChangeICReturnAddress(MacroAssembler& masm, Register reg)
{
masm.storePtr(reg, Address(StackPointer, 0));
}
inline void
EmitBaselineLeaveStubFrame(MacroAssembler& masm, bool calledIntoIon = false)
{

Просмотреть файл

@ -65,12 +65,6 @@ EmitReturnFromIC(MacroAssembler& masm)
masm.ret();
}
inline void
EmitChangeICReturnAddress(MacroAssembler& masm, Register reg)
{
masm.storePtr(reg, Address(StackPointer, 0));
}
inline void
EmitBaselineLeaveStubFrame(MacroAssembler& masm, bool calledIntoIon = false)
{

Просмотреть файл

@ -340,30 +340,9 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope,
/* NB: Keep this in sync with CopyScript. */
enum ScriptBits {
NoScriptRval,
Strict,
ContainsDynamicNameAccess,
FunHasExtensibleScope,
FunHasAnyAliasedFormal,
ArgumentsHasVarBinding,
NeedsArgsObj,
HasMappedArgsObj,
FunctionHasThisBinding,
FunctionHasExtraBodyVarScope,
IsGenerator,
IsAsync,
HasRest,
OwnSource,
ExplicitUseStrict,
SelfHosted,
HasSingleton,
TreatAsRunOnce,
HasLazyScript,
HasNonSyntacticScope,
HasInnerFunctions,
NeedsHomeObject,
IsDerivedClassConstructor,
IsDefaultClassConstructor,
};
uint32_t length, lineno, column, nfixed, nslots;
@ -374,6 +353,7 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope,
uint32_t nTypeSets = 0;
uint32_t scriptBits = 0;
uint32_t bodyScopeIndex = 0;
uint32_t immutableFlags = 0;
JSContext* cx = xdr->cx();
RootedScript script(cx);
@ -414,6 +394,8 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope,
bodyScopeIndex = script->bodyScopeIndex();
natoms = script->natoms();
immutableFlags = script->immutableFlags_;
nsrcnotes = script->numNotes();
nscopes = script->scopes().size();
@ -436,79 +418,16 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope,
nTypeSets = script->nTypeSets();
funLength = script->funLength();
if (script->noScriptRval()) {
scriptBits |= (1 << NoScriptRval);
}
if (script->strict()) {
scriptBits |= (1 << Strict);
}
if (script->explicitUseStrict()) {
scriptBits |= (1 << ExplicitUseStrict);
}
if (script->selfHosted()) {
scriptBits |= (1 << SelfHosted);
}
if (script->bindingsAccessedDynamically()) {
scriptBits |= (1 << ContainsDynamicNameAccess);
}
if (script->funHasExtensibleScope()) {
scriptBits |= (1 << FunHasExtensibleScope);
}
if (script->funHasAnyAliasedFormal()) {
scriptBits |= (1 << FunHasAnyAliasedFormal);
}
if (script->argumentsHasVarBinding()) {
scriptBits |= (1 << ArgumentsHasVarBinding);
}
if (script->analyzedArgsUsage() && script->needsArgsObj()) {
scriptBits |= (1 << NeedsArgsObj);
}
if (script->hasMappedArgsObj()) {
scriptBits |= (1 << HasMappedArgsObj);
}
if (script->functionHasThisBinding()) {
scriptBits |= (1 << FunctionHasThisBinding);
}
if (script->functionHasExtraBodyVarScope()) {
scriptBits |= (1 << FunctionHasExtraBodyVarScope);
}
MOZ_ASSERT_IF(sourceObjectArg, sourceObjectArg->source() == script->scriptSource());
if (!sourceObjectArg) {
scriptBits |= (1 << OwnSource);
}
if (script->isGenerator()) {
scriptBits |= (1 << IsGenerator);
}
if (script->isAsync()) {
scriptBits |= (1 << IsAsync);
}
if (script->hasRest()) {
scriptBits |= (1 << HasRest);
}
if (script->hasSingletons()) {
scriptBits |= (1 << HasSingleton);
}
if (script->treatAsRunOnce()) {
scriptBits |= (1 << TreatAsRunOnce);
}
if (script->isRelazifiable()) {
scriptBits |= (1 << HasLazyScript);
}
if (script->hasNonSyntacticScope()) {
scriptBits |= (1 << HasNonSyntacticScope);
}
if (script->hasInnerFunctions()) {
scriptBits |= (1 << HasInnerFunctions);
}
if (script->needsHomeObject()) {
scriptBits |= (1 << NeedsHomeObject);
}
if (script->isDerivedClassConstructor()) {
scriptBits |= (1 << IsDerivedClassConstructor);
}
if (script->isDefaultClassConstructor()) {
scriptBits |= (1 << IsDefaultClassConstructor);
}
}
MOZ_TRY(xdr->codeUint32(&prologueLength));
@ -525,6 +444,7 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope,
MOZ_TRY(xdr->codeUint32(&nTypeSets));
MOZ_TRY(xdr->codeUint32(&funLength));
MOZ_TRY(xdr->codeUint32(&scriptBits));
MOZ_TRY(xdr->codeUint32(&immutableFlags));
MOZ_ASSERT(!!(scriptBits & (1 << OwnSource)) == !sourceObjectArg);
RootedScriptSourceObject sourceObject(cx, sourceObjectArg);
@ -534,18 +454,20 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope,
// the document. If the noScriptRval or selfHostingMode flag doesn't
// match, we should fail. This only applies to the top-level and not
// its inner functions.
bool noScriptRval = !!(immutableFlags & uint32_t(ImmutableFlags::NoScriptRval));
bool selfHosted = !!(immutableFlags & uint32_t(ImmutableFlags::SelfHosted));
mozilla::Maybe<CompileOptions> options;
if (xdr->hasOptions() && (scriptBits & (1 << OwnSource))) {
options.emplace(xdr->cx(), xdr->options());
if (options->noScriptRval != !!(scriptBits & (1 << NoScriptRval)) ||
options->selfHostingMode != !!(scriptBits & (1 << SelfHosted)))
if (options->noScriptRval != noScriptRval ||
options->selfHostingMode != selfHosted)
{
return xdr->fail(JS::TranscodeResult_Failure_WrongCompileOption);
}
} else {
options.emplace(xdr->cx());
(*options).setNoScriptRval(!!(scriptBits & (1 << NoScriptRval)))
.setSelfHostingMode(!!(scriptBits & (1 << SelfHosted)));
(*options).setNoScriptRval(noScriptRval)
.setSelfHostingMode(selfHosted);
}
if (scriptBits & (1 << OwnSource)) {
@ -611,66 +533,16 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope,
scriptp.set(script);
if (scriptBits & (1 << Strict)) {
script->setFlag(ImmutableFlags::Strict);
}
if (scriptBits & (1 << ExplicitUseStrict)) {
script->setFlag(ImmutableFlags::ExplicitUseStrict);
}
if (scriptBits & (1 << ContainsDynamicNameAccess)) {
script->setFlag(ImmutableFlags::BindingsAccessedDynamically);
}
if (scriptBits & (1 << FunHasExtensibleScope)) {
script->setFlag(ImmutableFlags::FunHasExtensibleScope);
}
if (scriptBits & (1 << FunHasAnyAliasedFormal)) {
script->setFlag(ImmutableFlags::FunHasAnyAliasedFormal);
}
if (scriptBits & (1 << ArgumentsHasVarBinding)) {
script->immutableFlags_ = immutableFlags;
if (script->hasFlag(ImmutableFlags::ArgsHasVarBinding)) {
// Call setArgumentsHasVarBinding to initialize the
// NeedsArgsAnalysis flag.
script->setArgumentsHasVarBinding();
}
if (scriptBits & (1 << NeedsArgsObj)) {
script->setNeedsArgsObj(true);
}
if (scriptBits & (1 << HasMappedArgsObj)) {
script->setFlag(ImmutableFlags::HasMappedArgsObj);
}
if (scriptBits & (1 << FunctionHasThisBinding)) {
script->setFlag(ImmutableFlags::FunctionHasThisBinding);
}
if (scriptBits & (1 << FunctionHasExtraBodyVarScope)) {
script->setFlag(ImmutableFlags::FunctionHasExtraBodyVarScope);
}
if (scriptBits & (1 << HasSingleton)) {
script->setFlag(ImmutableFlags::HasSingletons);
}
if (scriptBits & (1 << TreatAsRunOnce)) {
script->setFlag(ImmutableFlags::TreatAsRunOnce);
}
if (scriptBits & (1 << HasNonSyntacticScope)) {
script->setFlag(ImmutableFlags::HasNonSyntacticScope);
}
if (scriptBits & (1 << HasInnerFunctions)) {
script->setFlag(ImmutableFlags::HasInnerFunctions);
}
if (scriptBits & (1 << NeedsHomeObject)) {
script->setFlag(ImmutableFlags::NeedsHomeObject);
}
if (scriptBits & (1 << IsDerivedClassConstructor)) {
script->setFlag(ImmutableFlags::IsDerivedClassConstructor);
}
if (scriptBits & (1 << IsDefaultClassConstructor)) {
script->setFlag(ImmutableFlags::IsDefaultClassConstructor);
}
if (scriptBits & (1 << IsGenerator)) {
script->setFlag(ImmutableFlags::IsGenerator);
}
if (scriptBits & (1 << IsAsync)) {
script->setFlag(ImmutableFlags::IsAsync);
}
if (scriptBits & (1 << HasRest)) {
script->setFlag(ImmutableFlags::HasRest);
}
}
JS_STATIC_ASSERT(sizeof(jsbytecode) == 1);
@ -3298,7 +3170,7 @@ JSScript::Create(JSContext* cx, const ReadOnlyCompileOptions& options,
script->setFlag(ImmutableFlags::NoScriptRval, options.noScriptRval);
script->setFlag(ImmutableFlags::SelfHosted, options.selfHostingMode);
script->setFlag(ImmutableFlags::TreatAsRunOnce, options.isRunOnce);
script->setFlag(ImmutableFlags::HideScriptFromDebugger, options.hideScriptFromDebugger);
script->setFlag(MutableFlags::HideScriptFromDebugger, options.hideScriptFromDebugger);
if (cx->runtime()->lcovOutput().isEnabled()) {
if (!script->initScriptName(cx)) {
@ -3479,13 +3351,6 @@ JSScript::initFromFunctionBox(HandleScript script, frontend::FunctionBox* funbox
/* static */ void
JSScript::initFromModuleContext(HandleScript script)
{
script->clearFlag(ImmutableFlags::FunHasExtensibleScope);
script->clearFlag(ImmutableFlags::NeedsHomeObject);
script->clearFlag(ImmutableFlags::IsDerivedClassConstructor);
script->funLength_ = 0;
script->clearFlag(ImmutableFlags::IsGenerator);
// Since modules are only run once, mark the script so that initializers
// created within it may be given more precise types.
script->setTreatAsRunOnce();
@ -4011,8 +3876,6 @@ bool
js::detail::CopyScript(JSContext* cx, HandleScript src, HandleScript dst,
MutableHandle<GCVector<Scope*>> scopes)
{
using ImmutableFlags = JSScript::ImmutableFlags;
if (src->treatAsRunOnce() && !src->functionNonDelazifying()) {
JS_ReportErrorASCII(cx, "No cloning toplevel run-once scripts");
return false;
@ -4119,31 +3982,19 @@ js::detail::CopyScript(JSContext* cx, HandleScript src, HandleScript dst,
dst->bodyScopeIndex_ = src->bodyScopeIndex_;
dst->funLength_ = src->funLength();
dst->nTypeSets_ = src->nTypeSets();
dst->immutableFlags_ = src->immutableFlags_;
dst->setFlag(JSScript::ImmutableFlags::HasNonSyntacticScope,
scopes[0]->hasOnChain(ScopeKind::NonSyntactic));
dst->setFlag(JSScript::MutableFlags::HideScriptFromDebugger, src->hideScriptFromDebugger());
if (src->argumentsHasVarBinding()) {
dst->setArgumentsHasVarBinding();
if (src->analyzedArgsUsage()) {
dst->setNeedsArgsObj(src->needsArgsObj());
}
}
dst->setFlag(ImmutableFlags::HasMappedArgsObj, src->hasMappedArgsObj());
dst->setFlag(ImmutableFlags::FunctionHasThisBinding, src->functionHasThisBinding());
dst->setFlag(ImmutableFlags::FunctionHasExtraBodyVarScope, src->functionHasExtraBodyVarScope());
dst->setFlag(ImmutableFlags::Strict, src->strict());
dst->setFlag(ImmutableFlags::ExplicitUseStrict, src->explicitUseStrict());
dst->setFlag(ImmutableFlags::HasNonSyntacticScope, scopes[0]->hasOnChain(ScopeKind::NonSyntactic));
dst->setFlag(ImmutableFlags::BindingsAccessedDynamically, src->bindingsAccessedDynamically());
dst->setFlag(ImmutableFlags::FunHasExtensibleScope, src->funHasExtensibleScope());
dst->setFlag(ImmutableFlags::FunHasAnyAliasedFormal, src->funHasAnyAliasedFormal());
dst->setFlag(ImmutableFlags::HasSingletons, src->hasSingletons());
dst->setFlag(ImmutableFlags::TreatAsRunOnce, src->treatAsRunOnce());
dst->setFlag(ImmutableFlags::HasInnerFunctions, src->hasInnerFunctions());
dst->setFlag(ImmutableFlags::IsGenerator, src->isGenerator());
dst->setFlag(ImmutableFlags::IsDerivedClassConstructor, src->isDerivedClassConstructor());
dst->setFlag(ImmutableFlags::NeedsHomeObject, src->needsHomeObject());
dst->setFlag(ImmutableFlags::IsDefaultClassConstructor, src->isDefaultClassConstructor());
dst->setFlag(ImmutableFlags::IsAsync, src->isAsync());
dst->setFlag(ImmutableFlags::HasRest, src->hasRest());
dst->setFlag(ImmutableFlags::HideScriptFromDebugger, src->hideScriptFromDebugger());
{
auto array = dst->data_->scopes();

Просмотреть файл

@ -1689,20 +1689,17 @@ class JSScript : public js::gc::TenuredCell
// 'this', 'arguments' and f.apply() are used. This is likely to be a wrapper.
IsLikelyConstructorWrapper = 1 << 17,
// Set if the debugger's onNewScript hook has not yet been called.
HideScriptFromDebugger = 1 << 18,
// Set if this function is a generator function or async generator.
IsGenerator = 1 << 19,
IsGenerator = 1 << 18,
// Set if this function is an async function or async generator.
IsAsync = 1 << 20,
IsAsync = 1 << 19,
// Set if this function has a rest parameter.
HasRest = 1 << 21,
HasRest = 1 << 20,
// See comments below.
ArgsHasVarBinding = 1 << 22,
ArgsHasVarBinding = 1 << 21,
};
uint32_t immutableFlags_ = 0;
@ -1768,6 +1765,9 @@ class JSScript : public js::gc::TenuredCell
// See comments below.
NeedsArgsAnalysis = 1 << 17,
NeedsArgsObj = 1 << 18,
// Set if the debugger's onNewScript hook has not yet been called.
HideScriptFromDebugger = 1 << 19,
};
uint32_t mutableFlags_ = 0;
@ -2171,10 +2171,10 @@ class JSScript : public js::gc::TenuredCell
}
bool hideScriptFromDebugger() const {
return hasFlag(ImmutableFlags::HideScriptFromDebugger);
return hasFlag(MutableFlags::HideScriptFromDebugger);
}
void clearHideScriptFromDebugger() {
clearFlag(ImmutableFlags::HideScriptFromDebugger);
clearFlag(MutableFlags::HideScriptFromDebugger);
}
bool needsHomeObject() const {

Просмотреть файл

@ -705,7 +705,7 @@
* Stack: i =>
* len: len
*/ \
macro(JSOP_TABLESWITCH, 70, "tableswitch", NULL, 16, 1, 0, JOF_TABLESWITCH|JOF_DETECTING|JOF_IC) \
macro(JSOP_TABLESWITCH, 70, "tableswitch", NULL, 16, 1, 0, JOF_TABLESWITCH|JOF_DETECTING) \
/*
* Prologue emitted in scripts expected to run once, which deoptimizes code
* if it executes multiple times.

Просмотреть файл

@ -28,6 +28,8 @@
//
// See the comment at the top of ServoBindingTypes.h for how to use these.
// clang-format off
// Needs to be a on single line
GECKO_BORROWED_TYPE(mozilla::dom::Element, RawGeckoElement)
GECKO_BORROWED_TYPE(nsIDocument, RawGeckoDocument)
GECKO_BORROWED_TYPE(nsINode, RawGeckoNode)
@ -50,3 +52,5 @@ GECKO_BORROWED_TYPE_MUT(nsTArray<nsCSSPropertyID>, RawGeckoCSSPropertyIDList)
GECKO_BORROWED_TYPE_MUT(nsTArray<nsFontFaceRuleContainer>, RawGeckoFontFaceRuleList)
GECKO_BORROWED_TYPE_MUT(nsTArray<RefPtr<RawServoAnimationValue>>, RawGeckoServoAnimationValueList)
GECKO_BORROWED_TYPE_MUT(nsTimingFunction, nsTimingFunction)
// clang-format on

Просмотреть файл

@ -20,6 +20,8 @@
// If you add an entry to this file, you should also add an impl_arc_ffi!()
// call to servo/components/style/gecko/arc_types.rs.
// clang-format off
// Needs to be a on single line
SERVO_ARC_TYPE(CssRules, ServoCssRules)
SERVO_ARC_TYPE(StyleSheetContents, RawServoStyleSheetContents)
SERVO_ARC_TYPE(DeclarationBlock, RawServoDeclarationBlock)
@ -40,3 +42,4 @@ SERVO_ARC_TYPE(FontFaceRule, RawServoFontFaceRule)
SERVO_ARC_TYPE(CounterStyleRule, RawServoCounterStyleRule)
SERVO_ARC_TYPE(CssUrlData, RawServoCssUrlData)
SERVO_ARC_TYPE(Quotes, RawServoQuotes)
// clang-format on

Просмотреть файл

@ -24,8 +24,12 @@
//
// TODO(heycam): Do some of this automatically.
// clang-format off
// Needs to be a on single line
SERVO_BOXED_TYPE(StyleSet, RawServoStyleSet)
SERVO_BOXED_TYPE(AuthorStyles, RawServoAuthorStyles)
SERVO_BOXED_TYPE(SelectorList, RawServoSelectorList)
SERVO_BOXED_TYPE(SourceSizeList, RawServoSourceSizeList)
SERVO_BOXED_TYPE(UseCounters, StyleUseCounters)
// clang-format on

Просмотреть файл

@ -0,0 +1,433 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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";
ChromeUtils.import("resource://gre/modules/GeckoViewChildModule.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
class GeckoViewMediaChild extends GeckoViewChildModule {
onInit() {
this._videoIndex = 0;
XPCOMUtils.defineLazyGetter(this, "_videos", () =>
new Map());
this._mediaEvents = [
"abort", "canplay", "canplaythrough", "durationchange", "emptied",
"ended", "error", "loadeddata", "loadedmetadata", "pause", "play", "playing",
"progress", "ratechange", "resize", "seeked", "seeking", "stalled", "suspend",
"timeupdate", "volumechange", "waiting",
];
this._mediaEventCallback = (event) => {
this.handleMediaEvent(event);
};
this._fullscreenMedia = null;
this._stateSymbol = Symbol();
}
onEnable() {
debug `onEnable`;
addEventListener("UAWidgetBindToTree", this, false);
addEventListener("MozDOMFullscreen:Entered", this, false);
addEventListener("MozDOMFullscreen:Exited", this, false);
addEventListener("pagehide", this, false);
this.messageManager.addMessageListener("GeckoView:MediaObserve", this);
this.messageManager.addMessageListener("GeckoView:MediaUnobserve", this);
this.messageManager.addMessageListener("GeckoView:MediaPlay", this);
this.messageManager.addMessageListener("GeckoView:MediaPause", this);
this.messageManager.addMessageListener("GeckoView:MediaSeek", this);
this.messageManager.addMessageListener("GeckoView:MediaSetVolume", this);
this.messageManager.addMessageListener("GeckoView:MediaSetMuted", this);
this.messageManager.addMessageListener("GeckoView:MediaSetPlaybackRate", this);
}
onDisable() {
debug `onDisable`;
removeEventListener("UAWidgetBindToTree", this);
removeEventListener("MozDOMFullscreen:Entered", this);
removeEventListener("MozDOMFullscreen:Exited", this);
removeEventListener("pagehide", this);
this.messageManager.removeMessageListener("GeckoView:MediaObserve", this);
this.messageManager.removeMessageListener("GeckoView:MediaUnobserve", this);
this.messageManager.removeMessageListener("GeckoView:MediaPlay", this);
this.messageManager.removeMessageListener("GeckoView:MediaPause", this);
this.messageManager.removeMessageListener("GeckoView:MediaSeek", this);
this.messageManager.removeMessageListener("GeckoView:MediaSetVolume", this);
this.messageManager.removeMessageListener("GeckoView:MediaSetMuted", this);
this.messageManager.removeMessageListener("GeckoView:MediaSetPlaybackRate", this);
}
receiveMessage(aMsg) {
debug `receiveMessage: ${aMsg.name}`;
const data = aMsg.data;
const element = this.findElement(data.id);
if (!element) {
warn `Didn't find HTMLMediaElement with id: ${data.id}`;
return;
}
switch (aMsg.name) {
case "GeckoView:MediaObserve":
this.observeMedia(element);
break;
case "GeckoView:MediaUnobserve":
this.unobserveMedia(element);
break;
case "GeckoView:MediaPlay":
element.play();
break;
case "GeckoView:MediaPause":
element.pause();
break;
case "GeckoView:MediaSeek":
element.currentTime = data.time;
break;
case "GeckoView:MediaSetVolume":
element.volume = data.volume;
break;
case "GeckoView:MediaSetMuted":
element.muted = !!data.muted;
break;
case "GeckoView:MediaSetPlaybackRate":
element.playbackRate = data.playbackRate;
break;
}
}
handleEvent(aEvent) {
debug `handleEvent: ${aEvent.type}`;
switch (aEvent.type) {
case "UAWidgetBindToTree":
this.handleNewMedia(aEvent.composedTarget);
break;
case "MozDOMFullscreen:Entered":
const element = content && content.document.fullscreenElement;
if (this.isMedia(element)) {
this.handleFullscreenChange(element);
} else {
// document.fullscreenElement can be a div container instead of the HTMLMediaElement
// in some pages (e.g Vimeo).
const childrenMedias = element.getElementsByTagName("video");
if (childrenMedias && childrenMedias.length > 0) {
this.handleFullscreenChange(childrenMedias[0]);
}
}
break;
case "MozDOMFullscreen:Exited":
this.handleFullscreenChange(null);
break;
case "pagehide":
if (aEvent.target === content.document) {
this.handleWindowUnload();
}
break;
}
}
handleWindowUnload() {
for (const weakElement of this._videos.values()) {
const element = weakElement.get();
if (element) {
this.unobserveMedia(element);
}
}
if (this._videos.size > 0) {
this.notifyMediaRemoveAll();
}
this._videos.clear();
this._fullscreenMedia = null;
}
handleNewMedia(aElement) {
if (this.getState(aElement) || !this.isMedia(aElement)) {
return;
}
const state = {
id: ++this._videoIndex,
notified: false,
observing: false,
};
aElement[this._stateSymbol] = state;
this._videos.set(state.id, Cu.getWeakReference(aElement));
this.notifyNewMedia(aElement);
}
notifyNewMedia(aElement) {
this.getState(aElement).notified = true;
const message = {
type: "GeckoView:MediaAdd",
id: this.getState(aElement).id,
};
this.fillMetadata(aElement, message);
this.eventDispatcher.sendRequest(message);
}
observeMedia(aElement) {
if (this.isObserving(aElement)) {
return;
}
this.getState(aElement).observing = true;
for (const name of this._mediaEvents) {
aElement.addEventListener(name, this._mediaEventCallback, true);
}
// Notify current state
this.notifyTimeChange(aElement);
this.notifyVolumeChange(aElement);
this.notifyRateChange(aElement);
if (aElement.readyState >= 1) {
this.notifyMetadataChange(aElement);
}
this.notifyReadyStateChange(aElement);
if (!aElement.paused) {
this.notifyPlaybackStateChange(aElement, "play");
}
if (aElement === this._fullscreenMedia) {
this.notifyFullscreenChange(aElement, true);
}
if (this.hasError(aElement)) {
this.notifyMediaError(aElement);
}
}
unobserveMedia(aElement) {
if (!this.isObserving(aElement)) {
return;
}
this.getState(aElement).observing = false;
for (const name of this._mediaEvents) {
aElement.removeEventListener(name, this._mediaEventCallback);
}
}
isMedia(aElement) {
if (!aElement) {
return false;
}
const elementType = ChromeUtils.getClassName(aElement);
return (elementType === "HTMLVideoElement") || (elementType === "HTMLAudioElement");
}
isObserving(aElement) {
return aElement && this.getState(aElement) && this.getState(aElement).observing;
}
findElement(aId) {
for (const weakElement of this._videos.values()) {
const element = weakElement.get();
if (element && this.getState(element).id === aId) {
return element;
}
}
return null;
}
getState(aElement) {
return aElement[this._stateSymbol];
}
hasError(aElement) {
// We either have an explicit error, or networkState is set to NETWORK_NO_SOURCE
// after selecting a source.
return aElement.error != null ||
(aElement.networkState === aElement.NETWORK_NO_SOURCE &&
this.hasSources(aElement));
}
hasSources(aElement) {
if (aElement.hasAttribute("src") && aElement.getAttribute("src") !== "") {
return true;
}
for (var child = aElement.firstChild; child !== null; child = child.nextElementSibling) {
if (child instanceof content.window.HTMLSourceElement) {
return true;
}
}
return false;
}
fillMetadata(aElement, aOut) {
aOut.src = aElement.currentSrc || aElement.src;
aOut.width = aElement.videoWidth || 0;
aOut.height = aElement.videoHeight || 0;
aOut.duration = aElement.duration;
aOut.seekable = !!aElement.seekable;
if (aElement.audioTracks) {
aOut.audioTrackCount = aElement.audioTracks.length;
} else {
aOut.audioTrackCount = (aElement.mozHasAudio || aElement.webkitAudioDecodedByteCount ||
ChromeUtils.getClassName(aElement) === "HTMLAudioElement") ? 1 : 0;
}
if (aElement.videoTracks) {
aOut.videoTrackCount = aElement.videoTracks.length;
} else {
aOut.videoTrackCount = ChromeUtils.getClassName(aElement) === "HTMLVideoElement" ? 1 : 0;
}
}
handleMediaEvent(aEvent) {
const element = aEvent.target;
if (!this.isObserving(element)) {
return;
}
switch (aEvent.type) {
case "timeupdate":
this.notifyTimeChange(element);
break;
case "volumechange":
this.notifyVolumeChange(element);
break;
case "loadedmetadata":
this.notifyMetadataChange(element);
this.notifyReadyStateChange(element);
break;
case "ratechange":
this.notifyRateChange(element);
break;
case "error":
this.notifyMediaError(element);
break;
case "progress":
this.notifyLoadProgress(element, aEvent);
break;
case "durationchange": // Fallthrough
case "resize":
this.notifyMetadataChange(element);
break;
case "canplay": // Fallthrough
case "canplaythrough": // Fallthrough
case "loadeddata":
this.notifyReadyStateChange(element);
break;
default:
this.notifyPlaybackStateChange(element, aEvent.type);
break;
}
}
handleFullscreenChange(aElement) {
if (aElement === this._fullscreenMedia) {
return;
}
if (this.isObserving(this._fullscreenMedia)) {
this.notifyFullscreenChange(this._fullscreenMedia, false);
}
this._fullscreenMedia = aElement;
if (this.isObserving(aElement)) {
this.notifyFullscreenChange(aElement, true);
}
}
notifyPlaybackStateChange(aElement, aName) {
this.eventDispatcher.sendRequest({
type: "GeckoView:MediaPlaybackStateChanged",
id: this.getState(aElement).id,
playbackState: aName,
});
}
notifyReadyStateChange(aElement) {
this.eventDispatcher.sendRequest({
type: "GeckoView:MediaReadyStateChanged",
id: this.getState(aElement).id,
readyState: aElement.readyState,
});
}
notifyMetadataChange(aElement) {
const message = {
type: "GeckoView:MediaMetadataChanged",
id: this.getState(aElement).id,
};
this.fillMetadata(aElement, message);
this.eventDispatcher.sendRequest(message);
}
notifyLoadProgress(aElement, aEvent) {
const message = {
type: "GeckoView:MediaProgress",
id: this.getState(aElement).id,
loadedBytes: aEvent.lengthComputable ? aEvent.loaded : -1,
totalBytes: aEvent.lengthComputable ? aEvent.total : -1,
};
if (aElement.buffered && aElement.buffered.length > 0) {
message.timeRangeStarts = [];
message.timeRangeEnds = [];
for (let i = 0; i < aElement.buffered.length; ++i) {
message.timeRangeStarts.push(aElement.buffered.start(i));
message.timeRangeEnds.push(aElement.buffered.end(i));
}
}
this.eventDispatcher.sendRequest(message);
}
notifyTimeChange(aElement) {
this.eventDispatcher.sendRequest({
type: "GeckoView:MediaTimeChanged",
id: this.getState(aElement).id,
time: aElement.currentTime,
});
}
notifyRateChange(aElement) {
this.eventDispatcher.sendRequest({
type: "GeckoView:MediaRateChanged",
id: this.getState(aElement).id,
rate: aElement.playbackRate,
});
}
notifyVolumeChange(aElement) {
this.eventDispatcher.sendRequest({
type: "GeckoView:MediaVolumeChanged",
id: this.getState(aElement).id,
volume: aElement.volume,
muted: !!aElement.muted,
});
}
notifyFullscreenChange(aElement, aIsFullscreen) {
this.eventDispatcher.sendRequest({
type: "GeckoView:MediaFullscreenChanged",
id: this.getState(aElement).id,
fullscreen: aIsFullscreen,
});
}
notifyMediaError(aElement) {
let code = aElement.error ? aElement.error.code : 0;
this.eventDispatcher.sendRequest({
type: "GeckoView:MediaError",
id: this.getState(aElement).id,
code: code,
});
}
notifyMediaRemove(aElement) {
this.eventDispatcher.sendRequest({
type: "GeckoView:MediaRemove",
id: this.getState(aElement).id,
});
}
notifyMediaRemoveAll() {
this.eventDispatcher.sendRequest({
type: "GeckoView:MediaRemoveAll",
});
}
}
const {debug, warn} = GeckoViewMediaChild.initLogging("GeckoViewMedia");
const module = GeckoViewMediaChild.create(this);

Просмотреть файл

@ -391,6 +391,12 @@ function startup() {
resource: "resource://gre/modules/GeckoViewContent.jsm",
frameScript: "chrome://geckoview/content/GeckoViewContentChild.js",
},
}, {
name: "GeckoViewMedia",
onEnable: {
resource: "resource://gre/modules/GeckoViewMedia.jsm",
frameScript: "chrome://geckoview/content/GeckoViewMediaChild.js",
},
}, {
name: "GeckoViewNavigation",
onInit: {

Просмотреть файл

@ -13,6 +13,7 @@ geckoview.jar:
content/geckoview.xul
content/geckoview.js
content/GeckoViewContentChild.js
content/GeckoViewMediaChild.js
content/GeckoViewNavigationChild.js
content/GeckoViewProgressChild.js
content/GeckoViewPromptChild.js

Просмотреть файл

@ -10,6 +10,7 @@ import org.mozilla.geckoview.GeckoResponse
import org.mozilla.geckoview.GeckoResult
import org.mozilla.geckoview.GeckoSession
import org.mozilla.geckoview.GeckoSession.NavigationDelegate.LoadRequest
import org.mozilla.geckoview.MediaElement
import org.mozilla.geckoview.WebRequestError
import android.view.inputmethod.CursorAnchorInfo
@ -21,7 +22,7 @@ class Callbacks private constructor() {
interface All : ContentDelegate, NavigationDelegate, PermissionDelegate, ProgressDelegate,
PromptDelegate, ScrollDelegate, SelectionActionDelegate, TextInputDelegate,
TrackingProtectionDelegate
TrackingProtectionDelegate, MediaDelegate
interface ContentDelegate : GeckoSession.ContentDelegate {
override fun onTitleChange(session: GeckoSession, title: String) {
@ -177,4 +178,12 @@ class Callbacks private constructor() {
override fun notifyAutoFill(session: GeckoSession, notification: Int, virtualId: Int) {
}
}
interface MediaDelegate: GeckoSession.MediaDelegate {
override fun onMediaAdd(session: GeckoSession, element: MediaElement) {
}
override fun onMediaRemove(session: GeckoSession, element: MediaElement) {
}
}
}

Просмотреть файл

@ -49,6 +49,7 @@ import android.support.annotation.StringDef;
import android.support.annotation.UiThread;
import android.util.Base64;
import android.util.Log;
import android.util.LongSparseArray;
import android.view.Surface;
import android.view.inputmethod.CursorAnchorInfo;
import android.view.inputmethod.ExtractedText;
@ -519,10 +520,85 @@ public class GeckoSession extends LayerSession
}
};
private LongSparseArray<MediaElement> mMediaElements = new LongSparseArray<>();
/* package */ LongSparseArray<MediaElement> getMediaElements() {
return mMediaElements;
}
private final GeckoSessionHandler<MediaDelegate> mMediaHandler =
new GeckoSessionHandler<MediaDelegate>(
"GeckoViewMedia", this,
new String[]{
"GeckoView:MediaAdd",
"GeckoView:MediaRemove",
"GeckoView:MediaRemoveAll",
"GeckoView:MediaReadyStateChanged",
"GeckoView:MediaTimeChanged",
"GeckoView:MediaPlaybackStateChanged",
"GeckoView:MediaMetadataChanged",
"GeckoView:MediaProgress",
"GeckoView:MediaVolumeChanged",
"GeckoView:MediaRateChanged",
"GeckoView:MediaFullscreenChanged",
"GeckoView:MediaError",
}
) {
@Override
public void handleMessage(final MediaDelegate delegate,
final String event,
final GeckoBundle message,
final EventCallback callback) {
if ("GeckoView:MediaAdd".equals(event)) {
final MediaElement element = new MediaElement(message.getLong("id"), GeckoSession.this);
delegate.onMediaAdd(GeckoSession.this, element);
return;
} else if ("GeckoView:MediaRemoveAll".equals(event)) {
for (int i = 0; i < mMediaElements.size(); i++) {
final long key = mMediaElements.keyAt(i);
delegate.onMediaRemove(GeckoSession.this, mMediaElements.get(key));
}
mMediaElements.clear();
return;
}
final long id = message.getLong("id", 0);
final MediaElement element = mMediaElements.get(id);
if (element == null) {
Log.w(LOGTAG, "MediaElement not found for '" + id + "'");
return;
}
if ("GeckoView:MediaTimeChanged".equals(event)) {
element.notifyTimeChange(message.getDouble("time"));
} else if ("GeckoView:MediaProgress".equals(event)) {
element.notifyLoadProgress(message);
} else if ("GeckoView:MediaMetadataChanged".equals(event)) {
element.notifyMetadataChange(message);
} else if ("GeckoView:MediaReadyStateChanged".equals(event)) {
element.notifyReadyStateChange(message.getInt("readyState"));
} else if ("GeckoView:MediaPlaybackStateChanged".equals(event)) {
element.notifyPlaybackStateChange(message.getString("playbackState"));
} else if ("GeckoView:MediaVolumeChanged".equals(event)) {
element.notifyVolumeChange(message.getDouble("volume"), message.getBoolean("muted"));
} else if ("GeckoView:MediaRateChanged".equals(event)) {
element.notifyPlaybackRateChange(message.getDouble("rate"));
} else if ("GeckoView:MediaFullscreenChanged".equals(event)) {
element.notifyFullscreenChange(message.getBoolean("fullscreen"));
} else if ("GeckoView:MediaRemove".equals(event)) {
delegate.onMediaRemove(GeckoSession.this, element);
mMediaElements.remove(element.getVideoId());
} else if ("GeckoView:MediaError".equals(event)) {
element.notifyError(message.getInt("code"));
} else {
throw new UnsupportedOperationException(event + " media message not implemented");
}
}
};
/* package */ int handlersCount;
private final GeckoSessionHandler<?>[] mSessionHandlers = new GeckoSessionHandler<?>[] {
mContentHandler, mNavigationHandler, mProgressHandler, mScrollHandler,
mContentHandler, mMediaHandler, mNavigationHandler, mProgressHandler, mScrollHandler,
mTrackingProtectionHandler, mPermissionHandler, mSelectionActionDelegate
};
@ -1595,6 +1671,24 @@ public class GeckoSession extends LayerSession
mSelectionActionDelegate.setDelegate(delegate, this);
}
/**
* Set the media callback handler.
* This will replace the current handler.
* @param delegate An implementation of MediaDelegate.
*/
public void setMediaDelegate(final @Nullable MediaDelegate delegate) {
mMediaHandler.setDelegate(delegate, this);
}
/**
* Get the Media callback handler.
* @return The current Media callback handler.
*/
public @Nullable MediaDelegate getMediaDelegate() {
return mMediaHandler.getDelegate();
}
/**
* Get the current selection action delegate for this GeckoSession.
*
@ -3504,4 +3598,22 @@ public class GeckoSession extends LayerSession
void notifyAutoFill(@NonNull GeckoSession session, @AutoFillNotification int notification,
int virtualId);
}
/**
* GeckoSession applications implement this interface to handle media events.
*/
public interface MediaDelegate {
/**
* An HTMLMediaElement has been created.
* @param session Session instance.
* @param element The media element that was just created.
*/
void onMediaAdd(@NonNull GeckoSession session, @NonNull MediaElement element);
/**
* An HTMLMediaElement has been unloaded.
* @param session Session instance.
* @param element The media element that was unloaded.
*/
void onMediaRemove(@NonNull GeckoSession session, @NonNull MediaElement element);
}
}

Просмотреть файл

@ -0,0 +1,540 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* vim: ts=4 sw=4 expandtab:
* 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/. */
package org.mozilla.geckoview;
import android.support.annotation.IntDef;
import android.support.annotation.Nullable;
import android.util.Log;
import org.mozilla.gecko.util.GeckoBundle;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* GeckoSession applications can use this class to handle media events
* and control the HTMLMediaElement externally.
**/
public class MediaElement {
@Retention(RetentionPolicy.SOURCE)
@IntDef({MEDIA_STATE_PLAY, MEDIA_STATE_PLAYING, MEDIA_STATE_PAUSE,
MEDIA_STATE_ENDED, MEDIA_STATE_SEEKING, MEDIA_STATE_SEEKED,
MEDIA_STATE_STALLED, MEDIA_STATE_SUSPEND, MEDIA_STATE_WAITING,
MEDIA_STATE_ABORT, MEDIA_STATE_EMPTIED})
/* package */ @interface MediaStateFlags {}
@Retention(RetentionPolicy.SOURCE)
@IntDef({MEDIA_READY_STATE_HAVE_NOTHING, MEDIA_READY_STATE_HAVE_METADATA,
MEDIA_READY_STATE_HAVE_CURRENT_DATA, MEDIA_READY_STATE_HAVE_FUTURE_DATA,
MEDIA_READY_STATE_HAVE_ENOUGH_DATA})
/* package */ @interface ReadyStateFlags {}
@Retention(RetentionPolicy.SOURCE)
@IntDef({MEDIA_ERROR_NETWORK_NO_SOURCE, MEDIA_ERROR_ABORTED, MEDIA_ERROR_NETWORK,
MEDIA_ERROR_DECODE, MEDIA_ERROR_SRC_NOT_SUPPORTED})
/* package */ @interface MediaErrorFlags {}
/**
* The media is no longer paused, as a result of the play method, or the autoplay attribute.
*/
public static final int MEDIA_STATE_PLAY = 0;
/**
* Sent when the media has enough data to start playing, after the play event,
* but also when recovering from being stalled, when looping media restarts,
* and after seeked, if it was playing before seeking.
*/
public static final int MEDIA_STATE_PLAYING = 1;
/**
* Sent when the playback state is changed to paused.
*/
public static final int MEDIA_STATE_PAUSE = 2;
/**
* Sent when playback completes.
*/
public static final int MEDIA_STATE_ENDED = 3;
/**
* Sent when a seek operation begins.
*/
public static final int MEDIA_STATE_SEEKING = 4;
/**
* Sent when a seek operation completes.
*/
public static final int MEDIA_STATE_SEEKED = 5;
/**
* Sent when the user agent is trying to fetch media data,
* but data is unexpectedly not forthcoming.
*/
public static final int MEDIA_STATE_STALLED = 6;
/**
* Sent when loading of the media is suspended. This may happen either because
* the download has completed or because it has been paused for any other reason.
*/
public static final int MEDIA_STATE_SUSPEND = 7;
/**
* Sent when the requested operation (such as playback) is delayed
* pending the completion of another operation (such as a seek).
*/
public static final int MEDIA_STATE_WAITING = 8;
/**
* Sent when playback is aborted; for example, if the media is playing
* and is restarted from the beginning, this event is sent.
*/
public static final int MEDIA_STATE_ABORT = 9;
/**
* The media has become empty. For example, this event is sent if the media
* has already been loaded, and the load() method is called to reload it.
*/
public static final int MEDIA_STATE_EMPTIED = 10;
/**
* No information is available about the media resource.
*/
public static final int MEDIA_READY_STATE_HAVE_NOTHING = 0;
/**
* Enough of the media resource has been retrieved that the metadata
* attributes are available.
*/
public static final int MEDIA_READY_STATE_HAVE_METADATA = 1;
/**
* Data is available for the current playback position,
* but not enough to actually play more than one frame.
*/
public static final int MEDIA_READY_STATE_HAVE_CURRENT_DATA = 2;
/**
* Data for the current playback position as well as for at least a little
* bit of time into the future is available.
*/
public static final int MEDIA_READY_STATE_HAVE_FUTURE_DATA = 3;
/**
* Enough data is availableand the download rate is high enough that the media
* can be played through to the end without interruption.
*/
public static final int MEDIA_READY_STATE_HAVE_ENOUGH_DATA = 4;
/**
* Media source not found or unable to select any of the child elements
* for playback during resource selection.
*/
public static final int MEDIA_ERROR_NETWORK_NO_SOURCE = 0;
/**
* The fetching of the associated resource was aborted by the user's request.
*/
public static final int MEDIA_ERROR_ABORTED = 1;
/**
* Some kind of network error occurred which prevented the media from being
* successfully fetched, despite having previously been available.
*/
public static final int MEDIA_ERROR_NETWORK = 2;
/**
* Despite having previously been determined to be usable,
* an error occurred while trying to decode the media resource, resulting in an error.
*/
public static final int MEDIA_ERROR_DECODE = 3;
/**
* The associated resource or media provider object has been found to be unsuitable.
*/
public static final int MEDIA_ERROR_SRC_NOT_SUPPORTED = 4;
/**
* Data class with the Metadata associated to a Media Element.
**/
public static class Metadata {
/**
* Contains the current media source URI.
*/
public final String currentSource;
/**
* Indicates the duration of the media in seconds.
*/
public final double duration;
/**
* Indicates the width of the video in device pixels.
*/
public final long width;
/**
* Indicates the height of the video in device pixels.
*/
public final long height;
/**
* Indicates if seek operations are compatible with the media.
*/
public final boolean isSeekable;
/**
* Indicates the number of audio tracks included in the media.
*/
public final int audioTrackCount;
/**
* Indicates the number of video tracks included in the media.
*/
public final int videoTrackCount;
/* package */ Metadata(final GeckoBundle bundle) {
currentSource = bundle.getString("src", "");
duration = bundle.getDouble("duration", 0);
width = bundle.getLong("width", 0);
height = bundle.getLong("height", 0);
isSeekable = bundle.getBoolean("seekable", false);
audioTrackCount = bundle.getInt("audioTrackCount", 0);
videoTrackCount = bundle.getInt("videoTrackCount", 0);
}
}
/**
* Data class that indicates infomation about a media load progress event.
**/
public static class LoadProgressInfo {
/*
* Class used to represent a set of time ranges.
*/
public class TimeRange {
/* package */ TimeRange(double start, double end) {
this.start = start;
this.end = end;
}
/*
* The start time of the range in seconds.
*/
public final double start;
/*
* The end time of the range in seconds.
*/
public final double end;
}
/**
* The number of bytes transferred since the beginning of the operation
* or -1 if the data is not computable.
*/
public final long loadedBytes;
/**
* The total number of bytes of content that will be transferred during the operation
* or -1 if the data is not computable.
*/
public final long totalBytes;
/**
* The ranges of the media source that the browser has currently buffered.
* Null if the browser has not buffered any time range or the data is not computable.
*/
public final @Nullable TimeRange[] buffered;
/* package */ LoadProgressInfo(final GeckoBundle bundle) {
loadedBytes = bundle.getLong("loadedBytes", -1);
totalBytes = bundle.getLong("loadedBytes", -1);
double[] starts = bundle.getDoubleArray("timeRangeStarts");
double[] ends = bundle.getDoubleArray("timeRangeEnds");
if (starts == null || ends == null) {
buffered = null;
return;
}
if (starts.length != ends.length) {
throw new AssertionError("timeRangeStarts and timeRangeEnds length do not match");
}
buffered = new TimeRange[starts.length];
for (int i = 0; i < starts.length; ++i) {
buffered[i] = new TimeRange(starts[i], ends[i]);
}
}
}
/**
* This interface allows apps to handle media events.
**/
public interface Delegate {
/**
* The media playback state has changed.
*
* @param mediaElement A reference to the MediaElement that dispatched the event.
* @param mediaState The playback state of the media.
* One of the {@link #MEDIA_STATE_PLAY MEDIA_STATE_*} flags.
*/
default void onPlaybackStateChange(MediaElement mediaElement, @MediaStateFlags int mediaState) {}
/**
* The readiness state of the media has changed.
*
* @param mediaElement A reference to the MediaElement that dispatched the event.
* @param readyState The readiness state of the media.
* One of the {@link #MEDIA_READY_STATE_HAVE_NOTHING MEDIA_READY_STATE_*} flags.
*/
default void onReadyStateChange(MediaElement mediaElement, @ReadyStateFlags int readyState) {}
/**
* The media metadata has loaded or changed.
*
* @param mediaElement A reference to the MediaElement that dispatched the event.
* @param metaData The MetaData values of the media.
*/
default void onMetadataChange(MediaElement mediaElement, Metadata metaData) {}
/**
* Indicates that a loading operation is in progress for the media.
*
* @param mediaElement A reference to the MediaElement that dispatched the event.
* @param progressInfo Information about the load progress and buffered ranges.
*/
default void onLoadProgress(MediaElement mediaElement, LoadProgressInfo progressInfo) {}
/**
* The media audio volume has changed.
*
* @param mediaElement A reference to the MediaElement that dispatched the event.
* @param volume The volume of the media.
* @param muted True if the media is muted.
*/
default void onVolumeChange(MediaElement mediaElement, double volume, boolean muted) {}
/**
* The current playback time has changed. This event is usually dispatched every 250ms.
*
* @param mediaElement A reference to the MediaElement that dispatched the event.
* @param time The current playback time in seconds.
*/
default void onTimeChange(MediaElement mediaElement, double time) {}
/**
* The media playback speed has changed.
*
* @param mediaElement A reference to the MediaElement that dispatched the event.
* @param rate The current playback rate. A value of 1.0 indicates normal speed.
*/
default void onPlaybackRateChange(MediaElement mediaElement, double rate) {}
/**
* A media element has entered or exited fullscreen mode.
*
* @param mediaElement A reference to the MediaElement that dispatched the event.
* @param fullscreen True if the media has entered full screen mode.
*/
default void onFullscreenChange(MediaElement mediaElement, boolean fullscreen) {}
/**
* An error has occurred.
*
* @param mediaElement A reference to the MediaElement that dispatched the event.
* @param errorCode The error code.
* One of the {@link #MEDIA_ERROR_NETWORK_NO_SOURCE MEDIA_ERROR_*} flags.
*/
default void onError(MediaElement mediaElement, @MediaErrorFlags int errorCode) {}
}
/* package */ long getVideoId() {
return mVideoId;
}
/**
* Gets the current the media callback handler.
*
* @return the current media callback handler.
*/
public @Nullable MediaElement.Delegate getDelegate() {
return mDelegate;
}
/**
* Sets the media callback handler.
* This will replace the current handler.
*
* @param delegate An implementation of MediaDelegate.
*/
public void setDelegate(final @Nullable MediaElement.Delegate delegate) {
if (mDelegate == delegate) {
return;
}
MediaElement.Delegate oldDelegate = mDelegate;
mDelegate = delegate;
if (oldDelegate != null && mDelegate == null) {
mSession.getEventDispatcher().dispatch("GeckoView:MediaUnobserve", createMessage());
mSession.getMediaElements().remove(mVideoId);
} else if (oldDelegate == null) {
mSession.getMediaElements().put(mVideoId, this);
mSession.getEventDispatcher().dispatch("GeckoView:MediaObserve", createMessage());
}
}
/**
* Pauses the media.
*/
public void pause() {
mSession.getEventDispatcher().dispatch("GeckoView:MediaPause", createMessage());
}
/**
* Plays the media.
*/
public void play() {
mSession.getEventDispatcher().dispatch("GeckoView:MediaPlay", createMessage());
}
/**
* Seek the media to a given time.
*
* @param time Seek time in seconds.
*/
public void seek(final double time) {
final GeckoBundle message = createMessage();
message.putDouble("time", time);
mSession.getEventDispatcher().dispatch("GeckoView:MediaSeek", message);
}
/**
* Set the volume at which the media will be played.
*
* @param volume A Volume value. It must fall between 0 and 1, where 0 is effectively muted
* and 1 is the loudest possible value.
*/
public void setVolume(final double volume) {
final GeckoBundle message = createMessage();
message.putDouble("volume", volume);
mSession.getEventDispatcher().dispatch("GeckoView:MediaSetVolume", message);
}
/**
* Mutes the media.
*
* @param muted True in order to mute the audio.
*/
public void setMuted(final boolean muted) {
final GeckoBundle message = createMessage();
message.putBoolean("muted", muted);
mSession.getEventDispatcher().dispatch("GeckoView:MediaSetMuted", message);
}
/**
* Sets the playback rate at which the media will be played.
*
* @param playbackRate The rate at which the media will be played.
* A value of 1.0 indicates normal speed.
*/
public void setPlaybackRate(final double playbackRate) {
final GeckoBundle message = createMessage();
message.putDouble("playbackRate", playbackRate);
mSession.getEventDispatcher().dispatch("GeckoView:MediaSetPlaybackRate", message);
}
// Helper methods used for event observers to update the current video state
/* package */ void notifyPlaybackStateChange(final String event) {
@MediaStateFlags int state;
switch (event.toLowerCase()) {
case "play":
state = MEDIA_STATE_PLAY;
break;
case "playing":
state = MEDIA_STATE_PLAYING;
break;
case "pause":
state = MEDIA_STATE_PAUSE;
break;
case "ended":
state = MEDIA_STATE_ENDED;
break;
case "seeking":
state = MEDIA_STATE_SEEKING;
break;
case "seeked":
state = MEDIA_STATE_SEEKED;
break;
case "stalled":
state = MEDIA_STATE_STALLED;
break;
case "suspend":
state = MEDIA_STATE_SUSPEND;
break;
case "waiting":
state = MEDIA_STATE_WAITING;
break;
case "abort":
state = MEDIA_STATE_ABORT;
break;
case "emptied":
state = MEDIA_STATE_EMPTIED;
break;
default:
throw new UnsupportedOperationException(event + " HTMLMediaElement event not implemented");
}
if (mDelegate != null) {
mDelegate.onPlaybackStateChange(this, state);
}
}
/* package */ void notifyReadyStateChange(final int readyState) {
if (mDelegate != null) {
mDelegate.onReadyStateChange(this, readyState);
}
}
/* package */ void notifyLoadProgress(final GeckoBundle message) {
if (mDelegate != null) {
mDelegate.onLoadProgress(this, new LoadProgressInfo(message));
}
}
/* package */ void notifyTimeChange(final double currentTime) {
if (mDelegate != null) {
mDelegate.onTimeChange(this, currentTime);
}
}
/* package */ void notifyVolumeChange(final double volume, final boolean muted) {
if (mDelegate != null) {
mDelegate.onVolumeChange(this, volume, muted);
}
}
/* package */ void notifyPlaybackRateChange(final double rate) {
if (mDelegate != null) {
mDelegate.onPlaybackRateChange(this, rate);
}
}
/* package */ void notifyMetadataChange(final GeckoBundle message) {
if (mDelegate != null) {
mDelegate.onMetadataChange(this, new Metadata(message));
}
}
/* package */ void notifyFullscreenChange(final boolean fullscreen) {
if (mDelegate != null) {
mDelegate.onFullscreenChange(this, fullscreen);
}
}
/* package */ void notifyError(final int aCode) {
if (mDelegate != null) {
mDelegate.onError(this, aCode);
}
}
private GeckoBundle createMessage() {
final GeckoBundle bundle = new GeckoBundle();
bundle.putLong("id", mVideoId);
return bundle;
}
/* package */ MediaElement(final long videoId, final GeckoSession session) {
mVideoId = videoId;
mSession = session;
}
final protected GeckoSession mSession;
final protected long mVideoId;
protected MediaElement.Delegate mDelegate;
}

Просмотреть файл

@ -0,0 +1,33 @@
/* 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";
var EXPORTED_SYMBOLS = ["GeckoViewMedia"];
ChromeUtils.import("resource://gre/modules/GeckoViewModule.jsm");
class GeckoViewMedia extends GeckoViewModule {
onEnable() {
this.registerListener([
"GeckoView:MediaObserve",
"GeckoView:MediaUnobserve",
"GeckoView:MediaPlay",
"GeckoView:MediaPause",
"GeckoView:MediaSeek",
"GeckoView:MediaSetVolume",
"GeckoView:MediaSetMuted",
"GeckoView:MediaSetPlaybackRate",
]);
}
onDisable() {
this.unregisterListener();
}
onEvent(aEvent, aData, aCallback) {
debug `onEvent: event=${aEvent}, data=${aData}`;
this.messageManager.sendAsyncMessage(aEvent, aData);
}
}

Просмотреть файл

@ -12,6 +12,7 @@ EXTRA_JS_MODULES += [
'GeckoViewChildModule.jsm',
'GeckoViewConsole.jsm',
'GeckoViewContent.jsm',
'GeckoViewMedia.jsm',
'GeckoViewModule.jsm',
'GeckoViewNavigation.jsm',
'GeckoViewProgress.jsm',

Просмотреть файл

@ -5457,6 +5457,7 @@ pref("network.trr.mode", 0);
pref("network.trr.uri", "https://mozilla.cloudflare-dns.com/dns-query");
// credentials to pass to DOH end-point
pref("network.trr.credentials", "");
pref("network.trr.custom_uri", "");
// Wait for captive portal confirmation before enabling TRR
#if defined(ANDROID)
// On Android, the captive portal is handled by the OS itself

Просмотреть файл

@ -1247,18 +1247,34 @@ class Artifacts(object):
return self._install_from_hg_pushheads(hg_pushheads, distdir)
def install_from_revset(self, revset, distdir):
if self._hg:
revision = subprocess.check_output([self._hg, 'log', '--template', '{node}\n',
'-r', revset], cwd=self._topsrcdir).strip()
if len(revision.split('\n')) != 1:
raise ValueError('hg revision specification must resolve to exactly one commit')
else:
revision = subprocess.check_output([self._git, 'rev-parse', revset], cwd=self._topsrcdir).strip()
revision = subprocess.check_output([self._git, 'cinnabar', 'git2hg', revision], cwd=self._topsrcdir).strip()
if len(revision.split('\n')) != 1:
raise ValueError('hg revision specification must resolve to exactly one commit')
if revision == "0" * 40:
raise ValueError('git revision specification must resolve to a commit known to hg')
revision = None
try:
if self._hg:
revision = subprocess.check_output([self._hg, 'log', '--template', '{node}\n',
'-r', revset], cwd=self._topsrcdir).strip()
elif self._git:
revset = subprocess.check_output([
self._git, 'rev-parse', '%s^{commit}' % revset],
stderr=open(os.devnull, 'w'), cwd=self._topsrcdir).strip()
else:
# Fallback to the exception handling case from both hg and git
raise subprocess.CalledProcessError()
except subprocess.CalledProcessError:
# If the mercurial of git commands above failed, it means the given
# revset is not known locally to the VCS. But if the revset looks
# like a complete sha1, assume it is a mercurial sha1 that hasn't
# been pulled, and use that.
if re.match(r'^[A-Fa-f0-9]{40}$', revset):
revision = revset
if revision is None and self._git:
revision = subprocess.check_output(
[self._git, 'cinnabar', 'git2hg', revset], cwd=self._topsrcdir).strip()
if revision == "0" * 40 or revision is None:
raise ValueError('revision specification must resolve to a commit known to hg')
if len(revision.split('\n')) != 1:
raise ValueError('revision specification must resolve to exactly one commit')
self.log(logging.INFO, 'artifact',
{'revset': revset,

Просмотреть файл

@ -267,7 +267,7 @@ linux64-base-toolchains/opt:
toolchains:
- linux64-clang-3.9
- linux64-gcc-6
- linux64-rust-1.29
- linux64-rust-1.30
- linux64-cbindgen
- linux64-sccache
- linux64-node
@ -301,7 +301,7 @@ linux64-base-toolchains/debug:
toolchains:
- linux64-clang-3.9
- linux64-gcc-6
- linux64-rust-1.29
- linux64-rust-1.30
- linux64-cbindgen
- linux64-sccache
- linux64-node

Просмотреть файл

@ -88,11 +88,7 @@ web-platform-tests-reftests:
description: "Web platform reftest run"
suite: web-platform-tests-reftests
treeherder-symbol: W(Wr)
chunks:
by-test-platform:
macosx.*: 1
default: 6
chunks: 6
e10s:
by-test-platform:
linux32/debug: both
@ -116,10 +112,7 @@ web-platform-tests-reftests-headless:
description: "Web platform reftest headless run"
suite: web-platform-tests-reftests
treeherder-symbol: W(WrH)
chunks:
by-test-platform:
macosx.*: 1
default: 6
chunks: 6
e10s:
by-test-platform:
linux32/debug: both

Просмотреть файл

@ -488,29 +488,6 @@ linux64-rust-1.28:
]
toolchain-artifact: public/build/rustc.tar.xz
linux64-rust-1.29:
description: "rust repack"
treeherder:
kind: build
platform: toolchains/opt
symbol: TL(rust-1.29)
tier: 1
worker-type: aws-provisioner-v1/gecko-{level}-b-linux
worker:
max-run-time: 7200
env:
UPLOAD_DIR: artifacts
run:
using: toolchain-script
script: repack_rust.py
arguments: [
'--channel', '1.29.2',
'--host', 'x86_64-unknown-linux-gnu',
'--target', 'x86_64-unknown-linux-gnu',
'--target', 'i686-unknown-linux-gnu',
]
toolchain-artifact: public/build/rustc.tar.xz
linux64-rust-1.30:
description: "rust repack"
treeherder:

Просмотреть файл

@ -230,9 +230,6 @@ class WebPlatformTest(TestingMixin, MercurialScript, CodeCoverageMixin, AndroidM
cmd += ["--device-serial=%s" % self.device_serial]
cmd += ["--package-name=%s" % self.query_package_name()]
if sys.platform == "darwin":
cmd += ["--exclude=css"]
if mozinfo.info["os"] == "win" and mozinfo.info["os_version"] == "6.1":
# On Windows 7 --install-fonts fails, so fall back to a Firefox-specific codepath
self._install_fonts()

Просмотреть файл

@ -186,13 +186,15 @@ def get_raptor_test_list(args, oskey):
# also allow the cmd line opt to override pagecycles auto set when gecko profiling is on
if args.page_cycles is not None:
LOG.info("setting page-cycles to %d as specified on the command line" % args.page_cycles)
next_test['page_cycles'] = args.page_cycles
for next_test in tests_to_run:
next_test['page_cycles'] = args.page_cycles
# if --page-timeout command line arg was provided, override the page_timeout value
# that was in the manifest/test INI with the command line arg value instead
if args.page_timeout is not None:
LOG.info("setting page-timeout to %d as specified on the command line" % args.page_timeout)
next_test['page_timeout'] = args.page_timeout
for next_test in tests_to_run:
next_test['page_timeout'] = args.page_timeout
# write out .json test setting files for the control server to read and send to web ext
if len(tests_to_run) != 0:

Просмотреть файл

@ -39,6 +39,7 @@
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource://gre/modules/Timer.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "aomStartup",
"@mozilla.org/addons/addon-manager-startup;1",
@ -93,9 +94,25 @@ this.pageloader = class extends ExtensionAPI {
]);
if (env.exists("MOZ_USE_PAGELOADER")) {
// This is async but we're delibeately not await-ing or return-ing
// TalosPowers is a separate WebExtension that may or may not already have
// finished loading. tryLoad is used to wait for TalosPowers to be around
// before continuing.
async function tryLoad() {
try {
ChromeUtils.import("resource://talos-powers/TalosParentProfiler.jsm");
} catch (err) {
await new Promise(resolve => setTimeout(resolve, 500));
return tryLoad();
}
return null;
}
// talosStart is async but we're deliberately not await-ing or return-ing
// it here since it doesn't block extension startup.
talosStart();
tryLoad().then(() => {
talosStart();
});
}
}

Просмотреть файл

@ -8,10 +8,8 @@
ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
ChromeUtils.import("resource://gre/modules/E10SUtils.jsm");
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyScriptGetter(this, "TalosParentProfiler",
"resource://talos-powers/TalosParentProfiler.js");
ChromeUtils.defineModuleGetter(this, "TalosParentProfiler",
"resource://talos-powers/TalosParentProfiler.jsm");
var NUM_CYCLES = 5;
var numPageCycles = 1;

Просмотреть файл

@ -9,15 +9,14 @@
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetters(this, {
BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
Services: "resource://gre/modules/Services.jsm",
SessionStartup: "resource:///modules/sessionstore/SessionStartup.jsm",
setTimeout: "resource://gre/modules/Timer.jsm",
StartupPerformance: "resource:///modules/sessionstore/StartupPerformance.jsm",
TalosParentProfiler: "resource://talos-powers/TalosParentProfiler.jsm",
});
XPCOMUtils.defineLazyScriptGetter(this, "TalosParentProfiler",
"resource://talos-powers/TalosParentProfiler.js");
/* globals ExtensionAPI */
this.sessionrestore = class extends ExtensionAPI {
@ -47,6 +46,21 @@ this.sessionrestore = class extends ExtensionAPI {
async function observe() {
Services.obs.removeObserver(observe, StartupPerformance.RESTORED_TOPIC);
let win = BrowserWindowTracker.getTopWindow();
let args = win.arguments[0];
if (args && args instanceof Ci.nsIArray) {
// For start-up tests Gecko Profiler arguments are passed to the first URL in
// the query string, with the presumption that some tab that is loaded at start-up
// will want to use them in the TalosContentProfiler.js script.
//
// Because we're running this part of the test in the parent process, we
// pull those arguments from the top window directly. This is mainly so
// that TalosParentProfiler knows where to save the profile.
Cu.importGlobalProperties(["URL"]);
let url = new URL(args.queryElementAt(0, Ci.nsISupportsString).data);
TalosParentProfiler.initFromURLQueryParams(url.search);
}
await TalosParentProfiler.pause("This test measures the time between sessionRestoreInit and sessionRestored, ignore everything around that");
await TalosParentProfiler.finishStartupProfiling();

Просмотреть файл

@ -1,21 +0,0 @@
<html>
<head>
<meta charset="UTF-8"/>
<title>Session Restore Regression Test</title>
<script type="text/javascript" src="chrome://pageloader/content/MozillaFileLogger.js"></script>
<script type="text/javascript" src="resource://talos-powers/TalosContentProfiler.js"></script>
<script type="text/javascript" src="resource://talos-powers/TalosPowersContent.js"></script>
<script type="text/javascript" src="chrome://session-restore-test/content/main.js">
</script>
<div>
<strong>Time between sessionRestoreInit and sessionRestored</strong>
<span id="sessionRestoreInit-to-sessionRestored">
(in progress)
</span>
</div>
</body>
</html>

Просмотреть файл

@ -1,40 +0,0 @@
"use strict";
var Services = ChromeUtils.import("resource://gre/modules/Services.jsm", {}).Services;
// Process Message Manager topics.
const MSG_REQUEST = "session-restore-test?duration";
const MSG_PROVIDE = "session-restore-test:duration";
addEventListener("load", function() {
Services.cpmm.addMessageListener(MSG_PROVIDE,
/**
* Display the result, send it to the harness and quit.
*/
async function finish(msg) {
console.log(`main.js: received data on ${MSG_PROVIDE}`, msg);
Services.cpmm.removeMessageListener(MSG_PROVIDE, finish);
var duration = msg.data.duration;
TalosContentProfiler.initFromURLQueryParams(location.search);
await TalosContentProfiler.pause("This test measures the time between sessionRestoreInit and sessionRestored, ignore everything around that");
await TalosContentProfiler.finishStartupProfiling();
// Show result on screen. Nice but not really necessary.
document.getElementById("sessionRestoreInit-to-sessionRestored").textContent = duration + "ms";
// Report data to Talos, if possible
dumpLog("__start_report" +
duration +
"__end_report\n\n");
// Next one is required by the test harness but not used
dumpLog("__startTimestamp" +
Date.now() + // eslint-disable-line mozilla/avoid-Date-timing
"__endTimestamp\n\n");
TalosPowersContent.goQuitApplication();
});
// In case the add-on has broadcasted the message before we were loaded,
// request a second broadcast.
Services.cpmm.sendAsyncMessage(MSG_REQUEST, {});
});

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше