Backed out 3 changesets (bug 1436665) for talos damp failures on netmonitor/simple.js. CLOSED TREE

Backed out changeset 09030f59fbea (bug 1436665)
Backed out changeset 513c72b05382 (bug 1436665)
Backed out changeset 4b8fe40857db (bug 1436665)
This commit is contained in:
Cosmin Sabou 2018-04-17 13:05:42 +03:00
parent bd6343c26c
commit c1f14b1d28
22 changed files with 337 additions and 620 deletions

View File

@ -76,11 +76,15 @@ function devtools_page() {
browser.devtools.network.onRequestFinished.removeListener(requestFinishedListener);
};
browser.devtools.network.onRequestFinished.addListener(requestFinishedListener);
}
browser.test.onMessage.addListener(msg => {
if (msg === "addOnRequestFinishedListener") {
browser.devtools.network.onRequestFinished.addListener(requestFinishedListener);
}
function waitForRequestAdded(toolbox) {
return new Promise(resolve => {
let netPanel = toolbox.getPanel("netmonitor");
netPanel.panelWin.once("NetMonitor:RequestAdded", () => {
resolve();
});
});
}
@ -104,26 +108,6 @@ let extData = {
},
};
function waitForRequestAdded(toolbox) {
return new Promise(async resolve => {
let netPanel = await toolbox.getNetMonitorAPI();
netPanel.once("NetMonitor:RequestAdded", () => {
resolve();
});
});
}
async function navigateToolboxTarget(extension, toolbox) {
extension.sendMessage("navigate");
// Wait till the navigation is complete.
await Promise.all([
extension.awaitMessage("tabUpdated"),
extension.awaitMessage("onNavigatedFired"),
waitForRequestAdded(toolbox),
]);
}
/**
* Test for `chrome.devtools.network.onNavigate()` API
*/
@ -178,14 +162,35 @@ add_task(async function test_devtools_network_get_har() {
let toolbox = await gDevTools.showToolbox(target, "webconsole");
info("Developer toolbox opened.");
// Get HAR, it should be empty since no data collected yet.
// Get HAR, it should be empty since the Net panel wasn't selected.
const getHAREmptyPromise = extension.awaitMessage("getHAR-result");
extension.sendMessage("getHAR");
const getHAREmptyResult = await getHAREmptyPromise;
is(getHAREmptyResult.entries.length, 0, "HAR log should be empty");
// Select the Net panel.
await toolbox.selectTool("netmonitor");
// Get HAR again, it should be empty because the Panel is selected
// but no data collected yet.
const getHAREmptyPromiseWithPanel = extension.awaitMessage("getHAR-result");
extension.sendMessage("getHAR");
const emptyResultWithPanel = await getHAREmptyPromiseWithPanel;
is(emptyResultWithPanel.entries.length, 0, "HAR log should be empty");
// Reload the page to collect some HTTP requests.
await navigateToolboxTarget(extension, toolbox);
extension.sendMessage("navigate");
// Wait till the navigation is complete and request
// added into the net panel.
await Promise.all([
extension.awaitMessage("tabUpdated"),
extension.awaitMessage("onNavigatedFired"),
extension.awaitMessage("onRequestFinished"),
extension.awaitMessage("onRequestFinished-callbackExecuted"),
extension.awaitMessage("onRequestFinished-promiseResolved"),
waitForRequestAdded(toolbox),
]);
// Get HAR, it should not be empty now.
const getHARPromise = extension.awaitMessage("getHAR-result");
@ -193,17 +198,6 @@ add_task(async function test_devtools_network_get_har() {
const getHARResult = await getHARPromise;
is(getHARResult.entries.length, 1, "HAR log should not be empty");
// Select the Net panel and reload page again.
await toolbox.selectTool("netmonitor");
await navigateToolboxTarget(extension, toolbox);
// Get HAR again, it should not be empty even if
// the Network panel is selected now.
const getHAREmptyPromiseWithPanel = extension.awaitMessage("getHAR-result");
extension.sendMessage("getHAR");
const emptyResultWithPanel = await getHAREmptyPromiseWithPanel;
is(emptyResultWithPanel.entries.length, 1, "HAR log should not be empty");
// Shutdown
await gDevTools.closeToolbox(target);
@ -223,23 +217,25 @@ add_task(async function test_devtools_network_on_request_finished() {
await extension.startup();
await extension.awaitMessage("ready");
let target = gDevTools.getTargetForTab(tab);
// Open the Toolbox
let toolbox = await gDevTools.showToolbox(target, "webconsole");
let toolbox = await gDevTools.showToolbox(target, "netmonitor");
info("Developer toolbox opened.");
// Wait the extension to subscribe the onRequestFinished listener.
await extension.sendMessage("addOnRequestFinishedListener");
// Reload and wait for onRequestFinished event.
extension.sendMessage("navigate");
// Reload the page
await navigateToolboxTarget(extension, toolbox);
await Promise.all([
extension.awaitMessage("tabUpdated"),
extension.awaitMessage("onNavigatedFired"),
waitForRequestAdded(toolbox),
]);
info("Wait for an onRequestFinished event");
await extension.awaitMessage("onRequestFinished");
// Wait for response content being fetched.
info("Wait for request.getBody results");
let [callbackRes, promiseRes] = await Promise.all([
extension.awaitMessage("onRequestFinished-callbackExecuted"),
extension.awaitMessage("onRequestFinished-promiseResolved"),
@ -249,6 +245,7 @@ add_task(async function test_devtools_network_on_request_finished() {
"The expected content has been retrieved.");
is(callbackRes[1], "text/html; charset=utf-8",
"The expected content has been retrieved.");
is(promiseRes[0], callbackRes[0],
"The resolved value is equal to the one received in the callback API mode");
is(promiseRes[1], callbackRes[1],

View File

@ -74,8 +74,6 @@ loader.lazyRequireGetter(this, "buildHarLog",
"devtools/client/netmonitor/src/har/har-builder-utils", true);
loader.lazyRequireGetter(this, "getKnownDeviceFront",
"devtools/shared/fronts/device", true);
loader.lazyRequireGetter(this, "NetMonitorAPI",
"devtools/client/netmonitor/src/api", true);
loader.lazyGetter(this, "domNodeConstants", () => {
return require("devtools/shared/dom-node-constants");
@ -118,12 +116,14 @@ function Toolbox(target, selectedTool, hostType, contentWindow, frameId) {
this._initInspector = null;
this._inspector = null;
this._styleSheets = null;
this._netMonitorAPI = null;
// Map of frames (id => frame-info) and currently selected frame id.
this.frameMap = new Map();
this.selectedFrameId = null;
// List of listeners for `devtools.network.onRequestFinished` WebExt API
this._requestFinishedListeners = new Set();
this._toolRegistered = this._toolRegistered.bind(this);
this._toolUnregistered = this._toolUnregistered.bind(this);
this._onWillNavigate = this._onWillNavigate.bind(this);
@ -2687,6 +2687,7 @@ Toolbox.prototype = {
this._sourceMapURLService.destroy();
this._sourceMapURLService = null;
}
if (this._sourceMapService) {
this._sourceMapService.stopSourceMapWorker();
this._sourceMapService = null;
@ -2784,11 +2785,6 @@ Toolbox.prototype = {
// target.
deferred.resolve(settleAll(outstanding)
.catch(console.error)
.then(() => {
let api = this._netMonitorAPI;
this._netMonitorAPI = null;
return api ? api.destroy() : null;
}, console.error)
.then(() => {
this._removeHostListeners();
@ -3048,39 +3044,22 @@ Toolbox.prototype = {
// Support for WebExtensions API (`devtools.network.*`)
/**
* Return Netmonitor API object. This object offers Network monitor
* public API that can be consumed by other panels or WE API.
*/
getNetMonitorAPI: async function() {
let netPanel = this.getPanel("netmonitor");
// Return Net panel if it exists.
if (netPanel) {
return netPanel.panelWin.Netmonitor.api;
}
if (this._netMonitorAPI) {
return this._netMonitorAPI;
}
// Create and initialize Network monitor API object.
// This object is only connected to the backend - not to the UI.
this._netMonitorAPI = new NetMonitorAPI();
await this._netMonitorAPI.connect(this);
return this._netMonitorAPI;
},
/**
* Returns data (HAR) collected by the Network panel.
*/
getHARFromNetMonitor: async function() {
let netMonitor = await this.getNetMonitorAPI();
let har = await netMonitor.getHar();
let netPanel = this.getPanel("netmonitor");
// Return default empty HAR file if needed.
har = har || buildHarLog(Services.appinfo);
// The panel doesn't have to exist (it must be selected
// by the user at least once to be created).
// Return default empty HAR log in such case.
if (!netPanel) {
let har = await buildHarLog(Services.appinfo);
return har.log;
}
// Use Netmonitor object to get the current HAR log.
let har = await netPanel.panelWin.Netmonitor.getHar();
// Return the log directly to be compatible with
// Chrome WebExtension API.
@ -3095,26 +3074,24 @@ Toolbox.prototype = {
* a function that takes ({harEntry, requestId})
* as first argument.
*/
addRequestFinishedListener: async function(listener) {
let netMonitor = await this.getNetMonitorAPI();
netMonitor.addRequestFinishedListener(listener);
addRequestFinishedListener: function(listener) {
// Log console message informing the extension developer
// that the Network panel needs to be selected at least
// once in order to receive `onRequestFinished` events.
let message = "The Network panel needs to be selected at least" +
" once in order to receive 'onRequestFinished' events.";
this.target.logWarningInPage(message, "har");
// Add the listener into internal list.
this._requestFinishedListeners.add(listener);
},
removeRequestFinishedListener: async function(listener) {
let netMonitor = await this.getNetMonitorAPI();
netMonitor.removeRequestFinishedListener(listener);
removeRequestFinishedListener: function(listener) {
this._requestFinishedListeners.delete(listener);
},
// Destroy Network monitor API object if the following is true:
// 1) there is no listener
// 2) the Net panel doesn't exist/use the API object (if the panel
// exists it's also responsible for destroying it,
// see `NetMonitorPanel.open` for more details)
let netPanel = this.getPanel("netmonitor");
let hasListeners = netMonitor.hasRequestFinishedListeners();
if (this._netMonitorAPI && !hasListeners && !netPanel) {
this._netMonitorAPI.destroy();
this._netMonitorAPI = null;
}
getRequestFinishedListeners: function() {
return this._requestFinishedListeners;
},
/**
@ -3125,9 +3102,17 @@ Toolbox.prototype = {
* Id of the request for which the response content
* should be fetched.
*/
fetchResponseContent: async function(requestId) {
let netMonitor = await this.getNetMonitorAPI();
return netMonitor.fetchResponseContent(requestId);
fetchResponseContent: function(requestId) {
let netPanel = this.getPanel("netmonitor");
// The panel doesn't have to exist (it must be selected
// by the user at least once to be created).
// Return undefined content in such case.
if (!netPanel) {
return Promise.resolve({content: {}});
}
return netPanel.panelWin.Netmonitor.fetchResponseContent(requestId);
},
// Support management of installed WebExtensions that provide a devtools_page.

View File

@ -1,7 +1,6 @@
/* 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/. */
/* exported initialize */
"use strict";
@ -17,52 +16,185 @@ const require = window.windowRequire = BrowserLoader({
window,
}).require;
const { NetMonitorAPI } = require("./src/api");
const { NetMonitorApp } = require("./src/app");
const EventEmitter = require("devtools/shared/event-emitter");
const { createFactory } = require("devtools/client/shared/vendor/react");
const { render, unmountComponentAtNode } = require("devtools/client/shared/vendor/react-dom");
const Provider = createFactory(require("devtools/client/shared/vendor/react-redux").Provider);
const { bindActionCreators } = require("devtools/client/shared/vendor/redux");
const { Connector } = require("./src/connector/index");
const { configureStore } = require("./src/create-store");
const App = createFactory(require("./src/components/App"));
const { EVENTS } = require("./src/constants");
const {
getDisplayedRequestById,
getSortedRequests
} = require("./src/selectors/index");
// Inject EventEmitter into global window.
EventEmitter.decorate(window);
/**
* This is the initialization point for the Network monitor.
*
* @param {Object} api Allows reusing existing API object.
*/
function initialize(api) {
const app = new NetMonitorApp(api);
// 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
window.Netmonitor = app;
window.api = api;
window.store = app.api.store;
window.connector = app.api.connector;
window.actions = app.api.actions;
return app;
}
// Inject to global window for testing
window.store = store;
window.connector = connector;
window.actions = actions;
/**
* The following code is used to open Network monitor in a tab.
* Like the Launchpad, but without Launchpad.
*
* For example:
* chrome://devtools/content/netmonitor/index.html?type=process
* loads the netmonitor for the parent process, exactly like the
* one in the browser toolbox
*
* It's also possible to connect to a tab.
* 1) go in about:debugging
* 2) In menu Tabs, click on a Debug button for particular tab
*
* This will open an about:devtools-toolbox url, from which you can
* take type and id query parameters and reuse them for the chrome url
* of the netmonitor
*
* chrome://devtools/content/netmonitor/index.html?type=tab&id=1234 URLs
* where 1234 is the tab id, you can retrieve from about:debugging#tabs links.
* Simply copy the id from about:devtools-toolbox?type=tab&id=1234 URLs.
* Global Netmonitor object in this panel. This object can be consumed
* by other panels (e.g. Console is using inspectRequest), by the
* Launchpad (bootstrap), WebExtension API (getHAR), etc.
*/
window.Netmonitor = {
bootstrap({ toolbox, panel }) {
this.mount = document.querySelector("#mount");
this.toolbox = toolbox;
const connection = {
tabConnection: {
tabTarget: toolbox.target,
},
toolbox,
panel,
};
const openLink = (link) => {
let parentDoc = toolbox.doc;
let iframe = parentDoc.getElementById("toolbox-panel-iframe-netmonitor");
let top = iframe.ownerDocument.defaultView.top;
top.openWebLinkIn(link, "tab");
};
const openSplitConsole = (err) => {
toolbox.openSplitConsole().then(() => {
toolbox.target.logErrorInPage(err, "har");
});
};
this.onRequestAdded = this.onRequestAdded.bind(this);
window.on(EVENTS.REQUEST_ADDED, this.onRequestAdded);
// Render the root Application component.
const sourceMapService = toolbox.sourceMapURLService;
const app = App({
actions,
connector,
openLink,
openSplitConsole,
sourceMapService
});
render(Provider({ store }, app), this.mount);
// Connect to the Firefox backend by default.
return connector.connectFirefox(connection, actions, store.getState);
},
destroy() {
unmountComponentAtNode(this.mount);
window.off(EVENTS.REQUEST_ADDED, this.onRequestAdded);
return connector.disconnect();
},
// Support for WebExtensions API
/**
* Support for `devtools.network.getHAR` (get collected data as HAR)
*/
getHar() {
let { HarExporter } = require("devtools/client/netmonitor/src/har/har-exporter");
let state = store.getState();
let options = {
connector,
items: getSortedRequests(state),
// Always generate HAR log even if there are no requests.
forceExport: true,
};
return HarExporter.getHar(options);
},
/**
* Support for `devtools.network.onRequestFinished`. A hook for
* every finished HTTP request used by WebExtensions API.
*/
onRequestAdded(requestId) {
let listeners = this.toolbox.getRequestFinishedListeners();
if (!listeners.size) {
return;
}
let { HarExporter } = require("devtools/client/netmonitor/src/har/har-exporter");
let options = {
connector,
includeResponseBodies: false,
items: [getDisplayedRequestById(store.getState(), requestId)],
};
// Build HAR for specified request only.
HarExporter.getHar(options).then(har => {
let harEntry = har.log.entries[0];
delete harEntry.pageref;
listeners.forEach(listener => listener({
harEntry,
requestId,
}));
});
},
/**
* Support for `Request.getContent` WebExt API (lazy loading response body)
*/
fetchResponseContent(requestId) {
return connector.requestData(requestId, "responseContent");
},
/**
* 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:
// chrome://devtools/content/netmonitor/index.html?type=tab&id=1234 URLs
// where 1234 is the tab id, you can retrieve from about:debugging#tabs links.
// Simply copy the id from about:devtools-toolbox?type=tab&id=1234 URLs.
// URL constructor doesn't support chrome: scheme
let href = window.location.href.replace(/chrome:/, "http://");
@ -90,13 +222,7 @@ if (window.location.protocol === "chrome:" && url.search.length > 1) {
}
};
let api = new NetMonitorAPI();
await api.connect(toolbox);
let app = window.initialize(api);
app.bootstrap({
toolbox,
document: window.document,
});
window.Netmonitor.bootstrap({ toolbox });
} catch (err) {
window.alert("Unable to start the network monitor:" + err);
}

View File

@ -14,22 +14,12 @@ NetMonitorPanel.prototype = {
if (!this.toolbox.target.isRemote) {
await this.toolbox.target.makeRemote();
}
// Reuse an existing Network monitor API object if available.
// It could have been created for WE API before Net panel opens.
let api = await this.toolbox.getNetMonitorAPI();
let app = this.panelWin.initialize(api);
// Connect the application object to the UI.
await app.bootstrap({
await this.panelWin.Netmonitor.bootstrap({
toolbox: this.toolbox,
document: this.panelWin.document,
panel: this,
});
// Ready to go!
this.emit("ready");
this.isReady = true;
return this;
},

View File

@ -1,197 +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 EventEmitter = require("devtools/shared/event-emitter");
const { bindActionCreators } = require("devtools/client/shared/vendor/redux");
const { Connector } = require("./connector/index");
const { configureStore } = require("./create-store");
const { EVENTS } = require("./constants");
const Actions = require("./actions/index");
const {
getDisplayedRequestById,
getSortedRequests
} = require("./selectors/index");
/**
* API object for NetMonitor panel (like a facade). This object can be
* consumed by other panels, WebExtension API, etc.
*
* This object doesn't depend on the panel UI and can be created
* and used even if the Network panel UI doesn't exist.
*/
function NetMonitorAPI() {
EventEmitter.decorate(this);
// Connector to the backend.
this.connector = new Connector();
// Configure store/state object.
this.store = configureStore(this.connector);
// List of listeners for `devtools.network.onRequestFinished` WebExt API
this._requestFinishedListeners = new Set();
// Bind event handlers
this.onRequestAdded = this.onRequestAdded.bind(this);
this.actions = bindActionCreators(Actions, this.store.dispatch);
}
NetMonitorAPI.prototype = {
async connect(toolbox) {
// Bail out if already connected.
if (this.toolbox) {
return;
}
this.toolbox = toolbox;
// Register listener for new requests (utilized by WebExtension API).
this.on(EVENTS.REQUEST_ADDED, this.onRequestAdded);
// Initialize connection to the backend. Pass `this` as the owner,
// so this object can receive all emitted events.
const connection = {
tabConnection: {
tabTarget: toolbox.target,
},
toolbox,
owner: this,
};
await this.connectBackend(this.connector, connection, this.actions,
this.store.getState);
},
/**
* Clean up (unmount from DOM, remove listeners, disconnect).
*/
async destroy() {
this.off(EVENTS.REQUEST_ADDED, this.onRequestAdded);
await this.connector.disconnect();
if (this.harExportConnector) {
await this.harExportConnector.disconnect();
}
},
/**
* Connect to the Firefox backend by default.
*
* As soon as connections to different back-ends is supported
* this function should be responsible for picking the right API.
*/
async connectBackend(connector, connection, actions, getState) {
// The connection might happen during Toolbox initialization
// so make sure the target is ready.
await connection.tabConnection.tabTarget.makeRemote();
return connector.connectFirefox(connection, actions, getState);
},
// HAR
/**
* Support for `devtools.network.getHAR` (get collected data as HAR)
*/
async getHar() {
let { HarExporter } = require("devtools/client/netmonitor/src/har/har-exporter");
let state = this.store.getState();
let options = {
connector: this.connector,
items: getSortedRequests(state),
};
return HarExporter.getHar(options);
},
/**
* Support for `devtools.network.onRequestFinished`. A hook for
* every finished HTTP request used by WebExtensions API.
*/
async onRequestAdded(requestId) {
if (!this._requestFinishedListeners.size) {
return;
}
let { HarExporter } = require("devtools/client/netmonitor/src/har/har-exporter");
let connector = await this.getHarExportConnector();
let request = getDisplayedRequestById(this.store.getState(), requestId);
if (!request) {
console.error("HAR: request not found " + requestId);
return;
}
let options = {
connector,
includeResponseBodies: false,
items: [request],
};
let har = await HarExporter.getHar(options);
// There is page so remove the page reference.
let harEntry = har.log.entries[0];
delete harEntry.pageref;
this._requestFinishedListeners.forEach(listener => listener({
harEntry,
requestId,
}));
},
/**
* Support for `Request.getContent` WebExt API (lazy loading response body)
*/
async fetchResponseContent(requestId) {
return this.connector.requestData(requestId, "responseContent");
},
/**
* Add listener for `onRequestFinished` events.
*
* @param {Object} listener
* The listener to be called it's expected to be
* a function that takes ({harEntry, requestId})
* as first argument.
*/
addRequestFinishedListener: function(listener) {
this._requestFinishedListeners.add(listener);
},
removeRequestFinishedListener: function(listener) {
this._requestFinishedListeners.delete(listener);
},
hasRequestFinishedListeners: function() {
return this._requestFinishedListeners.size > 0;
},
/**
* Separate connector for HAR export.
*/
async getHarExportConnector() {
if (this.harExportConnector) {
return this.harExportConnector;
}
const connection = {
tabConnection: {
tabTarget: this.toolbox.target,
},
toolbox: this.toolbox,
};
this.harExportConnector = new Connector();
await this.connectBackend(this.harExportConnector, connection);
return this.harExportConnector;
},
};
exports.NetMonitorAPI = NetMonitorAPI;

View File

@ -1,124 +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 { createFactory } = require("devtools/client/shared/vendor/react");
const { render, unmountComponentAtNode } = require("devtools/client/shared/vendor/react-dom");
const Provider = createFactory(require("devtools/client/shared/vendor/react-redux").Provider);
const App = createFactory(require("./components/App"));
const { EVENTS } = require("./constants");
const {
getDisplayedRequestById,
} = require("./selectors/index");
/**
* Global App object for Network panel. This object depends
* on the UI and can't be created independently.
*
* This object can be consumed by other panels (e.g. Console
* is using inspectRequest), by the Launchpad (bootstrap), etc.
*
* @param {Object} api An existing API object to be reused.
*/
function NetMonitorApp(api) {
this.api = api;
}
NetMonitorApp.prototype = {
async bootstrap({ toolbox, document }) {
// Get the root element for mounting.
this.mount = document.querySelector("#mount");
const openLink = (link) => {
let parentDoc = toolbox.doc;
let iframe = parentDoc.getElementById("toolbox-panel-iframe-netmonitor");
let top = iframe.ownerDocument.defaultView.top;
top.openUILinkIn(link, "tab");
};
const openSplitConsole = (err) => {
toolbox.openSplitConsole().then(() => {
toolbox.target.logErrorInPage(err, "har");
});
};
let {
actions,
connector,
store,
} = this.api;
const sourceMapService = toolbox.sourceMapURLService;
const app = App({
actions,
connector,
openLink,
openSplitConsole,
sourceMapService
});
// Render the root Application component.
render(Provider({ store: store }, app), this.mount);
},
/**
* Clean up (unmount from DOM, remove listeners, disconnect).
*/
async destroy() {
unmountComponentAtNode(this.mount);
// Make sure to destroy the API object. It's usually destroyed
// in the Toolbox destroy method, but we need it here for case
// where the Network panel is initialized without the toolbox
// and running in a tab (see initialize.js for details).
await this.api.destroy();
},
/**
* 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.
*/
async inspectRequest(requestId) {
let {
actions,
store,
} = this.api;
// 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) {
this.api.off(EVENTS.REQUEST_ADDED, inspector);
actions.selectRequest(request.id);
resolve();
}
};
inspector();
if (!request) {
this.api.on(EVENTS.REQUEST_ADDED, inspector);
}
});
}
};
exports.NetMonitorApp = NetMonitorApp;

View File

@ -98,4 +98,4 @@ class ChromeConnector {
}
}
module.exports = ChromeConnector;
module.exports = new ChromeConnector();

View File

@ -35,28 +35,18 @@ class FirefoxConnector {
this.getNetworkRequest = this.getNetworkRequest.bind(this);
}
/**
* Connect to the backend.
*
* @param {Object} connection object with e.g. reference to the Toolbox.
* @param {Object} actions (optional) is used to fire Redux actions to update store.
* @param {Object} getState (optional) is used to get access to the state.
*/
async connect(connection, actions, getState) {
this.actions = actions;
this.getState = getState;
this.tabTarget = connection.tabConnection.tabTarget;
this.toolbox = connection.toolbox;
// The owner object (NetMonitorAPI) received all events.
this.owner = connection.owner;
this.panel = connection.panel;
this.webConsoleClient = this.tabTarget.activeConsole;
this.dataProvider = new FirefoxDataProvider({
webConsoleClient: this.webConsoleClient,
actions: this.actions,
owner: this.owner,
});
await this.addListeners();
@ -66,32 +56,25 @@ class FirefoxConnector {
// these are used to pause/resume the connector.
// Paused network panel should be automatically resumed when page
// reload, so `will-navigate` listener needs to be there all the time.
if (this.tabTarget) {
this.tabTarget.on("will-navigate", this.willNavigate);
this.tabTarget.on("navigate", this.navigate);
}
this.tabTarget.on("will-navigate", this.willNavigate);
this.tabTarget.on("navigate", this.navigate);
// Displaying cache events is only intended for the UI panel.
if (this.actions) {
this.displayCachedEvents();
}
this.displayCachedEvents();
}
async disconnect() {
if (this.actions) {
this.actions.batchReset();
}
this.actions.batchReset();
await this.removeListeners();
if (this.tabTarget) {
this.tabTarget.off("will-navigate", this.willNavigate);
this.tabTarget.off("navigate", this.navigate);
this.tabTarget.off("will-navigate");
this.tabTarget = null;
}
this.webConsoleClient = null;
this.dataProvider = null;
this.panel = null;
}
async pause() {
@ -149,22 +132,18 @@ class FirefoxConnector {
}
willNavigate() {
if (this.actions) {
if (!Services.prefs.getBoolPref("devtools.netmonitor.persistlog")) {
this.actions.batchReset();
this.actions.clearRequests();
} else {
// If the log is persistent, just clear all accumulated timing markers.
this.actions.clearTimingMarkers();
}
if (!Services.prefs.getBoolPref("devtools.netmonitor.persistlog")) {
this.actions.batchReset();
this.actions.clearRequests();
} else {
// If the log is persistent, just clear all accumulated timing markers.
this.actions.clearTimingMarkers();
}
// Resume is done automatically on page reload/navigation.
if (this.actions && this.getState) {
let state = this.getState();
if (!state.requests.recording) {
this.actions.toggleRecording();
}
let state = this.getState();
if (!state.requests.recording) {
this.actions.toggleRecording();
}
}
@ -177,24 +156,19 @@ class FirefoxConnector {
if (this.dataProvider && !this.dataProvider.isPayloadQueueEmpty()) {
return;
}
if (this.owner) {
this.owner.off(EVENTS.PAYLOAD_READY, listener);
}
window.off(EVENTS.PAYLOAD_READY, listener);
// Netmonitor may already be destroyed,
// so do not try to notify the listeners
if (this.dataProvider) {
this.onReloaded();
}
};
if (this.owner) {
this.owner.on(EVENTS.PAYLOAD_READY, listener);
}
window.on(EVENTS.PAYLOAD_READY, listener);
}
onReloaded() {
let panel = this.toolbox.getPanel("netmonitor");
if (panel) {
panel.emit("reloaded");
if (this.panel) {
this.panel.emit("reloaded");
}
}
@ -230,12 +204,8 @@ class FirefoxConnector {
"dom-interactive" : "dom-complete",
time: marker.unixTime / 1000
};
if (this.actions) {
this.actions.addTimingMarker(event);
}
this.emit(EVENTS.TIMELINE_EVENT, event);
this.actions.addTimingMarker(event);
window.emit(EVENTS.TIMELINE_EVENT, event);
}
/**
@ -246,11 +216,8 @@ class FirefoxConnector {
* @param {object} marker
*/
onDocEvent(event) {
if (this.actions) {
this.actions.addTimingMarker(event);
}
this.emit(EVENTS.TIMELINE_EVENT, event);
this.actions.addTimingMarker(event);
window.emit(EVENTS.TIMELINE_EVENT, event);
}
/**
@ -404,22 +371,9 @@ class FirefoxConnector {
}
getTimingMarker(name) {
if (!this.getState) {
return -1;
}
let state = this.getState();
return getDisplayedTimingMarker(state, name);
}
/**
* Fire events for the owner object.
*/
emit(type, data) {
if (this.owner) {
this.owner.emit(type, data);
}
}
}
module.exports = FirefoxConnector;
module.exports = new FirefoxConnector();

View File

@ -18,19 +18,11 @@ const { fetchHeaders } = require("../utils/request-utils");
* or not.
*/
class FirefoxDataProvider {
/**
* Constructor for data provider
*
* @param {Object} webConcoleClient represents the client object for Console actor.
* @param {Object} actions set of actions fired during data fetching process
* @params {Object} owner all events are fired on this object
*/
constructor({webConsoleClient, actions, owner}) {
constructor({webConsoleClient, actions}) {
// Options
this.webConsoleClient = webConsoleClient;
this.actions = actions || {};
this.actions = actions;
this.actionsEnabled = true;
this.owner = owner;
// Internal properties
this.payloadQueue = new Map();
@ -92,7 +84,7 @@ class FirefoxDataProvider {
}, true);
}
this.emit(EVENTS.REQUEST_ADDED, id);
emit(EVENTS.REQUEST_ADDED, id);
}
/**
@ -322,7 +314,7 @@ class FirefoxDataProvider {
url,
});
this.emit(EVENTS.NETWORK_EVENT, actor);
emit(EVENTS.NETWORK_EVENT, actor);
}
/**
@ -349,7 +341,7 @@ class FirefoxDataProvider {
statusText: networkInfo.response.statusText,
headersSize: networkInfo.response.headersSize
});
this.emit(EVENTS.STARTED_RECEIVING_RESPONSE, actor);
emit(EVENTS.STARTED_RECEIVING_RESPONSE, actor);
break;
case "responseContent":
this.pushRequestToQueue(actor, {
@ -375,7 +367,7 @@ class FirefoxDataProvider {
this.onPayloadDataReceived(actor);
this.emit(EVENTS.NETWORK_EVENT_UPDATED, actor);
emit(EVENTS.NETWORK_EVENT_UPDATED, actor);
}
/**
@ -401,7 +393,7 @@ class FirefoxDataProvider {
// This event is fired only once per request, once all the properties are fetched
// from `onNetworkEventUpdate`. There should be no more RDP requests after this.
this.emit(EVENTS.PAYLOAD_READY, actor);
emit(EVENTS.PAYLOAD_READY, actor);
}
/**
@ -471,7 +463,7 @@ class FirefoxDataProvider {
let updatingEventName = `UPDATING_${method.replace(/([A-Z])/g, "_$1").toUpperCase()}`;
// Emit event that tell we just start fetching some data
this.emit(EVENTS[updatingEventName], actor);
emit(EVENTS[updatingEventName], actor);
let response = await new Promise((resolve, reject) => {
// Do a RDP request to fetch data from the actor.
@ -508,7 +500,7 @@ class FirefoxDataProvider {
let payload = await this.updateRequest(response.from, {
requestHeaders: response
});
this.emit(EVENTS.RECEIVED_REQUEST_HEADERS, response.from);
emit(EVENTS.RECEIVED_REQUEST_HEADERS, response.from);
return payload.requestHeaders;
}
@ -521,7 +513,7 @@ class FirefoxDataProvider {
let payload = await this.updateRequest(response.from, {
responseHeaders: response
});
this.emit(EVENTS.RECEIVED_RESPONSE_HEADERS, response.from);
emit(EVENTS.RECEIVED_RESPONSE_HEADERS, response.from);
return payload.responseHeaders;
}
@ -534,7 +526,7 @@ class FirefoxDataProvider {
let payload = await this.updateRequest(response.from, {
requestCookies: response
});
this.emit(EVENTS.RECEIVED_REQUEST_COOKIES, response.from);
emit(EVENTS.RECEIVED_REQUEST_COOKIES, response.from);
return payload.requestCookies;
}
@ -547,7 +539,7 @@ class FirefoxDataProvider {
let payload = await this.updateRequest(response.from, {
requestPostData: response
});
this.emit(EVENTS.RECEIVED_REQUEST_POST_DATA, response.from);
emit(EVENTS.RECEIVED_REQUEST_POST_DATA, response.from);
return payload.requestPostData;
}
@ -560,7 +552,7 @@ class FirefoxDataProvider {
let payload = await this.updateRequest(response.from, {
securityInfo: response.securityInfo
});
this.emit(EVENTS.RECEIVED_SECURITY_INFO, response.from);
emit(EVENTS.RECEIVED_SECURITY_INFO, response.from);
return payload.securityInfo;
}
@ -573,7 +565,7 @@ class FirefoxDataProvider {
let payload = await this.updateRequest(response.from, {
responseCookies: response
});
this.emit(EVENTS.RECEIVED_RESPONSE_COOKIES, response.from);
emit(EVENTS.RECEIVED_RESPONSE_COOKIES, response.from);
return payload.responseCookies;
}
@ -590,7 +582,7 @@ class FirefoxDataProvider {
mimeType: response.content.mimeType,
responseContent: response,
});
this.emit(EVENTS.RECEIVED_RESPONSE_CONTENT, response.from);
emit(EVENTS.RECEIVED_RESPONSE_CONTENT, response.from);
return payload.responseContent;
}
@ -603,7 +595,7 @@ class FirefoxDataProvider {
let payload = await this.updateRequest(response.from, {
eventTimings: response
});
this.emit(EVENTS.RECEIVED_EVENT_TIMINGS, response.from);
emit(EVENTS.RECEIVED_EVENT_TIMINGS, response.from);
return payload.eventTimings;
}
@ -616,17 +608,17 @@ class FirefoxDataProvider {
let payload = await this.updateRequest(response.from, {
stacktrace: response.stacktrace
});
this.emit(EVENTS.RECEIVED_EVENT_STACKTRACE, response.from);
emit(EVENTS.RECEIVED_EVENT_STACKTRACE, response.from);
return payload.stacktrace;
}
}
/**
* Fire events for the owner object.
*/
emit(type, data) {
if (this.owner) {
this.owner.emit(type, data);
}
/**
* Guard 'emit' to avoid exception in non-window environment.
*/
function emit(type, data) {
if (typeof window != "undefined") {
window.emit(type, data);
}
}

View File

@ -54,14 +54,12 @@ class Connector {
}
connectChrome(connection, actions, getState) {
let ChromeConnector = require("./chrome-connector");
this.connector = new ChromeConnector();
this.connector = require("./chrome-connector");
return this.connector.connect(connection, actions, getState);
}
connectFirefox(connection, actions, getState) {
let FirefoxConnector = require("./firefox-connector");
this.connector = new FirefoxConnector();
this.connector = require("./firefox-connector");
return this.connector.connect(connection, actions, getState);
}

View File

@ -126,9 +126,7 @@ const HarExporter = {
* Configuration object, see save() for detailed description.
*/
getHar: function(options) {
return this.fetchHarData(options).then(data => {
return data ? JSON.parse(data) : null;
});
return this.fetchHarData(options).then(JSON.parse);
},
// Helpers

View File

@ -15,8 +15,6 @@ DIRS += [
]
DevToolsModules(
'api.js',
'app.js',
'constants.js',
'create-store.js',
)

View File

@ -48,7 +48,7 @@ add_task(async function() {
});
wait = waitForDOM(document, ".CodeMirror-code");
let onResponseContent = monitor.panelWin.api.once(EVENTS.RECEIVED_RESPONSE_CONTENT);
let onResponseContent = monitor.panelWin.once(EVENTS.RECEIVED_RESPONSE_CONTENT);
store.dispatch(Actions.toggleNetworkDetails());
EventUtils.sendMouseEvent({ type: "click" },
document.querySelector("#response-tab"));

View File

@ -32,7 +32,7 @@ add_task(async function() {
});
let onNetworkEvents = waitForNetworkEvents(monitor, 1);
let onEventTimings = waitFor(monitor.panelWin.api, EVENTS.RECEIVED_EVENT_TIMINGS);
let onEventTimings = waitFor(monitor.panelWin, EVENTS.RECEIVED_EVENT_TIMINGS);
tab.linkedBrowser.reload();
await Promise.all([onNetworkEvents, onEventTimings]);

View File

@ -268,7 +268,7 @@ add_task(async function() {
}
async function selectIndexAndWaitForJSONView(index) {
let onResponseContent = monitor.panelWin.api.once(EVENTS.RECEIVED_RESPONSE_CONTENT);
let onResponseContent = monitor.panelWin.once(EVENTS.RECEIVED_RESPONSE_CONTENT);
let tabpanel = document.querySelector("#response-panel");
let waitDOM = waitForDOM(tabpanel, ".treeTable");
store.dispatch(Actions.selectRequestByIndex(index));
@ -281,7 +281,7 @@ add_task(async function() {
}
async function selectIndexAndWaitForImageView(index) {
let onResponseContent = monitor.panelWin.api.once(EVENTS.RECEIVED_RESPONSE_CONTENT);
let onResponseContent = monitor.panelWin.once(EVENTS.RECEIVED_RESPONSE_CONTENT);
let tabpanel = document.querySelector("#response-panel");
let waitDOM = waitForDOM(tabpanel, ".response-image");
store.dispatch(Actions.selectRequestByIndex(index));

View File

@ -28,7 +28,7 @@ add_task(async function() {
is(getSelectedRequest(store.getState()), null,
"There should be no selected item in the requests menu.");
let networkEvent = monitor.panelWin.api.once(EVENTS.NETWORK_EVENT);
let networkEvent = monitor.panelWin.once(EVENTS.NETWORK_EVENT);
tab.linkedBrowser.reload();
await networkEvent;

View File

@ -24,11 +24,11 @@ add_task(async function() {
assertRequestCount(store, 1);
let noRequest = true;
monitor.panelWin.api.once(EVENTS.NETWORK_EVENT, () => {
monitor.panelWin.once(EVENTS.NETWORK_EVENT, () => {
noRequest = false;
});
monitor.panelWin.api.once(EVENTS.NETWORK_EVENT_UPDATED, () => {
monitor.panelWin.once(EVENTS.NETWORK_EVENT_UPDATED, () => {
noRequest = false;
});

View File

@ -41,7 +41,7 @@ add_task(async function() {
for (let testcase of TEST_DATA) {
info("Testing Security tab visibility for " + testcase.desc);
let onNewItem = monitor.panelWin.api.once(EVENTS.NETWORK_EVENT);
let onNewItem = monitor.panelWin.once(EVENTS.NETWORK_EVENT);
let onComplete = testcase.isBroken ?
waitForSecurityBrokenNetworkEvent() :
waitForNetworkEvents(monitor, 1);
@ -106,7 +106,7 @@ add_task(async function() {
];
let promises = awaitedEvents.map((event) => {
return monitor.panelWin.api.once(EVENTS[event]);
return monitor.panelWin.once(EVENTS[event]);
});
return Promise.all(promises);

View File

@ -34,7 +34,7 @@ function test() {
function expectEvent(evt, cb) {
promiseList.push(new Promise((resolve, reject) => {
monitor.panelWin.api.once(evt, _ => {
monitor.panelWin.once(evt, _ => {
cb().then(resolve, reject);
});
}));
@ -151,7 +151,7 @@ function test() {
);
});
monitor.panelWin.api.once(EVENTS.RECEIVED_REQUEST_POST_DATA, () => {
monitor.panelWin.once(EVENTS.RECEIVED_REQUEST_POST_DATA, () => {
ok(false, "Trap listener: this request doesn't have any post data.");
});

View File

@ -161,7 +161,7 @@ add_task(async function() {
}
async function testCookiesTab() {
let onEvent = monitor.panelWin.api.once(EVENTS.TAB_UPDATED);
let onEvent = monitor.panelWin.once(EVENTS.TAB_UPDATED);
EventUtils.sendMouseEvent({ type: "mousedown" },
document.querySelectorAll("#details-pane tab")[1]);
await onEvent;
@ -204,7 +204,7 @@ add_task(async function() {
}
async function testResponseTab() {
let onEvent = monitor.panelWin.api.once(EVENTS.TAB_UPDATED);
let onEvent = monitor.panelWin.once(EVENTS.TAB_UPDATED);
EventUtils.sendMouseEvent({ type: "mousedown" },
document.querySelectorAll("#details-pane tab")[3]);
await onEvent;

View File

@ -155,13 +155,13 @@ function waitForTimelineMarkers(monitor) {
info(`Got marker: ${marker.name}`);
markers.push(marker);
if (markers.length == 2) {
monitor.panelWin.api.off(EVENTS.TIMELINE_EVENT, handleTimelineEvent);
monitor.panelWin.off(EVENTS.TIMELINE_EVENT, handleTimelineEvent);
info("Got two timeline markers, done waiting");
resolve(markers);
}
}
monitor.panelWin.api.on(EVENTS.TIMELINE_EVENT, handleTimelineEvent);
monitor.panelWin.on(EVENTS.TIMELINE_EVENT, handleTimelineEvent);
});
}
@ -212,14 +212,14 @@ function waitForAllRequestsFinished(monitor) {
}
// All requests are done - unsubscribe from events and resolve!
window.api.off(EVENTS.NETWORK_EVENT, onRequest);
window.api.off(EVENTS.PAYLOAD_READY, onTimings);
window.off(EVENTS.NETWORK_EVENT, onRequest);
window.off(EVENTS.PAYLOAD_READY, onTimings);
info("All requests finished");
resolve();
}
window.api.on(EVENTS.NETWORK_EVENT, onRequest);
window.api.on(EVENTS.PAYLOAD_READY, onTimings);
window.on(EVENTS.NETWORK_EVENT, onRequest);
window.on(EVENTS.PAYLOAD_READY, onTimings);
});
}
@ -248,12 +248,12 @@ let updatedTypes = [
// Start collecting all networkEventUpdate event when panel is opened.
// removeTab() should be called once all corresponded RECEIVED_* events finished.
function startNetworkEventUpdateObserver(panelWin) {
updatingTypes.forEach((type) => panelWin.api.on(type, actor => {
updatingTypes.forEach((type) => panelWin.on(type, actor => {
let key = actor + "-" + updatedTypes[updatingTypes.indexOf(type)];
finishedQueue[key] = finishedQueue[key] ? finishedQueue[key] + 1 : 1;
}));
updatedTypes.forEach((type) => panelWin.api.on(type, actor => {
updatedTypes.forEach((type) => panelWin.on(type, actor => {
let key = actor + "-" + type;
finishedQueue[key] = finishedQueue[key] ? finishedQueue[key] - 1 : -1;
}));
@ -389,14 +389,14 @@ function waitForNetworkEvents(monitor, getRequests) {
// Wait until networkEvent & payloadReady finish for each request.
if (networkEvent >= getRequests && payloadReady >= getRequests) {
panel.api.off(EVENTS.NETWORK_EVENT, onNetworkEvent);
panel.api.off(EVENTS.PAYLOAD_READY, onPayloadReady);
panel.off(EVENTS.NETWORK_EVENT, onNetworkEvent);
panel.off(EVENTS.PAYLOAD_READY, onPayloadReady);
executeSoon(resolve);
}
}
panel.api.on(EVENTS.NETWORK_EVENT, onNetworkEvent);
panel.api.on(EVENTS.PAYLOAD_READY, onPayloadReady);
panel.on(EVENTS.NETWORK_EVENT, onNetworkEvent);
panel.on(EVENTS.PAYLOAD_READY, onPayloadReady);
});
}
@ -563,7 +563,7 @@ function verifyRequestItemTarget(document, requestList, requestItem, method,
/**
* Helper function for waiting for an event to fire before resolving a promise.
* Example: waitFor(aMonitor.panelWin.api, EVENT_NAME);
* Example: waitFor(aMonitor.panelWin, EVENT_NAME);
*
* @param object subject
* The event emitter object that is being listened to.
@ -737,7 +737,7 @@ async function showColumn(monitor, column) {
*/
async function selectIndexAndWaitForSourceEditor(monitor, index) {
let document = monitor.panelWin.document;
let onResponseContent = monitor.panelWin.api.once(EVENTS.RECEIVED_RESPONSE_CONTENT);
let onResponseContent = monitor.panelWin.once(EVENTS.RECEIVED_RESPONSE_CONTENT);
// Select the request first, as it may try to fetch whatever is the current request's
// responseContent if we select the ResponseTab first.
EventUtils.sendMouseEvent({ type: "mousedown" },

View File

@ -18,7 +18,7 @@ add_task(async function task() {
let toolbox = gDevTools.getToolbox(target);
let monitor = toolbox.getCurrentPanel();
let netReady = monitor.panelWin.api.once("NetMonitor:PayloadReady");
let netReady = monitor.panelWin.once("NetMonitor:PayloadReady");
// Fire an XHR POST request.
await ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
@ -69,7 +69,7 @@ async function testNetworkMessage(messageNode) {
}
/**
* Wait until all lazily fetch requests in netmonitor get finished.
* Wait until all lazily fetch requests in netmonitor get finsished.
* Otherwise test will be shutdown too early and cause failure.
*/
async function waitForLazyRequests(toolbox) {