forked from mirrors/gecko-dev
Bug 1561435 - Format remote/, a=automatic-formatting
# ignore-this-changeset Differential Revision: https://phabricator.services.mozilla.com/D35925 --HG-- extra : source : b793788d0f38244b33eb59ea36e2c6624dbd12c5
This commit is contained in:
parent
91f67eb25e
commit
991b3c93c6
64 changed files with 1477 additions and 674 deletions
|
|
@ -45,7 +45,6 @@ module.exports = {
|
||||||
"overrides": [{
|
"overrides": [{
|
||||||
"files": [
|
"files": [
|
||||||
"devtools/**",
|
"devtools/**",
|
||||||
"remote/**",
|
|
||||||
"security/**",
|
"security/**",
|
||||||
"services/**",
|
"services/**",
|
||||||
"servo/**",
|
"servo/**",
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,6 @@ toolkit/components/telemetry/datareporting-prefs.js
|
||||||
toolkit/components/telemetry/healthreport-prefs.js
|
toolkit/components/telemetry/healthreport-prefs.js
|
||||||
|
|
||||||
# Ignore all top-level directories for now.
|
# Ignore all top-level directories for now.
|
||||||
remote/**
|
|
||||||
security/**
|
security/**
|
||||||
services/**
|
services/**
|
||||||
servo/**
|
servo/**
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,18 @@
|
||||||
|
|
||||||
var EXPORTED_SYMBOLS = ["Connection"];
|
var EXPORTED_SYMBOLS = ["Connection"];
|
||||||
|
|
||||||
const {Log} = ChromeUtils.import("chrome://remote/content/Log.jsm");
|
const { Log } = ChromeUtils.import("chrome://remote/content/Log.jsm");
|
||||||
const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
const { XPCOMUtils } = ChromeUtils.import(
|
||||||
|
"resource://gre/modules/XPCOMUtils.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
XPCOMUtils.defineLazyGetter(this, "log", Log.get);
|
XPCOMUtils.defineLazyGetter(this, "log", Log.get);
|
||||||
XPCOMUtils.defineLazyServiceGetter(this, "UUIDGen", "@mozilla.org/uuid-generator;1", "nsIUUIDGenerator");
|
XPCOMUtils.defineLazyServiceGetter(
|
||||||
|
this,
|
||||||
|
"UUIDGen",
|
||||||
|
"@mozilla.org/uuid-generator;1",
|
||||||
|
"nsIUUIDGenerator"
|
||||||
|
);
|
||||||
|
|
||||||
class Connection {
|
class Connection {
|
||||||
/**
|
/**
|
||||||
|
|
@ -40,8 +47,10 @@ class Connection {
|
||||||
registerSession(session) {
|
registerSession(session) {
|
||||||
if (!session.id) {
|
if (!session.id) {
|
||||||
if (this.defaultSession) {
|
if (this.defaultSession) {
|
||||||
throw new Error("Default session is already set on Connection," +
|
throw new Error(
|
||||||
"can't register another one.");
|
"Default session is already set on Connection," +
|
||||||
|
"can't register another one."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
this.defaultSession = session;
|
this.defaultSession = session;
|
||||||
}
|
}
|
||||||
|
|
@ -58,14 +67,14 @@ class Connection {
|
||||||
message: e.message,
|
message: e.message,
|
||||||
data: e.stack,
|
data: e.stack,
|
||||||
};
|
};
|
||||||
this.send({id, error});
|
this.send({ id, error });
|
||||||
}
|
}
|
||||||
|
|
||||||
deserialize(data) {
|
deserialize(data) {
|
||||||
const id = data.id;
|
const id = data.id;
|
||||||
const method = data.method;
|
const method = data.method;
|
||||||
const params = data.params || {};
|
const params = data.params || {};
|
||||||
return {id, method, params};
|
return { id, method, params };
|
||||||
}
|
}
|
||||||
|
|
||||||
// transport hooks
|
// transport hooks
|
||||||
|
|
@ -73,7 +82,7 @@ class Connection {
|
||||||
onPacket(packet) {
|
onPacket(packet) {
|
||||||
log.trace(`(connection ${this.id})-> ${JSON.stringify(packet)}`);
|
log.trace(`(connection ${this.id})-> ${JSON.stringify(packet)}`);
|
||||||
|
|
||||||
let message = {id: null};
|
let message = { id: null };
|
||||||
try {
|
try {
|
||||||
message = this.deserialize(packet);
|
message = this.deserialize(packet);
|
||||||
const { sessionId } = packet;
|
const { sessionId } = packet;
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,11 @@ var EXPORTED_SYMBOLS = [
|
||||||
"UnsupportedError",
|
"UnsupportedError",
|
||||||
];
|
];
|
||||||
|
|
||||||
const {Log} = ChromeUtils.import("chrome://remote/content/Log.jsm");
|
const { Log } = ChromeUtils.import("chrome://remote/content/Log.jsm");
|
||||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||||
const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
const { XPCOMUtils } = ChromeUtils.import(
|
||||||
|
"resource://gre/modules/XPCOMUtils.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
XPCOMUtils.defineLazyGetter(this, "log", Log.get);
|
XPCOMUtils.defineLazyGetter(this, "log", Log.get);
|
||||||
|
|
||||||
|
|
@ -31,15 +33,15 @@ class RemoteAgentError extends Error {
|
||||||
|
|
||||||
notify() {
|
notify() {
|
||||||
Cu.reportError(this);
|
Cu.reportError(this);
|
||||||
log.error(this.toString({stack: true}));
|
log.error(this.toString({ stack: true }));
|
||||||
}
|
}
|
||||||
|
|
||||||
toString({stack = false} = {}) {
|
toString({ stack = false } = {}) {
|
||||||
return RemoteAgentError.format(this, {stack});
|
return RemoteAgentError.format(this, { stack });
|
||||||
}
|
}
|
||||||
|
|
||||||
static format(e, {stack = false} = {}) {
|
static format(e, { stack = false } = {}) {
|
||||||
return formatError(e, {stack});
|
return formatError(e, { stack });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -88,7 +90,7 @@ class FatalError extends RemoteAgentError {
|
||||||
}
|
}
|
||||||
|
|
||||||
notify() {
|
notify() {
|
||||||
log.fatal(this.toString({stack: true}));
|
log.fatal(this.toString({ stack: true }));
|
||||||
}
|
}
|
||||||
|
|
||||||
quit(mode = Ci.nsIAppStartup.eForceQuit) {
|
quit(mode = Ci.nsIAppStartup.eForceQuit) {
|
||||||
|
|
@ -110,7 +112,7 @@ class UnknownMethodError extends RemoteAgentError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatError(error, {stack = false} = {}) {
|
function formatError(error, { stack = false } = {}) {
|
||||||
const els = [];
|
const els = [];
|
||||||
|
|
||||||
els.push(error.name);
|
els.push(error.name);
|
||||||
|
|
@ -127,7 +129,7 @@ function formatError(error, {stack = false} = {}) {
|
||||||
|
|
||||||
if (error.cause) {
|
if (error.cause) {
|
||||||
els.push("\n");
|
els.push("\n");
|
||||||
els.push("caused by: " + formatError(error.cause, {stack}));
|
els.push("caused by: " + formatError(error.cause, { stack }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,14 @@
|
||||||
|
|
||||||
var EXPORTED_SYMBOLS = ["JSONHandler"];
|
var EXPORTED_SYMBOLS = ["JSONHandler"];
|
||||||
|
|
||||||
const {HTTP_404, HTTP_505} = ChromeUtils.import("chrome://remote/content/server/HTTPD.jsm");
|
const { HTTP_404, HTTP_505 } = ChromeUtils.import(
|
||||||
const {Log} = ChromeUtils.import("chrome://remote/content/Log.jsm");
|
"chrome://remote/content/server/HTTPD.jsm"
|
||||||
const {Protocol} = ChromeUtils.import("chrome://remote/content/Protocol.jsm");
|
);
|
||||||
const {RemoteAgentError} = ChromeUtils.import("chrome://remote/content/Error.jsm");
|
const { Log } = ChromeUtils.import("chrome://remote/content/Log.jsm");
|
||||||
|
const { Protocol } = ChromeUtils.import("chrome://remote/content/Protocol.jsm");
|
||||||
|
const { RemoteAgentError } = ChromeUtils.import(
|
||||||
|
"chrome://remote/content/Error.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
class JSONHandler {
|
class JSONHandler {
|
||||||
constructor(agent) {
|
constructor(agent) {
|
||||||
|
|
@ -24,12 +28,12 @@ class JSONHandler {
|
||||||
getVersion() {
|
getVersion() {
|
||||||
const mainProcessTarget = this.agent.targets.getMainProcessTarget();
|
const mainProcessTarget = this.agent.targets.getMainProcessTarget();
|
||||||
return {
|
return {
|
||||||
"Browser": "Firefox",
|
Browser: "Firefox",
|
||||||
"Protocol-Version": "1.0",
|
"Protocol-Version": "1.0",
|
||||||
"User-Agent": "Mozilla",
|
"User-Agent": "Mozilla",
|
||||||
"V8-Version": "1.0",
|
"V8-Version": "1.0",
|
||||||
"WebKit-Version": "1.0",
|
"WebKit-Version": "1.0",
|
||||||
"webSocketDebuggerUrl": mainProcessTarget.toJSON().webSocketDebuggerUrl,
|
webSocketDebuggerUrl: mainProcessTarget.toJSON().webSocketDebuggerUrl,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -54,7 +58,11 @@ class JSONHandler {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const body = this.routes[request.path]();
|
const body = this.routes[request.path]();
|
||||||
const payload = JSON.stringify(body, sanitise, Log.verbose ? "\t" : undefined);
|
const payload = JSON.stringify(
|
||||||
|
body,
|
||||||
|
sanitise,
|
||||||
|
Log.verbose ? "\t" : undefined
|
||||||
|
);
|
||||||
|
|
||||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||||
response.setHeader("Content-Type", "application/json");
|
response.setHeader("Content-Type", "application/json");
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@
|
||||||
|
|
||||||
var EXPORTED_SYMBOLS = ["Log"];
|
var EXPORTED_SYMBOLS = ["Log"];
|
||||||
|
|
||||||
const {Log: StdLog} = ChromeUtils.import("resource://gre/modules/Log.jsm");
|
const { Log: StdLog } = ChromeUtils.import("resource://gre/modules/Log.jsm");
|
||||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||||
|
|
||||||
const LOG_LEVEL = "remote.log.level";
|
const LOG_LEVEL = "remote.log.level";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
var EXPORTED_SYMBOLS = ["Observer"];
|
var EXPORTED_SYMBOLS = ["Observer"];
|
||||||
|
|
||||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||||
|
|
||||||
class Observer {
|
class Observer {
|
||||||
static observe(type, observer) {
|
static observe(type, observer) {
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ const RecommendedPreferences = {
|
||||||
|
|
||||||
// Prevent various error message on the console
|
// Prevent various error message on the console
|
||||||
// jest-puppeteer asserts that no error message is emitted by the console
|
// jest-puppeteer asserts that no error message is emitted by the console
|
||||||
"browser.contentblocking.features.standard": "-tp,tpPrivate,cookieBehavior0,-cm,-fp",
|
"browser.contentblocking.features.standard":
|
||||||
|
"-tp,tpPrivate,cookieBehavior0,-cm,-fp",
|
||||||
"network.cookie.cookieBehavior": 0,
|
"network.cookie.cookieBehavior": 0,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,10 @@
|
||||||
|
|
||||||
var EXPORTED_SYMBOLS = ["RemoteAgent", "RemoteAgentFactory"];
|
var EXPORTED_SYMBOLS = ["RemoteAgent", "RemoteAgentFactory"];
|
||||||
|
|
||||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||||
const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
const { XPCOMUtils } = ChromeUtils.import(
|
||||||
|
"resource://gre/modules/XPCOMUtils.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||||
FatalError: "chrome://remote/content/Error.jsm",
|
FatalError: "chrome://remote/content/Error.jsm",
|
||||||
|
|
@ -36,7 +38,9 @@ class RemoteAgentClass {
|
||||||
throw new Error("Remote agent is disabled by its preference");
|
throw new Error("Remote agent is disabled by its preference");
|
||||||
}
|
}
|
||||||
if (Services.appinfo.processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT) {
|
if (Services.appinfo.processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT) {
|
||||||
throw new Error("Remote agent can only be instantiated from the parent process");
|
throw new Error(
|
||||||
|
"Remote agent can only be instantiated from the parent process"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.server) {
|
if (this.server) {
|
||||||
|
|
@ -48,7 +52,7 @@ class RemoteAgentClass {
|
||||||
|
|
||||||
this.server.registerPrefixHandler("/json/", new JSONHandler(this));
|
this.server.registerPrefixHandler("/json/", new JSONHandler(this));
|
||||||
|
|
||||||
this.tabs = new TabObserver({registerExisting: true});
|
this.tabs = new TabObserver({ registerExisting: true });
|
||||||
this.tabs.on("open", (eventName, tab) => {
|
this.tabs.on("open", (eventName, tab) => {
|
||||||
this.targets.connect(tab.linkedBrowser);
|
this.targets.connect(tab.linkedBrowser);
|
||||||
});
|
});
|
||||||
|
|
@ -78,7 +82,7 @@ class RemoteAgentClass {
|
||||||
throw new TypeError(`Expected nsIURI: ${address}`);
|
throw new TypeError(`Expected nsIURI: ${address}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
let {host, port} = address;
|
let { host, port } = address;
|
||||||
if (Preferences.get(FORCE_LOCAL) && !LOOPBACKS.includes(host)) {
|
if (Preferences.get(FORCE_LOCAL) && !LOOPBACKS.includes(host)) {
|
||||||
throw new Error("Restricted to loopback devices");
|
throw new Error("Restricted to loopback devices");
|
||||||
}
|
}
|
||||||
|
|
@ -167,7 +171,9 @@ class RemoteAgentClass {
|
||||||
const remoteDebuggingPort = flag("remote-debugging-port");
|
const remoteDebuggingPort = flag("remote-debugging-port");
|
||||||
|
|
||||||
if (remoteDebugger && remoteDebuggingPort) {
|
if (remoteDebugger && remoteDebuggingPort) {
|
||||||
log.fatal("Conflicting flags --remote-debugger and --remote-debugging-port");
|
log.fatal(
|
||||||
|
"Conflicting flags --remote-debugger and --remote-debugging-port"
|
||||||
|
);
|
||||||
cmdLine.preventDefault = true;
|
cmdLine.preventDefault = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -185,9 +191,14 @@ class RemoteAgentClass {
|
||||||
|
|
||||||
let addr;
|
let addr;
|
||||||
try {
|
try {
|
||||||
addr = NetUtil.newURI(`http://${host || DEFAULT_HOST}:${port || DEFAULT_PORT}/`);
|
addr = NetUtil.newURI(
|
||||||
|
`http://${host || DEFAULT_HOST}:${port || DEFAULT_PORT}/`
|
||||||
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.fatal(`Expected address syntax [<host>]:<port>: ${remoteDebugger || remoteDebuggingPort}`);
|
log.fatal(
|
||||||
|
`Expected address syntax [<host>]:<port>: ${remoteDebugger ||
|
||||||
|
remoteDebuggingPort}`
|
||||||
|
);
|
||||||
cmdLine.preventDefault = true;
|
cmdLine.preventDefault = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -200,15 +211,20 @@ class RemoteAgentClass {
|
||||||
this.listen(addr);
|
this.listen(addr);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.close();
|
this.close();
|
||||||
throw new FatalError(`Unable to start remote agent on ${addr.spec}: ${e.message}`, e);
|
throw new FatalError(
|
||||||
}
|
`Unable to start remote agent on ${addr.spec}: ${e.message}`,
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get helpInfo() {
|
get helpInfo() {
|
||||||
return " --remote-debugger [<host>][:<port>]\n" +
|
return (
|
||||||
" --remote-debugging-port <port> Start the Firefox remote agent, which is \n" +
|
" --remote-debugger [<host>][:<port>]\n" +
|
||||||
" a low-level debugging interface based on the CDP protocol.\n" +
|
" --remote-debugging-port <port> Start the Firefox remote agent, which is \n" +
|
||||||
" Defaults to listen on localhost:9222.\n";
|
" a low-level debugging interface based on the CDP protocol.\n" +
|
||||||
|
" Defaults to listen on localhost:9222.\n"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// XPCOM
|
// XPCOM
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ var EXPORTED_SYMBOLS = [
|
||||||
"MessagePromise",
|
"MessagePromise",
|
||||||
];
|
];
|
||||||
|
|
||||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wait for a single event to be fired on a specific EventListener.
|
* Wait for a single event to be fired on a specific EventListener.
|
||||||
|
|
@ -43,34 +43,48 @@ const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||||
*
|
*
|
||||||
* @throws {TypeError}
|
* @throws {TypeError}
|
||||||
*/
|
*/
|
||||||
function EventPromise(listener, type, options = {
|
function EventPromise(
|
||||||
|
listener,
|
||||||
|
type,
|
||||||
|
options = {
|
||||||
capture: false,
|
capture: false,
|
||||||
wantsUntrusted: false,
|
wantsUntrusted: false,
|
||||||
mozSystemGroup: false,
|
mozSystemGroup: false,
|
||||||
}) {
|
}
|
||||||
|
) {
|
||||||
if (!listener || !("addEventListener" in listener)) {
|
if (!listener || !("addEventListener" in listener)) {
|
||||||
throw new TypeError();
|
throw new TypeError();
|
||||||
}
|
}
|
||||||
if (typeof type != "string") {
|
if (typeof type != "string") {
|
||||||
throw new TypeError();
|
throw new TypeError();
|
||||||
}
|
}
|
||||||
if (("capture" in options && typeof options.capture != "boolean") ||
|
if (
|
||||||
("wantsUntrusted" in options && typeof options.wantsUntrusted != "boolean") ||
|
("capture" in options && typeof options.capture != "boolean") ||
|
||||||
("mozSystemGroup" in options && typeof options.mozSystemGroup != "boolean")) {
|
("wantsUntrusted" in options &&
|
||||||
|
typeof options.wantsUntrusted != "boolean") ||
|
||||||
|
("mozSystemGroup" in options && typeof options.mozSystemGroup != "boolean")
|
||||||
|
) {
|
||||||
throw new TypeError();
|
throw new TypeError();
|
||||||
}
|
}
|
||||||
|
|
||||||
options.once = true;
|
options.once = true;
|
||||||
|
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
listener.addEventListener(type, event => {
|
listener.addEventListener(
|
||||||
Services.tm.dispatchToMainThread(() => resolve(event));
|
type,
|
||||||
}, options);
|
event => {
|
||||||
|
Services.tm.dispatchToMainThread(() => resolve(event));
|
||||||
|
},
|
||||||
|
options
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function DOMContentLoadedPromise(window, options = {mozSystemGroup: true}) {
|
function DOMContentLoadedPromise(window, options = { mozSystemGroup: true }) {
|
||||||
if (window.document.readyState == "complete" || window.document.readyState == "interactive") {
|
if (
|
||||||
|
window.document.readyState == "complete" ||
|
||||||
|
window.document.readyState == "interactive"
|
||||||
|
) {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
return new EventPromise(window, "DOMContentLoaded", options);
|
return new EventPromise(window, "DOMContentLoaded", options);
|
||||||
|
|
|
||||||
|
|
@ -4,15 +4,15 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var EXPORTED_SYMBOLS = [
|
var EXPORTED_SYMBOLS = ["TabManager", "TabObserver", "WindowObserver"];
|
||||||
"TabManager",
|
|
||||||
"TabObserver",
|
|
||||||
"WindowObserver",
|
|
||||||
];
|
|
||||||
|
|
||||||
const {DOMContentLoadedPromise} = ChromeUtils.import("chrome://remote/content/Sync.jsm");
|
const { DOMContentLoadedPromise } = ChromeUtils.import(
|
||||||
const {EventEmitter} = ChromeUtils.import("resource://gre/modules/EventEmitter.jsm");
|
"chrome://remote/content/Sync.jsm"
|
||||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
);
|
||||||
|
const { EventEmitter } = ChromeUtils.import(
|
||||||
|
"resource://gre/modules/EventEmitter.jsm"
|
||||||
|
);
|
||||||
|
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The WindowManager provides tooling for application-agnostic
|
* The WindowManager provides tooling for application-agnostic
|
||||||
|
|
@ -40,7 +40,7 @@ class WindowObserver {
|
||||||
* Events will be despatched for the ChromeWindows that exist
|
* Events will be despatched for the ChromeWindows that exist
|
||||||
* at the time the observer is started.
|
* at the time the observer is started.
|
||||||
*/
|
*/
|
||||||
constructor({registerExisting = false} = {}) {
|
constructor({ registerExisting = false } = {}) {
|
||||||
this.registerExisting = registerExisting;
|
this.registerExisting = registerExisting;
|
||||||
EventEmitter.decorate(this);
|
EventEmitter.decorate(this);
|
||||||
}
|
}
|
||||||
|
|
@ -63,16 +63,16 @@ class WindowObserver {
|
||||||
|
|
||||||
async onOpenWindow(xulWindow) {
|
async onOpenWindow(xulWindow) {
|
||||||
const window = xulWindow
|
const window = xulWindow
|
||||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||||
.getInterface(Ci.nsIDOMWindow);
|
.getInterface(Ci.nsIDOMWindow);
|
||||||
await new DOMContentLoadedPromise(window);
|
await new DOMContentLoadedPromise(window);
|
||||||
this.emit("open", window);
|
this.emit("open", window);
|
||||||
}
|
}
|
||||||
|
|
||||||
onCloseWindow(xulWindow) {
|
onCloseWindow(xulWindow) {
|
||||||
const window = xulWindow
|
const window = xulWindow
|
||||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||||
.getInterface(Ci.nsIDOMWindow);
|
.getInterface(Ci.nsIDOMWindow);
|
||||||
this.emit("close", window);
|
this.emit("close", window);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -95,8 +95,8 @@ class TabObserver {
|
||||||
* Events will be fired for ChromeWIndows and their respective tabs
|
* Events will be fired for ChromeWIndows and their respective tabs
|
||||||
* at the time when the observer is started.
|
* at the time when the observer is started.
|
||||||
*/
|
*/
|
||||||
constructor({registerExisting = false} = {}) {
|
constructor({ registerExisting = false } = {}) {
|
||||||
this.windows = new WindowObserver({registerExisting});
|
this.windows = new WindowObserver({ registerExisting });
|
||||||
EventEmitter.decorate(this);
|
EventEmitter.decorate(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -136,8 +136,10 @@ class TabObserver {
|
||||||
this.onTabOpen(tab);
|
this.onTabOpen(tab);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener("TabOpen", ({target}) => this.onTabOpen(target));
|
window.addEventListener("TabOpen", ({ target }) => this.onTabOpen(target));
|
||||||
window.addEventListener("TabClose", ({target}) => this.onTabClose(target));
|
window.addEventListener("TabClose", ({ target }) =>
|
||||||
|
this.onTabClose(target)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
onWindowClose(window) {
|
onWindowClose(window) {
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,15 @@
|
||||||
|
|
||||||
var EXPORTED_SYMBOLS = ["ContentProcessDomain"];
|
var EXPORTED_SYMBOLS = ["ContentProcessDomain"];
|
||||||
|
|
||||||
const {Domain} = ChromeUtils.import("chrome://remote/content/domains/Domain.jsm");
|
const { Domain } = ChromeUtils.import(
|
||||||
|
"chrome://remote/content/domains/Domain.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
ChromeUtils.defineModuleGetter(this, "ContextObserver",
|
ChromeUtils.defineModuleGetter(
|
||||||
"chrome://remote/content/domains/ContextObserver.jsm");
|
this,
|
||||||
|
"ContextObserver",
|
||||||
|
"chrome://remote/content/domains/ContextObserver.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
class ContentProcessDomain extends Domain {
|
class ContentProcessDomain extends Domain {
|
||||||
destructor() {
|
destructor() {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,9 @@
|
||||||
|
|
||||||
var EXPORTED_SYMBOLS = ["ContentProcessDomains"];
|
var EXPORTED_SYMBOLS = ["ContentProcessDomains"];
|
||||||
|
|
||||||
const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
const { XPCOMUtils } = ChromeUtils.import(
|
||||||
|
"resource://gre/modules/XPCOMUtils.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
const ContentProcessDomains = {};
|
const ContentProcessDomains = {};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,37 +25,45 @@
|
||||||
|
|
||||||
var EXPORTED_SYMBOLS = ["ContextObserver"];
|
var EXPORTED_SYMBOLS = ["ContextObserver"];
|
||||||
|
|
||||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||||
const {EventEmitter} = ChromeUtils.import("resource://gre/modules/EventEmitter.jsm");
|
const { EventEmitter } = ChromeUtils.import(
|
||||||
|
"resource://gre/modules/EventEmitter.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
class ContextObserver {
|
class ContextObserver {
|
||||||
constructor(chromeEventHandler) {
|
constructor(chromeEventHandler) {
|
||||||
this.chromeEventHandler = chromeEventHandler;
|
this.chromeEventHandler = chromeEventHandler;
|
||||||
EventEmitter.decorate(this);
|
EventEmitter.decorate(this);
|
||||||
|
|
||||||
this.chromeEventHandler.addEventListener("DOMWindowCreated", this,
|
this.chromeEventHandler.addEventListener("DOMWindowCreated", this, {
|
||||||
{mozSystemGroup: true});
|
mozSystemGroup: true,
|
||||||
|
});
|
||||||
|
|
||||||
// Listen for pageshow and pagehide to track pages going in/out to/from the BF Cache
|
// Listen for pageshow and pagehide to track pages going in/out to/from the BF Cache
|
||||||
this.chromeEventHandler.addEventListener("pageshow", this,
|
this.chromeEventHandler.addEventListener("pageshow", this, {
|
||||||
{mozSystemGroup: true});
|
mozSystemGroup: true,
|
||||||
this.chromeEventHandler.addEventListener("pagehide", this,
|
});
|
||||||
{mozSystemGroup: true});
|
this.chromeEventHandler.addEventListener("pagehide", this, {
|
||||||
|
mozSystemGroup: true,
|
||||||
|
});
|
||||||
|
|
||||||
Services.obs.addObserver(this, "inner-window-destroyed");
|
Services.obs.addObserver(this, "inner-window-destroyed");
|
||||||
}
|
}
|
||||||
|
|
||||||
destructor() {
|
destructor() {
|
||||||
this.chromeEventHandler.removeEventListener("DOMWindowCreated", this,
|
this.chromeEventHandler.removeEventListener("DOMWindowCreated", this, {
|
||||||
{mozSystemGroup: true});
|
mozSystemGroup: true,
|
||||||
this.chromeEventHandler.removeEventListener("pageshow", this,
|
});
|
||||||
{mozSystemGroup: true});
|
this.chromeEventHandler.removeEventListener("pageshow", this, {
|
||||||
this.chromeEventHandler.removeEventListener("pagehide", this,
|
mozSystemGroup: true,
|
||||||
{mozSystemGroup: true});
|
});
|
||||||
|
this.chromeEventHandler.removeEventListener("pagehide", this, {
|
||||||
|
mozSystemGroup: true,
|
||||||
|
});
|
||||||
Services.obs.removeObserver(this, "inner-window-destroyed");
|
Services.obs.removeObserver(this, "inner-window-destroyed");
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEvent({type, target, persisted}) {
|
handleEvent({ type, target, persisted }) {
|
||||||
const window = target.defaultView;
|
const window = target.defaultView;
|
||||||
if (window.top != this.chromeEventHandler.ownerGlobal) {
|
if (window.top != this.chromeEventHandler.ownerGlobal) {
|
||||||
// Ignore iframes for now.
|
// Ignore iframes for now.
|
||||||
|
|
@ -65,31 +73,31 @@ class ContextObserver {
|
||||||
const frameId = windowUtils.outerWindowID;
|
const frameId = windowUtils.outerWindowID;
|
||||||
const id = windowUtils.currentInnerWindowID;
|
const id = windowUtils.currentInnerWindowID;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "DOMWindowCreated":
|
case "DOMWindowCreated":
|
||||||
// Do not pass `id` here as that's the new document ID instead of the old one
|
// Do not pass `id` here as that's the new document ID instead of the old one
|
||||||
// that is destroyed. Instead, pass the frameId and let the listener figure out
|
// that is destroyed. Instead, pass the frameId and let the listener figure out
|
||||||
// what ExecutionContext to destroy.
|
// what ExecutionContext to destroy.
|
||||||
this.emit("context-destroyed", { frameId });
|
this.emit("context-destroyed", { frameId });
|
||||||
this.emit("frame-navigated", { frameId, window });
|
this.emit("frame-navigated", { frameId, window });
|
||||||
this.emit("context-created", { id, window });
|
this.emit("context-created", { id, window });
|
||||||
break;
|
break;
|
||||||
case "pageshow":
|
case "pageshow":
|
||||||
// `persisted` is true when this is about a page being resurected from BF Cache
|
// `persisted` is true when this is about a page being resurected from BF Cache
|
||||||
if (!persisted) {
|
if (!persisted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// XXX(ochameau) we might have to emit FrameNavigate here to properly handle BF Cache
|
// XXX(ochameau) we might have to emit FrameNavigate here to properly handle BF Cache
|
||||||
// scenario in Page domain events
|
// scenario in Page domain events
|
||||||
this.emit("context-created", { id, window });
|
this.emit("context-created", { id, window });
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "pagehide":
|
case "pagehide":
|
||||||
// `persisted` is true when this is about a page being frozen into BF Cache
|
// `persisted` is true when this is about a page being frozen into BF Cache
|
||||||
if (!persisted) {
|
if (!persisted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.emit("context-destroyed", { id });
|
this.emit("context-destroyed", { id });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -99,5 +107,3 @@ class ContextObserver {
|
||||||
this.emit("context-destroyed", { id: innerWindowID });
|
this.emit("context-destroyed", { id: innerWindowID });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ class Domain {
|
||||||
}
|
}
|
||||||
|
|
||||||
function isEventHandler(listener) {
|
function isEventHandler(listener) {
|
||||||
return listener &&
|
return (
|
||||||
"onEvent" in listener &&
|
listener && "onEvent" in listener && typeof listener.onEvent == "function"
|
||||||
typeof listener.onEvent == "function";
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,12 @@
|
||||||
|
|
||||||
var EXPORTED_SYMBOLS = ["Domains"];
|
var EXPORTED_SYMBOLS = ["Domains"];
|
||||||
|
|
||||||
const {UnknownMethodError} = ChromeUtils.import("chrome://remote/content/Error.jsm");
|
const { UnknownMethodError } = ChromeUtils.import(
|
||||||
const {Domain} = ChromeUtils.import("chrome://remote/content/domains/Domain.jsm");
|
"chrome://remote/content/Error.jsm"
|
||||||
|
);
|
||||||
|
const { Domain } = ChromeUtils.import(
|
||||||
|
"chrome://remote/content/domains/Domain.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lazy domain instance cache.
|
* Lazy domain instance cache.
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,9 @@
|
||||||
|
|
||||||
var EXPORTED_SYMBOLS = ["ParentProcessDomains"];
|
var EXPORTED_SYMBOLS = ["ParentProcessDomains"];
|
||||||
|
|
||||||
const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
const { XPCOMUtils } = ChromeUtils.import(
|
||||||
|
"resource://gre/modules/XPCOMUtils.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
const ParentProcessDomains = {};
|
const ParentProcessDomains = {};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,9 @@
|
||||||
|
|
||||||
var EXPORTED_SYMBOLS = ["DOM"];
|
var EXPORTED_SYMBOLS = ["DOM"];
|
||||||
|
|
||||||
const {ContentProcessDomain} = ChromeUtils.import("chrome://remote/content/domains/ContentProcessDomain.jsm");
|
const { ContentProcessDomain } = ChromeUtils.import(
|
||||||
|
"chrome://remote/content/domains/ContentProcessDomain.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
class DOM extends ContentProcessDomain {
|
class DOM extends ContentProcessDomain {
|
||||||
constructor(session) {
|
constructor(session) {
|
||||||
|
|
@ -48,10 +50,14 @@ class DOM extends ContentProcessDomain {
|
||||||
let quads = unsafeObject.getBoxQuads({ relativeTo: this.content.document });
|
let quads = unsafeObject.getBoxQuads({ relativeTo: this.content.document });
|
||||||
quads = quads.map(quad => {
|
quads = quads.map(quad => {
|
||||||
return [
|
return [
|
||||||
quad.p1.x, quad.p1.y,
|
quad.p1.x,
|
||||||
quad.p2.x, quad.p2.y,
|
quad.p1.y,
|
||||||
quad.p3.x, quad.p3.y,
|
quad.p2.x,
|
||||||
quad.p4.x, quad.p4.y,
|
quad.p2.y,
|
||||||
|
quad.p3.x,
|
||||||
|
quad.p3.y,
|
||||||
|
quad.p4.x,
|
||||||
|
quad.p4.y,
|
||||||
].map(Math.round);
|
].map(Math.round);
|
||||||
});
|
});
|
||||||
return { quads };
|
return { quads };
|
||||||
|
|
@ -67,7 +73,10 @@ class DOM extends ContentProcessDomain {
|
||||||
height: Math.round(bounding.height),
|
height: Math.round(bounding.height),
|
||||||
};
|
};
|
||||||
for (const box of ["content", "padding", "border", "margin"]) {
|
for (const box of ["content", "padding", "border", "margin"]) {
|
||||||
const quads = unsafeObject.getBoxQuads({box, relativeTo: this.content.document});
|
const quads = unsafeObject.getBoxQuads({
|
||||||
|
box,
|
||||||
|
relativeTo: this.content.document,
|
||||||
|
});
|
||||||
|
|
||||||
// getBoxQuads may return more than one element. In this case we have to compute the bounding box
|
// getBoxQuads may return more than one element. In this case we have to compute the bounding box
|
||||||
// of all these boxes.
|
// of all these boxes.
|
||||||
|
|
@ -99,10 +108,14 @@ class DOM extends ContentProcessDomain {
|
||||||
});
|
});
|
||||||
|
|
||||||
model[box] = [
|
model[box] = [
|
||||||
bounding.p1.x, bounding.p1.y,
|
bounding.p1.x,
|
||||||
bounding.p2.x, bounding.p2.y,
|
bounding.p1.y,
|
||||||
bounding.p3.x, bounding.p3.y,
|
bounding.p2.x,
|
||||||
bounding.p4.x, bounding.p4.y,
|
bounding.p2.y,
|
||||||
|
bounding.p3.x,
|
||||||
|
bounding.p3.y,
|
||||||
|
bounding.p4.x,
|
||||||
|
bounding.p4.y,
|
||||||
].map(Math.round);
|
].map(Math.round);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,9 @@
|
||||||
|
|
||||||
var EXPORTED_SYMBOLS = ["Emulation"];
|
var EXPORTED_SYMBOLS = ["Emulation"];
|
||||||
|
|
||||||
const {ContentProcessDomain} = ChromeUtils.import("chrome://remote/content/domains/ContentProcessDomain.jsm");
|
const { ContentProcessDomain } = ChromeUtils.import(
|
||||||
|
"chrome://remote/content/domains/ContentProcessDomain.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
class Emulation extends ContentProcessDomain {
|
class Emulation extends ContentProcessDomain {
|
||||||
// commands
|
// commands
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
var EXPORTED_SYMBOLS = ["Input"];
|
var EXPORTED_SYMBOLS = ["Input"];
|
||||||
|
|
||||||
const {ContentProcessDomain} = ChromeUtils.import("chrome://remote/content/domains/ContentProcessDomain.jsm");
|
const { ContentProcessDomain } = ChromeUtils.import(
|
||||||
|
"chrome://remote/content/domains/ContentProcessDomain.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
class Input extends ContentProcessDomain {}
|
class Input extends ContentProcessDomain {}
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,10 @@
|
||||||
|
|
||||||
var EXPORTED_SYMBOLS = ["Log"];
|
var EXPORTED_SYMBOLS = ["Log"];
|
||||||
|
|
||||||
const {ContentProcessDomain} = ChromeUtils.import("chrome://remote/content/domains/ContentProcessDomain.jsm");
|
const { ContentProcessDomain } = ChromeUtils.import(
|
||||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
"chrome://remote/content/domains/ContentProcessDomain.jsm"
|
||||||
|
);
|
||||||
|
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||||
|
|
||||||
class Log extends ContentProcessDomain {
|
class Log extends ContentProcessDomain {
|
||||||
constructor(session) {
|
constructor(session) {
|
||||||
|
|
@ -57,7 +59,7 @@ class Log extends ContentProcessDomain {
|
||||||
entry = fromConsoleAPI(message.wrappedJSObject);
|
entry = fromConsoleAPI(message.wrappedJSObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.emit("Log.entryAdded", {entry});
|
this.emit("Log.entryAdded", { entry });
|
||||||
}
|
}
|
||||||
|
|
||||||
// XPCOM
|
// XPCOM
|
||||||
|
|
@ -91,12 +93,11 @@ function fromConsoleAPI(message) {
|
||||||
// A couple of possible level are defined here:
|
// A couple of possible level are defined here:
|
||||||
// https://searchfox.org/mozilla-central/rev/00c0d068ece99717bea7475f7dc07e61f7f35984/dom/console/Console.cpp#1086-1100
|
// https://searchfox.org/mozilla-central/rev/00c0d068ece99717bea7475f7dc07e61f7f35984/dom/console/Console.cpp#1086-1100
|
||||||
const levels = {
|
const levels = {
|
||||||
"log": "verbose",
|
log: "verbose",
|
||||||
"info": "info",
|
info: "info",
|
||||||
"warn": "warning",
|
warn: "warning",
|
||||||
"error": "error",
|
error: "error",
|
||||||
"exception": "error",
|
exception: "error",
|
||||||
|
|
||||||
};
|
};
|
||||||
const level = levels[message.level] || "info";
|
const level = levels[message.level] || "info";
|
||||||
|
|
||||||
|
|
@ -112,15 +113,19 @@ function fromConsoleAPI(message) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function fromScriptError(error) {
|
function fromScriptError(error) {
|
||||||
const {flags, errorMessage, sourceName, lineNumber, stack} = error;
|
const { flags, errorMessage, sourceName, lineNumber, stack } = error;
|
||||||
|
|
||||||
// lossy reduction from bitmask to CDP string level
|
// lossy reduction from bitmask to CDP string level
|
||||||
let level = "verbose";
|
let level = "verbose";
|
||||||
if ((flags & Ci.nsIScriptError.exceptionFlag) ||
|
if (
|
||||||
(flags & Ci.nsIScriptError.errorFlag)) {
|
flags & Ci.nsIScriptError.exceptionFlag ||
|
||||||
|
flags & Ci.nsIScriptError.errorFlag
|
||||||
|
) {
|
||||||
level = "error";
|
level = "error";
|
||||||
} else if ((flags & Ci.nsIScriptError.warningFlag) ||
|
} else if (
|
||||||
(flags & Ci.nsIScriptError.strictFlag)) {
|
flags & Ci.nsIScriptError.warningFlag ||
|
||||||
|
flags & Ci.nsIScriptError.strictFlag
|
||||||
|
) {
|
||||||
level = "warning";
|
level = "warning";
|
||||||
} else if (flags & Ci.nsIScriptError.infoFlag) {
|
} else if (flags & Ci.nsIScriptError.infoFlag) {
|
||||||
level = "info";
|
level = "info";
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,13 @@
|
||||||
|
|
||||||
var EXPORTED_SYMBOLS = ["Page"];
|
var EXPORTED_SYMBOLS = ["Page"];
|
||||||
|
|
||||||
const {ContentProcessDomain} = ChromeUtils.import("chrome://remote/content/domains/ContentProcessDomain.jsm");
|
const { ContentProcessDomain } = ChromeUtils.import(
|
||||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
"chrome://remote/content/domains/ContentProcessDomain.jsm"
|
||||||
const {UnsupportedError} = ChromeUtils.import("chrome://remote/content/Error.jsm");
|
);
|
||||||
|
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||||
|
const { UnsupportedError } = ChromeUtils.import(
|
||||||
|
"chrome://remote/content/Error.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
class Page extends ContentProcessDomain {
|
class Page extends ContentProcessDomain {
|
||||||
constructor(session) {
|
constructor(session) {
|
||||||
|
|
@ -31,10 +35,12 @@ class Page extends ContentProcessDomain {
|
||||||
this.enabled = true;
|
this.enabled = true;
|
||||||
this.contextObserver.on("frame-navigated", this.onFrameNavigated);
|
this.contextObserver.on("frame-navigated", this.onFrameNavigated);
|
||||||
|
|
||||||
this.chromeEventHandler.addEventListener("DOMContentLoaded", this,
|
this.chromeEventHandler.addEventListener("DOMContentLoaded", this, {
|
||||||
{mozSystemGroup: true});
|
mozSystemGroup: true,
|
||||||
this.chromeEventHandler.addEventListener("pageshow", this,
|
});
|
||||||
{mozSystemGroup: true});
|
this.chromeEventHandler.addEventListener("pageshow", this, {
|
||||||
|
mozSystemGroup: true,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -42,15 +48,17 @@ class Page extends ContentProcessDomain {
|
||||||
if (this.enabled) {
|
if (this.enabled) {
|
||||||
this.contextObserver.off("frame-navigated", this.onFrameNavigated);
|
this.contextObserver.off("frame-navigated", this.onFrameNavigated);
|
||||||
|
|
||||||
this.chromeEventHandler.removeEventListener("DOMContentLoaded", this,
|
this.chromeEventHandler.removeEventListener("DOMContentLoaded", this, {
|
||||||
{mozSystemGroup: true});
|
mozSystemGroup: true,
|
||||||
this.chromeEventHandler.removeEventListener("pageshow", this,
|
});
|
||||||
{mozSystemGroup: true});
|
this.chromeEventHandler.removeEventListener("pageshow", this, {
|
||||||
|
mozSystemGroup: true,
|
||||||
|
});
|
||||||
this.enabled = false;
|
this.enabled = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async navigate({url, referrer, transitionType, frameId} = {}) {
|
async navigate({ url, referrer, transitionType, frameId } = {}) {
|
||||||
if (frameId && frameId != this.content.windowUtils.outerWindowID) {
|
if (frameId && frameId != this.content.windowUtils.outerWindowID) {
|
||||||
throw new UnsupportedError("frameId not supported");
|
throw new UnsupportedError("frameId not supported");
|
||||||
}
|
}
|
||||||
|
|
@ -108,7 +116,7 @@ class Page extends ContentProcessDomain {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEvent({type, target}) {
|
handleEvent({ type, target }) {
|
||||||
if (target.defaultView != this.content) {
|
if (target.defaultView != this.content) {
|
||||||
// Ignore iframes for now
|
// Ignore iframes for now
|
||||||
return;
|
return;
|
||||||
|
|
@ -119,26 +127,26 @@ class Page extends ContentProcessDomain {
|
||||||
const url = target.location.href;
|
const url = target.location.href;
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "DOMContentLoaded":
|
case "DOMContentLoaded":
|
||||||
this.emit("Page.domContentEventFired", {timestamp});
|
this.emit("Page.domContentEventFired", { timestamp });
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "pageshow":
|
case "pageshow":
|
||||||
this.emit("Page.loadEventFired", {timestamp, frameId});
|
this.emit("Page.loadEventFired", { timestamp, frameId });
|
||||||
// XXX this should most likely be sent differently
|
// XXX this should most likely be sent differently
|
||||||
this.emit("Page.navigatedWithinDocument", {timestamp, frameId, url});
|
this.emit("Page.navigatedWithinDocument", { timestamp, frameId, url });
|
||||||
this.emit("Page.frameStoppedLoading", {timestamp, frameId});
|
this.emit("Page.frameStoppedLoading", { timestamp, frameId });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function transitionToLoadFlag(transitionType) {
|
function transitionToLoadFlag(transitionType) {
|
||||||
switch (transitionType) {
|
switch (transitionType) {
|
||||||
case "reload":
|
case "reload":
|
||||||
return Ci.nsIWebNavigation.LOAD_FLAGS_IS_REFRESH;
|
return Ci.nsIWebNavigation.LOAD_FLAGS_IS_REFRESH;
|
||||||
case "link":
|
case "link":
|
||||||
default:
|
default:
|
||||||
return Ci.nsIWebNavigation.LOAD_FLAGS_IS_LINK;
|
return Ci.nsIWebNavigation.LOAD_FLAGS_IS_LINK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,9 @@
|
||||||
|
|
||||||
var EXPORTED_SYMBOLS = ["Performance"];
|
var EXPORTED_SYMBOLS = ["Performance"];
|
||||||
|
|
||||||
const {ContentProcessDomain} = ChromeUtils.import("chrome://remote/content/domains/ContentProcessDomain.jsm");
|
const { ContentProcessDomain } = ChromeUtils.import(
|
||||||
|
"chrome://remote/content/domains/ContentProcessDomain.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
class Performance extends ContentProcessDomain {
|
class Performance extends ContentProcessDomain {
|
||||||
constructor(session) {
|
constructor(session) {
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,17 @@
|
||||||
|
|
||||||
var EXPORTED_SYMBOLS = ["Runtime"];
|
var EXPORTED_SYMBOLS = ["Runtime"];
|
||||||
|
|
||||||
const {ContentProcessDomain} = ChromeUtils.import("chrome://remote/content/domains/ContentProcessDomain.jsm");
|
const { ContentProcessDomain } = ChromeUtils.import(
|
||||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
"chrome://remote/content/domains/ContentProcessDomain.jsm"
|
||||||
const {ExecutionContext} = ChromeUtils.import("chrome://remote/content/domains/content/runtime/ExecutionContext.jsm");
|
);
|
||||||
const {addDebuggerToGlobal} = ChromeUtils.import("resource://gre/modules/jsdebugger.jsm", {});
|
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||||
|
const { ExecutionContext } = ChromeUtils.import(
|
||||||
|
"chrome://remote/content/domains/content/runtime/ExecutionContext.jsm"
|
||||||
|
);
|
||||||
|
const { addDebuggerToGlobal } = ChromeUtils.import(
|
||||||
|
"resource://gre/modules/jsdebugger.jsm",
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
// Import the `Debugger` constructor in the current scope
|
// Import the `Debugger` constructor in the current scope
|
||||||
addDebuggerToGlobal(Cu.getGlobalForObject(this));
|
addDebuggerToGlobal(Cu.getGlobalForObject(this));
|
||||||
|
|
@ -63,11 +70,15 @@ class Runtime extends ContentProcessDomain {
|
||||||
evaluate(request) {
|
evaluate(request) {
|
||||||
const context = this.contexts.get(request.contextId);
|
const context = this.contexts.get(request.contextId);
|
||||||
if (!context) {
|
if (!context) {
|
||||||
throw new Error(`Unable to find execution context with id: ${request.contextId}`);
|
throw new Error(
|
||||||
|
`Unable to find execution context with id: ${request.contextId}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (typeof(request.expression) != "string") {
|
if (typeof request.expression != "string") {
|
||||||
throw new Error(`Expecting 'expression' attribute to be a string. ` +
|
throw new Error(
|
||||||
`But was: ${typeof(request.expression)}`);
|
`Expecting 'expression' attribute to be a string. ` +
|
||||||
|
`But was: ${typeof request.expression}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return context.evaluate(request.expression);
|
return context.evaluate(request.expression);
|
||||||
}
|
}
|
||||||
|
|
@ -75,7 +86,7 @@ class Runtime extends ContentProcessDomain {
|
||||||
getRemoteObject(objectId) {
|
getRemoteObject(objectId) {
|
||||||
for (const ctx of this.contexts.values()) {
|
for (const ctx of this.contexts.values()) {
|
||||||
const obj = ctx.getRemoteObject(objectId);
|
const obj = ctx.getRemoteObject(objectId);
|
||||||
if (typeof(obj) != "undefined") {
|
if (typeof obj != "undefined") {
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -108,33 +119,47 @@ class Runtime extends ContentProcessDomain {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!context) {
|
if (!context) {
|
||||||
throw new Error(`Unable to get the context for object with id: ${request.objectId}`);
|
throw new Error(
|
||||||
|
`Unable to get the context for object with id: ${request.objectId}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
context = this.contexts.get(request.executionContextId);
|
context = this.contexts.get(request.executionContextId);
|
||||||
if (!context) {
|
if (!context) {
|
||||||
throw new Error(`Unable to find execution context with id: ${request.executionContextId}`);
|
throw new Error(
|
||||||
|
`Unable to find execution context with id: ${
|
||||||
|
request.executionContextId
|
||||||
|
}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (typeof(request.functionDeclaration) != "string") {
|
if (typeof request.functionDeclaration != "string") {
|
||||||
throw new Error("Expect 'functionDeclaration' attribute to be passed and be a string");
|
throw new Error(
|
||||||
|
"Expect 'functionDeclaration' attribute to be passed and be a string"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (request.arguments && !Array.isArray(request.arguments)) {
|
if (request.arguments && !Array.isArray(request.arguments)) {
|
||||||
throw new Error("Expect 'arguments' to be an array");
|
throw new Error("Expect 'arguments' to be an array");
|
||||||
}
|
}
|
||||||
if (request.returnByValue && typeof(request.returnByValue) != "boolean") {
|
if (request.returnByValue && typeof request.returnByValue != "boolean") {
|
||||||
throw new Error("Expect 'returnByValue' to be a boolean");
|
throw new Error("Expect 'returnByValue' to be a boolean");
|
||||||
}
|
}
|
||||||
if (request.awaitPromise && typeof(request.awaitPromise) != "boolean") {
|
if (request.awaitPromise && typeof request.awaitPromise != "boolean") {
|
||||||
throw new Error("Expect 'awaitPromise' to be a boolean");
|
throw new Error("Expect 'awaitPromise' to be a boolean");
|
||||||
}
|
}
|
||||||
return context.callFunctionOn(request.functionDeclaration, request.arguments, request.returnByValue, request.awaitPromise, request.objectId);
|
return context.callFunctionOn(
|
||||||
|
request.functionDeclaration,
|
||||||
|
request.arguments,
|
||||||
|
request.returnByValue,
|
||||||
|
request.awaitPromise,
|
||||||
|
request.objectId
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getProperties({ objectId, ownProperties }) {
|
getProperties({ objectId, ownProperties }) {
|
||||||
for (const ctx of this.contexts.values()) {
|
for (const ctx of this.contexts.values()) {
|
||||||
const obj = ctx.getRemoteObject(objectId);
|
const obj = ctx.getRemoteObject(objectId);
|
||||||
if (typeof(obj) != "undefined") {
|
if (typeof obj != "undefined") {
|
||||||
return ctx.getProperties({ objectId, ownProperties });
|
return ctx.getProperties({ objectId, ownProperties });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,9 @@
|
||||||
|
|
||||||
var EXPORTED_SYMBOLS = ["Security"];
|
var EXPORTED_SYMBOLS = ["Security"];
|
||||||
|
|
||||||
const {ContentProcessDomain} = ChromeUtils.import("chrome://remote/content/domains/ContentProcessDomain.jsm");
|
const { ContentProcessDomain } = ChromeUtils.import(
|
||||||
|
"chrome://remote/content/domains/ContentProcessDomain.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
class Security extends ContentProcessDomain {
|
class Security extends ContentProcessDomain {
|
||||||
constructor(session) {
|
constructor(session) {
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,26 @@
|
||||||
|
|
||||||
var EXPORTED_SYMBOLS = ["ExecutionContext"];
|
var EXPORTED_SYMBOLS = ["ExecutionContext"];
|
||||||
|
|
||||||
const uuidGen = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
|
const uuidGen = Cc["@mozilla.org/uuid-generator;1"].getService(
|
||||||
|
Ci.nsIUUIDGenerator
|
||||||
|
);
|
||||||
|
|
||||||
const TYPED_ARRAY_CLASSES = ["Uint8Array", "Uint8ClampedArray", "Uint16Array",
|
const TYPED_ARRAY_CLASSES = [
|
||||||
"Uint32Array", "Int8Array", "Int16Array", "Int32Array",
|
"Uint8Array",
|
||||||
"Float32Array", "Float64Array"];
|
"Uint8ClampedArray",
|
||||||
|
"Uint16Array",
|
||||||
|
"Uint32Array",
|
||||||
|
"Int8Array",
|
||||||
|
"Int16Array",
|
||||||
|
"Int32Array",
|
||||||
|
"Float32Array",
|
||||||
|
"Float64Array",
|
||||||
|
];
|
||||||
function uuid() {
|
function uuid() {
|
||||||
return uuidGen.generateUUID().toString().slice(1, -1);
|
return uuidGen
|
||||||
|
.generateUUID()
|
||||||
|
.toString()
|
||||||
|
.slice(1, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -89,10 +102,15 @@ class ExecutionContext {
|
||||||
* describing the exception by following CDP ExceptionDetails specification.
|
* describing the exception by following CDP ExceptionDetails specification.
|
||||||
*/
|
*/
|
||||||
_returnError(exception) {
|
_returnError(exception) {
|
||||||
if (this._debuggee.executeInGlobalWithBindings("exception instanceof Error",
|
if (
|
||||||
{exception}).return) {
|
this._debuggee.executeInGlobalWithBindings("exception instanceof Error", {
|
||||||
const text = this._debuggee.executeInGlobalWithBindings("exception.message",
|
exception,
|
||||||
{exception}).return;
|
}).return
|
||||||
|
) {
|
||||||
|
const text = this._debuggee.executeInGlobalWithBindings(
|
||||||
|
"exception.message",
|
||||||
|
{ exception }
|
||||||
|
).return;
|
||||||
return {
|
return {
|
||||||
exceptionDetails: {
|
exceptionDetails: {
|
||||||
text,
|
text,
|
||||||
|
|
@ -108,7 +126,13 @@ class ExecutionContext {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async callFunctionOn(functionDeclaration, callArguments = [], returnByValue = false, awaitPromise = false, objectId = null) {
|
async callFunctionOn(
|
||||||
|
functionDeclaration,
|
||||||
|
callArguments = [],
|
||||||
|
returnByValue = false,
|
||||||
|
awaitPromise = false,
|
||||||
|
objectId = null
|
||||||
|
) {
|
||||||
// Map the given objectId to a JS reference.
|
// Map the given objectId to a JS reference.
|
||||||
let thisArg = null;
|
let thisArg = null;
|
||||||
if (objectId) {
|
if (objectId) {
|
||||||
|
|
@ -188,8 +212,12 @@ class ExecutionContext {
|
||||||
enumerable: descriptor.enumerable,
|
enumerable: descriptor.enumerable,
|
||||||
writable: descriptor.writable,
|
writable: descriptor.writable,
|
||||||
value: this._toRemoteObject(descriptor.value),
|
value: this._toRemoteObject(descriptor.value),
|
||||||
get: descriptor.get ? this._toRemoteObject(descriptor.get) : undefined,
|
get: descriptor.get
|
||||||
set: descriptor.set ? this._toRemoteObject(descriptor.set) : undefined,
|
? this._toRemoteObject(descriptor.get)
|
||||||
|
: undefined,
|
||||||
|
set: descriptor.set
|
||||||
|
? this._toRemoteObject(descriptor.set)
|
||||||
|
: undefined,
|
||||||
|
|
||||||
isOwn,
|
isOwn,
|
||||||
});
|
});
|
||||||
|
|
@ -224,10 +252,13 @@ class ExecutionContext {
|
||||||
* The JSON string
|
* The JSON string
|
||||||
*/
|
*/
|
||||||
_serialize(obj) {
|
_serialize(obj) {
|
||||||
if (typeof(obj) == "undefined") {
|
if (typeof obj == "undefined") {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
const result = this._debuggee.executeInGlobalWithBindings("JSON.stringify(e)", {e: obj});
|
const result = this._debuggee.executeInGlobalWithBindings(
|
||||||
|
"JSON.stringify(e)",
|
||||||
|
{ e: obj }
|
||||||
|
);
|
||||||
if (result.throw) {
|
if (result.throw) {
|
||||||
throw new Error("Object is not serializable");
|
throw new Error("Object is not serializable");
|
||||||
}
|
}
|
||||||
|
|
@ -247,10 +278,14 @@ class ExecutionContext {
|
||||||
}
|
}
|
||||||
if (arg.unserializableValue) {
|
if (arg.unserializableValue) {
|
||||||
switch (arg.unserializableValue) {
|
switch (arg.unserializableValue) {
|
||||||
case "Infinity": return Infinity;
|
case "Infinity":
|
||||||
case "-Infinity": return -Infinity;
|
return Infinity;
|
||||||
case "-0": return -0;
|
case "-Infinity":
|
||||||
case "NaN": return NaN;
|
return -Infinity;
|
||||||
|
case "-0":
|
||||||
|
return -0;
|
||||||
|
case "NaN":
|
||||||
|
return NaN;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this._deserialize(arg.value);
|
return this._deserialize(arg.value);
|
||||||
|
|
@ -263,8 +298,10 @@ class ExecutionContext {
|
||||||
if (typeof obj !== "object") {
|
if (typeof obj !== "object") {
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
const result = this._debuggee.executeInGlobalWithBindings("JSON.parse(obj)",
|
const result = this._debuggee.executeInGlobalWithBindings(
|
||||||
{obj: JSON.stringify(obj)});
|
"JSON.parse(obj)",
|
||||||
|
{ obj: JSON.stringify(obj) }
|
||||||
|
);
|
||||||
if (result.throw) {
|
if (result.throw) {
|
||||||
throw new Error("Unable to deserialize object");
|
throw new Error("Unable to deserialize object");
|
||||||
}
|
}
|
||||||
|
|
@ -318,7 +355,7 @@ class ExecutionContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
const type = typeof rawObj;
|
const type = typeof rawObj;
|
||||||
return {objectId, type, subtype};
|
return { objectId, type, subtype };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now, handle all values that Debugger API isn't wrapping into Debugger.API.
|
// Now, handle all values that Debugger API isn't wrapping into Debugger.API.
|
||||||
|
|
@ -331,19 +368,20 @@ class ExecutionContext {
|
||||||
if (type == "symbol" || type == "bigint") {
|
if (type == "symbol" || type == "bigint") {
|
||||||
const objectId = uuid();
|
const objectId = uuid();
|
||||||
this._remoteObjects.set(objectId, debuggerObj);
|
this._remoteObjects.set(objectId, debuggerObj);
|
||||||
return {objectId, type};
|
return { objectId, type };
|
||||||
}
|
}
|
||||||
|
|
||||||
// A few primitive type can't be serialized and CDP has special case for them
|
// A few primitive type can't be serialized and CDP has special case for them
|
||||||
let unserializableValue = undefined;
|
let unserializableValue = undefined;
|
||||||
if (Object.is(debuggerObj, NaN))
|
if (Object.is(debuggerObj, NaN)) {
|
||||||
unserializableValue = "NaN";
|
unserializableValue = "NaN";
|
||||||
else if (Object.is(debuggerObj, -0))
|
} else if (Object.is(debuggerObj, -0)) {
|
||||||
unserializableValue = "-0";
|
unserializableValue = "-0";
|
||||||
else if (Object.is(debuggerObj, Infinity))
|
} else if (Object.is(debuggerObj, Infinity)) {
|
||||||
unserializableValue = "Infinity";
|
unserializableValue = "Infinity";
|
||||||
else if (Object.is(debuggerObj, -Infinity))
|
} else if (Object.is(debuggerObj, -Infinity)) {
|
||||||
unserializableValue = "-Infinity";
|
unserializableValue = "-Infinity";
|
||||||
|
}
|
||||||
if (unserializableValue) {
|
if (unserializableValue) {
|
||||||
return {
|
return {
|
||||||
unserializableValue,
|
unserializableValue,
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,19 @@
|
||||||
|
|
||||||
var EXPORTED_SYMBOLS = ["Browser"];
|
var EXPORTED_SYMBOLS = ["Browser"];
|
||||||
|
|
||||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||||
const {Domain} = ChromeUtils.import("chrome://remote/content/domains/Domain.jsm");
|
const { Domain } = ChromeUtils.import(
|
||||||
|
"chrome://remote/content/domains/Domain.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
class Browser extends Domain {
|
class Browser extends Domain {
|
||||||
getVersion() {
|
getVersion() {
|
||||||
const { isHeadless } = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
|
const { isHeadless } = Cc["@mozilla.org/gfx/info;1"].getService(
|
||||||
const { userAgent } = Cc["@mozilla.org/network/protocol;1?name=http"]
|
Ci.nsIGfxInfo
|
||||||
.getService(Ci.nsIHttpProtocolHandler);
|
);
|
||||||
|
const { userAgent } = Cc[
|
||||||
|
"@mozilla.org/network/protocol;1?name=http"
|
||||||
|
].getService(Ci.nsIHttpProtocolHandler);
|
||||||
return {
|
return {
|
||||||
protocolVersion: "1",
|
protocolVersion: "1",
|
||||||
product: (isHeadless ? "Headless " : "") + "Firefox",
|
product: (isHeadless ? "Headless " : "") + "Firefox",
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,10 @@
|
||||||
|
|
||||||
var EXPORTED_SYMBOLS = ["Input"];
|
var EXPORTED_SYMBOLS = ["Input"];
|
||||||
|
|
||||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||||
const {Domain} = ChromeUtils.import("chrome://remote/content/domains/Domain.jsm");
|
const { Domain } = ChromeUtils.import(
|
||||||
|
"chrome://remote/content/domains/Domain.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
class Input extends Domain {
|
class Input extends Domain {
|
||||||
// commands
|
// commands
|
||||||
|
|
@ -28,12 +30,7 @@ class Input extends Domain {
|
||||||
* - windowsVirtualKeyCode
|
* - windowsVirtualKeyCode
|
||||||
*/
|
*/
|
||||||
async dispatchKeyEvent(options) {
|
async dispatchKeyEvent(options) {
|
||||||
const {
|
const { key, modifiers, type, windowsVirtualKeyCode } = options;
|
||||||
key,
|
|
||||||
modifiers,
|
|
||||||
type,
|
|
||||||
windowsVirtualKeyCode,
|
|
||||||
} = options;
|
|
||||||
|
|
||||||
let domType;
|
let domType;
|
||||||
if (type == "keyDown" || type == "rawKeyDown") {
|
if (type == "keyDown" || type == "rawKeyDown") {
|
||||||
|
|
@ -54,8 +51,10 @@ class Input extends Domain {
|
||||||
|
|
||||||
const EventUtils = this._getEventUtils(browserWindow);
|
const EventUtils = this._getEventUtils(browserWindow);
|
||||||
const onEvent = new Promise(r => {
|
const onEvent = new Promise(r => {
|
||||||
browserWindow.addEventListener(domType, r,
|
browserWindow.addEventListener(domType, r, {
|
||||||
{mozSystemGroup: true, once: true});
|
mozSystemGroup: true,
|
||||||
|
once: true,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (type == "char") {
|
if (type == "char") {
|
||||||
|
|
@ -65,21 +64,23 @@ class Input extends Domain {
|
||||||
} else {
|
} else {
|
||||||
// Non printable keys should be prefixed with `KEY_`
|
// Non printable keys should be prefixed with `KEY_`
|
||||||
const eventUtilsKey = key.length == 1 ? key : "KEY_" + key;
|
const eventUtilsKey = key.length == 1 ? key : "KEY_" + key;
|
||||||
EventUtils.synthesizeKey(eventUtilsKey, {
|
EventUtils.synthesizeKey(
|
||||||
keyCode: windowsVirtualKeyCode,
|
eventUtilsKey,
|
||||||
type: domType,
|
{
|
||||||
altKey: !!(modifiers & 1),
|
keyCode: windowsVirtualKeyCode,
|
||||||
ctrlKey: !!(modifiers & 2),
|
type: domType,
|
||||||
metaKey: !!(modifiers & 4),
|
altKey: !!(modifiers & 1),
|
||||||
shiftKey: !!(modifiers & 8),
|
ctrlKey: !!(modifiers & 2),
|
||||||
}, browserWindow);
|
metaKey: !!(modifiers & 4),
|
||||||
|
shiftKey: !!(modifiers & 8),
|
||||||
|
},
|
||||||
|
browserWindow
|
||||||
|
);
|
||||||
}
|
}
|
||||||
await onEvent;
|
await onEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
async dispatchMouseEvent({
|
async dispatchMouseEvent({ type, button, x, y, modifiers, clickCount }) {
|
||||||
type, button, x, y, modifiers, clickCount,
|
|
||||||
}) {
|
|
||||||
if (type == "mousePressed") {
|
if (type == "mousePressed") {
|
||||||
type = "mousedown";
|
type = "mousedown";
|
||||||
} else if (type == "mouseReleased") {
|
} else if (type == "mouseReleased") {
|
||||||
|
|
@ -135,8 +136,10 @@ class Input extends Domain {
|
||||||
_EU_Ci: Ci,
|
_EU_Ci: Ci,
|
||||||
_EU_Cc: Cc,
|
_EU_Cc: Cc,
|
||||||
};
|
};
|
||||||
Services.scriptloader.loadSubScript("chrome://remote/content/external/EventUtils.js",
|
Services.scriptloader.loadSubScript(
|
||||||
this._eventUtils);
|
"chrome://remote/content/external/EventUtils.js",
|
||||||
|
this._eventUtils
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return this._eventUtils;
|
return this._eventUtils;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,12 @@
|
||||||
|
|
||||||
var EXPORTED_SYMBOLS = ["Network"];
|
var EXPORTED_SYMBOLS = ["Network"];
|
||||||
|
|
||||||
const {Domain} = ChromeUtils.import("chrome://remote/content/domains/Domain.jsm");
|
const { Domain } = ChromeUtils.import(
|
||||||
const {NetworkObserver} = ChromeUtils.import("chrome://remote/content/domains/parent/network/NetworkObserver.jsm");
|
"chrome://remote/content/domains/Domain.jsm"
|
||||||
|
);
|
||||||
|
const { NetworkObserver } = ChromeUtils.import(
|
||||||
|
"chrome://remote/content/domains/parent/network/NetworkObserver.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
const LOAD_CAUSE_STRINGS = {
|
const LOAD_CAUSE_STRINGS = {
|
||||||
[Ci.nsIContentPolicy.TYPE_INVALID]: "Invalid",
|
[Ci.nsIContentPolicy.TYPE_INVALID]: "Invalid",
|
||||||
|
|
@ -120,13 +124,17 @@ function getLoadContext(httpChannel) {
|
||||||
let loadContext = null;
|
let loadContext = null;
|
||||||
try {
|
try {
|
||||||
if (httpChannel.notificationCallbacks) {
|
if (httpChannel.notificationCallbacks) {
|
||||||
loadContext = httpChannel.notificationCallbacks.getInterface(Ci.nsILoadContext);
|
loadContext = httpChannel.notificationCallbacks.getInterface(
|
||||||
|
Ci.nsILoadContext
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
try {
|
try {
|
||||||
if (!loadContext && httpChannel.loadGroup) {
|
if (!loadContext && httpChannel.loadGroup) {
|
||||||
loadContext = httpChannel.loadGroup.notificationCallbacks.getInterface(Ci.nsILoadContext);
|
loadContext = httpChannel.loadGroup.notificationCallbacks.getInterface(
|
||||||
|
Ci.nsILoadContext
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch (e) { }
|
} catch (e) {}
|
||||||
return loadContext;
|
return loadContext;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,9 @@
|
||||||
|
|
||||||
var EXPORTED_SYMBOLS = ["Page"];
|
var EXPORTED_SYMBOLS = ["Page"];
|
||||||
|
|
||||||
const {Domain} = ChromeUtils.import("chrome://remote/content/domains/Domain.jsm");
|
const { Domain } = ChromeUtils.import(
|
||||||
|
"chrome://remote/content/domains/Domain.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
class Page extends Domain {
|
class Page extends Domain {
|
||||||
// commands
|
// commands
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,18 @@
|
||||||
|
|
||||||
var EXPORTED_SYMBOLS = ["Target"];
|
var EXPORTED_SYMBOLS = ["Target"];
|
||||||
|
|
||||||
const {Domain} = ChromeUtils.import("chrome://remote/content/domains/Domain.jsm");
|
const { Domain } = ChromeUtils.import(
|
||||||
const {TabManager} = ChromeUtils.import("chrome://remote/content/WindowManager.jsm");
|
"chrome://remote/content/domains/Domain.jsm"
|
||||||
const {TabSession} = ChromeUtils.import("chrome://remote/content/sessions/TabSession.jsm");
|
);
|
||||||
const {ContextualIdentityService} = ChromeUtils.import("resource://gre/modules/ContextualIdentityService.jsm");
|
const { TabManager } = ChromeUtils.import(
|
||||||
|
"chrome://remote/content/WindowManager.jsm"
|
||||||
|
);
|
||||||
|
const { TabSession } = ChromeUtils.import(
|
||||||
|
"chrome://remote/content/sessions/TabSession.jsm"
|
||||||
|
);
|
||||||
|
const { ContextualIdentityService } = ChromeUtils.import(
|
||||||
|
"resource://gre/modules/ContextualIdentityService.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
let sessionIds = 1;
|
let sessionIds = 1;
|
||||||
let browserContextIds = 1;
|
let browserContextIds = 1;
|
||||||
|
|
@ -29,7 +37,9 @@ class Target extends Domain {
|
||||||
}
|
}
|
||||||
|
|
||||||
createBrowserContext() {
|
createBrowserContext() {
|
||||||
const identity = ContextualIdentityService.create("remote-agent-" + (browserContextIds++));
|
const identity = ContextualIdentityService.create(
|
||||||
|
"remote-agent-" + browserContextIds++
|
||||||
|
);
|
||||||
return { browserContextId: identity.userContextId };
|
return { browserContextId: identity.userContextId };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -75,9 +85,11 @@ class Target extends Domain {
|
||||||
const tab = TabManager.addTab({ userContextId: browserContextId });
|
const tab = TabManager.addTab({ userContextId: browserContextId });
|
||||||
const target = await onTarget;
|
const target = await onTarget;
|
||||||
if (tab.linkedBrowser != target.browser) {
|
if (tab.linkedBrowser != target.browser) {
|
||||||
throw new Error("Unexpected tab opened: " + tab.linkedBrowser.currentURI.spec);
|
throw new Error(
|
||||||
|
"Unexpected tab opened: " + tab.linkedBrowser.currentURI.spec
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return {targetId: target.id};
|
return { targetId: target.id };
|
||||||
}
|
}
|
||||||
|
|
||||||
closeTarget({ targetId }) {
|
closeTarget({ targetId }) {
|
||||||
|
|
@ -93,7 +105,12 @@ class Target extends Domain {
|
||||||
return new Error(`Unable to find target with id '${targetId}'`);
|
return new Error(`Unable to find target with id '${targetId}'`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const session = new TabSession(this.session.connection, target, sessionIds++, this.session);
|
const session = new TabSession(
|
||||||
|
this.session.connection,
|
||||||
|
target,
|
||||||
|
sessionIds++,
|
||||||
|
this.session
|
||||||
|
);
|
||||||
this.emit("Target.attachedToTarget", {
|
this.emit("Target.attachedToTarget", {
|
||||||
targetInfo: {
|
targetInfo: {
|
||||||
type: "page",
|
type: "page",
|
||||||
|
|
|
||||||
|
|
@ -4,17 +4,33 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const {EventEmitter} = ChromeUtils.import("resource://gre/modules/EventEmitter.jsm");
|
const { EventEmitter } = ChromeUtils.import(
|
||||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
"resource://gre/modules/EventEmitter.jsm"
|
||||||
const {NetUtil} = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
|
);
|
||||||
const {CommonUtils} = ChromeUtils.import("resource://services-common/utils.js");
|
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||||
|
const { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
|
||||||
|
const { CommonUtils } = ChromeUtils.import(
|
||||||
|
"resource://services-common/utils.js"
|
||||||
|
);
|
||||||
|
|
||||||
const Cm = Components.manager;
|
const Cm = Components.manager;
|
||||||
const CC = Components.Constructor;
|
const CC = Components.Constructor;
|
||||||
|
|
||||||
const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", "nsIBinaryInputStream", "setInputStream");
|
const BinaryInputStream = CC(
|
||||||
const BinaryOutputStream = CC("@mozilla.org/binaryoutputstream;1", "nsIBinaryOutputStream", "setOutputStream");
|
"@mozilla.org/binaryinputstream;1",
|
||||||
const StorageStream = CC("@mozilla.org/storagestream;1", "nsIStorageStream", "init");
|
"nsIBinaryInputStream",
|
||||||
|
"setInputStream"
|
||||||
|
);
|
||||||
|
const BinaryOutputStream = CC(
|
||||||
|
"@mozilla.org/binaryoutputstream;1",
|
||||||
|
"nsIBinaryOutputStream",
|
||||||
|
"setOutputStream"
|
||||||
|
);
|
||||||
|
const StorageStream = CC(
|
||||||
|
"@mozilla.org/storagestream;1",
|
||||||
|
"nsIStorageStream",
|
||||||
|
"init"
|
||||||
|
);
|
||||||
|
|
||||||
// Cap response storage with 100Mb per tracked tab.
|
// Cap response storage with 100Mb per tracked tab.
|
||||||
const MAX_RESPONSE_STORAGE_SIZE = 100 * 1024 * 1024;
|
const MAX_RESPONSE_STORAGE_SIZE = 100 * 1024 * 1024;
|
||||||
|
|
@ -36,7 +52,9 @@ class NetworkObserver {
|
||||||
constructor() {
|
constructor() {
|
||||||
EventEmitter.decorate(this);
|
EventEmitter.decorate(this);
|
||||||
this._browserSessionCount = new Map();
|
this._browserSessionCount = new Map();
|
||||||
this._activityDistributor = Cc["@mozilla.org/network/http-activity-distributor;1"].getService(Ci.nsIHttpActivityDistributor);
|
this._activityDistributor = Cc[
|
||||||
|
"@mozilla.org/network/http-activity-distributor;1"
|
||||||
|
].getService(Ci.nsIHttpActivityDistributor);
|
||||||
this._activityDistributor.addObserver(this);
|
this._activityDistributor.addObserver(this);
|
||||||
|
|
||||||
this._redirectMap = new Map();
|
this._redirectMap = new Map();
|
||||||
|
|
@ -53,8 +71,19 @@ class NetworkObserver {
|
||||||
};
|
};
|
||||||
// Register self as ChannelEventSink to track redirects.
|
// Register self as ChannelEventSink to track redirects.
|
||||||
const registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
|
const registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
|
||||||
registrar.registerFactory(SINK_CLASS_ID, SINK_CLASS_DESCRIPTION, SINK_CONTRACT_ID, this._channelSinkFactory);
|
registrar.registerFactory(
|
||||||
Services.catMan.addCategoryEntry(SINK_CATEGORY_NAME, SINK_CONTRACT_ID, SINK_CONTRACT_ID, false, true);
|
SINK_CLASS_ID,
|
||||||
|
SINK_CLASS_DESCRIPTION,
|
||||||
|
SINK_CONTRACT_ID,
|
||||||
|
this._channelSinkFactory
|
||||||
|
);
|
||||||
|
Services.catMan.addCategoryEntry(
|
||||||
|
SINK_CATEGORY_NAME,
|
||||||
|
SINK_CONTRACT_ID,
|
||||||
|
SINK_CONTRACT_ID,
|
||||||
|
false,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
// Request interception state.
|
// Request interception state.
|
||||||
this._browserSuspendedChannels = new Map();
|
this._browserSuspendedChannels = new Map();
|
||||||
|
|
@ -62,12 +91,24 @@ class NetworkObserver {
|
||||||
this._browserResponseStorages = new Map();
|
this._browserResponseStorages = new Map();
|
||||||
|
|
||||||
this._onRequest = this._onRequest.bind(this);
|
this._onRequest = this._onRequest.bind(this);
|
||||||
this._onExamineResponse = this._onResponse.bind(this, false /* fromCache */);
|
this._onExamineResponse = this._onResponse.bind(
|
||||||
|
this,
|
||||||
|
false /* fromCache */
|
||||||
|
);
|
||||||
this._onCachedResponse = this._onResponse.bind(this, true /* fromCache */);
|
this._onCachedResponse = this._onResponse.bind(this, true /* fromCache */);
|
||||||
Services.obs.addObserver(this._onRequest, "http-on-modify-request");
|
Services.obs.addObserver(this._onRequest, "http-on-modify-request");
|
||||||
Services.obs.addObserver(this._onExamineResponse, "http-on-examine-response");
|
Services.obs.addObserver(
|
||||||
Services.obs.addObserver(this._onCachedResponse, "http-on-examine-cached-response");
|
this._onExamineResponse,
|
||||||
Services.obs.addObserver(this._onCachedResponse, "http-on-examine-merged-response");
|
"http-on-examine-response"
|
||||||
|
);
|
||||||
|
Services.obs.addObserver(
|
||||||
|
this._onCachedResponse,
|
||||||
|
"http-on-examine-cached-response"
|
||||||
|
);
|
||||||
|
Services.obs.addObserver(
|
||||||
|
this._onCachedResponse,
|
||||||
|
"http-on-examine-merged-response"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
setExtraHTTPHeaders(browser, headers) {
|
setExtraHTTPHeaders(browser, headers) {
|
||||||
|
|
@ -111,7 +152,11 @@ class NetworkObserver {
|
||||||
}
|
}
|
||||||
// 2. Set new headers.
|
// 2. Set new headers.
|
||||||
for (const header of headers) {
|
for (const header of headers) {
|
||||||
httpChannel.setRequestHeader(header.name, header.value, false /* merge */);
|
httpChannel.setRequestHeader(
|
||||||
|
header.name,
|
||||||
|
header.value,
|
||||||
|
false /* merge */
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
suspendedChannels.delete(requestId);
|
suspendedChannels.delete(requestId);
|
||||||
|
|
@ -150,14 +195,26 @@ class NetworkObserver {
|
||||||
}
|
}
|
||||||
const httpChannel = oldChannel.QueryInterface(Ci.nsIHttpChannel);
|
const httpChannel = oldChannel.QueryInterface(Ci.nsIHttpChannel);
|
||||||
const loadContext = getLoadContext(httpChannel);
|
const loadContext = getLoadContext(httpChannel);
|
||||||
if (!loadContext || !this._browserSessionCount.has(loadContext.topFrameElement)) {
|
if (
|
||||||
|
!loadContext ||
|
||||||
|
!this._browserSessionCount.has(loadContext.topFrameElement)
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._redirectMap.set(newChannel, oldChannel);
|
this._redirectMap.set(newChannel, oldChannel);
|
||||||
}
|
}
|
||||||
|
|
||||||
observeActivity(channel, activityType, activitySubtype, timestamp, extraSizeData, extraStringData) {
|
observeActivity(
|
||||||
if (activityType !== Ci.nsIHttpActivityObserver.ACTIVITY_TYPE_HTTP_TRANSACTION) {
|
channel,
|
||||||
|
activityType,
|
||||||
|
activitySubtype,
|
||||||
|
timestamp,
|
||||||
|
extraSizeData,
|
||||||
|
extraStringData
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
activityType !== Ci.nsIHttpActivityObserver.ACTIVITY_TYPE_HTTP_TRANSACTION
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!(channel instanceof Ci.nsIHttpChannel)) {
|
if (!(channel instanceof Ci.nsIHttpChannel)) {
|
||||||
|
|
@ -165,10 +222,16 @@ class NetworkObserver {
|
||||||
}
|
}
|
||||||
const httpChannel = channel.QueryInterface(Ci.nsIHttpChannel);
|
const httpChannel = channel.QueryInterface(Ci.nsIHttpChannel);
|
||||||
const loadContext = getLoadContext(httpChannel);
|
const loadContext = getLoadContext(httpChannel);
|
||||||
if (!loadContext || !this._browserSessionCount.has(loadContext.topFrameElement)) {
|
if (
|
||||||
|
!loadContext ||
|
||||||
|
!this._browserSessionCount.has(loadContext.topFrameElement)
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (activitySubtype !== Ci.nsIHttpActivityObserver.ACTIVITY_SUBTYPE_TRANSACTION_CLOSE) {
|
if (
|
||||||
|
activitySubtype !==
|
||||||
|
Ci.nsIHttpActivityObserver.ACTIVITY_SUBTYPE_TRANSACTION_CLOSE
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.emit("requestfinished", httpChannel, {
|
this.emit("requestfinished", httpChannel, {
|
||||||
|
|
@ -182,17 +245,30 @@ class NetworkObserver {
|
||||||
}
|
}
|
||||||
const httpChannel = channel.QueryInterface(Ci.nsIHttpChannel);
|
const httpChannel = channel.QueryInterface(Ci.nsIHttpChannel);
|
||||||
const loadContext = getLoadContext(httpChannel);
|
const loadContext = getLoadContext(httpChannel);
|
||||||
if (!loadContext || !this._browserSessionCount.has(loadContext.topFrameElement)) {
|
if (
|
||||||
|
!loadContext ||
|
||||||
|
!this._browserSessionCount.has(loadContext.topFrameElement)
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const extraHeaders = this._extraHTTPHeaders.get(loadContext.topFrameElement);
|
const extraHeaders = this._extraHTTPHeaders.get(
|
||||||
|
loadContext.topFrameElement
|
||||||
|
);
|
||||||
if (extraHeaders) {
|
if (extraHeaders) {
|
||||||
for (const header of extraHeaders) {
|
for (const header of extraHeaders) {
|
||||||
httpChannel.setRequestHeader(header.name, header.value, false /* merge */);
|
httpChannel.setRequestHeader(
|
||||||
|
header.name,
|
||||||
|
header.value,
|
||||||
|
false /* merge */
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const causeType = httpChannel.loadInfo ? httpChannel.loadInfo.externalContentPolicyType : Ci.nsIContentPolicy.TYPE_OTHER;
|
const causeType = httpChannel.loadInfo
|
||||||
const suspendedChannels = this._browserSuspendedChannels.get(loadContext.topFrameElement);
|
? httpChannel.loadInfo.externalContentPolicyType
|
||||||
|
: Ci.nsIContentPolicy.TYPE_OTHER;
|
||||||
|
const suspendedChannels = this._browserSuspendedChannels.get(
|
||||||
|
loadContext.topFrameElement
|
||||||
|
);
|
||||||
if (suspendedChannels) {
|
if (suspendedChannels) {
|
||||||
httpChannel.suspend();
|
httpChannel.suspend();
|
||||||
suspendedChannels.set(requestId(httpChannel), httpChannel);
|
suspendedChannels.set(requestId(httpChannel), httpChannel);
|
||||||
|
|
@ -218,13 +294,16 @@ class NetworkObserver {
|
||||||
|
|
||||||
_onResponse(fromCache, httpChannel, topic) {
|
_onResponse(fromCache, httpChannel, topic) {
|
||||||
const loadContext = getLoadContext(httpChannel);
|
const loadContext = getLoadContext(httpChannel);
|
||||||
if (!loadContext || !this._browserSessionCount.has(loadContext.topFrameElement)) {
|
if (
|
||||||
|
!loadContext ||
|
||||||
|
!this._browserSessionCount.has(loadContext.topFrameElement)
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
httpChannel.QueryInterface(Ci.nsIHttpChannelInternal);
|
httpChannel.QueryInterface(Ci.nsIHttpChannelInternal);
|
||||||
const headers = [];
|
const headers = [];
|
||||||
httpChannel.visitResponseHeaders({
|
httpChannel.visitResponseHeaders({
|
||||||
visitHeader: (name, value) => headers.push({name, value}),
|
visitHeader: (name, value) => headers.push({ name, value }),
|
||||||
});
|
});
|
||||||
|
|
||||||
let remoteIPAddress = undefined;
|
let remoteIPAddress = undefined;
|
||||||
|
|
@ -262,7 +341,13 @@ class NetworkObserver {
|
||||||
const value = this._browserSessionCount.get(browser) || 0;
|
const value = this._browserSessionCount.get(browser) || 0;
|
||||||
this._browserSessionCount.set(browser, value + 1);
|
this._browserSessionCount.set(browser, value + 1);
|
||||||
if (value === 0) {
|
if (value === 0) {
|
||||||
this._browserResponseStorages.set(browser, new ResponseStorage(MAX_RESPONSE_STORAGE_SIZE, MAX_RESPONSE_STORAGE_SIZE / 10));
|
this._browserResponseStorages.set(
|
||||||
|
browser,
|
||||||
|
new ResponseStorage(
|
||||||
|
MAX_RESPONSE_STORAGE_SIZE,
|
||||||
|
MAX_RESPONSE_STORAGE_SIZE / 10
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return () => this.stopTrackingBrowserNetwork(browser);
|
return () => this.stopTrackingBrowserNetwork(browser);
|
||||||
}
|
}
|
||||||
|
|
@ -281,11 +366,24 @@ class NetworkObserver {
|
||||||
this._activityDistributor.removeObserver(this);
|
this._activityDistributor.removeObserver(this);
|
||||||
const registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
|
const registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
|
||||||
registrar.unregisterFactory(SINK_CLASS_ID, this._channelSinkFactory);
|
registrar.unregisterFactory(SINK_CLASS_ID, this._channelSinkFactory);
|
||||||
Services.catMan.deleteCategoryEntry(SINK_CATEGORY_NAME, SINK_CONTRACT_ID, false);
|
Services.catMan.deleteCategoryEntry(
|
||||||
|
SINK_CATEGORY_NAME,
|
||||||
|
SINK_CONTRACT_ID,
|
||||||
|
false
|
||||||
|
);
|
||||||
Services.obs.removeObserver(this._onRequest, "http-on-modify-request");
|
Services.obs.removeObserver(this._onRequest, "http-on-modify-request");
|
||||||
Services.obs.removeObserver(this._onExamineResponse, "http-on-examine-response");
|
Services.obs.removeObserver(
|
||||||
Services.obs.removeObserver(this._onCachedResponse, "http-on-examine-cached-response");
|
this._onExamineResponse,
|
||||||
Services.obs.removeObserver(this._onCachedResponse, "http-on-examine-merged-response");
|
"http-on-examine-response"
|
||||||
|
);
|
||||||
|
Services.obs.removeObserver(
|
||||||
|
this._onCachedResponse,
|
||||||
|
"http-on-examine-cached-response"
|
||||||
|
);
|
||||||
|
Services.obs.removeObserver(
|
||||||
|
this._onCachedResponse,
|
||||||
|
"http-on-examine-merged-response"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -335,8 +433,9 @@ function readRequestPostData(httpChannel) {
|
||||||
let text = undefined;
|
let text = undefined;
|
||||||
try {
|
try {
|
||||||
text = NetUtil.readInputStreamToString(iStream, iStream.available());
|
text = NetUtil.readInputStreamToString(iStream, iStream.available());
|
||||||
const converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
|
const converter = Cc[
|
||||||
.createInstance(Ci.nsIScriptableUnicodeConverter);
|
"@mozilla.org/intl/scriptableunicodeconverter"
|
||||||
|
].createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||||
converter.charset = "UTF-8";
|
converter.charset = "UTF-8";
|
||||||
text = converter.ConvertToUnicode(text);
|
text = converter.ConvertToUnicode(text);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
@ -356,14 +455,18 @@ function getLoadContext(httpChannel) {
|
||||||
let loadContext = null;
|
let loadContext = null;
|
||||||
try {
|
try {
|
||||||
if (httpChannel.notificationCallbacks) {
|
if (httpChannel.notificationCallbacks) {
|
||||||
loadContext = httpChannel.notificationCallbacks.getInterface(Ci.nsILoadContext);
|
loadContext = httpChannel.notificationCallbacks.getInterface(
|
||||||
|
Ci.nsILoadContext
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
try {
|
try {
|
||||||
if (!loadContext && httpChannel.loadGroup) {
|
if (!loadContext && httpChannel.loadGroup) {
|
||||||
loadContext = httpChannel.loadGroup.notificationCallbacks.getInterface(Ci.nsILoadContext);
|
loadContext = httpChannel.loadGroup.notificationCallbacks.getInterface(
|
||||||
|
Ci.nsILoadContext
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} catch (e) { }
|
} catch (e) {}
|
||||||
return loadContext;
|
return loadContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -374,7 +477,7 @@ function requestId(httpChannel) {
|
||||||
function requestHeaders(httpChannel) {
|
function requestHeaders(httpChannel) {
|
||||||
const headers = [];
|
const headers = [];
|
||||||
httpChannel.visitRequestHeaders({
|
httpChannel.visitRequestHeaders({
|
||||||
visitHeader: (name, value) => headers.push({name, value}),
|
visitHeader: (name, value) => headers.push({ name, value }),
|
||||||
});
|
});
|
||||||
return headers;
|
return headers;
|
||||||
}
|
}
|
||||||
|
|
@ -405,11 +508,15 @@ class ResponseStorage {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let encodings = [];
|
let encodings = [];
|
||||||
if ((httpChannel instanceof Ci.nsIEncodedChannel) && httpChannel.contentEncodings && !httpChannel.applyConversion) {
|
if (
|
||||||
|
httpChannel instanceof Ci.nsIEncodedChannel &&
|
||||||
|
httpChannel.contentEncodings &&
|
||||||
|
!httpChannel.applyConversion
|
||||||
|
) {
|
||||||
const encodingHeader = httpChannel.getResponseHeader("Content-Encoding");
|
const encodingHeader = httpChannel.getResponseHeader("Content-Encoding");
|
||||||
encodings = encodingHeader.split(/\s*\t*,\s*\t*/);
|
encodings = encodingHeader.split(/\s*\t*,\s*\t*/);
|
||||||
}
|
}
|
||||||
this._responses.set(requestId(httpChannel), {body, encodings});
|
this._responses.set(requestId(httpChannel), { body, encodings });
|
||||||
this._totalSize += body.length;
|
this._totalSize += body.length;
|
||||||
if (this._totalSize > this._maxTotalSize) {
|
if (this._totalSize > this._maxTotalSize) {
|
||||||
for (let [, response] of this._responses) {
|
for (let [, response] of this._responses) {
|
||||||
|
|
@ -425,17 +532,19 @@ class ResponseStorage {
|
||||||
|
|
||||||
getBase64EncodedResponse(requestId) {
|
getBase64EncodedResponse(requestId) {
|
||||||
const response = this._responses.get(requestId);
|
const response = this._responses.get(requestId);
|
||||||
if (!response)
|
if (!response) {
|
||||||
throw new Error(`Request "${requestId}" is not found`);
|
throw new Error(`Request "${requestId}" is not found`);
|
||||||
if (response.evicted)
|
}
|
||||||
return {base64body: "", evicted: true};
|
if (response.evicted) {
|
||||||
|
return { base64body: "", evicted: true };
|
||||||
|
}
|
||||||
let result = response.body;
|
let result = response.body;
|
||||||
if (response.encodings && response.encodings.length) {
|
if (response.encodings && response.encodings.length) {
|
||||||
for (const encoding of response.encodings) {
|
for (const encoding of response.encodings) {
|
||||||
result = CommonUtils.convertString(result, encoding, "uncompressed");
|
result = CommonUtils.convertString(result, encoding, "uncompressed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {base64body: btoa(result)};
|
return { base64body: btoa(result) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -460,7 +569,12 @@ class ResponseBodyListener {
|
||||||
this._chunks.push(data);
|
this._chunks.push(data);
|
||||||
|
|
||||||
oStream.writeBytes(data, aCount);
|
oStream.writeBytes(data, aCount);
|
||||||
this.originalListener.onDataAvailable(aRequest, sStream.newInputStream(0), aOffset, aCount);
|
this.originalListener.onDataAvailable(
|
||||||
|
aRequest,
|
||||||
|
sStream.newInputStream(0),
|
||||||
|
aOffset,
|
||||||
|
aCount
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
onStartRequest(aRequest) {
|
onStartRequest(aRequest) {
|
||||||
|
|
@ -471,25 +585,35 @@ class ResponseBodyListener {
|
||||||
this.originalListener.onStopRequest(aRequest, aStatusCode);
|
this.originalListener.onStopRequest(aRequest, aStatusCode);
|
||||||
const body = this._chunks.join("");
|
const body = this._chunks.join("");
|
||||||
delete this._chunks;
|
delete this._chunks;
|
||||||
this._networkObserver._onResponseFinished(this._browser, this._httpChannel, body);
|
this._networkObserver._onResponseFinished(
|
||||||
|
this._browser,
|
||||||
|
this._httpChannel,
|
||||||
|
body
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNetworkErrorStatusText(status) {
|
function getNetworkErrorStatusText(status) {
|
||||||
if (!status)
|
if (!status) {
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
for (const key of Object.keys(Cr)) {
|
for (const key of Object.keys(Cr)) {
|
||||||
if (Cr[key] === status)
|
if (Cr[key] === status) {
|
||||||
return key;
|
return key;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Security module. The following is taken from
|
// Security module. The following is taken from
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/How_to_check_the_secruity_state_of_an_XMLHTTPRequest_over_SSL
|
// https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/How_to_check_the_secruity_state_of_an_XMLHTTPRequest_over_SSL
|
||||||
if ((status & 0xff0000) === 0x5a0000) {
|
if ((status & 0xff0000) === 0x5a0000) {
|
||||||
// NSS_SEC errors (happen below the base value because of negative vals)
|
// NSS_SEC errors (happen below the base value because of negative vals)
|
||||||
if ((status & 0xffff) < Math.abs(Ci.nsINSSErrorsService.NSS_SEC_ERROR_BASE)) {
|
if (
|
||||||
|
(status & 0xffff) <
|
||||||
|
Math.abs(Ci.nsINSSErrorsService.NSS_SEC_ERROR_BASE)
|
||||||
|
) {
|
||||||
// The bases are actually negative, so in our positive numeric space, we
|
// The bases are actually negative, so in our positive numeric space, we
|
||||||
// need to subtract the base off our value.
|
// need to subtract the base off our value.
|
||||||
const nssErr = Math.abs(Ci.nsINSSErrorsService.NSS_SEC_ERROR_BASE) - (status & 0xffff);
|
const nssErr =
|
||||||
|
Math.abs(Ci.nsINSSErrorsService.NSS_SEC_ERROR_BASE) - (status & 0xffff);
|
||||||
switch (nssErr) {
|
switch (nssErr) {
|
||||||
case 11:
|
case 11:
|
||||||
return "SEC_ERROR_EXPIRED_CERTIFICATE";
|
return "SEC_ERROR_EXPIRED_CERTIFICATE";
|
||||||
|
|
@ -511,7 +635,8 @@ function getNetworkErrorStatusText(status) {
|
||||||
return "SEC_ERROR_UNKNOWN";
|
return "SEC_ERROR_UNKNOWN";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const sslErr = Math.abs(Ci.nsINSSErrorsService.NSS_SSL_ERROR_BASE) - (status & 0xffff);
|
const sslErr =
|
||||||
|
Math.abs(Ci.nsINSSErrorsService.NSS_SSL_ERROR_BASE) - (status & 0xffff);
|
||||||
switch (sslErr) {
|
switch (sslErr) {
|
||||||
case 3:
|
case 3:
|
||||||
return "SSL_ERROR_NO_CERTIFICATE";
|
return "SSL_ERROR_NO_CERTIFICATE";
|
||||||
|
|
|
||||||
|
|
@ -10,15 +10,23 @@ var EXPORTED_SYMBOLS = ["WebSocketServer"];
|
||||||
|
|
||||||
const CC = Components.Constructor;
|
const CC = Components.Constructor;
|
||||||
|
|
||||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||||
const {Stream} = ChromeUtils.import("chrome://remote/content/server/Stream.jsm");
|
const { Stream } = ChromeUtils.import(
|
||||||
const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
"chrome://remote/content/server/Stream.jsm"
|
||||||
|
);
|
||||||
|
const { XPCOMUtils } = ChromeUtils.import(
|
||||||
|
"resource://gre/modules/XPCOMUtils.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
XPCOMUtils.defineLazyGetter(this, "WebSocket", () => {
|
XPCOMUtils.defineLazyGetter(this, "WebSocket", () => {
|
||||||
return Services.appShell.hiddenDOMWindow.WebSocket;
|
return Services.appShell.hiddenDOMWindow.WebSocket;
|
||||||
});
|
});
|
||||||
|
|
||||||
const CryptoHash = CC("@mozilla.org/security/hash;1", "nsICryptoHash", "initWithString");
|
const CryptoHash = CC(
|
||||||
|
"@mozilla.org/security/hash;1",
|
||||||
|
"nsICryptoHash",
|
||||||
|
"initWithString"
|
||||||
|
);
|
||||||
const threadManager = Cc["@mozilla.org/thread-manager;1"].getService();
|
const threadManager = Cc["@mozilla.org/thread-manager;1"].getService();
|
||||||
|
|
||||||
// limit the header size to put an upper bound on allocated memory
|
// limit the header size to put an upper bound on allocated memory
|
||||||
|
|
@ -36,26 +44,32 @@ function readLine(input) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let line = "";
|
let line = "";
|
||||||
const wait = () => {
|
const wait = () => {
|
||||||
input.asyncWait(stream => {
|
input.asyncWait(
|
||||||
try {
|
stream => {
|
||||||
const amountToRead = HEADER_MAX_LEN - line.length;
|
try {
|
||||||
line += Stream.delimitedRead(input, "\n", amountToRead);
|
const amountToRead = HEADER_MAX_LEN - line.length;
|
||||||
|
line += Stream.delimitedRead(input, "\n", amountToRead);
|
||||||
|
|
||||||
if (line.endsWith("\n")) {
|
if (line.endsWith("\n")) {
|
||||||
resolve(line.trimRight());
|
resolve(line.trimRight());
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.length >= HEADER_MAX_LEN) {
|
||||||
|
throw new Error(
|
||||||
|
`Failed to read HTTP header longer than ${HEADER_MAX_LEN} bytes`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
wait();
|
||||||
|
} catch (ex) {
|
||||||
|
reject(ex);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
if (line.length >= HEADER_MAX_LEN) {
|
0,
|
||||||
throw new Error(
|
0,
|
||||||
`Failed to read HTTP header longer than ${HEADER_MAX_LEN} bytes`);
|
threadManager.currentThread
|
||||||
}
|
);
|
||||||
|
|
||||||
wait();
|
|
||||||
} catch (ex) {
|
|
||||||
reject(ex);
|
|
||||||
}
|
|
||||||
}, 0, 0, threadManager.currentThread);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
wait();
|
wait();
|
||||||
|
|
@ -76,15 +90,20 @@ function writeString(output, data) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
output.asyncWait(stream => {
|
output.asyncWait(
|
||||||
try {
|
stream => {
|
||||||
const written = output.write(data, data.length);
|
try {
|
||||||
data = data.slice(written);
|
const written = output.write(data, data.length);
|
||||||
wait();
|
data = data.slice(written);
|
||||||
} catch (ex) {
|
wait();
|
||||||
reject(ex);
|
} catch (ex) {
|
||||||
}
|
reject(ex);
|
||||||
}, 0, 0, threadManager.currentThread);
|
}
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
threadManager.currentThread
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
wait();
|
wait();
|
||||||
|
|
@ -120,7 +139,7 @@ const readHttpRequest = async function(input) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {requestLine, headers};
|
return { requestLine, headers };
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Write HTTP response (array of strings) to async output stream. */
|
/** Write HTTP response (array of strings) to async output stream. */
|
||||||
|
|
@ -133,7 +152,7 @@ function writeHttpResponse(output, response) {
|
||||||
* Process the WebSocket handshake headers and return the key to be sent in
|
* Process the WebSocket handshake headers and return the key to be sent in
|
||||||
* Sec-WebSocket-Accept response header.
|
* Sec-WebSocket-Accept response header.
|
||||||
*/
|
*/
|
||||||
function processRequest({requestLine, headers}) {
|
function processRequest({ requestLine, headers }) {
|
||||||
const method = requestLine.split(" ")[0];
|
const method = requestLine.split(" ")[0];
|
||||||
if (method !== "GET") {
|
if (method !== "GET") {
|
||||||
throw new Error("The handshake request must use GET method");
|
throw new Error("The handshake request must use GET method");
|
||||||
|
|
@ -145,19 +164,29 @@ function processRequest({requestLine, headers}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const connection = headers.get("connection");
|
const connection = headers.get("connection");
|
||||||
if (!connection || !connection.split(",").map(t => t.trim()).includes("Upgrade")) {
|
if (
|
||||||
|
!connection ||
|
||||||
|
!connection
|
||||||
|
.split(",")
|
||||||
|
.map(t => t.trim())
|
||||||
|
.includes("Upgrade")
|
||||||
|
) {
|
||||||
throw new Error("The handshake request has incorrect Connection header");
|
throw new Error("The handshake request has incorrect Connection header");
|
||||||
}
|
}
|
||||||
|
|
||||||
const version = headers.get("sec-websocket-version");
|
const version = headers.get("sec-websocket-version");
|
||||||
if (!version || version !== "13") {
|
if (!version || version !== "13") {
|
||||||
throw new Error("The handshake request must have Sec-WebSocket-Version: 13");
|
throw new Error(
|
||||||
|
"The handshake request must have Sec-WebSocket-Version: 13"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute the accept key
|
// Compute the accept key
|
||||||
const key = headers.get("sec-websocket-key");
|
const key = headers.get("sec-websocket-key");
|
||||||
if (!key) {
|
if (!key) {
|
||||||
throw new Error("The handshake request must have a Sec-WebSocket-Key header");
|
throw new Error(
|
||||||
|
"The handshake request must have a Sec-WebSocket-Key header"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { acceptKey: computeKey(key) };
|
return { acceptKey: computeKey(key) };
|
||||||
|
|
@ -178,7 +207,7 @@ function computeKey(key) {
|
||||||
async function serverHandshake(request, output) {
|
async function serverHandshake(request, output) {
|
||||||
try {
|
try {
|
||||||
// Check and extract info from the request
|
// Check and extract info from the request
|
||||||
const {acceptKey} = processRequest(request);
|
const { acceptKey } = processRequest(request);
|
||||||
|
|
||||||
// Send response headers
|
// Send response headers
|
||||||
await writeHttpResponse(output, [
|
await writeHttpResponse(output, [
|
||||||
|
|
@ -189,7 +218,7 @@ async function serverHandshake(request, output) {
|
||||||
]);
|
]);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Send error response in case of error
|
// Send error response in case of error
|
||||||
await writeHttpResponse(output, [ "HTTP/1.1 400 Bad Request" ]);
|
await writeHttpResponse(output, ["HTTP/1.1 400 Bad Request"]);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -205,7 +234,12 @@ async function createWebSocket(transport, input, output) {
|
||||||
};
|
};
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const socket = WebSocket.createServerWebSocket(null, [], transportProvider, "");
|
const socket = WebSocket.createServerWebSocket(
|
||||||
|
null,
|
||||||
|
[],
|
||||||
|
transportProvider,
|
||||||
|
""
|
||||||
|
);
|
||||||
socket.addEventListener("close", () => {
|
socket.addEventListener("close", () => {
|
||||||
input.close();
|
input.close();
|
||||||
output.close();
|
output.close();
|
||||||
|
|
@ -233,7 +267,7 @@ async function upgrade(request, response) {
|
||||||
// handle response manually, allowing us to send arbitrary data
|
// handle response manually, allowing us to send arbitrary data
|
||||||
response._powerSeized = true;
|
response._powerSeized = true;
|
||||||
|
|
||||||
const {transport, input, output} = response._connection;
|
const { transport, input, output } = response._connection;
|
||||||
|
|
||||||
const headers = new Map();
|
const headers = new Map();
|
||||||
for (let [key, values] of Object.entries(request._headers._headers)) {
|
for (let [key, values] of Object.entries(request._headers._headers)) {
|
||||||
|
|
@ -248,4 +282,4 @@ async function upgrade(request, response) {
|
||||||
return createWebSocket(transport, input, output);
|
return createWebSocket(transport, input, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
const WebSocketServer = {accept, upgrade};
|
const WebSocketServer = { accept, upgrade };
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,9 @@
|
||||||
|
|
||||||
var EXPORTED_SYMBOLS = ["WebSocketDebuggerTransport"];
|
var EXPORTED_SYMBOLS = ["WebSocketDebuggerTransport"];
|
||||||
|
|
||||||
const {EventEmitter} = ChromeUtils.import("resource://gre/modules/EventEmitter.jsm");
|
const { EventEmitter } = ChromeUtils.import(
|
||||||
|
"resource://gre/modules/EventEmitter.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
function WebSocketDebuggerTransport(socket) {
|
function WebSocketDebuggerTransport(socket) {
|
||||||
EventEmitter.decorate(this);
|
EventEmitter.decorate(this);
|
||||||
|
|
@ -72,7 +74,9 @@ WebSocketDebuggerTransport.prototype = {
|
||||||
|
|
||||||
onMessage({ data }) {
|
onMessage({ data }) {
|
||||||
if (typeof data !== "string") {
|
if (typeof data !== "string") {
|
||||||
throw new Error("Binary messages are not supported by WebSocket transport");
|
throw new Error(
|
||||||
|
"Binary messages are not supported by WebSocket transport"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const object = JSON.parse(data);
|
const object = JSON.parse(data);
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,15 @@
|
||||||
|
|
||||||
var EXPORTED_SYMBOLS = ["ContentProcessSession"];
|
var EXPORTED_SYMBOLS = ["ContentProcessSession"];
|
||||||
|
|
||||||
const {ContentProcessDomains} = ChromeUtils.import("chrome://remote/content/domains/ContentProcessDomains.jsm");
|
const { ContentProcessDomains } = ChromeUtils.import(
|
||||||
const {Domains} = ChromeUtils.import("chrome://remote/content/domains/Domains.jsm");
|
"chrome://remote/content/domains/ContentProcessDomains.jsm"
|
||||||
const {UnknownMethodError} = ChromeUtils.import("chrome://remote/content/Error.jsm");
|
);
|
||||||
|
const { Domains } = ChromeUtils.import(
|
||||||
|
"chrome://remote/content/domains/Domains.jsm"
|
||||||
|
);
|
||||||
|
const { UnknownMethodError } = ChromeUtils.import(
|
||||||
|
"chrome://remote/content/Error.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
class ContentProcessSession {
|
class ContentProcessSession {
|
||||||
constructor(messageManager, browsingContext, content, docShell) {
|
constructor(messageManager, browsingContext, content, docShell) {
|
||||||
|
|
@ -45,8 +51,8 @@ class ContentProcessSession {
|
||||||
|
|
||||||
// nsIMessageListener
|
// nsIMessageListener
|
||||||
|
|
||||||
async receiveMessage({name, data}) {
|
async receiveMessage({ name, data }) {
|
||||||
const {browsingContextId} = data;
|
const { browsingContextId } = data;
|
||||||
|
|
||||||
// We may have more than one tab loaded in the same process,
|
// We may have more than one tab loaded in the same process,
|
||||||
// and debug the two at the same time. We want to ensure not
|
// and debug the two at the same time. We want to ensure not
|
||||||
|
|
@ -59,36 +65,36 @@ class ContentProcessSession {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case "remote:request":
|
case "remote:request":
|
||||||
try {
|
try {
|
||||||
const {id, domain, command, params} = data.request;
|
const { id, domain, command, params } = data.request;
|
||||||
if (!this.domains.domainSupportsMethod(domain, command)) {
|
if (!this.domains.domainSupportsMethod(domain, command)) {
|
||||||
throw new UnknownMethodError(domain, command);
|
throw new UnknownMethodError(domain, command);
|
||||||
|
}
|
||||||
|
const inst = this.domains.get(domain);
|
||||||
|
const result = await inst[command](params);
|
||||||
|
|
||||||
|
this.messageManager.sendAsyncMessage("remote:result", {
|
||||||
|
browsingContextId,
|
||||||
|
id,
|
||||||
|
result,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
this.messageManager.sendAsyncMessage("remote:error", {
|
||||||
|
browsingContextId,
|
||||||
|
id: data.request.id,
|
||||||
|
error: {
|
||||||
|
name: e.name || "exception",
|
||||||
|
message: e.message || String(e),
|
||||||
|
stack: e.stack,
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
const inst = this.domains.get(domain);
|
break;
|
||||||
const result = await inst[command](params);
|
|
||||||
|
|
||||||
this.messageManager.sendAsyncMessage("remote:result", {
|
case "remote:destroy":
|
||||||
browsingContextId,
|
this.destroy();
|
||||||
id,
|
break;
|
||||||
result,
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
this.messageManager.sendAsyncMessage("remote:error", {
|
|
||||||
browsingContextId,
|
|
||||||
id: data.request.id,
|
|
||||||
error: {
|
|
||||||
name: e.name || "exception",
|
|
||||||
message: e.message || String(e),
|
|
||||||
stack: e.stack,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "remote:destroy":
|
|
||||||
this.destroy();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,15 @@
|
||||||
|
|
||||||
var EXPORTED_SYMBOLS = ["Session"];
|
var EXPORTED_SYMBOLS = ["Session"];
|
||||||
|
|
||||||
const {ParentProcessDomains} = ChromeUtils.import("chrome://remote/content/domains/ParentProcessDomains.jsm");
|
const { ParentProcessDomains } = ChromeUtils.import(
|
||||||
const {Domains} = ChromeUtils.import("chrome://remote/content/domains/Domains.jsm");
|
"chrome://remote/content/domains/ParentProcessDomains.jsm"
|
||||||
const {
|
);
|
||||||
RemoteAgentError,
|
const { Domains } = ChromeUtils.import(
|
||||||
UnknownMethodError,
|
"chrome://remote/content/domains/Domains.jsm"
|
||||||
} = ChromeUtils.import("chrome://remote/content/Error.jsm");
|
);
|
||||||
|
const { RemoteAgentError, UnknownMethodError } = ChromeUtils.import(
|
||||||
|
"chrome://remote/content/Error.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A session represents exactly one client WebSocket connection.
|
* A session represents exactly one client WebSocket connection.
|
||||||
|
|
@ -55,7 +58,7 @@ class Session {
|
||||||
this.domains.clear();
|
this.domains.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
async onMessage({id, method, params}) {
|
async onMessage({ id, method, params }) {
|
||||||
try {
|
try {
|
||||||
if (typeof id == "undefined") {
|
if (typeof id == "undefined") {
|
||||||
throw new TypeError("Message missing 'id' field");
|
throw new TypeError("Message missing 'id' field");
|
||||||
|
|
@ -64,7 +67,7 @@ class Session {
|
||||||
throw new TypeError("Message missing 'method' field");
|
throw new TypeError("Message missing 'method' field");
|
||||||
}
|
}
|
||||||
|
|
||||||
const {domain, command} = Domains.splitMethod(method);
|
const { domain, command } = Domains.splitMethod(method);
|
||||||
await this.execute(id, domain, command, params);
|
await this.execute(id, domain, command, params);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.onError(id, e);
|
this.onError(id, e);
|
||||||
|
|
@ -93,7 +96,7 @@ class Session {
|
||||||
id,
|
id,
|
||||||
sessionId: this.id,
|
sessionId: this.id,
|
||||||
error: {
|
error: {
|
||||||
message: RemoteAgentError.format(error, {stack: true}),
|
message: RemoteAgentError.format(error, { stack: true }),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,12 @@
|
||||||
|
|
||||||
var EXPORTED_SYMBOLS = ["TabSession"];
|
var EXPORTED_SYMBOLS = ["TabSession"];
|
||||||
|
|
||||||
const {Domains} = ChromeUtils.import("chrome://remote/content/domains/Domains.jsm");
|
const { Domains } = ChromeUtils.import(
|
||||||
const {Session} = ChromeUtils.import("chrome://remote/content/sessions/Session.jsm");
|
"chrome://remote/content/domains/Domains.jsm"
|
||||||
|
);
|
||||||
|
const { Session } = ChromeUtils.import(
|
||||||
|
"chrome://remote/content/sessions/Session.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A session to communicate with a given tab
|
* A session to communicate with a given tab
|
||||||
|
|
@ -36,7 +40,10 @@ class TabSession extends Session {
|
||||||
this.mm.addMessageListener("remote:result", this);
|
this.mm.addMessageListener("remote:result", this);
|
||||||
this.mm.addMessageListener("remote:error", this);
|
this.mm.addMessageListener("remote:error", this);
|
||||||
|
|
||||||
this.mm.loadFrameScript("chrome://remote/content/sessions/frame-script.js", false);
|
this.mm.loadFrameScript(
|
||||||
|
"chrome://remote/content/sessions/frame-script.js",
|
||||||
|
false
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
destructor() {
|
destructor() {
|
||||||
|
|
@ -51,7 +58,7 @@ class TabSession extends Session {
|
||||||
this.mm.removeMessageListener("remote:error", this);
|
this.mm.removeMessageListener("remote:error", this);
|
||||||
}
|
}
|
||||||
|
|
||||||
async onMessage({id, method, params}) {
|
async onMessage({ id, method, params }) {
|
||||||
try {
|
try {
|
||||||
if (typeof id == "undefined") {
|
if (typeof id == "undefined") {
|
||||||
throw new TypeError("Message missing 'id' field");
|
throw new TypeError("Message missing 'id' field");
|
||||||
|
|
@ -60,7 +67,7 @@ class TabSession extends Session {
|
||||||
throw new TypeError("Message missing 'method' field");
|
throw new TypeError("Message missing 'method' field");
|
||||||
}
|
}
|
||||||
|
|
||||||
const {domain, command} = Domains.splitMethod(method);
|
const { domain, command } = Domains.splitMethod(method);
|
||||||
if (this.domains.domainSupportsMethod(domain, command)) {
|
if (this.domains.domainSupportsMethod(domain, command)) {
|
||||||
await this.execute(id, domain, command, params);
|
await this.execute(id, domain, command, params);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -74,7 +81,7 @@ class TabSession extends Session {
|
||||||
executeInChild(id, domain, command, params) {
|
executeInChild(id, domain, command, params) {
|
||||||
this.mm.sendAsyncMessage("remote:request", {
|
this.mm.sendAsyncMessage("remote:request", {
|
||||||
browsingContextId: this.browsingContext.id,
|
browsingContextId: this.browsingContext.id,
|
||||||
request: {id, domain, command, params},
|
request: { id, domain, command, params },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -117,21 +124,21 @@ class TabSession extends Session {
|
||||||
|
|
||||||
// nsIMessageListener
|
// nsIMessageListener
|
||||||
|
|
||||||
receiveMessage({name, data}) {
|
receiveMessage({ name, data }) {
|
||||||
const {id, result, event, error} = data;
|
const { id, result, event, error } = data;
|
||||||
|
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case "remote:result":
|
case "remote:result":
|
||||||
this.onResult(id, result);
|
this.onResult(id, result);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "remote:event":
|
case "remote:event":
|
||||||
this.onEvent(event.eventName, event.params);
|
this.onEvent(event.eventName, event.params);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "remote:error":
|
case "remote:error":
|
||||||
this.onError(id, error);
|
this.onError(id, error);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,9 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const {ContentProcessSession} =
|
const { ContentProcessSession } = ChromeUtils.import(
|
||||||
ChromeUtils.import("chrome://remote/content/sessions/ContentProcessSession.jsm");
|
"chrome://remote/content/sessions/ContentProcessSession.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
/* global content, docShell */
|
/* global content, docShell */
|
||||||
new ContentProcessSession(this, docShell.browsingContext, content, docShell);
|
new ContentProcessSession(this, docShell.browsingContext, content, docShell);
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,19 @@
|
||||||
|
|
||||||
var EXPORTED_SYMBOLS = ["MainProcessTarget"];
|
var EXPORTED_SYMBOLS = ["MainProcessTarget"];
|
||||||
|
|
||||||
const {Target} = ChromeUtils.import("chrome://remote/content/targets/Target.jsm");
|
const { Target } = ChromeUtils.import(
|
||||||
const {Session} = ChromeUtils.import("chrome://remote/content/sessions/Session.jsm");
|
"chrome://remote/content/targets/Target.jsm"
|
||||||
const {RemoteAgent} = ChromeUtils.import("chrome://remote/content/RemoteAgent.jsm");
|
);
|
||||||
|
const { Session } = ChromeUtils.import(
|
||||||
|
"chrome://remote/content/sessions/Session.jsm"
|
||||||
|
);
|
||||||
|
const { RemoteAgent } = ChromeUtils.import(
|
||||||
|
"chrome://remote/content/RemoteAgent.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
const UUIDGen = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
|
const UUIDGen = Cc["@mozilla.org/uuid-generator;1"].getService(
|
||||||
|
Ci.nsIUUIDGenerator
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main process Target.
|
* The main process Target.
|
||||||
|
|
@ -26,14 +34,16 @@ class MainProcessTarget extends Target {
|
||||||
super(targets, Session);
|
super(targets, Session);
|
||||||
|
|
||||||
this.type = "browser";
|
this.type = "browser";
|
||||||
this.id = UUIDGen.generateUUID().toString().slice(1, -1);
|
this.id = UUIDGen.generateUUID()
|
||||||
|
.toString()
|
||||||
|
.slice(1, -1);
|
||||||
|
|
||||||
// Define the HTTP path to query this target
|
// Define the HTTP path to query this target
|
||||||
this.path = `/devtools/browser/${this.id}`;
|
this.path = `/devtools/browser/${this.id}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
get wsDebuggerURL() {
|
get wsDebuggerURL() {
|
||||||
const {host, port} = RemoteAgent;
|
const { host, port } = RemoteAgent;
|
||||||
return `ws://${host}:${port}${this.path}`;
|
return `ws://${host}:${port}${this.path}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,14 +6,26 @@
|
||||||
|
|
||||||
var EXPORTED_SYMBOLS = ["TabTarget"];
|
var EXPORTED_SYMBOLS = ["TabTarget"];
|
||||||
|
|
||||||
const {Target} = ChromeUtils.import("chrome://remote/content/targets/Target.jsm");
|
const { Target } = ChromeUtils.import(
|
||||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
"chrome://remote/content/targets/Target.jsm"
|
||||||
const {TabSession} = ChromeUtils.import("chrome://remote/content/sessions/TabSession.jsm");
|
);
|
||||||
const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||||
const {RemoteAgent} = ChromeUtils.import("chrome://remote/content/RemoteAgent.jsm");
|
const { TabSession } = ChromeUtils.import(
|
||||||
|
"chrome://remote/content/sessions/TabSession.jsm"
|
||||||
|
);
|
||||||
|
const { XPCOMUtils } = ChromeUtils.import(
|
||||||
|
"resource://gre/modules/XPCOMUtils.jsm"
|
||||||
|
);
|
||||||
|
const { RemoteAgent } = ChromeUtils.import(
|
||||||
|
"chrome://remote/content/RemoteAgent.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
XPCOMUtils.defineLazyServiceGetter(this, "Favicons",
|
XPCOMUtils.defineLazyServiceGetter(
|
||||||
"@mozilla.org/browser/favicon-service;1", "nsIFaviconService");
|
this,
|
||||||
|
"Favicons",
|
||||||
|
"@mozilla.org/browser/favicon-service;1",
|
||||||
|
"nsIFaviconService"
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Target for a local tab or a remoted frame.
|
* Target for a local tab or a remoted frame.
|
||||||
|
|
@ -110,7 +122,7 @@ class TabTarget extends Target {
|
||||||
}
|
}
|
||||||
|
|
||||||
get wsDebuggerURL() {
|
get wsDebuggerURL() {
|
||||||
const {host, port} = RemoteAgent;
|
const { host, port } = RemoteAgent;
|
||||||
return `ws://${host}:${port}${this.path}`;
|
return `ws://${host}:${port}${this.path}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -147,9 +159,6 @@ class TabTarget extends Target {
|
||||||
// XPCOM
|
// XPCOM
|
||||||
|
|
||||||
get QueryInterface() {
|
get QueryInterface() {
|
||||||
return ChromeUtils.generateQI([
|
return ChromeUtils.generateQI([Ci.nsIHttpRequestHandler, Ci.nsIObserver]);
|
||||||
Ci.nsIHttpRequestHandler,
|
|
||||||
Ci.nsIObserver,
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,15 @@
|
||||||
|
|
||||||
var EXPORTED_SYMBOLS = ["Target"];
|
var EXPORTED_SYMBOLS = ["Target"];
|
||||||
|
|
||||||
const {Connection} = ChromeUtils.import("chrome://remote/content/Connection.jsm");
|
const { Connection } = ChromeUtils.import(
|
||||||
const {WebSocketDebuggerTransport} = ChromeUtils.import("chrome://remote/content/server/WebSocketTransport.jsm");
|
"chrome://remote/content/Connection.jsm"
|
||||||
const {WebSocketServer} = ChromeUtils.import("chrome://remote/content/server/WebSocket.jsm");
|
);
|
||||||
|
const { WebSocketDebuggerTransport } = ChromeUtils.import(
|
||||||
|
"chrome://remote/content/server/WebSocketTransport.jsm"
|
||||||
|
);
|
||||||
|
const { WebSocketServer } = ChromeUtils.import(
|
||||||
|
"chrome://remote/content/server/WebSocket.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for all the Targets.
|
* Base class for all the Targets.
|
||||||
|
|
@ -45,8 +51,6 @@ class Target {
|
||||||
// XPCOM
|
// XPCOM
|
||||||
|
|
||||||
get QueryInterface() {
|
get QueryInterface() {
|
||||||
return ChromeUtils.generateQI([
|
return ChromeUtils.generateQI([Ci.nsIHttpRequestHandler]);
|
||||||
Ci.nsIHttpRequestHandler,
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,18 @@
|
||||||
|
|
||||||
var EXPORTED_SYMBOLS = ["Targets"];
|
var EXPORTED_SYMBOLS = ["Targets"];
|
||||||
|
|
||||||
const {EventEmitter} = ChromeUtils.import("resource://gre/modules/EventEmitter.jsm");
|
const { EventEmitter } = ChromeUtils.import(
|
||||||
const {MessagePromise} = ChromeUtils.import("chrome://remote/content/Sync.jsm");
|
"resource://gre/modules/EventEmitter.jsm"
|
||||||
const {TabTarget} = ChromeUtils.import("chrome://remote/content/targets/TabTarget.jsm");
|
);
|
||||||
const {MainProcessTarget} = ChromeUtils.import("chrome://remote/content/targets/MainProcessTarget.jsm");
|
const { MessagePromise } = ChromeUtils.import(
|
||||||
|
"chrome://remote/content/Sync.jsm"
|
||||||
|
);
|
||||||
|
const { TabTarget } = ChromeUtils.import(
|
||||||
|
"chrome://remote/content/targets/TabTarget.jsm"
|
||||||
|
);
|
||||||
|
const { MainProcessTarget } = ChromeUtils.import(
|
||||||
|
"chrome://remote/content/targets/MainProcessTarget.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
class Targets {
|
class Targets {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|
@ -91,7 +99,7 @@ class Targets {
|
||||||
return this.mainProcessTarget;
|
return this.mainProcessTarget;
|
||||||
}
|
}
|
||||||
|
|
||||||
* [Symbol.iterator]() {
|
*[Symbol.iterator]() {
|
||||||
for (const target of this._targets.values()) {
|
for (const target of this._targets.values()) {
|
||||||
yield target;
|
yield target;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,22 +28,32 @@ add_task(async function testCDP() {
|
||||||
});
|
});
|
||||||
ok(true, "CDP client has been instantiated");
|
ok(true, "CDP client has been instantiated");
|
||||||
|
|
||||||
const {Browser, Log, Page} = client;
|
const { Browser, Log, Page } = client;
|
||||||
ok("Browser" in client, "Browser domain is available");
|
ok("Browser" in client, "Browser domain is available");
|
||||||
ok("Log" in client, "Log domain is available");
|
ok("Log" in client, "Log domain is available");
|
||||||
ok("Page" in client, "Page domain is available");
|
ok("Page" in client, "Page domain is available");
|
||||||
|
|
||||||
const version = await Browser.getVersion();
|
const version = await Browser.getVersion();
|
||||||
const { isHeadless } = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
|
const { isHeadless } = Cc["@mozilla.org/gfx/info;1"].getService(
|
||||||
is(version.product, isHeadless ? "Headless Firefox" : "Firefox", "Browser.getVersion works and depends on headless mode");
|
Ci.nsIGfxInfo
|
||||||
is(version.userAgent, window.navigator.userAgent, "Browser.getVersion().userAgent is correct");
|
);
|
||||||
|
is(
|
||||||
|
version.product,
|
||||||
|
isHeadless ? "Headless Firefox" : "Firefox",
|
||||||
|
"Browser.getVersion works and depends on headless mode"
|
||||||
|
);
|
||||||
|
is(
|
||||||
|
version.userAgent,
|
||||||
|
window.navigator.userAgent,
|
||||||
|
"Browser.getVersion().userAgent is correct"
|
||||||
|
);
|
||||||
|
|
||||||
// receive console.log messages and print them
|
// receive console.log messages and print them
|
||||||
Log.enable();
|
Log.enable();
|
||||||
ok(true, "Log domain has been enabled");
|
ok(true, "Log domain has been enabled");
|
||||||
|
|
||||||
Log.entryAdded(({entry}) => {
|
Log.entryAdded(({ entry }) => {
|
||||||
const {timestamp, level, text, args} = entry;
|
const { timestamp, level, text, args } = entry;
|
||||||
const msg = text || args.join(" ");
|
const msg = text || args.join(" ");
|
||||||
console.log(`${new Date(timestamp)}\t${level.toUpperCase()}\t${msg}`);
|
console.log(`${new Date(timestamp)}\t${level.toUpperCase()}\t${msg}`);
|
||||||
});
|
});
|
||||||
|
|
@ -55,7 +65,10 @@ add_task(async function testCDP() {
|
||||||
const frameStoppedLoading = Page.frameStoppedLoading();
|
const frameStoppedLoading = Page.frameStoppedLoading();
|
||||||
const navigatedWithinDocument = Page.navigatedWithinDocument();
|
const navigatedWithinDocument = Page.navigatedWithinDocument();
|
||||||
const loadEventFired = Page.loadEventFired();
|
const loadEventFired = Page.loadEventFired();
|
||||||
await Page.navigate({url: "data:text/html;charset=utf-8,test-page<script>console.log('foo');</script><script>'</script>"});
|
await Page.navigate({
|
||||||
|
url:
|
||||||
|
"data:text/html;charset=utf-8,test-page<script>console.log('foo');</script><script>'</script>",
|
||||||
|
});
|
||||||
ok(true, "A new page has been loaded");
|
ok(true, "A new page has been loaded");
|
||||||
|
|
||||||
await loadEventFired;
|
await loadEventFired;
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,13 @@ const TEST_URI = "data:text/html;charset=utf-8,<input type=text>";
|
||||||
|
|
||||||
// Map of key codes used in this test.
|
// Map of key codes used in this test.
|
||||||
const KEYCODES = {
|
const KEYCODES = {
|
||||||
"a": 65,
|
a: 65,
|
||||||
"Backspace": 8,
|
Backspace: 8,
|
||||||
"h": 72,
|
h: 72,
|
||||||
"H": 72,
|
H: 72,
|
||||||
"AltLeft": 18,
|
AltLeft: 18,
|
||||||
"ArrowLeft": 37,
|
ArrowLeft: 37,
|
||||||
"ArrowRight": 39,
|
ArrowRight: 39,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Modifier for move forward shortcut is CTRL+RightArrow on Linux/Windows, ALT+RightArrow
|
// Modifier for move forward shortcut is CTRL+RightArrow on Linux/Windows, ALT+RightArrow
|
||||||
|
|
@ -72,7 +72,6 @@ add_task(async function() {
|
||||||
await sendArrowKey(Input, "ArrowLeft");
|
await sendArrowKey(Input, "ArrowLeft");
|
||||||
await checkInputContent("hH’", 2);
|
await checkInputContent("hH’", 2);
|
||||||
|
|
||||||
|
|
||||||
info("Write 'a'");
|
info("Write 'a'");
|
||||||
await sendTextKey(Input, "a");
|
await sendTextKey(Input, "a");
|
||||||
await checkInputContent("hHa’", 3);
|
await checkInputContent("hHa’", 3);
|
||||||
|
|
@ -144,15 +143,25 @@ function dispatchKeyEvent(Input, key, type, modifiers = 0) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function checkInputContent(expectedValue, expectedCaret) {
|
async function checkInputContent(expectedValue, expectedCaret) {
|
||||||
const { value, caret } =
|
const { value, caret } = await ContentTask.spawn(
|
||||||
await ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
|
gBrowser.selectedBrowser,
|
||||||
|
null,
|
||||||
|
function() {
|
||||||
const input = content.document.querySelector("input");
|
const input = content.document.querySelector("input");
|
||||||
return { value: input.value, caret: input.selectionStart };
|
return { value: input.value, caret: input.selectionStart };
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
is(value, expectedValue, `The input value is correct ("${value}"="${expectedValue}")`);
|
is(
|
||||||
is(caret, expectedCaret,
|
value,
|
||||||
`The input caret has the correct index ("${caret}"="${expectedCaret}")`);
|
expectedValue,
|
||||||
|
`The input value is correct ("${value}"="${expectedValue}")`
|
||||||
|
);
|
||||||
|
is(
|
||||||
|
caret,
|
||||||
|
expectedCaret,
|
||||||
|
`The input caret has the correct index ("${caret}"="${expectedCaret}")`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendBackspace(Input, expected) {
|
async function sendBackspace(Input, expected) {
|
||||||
|
|
@ -182,16 +191,25 @@ async function sendBackspace(Input, expected) {
|
||||||
* await waitForInputEvent();
|
* await waitForInputEvent();
|
||||||
*/
|
*/
|
||||||
function addInputEventListener(eventName) {
|
function addInputEventListener(eventName) {
|
||||||
return ContentTask.spawn(gBrowser.selectedBrowser, eventName, async (_eventName) => {
|
return ContentTask.spawn(
|
||||||
const input = content.document.querySelector("input");
|
gBrowser.selectedBrowser,
|
||||||
this.__onInputEvent =
|
eventName,
|
||||||
new Promise(r => input.addEventListener(_eventName, r, { once: true }));
|
async _eventName => {
|
||||||
});
|
const input = content.document.querySelector("input");
|
||||||
|
this.__onInputEvent = new Promise(r =>
|
||||||
|
input.addEventListener(_eventName, r, { once: true })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See documentation for addInputEventListener.
|
* See documentation for addInputEventListener.
|
||||||
*/
|
*/
|
||||||
function waitForInputEvent() {
|
function waitForInputEvent() {
|
||||||
return ContentTask.spawn(gBrowser.selectedBrowser, null, () => this.__onInputEvent);
|
return ContentTask.spawn(
|
||||||
|
gBrowser.selectedBrowser,
|
||||||
|
null,
|
||||||
|
() => this.__onInputEvent
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,26 +12,31 @@ add_task(async function() {
|
||||||
await RemoteAgent.listen(Services.io.newURI("http://localhost:9222"));
|
await RemoteAgent.listen(Services.io.newURI("http://localhost:9222"));
|
||||||
|
|
||||||
const { mainProcessTarget } = RemoteAgent.targets;
|
const { mainProcessTarget } = RemoteAgent.targets;
|
||||||
ok(mainProcessTarget,
|
ok(
|
||||||
"The main process target is instantiated after the call to `listen`");
|
mainProcessTarget,
|
||||||
|
"The main process target is instantiated after the call to `listen`"
|
||||||
|
);
|
||||||
|
|
||||||
const targetURL = mainProcessTarget.wsDebuggerURL;
|
const targetURL = mainProcessTarget.wsDebuggerURL;
|
||||||
|
|
||||||
const CDP = await getCDP();
|
const CDP = await getCDP();
|
||||||
|
|
||||||
const client = await CDP({"target": targetURL});
|
const client = await CDP({ target: targetURL });
|
||||||
ok(true, "CDP client has been instantiated");
|
ok(true, "CDP client has been instantiated");
|
||||||
|
|
||||||
const {Browser, Target} = client;
|
const { Browser, Target } = client;
|
||||||
ok(Browser, "The main process target exposes Browser domain");
|
ok(Browser, "The main process target exposes Browser domain");
|
||||||
ok(Target, "The main process target exposes Target domain");
|
ok(Target, "The main process target exposes Target domain");
|
||||||
|
|
||||||
const version = await Browser.getVersion();
|
const version = await Browser.getVersion();
|
||||||
is(version.product, "Firefox", "Browser.getVersion works");
|
is(version.product, "Firefox", "Browser.getVersion works");
|
||||||
|
|
||||||
const {webSocketDebuggerUrl} = await CDP.Version();
|
const { webSocketDebuggerUrl } = await CDP.Version();
|
||||||
is(webSocketDebuggerUrl, targetURL,
|
is(
|
||||||
"Version endpoint refers to the same Main process target");
|
webSocketDebuggerUrl,
|
||||||
|
targetURL,
|
||||||
|
"Version endpoint refers to the same Main process target"
|
||||||
|
);
|
||||||
|
|
||||||
await RemoteAgent.close();
|
await RemoteAgent.close();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -6,13 +6,15 @@
|
||||||
// Test the Network.requestWillBeSent event
|
// Test the Network.requestWillBeSent event
|
||||||
|
|
||||||
const TEST_URI = "data:text/html;charset=utf-8,default-test-page";
|
const TEST_URI = "data:text/html;charset=utf-8,default-test-page";
|
||||||
const PAGE_URI = "http://example.com/browser/remote/test/browser/doc_network_requestWillBeSent.html";
|
const PAGE_URI =
|
||||||
const JS_URI = "http://example.com/browser/remote/test/browser/file_network_requestWillBeSent.js";
|
"http://example.com/browser/remote/test/browser/doc_network_requestWillBeSent.html";
|
||||||
|
const JS_URI =
|
||||||
|
"http://example.com/browser/remote/test/browser/file_network_requestWillBeSent.js";
|
||||||
|
|
||||||
add_task(async function() {
|
add_task(async function() {
|
||||||
const {client} = await setupTestForUri(TEST_URI);
|
const { client } = await setupTestForUri(TEST_URI);
|
||||||
|
|
||||||
const {Page, Network} = client;
|
const { Page, Network } = client;
|
||||||
|
|
||||||
await Network.enable();
|
await Network.enable();
|
||||||
ok(true, "Network domain has been enabled");
|
ok(true, "Network domain has been enabled");
|
||||||
|
|
@ -25,7 +27,11 @@ add_task(async function() {
|
||||||
case 1:
|
case 1:
|
||||||
is(event.request.url, PAGE_URI, "Got the page request");
|
is(event.request.url, PAGE_URI, "Got the page request");
|
||||||
is(event.type, "Document", "The page request has 'Document' type");
|
is(event.type, "Document", "The page request has 'Document' type");
|
||||||
is(event.requestId, event.loaderId, "The page request has requestId = loaderId (puppeteer assumes that to detect the page start request)");
|
is(
|
||||||
|
event.requestId,
|
||||||
|
event.loaderId,
|
||||||
|
"The page request has requestId = loaderId (puppeteer assumes that to detect the page start request)"
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
is(event.request.url, JS_URI, "Got the JS request");
|
is(event.request.url, JS_URI, "Got the JS request");
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,16 @@ add_task(async function testBringToFrontUpdatesSelectedTab() {
|
||||||
is(gBrowser.selectedTab, tab, "Selected tab is the target tab");
|
is(gBrowser.selectedTab, tab, "Selected tab is the target tab");
|
||||||
|
|
||||||
info("Open another tab that should become the front tab");
|
info("Open another tab that should become the front tab");
|
||||||
const otherTab = await BrowserTestUtils.openNewForegroundTab(gBrowser, OTHER_URI);
|
const otherTab = await BrowserTestUtils.openNewForegroundTab(
|
||||||
|
gBrowser,
|
||||||
|
OTHER_URI
|
||||||
|
);
|
||||||
is(gBrowser.selectedTab, otherTab, "Selected tab is now the new tab");
|
is(gBrowser.selectedTab, otherTab, "Selected tab is now the new tab");
|
||||||
|
|
||||||
const { Page } = client;
|
const { Page } = client;
|
||||||
info("Call Page.bringToFront() and check that the test tab becomes the selected tab");
|
info(
|
||||||
|
"Call Page.bringToFront() and check that the test tab becomes the selected tab"
|
||||||
|
);
|
||||||
await Page.bringToFront();
|
await Page.bringToFront();
|
||||||
is(gBrowser.selectedTab, tab, "Selected tab is the target tab again");
|
is(gBrowser.selectedTab, tab, "Selected tab is the target tab again");
|
||||||
is(tab.ownerGlobal, getFocusedNavigator(), "The initial window is focused");
|
is(tab.ownerGlobal, getFocusedNavigator(), "The initial window is focused");
|
||||||
|
|
@ -39,9 +44,15 @@ add_task(async function testBringToFrontUpdatesFocusedWindow() {
|
||||||
is(otherWindow, getFocusedNavigator(), "The new window is focused");
|
is(otherWindow, getFocusedNavigator(), "The new window is focused");
|
||||||
|
|
||||||
const { Page } = client;
|
const { Page } = client;
|
||||||
info("Call Page.bringToFront() and check that the tab window is focused again");
|
info(
|
||||||
|
"Call Page.bringToFront() and check that the tab window is focused again"
|
||||||
|
);
|
||||||
await Page.bringToFront();
|
await Page.bringToFront();
|
||||||
is(tab.ownerGlobal, getFocusedNavigator(), "The initial window is focused again");
|
is(
|
||||||
|
tab.ownerGlobal,
|
||||||
|
getFocusedNavigator(),
|
||||||
|
"The initial window is focused again"
|
||||||
|
);
|
||||||
|
|
||||||
await client.close();
|
await client.close();
|
||||||
ok(true, "The client is closed");
|
ok(true, "The client is closed");
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,9 @@ const promises = new Set();
|
||||||
const resolutions = new Map();
|
const resolutions = new Map();
|
||||||
|
|
||||||
add_task(async function() {
|
add_task(async function() {
|
||||||
const {client} = await setupTestForUri(TEST_URI);
|
const { client } = await setupTestForUri(TEST_URI);
|
||||||
|
|
||||||
const {Page} = client;
|
const { Page } = client;
|
||||||
|
|
||||||
// turn on navigation related events, such as DOMContentLoaded et al.
|
// turn on navigation related events, such as DOMContentLoaded et al.
|
||||||
await Page.enable();
|
await Page.enable();
|
||||||
|
|
@ -45,13 +45,17 @@ add_task(async function() {
|
||||||
info("Test Page.navigate");
|
info("Test Page.navigate");
|
||||||
recordPromises();
|
recordPromises();
|
||||||
|
|
||||||
const url = "http://example.com/browser/remote/test/browser/doc_page_frameNavigated.html";
|
const url =
|
||||||
|
"http://example.com/browser/remote/test/browser/doc_page_frameNavigated.html";
|
||||||
const { frameId } = await Page.navigate({ url });
|
const { frameId } = await Page.navigate({ url });
|
||||||
ok(true, "A new page has been loaded");
|
ok(true, "A new page has been loaded");
|
||||||
|
|
||||||
ok(frameId, "Page.navigate returned a frameId");
|
ok(frameId, "Page.navigate returned a frameId");
|
||||||
is(frameId, frameTree.frame.id, "The Page.navigate's frameId is the same than " +
|
is(
|
||||||
"getFrameTree's one");
|
frameId,
|
||||||
|
frameTree.frame.id,
|
||||||
|
"The Page.navigate's frameId is the same than " + "getFrameTree's one"
|
||||||
|
);
|
||||||
|
|
||||||
await assertNavigationEvents({ url, frameId });
|
await assertNavigationEvents({ url, frameId });
|
||||||
|
|
||||||
|
|
@ -68,7 +72,11 @@ add_task(async function() {
|
||||||
|
|
||||||
const randomId2 = await getTestTabRandomId();
|
const randomId2 = await getTestTabRandomId();
|
||||||
ok(!!randomId2, "Test tab has a valid randomId");
|
ok(!!randomId2, "Test tab has a valid randomId");
|
||||||
isnot(randomId2, randomId1, "Test tab randomId has been updated after reload");
|
isnot(
|
||||||
|
randomId2,
|
||||||
|
randomId1,
|
||||||
|
"Test tab randomId has been updated after reload"
|
||||||
|
);
|
||||||
|
|
||||||
info("Test Page.navigate with the same URL still reloads the current page");
|
info("Test Page.navigate with the same URL still reloads the current page");
|
||||||
recordPromises();
|
recordPromises();
|
||||||
|
|
@ -80,7 +88,11 @@ add_task(async function() {
|
||||||
|
|
||||||
const randomId3 = await getTestTabRandomId();
|
const randomId3 = await getTestTabRandomId();
|
||||||
ok(!!randomId3, "Test tab has a valid randomId");
|
ok(!!randomId3, "Test tab has a valid randomId");
|
||||||
isnot(randomId3, randomId2, "Test tab randomId has been updated after reload");
|
isnot(
|
||||||
|
randomId3,
|
||||||
|
randomId2,
|
||||||
|
"Test tab randomId has been updated after reload"
|
||||||
|
);
|
||||||
|
|
||||||
await client.close();
|
await client.close();
|
||||||
ok(true, "The client is closed");
|
ok(true, "The client is closed");
|
||||||
|
|
@ -102,25 +114,44 @@ async function assertNavigationEvents({ url, frameId }) {
|
||||||
"navigatedWithinDocument",
|
"navigatedWithinDocument",
|
||||||
"frameStoppedLoading",
|
"frameStoppedLoading",
|
||||||
];
|
];
|
||||||
Assert.deepEqual([...resolutions.keys()],
|
Assert.deepEqual(
|
||||||
expectedResolutions,
|
[...resolutions.keys()],
|
||||||
"Received various Page navigation events in the expected order");
|
expectedResolutions,
|
||||||
|
"Received various Page navigation events in the expected order"
|
||||||
|
);
|
||||||
|
|
||||||
// Now assert the data exposed by each of these events
|
// Now assert the data exposed by each of these events
|
||||||
const frameNavigated = resolutions.get("frameNavigated");
|
const frameNavigated = resolutions.get("frameNavigated");
|
||||||
ok(!frameNavigated.frame.parentId, "frameNavigated is for the top level document and" +
|
ok(
|
||||||
" has a null parentId");
|
!frameNavigated.frame.parentId,
|
||||||
|
"frameNavigated is for the top level document and" + " has a null parentId"
|
||||||
|
);
|
||||||
is(frameNavigated.frame.id, frameId, "frameNavigated id is the right one");
|
is(frameNavigated.frame.id, frameId, "frameNavigated id is the right one");
|
||||||
is(frameNavigated.frame.name, undefined, "frameNavigated name isn't implemented yet");
|
is(
|
||||||
|
frameNavigated.frame.name,
|
||||||
|
undefined,
|
||||||
|
"frameNavigated name isn't implemented yet"
|
||||||
|
);
|
||||||
is(frameNavigated.frame.url, url, "frameNavigated url is the right one");
|
is(frameNavigated.frame.url, url, "frameNavigated url is the right one");
|
||||||
|
|
||||||
const navigatedWithinDocument = resolutions.get("navigatedWithinDocument");
|
const navigatedWithinDocument = resolutions.get("navigatedWithinDocument");
|
||||||
is(navigatedWithinDocument.frameId, frameId, "navigatedWithinDocument frameId is " +
|
is(
|
||||||
"the same one");
|
navigatedWithinDocument.frameId,
|
||||||
is(navigatedWithinDocument.url, url, "navigatedWithinDocument url is the same one");
|
frameId,
|
||||||
|
"navigatedWithinDocument frameId is " + "the same one"
|
||||||
|
);
|
||||||
|
is(
|
||||||
|
navigatedWithinDocument.url,
|
||||||
|
url,
|
||||||
|
"navigatedWithinDocument url is the same one"
|
||||||
|
);
|
||||||
|
|
||||||
const frameStoppedLoading = resolutions.get("frameStoppedLoading");
|
const frameStoppedLoading = resolutions.get("frameStoppedLoading");
|
||||||
is(frameStoppedLoading.frameId, frameId, "frameStoppedLoading frameId is the same one");
|
is(
|
||||||
|
frameStoppedLoading.frameId,
|
||||||
|
frameId,
|
||||||
|
"frameStoppedLoading frameId is the same one"
|
||||||
|
);
|
||||||
|
|
||||||
promises.clear();
|
promises.clear();
|
||||||
resolutions.clear();
|
resolutions.clear();
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,8 @@
|
||||||
const TEST_URI = "data:text/html;charset=utf-8,default-test-page";
|
const TEST_URI = "data:text/html;charset=utf-8,default-test-page";
|
||||||
|
|
||||||
add_task(async function testCDP() {
|
add_task(async function testCDP() {
|
||||||
const {client} = await setupTestForUri(TEST_URI);
|
const { client } = await setupTestForUri(TEST_URI);
|
||||||
const {Page, Runtime} = client;
|
const { Page, Runtime } = client;
|
||||||
|
|
||||||
const events = [];
|
const events = [];
|
||||||
function assertReceivedEvents(expected, message) {
|
function assertReceivedEvents(expected, message) {
|
||||||
|
|
@ -43,49 +43,84 @@ add_task(async function testCDP() {
|
||||||
ok(context.auxData.isDefault, "The execution context is the default one");
|
ok(context.auxData.isDefault, "The execution context is the default one");
|
||||||
ok(!!context.auxData.frameId, "The execution context has a frame id set");
|
ok(!!context.auxData.frameId, "The execution context has a frame id set");
|
||||||
|
|
||||||
assertReceivedEvents(["executionContextCreated"], "Received only executionContextCreated event after Runtime.enable call");
|
assertReceivedEvents(
|
||||||
|
["executionContextCreated"],
|
||||||
|
"Received only executionContextCreated event after Runtime.enable call"
|
||||||
|
);
|
||||||
|
|
||||||
const { frameTree } = await Page.getFrameTree();
|
const { frameTree } = await Page.getFrameTree();
|
||||||
ok(!!frameTree.frame, "getFrameTree exposes one frame");
|
ok(!!frameTree.frame, "getFrameTree exposes one frame");
|
||||||
is(frameTree.childFrames.length, 0, "getFrameTree reports no child frame");
|
is(frameTree.childFrames.length, 0, "getFrameTree reports no child frame");
|
||||||
ok(!!frameTree.frame.id, "getFrameTree's frame has an id");
|
ok(!!frameTree.frame.id, "getFrameTree's frame has an id");
|
||||||
is(frameTree.frame.url, TEST_URI, "getFrameTree's frame has the right url");
|
is(frameTree.frame.url, TEST_URI, "getFrameTree's frame has the right url");
|
||||||
is(frameTree.frame.id, context.auxData.frameId, "getFrameTree and executionContextCreated refers about the same frame Id");
|
is(
|
||||||
|
frameTree.frame.id,
|
||||||
|
context.auxData.frameId,
|
||||||
|
"getFrameTree and executionContextCreated refers about the same frame Id"
|
||||||
|
);
|
||||||
|
|
||||||
const onFrameNavigated = Page.frameNavigated();
|
const onFrameNavigated = Page.frameNavigated();
|
||||||
const onExecutionContextDestroyed = Runtime.executionContextDestroyed();
|
const onExecutionContextDestroyed = Runtime.executionContextDestroyed();
|
||||||
const onExecutionContextCreated2 = Runtime.executionContextCreated();
|
const onExecutionContextCreated2 = Runtime.executionContextCreated();
|
||||||
const url = "data:text/html;charset=utf-8,test-page";
|
const url = "data:text/html;charset=utf-8,test-page";
|
||||||
const { frameId } = await Page.navigate({ url });
|
const { frameId } = await Page.navigate({ url });
|
||||||
ok(true, "A new page has been loaded");
|
ok(true, "A new page has been loaded");
|
||||||
ok(frameId, "Page.navigate returned a frameId");
|
ok(frameId, "Page.navigate returned a frameId");
|
||||||
is(frameId, frameTree.frame.id, "The Page.navigate's frameId is the same than " +
|
is(
|
||||||
"getFrameTree's one");
|
frameId,
|
||||||
|
frameTree.frame.id,
|
||||||
|
"The Page.navigate's frameId is the same than " + "getFrameTree's one"
|
||||||
|
);
|
||||||
|
|
||||||
const frameNavigated = await onFrameNavigated;
|
const frameNavigated = await onFrameNavigated;
|
||||||
ok(!frameNavigated.frame.parentId, "frameNavigated is for the top level document and" +
|
ok(
|
||||||
" has a null parentId");
|
!frameNavigated.frame.parentId,
|
||||||
is(frameNavigated.frame.id, frameId, "frameNavigated id is the same than the one " +
|
"frameNavigated is for the top level document and" + " has a null parentId"
|
||||||
"returned by Page.navigate");
|
);
|
||||||
is(frameNavigated.frame.name, undefined, "frameNavigated name isn't implemented yet");
|
is(
|
||||||
is(frameNavigated.frame.url, url, "frameNavigated url is the same being given to " +
|
frameNavigated.frame.id,
|
||||||
"Page.navigate");
|
frameId,
|
||||||
|
"frameNavigated id is the same than the one " + "returned by Page.navigate"
|
||||||
|
);
|
||||||
|
is(
|
||||||
|
frameNavigated.frame.name,
|
||||||
|
undefined,
|
||||||
|
"frameNavigated name isn't implemented yet"
|
||||||
|
);
|
||||||
|
is(
|
||||||
|
frameNavigated.frame.url,
|
||||||
|
url,
|
||||||
|
"frameNavigated url is the same being given to " + "Page.navigate"
|
||||||
|
);
|
||||||
|
|
||||||
const { executionContextId } = await onExecutionContextDestroyed;
|
const { executionContextId } = await onExecutionContextDestroyed;
|
||||||
ok(executionContextId, "The destroyed event reports an id");
|
ok(executionContextId, "The destroyed event reports an id");
|
||||||
is(executionContextId, context.id, "The destroyed event is for the first reported execution context");
|
is(
|
||||||
|
executionContextId,
|
||||||
|
context.id,
|
||||||
|
"The destroyed event is for the first reported execution context"
|
||||||
|
);
|
||||||
|
|
||||||
({ context } = await onExecutionContextCreated2);
|
({ context } = await onExecutionContextCreated2);
|
||||||
ok(!!context.id, "The execution context has an id");
|
ok(!!context.id, "The execution context has an id");
|
||||||
ok(context.auxData.isDefault, "The execution context is the default one");
|
ok(context.auxData.isDefault, "The execution context is the default one");
|
||||||
is(context.auxData.frameId, frameId, "The execution context frame id is the same " +
|
is(
|
||||||
"the one returned by Page.navigate");
|
context.auxData.frameId,
|
||||||
|
frameId,
|
||||||
|
"The execution context frame id is the same " +
|
||||||
|
"the one returned by Page.navigate"
|
||||||
|
);
|
||||||
|
|
||||||
isnot(executionContextId, context.id, "The destroyed id is different from the " +
|
isnot(
|
||||||
"created one");
|
executionContextId,
|
||||||
|
context.id,
|
||||||
|
"The destroyed id is different from the " + "created one"
|
||||||
|
);
|
||||||
|
|
||||||
assertReceivedEvents(["executionContextDestroyed", "frameNavigated", "executionContextCreated"],
|
assertReceivedEvents(
|
||||||
"Received frameNavigated between the two execution context events during navigation to another URL");
|
["executionContextDestroyed", "frameNavigated", "executionContextCreated"],
|
||||||
|
"Received frameNavigated between the two execution context events during navigation to another URL"
|
||||||
|
);
|
||||||
|
|
||||||
await client.close();
|
await client.close();
|
||||||
ok(true, "The client is closed");
|
ok(true, "The client is closed");
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
const TEST_URI = "data:text/html;charset=utf-8,default-test-page";
|
const TEST_URI = "data:text/html;charset=utf-8,default-test-page";
|
||||||
|
|
||||||
add_task(async function() {
|
add_task(async function() {
|
||||||
const {client} = await setupTestForUri(TEST_URI);
|
const { client } = await setupTestForUri(TEST_URI);
|
||||||
|
|
||||||
const firstContext = await testRuntimeEnable(client);
|
const firstContext = await testRuntimeEnable(client);
|
||||||
const contextId = firstContext.id;
|
const contextId = firstContext.id;
|
||||||
|
|
@ -43,7 +43,10 @@ async function testRuntimeEnable({ Runtime }) {
|
||||||
|
|
||||||
async function testObjectReferences({ Runtime }, contextId) {
|
async function testObjectReferences({ Runtime }, contextId) {
|
||||||
// First create a JS object remotely via Runtime.evaluate
|
// First create a JS object remotely via Runtime.evaluate
|
||||||
const { result } = await Runtime.evaluate({ contextId, expression: "({ foo: 1 })" });
|
const { result } = await Runtime.evaluate({
|
||||||
|
contextId,
|
||||||
|
expression: "({ foo: 1 })",
|
||||||
|
});
|
||||||
is(result.type, "object", "The type is correct");
|
is(result.type, "object", "The type is correct");
|
||||||
is(result.subtype, null, "The subtype is null for objects");
|
is(result.subtype, null, "The subtype is null for objects");
|
||||||
ok(!!result.objectId, "Got an object id");
|
ok(!!result.objectId, "Got an object id");
|
||||||
|
|
@ -57,7 +60,11 @@ async function testObjectReferences({ Runtime }, contextId) {
|
||||||
});
|
});
|
||||||
is(result2.type, "number", "The type is correct");
|
is(result2.type, "number", "The type is correct");
|
||||||
is(result2.subtype, null, "The subtype is null for numbers");
|
is(result2.subtype, null, "The subtype is null for numbers");
|
||||||
is(result2.value, 2, "Updated the existing object and returned the incremented value");
|
is(
|
||||||
|
result2.value,
|
||||||
|
2,
|
||||||
|
"Updated the existing object and returned the incremented value"
|
||||||
|
);
|
||||||
|
|
||||||
// Finally, try to pass this JS object and get it back. Ensure that it returns
|
// Finally, try to pass this JS object and get it back. Ensure that it returns
|
||||||
// the same object id. Also increment the attribute again.
|
// the same object id. Also increment the attribute again.
|
||||||
|
|
@ -83,7 +90,11 @@ async function testObjectReferences({ Runtime }, contextId) {
|
||||||
});
|
});
|
||||||
is(result4.type, "number", "The type is correct");
|
is(result4.type, "number", "The type is correct");
|
||||||
is(result4.subtype, null, "The subtype is null for numbers");
|
is(result4.subtype, null, "The subtype is null for numbers");
|
||||||
is(result4.value, 3, "Updated the existing object and returned the incremented value");
|
is(
|
||||||
|
result4.value,
|
||||||
|
3,
|
||||||
|
"Updated the existing object and returned the incremented value"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function testExceptions({ Runtime }, executionContextId) {
|
async function testExceptions({ Runtime }, executionContextId) {
|
||||||
|
|
@ -92,28 +103,36 @@ async function testExceptions({ Runtime }, executionContextId) {
|
||||||
executionContextId,
|
executionContextId,
|
||||||
functionDeclaration: "doesNotExists()",
|
functionDeclaration: "doesNotExists()",
|
||||||
});
|
});
|
||||||
is(exceptionDetails.text, "doesNotExists is not defined", "Exception message is passed to the client");
|
is(
|
||||||
|
exceptionDetails.text,
|
||||||
|
"doesNotExists is not defined",
|
||||||
|
"Exception message is passed to the client"
|
||||||
|
);
|
||||||
|
|
||||||
// Test error when calling the function
|
// Test error when calling the function
|
||||||
({ exceptionDetails } = await Runtime.callFunctionOn({
|
({ exceptionDetails } = await Runtime.callFunctionOn({
|
||||||
executionContextId,
|
executionContextId,
|
||||||
functionDeclaration: "() => doesNotExists()",
|
functionDeclaration: "() => doesNotExists()",
|
||||||
}));
|
}));
|
||||||
is(exceptionDetails.text, "doesNotExists is not defined", "Exception message is passed to the client");
|
is(
|
||||||
|
exceptionDetails.text,
|
||||||
|
"doesNotExists is not defined",
|
||||||
|
"Exception message is passed to the client"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function testReturnByValue({ Runtime }, executionContextId) {
|
async function testReturnByValue({ Runtime }, executionContextId) {
|
||||||
const values = [
|
const values = [
|
||||||
42,
|
42,
|
||||||
"42",
|
"42",
|
||||||
42.00,
|
42.0,
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
null,
|
null,
|
||||||
{ foo: true },
|
{ foo: true },
|
||||||
{ foo: { bar: 42, str: "str", array: [1, 2, 3] } },
|
{ foo: { bar: 42, str: "str", array: [1, 2, 3] } },
|
||||||
[ 42, "42", true ],
|
[42, "42", true],
|
||||||
[ { foo: true } ],
|
[{ foo: true }],
|
||||||
];
|
];
|
||||||
for (const value of values) {
|
for (const value of values) {
|
||||||
const { result } = await Runtime.callFunctionOn({
|
const { result } = await Runtime.callFunctionOn({
|
||||||
|
|
@ -121,7 +140,11 @@ async function testReturnByValue({ Runtime }, executionContextId) {
|
||||||
functionDeclaration: "() => (" + JSON.stringify(value) + ")",
|
functionDeclaration: "() => (" + JSON.stringify(value) + ")",
|
||||||
returnByValue: true,
|
returnByValue: true,
|
||||||
});
|
});
|
||||||
Assert.deepEqual(result.value, value, "The returned value is the same than the input value");
|
Assert.deepEqual(
|
||||||
|
result.value,
|
||||||
|
value,
|
||||||
|
"The returned value is the same than the input value"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test undefined individually as JSON.stringify doesn't return a string
|
// Test undefined individually as JSON.stringify doesn't return a string
|
||||||
|
|
@ -150,7 +173,11 @@ async function testAwaitPromise({ Runtime }, executionContextId) {
|
||||||
functionDeclaration: "() => Promise.reject(42)",
|
functionDeclaration: "() => Promise.reject(42)",
|
||||||
awaitPromise: true,
|
awaitPromise: true,
|
||||||
});
|
});
|
||||||
is(exceptionDetails.exception.value, 42, "The result is the promise's rejection");
|
is(
|
||||||
|
exceptionDetails.exception.value,
|
||||||
|
42,
|
||||||
|
"The result is the promise's rejection"
|
||||||
|
);
|
||||||
|
|
||||||
// Then check delayed promise resolution
|
// Then check delayed promise resolution
|
||||||
({ result } = await Runtime.callFunctionOn({
|
({ result } = await Runtime.callFunctionOn({
|
||||||
|
|
@ -165,10 +192,15 @@ async function testAwaitPromise({ Runtime }, executionContextId) {
|
||||||
// And delayed promise rejection
|
// And delayed promise rejection
|
||||||
({ exceptionDetails } = await Runtime.callFunctionOn({
|
({ exceptionDetails } = await Runtime.callFunctionOn({
|
||||||
executionContextId,
|
executionContextId,
|
||||||
functionDeclaration: "() => new Promise((_,r) => setTimeout(() => r(42), 0))",
|
functionDeclaration:
|
||||||
|
"() => new Promise((_,r) => setTimeout(() => r(42), 0))",
|
||||||
awaitPromise: true,
|
awaitPromise: true,
|
||||||
}));
|
}));
|
||||||
is(exceptionDetails.exception.value, 42, "The result is the promise's rejection");
|
is(
|
||||||
|
exceptionDetails.exception.value,
|
||||||
|
42,
|
||||||
|
"The result is the promise's rejection"
|
||||||
|
);
|
||||||
|
|
||||||
// Finally assert promise resolution without awaitPromise
|
// Finally assert promise resolution without awaitPromise
|
||||||
({ result } = await Runtime.callFunctionOn({
|
({ result } = await Runtime.callFunctionOn({
|
||||||
|
|
@ -195,7 +227,10 @@ async function testAwaitPromise({ Runtime }, executionContextId) {
|
||||||
|
|
||||||
async function testObjectId({ Runtime }, contextId) {
|
async function testObjectId({ Runtime }, contextId) {
|
||||||
// First create an object via Runtime.evaluate
|
// First create an object via Runtime.evaluate
|
||||||
const { result } = await Runtime.evaluate({ contextId, expression: "({ foo: 42 })" });
|
const { result } = await Runtime.evaluate({
|
||||||
|
contextId,
|
||||||
|
expression: "({ foo: 42 })",
|
||||||
|
});
|
||||||
is(result.type, "object", "The type is correct");
|
is(result.type, "object", "The type is correct");
|
||||||
is(result.subtype, null, "The subtype is null for objects");
|
is(result.subtype, null, "The subtype is null for objects");
|
||||||
ok(!!result.objectId, "Got an object id");
|
ok(!!result.objectId, "Got an object id");
|
||||||
|
|
@ -208,5 +243,9 @@ async function testObjectId({ Runtime }, contextId) {
|
||||||
});
|
});
|
||||||
is(result2.type, "number", "The type is correct");
|
is(result2.type, "number", "The type is correct");
|
||||||
is(result2.subtype, null, "The subtype is null for numbers");
|
is(result2.subtype, null, "The subtype is null for numbers");
|
||||||
is(result2.value, 42, "We have a good proof that the function was ran against the target object");
|
is(
|
||||||
|
result2.value,
|
||||||
|
42,
|
||||||
|
"We have a good proof that the function was ran against the target object"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ add_task(async function() {
|
||||||
// First evaluate the expression via Runtime.evaluate in order to generate the
|
// First evaluate the expression via Runtime.evaluate in order to generate the
|
||||||
// CDP's `RemoteObject` for the given expression. A previous test already
|
// CDP's `RemoteObject` for the given expression. A previous test already
|
||||||
// asserted the returned value of Runtime.evaluate, so we can trust this.
|
// asserted the returned value of Runtime.evaluate, so we can trust this.
|
||||||
const { result } = await Runtime.evaluate({ contextId, expression });
|
const { result } = await Runtime.evaluate({ contextId, expression });
|
||||||
|
|
||||||
// We then pass this RemoteObject as an argument to Runtime.callFunctionOn.
|
// We then pass this RemoteObject as an argument to Runtime.callFunctionOn.
|
||||||
return Runtime.callFunctionOn({
|
return Runtime.callFunctionOn({
|
||||||
|
|
@ -76,7 +76,11 @@ add_task(async function() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const fun of [runtimeEvaluate, callFunctionOn, callFunctionOnArguments]) {
|
for (const fun of [
|
||||||
|
runtimeEvaluate,
|
||||||
|
callFunctionOn,
|
||||||
|
callFunctionOnArguments,
|
||||||
|
]) {
|
||||||
info("Test " + fun.name);
|
info("Test " + fun.name);
|
||||||
await testPrimitiveTypes(fun);
|
await testPrimitiveTypes(fun);
|
||||||
await testUnserializable(fun);
|
await testUnserializable(fun);
|
||||||
|
|
@ -114,8 +118,15 @@ async function testRuntimeEnable({ Runtime }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function testEvaluate({ Runtime }, contextId) {
|
async function testEvaluate({ Runtime }, contextId) {
|
||||||
const { result } = await Runtime.evaluate({ contextId, expression: "location.href" });
|
const { result } = await Runtime.evaluate({
|
||||||
is(result.value, TEST_URI, "Runtime.evaluate works and is against the test page");
|
contextId,
|
||||||
|
expression: "location.href",
|
||||||
|
});
|
||||||
|
is(
|
||||||
|
result.value,
|
||||||
|
TEST_URI,
|
||||||
|
"Runtime.evaluate works and is against the test page"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function testEvaluateInvalidContextId({ Runtime }, contextId) {
|
async function testEvaluateInvalidContextId({ Runtime }, contextId) {
|
||||||
|
|
@ -123,23 +134,40 @@ async function testEvaluateInvalidContextId({ Runtime }, contextId) {
|
||||||
await Runtime.evaluate({ contextId: -1, expression: "" });
|
await Runtime.evaluate({ contextId: -1, expression: "" });
|
||||||
ok(false, "Evaluate shouldn't pass");
|
ok(false, "Evaluate shouldn't pass");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
ok(e.message.includes("Unable to find execution context with id: -1"),
|
ok(
|
||||||
"Throws with the expected error message");
|
e.message.includes("Unable to find execution context with id: -1"),
|
||||||
|
"Throws with the expected error message"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function testCallFunctionOn({ Runtime }, executionContextId) {
|
async function testCallFunctionOn({ Runtime }, executionContextId) {
|
||||||
const { result } = await Runtime.callFunctionOn({ executionContextId, functionDeclaration: "() => location.href" });
|
const { result } = await Runtime.callFunctionOn({
|
||||||
is(result.value, TEST_URI, "Runtime.callFunctionOn works and is against the test page");
|
executionContextId,
|
||||||
|
functionDeclaration: "() => location.href",
|
||||||
|
});
|
||||||
|
is(
|
||||||
|
result.value,
|
||||||
|
TEST_URI,
|
||||||
|
"Runtime.callFunctionOn works and is against the test page"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function testCallFunctionOnInvalidContextId({ Runtime }, executionContextId) {
|
async function testCallFunctionOnInvalidContextId(
|
||||||
|
{ Runtime },
|
||||||
|
executionContextId
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
await Runtime.callFunctionOn({ executionContextId: -1, functionDeclaration: "" });
|
await Runtime.callFunctionOn({
|
||||||
|
executionContextId: -1,
|
||||||
|
functionDeclaration: "",
|
||||||
|
});
|
||||||
ok(false, "callFunctionOn shouldn't pass");
|
ok(false, "callFunctionOn shouldn't pass");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
ok(e.message.includes("Unable to find execution context with id: -1"),
|
ok(
|
||||||
"Throws with the expected error message");
|
e.message.includes("Unable to find execution context with id: -1"),
|
||||||
|
"Throws with the expected error message"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -148,7 +176,7 @@ async function testPrimitiveTypes(testFunction) {
|
||||||
for (const expression of expressions) {
|
for (const expression of expressions) {
|
||||||
const { result } = await testFunction(JSON.stringify(expression));
|
const { result } = await testFunction(JSON.stringify(expression));
|
||||||
is(result.value, expression, `Evaluating primitive '${expression}' works`);
|
is(result.value, expression, `Evaluating primitive '${expression}' works`);
|
||||||
is(result.type, typeof(expression), `${expression} type is correct`);
|
is(result.type, typeof expression, `${expression} type is correct`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// undefined doesn't work with JSON.stringify, so test it independently
|
// undefined doesn't work with JSON.stringify, so test it independently
|
||||||
|
|
@ -169,7 +197,11 @@ async function testUnserializable(testFunction) {
|
||||||
const expressions = ["NaN", "-0", "Infinity", "-Infinity"];
|
const expressions = ["NaN", "-0", "Infinity", "-Infinity"];
|
||||||
for (const expression of expressions) {
|
for (const expression of expressions) {
|
||||||
const { result } = await testFunction(expression);
|
const { result } = await testFunction(expression);
|
||||||
is(result.unserializableValue, expression, `Evaluating unserializable '${expression}' works`);
|
is(
|
||||||
|
result.unserializableValue,
|
||||||
|
expression,
|
||||||
|
`Evaluating unserializable '${expression}' works`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -190,29 +222,48 @@ async function testObjectTypes(testFunction) {
|
||||||
{ expression: "new Date()", type: "object", subtype: "date" },
|
{ expression: "new Date()", type: "object", subtype: "date" },
|
||||||
{ expression: "document", type: "object", subtype: "node" },
|
{ expression: "document", type: "object", subtype: "node" },
|
||||||
{ expression: "document.documentElement", type: "object", subtype: "node" },
|
{ expression: "document.documentElement", type: "object", subtype: "node" },
|
||||||
{ expression: "document.createElement('div')", type: "object", subtype: "node" },
|
{
|
||||||
|
expression: "document.createElement('div')",
|
||||||
|
type: "object",
|
||||||
|
subtype: "node",
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const { expression, type, subtype } of expressions) {
|
for (const { expression, type, subtype } of expressions) {
|
||||||
const { result } = await testFunction(expression);
|
const { result } = await testFunction(expression);
|
||||||
is(result.subtype, subtype, `Evaluating '${expression}' has the expected subtype`);
|
is(
|
||||||
|
result.subtype,
|
||||||
|
subtype,
|
||||||
|
`Evaluating '${expression}' has the expected subtype`
|
||||||
|
);
|
||||||
is(result.type, type, "The type is correct");
|
is(result.type, type, "The type is correct");
|
||||||
ok(!!result.objectId, "Got an object id");
|
ok(!!result.objectId, "Got an object id");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function testThrowError(testFunction) {
|
async function testThrowError(testFunction) {
|
||||||
const { exceptionDetails } = await testFunction("throw new Error('foo')", true);
|
const { exceptionDetails } = await testFunction(
|
||||||
|
"throw new Error('foo')",
|
||||||
|
true
|
||||||
|
);
|
||||||
is(exceptionDetails.text, "foo", "Exception message is passed to the client");
|
is(exceptionDetails.text, "foo", "Exception message is passed to the client");
|
||||||
}
|
}
|
||||||
|
|
||||||
async function testThrowValue(testFunction) {
|
async function testThrowValue(testFunction) {
|
||||||
const { exceptionDetails } = await testFunction("throw 'foo'", true);
|
const { exceptionDetails } = await testFunction("throw 'foo'", true);
|
||||||
is(exceptionDetails.exception.type, "string", "Exception type is correct");
|
is(exceptionDetails.exception.type, "string", "Exception type is correct");
|
||||||
is(exceptionDetails.exception.value, "foo", "Exception value is passed as a RemoteObject");
|
is(
|
||||||
|
exceptionDetails.exception.value,
|
||||||
|
"foo",
|
||||||
|
"Exception value is passed as a RemoteObject"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function testJSError(testFunction) {
|
async function testJSError(testFunction) {
|
||||||
const { exceptionDetails } = await testFunction("doesNotExists()", true);
|
const { exceptionDetails } = await testFunction("doesNotExists()", true);
|
||||||
is(exceptionDetails.text, "doesNotExists is not defined", "Exception message is passed to the client");
|
is(
|
||||||
|
exceptionDetails.text,
|
||||||
|
"doesNotExists is not defined",
|
||||||
|
"Exception message is passed to the client"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
const TEST_URI = "data:text/html;charset=utf-8,default-test-page";
|
const TEST_URI = "data:text/html;charset=utf-8,default-test-page";
|
||||||
|
|
||||||
add_task(async function() {
|
add_task(async function() {
|
||||||
const {client} = await setupTestForUri(TEST_URI);
|
const { client } = await setupTestForUri(TEST_URI);
|
||||||
|
|
||||||
const firstContext = await testRuntimeEnable(client);
|
const firstContext = await testRuntimeEnable(client);
|
||||||
await testEvaluate(client, firstContext);
|
await testEvaluate(client, firstContext);
|
||||||
|
|
@ -42,8 +42,15 @@ async function testRuntimeEnable({ Runtime }) {
|
||||||
async function testEvaluate({ Runtime }, previousContext) {
|
async function testEvaluate({ Runtime }, previousContext) {
|
||||||
const contextId = previousContext.id;
|
const contextId = previousContext.id;
|
||||||
|
|
||||||
const { result } = await Runtime.evaluate({ contextId, expression: "location.href" });
|
const { result } = await Runtime.evaluate({
|
||||||
is(result.value, TEST_URI, "Runtime.evaluate works and is against the test page");
|
contextId,
|
||||||
|
expression: "location.href",
|
||||||
|
});
|
||||||
|
is(
|
||||||
|
result.value,
|
||||||
|
TEST_URI,
|
||||||
|
"Runtime.evaluate works and is against the test page"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function testNavigate({ Runtime, Page }, previousContext) {
|
async function testNavigate({ Runtime, Page }, previousContext) {
|
||||||
|
|
@ -55,20 +62,39 @@ async function testNavigate({ Runtime, Page }, previousContext) {
|
||||||
const url = "data:text/html;charset=utf-8,test-page";
|
const url = "data:text/html;charset=utf-8,test-page";
|
||||||
const { frameId } = await Page.navigate({ url });
|
const { frameId } = await Page.navigate({ url });
|
||||||
ok(true, "A new page has been loaded");
|
ok(true, "A new page has been loaded");
|
||||||
is(frameId, previousContext.auxData.frameId, "Page.navigate returns the same frameId than executionContextCreated");
|
is(
|
||||||
|
frameId,
|
||||||
|
previousContext.auxData.frameId,
|
||||||
|
"Page.navigate returns the same frameId than executionContextCreated"
|
||||||
|
);
|
||||||
|
|
||||||
const { executionContextId } = await executionContextDestroyed;
|
const { executionContextId } = await executionContextDestroyed;
|
||||||
is(executionContextId, previousContext.id, "The destroyed event reports the previous context id");
|
is(
|
||||||
|
executionContextId,
|
||||||
|
previousContext.id,
|
||||||
|
"The destroyed event reports the previous context id"
|
||||||
|
);
|
||||||
|
|
||||||
const { context } = await executionContextCreated;
|
const { context } = await executionContextCreated;
|
||||||
ok(!!context.id, "The execution context has an id");
|
ok(!!context.id, "The execution context has an id");
|
||||||
isnot(previousContext.id, context.id, "The new execution context has a different id");
|
isnot(
|
||||||
|
previousContext.id,
|
||||||
|
context.id,
|
||||||
|
"The new execution context has a different id"
|
||||||
|
);
|
||||||
ok(context.auxData.isDefault, "The execution context is the default one");
|
ok(context.auxData.isDefault, "The execution context is the default one");
|
||||||
is(context.auxData.frameId, frameId, "The execution context frame id is the same " +
|
is(
|
||||||
"than the one returned by Page.navigate");
|
context.auxData.frameId,
|
||||||
|
frameId,
|
||||||
|
"The execution context frame id is the same " +
|
||||||
|
"than the one returned by Page.navigate"
|
||||||
|
);
|
||||||
|
|
||||||
isnot(executionContextId, context.id, "The destroyed id is different from the " +
|
isnot(
|
||||||
"created one");
|
executionContextId,
|
||||||
|
context.id,
|
||||||
|
"The destroyed id is different from the " + "created one"
|
||||||
|
);
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
@ -85,15 +111,34 @@ async function testNavigateBack({ Runtime }, firstContext, previousContext) {
|
||||||
gBrowser.selectedBrowser.goBack();
|
gBrowser.selectedBrowser.goBack();
|
||||||
|
|
||||||
const { context } = await executionContextCreated;
|
const { context } = await executionContextCreated;
|
||||||
is(context.id, firstContext.id, "The new execution context should be the same than the first one");
|
is(
|
||||||
|
context.id,
|
||||||
|
firstContext.id,
|
||||||
|
"The new execution context should be the same than the first one"
|
||||||
|
);
|
||||||
ok(context.auxData.isDefault, "The execution context is the default one");
|
ok(context.auxData.isDefault, "The execution context is the default one");
|
||||||
is(context.auxData.frameId, firstContext.auxData.frameId, "The execution context frame id is always the same");
|
is(
|
||||||
|
context.auxData.frameId,
|
||||||
|
firstContext.auxData.frameId,
|
||||||
|
"The execution context frame id is always the same"
|
||||||
|
);
|
||||||
|
|
||||||
const { executionContextId } = await executionContextDestroyed;
|
const { executionContextId } = await executionContextDestroyed;
|
||||||
is(executionContextId, previousContext.id, "The destroyed event reports the previous context id");
|
is(
|
||||||
|
executionContextId,
|
||||||
|
previousContext.id,
|
||||||
|
"The destroyed event reports the previous context id"
|
||||||
|
);
|
||||||
|
|
||||||
const { result } = await Runtime.evaluate({ contextId: context.id, expression: "location.href" });
|
const { result } = await Runtime.evaluate({
|
||||||
is(result.value, TEST_URI, "Runtime.evaluate works and is against the page we just navigated to");
|
contextId: context.id,
|
||||||
|
expression: "location.href",
|
||||||
|
});
|
||||||
|
is(
|
||||||
|
result.value,
|
||||||
|
TEST_URI,
|
||||||
|
"Runtime.evaluate works and is against the page we just navigated to"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function testNavigateViaLocation({ Runtime }, previousContext) {
|
async function testNavigateViaLocation({ Runtime }, previousContext) {
|
||||||
|
|
@ -103,19 +148,33 @@ async function testNavigateViaLocation({ Runtime }, previousContext) {
|
||||||
const executionContextCreated = Runtime.executionContextCreated();
|
const executionContextCreated = Runtime.executionContextCreated();
|
||||||
|
|
||||||
const url2 = "data:text/html;charset=utf-8,test-page-2";
|
const url2 = "data:text/html;charset=utf-8,test-page-2";
|
||||||
await Runtime.evaluate({ contextId: previousContext.id, expression: `window.location = '${url2}';` });
|
await Runtime.evaluate({
|
||||||
|
contextId: previousContext.id,
|
||||||
|
expression: `window.location = '${url2}';`,
|
||||||
|
});
|
||||||
|
|
||||||
const { executionContextId } = await executionContextDestroyed;
|
const { executionContextId } = await executionContextDestroyed;
|
||||||
is(executionContextId, previousContext.id, "The destroyed event reports the previous context id");
|
is(
|
||||||
|
executionContextId,
|
||||||
|
previousContext.id,
|
||||||
|
"The destroyed event reports the previous context id"
|
||||||
|
);
|
||||||
|
|
||||||
const { context } = await executionContextCreated;
|
const { context } = await executionContextCreated;
|
||||||
ok(!!context.id, "The execution context has an id");
|
ok(!!context.id, "The execution context has an id");
|
||||||
ok(context.auxData.isDefault, "The execution context is the default one");
|
ok(context.auxData.isDefault, "The execution context is the default one");
|
||||||
is(context.auxData.frameId, previousContext.auxData.frameId, "The execution context frame id is the same " +
|
is(
|
||||||
"the one returned by Page.navigate");
|
context.auxData.frameId,
|
||||||
|
previousContext.auxData.frameId,
|
||||||
|
"The execution context frame id is the same " +
|
||||||
|
"the one returned by Page.navigate"
|
||||||
|
);
|
||||||
|
|
||||||
isnot(executionContextId, context.id, "The destroyed id is different from the " +
|
isnot(
|
||||||
"created one");
|
executionContextId,
|
||||||
|
context.id,
|
||||||
|
"The destroyed id is different from the " + "created one"
|
||||||
|
);
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
@ -129,14 +188,24 @@ async function testReload({ Runtime, Page }, previousContext) {
|
||||||
await Page.reload();
|
await Page.reload();
|
||||||
|
|
||||||
const { executionContextId } = await executionContextDestroyed;
|
const { executionContextId } = await executionContextDestroyed;
|
||||||
is(executionContextId, previousContext.id, "The destroyed event reports the previous context id");
|
is(
|
||||||
|
executionContextId,
|
||||||
|
previousContext.id,
|
||||||
|
"The destroyed event reports the previous context id"
|
||||||
|
);
|
||||||
|
|
||||||
const { context } = await executionContextCreated;
|
const { context } = await executionContextCreated;
|
||||||
ok(!!context.id, "The execution context has an id");
|
ok(!!context.id, "The execution context has an id");
|
||||||
ok(context.auxData.isDefault, "The execution context is the default one");
|
ok(context.auxData.isDefault, "The execution context is the default one");
|
||||||
is(context.auxData.frameId, previousContext.auxData.frameId, "The execution context " +
|
is(
|
||||||
"frame id is the same one");
|
context.auxData.frameId,
|
||||||
|
previousContext.auxData.frameId,
|
||||||
|
"The execution context " + "frame id is the same one"
|
||||||
|
);
|
||||||
|
|
||||||
isnot(executionContextId, context.id, "The destroyed id is different from the " +
|
isnot(
|
||||||
"created one");
|
executionContextId,
|
||||||
|
context.id,
|
||||||
|
"The destroyed id is different from the " + "created one"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
const TEST_URI = "data:text/html;charset=utf-8,default-test-page";
|
const TEST_URI = "data:text/html;charset=utf-8,default-test-page";
|
||||||
|
|
||||||
add_task(async function() {
|
add_task(async function() {
|
||||||
const {client} = await setupTestForUri(TEST_URI);
|
const { client } = await setupTestForUri(TEST_URI);
|
||||||
|
|
||||||
const firstContext = await testRuntimeEnable(client);
|
const firstContext = await testRuntimeEnable(client);
|
||||||
const contextId = firstContext.id;
|
const contextId = firstContext.id;
|
||||||
|
|
@ -41,13 +41,23 @@ async function testRuntimeEnable({ Runtime }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function testGetOwnSimpleProperties({ Runtime }, contextId) {
|
async function testGetOwnSimpleProperties({ Runtime }, contextId) {
|
||||||
const { result } = await Runtime.evaluate({ contextId, expression: "({ bool: true, fun() {}, int: 1, object: {}, string: 'foo' })" });
|
const { result } = await Runtime.evaluate({
|
||||||
|
contextId,
|
||||||
|
expression: "({ bool: true, fun() {}, int: 1, object: {}, string: 'foo' })",
|
||||||
|
});
|
||||||
is(result.subtype, null, "JS Object have no subtype");
|
is(result.subtype, null, "JS Object have no subtype");
|
||||||
is(result.type, "object", "The type is correct");
|
is(result.type, "object", "The type is correct");
|
||||||
ok(!!result.objectId, "Got an object id");
|
ok(!!result.objectId, "Got an object id");
|
||||||
|
|
||||||
const { result: result2 } = await Runtime.getProperties({ objectId: result.objectId, ownProperties: true });
|
const { result: result2 } = await Runtime.getProperties({
|
||||||
is(result2.length, 5, "ownProperties=true allows to iterate only over direct object properties (i.e. ignore prototype)");
|
objectId: result.objectId,
|
||||||
|
ownProperties: true,
|
||||||
|
});
|
||||||
|
is(
|
||||||
|
result2.length,
|
||||||
|
5,
|
||||||
|
"ownProperties=true allows to iterate only over direct object properties (i.e. ignore prototype)"
|
||||||
|
);
|
||||||
result2.sort((a, b) => a.name > b.name);
|
result2.sort((a, b) => a.name > b.name);
|
||||||
is(result2[0].name, "bool");
|
is(result2[0].name, "bool");
|
||||||
is(result2[0].configurable, true);
|
is(result2[0].configurable, true);
|
||||||
|
|
@ -91,35 +101,55 @@ async function testGetOwnSimpleProperties({ Runtime }, contextId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function testGetPrototypeProperties({ Runtime }, contextId) {
|
async function testGetPrototypeProperties({ Runtime }, contextId) {
|
||||||
const { result } = await Runtime.evaluate({ contextId, expression: "({ foo: 42 })" });
|
const { result } = await Runtime.evaluate({
|
||||||
|
contextId,
|
||||||
|
expression: "({ foo: 42 })",
|
||||||
|
});
|
||||||
is(result.subtype, null, "JS Object have no subtype");
|
is(result.subtype, null, "JS Object have no subtype");
|
||||||
is(result.type, "object", "The type is correct");
|
is(result.type, "object", "The type is correct");
|
||||||
ok(!!result.objectId, "Got an object id");
|
ok(!!result.objectId, "Got an object id");
|
||||||
|
|
||||||
const { result: result2 } = await Runtime.getProperties({ objectId: result.objectId, ownProperties: false });
|
const { result: result2 } = await Runtime.getProperties({
|
||||||
|
objectId: result.objectId,
|
||||||
|
ownProperties: false,
|
||||||
|
});
|
||||||
ok(result2.length > 1, "We have more properties than just the object one");
|
ok(result2.length > 1, "We have more properties than just the object one");
|
||||||
const foo = result2.find(p => p.name == "foo");
|
const foo = result2.find(p => p.name == "foo");
|
||||||
ok(foo, "The object property is described");
|
ok(foo, "The object property is described");
|
||||||
ok(foo.isOwn, "and is reported as 'own' property");
|
ok(foo.isOwn, "and is reported as 'own' property");
|
||||||
|
|
||||||
const toString = result2.find(p => p.name == "toString");
|
const toString = result2.find(p => p.name == "toString");
|
||||||
ok(toString, "Function from Object's prototype are also described like toString");
|
ok(
|
||||||
|
toString,
|
||||||
|
"Function from Object's prototype are also described like toString"
|
||||||
|
);
|
||||||
ok(!toString.isOwn, "but are reported as not being an 'own' property");
|
ok(!toString.isOwn, "but are reported as not being an 'own' property");
|
||||||
}
|
}
|
||||||
|
|
||||||
async function testGetGetterSetterProperties({ Runtime }, contextId) {
|
async function testGetGetterSetterProperties({ Runtime }, contextId) {
|
||||||
const { result } = await Runtime.evaluate({ contextId, expression: "({ get prop() { return this.x; }, set prop(v) { this.x = v; } })" });
|
const { result } = await Runtime.evaluate({
|
||||||
|
contextId,
|
||||||
|
expression:
|
||||||
|
"({ get prop() { return this.x; }, set prop(v) { this.x = v; } })",
|
||||||
|
});
|
||||||
is(result.subtype, null, "JS Object have no subtype");
|
is(result.subtype, null, "JS Object have no subtype");
|
||||||
is(result.type, "object", "The type is correct");
|
is(result.type, "object", "The type is correct");
|
||||||
ok(!!result.objectId, "Got an object id");
|
ok(!!result.objectId, "Got an object id");
|
||||||
|
|
||||||
const { result: result2 } = await Runtime.getProperties({ objectId: result.objectId, ownProperties: true });
|
const { result: result2 } = await Runtime.getProperties({
|
||||||
|
objectId: result.objectId,
|
||||||
|
ownProperties: true,
|
||||||
|
});
|
||||||
is(result2.length, 1);
|
is(result2.length, 1);
|
||||||
|
|
||||||
is(result2[0].name, "prop");
|
is(result2[0].name, "prop");
|
||||||
is(result2[0].configurable, true);
|
is(result2[0].configurable, true);
|
||||||
is(result2[0].enumerable, true);
|
is(result2[0].enumerable, true);
|
||||||
is(result2[0].writable, undefined, "writable is only set for data properties");
|
is(
|
||||||
|
result2[0].writable,
|
||||||
|
undefined,
|
||||||
|
"writable is only set for data properties"
|
||||||
|
);
|
||||||
|
|
||||||
is(result2[0].get.type, "function");
|
is(result2[0].get.type, "function");
|
||||||
ok(!!result2[0].get.objectId);
|
ok(!!result2[0].get.objectId);
|
||||||
|
|
@ -131,7 +161,10 @@ async function testGetGetterSetterProperties({ Runtime }, contextId) {
|
||||||
const { result: result3 } = await Runtime.callFunctionOn({
|
const { result: result3 } = await Runtime.callFunctionOn({
|
||||||
executionContextId: contextId,
|
executionContextId: contextId,
|
||||||
functionDeclaration: "(set, get) => { set(42); return get(); }",
|
functionDeclaration: "(set, get) => { set(42); return get(); }",
|
||||||
arguments: [{ objectId: result2[0].set.objectId }, { objectId: result2[0].get.objectId }],
|
arguments: [
|
||||||
|
{ objectId: result2[0].set.objectId },
|
||||||
|
{ objectId: result2[0].get.objectId },
|
||||||
|
],
|
||||||
});
|
});
|
||||||
is(result3.type, "number", "The type is correct");
|
is(result3.type, "number", "The type is correct");
|
||||||
is(result3.subtype, null, "The subtype is null for numbers");
|
is(result3.subtype, null, "The subtype is null for numbers");
|
||||||
|
|
@ -139,12 +172,18 @@ async function testGetGetterSetterProperties({ Runtime }, contextId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function testGetCustomProperty({ Runtime }, contextId) {
|
async function testGetCustomProperty({ Runtime }, contextId) {
|
||||||
const { result } = await Runtime.evaluate({ contextId, expression: `const obj = {}; Object.defineProperty(obj, "prop", { value: 42 }); obj` });
|
const { result } = await Runtime.evaluate({
|
||||||
|
contextId,
|
||||||
|
expression: `const obj = {}; Object.defineProperty(obj, "prop", { value: 42 }); obj`,
|
||||||
|
});
|
||||||
is(result.subtype, null, "JS Object have no subtype");
|
is(result.subtype, null, "JS Object have no subtype");
|
||||||
is(result.type, "object", "The type is correct");
|
is(result.type, "object", "The type is correct");
|
||||||
ok(!!result.objectId, "Got an object id");
|
ok(!!result.objectId, "Got an object id");
|
||||||
|
|
||||||
const { result: result2 } = await Runtime.getProperties({ objectId: result.objectId, ownProperties: true });
|
const { result: result2 } = await Runtime.getProperties({
|
||||||
|
objectId: result.objectId,
|
||||||
|
ownProperties: true,
|
||||||
|
});
|
||||||
is(result2.length, 1, "We only get the one object's property");
|
is(result2.length, 1, "We only get the one object's property");
|
||||||
is(result2[0].name, "prop");
|
is(result2[0].name, "prop");
|
||||||
is(result2[0].configurable, false);
|
is(result2[0].configurable, false);
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
const TEST_URI = "data:text/html;charset=utf-8,default-test-page";
|
const TEST_URI = "data:text/html;charset=utf-8,default-test-page";
|
||||||
|
|
||||||
add_task(async function() {
|
add_task(async function() {
|
||||||
const {client} = await setupTestForUri(TEST_URI);
|
const { client } = await setupTestForUri(TEST_URI);
|
||||||
|
|
||||||
const firstContext = await testRuntimeEnable(client);
|
const firstContext = await testRuntimeEnable(client);
|
||||||
const contextId = firstContext.id;
|
const contextId = firstContext.id;
|
||||||
|
|
@ -38,7 +38,10 @@ async function testRuntimeEnable({ Runtime }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function testObjectRelease({ Runtime }, contextId) {
|
async function testObjectRelease({ Runtime }, contextId) {
|
||||||
const { result } = await Runtime.evaluate({ contextId, expression: "({ foo: 42 })" });
|
const { result } = await Runtime.evaluate({
|
||||||
|
contextId,
|
||||||
|
expression: "({ foo: 42 })",
|
||||||
|
});
|
||||||
is(result.subtype, null, "JS Object have no subtype");
|
is(result.subtype, null, "JS Object have no subtype");
|
||||||
is(result.type, "object", "The type is correct");
|
is(result.type, "object", "The type is correct");
|
||||||
ok(!!result.objectId, "Got an object id");
|
ok(!!result.objectId, "Got an object id");
|
||||||
|
|
@ -71,7 +74,10 @@ async function testObjectRelease({ Runtime }, contextId) {
|
||||||
});
|
});
|
||||||
ok(false, "callFunctionOn with a released object as argument should throw");
|
ok(false, "callFunctionOn with a released object as argument should throw");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
ok(e.message.includes("Cannot find object with ID:"), "callFunctionOn throws on released argument");
|
ok(
|
||||||
|
e.message.includes("Cannot find object with ID:"),
|
||||||
|
"callFunctionOn throws on released argument"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await Runtime.callFunctionOn({
|
await Runtime.callFunctionOn({
|
||||||
|
|
@ -80,6 +86,9 @@ async function testObjectRelease({ Runtime }, contextId) {
|
||||||
});
|
});
|
||||||
ok(false, "callFunctionOn with a released object as target should throw");
|
ok(false, "callFunctionOn with a released object as target should throw");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
ok(e.message.includes("Unable to get the context for object with id"), "callFunctionOn throws on released target");
|
ok(
|
||||||
|
e.message.includes("Unable to get the context for object with id"),
|
||||||
|
"callFunctionOn throws on released target"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@
|
||||||
add_task(async function() {
|
add_task(async function() {
|
||||||
await RemoteAgent.listen(Services.io.newURI("http://localhost:9222"));
|
await RemoteAgent.listen(Services.io.newURI("http://localhost:9222"));
|
||||||
const CDP = await getCDP();
|
const CDP = await getCDP();
|
||||||
const {webSocketDebuggerUrl} = await CDP.Version();
|
const { webSocketDebuggerUrl } = await CDP.Version();
|
||||||
const client = await CDP({"target": webSocketDebuggerUrl});
|
const client = await CDP({ target: webSocketDebuggerUrl });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await client.send("Hoobaflooba");
|
await client.send("Hoobaflooba");
|
||||||
|
|
|
||||||
|
|
@ -24,12 +24,18 @@ add_task(async function() {
|
||||||
await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
|
await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
|
||||||
|
|
||||||
let targets = await getTargets(CDP);
|
let targets = await getTargets(CDP);
|
||||||
ok(targets.some(target => target.url == TEST_URI), "Found the tab in target list");
|
ok(
|
||||||
|
targets.some(target => target.url == TEST_URI),
|
||||||
|
"Found the tab in target list"
|
||||||
|
);
|
||||||
|
|
||||||
BrowserTestUtils.removeTab(tab);
|
BrowserTestUtils.removeTab(tab);
|
||||||
|
|
||||||
targets = await getTargets(CDP);
|
targets = await getTargets(CDP);
|
||||||
ok(!targets.some(target => target.url == TEST_URI), "Tab has been removed from the target list");
|
ok(
|
||||||
|
!targets.some(target => target.url == TEST_URI),
|
||||||
|
"Tab has been removed from the target list"
|
||||||
|
);
|
||||||
|
|
||||||
await RemoteAgent.close();
|
await RemoteAgent.close();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -13,18 +13,21 @@ add_task(async function() {
|
||||||
const CDP = await getCDP();
|
const CDP = await getCDP();
|
||||||
|
|
||||||
// Connect to the server
|
// Connect to the server
|
||||||
const {webSocketDebuggerUrl} = await CDP.Version();
|
const { webSocketDebuggerUrl } = await CDP.Version();
|
||||||
const client = await CDP({"target": webSocketDebuggerUrl});
|
const client = await CDP({ target: webSocketDebuggerUrl });
|
||||||
ok(true, "CDP client has been instantiated");
|
ok(true, "CDP client has been instantiated");
|
||||||
|
|
||||||
const {Target} = client;
|
const { Target } = client;
|
||||||
ok("Target" in client, "Target domain is available");
|
ok("Target" in client, "Target domain is available");
|
||||||
|
|
||||||
const onTargetsCreated = new Promise(resolve => {
|
const onTargetsCreated = new Promise(resolve => {
|
||||||
let gotTabTarget = false, gotMainTarget = false;
|
let gotTabTarget = false,
|
||||||
|
gotMainTarget = false;
|
||||||
const unsubscribe = Target.targetCreated(event => {
|
const unsubscribe = Target.targetCreated(event => {
|
||||||
if (event.targetInfo.type == "page" &&
|
if (
|
||||||
event.targetInfo.url == gBrowser.selectedBrowser.currentURI.spec) {
|
event.targetInfo.type == "page" &&
|
||||||
|
event.targetInfo.url == gBrowser.selectedBrowser.currentURI.spec
|
||||||
|
) {
|
||||||
info("Got the current tab target");
|
info("Got the current tab target");
|
||||||
gotTabTarget = true;
|
gotTabTarget = true;
|
||||||
}
|
}
|
||||||
|
|
@ -48,21 +51,33 @@ add_task(async function() {
|
||||||
|
|
||||||
// Create a new target so that the test runs against a fresh new tab
|
// Create a new target so that the test runs against a fresh new tab
|
||||||
const targetCreated = Target.targetCreated();
|
const targetCreated = Target.targetCreated();
|
||||||
const {targetId} = await Target.createTarget();
|
const { targetId } = await Target.createTarget();
|
||||||
ok(true, `Target created: ${targetId}`);
|
ok(true, `Target created: ${targetId}`);
|
||||||
ok(!!targetId, "createTarget returns a non-empty target id");
|
ok(!!targetId, "createTarget returns a non-empty target id");
|
||||||
const {targetInfo} = await targetCreated;
|
const { targetInfo } = await targetCreated;
|
||||||
is(targetId, targetInfo.targetId, "createTarget and targetCreated refers to the same target id");
|
is(
|
||||||
|
targetId,
|
||||||
|
targetInfo.targetId,
|
||||||
|
"createTarget and targetCreated refers to the same target id"
|
||||||
|
);
|
||||||
is(targetInfo.type, "page", "The target is a page");
|
is(targetInfo.type, "page", "The target is a page");
|
||||||
|
|
||||||
const attachedToTarget = Target.attachedToTarget();
|
const attachedToTarget = Target.attachedToTarget();
|
||||||
const {sessionId} = await Target.attachToTarget({ targetId });
|
const { sessionId } = await Target.attachToTarget({ targetId });
|
||||||
ok(true, "Target attached");
|
ok(true, "Target attached");
|
||||||
|
|
||||||
const attachedEvent = await attachedToTarget;
|
const attachedEvent = await attachedToTarget;
|
||||||
ok(true, "Received Target.attachToTarget event");
|
ok(true, "Received Target.attachToTarget event");
|
||||||
is(attachedEvent.sessionId, sessionId, "attachedToTarget and attachToTarget returns the same session id");
|
is(
|
||||||
is(attachedEvent.targetInfo.type, "page", "attachedToTarget creates a tab by default");
|
attachedEvent.sessionId,
|
||||||
|
sessionId,
|
||||||
|
"attachedToTarget and attachToTarget returns the same session id"
|
||||||
|
);
|
||||||
|
is(
|
||||||
|
attachedEvent.targetInfo.type,
|
||||||
|
"page",
|
||||||
|
"attachedToTarget creates a tab by default"
|
||||||
|
);
|
||||||
|
|
||||||
const onResponse = Target.receivedMessageFromTarget();
|
const onResponse = Target.receivedMessageFromTarget();
|
||||||
const id = 1;
|
const id = 1;
|
||||||
|
|
@ -78,7 +93,10 @@ add_task(async function() {
|
||||||
is(response.sessionId, sessionId, "The response is from the same session");
|
is(response.sessionId, sessionId, "The response is from the same session");
|
||||||
const responseMessage = JSON.parse(response.message);
|
const responseMessage = JSON.parse(response.message);
|
||||||
is(responseMessage.id, id, "The response is from the same session");
|
is(responseMessage.id, id, "The response is from the same session");
|
||||||
ok(!!responseMessage.result.frameId, "received the `frameId` out of `Page.navigate` request");
|
ok(
|
||||||
|
!!responseMessage.result.frameId,
|
||||||
|
"received the `frameId` out of `Page.navigate` request"
|
||||||
|
);
|
||||||
|
|
||||||
await client.close();
|
await client.close();
|
||||||
ok(true, "The client is closed");
|
ok(true, "The client is closed");
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,11 @@ add_task(async function() {
|
||||||
const CDP = await getCDP();
|
const CDP = await getCDP();
|
||||||
|
|
||||||
// Connect to the server
|
// Connect to the server
|
||||||
const {webSocketDebuggerUrl} = await CDP.Version();
|
const { webSocketDebuggerUrl } = await CDP.Version();
|
||||||
const client = await CDP({"target": webSocketDebuggerUrl});
|
const client = await CDP({ target: webSocketDebuggerUrl });
|
||||||
ok(true, "CDP client has been instantiated");
|
ok(true, "CDP client has been instantiated");
|
||||||
|
|
||||||
const {Target} = client;
|
const { Target } = client;
|
||||||
ok("Target" in client, "Target domain is available");
|
ok("Target" in client, "Target domain is available");
|
||||||
|
|
||||||
// Wait for all Target.targetCreated event. One for each tab, plus the one
|
// Wait for all Target.targetCreated event. One for each tab, plus the one
|
||||||
|
|
@ -34,16 +34,24 @@ add_task(async function() {
|
||||||
Target.setDiscoverTargets({ discover: true });
|
Target.setDiscoverTargets({ discover: true });
|
||||||
await targetsCreated;
|
await targetsCreated;
|
||||||
|
|
||||||
const {browserContextId} = await Target.createBrowserContext();
|
const { browserContextId } = await Target.createBrowserContext();
|
||||||
|
|
||||||
const targetCreated = Target.targetCreated();
|
const targetCreated = Target.targetCreated();
|
||||||
const {targetId} = await Target.createTarget({ browserContextId });
|
const { targetId } = await Target.createTarget({ browserContextId });
|
||||||
ok(true, `Target created: ${targetId}`);
|
ok(true, `Target created: ${targetId}`);
|
||||||
ok(!!targetId, "createTarget returns a non-empty target id");
|
ok(!!targetId, "createTarget returns a non-empty target id");
|
||||||
|
|
||||||
const {targetInfo} = await targetCreated;
|
const { targetInfo } = await targetCreated;
|
||||||
is(targetId, targetInfo.targetId, "createTarget and targetCreated refers to the same target id");
|
is(
|
||||||
is(browserContextId, targetInfo.browserContextId, "the created target is reported to be of the same browser context");
|
targetId,
|
||||||
|
targetInfo.targetId,
|
||||||
|
"createTarget and targetCreated refers to the same target id"
|
||||||
|
);
|
||||||
|
is(
|
||||||
|
browserContextId,
|
||||||
|
targetInfo.browserContextId,
|
||||||
|
"the created target is reported to be of the same browser context"
|
||||||
|
);
|
||||||
is(targetInfo.type, "page", "The target is a page");
|
is(targetInfo.type, "page", "The target is a page");
|
||||||
|
|
||||||
// Releasing the browser context is going to remove the tab opened when calling createTarget
|
// Releasing the browser context is going to remove the tab opened when calling createTarget
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ add_task(async function() {
|
||||||
info("Create a new tab and wait for the target to be created");
|
info("Create a new tab and wait for the target to be created");
|
||||||
const otherTargetCreated = Target.targetCreated();
|
const otherTargetCreated = Target.targetCreated();
|
||||||
const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URI);
|
const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URI);
|
||||||
const {targetInfo} = await otherTargetCreated;
|
const { targetInfo } = await otherTargetCreated;
|
||||||
is(targetInfo.type, "page");
|
is(targetInfo.type, "page");
|
||||||
|
|
||||||
const onTabClose = BrowserTestUtils.waitForEvent(tab, "TabClose");
|
const onTabClose = BrowserTestUtils.waitForEvent(tab, "TabClose");
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,12 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const {RemoteAgent} = ChromeUtils.import("chrome://remote/content/RemoteAgent.jsm");
|
const { RemoteAgent } = ChromeUtils.import(
|
||||||
const {RemoteAgentError} = ChromeUtils.import("chrome://remote/content/Error.jsm");
|
"chrome://remote/content/RemoteAgent.jsm"
|
||||||
|
);
|
||||||
|
const { RemoteAgentError } = ChromeUtils.import(
|
||||||
|
"chrome://remote/content/Error.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Override `add_task` in order to translate chrome-remote-interface exceptions
|
* Override `add_task` in order to translate chrome-remote-interface exceptions
|
||||||
|
|
@ -27,7 +31,8 @@ this.add_task = function(test) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const CRI_URI = "http://example.com/browser/remote/test/browser/chrome-remote-interface.js";
|
const CRI_URI =
|
||||||
|
"http://example.com/browser/remote/test/browser/chrome-remote-interface.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a test document in an invisible window.
|
* Create a test document in an invisible window.
|
||||||
|
|
@ -59,7 +64,7 @@ async function getCDP() {
|
||||||
script.setAttribute("src", CRI_URI);
|
script.setAttribute("src", CRI_URI);
|
||||||
document.documentElement.appendChild(script);
|
document.documentElement.appendChild(script);
|
||||||
await new Promise(resolve => {
|
await new Promise(resolve => {
|
||||||
script.addEventListener("load", resolve, {once: true});
|
script.addEventListener("load", resolve, { once: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
const window = document.defaultView.wrappedJSObject;
|
const window = document.defaultView.wrappedJSObject;
|
||||||
|
|
@ -68,7 +73,7 @@ async function getCDP() {
|
||||||
// library in order to do the cross-domain http request, which,
|
// library in order to do the cross-domain http request, which,
|
||||||
// in a regular Web page, is impossible.
|
// in a regular Web page, is impossible.
|
||||||
window.criRequest = (options, callback) => {
|
window.criRequest = (options, callback) => {
|
||||||
const {host, port, path} = options;
|
const { host, port, path } = options;
|
||||||
const url = `http://${host}:${port}${path}`;
|
const url = `http://${host}:${port}${path}`;
|
||||||
const xhr = new XMLHttpRequest();
|
const xhr = new XMLHttpRequest();
|
||||||
xhr.open("GET", url, true);
|
xhr.open("GET", url, true);
|
||||||
|
|
|
||||||
|
|
@ -13,12 +13,14 @@
|
||||||
const puppeteer = require("puppeteer");
|
const puppeteer = require("puppeteer");
|
||||||
|
|
||||||
console.log("Calling puppeteer.connect");
|
console.log("Calling puppeteer.connect");
|
||||||
puppeteer.connect({ browserURL: "http://localhost:9000"}).then(async browser => {
|
puppeteer
|
||||||
console.log("Connect success!");
|
.connect({ browserURL: "http://localhost:9000" })
|
||||||
|
.then(async browser => {
|
||||||
|
console.log("Connect success!");
|
||||||
|
|
||||||
const page = await browser.newPage();
|
const page = await browser.newPage();
|
||||||
console.log("page", !!page);
|
console.log("page", !!page);
|
||||||
await page.goto("https://www.mozilla.org/");
|
await page.goto("https://www.mozilla.org/");
|
||||||
|
|
||||||
return browser.close();
|
return browser.close();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,12 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const {Domain} = ChromeUtils.import("chrome://remote/content/domains/Domain.jsm");
|
const { Domain } = ChromeUtils.import(
|
||||||
const {Domains} = ChromeUtils.import("chrome://remote/content/domains/Domains.jsm");
|
"chrome://remote/content/domains/Domain.jsm"
|
||||||
|
);
|
||||||
|
const { Domains } = ChromeUtils.import(
|
||||||
|
"chrome://remote/content/domains/Domains.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
class MockSession {
|
class MockSession {
|
||||||
onEvent() {}
|
onEvent() {}
|
||||||
|
|
@ -20,7 +24,7 @@ add_test(function test_Domains_constructor() {
|
||||||
|
|
||||||
add_test(function test_Domains_domainSupportsMethod() {
|
add_test(function test_Domains_domainSupportsMethod() {
|
||||||
const modules = {
|
const modules = {
|
||||||
"Foo": class extends Domain {
|
Foo: class extends Domain {
|
||||||
bar() {}
|
bar() {}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -35,7 +39,7 @@ add_test(function test_Domains_domainSupportsMethod() {
|
||||||
|
|
||||||
add_test(function test_Domains_get_invalidModule() {
|
add_test(function test_Domains_get_invalidModule() {
|
||||||
Assert.throws(() => {
|
Assert.throws(() => {
|
||||||
const domains = new Domains(noopSession, {Foo: undefined});
|
const domains = new Domains(noopSession, { Foo: undefined });
|
||||||
domains.get("Foo");
|
domains.get("Foo");
|
||||||
}, /UnknownMethodError/);
|
}, /UnknownMethodError/);
|
||||||
|
|
||||||
|
|
@ -44,7 +48,7 @@ add_test(function test_Domains_get_invalidModule() {
|
||||||
|
|
||||||
add_test(function test_Domains_get_missingConstructor() {
|
add_test(function test_Domains_get_missingConstructor() {
|
||||||
Assert.throws(() => {
|
Assert.throws(() => {
|
||||||
const domains = new Domains(noopSession, {Foo: {}});
|
const domains = new Domains(noopSession, { Foo: {} });
|
||||||
domains.get("Foo");
|
domains.get("Foo");
|
||||||
}, /TypeError/);
|
}, /TypeError/);
|
||||||
|
|
||||||
|
|
@ -53,7 +57,7 @@ add_test(function test_Domains_get_missingConstructor() {
|
||||||
|
|
||||||
add_test(function test_Domain_get_superClassNotDomain() {
|
add_test(function test_Domain_get_superClassNotDomain() {
|
||||||
Assert.throws(() => {
|
Assert.throws(() => {
|
||||||
const domains = new Domain(noopSession, {Foo: class {}});
|
const domains = new Domain(noopSession, { Foo: class {} });
|
||||||
domains.get("Foo");
|
domains.get("Foo");
|
||||||
}, /TypeError/);
|
}, /TypeError/);
|
||||||
|
|
||||||
|
|
@ -77,7 +81,7 @@ add_test(function test_Domains_get_constructs() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const session = new Session();
|
const session = new Session();
|
||||||
const domains = new Domains(session, {Foo});
|
const domains = new Domains(session, { Foo });
|
||||||
|
|
||||||
const foo = domains.get("Foo");
|
const foo = domains.get("Foo");
|
||||||
ok(constructed);
|
ok(constructed);
|
||||||
|
|
@ -92,7 +96,7 @@ add_test(function test_Domains_get_constructs() {
|
||||||
|
|
||||||
add_test(function test_Domains_size() {
|
add_test(function test_Domains_size() {
|
||||||
class Foo extends Domain {}
|
class Foo extends Domain {}
|
||||||
const domains = new Domains(noopSession, {Foo});
|
const domains = new Domains(noopSession, { Foo });
|
||||||
|
|
||||||
equal(domains.size, 0);
|
equal(domains.size, 0);
|
||||||
domains.get("Foo");
|
domains.get("Foo");
|
||||||
|
|
@ -109,7 +113,7 @@ add_test(function test_Domains_clear() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const domains = new Domains(noopSession, {Foo});
|
const domains = new Domains(noopSession, { Foo });
|
||||||
|
|
||||||
equal(domains.size, 0);
|
equal(domains.size, 0);
|
||||||
domains.get("Foo");
|
domains.get("Foo");
|
||||||
|
|
@ -124,12 +128,20 @@ add_test(function test_Domains_clear() {
|
||||||
|
|
||||||
add_test(function test_Domains_splitMethod() {
|
add_test(function test_Domains_splitMethod() {
|
||||||
for (const t of [42, null, true, {}, [], undefined]) {
|
for (const t of [42, null, true, {}, [], undefined]) {
|
||||||
Assert.throws(() => Domains.splitMethod(t), /TypeError/, `${typeof t} throws`);
|
Assert.throws(
|
||||||
|
() => Domains.splitMethod(t),
|
||||||
|
/TypeError/,
|
||||||
|
`${typeof t} throws`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
for (const s of ["", ".", "foo.", ".bar", "foo.bar.baz"]) {
|
for (const s of ["", ".", "foo.", ".bar", "foo.bar.baz"]) {
|
||||||
Assert.throws(() => Domains.splitMethod(s), /Invalid method format: ".*"/, `"${s}" throws`);
|
Assert.throws(
|
||||||
|
() => Domains.splitMethod(s),
|
||||||
|
/Invalid method format: ".*"/,
|
||||||
|
`"${s}" throws`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
deepEqual(Domains.splitMethod("foo.bar"), {domain: "foo", command: "bar"});
|
deepEqual(Domains.splitMethod("foo.bar"), { domain: "foo", command: "bar" });
|
||||||
|
|
||||||
run_next_test();
|
run_next_test();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -37,16 +37,19 @@ add_test(function test_RemoteAgentError_notify() {
|
||||||
add_test(function test_RemoteAgentError_toString() {
|
add_test(function test_RemoteAgentError_toString() {
|
||||||
const e = new RemoteAgentError("message");
|
const e = new RemoteAgentError("message");
|
||||||
equal(e.toString(), RemoteAgentError.format(e));
|
equal(e.toString(), RemoteAgentError.format(e));
|
||||||
equal(e.toString({stack: true}), RemoteAgentError.format(e, {stack: true}));
|
equal(
|
||||||
|
e.toString({ stack: true }),
|
||||||
|
RemoteAgentError.format(e, { stack: true })
|
||||||
|
);
|
||||||
|
|
||||||
run_next_test();
|
run_next_test();
|
||||||
});
|
});
|
||||||
|
|
||||||
add_test(function test_RemoteAgentError_format() {
|
add_test(function test_RemoteAgentError_format() {
|
||||||
const {format} = RemoteAgentError;
|
const { format } = RemoteAgentError;
|
||||||
|
|
||||||
equal(format({name: "HippoError"}), "HippoError");
|
equal(format({ name: "HippoError" }), "HippoError");
|
||||||
equal(format({name: "HorseError", message: "neigh"}), "HorseError: neigh");
|
equal(format({ name: "HorseError", message: "neigh" }), "HorseError: neigh");
|
||||||
|
|
||||||
const dog = {
|
const dog = {
|
||||||
name: "DogError",
|
name: "DogError",
|
||||||
|
|
@ -54,11 +57,13 @@ add_test(function test_RemoteAgentError_format() {
|
||||||
stack: " one\ntwo\nthree ",
|
stack: " one\ntwo\nthree ",
|
||||||
};
|
};
|
||||||
equal(format(dog), "DogError: woof");
|
equal(format(dog), "DogError: woof");
|
||||||
equal(format(dog, {stack: true}),
|
equal(
|
||||||
`DogError: woof:
|
format(dog, { stack: true }),
|
||||||
|
`DogError: woof:
|
||||||
one
|
one
|
||||||
two
|
two
|
||||||
three`);
|
three`
|
||||||
|
);
|
||||||
|
|
||||||
const cat = {
|
const cat = {
|
||||||
name: "CatError",
|
name: "CatError",
|
||||||
|
|
@ -67,23 +72,27 @@ add_test(function test_RemoteAgentError_format() {
|
||||||
cause: dog,
|
cause: dog,
|
||||||
};
|
};
|
||||||
equal(format(cat), "CatError: meow");
|
equal(format(cat), "CatError: meow");
|
||||||
equal(format(cat, {stack: true}),
|
equal(
|
||||||
`CatError: meow:
|
format(cat, { stack: true }),
|
||||||
|
`CatError: meow:
|
||||||
four
|
four
|
||||||
five
|
five
|
||||||
six
|
six
|
||||||
caused by: DogError: woof:
|
caused by: DogError: woof:
|
||||||
one
|
one
|
||||||
two
|
two
|
||||||
three`);
|
three`
|
||||||
|
);
|
||||||
|
|
||||||
run_next_test();
|
run_next_test();
|
||||||
});
|
});
|
||||||
|
|
||||||
add_test(function test_RemoteAgentError_fromJSON() {
|
add_test(function test_RemoteAgentError_fromJSON() {
|
||||||
const cdpErr = {message: `TypeError: foo:
|
const cdpErr = {
|
||||||
|
message: `TypeError: foo:
|
||||||
bar
|
bar
|
||||||
baz`};
|
baz`,
|
||||||
|
};
|
||||||
const err = RemoteAgentError.fromJSON(cdpErr);
|
const err = RemoteAgentError.fromJSON(cdpErr);
|
||||||
|
|
||||||
equal(err.message, "TypeError: foo");
|
equal(err.message, "TypeError: foo");
|
||||||
|
|
@ -101,6 +110,10 @@ add_test(function test_UnsupportedError() {
|
||||||
add_test(function test_UnknownMethodError() {
|
add_test(function test_UnknownMethodError() {
|
||||||
ok(new UnknownMethodError() instanceof RemoteAgentError);
|
ok(new UnknownMethodError() instanceof RemoteAgentError);
|
||||||
ok(new UnknownMethodError("domain").message.endsWith("domain"));
|
ok(new UnknownMethodError("domain").message.endsWith("domain"));
|
||||||
ok(new UnknownMethodError("domain", "command").message.endsWith("domain.command"));
|
ok(
|
||||||
|
new UnknownMethodError("domain", "command").message.endsWith(
|
||||||
|
"domain.command"
|
||||||
|
)
|
||||||
|
);
|
||||||
run_next_test();
|
run_next_test();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,9 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const {Session} = ChromeUtils.import("chrome://remote/content/sessions/Session.jsm");
|
const { Session } = ChromeUtils.import(
|
||||||
|
"chrome://remote/content/sessions/Session.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
const connection = {
|
const connection = {
|
||||||
registerSession: () => {},
|
registerSession: () => {},
|
||||||
|
|
@ -13,11 +15,10 @@ const connection = {
|
||||||
};
|
};
|
||||||
|
|
||||||
class MockTarget {
|
class MockTarget {
|
||||||
constructor() {
|
constructor() {}
|
||||||
}
|
|
||||||
|
|
||||||
get browsingContext() {
|
get browsingContext() {
|
||||||
return {id: 42};
|
return { id: 42 };
|
||||||
}
|
}
|
||||||
|
|
||||||
get mm() {
|
get mm() {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue