1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/modules/ContentLinkHandler.jsm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,141 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +"use strict"; 1.9 + 1.10 +let Cc = Components.classes; 1.11 +let Ci = Components.interfaces; 1.12 +let Cu = Components.utils; 1.13 + 1.14 +this.EXPORTED_SYMBOLS = [ "ContentLinkHandler" ]; 1.15 + 1.16 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.17 +Cu.import("resource://gre/modules/Services.jsm"); 1.18 + 1.19 +XPCOMUtils.defineLazyModuleGetter(this, "Feeds", 1.20 + "resource:///modules/Feeds.jsm"); 1.21 +XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils", 1.22 + "resource://gre/modules/BrowserUtils.jsm"); 1.23 + 1.24 +this.ContentLinkHandler = { 1.25 + init: function(chromeGlobal) { 1.26 + chromeGlobal.addEventListener("DOMLinkAdded", (event) => { 1.27 + this.onLinkAdded(event, chromeGlobal); 1.28 + }, false); 1.29 + }, 1.30 + 1.31 + onLinkAdded: function(event, chromeGlobal) { 1.32 + var link = event.originalTarget; 1.33 + var rel = link.rel && link.rel.toLowerCase(); 1.34 + if (!link || !link.ownerDocument || !rel || !link.href) 1.35 + return; 1.36 + 1.37 + // Ignore sub-frames (bugs 305472, 479408). 1.38 + let window = link.ownerDocument.defaultView; 1.39 + if (window != window.top) 1.40 + return; 1.41 + 1.42 + var feedAdded = false; 1.43 + var iconAdded = false; 1.44 + var searchAdded = false; 1.45 + var rels = {}; 1.46 + for (let relString of rel.split(/\s+/)) 1.47 + rels[relString] = true; 1.48 + 1.49 + for (let relVal in rels) { 1.50 + switch (relVal) { 1.51 + case "feed": 1.52 + case "alternate": 1.53 + if (!feedAdded) { 1.54 + if (!rels.feed && rels.alternate && rels.stylesheet) 1.55 + break; 1.56 + 1.57 + if (Feeds.isValidFeed(link, link.ownerDocument.nodePrincipal, "feed" in rels)) { 1.58 + chromeGlobal.sendAsyncMessage("Link:AddFeed", 1.59 + {type: link.type, 1.60 + href: link.href, 1.61 + title: link.title}); 1.62 + feedAdded = true; 1.63 + } 1.64 + } 1.65 + break; 1.66 + case "icon": 1.67 + if (!iconAdded) { 1.68 + if (!Services.prefs.getBoolPref("browser.chrome.site_icons")) 1.69 + break; 1.70 + 1.71 + var uri = this.getLinkIconURI(link); 1.72 + if (!uri) 1.73 + break; 1.74 + 1.75 + [iconAdded] = chromeGlobal.sendSyncMessage("Link:AddIcon", {url: uri.spec}); 1.76 + } 1.77 + break; 1.78 + case "search": 1.79 + if (!searchAdded) { 1.80 + var type = link.type && link.type.toLowerCase(); 1.81 + type = type.replace(/^\s+|\s*(?:;.*)?$/g, ""); 1.82 + 1.83 + let re = /^(?:https?|ftp):/i; 1.84 + if (type == "application/opensearchdescription+xml" && link.title && 1.85 + re.test(link.href)) 1.86 + { 1.87 + let engine = { title: link.title, href: link.href }; 1.88 + chromeGlobal.sendAsyncMessage("Link:AddSearch", 1.89 + {engine: engine, 1.90 + url: link.ownerDocument.documentURI}); 1.91 + searchAdded = true; 1.92 + } 1.93 + } 1.94 + break; 1.95 + } 1.96 + } 1.97 + }, 1.98 + 1.99 + getLinkIconURI: function(aLink) { 1.100 + let targetDoc = aLink.ownerDocument; 1.101 + var uri = BrowserUtils.makeURI(aLink.href, targetDoc.characterSet); 1.102 + 1.103 + // Verify that the load of this icon is legal. 1.104 + // Some error or special pages can load their favicon. 1.105 + // To be on the safe side, only allow chrome:// favicons. 1.106 + var isAllowedPage = [ 1.107 + /^about:neterror\?/, 1.108 + /^about:blocked\?/, 1.109 + /^about:certerror\?/, 1.110 + /^about:home$/, 1.111 + ].some(function (re) re.test(targetDoc.documentURI)); 1.112 + 1.113 + if (!isAllowedPage || !uri.schemeIs("chrome")) { 1.114 + var ssm = Services.scriptSecurityManager; 1.115 + try { 1.116 + ssm.checkLoadURIWithPrincipal(targetDoc.nodePrincipal, uri, 1.117 + Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT); 1.118 + } catch(e) { 1.119 + return null; 1.120 + } 1.121 + } 1.122 + 1.123 + try { 1.124 + var contentPolicy = Cc["@mozilla.org/layout/content-policy;1"]. 1.125 + getService(Ci.nsIContentPolicy); 1.126 + } catch(e) { 1.127 + return null; // Refuse to load if we can't do a security check. 1.128 + } 1.129 + 1.130 + // Security says okay, now ask content policy 1.131 + if (contentPolicy.shouldLoad(Ci.nsIContentPolicy.TYPE_IMAGE, 1.132 + uri, targetDoc.documentURIObject, 1.133 + aLink, aLink.type, null) 1.134 + != Ci.nsIContentPolicy.ACCEPT) 1.135 + return null; 1.136 + 1.137 + try { 1.138 + uri.userPass = ""; 1.139 + } catch(e) { 1.140 + // some URIs are immutable 1.141 + } 1.142 + return uri; 1.143 + }, 1.144 +};