Bug 1005755 - Stop using Connector as a singleton; r=rickychien

MozReview-Commit-ID: tQKFiKDozA

--HG--
extra : rebase_source : f1392a9c909464d812e868dc3760e2f0d69fc8ae
This commit is contained in:
Jan Odvarko 2017-10-16 14:42:25 +02:00
Родитель 2b9694966e
Коммит b27a14a02a
25 изменённых файлов: 362 добавлений и 231 удалений

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

@ -14,7 +14,8 @@
<script> <script>
"use strict"; "use strict";
const { BrowserLoader } = Components.utils.import("resource://devtools/client/shared/browser-loader.js", {}); const { BrowserLoader } = Components.utils.import(
"resource://devtools/client/shared/browser-loader.js", {});
const require = window.windowRequire = BrowserLoader({ const require = window.windowRequire = BrowserLoader({
baseURI: "resource://devtools/client/netmonitor/", baseURI: "resource://devtools/client/netmonitor/",
window, window,
@ -25,15 +26,23 @@
const { render, unmountComponentAtNode } = require("devtools/client/shared/vendor/react-dom"); const { render, unmountComponentAtNode } = require("devtools/client/shared/vendor/react-dom");
const Provider = createFactory(require("devtools/client/shared/vendor/react-redux").Provider); const Provider = createFactory(require("devtools/client/shared/vendor/react-redux").Provider);
const { bindActionCreators } = require("devtools/client/shared/vendor/redux"); const { bindActionCreators } = require("devtools/client/shared/vendor/redux");
const { Connector } = require("./src/connector/index");
const { configureStore } = require("./src/utils/create-store"); const { configureStore } = require("./src/utils/create-store");
const store = configureStore(); const App = createFactory(require("./src/components/app"));
const actions = bindActionCreators(require("./src/actions/index"), store.dispatch); const { getDisplayedRequestById } = require("./src/selectors/index");
const { onFirefoxConnect, onDisconnect } = require("./src/connector/index"); const { EVENTS } = require("./src/constants");
// Inject EventEmitter into global window. // Inject EventEmitter into global window.
EventEmitter.decorate(window); EventEmitter.decorate(window);
// Configure store/state object.
let connector = new Connector();
const store = configureStore(connector);
const actions = bindActionCreators(require("./src/actions/index"), store.dispatch);
// Inject to global window for testing // Inject to global window for testing
window.store = store; window.store = store;
window.connector = connector;
window.Netmonitor = { window.Netmonitor = {
bootstrap({ toolbox }) { bootstrap({ toolbox }) {
@ -53,17 +62,57 @@
top.openUILinkIn(link, "tab"); top.openUILinkIn(link, "tab");
}; };
const App = createFactory(require("./src/components/app")); // Render the root Application component.
const sourceMapService = toolbox.sourceMapURLService; const sourceMapService = toolbox.sourceMapURLService;
const app = App({ sourceMapService, openLink }); const app = App({ connector, openLink, sourceMapService });
render(Provider({ store }, app), this.mount); render(Provider({ store }, app), this.mount);
return onFirefoxConnect(connection, actions, store.getState);
// Connect to the Firefox backend by default.
return connector.connectFirefox(connection, actions, store.getState);
}, },
destroy() { destroy() {
unmountComponentAtNode(this.mount); unmountComponentAtNode(this.mount);
return onDisconnect(); return connector.disconnect();
}, },
/**
* Selects the specified request in the waterfall and opens the details view.
* This is a firefox toolbox specific API, which providing an ability to inspect
* a network request directly from other internal toolbox panel.
*
* @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(store.getState(), requestId);
if (!request) {
// Reset filters so that the request is visible.
actions.toggleRequestFilterType("all");
request = getDisplayedRequestById(store.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);
actions.selectRequest(request.id);
resolve();
}
};
inspector();
if (!request) {
window.on(EVENTS.REQUEST_ADDED, inspector);
}
});
}
}; };
// Implement support for: // Implement support for:

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

@ -13,7 +13,6 @@ const React = require("react");
const ReactDOM = require("react-dom"); const ReactDOM = require("react-dom");
const { bindActionCreators } = require("redux"); const { bindActionCreators } = require("redux");
const { bootstrap, renderRoot } = require("devtools-launchpad"); const { bootstrap, renderRoot } = require("devtools-launchpad");
const EventEmitter = require("devtools-modules/src/utils/event-emitter");
const { Services: { appinfo, pref }} = require("devtools-modules"); const { Services: { appinfo, pref }} = require("devtools-modules");
// Initialize preferences as early as possible // Initialize preferences as early as possible
@ -39,15 +38,17 @@ pref("devtools.netmonitor.har.enableAutoExportToFile", false);
pref("devtools.netmonitor.persistlog", false); pref("devtools.netmonitor.persistlog", false);
pref("devtools.styleeditor.enabled", true); pref("devtools.styleeditor.enabled", true);
const { configureStore } = require("./src/utils/create-store");
require("./src/assets/styles/netmonitor.css"); require("./src/assets/styles/netmonitor.css");
const EventEmitter = require("devtools-modules/src/utils/event-emitter");
EventEmitter.decorate(window); EventEmitter.decorate(window);
const { configureStore } = require("./src/utils/create-store");
const App = require("./src/components/app"); const App = require("./src/components/app");
const store = configureStore(); const { Connector } = require("./src/connector/index");
const connector = new Connector();
const store = configureStore(connector);
const actions = bindActionCreators(require("./src/actions"), store.dispatch); const actions = bindActionCreators(require("./src/actions"), store.dispatch);
const { onConnect } = require("./src/connector");
// Inject to global window for testing // Inject to global window for testing
window.store = store; window.store = store;
@ -80,6 +81,7 @@ bootstrap(React, ReactDOM).then((connection) => {
if (!connection) { if (!connection) {
return; return;
} }
renderRoot(React, ReactDOM, App, store);
onConnect(connection, actions, store.getState); renderRoot(React, ReactDOM, App, store, {connector});
connector.connect(connection, actions, store.getState);
}); });

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

@ -4,7 +4,6 @@
"use strict"; "use strict";
const { sendHTTPRequest } = require("../connector/index");
const { const {
ADD_REQUEST, ADD_REQUEST,
CLEAR_REQUESTS, CLEAR_REQUESTS,
@ -47,7 +46,7 @@ function cloneSelectedRequest() {
/** /**
* Send a new HTTP request using the data in the custom request form. * Send a new HTTP request using the data in the custom request form.
*/ */
function sendCustomRequest() { function sendCustomRequest(connector) {
return (dispatch, getState) => { return (dispatch, getState) => {
const selected = getSelectedRequest(getState()); const selected = getSelectedRequest(getState());
@ -68,7 +67,7 @@ function sendCustomRequest() {
data.body = selected.requestPostData.postData.text; data.body = selected.requestPostData.postData.text;
} }
sendHTTPRequest(data, (response) => { connector.sendHTTPRequest(data, (response) => {
return dispatch({ return dispatch({
type: SEND_CUSTOM_REQUEST, type: SEND_CUSTOM_REQUEST,
id: response.eventActor.actor, id: response.eventActor.actor,

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

@ -15,7 +15,6 @@ const {
TOGGLE_COLUMN, TOGGLE_COLUMN,
WATERFALL_RESIZE, WATERFALL_RESIZE,
} = require("../constants"); } = require("../constants");
const { triggerActivity } = require("../connector/index");
/** /**
* Change network details panel. * Change network details panel.
@ -56,11 +55,12 @@ function disableBrowserCache(disabled) {
/** /**
* Change performance statistics panel open state. * Change performance statistics panel open state.
* *
* @param {Object} connector - connector object to the backend
* @param {boolean} visible - expected performance statistics panel open state * @param {boolean} visible - expected performance statistics panel open state
*/ */
function openStatistics(open) { function openStatistics(connector, open) {
if (open) { if (open) {
triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED); connector.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED);
} }
return { return {
type: OPEN_STATISTICS, type: OPEN_STATISTICS,
@ -139,9 +139,9 @@ function toggleBrowserCache() {
/** /**
* Toggle performance statistics panel. * Toggle performance statistics panel.
*/ */
function toggleStatistics() { function toggleStatistics(connector) {
return (dispatch, getState) => return (dispatch, getState) =>
dispatch(openStatistics(!getState().ui.statisticsOpen)); dispatch(openStatistics(connector, !getState().ui.statisticsOpen));
} }
module.exports = { module.exports = {

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

@ -17,18 +17,25 @@ const StatisticsPanel = createFactory(require("./statistics-panel"));
const { div } = DOM; const { div } = DOM;
/* /**
* App component * App component
* The top level component for representing main panel * The top level component for representing main panel
*/ */
function App({ function App({
connector,
openLink, openLink,
sourceMapService, sourceMapService,
statisticsOpen, statisticsOpen,
}) { }) {
return ( return (
div({ className: "network-monitor" }, div({ className: "network-monitor" },
!statisticsOpen ? MonitorPanel({ openLink, sourceMapService }) : StatisticsPanel() !statisticsOpen ? MonitorPanel({
connector,
sourceMapService,
openLink,
}) : StatisticsPanel({
connector
})
) )
); );
} }
@ -36,9 +43,13 @@ function App({
App.displayName = "App"; App.displayName = "App";
App.propTypes = { App.propTypes = {
// The backend connector object.
connector: PropTypes.object.isRequired,
// Callback for opening links in the UI
openLink: PropTypes.func, openLink: PropTypes.func,
// Service to enable the source map feature. // Service to enable the source map feature.
sourceMapService: PropTypes.object, sourceMapService: PropTypes.object,
// True if the stats panel is opened.
statisticsOpen: PropTypes.bool.isRequired, statisticsOpen: PropTypes.bool.isRequired,
}; };

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

@ -148,6 +148,7 @@ function CustomRequestPanel({
CustomRequestPanel.displayName = "CustomRequestPanel"; CustomRequestPanel.displayName = "CustomRequestPanel";
CustomRequestPanel.propTypes = { CustomRequestPanel.propTypes = {
connector: PropTypes.object.isRequired,
removeSelectedCustomRequest: PropTypes.func.isRequired, removeSelectedCustomRequest: PropTypes.func.isRequired,
request: PropTypes.object, request: PropTypes.object,
sendCustomRequest: PropTypes.func.isRequired, sendCustomRequest: PropTypes.func.isRequired,
@ -249,9 +250,9 @@ function updateCustomRequestFields(evt, request, updateRequest) {
module.exports = connect( module.exports = connect(
(state) => ({ request: getSelectedRequest(state) }), (state) => ({ request: getSelectedRequest(state) }),
(dispatch) => ({ (dispatch, props) => ({
removeSelectedCustomRequest: () => dispatch(Actions.removeSelectedCustomRequest()), removeSelectedCustomRequest: () => dispatch(Actions.removeSelectedCustomRequest()),
sendCustomRequest: () => dispatch(Actions.sendCustomRequest()), sendCustomRequest: () => dispatch(Actions.sendCustomRequest(props.connector)),
updateRequest: (id, data, batch) => dispatch(Actions.updateRequest(id, data, batch)), updateRequest: (id, data, batch) => dispatch(Actions.updateRequest(id, data, batch)),
}) })
)(CustomRequestPanel); )(CustomRequestPanel);

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

@ -14,7 +14,6 @@ const {
const { connect } = require("devtools/client/shared/vendor/react-redux"); const { connect } = require("devtools/client/shared/vendor/react-redux");
const { findDOMNode } = require("devtools/client/shared/vendor/react-dom"); const { findDOMNode } = require("devtools/client/shared/vendor/react-dom");
const Actions = require("../actions/index"); const Actions = require("../actions/index");
const { getLongString } = require("../connector/index");
const { getFormDataSections } = require("../utils/request-utils"); const { getFormDataSections } = require("../utils/request-utils");
const { getSelectedRequest } = require("../selectors/index"); const { getSelectedRequest } = require("../selectors/index");
@ -26,7 +25,7 @@ const Toolbar = createFactory(require("./toolbar"));
const { div } = DOM; const { div } = DOM;
const MediaQueryList = window.matchMedia("(min-width: 700px)"); const MediaQueryList = window.matchMedia("(min-width: 700px)");
/* /**
* Monitor panel component * Monitor panel component
* The main panel for displaying various network request information * The main panel for displaying various network request information
*/ */
@ -34,11 +33,11 @@ const MonitorPanel = createClass({
displayName: "MonitorPanel", displayName: "MonitorPanel",
propTypes: { propTypes: {
connector: PropTypes.object.isRequired,
isEmpty: PropTypes.bool.isRequired, isEmpty: PropTypes.bool.isRequired,
networkDetailsOpen: PropTypes.bool.isRequired, networkDetailsOpen: PropTypes.bool.isRequired,
openNetworkDetails: PropTypes.func.isRequired, openNetworkDetails: PropTypes.func.isRequired,
request: PropTypes.object, request: PropTypes.object,
// Service to enable the source map feature.
sourceMapService: PropTypes.object, sourceMapService: PropTypes.object,
openLink: PropTypes.func, openLink: PropTypes.func,
updateRequest: PropTypes.func.isRequired, updateRequest: PropTypes.func.isRequired,
@ -72,7 +71,7 @@ const MonitorPanel = createClass({
requestHeaders, requestHeaders,
requestHeadersFromUploadStream, requestHeadersFromUploadStream,
requestPostData, requestPostData,
getLongString, this.props.connector.getLongString,
).then((newFormDataSections) => { ).then((newFormDataSections) => {
updateRequest( updateRequest(
request.id, request.id,
@ -106,6 +105,7 @@ const MonitorPanel = createClass({
render() { render() {
let { let {
connector,
isEmpty, isEmpty,
networkDetailsOpen, networkDetailsOpen,
sourceMapService, sourceMapService,
@ -126,9 +126,10 @@ const MonitorPanel = createClass({
minSize: "50px", minSize: "50px",
maxSize: "80%", maxSize: "80%",
splitterSize: "1px", splitterSize: "1px",
startPanel: RequestList({ isEmpty }), startPanel: RequestList({ isEmpty, connector }),
endPanel: networkDetailsOpen && NetworkDetailsPanel({ endPanel: networkDetailsOpen && NetworkDetailsPanel({
ref: "endPanel", ref: "endPanel",
connector,
sourceMapService, sourceMapService,
openLink, openLink,
}), }),

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

@ -19,10 +19,11 @@ const TabboxPanel = createFactory(require("./tabbox-panel"));
const { div } = DOM; const { div } = DOM;
/* /**
* Network details panel component * Network details panel component
*/ */
function NetworkDetailsPanel({ function NetworkDetailsPanel({
connector,
activeTabId, activeTabId,
cloneSelectedRequest, cloneSelectedRequest,
request, request,
@ -40,12 +41,14 @@ function NetworkDetailsPanel({
TabboxPanel({ TabboxPanel({
activeTabId, activeTabId,
cloneSelectedRequest, cloneSelectedRequest,
connector,
openLink,
request, request,
selectTab, selectTab,
sourceMapService, sourceMapService,
openLink,
}) : }) :
CustomRequestPanel({ CustomRequestPanel({
connector,
request, request,
}) })
) )
@ -55,12 +58,12 @@ function NetworkDetailsPanel({
NetworkDetailsPanel.displayName = "NetworkDetailsPanel"; NetworkDetailsPanel.displayName = "NetworkDetailsPanel";
NetworkDetailsPanel.propTypes = { NetworkDetailsPanel.propTypes = {
connector: PropTypes.object.isRequired,
activeTabId: PropTypes.string, activeTabId: PropTypes.string,
cloneSelectedRequest: PropTypes.func.isRequired, cloneSelectedRequest: PropTypes.func.isRequired,
open: PropTypes.bool, open: PropTypes.bool,
request: PropTypes.object, request: PropTypes.object,
selectTab: PropTypes.func.isRequired, selectTab: PropTypes.func.isRequired,
// Service to enable the source map feature.
sourceMapService: PropTypes.object, sourceMapService: PropTypes.object,
openLink: PropTypes.func, openLink: PropTypes.func,
}; };

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

@ -35,6 +35,7 @@ const RequestListContent = createClass({
displayName: "RequestListContent", displayName: "RequestListContent",
propTypes: { propTypes: {
connector: PropTypes.object.isRequired,
columns: PropTypes.object.isRequired, columns: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
displayedRequests: PropTypes.object.isRequired, displayedRequests: PropTypes.object.isRequired,
@ -51,10 +52,12 @@ const RequestListContent = createClass({
}, },
componentWillMount() { componentWillMount() {
const { dispatch } = this.props; const { dispatch, connector } = this.props;
this.contextMenu = new RequestListContextMenu({ this.contextMenu = new RequestListContextMenu({
cloneSelectedRequest: () => dispatch(Actions.cloneSelectedRequest()), cloneSelectedRequest: () => dispatch(Actions.cloneSelectedRequest()),
openStatistics: (open) => dispatch(Actions.openStatistics(open)), getTabTarget: connector.getTabTarget,
getLongString: connector.getLongString,
openStatistics: (open) => dispatch(Actions.openStatistics(connector, open)),
}); });
this.tooltip = new HTMLTooltip(window.parent.document, { type: "arrow" }); this.tooltip = new HTMLTooltip(window.parent.document, { type: "arrow" });
}, },
@ -156,8 +159,9 @@ const RequestListContent = createClass({
return false; return false;
} }
let { connector } = this.props;
if (requestItem.responseContent && target.closest(".requests-list-icon")) { if (requestItem.responseContent && target.closest(".requests-list-icon")) {
return setTooltipImageContent(tooltip, itemEl, requestItem); return setTooltipImageContent(connector, tooltip, itemEl, requestItem);
} }
return false; return false;

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

@ -12,7 +12,6 @@ const {
} = require("devtools/client/shared/vendor/react"); } = require("devtools/client/shared/vendor/react");
const { connect } = require("devtools/client/shared/vendor/react-redux"); const { connect } = require("devtools/client/shared/vendor/react-redux");
const Actions = require("../actions/index"); const Actions = require("../actions/index");
const { triggerActivity } = require("../connector/index");
const { ACTIVITY_TYPE } = require("../constants"); const { ACTIVITY_TYPE } = require("../constants");
const { L10N } = require("../utils/l10n"); const { L10N } = require("../utils/l10n");
const { getPerformanceAnalysisURL } = require("../utils/mdn-utils"); const { getPerformanceAnalysisURL } = require("../utils/mdn-utils");
@ -37,6 +36,7 @@ const RequestListEmptyNotice = createClass({
displayName: "RequestListEmptyNotice", displayName: "RequestListEmptyNotice",
propTypes: { propTypes: {
connector: PropTypes.object.isRequired,
onReloadClick: PropTypes.func.isRequired, onReloadClick: PropTypes.func.isRequired,
onPerfClick: PropTypes.func.isRequired, onPerfClick: PropTypes.func.isRequired,
}, },
@ -75,8 +75,9 @@ const RequestListEmptyNotice = createClass({
module.exports = connect( module.exports = connect(
undefined, undefined,
dispatch => ({ (dispatch, props) => ({
onPerfClick: () => dispatch(Actions.openStatistics(true)), onPerfClick: () => dispatch(Actions.openStatistics(props.connector, true)),
onReloadClick: () => triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_DEFAULT), onReloadClick: () => props.connector.triggerActivity(
ACTIVITY_TYPE.RELOAD.WITH_CACHE_DEFAULT),
}) })
)(RequestListEmptyNotice); )(RequestListEmptyNotice);

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

@ -21,12 +21,15 @@ const { div } = DOM;
/** /**
* Request panel component * Request panel component
*/ */
function RequestList({ isEmpty }) { function RequestList({
connector,
isEmpty,
}) {
return ( return (
div({ className: "request-list-container" }, div({ className: "request-list-container" },
RequestListHeader(), RequestListHeader(),
isEmpty ? RequestListEmptyNotice() : RequestListContent(), isEmpty ? RequestListEmptyNotice({connector}) : RequestListContent({connector}),
StatusBar(), StatusBar({connector}),
) )
); );
} }
@ -34,6 +37,7 @@ function RequestList({ isEmpty }) {
RequestList.displayName = "RequestList"; RequestList.displayName = "RequestList";
RequestList.propTypes = { RequestList.propTypes = {
connector: PropTypes.object.isRequired,
isEmpty: PropTypes.bool.isRequired, isEmpty: PropTypes.bool.isRequired,
}; };

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

@ -9,14 +9,18 @@ const {
DOM, DOM,
PropTypes, PropTypes,
} = require("devtools/client/shared/vendor/react"); } = require("devtools/client/shared/vendor/react");
const { viewSourceInDebugger } = require("../connector/index");
const { div } = DOM; const { div } = DOM;
// Components // Components
const StackTrace = createFactory(require("devtools/client/shared/components/StackTrace")); const StackTrace = createFactory(require("devtools/client/shared/components/StackTrace"));
/**
* This component represents a side panel responsible for
* rendering stack-trace info for selected request.
*/
function StackTracePanel({ function StackTracePanel({
connector,
openLink, openLink,
request, request,
sourceMapService, sourceMapService,
@ -27,7 +31,9 @@ function StackTracePanel({
div({ className: "panel-container" }, div({ className: "panel-container" },
StackTrace({ StackTrace({
stacktrace, stacktrace,
onViewSourceInDebugger: ({ url, line }) => viewSourceInDebugger(url, line), onViewSourceInDebugger: ({ url, line }) => {
return connector.viewSourceInDebugger(url, line);
},
sourceMapService, sourceMapService,
openLink, openLink,
}), }),
@ -38,8 +44,8 @@ function StackTracePanel({
StackTracePanel.displayName = "StackTracePanel"; StackTracePanel.displayName = "StackTracePanel";
StackTracePanel.propTypes = { StackTracePanel.propTypes = {
connector: PropTypes.object.isRequired,
request: PropTypes.object.isRequired, request: PropTypes.object.isRequired,
// Service to enable the source map feature.
sourceMapService: PropTypes.object, sourceMapService: PropTypes.object,
openLink: PropTypes.func, openLink: PropTypes.func,
}; };

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

@ -44,6 +44,7 @@ const StatisticsPanel = createClass({
displayName: "StatisticsPanel", displayName: "StatisticsPanel",
propTypes: { propTypes: {
connector: PropTypes.object.isRequired,
closeStatistics: PropTypes.func.isRequired, closeStatistics: PropTypes.func.isRequired,
enableRequestFilterTypeOnly: PropTypes.func.isRequired, enableRequestFilterTypeOnly: PropTypes.func.isRequired,
requests: PropTypes.object, requests: PropTypes.object,
@ -302,8 +303,8 @@ module.exports = connect(
(state) => ({ (state) => ({
requests: state.requests.requests.valueSeq(), requests: state.requests.requests.valueSeq(),
}), }),
(dispatch) => ({ (dispatch, props) => ({
closeStatistics: () => dispatch(Actions.openStatistics(false)), closeStatistics: () => dispatch(Actions.openStatistics(props.connector, false)),
enableRequestFilterTypeOnly: (label) => enableRequestFilterTypeOnly: (label) =>
dispatch(Actions.enableRequestFilterTypeOnly(label)), dispatch(Actions.enableRequestFilterTypeOnly(label)),
}) })

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

@ -88,6 +88,7 @@ function StatusBar({ summary, openStatistics, timingMarkers }) {
StatusBar.displayName = "StatusBar"; StatusBar.displayName = "StatusBar";
StatusBar.propTypes = { StatusBar.propTypes = {
connector: PropTypes.object.isRequired,
openStatistics: PropTypes.func.isRequired, openStatistics: PropTypes.func.isRequired,
summary: PropTypes.object.isRequired, summary: PropTypes.object.isRequired,
timingMarkers: PropTypes.object.isRequired, timingMarkers: PropTypes.object.isRequired,
@ -102,7 +103,7 @@ module.exports = connect(
load: getDisplayedTimingMarker(state, "firstDocumentLoadTimestamp"), load: getDisplayedTimingMarker(state, "firstDocumentLoadTimestamp"),
}, },
}), }),
(dispatch) => ({ (dispatch, props) => ({
openStatistics: () => dispatch(Actions.openStatistics(true)), openStatistics: () => dispatch(Actions.openStatistics(props.connector, true)),
}), }),
)(StatusBar); )(StatusBar);

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

@ -31,17 +31,18 @@ const SECURITY_TITLE = L10N.getStr("netmonitor.tab.security");
const STACK_TRACE_TITLE = L10N.getStr("netmonitor.tab.stackTrace"); const STACK_TRACE_TITLE = L10N.getStr("netmonitor.tab.stackTrace");
const TIMINGS_TITLE = L10N.getStr("netmonitor.tab.timings"); const TIMINGS_TITLE = L10N.getStr("netmonitor.tab.timings");
/* /**
* Tabbox panel component * Tabbox panel component
* Display the network request details * Display the network request details
*/ */
function TabboxPanel({ function TabboxPanel({
activeTabId, activeTabId,
cloneSelectedRequest = ()=>{}, cloneSelectedRequest = () => {},
connector,
openLink,
request, request,
selectTab, selectTab,
sourceMapService, sourceMapService,
openLink,
}) { }) {
if (!request) { if (!request) {
return null; return null;
@ -90,7 +91,7 @@ function TabboxPanel({
id: PANELS.STACK_TRACE, id: PANELS.STACK_TRACE,
title: STACK_TRACE_TITLE, title: STACK_TRACE_TITLE,
}, },
StackTracePanel({ request, sourceMapService, openLink }), StackTracePanel({ request, sourceMapService, openLink, connector }),
), ),
request.securityState && request.securityState !== "insecure" && request.securityState && request.securityState !== "insecure" &&
TabPanel({ TabPanel({
@ -108,11 +109,11 @@ TabboxPanel.displayName = "TabboxPanel";
TabboxPanel.propTypes = { TabboxPanel.propTypes = {
activeTabId: PropTypes.string, activeTabId: PropTypes.string,
cloneSelectedRequest: PropTypes.func, cloneSelectedRequest: PropTypes.func,
connector: PropTypes.object.isRequired,
openLink: PropTypes.func,
request: PropTypes.object, request: PropTypes.object,
selectTab: PropTypes.func.isRequired, selectTab: PropTypes.func.isRequired,
// Service to enable the source map feature.
sourceMapService: PropTypes.object, sourceMapService: PropTypes.object,
openLink: PropTypes.func,
}; };
module.exports = connect()(TabboxPanel); module.exports = connect()(TabboxPanel);

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

@ -35,6 +35,14 @@ class ChromeConnector {
this.connector.disconnect(); this.connector.disconnect();
} }
pause() {
this.disconnect();
}
resume() {
this.setup();
}
/** /**
* currently all events are about "navigation" is not support on CDP * currently all events are about "navigation" is not support on CDP
*/ */

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

@ -7,7 +7,6 @@
const Services = require("Services"); const Services = require("Services");
const { TimelineFront } = require("devtools/shared/fronts/timeline"); const { TimelineFront } = require("devtools/shared/fronts/timeline");
const { ACTIVITY_TYPE, EVENTS } = require("../constants"); const { ACTIVITY_TYPE, EVENTS } = require("../constants");
const { getDisplayedRequestById } = require("../selectors/index");
const FirefoxDataProvider = require("./firefox-data-provider"); const FirefoxDataProvider = require("./firefox-data-provider");
class FirefoxConnector { class FirefoxConnector {
@ -21,7 +20,6 @@ class FirefoxConnector {
this.sendHTTPRequest = this.sendHTTPRequest.bind(this); this.sendHTTPRequest = this.sendHTTPRequest.bind(this);
this.setPreferences = this.setPreferences.bind(this); this.setPreferences = this.setPreferences.bind(this);
this.triggerActivity = this.triggerActivity.bind(this); this.triggerActivity = this.triggerActivity.bind(this);
this.inspectRequest = this.inspectRequest.bind(this);
this.getTabTarget = this.getTabTarget.bind(this); this.getTabTarget = this.getTabTarget.bind(this);
this.viewSourceInDebugger = this.viewSourceInDebugger.bind(this); this.viewSourceInDebugger = this.viewSourceInDebugger.bind(this);
@ -43,12 +41,7 @@ class FirefoxConnector {
actions: this.actions, actions: this.actions,
}); });
this.tabTarget.on("will-navigate", this.willNavigate); this.addListeners();
this.tabTarget.on("close", this.disconnect);
this.webConsoleClient.on("networkEvent",
this.dataProvider.onNetworkEvent);
this.webConsoleClient.on("networkEventUpdate",
this.dataProvider.onNetworkEventUpdate);
// Don't start up waiting for timeline markers if the server isn't // Don't start up waiting for timeline markers if the server isn't
// recent enough to emit the markers we're interested in. // recent enough to emit the markers we're interested in.
@ -71,16 +64,38 @@ class FirefoxConnector {
await this.timelineFront.destroy(); await this.timelineFront.destroy();
} }
this.tabTarget.off("will-navigate"); this.removeListeners();
this.tabTarget.off("close");
this.tabTarget = null; this.tabTarget = null;
this.webConsoleClient.off("networkEvent");
this.webConsoleClient.off("networkEventUpdate");
this.webConsoleClient = null; this.webConsoleClient = null;
this.timelineFront = null; this.timelineFront = null;
this.dataProvider = null; this.dataProvider = null;
} }
pause() {
this.removeListeners();
}
resume() {
this.addListeners();
}
addListeners() {
this.tabTarget.on("will-navigate", this.willNavigate);
this.tabTarget.on("close", this.disconnect);
this.webConsoleClient.on("networkEvent",
this.dataProvider.onNetworkEvent);
this.webConsoleClient.on("networkEventUpdate",
this.dataProvider.onNetworkEventUpdate);
}
removeListeners() {
this.tabTarget.off("will-navigate");
this.tabTarget.off("close");
this.webConsoleClient.off("networkEvent");
this.webConsoleClient.off("networkEventUpdate");
}
willNavigate() { willNavigate() {
if (!Services.prefs.getBoolPref("devtools.netmonitor.persistlog")) { if (!Services.prefs.getBoolPref("devtools.netmonitor.persistlog")) {
this.actions.batchReset(); this.actions.batchReset();
@ -214,42 +229,6 @@ class FirefoxConnector {
return Promise.reject(new Error("Invalid activity type")); 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 * Fetches the network information packet from actor server
* *

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

@ -3,83 +3,101 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict"; "use strict";
let connector = {};
function onConnect(connection, actions, getState) { /**
if (!connection || !connection.tab) { * Generic connector wrapper object that is responsible for
return; * instantiating specific connector implementation according
* to the client type.
*/
class Connector {
constructor() {
this.connector = null;
// Bind public API
this.connect = this.connect.bind(this);
this.disconnect = this.disconnect.bind(this);
this.connectChrome = this.connectChrome.bind(this);
this.connectFirefox = this.connectFirefox.bind(this);
this.getLongString = this.getLongString.bind(this);
this.getNetworkRequest = this.getNetworkRequest.bind(this);
this.getTabTarget = this.getTabTarget.bind(this);
this.sendHTTPRequest = this.sendHTTPRequest.bind(this);
this.setPreferences = this.setPreferences.bind(this);
this.triggerActivity = this.triggerActivity.bind(this);
this.viewSourceInDebugger = this.viewSourceInDebugger.bind(this);
} }
let { clientType } = connection.tab; // Connect/Disconnect API
switch (clientType) {
case "chrome": connect(connection, actions, getState) {
onChromeConnect(connection, actions, getState); if (!connection || !connection.tab) {
break; return;
case "firefox": }
onFirefoxConnect(connection, actions, getState);
break; let { clientType } = connection.tab;
default: switch (clientType) {
throw Error(`Unknown client type - ${clientType}`); case "chrome":
this.connectChrome(connection, actions, getState);
break;
case "firefox":
this.connectFirefox(connection, actions, getState);
break;
default:
throw Error(`Unknown client type - ${clientType}`);
}
}
disconnect() {
this.connector && this.connector.disconnect();
}
connectChrome(connection, actions, getState) {
this.connector = require("./chrome-connector");
this.connector.connect(connection, actions, getState);
}
connectFirefox(connection, actions, getState) {
this.connector = require("./firefox-connector");
this.connector.connect(connection, actions, getState);
}
pause() {
this.connector.pause();
}
resume() {
this.connector.resume();
}
// Public API
getLongString() {
return this.connector.getLongString(...arguments);
}
getNetworkRequest() {
return this.connector.getNetworkRequest(...arguments);
}
getTabTarget() {
return this.connector.getTabTarget();
}
sendHTTPRequest() {
return this.connector.sendHTTPRequest(...arguments);
}
setPreferences() {
return this.connector.setPreferences(...arguments);
}
triggerActivity() {
return this.connector.triggerActivity(...arguments);
}
viewSourceInDebugger() {
return this.connector.viewSourceInDebugger(...arguments);
} }
} }
function onDisconnect() { module.exports.Connector = Connector;
connector && connector.disconnect();
}
function onChromeConnect(connection, actions, getState) {
connector = require("./chrome-connector");
connector.connect(connection, actions, getState);
}
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(...arguments);
}
module.exports = {
onConnect,
onChromeConnect,
onFirefoxConnect,
onDisconnect,
getLongString,
getNetworkRequest,
getTabTarget,
inspectRequest,
sendHTTPRequest,
setPreferences,
triggerActivity,
viewSourceInDebugger,
};

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

@ -16,10 +16,9 @@ function* throttleUploadTest(actuallyThrottle) {
info("Starting test... (actuallyThrottle = " + actuallyThrottle + ")"); info("Starting test... (actuallyThrottle = " + actuallyThrottle + ")");
let { store, windowRequire } = monitor.panelWin; let { connector, store, windowRequire } = monitor.panelWin;
let Actions = windowRequire("devtools/client/netmonitor/src/actions/index"); let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
let { setPreferences } = let setPreferences = () => connector.setPreferences;
windowRequire("devtools/client/netmonitor/src/connector/index");
let RequestListContextMenu = windowRequire( let RequestListContextMenu = windowRequire(
"devtools/client/netmonitor/src/request-list-context-menu"); "devtools/client/netmonitor/src/request-list-context-menu");

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

@ -8,15 +8,32 @@ const {
TOGGLE_RECORDING, TOGGLE_RECORDING,
} = require("../constants"); } = require("../constants");
const {
getRecordingState,
} = require("../selectors/index");
/** /**
* Start/stop HTTP traffic recording. * Start/stop HTTP traffic recording.
*
* The UI state of the toolbar toggle button is stored in UI
* reducer and the backend connection is managed here in the
* middleware.
*/ */
function recordingMiddleware(store) { function recordingMiddleware(connector) {
return next => action => { return store => next => action => {
const res = next(action); const res = next(action);
// Pause/resume HTTP monitoring according to
// the user action.
if (action.type === TOGGLE_RECORDING) { if (action.type === TOGGLE_RECORDING) {
// TODO connect/disconnect the backend. let recording = getRecordingState(store.getState());
if (recording) {
connector.resume();
} else {
connector.pause();
}
} }
return res; return res;
}; };
} }

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

@ -10,10 +10,6 @@ const { gDevTools } = require("devtools/client/framework/devtools");
const { saveAs } = require("devtools/client/shared/file-saver"); const { saveAs } = require("devtools/client/shared/file-saver");
const { copyString } = require("devtools/shared/platform/clipboard"); const { copyString } = require("devtools/shared/platform/clipboard");
const { HarExporter } = require("./har/har-exporter"); const { HarExporter } = require("./har/har-exporter");
const {
getLongString,
getTabTarget,
} = require("./connector/index");
const { const {
getSelectedRequest, getSelectedRequest,
getSortedRequests, getSortedRequests,
@ -28,9 +24,13 @@ const {
function RequestListContextMenu({ function RequestListContextMenu({
cloneSelectedRequest, cloneSelectedRequest,
getLongString,
getTabTarget,
openStatistics, openStatistics,
}) { }) {
this.cloneSelectedRequest = cloneSelectedRequest; this.cloneSelectedRequest = cloneSelectedRequest;
this.getLongString = getLongString;
this.getTabTarget = getTabTarget;
this.openStatistics = openStatistics; this.openStatistics = openStatistics;
} }
@ -239,7 +239,7 @@ RequestListContextMenu.prototype = {
* Opens selected item in the debugger * Opens selected item in the debugger
*/ */
openInDebugger() { openInDebugger() {
let toolbox = gDevTools.getToolbox(getTabTarget()); let toolbox = gDevTools.getToolbox(this.getTabTarget());
toolbox.viewSourceInDebugger(this.selectedRequest.url, 0); toolbox.viewSourceInDebugger(this.selectedRequest.url, 0);
}, },
@ -247,7 +247,7 @@ RequestListContextMenu.prototype = {
* Opens selected item in the style editor * Opens selected item in the style editor
*/ */
openInStyleEditor() { openInStyleEditor() {
let toolbox = gDevTools.getToolbox(getTabTarget()); let toolbox = gDevTools.getToolbox(this.getTabTarget());
toolbox.viewSourceInStyleEditor(this.selectedRequest.url, 0); toolbox.viewSourceInStyleEditor(this.selectedRequest.url, 0);
}, },
@ -387,11 +387,11 @@ RequestListContextMenu.prototype = {
}, },
getDefaultHarOptions() { getDefaultHarOptions() {
let form = getTabTarget().form; let form = this.getTabTarget().form;
let title = form.title || form.url; let title = form.title || form.url;
return { return {
getString: getLongString, getString: this.getLongString,
items: this.sortedRequests, items: this.sortedRequests,
title: title title: title
}; };

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

@ -8,19 +8,18 @@ const {
setImageTooltip, setImageTooltip,
getImageDimensions, getImageDimensions,
} = require("devtools/client/shared/widgets/tooltip/ImageTooltipHelper"); } = require("devtools/client/shared/widgets/tooltip/ImageTooltipHelper");
const { getLongString } = require("./connector/index");
const { formDataURI } = require("./utils/request-utils"); const { formDataURI } = require("./utils/request-utils");
const REQUESTS_TOOLTIP_IMAGE_MAX_DIM = 400; // px const REQUESTS_TOOLTIP_IMAGE_MAX_DIM = 400; // px
async function setTooltipImageContent(tooltip, itemEl, requestItem) { async function setTooltipImageContent(connector, tooltip, itemEl, requestItem) {
let { mimeType, text, encoding } = requestItem.responseContent.content; let { mimeType, text, encoding } = requestItem.responseContent.content;
if (!mimeType || !mimeType.includes("image/")) { if (!mimeType || !mimeType.includes("image/")) {
return false; return false;
} }
let string = await getLongString(text); let string = await connector.getLongString(text);
let src = formDataURI(mimeType, encoding, string); let src = formDataURI(mimeType, encoding, string);
let maxDim = REQUESTS_TOOLTIP_IMAGE_MAX_DIM; let maxDim = REQUESTS_TOOLTIP_IMAGE_MAX_DIM;
let { naturalWidth, naturalHeight } = await getImageDimensions(tooltip.doc, src); let { naturalWidth, naturalHeight } = await getImageDimensions(tooltip.doc, src);

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

@ -6,9 +6,14 @@
const Services = require("Services"); const Services = require("Services");
const { applyMiddleware, createStore } = require("devtools/client/shared/vendor/redux"); const { applyMiddleware, createStore } = require("devtools/client/shared/vendor/redux");
// Middleware
const batching = require("../middleware/batching"); const batching = require("../middleware/batching");
const prefs = require("../middleware/prefs"); const prefs = require("../middleware/prefs");
const thunk = require("../middleware/thunk"); const thunk = require("../middleware/thunk");
const recording = require("../middleware/recording");
// Reducers
const rootReducer = require("../reducers/index"); const rootReducer = require("../reducers/index");
const { FilterTypes, Filters } = require("../reducers/filters"); const { FilterTypes, Filters } = require("../reducers/filters");
const { Requests } = require("../reducers/requests"); const { Requests } = require("../reducers/requests");
@ -16,21 +21,40 @@ const { Sort } = require("../reducers/sort");
const { TimingMarkers } = require("../reducers/timing-markers"); const { TimingMarkers } = require("../reducers/timing-markers");
const { UI, Columns } = require("../reducers/ui"); const { UI, Columns } = require("../reducers/ui");
function configureStore() { /**
const getPref = (pref) => { * Configure state and middleware for the Network monitor tool.
try { */
return JSON.parse(Services.prefs.getCharPref(pref)); function configureStore(connector) {
} catch (_) { // Prepare initial state.
return []; const initialState = {
} filters: new Filters({
requestFilterTypes: getFilterState()
}),
requests: new Requests(),
sort: new Sort(),
timingMarkers: new TimingMarkers(),
ui: new UI({
columns: getColumnState()
}),
}; };
let activeFilters = {}; // Prepare middleware.
let filters = getPref("devtools.netmonitor.filters"); let middleware = applyMiddleware(
filters.forEach((filter) => { thunk,
activeFilters[filter] = true; prefs,
}); batching,
recording(connector)
);
return createStore(rootReducer, initialState, middleware);
}
// Helpers
/**
* Get column state from preferences.
*/
function getColumnState() {
let columns = new Columns(); let columns = new Columns();
let visibleColumns = getPref("devtools.netmonitor.visibleColumns"); let visibleColumns = getPref("devtools.netmonitor.visibleColumns");
@ -40,19 +64,27 @@ function configureStore() {
}); });
} }
const initialState = { return columns;
filters: new Filters({ }
requestFilterTypes: new FilterTypes(activeFilters)
}),
requests: new Requests(),
sort: new Sort(),
timingMarkers: new TimingMarkers(),
ui: new UI({
columns,
}),
};
return createStore(rootReducer, initialState, applyMiddleware(thunk, prefs, batching)); /**
* Get filter state from preferences.
*/
function getFilterState() {
let activeFilters = {};
let filters = getPref("devtools.netmonitor.filters");
filters.forEach((filter) => {
activeFilters[filter] = true;
});
return new FilterTypes(activeFilters);
}
function getPref(pref) {
try {
return JSON.parse(Services.prefs.getCharPref(pref));
} catch (_) {
return [];
}
} }
exports.configureStore = configureStore; exports.configureStore = configureStore;

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

@ -139,9 +139,7 @@ NewConsoleOutputWrapper.prototype = {
), ),
openNetworkPanel: (requestId) => { openNetworkPanel: (requestId) => {
return this.toolbox.selectTool("netmonitor").then((panel) => { return this.toolbox.selectTool("netmonitor").then((panel) => {
let { inspectRequest } = panel.panelWin.windowRequire( return panel.panelWin.Netmonitor.inspectRequest(requestId);
"devtools/client/netmonitor/src/connector/index");
return inspectRequest(requestId);
}); });
}, },
sourceMapService: this.toolbox ? this.toolbox.sourceMapURLService : null, sourceMapService: this.toolbox ? this.toolbox.sourceMapURLService : null,

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

@ -1849,14 +1849,11 @@ WebConsoleFrame.prototype = {
openNetworkPanel: function (requestId) { openNetworkPanel: function (requestId) {
let toolbox = gDevTools.getToolbox(this.owner.target); let toolbox = gDevTools.getToolbox(this.owner.target);
// The browser console doesn't have a toolbox. // The browser console doesn't have a toolbox.
if (!toolbox) { if (toolbox) {
return; return toolbox.selectTool("netmonitor").then(panel => {
return panel.panelWin.Netmonitor.inspectRequest(requestId);
});
} }
return toolbox.selectTool("netmonitor").then(panel => {
let { inspectRequest } = panel.panelWin.windowRequire(
"devtools/client/netmonitor/src/connector/index");
return inspectRequest(requestId);
});
}, },
/** /**