sparkle updater fixes

This commit is contained in:
Nikhil Sonti
2025-06-27 14:40:42 -07:00
parent c2deffc3da
commit 12e86ecf9f

View File

@@ -1,23 +1,23 @@
From 4c30c5e4fdac32f5e27cf7fff544fec79aa05b31 Mon Sep 17 00:00:00 2001
From 32712f2b396a6bb5341a1a4dbbd4079ea0c5d90b Mon Sep 17 00:00:00 2001
From: Nikhil Sonti <nikhilsv92@gmail.com>
Date: Mon, 16 Jun 2025 14:14:40 -0700
Subject: [PATCH] nxtscape updater with sparkle
Subject: [PATCH] Nxtscape sparkler updater
---
chrome/BUILD.gn | 5 +
chrome/browser/BUILD.gn | 20 ++
chrome/browser/BUILD.gn | 20 +
.../mac/chrome_browser_main_extra_parts_mac.h | 1 +
.../chrome_browser_main_extra_parts_mac.mm | 12 +
chrome/browser/mac/sparkle_glue.h | 46 +++
chrome/browser/mac/sparkle_glue.mm | 262 ++++++++++++++++++
chrome/browser/mac/su_updater.h | 38 +++
chrome/browser/sparkle_buildflags.gni | 21 ++
.../chrome_browser_main_extra_parts_mac.mm | 19 +
chrome/browser/mac/sparkle_glue.h | 51 +++
chrome/browser/mac/sparkle_glue.mm | 409 ++++++++++++++++++
chrome/browser/mac/su_updater.h | 38 ++
chrome/browser/sparkle_buildflags.gni | 21 +
chrome/browser/ui/BUILD.gn | 3 +
.../webui/help/sparkle_version_updater_mac.h | 50 ++++
.../webui/help/sparkle_version_updater_mac.mm | 89 ++++++
.../ui/webui/help/version_updater_mac.mm | 48 +++-
third_party/sparkle/BUILD.gn | 50 ++++
13 files changed, 644 insertions(+), 1 deletion(-)
.../webui/help/sparkle_version_updater_mac.h | 55 +++
.../webui/help/sparkle_version_updater_mac.mm | 87 ++++
.../ui/webui/help/version_updater_mac.mm | 27 +-
third_party/sparkle/BUILD.gn | 50 +++
13 files changed, 785 insertions(+), 1 deletion(-)
create mode 100644 chrome/browser/mac/sparkle_glue.h
create mode 100644 chrome/browser/mac/sparkle_glue.mm
create mode 100644 chrome/browser/mac/su_updater.h
@@ -107,10 +107,10 @@ index 95726e7765367..da1f407aff2b3 100644
private:
std::unique_ptr<display::ScopedNativeScreen> screen_;
diff --git a/chrome/browser/mac/chrome_browser_main_extra_parts_mac.mm b/chrome/browser/mac/chrome_browser_main_extra_parts_mac.mm
index 6bb5ccb823895..48bc86ca53bea 100644
index 6bb5ccb823895..f839a26a405d7 100644
--- a/chrome/browser/mac/chrome_browser_main_extra_parts_mac.mm
+++ b/chrome/browser/mac/chrome_browser_main_extra_parts_mac.mm
@@ -4,11 +4,23 @@
@@ -4,11 +4,30 @@
#include "chrome/browser/mac/chrome_browser_main_extra_parts_mac.h"
@@ -131,15 +131,22 @@ index 6bb5ccb823895..48bc86ca53bea 100644
+void ChromeBrowserMainExtraPartsMac::PreCreateMainMessageLoop() {
+#if BUILDFLAG(ENABLE_SPARKLE)
+ // Initialize Sparkle updater if available
+ [[SparkleGlue sharedSparkleGlue] registerWithSparkle];
+ @try {
+ // Just get the shared instance - actual initialization is deferred
+ [SparkleGlue sharedSparkleGlue];
+ } @catch (NSException* exception) {
+ // Silently fail - updates just won't be available
+ } @catch (...) {
+ // Catch any C++ exceptions too
+ }
+#endif
+}
diff --git a/chrome/browser/mac/sparkle_glue.h b/chrome/browser/mac/sparkle_glue.h
new file mode 100644
index 0000000000000..66beee4a4e3cc
index 0000000000000..c0b84c7873a18
--- /dev/null
+++ b/chrome/browser/mac/sparkle_glue.h
@@ -0,0 +1,46 @@
@@ -0,0 +1,51 @@
+// Copyright 2024 Nxtscape Browser Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
@@ -151,6 +158,7 @@ index 0000000000000..66beee4a4e3cc
+
+// Forward declarations for C++ types in Objective-C context
+#ifdef __cplusplus
+#include "base/memory/weak_ptr.h"
+class SparkleVersionUpdater;
+#else
+typedef struct SparkleVersionUpdater SparkleVersionUpdater;
@@ -175,7 +183,11 @@ index 0000000000000..66beee4a4e3cc
+- (BOOL)isUpdateCheckEnabled;
+
+// Set the version updater to receive status notifications
+- (void)setVersionUpdater:(SparkleVersionUpdater*)updater;
+#ifdef __cplusplus
+- (void)setVersionUpdater:(base::WeakPtr<SparkleVersionUpdater>)updater;
+#else
+- (void)setVersionUpdater:(void*)updater;
+#endif
+
+@end // @interface SparkleGlue
+
@@ -189,10 +201,10 @@ index 0000000000000..66beee4a4e3cc
\ No newline at end of file
diff --git a/chrome/browser/mac/sparkle_glue.mm b/chrome/browser/mac/sparkle_glue.mm
new file mode 100644
index 0000000000000..3480f03c47075
index 0000000000000..8d02951dfd139
--- /dev/null
+++ b/chrome/browser/mac/sparkle_glue.mm
@@ -0,0 +1,262 @@
@@ -0,0 +1,409 @@
+// Copyright 2024 Nxtscape Browser Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
@@ -207,7 +219,7 @@ index 0000000000000..3480f03c47075
+#include "base/apple/scoped_nsautorelease_pool.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/memory/raw_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/strings/sys_string_conversions.h"
+#include "chrome/browser/mac/su_updater.h"
+#include "chrome/browser/ui/webui/help/sparkle_version_updater_mac.h"
@@ -224,15 +236,19 @@ index 0000000000000..3480f03c47075
+
+// Default update feed URL - replace with your actual update server
+NSString* GetUpdateFeedURL() {
+ // You can override with command line flag: --update-feed-url=<url>
+ auto* command_line = base::CommandLine::ForCurrentProcess();
+ if (command_line->HasSwitch("update-feed-url")) {
+ return base::SysUTF8ToNSString(
+ command_line->GetSwitchValueASCII("update-feed-url"));
+ }
+ @try {
+ // You can override with command line flag: --update-feed-url=<url>
+ auto* command_line = base::CommandLine::ForCurrentProcess();
+ if (command_line && command_line->HasSwitch("update-feed-url")) {
+ return base::SysUTF8ToNSString(
+ command_line->GetSwitchValueASCII("update-feed-url"));
+ }
+
+ // Default URL - replace with your actual update server
+ return @"https://nxtscape.github.io/nxtscape/appcast.xml";
+ // Default URL - replace with your actual update server
+ return @"https://nxtscape.github.io/nxtscape/appcast.xml";
+ } @catch (NSException* exception) {
+ return @"https://nxtscape.github.io/nxtscape/appcast.xml";
+ }
+}
+
+} // namespace
@@ -241,7 +257,8 @@ index 0000000000000..3480f03c47075
+ SUUpdater* __strong _updater;
+ BOOL _registered;
+ NSString* __strong _appPath;
+ raw_ptr<SparkleVersionUpdater> _versionUpdater; // Weak reference
+ base::WeakPtr<SparkleVersionUpdater> _versionUpdater; // Weak reference
+ BOOL _initializationAttempted;
+}
+
++ (instancetype)sharedSparkleGlue {
@@ -249,24 +266,20 @@ index 0000000000000..3480f03c47075
+ static dispatch_once_t onceToken;
+
+ dispatch_once(&onceToken, ^{
+ VLOG(0) << "SparkleGlue: Initializing Sparkle updater";
+ @try {
+ // Check if updates are disabled via command line
+ auto* command_line = base::CommandLine::ForCurrentProcess();
+ if (command_line && command_line->HasSwitch("disable-updates")) {
+ return;
+ }
+
+ // Check if updates are disabled via command line
+ auto* command_line = base::CommandLine::ForCurrentProcess();
+ if (command_line->HasSwitch("disable-updates")) {
+ VLOG(0) << "SparkleGlue: Updates disabled via command line";
+ return;
+ }
+
+ VLOG(0) << "SparkleGlue: Creating SparkleGlue instance";
+ shared = [[SparkleGlue alloc] init];
+ if (![shared loadSparkleFramework]) {
+ VLOG(0) << "SparkleGlue: Failed to load Sparkle framework";
+ shared = [[SparkleGlue alloc] init];
+ } @catch (NSException* exception) {
+ // Silently fail - updates just won't be available
+ shared = nil;
+ } @catch (...) {
+ // Catch any C++ exceptions too
+ shared = nil;
+ } else {
+ VLOG(0) << "SparkleGlue: Successfully loaded Sparkle framework";
+ // Automatically register with Sparkle on successful initialization
+ [shared registerWithSparkle];
+ }
+ });
+
@@ -274,98 +287,216 @@ index 0000000000000..3480f03c47075
+}
+
+- (instancetype)init {
+ if (self = [super init]) {
+ _registered = NO;
+ _appPath = [base::apple::OuterBundle().bundlePath copy];
+ return self;
+ @try {
+ if (self = [super init]) {
+ _registered = NO;
+ _initializationAttempted = NO;
+ _appPath = [base::apple::OuterBundle().bundlePath copy];
+
+ // Defer framework loading to main queue with delay
+ // This ensures all Chrome subsystems are initialized
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC),
+ dispatch_get_main_queue(), ^{
+ [self attemptSparkleInitialization];
+ });
+
+ return self;
+ }
+ } @catch (NSException* exception) {
+ return nil;
+ }
+ return nil;
+}
+
+- (void)attemptSparkleInitialization {
+ @try {
+ if (_initializationAttempted) {
+ return;
+ }
+ _initializationAttempted = YES;
+
+ if ([self loadSparkleFramework]) {
+ VLOG(1) << "Sparkle framework loaded successfully";
+ [self registerWithSparkle];
+ }
+ } @catch (NSException* exception) {
+ // Silently fail
+ } @catch (...) {
+ // Catch C++ exceptions too
+ }
+}
+
+- (BOOL)loadSparkleFramework {
+ VLOG(0) << "SparkleGlue: Loading Sparkle framework";
+ @try {
+ base::apple::ScopedNSAutoreleasePool pool;
+
+ // Check if running from read-only filesystem (e.g., DMG)
+ if ([self isOnReadOnlyFilesystem]) {
+ VLOG(0) << "SparkleGlue: Running from read-only filesystem, updates disabled";
+ return NO;
+ }
+ // Check if running from read-only filesystem (e.g., DMG)
+ if ([self isOnReadOnlyFilesystem]) {
+ return NO;
+ }
+
+ // Load Sparkle framework
+ NSString* sparkle_path =
+ // Try multiple paths for the Sparkle framework
+ NSArray<NSString*>* searchPaths = @[
+ // Path 1: Inside the Chromium Framework bundle (where it's actually bundled)
+ [[base::apple::FrameworkBundle() privateFrameworksPath]
+ stringByAppendingPathComponent:@"Sparkle.framework"];
+ stringByAppendingPathComponent:@"Sparkle.framework"],
+
+ // Path 2: In the main app's Frameworks directory
+ [[base::apple::OuterBundle() privateFrameworksPath]
+ stringByAppendingPathComponent:@"Sparkle.framework"],
+
+ // Path 3: Relative to the framework bundle
+ [[[base::apple::FrameworkBundle() bundlePath]
+ stringByAppendingPathComponent:@"Frameworks"]
+ stringByAppendingPathComponent:@"Sparkle.framework"]
+ ];
+
+ VLOG(0) << "SparkleGlue: Looking for Sparkle framework at: "
+ << base::SysNSStringToUTF8(sparkle_path);
+ NSBundle* sparkle_bundle = nil;
+
+ for (NSString* path in searchPaths) {
+ if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
+ sparkle_bundle = [NSBundle bundleWithPath:path];
+ if (sparkle_bundle) {
+ break;
+ }
+ }
+ }
+
+ NSBundle* sparkle_bundle = [NSBundle bundleWithPath:sparkle_path];
+ if (!sparkle_bundle) {
+ VLOG(0) << "SparkleGlue: Sparkle framework not found at: "
+ << base::SysNSStringToUTF8(sparkle_path);
+ if (!sparkle_bundle) {
+ return NO;
+ }
+
+ // Check if already loaded
+ if (![sparkle_bundle isLoaded]) {
+ NSError* load_error = nil;
+ if (![sparkle_bundle loadAndReturnError:&load_error]) {
+ return NO;
+ }
+ }
+
+ // Get SUUpdater class and create shared instance
+ Class updater_class = [sparkle_bundle classNamed:@"SUUpdater"];
+ if (!updater_class) {
+ return NO;
+ }
+
+ // Use performSelector to avoid direct class dependencies
+ SEL sharedUpdaterSelector = NSSelectorFromString(@"sharedUpdater");
+ if (![updater_class respondsToSelector:sharedUpdaterSelector]) {
+ return NO;
+ }
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+ _updater = [updater_class performSelector:sharedUpdaterSelector];
+#pragma clang diagnostic pop
+
+ if (!_updater) {
+ return NO;
+ }
+
+ return YES;
+
+ } @catch (NSException* exception) {
+ return NO;
+ } @catch (...) {
+ return NO;
+ }
+
+ VLOG(0) << "SparkleGlue: Found Sparkle bundle, attempting to load";
+ if (![sparkle_bundle load]) {
+ VLOG(0) << "SparkleGlue: Failed to load Sparkle framework";
+ return NO;
+ }
+
+ VLOG(0) << "SparkleGlue: Successfully loaded Sparkle framework";
+
+ // Get SUUpdater class and create shared instance
+ Class updater_class = [sparkle_bundle classNamed:@"SUUpdater"];
+ if (!updater_class) {
+ VLOG(0) << "SparkleGlue: SUUpdater class not found in Sparkle framework";
+ return NO;
+ }
+
+ VLOG(0) << "SparkleGlue: Found SUUpdater class, creating shared instance";
+ _updater = [updater_class sharedUpdater];
+ if (!_updater) {
+ VLOG(0) << "SparkleGlue: Failed to get SUUpdater shared instance";
+ return NO;
+ }
+
+ VLOG(0) << "SparkleGlue: Successfully created SUUpdater instance";
+ return YES;
+}
+
+- (void)registerWithSparkle {
+ if (_registered || !_updater) {
+ VLOG(0) << "SparkleGlue: Already registered or no updater available";
+ return;
+ @try {
+ if (_registered || !_updater) {
+ return;
+ }
+
+ _registered = YES;
+
+ // Configure updater using performSelector to avoid direct dependencies
+ SEL setDelegateSelector = NSSelectorFromString(@"setDelegate:");
+ if ([_updater respondsToSelector:setDelegateSelector]) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+ [_updater performSelector:setDelegateSelector withObject:self];
+#pragma clang diagnostic pop
+ }
+
+ // Set update check interval
+ SEL setIntervalSelector = NSSelectorFromString(@"setUpdateCheckInterval:");
+ if ([_updater respondsToSelector:setIntervalSelector]) {
+ NSMethodSignature* sig = [_updater methodSignatureForSelector:setIntervalSelector];
+ NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:sig];
+ [invocation setTarget:_updater];
+ [invocation setSelector:setIntervalSelector];
+ NSTimeInterval interval = kUpdateCheckIntervalInSec;
+ [invocation setArgument:&interval atIndex:2];
+ [invocation invoke];
+ }
+
+ // Set automatic checks
+ SEL setAutoCheckSelector = NSSelectorFromString(@"setAutomaticallyChecksForUpdates:");
+ if ([_updater respondsToSelector:setAutoCheckSelector]) {
+ NSMethodSignature* sig = [_updater methodSignatureForSelector:setAutoCheckSelector];
+ NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:sig];
+ [invocation setTarget:_updater];
+ [invocation setSelector:setAutoCheckSelector];
+ BOOL value = YES;
+ [invocation setArgument:&value atIndex:2];
+ [invocation invoke];
+ }
+
+ // Set automatic downloads
+ SEL setAutoDownloadSelector = NSSelectorFromString(@"setAutomaticallyDownloadsUpdates:");
+ if ([_updater respondsToSelector:setAutoDownloadSelector]) {
+ NSMethodSignature* sig = [_updater methodSignatureForSelector:setAutoDownloadSelector];
+ NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:sig];
+ [invocation setTarget:_updater];
+ [invocation setSelector:setAutoDownloadSelector];
+ BOOL value = YES;
+ [invocation setArgument:&value atIndex:2];
+ [invocation invoke];
+ }
+
+ // Set feed URL
+ SEL setFeedURLSelector = NSSelectorFromString(@"setFeedURL:");
+ if ([_updater respondsToSelector:setFeedURLSelector]) {
+ NSString* feedURLString = GetUpdateFeedURL();
+ if (feedURLString) {
+ NSURL* feedURL = [NSURL URLWithString:feedURLString];
+ if (feedURL) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+ [_updater performSelector:setFeedURLSelector withObject:feedURL];
+#pragma clang diagnostic pop
+ }
+ }
+ }
+
+ } @catch (NSException* exception) {
+ // Silently fail
+ _registered = NO;
+ } @catch (...) {
+ // Catch C++ exceptions too
+ _registered = NO;
+ }
+
+ VLOG(0) << "SparkleGlue: Registering with Sparkle updater";
+
+ _registered = YES;
+
+ // Configure updater
+ [_updater setDelegate:self];
+ [_updater setUpdateCheckInterval:kUpdateCheckIntervalInSec];
+ [_updater setAutomaticallyChecksForUpdates:YES];
+ [_updater setAutomaticallyDownloadsUpdates:YES];
+
+ // Set feed URL
+ NSString* feedURL = GetUpdateFeedURL();
+ VLOG(0) << "SparkleGlue: Update feed URL: " << base::SysNSStringToUTF8(feedURL);
+
+ VLOG(0) << "SparkleGlue: Successfully registered with Sparkle updater";
+ VLOG(0) << "SparkleGlue: Update check interval: " << kUpdateCheckIntervalInSec << " seconds";
+ VLOG(0) << "SparkleGlue: Automatic checks enabled: YES";
+ VLOG(0) << "SparkleGlue: Automatic downloads enabled: YES";
+}
+
+- (void)checkForUpdates {
+ if (!_registered || !_updater) {
+ VLOG(0) << "Updater not registered";
+ return;
+ }
+ @try {
+ if (!_registered || !_updater) {
+ return;
+ }
+
+ VLOG(1) << "Manually checking for updates";
+ [_updater checkForUpdatesInBackground];
+ SEL checkSelector = NSSelectorFromString(@"checkForUpdatesInBackground");
+ if ([_updater respondsToSelector:checkSelector]) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+ [_updater performSelector:checkSelector];
+#pragma clang diagnostic pop
+ }
+ } @catch (NSException* exception) {
+ // Silently fail
+ }
+}
+
+- (BOOL)isUpdateCheckEnabled {
@@ -373,74 +504,100 @@ index 0000000000000..3480f03c47075
+}
+
+- (BOOL)isOnReadOnlyFilesystem {
+ const char* appPathC = _appPath.fileSystemRepresentation;
+ struct statfs statfsBuf;
+ @try {
+ const char* appPathC = _appPath.fileSystemRepresentation;
+ struct statfs statfsBuf;
+
+ if (statfs(appPathC, &statfsBuf) != 0) {
+ PLOG(ERROR) << "statfs failed";
+ if (statfs(appPathC, &statfsBuf) != 0) {
+ return NO;
+ }
+
+ return (statfsBuf.f_flags & MNT_RDONLY) != 0;
+ } @catch (NSException* exception) {
+ return NO;
+ }
+
+ return (statfsBuf.f_flags & MNT_RDONLY) != 0;
+}
+
+- (void)setVersionUpdater:(SparkleVersionUpdater*)updater {
+ VLOG(0) << "SparkleGlue: Setting version updater: " << updater;
+ _versionUpdater = updater;
+- (void)setVersionUpdater:(base::WeakPtr<SparkleVersionUpdater>)updater {
+ @try {
+ _versionUpdater = updater;
+ } @catch (NSException* exception) {
+ // Ignore
+ }
+}
+
+#pragma mark - SUUpdaterDelegate
+
+- (NSString*)feedURLStringForUpdater:(SUUpdater*)updater {
+ NSString* feedURL = GetUpdateFeedURL();
+ VLOG(0) << "SparkleGlue: Delegate requested feed URL: " << base::SysNSStringToUTF8(feedURL);
+ return feedURL;
+ @try {
+ return GetUpdateFeedURL();
+ } @catch (NSException* exception) {
+ return @"https://nxtscape.github.io/nxtscape/appcast.xml";
+ }
+}
+
+- (void)updater:(SUUpdater*)updater didFinishLoadingAppcast:(SUAppcast*)appcast {
+ VLOG(0) << "SparkleGlue: Finished loading appcast";
+ // Notify version updater that we're still checking
+ if (_versionUpdater) {
+ _versionUpdater->OnSparkleStatusChange(kSparkleStatusChecking);
+ @try {
+ // Notify version updater that we're still checking
+ if (auto* versionUpdater = _versionUpdater.get()) {
+ versionUpdater->OnSparkleStatusChange(kSparkleStatusChecking);
+ }
+ } @catch (...) {
+ // Ignore all exceptions
+ }
+}
+
+- (void)updater:(SUUpdater*)updater didFindValidUpdate:(SUAppcastItem*)item {
+ VLOG(0) << "SparkleGlue: Found valid update";
+ if (_versionUpdater) {
+ _versionUpdater->OnSparkleStatusChange(kSparkleStatusUpdateFound);
+ @try {
+ VLOG(1) << "Sparkle: Update available";
+ if (auto* versionUpdater = _versionUpdater.get()) {
+ versionUpdater->OnSparkleStatusChange(kSparkleStatusUpdateFound);
+ }
+ } @catch (...) {
+ // Ignore all exceptions
+ }
+}
+
+- (void)updaterDidNotFindUpdate:(SUUpdater*)updater {
+ VLOG(0) << "SparkleGlue: No update found";
+ if (_versionUpdater) {
+ _versionUpdater->OnSparkleStatusChange(kSparkleStatusNoUpdate);
+ @try {
+ if (auto* versionUpdater = _versionUpdater.get()) {
+ versionUpdater->OnSparkleStatusChange(kSparkleStatusNoUpdate);
+ }
+ } @catch (...) {
+ // Ignore all exceptions
+ }
+}
+
+- (void)updater:(SUUpdater*)updater willInstallUpdate:(SUAppcastItem*)item {
+ VLOG(0) << "SparkleGlue: Will install update";
+ if (_versionUpdater) {
+ _versionUpdater->OnSparkleStatusChange(kSparkleStatusReadyToInstall);
+ @try {
+ if (auto* versionUpdater = _versionUpdater.get()) {
+ versionUpdater->OnSparkleStatusChange(kSparkleStatusReadyToInstall);
+ }
+ } @catch (...) {
+ // Ignore all exceptions
+ }
+}
+
+- (void)updater:(SUUpdater*)updater didAbortWithError:(NSError*)error {
+ VLOG(0) << "SparkleGlue: Update aborted with error: " << base::SysNSStringToUTF8([error localizedDescription]);
+
+ // Check if this is actually an error or just "no update needed"
+ NSString* errorDesc = [error localizedDescription];
+ if ([errorDesc containsString:@"up to date"] || [errorDesc containsString:@"You're up to date"]) {
+ // This is not really an error, it means no update is needed
+ if (_versionUpdater) {
+ _versionUpdater->OnSparkleStatusChange(kSparkleStatusNoUpdate);
+ }
+ } else {
+ // This is a real error
+ if (_versionUpdater) {
+ _versionUpdater->OnSparkleStatusChange(kSparkleStatusError, base::SysNSStringToUTF8(errorDesc));
+ @try {
+ // Check if this is actually an error or just "no update needed"
+ NSString* errorDesc = [error localizedDescription];
+ if ([errorDesc containsString:@"up to date"] ||
+ [errorDesc containsString:@"You're up to date"]) {
+ // This is not really an error, it means no update is needed
+ if (auto* versionUpdater = _versionUpdater.get()) {
+ versionUpdater->OnSparkleStatusChange(kSparkleStatusNoUpdate);
+ }
+ } else {
+ // This is a real error
+ VLOG(1) << "Sparkle update error: " << base::SysNSStringToUTF8(errorDesc);
+ if (auto* versionUpdater = _versionUpdater.get()) {
+ versionUpdater->OnSparkleStatusChange(kSparkleStatusError,
+ base::SysNSStringToUTF8(errorDesc));
+ }
+ }
+ } @catch (...) {
+ // Ignore all exceptions
+ }
+}
+
@@ -449,9 +606,11 @@ index 0000000000000..3480f03c47075
+namespace sparkle_glue {
+
+bool SparkleEnabled() {
+ bool enabled = [SparkleGlue sharedSparkleGlue] != nil;
+ VLOG(0) << "SparkleGlue: Sparkle enabled: " << (enabled ? "YES" : "NO");
+ return enabled;
+ @try {
+ return [SparkleGlue sharedSparkleGlue] != nil;
+ } @catch (...) {
+ return false;
+ }
+}
+
+} // namespace sparkle_glue
@@ -552,10 +711,10 @@ index 93096ad92c1a3..5accc0ef01f89 100644
"//chrome/browser/updater:browser_updater_client",
diff --git a/chrome/browser/ui/webui/help/sparkle_version_updater_mac.h b/chrome/browser/ui/webui/help/sparkle_version_updater_mac.h
new file mode 100644
index 0000000000000..c54fb05d1e984
index 0000000000000..91cf1bd365df6
--- /dev/null
+++ b/chrome/browser/ui/webui/help/sparkle_version_updater_mac.h
@@ -0,0 +1,50 @@
@@ -0,0 +1,55 @@
+// Copyright 2024 Nxtscape Browser Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
@@ -565,6 +724,7 @@ index 0000000000000..c54fb05d1e984
+
+#include "chrome/browser/ui/webui/help/version_updater.h"
+#include "chrome/browser/mac/sparkle_glue.h"
+#include "base/memory/weak_ptr.h"
+
+#if defined(__OBJC__)
+@class NSNotification;
@@ -599,20 +759,24 @@ index 0000000000000..c54fb05d1e984
+ // Called by SparkleGlue to notify of status changes
+ void OnSparkleStatusChange(SparkleUpdateStatus status, const std::string& error_message = "");
+
+ // Get a weak pointer to this object
+ base::WeakPtr<SparkleVersionUpdater> GetWeakPtr();
+
+ private:
+ void UpdateStatus(SparkleUpdateStatus status, const std::string& error_message = "");
+
+ StatusCallback status_callback_;
+ base::WeakPtrFactory<SparkleVersionUpdater> weak_ptr_factory_{this};
+};
+
+#endif // CHROME_BROWSER_UI_WEBUI_HELP_SPARKLE_VERSION_UPDATER_MAC_H_
\ No newline at end of file
diff --git a/chrome/browser/ui/webui/help/sparkle_version_updater_mac.mm b/chrome/browser/ui/webui/help/sparkle_version_updater_mac.mm
new file mode 100644
index 0000000000000..8f7857f4d0e40
index 0000000000000..b0222ef2f4427
--- /dev/null
+++ b/chrome/browser/ui/webui/help/sparkle_version_updater_mac.mm
@@ -0,0 +1,89 @@
@@ -0,0 +1,87 @@
+// Copyright 2024 Nxtscape Browser Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
@@ -630,22 +794,20 @@ index 0000000000000..8f7857f4d0e40
+
+void SparkleVersionUpdater::CheckForUpdate(StatusCallback status_callback,
+ PromoteCallback promote_callback) {
+ VLOG(0) << "SparkleVersionUpdater: CheckForUpdate called";
+ status_callback_ = std::move(status_callback);
+
+ SparkleGlue* sparkle = [SparkleGlue sharedSparkleGlue];
+ if (!sparkle || ![sparkle isUpdateCheckEnabled]) {
+ VLOG(0) << "SparkleVersionUpdater: Sparkle updater not available or disabled";
+ VLOG(1) << "Sparkle updater not available or disabled";
+ UpdateStatus(kSparkleStatusError, "Sparkle updater not available");
+ return;
+ }
+
+ VLOG(0) << "SparkleVersionUpdater: Starting update check";
+ // Start checking for updates
+ UpdateStatus(kSparkleStatusChecking);
+
+ // Set this updater as the current one so SparkleGlue can notify us
+ [sparkle setVersionUpdater:this];
+ [sparkle setVersionUpdater:GetWeakPtr()];
+
+ [sparkle checkForUpdates];
+}
@@ -653,17 +815,18 @@ index 0000000000000..8f7857f4d0e40
+void SparkleVersionUpdater::PromoteUpdater() {
+ // Sparkle doesn't require promotion like Google's updater
+ // This is a no-op for Sparkle
+ VLOG(0) << "SparkleVersionUpdater: PromoteUpdater called - not needed for Sparkle";
+}
+
+void SparkleVersionUpdater::OnSparkleStatusChange(SparkleUpdateStatus status, const std::string& error_message) {
+ VLOG(0) << "SparkleVersionUpdater: Status change received: " << static_cast<int>(status);
+ UpdateStatus(status, error_message);
+}
+
+base::WeakPtr<SparkleVersionUpdater> SparkleVersionUpdater::GetWeakPtr() {
+ return weak_ptr_factory_.GetWeakPtr();
+}
+
+void SparkleVersionUpdater::UpdateStatus(SparkleUpdateStatus status, const std::string& error_message) {
+ if (status_callback_.is_null()) {
+ VLOG(0) << "SparkleVersionUpdater: No status callback available";
+ return;
+ }
+
@@ -672,39 +835,38 @@ index 0000000000000..8f7857f4d0e40
+
+ switch (status) {
+ case kSparkleStatusChecking:
+ VLOG(0) << "SparkleVersionUpdater: Status - Checking for updates";
+ VLOG(1) << "Sparkle: Checking for updates";
+ update_status = CHECKING;
+ break;
+ case kSparkleStatusNoUpdate:
+ VLOG(0) << "SparkleVersionUpdater: Status - No update available";
+ VLOG(1) << "Sparkle: No update available";
+ update_status = UPDATED;
+ break;
+ case kSparkleStatusUpdateFound:
+ VLOG(0) << "SparkleVersionUpdater: Status - Update found";
+ VLOG(1) << "Sparkle: Update found";
+ update_status = UPDATING;
+ break;
+ case kSparkleStatusDownloading:
+ VLOG(0) << "SparkleVersionUpdater: Status - Downloading update";
+ VLOG(1) << "Sparkle: Downloading update";
+ update_status = UPDATING;
+ break;
+ case kSparkleStatusReadyToInstall:
+ VLOG(0) << "SparkleVersionUpdater: Status - Ready to install";
+ VLOG(1) << "Sparkle: Ready to install update";
+ update_status = NEARLY_UPDATED;
+ break;
+ case kSparkleStatusError:
+ VLOG(0) << "SparkleVersionUpdater: Status - Error: " << error_message;
+ VLOG(1) << "Sparkle update error: " << error_message;
+ update_status = FAILED;
+ message = base::UTF8ToUTF16(error_message);
+ break;
+ }
+
+ VLOG(0) << "SparkleVersionUpdater: Calling status callback with status: " << static_cast<int>(update_status);
+ status_callback_.Run(update_status, 0, false, false, std::string(), 0,
+ message);
+}
\ No newline at end of file
diff --git a/chrome/browser/ui/webui/help/version_updater_mac.mm b/chrome/browser/ui/webui/help/version_updater_mac.mm
index 992157e28e8f5..4c5949b1d266a 100644
index 992157e28e8f5..b44a4c8b8d7df 100644
--- a/chrome/browser/ui/webui/help/version_updater_mac.mm
+++ b/chrome/browser/ui/webui/help/version_updater_mac.mm
@@ -6,6 +6,15 @@
@@ -723,114 +885,21 @@ index 992157e28e8f5..4c5949b1d266a 100644
#include <algorithm>
#include <memory>
#include <string>
@@ -47,6 +56,8 @@ int GetDownloadProgress(int64_t downloaded_bytes, int64_t total_bytes) {
void UpdateStatus(VersionUpdater::StatusCallback status_callback,
const updater::UpdateService::UpdateState& update_state) {
+ VLOG(0) << "VersionUpdaterMac: UpdateStatus called with state: " << static_cast<int>(update_state.state);
+
VersionUpdater::Status status = VersionUpdater::Status::CHECKING;
int progress = 0;
std::string version;
@@ -54,38 +65,48 @@ void UpdateStatus(VersionUpdater::StatusCallback status_callback,
switch (update_state.state) {
case updater::UpdateService::UpdateState::State::kCheckingForUpdates:
+ VLOG(0) << "VersionUpdaterMac: Checking for updates";
[[fallthrough]];
case updater::UpdateService::UpdateState::State::kUpdateAvailable:
+ VLOG(0) << "VersionUpdaterMac: Update available";
status = VersionUpdater::Status::CHECKING;
break;
case updater::UpdateService::UpdateState::State::kDownloading:
progress = GetDownloadProgress(update_state.downloaded_bytes,
update_state.total_bytes);
+ VLOG(0) << "VersionUpdaterMac: Downloading update, progress: " << progress << "%";
[[fallthrough]];
case updater::UpdateService::UpdateState::State::kInstalling:
+ VLOG(0) << "VersionUpdaterMac: Installing update";
status = VersionUpdater::Status::UPDATING;
break;
case updater::UpdateService::UpdateState::State::kUpdated:
+ VLOG(0) << "VersionUpdaterMac: Update completed";
status = VersionUpdater::Status::NEARLY_UPDATED;
break;
case updater::UpdateService::UpdateState::State::kNoUpdate:
+ VLOG(0) << "VersionUpdaterMac: No update available";
status = UpgradeDetector::GetInstance()->is_upgrade_available()
? VersionUpdater::Status::NEARLY_UPDATED
@@ -74,6 +83,8 @@ void UpdateStatus(VersionUpdater::StatusCallback status_callback,
: VersionUpdater::Status::UPDATED;
break;
case updater::UpdateService::UpdateState::State::kUpdateError:
+ VLOG(0) << "VersionUpdaterMac: Update error, code: " << update_state.error_code;
+ // Log only errors
+ VLOG(1) << "Update error, code: " << update_state.error_code;
switch (update_state.error_code) {
case updater::GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY:
+ VLOG(0) << "VersionUpdaterMac: Updates disabled by policy";
status = VersionUpdater::Status::DISABLED_BY_ADMIN;
err_message =
l10n_util::GetStringUTF16(IDS_UPGRADE_DISABLED_BY_POLICY);
break;
case updater::GOOPDATE_E_APP_UPDATE_DISABLED_BY_POLICY_MANUAL:
+ VLOG(0) << "VersionUpdaterMac: Manual updates disabled by policy";
status = VersionUpdater::Status::DISABLED_BY_ADMIN;
err_message =
l10n_util::GetStringUTF16(IDS_UPGRADE_DISABLED_BY_POLICY_MANUAL);
break;
default:
+ VLOG(0) << "VersionUpdaterMac: Generic update error";
status = VersionUpdater::Status::FAILED;
err_message = l10n_util::GetStringFUTF16(
IDS_ABOUT_BOX_ERROR_DURING_UPDATE_CHECK,
@@ -98,11 +119,14 @@ void UpdateStatus(VersionUpdater::StatusCallback status_callback,
}
break;
case updater::UpdateService::UpdateState::State::kNotStarted:
+ VLOG(0) << "VersionUpdaterMac: Update not started";
[[fallthrough]];
case updater::UpdateService::UpdateState::State::kUnknown:
+ VLOG(0) << "VersionUpdaterMac: Unknown update state";
return;
}
+ VLOG(0) << "VersionUpdaterMac: Calling status callback with status: " << static_cast<int>(status);
status_callback.Run(status, progress, false, false, version, 0, err_message);
}
@@ -118,9 +142,11 @@ class VersionUpdaterMac : public VersionUpdater {
// VersionUpdater implementation.
void CheckForUpdate(StatusCallback status_callback,
PromoteCallback promote_callback) override {
+ VLOG(0) << "VersionUpdaterMac: Checking for updates";
EnsureUpdater(
base::BindOnce(
[](PromoteCallback prompt) {
+ VLOG(0) << "VersionUpdaterMac: Promote callback - enabling promotion";
prompt.Run(PromotionState::PROMOTE_ENABLED);
},
promote_callback),
@@ -128,6 +154,7 @@ class VersionUpdaterMac : public VersionUpdater {
[](base::RepeatingCallback<void(
const updater::UpdateService::UpdateState&)>
status_callback) {
+ VLOG(0) << "VersionUpdaterMac: Starting update check process";
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock()},
base::BindOnce(&GetBrowserUpdaterScope),
@@ -136,6 +163,7 @@ class VersionUpdaterMac : public VersionUpdater {
const updater::UpdateService::UpdateState&)>
status_callback,
updater::UpdaterScope scope) {
+ VLOG(0) << "VersionUpdaterMac: Creating browser updater client for scope: " << static_cast<int>(scope);
BrowserUpdaterClient::Create(scope)->CheckForUpdate(
status_callback);
},
@@ -143,12 +171,30 @@ class VersionUpdaterMac : public VersionUpdater {
@@ -143,12 +154,26 @@ class VersionUpdaterMac : public VersionUpdater {
},
base::BindRepeating(&UpdateStatus, status_callback)));
}
- void PromoteUpdater() override { SetupSystemUpdater(); }
+ void PromoteUpdater() override {
+ VLOG(0) << "VersionUpdaterMac: Promoting updater";
+ SetupSystemUpdater();
+ }
};
@@ -840,20 +909,17 @@ index 992157e28e8f5..4c5949b1d266a 100644
std::unique_ptr<VersionUpdater> VersionUpdater::Create(
content::WebContents* /* web_contents */) {
+#if BUILDFLAG(ENABLE_SPARKLE)
+ VLOG(0) << "VersionUpdater: Checking if Sparkle updater is available";
+ // Use Sparkle updater if it's enabled
+ if (sparkle_glue::SparkleEnabled()) {
+ VLOG(0) << "VersionUpdater: Using Sparkle updater";
+ VLOG(0) << "Using Sparkle updater";
+ return base::WrapUnique(new SparkleVersionUpdater());
+ } else {
+ VLOG(0) << "VersionUpdater: Sparkle not available, falling back to default updater";
+ }
+#else
+ VLOG(0) << "VersionUpdater: Sparkle not compiled in, using default updater";
+ else {
+ VLOG(1) << "Sparkle updater not available, using default updater";
+ }
+#endif
+
+ // Otherwise use the default Chromium updater
+ VLOG(0) << "VersionUpdater: Using default Chromium updater";
return base::WrapUnique(new VersionUpdaterMac());
}
diff --git a/third_party/sparkle/BUILD.gn b/third_party/sparkle/BUILD.gn