diff options
Diffstat (limited to 'application/basilisk/base/content/content.js')
-rw-r--r-- | application/basilisk/base/content/content.js | 1438 |
1 files changed, 0 insertions, 1438 deletions
diff --git a/application/basilisk/base/content/content.js b/application/basilisk/base/content/content.js deleted file mode 100644 index d2a70ba11..000000000 --- a/application/basilisk/base/content/content.js +++ /dev/null @@ -1,1438 +0,0 @@ -/* -*- 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. */ - -var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource:///modules/ContentWebRTC.jsm"); -Cu.import("resource:///modules/ContentObservers.jsm"); -Cu.import("resource://gre/modules/InlineSpellChecker.jsm"); -Cu.import("resource://gre/modules/InlineSpellCheckerContent.jsm"); -Cu.import("resource://gre/modules/Task.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "E10SUtils", - "resource:///modules/E10SUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils", - "resource://gre/modules/BrowserUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "ContentLinkHandler", - "resource:///modules/ContentLinkHandler.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "LoginManagerContent", - "resource://gre/modules/LoginManagerContent.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "LoginFormFactory", - "resource://gre/modules/LoginManagerContent.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "InsecurePasswordUtils", - "resource://gre/modules/InsecurePasswordUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "PluginContent", - "resource:///modules/PluginContent.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", - "resource://gre/modules/PrivateBrowsingUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "FormSubmitObserver", - "resource:///modules/FormSubmitObserver.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "PageMetadata", - "resource://gre/modules/PageMetadata.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "PlacesUIUtils", - "resource:///modules/PlacesUIUtils.jsm"); -XPCOMUtils.defineLazyGetter(this, "PageMenuChild", function() { - let tmp = {}; - Cu.import("resource://gre/modules/PageMenu.jsm", tmp); - return new tmp.PageMenuChild(); -}); -XPCOMUtils.defineLazyModuleGetter(this, "Feeds", - "resource:///modules/Feeds.jsm"); - -Cu.importGlobalProperties(["URL"]); - -// TabChildGlobal -var global = this; - -// Load the form validation popup handler -var formSubmitObserver = new FormSubmitObserver(content, this); - -addMessageListener("ContextMenu:DoCustomCommand", function(message) { - E10SUtils.wrapHandlingUserInput( - content, message.data.handlingUserInput, - () => PageMenuChild.executeMenu(message.data.generatedItemId)); -}); - -addMessageListener("RemoteLogins:fillForm", function(message) { - LoginManagerContent.receiveMessage(message, content); -}); -addEventListener("DOMFormHasPassword", function(event) { - LoginManagerContent.onDOMFormHasPassword(event, content); - let formLike = LoginFormFactory.createFromForm(event.target); - InsecurePasswordUtils.reportInsecurePasswords(formLike); -}); -addEventListener("DOMInputPasswordAdded", function(event) { - LoginManagerContent.onDOMInputPasswordAdded(event, content); - let formLike = LoginFormFactory.createFromField(event.target); - InsecurePasswordUtils.reportInsecurePasswords(formLike); -}); -addEventListener("pageshow", function(event) { - LoginManagerContent.onPageShow(event, content); -}); -addEventListener("DOMAutoComplete", function(event) { - LoginManagerContent.onUsernameInput(event); -}); -addEventListener("blur", function(event) { - LoginManagerContent.onUsernameInput(event); -}); - -var handleContentContextMenu = function (event) { - let defaultPrevented = event.defaultPrevented; - if (!Services.prefs.getBoolPref("dom.event.contextmenu.enabled")) { - let plugin = null; - try { - plugin = event.target.QueryInterface(Ci.nsIObjectLoadingContent); - } catch (e) {} - if (plugin && plugin.displayedType == Ci.nsIObjectLoadingContent.TYPE_PLUGIN) { - // Don't open a context menu for plugins. - return; - } - - defaultPrevented = false; - } - - if (defaultPrevented) - return; - - let addonInfo = {}; - let subject = { - event: event, - addonInfo: addonInfo, - }; - subject.wrappedJSObject = subject; - Services.obs.notifyObservers(subject, "content-contextmenu", null); - - let doc = event.target.ownerDocument; - let docLocation = doc.mozDocumentURIIfNotForErrorPages; - docLocation = docLocation && docLocation.spec; - let charSet = doc.characterSet; - let baseURI = doc.baseURI; - let referrer = doc.referrer; - let referrerPolicy = doc.referrerPolicy; - let frameOuterWindowID = doc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils) - .outerWindowID; - let loginFillInfo = LoginManagerContent.getFieldContext(event.target); - - // The same-origin check will be done in nsContextMenu.openLinkInTab. - let parentAllowsMixedContent = !!docShell.mixedContentChannel; - - // get referrer attribute from clicked link and parse it - // if per element referrer is enabled, the element referrer overrules - // the document wide referrer - if (Services.prefs.getBoolPref("network.http.enablePerElementReferrer")) { - let referrerAttrValue = Services.netUtils.parseAttributePolicyString(event.target. - getAttribute("referrerpolicy")); - if (referrerAttrValue !== Ci.nsIHttpChannel.REFERRER_POLICY_UNSET) { - referrerPolicy = referrerAttrValue; - } - } - - let disableSetDesktopBg = null; - // Media related cache info parent needs for saving - let contentType = null; - let contentDisposition = null; - if (event.target.nodeType == Ci.nsIDOMNode.ELEMENT_NODE && - event.target instanceof Ci.nsIImageLoadingContent && - event.target.currentURI) { - disableSetDesktopBg = disableSetDesktopBackground(event.target); - - try { - let imageCache = - Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools) - .getImgCacheForDocument(doc); - let props = - imageCache.findEntryProperties(event.target.currentURI, doc); - try { - contentType = props.get("type", Ci.nsISupportsCString).data; - } catch (e) {} - try { - contentDisposition = - props.get("content-disposition", Ci.nsISupportsCString).data; - } catch (e) {} - } catch (e) {} - } - - let selectionInfo = BrowserUtils.getSelectionDetails(content); - - if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) { - let editFlags = SpellCheckHelper.isEditable(event.target, content); - let spellInfo; - if (editFlags & - (SpellCheckHelper.EDITABLE | SpellCheckHelper.CONTENTEDITABLE)) { - spellInfo = - InlineSpellCheckerContent.initContextMenu(event, editFlags, this); - } - - // Set the event target first as the copy image command needs it to - // determine what was context-clicked on. Then, update the state of the - // commands on the context menu. - docShell.contentViewer.QueryInterface(Ci.nsIContentViewerEdit) - .setCommandNode(event.target); - event.target.ownerGlobal.updateCommands("contentcontextmenu"); - - let customMenuItems = PageMenuChild.build(event.target); - let principal = doc.nodePrincipal; - sendRpcMessage("contextmenu", - { editFlags, spellInfo, customMenuItems, addonInfo, - principal, docLocation, charSet, baseURI, referrer, - referrerPolicy, contentType, contentDisposition, - frameOuterWindowID, selectionInfo, disableSetDesktopBg, - loginFillInfo, parentAllowsMixedContent }, - { event, popupNode: event.target }); - } - else { - // Break out to the parent window and pass the add-on info along - let browser = docShell.chromeEventHandler; - let mainWin = browser.ownerGlobal; - mainWin.gContextMenuContentData = { - isRemote: false, - event: event, - popupNode: event.target, - browser: browser, - addonInfo: addonInfo, - documentURIObject: doc.documentURIObject, - docLocation: docLocation, - charSet: charSet, - referrer: referrer, - referrerPolicy: referrerPolicy, - contentType: contentType, - contentDisposition: contentDisposition, - selectionInfo: selectionInfo, - disableSetDesktopBackground: disableSetDesktopBg, - loginFillInfo, - parentAllowsMixedContent, - }; - } -} - -Cc["@mozilla.org/eventlistenerservice;1"] - .getService(Ci.nsIEventListenerService) - .addSystemEventListener(global, "contextmenu", handleContentContextMenu, false); - -// Values for telemtery bins: see TLS_ERROR_REPORT_UI in Histograms.json -const TLS_ERROR_REPORT_TELEMETRY_UI_SHOWN = 0; -const TLS_ERROR_REPORT_TELEMETRY_EXPANDED = 1; -const TLS_ERROR_REPORT_TELEMETRY_SUCCESS = 6; -const TLS_ERROR_REPORT_TELEMETRY_FAILURE = 7; - -const SEC_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SEC_ERROR_BASE; -const MOZILLA_PKIX_ERROR_BASE = Ci.nsINSSErrorsService.MOZILLA_PKIX_ERROR_BASE; - -const SEC_ERROR_EXPIRED_CERTIFICATE = SEC_ERROR_BASE + 11; -const SEC_ERROR_UNKNOWN_ISSUER = SEC_ERROR_BASE + 13; -const SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE = SEC_ERROR_BASE + 30; -const SEC_ERROR_OCSP_FUTURE_RESPONSE = SEC_ERROR_BASE + 131; -const SEC_ERROR_OCSP_OLD_RESPONSE = SEC_ERROR_BASE + 132; -const MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE = MOZILLA_PKIX_ERROR_BASE + 5; -const MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE = MOZILLA_PKIX_ERROR_BASE + 6; - -const PREF_BLOCKLIST_CLOCK_SKEW_SECONDS = "services.blocklist.clock_skew_seconds"; - -const PREF_SSL_IMPACT_ROOTS = ["security.tls.version.", "security.ssl3."]; - -const PREF_SSL_IMPACT = PREF_SSL_IMPACT_ROOTS.reduce((prefs, root) => { - return prefs.concat(Services.prefs.getChildList(root)); -}, []); - - -function getSerializedSecurityInfo(docShell) { - let serhelper = Cc["@mozilla.org/network/serialization-helper;1"] - .getService(Ci.nsISerializationHelper); - - let securityInfo = docShell.failedChannel && docShell.failedChannel.securityInfo; - if (!securityInfo) { - return ""; - } - securityInfo.QueryInterface(Ci.nsITransportSecurityInfo) - .QueryInterface(Ci.nsISerializable); - - return serhelper.serializeToString(securityInfo); -} - -var AboutNetAndCertErrorListener = { - init: function(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('AboutNetErrorOverride', this, false, true); - chromeGlobal.addEventListener('AboutNetErrorResetPreferences', this, false, true); - }, - - get isAboutNetError() { - return content.document.documentURI.startsWith("about:neterror"); - }, - - get isAboutCertError() { - return content.document.documentURI.startsWith("about:certerror"); - }, - - receiveMessage: function(msg) { - if (!this.isAboutCertError) { - return; - } - - switch (msg.name) { - case "CertErrorDetails": - this.onCertErrorDetails(msg); - break; - case "Browser:CaptivePortalFreed": - this.onCaptivePortalFreed(msg); - break; - } - }, - - onCertErrorDetails(msg) { - let div = content.document.getElementById("certificateErrorText"); - div.textContent = msg.data.info; - let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL"); - - switch (msg.data.code) { - case SEC_ERROR_UNKNOWN_ISSUER: - break; - - // in case the certificate expired we make sure the system clock - // matches settings server (kinto) time - case SEC_ERROR_EXPIRED_CERTIFICATE: - case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE: - case SEC_ERROR_OCSP_FUTURE_RESPONSE: - case SEC_ERROR_OCSP_OLD_RESPONSE: - case MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE: - case MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE: - - let appBuildId = Services.appinfo.appBuildID; - let year = parseInt(appBuildID.substr(0, 4), 10); - let month = parseInt(appBuildID.substr(4, 2), 10) - 1; - let day = parseInt(appBuildID.substr(6, 2), 10); - let buildDate = new Date(year, month, day); - let systemDate = new Date(); - - // if the difference is more than a day - if (buildDate > systemDate) { - let formatter = new Intl.DateTimeFormat(); - - content.document.getElementById("wrongSystemTime_URL") - .textContent = content.document.location.hostname; - content.document.getElementById("wrongSystemTime_systemDate") - .textContent = formatter.format(systemDate); - - content.document.getElementById("errorShortDesc") - .style.display = "none"; - content.document.getElementById("wrongSystemTimePanel") - .style.display = "block"; - } - break; - } - }, - - onCaptivePortalFreed(msg) { - content.dispatchEvent(new content.CustomEvent("AboutNetErrorCaptivePortalFreed")); - }, - - handleEvent: function(aEvent) { - if (!this.isAboutNetError && !this.isAboutCertError) { - return; - } - - switch (aEvent.type) { - case "AboutNetErrorLoad": - this.onPageLoad(aEvent); - break; - case "AboutNetErrorOpenCaptivePortal": - this.openCaptivePortalPage(aEvent); - break; - case "AboutNetErrorSetAutomatic": - this.onSetAutomatic(aEvent); - break; - case "AboutNetErrorOverride": - this.onOverride(aEvent); - break; - case "AboutNetErrorResetPreferences": - this.onResetPreferences(aEvent); - break; - } - }, - - changedCertPrefs: function () { - for (let prefName of PREF_SSL_IMPACT) { - if (Services.prefs.prefHasUserValue(prefName)) { - return true; - } - } - - return false; - }, - - onPageLoad: function(evt) { - if (this.isAboutCertError) { - let originalTarget = evt.originalTarget; - let ownerDoc = originalTarget.ownerDocument; - ClickEventHandler.onCertError(originalTarget, ownerDoc); - } - }, - - openCaptivePortalPage: function(evt) { - sendAsyncMessage("Browser:OpenCaptivePortalPage"); - }, - - - onResetPreferences: function(evt) { - sendAsyncMessage("Browser:ResetSSLPreferences"); - }, - - onOverride: function(evt) { - let {host, port} = content.document.mozDocumentURIIfNotForErrorPages; - sendAsyncMessage("Browser:OverrideWeakCrypto", { uri: {host, port} }); - } -} - -AboutNetAndCertErrorListener.init(this); - - -var ClickEventHandler = { - init: function init() { - Cc["@mozilla.org/eventlistenerservice;1"] - .getService(Ci.nsIEventListenerService) - .addSystemEventListener(global, "click", this, true); - }, - - handleEvent: function(event) { - if (!event.isTrusted || event.defaultPrevented || event.button == 2) { - return; - } - - let originalTarget = event.originalTarget; - let ownerDoc = originalTarget.ownerDocument; - if (!ownerDoc) { - return; - } - - // Handle click events from about pages - if (ownerDoc.documentURI.startsWith("about:certerror")) { - this.onCertError(originalTarget, ownerDoc); - return; - } else if (ownerDoc.documentURI.startsWith("about:blocked")) { - this.onAboutBlocked(originalTarget, ownerDoc); - return; - } else if (ownerDoc.documentURI.startsWith("about:neterror")) { - this.onAboutNetError(event, ownerDoc.documentURI); - return; - } - - let [href, node, principal] = this._hrefAndLinkNodeForClickEvent(event); - - // get referrer attribute from clicked link and parse it - // if per element referrer is enabled, the element referrer overrules - // the document wide referrer - let referrerPolicy = ownerDoc.referrerPolicy; - if (Services.prefs.getBoolPref("network.http.enablePerElementReferrer") && - node) { - let referrerAttrValue = Services.netUtils.parseAttributePolicyString(node. - getAttribute("referrerpolicy")); - if (referrerAttrValue !== Ci.nsIHttpChannel.REFERRER_POLICY_UNSET) { - referrerPolicy = referrerAttrValue; - } - } - - let json = { button: event.button, shiftKey: event.shiftKey, - ctrlKey: event.ctrlKey, metaKey: event.metaKey, - altKey: event.altKey, href: null, title: null, - bookmark: false, referrerPolicy: referrerPolicy, - triggeringPrincipal: principal, - originAttributes: principal ? principal.originAttributes : {}, - isContentWindowPrivate: PrivateBrowsingUtils.isContentWindowPrivate(ownerDoc.defaultView)}; - - if (href) { - try { - BrowserUtils.urlSecurityCheck(href, principal); - } catch (e) { - return; - } - - json.href = href; - if (node) { - json.title = node.getAttribute("title"); - if (event.button == 0 && !event.ctrlKey && !event.shiftKey && - !event.altKey && !event.metaKey) { - json.bookmark = node.getAttribute("rel") == "sidebar"; - if (json.bookmark) { - event.preventDefault(); // Need to prevent the pageload. - } - } - } - json.noReferrer = BrowserUtils.linkHasNoReferrer(node) - - // Check if the link needs to be opened with mixed content allowed. - // Only when the owner doc has |mixedContentChannel| and the same origin - // should we allow mixed content. - json.allowMixedContent = false; - let docshell = ownerDoc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShell); - if (docShell.mixedContentChannel) { - const sm = Services.scriptSecurityManager; - try { - let targetURI = BrowserUtils.makeURI(href); - sm.checkSameOriginURI(docshell.mixedContentChannel.URI, targetURI, false); - json.allowMixedContent = true; - } catch (e) {} - } - json.originPrincipal = ownerDoc.nodePrincipal; - json.triggeringPrincipal = ownerDoc.nodePrincipal; - - sendAsyncMessage("Content:Click", json); - return; - } - - // This might be middle mouse navigation. - if (event.button == 1) { - sendAsyncMessage("Content:Click", json); - } - }, - - onCertError: function (targetElement, ownerDoc) { - let docShell = ownerDoc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShell); - sendAsyncMessage("Browser:CertExceptionError", { - location: ownerDoc.location.href, - elementId: targetElement.getAttribute("id"), - isTopFrame: (ownerDoc.defaultView.parent === ownerDoc.defaultView), - securityInfoAsString: getSerializedSecurityInfo(docShell), - }); - }, - - onAboutBlocked: function (targetElement, ownerDoc) { - var reason = 'phishing'; - if (/e=malwareBlocked/.test(ownerDoc.documentURI)) { - reason = 'malware'; - } else if (/e=unwantedBlocked/.test(ownerDoc.documentURI)) { - reason = 'unwanted'; - } - sendAsyncMessage("Browser:SiteBlockedError", { - location: ownerDoc.location.href, - reason: reason, - elementId: targetElement.getAttribute("id"), - isTopFrame: (ownerDoc.defaultView.parent === ownerDoc.defaultView) - }); - }, - - onAboutNetError: function (event, documentURI) { - let elmId = event.originalTarget.getAttribute("id"); - if (elmId == "returnButton") { - sendAsyncMessage("Browser:SSLErrorGoBack", {}); - return; - } - if (elmId != "errorTryAgain" || !/e=netOffline/.test(documentURI)) { - return; - } - // browser front end will handle clearing offline mode and refreshing - // the page *if* we're in offline mode now. Otherwise let the error page - // handle the click. - if (Services.io.offline) { - event.preventDefault(); - sendAsyncMessage("Browser:EnableOnlineMode", {}); - } - }, - - /** - * Extracts linkNode and href for the current click target. - * - * @param event - * The click event. - * @return [href, linkNode, linkPrincipal]. - * - * @note linkNode will be null if the click wasn't on an anchor - * element. This includes SVG links, because callers expect |node| - * to behave like an <a> element, which SVG links (XLink) don't. - */ - _hrefAndLinkNodeForClickEvent: function(event) { - function isHTMLLink(aNode) { - // Be consistent with what nsContextMenu.js does. - return ((aNode instanceof content.HTMLAnchorElement && aNode.href) || - (aNode instanceof content.HTMLAreaElement && aNode.href) || - aNode instanceof content.HTMLLinkElement); - } - - let node = event.target; - while (node && !isHTMLLink(node)) { - node = node.parentNode; - } - - if (node) - return [node.href, node, node.ownerDocument.nodePrincipal]; - - // If there is no linkNode, try simple XLink. - let href, baseURI; - node = event.target; - while (node && !href) { - if (node.nodeType == content.Node.ELEMENT_NODE && - (node.localName == "a" || - node.namespaceURI == "http://www.w3.org/1998/Math/MathML")) { - href = node.getAttribute("href") || - node.getAttributeNS("http://www.w3.org/1999/xlink", "href"); - if (href) { - baseURI = node.ownerDocument.baseURIObject; - break; - } - } - node = node.parentNode; - } - - // In case of XLink, we don't return the node we got href from since - // callers expect <a>-like elements. - // Note: makeURI() will throw if aUri is not a valid URI. - return [href ? BrowserUtils.makeURI(href, null, baseURI).spec : null, null, - node && node.ownerDocument.nodePrincipal]; - } -}; -ClickEventHandler.init(); - -ContentLinkHandler.init(this); - -// TODO: Load this lazily so the JSM is run only if a relevant event/message fires. -var pluginContent = new PluginContent(global); - -addEventListener("DOMWebNotificationClicked", function(event) { - sendAsyncMessage("DOMWebNotificationClicked", {}); -}, false); - -addEventListener("DOMServiceWorkerFocusClient", function(event) { - sendAsyncMessage("DOMServiceWorkerFocusClient", {}); -}, false); - -ContentWebRTC.init(); -addMessageListener("rtcpeer:Allow", ContentWebRTC); -addMessageListener("rtcpeer:Deny", ContentWebRTC); -addMessageListener("webrtc:Allow", ContentWebRTC); -addMessageListener("webrtc:Deny", ContentWebRTC); -addMessageListener("webrtc:StopSharing", ContentWebRTC); -addMessageListener("webrtc:StartBrowserSharing", () => { - let windowID = content.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils).outerWindowID; - sendAsyncMessage("webrtc:response:StartBrowserSharing", { - windowID: windowID - }); -}); - -addEventListener("pageshow", function(event) { - if (event.target == content.document) { - sendAsyncMessage("PageVisibility:Show", { - persisted: event.persisted, - }); - } -}); -addEventListener("pagehide", function(event) { - if (event.target == content.document) { - sendAsyncMessage("PageVisibility:Hide", { - persisted: event.persisted, - }); - } -}); - -var PageMetadataMessenger = { - init() { - addMessageListener("PageMetadata:GetPageData", this); - addMessageListener("PageMetadata:GetMicroformats", this); - }, - receiveMessage(message) { - switch (message.name) { - case "PageMetadata:GetPageData": { - let target = message.objects.target; - let result = PageMetadata.getData(content.document, target); - sendAsyncMessage("PageMetadata:PageDataResult", result); - break; - } - case "PageMetadata:GetMicroformats": { - let target = message.objects.target; - let result = PageMetadata.getMicroformats(content.document, target); - sendAsyncMessage("PageMetadata:MicroformatsResult", result); - break; - } - } - } -} -PageMetadataMessenger.init(); - -addMessageListener("ContextMenu:SaveVideoFrameAsImage", (message) => { - let video = message.objects.target; - let canvas = content.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas"); - canvas.width = video.videoWidth; - canvas.height = video.videoHeight; - - let ctxDraw = canvas.getContext("2d"); - ctxDraw.drawImage(video, 0, 0); - sendAsyncMessage("ContextMenu:SaveVideoFrameAsImage:Result", { - dataURL: canvas.toDataURL("image/jpeg", ""), - }); -}); - -addMessageListener("ContextMenu:MediaCommand", (message) => { - E10SUtils.wrapHandlingUserInput( - content, message.data.handlingUserInput, - () => { - let media = message.objects.element; - switch (message.data.command) { - case "play": - media.play(); - break; - case "pause": - media.pause(); - break; - case "loop": - media.loop = !media.loop; - break; - case "mute": - media.muted = true; - break; - case "unmute": - media.muted = false; - break; - case "playbackRate": - media.playbackRate = message.data.data; - break; - case "hidecontrols": - media.removeAttribute("controls"); - break; - case "showcontrols": - media.setAttribute("controls", "true"); - break; - case "fullscreen": - if (content.document.fullscreenEnabled) - media.requestFullscreen(); - break; - } - }); -}); - -addMessageListener("ContextMenu:Canvas:ToBlobURL", (message) => { - message.objects.target.toBlob((blob) => { - let blobURL = URL.createObjectURL(blob); - sendAsyncMessage("ContextMenu:Canvas:ToBlobURL:Result", { blobURL }); - }); -}); - -addMessageListener("ContextMenu:ReloadFrame", (message) => { - message.objects.target.ownerDocument.location.reload(); -}); - -addMessageListener("ContextMenu:ReloadImage", (message) => { - let image = message.objects.target; - if (image instanceof Ci.nsIImageLoadingContent) - image.forceReload(); -}); - -addMessageListener("ContextMenu:BookmarkFrame", (message) => { - let frame = message.objects.target.ownerDocument; - sendAsyncMessage("ContextMenu:BookmarkFrame:Result", - { title: frame.title, - description: PlacesUIUtils.getDescriptionFromDocument(frame) }); -}); - -addMessageListener("ContextMenu:SearchFieldBookmarkData", (message) => { - let node = message.objects.target; - - let charset = node.ownerDocument.characterSet; - - let formBaseURI = BrowserUtils.makeURI(node.form.baseURI, - charset); - - let formURI = BrowserUtils.makeURI(node.form.getAttribute("action"), - charset, - formBaseURI); - - let spec = formURI.spec; - - let isURLEncoded = - (node.form.method.toUpperCase() == "POST" - && (node.form.enctype == "application/x-www-form-urlencoded" || - node.form.enctype == "")); - - let title = node.ownerDocument.title; - let description = PlacesUIUtils.getDescriptionFromDocument(node.ownerDocument); - - let formData = []; - - function escapeNameValuePair(aName, aValue, aIsFormUrlEncoded) { - if (aIsFormUrlEncoded) { - return escape(aName + "=" + aValue); - } - return escape(aName) + "=" + escape(aValue); - } - - for (let el of node.form.elements) { - if (!el.type) // happens with fieldsets - continue; - - if (el == node) { - formData.push((isURLEncoded) ? escapeNameValuePair(el.name, "%s", true) : - // Don't escape "%s", just append - escapeNameValuePair(el.name, "", false) + "%s"); - continue; - } - - let type = el.type.toLowerCase(); - - if (((el instanceof content.HTMLInputElement && el.mozIsTextField(true)) || - type == "hidden" || type == "textarea") || - ((type == "checkbox" || type == "radio") && el.checked)) { - formData.push(escapeNameValuePair(el.name, el.value, isURLEncoded)); - } else if (el instanceof content.HTMLSelectElement && el.selectedIndex >= 0) { - for (let j=0; j < el.options.length; j++) { - if (el.options[j].selected) - formData.push(escapeNameValuePair(el.name, el.options[j].value, - isURLEncoded)); - } - } - } - - let postData; - - if (isURLEncoded) - postData = formData.join("&"); - else { - let separator = spec.includes("?") ? "&" : "?"; - spec += separator + formData.join("&"); - } - - sendAsyncMessage("ContextMenu:SearchFieldBookmarkData:Result", - { spec, title, description, postData, charset }); -}); - -addMessageListener("Bookmarks:GetPageDetails", (message) => { - let doc = content.document; - let isErrorPage = /^about:(neterror|certerror|blocked)/.test(doc.documentURI); - sendAsyncMessage("Bookmarks:GetPageDetails:Result", - { isErrorPage: isErrorPage, - description: PlacesUIUtils.getDescriptionFromDocument(doc) }); -}); - -var LightWeightThemeWebInstallListener = { - _previewWindow: null, - - init: function() { - addEventListener("InstallBrowserTheme", this, false, true); - addEventListener("PreviewBrowserTheme", this, false, true); - addEventListener("ResetBrowserThemePreview", this, false, true); - }, - - handleEvent: function (event) { - switch (event.type) { - case "InstallBrowserTheme": { - sendAsyncMessage("LightWeightThemeWebInstaller:Install", { - baseURI: event.target.baseURI, - principal: event.target.nodePrincipal, - themeData: event.target.getAttribute("data-browsertheme"), - }); - break; - } - case "PreviewBrowserTheme": { - sendAsyncMessage("LightWeightThemeWebInstaller:Preview", { - baseURI: event.target.baseURI, - principal: event.target.nodePrincipal, - themeData: event.target.getAttribute("data-browsertheme"), - }); - this._previewWindow = event.target.ownerGlobal; - this._previewWindow.addEventListener("pagehide", this, true); - break; - } - case "pagehide": { - sendAsyncMessage("LightWeightThemeWebInstaller:ResetPreview"); - this._resetPreviewWindow(); - break; - } - case "ResetBrowserThemePreview": { - if (this._previewWindow) { - sendAsyncMessage("LightWeightThemeWebInstaller:ResetPreview", - {principal: event.target.nodePrincipal}); - this._resetPreviewWindow(); - } - break; - } - } - }, - - _resetPreviewWindow: function () { - this._previewWindow.removeEventListener("pagehide", this, true); - this._previewWindow = null; - } -}; - -LightWeightThemeWebInstallListener.init(); - -function disableSetDesktopBackground(aTarget) { - // Disable the Set as Desktop Background menu item if we're still trying - // to load the image or the load failed. - if (!(aTarget instanceof Ci.nsIImageLoadingContent)) - return true; - - if (("complete" in aTarget) && !aTarget.complete) - return true; - - if (aTarget.currentURI.schemeIs("javascript")) - return true; - - let request = aTarget.QueryInterface(Ci.nsIImageLoadingContent) - .getRequest(Ci.nsIImageLoadingContent.CURRENT_REQUEST); - if (!request) - return true; - - return false; -} - -addMessageListener("ContextMenu:SetAsDesktopBackground", (message) => { - let target = message.objects.target; - - // Paranoia: check disableSetDesktopBackground again, in case the - // image changed since the context menu was initiated. - let disable = disableSetDesktopBackground(target); - - if (!disable) { - try { - BrowserUtils.urlSecurityCheck(target.currentURI.spec, target.ownerDocument.nodePrincipal); - let canvas = content.document.createElement("canvas"); - canvas.width = target.naturalWidth; - canvas.height = target.naturalHeight; - let ctx = canvas.getContext("2d"); - ctx.drawImage(target, 0, 0); - let dataUrl = canvas.toDataURL(); - sendAsyncMessage("ContextMenu:SetAsDesktopBackground:Result", - { dataUrl }); - } - catch (e) { - Cu.reportError(e); - disable = true; - } - } - - if (disable) - sendAsyncMessage("ContextMenu:SetAsDesktopBackground:Result", { disable }); -}); - -var PageInfoListener = { - - init: function() { - addMessageListener("PageInfo:getData", this); - }, - - receiveMessage: function(message) { - let strings = message.data.strings; - let window; - let document; - - let frameOuterWindowID = message.data.frameOuterWindowID; - - // If inside frame then get the frame's window and document. - if (frameOuterWindowID) { - window = Services.wm.getOuterWindowWithId(frameOuterWindowID); - document = window.document; - } - else { - window = content.window; - document = content.document; - } - - let imageElement = message.objects.imageElement; - - let pageInfoData = {metaViewRows: this.getMetaInfo(document), - docInfo: this.getDocumentInfo(document), - feeds: this.getFeedsInfo(document, strings), - windowInfo: this.getWindowInfo(window), - imageInfo: this.getImageInfo(imageElement)}; - - sendAsyncMessage("PageInfo:data", pageInfoData); - - // Separate step so page info dialog isn't blank while waiting for this to finish. - this.getMediaInfo(document, window, strings); - }, - - getImageInfo: function(imageElement) { - let imageInfo = null; - if (imageElement) { - imageInfo = { - currentSrc: imageElement.currentSrc, - width: imageElement.width, - height: imageElement.height, - imageText: imageElement.title || imageElement.alt - }; - } - return imageInfo; - }, - - getMetaInfo: function(document) { - let metaViewRows = []; - - // Get the meta tags from the page. - let metaNodes = document.getElementsByTagName("meta"); - - for (let metaNode of metaNodes) { - metaViewRows.push([metaNode.name || metaNode.httpEquiv || metaNode.getAttribute("property"), - metaNode.content]); - } - - return metaViewRows; - }, - - getWindowInfo: function(window) { - let windowInfo = {}; - windowInfo.isTopWindow = window == window.top; - - let hostName = null; - try { - hostName = window.location.host; - } - catch (exception) { } - - windowInfo.hostName = hostName; - return windowInfo; - }, - - getDocumentInfo: function(document) { - let docInfo = {}; - docInfo.title = document.title; - docInfo.location = document.location.toString(); - docInfo.referrer = document.referrer; - docInfo.compatMode = document.compatMode; - docInfo.contentType = document.contentType; - docInfo.characterSet = document.characterSet; - docInfo.lastModified = document.lastModified; - docInfo.principal = document.nodePrincipal; - - let documentURIObject = {}; - documentURIObject.spec = document.documentURIObject.spec; - documentURIObject.originCharset = document.documentURIObject.originCharset; - docInfo.documentURIObject = documentURIObject; - - docInfo.isContentWindowPrivate = PrivateBrowsingUtils.isContentWindowPrivate(content); - - return docInfo; - }, - - getFeedsInfo: function(document, strings) { - let feeds = []; - // Get the feeds from the page. - let linkNodes = document.getElementsByTagName("link"); - let length = linkNodes.length; - for (let i = 0; i < length; i++) { - let link = linkNodes[i]; - if (!link.href) { - continue; - } - let rel = link.rel && link.rel.toLowerCase(); - let rels = {}; - - if (rel) { - for (let relVal of rel.split(/\s+/)) { - rels[relVal] = true; - } - } - - if (rels.feed || (link.type && rels.alternate && !rels.stylesheet)) { - let type = Feeds.isValidFeed(link, document.nodePrincipal, "feed" in rels); - if (type) { - type = strings[type] || strings["application/rss+xml"]; - feeds.push([link.title, type, link.href]); - } - } - } - return feeds; - }, - - // Only called once to get the media tab's media elements from the content page. - getMediaInfo: function(document, window, strings) - { - let frameList = this.goThroughFrames(document, window); - Task.spawn(() => this.processFrames(document, frameList, strings)); - }, - - goThroughFrames: function(document, window) - { - let frameList = [document]; - if (window && window.frames.length > 0) { - let num = window.frames.length; - for (let i = 0; i < num; i++) { - // Recurse through the frames. - frameList.concat(this.goThroughFrames(window.frames[i].document, - window.frames[i])); - } - } - return frameList; - }, - - processFrames: function*(document, frameList, strings) - { - let nodeCount = 0; - for (let doc of frameList) { - let iterator = doc.createTreeWalker(doc, content.NodeFilter.SHOW_ELEMENT); - - // Goes through all the elements on the doc. imageViewRows takes only the media elements. - while (iterator.nextNode()) { - let mediaItems = this.getMediaItems(document, strings, iterator.currentNode); - - if (mediaItems.length) { - sendAsyncMessage("PageInfo:mediaData", - {mediaItems, isComplete: false}); - } - - if (++nodeCount % 500 == 0) { - // setTimeout every 500 elements so we don't keep blocking the content process. - yield new Promise(resolve => setTimeout(resolve, 10)); - } - } - } - // Send that page info media fetching has finished. - sendAsyncMessage("PageInfo:mediaData", {isComplete: true}); - }, - - getMediaItems: function(document, strings, elem) - { - // Check for images defined in CSS (e.g. background, borders) - let computedStyle = elem.ownerGlobal.getComputedStyle(elem); - // A node can have multiple media items associated with it - for example, - // multiple background images. - let mediaItems = []; - - let addImage = (url, type, alt, elem, isBg) => { - let element = this.serializeElementInfo(document, url, type, alt, elem, isBg); - mediaItems.push([url, type, alt, element, isBg]); - }; - - if (computedStyle) { - let addImgFunc = (label, val) => { - if (val.primitiveType == content.CSSPrimitiveValue.CSS_URI) { - addImage(val.getStringValue(), label, strings.notSet, elem, true); - } - else if (val.primitiveType == content.CSSPrimitiveValue.CSS_STRING) { - // This is for -moz-image-rect. - // TODO: Reimplement once bug 714757 is fixed. - let strVal = val.getStringValue(); - if (strVal.search(/^.*url\(\"?/) > -1) { - let url = strVal.replace(/^.*url\(\"?/, "").replace(/\"?\).*$/, ""); - addImage(url, label, strings.notSet, elem, true); - } - } - else if (val.cssValueType == content.CSSValue.CSS_VALUE_LIST) { - // Recursively resolve multiple nested CSS value lists. - for (let i = 0; i < val.length; i++) { - addImgFunc(label, val.item(i)); - } - } - }; - - addImgFunc(strings.mediaBGImg, computedStyle.getPropertyCSSValue("background-image")); - addImgFunc(strings.mediaBorderImg, computedStyle.getPropertyCSSValue("border-image-source")); - addImgFunc(strings.mediaListImg, computedStyle.getPropertyCSSValue("list-style-image")); - addImgFunc(strings.mediaCursor, computedStyle.getPropertyCSSValue("cursor")); - } - - // One swi^H^H^Hif-else to rule them all. - if (elem instanceof content.HTMLImageElement) { - addImage(elem.src, strings.mediaImg, - (elem.hasAttribute("alt")) ? elem.alt : strings.notSet, elem, false); - } - else if (elem instanceof content.SVGImageElement) { - try { - // Note: makeURLAbsolute will throw if either the baseURI is not a valid URI - // or the URI formed from the baseURI and the URL is not a valid URI. - if (elem.href.baseVal) { - let href = Services.io.newURI(elem.href.baseVal, null, Services.io.newURI(elem.baseURI)).spec; - addImage(href, strings.mediaImg, "", elem, false); - } - } catch (e) { } - } - else if (elem instanceof content.HTMLVideoElement) { - addImage(elem.currentSrc, strings.mediaVideo, "", elem, false); - } - else if (elem instanceof content.HTMLAudioElement) { - addImage(elem.currentSrc, strings.mediaAudio, "", elem, false); - } - else if (elem instanceof content.HTMLLinkElement) { - if (elem.rel && /\bicon\b/i.test(elem.rel)) { - addImage(elem.href, strings.mediaLink, "", elem, false); - } - } - else if (elem instanceof content.HTMLInputElement || elem instanceof content.HTMLButtonElement) { - if (elem.type.toLowerCase() == "image") { - addImage(elem.src, strings.mediaInput, - (elem.hasAttribute("alt")) ? elem.alt : strings.notSet, elem, false); - } - } - else if (elem instanceof content.HTMLObjectElement) { - addImage(elem.data, strings.mediaObject, this.getValueText(elem), elem, false); - } - else if (elem instanceof content.HTMLEmbedElement) { - addImage(elem.src, strings.mediaEmbed, "", elem, false); - } - - return mediaItems; - }, - - /** - * Set up a JSON element object with all the instanceOf and other infomation that - * makePreview in pageInfo.js uses to figure out how to display the preview. - */ - - serializeElementInfo: function(document, url, type, alt, item, isBG) - { - let result = {}; - - let imageText; - if (!isBG && - !(item instanceof content.SVGImageElement) && - !(document instanceof content.ImageDocument)) { - imageText = item.title || item.alt; - - if (!imageText && !(item instanceof content.HTMLImageElement)) { - imageText = this.getValueText(item); - } - } - - result.imageText = imageText; - result.longDesc = item.longDesc; - result.numFrames = 1; - - if (item instanceof content.HTMLObjectElement || - item instanceof content.HTMLEmbedElement || - item instanceof content.HTMLLinkElement) { - result.mimeType = item.type; - } - - if (!result.mimeType && !isBG && item instanceof Ci.nsIImageLoadingContent) { - // Interface for image loading content. - let imageRequest = item.getRequest(Ci.nsIImageLoadingContent.CURRENT_REQUEST); - if (imageRequest) { - result.mimeType = imageRequest.mimeType; - let image = !(imageRequest.imageStatus & imageRequest.STATUS_ERROR) && imageRequest.image; - if (image) { - result.numFrames = image.numFrames; - } - } - } - - // If we have a data url, get the MIME type from the url. - if (!result.mimeType && url.startsWith("data:")) { - let dataMimeType = /^data:(image\/[^;,]+)/i.exec(url); - if (dataMimeType) - result.mimeType = dataMimeType[1].toLowerCase(); - } - - result.HTMLLinkElement = item instanceof content.HTMLLinkElement; - result.HTMLInputElement = item instanceof content.HTMLInputElement; - result.HTMLImageElement = item instanceof content.HTMLImageElement; - result.HTMLObjectElement = item instanceof content.HTMLObjectElement; - result.SVGImageElement = item instanceof content.SVGImageElement; - result.HTMLVideoElement = item instanceof content.HTMLVideoElement; - result.HTMLAudioElement = item instanceof content.HTMLAudioElement; - - if (isBG) { - // Items that are showing this image as a background - // image might not necessarily have a width or height, - // so we'll dynamically generate an image and send up the - // natural dimensions. - let img = content.document.createElement("img"); - img.src = url; - result.naturalWidth = img.naturalWidth; - result.naturalHeight = img.naturalHeight; - } else { - // Otherwise, we can use the current width and height - // of the image. - result.width = item.width; - result.height = item.height; - } - - if (item instanceof content.SVGImageElement) { - result.SVGImageElementWidth = item.width.baseVal.value; - result.SVGImageElementHeight = item.height.baseVal.value; - } - - result.baseURI = item.baseURI; - - return result; - }, - - // Other Misc Stuff - // Modified from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html - // parse a node to extract the contents of the node - getValueText: function(node) - { - - let valueText = ""; - - // Form input elements don't generally contain information that is useful to our callers, so return nothing. - if (node instanceof content.HTMLInputElement || - node instanceof content.HTMLSelectElement || - node instanceof content.HTMLTextAreaElement) { - return valueText; - } - - // Otherwise recurse for each child. - let length = node.childNodes.length; - - for (let i = 0; i < length; i++) { - let childNode = node.childNodes[i]; - let nodeType = childNode.nodeType; - - // Text nodes are where the goods are. - if (nodeType == content.Node.TEXT_NODE) { - valueText += " " + childNode.nodeValue; - } - // And elements can have more text inside them. - else if (nodeType == content.Node.ELEMENT_NODE) { - // Images are special, we want to capture the alt text as if the image weren't there. - if (childNode instanceof content.HTMLImageElement) { - valueText += " " + this.getAltText(childNode); - } - else { - valueText += " " + this.getValueText(childNode); - } - } - } - - return this.stripWS(valueText); - }, - - // Copied from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html. - // Traverse the tree in search of an img or area element and grab its alt tag. - getAltText: function(node) - { - let altText = ""; - - if (node.alt) { - return node.alt; - } - let length = node.childNodes.length; - for (let i = 0; i < length; i++) { - if ((altText = this.getAltText(node.childNodes[i]) != undefined)) { // stupid js warning... - return altText; - } - } - return ""; - }, - - // Copied from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html. - // Strip leading and trailing whitespace, and replace multiple consecutive whitespace characters with a single space. - stripWS: function(text) - { - let middleRE = /\s+/g; - let endRE = /(^\s+)|(\s+$)/g; - - text = text.replace(middleRE, " "); - return text.replace(endRE, ""); - } -}; -PageInfoListener.init(); - -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 { - var contentURI = BrowserUtils.makeURI(aWindow.location.href, null, null); - return BrowserUtils.makeURI(attr, aWindow.document.characterSet, contentURI); - } 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: XPCOMUtils.generateQI([Ci.nsIObserver, - Ci.nsISupportsWeakReference]), -}; - -addEventListener("MozApplicationManifest", OfflineApps, false); -addMessageListener("OfflineApps:StartFetching", OfflineApps); |