зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1690221 - [devtools] Merge FirefoxConnector into Connector. r=bomsy
Differential Revision: https://phabricator.services.mozilla.com/D103750
This commit is contained in:
Родитель
1e919fbc8d
Коммит
a212b7b7b5
|
@ -68,11 +68,7 @@ NetMonitorAPI.prototype = {
|
||||||
owner: this,
|
owner: this,
|
||||||
};
|
};
|
||||||
|
|
||||||
await this.connector.connectFirefox(
|
await this.connector.connect(connection, this.actions, this.store.getState);
|
||||||
connection,
|
|
||||||
this.actions,
|
|
||||||
this.store.getState
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -193,9 +189,7 @@ NetMonitorAPI.prototype = {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.harExportConnector = new Connector();
|
this.harExportConnector = new Connector();
|
||||||
this.harExportConnectorReady = this.harExportConnector.connectFirefox(
|
this.harExportConnectorReady = this.harExportConnector.connect(connection);
|
||||||
connection
|
|
||||||
);
|
|
||||||
|
|
||||||
await this.harExportConnectorReady;
|
await this.harExportConnectorReady;
|
||||||
return this.harExportConnector;
|
return this.harExportConnector;
|
||||||
|
|
|
@ -1,618 +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 Services = require("Services");
|
|
||||||
const {
|
|
||||||
ACTIVITY_TYPE,
|
|
||||||
EVENTS,
|
|
||||||
TEST_EVENTS,
|
|
||||||
} = require("devtools/client/netmonitor/src/constants");
|
|
||||||
const FirefoxDataProvider = require("devtools/client/netmonitor/src/connector/firefox-data-provider");
|
|
||||||
const {
|
|
||||||
getDisplayedTimingMarker,
|
|
||||||
} = require("devtools/client/netmonitor/src/selectors/index");
|
|
||||||
|
|
||||||
// Network throttling
|
|
||||||
loader.lazyRequireGetter(
|
|
||||||
this,
|
|
||||||
"throttlingProfiles",
|
|
||||||
"devtools/client/shared/components/throttling/profiles"
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Connector to Firefox backend.
|
|
||||||
*/
|
|
||||||
class FirefoxConnector {
|
|
||||||
constructor() {
|
|
||||||
// Public methods
|
|
||||||
this.connect = this.connect.bind(this);
|
|
||||||
this.disconnect = this.disconnect.bind(this);
|
|
||||||
this.willNavigate = this.willNavigate.bind(this);
|
|
||||||
this.navigate = this.navigate.bind(this);
|
|
||||||
this.sendHTTPRequest = this.sendHTTPRequest.bind(this);
|
|
||||||
this.setPreferences = this.setPreferences.bind(this);
|
|
||||||
this.triggerActivity = this.triggerActivity.bind(this);
|
|
||||||
this.getTabTarget = this.getTabTarget.bind(this);
|
|
||||||
this.viewSourceInDebugger = this.viewSourceInDebugger.bind(this);
|
|
||||||
this.requestData = this.requestData.bind(this);
|
|
||||||
this.getTimingMarker = this.getTimingMarker.bind(this);
|
|
||||||
this.updateNetworkThrottling = this.updateNetworkThrottling.bind(this);
|
|
||||||
|
|
||||||
// Internals
|
|
||||||
this.getLongString = this.getLongString.bind(this);
|
|
||||||
this.onTargetAvailable = this.onTargetAvailable.bind(this);
|
|
||||||
this.onResourceAvailable = this.onResourceAvailable.bind(this);
|
|
||||||
this.onResourceUpdated = this.onResourceUpdated.bind(this);
|
|
||||||
|
|
||||||
this.networkFront = null;
|
|
||||||
this.listenForNetworkEvents = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
get currentTarget() {
|
|
||||||
return this.toolbox.targetList.targetFront;
|
|
||||||
}
|
|
||||||
|
|
||||||
get hasResourceWatcherSupport() {
|
|
||||||
return this.toolbox.resourceWatcher.hasResourceWatcherSupport(
|
|
||||||
this.toolbox.resourceWatcher.TYPES.NETWORK_EVENT
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
get watcherFront() {
|
|
||||||
return this.toolbox.resourceWatcher.watcherFront;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.toolbox = connection.toolbox;
|
|
||||||
|
|
||||||
// The owner object (NetMonitorAPI) received all events.
|
|
||||||
this.owner = connection.owner;
|
|
||||||
|
|
||||||
await this.toolbox.targetList.watchTargets(
|
|
||||||
[this.toolbox.targetList.TYPES.FRAME],
|
|
||||||
this.onTargetAvailable
|
|
||||||
);
|
|
||||||
|
|
||||||
await this.toolbox.resourceWatcher.watchResources(
|
|
||||||
[this.toolbox.resourceWatcher.TYPES.DOCUMENT_EVENT],
|
|
||||||
{ onAvailable: this.onResourceAvailable }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
disconnect() {
|
|
||||||
// As this function might be called twice, we need to guard if already called.
|
|
||||||
if (this._destroyed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._destroyed = true;
|
|
||||||
|
|
||||||
this.toolbox.targetList.unwatchTargets(
|
|
||||||
[this.toolbox.targetList.TYPES.FRAME],
|
|
||||||
this.onTargetAvailable
|
|
||||||
);
|
|
||||||
|
|
||||||
this.toolbox.resourceWatcher.unwatchResources(
|
|
||||||
[this.toolbox.resourceWatcher.TYPES.DOCUMENT_EVENT],
|
|
||||||
{ onAvailable: this.onResourceAvailable }
|
|
||||||
);
|
|
||||||
|
|
||||||
if (this.actions) {
|
|
||||||
this.actions.batchReset();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.removeListeners();
|
|
||||||
|
|
||||||
this.currentTarget.off("will-navigate", this.willNavigate);
|
|
||||||
|
|
||||||
this.webConsoleFront = null;
|
|
||||||
this.dataProvider = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
async pause() {
|
|
||||||
this.listenForNetworkEvents = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
async resume() {
|
|
||||||
this.listenForNetworkEvents = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
async onTargetAvailable({ targetFront, isTargetSwitching }) {
|
|
||||||
if (!targetFront.isTopLevel) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isTargetSwitching) {
|
|
||||||
this.willNavigate();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Listener for `will-navigate` event is (un)registered outside
|
|
||||||
// of the `addListeners` and `removeListeners` methods since
|
|
||||||
// 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.
|
|
||||||
targetFront.on("will-navigate", this.willNavigate);
|
|
||||||
|
|
||||||
this.webConsoleFront = await this.currentTarget.getFront("console");
|
|
||||||
|
|
||||||
this.dataProvider = new FirefoxDataProvider({
|
|
||||||
webConsoleFront: this.webConsoleFront,
|
|
||||||
actions: this.actions,
|
|
||||||
owner: this.owner,
|
|
||||||
resourceWatcher: this.toolbox.resourceWatcher,
|
|
||||||
});
|
|
||||||
|
|
||||||
// If this is the first top level target, lets register all the listeners
|
|
||||||
if (!isTargetSwitching) {
|
|
||||||
await this.addListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize Responsive Emulation front for network throttling.
|
|
||||||
this.responsiveFront = await this.currentTarget.getFront("responsive");
|
|
||||||
if (this.hasResourceWatcherSupport) {
|
|
||||||
this.networkFront = await this.watcherFront.getNetworkParentActor();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async onResourceAvailable(resources) {
|
|
||||||
for (const resource of resources) {
|
|
||||||
const { TYPES } = this.toolbox.resourceWatcher;
|
|
||||||
|
|
||||||
if (resource.resourceType === TYPES.DOCUMENT_EVENT) {
|
|
||||||
this.onDocEvent(resource);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.listenForNetworkEvents) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resource.resourceType === TYPES.NETWORK_EVENT) {
|
|
||||||
this.dataProvider.onNetworkResourceAvailable(resource);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resource.resourceType === TYPES.NETWORK_EVENT_STACKTRACE) {
|
|
||||||
this.dataProvider.onStackTraceAvailable(resource);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resource.resourceType === TYPES.WEBSOCKET) {
|
|
||||||
const { wsMessageType } = resource;
|
|
||||||
|
|
||||||
switch (wsMessageType) {
|
|
||||||
case "webSocketOpened": {
|
|
||||||
this.dataProvider.onWebSocketOpened(
|
|
||||||
resource.httpChannelId,
|
|
||||||
resource.effectiveURI,
|
|
||||||
resource.protocols,
|
|
||||||
resource.extensions
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "webSocketClosed": {
|
|
||||||
this.dataProvider.onWebSocketClosed(
|
|
||||||
resource.httpChannelId,
|
|
||||||
resource.wasClean,
|
|
||||||
resource.code,
|
|
||||||
resource.reason
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "frameReceived": {
|
|
||||||
this.dataProvider.onFrameReceived(
|
|
||||||
resource.httpChannelId,
|
|
||||||
resource.data
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "frameSent": {
|
|
||||||
this.dataProvider.onFrameSent(
|
|
||||||
resource.httpChannelId,
|
|
||||||
resource.data
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resource.resourceType === TYPES.SERVER_SENT_EVENT) {
|
|
||||||
const { messageType, httpChannelId, data } = resource;
|
|
||||||
switch (messageType) {
|
|
||||||
case "eventSourceConnectionClosed": {
|
|
||||||
this.dataProvider.onEventSourceConnectionClosed(httpChannelId);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "eventReceived": {
|
|
||||||
this.dataProvider.onEventReceived(httpChannelId, data);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async onResourceUpdated(updates) {
|
|
||||||
for (const { resource, update } of updates) {
|
|
||||||
if (
|
|
||||||
resource.resourceType ===
|
|
||||||
this.toolbox.resourceWatcher.TYPES.NETWORK_EVENT &&
|
|
||||||
this.listenForNetworkEvents
|
|
||||||
) {
|
|
||||||
this.dataProvider.onNetworkResourceUpdated(resource, update);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async addListeners(ignoreExistingResources = false) {
|
|
||||||
const targetResources = [
|
|
||||||
this.toolbox.resourceWatcher.TYPES.NETWORK_EVENT,
|
|
||||||
this.toolbox.resourceWatcher.TYPES.NETWORK_EVENT_STACKTRACE,
|
|
||||||
];
|
|
||||||
if (Services.prefs.getBoolPref("devtools.netmonitor.features.webSockets")) {
|
|
||||||
targetResources.push(this.toolbox.resourceWatcher.TYPES.WEBSOCKET);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
Services.prefs.getBoolPref(
|
|
||||||
"devtools.netmonitor.features.serverSentEvents"
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
targetResources.push(
|
|
||||||
this.toolbox.resourceWatcher.TYPES.SERVER_SENT_EVENT
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.toolbox.resourceWatcher.watchResources(targetResources, {
|
|
||||||
onAvailable: this.onResourceAvailable,
|
|
||||||
onUpdated: this.onResourceUpdated,
|
|
||||||
ignoreExistingResources,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
removeListeners() {
|
|
||||||
this.toolbox.resourceWatcher.unwatchResources(
|
|
||||||
[
|
|
||||||
this.toolbox.resourceWatcher.TYPES.NETWORK_EVENT,
|
|
||||||
this.toolbox.resourceWatcher.TYPES.NETWORK_EVENT_STACKTRACE,
|
|
||||||
this.toolbox.resourceWatcher.TYPES.WEBSOCKET,
|
|
||||||
this.toolbox.resourceWatcher.TYPES.SERVER_SENT_EVENT,
|
|
||||||
],
|
|
||||||
{
|
|
||||||
onAvailable: this.onResourceAvailable,
|
|
||||||
onUpdated: this.onResourceUpdated,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
enableActions(enable) {
|
|
||||||
this.dataProvider.enableActions(enable);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (this.actions && this.getState) {
|
|
||||||
const state = this.getState();
|
|
||||||
// Resume is done automatically on page reload/navigation.
|
|
||||||
if (!state.requests.recording) {
|
|
||||||
this.actions.toggleRecording();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop any ongoing search.
|
|
||||||
if (state.search.ongoingSearch) {
|
|
||||||
this.actions.stopOngoingSearch();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
navigate() {
|
|
||||||
if (!this.dataProvider.hasPendingRequests()) {
|
|
||||||
this.onReloaded();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const listener = () => {
|
|
||||||
if (this.dataProvider && this.dataProvider.hasPendingRequests()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.owner) {
|
|
||||||
this.owner.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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onReloaded() {
|
|
||||||
const panel = this.toolbox.getPanel("netmonitor");
|
|
||||||
if (panel) {
|
|
||||||
panel.emit("reloaded");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The "DOMContentLoaded" and "Load" events sent by the console actor.
|
|
||||||
*
|
|
||||||
* @param {object} resource The DOCUMENT_EVENT resource
|
|
||||||
*/
|
|
||||||
onDocEvent(resource) {
|
|
||||||
if (!resource.targetFront.isTopLevel) {
|
|
||||||
// Only handle document events for the top level target.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resource.name === "dom-loading") {
|
|
||||||
// Netmonitor does not support dom-loading event yet.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.actions) {
|
|
||||||
this.actions.addTimingMarker(resource);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resource.name === "dom-complete") {
|
|
||||||
this.navigate();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.emitForTests(TEST_EVENTS.TIMELINE_EVENT, resource);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send a HTTP request data payload
|
|
||||||
*
|
|
||||||
* @param {object} data data payload would like to sent to backend
|
|
||||||
*/
|
|
||||||
async sendHTTPRequest(data) {
|
|
||||||
if (this.hasResourceWatcherSupport && this.currentTarget) {
|
|
||||||
const networkContentFront = await this.currentTarget.getFront(
|
|
||||||
"networkContent"
|
|
||||||
);
|
|
||||||
const { channelId } = await networkContentFront.sendHTTPRequest(data);
|
|
||||||
return { channelId };
|
|
||||||
}
|
|
||||||
const {
|
|
||||||
eventActor: { actor },
|
|
||||||
} = await this.webConsoleFront.sendHTTPRequest(data);
|
|
||||||
return { actor };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Block future requests matching a filter.
|
|
||||||
*
|
|
||||||
* @param {object} filter request filter specifying what to block
|
|
||||||
*/
|
|
||||||
blockRequest(filter) {
|
|
||||||
return this.webConsoleFront.blockRequest(filter);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unblock future requests matching a filter.
|
|
||||||
*
|
|
||||||
* @param {object} filter request filter specifying what to unblock
|
|
||||||
*/
|
|
||||||
unblockRequest(filter) {
|
|
||||||
return this.webConsoleFront.unblockRequest(filter);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Get the list of blocked URLs
|
|
||||||
*/
|
|
||||||
async getBlockedUrls() {
|
|
||||||
if (this.hasResourceWatcherSupport && this.networkFront) {
|
|
||||||
return this.networkFront.getBlockedUrls();
|
|
||||||
}
|
|
||||||
if (!this.webConsoleFront.traits.blockedUrls) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
return this.webConsoleFront.getBlockedUrls();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the list of blocked URLs
|
|
||||||
*
|
|
||||||
* @param {object} urls An array of URL strings
|
|
||||||
*/
|
|
||||||
async setBlockedUrls(urls) {
|
|
||||||
if (this.hasResourceWatcherSupport && this.networkFront) {
|
|
||||||
return this.networkFront.setBlockedUrls(urls);
|
|
||||||
}
|
|
||||||
return this.webConsoleFront.setBlockedUrls(urls);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set network preferences to control network flow
|
|
||||||
*
|
|
||||||
* @param {object} request request payload would like to sent to backend
|
|
||||||
* @param {function} callback callback will be invoked after the request finished
|
|
||||||
*/
|
|
||||||
setPreferences(request) {
|
|
||||||
return this.webConsoleFront.setPreferences(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Triggers a specific "activity" to be performed by the frontend.
|
|
||||||
* This can be, for example, triggering reloads or enabling/disabling cache.
|
|
||||||
*
|
|
||||||
* @param {number} type The activity type. See the ACTIVITY_TYPE const.
|
|
||||||
* @return {object} A promise resolved once the activity finishes and the frontend
|
|
||||||
* is back into "standby" mode.
|
|
||||||
*/
|
|
||||||
triggerActivity(type) {
|
|
||||||
// Puts the frontend into "standby" (when there's no particular activity).
|
|
||||||
const standBy = () => {
|
|
||||||
this.currentActivity = ACTIVITY_TYPE.NONE;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Waits for a series of "navigation start" and "navigation stop" events.
|
|
||||||
const waitForNavigation = async () => {
|
|
||||||
await this.currentTarget.once("will-navigate");
|
|
||||||
await this.currentTarget.once("navigate");
|
|
||||||
};
|
|
||||||
|
|
||||||
// Reconfigures the tab, optionally triggering a reload.
|
|
||||||
const reconfigureTab = options => {
|
|
||||||
return this.currentTarget.reconfigure({ options });
|
|
||||||
};
|
|
||||||
|
|
||||||
// Reconfigures the tab and waits for the target to finish navigating.
|
|
||||||
const reconfigureTabAndWaitForNavigation = options => {
|
|
||||||
options.performReload = true;
|
|
||||||
const navigationFinished = waitForNavigation();
|
|
||||||
return reconfigureTab(options).then(() => navigationFinished);
|
|
||||||
};
|
|
||||||
switch (type) {
|
|
||||||
case ACTIVITY_TYPE.RELOAD.WITH_CACHE_DEFAULT:
|
|
||||||
return reconfigureTabAndWaitForNavigation({}).then(standBy);
|
|
||||||
case ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED:
|
|
||||||
this.currentActivity = ACTIVITY_TYPE.ENABLE_CACHE;
|
|
||||||
this.currentTarget.once("will-navigate", () => {
|
|
||||||
this.currentActivity = type;
|
|
||||||
});
|
|
||||||
return reconfigureTabAndWaitForNavigation({
|
|
||||||
cacheDisabled: false,
|
|
||||||
performReload: true,
|
|
||||||
}).then(standBy);
|
|
||||||
case ACTIVITY_TYPE.RELOAD.WITH_CACHE_DISABLED:
|
|
||||||
this.currentActivity = ACTIVITY_TYPE.DISABLE_CACHE;
|
|
||||||
this.currentTarget.once("will-navigate", () => {
|
|
||||||
this.currentActivity = type;
|
|
||||||
});
|
|
||||||
return reconfigureTabAndWaitForNavigation({
|
|
||||||
cacheDisabled: true,
|
|
||||||
performReload: true,
|
|
||||||
}).then(standBy);
|
|
||||||
case ACTIVITY_TYPE.ENABLE_CACHE:
|
|
||||||
this.currentActivity = type;
|
|
||||||
return reconfigureTab({
|
|
||||||
cacheDisabled: false,
|
|
||||||
performReload: false,
|
|
||||||
}).then(standBy);
|
|
||||||
case ACTIVITY_TYPE.DISABLE_CACHE:
|
|
||||||
this.currentActivity = type;
|
|
||||||
return reconfigureTab({
|
|
||||||
cacheDisabled: true,
|
|
||||||
performReload: false,
|
|
||||||
}).then(standBy);
|
|
||||||
}
|
|
||||||
this.currentActivity = ACTIVITY_TYPE.NONE;
|
|
||||||
return Promise.reject(new Error("Invalid activity type"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetches the full text of a LongString.
|
|
||||||
*
|
|
||||||
* @param {object|string} stringGrip
|
|
||||||
* The long string grip containing the corresponding actor.
|
|
||||||
* If you pass in a plain string (by accident or because you're lazy),
|
|
||||||
* then a promise of the same string is simply returned.
|
|
||||||
* @return {object}
|
|
||||||
* A promise that is resolved when the full string contents
|
|
||||||
* are available, or rejected if something goes wrong.
|
|
||||||
*/
|
|
||||||
getLongString(stringGrip) {
|
|
||||||
return this.dataProvider.getLongString(stringGrip);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Getter that access tab target instance.
|
|
||||||
* @return {object} browser tab target instance
|
|
||||||
*/
|
|
||||||
getTabTarget() {
|
|
||||||
return this.currentTarget;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Open a given source in Debugger
|
|
||||||
* @param {string} sourceURL source url
|
|
||||||
* @param {number} sourceLine source line number
|
|
||||||
*/
|
|
||||||
viewSourceInDebugger(sourceURL, sourceLine, sourceColumn) {
|
|
||||||
if (this.toolbox) {
|
|
||||||
this.toolbox.viewSourceInDebugger(sourceURL, sourceLine, sourceColumn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch networkEventUpdate websocket message from back-end when
|
|
||||||
* data provider is connected.
|
|
||||||
* @param {object} request network request instance
|
|
||||||
* @param {string} type NetworkEventUpdate type
|
|
||||||
*/
|
|
||||||
requestData(request, type) {
|
|
||||||
return this.dataProvider.requestData(request, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
getTimingMarker(name) {
|
|
||||||
if (!this.getState) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const state = this.getState();
|
|
||||||
return getDisplayedTimingMarker(state, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateNetworkThrottling(enabled, profile) {
|
|
||||||
const throttlingFront =
|
|
||||||
this.hasResourceWatcherSupport && this.networkFront
|
|
||||||
? this.networkFront
|
|
||||||
: this.responsiveFront;
|
|
||||||
|
|
||||||
if (!enabled) {
|
|
||||||
throttlingFront.clearNetworkThrottling();
|
|
||||||
} else {
|
|
||||||
// The profile can be either a profile id which is used to
|
|
||||||
// search the predefined throttle profiles or a profile object
|
|
||||||
// as defined in the trottle tests.
|
|
||||||
if (typeof profile === "string") {
|
|
||||||
profile = throttlingProfiles.find(({ id }) => id == profile);
|
|
||||||
}
|
|
||||||
const { download, upload, latency } = profile;
|
|
||||||
await throttlingFront.setNetworkThrottling({
|
|
||||||
downloadThroughput: download,
|
|
||||||
uploadThroughput: upload,
|
|
||||||
latency,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.emitForTests(TEST_EVENTS.THROTTLING_CHANGED, { profile });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fire events for the owner object. These events are only
|
|
||||||
* used in tests so, don't fire them in production release.
|
|
||||||
*/
|
|
||||||
emitForTests(type, data) {
|
|
||||||
if (this.owner) {
|
|
||||||
this.owner.emitForTests(type, data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = FirefoxConnector;
|
|
|
@ -4,114 +4,614 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
const Services = require("Services");
|
||||||
|
const {
|
||||||
|
ACTIVITY_TYPE,
|
||||||
|
EVENTS,
|
||||||
|
TEST_EVENTS,
|
||||||
|
} = require("devtools/client/netmonitor/src/constants");
|
||||||
|
const FirefoxDataProvider = require("devtools/client/netmonitor/src/connector/firefox-data-provider");
|
||||||
|
const {
|
||||||
|
getDisplayedTimingMarker,
|
||||||
|
} = require("devtools/client/netmonitor/src/selectors/index");
|
||||||
|
|
||||||
|
// Network throttling
|
||||||
|
loader.lazyRequireGetter(
|
||||||
|
this,
|
||||||
|
"throttlingProfiles",
|
||||||
|
"devtools/client/shared/components/throttling/profiles"
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic connector wrapper object that is responsible for
|
* Connector to Firefox backend.
|
||||||
* instantiating specific connector implementation according
|
|
||||||
* to the client type.
|
|
||||||
*/
|
*/
|
||||||
class Connector {
|
class Connector {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.connector = null;
|
// Public methods
|
||||||
|
|
||||||
// Bind public API
|
|
||||||
this.connect = this.connect.bind(this);
|
this.connect = this.connect.bind(this);
|
||||||
this.disconnect = this.disconnect.bind(this);
|
this.disconnect = this.disconnect.bind(this);
|
||||||
this.connectFirefox = this.connectFirefox.bind(this);
|
this.willNavigate = this.willNavigate.bind(this);
|
||||||
this.getLongString = this.getLongString.bind(this);
|
this.navigate = this.navigate.bind(this);
|
||||||
this.getTabTarget = this.getTabTarget.bind(this);
|
|
||||||
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.getTabTarget = this.getTabTarget.bind(this);
|
||||||
this.viewSourceInDebugger = this.viewSourceInDebugger.bind(this);
|
this.viewSourceInDebugger = this.viewSourceInDebugger.bind(this);
|
||||||
this.requestData = this.requestData.bind(this);
|
this.requestData = this.requestData.bind(this);
|
||||||
this.getTimingMarker = this.getTimingMarker.bind(this);
|
this.getTimingMarker = this.getTimingMarker.bind(this);
|
||||||
this.updateNetworkThrottling = this.updateNetworkThrottling.bind(this);
|
this.updateNetworkThrottling = this.updateNetworkThrottling.bind(this);
|
||||||
|
|
||||||
|
// Internals
|
||||||
|
this.getLongString = this.getLongString.bind(this);
|
||||||
|
this.onTargetAvailable = this.onTargetAvailable.bind(this);
|
||||||
|
this.onResourceAvailable = this.onResourceAvailable.bind(this);
|
||||||
|
this.onResourceUpdated = this.onResourceUpdated.bind(this);
|
||||||
|
|
||||||
|
this.networkFront = null;
|
||||||
|
this.listenForNetworkEvents = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect/Disconnect API
|
get currentTarget() {
|
||||||
|
return this.toolbox.targetList.targetFront;
|
||||||
|
}
|
||||||
|
|
||||||
|
get hasResourceWatcherSupport() {
|
||||||
|
return this.toolbox.resourceWatcher.hasResourceWatcherSupport(
|
||||||
|
this.toolbox.resourceWatcher.TYPES.NETWORK_EVENT
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get watcherFront() {
|
||||||
|
return this.toolbox.resourceWatcher.watcherFront;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
async connect(connection, actions, getState) {
|
||||||
if (!connection || !connection.tab) {
|
this.actions = actions;
|
||||||
return;
|
this.getState = getState;
|
||||||
}
|
this.toolbox = connection.toolbox;
|
||||||
|
|
||||||
const { clientType } = connection.tab;
|
// The owner object (NetMonitorAPI) received all events.
|
||||||
switch (clientType) {
|
this.owner = connection.owner;
|
||||||
case "firefox":
|
|
||||||
await this.connectFirefox(connection, actions, getState);
|
await this.toolbox.targetList.watchTargets(
|
||||||
break;
|
[this.toolbox.targetList.TYPES.FRAME],
|
||||||
default:
|
this.onTargetAvailable
|
||||||
throw Error(`Unknown client type - ${clientType}`);
|
);
|
||||||
}
|
|
||||||
|
await this.toolbox.resourceWatcher.watchResources(
|
||||||
|
[this.toolbox.resourceWatcher.TYPES.DOCUMENT_EVENT],
|
||||||
|
{ onAvailable: this.onResourceAvailable }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnect() {
|
disconnect() {
|
||||||
this.connector && this.connector.disconnect();
|
// As this function might be called twice, we need to guard if already called.
|
||||||
|
if (this._destroyed) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
connectFirefox(connection, actions, getState) {
|
this._destroyed = true;
|
||||||
const FirefoxConnector = require("devtools/client/netmonitor/src/connector/firefox-connector");
|
|
||||||
this.connector = new FirefoxConnector();
|
this.toolbox.targetList.unwatchTargets(
|
||||||
return this.connector.connect(connection, actions, getState);
|
[this.toolbox.targetList.TYPES.FRAME],
|
||||||
|
this.onTargetAvailable
|
||||||
|
);
|
||||||
|
|
||||||
|
this.toolbox.resourceWatcher.unwatchResources(
|
||||||
|
[this.toolbox.resourceWatcher.TYPES.DOCUMENT_EVENT],
|
||||||
|
{ onAvailable: this.onResourceAvailable }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (this.actions) {
|
||||||
|
this.actions.batchReset();
|
||||||
}
|
}
|
||||||
|
|
||||||
pause() {
|
this.removeListeners();
|
||||||
return this.connector.pause();
|
|
||||||
|
this.currentTarget.off("will-navigate", this.willNavigate);
|
||||||
|
|
||||||
|
this.webConsoleFront = null;
|
||||||
|
this.dataProvider = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
resume() {
|
async pause() {
|
||||||
return this.connector.resume();
|
this.listenForNetworkEvents = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
enableActions() {
|
async resume() {
|
||||||
this.connector.enableActions(...arguments);
|
this.listenForNetworkEvents = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Public API
|
async onTargetAvailable({ targetFront, isTargetSwitching }) {
|
||||||
|
if (!targetFront.isTopLevel) {
|
||||||
getLongString() {
|
return;
|
||||||
return this.connector.getLongString(...arguments);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isTargetSwitching) {
|
||||||
|
this.willNavigate();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listener for `will-navigate` event is (un)registered outside
|
||||||
|
// of the `addListeners` and `removeListeners` methods since
|
||||||
|
// 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.
|
||||||
|
targetFront.on("will-navigate", this.willNavigate);
|
||||||
|
|
||||||
|
this.webConsoleFront = await this.currentTarget.getFront("console");
|
||||||
|
|
||||||
|
this.dataProvider = new FirefoxDataProvider({
|
||||||
|
webConsoleFront: this.webConsoleFront,
|
||||||
|
actions: this.actions,
|
||||||
|
owner: this.owner,
|
||||||
|
resourceWatcher: this.toolbox.resourceWatcher,
|
||||||
|
});
|
||||||
|
|
||||||
|
// If this is the first top level target, lets register all the listeners
|
||||||
|
if (!isTargetSwitching) {
|
||||||
|
await this.addListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize Responsive Emulation front for network throttling.
|
||||||
|
this.responsiveFront = await this.currentTarget.getFront("responsive");
|
||||||
|
if (this.hasResourceWatcherSupport) {
|
||||||
|
this.networkFront = await this.watcherFront.getNetworkParentActor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async onResourceAvailable(resources) {
|
||||||
|
for (const resource of resources) {
|
||||||
|
const { TYPES } = this.toolbox.resourceWatcher;
|
||||||
|
|
||||||
|
if (resource.resourceType === TYPES.DOCUMENT_EVENT) {
|
||||||
|
this.onDocEvent(resource);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.listenForNetworkEvents) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resource.resourceType === TYPES.NETWORK_EVENT) {
|
||||||
|
this.dataProvider.onNetworkResourceAvailable(resource);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resource.resourceType === TYPES.NETWORK_EVENT_STACKTRACE) {
|
||||||
|
this.dataProvider.onStackTraceAvailable(resource);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resource.resourceType === TYPES.WEBSOCKET) {
|
||||||
|
const { wsMessageType } = resource;
|
||||||
|
|
||||||
|
switch (wsMessageType) {
|
||||||
|
case "webSocketOpened": {
|
||||||
|
this.dataProvider.onWebSocketOpened(
|
||||||
|
resource.httpChannelId,
|
||||||
|
resource.effectiveURI,
|
||||||
|
resource.protocols,
|
||||||
|
resource.extensions
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "webSocketClosed": {
|
||||||
|
this.dataProvider.onWebSocketClosed(
|
||||||
|
resource.httpChannelId,
|
||||||
|
resource.wasClean,
|
||||||
|
resource.code,
|
||||||
|
resource.reason
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "frameReceived": {
|
||||||
|
this.dataProvider.onFrameReceived(
|
||||||
|
resource.httpChannelId,
|
||||||
|
resource.data
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "frameSent": {
|
||||||
|
this.dataProvider.onFrameSent(
|
||||||
|
resource.httpChannelId,
|
||||||
|
resource.data
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resource.resourceType === TYPES.SERVER_SENT_EVENT) {
|
||||||
|
const { messageType, httpChannelId, data } = resource;
|
||||||
|
switch (messageType) {
|
||||||
|
case "eventSourceConnectionClosed": {
|
||||||
|
this.dataProvider.onEventSourceConnectionClosed(httpChannelId);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "eventReceived": {
|
||||||
|
this.dataProvider.onEventReceived(httpChannelId, data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async onResourceUpdated(updates) {
|
||||||
|
for (const { resource, update } of updates) {
|
||||||
|
if (
|
||||||
|
resource.resourceType ===
|
||||||
|
this.toolbox.resourceWatcher.TYPES.NETWORK_EVENT &&
|
||||||
|
this.listenForNetworkEvents
|
||||||
|
) {
|
||||||
|
this.dataProvider.onNetworkResourceUpdated(resource, update);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async addListeners(ignoreExistingResources = false) {
|
||||||
|
const targetResources = [
|
||||||
|
this.toolbox.resourceWatcher.TYPES.NETWORK_EVENT,
|
||||||
|
this.toolbox.resourceWatcher.TYPES.NETWORK_EVENT_STACKTRACE,
|
||||||
|
];
|
||||||
|
if (Services.prefs.getBoolPref("devtools.netmonitor.features.webSockets")) {
|
||||||
|
targetResources.push(this.toolbox.resourceWatcher.TYPES.WEBSOCKET);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
Services.prefs.getBoolPref(
|
||||||
|
"devtools.netmonitor.features.serverSentEvents"
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
targetResources.push(
|
||||||
|
this.toolbox.resourceWatcher.TYPES.SERVER_SENT_EVENT
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.toolbox.resourceWatcher.watchResources(targetResources, {
|
||||||
|
onAvailable: this.onResourceAvailable,
|
||||||
|
onUpdated: this.onResourceUpdated,
|
||||||
|
ignoreExistingResources,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
removeListeners() {
|
||||||
|
this.toolbox.resourceWatcher.unwatchResources(
|
||||||
|
[
|
||||||
|
this.toolbox.resourceWatcher.TYPES.NETWORK_EVENT,
|
||||||
|
this.toolbox.resourceWatcher.TYPES.NETWORK_EVENT_STACKTRACE,
|
||||||
|
this.toolbox.resourceWatcher.TYPES.WEBSOCKET,
|
||||||
|
this.toolbox.resourceWatcher.TYPES.SERVER_SENT_EVENT,
|
||||||
|
],
|
||||||
|
{
|
||||||
|
onAvailable: this.onResourceAvailable,
|
||||||
|
onUpdated: this.onResourceUpdated,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
enableActions(enable) {
|
||||||
|
this.dataProvider.enableActions(enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (this.actions && this.getState) {
|
||||||
|
const state = this.getState();
|
||||||
|
// Resume is done automatically on page reload/navigation.
|
||||||
|
if (!state.requests.recording) {
|
||||||
|
this.actions.toggleRecording();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop any ongoing search.
|
||||||
|
if (state.search.ongoingSearch) {
|
||||||
|
this.actions.stopOngoingSearch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
navigate() {
|
||||||
|
if (!this.dataProvider.hasPendingRequests()) {
|
||||||
|
this.onReloaded();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const listener = () => {
|
||||||
|
if (this.dataProvider && this.dataProvider.hasPendingRequests()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.owner) {
|
||||||
|
this.owner.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onReloaded() {
|
||||||
|
const panel = this.toolbox.getPanel("netmonitor");
|
||||||
|
if (panel) {
|
||||||
|
panel.emit("reloaded");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The "DOMContentLoaded" and "Load" events sent by the console actor.
|
||||||
|
*
|
||||||
|
* @param {object} resource The DOCUMENT_EVENT resource
|
||||||
|
*/
|
||||||
|
onDocEvent(resource) {
|
||||||
|
if (!resource.targetFront.isTopLevel) {
|
||||||
|
// Only handle document events for the top level target.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resource.name === "dom-loading") {
|
||||||
|
// Netmonitor does not support dom-loading event yet.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.actions) {
|
||||||
|
this.actions.addTimingMarker(resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resource.name === "dom-complete") {
|
||||||
|
this.navigate();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.emitForTests(TEST_EVENTS.TIMELINE_EVENT, resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a HTTP request data payload
|
||||||
|
*
|
||||||
|
* @param {object} data data payload would like to sent to backend
|
||||||
|
*/
|
||||||
|
async sendHTTPRequest(data) {
|
||||||
|
if (this.hasResourceWatcherSupport && this.currentTarget) {
|
||||||
|
const networkContentFront = await this.currentTarget.getFront(
|
||||||
|
"networkContent"
|
||||||
|
);
|
||||||
|
const { channelId } = await networkContentFront.sendHTTPRequest(data);
|
||||||
|
return { channelId };
|
||||||
|
}
|
||||||
|
const {
|
||||||
|
eventActor: { actor },
|
||||||
|
} = await this.webConsoleFront.sendHTTPRequest(data);
|
||||||
|
return { actor };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Block future requests matching a filter.
|
||||||
|
*
|
||||||
|
* @param {object} filter request filter specifying what to block
|
||||||
|
*/
|
||||||
|
blockRequest(filter) {
|
||||||
|
return this.webConsoleFront.blockRequest(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unblock future requests matching a filter.
|
||||||
|
*
|
||||||
|
* @param {object} filter request filter specifying what to unblock
|
||||||
|
*/
|
||||||
|
unblockRequest(filter) {
|
||||||
|
return this.webConsoleFront.unblockRequest(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the list of blocked URLs
|
||||||
|
*/
|
||||||
|
async getBlockedUrls() {
|
||||||
|
if (this.hasResourceWatcherSupport && this.networkFront) {
|
||||||
|
return this.networkFront.getBlockedUrls();
|
||||||
|
}
|
||||||
|
if (!this.webConsoleFront.traits.blockedUrls) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return this.webConsoleFront.getBlockedUrls();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the list of blocked URLs
|
||||||
|
*
|
||||||
|
* @param {object} urls An array of URL strings
|
||||||
|
*/
|
||||||
|
async setBlockedUrls(urls) {
|
||||||
|
if (this.hasResourceWatcherSupport && this.networkFront) {
|
||||||
|
return this.networkFront.setBlockedUrls(urls);
|
||||||
|
}
|
||||||
|
return this.webConsoleFront.setBlockedUrls(urls);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set network preferences to control network flow
|
||||||
|
*
|
||||||
|
* @param {object} request request payload would like to sent to backend
|
||||||
|
* @param {function} callback callback will be invoked after the request finished
|
||||||
|
*/
|
||||||
|
setPreferences(request) {
|
||||||
|
return this.webConsoleFront.setPreferences(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggers a specific "activity" to be performed by the frontend.
|
||||||
|
* This can be, for example, triggering reloads or enabling/disabling cache.
|
||||||
|
*
|
||||||
|
* @param {number} type The activity type. See the ACTIVITY_TYPE const.
|
||||||
|
* @return {object} A promise resolved once the activity finishes and the frontend
|
||||||
|
* is back into "standby" mode.
|
||||||
|
*/
|
||||||
|
triggerActivity(type) {
|
||||||
|
// Puts the frontend into "standby" (when there's no particular activity).
|
||||||
|
const standBy = () => {
|
||||||
|
this.currentActivity = ACTIVITY_TYPE.NONE;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Waits for a series of "navigation start" and "navigation stop" events.
|
||||||
|
const waitForNavigation = async () => {
|
||||||
|
await this.currentTarget.once("will-navigate");
|
||||||
|
await this.currentTarget.once("navigate");
|
||||||
|
};
|
||||||
|
|
||||||
|
// Reconfigures the tab, optionally triggering a reload.
|
||||||
|
const reconfigureTab = options => {
|
||||||
|
return this.currentTarget.reconfigure({ options });
|
||||||
|
};
|
||||||
|
|
||||||
|
// Reconfigures the tab and waits for the target to finish navigating.
|
||||||
|
const reconfigureTabAndWaitForNavigation = options => {
|
||||||
|
options.performReload = true;
|
||||||
|
const navigationFinished = waitForNavigation();
|
||||||
|
return reconfigureTab(options).then(() => navigationFinished);
|
||||||
|
};
|
||||||
|
switch (type) {
|
||||||
|
case ACTIVITY_TYPE.RELOAD.WITH_CACHE_DEFAULT:
|
||||||
|
return reconfigureTabAndWaitForNavigation({}).then(standBy);
|
||||||
|
case ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED:
|
||||||
|
this.currentActivity = ACTIVITY_TYPE.ENABLE_CACHE;
|
||||||
|
this.currentTarget.once("will-navigate", () => {
|
||||||
|
this.currentActivity = type;
|
||||||
|
});
|
||||||
|
return reconfigureTabAndWaitForNavigation({
|
||||||
|
cacheDisabled: false,
|
||||||
|
performReload: true,
|
||||||
|
}).then(standBy);
|
||||||
|
case ACTIVITY_TYPE.RELOAD.WITH_CACHE_DISABLED:
|
||||||
|
this.currentActivity = ACTIVITY_TYPE.DISABLE_CACHE;
|
||||||
|
this.currentTarget.once("will-navigate", () => {
|
||||||
|
this.currentActivity = type;
|
||||||
|
});
|
||||||
|
return reconfigureTabAndWaitForNavigation({
|
||||||
|
cacheDisabled: true,
|
||||||
|
performReload: true,
|
||||||
|
}).then(standBy);
|
||||||
|
case ACTIVITY_TYPE.ENABLE_CACHE:
|
||||||
|
this.currentActivity = type;
|
||||||
|
return reconfigureTab({
|
||||||
|
cacheDisabled: false,
|
||||||
|
performReload: false,
|
||||||
|
}).then(standBy);
|
||||||
|
case ACTIVITY_TYPE.DISABLE_CACHE:
|
||||||
|
this.currentActivity = type;
|
||||||
|
return reconfigureTab({
|
||||||
|
cacheDisabled: true,
|
||||||
|
performReload: false,
|
||||||
|
}).then(standBy);
|
||||||
|
}
|
||||||
|
this.currentActivity = ACTIVITY_TYPE.NONE;
|
||||||
|
return Promise.reject(new Error("Invalid activity type"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the full text of a LongString.
|
||||||
|
*
|
||||||
|
* @param {object|string} stringGrip
|
||||||
|
* The long string grip containing the corresponding actor.
|
||||||
|
* If you pass in a plain string (by accident or because you're lazy),
|
||||||
|
* then a promise of the same string is simply returned.
|
||||||
|
* @return {object}
|
||||||
|
* A promise that is resolved when the full string contents
|
||||||
|
* are available, or rejected if something goes wrong.
|
||||||
|
*/
|
||||||
|
getLongString(stringGrip) {
|
||||||
|
return this.dataProvider.getLongString(stringGrip);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getter that access tab target instance.
|
||||||
|
* @return {object} browser tab target instance
|
||||||
|
*/
|
||||||
getTabTarget() {
|
getTabTarget() {
|
||||||
return this.connector.getTabTarget();
|
return this.currentTarget;
|
||||||
}
|
}
|
||||||
|
|
||||||
sendHTTPRequest() {
|
/**
|
||||||
return this.connector.sendHTTPRequest(...arguments);
|
* Open a given source in Debugger
|
||||||
|
* @param {string} sourceURL source url
|
||||||
|
* @param {number} sourceLine source line number
|
||||||
|
*/
|
||||||
|
viewSourceInDebugger(sourceURL, sourceLine, sourceColumn) {
|
||||||
|
if (this.toolbox) {
|
||||||
|
this.toolbox.viewSourceInDebugger(sourceURL, sourceLine, sourceColumn);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getBlockedUrls() {
|
/**
|
||||||
return this.connector.getBlockedUrls();
|
* Fetch networkEventUpdate websocket message from back-end when
|
||||||
|
* data provider is connected.
|
||||||
|
* @param {object} request network request instance
|
||||||
|
* @param {string} type NetworkEventUpdate type
|
||||||
|
*/
|
||||||
|
requestData(request, type) {
|
||||||
|
return this.dataProvider.requestData(request, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
setBlockedUrls() {
|
getTimingMarker(name) {
|
||||||
return this.connector.setBlockedUrls(...arguments);
|
if (!this.getState) {
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
setPreferences() {
|
const state = this.getState();
|
||||||
return this.connector.setPreferences(...arguments);
|
return getDisplayedTimingMarker(state, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
triggerActivity() {
|
async updateNetworkThrottling(enabled, profile) {
|
||||||
return this.connector.triggerActivity(...arguments);
|
const throttlingFront =
|
||||||
|
this.hasResourceWatcherSupport && this.networkFront
|
||||||
|
? this.networkFront
|
||||||
|
: this.responsiveFront;
|
||||||
|
|
||||||
|
if (!enabled) {
|
||||||
|
throttlingFront.clearNetworkThrottling();
|
||||||
|
} else {
|
||||||
|
// The profile can be either a profile id which is used to
|
||||||
|
// search the predefined throttle profiles or a profile object
|
||||||
|
// as defined in the trottle tests.
|
||||||
|
if (typeof profile === "string") {
|
||||||
|
profile = throttlingProfiles.find(({ id }) => id == profile);
|
||||||
|
}
|
||||||
|
const { download, upload, latency } = profile;
|
||||||
|
await throttlingFront.setNetworkThrottling({
|
||||||
|
downloadThroughput: download,
|
||||||
|
uploadThroughput: upload,
|
||||||
|
latency,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
viewSourceInDebugger() {
|
this.emitForTests(TEST_EVENTS.THROTTLING_CHANGED, { profile });
|
||||||
return this.connector.viewSourceInDebugger(...arguments);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
requestData() {
|
/**
|
||||||
return this.connector.requestData(...arguments);
|
* Fire events for the owner object. These events are only
|
||||||
|
* used in tests so, don't fire them in production release.
|
||||||
|
*/
|
||||||
|
emitForTests(type, data) {
|
||||||
|
if (this.owner) {
|
||||||
|
this.owner.emitForTests(type, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
getTimingMarker() {
|
|
||||||
return this.connector.getTimingMarker(...arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateNetworkThrottling() {
|
|
||||||
return this.connector.updateNetworkThrottling(...arguments);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.Connector = Connector;
|
module.exports.Connector = Connector;
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
DevToolsModules(
|
DevToolsModules(
|
||||||
"firefox-connector.js",
|
|
||||||
"firefox-data-provider.js",
|
"firefox-data-provider.js",
|
||||||
"index.js",
|
"index.js",
|
||||||
)
|
)
|
||||||
|
|
|
@ -33,7 +33,7 @@ executed by the page (including inner iframes). The final export is triggered
|
||||||
by HarAutomation at the right time.
|
by HarAutomation at the right time.
|
||||||
|
|
||||||
Note: this object is using it's own logic to fetch data from the backend.
|
Note: this object is using it's own logic to fetch data from the backend.
|
||||||
It should reuse the Firefox Connector (src/connector/firefox-connector),
|
It should reuse the Netmonitor Connector (src/connector/index),
|
||||||
so we don't have to maintain two code paths.
|
so we don't have to maintain two code paths.
|
||||||
|
|
||||||
## HarExporter
|
## HarExporter
|
||||||
|
|
Загрузка…
Ссылка в новой задаче