|
|
|
|
| 1 |
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ |
1 |
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ |
| 2 |
/* vim: set ft= javascript ts=2 et sw=2 tw=80: */ |
2 |
/* vim: set ft= javascript ts=2 et sw=2 tw=80: */ |
| 3 |
/* This Source Code Form is subject to the terms of the Mozilla Public |
3 |
/* This Source Code Form is subject to the terms of the Mozilla Public |
| 4 |
* License, v. 2.0. If a copy of the MPL was not distributed with this |
4 |
* License, v. 2.0. If a copy of the MPL was not distributed with this |
| 5 |
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
5 |
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| 6 |
|
6 |
|
| 7 |
"use strict"; |
7 |
"use strict"; |
| 8 |
|
8 |
|
| 9 |
const {Cc, Ci, Cu, Cr} = require("chrome"); |
9 |
const {Cc, Ci, Cu, Cr, components} = require("chrome"); |
| 10 |
const Services = require("Services"); |
10 |
const Services = require("Services"); |
| 11 |
|
11 |
|
| 12 |
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
12 |
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); |
| 13 |
|
13 |
|
| 14 |
loader.lazyRequireGetter(this, "NetworkHelper", |
14 |
loader.lazyRequireGetter(this, "NetworkHelper", |
| 15 |
"devtools/shared/webconsole/network-helper"); |
15 |
"devtools/shared/webconsole/network-helper"); |
| 16 |
loader.lazyRequireGetter(this, "DevToolsUtils", |
16 |
loader.lazyRequireGetter(this, "DevToolsUtils", |
| 17 |
"devtools/shared/DevToolsUtils"); |
17 |
"devtools/shared/DevToolsUtils"); |
|
|
| 32 |
const HTTP_FOUND = 302; |
32 |
const HTTP_FOUND = 302; |
| 33 |
const HTTP_SEE_OTHER = 303; |
33 |
const HTTP_SEE_OTHER = 303; |
| 34 |
const HTTP_TEMPORARY_REDIRECT = 307; |
34 |
const HTTP_TEMPORARY_REDIRECT = 307; |
| 35 |
|
35 |
|
| 36 |
// The maximum number of bytes a NetworkResponseListener can hold: 1 MB |
36 |
// The maximum number of bytes a NetworkResponseListener can hold: 1 MB |
| 37 |
const RESPONSE_BODY_LIMIT = 1048576; |
37 |
const RESPONSE_BODY_LIMIT = 1048576; |
| 38 |
|
38 |
|
| 39 |
/** |
39 |
/** |
|
|
40 |
* Check if a given network request should be logged by a network monitor |
| 41 |
* based on the specified filters. |
| 42 |
* |
| 43 |
* @param nsIHttpChannel channel |
| 44 |
* Request to check. |
| 45 |
* @param filters |
| 46 |
* NetworkMonitor filters to match against. |
| 47 |
* @return boolean |
| 48 |
* True if the network request should be logged, false otherwise. |
| 49 |
*/ |
| 50 |
function matchRequest(channel, filters) { |
| 51 |
// Log everything if no filter is specified |
| 52 |
if (!filters.topFrame && !filters.window && !filters.appId) { |
| 53 |
return true; |
| 54 |
} |
| 55 |
|
| 56 |
// Ignore requests from chrome or add-on code when we are monitoring |
| 57 |
// content. |
| 58 |
// TODO: one particular test (browser_styleeditor_fetch-from-cache.js) needs |
| 59 |
// the DevToolsUtils.testing check. We will move to a better way to serve |
| 60 |
// its needs in bug 1167188, where this check should be removed. |
| 61 |
if (!DevToolsUtils.testing && channel.loadInfo && |
| 62 |
channel.loadInfo.loadingDocument === null && |
| 63 |
channel.loadInfo.loadingPrincipal === |
| 64 |
Services.scriptSecurityManager.getSystemPrincipal()) { |
| 65 |
return false; |
| 66 |
} |
| 67 |
|
| 68 |
if (filters.window) { |
| 69 |
// Since frames support, this.window may not be the top level content |
| 70 |
// frame, so that we can't only compare with win.top. |
| 71 |
let win = NetworkHelper.getWindowForRequest(channel); |
| 72 |
while (win) { |
| 73 |
if (win == filters.window) { |
| 74 |
return true; |
| 75 |
} |
| 76 |
if (win.parent == win) { |
| 77 |
break; |
| 78 |
} |
| 79 |
win = win.parent; |
| 80 |
} |
| 81 |
} |
| 82 |
|
| 83 |
if (filters.topFrame) { |
| 84 |
let topFrame = NetworkHelper.getTopFrameForRequest(channel); |
| 85 |
if (topFrame && topFrame === filters.topFrame) { |
| 86 |
return true; |
| 87 |
} |
| 88 |
} |
| 89 |
|
| 90 |
if (filters.appId) { |
| 91 |
let appId = NetworkHelper.getAppIdForRequest(channel); |
| 92 |
if (appId && appId == filters.appId) { |
| 93 |
return true; |
| 94 |
} |
| 95 |
} |
| 96 |
|
| 97 |
// The following check is necessary because beacon channels don't come |
| 98 |
// associated with a load group. Bug 1160837 will hopefully introduce a |
| 99 |
// platform fix that will render the following code entirely useless. |
| 100 |
if (channel.loadInfo && |
| 101 |
channel.loadInfo.externalContentPolicyType == |
| 102 |
Ci.nsIContentPolicy.TYPE_BEACON) { |
| 103 |
let nonE10sMatch = filters.window && |
| 104 |
channel.loadInfo.loadingDocument === filters.window.document; |
| 105 |
const loadingPrincipal = channel.loadInfo.loadingPrincipal; |
| 106 |
let e10sMatch = filters.topFrame && |
| 107 |
filters.topFrame.contentPrincipal && |
| 108 |
filters.topFrame.contentPrincipal.equals(loadingPrincipal) && |
| 109 |
filters.topFrame.contentPrincipal.URI.spec == channel.referrer.spec; |
| 110 |
let b2gMatch = filters.appId && loadingPrincipal.appId === filters.appId; |
| 111 |
if (nonE10sMatch || e10sMatch || b2gMatch) { |
| 112 |
return true; |
| 113 |
} |
| 114 |
} |
| 115 |
|
| 116 |
return false; |
| 117 |
} |
| 118 |
|
| 119 |
function StackTraceCollector(filters) { |
| 120 |
this.filters = filters; |
| 121 |
this.stacktraces = []; |
| 122 |
this._onOpeningRequest = this._onOpeningRequest.bind(this); |
| 123 |
} |
| 124 |
|
| 125 |
StackTraceCollector.prototype = { |
| 126 |
init() { |
| 127 |
Services.obs.addObserver(this._onOpeningRequest, |
| 128 |
"http-on-opening-request", false); |
| 129 |
console.log("Added http-on-opening-request observer"); |
| 130 |
}, |
| 131 |
|
| 132 |
destroy() { |
| 133 |
Services.obs.removeObserver(this._onOpeningRequest, |
| 134 |
"http-on-opening-request"); |
| 135 |
}, |
| 136 |
|
| 137 |
_onOpeningRequest(subject) { |
| 138 |
let channel = subject.QueryInterface(Ci.nsIHttpChannel); |
| 139 |
|
| 140 |
if (!matchRequest(channel, this.filters)) { |
| 141 |
return; |
| 142 |
} |
| 143 |
|
| 144 |
let uri = channel.URI.spec; |
| 145 |
let frame = components.stack; |
| 146 |
let stacktrace = []; |
| 147 |
if (frame && frame.caller) { |
| 148 |
frame = frame.caller; |
| 149 |
while (frame) { |
| 150 |
stacktrace.push({ |
| 151 |
filename: frame.filename, |
| 152 |
lineNumber: frame.lineNumber, |
| 153 |
columnNumber: frame.columnNumber, |
| 154 |
functionName: frame.name, |
| 155 |
asyncCause: frame.asyncCause, |
| 156 |
}); |
| 157 |
if (frame.asyncCaller) { |
| 158 |
frame = frame.asyncCaller; |
| 159 |
} else { |
| 160 |
frame = frame.caller; |
| 161 |
} |
| 162 |
} |
| 163 |
} |
| 164 |
|
| 165 |
console.log("Queued stacktrace:", this.stacktraces.length, uri); |
| 166 |
this.stacktraces.push({ uri, stacktrace }); |
| 167 |
}, |
| 168 |
|
| 169 |
getStackTrace(uri) { |
| 170 |
let trace = this.stacktraces.shift(); |
| 171 |
if (!trace) { |
| 172 |
console.error(`Stacktrace for ${uri} not found in queue`); |
| 173 |
return null; |
| 174 |
} |
| 175 |
|
| 176 |
if (uri !== trace.uri) { |
| 177 |
console.error( |
| 178 |
`Stacktrace for ${uri} is not next in queue (got ${trace.uri})`); |
| 179 |
return null; |
| 180 |
} |
| 181 |
|
| 182 |
console.log("Retrieved stacktrace:", this.stacktraces.length, uri); |
| 183 |
return trace.stacktrace; |
| 184 |
} |
| 185 |
}; |
| 186 |
|
| 187 |
/** |
| 40 |
* The network response listener implements the nsIStreamListener and |
188 |
* The network response listener implements the nsIStreamListener and |
| 41 |
* nsIRequestObserver interfaces. This is used within the NetworkMonitor feature |
189 |
* nsIRequestObserver interfaces. This is used within the NetworkMonitor feature |
| 42 |
* to get the response body of the request. |
190 |
* to get the response body of the request. |
| 43 |
* |
191 |
* |
| 44 |
* The code is mostly based on code listings from: |
192 |
* The code is mostly based on code listings from: |
| 45 |
* |
193 |
* |
| 46 |
* http://www.softwareishard.com/blog/firebug/ |
194 |
* http://www.softwareishard.com/blog/firebug/ |
| 47 |
* nsitraceablechannel-intercept-http-traffic/ |
195 |
* nsitraceablechannel-intercept-http-traffic/ |
|
|
| 473 |
* - onNetworkEvent(requestInfo, channel, networkMonitor). |
621 |
* - onNetworkEvent(requestInfo, channel, networkMonitor). |
| 474 |
* This method is invoked once for every new network request and it is |
622 |
* This method is invoked once for every new network request and it is |
| 475 |
* given the following arguments: the initial network request |
623 |
* given the following arguments: the initial network request |
| 476 |
* information, and the channel. The third argument is the NetworkMonitor |
624 |
* information, and the channel. The third argument is the NetworkMonitor |
| 477 |
* instance. |
625 |
* instance. |
| 478 |
* onNetworkEvent() must return an object which holds several add*() |
626 |
* onNetworkEvent() must return an object which holds several add*() |
| 479 |
* methods which are used to add further network request/response |
627 |
* methods which are used to add further network request/response |
| 480 |
* information. |
628 |
* information. |
|
|
629 |
* @param boolean collectStackTraces |
| 630 |
* Should this network monitor be collecting stack traces? Should |
| 631 |
* happen only in non-e10s where there is only one NetworkMonitor and |
| 632 |
* there is no proxying between parent and child. In e10s, the |
| 633 |
* NetworkMonitorChild takes care of the stack trace collection. |
| 481 |
*/ |
634 |
*/ |
| 482 |
function NetworkMonitor(filters, owner) { |
635 |
function NetworkMonitor(filters, owner, collectStackTraces) { |
| 483 |
if (filters) { |
636 |
this.filters = filters; |
| 484 |
this.window = filters.window; |
|
|
| 485 |
this.appId = filters.appId; |
| 486 |
this.topFrame = filters.topFrame; |
| 487 |
} |
| 488 |
if (!this.window && !this.appId && !this.topFrame) { |
| 489 |
this._logEverything = true; |
| 490 |
} |
| 491 |
this.owner = owner; |
637 |
this.owner = owner; |
| 492 |
this.openRequests = {}; |
638 |
this.openRequests = {}; |
| 493 |
this.openResponses = {}; |
639 |
this.openResponses = {}; |
| 494 |
this._httpResponseExaminer = |
640 |
this._httpResponseExaminer = |
| 495 |
DevToolsUtils.makeInfallible(this._httpResponseExaminer).bind(this); |
641 |
DevToolsUtils.makeInfallible(this._httpResponseExaminer).bind(this); |
| 496 |
this._serviceWorkerRequest = this._serviceWorkerRequest.bind(this); |
642 |
this._serviceWorkerRequest = this._serviceWorkerRequest.bind(this); |
|
|
643 |
|
| 644 |
if (collectStackTraces) { |
| 645 |
this.stackTraceCollector = new StackTraceCollector(this.filters); |
| 646 |
this.stackTraceCollector.init(); |
| 647 |
} |
| 497 |
} |
648 |
} |
|
|
649 |
|
| 498 |
exports.NetworkMonitor = NetworkMonitor; |
650 |
exports.NetworkMonitor = NetworkMonitor; |
| 499 |
|
651 |
|
| 500 |
NetworkMonitor.prototype = { |
652 |
NetworkMonitor.prototype = { |
| 501 |
_logEverything: false, |
653 |
filters: null, |
| 502 |
window: null, |
|
|
| 503 |
appId: null, |
| 504 |
topFrame: null, |
| 505 |
|
654 |
|
| 506 |
httpTransactionCodes: { |
655 |
httpTransactionCodes: { |
| 507 |
0x5001: "REQUEST_HEADER", |
656 |
0x5001: "REQUEST_HEADER", |
| 508 |
0x5002: "REQUEST_BODY_SENT", |
657 |
0x5002: "REQUEST_BODY_SENT", |
| 509 |
0x5003: "RESPONSE_START", |
658 |
0x5003: "RESPONSE_START", |
| 510 |
0x5004: "RESPONSE_HEADER", |
659 |
0x5004: "RESPONSE_HEADER", |
| 511 |
0x5005: "RESPONSE_COMPLETE", |
660 |
0x5005: "RESPONSE_COMPLETE", |
| 512 |
0x5006: "TRANSACTION_CLOSE", |
661 |
0x5006: "TRANSACTION_CLOSE", |
|
|
| 547 |
*/ |
696 |
*/ |
| 548 |
init: function () { |
697 |
init: function () { |
| 549 |
this.responsePipeSegmentSize = Services.prefs |
698 |
this.responsePipeSegmentSize = Services.prefs |
| 550 |
.getIntPref("network.buffer.cache.size"); |
699 |
.getIntPref("network.buffer.cache.size"); |
| 551 |
this.interceptedChannels = new Set(); |
700 |
this.interceptedChannels = new Set(); |
| 552 |
|
701 |
|
| 553 |
if (Services.appinfo.processType != Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT) { |
702 |
if (Services.appinfo.processType != Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT) { |
| 554 |
gActivityDistributor.addObserver(this); |
703 |
gActivityDistributor.addObserver(this); |
|
|
704 |
console.log("Added observer to activity distributor"); |
| 555 |
Services.obs.addObserver(this._httpResponseExaminer, |
705 |
Services.obs.addObserver(this._httpResponseExaminer, |
| 556 |
"http-on-examine-response", false); |
706 |
"http-on-examine-response", false); |
| 557 |
Services.obs.addObserver(this._httpResponseExaminer, |
707 |
Services.obs.addObserver(this._httpResponseExaminer, |
| 558 |
"http-on-examine-cached-response", false); |
708 |
"http-on-examine-cached-response", false); |
| 559 |
} |
709 |
} |
| 560 |
// In child processes, only watch for service worker requests |
710 |
// In child processes, only watch for service worker requests |
| 561 |
// everything else only happens in the parent process |
711 |
// everything else only happens in the parent process |
| 562 |
Services.obs.addObserver(this._serviceWorkerRequest, |
712 |
Services.obs.addObserver(this._serviceWorkerRequest, |
|
|
| 597 |
(topic != "http-on-examine-response" && |
747 |
(topic != "http-on-examine-response" && |
| 598 |
topic != "http-on-examine-cached-response") || |
748 |
topic != "http-on-examine-cached-response") || |
| 599 |
!(subject instanceof Ci.nsIHttpChannel)) { |
749 |
!(subject instanceof Ci.nsIHttpChannel)) { |
| 600 |
return; |
750 |
return; |
| 601 |
} |
751 |
} |
| 602 |
|
752 |
|
| 603 |
let channel = subject.QueryInterface(Ci.nsIHttpChannel); |
753 |
let channel = subject.QueryInterface(Ci.nsIHttpChannel); |
| 604 |
|
754 |
|
| 605 |
if (!this._matchRequest(channel)) { |
755 |
if (!matchRequest(channel, this.filters)) { |
| 606 |
return; |
756 |
return; |
| 607 |
} |
757 |
} |
| 608 |
|
758 |
|
| 609 |
let response = { |
759 |
let response = { |
| 610 |
id: gSequenceId(), |
760 |
id: gSequenceId(), |
| 611 |
channel: channel, |
761 |
channel: channel, |
| 612 |
headers: [], |
762 |
headers: [], |
| 613 |
cookies: [], |
763 |
cookies: [], |
|
|
| 649 |
this.openResponses[response.id] = response; |
799 |
this.openResponses[response.id] = response; |
| 650 |
|
800 |
|
| 651 |
if (topic === "http-on-examine-cached-response") { |
801 |
if (topic === "http-on-examine-cached-response") { |
| 652 |
// Service worker requests emits cached-reponse notification on non-e10s, |
802 |
// Service worker requests emits cached-reponse notification on non-e10s, |
| 653 |
// and we fake one on e10s. |
803 |
// and we fake one on e10s. |
| 654 |
let fromServiceWorker = this.interceptedChannels.has(channel); |
804 |
let fromServiceWorker = this.interceptedChannels.has(channel); |
| 655 |
this.interceptedChannels.delete(channel); |
805 |
this.interceptedChannels.delete(channel); |
| 656 |
|
806 |
|
|
|
807 |
console.log("Observer for http-on-cached-response fired:", |
| 808 |
channel.URI.spec); |
| 809 |
|
| 657 |
// If this is a cached response, there never was a request event |
810 |
// If this is a cached response, there never was a request event |
| 658 |
// so we need to construct one here so the frontend gets all the |
811 |
// so we need to construct one here so the frontend gets all the |
| 659 |
// expected events. |
812 |
// expected events. |
| 660 |
let httpActivity = this._createNetworkEvent(channel, { |
813 |
let httpActivity = this._createNetworkEvent(channel, { |
| 661 |
fromCache: !fromServiceWorker, |
814 |
fromCache: !fromServiceWorker, |
| 662 |
fromServiceWorker: fromServiceWorker |
815 |
fromServiceWorker: fromServiceWorker |
| 663 |
}); |
816 |
}); |
| 664 |
httpActivity.owner.addResponseStart({ |
817 |
httpActivity.owner.addResponseStart({ |
|
|
| 752 |
this._onTransactionClose(httpActivity); |
905 |
this._onTransactionClose(httpActivity); |
| 753 |
break; |
906 |
break; |
| 754 |
default: |
907 |
default: |
| 755 |
break; |
908 |
break; |
| 756 |
} |
909 |
} |
| 757 |
}), |
910 |
}), |
| 758 |
|
911 |
|
| 759 |
/** |
912 |
/** |
| 760 |
* Check if a given network request should be logged by this network monitor |
|
|
| 761 |
* instance based on the current filters. |
| 762 |
* |
| 763 |
* @private |
| 764 |
* @param nsIHttpChannel channel |
| 765 |
* Request to check. |
| 766 |
* @return boolean |
| 767 |
* True if the network request should be logged, false otherwise. |
| 768 |
*/ |
| 769 |
_matchRequest: function (channel) { |
| 770 |
if (this._logEverything) { |
| 771 |
return true; |
| 772 |
} |
| 773 |
|
| 774 |
// Ignore requests from chrome or add-on code when we are monitoring |
| 775 |
// content. |
| 776 |
// TODO: one particular test (browser_styleeditor_fetch-from-cache.js) needs |
| 777 |
// the DevToolsUtils.testing check. We will move to a better way to serve |
| 778 |
// its needs in bug 1167188, where this check should be removed. |
| 779 |
if (!DevToolsUtils.testing && channel.loadInfo && |
| 780 |
channel.loadInfo.loadingDocument === null && |
| 781 |
channel.loadInfo.loadingPrincipal === |
| 782 |
Services.scriptSecurityManager.getSystemPrincipal()) { |
| 783 |
return false; |
| 784 |
} |
| 785 |
|
| 786 |
if (this.window) { |
| 787 |
// Since frames support, this.window may not be the top level content |
| 788 |
// frame, so that we can't only compare with win.top. |
| 789 |
let win = NetworkHelper.getWindowForRequest(channel); |
| 790 |
while (win) { |
| 791 |
if (win == this.window) { |
| 792 |
return true; |
| 793 |
} |
| 794 |
if (win.parent == win) { |
| 795 |
break; |
| 796 |
} |
| 797 |
win = win.parent; |
| 798 |
} |
| 799 |
} |
| 800 |
|
| 801 |
if (this.topFrame) { |
| 802 |
let topFrame = NetworkHelper.getTopFrameForRequest(channel); |
| 803 |
if (topFrame && topFrame === this.topFrame) { |
| 804 |
return true; |
| 805 |
} |
| 806 |
} |
| 807 |
|
| 808 |
if (this.appId) { |
| 809 |
let appId = NetworkHelper.getAppIdForRequest(channel); |
| 810 |
if (appId && appId == this.appId) { |
| 811 |
return true; |
| 812 |
} |
| 813 |
} |
| 814 |
|
| 815 |
// The following check is necessary because beacon channels don't come |
| 816 |
// associated with a load group. Bug 1160837 will hopefully introduce a |
| 817 |
// platform fix that will render the following code entirely useless. |
| 818 |
if (channel.loadInfo && |
| 819 |
channel.loadInfo.externalContentPolicyType == |
| 820 |
Ci.nsIContentPolicy.TYPE_BEACON) { |
| 821 |
let nonE10sMatch = this.window && |
| 822 |
channel.loadInfo.loadingDocument === this.window.document; |
| 823 |
const loadingPrincipal = channel.loadInfo.loadingPrincipal; |
| 824 |
let e10sMatch = this.topFrame && |
| 825 |
this.topFrame.contentPrincipal && |
| 826 |
this.topFrame.contentPrincipal.equals(loadingPrincipal) && |
| 827 |
this.topFrame.contentPrincipal.URI.spec == channel.referrer.spec; |
| 828 |
let b2gMatch = this.appId && loadingPrincipal.appId === this.appId; |
| 829 |
if (nonE10sMatch || e10sMatch || b2gMatch) { |
| 830 |
return true; |
| 831 |
} |
| 832 |
} |
| 833 |
|
| 834 |
return false; |
| 835 |
}, |
| 836 |
|
| 837 |
/** |
| 838 |
* |
913 |
* |
| 839 |
*/ |
914 |
*/ |
| 840 |
_createNetworkEvent: function (channel, { timestamp, extraStringData, |
915 |
_createNetworkEvent: function (channel, { timestamp, extraStringData, |
| 841 |
fromCache, fromServiceWorker }) { |
916 |
fromCache, fromServiceWorker }) { |
| 842 |
let win = NetworkHelper.getWindowForRequest(channel); |
917 |
let win = NetworkHelper.getWindowForRequest(channel); |
| 843 |
let httpActivity = this.createActivityObject(channel); |
918 |
let httpActivity = this.createActivityObject(channel); |
| 844 |
|
919 |
|
| 845 |
// see _onRequestBodySent() |
920 |
// see _onRequestBodySent() |
|
|
| 866 |
event.fromCache = fromCache; |
941 |
event.fromCache = fromCache; |
| 867 |
event.fromServiceWorker = fromServiceWorker; |
942 |
event.fromServiceWorker = fromServiceWorker; |
| 868 |
httpActivity.fromServiceWorker = fromServiceWorker; |
943 |
httpActivity.fromServiceWorker = fromServiceWorker; |
| 869 |
|
944 |
|
| 870 |
if (extraStringData) { |
945 |
if (extraStringData) { |
| 871 |
event.headersSize = extraStringData.length; |
946 |
event.headersSize = extraStringData.length; |
| 872 |
} |
947 |
} |
| 873 |
|
948 |
|
| 874 |
// Determine if this is an XHR request. |
949 |
// Determine the cause and if this is an XHR request. |
|
|
950 |
let causeType = channel.loadInfo.externalContentPolicyType; |
| 951 |
let loadingPrincipal = channel.loadInfo.loadingPrincipal; |
| 952 |
let causeUri = loadingPrincipal ? loadingPrincipal.URI : null; |
| 953 |
event.cause = { |
| 954 |
type: causeType, |
| 955 |
uri: causeUri ? causeUri.spec : null |
| 956 |
}; |
| 957 |
|
| 958 |
if (this.stackTraceCollector) { |
| 959 |
event.cause.stacktrace = |
| 960 |
this.stackTraceCollector.getStackTrace(event.url); |
| 961 |
} |
| 962 |
|
| 875 |
httpActivity.isXHR = event.isXHR = |
963 |
httpActivity.isXHR = event.isXHR = |
| 876 |
(channel.loadInfo.externalContentPolicyType === |
964 |
(causeType === Ci.nsIContentPolicy.TYPE_XMLHTTPREQUEST || |
| 877 |
Ci.nsIContentPolicy.TYPE_XMLHTTPREQUEST || |
965 |
causeType === Ci.nsIContentPolicy.TYPE_FETCH); |
| 878 |
channel.loadInfo.externalContentPolicyType === |
|
|
| 879 |
Ci.nsIContentPolicy.TYPE_FETCH); |
| 880 |
|
966 |
|
| 881 |
// Determine the HTTP version. |
967 |
// Determine the HTTP version. |
| 882 |
let httpVersionMaj = {}; |
968 |
let httpVersionMaj = {}; |
| 883 |
let httpVersionMin = {}; |
969 |
let httpVersionMin = {}; |
| 884 |
channel.QueryInterface(Ci.nsIHttpChannelInternal); |
970 |
channel.QueryInterface(Ci.nsIHttpChannelInternal); |
| 885 |
channel.getRequestVersion(httpVersionMaj, httpVersionMin); |
971 |
channel.getRequestVersion(httpVersionMaj, httpVersionMin); |
| 886 |
|
972 |
|
| 887 |
event.httpVersion = "HTTP/" + httpVersionMaj.value + "." + |
973 |
event.httpVersion = "HTTP/" + httpVersionMaj.value + "." + |
|
|
| 927 |
* |
1013 |
* |
| 928 |
* @private |
1014 |
* @private |
| 929 |
* @param nsIHttpChannel channel |
1015 |
* @param nsIHttpChannel channel |
| 930 |
* @param number timestamp |
1016 |
* @param number timestamp |
| 931 |
* @param string extraStringData |
1017 |
* @param string extraStringData |
| 932 |
* @return void |
1018 |
* @return void |
| 933 |
*/ |
1019 |
*/ |
| 934 |
_onRequestHeader: function (channel, timestamp, extraStringData) { |
1020 |
_onRequestHeader: function (channel, timestamp, extraStringData) { |
| 935 |
if (!this._matchRequest(channel)) { |
1021 |
if (!matchRequest(channel, this.filters)) { |
| 936 |
return; |
1022 |
return; |
| 937 |
} |
1023 |
} |
| 938 |
|
1024 |
|
|
|
1025 |
console.log("Observer for activity-request-header fired:", |
| 1026 |
channel.URI.spec); |
| 1027 |
|
| 939 |
this._createNetworkEvent(channel, { timestamp: timestamp, |
1028 |
this._createNetworkEvent(channel, { timestamp: timestamp, |
| 940 |
extraStringData: extraStringData }); |
1029 |
extraStringData: extraStringData }); |
| 941 |
}, |
1030 |
}, |
| 942 |
|
1031 |
|
| 943 |
/** |
1032 |
/** |
| 944 |
* Create the empty HTTP activity object. This object is used for storing all |
1033 |
* Create the empty HTTP activity object. This object is used for storing all |
| 945 |
* the request and response information. |
1034 |
* the request and response information. |
| 946 |
* |
1035 |
* |
|
|
| 1218 |
"http-on-examine-response"); |
1307 |
"http-on-examine-response"); |
| 1219 |
Services.obs.removeObserver(this._httpResponseExaminer, |
1308 |
Services.obs.removeObserver(this._httpResponseExaminer, |
| 1220 |
"http-on-examine-cached-response"); |
1309 |
"http-on-examine-cached-response"); |
| 1221 |
} |
1310 |
} |
| 1222 |
|
1311 |
|
| 1223 |
Services.obs.removeObserver(this._serviceWorkerRequest, |
1312 |
Services.obs.removeObserver(this._serviceWorkerRequest, |
| 1224 |
"service-worker-synthesized-response"); |
1313 |
"service-worker-synthesized-response"); |
| 1225 |
|
1314 |
|
|
|
1315 |
if (this.stackTraceCollector) { |
| 1316 |
this.stackTraceCollector.destroy(); |
| 1317 |
} |
| 1318 |
|
| 1226 |
this.interceptedChannels.clear(); |
1319 |
this.interceptedChannels.clear(); |
| 1227 |
this.openRequests = {}; |
1320 |
this.openRequests = {}; |
| 1228 |
this.openResponses = {}; |
1321 |
this.openResponses = {}; |
| 1229 |
this.owner = null; |
1322 |
this.owner = null; |
| 1230 |
this.window = null; |
1323 |
this.filters = null; |
| 1231 |
this.topFrame = null; |
|
|
| 1232 |
}, |
1324 |
}, |
| 1233 |
}; |
1325 |
}; |
| 1234 |
|
1326 |
|
| 1235 |
/** |
1327 |
/** |
| 1236 |
* The NetworkMonitorChild is used to proxy all of the network activity of the |
1328 |
* The NetworkMonitorChild is used to proxy all of the network activity of the |
| 1237 |
* child app process from the main process. The child WebConsoleActor creates an |
1329 |
* child app process from the main process. The child WebConsoleActor creates an |
| 1238 |
* instance of this object. |
1330 |
* instance of this object. |
| 1239 |
* |
1331 |
* |
|
|
| 1250 |
* The web appId of the child process. |
1342 |
* The web appId of the child process. |
| 1251 |
* @param nsIMessageManager messageManager |
1343 |
* @param nsIMessageManager messageManager |
| 1252 |
* The nsIMessageManager to use to communicate with the parent process. |
1344 |
* The nsIMessageManager to use to communicate with the parent process. |
| 1253 |
* @param string connID |
1345 |
* @param string connID |
| 1254 |
* The connection ID to use for send messages to the parent process. |
1346 |
* The connection ID to use for send messages to the parent process. |
| 1255 |
* @param object owner |
1347 |
* @param object owner |
| 1256 |
* The WebConsoleActor that is listening for the network requests. |
1348 |
* The WebConsoleActor that is listening for the network requests. |
| 1257 |
*/ |
1349 |
*/ |
| 1258 |
function NetworkMonitorChild(appId, messageManager, connID, owner) { |
1350 |
function NetworkMonitorChild(window, appId, messageManager, connID, owner) { |
|
|
1351 |
this.window = window; |
| 1259 |
this.appId = appId; |
1352 |
this.appId = appId; |
| 1260 |
this.connID = connID; |
1353 |
this.connID = connID; |
| 1261 |
this.owner = owner; |
1354 |
this.owner = owner; |
| 1262 |
this._messageManager = messageManager; |
1355 |
this._messageManager = messageManager; |
| 1263 |
this._onNewEvent = this._onNewEvent.bind(this); |
1356 |
this._onNewEvent = this._onNewEvent.bind(this); |
| 1264 |
this._onUpdateEvent = this._onUpdateEvent.bind(this); |
1357 |
this._onUpdateEvent = this._onUpdateEvent.bind(this); |
| 1265 |
this._netEvents = new Map(); |
1358 |
this._netEvents = new Map(); |
|
|
1359 |
|
| 1360 |
this.stackTraceCollector = new StackTraceCollector({ |
| 1361 |
window: this.window, |
| 1362 |
appId: this.appId |
| 1363 |
}); |
| 1364 |
this.stackTraceCollector.init(); |
| 1266 |
} |
1365 |
} |
| 1267 |
exports.NetworkMonitorChild = NetworkMonitorChild; |
1366 |
exports.NetworkMonitorChild = NetworkMonitorChild; |
| 1268 |
|
1367 |
|
| 1269 |
NetworkMonitorChild.prototype = { |
1368 |
NetworkMonitorChild.prototype = { |
| 1270 |
appId: null, |
1369 |
appId: null, |
| 1271 |
owner: null, |
1370 |
owner: null, |
| 1272 |
_netEvents: null, |
1371 |
_netEvents: null, |
| 1273 |
_saveRequestAndResponseBodies: true, |
1372 |
_saveRequestAndResponseBodies: true, |
|
|
| 1297 |
mm.sendAsyncMessage("debug:netmonitor:" + this.connID, { |
1396 |
mm.sendAsyncMessage("debug:netmonitor:" + this.connID, { |
| 1298 |
appId: this.appId, |
1397 |
appId: this.appId, |
| 1299 |
action: "start", |
1398 |
action: "start", |
| 1300 |
}); |
1399 |
}); |
| 1301 |
}, |
1400 |
}, |
| 1302 |
|
1401 |
|
| 1303 |
_onNewEvent: DevToolsUtils.makeInfallible(function _onNewEvent(msg) { |
1402 |
_onNewEvent: DevToolsUtils.makeInfallible(function _onNewEvent(msg) { |
| 1304 |
let {id, event} = msg.data; |
1403 |
let {id, event} = msg.data; |
|
|
1404 |
|
| 1405 |
event.cause.stacktrace = this.stackTraceCollector.getStackTrace(event.url); |
| 1406 |
|
| 1305 |
let actor = this.owner.onNetworkEvent(event); |
1407 |
let actor = this.owner.onNetworkEvent(event); |
| 1306 |
this._netEvents.set(id, Cu.getWeakReference(actor)); |
1408 |
this._netEvents.set(id, Cu.getWeakReference(actor)); |
| 1307 |
}), |
1409 |
}), |
| 1308 |
|
1410 |
|
| 1309 |
_onUpdateEvent: DevToolsUtils.makeInfallible(function _onUpdateEvent(msg) { |
1411 |
_onUpdateEvent: DevToolsUtils.makeInfallible(function _onUpdateEvent(msg) { |
| 1310 |
let {id, method, args} = msg.data; |
1412 |
let {id, method, args} = msg.data; |
| 1311 |
let weakActor = this._netEvents.get(id); |
1413 |
let weakActor = this._netEvents.get(id); |
| 1312 |
let actor = weakActor ? weakActor.get() : null; |
1414 |
let actor = weakActor ? weakActor.get() : null; |
|
|
| 1319 |
Cu.reportError("Received debug:netmonitor:updateEvent unsupported " + |
1421 |
Cu.reportError("Received debug:netmonitor:updateEvent unsupported " + |
| 1320 |
"method: " + method); |
1422 |
"method: " + method); |
| 1321 |
return; |
1423 |
return; |
| 1322 |
} |
1424 |
} |
| 1323 |
actor[method].apply(actor, args); |
1425 |
actor[method].apply(actor, args); |
| 1324 |
}), |
1426 |
}), |
| 1325 |
|
1427 |
|
| 1326 |
destroy: function () { |
1428 |
destroy: function () { |
|
|
1429 |
this.stackTraceCollector.destroy(); |
| 1430 |
|
| 1327 |
let mm = this._messageManager; |
1431 |
let mm = this._messageManager; |
| 1328 |
try { |
1432 |
try { |
| 1329 |
mm.removeMessageListener("debug:netmonitor:" + this.connID + ":newEvent", |
1433 |
mm.removeMessageListener("debug:netmonitor:" + this.connID + ":newEvent", |
| 1330 |
this._onNewEvent); |
1434 |
this._onNewEvent); |
| 1331 |
mm.removeMessageListener("debug:netmonitor:" + this.connID + |
1435 |
mm.removeMessageListener("debug:netmonitor:" + this.connID + |
| 1332 |
":updateEvent", |
1436 |
":updateEvent", |
| 1333 |
this._onUpdateEvent); |
1437 |
this._onUpdateEvent); |
| 1334 |
} catch (e) { |
1438 |
} catch (e) { |