bug 752666 - provide explicit mechanism to specify URL load target in webapp runtime; r=felipe
authorMyk Melez <myk@mozilla.org>
Mon, 11 Jun 2012 12:22:08 -0700
changeset 96423 fbad18e20935f9843782470fae4353ac6c2874e5
parent 96422 bed9bdc78e8cb79a4f2a2db6bd2627f391d4a7e8
child 96424 a6321d945bf28323499115b05252aa081634fec1
push idunknown
push userunknown
push dateunknown
reviewersfelipe
bugs752666
milestone16.0a1
bug 752666 - provide explicit mechanism to specify URL load target in webapp runtime; r=felipe
webapprt/ContentPolicy.js
webapprt/components.manifest
webapprt/content/webapp.js
--- a/webapprt/ContentPolicy.js
+++ b/webapprt/ContentPolicy.js
@@ -1,60 +1,40 @@
 /* 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/. */
 
+/* We used to use this component to heuristically determine which links to
+ * direct to the user's default browser, but we no longer use that heuristic.
+ * It will come in handy when we implement support for trusted apps, however,
+ * so we left it in (although it is disabled in the manifest).
+*/
+
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
-Cu.import("resource://webapprt/modules/WebappRT.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
-// Allow certain origins to load as top-level documents
-const allowedOrigins = [
-  WebappRT.config.app.origin,
-  "https://browserid.org",
-  "https://www.facebook.com",
-  "https://accounts.google.com",
-  "https://www.google.com",
-  "https://twitter.com",
-  "https://api.twitter.com",
-];
-
 function ContentPolicy() {}
 
 ContentPolicy.prototype = {
   classID: Components.ID("{75acd178-3d5a-48a7-bd92-fba383520ae6}"),
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPolicy]),
 
   shouldLoad: function(contentType, contentLocation, requestOrigin, context, mimeTypeGuess, extra) {
-    // Redirect top-level document loads that aren't special schemes to the
-    // default browser when trying to load pages outside of allowed origins
-    let {prePath, scheme} = contentLocation;
-    if (contentType == Ci.nsIContentPolicy.TYPE_DOCUMENT &&
-        !/^(about|chrome|resource)$/.test(scheme) &&
-        allowedOrigins.indexOf(prePath) == -1) {
-
-      // Send the url to the default browser
-      Cc["@mozilla.org/uriloader/external-protocol-service;1"].
-        getService(Ci.nsIExternalProtocolService).
-        getProtocolHandlerInfo(scheme).
-        launchWithURI(contentLocation);
-
-      // Using window.open will first open then navigate, so explicitly close
-      if (context.currentURI.spec == "about:blank") {
-        context.ownerDocument.defaultView.close();
-      };
-
-      return Ci.nsIContentPolicy.REJECT_SERVER;
-    }
 
     return Ci.nsIContentPolicy.ACCEPT;
+
+    // When rejecting a load due to a content policy violation, use this code
+    // to close the window opened by window.open, if any.
+    //if (context.currentURI.spec == "about:blank") {
+    //  context.ownerDocument.defaultView.close();
+    //};
   },
 
   shouldProcess: function(contentType, contentLocation, requestOrigin, context, mimeType, extra) {
     return Ci.nsIContentPolicy.ACCEPT;
   },
 };
 
 const NSGetFactory = XPCOMUtils.generateNSGetFactory([ContentPolicy]);
--- a/webapprt/components.manifest
+++ b/webapprt/components.manifest
@@ -3,16 +3,20 @@ component {6d69c782-40a3-469b-8bfd-3ee36
 contract @mozilla.org/webapprt/clh;1 {6d69c782-40a3-469b-8bfd-3ee366105a4a} application=webapprt@mozilla.org
 category command-line-handler x-default @mozilla.org/webapprt/clh;1 application=webapprt@mozilla.org
 
 # ContentPermission.js
 component {07ef5b2e-88fb-47bd-8cec-d3b0bef11ac4} ContentPermission.js
 contract @mozilla.org/content-permission/prompt;1 {07ef5b2e-88fb-47bd-8cec-d3b0bef11ac4}
 
 # ContentPolicy.js
-component {75acd178-3d5a-48a7-bd92-fba383520ae6} ContentPolicy.js application=webapprt@mozilla.org
-contract @mozilla.org/webapprt/content-policy;1 {75acd178-3d5a-48a7-bd92-fba383520ae6} application=webapprt@mozilla.org
-category content-policy webapprt-content-policy @mozilla.org/webapprt/content-policy;1 application=webapprt@mozilla.org
+# We used to use this component to heuristically determine which links to
+# direct to the user's default browser, but we no longer use that heuristic.
+# It will come in handy when we implement support for trusted apps, however,
+# so we left it in (although it is disabled here).
+# component {75acd178-3d5a-48a7-bd92-fba383520ae6} ContentPolicy.js application=webapprt@mozilla.org
+# contract @mozilla.org/webapprt/content-policy;1 {75acd178-3d5a-48a7-bd92-fba383520ae6} application=webapprt@mozilla.org
+# category content-policy webapprt-content-policy @mozilla.org/webapprt/content-policy;1 application=webapprt@mozilla.org
 
 # DirectoryProvider.js
 component {e1799fda-4b2f-4457-b671-e0641d95698d} DirectoryProvider.js application=webapprt@mozilla.org
 contract @mozilla.org/webapprt/directory-provider;1 {e1799fda-4b2f-4457-b671-e0641d95698d} application=webapprt@mozilla.org
 category xpcom-directory-providers webapprt-directory-provider @mozilla.org/webapprt/directory-provider;1 application=webapprt@mozilla.org
--- a/webapprt/content/webapp.js
+++ b/webapprt/content/webapp.js
@@ -11,28 +11,66 @@ Cu.import("resource://gre/modules/Servic
 
 function onLoad() {
   window.removeEventListener("load", onLoad, false);
 
   // Set the title of the window to the name of the webapp
   let manifest = WebappRT.config.app.manifest;
   document.documentElement.setAttribute("title", manifest.name);
 
+  // Listen for clicks to redirect <a target="_blank"> to the browser.
+  // This doesn't capture clicks so content can capture them itself and do
+  // something different if it doesn't want the default behavior.
+  document.getElementById("content").addEventListener("click", onContentClick,
+                                                      false, true);
+
   // Only load the webapp on the initially launched main window
   if ("arguments" in window) {
     // Load the webapp's launch URL
     let installRecord = WebappRT.config.app;
     let url = Services.io.newURI(installRecord.origin, null, null);
     if (manifest.launch_path)
       url = Services.io.newURI(manifest.launch_path, null, url);
     document.getElementById("content").setAttribute("src", url.spec);
   }
 }
 window.addEventListener("load", onLoad, false);
 
+/**
+ * Direct a click on <a target="_blank"> to the user's default browser.
+ *
+ * In the long run, it might be cleaner to move this to an extension of
+ * nsIWebBrowserChrome3::onBeforeLinkTraversal.
+ *
+ * @param {DOMEvent} event the DOM event
+ **/
+function onContentClick(event) {
+  let target = event.target;
+
+  if (!(target instanceof HTMLAnchorElement) ||
+      target.getAttribute("target") != "_blank") {
+    return;
+  }
+
+  let uri = Services.io.newURI(target.href,
+                               target.ownerDocument.characterSet,
+                               null);
+
+  // Direct the URL to the browser.
+  Cc["@mozilla.org/uriloader/external-protocol-service;1"].
+    getService(Ci.nsIExternalProtocolService).
+    getProtocolHandlerInfo(uri.scheme).
+    launchWithURI(uri);
+
+  // Prevent the runtime from loading the URL.  We do this after directing it
+  // to the browser to give the runtime a shot at handling the URL if we fail
+  // to direct it to the browser for some reason.
+  event.preventDefault();
+}
+
 #ifdef XP_MACOSX
 // On Mac, we dynamically create the label for the Quit menuitem, using
 // a string property to inject the name of the webapp into it.
 window.addEventListener("load", function onLoadUpdateMenuItems() {
   window.removeEventListener("load", onLoadUpdateMenuItems, false);
   let installRecord = WebappRT.config.app;
   let manifest = WebappRT.config.app.manifest;
   let bundle =