[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [tor-messenger-build/master] Update patch for #9173 and add patch for #4234 (Firefox update)
commit 770a2829ba7a84bc0ed910b6ce503d152221f684
Author: Sukhbir Singh <sukhbir@xxxxxxxxxxxxxx>
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@xxxxxxxxxxxxxxxxx>
+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@xxxxxxxxxxxxxxxxx>
+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@xxxxxxx $ext_path/https-everywhere-eff@xxxxxxx"
++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@xxxxxxx/*
++ # 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@xxxxxxx'
++ 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@xxxxxxx $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 mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits