Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/devtools/client/responsive/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 33 kB image not shown  

Quelle  ui.js   Sprache: JAVA

 
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


"use strict";

const EventEmitter = require("resource://devtools/shared/event-emitter.js");
const {
  getOrientation,
} = require("resource://devtools/client/responsive/utils/orientation.js");
const Constants = require("resource://devtools/client/responsive/constants.js");
const {
  CommandsFactory,
} = require("resource://devtools/shared/commands/commands-factory.js");

loader.lazyRequireGetter(
  this,
  "throttlingProfiles",
  "resource://devtools/client/shared/components/throttling/profiles.js"
);
loader.lazyRequireGetter(
  this,
  "message",
  "resource://devtools/client/responsive/utils/message.js"
);
loader.lazyRequireGetter(
  this,
  "showNotification",
  "resource://devtools/client/responsive/utils/notification.js",
  true
);
loader.lazyRequireGetter(
  this,
  "PriorityLevels",
  "resource://devtools/client/shared/components/NotificationBox.js",
  true
);
loader.lazyRequireGetter(
  this,
  "l10n",
  "resource://devtools/client/responsive/utils/l10n.js"
);
loader.lazyRequireGetter(
  this,
  "asyncStorage",
  "resource://devtools/shared/async-storage.js"
);
loader.lazyRequireGetter(
  this,
  "captureAndSaveScreenshot",
  "resource://devtools/client/shared/screenshot.js",
  true
);

const RELOAD_CONDITION_PREF_PREFIX = "devtools.responsive.reloadConditions.";
const RELOAD_NOTIFICATION_PREF =
  "devtools.responsive.reloadNotification.enabled";

function debug(_msg) {
  // console.log(`RDM manager: ${_msg}`);
}

/**
 * ResponsiveUI manages the responsive design tool for a specific tab.  The
 * actual tool itself lives in a separate chrome:// document that is loaded into
 * the tab upon opening responsive design.  This object acts a helper to
 * integrate the tool into the surrounding browser UI as needed.
 */

class ResponsiveUI {
  /**
   * @param {ResponsiveUIManager} manager
   *        The ResponsiveUIManager instance.
   * @param {ChromeWindow} window
   *        The main browser chrome window (that holds many tabs).
   * @param {Tab} tab
   *        The specific browser <tab> element this responsive instance is for.
   */

  constructor(manager, window, tab) {
    this.manager = manager;
    // The main browser chrome window (that holds many tabs).
    this.browserWindow = window;
    // The specific browser tab this responsive instance is for.
    this.tab = tab;

    // Flag set when destruction has begun.
    this.destroying = false;
    // Flag set when destruction has ended.
    this.destroyed = false;
    // The iframe containing the RDM UI.
    this.rdmFrame = null;

    // Bind callbacks for resizers.
    this.onResizeDrag = this.onResizeDrag.bind(this);
    this.onResizeStart = this.onResizeStart.bind(this);
    this.onResizeStop = this.onResizeStop.bind(this);

    this.onTargetAvailable = this.onTargetAvailable.bind(this);

    this.networkFront = null;
    // Promise resolved when the UI init has completed.
    const { promise, resolve } = Promise.withResolvers();
    this.initialized = promise;
    this.resolveInited = resolve;

    EventEmitter.decorate(this);
  }

  get toolWindow() {
    return this.rdmFrame.contentWindow;
  }

  get docShell() {
    return this.toolWindow.docShell;
  }

  get viewportElement() {
    return this.browserStackEl.querySelector("browser");
  }

  get currentTarget() {
    return this.commands.targetCommand.targetFront;
  }

  get watcherFront() {
    return this.resourceCommand.watcherFront;
  }

  /**
   * Open RDM while preserving the state of the page.
   */

  async initialize() {
    debug("Init start");

    this.initRDMFrame();

    // Hide the browser content temporarily while things move around to avoid displaying
    // strange intermediate states.
    this.hideBrowserUI();

    // Watch for tab close and window close so we can clean up RDM synchronously
    this.tab.addEventListener("TabClose"this);
    this.browserWindow.addEventListener("unload"this);
    this.rdmFrame.contentWindow.addEventListener("message"this);

    this.tab.linkedBrowser.enterResponsiveMode();

    // Listen to FullZoomChange events coming from the browser window,
    // so that we can zoom the size of the viewport by the same amount.
    this.browserWindow.addEventListener("FullZoomChange"this);

    // Get the protocol ready to speak with responsive emulation actor
    debug("Wait until RDP server connect");
    await this.connectToServer();

    // Restore the previous UI state.
    await this.restoreUIState();

    // Show the browser UI now that its state is ready.
    this.showBrowserUI();

    // Non-blocking message to tool UI to start any delayed init activities
    message.post(this.toolWindow, "post-init");

    debug("Init done");
    this.resolveInited();
  }

  /**
   * Initialize the RDM iframe inside of the browser document.
   */

  initRDMFrame() {
    const { document: doc, gBrowser } = this.browserWindow;
    const rdmFrame = doc.createElement("iframe");
    rdmFrame.src = "chrome://devtools/content/responsive/toolbar.xhtml";
    rdmFrame.classList.add("rdm-toolbar");

    // Create resizer handlers
    const resizeHandle = doc.createElement("div");
    resizeHandle.classList.add(
      "rdm-viewport-resize-handle",
      "viewport-resize-handle"
    );
    const resizeHandleX = doc.createElement("div");
    resizeHandleX.classList.add(
      "rdm-viewport-resize-handle",
      "viewport-horizontal-resize-handle"
    );
    const resizeHandleY = doc.createElement("div");
    resizeHandleY.classList.add(
      "rdm-viewport-resize-handle",
      "viewport-vertical-resize-handle"
    );

    this.browserContainerEl = gBrowser.getBrowserContainer(
      gBrowser.getBrowserForTab(this.tab)
    );
    this.browserStackEl =
      this.browserContainerEl.querySelector(".browserStack");

    this.browserContainerEl.classList.add("responsive-mode");

    // Prepend the RDM iframe inside of the current tab's browser container.
    this.browserContainerEl.prepend(rdmFrame);

    this.browserStackEl.append(resizeHandle);
    this.browserStackEl.append(resizeHandleX);
    this.browserStackEl.append(resizeHandleY);

    // Wait for the frame script to be loaded.
    message.wait(rdmFrame.contentWindow, "script-init").then(async () => {
      // Notify the frame window that the Resposnive UI manager has begun initializing.
      // At this point, we can render our React content inside the frame.
      message.post(rdmFrame.contentWindow, "init");
      // Wait for the tools to be rendered above the content. The frame script will
      // then dispatch the necessary actions to the Redux store to give the toolbar the
      // state it needs.
      message.wait(rdmFrame.contentWindow, "init:done").then(() => {
        rdmFrame.contentWindow.addInitialViewport({
          userContextId: this.tab.userContextId,
        });
      });
    });

    this.rdmFrame = rdmFrame;

    this.resizeHandle = resizeHandle;
    this.resizeHandle.addEventListener("mousedown"this.onResizeStart);

    this.resizeHandleX = resizeHandleX;
    this.resizeHandleX.addEventListener("mousedown"this.onResizeStart);

    this.resizeHandleY = resizeHandleY;
    this.resizeHandleY.addEventListener("mousedown"this.onResizeStart);

    this.resizeToolbarObserver = new this.browserWindow.ResizeObserver(
      entries => {
        for (const entry of entries) {
          // If the toolbar needs extra space for the UA input, then set a class
          // that will accomodate its height. We should also make sure to keep
          // the width value we're toggling against in sync with the media-query
          // in devtools/client/responsive/index.css
          this.rdmFrame.classList.toggle(
            "accomodate-ua",
            entry.contentBoxSize[0].inlineSize < 520
          );
        }
      }
    );

    this.resizeToolbarObserver.observe(this.browserStackEl);
  }

  /**
   * Close RDM and restore page content back into a regular tab.
   *
   * @param object
   *        Destroy options, which currently includes a `reason` string.
   * @return boolean
   *         Whether this call is actually destroying.  False means destruction
   *         was already in progress.
   */

  async destroy(options) {
    if (this.destroying) {
      return false;
    }
    this.destroying = true;

    // If our tab is about to be closed, there's not enough time to exit
    // gracefully, but that shouldn't be a problem since the tab will go away.
    // So, skip any waiting when we're about to close the tab.
    const isTabDestroyed =
      !this.tab.linkedBrowser || this.responsiveFront?.isDestroyed();
    const isWindowClosing = options?.reason === "unload" || isTabDestroyed;
    const isTabContentDestroying =
      isWindowClosing || options?.reason === "TabClose";

    // Ensure init has finished before starting destroy
    if (!isTabContentDestroying) {
      await this.initialized;

      // Restore screen orientation of physical device.
      await Promise.all([
        this.updateScreenOrientation("landscape-primary", 0),
        this.updateMaxTouchPointsEnabled(false),
      ]);

      // Hide browser UI to avoid displaying weird intermediate states while closing.
      this.hideBrowserUI();

      // Resseting the throtting needs to be done before the
      // network events watching is stopped.
      await this.updateNetworkThrottling();
    }

    this.tab.removeEventListener("TabClose"this);
    this.browserWindow.removeEventListener("unload"this);
    this.tab.linkedBrowser.leaveResponsiveMode();

    this.browserWindow.removeEventListener("FullZoomChange"this);
    this.rdmFrame.contentWindow.removeEventListener("message"this);

    // Remove observers on the stack.
    this.resizeToolbarObserver.unobserve(this.browserStackEl);

    // Cleanup the frame content before disconnecting the frame element.
    this.rdmFrame.contentWindow.destroy();

    this.rdmFrame.remove();

    // Clean up resize handlers
    this.resizeHandle.remove();
    this.resizeHandleX.remove();
    this.resizeHandleY.remove();

    this.browserContainerEl.classList.remove("responsive-mode");
    this.browserStackEl.style.removeProperty("--rdm-width");
    this.browserStackEl.style.removeProperty("--rdm-height");
    this.browserStackEl.style.removeProperty("--rdm-zoom");

    // Ensure the tab is reloaded if required when exiting RDM so that no emulated
    // settings are left in a customized state.
    if (!isTabContentDestroying) {
      let reloadNeeded = false;
      await this.updateDPPX(null);
      reloadNeeded |=
        (await this.updateUserAgent()) && this.reloadOnChange("userAgent");

      // Don't reload on the server if we're already doing a reload on the client
      const reloadOnTouchSimulationChange =
        this.reloadOnChange("touchSimulation") && !reloadNeeded;
      await this.updateTouchSimulation(null, reloadOnTouchSimulationChange);
      if (reloadNeeded) {
        await this.reloadBrowser();
      }

      // Unwatch targets & resources as the last step. If we are not waching for
      // any resource & target anymore, the JSWindowActors will be unregistered
      // which will trigger an early destruction of the RDM target, before we
      // could finalize the cleanup.
      this.commands.targetCommand.unwatchTargets({
        types: [this.commands.targetCommand.TYPES.FRAME],
        onAvailable: this.onTargetAvailable,
      });

      this.resourceCommand.unwatchResources(
        [this.resourceCommand.TYPES.NETWORK_EVENT],
        { onAvailable: this.onNetworkResourceAvailable }
      );

      this.commands.targetCommand.destroy();
    }

    // Show the browser UI now.
    this.showBrowserUI();

    // Destroy local state
    this.browserContainerEl = null;
    this.browserStackEl = null;
    this.browserWindow = null;
    this.tab = null;
    this.initialized = null;
    this.rdmFrame = null;
    this.resizeHandle = null;
    this.resizeHandleX = null;
    this.resizeHandleY = null;
    this.resizeToolbarObserver = null;

    // Destroying the commands will close the devtools 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 commandsDestroyed = this.commands.destroy();
    if (!isTabContentDestroying) {
      await commandsDestroyed;
    }
    this.commands = this.responsiveFront = null;
    this.destroyed = true;

    return true;
  }

  async connectToServer() {
    this.commands = await CommandsFactory.forTab(this.tab);
    this.resourceCommand = this.commands.resourceCommand;

    await this.commands.targetCommand.startListening();

    await this.commands.targetCommand.watchTargets({
      types: [this.commands.targetCommand.TYPES.FRAME],
      onAvailable: this.onTargetAvailable,
    });

    // To support network throttling the resource command
    // needs to be watching for network resources.
    await this.resourceCommand.watchResources(
      [this.resourceCommand.TYPES.NETWORK_EVENT],
      { onAvailable: this.onNetworkResourceAvailable }
    );

    this.networkFront = await this.watcherFront.getNetworkParentActor();
  }

  /**
   * Show one-time notification about reloads for responsive emulation.
   */

  showReloadNotification() {
    if (Services.prefs.getBoolPref(RELOAD_NOTIFICATION_PREF, false)) {
      showNotification(this.browserWindow, this.tab, {
        msg: l10n.getFormatStr("responsive.reloadNotification.description2"),
      });
      Services.prefs.setBoolPref(RELOAD_NOTIFICATION_PREF, false);
    }
  }

  reloadOnChange(id) {
    this.showReloadNotification();
    const pref = RELOAD_CONDITION_PREF_PREFIX + id;
    return Services.prefs.getBoolPref(pref, false);
  }

  hideBrowserUI() {
    this.tab.linkedBrowser.style.visibility = "hidden";
    this.resizeHandle.style.visibility = "hidden";
  }

  showBrowserUI() {
    this.tab.linkedBrowser.style.removeProperty("visibility");
    this.resizeHandle.style.removeProperty("visibility");
  }

  handleEvent(event) {
    const { browserWindow, tab } = this;

    switch (event.type) {
      case "message":
        this.handleMessage(event);
        break;
      case "FullZoomChange":
        // Get the current device size and update to that size, which
        // will pick up changes to the zoom.
        const { width, height } = this.getViewportSize();
        this.updateViewportSize(width, height);
        break;
      case "TabClose":
      case "unload":
        this.manager.closeIfNeeded(browserWindow, tab, {
          reason: event.type,
        });
        break;
    }
  }

  handleMessage(event) {
    if (event.origin !== "chrome://devtools") {
      return;
    }

    switch (event.data.type) {
      case "change-device":
        this.onChangeDevice(event);
        break;
      case "change-network-throttling":
        this.onChangeNetworkThrottling(event);
        break;
      case "change-pixel-ratio":
        this.onChangePixelRatio(event);
        break;
      case "change-touch-simulation":
        this.onChangeTouchSimulation(event);
        break;
      case "change-user-agent":
        this.onChangeUserAgent(event);
        break;
      case "exit":
        this.onExit();
        break;
      case "remove-device-association":
        this.onRemoveDeviceAssociation(event);
        break;
      case "viewport-orientation-change":
        this.onRotateViewport(event);
        break;
      case "viewport-resize":
        this.onResizeViewport(event);
        break;
      case "screenshot":
        this.onScreenshot();
        break;
      case "toggle-left-alignment":
        this.onToggleLeftAlignment(event);
        break;
      case "update-device-modal":
        this.onUpdateDeviceModal(event);
        break;
    }
  }

  async onChangeDevice(event) {
    const { pixelRatio, touch, userAgent } = event.data.device;
    let reloadNeeded = false;
    await this.updateDPPX(pixelRatio);

    // Get the orientation values of the device we are changing to and update.
    const { device, viewport } = event.data;
    const { type, angle } = getOrientation(device, viewport);
    await this.updateScreenOrientation(type, angle);
    await this.updateMaxTouchPointsEnabled(touch);

    reloadNeeded |=
      (await this.updateUserAgent(userAgent)) &&
      this.reloadOnChange("userAgent");

    // Don't reload on the server if we're already doing a reload on the client
    const reloadOnTouchSimulationChange =
      this.reloadOnChange("touchSimulation") && !reloadNeeded;
    await this.updateTouchSimulation(touch, reloadOnTouchSimulationChange);

    if (reloadNeeded) {
      this.reloadBrowser();
    }

    // Used by tests
    this.emitForTests("device-changed", {
      reloadTriggered: reloadNeeded || reloadOnTouchSimulationChange,
    });
  }

  async onChangeNetworkThrottling(event) {
    const { enabled, profile } = event.data;
    await this.updateNetworkThrottling(enabled, profile);
    // Used by tests
    this.emit("network-throttling-changed");
  }

  onChangePixelRatio(event) {
    const { pixelRatio } = event.data;
    this.updateDPPX(pixelRatio);
  }

  async onChangeTouchSimulation(event) {
    const { enabled } = event.data;

    await this.updateMaxTouchPointsEnabled(enabled);

    await this.updateTouchSimulation(
      enabled,
      this.reloadOnChange("touchSimulation")
    );

    // Used by tests
    this.emit("touch-simulation-changed");
  }

  async onChangeUserAgent(event) {
    const { userAgent } = event.data;
    const reloadNeeded =
      (await this.updateUserAgent(userAgent)) &&
      this.reloadOnChange("userAgent");
    if (reloadNeeded) {
      this.reloadBrowser();
    }
    this.emit("user-agent-changed");
  }

  onExit() {
    const { browserWindow, tab } = this;
    this.manager.closeIfNeeded(browserWindow, tab);
  }

  async onRemoveDeviceAssociation(event) {
    const { resetProfile } = event.data;

    if (resetProfile) {
      let reloadNeeded = false;
      await this.updateDPPX(null);
      reloadNeeded |=
        (await this.updateUserAgent()) && this.reloadOnChange("userAgent");

      // Don't reload on the server if we're already doing a reload on the client
      const reloadOnTouchSimulationChange =
        this.reloadOnChange("touchSimulation") && !reloadNeeded;
      await this.updateTouchSimulation(null, reloadOnTouchSimulationChange);
      if (reloadNeeded) {
        this.reloadBrowser();
      }
    }

    // Used by tests
    this.emitForTests("device-association-removed");
  }

  /**
   * Resizing the browser on mousemove
   */

  onResizeDrag({ screenX, screenY }) {
    if (!this.isResizing || !this.rdmFrame.contentWindow) {
      return;
    }

    const zoom = this.tab.linkedBrowser.fullZoom;

    let deltaX = (screenX - this.lastScreenX) / zoom;
    let deltaY = (screenY - this.lastScreenY) / zoom;

    const leftAlignmentEnabled = Services.prefs.getBoolPref(
      "devtools.responsive.leftAlignViewport.enabled",
      false
    );

    if (!leftAlignmentEnabled) {
      // The viewport is centered horizontally, so horizontal resize resizes
      // by twice the distance the mouse was dragged - on left and right side.
      deltaX = deltaX * 2;
    }

    if (this.ignoreX) {
      deltaX = 0;
    }
    if (this.ignoreY) {
      deltaY = 0;
    }

    const viewportSize = this.getViewportSize();

    let width = Math.round(viewportSize.width + deltaX);
    let height = Math.round(viewportSize.height + deltaY);

    if (width < Constants.MIN_VIEWPORT_DIMENSION) {
      width = Constants.MIN_VIEWPORT_DIMENSION;
    } else if (width != viewportSize.width) {
      this.lastScreenX = screenX;
    }

    if (height < Constants.MIN_VIEWPORT_DIMENSION) {
      height = Constants.MIN_VIEWPORT_DIMENSION;
    } else if (height != viewportSize.height) {
      this.lastScreenY = screenY;
    }

    // Update the RDM store and viewport size with the new width and height.
    this.rdmFrame.contentWindow.setViewportSize({ width, height });
    this.updateViewportSize(width, height);

    // Change the device selector back to an unselected device
    if (this.rdmFrame.contentWindow.getAssociatedDevice()) {
      this.rdmFrame.contentWindow.clearDeviceAssociation();
    }
  }

  /**
   * Start the process of resizing the browser.
   */

  onResizeStart({ target, screenX, screenY }) {
    this.browserWindow.addEventListener("mousemove"this.onResizeDrag, true);
    this.browserWindow.addEventListener("mouseup"this.onResizeStop, true);

    this.isResizing = true;
    this.lastScreenX = screenX;
    this.lastScreenY = screenY;
    this.ignoreX = target === this.resizeHandleY;
    this.ignoreY = target === this.resizeHandleX;
  }

  /**
   * Stop the process of resizing the browser.
   */

  onResizeStop() {
    this.browserWindow.removeEventListener(
      "mousemove",
      this.onResizeDrag,
      true
    );
    this.browserWindow.removeEventListener("mouseup"this.onResizeStop, true);

    this.isResizing = false;
    this.lastScreenX = 0;
    this.lastScreenY = 0;
    this.ignoreX = false;
    this.ignoreY = false;

    // Used by tests.
    this.emit("viewport-resize-dragend");
  }

  onResizeViewport(event) {
    const { width, height } = event.data;
    this.updateViewportSize(width, height);
    this.emit("viewport-resize", {
      width,
      height,
    });
  }

  async onRotateViewport(event) {
    const { orientationType: type, angle, isViewportRotated } = event.data;
    await this.updateScreenOrientation(type, angle, isViewportRotated);
  }

  async onScreenshot() {
    const messages = await captureAndSaveScreenshot(
      this.currentTarget,
      this.browserWindow
    );

    const priorityMap = {
      error: PriorityLevels.PRIORITY_CRITICAL_HIGH,
      warn: PriorityLevels.PRIORITY_WARNING_HIGH,
    };
    for (const { text, level } of messages) {
      // captureAndSaveScreenshot returns "saved" messages, that indicate where the
      // screenshot was saved. We don't want to display them as the download UI can be
      // used to open the file.
      if (level !== "warn" && level !== "error") {
        continue;
      }

      showNotification(this.browserWindow, this.tab, {
        msg: text,
        priority: priorityMap[level],
      });
    }

    message.post(this.rdmFrame.contentWindow, "screenshot-captured");
  }

  onToggleLeftAlignment(event) {
    this.updateUIAlignment(event.data.leftAlignmentEnabled);
  }

  onUpdateDeviceModal(event) {
    this.rdmFrame.classList.toggle("device-modal-opened", event.data.isOpen);
  }

  async hasDeviceState() {
    const deviceState = await asyncStorage.getItem(
      "devtools.responsive.deviceState"
    );
    return !!deviceState;
  }

  /**
   * Restores the previous UI state.
   */

  async restoreUIState() {
    const leftAlignmentEnabled = Services.prefs.getBoolPref(
      "devtools.responsive.leftAlignViewport.enabled",
      false
    );

    this.updateUIAlignment(leftAlignmentEnabled);

    const height = Services.prefs.getIntPref(
      "devtools.responsive.viewport.height",
      0
    );
    const width = Services.prefs.getIntPref(
      "devtools.responsive.viewport.width",
      0
    );
    this.updateViewportSize(width, height);
  }

  /**
   * Restores the previous actor state.
   *
   * @param {Boolean} isTargetSwitching
   */

  async restoreActorState(isTargetSwitching) {
    // It's possible the target will switch to a page loaded in the
    // parent-process (i.e: about:robots). When this happens, the values set
    // on the BrowsingContext by RDM are not preserved. So we need to call
    // enterResponsiveMode whenever there is a target switch.
    this.tab.linkedBrowser.enterResponsiveMode();

    // If the target follows the window global lifecycle, the configuration was already
    // restored from the server during target switch, so we can stop here.
    // This function is still called at startup to restore potential state from previous
    // RDM session so we only stop here during target switching.
    if (
      isTargetSwitching &&
      this.commands.targetCommand.targetFront.targetForm
        .followWindowGlobalLifeCycle
    ) {
      return;
    }

    const hasDeviceState = await this.hasDeviceState();
    if (hasDeviceState) {
      // Return if there is a device state to restore, this will be done when the
      // device list is loaded after the post-init.
      return;
    }

    const height = Services.prefs.getIntPref(
      "devtools.responsive.viewport.height",
      0
    );
    const pixelRatio = Services.prefs.getIntPref(
      "devtools.responsive.viewport.pixelRatio",
      0
    );
    const touchSimulationEnabled = Services.prefs.getBoolPref(
      "devtools.responsive.touchSimulation.enabled",
      false
    );
    const userAgent = Services.prefs.getCharPref(
      "devtools.responsive.userAgent",
      ""
    );
    const width = Services.prefs.getIntPref(
      "devtools.responsive.viewport.width",
      0
    );

    // Restore the previously set orientation, or get it from the initial viewport if it
    // wasn't set yet.
    const { type, angle } =
      this.commands.targetConfigurationCommand.configuration
        .rdmPaneOrientation ||
      this.getInitialViewportOrientation({
        width,
        height,
      });

    await this.updateDPPX(pixelRatio);
    await this.updateScreenOrientation(type, angle);
    await this.updateMaxTouchPointsEnabled(touchSimulationEnabled);

    if (touchSimulationEnabled) {
      await this.updateTouchSimulation(touchSimulationEnabled);
    }

    let reloadNeeded = false;
    if (userAgent) {
      reloadNeeded |=
        (await this.updateUserAgent(userAgent)) &&
        this.reloadOnChange("userAgent");
    }
    if (reloadNeeded) {
      await this.reloadBrowser();
    }
  }

  /**
   * Set or clear the emulated device pixel ratio.
   *
   * @param {Number|null} dppx: The ratio to simulate. Set to null to disable the
   *                      simulation and roll back to the original ratio
   */

  async updateDPPX(dppx = null) {
    await this.commands.targetConfigurationCommand.updateConfiguration({
      overrideDPPX: dppx,
    });
  }

  /**
   * Set or clear network throttling.
   *
   * @return boolean
   *         Whether a reload is needed to apply the change.
   *         (This is always immediate, so it's always false.)
   */

  async updateNetworkThrottling(enabled, profile) {
    if (!enabled) {
      await this.networkFront.clearNetworkThrottling();
      await this.commands.targetConfigurationCommand.updateConfiguration({
        setTabOffline: false,
      });
      return false;
    }
    const data = throttlingProfiles.profiles.find(({ id }) => id == profile);
    const { download, upload, latency, id } = data;

    // Update offline mode
    await this.commands.targetConfigurationCommand.updateConfiguration({
      setTabOffline: id === throttlingProfiles.PROFILE_CONSTANTS.OFFLINE,
    });

    await this.networkFront.setNetworkThrottling({
      downloadThroughput: download,
      uploadThroughput: upload,
      latency,
    });
    return false;
  }

  /**
   * Set or clear the emulated user agent.
   *
   * @param {String|null} userAgent: The user agent to set on the page. Set to null to revert
   *                      the user agent to its original value
   * @return {Boolean} Whether a reload is needed to apply the change.
   */

  async updateUserAgent(userAgent) {
    const getConfigurationCustomUserAgent = () =>
      this.commands.targetConfigurationCommand.configuration.customUserAgent ||
      "";
    const previousCustomUserAgent = getConfigurationCustomUserAgent();
    await this.commands.targetConfigurationCommand.updateConfiguration({
      customUserAgent: userAgent,
    });

    const updatedUserAgent = getConfigurationCustomUserAgent();
    return previousCustomUserAgent !== updatedUserAgent;
  }

  /**
   * Set or clear touch simulation. When setting to true, this method will
   * additionally set meta viewport override.
   * When setting to false, this method will clear all touch simulation and meta viewport
   * overrides, returning to default behavior for both settings.
   *
   * @param {boolean} enabled
   * @param {boolean} reloadOnTouchSimulationToggle: Set to true to trigger a page reload
   *        if the touch simulation state changes.
   */

  async updateTouchSimulation(enabled, reloadOnTouchSimulationToggle) {
    await this.commands.targetConfigurationCommand.updateConfiguration({
      touchEventsOverride: enabled ? "enabled" : null,
      reloadOnTouchSimulationToggle,
    });
  }

  /**
   * Sets the screen orientation values of the simulated device.
   *
   * @param {String} type
   *        The orientation type to update the current device screen to.
   * @param {Number} angle
   *        The rotation angle to update the current device screen to.
   * @param {Boolean} isViewportRotated
   *        Whether or not the reason for updating the screen orientation is a result
   *        of actually rotating the device via the RDM toolbar. If true, then an
   *        "orientationchange" event is simulated. Otherwise, the screen orientation is
   *        updated because of changing devices, opening RDM, or the page has been
   *        reloaded/navigated to, so we should not be simulating "orientationchange".
   */

  async updateScreenOrientation(type, angle, isViewportRotated = false) {
    await this.commands.targetConfigurationCommand.simulateScreenOrientationChange(
      {
        type,
        angle,
        isViewportRotated,
      }
    );
  }

  /**
   * Sets whether or not maximum touch points are supported for the simulated device.
   *
   * @param {Boolean} touchSimulationEnabled
   *        Whether or not touch is enabled for the simulated device.
   */

  async updateMaxTouchPointsEnabled(touchSimulationEnabled) {
    return this.commands.targetConfigurationCommand.updateConfiguration({
      rdmPaneMaxTouchPoints: touchSimulationEnabled ? 1 : 0,
    });
  }

  /**
   * Sets whether or not the RDM UI should be left-aligned.
   *
   * @param {Boolean} leftAlignmentEnabled
   *        Whether or not the UI is left-aligned.
   */

  updateUIAlignment(leftAlignmentEnabled) {
    this.browserContainerEl.classList.toggle(
      "left-aligned",
      leftAlignmentEnabled
    );
  }

  /**
   * Sets the browser element to be the given width and height.
   *
   * @param {Number} width
   *        The viewport's width.
   * @param {Number} height
   *        The viewport's height.
   */

  updateViewportSize(width, height) {
    const zoom = this.tab.linkedBrowser.fullZoom;

    // Setting this with a variable on the stack instead of directly as width/height
    // on the <browser> because we'll need to use this for the alert dialog as well.
    this.browserStackEl.style.setProperty("--rdm-width", `${width}px`);
    this.browserStackEl.style.setProperty("--rdm-height", `${height}px`);
    this.browserStackEl.style.setProperty("--rdm-zoom", zoom);

    // This is a bit premature, but we emit a content-resize event here. It
    // would be preferrable to wait until the viewport is actually resized,
    // but the "resize" event is not triggered by this style change. The
    // content-resize message is only used by tests, and if needed those tests
    // can use the testing function setViewportSizeAndAwaitReflow to ensure
    // the viewport has had time to reach this size.
    this.emit("content-resize", {
      width,
      height,
    });
  }

  /**
   * Helper for tests. Assumes a single viewport for now.
   */

  getViewportSize() {
    // The getViewportSize function is loaded in index.js, and might not be
    // available yet.
    if (this.toolWindow.getViewportSize) {
      return this.toolWindow.getViewportSize();
    }

    return { width: 0, height: 0 };
  }

  /**
   * Helper for tests, etc. Assumes a single viewport for now.
   */

  async setViewportSize(size) {
    await this.initialized;

    // Ensure that width and height are valid.
    let { width, height } = size;
    if (!size.width) {
      width = this.getViewportSize().width;
    }

    if (!size.height) {
      height = this.getViewportSize().height;
    }

    this.rdmFrame.contentWindow.setViewportSize({ width, height });
    this.updateViewportSize(width, height);
  }

  /**
   * Helper for tests/reloading the viewport. Assumes a single viewport for now.
   */

  getViewportBrowser() {
    return this.tab.linkedBrowser;
  }

  /**
   * Helper for contacting the viewport content. Assumes a single viewport for now.
   */

  getViewportMessageManager() {
    return this.getViewportBrowser().messageManager;
  }

  /**
   * Helper for getting the initial viewport orientation.
   */

  getInitialViewportOrientation(viewport) {
    return getOrientation(viewport, viewport);
  }

  /**
   * Helper for tests to get the browser's window.
   */

  getBrowserWindow() {
    return this.browserWindow;
  }

  async onTargetAvailable({ targetFront, isTargetSwitching }) {
    if (this.destroying) {
      return;
    }

    if (targetFront.isTopLevel) {
      this.responsiveFront = await targetFront.getFront("responsive");

      if (this.destroying) {
        return;
      }

      await this.restoreActorState(isTargetSwitching);
      this.emitForTests("responsive-ui-target-switch-done");
    }
  }
  // This just needed to setup watching for network resources,
  // to support network throttling.
  onNetworkResourceAvailable() {}

  /**
   * Reload the current tab.
   */

  async reloadBrowser() {
    await this.commands.targetCommand.reloadTopLevelTarget();
  }
}

module.exports = ResponsiveUI;

Messung V0.5
C=88 H=94 G=90

¤ Dauer der Verarbeitung: 0.9 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.