diff options
Diffstat (limited to 'browser/modules/ContentLinkHandler.jsm')
-rw-r--r-- | browser/modules/ContentLinkHandler.jsm | 147 |
1 files changed, 147 insertions, 0 deletions
diff --git a/browser/modules/ContentLinkHandler.jsm b/browser/modules/ContentLinkHandler.jsm new file mode 100644 index 000000000..443cae2da --- /dev/null +++ b/browser/modules/ContentLinkHandler.jsm @@ -0,0 +1,147 @@ +/* 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/. */ + +"use strict"; + +var Cc = Components.classes; +var Ci = Components.interfaces; +var Cu = Components.utils; + +this.EXPORTED_SYMBOLS = [ "ContentLinkHandler" ]; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/NetUtil.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "Feeds", + "resource:///modules/Feeds.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils", + "resource://gre/modules/BrowserUtils.jsm"); + +const SIZES_TELEMETRY_ENUM = { + NO_SIZES: 0, + ANY: 1, + DIMENSION: 2, + INVALID: 3, +}; + +this.ContentLinkHandler = { + init: function(chromeGlobal) { + chromeGlobal.addEventListener("DOMLinkAdded", (event) => { + this.onLinkEvent(event, chromeGlobal); + }, false); + chromeGlobal.addEventListener("DOMLinkChanged", (event) => { + this.onLinkEvent(event, chromeGlobal); + }, false); + }, + + onLinkEvent: function(event, chromeGlobal) { + var link = event.originalTarget; + var rel = link.rel && link.rel.toLowerCase(); + if (!link || !link.ownerDocument || !rel || !link.href) + return; + + // Ignore sub-frames (bugs 305472, 479408). + let window = link.ownerGlobal; + if (window != window.top) + return; + + var feedAdded = false; + var iconAdded = false; + var searchAdded = false; + var rels = {}; + for (let relString of rel.split(/\s+/)) + rels[relString] = true; + + for (let relVal in rels) { + switch (relVal) { + case "feed": + case "alternate": + if (!feedAdded && event.type == "DOMLinkAdded") { + if (!rels.feed && rels.alternate && rels.stylesheet) + break; + + if (Feeds.isValidFeed(link, link.ownerDocument.nodePrincipal, "feed" in rels)) { + chromeGlobal.sendAsyncMessage("Link:AddFeed", + {type: link.type, + href: link.href, + title: link.title}); + feedAdded = true; + } + } + break; + case "icon": + if (iconAdded || !Services.prefs.getBoolPref("browser.chrome.site_icons")) + break; + + var uri = this.getLinkIconURI(link); + if (!uri) + break; + + // Telemetry probes for measuring the sizes attribute + // usage and available dimensions. + let sizeHistogramTypes = Services.telemetry. + getHistogramById("LINK_ICON_SIZES_ATTR_USAGE"); + let sizeHistogramDimension = Services.telemetry. + getHistogramById("LINK_ICON_SIZES_ATTR_DIMENSION"); + let sizesType; + if (link.sizes.length) { + for (let size of link.sizes) { + if (size.toLowerCase() == "any") { + sizesType = SIZES_TELEMETRY_ENUM.ANY; + break; + } else { + let re = /^([1-9][0-9]*)x[1-9][0-9]*$/i; + let values = re.exec(size); + if (values && values.length > 1) { + sizesType = SIZES_TELEMETRY_ENUM.DIMENSION; + sizeHistogramDimension.add(parseInt(values[1])); + } else { + sizesType = SIZES_TELEMETRY_ENUM.INVALID; + break; + } + } + } + } else { + sizesType = SIZES_TELEMETRY_ENUM.NO_SIZES; + } + sizeHistogramTypes.add(sizesType); + + chromeGlobal.sendAsyncMessage( + "Link:SetIcon", + {url: uri.spec, loadingPrincipal: link.ownerDocument.nodePrincipal}); + iconAdded = true; + break; + case "search": + if (!searchAdded && event.type == "DOMLinkAdded") { + var type = link.type && link.type.toLowerCase(); + type = type.replace(/^\s+|\s*(?:;.*)?$/g, ""); + + let re = /^(?:https?|ftp):/i; + if (type == "application/opensearchdescription+xml" && link.title && + re.test(link.href)) + { + let engine = { title: link.title, href: link.href }; + chromeGlobal.sendAsyncMessage("Link:AddSearch", + {engine: engine, + url: link.ownerDocument.documentURI}); + searchAdded = true; + } + } + break; + } + } + }, + + getLinkIconURI: function(aLink) { + let targetDoc = aLink.ownerDocument; + var uri = BrowserUtils.makeURI(aLink.href, targetDoc.characterSet); + try { + uri.userPass = ""; + } catch (e) { + // some URIs are immutable + } + return uri; + }, +}; |