Bug 1344160 - Refactor netmonitor-controller.js r=Honza

MozReview-Commit-ID: JPUB83Qsmav

--HG--
extra : rebase_source : 32f3d355d4ba0f79dc06b7394b6fa265c509990b
This commit is contained in:
Ricky Chien 2017-04-29 23:20:24 +08:00
Родитель 5292f017eb
Коммит ffb7c92d26
25 изменённых файлов: 880 добавлений и 949 удалений

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

@ -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);
});
},