Last active
August 26, 2016 13:59
-
-
Save rpl/3bd095bedf8bd9b49649406d6f2e6c22 to your computer and use it in GitHub Desktop.
webext devtools api internals
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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