Source code

Revision control

Other Tools

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