merge mozilla-central to mozilla-inbound. CLOSED TREE

This commit is contained in:
Sebastian Hengst 2018-04-12 22:22:51 +03:00
commit c12ee0bc10
94 changed files with 4036 additions and 1129 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -15,6 +15,7 @@ DevToolsModules(
'FilterButton.js',
'FilterCheckbox.js',
'GripMessageBody.js',
'JSTerm.js',
'Message.js',
'MessageContainer.js',
'MessageIcon.js',

View File

@ -17,7 +17,6 @@ DevToolsModules(
'console-commands.js',
'constants.js',
'hudservice.js',
'jsterm.js',
'main.js',
'new-console-output-wrapper.js',
'new-webconsole.js',

View File

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

View File

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

View File

@ -11,6 +11,7 @@ DIRS += [
]
DevToolsModules(
'console-output.js',
'jsterm.js',
'webconsole.js',
)
with Files('**'):

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1140,6 +1140,11 @@ public:
microTaskQueue->push(runnable.forget());
}
bool IsSystemCaller() const override
{
return mWorkerPrivate->UsesSystemPrincipal();
}
private:
WorkerPrivate* mWorkerPrivate;
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View 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

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

View File

@ -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 += [

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1257,3 +1257,9 @@ XPCJSContext::AfterProcessTask(uint32_t aNewRecursionDepth)
mozilla::jsipc::AfterProcessTask();
}
bool
XPCJSContext::IsSystemCaller() const
{
return nsContentUtils::IsSystemCaller(Context());
}

View File

@ -413,6 +413,8 @@ public:
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
bool IsSystemCaller() const override;
AutoMarkingPtr** GetAutoRootsAdr() {return &mAutoRoots;}
nsresult GetPendingResult() { return mPendingResult; }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -557,7 +557,8 @@
"oc": {
"platforms": [
"android",
"android-api-16"
"android-api-16",
"android-multilocale"
],
"revision": "default"
},

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -143,7 +143,7 @@ struct SerialNumberRecord
return;
}
JSContext* cx = nsContentUtils::GetCurrentJSContextForThread();
JSContext* cx = nsContentUtils::GetCurrentJSContext();
if (!cx) {
return;
}

View File

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