summaryrefslogtreecommitdiffstats
path: root/devtools/client/netmonitor/src/api.js
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--devtools/client/netmonitor/src/api.js216
1 files changed, 216 insertions, 0 deletions
diff --git a/devtools/client/netmonitor/src/api.js b/devtools/client/netmonitor/src/api.js
new file mode 100644
index 0000000000..8d760de788
--- /dev/null
+++ b/devtools/client/netmonitor/src/api.js
@@ -0,0 +1,216 @@
+/* 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("resource://devtools/shared/event-emitter.js");
+
+const {
+ bindActionCreators,
+} = require("resource://devtools/client/shared/vendor/redux.js");
+const {
+ Connector,
+} = require("resource://devtools/client/netmonitor/src/connector/index.js");
+const {
+ configureStore,
+} = require("resource://devtools/client/netmonitor/src/create-store.js");
+const {
+ EVENTS,
+} = require("resource://devtools/client/netmonitor/src/constants.js");
+const Actions = require("resource://devtools/client/netmonitor/src/actions/index.js");
+
+const {
+ getDisplayedRequestById,
+ getSortedRequests,
+} = require("resource://devtools/client/netmonitor/src/selectors/index.js");
+
+/**
+ * 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();
+
+ // List of listeners for `devtools.network.onRequestFinished` WebExt API
+ this._requestFinishedListeners = new Set();
+
+ // Bind event handlers
+ this.onPayloadReady = this.onPayloadReady.bind(this);
+}
+
+NetMonitorAPI.prototype = {
+ async connect(toolbox) {
+ // Bail out if already connected.
+ if (this.toolbox) {
+ return;
+ }
+
+ this.toolbox = toolbox;
+
+ // Configure store/state object.
+ this.store = configureStore(
+ this.connector,
+ this.toolbox.commands,
+ this.toolbox.telemetry
+ );
+ this.actions = bindActionCreators(Actions, this.store.dispatch);
+
+ // Register listener for new requests (utilized by WebExtension API).
+ this.on(EVENTS.PAYLOAD_READY, this.onPayloadReady);
+
+ // Initialize connection to the backend. Pass `this` as the owner,
+ // so this object can receive all emitted events.
+ const connection = {
+ toolbox,
+ owner: this,
+ };
+
+ await this.connector.connect(connection, this.actions, this.store.getState);
+ },
+
+ /**
+ * Clean up (unmount from DOM, remove listeners, disconnect).
+ */
+ destroy() {
+ this.off(EVENTS.PAYLOAD_READY, this.onPayloadReady);
+
+ this.connector.disconnect();
+
+ if (this.harExportConnector) {
+ this.harExportConnector.disconnect();
+ }
+ },
+
+ // HAR
+
+ /**
+ * Support for `devtools.network.getHAR` (get collected data as HAR)
+ */
+ async getHar() {
+ const {
+ HarExporter,
+ } = require("resource://devtools/client/netmonitor/src/har/har-exporter.js");
+ const state = this.store.getState();
+
+ const 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 onPayloadReady(resource) {
+ if (!this._requestFinishedListeners.size) {
+ return;
+ }
+
+ const {
+ HarExporter,
+ } = require("resource://devtools/client/netmonitor/src/har/har-exporter.js");
+
+ const connector = await this.getHarExportConnector();
+ const request = getDisplayedRequestById(
+ this.store.getState(),
+ resource.actor
+ );
+ if (!request) {
+ console.error("HAR: request not found " + resource.actor);
+ return;
+ }
+
+ const options = {
+ connector,
+ includeResponseBodies: false,
+ items: [request],
+ };
+
+ const har = await HarExporter.getHar(options);
+
+ // There is page so remove the page reference.
+ const harEntry = har.log.entries[0];
+ delete harEntry.pageref;
+
+ this._requestFinishedListeners.forEach(listener =>
+ listener({
+ harEntry,
+ requestId: resource.actor,
+ })
+ );
+ },
+
+ /**
+ * 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(listener) {
+ this._requestFinishedListeners.add(listener);
+ },
+
+ removeRequestFinishedListener(listener) {
+ this._requestFinishedListeners.delete(listener);
+ },
+
+ hasRequestFinishedListeners() {
+ return this._requestFinishedListeners.size > 0;
+ },
+
+ /**
+ * Separate connector for HAR export.
+ */
+ async getHarExportConnector() {
+ if (this.harExportConnector) {
+ // Wait for the connector to be ready to avoid exceptions if this method is called
+ // twice during its initialization.
+ await this.harExportConnectorReady;
+ return this.harExportConnector;
+ }
+
+ const connection = {
+ toolbox: this.toolbox,
+ };
+
+ this.harExportConnector = new Connector();
+ this.harExportConnectorReady = this.harExportConnector.connect(connection);
+
+ await this.harExportConnectorReady;
+ return this.harExportConnector;
+ },
+
+ /**
+ * Resends a given network request
+ * @param {String} requestId
+ * Id of the network request
+ */
+ resendRequest(requestId) {
+ // Flush queued requests.
+ this.store.dispatch(Actions.batchFlush());
+ // Send custom request with same url, headers and body as the request
+ // with the given requestId.
+ this.store.dispatch(Actions.sendCustomRequest(requestId));
+ },
+};
+
+exports.NetMonitorAPI = NetMonitorAPI;