Bug 832983 - Basic support for adding xhr breakpoints r=jlast

Differential Revision: https://phabricator.services.mozilla.com/D5662

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Anshul Malik 2018-09-21 22:20:37 +00:00
Родитель eeaa237703
Коммит efdb07ca38
2 изменённых файлов: 123 добавлений и 3 удалений

Просмотреть файл

@ -7,7 +7,7 @@
"use strict";
const Services = require("Services");
const { Cr } = require("chrome");
const { Cr, Ci } = require("chrome");
const { ActorPool, GeneratedLocation } = require("devtools/server/actors/common");
const { createValueGrip } = require("devtools/server/actors/object/utils");
const { longStringGrip } = require("devtools/server/actors/object/long-string");
@ -64,6 +64,8 @@ const ThreadActor = ActorClassWithSpec(threadSpec, {
this._parentClosed = false;
this._scripts = null;
this._pauseOnDOMEvents = null;
this._xhrBreakpoints = [];
this._observingNetwork = false;
this._options = {
useSourceMaps: false,
@ -92,7 +94,9 @@ const ThreadActor = ActorClassWithSpec(threadSpec, {
this.objectGrip = this.objectGrip.bind(this);
this.pauseObjectGrip = this.pauseObjectGrip.bind(this);
this._onWindowReady = this._onWindowReady.bind(this);
this._onOpeningRequest = this._onOpeningRequest.bind(this);
EventEmitter.on(this._parent, "window-ready", this._onWindowReady);
// Set a wrappedJSObject property so |this| can be sent via the observer svc
// for the xpcshell harness.
this.wrappedJSObject = this;
@ -228,6 +232,9 @@ const ThreadActor = ActorClassWithSpec(threadSpec, {
// things like breakpoints across connections.
this._sourceActorStore = null;
this._xhrBreakpoints = [];
this._updateNetworkObserver();
EventEmitter.off(this._parent, "window-ready", this._onWindowReady);
this.sources.off("newSource", this.onNewSourceEvent);
this.sources.off("updatedSource", this.onUpdatedSourceEvent);
@ -317,6 +324,100 @@ const ThreadActor = ActorClassWithSpec(threadSpec, {
}
},
_findXHRBreakpointIndex(p, m) {
return this._xhrBreakpoints.findIndex(
({ path, method }) => path === p && method === m);
},
removeXHRBreakpoint: function(path, method) {
const index = this._findXHRBreakpointIndex(path, method);
if (index >= 0) {
this._xhrBreakpoints.splice(index, 1);
}
return this._updateNetworkObserver();
},
setXHRBreakpoint: function(path, method) {
// request.path is a string,
// If requested url contains the path, then we pause.
const index = this._findXHRBreakpointIndex(path, method);
if (index === -1) {
this._xhrBreakpoints.push({ path, method });
}
return this._updateNetworkObserver();
},
_updateNetworkObserver() {
// Workers don't have access to `Services` and even if they did, network
// requests are all dispatched to the main thread, so there would be
// nothing here to listen for. We'll need to revisit implementing
// XHR breakpoints for workers.
if (isWorker) {
return false;
}
if (this._xhrBreakpoints.length > 0 && !this._observingNetwork) {
this._observingNetwork = true;
Services.obs.addObserver(this._onOpeningRequest, "http-on-opening-request");
} else if (this._xhrBreakpoints.length === 0 && this._observingNetwork) {
this._observingNetwork = false;
Services.obs.removeObserver(this._onOpeningRequest, "http-on-opening-request");
}
return true;
},
_onOpeningRequest: function(subject) {
if (this.skipBreakpoints) {
return;
}
const channel = subject.QueryInterface(Ci.nsIHttpChannel);
const url = channel.URI.asciiSpec;
const requestMethod = channel.requestMethod;
let causeType = Ci.nsIContentPolicy.TYPE_OTHER;
if (channel.loadInfo) {
causeType = channel.loadInfo.externalContentPolicyType;
}
const isXHR = (
causeType === Ci.nsIContentPolicy.TYPE_XMLHTTPREQUEST ||
causeType === Ci.nsIContentPolicy.TYPE_FETCH
);
if (!isXHR) {
// We currently break only if the request is either fetch or xhr
return;
}
let shouldPause = false;
for (const { path, method } of this._xhrBreakpoints) {
if (method !== "ANY" && method !== requestMethod) {
continue;
}
if (url.includes(path)) {
shouldPause = true;
break;
}
}
if (shouldPause) {
const frame = this.dbg.getNewestFrame();
// If there is no frame, this request was dispatched by logic that isn't
// primarily JS, so pausing the event loop wouldn't make sense.
// This covers background requests like loading the initial page document,
// or loading favicons. This also includes requests dispatched indirectly
// from workers. We'll need to handle them separately in the future.
if (frame) {
this._pauseAndRespond(frame, { type: "XHR" });
}
}
},
onDetach: function(request) {
this.destroy();
this._state = "detached";

Просмотреть файл

@ -3,12 +3,31 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const {generateActorSpec} = require("devtools/shared/protocol");
const {Arg, RetVal, generateActorSpec} = require("devtools/shared/protocol");
const threadSpec = generateActorSpec({
typeName: "context",
methods: {},
methods: {
setXHRBreakpoint: {
request: {
path: Arg(0, "string"),
method: Arg(1, "string")
},
response: {
value: RetVal("boolean")
}
},
removeXHRBreakpoint: {
request: {
path: Arg(0, "string"),
method: Arg(1, "string")
},
response: {
value: RetVal("boolean")
}
}
},
});
exports.threadSpec = threadSpec;