tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

_configuration.sys.mjs (16046B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 import { WindowGlobalBiDiModule } from "chrome://remote/content/webdriver-bidi/modules/WindowGlobalBiDiModule.sys.mjs";
      6 
      7 const lazy = {};
      8 
      9 ChromeUtils.defineESModuleGetters(lazy, {
     10  ContextDescriptorType:
     11    "chrome://remote/content/shared/messagehandler/MessageHandler.sys.mjs",
     12  RootMessageHandler:
     13    "chrome://remote/content/shared/messagehandler/RootMessageHandler.sys.mjs",
     14  WindowGlobalMessageHandler:
     15    "chrome://remote/content/shared/messagehandler/WindowGlobalMessageHandler.sys.mjs",
     16 });
     17 
     18 /**
     19 * Internal module to set the configuration on the newly created navigables.
     20 */
     21 class _ConfigurationModule extends WindowGlobalBiDiModule {
     22  #geolocationConfiguration;
     23  #localeOverride;
     24  #preloadScripts;
     25  #resolveBlockerPromise;
     26  #screenOrientationOverride;
     27  #screenSettingsOverride;
     28  #timezoneOverride;
     29  #userAgentOverride;
     30  #viewportConfiguration;
     31 
     32  constructor(messageHandler) {
     33    super(messageHandler);
     34 
     35    this.#geolocationConfiguration = undefined;
     36    this.#localeOverride = null;
     37    this.#preloadScripts = new Set();
     38    this.#screenOrientationOverride = undefined;
     39    this.#screenSettingsOverride = undefined;
     40    this.#timezoneOverride = null;
     41    this.#userAgentOverride = null;
     42    this.#viewportConfiguration = new Map();
     43 
     44    Services.obs.addObserver(this, "document-element-inserted");
     45  }
     46 
     47  destroy() {
     48    // Unblock the document parsing.
     49    if (this.#resolveBlockerPromise) {
     50      this.#resolveBlockerPromise();
     51    }
     52 
     53    Services.obs.removeObserver(this, "document-element-inserted");
     54 
     55    this.#preloadScripts = null;
     56    this.#viewportConfiguration = null;
     57  }
     58 
     59  async observe(subject, topic) {
     60    if (topic === "document-element-inserted") {
     61      const window = subject?.defaultView;
     62      // Ignore events without a window.
     63      if (window !== this.messageHandler.window) {
     64        return;
     65      }
     66 
     67      // Do nothing if there is no configuration to apply.
     68      if (
     69        this.#preloadScripts.size === 0 &&
     70        this.#viewportConfiguration.size === 0 &&
     71        this.#geolocationConfiguration === undefined &&
     72        this.#localeOverride === null &&
     73        this.#screenOrientationOverride === undefined &&
     74        this.#screenSettingsOverride === undefined &&
     75        this.#timezoneOverride === null &&
     76        this.#userAgentOverride === null
     77      ) {
     78        this.#onConfigurationComplete(window);
     79        return;
     80      }
     81 
     82      // Block document parsing.
     83      const blockerPromise = new Promise(resolve => {
     84        this.#resolveBlockerPromise = resolve;
     85      });
     86      window.document.blockParsing(blockerPromise);
     87 
     88      // Usually rendering is blocked until layout is started implicitly (by
     89      // end of parsing) or explicitly. Since we block the implicit
     90      // initialization and some code we call may block on it (like waiting for
     91      // requestAnimationFrame or viewport dimensions), we initialize it
     92      // explicitly here by forcing a layout flush. Note that this will cause
     93      // flashes of unstyled content, but that was already the case before
     94      // bug 1958942.
     95      window.document.documentElement.getBoundingClientRect();
     96 
     97      if (this.#geolocationConfiguration !== undefined) {
     98        await this.messageHandler.handleCommand({
     99          moduleName: "emulation",
    100          commandName: "_setGeolocationOverride",
    101          destination: {
    102            type: lazy.WindowGlobalMessageHandler.type,
    103            id: this.messageHandler.context.id,
    104          },
    105          params: {
    106            coordinates: this.#geolocationConfiguration,
    107          },
    108        });
    109      }
    110 
    111      if (this.#localeOverride !== null) {
    112        await this.messageHandler.forwardCommand({
    113          moduleName: "emulation",
    114          commandName: "_setLocaleForBrowsingContext",
    115          destination: {
    116            type: lazy.RootMessageHandler.type,
    117          },
    118          params: {
    119            context: this.messageHandler.context,
    120            value: this.#localeOverride,
    121          },
    122        });
    123      }
    124 
    125      // Compare with `undefined`, since `null` value is used as a reset value.
    126      if (this.#screenSettingsOverride !== undefined) {
    127        await this.messageHandler.forwardCommand({
    128          moduleName: "emulation",
    129          commandName: "_setScreenSettingsOverride",
    130          destination: {
    131            type: lazy.RootMessageHandler.type,
    132          },
    133          params: {
    134            context: this.messageHandler.context,
    135            value: this.#screenSettingsOverride,
    136          },
    137        });
    138      }
    139 
    140      if (this.#timezoneOverride !== null) {
    141        await this.messageHandler.forwardCommand({
    142          moduleName: "emulation",
    143          commandName: "_setTimezoneOverride",
    144          destination: {
    145            type: lazy.RootMessageHandler.type,
    146          },
    147          params: {
    148            context: this.messageHandler.context,
    149            value: this.#timezoneOverride,
    150          },
    151        });
    152      }
    153 
    154      if (this.#userAgentOverride !== null) {
    155        await this.messageHandler.forwardCommand({
    156          moduleName: "emulation",
    157          commandName: "_setUserAgentOverride",
    158          destination: {
    159            type: lazy.RootMessageHandler.type,
    160          },
    161          params: {
    162            context: this.messageHandler.context,
    163            value: this.#userAgentOverride,
    164          },
    165        });
    166      }
    167 
    168      if (this.#screenOrientationOverride !== undefined) {
    169        await this.messageHandler.forwardCommand({
    170          moduleName: "emulation",
    171          commandName: "_setEmulatedScreenOrientation",
    172          destination: {
    173            type: lazy.RootMessageHandler.type,
    174          },
    175          params: {
    176            context: this.messageHandler.context,
    177            value: this.#screenOrientationOverride,
    178          },
    179        });
    180      }
    181 
    182      if (this.#viewportConfiguration.size !== 0) {
    183        await this.messageHandler.forwardCommand({
    184          moduleName: "browsingContext",
    185          commandName: "_updateNavigableViewport",
    186          destination: {
    187            type: lazy.RootMessageHandler.type,
    188          },
    189          params: {
    190            navigable: this.messageHandler.context,
    191            viewportOverride: Object.fromEntries(this.#viewportConfiguration),
    192          },
    193        });
    194      }
    195 
    196      if (this.#preloadScripts.size !== 0) {
    197        await this.messageHandler.handleCommand({
    198          moduleName: "script",
    199          commandName: "_evaluatePreloadScripts",
    200          destination: {
    201            type: lazy.WindowGlobalMessageHandler.type,
    202            id: this.messageHandler.context.id,
    203          },
    204          params: {
    205            scripts: this.#preloadScripts,
    206          },
    207        });
    208      }
    209 
    210      // Continue script parsing.
    211      this.#resolveBlockerPromise();
    212      this.#onConfigurationComplete(window);
    213    }
    214  }
    215 
    216  /**
    217   * Check if the provided value matches the provided type.
    218   *
    219   * @param {*} value
    220   *     The value to verify.
    221   * @param {string} type
    222   *     The type to match.
    223   *
    224   * @returns {boolean}
    225   *     Returns true if the value type is the same as
    226   *     the provided type. False, otherwise.
    227   */
    228  #isOfType(value, type) {
    229    if (type === "object") {
    230      return typeof value === "object" && value !== null;
    231    }
    232 
    233    return typeof value === type;
    234  }
    235 
    236  /**
    237   * For some emulations a value set per a browsing context overrides
    238   * a value set per a user context or set globally. And a value set per
    239   * a user context overrides a global value.
    240   *
    241   * @param {string} type
    242   *     The type to verify that the value was set.
    243   * @param {*} contextValue
    244   *     The override value set per browsing context.
    245   * @param {*} userContextValue
    246   *     The override value set per user context.
    247   * @param {*} globalValue
    248   *     The override value set globally.
    249   *
    250   * @returns {*}
    251   *     Returns the override value which should be applied.
    252   */
    253  #findCorrectOverrideValue(type, contextValue, userContextValue, globalValue) {
    254    if (this.#isOfType(contextValue, type)) {
    255      return contextValue;
    256    }
    257    if (this.#isOfType(userContextValue, type)) {
    258      return userContextValue;
    259    }
    260    if (this.#isOfType(globalValue, type)) {
    261      return globalValue;
    262    }
    263    return null;
    264  }
    265 
    266  async #onConfigurationComplete(window) {
    267    // parser blocking doesn't work for initial about:blank, so ensure
    268    // browsing_context.create waits for configuration to complete
    269    if (window.document.isInitialDocument) {
    270      await this.messageHandler.forwardCommand({
    271        moduleName: "browsingContext",
    272        commandName: "_onConfigurationComplete",
    273        destination: {
    274          type: lazy.RootMessageHandler.type,
    275        },
    276        params: {
    277          navigable: this.messageHandler.context,
    278        },
    279      });
    280    }
    281  }
    282 
    283  #updatePreloadScripts(sessionData) {
    284    this.#preloadScripts.clear();
    285 
    286    for (const { contextDescriptor, value } of sessionData) {
    287      if (!this.messageHandler.matchesContext(contextDescriptor)) {
    288        continue;
    289      }
    290 
    291      this.#preloadScripts.add(value);
    292    }
    293  }
    294 
    295  /**
    296   * Internal commands
    297   */
    298 
    299  _applySessionData(params) {
    300    const { category, sessionData } = params;
    301 
    302    if (category === "preload-script") {
    303      this.#updatePreloadScripts(sessionData);
    304    }
    305 
    306    // The following overrides apply only to top-level traversables.
    307    if (
    308      [
    309        "geolocation-override",
    310        "locale-override",
    311        "screen-orientation-override",
    312        "screen-settings-override",
    313        "timezone-override",
    314        "user-agent-override",
    315        "viewport-overrides",
    316      ].includes(category) &&
    317      !this.messageHandler.context.parent
    318    ) {
    319      let geolocationOverridePerContext = null;
    320      let geolocationOverridePerUserContext = null;
    321 
    322      let localeOverridePerContext = null;
    323      let localeOverridePerUserContext = null;
    324 
    325      let screenOrientationOverridePerContext = null;
    326      let screenOrientationOverridePerUserContext = null;
    327 
    328      let screenSettingsOverridePerContext = null;
    329      let screenSettingsOverridePerUserContext = null;
    330 
    331      let timezoneOverridePerContext = null;
    332      let timezoneOverridePerUserContext = null;
    333 
    334      let userAgentOverrideGlobal = null;
    335      let userAgentOverridePerUserContext = null;
    336      let userAgentOverridePerContext = null;
    337 
    338      for (const { contextDescriptor, value } of sessionData) {
    339        if (!this.messageHandler.matchesContext(contextDescriptor)) {
    340          continue;
    341        }
    342 
    343        switch (category) {
    344          case "geolocation-override": {
    345            switch (contextDescriptor.type) {
    346              case lazy.ContextDescriptorType.TopBrowsingContext: {
    347                geolocationOverridePerContext = value;
    348                break;
    349              }
    350              case lazy.ContextDescriptorType.UserContext: {
    351                geolocationOverridePerUserContext = value;
    352                break;
    353              }
    354            }
    355            break;
    356          }
    357          case "viewport-overrides": {
    358            if (value.viewport !== undefined) {
    359              this.#viewportConfiguration.set("viewport", value.viewport);
    360            }
    361 
    362            if (value.devicePixelRatio !== undefined) {
    363              this.#viewportConfiguration.set(
    364                "devicePixelRatio",
    365                value.devicePixelRatio
    366              );
    367            }
    368            break;
    369          }
    370          case "locale-override": {
    371            switch (contextDescriptor.type) {
    372              case lazy.ContextDescriptorType.TopBrowsingContext: {
    373                localeOverridePerContext = value;
    374                break;
    375              }
    376              case lazy.ContextDescriptorType.UserContext: {
    377                localeOverridePerUserContext = value;
    378                break;
    379              }
    380            }
    381            break;
    382          }
    383          case "screen-orientation-override": {
    384            switch (contextDescriptor.type) {
    385              case lazy.ContextDescriptorType.TopBrowsingContext: {
    386                screenOrientationOverridePerContext = value;
    387                break;
    388              }
    389              case lazy.ContextDescriptorType.UserContext: {
    390                screenOrientationOverridePerUserContext = value;
    391                break;
    392              }
    393            }
    394            break;
    395          }
    396          case "screen-settings-override": {
    397            switch (contextDescriptor.type) {
    398              case lazy.ContextDescriptorType.TopBrowsingContext: {
    399                screenSettingsOverridePerContext = value;
    400                break;
    401              }
    402              case lazy.ContextDescriptorType.UserContext: {
    403                screenSettingsOverridePerUserContext = value;
    404                break;
    405              }
    406            }
    407            break;
    408          }
    409          case "timezone-override": {
    410            switch (contextDescriptor.type) {
    411              case lazy.ContextDescriptorType.TopBrowsingContext: {
    412                timezoneOverridePerContext = value;
    413                break;
    414              }
    415              case lazy.ContextDescriptorType.UserContext: {
    416                timezoneOverridePerUserContext = value;
    417                break;
    418              }
    419            }
    420            break;
    421          }
    422          case "user-agent-override": {
    423            switch (contextDescriptor.type) {
    424              case lazy.ContextDescriptorType.TopBrowsingContext: {
    425                userAgentOverridePerContext = value;
    426                break;
    427              }
    428              case lazy.ContextDescriptorType.UserContext: {
    429                userAgentOverridePerUserContext = value;
    430                break;
    431              }
    432              case lazy.ContextDescriptorType.All: {
    433                userAgentOverrideGlobal = value;
    434              }
    435            }
    436            break;
    437          }
    438        }
    439      }
    440 
    441      // For the following emulations on the previous step, we found session items
    442      // that would apply an override for a browsing context,a user context, and in some cases globally.
    443      // Now from these items we have to choose the one that would take precedence.
    444      // The order is the user context item overrides the global one, and the browsing context overrides the user context item.
    445      switch (category) {
    446        case "geolocation-override": {
    447          this.#geolocationConfiguration = this.#findCorrectOverrideValue(
    448            "object",
    449            geolocationOverridePerContext,
    450            geolocationOverridePerUserContext
    451          );
    452          break;
    453        }
    454        case "locale-override": {
    455          this.#localeOverride = this.#findCorrectOverrideValue(
    456            "string",
    457            localeOverridePerContext,
    458            localeOverridePerUserContext
    459          );
    460          break;
    461        }
    462        case "screen-orientation-override": {
    463          this.#screenOrientationOverride = this.#findCorrectOverrideValue(
    464            "object",
    465            screenOrientationOverridePerContext,
    466            screenOrientationOverridePerUserContext
    467          );
    468 
    469          break;
    470        }
    471        case "screen-settings-override": {
    472          this.#screenSettingsOverride = this.#findCorrectOverrideValue(
    473            "object",
    474            screenSettingsOverridePerContext,
    475            screenSettingsOverridePerUserContext
    476          );
    477 
    478          break;
    479        }
    480        case "timezone-override": {
    481          this.#timezoneOverride = this.#findCorrectOverrideValue(
    482            "string",
    483            timezoneOverridePerContext,
    484            timezoneOverridePerUserContext
    485          );
    486 
    487          break;
    488        }
    489        case "user-agent-override": {
    490          this.#userAgentOverride = this.#findCorrectOverrideValue(
    491            "string",
    492            userAgentOverridePerContext,
    493            userAgentOverridePerUserContext,
    494            userAgentOverrideGlobal
    495          );
    496 
    497          break;
    498        }
    499      }
    500    }
    501  }
    502 }
    503 
    504 export const _configuration = _ConfigurationModule;