Back to index

lightning-sunbird  0.9+nobinonly
nsOSHelperAppService.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is the Mozilla browser.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications, Inc.
00020  * Portions created by the Initial Developer are Copyright (C) 1999
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Scott MacGregor <mscott@netscape.com>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "nsOSHelperAppService.h"
00041 #include "nsISupports.h"
00042 #include "nsString.h"
00043 #include "nsAutoBuffer.h"
00044 #include "nsXPIDLString.h"
00045 #include "nsIURL.h"
00046 #include "nsILocalFile.h"
00047 #include "nsILocalFileMac.h"
00048 #include "nsMimeTypes.h"
00049 #include "nsIStringBundle.h"
00050 #include "nsIPromptService.h"
00051 #include "nsMemory.h"
00052 #include "nsCRT.h"
00053 #include "nsMIMEInfoMac.h"
00054 
00055 #include "nsIInternetConfigService.h"
00056 #include "nsEmbedCID.h"
00057 #include <LaunchServices.h>
00058 
00059 // chrome URL's
00060 #define HELPERAPPLAUNCHER_BUNDLE_URL "chrome://global/locale/helperAppLauncher.properties"
00061 #define BRAND_BUNDLE_URL "chrome://branding/locale/brand.properties"
00062 
00063 extern "C" {
00064   // Returns the CFURL for application currently set as the default opener for
00065   // the given URL scheme. appURL must be released by the caller.
00066   extern OSStatus _LSCopyDefaultSchemeHandlerURL(CFStringRef scheme,
00067                                                  CFURLRef *appURL);
00068 }
00069 
00070 nsOSHelperAppService::nsOSHelperAppService() : nsExternalHelperAppService()
00071 {
00072 }
00073 
00074 nsOSHelperAppService::~nsOSHelperAppService()
00075 {}
00076 
00077 NS_IMETHODIMP nsOSHelperAppService::ExternalProtocolHandlerExists(const char * aProtocolScheme, PRBool * aHandlerExists)
00078 {
00079   // look up the protocol scheme in Internet Config....if we find a match then we have a handler for it...
00080   *aHandlerExists = PR_FALSE;
00081   // ask the internet config service to look it up for us...
00082   nsresult rv = NS_ERROR_FAILURE;
00083   nsCOMPtr<nsIInternetConfigService> icService (do_GetService(NS_INTERNETCONFIGSERVICE_CONTRACTID));
00084   if (icService)
00085   {
00086     rv = icService->HasProtocolHandler(aProtocolScheme, aHandlerExists);
00087     if (rv == NS_ERROR_NOT_AVAILABLE)
00088     {
00089       // current app is registered to handle the protocol, put up an alert
00090       nsCOMPtr<nsIStringBundleService> stringBundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID);
00091       if (stringBundleService)
00092       {
00093         nsCOMPtr<nsIStringBundle> appLauncherBundle;
00094         rv = stringBundleService->CreateBundle(HELPERAPPLAUNCHER_BUNDLE_URL, getter_AddRefs(appLauncherBundle));
00095         if (rv == NS_OK)
00096         {
00097           nsCOMPtr<nsIStringBundle> brandBundle;
00098           rv = stringBundleService->CreateBundle(BRAND_BUNDLE_URL, getter_AddRefs(brandBundle));
00099           if (rv == NS_OK)
00100           {
00101             nsXPIDLString brandName;
00102             rv = brandBundle->GetStringFromName(NS_LITERAL_STRING("brandShortName").get(), getter_Copies(brandName));
00103             if (rv == NS_OK)
00104             {
00105               nsXPIDLString errorStr;
00106               NS_ConvertASCIItoUCS2 proto(aProtocolScheme);
00107               const PRUnichar *formatStrings[] = { brandName.get(), proto.get() };
00108               rv = appLauncherBundle->FormatStringFromName(NS_LITERAL_STRING("protocolNotHandled").get(),
00109                                                            formatStrings,
00110                                                            2,
00111                                                            getter_Copies(errorStr));
00112               if (rv == NS_OK)
00113               {
00114                 nsCOMPtr<nsIPromptService> prompt (do_GetService(NS_PROMPTSERVICE_CONTRACTID));
00115                 if (prompt)
00116                   prompt->Alert(nsnull, NS_LITERAL_STRING("Alert").get(), errorStr.get());
00117               }
00118             }
00119           }
00120         }
00121       }
00122     }
00123   }
00124   return rv;
00125 }
00126 
00127 NS_IMETHODIMP nsOSHelperAppService::GetApplicationDescription(const nsACString& aScheme, nsAString& _retval)
00128 {
00129   nsresult rv = NS_ERROR_NOT_AVAILABLE;
00130 
00131   CFStringRef schemeCFString = 
00132     ::CFStringCreateWithBytes(kCFAllocatorDefault,
00133                               (const UInt8 *)PromiseFlatCString(aScheme).get(),
00134                               aScheme.Length(),
00135                               kCFStringEncodingUTF8,
00136                               false);
00137   if (schemeCFString) {
00138     // Since the public API (LSGetApplicationForURL) fails every now and then,
00139     // we're using undocumented _LSCopyDefaultSchemeHandlerURL
00140     CFURLRef handlerBundleURL;
00141     OSStatus err = ::_LSCopyDefaultSchemeHandlerURL(schemeCFString,
00142                                                     &handlerBundleURL);
00143     if (err == noErr) {
00144       CFBundleRef handlerBundle = ::CFBundleCreate(NULL, handlerBundleURL);
00145       if (handlerBundle) {
00146         // Get the human-readable name of the default handler bundle
00147         CFStringRef bundleName =
00148           (CFStringRef)::CFBundleGetValueForInfoDictionaryKey(handlerBundle,
00149                                                               kCFBundleNameKey);
00150         if (bundleName) {
00151           nsAutoBuffer<UniChar, 255> buffer;
00152           CFIndex bundleNameLength = ::CFStringGetLength(bundleName);
00153           buffer.EnsureElemCapacity(bundleNameLength);
00154           ::CFStringGetCharacters(bundleName, CFRangeMake(0, bundleNameLength),
00155                                   buffer.get());
00156           _retval.Assign(buffer.get(), bundleNameLength);
00157           rv = NS_OK;
00158         }
00159 
00160         ::CFRelease(handlerBundle);
00161       }
00162 
00163       ::CFRelease(handlerBundleURL);
00164     }
00165 
00166     ::CFRelease(schemeCFString);
00167   }
00168 
00169   return rv;
00170 }
00171 
00172 nsresult nsOSHelperAppService::LoadUriInternal(nsIURI * aURL)
00173 {
00174   nsCAutoString url;
00175   nsresult rv = NS_ERROR_FAILURE;
00176   
00177   if (aURL)
00178   {
00179     aURL->GetSpec(url);
00180     if (!url.IsEmpty())
00181     {
00182       nsCOMPtr<nsIInternetConfigService> icService (do_GetService(NS_INTERNETCONFIGSERVICE_CONTRACTID));
00183       if (icService)
00184         rv = icService->LaunchURL(url.get());
00185     }
00186   }
00187   return rv;
00188 }
00189 
00190 nsresult nsOSHelperAppService::GetFileTokenForPath(const PRUnichar * aPlatformAppPath, nsIFile ** aFile)
00191 {
00192   nsresult rv;
00193   nsCOMPtr<nsILocalFileMac> localFile (do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
00194   NS_ENSURE_SUCCESS(rv,rv);
00195 
00196   CFURLRef pathAsCFURL;
00197   CFStringRef pathAsCFString = ::CFStringCreateWithCharacters(NULL,
00198                                                               aPlatformAppPath,
00199                                                               nsCRT::strlen(aPlatformAppPath));
00200   if (!pathAsCFString)
00201     return NS_ERROR_OUT_OF_MEMORY;
00202 
00203   if (::CFStringGetCharacterAtIndex(pathAsCFString, 0) == '/') {
00204     // we have a Posix path
00205     pathAsCFURL = ::CFURLCreateWithFileSystemPath(nsnull, pathAsCFString,
00206                                                   kCFURLPOSIXPathStyle, PR_FALSE);
00207     if (!pathAsCFURL) {
00208       ::CFRelease(pathAsCFString);
00209       return NS_ERROR_OUT_OF_MEMORY;
00210     }
00211   }
00212   else {
00213     // if it doesn't start with a / it's not an absolute Posix path
00214     // let's check if it's a HFS path left over from old preferences
00215 
00216     // If it starts with a ':' char, it's not an absolute HFS path
00217     // so bail for that, and also if it's empty
00218     if (::CFStringGetLength(pathAsCFString) == 0 ||
00219         ::CFStringGetCharacterAtIndex(pathAsCFString, 0) == ':')
00220     {
00221       ::CFRelease(pathAsCFString);
00222       return NS_ERROR_FILE_UNRECOGNIZED_PATH;
00223     }
00224 
00225     pathAsCFURL = ::CFURLCreateWithFileSystemPath(nsnull, pathAsCFString,
00226                                                   kCFURLHFSPathStyle, PR_FALSE);
00227     if (!pathAsCFURL) {
00228       ::CFRelease(pathAsCFString);
00229       return NS_ERROR_OUT_OF_MEMORY;
00230     }
00231   }
00232 
00233   rv = localFile->InitWithCFURL(pathAsCFURL);
00234   ::CFRelease(pathAsCFString);
00235   ::CFRelease(pathAsCFURL);
00236   if (NS_FAILED(rv))
00237     return rv;
00238   *aFile = localFile;
00239   NS_IF_ADDREF(*aFile);
00240 
00241   return NS_OK;
00242 }
00244 // method overrides --> use internet config information for mime type lookup.
00246 
00247 NS_IMETHODIMP nsOSHelperAppService::GetFromTypeAndExtension(const nsACString& aType, const nsACString& aFileExt, nsIMIMEInfo ** aMIMEInfo)
00248 {
00249   // first, ask our base class....
00250   nsresult rv = nsExternalHelperAppService::GetFromTypeAndExtension(aType, aFileExt, aMIMEInfo);
00251   if (NS_SUCCEEDED(rv) && *aMIMEInfo) 
00252   {
00253     UpdateCreatorInfo(*aMIMEInfo);
00254   }
00255   return rv;
00256 }
00257 
00258 already_AddRefed<nsIMIMEInfo>
00259 nsOSHelperAppService::GetMIMEInfoFromOS(const nsACString& aMIMEType,
00260                                         const nsACString& aFileExt,
00261                                         PRBool * aFound)
00262 {
00263   nsIMIMEInfo* mimeInfo = nsnull;
00264   *aFound = PR_TRUE;
00265 
00266   const nsCString& flatType = PromiseFlatCString(aMIMEType);
00267   const nsCString& flatExt = PromiseFlatCString(aFileExt);
00268 
00269   // ask the internet config service to look it up for us...
00270   nsCOMPtr<nsIInternetConfigService> icService (do_GetService(NS_INTERNETCONFIGSERVICE_CONTRACTID));
00271   PR_LOG(mLog, PR_LOG_DEBUG, ("Mac: HelperAppService lookup for type '%s' ext '%s' (IC: 0x%p)\n",
00272                               flatType.get(), flatExt.get(), icService.get()));
00273   if (icService)
00274   {
00275     nsCOMPtr<nsIMIMEInfo> miByType, miByExt;
00276     if (!aMIMEType.IsEmpty())
00277       icService->FillInMIMEInfo(flatType.get(), flatExt.get(), getter_AddRefs(miByType));
00278 
00279     PRBool hasDefault = PR_FALSE;
00280     if (miByType)
00281       miByType->GetHasDefaultHandler(&hasDefault);
00282 
00283     if (!aFileExt.IsEmpty() && (!hasDefault || !miByType)) {
00284       icService->GetMIMEInfoFromExtension(flatExt.get(), getter_AddRefs(miByExt));
00285       if (miByExt && !aMIMEType.IsEmpty()) {
00286         // XXX see XXX comment below
00287         nsIMIMEInfo* pByExt = miByExt.get();
00288         nsMIMEInfoBase* byExt = NS_STATIC_CAST(nsMIMEInfoBase*, pByExt);
00289         byExt->SetMIMEType(aMIMEType);
00290       }
00291     }
00292     PR_LOG(mLog, PR_LOG_DEBUG, ("OS gave us: By Type: 0x%p By Ext: 0x%p type has default: %s\n",
00293                                 miByType.get(), miByExt.get(), hasDefault ? "true" : "false"));
00294 
00295     // If we got two matches, and the type has no default app, copy default app
00296     if (!hasDefault && miByType && miByExt) {
00297       // IC currently always uses nsMIMEInfoBase-derived classes.
00298       // When it stops doing that, this code will need changing.
00299       // XXX This assumes that IC will give os an nsMIMEInfoBase. I'd use
00300       // dynamic_cast but that crashes.
00301       // XXX these pBy* variables are needed because .get() returns an
00302       // nsDerivedSafe thingy that can't be cast to nsMIMEInfoBase*
00303       nsIMIMEInfo* pByType = miByType.get();
00304       nsIMIMEInfo* pByExt = miByExt.get();
00305       nsMIMEInfoBase* byType = NS_STATIC_CAST(nsMIMEInfoBase*, pByType);
00306       nsMIMEInfoBase* byExt = NS_STATIC_CAST(nsMIMEInfoBase*, pByExt);
00307       if (!byType || !byExt) {
00308         NS_ERROR("IC gave us an nsIMIMEInfo that's no nsMIMEInfoBase! Fix nsOSHelperAppService.");
00309         return nsnull;
00310       }
00311       // Copy the attributes of miByType onto miByExt
00312       byType->CopyBasicDataTo(byExt);
00313       miByType = miByExt;
00314     }
00315     if (miByType)
00316       miByType.swap(mimeInfo);
00317     else if (miByExt)
00318       miByExt.swap(mimeInfo);
00319   }
00320 
00321   if (!mimeInfo) {
00322     *aFound = PR_FALSE;
00323     PR_LOG(mLog, PR_LOG_DEBUG, ("Creating new mimeinfo\n"));
00324     mimeInfo = new nsMIMEInfoMac(aMIMEType);
00325     if (!mimeInfo)
00326       return nsnull;
00327     NS_ADDREF(mimeInfo);
00328 
00329     if (!aFileExt.IsEmpty())
00330       mimeInfo->AppendExtension(aFileExt);
00331   }
00332   
00333   return mimeInfo;
00334 }
00335 
00336 // we never want to use a hard coded value for the creator and file type for the mac. always look these values up
00337 // from internet config.
00338 void nsOSHelperAppService::UpdateCreatorInfo(nsIMIMEInfo * aMIMEInfo)
00339 {
00340   PRUint32 macCreatorType;
00341   PRUint32 macFileType;
00342   aMIMEInfo->GetMacType(&macFileType);
00343   aMIMEInfo->GetMacCreator(&macCreatorType);
00344   
00345   if (macFileType == 0 || macCreatorType == 0)
00346   {
00347     // okay these values haven't been initialized yet so fetch a mime object from internet config.
00348     nsCAutoString mimeType;
00349     aMIMEInfo->GetMIMEType(mimeType);
00350     nsCOMPtr<nsIInternetConfigService> icService (do_GetService(NS_INTERNETCONFIGSERVICE_CONTRACTID));
00351     if (icService)
00352     {
00353       nsCOMPtr<nsIMIMEInfo> osMimeObject;
00354       icService->FillInMIMEInfo(mimeType.get(), nsnull, getter_AddRefs(osMimeObject));
00355       if (osMimeObject)
00356       {
00357         osMimeObject->GetMacType(&macFileType);
00358         osMimeObject->GetMacCreator(&macCreatorType);
00359         aMIMEInfo->SetMacCreator(macCreatorType);
00360         aMIMEInfo->SetMacType(macFileType);
00361       } // if we got an os object
00362     } // if we got the ic service
00363   } // if the creator or file type hasn't been initialized yet
00364 }