tor-browser

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

ext-ipp.js (10252B)


      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 /* global ExtensionAPI, ExtensionCommon, Cr */
      6 
      7 const lazy = {};
      8 ChromeUtils.defineESModuleGetters(lazy, {
      9  ExtensionParent: "resource://gre/modules/ExtensionParent.sys.mjs",
     10  IPPExceptionsManager:
     11    "moz-src:///browser/components/ipprotection/IPPExceptionsManager.sys.mjs",
     12  IPPProxyManager:
     13    "moz-src:///browser/components/ipprotection/IPPProxyManager.sys.mjs",
     14  IPPProxyStates:
     15    "moz-src:///browser/components/ipprotection/IPPProxyManager.sys.mjs",
     16 });
     17 
     18 ChromeUtils.defineLazyGetter(lazy, "tabTracker", () => {
     19  return lazy.ExtensionParent.apiManager.global.tabTracker;
     20 });
     21 
     22 const PREF_DYNAMIC_TAB_BREAKAGES =
     23  "extensions.ippactivator.dynamicTabBreakages";
     24 const PREF_DYNAMIC_WEBREQUEST_BREAKAGES =
     25  "extensions.ippactivator.dynamicWebRequestBreakages";
     26 const PREF_NOTIFIED_DOMAINS = "extensions.ippactivator.notifiedDomains";
     27 
     28 this.ippActivator = class extends ExtensionAPI {
     29  onStartup() {}
     30 
     31  onShutdown(_isAppShutdown) {}
     32 
     33  getAPI(context) {
     34    return {
     35      ippActivator: {
     36        onIPPActivated: new ExtensionCommon.EventManager({
     37          context,
     38          name: "ippActivator.onIPPActivated",
     39          register: fire => {
     40            const topics = ["IPPProxyManager:StateChanged"];
     41            const observer = _event => {
     42              fire.async();
     43            };
     44 
     45            topics.forEach(topic =>
     46              lazy.IPPProxyManager.addEventListener(topic, observer)
     47            );
     48 
     49            return () => {
     50              topics.forEach(topic =>
     51                lazy.IPPProxyManager.removeEventListener(topic, observer)
     52              );
     53            };
     54          },
     55        }).api(),
     56        isTesting() {
     57          return Services.prefs.getBoolPref(
     58            "extensions.ippactivator.testMode",
     59            false
     60          );
     61        },
     62        hideMessage(tabId) {
     63          try {
     64            const tab = tabId
     65              ? lazy.tabTracker.getTab(tabId)
     66              : lazy.tabTracker.activeTab;
     67            const browser = tab?.linkedBrowser;
     68            const win = browser?.ownerGlobal;
     69            if (!browser || !win || !win.gBrowser) {
     70              return;
     71            }
     72 
     73            const nbox = win.gBrowser.getNotificationBox(browser);
     74            const id = "ipp-activator-notification";
     75            const existing = nbox.getNotificationWithValue?.(id);
     76            if (existing) {
     77              nbox.removeNotification(existing);
     78            }
     79          } catch (e) {
     80            console.warn("Unable to hide the message", e);
     81          }
     82        },
     83        isIPPActive() {
     84          return lazy.IPPProxyManager.state === lazy.IPPProxyStates.ACTIVE;
     85        },
     86        getDynamicTabBreakages() {
     87          try {
     88            const json = Services.prefs.getStringPref(
     89              PREF_DYNAMIC_TAB_BREAKAGES,
     90              "[]"
     91            );
     92            const arr = JSON.parse(json);
     93            return Array.isArray(arr) ? arr : [];
     94          } catch (_) {
     95            return [];
     96          }
     97        },
     98        getDynamicWebRequestBreakages() {
     99          try {
    100            const json = Services.prefs.getStringPref(
    101              PREF_DYNAMIC_WEBREQUEST_BREAKAGES,
    102              "[]"
    103            );
    104            const arr = JSON.parse(json);
    105            return Array.isArray(arr) ? arr : [];
    106          } catch (_) {
    107            return [];
    108          }
    109        },
    110        getNotifiedDomains() {
    111          try {
    112            const json = Services.prefs.getStringPref(
    113              PREF_NOTIFIED_DOMAINS,
    114              "[]"
    115            );
    116            const arr = JSON.parse(json);
    117            return Array.isArray(arr) ? arr : [];
    118          } catch (_) {
    119            return [];
    120          }
    121        },
    122        addNotifiedDomain(domain) {
    123          const d = String(domain || "");
    124          if (!d) {
    125            return;
    126          }
    127          let arr = [];
    128          try {
    129            const json = Services.prefs.getStringPref(
    130              PREF_NOTIFIED_DOMAINS,
    131              "[]"
    132            );
    133            arr = JSON.parse(json);
    134            if (!Array.isArray(arr)) {
    135              arr = [];
    136            }
    137          } catch (_) {
    138            arr = [];
    139          }
    140          if (!arr.includes(d)) {
    141            arr.push(d);
    142            Services.prefs.setStringPref(
    143              PREF_NOTIFIED_DOMAINS,
    144              JSON.stringify(arr)
    145            );
    146          }
    147        },
    148        getBaseDomainFromURL(url) {
    149          try {
    150            const host = Services.io.newURI(url).host;
    151            if (!host) {
    152              return { baseDomain: "", host: "" };
    153            }
    154            let baseDomain = "";
    155            try {
    156              baseDomain = Services.eTLD.getBaseDomainFromHost(host);
    157            } catch (e) {
    158              if (e.result === Cr.NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
    159                baseDomain = host;
    160              } else {
    161                baseDomain = "";
    162              }
    163            }
    164            return { baseDomain, host };
    165          } catch (_) {
    166            return { baseDomain: "", host: "" };
    167          }
    168        },
    169        hasExclusion(url) {
    170          if (
    171            !Services.prefs.getBoolPref(
    172              "browser.ipProtection.features.siteExceptions",
    173              false
    174            )
    175          ) {
    176            return false;
    177          }
    178 
    179          try {
    180            const uri = Services.io.newURI(url);
    181            const principal =
    182              Services.scriptSecurityManager.createContentPrincipal(uri, {});
    183            return lazy.IPPExceptionsManager.hasExclusion(principal);
    184          } catch (e) {
    185            return false;
    186          }
    187        },
    188        async showMessage(message, tabId) {
    189          try {
    190            // Choose the target tab (by id if provided, else active tab)
    191            const tab = tabId
    192              ? lazy.tabTracker.getTab(tabId)
    193              : lazy.tabTracker.activeTab;
    194            const browser = tab?.linkedBrowser;
    195            const win = browser?.ownerGlobal;
    196            if (!browser || !win || !win.gBrowser) {
    197              return Promise.resolve(false);
    198            }
    199 
    200            const nbox = win.gBrowser.getNotificationBox(browser);
    201            const id = "ipp-activator-notification";
    202 
    203            const existing = nbox.getNotificationWithValue?.(id);
    204            if (existing) {
    205              nbox.removeNotification(existing);
    206            }
    207 
    208            const buildLabel = msg => {
    209              // Accept either string or array of parts {text, modifier}
    210              if (Array.isArray(msg)) {
    211                const frag = win.document.createDocumentFragment();
    212                for (const part of msg) {
    213                  const text = String(part?.text ?? "");
    214                  const mods = Array.isArray(part?.modifier)
    215                    ? part.modifier
    216                    : [];
    217                  if (mods.includes("strong")) {
    218                    const strong = win.document.createElement("strong");
    219                    strong.textContent = text;
    220                    frag.append(strong);
    221                  } else {
    222                    frag.append(win.document.createTextNode(text));
    223                  }
    224                }
    225                return frag;
    226              }
    227              return String(msg ?? "");
    228            };
    229 
    230            const label = buildLabel(message);
    231 
    232            // Promise that resolves when the notification is dismissed
    233            let resolveDismiss;
    234            const dismissedPromise = new Promise(resolve => {
    235              resolveDismiss = resolve;
    236            });
    237 
    238            // Create the notification; set persistence when available
    239            nbox
    240              .appendNotification(
    241                id,
    242                {
    243                  // If label is a string, pass it through; if it's a Node, the
    244                  // notification box will handle it as rich content.
    245                  label,
    246                  priority: nbox.PRIORITY_WARNING_HIGH,
    247                  eventCallback: param => {
    248                    resolveDismiss(param === "dismissed");
    249                  },
    250                },
    251                []
    252              )
    253              .then(notification => {
    254                // Persist the notification until the user removes so it
    255                // doesn't get removed on redirects.
    256                notification.persistence = -1;
    257              });
    258 
    259            return dismissedPromise;
    260          } catch (e) {
    261            console.warn("Unable to show the message", e);
    262            return Promise.resolve(false);
    263          }
    264        },
    265        onDynamicTabBreakagesUpdated: new ExtensionCommon.EventManager({
    266          context,
    267          name: "ippActivator.onDynamicTabBreakagesUpdated",
    268          register: fire => {
    269            const observer = {
    270              observe(subject, topic, data) {
    271                if (
    272                  topic === "nsPref:changed" &&
    273                  data === PREF_DYNAMIC_TAB_BREAKAGES
    274                ) {
    275                  fire.async();
    276                }
    277              },
    278            };
    279            Services.prefs.addObserver(PREF_DYNAMIC_TAB_BREAKAGES, observer);
    280            return () =>
    281              Services.prefs.removeObserver(
    282                PREF_DYNAMIC_TAB_BREAKAGES,
    283                observer
    284              );
    285          },
    286        }).api(),
    287        onDynamicWebRequestBreakagesUpdated: new ExtensionCommon.EventManager({
    288          context,
    289          name: "ippActivator.onDynamicWebRequestBreakagesUpdated",
    290          register: fire => {
    291            const observer = {
    292              observe(subject, topic, data) {
    293                if (
    294                  topic === "nsPref:changed" &&
    295                  data === PREF_DYNAMIC_WEBREQUEST_BREAKAGES
    296                ) {
    297                  fire.async();
    298                }
    299              },
    300            };
    301            Services.prefs.addObserver(
    302              PREF_DYNAMIC_WEBREQUEST_BREAKAGES,
    303              observer
    304            );
    305            return () =>
    306              Services.prefs.removeObserver(
    307                PREF_DYNAMIC_WEBREQUEST_BREAKAGES,
    308                observer
    309              );
    310          },
    311        }).api(),
    312      },
    313    };
    314  }
    315 };