diff options
Diffstat (limited to 'browser/base/content/social-content.js')
-rw-r--r-- | browser/base/content/social-content.js | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/browser/base/content/social-content.js b/browser/base/content/social-content.js new file mode 100644 index 000000000..b5fa6a5c4 --- /dev/null +++ b/browser/base/content/social-content.js @@ -0,0 +1,172 @@ +/* -*- 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 is intended for use by iframes in the share panel. */ + +var {interfaces: Ci, utils: Cu} = Components; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); + +// social frames are always treated as app tabs +docShell.isAppTab = true; + +addEventListener("DOMContentLoaded", function(event) { + if (event.target != content.document) + return; + // Some share panels (e.g. twitter and facebook) check content.opener, and if + // it doesn't exist they act like they are in a browser tab. We want them to + // act like they are in a dialog (which is the typical case). + if (content && !content.opener) { + content.opener = content; + } + hookWindowClose(); + disableDialogs(); +}); + +addMessageListener("Social:OpenGraphData", (message) => { + let ev = new content.CustomEvent("OpenGraphData", { detail: JSON.stringify(message.data) }); + content.dispatchEvent(ev); +}); + +addMessageListener("Social:ClearFrame", () => { + docShell.createAboutBlankContentViewer(null); +}); + +addEventListener("DOMWindowClose", (evt) => { + // preventDefault stops the default window.close() function being called, + // which doesn't actually close anything but causes things to get into + // a bad state (an internal 'closed' flag is set and debug builds start + // asserting as the window is used.). + // None of the windows we inject this API into are suitable for this + // default close behaviour, so even if we took no action above, we avoid + // the default close from doing anything. + evt.preventDefault(); + + // Tells the SocialShare class to close the panel + sendAsyncMessage("Social:DOMWindowClose"); +}); + +function hookWindowClose() { + // Allow scripts to close the "window". Because we are in a panel and not + // in a full dialog, the DOMWindowClose listener above will only receive the + // event if we do this. + let dwu = content.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + dwu.allowScriptsToClose(); +} + +function disableDialogs() { + let windowUtils = content.QueryInterface(Ci.nsIInterfaceRequestor). + getInterface(Ci.nsIDOMWindowUtils); + windowUtils.disableDialogs(); +} + +// Error handling class used to listen for network errors in the social frames +// and replace them with a social-specific error page +const SocialErrorListener = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMEventListener, + Ci.nsIWebProgressListener, + Ci.nsISupportsWeakReference, + Ci.nsISupports]), + + defaultTemplate: "about:socialerror?mode=tryAgainOnly&url=%{url}&origin=%{origin}", + urlTemplate: null, + + init() { + addMessageListener("Social:SetErrorURL", this); + let webProgress = docShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor) + .getInterface(Components.interfaces.nsIWebProgress); + webProgress.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_STATE_REQUEST | + Ci.nsIWebProgress.NOTIFY_LOCATION); + }, + + receiveMessage(message) { + switch (message.name) { + case "Social:SetErrorURL": + // Either a url or null to reset to default template. + this.urlTemplate = message.data.template; + break; + } + }, + + setErrorPage() { + // if this is about:providerdirectory, use the directory iframe + let frame = docShell.chromeEventHandler; + let origin = frame.getAttribute("origin"); + let src = frame.getAttribute("src"); + if (src == "about:providerdirectory") { + frame = content.document.getElementById("activation-frame"); + src = frame.getAttribute("src"); + } + + let url = this.urlTemplate || this.defaultTemplate; + url = url.replace("%{url}", encodeURIComponent(src)); + url = url.replace("%{origin}", encodeURIComponent(origin)); + if (frame != docShell.chromeEventHandler) { + // Unable to access frame.docShell here. This is our own frame and doesn't + // provide reload, so we'll just set the src. + frame.setAttribute("src", url); + } else { + let webNav = docShell.QueryInterface(Ci.nsIWebNavigation); + webNav.loadURI(url, null, null, null, null); + } + sendAsyncMessage("Social:ErrorPageNotify", { + origin: origin, + url: src + }); + }, + + onStateChange(aWebProgress, aRequest, aState, aStatus) { + let failure = false; + if ((aState & Ci.nsIWebProgressListener.STATE_IS_REQUEST)) + return; + if ((aState & Ci.nsIWebProgressListener.STATE_STOP)) { + if (aRequest instanceof Ci.nsIHttpChannel) { + try { + // Change the frame to an error page on 4xx (client errors) + // and 5xx (server errors). responseStatus throws if it is not set. + failure = aRequest.responseStatus >= 400 && + aRequest.responseStatus < 600; + } catch (e) { + failure = aStatus != Components.results.NS_OK; + } + } + } + + // Calling cancel() will raise some OnStateChange notifications by itself, + // so avoid doing that more than once + if (failure && aStatus != Components.results.NS_BINDING_ABORTED) { + // if tp is enabled and we get a failure, ignore failures (ie. STATE_STOP) + // on child resources since they *may* have been blocked. We don't have an + // easy way to know if a particular url is blocked by TP, only that + // something was. + if (docShell.hasTrackingContentBlocked) { + let frame = docShell.chromeEventHandler; + let src = frame.getAttribute("src"); + if (aRequest && aRequest.name != src) { + Cu.reportError("SocialErrorListener ignoring blocked content error for " + aRequest.name); + return; + } + } + + aRequest.cancel(Components.results.NS_BINDING_ABORTED); + this.setErrorPage(); + } + }, + + onLocationChange(aWebProgress, aRequest, aLocation, aFlags) { + if (aRequest && aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE) { + aRequest.cancel(Components.results.NS_BINDING_ABORTED); + this.setErrorPage(); + } + }, + + onProgressChange() {}, + onStatusChange() {}, + onSecurityChange() {}, +}; + +SocialErrorListener.init(); |