commit 770a2829ba7a84bc0ed910b6ce503d152221f684 Author: Sukhbir Singh sukhbir@torproject.org Date: Mon Jul 18 12:36:28 2016 -0400
Update patch for #9173 and add patch for #4234 (Firefox update) --- ...ault-Firefox-profile-director-bug-9173.mozpatch | 451 ++++ .../Firefox-update-process-bug-4234.mozpatch | 2156 ++++++++++++++++++++ 2 files changed, 2607 insertions(+)
diff --git a/projects/instantbird/Change-the-default-Firefox-profile-director-bug-9173.mozpatch b/projects/instantbird/Change-the-default-Firefox-profile-director-bug-9173.mozpatch new file mode 100644 index 0000000..4447c70 --- /dev/null +++ b/projects/instantbird/Change-the-default-Firefox-profile-director-bug-9173.mozpatch @@ -0,0 +1,451 @@ +From 48068e88b66ba37c725850331d099f10f9d34c90 Mon Sep 17 00:00:00 2001 +From: Kathy Brade brade@pearlcrescent.com +Date: Fri, 18 Oct 2013 15:20:06 -0400 +Subject: Bug #9173: Change the default Firefox profile directory to be + TBB-relative. + +This should eliminate our need to rely on a wrapper script that +sets $HOME and launches Firefox with -profile. + +diff --git a/toolkit/xre/nsXREDirProvider.cpp b/toolkit/xre/nsXREDirProvider.cpp +index a828d33..831a373 100644 +--- a/toolkit/xre/nsXREDirProvider.cpp ++++ b/toolkit/xre/nsXREDirProvider.cpp +@@ -32,6 +32,7 @@ + #include "nsArrayEnumerator.h" + #include "nsEnumeratorUtils.h" + #include "nsReadableUtils.h" ++#include "nsXPCOMPrivate.h" // for XPCOM_FILE_PATH_SEPARATOR + #include "mozilla/Services.h" + #include "mozilla/Omnijar.h" + #include "mozilla/Preferences.h" +@@ -200,9 +201,6 @@ nsXREDirProvider::GetUserProfilesRootDir(nsIFile** aResult, + aProfileName, aAppName, aVendorName); + + if (NS_SUCCEEDED(rv)) { +-#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. + nsresult tmp = EnsureDirectoryExists(file); + if (NS_FAILED(tmp)) { +@@ -225,9 +223,6 @@ nsXREDirProvider::GetUserProfilesLocalDir(nsIFile** aResult, + aProfileName, aAppName, aVendorName); + + if (NS_SUCCEEDED(rv)) { +-#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. + nsresult tmp = EnsureDirectoryExists(file); + if (NS_FAILED(tmp)) { +@@ -1245,90 +1240,45 @@ nsresult + nsXREDirProvider::GetUserDataDirectoryHome(nsIFile** aFile, bool aLocal) + { + // Copied from nsAppFileLocationProvider (more or less) +- nsresult rv; ++ NS_ENSURE_ARG_POINTER(aFile); + nsCOMPtr<nsIFile> localDir; + +-#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)); ++ nsresult rv = GetAppDir()->Clone(getter_AddRefs(localDir)); + NS_ENSURE_SUCCESS(rv, rv); + +- nsCOMPtr<nsILocalFileMac> dirFileMac = do_QueryInterface(localDir); +- NS_ENSURE_TRUE(dirFileMac, NS_ERROR_UNEXPECTED); ++ int levelsToRemove = 0; // In FF21+, appDir points to browser subdirectory. ++#if defined(XP_MACOSX) ++ levelsToRemove += 1; ++#endif ++ while (localDir && (levelsToRemove > 0)) { ++ // When crawling up the hierarchy, components named "." do not count. ++ nsAutoCString removedName; ++ rv = localDir->GetNativeLeafName(removedName); ++ NS_ENSURE_SUCCESS(rv, rv); ++ bool didRemove = !removedName.Equals("."); + +- rv = dirFileMac->InitWithFSRef(&fsRef); +- NS_ENSURE_SUCCESS(rv, rv); ++ // Remove a directory component. ++ nsCOMPtr<nsIFile> parentDir; ++ rv = localDir->GetParent(getter_AddRefs(parentDir)); ++ NS_ENSURE_SUCCESS(rv, rv); ++ localDir = parentDir; + +- localDir = do_QueryInterface(dirFileMac, &rv); +-#elif defined(XP_IOS) +- nsAutoCString userDir; +- if (GetUIKitDirectory(aLocal, userDir)) { +- rv = NS_NewNativeLocalFile(userDir, true, getter_AddRefs(localDir)); +- } else { +- rv = NS_ERROR_FAILURE; +- } +- NS_ENSURE_SUCCESS(rv, rv); +-#elif defined(XP_WIN) +- nsString path; +- if (aLocal) { +- rv = GetShellFolderPath(CSIDL_LOCAL_APPDATA, path); +- if (NS_FAILED(rv)) +- rv = GetRegWindowsAppDataFolder(aLocal, path); +- } +- if (!aLocal || NS_FAILED(rv)) { +- rv = GetShellFolderPath(CSIDL_APPDATA, path); +- if (NS_FAILED(rv)) { +- if (!aLocal) +- rv = GetRegWindowsAppDataFolder(aLocal, path); +- } ++ if (didRemove) ++ --levelsToRemove; + } +- NS_ENSURE_SUCCESS(rv, rv); + +- rv = NS_NewLocalFile(path, true, getter_AddRefs(localDir)); +-#elif defined(MOZ_WIDGET_GONK) +- rv = NS_NewNativeLocalFile(NS_LITERAL_CSTRING("/data/b2g"), true, +- getter_AddRefs(localDir)); +-#elif defined(XP_UNIX) +- const char* homeDir = getenv("HOME"); +- if (!homeDir || !*homeDir) ++ if (!localDir) + return NS_ERROR_FAILURE; + +-#ifdef ANDROID /* We want (ProfD == ProfLD) on Android. */ +- aLocal = false; +-#endif ++ rv = localDir->AppendRelativeNativePath(NS_LITERAL_CSTRING("TorBrowser" ++ XPCOM_FILE_PATH_SEPARATOR "Data" ++ XPCOM_FILE_PATH_SEPARATOR "Browser")); ++ NS_ENSURE_SUCCESS(rv, rv); + + if (aLocal) { +- // If $XDG_CACHE_HOME is defined use it, otherwise use $HOME/.cache. +- const char* cacheHome = getenv("XDG_CACHE_HOME"); +- if (cacheHome && *cacheHome) { +- rv = NS_NewNativeLocalFile(nsDependentCString(cacheHome), true, +- getter_AddRefs(localDir)); +- } else { +- rv = NS_NewNativeLocalFile(nsDependentCString(homeDir), true, +- getter_AddRefs(localDir)); +- if (NS_SUCCEEDED(rv)) +- rv = localDir->AppendNative(NS_LITERAL_CSTRING(".cache")); +- } +- } else { +- rv = NS_NewNativeLocalFile(nsDependentCString(homeDir), true, +- getter_AddRefs(localDir)); ++ rv = localDir->AppendNative(NS_LITERAL_CSTRING("Caches")); ++ NS_ENSURE_SUCCESS(rv, rv); + } +-#else +-#error "Don't know how to get product dir on your platform" +-#endif + + NS_IF_ADDREF(*aFile = localDir); + return rv; +@@ -1541,48 +1491,25 @@ nsXREDirProvider::AppendProfilePath(nsIFile* aFile, + } + + nsAutoCString profile; +- nsAutoCString appName; +- nsAutoCString vendor; + if (aProfileName && !aProfileName->IsEmpty()) { + profile = *aProfileName; +- } else if (aAppName) { +- appName = *aAppName; +- if (aVendorName) { +- vendor = *aVendorName; +- } + } else if (gAppData->profile) { + profile = gAppData->profile; +- } else { +- appName = gAppData->name; +- vendor = gAppData->vendor; + } + +- nsresult rv; ++ nsresult rv = NS_ERROR_FAILURE; + + #if defined (XP_MACOSX) + if (!profile.IsEmpty()) { + rv = AppendProfileString(aFile, profile.get()); ++ NS_ENSURE_SUCCESS(rv, rv); + } +- 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()); ++ NS_ENSURE_SUCCESS(rv, rv); + } +- else { +- if (!vendor.IsEmpty()) { +- rv = aFile->AppendNative(vendor); +- NS_ENSURE_SUCCESS(rv, rv); +- } +- rv = aFile->AppendNative(appName); +- } +- NS_ENSURE_SUCCESS(rv, rv); + + #elif defined(ANDROID) + // The directory used for storing profiles +@@ -1594,12 +1521,6 @@ nsXREDirProvider::AppendProfilePath(nsIFile* aFile, + rv = aFile->AppendNative(nsDependentCString("mozilla")); + NS_ENSURE_SUCCESS(rv, rv); + #elif defined(XP_UNIX) +- nsAutoCString folder; +- // Make it hidden (by starting with "."), except when local (the +- // profile is already under ~/.cache or XDG_CACHE_HOME). +- if (!aLocal) +- folder.Assign('.'); +- + if (!profile.IsEmpty()) { + // Skip any leading path characters + const char* profileStart = profile.get(); +@@ -1608,31 +1529,17 @@ nsXREDirProvider::AppendProfilePath(nsIFile* aFile, + + // On the off chance that someone wanted their folder to be hidden don't + // let it become ".." +- if (*profileStart == '.' && !aLocal) ++ if (*profileStart == '.') + profileStart++; + ++ // Make it hidden (by starting with "."). ++ nsAutoCString folder("."); + folder.Append(profileStart); + ToLowerCase(folder); + + rv = AppendProfileString(aFile, folder.BeginReading()); ++ NS_ENSURE_SUCCESS(rv, rv); + } +- else { +- if (!vendor.IsEmpty()) { +- folder.Append(vendor); +- ToLowerCase(folder); +- +- rv = aFile->AppendNative(folder); +- NS_ENSURE_SUCCESS(rv, rv); +- +- folder.Truncate(); +- } +- +- folder.Append(appName); +- ToLowerCase(folder); +- +- rv = aFile->AppendNative(folder); +- } +- NS_ENSURE_SUCCESS(rv, rv); + + #else + #error "Don't know how to get profile path on your platform" +diff --git a/toolkit/xre/nsXREDirProvider.h b/toolkit/xre/nsXREDirProvider.h +index eb27ed2..1985f66 100644 +--- a/toolkit/xre/nsXREDirProvider.h ++++ b/toolkit/xre/nsXREDirProvider.h +@@ -56,16 +56,16 @@ public: + + nsresult GetProfileDefaultsDir(nsIFile* *aResult); + +- static nsresult GetUserAppDataDirectory(nsIFile* *aFile) { ++ nsresult GetUserAppDataDirectory(nsIFile* *aFile) { + return GetUserDataDirectory(aFile, false, nullptr, nullptr, nullptr); + } +- static nsresult GetUserLocalDataDirectory(nsIFile* *aFile) { ++ nsresult GetUserLocalDataDirectory(nsIFile* *aFile) { + return GetUserDataDirectory(aFile, true, nullptr, nullptr, nullptr); + } + + // By default GetUserDataDirectory gets profile path from gAppData, + // but that can be overridden by using aProfileName/aAppName/aVendorName. +- static nsresult GetUserDataDirectory(nsIFile** aFile, bool aLocal, ++ nsresult GetUserDataDirectory(nsIFile** aFile, bool aLocal, + const nsACString* aProfileName, + const nsACString* aAppName, + const nsACString* aVendorName); +@@ -102,8 +102,8 @@ public: + + protected: + nsresult GetFilesInternal(const char* aProperty, nsISimpleEnumerator** aResult); +- static nsresult GetUserDataDirectoryHome(nsIFile* *aFile, bool aLocal); +- static nsresult GetSysUserExtensionsDirectory(nsIFile* *aFile); ++ nsresult GetUserDataDirectoryHome(nsIFile* *aFile, bool aLocal); ++ nsresult GetSysUserExtensionsDirectory(nsIFile* *aFile); + #if defined(XP_UNIX) || defined(XP_MACOSX) + static nsresult GetSystemExtensionsDirectory(nsIFile** aFile); + #endif +diff --git a/xpcom/io/moz.build b/xpcom/io/moz.build +index 45732e4..bc62f71 100644 +--- a/xpcom/io/moz.build ++++ b/xpcom/io/moz.build +@@ -134,4 +134,7 @@ FINAL_LIBRARY = 'xul' + if CONFIG['OS_ARCH'] == 'Linux' and 'lib64' in CONFIG['libdir']: + DEFINES['HAVE_USR_LIB64_DIR'] = True + +-LOCAL_INCLUDES += ['!..'] ++LOCAL_INCLUDES += [ ++ '!..', ++ '../build', ++] +diff --git a/xpcom/io/nsAppFileLocationProvider.cpp b/xpcom/io/nsAppFileLocationProvider.cpp +index 1211c39..039a89e 100644 +--- a/xpcom/io/nsAppFileLocationProvider.cpp ++++ b/xpcom/io/nsAppFileLocationProvider.cpp +@@ -15,6 +15,7 @@ + #include "nsISimpleEnumerator.h" + #include "prenv.h" + #include "nsCRT.h" ++#include "nsXPCOMPrivate.h" // for XPCOM_FILE_PATH_SEPARATOR + + #if defined(MOZ_WIDGET_COCOA) + #include <Carbon/Carbon.h> +@@ -281,9 +282,8 @@ nsAppFileLocationProvider::CloneMozBinDirectory(nsIFile** aLocalFile) + //---------------------------------------------------------------------------------------- + // GetProductDirectory - Gets the directory which contains the application data folder + // +-// UNIX : ~/.mozilla/ +-// WIN : <Application Data folder on user's machine>\Mozilla +-// Mac : :Documents:Mozilla: ++// UNIX and WIN : <App Folder>/TorBrowser/Data/Browser ++// Mac : <App Folder>/../../TorBrowser/Data/Browser + //---------------------------------------------------------------------------------------- + NS_METHOD + nsAppFileLocationProvider::GetProductDirectory(nsIFile** aLocalFile, +@@ -297,48 +297,45 @@ nsAppFileLocationProvider::GetProductDirectory(nsIFile** aLocalFile, + bool exists; + nsCOMPtr<nsIFile> localDir; + +-#if defined(MOZ_WIDGET_COCOA) +- FSRef fsRef; +- OSType folderType = aLocal ? (OSType)kCachedDataFolderType : +- (OSType)kDomainLibraryFolderType; +- OSErr err = ::FSFindFolder(kUserDomain, folderType, kCreateFolder, &fsRef); +- if (err) { +- return NS_ERROR_FAILURE; ++ rv = CloneMozBinDirectory(getter_AddRefs(localDir)); ++ NS_ENSURE_SUCCESS(rv, rv); ++ ++ int levelsToRemove = 0; // In FF21+, bin dir points to browser subdirectory. ++#if defined(XP_MACOSX) ++ levelsToRemove += 1; ++#endif ++ while (localDir && (levelsToRemove > 0)) { ++ // When crawling up the hierarchy, components named "." do not count. ++ nsAutoCString removedName; ++ rv = localDir->GetNativeLeafName(removedName); ++ NS_ENSURE_SUCCESS(rv, rv); ++ bool didRemove = !removedName.Equals("."); ++ ++ // Remove a directory component. ++ nsCOMPtr<nsIFile> parentDir; ++ rv = localDir->GetParent(getter_AddRefs(parentDir)); ++ NS_ENSURE_SUCCESS(rv, rv); ++ localDir = parentDir; ++ ++ if (didRemove) { ++ --levelsToRemove; ++ } + } +- NS_NewLocalFile(EmptyString(), true, getter_AddRefs(localDir)); ++ + if (!localDir) { + return NS_ERROR_FAILURE; + } +- nsCOMPtr<nsILocalFileMac> localDirMac(do_QueryInterface(localDir)); +- rv = localDirMac->InitWithFSRef(&fsRef); +- if (NS_FAILED(rv)) { +- return rv; +- } +-#elif defined(XP_WIN) +- nsCOMPtr<nsIProperties> directoryService = +- do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv); +- if (NS_FAILED(rv)) { +- return rv; +- } +- const char* prop = aLocal ? NS_WIN_LOCAL_APPDATA_DIR : NS_WIN_APPDATA_DIR; +- rv = directoryService->Get(prop, NS_GET_IID(nsIFile), getter_AddRefs(localDir)); +- if (NS_FAILED(rv)) { +- return rv; +- } +-#elif defined(XP_UNIX) +- rv = NS_NewNativeLocalFile(nsDependentCString(PR_GetEnv("HOME")), true, +- getter_AddRefs(localDir)); +- if (NS_FAILED(rv)) { +- return rv; +- } +-#else +-#error dont_know_how_to_get_product_dir_on_your_platform +-#endif + +- rv = localDir->AppendRelativeNativePath(DEFAULT_PRODUCT_DIR); +- if (NS_FAILED(rv)) { +- return rv; ++ rv = localDir->AppendRelativeNativePath(NS_LITERAL_CSTRING("TorBrowser" ++ XPCOM_FILE_PATH_SEPARATOR "Data" ++ XPCOM_FILE_PATH_SEPARATOR "Browser")); ++ NS_ENSURE_SUCCESS(rv, rv); ++ ++ if (aLocal) { ++ rv = localDir->AppendNative(NS_LITERAL_CSTRING("Caches")); ++ NS_ENSURE_SUCCESS(rv, rv); + } ++ + rv = localDir->Exists(&exists); + + if (NS_SUCCEEDED(rv) && !exists) { +@@ -357,10 +354,6 @@ nsAppFileLocationProvider::GetProductDirectory(nsIFile** aLocalFile, + + //---------------------------------------------------------------------------------------- + // GetDefaultUserProfileRoot - Gets the directory which contains each user profile dir +-// +-// UNIX : ~/.mozilla/ +-// WIN : <Application Data folder on user's machine>\Mozilla\Profiles +-// Mac : :Documents:Mozilla:Profiles: + //---------------------------------------------------------------------------------------- + NS_METHOD + nsAppFileLocationProvider::GetDefaultUserProfileRoot(nsIFile** aLocalFile, +@@ -378,23 +371,6 @@ nsAppFileLocationProvider::GetDefaultUserProfileRoot(nsIFile** aLocalFile, + return rv; + } + +-#if defined(MOZ_WIDGET_COCOA) || defined(XP_WIN) +- // These 3 platforms share this part of the path - do them as one +- rv = localDir->AppendRelativeNativePath(NS_LITERAL_CSTRING("Profiles")); +- if (NS_FAILED(rv)) { +- return rv; +- } +- +- bool exists; +- rv = localDir->Exists(&exists); +- if (NS_SUCCEEDED(rv) && !exists) { +- rv = localDir->Create(nsIFile::DIRECTORY_TYPE, 0775); +- } +- if (NS_FAILED(rv)) { +- return rv; +- } +-#endif +- + localDir.forget(aLocalFile); + + return rv; +-- +cgit v0.10.2 + diff --git a/projects/instantbird/Firefox-update-process-bug-4234.mozpatch b/projects/instantbird/Firefox-update-process-bug-4234.mozpatch new file mode 100644 index 0000000..a02d80b --- /dev/null +++ b/projects/instantbird/Firefox-update-process-bug-4234.mozpatch @@ -0,0 +1,2156 @@ +From 3f8110f69e1ddfc762a7c7dba4d06b537a785a9c Mon Sep 17 00:00:00 2001 +From: Kathy Brade brade@pearlcrescent.com +Date: Thu, 4 Sep 2014 15:45:23 +0000 +Subject: Bug #4234: Use the Firefox Update Process for Tor Browser. + +New configure options: + --with-tor-browser-version=VERSION # Pass TB version throughout build. + --enable-tor-browser-update # Enable bundle update behavior. +The following files are never updated: + TorBrowser/Data/Browser/profiles.ini + TorBrowser/Data/Browser/profile.default/bookmarks.html + TorBrowser/Data/Tor/torrc +Mac OS: Store update metadata under TorBrowser/UpdateInfo. +Removed the %OS_VERSION% component from the update URL (13047) and + added support for minSupportedOSVersion, an attribute of the + <update> element that may be used to trigger Firefox's + "unsupported platform" behavior. +Windows: disable "runas" code path in updater (15201). +Windows: avoid writing to the registry (16236). +Also includes fixes for tickets 13047, 13301, 13356, 13594, 15406, + 16014, and 16909. + +diff --git a/browser/components/moz.build b/browser/components/moz.build +index 431d505..bf7a40c 100644 +--- a/browser/components/moz.build ++++ b/browser/components/moz.build +@@ -38,11 +38,8 @@ XPIDL_MODULE = 'browsercompsbase' + + EXTRA_PP_COMPONENTS += [ + 'BrowserComponents.manifest', +- 'nsBrowserGlue.js', +-] +- +-EXTRA_COMPONENTS += [ + 'nsBrowserContentHandler.js', ++ 'nsBrowserGlue.js', + ] + + EXTRA_JS_MODULES += [ +diff --git a/browser/components/nsBrowserContentHandler.js b/browser/components/nsBrowserContentHandler.js +index e89004d..b892eaf 100644 +--- a/browser/components/nsBrowserContentHandler.js ++++ b/browser/components/nsBrowserContentHandler.js +@@ -45,6 +45,10 @@ const NS_BINDING_ABORTED = Components.results.NS_BINDING_ABORTED; + const NS_ERROR_WONT_HANDLE_CONTENT = 0x805d0001; + const NS_ERROR_ABORT = Components.results.NS_ERROR_ABORT; + ++#ifdef TOR_BROWSER_VERSION ++const kTBSavedVersionPref = "browser.startup.homepage_override.torbrowser.version"; ++#endif ++ + function shouldLoadURI(aURI) { + if (aURI && !aURI.schemeIs("chrome")) + return true; +@@ -96,7 +100,8 @@ const OVERRIDE_NEW_BUILD_ID = 3; + * Returns: + * OVERRIDE_NEW_PROFILE if this is the first run with a new profile. + * OVERRIDE_NEW_MSTONE if this is the first run with a build with a different +- * Gecko milestone (i.e. right after an upgrade). ++ * Gecko milestone or Tor Browser version (i.e. right ++ * after an upgrade). + * OVERRIDE_NEW_BUILD_ID if this is the first run with a new build ID of the + * same Gecko milestone (i.e. after a nightly upgrade). + * OVERRIDE_NONE otherwise. +@@ -112,6 +117,15 @@ function needHomepageOverride(prefb) { + + var mstone = Services.appinfo.platformVersion; + ++#ifdef TOR_BROWSER_VERSION ++#expand const TOR_BROWSER_VERSION = __TOR_BROWSER_VERSION__; ++ ++ var savedTBVersion = null; ++ try { ++ savedTBVersion = prefb.getCharPref(kTBSavedVersionPref); ++ } catch (e) {} ++#endif ++ + var savedBuildID = null; + try { + savedBuildID = prefb.getCharPref("browser.startup.homepage_override.buildID"); +@@ -129,9 +143,30 @@ function needHomepageOverride(prefb) { + + prefb.setCharPref("browser.startup.homepage_override.mstone", mstone); + prefb.setCharPref("browser.startup.homepage_override.buildID", buildID); ++#ifdef TOR_BROWSER_VERSION ++ prefb.setCharPref(kTBSavedVersionPref, TOR_BROWSER_VERSION); ++ ++ // After an upgrade from an older release of Tor Browser (<= 5.5a1), the ++ // savedmstone will be undefined because those releases included the ++ // value "ignore" for the browser.startup.homepage_override.mstone pref. ++ // To correctly detect an upgrade vs. a new profile, we check for the ++ // presence of the "app.update.postupdate" pref. ++ var updated = prefb.prefHasUserValue("app.update.postupdate"); ++ return (savedmstone || updated) ? OVERRIDE_NEW_MSTONE ++ : OVERRIDE_NEW_PROFILE; ++#else + return (savedmstone ? OVERRIDE_NEW_MSTONE : OVERRIDE_NEW_PROFILE); ++#endif + } + ++#ifdef TOR_BROWSER_VERSION ++ if (TOR_BROWSER_VERSION != savedTBVersion) { ++ prefb.setCharPref("browser.startup.homepage_override.buildID", buildID); ++ prefb.setCharPref(kTBSavedVersionPref, TOR_BROWSER_VERSION); ++ return OVERRIDE_NEW_MSTONE; ++ } ++#endif ++ + if (buildID != savedBuildID) { + prefb.setCharPref("browser.startup.homepage_override.buildID", buildID); + return OVERRIDE_NEW_BUILD_ID; +@@ -515,6 +550,15 @@ nsBrowserContentHandler.prototype = { + try { + old_mstone = Services.prefs.getCharPref("browser.startup.homepage_override.mstone"); + } catch (ex) {} ++ ++#ifdef TOR_BROWSER_VERSION ++ // We do the same for the Tor Browser version. ++ var old_tbversion = null; ++ try { ++ old_tbversion = prefb.getCharPref(kTBSavedVersionPref); ++ } catch (e) {} ++#endif ++ + override = needHomepageOverride(prefb); + if (override != OVERRIDE_NONE) { + switch (override) { +@@ -538,6 +582,10 @@ nsBrowserContentHandler.prototype = { + overridePage = getPostUpdateOverridePage(overridePage); + + overridePage = overridePage.replace("%OLD_VERSION%", old_mstone); ++#ifdef TOR_BROWSER_VERSION ++ overridePage = overridePage.replace("%OLD_TOR_BROWSER_VERSION%", ++ old_tbversion); ++#endif + break; + } + } +diff --git a/browser/confvars.sh b/browser/confvars.sh +index 6935e05..ed93deb 100755 +--- a/browser/confvars.sh ++++ b/browser/confvars.sh +@@ -8,22 +8,6 @@ MOZ_APP_VENDOR=Mozilla + MOZ_UPDATER=1 + MOZ_PHOENIX=1 + +-if test "$OS_ARCH" = "WINNT"; then +- MOZ_MAINTENANCE_SERVICE=1 +- if ! test "$HAVE_64BIT_BUILD"; then +- if test "$MOZ_UPDATE_CHANNEL" = "nightly" -o \ +- "$MOZ_UPDATE_CHANNEL" = "aurora" -o \ +- "$MOZ_UPDATE_CHANNEL" = "beta" -o \ +- "$MOZ_UPDATE_CHANNEL" = "beta-dev" -o \ +- "$MOZ_UPDATE_CHANNEL" = "release" -o \ +- "$MOZ_UPDATE_CHANNEL" = "release-dev"; then +- if ! test "$MOZ_DEBUG"; then +- MOZ_STUB_INSTALLER=1 +- fi +- fi +- fi +-fi +- + # Enable building ./signmar and running libmar signature tests + MOZ_ENABLE_SIGNMAR=1 + +@@ -51,9 +35,9 @@ MOZ_APP_ID={ec8030f7-c20a-464f-9b0e-13a3a9e97384} + # This should usually be the same as the value MAR_CHANNEL_ID. + # If more than one ID is needed, then you should use a comma separated list + # of values. +-ACCEPTED_MAR_CHANNEL_IDS=firefox-mozilla-esr ++ACCEPTED_MAR_CHANNEL_IDS=torbrowser-torproject-release + # The MAR_CHANNEL_ID must not contain the following 3 characters: ",\t " +-MAR_CHANNEL_ID=firefox-mozilla-esr ++MAR_CHANNEL_ID=torbrowser-torproject-release + MOZ_PROFILE_MIGRATOR=1 + MOZ_APP_STATIC_INI=1 + MOZ_WEBAPP_RUNTIME=1 +diff --git a/browser/installer/Makefile.in b/browser/installer/Makefile.in +index bb91b4a..ecab9ad 100644 +--- a/browser/installer/Makefile.in ++++ b/browser/installer/Makefile.in +@@ -72,6 +72,10 @@ endif + endif + endif + ++ifdef TOR_BROWSER_UPDATE ++DEFINES += -DTOR_BROWSER_UPDATE ++endif ++ + ifneq (,$(filter WINNT Darwin Android,$(OS_TARGET))) + DEFINES += -DMOZ_SHARED_MOZGLUE=1 + endif +diff --git a/config/createprecomplete.py b/config/createprecomplete.py +index 3241d52..be571be 100644 +--- a/config/createprecomplete.py ++++ b/config/createprecomplete.py +@@ -5,13 +5,22 @@ + # update instructions which is used to remove files and directories that are no + # longer present in a complete update. The current working directory is used for + # the location to enumerate and to create the precomplete file. ++# For symlinks, remove instructions are always generated. + + import sys + import os + ++# TODO When TOR_BROWSER_DATA_OUTSIDE_APP_DIR is used on all platforms, ++# we should remove all lines in this file that contain: ++# TorBrowser/Data ++ + def get_build_entries(root_path): + """ Iterates through the root_path, creating a list for each file and + directory. Excludes any file paths ending with channel-prefs.js. ++ To support Tor Browser updates, excludes: ++ TorBrowser/Data/Browser/profiles.ini ++ TorBrowser/Data/Browser/profile.default/bookmarks.html ++ TorBrowser/Data/Tor/torrc + """ + rel_file_path_set = set() + rel_dir_path_set = set() +@@ -22,6 +31,9 @@ def get_build_entries(root_path): + rel_path_file = rel_path_file.replace("\", "/") + if not (rel_path_file.endswith("channel-prefs.js") or + rel_path_file.endswith("update-settings.ini") or ++ rel_path_file == "TorBrowser/Data/Browser/profiles.ini" or ++ rel_path_file == "TorBrowser/Data/Browser/profile.default/bookmarks.html" or ++ rel_path_file == "TorBrowser/Data/Tor/torrc" or + rel_path_file.find("distribution/") != -1): + rel_file_path_set.add(rel_path_file) + +@@ -30,7 +42,10 @@ def get_build_entries(root_path): + rel_path_dir = os.path.join(parent_dir_rel_path, dir_name) + rel_path_dir = rel_path_dir.replace("\", "/")+"/" + if rel_path_dir.find("distribution/") == -1: +- rel_dir_path_set.add(rel_path_dir) ++ if (os.path.islink(rel_path_dir[:-1])): ++ rel_file_path_set.add(rel_path_dir[:-1]) ++ else: ++ rel_dir_path_set.add(rel_path_dir) + + rel_file_path_list = list(rel_file_path_set) + rel_file_path_list.sort(reverse=True) +diff --git a/configure.in b/configure.in +index 38df30e..e9fb038 100644 +--- a/configure.in ++++ b/configure.in +@@ -3369,15 +3369,12 @@ AC_SUBST(GRE_MILESTONE) + # set RELEASE_BUILD and NIGHTLY_BUILD variables depending on the cycle we're in + # The logic works like this: + # - if we have "a1" in GRE_MILESTONE, we're building Nightly (define NIGHTLY_BUILD) +-# - otherwise, if we have "a" in GRE_MILESTONE, we're building Nightly or Aurora + # - otherwise, we're building Release/Beta (define RELEASE_BUILD) + case "$GRE_MILESTONE" in + *a1*) + NIGHTLY_BUILD=1 + AC_DEFINE(NIGHTLY_BUILD) + ;; +- *a*) +- ;; + *) + RELEASE_BUILD=1 + AC_DEFINE(RELEASE_BUILD) +@@ -6521,6 +6518,31 @@ MOZ_ARG_ENABLE_BOOL(update-packaging, + AC_SUBST(MOZ_UPDATE_PACKAGING) + + dnl ======================================================== ++dnl Tor Additions ++dnl ======================================================== ++MOZ_ARG_WITH_STRING(eor-browser-version, ++[ --with-tor-browser-version=VERSION ++ Set Tor Browser version, e.g., 4.0b1], ++ TOR_BROWSER_VERSION="$withval") ++ ++MOZ_ARG_ENABLE_BOOL(tor-browser-update, ++[ --enable-tor-browser-update ++ Enable Tor Browser update], ++ TOR_BROWSER_UPDATE=1, ++ TOR_BROWSER_UPDATE= ) ++ ++if test -n "$TOR_BROWSER_UPDATE"; then ++ if test -z "$TOR_BROWSER_VERSION"; then ++ AC_MSG_ERROR([--enable-tor-browser-update requires --with-tor-browser-version.]) ++ fi ++ AC_DEFINE(TOR_BROWSER_UPDATE) ++fi ++ ++AC_DEFINE_UNQUOTED(TOR_BROWSER_VERSION,"$TOR_BROWSER_VERSION") ++AC_SUBST(TOR_BROWSER_VERSION) ++AC_SUBST(TOR_BROWSER_UPDATE) ++ ++dnl ======================================================== + dnl build the tests by default + dnl ======================================================== + MOZ_ARG_DISABLE_BOOL(tests, +@@ -9068,7 +9090,7 @@ if test "$MOZ_DEBUG"; then + A11Y_LOG=1 + fi + case "$MOZ_UPDATE_CHANNEL" in +-aurora|beta|release|esr) ++aurora|alpha|beta|hardened|release|esr) + ;; + *) + A11Y_LOG=1 +diff --git a/js/src/configure.in b/js/src/configure.in +index 25b4ae8..7fae8a4 100644 +--- a/js/src/configure.in ++++ b/js/src/configure.in +@@ -2691,15 +2691,12 @@ AC_SUBST(GRE_MILESTONE) + dnl set RELEASE_BUILD and NIGHTLY_BUILD variables depending on the cycle we're in + dnl The logic works like this: + dnl - if we have "a1" in GRE_MILESTONE, we're building Nightly (define NIGHTLY_BUILD) +-dnl - otherwise, if we have "a" in GRE_MILESTONE, we're building Nightly or Aurora + dnl - otherwise, we're building Release/Beta (define RELEASE_BUILD) + case "$GRE_MILESTONE" in + *a1*) + NIGHTLY_BUILD=1 + AC_DEFINE(NIGHTLY_BUILD) + ;; +- *a*) +- ;; + *) + RELEASE_BUILD=1 + AC_DEFINE(RELEASE_BUILD) +diff --git a/toolkit/modules/UpdateUtils.jsm b/toolkit/modules/UpdateUtils.jsm +index b943467..4638c8e 100644 +--- a/toolkit/modules/UpdateUtils.jsm ++++ b/toolkit/modules/UpdateUtils.jsm +@@ -20,6 +20,9 @@ const PREF_APP_B2G_VERSION = "b2g.version"; + const PREF_APP_UPDATE_CUSTOM = "app.update.custom"; + const PREF_APP_UPDATE_IMEI_HASH = "app.update.imei_hash"; + ++#ifdef TOR_BROWSER_VERSION ++#expand const TOR_BROWSER_VERSION = __TOR_BROWSER_VERSION__; ++#endif + + this.UpdateUtils = { + /** +@@ -70,7 +73,11 @@ this.UpdateUtils = { + */ + formatUpdateURL(url) { + url = url.replace(/%PRODUCT%/g, Services.appinfo.name); ++#ifdef TOR_BROWSER_UPDATE ++ url = url.replace(/%VERSION%/g, TOR_BROWSER_VERSION); ++#else + url = url.replace(/%VERSION%/g, Services.appinfo.version); ++#endif + url = url.replace(/%BUILD_ID%/g, Services.appinfo.appBuildID); + url = url.replace(/%BUILD_TARGET%/g, Services.appinfo.OS + "_" + this.ABI); + url = url.replace(/%OS_VERSION%/g, this.OSVersion); +diff --git a/toolkit/modules/debug.js b/toolkit/modules/debug.js +index ba59c65..486105e 100644 +--- a/toolkit/modules/debug.js ++++ b/toolkit/modules/debug.js +@@ -41,6 +41,8 @@ this.NS_ASSERT = function NS_ASSERT(condition, message) { + switch (defB.getCharPref("app.update.channel")) { + case "nightly": + case "aurora": ++ case "alpha": ++ case "hardened": + case "beta": + case "default": + releaseBuild = false; +diff --git a/toolkit/modules/moz.build b/toolkit/modules/moz.build +index 0600b82..81125d1 100644 +--- a/toolkit/modules/moz.build ++++ b/toolkit/modules/moz.build +@@ -72,7 +72,6 @@ EXTRA_JS_MODULES += [ + 'Task.jsm', + 'Timer.jsm', + 'Troubleshoot.jsm', +- 'UpdateUtils.jsm', + 'WebChannel.jsm', + 'WindowDraggingUtils.jsm', + 'ZipUtils.jsm', +@@ -87,6 +86,7 @@ if not CONFIG['TOR_BROWSER_VERSION']: + EXTRA_PP_JS_MODULES += [ + 'AppConstants.jsm', + 'SessionRecorder.jsm', ++ 'UpdateUtils.jsm', + ] + + if 'Android' != CONFIG['OS_TARGET']: +diff --git a/toolkit/mozapps/extensions/AddonManager.jsm b/toolkit/mozapps/extensions/AddonManager.jsm +index ab63c3d..a453062 100644 +--- a/toolkit/mozapps/extensions/AddonManager.jsm ++++ b/toolkit/mozapps/extensions/AddonManager.jsm +@@ -23,7 +23,7 @@ if ("@mozilla.org/xre/app-info;1" in Cc) { + + Cu.import("resource://gre/modules/AppConstants.jsm"); + +-const MOZ_COMPATIBILITY_NIGHTLY = !['aurora', 'beta', 'release', 'esr'].includes(AppConstants.MOZ_UPDATE_CHANNEL); ++const MOZ_COMPATIBILITY_NIGHTLY = !['aurora', 'alpha', 'beta', 'hardened', 'release', 'esr'].includes(AppConstants.MOZ_UPDATE_CHANNEL); + + const PREF_BLOCKLIST_PINGCOUNTVERSION = "extensions.blocklist.pingCountVersion"; + const PREF_DEFAULT_PROVIDERS_ENABLED = "extensions.defaultProviders.enabled"; +diff --git a/toolkit/mozapps/update/content/updates.js b/toolkit/mozapps/update/content/updates.js +index 48f8bad..40e76c8 100644 +--- a/toolkit/mozapps/update/content/updates.js ++++ b/toolkit/mozapps/update/content/updates.js +@@ -3,6 +3,8 @@ + * 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/. */ + ++#filter substitution ++ + 'use strict'; + + // Firefox's macBrowserOverlay.xul includes scripts that define Cc, Ci, and Cr +@@ -51,6 +53,11 @@ const CERT_ATTR_CHECK_FAILED_NO_UPDATE = 100; + const CERT_ATTR_CHECK_FAILED_HAS_UPDATE = 101; + const BACKGROUNDCHECK_MULTIPLE_FAILURES = 110; + ++#ifdef TOR_BROWSER_VERSION ++# Add double-quotes back on (stripped by JarMaker.py). ++#expand const TOR_BROWSER_VERSION = "__TOR_BROWSER_VERSION__"; ++#endif ++ + var gLogEnabled = false; + var gUpdatesFoundPageId; + +@@ -510,8 +517,13 @@ var gUpdates = { + return; + } + ++#ifdef TOR_BROWSER_UPDATE ++ var appVersion = TOR_BROWSER_VERSION; ++#else ++ var appVersion = Services.appinfo.version; ++#endif + if (!this.update.appVersion || +- Services.vc.compare(this.update.appVersion, Services.appinfo.version) == 0) { ++ Services.vc.compare(this.update.appVersion, appVersion) == 0) { + aCallback(false); + return; + } +@@ -523,6 +535,11 @@ var gUpdates = { + + var self = this; + AddonManager.getAllAddons(function(addons) { ++#ifdef TOR_BROWSER_UPDATE ++ let compatVersion = self.update.platformVersion; ++#else ++ let compatVersion = self.update.appVersion; ++#endif + self.addons = []; + addons.forEach(function(addon) { + // Protect against code that overrides the add-ons manager and doesn't +@@ -551,7 +568,7 @@ var gUpdates = { + !addon.appDisabled && !addon.userDisabled && + addon.scope != AddonManager.SCOPE_APPLICATION && + addon.isCompatible && +- !addon.isCompatibleWith(self.update.appVersion, ++ !addon.isCompatibleWith(compatVersion, + self.update.platformVersion)) + self.addons.push(addon); + } +@@ -821,9 +838,14 @@ var gIncompatibleCheckPage = { + this._totalCount = gUpdates.addons.length; + + this._pBar.mode = "normal"; ++#ifdef TOR_BROWSER_UPDATE ++ let compatVersion = gUpdates.update.platformVersion; ++#else ++ let compatVersion = gUpdates.update.appVersion; ++#endif + gUpdates.addons.forEach(function(addon) { + addon.findUpdates(this, AddonManager.UPDATE_WHEN_NEW_APP_DETECTED, +- gUpdates.update.appVersion, ++ compatVersion, + gUpdates.update.platformVersion); + }, this); + }, +@@ -848,8 +870,13 @@ var gIncompatibleCheckPage = { + // the add-on will become incompatible. + let bs = CoC["@mozilla.org/extensions/blocklist;1"]. + getService(CoI.nsIBlocklistService); ++#ifdef TOR_BROWSER_UPDATE ++ let compatVersion = gUpdates.update.platformVersion; ++#else ++ let compatVersion = gUpdates.update.appVersion; ++#endif + if (bs.isAddonBlocklisted(addon, +- gUpdates.update.appVersion, ++ compatVersion, + gUpdates.update.platformVersion)) + return; + +diff --git a/toolkit/mozapps/update/jar.mn b/toolkit/mozapps/update/jar.mn +index 9bb5d2d..5347123 100644 +--- a/toolkit/mozapps/update/jar.mn ++++ b/toolkit/mozapps/update/jar.mn +@@ -7,6 +7,6 @@ toolkit.jar: + content/mozapps/update/history.xul (content/history.xul) + content/mozapps/update/history.js (content/history.js) + content/mozapps/update/updates.css (content/updates.css) +- content/mozapps/update/updates.js (content/updates.js) ++* content/mozapps/update/updates.js (content/updates.js) + content/mozapps/update/updates.xml (content/updates.xml) + content/mozapps/update/updates.xul (content/updates.xul) +diff --git a/toolkit/mozapps/update/moz.build b/toolkit/mozapps/update/moz.build +index 3824b88..c93100a 100644 +--- a/toolkit/mozapps/update/moz.build ++++ b/toolkit/mozapps/update/moz.build +@@ -18,11 +18,14 @@ XPIDL_SOURCES += [ + TEST_DIRS += ['tests'] + + EXTRA_COMPONENTS += [ +- 'nsUpdateService.js', + 'nsUpdateService.manifest', + 'nsUpdateServiceStub.js', + ] + ++EXTRA_PP_COMPONENTS += [ ++ 'nsUpdateService.js', ++] ++ + EXTRA_JS_MODULES += [ + 'UpdateTelemetry.jsm', + ] +diff --git a/toolkit/mozapps/update/nsUpdateService.js b/toolkit/mozapps/update/nsUpdateService.js +index 489d085..e12636d 100644 +--- a/toolkit/mozapps/update/nsUpdateService.js ++++ b/toolkit/mozapps/update/nsUpdateService.js +@@ -12,7 +12,9 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm", this); + Cu.import("resource://gre/modules/FileUtils.jsm", this); + Cu.import("resource://gre/modules/AddonManager.jsm", this); + Cu.import("resource://gre/modules/Services.jsm", this); ++#ifdef XP_WIN + Cu.import("resource://gre/modules/ctypes.jsm", this); ++#endif + Cu.import("resource://gre/modules/UpdateTelemetry.jsm", this); + Cu.import("resource://gre/modules/AppConstants.jsm", this); + +@@ -66,6 +68,10 @@ const KEY_EXECUTABLE = "XREExeF"; + // Gonk only + const KEY_UPDATE_ARCHIVE_DIR = "UpdArchD"; + ++#ifdef TOR_BROWSER_VERSION ++#expand const TOR_BROWSER_VERSION = __TOR_BROWSER_VERSION__; ++#endif ++ + const DIR_UPDATED = "updated"; + const DIR_UPDATED_APP = "Updated.app"; + const DIR_UPDATES = "updates"; +@@ -701,8 +707,13 @@ function getUpdatesDirInApplyToDir() { + if (AppConstants.platform == "macosx") { + dir = dir.parent.parent; // the bundle directory + dir.append(DIR_UPDATED_APP); ++#if defined(TOR_BROWSER_UPDATE) && !defined(TOR_BROWSER_DATA_OUTSIDE_APP_DIR) ++ dir.append("TorBrowser"); ++ dir.append("UpdateInfo"); ++#else + dir.append("Contents"); + dir.append("MacOS"); ++#endif + } else { + dir.append(DIR_UPDATED); + } +@@ -1521,7 +1532,17 @@ function Update(update) { + this._patches.push(patch); + } + +- if (this._patches.length == 0 && !update.hasAttribute("unsupported")) { ++ if (update.hasAttribute("unsupported")) { ++ this.unsupported = ("true" == update.getAttribute("unsupported")); ++ } else if (update.hasAttribute("minSupportedOSVersion")) { ++ let minOSVersion = update.getAttribute("minSupportedOSVersion"); ++ try { ++ let osVersion = Services.sysinfo.getProperty("version"); ++ this.unsupported = (Services.vc.compare(osVersion, minOSVersion) < 0); ++ } catch (e) {} ++ } ++ ++ if (this._patches.length == 0 && !this.unsupported) { + throw Cr.NS_ERROR_ILLEGAL_VALUE; + } + +@@ -1574,15 +1595,13 @@ function Update(update) { + if(!isNaN(attr.value)) { + this.promptWaitTime = parseInt(attr.value); + } +- } else if (attr.name == "unsupported") { +- this.unsupported = attr.value == "true"; + } else if (attr.name == "version") { + // Prevent version from replacing displayVersion if displayVersion is + // present in the update xml. + if (!this.displayVersion) { + this.displayVersion = attr.value; + } +- } else { ++ } else if (attr.name != "unsupported") { + this[attr.name] = attr.value; + + switch (attr.name) { +@@ -2408,9 +2427,14 @@ UpdateService.prototype = { + updates.forEach(function(aUpdate) { + // Ignore updates for older versions of the application and updates for + // the same version of the application with the same build ID. +- if (vc.compare(aUpdate.appVersion, Services.appinfo.version) < 0 || +- vc.compare(aUpdate.appVersion, Services.appinfo.version) == 0 && +- aUpdate.buildID == Services.appinfo.appBuildID) { ++#ifdef TOR_BROWSER_UPDATE ++ var compatVersion = TOR_BROWSER_VERSION; ++#else ++ var compatVersion = Services.appinfo.version; ++#endif ++ var rc = vc.compare(aUpdate.appVersion, compatVersion); ++ if (rc < 0 || ((rc == 0) && ++ (aUpdate.buildID == Services.appinfo.appBuildID))) { + LOG("UpdateService:selectUpdate - skipping update because the " + + "update's application version is less than the current " + + "application version"); +@@ -2574,8 +2598,13 @@ UpdateService.prototype = { + } + + // Only check add-on compatibility when the version changes. ++#ifdef TOR_BROWSER_UPDATE ++ var compatVersion = TOR_BROWSER_VERSION; ++#else ++ var compatVersion = Services.appinfo.version; ++#endif + if (update.appVersion && +- Services.vc.compare(update.appVersion, Services.appinfo.version) != 0) { ++ Services.vc.compare(update.appVersion, compatVersion) != 0) { + this._update = update; + this._checkAddonCompatibility(); + } +@@ -2606,6 +2635,11 @@ UpdateService.prototype = { + // Get all the installed add-ons + var self = this; + AddonManager.getAllAddons(function(addons) { ++#ifdef TOR_BROWSER_UPDATE ++ let compatVersion = self._update.platformVersion; ++#else ++ let compatVersion = self._update.appVersion; ++#endif + self._incompatibleAddons = []; + addons.forEach(function(addon) { + // Protect against code that overrides the add-ons manager and doesn't +@@ -2635,7 +2669,7 @@ UpdateService.prototype = { + !addon.appDisabled && !addon.userDisabled && + addon.scope != AddonManager.SCOPE_APPLICATION && + addon.isCompatible && +- !addon.isCompatibleWith(self._update.appVersion, ++ !addon.isCompatibleWith(compatVersion, + self._update.platformVersion)) { + self._incompatibleAddons.push(addon); + } +@@ -2671,7 +2705,7 @@ UpdateService.prototype = { + + self._incompatibleAddons.forEach(function(addon) { + addon.findUpdates(this, AddonManager.UPDATE_WHEN_NEW_APP_DETECTED, +- this._update.appVersion, this._update.platformVersion); ++ compatVersion, this._update.platformVersion); + }, self); + } + else { +@@ -2707,7 +2741,12 @@ UpdateService.prototype = { + // If the new version of this add-on is blocklisted for the new application + // then it isn't a valid update and the user should still be warned that + // the add-on will become incompatible. +- if (Services.blocklist.isAddonBlocklisted(addon, this._update.appVersion, ++#ifdef TOR_BROWSER_UPDATE ++ let compatVersion = this._update.platformVersion; ++#else ++ let compatVersion = this._update.appVersion; ++#endif ++ if (Services.blocklist.isAddonBlocklisted(addon, compatVersion, + this._update.platformVersion)) { + return; + } +@@ -2819,14 +2858,24 @@ UpdateService.prototype = { + // current application's version or the update's version is the same as the + // application's version and the build ID is the same as the application's + // build ID. ++#ifdef TOR_BROWSER_UPDATE ++ var compatVersion = TOR_BROWSER_VERSION; ++#else ++ var compatVersion = Services.appinfo.version; ++#endif + if (update.appVersion && +- (Services.vc.compare(update.appVersion, Services.appinfo.version) < 0 || ++ (Services.vc.compare(update.appVersion, compatVersion) < 0 || + update.buildID && update.buildID == Services.appinfo.appBuildID && +- update.appVersion == Services.appinfo.version)) { ++ update.appVersion == compatVersion)) { + LOG("UpdateService:downloadUpdate - canceling download of update since " + + "it is for an earlier or same application version and build ID.\n" + +- "current application version: " + Services.appinfo.version + "\n" + ++#ifdef TOR_BROWSER_UPDATE ++ "current Tor Browser version: " + compatVersion + "\n" + ++ "update Tor Browser version : " + update.appVersion + "\n" + ++#else ++ "current application version: " + compatVersion + "\n" + + "update application version : " + update.appVersion + "\n" + ++#endif + "current build ID: " + Services.appinfo.appBuildID + "\n" + + "update build ID : " + update.buildID); + cleanupActiveUpdate(); +@@ -2859,7 +2908,7 @@ UpdateService.prototype = { + } + } + // Set the previous application version prior to downloading the update. +- update.previousAppVersion = Services.appinfo.version; ++ update.previousAppVersion = compatVersion; + this._downloader = new Downloader(background, this); + return this._downloader.downloadUpdate(update); + }, +diff --git a/toolkit/mozapps/update/updater/updater.cpp b/toolkit/mozapps/update/updater/updater.cpp +index 6ef82a1..f362292 100644 +--- a/toolkit/mozapps/update/updater/updater.cpp ++++ b/toolkit/mozapps/update/updater/updater.cpp +@@ -25,7 +25,7 @@ + * updatev3.manifest + * ----------------- + * method = "add" | "add-if" | "add-if-not" | "patch" | "patch-if" | +- * "remove" | "rmdir" | "rmrfdir" | type ++ * "remove" | "rmdir" | "rmrfdir" | "addsymlink" | type + * + * 'add-if-not' adds a file if it doesn't exist. + * +@@ -403,10 +403,12 @@ get_full_path(const NS_tchar *relpath) + * The line from the manifest that contains the path. + * @param isdir + * Whether the path is a directory path. Defaults to false. ++ * @param islinktarget ++ * Whether the path is a symbolic link target. Defaults to false. + * @return valid filesystem path or nullptr if the path checks fail. + */ + static NS_tchar* +-get_valid_path(NS_tchar **line, bool isdir = false) ++get_valid_path(NS_tchar **line, bool isdir = false, bool islinktarget = false) + { + NS_tchar *path = mstrtok(kQuote, line); + if (!path) { +@@ -441,10 +443,12 @@ get_valid_path(NS_tchar **line, bool isdir = false) + path[NS_tstrlen(path) - 1] = NS_T('\0'); + } + +- // Don't allow relative paths that resolve to a parent directory. +- if (NS_tstrstr(path, NS_T("..")) != nullptr) { +- LOG(("get_valid_path: paths must not contain '..': " LOG_S, path)); +- return nullptr; ++ if (!islinktarget) { ++ // Don't allow relative paths that resolve to a parent directory. ++ if (NS_tstrstr(path, NS_T("..")) != nullptr) { ++ LOG(("get_valid_path: paths must not contain '..': " LOG_S, path)); ++ return nullptr; ++ } + } + + return path; +@@ -479,7 +483,8 @@ static void ensure_write_permissions(const NS_tchar *path) + (void) _wchmod(path, _S_IREAD | _S_IWRITE); + #else + struct stat fs; +- if (!stat(path, &fs) && !(fs.st_mode & S_IWUSR)) { ++ if (!lstat(path, &fs) && !S_ISLNK(fs.st_mode) ++ && !(fs.st_mode & S_IWUSR)) { + (void)chmod(path, fs.st_mode | S_IWUSR); + } + #endif +@@ -680,11 +685,9 @@ static int ensure_copy(const NS_tchar *path, const NS_tchar *dest) + return READ_ERROR; + } + +-#ifdef XP_UNIX + if (S_ISLNK(ss.st_mode)) { + return ensure_copy_symlink(path, dest); + } +-#endif + + #if MAYBE_USE_HARD_LINKS + if (sUseHardLinks) { +@@ -780,7 +783,7 @@ static int ensure_copy_recursive(const NS_tchar *path, const NS_tchar *dest, + return READ_ERROR; + } + +-#ifdef XP_UNIX ++#ifndef XP_WIN + if (S_ISLNK(sInfo.st_mode)) { + return ensure_copy_symlink(path, dest); + } +@@ -839,14 +842,19 @@ static int rename_file(const NS_tchar *spath, const NS_tchar *dpath, + return rv; + + struct NS_tstat_t spathInfo; +- rv = NS_tstat(spath, &spathInfo); ++ rv = NS_tlstat(spath, &spathInfo); // Get info about file or symlink. + if (rv) { + LOG(("rename_file: failed to read file status info: " LOG_S ", " \ + "err: %d", spath, errno)); + return READ_ERROR; + } + +- if (!S_ISREG(spathInfo.st_mode)) { ++#ifdef XP_WIN ++ if (!S_ISREG(spathInfo.st_mode)) ++#else ++ if (!S_ISREG(spathInfo.st_mode) && !S_ISLNK(spathInfo.st_mode)) ++#endif ++ { + if (allowDirs && !S_ISDIR(spathInfo.st_mode)) { + LOG(("rename_file: path present, but not a file: " LOG_S ", err: %d", + spath, errno)); +@@ -856,7 +864,12 @@ static int rename_file(const NS_tchar *spath, const NS_tchar *dpath, + } + } + +- if (!NS_taccess(dpath, F_OK)) { ++#ifdef XP_WIN ++ if (!NS_taccess(dpath, F_OK)) ++#else ++ if (!S_ISLNK(spathInfo.st_mode) && !NS_taccess(dpath, F_OK)) ++#endif ++ { + if (ensure_remove(dpath)) { + LOG(("rename_file: destination file exists and could not be " \ + "removed: " LOG_S, dpath)); +@@ -873,7 +886,7 @@ static int rename_file(const NS_tchar *spath, const NS_tchar *dpath, + return OK; + } + +-#ifdef XP_WIN ++#if defined(XP_WIN) && !defined(TOR_BROWSER_UPDATE) + // Remove the directory pointed to by path and all of its files and + // sub-directories. If a file is in use move it to the tobedeleted directory + // and attempt to schedule removal of the file on reboot +@@ -960,7 +973,18 @@ static int backup_restore(const NS_tchar *path) + NS_tsnprintf(backup, sizeof(backup)/sizeof(backup[0]), + NS_T("%s") BACKUP_EXT, path); + +- if (NS_taccess(backup, F_OK)) { ++ bool isLink = false; ++#ifndef XP_WIN ++ struct stat linkInfo; ++ int rv = lstat(path, &linkInfo); ++ if (!rv) { ++ LOG(("backup_restore: cannot get info for backup file: " LOG_S, backup)); ++ return OK; ++ } ++ isLink = S_ISLNK(linkInfo.st_mode); ++#endif ++ ++ if (!isLink && NS_taccess(backup, F_OK)) { + LOG(("backup_restore: backup file doesn't exist: " LOG_S, backup)); + return OK; + } +@@ -975,8 +999,18 @@ static int backup_discard(const NS_tchar *path) + NS_tsnprintf(backup, sizeof(backup)/sizeof(backup[0]), + NS_T("%s") BACKUP_EXT, path); + ++ bool isLink = false; ++#ifndef XP_WIN ++ struct stat linkInfo; ++ int rv2 = lstat(backup, &linkInfo); ++ if (rv2) { ++ return OK; // File does not exist; nothing to do. ++ } ++ isLink = S_ISLNK(linkInfo.st_mode); ++#endif ++ + // Nothing to discard +- if (NS_taccess(backup, F_OK)) { ++ if (!isLink && NS_taccess(backup, F_OK)) { + return OK; + } + +@@ -991,6 +1025,8 @@ static int backup_discard(const NS_tchar *path) + backup, path)); + return WRITE_ERROR_DELETE_BACKUP; + } ++ ++#if !defined(TOR_BROWSER_UPDATE) + // The MoveFileEx call to remove the file on OS reboot will fail if the + // process doesn't have write access to the HKEY_LOCAL_MACHINE registry key + // but this is ok since the installer / uninstaller will delete the +@@ -1003,6 +1039,7 @@ static int backup_discard(const NS_tchar *path) + LOG(("backup_discard: failed to schedule OS reboot removal of " \ + "file: " LOG_S, path)); + } ++#endif + } + #else + if (rv) +@@ -1056,7 +1093,7 @@ private: + class RemoveFile : public Action + { + public: +- RemoveFile() : mFile(nullptr), mSkip(0) { } ++ RemoveFile() : mFile(nullptr), mSkip(0), mIsLink(0) { } + + int Parse(NS_tchar *line); + int Prepare(); +@@ -1066,6 +1103,7 @@ public: + private: + const NS_tchar *mFile; + int mSkip; ++ int mIsLink; + }; + + int +@@ -1083,28 +1121,39 @@ RemoveFile::Parse(NS_tchar *line) + int + RemoveFile::Prepare() + { +- // Skip the file if it already doesn't exist. +- int rv = NS_taccess(mFile, F_OK); +- if (rv) { +- mSkip = 1; +- mProgressCost = 0; +- return OK; ++ int rv; ++#ifndef XP_WIN ++ struct stat linkInfo; ++ rv = lstat(mFile, &linkInfo); ++ mIsLink = ((0 == rv) && S_ISLNK(linkInfo.st_mode)); ++#endif ++ ++ if (!mIsLink) { ++ // Skip the file if it already doesn't exist. ++ rv = NS_taccess(mFile, F_OK); ++ if (rv) { ++ mSkip = 1; ++ mProgressCost = 0; ++ return OK; ++ } + } + + LOG(("PREPARE REMOVEFILE " LOG_S, mFile)); + +- // Make sure that we're actually a file... +- struct NS_tstat_t fileInfo; +- rv = NS_tstat(mFile, &fileInfo); +- if (rv) { +- LOG(("failed to read file status info: " LOG_S ", err: %d", mFile, +- errno)); +- return READ_ERROR; +- } ++ if (!mIsLink) { ++ // Make sure that we're actually a file... ++ struct NS_tstat_t fileInfo; ++ rv = NS_tstat(mFile, &fileInfo); ++ if (rv) { ++ LOG(("failed to read file status info: " LOG_S ", err: %d", mFile, ++ errno)); ++ return READ_ERROR; ++ } + +- if (!S_ISREG(fileInfo.st_mode)) { +- LOG(("path present, but not a file: " LOG_S, mFile)); +- return DELETE_ERROR_EXPECTED_FILE; ++ if (!S_ISREG(fileInfo.st_mode)) { ++ LOG(("path present, but not a file: " LOG_S, mFile)); ++ return DELETE_ERROR_EXPECTED_FILE; ++ } + } + + NS_tchar *slash = (NS_tchar *) NS_tstrrchr(mFile, NS_T('/')); +@@ -1134,7 +1183,13 @@ RemoveFile::Execute() + + // The file is checked for existence here and in Prepare since it might have + // been removed by a separate instruction: bug 311099. +- int rv = NS_taccess(mFile, F_OK); ++ int rv = 0; ++ if (mIsLink) { ++ struct NS_tstat_t linkInfo; ++ rv = NS_tlstat(mFile, &linkInfo); ++ } else { ++ rv = NS_taccess(mFile, F_OK); ++ } + if (rv) { + LOG(("file cannot be removed because it does not exist; skipping")); + mSkip = 1; +@@ -1814,6 +1869,97 @@ PatchIfFile::Finish(int status) + PatchFile::Finish(status); + } + ++#ifndef XP_WIN ++class AddSymlink : public Action ++{ ++public: ++ AddSymlink() : mLinkName(NULL) ++ , mTarget(NULL) ++ , mAdded(false) ++ { } ++ ++ virtual int Parse(NS_tchar *line); ++ virtual int Prepare(); ++ virtual int Execute(); ++ virtual void Finish(int status); ++ ++private: ++ const NS_tchar *mLinkName; ++ const NS_tchar *mTarget; ++ bool mAdded; ++}; ++ ++int ++AddSymlink::Parse(NS_tchar *line) ++{ ++ // format "<linkname>" "target" ++ ++ mLinkName = get_valid_path(&line); ++ if (!mLinkName) ++ return PARSE_ERROR; ++ ++ // consume whitespace between args ++ NS_tchar *q = mstrtok(kQuote, &line); ++ if (!q) ++ return PARSE_ERROR; ++ ++ mTarget = get_valid_path(&line, false, true); ++ if (!mTarget) ++ return PARSE_ERROR; ++ ++ return OK; ++} ++ ++int ++AddSymlink::Prepare() ++{ ++ LOG(("PREPARE ADDSYMLINK " LOG_S " -> " LOG_S, mLinkName, mTarget)); ++ ++ return OK; ++} ++ ++int ++AddSymlink::Execute() ++{ ++ LOG(("EXECUTE ADDSYMLINK " LOG_S " -> " LOG_S, mLinkName, mTarget)); ++ ++ // First make sure that we can actually get rid of any existing file or link. ++ struct stat linkInfo; ++ int rv = lstat(mLinkName, &linkInfo); ++ if ((0 == rv) && !S_ISLNK(linkInfo.st_mode)) { ++ rv = NS_taccess(mLinkName, F_OK); ++ } ++ if (rv == 0) { ++ rv = backup_create(mLinkName); ++ if (rv) ++ return rv; ++ } else { ++ rv = ensure_parent_dir(mLinkName); ++ if (rv) ++ return rv; ++ } ++ ++ // Create the link. ++ rv = symlink(mTarget, mLinkName); ++ if (!rv) { ++ mAdded = true; ++ } ++ ++ return rv; ++} ++ ++void ++AddSymlink::Finish(int status) ++{ ++ LOG(("FINISH ADDSYMLINK " LOG_S " -> " LOG_S, mLinkName, mTarget)); ++ // When there is an update failure and a link has been added it is removed ++ // here since there might not be a backup to replace it. ++ if (status && mAdded) ++ NS_tremove(mLinkName); ++ backup_finish(mLinkName, status); ++} ++#endif ++ + //----------------------------------------------------------------------------- + + #ifdef XP_WIN +@@ -2115,14 +2261,29 @@ static int + 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 ++#if defined(TOR_BROWSER_UPDATE) && !defined(TOR_BROWSER_DATA_OUTSIDE_APP_DIR) ++ #ifdef XP_WIN ++ #define SKIPLIST_COUNT 6 ++ #else ++ #define SKIPLIST_COUNT 5 ++ #endif + #else +-#define SKIPLIST_COUNT 2 ++ #ifdef XP_WIN ++ #define SKIPLIST_COUNT 3 ++ #elif XP_MACOSX ++ #define SKIPLIST_COUNT 0 ++ #else ++ #define SKIPLIST_COUNT 2 ++ #endif + #endif + copy_recursive_skiplist<SKIPLIST_COUNT> skiplist; ++#if defined(TOR_BROWSER_UPDATE) && !defined(TOR_BROWSER_DATA_OUTSIDE_APP_DIR) ++#ifdef XP_MACOSX ++ skiplist.append(0, gInstallDirPath, NS_T("Updated.app")); ++ skiplist.append(1, gInstallDirPath, NS_T("TorBrowser/UpdateInfo/updates/0")); ++#endif ++#endif ++ + #ifndef XP_MACOSX + skiplist.append(0, gInstallDirPath, NS_T("updated")); + skiplist.append(1, gInstallDirPath, NS_T("updates/0")); +@@ -2131,6 +2292,23 @@ CopyInstallDirToDestDir() + #endif + #endif + ++#if defined(TOR_BROWSER_UPDATE) && !defined(TOR_BROWSER_DATA_OUTSIDE_APP_DIR) ++#ifdef XP_WIN ++ skiplist.append(SKIPLIST_COUNT - 3, gInstallDirPath, ++ NS_T("TorBrowser/Data/Browser/profile.default/parent.lock")); ++ skiplist.append(SKIPLIST_COUNT - 2, gInstallDirPath, ++ NS_T("TorBrowser/Data/Browser/profile.meek-http-helper/parent.lock")); ++#else ++ skiplist.append(SKIPLIST_COUNT - 3, gInstallDirPath, ++ NS_T("TorBrowser/Data/Browser/profile.default/.parentlock")); ++ skiplist.append(SKIPLIST_COUNT - 2, gInstallDirPath, ++ NS_T("TorBrowser/Data/Browser/profile.meek-http-helper/.parentlock")); ++#endif ++ ++skiplist.append(SKIPLIST_COUNT - 1, gInstallDirPath, ++ NS_T("TorBrowser/Data/Tor/lock")); ++#endif ++ + return ensure_copy_recursive(gInstallDirPath, gWorkingDirPath, skiplist); + } + +@@ -2251,16 +2429,52 @@ ProcessReplaceRequest() + if (NS_taccess(deleteDir, F_OK)) { + NS_tmkdir(deleteDir, 0755); + } ++#if !defined(TOR_BROWSER_UPDATE) + remove_recursive_on_reboot(tmpDir, deleteDir); + #endif ++#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]; ++#if defined(TOR_BROWSER_UPDATE) && !defined(TOR_BROWSER_DATA_OUTSIDE_APP_DIR) ++ NS_tsnprintf(updatedAppDir, sizeof(updatedAppDir)/sizeof(updatedAppDir[0]), ++ NS_T("%s/Updated.app"), gInstallDirPath); ++ // For Tor Browser on OS X, we also need to copy everything else that is inside Updated.app. ++ NS_tDIR *dir = NS_topendir(updatedAppDir); ++ if (dir) { ++ NS_tdirent *entry; ++ while ((entry = NS_treaddir(dir)) != 0) { ++ if (NS_tstrcmp(entry->d_name, NS_T(".")) && ++ NS_tstrcmp(entry->d_name, NS_T(".."))) { ++ NS_tchar childSrcPath[MAXPATHLEN]; ++ NS_tsnprintf(childSrcPath, sizeof(childSrcPath)/sizeof(childSrcPath[0]), ++ NS_T("%s/%s"), updatedAppDir, entry->d_name); ++ NS_tchar childDstPath[MAXPATHLEN]; ++ NS_tsnprintf(childDstPath, sizeof(childDstPath)/sizeof(childDstPath[0]), ++ NS_T("%s/%s"), gInstallDirPath, entry->d_name); ++ ensure_remove_recursive(childDstPath); ++ rv = rename_file(childSrcPath, childDstPath, true); ++ if (rv) { ++ LOG(("Moving " LOG_S " to " LOG_S " failed, err: %d", ++ childSrcPath, childDstPath, errno)); ++ } ++ } ++ } ++ ++ NS_tclosedir(dir); ++ } else { ++ LOG(("Updated.app dir can't be found: " LOG_S ", err: %d", ++ updatedAppDir, errno)); ++ } ++#else + NS_tsnprintf(updatedAppDir, sizeof(updatedAppDir)/sizeof(updatedAppDir[0]), + NS_T("%s/Updated.app"), gPatchDirPath); ++#endif ++ ++ // Remove the Updated.app directory. + ensure_remove_recursive(updatedAppDir); + #endif + +@@ -2998,6 +3212,26 @@ int NS_main(int argc, NS_tchar **argv) + // using the service is because we are testing. + if (!useService && !noServiceFallback && + updateLockFileHandle == INVALID_HANDLE_VALUE) { ++#ifdef TOR_BROWSER_UPDATE ++#ifdef TOR_BROWSER_DATA_OUTSIDE_APP_DIR ++ // Because the TorBrowser-Data directory that contains the user's ++ // profile is a sibling of the Tor Browser installation directory, ++ // the user probably has permission to apply updates. Therefore, to ++ // avoid potential security issues such as CVE-2015-0833, do not ++ // attempt to elevate privileges. Instead, write a "failed" message ++ // to the update status file (this function will return immediately ++ // after the CloseHandle(elevatedFileHandle) call below). ++#else ++ // Because the user profile is contained within the Tor Browser ++ // installation directory, the user almost certainly has permission to ++ // apply updates. Therefore, to avoid potential security issues such ++ // as CVE-2015-0833, do not attempt to elevate privileges. Instead, ++ // write a "failed" message to the update status file (this function ++ // will return immediately after the CloseHandle(elevatedFileHandle) ++ // call below). ++#endif ++ WriteStatusFile(WRITE_ERROR_ACCESS_DENIED); ++#else + SHELLEXECUTEINFO sinfo; + memset(&sinfo, 0, sizeof(SHELLEXECUTEINFO)); + sinfo.cbSize = sizeof(SHELLEXECUTEINFO); +@@ -3019,6 +3253,7 @@ int NS_main(int argc, NS_tchar **argv) + } else { + WriteStatusFile(ELEVATION_CANCELED); + } ++#endif + } + + if (argc > callbackIndex) { +@@ -3310,6 +3545,7 @@ int NS_main(int argc, NS_tchar **argv) + if (!sStagedUpdate && !sReplaceRequest && _wrmdir(DELETE_DIR)) { + LOG(("NS_main: unable to remove directory: " LOG_S ", err: %d", + DELETE_DIR, errno)); ++#if !defined(TOR_BROWSER_UPDATE) + // The directory probably couldn't be removed due to it containing files + // that are in use and will be removed on OS reboot. The call to remove the + // directory on OS reboot is done after the calls to remove the files so the +@@ -3326,6 +3562,7 @@ int NS_main(int argc, NS_tchar **argv) + LOG(("NS_main: failed to schedule OS reboot removal of " \ + "directory: " LOG_S, DELETE_DIR)); + } ++#endif + } + #endif /* XP_WIN */ + +@@ -3376,7 +3613,7 @@ int NS_main(int argc, NS_tchar **argv) + LogFinish(); + + if (argc > callbackIndex) { +-#if defined(XP_WIN) ++#if defined(XP_WIN) && !defined(TOR_BROWSER_UPDATE) + if (gSucceeded) { + if (!LaunchWinPostProcess(gInstallDirPath, gPatchDirPath)) { + fprintf(stderr, "The post update process was not launched"); +@@ -4005,6 +4242,11 @@ int DoUpdate() + else if (NS_tstrcmp(token, NS_T("patch-if")) == 0) { // Patch if exists + action = new PatchIfFile(); + } ++#ifndef XP_WIN ++ else if (NS_tstrcmp(token, NS_T("addsymlink")) == 0) { ++ action = new AddSymlink(); ++ } ++#endif + else { + LOG(("DoUpdate: unknown token: " LOG_S, token)); + return PARSE_ERROR; +diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp +index 6d04d46..a3a2857 100644 +--- a/toolkit/xre/nsAppRunner.cpp ++++ b/toolkit/xre/nsAppRunner.cpp +@@ -4051,12 +4051,20 @@ XREMain::XRE_mainStartup(bool* aExitFlag) + NS_ENSURE_SUCCESS(rv, 1); + rv = exeFile->GetParent(getter_AddRefs(exeDir)); + NS_ENSURE_SUCCESS(rv, 1); ++#ifdef TOR_BROWSER_UPDATE ++ nsAutoCString compatVersion(TOR_BROWSER_VERSION); ++#endif + ProcessUpdates(mDirProvider.GetGREDir(), + exeDir, + updRoot, + gRestartArgc, + gRestartArgv, +- mAppData->version); ++#ifdef TOR_BROWSER_UPDATE ++ compatVersion.get() ++#else ++ mAppData->version ++#endif ++ ); + if (EnvHasValue("MOZ_TEST_PROCESS_UPDATES")) { + SaveToEnv("MOZ_TEST_PROCESS_UPDATES="); + *aExitFlag = true; +diff --git a/toolkit/xre/nsUpdateDriver.cpp b/toolkit/xre/nsUpdateDriver.cpp +index 83002ca..ee60aee 100644 +--- a/toolkit/xre/nsUpdateDriver.cpp ++++ b/toolkit/xre/nsUpdateDriver.cpp +@@ -39,6 +39,7 @@ + # include <windows.h> + # include <shlwapi.h> + # include "nsWindowsHelpers.h" ++# include "prprf.h" + # define getcwd(path, size) _getcwd(path, size) + # define getpid() GetCurrentProcessId() + #elif defined(XP_UNIX) +@@ -168,6 +169,46 @@ GetInstallDirPath(nsIFile *appDir, nsACString& installDirPath) + return NS_OK; + } + ++#if defined(TOR_BROWSER_UPDATE) && defined(XP_WIN) ++#define PATH_SEPARATOR ";" ++ ++// In Tor Browser, updater.exe depends on some DLLs that are located in the ++// app directory. To allow the updater to run when it has been copied into ++// the update directory, we append the app directory to the PATH. ++static nsresult ++AdjustPathForUpdater(nsIFile *appDir) ++{ ++ nsAutoCString appPath; ++ nsresult rv = appDir->GetNativePath(appPath); ++ NS_ENSURE_SUCCESS(rv, rv); ++ ++ char *s = nullptr; ++ char *pathValue = PR_GetEnv("PATH"); ++ if ((nullptr == pathValue) || ('\0' == *pathValue)) { ++ s = PR_smprintf("PATH=%s", appPath.get()); ++ } else { ++ s = PR_smprintf("PATH=%s" PATH_SEPARATOR "%s", pathValue, appPath.get()); ++ } ++ ++ // We intentionally leak the value that is passed into PR_SetEnv() because ++ // the environment will hold a pointer to it. ++ if ((nullptr == s) || (PR_SUCCESS != PR_SetEnv(s))) ++ return NS_ERROR_FAILURE; ++ ++ return NS_OK; ++} ++#endif ++ ++#ifdef DEBUG ++static void ++dump_argv(const char *aPrefix, char **argv, int argc) ++{ ++ printf("%s - %d args\n", aPrefix, argc); ++ for (int i = 0; i < argc; ++i) ++ printf(" %d: %s\n", i, argv[i]); ++} ++#endif ++ + #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 +@@ -255,6 +296,33 @@ typedef enum { + eAppliedService + } UpdateStatus; + ++#ifdef DEBUG ++static const char * ++UpdateStatusToString(UpdateStatus aStatus) ++{ ++ const char *rv = "unknown"; ++ switch (aStatus) { ++ case eNoUpdateAction: ++ rv = "NoUpdateAction"; ++ break; ++ case ePendingUpdate: ++ rv = "PendingUpdate"; ++ break; ++ case ePendingService: ++ rv = "PendingService"; ++ break; ++ case eAppliedUpdate: ++ rv = "AppliedUpdate"; ++ break; ++ case eAppliedService: ++ rv = "AppliedService"; ++ break; ++ } ++ ++ return rv; ++} ++#endif ++ + /** + * Returns a value indicating what needs to be done in order to handle an update. + * +@@ -323,12 +391,44 @@ IsOlderVersion(nsIFile *versionFile, const char *appVersion) + if (strncmp(buf, kNull, sizeof(kNull) - 1) == 0) + return false; + ++#ifdef DEBUG ++ printf("IsOlderVersion checking appVersion %s against updateVersion %s\n", ++ appVersion, buf); ++#endif ++ + if (mozilla::Version(appVersion) > buf) + return true; + + return false; + } + ++#ifndef TOR_BROWSER_DATA_OUTSIDE_APP_DIR ++#if defined(TOR_BROWSER_UPDATE) && defined(XP_MACOSX) ++static nsresult ++GetUpdateDirFromAppDir(nsIFile *aAppDir, nsIFile* *aResult) ++{ ++ // On Mac OSX, we stage the update to an Updated.app directory that is ++ // directly below the main Tor Browser.app directory (two levels up from ++ // the appDir). ++ NS_ENSURE_ARG_POINTER(aAppDir); ++ NS_ENSURE_ARG_POINTER(aResult); ++ nsCOMPtr<nsIFile> parentDir1, parentDir2; ++ nsresult rv = aAppDir->GetParent(getter_AddRefs(parentDir1)); ++ NS_ENSURE_SUCCESS(rv, rv); ++ rv = parentDir1->GetParent(getter_AddRefs(parentDir2)); ++ NS_ENSURE_SUCCESS(rv, rv); ++ ++ nsCOMPtr<nsIFile> updatedDir; ++ if (!GetFile(parentDir2, NS_LITERAL_CSTRING("Updated.app"), updatedDir)) { ++ return NS_ERROR_FAILURE; ++ } ++ ++ updatedDir.forget(aResult); ++ return NS_OK; ++} ++#endif ++#endif ++ + static bool + CopyFileIntoUpdateDir(nsIFile *parentDir, const nsACString& leaf, nsIFile *updateDir) + { +@@ -541,7 +641,12 @@ SwitchToUpdatedApp(nsIFile *greDir, nsIFile *updateDir, + nsAutoCString applyToDir; + nsCOMPtr<nsIFile> updatedDir; + #ifdef XP_MACOSX ++#if defined(TOR_BROWSER_UPDATE) && !defined(TOR_BROWSER_DATA_OUTSIDE_APP_DIR) ++ rv = GetUpdateDirFromAppDir(appDir, getter_AddRefs(updatedDir)); ++ if (NS_FAILED(rv)) { ++#else + if (!GetFile(updateDir, NS_LITERAL_CSTRING("Updated.app"), updatedDir)) { ++#endif + #else + if (!GetFile(appDir, NS_LITERAL_CSTRING("updated"), updatedDir)) { + #endif +@@ -632,6 +737,13 @@ SwitchToUpdatedApp(nsIFile *greDir, nsIFile *updateDir, + AppendToLibPath(installDirPath.get()); + #endif + ++#if defined(TOR_BROWSER_UPDATE) && defined(XP_WIN) ++ nsresult rv2 = AdjustPathForUpdater(appDir); ++ if (NS_FAILED(rv2)) { ++ LOG(("SwitchToUpdatedApp -- AdjustPathForUpdater failed (0x%x)\n", rv2)); ++ } ++#endif ++ + LOG(("spawning updater process for replacing [%s]\n", updaterPath.get())); + + #if defined(USE_EXECV) +@@ -807,7 +919,12 @@ ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsIFile *statusFile, + applyToDir.Assign(installDirPath); + } else { + #ifdef XP_MACOSX ++#if defined(TOR_BROWSER_UPDATE) && !defined(TOR_BROWSER_DATA_OUTSIDE_APP_DIR) ++ rv = GetUpdateDirFromAppDir(appDir, getter_AddRefs(updatedDir)); ++ if (NS_FAILED(rv)) { ++#else + if (!GetFile(updateDir, NS_LITERAL_CSTRING("Updated.app"), updatedDir)) { ++#endif + #else + if (!GetFile(appDir, NS_LITERAL_CSTRING("updated"), updatedDir)) { + #endif +@@ -911,6 +1028,14 @@ ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsIFile *statusFile, + if (isOSUpdate) { + PR_SetEnv("MOZ_OS_UPDATE=1"); + } ++ ++#if defined(TOR_BROWSER_UPDATE) && defined(XP_WIN) ++ nsresult rv2 = AdjustPathForUpdater(appDir); ++ if (NS_FAILED(rv2)) { ++ LOG(("ApplyUpdate -- AdjustPathForUpdater failed (0x%x)\n", rv2)); ++ } ++#endif ++ + #if defined(MOZ_WIDGET_GONK) + // We want the updater to be CPU friendly and not subject to being killed by + // the low memory killer, so we pass in some preferences to allow it to +@@ -933,6 +1058,9 @@ ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsIFile *statusFile, + #endif + + LOG(("spawning updater process [%s]\n", updaterPath.get())); ++#ifdef DEBUG ++ dump_argv("ApplyUpdate updater", argv, argc); ++#endif + + #if defined(USE_EXECV) + // Don't use execv when staging updates. +@@ -956,6 +1084,9 @@ ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsIFile *statusFile, + // LaunchChildMac uses posix_spawnp and prefers the current + // architecture when launching. It doesn't require a + // null-terminated string but it doesn't matter if we pass one. ++#ifdef DEBUG ++ dump_argv("ApplyUpdate after SetupMacCommandLine", argv, argc); ++#endif + LaunchChildMac(argc, argv, 0, outpid); + if (restart) { + exit(0); +@@ -994,9 +1125,29 @@ ProcessUpdates(nsIFile *greDir, nsIFile *appDir, nsIFile *updRootDir, + bool restart, bool isOSUpdate, nsIFile *osApplyToDir, + ProcessType *pid) + { ++#if defined(XP_WIN) && defined(TOR_BROWSER_UPDATE) ++ // Try to remove the "tobedeleted" directory which, if present, contains ++ // files that could not be removed during a previous update (e.g., DLLs ++ // that were in use and therefore locked by Windows). ++ nsCOMPtr<nsIFile> deleteDir; ++ nsresult winrv = appDir->Clone(getter_AddRefs(deleteDir)); ++ if (NS_SUCCEEDED(winrv)) { ++ winrv = deleteDir->AppendNative(NS_LITERAL_CSTRING("tobedeleted")); ++ if (NS_SUCCEEDED(winrv)) { ++ winrv = deleteDir->Remove(true); ++ } ++ } ++#endif ++ + nsresult rv; + + nsCOMPtr<nsIFile> updatesDir; ++#ifdef DEBUG ++ nsAutoCString path; ++ updRootDir->GetNativePath(path); ++ printf("ProcessUpdates updateRootDir: %s appVersion: %s\n", ++ path.get(), appVersion); ++#endif + rv = updRootDir->Clone(getter_AddRefs(updatesDir)); + if (NS_FAILED(rv)) + return rv; +@@ -1022,6 +1173,12 @@ ProcessUpdates(nsIFile *greDir, nsIFile *appDir, nsIFile *updRootDir, + + nsCOMPtr<nsIFile> statusFile; + UpdateStatus status = GetUpdateStatus(updatesDir, statusFile); ++#ifdef DEBUG ++ printf("ProcessUpdates status: %s (%d)\n", ++ UpdateStatusToString(status), status); ++ updatesDir->GetNativePath(path); ++ printf("ProcessUpdates updatesDir: %s\n", path.get()); ++#endif + switch (status) { + case ePendingUpdate: + case ePendingService: { +@@ -1104,7 +1261,11 @@ nsUpdateProcessor::ProcessUpdate(nsIUpdate* aUpdate) + if (NS_FAILED(rv)) + appDir = dirProvider->GetAppDir(); + ++#ifdef TOR_BROWSER_UPDATE ++ appVersion = TOR_BROWSER_VERSION; ++#else + appVersion = gAppData->version; ++#endif + argc = gRestartArgc; + argv = gRestartArgv; + } else { +@@ -1134,6 +1295,8 @@ nsUpdateProcessor::ProcessUpdate(nsIUpdate* aUpdate) + getter_AddRefs(updRoot)); + NS_ASSERTION(NS_SUCCEEDED(rv), "Can't get the UpdRootD dir"); + ++ // To support Tor Browser updates from xpcshell, modify the following ++ // code to use TOR_BROWSER_VERSION from the configure process. + nsCOMPtr<nsIXULAppInfo> appInfo = + do_GetService("@mozilla.org/xre/app-info;1"); + if (appInfo) { +diff --git a/toolkit/xre/nsXREDirProvider.cpp b/toolkit/xre/nsXREDirProvider.cpp +index 831a373..403e820 100644 +--- a/toolkit/xre/nsXREDirProvider.cpp ++++ b/toolkit/xre/nsXREDirProvider.cpp +@@ -1066,6 +1066,31 @@ nsXREDirProvider::GetUpdateRootDir(nsIFile* *aResult) + NS_ENSURE_SUCCESS(rv, rv); + + #ifdef XP_MACOSX ++#ifdef TOR_BROWSER_UPDATE ++#ifdef TOR_BROWSER_DATA_OUTSIDE_APP_DIR ++ // For Tor Browser, we cannot store update history, etc. under the user's ++ // home directory. Instead, we place it under ++ // Tor Browser.app/../TorBrowser-Data/UpdateInfo/ ++ nsCOMPtr<nsIFile> appRootDir; ++ rv = GetAppRootDir(getter_AddRefs(appRootDir)); ++ NS_ENSURE_SUCCESS(rv, rv); ++ nsCOMPtr<nsIFile> localDir; ++ rv = appRootDir->GetParent(getter_AddRefs(localDir)); ++ NS_ENSURE_SUCCESS(rv, rv); ++ rv = localDir->AppendRelativeNativePath(NS_LITERAL_CSTRING("TorBrowser-Data" ++ XPCOM_FILE_PATH_SEPARATOR "UpdateInfo")); ++#else ++ // For Tor Browser, we cannot store update history, etc. under the user's home directory. ++ // Instead, we place it under Tor Browser.app/TorBrowser/UpdateInfo/ ++ nsCOMPtr<nsIFile> localDir; ++ rv = GetAppRootDir(getter_AddRefs(localDir)); ++ NS_ENSURE_SUCCESS(rv, rv); ++ rv = localDir->AppendNative(NS_LITERAL_CSTRING("TorBrowser")); ++ NS_ENSURE_SUCCESS(rv, rv); ++ rv = localDir->AppendNative(NS_LITERAL_CSTRING("UpdateInfo")); ++#endif ++ NS_ENSURE_SUCCESS(rv, rv); ++#else + nsCOMPtr<nsIFile> appRootDirFile; + nsCOMPtr<nsIFile> localDir; + nsAutoString appDirPath; +@@ -1096,6 +1121,7 @@ nsXREDirProvider::GetUpdateRootDir(nsIFile* *aResult) + NS_FAILED(localDir->AppendRelativePath(appDirPath))) { + return NS_ERROR_FAILURE; + } ++#endif + + localDir.forget(aResult); + return NS_OK; +@@ -1243,33 +1269,8 @@ nsXREDirProvider::GetUserDataDirectoryHome(nsIFile** aFile, bool aLocal) + NS_ENSURE_ARG_POINTER(aFile); + nsCOMPtr<nsIFile> localDir; + +- nsresult rv = GetAppDir()->Clone(getter_AddRefs(localDir)); ++ nsresult rv = GetAppRootDir(getter_AddRefs(localDir)); + NS_ENSURE_SUCCESS(rv, rv); +- +- int levelsToRemove = 0; // In FF21+, appDir points to browser subdirectory. +-#if defined(XP_MACOSX) +- levelsToRemove += 1; +-#endif +- while (localDir && (levelsToRemove > 0)) { +- // When crawling up the hierarchy, components named "." do not count. +- nsAutoCString removedName; +- rv = localDir->GetNativeLeafName(removedName); +- NS_ENSURE_SUCCESS(rv, rv); +- bool didRemove = !removedName.Equals("."); +- +- // Remove a directory component. +- nsCOMPtr<nsIFile> parentDir; +- rv = localDir->GetParent(getter_AddRefs(parentDir)); +- NS_ENSURE_SUCCESS(rv, rv); +- localDir = parentDir; +- +- if (didRemove) +- --levelsToRemove; +- } +- +- if (!localDir) +- return NS_ERROR_FAILURE; +- + rv = localDir->AppendRelativeNativePath(NS_LITERAL_CSTRING("TorBrowser" + XPCOM_FILE_PATH_SEPARATOR "Data" + XPCOM_FILE_PATH_SEPARATOR "Browser")); +@@ -1376,6 +1377,44 @@ nsXREDirProvider::GetUserDataDirectory(nsIFile** aFile, bool aLocal, + } + + nsresult ++nsXREDirProvider::GetAppRootDir(nsIFile* *aFile) ++{ ++ NS_ENSURE_ARG_POINTER(aFile); ++ nsCOMPtr<nsIFile> appRootDir; ++ ++ nsresult rv = GetAppDir()->Clone(getter_AddRefs(appRootDir)); ++ NS_ENSURE_SUCCESS(rv, rv); ++ ++ int levelsToRemove = 0; // In FF21+, appDir points to browser subdirectory. ++#if defined(XP_MACOSX) ++ levelsToRemove += 1; ++#endif ++ while (appRootDir && (levelsToRemove > 0)) { ++ // When crawling up the hierarchy, components named "." do not count. ++ nsAutoCString removedName; ++ rv = appRootDir->GetNativeLeafName(removedName); ++ NS_ENSURE_SUCCESS(rv, rv); ++ bool didRemove = !removedName.Equals("."); ++ ++ // Remove a directory component. ++ nsCOMPtr<nsIFile> parentDir; ++ rv = appRootDir->GetParent(getter_AddRefs(parentDir)); ++ NS_ENSURE_SUCCESS(rv, rv); ++ appRootDir = parentDir; ++ ++ if (didRemove) ++ --levelsToRemove; ++ } ++ ++ if (!appRootDir) ++ return NS_ERROR_FAILURE; ++ ++ appRootDir.forget(aFile); ++ return NS_OK; ++} ++ ++ ++nsresult + nsXREDirProvider::EnsureDirectoryExists(nsIFile* aDirectory) + { + bool exists; +diff --git a/toolkit/xre/nsXREDirProvider.h b/toolkit/xre/nsXREDirProvider.h +index 1985f66..b86cc68 100644 +--- a/toolkit/xre/nsXREDirProvider.h ++++ b/toolkit/xre/nsXREDirProvider.h +@@ -107,6 +107,7 @@ protected: + #if defined(XP_UNIX) || defined(XP_MACOSX) + static nsresult GetSystemExtensionsDirectory(nsIFile** aFile); + #endif ++ nsresult GetAppRootDir(nsIFile* *aFile); + static nsresult EnsureDirectoryExists(nsIFile* aDirectory); + void EnsureProfileFileExists(nsIFile* aFile); + +diff --git a/tools/update-packaging/common.sh b/tools/update-packaging/common.sh +index eb35880..96bfa43 100755 +--- a/tools/update-packaging/common.sh ++++ b/tools/update-packaging/common.sh +@@ -8,7 +8,13 @@ + # Author: Darin Fisher + # + ++# TODO When TOR_BROWSER_DATA_OUTSIDE_APP_DIR is used on all platforms, ++# we should remove all lines in this file that contain: ++# TorBrowser/Data ++ + # ----------------------------------------------------------------------------- ++QUIET=0 ++ + # By default just assume that these tools exist on our path + MAR=${MAR:-mar} + BZIP2=${BZIP2:-bzip2} +@@ -21,6 +27,12 @@ notice() { + echo "$*" 1>&2 + } + ++verbose_notice() { ++ if [ $QUIET -eq 0 ]; then ++ notice "$*" ++ fi ++} ++ + get_file_size() { + info=($(ls -ln "$1")) + echo ${info[4]} +@@ -54,22 +66,10 @@ make_add_instruction() { + forced= + fi + +- is_extension=$(echo "$f" | grep -c 'distribution/extensions/.*/') +- if [ $is_extension = "1" ]; then +- # Use the subdirectory of the extensions folder as the file to test +- # before performing this add instruction. +- testdir=$(echo "$f" | sed 's/(.*distribution/extensions/[^/]*)/.*/\1/') +- notice " add-if "$testdir" "$f"" +- echo "add-if "$testdir" "$f"" >> $filev2 +- if [ ! $filev3 = "" ]; then +- echo "add-if "$testdir" "$f"" >> $filev3 +- fi +- else +- notice " add "$f"$forced" +- echo "add "$f"" >> $filev2 +- if [ ! $filev3 = "" ]; then +- echo "add "$f"" >> $filev3 +- fi ++ verbose_notice " add "$f"$forced" ++ echo "add "$f"" >> "$filev2" ++ if [ ! "$filev3" = "" ]; then ++ echo "add "$f"" >> "$filev3" + fi + } + +@@ -100,8 +100,19 @@ make_add_if_not_instruction() { + f="$1" + filev3="$2" + +- notice " add-if-not "$f" "$f"" +- echo "add-if-not "$f" "$f"" >> $filev3 ++ verbose_notice " add-if-not "$f" "$f"" ++ echo "add-if-not "$f" "$f"" >> "$filev3" ++} ++ ++make_addsymlink_instruction() { ++ link="$1" ++ target="$2" ++ filev2="$3" ++ filev3="$4" ++ ++ verbose_notice " addsymlink: $link -> $target" ++ echo "addsymlink "$link" "$target"" >> "$filev2" ++ echo "addsymlink "$link" "$target"" >> "$filev3" + } + + make_patch_instruction() { +@@ -109,19 +120,9 @@ make_patch_instruction() { + filev2="$2" + filev3="$3" + +- is_extension=$(echo "$f" | grep -c 'distribution/extensions/.*/') +- if [ $is_extension = "1" ]; then +- # Use the subdirectory of the extensions folder as the file to test +- # before performing this add instruction. +- testdir=$(echo "$f" | sed 's/(.*distribution/extensions/[^/]*)/.*/\1/') +- notice " patch-if "$testdir" "$f.patch" "$f"" +- echo "patch-if "$testdir" "$f.patch" "$f"" >> $filev2 +- echo "patch-if "$testdir" "$f.patch" "$f"" >> $filev3 +- else +- notice " patch "$f.patch" "$f"" +- echo "patch "$f.patch" "$f"" >> $filev2 +- echo "patch "$f.patch" "$f"" >> $filev3 +- fi ++ verbose_notice " patch "$f.patch" "$f"" ++ echo "patch "$f.patch" "$f"" >> "$filev2" ++ echo "patch "$f.patch" "$f"" >> "$filev3" + } + + append_remove_instructions() { +@@ -148,19 +149,19 @@ append_remove_instructions() { + # Exclude comments + if [ ! $(echo "$f" | grep -c '^#') = 1 ]; then + if [ $(echo "$f" | grep -c '/$') = 1 ]; then +- notice " rmdir "$f"" +- echo "rmdir "$f"" >> $filev2 +- echo "rmdir "$f"" >> $filev3 ++ verbose_notice " rmdir "$f"" ++ echo "rmdir "$f"" >> "$filev2" ++ echo "rmdir "$f"" >> "$filev3" + elif [ $(echo "$f" | grep -c '/*$') = 1 ]; then + # Remove the * + f=$(echo "$f" | sed -e 's:*$::') +- notice " rmrfdir "$f"" +- echo "rmrfdir "$f"" >> $filev2 +- echo "rmrfdir "$f"" >> $filev3 ++ verbose_notice " rmrfdir "$f"" ++ echo "rmrfdir "$f"" >> "$filev2" ++ echo "rmrfdir "$f"" >> "$filev3" + else +- notice " remove "$f"" +- echo "remove "$f"" >> $filev2 +- echo "remove "$f"" >> $filev3 ++ verbose_notice " remove "$f"" ++ echo "remove "$f"" >> "$filev2" ++ echo "remove "$f"" >> "$filev3" + fi + fi + fi +@@ -170,6 +171,10 @@ append_remove_instructions() { + + # List all files in the current directory, stripping leading "./" + # Pass a variable name and it will be filled as an array. ++# To support Tor Browser updates, skip the following files: ++# TorBrowser/Data/Browser/profiles.ini ++# TorBrowser/Data/Browser/profile.default/bookmarks.html ++# TorBrowser/Data/Tor/torrc + list_files() { + count=0 + +@@ -182,6 +187,11 @@ list_files() { + | sed 's/./(.*)/\1/' \ + | sort -r > "temp-filelist" + while read file; do ++ if [ "$file" = "TorBrowser/Data/Browser/profiles.ini" -o \ ++ "$file" = "TorBrowser/Data/Browser/profile.default/bookmarks.html" -o \ ++ "$file" = "TorBrowser/Data/Tor/torrc" ]; then ++ continue; ++ fi + eval "${1}[$count]="$file"" + (( count++ )) + done < "temp-filelist" +@@ -203,3 +213,19 @@ list_dirs() { + done < "temp-dirlist" + rm "temp-dirlist" + } ++ ++# List all symbolic links in the current directory, stripping leading "./" ++list_symlinks() { ++ count=0 ++ ++ find . -type l \ ++ | sed 's/./(.*)/\1/' \ ++ | sort -r > "temp-symlinklist" ++ while read symlink; do ++ target=$(readlink "$symlink") ++ eval "${1}[$count]="$symlink"" ++ eval "${2}[$count]="$target"" ++ (( count++ )) ++ done < "temp-symlinklist" ++ rm "temp-symlinklist" ++} +diff --git a/tools/update-packaging/make_full_update.sh b/tools/update-packaging/make_full_update.sh +index f046614..f0bd7f6 100755 +--- a/tools/update-packaging/make_full_update.sh ++++ b/tools/update-packaging/make_full_update.sh +@@ -28,10 +28,16 @@ if [ $1 = -h ]; then + notice "" + notice "Options:" + notice " -h show this help text" ++ notice " -q be less verbose" + notice "" + exit 1 + fi + ++if [ $1 = -q ]; then ++ QUIET=1 ++ shift ++fi ++ + # ----------------------------------------------------------------------------- + + archive="$1" +@@ -63,17 +69,46 @@ if [ ! -f "precomplete" ]; then + fi + + list_files files ++list_symlinks symlinks symlink_targets ++ ++# TODO When TOR_BROWSER_DATA_OUTSIDE_APP_DIR is used on all platforms, ++# we should remove the following lines: ++# Make sure we delete the pre 5.1.0 HTTPS Everywhere as well in case it ++# exists. The extension ID got changed with the version bump to 5.1.0. ++ext_path='TorBrowser/Data/Browser/profile.default/extensions' ++if [ -d "$ext_dir" ]; then ++ directories_to_remove="$ext_path/https-everywhere@eff.org $ext_path/https-everywhere-eff@eff.org" ++else ++ directories_to_remove="" ++fi ++# END TOR_BROWSER_DATA_OUTSIDE_APP_DIR removal + + popd + + # Add the type of update to the beginning of the update manifests. +-> $updatemanifestv2 +-> $updatemanifestv3 ++> "$updatemanifestv2" ++> "$updatemanifestv3" + notice "" + notice "Adding type instruction to update manifests" + notice " type complete" +-echo "type "complete"" >> $updatemanifestv2 +-echo "type "complete"" >> $updatemanifestv3 ++echo "type "complete"" >> "$updatemanifestv2" ++echo "type "complete"" >> "$updatemanifestv3" ++ ++# TODO When TOR_BROWSER_DATA_OUTSIDE_APP_DIR is used on all platforms, ++# we should remove the following lines: ++# If removal of any old, existing directories is desired, emit the appropriate ++# rmrfdir commands. ++notice "" ++notice "Adding directory removal instructions to update manifests" ++for dir_to_remove in $directories_to_remove; do ++ # rmrfdir requires a trailing slash; if slash is missing, add one. ++ if ! [[ "$dir_to_remove" =~ /$ ]]; then ++ dir_to_remove="${dir_to_remove}/" ++ fi ++ echo "rmrfdir "$dir_to_remove"" >> "$updatemanifestv2" ++ echo "rmrfdir "$dir_to_remove"" >> "$updatemanifestv3" ++done ++# END TOR_BROWSER_DATA_OUTSIDE_APP_DIR removal + + notice "" + notice "Adding file add instructions to update manifests" +@@ -99,6 +134,15 @@ for ((i=0; $i<$num_files; i=$i+1)); do + targetfiles="$targetfiles "$f"" + done + ++notice "" ++notice "Adding symlink add instructions to update manifests" ++num_symlinks=${#symlinks[*]} ++for ((i=0; $i<$num_symlinks; i=$i+1)); do ++ link="${symlinks[$i]}" ++ target="${symlink_targets[$i]}" ++ make_addsymlink_instruction "$link" "$target" "$updatemanifestv2" "$updatemanifestv3" ++done ++ + # Append remove instructions for any dead files. + notice "" + notice "Adding file and directory remove instructions from file 'removed-files'" +diff --git a/tools/update-packaging/make_incremental_update.sh b/tools/update-packaging/make_incremental_update.sh +index 592b7ab..15af172 100755 +--- a/tools/update-packaging/make_incremental_update.sh ++++ b/tools/update-packaging/make_incremental_update.sh +@@ -21,6 +21,7 @@ print_usage() { + notice " -h show this help text" + notice " -f clobber this file in the installation" + notice " Must be a path to a file to clobber in the partial update." ++ notice " -q be less verbose" + notice "" + } + +@@ -61,6 +62,21 @@ check_for_forced_update() { + ## "true" *giggle* + return 0; + fi ++ ++# TODO When TOR_BROWSER_DATA_OUTSIDE_APP_DIR is used on all platforms, ++# we should remove the following lines: ++ # If the file in the skip list ends with /*, do a prefix match. ++ # This allows TorBrowser/Data/Browser/profile.default/extensions/https-everywhere-eff@eff.org/* ++ # to be used to force all HTTPS Everywhere files to be updated. ++ f_suffix=${f##*/} ++ if [[ $f_suffix = "*" ]]; then ++ f_prefix="${f%/*}"; ++ if [[ $forced_file_chk == $f_prefix* ]]; then ++ ## 0 means "true" ++ return 0; ++ fi ++ fi ++# END TOR_BROWSER_DATA_OUTSIDE_APP_DIR removal + done + ## 'false'... because this is bash. Oh yay! + return 1; +@@ -71,13 +87,18 @@ if [ $# = 0 ]; then + exit 1 + fi + +-requested_forced_updates='Contents/MacOS/firefox' ++# Firefox uses requested_forced_updates='Contents/MacOS/firefox' due to ++# 770996 but in Tor Browser we do not need that fix. ++requested_forced_updates="" ++directories_to_remove="" + +-while getopts "hf:" flag ++while getopts "hqf:" flag + do + case "$flag" in + h) print_usage; exit 0 + ;; ++ q) QUIET=1 ++ ;; + f) requested_forced_updates="$requested_forced_updates $OPTARG" + ;; + ?) print_usage; exit 1 +@@ -104,6 +125,46 @@ updatemanifestv2="$workdir/updatev2.manifest" + updatemanifestv3="$workdir/updatev3.manifest" + archivefiles="updatev2.manifest updatev3.manifest" + ++# TODO When TOR_BROWSER_DATA_OUTSIDE_APP_DIR is used on all platforms, ++# we should remove the following lines: ++# If the NoScript or HTTPS Everywhere extensions have changed between ++# releases, add them to the "force updates" list. ++ext_path='TorBrowser/Data/Browser/profile.default/extensions' ++if [ -d "$newdir/$ext_path" ]; then ++ https_everywhere='https-everywhere-eff@eff.org' ++ noscript='{73a6fe31-595d-460b-a920-fcc0f8843232}.xpi' ++ ++ # NoScript is a packed extension, so we simply compare the old and the new ++ # .xpi files. ++ noscript_path="$ext_path/$noscript" ++ diff -a "$olddir/$noscript_path" "$newdir/$noscript_path" > /dev/null ++ rc=$? ++ if [ $rc -gt 1 ]; then ++ notice "Unexpected exit $rc from $noscript_path diff command" ++ exit 2 ++ elif [ $rc -eq 1 ]; then ++ requested_forced_updates="$requested_forced_updates $noscript_path" ++ fi ++ ++ # HTTPS Everywhere is an unpacked extension, so we need to determine if any of ++ # the unpacked files have changed. Since that is messy, we simply compare the ++ # old extension's install.rdf file to the new one. ++ https_everywhere_install_rdf="$ext_path/$https_everywhere/install.rdf" ++ diff "$olddir/$https_everywhere_install_rdf" \ ++ "$newdir/$https_everywhere_install_rdf" > /dev/null ++ rc=$? ++ if [ $rc -gt 1 -a -e "$olddir/$https_everywhere_install_rdf" ]; then ++ notice "Unexpected exit $rc from $https_everywhere_install_rdf diff command" ++ exit 2 ++ elif [ $rc -ge 1 ]; then ++ requested_forced_updates="$requested_forced_updates $ext_path/$https_everywhere/*" ++ # Make sure we delete the pre 5.1.0 HTTPS Everywhere as well in case it ++ # exists. The extension ID got changed with the version bump to 5.1.0. ++ directories_to_remove="$directories_to_remove $ext_path/https-everywhere@eff.org $ext_path/$https_everywhere" ++ fi ++fi ++# END TOR_BROWSER_DATA_OUTSIDE_APP_DIR removal ++ + mkdir -p "$workdir" + + # Generate a list of all files in the target directory. +@@ -113,6 +174,7 @@ if test $? -ne 0 ; then + fi + + list_files oldfiles ++list_symlinks oldsymlinks oldsymlink_targets + list_dirs olddirs + + popd +@@ -131,17 +193,34 @@ fi + + list_dirs newdirs + list_files newfiles ++list_symlinks newsymlinks newsymlink_targets + + popd + + # Add the type of update to the beginning of the update manifests. + notice "" + notice "Adding type instruction to update manifests" +-> $updatemanifestv2 +-> $updatemanifestv3 ++> "$updatemanifestv2" ++> "$updatemanifestv3" + notice " type partial" +-echo "type "partial"" >> $updatemanifestv2 +-echo "type "partial"" >> $updatemanifestv3 ++echo "type "partial"" >> "$updatemanifestv2" ++echo "type "partial"" >> "$updatemanifestv3" ++ ++# TODO When TOR_BROWSER_DATA_OUTSIDE_APP_DIR is used on all platforms, ++# we should remove the following lines: ++# If removal of any old, existing directories is desired, emit the appropriate ++# rmrfdir commands. ++notice "" ++notice "Adding directory removal instructions to update manifests" ++for dir_to_remove in $directories_to_remove; do ++ # rmrfdir requires a trailing slash, so add one if missing. ++ if ! [[ "$dir_to_remove" =~ /$ ]]; then ++ dir_to_remove="${dir_to_remove}/" ++ fi ++ echo "rmrfdir "$dir_to_remove"" >> "$updatemanifestv2" ++ echo "rmrfdir "$dir_to_remove"" >> "$updatemanifestv3" ++done ++# END TOR_BROWSER_DATA_OUTSIDE_APP_DIR removal + + notice "" + notice "Adding file patch and add instructions to update manifests" +@@ -234,6 +313,24 @@ for ((i=0; $i<$num_oldfiles; i=$i+1)); do + fi + done + ++# Remove and re-add symlinks ++notice "" ++notice "Adding symlink remove/add instructions to update manifests" ++num_oldsymlinks=${#oldsymlinks[*]} ++for ((i=0; $i<$num_oldsymlinks; i=$i+1)); do ++ link="${oldsymlinks[$i]}" ++ verbose_notice " remove: $link" ++ echo "remove "$link"" >> "$updatemanifestv2" ++ echo "remove "$link"" >> "$updatemanifestv3" ++done ++ ++num_newsymlinks=${#newsymlinks[*]} ++for ((i=0; $i<$num_newsymlinks; i=$i+1)); do ++ link="${newsymlinks[$i]}" ++ target="${newsymlink_targets[$i]}" ++ make_addsymlink_instruction "$link" "$target" "$updatemanifestv2" "$updatemanifestv3" ++done ++ + # Newly added files + notice "" + notice "Adding file add instructions to update manifests" +@@ -270,8 +367,8 @@ notice "Adding file remove instructions to update manifests" + for ((i=0; $i<$num_removes; i=$i+1)); do + f="${remove_array[$i]}" + notice " remove "$f"" +- echo "remove "$f"" >> $updatemanifestv2 +- echo "remove "$f"" >> $updatemanifestv3 ++ echo "remove "$f"" >> "$updatemanifestv2" ++ echo "remove "$f"" >> "$updatemanifestv3" + done + + # Add remove instructions for any dead files. +@@ -288,8 +385,8 @@ for ((i=0; $i<$num_olddirs; i=$i+1)); do + # If this dir doesn't exist in the new directory remove it. + if [ ! -d "$newdir/$f" ]; then + notice " rmdir $f/" +- echo "rmdir "$f/"" >> $updatemanifestv2 +- echo "rmdir "$f/"" >> $updatemanifestv3 ++ echo "rmdir "$f/"" >> "$updatemanifestv2" ++ echo "rmdir "$f/"" >> "$updatemanifestv3" + fi + done + +-- +cgit v0.10.2 +
tor-commits@lists.torproject.org