Skip to content

Instantly share code, notes, and snippets.

@rpl
Last active August 26, 2016 13:59
Show Gist options
  • Save rpl/3bd095bedf8bd9b49649406d6f2e6c22 to your computer and use it in GitHub Desktop.
Save rpl/3bd095bedf8bd9b49649406d6f2e6c22 to your computer and use it in GitHub Desktop.
webext devtools api internals
/* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const protocol = require("devtools/shared/protocol");
const {Ci} = require("chrome");
const Services = require("Services");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const {
webExtensionInspectedWindowSpec,
} = require("devtools/shared/specs/webextension-devtools-api");
const {
WebExtensionInspectedWindowFront,
} = require("devtools/shared/fronts/webextension-devtools-api");
loader.lazyRequireGetter(this, "events", "sdk/event/core");
var WebExtensionInspectedWindowActor = protocol.ActorClass(
webExtensionInspectedWindowSpec,
{
/**
* Created the WebExtension InspectedWindow actor
*/
initialize(conn, tabActor) {
protocol.Actor.prototype.initialize.call(this, conn);
this.tabActor = tabActor;
this.dbg = this.tabActor.makeDebugger();
},
destroy(conn) {
protocol.Actor.prototype.destroy.call(this, conn);
this.finalize();
},
get window() {
return this.tabActor.window;
},
get docShell() {
return this.tabActor.docShell;
},
get webNavigation() {
return this.tabActor.webNavigation;
},
finalize() {
if (this.customizedUserAgent) {
this.docShell.customUserAgent = null;
delete this.customizedUserAgent;
}
},
reload(ignoreCache, {userAgent, injectedScript}) {
const dispatchInfallible = (desc, cb) =>
Services.tm.currentThread.dispatch(DevToolsUtils.makeInfallible(cb, desc), 0);
const delayedReload = () => {
// This won't work while the browser is shutting down and we don't really
// care.
if (Services.startup.shuttingDown) {
return;
}
if (injectedScript) {
let injectScriptListener = event => dispatchInfallible(
"WebExtensionInspectedWindowActor.prototype.reload's delayed inject",
() => this.evalInWindow(event.target.defaultView, injectedScript, {})
);
// Listening for DOMContentLoaded events on the top level frame and its sub frames.
this.docShell.tabChild.messageManager
.addEventListener("DOMContentLoaded", injectScriptListener);
let loadedListener = () => {
// Unregister all the listeners once the page is fully loaded.
this.window.removeEventListener("load", loadedListener);
this.docShell.tabChild.messageManager
.removeEventListener("DOMContentLoaded", injectScriptListener);
};
events.once(this.tabActor, "window-ready", () => {
// Wait for the fully loaded page.
this.window.addEventListener("load", loadedListener);
});
}
if (userAgent) {
this.customizedUserAgent = userAgent;
this.docShell.customUserAgent = userAgent;
} else {
this.docShell.customUserAgent = null;
delete this.customizedUserAgent;
}
this.webNavigation.reload(ignoreCache ?
Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE :
Ci.nsIWebNavigation.LOAD_FLAGS_NONE);
};
dispatchInfallible(
"WebExtensionInspectedWindowActor.prototype.reload's delayed body",
delayedReload);
return {};
},
eval(expression, options) {
return this.evalInWindow(this.window, expression, options);
},
evalInWindow(targetWindow, expression, options) {
const dbg = this.dbg;
const dbgWindow = dbg.makeGlobalObjectReference(targetWindow);
const result = dbgWindow.executeInGlobalWithBindings(expression, {}, {});
let expressionResult, expressionException;
if (result) {
if ("return" in result) {
expressionResult = result.return;
} else if ("yield" in result) {
expressionResult = result.yield;
} else if ("throw" in result) {
const throwErr = result.throw;
// XXXworkers: Calling unsafeDereference() returns an object with no
// toString method in workers. See Bug 1215120.
const unsafeDereference = throwErr && (typeof throwErr === "object") &&
throwErr.unsafeDereference();
const message = unsafeDereference && unsafeDereference.toString ?
unsafeDereference.toString() : String(throwErr);
const name = unsafeDereference && unsafeDereference.name ?
unsafeDereference.name : null;
const stack = unsafeDereference && unsafeDereference.stack ?
unsafeDereference.stack : null;
const fileName = unsafeDereference && unsafeDereference.fileName ?
unsafeDereference.fileName : null;
const lineNumber = unsafeDereference && unsafeDereference.lineNumber ?
unsafeDereference.lineNumber : "";
const columnNumber = unsafeDereference && unsafeDereference.columnNumber ?
unsafeDereference.columnNumber : "";
expressionException = {
message,
name,
stack,
fileName,
lineNumber,
columnNumber,
};
}
} else {
// TODO: send error on no result
}
let devtoolsError;
if (expressionResult) {
try {
expressionResult = expressionResult && (typeof expressionResult === "object") &&
expressionResult.unsafeDereference();
expressionResult = JSON.parse(JSON.stringify(expressionResult));
} catch (err) {
// The evaluation result could not be sent over the RDP Protocol.
expressionResult = "";
devtoolsError = {
code: "E_PROTOCOLERROR",
description: "Inspector protocol error: %s",
details: [
String(err),
],
};
}
}
return {expressionResult, devtoolsError, expressionException};
}
}
);
exports.WebExtensionInspectedWindowActor = WebExtensionInspectedWindowActor;
reload(options) {
const toolbox = getDeveloperToolboxForContext(context);
if (!toolbox || !toolbox.target.isLocalTab) {
throw new Error("unable to execute eval for the current inspectedWindow");
}
const {target} = toolbox;
options = options || {};
const {ignoreCache, userAgent, injectedScript} = options;
let inspectedWindowFront = new WebExtensionInspectedWindowFront(
target.client, target.form);
inspectedWindowFront.reload(ignoreCache, {
userAgent,
injectedScript,
});
},
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const {
Arg,
RetVal,
generateActorSpec,
} = require("devtools/shared/protocol");
const webExtensionInspectedWindowSpec = generateActorSpec({
typeName: "webExtensionInspectedWindow",
methods: {
reload: {
request: {
ignoreCache: Arg(0, "boolean"),
options: Arg(1, "json")
}
},
eval: {
request: {
expression: Arg(0, "string"),
options: Arg(1, "json")
},
response: {
evalResult: RetVal("json")
}
}
}
});
exports.webExtensionInspectedWindowSpec = webExtensionInspectedWindowSpec;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment