зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1344160 - Refactor netmonitor-controller.js r=Honza
MozReview-Commit-ID: JPUB83Qsmav --HG-- extra : rebase_source : 32f3d355d4ba0f79dc06b7394b6fa265c509990b
This commit is contained in:
Родитель
5292f017eb
Коммит
ffb7c92d26
|
@ -26,29 +26,32 @@
|
|||
const Provider = createFactory(require("devtools/client/shared/vendor/react-redux").Provider);
|
||||
const { bindActionCreators } = require("devtools/client/shared/vendor/redux");
|
||||
const { configureStore } = require("./src/utils/create-store");
|
||||
const store = window.gStore = configureStore();
|
||||
const store = configureStore();
|
||||
const actions = bindActionCreators(require("./src/actions/index"), store.dispatch);
|
||||
const { NetMonitorController } = require("./src/netmonitor-controller");
|
||||
const { onFirefoxConnect, onDisconnect } = require("./src/connector/index");
|
||||
|
||||
// Inject EventEmitter into global window.
|
||||
EventEmitter.decorate(window);
|
||||
// Inject to global window for testing
|
||||
window.store = store;
|
||||
|
||||
window.Netmonitor = {
|
||||
bootstrap({ toolbox }) {
|
||||
this.mount = document.querySelector("#mount");
|
||||
const App = createFactory(require("./src/components/app"));
|
||||
render(Provider({ store }, App()), this.mount);
|
||||
return NetMonitorController.startupNetMonitor({
|
||||
const connection = {
|
||||
tabConnection: {
|
||||
tabTarget: toolbox.target,
|
||||
},
|
||||
toolbox,
|
||||
}, actions);
|
||||
};
|
||||
const App = createFactory(require("./src/components/app"));
|
||||
render(Provider({ store }, App()), this.mount);
|
||||
return onFirefoxConnect(connection, actions, store.getState);
|
||||
},
|
||||
|
||||
destroy() {
|
||||
unmountComponentAtNode(this.mount);
|
||||
return NetMonitorController.shutdownNetMonitor();
|
||||
return onDisconnect();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -39,9 +39,12 @@ pref("devtools.netmonitor.har.enableAutoExportToFile", false);
|
|||
pref("devtools.webconsole.persistlog", false);
|
||||
|
||||
const App = require("./src/components/app");
|
||||
const store = window.gStore = configureStore();
|
||||
const store = configureStore();
|
||||
const actions = bindActionCreators(require("./src/actions"), store.dispatch);
|
||||
const { NetMonitorController } = require("./src/netmonitor-controller");
|
||||
const { onConnect } = require("./src/connector");
|
||||
|
||||
// Inject to global window for testing
|
||||
window.store = store;
|
||||
|
||||
/**
|
||||
* Stylesheet links in devtools xhtml files are using chrome or resource URLs.
|
||||
|
@ -67,10 +70,10 @@ window.addEventListener("DOMContentLoaded", () => {
|
|||
}
|
||||
});
|
||||
|
||||
bootstrap(React, ReactDOM).then(connection => {
|
||||
bootstrap(React, ReactDOM).then((connection) => {
|
||||
if (!connection) {
|
||||
return;
|
||||
}
|
||||
renderRoot(React, ReactDOM, App, store);
|
||||
NetMonitorController.startupNetMonitor(connection, actions);
|
||||
onConnect(connection, actions, store.getState);
|
||||
});
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const { sendHTTPRequest } = require("../connector/index");
|
||||
const {
|
||||
ADD_REQUEST,
|
||||
CLEAR_REQUESTS,
|
||||
|
@ -12,7 +13,6 @@ const {
|
|||
SEND_CUSTOM_REQUEST,
|
||||
UPDATE_REQUEST,
|
||||
} = require("../constants");
|
||||
const { NetMonitorController } = require("../netmonitor-controller");
|
||||
const { getSelectedRequest } = require("../selectors/index");
|
||||
|
||||
function addRequest(id, data, batch) {
|
||||
|
@ -47,10 +47,6 @@ function cloneSelectedRequest() {
|
|||
* Send a new HTTP request using the data in the custom request form.
|
||||
*/
|
||||
function sendCustomRequest() {
|
||||
if (!NetMonitorController.supportsCustomRequest) {
|
||||
return cloneSelectedRequest();
|
||||
}
|
||||
|
||||
return (dispatch, getState) => {
|
||||
const selected = getSelectedRequest(getState());
|
||||
|
||||
|
@ -71,7 +67,7 @@ function sendCustomRequest() {
|
|||
data.body = selected.requestPostData.postData.text;
|
||||
}
|
||||
|
||||
NetMonitorController.webConsoleClient.sendHTTPRequest(data, (response) => {
|
||||
sendHTTPRequest(data, (response) => {
|
||||
return dispatch({
|
||||
type: SEND_CUSTOM_REQUEST,
|
||||
id: response.eventActor.actor,
|
||||
|
|
|
@ -13,7 +13,7 @@ const {
|
|||
TOGGLE_COLUMN,
|
||||
WATERFALL_RESIZE,
|
||||
} = require("../constants");
|
||||
const { NetMonitorController } = require("../netmonitor-controller");
|
||||
const { triggerActivity } = require("../connector/index");
|
||||
|
||||
/**
|
||||
* Change network details panel.
|
||||
|
@ -34,7 +34,7 @@ function openNetworkDetails(open) {
|
|||
*/
|
||||
function openStatistics(open) {
|
||||
if (open) {
|
||||
NetMonitorController.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED);
|
||||
triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED);
|
||||
}
|
||||
return {
|
||||
type: OPEN_STATISTICS,
|
||||
|
|
|
@ -10,7 +10,6 @@ const {
|
|||
DOM,
|
||||
PropTypes,
|
||||
} = require("devtools/client/shared/vendor/react");
|
||||
const { NetMonitorController } = require("../netmonitor-controller");
|
||||
const {
|
||||
getFormattedIPAndPort,
|
||||
getFormattedSize,
|
||||
|
@ -201,7 +200,7 @@ const HeadersPanel = createClass({
|
|||
statusCodeDocURL ? MDNLink({
|
||||
url: statusCodeDocURL,
|
||||
}) : null,
|
||||
NetMonitorController.supportsCustomRequest && button({
|
||||
button({
|
||||
className: "devtools-button",
|
||||
onClick: cloneSelectedRequest,
|
||||
}, EDIT_AND_RESEND),
|
||||
|
|
|
@ -14,7 +14,7 @@ const {
|
|||
const { connect } = require("devtools/client/shared/vendor/react-redux");
|
||||
const { findDOMNode } = require("devtools/client/shared/vendor/react-dom");
|
||||
const Actions = require("../actions/index");
|
||||
const { getLongString } = require("../utils/client");
|
||||
const { getLongString } = require("../connector/index");
|
||||
const { getFormDataSections } = require("../utils/request-utils");
|
||||
const { getSelectedRequest } = require("../selectors/index");
|
||||
|
||||
|
|
|
@ -12,8 +12,8 @@ const {
|
|||
} = require("devtools/client/shared/vendor/react");
|
||||
const { connect } = require("devtools/client/shared/vendor/react-redux");
|
||||
const Actions = require("../actions/index");
|
||||
const { triggerActivity } = require("../connector/index");
|
||||
const { ACTIVITY_TYPE } = require("../constants");
|
||||
const { NetMonitorController } = require("../netmonitor-controller");
|
||||
const { L10N } = require("../utils/l10n");
|
||||
const { getPerformanceAnalysisURL } = require("../utils/mdn-utils");
|
||||
|
||||
|
@ -70,8 +70,6 @@ module.exports = connect(
|
|||
undefined,
|
||||
dispatch => ({
|
||||
onPerfClick: () => dispatch(Actions.openStatistics(true)),
|
||||
onReloadClick: () =>
|
||||
NetMonitorController
|
||||
.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_DEFAULT),
|
||||
onReloadClick: () => triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_DEFAULT),
|
||||
})
|
||||
)(RequestListEmptyNotice);
|
||||
|
|
|
@ -9,6 +9,7 @@ const {
|
|||
DOM,
|
||||
PropTypes,
|
||||
} = require("devtools/client/shared/vendor/react");
|
||||
const { viewSourceInDebugger } = require("../connector/index");
|
||||
|
||||
const { div } = DOM;
|
||||
|
||||
|
@ -22,9 +23,7 @@ function StackTracePanel({ request }) {
|
|||
div({ className: "panel-container" },
|
||||
StackTrace({
|
||||
stacktrace,
|
||||
onViewSourceInDebugger: (frame) => {
|
||||
window.NetMonitorController.viewSourceInDebugger(frame.url, frame.line);
|
||||
},
|
||||
onViewSourceInDebugger: ({ url, line }) => viewSourceInDebugger(url, line),
|
||||
}),
|
||||
)
|
||||
);
|
||||
|
|
|
@ -0,0 +1,724 @@
|
|||
/* 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 Services = require("Services");
|
||||
const { CurlUtils } = require("devtools/client/shared/curl");
|
||||
const { TimelineFront } = require("devtools/shared/fronts/timeline");
|
||||
const { ACTIVITY_TYPE, EVENTS } = require("../constants");
|
||||
const { getDisplayedRequestById } = require("../selectors/index");
|
||||
const { fetchHeaders, formDataURI } = require("../utils/request-utils");
|
||||
|
||||
class FirefoxConnector {
|
||||
constructor() {
|
||||
this.connect = this.connect.bind(this);
|
||||
this.disconnect = this.disconnect.bind(this);
|
||||
this.willNavigate = this.willNavigate.bind(this);
|
||||
this.displayCachedEvents = this.displayCachedEvents.bind(this);
|
||||
this.onDocLoadingMarker = this.onDocLoadingMarker.bind(this);
|
||||
this.addRequest = this.addRequest.bind(this);
|
||||
this.updateRequest = this.updateRequest.bind(this);
|
||||
this.fetchImage = this.fetchImage.bind(this);
|
||||
this.fetchRequestHeaders = this.fetchRequestHeaders.bind(this);
|
||||
this.fetchResponseHeaders = this.fetchResponseHeaders.bind(this);
|
||||
this.fetchPostData = this.fetchPostData.bind(this);
|
||||
this.fetchResponseCookies = this.fetchResponseCookies.bind(this);
|
||||
this.fetchRequestCookies = this.fetchRequestCookies.bind(this);
|
||||
this.getPayloadFromQueue = this.getPayloadFromQueue.bind(this);
|
||||
this.isQueuePayloadReady = this.isQueuePayloadReady.bind(this);
|
||||
this.pushPayloadToQueue = this.pushPayloadToQueue.bind(this);
|
||||
this.sendHTTPRequest = this.sendHTTPRequest.bind(this);
|
||||
this.setPreferences = this.setPreferences.bind(this);
|
||||
this.triggerActivity = this.triggerActivity.bind(this);
|
||||
this.inspectRequest = this.inspectRequest.bind(this);
|
||||
this.getLongString = this.getLongString.bind(this);
|
||||
this.getNetworkRequest = this.getNetworkRequest.bind(this);
|
||||
this.getTabTarget = this.getTabTarget.bind(this);
|
||||
this.viewSourceInDebugger = this.viewSourceInDebugger.bind(this);
|
||||
|
||||
// Event handlers
|
||||
this.onNetworkEvent = this.onNetworkEvent.bind(this);
|
||||
this.onNetworkEventUpdate = this.onNetworkEventUpdate.bind(this);
|
||||
this.onRequestHeaders = this.onRequestHeaders.bind(this);
|
||||
this.onRequestCookies = this.onRequestCookies.bind(this);
|
||||
this.onRequestPostData = this.onRequestPostData.bind(this);
|
||||
this.onSecurityInfo = this.onSecurityInfo.bind(this);
|
||||
this.onResponseHeaders = this.onResponseHeaders.bind(this);
|
||||
this.onResponseCookies = this.onResponseCookies.bind(this);
|
||||
this.onResponseContent = this.onResponseContent.bind(this);
|
||||
this.onEventTimings = this.onEventTimings.bind(this);
|
||||
}
|
||||
|
||||
async connect(connection, actions, getState) {
|
||||
this.actions = actions;
|
||||
this.getState = getState;
|
||||
this.tabTarget = connection.tabConnection.tabTarget;
|
||||
this.tabClient = this.tabTarget.isTabActor ? this.tabTarget.activeTab : null;
|
||||
this.webConsoleClient = this.tabTarget.activeConsole;
|
||||
|
||||
this.tabTarget.on("will-navigate", this.willNavigate);
|
||||
this.tabTarget.on("close", this.disconnect);
|
||||
this.webConsoleClient.on("networkEvent", this.onNetworkEvent);
|
||||
this.webConsoleClient.on("networkEventUpdate", this.onNetworkEventUpdate);
|
||||
|
||||
// Don't start up waiting for timeline markers if the server isn't
|
||||
// recent enough to emit the markers we're interested in.
|
||||
if (this.tabTarget.getTrait("documentLoadingMarkers")) {
|
||||
this.timelineFront = new TimelineFront(this.tabTarget.client, this.tabTarget.form);
|
||||
this.timelineFront.on("doc-loading", this.onDocLoadingMarker);
|
||||
await this.timelineFront.start({ withDocLoadingEvents: true });
|
||||
}
|
||||
|
||||
this.displayCachedEvents();
|
||||
}
|
||||
|
||||
async disconnect() {
|
||||
// When debugging local or a remote instance, the connection is closed by
|
||||
// the RemoteTarget. The webconsole actor is stopped on disconnect.
|
||||
this.tabClient = null;
|
||||
this.webConsoleClient = null;
|
||||
|
||||
// The timeline front wasn't initialized and started if the server wasn't
|
||||
// recent enough to emit the markers we were interested in.
|
||||
if (this.tabTarget.getTrait("documentLoadingMarkers") && this.timelineFront) {
|
||||
this.timelineFront.off("doc-loading", this.onDocLoadingMarker);
|
||||
await this.timelineFront.destroy();
|
||||
this.timelineFront = null;
|
||||
}
|
||||
}
|
||||
|
||||
willNavigate() {
|
||||
if (!Services.prefs.getBoolPref("devtools.webconsole.persistlog")) {
|
||||
this.actions.batchReset();
|
||||
this.actions.clearRequests();
|
||||
} else {
|
||||
// If the log is persistent, just clear all accumulated timing markers.
|
||||
this.actions.clearTimingMarkers();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display any network events already in the cache.
|
||||
*/
|
||||
displayCachedEvents() {
|
||||
for (let networkInfo of this.webConsoleClient.getNetworkEvents()) {
|
||||
// First add the request to the timeline.
|
||||
this.onNetworkEvent("networkEvent", networkInfo);
|
||||
// Then replay any updates already received.
|
||||
for (let updateType of networkInfo.updates) {
|
||||
this.onNetworkEventUpdate("networkEventUpdate", {
|
||||
packet: { updateType },
|
||||
networkInfo,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The "DOMContentLoaded" and "Load" events sent by the timeline actor.
|
||||
*
|
||||
* @param {object} marker
|
||||
*/
|
||||
onDocLoadingMarker(marker) {
|
||||
window.emit(EVENTS.TIMELINE_EVENT, marker);
|
||||
this.actions.addTimingMarker(marker);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new network request to application state.
|
||||
*
|
||||
* @param {string} id request id
|
||||
* @param {object} data data payload will be added to application state
|
||||
*/
|
||||
addRequest(id, data) {
|
||||
let {
|
||||
method,
|
||||
url,
|
||||
isXHR,
|
||||
cause,
|
||||
startedDateTime,
|
||||
fromCache,
|
||||
fromServiceWorker,
|
||||
} = data;
|
||||
|
||||
this.actions.addRequest(
|
||||
id,
|
||||
{
|
||||
// Convert the received date/time string to a unix timestamp.
|
||||
startedMillis: Date.parse(startedDateTime),
|
||||
method,
|
||||
url,
|
||||
isXHR,
|
||||
cause,
|
||||
fromCache,
|
||||
fromServiceWorker,
|
||||
},
|
||||
true,
|
||||
)
|
||||
.then(() => window.emit(EVENTS.REQUEST_ADDED, id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a network request if it already exists in application state.
|
||||
*
|
||||
* @param {string} id request id
|
||||
* @param {object} data data payload will be updated to application state
|
||||
*/
|
||||
async updateRequest(id, data) {
|
||||
let {
|
||||
mimeType,
|
||||
responseContent,
|
||||
responseCookies,
|
||||
responseHeaders,
|
||||
requestCookies,
|
||||
requestHeaders,
|
||||
requestPostData,
|
||||
} = data;
|
||||
|
||||
// fetch request detail contents in parallel
|
||||
let [
|
||||
imageObj,
|
||||
requestHeadersObj,
|
||||
responseHeadersObj,
|
||||
postDataObj,
|
||||
requestCookiesObj,
|
||||
responseCookiesObj,
|
||||
] = await Promise.all([
|
||||
this.fetchImage(mimeType, responseContent),
|
||||
this.fetchRequestHeaders(requestHeaders),
|
||||
this.fetchResponseHeaders(responseHeaders),
|
||||
this.fetchPostData(requestPostData),
|
||||
this.fetchRequestCookies(requestCookies),
|
||||
this.fetchResponseCookies(responseCookies),
|
||||
]);
|
||||
|
||||
let payload = Object.assign({}, data,
|
||||
imageObj, requestHeadersObj, responseHeadersObj,
|
||||
postDataObj, requestCookiesObj, responseCookiesObj);
|
||||
await this.actions.updateRequest(id, payload, true);
|
||||
}
|
||||
|
||||
async fetchImage(mimeType, responseContent) {
|
||||
let payload = {};
|
||||
if (mimeType && responseContent && responseContent.content) {
|
||||
let { encoding, text } = responseContent.content;
|
||||
let response = await this.getLongString(text);
|
||||
|
||||
if (mimeType.includes("image/")) {
|
||||
payload.responseContentDataUri = formDataURI(mimeType, encoding, response);
|
||||
}
|
||||
|
||||
responseContent.content.text = response;
|
||||
payload.responseContent = responseContent;
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
|
||||
async fetchRequestHeaders(requestHeaders) {
|
||||
let payload = {};
|
||||
if (requestHeaders && requestHeaders.headers && requestHeaders.headers.length) {
|
||||
let headers = await fetchHeaders(requestHeaders, this.getLongString);
|
||||
if (headers) {
|
||||
payload.requestHeaders = headers;
|
||||
}
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
|
||||
async fetchResponseHeaders(responseHeaders) {
|
||||
let payload = {};
|
||||
if (responseHeaders && responseHeaders.headers && responseHeaders.headers.length) {
|
||||
let headers = await fetchHeaders(responseHeaders, this.getLongString);
|
||||
if (headers) {
|
||||
payload.responseHeaders = headers;
|
||||
}
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
|
||||
async fetchPostData(requestPostData) {
|
||||
let payload = {};
|
||||
if (requestPostData && requestPostData.postData) {
|
||||
let { text } = requestPostData.postData;
|
||||
let postData = await this.getLongString(text);
|
||||
const headers = CurlUtils.getHeadersFromMultipartText(postData);
|
||||
const headersSize = headers.reduce((acc, { name, value }) => {
|
||||
return acc + name.length + value.length + 2;
|
||||
}, 0);
|
||||
requestPostData.postData.text = postData;
|
||||
payload.requestPostData = Object.assign({}, requestPostData);
|
||||
payload.requestHeadersFromUploadStream = { headers, headersSize };
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
|
||||
async fetchResponseCookies(responseCookies) {
|
||||
let payload = {};
|
||||
if (responseCookies) {
|
||||
let resCookies = [];
|
||||
// response store cookies in responseCookies or responseCookies.cookies
|
||||
let cookies = responseCookies.cookies ?
|
||||
responseCookies.cookies : responseCookies;
|
||||
// make sure cookies is iterable
|
||||
if (typeof cookies[Symbol.iterator] === "function") {
|
||||
for (let cookie of cookies) {
|
||||
resCookies.push(Object.assign({}, cookie, {
|
||||
value: await this.getLongString(cookie.value),
|
||||
}));
|
||||
}
|
||||
if (resCookies.length) {
|
||||
payload.responseCookies = resCookies;
|
||||
}
|
||||
}
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
|
||||
async fetchRequestCookies(requestCookies) {
|
||||
let payload = {};
|
||||
if (requestCookies) {
|
||||
let reqCookies = [];
|
||||
// request store cookies in requestCookies or requestCookies.cookies
|
||||
let cookies = requestCookies.cookies ?
|
||||
requestCookies.cookies : requestCookies;
|
||||
// make sure cookies is iterable
|
||||
if (typeof cookies[Symbol.iterator] === "function") {
|
||||
for (let cookie of cookies) {
|
||||
reqCookies.push(Object.assign({}, cookie, {
|
||||
value: await this.getLongString(cookie.value),
|
||||
}));
|
||||
}
|
||||
if (reqCookies.length) {
|
||||
payload.requestCookies = reqCookies;
|
||||
}
|
||||
}
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access a payload item from payload queue.
|
||||
*
|
||||
* @param {string} id request id
|
||||
* @return {boolean} return a queued payload item from queue.
|
||||
*/
|
||||
getPayloadFromQueue(id) {
|
||||
return this.payloadQueue.find((item) => item.id === id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Packet order of "networkUpdateEvent" is predictable, as a result we can wait for
|
||||
* the last one "eventTimings" packet arrives to check payload is ready.
|
||||
*
|
||||
* @param {string} id request id
|
||||
* @return {boolean} return whether a specific networkEvent has been updated completely.
|
||||
*/
|
||||
isQueuePayloadReady(id) {
|
||||
let queuedPayload = this.getPayloadFromQueue(id);
|
||||
return queuedPayload && queuedPayload.payload.eventTimings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Push a request payload into a queue if request doesn't exist. Otherwise update the
|
||||
* request itself.
|
||||
*
|
||||
* @param {string} id request id
|
||||
* @param {object} payload request data payload
|
||||
*/
|
||||
pushPayloadToQueue(id, payload) {
|
||||
let queuedPayload = this.getPayloadFromQueue(id);
|
||||
if (!queuedPayload) {
|
||||
this.payloadQueue.push({ id, payload });
|
||||
} else {
|
||||
// Merge upcoming networkEventUpdate payload into existing one
|
||||
queuedPayload.payload = Object.assign({}, queuedPayload.payload, payload);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a HTTP request data payload
|
||||
*
|
||||
* @param {object} data data payload would like to sent to backend
|
||||
* @param {function} callback callback will be invoked after the request finished
|
||||
*/
|
||||
sendHTTPRequest(data, callback) {
|
||||
this.webConsoleClient.sendHTTPRequest(data, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set network preferences to control network flow
|
||||
*
|
||||
* @param {object} request request payload would like to sent to backend
|
||||
* @param {function} callback callback will be invoked after the request finished
|
||||
*/
|
||||
setPreferences(request, callback) {
|
||||
this.webConsoleClient.setPreferences(request, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers a specific "activity" to be performed by the frontend.
|
||||
* This can be, for example, triggering reloads or enabling/disabling cache.
|
||||
*
|
||||
* @param {number} type The activity type. See the ACTIVITY_TYPE const.
|
||||
* @return {object} A promise resolved once the activity finishes and the frontend
|
||||
* is back into "standby" mode.
|
||||
*/
|
||||
triggerActivity(type) {
|
||||
// Puts the frontend into "standby" (when there's no particular activity).
|
||||
let standBy = () => {
|
||||
this.currentActivity = ACTIVITY_TYPE.NONE;
|
||||
};
|
||||
|
||||
// Waits for a series of "navigation start" and "navigation stop" events.
|
||||
let waitForNavigation = () => {
|
||||
return new Promise((resolve) => {
|
||||
this.tabTarget.once("will-navigate", () => {
|
||||
this.tabTarget.once("navigate", () => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Reconfigures the tab, optionally triggering a reload.
|
||||
let reconfigureTab = (options) => {
|
||||
return new Promise((resolve) => {
|
||||
this.tabTarget.activeTab.reconfigure(options, resolve);
|
||||
});
|
||||
};
|
||||
|
||||
// Reconfigures the tab and waits for the target to finish navigating.
|
||||
let reconfigureTabAndWaitForNavigation = (options) => {
|
||||
options.performReload = true;
|
||||
let navigationFinished = waitForNavigation();
|
||||
return reconfigureTab(options).then(() => navigationFinished);
|
||||
};
|
||||
switch (type) {
|
||||
case ACTIVITY_TYPE.RELOAD.WITH_CACHE_DEFAULT:
|
||||
return reconfigureTabAndWaitForNavigation({}).then(standBy);
|
||||
case ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED:
|
||||
this.currentActivity = ACTIVITY_TYPE.ENABLE_CACHE;
|
||||
this.tabTarget.once("will-navigate", () => {
|
||||
this.currentActivity = type;
|
||||
});
|
||||
return reconfigureTabAndWaitForNavigation({
|
||||
cacheDisabled: false,
|
||||
performReload: true,
|
||||
}).then(standBy);
|
||||
case ACTIVITY_TYPE.RELOAD.WITH_CACHE_DISABLED:
|
||||
this.currentActivity = ACTIVITY_TYPE.DISABLE_CACHE;
|
||||
this.tabTarget.once("will-navigate", () => {
|
||||
this.currentActivity = type;
|
||||
});
|
||||
return reconfigureTabAndWaitForNavigation({
|
||||
cacheDisabled: true,
|
||||
performReload: true,
|
||||
}).then(standBy);
|
||||
case ACTIVITY_TYPE.ENABLE_CACHE:
|
||||
this.currentActivity = type;
|
||||
return reconfigureTab({
|
||||
cacheDisabled: false,
|
||||
performReload: false,
|
||||
}).then(standBy);
|
||||
case ACTIVITY_TYPE.DISABLE_CACHE:
|
||||
this.currentActivity = type;
|
||||
return reconfigureTab({
|
||||
cacheDisabled: true,
|
||||
performReload: false,
|
||||
}).then(standBy);
|
||||
}
|
||||
this.currentActivity = ACTIVITY_TYPE.NONE;
|
||||
return Promise.reject(new Error("Invalid activity type"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the specified request in the waterfall and opens the details view.
|
||||
*
|
||||
* @param {string} requestId The actor ID of the request to inspect.
|
||||
* @return {object} A promise resolved once the task finishes.
|
||||
*/
|
||||
inspectRequest(requestId) {
|
||||
// Look for the request in the existing ones or wait for it to appear, if
|
||||
// the network monitor is still loading.
|
||||
return new Promise((resolve) => {
|
||||
let request = null;
|
||||
let inspector = () => {
|
||||
request = getDisplayedRequestById(this.getState(), requestId);
|
||||
if (!request) {
|
||||
// Reset filters so that the request is visible.
|
||||
this.actions.toggleRequestFilterType("all");
|
||||
request = getDisplayedRequestById(this.getState(), requestId);
|
||||
}
|
||||
|
||||
// If the request was found, select it. Otherwise this function will be
|
||||
// called again once new requests arrive.
|
||||
if (request) {
|
||||
window.off(EVENTS.REQUEST_ADDED, inspector);
|
||||
this.actions.selectRequest(request.id);
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
|
||||
inspector();
|
||||
|
||||
if (!request) {
|
||||
window.on(EVENTS.REQUEST_ADDED, inspector);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the network information packet from actor server
|
||||
*
|
||||
* @param {string} id request id
|
||||
* @return {object} networkInfo data packet
|
||||
*/
|
||||
getNetworkRequest(id) {
|
||||
return this.webConsoleClient.getNetworkRequest(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the full text of a LongString.
|
||||
*
|
||||
* @param {object|string} stringGrip
|
||||
* The long string grip containing the corresponding actor.
|
||||
* If you pass in a plain string (by accident or because you're lazy),
|
||||
* then a promise of the same string is simply returned.
|
||||
* @return {object}
|
||||
* A promise that is resolved when the full string contents
|
||||
* are available, or rejected if something goes wrong.
|
||||
*/
|
||||
getLongString(stringGrip) {
|
||||
return this.webConsoleClient.getString(stringGrip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter that access tab target instance.
|
||||
* @return {object} browser tab target instance
|
||||
*/
|
||||
getTabTarget() {
|
||||
return this.tabTarget;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a given source in Debugger
|
||||
* @param {string} sourceURL source url
|
||||
* @param {number} sourceLine source line number
|
||||
*/
|
||||
viewSourceInDebugger(sourceURL, sourceLine) {
|
||||
if (this.toolbox) {
|
||||
this.toolbox.viewSourceInDebugger(sourceURL, sourceLine);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The "networkEvent" message type handler.
|
||||
*
|
||||
* @param {string} type message type
|
||||
* @param {object} networkInfo network request information
|
||||
*/
|
||||
onNetworkEvent(type, networkInfo) {
|
||||
let {
|
||||
actor,
|
||||
cause,
|
||||
fromCache,
|
||||
fromServiceWorker,
|
||||
isXHR,
|
||||
request: {
|
||||
method,
|
||||
url,
|
||||
},
|
||||
startedDateTime,
|
||||
} = networkInfo;
|
||||
|
||||
this.addRequest(actor, {
|
||||
cause,
|
||||
fromCache,
|
||||
fromServiceWorker,
|
||||
isXHR,
|
||||
method,
|
||||
startedDateTime,
|
||||
url,
|
||||
});
|
||||
|
||||
window.emit(EVENTS.NETWORK_EVENT, actor);
|
||||
}
|
||||
|
||||
/**
|
||||
* The "networkEventUpdate" message type handler.
|
||||
*
|
||||
* @param {string} type message type
|
||||
* @param {object} packet the message received from the server.
|
||||
* @param {object} networkInfo the network request information.
|
||||
*/
|
||||
onNetworkEventUpdate(type, { packet, networkInfo }) {
|
||||
let { actor } = networkInfo;
|
||||
|
||||
switch (packet.updateType) {
|
||||
case "requestHeaders":
|
||||
this.webConsoleClient.getRequestHeaders(actor, this.onRequestHeaders);
|
||||
window.emit(EVENTS.UPDATING_REQUEST_HEADERS, actor);
|
||||
break;
|
||||
case "requestCookies":
|
||||
this.webConsoleClient.getRequestCookies(actor, this.onRequestCookies);
|
||||
window.emit(EVENTS.UPDATING_REQUEST_COOKIES, actor);
|
||||
break;
|
||||
case "requestPostData":
|
||||
this.webConsoleClient.getRequestPostData(actor, this.onRequestPostData);
|
||||
window.emit(EVENTS.UPDATING_REQUEST_POST_DATA, actor);
|
||||
break;
|
||||
case "securityInfo":
|
||||
this.updateRequest(actor, {
|
||||
securityState: networkInfo.securityInfo,
|
||||
}).then(() => {
|
||||
this.webConsoleClient.getSecurityInfo(actor, this.onSecurityInfo);
|
||||
window.emit(EVENTS.UPDATING_SECURITY_INFO, actor);
|
||||
});
|
||||
break;
|
||||
case "responseHeaders":
|
||||
this.webConsoleClient.getResponseHeaders(actor, this.onResponseHeaders);
|
||||
window.emit(EVENTS.UPDATING_RESPONSE_HEADERS, actor);
|
||||
break;
|
||||
case "responseCookies":
|
||||
this.webConsoleClient.getResponseCookies(actor, this.onResponseCookies);
|
||||
window.emit(EVENTS.UPDATING_RESPONSE_COOKIES, actor);
|
||||
break;
|
||||
case "responseStart":
|
||||
this.updateRequest(actor, {
|
||||
httpVersion: networkInfo.response.httpVersion,
|
||||
remoteAddress: networkInfo.response.remoteAddress,
|
||||
remotePort: networkInfo.response.remotePort,
|
||||
status: networkInfo.response.status,
|
||||
statusText: networkInfo.response.statusText,
|
||||
headersSize: networkInfo.response.headersSize
|
||||
}).then(() => {
|
||||
window.emit(EVENTS.STARTED_RECEIVING_RESPONSE, actor);
|
||||
});
|
||||
break;
|
||||
case "responseContent":
|
||||
this.webConsoleClient.getResponseContent(actor,
|
||||
this.onResponseContent.bind(this, {
|
||||
contentSize: networkInfo.response.bodySize,
|
||||
transferredSize: networkInfo.response.transferredSize,
|
||||
mimeType: networkInfo.response.content.mimeType
|
||||
}));
|
||||
window.emit(EVENTS.UPDATING_RESPONSE_CONTENT, actor);
|
||||
break;
|
||||
case "eventTimings":
|
||||
this.updateRequest(actor, { totalTime: networkInfo.totalTime })
|
||||
.then(() => {
|
||||
this.webConsoleClient.getEventTimings(actor, this.onEventTimings);
|
||||
window.emit(EVENTS.UPDATING_EVENT_TIMINGS, actor);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles additional information received for a "requestHeaders" packet.
|
||||
*
|
||||
* @param {object} response the message received from the server.
|
||||
*/
|
||||
onRequestHeaders(response) {
|
||||
this.updateRequest(response.from, {
|
||||
requestHeaders: response
|
||||
}).then(() => {
|
||||
window.emit(EVENTS.RECEIVED_REQUEST_HEADERS, response.from);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles additional information received for a "requestCookies" packet.
|
||||
*
|
||||
* @param {object} response the message received from the server.
|
||||
*/
|
||||
onRequestCookies(response) {
|
||||
this.updateRequest(response.from, {
|
||||
requestCookies: response
|
||||
}).then(() => {
|
||||
window.emit(EVENTS.RECEIVED_REQUEST_COOKIES, response.from);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles additional information received for a "requestPostData" packet.
|
||||
*
|
||||
* @param {object} response the message received from the server.
|
||||
*/
|
||||
onRequestPostData(response) {
|
||||
this.updateRequest(response.from, {
|
||||
requestPostData: response
|
||||
}).then(() => {
|
||||
window.emit(EVENTS.RECEIVED_REQUEST_POST_DATA, response.from);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles additional information received for a "securityInfo" packet.
|
||||
*
|
||||
* @param {object} response the message received from the server.
|
||||
*/
|
||||
onSecurityInfo(response) {
|
||||
this.updateRequest(response.from, {
|
||||
securityInfo: response.securityInfo
|
||||
}).then(() => {
|
||||
window.emit(EVENTS.RECEIVED_SECURITY_INFO, response.from);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles additional information received for a "responseHeaders" packet.
|
||||
*
|
||||
* @param {object} response the message received from the server.
|
||||
*/
|
||||
onResponseHeaders(response) {
|
||||
this.updateRequest(response.from, {
|
||||
responseHeaders: response
|
||||
}).then(() => {
|
||||
window.emit(EVENTS.RECEIVED_RESPONSE_HEADERS, response.from);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles additional information received for a "responseCookies" packet.
|
||||
*
|
||||
* @param {object} response the message received from the server.
|
||||
*/
|
||||
onResponseCookies(response) {
|
||||
this.updateRequest(response.from, {
|
||||
responseCookies: response
|
||||
}).then(() => {
|
||||
window.emit(EVENTS.RECEIVED_RESPONSE_COOKIES, response.from);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles additional information received for a "responseContent" packet.
|
||||
*
|
||||
* @param {object} data the message received from the server event.
|
||||
* @param {object} response the message received from the server.
|
||||
*/
|
||||
onResponseContent(data, response) {
|
||||
let payload = Object.assign({ responseContent: response }, data);
|
||||
this.updateRequest(response.from, payload).then(() => {
|
||||
window.emit(EVENTS.RECEIVED_RESPONSE_CONTENT, response.from);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles additional information received for a "eventTimings" packet.
|
||||
*
|
||||
* @param {object} response the message received from the server.
|
||||
*/
|
||||
onEventTimings(response) {
|
||||
this.updateRequest(response.from, {
|
||||
eventTimings: response
|
||||
}).then(() => {
|
||||
window.emit(EVENTS.RECEIVED_EVENT_TIMINGS, response.from);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new FirefoxConnector();
|
|
@ -0,0 +1,85 @@
|
|||
/* 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";
|
||||
|
||||
let connector = {};
|
||||
|
||||
function onConnect(connection, actions, getState) {
|
||||
if (!connection || !connection.tab) {
|
||||
return;
|
||||
}
|
||||
|
||||
let { clientType } = connection.tab;
|
||||
switch (clientType) {
|
||||
case "chrome":
|
||||
onChromeConnect(connection, actions, getState);
|
||||
break;
|
||||
case "firefox":
|
||||
onFirefoxConnect(connection, actions, getState);
|
||||
break;
|
||||
default:
|
||||
throw Error(`Unknown client type - ${clientType}`);
|
||||
}
|
||||
}
|
||||
|
||||
function onDisconnect() {
|
||||
connector && connector.disconnect();
|
||||
}
|
||||
|
||||
function onChromeConnect(connection, actions, getState) {
|
||||
// TODO: support chrome debugging protocol
|
||||
}
|
||||
|
||||
function onFirefoxConnect(connection, actions, getState) {
|
||||
connector = require("./firefox-connector");
|
||||
connector.connect(connection, actions, getState);
|
||||
}
|
||||
|
||||
function inspectRequest() {
|
||||
return connector.inspectRequest(...arguments);
|
||||
}
|
||||
|
||||
function getLongString() {
|
||||
return connector.getLongString(...arguments);
|
||||
}
|
||||
|
||||
function getNetworkRequest() {
|
||||
return connector.getNetworkRequest(...arguments);
|
||||
}
|
||||
|
||||
function getTabTarget() {
|
||||
return connector.getTabTarget();
|
||||
}
|
||||
|
||||
function sendHTTPRequest() {
|
||||
return connector.sendHTTPRequest(...arguments);
|
||||
}
|
||||
|
||||
function setPreferences() {
|
||||
return connector.setPreferences(...arguments);
|
||||
}
|
||||
|
||||
function triggerActivity() {
|
||||
return connector.triggerActivity(...arguments);
|
||||
}
|
||||
|
||||
function viewSourceInDebugger() {
|
||||
return connector.viewSourceInDebugger();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
onConnect,
|
||||
onChromeConnect,
|
||||
onFirefoxConnect,
|
||||
onDisconnect,
|
||||
getLongString,
|
||||
getNetworkRequest,
|
||||
getTabTarget,
|
||||
inspectRequest,
|
||||
sendHTTPRequest,
|
||||
setPreferences,
|
||||
triggerActivity,
|
||||
viewSourceInDebugger,
|
||||
};
|
|
@ -0,0 +1,8 @@
|
|||
# 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/.
|
||||
|
||||
DevToolsModules(
|
||||
'firefox-connector.js',
|
||||
'index.js',
|
||||
)
|
|
@ -47,10 +47,6 @@ const ACTIVITY_TYPE = {
|
|||
|
||||
// The panel's window global is an EventEmitter firing the following events:
|
||||
const EVENTS = {
|
||||
// When the monitored target begins and finishes navigating.
|
||||
TARGET_WILL_NAVIGATE: "NetMonitor:TargetWillNavigate",
|
||||
TARGET_DID_NAVIGATE: "NetMonitor:TargetNavigate",
|
||||
|
||||
// When a network or timeline event is received.
|
||||
// See https://developer.mozilla.org/docs/Tools/Web_Console/remoting for
|
||||
// more information about what each packet is supposed to deliver.
|
||||
|
@ -93,8 +89,7 @@ const EVENTS = {
|
|||
UPDATING_RESPONSE_CONTENT: "NetMonitor:NetworkEventUpdating:ResponseContent",
|
||||
RECEIVED_RESPONSE_CONTENT: "NetMonitor:NetworkEventUpdated:ResponseContent",
|
||||
|
||||
// Fired once the NetMonitorController establishes a connection to the debug
|
||||
// target.
|
||||
// Fired once the connection is established
|
||||
CONNECTED: "connected",
|
||||
};
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ const Services = require("Services");
|
|||
const appInfo = Services.appinfo;
|
||||
const { LocalizationHelper } = require("devtools/shared/l10n");
|
||||
const { CurlUtils } = require("devtools/client/shared/curl");
|
||||
const { getLongString } = require("../utils/client");
|
||||
const { getLongString } = require("../connector/index");
|
||||
const {
|
||||
getFormDataSections,
|
||||
getUrlQuery,
|
||||
|
|
|
@ -11,12 +11,11 @@ add_task(function* () {
|
|||
|
||||
info("Starting test... ");
|
||||
|
||||
let { gStore, windowRequire } = monitor.panelWin;
|
||||
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
|
||||
let { actions, windowRequire } = monitor.panelWin;
|
||||
let RequestListContextMenu = windowRequire(
|
||||
"devtools/client/netmonitor/src/request-list-context-menu");
|
||||
|
||||
gStore.dispatch(Actions.batchEnable(false));
|
||||
actions.batchEnable(false);
|
||||
|
||||
let wait = waitForNetworkEvents(monitor, 1);
|
||||
tab.linkedBrowser.reload();
|
||||
|
|
|
@ -12,12 +12,11 @@ add_task(function* () {
|
|||
|
||||
info("Starting test... ");
|
||||
|
||||
let { gStore, windowRequire } = monitor.panelWin;
|
||||
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
|
||||
let { actions, windowRequire } = monitor.panelWin;
|
||||
let RequestListContextMenu = windowRequire(
|
||||
"devtools/client/netmonitor/src/request-list-context-menu");
|
||||
|
||||
gStore.dispatch(Actions.batchEnable(false));
|
||||
actions.batchEnable(false);
|
||||
|
||||
// Execute one POST request on the page and wait till its done.
|
||||
let wait = waitForNetworkEvents(monitor, 0, 1);
|
||||
|
|
|
@ -16,14 +16,13 @@ function* throttleUploadTest(actuallyThrottle) {
|
|||
|
||||
info("Starting test... (actuallyThrottle = " + actuallyThrottle + ")");
|
||||
|
||||
let { gStore, windowRequire } = monitor.panelWin;
|
||||
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
|
||||
let { NetMonitorController } =
|
||||
windowRequire("devtools/client/netmonitor/src/netmonitor-controller");
|
||||
let { actions, windowRequire } = monitor.panelWin;
|
||||
let { setPreferences } =
|
||||
windowRequire("devtools/client/netmonitor/src/connector/index");
|
||||
let RequestListContextMenu = windowRequire(
|
||||
"devtools/client/netmonitor/src/request-list-context-menu");
|
||||
|
||||
gStore.dispatch(Actions.batchEnable(false));
|
||||
actions.batchEnable(false);
|
||||
|
||||
const size = 4096;
|
||||
const uploadSize = actuallyThrottle ? size / 3 : 0;
|
||||
|
@ -38,11 +37,10 @@ function* throttleUploadTest(actuallyThrottle) {
|
|||
uploadBPSMax: uploadSize,
|
||||
},
|
||||
};
|
||||
let client = NetMonitorController.webConsoleClient;
|
||||
|
||||
info("sending throttle request");
|
||||
yield new Promise((resolve) => {
|
||||
client.setPreferences(request, response => {
|
||||
setPreferences(request, (response) => {
|
||||
resolve(response);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
DIRS += [
|
||||
'actions',
|
||||
'components',
|
||||
'connector',
|
||||
'har',
|
||||
'middleware',
|
||||
'reducers',
|
||||
|
@ -14,7 +15,6 @@ DIRS += [
|
|||
|
||||
DevToolsModules(
|
||||
'constants.js',
|
||||
'netmonitor-controller.js',
|
||||
'request-list-context-menu.js',
|
||||
'request-list-header-context-menu.js',
|
||||
'request-list-tooltip.js',
|
||||
|
|
|
@ -1,781 +0,0 @@
|
|||
/* 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 { TimelineFront } = require("devtools/shared/fronts/timeline");
|
||||
const { CurlUtils } = require("devtools/client/shared/curl");
|
||||
const { ACTIVITY_TYPE, EVENTS } = require("./constants");
|
||||
const { getDisplayedRequestById } = require("./selectors/index");
|
||||
const {
|
||||
fetchHeaders,
|
||||
formDataURI,
|
||||
} = require("./utils/request-utils");
|
||||
const {
|
||||
getLongString,
|
||||
getWebConsoleClient,
|
||||
onFirefoxConnect,
|
||||
onFirefoxDisconnect,
|
||||
} = require("./utils/client");
|
||||
|
||||
/**
|
||||
* Object defining the network monitor controller components.
|
||||
*/
|
||||
var NetMonitorController = {
|
||||
/**
|
||||
* Initializes the view and connects the monitor client.
|
||||
*
|
||||
* @param {Object} connection connection data wrapper
|
||||
* @return {Object} A promise that is resolved when the monitor finishes startup.
|
||||
*/
|
||||
startupNetMonitor(connection, actions) {
|
||||
if (this._startup) {
|
||||
return this._startup;
|
||||
}
|
||||
this.actions = actions;
|
||||
this._startup = new Promise(async (resolve) => {
|
||||
await this.connect(connection);
|
||||
resolve();
|
||||
});
|
||||
return this._startup;
|
||||
},
|
||||
|
||||
/**
|
||||
* Destroys the view and disconnects the monitor client from the server.
|
||||
*
|
||||
* @return object
|
||||
* A promise that is resolved when the monitor finishes shutdown.
|
||||
*/
|
||||
shutdownNetMonitor() {
|
||||
if (this._shutdown) {
|
||||
return this._shutdown;
|
||||
}
|
||||
this._shutdown = new Promise(async (resolve) => {
|
||||
this.actions.batchReset();
|
||||
onFirefoxDisconnect(this._target);
|
||||
this._target.off("close", this._onTabDetached);
|
||||
this.NetworkEventsHandler.disconnect();
|
||||
await this.disconnect();
|
||||
resolve();
|
||||
});
|
||||
|
||||
return this._shutdown;
|
||||
},
|
||||
|
||||
/**
|
||||
* Initiates remote or chrome network monitoring based on the current target,
|
||||
* wiring event handlers as necessary. Since the TabTarget will have already
|
||||
* started listening to network requests by now, this is largely
|
||||
* netmonitor-specific initialization.
|
||||
*
|
||||
* @param {Object} connection connection data wrapper
|
||||
* @return {Object} A promise that is resolved when the monitor finishes connecting.
|
||||
*/
|
||||
connect(connection) {
|
||||
if (this._connection) {
|
||||
return this._connection;
|
||||
}
|
||||
this._onTabDetached = this.shutdownNetMonitor.bind(this);
|
||||
|
||||
this._connection = new Promise(async (resolve) => {
|
||||
// Some actors like AddonActor or RootActor for chrome debugging
|
||||
// aren't actual tabs.
|
||||
this.toolbox = connection.toolbox;
|
||||
this._target = connection.tabConnection.tabTarget;
|
||||
this.tabClient = this._target.isTabActor ? this._target.activeTab : null;
|
||||
|
||||
let connectTimeline = () => {
|
||||
// Don't start up waiting for timeline markers if the server isn't
|
||||
// recent enough to emit the markers we're interested in.
|
||||
if (this._target.getTrait("documentLoadingMarkers")) {
|
||||
this.timelineFront = new TimelineFront(this._target.client,
|
||||
this._target.form);
|
||||
return this.timelineFront.start({ withDocLoadingEvents: true });
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
await connectTimeline();
|
||||
|
||||
onFirefoxConnect(this._target);
|
||||
this._target.on("close", this._onTabDetached);
|
||||
|
||||
this.webConsoleClient = getWebConsoleClient();
|
||||
this.NetworkEventsHandler = new NetworkEventsHandler();
|
||||
this.NetworkEventsHandler.connect(this.actions);
|
||||
|
||||
window.emit(EVENTS.CONNECTED);
|
||||
|
||||
resolve();
|
||||
this._connected = true;
|
||||
});
|
||||
return this._connection;
|
||||
},
|
||||
|
||||
/**
|
||||
* Disconnects the debugger client and removes event handlers as necessary.
|
||||
*/
|
||||
disconnect() {
|
||||
if (this._disconnection) {
|
||||
return this._disconnection;
|
||||
}
|
||||
this._disconnection = new Promise(async (resolve) => {
|
||||
// Wait for the connection to finish first.
|
||||
if (!this._connected) {
|
||||
await this._connection;
|
||||
}
|
||||
|
||||
// When debugging local or a remote instance, the connection is closed by
|
||||
// the RemoteTarget. The webconsole actor is stopped on disconnect.
|
||||
this.tabClient = null;
|
||||
this.webConsoleClient = null;
|
||||
|
||||
// The timeline front wasn't initialized and started if the server wasn't
|
||||
// recent enough to emit the markers we were interested in.
|
||||
if (this._target.getTrait("documentLoadingMarkers")) {
|
||||
await this.timelineFront.destroy();
|
||||
this.timelineFront = null;
|
||||
}
|
||||
|
||||
resolve();
|
||||
this._connected = false;
|
||||
});
|
||||
return this._disconnection;
|
||||
},
|
||||
|
||||
/**
|
||||
* Triggers a specific "activity" to be performed by the frontend.
|
||||
* This can be, for example, triggering reloads or enabling/disabling cache.
|
||||
*
|
||||
* @param number type
|
||||
* The activity type. See the ACTIVITY_TYPE const.
|
||||
* @return object
|
||||
* A promise resolved once the activity finishes and the frontend
|
||||
* is back into "standby" mode.
|
||||
*/
|
||||
triggerActivity: function (type) {
|
||||
// Puts the frontend into "standby" (when there's no particular activity).
|
||||
let standBy = () => {
|
||||
this._currentActivity = ACTIVITY_TYPE.NONE;
|
||||
};
|
||||
|
||||
// Waits for a series of "navigation start" and "navigation stop" events.
|
||||
let waitForNavigation = () => {
|
||||
return new Promise((resolve) => {
|
||||
this._target.once("will-navigate", () => {
|
||||
this._target.once("navigate", () => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Reconfigures the tab, optionally triggering a reload.
|
||||
let reconfigureTab = options => {
|
||||
return new Promise((resolve) => {
|
||||
this._target.activeTab.reconfigure(options, resolve);
|
||||
});
|
||||
};
|
||||
|
||||
// Reconfigures the tab and waits for the target to finish navigating.
|
||||
let reconfigureTabAndWaitForNavigation = options => {
|
||||
options.performReload = true;
|
||||
let navigationFinished = waitForNavigation();
|
||||
return reconfigureTab(options).then(() => navigationFinished);
|
||||
};
|
||||
if (type == ACTIVITY_TYPE.RELOAD.WITH_CACHE_DEFAULT) {
|
||||
return reconfigureTabAndWaitForNavigation({}).then(standBy);
|
||||
}
|
||||
if (type == ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED) {
|
||||
this._currentActivity = ACTIVITY_TYPE.ENABLE_CACHE;
|
||||
this._target.once("will-navigate", () => {
|
||||
this._currentActivity = type;
|
||||
});
|
||||
return reconfigureTabAndWaitForNavigation({
|
||||
cacheDisabled: false,
|
||||
performReload: true
|
||||
}).then(standBy);
|
||||
}
|
||||
if (type == ACTIVITY_TYPE.RELOAD.WITH_CACHE_DISABLED) {
|
||||
this._currentActivity = ACTIVITY_TYPE.DISABLE_CACHE;
|
||||
this._target.once("will-navigate", () => {
|
||||
this._currentActivity = type;
|
||||
});
|
||||
return reconfigureTabAndWaitForNavigation({
|
||||
cacheDisabled: true,
|
||||
performReload: true
|
||||
}).then(standBy);
|
||||
}
|
||||
if (type == ACTIVITY_TYPE.ENABLE_CACHE) {
|
||||
this._currentActivity = type;
|
||||
return reconfigureTab({
|
||||
cacheDisabled: false,
|
||||
performReload: false
|
||||
}).then(standBy);
|
||||
}
|
||||
if (type == ACTIVITY_TYPE.DISABLE_CACHE) {
|
||||
this._currentActivity = type;
|
||||
return reconfigureTab({
|
||||
cacheDisabled: true,
|
||||
performReload: false
|
||||
}).then(standBy);
|
||||
}
|
||||
this._currentActivity = ACTIVITY_TYPE.NONE;
|
||||
return Promise.reject(new Error("Invalid activity type"));
|
||||
},
|
||||
|
||||
/**
|
||||
* Selects the specified request in the waterfall and opens the details view.
|
||||
*
|
||||
* @param string requestId
|
||||
* The actor ID of the request to inspect.
|
||||
* @return object
|
||||
* A promise resolved once the task finishes.
|
||||
*/
|
||||
inspectRequest(requestId) {
|
||||
// Look for the request in the existing ones or wait for it to appear, if
|
||||
// the network monitor is still loading.
|
||||
return new Promise((resolve) => {
|
||||
let request = null;
|
||||
let inspector = () => {
|
||||
request = getDisplayedRequestById(window.gStore.getState(), requestId);
|
||||
if (!request) {
|
||||
// Reset filters so that the request is visible.
|
||||
this.actions.toggleRequestFilterType("all");
|
||||
request = getDisplayedRequestById(window.gStore.getState(), requestId);
|
||||
}
|
||||
|
||||
// If the request was found, select it. Otherwise this function will be
|
||||
// called again once new requests arrive.
|
||||
if (request) {
|
||||
window.off(EVENTS.REQUEST_ADDED, inspector);
|
||||
this.actions.selectRequest(request.id);
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
|
||||
inspector();
|
||||
if (!request) {
|
||||
window.on(EVENTS.REQUEST_ADDED, inspector);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Getter that tells if the server supports sending custom network requests.
|
||||
* @type boolean
|
||||
*/
|
||||
get supportsCustomRequest() {
|
||||
return this.webConsoleClient &&
|
||||
(this.webConsoleClient.traits.customNetworkRequest ||
|
||||
!this._target.isApp);
|
||||
},
|
||||
|
||||
/**
|
||||
* Getter that tells if the server can do network performance statistics.
|
||||
* @type boolean
|
||||
*/
|
||||
get supportsPerfStats() {
|
||||
return this.tabClient &&
|
||||
(this.tabClient.traits.reconfigure || !this._target.isApp);
|
||||
},
|
||||
|
||||
/**
|
||||
* Open a given source in Debugger
|
||||
*/
|
||||
viewSourceInDebugger(sourceURL, sourceLine) {
|
||||
if (this.toolbox) {
|
||||
this.toolbox.viewSourceInDebugger(sourceURL, sourceLine);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Functions handling target network events.
|
||||
*/
|
||||
function NetworkEventsHandler() {
|
||||
this.payloadQueue = [];
|
||||
this.addRequest = this.addRequest.bind(this);
|
||||
this.updateRequest = this.updateRequest.bind(this);
|
||||
this._onNetworkEvent = this._onNetworkEvent.bind(this);
|
||||
this._onNetworkEventUpdate = this._onNetworkEventUpdate.bind(this);
|
||||
this._onDocLoadingMarker = this._onDocLoadingMarker.bind(this);
|
||||
this._onRequestHeaders = this._onRequestHeaders.bind(this);
|
||||
this._onRequestCookies = this._onRequestCookies.bind(this);
|
||||
this._onRequestPostData = this._onRequestPostData.bind(this);
|
||||
this._onResponseHeaders = this._onResponseHeaders.bind(this);
|
||||
this._onResponseCookies = this._onResponseCookies.bind(this);
|
||||
this._onSecurityInfo = this._onSecurityInfo.bind(this);
|
||||
this._onEventTimings = this._onEventTimings.bind(this);
|
||||
}
|
||||
|
||||
NetworkEventsHandler.prototype = {
|
||||
get client() {
|
||||
return NetMonitorController._target.client;
|
||||
},
|
||||
|
||||
get webConsoleClient() {
|
||||
return NetMonitorController.webConsoleClient;
|
||||
},
|
||||
|
||||
get timelineFront() {
|
||||
return NetMonitorController.timelineFront;
|
||||
},
|
||||
|
||||
/**
|
||||
* Connect to the current target client.
|
||||
*/
|
||||
connect(actions) {
|
||||
this.actions = actions;
|
||||
this.webConsoleClient.on("networkEvent", this._onNetworkEvent);
|
||||
this.webConsoleClient.on("networkEventUpdate", this._onNetworkEventUpdate);
|
||||
|
||||
if (this.timelineFront) {
|
||||
this.timelineFront.on("doc-loading", this._onDocLoadingMarker);
|
||||
}
|
||||
|
||||
this._displayCachedEvents();
|
||||
},
|
||||
|
||||
/**
|
||||
* Disconnect from the client.
|
||||
*/
|
||||
disconnect() {
|
||||
if (!this.client) {
|
||||
return;
|
||||
}
|
||||
this.webConsoleClient.off("networkEvent", this._onNetworkEvent);
|
||||
this.webConsoleClient.off("networkEventUpdate", this._onNetworkEventUpdate);
|
||||
|
||||
if (this.timelineFront) {
|
||||
this.timelineFront.off("doc-loading", this._onDocLoadingMarker);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Display any network events already in the cache.
|
||||
*/
|
||||
_displayCachedEvents: function () {
|
||||
for (let cachedEvent of this.webConsoleClient.getNetworkEvents()) {
|
||||
// First add the request to the timeline.
|
||||
this._onNetworkEvent("networkEvent", cachedEvent);
|
||||
// Then replay any updates already received.
|
||||
for (let update of cachedEvent.updates) {
|
||||
this._onNetworkEventUpdate("networkEventUpdate", {
|
||||
packet: {
|
||||
updateType: update
|
||||
},
|
||||
networkInfo: cachedEvent
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The "DOMContentLoaded" and "Load" events sent by the timeline actor.
|
||||
* @param object marker
|
||||
*/
|
||||
_onDocLoadingMarker: function (marker) {
|
||||
this.actions.addTimingMarker(marker);
|
||||
window.emit(EVENTS.TIMELINE_EVENT, marker);
|
||||
},
|
||||
|
||||
/**
|
||||
* The "networkEvent" message type handler.
|
||||
*
|
||||
* @param string type
|
||||
* Message type.
|
||||
* @param object networkInfo
|
||||
* The network request information.
|
||||
*/
|
||||
_onNetworkEvent: function (type, networkInfo) {
|
||||
let { actor,
|
||||
startedDateTime,
|
||||
request: { method, url },
|
||||
isXHR,
|
||||
cause,
|
||||
fromCache,
|
||||
fromServiceWorker
|
||||
} = networkInfo;
|
||||
|
||||
this.addRequest(
|
||||
actor, {startedDateTime, method, url, isXHR, cause, fromCache, fromServiceWorker}
|
||||
);
|
||||
window.emit(EVENTS.NETWORK_EVENT, actor);
|
||||
},
|
||||
|
||||
addRequest(id, data) {
|
||||
let { method, url, isXHR, cause, startedDateTime, fromCache,
|
||||
fromServiceWorker } = data;
|
||||
|
||||
this.actions.addRequest(
|
||||
id,
|
||||
{
|
||||
// Convert the received date/time string to a unix timestamp.
|
||||
startedMillis: Date.parse(startedDateTime),
|
||||
method,
|
||||
url,
|
||||
isXHR,
|
||||
cause,
|
||||
fromCache,
|
||||
fromServiceWorker,
|
||||
},
|
||||
true
|
||||
)
|
||||
.then(() => window.emit(EVENTS.REQUEST_ADDED, id));
|
||||
},
|
||||
|
||||
async fetchImage(mimeType, responseContent) {
|
||||
let payload = {};
|
||||
if (mimeType && responseContent && responseContent.content) {
|
||||
let { encoding, text } = responseContent.content;
|
||||
let response = await getLongString(text);
|
||||
|
||||
if (mimeType.includes("image/")) {
|
||||
payload.responseContentDataUri = formDataURI(mimeType, encoding, response);
|
||||
}
|
||||
|
||||
responseContent.content.text = response;
|
||||
payload.responseContent = responseContent;
|
||||
}
|
||||
return payload;
|
||||
},
|
||||
|
||||
async fetchRequestHeaders(requestHeaders) {
|
||||
let payload = {};
|
||||
if (requestHeaders && requestHeaders.headers && requestHeaders.headers.length) {
|
||||
let headers = await fetchHeaders(requestHeaders, getLongString);
|
||||
if (headers) {
|
||||
payload.requestHeaders = headers;
|
||||
}
|
||||
}
|
||||
return payload;
|
||||
},
|
||||
|
||||
async fetchResponseHeaders(responseHeaders) {
|
||||
let payload = {};
|
||||
if (responseHeaders && responseHeaders.headers && responseHeaders.headers.length) {
|
||||
let headers = await fetchHeaders(responseHeaders, getLongString);
|
||||
if (headers) {
|
||||
payload.responseHeaders = headers;
|
||||
}
|
||||
}
|
||||
return payload;
|
||||
},
|
||||
|
||||
// Search the POST data upload stream for request headers and add
|
||||
// them as a separate property, different from the classic headers.
|
||||
async fetchPostData(requestPostData) {
|
||||
let payload = {};
|
||||
if (requestPostData && requestPostData.postData) {
|
||||
let { text } = requestPostData.postData;
|
||||
let postData = await getLongString(text);
|
||||
const headers = CurlUtils.getHeadersFromMultipartText(postData);
|
||||
const headersSize = headers.reduce((acc, { name, value }) => {
|
||||
return acc + name.length + value.length + 2;
|
||||
}, 0);
|
||||
requestPostData.postData.text = postData;
|
||||
payload.requestPostData = Object.assign({}, requestPostData);
|
||||
payload.requestHeadersFromUploadStream = { headers, headersSize };
|
||||
}
|
||||
return payload;
|
||||
},
|
||||
|
||||
async fetchResponseCookies(responseCookies) {
|
||||
let payload = {};
|
||||
if (responseCookies) {
|
||||
let resCookies = [];
|
||||
// response store cookies in responseCookies or responseCookies.cookies
|
||||
let cookies = responseCookies.cookies ?
|
||||
responseCookies.cookies : responseCookies;
|
||||
// make sure cookies is iterable
|
||||
if (typeof cookies[Symbol.iterator] === "function") {
|
||||
for (let cookie of cookies) {
|
||||
resCookies.push(Object.assign({}, cookie, {
|
||||
value: await getLongString(cookie.value),
|
||||
}));
|
||||
}
|
||||
if (resCookies.length) {
|
||||
payload.responseCookies = resCookies;
|
||||
}
|
||||
}
|
||||
}
|
||||
return payload;
|
||||
},
|
||||
|
||||
// Fetch request and response cookies long value.
|
||||
// Actor does not provide full sized cookie value when the value is too long
|
||||
// To display values correctly, we need fetch them in each request.
|
||||
async fetchRequestCookies(requestCookies) {
|
||||
let payload = {};
|
||||
if (requestCookies) {
|
||||
let reqCookies = [];
|
||||
// request store cookies in requestCookies or requestCookies.cookies
|
||||
let cookies = requestCookies.cookies ?
|
||||
requestCookies.cookies : requestCookies;
|
||||
// make sure cookies is iterable
|
||||
if (typeof cookies[Symbol.iterator] === "function") {
|
||||
for (let cookie of cookies) {
|
||||
reqCookies.push(Object.assign({}, cookie, {
|
||||
value: await getLongString(cookie.value),
|
||||
}));
|
||||
}
|
||||
if (reqCookies.length) {
|
||||
payload.requestCookies = reqCookies;
|
||||
}
|
||||
}
|
||||
}
|
||||
return payload;
|
||||
},
|
||||
|
||||
getPayloadFromQueue(id) {
|
||||
return this.payloadQueue.find((item) => item.id === id);
|
||||
},
|
||||
|
||||
// Packet order of "networkUpdateEvent" is predictable, as a result we can wait for
|
||||
// the last one "eventTimings" packet arrives to check payload is ready
|
||||
isQueuePayloadReady(id) {
|
||||
let queuedPayload = this.getPayloadFromQueue(id);
|
||||
return queuedPayload && queuedPayload.payload.eventTimings;
|
||||
},
|
||||
|
||||
pushPayloadToQueue(id, payload) {
|
||||
let queuedPayload = this.getPayloadFromQueue(id);
|
||||
if (!queuedPayload) {
|
||||
this.payloadQueue.push({ id, payload });
|
||||
} else {
|
||||
// Merge upcoming networkEventUpdate payload into existing one
|
||||
queuedPayload.payload = Object.assign({}, queuedPayload.payload, payload);
|
||||
}
|
||||
},
|
||||
|
||||
async updateRequest(id, data) {
|
||||
let {
|
||||
mimeType,
|
||||
responseContent,
|
||||
responseCookies,
|
||||
responseHeaders,
|
||||
requestCookies,
|
||||
requestHeaders,
|
||||
requestPostData,
|
||||
} = data;
|
||||
|
||||
// fetch request detail contents in parallel
|
||||
let [
|
||||
imageObj,
|
||||
requestHeadersObj,
|
||||
responseHeadersObj,
|
||||
postDataObj,
|
||||
requestCookiesObj,
|
||||
responseCookiesObj,
|
||||
] = await Promise.all([
|
||||
this.fetchImage(mimeType, responseContent),
|
||||
this.fetchRequestHeaders(requestHeaders),
|
||||
this.fetchResponseHeaders(responseHeaders),
|
||||
this.fetchPostData(requestPostData),
|
||||
this.fetchRequestCookies(requestCookies),
|
||||
this.fetchResponseCookies(responseCookies),
|
||||
]);
|
||||
|
||||
let payload = Object.assign({}, data,
|
||||
imageObj, requestHeadersObj, responseHeadersObj,
|
||||
postDataObj, requestCookiesObj, responseCookiesObj);
|
||||
|
||||
this.pushPayloadToQueue(id, payload);
|
||||
|
||||
if (this.isQueuePayloadReady(id)) {
|
||||
await this.actions.updateRequest(id, this.getPayloadFromQueue(id).payload, true);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The "networkEventUpdate" message type handler.
|
||||
*
|
||||
* @param string type
|
||||
* Message type.
|
||||
* @param object packet
|
||||
* The message received from the server.
|
||||
* @param object networkInfo
|
||||
* The network request information.
|
||||
*/
|
||||
_onNetworkEventUpdate: function (type, { packet, networkInfo }) {
|
||||
let { actor } = networkInfo;
|
||||
switch (packet.updateType) {
|
||||
case "requestHeaders":
|
||||
this.webConsoleClient.getRequestHeaders(actor, this._onRequestHeaders);
|
||||
window.emit(EVENTS.UPDATING_REQUEST_HEADERS, actor);
|
||||
break;
|
||||
case "requestCookies":
|
||||
this.webConsoleClient.getRequestCookies(actor, this._onRequestCookies);
|
||||
window.emit(EVENTS.UPDATING_REQUEST_COOKIES, actor);
|
||||
break;
|
||||
case "requestPostData":
|
||||
this.webConsoleClient.getRequestPostData(actor,
|
||||
this._onRequestPostData);
|
||||
window.emit(EVENTS.UPDATING_REQUEST_POST_DATA, actor);
|
||||
break;
|
||||
case "securityInfo":
|
||||
this.updateRequest(actor, {
|
||||
securityState: networkInfo.securityInfo,
|
||||
}).then(() => {
|
||||
this.webConsoleClient.getSecurityInfo(actor, this._onSecurityInfo);
|
||||
window.emit(EVENTS.UPDATING_SECURITY_INFO, actor);
|
||||
});
|
||||
break;
|
||||
case "responseHeaders":
|
||||
this.webConsoleClient.getResponseHeaders(actor,
|
||||
this._onResponseHeaders);
|
||||
window.emit(EVENTS.UPDATING_RESPONSE_HEADERS, actor);
|
||||
break;
|
||||
case "responseCookies":
|
||||
this.webConsoleClient.getResponseCookies(actor,
|
||||
this._onResponseCookies);
|
||||
window.emit(EVENTS.UPDATING_RESPONSE_COOKIES, actor);
|
||||
break;
|
||||
case "responseStart":
|
||||
this.updateRequest(actor, {
|
||||
httpVersion: networkInfo.response.httpVersion,
|
||||
remoteAddress: networkInfo.response.remoteAddress,
|
||||
remotePort: networkInfo.response.remotePort,
|
||||
status: networkInfo.response.status,
|
||||
statusText: networkInfo.response.statusText,
|
||||
headersSize: networkInfo.response.headersSize
|
||||
}).then(() => {
|
||||
window.emit(EVENTS.STARTED_RECEIVING_RESPONSE, actor);
|
||||
});
|
||||
break;
|
||||
case "responseContent":
|
||||
this.webConsoleClient.getResponseContent(actor,
|
||||
this._onResponseContent.bind(this, {
|
||||
contentSize: networkInfo.response.bodySize,
|
||||
transferredSize: networkInfo.response.transferredSize,
|
||||
mimeType: networkInfo.response.content.mimeType
|
||||
}));
|
||||
window.emit(EVENTS.UPDATING_RESPONSE_CONTENT, actor);
|
||||
break;
|
||||
case "eventTimings":
|
||||
this.updateRequest(actor, {
|
||||
totalTime: networkInfo.totalTime
|
||||
}).then(() => {
|
||||
this.webConsoleClient.getEventTimings(actor, this._onEventTimings);
|
||||
window.emit(EVENTS.UPDATING_EVENT_TIMINGS, actor);
|
||||
});
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles additional information received for a "requestHeaders" packet.
|
||||
*
|
||||
* @param object response
|
||||
* The message received from the server.
|
||||
*/
|
||||
_onRequestHeaders: function (response) {
|
||||
this.updateRequest(response.from, {
|
||||
requestHeaders: response
|
||||
}).then(() => {
|
||||
window.emit(EVENTS.RECEIVED_REQUEST_HEADERS, response.from);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles additional information received for a "requestCookies" packet.
|
||||
*
|
||||
* @param object response
|
||||
* The message received from the server.
|
||||
*/
|
||||
_onRequestCookies: function (response) {
|
||||
this.updateRequest(response.from, {
|
||||
requestCookies: response
|
||||
}).then(() => {
|
||||
window.emit(EVENTS.RECEIVED_REQUEST_COOKIES, response.from);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles additional information received for a "requestPostData" packet.
|
||||
*
|
||||
* @param object response
|
||||
* The message received from the server.
|
||||
*/
|
||||
_onRequestPostData: function (response) {
|
||||
this.updateRequest(response.from, {
|
||||
requestPostData: response
|
||||
}).then(() => {
|
||||
window.emit(EVENTS.RECEIVED_REQUEST_POST_DATA, response.from);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles additional information received for a "securityInfo" packet.
|
||||
*
|
||||
* @param object response
|
||||
* The message received from the server.
|
||||
*/
|
||||
_onSecurityInfo: function (response) {
|
||||
this.updateRequest(response.from, {
|
||||
securityInfo: response.securityInfo
|
||||
}).then(() => {
|
||||
window.emit(EVENTS.RECEIVED_SECURITY_INFO, response.from);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles additional information received for a "responseHeaders" packet.
|
||||
*
|
||||
* @param object response
|
||||
* The message received from the server.
|
||||
*/
|
||||
_onResponseHeaders: function (response) {
|
||||
this.updateRequest(response.from, {
|
||||
responseHeaders: response
|
||||
}).then(() => {
|
||||
window.emit(EVENTS.RECEIVED_RESPONSE_HEADERS, response.from);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles additional information received for a "responseCookies" packet.
|
||||
*
|
||||
* @param object response
|
||||
* The message received from the server.
|
||||
*/
|
||||
_onResponseCookies: function (response) {
|
||||
this.updateRequest(response.from, {
|
||||
responseCookies: response
|
||||
}).then(() => {
|
||||
window.emit(EVENTS.RECEIVED_RESPONSE_COOKIES, response.from);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles additional information received for a "responseContent" packet.
|
||||
*
|
||||
* @param object data
|
||||
* The message received from the server event.
|
||||
* @param object response
|
||||
* The message received from the server.
|
||||
*/
|
||||
_onResponseContent: function (data, response) {
|
||||
let payload = Object.assign({ responseContent: response }, data);
|
||||
this.updateRequest(response.from, payload).then(() => {
|
||||
window.emit(EVENTS.RECEIVED_RESPONSE_CONTENT, response.from);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles additional information received for a "eventTimings" packet.
|
||||
*
|
||||
* @param object response
|
||||
* The message received from the server.
|
||||
*/
|
||||
_onEventTimings: function (response) {
|
||||
this.updateRequest(response.from, {
|
||||
eventTimings: response
|
||||
}).then(() => {
|
||||
window.emit(EVENTS.RECEIVED_EVENT_TIMINGS, response.from);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
exports.NetMonitorController = NetMonitorController;
|
|
@ -10,12 +10,14 @@ const { gDevTools } = require("devtools/client/framework/devtools");
|
|||
const { saveAs } = require("devtools/client/shared/file-saver");
|
||||
const { copyString } = require("devtools/shared/platform/clipboard");
|
||||
const { HarExporter } = require("./har/har-exporter");
|
||||
const { NetMonitorController } = require("./netmonitor-controller");
|
||||
const {
|
||||
getLongString,
|
||||
getTabTarget,
|
||||
} = require("./connector/index");
|
||||
const {
|
||||
getSelectedRequest,
|
||||
getSortedRequests,
|
||||
} = require("./selectors/index");
|
||||
const { getLongString } = require("./utils/client");
|
||||
const { L10N } = require("./utils/l10n");
|
||||
const { showMenu } = require("./utils/menu");
|
||||
const {
|
||||
|
@ -33,11 +35,15 @@ function RequestListContextMenu({
|
|||
|
||||
RequestListContextMenu.prototype = {
|
||||
get selectedRequest() {
|
||||
return getSelectedRequest(window.gStore.getState());
|
||||
// FIXME: Bug 1336382 - Implement RequestListContextMenu React component
|
||||
// Remove window.store
|
||||
return getSelectedRequest(window.store.getState());
|
||||
},
|
||||
|
||||
get sortedRequests() {
|
||||
return getSortedRequests(window.gStore.getState());
|
||||
// FIXME: Bug 1336382 - Implement RequestListContextMenu React component
|
||||
// Remove window.store
|
||||
return getSortedRequests(window.store.getState());
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -164,16 +170,14 @@ RequestListContextMenu.prototype = {
|
|||
|
||||
menu.push({
|
||||
type: "separator",
|
||||
visible: !!(NetMonitorController.supportsCustomRequest &&
|
||||
selectedRequest && !selectedRequest.isCustom),
|
||||
visible: !!(selectedRequest && !selectedRequest.isCustom),
|
||||
});
|
||||
|
||||
menu.push({
|
||||
id: "request-list-context-resend",
|
||||
label: L10N.getStr("netmonitor.context.editAndResend"),
|
||||
accesskey: L10N.getStr("netmonitor.context.editAndResend.accesskey"),
|
||||
visible: !!(NetMonitorController.supportsCustomRequest &&
|
||||
selectedRequest && !selectedRequest.isCustom),
|
||||
visible: !!(selectedRequest && !selectedRequest.isCustom),
|
||||
click: this.cloneSelectedRequest,
|
||||
});
|
||||
|
||||
|
@ -345,7 +349,7 @@ RequestListContextMenu.prototype = {
|
|||
},
|
||||
|
||||
getDefaultHarOptions() {
|
||||
let form = NetMonitorController._target.form;
|
||||
let form = getTabTarget().form;
|
||||
let title = form.title || form.url;
|
||||
|
||||
return {
|
||||
|
|
|
@ -19,7 +19,9 @@ class RequestListHeaderContextMenu {
|
|||
}
|
||||
|
||||
get columns() {
|
||||
return window.gStore.getState().ui.columns;
|
||||
// FIXME: Bug 1362059 - Implement RequestListHeaderContextMenu React component
|
||||
// Remove window.store
|
||||
return window.store.getState().ui.columns;
|
||||
}
|
||||
|
||||
get visibleColumns() {
|
||||
|
|
|
@ -8,7 +8,7 @@ const {
|
|||
setImageTooltip,
|
||||
getImageDimensions,
|
||||
} = require("devtools/client/shared/widgets/tooltip/ImageTooltipHelper");
|
||||
const { getLongString } = require("./utils/client");
|
||||
const { getLongString } = require("./connector/index");
|
||||
const { formDataURI } = require("./utils/request-utils");
|
||||
|
||||
const REQUESTS_TOOLTIP_IMAGE_MAX_DIM = 400; // px
|
||||
|
|
|
@ -1,101 +0,0 @@
|
|||
/* 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/. */
|
||||
|
||||
/* global gStore */
|
||||
|
||||
"use strict";
|
||||
|
||||
const Services = require("Services");
|
||||
const Actions = require("../actions/index");
|
||||
const { EVENTS } = require("../constants");
|
||||
|
||||
let activeConsole;
|
||||
|
||||
/**
|
||||
* Called for each location change in the monitored tab.
|
||||
*
|
||||
* @param {String} type Packet type.
|
||||
* @param {Object} packet Packet received from the server.
|
||||
*/
|
||||
function navigated(type) {
|
||||
window.emit(EVENTS.TARGET_DID_NAVIGATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called for each location change in the monitored tab.
|
||||
*
|
||||
* @param {String} type Packet type.
|
||||
* @param {Object} packet Packet received from the server.
|
||||
*/
|
||||
function willNavigate(type) {
|
||||
// Reset UI.
|
||||
if (!Services.prefs.getBoolPref("devtools.webconsole.persistlog")) {
|
||||
gStore.dispatch(Actions.batchReset());
|
||||
gStore.dispatch(Actions.clearRequests());
|
||||
} else {
|
||||
// If the log is persistent, just clear all accumulated timing markers.
|
||||
gStore.dispatch(Actions.clearTimingMarkers());
|
||||
}
|
||||
|
||||
window.emit(EVENTS.TARGET_WILL_NAVIGATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process connection events.
|
||||
*
|
||||
* @param {Object} tabTarget
|
||||
*/
|
||||
function onFirefoxConnect(tabTarget) {
|
||||
activeConsole = tabTarget.activeConsole;
|
||||
tabTarget.on("navigate", navigated);
|
||||
tabTarget.on("will-navigate", willNavigate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process disconnect events.
|
||||
*
|
||||
* @param {Object} tabTarget
|
||||
*/
|
||||
function onFirefoxDisconnect(tabTarget) {
|
||||
activeConsole = null;
|
||||
tabTarget.off("navigate", navigated);
|
||||
tabTarget.off("will-navigate", willNavigate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve webconsole object
|
||||
*
|
||||
* @returns {Object} webConsole
|
||||
*/
|
||||
function getWebConsoleClient() {
|
||||
return activeConsole;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the full text of a LongString.
|
||||
*
|
||||
* @param object | string stringGrip
|
||||
* The long string grip containing the corresponding actor.
|
||||
* If you pass in a plain string (by accident or because you're lazy),
|
||||
* then a promise of the same string is simply returned.
|
||||
* @return object Promise
|
||||
* A promise that is resolved when the full string contents
|
||||
* are available, or rejected if something goes wrong.
|
||||
*/
|
||||
function getLongString(stringGrip) {
|
||||
// FIXME: this.webConsoleClient will be undefined in mochitest,
|
||||
// so we return string instantly to skip undefined error
|
||||
if (typeof stringGrip === "string") {
|
||||
return Promise.resolve(stringGrip);
|
||||
}
|
||||
|
||||
return activeConsole.getString(stringGrip);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getLongString,
|
||||
getWebConsoleClient,
|
||||
onFirefoxConnect,
|
||||
onFirefoxDisconnect,
|
||||
};
|
|
@ -4,7 +4,6 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
DevToolsModules(
|
||||
'client.js',
|
||||
'create-store.js',
|
||||
'filter-predicates.js',
|
||||
'filter-text-utils.js',
|
||||
|
|
|
@ -88,8 +88,10 @@ NewConsoleOutputWrapper.prototype = {
|
|||
frame.line
|
||||
),
|
||||
openNetworkPanel: (requestId) => {
|
||||
return this.toolbox.selectTool("netmonitor").then(panel => {
|
||||
return panel.panelWin.NetMonitorController.inspectRequest(requestId);
|
||||
return this.toolbox.selectTool("netmonitor").then((panel) => {
|
||||
let { inspectRequest } = panel.panelWin.windowRequire(
|
||||
"devtools/client/netmonitor/src/connector/index");
|
||||
return inspectRequest(requestId);
|
||||
});
|
||||
},
|
||||
sourceMapService: this.toolbox ? this.toolbox.sourceMapURLService : null,
|
||||
|
|
|
@ -1937,9 +1937,9 @@ WebConsoleFrame.prototype = {
|
|||
return;
|
||||
}
|
||||
return toolbox.selectTool("netmonitor").then(panel => {
|
||||
let { NetMonitorController } = panel.panelWin
|
||||
.windowRequire("devtools/client/netmonitor/src/netmonitor-controller");
|
||||
return NetMonitorController.inspectRequest(requestId);
|
||||
let { inspectRequest } = panel.panelWin.windowRequire(
|
||||
"devtools/client/netmonitor/src/connector/index");
|
||||
return inspectRequest(requestId);
|
||||
});
|
||||
},
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче