mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-27 14:52:16 +00:00
130ce23c4c
Differential Revision: https://phabricator.services.mozilla.com/D179169
176 lines
4.6 KiB
JavaScript
176 lines
4.6 KiB
JavaScript
/* 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/. */
|
|
|
|
const lazy = {};
|
|
|
|
ChromeUtils.defineESModuleGetters(lazy, {
|
|
FaviconLoader: "resource:///modules/FaviconLoader.sys.mjs",
|
|
});
|
|
|
|
export class LinkHandlerChild extends JSWindowActorChild {
|
|
constructor() {
|
|
super();
|
|
|
|
this.seenTabIcon = false;
|
|
this._iconLoader = null;
|
|
}
|
|
|
|
get iconLoader() {
|
|
if (!this._iconLoader) {
|
|
this._iconLoader = new lazy.FaviconLoader(this);
|
|
}
|
|
return this._iconLoader;
|
|
}
|
|
|
|
addRootIcon() {
|
|
if (
|
|
!this.seenTabIcon &&
|
|
Services.prefs.getBoolPref("browser.chrome.guess_favicon", true) &&
|
|
Services.prefs.getBoolPref("browser.chrome.site_icons", true)
|
|
) {
|
|
// Inject the default icon. Use documentURIObject so that we do the right
|
|
// thing with about:-style error pages. See bug 453442
|
|
let pageURI = this.document.documentURIObject;
|
|
if (["http", "https"].includes(pageURI.scheme)) {
|
|
this.seenTabIcon = true;
|
|
this.iconLoader.addDefaultIcon(pageURI);
|
|
}
|
|
}
|
|
}
|
|
|
|
onHeadParsed(event) {
|
|
if (event.target.ownerDocument != this.document) {
|
|
return;
|
|
}
|
|
|
|
// Per spec icons are meant to be in the <head> tag so we should have seen
|
|
// all the icons now so add the root icon if no other tab icons have been
|
|
// seen.
|
|
this.addRootIcon();
|
|
|
|
// We're likely done with icon parsing so load the pending icons now.
|
|
if (this._iconLoader) {
|
|
this._iconLoader.onPageShow();
|
|
}
|
|
}
|
|
|
|
onPageShow(event) {
|
|
if (event.target != this.document) {
|
|
return;
|
|
}
|
|
|
|
this.addRootIcon();
|
|
|
|
if (this._iconLoader) {
|
|
this._iconLoader.onPageShow();
|
|
}
|
|
}
|
|
|
|
onPageHide(event) {
|
|
if (event.target != this.document) {
|
|
return;
|
|
}
|
|
|
|
if (this._iconLoader) {
|
|
this._iconLoader.onPageHide();
|
|
}
|
|
|
|
this.seenTabIcon = false;
|
|
}
|
|
|
|
onLinkEvent(event) {
|
|
let link = event.target;
|
|
// Ignore sub-frames (bugs 305472, 479408).
|
|
if (link.ownerGlobal != this.contentWindow) {
|
|
return;
|
|
}
|
|
|
|
let rel = link.rel && link.rel.toLowerCase();
|
|
// We also check .getAttribute, since an empty href attribute will give us
|
|
// a link.href that is the same as the document.
|
|
if (!rel || !link.href || !link.getAttribute("href")) {
|
|
return;
|
|
}
|
|
|
|
// Note: following booleans only work for the current link, not for the
|
|
// whole content
|
|
let iconAdded = false;
|
|
let searchAdded = false;
|
|
let rels = {};
|
|
for (let relString of rel.split(/\s+/)) {
|
|
rels[relString] = true;
|
|
}
|
|
|
|
for (let relVal in rels) {
|
|
let isRichIcon = false;
|
|
|
|
switch (relVal) {
|
|
case "apple-touch-icon":
|
|
case "apple-touch-icon-precomposed":
|
|
case "fluid-icon":
|
|
isRichIcon = true;
|
|
// fall through
|
|
case "icon":
|
|
if (iconAdded || link.hasAttribute("mask")) {
|
|
// Masked icons are not supported yet.
|
|
break;
|
|
}
|
|
|
|
if (!Services.prefs.getBoolPref("browser.chrome.site_icons", true)) {
|
|
return;
|
|
}
|
|
|
|
if (this.iconLoader.addIconFromLink(link, isRichIcon)) {
|
|
iconAdded = true;
|
|
if (!isRichIcon) {
|
|
this.seenTabIcon = true;
|
|
}
|
|
}
|
|
break;
|
|
case "search":
|
|
if (
|
|
Services.policies &&
|
|
!Services.policies.isAllowed("installSearchEngine")
|
|
) {
|
|
break;
|
|
}
|
|
|
|
if (!searchAdded && event.type == "DOMLinkAdded") {
|
|
let type = link.type && link.type.toLowerCase();
|
|
type = type.replace(/^\s+|\s*(?:;.*)?$/g, "");
|
|
|
|
// Note: This protocol list should be kept in sync with
|
|
// the one in OpenSearchEngine's install function.
|
|
let re = /^https?:/i;
|
|
if (
|
|
type == "application/opensearchdescription+xml" &&
|
|
link.title &&
|
|
re.test(link.href)
|
|
) {
|
|
let engine = { title: link.title, href: link.href };
|
|
this.sendAsyncMessage("Link:AddSearch", {
|
|
engine,
|
|
});
|
|
searchAdded = true;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
handleEvent(event) {
|
|
switch (event.type) {
|
|
case "pageshow":
|
|
return this.onPageShow(event);
|
|
case "pagehide":
|
|
return this.onPageHide(event);
|
|
case "DOMHeadElementParsed":
|
|
return this.onHeadParsed(event);
|
|
default:
|
|
return this.onLinkEvent(event);
|
|
}
|
|
}
|
|
}
|