mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 11:25:00 +00:00
merge mozilla-central to mozilla-inbound. CLOSED TREE
This commit is contained in:
commit
c12ee0bc10
@ -138,3 +138,88 @@ add_task(async function testWindowCreate() {
|
||||
await extension.awaitFinish("window-create");
|
||||
await extension.unload();
|
||||
});
|
||||
|
||||
add_task(async function testWebNavigationOnWindowCreateTabId() {
|
||||
async function background() {
|
||||
const webNavEvents = [];
|
||||
const onceTabsAttached = [];
|
||||
|
||||
let promiseTabAttached = (tab) => {
|
||||
return new Promise(resolve => {
|
||||
browser.tabs.onAttached.addListener(function listener(tabId) {
|
||||
if (tabId !== tab.id) {
|
||||
return;
|
||||
}
|
||||
browser.tabs.onAttached.removeListener(listener);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Listen to webNavigation.onCompleted events to ensure that
|
||||
// it is not going to be fired when we move the existent tabs
|
||||
// to new windows.
|
||||
browser.webNavigation.onCompleted.addListener((data) => {
|
||||
webNavEvents.push(data);
|
||||
});
|
||||
|
||||
// Wait for the list of urls needed to select the test tabs,
|
||||
// and then move these tabs to a new window and assert that
|
||||
// no webNavigation.onCompleted events should be received
|
||||
// while the tabs are being adopted into the new windows.
|
||||
browser.test.onMessage.addListener(async (msg, testTabURLs) => {
|
||||
if (msg !== "testTabURLs") {
|
||||
return;
|
||||
}
|
||||
|
||||
// Retrieve the tabs list and filter out the tabs that should
|
||||
// not be moved into a new window.
|
||||
let allTabs = await browser.tabs.query({});
|
||||
let testTabs = allTabs.filter(tab => {
|
||||
return testTabURLs.includes(tab.url);
|
||||
});
|
||||
|
||||
browser.test.assertEq(2, testTabs.length, "Got the expected number of test tabs");
|
||||
|
||||
for (let tab of testTabs) {
|
||||
onceTabsAttached.push(promiseTabAttached(tab));
|
||||
await browser.windows.create({tabId: tab.id});
|
||||
}
|
||||
|
||||
// Wait the tabs to have been attached to the new window and then assert that no
|
||||
// webNavigation.onCompleted event has been received.
|
||||
browser.test.log("Waiting tabs move to new window to be attached");
|
||||
await Promise.all(onceTabsAttached);
|
||||
|
||||
browser.test.assertEq("[]", JSON.stringify(webNavEvents),
|
||||
"No webNavigation.onCompleted event should have been received");
|
||||
|
||||
// Remove all the test tabs before exiting the test successfully.
|
||||
for (let tab of testTabs) {
|
||||
await browser.tabs.remove(tab.id);
|
||||
}
|
||||
|
||||
browser.test.notifyPass("webNavigation-on-window-create-tabId");
|
||||
});
|
||||
}
|
||||
|
||||
const testURLs = ["http://example.com/", "http://example.org/"];
|
||||
|
||||
for (let url of testURLs) {
|
||||
await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
|
||||
}
|
||||
|
||||
let extension = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
"permissions": ["tabs", "webNavigation"],
|
||||
},
|
||||
background,
|
||||
});
|
||||
|
||||
await extension.startup();
|
||||
|
||||
await extension.sendMessage("testTabURLs", testURLs);
|
||||
|
||||
await extension.awaitFinish("webNavigation-on-window-create-tabId");
|
||||
await extension.unload();
|
||||
});
|
||||
|
@ -765,14 +765,12 @@ var PlacesUIUtils = {
|
||||
* Helper for guessing scheme from an url string.
|
||||
* Used to avoid nsIURI overhead in frequently called UI functions.
|
||||
*
|
||||
* @param aUrlString the url to guess the scheme from.
|
||||
*
|
||||
* @param {string} href The url to guess the scheme from.
|
||||
* @return guessed scheme for this url string.
|
||||
*
|
||||
* @note this is not supposed be perfect, so use it only for UI purposes.
|
||||
*/
|
||||
guessUrlSchemeForUI: function PUIU_guessUrlSchemeForUI(aUrlString) {
|
||||
return aUrlString.substr(0, aUrlString.indexOf(":"));
|
||||
guessUrlSchemeForUI(href) {
|
||||
return href.substr(0, href.indexOf(":"));
|
||||
},
|
||||
|
||||
getBestTitle: function PUIU_getBestTitle(aNode, aDoNotCutTitle) {
|
||||
|
@ -148,9 +148,9 @@ function handleGUMRequest(aSubject, aTopic, aData) {
|
||||
constraints, devices, secure, isHandlingUserInput);
|
||||
},
|
||||
function(error) {
|
||||
// bug 827146 -- In the future, the UI should catch NotFoundError
|
||||
// and allow the user to plug in a device, instead of immediately failing.
|
||||
denyGUMRequest({callID: aSubject.callID}, error);
|
||||
// Device enumeration is done ahead of handleGUMRequest, so we're not
|
||||
// responsible for handling the NotFoundError spec case.
|
||||
denyGUMRequest({callID: aSubject.callID});
|
||||
},
|
||||
aSubject.innerWindowID,
|
||||
aSubject.callID);
|
||||
@ -203,7 +203,9 @@ function prompt(aContentWindow, aWindowID, aCallID, aConstraints, aDevices, aSec
|
||||
requestTypes.push(sharingAudio ? "AudioCapture" : "Microphone");
|
||||
|
||||
if (!requestTypes.length) {
|
||||
denyGUMRequest({callID: aCallID}, "NotFoundError");
|
||||
// Device enumeration is done ahead of handleGUMRequest, so we're not
|
||||
// responsible for handling the NotFoundError spec case.
|
||||
denyGUMRequest({callID: aCallID});
|
||||
return;
|
||||
}
|
||||
|
||||
@ -235,13 +237,8 @@ function prompt(aContentWindow, aWindowID, aCallID, aConstraints, aDevices, aSec
|
||||
mm.sendAsyncMessage("webrtc:Request", request);
|
||||
}
|
||||
|
||||
function denyGUMRequest(aData, aError) {
|
||||
let msg = null;
|
||||
if (aError) {
|
||||
msg = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
|
||||
msg.data = aError;
|
||||
}
|
||||
Services.obs.notifyObservers(msg, "getUserMedia:response:deny", aData.callID);
|
||||
function denyGUMRequest(aData) {
|
||||
Services.obs.notifyObservers(null, "getUserMedia:response:deny", aData.callID);
|
||||
|
||||
if (!aData.windowID)
|
||||
return;
|
||||
|
@ -17,6 +17,8 @@
|
||||
#include "mozilla/dom/StructuredCloneTags.h"
|
||||
// for mozilla::dom::workerinternals::kJSPrincipalsDebugToken
|
||||
#include "mozilla/dom/workerinternals/JSSettings.h"
|
||||
// for mozilla::dom::worklet::kJSPrincipalsDebugToken
|
||||
#include "mozilla/dom/WorkletPrincipal.h"
|
||||
#include "mozilla/ipc/BackgroundUtils.h"
|
||||
|
||||
using namespace mozilla;
|
||||
@ -92,6 +94,8 @@ JSPrincipals::dump()
|
||||
NS_SUCCEEDED(rv) ? str.get() : "(unknown)");
|
||||
} else if (debugToken == dom::workerinternals::kJSPrincipalsDebugToken) {
|
||||
fprintf(stderr, "Web Worker principal singleton (%p)\n", this);
|
||||
} else if (debugToken == mozilla::dom::WorkletPrincipal::kJSPrincipalsDebugToken) {
|
||||
fprintf(stderr, "Web Worklet principal singleton (%p)\n", this);
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"!!! JSPrincipals (%p) is not nsJSPrincipals instance - bad token: "
|
||||
|
@ -7,7 +7,7 @@ const { Component, createFactory } = require("devtools/client/shared/vendor/reac
|
||||
const dom = require("devtools/client/shared/vendor/react-dom-factories");
|
||||
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
|
||||
const {div, button} = dom;
|
||||
const {openLink} = require("devtools/client/shared/link");
|
||||
const {openTrustedLink} = require("devtools/client/shared/link");
|
||||
|
||||
const Menu = require("devtools/client/framework/menu");
|
||||
const MenuItem = require("devtools/client/framework/menu-item");
|
||||
@ -397,7 +397,7 @@ function showMeatballMenu(
|
||||
id: "toolbox-meatball-menu-gettingstarted",
|
||||
label: L10N.getStr("toolbox.meatballMenu.gettingStarted.label"),
|
||||
click: () => {
|
||||
openLink("https://developer.mozilla.org/docs/Tools", toolbox);
|
||||
openTrustedLink("https://developer.mozilla.org/docs/Tools", toolbox);
|
||||
},
|
||||
}));
|
||||
|
||||
@ -406,7 +406,7 @@ function showMeatballMenu(
|
||||
id: "toolbox-meatball-menu-feedback",
|
||||
label: L10N.getStr("toolbox.meatballMenu.giveFeedback.label"),
|
||||
click: () => {
|
||||
openLink("https://discourse.mozilla.org/c/devtools", toolbox);
|
||||
openTrustedLink("https://discourse.mozilla.org/c/devtools", toolbox);
|
||||
},
|
||||
}));
|
||||
|
||||
|
@ -555,6 +555,15 @@ MessageManagerTunnel.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Expose the inner frame's value for `processMessageManager`. This is done mainly to
|
||||
* allow Browser Content Toolbox (which needs to find a tab's process) to work for RDM
|
||||
* tabs. (The property is quite rarely used in general.)
|
||||
*/
|
||||
get processMessageManager() {
|
||||
return this.innerParentMM.processMessageManager;
|
||||
},
|
||||
|
||||
loadFrameScript(url, ...args) {
|
||||
debug(`Calling loadFrameScript for ${url}`);
|
||||
|
||||
|
@ -5,23 +5,53 @@
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Opens |url| in a new tab.
|
||||
* Retrieve the top window for the provided toolbox that should have the expected
|
||||
* open*LinkIn methods.
|
||||
*/
|
||||
exports.openLink = async function(url, toolbox) {
|
||||
function _getTopWindow(toolbox) {
|
||||
const parentDoc = toolbox.doc;
|
||||
if (!parentDoc) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
const win = parentDoc.querySelector("window");
|
||||
if (!win) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
const top = win.ownerDocument.defaultView.top;
|
||||
if (!top || typeof top.openUILinkIn !== "function") {
|
||||
return;
|
||||
}
|
||||
return win.ownerDocument.defaultView.top;
|
||||
}
|
||||
|
||||
top.openUILinkIn(url, "tab");
|
||||
/**
|
||||
* Opens a |url| controlled by webcontent in a new tab.
|
||||
*
|
||||
* @param {String} url
|
||||
* The url to open.
|
||||
* @param {Toolbox} toolbox
|
||||
* Toolbox reference to find the parent window.
|
||||
* @param {Object} options
|
||||
* Optional parameters, see documentation for openUILinkIn in utilityOverlay.js
|
||||
*/
|
||||
exports.openWebLink = async function(url, toolbox, options) {
|
||||
const top = _getTopWindow(toolbox);
|
||||
if (top && typeof top.openWebLinkIn === "function") {
|
||||
top.openWebLinkIn(url, "tab", options);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Open a trusted |url| in a new tab using the SystemPrincipal.
|
||||
*
|
||||
* @param {String} url
|
||||
* The url to open.
|
||||
* @param {Toolbox} toolbox
|
||||
* Toolbox reference to find the parent window.
|
||||
* @param {Object} options
|
||||
* Optional parameters, see documentation for openUILinkIn in utilityOverlay.js
|
||||
*/
|
||||
exports.openTrustedLink = async function(url, toolbox, options) {
|
||||
const top = _getTopWindow(toolbox);
|
||||
if (top && typeof top.openTrustedLinkIn === "function") {
|
||||
top.openTrustedLinkIn(url, "tab", options);
|
||||
}
|
||||
};
|
||||
|
@ -353,8 +353,6 @@ a {
|
||||
|
||||
/* JSTerm Styles */
|
||||
|
||||
html #jsterm-wrapper,
|
||||
html .jsterm-stack-node,
|
||||
html .jsterm-input-node-html,
|
||||
html #webconsole-notificationbox {
|
||||
flex: 0;
|
||||
@ -391,10 +389,6 @@ html #webconsole-notificationbox {
|
||||
|
||||
/* styles for the new HTML frontend */
|
||||
|
||||
html .jsterm-stack-node {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
textarea.jsterm-input-node,
|
||||
textarea.jsterm-complete-node {
|
||||
width: 100%;
|
||||
@ -432,6 +426,12 @@ textarea.jsterm-input-node:focus {
|
||||
border-radius: 0 0 4px 4px;
|
||||
}
|
||||
|
||||
/* Unset the bottom right radius on the jsterm inputs when the sidebar is visible */
|
||||
:root[platform="mac"] .sidebar ~ .jsterm-input-container textarea.jsterm-input-node,
|
||||
:root[platform="mac"] .sidebar ~ .jsterm-input-container textarea.jsterm-complete-node {
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
|
||||
/* styles for the old frontend, which can be removed in Bug 1381834 */
|
||||
|
||||
@ -1097,14 +1097,42 @@ body #output-container {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Here's what the layout of the console looks like:
|
||||
*
|
||||
* +------------------------------+--------------+
|
||||
* | FILTER BAR | |
|
||||
* +------------------------------+ |
|
||||
* | | |
|
||||
* | CONSOLE OUTPUT | SIDEBAR |
|
||||
* | | |
|
||||
* +------------------------------+ |
|
||||
* | NOTIFICATION BOX | |
|
||||
* +------------------------------+ |
|
||||
* | JSTERM CONTAINER | |
|
||||
* +------------------------------+--------------+
|
||||
*/
|
||||
.webconsole-output-wrapper {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
grid-template-rows: auto 1fr;
|
||||
grid-template-columns: minmax(200px, 1fr) auto;
|
||||
grid-template-rows: auto 1fr auto auto;
|
||||
height: 100%;
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
.webconsole-output-wrapper #webconsole-notificationbox {
|
||||
grid-column: 1 / 2;
|
||||
grid-row: 3 / 4;
|
||||
}
|
||||
|
||||
.webconsole-output-wrapper .jsterm-input-container {
|
||||
grid-column: 1 / 2;
|
||||
grid-row: -1 / -2;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Object Inspector */
|
||||
.webconsole-output-wrapper .object-inspector.tree {
|
||||
display: inline-block;
|
||||
|
1373
devtools/client/webconsole/components/JSTerm.js
Normal file
1373
devtools/client/webconsole/components/JSTerm.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -15,6 +15,7 @@ DevToolsModules(
|
||||
'FilterButton.js',
|
||||
'FilterCheckbox.js',
|
||||
'GripMessageBody.js',
|
||||
'JSTerm.js',
|
||||
'Message.js',
|
||||
'MessageContainer.js',
|
||||
'MessageIcon.js',
|
||||
|
@ -17,7 +17,6 @@ DevToolsModules(
|
||||
'console-commands.js',
|
||||
'constants.js',
|
||||
'hudservice.js',
|
||||
'jsterm.js',
|
||||
'main.js',
|
||||
'new-console-output-wrapper.js',
|
||||
'new-webconsole.js',
|
||||
|
@ -18,14 +18,15 @@ const EventEmitter = require("devtools/shared/event-emitter");
|
||||
const ConsoleOutput = createFactory(require("devtools/client/webconsole/components/ConsoleOutput"));
|
||||
const FilterBar = createFactory(require("devtools/client/webconsole/components/FilterBar"));
|
||||
const SideBar = createFactory(require("devtools/client/webconsole/components/SideBar"));
|
||||
const JSTerm = createFactory(require("devtools/client/webconsole/components/JSTerm"));
|
||||
|
||||
let store = null;
|
||||
|
||||
function NewConsoleOutputWrapper(parentNode, jsterm, toolbox, owner, document) {
|
||||
function NewConsoleOutputWrapper(parentNode, hud, toolbox, owner, document) {
|
||||
EventEmitter.decorate(this);
|
||||
|
||||
this.parentNode = parentNode;
|
||||
this.jsterm = jsterm;
|
||||
this.hud = hud;
|
||||
this.toolbox = toolbox;
|
||||
this.owner = owner;
|
||||
this.document = document;
|
||||
@ -37,13 +38,13 @@ function NewConsoleOutputWrapper(parentNode, jsterm, toolbox, owner, document) {
|
||||
this.queuedRequestUpdates = [];
|
||||
this.throttledDispatchPromise = null;
|
||||
|
||||
store = configureStore(this.jsterm.hud);
|
||||
store = configureStore(this.hud);
|
||||
}
|
||||
NewConsoleOutputWrapper.prototype = {
|
||||
init: function() {
|
||||
return new Promise((resolve) => {
|
||||
const attachRefToHud = (id, node) => {
|
||||
this.jsterm.hud[id] = node;
|
||||
this.hud[id] = node;
|
||||
};
|
||||
// Focus the input line whenever the output area is clicked.
|
||||
this.parentNode.addEventListener("click", (event) => {
|
||||
@ -75,15 +76,17 @@ NewConsoleOutputWrapper.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
this.jsterm.focus();
|
||||
if (this.hud && this.hud.jsterm) {
|
||||
this.hud.jsterm.focus();
|
||||
}
|
||||
});
|
||||
|
||||
let { hud } = this.jsterm;
|
||||
let { hud } = this;
|
||||
|
||||
const serviceContainer = {
|
||||
attachRefToHud,
|
||||
emitNewMessage: (node, messageId, timeStamp) => {
|
||||
this.jsterm.hud.emit("new-messages", new Set([{
|
||||
hud.emit("new-messages", new Set([{
|
||||
node,
|
||||
messageId,
|
||||
timeStamp,
|
||||
@ -140,9 +143,15 @@ NewConsoleOutputWrapper.prototype = {
|
||||
store.dispatch(actions.showObjectInSidebar(rootActorId, messageId));
|
||||
} : null;
|
||||
|
||||
let menu = createContextMenu(this.jsterm, this.parentNode,
|
||||
{ actor, clipboardText, variableText, message,
|
||||
serviceContainer, openSidebar, rootActorId });
|
||||
let menu = createContextMenu(this.hud, this.parentNode, {
|
||||
actor,
|
||||
clipboardText,
|
||||
variableText,
|
||||
message,
|
||||
serviceContainer,
|
||||
openSidebar,
|
||||
rootActorId
|
||||
});
|
||||
|
||||
// Emit the "menu-open" event for testing.
|
||||
menu.once("open", () => this.emit("menu-open"));
|
||||
@ -155,7 +164,7 @@ NewConsoleOutputWrapper.prototype = {
|
||||
Object.assign(serviceContainer, {
|
||||
onViewSourceInDebugger: frame => {
|
||||
this.toolbox.viewSourceInDebugger(frame.url, frame.line).then(() =>
|
||||
this.jsterm.hud.emit("source-in-debugger-opened")
|
||||
this.hud.emit("source-in-debugger-opened")
|
||||
);
|
||||
},
|
||||
onViewSourceInScratchpad: frame => this.toolbox.viewSourceInScratchpad(
|
||||
@ -199,34 +208,29 @@ NewConsoleOutputWrapper.prototype = {
|
||||
});
|
||||
}
|
||||
|
||||
let consoleOutput = ConsoleOutput({
|
||||
serviceContainer,
|
||||
onFirstMeaningfulPaint: resolve
|
||||
});
|
||||
|
||||
let filterBar = FilterBar({
|
||||
hidePersistLogsCheckbox: this.jsterm.hud.isBrowserConsole,
|
||||
serviceContainer: {
|
||||
attachRefToHud
|
||||
}
|
||||
});
|
||||
|
||||
let sideBar = SideBar({
|
||||
serviceContainer,
|
||||
});
|
||||
|
||||
let provider = createElement(
|
||||
Provider,
|
||||
{ store },
|
||||
dom.div(
|
||||
{className: "webconsole-output-wrapper"},
|
||||
filterBar,
|
||||
consoleOutput,
|
||||
sideBar
|
||||
FilterBar({
|
||||
hidePersistLogsCheckbox: this.hud.isBrowserConsole,
|
||||
serviceContainer: {
|
||||
attachRefToHud
|
||||
}
|
||||
}),
|
||||
ConsoleOutput({
|
||||
serviceContainer,
|
||||
onFirstMeaningfulPaint: resolve
|
||||
}),
|
||||
SideBar({
|
||||
serviceContainer,
|
||||
}),
|
||||
JSTerm({
|
||||
hud: this.hud,
|
||||
}),
|
||||
));
|
||||
this.body = ReactDOM.render(provider, this.parentNode);
|
||||
|
||||
this.jsterm.focus();
|
||||
});
|
||||
},
|
||||
|
||||
@ -243,16 +247,15 @@ NewConsoleOutputWrapper.prototype = {
|
||||
: packet.timestamp;
|
||||
|
||||
promise = new Promise(resolve => {
|
||||
let jsterm = this.jsterm;
|
||||
jsterm.hud.on("new-messages", function onThisMessage(messages) {
|
||||
this.hud.on("new-messages", function onThisMessage(messages) {
|
||||
for (let m of messages) {
|
||||
if (m.timeStamp === timeStampToMatch) {
|
||||
resolve(m.node);
|
||||
jsterm.hud.off("new-messages", onThisMessage);
|
||||
this.hud.off("new-messages", onThisMessage);
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
}.bind(this));
|
||||
});
|
||||
} else {
|
||||
promise = Promise.resolve();
|
||||
@ -388,7 +391,7 @@ NewConsoleOutputWrapper.prototype = {
|
||||
if (this.queuedMessageUpdates.length > 0) {
|
||||
this.queuedMessageUpdates.forEach(({ message, res }) => {
|
||||
store.dispatch(actions.networkMessageUpdate(message, null, res));
|
||||
this.jsterm.hud.emit("network-message-updated", res);
|
||||
this.hud.emit("network-message-updated", res);
|
||||
});
|
||||
this.queuedMessageUpdates = [];
|
||||
}
|
||||
@ -405,7 +408,7 @@ NewConsoleOutputWrapper.prototype = {
|
||||
// (netmonitor/src/connector/firefox-data-provider).
|
||||
// This event might be utilized in tests to find the right
|
||||
// time when to finish.
|
||||
this.jsterm.hud.emit("network-request-payload-ready");
|
||||
this.hud.emit("network-request-payload-ready");
|
||||
}
|
||||
done();
|
||||
}, 50);
|
||||
|
@ -12,7 +12,6 @@ const promise = require("promise");
|
||||
const defer = require("devtools/shared/defer");
|
||||
const Services = require("Services");
|
||||
const { gDevTools } = require("devtools/client/framework/devtools");
|
||||
const { JSTerm } = require("devtools/client/webconsole/jsterm");
|
||||
const { WebConsoleConnectionProxy } = require("devtools/client/webconsole/webconsole-connection-proxy");
|
||||
const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
|
||||
const { l10n } = require("devtools/client/webconsole/utils/messages");
|
||||
@ -82,13 +81,7 @@ NewWebConsoleFrame.prototype = {
|
||||
*/
|
||||
async init() {
|
||||
this._initUI();
|
||||
let connectionInited = this._initConnection();
|
||||
// Don't reject if the history fails to load for some reason.
|
||||
// This would be fine, the panel will just start with empty history.
|
||||
let onJsTermHistoryLoaded = this.jsterm.historyLoaded
|
||||
.catch(() => {});
|
||||
|
||||
await Promise.all([connectionInited, onJsTermHistoryLoaded]);
|
||||
await this._initConnection();
|
||||
await this.newConsoleOutput.init();
|
||||
|
||||
let id = WebConsoleUtils.supportsString(this.hudId);
|
||||
@ -104,8 +97,6 @@ NewWebConsoleFrame.prototype = {
|
||||
Services.prefs.removeObserver(PREF_MESSAGE_TIMESTAMP, this._onToolboxPrefChanged);
|
||||
this.React = this.ReactDOM = this.FrameView = null;
|
||||
if (this.jsterm) {
|
||||
this.jsterm.off("sidebar-opened", this.resize);
|
||||
this.jsterm.off("sidebar-closed", this.resize);
|
||||
this.jsterm.destroy();
|
||||
this.jsterm = null;
|
||||
}
|
||||
@ -207,23 +198,13 @@ NewWebConsoleFrame.prototype = {
|
||||
this.rootElement = this.document.documentElement;
|
||||
|
||||
this.outputNode = this.document.getElementById("output-container");
|
||||
this.completeNode = this.document.querySelector(".jsterm-complete-node");
|
||||
this.inputNode = this.document.querySelector(".jsterm-input-node");
|
||||
|
||||
this.jsterm = new JSTerm(this);
|
||||
this.jsterm.init();
|
||||
|
||||
let toolbox = gDevTools.getToolbox(this.owner.target);
|
||||
|
||||
// @TODO Remove this once JSTerm is handled with React/Redux.
|
||||
this.window.jsterm = this.jsterm;
|
||||
// @TODO Once the toolbox has been converted to React, see if passing
|
||||
// in JSTerm is still necessary.
|
||||
|
||||
// Handle both launchpad and toolbox loading
|
||||
let Wrapper = this.owner.NewConsoleOutputWrapper || this.window.NewConsoleOutput;
|
||||
this.newConsoleOutput = new Wrapper(
|
||||
this.outputNode, this.jsterm, toolbox, this.owner, this.document);
|
||||
this.newConsoleOutput =
|
||||
new Wrapper(this.outputNode, this, toolbox, this.owner, this.document);
|
||||
// Toggle the timestamp on preference change
|
||||
Services.prefs.addObserver(PREF_MESSAGE_TIMESTAMP, this._onToolboxPrefChanged);
|
||||
this._onToolboxPrefChanged();
|
||||
|
@ -11,6 +11,7 @@ DIRS += [
|
||||
]
|
||||
DevToolsModules(
|
||||
'console-output.js',
|
||||
'jsterm.js',
|
||||
'webconsole.js',
|
||||
)
|
||||
with Files('**'):
|
||||
|
@ -31,8 +31,8 @@ loader.lazyRequireGetter(this, "ConsoleOutput", "devtools/client/webconsole/old/
|
||||
loader.lazyRequireGetter(this, "Messages", "devtools/client/webconsole/old/console-output", true);
|
||||
loader.lazyRequireGetter(this, "EnvironmentClient", "devtools/shared/client/environment-client");
|
||||
loader.lazyRequireGetter(this, "ObjectClient", "devtools/shared/client/object-client");
|
||||
loader.lazyRequireGetter(this, "JSTerm", "devtools/client/webconsole/jsterm", true);
|
||||
loader.lazyRequireGetter(this, "gSequenceId", "devtools/client/webconsole/jsterm", true);
|
||||
loader.lazyRequireGetter(this, "JSTerm", "devtools/client/webconsole/old/jsterm", true);
|
||||
loader.lazyRequireGetter(this, "gSequenceId", "devtools/client/webconsole/old/jsterm", true);
|
||||
loader.lazyImporter(this, "VariablesView", "resource://devtools/client/shared/widgets/VariablesView.jsm");
|
||||
loader.lazyImporter(this, "VariablesViewController", "resource://devtools/client/shared/widgets/VariablesViewController.jsm");
|
||||
loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
|
||||
|
@ -104,7 +104,6 @@ async function testObjectInspectorPropertiesAreSet(objInspector) {
|
||||
}
|
||||
|
||||
function testJSTermIsNotVisible(hud) {
|
||||
let inputContainer = hud.ui.window.document
|
||||
.querySelector(".jsterm-input-container");
|
||||
is(inputContainer.style.display, "none", "input is not visible");
|
||||
let inputContainer = hud.ui.window.document.querySelector(".jsterm-input-container");
|
||||
is(inputContainer, null, "input is not in dom");
|
||||
}
|
||||
|
@ -20,8 +20,8 @@ const { l10n } = require("devtools/client/webconsole/utils/messages");
|
||||
/**
|
||||
* Create a Menu instance for the webconsole.
|
||||
*
|
||||
* @param {Object} jsterm
|
||||
* The JSTerm instance used by the webconsole.
|
||||
* @param {Object} hud
|
||||
* The webConsoleFrame.
|
||||
* @param {Element} parentNode
|
||||
* The container of the new console frontend output wrapper.
|
||||
* @param {Object} options
|
||||
@ -36,7 +36,7 @@ const { l10n } = require("devtools/client/webconsole/utils/messages");
|
||||
* inspector sidebar
|
||||
* - {String} rootActorId (optional) actor id for the root object being clicked on
|
||||
*/
|
||||
function createContextMenu(jsterm, parentNode, {
|
||||
function createContextMenu(hud, parentNode, {
|
||||
actor,
|
||||
clipboardText,
|
||||
variableText,
|
||||
@ -114,9 +114,9 @@ function createContextMenu(jsterm, parentNode, {
|
||||
selectedObjectActor: actor,
|
||||
};
|
||||
|
||||
jsterm.requestEvaluation(evalString, options).then((res) => {
|
||||
jsterm.focus();
|
||||
jsterm.setInputValue(res.result);
|
||||
hud.jsterm.requestEvaluation(evalString, options).then((res) => {
|
||||
hud.jsterm.focus();
|
||||
hud.jsterm.setInputValue(res.result);
|
||||
});
|
||||
},
|
||||
}));
|
||||
@ -149,7 +149,7 @@ function createContextMenu(jsterm, parentNode, {
|
||||
click: () => {
|
||||
if (actor) {
|
||||
// The Debugger.Object of the OA will be bound to |_self| during evaluation,
|
||||
jsterm.copyObject(`_self`, { selectedObjectActor: actor }).then((res) => {
|
||||
hud.jsterm.copyObject(`_self`, { selectedObjectActor: actor }).then((res) => {
|
||||
clipboardHelper.copyString(res.helperResult.value);
|
||||
});
|
||||
} else {
|
||||
|
@ -21,18 +21,6 @@
|
||||
<body class="theme-sidebar" role="application">
|
||||
<div id="app-wrapper" class="theme-body">
|
||||
<div id="output-container" role="document" aria-live="polite"></div>
|
||||
<div id="jsterm-wrapper">
|
||||
<div id="webconsole-notificationbox"></div>
|
||||
<div class="jsterm-input-container" style="direction:ltr">
|
||||
<div class="jsterm-stack-node">
|
||||
<textarea class="jsterm-complete-node devtools-monospace"
|
||||
tabindex="-1"></textarea>
|
||||
<textarea class="jsterm-input-node devtools-monospace"
|
||||
rows="1" tabindex="0"
|
||||
aria-autocomplete="list"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -77,7 +77,6 @@ webpackConfig.resolve = {
|
||||
alias: {
|
||||
"Services": "devtools-modules/src/Services",
|
||||
|
||||
"devtools/client/webconsole/jsterm": path.join(__dirname, "../../client/shared/webpack/shims/jsterm-stub"),
|
||||
"devtools/client/webconsole/utils": path.join(__dirname, "test/fixtures/WebConsoleUtils"),
|
||||
|
||||
"devtools/client/shared/vendor/immutable": "immutable",
|
||||
|
@ -63,7 +63,7 @@
|
||||
#include "mozilla/dom/TouchEvent.h"
|
||||
#include "mozilla/dom/ShadowRoot.h"
|
||||
#include "mozilla/dom/XULCommandEvent.h"
|
||||
#include "mozilla/dom/WorkerPrivate.h"
|
||||
#include "mozilla/dom/WorkerCommon.h"
|
||||
#include "mozilla/EventDispatcher.h"
|
||||
#include "mozilla/EventListenerManager.h"
|
||||
#include "mozilla/EventStateManager.h"
|
||||
@ -2379,11 +2379,10 @@ nsContentUtils::IsSystemCaller(JSContext* aCx)
|
||||
bool
|
||||
nsContentUtils::ThreadsafeIsSystemCaller(JSContext* aCx)
|
||||
{
|
||||
if (NS_IsMainThread()) {
|
||||
return IsSystemCaller(aCx);
|
||||
}
|
||||
CycleCollectedJSContext* ccjscx = CycleCollectedJSContext::Get();
|
||||
MOZ_ASSERT(ccjscx->Context() == aCx);
|
||||
|
||||
return GetWorkerPrivateFromContext(aCx)->UsesSystemPrincipal();
|
||||
return ccjscx->IsSystemCaller();
|
||||
}
|
||||
|
||||
// static
|
||||
@ -6067,7 +6066,6 @@ nsContentUtils::URIIsLocalFile(nsIURI *aURI)
|
||||
JSContext *
|
||||
nsContentUtils::GetCurrentJSContext()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(IsInitialized());
|
||||
if (!IsJSAPIActive()) {
|
||||
return nullptr;
|
||||
@ -6075,17 +6073,6 @@ nsContentUtils::GetCurrentJSContext()
|
||||
return danger::GetJSContext();
|
||||
}
|
||||
|
||||
/* static */
|
||||
JSContext *
|
||||
nsContentUtils::GetCurrentJSContextForThread()
|
||||
{
|
||||
MOZ_ASSERT(IsInitialized());
|
||||
if (MOZ_LIKELY(NS_IsMainThread())) {
|
||||
return GetCurrentJSContext();
|
||||
}
|
||||
return GetCurrentWorkerThreadJSContext();
|
||||
}
|
||||
|
||||
template<typename StringType, typename CharType>
|
||||
void
|
||||
_ASCIIToLowerInSitu(StringType& aStr)
|
||||
|
@ -229,7 +229,8 @@ public:
|
||||
// Check whether the caller is system if you know you're on the main thread.
|
||||
static bool IsSystemCaller(JSContext* aCx);
|
||||
|
||||
// Check whether the caller is system if you might be on a worker thread.
|
||||
// Check whether the caller is system if you might be on a worker or worklet
|
||||
// thread.
|
||||
static bool ThreadsafeIsSystemCaller(JSContext* aCx);
|
||||
|
||||
// In the traditional Gecko architecture, both C++ code and untrusted JS code
|
||||
@ -2047,7 +2048,6 @@ public:
|
||||
const nsAString &viewportInfo);
|
||||
|
||||
static JSContext *GetCurrentJSContext();
|
||||
static JSContext *GetCurrentJSContextForThread();
|
||||
|
||||
/**
|
||||
* Case insensitive comparison between two strings. However it only ignores
|
||||
|
@ -187,7 +187,7 @@ already_AddRefed<nsIStackFrame>
|
||||
GetCurrentJSStack(int32_t aMaxDepth)
|
||||
{
|
||||
// is there a current context available?
|
||||
JSContext* cx = nsContentUtils::GetCurrentJSContextForThread();
|
||||
JSContext* cx = nsContentUtils::GetCurrentJSContext();
|
||||
|
||||
if (!cx || !js::GetContextCompartment(cx)) {
|
||||
return nullptr;
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "mozilla/dom/WorkerRunnable.h"
|
||||
#include "mozilla/dom/WorkerScope.h"
|
||||
#include "mozilla/dom/WorkletGlobalScope.h"
|
||||
#include "mozilla/dom/WorkletThread.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsDocument.h"
|
||||
@ -297,22 +298,380 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
class ConsoleRunnable : public WorkerProxyToMainThreadRunnable
|
||||
, public StructuredCloneHolderBase
|
||||
// This base class must be extended for Worker and for Worklet.
|
||||
class ConsoleRunnable : public StructuredCloneHolderBase
|
||||
{
|
||||
public:
|
||||
explicit ConsoleRunnable(Console* aConsole)
|
||||
: WorkerProxyToMainThreadRunnable(GetCurrentThreadWorkerPrivate())
|
||||
, mConsole(aConsole)
|
||||
{}
|
||||
|
||||
virtual
|
||||
~ConsoleRunnable()
|
||||
~ConsoleRunnable() override
|
||||
{
|
||||
// Clear the StructuredCloneHolderBase class.
|
||||
Clear();
|
||||
}
|
||||
|
||||
protected:
|
||||
JSObject*
|
||||
CustomReadHandler(JSContext* aCx,
|
||||
JSStructuredCloneReader* aReader,
|
||||
uint32_t aTag,
|
||||
uint32_t aIndex) override
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
if (aTag == CONSOLE_TAG_BLOB) {
|
||||
MOZ_ASSERT(mClonedData.mBlobs.Length() > aIndex);
|
||||
|
||||
JS::Rooted<JS::Value> val(aCx);
|
||||
{
|
||||
RefPtr<Blob> blob =
|
||||
Blob::Create(mClonedData.mParent,
|
||||
mClonedData.mBlobs.ElementAt(aIndex));
|
||||
if (!ToJSValue(aCx, blob, &val)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return &val.toObject();
|
||||
}
|
||||
|
||||
MOZ_CRASH("No other tags are supported.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
CustomWriteHandler(JSContext* aCx,
|
||||
JSStructuredCloneWriter* aWriter,
|
||||
JS::Handle<JSObject*> aObj) override
|
||||
{
|
||||
RefPtr<Blob> blob;
|
||||
if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, aObj, blob)) &&
|
||||
blob->Impl()->MayBeClonedToOtherThreads()) {
|
||||
if (NS_WARN_IF(!JS_WriteUint32Pair(aWriter, CONSOLE_TAG_BLOB,
|
||||
mClonedData.mBlobs.Length()))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mClonedData.mBlobs.AppendElement(blob->Impl());
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!JS_ObjectNotWritten(aWriter, aObj)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> value(aCx, JS::ObjectOrNullValue(aObj));
|
||||
JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, value));
|
||||
if (NS_WARN_IF(!jsString)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!JS_WriteString(aWriter, jsString))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Helper methods for CallData
|
||||
bool
|
||||
StoreConsoleData(JSContext* aCx, ConsoleCallData* aCallData)
|
||||
{
|
||||
ConsoleCommon::ClearException ce(aCx);
|
||||
|
||||
JS::Rooted<JSObject*> arguments(aCx,
|
||||
JS_NewArrayObject(aCx, aCallData->mCopiedArguments.Length()));
|
||||
if (NS_WARN_IF(!arguments)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> arg(aCx);
|
||||
for (uint32_t i = 0; i < aCallData->mCopiedArguments.Length(); ++i) {
|
||||
arg = aCallData->mCopiedArguments[i];
|
||||
if (NS_WARN_IF(!JS_DefineElement(aCx, arguments, i, arg,
|
||||
JSPROP_ENUMERATE))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*arguments));
|
||||
|
||||
if (NS_WARN_IF(!Write(aCx, value))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ProcessCallData(JSContext* aCx, Console* aConsole, ConsoleCallData* aCallData)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
ConsoleCommon::ClearException ce(aCx);
|
||||
|
||||
JS::Rooted<JS::Value> argumentsValue(aCx);
|
||||
if (!Read(aCx, &argumentsValue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(argumentsValue.isObject());
|
||||
|
||||
JS::Rooted<JSObject*> argumentsObj(aCx, &argumentsValue.toObject());
|
||||
|
||||
uint32_t length;
|
||||
if (!JS_GetArrayLength(aCx, argumentsObj, &length)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Sequence<JS::Value> values;
|
||||
SequenceRooter<JS::Value> arguments(aCx, &values);
|
||||
|
||||
for (uint32_t i = 0; i < length; ++i) {
|
||||
JS::Rooted<JS::Value> value(aCx);
|
||||
|
||||
if (!JS_GetElement(aCx, argumentsObj, i, &value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!values.AppendElement(value, fallible)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(values.Length() == length);
|
||||
|
||||
aConsole->ProcessCallData(aCx, aCallData, values);
|
||||
}
|
||||
|
||||
void
|
||||
ReleaseCallData(Console* aConsole, ConsoleCallData* aCallData)
|
||||
{
|
||||
aConsole->AssertIsOnOwningThread();
|
||||
|
||||
if (aCallData->mStatus == ConsoleCallData::eToBeDeleted) {
|
||||
aConsole->ReleaseCallData(aCallData);
|
||||
} else {
|
||||
MOZ_ASSERT(aCallData->mStatus == ConsoleCallData::eInUse);
|
||||
aCallData->mStatus = ConsoleCallData::eUnused;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper methods for Profile calls
|
||||
bool
|
||||
StoreProfileData(JSContext* aCx, const Sequence<JS::Value>& aArguments)
|
||||
{
|
||||
ConsoleCommon::ClearException ce(aCx);
|
||||
|
||||
JS::Rooted<JSObject*> arguments(aCx,
|
||||
JS_NewArrayObject(aCx, aArguments.Length()));
|
||||
if (NS_WARN_IF(!arguments)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> arg(aCx);
|
||||
for (uint32_t i = 0; i < aArguments.Length(); ++i) {
|
||||
arg = aArguments[i];
|
||||
if (NS_WARN_IF(!JS_DefineElement(aCx, arguments, i, arg,
|
||||
JSPROP_ENUMERATE))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*arguments));
|
||||
|
||||
if (NS_WARN_IF(!Write(aCx, value))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ProcessProfileData(JSContext* aCx, Console* aConsole,
|
||||
Console::MethodName aMethodName, const nsAString& aAction)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
ConsoleCommon::ClearException ce(aCx);
|
||||
|
||||
JS::Rooted<JS::Value> argumentsValue(aCx);
|
||||
bool ok = Read(aCx, &argumentsValue);
|
||||
mClonedData.mParent = nullptr;
|
||||
|
||||
if (!ok) {
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(argumentsValue.isObject());
|
||||
JS::Rooted<JSObject*> argumentsObj(aCx, &argumentsValue.toObject());
|
||||
if (NS_WARN_IF(!argumentsObj)) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t length;
|
||||
if (!JS_GetArrayLength(aCx, argumentsObj, &length)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Sequence<JS::Value> arguments;
|
||||
|
||||
for (uint32_t i = 0; i < length; ++i) {
|
||||
JS::Rooted<JS::Value> value(aCx);
|
||||
|
||||
if (!JS_GetElement(aCx, argumentsObj, i, &value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!arguments.AppendElement(value, fallible)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
aConsole->ProfileMethodInternal(aCx, aMethodName, aAction, arguments);
|
||||
}
|
||||
|
||||
ConsoleStructuredCloneData mClonedData;
|
||||
};
|
||||
|
||||
class ConsoleWorkletRunnable : public Runnable
|
||||
, public ConsoleRunnable
|
||||
{
|
||||
protected:
|
||||
explicit ConsoleWorkletRunnable(Console* aConsole)
|
||||
: Runnable("dom::console::ConsoleWorkletRunnable")
|
||||
, mConsole(aConsole)
|
||||
, mWorkletThread(WorkletThread::Get())
|
||||
{
|
||||
WorkletThread::AssertIsOnWorkletThread();
|
||||
MOZ_ASSERT(mWorkletThread);
|
||||
}
|
||||
|
||||
~ConsoleWorkletRunnable() override = default;
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
// This runnable is dispatched to main-thread first, then it goes back to
|
||||
// worklet thread.
|
||||
if (NS_IsMainThread()) {
|
||||
RunOnMainThread();
|
||||
RefPtr<ConsoleWorkletRunnable> runnable(this);
|
||||
return mWorkletThread->DispatchRunnable(runnable.forget());
|
||||
}
|
||||
|
||||
WorkletThread::AssertIsOnWorkletThread();
|
||||
|
||||
ReleaseData();
|
||||
mConsole = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
protected:
|
||||
// This method is called in the main-thread.
|
||||
virtual void
|
||||
RunOnMainThread() = 0;
|
||||
|
||||
// This method is called in the owning thread of the Console object.
|
||||
virtual void
|
||||
ReleaseData() = 0;
|
||||
|
||||
// This must be released on the worker thread.
|
||||
RefPtr<Console> mConsole;
|
||||
|
||||
RefPtr<WorkletThread> mWorkletThread;
|
||||
};
|
||||
|
||||
// This runnable appends a CallData object into the Console queue running on
|
||||
// the main-thread.
|
||||
class ConsoleCallDataWorkletRunnable final : public ConsoleWorkletRunnable
|
||||
{
|
||||
public:
|
||||
static already_AddRefed<ConsoleCallDataWorkletRunnable>
|
||||
Create(Console* aConsole, ConsoleCallData* aConsoleData)
|
||||
{
|
||||
WorkletThread::AssertIsOnWorkletThread();
|
||||
|
||||
RefPtr<WorkletThread> workletThread = WorkletThread::Get();
|
||||
MOZ_ASSERT(workletThread);
|
||||
|
||||
aConsoleData->SetIDs(workletThread->GetWorkletLoadInfo().OuterWindowID(),
|
||||
workletThread->GetWorkletLoadInfo().InnerWindowID());
|
||||
|
||||
RefPtr<ConsoleCallDataWorkletRunnable> runnable =
|
||||
new ConsoleCallDataWorkletRunnable(aConsole, aConsoleData);
|
||||
|
||||
if (!runnable->StoreConsoleData(WorkletThread::Get()->GetJSContext(),
|
||||
aConsoleData)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return runnable.forget();
|
||||
}
|
||||
|
||||
private:
|
||||
ConsoleCallDataWorkletRunnable(Console* aConsole,
|
||||
ConsoleCallData* aCallData)
|
||||
: ConsoleWorkletRunnable(aConsole)
|
||||
, mCallData(aCallData)
|
||||
{
|
||||
WorkletThread::AssertIsOnWorkletThread();
|
||||
MOZ_ASSERT(aCallData);
|
||||
aCallData->AssertIsOnOwningThread();
|
||||
|
||||
// Marking this CallData as in use.
|
||||
mCallData->mStatus = ConsoleCallData::eInUse;
|
||||
}
|
||||
|
||||
~ConsoleCallDataWorkletRunnable() override
|
||||
{
|
||||
MOZ_ASSERT(!mCallData);
|
||||
}
|
||||
|
||||
void
|
||||
RunOnMainThread() override
|
||||
{
|
||||
AutoSafeJSContext cx;
|
||||
|
||||
JSObject* sandbox =
|
||||
mConsole->GetOrCreateSandbox(cx,
|
||||
mWorkletThread->GetWorkletLoadInfo().Principal());
|
||||
JS::Rooted<JSObject*> global(cx, sandbox);
|
||||
if (NS_WARN_IF(!global)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The CreateSandbox call returns a proxy to the actual sandbox object. We
|
||||
// don't need a proxy here.
|
||||
global = js::UncheckedUnwrap(global);
|
||||
|
||||
JSAutoCompartment ac(cx, global);
|
||||
|
||||
// We don't need to set a parent object in mCallData bacause there are not
|
||||
// DOM objects exposed to worklet.
|
||||
|
||||
ProcessCallData(cx, mConsole, mCallData);
|
||||
}
|
||||
|
||||
virtual void
|
||||
ReleaseData() override
|
||||
{
|
||||
ReleaseCallData(mConsole, mCallData);
|
||||
mCallData = nullptr;
|
||||
}
|
||||
|
||||
RefPtr<ConsoleCallData> mCallData;
|
||||
};
|
||||
|
||||
class ConsoleWorkerRunnable : public WorkerProxyToMainThreadRunnable
|
||||
, public ConsoleRunnable
|
||||
{
|
||||
public:
|
||||
explicit ConsoleWorkerRunnable(Console* aConsole)
|
||||
: WorkerProxyToMainThreadRunnable(GetCurrentThreadWorkerPrivate())
|
||||
, mConsole(aConsole)
|
||||
{}
|
||||
|
||||
~ConsoleWorkerRunnable() override = default;
|
||||
|
||||
bool
|
||||
Dispatch(JSContext* aCx)
|
||||
{
|
||||
@ -425,65 +784,6 @@ protected:
|
||||
virtual void
|
||||
ReleaseData() = 0;
|
||||
|
||||
virtual JSObject* CustomReadHandler(JSContext* aCx,
|
||||
JSStructuredCloneReader* aReader,
|
||||
uint32_t aTag,
|
||||
uint32_t aIndex) override
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
if (aTag == CONSOLE_TAG_BLOB) {
|
||||
MOZ_ASSERT(mClonedData.mBlobs.Length() > aIndex);
|
||||
|
||||
JS::Rooted<JS::Value> val(aCx);
|
||||
{
|
||||
RefPtr<Blob> blob =
|
||||
Blob::Create(mClonedData.mParent, mClonedData.mBlobs.ElementAt(aIndex));
|
||||
if (!ToJSValue(aCx, blob, &val)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return &val.toObject();
|
||||
}
|
||||
|
||||
MOZ_CRASH("No other tags are supported.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual bool CustomWriteHandler(JSContext* aCx,
|
||||
JSStructuredCloneWriter* aWriter,
|
||||
JS::Handle<JSObject*> aObj) override
|
||||
{
|
||||
RefPtr<Blob> blob;
|
||||
if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, aObj, blob)) &&
|
||||
blob->Impl()->MayBeClonedToOtherThreads()) {
|
||||
if (NS_WARN_IF(!JS_WriteUint32Pair(aWriter, CONSOLE_TAG_BLOB,
|
||||
mClonedData.mBlobs.Length()))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mClonedData.mBlobs.AppendElement(blob->Impl());
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!JS_ObjectNotWritten(aWriter, aObj)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> value(aCx, JS::ObjectOrNullValue(aObj));
|
||||
JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, value));
|
||||
if (NS_WARN_IF(!jsString)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!JS_WriteString(aWriter, jsString))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// This must be released on the worker thread.
|
||||
RefPtr<Console> mConsole;
|
||||
|
||||
@ -492,12 +792,12 @@ protected:
|
||||
|
||||
// This runnable appends a CallData object into the Console queue running on
|
||||
// the main-thread.
|
||||
class ConsoleCallDataRunnable final : public ConsoleRunnable
|
||||
class ConsoleCallDataWorkerRunnable final : public ConsoleWorkerRunnable
|
||||
{
|
||||
public:
|
||||
ConsoleCallDataRunnable(Console* aConsole,
|
||||
ConsoleCallData* aCallData)
|
||||
: ConsoleRunnable(aConsole)
|
||||
ConsoleCallDataWorkerRunnable(Console* aConsole,
|
||||
ConsoleCallData* aCallData)
|
||||
: ConsoleWorkerRunnable(aConsole)
|
||||
, mCallData(aCallData)
|
||||
{
|
||||
MOZ_ASSERT(aCallData);
|
||||
@ -509,7 +809,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
~ConsoleCallDataRunnable()
|
||||
~ConsoleCallDataWorkerRunnable() override
|
||||
{
|
||||
MOZ_ASSERT(!mCallData);
|
||||
}
|
||||
@ -517,33 +817,7 @@ private:
|
||||
bool
|
||||
PreDispatch(JSContext* aCx) override
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
mCallData->AssertIsOnOwningThread();
|
||||
|
||||
ConsoleCommon::ClearException ce(aCx);
|
||||
|
||||
JS::Rooted<JSObject*> arguments(aCx,
|
||||
JS_NewArrayObject(aCx, mCallData->mCopiedArguments.Length()));
|
||||
if (NS_WARN_IF(!arguments)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> arg(aCx);
|
||||
for (uint32_t i = 0; i < mCallData->mCopiedArguments.Length(); ++i) {
|
||||
arg = mCallData->mCopiedArguments[i];
|
||||
if (NS_WARN_IF(!JS_DefineElement(aCx, arguments, i, arg,
|
||||
JSPROP_ENUMERATE))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*arguments));
|
||||
|
||||
if (NS_WARN_IF(!Write(aCx, value))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return StoreConsoleData(aCx, mCallData);
|
||||
}
|
||||
|
||||
void
|
||||
@ -582,7 +856,7 @@ private:
|
||||
// Now we could have the correct window (if we are not window-less).
|
||||
mClonedData.mParent = aInnerWindow;
|
||||
|
||||
ProcessCallData(aCx);
|
||||
ProcessCallData(aCx, mConsole, mCallData);
|
||||
|
||||
mClonedData.mParent = nullptr;
|
||||
}
|
||||
@ -590,70 +864,87 @@ private:
|
||||
virtual void
|
||||
ReleaseData() override
|
||||
{
|
||||
mConsole->AssertIsOnOwningThread();
|
||||
|
||||
if (mCallData->mStatus == ConsoleCallData::eToBeDeleted) {
|
||||
mConsole->ReleaseCallData(mCallData);
|
||||
} else {
|
||||
MOZ_ASSERT(mCallData->mStatus == ConsoleCallData::eInUse);
|
||||
mCallData->mStatus = ConsoleCallData::eUnused;
|
||||
}
|
||||
|
||||
ReleaseCallData(mConsole, mCallData);
|
||||
mCallData = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
ProcessCallData(JSContext* aCx)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
ConsoleCommon::ClearException ce(aCx);
|
||||
|
||||
JS::Rooted<JS::Value> argumentsValue(aCx);
|
||||
if (!Read(aCx, &argumentsValue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(argumentsValue.isObject());
|
||||
|
||||
JS::Rooted<JSObject*> argumentsObj(aCx, &argumentsValue.toObject());
|
||||
|
||||
uint32_t length;
|
||||
if (!JS_GetArrayLength(aCx, argumentsObj, &length)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Sequence<JS::Value> values;
|
||||
SequenceRooter<JS::Value> arguments(aCx, &values);
|
||||
|
||||
for (uint32_t i = 0; i < length; ++i) {
|
||||
JS::Rooted<JS::Value> value(aCx);
|
||||
|
||||
if (!JS_GetElement(aCx, argumentsObj, i, &value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!values.AppendElement(value, fallible)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(values.Length() == length);
|
||||
|
||||
mConsole->ProcessCallData(aCx, mCallData, values);
|
||||
}
|
||||
|
||||
RefPtr<ConsoleCallData> mCallData;
|
||||
};
|
||||
|
||||
// This runnable calls ProfileMethod() on the console on the main-thread.
|
||||
class ConsoleProfileRunnable final : public ConsoleRunnable
|
||||
class ConsoleProfileWorkletRunnable final : public ConsoleWorkletRunnable
|
||||
{
|
||||
public:
|
||||
ConsoleProfileRunnable(Console* aConsole, Console::MethodName aName,
|
||||
const nsAString& aAction,
|
||||
const Sequence<JS::Value>& aArguments)
|
||||
: ConsoleRunnable(aConsole)
|
||||
static already_AddRefed<ConsoleProfileWorkletRunnable>
|
||||
Create(Console* aConsole, Console::MethodName aName, const nsAString& aAction,
|
||||
const Sequence<JS::Value>& aArguments)
|
||||
{
|
||||
WorkletThread::AssertIsOnWorkletThread();
|
||||
|
||||
RefPtr<ConsoleProfileWorkletRunnable> runnable =
|
||||
new ConsoleProfileWorkletRunnable(aConsole, aName, aAction);
|
||||
|
||||
if (!runnable->StoreProfileData(WorkletThread::Get()->GetJSContext(),
|
||||
aArguments)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return runnable.forget();
|
||||
}
|
||||
|
||||
private:
|
||||
ConsoleProfileWorkletRunnable(Console* aConsole, Console::MethodName aName,
|
||||
const nsAString& aAction)
|
||||
: ConsoleWorkletRunnable(aConsole)
|
||||
, mName(aName)
|
||||
, mAction(aAction)
|
||||
{
|
||||
MOZ_ASSERT(aConsole);
|
||||
}
|
||||
|
||||
void
|
||||
RunOnMainThread() override
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
AutoSafeJSContext cx;
|
||||
|
||||
JSObject* sandbox =
|
||||
mConsole->GetOrCreateSandbox(cx,
|
||||
mWorkletThread->GetWorkletLoadInfo().Principal());
|
||||
JS::Rooted<JSObject*> global(cx, sandbox);
|
||||
if (NS_WARN_IF(!global)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The CreateSandbox call returns a proxy to the actual sandbox object. We
|
||||
// don't need a proxy here.
|
||||
global = js::UncheckedUnwrap(global);
|
||||
|
||||
JSAutoCompartment ac(cx, global);
|
||||
|
||||
// We don't need to set a parent object in mCallData bacause there are not
|
||||
// DOM objects exposed to worklet.
|
||||
|
||||
ProcessProfileData(cx, mConsole, mName, mAction);
|
||||
}
|
||||
|
||||
virtual void
|
||||
ReleaseData() override
|
||||
{}
|
||||
|
||||
Console::MethodName mName;
|
||||
nsString mAction;
|
||||
};
|
||||
|
||||
// This runnable calls ProfileMethod() on the console on the main-thread.
|
||||
class ConsoleProfileWorkerRunnable final : public ConsoleWorkerRunnable
|
||||
{
|
||||
public:
|
||||
ConsoleProfileWorkerRunnable(Console* aConsole, Console::MethodName aName,
|
||||
const nsAString& aAction,
|
||||
const Sequence<JS::Value>& aArguments)
|
||||
: ConsoleWorkerRunnable(aConsole)
|
||||
, mName(aName)
|
||||
, mAction(aAction)
|
||||
, mArguments(aArguments)
|
||||
@ -665,30 +956,7 @@ private:
|
||||
bool
|
||||
PreDispatch(JSContext* aCx) override
|
||||
{
|
||||
ConsoleCommon::ClearException ce(aCx);
|
||||
|
||||
JS::Rooted<JSObject*> arguments(aCx,
|
||||
JS_NewArrayObject(aCx, mArguments.Length()));
|
||||
if (NS_WARN_IF(!arguments)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> arg(aCx);
|
||||
for (uint32_t i = 0; i < mArguments.Length(); ++i) {
|
||||
arg = mArguments[i];
|
||||
if (NS_WARN_IF(!JS_DefineElement(aCx, arguments, i, arg,
|
||||
JSPROP_ENUMERATE))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*arguments));
|
||||
|
||||
if (NS_WARN_IF(!Write(aCx, value))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return StoreProfileData(aCx, mArguments);
|
||||
}
|
||||
|
||||
void
|
||||
@ -697,45 +965,12 @@ private:
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
ConsoleCommon::ClearException ce(aCx);
|
||||
|
||||
// Now we could have the correct window (if we are not window-less).
|
||||
mClonedData.mParent = aInnerWindow;
|
||||
|
||||
JS::Rooted<JS::Value> argumentsValue(aCx);
|
||||
bool ok = Read(aCx, &argumentsValue);
|
||||
ProcessProfileData(aCx, mConsole, mName, mAction);
|
||||
|
||||
mClonedData.mParent = nullptr;
|
||||
|
||||
if (!ok) {
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(argumentsValue.isObject());
|
||||
JS::Rooted<JSObject*> argumentsObj(aCx, &argumentsValue.toObject());
|
||||
if (NS_WARN_IF(!argumentsObj)) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t length;
|
||||
if (!JS_GetArrayLength(aCx, argumentsObj, &length)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Sequence<JS::Value> arguments;
|
||||
|
||||
for (uint32_t i = 0; i < length; ++i) {
|
||||
JS::Rooted<JS::Value> value(aCx);
|
||||
|
||||
if (!JS_GetElement(aCx, argumentsObj, i, &value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!arguments.AppendElement(value, fallible)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mConsole->ProfileMethodInternal(aCx, mName, mAction, arguments);
|
||||
}
|
||||
|
||||
virtual void
|
||||
@ -795,7 +1030,22 @@ Console::Create(JSContext* aCx, nsPIDOMWindowInner* aWindow, ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT_IF(NS_IsMainThread(), aWindow);
|
||||
|
||||
RefPtr<Console> console = new Console(aCx, aWindow);
|
||||
uint64_t outerWindowID = 0;
|
||||
uint64_t innerWindowID = 0;
|
||||
|
||||
if (aWindow) {
|
||||
innerWindowID = aWindow->WindowID();
|
||||
|
||||
// Without outerwindow any console message coming from this object will not
|
||||
// shown in the devtools webconsole. But this should be fine because
|
||||
// probably we are shutting down, or the window is CCed/GCed.
|
||||
nsPIDOMWindowOuter* outerWindow = aWindow->GetOuterWindow();
|
||||
if (outerWindow) {
|
||||
outerWindowID = outerWindow->WindowID();
|
||||
}
|
||||
}
|
||||
|
||||
RefPtr<Console> console = new Console(aCx, aWindow, outerWindowID, innerWindowID);
|
||||
console->Initialize(aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
@ -804,28 +1054,33 @@ Console::Create(JSContext* aCx, nsPIDOMWindowInner* aWindow, ErrorResult& aRv)
|
||||
return console.forget();
|
||||
}
|
||||
|
||||
Console::Console(JSContext* aCx, nsPIDOMWindowInner* aWindow)
|
||||
/* static */ already_AddRefed<Console>
|
||||
Console::CreateForWorklet(JSContext* aCx, uint64_t aOuterWindowID,
|
||||
uint64_t aInnerWindowID, ErrorResult& aRv)
|
||||
{
|
||||
WorkletThread::AssertIsOnWorkletThread();
|
||||
|
||||
RefPtr<Console> console =
|
||||
new Console(aCx, nullptr, aOuterWindowID, aInnerWindowID);
|
||||
console->Initialize(aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return console.forget();
|
||||
}
|
||||
|
||||
Console::Console(JSContext* aCx, nsPIDOMWindowInner* aWindow,
|
||||
uint64_t aOuterWindowID, uint64_t aInnerWindowID)
|
||||
: mWindow(aWindow)
|
||||
, mOuterID(0)
|
||||
, mInnerID(0)
|
||||
, mOuterID(aOuterWindowID)
|
||||
, mInnerID(aInnerWindowID)
|
||||
, mDumpToStdout(false)
|
||||
, mChromeInstance(false)
|
||||
, mMaxLogLevel(ConsoleLogLevel::All)
|
||||
, mStatus(eUnknown)
|
||||
, mCreationTimeStamp(TimeStamp::Now())
|
||||
{
|
||||
if (mWindow) {
|
||||
mInnerID = mWindow->WindowID();
|
||||
|
||||
// Without outerwindow any console message coming from this object will not
|
||||
// shown in the devtools webconsole. But this should be fine because
|
||||
// probably we are shutting down, or the window is CCed/GCed.
|
||||
nsPIDOMWindowOuter* outerWindow = mWindow->GetOuterWindow();
|
||||
if (outerWindow) {
|
||||
mOuterID = outerWindow->WindowID();
|
||||
}
|
||||
}
|
||||
|
||||
// Let's enable the dumping to stdout by default for chrome.
|
||||
if (nsContentUtils::ThreadsafeIsSystemCaller(aCx)) {
|
||||
mDumpToStdout = DOMPrefs::DumpEnabled();
|
||||
@ -1093,10 +1348,22 @@ Console::ProfileMethodInternal(JSContext* aCx, MethodName aMethodName,
|
||||
|
||||
MaybeExecuteDumpFunction(aCx, aAction, aData, nullptr);
|
||||
|
||||
if (WorkletThread::IsOnWorkletThread()) {
|
||||
RefPtr<ConsoleProfileWorkletRunnable> runnable =
|
||||
ConsoleProfileWorkletRunnable::Create(this, aMethodName, aAction, aData);
|
||||
if (!runnable) {
|
||||
return;
|
||||
}
|
||||
|
||||
WorkletThread::Get()->DispatchRunnable(runnable.forget());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!NS_IsMainThread()) {
|
||||
// Here we are in a worker thread.
|
||||
RefPtr<ConsoleProfileRunnable> runnable =
|
||||
new ConsoleProfileRunnable(this, aMethodName, aAction, aData);
|
||||
RefPtr<ConsoleProfileWorkerRunnable> runnable =
|
||||
new ConsoleProfileWorkerRunnable(this, aMethodName, aAction, aData);
|
||||
|
||||
|
||||
runnable->Dispatch(aCx);
|
||||
return;
|
||||
@ -1275,6 +1542,8 @@ Console::MethodInternal(JSContext* aCx, MethodName aMethodName,
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} else if (WorkletThread::IsOnWorkletThread()) {
|
||||
oa = WorkletThread::Get()->GetWorkletLoadInfo().OriginAttributesRef();
|
||||
} else {
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
@ -1369,11 +1638,22 @@ Console::MethodInternal(JSContext* aCx, MethodName aMethodName,
|
||||
return;
|
||||
}
|
||||
|
||||
if (WorkletThread::IsOnWorkletThread()) {
|
||||
RefPtr<ConsoleCallDataWorkletRunnable> runnable =
|
||||
ConsoleCallDataWorkletRunnable::Create(this, callData);
|
||||
if (!runnable) {
|
||||
return;
|
||||
}
|
||||
|
||||
NS_DispatchToMainThread(runnable);
|
||||
return;
|
||||
}
|
||||
|
||||
// We do this only in workers for now.
|
||||
NotifyHandler(aCx, aData, callData);
|
||||
|
||||
RefPtr<ConsoleCallDataRunnable> runnable =
|
||||
new ConsoleCallDataRunnable(this, callData);
|
||||
RefPtr<ConsoleCallDataWorkerRunnable> runnable =
|
||||
new ConsoleCallDataWorkerRunnable(this, callData);
|
||||
Unused << NS_WARN_IF(!runnable->Dispatch(aCx));
|
||||
}
|
||||
|
||||
@ -2267,7 +2547,8 @@ Console::StoreCallData(ConsoleCallData* aCallData)
|
||||
MOZ_ASSERT(callData->mStatus != ConsoleCallData::eToBeDeleted);
|
||||
|
||||
// We cannot delete this object now because we have to trace its JSValues
|
||||
// until the pending operation (ConsoleCallDataRunnable) is completed.
|
||||
// until the pending operation (ConsoleCallDataWorkerRunnable or
|
||||
// ConsoleCallDataWorkletRunnable) is completed.
|
||||
if (callData->mStatus == ConsoleCallData::eInUse) {
|
||||
callData->mStatus = ConsoleCallData::eToBeDeleted;
|
||||
mCallDataStoragePending.AppendElement(callData);
|
||||
@ -2421,15 +2702,6 @@ Console::GetConsole(const GlobalObject& aGlobal)
|
||||
/* static */ already_AddRefed<Console>
|
||||
Console::GetConsoleInternal(const GlobalObject& aGlobal, ErrorResult& aRv)
|
||||
{
|
||||
// Worklet
|
||||
if (NS_IsMainThread()) {
|
||||
nsCOMPtr<WorkletGlobalScope> workletScope =
|
||||
do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (workletScope) {
|
||||
return workletScope->GetConsole(aGlobal.Context(), aRv);
|
||||
}
|
||||
}
|
||||
|
||||
// Window
|
||||
if (NS_IsMainThread()) {
|
||||
nsCOMPtr<nsPIDOMWindowInner> innerWindow =
|
||||
@ -2437,7 +2709,7 @@ Console::GetConsoleInternal(const GlobalObject& aGlobal, ErrorResult& aRv)
|
||||
|
||||
// we are probably running a chrome script.
|
||||
if (!innerWindow) {
|
||||
RefPtr<Console> console = new Console(aGlobal.Context(), nullptr);
|
||||
RefPtr<Console> console = new Console(aGlobal.Context(), nullptr, 0, 0);
|
||||
console->Initialize(aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
@ -2450,6 +2722,14 @@ Console::GetConsoleInternal(const GlobalObject& aGlobal, ErrorResult& aRv)
|
||||
return window->GetConsole(aGlobal.Context(), aRv);
|
||||
}
|
||||
|
||||
// Worklet
|
||||
nsCOMPtr<WorkletGlobalScope> workletScope =
|
||||
do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (workletScope) {
|
||||
WorkletThread::AssertIsOnWorkletThread();
|
||||
return workletScope->GetConsole(aGlobal.Context(), aRv);
|
||||
}
|
||||
|
||||
// Workers
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
@ -2548,6 +2828,11 @@ Console::MonotonicTimer(JSContext* aCx, MethodName aMethodName,
|
||||
return true;
|
||||
}
|
||||
|
||||
if (WorkletThread::IsOnWorkletThread()) {
|
||||
*aTimeStamp = WorkletThread::Get()->TimeStampToDOMHighRes(TimeStamp::Now());
|
||||
return true;
|
||||
}
|
||||
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
|
||||
|
@ -44,6 +44,10 @@ public:
|
||||
static already_AddRefed<Console>
|
||||
Create(JSContext* aCx, nsPIDOMWindowInner* aWindow, ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<Console>
|
||||
CreateForWorklet(JSContext* aCx, uint64_t aOuterWindowID,
|
||||
uint64_t aInnerWindowID, ErrorResult& aRv);
|
||||
|
||||
// WebIDL methods
|
||||
nsPIDOMWindowInner* GetParentObject() const
|
||||
{
|
||||
@ -129,7 +133,8 @@ public:
|
||||
SetConsoleEventHandler(AnyCallback* aHandler);
|
||||
|
||||
private:
|
||||
Console(JSContext* aCx, nsPIDOMWindowInner* aWindow);
|
||||
Console(JSContext* aCx, nsPIDOMWindowInner* aWindow,
|
||||
uint64_t aOuterWindowID, uint64_t aInnerWIndowID);
|
||||
~Console();
|
||||
|
||||
void
|
||||
@ -463,10 +468,12 @@ private:
|
||||
mozilla::TimeStamp mCreationTimeStamp;
|
||||
|
||||
friend class ConsoleCallData;
|
||||
friend class ConsoleCallDataWorkletRunnable;
|
||||
friend class ConsoleInstance;
|
||||
friend class ConsoleProfileWorkerRunnable;
|
||||
friend class ConsoleProfileWorkletRunnable;
|
||||
friend class ConsoleRunnable;
|
||||
friend class ConsoleCallDataRunnable;
|
||||
friend class ConsoleProfileRunnable;
|
||||
friend class ConsoleWorkerRunnable;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -80,7 +80,7 @@ WebIDLevelToConsoleUtilsLevel(ConsoleLevel aLevel)
|
||||
|
||||
ConsoleInstance::ConsoleInstance(JSContext* aCx,
|
||||
const ConsoleInstanceOptions& aOptions)
|
||||
: mConsole(new Console(aCx, nullptr))
|
||||
: mConsole(new Console(aCx, nullptr, 0, 0))
|
||||
{
|
||||
mConsole->mConsoleID = aOptions.mConsoleID;
|
||||
mConsole->mPassedInnerID = aOptions.mInnerID;
|
||||
|
@ -50,6 +50,10 @@ using nscolor from "nsColor.h";
|
||||
using class mozilla::WidgetCompositionEvent from "ipc/nsGUIEventIPC.h";
|
||||
using struct mozilla::widget::IMENotification from "mozilla/widget/IMEData.h";
|
||||
using struct mozilla::widget::IMENotificationRequests from "mozilla/widget/IMEData.h";
|
||||
using mozilla::widget::IMEState::Enabled from "mozilla/widget/IMEData.h";
|
||||
using mozilla::widget::IMEState::Open from "mozilla/widget/IMEData.h";
|
||||
using mozilla::widget::InputContextAction::Cause from "mozilla/widget/IMEData.h";
|
||||
using mozilla::widget::InputContextAction::FocusChange from "mozilla/widget/IMEData.h";
|
||||
using mozilla::gfx::IntSize from "mozilla/gfx/Point.h";
|
||||
using mozilla::gfx::IntPoint from "mozilla/gfx/Point.h";
|
||||
using mozilla::gfx::IntRect from "mozilla/gfx/Rect.h";
|
||||
@ -345,17 +349,17 @@ parent:
|
||||
nsCString[] enabledCommands,
|
||||
nsCString[] disabledCommands);
|
||||
|
||||
nested(inside_cpow) sync GetInputContext() returns (int32_t IMEEnabled,
|
||||
int32_t IMEOpen);
|
||||
nested(inside_cpow) sync GetInputContext() returns (Enabled IMEEnabled,
|
||||
Open IMEOpen);
|
||||
|
||||
nested(inside_cpow) async SetInputContext(int32_t IMEEnabled,
|
||||
int32_t IMEOpen,
|
||||
nested(inside_cpow) async SetInputContext(Enabled IMEEnabled,
|
||||
Open IMEOpen,
|
||||
nsString type,
|
||||
nsString inputmode,
|
||||
nsString actionHint,
|
||||
bool inPrivateBrowsing,
|
||||
int32_t cause,
|
||||
int32_t focusChange);
|
||||
Cause cause,
|
||||
FocusChange focusChange);
|
||||
|
||||
sync IsParentWindowMainWidgetVisible() returns (bool visible);
|
||||
|
||||
|
@ -2443,8 +2443,8 @@ TabParent::RecvDefaultProcOfPluginEvent(const WidgetPluginEvent& aEvent)
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
TabParent::RecvGetInputContext(int32_t* aIMEEnabled,
|
||||
int32_t* aIMEOpen)
|
||||
TabParent::RecvGetInputContext(IMEState::Enabled* aIMEEnabled,
|
||||
IMEState::Open* aIMEOpen)
|
||||
{
|
||||
nsCOMPtr<nsIWidget> widget = GetWidget();
|
||||
if (!widget) {
|
||||
@ -2454,33 +2454,32 @@ TabParent::RecvGetInputContext(int32_t* aIMEEnabled,
|
||||
}
|
||||
|
||||
InputContext context = widget->GetInputContext();
|
||||
*aIMEEnabled = static_cast<int32_t>(context.mIMEState.mEnabled);
|
||||
*aIMEOpen = static_cast<int32_t>(context.mIMEState.mOpen);
|
||||
*aIMEEnabled = context.mIMEState.mEnabled;
|
||||
*aIMEOpen = context.mIMEState.mOpen;
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult
|
||||
TabParent::RecvSetInputContext(const int32_t& aIMEEnabled,
|
||||
const int32_t& aIMEOpen,
|
||||
const nsString& aType,
|
||||
const nsString& aInputmode,
|
||||
const nsString& aActionHint,
|
||||
const bool& aInPrivateBrowsing,
|
||||
const int32_t& aCause,
|
||||
const int32_t& aFocusChange)
|
||||
TabParent::RecvSetInputContext(
|
||||
const IMEState::Enabled& aIMEEnabled,
|
||||
const IMEState::Open& aIMEOpen,
|
||||
const nsString& aType,
|
||||
const nsString& aInputmode,
|
||||
const nsString& aActionHint,
|
||||
const bool& aInPrivateBrowsing,
|
||||
const InputContextAction::Cause& aCause,
|
||||
const InputContextAction::FocusChange& aFocusChange)
|
||||
{
|
||||
InputContext context;
|
||||
context.mIMEState.mEnabled = static_cast<IMEState::Enabled>(aIMEEnabled);
|
||||
context.mIMEState.mOpen = static_cast<IMEState::Open>(aIMEOpen);
|
||||
context.mIMEState.mEnabled = aIMEEnabled;
|
||||
context.mIMEState.mOpen = aIMEOpen;
|
||||
context.mHTMLInputType.Assign(aType);
|
||||
context.mHTMLInputInputmode.Assign(aInputmode);
|
||||
context.mActionHint.Assign(aActionHint);
|
||||
context.mOrigin = InputContext::ORIGIN_CONTENT;
|
||||
context.mInPrivateBrowsing = aInPrivateBrowsing;
|
||||
|
||||
InputContextAction action(
|
||||
static_cast<InputContextAction::Cause>(aCause),
|
||||
static_cast<InputContextAction::FocusChange>(aFocusChange));
|
||||
InputContextAction action(aCause, aFocusChange);
|
||||
|
||||
IMEStateManager::SetInputContextForChildProcess(this, context, action);
|
||||
|
||||
|
@ -238,18 +238,19 @@ public:
|
||||
virtual mozilla::ipc::IPCResult
|
||||
RecvDefaultProcOfPluginEvent(const WidgetPluginEvent& aEvent) override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult RecvGetInputContext(int32_t* aIMEEnabled,
|
||||
int32_t* aIMEOpen) override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult RecvSetInputContext(const int32_t& aIMEEnabled,
|
||||
const int32_t& aIMEOpen,
|
||||
const nsString& aType,
|
||||
const nsString& aInputmode,
|
||||
const nsString& aActionHint,
|
||||
const bool& aInPrivateBrowsing,
|
||||
const int32_t& aCause,
|
||||
const int32_t& aFocusChange) override;
|
||||
virtual mozilla::ipc::IPCResult RecvGetInputContext(
|
||||
widget::IMEState::Enabled* aIMEEnabled,
|
||||
widget::IMEState::Open* aIMEOpen) override;
|
||||
|
||||
virtual mozilla::ipc::IPCResult RecvSetInputContext(
|
||||
const widget::IMEState::Enabled& aIMEEnabled,
|
||||
const widget::IMEState::Open& aIMEOpen,
|
||||
const nsString& aType,
|
||||
const nsString& aInputmode,
|
||||
const nsString& aActionHint,
|
||||
const bool& aInPrivateBrowsing,
|
||||
const widget::InputContextAction::Cause& aCause,
|
||||
const widget::InputContextAction::FocusChange& aFocusChange) override;
|
||||
|
||||
// See nsIKeyEventInPluginCallback
|
||||
virtual void HandledWindowedPluginKeyEvent(
|
||||
|
@ -1300,7 +1300,7 @@ public:
|
||||
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(weakWindow);
|
||||
auto error = MakeRefPtr<MediaStreamError>(
|
||||
window,
|
||||
NS_LITERAL_STRING("OverConstrainedError"),
|
||||
MediaMgrError::Name::OverconstrainedError,
|
||||
NS_LITERAL_STRING(""),
|
||||
aBadConstraint.valueOr(nsString()));
|
||||
p->Reject(error);
|
||||
@ -1402,9 +1402,9 @@ public:
|
||||
|
||||
if (auto* window = nsGlobalWindowInner::GetInnerWindowWithId(mWindowID)) {
|
||||
RefPtr<MediaStreamError> error = new MediaStreamError(window->AsInner(),
|
||||
NS_LITERAL_STRING("InternalError"),
|
||||
MediaStreamError::Name::AbortError,
|
||||
sHasShutdown ? NS_LITERAL_STRING("In shutdown") :
|
||||
NS_LITERAL_STRING("No stream."));
|
||||
NS_LITERAL_STRING("No stream."));
|
||||
mOnFailure->OnError(error);
|
||||
}
|
||||
return NS_OK;
|
||||
@ -1649,7 +1649,7 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
Fail(const nsAString& aName,
|
||||
Fail(MediaMgrError::Name aName,
|
||||
const nsAString& aMessage = EmptyString(),
|
||||
const nsAString& aConstraint = EmptyString()) {
|
||||
RefPtr<MediaMgrError> error = new MediaMgrError(aName, aMessage, aConstraint);
|
||||
@ -1715,11 +1715,11 @@ public:
|
||||
if (errorMsg) {
|
||||
LOG(("%s %" PRIu32, errorMsg, static_cast<uint32_t>(rv)));
|
||||
if (badConstraint) {
|
||||
Fail(NS_LITERAL_STRING("OverconstrainedError"),
|
||||
Fail(MediaMgrError::Name::OverconstrainedError,
|
||||
NS_LITERAL_STRING(""),
|
||||
NS_ConvertUTF8toUTF16(badConstraint));
|
||||
} else {
|
||||
Fail(NS_LITERAL_STRING("NotReadableError"),
|
||||
Fail(MediaMgrError::Name::NotReadableError,
|
||||
NS_ConvertUTF8toUTF16(errorMsg));
|
||||
}
|
||||
NS_DispatchToMainThread(NS_NewRunnableFunction("MediaManager::SendPendingGUMRequest",
|
||||
@ -1747,7 +1747,7 @@ public:
|
||||
}
|
||||
|
||||
nsresult
|
||||
Denied(const nsAString& aName,
|
||||
Denied(MediaMgrError::Name aName,
|
||||
const nsAString& aMessage = EmptyString())
|
||||
{
|
||||
MOZ_ASSERT(mOnSuccess);
|
||||
@ -2445,7 +2445,7 @@ MediaManager::GetUserMedia(nsPIDOMWindowInner* aWindow,
|
||||
if (!IsOn(c.mVideo) && !IsOn(c.mAudio)) {
|
||||
RefPtr<MediaStreamError> error =
|
||||
new MediaStreamError(aWindow,
|
||||
NS_LITERAL_STRING("TypeError"),
|
||||
MediaStreamError::Name::TypeError,
|
||||
NS_LITERAL_STRING("audio and/or video is required"));
|
||||
onFailure->OnError(error);
|
||||
return NS_OK;
|
||||
@ -2453,7 +2453,7 @@ MediaManager::GetUserMedia(nsPIDOMWindowInner* aWindow,
|
||||
|
||||
if (!IsFullyActive(aWindow)) {
|
||||
RefPtr<MediaStreamError> error =
|
||||
new MediaStreamError(aWindow, NS_LITERAL_STRING("InvalidStateError"));
|
||||
new MediaStreamError(aWindow, MediaStreamError::Name::InvalidStateError);
|
||||
onFailure->OnError(error);
|
||||
return NS_OK;
|
||||
}
|
||||
@ -2461,7 +2461,7 @@ MediaManager::GetUserMedia(nsPIDOMWindowInner* aWindow,
|
||||
if (sHasShutdown) {
|
||||
RefPtr<MediaStreamError> error =
|
||||
new MediaStreamError(aWindow,
|
||||
NS_LITERAL_STRING("AbortError"),
|
||||
MediaStreamError::Name::AbortError,
|
||||
NS_LITERAL_STRING("In shutdown"));
|
||||
onFailure->OnError(error);
|
||||
return NS_OK;
|
||||
@ -2575,7 +2575,7 @@ MediaManager::GetUserMedia(nsPIDOMWindowInner* aWindow,
|
||||
(!privileged && !aWindow->IsSecureContext())) {
|
||||
RefPtr<MediaStreamError> error =
|
||||
new MediaStreamError(aWindow,
|
||||
NS_LITERAL_STRING("NotAllowedError"));
|
||||
MediaStreamError::Name::NotAllowedError);
|
||||
onFailure->OnError(error);
|
||||
return NS_OK;
|
||||
}
|
||||
@ -2586,7 +2586,7 @@ MediaManager::GetUserMedia(nsPIDOMWindowInner* aWindow,
|
||||
default: {
|
||||
RefPtr<MediaStreamError> error =
|
||||
new MediaStreamError(aWindow,
|
||||
NS_LITERAL_STRING("OverconstrainedError"),
|
||||
MediaStreamError::Name::OverconstrainedError,
|
||||
NS_LITERAL_STRING(""),
|
||||
NS_LITERAL_STRING("mediaSource"));
|
||||
onFailure->OnError(error);
|
||||
@ -2655,7 +2655,7 @@ MediaManager::GetUserMedia(nsPIDOMWindowInner* aWindow,
|
||||
if (!Preferences::GetBool("media.getusermedia.audiocapture.enabled")) {
|
||||
RefPtr<MediaStreamError> error =
|
||||
new MediaStreamError(aWindow,
|
||||
NS_LITERAL_STRING("NotAllowedError"));
|
||||
MediaStreamError::Name::NotAllowedError);
|
||||
onFailure->OnError(error);
|
||||
return NS_OK;
|
||||
}
|
||||
@ -2665,7 +2665,7 @@ MediaManager::GetUserMedia(nsPIDOMWindowInner* aWindow,
|
||||
default: {
|
||||
RefPtr<MediaStreamError> error =
|
||||
new MediaStreamError(aWindow,
|
||||
NS_LITERAL_STRING("OverconstrainedError"),
|
||||
MediaStreamError::Name::OverconstrainedError,
|
||||
NS_LITERAL_STRING(""),
|
||||
NS_LITERAL_STRING("mediaSource"));
|
||||
onFailure->OnError(error);
|
||||
@ -2738,7 +2738,7 @@ MediaManager::GetUserMedia(nsPIDOMWindowInner* aWindow,
|
||||
(IsOn(c.mAudio) && audioPerm == nsIPermissionManager::DENY_ACTION) ||
|
||||
(IsOn(c.mVideo) && videoPerm == nsIPermissionManager::DENY_ACTION)) {
|
||||
RefPtr<MediaStreamError> error =
|
||||
new MediaStreamError(aWindow, NS_LITERAL_STRING("NotAllowedError"));
|
||||
new MediaStreamError(aWindow, MediaStreamError::Name::NotAllowedError);
|
||||
onFailure->OnError(error);
|
||||
windowListener->Remove(sourceListener);
|
||||
return NS_OK;
|
||||
@ -2844,7 +2844,7 @@ MediaManager::GetUserMedia(nsPIDOMWindowInner* aWindow,
|
||||
constraint.AssignASCII(badConstraint);
|
||||
RefPtr<MediaStreamError> error =
|
||||
new MediaStreamError(window,
|
||||
NS_LITERAL_STRING("OverconstrainedError"),
|
||||
MediaStreamError::Name::OverconstrainedError,
|
||||
NS_LITERAL_STRING(""),
|
||||
constraint);
|
||||
onFailure->OnError(error);
|
||||
@ -2859,8 +2859,8 @@ MediaManager::GetUserMedia(nsPIDOMWindowInner* aWindow,
|
||||
// When privacy.resistFingerprinting = true, no available
|
||||
// device implies content script is requesting a fake
|
||||
// device, so report NotAllowedError.
|
||||
resistFingerprinting ? NS_LITERAL_STRING("NotAllowedError")
|
||||
: NS_LITERAL_STRING("NotFoundError"));
|
||||
resistFingerprinting ? MediaStreamError::Name::NotAllowedError
|
||||
: MediaStreamError::Name::NotFoundError);
|
||||
onFailure->OnError(error);
|
||||
return;
|
||||
}
|
||||
@ -3050,7 +3050,7 @@ MediaManager::EnumerateDevicesImpl(uint64_t aWindowId,
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
RefPtr<PledgeSourceSet> p = new PledgeSourceSet();
|
||||
RefPtr<MediaStreamError> error =
|
||||
new MediaStreamError(window, NS_LITERAL_STRING("NotAllowedError"));
|
||||
new MediaStreamError(window, MediaStreamError::Name::NotAllowedError);
|
||||
p->Reject(error);
|
||||
return p.forget();
|
||||
}
|
||||
@ -3651,34 +3651,25 @@ MediaManager::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
MOZ_ASSERT(needVideo || needAudio);
|
||||
|
||||
if ((needVideo && !videoFound) || (needAudio && !audioFound)) {
|
||||
task->Denied(NS_LITERAL_STRING("NotAllowedError"));
|
||||
task->Denied(MediaMgrError::Name::NotAllowedError);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
if (sHasShutdown) {
|
||||
return task->Denied(NS_LITERAL_STRING("In shutdown"));
|
||||
return task->Denied(MediaMgrError::Name::AbortError,
|
||||
NS_LITERAL_STRING("In shutdown"));
|
||||
}
|
||||
// Reuse the same thread to save memory.
|
||||
MediaManager::PostTask(task.forget());
|
||||
return NS_OK;
|
||||
|
||||
} else if (!strcmp(aTopic, "getUserMedia:response:deny")) {
|
||||
nsString errorMessage(NS_LITERAL_STRING("NotAllowedError"));
|
||||
|
||||
if (aSubject) {
|
||||
nsCOMPtr<nsISupportsString> msg(do_QueryInterface(aSubject));
|
||||
MOZ_ASSERT(msg);
|
||||
msg->GetData(errorMessage);
|
||||
if (errorMessage.IsEmpty())
|
||||
errorMessage.AssignLiteral(u"InternalError");
|
||||
}
|
||||
|
||||
nsString key(aData);
|
||||
RefPtr<GetUserMediaTask> task;
|
||||
mActiveCallbacks.Remove(key, getter_AddRefs(task));
|
||||
if (task) {
|
||||
task->Denied(errorMessage);
|
||||
task->Denied(MediaMgrError::Name::NotAllowedError);
|
||||
nsTArray<nsString>* array;
|
||||
if (!mCallIds.Get(task->GetWindowID(), &array)) {
|
||||
return NS_OK;
|
||||
@ -4025,12 +4016,12 @@ SourceListener::InitializeAsync()
|
||||
if (rv == NS_ERROR_NOT_AVAILABLE) {
|
||||
log.AssignASCII("Concurrent mic process limit.");
|
||||
aHolder.Reject(MakeRefPtr<MediaMgrError>(
|
||||
NS_LITERAL_STRING("NotReadableError"), log), __func__);
|
||||
MediaMgrError::Name::NotReadableError, log), __func__);
|
||||
return;
|
||||
}
|
||||
log.AssignASCII("Starting audio failed");
|
||||
aHolder.Reject(MakeRefPtr<MediaMgrError>(
|
||||
NS_LITERAL_STRING("InternalError"), log), __func__);
|
||||
MediaMgrError::Name::AbortError, log), __func__);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -4048,7 +4039,7 @@ SourceListener::InitializeAsync()
|
||||
}
|
||||
nsString log;
|
||||
log.AssignASCII("Starting video failed");
|
||||
aHolder.Reject(MakeRefPtr<MediaMgrError>(NS_LITERAL_STRING("InternalError"), log), __func__);
|
||||
aHolder.Reject(MakeRefPtr<MediaMgrError>(MediaMgrError::Name::AbortError, log), __func__);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -10,41 +10,51 @@
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
BaseMediaMgrError::BaseMediaMgrError(const nsAString& aName,
|
||||
BaseMediaMgrError::BaseMediaMgrError(Name aName,
|
||||
const nsAString& aMessage,
|
||||
const nsAString& aConstraint)
|
||||
: mName(aName)
|
||||
, mMessage(aMessage)
|
||||
: mMessage(aMessage)
|
||||
, mConstraint(aConstraint)
|
||||
, mName(aName)
|
||||
{
|
||||
if (mMessage.IsEmpty()) {
|
||||
if (mName.EqualsLiteral("NotFoundError")) {
|
||||
mMessage.AssignLiteral("The object can not be found here.");
|
||||
} else if (mName.EqualsLiteral("NotAllowedError")) {
|
||||
mMessage.AssignLiteral("The request is not allowed by the user agent "
|
||||
"or the platform in the current context.");
|
||||
} else if (mName.EqualsLiteral("SecurityError")) {
|
||||
mMessage.AssignLiteral("The operation is insecure.");
|
||||
} else if (mName.EqualsLiteral("NotReadableError")) {
|
||||
mMessage.AssignLiteral("The I/O read operation failed.");
|
||||
} else if (mName.EqualsLiteral("InternalError")) {
|
||||
mMessage.AssignLiteral("Internal error.");
|
||||
} else if (mName.EqualsLiteral("NotSupportedError")) {
|
||||
mMessage.AssignLiteral("The operation is not supported.");
|
||||
} else if (mName.EqualsLiteral("OverconstrainedError")) {
|
||||
mMessage.AssignLiteral("Constraints could be not satisfied.");
|
||||
|
||||
#define MAP_MEDIAERR(name, msg) { Name::name, #name, msg }
|
||||
|
||||
static struct {
|
||||
Name mName;
|
||||
const char* mNameString;
|
||||
const char* mMessage;
|
||||
} map[] = {
|
||||
MAP_MEDIAERR(AbortError, "The operation was aborted."),
|
||||
MAP_MEDIAERR(InvalidStateError, "The object is in an invalid state."),
|
||||
MAP_MEDIAERR(NotAllowedError, "The request is not allowed by the user agent "
|
||||
"or the platform in the current context."),
|
||||
MAP_MEDIAERR(NotFoundError, "The object can not be found here."),
|
||||
MAP_MEDIAERR(NotReadableError, "The I/O read operation failed."),
|
||||
MAP_MEDIAERR(NotSupportedError, "The operation is not supported."),
|
||||
MAP_MEDIAERR(OverconstrainedError, "Constraints could be not satisfied."),
|
||||
MAP_MEDIAERR(SecurityError, "The operation is insecure."),
|
||||
MAP_MEDIAERR(TypeError, ""),
|
||||
};
|
||||
for (auto& entry : map) {
|
||||
if (entry.mName == mName) {
|
||||
mNameString.AssignASCII(entry.mNameString);
|
||||
if (mMessage.IsEmpty()) {
|
||||
mMessage.AssignASCII(entry.mMessage);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
MOZ_ASSERT_UNREACHABLE("Unknown error type");
|
||||
}
|
||||
|
||||
|
||||
NS_IMPL_ISUPPORTS0(MediaMgrError)
|
||||
|
||||
namespace dom {
|
||||
|
||||
MediaStreamError::MediaStreamError(
|
||||
nsPIDOMWindowInner* aParent,
|
||||
const nsAString& aName,
|
||||
Name aName,
|
||||
const nsAString& aMessage,
|
||||
const nsAString& aConstraint)
|
||||
: BaseMediaMgrError(aName, aMessage, aConstraint)
|
||||
@ -68,7 +78,7 @@ MediaStreamError::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
void
|
||||
MediaStreamError::GetName(nsAString& aName) const
|
||||
{
|
||||
aName = mName;
|
||||
aName = mNameString;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -19,6 +19,7 @@
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
|
||||
#define MOZILLA_DOM_MEDIASTREAMERROR_IMPLEMENTATION_IID \
|
||||
@ -31,20 +32,36 @@ class MediaStreamError;
|
||||
class BaseMediaMgrError
|
||||
{
|
||||
friend class dom::MediaStreamError;
|
||||
public:
|
||||
enum class Name
|
||||
{
|
||||
AbortError,
|
||||
InvalidStateError,
|
||||
NotAllowedError,
|
||||
NotFoundError,
|
||||
NotReadableError,
|
||||
NotSupportedError,
|
||||
OverconstrainedError,
|
||||
SecurityError,
|
||||
TypeError,
|
||||
};
|
||||
|
||||
protected:
|
||||
BaseMediaMgrError(const nsAString& aName,
|
||||
BaseMediaMgrError(Name aName,
|
||||
const nsAString& aMessage,
|
||||
const nsAString& aConstraint);
|
||||
const nsString mName;
|
||||
nsString mNameString;
|
||||
nsString mMessage;
|
||||
const nsString mConstraint;
|
||||
private:
|
||||
const Name mName;
|
||||
};
|
||||
|
||||
class MediaMgrError final : public nsISupports,
|
||||
public BaseMediaMgrError
|
||||
{
|
||||
public:
|
||||
explicit MediaMgrError(const nsAString& aName,
|
||||
explicit MediaMgrError(Name aName,
|
||||
const nsAString& aMessage = EmptyString(),
|
||||
const nsAString& aConstraint = EmptyString())
|
||||
: BaseMediaMgrError(aName, aMessage, aConstraint) {}
|
||||
@ -62,7 +79,7 @@ class MediaStreamError final : public nsISupports,
|
||||
{
|
||||
public:
|
||||
MediaStreamError(nsPIDOMWindowInner* aParent,
|
||||
const nsAString& aName,
|
||||
Name aName,
|
||||
const nsAString& aMessage = EmptyString(),
|
||||
const nsAString& aConstraint = EmptyString());
|
||||
|
||||
|
@ -52,7 +52,7 @@ MediaStreamTrackSource::ApplyConstraints(
|
||||
{
|
||||
RefPtr<PledgeVoid> p = new PledgeVoid();
|
||||
p->Reject(new MediaStreamError(aWindow,
|
||||
NS_LITERAL_STRING("OverconstrainedError"),
|
||||
MediaStreamError::Name::OverconstrainedError,
|
||||
NS_LITERAL_STRING("")));
|
||||
return p.forget();
|
||||
}
|
||||
|
@ -667,6 +667,12 @@ double
|
||||
AudioContext::CurrentTime()
|
||||
{
|
||||
MediaStream* stream = Destination()->Stream();
|
||||
|
||||
if (!mIsStarted &&
|
||||
stream->StreamTimeToSeconds(stream->GetCurrentTime()) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The value of a MediaStream's CurrentTime will always advance forward; it will never
|
||||
// reset (even if one rewinds a video.) Therefore we can use a single Random Seed
|
||||
// initialized at the same time as the object.
|
||||
|
@ -71,10 +71,8 @@ tags=capturestream
|
||||
[test_audioParamExponentialRamp.html]
|
||||
[test_audioParamGain.html]
|
||||
[test_audioParamLinearRamp.html]
|
||||
skip-if = os == "linux" || os == "win" #bug 1446346
|
||||
[test_audioParamSetCurveAtTime.html]
|
||||
[test_audioParamSetCurveAtTimeTwice.html]
|
||||
skip-if = os == "linux" || os == "win" #bug 1446456
|
||||
[test_audioParamSetCurveAtTimeZeroDuration.html]
|
||||
[test_audioParamSetTargetAtTime.html]
|
||||
[test_audioParamSetTargetAtTimeZeroTimeConstant.html]
|
||||
|
@ -225,7 +225,7 @@ GetIncumbentGlobal()
|
||||
// manipulated the stack. If it's null, that means that there
|
||||
// must be no entry global on the stack, and therefore no incumbent
|
||||
// global either.
|
||||
JSContext* cx = nsContentUtils::GetCurrentJSContextForThread();
|
||||
JSContext* cx = nsContentUtils::GetCurrentJSContext();
|
||||
if (!cx) {
|
||||
MOZ_ASSERT(ScriptSettingsStack::EntryGlobal() == nullptr);
|
||||
return nullptr;
|
||||
@ -247,7 +247,7 @@ GetIncumbentGlobal()
|
||||
nsIGlobalObject*
|
||||
GetCurrentGlobal()
|
||||
{
|
||||
JSContext* cx = nsContentUtils::GetCurrentJSContextForThread();
|
||||
JSContext* cx = nsContentUtils::GetCurrentJSContext();
|
||||
if (!cx) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -748,7 +748,7 @@ AutoEntryScript::DocshellEntryMonitor::Exit(JSContext* aCx)
|
||||
|
||||
AutoIncumbentScript::AutoIncumbentScript(nsIGlobalObject* aGlobalObject)
|
||||
: ScriptSettingsStackEntry(aGlobalObject, eIncumbentScript)
|
||||
, mCallerOverride(nsContentUtils::GetCurrentJSContextForThread())
|
||||
, mCallerOverride(nsContentUtils::GetCurrentJSContext())
|
||||
{
|
||||
ScriptSettingsStack::Push(this);
|
||||
}
|
||||
|
@ -1140,6 +1140,11 @@ public:
|
||||
microTaskQueue->push(runnable.forget());
|
||||
}
|
||||
|
||||
bool IsSystemCaller() const override
|
||||
{
|
||||
return mWorkerPrivate->UsesSystemPrincipal();
|
||||
}
|
||||
|
||||
private:
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
};
|
||||
|
@ -5,30 +5,23 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "AudioWorkletGlobalScope.h"
|
||||
#include "WorkletPrincipal.h"
|
||||
#include "mozilla/dom/AudioWorkletGlobalScopeBinding.h"
|
||||
#include "mozilla/dom/FunctionBinding.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
AudioWorkletGlobalScope::AudioWorkletGlobalScope(nsPIDOMWindowInner* aWindow)
|
||||
: WorkletGlobalScope(aWindow)
|
||||
{
|
||||
}
|
||||
|
||||
AudioWorkletGlobalScope::~AudioWorkletGlobalScope()
|
||||
{
|
||||
}
|
||||
AudioWorkletGlobalScope::AudioWorkletGlobalScope() = default;
|
||||
|
||||
bool
|
||||
AudioWorkletGlobalScope::WrapGlobalObject(JSContext* aCx,
|
||||
nsIPrincipal* aPrincipal,
|
||||
JS::MutableHandle<JSObject*> aReflector)
|
||||
{
|
||||
JS::CompartmentOptions options;
|
||||
return AudioWorkletGlobalScopeBinding::Wrap(aCx, this, this,
|
||||
options,
|
||||
nsJSPrincipals::get(aPrincipal),
|
||||
WorkletPrincipal::GetWorkletPrincipal(),
|
||||
true, aReflector);
|
||||
}
|
||||
|
||||
|
@ -17,10 +17,10 @@ class VoidFunction;
|
||||
class AudioWorkletGlobalScope final : public WorkletGlobalScope
|
||||
{
|
||||
public:
|
||||
explicit AudioWorkletGlobalScope(nsPIDOMWindowInner* aWindow);
|
||||
AudioWorkletGlobalScope();
|
||||
|
||||
bool
|
||||
WrapGlobalObject(JSContext* aCx, nsIPrincipal* aPrincipal,
|
||||
WrapGlobalObject(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aReflector) override;
|
||||
|
||||
void
|
||||
@ -28,7 +28,7 @@ public:
|
||||
VoidFunction& aProcessorCtor);
|
||||
|
||||
private:
|
||||
~AudioWorkletGlobalScope();
|
||||
~AudioWorkletGlobalScope() = default;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -5,30 +5,23 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "PaintWorkletGlobalScope.h"
|
||||
#include "WorkletPrincipal.h"
|
||||
#include "mozilla/dom/PaintWorkletGlobalScopeBinding.h"
|
||||
#include "mozilla/dom/FunctionBinding.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
PaintWorkletGlobalScope::PaintWorkletGlobalScope(nsPIDOMWindowInner* aWindow)
|
||||
: WorkletGlobalScope(aWindow)
|
||||
{
|
||||
}
|
||||
|
||||
PaintWorkletGlobalScope::~PaintWorkletGlobalScope()
|
||||
{
|
||||
}
|
||||
PaintWorkletGlobalScope::PaintWorkletGlobalScope() = default;
|
||||
|
||||
bool
|
||||
PaintWorkletGlobalScope::WrapGlobalObject(JSContext* aCx,
|
||||
nsIPrincipal* aPrincipal,
|
||||
JS::MutableHandle<JSObject*> aReflector)
|
||||
{
|
||||
JS::CompartmentOptions options;
|
||||
return PaintWorkletGlobalScopeBinding::Wrap(aCx, this, this,
|
||||
options,
|
||||
nsJSPrincipals::get(aPrincipal),
|
||||
WorkletPrincipal::GetWorkletPrincipal(),
|
||||
true, aReflector);
|
||||
}
|
||||
|
||||
|
@ -17,17 +17,17 @@ class VoidFunction;
|
||||
class PaintWorkletGlobalScope final : public WorkletGlobalScope
|
||||
{
|
||||
public:
|
||||
explicit PaintWorkletGlobalScope(nsPIDOMWindowInner* aWindow);
|
||||
PaintWorkletGlobalScope();
|
||||
|
||||
bool
|
||||
WrapGlobalObject(JSContext* aCx, nsIPrincipal* aPrincipal,
|
||||
WrapGlobalObject(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aReflector) override;
|
||||
|
||||
void
|
||||
RegisterPaint(const nsAString& aType, VoidFunction& aProcessorCtor);
|
||||
|
||||
private:
|
||||
~PaintWorkletGlobalScope();
|
||||
~PaintWorkletGlobalScope() = default;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -5,11 +5,13 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "Worklet.h"
|
||||
#include "WorkletThread.h"
|
||||
#include "AudioWorkletGlobalScope.h"
|
||||
#include "PaintWorkletGlobalScope.h"
|
||||
|
||||
#include "mozilla/dom/WorkletBinding.h"
|
||||
#include "mozilla/dom/BlobBinding.h"
|
||||
#include "mozilla/dom/DOMPrefs.h"
|
||||
#include "mozilla/dom/Fetch.h"
|
||||
#include "mozilla/dom/PromiseNativeHandler.h"
|
||||
#include "mozilla/dom/RegisterWorkletBindings.h"
|
||||
@ -24,20 +26,54 @@
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class ExecutionRunnable final : public Runnable
|
||||
{
|
||||
public:
|
||||
ExecutionRunnable(WorkletFetchHandler* aHandler, Worklet::WorkletType aType,
|
||||
JS::UniqueTwoByteChars aScriptBuffer, size_t aScriptLength,
|
||||
const WorkletLoadInfo& aWorkletLoadInfo)
|
||||
: Runnable("Worklet::ExecutionRunnable")
|
||||
, mHandler(aHandler)
|
||||
, mScriptBuffer(Move(aScriptBuffer))
|
||||
, mScriptLength(aScriptLength)
|
||||
, mWorkletType(aType)
|
||||
, mResult(NS_ERROR_FAILURE)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override;
|
||||
|
||||
private:
|
||||
void
|
||||
RunOnWorkletThread();
|
||||
|
||||
void
|
||||
RunOnMainThread();
|
||||
|
||||
RefPtr<WorkletFetchHandler> mHandler;
|
||||
JS::UniqueTwoByteChars mScriptBuffer;
|
||||
size_t mScriptLength;
|
||||
Worklet::WorkletType mWorkletType;
|
||||
nsresult mResult;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// WorkletFetchHandler
|
||||
|
||||
class WorkletFetchHandler : public PromiseNativeHandler
|
||||
, public nsIStreamLoaderObserver
|
||||
class WorkletFetchHandler final : public PromiseNativeHandler
|
||||
, public nsIStreamLoaderObserver
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
static already_AddRefed<Promise>
|
||||
Fetch(Worklet* aWorklet, const nsAString& aModuleURL, CallerType aCallerType,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(aWorklet);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> global =
|
||||
do_QueryInterface(aWorklet->GetParentObject());
|
||||
@ -60,7 +96,8 @@ public:
|
||||
|
||||
nsCOMPtr<nsIURI> baseURI = doc->GetBaseURI();
|
||||
nsCOMPtr<nsIURI> resolvedURI;
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(resolvedURI), aModuleURL, nullptr, baseURI);
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(resolvedURI), aModuleURL, nullptr,
|
||||
baseURI);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
promise->MaybeReject(rv);
|
||||
return promise.forget();
|
||||
@ -105,6 +142,8 @@ public:
|
||||
virtual void
|
||||
ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!aValue.isObject()) {
|
||||
RejectPromises(NS_ERROR_FAILURE);
|
||||
return;
|
||||
@ -172,7 +211,7 @@ public:
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
char16_t* scriptTextBuf;
|
||||
JS::UniqueTwoByteChars scriptTextBuf;
|
||||
size_t scriptTextLength;
|
||||
nsresult rv =
|
||||
ScriptLoader::ConvertToUTF16(nullptr, aString, aStringLen,
|
||||
@ -184,53 +223,50 @@ public:
|
||||
}
|
||||
|
||||
// Moving the ownership of the buffer
|
||||
JS::SourceBufferHolder buffer(scriptTextBuf, scriptTextLength,
|
||||
JS::SourceBufferHolder::GiveOwnership);
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
new ExecutionRunnable(this, mWorklet->Type(), Move(scriptTextBuf),
|
||||
scriptTextLength, mWorklet->LoadInfo());
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
jsapi.Init();
|
||||
|
||||
RefPtr<WorkletGlobalScope> globalScope =
|
||||
mWorklet->GetOrCreateGlobalScope(jsapi.cx());
|
||||
MOZ_ASSERT(globalScope);
|
||||
|
||||
AutoEntryScript aes(globalScope, "Worklet");
|
||||
JSContext* cx = aes.cx();
|
||||
|
||||
JS::Rooted<JSObject*> globalObj(cx, globalScope->GetGlobalJSObject());
|
||||
|
||||
(void) new XPCWrappedNativeScope(cx, globalObj);
|
||||
|
||||
NS_ConvertUTF16toUTF8 url(mURL);
|
||||
|
||||
JS::CompileOptions compileOptions(cx);
|
||||
compileOptions.setIntroductionType("Worklet");
|
||||
compileOptions.setFileAndLine(url.get(), 0);
|
||||
compileOptions.setIsRunOnce(true);
|
||||
compileOptions.setNoScriptRval(true);
|
||||
|
||||
JSAutoCompartment comp(cx, globalObj);
|
||||
|
||||
JS::Rooted<JS::Value> unused(cx);
|
||||
if (!JS::Evaluate(cx, compileOptions, buffer, &unused)) {
|
||||
ErrorResult error;
|
||||
error.MightThrowJSException();
|
||||
error.StealExceptionFromJSContext(cx);
|
||||
RejectPromises(error.StealNSResult());
|
||||
RefPtr<WorkletThread> thread = mWorklet->GetOrCreateThread();
|
||||
if (!thread) {
|
||||
RejectPromises(NS_ERROR_FAILURE);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (NS_FAILED(thread->DispatchRunnable(runnable.forget()))) {
|
||||
RejectPromises(NS_ERROR_FAILURE);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// All done.
|
||||
ResolvePromises();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
virtual void
|
||||
RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
RejectPromises(NS_ERROR_DOM_NETWORK_ERR);
|
||||
}
|
||||
|
||||
const nsString& URL() const
|
||||
{
|
||||
return mURL;
|
||||
}
|
||||
|
||||
void
|
||||
ExecutionFailed(nsresult aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
RejectPromises(aRv);
|
||||
}
|
||||
|
||||
void
|
||||
ExecutionSucceeded()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
ResolvePromises();
|
||||
}
|
||||
|
||||
private:
|
||||
WorkletFetchHandler(Worklet* aWorklet, const nsAString& aURL,
|
||||
Promise* aPromise)
|
||||
@ -241,6 +277,7 @@ private:
|
||||
{
|
||||
MOZ_ASSERT(aWorklet);
|
||||
MOZ_ASSERT(aPromise);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mPromises.AppendElement(aPromise);
|
||||
}
|
||||
@ -252,6 +289,7 @@ private:
|
||||
AddPromise(Promise* aPromise)
|
||||
{
|
||||
MOZ_ASSERT(aPromise);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
switch (mStatus) {
|
||||
case ePending:
|
||||
@ -274,6 +312,7 @@ private:
|
||||
{
|
||||
MOZ_ASSERT(mStatus == ePending);
|
||||
MOZ_ASSERT(NS_FAILED(aResult));
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
for (uint32_t i = 0; i < mPromises.Length(); ++i) {
|
||||
mPromises[i]->MaybeReject(aResult);
|
||||
@ -289,6 +328,7 @@ private:
|
||||
ResolvePromises()
|
||||
{
|
||||
MOZ_ASSERT(mStatus == ePending);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
for (uint32_t i = 0; i < mPromises.Length(); ++i) {
|
||||
mPromises[i]->MaybeResolveWithUndefined();
|
||||
@ -315,10 +355,110 @@ private:
|
||||
|
||||
NS_IMPL_ISUPPORTS(WorkletFetchHandler, nsIStreamLoaderObserver)
|
||||
|
||||
NS_IMETHODIMP
|
||||
ExecutionRunnable::Run()
|
||||
{
|
||||
if (WorkletThread::IsOnWorkletThread()) {
|
||||
RunOnWorkletThread();
|
||||
return NS_DispatchToMainThread(this);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
RunOnMainThread();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
ExecutionRunnable::RunOnWorkletThread()
|
||||
{
|
||||
WorkletThread::AssertIsOnWorkletThread();
|
||||
|
||||
WorkletThread* workletThread = WorkletThread::Get();
|
||||
MOZ_ASSERT(workletThread);
|
||||
|
||||
JSContext* cx = workletThread->GetJSContext();
|
||||
JSAutoRequest ar(cx);
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
jsapi.Init();
|
||||
|
||||
RefPtr<WorkletGlobalScope> globalScope =
|
||||
Worklet::CreateGlobalScope(jsapi.cx(), mWorkletType);
|
||||
MOZ_ASSERT(globalScope);
|
||||
|
||||
AutoEntryScript aes(globalScope, "Worklet");
|
||||
cx = aes.cx();
|
||||
|
||||
JS::Rooted<JSObject*> globalObj(cx, globalScope->GetGlobalJSObject());
|
||||
|
||||
NS_ConvertUTF16toUTF8 url(mHandler->URL());
|
||||
|
||||
JS::CompileOptions compileOptions(cx);
|
||||
compileOptions.setIntroductionType("Worklet");
|
||||
compileOptions.setFileAndLine(url.get(), 0);
|
||||
compileOptions.setIsRunOnce(true);
|
||||
compileOptions.setNoScriptRval(true);
|
||||
|
||||
JSAutoCompartment comp(cx, globalObj);
|
||||
|
||||
JS::SourceBufferHolder buffer(mScriptBuffer.release(), mScriptLength,
|
||||
JS::SourceBufferHolder::GiveOwnership);
|
||||
JS::Rooted<JS::Value> unused(cx);
|
||||
if (!JS::Evaluate(cx, compileOptions, buffer, &unused)) {
|
||||
ErrorResult error;
|
||||
error.MightThrowJSException();
|
||||
error.StealExceptionFromJSContext(cx);
|
||||
mResult = error.StealNSResult();
|
||||
return;
|
||||
}
|
||||
|
||||
// All done.
|
||||
mResult = NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
ExecutionRunnable::RunOnMainThread()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (NS_FAILED(mResult)) {
|
||||
mHandler->ExecutionFailed(mResult);
|
||||
return;
|
||||
}
|
||||
|
||||
mHandler->ExecutionSucceeded();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// WorkletLoadInfo
|
||||
|
||||
WorkletLoadInfo::WorkletLoadInfo()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
}
|
||||
|
||||
WorkletLoadInfo::~WorkletLoadInfo()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Worklet
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Worklet, mWindow, mScope)
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(Worklet)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Worklet)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
|
||||
tmp->TerminateThread();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Worklet)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(Worklet)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(Worklet)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(Worklet)
|
||||
|
||||
@ -330,23 +470,44 @@ NS_INTERFACE_MAP_END
|
||||
Worklet::Worklet(nsPIDOMWindowInner* aWindow, nsIPrincipal* aPrincipal,
|
||||
WorkletType aWorkletType)
|
||||
: mWindow(aWindow)
|
||||
, mPrincipal(aPrincipal)
|
||||
, mWorkletType(aWorkletType)
|
||||
{
|
||||
MOZ_ASSERT(aWindow);
|
||||
MOZ_ASSERT(aPrincipal);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
#ifdef RELEASE_OR_BETA
|
||||
MOZ_CRASH("This code should not go to release/beta yet!");
|
||||
#endif
|
||||
|
||||
// Reset mWorkletLoadInfo and populate it.
|
||||
|
||||
memset(&mWorkletLoadInfo, 0, sizeof(WorkletLoadInfo));
|
||||
|
||||
mWorkletLoadInfo.mInnerWindowID = aWindow->WindowID();
|
||||
|
||||
nsPIDOMWindowOuter* outerWindow = aWindow->GetOuterWindow();
|
||||
if (outerWindow) {
|
||||
mWorkletLoadInfo.mOuterWindowID = outerWindow->WindowID();
|
||||
}
|
||||
|
||||
mWorkletLoadInfo.mOriginAttributes =
|
||||
BasePrincipal::Cast(aPrincipal)->OriginAttributesRef();
|
||||
|
||||
mWorkletLoadInfo.mPrincipal = aPrincipal;
|
||||
|
||||
mWorkletLoadInfo.mDumpEnabled = DOMPrefs::DumpEnabled();
|
||||
}
|
||||
|
||||
Worklet::~Worklet()
|
||||
{}
|
||||
{
|
||||
TerminateThread();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
Worklet::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return WorkletBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
@ -354,42 +515,45 @@ already_AddRefed<Promise>
|
||||
Worklet::Import(const nsAString& aModuleURL, CallerType aCallerType,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return WorkletFetchHandler::Fetch(this, aModuleURL, aCallerType, aRv);
|
||||
}
|
||||
|
||||
WorkletGlobalScope*
|
||||
Worklet::GetOrCreateGlobalScope(JSContext* aCx)
|
||||
/* static */ already_AddRefed<WorkletGlobalScope>
|
||||
Worklet::CreateGlobalScope(JSContext* aCx, WorkletType aWorkletType)
|
||||
{
|
||||
if (!mScope) {
|
||||
switch (mWorkletType) {
|
||||
case eAudioWorklet:
|
||||
mScope = new AudioWorkletGlobalScope(mWindow);
|
||||
break;
|
||||
case ePaintWorklet:
|
||||
mScope = new PaintWorkletGlobalScope(mWindow);
|
||||
break;
|
||||
}
|
||||
WorkletThread::AssertIsOnWorkletThread();
|
||||
|
||||
JS::Rooted<JSObject*> global(aCx);
|
||||
NS_ENSURE_TRUE(mScope->WrapGlobalObject(aCx, mPrincipal, &global), nullptr);
|
||||
RefPtr<WorkletGlobalScope> scope;
|
||||
|
||||
JSAutoCompartment ac(aCx, global);
|
||||
|
||||
// Init Web IDL bindings
|
||||
if (!RegisterWorkletBindings(aCx, global)) {
|
||||
mScope = nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JS_FireOnNewGlobalObject(aCx, global);
|
||||
switch (aWorkletType) {
|
||||
case eAudioWorklet:
|
||||
scope = new AudioWorkletGlobalScope();
|
||||
break;
|
||||
case ePaintWorklet:
|
||||
scope = new PaintWorkletGlobalScope();
|
||||
break;
|
||||
}
|
||||
|
||||
return mScope;
|
||||
JS::Rooted<JSObject*> global(aCx);
|
||||
NS_ENSURE_TRUE(scope->WrapGlobalObject(aCx, &global), nullptr);
|
||||
|
||||
JSAutoCompartment ac(aCx, global);
|
||||
|
||||
// Init Web IDL bindings
|
||||
if (!RegisterWorkletBindings(aCx, global)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JS_FireOnNewGlobalObject(aCx, global);
|
||||
|
||||
return scope.forget();
|
||||
}
|
||||
|
||||
WorkletFetchHandler*
|
||||
Worklet::GetImportFetchHandler(const nsACString& aURI)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return mImportHandlers.GetWeak(aURI);
|
||||
}
|
||||
|
||||
@ -399,9 +563,36 @@ Worklet::AddImportFetchHandler(const nsACString& aURI,
|
||||
{
|
||||
MOZ_ASSERT(aHandler);
|
||||
MOZ_ASSERT(!mImportHandlers.GetWeak(aURI));
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mImportHandlers.Put(aURI, aHandler);
|
||||
}
|
||||
|
||||
WorkletThread*
|
||||
Worklet::GetOrCreateThread()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!mWorkletThread) {
|
||||
// Thread creation. FIXME: this will change.
|
||||
mWorkletThread = WorkletThread::Create(mWorkletLoadInfo);
|
||||
}
|
||||
|
||||
return mWorkletThread;
|
||||
}
|
||||
|
||||
void
|
||||
Worklet::TerminateThread()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!mWorkletThread) {
|
||||
return;
|
||||
}
|
||||
|
||||
mWorkletThread->Terminate();
|
||||
mWorkletThread = nullptr;
|
||||
mWorkletLoadInfo.mPrincipal = nullptr;
|
||||
}
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
||||
|
@ -8,6 +8,7 @@
|
||||
#define mozilla_dom_Worklet_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/BasePrincipal.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "nsRefPtrHashtable.h"
|
||||
#include "nsWrapperCache.h"
|
||||
@ -20,10 +21,44 @@ namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class Promise;
|
||||
class WorkletGlobalScope;
|
||||
class Worklet;
|
||||
class WorkletFetchHandler;
|
||||
class WorkletGlobalScope;
|
||||
class WorkletThread;
|
||||
enum class CallerType : uint32_t;
|
||||
|
||||
class WorkletLoadInfo
|
||||
{
|
||||
public:
|
||||
WorkletLoadInfo();
|
||||
~WorkletLoadInfo();
|
||||
|
||||
uint64_t OuterWindowID() const { return mOuterWindowID; }
|
||||
uint64_t InnerWindowID() const { return mInnerWindowID; }
|
||||
bool DumpEnabled() const { return mDumpEnabled; }
|
||||
|
||||
const OriginAttributes& OriginAttributesRef() const
|
||||
{
|
||||
return mOriginAttributes;
|
||||
}
|
||||
|
||||
nsIPrincipal* Principal() const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return mPrincipal;
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t mOuterWindowID;
|
||||
uint64_t mInnerWindowID;
|
||||
bool mDumpEnabled;
|
||||
OriginAttributes mOriginAttributes;
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
|
||||
friend class Worklet;
|
||||
friend class WorkletThread;
|
||||
};
|
||||
|
||||
class Worklet final : public nsISupports
|
||||
, public nsWrapperCache
|
||||
{
|
||||
@ -51,8 +86,22 @@ public:
|
||||
Import(const nsAString& aModuleURL, CallerType aCallerType,
|
||||
ErrorResult& aRv);
|
||||
|
||||
WorkletGlobalScope*
|
||||
GetOrCreateGlobalScope(JSContext* aCx);
|
||||
WorkletType Type() const
|
||||
{
|
||||
return mWorkletType;
|
||||
}
|
||||
|
||||
static already_AddRefed<WorkletGlobalScope>
|
||||
CreateGlobalScope(JSContext* aCx, WorkletType aWorkletType);
|
||||
|
||||
WorkletThread*
|
||||
GetOrCreateThread();
|
||||
|
||||
const WorkletLoadInfo&
|
||||
LoadInfo() const
|
||||
{
|
||||
return mWorkletLoadInfo;
|
||||
}
|
||||
|
||||
private:
|
||||
~Worklet();
|
||||
@ -63,14 +112,19 @@ private:
|
||||
void
|
||||
AddImportFetchHandler(const nsACString& aURI, WorkletFetchHandler* aHandler);
|
||||
|
||||
void
|
||||
TerminateThread();
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> mWindow;
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
|
||||
WorkletType mWorkletType;
|
||||
|
||||
RefPtr<WorkletGlobalScope> mScope;
|
||||
nsRefPtrHashtable<nsCStringHashKey, WorkletFetchHandler> mImportHandlers;
|
||||
|
||||
RefPtr<WorkletThread> mWorkletThread;
|
||||
|
||||
WorkletLoadInfo mWorkletLoadInfo;
|
||||
|
||||
friend class WorkletFetchHandler;
|
||||
};
|
||||
|
||||
|
@ -7,7 +7,6 @@
|
||||
#include "WorkletGlobalScope.h"
|
||||
#include "mozilla/dom/WorkletGlobalScopeBinding.h"
|
||||
#include "mozilla/dom/Console.h"
|
||||
#include "mozilla/dom/DOMPrefs.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
@ -16,13 +15,11 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(WorkletGlobalScope)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(WorkletGlobalScope)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole)
|
||||
tmp->UnlinkHostObjectURIs();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(WorkletGlobalScope)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole)
|
||||
tmp->TraverseHostObjectURIs(cb);
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
@ -37,18 +34,11 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WorkletGlobalScope)
|
||||
NS_INTERFACE_MAP_ENTRY(WorkletGlobalScope)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
WorkletGlobalScope::WorkletGlobalScope(nsPIDOMWindowInner* aWindow)
|
||||
: mWindow(aWindow)
|
||||
{
|
||||
MOZ_ASSERT(aWindow);
|
||||
}
|
||||
|
||||
WorkletGlobalScope::~WorkletGlobalScope()
|
||||
{
|
||||
}
|
||||
WorkletGlobalScope::WorkletGlobalScope() = default;
|
||||
|
||||
JSObject*
|
||||
WorkletGlobalScope::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
WorkletGlobalScope::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
MOZ_CRASH("We should never get here!");
|
||||
return nullptr;
|
||||
@ -57,8 +47,15 @@ WorkletGlobalScope::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto
|
||||
already_AddRefed<Console>
|
||||
WorkletGlobalScope::GetConsole(JSContext* aCx, ErrorResult& aRv)
|
||||
{
|
||||
RefPtr<WorkletThread> thread = WorkletThread::Get();
|
||||
MOZ_ASSERT(thread);
|
||||
|
||||
if (!mConsole) {
|
||||
mConsole = Console::Create(aCx, mWindow, aRv);
|
||||
mConsole =
|
||||
Console::CreateForWorklet(aCx,
|
||||
thread->GetWorkletLoadInfo().OuterWindowID(),
|
||||
thread->GetWorkletLoadInfo().InnerWindowID(),
|
||||
aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -71,7 +68,12 @@ WorkletGlobalScope::GetConsole(JSContext* aCx, ErrorResult& aRv)
|
||||
void
|
||||
WorkletGlobalScope::Dump(const Optional<nsAString>& aString) const
|
||||
{
|
||||
if (!DOMPrefs::DumpEnabled()) {
|
||||
WorkletThread::AssertIsOnWorkletThread();
|
||||
|
||||
WorkletThread* workletThread = WorkletThread::Get();
|
||||
MOZ_ASSERT(workletThread);
|
||||
|
||||
if (!workletThread->GetWorkletLoadInfo().DumpEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -17,9 +17,6 @@
|
||||
{ 0x1b3f62e7, 0xe357, 0x44be, \
|
||||
{ 0xbf, 0xe0, 0xdf, 0x85, 0xe6, 0x56, 0x85, 0xac } }
|
||||
|
||||
class nsIPrincipal;
|
||||
class nsPIDOMWindowInner;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
@ -34,7 +31,7 @@ public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WorkletGlobalScope)
|
||||
|
||||
explicit WorkletGlobalScope(nsPIDOMWindowInner* aWindow);
|
||||
WorkletGlobalScope();
|
||||
|
||||
nsIGlobalObject* GetParentObject() const
|
||||
{
|
||||
@ -45,8 +42,7 @@ public:
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
virtual bool
|
||||
WrapGlobalObject(JSContext* aCx, nsIPrincipal* aPrincipal,
|
||||
JS::MutableHandle<JSObject*> aReflector) = 0;
|
||||
WrapGlobalObject(JSContext* aCx, JS::MutableHandle<JSObject*> aReflector) = 0;
|
||||
|
||||
virtual JSObject*
|
||||
GetGlobalJSObject() override
|
||||
@ -61,10 +57,9 @@ public:
|
||||
Dump(const Optional<nsAString>& aString) const;
|
||||
|
||||
protected:
|
||||
~WorkletGlobalScope();
|
||||
~WorkletGlobalScope() = default;
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsPIDOMWindowInner> mWindow;
|
||||
RefPtr<Console> mConsole;
|
||||
};
|
||||
|
||||
|
49
dom/worklet/WorkletPrincipal.cpp
Normal file
49
dom/worklet/WorkletPrincipal.cpp
Normal file
@ -0,0 +1,49 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "WorkletPrincipal.h"
|
||||
#include "jsapi.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace WorkletPrincipal {
|
||||
|
||||
struct WorkletPrincipal final : public JSPrincipals
|
||||
{
|
||||
bool write(JSContext* aCx, JSStructuredCloneWriter* aWriter) override
|
||||
{
|
||||
MOZ_CRASH("WorkletPrincipal::write not implemented");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
JSPrincipals*
|
||||
GetWorkletPrincipal()
|
||||
{
|
||||
static WorkletPrincipal sPrincipal;
|
||||
|
||||
/*
|
||||
* To make sure the the principals refcount is initialized to one, atomically
|
||||
* increment it on every pass though this function. If we discover this wasn't
|
||||
* the first time, decrement it again. This avoids the need for
|
||||
* synchronization.
|
||||
*/
|
||||
int32_t prevRefcount = sPrincipal.refcount++;
|
||||
if (prevRefcount > 0) {
|
||||
--sPrincipal.refcount;
|
||||
} else {
|
||||
#ifdef DEBUG
|
||||
sPrincipal.debugToken = kJSPrincipalsDebugToken;
|
||||
#endif
|
||||
}
|
||||
|
||||
return &sPrincipal;
|
||||
}
|
||||
|
||||
} // namespace WorkletPrincipal
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
25
dom/worklet/WorkletPrincipal.h
Normal file
25
dom/worklet/WorkletPrincipal.h
Normal file
@ -0,0 +1,25 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_worklet_WorkletPrincipal_h
|
||||
#define mozilla_dom_worklet_WorkletPrincipal_h
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
namespace WorkletPrincipal {
|
||||
|
||||
JSPrincipals*
|
||||
GetWorkletPrincipal();
|
||||
|
||||
static const uint32_t kJSPrincipalsDebugToken = 0x7e2df9f4;
|
||||
|
||||
} // WorkletPrincipal
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
||||
|
||||
#endif // mozilla_dom_worklet_WorkletPrincipal_h
|
495
dom/worklet/WorkletThread.cpp
Normal file
495
dom/worklet/WorkletThread.cpp
Normal file
@ -0,0 +1,495 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "WorkletThread.h"
|
||||
#include "prthread.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsCycleCollector.h"
|
||||
#include "mozilla/dom/AtomList.h"
|
||||
#include "mozilla/EventQueue.h"
|
||||
#include "mozilla/ThreadEventQueue.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
namespace {
|
||||
|
||||
// The size of the worklet runtime heaps in bytes.
|
||||
#define WORKLET_DEFAULT_RUNTIME_HEAPSIZE 32 * 1024 * 1024
|
||||
|
||||
// The size of the generational GC nursery for worklet, in bytes.
|
||||
#define WORKLET_DEFAULT_NURSERY_SIZE 1 * 1024 * 1024
|
||||
|
||||
// The C stack size. We use the same stack size on all platforms for
|
||||
// consistency.
|
||||
const uint32_t kWorkletStackSize = 256 * sizeof(size_t) * 1024;
|
||||
|
||||
// This class is allocated per thread and can be retrieved from CC.
|
||||
// It's used to get the current WorkletThread object.
|
||||
class WorkletThreadContextPrivate : private PerThreadAtomCache
|
||||
{
|
||||
public:
|
||||
explicit
|
||||
WorkletThreadContextPrivate(WorkletThread* aWorkletThread)
|
||||
: mWorkletThread(aWorkletThread)
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
// Zero out the base class members.
|
||||
memset(this, 0, sizeof(PerThreadAtomCache));
|
||||
|
||||
MOZ_ASSERT(mWorkletThread);
|
||||
}
|
||||
|
||||
~WorkletThreadContextPrivate()
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
}
|
||||
|
||||
WorkletThread*
|
||||
GetWorkletThread() const
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(mWorkletThread);
|
||||
return mWorkletThread;
|
||||
}
|
||||
|
||||
private:
|
||||
WorkletThreadContextPrivate(const WorkletThreadContextPrivate&) = delete;
|
||||
WorkletThreadContextPrivate& operator=(const WorkletThreadContextPrivate&) = delete;
|
||||
|
||||
RefPtr<WorkletThread> mWorkletThread;
|
||||
};
|
||||
|
||||
// Helper functions
|
||||
|
||||
bool
|
||||
PreserveWrapper(JSContext* aCx, JSObject* aObj)
|
||||
{
|
||||
MOZ_ASSERT(aCx);
|
||||
MOZ_ASSERT(aObj);
|
||||
MOZ_ASSERT(mozilla::dom::IsDOMObject(aObj));
|
||||
return mozilla::dom::TryPreserveWrapper(aObj);
|
||||
}
|
||||
|
||||
void
|
||||
DestroyWorkletPrincipals(JSPrincipals* aPrincipals)
|
||||
{
|
||||
MOZ_ASSERT_UNREACHABLE("Worklet principals refcount should never fall below one");
|
||||
}
|
||||
|
||||
JSObject*
|
||||
Wrap(JSContext* aCx, JS::HandleObject aExisting, JS::HandleObject aObj)
|
||||
{
|
||||
if (aExisting) {
|
||||
js::Wrapper::Renew(aExisting, aObj,
|
||||
&js::OpaqueCrossCompartmentWrapper::singleton);
|
||||
}
|
||||
|
||||
return js::Wrapper::New(aCx, aObj,
|
||||
&js::OpaqueCrossCompartmentWrapper::singleton);
|
||||
}
|
||||
|
||||
const JSWrapObjectCallbacks WrapObjectCallbacks =
|
||||
{
|
||||
Wrap,
|
||||
nullptr,
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
// This classes control CC in the worklet thread.
|
||||
|
||||
class WorkletJSRuntime final : public mozilla::CycleCollectedJSRuntime
|
||||
{
|
||||
public:
|
||||
explicit WorkletJSRuntime(JSContext* aCx)
|
||||
: CycleCollectedJSRuntime(aCx)
|
||||
{
|
||||
}
|
||||
|
||||
~WorkletJSRuntime() override = default;
|
||||
|
||||
virtual void
|
||||
PrepareForForgetSkippable() override
|
||||
{
|
||||
}
|
||||
|
||||
virtual void
|
||||
BeginCycleCollectionCallback() override
|
||||
{
|
||||
}
|
||||
|
||||
virtual void
|
||||
EndCycleCollectionCallback(CycleCollectorResults& aResults) override
|
||||
{
|
||||
}
|
||||
|
||||
virtual void
|
||||
DispatchDeferredDeletion(bool aContinuation, bool aPurge) override
|
||||
{
|
||||
MOZ_ASSERT(!aContinuation);
|
||||
nsCycleCollector_doDeferredDeletion();
|
||||
}
|
||||
|
||||
virtual void
|
||||
CustomGCCallback(JSGCStatus aStatus) override
|
||||
{
|
||||
// nsCycleCollector_collect() requires a cycle collector but
|
||||
// ~WorkletJSContext calls nsCycleCollector_shutdown() and the base class
|
||||
// destructor will trigger a final GC. The nsCycleCollector_collect()
|
||||
// call can be skipped in this GC as ~CycleCollectedJSContext removes the
|
||||
// context from |this|.
|
||||
if (aStatus == JSGC_END && !Contexts().isEmpty()) {
|
||||
nsCycleCollector_collect(nullptr);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class WorkletJSContext final : public CycleCollectedJSContext
|
||||
{
|
||||
public:
|
||||
explicit WorkletJSContext(WorkletThread* aWorkletThread)
|
||||
: mWorkletThread(aWorkletThread)
|
||||
{
|
||||
MOZ_ASSERT(aWorkletThread);
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
nsCycleCollector_startup();
|
||||
}
|
||||
|
||||
~WorkletJSContext() override
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
JSContext* cx = MaybeContext();
|
||||
if (!cx) {
|
||||
return; // Initialize() must have failed
|
||||
}
|
||||
|
||||
delete static_cast<WorkletThreadContextPrivate*>(JS_GetContextPrivate(cx));
|
||||
JS_SetContextPrivate(cx, nullptr);
|
||||
|
||||
nsCycleCollector_shutdown();
|
||||
}
|
||||
|
||||
WorkletJSContext* GetAsWorkletJSContext() override { return this; }
|
||||
|
||||
CycleCollectedJSRuntime* CreateRuntime(JSContext* aCx) override
|
||||
{
|
||||
return new WorkletJSRuntime(aCx);
|
||||
}
|
||||
|
||||
nsresult
|
||||
Initialize(JSRuntime* aParentRuntime)
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
nsresult rv =
|
||||
CycleCollectedJSContext::Initialize(aParentRuntime,
|
||||
WORKLET_DEFAULT_RUNTIME_HEAPSIZE,
|
||||
WORKLET_DEFAULT_NURSERY_SIZE);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
JSContext* cx = Context();
|
||||
|
||||
JS_SetContextPrivate(cx, new WorkletThreadContextPrivate(mWorkletThread));
|
||||
|
||||
js::SetPreserveWrapperCallback(cx, PreserveWrapper);
|
||||
JS_InitDestroyPrincipalsCallback(cx, DestroyWorkletPrincipals);
|
||||
JS_SetWrapObjectCallbacks(cx, &WrapObjectCallbacks);
|
||||
JS_SetFutexCanWait(cx);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
DispatchToMicroTask(already_AddRefed<MicroTaskRunnable> aRunnable) override
|
||||
{
|
||||
RefPtr<MicroTaskRunnable> runnable(aRunnable);
|
||||
|
||||
#ifdef DEBUG
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(runnable);
|
||||
|
||||
WorkletThread* workletThread = WorkletThread::Get();
|
||||
MOZ_ASSERT(workletThread);
|
||||
|
||||
JSContext* cx = workletThread->GetJSContext();
|
||||
MOZ_ASSERT(cx);
|
||||
|
||||
JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
|
||||
MOZ_ASSERT(global);
|
||||
#endif
|
||||
|
||||
GetMicroTaskQueue().push(runnable.forget());
|
||||
}
|
||||
|
||||
bool IsSystemCaller() const override
|
||||
{
|
||||
// Currently no support for special system worklet privileges.
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
RefPtr<WorkletThread> mWorkletThread;
|
||||
};
|
||||
|
||||
// This is the first runnable to be dispatched. It calls the RunEventLoop() so
|
||||
// basically everything happens into this runnable. The reason behind this
|
||||
// approach is that, when the Worklet is terminated, it must not have any JS in
|
||||
// stack, but, because we have CC, nsIThread creates an AutoNoJSAPI object by
|
||||
// default. Using this runnable, CC exists only into it.
|
||||
class WorkletThread::PrimaryRunnable final : public Runnable
|
||||
{
|
||||
public:
|
||||
explicit PrimaryRunnable(WorkletThread* aWorkletThread)
|
||||
: Runnable("WorkletThread::PrimaryRunnable")
|
||||
, mWorkletThread(aWorkletThread)
|
||||
{
|
||||
MOZ_ASSERT(aWorkletThread);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
mParentRuntime =
|
||||
JS_GetParentRuntime(CycleCollectedJSContext::Get()->Context());
|
||||
MOZ_ASSERT(mParentRuntime);
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
mWorkletThread->RunEventLoop(mParentRuntime);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
RefPtr<WorkletThread> mWorkletThread;
|
||||
JSRuntime* mParentRuntime;
|
||||
};
|
||||
|
||||
// This is the last runnable to be dispatched. It calls the TerminateInternal()
|
||||
class WorkletThread::TerminateRunnable final : public Runnable
|
||||
{
|
||||
public:
|
||||
explicit TerminateRunnable(WorkletThread* aWorkletThread)
|
||||
: Runnable("WorkletThread::TerminateRunnable")
|
||||
, mWorkletThread(aWorkletThread)
|
||||
{
|
||||
MOZ_ASSERT(aWorkletThread);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
mWorkletThread->TerminateInternal();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
RefPtr<WorkletThread> mWorkletThread;
|
||||
};
|
||||
|
||||
WorkletThread::WorkletThread(const WorkletLoadInfo& aWorkletLoadInfo)
|
||||
: nsThread(MakeNotNull<ThreadEventQueue<mozilla::EventQueue>*>(
|
||||
MakeUnique<mozilla::EventQueue>()),
|
||||
nsThread::NOT_MAIN_THREAD, kWorkletStackSize)
|
||||
, mWorkletLoadInfo(aWorkletLoadInfo)
|
||||
, mCreationTimeStamp(TimeStamp::Now())
|
||||
, mJSContext(nullptr)
|
||||
, mIsTerminating(false)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsContentUtils::RegisterShutdownObserver(this);
|
||||
}
|
||||
|
||||
WorkletThread::~WorkletThread()
|
||||
{
|
||||
// This should be gone during the termination step.
|
||||
MOZ_ASSERT(!mJSContext);
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<WorkletThread>
|
||||
WorkletThread::Create(const WorkletLoadInfo& aWorkletLoadInfo)
|
||||
{
|
||||
RefPtr<WorkletThread> thread =
|
||||
new WorkletThread(aWorkletLoadInfo);
|
||||
if (NS_WARN_IF(NS_FAILED(thread->Init()))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<PrimaryRunnable> runnable = new PrimaryRunnable(thread);
|
||||
if (NS_WARN_IF(NS_FAILED(thread->DispatchRunnable(runnable.forget())))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return thread.forget();
|
||||
}
|
||||
|
||||
nsresult
|
||||
WorkletThread::DispatchRunnable(already_AddRefed<nsIRunnable> aRunnable)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> runnable(aRunnable);
|
||||
return nsThread::Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WorkletThread::DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> runnable(aRunnable);
|
||||
return Dispatch(runnable.forget(), aFlags);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WorkletThread::Dispatch(already_AddRefed<nsIRunnable> aRunnable,
|
||||
uint32_t aFlags)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> runnable(aRunnable);
|
||||
|
||||
// Worklet only supports asynchronous dispatch.
|
||||
if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
return nsThread::Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WorkletThread::DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t aFlags)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
void
|
||||
WorkletThread::RunEventLoop(JSRuntime* aParentRuntime)
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
PR_SetCurrentThreadName("worklet");
|
||||
|
||||
WorkletJSContext context(this);
|
||||
nsresult rv = context.Initialize(aParentRuntime);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
// TODO: error propagation
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: JS_SetDefaultLocale
|
||||
// FIXME: JSSettings
|
||||
// FIXME: JS_SetNativeStackQuota
|
||||
// FIXME: JS_SetSecurityCallbacks
|
||||
// FIXME: JS::SetAsmJSCacheOps
|
||||
// FIXME: JS::SetAsyncTaskCallbacks
|
||||
// FIXME: JS_AddInterruptCallback
|
||||
// FIXME: JS::SetCTypesActivityCallback
|
||||
// FIXME: JS_SetGCZeal
|
||||
|
||||
if (!JS::InitSelfHostedCode(context.Context())) {
|
||||
// TODO: error propagation
|
||||
return;
|
||||
}
|
||||
|
||||
mJSContext = context.Context();
|
||||
|
||||
while (mJSContext) {
|
||||
MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(this, /* wait: */ true));
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mJSContext == nullptr);
|
||||
}
|
||||
|
||||
void
|
||||
WorkletThread::Terminate()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (mIsTerminating) {
|
||||
// nsThread::Dispatch() would leak the runnable if the event queue is no
|
||||
// longer accepting runnables.
|
||||
return;
|
||||
}
|
||||
|
||||
mIsTerminating = true;
|
||||
|
||||
nsContentUtils::UnregisterShutdownObserver(this);
|
||||
|
||||
RefPtr<TerminateRunnable> runnable = new TerminateRunnable(this);
|
||||
DispatchRunnable(runnable.forget());
|
||||
}
|
||||
|
||||
void
|
||||
WorkletThread::TerminateInternal()
|
||||
{
|
||||
AssertIsOnWorkletThread();
|
||||
|
||||
mJSContext = nullptr;
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NewRunnableMethod("WorkletThread::Shutdown", this,
|
||||
&WorkletThread::Shutdown);
|
||||
NS_DispatchToMainThread(runnable);
|
||||
}
|
||||
|
||||
JSContext*
|
||||
WorkletThread::GetJSContext() const
|
||||
{
|
||||
AssertIsOnWorkletThread();
|
||||
MOZ_ASSERT(mJSContext);
|
||||
return mJSContext;
|
||||
}
|
||||
|
||||
const WorkletLoadInfo&
|
||||
WorkletThread::GetWorkletLoadInfo() const
|
||||
{
|
||||
return mWorkletLoadInfo;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
WorkletThread::IsOnWorkletThread()
|
||||
{
|
||||
CycleCollectedJSContext* ccjscx = CycleCollectedJSContext::Get();
|
||||
return ccjscx && ccjscx->GetAsWorkletJSContext();
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
WorkletThread::AssertIsOnWorkletThread()
|
||||
{
|
||||
MOZ_ASSERT(IsOnWorkletThread());
|
||||
}
|
||||
|
||||
/* static */ WorkletThread*
|
||||
WorkletThread::Get()
|
||||
{
|
||||
AssertIsOnWorkletThread();
|
||||
|
||||
CycleCollectedJSContext* ccjscx = CycleCollectedJSContext::Get();
|
||||
MOZ_ASSERT(ccjscx);
|
||||
|
||||
void* cxPrivate = JS_GetContextPrivate(ccjscx->Context());
|
||||
MOZ_ASSERT(cxPrivate);
|
||||
|
||||
return
|
||||
static_cast<WorkletThreadContextPrivate*>(cxPrivate)->GetWorkletThread();
|
||||
}
|
||||
|
||||
// nsIObserver
|
||||
NS_IMETHODIMP
|
||||
WorkletThread::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
const char16_t*)
|
||||
{
|
||||
MOZ_ASSERT(strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0);
|
||||
|
||||
Terminate();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED(WorkletThread, nsThread, nsIObserver)
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
100
dom/worklet/WorkletThread.h
Normal file
100
dom/worklet/WorkletThread.h
Normal file
@ -0,0 +1,100 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_worklet_WorkletThread_h
|
||||
#define mozilla_dom_worklet_WorkletThread_h
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/CondVar.h"
|
||||
#include "mozilla/dom/Worklet.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "nsThread.h"
|
||||
|
||||
class nsIRunnable;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class WorkletThread final : public nsThread, public nsIObserver
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
static already_AddRefed<WorkletThread>
|
||||
Create(const WorkletLoadInfo& aWorkletLoadInfo);
|
||||
|
||||
static WorkletThread*
|
||||
Get();
|
||||
|
||||
static bool
|
||||
IsOnWorkletThread();
|
||||
|
||||
static void
|
||||
AssertIsOnWorkletThread();
|
||||
|
||||
static JSPrincipals*
|
||||
GetWorkerPrincipal();
|
||||
|
||||
JSContext*
|
||||
GetJSContext() const;
|
||||
|
||||
const WorkletLoadInfo&
|
||||
GetWorkletLoadInfo() const;
|
||||
|
||||
nsresult
|
||||
DispatchRunnable(already_AddRefed<nsIRunnable> aRunnable);
|
||||
|
||||
void
|
||||
Terminate();
|
||||
|
||||
DOMHighResTimeStamp
|
||||
TimeStampToDOMHighRes(const TimeStamp& aTimeStamp) const
|
||||
{
|
||||
MOZ_ASSERT(!aTimeStamp.IsNull());
|
||||
TimeDuration duration = aTimeStamp - mCreationTimeStamp;
|
||||
return duration.ToMilliseconds();
|
||||
}
|
||||
|
||||
private:
|
||||
explicit WorkletThread(const WorkletLoadInfo& aWorkletLoadInfo);
|
||||
~WorkletThread();
|
||||
|
||||
void
|
||||
RunEventLoop(JSRuntime* aParentRuntime);
|
||||
class PrimaryRunnable;
|
||||
|
||||
void
|
||||
TerminateInternal();
|
||||
class TerminateRunnable;
|
||||
|
||||
// This should only be called by consumers that have an
|
||||
// nsIEventTarget/nsIThread pointer.
|
||||
NS_IMETHOD
|
||||
Dispatch(already_AddRefed<nsIRunnable> aRunnable, uint32_t aFlags) override;
|
||||
|
||||
NS_IMETHOD
|
||||
DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags) override;
|
||||
|
||||
NS_IMETHOD
|
||||
DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t) override;
|
||||
|
||||
const WorkletLoadInfo mWorkletLoadInfo;
|
||||
TimeStamp mCreationTimeStamp;
|
||||
|
||||
// Touched only on the worklet thread. This is a raw pointer because it's set
|
||||
// and nullified by RunEventLoop().
|
||||
JSContext* mJSContext;
|
||||
|
||||
bool mIsTerminating; // main thread
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_worklet_WorkletThread_h
|
@ -12,6 +12,8 @@ EXPORTS.mozilla.dom += [
|
||||
'PaintWorkletGlobalScope.h',
|
||||
'Worklet.h',
|
||||
'WorkletGlobalScope.h',
|
||||
'WorkletPrincipal.h',
|
||||
'WorkletThread.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
@ -19,6 +21,8 @@ UNIFIED_SOURCES += [
|
||||
'PaintWorkletGlobalScope.cpp',
|
||||
'Worklet.cpp',
|
||||
'WorkletGlobalScope.cpp',
|
||||
'WorkletPrincipal.cpp',
|
||||
'WorkletThread.cpp',
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
|
@ -970,15 +970,7 @@ class HashTable : private AllocPolicy
|
||||
{}
|
||||
|
||||
public:
|
||||
AddPtr()
|
||||
: keyHash(0)
|
||||
#ifdef JS_DEBUG
|
||||
, mutationCount
|
||||
{
|
||||
}
|
||||
#endif
|
||||
{
|
||||
}
|
||||
AddPtr() : keyHash(0) {}
|
||||
};
|
||||
|
||||
// A collection of hash table entries. The collection is enumerated by
|
||||
@ -1273,9 +1265,6 @@ class HashTable : private AllocPolicy
|
||||
#ifdef JS_DEBUG
|
||||
, mutationCount(0)
|
||||
, mEntered(false)
|
||||
, stats
|
||||
{
|
||||
}
|
||||
#endif
|
||||
{}
|
||||
|
||||
|
@ -30,15 +30,7 @@ struct JSPrincipals {
|
||||
uint32_t debugToken;
|
||||
#endif
|
||||
|
||||
JSPrincipals()
|
||||
: refcount(0)
|
||||
#ifdef JS_DEBUG
|
||||
, debugToken
|
||||
{
|
||||
}
|
||||
#endif
|
||||
{
|
||||
}
|
||||
JSPrincipals() : refcount(0) {}
|
||||
|
||||
void setDebugToken(uint32_t token) {
|
||||
# ifdef JS_DEBUG
|
||||
|
@ -586,18 +586,10 @@ class JS_FRIEND_API(AutoEnterPolicy)
|
||||
{
|
||||
public:
|
||||
typedef BaseProxyHandler::Action Action;
|
||||
AutoEnterPolicy(JSContext* cx,
|
||||
const BaseProxyHandler* handler,
|
||||
HandleObject wrapper,
|
||||
HandleId id,
|
||||
Action act,
|
||||
bool mayThrow)
|
||||
: rv
|
||||
{
|
||||
false
|
||||
}
|
||||
AutoEnterPolicy(JSContext* cx, const BaseProxyHandler* handler,
|
||||
HandleObject wrapper, HandleId id, Action act, bool mayThrow)
|
||||
#ifdef JS_DEBUG
|
||||
, context(nullptr), enteredAction{}, prev { nullptr }
|
||||
: context(nullptr)
|
||||
#endif
|
||||
{
|
||||
allow = handler->hasSecurityPolicy() ? handler->enter(cx, wrapper, id, act, mayThrow, &rv)
|
||||
@ -619,17 +611,11 @@ class JS_FRIEND_API(AutoEnterPolicy)
|
||||
protected:
|
||||
// no-op constructor for subclass
|
||||
AutoEnterPolicy()
|
||||
: allow{ false }
|
||||
, rv
|
||||
{
|
||||
false
|
||||
}
|
||||
#ifdef JS_DEBUG
|
||||
, context(nullptr), enteredAction(BaseProxyHandler::NONE), prev { nullptr }
|
||||
: context(nullptr)
|
||||
, enteredAction(BaseProxyHandler::NONE)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
{}
|
||||
void reportErrorIfExceptionIsNotPending(JSContext* cx, jsid id);
|
||||
bool allow;
|
||||
bool rv;
|
||||
|
@ -542,10 +542,7 @@ class MOZ_NONHEAP_CLASS Handle : public js::HandleBase<T, Handle<T>>
|
||||
DECLARE_NONPOINTER_ACCESSOR_METHODS(*ptr);
|
||||
|
||||
private:
|
||||
Handle()
|
||||
: ptr{ nullptr }
|
||||
{
|
||||
}
|
||||
Handle() {}
|
||||
DELETE_ASSIGNMENT_OPS(Handle, T);
|
||||
|
||||
enum Disambiguator { DeliberatelyChoosingThisOverload = 42 };
|
||||
@ -604,10 +601,7 @@ class MOZ_STACK_CLASS MutableHandle : public js::MutableHandleBase<T, MutableHan
|
||||
DECLARE_NONPOINTER_MUTABLE_ACCESSOR_METHODS(*ptr);
|
||||
|
||||
private:
|
||||
MutableHandle()
|
||||
: ptr{ nullptr }
|
||||
{
|
||||
}
|
||||
MutableHandle() {}
|
||||
DELETE_ASSIGNMENT_OPS(MutableHandle, T);
|
||||
|
||||
T* ptr;
|
||||
|
@ -38,7 +38,7 @@ static void DebugDump(const char* fmt, ...)
|
||||
bool
|
||||
xpc_DumpJSStack(bool showArgs, bool showLocals, bool showThisProps)
|
||||
{
|
||||
JSContext* cx = nsContentUtils::GetCurrentJSContextForThread();
|
||||
JSContext* cx = nsContentUtils::GetCurrentJSContext();
|
||||
if (!cx) {
|
||||
printf("there is no JSContext on the stack!\n");
|
||||
} else if (JS::UniqueChars buf = xpc_PrintJSStack(cx, showArgs, showLocals, showThisProps)) {
|
||||
|
@ -1257,3 +1257,9 @@ XPCJSContext::AfterProcessTask(uint32_t aNewRecursionDepth)
|
||||
|
||||
mozilla::jsipc::AfterProcessTask();
|
||||
}
|
||||
|
||||
bool
|
||||
XPCJSContext::IsSystemCaller() const
|
||||
{
|
||||
return nsContentUtils::IsSystemCaller(Context());
|
||||
}
|
||||
|
@ -413,6 +413,8 @@ public:
|
||||
|
||||
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
|
||||
|
||||
bool IsSystemCaller() const override;
|
||||
|
||||
AutoMarkingPtr** GetAutoRootsAdr() {return &mAutoRoots;}
|
||||
|
||||
nsresult GetPendingResult() { return mPendingResult; }
|
||||
|
@ -5119,16 +5119,22 @@ nsComputedDOMStyle::DoGetOverflow()
|
||||
{
|
||||
const nsStyleDisplay* display = StyleDisplay();
|
||||
|
||||
if (display->mOverflowX != display->mOverflowY) {
|
||||
// No value to return. We can't express this combination of
|
||||
// values as a shorthand.
|
||||
return nullptr;
|
||||
RefPtr<nsROCSSPrimitiveValue> overflowX = new nsROCSSPrimitiveValue;
|
||||
overflowX->SetIdent(
|
||||
nsCSSProps::ValueToKeywordEnum(display->mOverflowX,
|
||||
nsCSSProps::kOverflowKTable));
|
||||
if (display->mOverflowX == display->mOverflowY) {
|
||||
return overflowX.forget();
|
||||
}
|
||||
RefPtr<nsDOMCSSValueList> valueList = GetROCSSValueList(false);
|
||||
valueList->AppendCSSValue(overflowX.forget());
|
||||
|
||||
RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
|
||||
val->SetIdent(nsCSSProps::ValueToKeywordEnum(display->mOverflowX,
|
||||
nsCSSProps::kOverflowKTable));
|
||||
return val.forget();
|
||||
RefPtr<nsROCSSPrimitiveValue> overflowY= new nsROCSSPrimitiveValue;
|
||||
overflowY->SetIdent(
|
||||
nsCSSProps::ValueToKeywordEnum(display->mOverflowY,
|
||||
nsCSSProps::kOverflowKTable));
|
||||
valueList->AppendCSSValue(overflowY.forget());
|
||||
return valueList.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<CSSValue>
|
||||
|
@ -4208,8 +4208,10 @@ var gCSSProperties = {
|
||||
prerequisites: { "display": "block", "contain": "none" },
|
||||
subproperties: [ "overflow-x", "overflow-y" ],
|
||||
initial_values: [ "visible" ],
|
||||
other_values: [ "auto", "scroll", "hidden", "-moz-hidden-unscrollable", "-moz-scrollbars-none" ],
|
||||
invalid_values: []
|
||||
other_values: [ "auto", "scroll", "hidden", "-moz-hidden-unscrollable", "-moz-scrollbars-none",
|
||||
"auto auto", "auto scroll", "hidden scroll", "auto hidden",
|
||||
"-moz-hidden-unscrollable -moz-hidden-unscrollable" ],
|
||||
invalid_values: [ "-moz-hidden-unscrollable -moz-scrollbars-none" ]
|
||||
},
|
||||
"overflow-x": {
|
||||
domProp: "overflowX",
|
||||
|
@ -43,16 +43,16 @@ for (i = 0; i < vals.length; ++i) {
|
||||
$('t').style.overflow = mozVals[0];
|
||||
is($('t').style.getPropertyValue("overflow-y"), "scroll", "Roundtrip");
|
||||
is($('t').style.getPropertyValue("overflow-x"), "hidden", "Roundtrip");
|
||||
is($('t').style.overflow, "", "Shorthand read directly");
|
||||
is(c(), "", "Shorthand computed");
|
||||
is(cval(), null, "Shorthand as CSSValue");
|
||||
is($('t').style.overflow, "hidden scroll", "Shorthand read directly");
|
||||
is(c(), "hidden scroll", "Shorthand computed");
|
||||
isnot(cval(), null, "Shorthand as CSSValue");
|
||||
|
||||
$('t').style.overflow = mozVals[1];
|
||||
is($('t').style.getPropertyValue("overflow-x"), "scroll", "Roundtrip");
|
||||
is($('t').style.getPropertyValue("overflow-y"), "hidden", "Roundtrip");
|
||||
is($('t').style.overflow, "", "Shorthand read directly");
|
||||
is(c(), "", "Shorthand computed");
|
||||
is(cval(), null, "Shorthand as CSSValue");
|
||||
is($('t').style.overflow, "scroll hidden", "Shorthand read directly");
|
||||
is(c(), "scroll hidden", "Shorthand computed");
|
||||
isnot(cval(), null, "Shorthand as CSSValue");
|
||||
|
||||
for (i = 0; i < vals.length; ++i) {
|
||||
for (j = 0; j < vals.length; ++j) {
|
||||
@ -64,7 +64,7 @@ for (i = 0; i < vals.length; ++i) {
|
||||
if (i == j) {
|
||||
is($('t').style.overflow, vals[i], "Shorthand serialization");
|
||||
} else {
|
||||
is($('t').style.overflow, "", "Shorthand serialization");
|
||||
is($('t').style.overflow, vals[i] + " " + vals[j], "Shorthand serialization");
|
||||
}
|
||||
|
||||
// "visible" overflow-x and overflow-y become "auto" in computed style if
|
||||
@ -74,10 +74,12 @@ for (i = 0; i < vals.length; ++i) {
|
||||
isnot(cval(), null, "Shorthand computation as CSSValue");
|
||||
} else if (vals[j] == "visible" && vals[i] == "auto") {
|
||||
is(c(), vals[i], "Shorthand computation");
|
||||
isnot(cval(), null, "Shorthand computation as CSSValue");
|
||||
isnot(cval(), null, "Shorthand computation as CSSValue");
|
||||
} else {
|
||||
is(c(), "", "Shorthand computation");
|
||||
is(cval(), null, "Shorthand computation as CSSValue");
|
||||
let x = vals[i] == "visible" ? "auto" : vals[i];
|
||||
let y = vals[j] == "visible" ? "auto" : vals[j];
|
||||
is(c(), x + " " + y, "Shorthand computation");
|
||||
isnot(cval(), null, "Shorthand computation as CSSValue");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ public:
|
||||
RefPtr<PledgeVoid> p = new PledgeVoid();
|
||||
p->Reject(
|
||||
new dom::MediaStreamError(aWindow,
|
||||
NS_LITERAL_STRING("OverconstrainedError"),
|
||||
MediaStreamError::Name::OverconstrainedError,
|
||||
NS_LITERAL_STRING("")));
|
||||
return p.forget();
|
||||
}
|
||||
|
@ -80,6 +80,8 @@ public class TelemetryCrashPingBuilder extends TelemetryPingBuilder {
|
||||
// Parse the telemetry environment and extract relevant fields from it
|
||||
ExtendedJSONObject env = null;
|
||||
String architecture = null;
|
||||
String displayVersion = null;
|
||||
String platformVersion = null;
|
||||
String xpcomAbi = null;
|
||||
|
||||
try {
|
||||
@ -88,6 +90,8 @@ public class TelemetryCrashPingBuilder extends TelemetryPingBuilder {
|
||||
|
||||
if (build != null) {
|
||||
architecture = build.getString("architecture");
|
||||
displayVersion = build.getString("displayVersion");
|
||||
platformVersion = build.getString("platformVersion");
|
||||
xpcomAbi = build.getString("xpcomAbi");
|
||||
}
|
||||
} catch (NonObjectJSONException | IOException e) {
|
||||
@ -99,7 +103,8 @@ public class TelemetryCrashPingBuilder extends TelemetryPingBuilder {
|
||||
}
|
||||
|
||||
payload.put("payload", createPayloadNode(crashId, annotations));
|
||||
payload.put("application", createApplicationNode(annotations, architecture, xpcomAbi));
|
||||
payload.put("application",
|
||||
createApplicationNode(annotations, architecture, displayVersion, platformVersion, xpcomAbi));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -180,15 +185,16 @@ public class TelemetryCrashPingBuilder extends TelemetryPingBuilder {
|
||||
* @returns A JSON object representing the ping's application node
|
||||
*/
|
||||
private static ExtendedJSONObject createApplicationNode(HashMap<String, String> annotations,
|
||||
String architecture, String xpcomAbi) {
|
||||
String architecture, String displayVersion,
|
||||
String platformVersion, String xpcomAbi) {
|
||||
ExtendedJSONObject node = new ExtendedJSONObject();
|
||||
final String version = annotations.get("Version");
|
||||
|
||||
node.put("vendor", annotations.get("Vendor"));
|
||||
node.put("name", annotations.get("ProductName"));
|
||||
node.put("buildId", annotations.get("BuildID"));
|
||||
node.put("displayVersion", version);
|
||||
node.put("platformVersion", version);
|
||||
node.put("displayVersion", displayVersion);
|
||||
node.put("platformVersion", platformVersion);
|
||||
node.put("version", version);
|
||||
node.put("channel", annotations.get("ReleaseChannel"));
|
||||
|
||||
|
@ -15,6 +15,7 @@ import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import org.mozilla.gecko.GeckoEditableChild;
|
||||
import org.mozilla.gecko.IGeckoEditableChild;
|
||||
import org.mozilla.gecko.IGeckoEditableParent;
|
||||
import org.mozilla.gecko.util.GamepadUtils;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
import org.mozilla.gecko.util.ThreadUtils.AssertBehavior;
|
||||
|
||||
@ -23,6 +24,9 @@ import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemClock;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.Editable;
|
||||
import android.text.InputFilter;
|
||||
import android.text.Selection;
|
||||
@ -32,10 +36,13 @@ import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextPaint;
|
||||
import android.text.TextUtils;
|
||||
import android.text.method.KeyListener;
|
||||
import android.text.method.TextKeyListener;
|
||||
import android.text.style.CharacterStyle;
|
||||
import android.util.Log;
|
||||
import android.view.KeyCharacterMap;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* GeckoEditable implements only some functions of Editable
|
||||
@ -846,7 +853,49 @@ import android.view.KeyEvent;
|
||||
}
|
||||
|
||||
@Override // SessionTextInput.EditableClient
|
||||
public void sendKeyEvent(final KeyEvent event, int action, int metaState) {
|
||||
public void sendKeyEvent(final @Nullable View view, final boolean inputActive, final int action,
|
||||
@NonNull KeyEvent event) {
|
||||
final Editable editable = getEditable();
|
||||
if (editable == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final KeyListener keyListener = TextKeyListener.getInstance();
|
||||
event = translateKey(event.getKeyCode(), event);
|
||||
|
||||
// We only let TextKeyListener do UI things on the UI thread.
|
||||
final View v = ThreadUtils.isOnUiThread() ? view : null;
|
||||
final int keyCode = event.getKeyCode();
|
||||
final boolean handled;
|
||||
|
||||
if (!inputActive || shouldSkipKeyListener(keyCode, event)) {
|
||||
handled = false;
|
||||
} else if (action == KeyEvent.ACTION_DOWN) {
|
||||
setSuppressKeyUp(true);
|
||||
handled = keyListener.onKeyDown(v, editable, keyCode, event);
|
||||
} else if (action == KeyEvent.ACTION_UP) {
|
||||
handled = keyListener.onKeyUp(v, editable, keyCode, event);
|
||||
} else {
|
||||
handled = keyListener.onKeyOther(v, editable, event);
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
sendKeyEvent(event, action, TextKeyListener.getMetaState(editable));
|
||||
}
|
||||
|
||||
if (action == KeyEvent.ACTION_DOWN) {
|
||||
if (!handled) {
|
||||
// Usually, the down key listener call above adjusts meta states for us.
|
||||
// However, if the call didn't handle the event, we have to manually
|
||||
// adjust meta states so the meta states remain consistent.
|
||||
TextKeyListener.adjustMetaAfterKeypress(editable);
|
||||
}
|
||||
setSuppressKeyUp(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendKeyEvent(final @NonNull KeyEvent event, final int action,
|
||||
final int metaState) {
|
||||
if (DEBUG) {
|
||||
assertOnIcThread();
|
||||
Log.d(LOGTAG, "sendKeyEvent(" + event + ", " + action + ", " + metaState + ")");
|
||||
@ -879,6 +928,28 @@ import android.view.KeyEvent;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldSkipKeyListener(final int keyCode, final @NonNull KeyEvent event) {
|
||||
// Preserve enter and tab keys for the browser
|
||||
if (keyCode == KeyEvent.KEYCODE_ENTER ||
|
||||
keyCode == KeyEvent.KEYCODE_TAB) {
|
||||
return true;
|
||||
}
|
||||
// BaseKeyListener returns false even if it handled these keys for us,
|
||||
// so we skip the key listener entirely and handle these ourselves
|
||||
if (keyCode == KeyEvent.KEYCODE_DEL ||
|
||||
keyCode == KeyEvent.KEYCODE_FORWARD_DEL) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private KeyEvent translateKey(final int keyCode, final @NonNull KeyEvent event) {
|
||||
if (GamepadUtils.isSonyXperiaGamepadKeyEvent(event)) {
|
||||
return GamepadUtils.translateSonyXperiaGamepadKeys(keyCode, event);
|
||||
}
|
||||
return event;
|
||||
}
|
||||
|
||||
@Override // SessionTextInput.EditableClient
|
||||
public Editable getEditable() {
|
||||
if (!onIcThread()) {
|
||||
@ -927,8 +998,7 @@ import android.view.KeyEvent;
|
||||
mText.syncShadowText(mListener);
|
||||
}
|
||||
|
||||
@Override // SessionTextInput.EditableClient
|
||||
public void setSuppressKeyUp(boolean suppress) {
|
||||
private void setSuppressKeyUp(boolean suppress) {
|
||||
if (DEBUG) {
|
||||
assertOnIcThread();
|
||||
}
|
||||
@ -1633,5 +1703,100 @@ import android.view.KeyEvent;
|
||||
public String toString() {
|
||||
throw new UnsupportedOperationException("method must be called through mProxy");
|
||||
}
|
||||
|
||||
public boolean onKeyPreIme(final @Nullable View view, final boolean inputActive,
|
||||
final int keyCode, final @NonNull KeyEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean onKeyDown(final @Nullable View view, final boolean inputActive,
|
||||
final int keyCode, final @NonNull KeyEvent event) {
|
||||
return processKey(view, inputActive, KeyEvent.ACTION_DOWN, keyCode, event);
|
||||
}
|
||||
|
||||
public boolean onKeyUp(final @Nullable View view, final boolean inputActive,
|
||||
final int keyCode, final @NonNull KeyEvent event) {
|
||||
return processKey(view, inputActive, KeyEvent.ACTION_UP, keyCode, event);
|
||||
}
|
||||
|
||||
public boolean onKeyMultiple(final @Nullable View view, final boolean inputActive,
|
||||
final int keyCode, int repeatCount,
|
||||
final @NonNull KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_UNKNOWN) {
|
||||
// KEYCODE_UNKNOWN means the characters are in KeyEvent.getCharacters()
|
||||
final String str = event.getCharacters();
|
||||
for (int i = 0; i < str.length(); i++) {
|
||||
final KeyEvent charEvent = getCharKeyEvent(str.charAt(i));
|
||||
if (!processKey(view, inputActive, KeyEvent.ACTION_DOWN,
|
||||
KeyEvent.KEYCODE_UNKNOWN, charEvent) ||
|
||||
!processKey(view, inputActive, KeyEvent.ACTION_UP,
|
||||
KeyEvent.KEYCODE_UNKNOWN, charEvent)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
while ((repeatCount--) > 0) {
|
||||
if (!processKey(view, inputActive, KeyEvent.ACTION_DOWN, keyCode, event) ||
|
||||
!processKey(view, inputActive, KeyEvent.ACTION_UP, keyCode, event)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean onKeyLongPress(final @Nullable View view, final boolean inputActive,
|
||||
final int keyCode, final @NonNull KeyEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a key that represents a given character.
|
||||
*/
|
||||
private static KeyEvent getCharKeyEvent(final char c) {
|
||||
final long time = SystemClock.uptimeMillis();
|
||||
return new KeyEvent(time, time, KeyEvent.ACTION_MULTIPLE,
|
||||
KeyEvent.KEYCODE_UNKNOWN, /* repeat */ 0) {
|
||||
@Override
|
||||
public int getUnicodeChar() {
|
||||
return c;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUnicodeChar(int metaState) {
|
||||
return c;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private boolean processKey(final @Nullable View view, final boolean inputActive,
|
||||
final int action, final int keyCode, final @NonNull KeyEvent event) {
|
||||
if (keyCode > KeyEvent.getMaxKeyCode() || !shouldProcessKey(keyCode, event)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
postToInputConnection(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
sendKeyEvent(view, inputActive, action, event);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean shouldProcessKey(int keyCode, KeyEvent event) {
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_MENU:
|
||||
case KeyEvent.KEYCODE_BACK:
|
||||
case KeyEvent.KEYCODE_VOLUME_UP:
|
||||
case KeyEvent.KEYCODE_VOLUME_DOWN:
|
||||
case KeyEvent.KEYCODE_SEARCH:
|
||||
// ignore HEADSETHOOK to allow hold-for-voice-search to work
|
||||
case KeyEvent.KEYCODE_HEADSETHOOK:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,12 +5,6 @@
|
||||
|
||||
package org.mozilla.geckoview;
|
||||
|
||||
import org.mozilla.gecko.Clipboard;
|
||||
import org.mozilla.gecko.InputMethods;
|
||||
import org.mozilla.gecko.util.ActivityUtils;
|
||||
import org.mozilla.gecko.util.GamepadUtils;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
@ -22,14 +16,12 @@ import android.media.AudioManager;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.SystemClock;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.text.Editable;
|
||||
import android.text.InputType;
|
||||
import android.text.Selection;
|
||||
import android.text.SpannableString;
|
||||
import android.text.Spanned;
|
||||
import android.text.method.KeyListener;
|
||||
import android.text.method.TextKeyListener;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
@ -42,6 +34,11 @@ import android.view.inputmethod.ExtractedTextRequest;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
|
||||
import org.mozilla.gecko.Clipboard;
|
||||
import org.mozilla.gecko.InputMethods;
|
||||
import org.mozilla.gecko.util.ActivityUtils;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
@ -85,7 +82,7 @@ import java.lang.reflect.Proxy;
|
||||
|
||||
// Prevent showSoftInput and hideSoftInput from causing reentrant calls on some devices.
|
||||
private volatile boolean mSoftInputReentrancyGuard;
|
||||
private boolean mShowSoftInputOnFocus = true;
|
||||
/* package */ boolean mShowSoftInputOnFocus = true;
|
||||
|
||||
public static SessionTextInput.Delegate create(
|
||||
final GeckoSession session,
|
||||
@ -287,9 +284,6 @@ import java.lang.reflect.Proxy;
|
||||
}
|
||||
|
||||
private void showSoftInputWithToolbar(final boolean showToolbar) {
|
||||
if (!mShowSoftInputOnFocus) {
|
||||
return;
|
||||
}
|
||||
if (mSoftInputReentrancyGuard) {
|
||||
return;
|
||||
}
|
||||
@ -302,6 +296,10 @@ import java.lang.reflect.Proxy;
|
||||
v.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!mShowSoftInputOnFocus) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (v.hasFocus() && !imm.isActive(v)) {
|
||||
// Marshmallow workaround: The view has focus but it is not the active
|
||||
// view for the input method. (Bug 1211848)
|
||||
@ -773,103 +771,22 @@ import java.lang.reflect.Proxy;
|
||||
return super.setSelection(start, end);
|
||||
}
|
||||
|
||||
/* package */ void sendKeyEvent(final int action, KeyEvent event) {
|
||||
final Editable editable = getEditable();
|
||||
if (editable == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final KeyListener keyListener = TextKeyListener.getInstance();
|
||||
event = translateKey(event.getKeyCode(), event);
|
||||
|
||||
// We only let TextKeyListener do UI things on the UI thread.
|
||||
final View v = ThreadUtils.isOnUiThread() ? getView() : null;
|
||||
final int keyCode = event.getKeyCode();
|
||||
final boolean handled;
|
||||
|
||||
if (shouldSkipKeyListener(keyCode, event)) {
|
||||
handled = false;
|
||||
} else if (action == KeyEvent.ACTION_DOWN) {
|
||||
mEditableClient.setSuppressKeyUp(true);
|
||||
handled = keyListener.onKeyDown(v, editable, keyCode, event);
|
||||
} else if (action == KeyEvent.ACTION_UP) {
|
||||
handled = keyListener.onKeyUp(v, editable, keyCode, event);
|
||||
} else {
|
||||
handled = keyListener.onKeyOther(v, editable, event);
|
||||
}
|
||||
|
||||
if (!handled) {
|
||||
mEditableClient.sendKeyEvent(event, action, TextKeyListener.getMetaState(editable));
|
||||
}
|
||||
|
||||
if (action == KeyEvent.ACTION_DOWN) {
|
||||
if (!handled) {
|
||||
// Usually, the down key listener call above adjusts meta states for us.
|
||||
// However, if the call didn't handle the event, we have to manually
|
||||
// adjust meta states so the meta states remain consistent.
|
||||
TextKeyListener.adjustMetaAfterKeypress(editable);
|
||||
}
|
||||
mEditableClient.setSuppressKeyUp(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sendKeyEvent(KeyEvent event) {
|
||||
sendKeyEvent(event.getAction(), event);
|
||||
public boolean sendKeyEvent(@NonNull KeyEvent event) {
|
||||
event = translateKey(event.getKeyCode(), event);
|
||||
mEditableClient.sendKeyEvent(getView(), isInputActive(), event.getAction(), event);
|
||||
return false; // seems to always return false
|
||||
}
|
||||
|
||||
@Override // SessionTextInput.Delegate
|
||||
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean shouldProcessKey(int keyCode, KeyEvent event) {
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_MENU:
|
||||
case KeyEvent.KEYCODE_BACK:
|
||||
case KeyEvent.KEYCODE_VOLUME_UP:
|
||||
case KeyEvent.KEYCODE_VOLUME_DOWN:
|
||||
case KeyEvent.KEYCODE_SEARCH:
|
||||
// ignore HEADSETHOOK to allow hold-for-voice-search to work
|
||||
case KeyEvent.KEYCODE_HEADSETHOOK:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean shouldSkipKeyListener(int keyCode, KeyEvent event) {
|
||||
if (mIMEState == IME_STATE_DISABLED) {
|
||||
return true;
|
||||
}
|
||||
// Preserve enter and tab keys for the browser
|
||||
if (keyCode == KeyEvent.KEYCODE_ENTER ||
|
||||
keyCode == KeyEvent.KEYCODE_TAB) {
|
||||
return true;
|
||||
}
|
||||
// BaseKeyListener returns false even if it handled these keys for us,
|
||||
// so we skip the key listener entirely and handle these ourselves
|
||||
if (keyCode == KeyEvent.KEYCODE_DEL ||
|
||||
keyCode == KeyEvent.KEYCODE_FORWARD_DEL) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private KeyEvent translateKey(int keyCode, KeyEvent event) {
|
||||
private KeyEvent translateKey(final int keyCode, final @NonNull KeyEvent event) {
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_ENTER:
|
||||
if ((event.getFlags() & KeyEvent.FLAG_EDITOR_ACTION) != 0 &&
|
||||
mIMEActionHint.equalsIgnoreCase("next")) {
|
||||
mIMEActionHint.equalsIgnoreCase("next")) {
|
||||
return new KeyEvent(event.getAction(), KeyEvent.KEYCODE_TAB);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (GamepadUtils.isSonyXperiaGamepadKeyEvent(event)) {
|
||||
return GamepadUtils.translateSonyXperiaGamepadKeys(keyCode, event);
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
@ -904,92 +821,6 @@ import java.lang.reflect.Proxy;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean processKey(final int action, final int keyCode, final KeyEvent event) {
|
||||
|
||||
if (keyCode > KeyEvent.getMaxKeyCode() || !shouldProcessKey(keyCode, event)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mEditableClient.postToInputConnection(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
sendKeyEvent(action, event);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override // SessionTextInput.Delegate
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
return processKey(KeyEvent.ACTION_DOWN, keyCode, event);
|
||||
}
|
||||
|
||||
@Override // SessionTextInput.Delegate
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||
return processKey(KeyEvent.ACTION_UP, keyCode, event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a key that represents a given character.
|
||||
*/
|
||||
private KeyEvent getCharKeyEvent(final char c) {
|
||||
final long time = SystemClock.uptimeMillis();
|
||||
return new KeyEvent(time, time, KeyEvent.ACTION_MULTIPLE,
|
||||
KeyEvent.KEYCODE_UNKNOWN, /* repeat */ 0) {
|
||||
@Override
|
||||
public int getUnicodeChar() {
|
||||
return c;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUnicodeChar(int metaState) {
|
||||
return c;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override // SessionTextInput.Delegate
|
||||
public boolean onKeyMultiple(int keyCode, int repeatCount, final KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_UNKNOWN) {
|
||||
// KEYCODE_UNKNOWN means the characters are in KeyEvent.getCharacters()
|
||||
final String str = event.getCharacters();
|
||||
for (int i = 0; i < str.length(); i++) {
|
||||
final KeyEvent charEvent = getCharKeyEvent(str.charAt(i));
|
||||
if (!processKey(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_UNKNOWN, charEvent) ||
|
||||
!processKey(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_UNKNOWN, charEvent)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
while ((repeatCount--) != 0) {
|
||||
if (!processKey(KeyEvent.ACTION_DOWN, keyCode, event) ||
|
||||
!processKey(KeyEvent.ACTION_UP, keyCode, event)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override // SessionTextInput.Delegate
|
||||
public boolean onKeyLongPress(int keyCode, KeyEvent event) {
|
||||
View v = getView();
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_MENU:
|
||||
if (!mShowSoftInputOnFocus) {
|
||||
return false;
|
||||
}
|
||||
InputMethodManager imm = getInputMethodManager();
|
||||
imm.toggleSoftInputFromWindow(v.getWindowToken(),
|
||||
InputMethodManager.SHOW_FORCED, 0);
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override // SessionTextInput.Delegate
|
||||
public synchronized boolean isInputActive() {
|
||||
// Make sure this picks up PASSWORD state as well.
|
||||
@ -998,6 +829,7 @@ import java.lang.reflect.Proxy;
|
||||
|
||||
@Override // SessionTextInput.Delegate
|
||||
public void setShowSoftInputOnFocus(final boolean showSoftInputOnFocus) {
|
||||
ThreadUtils.assertOnUiThread();
|
||||
mShowSoftInputOnFocus = showSoftInputOnFocus;
|
||||
}
|
||||
|
||||
|
@ -323,7 +323,9 @@ public class GeckoView extends FrameLayout {
|
||||
mSession.open(mRuntime);
|
||||
}
|
||||
|
||||
mSession.getTextInput().setView(this);
|
||||
if (mSession.getTextInput().getView() == null) {
|
||||
mSession.getTextInput().setView(this);
|
||||
}
|
||||
|
||||
super.onAttachedToWindow();
|
||||
}
|
||||
@ -332,16 +334,16 @@ public class GeckoView extends FrameLayout {
|
||||
public void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
|
||||
if (mSession != null) {
|
||||
mSession.getTextInput().setView(null);
|
||||
}
|
||||
|
||||
if (mStateSaved) {
|
||||
// If we saved state earlier, we don't want to close the window.
|
||||
if (mSession == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mSession != null && mSession.isOpen()) {
|
||||
if (mSession.getTextInput().getView() == this) {
|
||||
mSession.getTextInput().setView(null);
|
||||
}
|
||||
|
||||
// If we saved state earlier, we don't want to close the window.
|
||||
if (!mStateSaved && mSession.isOpen()) {
|
||||
mSession.close();
|
||||
}
|
||||
}
|
||||
|
@ -34,11 +34,6 @@ public final class SessionTextInput {
|
||||
View getView();
|
||||
Handler getHandler(Handler defHandler);
|
||||
InputConnection onCreateInputConnection(EditorInfo attrs);
|
||||
boolean onKeyPreIme(int keyCode, KeyEvent event);
|
||||
boolean onKeyDown(int keyCode, KeyEvent event);
|
||||
boolean onKeyUp(int keyCode, KeyEvent event);
|
||||
boolean onKeyLongPress(int keyCode, KeyEvent event);
|
||||
boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event);
|
||||
boolean isInputActive();
|
||||
void setShowSoftInputOnFocus(boolean showSoftInputOnFocus);
|
||||
}
|
||||
@ -55,12 +50,12 @@ public final class SessionTextInput {
|
||||
// ENDT_MONITOR stops the monitor for composing character rects.
|
||||
@WrapForJNI final int END_MONITOR = 3;
|
||||
|
||||
void sendKeyEvent(KeyEvent event, int action, int metaState);
|
||||
void sendKeyEvent(@Nullable View view, boolean inputActive, int action,
|
||||
@NonNull KeyEvent event);
|
||||
Editable getEditable();
|
||||
void setBatchMode(boolean isBatchMode);
|
||||
void setSuppressKeyUp(boolean suppress);
|
||||
Handler setInputConnectionHandler(Handler handler);
|
||||
void postToInputConnection(Runnable runnable);
|
||||
Handler setInputConnectionHandler(@NonNull Handler handler);
|
||||
void postToInputConnection(@NonNull Runnable runnable);
|
||||
void requestCursorUpdates(int requestMode);
|
||||
}
|
||||
|
||||
@ -135,7 +130,7 @@ public final class SessionTextInput {
|
||||
* @param defHandler Handler returned by the system {@code getHandler} implementation.
|
||||
* @return Handler to return to the system through {@code getHandler}.
|
||||
*/
|
||||
public @NonNull Handler getHandler(final @NonNull Handler defHandler) {
|
||||
public synchronized @NonNull Handler getHandler(final @NonNull Handler defHandler) {
|
||||
// May be called on any thread.
|
||||
if (mInputConnection != null) {
|
||||
return mInputConnection.getHandler(defHandler);
|
||||
@ -143,21 +138,6 @@ public final class SessionTextInput {
|
||||
return defHandler;
|
||||
}
|
||||
|
||||
private synchronized boolean ensureInputConnection() {
|
||||
if (!mQueue.isReady()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mInputConnection == null) {
|
||||
mInputConnection = GeckoInputConnection.create(mSession,
|
||||
/* view */ null,
|
||||
mEditable);
|
||||
mInputConnection.setShowSoftInputOnFocus(mShowSoftInputOnFocus);
|
||||
mEditable.setListener((EditableListener) mInputConnection);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current View for text input.
|
||||
*
|
||||
@ -194,9 +174,10 @@ public final class SessionTextInput {
|
||||
* @param attrs EditorInfo instance to be filled on return.
|
||||
* @return InputConnection instance or null if input method is not active.
|
||||
*/
|
||||
public @Nullable InputConnection onCreateInputConnection(final @NonNull EditorInfo attrs) {
|
||||
public synchronized @Nullable InputConnection onCreateInputConnection(
|
||||
final @NonNull EditorInfo attrs) {
|
||||
// May be called on any thread.
|
||||
if (!ensureInputConnection()) {
|
||||
if (!mQueue.isReady() || mInputConnection == null) {
|
||||
return null;
|
||||
}
|
||||
return mInputConnection.onCreateInputConnection(attrs);
|
||||
@ -211,10 +192,7 @@ public final class SessionTextInput {
|
||||
*/
|
||||
public boolean onKeyPreIme(final int keyCode, final @NonNull KeyEvent event) {
|
||||
ThreadUtils.assertOnUiThread();
|
||||
if (!ensureInputConnection()) {
|
||||
return false;
|
||||
}
|
||||
return mInputConnection.onKeyPreIme(keyCode, event);
|
||||
return mEditable.onKeyPreIme(getView(), isInputActive(), keyCode, event);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -226,10 +204,7 @@ public final class SessionTextInput {
|
||||
*/
|
||||
public boolean onKeyDown(final int keyCode, final @NonNull KeyEvent event) {
|
||||
ThreadUtils.assertOnUiThread();
|
||||
if (!ensureInputConnection()) {
|
||||
return false;
|
||||
}
|
||||
return mInputConnection.onKeyDown(keyCode, event);
|
||||
return mEditable.onKeyDown(getView(), isInputActive(), keyCode, event);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -241,10 +216,7 @@ public final class SessionTextInput {
|
||||
*/
|
||||
public boolean onKeyUp(final int keyCode, final @NonNull KeyEvent event) {
|
||||
ThreadUtils.assertOnUiThread();
|
||||
if (!ensureInputConnection()) {
|
||||
return false;
|
||||
}
|
||||
return mInputConnection.onKeyUp(keyCode, event);
|
||||
return mEditable.onKeyUp(getView(), isInputActive(), keyCode, event);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -256,10 +228,7 @@ public final class SessionTextInput {
|
||||
*/
|
||||
public boolean onKeyLongPress(final int keyCode, final @NonNull KeyEvent event) {
|
||||
ThreadUtils.assertOnUiThread();
|
||||
if (!ensureInputConnection()) {
|
||||
return false;
|
||||
}
|
||||
return mInputConnection.onKeyLongPress(keyCode, event);
|
||||
return mEditable.onKeyLongPress(getView(), isInputActive(), keyCode, event);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -273,10 +242,7 @@ public final class SessionTextInput {
|
||||
public boolean onKeyMultiple(final int keyCode, final int repeatCount,
|
||||
final @NonNull KeyEvent event) {
|
||||
ThreadUtils.assertOnUiThread();
|
||||
if (!ensureInputConnection()) {
|
||||
return false;
|
||||
}
|
||||
return mInputConnection.onKeyMultiple(keyCode, repeatCount, event);
|
||||
return mEditable.onKeyMultiple(getView(), isInputActive(), keyCode, repeatCount, event);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -291,14 +257,26 @@ public final class SessionTextInput {
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent soft input when gaining focus
|
||||
* Set whether to show the soft keyboard when an input field gains focus.
|
||||
*
|
||||
* @param showSoftInputOnFocus Set whether soft input is shown when an input field gains focus.
|
||||
* @param showSoftInputOnFocus True to show soft input on input focus.
|
||||
*/
|
||||
synchronized public void setShowSoftInputOnFocus(boolean showSoftInputOnFocus) {
|
||||
public void setShowSoftInputOnFocus(final boolean showSoftInputOnFocus) {
|
||||
ThreadUtils.assertOnUiThread();
|
||||
|
||||
mShowSoftInputOnFocus = showSoftInputOnFocus;
|
||||
if (mInputConnection != null) {
|
||||
mInputConnection.setShowSoftInputOnFocus(showSoftInputOnFocus);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether to show the soft keyboard when an input field gains focus.
|
||||
*
|
||||
* @return True if soft input is shown on input focus.
|
||||
*/
|
||||
public boolean getShowSoftInputOnFocus() {
|
||||
ThreadUtils.assertOnUiThread();
|
||||
return mShowSoftInputOnFocus;
|
||||
}
|
||||
}
|
||||
|
@ -557,7 +557,8 @@
|
||||
"oc": {
|
||||
"platforms": [
|
||||
"android",
|
||||
"android-api-16"
|
||||
"android-api-16",
|
||||
"android-multilocale"
|
||||
],
|
||||
"revision": "default"
|
||||
},
|
||||
|
@ -276,3 +276,4 @@ There are lots of mach commands you can use. You can list them with `./mach
|
||||
|
||||
|
||||
The generated documentation can be found on http://doc.servo.org/servo/index.html
|
||||
|
||||
|
@ -4,8 +4,11 @@
|
||||
|
||||
<%namespace name="helpers" file="/helpers.mako.rs" />
|
||||
|
||||
<%helpers:shorthand name="overflow" sub_properties="overflow-x overflow-y"
|
||||
spec="https://drafts.csswg.org/css-overflow/#propdef-overflow">
|
||||
<%helpers:shorthand
|
||||
name="overflow"
|
||||
sub_properties="overflow-x overflow-y"
|
||||
spec="https://drafts.csswg.org/css-overflow/#propdef-overflow"
|
||||
>
|
||||
use properties::longhands::overflow_x::parse as parse_overflow;
|
||||
% if product == "gecko":
|
||||
use properties::longhands::overflow_x::SpecifiedValue;
|
||||
@ -42,20 +45,23 @@
|
||||
return moz_kw_found
|
||||
}
|
||||
% endif
|
||||
let overflow = parse_overflow(context, input)?;
|
||||
let overflow_x = parse_overflow(context, input)?;
|
||||
let overflow_y =
|
||||
input.try(|i| parse_overflow(context, i)).unwrap_or(overflow_x);
|
||||
Ok(expanded! {
|
||||
overflow_x: overflow,
|
||||
overflow_y: overflow,
|
||||
overflow_x: overflow_x,
|
||||
overflow_y: overflow_y,
|
||||
})
|
||||
}
|
||||
|
||||
impl<'a> ToCss for LonghandsToSerialize<'a> {
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
|
||||
if self.overflow_x == self.overflow_y {
|
||||
self.overflow_x.to_css(dest)
|
||||
} else {
|
||||
Ok(())
|
||||
self.overflow_x.to_css(dest)?;
|
||||
if self.overflow_x != self.overflow_y {
|
||||
dest.write_char(' ')?;
|
||||
self.overflow_y.to_css(dest)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
</%helpers:shorthand>
|
||||
|
@ -113,6 +113,8 @@ linux-qr-tests:
|
||||
- mochitest-media
|
||||
- mochitest-webgl
|
||||
- reftest
|
||||
- test-verify
|
||||
- test-verify-wpt
|
||||
- xpcshell
|
||||
|
||||
linux-talos-profiling:
|
||||
@ -141,6 +143,7 @@ windows-qr-tests:
|
||||
- mochitest-media
|
||||
- mochitest-webgl
|
||||
- reftest
|
||||
- test-verify
|
||||
|
||||
windows-qr-talos:
|
||||
- talos-chrome
|
||||
|
@ -7,10 +7,20 @@ All notable changes to this program is documented in this file.
|
||||
Unreleased
|
||||
----------
|
||||
|
||||
Note that with geckodriver 'next' the following versions are recommended:
|
||||
- Firefox 56.0 (and greater)
|
||||
- Selenium 3.11 (and greater)
|
||||
|
||||
### Added
|
||||
|
||||
- Support for the new chrome element identifier in Marionette.
|
||||
|
||||
### Changed
|
||||
|
||||
- Updated mapping of all supported commands to the new prefixed commands
|
||||
in Marionette, whereby all WebDriver specific commands make use of the
|
||||
`WebDriver:` prefix.
|
||||
|
||||
|
||||
0.20.1 (2018-04-06)
|
||||
-------------------
|
||||
|
@ -1011,8 +1011,107 @@ impl MarionetteCommand {
|
||||
msg: &WebDriverMessage<GeckoExtensionRoute>)
|
||||
-> WebDriverResult<MarionetteCommand> {
|
||||
let (opt_name, opt_parameters) = match msg.command {
|
||||
Status => panic!("Got status command that should already have been handled"),
|
||||
AcceptAlert => {
|
||||
// Needs to be updated to "WebDriver:AcceptAlert" for Firefox 63
|
||||
(Some("WebDriver:AcceptDialog"), None)
|
||||
}
|
||||
AddCookie(ref x) => (Some("WebDriver:AddCookie"), Some(x.to_marionette())),
|
||||
CloseWindow => (Some("WebDriver:CloseWindow"), None),
|
||||
DeleteCookie(ref x) => {
|
||||
let mut data = BTreeMap::new();
|
||||
data.insert("name".to_string(), x.to_json());
|
||||
(Some("WebDriver:DeleteCookie"), Some(Ok(data)))
|
||||
}
|
||||
DeleteCookies => (Some("WebDriver:DeleteAllCookies"), None),
|
||||
DeleteSession => {
|
||||
let mut body = BTreeMap::new();
|
||||
body.insert("flags".to_owned(), vec!["eForceQuit".to_json()].to_json());
|
||||
(Some("Marionette:Quit"), Some(Ok(body)))
|
||||
}
|
||||
DismissAlert => (Some("WebDriver:DismissAlert"), None),
|
||||
ElementClear(ref x) => (Some("WebDriver:ElementClear"), Some(x.to_marionette())),
|
||||
ElementClick(ref x) => (Some("WebDriver:ElementClick"), Some(x.to_marionette())),
|
||||
ElementSendKeys(ref e, ref x) => {
|
||||
let mut data = BTreeMap::new();
|
||||
data.insert("id".to_string(), e.id.to_json());
|
||||
data.insert("text".to_string(), x.text.to_json());
|
||||
data.insert(
|
||||
"value".to_string(),
|
||||
x.text
|
||||
.chars()
|
||||
.map(|x| x.to_string())
|
||||
.collect::<Vec<String>>()
|
||||
.to_json(),
|
||||
);
|
||||
(Some("WebDriver:ElementSendKeys"), Some(Ok(data)))
|
||||
}
|
||||
ElementTap(ref x) => (Some("singleTap"), Some(x.to_marionette())),
|
||||
ExecuteAsyncScript(ref x) => (
|
||||
Some("WebDriver:ExecuteAsyncScript"),
|
||||
Some(x.to_marionette()),
|
||||
),
|
||||
ExecuteScript(ref x) => (Some("WebDriver:ExecuteScript"), Some(x.to_marionette())),
|
||||
FindElement(ref x) => (Some("WebDriver:FindElement"), Some(x.to_marionette())),
|
||||
FindElementElement(ref e, ref x) => {
|
||||
let mut data = try!(x.to_marionette());
|
||||
data.insert("element".to_string(), e.id.to_json());
|
||||
(Some("WebDriver:FindElement"), Some(Ok(data)))
|
||||
}
|
||||
FindElements(ref x) => (Some("WebDriver:FindElements"), Some(x.to_marionette())),
|
||||
FindElementElements(ref e, ref x) => {
|
||||
let mut data = try!(x.to_marionette());
|
||||
data.insert("element".to_string(), e.id.to_json());
|
||||
(Some("WebDriver:FindElements"), Some(Ok(data)))
|
||||
}
|
||||
FullscreenWindow => (Some("WebDriver:FullscreenWindow"), None),
|
||||
Get(ref x) => (Some("WebDriver:Navigate"), Some(x.to_marionette())),
|
||||
GetAlertText => (Some("WebDriver:GetAlertText"), None),
|
||||
GetActiveElement => (Some("WebDriver:GetActiveElement"), None),
|
||||
GetCookies | GetNamedCookie(_) => (Some("WebDriver:GetCookies"), None),
|
||||
GetCurrentUrl => (Some("WebDriver:GetCurrentURL"), None),
|
||||
GetCSSValue(ref e, ref x) => {
|
||||
let mut data = BTreeMap::new();
|
||||
data.insert("id".to_string(), e.id.to_json());
|
||||
data.insert("propertyName".to_string(), x.to_json());
|
||||
(Some("WebDriver:GetElementCSSValue"), Some(Ok(data)))
|
||||
}
|
||||
GetElementAttribute(ref e, ref x) => {
|
||||
let mut data = BTreeMap::new();
|
||||
data.insert("id".to_string(), e.id.to_json());
|
||||
data.insert("name".to_string(), x.to_json());
|
||||
(Some("WebDriver:GetElementAttribute"), Some(Ok(data)))
|
||||
}
|
||||
GetElementProperty(ref e, ref x) => {
|
||||
let mut data = BTreeMap::new();
|
||||
data.insert("id".to_string(), e.id.to_json());
|
||||
data.insert("name".to_string(), x.to_json());
|
||||
(Some("WebDriver:GetElementProperty"), Some(Ok(data)))
|
||||
}
|
||||
GetElementRect(ref x) => (Some("WebDriver:GetElementRect"), Some(x.to_marionette())),
|
||||
GetElementTagName(ref x) => {
|
||||
(Some("WebDriver:GetElementTagName"), Some(x.to_marionette()))
|
||||
}
|
||||
GetElementText(ref x) => (Some("WebDriver:GetElementText"), Some(x.to_marionette())),
|
||||
GetPageSource => (Some("WebDriver:GetPageSource"), None),
|
||||
GetTimeouts => (Some("WebDriver:GetTimeouts"), None),
|
||||
GetTitle => (Some("WebDriver:GetTitle"), None),
|
||||
GetWindowHandle => (Some("WebDriver:GetWindowHandle"), None),
|
||||
GetWindowHandles => (Some("WebDriver:GetWindowHandles"), None),
|
||||
GetWindowRect => (Some("WebDriver:GetWindowRect"), None),
|
||||
GoBack => (Some("WebDriver:Back"), None),
|
||||
GoForward => (Some("WebDriver::Forward"), None),
|
||||
IsDisplayed(ref x) => (
|
||||
Some("WebDriver:IsElementDisplayed"),
|
||||
Some(x.to_marionette()),
|
||||
),
|
||||
IsEnabled(ref x) => (Some("WebDriver:IsElementEnabled"), Some(x.to_marionette())),
|
||||
IsSelected(ref x) => (Some("WebDriver:IsElementSelected"), Some(x.to_marionette())),
|
||||
MaximizeWindow => (Some("WebDriver:MaximizeWindow"), None),
|
||||
MinimizeWindow => (Some("WebDriver:MinimizeWindow"), None),
|
||||
NewSession(_) => {
|
||||
let caps = capabilities.expect("Tried to create new session without processing capabilities");
|
||||
let caps = capabilities
|
||||
.expect("Tried to create new session without processing capabilities");
|
||||
|
||||
let mut data = BTreeMap::new();
|
||||
for (k, v) in caps.iter() {
|
||||
@ -1024,152 +1123,67 @@ impl MarionetteCommand {
|
||||
legacy_caps.insert("desiredCapabilities".to_string(), caps.to_json());
|
||||
data.insert("capabilities".to_string(), legacy_caps.to_json());
|
||||
|
||||
(Some("newSession"), Some(Ok(data)))
|
||||
},
|
||||
DeleteSession => {
|
||||
let mut body = BTreeMap::new();
|
||||
body.insert("flags".to_owned(), vec!["eForceQuit".to_json()].to_json());
|
||||
(Some("quit"), Some(Ok(body)))
|
||||
},
|
||||
Status => panic!("Got status command that should already have been handled"),
|
||||
Get(ref x) => (Some("get"), Some(x.to_marionette())),
|
||||
GetCurrentUrl => (Some("getCurrentUrl"), None),
|
||||
GoBack => (Some("goBack"), None),
|
||||
GoForward => (Some("goForward"), None),
|
||||
Refresh => (Some("refresh"), None),
|
||||
GetTitle => (Some("getTitle"), None),
|
||||
GetPageSource => (Some("getPageSource"), None),
|
||||
GetWindowHandle => (Some("getWindowHandle"), None),
|
||||
GetWindowHandles => (Some("getWindowHandles"), None),
|
||||
CloseWindow => (Some("close"), None),
|
||||
GetTimeouts => (Some("getTimeouts"), None),
|
||||
SetTimeouts(ref x) => (Some("setTimeouts"), Some(x.to_marionette())),
|
||||
SetWindowRect(ref x) => (Some("setWindowRect"), Some(x.to_marionette())),
|
||||
GetWindowRect => (Some("getWindowRect"), None),
|
||||
MinimizeWindow => (Some("WebDriver:MinimizeWindow"), None),
|
||||
MaximizeWindow => (Some("maximizeWindow"), None),
|
||||
FullscreenWindow => (Some("fullscreen"), None),
|
||||
SwitchToWindow(ref x) => (Some("switchToWindow"), Some(x.to_marionette())),
|
||||
SwitchToFrame(ref x) => (Some("switchToFrame"), Some(x.to_marionette())),
|
||||
SwitchToParentFrame => (Some("switchToParentFrame"), None),
|
||||
FindElement(ref x) => (Some("findElement"), Some(x.to_marionette())),
|
||||
FindElements(ref x) => (Some("findElements"), Some(x.to_marionette())),
|
||||
FindElementElement(ref e, ref x) => {
|
||||
let mut data = try!(x.to_marionette());
|
||||
data.insert("element".to_string(), e.id.to_json());
|
||||
(Some("findElement"), Some(Ok(data)))
|
||||
},
|
||||
FindElementElements(ref e, ref x) => {
|
||||
let mut data = try!(x.to_marionette());
|
||||
data.insert("element".to_string(), e.id.to_json());
|
||||
(Some("findElements"), Some(Ok(data)))
|
||||
},
|
||||
GetActiveElement => (Some("getActiveElement"), None),
|
||||
IsDisplayed(ref x) => (Some("isElementDisplayed"), Some(x.to_marionette())),
|
||||
IsSelected(ref x) => (Some("isElementSelected"), Some(x.to_marionette())),
|
||||
GetElementAttribute(ref e, ref x) => {
|
||||
let mut data = BTreeMap::new();
|
||||
data.insert("id".to_string(), e.id.to_json());
|
||||
data.insert("name".to_string(), x.to_json());
|
||||
(Some("getElementAttribute"), Some(Ok(data)))
|
||||
},
|
||||
GetElementProperty(ref e, ref x) => {
|
||||
let mut data = BTreeMap::new();
|
||||
data.insert("id".to_string(), e.id.to_json());
|
||||
data.insert("name".to_string(), x.to_json());
|
||||
(Some("getElementProperty"), Some(Ok(data)))
|
||||
},
|
||||
GetCSSValue(ref e, ref x) => {
|
||||
let mut data = BTreeMap::new();
|
||||
data.insert("id".to_string(), e.id.to_json());
|
||||
data.insert("propertyName".to_string(), x.to_json());
|
||||
(Some("getElementValueOfCssProperty"), Some(Ok(data)))
|
||||
},
|
||||
GetElementText(ref x) => (Some("getElementText"), Some(x.to_marionette())),
|
||||
GetElementTagName(ref x) => (Some("getElementTagName"), Some(x.to_marionette())),
|
||||
GetElementRect(ref x) => (Some("getElementRect"), Some(x.to_marionette())),
|
||||
IsEnabled(ref x) => (Some("isElementEnabled"), Some(x.to_marionette())),
|
||||
PerformActions(ref x) => (Some("performActions"), Some(x.to_marionette())),
|
||||
ReleaseActions => (Some("releaseActions"), None),
|
||||
ElementClick(ref x) => (Some("clickElement"), Some(x.to_marionette())),
|
||||
ElementTap(ref x) => (Some("singleTap"), Some(x.to_marionette())),
|
||||
ElementClear(ref x) => (Some("clearElement"), Some(x.to_marionette())),
|
||||
ElementSendKeys(ref e, ref x) => {
|
||||
let mut data = BTreeMap::new();
|
||||
data.insert("id".to_string(), e.id.to_json());
|
||||
data.insert("text".to_string(), x.text.to_json());
|
||||
data.insert("value".to_string(),
|
||||
x.text
|
||||
.chars()
|
||||
.map(|x| x.to_string())
|
||||
.collect::<Vec<String>>()
|
||||
.to_json());
|
||||
(Some("sendKeysToElement"), Some(Ok(data)))
|
||||
},
|
||||
ExecuteScript(ref x) => (Some("executeScript"), Some(x.to_marionette())),
|
||||
ExecuteAsyncScript(ref x) => (Some("executeAsyncScript"), Some(x.to_marionette())),
|
||||
GetCookies | GetNamedCookie(_) => (Some("getCookies"), None),
|
||||
DeleteCookies => (Some("deleteAllCookies"), None),
|
||||
DeleteCookie(ref x) => {
|
||||
let mut data = BTreeMap::new();
|
||||
data.insert("name".to_string(), x.to_json());
|
||||
(Some("deleteCookie"), Some(Ok(data)))
|
||||
},
|
||||
AddCookie(ref x) => (Some("addCookie"), Some(x.to_marionette())),
|
||||
DismissAlert => (Some("dismissDialog"), None),
|
||||
AcceptAlert => (Some("acceptDialog"), None),
|
||||
GetAlertText => (Some("getTextFromDialog"), None),
|
||||
(Some("WebDriver:NewSession"), Some(Ok(data)))
|
||||
}
|
||||
PerformActions(ref x) => (Some("WebDriver:PerformActions"), Some(x.to_marionette())),
|
||||
Refresh => (Some("WebDriver:Refresh"), None),
|
||||
ReleaseActions => (Some("WebDriver:ReleaseActions"), None),
|
||||
SendAlertText(ref x) => {
|
||||
let mut data = BTreeMap::new();
|
||||
data.insert("text".to_string(), x.text.to_json());
|
||||
data.insert("value".to_string(),
|
||||
x.text
|
||||
.chars()
|
||||
.map(|x| x.to_string())
|
||||
.collect::<Vec<String>>()
|
||||
.to_json());
|
||||
(Some("sendKeysToDialog"), Some(Ok(data)))
|
||||
},
|
||||
TakeScreenshot => {
|
||||
let mut data = BTreeMap::new();
|
||||
data.insert("id".to_string(), Json::Null);
|
||||
data.insert("highlights".to_string(), Json::Array(vec![]));
|
||||
data.insert("full".to_string(), Json::Boolean(false));
|
||||
(Some("takeScreenshot"), Some(Ok(data)))
|
||||
},
|
||||
data.insert(
|
||||
"value".to_string(),
|
||||
x.text
|
||||
.chars()
|
||||
.map(|x| x.to_string())
|
||||
.collect::<Vec<String>>()
|
||||
.to_json(),
|
||||
);
|
||||
(Some("WebDriver:SendAlertText"), Some(Ok(data)))
|
||||
}
|
||||
SetTimeouts(ref x) => (Some("WebDriver:SetTimeouts"), Some(x.to_marionette())),
|
||||
SetWindowRect(ref x) => (Some("WebDriver:SetWindowRect"), Some(x.to_marionette())),
|
||||
SwitchToFrame(ref x) => (Some("WebDriver:SwitchToFrame"), Some(x.to_marionette())),
|
||||
SwitchToParentFrame => (Some("WebDriver:SwitchToParentFrame"), None),
|
||||
SwitchToWindow(ref x) => (Some("WebDriver:SwitchToWindow"), Some(x.to_marionette())),
|
||||
TakeElementScreenshot(ref e) => {
|
||||
let mut data = BTreeMap::new();
|
||||
data.insert("id".to_string(), e.id.to_json());
|
||||
data.insert("highlights".to_string(), Json::Array(vec![]));
|
||||
data.insert("full".to_string(), Json::Boolean(false));
|
||||
(Some("takeScreenshot"), Some(Ok(data)))
|
||||
},
|
||||
Extension(ref extension) => {
|
||||
match extension {
|
||||
&GeckoExtensionCommand::GetContext => (Some("getContext"), None),
|
||||
&GeckoExtensionCommand::SetContext(ref x) => {
|
||||
(Some("setContext"), Some(x.to_marionette()))
|
||||
},
|
||||
&GeckoExtensionCommand::XblAnonymousChildren(ref e) => {
|
||||
let mut data = BTreeMap::new();
|
||||
data.insert("using".to_owned(), "anon".to_json());
|
||||
data.insert("value".to_owned(), Json::Null);
|
||||
data.insert("element".to_string(), e.id.to_json());
|
||||
(Some("findElements"), Some(Ok(data)))
|
||||
},
|
||||
&GeckoExtensionCommand::XblAnonymousByAttribute(ref e, ref x) => {
|
||||
let mut data = try!(x.to_marionette());
|
||||
data.insert("element".to_string(), e.id.to_json());
|
||||
(Some("findElement"), Some(Ok(data)))
|
||||
},
|
||||
&GeckoExtensionCommand::InstallAddon(ref x) => {
|
||||
(Some("addon:install"), Some(x.to_marionette()))
|
||||
},
|
||||
&GeckoExtensionCommand::UninstallAddon(ref x) => {
|
||||
(Some("addon:uninstall"), Some(x.to_marionette()))
|
||||
}
|
||||
}
|
||||
(Some("WebDriver:TakeScreenshot"), Some(Ok(data)))
|
||||
}
|
||||
TakeScreenshot => {
|
||||
let mut data = BTreeMap::new();
|
||||
data.insert("id".to_string(), Json::Null);
|
||||
data.insert("highlights".to_string(), Json::Array(vec![]));
|
||||
data.insert("full".to_string(), Json::Boolean(false));
|
||||
(Some("WebDriver:TakeScreenshot"), Some(Ok(data)))
|
||||
}
|
||||
Extension(ref extension) => match extension {
|
||||
&GeckoExtensionCommand::GetContext => (Some("Marionette:GetContext"), None),
|
||||
&GeckoExtensionCommand::InstallAddon(ref x) => {
|
||||
(Some("Addon:Install"), Some(x.to_marionette()))
|
||||
}
|
||||
&GeckoExtensionCommand::SetContext(ref x) => {
|
||||
(Some("Marionette:SetContext"), Some(x.to_marionette()))
|
||||
}
|
||||
&GeckoExtensionCommand::UninstallAddon(ref x) => {
|
||||
(Some("Addon:Uninstall"), Some(x.to_marionette()))
|
||||
}
|
||||
&GeckoExtensionCommand::XblAnonymousByAttribute(ref e, ref x) => {
|
||||
let mut data = try!(x.to_marionette());
|
||||
data.insert("element".to_string(), e.id.to_json());
|
||||
(Some("WebDriver:FindElement"), Some(Ok(data)))
|
||||
}
|
||||
&GeckoExtensionCommand::XblAnonymousChildren(ref e) => {
|
||||
let mut data = BTreeMap::new();
|
||||
data.insert("using".to_owned(), "anon".to_json());
|
||||
data.insert("value".to_owned(), Json::Null);
|
||||
data.insert("element".to_string(), e.id.to_json());
|
||||
(Some("WebDriver:FindElements"), Some(Ok(data)))
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let name = try_opt!(opt_name,
|
||||
|
@ -7,7 +7,7 @@ from __future__ import absolute_import
|
||||
from setuptools import setup
|
||||
|
||||
PACKAGE_NAME = 'mozprofile'
|
||||
PACKAGE_VERSION = '0.29'
|
||||
PACKAGE_VERSION = '1.0.0'
|
||||
|
||||
deps = ['mozfile >= 1.0',
|
||||
'mozlog >= 3.0',
|
||||
|
@ -59,18 +59,19 @@ virtualenv_config_options = [
|
||||
[["--find-links"], {
|
||||
"action": "extend",
|
||||
"dest": "find_links",
|
||||
"default": ["http://pypi.pub.build.mozilla.org/pub"],
|
||||
"help": "URL to look for packages at"
|
||||
}],
|
||||
[["--pip-index"], {
|
||||
"action": "store_true",
|
||||
"default": True,
|
||||
"default": False,
|
||||
"dest": "pip_index",
|
||||
"help": "Use pip indexes (default)"
|
||||
"help": "Use pip indexes"
|
||||
}],
|
||||
[["--no-pip-index"], {
|
||||
"action": "store_false",
|
||||
"dest": "pip_index",
|
||||
"help": "Don't use pip indexes"
|
||||
"help": "Don't use pip indexes (default)"
|
||||
}],
|
||||
]
|
||||
|
||||
@ -779,12 +780,17 @@ class Python3Virtualenv(object):
|
||||
env=self.query_env())
|
||||
|
||||
@py3_venv_initialized
|
||||
def py3_install_modules(self, modules):
|
||||
def py3_install_modules(self, modules,
|
||||
use_mozharness_pip_config=True):
|
||||
if not os.path.exists(self.py3_venv_path):
|
||||
raise Exception('You need to call py3_create_venv() first.')
|
||||
|
||||
for m in modules:
|
||||
self.run_command('%s install %s' % (self.py3_pip_path, m), env=self.query_env())
|
||||
cmd = [self.py3_pip_path, 'install']
|
||||
if use_mozharness_pip_config:
|
||||
cmd += self._mozharness_pip_args()
|
||||
cmd += [m]
|
||||
self.run_command(cmd, env=self.query_env())
|
||||
|
||||
def _mozharness_pip_args(self):
|
||||
'''We have information in Mozharness configs that apply to pip'''
|
||||
|
@ -489,6 +489,7 @@ class Talos(TestingMixin, MercurialScript, BlobUploadMixin, TooltoolMixin,
|
||||
# now create the py3 venv
|
||||
self.py3_venv_configuration(python_path=self.py3_path, venv_path='py3venv')
|
||||
self.py3_create_venv()
|
||||
self.py3_install_modules(["cffi==1.10.0"])
|
||||
requirements = [os.path.join(self.talos_path, 'talos', 'mitmproxy', 'mitmproxy_requirements.txt')]
|
||||
self.py3_install_requirement_files(requirements)
|
||||
# add py3 executables path to system path
|
||||
|
@ -313623,6 +313623,12 @@
|
||||
{}
|
||||
]
|
||||
],
|
||||
"css/css-overflow/overflow-shorthand-001.html": [
|
||||
[
|
||||
"/css/css-overflow/overflow-shorthand-001.html",
|
||||
{}
|
||||
]
|
||||
],
|
||||
"css/css-position/position-sticky-bottom.html": [
|
||||
[
|
||||
"/css/css-position/position-sticky-bottom.html",
|
||||
@ -503530,6 +503536,10 @@
|
||||
"f51bc673da28b0471018cdf945b4449ab00ce717",
|
||||
"reftest"
|
||||
],
|
||||
"css/css-overflow/overflow-shorthand-001.html": [
|
||||
"6409ee499d3e853d8f4933f1b532e12ed9ab406b",
|
||||
"testharness"
|
||||
],
|
||||
"css/css-overflow/reference/input-scrollable-region-001-ref.html": [
|
||||
"31e24bb1a2cb6f42703cc05e055fcb345c770a22",
|
||||
"support"
|
||||
|
@ -26,9 +26,6 @@
|
||||
[border is expected to be border: dotted;]
|
||||
expected: FAIL
|
||||
|
||||
[overflow is expected to be overflow: scroll hidden;]
|
||||
expected: FAIL
|
||||
|
||||
[outline is expected to be outline: blue dotted 2px;]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -0,0 +1,42 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>CSS Overflow Test: Overflow longhand accepts two values</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<link rel="author" title="Emilio Cobos Álvarez <emilio@crisal.io>">
|
||||
<link rel="help" href="https://drafts.csswg.org/css-overflow/#propdef-overflow">
|
||||
<div id="test-div"></div>
|
||||
<script>
|
||||
let div = document.getElementById("test-div");
|
||||
function testOverflowShorthand(x, y) {
|
||||
test(function() {
|
||||
div.style.overflowX = x;
|
||||
div.style.overflowY = y;
|
||||
|
||||
let expectedX = getComputedStyle(div).overflowX;
|
||||
let expectedY = getComputedStyle(div).overflowY;
|
||||
let expectedComputedSerialization = expectedX == expectedY ? expectedX : `${expectedX} ${expectedY}`;
|
||||
let expectedSpecifiedSerialization = x == y ? x : `${x} ${y}`;
|
||||
|
||||
assert_equals(div.style.overflow, expectedSpecifiedSerialization);
|
||||
assert_equals(getComputedStyle(div).overflow, expectedComputedSerialization);
|
||||
|
||||
div.style.overflowX = "";
|
||||
div.style.overflowY = "";
|
||||
assert_equals(div.style.overflow, "");
|
||||
|
||||
div.style.overflow = `${x} ${y}`;
|
||||
assert_equals(div.style.overflow, expectedSpecifiedSerialization);
|
||||
assert_equals(div.style.overflowX, x);
|
||||
assert_equals(div.style.overflowY, y);
|
||||
assert_equals(getComputedStyle(div).overflow, expectedComputedSerialization);
|
||||
assert_equals(getComputedStyle(div).overflowX, expectedX);
|
||||
assert_equals(getComputedStyle(div).overflowY, expectedY);
|
||||
}, `overflow: ${x} ${y} works`);
|
||||
}
|
||||
|
||||
let OVERFLOW_VALUES = [ "auto", "hidden", "scroll", "visible" ];
|
||||
for (let x of OVERFLOW_VALUES)
|
||||
for (let y of OVERFLOW_VALUES)
|
||||
testOverflowShorthand(x, y);
|
||||
</script>
|
@ -120,6 +120,14 @@ class WebNavigationEventManager extends EventManager {
|
||||
data2.sourceFrameId = data.sourceFrameId;
|
||||
}
|
||||
|
||||
// Do not send a webNavigation event when the data.browser is related to a tab from a
|
||||
// new window opened to adopt an existent tab (See Bug 1443221 for a rationale).
|
||||
const chromeWin = data.browser.ownerGlobal;
|
||||
if (chromeWin && chromeWin.arguments && chromeWin.arguments[0] instanceof chromeWin.XULElement &&
|
||||
chromeWin.gBrowser.selectedBrowser === data.browser) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Fills in tabId typically.
|
||||
Object.assign(data2, tabTracker.getBrowserData(data.browser));
|
||||
if (data2.tabId < 0) {
|
||||
|
@ -1245,7 +1245,12 @@ Database::InitSchema(bool* aDatabaseMigrated)
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Firefox 61 uses schema version 46.
|
||||
if (currentSchemaVersion < 47) {
|
||||
rv = MigrateV47Up();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Firefox 61 uses schema version 47.
|
||||
|
||||
// Schema Upgrades must add migration code here.
|
||||
// >>> IMPORTANT! <<<
|
||||
@ -2110,13 +2115,15 @@ Database::MigrateV46Up() {
|
||||
// edit these queries, and just do a 1:1 conversion.
|
||||
nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"UPDATE moz_places "
|
||||
"SET url = 'place:tag=' || ( "
|
||||
"SELECT title FROM moz_bookmarks "
|
||||
"WHERE id = CAST(get_query_param(substr(url, 7), 'folder') AS INT) "
|
||||
") "
|
||||
"SET url = IFNULL('place:tag=' || ( "
|
||||
"SELECT title FROM moz_bookmarks "
|
||||
"WHERE id = CAST(get_query_param(substr(url, 7), 'folder') AS INT) "
|
||||
"), url) "
|
||||
"WHERE url_hash BETWEEN hash('place', 'prefix_lo') AND "
|
||||
"hash('place', 'prefix_hi') "
|
||||
"AND url LIKE '%type=7%' "
|
||||
"AND EXISTS(SELECT 1 FROM moz_bookmarks "
|
||||
"WHERE id = CAST(get_query_param(substr(url, 7), 'folder') AS INT)) "
|
||||
));
|
||||
|
||||
// Recalculate hashes for all tag queries.
|
||||
@ -2139,7 +2146,30 @@ Database::MigrateV46Up() {
|
||||
") "
|
||||
));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Database::MigrateV47Up() {
|
||||
// v46 may have mistakenly set some url to NULL, we must fix those.
|
||||
// Since the original url was an invalid query, we replace NULLs with an
|
||||
// empty query.
|
||||
nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"UPDATE moz_places "
|
||||
"SET url = 'place:excludeItems=1', url_hash = hash('place:excludeItems=1') "
|
||||
"WHERE url ISNULL "
|
||||
));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
// Update Sync fields for these queries.
|
||||
rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
|
||||
"UPDATE moz_bookmarks SET syncChangeCounter = syncChangeCounter + 1 "
|
||||
"WHERE fk IN ( "
|
||||
"SELECT id FROM moz_places "
|
||||
"WHERE url_hash = hash('place:excludeItems=1') "
|
||||
"AND url = 'place:excludeItems=1' "
|
||||
") "
|
||||
));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -2340,6 +2370,15 @@ Database::Shutdown()
|
||||
rv = stmt->ExecuteStep(&hasResult);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
MOZ_ASSERT(!hasResult, "Found a duplicate url!");
|
||||
|
||||
// Sanity check NULL urls
|
||||
rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT 1 FROM moz_places WHERE url ISNULL "
|
||||
), getter_AddRefs(stmt));
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
rv = stmt->ExecuteStep(&hasResult);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
MOZ_ASSERT(!hasResult, "Found a NULL url!");
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -19,7 +19,7 @@
|
||||
|
||||
// This is the schema version. Update it at any schema change and add a
|
||||
// corresponding migrateVxx method below.
|
||||
#define DATABASE_SCHEMA_VERSION 46
|
||||
#define DATABASE_SCHEMA_VERSION 47
|
||||
|
||||
// Fired after Places inited.
|
||||
#define TOPIC_PLACES_INIT_COMPLETE "places-init-complete"
|
||||
@ -307,6 +307,7 @@ protected:
|
||||
nsresult MigrateV44Up();
|
||||
nsresult MigrateV45Up();
|
||||
nsresult MigrateV46Up();
|
||||
nsresult MigrateV47Up();
|
||||
|
||||
nsresult UpdateBookmarkRootTitles();
|
||||
|
||||
|
@ -3460,6 +3460,12 @@ nsNavHistory::RowToResult(mozIStorageValueArray* aRow,
|
||||
nsAutoCString url;
|
||||
nsresult rv = aRow->GetUTF8String(kGetInfoIndex_URL, url);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
// In case of data corruption URL may be null, but our UI code prefers an
|
||||
// empty string.
|
||||
if (url.IsVoid()) {
|
||||
MOZ_ASSERT(false, "Found a NULL url in moz_places");
|
||||
url.SetIsVoid(false);
|
||||
}
|
||||
|
||||
// title
|
||||
nsAutoCString title;
|
||||
|
@ -3,7 +3,7 @@
|
||||
* 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/. */
|
||||
|
||||
const CURRENT_SCHEMA_VERSION = 46;
|
||||
const CURRENT_SCHEMA_VERSION = 47;
|
||||
const FIRST_UPGRADABLE_SCHEMA_VERSION = 30;
|
||||
|
||||
const NS_APP_USER_PROFILE_50_DIR = "ProfD";
|
||||
|
@ -17,7 +17,13 @@ let gTags = [
|
||||
{ folder: 345678,
|
||||
url: "place:type=7&folder=345678&queryType=1",
|
||||
title: "tag3",
|
||||
hash: " 268506471927988",
|
||||
hash: "268506471927988",
|
||||
},
|
||||
// This will point to an invalid folder id.
|
||||
{ folder: 456789,
|
||||
url: "place:type=7&folder=456789&queryType=1",
|
||||
title: "invalid",
|
||||
hash: "268505972797836",
|
||||
},
|
||||
];
|
||||
gTags.forEach(t => t.guid = t.title.padEnd(12, "_"));
|
||||
@ -34,9 +40,11 @@ add_task(async function setup() {
|
||||
await db.execute(`INSERT INTO moz_places (url, guid, url_hash)
|
||||
VALUES (:url, :guid, :hash)
|
||||
`, { url: tag.url, guid: tag.guid, hash: tag.hash });
|
||||
await db.execute(`INSERT INTO moz_bookmarks (id, fk, guid, title)
|
||||
VALUES (:id, (SELECT id FROM moz_places WHERE guid = :guid), :guid, :title)
|
||||
`, { id: tag.folder, guid: tag.guid, title: tag.title });
|
||||
if (tag.title != "invalid") {
|
||||
await db.execute(`INSERT INTO moz_bookmarks (id, fk, guid, title)
|
||||
VALUES (:id, (SELECT id FROM moz_places WHERE guid = :guid), :guid, :title)
|
||||
`, { id: tag.folder, guid: tag.guid, title: tag.title });
|
||||
}
|
||||
}
|
||||
|
||||
await db.close();
|
||||
@ -53,19 +61,22 @@ add_task(async function database_is_valid() {
|
||||
|
||||
add_task(async function test_queries_converted() {
|
||||
for (let tag of gTags) {
|
||||
let bm = await PlacesUtils.bookmarks.fetch(tag.guid);
|
||||
Assert.equal(bm.url.href, "place:tag=" + tag.title);
|
||||
let url = tag.title == "invalid" ? tag.url : "place:tag=" + tag.title;
|
||||
let page = await PlacesUtils.history.fetch(tag.guid);
|
||||
Assert.equal(page.url.href, url);
|
||||
}
|
||||
});
|
||||
|
||||
add_task(async function test_sync_fields() {
|
||||
let db = await PlacesUtils.promiseDBConnection();
|
||||
for (let tag of gTags) {
|
||||
let rows = await db.execute(`
|
||||
SELECT syncChangeCounter
|
||||
FROM moz_bookmarks
|
||||
WHERE guid = :guid
|
||||
`, { guid: tag.guid });
|
||||
Assert.equal(rows[0].getResultByIndex(0), 2);
|
||||
if (tag.title != "invalid") {
|
||||
let rows = await db.execute(`
|
||||
SELECT syncChangeCounter
|
||||
FROM moz_bookmarks
|
||||
WHERE guid = :guid
|
||||
`, { guid: tag.guid });
|
||||
Assert.equal(rows[0].getResultByIndex(0), 2);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -0,0 +1,41 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
let guid = "null".padEnd(12, "_");
|
||||
|
||||
add_task(async function setup() {
|
||||
await setupPlacesDatabase("places_v43.sqlite");
|
||||
|
||||
// Setup database contents to be migrated.
|
||||
let path = OS.Path.join(OS.Constants.Path.profileDir, DB_FILENAME);
|
||||
let db = await Sqlite.openConnection({ path });
|
||||
// We can reuse the same guid, it doesn't matter for this test.
|
||||
|
||||
await db.execute(`INSERT INTO moz_places (url, guid, url_hash)
|
||||
VALUES (NULL, :guid, "123456")`, { guid });
|
||||
await db.execute(`INSERT INTO moz_bookmarks (fk, guid)
|
||||
VALUES ((SELECT id FROM moz_places WHERE guid = :guid), :guid)
|
||||
`, { guid });
|
||||
await db.close();
|
||||
});
|
||||
|
||||
add_task(async function database_is_valid() {
|
||||
// Accessing the database for the first time triggers migration.
|
||||
Assert.equal(PlacesUtils.history.databaseStatus,
|
||||
PlacesUtils.history.DATABASE_STATUS_UPGRADED);
|
||||
|
||||
let db = await PlacesUtils.promiseDBConnection();
|
||||
Assert.equal((await db.getSchemaVersion()), CURRENT_SCHEMA_VERSION);
|
||||
|
||||
let page = await PlacesUtils.history.fetch(guid);
|
||||
Assert.equal(page.url.href, "place:excludeItems=1");
|
||||
|
||||
let rows = await db.execute(`
|
||||
SELECT syncChangeCounter
|
||||
FROM moz_bookmarks
|
||||
WHERE guid = :guid
|
||||
`, { guid });
|
||||
Assert.equal(rows[0].getResultByIndex(0), 2);
|
||||
});
|
@ -24,3 +24,4 @@ support-files =
|
||||
[test_current_from_v42.js]
|
||||
[test_current_from_v43.js]
|
||||
[test_current_from_v45.js]
|
||||
[test_current_from_v46.js]
|
||||
|
@ -475,7 +475,7 @@ add_task(async function test_dynamicEvents() {
|
||||
Assert.deepEqual(snapshot.dynamic.map(e => e.slice(1)), expected);
|
||||
});
|
||||
|
||||
add_task(function* test_dynamicEventRegistrationValidation() {
|
||||
add_task(async function test_dynamicEventRegistrationValidation() {
|
||||
Telemetry.canRecordExtended = true;
|
||||
Telemetry.clearEvents();
|
||||
|
||||
@ -601,7 +601,7 @@ add_task(function* test_dynamicEventRegistrationValidation() {
|
||||
|
||||
// When add-ons update, they may re-register some of the dynamic events.
|
||||
// Test through some possible scenarios.
|
||||
add_task(function* test_dynamicEventRegisterAgain() {
|
||||
add_task(async function test_dynamicEventRegisterAgain() {
|
||||
Telemetry.canRecordExtended = true;
|
||||
Telemetry.clearEvents();
|
||||
|
||||
|
@ -1007,7 +1007,7 @@ function test_keyed_subsession() {
|
||||
Assert.equal(h.subsessionSnapshot(KEY).sum, 0);
|
||||
});
|
||||
|
||||
add_task(function* test_keyed_keys() {
|
||||
add_task(async function test_keyed_keys() {
|
||||
let h = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_KEYS");
|
||||
h.clear();
|
||||
Telemetry.clearScalars();
|
||||
|
@ -536,7 +536,7 @@ add_task(async function test_keyed_max_keys() {
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* test_dynamicScalars_registration() {
|
||||
add_task(async function test_dynamicScalars_registration() {
|
||||
Telemetry.clearScalars();
|
||||
|
||||
const TEST_CASES = [
|
||||
@ -606,7 +606,7 @@ add_task(function* test_dynamicScalars_registration() {
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* test_dynamicScalars_doubleRegistration() {
|
||||
add_task(async function test_dynamicScalars_doubleRegistration() {
|
||||
Telemetry.clearScalars();
|
||||
|
||||
// Register a test scalar.
|
||||
@ -664,7 +664,7 @@ add_task(function* test_dynamicScalars_doubleRegistration() {
|
||||
"The recorded scalar must contain the right value.");
|
||||
});
|
||||
|
||||
add_task(function* test_dynamicScalars_recording() {
|
||||
add_task(async function test_dynamicScalars_recording() {
|
||||
Telemetry.clearScalars();
|
||||
|
||||
// Disable extended recording so that we will just record opt-out.
|
||||
|
@ -763,15 +763,14 @@ PuppetWidget::SetInputContext(const InputContext& aContext,
|
||||
if (!mTabChild) {
|
||||
return;
|
||||
}
|
||||
mTabChild->SendSetInputContext(
|
||||
static_cast<int32_t>(aContext.mIMEState.mEnabled),
|
||||
static_cast<int32_t>(aContext.mIMEState.mOpen),
|
||||
aContext.mHTMLInputType,
|
||||
aContext.mHTMLInputInputmode,
|
||||
aContext.mActionHint,
|
||||
aContext.mInPrivateBrowsing,
|
||||
static_cast<int32_t>(aAction.mCause),
|
||||
static_cast<int32_t>(aAction.mFocusChange));
|
||||
mTabChild->SendSetInputContext(aContext.mIMEState.mEnabled,
|
||||
aContext.mIMEState.mOpen,
|
||||
aContext.mHTMLInputType,
|
||||
aContext.mHTMLInputInputmode,
|
||||
aContext.mActionHint,
|
||||
aContext.mInPrivateBrowsing,
|
||||
aAction.mCause,
|
||||
aAction.mFocusChange);
|
||||
}
|
||||
|
||||
InputContext
|
||||
@ -795,10 +794,11 @@ PuppetWidget::GetInputContext()
|
||||
// chrome widget is set to new context.
|
||||
InputContext context;
|
||||
if (mTabChild) {
|
||||
int32_t enabled, open;
|
||||
IMEState::Enabled enabled;
|
||||
IMEState::Open open;
|
||||
mTabChild->SendGetInputContext(&enabled, &open);
|
||||
context.mIMEState.mEnabled = static_cast<IMEState::Enabled>(enabled);
|
||||
context.mIMEState.mOpen = static_cast<IMEState::Open>(open);
|
||||
context.mIMEState.mEnabled = enabled;
|
||||
context.mIMEState.mOpen = open;
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
@ -948,6 +948,42 @@ struct ParamTraits<mozilla::widget::IMENotification>
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ParamTraits<mozilla::widget::IMEState::Enabled>
|
||||
: ContiguousEnumSerializer<mozilla::widget::IMEState::Enabled,
|
||||
mozilla::widget::IMEState::Enabled::DISABLED,
|
||||
mozilla::widget::IMEState::Enabled::UNKNOWN>
|
||||
{
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ParamTraits<mozilla::widget::IMEState::Open>
|
||||
: ContiguousEnumSerializerInclusive<
|
||||
mozilla::widget::IMEState::Open,
|
||||
mozilla::widget::IMEState::Open::OPEN_STATE_NOT_SUPPORTED,
|
||||
mozilla::widget::IMEState::Open::CLOSED>
|
||||
{
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ParamTraits<mozilla::widget::InputContextAction::Cause>
|
||||
: ContiguousEnumSerializerInclusive<
|
||||
mozilla::widget::InputContextAction::Cause,
|
||||
mozilla::widget::InputContextAction::Cause::CAUSE_UNKNOWN,
|
||||
mozilla::widget::InputContextAction::Cause::
|
||||
CAUSE_UNKNOWN_DURING_KEYBOARD_INPUT>
|
||||
{
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ParamTraits<mozilla::widget::InputContextAction::FocusChange>
|
||||
: ContiguousEnumSerializerInclusive<
|
||||
mozilla::widget::InputContextAction::FocusChange,
|
||||
mozilla::widget::InputContextAction::FocusChange::FOCUS_NOT_CHANGED,
|
||||
mozilla::widget::InputContextAction::FocusChange::WIDGET_CREATED>
|
||||
{
|
||||
};
|
||||
|
||||
template<>
|
||||
struct ParamTraits<mozilla::WidgetPluginEvent>
|
||||
{
|
||||
|
@ -32,6 +32,7 @@ class CycleCollectedJSRuntime;
|
||||
|
||||
namespace dom {
|
||||
class Exception;
|
||||
class WorkletJSContext;
|
||||
} // namespace dom
|
||||
|
||||
// Contains various stats about the cycle collection.
|
||||
@ -130,6 +131,8 @@ public:
|
||||
FinalizeNow,
|
||||
};
|
||||
|
||||
virtual dom::WorkletJSContext* GetAsWorkletJSContext() { return nullptr; }
|
||||
|
||||
CycleCollectedJSRuntime* Runtime() const
|
||||
{
|
||||
MOZ_ASSERT(mRuntime);
|
||||
@ -236,6 +239,8 @@ public:
|
||||
JS::PersistentRooted<JS::GCVector<JSObject*, 0, js::SystemAllocPolicy>> mConsumedRejections;
|
||||
nsTArray<nsCOMPtr<nsISupports /* UncaughtRejectionObserver */ >> mUncaughtRejectionObservers;
|
||||
|
||||
virtual bool IsSystemCaller() const = 0;
|
||||
|
||||
private:
|
||||
// A primary context owns the mRuntime. Non-main-thread contexts should always
|
||||
// be primary. On the main thread, the primary context should be the first one
|
||||
|
@ -143,7 +143,7 @@ struct SerialNumberRecord
|
||||
return;
|
||||
}
|
||||
|
||||
JSContext* cx = nsContentUtils::GetCurrentJSContextForThread();
|
||||
JSContext* cx = nsContentUtils::GetCurrentJSContext();
|
||||
if (!cx) {
|
||||
return;
|
||||
}
|
||||
|
@ -136,7 +136,7 @@ TEST(TArray, AssignmentOperatorSelfAssignment)
|
||||
nsTArray<int> array;
|
||||
array = DummyArray();
|
||||
|
||||
array = array;
|
||||
array = *&array;
|
||||
ASSERT_EQ(DummyArray(), array);
|
||||
array = Move(array);
|
||||
ASSERT_EQ(DummyArray(), array);
|
||||
|
Loading…
Reference in New Issue
Block a user