auto_updater_mac.mm 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. // Copyright (c) 2013 GitHub, Inc.
  2. // Use of this source code is governed by the MIT license that can be
  3. // found in the LICENSE file.
  4. #include "shell/browser/auto_updater.h"
  5. #include <string>
  6. #import <ReactiveObjC/NSObject+RACPropertySubscribing.h>
  7. #import <ReactiveObjC/RACCommand.h>
  8. #import <ReactiveObjC/RACSignal.h>
  9. #import <Squirrel/Squirrel.h>
  10. #include "base/functional/bind.h"
  11. #include "base/strings/sys_string_conversions.h"
  12. #include "base/time/time.h"
  13. #include "gin/arguments.h"
  14. #include "shell/browser/browser.h"
  15. #include "shell/common/gin_converters/value_converter.h"
  16. #include "shell/common/gin_helper/dictionary.h"
  17. #include "shell/common/gin_helper/error_thrower.h"
  18. namespace auto_updater {
  19. namespace {
  20. // The global SQRLUpdater object.
  21. SQRLUpdater* __strong g_updater = nil;
  22. } // namespace
  23. namespace {
  24. bool g_update_available = false;
  25. std::string update_url_ = ""; // NOLINT(runtime/string)
  26. } // namespace
  27. std::string AutoUpdater::GetFeedURL() {
  28. return update_url_;
  29. }
  30. // static
  31. void AutoUpdater::SetFeedURL(gin::Arguments* args) {
  32. gin_helper::ErrorThrower thrower(args->isolate());
  33. gin_helper::Dictionary opts;
  34. std::string feed;
  35. HeaderMap requestHeaders;
  36. std::string serverType = "default";
  37. v8::Local<v8::Value> first_arg = args->PeekNext();
  38. if (!first_arg.IsEmpty() && first_arg->IsString()) {
  39. if (args->GetNext(&feed)) {
  40. args->GetNext(&requestHeaders);
  41. }
  42. } else if (args->GetNext(&opts)) {
  43. if (!opts.Get("url", &feed)) {
  44. thrower.ThrowError(
  45. "Expected options object to contain a 'url' string property in "
  46. "setFeedUrl call");
  47. return;
  48. }
  49. opts.Get("headers", &requestHeaders);
  50. opts.Get("serverType", &serverType);
  51. if (serverType != "default" && serverType != "json") {
  52. thrower.ThrowError("Expected serverType to be 'default' or 'json'");
  53. return;
  54. }
  55. } else {
  56. thrower.ThrowError(
  57. "Expected an options object with a 'url' property to be provided");
  58. return;
  59. }
  60. Delegate* delegate = GetDelegate();
  61. if (!delegate)
  62. return;
  63. update_url_ = feed;
  64. NSURL* url = [NSURL URLWithString:base::SysUTF8ToNSString(feed)];
  65. NSMutableURLRequest* urlRequest = [NSMutableURLRequest requestWithURL:url];
  66. for (const auto& it : requestHeaders) {
  67. [urlRequest setValue:base::SysUTF8ToNSString(it.second)
  68. forHTTPHeaderField:base::SysUTF8ToNSString(it.first)];
  69. }
  70. if (g_updater)
  71. g_updater = nil;
  72. // Initialize the SQRLUpdater.
  73. @try {
  74. if (serverType == "json") {
  75. NSString* nsAppVersion =
  76. base::SysUTF8ToNSString(electron::Browser::Get()->GetVersion());
  77. g_updater = [[SQRLUpdater alloc] initWithUpdateRequest:urlRequest
  78. forVersion:nsAppVersion];
  79. } else {
  80. // default
  81. g_updater = [[SQRLUpdater alloc] initWithUpdateRequest:urlRequest];
  82. }
  83. } @catch (NSException* error) {
  84. delegate->OnError(base::SysNSStringToUTF8(error.reason));
  85. return;
  86. }
  87. [[g_updater rac_valuesForKeyPath:@"state" observer:g_updater]
  88. subscribeNext:^(NSNumber* stateNumber) {
  89. int state = [stateNumber integerValue];
  90. // Dispatching the event on main thread.
  91. dispatch_async(dispatch_get_main_queue(), ^{
  92. if (state == SQRLUpdaterStateCheckingForUpdate)
  93. delegate->OnCheckingForUpdate();
  94. else if (state == SQRLUpdaterStateDownloadingUpdate)
  95. delegate->OnUpdateAvailable();
  96. });
  97. }];
  98. }
  99. // static
  100. void AutoUpdater::CheckForUpdates() {
  101. Delegate* delegate = GetDelegate();
  102. if (!delegate)
  103. return;
  104. [[[[g_updater.checkForUpdatesCommand execute:nil]
  105. // Send a `nil` after everything...
  106. concat:[RACSignal return:nil]]
  107. // But only take the first value. If an update is sent, we'll get that.
  108. // Otherwise, we'll get our inserted `nil` value.
  109. take:1]
  110. subscribeNext:^(SQRLDownloadedUpdate* downloadedUpdate) {
  111. if (downloadedUpdate) {
  112. g_update_available = true;
  113. SQRLUpdate* update = downloadedUpdate.update;
  114. // There is a new update that has been downloaded.
  115. delegate->OnUpdateDownloaded(
  116. base::SysNSStringToUTF8(update.releaseNotes),
  117. base::SysNSStringToUTF8(update.releaseName),
  118. base::Time::FromSecondsSinceUnixEpoch(
  119. update.releaseDate.timeIntervalSince1970),
  120. base::SysNSStringToUTF8(update.updateURL.absoluteString));
  121. } else {
  122. g_update_available = false;
  123. // When the completed event is sent with no update, then we know there
  124. // is no update available.
  125. delegate->OnUpdateNotAvailable();
  126. }
  127. }
  128. error:^(NSError* error) {
  129. NSMutableString* failureString =
  130. [NSMutableString stringWithString:error.localizedDescription];
  131. if (error.localizedFailureReason) {
  132. [failureString appendString:@": "];
  133. [failureString appendString:error.localizedFailureReason];
  134. }
  135. if (error.localizedRecoverySuggestion) {
  136. if (![failureString hasSuffix:@"."])
  137. [failureString appendString:@"."];
  138. [failureString appendString:@" "];
  139. [failureString appendString:error.localizedRecoverySuggestion];
  140. }
  141. delegate->OnError(base::SysNSStringToUTF8(failureString), error.code,
  142. base::SysNSStringToUTF8(error.domain));
  143. }];
  144. }
  145. void AutoUpdater::QuitAndInstall() {
  146. Delegate* delegate = AutoUpdater::GetDelegate();
  147. if (g_update_available) {
  148. [[g_updater relaunchToInstallUpdate] subscribeError:^(NSError* error) {
  149. if (delegate)
  150. delegate->OnError(base::SysNSStringToUTF8(error.localizedDescription),
  151. error.code, base::SysNSStringToUTF8(error.domain));
  152. }];
  153. } else {
  154. if (delegate)
  155. delegate->OnError("No update available, can't quit and install");
  156. }
  157. }
  158. bool AutoUpdater::IsVersionAllowedForUpdate(const std::string& current_version,
  159. const std::string& target_version) {
  160. return [SQRLUpdater
  161. isVersionAllowedForUpdate:base::SysUTF8ToNSString(target_version)
  162. from:base::SysUTF8ToNSString(current_version)];
  163. }
  164. } // namespace auto_updater