forked from mirrors/gecko-dev
		
	 da381d04b7
			
		
	
	
		da381d04b7
		
	
	
	
	
		
			
			Believe it or not, the memory these references hold alive is significant. Nulling them out saves 5-10K per process. MozReview-Commit-ID: JONjE48yE8I --HG-- extra : rebase_source : 49adc538070eecb9183e6e052e6e43db9c4c7a99 extra : histedit_source : 699f49fad1bfa69b8c511bf96187096e751c8606
		
			
				
	
	
		
			396 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			396 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 | |
| /* 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/. */
 | |
| 
 | |
| /* This content script should work in any browser or iframe and should not
 | |
|  * depend on the frame being contained in tabbrowser. */
 | |
| 
 | |
| /* eslint-env mozilla/frame-script */
 | |
| /* eslint no-unused-vars: ["error", {args: "none"}] */
 | |
| 
 | |
| ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 | |
| ChromeUtils.import("resource://gre/modules/Services.jsm");
 | |
| 
 | |
| // TabChildGlobal
 | |
| var global = this;
 | |
| 
 | |
| XPCOMUtils.defineLazyModuleGetters(this, {
 | |
|   BlockedSiteContent: "resource:///modules/BlockedSiteContent.jsm",
 | |
|   ContentLinkHandler: "resource:///modules/ContentLinkHandler.jsm",
 | |
|   ContentMetaHandler: "resource:///modules/ContentMetaHandler.jsm",
 | |
|   ContentWebRTC: "resource:///modules/ContentWebRTC.jsm",
 | |
|   LoginFormFactory: "resource://gre/modules/LoginManagerContent.jsm",
 | |
|   InsecurePasswordUtils: "resource://gre/modules/InsecurePasswordUtils.jsm",
 | |
|   PluginContent: "resource:///modules/PluginContent.jsm",
 | |
|   FormSubmitObserver: "resource:///modules/FormSubmitObserver.jsm",
 | |
|   NetErrorContent: "resource:///modules/NetErrorContent.jsm",
 | |
|   PageMetadata: "resource://gre/modules/PageMetadata.jsm",
 | |
|   WebNavigationFrames: "resource://gre/modules/WebNavigationFrames.jsm",
 | |
|   ContextMenu: "resource:///modules/ContextMenu.jsm",
 | |
| });
 | |
| 
 | |
| XPCOMUtils.defineLazyProxy(this, "contextMenu", () => {
 | |
|   return new ContextMenu(global);
 | |
| });
 | |
| 
 | |
| XPCOMUtils.defineLazyProxy(this, "ClickEventHandler", () => {
 | |
|   let tmp = {};
 | |
|   ChromeUtils.import("resource:///modules/ClickEventHandler.jsm", tmp);
 | |
|   return new tmp.ClickEventHandler(global);
 | |
| });
 | |
| 
 | |
| XPCOMUtils.defineLazyGetter(this, "LoginManagerContent", () => {
 | |
|   let tmp = {};
 | |
|   ChromeUtils.import("resource://gre/modules/LoginManagerContent.jsm", tmp);
 | |
|   tmp.LoginManagerContent.setupEventListeners(global);
 | |
|   return tmp.LoginManagerContent;
 | |
| });
 | |
| 
 | |
| XPCOMUtils.defineLazyProxy(this, "formSubmitObserver", () => {
 | |
|   return new FormSubmitObserver(content, this);
 | |
| }, {
 | |
|   // stub QI
 | |
|   QueryInterface: ChromeUtils.generateQI([Ci.nsIFormSubmitObserver, Ci.nsISupportsWeakReference])
 | |
| });
 | |
| 
 | |
| XPCOMUtils.defineLazyProxy(this, "PageInfoListener",
 | |
|                            "resource:///modules/PageInfoListener.jsm");
 | |
| 
 | |
| XPCOMUtils.defineLazyProxy(this, "LightWeightThemeWebInstallListener",
 | |
|                            "resource:///modules/LightWeightThemeWebInstallListener.jsm");
 | |
| 
 | |
| Services.els.addSystemEventListener(global, "contextmenu", contextMenu, false);
 | |
| 
 | |
| Services.obs.addObserver(formSubmitObserver, "invalidformsubmit", true);
 | |
| 
 | |
| addMessageListener("PageInfo:getData", PageInfoListener);
 | |
| 
 | |
| // NOTE: Much of this logic is duplicated in BrowserCLH.js for Android.
 | |
| addMessageListener("RemoteLogins:fillForm", function(message) {
 | |
|   // intercept if ContextMenu.jsm had sent a plain object for remote targets
 | |
|   message.objects.inputElement = contextMenu.getTarget(message, "inputElement");
 | |
|   LoginManagerContent.receiveMessage(message, content);
 | |
| });
 | |
| addEventListener("DOMFormHasPassword", function(event) {
 | |
|   LoginManagerContent.onDOMFormHasPassword(event, content);
 | |
|   let formLike = LoginFormFactory.createFromForm(event.originalTarget);
 | |
|   InsecurePasswordUtils.reportInsecurePasswords(formLike);
 | |
| });
 | |
| addEventListener("DOMInputPasswordAdded", function(event) {
 | |
|   LoginManagerContent.onDOMInputPasswordAdded(event, content);
 | |
|   let formLike = LoginFormFactory.createFromField(event.originalTarget);
 | |
|   InsecurePasswordUtils.reportInsecurePasswords(formLike);
 | |
| });
 | |
| addEventListener("DOMAutoComplete", function(event) {
 | |
|   LoginManagerContent.onUsernameInput(event);
 | |
| });
 | |
| 
 | |
| var AboutBlockedSiteListener = {
 | |
|   init(chromeGlobal) {
 | |
|     addMessageListener("DeceptiveBlockedDetails", this);
 | |
|     chromeGlobal.addEventListener("AboutBlockedLoaded", this, false, true);
 | |
|     this.init = null;
 | |
|   },
 | |
| 
 | |
|   get isBlockedSite() {
 | |
|     return content.document.documentURI.startsWith("about:blocked");
 | |
|   },
 | |
| 
 | |
|   receiveMessage(msg) {
 | |
|     if (!this.isBlockedSite) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     BlockedSiteContent.receiveMessage(global, msg);
 | |
|   },
 | |
| 
 | |
|   handleEvent(aEvent) {
 | |
|     if (!this.isBlockedSite) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     if (aEvent.type != "AboutBlockedLoaded") {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     BlockedSiteContent.handleEvent(global, aEvent);
 | |
|   },
 | |
| };
 | |
| AboutBlockedSiteListener.init(this);
 | |
| 
 | |
| this.AboutNetAndCertErrorListener = {
 | |
|   init(chromeGlobal) {
 | |
|     addMessageListener("CertErrorDetails", this);
 | |
|     addMessageListener("Browser:CaptivePortalFreed", this);
 | |
|     chromeGlobal.addEventListener("AboutNetErrorLoad", this, false, true);
 | |
|     chromeGlobal.addEventListener("AboutNetErrorOpenCaptivePortal", this, false, true);
 | |
|     chromeGlobal.addEventListener("AboutNetErrorSetAutomatic", this, false, true);
 | |
|     chromeGlobal.addEventListener("AboutNetErrorResetPreferences", this, false, true);
 | |
|     this.init = null;
 | |
|   },
 | |
| 
 | |
|   isAboutNetError(doc) {
 | |
|     return doc.documentURI.startsWith("about:neterror");
 | |
|   },
 | |
| 
 | |
|   isAboutCertError(doc) {
 | |
|     return doc.documentURI.startsWith("about:certerror");
 | |
|   },
 | |
| 
 | |
|   receiveMessage(msg) {
 | |
|     if (msg.name == "CertErrorDetails") {
 | |
|       let frameDocShell = WebNavigationFrames.findDocShell(msg.data.frameId, docShell);
 | |
|       // We need nsIWebNavigation to access docShell.document.
 | |
|       frameDocShell && frameDocShell.QueryInterface(Ci.nsIWebNavigation);
 | |
|       if (!frameDocShell || !this.isAboutCertError(frameDocShell.document)) {
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       NetErrorContent.onCertErrorDetails(global, msg, frameDocShell);
 | |
|     } else if (msg.name == "Browser:CaptivePortalFreed") {
 | |
|       // TODO: This check is not correct for frames.
 | |
|       if (!this.isAboutCertError(content.document)) {
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       this.onCaptivePortalFreed(msg);
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   onCaptivePortalFreed(msg) {
 | |
|     content.dispatchEvent(new content.CustomEvent("AboutNetErrorCaptivePortalFreed"));
 | |
|   },
 | |
| 
 | |
|   handleEvent(aEvent) {
 | |
|     // Documents have a null ownerDocument.
 | |
|     let doc = aEvent.originalTarget.ownerDocument || aEvent.originalTarget;
 | |
| 
 | |
|     if (!this.isAboutNetError(doc) && !this.isAboutCertError(doc)) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     NetErrorContent.handleEvent(global, aEvent);
 | |
|   },
 | |
| };
 | |
| AboutNetAndCertErrorListener.init(this);
 | |
| 
 | |
| Services.els.addSystemEventListener(global, "click", ClickEventHandler, true);
 | |
| 
 | |
| new ContentLinkHandler(this);
 | |
| ContentMetaHandler.init(this);
 | |
| 
 | |
| var PluginContentStub = {
 | |
|   EVENTS: [
 | |
|     "PluginCrashed",
 | |
|     "PluginOutdated",
 | |
|     "PluginInstantiated",
 | |
|     "PluginRemoved",
 | |
|     "HiddenPlugin",
 | |
|   ],
 | |
| 
 | |
|   MESSAGES: [
 | |
|     "BrowserPlugins:ActivatePlugins",
 | |
|     "BrowserPlugins:NotificationShown",
 | |
|     "BrowserPlugins:ContextMenuCommand",
 | |
|     "BrowserPlugins:NPAPIPluginProcessCrashed",
 | |
|     "BrowserPlugins:CrashReportSubmitted",
 | |
|     "BrowserPlugins:Test:ClearCrashData",
 | |
|   ],
 | |
| 
 | |
|   _pluginContent: null,
 | |
|   get pluginContent() {
 | |
|     if (!this._pluginContent) {
 | |
|       this._pluginContent = new PluginContent(global);
 | |
|     }
 | |
|     return this._pluginContent;
 | |
|   },
 | |
| 
 | |
|   init() {
 | |
|     addEventListener("unload", this);
 | |
| 
 | |
|     addEventListener("PluginBindingAttached", this, true, true);
 | |
| 
 | |
|     for (let event of this.EVENTS) {
 | |
|       addEventListener(event, this, true);
 | |
|     }
 | |
|     for (let msg of this.MESSAGES) {
 | |
|       addMessageListener(msg, this);
 | |
|     }
 | |
|     Services.obs.addObserver(this, "decoder-doctor-notification");
 | |
|     this.init = null;
 | |
|   },
 | |
| 
 | |
|   uninit() {
 | |
|     Services.obs.removeObserver(this, "decoder-doctor-notification");
 | |
|   },
 | |
| 
 | |
|   observe(subject, topic, data) {
 | |
|     return this.pluginContent.observe(subject, topic, data);
 | |
|   },
 | |
| 
 | |
|   handleEvent(event) {
 | |
|     if (event.type === "unload") {
 | |
|       return this.uninit();
 | |
|     }
 | |
|     return this.pluginContent.handleEvent(event);
 | |
|   },
 | |
| 
 | |
|   receiveMessage(msg) {
 | |
|     return this.pluginContent.receiveMessage(msg);
 | |
|   },
 | |
| };
 | |
| 
 | |
| PluginContentStub.init();
 | |
| 
 | |
| // This is a temporary hack to prevent regressions (bug 1471327).
 | |
| void content;
 | |
| 
 | |
| addEventListener("DOMWindowFocus", function(event) {
 | |
|   sendAsyncMessage("DOMWindowFocus", {});
 | |
| }, false);
 | |
| 
 | |
| // We use this shim so that ContentWebRTC.jsm will not be loaded until
 | |
| // it is actually needed.
 | |
| var ContentWebRTCShim = message => ContentWebRTC.receiveMessage(message);
 | |
| 
 | |
| addMessageListener("rtcpeer:Allow", ContentWebRTCShim);
 | |
| addMessageListener("rtcpeer:Deny", ContentWebRTCShim);
 | |
| addMessageListener("webrtc:Allow", ContentWebRTCShim);
 | |
| addMessageListener("webrtc:Deny", ContentWebRTCShim);
 | |
| addMessageListener("webrtc:StopSharing", ContentWebRTCShim);
 | |
| 
 | |
| var PageMetadataMessenger = {
 | |
|   init() {
 | |
|     addMessageListener("PageMetadata:GetPageData", this);
 | |
|     addMessageListener("PageMetadata:GetMicroformats", this);
 | |
|     this.init = null;
 | |
|   },
 | |
|   receiveMessage(message) {
 | |
|     switch (message.name) {
 | |
|       case "PageMetadata:GetPageData": {
 | |
|         let target = contextMenu.getTarget(message);
 | |
|         let result = PageMetadata.getData(content.document, target);
 | |
|         sendAsyncMessage("PageMetadata:PageDataResult", result);
 | |
|         break;
 | |
|       }
 | |
|       case "PageMetadata:GetMicroformats": {
 | |
|         let target = contextMenu.getTarget(message);
 | |
|         let result = PageMetadata.getMicroformats(content.document, target);
 | |
|         sendAsyncMessage("PageMetadata:MicroformatsResult", result);
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| };
 | |
| PageMetadataMessenger.init();
 | |
| 
 | |
| addEventListener("InstallBrowserTheme", LightWeightThemeWebInstallListener, false, true);
 | |
| addEventListener("PreviewBrowserTheme", LightWeightThemeWebInstallListener, false, true);
 | |
| addEventListener("ResetBrowserThemePreview", LightWeightThemeWebInstallListener, false, true);
 | |
| 
 | |
| let OfflineApps = {
 | |
|   _docId: 0,
 | |
|   _docIdMap: new Map(),
 | |
| 
 | |
|   _docManifestSet: new Set(),
 | |
| 
 | |
|   _observerAdded: false,
 | |
|   registerWindow(aWindow) {
 | |
|     if (!this._observerAdded) {
 | |
|       this._observerAdded = true;
 | |
|       Services.obs.addObserver(this, "offline-cache-update-completed", true);
 | |
|     }
 | |
|     let manifestURI = this._getManifestURI(aWindow);
 | |
|     this._docManifestSet.add(manifestURI.spec);
 | |
|   },
 | |
| 
 | |
|   handleEvent(event) {
 | |
|     if (event.type == "MozApplicationManifest") {
 | |
|       this.offlineAppRequested(event.originalTarget.defaultView);
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   _getManifestURI(aWindow) {
 | |
|     if (!aWindow.document.documentElement)
 | |
|       return null;
 | |
| 
 | |
|     var attr = aWindow.document.documentElement.getAttribute("manifest");
 | |
|     if (!attr)
 | |
|       return null;
 | |
| 
 | |
|     try {
 | |
|       return Services.io.newURI(attr, aWindow.document.characterSet,
 | |
|                                 Services.io.newURI(aWindow.location.href));
 | |
|     } catch (e) {
 | |
|       return null;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   offlineAppRequested(aContentWindow) {
 | |
|     this.registerWindow(aContentWindow);
 | |
|     if (!Services.prefs.getBoolPref("browser.offline-apps.notify")) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     let currentURI = aContentWindow.document.documentURIObject;
 | |
|     // don't bother showing UI if the user has already made a decision
 | |
|     if (Services.perms.testExactPermission(currentURI, "offline-app") != Services.perms.UNKNOWN_ACTION)
 | |
|       return;
 | |
| 
 | |
|     try {
 | |
|       if (Services.prefs.getBoolPref("offline-apps.allow_by_default")) {
 | |
|         // all pages can use offline capabilities, no need to ask the user
 | |
|         return;
 | |
|       }
 | |
|     } catch (e) {
 | |
|       // this pref isn't set by default, ignore failures
 | |
|     }
 | |
|     let docId = ++this._docId;
 | |
|     this._docIdMap.set(docId, Cu.getWeakReference(aContentWindow.document));
 | |
|     sendAsyncMessage("OfflineApps:RequestPermission", {
 | |
|       uri: currentURI.spec,
 | |
|       docId,
 | |
|     });
 | |
|   },
 | |
| 
 | |
|   _startFetching(aDocument) {
 | |
|     if (!aDocument.documentElement)
 | |
|       return;
 | |
| 
 | |
|     let manifestURI = this._getManifestURI(aDocument.defaultView);
 | |
|     if (!manifestURI)
 | |
|       return;
 | |
| 
 | |
|     var updateService = Cc["@mozilla.org/offlinecacheupdate-service;1"].
 | |
|                         getService(Ci.nsIOfflineCacheUpdateService);
 | |
|     updateService.scheduleUpdate(manifestURI, aDocument.documentURIObject,
 | |
|                                  aDocument.nodePrincipal, aDocument.defaultView);
 | |
|   },
 | |
| 
 | |
|   receiveMessage(aMessage) {
 | |
|     if (aMessage.name == "OfflineApps:StartFetching") {
 | |
|       let doc = this._docIdMap.get(aMessage.data.docId);
 | |
|       doc = doc && doc.get();
 | |
|       if (doc) {
 | |
|         this._startFetching(doc);
 | |
|       }
 | |
|       this._docIdMap.delete(aMessage.data.docId);
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   observe(aSubject, aTopic, aState) {
 | |
|     if (aTopic == "offline-cache-update-completed") {
 | |
|       let cacheUpdate = aSubject.QueryInterface(Ci.nsIOfflineCacheUpdate);
 | |
|       let uri = cacheUpdate.manifestURI;
 | |
|       if (uri && this._docManifestSet.has(uri.spec)) {
 | |
|         sendAsyncMessage("OfflineApps:CheckUsage", {uri: uri.spec});
 | |
|       }
 | |
|     }
 | |
|   },
 | |
|   QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver,
 | |
|                                           Ci.nsISupportsWeakReference]),
 | |
| };
 | |
| 
 | |
| addEventListener("MozApplicationManifest", OfflineApps, false);
 | |
| addMessageListener("OfflineApps:StartFetching", OfflineApps);
 |