forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			146 lines
		
	
	
	
		
			4.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			146 lines
		
	
	
	
		
			4.8 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/. */
 | |
| 
 | |
| "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(chromeGlobal) {
 | |
|     chromeGlobal.addEventListener("DOMLinkAdded", (event) => {
 | |
|       this.onLinkEvent(event, chromeGlobal);
 | |
|     });
 | |
|     chromeGlobal.addEventListener("DOMLinkChanged", (event) => {
 | |
|       this.onLinkEvent(event, chromeGlobal);
 | |
|     });
 | |
|   },
 | |
| 
 | |
|   onLinkEvent(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,
 | |
|                                              url: link.ownerDocument.documentURI});
 | |
|               searchAdded = true;
 | |
|             }
 | |
|           }
 | |
|           break;
 | |
|       }
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   getLinkIconURI(aLink) {
 | |
|     let targetDoc = aLink.ownerDocument;
 | |
|     var uri = BrowserUtils.makeURI(aLink.href, targetDoc.characterSet);
 | |
|     try {
 | |
|       uri.userPass = "";
 | |
|     } catch (e) {
 | |
|       // some URIs are immutable
 | |
|     }
 | |
|     return uri;
 | |
|   },
 | |
| };
 | 
