bug 385740: support multiple apps per nsIHandlerInfo via a new nsIHandlerInfo::possibleApplicationHandlers attribute; r=biesi, sr=dmose
authormyk@mozilla.org
Mon, 13 Aug 2007 13:41:34 -0700
changeset 4575 6b326374ea3947b28c7c92c95c6809effe85d77e
parent 4574 13fe21f354eae59fe348f503b568f9484a39d279
child 4576 68b712189a94842ed9fab098465d65c8a0f5d1d6
push id1
push userbsmedberg@mozilla.com
push dateThu, 20 Mar 2008 16:49:24 +0000
treeherdermozilla-central@61007906a1f8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbiesi, dmose
bugs385740
milestone1.9a8pre
bug 385740: support multiple apps per nsIHandlerInfo via a new nsIHandlerInfo::possibleApplicationHandlers attribute; r=biesi, sr=dmose
netwerk/mime/public/nsIMIMEInfo.idl
uriloader/exthandler/nsExternalHelperAppService.cpp
uriloader/exthandler/nsExternalHelperAppService.h
uriloader/exthandler/nsHelperAppRDF.h
uriloader/exthandler/nsMIMEInfoImpl.cpp
uriloader/exthandler/nsMIMEInfoImpl.h
--- a/netwerk/mime/public/nsIMIMEInfo.idl
+++ b/netwerk/mime/public/nsIMIMEInfo.idl
@@ -37,24 +37,25 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 
 interface nsIURI;
 interface nsIFile;
 interface nsIUTF8StringEnumerator;
 interface nsIHandlerApp;
+interface nsIMutableArray;
 
 typedef long nsHandlerInfoAction;
 
 /**
  * nsIHandlerInfo gives access to the information about how a given protocol
  * scheme or MIME-type is handled.
  */
-[scriptable, uuid(51ddc1c5-7077-4c58-a5bd-327d00777b46)]
+[scriptable, uuid(4c7f5603-cfa9-4576-a769-c3343cb0135b)]
 interface nsIHandlerInfo : nsISupports {
     /**
      * The type of this handler info.  For MIME handlers, this is the MIME type.
      * For protocol handlers, it's the scheme.
      * 
      * @return String representing the type.
      */
     readonly attribute ACString type;
@@ -66,16 +67,27 @@ interface nsIHandlerInfo : nsISupports {
 
     /**
      * The application the user has said they want associated with this content
      * type. This is not always guaranteed to be set!!
      */
     attribute nsIHandlerApp preferredApplicationHandler;
 
     /**
+     * Applications that can handle this content type.
+     *
+     * The list will include the preferred handler, if any.  Elements of this
+     * array are nsIHandlerApp objects, and this attribute will always reference
+     * an array, whether or not there are any possible handlers.  If there are
+     * no possible handlers, the array will contain no elements, so just check
+     * its length (nsIArray::length) to see if there are any possible handlers.
+     */
+    readonly attribute nsIMutableArray possibleApplicationHandlers;
+
+    /**
      * Indicates whether a default application handler exists,
      * i.e. whether launchWithFile with action = useSystemDefault is possible
      * and defaultDescription will contain usable information.
      */
     readonly attribute boolean hasDefaultHandler;
 
     /**
      * A pretty name description of the associated default application. Only
--- a/uriloader/exthandler/nsExternalHelperAppService.cpp
+++ b/uriloader/exthandler/nsExternalHelperAppService.cpp
@@ -60,16 +60,17 @@
 #include "nsIWebProgressListener.h"
 #include "nsITransfer.h"
 #include "nsReadableUtils.h"
 #include "nsIRequest.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsThreadUtils.h"
 #include "nsAutoPtr.h"
+#include "nsIMutableArray.h"
 
 // used to manage our in memory data source of helper applications
 #ifdef MOZ_RDF
 #include "nsRDFCID.h"
 #include "rdf.h"
 #include "nsIRDFService.h"
 #include "nsIRDFRemoteDataSource.h"
 #include "nsHelperAppRDF.h"
@@ -565,16 +566,18 @@ nsresult nsExternalHelperAppService::Ini
     rdf->GetResource(NS_LITERAL_CSTRING(NC_RDF_SAVETODISK),
                      getter_AddRefs(kNC_SaveToDisk));
     rdf->GetResource(NS_LITERAL_CSTRING(NC_RDF_USESYSTEMDEFAULT),
                      getter_AddRefs(kNC_UseSystemDefault));
     rdf->GetResource(NS_LITERAL_CSTRING(NC_RDF_HANDLEINTERNAL),
                      getter_AddRefs(kNC_HandleInternal));
     rdf->GetResource(NS_LITERAL_CSTRING(NC_RDF_ALWAYSASK),
                      getter_AddRefs(kNC_AlwaysAsk));  
+    rdf->GetResource(NS_LITERAL_CSTRING(NC_RDF_POSSIBLEAPPLICATION),
+                     getter_AddRefs(kNC_PossibleApplication));
     rdf->GetResource(NS_LITERAL_CSTRING(NC_RDF_PRETTYNAME),
                      getter_AddRefs(kNC_PrettyName));
     rdf->GetResource(NS_LITERAL_CSTRING(NC_RDF_URITEMPLATE),
                      getter_AddRefs(kNC_UriTemplate));
      
   }
   
   mDataSourceInitialized = PR_TRUE;
@@ -762,17 +765,19 @@ nsresult nsExternalHelperAppService::Fil
   
       fileExtensions->HasMoreElements(&hasMoreElements);
     } // while we have more extensions to parse....
   }
 
   return rv;
 }
 
-nsresult nsExternalHelperAppService::FillLiteralValueFromTarget(nsIRDFResource * aSource, nsIRDFResource * aProperty, const PRUnichar ** aLiteralValue)
+nsresult nsExternalHelperAppService::FillLiteralValueFromTarget(
+  nsIRDFResource * aSource, nsIRDFResource * aProperty,
+  const PRUnichar ** aLiteralValue)
 {
   nsresult rv = NS_OK;
   nsCOMPtr<nsIRDFLiteral> literal;
   nsCOMPtr<nsIRDFNode> target;
 
   *aLiteralValue = nsnull;
   rv = InitDataSource();
   if (NS_FAILED(rv)) return rv;
@@ -786,16 +791,95 @@ nsresult nsExternalHelperAppService::Fil
     literal->GetValueConst(aLiteralValue);
   }
   else
     rv = NS_ERROR_FAILURE;
 
   return rv;
 }
 
+nsresult nsExternalHelperAppService::FillHandlerAppFromSource(
+  nsIRDFResource * aSource, nsIHandlerApp ** aHandlerApp)
+{
+  nsresult rv = NS_OK;
+
+  const PRUnichar * appName = nsnull;
+  FillLiteralValueFromTarget(aSource, kNC_PrettyName, &appName);
+
+  // if we've got a path name, this must be a local app
+  const PRUnichar * path = nsnull;
+  FillLiteralValueFromTarget(aSource, kNC_Path, &path);
+  if (path && path[0])
+  {
+    nsCOMPtr<nsIFile> application;
+    GetFileTokenForPath(path, getter_AddRefs(application));
+    if (application) {
+      nsLocalHandlerApp *handlerApp(new nsLocalHandlerApp(appName, application));
+      if (!handlerApp) { 
+        return NS_ERROR_OUT_OF_MEMORY;
+      }
+      NS_ADDREF(*aHandlerApp = handlerApp);
+    }
+  } else {
+    // if we got here, there's no path name in the RDF graph, so this must 
+    // be a web app
+    const PRUnichar * uriTemplate = nsnull;
+    FillLiteralValueFromTarget(aSource, kNC_UriTemplate, &uriTemplate);
+    if (uriTemplate && uriTemplate[0]) {
+      nsWebHandlerApp *handlerApp(new nsWebHandlerApp(appName, 
+        NS_ConvertUTF16toUTF8(uriTemplate)));
+      if (!handlerApp) {
+          return NS_ERROR_OUT_OF_MEMORY;
+      }
+      NS_ADDREF(*aHandlerApp = handlerApp);
+    } else {
+      return NS_ERROR_FAILURE; // no path name _and_ no uri template
+    }
+  }
+
+  return rv;
+}
+
+nsresult nsExternalHelperAppService::FillPossibleAppsFromSource(
+  nsIRDFResource * aSource, nsIMutableArray * aPossibleApps)
+{
+  nsresult rv = NS_OK;
+
+  rv = InitDataSource();
+  if (NS_FAILED(rv)) return rv;
+
+  nsCOMPtr<nsISimpleEnumerator> possibleAppResources;
+  rv = mOverRideDataSource->GetTargets(aSource, kNC_PossibleApplication, PR_TRUE,
+                                       getter_AddRefs(possibleAppResources));
+  if (NS_FAILED(rv)) return rv;
+
+  PRBool more;
+  nsCOMPtr<nsISupports> supports;
+  while (NS_SUCCEEDED(possibleAppResources->HasMoreElements(&more)) && more) {
+    rv = possibleAppResources->GetNext(getter_AddRefs(supports));
+    if (NS_FAILED(rv)) return rv;
+
+    nsCOMPtr<nsIRDFResource> possibleAppResource = do_QueryInterface(supports);
+
+    if (possibleAppResource) {
+      nsCOMPtr<nsIHandlerApp> possibleApp;
+      rv = FillHandlerAppFromSource(possibleAppResource, getter_AddRefs(possibleApp));
+      // It's ok if we failed to get one of the possible apps, as some of the
+      // others might still be good, so merely continue on failure.
+      if (NS_FAILED(rv)) continue;
+
+      rv = aPossibleApps->AppendElement(possibleApp, PR_FALSE);
+      if (NS_FAILED(rv)) return rv;
+    }
+  }
+
+  return NS_OK;
+}
+
+
 nsresult nsExternalHelperAppService::FillContentHandlerProperties(
   const char * aContentType, const char * aTypeNodePrefix,
   nsIRDFService * aRDFService, nsIHandlerInfo * aHandlerInfo)
 {
   nsCOMPtr<nsIRDFNode> target;
   nsCOMPtr<nsIRDFLiteral> literal;
   const PRUnichar * stringValue = nsnull;
   nsresult rv = NS_OK;
@@ -831,68 +915,51 @@ nsresult nsExternalHelperAppService::Fil
   if (stringValue && trueString.Equals(stringValue))
        aHandlerInfo->SetPreferredAction(nsIHandlerInfo::handleInternally);
   
   // always ask
   FillLiteralValueFromTarget(contentTypeHandlerNodeResource,kNC_AlwaysAsk, &stringValue);
   // Only skip asking if we are absolutely sure the user does not want
   // to be asked.  Any sort of bofus data should mean we ask.
   aHandlerInfo->SetAlwaysAskBeforeHandling(!stringValue ||
-                                        !falseString.Equals(stringValue));
+                                           !falseString.Equals(stringValue));
 
 
   // now digest the external application information
 
+  // Clear out any possibly set applications to match the datasource.
+  aHandlerInfo->SetPreferredApplicationHandler(nsnull);
+
+  // Get the preferred application, if any.
   nsCAutoString externalAppNodeName(aTypeNodePrefix);
   externalAppNodeName.AppendLiteral(NC_EXTERNALAPP_SUFFIX);
   externalAppNodeName.Append(aContentType);
   nsCOMPtr<nsIRDFResource> externalAppNodeResource;
   aRDFService->GetResource(externalAppNodeName, getter_AddRefs(externalAppNodeResource));
 
-  // Clear out any possibly set preferred application, to match the datasource
-  aHandlerInfo->SetPreferredApplicationHandler(nsnull);
   if (externalAppNodeResource)
   {
-    const PRUnichar * appName;
-    FillLiteralValueFromTarget(externalAppNodeResource, kNC_PrettyName,
-                               &appName);
-
-    // if we've got a path name, this must be a local app
-    FillLiteralValueFromTarget(externalAppNodeResource, kNC_Path, &stringValue);
-    if (stringValue && stringValue[0])
-    {
-      nsCOMPtr<nsIFile> application;
-      GetFileTokenForPath(stringValue, getter_AddRefs(application));
-      if (application) {
-        nsLocalHandlerApp *handlerApp(new nsLocalHandlerApp(appName, application));
-        if (!handlerApp) { 
-          return NS_ERROR_OUT_OF_MEMORY;
-        }
-
-        return aHandlerInfo->SetPreferredApplicationHandler(handlerApp);
-      }
-    } else {
-      // maybe we've get a uri template, which would indicate that this is a
-      // web-handler
-      FillLiteralValueFromTarget(externalAppNodeResource, kNC_UriTemplate, 
-                                 &stringValue);
-      if (stringValue && stringValue[0]) {
-        nsWebHandlerApp *handlerApp(new nsWebHandlerApp(appName, 
-          NS_ConvertUTF16toUTF8(stringValue)));
-        
-        if (!handlerApp) {
-            return NS_ERROR_OUT_OF_MEMORY;
-        }
-        return aHandlerInfo->SetPreferredApplicationHandler(handlerApp);
-      }
-    // otherwise, no handler at all; fall through to return NS_OK
-    }
+    nsCOMPtr<nsIHandlerApp> preferredApp;
+    rv = FillHandlerAppFromSource(externalAppNodeResource, getter_AddRefs(preferredApp));
+
+    if (NS_SUCCEEDED(rv))
+      aHandlerInfo->SetPreferredApplicationHandler(preferredApp);
   }
 
-  return NS_OK;
+  // Get the possible applications, if any.
+  nsCOMPtr<nsIMutableArray> possibleApps;
+  rv = aHandlerInfo->GetPossibleApplicationHandlers(getter_AddRefs(possibleApps));
+  if (NS_FAILED(rv)) return rv;
+
+  // Clear out any possibly set applications to match the datasource.
+  possibleApps->Clear();
+
+  rv = FillPossibleAppsFromSource(contentTypeHandlerNodeResource, possibleApps);
+
+  return rv;
 }
 #endif /* MOZ_RDF */
 
 PRBool nsExternalHelperAppService::MIMETypeIsInDataSource(const char * aContentType)
 {
 #ifdef MOZ_RDF
   nsresult rv = InitDataSource();
   if (NS_FAILED(rv)) return PR_FALSE;
--- a/uriloader/exthandler/nsExternalHelperAppService.h
+++ b/uriloader/exthandler/nsExternalHelperAppService.h
@@ -247,27 +247,28 @@ protected:
   nsCOMPtr<nsIRDFResource> kNC_FileExtensions;
   nsCOMPtr<nsIRDFResource> kNC_Path;
   nsCOMPtr<nsIRDFResource> kNC_UseSystemDefault;
   nsCOMPtr<nsIRDFResource> kNC_SaveToDisk;
   nsCOMPtr<nsIRDFResource> kNC_AlwaysAsk;
   nsCOMPtr<nsIRDFResource> kNC_HandleInternal;
   nsCOMPtr<nsIRDFResource> kNC_PrettyName;
   nsCOMPtr<nsIRDFResource> kNC_UriTemplate;
+  nsCOMPtr<nsIRDFResource> kNC_PossibleApplication;
 #endif
 
   /**
    * Whether mOverRideDataSource is initialized
    */
   PRBool mDataSourceInitialized;
 
   /**
-   * Helper routines for digesting the data source and filling in a mime info
-   * object for a given content type inside that data source.
-   * The content type of the MIME Info will not be changed.
+   * Helper routines for digesting the data source and filling in a handler
+   * info object for a given content type inside that data source. The content
+   * type of the handler info object will not be changed.
    */
 #ifdef MOZ_RDF
   NS_HIDDEN_(nsresult) FillMIMEExtensionProperties(
     nsIRDFResource * aContentTypeNodeResource, nsIRDFService * aRDFService,
     nsIMIMEInfo * aMIMEInfo);
   
   /**
    * @see FillMIMEExtensionProperties
@@ -280,16 +281,29 @@ protected:
   /**
    * A small helper function which gets the target for a given source and
    * property. QIs to a literal and returns a CONST ptr to the string value
    * of that target
    */
   NS_HIDDEN_(nsresult) FillLiteralValueFromTarget(nsIRDFResource * aSource,
                                                   nsIRDFResource * aProperty,
                                                   const PRUnichar ** aLiteralValue);
+
+  /**
+   * Returns the nsIHandlerApp represented by the source node.
+   */
+  NS_HIDDEN_(nsresult) FillHandlerAppFromSource(nsIRDFResource * aSource,
+                                                nsIHandlerApp ** aHandlerApp);
+
+  /**
+   * Returns an array of nsIHandlerApp objects representing possible apps
+   * for the handler represented by the source node.
+   */
+  NS_HIDDEN_(nsresult) FillPossibleAppsFromSource(nsIRDFResource * aSource,
+                                                  nsIMutableArray * aPossibleApps);
 #endif
 
   /**
    * Searches the "extra" array of MIMEInfo objects for an object
    * with a specific type. If found, it will modify the passed-in
    * MIMEInfo. Otherwise, it will return an error and the MIMEInfo
    * will be untouched.
    * @param aContentType The type to search for.
--- a/uriloader/exthandler/nsHelperAppRDF.h
+++ b/uriloader/exthandler/nsHelperAppRDF.h
@@ -53,30 +53,32 @@
 #define NC_RDF_LARGEICON				NC_NAMESPACE_URI"largeIcon"
 #define NC_RDF_SMALLICON				NC_NAMESPACE_URI"smallIcon"
 #define NC_RDF_HANDLER				  NC_NAMESPACE_URI"handler"
 #define NC_RDF_FILEEXTENSIONS	  NC_NAMESPACE_URI"fileExtensions"
 #define NC_RDF_CHILD            NC_NAMESPACE_URI"child"
 #define NC_RDF_ROOT 			      "NC:HelperAppRoot"
 #define NC_CONTENT_NODE_PREFIX  "urn:mimetype:"
 #define NC_HANDLER_SUFFIX "handler:"
+// This should be called preferredApplication to distinguish it from the larger
+// set of possible external applications, but it's too late to change this now.
 #define NC_EXTERNALAPP_SUFFIX "externalApplication:"
 
 // for URI schemes.  We re-use NC_RDF_HANDLER as an arc from these nodes.
 #define NC_SCHEME_NODE_PREFIX "urn:scheme:"
 #define NS_RDF_PROTOCOLSCHEMES NC_NAMESPACE_URI"Protocol-Schemes"
 
 // File Extensions have file extension properties....
 #define NC_RDF_FILEEXTENSION    NC_NAMESPACE_URI"fileExtension"
 
 // handler properties
-#define NC_RDF_SAVETODISK				    NC_NAMESPACE_URI"saveToDisk"
+#define NC_RDF_SAVETODISK           NC_NAMESPACE_URI"saveToDisk"
 #define NC_RDF_USESYSTEMDEFAULT     NC_NAMESPACE_URI"useSystemDefault"
 #define NC_RDF_HANDLEINTERNAL       NC_NAMESPACE_URI"handleInternal"
 #define NC_RDF_ALWAYSASK            NC_NAMESPACE_URI"alwaysAsk"
-#define NC_RDF_EXTERNALAPPLICATION  NC_NAMESPACE_URI"externalApplication"
+#define NC_RDF_POSSIBLEAPPLICATION  NC_NAMESPACE_URI"possibleApplication"
 
 // external applications properties....
 #define NC_RDF_PRETTYNAME 			    NC_NAMESPACE_URI"prettyName"
 // for local apps, we'll have the path (but not uriTemplate)
 #define NC_RDF_PATH 			          NC_NAMESPACE_URI"path"
 // for web apps, we'll have the uriTemplate (but not path)
 #define NC_RDF_URITEMPLATE NC_NAMESPACE_URI"uriTemplate"  
--- a/uriloader/exthandler/nsMIMEInfoImpl.cpp
+++ b/uriloader/exthandler/nsMIMEInfoImpl.cpp
@@ -282,16 +282,30 @@ nsMIMEInfoBase::GetPreferredApplicationH
 NS_IMETHODIMP
 nsMIMEInfoBase::SetPreferredApplicationHandler(nsIHandlerApp * aPreferredAppHandler)
 {
   mPreferredApplication = aPreferredAppHandler;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsMIMEInfoBase::GetPossibleApplicationHandlers(nsIMutableArray ** aPossibleAppHandlers)
+{
+  if (!mPossibleApplications)
+    mPossibleApplications = do_CreateInstance(NS_ARRAY_CONTRACTID);
+
+  if (!mPossibleApplications)
+    return NS_ERROR_OUT_OF_MEMORY;
+
+  *aPossibleAppHandlers = mPossibleApplications;
+  NS_IF_ADDREF(*aPossibleAppHandlers);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsMIMEInfoBase::GetPreferredAction(nsHandlerInfoAction * aPreferredAction)
 {
   *aPreferredAction = mPreferredAction;
   return NS_OK;
 }
  
 NS_IMETHODIMP
 nsMIMEInfoBase::SetPreferredAction(nsHandlerInfoAction aPreferredAction)
--- a/uriloader/exthandler/nsMIMEInfoImpl.h
+++ b/uriloader/exthandler/nsMIMEInfoImpl.h
@@ -38,16 +38,17 @@
  * ***** END LICENSE BLOCK ***** */
 #ifndef __nsmimeinfoimpl_h___
 #define __nsmimeinfoimpl_h___
 
 #include "nsIMIMEInfo.h"
 #include "nsIAtom.h"
 #include "nsString.h"
 #include "nsVoidArray.h"
+#include "nsIMutableArray.h"
 #include "nsIFile.h"
 #include "nsCOMPtr.h"
 #include "nsIURI.h"
 
 /** 
  * UTF8 moz-icon URI string for the default handler application's icon, if 
  * available.
  */
@@ -78,18 +79,19 @@ class nsMIMEInfoBase : public nsIMIMEInf
     NS_IMETHOD GetMIMEType(nsACString & aMIMEType);
     NS_IMETHOD GetDescription(nsAString & aDescription);
     NS_IMETHOD SetDescription(const nsAString & aDescription);
     NS_IMETHOD GetMacType(PRUint32 *aMacType);
     NS_IMETHOD SetMacType(PRUint32 aMacType);
     NS_IMETHOD GetMacCreator(PRUint32 *aMacCreator);
     NS_IMETHOD SetMacCreator(PRUint32 aMacCreator);
     NS_IMETHOD Equals(nsIMIMEInfo *aMIMEInfo, PRBool *_retval);
-    NS_IMETHOD GetPreferredApplicationHandler(nsIHandlerApp * *aPreferredApplicationHandler);
-    NS_IMETHOD SetPreferredApplicationHandler(nsIHandlerApp * aPreferredApplicationHandler);
+    NS_IMETHOD GetPreferredApplicationHandler(nsIHandlerApp * *aPreferredAppHandler);
+    NS_IMETHOD SetPreferredApplicationHandler(nsIHandlerApp * aPreferredAppHandler);
+    NS_IMETHOD GetPossibleApplicationHandlers(nsIMutableArray * *aPossibleAppHandlers);
     NS_IMETHOD GetDefaultDescription(nsAString & aDefaultDescription);
     NS_IMETHOD LaunchWithURI(nsIURI *aURI);
     NS_IMETHOD GetPreferredAction(nsHandlerInfoAction *aPreferredAction);
     NS_IMETHOD SetPreferredAction(nsHandlerInfoAction aPreferredAction);
     NS_IMETHOD GetAlwaysAskBeforeHandling(PRBool *aAlwaysAskBeforeHandling);
     NS_IMETHOD SetAlwaysAskBeforeHandling(PRBool aAlwaysAskBeforeHandling); 
 
     enum HandlerClass {
@@ -170,16 +172,17 @@ class nsMIMEInfoBase : public nsIMIMEInf
 
     // member variables
     nsCStringArray         mExtensions; ///< array of file extensions associated w/ this MIME obj
     nsString               mDescription; ///< human readable description
     PRUint32               mMacType, mMacCreator; ///< Mac file type and creator
     nsCString              mType;
     HandlerClass           mClass;
     nsCOMPtr<nsIHandlerApp> mPreferredApplication;
+    nsCOMPtr<nsIMutableArray> mPossibleApplications;
     nsHandlerInfoAction    mPreferredAction; ///< preferred action to associate with this type
     nsString               mPreferredAppDescription;
     nsString               mDefaultAppDescription;
     PRBool                 mAlwaysAskBeforeHandling;
 };
 
 
 /**