[tor-commits] [tor-browser/tor-browser-24.7.0esr-4.x-2] Bug 4234: Use the Firefox Update Process for TBB.

mikeperry at torproject.org mikeperry at torproject.org
Thu Aug 28 06:45:30 UTC 2014


commit 43c5ec54ce50cd08ccc27b02ef2b3ebbef9a02aa
Author: Kathy Brade <brade at pearlcrescent.com>
Date:   Thu Aug 14 11:39:09 2014 -0400

    Bug 4234: Use the Firefox Update Process for TBB.
    
    New configure options:
      --with-tor-browser-version=VERSION   # Pass TBB 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
---
 .mozconfig                                         |    5 +-
 .mozconfig-mac                                     |    6 +-
 .mozconfig-mingw                                   |    5 +-
 accessible/src/Makefile.in                         |    2 +-
 accessible/src/shared.mozbuild                     |    2 +-
 browser/app/profile/000-tor-browser.js             |    8 +-
 browser/app/profile/firefox.js                     |   11 +-
 browser/base/content/aboutDialog.js                |   45 +++-
 browser/branding/official/pref/firefox-branding.js |    4 +-
 browser/confvars.sh                                |   19 +-
 browser/installer/Makefile.in                      |    4 +
 browser/installer/removed-files.in                 |    4 +-
 config/createprecomplete.py                        |   14 +-
 configure.in                                       |   28 ++-
 js/src/configure.in                                |    3 -
 toolkit/modules/debug.js                           |    1 +
 toolkit/mozapps/extensions/Makefile.in             |    2 +-
 toolkit/mozapps/update/nsUpdateService.js          |   68 +++++-
 toolkit/mozapps/update/updater/updater.cpp         |  245 +++++++++++++++++---
 toolkit/xre/nsAppRunner.cpp                        |   10 +-
 toolkit/xre/nsUpdateDriver.cpp                     |   39 ++++
 tools/update-packaging/common.sh                   |   62 ++++-
 tools/update-packaging/make_full_update.sh         |   40 +++-
 tools/update-packaging/make_incremental_update.sh  |   62 ++++-
 24 files changed, 566 insertions(+), 123 deletions(-)

diff --git a/.mozconfig b/.mozconfig
index e9a9432..97e0349 100755
--- a/.mozconfig
+++ b/.mozconfig
@@ -9,12 +9,15 @@ mk_add_options BUILD_OFFICIAL=1
 ac_add_options --enable-optimize
 #ac_add_options --disable-optimize
 ac_add_options --enable-official-branding
+ac_add_options --enable-tor-browser-update
+ac_add_options --enable-update-packaging
+# We do not use signed MAR files yet (Mozilla uses them on Windows only).
+ac_add_options --disable-verify-mar
 ac_add_options --disable-strip
 ac_add_options --disable-install-strip
 ac_add_options --disable-tests
 ac_add_options --disable-debug
 ac_add_options --disable-maintenance-service
-ac_add_options --disable-updater
 ac_add_options --disable-crashreporter
 ac_add_options --disable-webrtc
 #ac_add_options --disable-ctypes
diff --git a/.mozconfig-mac b/.mozconfig-mac
index 25a3f7d..7f28096 100644
--- a/.mozconfig-mac
+++ b/.mozconfig-mac
@@ -38,7 +38,11 @@ ac_add_options --disable-debug
 
 # See above for a reason why this is currently disabled
 # ac_add_options --with-macos-private-frameworks=$CROSS_PRIVATE_FRAMEWORKS
-ac_add_options --disable-updater
+#ac_add_options --disable-updater
+ac_add_options --enable-tor-browser-update
+ac_add_options --enable-update-packaging
+# We do not use signed MAR files yet (Mozilla uses them on Windows only).
+ac_add_options --disable-verify-mar
 ac_add_options --disable-crashreporter
 ac_add_options --disable-maintenance-service
 ac_add_options --disable-webrtc
diff --git a/.mozconfig-mingw b/.mozconfig-mingw
index 78f9a06..9c8695d 100644
--- a/.mozconfig-mingw
+++ b/.mozconfig-mingw
@@ -14,8 +14,11 @@ ac_add_options --disable-debug
 ac_add_options --enable-optimize
 ac_add_options --enable-strip
 ac_add_options --enable-official-branding
+ac_add_options --enable-tor-browser-update
+ac_add_options --enable-update-packaging
+# We do not use signed MAR files yet (Mozilla uses them on Windows only).
+ac_add_options --disable-verify-mar
 
-ac_add_options --disable-updater
 ac_add_options --disable-crashreporter
 ac_add_options --disable-maintenance-service
 ac_add_options --disable-webrtc
diff --git a/accessible/src/Makefile.in b/accessible/src/Makefile.in
index aafeb3b..762a89c 100644
--- a/accessible/src/Makefile.in
+++ b/accessible/src/Makefile.in
@@ -13,7 +13,7 @@ A11Y_LOG = 0
 ifdef MOZ_DEBUG
   A11Y_LOG = 1
 endif
-ifeq (,$(filter aurora beta release esr,$(MOZ_UPDATE_CHANNEL)))
+ifeq (,$(filter aurora alpha beta release esr,$(MOZ_UPDATE_CHANNEL)))
   A11Y_LOG = 1
 endif
 export A11Y_LOG
diff --git a/accessible/src/shared.mozbuild b/accessible/src/shared.mozbuild
index 839ef82..52e9804 100644
--- a/accessible/src/shared.mozbuild
+++ b/accessible/src/shared.mozbuild
@@ -8,5 +8,5 @@ a11y_log = 0
 if CONFIG['MOZ_DEBUG']:
     a11y_log = 1
 
-if CONFIG['MOZ_UPDATE_CHANNEL'] not in ('aurora', 'beta', 'release', 'esr'):
+if CONFIG['MOZ_UPDATE_CHANNEL'] not in ('aurora', 'alpha', 'beta', 'release', 'esr'):
     a11y_log = 1
diff --git a/browser/app/profile/000-tor-browser.js b/browser/app/profile/000-tor-browser.js
index a6ff777..ef2a2a3 100644
--- a/browser/app/profile/000-tor-browser.js
+++ b/browser/app/profile/000-tor-browser.js
@@ -2,9 +2,8 @@
 # Tor Browser Bundle
 # Do not edit this file.
 
-// Disable browser auto updaters and associated homepage notifications
+// Disable browser automatic updates and associated homepage notifications
 pref("app.update.auto", false);
-pref("app.update.enabled", false);
 pref("browser.search.update", false);
 pref("browser.rights.3.shown", true);
 pref("browser.startup.homepage_override.mstone", "ignore");
@@ -156,5 +155,6 @@ pref("media.audio_data.enabled", false);
 // https://trac.torproject.org/projects/tor/ticket/11253
 pref("security.tls.version.max", 3);
 
-// Version placeholder
-pref("torbrowser.version", "UNKNOWN");
+#ifdef TOR_BROWSER_VERSION
+#expand pref("torbrowser.version", __TOR_BROWSER_VERSION__);
+#endif
diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js
index ae78798..3e3a77c 100644
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -121,11 +121,8 @@ pref("app.update.cert.maxErrors", 5);
 // when the |app.update.cert.checkAttributes| preference is set to false. Also,
 // the |app.update.url.override| preference should ONLY be used for testing.
 // IMPORTANT! metro.js should also be updated for updates to certs.X.issuerName
-pref("app.update.certs.1.issuerName", "CN=Thawte SSL CA,O=\"Thawte, Inc.\",C=US");
-pref("app.update.certs.1.commonName", "aus3.mozilla.org");
-
-pref("app.update.certs.2.issuerName", "CN=DigiCert Secure Server CA,O=DigiCert Inc,C=US");
-pref("app.update.certs.2.commonName", "aus3.mozilla.org");
+pref("app.update.certs.1.issuerName", "CN=DigiCert SHA2 High Assurance Server CA,OU=www.digicert.com,O=DigiCert Inc,C=US");
+pref("app.update.certs.1.commonName", "*.torproject.org");
 
 // Whether or not app updates are enabled
 pref("app.update.enabled", true);
@@ -155,7 +152,7 @@ pref("app.update.silent", false);
 pref("app.update.staging.enabled", true);
 
 // Update service URL:
-pref("app.update.url", "https://aus3.mozilla.org/update/3/%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml");
+pref("app.update.url", "https://www.torproject.org/dist/torbrowser/update/%CHANNEL%/%BUILD_TARGET%/%OS_VERSION%/%VERSION%/%LOCALE%");
 // app.update.url.manual is in branding section
 // app.update.url.details is in branding section
 
@@ -173,7 +170,7 @@ pref("app.update.idletime", 60);
 // upgrade start page instead! Other apps may wish to show this UI, and supply
 // a whatsNewURL field in their brand.properties that contains a link to a page
 // which tells users what's new in this new update.
-pref("app.update.showInstalledUI", false);
+pref("app.update.showInstalledUI", true);
 
 // 0 = suppress prompting for incompatibilities if there are updates available
 //     to newer versions of installed addons that resolve them.
diff --git a/browser/base/content/aboutDialog.js b/browser/base/content/aboutDialog.js
index ea8350c..c72c828 100644
--- a/browser/base/content/aboutDialog.js
+++ b/browser/base/content/aboutDialog.js
@@ -7,6 +7,11 @@ Components.utils.import("resource://gre/modules/Services.jsm");
 
 const PREF_EM_HOTFIX_ID = "extensions.hotfix.id";
 
+#ifdef TOR_BROWSER_VERSION
+# Add double-quotes back on (stripped by JarMaker.py).
+#expand const TOR_BROWSER_VERSION = "__TOR_BROWSER_VERSION__";
+#endif
+
 function init(aEvent)
 {
   if (aEvent.target != document)
@@ -37,13 +42,16 @@ function init(aEvent)
   // Include the build ID and display warning if this is an "a#" (nightly or aurora) build
   let version = Services.appinfo.version;
   if (/a\d+$/.test(version)) {
-    let buildID = Services.appinfo.appBuildID;
-    let buildDate = buildID.slice(0,4) + "-" + buildID.slice(4,6) + "-" + buildID.slice(6,8);
-    document.getElementById("version").textContent += " (" + buildDate + ")";
     document.getElementById("experimental").hidden = false;
     document.getElementById("communityDesc").hidden = true;
   }
 
+#ifdef TOR_BROWSER_VERSION
+  let versionElem = document.getElementById("version");
+  if (versionElem)
+    versionElem.textContent += " (TBB " + TOR_BROWSER_VERSION + ")";
+#endif
+
 #ifdef MOZ_UPDATER
   gAppUpdater = new appUpdater();
 
@@ -328,9 +336,15 @@ appUpdater.prototype =
         return;
       }
 
+     // If the update has the same version as the current application,
+     // skip add-on compatibility check and start downloading now.
+#ifdef TOR_BROWSER_UPDATE
+      var pkgVersion = TOR_BROWSER_VERSION;
+#else
+      var pkgVersion = Services.appinfo.version;
+#endif
       if (!gAppUpdater.update.appVersion ||
-          Services.vc.compare(gAppUpdater.update.appVersion,
-                              Services.appinfo.version) == 0) {
+          Services.vc.compare(gAppUpdater.update.appVersion, pkgVersion) == 0) {
         gAppUpdater.startDownload();
         return;
       }
@@ -396,11 +410,16 @@ appUpdater.prototype =
         // (see bug 566787). The hotfix add-on is also ignored as it shouldn't
         // block the user from upgrading.
         try {
+#ifdef TOR_BROWSER_UPDATE
+          let compatVersion = self.update.platformVersion;
+#else
+          let compatVersion = self.update.appVersion;
+#endif
           if (aAddon.type != "plugin" && aAddon.id != hotfixID &&
               !aAddon.appDisabled && !aAddon.userDisabled &&
               aAddon.scope != AddonManager.SCOPE_APPLICATION &&
               aAddon.isCompatible &&
-              !aAddon.isCompatibleWith(self.update.appVersion,
+              !aAddon.isCompatibleWith(compatVersion,
                                        self.update.platformVersion))
             self.addons.push(aAddon);
         }
@@ -424,8 +443,13 @@ appUpdater.prototype =
    */
   checkAddonsForUpdates: function() {
     this.addons.forEach(function(aAddon) {
+#ifdef TOR_BROWSER_UPDATE
+      let compatVersion = self.update.platformVersion;
+#else
+      let compatVersion = self.update.appVersion;
+#endif
       aAddon.findUpdates(this, AddonManager.UPDATE_WHEN_NEW_APP_DETECTED,
-                         this.update.appVersion,
+                         compatVersion,
                          this.update.platformVersion);
     }, this);
   },
@@ -446,8 +470,13 @@ appUpdater.prototype =
    * See XPIProvider.jsm
    */
   onUpdateAvailable: function(aAddon, aInstall) {
+#ifdef TOR_BROWSER_UPDATE
+    let compatVersion = self.update.platformVersion;
+#else
+    let compatVersion = self.update.appVersion;
+#endif
     if (!Services.blocklist.isAddonBlocklisted(aAddon.id, aInstall.version,
-                                               this.update.appVersion,
+                                               compatVersion,
                                                this.update.platformVersion)) {
       // Compatibility or new version updates mean the same thing here.
       this.onCompatibilityUpdateAvailable(aAddon);
diff --git a/browser/branding/official/pref/firefox-branding.js b/browser/branding/official/pref/firefox-branding.js
index 0201301..6a15345 100644
--- a/browser/branding/official/pref/firefox-branding.js
+++ b/browser/branding/official/pref/firefox-branding.js
@@ -13,10 +13,10 @@ pref("app.update.download.backgroundInterval", 60);
 pref("app.update.promptWaitTime", 172800);
 // URL user can browse to manually if for some reason all update installation
 // attempts fail.
-pref("app.update.url.manual", "https://www.mozilla.org/firefox/");
+pref("app.update.url.manual", "https://www.torproject.org/download/download-easy.html");
 // A default value for the "More information about this update" link
 // supplied in the "An update is available" page of the update wizard. 
-pref("app.update.url.details", "https://www.mozilla.org/%LOCALE%/firefox/notes");
+pref("app.update.url.details", "https://www.torproject.org/projects/torbrowser.html");
 
 pref("browser.search.param.ms-pc", "MOZI");
 pref("browser.search.param.yahoo-fr", "moz35");
diff --git a/browser/confvars.sh b/browser/confvars.sh
index 5bcb399..5432d51 100644
--- a/browser/confvars.sh
+++ b/browser/confvars.sh
@@ -8,21 +8,6 @@ MOZ_APP_VENDOR=Mozilla
 MOZ_UPDATER=1
 MOZ_PHOENIX=1
 
-if test "$OS_ARCH" = "WINNT"; then
-  if ! test "$HAVE_64BIT_OS"; then
-    MOZ_VERIFY_MAR_SIGNATURE=1
-    MOZ_MAINTENANCE_SERVICE=1
-    if test "$MOZ_UPDATE_CHANNEL" = "nightly" -o \
-            "$MOZ_UPDATE_CHANNEL" = "aurora" -o \
-            "$MOZ_UPDATE_CHANNEL" = "beta" -o \
-            "$MOZ_UPDATE_CHANNEL" = "release"; then
-      if ! test "$MOZ_DEBUG"; then
-        MOZ_STUB_INSTALLER=1
-      fi
-    fi
-  fi
-fi
-
 MOZ_CHROME_FILE_FORMAT=omni
 MOZ_SAFE_BROWSING=1
 MOZ_SERVICES_COMMON=1
@@ -46,9 +31,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_EXTENSION_MANAGER=1
 MOZ_APP_STATIC_INI=1
diff --git a/browser/installer/Makefile.in b/browser/installer/Makefile.in
index 4e3ab6f..b0700db 100644
--- a/browser/installer/Makefile.in
+++ b/browser/installer/Makefile.in
@@ -76,6 +76,10 @@ ifdef WIN32_REDIST_DIR
 DEFINES += -DMOZ_MSVC_REDIST=$(_MSC_VER)
 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/browser/installer/removed-files.in b/browser/installer/removed-files.in
index 6801f63..f2554ea 100644
--- a/browser/installer/removed-files.in
+++ b/browser/installer/removed-files.in
@@ -1434,7 +1434,9 @@ xpicleanup at BIN_SUFFIX@
   #endif
   #if MOZ_MSVC_REDIST != 1600
     msvcp100.dll
-    msvcr100.dll
+    #ifndef TOR_BROWSER_UPDATE
+      msvcr100.dll
+    #endif
   #endif
   #if MOZ_MSVC_REDIST != 1700
     msvcp110.dll
diff --git a/config/createprecomplete.py b/config/createprecomplete.py
index 7fe8109..8332b21 100644
--- a/config/createprecomplete.py
+++ b/config/createprecomplete.py
@@ -5,6 +5,7 @@
 # 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
@@ -12,7 +13,10 @@ import os
 def get_build_entries(root_path):
     """ Iterates through the root_path, creating a list for each file and
         directory. Excludes any path starting with extensions or distribution
-        and paths ending with channel-prefs.js.
+        and paths ending with channel-prefs.js. To support Tor Browser updates,
+        excludes TorBrowser/Data/Browser/profiles.ini,
+        TorBrowser/Data/Browser/profile.default/bookmarks.html and
+        TorBrowser/Data/Tor/torrc.
     """
     rel_file_path_set = set()
     rel_dir_path_set = set()
@@ -23,6 +27,9 @@ def get_build_entries(root_path):
             rel_path_file = rel_path_file.replace("\\", "/")
             if not (rel_path_file.startswith("distribution/") or
                     rel_path_file.startswith("extensions/") 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.endswith("channel-prefs.js")):
                 rel_file_path_set.add(rel_path_file)
 
@@ -32,7 +39,10 @@ def get_build_entries(root_path):
             rel_path_dir = rel_path_dir.replace("\\", "/")+"/"
             if not (rel_path_dir.startswith("distribution/") or
                     rel_path_dir.startswith("extensions/")):
-                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 7de51e7..243aaee 100644
--- a/configure.in
+++ b/configure.in
@@ -3849,15 +3849,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)
@@ -6408,6 +6405,31 @@ MOZ_ARG_ENABLE_BOOL(update-packaging,
 AC_SUBST(MOZ_UPDATE_PACKAGING)
 
 dnl ========================================================
+dnl Tor Additions
+dnl ========================================================
+MOZ_ARG_WITH_STRING(tor-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,
diff --git a/js/src/configure.in b/js/src/configure.in
index 22ef8e2..e00c032 100644
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -3074,15 +3074,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/debug.js b/toolkit/modules/debug.js
index 822ae90..4c20b82 100644
--- a/toolkit/modules/debug.js
+++ b/toolkit/modules/debug.js
@@ -40,6 +40,7 @@ this.NS_ASSERT = function NS_ASSERT(condition, message) {
   try {
     switch (defB.getCharPref("app.update.channel")) {
       case "nightly":
+      case "alpha":
       case "beta":
       case "default":
         releaseBuild = false;
diff --git a/toolkit/mozapps/extensions/Makefile.in b/toolkit/mozapps/extensions/Makefile.in
index ad9cc62..c90698d 100644
--- a/toolkit/mozapps/extensions/Makefile.in
+++ b/toolkit/mozapps/extensions/Makefile.in
@@ -9,7 +9,7 @@ VPATH     = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
-ifeq (,$(filter aurora beta release esr,$(MOZ_UPDATE_CHANNEL)))
+ifeq (,$(filter aurora alpha beta release esr,$(MOZ_UPDATE_CHANNEL)))
 DEFINES += -DMOZ_COMPATIBILITY_NIGHTLY=1
 endif
 
diff --git a/toolkit/mozapps/update/nsUpdateService.js b/toolkit/mozapps/update/nsUpdateService.js
index e7b6fa8..eb4a74a 100644
--- a/toolkit/mozapps/update/nsUpdateService.js
+++ b/toolkit/mozapps/update/nsUpdateService.js
@@ -11,7 +11,11 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/FileUtils.jsm");
 Components.utils.import("resource://gre/modules/AddonManager.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
+#ifdef BUILD_CTYPES
+#ifdef XP_WIN
 Components.utils.import("resource://gre/modules/ctypes.jsm");
+#endif
+#endif
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
@@ -73,6 +77,10 @@ const KEY_GRED            = "GreD";
 const KEY_UPDROOT         = "UpdRootD";
 const KEY_EXECUTABLE      = "XREExeF";
 
+#ifdef TOR_BROWSER_VERSION
+#expand const TOR_BROWSER_VERSION = __TOR_BROWSER_VERSION__;
+#endif
+
 #ifdef MOZ_WIDGET_GONK
 #define USE_UPDATE_ARCHIVE_DIR
 #endif
@@ -253,6 +261,7 @@ XPCOMUtils.defineLazyGetter(this, "gOSVersion", function aus_gOSVersion() {
   }
 
   if (osVersion) {
+#ifdef BUILD_CTYPES
 #ifdef XP_WIN
     const BYTE = ctypes.uint8_t;
     const WORD = ctypes.uint16_t;
@@ -359,6 +368,7 @@ XPCOMUtils.defineLazyGetter(this, "gOSVersion", function aus_gOSVersion() {
       }
     }
 #endif
+#endif
 
     try {
       osVersion += " (" + sysInfo.getProperty("secondaryLibrary") + ")";
@@ -1224,7 +1234,7 @@ function getLocale() {
   if (gLocale)
     return gLocale;
 
-  for each (res in ['app', 'gre']) {
+  for each (var res in ['app', 'gre']) {
     var channel = Services.io.newChannel("resource://" + res + "/" + FILE_UPDATE_LOCALE, null, null);
     try {
       var inputStream = channel.open();
@@ -2390,9 +2400,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");
@@ -2548,8 +2563,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();
     }
@@ -2577,6 +2597,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
@@ -2605,7 +2630,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);
         }
@@ -2641,7 +2666,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 {
@@ -2677,8 +2702,13 @@ UpdateService.prototype = {
     // the add-on will become incompatible.
     let bs = Cc["@mozilla.org/extensions/blocklist;1"].
              getService(Ci.nsIBlocklistService);
+#ifdef TOR_BROWSER_UPDATE
+    let compatVersion = gUpdates.update.platformVersion;
+#else
+    let compatVersion = gUpdates.update.appVersion;
+#endif
     if (bs.isAddonBlocklisted(addon.id, install.version,
-                              gUpdates.update.appVersion,
+                              compatVersion,
                               gUpdates.update.platformVersion))
       return;
 
@@ -2781,14 +2811,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();
@@ -2821,7 +2861,7 @@ UpdateService.prototype = {
     }
 #endif
     // 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);
   },
@@ -3233,7 +3273,11 @@ Checker.prototype = {
     }
 
     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 + "_" + gABI);
     url = url.replace(/%OS_VERSION%/g, gOSVersion);
diff --git a/toolkit/mozapps/update/updater/updater.cpp b/toolkit/mozapps/update/updater/updater.cpp
index 4012f00..7a5220d 100644
--- a/toolkit/mozapps/update/updater/updater.cpp
+++ b/toolkit/mozapps/update/updater/updater.cpp
@@ -20,7 +20,7 @@
  *  updatev2.manifest
  *  -----------------
  *  method   = "add" | "add-if" | "patch" | "patch-if" | "remove" |
- *             "rmdir" | "rmrfdir" | type
+ *             "rmdir" | "rmrfdir" | "addsymlink" | type
  *
  *  'type' is the update type (e.g. complete or partial) and when present MUST
  *  be the first entry in the update manifest. The type is used to support
@@ -404,10 +404,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 NULL 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) {
@@ -442,10 +444,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("..")) != NULL) {
-    LOG(("get_valid_path: paths must not contain '..': " LOG_S, path));
-    return NULL;
+  if (!islinktarget) {
+    // Don't allow relative paths that resolve to a parent directory.
+    if (NS_tstrstr(path, NS_T("..")) != NULL) {
+      LOG(("get_valid_path: paths must not contain '..': " LOG_S, path));
+      return NULL;
+    }
   }
 
   return path;
@@ -480,7 +484,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 (!NS_tlstat(path, &fs) && !S_ISLNK(fs.st_mode)
+      && !(fs.st_mode & S_IWUSR)) {
     (void)chmod(path, fs.st_mode | S_IWUSR);
   }
 #endif
@@ -680,7 +685,7 @@ static int ensure_copy(const NS_tchar *path, const NS_tchar *dest)
     return READ_ERROR;
   }
 
-#ifdef XP_UNIX
+#ifndef XP_WIN
   if (S_ISLNK(ss.st_mode)) {
     return ensure_copy_symlink(path, dest);
   }
@@ -779,7 +784,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);
   }
@@ -838,14 +843,19 @@ static int rename_file(const NS_tchar *spath, const NS_tchar *dpath,
     return rv;
 
   struct stat spathInfo;
-  rv = NS_tstat(spath, &spathInfo);
+  rv = NS_tlstat(spath, &spathInfo);
   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));
@@ -855,7 +865,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));
@@ -892,7 +907,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 = NS_tlstat(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;
   }
@@ -907,8 +933,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 = NS_tlstat(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;
   }
 
@@ -988,7 +1024,7 @@ private:
 class RemoveFile : public Action
 {
 public:
-  RemoveFile() : mFile(NULL), mSkip(0) { }
+  RemoveFile() : mFile(NULL), mSkip(0), mIsLink(0) { }
 
   int Parse(NS_tchar *line);
   int Prepare();
@@ -998,6 +1034,7 @@ public:
 private:
   const NS_tchar *mFile;
   int mSkip;
+  int mIsLink;
 };
 
 int
@@ -1015,28 +1052,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 = NS_tlstat(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 stat 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 stat 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 UNEXPECTED_FILE_OPERATION_ERROR;
+    if (!S_ISREG(fileInfo.st_mode)) {
+      LOG(("path present, but not a file: " LOG_S, mFile));
+      return UNEXPECTED_FILE_OPERATION_ERROR;
+    }
   }
 
   NS_tchar *slash = (NS_tchar *) NS_tstrrchr(mFile, NS_T('/'));
@@ -1066,7 +1114,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 stat 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;
@@ -1683,6 +1737,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 = NS_tlstat(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
@@ -1902,19 +2047,40 @@ CopyInstallDirToDestDir()
 
   // These files should not be copied over to the updated app
 #ifdef XP_WIN
-#define SKIPLIST_COUNT 3
+  #ifdef TOR_BROWSER_UPDATE
+    #define SKIPLIST_COUNT 5
+  #else
+    #define SKIPLIST_COUNT 3
+  #endif
 #else
-#define SKIPLIST_COUNT 2
+  #ifdef TOR_BROWSER_UPDATE
+    #define SKIPLIST_COUNT 4
+  #else
+    #define SKIPLIST_COUNT 2
+  #endif
 #endif
   copy_recursive_skiplist<SKIPLIST_COUNT> skiplist;
 #ifdef XP_MACOSX
   skiplist.append(0, installDir, NS_T("Updated.app"));
   skiplist.append(1, installDir, NS_T("Contents/MacOS/updates/0"));
+#ifdef TOR_BROWSER_UPDATE
+  skiplist.append(2, installDir, NS_T("TorBrowser/Data/Browser/profile.default/.parentlock"));
+  skiplist.append(3, installDir, NS_T("TorBrowser/Data/Tor/lock"));
+#endif
 #else
   skiplist.append(0, installDir, NS_T("updated"));
   skiplist.append(1, installDir, NS_T("updates/0"));
+#ifdef TOR_BROWSER_UPDATE
+#ifdef XP_UNIX
+  skiplist.append(2, installDir, NS_T("TorBrowser/Data/Browser/profile.default/.parentlock"));
+#else
+  skiplist.append(2, installDir, NS_T("TorBrowser/Data/Browser/profile.default/parent.lock"));
+#endif
+  skiplist.append(3, installDir, NS_T("TorBrowser/Data/Tor/lock"));
+#endif
 #ifdef XP_WIN
-  skiplist.append(2, installDir, NS_T("updated.update_in_progress.lock"));
+  skiplist.append(SKIPLIST_COUNT - 1, installDir,
+                  NS_T("updated.update_in_progress.lock"));
 #endif
 #endif
 
@@ -3124,7 +3290,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) {
       // The service update will only be executed if it is already installed.
       // For first time installs of the service, the install will happen from
@@ -3747,6 +3913,11 @@ int DoUpdate()
     else if (NS_tstrcmp(token, NS_T("add-cc")) == 0) { // no longer supported
       continue;
     }
+#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 42787c0..c94839f 100644
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -3499,12 +3499,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_PROCESS_UPDATES")) {
     SaveToEnv("MOZ_PROCESS_UPDATES=");
     *aExitFlag = true;
diff --git a/toolkit/xre/nsUpdateDriver.cpp b/toolkit/xre/nsUpdateDriver.cpp
index 5223e8cc8..a4e4034 100644
--- a/toolkit/xre/nsUpdateDriver.cpp
+++ b/toolkit/xre/nsUpdateDriver.cpp
@@ -139,6 +139,18 @@ GetCurrentWorkingDir(char *buf, size_t size)
   return NS_OK;
 }
 
+
+#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
@@ -294,6 +306,10 @@ 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;
 
@@ -874,6 +890,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 for background updates.
@@ -897,6 +916,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);
@@ -938,6 +960,12 @@ ProcessUpdates(nsIFile *greDir, nsIFile *appDir, nsIFile *updRootDir,
   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;
@@ -985,6 +1013,11 @@ ProcessUpdates(nsIFile *greDir, nsIFile *appDir, nsIFile *updRootDir,
 
   nsCOMPtr<nsIFile> statusFile;
   UpdateStatus status = GetUpdateStatus(updatesDir, statusFile);
+#ifdef DEBUG
+  printf("ProcessUpdates status: %d\n", status);
+  updatesDir->GetNativePath(path);
+  printf("ProcessUpdates updatesDir: %s\n", path.get());
+#endif
   switch (status) {
   case ePendingUpdate:
   case ePendingService: {
@@ -1064,7 +1097,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 {
@@ -1088,6 +1125,8 @@ nsUpdateProcessor::ProcessUpdate(nsIUpdate* aUpdate)
     if (NS_FAILED(rv))
       updRoot = appDir;
 
+    // To support Tor Browser Bundle updates from xpcshell, modify the
+    // following code to use the TBB version fron the configure process.
     nsCOMPtr<nsIXULAppInfo> appInfo =
       do_GetService("@mozilla.org/xre/app-info;1");
     if (appInfo) {
diff --git a/tools/update-packaging/common.sh b/tools/update-packaging/common.sh
index c358846..075ab08 100755
--- a/tools/update-packaging/common.sh
+++ b/tools/update-packaging/common.sh
@@ -9,6 +9,8 @@
 #
 
 # -----------------------------------------------------------------------------
+QUIET=0
+
 # By default just assume that these tools exist on our path
 MAR=${MAR:-mar}
 BZIP2=${BZIP2:-bzip2}
@@ -21,6 +23,12 @@ notice() {
   echo "$*" 1>&2
 }
 
+verbose_notice() {
+  if [ $QUIET -eq 0 ]; then
+    notice "$*"
+  fi
+}
+
 get_file_size() {
   info=($(ls -ln "$1"))
   echo ${info[4]}
@@ -52,14 +60,22 @@ make_add_instruction() {
     # 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: $f$forced"
+    verbose_notice "     add-if: $f$forced"
     echo "add-if \"$testdir\" \"$f\""
   else
-    notice "        add: $f$forced"
+    verbose_notice "        add: $f$forced"
     echo "add \"$f\""
   fi
 }
 
+make_addsymlink_instruction() {
+  link="$1"
+  target="$2"
+
+  verbose_notice "        addsymlink: $link -> $target"
+  echo "addsymlink \"$link\" \"$target\""
+}
+
 make_patch_instruction() {
   f="$1"
   is_extension=$(echo "$f" | grep -c 'distribution/extensions/.*/')
@@ -67,10 +83,10 @@ make_patch_instruction() {
     # 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: $f"
+    verbose_notice "   patch-if: $f"
     echo "patch-if \"$testdir\" \"$f.patch\" \"$f\""
   else
-    notice "      patch: $f"
+    verbose_notice "      patch: $f"
     echo "patch \"$f.patch\" \"$f\""
   fi
 }
@@ -113,17 +129,17 @@ append_remove_instructions() {
             fi
           fi
           if [ $(echo "$f" | grep -c '\/$') = 1 ]; then
-            notice "      rmdir: $fixedprefix$f"
-            echo "rmdir \"$fixedprefix$f\"" >> $filev2
+            verbose_notice "      rmdir: $fixedprefix$f"
+            echo "rmdir \"$fixedprefix$f\"" >> "$filev2"
           elif [ $(echo "$f" | grep -c '\/\*$') = 1 ]; then
             # Remove the *
             f=$(echo "$f" | sed -e 's:\*$::')
-            notice "    rmrfdir: $fixedprefix$f"
-            echo "rmrfdir \"$fixedprefix$f\"" >> $filev2
+            verbose_notice "    rmrfdir: $fixedprefix$f"
+            echo "rmrfdir \"$fixedprefix$f\"" >> "$filev2"
           else
-            notice "     remove: $fixedprefix$f"
-            echo "remove \"$fixedprefix$f\"" >> $filev1
-            echo "remove \"$fixedprefix$f\"" >> $filev2
+            verbose_notice "     remove: $fixedprefix$f"
+            echo "remove \"$fixedprefix$f\"" >> "$filev1"
+            echo "remove \"$fixedprefix$f\"" >> "$filev2"
           fi
         fi
       fi
@@ -132,6 +148,9 @@ append_remove_instructions() {
 }
 
 # List all files in the current directory, stripping leading "./"
+# To support Tor Browser updates, skip TorBrowser/Data/Browser/profiles.ini,
+# TorBrowser/Data/Browser/profile.default/bookmarks.html and
+# TorBrowser/Data/Tor/torrc.
 # Skip the channel-prefs.js file as it should not be included in any
 # generated MAR files (see bug 306077). Pass a variable name and it will be
 # filled as an array.
@@ -147,6 +166,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"
@@ -168,3 +192,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 03ba9af..b1ba54e 100755
--- a/tools/update-packaging/make_full_update.sh
+++ b/tools/update-packaging/make_full_update.sh
@@ -10,6 +10,9 @@
 
 . $(dirname "$0")/common.sh
 
+# TODO: it would be better to pass this as a command line option.
+directories_to_remove='TorBrowser/Data/Browser/profile.default/extensions/https-everywhere at eff.org'
+
 # -----------------------------------------------------------------------------
 
 print_usage() {
@@ -28,10 +31,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"
@@ -64,19 +73,20 @@ if [ ! -f "precomplete" ]; then
 fi
 
 list_files files
+list_symlinks symlinks symlink_targets
 
 popd
 
 notice ""
 notice "Adding file add instructions to file 'update.manifest'"
-> $updatemanifestv1
+> "$updatemanifestv1"
 
 num_files=${#files[*]}
 
 for ((i=0; $i<$num_files; i=$i+1)); do
   f="${files[$i]}"
 
-  make_add_instruction "$f" >> $updatemanifestv1
+  make_add_instruction "$f" >> "$updatemanifestv1"
 
   dir=$(dirname "$f")
   mkdir -p "$workdir/$dir"
@@ -88,15 +98,35 @@ done
 
 # Add the type of update to the beginning of and cat the contents of the version
 # 1 update manifest to the version 2 update manifest.
-> $updatemanifestv2
+> "$updatemanifestv2"
 notice ""
 notice "Adding type instruction to file 'updatev2.manifest'"
 notice "       type: complete"
-echo "type \"complete\"" >> $updatemanifestv2
+echo "type \"complete\"" >> "$updatemanifestv2"
+
+# If removal of any old, existing directories is desired, emit the appropriate
+# rmdfdir commands.
+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"
+done
+
 
 notice ""
 notice "Concatenating file 'update.manifest' to file 'updatev2.manifest'"
-cat $updatemanifestv1 >> $updatemanifestv2
+cat "$updatemanifestv1" >> "$updatemanifestv2"
+
+notice ""
+notice "Adding symlink add instructions to file 'updatev2.manifest'"
+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"
+done
 
 # Append remove instructions for any dead files.
 notice ""
diff --git a/tools/update-packaging/make_incremental_update.sh b/tools/update-packaging/make_incremental_update.sh
index 0363831..b58e7dd 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 ""
 }
 
@@ -47,6 +48,17 @@ check_for_forced_update() {
       ## "true" *giggle*
       return 0;
     fi
+
+    # If the file in the skip list ends with /*, do a prefix match.
+    # This allows TorBrowser/Data/Browser/profile.default/extensions/https-everywhere at 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
   done
   ## 'false'... because this is bash. Oh yay!
   return 1;
@@ -57,13 +69,26 @@ if [ $# = 0 ]; then
   exit 1
 fi
 
-requested_forced_updates='Contents/MacOS/firefox'
+# The last .xpi is NoScript.
+ext_path='TorBrowser/Data/Browser/profile.default/extensions';
+# TODO: it would be better to pass this as a command line option.
+exts='https-everywhere at eff.org/* tor-launcher at torproject.org.xpi torbutton at torproject.org.xpi uriloader at pdf.js.xpi {73a6fe31-595d-460b-a920-fcc0f8843232}.xpi'
+requested_forced_updates='Contents/MacOS/TorBrowser.app/Contents/MacOS/firefox'
+for ext in $exts; do
+  requested_forced_updates="$requested_forced_updates $ext_path/$ext"
+done
+
+
+# TODO: it would be better to pass this as a command line option.
+directories_to_remove='TorBrowser/Data/Browser/profile.default/extensions/https-everywhere at eff.org'
 
-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
@@ -102,6 +127,7 @@ if test $? -ne 0 ; then
 fi
 
 list_files oldfiles
+list_symlinks oldsymlinks oldsymlink_targets
 list_dirs olddirs
 
 popd
@@ -118,6 +144,7 @@ fi
 
 list_dirs newdirs
 list_files newfiles
+list_symlinks newsymlinks newsymlink_targets
 
 popd
 
@@ -139,7 +166,7 @@ for ((i=0; $i<$num_oldfiles; i=$i+1)); do
 
   # removed-files is excluded by make_incremental_updates.py so it is excluded
   # here for consistency.
-  if [ `basename $f` = "removed-files" ]; then
+  if [ "`basename "$f"`" = "removed-files" ]; then
     continue 1
   fi
 
@@ -188,6 +215,23 @@ 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 file 'update.manifest'"
+num_oldsymlinks=${#oldsymlinks[*]}
+for ((i=0; $i<$num_oldsymlinks; i=$i+1)); do
+  link="${oldsymlinks[$i]}"
+  verbose_notice "        remove: $link"
+  echo "remove \"$link\"" >> $updatemanifestv1
+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" >> $updatemanifestv1
+done
+
 # Newly added files
 notice ""
 notice "Adding file add instructions to file 'update.manifest'"
@@ -198,7 +242,7 @@ for ((i=0; $i<$num_newfiles; i=$i+1)); do
 
   # removed-files is excluded by make_incremental_updates.py so it is excluded
   # here for consistency.
-  if [ `basename $f` = "removed-files" ]; then
+  if [ "`basename "$f"`" = "removed-files" ]; then
     continue 1
   fi
 
@@ -235,6 +279,16 @@ notice "Adding type instruction to file 'updatev2.manifest'"
 notice "       type: partial"
 echo "type \"partial\"" >> $updatemanifestv2
 
+# If removal of any old, existing directories is desired, emit the appropriate
+# rmdfdir commands.
+for dir_to_remove in $directories_to_remove; do
+  # rmrfdir reuires 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
+done
+
 notice ""
 notice "Concatenating file 'update.manifest' to file 'updatev2.manifest'"
 cat $updatemanifestv1 >> $updatemanifestv2





More information about the tor-commits mailing list