author | J. Ryan Stinnett <jryans@gmail.com> |
Fri, 07 Oct 2016 14:55:16 -0500 | |
changeset 317873 | 9f289545ab7d9f3a3b5b18452818df1e3e44f382 |
parent 317872 | 44726da7a2869614b04e2193f17f6060bdbf4ceb |
child 317874 | 56b16d2eaa77c4639f624f1b8eb01da70b5f0227 |
push id | 33170 |
push user | cbook@mozilla.com |
push date | Fri, 14 Oct 2016 10:37:07 +0000 |
treeherder | autoland@0d101ebfd95c [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | tromey |
bugs | 1283453 |
milestone | 52.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/devtools/server/actors/emulation.js +++ b/devtools/server/actors/emulation.js @@ -4,33 +4,193 @@ "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: null, + _previousTouchEventsOverride: undefined, setTouchEventsOverride(flag) { - if (this.docShell.touchEventsOverride == flag) { + if (this.getTouchEventsOverride() == flag) { return false; } - if (this._previousTouchEventsOverride === null) { - this._previousTouchEventsOverride = this.docShell.touchEventsOverride; + 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(); } @@ -39,87 +199,43 @@ let EmulationActor = protocol.ActorClass return true; }, getTouchEventsOverride() { return this.docShell.touchEventsOverride; }, clearTouchEventsOverride() { - if (this._previousTouchEventsOverride !== null) { + if (this._previousTouchEventsOverride !== undefined) { return this.setTouchEventsOverride(this._previousTouchEventsOverride); } return false; }, /* User agent override */ - _previousUserAgentOverride: null, + _previousUserAgentOverride: undefined, setUserAgentOverride(userAgent) { - if (this.docShell.customUserAgent == userAgent) { + if (this.getUserAgentOverride() == userAgent) { return false; } - if (this._previousUserAgentOverride === null) { - this._previousUserAgentOverride = this.docShell.customUserAgent; + if (this._previousUserAgentOverride === undefined) { + this._previousUserAgentOverride = this.getUserAgentOverride(); } this.docShell.customUserAgent = userAgent; return true; }, getUserAgentOverride() { return this.docShell.customUserAgent; }, clearUserAgentOverride() { - if (this._previousUserAgentOverride !== null) { + if (this._previousUserAgentOverride !== undefined) { return this.setUserAgentOverride(this._previousUserAgentOverride); } return false; }, - /* DPPX override */ - - _previousDPPXOverride: null, - - setDPPXOverride(dppx) { - let { contentViewer } = this.docShell; - - if (contentViewer.overrideDPPX === dppx) { - return false; - } - - if (this._previousDPPXOverride === null) { - this._previousDPPXOverride = contentViewer.overrideDPPX; - } - - contentViewer.overrideDPPX = dppx; - - return true; - }, - - getDPPXOverride() { - return this.docShell.contentViewer.overrideDPPX; - }, - - clearDPPXOverride() { - if (this._previousDPPXOverride !== null) { - return this.setDPPXOverride(this._previousDPPXOverride); - } - - return false; - }, - - disconnect() { - this.destroy(); - }, - - destroy() { - this.clearTouchEventsOverride(); - this.clearUserAgentOverride(); - this.clearDPPXOverride(); - this.docShell = null; - this.simulatorCore = null; - protocol.Actor.prototype.destroy.call(this); - }, }); exports.EmulationActor = EmulationActor;
--- a/devtools/server/actors/webconsole.js +++ b/devtools/server/actors/webconsole.js @@ -1054,17 +1054,17 @@ WebConsoleActor.prototype = * The request message - which preferences need to be retrieved. * @return object * The response message - a { key: value } object map. */ onGetPreferences: function WCA_onGetPreferences(aRequest) { let prefs = Object.create(null); for (let key of aRequest.preferences) { - prefs[key] = !!this._prefs[key]; + prefs[key] = this._prefs[key]; } return { preferences: prefs }; }, /** * The "setPreferences" request handler. * * @param object aRequest
--- a/devtools/shared/specs/emulation.js +++ b/devtools/shared/specs/emulation.js @@ -4,16 +4,62 @@ "use strict"; const { Arg, RetVal, generateActorSpec } = require("devtools/shared/protocol"); const emulationSpec = generateActorSpec({ typeName: "emulation", methods: { + setDPPXOverride: { + request: { + dppx: Arg(0, "number") + }, + response: { + valueChanged: RetVal("boolean") + } + }, + + getDPPXOverride: { + request: {}, + response: { + dppx: RetVal("number") + } + }, + + clearDPPXOverride: { + request: {}, + response: { + valueChanged: RetVal("boolean") + } + }, + + setNetworkThrottling: { + request: { + options: Arg(0, "json") + }, + response: { + valueChanged: RetVal("boolean") + } + }, + + getNetworkThrottling: { + request: {}, + response: { + state: RetVal("json") + } + }, + + clearNetworkThrottling: { + request: {}, + response: { + valueChanged: RetVal("boolean") + } + }, + setTouchEventsOverride: { request: { flag: Arg(0, "number") }, response: { valueChanged: RetVal("boolean") } }, @@ -49,35 +95,12 @@ const emulationSpec = generateActorSpec( }, clearUserAgentOverride: { request: {}, response: { valueChanged: RetVal("boolean") } }, - - setDPPXOverride: { - request: { - dppx: Arg(0, "number") - }, - response: { - valueChanged: RetVal("boolean") - } - }, - - getDPPXOverride: { - request: {}, - response: { - dppx: RetVal("number") - } - }, - - clearDPPXOverride: { - request: {}, - response: { - valueChanged: RetVal("boolean") - } - }, } }); exports.emulationSpec = emulationSpec;
--- a/devtools/shared/webconsole/network-monitor.js +++ b/devtools/shared/webconsole/network-monitor.js @@ -640,17 +640,17 @@ function NetworkMonitor(filters, owner) this.owner = owner; this.openRequests = {}; this.openResponses = {}; this._httpResponseExaminer = DevToolsUtils.makeInfallible(this._httpResponseExaminer).bind(this); this._httpModifyExaminer = DevToolsUtils.makeInfallible(this._httpModifyExaminer).bind(this); this._serviceWorkerRequest = this._serviceWorkerRequest.bind(this); - this.throttleData = null; + this._throttleData = null; this._throttler = null; } exports.NetworkMonitor = NetworkMonitor; NetworkMonitor.prototype = { filters: null, @@ -718,16 +718,26 @@ NetworkMonitor.prototype = { "http-on-modify-request", false); } // In child processes, only watch for service worker requests // everything else only happens in the parent process Services.obs.addObserver(this._serviceWorkerRequest, "service-worker-synthesized-response", false); }, + get throttleData() { + return this._throttleData; + }, + + set throttleData(value) { + this._throttleData = value; + // Clear out any existing throttlers + this._throttler = null; + }, + _getThrottler: function () { if (this.throttleData !== null && this._throttler === null) { this._throttler = new NetworkThrottleManager(this.throttleData); } return this._throttler; }, _serviceWorkerRequest: function (subject, topic, data) {