forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			175 lines
		
	
	
	
		
			4.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			175 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);
 | |
|     }
 | |
|   }
 | |
| }
 | 
