fune/browser/components/shell/nsMacShellService.cpp
Matthew Noorenberghe ff27adf28d Bug 1615134 - Shell service API to open macOS Security & Privacy preferences panes. r=spohl
Example: Cc["@mozilla.org/browser/shell-service;1"].getService(Ci.nsIMacShellService).showSecurityPreferences("Privacy_AllFiles")

Differential Revision: https://phabricator.services.mozilla.com/D62690

--HG--
extra : moz-landing-system : lando
2020-02-14 04:06:09 +00:00

320 lines
11 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
#include "CocoaFileUtils.h"
#include "nsDirectoryServiceDefs.h"
#include "nsIImageLoadingContent.h"
#include "mozilla/dom/Document.h"
#include "nsIContent.h"
#include "nsIObserverService.h"
#include "nsIWebBrowserPersist.h"
#include "nsMacShellService.h"
#include "nsIProperties.h"
#include "nsServiceManagerUtils.h"
#include "nsShellService.h"
#include "nsString.h"
#include "nsIDocShell.h"
#include "nsILoadContext.h"
#include "mozilla/dom/Element.h"
#include <Carbon/Carbon.h>
#include <CoreFoundation/CoreFoundation.h>
#include <ApplicationServices/ApplicationServices.h>
using mozilla::dom::Element;
#define NETWORK_PREFPANE \
NS_LITERAL_CSTRING("/System/Library/PreferencePanes/Network.prefPane")
#define DESKTOP_PREFPANE \
NS_LITERAL_CSTRING( \
"/System/Library/PreferencePanes/DesktopScreenEffectsPref.prefPane")
#define SAFARI_BUNDLE_IDENTIFIER "com.apple.Safari"
NS_IMPL_ISUPPORTS(nsMacShellService, nsIMacShellService, nsIShellService,
nsIToolkitShellService, nsIWebProgressListener)
NS_IMETHODIMP
nsMacShellService::IsDefaultBrowser(bool aForAllTypes,
bool* aIsDefaultBrowser) {
*aIsDefaultBrowser = false;
CFStringRef firefoxID = ::CFBundleGetIdentifier(::CFBundleGetMainBundle());
if (!firefoxID) {
// CFBundleGetIdentifier is expected to return nullptr only if the specified
// bundle doesn't have a bundle identifier in its plist. In this case, that
// means a failure, since our bundle does have an identifier.
return NS_ERROR_FAILURE;
}
// Get the default http handler's bundle ID (or nullptr if it has not been
// explicitly set)
CFStringRef defaultBrowserID =
::LSCopyDefaultHandlerForURLScheme(CFSTR("http"));
if (defaultBrowserID) {
*aIsDefaultBrowser =
::CFStringCompare(firefoxID, defaultBrowserID, 0) == kCFCompareEqualTo;
::CFRelease(defaultBrowserID);
}
return NS_OK;
}
NS_IMETHODIMP
nsMacShellService::SetDefaultBrowser(bool aClaimAllTypes, bool aForAllUsers) {
// Note: We don't support aForAllUsers on Mac OS X.
CFStringRef firefoxID = ::CFBundleGetIdentifier(::CFBundleGetMainBundle());
if (!firefoxID) {
return NS_ERROR_FAILURE;
}
if (::LSSetDefaultHandlerForURLScheme(CFSTR("http"), firefoxID) != noErr) {
return NS_ERROR_FAILURE;
}
if (::LSSetDefaultHandlerForURLScheme(CFSTR("https"), firefoxID) != noErr) {
return NS_ERROR_FAILURE;
}
if (aClaimAllTypes) {
if (::LSSetDefaultHandlerForURLScheme(CFSTR("ftp"), firefoxID) != noErr) {
return NS_ERROR_FAILURE;
}
if (::LSSetDefaultRoleHandlerForContentType(kUTTypeHTML, kLSRolesAll,
firefoxID) != noErr) {
return NS_ERROR_FAILURE;
}
}
nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
if (prefs) {
(void)prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, true);
// Reset the number of times the dialog should be shown
// before it is silenced.
(void)prefs->SetIntPref(PREF_DEFAULTBROWSERCHECKCOUNT, 0);
}
return NS_OK;
}
NS_IMETHODIMP
nsMacShellService::SetDesktopBackground(Element* aElement, int32_t aPosition,
const nsACString& aImageName) {
// Note: We don't support aPosition on OS X.
// Get the image URI:
nsresult rv;
nsCOMPtr<nsIImageLoadingContent> imageContent =
do_QueryInterface(aElement, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIURI> imageURI;
rv = imageContent->GetCurrentURI(getter_AddRefs(imageURI));
NS_ENSURE_SUCCESS(rv, rv);
nsIURI* docURI = aElement->OwnerDoc()->GetDocumentURI();
if (!docURI) return NS_ERROR_FAILURE;
nsCOMPtr<nsIProperties> fileLocator(
do_GetService("@mozilla.org/file/directory_service;1", &rv));
NS_ENSURE_SUCCESS(rv, rv);
// Get the current user's "Pictures" folder (That's ~/Pictures):
fileLocator->Get(NS_OSX_PICTURE_DOCUMENTS_DIR, NS_GET_IID(nsIFile),
getter_AddRefs(mBackgroundFile));
if (!mBackgroundFile) return NS_ERROR_OUT_OF_MEMORY;
nsAutoString fileNameUnicode;
CopyUTF8toUTF16(aImageName, fileNameUnicode);
// and add the imgage file name itself:
mBackgroundFile->Append(fileNameUnicode);
// Download the image; the desktop background will be set in OnStateChange()
nsCOMPtr<nsIWebBrowserPersist> wbp(do_CreateInstance(
"@mozilla.org/embedding/browser/nsWebBrowserPersist;1", &rv));
NS_ENSURE_SUCCESS(rv, rv);
uint32_t flags = nsIWebBrowserPersist::PERSIST_FLAGS_NO_CONVERSION |
nsIWebBrowserPersist::PERSIST_FLAGS_REPLACE_EXISTING_FILES |
nsIWebBrowserPersist::PERSIST_FLAGS_FROM_CACHE;
wbp->SetPersistFlags(flags);
wbp->SetProgressListener(this);
nsCOMPtr<nsILoadContext> loadContext;
nsCOMPtr<nsISupports> container = aElement->OwnerDoc()->GetContainer();
nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(container);
if (docShell) {
loadContext = do_QueryInterface(docShell);
}
nsCOMPtr<nsIReferrerInfo> referrerInfo = new mozilla::dom::ReferrerInfo();
referrerInfo->InitWithNode(aElement);
return wbp->SaveURI(imageURI, aElement->NodePrincipal(), 0, referrerInfo,
nullptr, nullptr, mBackgroundFile,
nsIContentPolicy::TYPE_IMAGE, loadContext);
}
NS_IMETHODIMP
nsMacShellService::OnProgressChange(nsIWebProgress* aWebProgress,
nsIRequest* aRequest,
int32_t aCurSelfProgress,
int32_t aMaxSelfProgress,
int32_t aCurTotalProgress,
int32_t aMaxTotalProgress) {
return NS_OK;
}
NS_IMETHODIMP
nsMacShellService::OnLocationChange(nsIWebProgress* aWebProgress,
nsIRequest* aRequest, nsIURI* aLocation,
uint32_t aFlags) {
return NS_OK;
}
NS_IMETHODIMP
nsMacShellService::OnStatusChange(nsIWebProgress* aWebProgress,
nsIRequest* aRequest, nsresult aStatus,
const char16_t* aMessage) {
return NS_OK;
}
NS_IMETHODIMP
nsMacShellService::OnSecurityChange(nsIWebProgress* aWebProgress,
nsIRequest* aRequest, uint32_t aState) {
return NS_OK;
}
NS_IMETHODIMP
nsMacShellService::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
nsIRequest* aRequest,
uint32_t aEvent) {
return NS_OK;
}
NS_IMETHODIMP
nsMacShellService::OnStateChange(nsIWebProgress* aWebProgress,
nsIRequest* aRequest, uint32_t aStateFlags,
nsresult aStatus) {
if (aStateFlags & STATE_STOP) {
nsCOMPtr<nsIObserverService> os(
do_GetService("@mozilla.org/observer-service;1"));
if (os)
os->NotifyObservers(nullptr, "shell:desktop-background-changed", nullptr);
bool exists = false;
mBackgroundFile->Exists(&exists);
if (!exists) return NS_OK;
nsAutoCString nativePath;
mBackgroundFile->GetNativePath(nativePath);
AEDesc tAEDesc = {typeNull, nil};
OSErr err = noErr;
AliasHandle aliasHandle = nil;
FSRef pictureRef;
OSStatus status;
// Convert the path into a FSRef
status =
::FSPathMakeRef((const UInt8*)nativePath.get(), &pictureRef, nullptr);
if (status == noErr) {
err = ::FSNewAlias(nil, &pictureRef, &aliasHandle);
if (err == noErr && aliasHandle == nil) err = paramErr;
if (err == noErr) {
// We need the descriptor (based on the picture file reference)
// for the 'Set Desktop Picture' apple event.
char handleState = ::HGetState((Handle)aliasHandle);
::HLock((Handle)aliasHandle);
err = ::AECreateDesc(typeAlias, *aliasHandle,
GetHandleSize((Handle)aliasHandle), &tAEDesc);
// unlock the alias handler
::HSetState((Handle)aliasHandle, handleState);
::DisposeHandle((Handle)aliasHandle);
}
if (err == noErr) {
AppleEvent tAppleEvent;
OSType sig = 'MACS';
AEBuildError tAEBuildError;
// Create a 'Set Desktop Pictue' Apple Event
err =
::AEBuildAppleEvent(kAECoreSuite, kAESetData, typeApplSignature,
&sig, sizeof(OSType), kAutoGenerateReturnID,
kAnyTransactionID, &tAppleEvent, &tAEBuildError,
"'----':'obj '{want:type (prop),form:prop"
",seld:type('dpic'),from:'null'()},data:(@)",
&tAEDesc);
if (err == noErr) {
AppleEvent reply = {typeNull, nil};
// Sent the event we built, the reply event isn't necessary
err = ::AESend(&tAppleEvent, &reply, kAENoReply, kAENormalPriority,
kNoTimeOut, nil, nil);
::AEDisposeDesc(&tAppleEvent);
}
}
}
}
return NS_OK;
}
NS_IMETHODIMP
nsMacShellService::ShowDesktopPreferences() {
nsCOMPtr<nsIFile> lf;
nsresult rv =
NS_NewNativeLocalFile(DESKTOP_PREFPANE, true, getter_AddRefs(lf));
NS_ENSURE_SUCCESS(rv, rv);
bool exists;
lf->Exists(&exists);
if (!exists) return NS_ERROR_FILE_NOT_FOUND;
return lf->Launch();
}
NS_IMETHODIMP
nsMacShellService::GetDesktopBackgroundColor(uint32_t* aColor) {
// This method and |SetDesktopBackgroundColor| has no meaning on Mac OS X.
// The mac desktop preferences UI uses pictures for the few solid colors it
// supports.
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMacShellService::SetDesktopBackgroundColor(uint32_t aColor) {
// This method and |GetDesktopBackgroundColor| has no meaning on Mac OS X.
// The mac desktop preferences UI uses pictures for the few solid colors it
// supports.
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsMacShellService::ShowSecurityPreferences(const nsACString& aPaneID) {
nsresult rv = NS_ERROR_NOT_AVAILABLE;
CFStringRef paneID = ::CFStringCreateWithBytes(
kCFAllocatorDefault, (const UInt8*)PromiseFlatCString(aPaneID).get(),
aPaneID.Length(), kCFStringEncodingUTF8, false);
if (paneID) {
CFStringRef format =
CFSTR("x-apple.systempreferences:com.apple.preference.security?%@");
if (format) {
CFStringRef urlStr =
CFStringCreateWithFormat(kCFAllocatorDefault, NULL, format, paneID);
if (urlStr) {
CFURLRef url = ::CFURLCreateWithString(NULL, urlStr, NULL);
rv = CocoaFileUtils::OpenURL(url);
::CFRelease(urlStr);
}
::CFRelease(format);
}
::CFRelease(paneID);
}
return rv;
}