зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to autoland a=merge on a CLOSED TREE
This commit is contained in:
Коммит
6455148cff
|
@ -98,6 +98,8 @@ devtools/client/inspector/markup/test/doc_markup_events_*.html
|
|||
devtools/client/inspector/rules/test/doc_media_queries.html
|
||||
devtools/client/performance/components/test/test_jit_optimizations_01.html
|
||||
devtools/client/responsive.html/test/browser/touch.html
|
||||
devtools/client/shared/test/*.html
|
||||
devtools/client/shared/test/code_*.js
|
||||
devtools/client/shared/components/test/mochitest/*.html
|
||||
!devtools/client/shared/components/test/mochitest/test_stack-trace.html
|
||||
devtools/client/storage/test/*.html
|
||||
|
|
|
@ -27,7 +27,10 @@ add_task(async function findbar_test() {
|
|||
ok(!gFindBar.hidden, "the Find bar isn't hidden after the location of a " +
|
||||
"subdocument changes");
|
||||
|
||||
let findBarClosePromise = promiseWaitForEvent(gBrowser, "findbarclose");
|
||||
gFindBar.close();
|
||||
await findBarClosePromise;
|
||||
|
||||
gBrowser.removeTab(newTab);
|
||||
});
|
||||
|
||||
|
|
|
@ -192,24 +192,27 @@ var PdfjsChromeUtils = {
|
|||
},
|
||||
|
||||
handleEvent(aEvent) {
|
||||
const type = aEvent.type;
|
||||
// Handle the tab find initialized event specially:
|
||||
if (aEvent.type == "TabFindInitialized") {
|
||||
if (type == "TabFindInitialized") {
|
||||
let browser = aEvent.target.linkedBrowser;
|
||||
this._hookupEventListeners(browser);
|
||||
aEvent.target.removeEventListener(aEvent.type, this);
|
||||
aEvent.target.removeEventListener(type, this);
|
||||
return;
|
||||
}
|
||||
|
||||
// To avoid forwarding the message as a CPOW, create a structured cloneable
|
||||
// version of the event for both performance, and ease of usage, reasons.
|
||||
let type = aEvent.type;
|
||||
let detail = {
|
||||
query: aEvent.detail.query,
|
||||
caseSensitive: aEvent.detail.caseSensitive,
|
||||
entireWord: aEvent.detail.entireWord,
|
||||
highlightAll: aEvent.detail.highlightAll,
|
||||
findPrevious: aEvent.detail.findPrevious,
|
||||
};
|
||||
let detail = null;
|
||||
if (type !== "findbarclose") {
|
||||
detail = {
|
||||
query: aEvent.detail.query,
|
||||
caseSensitive: aEvent.detail.caseSensitive,
|
||||
entireWord: aEvent.detail.entireWord,
|
||||
highlightAll: aEvent.detail.highlightAll,
|
||||
findPrevious: aEvent.detail.findPrevious,
|
||||
};
|
||||
}
|
||||
|
||||
let browser = aEvent.currentTarget.browser;
|
||||
if (!this._browsers.has(browser)) {
|
||||
|
@ -222,10 +225,13 @@ var PdfjsChromeUtils = {
|
|||
aEvent.preventDefault();
|
||||
},
|
||||
|
||||
_types: ["find",
|
||||
"findagain",
|
||||
"findhighlightallchange",
|
||||
"findcasesensitivitychange"],
|
||||
_types: [
|
||||
"find",
|
||||
"findagain",
|
||||
"findhighlightallchange",
|
||||
"findcasesensitivitychange",
|
||||
"findbarclose",
|
||||
],
|
||||
|
||||
_addEventListener(aMsg) {
|
||||
let browser = aMsg.target;
|
||||
|
|
|
@ -1,40 +1,15 @@
|
|||
# Tests in this directory are split into two manifests (this and browser2.ini)
|
||||
# to facilitate better chunking; see bug 1294489.
|
||||
|
||||
[DEFAULT]
|
||||
tags = devtools
|
||||
subsuite = devtools
|
||||
skip-if = (os == 'linux' && debug && bits == 32)
|
||||
support-files =
|
||||
addon4.xpi
|
||||
code_listworkers-worker1.js
|
||||
code_listworkers-worker2.js
|
||||
code_WorkerTargetActor.attachThread-worker.js
|
||||
doc_event-listeners-01.html
|
||||
doc_event-listeners-03.html
|
||||
doc_inline-debugger-statement.html
|
||||
doc_listworkers-tab.html
|
||||
doc_native-event-handler.html
|
||||
doc_promise-get-allocation-stack.html
|
||||
doc_promise-get-fulfillment-stack.html
|
||||
doc_promise-get-rejection-stack.html
|
||||
doc_terminate-on-tab-close.html
|
||||
doc_WorkerTargetActor.attachThread-tab.html
|
||||
head.js
|
||||
!/devtools/client/shared/test/shared-head.js
|
||||
!/devtools/client/shared/test/telemetry-test-helpers.js
|
||||
[browser_dbg_addon-console.js]
|
||||
skip-if = (e10s && debug || os == 'win' || verify) || true # bug 1005274
|
||||
tags = addons
|
||||
[browser_dbg_debugger-statement.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_event-listeners-01.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_event-listeners-02.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_event-listeners-03.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_listworkers.js]
|
||||
[browser_dbg_promises-allocation-stack.js]
|
||||
uses-unsafe-cpows = true
|
||||
skip-if = true
|
||||
|
@ -49,6 +24,4 @@ uses-unsafe-cpows = true
|
|||
skip-if = true
|
||||
[browser_dbg_terminate-on-tab-close.js]
|
||||
uses-unsafe-cpows = true
|
||||
skip-if = true
|
||||
[browser_dbg_worker-window.js]
|
||||
skip-if = (e10s && debug) || true # Bug 1486974
|
||||
skip-if = true
|
|
@ -1,47 +0,0 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that the we can see console messages from the add-on
|
||||
|
||||
const ADDON_ID = "browser_dbg_addon4@tests.mozilla.org";
|
||||
const ADDON_PATH = "addon4.xpi";
|
||||
|
||||
function getCachedMessages(webConsole) {
|
||||
let deferred = promise.defer();
|
||||
webConsole.getCachedMessages(["ConsoleAPI"], (aResponse) => {
|
||||
if (aResponse.error) {
|
||||
deferred.reject(aResponse.error);
|
||||
return;
|
||||
}
|
||||
deferred.resolve(aResponse.messages);
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function test() {
|
||||
Task.spawn(function* () {
|
||||
let addon = yield addTemporaryAddon(ADDON_PATH);
|
||||
let addonDebugger = yield initAddonDebugger(ADDON_ID);
|
||||
|
||||
let webConsole = addonDebugger.webConsole;
|
||||
let messages = yield getCachedMessages(webConsole);
|
||||
is(messages.length, 1, "Should be one cached message");
|
||||
is(messages[0].arguments[0].type, "object", "Should have logged an object");
|
||||
is(messages[0].arguments[0].preview.ownProperties.msg.value, "Hello from the test add-on", "Should have got the right message");
|
||||
|
||||
let consolePromise = addonDebugger.once("console");
|
||||
|
||||
console.log("Bad message");
|
||||
Services.obs.notifyObservers(null, "addon-test-ping");
|
||||
|
||||
let messageGrip = yield consolePromise;
|
||||
is(messageGrip.arguments[0].type, "object", "Should have logged an object");
|
||||
is(messageGrip.arguments[0].preview.ownProperties.msg.value, "Hello again", "Should have got the right message");
|
||||
|
||||
yield addonDebugger.destroy();
|
||||
yield removeAddon(addon);
|
||||
finish();
|
||||
});
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
var TAB_URL = EXAMPLE_URL + "doc_listworkers-tab.html";
|
||||
var WORKER1_URL = "code_listworkers-worker1.js";
|
||||
var WORKER2_URL = "code_listworkers-worker2.js";
|
||||
|
||||
function test() {
|
||||
Task.spawn(function* () {
|
||||
DebuggerServer.init();
|
||||
DebuggerServer.registerAllActors();
|
||||
|
||||
let client = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
yield connect(client);
|
||||
|
||||
let tab = yield addTab(TAB_URL);
|
||||
let { tabs } = yield listTabs(client);
|
||||
let [, targetFront] = yield attachTarget(client, findTab(tabs, TAB_URL));
|
||||
|
||||
let { workers } = yield listWorkers(targetFront);
|
||||
is(workers.length, 0);
|
||||
|
||||
executeSoon(() => {
|
||||
evalInTab(tab, "var worker1 = new Worker('" + WORKER1_URL + "');");
|
||||
});
|
||||
yield waitForWorkerListChanged(targetFront);
|
||||
|
||||
({ workers } = yield listWorkers(targetFront));
|
||||
is(workers.length, 1);
|
||||
is(workers[0].url, WORKER1_URL);
|
||||
|
||||
executeSoon(() => {
|
||||
evalInTab(tab, "var worker2 = new Worker('" + WORKER2_URL + "');");
|
||||
});
|
||||
yield waitForWorkerListChanged(targetFront);
|
||||
|
||||
({ workers } = yield listWorkers(targetFront));
|
||||
is(workers.length, 2);
|
||||
is(workers[0].url, WORKER1_URL);
|
||||
is(workers[1].url, WORKER2_URL);
|
||||
|
||||
executeSoon(() => {
|
||||
evalInTab(tab, "worker1.terminate()");
|
||||
});
|
||||
yield waitForWorkerListChanged(targetFront);
|
||||
|
||||
({ workers } = yield listWorkers(targetFront));
|
||||
is(workers.length, 1);
|
||||
is(workers[0].url, WORKER2_URL);
|
||||
|
||||
executeSoon(() => {
|
||||
evalInTab(tab, "worker2.terminate()");
|
||||
});
|
||||
yield waitForWorkerListChanged(targetFront);
|
||||
|
||||
({ workers } = yield listWorkers(targetFront));
|
||||
is(workers.length, 0);
|
||||
|
||||
yield close(client);
|
||||
finish();
|
||||
});
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
function f() {
|
||||
var a = 1;
|
||||
var b = 2;
|
||||
var c = 3;
|
||||
}
|
||||
|
||||
self.onmessage = function (event) {
|
||||
if (event.data == "ping") {
|
||||
f();
|
||||
postMessage("pong");
|
||||
}
|
||||
};
|
||||
|
||||
postMessage("load");
|
|
@ -1,3 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
self.onmessage = function () {};
|
|
@ -1,3 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
self.onmessage = function () {};
|
|
@ -1,8 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
|
@ -591,157 +591,6 @@ let initDebugger = Task.async(function*(urlOrTab, options) {
|
|||
return [tab, debuggerPanel, window];
|
||||
});
|
||||
|
||||
// Creates an add-on debugger for a given add-on. The returned AddonDebugger
|
||||
// object must be destroyed before finishing the test
|
||||
function initAddonDebugger(aAddonId) {
|
||||
let addonDebugger = new AddonDebugger();
|
||||
return addonDebugger.init(aAddonId).then(() => addonDebugger);
|
||||
}
|
||||
|
||||
function AddonDebugger() {
|
||||
this._onMessage = this._onMessage.bind(this);
|
||||
this._onConsoleAPICall = this._onConsoleAPICall.bind(this);
|
||||
EventEmitter.decorate(this);
|
||||
}
|
||||
|
||||
AddonDebugger.prototype = {
|
||||
init: Task.async(function* (aAddonId) {
|
||||
info("Initializing an addon debugger panel.");
|
||||
|
||||
DebuggerServer.init();
|
||||
DebuggerServer.registerAllActors();
|
||||
DebuggerServer.allowChromeProcess = true;
|
||||
|
||||
this.frame = document.createElement("iframe");
|
||||
this.frame.setAttribute("height", 400);
|
||||
document.documentElement.appendChild(this.frame);
|
||||
window.addEventListener("message", this._onMessage);
|
||||
|
||||
let transport = DebuggerServer.connectPipe();
|
||||
this.client = new DebuggerClient(transport);
|
||||
|
||||
yield this.client.connect();
|
||||
|
||||
let addonTargetActor = yield getAddonActorForId(this.client, aAddonId);
|
||||
|
||||
let targetOptions = {
|
||||
form: addonTargetActor,
|
||||
client: this.client,
|
||||
chrome: true,
|
||||
};
|
||||
|
||||
let toolboxOptions = {
|
||||
customIframe: this.frame
|
||||
};
|
||||
|
||||
this.target = yield TargetFactory.forRemoteTab(targetOptions);
|
||||
let toolbox = yield gDevTools.showToolbox(this.target, "jsdebugger", Toolbox.HostType.CUSTOM, toolboxOptions);
|
||||
|
||||
info("Addon debugger panel shown successfully.");
|
||||
|
||||
this.debuggerPanel = toolbox.getCurrentPanel();
|
||||
yield waitForSourceShown(this.debuggerPanel, "");
|
||||
|
||||
prepareDebugger(this.debuggerPanel);
|
||||
yield this._attachConsole();
|
||||
}),
|
||||
|
||||
destroy: Task.async(function* () {
|
||||
yield this.client.close();
|
||||
yield this.debuggerPanel._toolbox.destroy();
|
||||
this.frame.remove();
|
||||
window.removeEventListener("message", this._onMessage);
|
||||
}),
|
||||
|
||||
_attachConsole: function () {
|
||||
let deferred = promise.defer();
|
||||
this.client.attachConsole(this.target.form.consoleActor, ["ConsoleAPI"])
|
||||
.then(([aResponse, aWebConsoleClient]) => {
|
||||
this.webConsole = aWebConsoleClient;
|
||||
this.client.addListener("consoleAPICall", this._onConsoleAPICall);
|
||||
deferred.resolve();
|
||||
}, e => {
|
||||
deferred.reject(e);
|
||||
});
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
_onConsoleAPICall: function (aType, aPacket) {
|
||||
if (aPacket.from != this.webConsole.actor)
|
||||
return;
|
||||
this.emit("console", aPacket.message);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a list of the groups and sources in the UI. The returned array
|
||||
* contains objects for each group with properties name and sources. The
|
||||
* sources property contains an array with objects for each source for that
|
||||
* group with properties label and url.
|
||||
*/
|
||||
getSourceGroups: Task.async(function* () {
|
||||
let debuggerWin = this.debuggerPanel.panelWin;
|
||||
let sources = yield getSources(debuggerWin.gThreadClient);
|
||||
ok(sources.length, "retrieved sources");
|
||||
|
||||
// groups will be the return value, groupmap and the maps we put in it will
|
||||
// be used as quick lookups to add the url information in below
|
||||
let groups = [];
|
||||
let groupmap = new Map();
|
||||
|
||||
let uigroups = this.debuggerPanel.panelWin.document.querySelectorAll(".side-menu-widget-group");
|
||||
for (let g of uigroups) {
|
||||
let name = g.querySelector(".side-menu-widget-group-title .name").value;
|
||||
let group = {
|
||||
name: name,
|
||||
sources: []
|
||||
};
|
||||
groups.push(group);
|
||||
let labelmap = new Map();
|
||||
groupmap.set(name, labelmap);
|
||||
|
||||
for (let l of g.querySelectorAll(".dbg-source-item")) {
|
||||
let source = {
|
||||
label: l.value,
|
||||
url: null
|
||||
};
|
||||
|
||||
labelmap.set(l.value, source);
|
||||
group.sources.push(source);
|
||||
}
|
||||
}
|
||||
|
||||
for (let source of sources) {
|
||||
let { label, group } = debuggerWin.DebuggerView.Sources.getItemByValue(source.actor).attachment;
|
||||
|
||||
if (!groupmap.has(group)) {
|
||||
ok(false, "Saw a source group not in the UI: " + group);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!groupmap.get(group).has(label)) {
|
||||
ok(false, "Saw a source label not in the UI: " + label);
|
||||
continue;
|
||||
}
|
||||
|
||||
groupmap.get(group).get(label).url = source.url.split(" -> ").pop();
|
||||
}
|
||||
|
||||
return groups;
|
||||
}),
|
||||
|
||||
_onMessage: function(event) {
|
||||
if (!event.data) {
|
||||
return;
|
||||
}
|
||||
const msg = event.data;
|
||||
switch (msg.name) {
|
||||
case "toolbox-title":
|
||||
this.title = msg.data.value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function initChromeDebugger(aOnClose) {
|
||||
info("Initializing a chrome debugger process.");
|
||||
|
||||
|
|
|
@ -4,7 +4,10 @@ subsuite = devtools
|
|||
support-files =
|
||||
addon1.xpi
|
||||
addon2.xpi
|
||||
addon4.xpi
|
||||
browser_devices.json
|
||||
code_listworkers-worker1.js
|
||||
code_listworkers-worker2.js
|
||||
code_WorkerTargetActor.attach-worker1.js
|
||||
code_WorkerTargetActor.attach-worker2.js
|
||||
code_WorkerTargetActor.attachThread-worker.js
|
||||
|
@ -13,6 +16,8 @@ support-files =
|
|||
doc_cubic-bezier-02.html
|
||||
doc_empty-tab-01.html
|
||||
doc_empty-tab-02.html
|
||||
doc_event-listeners-01.html
|
||||
doc_event-listeners-03.html
|
||||
doc_filter-editor-01.html
|
||||
doc_html_tooltip-02.xul
|
||||
doc_html_tooltip-03.xul
|
||||
|
@ -25,9 +30,12 @@ support-files =
|
|||
doc_html_tooltip_doorhanger-02.xul
|
||||
doc_html_tooltip_hover.xul
|
||||
doc_html_tooltip_rtl.xul
|
||||
doc_inline-debugger-statement.html
|
||||
doc_inplace-editor_autocomplete_offset.xul
|
||||
doc_layoutHelpers-getBoxQuads.html
|
||||
doc_layoutHelpers.html
|
||||
doc_listworkers-tab.html
|
||||
doc_native-event-handler.html
|
||||
doc_options-view.xul
|
||||
doc_script-switching-01.html
|
||||
doc_script-switching-02.html
|
||||
|
@ -42,6 +50,7 @@ support-files =
|
|||
dummy.html
|
||||
frame-script-utils.js
|
||||
head.js
|
||||
helper_addons.js
|
||||
helper_color_data.js
|
||||
helper_html_tooltip.js
|
||||
helper_inplace_editor.js
|
||||
|
@ -67,8 +76,20 @@ support-files =
|
|||
[browser_cubic-bezier-05.js]
|
||||
[browser_cubic-bezier-06.js]
|
||||
[browser_cubic-bezier-07.js]
|
||||
[browser_dbg_addon-console.js]
|
||||
skip-if = (e10s && debug || os == 'win' || verify)
|
||||
tags = addons
|
||||
[browser_dbg_debugger-statement.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_event-listeners-01.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_event-listeners-02.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_event-listeners-03.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_globalactor.js]
|
||||
skip-if = e10s
|
||||
[browser_dbg_listworkers.js]
|
||||
[browser_filter-editor-01.js]
|
||||
[browser_filter-editor-02.js]
|
||||
[browser_filter-editor-03.js]
|
||||
|
@ -246,3 +267,5 @@ skip-if = debug # bug 1334683
|
|||
skip-if = e10s && debug
|
||||
[browser_dbg_WorkerTargetActor.attach.js]
|
||||
skip-if = e10s && debug
|
||||
[browser_dbg_worker-window.js]
|
||||
skip-if = (e10s && debug) || true # Bug 1486974
|
|
@ -0,0 +1,151 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/* import-globals-from helper_addons.js */
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://mochitests/content/browser/devtools/client/shared/test/helper_addons.js",
|
||||
this);
|
||||
|
||||
var { DebuggerServer } = require("devtools/server/main");
|
||||
var { DebuggerClient } = require("devtools/shared/client/debugger-client");
|
||||
var { Toolbox } = require("devtools/client/framework/toolbox");
|
||||
var { Task } = require("devtools/shared/task");
|
||||
|
||||
// Test that the we can see console messages from the add-on
|
||||
|
||||
const ADDON_ID = "browser_dbg_addon4@tests.mozilla.org";
|
||||
const ADDON_PATH = "addon4.xpi";
|
||||
|
||||
function getCachedMessages(webConsole) {
|
||||
const deferred = getDeferredPromise().defer();
|
||||
webConsole.getCachedMessages(["ConsoleAPI"], response => {
|
||||
if (response.error) {
|
||||
deferred.reject(response.error);
|
||||
return;
|
||||
}
|
||||
deferred.resolve(response.messages);
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
// Creates an add-on debugger for a given add-on. The returned AddonDebugger
|
||||
// object must be destroyed before finishing the test
|
||||
function initAddonDebugger(addonId) {
|
||||
const addonDebugger = new AddonDebugger();
|
||||
return addonDebugger.init(addonId).then(() => addonDebugger);
|
||||
}
|
||||
|
||||
function AddonDebugger() {
|
||||
this._onMessage = this._onMessage.bind(this);
|
||||
this._onConsoleAPICall = this._onConsoleAPICall.bind(this);
|
||||
EventEmitter.decorate(this);
|
||||
}
|
||||
|
||||
AddonDebugger.prototype = {
|
||||
init: Task.async(function* (addonId) {
|
||||
info("Initializing an addon debugger panel.");
|
||||
|
||||
DebuggerServer.init();
|
||||
DebuggerServer.registerAllActors();
|
||||
DebuggerServer.allowChromeProcess = true;
|
||||
|
||||
this.frame = document.createElement("iframe");
|
||||
this.frame.setAttribute("height", 400);
|
||||
document.documentElement.appendChild(this.frame);
|
||||
window.addEventListener("message", this._onMessage);
|
||||
|
||||
const transport = DebuggerServer.connectPipe();
|
||||
this.client = new DebuggerClient(transport);
|
||||
|
||||
yield this.client.connect();
|
||||
|
||||
const addonTargetActor = yield getAddonActorForId(this.client, addonId);
|
||||
|
||||
const targetOptions = {
|
||||
form: addonTargetActor,
|
||||
client: this.client,
|
||||
chrome: true,
|
||||
};
|
||||
|
||||
const toolboxOptions = {
|
||||
customIframe: this.frame,
|
||||
};
|
||||
|
||||
this.target = yield TargetFactory.forRemoteTab(targetOptions);
|
||||
const toolbox = yield gDevTools.showToolbox(
|
||||
this.target, "jsdebugger", Toolbox.HostType.CUSTOM, toolboxOptions);
|
||||
|
||||
info("Addon debugger panel shown successfully.");
|
||||
|
||||
this.debuggerPanel = toolbox.getCurrentPanel();
|
||||
yield this._attachConsole();
|
||||
}),
|
||||
|
||||
destroy: Task.async(function* () {
|
||||
yield this.client.close();
|
||||
this.frame.remove();
|
||||
window.removeEventListener("message", this._onMessage);
|
||||
}),
|
||||
|
||||
_attachConsole: function() {
|
||||
const deferred = getDeferredPromise().defer();
|
||||
this.client.attachConsole(this.target.form.consoleActor, ["ConsoleAPI"])
|
||||
.then(([aResponse, aWebConsoleClient]) => {
|
||||
this.webConsole = aWebConsoleClient;
|
||||
this.client.addListener("consoleAPICall", this._onConsoleAPICall);
|
||||
deferred.resolve();
|
||||
}, e => {
|
||||
deferred.reject(e);
|
||||
});
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
_onConsoleAPICall: function(type, packet) {
|
||||
if (packet.from != this.webConsole.actor) {
|
||||
return;
|
||||
}
|
||||
this.emit("console", packet.message);
|
||||
},
|
||||
|
||||
_onMessage: function(event) {
|
||||
if (!event.data) {
|
||||
return;
|
||||
}
|
||||
const msg = event.data;
|
||||
switch (msg.name) {
|
||||
case "toolbox-title":
|
||||
this.title = msg.data.value;
|
||||
break;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
add_task(async function test() {
|
||||
const addon = await addTemporaryAddon(ADDON_PATH);
|
||||
const addonDebugger = await initAddonDebugger(ADDON_ID);
|
||||
|
||||
const webConsole = addonDebugger.webConsole;
|
||||
const messages = await getCachedMessages(webConsole);
|
||||
is(messages.length, 1, "Should be one cached message");
|
||||
is(messages[0].arguments[0].type, "object", "Should have logged an object");
|
||||
is(messages[0].arguments[0].preview.ownProperties.msg.value,
|
||||
"Hello from the test add-on", "Should have got the right message");
|
||||
|
||||
const consolePromise = addonDebugger.once("console");
|
||||
|
||||
console.log("Bad message");
|
||||
Services.obs.notifyObservers(null, "addon-test-ping");
|
||||
|
||||
const messageGrip = await consolePromise;
|
||||
is(messageGrip.arguments[0].type, "object", "Should have logged an object");
|
||||
is(messageGrip.arguments[0].preview.ownProperties.msg.value,
|
||||
"Hello again", "Should have got the right message");
|
||||
|
||||
await addonDebugger.destroy();
|
||||
await removeAddon(addon);
|
||||
finish();
|
||||
});
|
|
@ -3,11 +3,22 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Tests the behavior of the debugger statement.
|
||||
*/
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_inline-debugger-statement.html";
|
||||
// Import helpers for the workers
|
||||
/* import-globals-from helper_workers.js */
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://mochitests/content/browser/devtools/client/shared/test/helper_workers.js",
|
||||
this);
|
||||
|
||||
var { DebuggerServer } = require("devtools/server/main");
|
||||
var { DebuggerClient } = require("devtools/shared/client/debugger-client");
|
||||
|
||||
const TAB_URL = TEST_URI_ROOT + "doc_inline-debugger-statement.html";
|
||||
|
||||
var gClient;
|
||||
var gTab;
|
||||
|
@ -16,31 +27,31 @@ function test() {
|
|||
DebuggerServer.init();
|
||||
DebuggerServer.registerAllActors();
|
||||
|
||||
let transport = DebuggerServer.connectPipe();
|
||||
const transport = DebuggerServer.connectPipe();
|
||||
gClient = new DebuggerClient(transport);
|
||||
gClient.connect().then(([aType, aTraits]) => {
|
||||
is(aType, "browser",
|
||||
"Root actor should identify itself as a browser.");
|
||||
|
||||
addTab(TAB_URL)
|
||||
.then((aTab) => {
|
||||
gTab = aTab;
|
||||
.then(tab => {
|
||||
gTab = tab;
|
||||
return attachTargetActorForUrl(gClient, TAB_URL);
|
||||
})
|
||||
.then(testEarlyDebuggerStatement)
|
||||
.then(testDebuggerStatement)
|
||||
.then(() => gClient.close())
|
||||
.then(finish)
|
||||
.catch(aError => {
|
||||
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
|
||||
.catch(error => {
|
||||
ok(false, "Got an error: " + error.message + "\n" + error.stack);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function testEarlyDebuggerStatement([aGrip, aResponse]) {
|
||||
let deferred = promise.defer();
|
||||
const deferred = getDeferredPromise().defer();
|
||||
|
||||
let onPaused = function (aEvent, aPacket) {
|
||||
const onPaused = function(event, packet) {
|
||||
ok(false, "Pause shouldn't be called before we've attached!");
|
||||
deferred.reject();
|
||||
};
|
||||
|
@ -65,9 +76,9 @@ function testEarlyDebuggerStatement([aGrip, aResponse]) {
|
|||
}
|
||||
|
||||
function testDebuggerStatement([aGrip, aResponse]) {
|
||||
let deferred = promise.defer();
|
||||
const deferred = getDeferredPromise().defer();
|
||||
|
||||
gClient.addListener("paused", (aEvent, aPacket) => {
|
||||
gClient.addListener("paused", (event, packet) => {
|
||||
gClient.request({ to: aResponse.threadActor, type: "resume" }, () => {
|
||||
ok(true, "The pause handler was triggered on a debugger statement.");
|
||||
deferred.resolve();
|
||||
|
@ -80,6 +91,6 @@ function testDebuggerStatement([aGrip, aResponse]) {
|
|||
return deferred.promise;
|
||||
}
|
||||
|
||||
registerCleanupFunction(function () {
|
||||
registerCleanupFunction(function() {
|
||||
gClient = null;
|
||||
});
|
|
@ -3,92 +3,104 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Tests that the eventListeners request works.
|
||||
*/
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_event-listeners-01.html";
|
||||
// Import helpers for the workers
|
||||
/* import-globals-from helper_workers.js */
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://mochitests/content/browser/devtools/client/shared/test/helper_workers.js",
|
||||
this);
|
||||
|
||||
var { DebuggerServer } = require("devtools/server/main");
|
||||
var { DebuggerClient } = require("devtools/shared/client/debugger-client");
|
||||
|
||||
const TAB_URL = TEST_URI_ROOT + "doc_event-listeners-01.html";
|
||||
|
||||
add_task(async function() {
|
||||
DebuggerServer.init();
|
||||
DebuggerServer.registerAllActors();
|
||||
|
||||
let transport = DebuggerServer.connectPipe();
|
||||
let client = new DebuggerClient(transport);
|
||||
let [type, traits] = await client.connect();
|
||||
const transport = DebuggerServer.connectPipe();
|
||||
const client = new DebuggerClient(transport);
|
||||
const [type] = await client.connect();
|
||||
|
||||
Assert.equal(type, "browser",
|
||||
"Root actor should identify itself as a browser.");
|
||||
|
||||
let tab = await addTab(TAB_URL);
|
||||
let threadClient = await attachThreadActorForUrl(client, TAB_URL);
|
||||
const tab = await addTab(TAB_URL);
|
||||
const threadClient = await attachThreadActorForUrl(client, TAB_URL);
|
||||
await pauseDebuggee(tab, client, threadClient);
|
||||
await testEventListeners(client, threadClient);
|
||||
|
||||
await client.close();
|
||||
});
|
||||
|
||||
function pauseDebuggee(aTab, aClient, aThreadClient) {
|
||||
let deferred = promise.defer();
|
||||
function pauseDebuggee(tab, client, threadClient) {
|
||||
const deferred = getDeferredPromise().defer();
|
||||
|
||||
aClient.addOneTimeListener("paused", (aEvent, aPacket) => {
|
||||
is(aPacket.type, "paused",
|
||||
client.addOneTimeListener("paused", (event, packet) => {
|
||||
is(packet.type, "paused",
|
||||
"We should now be paused.");
|
||||
is(aPacket.why.type, "debuggerStatement",
|
||||
is(packet.why.type, "debuggerStatement",
|
||||
"The debugger statement was hit.");
|
||||
|
||||
deferred.resolve(aThreadClient);
|
||||
deferred.resolve(threadClient);
|
||||
});
|
||||
|
||||
generateMouseClickInTab(aTab, "content.document.querySelector('button')");
|
||||
generateMouseClickInTab(tab, "content.document.querySelector('button')");
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
async function testEventListeners(aClient, aThreadClient) {
|
||||
let packet = await aThreadClient.eventListeners();
|
||||
async function testEventListeners(client, threadClient) {
|
||||
const packet = await threadClient.eventListeners();
|
||||
|
||||
if (packet.error) {
|
||||
let msg = "Error getting event listeners: " + packet.message;
|
||||
const msg = "Error getting event listeners: " + packet.message;
|
||||
Assert.ok(false, msg);
|
||||
return;
|
||||
}
|
||||
|
||||
is(packet.listeners.length, 3, "Found all event listeners.");
|
||||
|
||||
let listeners = await promise.all(packet.listeners.map(listener => {
|
||||
const lDeferred = promise.defer();
|
||||
aThreadClient.pauseGrip(listener.function).getDefinitionSite(aResponse => {
|
||||
if (aResponse.error) {
|
||||
const msg = "Error getting function definition site: " + aResponse.message;
|
||||
const listeners = await getDeferredPromise().all(packet.listeners.map(listener => {
|
||||
const lDeferred = getDeferredPromise().defer();
|
||||
threadClient.pauseGrip(listener.function).getDefinitionSite(response => {
|
||||
if (response.error) {
|
||||
const msg = "Error getting function definition site: " + response.message;
|
||||
ok(false, msg);
|
||||
lDeferred.reject(msg);
|
||||
return;
|
||||
}
|
||||
listener.function.url = aResponse.source.url;
|
||||
listener.function.url = response.source.url;
|
||||
lDeferred.resolve(listener);
|
||||
});
|
||||
return lDeferred.promise;
|
||||
}));
|
||||
|
||||
let types = [];
|
||||
const types = [];
|
||||
|
||||
for (let l of listeners) {
|
||||
for (const l of listeners) {
|
||||
info("Listener for the " + l.type + " event.");
|
||||
let node = l.node;
|
||||
const node = l.node;
|
||||
ok(node, "There is a node property.");
|
||||
ok(node.object, "There is a node object property.");
|
||||
if (node.selector != "window") {
|
||||
let nodeCount =
|
||||
await ContentTask.spawn(gBrowser.selectedBrowser, node.selector, async (selector) => {
|
||||
return content.document.querySelectorAll(selector).length;
|
||||
});
|
||||
const nodeCount =
|
||||
await ContentTask.spawn(gBrowser.selectedBrowser, node.selector,
|
||||
async (selector) => {
|
||||
return content.document.querySelectorAll(selector).length;
|
||||
});
|
||||
Assert.equal(nodeCount, 1, "The node property is a unique CSS selector.");
|
||||
} else {
|
||||
Assert.ok(true, "The node property is a unique CSS selector.");
|
||||
}
|
||||
|
||||
let func = l.function;
|
||||
const func = l.function;
|
||||
ok(func, "There is a function property.");
|
||||
is(func.type, "object", "The function form is of type 'object'.");
|
||||
is(func.class, "Function", "The function form is of class 'Function'.");
|
||||
|
@ -128,5 +140,5 @@ async function testEventListeners(aClient, aThreadClient) {
|
|||
Assert.ok(types.includes("change"), "Found the change handler.");
|
||||
Assert.ok(types.includes("keyup"), "Found the keyup handler.");
|
||||
|
||||
await aThreadClient.resume();
|
||||
await threadClient.resume();
|
||||
}
|
|
@ -3,54 +3,64 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Tests that the eventListeners request works when bound functions are used as
|
||||
* event listeners.
|
||||
*/
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_event-listeners-03.html";
|
||||
// Import helpers for the workers
|
||||
/* import-globals-from helper_workers.js */
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://mochitests/content/browser/devtools/client/shared/test/helper_workers.js",
|
||||
this);
|
||||
|
||||
var { DebuggerServer } = require("devtools/server/main");
|
||||
var { DebuggerClient } = require("devtools/shared/client/debugger-client");
|
||||
|
||||
const TAB_URL = TEST_URI_ROOT + "doc_event-listeners-03.html";
|
||||
|
||||
add_task(async function() {
|
||||
DebuggerServer.init();
|
||||
DebuggerServer.registerAllActors();
|
||||
|
||||
let transport = DebuggerServer.connectPipe();
|
||||
let client = new DebuggerClient(transport);
|
||||
let [type, traits] = await client.connect();
|
||||
const transport = DebuggerServer.connectPipe();
|
||||
const client = new DebuggerClient(transport);
|
||||
const [type] = await client.connect();
|
||||
|
||||
Assert.equal(type, "browser",
|
||||
"Root actor should identify itself as a browser.");
|
||||
|
||||
let tab = await addTab(TAB_URL);
|
||||
let threadClient = await attachThreadActorForUrl(client, TAB_URL);
|
||||
const tab = await addTab(TAB_URL);
|
||||
const threadClient = await attachThreadActorForUrl(client, TAB_URL);
|
||||
await pauseDebuggee(tab, client, threadClient);
|
||||
await testEventListeners(client, threadClient);
|
||||
await client.close();
|
||||
});
|
||||
|
||||
function pauseDebuggee(aTab, aClient, aThreadClient) {
|
||||
let deferred = promise.defer();
|
||||
function pauseDebuggee(tab, client, threadClient) {
|
||||
const deferred = getDeferredPromise().defer();
|
||||
|
||||
aClient.addOneTimeListener("paused", (aEvent, aPacket) => {
|
||||
is(aPacket.type, "paused",
|
||||
client.addOneTimeListener("paused", (event, packet) => {
|
||||
is(packet.type, "paused",
|
||||
"We should now be paused.");
|
||||
is(aPacket.why.type, "debuggerStatement",
|
||||
is(packet.why.type, "debuggerStatement",
|
||||
"The debugger statement was hit.");
|
||||
|
||||
deferred.resolve(aThreadClient);
|
||||
deferred.resolve(threadClient);
|
||||
});
|
||||
|
||||
generateMouseClickInTab(aTab, "content.document.querySelector('button')");
|
||||
generateMouseClickInTab(tab, "content.document.querySelector('button')");
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
async function testEventListeners(aClient, aThreadClient) {
|
||||
let packet = await aThreadClient.eventListeners();
|
||||
async function testEventListeners(client, threadClient) {
|
||||
const packet = await threadClient.eventListeners();
|
||||
|
||||
if (packet.error) {
|
||||
let msg = "Error getting event listeners: " + aPacket.message;
|
||||
const msg = "Error getting event listeners: " + packet.message;
|
||||
ok(false, msg);
|
||||
return;
|
||||
}
|
||||
|
@ -58,16 +68,16 @@ async function testEventListeners(aClient, aThreadClient) {
|
|||
is(packet.listeners.length, 3,
|
||||
"Found all event listeners.");
|
||||
|
||||
let listeners = await promise.all(packet.listeners.map(listener => {
|
||||
const lDeferred = promise.defer();
|
||||
aThreadClient.pauseGrip(listener.function).getDefinitionSite(aResponse => {
|
||||
if (aResponse.error) {
|
||||
const msg = "Error getting function definition site: " + aResponse.message;
|
||||
const listeners = await getDeferredPromise().all(packet.listeners.map(listener => {
|
||||
const lDeferred = getDeferredPromise().defer();
|
||||
threadClient.pauseGrip(listener.function).getDefinitionSite(response => {
|
||||
if (response.error) {
|
||||
const msg = "Error getting function definition site: " + response.message;
|
||||
ok(false, msg);
|
||||
lDeferred.reject(msg);
|
||||
return;
|
||||
}
|
||||
listener.function.url = aResponse.source.url;
|
||||
listener.function.url = response.source.url;
|
||||
lDeferred.resolve(listener);
|
||||
});
|
||||
return lDeferred.promise;
|
||||
|
@ -75,22 +85,23 @@ async function testEventListeners(aClient, aThreadClient) {
|
|||
|
||||
Assert.equal(listeners.length, 3, "Found three event listeners.");
|
||||
|
||||
for (let l of listeners) {
|
||||
let node = l.node;
|
||||
for (const l of listeners) {
|
||||
const node = l.node;
|
||||
ok(node, "There is a node property.");
|
||||
ok(node.object, "There is a node object property.");
|
||||
|
||||
if (node.selector != "window") {
|
||||
let nodeCount =
|
||||
await ContentTask.spawn(gBrowser.selectedBrowser, node.selector, async (selector) => {
|
||||
return content.document.querySelectorAll(selector).length;
|
||||
});
|
||||
const nodeCount =
|
||||
await ContentTask.spawn(gBrowser.selectedBrowser, node.selector,
|
||||
async (selector) => {
|
||||
return content.document.querySelectorAll(selector).length;
|
||||
});
|
||||
Assert.equal(nodeCount, 1, "The node property is a unique CSS selector.");
|
||||
} else {
|
||||
Assert.ok(true, "The node property is a unique CSS selector.");
|
||||
}
|
||||
|
||||
let func = l.function;
|
||||
const func = l.function;
|
||||
ok(func, "There is a function property.");
|
||||
is(func.type, "object", "The function form is of type 'object'.");
|
||||
is(func.class, "Function", "The function form is of class 'Function'.");
|
||||
|
@ -107,5 +118,5 @@ async function testEventListeners(aClient, aThreadClient) {
|
|||
"Capturing property has the right value.");
|
||||
}
|
||||
|
||||
await aThreadClient.resume();
|
||||
await threadClient.resume();
|
||||
}
|
|
@ -3,12 +3,23 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Tests that the eventListeners request works when there are event handlers
|
||||
* that the debugger cannot unwrap.
|
||||
*/
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_native-event-handler.html";
|
||||
// Import helpers for the workers
|
||||
/* import-globals-from helper_workers.js */
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://mochitests/content/browser/devtools/client/shared/test/helper_workers.js",
|
||||
this);
|
||||
|
||||
var { DebuggerServer } = require("devtools/server/main");
|
||||
var { DebuggerClient } = require("devtools/shared/client/debugger-client");
|
||||
|
||||
const TAB_URL = TEST_URI_ROOT + "doc_native-event-handler.html";
|
||||
|
||||
var gClient;
|
||||
var gTab;
|
||||
|
@ -17,37 +28,37 @@ function test() {
|
|||
DebuggerServer.init();
|
||||
DebuggerServer.registerAllActors();
|
||||
|
||||
let transport = DebuggerServer.connectPipe();
|
||||
const transport = DebuggerServer.connectPipe();
|
||||
gClient = new DebuggerClient(transport);
|
||||
gClient.connect().then(([aType, aTraits]) => {
|
||||
is(aType, "browser",
|
||||
"Root actor should identify itself as a browser.");
|
||||
|
||||
addTab(TAB_URL)
|
||||
.then((aTab) => {
|
||||
gTab = aTab;
|
||||
.then((tab) => {
|
||||
gTab = tab;
|
||||
return attachThreadActorForUrl(gClient, TAB_URL);
|
||||
})
|
||||
.then(pauseDebuggee)
|
||||
.then(testEventListeners)
|
||||
.then(() => gClient.close())
|
||||
.then(finish)
|
||||
.catch(aError => {
|
||||
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
|
||||
.catch(error => {
|
||||
ok(false, "Got an error: " + error.message + "\n" + error.stack);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function pauseDebuggee(aThreadClient) {
|
||||
let deferred = promise.defer();
|
||||
function pauseDebuggee(threadClient) {
|
||||
const deferred = getDeferredPromise().defer();
|
||||
|
||||
gClient.addOneTimeListener("paused", (aEvent, aPacket) => {
|
||||
is(aPacket.type, "paused",
|
||||
gClient.addOneTimeListener("paused", (event, packet) => {
|
||||
is(packet.type, "paused",
|
||||
"We should now be paused.");
|
||||
is(aPacket.why.type, "debuggerStatement",
|
||||
is(packet.why.type, "debuggerStatement",
|
||||
"The debugger statement was hit.");
|
||||
|
||||
deferred.resolve(aThreadClient);
|
||||
deferred.resolve(threadClient);
|
||||
});
|
||||
|
||||
generateMouseClickInTab(gTab, "content.document.querySelector('button')");
|
||||
|
@ -55,12 +66,12 @@ function pauseDebuggee(aThreadClient) {
|
|||
return deferred.promise;
|
||||
}
|
||||
|
||||
function testEventListeners(aThreadClient) {
|
||||
let deferred = promise.defer();
|
||||
function testEventListeners(threadClient) {
|
||||
const deferred = getDeferredPromise().defer();
|
||||
|
||||
aThreadClient.eventListeners(aPacket => {
|
||||
if (aPacket.error) {
|
||||
let msg = "Error getting event listeners: " + aPacket.message;
|
||||
threadClient.eventListeners(packet => {
|
||||
if (packet.error) {
|
||||
const msg = "Error getting event listeners: " + packet.message;
|
||||
ok(false, msg);
|
||||
deferred.reject(msg);
|
||||
return;
|
||||
|
@ -69,13 +80,13 @@ function testEventListeners(aThreadClient) {
|
|||
// There are 2 event listeners in the page: button.onclick, window.onload.
|
||||
// The video element controls listeners are skipped — they cannot be
|
||||
// unwrapped but they shouldn't cause us to throw either.
|
||||
is(aPacket.listeners.length, 2, "Found all event listeners.");
|
||||
aThreadClient.resume(deferred.resolve);
|
||||
is(packet.listeners.length, 2, "Found all event listeners.");
|
||||
threadClient.resume(deferred.resolve);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
registerCleanupFunction(function () {
|
||||
registerCleanupFunction(function() {
|
||||
gClient = null;
|
||||
});
|
|
@ -5,16 +5,14 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
/* import-globals-from helper_addons.js */
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://mochitests/content/browser/devtools/client/shared/test/helper_addons.js",
|
||||
this);
|
||||
|
||||
var { DebuggerServer } = require("devtools/server/main");
|
||||
var { DebuggerClient } = require("devtools/shared/client/debugger-client");
|
||||
|
||||
const chromeRegistry =
|
||||
Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIChromeRegistry);
|
||||
const DEBUGGER_CHROME_URL = "chrome://mochitests/content/browser/devtools/client/shared/test/";
|
||||
const DEBUGGER_CHROME_URI = Services.io.newURI(DEBUGGER_CHROME_URL);
|
||||
|
||||
var { AddonManager } = ChromeUtils.import("resource://gre/modules/AddonManager.jsm", {});
|
||||
|
||||
/**
|
||||
* Make sure the listAddons request works as specified.
|
||||
*/
|
||||
|
@ -118,48 +116,3 @@ registerCleanupFunction(function() {
|
|||
gAddon2 = null;
|
||||
gClient = null;
|
||||
});
|
||||
|
||||
function getAddonURIFromPath(path) {
|
||||
const chromeURI = Services.io.newURI(path, null, DEBUGGER_CHROME_URI);
|
||||
return chromeRegistry.convertChromeURL(chromeURI).QueryInterface(Ci.nsIFileURL);
|
||||
}
|
||||
|
||||
function addTemporaryAddon(path) {
|
||||
const addonFile = getAddonURIFromPath(path).file;
|
||||
info("Installing addon: " + addonFile.path);
|
||||
|
||||
return AddonManager.installTemporaryAddon(addonFile);
|
||||
}
|
||||
|
||||
function getAddonActorForId(client, addonId) {
|
||||
info("Get addon actor for ID: " + addonId);
|
||||
const deferred = promise.defer();
|
||||
|
||||
client.listAddons().then(response => {
|
||||
const addonTargetActor = response.addons.filter(grip => grip.id == addonId).pop();
|
||||
info("got addon actor for ID: " + addonId);
|
||||
deferred.resolve(addonTargetActor);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function removeAddon(addon) {
|
||||
info("Removing addon.");
|
||||
|
||||
const deferred = promise.defer();
|
||||
|
||||
const listener = {
|
||||
onUninstalled: function(uninstalledAddon) {
|
||||
if (uninstalledAddon != addon) {
|
||||
return;
|
||||
}
|
||||
AddonManager.removeAddonListener(listener);
|
||||
deferred.resolve();
|
||||
},
|
||||
};
|
||||
AddonManager.addAddonListener(listener);
|
||||
addon.uninstall();
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Import helpers for the workers
|
||||
/* import-globals-from helper_workers.js */
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://mochitests/content/browser/devtools/client/shared/test/helper_workers.js",
|
||||
this);
|
||||
|
||||
var { DebuggerServer } = require("devtools/server/main");
|
||||
var { DebuggerClient } = require("devtools/shared/client/debugger-client");
|
||||
|
||||
var TAB_URL = EXAMPLE_URL + "doc_listworkers-tab.html";
|
||||
var WORKER1_URL = "code_listworkers-worker1.js";
|
||||
var WORKER2_URL = "code_listworkers-worker2.js";
|
||||
|
||||
add_task(async function test() {
|
||||
DebuggerServer.init();
|
||||
DebuggerServer.registerAllActors();
|
||||
|
||||
const client = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
await connect(client);
|
||||
|
||||
const tab = await addTab(TAB_URL);
|
||||
const { tabs } = await listTabs(client);
|
||||
const [, targetFront] = await attachTarget(client, findTab(tabs, TAB_URL));
|
||||
|
||||
let { workers } = await listWorkers(targetFront);
|
||||
is(workers.length, 0);
|
||||
|
||||
executeSoon(() => {
|
||||
evalInTab(tab, "var worker1 = new Worker('" + WORKER1_URL + "');");
|
||||
});
|
||||
await waitForWorkerListChanged(targetFront);
|
||||
|
||||
({ workers } = await listWorkers(targetFront));
|
||||
is(workers.length, 1);
|
||||
is(workers[0].url, WORKER1_URL);
|
||||
|
||||
executeSoon(() => {
|
||||
evalInTab(tab, "var worker2 = new Worker('" + WORKER2_URL + "');");
|
||||
});
|
||||
await waitForWorkerListChanged(targetFront);
|
||||
|
||||
({ workers } = await listWorkers(targetFront));
|
||||
is(workers.length, 2);
|
||||
is(workers[0].url, WORKER1_URL);
|
||||
is(workers[1].url, WORKER2_URL);
|
||||
|
||||
executeSoon(() => {
|
||||
evalInTab(tab, "worker1.terminate()");
|
||||
});
|
||||
await waitForWorkerListChanged(targetFront);
|
||||
|
||||
({ workers } = await listWorkers(targetFront));
|
||||
is(workers.length, 1);
|
||||
is(workers[0].url, WORKER2_URL);
|
||||
|
||||
executeSoon(() => {
|
||||
evalInTab(tab, "worker2.terminate()");
|
||||
});
|
||||
await waitForWorkerListChanged(targetFront);
|
||||
|
||||
({ workers } = await listWorkers(targetFront));
|
||||
is(workers.length, 0);
|
||||
|
||||
await close(client);
|
||||
finish();
|
||||
});
|
|
@ -3,13 +3,19 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
// Import helpers for the workers
|
||||
/* import-globals-from helper_workers.js */
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://mochitests/content/browser/devtools/client/shared/test/helper_workers.js",
|
||||
this);
|
||||
|
||||
// The following "connectionClosed" rejection should not be left uncaught. This
|
||||
// test has been whitelisted until the issue is fixed.
|
||||
ChromeUtils.import("resource://testing-common/PromiseTestUtils.jsm", this);
|
||||
PromiseTestUtils.expectUncaughtRejection(/[object Object]/);
|
||||
|
||||
var TAB_URL = EXAMPLE_URL + "doc_WorkerTargetActor.attachThread-tab.html";
|
||||
var WORKER_URL = "code_WorkerTargetActor.attachThread-worker.js";
|
||||
const TAB_URL = EXAMPLE_URL + "doc_WorkerTargetActor.attachThread-tab.html";
|
||||
const WORKER_URL = "code_WorkerTargetActor.attachThread-worker.js";
|
||||
|
||||
add_task(async function() {
|
||||
await pushPrefs(["devtools.scratchpad.enabled", true]);
|
||||
|
@ -17,21 +23,21 @@ add_task(async function() {
|
|||
DebuggerServer.init();
|
||||
DebuggerServer.registerAllActors();
|
||||
|
||||
let client = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
const client = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
await connect(client);
|
||||
|
||||
let tab = await addTab(TAB_URL);
|
||||
let { tabs } = await listTabs(client);
|
||||
let [, targetFront] = await attachTarget(client, findTab(tabs, TAB_URL));
|
||||
const tab = await addTab(TAB_URL);
|
||||
const { tabs } = await listTabs(client);
|
||||
const [, targetFront] = await attachTarget(client, findTab(tabs, TAB_URL));
|
||||
|
||||
await listWorkers(targetFront);
|
||||
await createWorkerInTab(tab, WORKER_URL);
|
||||
|
||||
let { workers } = await listWorkers(targetFront);
|
||||
let [, workerTargetFront] = await attachWorker(targetFront,
|
||||
const { workers } = await listWorkers(targetFront);
|
||||
const [, workerTargetFront] = await attachWorker(targetFront,
|
||||
findWorker(workers, WORKER_URL));
|
||||
|
||||
let toolbox = await gDevTools.showToolbox(TargetFactory.forWorker(workerTargetFront),
|
||||
const toolbox = await gDevTools.showToolbox(TargetFactory.forWorker(workerTargetFront),
|
||||
"jsdebugger",
|
||||
Toolbox.HostType.WINDOW);
|
||||
|
||||
|
@ -48,8 +54,8 @@ add_task(async function() {
|
|||
ok(toolbox.win.parent.document.title.includes(WORKER_URL),
|
||||
"worker URL in host title");
|
||||
|
||||
let toolTabs = toolbox.doc.querySelectorAll(".devtools-tab");
|
||||
let activeTools = [...toolTabs].map(tab=>tab.getAttribute("data-id"));
|
||||
const toolTabs = toolbox.doc.querySelectorAll(".devtools-tab");
|
||||
const activeTools = [...toolTabs].map(toolTab => toolTab.getAttribute("data-id"));
|
||||
|
||||
is(activeTools.join(","), "webconsole,jsdebugger,scratchpad",
|
||||
"Correct set of tools supported by worker");
|
|
@ -0,0 +1,3 @@
|
|||
"use strict";
|
||||
|
||||
self.onmessage = function() {};
|
|
@ -0,0 +1,3 @@
|
|||
"use strict";
|
||||
|
||||
self.onmessage = function() {};
|
|
@ -12,10 +12,12 @@
|
|||
<input type="text" onchange="changeHandler()">
|
||||
|
||||
<script type="text/javascript">
|
||||
window.addEventListener("load", function () {
|
||||
"use strict";
|
||||
|
||||
window.addEventListener("load", function() {
|
||||
function initialSetup(event) {
|
||||
debugger;
|
||||
var button = document.querySelector("button");
|
||||
const button = document.querySelector("button");
|
||||
button.onclick = clickHandler;
|
||||
}
|
||||
function clickHandler(event) {
|
||||
|
@ -28,10 +30,10 @@
|
|||
window.foobar = "keyupHandler";
|
||||
}
|
||||
|
||||
var button = document.querySelector("button");
|
||||
const button = document.querySelector("button");
|
||||
button.onclick = initialSetup;
|
||||
|
||||
var input = document.querySelector("input");
|
||||
const input = document.querySelector("input");
|
||||
input.addEventListener("keyup", keyupHandler, true);
|
||||
|
||||
window.changeHandler = changeHandler;
|
|
@ -14,9 +14,11 @@
|
|||
<button id="boundHandleEventClick">boundHandleEventClick</button>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.addEventListener("load", function () {
|
||||
"use strict";
|
||||
|
||||
window.addEventListener("load", function() {
|
||||
function initialSetup(event) {
|
||||
var button = document.getElementById("initialSetup");
|
||||
const button = document.getElementById("initialSetup");
|
||||
button.removeEventListener("click", initialSetup);
|
||||
debugger;
|
||||
}
|
||||
|
@ -26,7 +28,7 @@
|
|||
}
|
||||
|
||||
function handleEventClick() {
|
||||
var button = document.getElementById("handleEventClick");
|
||||
const button = document.getElementById("handleEventClick");
|
||||
// Create a long prototype chain to test for weird edge cases.
|
||||
button.addEventListener("click", Object.create(Object.create(this)));
|
||||
}
|
||||
|
@ -36,7 +38,7 @@
|
|||
};
|
||||
|
||||
function boundHandleEventClick() {
|
||||
var button = document.getElementById("boundHandleEventClick");
|
||||
const button = document.getElementById("boundHandleEventClick");
|
||||
this.handleEvent = this.handleEvent.bind(this);
|
||||
button.addEventListener("click", this);
|
||||
}
|
||||
|
@ -45,15 +47,15 @@
|
|||
window.foobar = "boundHandleEventClick";
|
||||
};
|
||||
|
||||
var button = document.getElementById("clicker");
|
||||
const button = document.getElementById("clicker");
|
||||
// Bind more than once to test for weird edge cases.
|
||||
var boundClicker = clicker.bind(this).bind(this).bind(this);
|
||||
const boundClicker = clicker.bind(this).bind(this).bind(this);
|
||||
button.addEventListener("click", boundClicker);
|
||||
|
||||
new handleEventClick();
|
||||
new boundHandleEventClick();
|
||||
|
||||
var initButton = document.getElementById("initialSetup");
|
||||
const initButton = document.getElementById("initialSetup");
|
||||
initButton.addEventListener("click", initialSetup);
|
||||
}, {once: true});
|
||||
</script>
|
|
@ -12,6 +12,7 @@
|
|||
<button>Click me!</button>
|
||||
|
||||
<script type="text/javascript">
|
||||
"use strict";
|
||||
function runDebuggerStatement() {
|
||||
debugger;
|
||||
}
|
|
@ -6,6 +6,8 @@
|
|||
<meta charset="utf-8">
|
||||
<title>A video element with native event handlers</title>
|
||||
<script type="text/javascript">
|
||||
"use strict";
|
||||
|
||||
function initialSetup(event) {
|
||||
debugger;
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
/* eslint no-unused-vars: [2, {"vars": "local", "args": "none"}] */
|
||||
|
||||
"use strict";
|
||||
|
||||
const chromeRegistry =
|
||||
Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIChromeRegistry);
|
||||
const DEBUGGER_CHROME_URL = "chrome://mochitests/content/browser/devtools/client/shared/test/";
|
||||
const DEBUGGER_CHROME_URI = Services.io.newURI(DEBUGGER_CHROME_URL);
|
||||
|
||||
const EventEmitter = require("devtools/shared/event-emitter");
|
||||
|
||||
var { AddonManager } = ChromeUtils.import("resource://gre/modules/AddonManager.jsm", {});
|
||||
|
||||
/**
|
||||
* Returns a thenable promise
|
||||
* @return {Promise}
|
||||
*/
|
||||
function getDeferredPromise() {
|
||||
// Override promise with deprecated-sync-thenables
|
||||
const promise = require("devtools/shared/deprecated-sync-thenables");
|
||||
return promise;
|
||||
}
|
||||
|
||||
function getAddonURIFromPath(path) {
|
||||
const chromeURI = Services.io.newURI(path, null, DEBUGGER_CHROME_URI);
|
||||
return chromeRegistry.convertChromeURL(chromeURI).QueryInterface(Ci.nsIFileURL);
|
||||
}
|
||||
|
||||
function addTemporaryAddon(path) {
|
||||
const addonFile = getAddonURIFromPath(path).file;
|
||||
info("Installing addon: " + addonFile.path);
|
||||
|
||||
return AddonManager.installTemporaryAddon(addonFile);
|
||||
}
|
||||
|
||||
function getAddonActorForId(client, addonId) {
|
||||
info("Get addon actor for ID: " + addonId);
|
||||
const deferred = getDeferredPromise().defer();
|
||||
|
||||
client.listAddons().then(response => {
|
||||
const addonTargetActor = response.addons.filter(grip => grip.id == addonId).pop();
|
||||
info("got addon actor for ID: " + addonId);
|
||||
deferred.resolve(addonTargetActor);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function removeAddon(addon) {
|
||||
info("Removing addon.");
|
||||
|
||||
const deferred = getDeferredPromise().defer();
|
||||
|
||||
const listener = {
|
||||
onUninstalled: function(uninstalledAddon) {
|
||||
if (uninstalledAddon != addon) {
|
||||
return;
|
||||
}
|
||||
AddonManager.removeAddonListener(listener);
|
||||
deferred.resolve();
|
||||
},
|
||||
};
|
||||
AddonManager.addAddonListener(listener);
|
||||
addon.uninstall();
|
||||
|
||||
return deferred.promise;
|
||||
}
|
|
@ -12,13 +12,16 @@ Services.scriptloader.loadSubScript(
|
|||
|
||||
var { DebuggerServer } = require("devtools/server/main");
|
||||
var { DebuggerClient } = require("devtools/shared/client/debugger-client");
|
||||
|
||||
var { Toolbox } = require("devtools/client/framework/toolbox");
|
||||
|
||||
const FRAME_SCRIPT_URL = getRootDirectory(gTestPath) + "code_frame-script.js";
|
||||
|
||||
var nextId = 0;
|
||||
|
||||
/**
|
||||
* Returns a thenable promise
|
||||
* @return {Promise}
|
||||
*/
|
||||
function getDeferredPromise() {
|
||||
// Override promise with deprecated-sync-thenables
|
||||
const promise = require("devtools/shared/deprecated-sync-thenables");
|
||||
|
@ -68,6 +71,24 @@ function postMessageToWorkerInTab(tab, url, message) {
|
|||
return jsonrpc(tab, "postMessageToWorker", [url, message]);
|
||||
}
|
||||
|
||||
function generateMouseClickInTab(tab, path) {
|
||||
info("Generating mouse click in tab.");
|
||||
|
||||
return jsonrpc(tab, "generateMouseClick", [path]);
|
||||
}
|
||||
|
||||
function evalInTab(tab, string) {
|
||||
info("Evalling string in tab.");
|
||||
|
||||
return jsonrpc(tab, "_eval", [string]);
|
||||
}
|
||||
|
||||
function callInTab(tab, name) {
|
||||
info("Calling function with name '" + name + "' in tab.");
|
||||
|
||||
return jsonrpc(tab, "call", [name, Array.prototype.slice.call(arguments, 2)]);
|
||||
}
|
||||
|
||||
function connect(client) {
|
||||
info("Connecting client.");
|
||||
return client.connect();
|
||||
|
@ -118,6 +139,11 @@ function attachWorker(targetFront, worker) {
|
|||
return targetFront.attachWorker(worker.actor);
|
||||
}
|
||||
|
||||
function waitForWorkerListChanged(targetFront) {
|
||||
info("Waiting for worker list to change.");
|
||||
return targetFront.once("workerListChanged");
|
||||
}
|
||||
|
||||
function attachThread(workerTargetFront, options) {
|
||||
info("Attaching to thread.");
|
||||
return workerTargetFront.attachThread(options);
|
||||
|
@ -221,3 +247,39 @@ this.removeTab = function removeTab(tab, win) {
|
|||
targetBrowser.removeTab(tab);
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
async function attachTargetActorForUrl(client, url) {
|
||||
const grip = await getTargetActorForUrl(client, url);
|
||||
const [ response, front ] = await client.attachTarget(grip.actor);
|
||||
return [grip, response, front];
|
||||
}
|
||||
|
||||
async function attachThreadActorForUrl(client, url) {
|
||||
const [, response] = await attachTargetActorForUrl(client, url);
|
||||
const [, threadClient] = await client.attachThread(response.threadActor);
|
||||
await threadClient.resume();
|
||||
return threadClient;
|
||||
}
|
||||
|
||||
function getTargetActorForUrl(client, url) {
|
||||
const deferred = getDeferredPromise().defer();
|
||||
|
||||
client.listTabs().then(response => {
|
||||
const targetActor = response.tabs.filter(grip => grip.url == url).pop();
|
||||
deferred.resolve(targetActor);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function pushPrefs(...aPrefs) {
|
||||
const deferred = getDeferredPromise().defer();
|
||||
SpecialPowers.pushPrefEnv({"set": aPrefs}, deferred.resolve);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function popPrefs() {
|
||||
const deferred = getDeferredPromise().defer();
|
||||
SpecialPowers.popPrefEnv(deferred.resolve);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "mozilla/dom/MessageManagerGlobal.h"
|
||||
#include "mozilla/dom/ResolveSystemBinding.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "xpcpublic.h"
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/dom/MessageManagerBinding.h"
|
||||
#include "mozilla/dom/ParentProcessMessageManager.h"
|
||||
#include "mozilla/dom/ResolveSystemBinding.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "mozilla/dom/ipc/SharedMap.h"
|
||||
|
||||
|
|
|
@ -47,7 +47,6 @@
|
|||
#include "mozilla/dom/ParentProcessMessageManager.h"
|
||||
#include "mozilla/dom/PermissionMessageUtils.h"
|
||||
#include "mozilla/dom/ProcessMessageManager.h"
|
||||
#include "mozilla/dom/ResolveSystemBinding.h"
|
||||
#include "mozilla/dom/SameProcessMessageQueue.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "mozilla/dom/ToJSValue.h"
|
||||
|
|
|
@ -56,7 +56,6 @@
|
|||
#include "mozilla/dom/XULPopupElementBinding.h"
|
||||
#include "mozilla/dom/XULTextElementBinding.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/ResolveSystemBinding.h"
|
||||
#include "mozilla/dom/WebIDLGlobalNameHash.h"
|
||||
#include "mozilla/dom/WorkerPrivate.h"
|
||||
#include "mozilla/dom/WorkerScope.h"
|
||||
|
@ -3512,29 +3511,6 @@ UnwrapWindowProxyImpl(JSContext* cx,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
SystemGlobalResolve(JSContext* cx, JS::Handle<JSObject*> obj,
|
||||
JS::Handle<jsid> id, bool* resolvedp)
|
||||
{
|
||||
if (!ResolveGlobal(cx, obj, id, resolvedp)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (*resolvedp) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return ResolveSystemBinding(cx, obj, id, resolvedp);
|
||||
}
|
||||
|
||||
bool
|
||||
SystemGlobalEnumerate(JSContext* cx, JS::Handle<JSObject*> obj)
|
||||
{
|
||||
bool ignored = false;
|
||||
return JS_EnumerateStandardClasses(cx, obj) &&
|
||||
ResolveSystemBinding(cx, obj, JSID_VOIDHANDLE, &ignored);
|
||||
}
|
||||
|
||||
template<decltype(JS::NewMapObject) Method>
|
||||
bool
|
||||
GetMaplikeSetlikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj,
|
||||
|
|
|
@ -13576,121 +13576,6 @@ class CGRegisterWorkletBindings(CGAbstractMethod):
|
|||
return CGList(lines, "\n").define()
|
||||
|
||||
|
||||
class CGSystemBindingInitIds(CGAbstractMethod):
|
||||
def __init__(self):
|
||||
CGAbstractMethod.__init__(self, None, 'SystemBindingInitIds', 'bool',
|
||||
[Argument('JSContext*', 'aCx')])
|
||||
|
||||
def definition_body(self):
|
||||
return dedent("""
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!idsInited) {
|
||||
// We can't use range-based for because we need the index to call IdString.
|
||||
for (uint32_t i = 0; i < ArrayLength(properties); ++i) {
|
||||
if (!properties[i].id.init(aCx, IdString(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
idsInited = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
""")
|
||||
|
||||
|
||||
class CGResolveSystemBinding(CGAbstractMethod):
|
||||
def __init__(self):
|
||||
CGAbstractMethod.__init__(self, None, 'ResolveSystemBinding', 'bool',
|
||||
[Argument('JSContext*', 'aCx'),
|
||||
Argument('JS::Handle<JSObject*>', 'aObj'),
|
||||
Argument('JS::Handle<jsid>', 'aId'),
|
||||
Argument('bool*', 'aResolvedp')])
|
||||
|
||||
def definition_body(self):
|
||||
return dedent("""
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(idsInited);
|
||||
|
||||
if (JSID_IS_VOID(aId)) {
|
||||
for (const auto& property : properties) {
|
||||
if (!property.enabled || property.enabled(aCx, aObj)) {
|
||||
if (!property.define(aCx)) {
|
||||
return false;
|
||||
}
|
||||
*aResolvedp = true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const auto& property : properties) {
|
||||
if (property.id == aId) {
|
||||
if (!property.enabled || property.enabled(aCx, aObj)) {
|
||||
if (!property.define(aCx)) {
|
||||
return false;
|
||||
}
|
||||
*aResolvedp = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
""")
|
||||
|
||||
|
||||
class CGMayResolveAsSystemBindingName(CGAbstractMethod):
|
||||
def __init__(self):
|
||||
CGAbstractMethod.__init__(self, None, 'MayResolveAsSystemBindingName', 'bool',
|
||||
[Argument('jsid', 'aId')])
|
||||
|
||||
def definition_body(self):
|
||||
return dedent("""
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(idsInited);
|
||||
|
||||
for (const auto& property : properties) {
|
||||
if (aId == property.id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
""")
|
||||
|
||||
|
||||
class CGGetSystemBindingNames(CGAbstractMethod):
|
||||
def __init__(self):
|
||||
CGAbstractMethod.__init__(self, None, 'GetSystemBindingNames', 'void',
|
||||
[Argument('JSContext*', 'aCx'),
|
||||
Argument('JS::Handle<JSObject*>', 'aObj'),
|
||||
Argument('JS::AutoIdVector&', 'aNames'),
|
||||
Argument('bool', 'aEnumerableOnly'),
|
||||
Argument('mozilla::ErrorResult&', 'aRv')])
|
||||
|
||||
def definition_body(self):
|
||||
return dedent("""
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (aEnumerableOnly) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SystemBindingInitIds(aCx)) {
|
||||
aRv.NoteJSContextException(aCx);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& property : properties) {
|
||||
if (!property.enabled || property.enabled(aCx, aObj)) {
|
||||
if (!aNames.append(property.id)) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
""")
|
||||
|
||||
|
||||
def getGlobalNames(config):
|
||||
names = []
|
||||
for desc in config.getDescriptors(registersGlobalNamesOnWindow=True):
|
||||
|
@ -17259,70 +17144,6 @@ class GlobalGenRoots():
|
|||
# Done.
|
||||
return curr
|
||||
|
||||
@staticmethod
|
||||
def ResolveSystemBinding(config):
|
||||
curr = CGList([], "\n")
|
||||
|
||||
descriptors = config.getDescriptors(hasInterfaceObject=True,
|
||||
isExposedInWindow=True,
|
||||
register=True)
|
||||
properties = [desc.name for desc in descriptors]
|
||||
|
||||
curr.append(CGStringTable("IdString", properties, static=True))
|
||||
|
||||
initValues = []
|
||||
for desc in descriptors:
|
||||
bindingNS = toBindingNamespace(desc.name)
|
||||
if desc.isExposedConditionally():
|
||||
enabled = "%s::ConstructorEnabled" % bindingNS
|
||||
else:
|
||||
enabled = "nullptr"
|
||||
define = "%s::GetConstructorObject" % bindingNS
|
||||
initValues.append("{ %s, %s },\n" % (enabled, define))
|
||||
curr.append(CGGeneric(fill("""
|
||||
struct SystemProperty
|
||||
{
|
||||
WebIDLGlobalNameHash::ConstructorEnabled enabled;
|
||||
ProtoGetter define;
|
||||
PinnedStringId id;
|
||||
};
|
||||
|
||||
static SystemProperty properties[] = {
|
||||
$*{init}
|
||||
};
|
||||
|
||||
static bool idsInited = false;
|
||||
""",
|
||||
init="".join(initValues))))
|
||||
|
||||
curr.append(CGSystemBindingInitIds())
|
||||
curr.append(CGResolveSystemBinding())
|
||||
curr.append(CGMayResolveAsSystemBindingName())
|
||||
curr.append(CGGetSystemBindingNames())
|
||||
|
||||
# Wrap all of that in our namespaces.
|
||||
curr = CGNamespace.build(['mozilla', 'dom'],
|
||||
CGWrapper(curr, post='\n'))
|
||||
curr = CGWrapper(curr, post='\n')
|
||||
|
||||
# Add the includes
|
||||
defineIncludes = [CGHeaders.getDeclarationFilename(desc.interface)
|
||||
for desc in config.getDescriptors(hasInterfaceObject=True,
|
||||
register=True,
|
||||
isExposedInWindow=True)]
|
||||
defineIncludes.append("nsThreadUtils.h") # For NS_IsMainThread
|
||||
defineIncludes.append("js/Id.h") # For jsid
|
||||
defineIncludes.append("mozilla/dom/WebIDLGlobalNameHash.h")
|
||||
|
||||
curr = CGHeaders([], [], [], [], [], defineIncludes,
|
||||
'ResolveSystemBinding', curr)
|
||||
|
||||
# Add include guards.
|
||||
curr = CGIncludeGuard('ResolveSystemBinding', curr)
|
||||
|
||||
# Done.
|
||||
return curr
|
||||
|
||||
@staticmethod
|
||||
def UnionTypes(config):
|
||||
unionTypes = UnionsForFile(config, None)
|
||||
|
|
|
@ -206,5 +206,79 @@ WebIDLGlobalNameHash::GetNames(JSContext* aCx, JS::Handle<JSObject*> aObj,
|
|||
return true;
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool
|
||||
WebIDLGlobalNameHash::ResolveForSystemGlobal(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aObj,
|
||||
JS::Handle<jsid> aId,
|
||||
bool* aResolvedp)
|
||||
{
|
||||
MOZ_ASSERT(JS_IsGlobalObject(aObj));
|
||||
|
||||
// First we try to resolve standard classes.
|
||||
if (!JS_ResolveStandardClass(aCx, aObj, aId, aResolvedp)) {
|
||||
return false;
|
||||
}
|
||||
if (*aResolvedp) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// We don't resolve any non-string entries.
|
||||
if (!JSID_IS_STRING(aId)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// XXX(nika): In the Window case, we unwrap our global object here to handle
|
||||
// XRays. I don't think we ever create xrays to system globals, so I believe
|
||||
// we can skip this step.
|
||||
MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(aObj), "Xrays not supported!");
|
||||
|
||||
// Look up the corresponding entry in the name table, and resolve if enabled.
|
||||
const WebIDLNameTableEntry* entry = GetEntry(JSID_TO_FLAT_STRING(aId));
|
||||
if (entry && (!entry->mEnabled || entry->mEnabled(aCx, aObj))) {
|
||||
if (NS_WARN_IF(!GetPerInterfaceObjectHandle(aCx, entry->mConstructorId,
|
||||
entry->mCreate,
|
||||
/* aDefineOnGlobal = */ true))) {
|
||||
return Throw(aCx, NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
*aResolvedp = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool
|
||||
WebIDLGlobalNameHash::NewEnumerateSystemGlobal(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aObj,
|
||||
JS::AutoIdVector& aProperties,
|
||||
bool aEnumerableOnly)
|
||||
{
|
||||
MOZ_ASSERT(JS_IsGlobalObject(aObj));
|
||||
|
||||
if (!JS_NewEnumerateStandardClasses(aCx, aObj, aProperties, aEnumerableOnly)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// All properties defined on our global are non-enumerable, so we can skip
|
||||
// remaining properties.
|
||||
if (aEnumerableOnly) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Enumerate all entries & add enabled ones.
|
||||
for (size_t i = 0; i < sCount; ++i) {
|
||||
const WebIDLNameTableEntry& entry = sEntries[i];
|
||||
if (!entry.mEnabled || entry.mEnabled(aCx, aObj)) {
|
||||
JSString* str = JS_AtomizeStringN(aCx, sNames + entry.mNameOffset,
|
||||
entry.mNameLength);
|
||||
if (!str || !aProperties.append(NON_INTEGER_ATOM_TO_JSID(str))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -64,6 +64,17 @@ public:
|
|||
NameType aNameType,
|
||||
JS::AutoIdVector& aNames);
|
||||
|
||||
// Helpers for resolving & enumerating names on the system global.
|
||||
// NOTE: These are distinct as it currently lacks a ProtoAndIfaceCache, and is
|
||||
// an XPCOM global.
|
||||
static bool ResolveForSystemGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj,
|
||||
JS::Handle<jsid> aId, bool* aResolvedp);
|
||||
|
||||
static bool NewEnumerateSystemGlobal(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aObj,
|
||||
JS::AutoIdVector& aProperties,
|
||||
bool aEnumerableOnly);
|
||||
|
||||
private:
|
||||
friend struct WebIDLNameTableEntry;
|
||||
|
||||
|
|
|
@ -134,7 +134,6 @@ class WebIDLCodegenManager(LoggingMixin):
|
|||
'RegisterWorkerBindings.h',
|
||||
'RegisterWorkerDebuggerBindings.h',
|
||||
'RegisterWorkletBindings.h',
|
||||
'ResolveSystemBinding.h',
|
||||
'UnionConversions.h',
|
||||
'UnionTypes.h',
|
||||
}
|
||||
|
@ -145,7 +144,6 @@ class WebIDLCodegenManager(LoggingMixin):
|
|||
'RegisterWorkerBindings.cpp',
|
||||
'RegisterWorkerDebuggerBindings.cpp',
|
||||
'RegisterWorkletBindings.cpp',
|
||||
'ResolveSystemBinding.cpp',
|
||||
'UnionTypes.cpp',
|
||||
'PrototypeList.cpp',
|
||||
}
|
||||
|
|
|
@ -36,17 +36,17 @@ FeaturePolicy::InheritPolicy(FeaturePolicy* aParentPolicy)
|
|||
|
||||
RefPtr<FeaturePolicy> dest = this;
|
||||
RefPtr<FeaturePolicy> src = aParentPolicy;
|
||||
nsCOMPtr<nsIPrincipal> origin = mDefaultOrigin;
|
||||
FeaturePolicyUtils::ForEachFeature([dest, src, origin](const char* aFeatureName) {
|
||||
FeaturePolicyUtils::ForEachFeature([dest, src](const char* aFeatureName) {
|
||||
nsString featureName;
|
||||
featureName.AppendASCII(aFeatureName);
|
||||
|
||||
// If the destination has a declared feature (via the HTTP header or 'allow'
|
||||
// attribute) we allow the feature only if both parent FeaturePolicy and this
|
||||
// one allow the current origin.
|
||||
if (dest->HasDeclaredFeature(featureName)) {
|
||||
if (!dest->AllowsFeatureInternal(featureName, origin) ||
|
||||
!src->AllowsFeatureInternal(featureName, origin)) {
|
||||
// attribute) we allow the feature if the destination allows it and the
|
||||
// parent allows its origin or the destinations' one.
|
||||
if (dest->HasDeclaredFeature(featureName) &&
|
||||
dest->AllowsFeatureInternal(featureName, dest->mDefaultOrigin)) {
|
||||
if (!src->AllowsFeatureInternal(featureName, src->mDefaultOrigin) &&
|
||||
!src->AllowsFeatureInternal(featureName, dest->mDefaultOrigin)) {
|
||||
dest->SetInheritedDeniedFeature(featureName);
|
||||
}
|
||||
return;
|
||||
|
@ -54,7 +54,7 @@ FeaturePolicy::InheritPolicy(FeaturePolicy* aParentPolicy)
|
|||
|
||||
// If there was not a declared feature, we allow the feature if the parent
|
||||
// FeaturePolicy allows the current origin.
|
||||
if (!src->AllowsFeatureInternal(featureName, origin)) {
|
||||
if (!src->AllowsFeatureInternal(featureName, dest->mDefaultOrigin)) {
|
||||
dest->SetInheritedDeniedFeature(featureName);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -446,10 +446,12 @@ APZUpdater::RunOnControllerThread(LayersId aLayersId, already_AddRefed<Runnable>
|
|||
{
|
||||
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
|
||||
|
||||
RefPtr<Runnable> task = aTask;
|
||||
|
||||
RunOnUpdaterThread(aLayersId, NewRunnableFunction(
|
||||
"APZUpdater::RunOnControllerThread",
|
||||
&APZThreadUtils::RunOnControllerThread,
|
||||
std::move(aTask)));
|
||||
std::move(task)));
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -41,9 +41,9 @@ APZThreadUtils::AssertOnControllerThread() {
|
|||
}
|
||||
|
||||
/*static*/ void
|
||||
APZThreadUtils::RunOnControllerThread(already_AddRefed<Runnable> aTask)
|
||||
APZThreadUtils::RunOnControllerThread(RefPtr<Runnable>&& aTask)
|
||||
{
|
||||
RefPtr<Runnable> task = aTask;
|
||||
RefPtr<Runnable> task = std::move(aTask);
|
||||
|
||||
if (!sControllerThread) {
|
||||
// Could happen on startup
|
||||
|
|
|
@ -45,7 +45,7 @@ public:
|
|||
* this function is called from the controller thread itself then the task is
|
||||
* run immediately without getting queued.
|
||||
*/
|
||||
static void RunOnControllerThread(already_AddRefed<Runnable> aTask);
|
||||
static void RunOnControllerThread(RefPtr<Runnable>&& aTask);
|
||||
|
||||
/**
|
||||
* Returns true if currently on APZ "controller thread".
|
||||
|
|
|
@ -292,9 +292,12 @@ ClipManager::DefineScrollLayers(const ActiveScrolledRoot* aASR,
|
|||
|
||||
Maybe<ScrollMetadata> metadata = aASR->mScrollableFrame->ComputeScrollMetadata(
|
||||
mManager, aItem->ReferenceFrame(), Nothing(), nullptr);
|
||||
MOZ_ASSERT(metadata);
|
||||
FrameMetrics& metrics = metadata->GetMetrics();
|
||||
if (!metadata) {
|
||||
MOZ_ASSERT_UNREACHABLE("Expected scroll metadata to be available!");
|
||||
return ancestorScrollId;
|
||||
}
|
||||
|
||||
FrameMetrics& metrics = metadata->GetMetrics();
|
||||
if (!metrics.IsScrollable()) {
|
||||
// This item is a scrolling no-op, skip over it in the ASR chain.
|
||||
return ancestorScrollId;
|
||||
|
|
|
@ -79,7 +79,8 @@ interface nsIXPCScriptable : nsISupports
|
|||
|
||||
boolean newEnumerate(in nsIXPConnectWrappedNative wrapper,
|
||||
in JSContextPtr cx, in JSObjectPtr obj,
|
||||
in JSAutoIdVector properties);
|
||||
in JSAutoIdVector properties,
|
||||
in boolean enumerableOnly);
|
||||
|
||||
boolean resolve(in nsIXPConnectWrappedNative wrapper,
|
||||
in JSContextPtr cx, in JSObjectPtr obj, in jsid id,
|
||||
|
|
|
@ -65,7 +65,7 @@ NS_IMETHODIMP XPC_MAP_CLASSNAME::PreCreate(nsISupports* nativeObj, JSContext * c
|
|||
#endif
|
||||
|
||||
#if !((XPC_MAP_FLAGS) & XPC_SCRIPTABLE_WANT_NEWENUMERATE)
|
||||
NS_IMETHODIMP XPC_MAP_CLASSNAME::NewEnumerate(nsIXPConnectWrappedNative* wrapper, JSContext * cx, JSObject * obj, JS::AutoIdVector& properties, bool* _retval)
|
||||
NS_IMETHODIMP XPC_MAP_CLASSNAME::NewEnumerate(nsIXPConnectWrappedNative* wrapper, JSContext * cx, JSObject * obj, JS::AutoIdVector& properties, bool enumerableOnly, bool* _retval)
|
||||
{NS_ERROR("never called"); return NS_ERROR_NOT_IMPLEMENTED;}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -207,6 +207,7 @@ NS_IMETHODIMP
|
|||
nsXPCComponents_Interfaces::NewEnumerate(nsIXPConnectWrappedNative* wrapper,
|
||||
JSContext* cx, JSObject* obj,
|
||||
JS::AutoIdVector& properties,
|
||||
bool enumerableOnly,
|
||||
bool* _retval)
|
||||
{
|
||||
|
||||
|
@ -394,6 +395,7 @@ NS_IMETHODIMP
|
|||
nsXPCComponents_InterfacesByID::NewEnumerate(nsIXPConnectWrappedNative* wrapper,
|
||||
JSContext* cx, JSObject* obj,
|
||||
JS::AutoIdVector& properties,
|
||||
bool enumerableOnly,
|
||||
bool* _retval)
|
||||
{
|
||||
|
||||
|
@ -589,6 +591,7 @@ NS_IMETHODIMP
|
|||
nsXPCComponents_Classes::NewEnumerate(nsIXPConnectWrappedNative* wrapper,
|
||||
JSContext* cx, JSObject* obj,
|
||||
JS::AutoIdVector& properties,
|
||||
bool enumerableOnly,
|
||||
bool* _retval)
|
||||
{
|
||||
nsCOMPtr<nsIComponentRegistrar> compMgr;
|
||||
|
@ -784,6 +787,7 @@ NS_IMETHODIMP
|
|||
nsXPCComponents_ClassesByID::NewEnumerate(nsIXPConnectWrappedNative* wrapper,
|
||||
JSContext* cx, JSObject* obj,
|
||||
JS::AutoIdVector& properties,
|
||||
bool enumerableOnly,
|
||||
bool* _retval)
|
||||
{
|
||||
|
||||
|
@ -993,6 +997,7 @@ NS_IMETHODIMP
|
|||
nsXPCComponents_Results::NewEnumerate(nsIXPConnectWrappedNative* wrapper,
|
||||
JSContext* cx, JSObject* obj,
|
||||
JS::AutoIdVector& properties,
|
||||
bool enumerableOnly,
|
||||
bool* _retval)
|
||||
{
|
||||
const char* name;
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
#include "BackstagePass.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "mozilla/dom/WebIDLGlobalNameHash.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
NS_IMPL_ISUPPORTS(BackstagePass,
|
||||
nsIXPCScriptable,
|
||||
|
@ -18,11 +21,15 @@ NS_IMPL_ISUPPORTS(BackstagePass,
|
|||
nsIScriptObjectPrincipal,
|
||||
nsISupportsWeakReference)
|
||||
|
||||
// XXX(nika): It appears we don't have support for mayresolve hooks in
|
||||
// nsIXPCScriptable, and I don't really want to add it because I'd rather just
|
||||
// kill nsIXPCScriptable alltogether, so we don't use it here.
|
||||
|
||||
// The nsIXPCScriptable map declaration that will generate stubs for us...
|
||||
#define XPC_MAP_CLASSNAME BackstagePass
|
||||
#define XPC_MAP_QUOTED_CLASSNAME "BackstagePass"
|
||||
#define XPC_MAP_FLAGS (XPC_SCRIPTABLE_WANT_RESOLVE | \
|
||||
XPC_SCRIPTABLE_WANT_ENUMERATE | \
|
||||
XPC_SCRIPTABLE_WANT_NEWENUMERATE | \
|
||||
XPC_SCRIPTABLE_WANT_FINALIZE | \
|
||||
XPC_SCRIPTABLE_WANT_PRECREATE | \
|
||||
XPC_SCRIPTABLE_USE_JSSTUB_FOR_ADDPROPERTY | \
|
||||
|
@ -58,17 +65,21 @@ BackstagePass::Resolve(nsIXPConnectWrappedNative* wrapper,
|
|||
{
|
||||
JS::RootedObject obj(cx, objArg);
|
||||
JS::RootedId id(cx, idArg);
|
||||
*_retval = mozilla::dom::SystemGlobalResolve(cx, obj, id, resolvedp);
|
||||
*_retval =
|
||||
WebIDLGlobalNameHash::ResolveForSystemGlobal(cx, obj, id, resolvedp);
|
||||
return *_retval ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
BackstagePass::Enumerate(nsIXPConnectWrappedNative* wrapper, JSContext* cx,
|
||||
JSObject* objArg, bool* _retval)
|
||||
BackstagePass::NewEnumerate(nsIXPConnectWrappedNative* wrapper, JSContext* cx,
|
||||
JSObject* objArg, JS::AutoIdVector& properties,
|
||||
bool enumerableOnly, bool* _retval)
|
||||
{
|
||||
JS::RootedObject obj(cx, objArg);
|
||||
*_retval = mozilla::dom::SystemGlobalEnumerate(cx, obj);
|
||||
return *_retval ? NS_OK : NS_ERROR_FAILURE;
|
||||
*_retval =
|
||||
WebIDLGlobalNameHash::NewEnumerateSystemGlobal(cx, obj, properties,
|
||||
enumerableOnly);
|
||||
return *_retval ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
|
|
|
@ -941,7 +941,8 @@ XPC_WN_NewEnumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
|
|||
}
|
||||
|
||||
bool retval = true;
|
||||
nsresult rv = scr->NewEnumerate(wrapper, cx, obj, properties, &retval);
|
||||
nsresult rv = scr->NewEnumerate(wrapper, cx, obj, properties,
|
||||
enumerableOnly, &retval);
|
||||
if (NS_FAILED(rv)) {
|
||||
return Throw(rv, cx);
|
||||
}
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
#include "mozilla/dom/DOMPrefs.h"
|
||||
#include "mozilla/dom/Exceptions.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/ResolveSystemBinding.h"
|
||||
|
||||
#include "nsDOMMutationObserver.h"
|
||||
#include "nsICycleCollectorListener.h"
|
||||
|
@ -580,10 +579,6 @@ InitClassesWithNewWrappedGlobal(JSContext* aJSContext,
|
|||
// we need to have a principal.
|
||||
MOZ_ASSERT(aPrincipal);
|
||||
|
||||
if (!SystemBindingInitIds(aJSContext)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
InitGlobalObjectOptions(aOptions, aPrincipal);
|
||||
|
||||
// Call into XPCWrappedNative to make a new global object, scope, and global
|
||||
|
|
|
@ -634,6 +634,11 @@ nsFileControlFrame::UpdateDisplayedValue(const nsAString& aValue, bool aNotify)
|
|||
if (auto* textFrame = static_cast<nsTextFrame*>(text->GetPrimaryFrame())) {
|
||||
textFrame->NotifyNativeAnonymousTextnodeChange(oldLength);
|
||||
}
|
||||
nsBlockFrame* label = do_QueryFrame(mTextContent->GetPrimaryFrame());
|
||||
if (label && label->LinesBegin() != label->LinesEnd()) {
|
||||
label->AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
|
||||
label->LinesBegin()->MarkDirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -219,17 +219,10 @@ nsLineLayout::BeginLineReflow(nscoord aICoord, nscoord aBCoord,
|
|||
// property amounts to anything.
|
||||
|
||||
if (0 == mLineNumber && !HasPrevInFlow(mBlockReflowInput->mFrame)) {
|
||||
const nsStyleCoord &textIndent = mStyleText->mTextIndent;
|
||||
nscoord pctBasis = 0;
|
||||
if (textIndent.HasPercent()) {
|
||||
pctBasis =
|
||||
mBlockReflowInput->GetContainingBlockContentISize(aWritingMode);
|
||||
}
|
||||
nscoord indent = textIndent.ComputeCoordPercentCalc(pctBasis);
|
||||
|
||||
mTextIndent = indent;
|
||||
|
||||
psd->mICoord += indent;
|
||||
nscoord pctBasis = mBlockReflowInput->ComputedISize();
|
||||
mTextIndent = nsLayoutUtils::ResolveToLength<false>(mStyleText->mTextIndent,
|
||||
pctBasis);
|
||||
psd->mICoord += mTextIndent;
|
||||
}
|
||||
|
||||
PerFrameData* pfd = NewPerFrameData(mBlockReflowInput->mFrame);
|
||||
|
|
|
@ -2045,7 +2045,7 @@ skip-if(isDebugBuild&&winWidget) == 1330051.svg 1330051-ref.svg
|
|||
== 1375674.html 1375674-ref.html
|
||||
== 1372041.html 1372041-ref.html
|
||||
== 1376092.html 1376092-ref.html
|
||||
fuzzy-if(Android&&debug,1-1,1-1) needs-focus == 1377447-1.html 1377447-1-ref.html
|
||||
fuzzy-if(Android,0-3,0-3) needs-focus == 1377447-1.html 1377447-1-ref.html
|
||||
needs-focus != 1377447-1.html 1377447-2.html
|
||||
== 1379041.html 1379041-ref.html
|
||||
== 1379696.html 1379696-ref.html
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
<!DOCTYPE HTML>
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<html><head>
|
||||
<meta charset="utf-8">
|
||||
<title>Testcase for bug 1500530</title>
|
||||
<style type="text/css">
|
||||
html,body {
|
||||
color:black; background-color:black; font:48pt/1 Arial; padding:0; margin:0;
|
||||
}
|
||||
|
||||
div { width: 100px; height: 20px; margin-top: -2px; background: lime; }
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div></div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,44 @@
|
|||
<!DOCTYPE HTML>
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<html><head>
|
||||
<meta charset="utf-8">
|
||||
<title>Testcase for bug 1500530</title>
|
||||
<style type="text/css">
|
||||
html,body {
|
||||
color:black; background-color:white; font:48pt/1 Arial; padding:0; margin:0;
|
||||
}
|
||||
|
||||
input {
|
||||
font-family: Arial;
|
||||
font-size: 48pt;
|
||||
vertical-align: top;
|
||||
background: lime;
|
||||
}
|
||||
div { text-indent: -24ch; margin-top: -2px; }
|
||||
|
||||
mask {
|
||||
position: absolute;
|
||||
left: 100px; right: 0; top: 0; bottom: 0;
|
||||
background: black;
|
||||
}
|
||||
|
||||
mask2 {
|
||||
position: absolute;
|
||||
left: 0; right: 0; top: 18px; bottom: 0;
|
||||
background: black;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<mask></mask>
|
||||
<mask2></mask2>
|
||||
<div><input type="file"></div>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -5,4 +5,5 @@ fuzzy-if(gtkWidget||webrender,0-1,0-10) fails-if(Android) == background.html bac
|
|||
fuzzy-if(gtkWidget,0-1,0-10) fails-if(Android) == style.html style-ref.xul
|
||||
!= width-clip.html width-clip-ref.html
|
||||
fails-if(Android) == color-inherit.html color-inherit-ref.html
|
||||
fuzzy-if(Android,1-2,2-2) fails-if(webrender) == dynamic-max-width.html dynamic-max-width-ref.html # bug 1496542 for webrender.
|
||||
fuzzy-if(Android,1-2,2-2) fails-if(webrender&&!cocoaWidget) == dynamic-max-width.html dynamic-max-width-ref.html # bug 1496542 for webrender.
|
||||
== label-min-inline-size.html label-min-inline-size-ref.html
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<body>
|
||||
|
||||
<div id="x" style="background: lightgreen; height: 3em; width: 500px; padding: 4px;">
|
||||
<div style="text-indent: 200px; width: 200px; background: yellow;">X</div>
|
||||
<div style="text-indent: 80px; width: 200px; background: yellow;">X</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<title>text-indent test</title>
|
||||
<style type="text/css">
|
||||
div { width: 500px; }
|
||||
p { width: 300px; text-indent: 50px; }
|
||||
p { width: 300px; text-indent: 30px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
@ -486,6 +486,7 @@ input[type="file"] {
|
|||
}
|
||||
|
||||
input[type="file"] > label {
|
||||
display: inline-block;
|
||||
min-inline-size: 12em;
|
||||
padding-inline-start: 5px;
|
||||
text-align: match-parent;
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
[text-indent-percentage-001.xht]
|
||||
expected:
|
||||
if debug and not webrender and e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[text-indent-percentage-002.html]
|
||||
expected: FAIL
|
|
@ -1,6 +0,0 @@
|
|||
[text-indent-percentage-003.html]
|
||||
expected:
|
||||
if debug and not webrender and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
|
||||
if not debug and not webrender and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
|
||||
if debug and not webrender and e10s and (os == "win") and (version == "10.0.15063") and (processor == "x86_64") and (bits == 64): FAIL
|
||||
if not debug and not webrender and e10s and (os == "win") and (version == "10.0.15063") and (processor == "x86_64") and (bits == 64): FAIL
|
|
@ -1,2 +0,0 @@
|
|||
[text-indent-percentage-004.html]
|
||||
expected: FAIL
|
|
@ -1,33 +1,12 @@
|
|||
[feature-policy-frame-policy-allowed-for-self.https.sub.html]
|
||||
[Test frame policy on sandboxed iframe with allow="fullscreen https://www.web-platform.test:8443".]
|
||||
expected: FAIL
|
||||
|
||||
[Test frame policy on sandboxed iframe with no allow attribute.]
|
||||
expected: FAIL
|
||||
|
||||
[Test frame policy on cross origin iframe with allow = "*".]
|
||||
[Test frame policy on data: URL origin iframe with allow = "*".]
|
||||
expected: FAIL
|
||||
|
||||
[Test frame policy on cross origin iframe with allow = "'self' https://www.web-platform.test:8443 https://www.example.com".]
|
||||
expected: FAIL
|
||||
|
||||
[Test frame policy on cross origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen *;".]
|
||||
expected: FAIL
|
||||
|
||||
[Test frame policy on cross origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen 'self';".]
|
||||
expected: FAIL
|
||||
|
||||
[Test frame policy on cross origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen 'none';".]
|
||||
expected: FAIL
|
||||
|
||||
[Test frame policy on cross origin iframe with allow = "'self' https://www.web-platform.test:8443 https://www.example.com" and header policy = "Feature-Policy: fullscreen *;".]
|
||||
expected: FAIL
|
||||
|
||||
[Test frame policy on cross origin iframe with allow = "'self' https://www.web-platform.test:8443 https://www.example.com" and header policy = "Feature-Policy: fullscreen 'self';".]
|
||||
expected: FAIL
|
||||
|
||||
[Test frame policy on cross origin iframe with allow = "'self' https://www.web-platform.test:8443 https://www.example.com" and header policy = "Feature-Policy: fullscreen 'none';".]
|
||||
expected: FAIL
|
||||
|
||||
[Test frame policy on cross origin iframe with allow = "*" and allowfullscreen.]
|
||||
expected: FAIL
|
||||
|
||||
[Test frame policy on cross origin iframe with allow = "'self' https://www.web-platform.test:8443 https://www.example.com" and allowfullscreen.]
|
||||
[Test frame policy on data: URL origin iframe with allow = "*" and allowfullscreen.]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,28 +1,6 @@
|
|||
[feature-policy-frame-policy-allowed-for-some.https.sub.html]
|
||||
[Test frame policy on another cross origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen *;".]
|
||||
[Test frame policy on data: URL cross origin iframe with allow = "*".]
|
||||
expected: FAIL
|
||||
|
||||
[Test frame policy on another cross origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen 'self';".]
|
||||
[Test frame policy on data: URL cross origin iframe with allow = "*" and allowfullscreen.]
|
||||
expected: FAIL
|
||||
|
||||
[Test frame policy on another cross origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen 'none';".]
|
||||
expected: FAIL
|
||||
|
||||
[Test frame policy on another cross origin iframe with allow = "*" and allowfullscreen.]
|
||||
expected: FAIL
|
||||
|
||||
[Test frame policy on another cross origin iframe with allow = "*".]
|
||||
expected: FAIL
|
||||
|
||||
[Test frame policy on another cross origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen *;".]
|
||||
expected: FAIL
|
||||
|
||||
[Test frame policy on another cross origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen 'self';".]
|
||||
expected: FAIL
|
||||
|
||||
[Test frame policy on another cross origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen 'none';".]
|
||||
expected: FAIL
|
||||
|
||||
[Test frame policy on another cross origin iframe with allow = "*" and allowfullscreen.]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -32,13 +32,7 @@
|
|||
margin-top: 0em;
|
||||
}
|
||||
|
||||
div#X
|
||||
{
|
||||
float: left;
|
||||
margin-left: 50%;
|
||||
}
|
||||
|
||||
div#after-X
|
||||
div
|
||||
{
|
||||
background-color: aqua;
|
||||
width: 25%;
|
||||
|
@ -58,9 +52,7 @@
|
|||
the others should all be<br />
|
||||
<em>aligned on the left</em> of the window.</p>
|
||||
|
||||
<div id="X">X</div>
|
||||
|
||||
<div id="after-X"><br />The X on the previous line should be centered across the window.</div>
|
||||
<div><span style="padding-left:50%">X The first X in this sentence should be indented to the center of this block.</span></div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
<em>aligned on the left</em> of the window.
|
||||
</p>
|
||||
<div>
|
||||
X The X on the previous line should be centered across the window.
|
||||
X The first X in this sentence should be indented to the center of this block.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<title>text-indent test</title>
|
||||
<style type="text/css">
|
||||
div { width: 500px; }
|
||||
p { width: 300px; text-indent: 50px; }
|
||||
p { width: 300px; text-indent: 30px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -11,4 +11,4 @@ p { width: 300px; text-indent: 50px; }
|
|||
</div>
|
||||
|
||||
|
||||
</body></html>
|
||||
</body></html>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<body>
|
||||
<p>Test passes if there is no red visible on the page.</p>
|
||||
<div id="parent">
|
||||
<div>X</div>
|
||||
<div style="padding-left: 100px">X</div>
|
||||
</div>
|
||||
<p>Test passes if the following two text blocks look same in terms of margin-left and text-indent respectively.</p>
|
||||
<div>
|
||||
|
@ -31,4 +31,4 @@
|
|||
ABCDEFGHIJKLMNOPQRSTUVWXYZ ABCDEFGHIJKLMNOPQRSTUVWXYZ ABCDEFGHIJKLMNOPQRSTUVWXYZ ABCDEFGHIJKLMNOPQRSTUVWXYZ ABCDEFGHIJKLMNOPQRSTUVWXYZ ABCDEFGHIJKLMNOPQRSTUVWXYZ.<br />
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
<title>CSS Text Test reference</title>
|
||||
<link rel="author" title="Florian Rivoal" href="http://florian.rivoal.net/">
|
||||
<style>
|
||||
body { background: white; }
|
||||
div { padding-left: 50px; }
|
||||
</style>
|
||||
|
||||
<p>Test passes if there is a single black X below and no red.
|
||||
<div>X</div>
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#reference1
|
||||
{
|
||||
color: red;
|
||||
left: 0;
|
||||
left: 100px; /* see comments for #test1 below */
|
||||
position: absolute;
|
||||
top: 0;
|
||||
z-index: -1;
|
||||
|
@ -29,8 +29,8 @@
|
|||
}
|
||||
#test1
|
||||
{
|
||||
text-indent: 50%;
|
||||
margin-left: -50%;
|
||||
margin-left: -50%; /* -50% * 400px = -200px which makes the inline-size of this block 600px */
|
||||
text-indent: 50%; /* 50% * 600px = 300px (which is 100px from the start of #parent due to the negative margin) */
|
||||
}
|
||||
#test2
|
||||
{
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<link rel="match" href="reference/text-indent-percentage-002-ref.html">
|
||||
<meta name="assert" content="Percentages in text-indent refer to width of the element's content box">
|
||||
<style>
|
||||
section { position: absolute; }
|
||||
body { background: white; }
|
||||
section, div {
|
||||
border-right: 10px solid white;
|
||||
margin-right: 10px;
|
||||
|
@ -18,10 +18,7 @@ div {
|
|||
box-sizing: border-box;
|
||||
width: 120px;
|
||||
}
|
||||
.test div { text-indent: 50%; color: red; }
|
||||
.ref div { text-indent: 50px; }
|
||||
.test div { text-indent: 50%; }
|
||||
</style>
|
||||
|
||||
<p>Test passes if there is a single black X below and no red.
|
||||
<section class=test><div>X</div></section>
|
||||
<section class=ref><div>X</div></section>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<link rel="match" href="reference/text-indent-percentage-002-ref.html">
|
||||
<meta name="assert" content="Percentages in text-indent refer to width of the element's content box">
|
||||
<style>
|
||||
section { position: absolute; }
|
||||
body { background: white; }
|
||||
section, div {
|
||||
border-right: 10px solid white;
|
||||
margin-right: 10px;
|
||||
|
@ -18,10 +18,7 @@ div {
|
|||
box-sizing: border-box;
|
||||
width: 120px;
|
||||
}
|
||||
.test div { text-indent: 50%; color: red; overflow: hidden; } /* overflow:hidden should not make any difference, but it does in some browsers */
|
||||
.ref div { text-indent: 50px; }
|
||||
.test div { text-indent: 50%; overflow: hidden; } /* overflow:hidden should not make any difference, but it does in some browsers */
|
||||
</style>
|
||||
|
||||
<p>Test passes if there is a single black X below and no red.
|
||||
<section class=test><div>X</div></section>
|
||||
<section class=ref><div>X</div></section>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<link rel="match" href="reference/text-indent-percentage-002-ref.html">
|
||||
<meta name="assert" content="Percentages in text-indent refer to width of the element's content box, when used in a calc expression">
|
||||
<style>
|
||||
section { position: absolute; }
|
||||
body { background: white; }
|
||||
section, div {
|
||||
border-right: 10px solid white;
|
||||
margin-right: 10px;
|
||||
|
@ -18,10 +18,7 @@ div {
|
|||
box-sizing: border-box;
|
||||
width: 120px;
|
||||
}
|
||||
.test div { text-indent: calc(25px + 25%); color: red; }
|
||||
.ref div { text-indent: 50px; }
|
||||
.test div { text-indent: calc(25px + 25%); }
|
||||
</style>
|
||||
|
||||
<p>Test passes if there is a single black X below and no red.
|
||||
<section class=test><div>X</div></section>
|
||||
<section class=ref><div>X</div></section>
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "mozilla/Unused.h"
|
||||
#include "nsASCIIMask.h"
|
||||
#include "nsCharSeparatedTokenizer.h"
|
||||
#include "nsContentSecurityManager.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIClearDataService.h"
|
||||
#include "nsIHttpChannel.h"
|
||||
|
@ -20,6 +21,7 @@
|
|||
#include "nsIObserverService.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsIScriptError.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsNetUtil.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
@ -194,6 +196,26 @@ ClearSiteData::ClearDataFromChannel(nsIHttpChannel* aChannel)
|
|||
nsresult rv;
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
|
||||
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
|
||||
if (NS_WARN_IF(!ssm)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
rv = ssm->GetChannelResultPrincipal(aChannel, getter_AddRefs(principal));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIContentSecurityManager> csm =
|
||||
do_GetService(NS_CONTENTSECURITYMANAGER_CONTRACTID);
|
||||
|
||||
bool secure;
|
||||
rv = csm->IsOriginPotentiallyTrustworthy(principal, &secure);
|
||||
if (NS_WARN_IF(NS_FAILED(rv)) || !secure) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We want to use the final URI to check if Clear-Site-Data should be allowed
|
||||
// or not.
|
||||
rv = aChannel->GetURI(getter_AddRefs(uri));
|
||||
|
@ -201,27 +223,12 @@ ClearSiteData::ClearDataFromChannel(nsIHttpChannel* aChannel)
|
|||
return;
|
||||
}
|
||||
|
||||
if (!IsSecureURI(uri)) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t flags = ParseHeader(aChannel, uri);
|
||||
if (flags == 0) {
|
||||
// Nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
|
||||
if (NS_WARN_IF(!ssm)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
rv = ssm->GetChannelURIPrincipal(aChannel, getter_AddRefs(principal));
|
||||
if (NS_WARN_IF(NS_FAILED(rv)) || !principal) {
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t cleanFlags = 0;
|
||||
RefPtr<PendingCleanupHolder> holder = new PendingCleanupHolder(aChannel);
|
||||
|
||||
|
@ -264,21 +271,6 @@ ClearSiteData::ClearDataFromChannel(nsIHttpChannel* aChannel)
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ClearSiteData::IsSecureURI(nsIURI* aURI) const
|
||||
{
|
||||
MOZ_ASSERT(aURI);
|
||||
|
||||
bool prioriAuthenticated = false;
|
||||
if (NS_WARN_IF(NS_FAILED(NS_URIChainHasFlags(aURI,
|
||||
nsIProtocolHandler::URI_IS_POTENTIALLY_TRUSTWORTHY,
|
||||
&prioriAuthenticated)))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return prioriAuthenticated;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
ClearSiteData::ParseHeader(nsIHttpChannel* aChannel, nsIURI* aURI) const
|
||||
{
|
||||
|
|
|
@ -39,11 +39,6 @@ private:
|
|||
void
|
||||
ClearDataFromChannel(nsIHttpChannel* aChannel);
|
||||
|
||||
// This method checks if the protocol handler of the URI has the
|
||||
// URI_IS_POTENTIALLY_TRUSTWORTHY flag.
|
||||
bool
|
||||
IsSecureURI(nsIURI* aURI) const;
|
||||
|
||||
// From the Clear-Site-Data header, it returns a bitmap with Type values.
|
||||
uint32_t
|
||||
ParseHeader(nsIHttpChannel* aChannel, nsIURI* aURI) const;
|
||||
|
|
|
@ -668,6 +668,10 @@ class MozFindbar extends XULElement {
|
|||
this.setAttribute("noanim", true);
|
||||
this.hidden = true;
|
||||
|
||||
let event = document.createEvent("Events");
|
||||
event.initEvent("findbarclose", true, false);
|
||||
this.dispatchEvent(event);
|
||||
|
||||
// 'focusContent()' iterates over all listeners in the chrome
|
||||
// process, so we need to call it from here.
|
||||
this.browser.finder.focusContent();
|
||||
|
|
|
@ -14,18 +14,7 @@
|
|||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:xbl="http://www.mozilla.org/xbl">
|
||||
|
||||
<binding id="tree-base" extends="chrome://global/content/bindings/general.xml#basecontrol">
|
||||
<implementation>
|
||||
<method name="_isAccelPressed">
|
||||
<parameter name="aEvent"/>
|
||||
<body><![CDATA[
|
||||
return aEvent.getModifierState("Accel");
|
||||
]]></body>
|
||||
</method>
|
||||
</implementation>
|
||||
</binding>
|
||||
|
||||
<binding id="tree" extends="chrome://global/content/bindings/tree.xml#tree-base">
|
||||
<binding id="tree" extends="chrome://global/content/bindings/general.xml#basecontrol">
|
||||
<content hidevscroll="true" hidehscroll="true" clickthrough="never">
|
||||
<children includes="treecols"/>
|
||||
<xul:stack class="tree-stack" flex="1">
|
||||
|
@ -416,7 +405,7 @@
|
|||
if (this.view.rowCount == 0)
|
||||
return;
|
||||
|
||||
if (this._isAccelPressed(event) && this.view.selection.single) {
|
||||
if (event.getModifierState("Accel") && this.view.selection.single) {
|
||||
this.treeBoxObject.scrollByLines(offset);
|
||||
return;
|
||||
}
|
||||
|
@ -428,7 +417,7 @@
|
|||
c = edge;
|
||||
}
|
||||
|
||||
if (!this._isAccelPressed(event))
|
||||
if (!event.getModifierState("Accel"))
|
||||
this.view.selection.timedSelect(c, this._selectDelay);
|
||||
else // Ctrl+Up/Down moves the anchor without selecting
|
||||
this.currentIndex = c;
|
||||
|
@ -469,7 +458,7 @@
|
|||
|
||||
// Extend the selection from the existing pivot, if any
|
||||
this.view.selection.rangedSelect(-1, c + offset,
|
||||
this._isAccelPressed(event));
|
||||
event.getModifierState("Accel"));
|
||||
this.treeBoxObject.ensureRowIsVisible(c + offset);
|
||||
|
||||
]]>
|
||||
|
@ -487,7 +476,7 @@
|
|||
if (this.view.rowCount == 0)
|
||||
return;
|
||||
|
||||
if (this.pageUpOrDownMovesSelection == this._isAccelPressed(event)) {
|
||||
if (this.pageUpOrDownMovesSelection == event.getModifierState("Accel")) {
|
||||
this.treeBoxObject.scrollByPages(offset);
|
||||
return;
|
||||
}
|
||||
|
@ -537,7 +526,7 @@
|
|||
return;
|
||||
|
||||
if (this.view.rowCount == 1 && !this.view.selection.isSelected(0) &&
|
||||
!(this.pageUpOrDownMovesSelection == this._isAccelPressed(event))) {
|
||||
!(this.pageUpOrDownMovesSelection == event.getModifierState("Accel"))) {
|
||||
this.view.selection.timedSelect(0, this._selectDelay);
|
||||
return;
|
||||
}
|
||||
|
@ -562,7 +551,7 @@
|
|||
this.treeBoxObject.ensureRowIsVisible(i > edge ? edge : i);
|
||||
}
|
||||
// Extend the selection from the existing pivot, if any
|
||||
this.view.selection.rangedSelect(-1, i > edge ? edge : i, this._isAccelPressed(event));
|
||||
this.view.selection.rangedSelect(-1, i > edge ? edge : i, event.getModifierState("Accel"));
|
||||
|
||||
} else {
|
||||
|
||||
|
@ -571,7 +560,7 @@
|
|||
this.treeBoxObject.ensureRowIsVisible(i);
|
||||
}
|
||||
// Extend the selection from the existing pivot, if any
|
||||
this.view.selection.rangedSelect(-1, i, this._isAccelPressed(event));
|
||||
this.view.selection.rangedSelect(-1, i, event.getModifierState("Accel"));
|
||||
}
|
||||
|
||||
]]>
|
||||
|
@ -594,7 +583,7 @@
|
|||
}
|
||||
|
||||
// Normal behaviour is to select the first/last row
|
||||
if (!this._isAccelPressed(event))
|
||||
if (!event.getModifierState("Accel"))
|
||||
this.view.selection.timedSelect(edge, this._selectDelay);
|
||||
|
||||
// In a multiselect tree Ctrl+Home/End moves the anchor
|
||||
|
@ -627,7 +616,7 @@
|
|||
|
||||
// Extend the selection from the existing pivot, if any.
|
||||
// -1 doesn't work here, so using currentIndex instead
|
||||
this.view.selection.rangedSelect(this.currentIndex, edge, this._isAccelPressed(event));
|
||||
this.view.selection.rangedSelect(this.currentIndex, edge, event.getModifierState("Accel"));
|
||||
|
||||
this.treeBoxObject.ensureRowIsVisible(edge);
|
||||
]]>
|
||||
|
@ -906,12 +895,12 @@
|
|||
if (event.charCode == " ".charCodeAt(0)) {
|
||||
var c = this.currentIndex;
|
||||
if (!this.view.selection.isSelected(c) ||
|
||||
(!this.view.selection.single && this._isAccelPressed(event))) {
|
||||
(!this.view.selection.single && event.getModifierState("Accel"))) {
|
||||
this.view.selection.toggleSelect(c);
|
||||
event.preventDefault();
|
||||
}
|
||||
} else if (!this.disableKeyNavigation && event.charCode > 0 &&
|
||||
!event.altKey && !this._isAccelPressed(event) &&
|
||||
!event.altKey && !event.getModifierState("Accel") &&
|
||||
!event.metaKey && !event.ctrlKey) {
|
||||
var l = this._keyNavigate(event);
|
||||
if (l >= 0) {
|
||||
|
@ -944,7 +933,7 @@
|
|||
</implementation>
|
||||
</binding>
|
||||
|
||||
<binding id="treerows" extends="chrome://global/content/bindings/tree.xml#tree-base">
|
||||
<binding id="treerows" extends="chrome://global/content/bindings/general.xml#basecontrol">
|
||||
<content>
|
||||
<xul:hbox flex="1" class="tree-bodybox">
|
||||
<children/>
|
||||
|
@ -983,7 +972,7 @@
|
|||
</handlers>
|
||||
</binding>
|
||||
|
||||
<binding id="treebody" extends="chrome://global/content/bindings/tree.xml#tree-base">
|
||||
<binding id="treebody" extends="chrome://global/content/bindings/general.xml#basecontrol">
|
||||
<implementation>
|
||||
<constructor>
|
||||
if ("_ensureColumnOrder" in this.parentNode)
|
||||
|
@ -1001,7 +990,7 @@
|
|||
<![CDATA[
|
||||
if (this.parentNode.disabled)
|
||||
return;
|
||||
if (((!this._isAccelPressed(event) ||
|
||||
if (((!event.getModifierState("Accel") ||
|
||||
!this.parentNode.pageUpOrDownMovesSelection) &&
|
||||
!event.shiftKey && !event.metaKey) ||
|
||||
this.parentNode.view.selection.single) {
|
||||
|
@ -1071,7 +1060,7 @@
|
|||
}
|
||||
|
||||
if (!view.selection.single) {
|
||||
var augment = this._isAccelPressed(event);
|
||||
var augment = event.getModifierState("Accel");
|
||||
if (event.shiftKey) {
|
||||
view.selection.rangedSelect(-1, cell.row, augment);
|
||||
b.ensureRowIsVisible(cell.row);
|
||||
|
@ -1135,7 +1124,7 @@
|
|||
</binding>
|
||||
|
||||
<binding id="treecol-base"
|
||||
extends="chrome://global/content/bindings/tree.xml#tree-base">
|
||||
extends="chrome://global/content/bindings/general.xml#basecontrol">
|
||||
<implementation>
|
||||
<constructor>
|
||||
this.parentNode.parentNode._columnsDirty = true;
|
||||
|
@ -1353,7 +1342,7 @@
|
|||
</binding>
|
||||
|
||||
<binding id="columnpicker" display="xul:button"
|
||||
extends="chrome://global/content/bindings/tree.xml#tree-base">
|
||||
extends="chrome://global/content/bindings/general.xml#basecontrol">
|
||||
<content>
|
||||
<xul:image class="tree-columnpicker-icon"/>
|
||||
<xul:menupopup anonid="popup">
|
||||
|
|
|
@ -87,7 +87,7 @@ class StableHashTableInfo
|
|||
// Get an existing key in the table.
|
||||
KeyInfo* FindKeyInfo(HashNumber aOriginalHash, const void* aKey, HashInfo** aHashInfo = nullptr) {
|
||||
HashToKeyMap::iterator iter = mHashToKey.find(aOriginalHash);
|
||||
MOZ_ASSERT(iter != mHashToKey.end());
|
||||
MOZ_RELEASE_ASSERT(iter != mHashToKey.end());
|
||||
|
||||
HashInfo* hashInfo = iter->second.get();
|
||||
for (KeyInfo& keyInfo : hashInfo->mKeys) {
|
||||
|
@ -114,7 +114,7 @@ public:
|
|||
}
|
||||
|
||||
~StableHashTableInfo() {
|
||||
MOZ_ASSERT(mHashToKey.empty());
|
||||
MOZ_RELEASE_ASSERT(mHashToKey.empty());
|
||||
DeallocateMemory(mCallbackStorage, CallbackStorageCapacity, MemoryKind::Tracked);
|
||||
}
|
||||
|
||||
|
@ -175,7 +175,7 @@ public:
|
|||
|
||||
HashNumber GetOriginalHashNumber(const void* aKey) {
|
||||
KeyToHashMap::iterator iter = mKeyToHash.find(aKey);
|
||||
MOZ_ASSERT(iter != mKeyToHash.end());
|
||||
MOZ_RELEASE_ASSERT(iter != mKeyToHash.end());
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
|
@ -212,7 +212,7 @@ public:
|
|||
}
|
||||
|
||||
HashNumber GetLastNewHash(const void* aKey) {
|
||||
MOZ_ASSERT(aKey == mLastKey);
|
||||
MOZ_RELEASE_ASSERT(aKey == mLastKey);
|
||||
return mLastNewHash;
|
||||
}
|
||||
|
||||
|
@ -302,7 +302,7 @@ WrapPLHashAllocEntry(void* aAllocPrivate, const void* aKey)
|
|||
// the hashes are supplied directly to the table and we don't have a chance
|
||||
// to modify them. Fortunately, none of these tables are iterated in a way
|
||||
// that can cause the replay to diverge, so just punt in these cases.
|
||||
MOZ_ASSERT(info->IsEmpty());
|
||||
MOZ_RELEASE_ASSERT(info->IsEmpty());
|
||||
}
|
||||
|
||||
return info->mAllocOps
|
||||
|
|
|
@ -135,10 +135,21 @@ Lock::Find(void* aNativeLock)
|
|||
// should be generated right now. Doing things in this order avoids
|
||||
// reentrancy issues when initializing the thread-local state used by
|
||||
// these calls.
|
||||
if (AreThreadEventsPassedThrough() || HasDivergedFromRecording()) {
|
||||
Lock* lock = iter->second;
|
||||
if (AreThreadEventsPassedThrough()) {
|
||||
return nullptr;
|
||||
}
|
||||
return iter->second;
|
||||
if (HasDivergedFromRecording()) {
|
||||
// When diverged from the recording, don't allow uses of locks that are
|
||||
// held by idling threads that have not diverged from the recording.
|
||||
// This will cause the process to deadlock, so rewind instead.
|
||||
if (lock->mOwner && Thread::GetById(lock->mOwner)->IsIdle()) {
|
||||
EnsureNotDivergedFromRecording();
|
||||
Unreachable();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
return lock;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,6 +182,9 @@ Lock::Enter()
|
|||
while (thread->Id() != acquires->mNextOwner && !thread->MaybeDivergeFromRecording()) {
|
||||
Thread::Wait();
|
||||
}
|
||||
if (!thread->HasDivergedFromRecording()) {
|
||||
mOwner = thread->Id();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -179,6 +193,8 @@ Lock::Exit()
|
|||
{
|
||||
Thread* thread = Thread::Current();
|
||||
if (IsReplaying() && !thread->HasDivergedFromRecording()) {
|
||||
mOwner = 0;
|
||||
|
||||
// Notify the next owner before releasing the lock.
|
||||
LockAcquires* acquires = gLockAcquires.Get(mId);
|
||||
acquires->ReadAndNotifyNextOwner(thread);
|
||||
|
|
|
@ -30,9 +30,12 @@ class Lock
|
|||
// Unique ID for this lock.
|
||||
size_t mId;
|
||||
|
||||
// When replaying, any thread owning this lock as part of the recording.
|
||||
Atomic<size_t, SequentiallyConsistent, Behavior::DontPreserve> mOwner;
|
||||
|
||||
public:
|
||||
explicit Lock(size_t aId)
|
||||
: mId(aId)
|
||||
: mId(aId), mOwner(0)
|
||||
{
|
||||
MOZ_ASSERT(aId);
|
||||
}
|
||||
|
|
|
@ -662,6 +662,17 @@ HandleDirtyMemoryFault(uint8_t* aAddress)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
MemoryRangeIsTracked(void* aAddress, size_t aSize)
|
||||
{
|
||||
for (uint8_t* ptr = PageBase(aAddress); ptr < (uint8_t*) aAddress + aSize; ptr += PageSize) {
|
||||
if (!IsTrackedAddress(ptr, nullptr)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
UnrecoverableSnapshotFailure()
|
||||
{
|
||||
|
|
|
@ -50,6 +50,11 @@ void RegisterAllocatedMemory(void* aBaseAddress, size_t aSize, MemoryKind aKind)
|
|||
// been reached.
|
||||
void AddInitialUntrackedMemoryRegion(uint8_t* aBase, size_t aSize);
|
||||
|
||||
// Return whether a range of memory is in a tracked region. This excludes
|
||||
// memory that was allocated after the last checkpoint and is not write
|
||||
// protected.
|
||||
bool MemoryRangeIsTracked(void* aAddress, size_t aSize);
|
||||
|
||||
// Initialize the memory snapshots system.
|
||||
void InitializeMemorySnapshots();
|
||||
|
||||
|
|
|
@ -163,7 +163,7 @@ ProcessMiddlemanCall(const char* aInputData, size_t aInputSize,
|
|||
call->DecodeInput(inputStream);
|
||||
|
||||
const Redirection& redirection = gRedirections[call->mCallId];
|
||||
MOZ_RELEASE_ASSERT(gRedirections[call->mCallId].mMiddlemanCall);
|
||||
MOZ_RELEASE_ASSERT(redirection.mMiddlemanCall);
|
||||
|
||||
CallArguments arguments;
|
||||
call->mArguments.CopyTo(&arguments);
|
||||
|
@ -323,6 +323,9 @@ void
|
|||
Middleman_SystemOutput(MiddlemanCallContext& aCx, const void** aOutput, bool aUpdating)
|
||||
{
|
||||
if (!*aOutput) {
|
||||
if (aCx.mPhase == MiddlemanCallPhase::MiddlemanOutput) {
|
||||
aCx.mCall->SetMiddlemanValue(*aOutput);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -354,7 +354,7 @@ void ResetMiddlemanCalls();
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Capture the contents of an input buffer at BufferArg with element count at CountArg.
|
||||
template <size_t BufferArg, size_t CountArg, typename ElemType>
|
||||
template <size_t BufferArg, size_t CountArg, typename ElemType = char>
|
||||
static inline void
|
||||
Middleman_Buffer(MiddlemanCallContext& aCx)
|
||||
{
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "InfallibleVector.h"
|
||||
#include "MiddlemanCall.h"
|
||||
#include "ipc/ParentInternal.h"
|
||||
#include "mozilla/Sprintf.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
@ -99,6 +100,11 @@ RecordReplayInterceptCall(int aCallId, CallArguments* aArguments)
|
|||
}
|
||||
}
|
||||
|
||||
if (parent::InRepaintStressMode()) {
|
||||
// We're about to crash, so print out the name of the call that failed.
|
||||
Print("Could not perform middleman call: %s\n", redirection.mName);
|
||||
}
|
||||
|
||||
// Calling any redirection which performs the standard steps will cause
|
||||
// debugger operations that have diverged from the recording to fail.
|
||||
EnsureNotDivergedFromRecording();
|
||||
|
|
|
@ -152,7 +152,7 @@ namespace recordreplay {
|
|||
MACRO(pthread_cond_wait, nullptr, Preamble_pthread_cond_wait) \
|
||||
MACRO(pthread_cond_timedwait, nullptr, Preamble_pthread_cond_timedwait) \
|
||||
MACRO(pthread_cond_timedwait_relative_np, nullptr, Preamble_pthread_cond_timedwait_relative_np) \
|
||||
MACRO(pthread_create, nullptr, Preamble_pthread_create, nullptr, Preamble_SetError) \
|
||||
MACRO(pthread_create, nullptr, Preamble_pthread_create) \
|
||||
MACRO(pthread_join, nullptr, Preamble_pthread_join) \
|
||||
MACRO(pthread_mutex_init, nullptr, Preamble_pthread_mutex_init) \
|
||||
MACRO(pthread_mutex_destroy, nullptr, Preamble_pthread_mutex_destroy) \
|
||||
|
@ -291,6 +291,7 @@ namespace recordreplay {
|
|||
MACRO(CFRunLoopGetCurrent, RR_ScalarRval) \
|
||||
MACRO(CFRunLoopRemoveSource) \
|
||||
MACRO(CFRunLoopSourceCreate, RR_ScalarRval, Preamble_CFRunLoopSourceCreate) \
|
||||
MACRO(CFRunLoopSourceInvalidate) \
|
||||
MACRO(CFRunLoopSourceSignal) \
|
||||
MACRO(CFRunLoopWakeUp) \
|
||||
MACRO(CFStringAppendCharacters) \
|
||||
|
@ -328,9 +329,9 @@ namespace recordreplay {
|
|||
MACRO(CFURLCreateWithString, RR_ScalarRval) \
|
||||
MACRO(CFURLGetFileSystemRepresentation, RR_Compose<RR_ScalarRval, RR_WriteBuffer<2, 3>>) \
|
||||
MACRO(CFURLGetFSRef, RR_Compose<RR_ScalarRval, RR_WriteBufferFixedSize<1, sizeof(FSRef)>>) \
|
||||
MACRO(CFUUIDCreate, RR_ScalarRval) \
|
||||
MACRO(CFUUIDCreate, RR_ScalarRval, nullptr, Middleman_CreateCFTypeRval) \
|
||||
MACRO(CFUUIDCreateString, RR_ScalarRval) \
|
||||
MACRO(CFUUIDGetUUIDBytes, RR_ComplexScalarRval) \
|
||||
MACRO(CFUUIDGetUUIDBytes, RR_ComplexScalarRval, nullptr, Middleman_CFTypeArg<0>) \
|
||||
MACRO(CGAffineTransformConcat, RR_OversizeRval<sizeof(CGAffineTransform)>) \
|
||||
MACRO(CGBitmapContextCreateImage, RR_ScalarRval) \
|
||||
MACRO(CGBitmapContextCreateWithData, \
|
||||
|
@ -386,8 +387,9 @@ namespace recordreplay {
|
|||
Middleman_StackArgumentData<sizeof(CGAffineTransform)>>) \
|
||||
MACRO(CGContextScaleCTM, nullptr, nullptr, Middleman_UpdateCFTypeArg<0>) \
|
||||
MACRO(CGContextTranslateCTM, nullptr, nullptr, Middleman_UpdateCFTypeArg<0>) \
|
||||
MACRO(CGDataProviderCreateWithData, RR_Compose<RR_ScalarRval, RR_CGDataProviderCreateWithData>) \
|
||||
MACRO(CGDataProviderRelease) \
|
||||
MACRO(CGDataProviderCreateWithData, RR_Compose<RR_ScalarRval, RR_CGDataProviderCreateWithData>, \
|
||||
nullptr, Middleman_CGDataProviderCreateWithData) \
|
||||
MACRO(CGDataProviderRelease, nullptr, nullptr, nullptr, Preamble_Veto<0>) \
|
||||
MACRO(CGDisplayCopyColorSpace, RR_ScalarRval) \
|
||||
MACRO(CGDisplayIOServicePort, RR_ScalarRval) \
|
||||
MACRO(CGEventSourceCounterForEventType, RR_ScalarRval) \
|
||||
|
@ -398,7 +400,8 @@ namespace recordreplay {
|
|||
MACRO(CGFontCopyVariations, RR_ScalarRval, nullptr, \
|
||||
Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CreateCFTypeRval>) \
|
||||
MACRO(CGFontCreateCopyWithVariations, RR_ScalarRval) \
|
||||
MACRO(CGFontCreateWithDataProvider, RR_ScalarRval) \
|
||||
MACRO(CGFontCreateWithDataProvider, RR_ScalarRval, nullptr, \
|
||||
Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CreateCFTypeRval>) \
|
||||
MACRO(CGFontCreateWithFontName, RR_ScalarRval, nullptr, \
|
||||
Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CreateCFTypeRval>) \
|
||||
MACRO(CGFontCreateWithPlatformFont, RR_ScalarRval) \
|
||||
|
@ -442,10 +445,14 @@ namespace recordreplay {
|
|||
Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CreateCFTypeRval>) \
|
||||
MACRO(CTFontCopyTable, RR_ScalarRval, nullptr, \
|
||||
Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CreateCFTypeRval>) \
|
||||
MACRO(CTFontCopyVariationAxes, RR_ScalarRval) \
|
||||
MACRO(CTFontCopyVariationAxes, RR_ScalarRval, nullptr, \
|
||||
Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CreateCFTypeRval>) \
|
||||
MACRO(CTFontCreateForString, RR_ScalarRval, nullptr, \
|
||||
Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CFTypeArg<1>, Middleman_CreateCFTypeRval>) \
|
||||
MACRO(CTFontCreatePathForGlyph, RR_ScalarRval) \
|
||||
MACRO(CTFontCreatePathForGlyph, RR_ScalarRval, nullptr, \
|
||||
Middleman_Compose<Middleman_CFTypeArg<0>, \
|
||||
Middleman_BufferFixedSize<2, sizeof(CGAffineTransform)>, \
|
||||
Middleman_CreateCFTypeRval>) \
|
||||
MACRO(CTFontCreateWithFontDescriptor, RR_ScalarRval, nullptr, \
|
||||
Middleman_Compose<Middleman_CFTypeArg<0>, \
|
||||
Middleman_BufferFixedSize<1, sizeof(CGAffineTransform)>, \
|
||||
|
@ -589,6 +596,11 @@ namespace recordreplay {
|
|||
MACRO(ReleaseEvent, RR_ScalarRval) \
|
||||
MACRO(RemoveEventFromQueue, RR_ScalarRval) \
|
||||
MACRO(RetainEvent, RR_ScalarRval) \
|
||||
MACRO(SCDynamicStoreCopyProxies, RR_ScalarRval) \
|
||||
MACRO(SCDynamicStoreCreate, RR_ScalarRval) \
|
||||
MACRO(SCDynamicStoreCreateRunLoopSource, RR_ScalarRval) \
|
||||
MACRO(SCDynamicStoreKeyCreateProxies, RR_ScalarRval) \
|
||||
MACRO(SCDynamicStoreSetNotificationKeys, RR_ScalarRval) \
|
||||
MACRO(SendEventToEventTarget, RR_ScalarRval) \
|
||||
/* These are not public APIs, but other redirected functions may be aliases for */ \
|
||||
/* these which are dynamically installed on the first call in a way that our */ \
|
||||
|
@ -689,20 +701,6 @@ ReplayInvokeCallback(size_t aCallbackId)
|
|||
// Middleman Call Helpers
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static bool
|
||||
TestObjCObjectClass(id aObj, const char* aName)
|
||||
{
|
||||
Class cls = object_getClass(aObj);
|
||||
while (cls) {
|
||||
const char* className = class_getName(cls);
|
||||
if (!strcmp(className, aName)) {
|
||||
return true;
|
||||
}
|
||||
cls = class_getSuperclass(cls);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Inputs that originate from static data in the replaying process itself
|
||||
// rather than from previous middleman calls.
|
||||
enum class ObjCInputKind {
|
||||
|
@ -710,6 +708,14 @@ enum class ObjCInputKind {
|
|||
ConstantString,
|
||||
};
|
||||
|
||||
// Internal layout of a constant compile time CFStringRef.
|
||||
struct CFConstantString {
|
||||
Class mClass;
|
||||
size_t mStuff;
|
||||
char* mData;
|
||||
size_t mLength;
|
||||
};
|
||||
|
||||
// Capture an Objective C or CoreFoundation input to a call, which may come
|
||||
// either from another middleman call, or from static data in the replaying
|
||||
// process.
|
||||
|
@ -753,22 +759,23 @@ Middleman_ObjCInput(MiddlemanCallContext& aCx, id* aThingPtr)
|
|||
}
|
||||
|
||||
// Watch for constant compile time strings baked into the generated code or
|
||||
// stored in system libraries. We can crash here if the object came from
|
||||
// e.g. a replayed pointer from the recording, as can happen if not enough
|
||||
// redirections have middleman call hooks. We could do better here to make
|
||||
// sure the pointer looks like it could be a constant string, but it seems
|
||||
// better and simpler to crash more reliably here than mask problems due to
|
||||
// missing middleman call hooks.
|
||||
if (TestObjCObjectClass(*aThingPtr, "NSString")) {
|
||||
AutoPassThroughThreadEvents pt;
|
||||
CFIndex len = CFStringGetLength((CFStringRef)*aThingPtr);
|
||||
InfallibleVector<UniChar> buffer;
|
||||
buffer.appendN(0, len);
|
||||
CFStringGetCharacters((CFStringRef)*aThingPtr, { 0, len }, buffer.begin());
|
||||
aCx.WriteInputScalar((size_t) ObjCInputKind::ConstantString);
|
||||
aCx.WriteInputScalar(len);
|
||||
aCx.WriteInputBytes(buffer.begin(), len * sizeof(UniChar));
|
||||
return;
|
||||
// stored in system libraries. Be careful when accessing the pointer as in
|
||||
// the case where a middleman call hook for a function is missing the
|
||||
// pointer could have originated from the recording and its address may not
|
||||
// be mapped. In this case we would rather gracefully recover and fail to
|
||||
// paint, instead of crashing.
|
||||
if (MemoryRangeIsTracked(*aThingPtr, sizeof(CFConstantString))) {
|
||||
CFConstantString* str = (CFConstantString*) *aThingPtr;
|
||||
if (str->mClass == objc_lookUpClass("__NSCFConstantString") &&
|
||||
str->mLength <= 4096 && // Sanity check.
|
||||
MemoryRangeIsTracked(str->mData, str->mLength)) {
|
||||
InfallibleVector<UniChar> buffer;
|
||||
NS_ConvertUTF8toUTF16 converted(str->mData, str->mLength);
|
||||
aCx.WriteInputScalar((size_t) ObjCInputKind::ConstantString);
|
||||
aCx.WriteInputScalar(str->mLength);
|
||||
aCx.WriteInputBytes(converted.get(), str->mLength * sizeof(UniChar));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
aCx.MarkAsFailed();
|
||||
|
@ -1212,6 +1219,12 @@ Preamble_pthread_create(CallArguments* aArguments)
|
|||
|
||||
*token = Thread::StartThread((Thread::Callback) start, startArg,
|
||||
detachState == PTHREAD_CREATE_JOINABLE);
|
||||
if (!*token) {
|
||||
// Don't create new threads after diverging from the recording.
|
||||
MOZ_RELEASE_ASSERT(HasDivergedFromRecording());
|
||||
return Preamble_SetError(aArguments);
|
||||
}
|
||||
|
||||
aArguments->Rval<ssize_t>() = 0;
|
||||
return PreambleResult::Veto;
|
||||
}
|
||||
|
@ -1752,6 +1765,7 @@ Middleman_CFArrayGetValueAtIndex(MiddlemanCallContext& aCx)
|
|||
if (call) {
|
||||
switch (call->mCallId) {
|
||||
case CallEvent_CTLineGetGlyphRuns:
|
||||
case CallEvent_CTFontCopyVariationAxes:
|
||||
case CallEvent_CTFontDescriptorCreateMatchingFontDescriptors:
|
||||
isCFTypeRval = true;
|
||||
break;
|
||||
|
@ -2048,6 +2062,39 @@ RR_CGDataProviderCreateWithData(Stream& aEvents, CallArguments* aArguments, Erro
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ReleaseDataCallback(void*, const void* aData, size_t)
|
||||
{
|
||||
free((void*) aData);
|
||||
}
|
||||
|
||||
static void
|
||||
Middleman_CGDataProviderCreateWithData(MiddlemanCallContext& aCx)
|
||||
{
|
||||
Middleman_Buffer<1, 2>(aCx);
|
||||
Middleman_CreateCFTypeRval(aCx);
|
||||
|
||||
auto& info = aCx.mArguments->Arg<0, void*>();
|
||||
auto& data = aCx.mArguments->Arg<1, const void*>();
|
||||
auto& size = aCx.mArguments->Arg<2, size_t>();
|
||||
auto& releaseData = aCx.mArguments->Arg<3, CGDataProviderReleaseDataCallback>();
|
||||
|
||||
// Make a copy of the data that won't be released the next time middleman
|
||||
// calls are reset, in case CoreGraphics decides to hang onto the data
|
||||
// provider after that point.
|
||||
if (aCx.mPhase == MiddlemanCallPhase::MiddlemanInput) {
|
||||
void* newData = malloc(size);
|
||||
memcpy(newData, data, size);
|
||||
data = newData;
|
||||
releaseData = ReleaseDataCallback;
|
||||
}
|
||||
|
||||
// Immediately release the data in the replaying process.
|
||||
if (aCx.mPhase == MiddlemanCallPhase::ReplayInput) {
|
||||
releaseData(info, data, size);
|
||||
}
|
||||
}
|
||||
|
||||
static PreambleResult
|
||||
Preamble_CGPathApply(CallArguments* aArguments)
|
||||
{
|
||||
|
|
|
@ -199,6 +199,11 @@ DivergeFromRecording()
|
|||
// Reset middleman call state whenever we first diverge from the recording.
|
||||
child::SendResetMiddlemanCalls();
|
||||
|
||||
// Make sure all non-main threads are idle before we begin diverging. This
|
||||
// thread's new behavior can change values used by other threads and induce
|
||||
// recording mismatches.
|
||||
Thread::WaitForIdleThreads();
|
||||
|
||||
thread->DivergeFromRecording();
|
||||
}
|
||||
|
||||
|
|
|
@ -237,8 +237,7 @@ Thread::StartThread(Callback aStart, void* aArgument, bool aNeedsJoin)
|
|||
Thread* thread = Thread::Current();
|
||||
RecordingEventSection res(thread);
|
||||
if (!res.CanAccessEvents()) {
|
||||
EnsureNotDivergedFromRecording();
|
||||
Unreachable();
|
||||
return 0;
|
||||
}
|
||||
|
||||
MonitorAutoLock lock(*gMonitor);
|
||||
|
@ -364,24 +363,16 @@ RecordReplayInterface_InternalAreThreadEventsDisallowed()
|
|||
// Thread Coordination
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Whether all threads should attempt to idle.
|
||||
static Atomic<bool, SequentiallyConsistent, Behavior::DontPreserve> gThreadsShouldIdle;
|
||||
|
||||
// Whether all threads are considered to be idle.
|
||||
static Atomic<bool, SequentiallyConsistent, Behavior::DontPreserve> gThreadsAreIdle;
|
||||
|
||||
/* static */ void
|
||||
Thread::WaitForIdleThreads()
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(CurrentIsMainThread());
|
||||
|
||||
MOZ_RELEASE_ASSERT(!gThreadsShouldIdle);
|
||||
MOZ_RELEASE_ASSERT(!gThreadsAreIdle);
|
||||
gThreadsShouldIdle = true;
|
||||
|
||||
MonitorAutoLock lock(*gMonitor);
|
||||
for (size_t i = MainThreadId + 1; i <= MaxRecordedThreadId; i++) {
|
||||
GetById(i)->mUnrecordedWaitNotified = false;
|
||||
Thread* thread = GetById(i);
|
||||
thread->mShouldIdle = true;
|
||||
thread->mUnrecordedWaitNotified = false;
|
||||
}
|
||||
while (true) {
|
||||
bool done = true;
|
||||
|
@ -426,23 +417,21 @@ Thread::WaitForIdleThreads()
|
|||
MonitorAutoUnlock unlock(*gMonitor);
|
||||
WaitNoIdle();
|
||||
}
|
||||
}
|
||||
|
||||
gThreadsAreIdle = true;
|
||||
/* static */ void
|
||||
Thread::ResumeSingleIdleThread(size_t aId)
|
||||
{
|
||||
GetById(aId)->mShouldIdle = false;
|
||||
Notify(aId);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
Thread::ResumeIdleThreads()
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(CurrentIsMainThread());
|
||||
|
||||
MOZ_RELEASE_ASSERT(gThreadsAreIdle);
|
||||
gThreadsAreIdle = false;
|
||||
|
||||
MOZ_RELEASE_ASSERT(gThreadsShouldIdle);
|
||||
gThreadsShouldIdle = false;
|
||||
|
||||
for (size_t i = MainThreadId + 1; i <= MaxRecordedThreadId; i++) {
|
||||
Notify(i);
|
||||
ResumeSingleIdleThread(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -464,7 +453,7 @@ Thread::NotifyUnrecordedWait(const std::function<void()>& aCallback, bool aOnlyW
|
|||
|
||||
// The main thread might be able to make progress now by calling the routine
|
||||
// if it is waiting for idle replay threads.
|
||||
if (gThreadsShouldIdle) {
|
||||
if (mShouldIdle) {
|
||||
Notify(MainThreadId);
|
||||
}
|
||||
}
|
||||
|
@ -473,7 +462,8 @@ Thread::NotifyUnrecordedWait(const std::function<void()>& aCallback, bool aOnlyW
|
|||
Thread::MaybeWaitForCheckpointSave()
|
||||
{
|
||||
MonitorAutoLock lock(*gMonitor);
|
||||
while (gThreadsShouldIdle) {
|
||||
Thread* thread = Thread::Current();
|
||||
while (thread->mShouldIdle) {
|
||||
MonitorAutoUnlock unlock(*gMonitor);
|
||||
Wait();
|
||||
}
|
||||
|
@ -531,7 +521,7 @@ Thread::Wait()
|
|||
}
|
||||
|
||||
thread->mIdle = true;
|
||||
if (gThreadsShouldIdle) {
|
||||
if (thread->mShouldIdle) {
|
||||
// Notify the main thread that we just became idle.
|
||||
Notify(MainThreadId);
|
||||
}
|
||||
|
@ -546,7 +536,7 @@ Thread::Wait()
|
|||
RestoreThreadStack(thread->Id());
|
||||
Unreachable();
|
||||
}
|
||||
} while (gThreadsShouldIdle);
|
||||
} while (thread->mShouldIdle);
|
||||
|
||||
thread->mIdle = false;
|
||||
thread->SetPassThrough(false);
|
||||
|
|
|
@ -127,6 +127,9 @@ private:
|
|||
// File descriptor to notify to wake the thread up, fixed at creation.
|
||||
FileHandle mNotifyfd;
|
||||
|
||||
// Whether the thread should attempt to idle.
|
||||
Atomic<bool, SequentiallyConsistent, Behavior::DontPreserve> mShouldIdle;
|
||||
|
||||
// Whether the thread is waiting on idlefd.
|
||||
Atomic<bool, SequentiallyConsistent, Behavior::DontPreserve> mIdle;
|
||||
|
||||
|
@ -289,6 +292,13 @@ public:
|
|||
// After WaitForIdleThreads(), the main thread will call this to allow
|
||||
// other threads to resume execution.
|
||||
static void ResumeIdleThreads();
|
||||
|
||||
// Allow a single thread to resume execution.
|
||||
static void ResumeSingleIdleThread(size_t aId);
|
||||
|
||||
// Return whether this thread is in the idle state entered after
|
||||
// WaitForIdleThreads.
|
||||
bool IsIdle() { return mIdle; }
|
||||
};
|
||||
|
||||
// This uses a stack pointer instead of TLS to make sure events are passed
|
||||
|
|
|
@ -545,7 +545,7 @@ PaintFromMainThread()
|
|||
void
|
||||
NotifyPaintComplete()
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(Thread::Current()->Id() == gCompositorThreadId);
|
||||
MOZ_RELEASE_ASSERT(!gCompositorThreadId || Thread::Current()->Id() == gCompositorThreadId);
|
||||
|
||||
// Notify the main thread in case it is waiting for this paint to complete.
|
||||
{
|
||||
|
@ -577,22 +577,21 @@ Repaint(size_t* aWidth, size_t* aHeight)
|
|||
// and the last graphics we sent will still be correct.
|
||||
Thread* compositorThread = Thread::GetById(gCompositorThreadId);
|
||||
if (!compositorThread->WillDivergeFromRecordingSoon()) {
|
||||
// Allow the compositor to diverge from the recording so it can perform
|
||||
// any paint we are about to trigger, or finish any in flight paint that
|
||||
// that existed at the point we are paused at.
|
||||
Thread::GetById(gCompositorThreadId)->SetShouldDivergeFromRecording();
|
||||
Thread::ResumeSingleIdleThread(gCompositorThreadId);
|
||||
|
||||
// Create an artifical vsync to see if graphics have changed since the last
|
||||
// paint and a new paint is needed.
|
||||
NotifyVsyncObserver();
|
||||
|
||||
if (gNumPendingPaints) {
|
||||
// Allow the compositor to diverge from the recording so it can perform
|
||||
// any paint we just triggered, or finish any in flight paint that that
|
||||
// existed at the point we are paused at.
|
||||
Thread::GetById(gCompositorThreadId)->SetShouldDivergeFromRecording();
|
||||
|
||||
// Wait for the compositor to finish all in flight paints, including any
|
||||
// one we just triggered.
|
||||
MonitorAutoLock lock(*gMonitor);
|
||||
while (gNumPendingPaints) {
|
||||
gMonitor->Wait();
|
||||
}
|
||||
// Wait for the compositor to finish all in flight paints, including any
|
||||
// one we just triggered.
|
||||
MonitorAutoLock lock(*gMonitor);
|
||||
while (gNumPendingPaints) {
|
||||
gMonitor->Wait();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,8 +23,8 @@ module.exports = {
|
|||
"Ci": false,
|
||||
"Cr": false,
|
||||
"Cu": false,
|
||||
// These globals are made available via WebIDL files, see ResolveSystemBinding in:
|
||||
// https://searchfox.org/mozilla-central/source/__GENERATED__/dom/bindings/ResolveSystemBinding.cpp
|
||||
// These globals are made available via WebIDL files, see WebIDLGlobalNameHash.
|
||||
// XXX(nika): We should also explicitly include window-defined globals here now.
|
||||
"AbortController": false,
|
||||
"AbortSignal": false,
|
||||
"AddonManagerPermissions": false,
|
||||
|
|
Загрузка…
Ссылка в новой задаче