diff options
Diffstat (limited to 'toolkit')
71 files changed, 1800 insertions, 99 deletions
diff --git a/toolkit/components/apppicker/content/appPicker.js b/toolkit/components/apppicker/content/appPicker.js index 6922fce39f..21a007632b 100644 --- a/toolkit/components/apppicker/content/appPicker.js +++ b/toolkit/components/apppicker/content/appPicker.js @@ -119,6 +119,12 @@ AppPicker.prototype = return file.getVersionInfoField("FileDescription"); } catch (e) {} } +#elifdef XP_MACOSX + if (file instanceof Components.interfaces.nsILocalFileMac) { + try { + return file.bundleDisplayName; + } catch (e) {} + } #endif return file.leafName; }, @@ -177,6 +183,8 @@ AppPicker.prototype = var startLocation; #ifdef XP_WIN startLocation = "ProgF"; // Program Files +#elifdef XP_MACOSX + startLocation = "LocApp"; // Local Applications #else startLocation = "Home"; #endif diff --git a/toolkit/components/blocklist/nsBlocklistService.js b/toolkit/components/blocklist/nsBlocklistService.js index 188fdfb387..f016fe6cdc 100644 --- a/toolkit/components/blocklist/nsBlocklistService.js +++ b/toolkit/components/blocklist/nsBlocklistService.js @@ -114,6 +114,15 @@ XPCOMUtils.defineLazyGetter(this, "gABI", function() { LOG("BlockList Global gABI: XPCOM ABI unknown."); } +#ifdef XP_MACOSX + // Mac universal build should report a different ABI than either macppc + // or mactel. + let macutils = Cc["@mozilla.org/xpcom/mac-utils;1"]. + getService(Ci.nsIMacUtils); + + if (macutils.isUniversalBinary) + abi += "-u-" + macutils.architecturesInBinary; +#endif return abi; }); diff --git a/toolkit/components/downloads/nsDownloadManager.cpp b/toolkit/components/downloads/nsDownloadManager.cpp index bc01b9ae58..587c1ac8ab 100644 --- a/toolkit/components/downloads/nsDownloadManager.cpp +++ b/toolkit/components/downloads/nsDownloadManager.cpp @@ -57,6 +57,10 @@ #endif #endif +#ifdef XP_MACOSX +#include <CoreFoundation/CoreFoundation.h> +#endif + #ifdef MOZ_WIDGET_GTK #include <gtk/gtk.h> #endif @@ -1372,7 +1376,12 @@ nsDownloadManager::GetDefaultDownloadsDirectory(nsIFile **aResult) mBundle->GetStringFromName(u"downloadsFolder", getter_Copies(folderName)); -#if defined(XP_WIN) +#if defined (XP_MACOSX) + rv = dirService->Get(NS_OSX_DEFAULT_DOWNLOAD_DIR, + NS_GET_IID(nsIFile), + getter_AddRefs(downloadDir)); + NS_ENSURE_SUCCESS(rv, rv); +#elif defined(XP_WIN) rv = dirService->Get(NS_WIN_DEFAULT_DOWNLOAD_DIR, NS_GET_IID(nsIFile), getter_AddRefs(downloadDir)); @@ -2436,11 +2445,19 @@ nsDownloadManager::Observe(nsISupports *aSubject, nsCOMPtr<nsISupportsPRBool> cancelDownloads = do_QueryInterface(aSubject, &rv); NS_ENSURE_SUCCESS(rv, rv); +#ifndef XP_MACOSX ConfirmCancelDownloads(currDownloadCount, cancelDownloads, u"quitCancelDownloadsAlertTitle", u"quitCancelDownloadsAlertMsgMultiple", u"quitCancelDownloadsAlertMsg", u"dontQuitButtonWin"); +#else + ConfirmCancelDownloads(currDownloadCount, cancelDownloads, + u"quitCancelDownloadsAlertTitle", + u"quitCancelDownloadsAlertMsgMacMultiple", + u"quitCancelDownloadsAlertMsgMac", + u"dontQuitButtonMac"); +#endif } else if (strcmp(aTopic, "offline-requested") == 0 && currDownloadCount) { nsCOMPtr<nsISupportsPRBool> cancelDownloads = do_QueryInterface(aSubject, &rv); @@ -2730,7 +2747,7 @@ nsDownload::SetState(DownloadState aState) } } -#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) +#if defined(XP_WIN) || defined(XP_MACOSX) || defined(MOZ_WIDGET_GTK) nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mTarget); nsCOMPtr<nsIFile> file; nsAutoString path; @@ -2740,6 +2757,7 @@ nsDownload::SetState(DownloadState aState) file && NS_SUCCEEDED(file->GetPath(path))) { +#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) // On Windows and Gtk, add the download to the system's "recent documents" // list, with a pref to disable. { @@ -2777,6 +2795,18 @@ nsDownload::SetState(DownloadState aState) g_object_unref(gio_file); #endif } +#endif + +#ifdef XP_MACOSX + // On OS X, make the downloads stack bounce. + CFStringRef observedObject = ::CFStringCreateWithCString(kCFAllocatorDefault, + NS_ConvertUTF16toUTF8(path).get(), + kCFStringEncodingUTF8); + CFNotificationCenterRef center = ::CFNotificationCenterGetDistributedCenter(); + ::CFNotificationCenterPostNotification(center, CFSTR("com.apple.DownloadFileFinished"), + observedObject, nullptr, TRUE); + ::CFRelease(observedObject); +#endif } #ifdef XP_WIN @@ -3359,10 +3389,14 @@ nsDownload::OpenWithApplication() if (!prefs || NS_FAILED(prefs->GetBoolPref(PREF_BH_DELETETEMPFILEONEXIT, &deleteTempFileOnExit))) { // No prefservice or no pref set; use default value - // Some users have been very verbal about temp files being deleted on +#if !defined(XP_MACOSX) + // Mac users have been very verbal about temp files being deleted on // app exit - they don't like it - but we'll continue to do this on - // all platforms for now. + // other platforms for now. deleteTempFileOnExit = true; +#else + deleteTempFileOnExit = false; +#endif } // Always schedule files to be deleted at the end of the private browsing diff --git a/toolkit/components/jsdownloads/src/DownloadIntegration.jsm b/toolkit/components/jsdownloads/src/DownloadIntegration.jsm index 8b5c644987..22a6570dac 100644 --- a/toolkit/components/jsdownloads/src/DownloadIntegration.jsm +++ b/toolkit/components/jsdownloads/src/DownloadIntegration.jsm @@ -294,7 +294,9 @@ this.DownloadIntegration = { } let directoryPath = null; -#ifdef XP_WIN +#ifdef XP_MACOSX + directoryPath = this._getDirectory("DfltDwnld"); +#elifdef XP_WIN // For XP/2K, use My Documents/Downloads. Other version uses // the default Downloads directory. let version = parseFloat(Services.sysinfo.getProperty("version")); @@ -363,7 +365,11 @@ this.DownloadIntegration = { */ getTemporaryDownloadsDirectory: Task.async(function* () { let directoryPath = null; +#ifdef XP_MACOSX + directoryPath = yield this.getPreferredDownloadsDirectory(); +#else directoryPath = this._getDirectory("TmpD"); +#endif return directoryPath; }), diff --git a/toolkit/components/jsdownloads/src/DownloadPlatform.cpp b/toolkit/components/jsdownloads/src/DownloadPlatform.cpp index 14277e5bd4..66ad2b8fa8 100644 --- a/toolkit/components/jsdownloads/src/DownloadPlatform.cpp +++ b/toolkit/components/jsdownloads/src/DownloadPlatform.cpp @@ -25,6 +25,11 @@ #include "nsILocalFileWin.h" #endif +#ifdef XP_MACOSX +#include <CoreFoundation/CoreFoundation.h> +#include "../../../../xpcom/io/CocoaFileUtils.h" +#endif + #ifdef MOZ_WIDGET_GTK #include <gtk/gtk.h> #endif @@ -64,13 +69,39 @@ static void gio_set_metadata_done(GObject *source_obj, GAsyncResult *res, gpoint } #endif +#ifdef XP_MACOSX +// Caller is responsible for freeing any result (CF Create Rule) +CFURLRef CreateCFURLFromNSIURI(nsIURI *aURI) { + nsAutoCString spec; + if (aURI) { + aURI->GetSpec(spec); + } + + CFStringRef urlStr = ::CFStringCreateWithCString(kCFAllocatorDefault, + spec.get(), + kCFStringEncodingUTF8); + if (!urlStr) { + return NULL; + } + + CFURLRef url = ::CFURLCreateWithString(kCFAllocatorDefault, + urlStr, + NULL); + + ::CFRelease(urlStr); + + return url; +} +#endif + nsresult DownloadPlatform::DownloadDone(nsIURI* aSource, nsIURI* aReferrer, nsIFile* aTarget, const nsACString& aContentType, bool aIsPrivate) { -#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) +#if defined(XP_WIN) || defined(XP_MACOSX) || defined(MOZ_WIDGET_GTK) nsAutoString path; if (aTarget && NS_SUCCEEDED(aTarget->GetPath(path))) { +#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) // On Windows and Gtk, add the download to the system's "recent documents" // list, with a pref to disable. { @@ -105,10 +136,53 @@ nsresult DownloadPlatform::DownloadDone(nsIURI* aSource, nsIURI* aReferrer, nsIF nullptr, gio_set_metadata_done, nullptr); g_object_unref(file_info); g_object_unref(gio_file); -#endif // MOZ_ENABLE_GIO +#endif } +#endif + +#ifdef XP_MACOSX + // On OS X, make the downloads stack bounce. + CFStringRef observedObject = ::CFStringCreateWithCString(kCFAllocatorDefault, + NS_ConvertUTF16toUTF8(path).get(), + kCFStringEncodingUTF8); + CFNotificationCenterRef center = ::CFNotificationCenterGetDistributedCenter(); + ::CFNotificationCenterPostNotification(center, CFSTR("com.apple.DownloadFileFinished"), + observedObject, nullptr, TRUE); + ::CFRelease(observedObject); + + // Add OS X origin and referrer file metadata + CFStringRef pathCFStr = NULL; + if (!path.IsEmpty()) { + pathCFStr = ::CFStringCreateWithCharacters(kCFAllocatorDefault, + (const UniChar*)path.get(), + path.Length()); + } + if (pathCFStr) { + bool isFromWeb = IsURLPossiblyFromWeb(aSource); + + CFURLRef sourceCFURL = CreateCFURLFromNSIURI(aSource); + CFURLRef referrerCFURL = CreateCFURLFromNSIURI(aReferrer); + + CocoaFileUtils::AddOriginMetadataToFile(pathCFStr, + sourceCFURL, + referrerCFURL); + CocoaFileUtils::AddQuarantineMetadataToFile(pathCFStr, + sourceCFURL, + referrerCFURL, + isFromWeb); + + ::CFRelease(pathCFStr); + if (sourceCFURL) { + ::CFRelease(sourceCFURL); + } + if (referrerCFURL) { + ::CFRelease(referrerCFURL); + } + } +#endif } -#endif // defined(XP_WIN) || defined(MOZ_WIDGET_GTK) + +#endif return NS_OK; } diff --git a/toolkit/components/jsdownloads/src/DownloadUIHelper.jsm b/toolkit/components/jsdownloads/src/DownloadUIHelper.jsm index 860a325d4b..3039525f5f 100644 --- a/toolkit/components/jsdownloads/src/DownloadUIHelper.jsm +++ b/toolkit/components/jsdownloads/src/DownloadUIHelper.jsm @@ -184,10 +184,17 @@ this.DownloadPrompter.prototype = { switch (aPromptType) { case this.ON_QUIT: title = s.quitCancelDownloadsAlertTitle; +#ifdef XP_MACOSX + message = aDownloadsCount > 1 + ? s.quitCancelDownloadsAlertMsgMacMultiple(aDownloadsCount) + : s.quitCancelDownloadsAlertMsgMac; + cancelButton = s.dontQuitButtonMac; +#else message = aDownloadsCount > 1 ? s.quitCancelDownloadsAlertMsgMultiple(aDownloadsCount) : s.quitCancelDownloadsAlertMsg; cancelButton = s.dontQuitButtonWin; +#endif break; case this.ON_OFFLINE: title = s.offlineCancelDownloadsAlertTitle; diff --git a/toolkit/components/jsdownloads/src/moz.build b/toolkit/components/jsdownloads/src/moz.build index 115435f641..5a08340ae2 100644 --- a/toolkit/components/jsdownloads/src/moz.build +++ b/toolkit/components/jsdownloads/src/moz.build @@ -18,11 +18,11 @@ EXTRA_JS_MODULES += [ 'DownloadList.jsm', 'Downloads.jsm', 'DownloadStore.jsm', - 'DownloadUIHelper.jsm', ] EXTRA_PP_JS_MODULES += [ 'DownloadIntegration.jsm', + 'DownloadUIHelper.jsm', ] FINAL_LIBRARY = 'xul' diff --git a/toolkit/components/passwordmgr/content/passwordManager.js b/toolkit/components/passwordmgr/content/passwordManager.js index 4db3e81b9a..2d71d05f64 100644 --- a/toolkit/components/passwordmgr/content/passwordManager.js +++ b/toolkit/components/passwordmgr/content/passwordManager.js @@ -479,7 +479,12 @@ function HandleSignonKeyPress(e) { return; } +#ifdef XP_MACOSX + if (e.keyCode == KeyboardEvent.DOM_VK_DELETE || + e.keyCode == KeyboardEvent.DOM_VK_BACK_SPACE) { +#else if (e.keyCode == KeyboardEvent.DOM_VK_DELETE) { +#endif DeleteSignon(); } } diff --git a/toolkit/components/passwordmgr/content/passwordManager.xul b/toolkit/components/passwordmgr/content/passwordManager.xul index b2713ae6c7..8590d96aca 100644 --- a/toolkit/components/passwordmgr/content/passwordManager.xul +++ b/toolkit/components/passwordmgr/content/passwordManager.xul @@ -1,4 +1,4 @@ -<?xml version="1.0"?> <!-- -*- Mode: XML; indent-tabs-mode: nil -*- --> +<?xml version="1.0"?> <!-- -*- Mode: SGML; indent-tabs-mode: nil -*- --> # 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/. @@ -126,8 +126,10 @@ <hbox align="end"> <hbox class="actionButtons" flex="1"> <spacer flex="1"/> +#ifndef XP_MACOSX <button oncommand="close();" icon="close" label="&closebutton.label;" accesskey="&closebutton.accesskey;"/> +#endif </hbox> </hbox> </window> diff --git a/toolkit/components/perfmonitoring/nsPerformanceStats.cpp b/toolkit/components/perfmonitoring/nsPerformanceStats.cpp index 15a021bbab..59d84ced15 100644 --- a/toolkit/components/perfmonitoring/nsPerformanceStats.cpp +++ b/toolkit/components/perfmonitoring/nsPerformanceStats.cpp @@ -35,7 +35,14 @@ #include <unistd.h> #endif // defined(XP_WIN) -#if defined(XP_UNIX) +#if defined(XP_MACOSX) +#include <mach/mach_init.h> +#include <mach/mach_interface.h> +#include <mach/mach_port.h> +#include <mach/mach_types.h> +#include <mach/message.h> +#include <mach/thread_info.h> +#elif defined(XP_UNIX) #include <sys/time.h> #include <sys/resource.h> #endif // defined(XP_UNIX) @@ -1232,7 +1239,30 @@ nsPerformanceStatsService::GetResources(uint64_t* userTime, MOZ_ASSERT(userTime); MOZ_ASSERT(systemTime); -#if defined(XP_UNIX) +#if defined(XP_MACOSX) + // On MacOS X, to get we per-thread data, we need to + // reach into the kernel. + + mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT; + thread_basic_info_data_t info; + mach_port_t port = mach_thread_self(); + kern_return_t err = + thread_info(/* [in] targeted thread*/ port, + /* [in] nature of information*/ THREAD_BASIC_INFO, + /* [out] thread information */ (thread_info_t)&info, + /* [inout] number of items */ &count); + + // We do not need ability to communicate with the thread, so + // let's release the port. + mach_port_deallocate(mach_task_self(), port); + + if (err != KERN_SUCCESS) + return NS_ERROR_FAILURE; + + *userTime = info.user_time.microseconds + info.user_time.seconds * 1000000; + *systemTime = info.system_time.microseconds + info.system_time.seconds * 1000000; + +#elif defined(XP_UNIX) struct rusage rusage; #if defined(RUSAGE_THREAD) // Under Linux, we can obtain per-thread statistics @@ -1276,7 +1306,7 @@ nsPerformanceStatsService::GetResources(uint64_t* userTime, // Convert 100 ns to 1 us. *userTime = userTimeInt.QuadPart / 10; -#endif // defined(XP_UNIX) || defined(XP_WIN) +#endif // defined(XP_MACOSX) || defined(XP_UNIX) || defined(XP_WIN) return NS_OK; } diff --git a/toolkit/components/places/PlacesUtils.jsm b/toolkit/components/places/PlacesUtils.jsm index ab753ba86b..259fb7aa7d 100644 --- a/toolkit/components/places/PlacesUtils.jsm +++ b/toolkit/components/places/PlacesUtils.jsm @@ -60,8 +60,14 @@ XPCOMUtils.defineLazyModuleGetter(this, "PlacesUIUtils", // refresh instead. const MIN_TRANSACTIONS_FOR_BATCH = 5; -// The transferable system converts "\r\n" to "\n" where needed. +// On Mac OSX, the transferable system converts "\r\n" to "\n\n", where +// we really just want "\n". On other platforms, the transferable system +// converts "\r\n" to "\n". +#ifdef XP_MACOSX +const NEWLINE = "\n"; +#else const NEWLINE = "\r\n"; +#endif function QI_node(aNode, aIID) { var result = null; diff --git a/toolkit/components/places/moz.build b/toolkit/components/places/moz.build index 4c6ce89565..fda73f761f 100644 --- a/toolkit/components/places/moz.build +++ b/toolkit/components/places/moz.build @@ -68,9 +68,10 @@ if CONFIG['MOZ_PLACES']: 'PlacesSearchAutocompleteProvider.jsm', 'PlacesSyncUtils.jsm', 'PlacesTransactions.jsm', - 'PlacesUtils.jsm', ] + EXTRA_PP_JS_MODULES += ['PlacesUtils.jsm'] + EXTRA_COMPONENTS += [ 'ColorAnalyzer.js', 'nsLivemarkService.js', diff --git a/toolkit/components/printing/jar.mn b/toolkit/components/printing/jar.mn index f77cf1c6aa..a0e9510304 100644 --- a/toolkit/components/printing/jar.mn +++ b/toolkit/components/printing/jar.mn @@ -6,9 +6,11 @@ toolkit.jar: content/global/printdialog.js (content/printdialog.js) content/global/printdialog.xul (content/printdialog.xul) #ifdef XP_UNIX +#ifndef XP_MACOSX content/global/printjoboptions.js (content/printjoboptions.js) content/global/printjoboptions.xul (content/printjoboptions.xul) #endif +#endif content/global/printPageSetup.js (content/printPageSetup.js) content/global/printPageSetup.xul (content/printPageSetup.xul) * content/global/printPreviewBindings.xml (content/printPreviewBindings.xml) diff --git a/toolkit/components/prompts/content/commonDialog.xul b/toolkit/components/prompts/content/commonDialog.xul index a7621ccdfb..990b26586b 100644 --- a/toolkit/components/prompts/content/commonDialog.xul +++ b/toolkit/components/prompts/content/commonDialog.xul @@ -3,6 +3,7 @@ - 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/. --> + <?xml-stylesheet href="chrome://global/skin/" type="text/css"?> <?xml-stylesheet href="chrome://global/content/commonDialog.css" type="text/css"?> <?xml-stylesheet href="chrome://global/skin/commonDialog.css" type="text/css"?> @@ -62,9 +63,18 @@ <hbox id="iconContainer" align="start"> <image id="info.icon" class="spaced"/> </hbox> - <vbox id="infoContainer" pack="center"> + <vbox id="infoContainer" +#ifndef XP_MACOSX + pack="center" +#endif + > + <!-- Only shown on OS X, since it has no dialog title --> <description id="info.title" +#ifndef XP_MACOSX hidden="true" +#else + style="margin-bottom: 1em" +#endif /> <description id="info.body" context="contentAreaContextMenu" noinitialfocus="true"/> </vbox> diff --git a/toolkit/components/prompts/content/tabprompts.xml b/toolkit/components/prompts/content/tabprompts.xml index 5355bb4cff..0ce13203c1 100644 --- a/toolkit/components/prompts/content/tabprompts.xml +++ b/toolkit/components/prompts/content/tabprompts.xml @@ -315,16 +315,27 @@ group="system" action="this.onKeyAction('default', event);"/> <handler event="keypress" keycode="VK_ESCAPE" group="system" action="this.onKeyAction('cancel', event);"/> +#ifdef XP_MACOSX + <handler event="keypress" key="." modifiers="meta" + group="system" action="this.onKeyAction('cancel', event);"/> +#endif <handler event="focus" phase="capturing"> let bnum = this.args.defaultButtonNum || 0; let defaultButton = this.ui["button" + bnum]; - // The default button is only marked as such when no other button has focus. - // XUL buttons will react to pressing enter as a command, so you can't trigger - // the default without tabbing to it or something that isn't a button. +#ifdef XP_MACOSX + // On OS X, the default button always stays marked as such (until + // the entire prompt blurs). + defaultButton.setAttribute("default", true); +#else + // On other platforms, the default button is only marked as such + // when no other button has focus. XUL buttons on not-OSX will + // react to pressing enter as a command, so you can't trigger the + // default without tabbing to it or something that isn't a button. let focusedDefault = (event.originalTarget == defaultButton); let someButtonFocused = event.originalTarget instanceof Ci.nsIDOMXULButtonElement; defaultButton.setAttribute("default", focusedDefault || !someButtonFocused); +#endif </handler> <handler event="blur"> // If focus shifted to somewhere else in the browser, don't make diff --git a/toolkit/components/prompts/jar.mn b/toolkit/components/prompts/jar.mn index 1b5da3f801..60ecbdcbc4 100644 --- a/toolkit/components/prompts/jar.mn +++ b/toolkit/components/prompts/jar.mn @@ -4,7 +4,7 @@ toolkit.jar: content/global/commonDialog.js (content/commonDialog.js) - content/global/commonDialog.xul (content/commonDialog.xul) +* content/global/commonDialog.xul (content/commonDialog.xul) content/global/commonDialog.css (content/commonDialog.css) content/global/selectDialog.js (content/selectDialog.js) content/global/selectDialog.xul (content/selectDialog.xul) diff --git a/toolkit/components/satchel/nsFormFillController.cpp b/toolkit/components/satchel/nsFormFillController.cpp index 880ca79b21..bebc60f521 100644 --- a/toolkit/components/satchel/nsFormFillController.cpp +++ b/toolkit/components/satchel/nsFormFillController.cpp @@ -1016,11 +1016,27 @@ nsFormFillController::KeyPress(nsIDOMEvent* aEvent) keyEvent->GetKeyCode(&k); switch (k) { case nsIDOMKeyEvent::DOM_VK_DELETE: +#ifndef XP_MACOSX mController->HandleDelete(&cancel); break; case nsIDOMKeyEvent::DOM_VK_BACK_SPACE: mController->HandleText(&unused); break; +#else + case nsIDOMKeyEvent::DOM_VK_BACK_SPACE: + { + bool isShift = false; + keyEvent->GetShiftKey(&isShift); + + if (isShift) { + mController->HandleDelete(&cancel); + } else { + mController->HandleText(&unused); + } + + break; + } +#endif case nsIDOMKeyEvent::DOM_VK_PAGE_UP: case nsIDOMKeyEvent::DOM_VK_PAGE_DOWN: { diff --git a/toolkit/components/startup/nsAppStartup.cpp b/toolkit/components/startup/nsAppStartup.cpp index 11a000270e..7f67323b7d 100644 --- a/toolkit/components/startup/nsAppStartup.cpp +++ b/toolkit/components/startup/nsAppStartup.cpp @@ -273,6 +273,10 @@ nsAppStartup::Run(void) // with a zombie process. if (!mShuttingDown && mConsiderQuitStopper != 0) { +#ifdef XP_MACOSX + EnterLastWindowClosingSurvivalArea(); +#endif + mRunning = true; nsresult rv = mAppShell->Run(); @@ -308,10 +312,45 @@ nsAppStartup::Quit(uint32_t aMode) // If we're considering quitting, we will only do so if: if (ferocity == eConsiderQuit) { +#ifdef XP_MACOSX + nsCOMPtr<nsIAppShellService> appShell + (do_GetService(NS_APPSHELLSERVICE_CONTRACTID)); + bool hasHiddenPrivateWindow = false; + if (appShell) { + appShell->GetHasHiddenPrivateWindow(&hasHiddenPrivateWindow); + } + int32_t suspiciousCount = hasHiddenPrivateWindow ? 2 : 1; +#endif + if (mConsiderQuitStopper == 0) { // there are no windows... ferocity = eAttemptQuit; } +#ifdef XP_MACOSX + else if (mConsiderQuitStopper == suspiciousCount) { + // ... or there is only a hiddenWindow left, and it's useless: + + // Failure shouldn't be fatal, but will abort quit attempt: + if (!appShell) + return NS_OK; + + bool usefulHiddenWindow; + appShell->GetApplicationProvidedHiddenWindow(&usefulHiddenWindow); + nsCOMPtr<nsIXULWindow> hiddenWindow; + appShell->GetHiddenWindow(getter_AddRefs(hiddenWindow)); + // If the remaining windows are useful, we won't quit: + nsCOMPtr<nsIXULWindow> hiddenPrivateWindow; + if (hasHiddenPrivateWindow) { + appShell->GetHiddenPrivateWindow(getter_AddRefs(hiddenPrivateWindow)); + if ((!hiddenWindow && !hiddenPrivateWindow) || usefulHiddenWindow) + return NS_OK; + } else if (!hiddenWindow || usefulHiddenWindow) { + return NS_OK; + } + + ferocity = eAttemptQuit; + } +#endif } nsCOMPtr<nsIObserverService> obsService; @@ -365,6 +404,10 @@ nsAppStartup::Quit(uint32_t aMode) if (!mAttemptingQuit) { mAttemptingQuit = true; +#ifdef XP_MACOSX + // now even the Mac wants to quit when the last window is closed + ExitLastWindowClosingSurvivalArea(); +#endif if (obsService) obsService->NotifyObservers(nullptr, "quit-application-granted", nullptr); } diff --git a/toolkit/components/thumbnails/PageThumbUtils.jsm b/toolkit/components/thumbnails/PageThumbUtils.jsm index f91b364800..fb5d67ddb5 100644 --- a/toolkit/components/thumbnails/PageThumbUtils.jsm +++ b/toolkit/components/thumbnails/PageThumbUtils.jsm @@ -71,6 +71,18 @@ this.PageThumbUtils = { let windowScale = aWindow ? aWindow.devicePixelRatio : systemScale; let scale = Math.max(systemScale, windowScale); +#ifdef XP_MACOSX + /** * + * On retina displays, we can sometimes go down this path + * without a window object. In those cases, force 2x scaling + * as the system scale doesn't represent the 2x scaling + * on OS X. + */ + if (!aWindow) { + scale = 2; + } +#endif + /** * * THESE VALUES ARE DEFINED IN newtab.css and hard coded. * If you change these values from the prefs, diff --git a/toolkit/components/thumbnails/moz.build b/toolkit/components/thumbnails/moz.build index 957bc7df0d..92ff2af946 100644 --- a/toolkit/components/thumbnails/moz.build +++ b/toolkit/components/thumbnails/moz.build @@ -11,11 +11,11 @@ EXTRA_COMPONENTS += [ EXTRA_JS_MODULES += [ 'PageThumbs.jsm', 'PageThumbsWorker.js', - 'PageThumbUtils.jsm', ] EXTRA_PP_JS_MODULES += [ 'BackgroundPageThumbs.jsm', + 'PageThumbUtils.jsm', ] JAR_MANIFESTS += ['jar.mn'] diff --git a/toolkit/components/viewsource/content/viewPartialSource.xul b/toolkit/components/viewsource/content/viewPartialSource.xul index c51744fd21..fdec367b1c 100644 --- a/toolkit/components/viewsource/content/viewPartialSource.xul +++ b/toolkit/components/viewsource/content/viewPartialSource.xul @@ -1,8 +1,8 @@ <?xml version="1.0"?> -<!-- -*- Mode: XML -*- - 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/. --> +# -*- Mode: HTML -*- +# 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/. <?xml-stylesheet href="chrome://global/skin/" type="text/css"?> <?xml-stylesheet href="chrome://global/content/viewSource.css" type="text/css"?> @@ -99,8 +99,10 @@ label="&savePageCmd.label;" accesskey="&savePageCmd.accesskey;"/> <menuitem command="cmd_pagesetup" id="menu_pageSetup" label="&pageSetupCmd.label;" accesskey="&pageSetupCmd.accesskey;"/> +#ifndef XP_MACOSX <menuitem command="cmd_printpreview" id="menu_printPreview" label="&printPreviewCmd.label;" accesskey="&printPreviewCmd.accesskey;"/> +#endif <menuitem key="key_print" command="cmd_print" id="menu_print" label="&printCmd.label;" accesskey="&printCmd.accesskey;"/> <menuseparator/> diff --git a/toolkit/components/viewsource/content/viewSource.xul b/toolkit/components/viewsource/content/viewSource.xul index 3ad45a9d9d..c6ca58234e 100644 --- a/toolkit/components/viewsource/content/viewSource.xul +++ b/toolkit/components/viewsource/content/viewSource.xul @@ -1,5 +1,5 @@ <?xml version="1.0"?> -# -*- Mode: XML -*- +# -*- Mode: HTML -*- # 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/. @@ -50,6 +50,10 @@ oncommand="document.getElementById('FindToolbar').onFindAgainCommand(false);"/> <command id="cmd_findPrevious" oncommand="document.getElementById('FindToolbar').onFindAgainCommand(true);"/> +#ifdef XP_MACOSX + <command id="cmd_findSelection" + oncommand="document.getElementById('FindToolbar').onFindSelectionCommand();"/> +#endif <command id="cmd_reload" oncommand="viewSourceChrome.reload();"/> <command id="cmd_goToLine" oncommand="viewSourceChrome.promptAndGoToLine();" disabled="true"/> <command id="cmd_highlightSyntax" oncommand="viewSourceChrome.toggleSyntaxHighlighting();"/> @@ -85,13 +89,21 @@ <key id="key_find" key="&findOnCmd.commandkey;" command="cmd_find" modifiers="accel"/> <key id="key_findAgain" key="&findAgainCmd.commandkey;" command="cmd_findAgain" modifiers="accel"/> <key id="key_findPrevious" key="&findAgainCmd.commandkey;" command="cmd_findPrevious" modifiers="accel,shift"/> +#ifdef XP_MACOSX + <key id="key_findSelection" key="&findSelectionCmd.commandkey;" command="cmd_findSelection" modifiers="accel"/> +#endif <key keycode="&findAgainCmd.commandkey2;" command="cmd_findAgain"/> <key keycode="&findAgainCmd.commandkey2;" command="cmd_findPrevious" modifiers="shift"/> <key keycode="VK_BACK" command="Browser:Back"/> <key keycode="VK_BACK" command="Browser:Forward" modifiers="shift"/> +#ifndef XP_MACOSX <key id="goBackKb" keycode="VK_LEFT" command="Browser:Back" modifiers="alt"/> <key id="goForwardKb" keycode="VK_RIGHT" command="Browser:Forward" modifiers="alt"/> +#else + <key id="goBackKb" keycode="VK_LEFT" command="Browser:Back" modifiers="accel" /> + <key id="goForwardKb" keycode="VK_RIGHT" command="Browser:Forward" modifiers="accel" /> +#endif #ifdef XP_UNIX <key id="goBackKb2" key="&goBackCmd.commandKey;" command="Browser:Back" modifiers="accel"/> <key id="goForwardKb2" key="&goForwardCmd.commandKey;" command="Browser:Forward" modifiers="accel"/> @@ -138,8 +150,10 @@ label="&savePageCmd.label;" accesskey="&savePageCmd.accesskey;"/> <menuitem command="cmd_pagesetup" id="menu_pageSetup" label="&pageSetupCmd.label;" accesskey="&pageSetupCmd.accesskey;"/> +#ifndef XP_MACOSX <menuitem command="cmd_printpreview" id="menu_printPreview" label="&printPreviewCmd.label;" accesskey="&printPreviewCmd.accesskey;"/> +#endif <menuitem key="key_print" command="cmd_print" id="menu_print" label="&printCmd.label;" accesskey="&printCmd.accesskey;"/> <menuseparator/> diff --git a/toolkit/components/viewsource/jar.mn b/toolkit/components/viewsource/jar.mn index 33818ae0db..00a1f19a46 100644 --- a/toolkit/components/viewsource/jar.mn +++ b/toolkit/components/viewsource/jar.mn @@ -7,6 +7,6 @@ toolkit.jar: content/global/viewSource.js (content/viewSource.js) * content/global/viewSource.xul (content/viewSource.xul) content/global/viewPartialSource.js (content/viewPartialSource.js) - content/global/viewPartialSource.xul (content/viewPartialSource.xul) +* content/global/viewPartialSource.xul (content/viewPartialSource.xul) content/global/viewSourceUtils.js (content/viewSourceUtils.js) content/global/viewSource-content.js (content/viewSource-content.js) diff --git a/toolkit/content/aboutProfiles.js b/toolkit/content/aboutProfiles.js index 0e548fd0ff..29c5f67ad7 100644 --- a/toolkit/content/aboutProfiles.js +++ b/toolkit/content/aboutProfiles.js @@ -131,6 +131,8 @@ function display(profileData) { let button = document.createElement('button'); #ifdef XP_WIN let string = 'winOpenDir2'; +#elifdef XP_MACOSX + let string = 'macOpenDir'; #else let string = 'openDir'; #endif diff --git a/toolkit/content/aboutSupport.xhtml b/toolkit/content/aboutSupport.xhtml index 4ae9927399..8afee18676 100644 --- a/toolkit/content/aboutSupport.xhtml +++ b/toolkit/content/aboutSupport.xhtml @@ -156,8 +156,12 @@ #ifdef XP_WIN &aboutSupport.appBasicsProfileDirWinMac; #else +#ifdef XP_MACOSX + &aboutSupport.appBasicsProfileDirWinMac; +#else &aboutSupport.appBasicsProfileDir; #endif +#endif </th> <td> @@ -165,8 +169,12 @@ #ifdef XP_WIN &aboutSupport.showWin2.label; #else +#ifdef XP_MACOSX + &aboutSupport.showMac.label; +#else &aboutSupport.showDir.label; #endif +#endif </button> </td> </tr> diff --git a/toolkit/content/customizeToolbar.js b/toolkit/content/customizeToolbar.js index 1775d9f52a..05151b905d 100644 --- a/toolkit/content/customizeToolbar.js +++ b/toolkit/content/customizeToolbar.js @@ -212,6 +212,10 @@ function wrapToolbarItems() { forEachCustomizableToolbar(function (toolbar) { Array.forEach(toolbar.childNodes, function (item) { +#ifdef XP_MACOSX + if (item.firstChild && item.firstChild.localName == "menubar") + return; +#endif if (isToolbarItem(item)) { let wrapper = wrapToolbarItem(item); cleanupItemForToolbar(item, wrapper); diff --git a/toolkit/content/dialogOverlay.xul b/toolkit/content/dialogOverlay.xul index 9d4d8b6138..09e00613e1 100644 --- a/toolkit/content/dialogOverlay.xul +++ b/toolkit/content/dialogOverlay.xul @@ -1,11 +1,11 @@ <?xml version="1.0"?> -<!-- -*- Mode: XML -*- - 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/. - - WARNING!!! This file is obsoleted by the dialog.xml widget ---> +# -*- Mode: HTML -*- +# 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/. +# +# WARNING!!! This file is obsoleted by the dialog.xml widget +# <!DOCTYPE overlay SYSTEM "chrome://global/locale/dialogOverlay.dtd"> @@ -14,6 +14,44 @@ <script type="application/javascript" src="chrome://global/content/dialogOverlay.js"/> +#ifdef XP_MACOSX +# + <hbox id="okCancelButtons"> + <spacer flex="1"/> + <button class="exit-dialog" id="Button3" label="" collapsed="true" oncommand="doButton3();"/> + <button class="exit-dialog" id="Button2" label="" collapsed="true" oncommand="doButton2();"/> + <button class="exit-dialog" id="cancel" label="&cancelButton.label;" oncommand="doCancelButton();"/> + <button class="right exit-dialog" id="ok" default="true" label="&okButton.label;" oncommand="doOKButton();"/> + </hbox> + + <hbox id="okCancelHelpButtons"> + <button class="exit-dialog" id="help" label="&helpButton.label;" oncommand="doHelpButton();"/> + <spacer flex="1"/> + <button class="exit-dialog" id="Button3" label="" collapsed="true" oncommand="doButton3();"/> + <button class="exit-dialog" id="Button2" label="" collapsed="true" oncommand="doButton2();"/> + <button class="exit-dialog" id="cancel" label="&cancelButton.label;" oncommand="doCancelButton();"/> + <button class="right exit-dialog" id="ok" default="true" label="&okButton.label;" oncommand="doOKButton();"/> + </hbox> + + <hbox id="okCancelButtonsRight"> + <spacer flex="1"/> + <button class="exit-dialog" id="Button3" label="" collapsed="true" oncommand="doButton3();"/> + <button class="exit-dialog" id="Button2" label="" collapsed="true" oncommand="doButton2();"/> + <button class="exit-dialog" id="cancel" label="&cancelButton.label;" oncommand="doCancelButton();"/> + <button class="right exit-dialog" id="ok" default="true" label="&okButton.label;" oncommand="doOKButton();"/> + </hbox> + + <hbox id="okCancelHelpButtonsRight"> + <button class="exit-dialog" id="help" label="&helpButton.label;" oncommand="doHelpButton();"/> + <spacer flex="1"/> + <button class="exit-dialog" id="Button3" label="" collapsed="true" oncommand="doButton3();"/> + <button class="exit-dialog" id="Button2" label="" collapsed="true" oncommand="doButton2();"/> + <button class="exit-dialog" id="cancel" label="&cancelButton.label;" oncommand="doCancelButton();"/> + <button class="right exit-dialog" id="ok" default="true" label="&okButton.label;" oncommand="doOKButton();"/> + </hbox> +# +#else +# <hbox id="okCancelButtons"> <spacer flex="1"/> <button class="right exit-dialog" id="ok" label="&okButton.label;" default="true" oncommand="doOKButton();"/> @@ -49,10 +87,15 @@ <button class="exit-dialog" id="cancel" label="&cancelButton.label;" oncommand="doCancelButton();"/> <button class="exit-dialog" id="help" label="&helpButton.label;" oncommand="doHelpButton();"/> </hbox> +#endif <keyset id="dialogKeys"> <key keycode="VK_RETURN" oncommand="if (!document.getElementById('ok').disabled) doOKButton();"/> <key keycode="VK_ESCAPE" oncommand="doCancelButton();"/> +#ifdef XP_MACOSX + <key key="." modifiers="meta" oncommand="doCancelButton();"/> +# +#endif </keyset> </overlay> diff --git a/toolkit/content/globalOverlay.js b/toolkit/content/globalOverlay.js index d5ee131916..d8467f0a12 100644 --- a/toolkit/content/globalOverlay.js +++ b/toolkit/content/globalOverlay.js @@ -4,6 +4,12 @@ function closeWindow(aClose, aPromptFunction) { +#ifdef XP_MACOSX + // Closing the last window doesn't quit the application on OS X. + if (typeof(aPromptFunction) == "function" && !aPromptFunction()) { + return false; + } +#else var windowCount = 0; var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"] .getService(Components.interfaces.nsIWindowMediator); @@ -23,6 +29,7 @@ function closeWindow(aClose, aPromptFunction) return false; if (windowCount != 1 && typeof(aPromptFunction) == "function" && !aPromptFunction()) return false; +#endif if (aClose) { window.close(); diff --git a/toolkit/content/jar.mn b/toolkit/content/jar.mn index 1f1c880397..6494645086 100644 --- a/toolkit/content/jar.mn +++ b/toolkit/content/jar.mn @@ -15,7 +15,7 @@ toolkit.jar: #ifdef MOZ_OFFICIAL_BRANDING * content/global/aboutRights.xhtml #else - content/global/aboutRights.xhtml (aboutRights-unbranded.xhtml) +* content/global/aboutRights.xhtml (aboutRights-unbranded.xhtml) #endif content/global/aboutNetworking.js content/global/aboutNetworking.xhtml @@ -38,7 +38,7 @@ toolkit.jar: * content/global/buildconfig.html * content/global/contentAreaUtils.js content/global/customizeToolbar.css - content/global/customizeToolbar.js +* content/global/customizeToolbar.js content/global/customizeToolbar.xul content/global/datepicker.xhtml content/global/editMenuOverlay.js @@ -47,7 +47,7 @@ toolkit.jar: * content/global/finddialog.xul content/global/findUtils.js content/global/filepicker.properties - content/global/globalOverlay.js +* content/global/globalOverlay.js content/global/mozilla.xhtml #ifdef MOZ_PHOENIX content/global/logopage.xhtml @@ -63,7 +63,7 @@ toolkit.jar: content/global/treeUtils.js content/global/viewZoomOverlay.js content/global/globalOverlay.xul - content/global/dialogOverlay.xul +* content/global/dialogOverlay.xul content/global/dialogOverlay.js content/global/inlineSpellCheckUI.js content/global/nsClipboard.js @@ -85,7 +85,7 @@ toolkit.jar: content/global/bindings/editor.xml (widgets/editor.xml) content/global/bindings/expander.xml (widgets/expander.xml) content/global/bindings/filefield.xml (widgets/filefield.xml) - content/global/bindings/findbar.xml (widgets/findbar.xml) +* content/global/bindings/findbar.xml (widgets/findbar.xml) content/global/bindings/general.xml (widgets/general.xml) content/global/bindings/groupbox.xml (widgets/groupbox.xml) content/global/bindings/listbox.xml (widgets/listbox.xml) @@ -114,10 +114,13 @@ toolkit.jar: content/global/bindings/timepicker.js (widgets/timepicker.js) content/global/bindings/toolbar.xml (widgets/toolbar.xml) content/global/bindings/toolbarbutton.xml (widgets/toolbarbutton.xml) - content/global/bindings/tree.xml (widgets/tree.xml) +* content/global/bindings/tree.xml (widgets/tree.xml) content/global/bindings/videocontrols.xml (widgets/videocontrols.xml) content/global/bindings/videocontrols.css (widgets/videocontrols.css) * content/global/bindings/wizard.xml (widgets/wizard.xml) +#ifdef XP_MACOSX + content/global/macWindowMenu.js +#endif content/global/svg/svgBindings.xml (/layout/svg/resources/content/svgBindings.xml) content/global/gmp-sources/openh264.json (gmp-sources/openh264.json) content/global/gmp-sources/widevinecdm.json (gmp-sources/widevinecdm.json) diff --git a/toolkit/content/license.html b/toolkit/content/license.html index 94d39959b2..0de306f36e 100644 --- a/toolkit/content/license.html +++ b/toolkit/content/license.html @@ -5341,7 +5341,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. </pre> -#if defined(XP_WIN) || defined(XP_LINUX) +#if defined(XP_WIN) || defined(XP_MACOSX) || defined(XP_LINUX) <hr> diff --git a/toolkit/content/macWindowMenu.inc b/toolkit/content/macWindowMenu.inc new file mode 100644 index 0000000000..c345ad8b7f --- /dev/null +++ b/toolkit/content/macWindowMenu.inc @@ -0,0 +1,40 @@ + <script type="application/javascript" src="chrome://global/content/macWindowMenu.js"/> + <commandset id="baseMenuCommandSet"> + <command id="minimizeWindow" + label="&minimizeWindow.label;" + oncommand="window.minimize();" /> + <command id="zoomWindow" + label="&zoomWindow.label;" + oncommand="zoomWindow();" /> + </commandset> + <keyset id="baseMenuKeyset"> + <key id="key_minimizeWindow" + command="minimizeWindow" + key="&minimizeWindow.key;" + modifiers="accel"/> + </keyset> + <menu id="windowMenu" + label="&windowMenu.label;" + datasources="rdf:window-mediator" ref="NC:WindowMediatorRoot" + onpopupshowing="macWindowMenuDidShow();" + hidden="false"> + <template> + <rule> + <menupopup> + <menuitem uri="rdf:*" + label="rdf:http://home.netscape.com/NC-rdf#Name" + type="radio" + name="windowList" + oncommand="ShowWindowFromResource(event.target)"/> + </menupopup> + </rule> + </template> + <menupopup id="windowPopup"> + <menuitem command="minimizeWindow" key="key_minimizeWindow"/> + <menuitem command="zoomWindow"/> + <!-- decomment when "BringAllToFront" is implemented + <menuseparator/> + <menuitem label="&bringAllToFront.label;" disabled="true"/> --> + <menuseparator id="sep-window-list"/> + </menupopup> + </menu> diff --git a/toolkit/content/macWindowMenu.js b/toolkit/content/macWindowMenu.js new file mode 100644 index 0000000000..46654c4f8a --- /dev/null +++ b/toolkit/content/macWindowMenu.js @@ -0,0 +1,51 @@ +// -*- indent-tabs-mode: nil; js-indent-level: 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/. */ + +function macWindowMenuDidShow() +{ + var windowManagerDS = + Components.classes['@mozilla.org/rdf/datasource;1?name=window-mediator'] + .getService(Components.interfaces.nsIWindowDataSource); + var sep = document.getElementById("sep-window-list"); + // Using double parens to avoid warning + while ((sep = sep.nextSibling)) { + var url = sep.getAttribute('id'); + var win = windowManagerDS.getWindowForResource(url); + if (win.document.documentElement.getAttribute("inwindowmenu") == "false") + sep.hidden = true; + else if (win == window) + sep.setAttribute("checked", "true"); + } +} + +function toOpenWindow( aWindow ) +{ + // deminiaturize the window, if it's in the Dock + if (aWindow.windowState == STATE_MINIMIZED) + aWindow.restore(); + aWindow.document.commandDispatcher.focusedWindow.focus(); +} + +function ShowWindowFromResource( node ) +{ + var windowManagerDS = + Components.classes['@mozilla.org/rdf/datasource;1?name=window-mediator'] + .getService(Components.interfaces.nsIWindowDataSource); + + var desiredWindow = null; + var url = node.getAttribute('id'); + desiredWindow = windowManagerDS.getWindowForResource( url ); + if (desiredWindow) + toOpenWindow(desiredWindow); +} + +function zoomWindow() +{ + if (window.windowState == STATE_NORMAL) + window.maximize(); + else + window.restore(); +} diff --git a/toolkit/content/widgets/dialog.xml b/toolkit/content/widgets/dialog.xml index aff1621965..d83570ac0f 100644 --- a/toolkit/content/widgets/dialog.xml +++ b/toolkit/content/widgets/dialog.xml @@ -422,11 +422,15 @@ if (!event.defaultPrevented) this.cancelDialog(); </handler> +#ifdef XP_MACOSX + <handler event="keypress" key="." modifiers="meta" phase="capturing" action="this.cancelDialog();"/> +#else <handler event="focus" phase="capturing"> var btn = this.getButton(this.defaultButton); if (btn) btn.setAttribute("default", event.originalTarget == btn || !(event.originalTarget instanceof Components.interfaces.nsIDOMXULButtonElement)); </handler> +#endif </handlers> </binding> diff --git a/toolkit/content/widgets/findbar.xml b/toolkit/content/widgets/findbar.xml index aae799554d..c312a6a25a 100644 --- a/toolkit/content/widgets/findbar.xml +++ b/toolkit/content/widgets/findbar.xml @@ -1232,6 +1232,39 @@ ]]></body> </method> +#ifdef XP_MACOSX + <!-- + - Fetches the currently selected text and sets that as the text to search + - next. This is a MacOS specific feature. + --> + <method name="onFindSelectionCommand"> + <body><![CDATA[ + let searchString = this.browser.finder.setSearchStringToSelection(); + if (searchString) + this._findField.value = searchString; + ]]></body> + </method> + + <method name="_onFindFieldFocus"> + <body><![CDATA[ + let prefsvc = this._prefsvc; + const kPref = "accessibility.typeaheadfind.prefillwithselection"; + if (this.prefillWithSelection && prefsvc.getBoolPref(kPref)) + return; + + let clipboardSearchString = this._browser.finder.clipboardSearchString; + if (clipboardSearchString && this._findField.value != clipboardSearchString && + !this._findField._willfullyDeleted) { + this._findField.value = clipboardSearchString; + this._findField._hadValue = true; + // Changing the search string makes the previous status invalid, so + // we better clear it here. + this._updateStatusUI(); + } + ]]></body> + </method> +#endif + <!-- - This handles all the result changes for both - type-ahead-find and highlighting. diff --git a/toolkit/content/widgets/optionsDialog.xml b/toolkit/content/widgets/optionsDialog.xml index efd20663e5..f0cdba62f2 100644 --- a/toolkit/content/widgets/optionsDialog.xml +++ b/toolkit/content/widgets/optionsDialog.xml @@ -11,6 +11,7 @@ <binding id="optionsDialog" extends="chrome://global/content/bindings/dialog.xml#dialog"> <content> +#ifndef XP_MACOSX <xul:hbox flex="1"> <xul:categoryBox anonid="prefsCategories"> <children/> @@ -20,6 +21,16 @@ <xul:iframe anonid="panelFrame" name="panelFrame" style="width: 0px;" flex="1"/> </xul:vbox> </xul:hbox> +#else + <xul:vbox flex="1"> + <xul:categoryBox anonid="prefsCategories"> + <children/> + </xul:categoryBox> + <xul:vbox flex="1"> + <xul:iframe anonid="panelFrame" name="panelFrame" style="width: 0px;" flex="1"/> + </xul:vbox> + </xul:vbox> +#endif </content> <implementation> diff --git a/toolkit/content/widgets/preferences.xml b/toolkit/content/widgets/preferences.xml index f7cb109488..11de032462 100644 --- a/toolkit/content/widgets/preferences.xml +++ b/toolkit/content/widgets/preferences.xml @@ -1146,7 +1146,11 @@ </handler> <handler event="keypress" +#ifdef XP_MACOSX + key="&openHelpMac.commandkey;" modifiers="accel" +#else keycode="&openHelp.commandkey;" +#endif phase="capturing"> <![CDATA[ var helpButton = this.getButton("help"); diff --git a/toolkit/content/widgets/tree.xml b/toolkit/content/widgets/tree.xml index 19a1fa7727..aa17172575 100644 --- a/toolkit/content/widgets/tree.xml +++ b/toolkit/content/widgets/tree.xml @@ -79,11 +79,11 @@ <property name="treeBoxObject" onget="return this.boxObject;" readonly="true"/> -<!-- contentView is obsolete (see bug 202391) --> +# contentView is obsolete (see bug 202391) <property name="contentView" onget="return this.view; /*.QueryInterface(Components.interfaces.nsITreeContentView)*/" readonly="true"/> -<!-- builderView is obsolete (see bug 202393) --> +# builderView is obsolete (see bug 202393) <property name="builderView" onget="return this.view; /*.QueryInterface(Components.interfaces.nsIXULTreeBuilder)*/" readonly="true"/> @@ -774,6 +774,7 @@ event.preventDefault(); } </handler> +#ifndef XP_MACOSX <!-- Use F2 key to enter text editing. --> <handler event="keydown" keycode="VK_F2"> <![CDATA[ @@ -785,6 +786,7 @@ event.preventDefault(); ]]> </handler> +#endif // XP_MACOSX <handler event="keydown" keycode="VK_ESCAPE"> <![CDATA[ diff --git a/toolkit/content/widgets/wizard.xml b/toolkit/content/widgets/wizard.xml index eb812f0652..3a8ec2cfef 100644 --- a/toolkit/content/widgets/wizard.xml +++ b/toolkit/content/widgets/wizard.xml @@ -465,6 +465,54 @@ </implementation> </binding> +#ifdef XP_MACOSX + <binding id="wizard-header" extends="chrome://global/content/bindings/wizard.xml#wizard-base"> + <content> + <xul:stack class="wizard-header-stack" flex="1"> + <xul:vbox class="wizard-header-box-1"> + <xul:vbox class="wizard-header-box-text"> + <xul:label class="wizard-header-label" xbl:inherits="xbl:text=label"/> + </xul:vbox> + </xul:vbox> + <xul:hbox class="wizard-header-box-icon"> + <xul:spacer flex="1"/> + <xul:image class="wizard-header-icon" xbl:inherits="src=iconsrc"/> + </xul:hbox> + </xul:stack> + </content> + </binding> + + <binding id="wizard-buttons" extends="chrome://global/content/bindings/wizard.xml#wizard-base"> + <content> + <xul:vbox flex="1"> + <xul:hbox class="wizard-buttons-btm"> + <xul:button class="wizard-button" dlgtype="extra1" hidden="true"/> + <xul:button class="wizard-button" dlgtype="extra2" hidden="true"/> + <xul:button label="&button-cancel-mac.label;" class="wizard-button" dlgtype="cancel"/> + <xul:spacer flex="1"/> + <xul:button label="&button-back-mac.label;" accesskey="&button-back-mac.accesskey;" + class="wizard-button wizard-nav-button" dlgtype="back"/> + <xul:button label="&button-next-mac.label;" accesskey="&button-next-mac.accesskey;" + class="wizard-button wizard-nav-button" dlgtype="next" + default="true" xbl:inherits="hidden=lastpage" /> + <xul:button label="&button-finish-mac.label;" class="wizard-button" + dlgtype="finish" default="true" xbl:inherits="hidden=hidefinishbutton" /> + </xul:hbox> + </xul:vbox> + </content> + + <implementation> + <method name="onPageChange"> + <body><![CDATA[ + this.setAttribute("hidefinishbutton", !(this.getAttribute("lastpage") == "true")); + ]]></body> + </method> + </implementation> + + </binding> + +#else + <binding id="wizard-header" extends="chrome://global/content/bindings/wizard.xml#wizard-base"> <content> <xul:hbox class="wizard-header-box-1" flex="1"> @@ -554,5 +602,6 @@ </property> </implementation> </binding> +#endif </bindings> diff --git a/toolkit/content/xul.css b/toolkit/content/xul.css index 7c16cab703..0aa0d3a217 100644 --- a/toolkit/content/xul.css +++ b/toolkit/content/xul.css @@ -243,6 +243,11 @@ caption { /******** draggable elements *********/ +%ifdef XP_MACOSX +titlebar, +toolbar:not([nowindowdrag="true"]):not([customizing="true"]), +statusbar:not([nowindowdrag="true"]), +%endif windowdragbox { -moz-window-dragging: drag; } @@ -283,6 +288,12 @@ toolbar[customizing="true"][hidden="true"] { display: -moz-box; } +%ifdef XP_MACOSX +toolbar[type="menubar"] { + min-height: 0 !important; + border: 0 !important; +} +%else toolbar[type="menubar"][autohide="true"] { -moz-binding: url("chrome://global/content/bindings/toolbar.xml#toolbar-menubar-autohide"); overflow: hidden; @@ -294,6 +305,7 @@ toolbar[type="menubar"][autohide="true"][inactive="true"]:not([customizing="true -moz-appearance: none !important; border-style: none !important; } +%endif %ifdef MOZ_WIDGET_GTK window[shellshowingmenubar="true"] menubar, @@ -487,9 +499,15 @@ panel[arrowposition="start_after"]:-moz-locale-dir(rtl) { %endif +%ifdef XP_MACOSX +.statusbar-resizerpanel { + display: none; +} +%else window[sizemode="maximized"] statusbarpanel.statusbar-resizerpanel { visibility: collapse; } +%endif /******** grid **********/ @@ -1000,6 +1018,9 @@ autorepeatbutton { statusbar { -moz-binding: url("chrome://global/content/bindings/general.xml#statusbar"); +%ifdef XP_MACOSX + padding-right: 14px; +%endif } statusbarpanel { diff --git a/toolkit/modules/AppConstants.jsm b/toolkit/modules/AppConstants.jsm index 40a630a445..ba94f4a3fc 100644 --- a/toolkit/modules/AppConstants.jsm +++ b/toolkit/modules/AppConstants.jsm @@ -105,6 +105,8 @@ this.AppConstants = Object.freeze({ "linux", #elif XP_WIN "win", +#elif XP_MACOSX + "macosx", #elif XP_LINUX "linux", #else diff --git a/toolkit/modules/LightweightThemeConsumer.jsm b/toolkit/modules/LightweightThemeConsumer.jsm index 9419fdcf23..4010a9ff22 100644 --- a/toolkit/modules/LightweightThemeConsumer.jsm +++ b/toolkit/modules/LightweightThemeConsumer.jsm @@ -147,6 +147,29 @@ LightweightThemeConsumer.prototype = { footer.removeAttribute("lwthemefooter"); } +#if defined(XP_MACOSX) && defined(MOZ_CAN_DRAW_IN_TITLEBAR) + // On OS X, we extend the lightweight theme into the titlebar, which means setting + // the chromemargin attribute. Some XUL applications already draw in the titlebar, + // so we need to save the chromemargin value before we overwrite it with the value + // that lets us draw in the titlebar. We stash this value on the root attribute so + // that XUL applications have the ability to invalidate the saved value. + if (stateChanging) { + if (!root.hasAttribute("chromemargin-nonlwtheme")) { + root.setAttribute("chromemargin-nonlwtheme", root.getAttribute("chromemargin")); + } + + if (active) { + root.setAttribute("chromemargin", "0,-1,-1,-1"); + } else { + let defaultChromemargin = root.getAttribute("chromemargin-nonlwtheme"); + if (defaultChromemargin) { + root.setAttribute("chromemargin", defaultChromemargin); + } else { + root.removeAttribute("chromemargin"); + } + } + } +#endif Services.obs.notifyObservers(this._win, "lightweight-theme-window-updated", JSON.stringify(aData)); } diff --git a/toolkit/modules/UpdateUtils.jsm b/toolkit/modules/UpdateUtils.jsm index 5e3a2e100a..5acf395d3d 100644 --- a/toolkit/modules/UpdateUtils.jsm +++ b/toolkit/modules/UpdateUtils.jsm @@ -168,6 +168,17 @@ XPCOMUtils.defineLazyGetter(UpdateUtils, "ABI", function() { Cu.reportError("XPCOM ABI unknown"); } +#ifdef XP_MACOSX + // Mac universal build should report a different ABI than either macppc + // or mactel. + let macutils = Cc["@mozilla.org/xpcom/mac-utils;1"]. + getService(Ci.nsIMacUtils); + + if (macutils.isUniversalBinary) { + abi += "-u-" + macutils.architecturesInBinary; + } +#endif + return abi; }); diff --git a/toolkit/modules/WindowDraggingUtils.jsm b/toolkit/modules/WindowDraggingUtils.jsm index 5be8814de1..a7986c8b4a 100644 --- a/toolkit/modules/WindowDraggingUtils.jsm +++ b/toolkit/modules/WindowDraggingUtils.jsm @@ -2,7 +2,7 @@ * 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/. */ -#if defined(XP_WIN) +#if defined(XP_WIN) || defined(XP_MACOSX) const HAVE_CSS_WINDOW_DRAG_SUPPORT = true; #else const HAVE_CSS_WINDOW_DRAG_SUPPORT = false; diff --git a/toolkit/modules/moz.build b/toolkit/modules/moz.build index 836fd66b36..8509eb7cd3 100644 --- a/toolkit/modules/moz.build +++ b/toolkit/modules/moz.build @@ -103,7 +103,7 @@ EXTRA_PP_JS_MODULES += [ ] -EXTRA_JS_MODULES += [ +EXTRA_PP_JS_MODULES += [ 'LightweightThemeConsumer.jsm', ] diff --git a/toolkit/mozapps/downloads/content/downloads.xul b/toolkit/mozapps/downloads/content/downloads.xul index b5ca87a0c2..5ca9eec2d4 100644 --- a/toolkit/mozapps/downloads/content/downloads.xul +++ b/toolkit/mozapps/downloads/content/downloads.xul @@ -1,13 +1,15 @@ <?xml version="1.0"?> -# -*- Mode: XML; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- # 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/. #ifdef XP_UNIX +#ifndef XP_MACOSX #define XP_GNOME 1 #endif +#endif <?xml-stylesheet href="chrome://global/skin/" type="text/css"?> <?xml-stylesheet href="chrome://mozapps/content/downloads/downloads.css"?> @@ -50,6 +52,9 @@ <key keycode="VK_RETURN" oncommand="doDefaultForSelected();"/> <key id="key_pauseResume" key=" " oncommand="performCommand('cmd_pauseResume');"/> <key id="key_removeFromList" keycode="VK_DELETE" oncommand="performCommand('cmd_removeFromList');"/> +#ifdef XP_MACOSX + <key id="key_removeFromList2" keycode="VK_BACK" oncommand="performCommand('cmd_removeFromList');"/> +#endif <key id="key_close" key="&cmd.close.commandKey;" oncommand="closeWindow(true);" modifiers="accel"/> #ifdef XP_GNOME <key id="key_close2" key="&cmd.close2Unix.commandKey;" oncommand="closeWindow(true);" modifiers="accel,shift"/> @@ -95,8 +100,13 @@ oncommand="performCommand('cmd_open');" cmd="cmd_open"/> <menuitem id="menuitem_show" +#ifdef XP_MACOSX + label="&cmd.showMac.label;" + accesskey="&cmd.showMac.accesskey;" +#else label="&cmd.show.label;" accesskey="&cmd.show.accesskey;" +#endif oncommand="performCommand('cmd_show');" cmd="cmd_show"/> diff --git a/toolkit/mozapps/downloads/content/unknownContentType.xul b/toolkit/mozapps/downloads/content/unknownContentType.xul index 42d356e9f9..af8b7b016a 100644 --- a/toolkit/mozapps/downloads/content/unknownContentType.xul +++ b/toolkit/mozapps/downloads/content/unknownContentType.xul @@ -1,5 +1,5 @@ <?xml version="1.0"?> -# -*- Mode: XML; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- # 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/. @@ -68,7 +68,11 @@ <hbox id="openHandlerBox" flex="1" align="center"/> <hbox flex="1" align="center"> <button id="chooseButton" oncommand="dialog.chooseApp();" +#ifdef XP_MACOSX + label="&chooseHandlerMac.label;" accesskey="&chooseHandlerMac.accesskey;"/> +#else label="&chooseHandler.label;" accesskey="&chooseHandler.accesskey;"/> +#endif </hbox> </deck> </hbox> diff --git a/toolkit/mozapps/downloads/nsHelperAppDlg.js b/toolkit/mozapps/downloads/nsHelperAppDlg.js index 0e5cfdaf0e..90d38c90b0 100644 --- a/toolkit/mozapps/downloads/nsHelperAppDlg.js +++ b/toolkit/mozapps/downloads/nsHelperAppDlg.js @@ -675,7 +675,11 @@ nsUnknownContentTypeDialog.prototype = { // getPath: getPath: function (aFile) { +#ifdef XP_MACOSX + return aFile.leafName || aFile.path; +#else return aFile.path; +#endif }, // initAppAndSaveToDiskValues: @@ -983,6 +987,12 @@ nsUnknownContentTypeDialog.prototype = { return file.getVersionInfoField("FileDescription"); } catch (e) {} } +#elifdef XP_MACOSX + if (file instanceof Components.interfaces.nsILocalFileMac) { + try { + return file.bundleDisplayName; + } catch (e) {} + } #endif return file.leafName; diff --git a/toolkit/mozapps/extensions/GMPUtils.jsm b/toolkit/mozapps/extensions/GMPUtils.jsm index 593fc3c8da..3c691610d5 100644 --- a/toolkit/mozapps/extensions/GMPUtils.jsm +++ b/toolkit/mozapps/extensions/GMPUtils.jsm @@ -39,6 +39,11 @@ this.GMPUtils = { * The plugin to check. */ isPluginHidden: function(aPlugin) { + if (this._is32bitModeMacOS()) { + // GMPs are hidden on MacOS when running in 32 bit mode. + // See bug 1291537. + return true; + } if (!aPlugin.isEME) { return false; } @@ -66,7 +71,7 @@ this.GMPUtils = { } if (aPlugin.id == WIDEVINE_ID) { -#if defined(XP_WIN) || defined(XP_LINUX) +#if defined(XP_WIN) || defined(XP_LINUX) || defined(XP_MACOSX) // The Widevine plugin is available for Windows versions Vista and later, // Mac OSX, and Linux. return true; @@ -78,6 +83,14 @@ this.GMPUtils = { return true; }, + _is32bitModeMacOS: function() { +#ifdef XP_MACOSX + return Services.appinfo.XPCOMABI.split("-")[0] == "x86"; +#else + return false; +#endif + }, + /** * Checks whether or not a given plugin is visible in the addons manager * UI and the "enable DRM" notification box. This can be used to test diff --git a/toolkit/mozapps/extensions/content/extensions.js b/toolkit/mozapps/extensions/content/extensions.js index fe84bc4602..2ca3898f17 100644 --- a/toolkit/mozapps/extensions/content/extensions.js +++ b/toolkit/mozapps/extensions/content/extensions.js @@ -1878,7 +1878,11 @@ var gHeader = { // XXXunf Temporary until bug 371900 is fixed. let key = document.getElementById("focusSearch").getAttribute("key"); +#ifdef XP_MACOSX + let keyModifier = aEvent.metaKey; +#else let keyModifier = aEvent.ctrlKey; +#endif if (String.fromCharCode(aEvent.charCode) == key && keyModifier) { this.focusSearchBox(); return; diff --git a/toolkit/mozapps/extensions/content/update.xul b/toolkit/mozapps/extensions/content/update.xul index 9f5f35196e..094651fa5f 100644 --- a/toolkit/mozapps/extensions/content/update.xul +++ b/toolkit/mozapps/extensions/content/update.xul @@ -1,9 +1,9 @@ <?xml version="1.0"?> -<!-- -*- Mode: XML; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- --> -<!-- 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/. --> +# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +# 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/. <?xml-stylesheet href="chrome://global/skin/" type="text/css"?> <?xml-stylesheet href="chrome://mozapps/skin/extensions/update.css" type="text/css"?> @@ -100,7 +100,11 @@ oncommand="gUpdateWizard.shouldAutoCheck = this.checked;"/> </vbox> <separator flex="1"/> +#ifndef XP_MACOSX <label>&clickFinish.label;</label> +#else + <label>&clickFinish.labelMac;</label> +#endif <separator class="thin"/> </wizardpage> @@ -140,7 +144,11 @@ <description flex="1">&installerrors.intro.label;</description> </hbox> <separator flex="1"/> +#ifndef XP_MACOSX <label>&clickFinish.label;</label> +#else + <label>&clickFinish.labelMac;</label> +#endif <separator class="thin"/> </wizardpage> @@ -153,7 +161,11 @@ <description flex="1">&adminDisabled.warning.label;</description> </hbox> <separator flex="1"/> +#ifndef XP_MACOSX <label>&clickFinish.label;</label> +#else + <label>&clickFinish.labelMac;</label> +#endif <separator class="thin"/> </wizardpage> @@ -172,7 +184,11 @@ oncommand="gUpdateWizard.shouldAutoCheck = this.checked;"/> </vbox> <separator flex="1"/> +#ifndef XP_MACOSX <label>&clickFinish.label;</label> +#else + <label>&clickFinish.labelMac;</label> +#endif <separator class="thin"/> </wizardpage> diff --git a/toolkit/mozapps/extensions/jar.mn b/toolkit/mozapps/extensions/jar.mn index 878be4df13..e95d93ca02 100644 --- a/toolkit/mozapps/extensions/jar.mn +++ b/toolkit/mozapps/extensions/jar.mn @@ -21,7 +21,7 @@ toolkit.jar: content/mozapps/extensions/selectAddons.xml (content/selectAddons.xml) content/mozapps/extensions/selectAddons.js (content/selectAddons.js) content/mozapps/extensions/selectAddons.css (content/selectAddons.css) - content/mozapps/extensions/update.xul (content/update.xul) +* content/mozapps/extensions/update.xul (content/update.xul) content/mozapps/extensions/update.js (content/update.js) content/mozapps/extensions/eula.xul (content/eula.xul) content/mozapps/extensions/eula.js (content/eula.js) diff --git a/toolkit/mozapps/update/common/updatecommon.cpp b/toolkit/mozapps/update/common/updatecommon.cpp index 17a57eae38..aff7d72602 100644 --- a/toolkit/mozapps/update/common/updatecommon.cpp +++ b/toolkit/mozapps/update/common/updatecommon.cpp @@ -41,6 +41,8 @@ void UpdateLog::Init(NS_tchar* sourcePath, // updater process if the elevated updater process has written the log. DeleteFileW(mDstFilePath); } +#elif XP_MACOSX + logFP = NS_tfopen(mDstFilePath, NS_T("w")); #else // On platforms that have an updates directory in the installation directory // (e.g. platforms other than Windows and Mac) the update log is written to @@ -59,7 +61,7 @@ void UpdateLog::Finish() return; } -#if !defined(XP_WIN) +#if !defined(XP_WIN) && !defined(XP_MACOSX) const int blockSize = 1024; char buffer[blockSize]; fflush(logFP); diff --git a/toolkit/mozapps/update/common/updatedefines.h b/toolkit/mozapps/update/common/updatedefines.h index 0db182219a..49fbde6f2f 100644 --- a/toolkit/mozapps/update/common/updatedefines.h +++ b/toolkit/mozapps/update/common/updatedefines.h @@ -100,6 +100,10 @@ static inline int mywcsprintf(WCHAR* dest, size_t count, const WCHAR* fmt, ...) #endif # include <dirent.h> +#ifdef XP_MACOSX +# include <sys/time.h> +#endif + # define LOG_S "%s" # define NS_T(str) str # define NS_SLASH NS_T('/') diff --git a/toolkit/mozapps/update/nsUpdateService.js b/toolkit/mozapps/update/nsUpdateService.js index 9bea453dbb..ce9be2c512 100644 --- a/toolkit/mozapps/update/nsUpdateService.js +++ b/toolkit/mozapps/update/nsUpdateService.js @@ -315,6 +315,36 @@ function areDirectoryEntriesWriteable(aDir) { } /** + * OSX only function to determine if the user requires elevation to be able to + * write to the application bundle. + * + * @return true if elevation is required, false otherwise + */ +function getElevationRequired() { +#ifdef XP_MACOSX + try { + // Recursively check that the application bundle (and its descendants) can + // be written to. + LOG("getElevationRequired - recursively testing write access on " + + getInstallDirRoot().path); + if (!getInstallDirRoot().isWritable() || + !areDirectoryEntriesWriteable(getInstallDirRoot())) { + LOG("getElevationRequired - unable to write to application bundle, " + + "elevation required"); + return true; + } + } catch (ex) { + LOG("getElevationRequired - unable to write to application bundle, " + + "elevation required. Exception: " + ex); + return true; + } + LOG("getElevationRequired - able to write to application bundle, elevation " + + "not required"); +#endif + return false; +} + +/** * Determines whether or not an update can be applied. This is always true on * Windows when the service is used. Also, this is always true on OSX because we * offer users the option to perform an elevated update when necessary. @@ -322,6 +352,7 @@ function areDirectoryEntriesWriteable(aDir) { * @return true if an update can be applied, false otherwise */ function getCanApplyUpdates() { +#ifndef XP_MACOSX try { let updateTestFile = getUpdateFile([FILE_UPDATE_TEST]); LOG("getCanApplyUpdates - testing write access " + updateTestFile.path); @@ -400,6 +431,7 @@ function getCanApplyUpdates() { // No write privileges to install directory return false; } +#endif // !XP_MACOSX LOG("getCanApplyUpdates - able to apply updates"); return true; @@ -412,17 +444,28 @@ function getCanApplyUpdates() { * @return true if updates can be staged for this session. */ XPCOMUtils.defineLazyGetter(this, "gCanStageUpdatesSession", function aus_gCSUS() { + if (getElevationRequired()) { + LOG("gCanStageUpdatesSession - unable to stage updates because elevation " + + "is required."); + return false; + } + try { let updateTestFile; +#ifdef XP_MACOSX + updateTestFile = getUpdateFile([FILE_UPDATE_TEST]); +#else updateTestFile = getInstallDirRoot(); updateTestFile.append(FILE_UPDATE_TEST); +#endif LOG("gCanStageUpdatesSession - testing write access " + updateTestFile.path); testWriteAccess(updateTestFile, true); - // On all platforms, we need to test the parent directory as well, - // as we need to be able to move files in that directory during the +#ifndef XP_MACOSX + // On all platforms except Mac, we need to test the parent directory as + // well, as we need to be able to move files in that directory during the // replacing step. updateTestFile = getInstallDirRoot().parent; updateTestFile.append(FILE_UPDATE_TEST); @@ -431,6 +474,7 @@ XPCOMUtils.defineLazyGetter(this, "gCanStageUpdatesSession", function aus_gCSUS( updateTestFile.createUnique(Ci.nsILocalFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY); updateTestFile.remove(false); +#endif // !XP_MACOSX } catch (e) { LOG("gCanStageUpdatesSession - unable to stage updates. Exception: " + e); @@ -549,6 +593,10 @@ function getAppBaseDir() { */ function getInstallDirRoot() { let dir = getAppBaseDir(); +#ifdef XP_MACOSX + // On Mac, we store the Updated.app directory inside the bundle directory. + dir = dir.parent.parent; +#endif return dir; } @@ -832,7 +880,31 @@ function handleUpdateFailure(update, errorCode) { cancelations++; Services.prefs.setIntPref(PREF_APP_UPDATE_CANCELATIONS, cancelations); +#ifdef XP_MACOSX + let osxCancelations = Services.prefs.getIntPref(PREF_APP_UPDATE_CANCELATIONS_OSX, 0); + osxCancelations++; + Services.prefs.setIntPref(PREF_APP_UPDATE_CANCELATIONS_OSX, + osxCancelations); + let maxCancels = Services.prefs.getIntPref( + PREF_APP_UPDATE_CANCELATIONS_OSX_MAX, + DEFAULT_CANCELATIONS_OSX_MAX); + // Prevent the preference from setting a value greater than 5. + maxCancels = Math.min(maxCancels, 5); + if (osxCancelations >= maxCancels) { + cleanupActiveUpdate(); + } else { + writeStatusFile(getUpdatesDir(), + update.state = STATE_PENDING_ELEVATE); + } + update.statusText = gUpdateBundle.GetStringFromName("elevationFailure"); + update.QueryInterface(Ci.nsIWritablePropertyBag); + update.setProperty("patchingFailed", "elevationFailure"); + let prompter = Cc["@mozilla.org/updates/update-prompt;1"]. + createInstance(Ci.nsIUpdatePrompt); + prompter.showUpdateError(update); +#else writeStatusFile(getUpdatesDir(), update.state = STATE_PENDING); +#endif return true; } @@ -1778,6 +1850,58 @@ UpdateService.prototype = { }); let update = minorUpdate || majorUpdate; +#ifdef XP_MACOSX + if (update) { + if (getElevationRequired()) { + let installAttemptVersion = Services.prefs.getCharPref( + PREF_APP_UPDATE_ELEVATE_VERSION, + ""); + if (vc.compare(installAttemptVersion, update.appVersion) != 0) { + Services.prefs.setCharPref(PREF_APP_UPDATE_ELEVATE_VERSION, + update.appVersion); + if (Services.prefs.prefHasUserValue( + PREF_APP_UPDATE_CANCELATIONS_OSX)) { + Services.prefs.clearUserPref(PREF_APP_UPDATE_CANCELATIONS_OSX); + } + if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_ELEVATE_NEVER)) { + Services.prefs.clearUserPref(PREF_APP_UPDATE_ELEVATE_NEVER); + } + } else { + let numCancels = Services.prefs.getIntPref(PREF_APP_UPDATE_CANCELATIONS_OSX, 0); + let rejectedVersion = Services.prefs.getCharPref(PREF_APP_UPDATE_ELEVATE_NEVER, ""); + let maxCancels = Services.prefs.getIntPref(PREF_APP_UPDATE_CANCELATIONS_OSX_MAX, + DEFAULT_CANCELATIONS_OSX_MAX); + if (numCancels >= maxCancels) { + LOG("UpdateService:selectUpdate - the user requires elevation to " + + "install this update, but the user has exceeded the max " + + "number of elevation attempts."); + update.elevationFailure = true; + } else if (vc.compare(rejectedVersion, update.appVersion) == 0) { + LOG("UpdateService:selectUpdate - the user requires elevation to " + + "install this update, but elevation is disabled for this " + + "version."); + update.elevationFailure = true; + } else { + LOG("UpdateService:selectUpdate - the user requires elevation to " + + "install the update."); + } + } + } else { + // Clear elevation-related prefs since they no longer apply (the user + // may have gained write access to the Firefox directory or an update + // was executed with a different profile). + if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_ELEVATE_VERSION)) { + Services.prefs.clearUserPref(PREF_APP_UPDATE_ELEVATE_VERSION); + } + if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_CANCELATIONS_OSX)) { + Services.prefs.clearUserPref(PREF_APP_UPDATE_CANCELATIONS_OSX); + } + if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_ELEVATE_NEVER)) { + Services.prefs.clearUserPref(PREF_APP_UPDATE_ELEVATE_NEVER); + } + } + } +#endif return update; }, @@ -1895,8 +2019,7 @@ UpdateService.prototype = { * See nsIUpdateService.idl */ get elevationRequired() { - /** Mac Stub, but keeping this for now to not break the API **/ - return false; + return getElevationRequired(); }, /** @@ -3147,7 +3270,11 @@ Downloader.prototype = { "max fail: " + maxFail + ", " + "retryTimeout: " + retryTimeout); if (Components.isSuccessCode(status)) { if (this._verifyDownload()) { - state = STATE_PENDING; + if (getElevationRequired()) { + state = STATE_PENDING_ELEVATE; + } else { + state = STATE_PENDING; + } if (this.background) { shouldShowPrompt = !getCanStageUpdates(); } diff --git a/toolkit/mozapps/update/updater/progressui.h b/toolkit/mozapps/update/updater/progressui.h index 6dc20e06bc..5462815dee 100644 --- a/toolkit/mozapps/update/updater/progressui.h +++ b/toolkit/mozapps/update/updater/progressui.h @@ -24,6 +24,9 @@ int InitProgressUI(int *argc, NS_tchar ***argv); // Called on the main thread at startup int ShowProgressUI(bool indeterminate = false, bool initUIStrings = true); int InitProgressUIStrings(); +#elif defined(XP_MACOSX) + // Called on the main thread at startup + int ShowProgressUI(bool indeterminate = false); #else // Called on the main thread at startup int ShowProgressUI(); diff --git a/toolkit/mozapps/update/updater/updater.cpp b/toolkit/mozapps/update/updater/updater.cpp index 13d829243f..f5f71935dc 100644 --- a/toolkit/mozapps/update/updater/updater.cpp +++ b/toolkit/mozapps/update/updater/updater.cpp @@ -53,6 +53,9 @@ #include <algorithm> #include "updatecommon.h" +#ifdef XP_MACOSX +#include "updaterfileutils_osx.h" +#endif // XP_MACOSX #include "mozilla/Compiler.h" #include "mozilla/Types.h" @@ -72,6 +75,23 @@ #define PARENT_WAIT 10000 #endif +#if defined(XP_MACOSX) +// These functions are defined in launchchild_osx.mm +void CleanupElevatedMacUpdate(bool aFailureOccurred); +bool IsOwnedByGroupAdmin(const char* aAppBundle); +bool IsRecursivelyWritable(const char* aPath); +void LaunchChild(int argc, const char** argv); +void LaunchMacPostProcess(const char* aAppBundle); +bool ObtainUpdaterArguments(int* argc, char*** argv); +bool ServeElevatedUpdate(int argc, const char** argv); +void SetGroupOwnershipAndPermissions(const char* aAppBundle); +struct UpdateServerThreadArgs +{ + int argc; + const NS_tchar** argv; +}; +#endif + #ifndef _O_BINARY # define _O_BINARY 0 #endif @@ -86,13 +106,14 @@ // We want to use execv to invoke the callback executable on platforms where // we were launched using execv. See nsUpdateDriver.cpp. -#if defined(XP_UNIX) +#if defined(XP_UNIX) && !defined(XP_MACOSX) #define USE_EXECV #endif # define MAYBE_USE_HARD_LINKS 0 -#if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN) +#if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN) && \ + !defined(XP_MACOSX) #include "nss.h" #include "prerror.h" #endif @@ -1675,6 +1696,21 @@ PatchFile::Execute() AutoFile ofile(ensure_open(mFile.get(), shouldTruncate ? NS_T("wb+") : NS_T("rb+"), ss.st_mode)); +#elif defined(XP_MACOSX) + AutoFile ofile(ensure_open(mFile.get(), NS_T("wb+"), ss.st_mode)); + // Modified code from FileUtils.cpp + fstore_t store = {F_ALLOCATECONTIG, F_PEOFPOSMODE, 0, header.dlen}; + // Try to get a continous chunk of disk space + rv = fcntl(fileno((FILE *)ofile), F_PREALLOCATE, &store); + if (rv == -1) { + // OK, perhaps we are too fragmented, allocate non-continuous + store.fst_flags = F_ALLOCATEALL; + rv = fcntl(fileno((FILE *)ofile), F_PREALLOCATE, &store); + } + + if (rv != -1) { + ftruncate(fileno((FILE *)ofile), header.dlen); + } #else AutoFile ofile(ensure_open(mFile.get(), NS_T("wb+"), ss.st_mode)); #endif @@ -2055,6 +2091,8 @@ LaunchCallbackApp(const NS_tchar *workingDir, #if defined(USE_EXECV) execv(argv[0], argv); +#elif defined(XP_MACOSX) + LaunchChild(argc, (const char**)argv); #elif defined(XP_WIN) WinLaunchChild(argv[0], argc, argv, nullptr); #else @@ -2170,15 +2208,19 @@ CopyInstallDirToDestDir() // These files should not be copied over to the updated app #ifdef XP_WIN #define SKIPLIST_COUNT 3 +#elif XP_MACOSX +#define SKIPLIST_COUNT 0 #else #define SKIPLIST_COUNT 2 #endif copy_recursive_skiplist<SKIPLIST_COUNT> skiplist; +#ifndef XP_MACOSX skiplist.append(0, gInstallDirPath, NS_T("updated")); skiplist.append(1, gInstallDirPath, NS_T("updates/0")); #ifdef XP_WIN skiplist.append(2, gInstallDirPath, NS_T("updated.update_in_progress.lock")); #endif +#endif return ensure_copy_recursive(gInstallDirPath, gWorkingDirPath, skiplist); } @@ -2197,7 +2239,11 @@ ProcessReplaceRequest() // 2. Move newDir to destDir. In case of failure, revert step 1 and abort. // 3. Delete tmpDir (or defer it to the next reboot). -#if XP_WIN +#ifdef XP_MACOSX + NS_tchar destDir[MAXPATHLEN]; + NS_tsnprintf(destDir, sizeof(destDir)/sizeof(destDir[0]), + NS_T("%s/Contents"), gInstallDirPath); +#elif XP_WIN // Windows preserves the case of the file/directory names. We use the // GetLongPathName API in order to get the correct case for the directory // name, so that if the user has used a different case when launching the @@ -2217,8 +2263,13 @@ ProcessReplaceRequest() NS_tchar newDir[MAXPATHLEN]; NS_tsnprintf(newDir, sizeof(newDir)/sizeof(newDir[0]), +#ifdef XP_MACOSX + NS_T("%s/Contents"), + gWorkingDirPath); +#else NS_T("%s.bak/updated"), gInstallDirPath); +#endif // First try to remove the possibly existing temp directory, because if this // directory exists, we will fail to rename destDir. @@ -2256,6 +2307,14 @@ ProcessReplaceRequest() LOG(("Begin moving newDir (" LOG_S ") to destDir (" LOG_S ")", newDir, destDir)); rv = rename_file(newDir, destDir, true); +#ifdef XP_MACOSX + if (rv) { + LOG(("Moving failed. Begin copying newDir (" LOG_S ") to destDir (" LOG_S ")", + newDir, destDir)); + copy_recursive_skiplist<0> skiplist; + rv = ensure_copy_recursive(newDir, destDir, skiplist); + } +#endif if (rv) { LOG(("Moving newDir to destDir failed, err: %d", rv)); LOG(("Now, try to move tmpDir back to destDir")); @@ -2269,7 +2328,7 @@ ProcessReplaceRequest() return rv; } -#if !defined(XP_WIN) +#if !defined(XP_WIN) && !defined(XP_MACOSX) // Platforms that have their updates directory in the installation directory // need to have the last-update.log and backup-update.log files moved from the // old installation directory to the new installation directory. @@ -2303,6 +2362,15 @@ ProcessReplaceRequest() #endif } +#ifdef XP_MACOSX + // On OS X, we we need to remove the staging directory after its Contents + // directory has been moved. + NS_tchar updatedAppDir[MAXPATHLEN]; + NS_tsnprintf(updatedAppDir, sizeof(updatedAppDir)/sizeof(updatedAppDir[0]), + NS_T("%s/Updated.app"), gPatchDirPath); + ensure_remove_recursive(updatedAppDir); +#endif + gSucceeded = true; return 0; @@ -2412,7 +2480,11 @@ UpdateThreadFunc(void *param) NS_tchar updateSettingsPath[MAX_TEXT_LEN]; NS_tsnprintf(updateSettingsPath, sizeof(updateSettingsPath) / sizeof(updateSettingsPath[0]), +#ifdef XP_MACOSX + NS_T("%s/Contents/Resources/update-settings.ini"), +#else NS_T("%s/update-settings.ini"), +#endif gWorkingDirPath); MARChannelStringTable MARStrings; if (ReadMARChannelIDs(updateSettingsPath, &MARStrings) != OK) { @@ -2484,6 +2556,15 @@ UpdateThreadFunc(void *param) if (rv) { LOG(("failed: %d", rv)); } else { +#ifdef XP_MACOSX + // If the update was successful we need to update the timestamp on the + // top-level Mac OS X bundle directory so that Mac OS X's Launch Services + // picks up any major changes when the bundle is updated. + if (!sStagedUpdate && utimes(gInstallDirPath, nullptr) != 0) { + LOG(("Couldn't set access/modification time on application bundle.")); + } +#endif + LOG(("succeeded")); } WriteStatusFile(rv); @@ -2493,11 +2574,34 @@ UpdateThreadFunc(void *param) QuitProgressUI(); } +#ifdef XP_MACOSX +static void +ServeElevatedUpdateThreadFunc(void* param) +{ + UpdateServerThreadArgs* threadArgs = (UpdateServerThreadArgs*)param; + gSucceeded = ServeElevatedUpdate(threadArgs->argc, threadArgs->argv); + if (!gSucceeded) { + WriteStatusFile(ELEVATION_CANCELED); + } + QuitProgressUI(); +} + +void freeArguments(int argc, char** argv) +{ + for (int i = 0; i < argc; i++) { + free(argv[i]); + } + free(argv); +} +#endif + int LaunchCallbackAndPostProcessApps(int argc, NS_tchar** argv, int callbackIndex #ifdef XP_WIN , const WCHAR* elevatedLockFilePath , HANDLE updateLockFileHandle +#elif XP_MACOSX + , bool isElevated #endif ) { @@ -2509,11 +2613,19 @@ int LaunchCallbackAndPostProcessApps(int argc, NS_tchar** argv, } } EXIT_WHEN_ELEVATED(elevatedLockFilePath, updateLockFileHandle, 0); +#elif XP_MACOSX + if (!isElevated) { + if (gSucceeded) { + LaunchMacPostProcess(gInstallDirPath); + } #endif LaunchCallbackApp(argv[5], argc - callbackIndex, argv + callbackIndex); +#ifdef XP_MACOSX + } // if (!isElevated) +#endif /* XP_MACOSX */ } return 0; } @@ -2525,7 +2637,20 @@ int NS_main(int argc, NS_tchar **argv) // argument prior to callbackIndex is the working directory. const int callbackIndex = 6; -#if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN) +#ifdef XP_MACOSX + bool isElevated = + strstr(argv[0], "/Library/PrivilegedHelperTools/org.mozilla.updater") != 0; + if (isElevated) { + if (!ObtainUpdaterArguments(&argc, &argv)) { + // Won't actually get here because ObtainUpdaterArguments will terminate + // the current process on failure. + return 1; + } + } +#endif + +#if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN) && \ + !defined(XP_MACOSX) // On Windows and Mac we rely on native APIs to do verifications so we don't // need to initialize NSS at all there. // Otherwise, minimize the amount of NSS we depend on by avoiding all the NSS @@ -2538,7 +2663,13 @@ int NS_main(int argc, NS_tchar **argv) } #endif - InitProgressUI(&argc, &argv); +#ifdef XP_MACOSX + if (!isElevated) { +#endif + InitProgressUI(&argc, &argv); +#ifdef XP_MACOSX + } +#endif // To process an update the updater command line must at a minimum have the // directory path containing the updater.mar file to process as the first @@ -2556,6 +2687,12 @@ int NS_main(int argc, NS_tchar **argv) // launched. if (argc < 4) { fprintf(stderr, "Usage: updater patch-dir install-dir apply-to-dir [wait-pid [callback-working-dir callback-path args...]]\n"); +#ifdef XP_MACOSX + if (isElevated) { + freeArguments(argc, argv); + CleanupElevatedMacUpdate(true); + } +#endif return 1; } @@ -2564,6 +2701,12 @@ int NS_main(int argc, NS_tchar **argv) // directory is invalid don't write the status file. fprintf(stderr, "The patch directory path is not valid for this " \ "application (" LOG_S ")\n", argv[1]); +#ifdef XP_MACOSX + if (isElevated) { + freeArguments(argc, argv); + CleanupElevatedMacUpdate(true); + } +#endif return 1; } // The directory containing the update information. @@ -2573,6 +2716,12 @@ int NS_main(int argc, NS_tchar **argv) WriteStatusFile(INVALID_INSTALL_DIR_PATH_ERROR); fprintf(stderr, "The install directory path is not valid for this " \ "application (" LOG_S ")\n", argv[2]); +#ifdef XP_MACOSX + if (isElevated) { + freeArguments(argc, argv); + CleanupElevatedMacUpdate(true); + } +#endif return 1; } // The directory we're going to update to. @@ -2635,6 +2784,12 @@ int NS_main(int argc, NS_tchar **argv) WriteStatusFile(INVALID_WORKING_DIR_PATH_ERROR); fprintf(stderr, "The working directory path is not valid for this " \ "application (" LOG_S ")\n", argv[3]); +#ifdef XP_MACOSX + if (isElevated) { + freeArguments(argc, argv); + CleanupElevatedMacUpdate(true); + } +#endif return 1; } // The directory we're going to update to. @@ -2653,6 +2808,12 @@ int NS_main(int argc, NS_tchar **argv) WriteStatusFile(INVALID_CALLBACK_PATH_ERROR); fprintf(stderr, "The callback file path is not valid for this " \ "application (" LOG_S ")\n", argv[callbackIndex]); +#ifdef XP_MACOSX + if (isElevated) { + freeArguments(argc, argv); + CleanupElevatedMacUpdate(true); + } +#endif return 1; } @@ -2663,10 +2824,37 @@ int NS_main(int argc, NS_tchar **argv) WriteStatusFile(INVALID_CALLBACK_DIR_ERROR); fprintf(stderr, "The callback file must be located in the " \ "installation directory (" LOG_S ")\n", argv[callbackIndex]); +#ifdef XP_MACOSX + if (isElevated) { + freeArguments(argc, argv); + CleanupElevatedMacUpdate(true); + } +#endif return 1; } } +#ifdef XP_MACOSX + if (!isElevated && !IsRecursivelyWritable(argv[2])) { + // If the app directory isn't recursively writeable, an elevated update is + // required. + UpdateServerThreadArgs threadArgs; + threadArgs.argc = argc; + threadArgs.argv = const_cast<const NS_tchar**>(argv); + + Thread t1; + if (t1.Run(ServeElevatedUpdateThreadFunc, &threadArgs) == 0) { + // Show an indeterminate progress bar while an elevated update is in + // progress. + ShowProgressUI(true); + } + t1.Join(); + + LaunchCallbackAndPostProcessApps(argc, argv, callbackIndex, false); + return gSucceeded ? 0 : 1; + } +#endif + if (EnvHasValue("MOZ_OS_UPDATE")) { sIsOSUpdate = true; putenv(const_cast<char*>("MOZ_OS_UPDATE=")); @@ -2676,6 +2864,12 @@ int NS_main(int argc, NS_tchar **argv) if (!WriteStatusFile("applying")) { LOG(("failed setting status to 'applying'")); +#ifdef XP_MACOSX + if (isElevated) { + freeArguments(argc, argv); + CleanupElevatedMacUpdate(true); + } +#endif return 1; } @@ -2912,6 +3106,12 @@ int NS_main(int argc, NS_tchar **argv) // Try to create the destination directory if it doesn't exist int rv = NS_tmkdir(gWorkingDirPath, 0755); if (rv != OK && errno != EEXIST) { +#ifdef XP_MACOSX + if (isElevated) { + freeArguments(argc, argv); + CleanupElevatedMacUpdate(true); + } +#endif return 1; } } @@ -3152,7 +3352,11 @@ int NS_main(int argc, NS_tchar **argv) // is an elevated process on OSX. Thread t; if (t.Run(UpdateThreadFunc, nullptr) == 0) { - if (!sStagedUpdate && !sReplaceRequest) { + if (!sStagedUpdate && !sReplaceRequest +#ifdef XP_MACOSX + && !isElevated +#endif + ) { ShowProgressUI(); } } @@ -3189,12 +3393,66 @@ int NS_main(int argc, NS_tchar **argv) } #endif /* XP_WIN */ +#ifdef XP_MACOSX + // When the update is successful remove the precomplete file in the root of + // the application bundle and move the distribution directory from + // Contents/MacOS to Contents/Resources and if both exist delete the + // directory under Contents/MacOS (see Bug 1068439). + if (gSucceeded && !sStagedUpdate) { + NS_tchar oldPrecomplete[MAXPATHLEN]; + NS_tsnprintf(oldPrecomplete, sizeof(oldPrecomplete)/sizeof(oldPrecomplete[0]), + NS_T("%s/precomplete"), gInstallDirPath); + NS_tremove(oldPrecomplete); + + NS_tchar oldDistDir[MAXPATHLEN]; + NS_tsnprintf(oldDistDir, sizeof(oldDistDir)/sizeof(oldDistDir[0]), + NS_T("%s/Contents/MacOS/distribution"), gInstallDirPath); + int rv = NS_taccess(oldDistDir, F_OK); + if (!rv) { + NS_tchar newDistDir[MAXPATHLEN]; + NS_tsnprintf(newDistDir, sizeof(newDistDir)/sizeof(newDistDir[0]), + NS_T("%s/Contents/Resources/distribution"), gInstallDirPath); + rv = NS_taccess(newDistDir, F_OK); + if (!rv) { + LOG(("New distribution directory already exists... removing old " \ + "distribution directory: " LOG_S, oldDistDir)); + rv = ensure_remove_recursive(oldDistDir); + if (rv) { + LOG(("Removing old distribution directory failed - err: %d", rv)); + } + } else { + LOG(("Moving old distribution directory to new location. src: " LOG_S \ + ", dst:" LOG_S, oldDistDir, newDistDir)); + rv = rename_file(oldDistDir, newDistDir, true); + if (rv) { + LOG(("Moving old distribution directory to new location failed - " \ + "err: %d", rv)); + } + } + } + } + + if (isElevated) { + SetGroupOwnershipAndPermissions(gInstallDirPath); + freeArguments(argc, argv); + CleanupElevatedMacUpdate(false); + } else if (IsOwnedByGroupAdmin(gInstallDirPath)) { + // If the group ownership of the Firefox .app bundle was set to the "admin" + // group during a previous elevated update, we need to ensure that all files + // in the bundle have group ownership of "admin" as well as write permission + // for the group to not break updates in the future. + SetGroupOwnershipAndPermissions(gInstallDirPath); + } +#endif /* XP_MACOSX */ + LogFinish(); int retVal = LaunchCallbackAndPostProcessApps(argc, argv, callbackIndex #ifdef XP_WIN , elevatedLockFilePath , updateLockFileHandle +#elif XP_MACOSX + , isElevated #endif ); @@ -3650,8 +3908,13 @@ int AddPreCompleteActions(ActionList *list) return OK; } +#ifdef XP_MACOSX + mozilla::UniquePtr<NS_tchar[]> manifestPath(get_full_path( + NS_T("Contents/Resources/precomplete"))); +#else mozilla::UniquePtr<NS_tchar[]> manifestPath(get_full_path( NS_T("precomplete"))); +#endif NS_tchar *rb = GetManifestContents(manifestPath.get()); if (rb == nullptr) { diff --git a/toolkit/profile/content/createProfileWizard.js b/toolkit/profile/content/createProfileWizard.js index f378f36769..aa87eacd71 100644 --- a/toolkit/profile/content/createProfileWizard.js +++ b/toolkit/profile/content/createProfileWizard.js @@ -116,7 +116,11 @@ function checkCurrentInput(currentInput) if (!errorMessage) { finishText.className = ""; +#ifdef XP_MACOSX + finishText.firstChild.data = gProfileManagerBundle.getString("profileFinishTextMac"); +#else finishText.firstChild.data = gProfileManagerBundle.getString("profileFinishText"); +#endif canAdvance = true; } else { diff --git a/toolkit/profile/content/createProfileWizard.xul b/toolkit/profile/content/createProfileWizard.xul index c7e0702a62..eab1a93414 100644 --- a/toolkit/profile/content/createProfileWizard.xul +++ b/toolkit/profile/content/createProfileWizard.xul @@ -31,11 +31,15 @@ <description>&profileCreationExplanation_2.text;</description> <description>&profileCreationExplanation_3.text;</description> <spacer flex="1"/> +#ifdef XP_MACOSX + <description>&profileCreationExplanation_4Mac.text;</description> +#else #ifdef XP_UNIX <description>&profileCreationExplanation_4Gnome.text;</description> #else <description>&profileCreationExplanation_4.text;</description> #endif +#endif </wizardpage> <wizardpage id="createProfile" onpageshow="initSecondWizardPage();"> diff --git a/toolkit/profile/content/profileSelection.js b/toolkit/profile/content/profileSelection.js index 9fb77dfcd0..05ef6f5edb 100644 --- a/toolkit/profile/content/profileSelection.js +++ b/toolkit/profile/content/profileSelection.js @@ -134,7 +134,9 @@ function onProfilesKey(aEvent) switch ( aEvent.keyCode ) { case KeyEvent.DOM_VK_BACK_SPACE: +#ifndef XP_MACOSX break; +#endif case KeyEvent.DOM_VK_DELETE: ConfirmDelete(); break; diff --git a/toolkit/profile/jar.mn b/toolkit/profile/jar.mn index 9b7c22266e..1c4afac4ca 100644 --- a/toolkit/profile/jar.mn +++ b/toolkit/profile/jar.mn @@ -3,7 +3,7 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. toolkit.jar: - content/mozapps/profile/createProfileWizard.js (content/createProfileWizard.js) +* content/mozapps/profile/createProfileWizard.js (content/createProfileWizard.js) * content/mozapps/profile/createProfileWizard.xul (content/createProfileWizard.xul) - content/mozapps/profile/profileSelection.js (content/profileSelection.js) +* content/mozapps/profile/profileSelection.js (content/profileSelection.js) content/mozapps/profile/profileSelection.xul (content/profileSelection.xul) diff --git a/toolkit/profile/nsProfileLock.cpp b/toolkit/profile/nsProfileLock.cpp index 654fbcd465..d75b6082d3 100644 --- a/toolkit/profile/nsProfileLock.cpp +++ b/toolkit/profile/nsProfileLock.cpp @@ -13,6 +13,11 @@ #include "nsAutoPtr.h" #endif +#if defined(XP_MACOSX) +#include <Carbon/Carbon.h> +#include <CoreFoundation/CoreFoundation.h> +#endif + #ifdef XP_UNIX #include <unistd.h> #include <fcntl.h> @@ -421,7 +426,10 @@ nsresult nsProfileLock::GetReplacedLockTime(PRTime *aResult) { nsresult nsProfileLock::Lock(nsIFile* aProfileDir, nsIProfileUnlocker* *aUnlocker) { -#if defined (XP_UNIX) +#if defined (XP_MACOSX) + NS_NAMED_LITERAL_STRING(LOCKFILE_NAME, ".parentlock"); + NS_NAMED_LITERAL_STRING(OLD_LOCKFILE_NAME, "parent.lock"); +#elif defined (XP_UNIX) NS_NAMED_LITERAL_STRING(OLD_LOCKFILE_NAME, "lock"); NS_NAMED_LITERAL_STRING(LOCKFILE_NAME, ".parentlock"); #else @@ -450,7 +458,67 @@ nsresult nsProfileLock::Lock(nsIFile* aProfileDir, if (NS_FAILED(rv)) return rv; -#if defined(XP_UNIX) +#if defined(XP_MACOSX) + // First, try locking using fcntl. It is more reliable on + // a local machine, but may not be supported by an NFS server. + + rv = LockWithFcntl(lockFile); + if (NS_FAILED(rv) && (rv != NS_ERROR_FILE_ACCESS_DENIED)) + { + // If that failed for any reason other than NS_ERROR_FILE_ACCESS_DENIED, + // assume we tried an NFS that does not support it. Now, try with symlink. + rv = LockWithSymlink(lockFile, false); + } + + if (NS_SUCCEEDED(rv)) + { + // Check for the old-style lock used by pre-mozilla 1.3 builds. + // Those builds used an earlier check to prevent the application + // from launching if another instance was already running. Because + // of that, we don't need to create an old-style lock as well. + struct LockProcessInfo + { + ProcessSerialNumber psn; + unsigned long launchDate; + }; + + PRFileDesc *fd = nullptr; + int32_t ioBytes; + ProcessInfoRec processInfo; + LockProcessInfo lockProcessInfo; + + rv = lockFile->SetLeafName(OLD_LOCKFILE_NAME); + if (NS_FAILED(rv)) + return rv; + rv = lockFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fd); + if (NS_SUCCEEDED(rv)) + { + ioBytes = PR_Read(fd, &lockProcessInfo, sizeof(LockProcessInfo)); + PR_Close(fd); + + if (ioBytes == sizeof(LockProcessInfo)) + { +#ifdef __LP64__ + processInfo.processAppRef = nullptr; +#else + processInfo.processAppSpec = nullptr; +#endif + processInfo.processName = nullptr; + processInfo.processInfoLength = sizeof(ProcessInfoRec); + if (::GetProcessInformation(&lockProcessInfo.psn, &processInfo) == noErr && + processInfo.processLaunchDate == lockProcessInfo.launchDate) + { + return NS_ERROR_FILE_ACCESS_DENIED; + } + } + else + { + NS_WARNING("Could not read lock file - ignoring lock"); + } + } + rv = NS_OK; // Don't propagate error from OpenNSPRFileDesc. + } +#elif defined(XP_UNIX) // Get the old lockfile name nsCOMPtr<nsIFile> oldLockFile; rv = aProfileDir->Clone(getter_AddRefs(oldLockFile)); diff --git a/toolkit/profile/nsToolkitProfileService.cpp b/toolkit/profile/nsToolkitProfileService.cpp index 3380246da1..2fe51b2853 100644 --- a/toolkit/profile/nsToolkitProfileService.cpp +++ b/toolkit/profile/nsToolkitProfileService.cpp @@ -26,6 +26,11 @@ #include "nsIFile.h" #include "nsISimpleEnumerator.h" +#ifdef XP_MACOSX +#include <CoreFoundation/CoreFoundation.h> +#include "nsILocalFileMac.h" +#endif + #include "nsAppDirectoryServiceDefs.h" #include "nsXULAppAPI.h" @@ -1020,7 +1025,33 @@ NS_NewToolkitProfileService(nsIToolkitProfileService* *aResult) nsresult XRE_GetFileFromPath(const char *aPath, nsIFile* *aResult) { -#if defined(XP_UNIX) +#if defined(XP_MACOSX) + int32_t pathLen = strlen(aPath); + if (pathLen > MAXPATHLEN) + return NS_ERROR_INVALID_ARG; + + CFURLRef fullPath = + CFURLCreateFromFileSystemRepresentation(nullptr, (const UInt8 *) aPath, + pathLen, true); + if (!fullPath) + return NS_ERROR_FAILURE; + + nsCOMPtr<nsIFile> lf; + nsresult rv = NS_NewNativeLocalFile(EmptyCString(), true, + getter_AddRefs(lf)); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr<nsILocalFileMac> lfMac = do_QueryInterface(lf, &rv); + if (NS_SUCCEEDED(rv)) { + rv = lfMac->InitWithCFURL(fullPath); + if (NS_SUCCEEDED(rv)) { + lf.forget(aResult); + } + } + } + CFRelease(fullPath); + return rv; + +#elif defined(XP_UNIX) char fullPath[MAXPATHLEN]; if (!realpath(aPath, fullPath)) diff --git a/toolkit/themes/shared/aboutReader.css b/toolkit/themes/shared/aboutReader.css index c6cd959277..1bfbd0309d 100644 --- a/toolkit/themes/shared/aboutReader.css +++ b/toolkit/themes/shared/aboutReader.css @@ -388,7 +388,19 @@ body:not(.loaded) .toolbar:-moz-locale-dir(rtl) { } /* Font sizes are different per-platform, so we need custom CSS to line them up. */ -%ifdef XP_WIN +%ifdef XP_MACOSX +.font-type-buttons > .sans-serif-button > .name { + margin-top: 10px; +} + +.font-type-buttons > .sans-serif-button > .description { + margin-top: -4px; +} + +.font-type-buttons > .serif-button > .name { + font-size: 63px; +} +%elifdef XP_WIN .font-type-buttons > .sans-serif-button > .name { margin-top: 2px; } diff --git a/toolkit/themes/shared/jar.inc.mn b/toolkit/themes/shared/jar.inc.mn index 08f3250dcc..3755688279 100644 --- a/toolkit/themes/shared/jar.inc.mn +++ b/toolkit/themes/shared/jar.inc.mn @@ -62,7 +62,7 @@ toolkit.jar: skin/classic/global/media/TopLevelVideoDocument.css (../../shared/media/TopLevelVideoDocument.css) skin/classic/global/media/imagedoc-lightnoise.png (../../shared/media/imagedoc-lightnoise.png) skin/classic/global/media/imagedoc-darknoise.png (../../shared/media/imagedoc-darknoise.png) - skin/classic/global/media/videocontrols.css (../../shared/media/videocontrols.css) +* skin/classic/global/media/videocontrols.css (../../shared/media/videocontrols.css) skin/classic/global/media/pauseButton.png (../../shared/media/pauseButton.png) skin/classic/global/media/pauseButton@2x.png (../../shared/media/pauseButton@2x.png) skin/classic/global/media/playButton.png (../../shared/media/playButton.png) diff --git a/toolkit/themes/shared/media/videocontrols.css b/toolkit/themes/shared/media/videocontrols.css index 690762062b..a40d77fe3a 100644 --- a/toolkit/themes/shared/media/videocontrols.css +++ b/toolkit/themes/shared/media/videocontrols.css @@ -165,9 +165,15 @@ font-size: 11px; } +%ifdef XP_MACOSX +.durationLabel { + padding-top: 2px; /* center vertically with scrubber bar */ +} +%else .durationLabel { padding-top: 0; /* center vertically with scrubber bar */ } +%endif .positionLabel { display: none; @@ -253,9 +259,15 @@ padding-top: 7px; } +%ifdef XP_MACOSX +.timeLabel { + padding-top: 7px; /* center vertically with scrubber bar */ +} +%else .timeLabel { padding-top: 5px; /* center vertically with scrubber bar */ } +%endif .statusOverlay { -moz-box-align: center; diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp index b15335ade3..274631aac8 100644 --- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -31,6 +31,16 @@ #include "EventTracer.h" #endif +#ifdef XP_MACOSX +#include "nsVersionComparator.h" +#include "MacLaunchHelper.h" +#include "MacApplicationDelegate.h" +#include "MacAutoreleasePool.h" +// these are needed for sysctl +#include <sys/types.h> +#include <sys/sysctl.h> +#endif + #include "prmem.h" #include "prnetdb.h" #include "prprf.h" @@ -144,6 +154,11 @@ #include "WinUtils.h" #endif +#ifdef XP_MACOSX +#include "nsILocalFileMac.h" +#include "nsCommandLineServiceMac.h" +#endif + // for X remote support #ifdef MOZ_ENABLE_XREMOTE #include "XRemoteClient.h" @@ -159,6 +174,10 @@ #include <malloc.h> #endif +#if defined (XP_MACOSX) +#include <Carbon/Carbon.h> +#endif + #ifdef DEBUG #include "mozilla/Logging.h" #endif @@ -1072,6 +1091,12 @@ ScopedXPCOMStartup::~ScopedXPCOMStartup() NS_IF_RELEASE(gNativeAppSupport); if (mServiceManager) { +#ifdef XP_MACOSX + // On OS X, we need a pool to catch cocoa objects that are autoreleased + // during teardown. + mozilla::MacAutoreleasePool pool; +#endif + nsCOMPtr<nsIAppStartup> appStartup (do_GetService(NS_APPSTARTUP_CONTRACTID)); if (appStartup) appStartup->DestroyHiddenWindow(); @@ -1457,6 +1482,10 @@ static nsresult LaunchChild(nsINativeAppSupport* aNative, SaveToEnv("MOZ_LAUNCHED_CHILD=1"); +#if defined(XP_MACOSX) + CommandLineServiceMac::SetupMacCommandLine(gRestartArgc, gRestartArgv, true); + LaunchChildMac(gRestartArgc, gRestartArgv); +#else nsCOMPtr<nsIFile> lf; nsresult rv = XRE_GetBinaryPath(gArgv[0], getter_AddRefs(lf)); if (NS_FAILED(rv)) @@ -1491,6 +1520,7 @@ static nsresult LaunchChild(nsINativeAppSupport* aNative, return NS_ERROR_FAILURE; #endif // XP_UNIX #endif // WP_WIN +#endif // WP_MACOSX return NS_ERROR_LAUNCHED_CHILD_PROCESS; } @@ -1559,9 +1589,15 @@ ProfileLockedDialog(nsIFile* aProfileDir, nsIFile* aProfileLocalDir, const char16_t* params[] = {appName.get(), appName.get()}; nsXPIDLString killMessage; +#ifndef XP_MACOSX sb->FormatStringFromName(aUnlocker ? u"restartMessageUnlocker" : u"restartMessageNoUnlocker", params, 2, getter_Copies(killMessage)); +#else + sb->FormatStringFromName(aUnlocker ? u"restartMessageUnlockerMac" + : u"restartMessageNoUnlockerMac", + params, 2, getter_Copies(killMessage)); +#endif nsXPIDLString killTitle; sb->FormatStringFromName(u"restartTitle", @@ -1705,6 +1741,10 @@ ShowProfileManager(nsIToolkitProfileService* aProfileSvc, rv = xpcom.SetWindowCreator(aNative); NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); +#ifdef XP_MACOSX + CommandLineServiceMac::SetupMacCommandLine(gRestartArgc, gRestartArgv, true); +#endif + #ifdef XP_WIN // we don't have to wait here because profile manager window will pump // and DDE message will be handled @@ -2810,6 +2850,13 @@ XREMain::XRE_mainInit(bool* aExitFlag) if (NS_FAILED(rv)) return 2; +#ifdef XP_MACOSX + nsCOMPtr<nsIFile> parent; + greDir->GetParent(getter_AddRefs(parent)); + greDir = parent.forget(); + greDir->AppendNative(NS_LITERAL_CSTRING("Resources")); +#endif + greDir.forget(&mAppData->xreDirectory); } @@ -2843,6 +2890,40 @@ XREMain::XRE_mainInit(bool* aExitFlag) if (NS_FAILED(rv)) return 1; +#ifdef XP_MACOSX + // Set up ability to respond to system (Apple) events. This must occur before + // ProcessUpdates to ensure that links clicked in external applications aren't + // lost when updates are pending. + SetupMacApplicationDelegate(); + + if (EnvHasValue("MOZ_LAUNCHED_CHILD")) { + // This is needed, on relaunch, to force the OS to use the "Cocoa Dock + // API". Otherwise the call to ReceiveNextEvent() below will make it + // use the "Carbon Dock API". For more info see bmo bug 377166. + EnsureUseCocoaDockAPI(); + + // When the app relaunches, the original process exits. This causes + // the dock tile to stop bouncing, lose the "running" triangle, and + // if the tile does not permanently reside in the Dock, even disappear. + // This can be confusing to the user, who is expecting the app to launch. + // Calling ReceiveNextEvent without requesting any event is enough to + // cause a dock tile for the child process to appear. + const EventTypeSpec kFakeEventList[] = { { INT_MAX, INT_MAX } }; + EventRef event; + ::ReceiveNextEvent(GetEventTypeCount(kFakeEventList), kFakeEventList, + kEventDurationNoWait, false, &event); + } + + if (CheckArg("foreground")) { + // The original process communicates that it was in the foreground by + // adding this argument. This new process, which is taking over for + // the old one, should make itself the active application. + ProcessSerialNumber psn; + if (::GetCurrentProcess(&psn) == noErr) + ::SetFrontProcess(&psn); + } +#endif + SaveToEnv("MOZ_LAUNCHED_CHILD="); gRestartArgc = gArgc; @@ -2893,6 +2974,12 @@ XREMain::XRE_mainInit(bool* aExitFlag) } #endif +#ifdef XP_MACOSX + if ((GetCurrentEventKeyModifiers() & optionKey) && + !EnvHasValue("MOZ_DISABLE_SAFE_MODE_KEY")) + gSafeMode = true; +#endif + #ifdef XP_WIN { // Add CPU microcode version to the crash report as "CPUMicrocodeVersion". @@ -3666,6 +3753,19 @@ XREMain::XRE_mainRun() g_unsetenv ("DESKTOP_STARTUP_ID"); #endif +#ifdef XP_MACOSX + // we re-initialize the command-line service and do appleevents munging + // after we are sure that we're not restarting + cmdLine = do_CreateInstance("@mozilla.org/toolkit/command-line;1"); + NS_ENSURE_TRUE(cmdLine, NS_ERROR_FAILURE); + + CommandLineServiceMac::SetupMacCommandLine(gArgc, gArgv, false); + + rv = cmdLine->Init(gArgc, gArgv, + workingDir, nsICommandLine::STATE_INITIAL_LAUNCH); + NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); +#endif + nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService(); if (obsService) diff --git a/toolkit/xre/nsEmbedFunctions.cpp b/toolkit/xre/nsEmbedFunctions.cpp index 841ea2a2d0..109782b5f8 100644 --- a/toolkit/xre/nsEmbedFunctions.cpp +++ b/toolkit/xre/nsEmbedFunctions.cpp @@ -39,6 +39,10 @@ #include "nsXREDirProvider.h" #include "mozilla/Omnijar.h" +#if defined(XP_MACOSX) +#include "nsVersionComparator.h" +#include "chrome/common/mach_ipc_mac.h" +#endif #include "nsX11ErrorHandler.h" #include "nsGDKErrorHandler.h" #include "base/at_exit.h" @@ -280,6 +284,81 @@ XRE_InitChildProcess(int aArgc, PROFILER_LABEL("Startup", "XRE_InitChildProcess", js::ProfileEntry::Category::OTHER); + // Complete 'task_t' exchange for Mac OS X. This structure has the same size + // regardless of architecture so we don't have any cross-arch issues here. +#ifdef XP_MACOSX + if (aArgc < 1) + return NS_ERROR_FAILURE; + const char* const mach_port_name = aArgv[--aArgc]; + + const int kTimeoutMs = 1000; + + MachSendMessage child_message(0); + if (!child_message.AddDescriptor(MachMsgPortDescriptor(mach_task_self()))) { + NS_WARNING("child AddDescriptor(mach_task_self()) failed."); + return NS_ERROR_FAILURE; + } + + ReceivePort child_recv_port; + mach_port_t raw_child_recv_port = child_recv_port.GetPort(); + if (!child_message.AddDescriptor(MachMsgPortDescriptor(raw_child_recv_port))) { + NS_WARNING("Adding descriptor to message failed"); + return NS_ERROR_FAILURE; + } + + ReceivePort* ports_out_receiver = new ReceivePort(); + if (!child_message.AddDescriptor(MachMsgPortDescriptor(ports_out_receiver->GetPort()))) { + NS_WARNING("Adding descriptor to message failed"); + return NS_ERROR_FAILURE; + } + + ReceivePort* ports_in_receiver = new ReceivePort(); + if (!child_message.AddDescriptor(MachMsgPortDescriptor(ports_in_receiver->GetPort()))) { + NS_WARNING("Adding descriptor to message failed"); + return NS_ERROR_FAILURE; + } + + MachPortSender child_sender(mach_port_name); + kern_return_t err = child_sender.SendMessage(child_message, kTimeoutMs); + if (err != KERN_SUCCESS) { + NS_WARNING("child SendMessage() failed"); + return NS_ERROR_FAILURE; + } + + MachReceiveMessage parent_message; + err = child_recv_port.WaitForMessage(&parent_message, kTimeoutMs); + if (err != KERN_SUCCESS) { + NS_WARNING("child WaitForMessage() failed"); + return NS_ERROR_FAILURE; + } + + if (parent_message.GetTranslatedPort(0) == MACH_PORT_NULL) { + NS_WARNING("child GetTranslatedPort(0) failed"); + return NS_ERROR_FAILURE; + } + + err = task_set_bootstrap_port(mach_task_self(), + parent_message.GetTranslatedPort(0)); + + if (parent_message.GetTranslatedPort(1) == MACH_PORT_NULL) { + NS_WARNING("child GetTranslatedPort(1) failed"); + return NS_ERROR_FAILURE; + } + MachPortSender* ports_out_sender = new MachPortSender(parent_message.GetTranslatedPort(1)); + + if (parent_message.GetTranslatedPort(2) == MACH_PORT_NULL) { + NS_WARNING("child GetTranslatedPort(2) failed"); + return NS_ERROR_FAILURE; + } + MachPortSender* ports_in_sender = new MachPortSender(parent_message.GetTranslatedPort(2)); + + if (err != KERN_SUCCESS) { + NS_WARNING("child task_set_bootstrap_port() failed"); + return NS_ERROR_FAILURE; + } + +#endif + SetupErrorHandling(aArgv[0]); gArgv = aArgv; @@ -325,6 +404,11 @@ XRE_InitChildProcess(int aArgc, base::ProcessId parentPID = strtol(parentPIDString, &end, 10); MOZ_ASSERT(!*end, "invalid parent PID"); +#ifdef XP_MACOSX + mozilla::ipc::SharedMemoryBasic::SetupMachMemory(parentPID, ports_in_receiver, ports_in_sender, + ports_out_sender, ports_out_receiver, true); +#endif + #if defined(XP_WIN) // On Win7+, register the application user model id passed in by // parent. This insures windows created by the container properly @@ -456,6 +540,11 @@ XRE_InitChildProcess(int aArgc, // scope and being deleted process->CleanUp(); mozilla::Omnijar::CleanUp(); + +#if defined(XP_MACOSX) + // Everybody should be done using shared memory by now. + mozilla::ipc::SharedMemoryBasic::Shutdown(); +#endif } } @@ -571,6 +660,47 @@ XRE_RunAppShell() { nsCOMPtr<nsIAppShell> appShell(do_GetService(kAppShellCID)); NS_ENSURE_TRUE(appShell, NS_ERROR_FAILURE); +#if defined(XP_MACOSX) + { + // In content processes that want XPCOM (and hence want + // AppShell), we usually run our hybrid event loop through + // MessagePump::Run(), by way of nsBaseAppShell::Run(). The + // Cocoa nsAppShell impl, however, implements its own Run() + // that's unaware of MessagePump. That's all rather suboptimal, + // but oddly enough not a problem... usually. + // + // The problem with this setup comes during startup. + // XPCOM-in-subprocesses depends on IPC, e.g. to init the pref + // service, so we have to init IPC first. But, IPC also + // indirectly kinda-depends on XPCOM, because MessagePump + // schedules work from off-main threads (e.g. IO thread) by + // using NS_DispatchToMainThread(). If the IO thread receives a + // Message from the parent before nsThreadManager is + // initialized, then DispatchToMainThread() will fail, although + // MessagePump will remember the task. This race condition + // isn't a problem when appShell->Run() ends up in + // MessagePump::Run(), because MessagePump will immediate see it + // has work to do. It *is* a problem when we end up in [NSApp + // run], because it's not aware that MessagePump has work that + // needs to be processed; that was supposed to be signaled by + // nsIRunnable(s). + // + // So instead of hacking Cocoa nsAppShell or rewriting the + // event-loop system, we compromise here by processing any tasks + // that might have been enqueued on MessagePump, *before* + // MessagePump::ScheduleWork was able to successfully + // DispatchToMainThread(). + MessageLoop* loop = MessageLoop::current(); + bool couldNest = loop->NestableTasksAllowed(); + + loop->SetNestableTasksAllowed(true); + RefPtr<Runnable> task = new MessageLoop::QuitTask(); + loop->PostTask(task.forget()); + loop->Run(); + + loop->SetNestableTasksAllowed(couldNest); + } +#endif // XP_MACOSX return appShell->Run(); } @@ -589,6 +719,16 @@ XRE_ShutdownChildProcess() // (4) ProcessChild joins the IO thread // (5) exit() MessageLoop::current()->Quit(); +#if defined(XP_MACOSX) + nsCOMPtr<nsIAppShell> appShell(do_GetService(kAppShellCID)); + if (appShell) { + // On Mac, we might be only above nsAppShell::Run(), not + // MessagePump::Run(). See XRE_RunAppShell(). To account for + // that case, we fire off an Exit() here. If we were indeed + // above MessagePump::Run(), this Exit() is just superfluous. + appShell->Exit(); + } +#endif // XP_MACOSX } namespace { diff --git a/toolkit/xre/nsSigHandlers.cpp b/toolkit/xre/nsSigHandlers.cpp index 68d909848c..11648e45ac 100644 --- a/toolkit/xre/nsSigHandlers.cpp +++ b/toolkit/xre/nsSigHandlers.cpp @@ -154,6 +154,22 @@ static void fpehandler(int signum, siginfo_t *si, void *context) NS_DebugBreak(NS_DEBUG_ABORT, "Divide by zero", nullptr, __FILE__, __LINE__); } +#ifdef XP_MACOSX + ucontext_t *uc = (ucontext_t *)context; + +#if defined(__i386__) || defined(__amd64__) + _STRUCT_FP_CONTROL *ctrl = &uc->uc_mcontext->__fs.__fpu_fcw; + ctrl->__invalid = ctrl->__denorm = ctrl->__zdiv = ctrl->__ovrfl = ctrl->__undfl = ctrl->__precis = 1; + + _STRUCT_FP_STATUS *status = &uc->uc_mcontext->__fs.__fpu_fsw; + status->__invalid = status->__denorm = status->__zdiv = status->__ovrfl = status->__undfl = + status->__precis = status->__stkflt = status->__errsumm = 0; + + uint32_t *mxcsr = &uc->uc_mcontext->__fs.__fpu_mxcsr; + *mxcsr |= SSE_EXCEPTION_MASK; /* disable all SSE exceptions */ + *mxcsr &= ~SSE_STATUS_FLAGS; /* clear all pending SSE exceptions */ +#endif +#endif #if defined(LINUX) ucontext_t *uc = (ucontext_t *)context; diff --git a/toolkit/xre/nsUpdateDriver.cpp b/toolkit/xre/nsUpdateDriver.cpp index 812818788c..4994458852 100644 --- a/toolkit/xre/nsUpdateDriver.cpp +++ b/toolkit/xre/nsUpdateDriver.cpp @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ /* 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/. */ @@ -26,6 +27,13 @@ #include "nsPrintfCString.h" #include "mozilla/DebugOnly.h" +#ifdef XP_MACOSX +#include "nsILocalFileMac.h" +#include "nsCommandLineServiceMac.h" +#include "MacLaunchHelper.h" +#include "updaterfileutils_osx.h" +#endif + #if defined(XP_WIN) # include <direct.h> # include <process.h> @@ -53,11 +61,16 @@ GetUpdateLog() #ifdef XP_WIN #define UPDATER_BIN "updater.exe" +#elif XP_MACOSX +#define UPDATER_BIN "org.mozilla.updater" #else #define UPDATER_BIN "updater" #endif #define UPDATER_INI "updater.ini" -#if defined(XP_UNIX) +#ifdef XP_MACOSX +#define UPDATER_APP "updater.app" +#endif +#if defined(XP_UNIX) && !defined(XP_MACOSX) #define UPDATER_PNG "updater.png" #endif @@ -96,7 +109,18 @@ static nsresult GetInstallDirPath(nsIFile *appDir, nsACString& installDirPath) { nsresult rv; -#if XP_WIN +#ifdef XP_MACOSX + nsCOMPtr<nsIFile> parentDir1, parentDir2; + rv = appDir->GetParent(getter_AddRefs(parentDir1)); + if (NS_FAILED(rv)) { + return rv; + } + rv = parentDir1->GetParent(getter_AddRefs(parentDir2)); + if (NS_FAILED(rv)) { + return rv; + } + rv = parentDir2->GetNativePath(installDirPath); +#elif XP_WIN nsAutoString installDirPathW; rv = appDir->GetPath(installDirPathW); if (NS_FAILED(rv)) { @@ -112,6 +136,35 @@ GetInstallDirPath(nsIFile *appDir, nsACString& installDirPath) return NS_OK; } +#if defined(XP_MACOSX) +// This is a copy of OS X's XRE_GetBinaryPath from nsAppRunner.cpp with the +// gBinaryPath check removed so that the updater can reload the stub executable +// instead of xulrunner-bin. See bug 349737. +static nsresult +GetXULRunnerStubPath(const char* argv0, nsIFile* *aResult) +{ + // Works even if we're not bundled. + CFBundleRef appBundle = ::CFBundleGetMainBundle(); + if (!appBundle) + return NS_ERROR_FAILURE; + + CFURLRef bundleURL = ::CFBundleCopyExecutableURL(appBundle); + if (!bundleURL) + return NS_ERROR_FAILURE; + + nsCOMPtr<nsILocalFileMac> lfm; + nsresult rv = NS_NewLocalFileWithCFURL(bundleURL, true, getter_AddRefs(lfm)); + + ::CFRelease(bundleURL); + + if (NS_FAILED(rv)) + return rv; + + lfm.forget(aResult); + return NS_OK; +} +#endif /* XP_MACOSX */ + static bool GetFile(nsIFile *dir, const nsCSubstring &name, nsCOMPtr<nsIFile> &result) { @@ -271,10 +324,16 @@ CopyUpdaterIntoUpdateDir(nsIFile *greDir, nsIFile *appDir, nsIFile *updateDir, nsCOMPtr<nsIFile> &updater) { // Copy the updater application from the GRE and the updater ini from the app +#if defined(XP_MACOSX) + if (!CopyFileIntoUpdateDir(appDir, NS_LITERAL_CSTRING(UPDATER_APP), updateDir)) + return false; + CopyFileIntoUpdateDir(greDir, NS_LITERAL_CSTRING(UPDATER_INI), updateDir); +#else if (!CopyFileIntoUpdateDir(greDir, NS_LITERAL_CSTRING(UPDATER_BIN), updateDir)) return false; CopyFileIntoUpdateDir(appDir, NS_LITERAL_CSTRING(UPDATER_INI), updateDir); -#if defined(XP_UNIX) +#endif +#if defined(XP_UNIX) && !defined(XP_MACOSX) nsCOMPtr<nsIFile> iconDir; appDir->Clone(getter_AddRefs(iconDir)); iconDir->AppendNative(NS_LITERAL_CSTRING("icons")); @@ -285,6 +344,16 @@ CopyUpdaterIntoUpdateDir(nsIFile *greDir, nsIFile *appDir, nsIFile *updateDir, nsresult rv = updateDir->Clone(getter_AddRefs(updater)); if (NS_FAILED(rv)) return false; +#if defined(XP_MACOSX) + rv = updater->AppendNative(NS_LITERAL_CSTRING(UPDATER_APP)); + nsresult tmp = updater->AppendNative(NS_LITERAL_CSTRING("Contents")); + if (NS_FAILED(tmp)) { + rv = tmp; + } + tmp = updater->AppendNative(NS_LITERAL_CSTRING("MacOS")); + if (NS_FAILED(tmp) || NS_FAILED(rv)) + return false; +#endif rv = updater->AppendNative(NS_LITERAL_CSTRING(UPDATER_BIN)); return NS_SUCCEEDED(rv); } @@ -295,7 +364,8 @@ CopyUpdaterIntoUpdateDir(nsIFile *greDir, nsIFile *appDir, nsIFile *updateDir, * * @param pathToAppend A new library path to prepend to LD_LIBRARY_PATH */ -#if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN) +#if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN) && \ + !defined(XP_MACOSX) #include "prprf.h" #define PATH_SEPARATOR ":" #define LD_LIBRARY_PATH_ENVVAR_NAME "LD_LIBRARY_PATH" @@ -372,7 +442,13 @@ SwitchToUpdatedApp(nsIFile *greDir, nsIFile *updateDir, // to restart the running application. nsCOMPtr<nsIFile> appFile; +#if defined(XP_MACOSX) + // On OS X we need to pass the location of the xulrunner-stub executable + // rather than xulrunner-bin. See bug 349737. + GetXULRunnerStubPath(appArgv[0], getter_AddRefs(appFile)); +#else XRE_GetBinaryPath(appArgv[0], getter_AddRefs(appFile)); +#endif if (!appFile) return; @@ -424,7 +500,11 @@ SwitchToUpdatedApp(nsIFile *greDir, nsIFile *updateDir, // Get the directory where the update will be staged. nsAutoCString applyToDir; nsCOMPtr<nsIFile> updatedDir; +#ifdef XP_MACOSX + if (!GetFile(updateDir, NS_LITERAL_CSTRING("Updated.app"), updatedDir)) { +#else if (!GetFile(appDir, NS_LITERAL_CSTRING("updated"), updatedDir)) { +#endif return; } #ifdef XP_WIN @@ -468,7 +548,7 @@ SwitchToUpdatedApp(nsIFile *greDir, nsIFile *updateDir, // Construct the PID argument for this process. We start the updater using // execv on all Unix platforms except Mac, so on those platforms we pass 0 // instead of a good PID to signal the updater not to try and wait for us. -#if defined(XP_UNIX) +#if defined(XP_UNIX) & !defined(XP_MACOSX) nsAutoCString pid("0"); #else nsAutoCString pid; @@ -508,13 +588,14 @@ SwitchToUpdatedApp(nsIFile *greDir, nsIFile *updateDir, if (gSafeMode) { PR_SetEnv("MOZ_SAFE_MODE_RESTART=1"); } -#if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN) +#if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN) && \ + !defined(XP_MACOSX) AppendToLibPath(installDirPath.get()); #endif LOG(("spawning updater process for replacing [%s]\n", updaterPath.get())); -#if defined(XP_UNIX) +#if defined(XP_UNIX) & !defined(XP_MACOSX) exit(execv(updaterPath.get(), argv)); #elif defined(XP_WIN) // Switch the application using updater.exe @@ -522,6 +603,10 @@ SwitchToUpdatedApp(nsIFile *greDir, nsIFile *updateDir, return; } _exit(0); +#elif defined(XP_MACOSX) + CommandLineServiceMac::SetupMacCommandLine(argc, argv, true); + LaunchChildMac(argc, argv); + exit(0); #else PR_CreateProcessDetached(updaterPath.get(), argv, nullptr, nullptr); exit(0); @@ -567,7 +652,13 @@ ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsIFile *statusFile, // to restart the running application. nsCOMPtr<nsIFile> appFile; +#if defined(XP_MACOSX) + // On OS X we need to pass the location of the xulrunner-stub executable + // rather than xulrunner-bin. See bug 349737. + GetXULRunnerStubPath(appArgv[0], getter_AddRefs(appFile)); +#else XRE_GetBinaryPath(appArgv[0], getter_AddRefs(appFile)); +#endif if (!appFile) return; @@ -623,7 +714,11 @@ ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsIFile *statusFile, if (restart && !isOSUpdate) { applyToDir.Assign(installDirPath); } else { +#ifdef XP_MACOSX + if (!GetFile(updateDir, NS_LITERAL_CSTRING("Updated.app"), updatedDir)) { +#else if (!GetFile(appDir, NS_LITERAL_CSTRING("updated"), updatedDir)) { +#endif return; } #ifdef XP_WIN @@ -678,7 +773,7 @@ ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsIFile *statusFile, // Signal the updater application that it should stage the update. pid.AssignASCII("-1"); } else { -#if defined(XP_UNIX) +#if defined(XP_UNIX) & !defined(XP_MACOSX) pid.AssignASCII("0"); #else pid.AppendInt((int32_t) getpid()); @@ -714,7 +809,8 @@ ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsIFile *statusFile, if (gSafeMode) { PR_SetEnv("MOZ_SAFE_MODE_RESTART=1"); } -#if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN) +#if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN) && \ + !defined(XP_MACOSX) AppendToLibPath(installDirPath.get()); #endif @@ -724,10 +820,10 @@ ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsIFile *statusFile, LOG(("spawning updater process [%s]\n", updaterPath.get())); -#if defined(XP_UNIX) - // We use execv to spawn the updater process on all UNIX-like systems. - // Windows has execv, but it is a faked implementation that doesn't really - // replace the current process. +#if defined(XP_UNIX) && !defined(XP_MACOSX) + // We use execv to spawn the updater process on all UNIX systems except Mac OSX + // since it is known to cause problems on the Mac. Windows has execv, but it + // is a faked implementation that doesn't really replace the current process. // Instead it spawns a new process, so we gain nothing from using execv on // Windows. if (restart) { @@ -749,6 +845,24 @@ ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsIFile *statusFile, // We are going to process an update so we should exit now _exit(0); } +#elif defined(XP_MACOSX) + CommandLineServiceMac::SetupMacCommandLine(argc, argv, restart); + // We need to detect whether elevation is required for this update. This can + // occur when an admin user installs the application, but another admin + // user attempts to update (see bug 394984). + if (restart && !IsRecursivelyWritable(installDirPath.get())) { + if (!LaunchElevatedUpdate(argc, argv, outpid)) { + LOG(("Failed to launch elevated update!")); + exit(1); + } + exit(0); + } else { + if (restart) { + LaunchChildMac(argc, argv); + exit(0); + } + LaunchChildMac(argc, argv, outpid); + } #else *outpid = PR_CreateProcess(updaterPath.get(), argv, nullptr, nullptr); if (restart) { @@ -769,6 +883,9 @@ ProcessHasTerminated(ProcessType pt) } CloseHandle(pt); return true; +#elif defined(XP_MACOSX) + // We're waiting for the process to terminate in LaunchChildMac. + return true; #elif defined(XP_UNIX) int exitStatus; pid_t exited = waitpid(pt, &exitStatus, WNOHANG); diff --git a/toolkit/xre/nsXREDirProvider.cpp b/toolkit/xre/nsXREDirProvider.cpp index fdeee8d0f9..e38cc4f35b 100644 --- a/toolkit/xre/nsXREDirProvider.cpp +++ b/toolkit/xre/nsXREDirProvider.cpp @@ -48,11 +48,19 @@ #include <windows.h> #include <shlobj.h> #endif +#ifdef XP_MACOSX +#include "nsILocalFileMac.h" +// for chflags() +#include <sys/stat.h> +#include <unistd.h> +#endif #ifdef XP_UNIX #include <ctype.h> #endif -#if defined(XP_WIN) +#if defined(XP_MACOSX) +#define APP_REGISTRY_NAME "Application Registry" +#elif defined(XP_WIN) #define APP_REGISTRY_NAME "registry.dat" #else #define APP_REGISTRY_NAME "appreg" @@ -100,6 +108,9 @@ nsXREDirProvider::Initialize(nsIFile *aXULAppDir, mXULAppDir = aXULAppDir; mGREDir = aGREDir; mGREDir->Clone(getter_AddRefs(mGREBinDir)); +#ifdef XP_MACOSX + mGREBinDir->SetNativeLeafName(NS_LITERAL_CSTRING("MacOS")); +#endif if (!mProfileDir) { nsCOMPtr<nsIDirectoryServiceProvider> app(do_QueryInterface(mAppProvider)); @@ -129,6 +140,30 @@ nsXREDirProvider::SetProfile(nsIFile* aDir, nsIFile* aLocalDir) if (NS_FAILED(rv)) return rv; +#ifdef XP_MACOSX + bool same; + if (NS_SUCCEEDED(aDir->Equals(aLocalDir, &same)) && !same) { + // Ensure that the cache directory is not indexed by Spotlight + // (bug 718910). At least on OS X, the cache directory (under + // ~/Library/Caches/) is always the "local" user profile + // directory. This is confusing, since *both* user profile + // directories are "local" (they both exist under the user's + // home directory). But this usage dates back at least as far + // as the patch for bug 291033, where "local" seems to mean + // "suitable for temporary storage". Don't hide the cache + // directory if by some chance it and the "non-local" profile + // directory are the same -- there are bad side effects from + // hiding a profile directory under /Library/Application Support/ + // (see bug 801883). + nsAutoCString cacheDir; + if (NS_SUCCEEDED(aLocalDir->GetNativePath(cacheDir))) { + if (chflags(cacheDir.get(), UF_HIDDEN)) { + NS_WARNING("Failed to set Cache directory to HIDDEN."); + } + } + } +#endif + mProfileDir = aDir; mProfileLocalDir = aLocalDir; return NS_OK; @@ -163,7 +198,7 @@ nsXREDirProvider::GetUserProfilesRootDir(nsIFile** aResult, aProfileName, aAppName, aVendorName); if (NS_SUCCEEDED(rv)) { -#if !defined(XP_UNIX) +#if !defined(XP_UNIX) || defined(XP_MACOSX) rv = file->AppendNative(NS_LITERAL_CSTRING("Profiles")); #endif // We must create the profile directory here if it does not exist. @@ -188,7 +223,7 @@ nsXREDirProvider::GetUserProfilesLocalDir(nsIFile** aResult, aProfileName, aAppName, aVendorName); if (NS_SUCCEEDED(rv)) { -#if !defined(XP_UNIX) +#if !defined(XP_UNIX) || defined(XP_MACOSX) rv = file->AppendNative(NS_LITERAL_CSTRING("Profiles")); #endif // We must create the profile directory here if it does not exist. @@ -201,7 +236,7 @@ nsXREDirProvider::GetUserProfilesLocalDir(nsIFile** aResult, return NS_OK; } -#if defined(XP_UNIX) +#if defined(XP_UNIX) || defined(XP_MACOSX) /** * Get the directory that is the parent of the system-wide directories * for extensions and native-messaing manifests. @@ -215,6 +250,12 @@ GetSystemParentDirectory(nsIFile** aFile) { nsresult rv; nsCOMPtr<nsIFile> localDir; +#if defined(XP_MACOSX) + rv = GetOSXFolderType(kOnSystemDisk, kApplicationSupportFolderType, getter_AddRefs(localDir)); + if (NS_SUCCEEDED(rv)) { + rv = localDir->AppendNative(NS_LITERAL_CSTRING("Mozilla")); + } +#else NS_NAMED_LITERAL_CSTRING(dirname, #ifdef HAVE_USR_LIB64_DIR "/usr/lib64/mozilla" @@ -225,6 +266,7 @@ GetSystemParentDirectory(nsIFile** aFile) #endif ); rv = NS_NewNativeLocalFile(dirname, false, getter_AddRefs(localDir)); +#endif if (NS_SUCCEEDED(rv)) { localDir.forget(aFile); @@ -308,14 +350,18 @@ nsXREDirProvider::GetFile(const char* aProperty, bool* aPersistent, !strcmp(aProperty, XRE_USER_APP_DATA_DIR)) { rv = GetUserAppDataDirectory(getter_AddRefs(file)); } -#if defined(XP_UNIX) +#if defined(XP_UNIX) || defined(XP_MACOSX) else if (!strcmp(aProperty, XRE_SYS_NATIVE_MESSAGING_MANIFESTS)) { nsCOMPtr<nsIFile> localDir; rv = ::GetSystemParentDirectory(getter_AddRefs(localDir)); if (NS_SUCCEEDED(rv)) { NS_NAMED_LITERAL_CSTRING(dirname, +#if defined(XP_MACOSX) + "NativeMessagingHosts" +#else "native-messaging-hosts" +#endif ); rv = localDir->AppendNative(dirname); if (NS_SUCCEEDED(rv)) { @@ -327,10 +373,17 @@ nsXREDirProvider::GetFile(const char* aProperty, bool* aPersistent, nsCOMPtr<nsIFile> localDir; rv = GetUserDataDirectoryHome(getter_AddRefs(localDir), false); if (NS_SUCCEEDED(rv)) { +#if defined(XP_MACOSX) + rv = localDir->AppendNative(NS_LITERAL_CSTRING("Mozilla")); + if (NS_SUCCEEDED(rv)) { + rv = localDir->AppendNative(NS_LITERAL_CSTRING("NativeMessagingHosts")); + } +#else rv = localDir->AppendNative(NS_LITERAL_CSTRING(".mozilla")); if (NS_SUCCEEDED(rv)) { rv = localDir->AppendNative(NS_LITERAL_CSTRING("native-messaging-hosts")); } +#endif } if (NS_SUCCEEDED(rv)) { localDir.swap(file); @@ -372,7 +425,7 @@ nsXREDirProvider::GetFile(const char* aProperty, bool* aPersistent, return mAppProvider->GetFile(NS_APP_PROFILE_DIR_STARTUP, aPersistent, aFile); } -#if defined(XP_UNIX) +#if defined(XP_UNIX) || defined(XP_MACOSX) else if (!strcmp(aProperty, XRE_SYS_LOCAL_EXTENSION_PARENT_DIR)) { #ifdef ENABLE_SYSTEM_EXTENSION_DIRS return GetSystemExtensionsDirectory(aFile); @@ -381,7 +434,7 @@ nsXREDirProvider::GetFile(const char* aProperty, bool* aPersistent, #endif } #endif -#if defined(XP_UNIX) +#if defined(XP_UNIX) && !defined(XP_MACOSX) else if (!strcmp(aProperty, XRE_SYS_SHARE_EXTENSION_PARENT_DIR)) { #ifdef ENABLE_SYSTEM_EXTENSION_DIRS #if defined(__OpenBSD__) || defined(__FreeBSD__) @@ -1050,7 +1103,42 @@ nsXREDirProvider::GetUpdateRootDir(nsIFile* *aResult) rv = appFile->GetParent(getter_AddRefs(updRoot)); NS_ENSURE_SUCCESS(rv, rv); -#if XP_WIN +#ifdef XP_MACOSX + nsCOMPtr<nsIFile> appRootDirFile; + nsCOMPtr<nsIFile> localDir; + nsAutoString appDirPath; + if (NS_FAILED(appFile->GetParent(getter_AddRefs(appRootDirFile))) || + NS_FAILED(appRootDirFile->GetPath(appDirPath)) || + NS_FAILED(GetUserDataDirectoryHome(getter_AddRefs(localDir), true))) { + return NS_ERROR_FAILURE; + } + + int32_t dotIndex = appDirPath.RFind(".app"); + if (dotIndex == kNotFound) { + dotIndex = appDirPath.Length(); + } + appDirPath = Substring(appDirPath, 1, dotIndex - 1); + + bool hasVendor = gAppData->vendor && strlen(gAppData->vendor) != 0; + if (hasVendor || gAppData->name) { + if (NS_FAILED(localDir->AppendNative(nsDependentCString(hasVendor ? + gAppData->vendor : + gAppData->name)))) { + return NS_ERROR_FAILURE; + } + } else if (NS_FAILED(localDir->AppendNative(NS_LITERAL_CSTRING("Mozilla")))) { + return NS_ERROR_FAILURE; + } + + if (NS_FAILED(localDir->Append(NS_LITERAL_STRING("updates"))) || + NS_FAILED(localDir->AppendRelativePath(appDirPath))) { + return NS_ERROR_FAILURE; + } + + localDir.forget(aResult); + return NS_OK; + +#elif XP_WIN nsAutoString pathHash; bool pathHashResult = false; bool hasVendor = gAppData->vendor && strlen(gAppData->vendor) != 0; @@ -1192,7 +1280,32 @@ nsXREDirProvider::GetUserDataDirectoryHome(nsIFile** aFile, bool aLocal) nsresult rv; nsCOMPtr<nsIFile> localDir; -#if defined(XP_WIN) +#if defined(XP_MACOSX) + FSRef fsRef; + OSType folderType; + if (aLocal) { + folderType = kCachedDataFolderType; + } else { +#ifdef MOZ_THUNDERBIRD + folderType = kDomainLibraryFolderType; +#else + folderType = kApplicationSupportFolderType; +#endif + } + OSErr err = ::FSFindFolder(kUserDomain, folderType, kCreateFolder, &fsRef); + NS_ENSURE_FALSE(err, NS_ERROR_FAILURE); + + rv = NS_NewNativeLocalFile(EmptyCString(), true, getter_AddRefs(localDir)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsILocalFileMac> dirFileMac = do_QueryInterface(localDir); + NS_ENSURE_TRUE(dirFileMac, NS_ERROR_UNEXPECTED); + + rv = dirFileMac->InitWithFSRef(&fsRef); + NS_ENSURE_SUCCESS(rv, rv); + + localDir = do_QueryInterface(dirFileMac, &rv); +#elif defined(XP_WIN) nsString path; if (aLocal) { rv = GetShellFolderPath(CSIDL_LOCAL_APPDATA, path); @@ -1255,7 +1368,7 @@ nsXREDirProvider::GetSysUserExtensionsDirectory(nsIFile** aFile) return NS_OK; } -#if defined(XP_UNIX) +#if defined(XP_UNIX) || defined(XP_MACOSX) nsresult nsXREDirProvider::GetSystemExtensionsDirectory(nsIFile** aFile) { @@ -1265,7 +1378,11 @@ nsXREDirProvider::GetSystemExtensionsDirectory(nsIFile** aFile) rv = GetSystemParentDirectory(getter_AddRefs(localDir)); if (NS_SUCCEEDED(rv)) { NS_NAMED_LITERAL_CSTRING(sExtensions, +#if defined(XP_MACOSX) + "Extensions" +#else "extensions" +#endif ); rv = localDir->AppendNative(sExtensions); @@ -1332,7 +1449,7 @@ nsXREDirProvider::AppendSysUserExtensionPath(nsIFile* aFile) nsresult rv; -#if defined(XP_WIN) +#if defined (XP_MACOSX) || defined(XP_WIN) static const char* const sXR = "Mozilla"; rv = aFile->AppendNative(nsDependentCString(sXR)); @@ -1391,7 +1508,19 @@ nsXREDirProvider::AppendProfilePath(nsIFile* aFile, nsresult rv; -#if defined(XP_WIN) +#if defined (XP_MACOSX) + if (!profile.IsEmpty()) { + rv = AppendProfileString(aFile, profile.get()); + } + else { + // Note that MacOS ignores the vendor when creating the profile hierarchy - + // all application preferences directories live alongside one another in + // ~/Library/Application Support/ + rv = aFile->AppendNative(appName); + } + NS_ENSURE_SUCCESS(rv, rv); + +#elif defined(XP_WIN) if (!profile.IsEmpty()) { rv = AppendProfileString(aFile, profile.get()); } diff --git a/toolkit/xre/nsXREDirProvider.h b/toolkit/xre/nsXREDirProvider.h index 014317e641..655f664e6d 100644 --- a/toolkit/xre/nsXREDirProvider.h +++ b/toolkit/xre/nsXREDirProvider.h @@ -102,7 +102,7 @@ protected: nsresult GetFilesInternal(const char* aProperty, nsISimpleEnumerator** aResult); static nsresult GetUserDataDirectoryHome(nsIFile* *aFile, bool aLocal); static nsresult GetSysUserExtensionsDirectory(nsIFile* *aFile); -#if defined(XP_UNIX) +#if defined(XP_UNIX) || defined(XP_MACOSX) static nsresult GetSystemExtensionsDirectory(nsIFile** aFile); #endif static nsresult EnsureDirectoryExists(nsIFile* aDirectory); |