mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-10 03:45:46 +00:00
Bug 1168853 - Implement (Worker)SourceActor.source;r=jlong
This commit is contained in:
parent
10a086a22d
commit
2ecb55970f
@ -43,6 +43,17 @@ function test() {
|
||||
let [, breakpointClient2] = yield setBreakpoint(sourceClient2, location);
|
||||
yield resume(threadClient2);
|
||||
|
||||
let packet = yield source(sourceClient1);
|
||||
let text = (yield new Promise(function (resolve) {
|
||||
let request = new XMLHttpRequest();
|
||||
request.open("GET", EXAMPLE_URL + WORKER_URL, true);
|
||||
request.send();
|
||||
request.onload = function () {
|
||||
resolve(request.responseText);
|
||||
};
|
||||
}));
|
||||
is(packet.source, text);
|
||||
|
||||
postMessageToWorkerInTab(tab, WORKER_URL, "ping");
|
||||
yield Promise.all([
|
||||
waitForPause(threadClient1).then((packet) => {
|
||||
|
@ -934,10 +934,13 @@ function attachAddonActorForUrl(aClient, aUrl) {
|
||||
|
||||
function rdpInvoke(aClient, aMethod, ...args) {
|
||||
return promiseInvoke(aClient, aMethod, ...args)
|
||||
.then(({error, message }) => {
|
||||
.then((packet) => {
|
||||
let { error, message } = packet;
|
||||
if (error) {
|
||||
throw new Error(error + ": " + message);
|
||||
}
|
||||
|
||||
return packet;
|
||||
});
|
||||
}
|
||||
|
||||
@ -1185,15 +1188,6 @@ function findSource(sources, url) {
|
||||
return null;
|
||||
}
|
||||
|
||||
function setBreakpoint(sourceClient, location) {
|
||||
info("Setting breakpoint.\n");
|
||||
return new Promise(function (resolve) {
|
||||
sourceClient.setBreakpoint(location, function (response, breakpointClient) {
|
||||
resolve([response, breakpointClient]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function waitForEvent(client, type, predicate) {
|
||||
return new Promise(function (resolve) {
|
||||
function listener(type, packet) {
|
||||
@ -1218,3 +1212,13 @@ function waitForPause(threadClient) {
|
||||
info("Waiting for pause.\n");
|
||||
return waitForEvent(threadClient, "paused");
|
||||
}
|
||||
|
||||
function setBreakpoint(sourceClient, location) {
|
||||
info("Setting breakpoint.\n");
|
||||
return rdpInvoke(sourceClient, sourceClient.setBreakpoint, location);
|
||||
}
|
||||
|
||||
function source(sourceClient) {
|
||||
info("Getting source.\n");
|
||||
return rdpInvoke(sourceClient, sourceClient.source);
|
||||
}
|
||||
|
@ -445,66 +445,69 @@ exports.defineLazyGetter(this, "NetUtil", () => {
|
||||
* without relying on caching when we can (not for eval, etc.):
|
||||
* http://www.softwareishard.com/blog/firebug/nsitraceablechannel-intercept-http-traffic/
|
||||
*/
|
||||
exports.fetch = function fetch(aURL, aOptions={ loadFromCache: true,
|
||||
policy: Ci.nsIContentPolicy.TYPE_OTHER,
|
||||
window: null,
|
||||
charset: null }) {
|
||||
let deferred = promise.defer();
|
||||
let scheme;
|
||||
let url = aURL.split(" -> ").pop();
|
||||
let charset;
|
||||
let contentType;
|
||||
|
||||
try {
|
||||
scheme = Services.io.extractScheme(url);
|
||||
} catch (e) {
|
||||
// In the xpcshell tests, the script url is the absolute path of the test
|
||||
// file, which will make a malformed URI error be thrown. Add the file
|
||||
// scheme prefix ourselves.
|
||||
url = "file://" + url;
|
||||
scheme = Services.io.extractScheme(url);
|
||||
}
|
||||
// Fetch is defined differently depending on whether we are on the main thread
|
||||
// or a worker thread.
|
||||
if (!this.isWorker) {
|
||||
exports.fetch = function (aURL, aOptions={ loadFromCache: true,
|
||||
policy: Ci.nsIContentPolicy.TYPE_OTHER,
|
||||
window: null,
|
||||
charset: null }) {
|
||||
let deferred = promise.defer();
|
||||
let scheme;
|
||||
let url = aURL.split(" -> ").pop();
|
||||
let charset;
|
||||
let contentType;
|
||||
|
||||
switch (scheme) {
|
||||
case "file":
|
||||
case "chrome":
|
||||
case "resource":
|
||||
try {
|
||||
NetUtil.asyncFetch({
|
||||
uri: url,
|
||||
loadUsingSystemPrincipal: true
|
||||
}, function onFetch(aStream, aStatus, aRequest) {
|
||||
if (!components.isSuccessCode(aStatus)) {
|
||||
deferred.reject(new Error("Request failed with status code = "
|
||||
+ aStatus
|
||||
+ " after NetUtil.asyncFetch for url = "
|
||||
+ url));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
scheme = Services.io.extractScheme(url);
|
||||
} catch (e) {
|
||||
// In the xpcshell tests, the script url is the absolute path of the test
|
||||
// file, which will make a malformed URI error be thrown. Add the file
|
||||
// scheme prefix ourselves.
|
||||
url = "file://" + url;
|
||||
scheme = Services.io.extractScheme(url);
|
||||
}
|
||||
|
||||
let source = NetUtil.readInputStreamToString(aStream, aStream.available());
|
||||
contentType = aRequest.contentType;
|
||||
deferred.resolve(source);
|
||||
aStream.close();
|
||||
});
|
||||
} catch (ex) {
|
||||
deferred.reject(ex);
|
||||
}
|
||||
break;
|
||||
switch (scheme) {
|
||||
case "file":
|
||||
case "chrome":
|
||||
case "resource":
|
||||
try {
|
||||
NetUtil.asyncFetch({
|
||||
uri: url,
|
||||
loadUsingSystemPrincipal: true
|
||||
}, function onFetch(aStream, aStatus, aRequest) {
|
||||
if (!components.isSuccessCode(aStatus)) {
|
||||
deferred.reject(new Error("Request failed with status code = "
|
||||
+ aStatus
|
||||
+ " after NetUtil.asyncFetch for url = "
|
||||
+ url));
|
||||
return;
|
||||
}
|
||||
|
||||
default:
|
||||
let channel;
|
||||
try {
|
||||
channel = Services.io.newChannel2(url,
|
||||
null,
|
||||
null,
|
||||
null, // aLoadingNode
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
null, // aTriggeringPrincipal
|
||||
Ci.nsILoadInfo.SEC_NORMAL,
|
||||
aOptions.policy);
|
||||
} catch (e) {
|
||||
if (e.name == "NS_ERROR_UNKNOWN_PROTOCOL") {
|
||||
let source = NetUtil.readInputStreamToString(aStream, aStream.available());
|
||||
contentType = aRequest.contentType;
|
||||
deferred.resolve(source);
|
||||
aStream.close();
|
||||
});
|
||||
} catch (ex) {
|
||||
deferred.reject(ex);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
let channel;
|
||||
try {
|
||||
channel = Services.io.newChannel2(url,
|
||||
null,
|
||||
null,
|
||||
null, // aLoadingNode
|
||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
||||
null, // aTriggeringPrincipal
|
||||
Ci.nsILoadInfo.SEC_NORMAL,
|
||||
aOptions.policy);
|
||||
} catch (e if e.name == "NS_ERROR_UNKNOWN_PROTOCOL") {
|
||||
// On Windows xpcshell tests, c:/foo/bar can pass as a valid URL, but
|
||||
// newChannel won't be able to handle it.
|
||||
url = "file:///" + url;
|
||||
@ -516,65 +519,70 @@ exports.fetch = function fetch(aURL, aOptions={ loadFromCache: true,
|
||||
null, // aTriggeringPrincipal
|
||||
Ci.nsILoadInfo.SEC_NORMAL,
|
||||
aOptions.policy);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
let chunks = [];
|
||||
let streamListener = {
|
||||
onStartRequest: function(aRequest, aContext, aStatusCode) {
|
||||
if (!components.isSuccessCode(aStatusCode)) {
|
||||
deferred.reject(new Error("Request failed with status code = "
|
||||
+ aStatusCode
|
||||
+ " in onStartRequest handler for url = "
|
||||
+ url));
|
||||
}
|
||||
},
|
||||
onDataAvailable: function(aRequest, aContext, aStream, aOffset, aCount) {
|
||||
chunks.push(NetUtil.readInputStreamToString(aStream, aCount));
|
||||
},
|
||||
onStopRequest: function(aRequest, aContext, aStatusCode) {
|
||||
if (!components.isSuccessCode(aStatusCode)) {
|
||||
deferred.reject(new Error("Request failed with status code = "
|
||||
+ aStatusCode
|
||||
+ " in onStopRequest handler for url = "
|
||||
+ url));
|
||||
return;
|
||||
}
|
||||
let chunks = [];
|
||||
let streamListener = {
|
||||
onStartRequest: function(aRequest, aContext, aStatusCode) {
|
||||
if (!components.isSuccessCode(aStatusCode)) {
|
||||
deferred.reject(new Error("Request failed with status code = "
|
||||
+ aStatusCode
|
||||
+ " in onStartRequest handler for url = "
|
||||
+ url));
|
||||
}
|
||||
},
|
||||
onDataAvailable: function(aRequest, aContext, aStream, aOffset, aCount) {
|
||||
chunks.push(NetUtil.readInputStreamToString(aStream, aCount));
|
||||
},
|
||||
onStopRequest: function(aRequest, aContext, aStatusCode) {
|
||||
if (!components.isSuccessCode(aStatusCode)) {
|
||||
deferred.reject(new Error("Request failed with status code = "
|
||||
+ aStatusCode
|
||||
+ " in onStopRequest handler for url = "
|
||||
+ url));
|
||||
return;
|
||||
}
|
||||
|
||||
charset = channel.contentCharset || aOptions.charset;
|
||||
contentType = channel.contentType;
|
||||
deferred.resolve(chunks.join(""));
|
||||
charset = channel.contentCharset || aOptions.charset;
|
||||
contentType = channel.contentType;
|
||||
deferred.resolve(chunks.join(""));
|
||||
}
|
||||
};
|
||||
|
||||
if (aOptions.window) {
|
||||
// Respect private browsing.
|
||||
channel.loadGroup = aOptions.window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocumentLoader)
|
||||
.loadGroup;
|
||||
}
|
||||
channel.loadFlags = aOptions.loadFromCache
|
||||
? channel.LOAD_FROM_CACHE
|
||||
: channel.LOAD_BYPASS_CACHE;
|
||||
try {
|
||||
channel.asyncOpen(streamListener, null);
|
||||
} catch(e) {
|
||||
deferred.reject(new Error("Request failed for '"
|
||||
+ url
|
||||
+ "': "
|
||||
+ e.message));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return deferred.promise.then(source => {
|
||||
return {
|
||||
content: convertToUnicode(source, charset),
|
||||
contentType: contentType
|
||||
};
|
||||
|
||||
if (aOptions.window) {
|
||||
// Respect private browsing.
|
||||
channel.loadGroup = aOptions.window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocumentLoader)
|
||||
.loadGroup;
|
||||
}
|
||||
channel.loadFlags = aOptions.loadFromCache
|
||||
? channel.LOAD_FROM_CACHE
|
||||
: channel.LOAD_BYPASS_CACHE;
|
||||
try {
|
||||
channel.asyncOpen(streamListener, null);
|
||||
} catch(e) {
|
||||
deferred.reject(new Error("Request failed for '"
|
||||
+ url
|
||||
+ "': "
|
||||
+ e.message));
|
||||
}
|
||||
break;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Services is not available in worker threads, nor is there any other way
|
||||
// to fetch a URL. We need to enlist the help from the main thread here, by
|
||||
// issuing an rpc request, to fetch the URL on our behalf.
|
||||
exports.fetch = function (url, options) {
|
||||
return rpc("fetch", url, options);
|
||||
}
|
||||
|
||||
return deferred.promise.then(source => {
|
||||
return {
|
||||
content: convertToUnicode(source, charset),
|
||||
contentType: contentType
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -20,6 +20,7 @@ let DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
|
||||
let { dumpn, dumpv, dbg_assert } = DevToolsUtils;
|
||||
let EventEmitter = require("devtools/toolkit/event-emitter");
|
||||
let Debugger = require("Debugger");
|
||||
let Promise = require("promise");
|
||||
|
||||
DevToolsUtils.defineLazyGetter(this, "DebuggerSocket", () => {
|
||||
let { DebuggerSocket } = require("devtools/toolkit/security/socket");
|
||||
@ -760,8 +761,53 @@ var DebuggerServer = {
|
||||
|
||||
connectToWorker: function (aConnection, aDbg, aId, aOptions) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Step 1: Initialize the worker debugger.
|
||||
aDbg.initialize("resource://gre/modules/devtools/server/worker.js");
|
||||
// Step 1: Ensure the worker debugger is initialized.
|
||||
if (!aDbg.isInitialized) {
|
||||
aDbg.initialize("resource://gre/modules/devtools/server/worker.js");
|
||||
|
||||
// Create a listener for rpc requests from the worker debugger. Only do
|
||||
// this once, when the worker debugger is first initialized, rather than
|
||||
// for each connection.
|
||||
let listener = {
|
||||
onClose: () => {
|
||||
aDbg.removeListener(listener);
|
||||
},
|
||||
|
||||
onMessage: (message) => {
|
||||
let packet = JSON.parse(message);
|
||||
if (packet.type !== "rpc") {
|
||||
return;
|
||||
}
|
||||
|
||||
Promise.resolve().then(() => {
|
||||
let method = {
|
||||
"fetch": DevToolsUtils.fetch,
|
||||
}[packet.method];
|
||||
if (!method) {
|
||||
throw Error("Unknown method: " + packet.method);
|
||||
}
|
||||
|
||||
return method.apply(undefined, packet.params);
|
||||
}).then((value) => {
|
||||
aDbg.postMessage(JSON.stringify({
|
||||
type: "rpc",
|
||||
result: value,
|
||||
error: null,
|
||||
id: packet.id
|
||||
}));
|
||||
}, (reason) => {
|
||||
aDbg.postMessage(JSON.stringify({
|
||||
type: "rpc",
|
||||
result: null,
|
||||
error: reason,
|
||||
id: packet.id
|
||||
}));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
aDbg.addListener(listener);
|
||||
}
|
||||
|
||||
// Step 2: Send a connect request to the worker debugger.
|
||||
aDbg.postMessage(JSON.stringify({
|
||||
|
@ -1,7 +1,27 @@
|
||||
"use strict"
|
||||
|
||||
// This function is used to do remote procedure calls from the worker to the
|
||||
// main thread. It is exposed as a built-in global to every module by the
|
||||
// worker loader. To make sure the worker loader can access it, it needs to be
|
||||
// defined before loading the worker loader script below.
|
||||
this.rpc = function (method, ...params) {
|
||||
let id = nextId++;
|
||||
|
||||
postMessage(JSON.stringify({
|
||||
type: "rpc",
|
||||
method: method,
|
||||
params: params,
|
||||
id: id
|
||||
}));
|
||||
|
||||
let deferred = Promise.defer();
|
||||
rpcDeferreds[id] = deferred;
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
loadSubScript("resource://gre/modules/devtools/worker-loader.js");
|
||||
|
||||
let Promise = worker.require("promise");
|
||||
let { ActorPool } = worker.require("devtools/server/actors/common");
|
||||
let { ThreadActor } = worker.require("devtools/server/actors/script");
|
||||
let { TabSources } = worker.require("devtools/server/actors/utils/TabSources");
|
||||
@ -14,6 +34,8 @@ DebuggerServer.createRootActor = function () {
|
||||
};
|
||||
|
||||
let connections = Object.create(null);
|
||||
let nextId = 0;
|
||||
let rpcDeferreds = [];
|
||||
|
||||
this.addEventListener("message", function (event) {
|
||||
let packet = JSON.parse(event.data);
|
||||
@ -21,7 +43,10 @@ this.addEventListener("message", function (event) {
|
||||
case "connect":
|
||||
// Step 3: Create a connection to the parent.
|
||||
let connection = DebuggerServer.connectToParent(packet.id, this);
|
||||
connections[packet.id] = connection;
|
||||
connections[packet.id] = {
|
||||
connection : connection,
|
||||
rpcs: []
|
||||
};
|
||||
|
||||
// Step 4: Create a thread actor for the connection to the parent.
|
||||
let pool = new ActorPool(connection);
|
||||
@ -60,7 +85,16 @@ this.addEventListener("message", function (event) {
|
||||
break;
|
||||
|
||||
case "disconnect":
|
||||
connections[packet.id].close();
|
||||
connections[packet.id].connection.close();
|
||||
break;
|
||||
|
||||
case "rpc":
|
||||
let deferred = rpcDeferreds[packet.id];
|
||||
delete rpcDeferreds[packet.id];
|
||||
if (packet.error) {
|
||||
deferred.reject(packet.error);
|
||||
}
|
||||
deferred.resolve(packet.result);
|
||||
break;
|
||||
};
|
||||
});
|
||||
|
@ -368,6 +368,7 @@ let {
|
||||
Debugger,
|
||||
createSandbox,
|
||||
dump,
|
||||
rpc,
|
||||
loadSubScript,
|
||||
reportError,
|
||||
setImmediate,
|
||||
@ -405,6 +406,8 @@ let {
|
||||
});
|
||||
};
|
||||
|
||||
let rpc = undefined;
|
||||
|
||||
let subScriptLoader = Cc['@mozilla.org/moz/jssubscript-loader;1'].
|
||||
getService(Ci.mozIJSSubScriptLoader);
|
||||
|
||||
@ -427,6 +430,7 @@ let {
|
||||
Debugger,
|
||||
createSandbox,
|
||||
dump,
|
||||
rpc,
|
||||
loadSubScript,
|
||||
reportError,
|
||||
setImmediate,
|
||||
@ -459,6 +463,7 @@ let {
|
||||
Debugger: this.Debugger,
|
||||
createSandbox: this.createSandbox,
|
||||
dump: this.dump,
|
||||
rpc: this.rpc,
|
||||
loadSubScript: this.loadSubScript,
|
||||
reportError: this.reportError,
|
||||
setImmediate: this.setImmediate,
|
||||
@ -477,6 +482,7 @@ this.worker = new WorkerDebuggerLoader({
|
||||
"dump": dump,
|
||||
"loader": loader,
|
||||
"reportError": reportError,
|
||||
"rpc": rpc,
|
||||
"setImmediate": setImmediate
|
||||
},
|
||||
loadSubScript: loadSubScript,
|
||||
|
Loading…
Reference in New Issue
Block a user