michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsDirectoryServiceDefs.h" michael@0: #include "nsIDOMElement.h" michael@0: #include "nsIDOMHTMLImageElement.h" michael@0: #include "nsIImageLoadingContent.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIContent.h" michael@0: #include "nsILocalFileMac.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsIPrefService.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsIStringBundle.h" michael@0: #include "nsIURL.h" michael@0: #include "nsIWebBrowserPersist.h" michael@0: #include "nsMacShellService.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsShellService.h" michael@0: #include "nsStringAPI.h" michael@0: #include "nsIDocShell.h" michael@0: #include "nsILoadContext.h" michael@0: michael@0: #include michael@0: #include michael@0: michael@0: #define NETWORK_PREFPANE NS_LITERAL_CSTRING("/System/Library/PreferencePanes/Network.prefPane") michael@0: #define DESKTOP_PREFPANE NS_LITERAL_CSTRING("/System/Library/PreferencePanes/DesktopScreenEffectsPref.prefPane") michael@0: michael@0: #define SAFARI_BUNDLE_IDENTIFIER "com.apple.Safari" michael@0: michael@0: NS_IMPL_ISUPPORTS(nsMacShellService, nsIMacShellService, nsIShellService, nsIWebProgressListener) michael@0: michael@0: NS_IMETHODIMP michael@0: nsMacShellService::IsDefaultBrowser(bool aStartupCheck, michael@0: bool aForAllTypes, michael@0: bool* aIsDefaultBrowser) michael@0: { michael@0: *aIsDefaultBrowser = false; michael@0: michael@0: CFStringRef firefoxID = ::CFBundleGetIdentifier(::CFBundleGetMainBundle()); michael@0: if (!firefoxID) { michael@0: // CFBundleGetIdentifier is expected to return nullptr only if the specified michael@0: // bundle doesn't have a bundle identifier in its plist. In this case, that michael@0: // means a failure, since our bundle does have an identifier. michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // Get the default http handler's bundle ID (or nullptr if it has not been michael@0: // explicitly set) michael@0: CFStringRef defaultBrowserID = ::LSCopyDefaultHandlerForURLScheme(CFSTR("http")); michael@0: if (defaultBrowserID) { michael@0: *aIsDefaultBrowser = ::CFStringCompare(firefoxID, defaultBrowserID, 0) == kCFCompareEqualTo; michael@0: ::CFRelease(defaultBrowserID); michael@0: } michael@0: michael@0: // If this is the first browser window, maintain internal state that we've michael@0: // checked this session (so that subsequent window opens don't show the michael@0: // default browser dialog). michael@0: if (aStartupCheck) michael@0: mCheckedThisSession = true; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMacShellService::SetDefaultBrowser(bool aClaimAllTypes, bool aForAllUsers) michael@0: { michael@0: // Note: We don't support aForAllUsers on Mac OS X. michael@0: michael@0: CFStringRef firefoxID = ::CFBundleGetIdentifier(::CFBundleGetMainBundle()); michael@0: if (!firefoxID) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (::LSSetDefaultHandlerForURLScheme(CFSTR("http"), firefoxID) != noErr) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: if (::LSSetDefaultHandlerForURLScheme(CFSTR("https"), firefoxID) != noErr) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (aClaimAllTypes) { michael@0: if (::LSSetDefaultHandlerForURLScheme(CFSTR("ftp"), firefoxID) != noErr) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: if (::LSSetDefaultRoleHandlerForContentType(kUTTypeHTML, kLSRolesAll, firefoxID) != noErr) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMacShellService::GetShouldCheckDefaultBrowser(bool* aResult) michael@0: { michael@0: // If we've already checked, the browser has been started and this is a michael@0: // new window open, and we don't want to check again. michael@0: if (mCheckedThisSession) { michael@0: *aResult = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsCOMPtr prefs; michael@0: nsCOMPtr pserve(do_GetService(NS_PREFSERVICE_CONTRACTID)); michael@0: if (pserve) michael@0: pserve->GetBranch("", getter_AddRefs(prefs)); michael@0: michael@0: prefs->GetBoolPref(PREF_CHECKDEFAULTBROWSER, aResult); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMacShellService::SetShouldCheckDefaultBrowser(bool aShouldCheck) michael@0: { michael@0: nsCOMPtr prefs; michael@0: nsCOMPtr pserve(do_GetService(NS_PREFSERVICE_CONTRACTID)); michael@0: if (pserve) michael@0: pserve->GetBranch("", getter_AddRefs(prefs)); michael@0: michael@0: prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, aShouldCheck); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMacShellService::GetCanSetDesktopBackground(bool* aResult) michael@0: { michael@0: *aResult = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMacShellService::SetDesktopBackground(nsIDOMElement* aElement, michael@0: int32_t aPosition) michael@0: { michael@0: // Note: We don't support aPosition on OS X. michael@0: michael@0: // Get the image URI: michael@0: nsresult rv; michael@0: nsCOMPtr imageContent = do_QueryInterface(aElement, michael@0: &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: nsCOMPtr imageURI; michael@0: rv = imageContent->GetCurrentURI(getter_AddRefs(imageURI)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // We need the referer URI for nsIWebBrowserPersist::saveURI michael@0: nsCOMPtr content = do_QueryInterface(aElement, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsIURI *docURI = content->OwnerDoc()->GetDocumentURI(); michael@0: if (!docURI) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: // Get the desired image file name michael@0: nsCOMPtr imageURL(do_QueryInterface(imageURI)); michael@0: if (!imageURL) { michael@0: // XXXmano (bug 300293): Non-URL images (e.g. the data: protocol) are not michael@0: // yet supported. What filename should we take here? michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: nsAutoCString fileName; michael@0: imageURL->GetFileName(fileName); michael@0: nsCOMPtr fileLocator michael@0: (do_GetService("@mozilla.org/file/directory_service;1", &rv)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Get the current user's "Pictures" folder (That's ~/Pictures): michael@0: fileLocator->Get(NS_OSX_PICTURE_DOCUMENTS_DIR, NS_GET_IID(nsIFile), michael@0: getter_AddRefs(mBackgroundFile)); michael@0: if (!mBackgroundFile) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: nsAutoString fileNameUnicode; michael@0: CopyUTF8toUTF16(fileName, fileNameUnicode); michael@0: michael@0: // and add the imgage file name itself: michael@0: mBackgroundFile->Append(fileNameUnicode); michael@0: michael@0: // Download the image; the desktop background will be set in OnStateChange() michael@0: nsCOMPtr wbp michael@0: (do_CreateInstance("@mozilla.org/embedding/browser/nsWebBrowserPersist;1", &rv)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: uint32_t flags = nsIWebBrowserPersist::PERSIST_FLAGS_NO_CONVERSION | michael@0: nsIWebBrowserPersist::PERSIST_FLAGS_REPLACE_EXISTING_FILES | michael@0: nsIWebBrowserPersist::PERSIST_FLAGS_FROM_CACHE; michael@0: michael@0: wbp->SetPersistFlags(flags); michael@0: wbp->SetProgressListener(this); michael@0: michael@0: nsCOMPtr loadContext; michael@0: nsCOMPtr container = content->OwnerDoc()->GetContainer(); michael@0: nsCOMPtr docShell = do_QueryInterface(container); michael@0: if (docShell) { michael@0: loadContext = do_QueryInterface(docShell); michael@0: } michael@0: michael@0: return wbp->SaveURI(imageURI, nullptr, docURI, nullptr, nullptr, michael@0: mBackgroundFile, loadContext); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMacShellService::OnProgressChange(nsIWebProgress* aWebProgress, michael@0: nsIRequest* aRequest, michael@0: int32_t aCurSelfProgress, michael@0: int32_t aMaxSelfProgress, michael@0: int32_t aCurTotalProgress, michael@0: int32_t aMaxTotalProgress) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMacShellService::OnLocationChange(nsIWebProgress* aWebProgress, michael@0: nsIRequest* aRequest, michael@0: nsIURI* aLocation, michael@0: uint32_t aFlags) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMacShellService::OnStatusChange(nsIWebProgress* aWebProgress, michael@0: nsIRequest* aRequest, michael@0: nsresult aStatus, michael@0: const char16_t* aMessage) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMacShellService::OnSecurityChange(nsIWebProgress* aWebProgress, michael@0: nsIRequest* aRequest, michael@0: uint32_t aState) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMacShellService::OnStateChange(nsIWebProgress* aWebProgress, michael@0: nsIRequest* aRequest, michael@0: uint32_t aStateFlags, michael@0: nsresult aStatus) michael@0: { michael@0: if (aStateFlags & STATE_STOP) { michael@0: nsCOMPtr os(do_GetService("@mozilla.org/observer-service;1")); michael@0: if (os) michael@0: os->NotifyObservers(nullptr, "shell:desktop-background-changed", nullptr); michael@0: michael@0: bool exists = false; michael@0: mBackgroundFile->Exists(&exists); michael@0: if (!exists) michael@0: return NS_OK; michael@0: michael@0: nsAutoCString nativePath; michael@0: mBackgroundFile->GetNativePath(nativePath); michael@0: michael@0: AEDesc tAEDesc = { typeNull, nil }; michael@0: OSErr err = noErr; michael@0: AliasHandle aliasHandle = nil; michael@0: FSRef pictureRef; michael@0: OSStatus status; michael@0: michael@0: // Convert the path into a FSRef michael@0: status = ::FSPathMakeRef((const UInt8*)nativePath.get(), &pictureRef, michael@0: nullptr); michael@0: if (status == noErr) { michael@0: err = ::FSNewAlias(nil, &pictureRef, &aliasHandle); michael@0: if (err == noErr && aliasHandle == nil) michael@0: err = paramErr; michael@0: michael@0: if (err == noErr) { michael@0: // We need the descriptor (based on the picture file reference) michael@0: // for the 'Set Desktop Picture' apple event. michael@0: char handleState = ::HGetState((Handle)aliasHandle); michael@0: ::HLock((Handle)aliasHandle); michael@0: err = ::AECreateDesc(typeAlias, *aliasHandle, michael@0: GetHandleSize((Handle)aliasHandle), &tAEDesc); michael@0: // unlock the alias handler michael@0: ::HSetState((Handle)aliasHandle, handleState); michael@0: ::DisposeHandle((Handle)aliasHandle); michael@0: } michael@0: if (err == noErr) { michael@0: AppleEvent tAppleEvent; michael@0: OSType sig = 'MACS'; michael@0: AEBuildError tAEBuildError; michael@0: // Create a 'Set Desktop Pictue' Apple Event michael@0: err = ::AEBuildAppleEvent(kAECoreSuite, kAESetData, typeApplSignature, michael@0: &sig, sizeof(OSType), kAutoGenerateReturnID, michael@0: kAnyTransactionID, &tAppleEvent, &tAEBuildError, michael@0: "'----':'obj '{want:type (prop),form:prop" \ michael@0: ",seld:type('dpic'),from:'null'()},data:(@)", michael@0: &tAEDesc); michael@0: if (err == noErr) { michael@0: AppleEvent reply = { typeNull, nil }; michael@0: // Sent the event we built, the reply event isn't necessary michael@0: err = ::AESend(&tAppleEvent, &reply, kAENoReply, kAENormalPriority, michael@0: kNoTimeOut, nil, nil); michael@0: ::AEDisposeDesc(&tAppleEvent); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMacShellService::OpenApplication(int32_t aApplication) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: CFURLRef appURL = nil; michael@0: OSStatus err = noErr; michael@0: michael@0: switch (aApplication) { michael@0: case nsIShellService::APPLICATION_MAIL: michael@0: { michael@0: CFURLRef tempURL = ::CFURLCreateWithString(kCFAllocatorDefault, michael@0: CFSTR("mailto:"), nullptr); michael@0: err = ::LSGetApplicationForURL(tempURL, kLSRolesAll, nullptr, &appURL); michael@0: ::CFRelease(tempURL); michael@0: } michael@0: break; michael@0: case nsIShellService::APPLICATION_NEWS: michael@0: { michael@0: CFURLRef tempURL = ::CFURLCreateWithString(kCFAllocatorDefault, michael@0: CFSTR("news:"), nullptr); michael@0: err = ::LSGetApplicationForURL(tempURL, kLSRolesAll, nullptr, &appURL); michael@0: ::CFRelease(tempURL); michael@0: } michael@0: break; michael@0: case nsIMacShellService::APPLICATION_KEYCHAIN_ACCESS: michael@0: err = ::LSGetApplicationForInfo('APPL', 'kcmr', nullptr, kLSRolesAll, michael@0: nullptr, &appURL); michael@0: break; michael@0: case nsIMacShellService::APPLICATION_NETWORK: michael@0: { michael@0: nsCOMPtr lf; michael@0: rv = NS_NewNativeLocalFile(NETWORK_PREFPANE, true, getter_AddRefs(lf)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: bool exists; michael@0: lf->Exists(&exists); michael@0: if (!exists) michael@0: return NS_ERROR_FILE_NOT_FOUND; michael@0: return lf->Launch(); michael@0: } michael@0: break; michael@0: case nsIMacShellService::APPLICATION_DESKTOP: michael@0: { michael@0: nsCOMPtr lf; michael@0: rv = NS_NewNativeLocalFile(DESKTOP_PREFPANE, true, getter_AddRefs(lf)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: bool exists; michael@0: lf->Exists(&exists); michael@0: if (!exists) michael@0: return NS_ERROR_FILE_NOT_FOUND; michael@0: return lf->Launch(); michael@0: } michael@0: break; michael@0: } michael@0: michael@0: if (appURL && err == noErr) { michael@0: err = ::LSOpenCFURLRef(appURL, nullptr); michael@0: rv = err != noErr ? NS_ERROR_FAILURE : NS_OK; michael@0: michael@0: ::CFRelease(appURL); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMacShellService::GetDesktopBackgroundColor(uint32_t *aColor) michael@0: { michael@0: // This method and |SetDesktopBackgroundColor| has no meaning on Mac OS X. michael@0: // The mac desktop preferences UI uses pictures for the few solid colors it michael@0: // supports. michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMacShellService::SetDesktopBackgroundColor(uint32_t aColor) michael@0: { michael@0: // This method and |GetDesktopBackgroundColor| has no meaning on Mac OS X. michael@0: // The mac desktop preferences UI uses pictures for the few solid colors it michael@0: // supports. michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMacShellService::OpenApplicationWithURI(nsIFile* aApplication, const nsACString& aURI) michael@0: { michael@0: nsCOMPtr lfm(do_QueryInterface(aApplication)); michael@0: CFURLRef appURL; michael@0: nsresult rv = lfm->GetCFURL(&appURL); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: const nsCString spec(aURI); michael@0: const UInt8* uriString = (const UInt8*)spec.get(); michael@0: CFURLRef uri = ::CFURLCreateWithBytes(nullptr, uriString, aURI.Length(), michael@0: kCFStringEncodingUTF8, nullptr); michael@0: if (!uri) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: CFArrayRef uris = ::CFArrayCreate(nullptr, (const void**)&uri, 1, nullptr); michael@0: if (!uris) { michael@0: ::CFRelease(uri); michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: LSLaunchURLSpec launchSpec; michael@0: launchSpec.appURL = appURL; michael@0: launchSpec.itemURLs = uris; michael@0: launchSpec.passThruParams = nullptr; michael@0: launchSpec.launchFlags = kLSLaunchDefaults; michael@0: launchSpec.asyncRefCon = nullptr; michael@0: michael@0: OSErr err = ::LSOpenFromURLSpec(&launchSpec, nullptr); michael@0: michael@0: ::CFRelease(uris); michael@0: ::CFRelease(uri); michael@0: michael@0: return err != noErr ? NS_ERROR_FAILURE : NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMacShellService::GetDefaultFeedReader(nsIFile** _retval) michael@0: { michael@0: nsresult rv = NS_ERROR_FAILURE; michael@0: *_retval = nullptr; michael@0: michael@0: CFStringRef defaultHandlerID = ::LSCopyDefaultHandlerForURLScheme(CFSTR("feed")); michael@0: if (!defaultHandlerID) { michael@0: defaultHandlerID = ::CFStringCreateWithCString(kCFAllocatorDefault, michael@0: SAFARI_BUNDLE_IDENTIFIER, michael@0: kCFStringEncodingASCII); michael@0: } michael@0: michael@0: CFURLRef defaultHandlerURL = nullptr; michael@0: OSStatus status = ::LSFindApplicationForInfo(kLSUnknownCreator, michael@0: defaultHandlerID, michael@0: nullptr, // inName michael@0: nullptr, // outAppRef michael@0: &defaultHandlerURL); michael@0: michael@0: if (status == noErr && defaultHandlerURL) { michael@0: nsCOMPtr defaultReader = michael@0: do_CreateInstance("@mozilla.org/file/local;1", &rv); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: rv = defaultReader->InitWithCFURL(defaultHandlerURL); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: NS_ADDREF(*_retval = defaultReader); michael@0: rv = NS_OK; michael@0: } michael@0: } michael@0: michael@0: ::CFRelease(defaultHandlerURL); michael@0: } michael@0: michael@0: ::CFRelease(defaultHandlerID); michael@0: michael@0: return rv; michael@0: }