Fix web-based protocol handlers not working from Send Link and certain other situations (bug 394483), r=biesi, r=gavin (browser portion), sr=sicking, a=beta blocker
authordmose@mozilla.org
Tue, 23 Oct 2007 11:20:56 -0700
changeset 7104 c3a3944ce9c4b1778b06e076bd804699f59ea6c8
parent 7103 01e36cc48baf385f3c626e0eec6b513947cd1247
child 7105 ce6093b9f6625abba78ca0222223e899e8a0b8ad
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbiesi, gavin, sicking, beta
bugs394483
milestone1.9a9pre
Fix web-based protocol handlers not working from Send Link and certain other situations (bug 394483), r=biesi, r=gavin (browser portion), sr=sicking, a=beta blocker
browser/base/content/browser.js
netwerk/mime/public/nsIMIMEInfo.idl
uriloader/exthandler/nsWebHandlerApp.js
uriloader/exthandler/tests/mochitest/handlerApp.xhtml
uriloader/exthandler/tests/mochitest/handlerApps.js
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -3936,16 +3936,20 @@ nsBrowserAccess.prototype =
     var referrer = null;
     var isExternal = (aContext == Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
 
     if (isExternal && aURI && aURI.schemeIs("chrome")) {
       dump("use -chrome command-line option to load external chrome urls\n");
       return null;
     }
 
+    if (!gPrefService)
+      gPrefService = Components.classes["@mozilla.org/preferences-service;1"]
+                               .getService(Components.interfaces.nsIPrefBranch2);
+
     var loadflags = isExternal ?
                        Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL :
                        Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
     var location;
     if (aWhere == Ci.nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW) {
       switch (aContext) {
         case Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL :
           aWhere = gPrefService.getIntPref("browser.link.open_external");
--- a/netwerk/mime/public/nsIMIMEInfo.idl
+++ b/netwerk/mime/public/nsIMIMEInfo.idl
@@ -109,22 +109,19 @@ interface nsIHandlerInfo : nsISupports {
      * and our code will not make any decision based on the content-type or
      * extension, though the invoked file: handler is free to do so. 
      *
      * @param aURI
      *        The URI to launch this application with
      *
      * @param aWindowContext 
      *        The window to parent the dialog against, and, if a web handler
-     *        is chosen, it is loaded in this window as well.  This parameter
-     *        may be ultimately passed nsIURILoader.openURI in the case of a
-     *        web handler, and aWindowContext is null or not present, web
-     *        handlers will fail.  We need to do better than that; bug 394483
-     *        filed in order to track.
-     *       
+     *        is chosen, it is loaded in this window as well.  See 
+     *        nsIHandlerApp.launchWithURI for more details.
+     *
      * @throw NS_ERROR_INVALID_ARG if preferredAction is not valid for this
      * call. Other exceptions may be thrown.
      */
     void launchWithURI(in nsIURI aURI, 
                        [optional] in nsIInterfaceRequestor aWindowContext);
 
     /**
      * preferredAction is how the user specified they would like to handle
@@ -274,19 +271,26 @@ interface nsIHandlerApp : nsISupports {
 
     /**
      * Launches the application with the specified URI.
      *
      * @param aURI
      *        The URI to launch this application with
      *
      * @param aWindowContext 
-     *        Required for web handlers; is passed through to 
-     *        nsIURILoader.openURI, but see bug 394483 for info on 
-     *        how this needs to evolve.
+     *
+     *        Currently only relevant to web-handler apps.  If given, this
+     *        represents the docshell to load the handler in and is passed
+     *        through to nsIURILoader.openURI.  If this parameter is null or
+     *        not present, the web handler app implementation will attempt to 
+     *        find/create a place to load the handler and do so.  As of this
+     *        writing, it tries to load the web handler in a new window using
+     *        nsIBrowserDOMWindow.openURI.  In the future, it may attempt to 
+     *        have a more comprehensive strategy which could include handing
+     *        off to the system default browser (bug 394479).
      */
     void launchWithURI(in nsIURI aURI, 
                        [optional] in nsIInterfaceRequestor aWindowContext);
 
 };
 
 /**
  * nsILocalHandlerApp is a local OS-level executable
--- a/uriloader/exthandler/nsWebHandlerApp.js
+++ b/uriloader/exthandler/nsWebHandlerApp.js
@@ -39,16 +39,17 @@
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Constants
 
 const Ci = Components.interfaces;
 const Cr = Components.results;
+const Cc = Components.classes;
 
 ////////////////////////////////////////////////////////////////////////////////
 //// nsWebHandler class
 
 function nsWebHandlerApp() {}
 
 nsWebHandlerApp.prototype = {
   //////////////////////////////////////////////////////////////////////////////
@@ -90,35 +91,72 @@ nsWebHandlerApp.prototype = {
     // XXX need to strip passwd & username from URI to handle, as per the
     // WhatWG HTML5 draft.  nsSimpleURL, which is what we're going to get,
     // can't do this directly.  Ideally, we'd fix nsStandardURL to make it
     // possible to turn off all of its quirks handling, and use that...
 
     // encode the URI to be handled
     var escapedUriSpecToHandle = encodeURIComponent(aURI.spec);
 
-    // insert the encoded URI 
-    var uriToSend = this.uriTemplate.replace("%s", escapedUriSpecToHandle);
+    // insert the encoded URI and create the object version 
+    var uriSpecToSend = this.uriTemplate.replace("%s", escapedUriSpecToHandle);
+    var ioService = Cc["@mozilla.org/network/io-service;1"].
+                    getService(Ci.nsIIOService);
+    var uriToSend = ioService.newURI(uriSpecToSend, null, null);
+    
+    // if we have a window context, use the URI loader to load there
+    if (aWindowContext) {
 
-    // create a channel from this URI
-    var ioService = Components.classes["@mozilla.org/network/io-service;1"].
-                    getService(Components.interfaces.nsIIOService);
-    var channel = ioService.newChannel(uriToSend, null, null);
-    channel.loadFlags = Components.interfaces.nsIChannel.LOAD_DOCUMENT_URI;
+      // create a channel from this URI
+      var channel = ioService.newChannelFromURI(uriToSend);
+      channel.loadFlags = Ci.nsIChannel.LOAD_DOCUMENT_URI;
+
+      // load the channel
+      var uriLoader = Cc["@mozilla.org/uriloader;1"].
+                      getService(Ci.nsIURILoader);
+      // XXX ideally, aIsContentPreferred (the second param) should really be
+      // passed in from above.  Practically, true is probably a reasonable
+      // default since browsers don't care much, and link click is likely to be
+      // the more interesting case for non-browser apps.  See 
+      // <https://bugzilla.mozilla.org/show_bug.cgi?id=392957#c9> for details.
+      uriLoader.openURI(channel, true, aWindowContext);
+      return;
+    } 
 
-    // load the channel
-    var uriLoader = Components.classes["@mozilla.org/uriloader;1"].
-                    getService(Components.interfaces.nsIURILoader);
-    // XXX ideally, aIsContentPreferred (the second param) should really be
-    // passed in from above.  Practically, true is probably a reasonable
-    // default since browsers don't care much, and link click is likely to be
-    // the more interesting case for non-browser apps.  See 
-    // <https://bugzilla.mozilla.org/show_bug.cgi?id=392957#c9> for details.
-    uriLoader.openURI(channel, true, aWindowContext);
+    // since we don't have a window context, hand it off to a browser
+    var windowMediator = Cc["@mozilla.org/appshell/window-mediator;1"].
+      getService(Ci.nsIWindowMediator);
+
+    // get browser dom window
+    var browserDOMWin = windowMediator.getMostRecentWindow("navigator:browser")
+                        .QueryInterface(Ci.nsIDOMChromeWindow)
+                        .browserDOMWindow;
 
+    // if we got an exception, there are several possible reasons why:
+    // a) this gecko embedding doesn't provide an nsIBrowserDOMWindow
+    //    implementation (i.e. doesn't support browser-style functionality),
+    //    so we need to kick the URL out to the OS default browser.  This is
+    //    the subject of bug 394479.
+    // b) this embedding does provide an nsIBrowserDOMWindow impl, but
+    //    there doesn't happen to be a browser window open at the moment; one
+    //    should be opened.  It's not clear whether this situation will really
+    //    ever occur in real life.  If it does, the only API that I can find
+    //    that seems reasonably likely to work for most embedders is the
+    //    command line handler.  
+    // c) something else went wrong 
+    //
+    // it's not clear how one would differentiate between the three cases
+    // above, so for now we don't catch the exception
+
+    // openURI
+    browserDOMWin.openURI(uriToSend,
+                          null, // no window.opener 
+                          Ci.nsIBrowserDOMWindow.OPEN_DEFAULT_WINDOW,
+                          Ci.nsIBrowserDOMWindow.OPEN_NEW);
+      
     return;
   },
 
   //////////////////////////////////////////////////////////////////////////////
   //// nsIWebHandlerApp
 
   get uriTemplate() {
     return this._uriTemplate;
--- a/uriloader/exthandler/tests/mochitest/handlerApp.xhtml
+++ b/uriloader/exthandler/tests/mochitest/handlerApp.xhtml
@@ -7,21 +7,27 @@
 </head>
 <body onload="onLoad()">
 Pseudo Web Handler App
 
 <script class="testbody" type="text/javascript">
 <![CDATA[
 function onLoad() {
 
-	var originalUriToHandle = encodeURIComponent(window.opener.testURI); 
-    
-  window.opener.is(location.search, "?uri=" + originalUriToHandle,
-                   "uri passed to web-handler app");
-  window.opener.SimpleTest.finish() 
+  // if we have a window.opener, this must be the windowContext
+  // instance of this test.  check that we got the URI right and clean up.
+  if (window.opener) {
+    window.opener.is(location.search, 
+                     "?uri=" + encodeURIComponent(window.opener.testURI), 
+                     "uri passed to web-handler app");
+    window.opener.SimpleTest.finish();
+  } 
+
+  // ensure that we actually have the privs to close non-script-opened windows
+  netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); 
   window.close();
 }
 ]]>
 </script>
 
 </body>
 </html>
 
--- a/uriloader/exthandler/tests/mochitest/handlerApps.js
+++ b/uriloader/exthandler/tests/mochitest/handlerApps.js
@@ -60,19 +60,25 @@ function test() {
   var newWindow = window.open("", "handlerWindow", "height=300,width=300");
   var windowContext = 
     newWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor). 
     getInterface(Components.interfaces.nsIWebNavigation).
     QueryInterface(Components.interfaces.nsIDocShell);
  
   webHandler.launchWithURI(uri, windowContext); 
 
+  // if we get this far without an exception, we've at least partly passed
+  // (remaining check in handlerApp.xhtml)
+  ok(true, "webHandler launchWithURI (existing window/tab) started");
+
+  // make the web browser launch in its own window/tab
+  webHandler.launchWithURI(uri);
+  
   // if we get this far without an exception, we've passed
-  ok(true, "webHandler launchWithURI test");
-
+  ok(true, "webHandler launchWithURI (new window/tab) test started");
 
   // set up the local handler object
   var localHandler = 
     Components.classes["@mozilla.org/uriloader/local-handler-app;1"].
     createInstance(Components.interfaces.nsILocalHandlerApp);
   localHandler.name = "Test Local Handler App";
   
   // get a local app that we know will be there and do something sane