Bug 1593089 - Refactor existing emulation actor to ResponsiveEmulation actor and ContentViewerEmulation actor. r=jdescottes

Differential Revision: https://phabricator.services.mozilla.com/D52754

--HG--
rename : devtools/server/actors/emulation.js => devtools/server/actors/emulation/content-viewer.js
rename : devtools/server/actors/emulation.js => devtools/server/actors/emulation/responsive.js
rename : devtools/shared/fronts/emulation.js => devtools/shared/fronts/content-viewer.js
rename : devtools/shared/fronts/emulation.js => devtools/shared/fronts/responsive.js
rename : devtools/shared/specs/emulation.js => devtools/shared/specs/content-viewer.js
rename : devtools/shared/specs/emulation.js => devtools/shared/specs/responsive.js
extra : moz-landing-system : lando
This commit is contained in:
Micah Tigley 2020-01-03 18:28:14 +00:00
parent 95e6bca783
commit e9f7bfd194
23 changed files with 915 additions and 81 deletions

View File

@ -158,9 +158,9 @@ export type TabPayload = {
actor: ActorId,
animationsActor: ActorId,
consoleActor: ActorId,
contentViewerActor: ActorId,
cssPropertiesActor: ActorId,
directorManagerActor: ActorId,
emulationActor: ActorId,
eventLoopLagActor: ActorId,
framerateActor: ActorId,
inspectorActor: ActorId,
@ -171,6 +171,7 @@ export type TabPayload = {
performanceEntriesActor: ActorId,
profilerActor: ActorId,
reflowActor: ActorId,
responsiveActor: ActorId,
storageActor: ActorId,
styleEditorActor: ActorId,
styleSheetsActor: ActorId,

View File

@ -1966,13 +1966,13 @@ Toolbox.prototype = {
if (
!ResponsiveUIManager.isActiveForTab(localTab) ||
(await !this.target.actorHasMethod("emulation", "setElementPickerState"))
(await !this.target.actorHasMethod("responsive", "setElementPickerState"))
) {
return;
}
const ui = ResponsiveUIManager.getResponsiveUIForTab(localTab);
await ui.emulationFront.setElementPickerState(state);
await ui.responsiveFront.setElementPickerState(state);
},
/**

View File

@ -168,11 +168,29 @@ class RulesView {
this.provider = provider;
}
/**
* Initializes the content-viewer front and enable the print and color scheme simulation
* if they are supported in the current target.
*/
async initSimulationFeatures() {
// In order to query if the emulation actor's print simulation methods are supported,
// we have to call the emulation front so that the actor is lazily loaded. This allows
// us to use `actorHasMethod`. Please see `getActorDescription` for more information.
this.emulationFront = await this.currentTarget.getFront("emulation");
// In order to query if the content-viewer actor's print and color simulation methods are
// supported, we have to call the content-viewer front so that the actor is lazily loaded.
// This allows us to use `actorHasMethod`. Please see `getActorDescription` for more
// information.
try {
this.contentViewerFront = await this.currentTarget.getFront(
"contentViewer"
);
} catch (e) {
console.error(e);
}
// Bug 1606852: For backwards compatibility, we need to get the emulation actor. The ContentViewer
// actor is only available in Firefox 73 or newer. We can remove this call when Firefox 73
// is on release.
if (!this.contentViewerFront) {
this.contentViewerFront = await this.currentTarget.getFront("emulation");
}
if (!this.currentTarget.chrome) {
this.store.dispatch(updatePrintSimulationHidden(false));
@ -183,14 +201,22 @@ class RulesView {
// Show the color scheme simulation toggle button if:
// - The feature pref is enabled.
// - Color scheme simulation is supported for the current target.
const isEmulateColorSchemeSupported =
(await this.currentTarget.actorHasMethod(
"contentViewer",
"getEmulatedColorScheme"
)) ||
// Bug 1606852: We can removed this check when Firefox 73 is on release.
(await this.currentTarget.actorHasMethod(
"emulation",
"getEmulatedColorScheme"
));
if (
Services.prefs.getBoolPref(
"devtools.inspector.color-scheme-simulation.enabled"
) &&
(await this.currentTarget.actorHasMethod(
"emulation",
"getEmulatedColorScheme"
))
isEmulateColorSchemeSupported
) {
this.store.dispatch(updateColorSchemeSimulationHidden(false));
} else {
@ -226,9 +252,9 @@ class RulesView {
this.elementStyle = null;
}
if (this.emulationFront) {
this.emulationFront.destroy();
this.emulationFront = null;
if (this.contentViewerFront) {
this.contentViewerFront.destroy();
this.contentViewerFront = null;
}
this._dummyElement = null;
@ -491,10 +517,10 @@ class RulesView {
* Handler for toggling color scheme simulation.
*/
async onToggleColorSchemeSimulation() {
const currentState = await this.emulationFront.getEmulatedColorScheme();
const currentState = await this.contentViewerFront.getEmulatedColorScheme();
const index = COLOR_SCHEMES.indexOf(currentState);
const nextState = COLOR_SCHEMES[(index + 1) % COLOR_SCHEMES.length];
await this.emulationFront.setEmulatedColorScheme(nextState);
await this.contentViewerFront.setEmulatedColorScheme(nextState);
await this.updateElementStyle();
}
@ -502,12 +528,12 @@ class RulesView {
* Handler for toggling print media simulation.
*/
async onTogglePrintSimulation() {
const enabled = await this.emulationFront.getIsPrintSimulationEnabled();
const enabled = await this.contentViewerFront.getIsPrintSimulationEnabled();
if (!enabled) {
await this.emulationFront.startPrintMediaSimulation();
await this.contentViewerFront.startPrintMediaSimulation();
} else {
await this.emulationFront.stopPrintMediaSimulation(false);
await this.contentViewerFront.stopPrintMediaSimulation(false);
}
await this.updateElementStyle();

View File

@ -288,10 +288,6 @@ CssRuleView.prototype = {
return this._dummyElement;
},
get emulationFront() {
return this._emulationFront;
},
// Get the highlighters overlay from the Inspector.
get highlighters() {
if (!this._highlighters) {
@ -405,15 +401,28 @@ CssRuleView.prototype = {
},
/**
* Initializes the emulation front and enable the print and color scheme simulation
* Initializes the content-viewer front and enable the print and color scheme simulation
* if they are supported in the current target.
*/
async _initSimulationFeatures() {
// In order to query if the emulation actor's print and color simulation methods are
// supported, we have to call the emulation front so that the actor is lazily loaded.
// In order to query if the content-viewer actor's print and color simulation methods are
// supported, we have to call the content-viewer front so that the actor is lazily loaded.
// This allows us to use `actorHasMethod`. Please see `getActorDescription` for more
// information.
this._emulationFront = await this.currentTarget.getFront("emulation");
try {
this.contentViewerFront = await this.currentTarget.getFront(
"contentViewer"
);
} catch (e) {
console.error(e);
}
// Bug 1606852: For backwards compatibility, we need to get the emulation actor. The ContentViewer
// actor is only available in Firefox 73 or newer. We can remove this call when Firefox 73
// is on release.
if (!this.contentViewerFront) {
this.contentViewerFront = await this.currentTarget.getFront("emulation");
}
if (!this.currentTarget.chrome) {
this.printSimulationButton.removeAttribute("hidden");
@ -426,14 +435,22 @@ CssRuleView.prototype = {
// Show the color scheme simulation toggle button if:
// - The feature pref is enabled.
// - Color scheme simulation is supported for the current target.
const isEmulateColorSchemeSupported =
(await this.currentTarget.actorHasMethod(
"contentViewer",
"getEmulatedColorScheme"
)) ||
// Bug 1606852: We can removed this check when Firefox 73 is on release.
(await this.currentTarget.actorHasMethod(
"emulation",
"getEmulatedColorScheme"
));
if (
Services.prefs.getBoolPref(
"devtools.inspector.color-scheme-simulation.enabled"
) &&
(await this.currentTarget.actorHasMethod(
"emulation",
"getEmulatedColorScheme"
))
isEmulateColorSchemeSupported
) {
this.colorSchemeSimulationButton.removeAttribute("hidden");
this.colorSchemeSimulationButton.addEventListener(
@ -847,7 +864,7 @@ CssRuleView.prototype = {
}
// Clean-up for print simulation.
if (this._emulationFront) {
if (this.contentViewerFront) {
this.colorSchemeSimulationButton.removeEventListener(
"click",
this._onToggleColorSchemeSimulation
@ -857,11 +874,11 @@ CssRuleView.prototype = {
this._onTogglePrintSimulation
);
this._emulationFront.destroy();
this.contentViewerFront.destroy();
this.colorSchemeSimulationButton = null;
this.printSimulationButton = null;
this._emulationFront = null;
this.contentViewerFront = null;
}
this.tooltips.destroy();
@ -1739,7 +1756,7 @@ CssRuleView.prototype = {
},
async _onToggleColorSchemeSimulation() {
const currentState = await this.emulationFront.getEmulatedColorScheme();
const currentState = await this.contentViewerFront.getEmulatedColorScheme();
const index = COLOR_SCHEMES.indexOf(currentState);
const nextState = COLOR_SCHEMES[(index + 1) % COLOR_SCHEMES.length];
@ -1749,19 +1766,19 @@ CssRuleView.prototype = {
this.colorSchemeSimulationButton.removeAttribute("state");
}
await this.emulationFront.setEmulatedColorScheme(nextState);
await this.contentViewerFront.setEmulatedColorScheme(nextState);
this.refreshPanel();
},
async _onTogglePrintSimulation() {
const enabled = await this.emulationFront.getIsPrintSimulationEnabled();
const enabled = await this.contentViewerFront.getIsPrintSimulationEnabled();
if (!enabled) {
this.printSimulationButton.classList.add("checked");
await this.emulationFront.startPrintMediaSimulation();
await this.contentViewerFront.startPrintMediaSimulation();
} else {
this.printSimulationButton.classList.remove("checked");
await this.emulationFront.stopPrintMediaSimulation(false);
await this.contentViewerFront.stopPrintMediaSimulation(false);
}
// Refresh the current element's rules in the panel.

View File

@ -151,17 +151,17 @@ exports.menuitems = [
// Similarly, enable them when the color picker is done picking.
if (
ResponsiveUIManager.isActiveForTab(target.localTab) &&
target.actorHasMethod("emulation", "setElementPickerState")
target.actorHasMethod("responsive", "setElementPickerState")
) {
const ui = ResponsiveUIManager.getResponsiveUIForTab(target.localTab);
await ui.emulationFront.setElementPickerState(true);
await ui.responsiveFront.setElementPickerState(true);
inspectorFront.once("color-picked", async () => {
await ui.emulationFront.setElementPickerState(false);
await ui.responsiveFront.setElementPickerState(false);
});
inspectorFront.once("color-pick-canceled", async () => {
await ui.emulationFront.setElementPickerState(false);
await ui.responsiveFront.setElementPickerState(false);
});
}

View File

@ -83,8 +83,19 @@ class FirefoxConnector {
this.tabTarget.on("will-navigate", this.willNavigate);
this.tabTarget.on("navigate", this.navigate);
// Initialize Emulation front for network throttling.
this.emulationFront = await this.tabTarget.getFront("emulation");
// Initialize Responsive Emulation front for network throttling.
try {
this.responsiveFront = await this.tabTarget.getFront("responsive");
} catch (e) {
console.error(e);
}
// Bug 1606852: For backwards compatibility, we need to get the emulation actor. The Responsive
// actor is only available in Firefox 73 or newer. We can remove this call when Firefox 73
// is on release.
if (!this.responsiveFront) {
this.responsiveFront = await this.tabTarget.getFront("emulation");
}
}
// Displaying cache events is only intended for the UI panel.
@ -100,9 +111,9 @@ class FirefoxConnector {
this.removeListeners();
if (this.emulationFront) {
this.emulationFront.destroy();
this.emulationFront = null;
if (this.responsiveFront) {
this.responsiveFront.destroy();
this.responsiveFront = null;
}
if (this.webSocketFront) {
@ -474,11 +485,11 @@ class FirefoxConnector {
async updateNetworkThrottling(enabled, profile) {
if (!enabled) {
await this.emulationFront.clearNetworkThrottling();
await this.responsiveFront.clearNetworkThrottling();
} else {
const data = throttlingProfiles.find(({ id }) => id == profile);
const { download, upload, latency } = data;
await this.emulationFront.setNetworkThrottling({
await this.responsiveFront.setNetworkThrottling({
downloadThroughput: download,
uploadThroughput: upload,
latency,

View File

@ -42,7 +42,7 @@ function testNetworkThrottlingSelectorLabel(ui, expected) {
}
var testNetworkThrottlingState = async function(ui, expected) {
const state = await ui.emulationFront.getNetworkThrottling();
const state = await ui.responsiveFront.getNetworkThrottling();
Assert.deepEqual(
state,
expected,

View File

@ -543,7 +543,7 @@ async function testTouchEventsOverride(ui, expected) {
const { document } = ui.toolWindow;
const touchButton = document.getElementById("touch-simulation-button");
const flag = await ui.emulationFront.getTouchEventsOverride();
const flag = await ui.responsiveFront.getTouchEventsOverride();
is(
flag === Ci.nsIDocShell.TOUCHEVENTS_OVERRIDE_ENABLED,
expected,

View File

@ -206,7 +206,7 @@ class ResponsiveUI {
await message.request(this.toolWindow, "start-frame-script");
}
// Get the protocol ready to speak with emulation actor
// Get the protocol ready to speak with responsive emulation actor
debug("Wait until RDP server connect");
await this.connectToServer();
@ -214,7 +214,7 @@ class ResponsiveUI {
await this.restoreState();
if (this.isBrowserUIEnabled) {
await this.emulationFront.setDocumentInRDMPane(true);
await this.responsiveFront.setDocumentInRDMPane(true);
}
if (!this.isBrowserUIEnabled) {
@ -316,7 +316,7 @@ class ResponsiveUI {
}
if (this.isBrowserUIEnabled) {
await this.emulationFront.setDocumentInRDMPane(false);
await this.responsiveFront.setDocumentInRDMPane(false);
}
this.tab.removeEventListener("TabClose", this);
@ -368,14 +368,14 @@ class ResponsiveUI {
this.toolWindow = null;
this.swap = null;
// Close the debugger client used to speak with emulation actor.
// Close the debugger client used to speak with responsive emulation actor.
// The actor handles clearing any overrides itself, so it's not necessary to clear
// anything on shutdown client side.
const clientClosed = this.client.close();
if (!isTabContentDestroying) {
await clientClosed;
}
this.client = this.emulationFront = null;
this.client = this.responsiveFront = null;
if (!this.isBrowserUIEnabled && !isWindowClosing) {
// Undo the swap and return the content back to a normal tab
@ -395,11 +395,11 @@ class ResponsiveUI {
this.client = new DebuggerClient(DebuggerServer.connectPipe());
await this.client.connect();
const targetFront = await this.client.mainRoot.getTab();
this.emulationFront = await targetFront.getFront("emulation");
this.responsiveFront = await targetFront.getFront("responsive");
}
/**
* Show one-time notification about reloads for emulation.
* Show one-time notification about reloads for responsive emulation.
*/
showReloadNotification() {
if (Services.prefs.getBoolPref(RELOAD_NOTIFICATION_PREF, false)) {
@ -597,12 +597,12 @@ class ResponsiveUI {
async onScreenshot() {
const targetFront = await this.client.mainRoot.getTab();
const captureScreenshotSupported = await targetFront.actorHasMethod(
"emulation",
"responsive",
"captureScreenshot"
);
if (captureScreenshotSupported) {
const data = await this.emulationFront.captureScreenshot();
const data = await this.responsiveFront.captureScreenshot();
await saveScreenshot(this.browserWindow, {}, data);
message.post(this.rdmFrame.contentWindow, "screenshot-captured");
@ -698,10 +698,10 @@ class ResponsiveUI {
*/
async updateDPPX(dppx) {
if (!dppx) {
await this.emulationFront.clearDPPXOverride();
await this.responsiveFront.clearDPPXOverride();
return false;
}
await this.emulationFront.setDPPXOverride(dppx);
await this.responsiveFront.setDPPXOverride(dppx);
return false;
}
@ -714,12 +714,12 @@ class ResponsiveUI {
*/
async updateNetworkThrottling(enabled, profile) {
if (!enabled) {
await this.emulationFront.clearNetworkThrottling();
await this.responsiveFront.clearNetworkThrottling();
return false;
}
const data = throttlingProfiles.find(({ id }) => id == profile);
const { download, upload, latency } = data;
await this.emulationFront.setNetworkThrottling({
await this.responsiveFront.setNetworkThrottling({
downloadThroughput: download,
uploadThroughput: upload,
latency,
@ -735,9 +735,9 @@ class ResponsiveUI {
*/
updateUserAgent(userAgent) {
if (!userAgent) {
return this.emulationFront.clearUserAgentOverride();
return this.responsiveFront.clearUserAgentOverride();
}
return this.emulationFront.setUserAgentOverride(userAgent);
return this.responsiveFront.setUserAgentOverride(userAgent);
}
/**
@ -758,18 +758,18 @@ class ResponsiveUI {
false
);
reloadNeeded = await this.emulationFront.setTouchEventsOverride(
reloadNeeded = await this.responsiveFront.setTouchEventsOverride(
Ci.nsIDocShell.TOUCHEVENTS_OVERRIDE_ENABLED
);
if (metaViewportEnabled) {
reloadNeeded |= await this.emulationFront.setMetaViewportOverride(
reloadNeeded |= await this.responsiveFront.setMetaViewportOverride(
Ci.nsIDocShell.META_VIEWPORT_OVERRIDE_ENABLED
);
}
} else {
reloadNeeded = await this.emulationFront.clearTouchEventsOverride();
reloadNeeded |= await this.emulationFront.clearMetaViewportOverride();
reloadNeeded = await this.responsiveFront.clearTouchEventsOverride();
reloadNeeded |= await this.responsiveFront.clearMetaViewportOverride();
}
return reloadNeeded;
}
@ -791,13 +791,13 @@ class ResponsiveUI {
async updateScreenOrientation(type, angle, isViewportRotated = false) {
const targetFront = await this.client.mainRoot.getTab();
const simulateOrientationChangeSupported = await targetFront.actorHasMethod(
"emulation",
"responsive",
"simulateScreenOrientationChange"
);
// Ensure that simulateScreenOrientationChange is supported.
if (simulateOrientationChangeSupported) {
await this.emulationFront.simulateScreenOrientationChange(
await this.responsiveFront.simulateScreenOrientationChange(
type,
angle,
isViewportRotated

View File

@ -33,6 +33,8 @@ loader.lazyRequireGetter(
* 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.
*
* Bug 1606852: Delete this file when Firefox 73 is on release.
*/
const EmulationActor = protocol.ActorClassWithSpec(emulationSpec, {
initialize(conn, targetActor) {

View File

@ -0,0 +1,132 @@
/* 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 { contentViewerSpec } = require("devtools/shared/specs/content-viewer");
/**
* This actor emulates various browser content environments by using methods available
* on the ContentViewer exposed by the platform.
*/
const ContentViewerActor = protocol.ActorClassWithSpec(contentViewerSpec, {
initialize(conn, targetActor) {
protocol.Actor.prototype.initialize.call(this, conn);
this.targetActor = targetActor;
this.docShell = targetActor.docShell;
this.onWillNavigate = this.onWillNavigate.bind(this);
this.onWindowReady = this.onWindowReady.bind(this);
this.targetActor.on("will-navigate", this.onWillNavigate);
this.targetActor.on("window-ready", this.onWindowReady);
},
destroy() {
this.stopPrintMediaSimulation();
this.setEmulatedColorScheme();
this.targetActor.off("will-navigate", this.onWillNavigate);
this.targetActor.off("window-ready", this.onWindowReady);
this.targetActor = null;
this.docShell = null;
protocol.Actor.prototype.destroy.call(this);
},
onWillNavigate({ isTopLevel }) {
// Make sure that print simulation is stopped before navigating to another page. We
// need to do this since the browser will cache the last state of the page in its
// session history.
if (this._printSimulationEnabled && isTopLevel) {
this.stopPrintMediaSimulation(true);
}
},
onWindowReady({ isTopLevel }) {
// Since `emulateMedium` only works for the current page, we need to ensure persistent
// print simulation for when the user navigates to a new page while its enabled.
// To do this, we need to tell the page to begin print simulation before the DOM
// content is available to the user:
if (this._printSimulationEnabled && isTopLevel) {
this.startPrintMediaSimulation();
}
},
/* Color scheme simulation */
/**
* Returns the currently emulated color scheme.
*/
getEmulatedColorScheme() {
return this._emulatedColorScheme;
},
/**
* Sets the currently emulated color scheme or if an invalid value is given,
* the override is cleared.
*/
setEmulatedColorScheme(scheme = null) {
if (this._emulatedColorScheme === scheme) {
return;
}
let internalColorScheme;
switch (scheme) {
case "light":
internalColorScheme = Ci.nsIContentViewer.PREFERS_COLOR_SCHEME_LIGHT;
break;
case "dark":
internalColorScheme = Ci.nsIContentViewer.PREFERS_COLOR_SCHEME_DARK;
break;
case "no-preference":
internalColorScheme =
Ci.nsIContentViewer.PREFERS_COLOR_SCHEME_NO_PREFERENCE;
break;
default:
internalColorScheme = Ci.nsIContentViewer.PREFERS_COLOR_SCHEME_NONE;
}
this._emulatedColorScheme = scheme;
this.docShell.contentViewer.emulatePrefersColorScheme(internalColorScheme);
},
// The current emulated color scheme value. It's possible values are listed in the
// COLOR_SCHEMES constant in devtools/client/inspector/rules/constants.
_emulatedColorScheme: null,
/* Simulating print media for the page */
_printSimulationEnabled: false,
getIsPrintSimulationEnabled() {
return this._printSimulationEnabled;
},
async startPrintMediaSimulation() {
this._printSimulationEnabled = true;
this.targetActor.docShell.contentViewer.emulateMedium("print");
},
/**
* Stop simulating print media for the current page.
*
* @param {Boolean} state
* Whether or not to set _printSimulationEnabled to false. If true, we want to
* stop simulation print media for the current page but NOT set
* _printSimulationEnabled to false. We do this specifically for the
* "will-navigate" event where we still want to continue simulating print when
* navigating to the next page. Defaults to false, meaning we want to completely
* stop print simulation.
*/
async stopPrintMediaSimulation(state = false) {
this._printSimulationEnabled = state;
this.targetActor.docShell.contentViewer.stopEmulatingMedium();
},
});
exports.ContentViewerActor = ContentViewerActor;

View File

@ -5,5 +5,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DevToolsModules(
'content-viewer.js',
'responsive.js',
'touch-simulator.js',
)

View File

@ -0,0 +1,367 @@
/* 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 { responsiveSpec } = require("devtools/shared/specs/responsive");
loader.lazyRequireGetter(
this,
"ScreenshotActor",
"devtools/server/actors/screenshot",
true
);
loader.lazyRequireGetter(
this,
"TouchSimulator",
"devtools/server/actors/emulation/touch-simulator",
true
);
/**
* 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.
*/
const ResponsiveActor = protocol.ActorClassWithSpec(responsiveSpec, {
initialize(conn, targetActor) {
protocol.Actor.prototype.initialize.call(this, conn);
this.targetActor = targetActor;
this.docShell = targetActor.docShell;
},
destroy() {
this.clearDPPXOverride();
this.clearNetworkThrottling();
this.clearTouchEventsOverride();
this.clearMetaViewportOverride();
this.clearUserAgentOverride();
this.targetActor = null;
this.docShell = null;
this._screenshotActor = null;
this._touchSimulator = 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.targetActor.exited || !this.targetActor.actorID) {
return null;
}
const form = this.targetActor.form();
return this.conn._getOrCreateActor(form.consoleActor);
},
get screenshotActor() {
if (!this._screenshotActor) {
this._screenshotActor = new ScreenshotActor(this.conn, this.targetActor);
this.manage(this._screenshotActor);
}
return this._screenshotActor;
},
get touchSimulator() {
if (!this._touchSimulator) {
this._touchSimulator = new TouchSimulator(
this.targetActor.chromeEventHandler
);
}
return this._touchSimulator;
},
get win() {
return this.docShell.chromeEventHandler.ownerGlobal;
},
/* 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 }) {
const throttleData = {
latencyMean: latency,
latencyMax: latency,
downloadBPSMean: downloadThroughput,
downloadBPSMax: downloadThroughput,
uploadBPSMean: uploadThroughput,
uploadBPSMax: uploadThroughput,
};
return this._setNetworkThrottling(throttleData);
},
_setNetworkThrottling(throttleData) {
const 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;
}
const consoleActor = this._consoleActor;
if (!consoleActor) {
return false;
}
consoleActor.startListeners(["NetworkActivity"]);
consoleActor.setPreferences({
"NetworkMonitor.throttleData": throttleData,
});
return true;
},
/**
* Get network throttling and then transform the internal format into the RDP format.
*/
getNetworkThrottling() {
const throttleData = this._getNetworkThrottling();
if (!throttleData) {
return null;
}
const { downloadBPSMax, uploadBPSMax, latencyMax } = throttleData;
return {
downloadThroughput: downloadBPSMax,
uploadThroughput: uploadBPSMax,
latency: latencyMax,
};
},
_getNetworkThrottling() {
const consoleActor = this._consoleActor;
if (!consoleActor) {
return null;
}
const prefs = consoleActor.getPreferences(["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,
/**
* Set the current element picker state.
*
* True means the element picker is currently active and we should not be emulating
* touch events.
* False means the element picker is not active and it is ok to emulate touch events.
*
* This actor method is meant to be called by the DevTools front-end. The reason for
* this is the following:
* RDM is the only current consumer of the touch simulator. RDM instantiates this actor
* on its own, whether or not the Toolbox is opened. That means it does so in its own
* Debugger Server instance.
* When the Toolbox is running, it uses a different DebuggerServer. Therefore, it is not
* possible for the touch simulator to know whether the picker is active or not. This
* state has to be sent by the client code of the Toolbox to this actor.
* If a future use case arises where we want to use the touch simulator from the Toolbox
* too, then we could add code in here to detect the picker mode as described in
* https://bugzilla.mozilla.org/show_bug.cgi?id=1409085#c3
* @param {Boolean} state
*/
setElementPickerState(state) {
this.touchSimulator.setElementPickerState(state);
},
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.touchSimulator.start();
} else {
this.touchSimulator.stop();
}
this.docShell.touchEventsOverride = flag;
return true;
},
getTouchEventsOverride() {
return this.docShell.touchEventsOverride;
},
clearTouchEventsOverride() {
if (this._previousTouchEventsOverride !== undefined) {
return this.setTouchEventsOverride(this._previousTouchEventsOverride);
}
return false;
},
/* Meta viewport override */
_previousMetaViewportOverride: undefined,
setMetaViewportOverride(flag) {
if (this.getMetaViewportOverride() == flag) {
return false;
}
if (this._previousMetaViewportOverride === undefined) {
this._previousMetaViewportOverride = this.getMetaViewportOverride();
}
this.docShell.metaViewportOverride = flag;
return true;
},
getMetaViewportOverride() {
return this.docShell.metaViewportOverride;
},
clearMetaViewportOverride() {
if (this._previousMetaViewportOverride !== undefined) {
return this.setMetaViewportOverride(this._previousMetaViewportOverride);
}
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;
},
setScreenOrientation(type, angle) {
if (
this.win.screen.orientation.angle !== angle ||
this.win.screen.orientation.type !== type
) {
this.docShell.browsingContext.setRDMPaneOrientation(type, angle);
}
},
/**
* Simulates the "orientationchange" event when device screen is rotated.
*
* @param {String} type
* The orientation type of the rotated device.
* @param {Number} angle
* The rotated angle of the device.
* @param {Boolean} isViewportRotated
* Whether or not screen orientation change is a result of rotating the viewport.
* If true, then dispatch the "orientationchange" event on the content window.
*/
async simulateScreenOrientationChange(
type,
angle,
isViewportRotated = false
) {
// Don't dispatch the "orientationchange" event if orientation change is a result
// of switching to a new device, location change, or opening RDM.
if (!isViewportRotated) {
this.setScreenOrientation(type, angle);
return;
}
const { CustomEvent } = this.win;
const orientationChangeEvent = new CustomEvent("orientationchange");
this.setScreenOrientation(type, angle);
this.win.dispatchEvent(orientationChangeEvent);
},
async captureScreenshot() {
return this.screenshotActor.capture({});
},
async setDocumentInRDMPane(inRDMPane) {
if (this.docShell && this.docShell.document) {
this.docShell.browsingContext.inRDMPane = inRDMPane;
}
},
});
exports.ResponsiveActor = ResponsiveActor;

View File

@ -214,9 +214,14 @@ const ActorRegistry = {
constructor: "AnimationsActor",
type: { target: true },
});
this.registerModule("devtools/server/actors/emulation", {
prefix: "emulation",
constructor: "EmulationActor",
this.registerModule("devtools/server/actors/emulation/responsive", {
prefix: "responsive",
constructor: "ResponsiveActor",
type: { target: true },
});
this.registerModule("devtools/server/actors/emulation/content-viewer", {
prefix: "contentViewer",
constructor: "ContentViewerActor",
type: { target: true },
});
this.registerModule(

View File

@ -0,0 +1,26 @@
/* 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 {
FrontClassWithSpec,
registerFront,
} = require("devtools/shared/protocol");
const { contentViewerSpec } = require("devtools/shared/specs/content-viewer");
/**
* The corresponding Front object for the ContentViewer actor.
*/
class ContentViewerFront extends FrontClassWithSpec(contentViewerSpec) {
constructor(client, targetFront, parentFront) {
super(client, targetFront, parentFront);
// Attribute name from which to retrieve the actorID out of the target actor's form
this.formAttributeName = "contentViewerActor";
}
}
exports.ContentViewerFront = ContentViewerFront;
registerFront(ContentViewerFront);

View File

@ -12,6 +12,8 @@ const { emulationSpec } = require("devtools/shared/specs/emulation");
/**
* The corresponding Front object for the EmulationActor.
*
* Bug 1606852: Delete this file when Firefox 73 is on release.
*/
class EmulationFront extends FrontClassWithSpec(emulationSpec) {
constructor(client, targetFront, parentFront) {

View File

@ -18,6 +18,7 @@ DevToolsModules(
'animation.js',
'array-buffer.js',
'changes.js',
'content-viewer.js',
'css-properties.js',
'device.js',
'emulation.js',
@ -37,6 +38,7 @@ DevToolsModules(
'preference.js',
'property-iterator.js',
'reflow.js',
'responsive.js',
'root.js',
'screenshot.js',
'source.js',

View File

@ -0,0 +1,26 @@
/* 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 {
FrontClassWithSpec,
registerFront,
} = require("devtools/shared/protocol");
const { responsiveSpec } = require("devtools/shared/specs/responsive");
/**
* The corresponding Front object for the Responsive actor.
*/
class ResponsiveFront extends FrontClassWithSpec(responsiveSpec) {
constructor(client, targetFront, parentFront) {
super(client, targetFront, parentFront);
// Attribute name from which to retrieve the actorID out of the target actor's form
this.formAttributeName = "responsiveActor";
}
}
exports.ResponsiveFront = ResponsiveFront;
registerFront(ResponsiveFront);

View File

@ -0,0 +1,47 @@
/* 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 { Arg, RetVal, generateActorSpec } = require("devtools/shared/protocol");
const contentViewerSpec = generateActorSpec({
typeName: "contentViewer",
methods: {
getEmulatedColorScheme: {
request: {},
response: {
emulated: RetVal("nullable:string"),
},
},
setEmulatedColorScheme: {
request: {
scheme: Arg(0, "nullable:string"),
},
response: {},
},
getIsPrintSimulationEnabled: {
request: {},
response: {
enabled: RetVal("boolean"),
},
},
startPrintMediaSimulation: {
request: {},
response: {},
},
stopPrintMediaSimulation: {
request: {
state: Arg(0, "boolean"),
},
response: {},
},
},
});
exports.contentViewerSpec = contentViewerSpec;

View File

@ -5,6 +5,8 @@
const { Arg, RetVal, generateActorSpec } = require("devtools/shared/protocol");
// Bug 1606852: Delete this file when Firefox 73 is on release.
const emulationSpec = generateActorSpec({
typeName: "emulation",

View File

@ -47,6 +47,11 @@ const Types = (exports.__TypesForTests = [
spec: "devtools/shared/specs/changes",
front: "devtools/shared/fronts/changes",
},
{
types: ["contentViewer"],
spec: "devtools/shared/specs/content-viewer",
front: "devtools/shared/fronts/content-viewer",
},
{
types: ["cssProperties"],
spec: "devtools/shared/specs/css-properties",
@ -72,11 +77,6 @@ const Types = (exports.__TypesForTests = [
spec: "devtools/shared/specs/device",
front: "devtools/shared/fronts/device",
},
{
types: ["emulation"],
spec: "devtools/shared/specs/emulation",
front: "devtools/shared/fronts/emulation",
},
{
types: ["environment"],
spec: "devtools/shared/specs/environment",
@ -174,6 +174,11 @@ const Types = (exports.__TypesForTests = [
spec: "devtools/shared/specs/reflow",
front: "devtools/shared/fronts/reflow",
},
{
types: ["responsive"],
spec: "devtools/shared/specs/responsive",
front: "devtools/shared/fronts/responsive",
},
{
types: ["screenshot"],
spec: "devtools/shared/specs/screenshot",

View File

@ -17,6 +17,7 @@ DevToolsModules(
'animation.js',
'array-buffer.js',
'changes.js',
'content-viewer.js',
'css-properties.js',
'device.js',
'emulation.js',
@ -40,6 +41,7 @@ DevToolsModules(
'preference.js',
'property-iterator.js',
'reflow.js',
'responsive.js',
'root.js',
'screenshot.js',
'source.js',

View File

@ -0,0 +1,159 @@
/* 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 { Arg, RetVal, generateActorSpec } = require("devtools/shared/protocol");
const responsiveSpec = generateActorSpec({
typeName: "responsive",
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"),
},
},
getTouchEventsOverride: {
request: {},
response: {
flag: RetVal("number"),
},
},
clearTouchEventsOverride: {
request: {},
response: {
valueChanged: RetVal("boolean"),
},
},
setMetaViewportOverride: {
request: {
flag: Arg(0, "number"),
},
response: {
valueChanged: RetVal("boolean"),
},
},
getMetaViewportOverride: {
request: {},
response: {
flag: RetVal("number"),
},
},
clearMetaViewportOverride: {
request: {},
response: {
valueChanged: RetVal("boolean"),
},
},
setUserAgentOverride: {
request: {
flag: Arg(0, "string"),
},
response: {
valueChanged: RetVal("boolean"),
},
},
getUserAgentOverride: {
request: {},
response: {
userAgent: RetVal("string"),
},
},
clearUserAgentOverride: {
request: {},
response: {
valueChanged: RetVal("boolean"),
},
},
setElementPickerState: {
request: {
state: Arg(0, "boolean"),
},
response: {},
},
simulateScreenOrientationChange: {
request: {
orientation: Arg(0, "string"),
angle: Arg(1, "number"),
deviceChange: Arg(2, "boolean"),
},
response: {},
},
captureScreenshot: {
request: {},
response: {
value: RetVal("json"),
},
},
setDocumentInRDMPane: {
request: {
state: Arg(0, "boolean"),
},
response: {},
},
},
});
exports.responsiveSpec = responsiveSpec;