--- a/devtools/client/netmonitor/index.html
+++ b/devtools/client/netmonitor/index.html
@@ -26,20 +26,16 @@
const Provider = createFactory(require("devtools/client/shared/vendor/react-redux").Provider);
const { configureStore } = require("./src/utils/create-store");
const store = window.gStore = configureStore();
const { NetMonitorController } = require("./src/netmonitor-controller");
// Inject EventEmitter into global window.
EventEmitter.decorate(window);
- // Export NetMonitorController to global window
- // FIXME: Use module export mechanism instead of this tricky global variables
- window.NetMonitorController = NetMonitorController;
-
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({
client: {
getTabTarget: () => toolbox.target,
--- a/devtools/client/netmonitor/index.js
+++ b/devtools/client/netmonitor/index.js
@@ -8,17 +8,17 @@
* This script is the entry point of devtools-launchpad. Make netmonitor possible
* to run on standalone browser tab without chrome privilege.
* See README.md for more information.
*/
const React = require("react");
const ReactDOM = require("react-dom");
const { bootstrap } = require("devtools-launchpad");
const { EventEmitter } = require("devtools-modules");
-const { Services: { pref }} = require("devtools-modules");
+const { Services: { appinfo, pref }} = require("devtools-modules");
const { configureStore } = require("./src/utils/create-store");
require("./src/assets/styles/netmonitor.css");
EventEmitter.decorate(window);
pref("devtools.netmonitor.enabled", true);
pref("devtools.netmonitor.filters", "[\"all\"]");
@@ -35,17 +35,24 @@ pref("devtools.netmonitor.har.forceExpor
pref("devtools.netmonitor.har.pageLoadedTimeout", 1500);
pref("devtools.netmonitor.har.enableAutoExportToFile", false);
pref("devtools.webconsole.persistlog", false);
const App = require("./src/components/app");
const store = window.gStore = configureStore();
const { NetMonitorController } = require("./src/netmonitor-controller");
-// FIXME: Inject NetMonitorController to global window
-window.NetMonitorController = NetMonitorController;
+window.addEventListener("DOMContentLoaded", () => {
+ if (appinfo.OS === "Darwin") {
+ document.documentElement.setAttribute("platform", "mac");
+ } else if (appinfo.OS === "Linux") {
+ document.documentElement.setAttribute("platform", "linux");
+ } else {
+ document.documentElement.setAttribute("platform", "win");
+ }
+});
bootstrap(React, ReactDOM, App, null, store).then(connection => {
if (!connection || !connection.tab) {
return;
}
NetMonitorController.startupNetMonitor(connection);
});
--- a/devtools/client/netmonitor/src/actions/requests.js
+++ b/devtools/client/netmonitor/src/actions/requests.js
@@ -7,16 +7,17 @@
const {
ADD_REQUEST,
CLEAR_REQUESTS,
CLONE_SELECTED_REQUEST,
REMOVE_SELECTED_CUSTOM_REQUEST,
SEND_CUSTOM_REQUEST,
UPDATE_REQUEST,
} = require("../constants");
+const { NetMonitorController } = require("../netmonitor-controller");
const { getSelectedRequest } = require("../selectors/index");
function addRequest(id, data, batch) {
return {
type: ADD_REQUEST,
id,
data,
meta: { batch },
@@ -41,17 +42,17 @@ function cloneSelectedRequest() {
type: CLONE_SELECTED_REQUEST
};
}
/**
* Send a new HTTP request using the data in the custom request form.
*/
function sendCustomRequest() {
- if (!window.NetMonitorController.supportsCustomRequest) {
+ if (!NetMonitorController.supportsCustomRequest) {
return cloneSelectedRequest();
}
return (dispatch, getState) => {
const selected = getSelectedRequest(getState());
if (!selected) {
return;
@@ -65,17 +66,17 @@ function sendCustomRequest() {
};
if (selected.requestHeaders) {
data.headers = selected.requestHeaders.headers;
}
if (selected.requestPostData) {
data.body = selected.requestPostData.postData.text;
}
- window.NetMonitorController.webConsoleClient.sendHTTPRequest(data, (response) => {
+ NetMonitorController.webConsoleClient.sendHTTPRequest(data, (response) => {
return dispatch({
type: SEND_CUSTOM_REQUEST,
id: response.eventActor.actor,
});
});
};
}
--- a/devtools/client/netmonitor/src/actions/ui.js
+++ b/devtools/client/netmonitor/src/actions/ui.js
@@ -8,16 +8,17 @@ const {
ACTIVITY_TYPE,
OPEN_NETWORK_DETAILS,
OPEN_STATISTICS,
RESET_COLUMNS,
SELECT_DETAILS_PANEL_TAB,
TOGGLE_COLUMN,
WATERFALL_RESIZE,
} = require("../constants");
+const { NetMonitorController } = require("../netmonitor-controller");
/**
* Change network details panel.
*
* @param {boolean} open - expected network details panel open state
*/
function openNetworkDetails(open) {
return {
@@ -28,17 +29,17 @@ function openNetworkDetails(open) {
/**
* Change performance statistics panel open state.
*
* @param {boolean} visible - expected performance statistics panel open state
*/
function openStatistics(open) {
if (open) {
- window.NetMonitorController.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED);
+ NetMonitorController.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED);
}
return {
type: OPEN_STATISTICS,
open,
};
}
/**
--- a/devtools/client/netmonitor/src/assets/styles/netmonitor.css
+++ b/devtools/client/netmonitor/src/assets/styles/netmonitor.css
@@ -1,13 +1,14 @@
/* 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/. */
@import "chrome://devtools/skin/widgets.css";
+@import "resource://devtools/client/themes/light-theme.css";
@import "resource://devtools/client/shared/components/splitter/split-box.css";
@import "resource://devtools/client/shared/components/tree/tree-view.css";
@import "resource://devtools/client/shared/components/tabs/tabs.css";
@import "resource://devtools/client/shared/components/tabs/tabbar.css";
@import "chrome://devtools/skin/components-frame.css";
:root.theme-dark {
--table-splitter-color: rgba(255,255,255,0.15);
--- a/devtools/client/netmonitor/src/components/headers-panel.js
+++ b/devtools/client/netmonitor/src/components/headers-panel.js
@@ -5,16 +5,17 @@
"use strict";
const {
createClass,
createFactory,
DOM,
PropTypes,
} = require("devtools/client/shared/vendor/react");
+const { NetMonitorController } = require("../netmonitor-controller");
const { getFormattedSize } = require("../utils/format-utils");
const { L10N } = require("../utils/l10n");
const {
getHeadersURL,
getHTTPStatusCodeURL,
} = require("../utils/mdn-utils");
const { writeHeaderText } = require("../utils/request-utils");
@@ -192,17 +193,17 @@ const HeadersPanel = createClass({
+ " status-text",
readOnly: true,
value: `${status} ${statusText}`,
size: `${inputWidth}`,
}),
statusCodeDocURL ? MDNLink({
url: statusCodeDocURL,
}) : null,
- window.NetMonitorController.supportsCustomRequest && button({
+ NetMonitorController.supportsCustomRequest && button({
className: "devtools-button",
onClick: cloneSelectedRequest,
}, EDIT_AND_RESEND),
button({
className: "devtools-button",
onClick: this.toggleRawHeaders,
}, RAW_HEADERS),
)
--- a/devtools/client/netmonitor/src/components/request-list-content.js
+++ b/devtools/client/netmonitor/src/components/request-list-content.js
@@ -34,17 +34,17 @@ const REQUESTS_TOOLTIP_TOGGLE_DELAY = 50
const RequestListContent = createClass({
displayName: "RequestListContent",
propTypes: {
columns: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired,
displayedRequests: PropTypes.object.isRequired,
firstRequestStartedMillis: PropTypes.number.isRequired,
- fromCache: PropTypes.bool.isRequired,
+ fromCache: PropTypes.bool,
onCauseBadgeClick: PropTypes.func.isRequired,
onItemMouseDown: PropTypes.func.isRequired,
onSecurityIconClick: PropTypes.func.isRequired,
onSelectDelta: PropTypes.func.isRequired,
scale: PropTypes.number,
selectedRequestId: PropTypes.string,
},
@@ -261,29 +261,29 @@ module.exports = connect(
columns: state.ui.columns,
displayedRequests: getDisplayedRequests(state),
firstRequestStartedMillis: state.requests.firstStartedMillis,
selectedRequestId: state.requests.selectedId,
scale: getWaterfallScale(state),
}),
(dispatch) => ({
dispatch,
+ /**
+ * A handler that opens the stack trace tab when a stack trace is available
+ */
+ onCauseBadgeClick: (cause) => {
+ if (cause.stacktrace && cause.stacktrace.length > 0) {
+ dispatch(Actions.selectDetailsPanelTab("stack-trace"));
+ }
+ },
onItemMouseDown: (id) => dispatch(Actions.selectRequest(id)),
/**
* A handler that opens the security tab in the details view if secure or
* broken security indicator is clicked.
*/
onSecurityIconClick: (securityState) => {
if (securityState && securityState !== "insecure") {
dispatch(Actions.selectDetailsPanelTab("security"));
}
},
- /**
- * A handler that opens the stack trace tab when a stack trace is available
- */
- onCauseBadgeClick: (cause) => {
- if (cause.stacktrace && cause.stacktrace.length > 0) {
- dispatch(Actions.selectDetailsPanelTab("stack-trace"));
- }
- },
onSelectDelta: (delta) => dispatch(Actions.selectDelta(delta)),
}),
)(RequestListContent);
--- a/devtools/client/netmonitor/src/components/request-list-empty-notice.js
+++ b/devtools/client/netmonitor/src/components/request-list-empty-notice.js
@@ -7,16 +7,17 @@
const {
createClass,
DOM,
PropTypes,
} = require("devtools/client/shared/vendor/react");
const { connect } = require("devtools/client/shared/vendor/react-redux");
const Actions = require("../actions/index");
const { ACTIVITY_TYPE } = require("../constants");
+const { NetMonitorController } = require("../netmonitor-controller");
const { L10N } = require("../utils/l10n");
const { button, div, span } = DOM;
/**
* UI displayed when the request list is empty. Contains instructions on reloading
* the page and on triggering performance analysis of the page.
*/
@@ -59,12 +60,12 @@ const RequestListEmptyNotice = createCla
}
});
module.exports = connect(
undefined,
dispatch => ({
onPerfClick: () => dispatch(Actions.openStatistics(true)),
onReloadClick: () =>
- window.NetMonitorController
+ NetMonitorController
.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_DEFAULT),
})
)(RequestListEmptyNotice);
--- a/devtools/client/netmonitor/src/components/request-list-item.js
+++ b/devtools/client/netmonitor/src/components/request-list-item.js
@@ -63,17 +63,17 @@ const RequestListItem = createClass({
displayName: "RequestListItem",
propTypes: {
columns: PropTypes.object.isRequired,
item: PropTypes.object.isRequired,
index: PropTypes.number.isRequired,
isSelected: PropTypes.bool.isRequired,
firstRequestStartedMillis: PropTypes.number.isRequired,
- fromCache: PropTypes.bool.isRequired,
+ fromCache: PropTypes.bool,
onCauseBadgeClick: PropTypes.func.isRequired,
onContextMenu: PropTypes.func.isRequired,
onFocusedNodeChange: PropTypes.func,
onMouseDown: PropTypes.func.isRequired,
onSecurityIconClick: PropTypes.func.isRequired,
},
componentDidMount() {
--- a/devtools/client/netmonitor/src/netmonitor-controller.js
+++ b/devtools/client/netmonitor/src/netmonitor-controller.js
@@ -18,18 +18,16 @@ const {
onFirefoxConnect,
onFirefoxDisconnect,
} = require("./utils/client");
const {
getRequestById,
getDisplayedRequestById,
} = require("./selectors/index");
-const gStore = window.gStore;
-
/**
* Object defining the network monitor controller components.
*/
var NetMonitorController = {
/**
* Initializes the view and connects the monitor client.
*
* @param {Object} connection connection data wrapper
@@ -52,17 +50,17 @@ var NetMonitorController = {
* @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) => {
- gStore.dispatch(Actions.batchReset());
+ window.gStore.dispatch(Actions.batchReset());
onFirefoxDisconnect(this._target);
this._target.off("close", this._onTabDetached);
this.NetworkEventsHandler.disconnect();
await this.disconnect();
resolve();
});
return this._shutdown;
@@ -254,28 +252,28 @@ var NetMonitorController = {
* A promise resolved once the task finishes.
*/
inspectRequest: function (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 = function () {
- request = getDisplayedRequestById(gStore.getState(), requestId);
+ request = getDisplayedRequestById(window.gStore.getState(), requestId);
if (!request) {
// Reset filters so that the request is visible.
- gStore.dispatch(Actions.toggleRequestFilterType("all"));
- request = getDisplayedRequestById(gStore.getState(), requestId);
+ window.gStore.dispatch(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);
- gStore.dispatch(Actions.selectRequest(request.id));
+ window.gStore.dispatch(Actions.selectRequest(request.id));
resolve();
}
};
inspector();
if (!request) {
window.on(EVENTS.REQUEST_ADDED, inspector);
}
@@ -450,17 +448,17 @@ NetworkEventsHandler.prototype = {
},
/**
* The "DOMContentLoaded" and "Load" events sent by the timeline actor.
* @param object marker
*/
_onDocLoadingMarker: function (marker) {
window.emit(EVENTS.TIMELINE_EVENT, marker);
- gStore.dispatch(Actions.addTimingMarker(marker));
+ window.gStore.dispatch(Actions.addTimingMarker(marker));
},
/**
* The "networkEvent" message type handler.
*
* @param string type
* Message type.
* @param object networkInfo
@@ -481,17 +479,17 @@ NetworkEventsHandler.prototype = {
);
window.emit(EVENTS.NETWORK_EVENT, actor);
},
addRequest(id, data) {
let { method, url, isXHR, cause, startedDateTime, fromCache,
fromServiceWorker } = data;
- gStore.dispatch(Actions.addRequest(
+ window.gStore.dispatch(Actions.addRequest(
id,
{
// Convert the received date/time string to a unix timestamp.
startedMillis: Date.parse(startedDateTime),
method,
url,
isXHR,
cause,
@@ -500,42 +498,42 @@ NetworkEventsHandler.prototype = {
},
true
))
.then(() => window.emit(EVENTS.REQUEST_ADDED, id));
},
async updateRequest(id, data) {
const action = Actions.updateRequest(id, data, true);
- await gStore.dispatch(action);
+ await window.gStore.dispatch(action);
let {
responseContent,
responseCookies,
responseHeaders,
requestCookies,
requestHeaders,
requestPostData,
} = action.data;
- let request = getRequestById(gStore.getState(), action.id);
+ let request = getRequestById(window.gStore.getState(), action.id);
if (requestHeaders && requestHeaders.headers && requestHeaders.headers.length) {
let headers = await fetchHeaders(requestHeaders, getLongString);
if (headers) {
- await gStore.dispatch(Actions.updateRequest(
+ await window.gStore.dispatch(Actions.updateRequest(
action.id,
{ requestHeaders: headers },
true,
));
}
}
if (responseHeaders && responseHeaders.headers && responseHeaders.headers.length) {
let headers = await fetchHeaders(responseHeaders, getLongString);
if (headers) {
- await gStore.dispatch(Actions.updateRequest(
+ await window.gStore.dispatch(Actions.updateRequest(
action.id,
{ responseHeaders: headers },
true,
));
}
}
if (request && responseContent && responseContent.content) {
@@ -546,17 +544,17 @@ NetworkEventsHandler.prototype = {
if (mimeType.includes("image/")) {
payload.responseContentDataUri = formDataURI(mimeType, encoding, response);
}
responseContent.content.text = response;
payload.responseContent = responseContent;
- await gStore.dispatch(Actions.updateRequest(action.id, payload, true));
+ await window.gStore.dispatch(Actions.updateRequest(action.id, payload, true));
if (mimeType.includes("image/")) {
window.emit(EVENTS.RESPONSE_IMAGE_THUMBNAIL_DISPLAYED);
}
}
// Search the POST data upload stream for request headers and add
// them as a separate property, different from the classic headers.
@@ -567,17 +565,17 @@ NetworkEventsHandler.prototype = {
const headersSize = headers.reduce((acc, { name, value }) => {
return acc + name.length + value.length + 2;
}, 0);
let payload = {};
requestPostData.postData.text = postData;
payload.requestPostData = Object.assign({}, requestPostData);
payload.requestHeadersFromUploadStream = { headers, headersSize };
- await gStore.dispatch(Actions.updateRequest(action.id, payload, true));
+ await window.gStore.dispatch(Actions.updateRequest(action.id, payload, true));
}
// 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.
if (requestCookies) {
let reqCookies = [];
// request store cookies in requestCookies or requestCookies.cookies
@@ -586,17 +584,17 @@ NetworkEventsHandler.prototype = {
// 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) {
- await gStore.dispatch(Actions.updateRequest(
+ await window.gStore.dispatch(Actions.updateRequest(
action.id,
{ requestCookies: reqCookies },
true));
}
}
}
if (responseCookies) {
@@ -607,17 +605,17 @@ NetworkEventsHandler.prototype = {
// 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) {
- await gStore.dispatch(Actions.updateRequest(
+ await window.gStore.dispatch(Actions.updateRequest(
action.id,
{ responseCookies: resCookies },
true));
}
}
}
},
--- a/devtools/client/netmonitor/src/request-list-context-menu.js
+++ b/devtools/client/netmonitor/src/request-list-context-menu.js
@@ -6,16 +6,17 @@
const Services = require("Services");
const { Curl } = require("devtools/client/shared/curl");
const { gDevTools } = require("devtools/client/framework/devtools");
const Menu = require("devtools/client/framework/menu");
const MenuItem = require("devtools/client/framework/menu-item");
const clipboardHelper = require("devtools/shared/platform/clipboard");
const { HarExporter } = require("./har/har-exporter");
+const { NetMonitorController } = require("./netmonitor-controller");
const { getLongString } = require("./utils/client");
const { L10N } = require("./utils/l10n");
const {
formDataURI,
getFormDataSections,
getUrlQuery,
parseQueryString,
} = require("./utils/request-utils");
@@ -151,25 +152,25 @@ RequestListContextMenu.prototype = {
label: L10N.getStr("netmonitor.context.saveAllAsHar"),
accesskey: L10N.getStr("netmonitor.context.saveAllAsHar.accesskey"),
visible: this.sortedRequests.size > 0,
click: () => this.saveAllAsHar(),
}));
menu.append(new MenuItem({
type: "separator",
- visible: !!(window.NetMonitorController.supportsCustomRequest &&
+ visible: !!(NetMonitorController.supportsCustomRequest &&
selectedRequest && !selectedRequest.isCustom),
}));
menu.append(new MenuItem({
id: "request-list-context-resend",
label: L10N.getStr("netmonitor.context.editAndResend"),
accesskey: L10N.getStr("netmonitor.context.editAndResend.accesskey"),
- visible: !!(window.NetMonitorController.supportsCustomRequest &&
+ visible: !!(NetMonitorController.supportsCustomRequest &&
selectedRequest && !selectedRequest.isCustom),
click: this.cloneSelectedRequest,
}));
menu.append(new MenuItem({
type: "separator",
visible: !!selectedRequest,
}));
@@ -181,17 +182,17 @@ RequestListContextMenu.prototype = {
visible: !!selectedRequest,
click: () => this.openRequestInTab()
}));
menu.append(new MenuItem({
id: "request-list-context-perf",
label: L10N.getStr("netmonitor.context.perfTools"),
accesskey: L10N.getStr("netmonitor.context.perfTools.accesskey"),
- visible: !!window.NetMonitorController.supportsPerfStats,
+ visible: !!NetMonitorController.supportsPerfStats,
click: () => this.openStatistics(true)
}));
menu.popup(screenX, screenY, { doc: window.parent.document });
return menu;
},
/**
@@ -343,17 +344,17 @@ RequestListContextMenu.prototype = {
/**
* Save HAR from the network panel content to a file.
*/
saveAllAsHar() {
return HarExporter.save(this.getDefaultHarOptions());
},
getDefaultHarOptions() {
- let form = window.NetMonitorController._target.form;
+ let form = NetMonitorController._target.form;
let title = form.title || form.url;
return {
getString: getLongString,
items: this.sortedRequests,
title: title
};
}
--- a/devtools/client/netmonitor/src/utils/create-store.js
+++ b/devtools/client/netmonitor/src/utils/create-store.js
@@ -1,17 +1,16 @@
/* 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 { createStore, applyMiddleware } = require("devtools/client/shared/vendor/redux");
-const { thunk } = require("devtools/client/shared/redux/middleware/thunk");
const batching = require("../middleware/batching");
const prefs = require("../middleware/prefs");
const rootReducer = require("../reducers/index");
const { FilterTypes, Filters } = require("../reducers/filters");
const { Requests } = require("../reducers/requests");
const { Sort } = require("../reducers/sort");
const { TimingMarkers } = require("../reducers/timing-markers");
const { UI, Columns } = require("../reducers/ui");
@@ -35,20 +34,12 @@ function configureStore() {
requests: new Requests(),
sort: new Sort(),
timingMarkers: new TimingMarkers(),
ui: new UI({
columns: new Columns(inactiveColumns)
}),
};
- return createStore(
- rootReducer,
- initialState,
- applyMiddleware(
- thunk,
- prefs,
- batching
- )
- );
+ return createStore(rootReducer, initialState, applyMiddleware(prefs, batching));
}
exports.configureStore = configureStore;