tor-browser

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

LinkHandlerChild.sys.mjs (4735B)


      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  FaviconLoader: "resource:///modules/FaviconLoader.sys.mjs",
      9 });
     10 
     11 export class LinkHandlerChild extends JSWindowActorChild {
     12  constructor() {
     13    super();
     14 
     15    this.seenTabIcon = false;
     16    this._iconLoader = null;
     17  }
     18 
     19  get iconLoader() {
     20    if (!this._iconLoader) {
     21      this._iconLoader = new lazy.FaviconLoader(this);
     22    }
     23    return this._iconLoader;
     24  }
     25 
     26  addRootIcon() {
     27    if (
     28      !this.seenTabIcon &&
     29      Services.prefs.getBoolPref("browser.chrome.guess_favicon", true) &&
     30      Services.prefs.getBoolPref("browser.chrome.site_icons", true)
     31    ) {
     32      // Inject the default icon. Use documentURIObject so that we do the right
     33      // thing with about:-style error pages. See bug 453442
     34      let pageURI = this.document.documentURIObject;
     35      if (["http", "https"].includes(pageURI.scheme)) {
     36        this.seenTabIcon = true;
     37        this.iconLoader.addDefaultIcon(pageURI);
     38      }
     39    }
     40  }
     41 
     42  onHeadParsed(event) {
     43    if (event.target.ownerDocument != this.document) {
     44      return;
     45    }
     46 
     47    // Per spec icons are meant to be in the <head> tag so we should have seen
     48    // all the icons now so add the root icon if no other tab icons have been
     49    // seen.
     50    this.addRootIcon();
     51 
     52    // We're likely done with icon parsing so load the pending icons now.
     53    if (this._iconLoader) {
     54      this._iconLoader.onPageShow();
     55    }
     56  }
     57 
     58  onPageShow(event) {
     59    if (event.target != this.document) {
     60      return;
     61    }
     62 
     63    this.addRootIcon();
     64 
     65    if (this._iconLoader) {
     66      this._iconLoader.onPageShow();
     67    }
     68  }
     69 
     70  onPageHide(event) {
     71    if (event.target != this.document) {
     72      return;
     73    }
     74 
     75    if (this._iconLoader) {
     76      this._iconLoader.onPageHide();
     77    }
     78 
     79    this.seenTabIcon = false;
     80  }
     81 
     82  onLinkEvent(event) {
     83    let link = event.target;
     84    // Ignore sub-frames (bugs 305472, 479408).
     85    if (link.ownerGlobal != this.contentWindow) {
     86      return;
     87    }
     88 
     89    let rel = link.rel && link.rel.toLowerCase();
     90    // We also check .getAttribute, since an empty href attribute will give us
     91    // a link.href that is the same as the document.
     92    if (!rel || !link.href || !link.getAttribute("href")) {
     93      return;
     94    }
     95 
     96    // Note: following booleans only work for the current link, not for the
     97    // whole content
     98    let iconAdded = false;
     99    let searchAdded = false;
    100    let rels = {};
    101    for (let relString of rel.split(/\s+/)) {
    102      rels[relString] = true;
    103    }
    104 
    105    for (let relVal in rels) {
    106      let isRichIcon = false;
    107 
    108      switch (relVal) {
    109        case "apple-touch-icon":
    110        case "apple-touch-icon-precomposed":
    111        case "fluid-icon":
    112          isRichIcon = true;
    113        // fall through
    114        case "icon":
    115          if (iconAdded || link.hasAttribute("color") || rel.includes("mask")) {
    116            // TODO (Bug 1337397): Add support for mask-icon favicons.
    117            break;
    118          }
    119 
    120          if (!Services.prefs.getBoolPref("browser.chrome.site_icons", true)) {
    121            return;
    122          }
    123 
    124          if (this.iconLoader.addIconFromLink(link, isRichIcon)) {
    125            iconAdded = true;
    126            if (!isRichIcon) {
    127              this.seenTabIcon = true;
    128            }
    129          }
    130          break;
    131        case "search":
    132          if (
    133            Services.policies &&
    134            !Services.policies.isAllowed("installSearchEngine")
    135          ) {
    136            break;
    137          }
    138 
    139          if (!searchAdded && event.type == "DOMLinkAdded") {
    140            let type = link.type && link.type.toLowerCase();
    141            type = type.replace(/^\s+|\s*(?:;.*)?$/g, "");
    142 
    143            // Note: This protocol list should be kept in sync with
    144            // the one in OpenSearchEngine's install function.
    145            let re = /^https?:/i;
    146            if (
    147              type == "application/opensearchdescription+xml" &&
    148              link.title &&
    149              re.test(link.href)
    150            ) {
    151              let engine = { title: link.title, href: link.href };
    152              this.sendAsyncMessage("Link:AddSearch", {
    153                engine,
    154              });
    155              searchAdded = true;
    156            }
    157          }
    158          break;
    159      }
    160    }
    161  }
    162 
    163  handleEvent(event) {
    164    switch (event.type) {
    165      case "pageshow":
    166        return this.onPageShow(event);
    167      case "pagehide":
    168        return this.onPageHide(event);
    169      case "DOMHeadElementParsed":
    170        return this.onHeadParsed(event);
    171      default:
    172        return this.onLinkEvent(event);
    173    }
    174  }
    175 }