Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/browser/extensions/webcompat/lib/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 12 kB image not shown  

Quelle  interventions.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";

/* globals browser, InterventionHelpers, module */

class Interventions {
  constructor(availableInterventions, customFunctions) {
    this.INTERVENTION_PREF = "perform_injections";

    this._interventionsEnabled = true;

    this._availableInterventions = Object.entries(availableInterventions).map(
      ([id, obj]) => {
        obj.id = id;
        return obj;
      }
    );
    this._customFunctions = customFunctions;

    // We do not try to enable/disable until we finish any such previous operation.
    this._enablingOrDisablingOperationInProgress = Promise.resolve();

    this._activeListenersPerIntervention = new Map();
    this._contentScriptsPerIntervention = new Map();
  }

  bindAboutCompatBroker(broker) {
    this._aboutCompatBroker = broker;
  }

  bootup() {
    browser.testUtils.interventionsInactive();
    browser.aboutConfigPrefs.onPrefChange.addListener(() => {
      this.checkInterventionPref();
    }, this.INTERVENTION_PREF);
    this.checkInterventionPref();
  }

  checkInterventionPref() {
    browser.aboutConfigPrefs.getPref(this.INTERVENTION_PREF).then(value => {
      if (value === undefined) {
        browser.aboutConfigPrefs.setPref(this.INTERVENTION_PREF, true);
      } else if (value === false) {
        this.disableInterventions();
      } else {
        this.enableInterventions();
      }
    });
  }

  checkOverridePref() {
    browser.aboutConfigPrefs.getPref(this.OVERRIDE_PREF).then(value => {
      if (value === undefined) {
        browser.aboutConfigPrefs.setPref(this.OVERRIDE_PREF, true);
      } else if (value === false) {
        this.unregisterUAOverrides();
      } else {
        this.registerUAOverrides();
      }
    });
  }

  getAvailableInterventions() {
    return this._availableInterventions;
  }

  isEnabled() {
    return this._interventionsEnabled;
  }

  async enableInterventions() {
    await this._enablingOrDisablingOperationInProgress;

    this._enablingOrDisablingOperationInProgress = new Promise(done => {
      this._enableInterventionsNow();
      done();
    });

    return this._enablingOrDisablingOperationInProgress;
  }

  async disableInterventions() {
    await this._enablingOrDisablingOperationInProgress;

    this._enablingOrDisablingOperationInProgress = new Promise(done => {
      for (const config of this._availableInterventions) {
        this._disableInterventionNow(config);
      }

      this._interventionsEnabled = false;
      this._aboutCompatBroker.portsToAboutCompatTabs.broadcast({
        interventionsChanged: false,
      });

      browser.testUtils.interventionsInactive();
      done();
    });

    return this._enablingOrDisablingOperationInProgress;
  }

  async _enableInterventionsNow() {
    await this._enablingOrDisablingOperationInProgress;

    const skipped = [];

    const { version } = await browser.runtime.getBrowserInfo();
    const cleanVersion = parseFloat(version.match(/\d+(\.\d+)?/)[0]);

    const { os } = await browser.runtime.getPlatformInfo();
    this.currentPlatform = os;

    for (const config of this._availableInterventions) {
      for (const intervention of config.interventions) {
        if (await InterventionHelpers.shouldSkip(intervention, cleanVersion)) {
          continue;
        }
        if (!(await InterventionHelpers.checkPlatformMatches(intervention))) {
          continue;
        }
        intervention.enabled = true;
        config.availableOnPlatform = true;
      }

      if (!config.availableOnPlatform) {
        skipped.push(config.label);
        continue;
      }

      try {
        await this._enableInterventionNow(config);
      } catch (e) {
        console.error("Error enabling intervention(s) for", config.label, e);
      }
    }

    if (skipped.length) {
      console.warn(
        "Skipping",
        skipped.length,
        "un-needed interventions",
        skipped.sort()
      );
    }

    this._interventionsEnabled = true;
    this._aboutCompatBroker.portsToAboutCompatTabs.broadcast({
      interventionsChanged: this._aboutCompatBroker.filterInterventions(
        this._availableInterventions
      ),
    });

    await browser.testUtils.interventionsActive();
  }

  async enableIntervention(config) {
    await this._enablingOrDisablingOperationInProgress;

    this._enablingOrDisablingOperationInProgress = new Promise(done => {
      this._enableInterventionNow(config);
      done();
    });

    return this._enablingOrDisablingOperationInProgress;
  }

  async disableIntervention(config) {
    await this._enablingOrDisablingOperationInProgress;

    this._enablingOrDisablingOperationInProgress = new Promise(done => {
      this._disableInterventionNow(config);
      done();
    });

    return this._enablingOrDisablingOperationInProgress;
  }

  async _enableInterventionNow(config) {
    if (config.active) {
      return;
    }

    const { bugs, label } = config;
    const blocks = Object.values(bugs)
      .map(bug => bug.blocks)
      .flat()
      .filter(v => v !== undefined);
    const matches = Object.values(bugs)
      .map(bug => bug.matches)
      .flat()
      .filter(v => v !== undefined);

    for (const intervention of config.interventions) {
      await this._changeCustomFuncs("enable", label, intervention, config);
      await this._enableContentScripts(label, intervention, matches);
      await this._enableUAOverrides(label, intervention, matches);
      await this._enableRequestBlocks(label, intervention, blocks);
    }

    config.active = true;
  }

  async _disableInterventionNow(config) {
    const { active, label, interventions } = config;

    if (!active) {
      return;
    }

    for (const intervention of interventions) {
      await this._changeCustomFuncs("disable", label, intervention, config);
      await this._disableContentScripts(label, intervention);

      // This covers both request blocks and ua_string cases
      const listeners = this._activeListenersPerIntervention.get(intervention);
      if (listeners) {
        for (const [name, listener] of Object.entries(listeners)) {
          browser.webRequest[name].removeListener(listener);
        }
        this._activeListenersPerIntervention.delete(intervention);
      }
    }

    config.active = false;
  }

  async _changeCustomFuncs(action, label, intervention, config) {
    for (const [customFuncName, customFunc] of Object.entries(
      this._customFunctions
    )) {
      if (customFuncName in intervention) {
        try {
          await customFunc[action](config, intervention);
        } catch (e) {
          console.trace(
            `Error while calling custom function ${customFuncName}.${action} for ${label}:`,
            e
          );
        }
      }
    }
  }

  async _enableUAOverrides(label, intervention, matches) {
    if (!("ua_string" in intervention)) {
      return;
    }

    let listeners = this._activeListenersPerIntervention.get(intervention);
    if (!listeners) {
      listeners = {};
      this._activeListenersPerIntervention.set(intervention, listeners);
    }

    const listener = details => {
      const { enabled, ua_string } = intervention;

      // Don't actually override the UA for an experiment if the user is not
      // part of the experiment (unless they force-enabed the override).
      if (
        enabled &&
        (!intervention.experiment || intervention.permanentPrefEnabled === true)
      ) {
        for (const header of details.requestHeaders) {
          if (header.name.toLowerCase() !== "user-agent") {
            continue;
          }

          // Don't override the UA if we're on a mobile device that has the
          // "Request Desktop Site" mode enabled. The UA for the desktop mode
          // is set inside Gecko with a simple string replace, so we can use
          // that as a check, see https://searchfox.org/mozilla-central/rev/89d33e1c3b0a57a9377b4815c2f4b58d933b7c32/mobile/android/chrome/geckoview/GeckoViewSettingsChild.js#23-28
          let isMobileWithDesktopMode =
            this.currentPlatform == "android" &&
            header.value.includes("X11; Linux x86_64");
          if (isMobileWithDesktopMode) {
            continue;
          }

          header.value = InterventionHelpers.applyUAChanges(
            header.value,
            ua_string
          );
        }
      }
      return { requestHeaders: details.requestHeaders };
    };

    browser.webRequest.onBeforeSendHeaders.addListener(
      listener,
      { urls: matches },
      ["blocking""requestHeaders"]
    );

    listeners.onBeforeSendHeaders = listener;

    console.info(`Enabled UA override for ${label}`);
  }

  async _enableRequestBlocks(label, intervention, blocks) {
    if (!blocks.length) {
      return;
    }

    let listeners = this._activeListenersPerIntervention.get(intervention);
    if (!listeners) {
      listeners = {};
      this._activeListenersPerIntervention.set(intervention, listeners);
    }

    const listener = () => {
      return { cancel: true };
    };

    browser.webRequest.onBeforeRequest.addListener(listener, { urls: blocks }, [
      "blocking",
    ]);

    listeners.onBeforeRequest = listener;
    console.info(`Blocking requests as specified for ${label}`);
  }

  async _enableContentScripts(label, intervention, matches) {
    if (!("content_scripts" in intervention)) {
      return;
    }

    const scriptsToReg = this._buildContentScriptRegistrations(
      label,
      intervention,
      matches
    );
    this._contentScriptsPerIntervention.set(intervention, scriptsToReg);

    // Try to avoid re-registering scripts already registered
    // (e.g. if the webcompat background page is restarted
    // after an extension process crash, after having registered
    // the content scripts already once), but do not prevent
    // to try registering them again if the getRegisteredContentScripts
    // method returns an unexpected rejection.

    const ids = scriptsToReg.map(s => s.id);
    try {
      const alreadyRegged = await browser.scripting.getRegisteredContentScripts(
        { ids }
      );
      const alreadyReggedIds = alreadyRegged.map(script => script.id);
      const stillNeeded = scriptsToReg.filter(
        ({ id }) => !alreadyReggedIds.includes(id)
      );
      await browser.scripting.registerContentScripts(stillNeeded);
      console.info(
        `Registered still-not-active content scripts for ${label}`,
        stillNeeded
      );
    } catch (e) {
      try {
        await browser.scripting.registerContentScripts(scriptsToReg);
        console.debug(
          `Registered all content scripts for ${label} after error registering just non-active ones`,
          scriptsToReg,
          e
        );
      } catch (e2) {
        console.error(
          `Error while registering content scripts for ${label}:`,
          e2,
          scriptsToReg
        );
      }
    }
  }

  async _disableContentScripts(label, intervention) {
    const contentScripts =
      this._contentScriptsPerIntervention.get(intervention);
    if (contentScripts) {
      const ids = contentScripts.map(s => s.id);
      await browser.scripting.unregisterContentScripts({ ids });
    }
  }

  _buildContentScriptRegistrations(label, intervention, matches) {
    const registration = {
      id: `webcompat intervention for ${label}`,
      matches,
      persistAcrossSessions: false,
    };

    let { all_frames, css, js, run_at } = intervention.content_scripts;
    if (!css && !js) {
      console.error(`Missing js or css for content_script in ${label}`);
      return [];
    }
    if (all_frames) {
      registration.allFrames = true;
    }
    if (css) {
      registration.css = css.map(item => {
        if (item.includes("/")) {
          return item;
        }
        return `injections/css/${item}`;
      });
    }
    if (js) {
      registration.js = js.map(item => {
        if (item.includes("/")) {
          return item;
        }
        return `injections/js/${item}`;
      });
    }
    if (run_at) {
      registration.runAt = run_at;
    } else {
      registration.runAt = "document_start";
    }

    return [registration];
  }
}

module.exports = Interventions;

Messung V0.5
C=90 H=93 G=91

¤ Dauer der Verarbeitung: 0.15 Sekunden  (vorverarbeitet)  ¤

*© 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.