diff --git a/browser/app/Makefile.in b/browser/app/Makefile.in
index 54d6b43fe126f0ad61a2d967ea8258a3578dc6b7..5e66dede6b2ea019ce88e99c8b899df6617947d6 100644
--- a/browser/app/Makefile.in
+++ b/browser/app/Makefile.in
@@ -97,10 +97,12 @@ tools repackage:: $(DIST)/bin/$(MOZ_APP_NAME) $(objdir)/macbuild/Contents/MacOS-
 	rsync -aL $(DIST)/bin/$(MOZ_APP_NAME) '$(dist_dest)/Contents/MacOS'
 	cp -RL $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/firefox.icns '$(dist_dest)/Contents/Resources/firefox.icns'
 	cp -RL $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/document.icns '$(dist_dest)/Contents/Resources/document.icns'
+ifndef BASE_BROWSER_UPDATE
 	$(MKDIR) -p '$(dist_dest)/Contents/Library/LaunchServices'
 ifdef MOZ_UPDATER
 	mv -f '$(dist_dest)/Contents/MacOS/updater.app/Contents/MacOS/org.mozilla.updater' '$(dist_dest)/Contents/Library/LaunchServices'
 	ln -s ../../../../Library/LaunchServices/org.mozilla.updater '$(dist_dest)/Contents/MacOS/updater.app/Contents/MacOS/org.mozilla.updater'
+endif
 endif
 	printf APPLMOZB > '$(dist_dest)/Contents/PkgInfo'
 endif
diff --git a/browser/base/content/aboutDialog.js b/browser/base/content/aboutDialog.js
index fc0252ad1bd1c59a6f7c92dbf8a7df59f5998937..209d9f3874950f8a3c3d99ce3819d99b08ca7000 100644
--- a/browser/base/content/aboutDialog.js
+++ b/browser/base/content/aboutDialog.js
@@ -43,10 +43,10 @@ function init() {
   }
 
   // Include the build ID and display warning if this is an "a#" (nightly or aurora) build
-  let versionId = "aboutDialog-version";
+  let versionId = "basebrowser-about-dialog-version";
   let versionAttributes = {
-    version: AppConstants.MOZ_APP_VERSION_DISPLAY,
-    bits: Services.appinfo.is64Bit ? 64 : 32,
+    version: AppConstants.BASE_BROWSER_VERSION,
+    firefoxVersion: AppConstants.MOZ_APP_VERSION_DISPLAY,
   };
 
   let version = Services.appinfo.version;
diff --git a/browser/base/content/aboutDialog.xhtml b/browser/base/content/aboutDialog.xhtml
index 0824af6462593b52950c044c36b52fd41484001c..69a85886336cd11ff922ce66c4978993475e2512 100644
--- a/browser/base/content/aboutDialog.xhtml
+++ b/browser/base/content/aboutDialog.xhtml
@@ -30,6 +30,7 @@
   <linkset>
     <html:link rel="localization" href="branding/brand.ftl"/>
     <html:link rel="localization" href="browser/aboutDialog.ftl"/>
+    <html:link rel="localization" href="browser/base-browser.ftl"/>
   </linkset>
 
   <html:div id="aboutDialogContainer">
diff --git a/browser/base/moz.build b/browser/base/moz.build
index 8c6d23deb08f55dbcfafab48c1d8b40481c9a28d..b2552f809e142591fc313fccd1ff8717969dfd19 100644
--- a/browser/base/moz.build
+++ b/browser/base/moz.build
@@ -88,4 +88,7 @@ if CONFIG["OS_ARCH"] == "WINNT" and CONFIG["MOZ_DEFAULT_BROWSER_AGENT"]:
     # Impacts `/toolkit/content/license.html`.
     DEFINES["MOZ_DEFAULT_BROWSER_AGENT"] = True
 
+if CONFIG["BASE_BROWSER_UPDATE"]:
+    DEFINES["BASE_BROWSER_UPDATE"] = True
+
 JAR_MANIFESTS += ["jar.mn"]
diff --git a/browser/components/BrowserContentHandler.sys.mjs b/browser/components/BrowserContentHandler.sys.mjs
index 32ebdd4dcba5e11857084c20ab05b85e9eafd414..e8923e5c0089d291b95fa09eacf89f61b982642f 100644
--- a/browser/components/BrowserContentHandler.sys.mjs
+++ b/browser/components/BrowserContentHandler.sys.mjs
@@ -42,6 +42,9 @@ XPCOMUtils.defineLazyGetter(lazy, "gWindowsAlertsService", () => {
     ?.QueryInterface(Ci.nsIWindowsAlertsService);
 });
 
+const FORK_VERSION_PREF =
+  "browser.startup.homepage_override.basebrowser.version";
+
 // One-time startup homepage override configurations
 const ONCE_DOMAINS = ["mozilla.org", "firefox.com"];
 const ONCE_PREF = "browser.startup.homepage_override.once";
@@ -105,7 +108,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 fork 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.
@@ -122,6 +126,8 @@ function needHomepageOverride(prefb) {
 
   var mstone = Services.appinfo.platformVersion;
 
+  var savedForkVersion = prefb.getCharPref(FORK_VERSION_PREF, null);
+
   var savedBuildID = prefb.getCharPref(
     "browser.startup.homepage_override.buildID",
     ""
@@ -143,9 +149,16 @@ function needHomepageOverride(prefb) {
 
     prefb.setCharPref("browser.startup.homepage_override.mstone", mstone);
     prefb.setCharPref("browser.startup.homepage_override.buildID", buildID);
+    prefb.setCharPref(FORK_VERSION_PREF, AppConstants.BASE_BROWSER_VERSION);
     return savedmstone ? OVERRIDE_NEW_MSTONE : OVERRIDE_NEW_PROFILE;
   }
 
+  if (AppConstants.BASE_BROWSER_VERSION != savedForkVersion) {
+    prefb.setCharPref("browser.startup.homepage_override.buildID", buildID);
+    prefb.setCharPref(FORK_VERSION_PREF, AppConstants.BASE_BROWSER_VERSION);
+    return OVERRIDE_NEW_MSTONE;
+  }
+
   if (buildID != savedBuildID) {
     prefb.setCharPref("browser.startup.homepage_override.buildID", buildID);
     return OVERRIDE_NEW_BUILD_ID;
@@ -671,6 +684,10 @@ nsBrowserContentHandler.prototype = {
         "browser.startup.homepage_override.buildID",
         "unknown"
       );
+
+      // We do the same for the fork version.
+      let old_forkVersion = prefb.getCharPref(FORK_VERSION_PREF, null);
+
       override = needHomepageOverride(prefb);
       if (override != OVERRIDE_NONE) {
         switch (override) {
@@ -702,9 +719,10 @@ nsBrowserContentHandler.prototype = {
               "startup.homepage_override_url"
             );
             let update = lazy.UpdateManager.readyUpdate;
+            let old_version = old_forkVersion ? old_forkVersion : old_mstone;
             if (
               update &&
-              Services.vc.compare(update.appVersion, old_mstone) > 0
+              Services.vc.compare(update.appVersion, old_version) > 0
             ) {
               overridePage = getPostUpdateOverridePage(update, overridePage);
               // Send the update ping to signal that the update was successful.
@@ -712,6 +730,10 @@ nsBrowserContentHandler.prototype = {
             }
 
             overridePage = overridePage.replace("%OLD_VERSION%", old_mstone);
+            overridePage = overridePage.replace(
+              "%OLD_BASE_BROWSER_VERSION%",
+              old_forkVersion
+            );
             break;
           case OVERRIDE_NEW_BUILD_ID:
             if (lazy.UpdateManager.readyUpdate) {
diff --git a/browser/components/customizableui/content/panelUI.inc.xhtml b/browser/components/customizableui/content/panelUI.inc.xhtml
index 4e93102bcfb220e112da44e80fbfa21098c0d848..8776e6c004c95bd5929ea18310790b807a47bcf8 100644
--- a/browser/components/customizableui/content/panelUI.inc.xhtml
+++ b/browser/components/customizableui/content/panelUI.inc.xhtml
@@ -200,7 +200,7 @@
                        hasicon="true"
                        hidden="true">
       <popupnotificationcontent id="update-restart-notification-content" orient="vertical">
-        <description id="update-restart-description" data-lazy-l10n-id="appmenu-update-restart-message2"></description>
+        <description id="update-restart-description">&#160;</description>
       </popupnotificationcontent>
     </popupnotification>
 
diff --git a/browser/components/preferences/main.js b/browser/components/preferences/main.js
index 385254820ec38566d3c7f8b1d54e2358632431d3..273a0559563c0a11ed9502e8dde39aedf2ebcf1e 100644
--- a/browser/components/preferences/main.js
+++ b/browser/components/preferences/main.js
@@ -549,7 +549,7 @@ var gMainPane = {
         .setAttribute("style", "display: none !important");
     }
     // Initialize the Firefox Updates section.
-    let version = AppConstants.MOZ_APP_VERSION_DISPLAY;
+    let version = AppConstants.BASE_BROWSER_VERSION;
 
     // Include the build ID if this is an "a#" (nightly) build
     if (/a\d+$/.test(version)) {
diff --git a/browser/config/mozconfigs/base-browser b/browser/config/mozconfigs/base-browser
index 18838046558c75c2795363b975ae6caf09707b6e..37ed0949772a42de83715bdb4e584a6288ddef94 100644
--- a/browser/config/mozconfigs/base-browser
+++ b/browser/config/mozconfigs/base-browser
@@ -15,6 +15,7 @@ ac_add_options --enable-optimize
 ac_add_options --enable-rust-simd
 
 ac_add_options --disable-unverified-updates
+ac_add_options --enable-base-browser-update
 
 ac_add_options --enable-bundled-fonts
 
diff --git a/browser/confvars.sh b/browser/confvars.sh
index 5143e486caf865c91bf7bcb6c236c9aef68fd7ec..b4546aeac15a402cfaf1a4702045a1360c0dac48 100755
--- a/browser/confvars.sh
+++ b/browser/confvars.sh
@@ -20,5 +20,20 @@ MOZ_APP_ID={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
 # tor-browser#41577: Do not enable profile migration
 # MOZ_PROFILE_MIGRATOR=1
 
+# ACCEPTED_MAR_CHANNEL_IDS 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.
+# The MAR_CHANNEL_ID must not contain the following 3 characters: ",\t "
+if test "$MOZ_UPDATE_CHANNEL" = "alpha"; then
+  ACCEPTED_MAR_CHANNEL_IDS=basebrowser-torproject-alpha
+  MAR_CHANNEL_ID=basebrowser-torproject-alpha
+elif test "$MOZ_UPDATE_CHANNEL" = "nightly"; then
+  ACCEPTED_MAR_CHANNEL_IDS=basebrowser-torproject-nightly
+  MAR_CHANNEL_ID=basebrowser-torproject-nightly
+else
+  ACCEPTED_MAR_CHANNEL_IDS=basebrowser-torproject-release
+  MAR_CHANNEL_ID=basebrowser-torproject-release
+fi
+
 # Include the DevTools client, not just the server (which is the default)
 MOZ_DEVTOOLS=all
diff --git a/browser/installer/Makefile.in b/browser/installer/Makefile.in
index fc6d4808dca0c1b1cf8428325dc81ac886f8a6a3..b5b5e8ca29fdf7866638c73d8343b219d72fb071 100644
--- a/browser/installer/Makefile.in
+++ b/browser/installer/Makefile.in
@@ -85,6 +85,10 @@ endif
 endif
 endif
 
+ifdef BASE_BROWSER_UPDATE
+DEFINES += -DBASE_BROWSER_UPDATE
+endif
+
 ifneq (,$(filter WINNT Darwin Android,$(OS_TARGET)))
 DEFINES += -DMOZ_SHARED_MOZGLUE=1
 endif
diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in
index 84fa4257f7aa64fcc54c453b58d4c41be5791f49..c69f4eb1a96ebee8071c45f8a6412e91112cba92 100644
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -36,8 +36,10 @@
 ; Mac bundle stuff
 @APPNAME@/Contents/Info.plist
 #ifdef MOZ_UPDATER
+#ifndef BASE_BROWSER_UPDATE
 @APPNAME@/Contents/Library/LaunchServices
 #endif
+#endif
 @APPNAME@/Contents/PkgInfo
 @RESPATH@/firefox.icns
 @RESPATH@/document.icns
diff --git a/build/application.ini.in b/build/application.ini.in
index 6df13230a45b2a86356f4e5a7b189c46f53e44cc..aa02201e619ff78286fa2d4f16f1ae3766daa495 100644
--- a/build/application.ini.in
+++ b/build/application.ini.in
@@ -52,5 +52,5 @@ ServerURL=@MOZ_CRASHREPORTER_URL@/submit?id=@MOZ_APP_ID@&version=@MOZ_APP_VERSIO
 
 #if MOZ_UPDATER
 [AppUpdate]
-URL=https://@MOZ_APPUPDATE_HOST@/update/6/%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%SYSTEM_CAPABILITIES%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml
+URL=@BB_UPDATER_URL@/%CHANNEL%/%BUILD_TARGET%/%VERSION%/ALL
 #endif
diff --git a/build/moz.build b/build/moz.build
index 671e77e97b351148a871022769e41798db3781eb..5662e29cae0da0224a79d0b4e67524eb172cb690 100644
--- a/build/moz.build
+++ b/build/moz.build
@@ -75,6 +75,7 @@ if CONFIG["MOZ_APP_BASENAME"]:
         "MAR_CHANNEL_ID",
         "MOZ_APP_REMOTINGNAME",
         "MOZ_CRASHREPORTER_URL",
+        "BB_UPDATER_URL",
     ):
         appini_defines[var] = CONFIG[var]
 
diff --git a/build/moz.configure/init.configure b/build/moz.configure/init.configure
index 53bbc4203fcc8297d6e994eba8f5d361cb83a3d3..3042fe0a7a678f977f7ae3158c5ade3a17876ac4 100644
--- a/build/moz.configure/init.configure
+++ b/build/moz.configure/init.configure
@@ -1051,11 +1051,10 @@ option(
 )
 
 
-# set RELEASE_OR_BETA 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_OR_BETA)
+# Firefox looks for "a" or "a1" in the milestone to detect whether nightly
+# features should be enabled. We do not want them, because we want our nightly
+# builds to be as close as possible to actual releases.
+# So we set always is_release_or_beta to True.
 @depends(
     build_environment,
     build_project,
@@ -1116,10 +1115,8 @@ def milestone(build_env, build_project, version_path, as_milestone, _):
 
     is_nightly = is_release_or_beta = None
 
-    if "a1" in milestone:
-        is_nightly = True
-    elif "a" not in milestone:
-        is_release_or_beta = True
+    # Do not enable extra nightly features
+    is_release_or_beta = True
 
     major_version = milestone.split(".")[0]
     m = re.search(r"([ab]\d+)", milestone)
diff --git a/build/moz.configure/update-programs.configure b/build/moz.configure/update-programs.configure
index bcdd457ad3397a74ff5a1c3ed3b0ca4fa07d9bea..213a95550861d7f809f3604721086fc2378687d3 100644
--- a/build/moz.configure/update-programs.configure
+++ b/build/moz.configure/update-programs.configure
@@ -192,3 +192,31 @@ option(
 )
 
 set_config("MOZ_NOTIFICATION_SERVER", True, when="--enable-notification-server")
+
+
+# Enable updater customization for Base Browser-based browsers
+# ==============================================================================
+
+option(
+    "--enable-base-browser-update",
+    help="Enable Base Browser update"
+)
+
+set_config("BASE_BROWSER_UPDATE", True, when="--enable-base-browser-update")
+set_define("BASE_BROWSER_UPDATE", True, when="--enable-base-browser-update")
+
+
+# Updater URL
+# ==============================================================
+
+option(
+    "--with-updater-url",
+    default="https://aus1.torproject.org/torbrowser/update_3/",
+    nargs=1,
+    help="Set the updater URL",
+)
+
+set_config(
+    "BB_UPDATER_URL",
+    depends("--with-updater-url")(lambda x: x[0].rstrip("/")),
+)
diff --git a/mozconfig-linux-x86_64-dev b/mozconfig-linux-x86_64-dev
index e9389eca5ef89288e5b1f059c7fe99167137338c..7d5d4d767a21f6b38834982fed9f646843036cf0 100644
--- a/mozconfig-linux-x86_64-dev
+++ b/mozconfig-linux-x86_64-dev
@@ -12,3 +12,4 @@ ac_add_options --disable-strip
 ac_add_options --disable-install-strip
 
 ac_add_options --with-base-browser-version=dev-build
+ac_add_options --disable-base-browser-update
diff --git a/toolkit/modules/AppConstants.sys.mjs b/toolkit/modules/AppConstants.sys.mjs
index 0d042cce879c434ab737c0b18e4c4c2ab0786b56..abf490f990be3ec84442f63f93abe4f7142effbb 100644
--- a/toolkit/modules/AppConstants.sys.mjs
+++ b/toolkit/modules/AppConstants.sys.mjs
@@ -466,6 +466,13 @@ export var AppConstants = Object.freeze({
     false,
 #endif
 
+  BASE_BROWSER_UPDATE:
+#ifdef BASE_BROWSER_UPDATE
+    true,
+#else
+    false,
+#endif
+
   // Returns true for CN region build when distibution id set as 'MozillaOnline'
   isChinaRepack() {
     return (
diff --git a/toolkit/modules/UpdateUtils.sys.mjs b/toolkit/modules/UpdateUtils.sys.mjs
index 3a86099aa81d21bb97b89937c0c7cb2c63e36dce..a4be264a91ab3e3973044cb69bbf60c72e496015 100644
--- a/toolkit/modules/UpdateUtils.sys.mjs
+++ b/toolkit/modules/UpdateUtils.sys.mjs
@@ -82,7 +82,7 @@ export var UpdateUtils = {
           case "PRODUCT":
             return Services.appinfo.name;
           case "VERSION":
-            return Services.appinfo.version;
+            return AppConstants.BASE_BROWSER_VERSION;
           case "BUILD_ID":
             return Services.appinfo.appBuildID;
           case "BUILD_TARGET":
@@ -161,7 +161,8 @@ export var UpdateUtils = {
    * downloads and installs updates. This corresponds to whether or not the user
    * has selected "Automatically install updates" in about:preferences.
    *
-   * On Windows, this setting is shared across all profiles for the installation
+   * On Windows (except in Base Browser and derivatives), this setting is shared
+   * across all profiles for the installation
    * and is read asynchronously from the file. On other operating systems, this
    * setting is stored in a pref and is thus a per-profile setting.
    *
@@ -177,7 +178,8 @@ export var UpdateUtils = {
    * updates" and "Check for updates but let you choose to install them" options
    * in about:preferences.
    *
-   * On Windows, this setting is shared across all profiles for the installation
+   * On Windows (except in Base Browser and derivatives), this setting is shared
+   * across all profiles for the installation
    * and is written asynchronously to the file. On other operating systems, this
    * setting is stored in a pref and is thus a per-profile setting.
    *
@@ -249,7 +251,10 @@ export var UpdateUtils = {
     // setting is just to propagate it from a pref observer. This ensures that
     // the expected observers still get notified, even if a user manually
     // changes the pref value.
-    if (!UpdateUtils.PER_INSTALLATION_PREFS_SUPPORTED) {
+    if (
+      AppConstants.BASE_BROWSER_UPDATE ||
+      !UpdateUtils.PER_INSTALLATION_PREFS_SUPPORTED
+    ) {
       let initialConfig = {};
       for (const [prefName, pref] of Object.entries(
         UpdateUtils.PER_INSTALLATION_PREFS
@@ -318,7 +323,10 @@ export var UpdateUtils = {
       }
     }
 
-    if (!this.PER_INSTALLATION_PREFS_SUPPORTED) {
+    if (
+      AppConstants.BASE_BROWSER_UPDATE ||
+      !this.PER_INSTALLATION_PREFS_SUPPORTED
+    ) {
       // If we don't have per-installation prefs, we use regular preferences.
       let prefValue = prefTypeFns.getProfilePref(prefName, pref.defaultValue);
       return Promise.resolve(prefValue);
@@ -413,7 +421,10 @@ export var UpdateUtils = {
       );
     }
 
-    if (!this.PER_INSTALLATION_PREFS_SUPPORTED) {
+    if (
+      AppConstants.BASE_BROWSER_UPDATE ||
+      !this.PER_INSTALLATION_PREFS_SUPPORTED
+    ) {
       // If we don't have per-installation prefs, we use regular preferences.
       if (options.setDefaultOnly) {
         prefTypeFns.setProfileDefaultPref(prefName, value);
@@ -549,14 +560,6 @@ UpdateUtils.PER_INSTALLATION_PREFS = {
     migrate: true,
     observerTopic: "auto-update-config-change",
     policyFn: () => {
-      if (!Services.policies.isAllowed("app-auto-updates-off")) {
-        // We aren't allowed to turn off auto-update - it is forced on.
-        return true;
-      }
-      if (!Services.policies.isAllowed("app-auto-updates-on")) {
-        // We aren't allowed to turn on auto-update - it is forced off.
-        return false;
-      }
       return null;
     },
   },
diff --git a/toolkit/modules/moz.build b/toolkit/modules/moz.build
index 858bfc466ef610b18de4bb9408f00d42312cc0f2..a3a8f3bafca412c770e4659bac19c6047efca91a 100644
--- a/toolkit/modules/moz.build
+++ b/toolkit/modules/moz.build
@@ -297,6 +297,9 @@ for var in (
     if CONFIG[var]:
         DEFINES[var] = True
 
+if CONFIG["BASE_BROWSER_UPDATE"]:
+    DEFINES["BASE_BROWSER_UPDATE"] = True
+
 JAR_MANIFESTS += ["jar.mn"]
 
 DEFINES["TOPOBJDIR"] = TOPOBJDIR
diff --git a/toolkit/mozapps/extensions/AddonManager.sys.mjs b/toolkit/mozapps/extensions/AddonManager.sys.mjs
index 2ae57f0b5201a7df11ae26cd5a156970fafe0409..82e8429750b86762a8b6ec4959fdc49e74851b90 100644
--- a/toolkit/mozapps/extensions/AddonManager.sys.mjs
+++ b/toolkit/mozapps/extensions/AddonManager.sys.mjs
@@ -36,6 +36,7 @@ const PREF_EM_STRICT_COMPATIBILITY = "extensions.strictCompatibility";
 const PREF_EM_CHECK_UPDATE_SECURITY = "extensions.checkUpdateSecurity";
 const PREF_SYS_ADDON_UPDATE_ENABLED = "extensions.systemAddon.update.enabled";
 const PREF_REMOTESETTINGS_DISABLED = "extensions.remoteSettings.disabled";
+const PREF_EM_LAST_FORK_VERSION = "extensions.lastBaseBrowserVersion";
 
 const PREF_MIN_WEBEXT_PLATFORM_VERSION =
   "extensions.webExtensionsMinPlatformVersion";
@@ -638,6 +639,24 @@ var AddonManagerInternal = {
         );
       }
 
+      // To ensure that extension and plugin code gets a chance to run after
+      // each browser update, set appChanged = true when BASE_BROWSER_VERSION
+      // has changed even if the Mozilla app version has not changed.
+      const forkChanged =
+        AppConstants.BASE_BROWSER_VERSION !==
+        Services.prefs.getCharPref(PREF_EM_LAST_FORK_VERSION, "");
+      if (forkChanged) {
+        // appChanged could be undefined (in case of a new profile)
+        if (appChanged === false) {
+          appChanged = true;
+        }
+
+        Services.prefs.setCharPref(
+          PREF_EM_LAST_FORK_VERSION,
+          AppConstants.BASE_BROWSER_VERSION
+        );
+      }
+
       if (!MOZ_COMPATIBILITY_NIGHTLY) {
         PREF_EM_CHECK_COMPATIBILITY =
           PREF_EM_CHECK_COMPATIBILITY_BASE +
diff --git a/toolkit/mozapps/update/UpdateService.sys.mjs b/toolkit/mozapps/update/UpdateService.sys.mjs
index 5e9caf090e39359aa10b1412ca1a0987bbacd030..945146eade0c4a7752947d00655bf813dc52576e 100644
--- a/toolkit/mozapps/update/UpdateService.sys.mjs
+++ b/toolkit/mozapps/update/UpdateService.sys.mjs
@@ -704,6 +704,11 @@ function areDirectoryEntriesWriteable(aDir) {
  * @return true if elevation is required, false otherwise
  */
 function getElevationRequired() {
+  if (AppConstants.BASE_BROWSER_UPDATE) {
+    // To avoid potential security holes associated with running the updater
+    // process with elevated privileges, Tor Browser does not support elevation.
+    return false;
+  }
   if (AppConstants.platform != "macosx") {
     return false;
   }
@@ -783,7 +788,7 @@ function getCanApplyUpdates() {
     return false;
   }
 
-  if (AppConstants.platform == "macosx") {
+  if (!AppConstants.BASE_BROWSER_UPDATE && AppConstants.platform == "macosx") {
     LOG(
       "getCanApplyUpdates - bypass the write since elevation can be used " +
         "on Mac OS X"
@@ -791,7 +796,7 @@ function getCanApplyUpdates() {
     return true;
   }
 
-  if (shouldUseService()) {
+  if (!AppConstants.BASE_BROWSER_UPDATE && shouldUseService()) {
     LOG(
       "getCanApplyUpdates - bypass the write checks because the Windows " +
         "Maintenance Service can be used"
@@ -1646,7 +1651,11 @@ function handleUpdateFailure(update) {
     );
     cancelations++;
     Services.prefs.setIntPref(PREF_APP_UPDATE_CANCELATIONS, cancelations);
-    if (AppConstants.platform == "macosx") {
+    if (AppConstants.platform == "macosx" && AppConstants.BASE_BROWSER_UPDATE) {
+      cleanupActiveUpdates();
+      update.statusText =
+        lazy.gUpdateBundle.GetStringFromName("elevationFailure");
+    } else if (AppConstants.platform == "macosx") {
       let osxCancelations = Services.prefs.getIntPref(
         PREF_APP_UPDATE_CANCELATIONS_OSX,
         0
@@ -1918,6 +1927,15 @@ function updateIsAtLeastAsOldAs(update, version, buildID) {
   );
 }
 
+/**
+ * This returns the current version of the browser to use to check updates.
+ */
+function getCompatVersion() {
+  return AppConstants.BASE_BROWSER_VERSION
+    ? AppConstants.BASE_BROWSER_VERSION
+    : Services.appinfo.version;
+}
+
 /**
  * This returns true if the passed update is the same version or older than
  * currently installed Firefox version.
@@ -1925,7 +1943,7 @@ function updateIsAtLeastAsOldAs(update, version, buildID) {
 function updateIsAtLeastAsOldAsCurrentVersion(update) {
   return updateIsAtLeastAsOldAs(
     update,
-    Services.appinfo.version,
+    getCompatVersion(),
     Services.appinfo.appBuildID
   );
 }
@@ -2261,6 +2279,7 @@ UpdatePatch.prototype = {
  * @throws if the update contains no patches
  * @constructor
  */
+// eslint-disable-next-line complexity
 function Update(update) {
   this._patches = [];
   this._properties = {};
@@ -2296,7 +2315,31 @@ function Update(update) {
     this._patches.push(patch);
   }
 
-  if (!this._patches.length && !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.unsupported && update.hasAttribute("minSupportedInstructionSet")) {
+    let minInstructionSet = update.getAttribute("minSupportedInstructionSet");
+    if (
+      ["MMX", "SSE", "SSE2", "SSE3", "SSE4A", "SSE4_1", "SSE4_2"].includes(
+        minInstructionSet
+      )
+    ) {
+      try {
+        this.unsupported = !Services.sysinfo.getProperty(
+          "has" + minInstructionSet
+        );
+      } catch (e) {}
+    }
+  }
+
+  if (!this._patches.length && !this.unsupported) {
     throw Components.Exception("", Cr.NS_ERROR_ILLEGAL_VALUE);
   }
 
@@ -2334,9 +2377,7 @@ function Update(update) {
       if (!isNaN(attr.value)) {
         this.promptWaitTime = parseInt(attr.value);
       }
-    } else if (attr.name == "unsupported") {
-      this.unsupported = attr.value == "true";
-    } else {
+    } else if (attr.name != "unsupported") {
       switch (attr.name) {
         case "appVersion":
         case "buildID":
@@ -2361,7 +2402,7 @@ function Update(update) {
   }
 
   if (!this.previousAppVersion) {
-    this.previousAppVersion = Services.appinfo.version;
+    this.previousAppVersion = getCompatVersion();
   }
 
   if (!this.elevationFailure) {
@@ -4074,7 +4115,7 @@ UpdateService.prototype = {
         "UpdateService:downloadUpdate - Skipping download of update since " +
           "it is for an earlier or same application version and build ID.\n" +
           "current application version: " +
-          Services.appinfo.version +
+          getCompatVersion() +
           "\n" +
           "update application version : " +
           update.appVersion +
@@ -4998,7 +5039,7 @@ export class CheckerService {
   }
 
   #getCanMigrate() {
-    if (AppConstants.platform != "win") {
+    if (AppConstants.platform != "win" || AppConstants.BASE_BROWSER_UPDATE) {
       return false;
     }
 
diff --git a/toolkit/mozapps/update/UpdateServiceStub.sys.mjs b/toolkit/mozapps/update/UpdateServiceStub.sys.mjs
index ae2d5b3f996188b8f7d6430a8869860dc7bddb07..958e5d46f4c33d39c610bcf70a4346429363cfa1 100644
--- a/toolkit/mozapps/update/UpdateServiceStub.sys.mjs
+++ b/toolkit/mozapps/update/UpdateServiceStub.sys.mjs
@@ -76,8 +76,13 @@ export function UpdateServiceStub() {
   // contains the status file's path
 
   // We may need to migrate update data
+  // In Base Browser and derivatives, we skip this because we do not use an
+  // update agent and we do not want to store any data outside of the browser
+  // installation directory.
+  // For more info, see https://bugzilla.mozilla.org/show_bug.cgi?id=1458314
   if (
     AppConstants.platform == "win" &&
+    !AppConstants.BASE_BROWSER_UPDATE &&
     !Services.prefs.getBoolPref(prefUpdateDirMigrated, false)
   ) {
     Services.prefs.setBoolPref(prefUpdateDirMigrated, true);
diff --git a/toolkit/mozapps/update/common/updatehelper.cpp b/toolkit/mozapps/update/common/updatehelper.cpp
index b094d9eb75e9d8e27e9e540b613531c87ddc944c..0f4f4976a52e94e473cd62565efced3241619394 100644
--- a/toolkit/mozapps/update/common/updatehelper.cpp
+++ b/toolkit/mozapps/update/common/updatehelper.cpp
@@ -66,6 +66,13 @@ BOOL PathGetSiblingFilePath(LPWSTR destinationBuffer, LPCWSTR siblingFilePath,
  * @return TRUE if successful
  */
 BOOL GetSecureOutputDirectoryPath(LPWSTR outBuf) {
+#ifdef BASE_BROWSER_UPDATE
+  // This function is used to support the maintenance service and elevated
+  // updates and is therefore not called by Base Browser's updater. We stub
+  // it out to avoid any chance that the Base Browser updater will create
+  // files under C:\Program Files (x86)\ or a similar location.
+  return FALSE;
+#else
   PWSTR progFilesX86;
   if (FAILED(SHGetKnownFolderPath(FOLDERID_ProgramFilesX86, KF_FLAG_CREATE,
                                   nullptr, &progFilesX86))) {
@@ -99,6 +106,7 @@ BOOL GetSecureOutputDirectoryPath(LPWSTR outBuf) {
   }
 
   return TRUE;
+#endif
 }
 
 /**
diff --git a/toolkit/mozapps/update/updater/launchchild_osx.mm b/toolkit/mozapps/update/updater/launchchild_osx.mm
index 3d28a064c72ebe437bc8c0ac4d5319a21bc36674..c1e2d067a4475ad8c5e5f014601ab68a0fd06757 100644
--- a/toolkit/mozapps/update/updater/launchchild_osx.mm
+++ b/toolkit/mozapps/update/updater/launchchild_osx.mm
@@ -372,6 +372,7 @@ bool ObtainUpdaterArguments(int* argc, char*** argv) {
 
 @end
 
+#ifndef BASE_BROWSER_UPDATE
 bool ServeElevatedUpdate(int argc, const char** argv) {
   MacAutoreleasePool pool;
 
@@ -387,6 +388,7 @@ bool ServeElevatedUpdate(int argc, const char** argv) {
   [updater release];
   return didSucceed;
 }
+#endif
 
 bool IsOwnedByGroupAdmin(const char* aAppBundle) {
   MacAutoreleasePool pool;
diff --git a/toolkit/mozapps/update/updater/updater.cpp b/toolkit/mozapps/update/updater/updater.cpp
index 446f1578eeb208d5b3d439232bfdce0c5bbd9b36..898b34e8b87dc198f3332225aaee106a3c9ea153 100644
--- a/toolkit/mozapps/update/updater/updater.cpp
+++ b/toolkit/mozapps/update/updater/updater.cpp
@@ -76,7 +76,9 @@ bool IsRecursivelyWritable(const char* aPath);
 void LaunchChild(int argc, const char** argv);
 void LaunchMacPostProcess(const char* aAppBundle);
 bool ObtainUpdaterArguments(int* argc, char*** argv);
+#  ifndef BASE_BROWSER_UPDATE
 bool ServeElevatedUpdate(int argc, const char** argv);
+#  endif
 void SetGroupOwnershipAndPermissions(const char* aAppBundle);
 bool PerformInstallationFromDMG(int argc, char** argv);
 struct UpdateServerThreadArgs {
@@ -852,6 +854,11 @@ static int ensure_copy_recursive(const NS_tchar* path, const NS_tchar* dest,
   if (S_ISLNK(sInfo.st_mode)) {
     return ensure_copy_symlink(path, dest);
   }
+
+  // Ignore Unix domain sockets. See #20691.
+  if (S_ISSOCK(sInfo.st_mode)) {
+    return 0;
+  }
 #endif
 
   if (!S_ISDIR(sInfo.st_mode)) {
@@ -947,7 +954,7 @@ static int rename_file(const NS_tchar* spath, const NS_tchar* dpath,
   return OK;
 }
 
-#ifdef XP_WIN
+#if defined(XP_WIN) && !defined(BASE_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
@@ -1080,6 +1087,8 @@ static int backup_discard(const NS_tchar* path, const NS_tchar* relPath) {
            relBackup, relPath));
       return WRITE_ERROR_DELETE_BACKUP;
     }
+
+#  if !defined(BASE_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
@@ -1096,6 +1105,7 @@ static int backup_discard(const NS_tchar* path, const NS_tchar* relPath) {
            "file: " LOG_S,
            relPath));
     }
+#  endif
   }
 #else
   if (rv) {
@@ -2564,7 +2574,9 @@ static int ProcessReplaceRequest() {
     if (NS_taccess(deleteDir, F_OK)) {
       NS_tmkdir(deleteDir, 0755);
     }
+#  if !defined(BASE_BROWSER_UPDATE)
     remove_recursive_on_reboot(tmpDir, deleteDir);
+#  endif
 #endif
   }
 
@@ -2646,8 +2658,14 @@ static void UpdateThreadFunc(void* param) {
       if (ReadMARChannelIDs(updateSettingsPath, &MARStrings) != OK) {
         rv = UPDATE_SETTINGS_FILE_CHANNEL;
       } else {
+#  ifdef BASE_BROWSER_VERSION_QUOTED
+        // Use the base browser version to prevent downgrade attacks.
+        const char* appVersion = BASE_BROWSER_VERSION_QUOTED;
+#  else
+        const char* appVersion = MOZ_APP_VERSION;
+#  endif
         rv = gArchiveReader.VerifyProductInformation(
-            MARStrings.MARChannelID.get(), MOZ_APP_VERSION);
+            MARStrings.MARChannelID.get(), appVersion);
       }
     }
 #endif
@@ -2751,11 +2769,15 @@ static void UpdateThreadFunc(void* param) {
 
 #ifdef XP_MACOSX
 static void ServeElevatedUpdateThreadFunc(void* param) {
+#  ifdef BASE_BROWSER_UPDATE
+  WriteStatusFile(ELEVATION_CANCELED);
+#  else
   UpdateServerThreadArgs* threadArgs = (UpdateServerThreadArgs*)param;
   gSucceeded = ServeElevatedUpdate(threadArgs->argc, threadArgs->argv);
   if (!gSucceeded) {
     WriteStatusFile(ELEVATION_CANCELED);
   }
+#  endif
   QuitProgressUI();
 }
 
@@ -2785,7 +2807,7 @@ int LaunchCallbackAndPostProcessApps(int argc, NS_tchar** argv,
 #endif
 
   if (argc > callbackIndex) {
-#if defined(XP_WIN)
+#if defined(XP_WIN) && !defined(BASE_BROWSER_UPDATE)
     if (gSucceeded) {
       if (!LaunchWinPostProcess(gInstallDirPath, gPatchDirPath)) {
         fprintf(stderr, "The post update process was not launched");
@@ -2868,8 +2890,12 @@ int NS_main(int argc, NS_tchar** argv) {
   mozilla::UniquePtr<UmaskContext> umaskContext(new UmaskContext(0));
 
   bool isElevated =
+#  ifdef BASE_BROWSER_UPDATE
+      false;
+#  else
       strstr(argv[0], "/Library/PrivilegedHelperTools/org.mozilla.updater") !=
       0;
+#  endif
   if (isElevated) {
     if (!ObtainUpdaterArguments(&argc, &argv)) {
       // Won't actually get here because ObtainUpdaterArguments will terminate
@@ -3607,6 +3633,13 @@ int NS_main(int argc, NS_tchar** argv) {
         if (!useService && !noServiceFallback &&
             (updateLockFileHandle == INVALID_HANDLE_VALUE ||
              forceServiceFallback)) {
+#  ifdef BASE_BROWSER_UPDATE
+          // 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).
+          WriteStatusFile(WRITE_ERROR_ACCESS_DENIED);
+#  else
           // Get the secure ID before trying to update so it is possible to
           // determine if the updater has created a new one.
           char uuidStringBefore[UUID_LEN] = {'\0'};
@@ -3676,6 +3709,7 @@ int NS_main(int argc, NS_tchar** argv) {
             gCopyOutputFiles = false;
             WriteStatusFile(ELEVATION_CANCELED);
           }
+#  endif /* BASE_BROWSER_UPDATE */
         }
 
         // If we started the elevated updater, and it finished, check the secure
@@ -4046,6 +4080,7 @@ int NS_main(int argc, NS_tchar** argv) {
     if (!sStagedUpdate && !sReplaceRequest && _wrmdir(gDeleteDirPath)) {
       LOG(("NS_main: unable to remove directory: " LOG_S ", err: %d",
            DELETE_DIR, errno));
+#  if !defined(BASE_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
@@ -4065,6 +4100,7 @@ int NS_main(int argc, NS_tchar** argv) {
              "directory: " LOG_S,
              DELETE_DIR));
       }
+#  endif /* BASE_BROWSER_UPDATE */
     }
 #endif /* XP_WIN */
 
diff --git a/toolkit/xre/MacLaunchHelper.h b/toolkit/xre/MacLaunchHelper.h
index a4a8fb16661a0ecd73898c0356809d8fd0ef5a40..ce480c2f9c602439ff9f04dba9664efc1b1f0d3d 100644
--- a/toolkit/xre/MacLaunchHelper.h
+++ b/toolkit/xre/MacLaunchHelper.h
@@ -17,9 +17,11 @@ extern "C" {
  * pid of the terminated process to confirm that it executed successfully.
  */
 void LaunchChildMac(int aArgc, char** aArgv, pid_t* aPid = 0);
+#ifndef BASE_BROWSER_UPDATE
 bool LaunchElevatedUpdate(int aArgc, char** aArgv, pid_t* aPid = 0);
 bool InstallPrivilegedHelper();
 void AbortElevatedUpdate();
+#endif
 }
 
 #endif
diff --git a/toolkit/xre/MacLaunchHelper.mm b/toolkit/xre/MacLaunchHelper.mm
index 555e15d08479f61383674b11bb6ae349795fd9a2..56a574f4dc34fa290ff57814a3e52f6fd408eba8 100644
--- a/toolkit/xre/MacLaunchHelper.mm
+++ b/toolkit/xre/MacLaunchHelper.mm
@@ -39,6 +39,7 @@ void LaunchChildMac(int aArgc, char** aArgv, pid_t* aPid) {
   }
 }
 
+#ifndef BASE_BROWSER_UPDATE
 bool InstallPrivilegedHelper() {
   AuthorizationRef authRef = NULL;
   OSStatus status = AuthorizationCreate(
@@ -115,3 +116,4 @@ bool LaunchElevatedUpdate(int aArgc, char** aArgv, pid_t* aPid) {
   }
   return didSucceed;
 }
+#endif
diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp
index b12187a9c259ba91da44c498fe918afeadf29246..cd15676ad169fc3f1da53b7fb081841531d3bbda 100644
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -3432,6 +3432,11 @@ static bool CheckCompatibility(nsIFile* aProfileDir, const nsCString& aVersion,
   gLastAppBuildID.Assign(gAppData->buildID);
 
   nsAutoCString buf;
+
+  nsAutoCString forkVersion(BASE_BROWSER_VERSION_QUOTED);
+  rv = parser.GetString("Compatibility", "LastBaseBrowserVersion", buf);
+  if (NS_FAILED(rv) || !forkVersion.Equals(buf)) return false;
+
   rv = parser.GetString("Compatibility", "LastOSABI", buf);
   if (NS_FAILED(rv) || !aOSABI.Equals(buf)) return false;
 
@@ -3517,6 +3522,12 @@ static void WriteVersion(nsIFile* aProfileDir, const nsCString& aVersion,
   PR_Write(fd, kHeader, sizeof(kHeader) - 1);
   PR_Write(fd, aVersion.get(), aVersion.Length());
 
+  nsAutoCString forkVersion(BASE_BROWSER_VERSION_QUOTED);
+  static const char kForkVersionHeader[] =
+      NS_LINEBREAK "LastBaseBrowserVersion=";
+  PR_Write(fd, kForkVersionHeader, sizeof(kForkVersionHeader) - 1);
+  PR_Write(fd, forkVersion.get(), forkVersion.Length());
+
   static const char kOSABIHeader[] = NS_LINEBREAK "LastOSABI=";
   PR_Write(fd, kOSABIHeader, sizeof(kOSABIHeader) - 1);
   PR_Write(fd, aOSABI.get(), aOSABI.Length());
@@ -4905,8 +4916,18 @@ int XREMain::XRE_mainStartup(bool* aExitFlag) {
     NS_ENSURE_SUCCESS(rv, 1);
     rv = exeFile->GetParent(getter_AddRefs(exeDir));
     NS_ENSURE_SUCCESS(rv, 1);
+
+#  ifdef BASE_BROWSER_VERSION_QUOTED
+    nsAutoCString compatVersion(BASE_BROWSER_VERSION_QUOTED);
+#  endif
     ProcessUpdates(mDirProvider.GetGREDir(), exeDir, updRoot, gRestartArgc,
-                   gRestartArgv, mAppData->version);
+                   gRestartArgv,
+#  ifdef BASE_BROWSER_VERSION_QUOTED
+                   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 c355741c76c973ccc2c81a33a864079558f66dc5..b079f88f649c482ab78b1825c9ac4729957e08d3 100644
--- a/toolkit/xre/nsUpdateDriver.cpp
+++ b/toolkit/xre/nsUpdateDriver.cpp
@@ -52,7 +52,6 @@
 #  include "commonupdatedir.h"
 #  include "nsWindowsHelpers.h"
 #  include "pathhash.h"
-#  include "WinUtils.h"
 #  define getcwd(path, size) _getcwd(path, size)
 #  define getpid() GetCurrentProcessId()
 #elif defined(XP_UNIX)
@@ -161,6 +160,13 @@ static nsresult GetInstallDirPath(nsIFile* appDir, nsACString& installDirPath) {
   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
+
 static bool GetFile(nsIFile* dir, const nsACString& name,
                     nsCOMPtr<nsIFile>& result) {
   nsresult rv;
@@ -222,6 +228,34 @@ enum UpdateStatus {
   eAppliedService,
 };
 
+#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 ePendingElevate:
+      rv = "PendingElevate";
+      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.
@@ -294,6 +328,11 @@ static bool IsOlderVersion(nsIFile* versionFile, const char* appVersion) {
     return false;
   }
 
+#ifdef DEBUG
+  printf("IsOlderVersion checking appVersion %s against updateVersion %s\n",
+         appVersion, buf);
+#endif
+
   return mozilla::Version(appVersion) > buf;
 }
 
@@ -443,6 +482,7 @@ static void ApplyUpdate(nsIFile* greDir, nsIFile* updateDir, nsIFile* appDir,
     gfxPlatformMac::WaitForFontRegistration();
   }
 
+#  ifndef BASE_BROWSER_UPDATE
   // We need to detect whether elevation is required for this update. This can
   // occur when an admin user installs the application, but another admin
   // user attempts to update (see bug 394984).
@@ -469,6 +509,7 @@ static void ApplyUpdate(nsIFile* greDir, nsIFile* updateDir, nsIFile* appDir,
       }
     }
   }
+#  endif
 #endif
 
   nsAutoCString applyToDirPath;
@@ -577,6 +618,9 @@ static void ApplyUpdate(nsIFile* greDir, nsIFile* updateDir, nsIFile* appDir,
   }
 
   LOG(("spawning updater process [%s]\n", updaterPath.get()));
+#ifdef DEBUG
+  dump_argv("ApplyUpdate updater", argv, argc);
+#endif
 
 #if defined(XP_UNIX) && !defined(XP_MACOSX)
   // We use execv to spawn the updater process on all UNIX systems except Mac
@@ -615,6 +659,13 @@ static void ApplyUpdate(nsIFile* greDir, nsIFile* updateDir, nsIFile* appDir,
   }
 #elif defined(XP_MACOSX)
 UpdateDriverSetupMacCommandLine(argc, argv, restart);
+#  ifdef DEBUG
+dump_argv("ApplyUpdate after SetupMacCommandLine", argv, argc);
+#  endif
+#  ifndef BASE_BROWSER_UPDATE
+// We need to detect whether elevation is required for this update. This can
+// occur when an admin user installs the application, but another admin
+// user attempts to update (see bug 394984).
 if (restart && needElevation) {
   bool hasLaunched = LaunchElevatedUpdate(argc, argv, outpid);
   free(argv);
@@ -624,6 +675,7 @@ if (restart && needElevation) {
   }
   exit(0);
 }
+#  endif
 
 if (isStaged) {
   // Launch the updater to replace the installation with the staged updated.
@@ -694,14 +746,25 @@ nsresult ProcessUpdates(nsIFile* greDir, nsIFile* appDir, nsIFile* updRootDir,
                         bool restart, ProcessType* pid) {
   nsresult rv;
 
-#ifdef XP_WIN
-  // If we're in a package, we know any updates that we find are not for us.
-  if (mozilla::widget::WinUtils::HasPackageIdentity()) {
-    return NS_OK;
+#if defined(XP_WIN) && defined(BASE_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("tobedeleted"_ns);
+    if (NS_SUCCEEDED(winrv)) {
+      winrv = deleteDir->Remove(true);
+    }
   }
 #endif
 
   nsCOMPtr<nsIFile> updatesDir;
+#ifdef DEBUG
+  printf("ProcessUpdates updateRootDir: %s appVersion: %s\n",
+         updRootDir->HumanReadablePath().get(), appVersion);
+#endif
   rv = updRootDir->Clone(getter_AddRefs(updatesDir));
   NS_ENSURE_SUCCESS(rv, rv);
   rv = updatesDir->AppendNative("updates"_ns);
@@ -721,6 +784,12 @@ nsresult 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);
+  printf("ProcessUpdates updatesDir: %s\n",
+         updatesDir->HumanReadablePath().get());
+#endif
   switch (status) {
     case ePendingUpdate:
     case ePendingService: {
@@ -788,13 +857,16 @@ nsUpdateProcessor::ProcessUpdate() {
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
+  nsAutoCString appVersion;
+#ifdef BASE_BROWSER_VERSION_QUOTED
+  appVersion = BASE_BROWSER_VERSION_QUOTED;
+#else
   nsCOMPtr<nsIXULAppInfo> appInfo =
       do_GetService("@mozilla.org/xre/app-info;1", &rv);
   NS_ENSURE_SUCCESS(rv, rv);
-
-  nsAutoCString appVersion;
   rv = appInfo->GetVersion(appVersion);
   NS_ENSURE_SUCCESS(rv, rv);
+#endif
 
   // Copy the parameters to the StagedUpdateInfo structure shared with the
   // worker thread.
diff --git a/toolkit/xre/nsXREDirProvider.cpp b/toolkit/xre/nsXREDirProvider.cpp
index 566219fa303fc12abb9d05d7c9c3b317c7e4fb75..ec9183ebc96e60b295979a7333f219b99fcd0963 100644
--- a/toolkit/xre/nsXREDirProvider.cpp
+++ b/toolkit/xre/nsXREDirProvider.cpp
@@ -1100,7 +1100,44 @@ nsresult nsXREDirProvider::GetUpdateRootDir(nsIFile** aResult,
   rv = appFile->GetParent(getter_AddRefs(updRoot));
   NS_ENSURE_SUCCESS(rv, rv);
 
-#ifdef XP_MACOSX
+#if defined(BASE_BROWSER_UPDATE)
+  // For Base Browser and derivatives, we store update history, etc. within the
+  // UpdateInfo directory under the user data directory.
+#  if defined(ANDROID)
+#    error "The Base Browser updater is not supported on Android."
+#  elif defined(XP_MACOSX)
+  rv = GetUserDataDirectory(getter_AddRefs(updRoot), false);
+  NS_ENSURE_SUCCESS(rv, rv);
+#  else
+  rv = GetUserDataDirectoryHome(getter_AddRefs(updRoot), false);
+  NS_ENSURE_SUCCESS(rv, rv);
+#  endif
+  rv = updRoot->AppendNative("UpdateInfo"_ns);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+#  if defined(XP_MACOSX)
+  // Since the data directory may be shared among different installations of the
+  // application, embed the app path in the update dir so that the update
+  // history is partitioned. This is much less likely to be an issue on Linux or
+  // Windows, because our packages for those platforms include a "container"
+  // folder that provides partitioning by default, and we do not support use of
+  // a shared, OS-recommended area for user data on those platforms.
+  nsAutoString appPath;
+  rv = appFile->GetPath(appPath);
+  NS_ENSURE_SUCCESS(rv, rv);
+  int32_t dotIndex = appPath.RFind(u".app");
+  if (dotIndex == kNotFound) {
+    dotIndex = appPath.Length();
+  }
+  appPath = Substring(appPath, 1, dotIndex - 1);
+  rv = updRoot->AppendRelativePath(appPath);
+  NS_ENSURE_SUCCESS(rv, rv);
+#  endif
+
+  updRoot.forget(aResult);
+  return NS_OK;
+// The rest of the function is for ! BASE_BROWSER_UPDATE
+#elif defined(XP_MACOSX)
   nsCOMPtr<nsIFile> appRootDirFile;
   nsCOMPtr<nsIFile> localDir;
   nsAutoString appDirPath;
diff --git a/tools/update-packaging/common.sh b/tools/update-packaging/common.sh
index 4b994f30169c9ed2b28d92dbfe7a168ad8abd514..aef7c49c2b2a7ed068f9be8c6f72bb4a29bbcce8 100755
--- a/tools/update-packaging/common.sh
+++ b/tools/update-packaging/common.sh
@@ -76,17 +76,8 @@ 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/')
-    verbose_notice "     add-if \"$testdir\" \"$f\""
-    echo "add-if \"$testdir\" \"$f\"" >> "$filev3"
-  else
-    verbose_notice "        add \"$f\"$forced"
-    echo "add \"$f\"" >> "$filev3"
-  fi
+  verbose_notice "        add \"$f\"$forced"
+  echo "add \"$f\"" >> "$filev3"
 }
 
 check_for_add_if_not_update() {
@@ -113,17 +104,8 @@ make_patch_instruction() {
   f="$1"
   filev3="$2"
 
-  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/')
-    verbose_notice "   patch-if \"$testdir\" \"$f.patch\" \"$f\""
-    echo "patch-if \"$testdir\" \"$f.patch\" \"$f\"" >> "$filev3"
-  else
-    verbose_notice "      patch \"$f.patch\" \"$f\""
-    echo "patch \"$f.patch\" \"$f\"" >> "$filev3"
-  fi
+  verbose_notice "      patch \"$f.patch\" \"$f\""
+  echo "patch \"$f.patch\" \"$f\"" >> "$filev3"
 }
 
 append_remove_instructions() {