Mypal/devtools/server/actors/emulation.js

242 lines
6.6 KiB
JavaScript

/* 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 { Ci } = require("chrome");
const protocol = require("devtools/shared/protocol");
const { emulationSpec } = require("devtools/shared/specs/emulation");
const { SimulatorCore } = require("devtools/shared/touch/simulator-core");
/**
* This actor overrides various browser features to simulate different environments to
* test how pages perform under various conditions.
*
* The design below, which saves the previous value of each property before setting, is
* needed because it's possible to have multiple copies of this actor for a single page.
* When some instance of this actor changes a property, we want it to be able to restore
* that property to the way it was found before the change.
*
* A subtle aspect of the code below is that all get* methods must return non-undefined
* values, so that the absence of a previous value can be distinguished from the value for
* "no override" for each of the properties.
*/
let EmulationActor = protocol.ActorClassWithSpec(emulationSpec, {
initialize(conn, tabActor) {
protocol.Actor.prototype.initialize.call(this, conn);
this.tabActor = tabActor;
this.docShell = tabActor.docShell;
this.simulatorCore = new SimulatorCore(tabActor.chromeEventHandler);
},
disconnect() {
this.destroy();
},
destroy() {
this.clearDPPXOverride();
this.clearNetworkThrottling();
this.clearTouchEventsOverride();
this.clearUserAgentOverride();
this.tabActor = null;
this.docShell = null;
this.simulatorCore = null;
protocol.Actor.prototype.destroy.call(this);
},
/**
* Retrieve the console actor for this tab. This allows us to expose network throttling
* as part of emulation settings, even though it's internally connected to the network
* monitor, which for historical reasons is part of the console actor.
*/
get _consoleActor() {
if (this.tabActor.exited) {
return null;
}
let form = this.tabActor.form();
return this.conn._getOrCreateActor(form.consoleActor);
},
/* DPPX override */
_previousDPPXOverride: undefined,
setDPPXOverride(dppx) {
if (this.getDPPXOverride() === dppx) {
return false;
}
if (this._previousDPPXOverride === undefined) {
this._previousDPPXOverride = this.getDPPXOverride();
}
this.docShell.contentViewer.overrideDPPX = dppx;
return true;
},
getDPPXOverride() {
return this.docShell.contentViewer.overrideDPPX;
},
clearDPPXOverride() {
if (this._previousDPPXOverride !== undefined) {
return this.setDPPXOverride(this._previousDPPXOverride);
}
return false;
},
/* Network Throttling */
_previousNetworkThrottling: undefined,
/**
* Transform the RDP format into the internal format and then set network throttling.
*/
setNetworkThrottling({ downloadThroughput, uploadThroughput, latency }) {
let throttleData = {
roundTripTimeMean: latency,
roundTripTimeMax: latency,
downloadBPSMean: downloadThroughput,
downloadBPSMax: downloadThroughput,
uploadBPSMean: uploadThroughput,
uploadBPSMax: uploadThroughput,
};
return this._setNetworkThrottling(throttleData);
},
_setNetworkThrottling(throttleData) {
let current = this._getNetworkThrottling();
// Check if they are both objects or both null
let match = throttleData == current;
// If both objects, check all entries
if (match && current && throttleData) {
match = Object.entries(current).every(([ k, v ]) => {
return throttleData[k] === v;
});
}
if (match) {
return false;
}
if (this._previousNetworkThrottling === undefined) {
this._previousNetworkThrottling = current;
}
let consoleActor = this._consoleActor;
if (!consoleActor) {
return false;
}
consoleActor.onStartListeners({
listeners: [ "NetworkActivity" ],
});
consoleActor.onSetPreferences({
preferences: {
"NetworkMonitor.throttleData": throttleData,
}
});
return true;
},
/**
* Get network throttling and then transform the internal format into the RDP format.
*/
getNetworkThrottling() {
let throttleData = this._getNetworkThrottling();
if (!throttleData) {
return null;
}
let { downloadBPSMax, uploadBPSMax, roundTripTimeMax } = throttleData;
return {
downloadThroughput: downloadBPSMax,
uploadThroughput: uploadBPSMax,
latency: roundTripTimeMax,
};
},
_getNetworkThrottling() {
let consoleActor = this._consoleActor;
if (!consoleActor) {
return null;
}
let prefs = consoleActor.onGetPreferences({
preferences: [ "NetworkMonitor.throttleData" ],
});
return prefs.preferences["NetworkMonitor.throttleData"] || null;
},
clearNetworkThrottling() {
if (this._previousNetworkThrottling !== undefined) {
return this._setNetworkThrottling(this._previousNetworkThrottling);
}
return false;
},
/* Touch events override */
_previousTouchEventsOverride: undefined,
setTouchEventsOverride(flag) {
if (this.getTouchEventsOverride() == flag) {
return false;
}
if (this._previousTouchEventsOverride === undefined) {
this._previousTouchEventsOverride = this.getTouchEventsOverride();
}
// Start or stop the touch simulator depending on the override flag
if (flag == Ci.nsIDocShell.TOUCHEVENTS_OVERRIDE_ENABLED) {
this.simulatorCore.start();
} else {
this.simulatorCore.stop();
}
this.docShell.touchEventsOverride = flag;
return true;
},
getTouchEventsOverride() {
return this.docShell.touchEventsOverride;
},
clearTouchEventsOverride() {
if (this._previousTouchEventsOverride !== undefined) {
return this.setTouchEventsOverride(this._previousTouchEventsOverride);
}
return false;
},
/* User agent override */
_previousUserAgentOverride: undefined,
setUserAgentOverride(userAgent) {
if (this.getUserAgentOverride() == userAgent) {
return false;
}
if (this._previousUserAgentOverride === undefined) {
this._previousUserAgentOverride = this.getUserAgentOverride();
}
this.docShell.customUserAgent = userAgent;
return true;
},
getUserAgentOverride() {
return this.docShell.customUserAgent;
},
clearUserAgentOverride() {
if (this._previousUserAgentOverride !== undefined) {
return this.setUserAgentOverride(this._previousUserAgentOverride);
}
return false;
},
});
exports.EmulationActor = EmulationActor;