tor-browser

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

BrowsingContextUtils.sys.mjs (7283B)


      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 const lazy = {};
      6 
      7 ChromeUtils.defineESModuleGetters(lazy, {
      8  error: "chrome://remote/content/shared/messagehandler/Errors.sys.mjs",
      9  PollPromise: "chrome://remote/content/shared/Sync.sys.mjs",
     10 });
     11 
     12 /**
     13 * @typedef {object} BrowsingContextDetails
     14 *
     15 * @property {number} browserId
     16 *     Browser id for this browsing context.
     17 * @property {number} browsingContextId
     18 *     Internal id of the browsing context.
     19 * @property {boolean} isTopBrowsingContext
     20 *     Flag that indicates if the browsing context is top-level.
     21 * @property {boolean} isContent
     22 *     Flag that indicates if it is a content or a chrome browsing context.
     23 */
     24 
     25 /**
     26 * A browsing context might be replaced before reaching the parent process,
     27 * instead we serialize enough information to retrieve the navigable in the
     28 * parent process.
     29 *
     30 * If the browsing context is top level, then the browserId can be used to
     31 * find the browser element and the new browsing context.
     32 * Otherwise (frames) the browsing context should not be replaced and the
     33 * browsing context id should be enough to find the browsing context.
     34 *
     35 * Should be used when preparing an event payload from the content to the
     36 * parent process.
     37 *
     38 * @param {BrowsingContext} browsingContext
     39 *     The browsing context for which we want to get details.
     40 *
     41 * @returns {BrowsingContextDetails}
     42 *     Details of the browsing context.
     43 */
     44 export function getBrowsingContextDetails(browsingContext) {
     45  return {
     46    browserId: browsingContext.browserId,
     47    browsingContextId: browsingContext.id,
     48    isContent: browsingContext.isContent,
     49    isTopBrowsingContext: browsingContext.parent === null,
     50  };
     51 }
     52 
     53 function isExtensionContext(browsingContext) {
     54  let principal;
     55  try {
     56    if (CanonicalBrowsingContext.isInstance(browsingContext)) {
     57      principal = browsingContext.currentWindowGlobal.documentPrincipal;
     58    } else {
     59      principal = browsingContext.window.document.nodePrincipal;
     60    }
     61  } catch (e) {
     62    throw new Error(
     63      `Could not retrieve principal for browsingContext (${e.message})`
     64    );
     65  }
     66 
     67  // In practice, note that the principal will never be an expanded principal.
     68  // The are only used for content scripts executed in a Sandbox, and do not
     69  // have a browsing context on their own.
     70  // But we still use this flag because there is no isAddonPrincipal flag.
     71  return principal.isAddonOrExpandedAddonPrincipal;
     72 }
     73 
     74 function isParentProcess(browsingContext) {
     75  if (CanonicalBrowsingContext.isInstance(browsingContext)) {
     76    return browsingContext.currentWindowGlobal.osPid === -1;
     77  }
     78 
     79  // If `browsingContext` is not a `CanonicalBrowsingContext`, then we are
     80  // necessarily in a content process page.
     81  return false;
     82 }
     83 
     84 /**
     85 * Check if the provided browsing context is currently displaying its initial
     86 * document. For top level browsing contexts, this is usually the initial
     87 * about:blank.
     88 *
     89 * @param {BrowsingContext} browsingContext
     90 *     The browsing context to check.
     91 *
     92 * @returns {boolean}
     93 *     True if the browsing context is on the initial document, false otherwise.
     94 */
     95 export function isInitialDocument(browsingContext) {
     96  if (!browsingContext.currentWindowGlobal) {
     97    // Right after a browsing context has been attached it could happen that
     98    // no window global has been set yet. Consider this as nothing has been
     99    // loaded yet.
    100    return true;
    101  }
    102 
    103  return browsingContext.currentWindowGlobal.isInitialDocument;
    104 }
    105 
    106 /**
    107 * Check if the provided browsing context is currently displaying its initial
    108 * document. For top level browsing contexts, this is usually the initial
    109 * about:blank which will be replaced soon.
    110 *
    111 * @param {BrowsingContext} browsingContext
    112 *     The browsing context to check.
    113 *
    114 * @returns {boolean}
    115 *     True if the browsing context is on the initial document, false otherwise.
    116 */
    117 export function isUncommittedInitialDocument(browsingContext) {
    118  if (!browsingContext.currentWindowGlobal) {
    119    // Right after a browsing context has been attached it could happen that
    120    // no window global has been set yet. Consider this as nothing has been
    121    // loaded yet.
    122    return true;
    123  }
    124 
    125  return browsingContext.currentWindowGlobal.isUncommittedInitialDocument;
    126 }
    127 
    128 /**
    129 * Check if the given browsing context is valid for the message handler
    130 * to use.
    131 *
    132 * @param {BrowsingContext} browsingContext
    133 *     The browsing context to check.
    134 * @param {object=} options
    135 * @param {string=} options.browserId
    136 *    The id of the browser to filter the browsing contexts by (optional).
    137 * @param {string=} options.userContext
    138 *    The id of the user context to filter the browsing contexts by (optional).
    139 *
    140 * @returns {boolean}
    141 *     True if the browsing context is valid, false otherwise.
    142 */
    143 export function isBrowsingContextCompatible(browsingContext, options = {}) {
    144  const { browserId, userContext } = options;
    145 
    146  if (!BrowsingContext.isInstance(browsingContext)) {
    147    return false;
    148  }
    149 
    150  // If a browserId was provided, skip browsing contexts which are not
    151  // associated with this browserId.
    152  if (browserId !== undefined && browsingContext.browserId !== browserId) {
    153    return false;
    154  }
    155 
    156  // If a userContext was provided, skip browsing contexts which are not
    157  // associated with this userContext.
    158  if (
    159    userContext !== undefined &&
    160    browsingContext.originAttributes.userContextId !== userContext
    161  ) {
    162    return false;
    163  }
    164 
    165  // If this is a CanonicalBrowsingContext but the currentWindowGlobal is not
    166  // attached yet, skip it.
    167  if (
    168    CanonicalBrowsingContext.isInstance(browsingContext) &&
    169    !browsingContext.currentWindowGlobal
    170  ) {
    171    return false;
    172  }
    173 
    174  // Skip:
    175  // - extension contexts until we support debugging webextensions, see Bug 1755014.
    176  // - privileged contexts until we support debugging Chrome context, see Bug 1713440.
    177  return (
    178    !isExtensionContext(browsingContext) && !isParentProcess(browsingContext)
    179  );
    180 }
    181 
    182 /**
    183 * Wait until `currentWindowGlobal` is available on a browsing context. When a
    184 * browsing context has just been created, the `currentWindowGlobal` might not
    185 * be attached yet.
    186 *
    187 * @param {CanonicalBrowsingContext} browsingContext
    188 *     The browsing context to wait for.
    189 *
    190 * @returns {Promise}
    191 *     Promise which resolves when `currentWindowGlobal` is available.
    192 *
    193 * @throws DiscardedBrowsingContextError
    194 *     Browsing context is discarded or still no
    195 *     `currentWindowGlobal` set after 100ms.
    196 */
    197 export async function waitForCurrentWindowGlobal(browsingContext) {
    198  await lazy.PollPromise(
    199    (resolve, reject) => {
    200      if (browsingContext.currentWindowGlobal || browsingContext.isDiscarded) {
    201        // If the browsing context is discarded while checking for
    202        // the current window global, return early to avoid waiting
    203        // unnecessarily until the timeout expires.
    204        resolve();
    205      } else {
    206        reject();
    207      }
    208    },
    209    {
    210      timeout: 100,
    211    }
    212  );
    213 
    214  if (!browsingContext.currentWindowGlobal) {
    215    throw new lazy.error.DiscardedBrowsingContextError(
    216      `BrowsingContext does no longer exist`
    217    );
    218  }
    219 }