Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/browser/extensions/ipp-activator/extension/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 12 kB image not shown  

Quelle  bg.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/. */


/* global browser, ConditionFactory */

/**
 * The main class for the IPP activator add-on.
 */

class IPPAddonActivator {
  #initialized = false;

  #tabBaseBreakages;
  #webrequestBaseBreakages;

  #tabBreakages;
  #webrequestBreakages;

  #pendingTabs = new Set(); // pending due to tab URL change while inactive
  #pendingWebRequests = new Map(); // tabId -> Set of pending request URLs
  // tabId -> { domain, l10nId, condition, shown } for every tab that currently
  // has a matching breakage. The entry is kept alive (and the condition keeps
  // listening) even when the condition is false, so we re-show the
  // notification if the condition becomes true again.
  #stateByTab = new Map();

  constructor() {
    this.tabUpdated = this.#tabUpdated.bind(this);
    this.tabActivated = this.#tabActivated.bind(this);
    this.tabRemoved = this.#tabRemoved.bind(this);
    this.onRequest = this.#onRequest.bind(this);
    this.ippExceptionsChanged = this.#ippExceptionsChanged.bind(this);

    this.#loadAndRebuildBreakages().then(() => {
      browser.ippActivator.onDynamicTabBreakagesUpdated.addListener(() =>
        this.#loadAndRebuildBreakages()
      );
      browser.ippActivator.onDynamicWebRequestBreakagesUpdated.addListener(() =>
        this.#loadAndRebuildBreakages()
      );

      this.#init();
    });
  }

  async #init() {
    if (this.#initialized) {
      return;
    }

    // Register only the listeners that are needed for existing breakages.
    this.#registerListeners();

    this.#initialized = true;
  }

  async #fetchBaseBreakages(url) {
    try {
      const res = await fetch(url);
      const base = await res.json();
      return Array.isArray(base) ? base : [];
    } catch (_) {
      return [];
    }
  }

  async #loadAndRebuildBreakages() {
    if (!this.#tabBaseBreakages) {
      const customUrl = await browser.ippActivator.getTabBreakagesUrl();
      this.#tabBaseBreakages = await this.#fetchBaseBreakages(
        browser.runtime.getURL(customUrl || "breakages/tab.json")
      );
    }

    if (!this.#webrequestBaseBreakages) {
      const customUrl = await browser.ippActivator.getWebRequestBreakagesUrl();
      this.#webrequestBaseBreakages = await this.#fetchBaseBreakages(
        browser.runtime.getURL(customUrl || "breakages/webrequest.json")
      );
    }

    let dynamicTab = [];
    try {
      const dynT = await browser.ippActivator.getDynamicTabBreakages();
      dynamicTab = Array.isArray(dynT) ? dynT : [];
    } catch (_) {
      console.warn("Unable to retrieve dynamicTabBreakages");
    }

    let dynamicWr = [];
    try {
      const dynW = await browser.ippActivator.getDynamicWebRequestBreakages();
      dynamicWr = Array.isArray(dynW) ? dynW : [];
    } catch (_) {
      console.warn("Unable to retrieve dynamicWebRequestBreakages");
    }

    this.#tabBreakages = [...(this.#tabBaseBreakages || []), ...dynamicTab];
    this.#webrequestBreakages = [
      ...(this.#webrequestBaseBreakages || []),
      ...dynamicWr,
    ];

    // Adjust listeners if we've already initialized.
    if (this.#initialized) {
      this.#registerListeners();
    }
  }

  #registerListeners() {
    this.#unregisterListeners();

    const needTabUpdated =
      Array.isArray(this.#tabBreakages) && !!this.#tabBreakages.length;
    const needWebRequest =
      Array.isArray(this.#webrequestBreakages) &&
      !!this.#webrequestBreakages.length;
    const needActivation = needTabUpdated || needWebRequest;

    // tabs.onUpdated (only if there are tab breakages)
    if (needTabUpdated) {
      browser.tabs.onUpdated.addListener(this.tabUpdated, {
        properties: ["url""status"],
      });
    }

    // webRequest.onBeforeRequest (only if there are webRequest breakages)
    if (needWebRequest) {
      browser.webRequest.onBeforeRequest.addListener(
        this.onRequest,
        {
          urls: ["<all_urls>"],
          types: ["media""sub_frame""xmlhttprequest"],
        },
        []
      );
    }

    // tabs.onActivated and tabs.onRemoved are needed when either above is needed
    if (needActivation) {
      browser.tabs.onActivated.addListener(this.tabActivated);
      browser.tabs.onRemoved.addListener(this.tabRemoved);
    }

    browser.ippActivator.onIPPExceptionsChanged.addListener(
      this.ippExceptionsChanged
    );
  }

  #unregisterListeners() {
    if (browser.tabs.onUpdated.hasListener(this.tabUpdated)) {
      browser.tabs.onUpdated.removeListener(this.tabUpdated);
    }

    if (browser.tabs.onActivated.hasListener(this.tabActivated)) {
      browser.tabs.onActivated.removeListener(this.tabActivated);
    }

    if (browser.tabs.onRemoved.hasListener(this.tabRemoved)) {
      browser.tabs.onRemoved.removeListener(this.tabRemoved);
    }

    if (browser.webRequest.onBeforeRequest.hasListener(this.onRequest)) {
      browser.webRequest.onBeforeRequest.removeListener(this.onRequest);
    }

    browser.ippActivator.onIPPExceptionsChanged.removeListener(
      this.ippExceptionsChanged
    );

    this.#pendingTabs.clear();
    this.#pendingWebRequests.clear();
  }

  async #ippExceptionsChanged() {
    if (!this.#stateByTab.size) {
      return;
    }

    const tabIds = Array.from(this.#stateByTab.keys());
    await Promise.allSettled(
      tabIds.map(async tabId => {
        try {
          const tab = await browser.tabs.get(tabId);
          if (!tab?.url) {
            return;
          }

          if (await browser.ippActivator.hasExclusion(tab.url)) {
            await this.#dropTabState(tabId);
          }
        } catch (_) {}
      })
    );
  }

  async #tabUpdated(tabId, changeInfo, tab) {
    // React only to URL changes and to load completion; avoid showing during 'loading'
    if (!("url" in changeInfo) && changeInfo.status !== "complete") {
      return;
    }

    // If the tab URL changed, reset any pending web requests for this tab
    if ("url" in changeInfo) {
      try {
        // If we had a notification for a different base domain, hide it
        const info = await browser.ippActivator.getBaseDomainFromURL(
          changeInfo.url || tab?.url || ""
        );
        const entry = this.#stateByTab.get(tabId);
        if (
          entry &&
          entry.domain !== info.baseDomain &&
          entry.domain !== info.host
        ) {
          await this.#dropTabState(tabId);
        }
      } catch (_) {
        // ignore lookup issues
      }
      this.#pendingWebRequests.delete(tabId);
    }

    // If we haven't reached load completion yet, wait for later events
    if (changeInfo.status && changeInfo.status !== "complete") {
      if (!tab.active) {
        this.#pendingTabs.add(tabId);
      }
      return;
    }

    // At this point, either the URL changed and load already completed, or
    // we received the 'complete' status: handle only if tab is active
    if (!tab.active) {
      this.#pendingTabs.add(tabId);
      return;
    }

    await this.#maybeNotify(tab, this.#tabBreakages, tab.url);
  }

  async #tabActivated(activeInfo) {
    const { tabId } = activeInfo || {};

    const hadTabPending = this.#pendingTabs.has(tabId);
    const wrSet = this.#pendingWebRequests.get(tabId);
    const pendingWrUrls = wrSet ? Array.from(wrSet) : [];
    if (!hadTabPending && pendingWrUrls.length === 0) {
      return;
    }

    this.#pendingTabs.delete(tabId);
    this.#pendingWebRequests.delete(tabId);

    let tab;
    try {
      tab = await browser.tabs.get(tabId);
      if (!tab || !tab.active) {
        return;
      }
    } catch (_) {
      // Tab might have been closed; ignore.
      return;
    }

    if (
      hadTabPending &&
      (await this.#maybeNotify(tab, this.#tabBreakages, tab.url))
    ) {
      return;
    }

    for (const url of pendingWrUrls) {
      if (await this.#maybeNotify(tab, this.#webrequestBreakages, url)) {
        return;
      }
    }
  }

  async #maybeNotify(tab, breakages, url) {
    const info = await browser.ippActivator.getBaseDomainFromURL(url);
    if (!info.baseDomain && !info.host) {
      await this.#dropTabState(tab.id);
      return false;
    }

    let domain = info.baseDomain;
    let breakage = breakages.find(
      b => Array.isArray(b.domains) && b.domains.includes(info.baseDomain)
    );
    if (!breakage) {
      breakage = breakages.find(
        b => Array.isArray(b.domains) && b.domains.includes(info.host)
      );
      if (!breakage) {
        await this.#dropTabState(tab.id);
        return false;
      }

      domain = info.host;
    }

    if (await browser.ippActivator.hasExclusion(url)) {
      await this.#dropTabState(tab.id);
      return false;
    }

    // Do not show the same notification again for the same base domain.
    const notified = await browser.ippActivator.getNotifiedDomains();
    if (Array.isArray(notified) && notified.includes(domain)) {
      await this.#dropTabState(tab.id);
      return false;
    }

    // If we already track this tab but for a different domain, reset the state.
    let entry = this.#stateByTab.get(tab.id);
    if (entry && entry.domain !== domain) {
      await this.#dropTabState(tab.id);
      entry = null;
    }

    if (!entry) {
      let condition = null;
      if (breakage.condition !== undefined) {
        const factory = new ConditionFactory({ tabId: tab.id });
        condition = factory.create(breakage.condition);
        await condition.init();
      }

      entry = { domain, l10nId: breakage.l10nId, condition, shown: false };
      this.#stateByTab.set(tab.id, entry);

      if (condition) {
        condition.onChange(() => {
          if (this.#stateByTab.get(tab.id) !== entry) {
            return;
          }
          this.#updateNotification(tab.id);
        });
      }
    }

    return this.#updateNotification(tab.id);
  }

  #updateNotification(tabId) {
    const entry = this.#stateByTab.get(tabId);
    if (!entry) {
      return false;
    }

    const matches = !entry.condition || entry.condition.check();

    if (!matches) {
      if (entry.shown) {
        entry.shown = false;
        browser.ippActivator.hideMessage(tabId);
      }
      return false;
    }

    if (entry.shown) {
      return true;
    }

    entry.shown = true;

    // This function returns when the notification is dismissed. We don't want
    // to wait for that to happen.
    browser.ippActivator
      .showMessage({ l10nId: entry.l10nId }, tabId)
      .then(async dismissed => {
        if (!dismissed) {
          return;
        }

        await browser.ippActivator.addNotifiedDomain(entry.domain);

        // Close all notifications currently shown for the same base domain
        // across all tabs and clean up tracking state.
        const toClose = [];
        for (const [tid, e] of this.#stateByTab.entries()) {
          if (e.domain === entry.domain) {
            toClose.push(tid);
          }
        }

        await Promise.allSettled(toClose.map(id => this.#dropTabState(id)));
      });

    return true;
  }

  async #dropTabState(tabId) {
    const entry = this.#stateByTab.get(tabId);
    if (!entry) {
      return;
    }
    if (entry.condition) {
      entry.condition.uninit();
    }
    if (entry.shown) {
      await browser.ippActivator.hideMessage(tabId);
    }
    this.#stateByTab.delete(tabId);
  }

  async #onRequest(details) {
    if (
      typeof details.tabId !== "number" ||
      details.tabId < 0 ||
      !details.url
    ) {
      return;
    }

    try {
      const tab = await browser.tabs.get(details.tabId);
      if (!tab) {
        return;
      }

      if (tab.active) {
        await this.#maybeNotify(tab, this.#webrequestBreakages, details.url);
      } else {
        const set = this.#pendingWebRequests.get(details.tabId) || new Set();
        set.add(details.url);

        this.#pendingWebRequests.set(details.tabId, set);
      }
    } catch (_) {
      // tab may not exist
    }
  }

  async #tabRemoved(tabId, _removeInfo) {
    // Clean up any pending state associated with the closed tab
    this.#pendingTabs.delete(tabId);
    this.#pendingWebRequests.delete(tabId);
    const entry = this.#stateByTab.get(tabId);
    if (entry?.condition) {
      entry.condition.uninit();
    }
    this.#stateByTab.delete(tabId);
  }
}

/* This object is kept alive by listeners */
new IPPAddonActivator();

Messung V0.5 in Prozent
C=78 H=90 G=83

¤ Dauer der Verarbeitung: 0.6 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

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.