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 101708 fbad18e20935f9843782470fae4353ac6c2874e5
parent 101707 bed9bdc78e8cb79a4f2a2db6bd2627f391d4a7e8
child 101709 a6321d945bf28323499115b05252aa081634fec1
push id191
push userlsblakk@mozilla.com
push dateFri, 05 Oct 2012 17:12:53 +0000
treeherdermozilla-release@ddb22ac6c03b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfelipe
bugs752666
milestone16.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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 =