Commit 6dc34aee authored by Michael Chang's avatar Michael Chang Committed by Commit Bot

Integrate Chromium Updater to the browser

The purpose of this CL is to implement an integration between the
browser and the new Chromium updater. Everything related to the Chromium
updater is behind a build flag, so that the functionality can be easily
turned on and off. This CL is more mac oriented in its implementation.
Implementation ranges from installing the updater, promoting the updater
to be the latest, registering the browser to the updater and requesting
on-demand updates to the updater through chrome://settings/help page.

Bug: 1063435,1048654
Change-Id: I36154dc6e93a31d491d81ad038e78b37e057116a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2340874Reviewed-by: default avatarNico Weber <thakis@chromium.org>
Reviewed-by: default avatarSorin Jianu <sorin@chromium.org>
Reviewed-by: default avatarJoshua Pawlicki <waffles@chromium.org>
Reviewed-by: default avatarRobert Sesek <rsesek@chromium.org>
Commit-Queue: Michael Chang <donchan@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#826472}
parent b13c87d5
......@@ -41,6 +41,8 @@ if (is_android) {
import("//build/config/mac/symbols.gni")
import("//build/util/branding.gni")
import("//build/util/version.gni")
import("//chrome/browser/buildflags.gni")
import("//chrome/updater/branding.gni")
import("//content/public/app/mac_helpers.gni")
import("//media/cdm/library_cdm/cdm_paths.gni")
import("//third_party/icu/config.gni")
......@@ -730,6 +732,20 @@ if (is_win) {
}
}
if (enable_chromium_updater) {
# A stable updater from CIPD is bundled with official builds only. For
# local builds, a locally built updater will be bundled into the app.
# TODO(crbug.com/1134642): Add ChromiumUpdater app to CIPD for use in
# official builds.
sources += [ "$root_out_dir/${updater_product_full_name}.app" ]
public_deps += [
"//chrome/updater/mac:browser_install_script",
"//chrome/updater/mac:updater_bundle",
"//chrome/updater/mac:updater_install_script",
]
}
if (is_asan) {
# crashpad_handler requires the ASan runtime at its @executable_path.
sources += [ "$root_out_dir/libclang_rt.asan_osx_dynamic.dylib" ]
......
......@@ -78,6 +78,7 @@ buildflag_header("buildflags") {
flags = [
"ENABLE_KALEIDOSCOPE=$enable_kaleidoscope",
"USE_MINIKIN_HYPHENATION=$use_minikin_hyphenation",
"ENABLE_CHROMIUM_UPDATER=$enable_chromium_updater",
"USE_THIN_LTO=$use_thin_lto",
]
if (is_win) {
......@@ -4876,6 +4877,13 @@ static_library("browser") {
"SafariServices.framework", # macOS 10.12
"UserNotifications.framework", # macOS 10.14
]
if (enable_chromium_updater) {
sources += [
"mac/install_updater.h",
"mac/install_updater.mm",
]
}
}
if (is_linux || is_chromeos) {
......@@ -4976,6 +4984,10 @@ static_library("browser") {
"recovery/recovery_install_global_error_factory.cc",
"recovery/recovery_install_global_error_factory.h",
]
if (enable_chromium_updater) {
deps += [ "//chrome/browser/updater:browser_updater_client" ]
}
} else {
# On other platforms, use the generic implementation.
sources += [ "hang_monitor/hang_crash_dump.cc" ]
......
......@@ -9,5 +9,13 @@ declare_args() {
# code.
enable_kaleidoscope = !is_android && is_chrome_branded
# Chromium Updater is a cross-platform updater for desktop clients built using
# Chromium code and tools. Code is in //chrome/updater. The design doc is
# located at http://bit.ly/chromium-updater. Chrome is currently installed and
# updated with proprietary updater (Omaha & Keystone). This build flag allows
# integration with the open source, cross-platform Chromium updater.
# TODO(crbug.com/1054060)
enable_chromium_updater = false
tpm_fallback = false
}
......@@ -20,6 +20,7 @@
#include "chrome/browser/apps/app_shim/app_shim_listener.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_platform_part.h"
#include "chrome/browser/buildflags.h"
#import "chrome/browser/chrome_browser_application_mac.h"
#include "chrome/browser/first_run/first_run.h"
#include "chrome/browser/mac/install_from_dmg.h"
......@@ -42,6 +43,10 @@
#include "ui/base/resource/resource_handle.h"
#include "ui/native_theme/native_theme_mac.h"
#if BUILDFLAG(ENABLE_CHROMIUM_UPDATER)
#include "chrome/browser/mac/install_updater.h"
#endif // BUILDFLAG(ENABLE_CHROMIUM_UPDATER)
// ChromeBrowserMainPartsMac ---------------------------------------------------
ChromeBrowserMainPartsMac::ChromeBrowserMainPartsMac(
......@@ -75,9 +80,13 @@ void ChromeBrowserMainPartsMac::PreMainMessageLoopStart() {
// point (needed to load the nib).
CHECK(ui::ResourceBundle::HasSharedInstance());
#if BUILDFLAG(ENABLE_CHROMIUM_UPDATER)
InstallUpdaterAndRegisterBrowser();
#else
// This is a no-op if the KeystoneRegistration framework is not present.
// The framework is only distributed with branded Google Chrome builds.
[[KeystoneGlue defaultKeystoneGlue] registerWithKeystone];
#endif // BUILDFLAG(ENABLE_CHROMIUM_UPDATER)
// Disk image installation is sort of a first-run task, so it shares the
// no first run switches.
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_MAC_INSTALL_UPDATER_H_
#define CHROME_BROWSER_MAC_INSTALL_UPDATER_H_
// Invokes the executable within the updater bundle at
// ../Versions/$(Version)/Helpers/Updater.app/Contents/Updater with the argument
// --install. That will copy the updater to its install directory and set up
// launchd plists. After the updater is installed, the IPC Registration API is
// invoked to register the browser to the updater.
void InstallUpdaterAndRegisterBrowser();
#endif // CHROME_BROWSER_MAC_INSTALL_UPDATER_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/mac/install_updater.h"
#include <string>
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/mac/bundle_locations.h"
#include "base/process/launch.h"
#include "base/process/process.h"
#include "base/strings/strcat.h"
#include "base/task/post_task.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/time/time.h"
#include "chrome/browser/updater/browser_updater_client.h"
#include "chrome/browser/updater/browser_updater_client_util.h"
namespace {
constexpr char kInstallCommand[] = "install";
int RunCommand(const base::FilePath& exe_path, const char* cmd_switch) {
base::CommandLine command(exe_path);
command.AppendSwitch(cmd_switch);
int exit_code = -1;
auto process = base::LaunchProcess(command, {});
if (!process.IsValid())
return exit_code;
process.WaitForExitWithTimeout(base::TimeDelta::FromSeconds(120), &exit_code);
return exit_code;
}
base::FilePath GetUpdaterExecutableName() {
return base::FilePath(base::StrCat({kUpdaterName, ".app"}))
.Append(FILE_PATH_LITERAL("Contents"))
.Append(FILE_PATH_LITERAL("MacOS"))
.Append(kUpdaterName);
}
} // namespace
void InstallUpdaterAndRegisterBrowser() {
base::ThreadPool::PostTask(
FROM_HERE,
{base::MayBlock(), base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
base::BindOnce([] {
// The updater executable should be in
// BRANDING.app/Contents/Frameworks/BRANDING.framework/Versions/V/
// Helpers/Updater.app/Contents/MacOS/Updater
const base::FilePath updater_executable_path =
base::mac::FrameworkBundlePath()
.Append(FILE_PATH_LITERAL("Helpers"))
.Append(GetUpdaterExecutableName());
if (!base::PathExists(updater_executable_path)) {
VLOG(1) << "The updater does not exist in the bundle.";
return;
}
int exit_code = RunCommand(updater_executable_path, kInstallCommand);
if (exit_code != 0) {
VLOG(1) << "Couldn't install the updater. Exit code: " << exit_code;
return;
}
BrowserUpdaterClient::Create()->Register();
}));
}
......@@ -3096,6 +3096,13 @@ static_library("ui") {
"Carbon.framework",
"Quartz.framework",
]
if (enable_chromium_updater) {
deps += [
"//chrome/browser/updater:browser_updater_client",
"//chrome/updater:browser_sources",
]
}
}
if (is_win) {
......
include_rules = [
"+chrome/browser/image_decoder",
"-chrome/browser/ui/views",
"+chrome/updater",
"+components/browser_ui/client_certificate",
"+components/browser_ui/util/android/url_constants.h",
"+components/country_codes",
......
......@@ -10,8 +10,16 @@
#include "base/compiler_specific.h"
#include "base/mac/scoped_nsobject.h"
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "chrome/browser/buildflags.h"
#include "chrome/browser/ui/webui/help/version_updater.h"
#if BUILDFLAG(ENABLE_CHROMIUM_UPDATER)
#include "chrome/updater/update_service.h" // nogncheck
class BrowserUpdaterClient;
#endif // BUILDFLAG(ENABLE_CHROMIUM_UPDATER)
@class KeystoneObserver;
// OS X implementation of version update functionality, used by the WebUI
......@@ -52,6 +60,12 @@ class VersionUpdaterMac : public VersionUpdater {
// The observer that will receive keystone status updates.
base::scoped_nsobject<KeystoneObserver> keystone_observer_;
#if BUILDFLAG(ENABLE_CHROMIUM_UPDATER)
// Instance of the BrowserUpdaterClient used to update the browser with the
// new updater.
scoped_refptr<BrowserUpdaterClient> update_client_;
#endif // BUILDFLAG(ENABLE_CHROMIUM_UPDATER)
DISALLOW_COPY_AND_ASSIGN(VersionUpdaterMac);
};
......
......@@ -4,12 +4,15 @@
#include "chrome/browser/ui/webui/help/version_updater_mac.h"
#include <string>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/logging.h"
#include "base/mac/foundation_util.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/buildflags.h"
#import "chrome/browser/mac/keystone_glue.h"
#include "chrome/browser/obsolete_system/obsolete_system.h"
#include "chrome/grit/chromium_strings.h"
......@@ -17,6 +20,13 @@
#include "net/base/escape.h"
#include "ui/base/l10n/l10n_util.h"
#if BUILDFLAG(ENABLE_CHROMIUM_UPDATER)
#include "base/numerics/ranges.h"
#include "base/strings/stringprintf.h"
#include "chrome/browser/updater/browser_updater_client.h" // nogncheck
#include "chrome/updater/update_service.h" // nogncheck
#endif // BUILDFLAG(ENABLE_CHROMIUM_UPDATER)
// KeystoneObserver is a simple notification observer for Keystone status
// updates. It will be created and managed by VersionUpdaterMac.
@interface KeystoneObserver : NSObject {
......@@ -57,6 +67,63 @@
@end // @implementation KeystoneObserver
#if BUILDFLAG(ENABLE_CHROMIUM_UPDATER)
namespace {
int GetDownloadProgress(int64_t downloaded_bytes, int64_t total_bytes) {
if (downloaded_bytes < 0 || total_bytes <= 0)
return -1;
return 100 *
base::ClampToRange(double{downloaded_bytes} / total_bytes, 0.0, 1.0);
}
void UpdateStatusFromChromiumUpdater(
const VersionUpdater::StatusCallback& status_callback,
updater::UpdateService::UpdateState update_state) {
VersionUpdater::Status status = VersionUpdater::Status::CHECKING;
int progress = 0;
std::string version;
std::string err_message;
switch (update_state.state) {
case updater::UpdateService::UpdateState::State::kCheckingForUpdates:
FALLTHROUGH;
case updater::UpdateService::UpdateState::State::kUpdateAvailable:
status = VersionUpdater::Status::CHECKING;
break;
case updater::UpdateService::UpdateState::State::kDownloading:
progress = GetDownloadProgress(update_state.downloaded_bytes,
update_state.total_bytes);
FALLTHROUGH;
case updater::UpdateService::UpdateState::State::kInstalling:
status = VersionUpdater::Status::UPDATING;
break;
case updater::UpdateService::UpdateState::State::kUpdated:
status = VersionUpdater::Status::NEARLY_UPDATED;
break;
case updater::UpdateService::UpdateState::State::kNoUpdate:
status = VersionUpdater::Status::UPDATED;
break;
case updater::UpdateService::UpdateState::State::kUpdateError:
status = VersionUpdater::Status::FAILED;
// TODO(https://crbug.com/1146201): Localize error string.
err_message = base::StringPrintf(
"An error occurred. (Error code: %d) (Extra code: %d)",
update_state.error_code, update_state.extra_code1);
break;
case updater::UpdateService::UpdateState::State::kNotStarted:
FALLTHROUGH;
case updater::UpdateService::UpdateState::State::kUnknown:
return;
}
status_callback.Run(status, progress, false, false, version, 0,
base::UTF8ToUTF16(err_message));
}
} // namespace
#endif // BUILDFLAG(ENABLE_CHROMIUM_UPDATER)
VersionUpdater* VersionUpdater::Create(
content::WebContents* /* web_contents */) {
return new VersionUpdaterMac;
......@@ -64,17 +131,21 @@ VersionUpdater* VersionUpdater::Create(
VersionUpdaterMac::VersionUpdaterMac()
: show_promote_button_(false),
keystone_observer_([[KeystoneObserver alloc] initWithUpdater:this]) {
}
keystone_observer_([[KeystoneObserver alloc] initWithUpdater:this]) {}
VersionUpdaterMac::~VersionUpdaterMac() {
}
VersionUpdaterMac::~VersionUpdaterMac() {}
void VersionUpdaterMac::CheckForUpdate(
const StatusCallback& status_callback,
const PromoteCallback& promote_callback) {
// Copy the callbacks, we will re-use this for the remaining lifetime
// of this object.
#if BUILDFLAG(ENABLE_CHROMIUM_UPDATER)
if (!update_client_)
update_client_ = BrowserUpdaterClient::Create();
update_client_->CheckForUpdate(
base::BindRepeating(&UpdateStatusFromChromiumUpdater, status_callback));
return;
#else
status_callback_ = status_callback;
promote_callback_ = promote_callback;
......@@ -111,6 +182,7 @@ void VersionUpdaterMac::CheckForUpdate(
status_callback_.Run(DISABLED, 0, false, false, std::string(), 0,
base::string16());
}
#endif // BUILDFLAG(ENABLE_CHROMIUM_UPDATER)
}
void VersionUpdaterMac::PromoteUpdater() const {
......@@ -130,11 +202,11 @@ void VersionUpdaterMac::PromoteUpdater() const {
}
void VersionUpdaterMac::UpdateStatus(NSDictionary* dictionary) {
AutoupdateStatus keystone_status = static_cast<AutoupdateStatus>(
[base::mac::ObjCCastStrict<NSNumber>(
AutoupdateStatus keystone_status =
static_cast<AutoupdateStatus>([base::mac::ObjCCastStrict<NSNumber>(
[dictionary objectForKey:kAutoupdateStatusStatus]) intValue]);
std::string error_messages = base::SysNSStringToUTF8(
base::mac::ObjCCastStrict<NSString>(
std::string error_messages =
base::SysNSStringToUTF8(base::mac::ObjCCastStrict<NSString>(
[dictionary objectForKey:kAutoupdateStatusErrorMessages]));
bool enable_promote_button = true;
......@@ -192,19 +264,16 @@ void VersionUpdaterMac::UpdateStatus(NSDictionary* dictionary) {
case kAutoupdateInstallFailed:
case kAutoupdatePromoteFailed:
status = FAILED;
message = l10n_util::GetStringFUTF16Int(IDS_UPGRADE_ERROR,
keystone_status);
message =
l10n_util::GetStringFUTF16Int(IDS_UPGRADE_ERROR, keystone_status);
break;
case kAutoupdateNeedsPromotion:
{
status = FAILED;
base::string16 product_name =
l10n_util::GetStringUTF16(IDS_PRODUCT_NAME);
message = l10n_util::GetStringFUTF16(IDS_PROMOTE_INFOBAR_TEXT,
product_name);
}
break;
case kAutoupdateNeedsPromotion: {
status = FAILED;
base::string16 product_name = l10n_util::GetStringUTF16(IDS_PRODUCT_NAME);
message =
l10n_util::GetStringFUTF16(IDS_PROMOTE_INFOBAR_TEXT, product_name);
} break;
default:
NOTREACHED();
......@@ -242,8 +311,8 @@ void VersionUpdaterMac::UpdateStatus(NSDictionary* dictionary) {
promotion_state = PROMOTE_HIDDEN;
if (show_promote_button_) {
promotion_state = enable_promote_button ? PROMOTE_ENABLED
: PROMOTE_DISABLED;
promotion_state =
enable_promote_button ? PROMOTE_ENABLED : PROMOTE_DISABLED;
}
}
......
# Copyright 2019 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
source_set("browser_updater_client") {
sources = [
"browser_updater_client.cc",
"browser_updater_client.h",
"browser_updater_client_util.cc",
"browser_updater_client_util.h",
]
if (is_mac) {
sources += [
"browser_updater_client_mac.h",
"browser_updater_client_mac.mm",
"browser_updater_client_util_mac.mm",
]
}
deps = [
"//base",
"//chrome/browser:buildflags",
"//chrome/common:version_header",
"//chrome/updater:browser_sources",
"//components/version_info",
]
}
include_rules = [
"+chrome/updater",
]
sorin@chromium.org
waffles@chromium.org
donchan@microsoft.com
# COMPONENT: Internals>Updater
# TEAM: chrome-updates-dev@chromium.org
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/updater/browser_updater_client.h"
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/callback.h"
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/post_task.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "chrome/updater/update_service.h"
#include "components/version_info/version_info.h"
BrowserUpdaterClient::BrowserUpdaterClient()
: callback_task_runner_(base::SequencedTaskRunnerHandle::Get()) {}
BrowserUpdaterClient::~BrowserUpdaterClient() = default;
void BrowserUpdaterClient::Register() {
BeginRegister(
{}, {}, version_info::GetVersionNumber(),
base::BindOnce(&BrowserUpdaterClient::RegistrationCompleted, this));
}
void BrowserUpdaterClient::RegistrationCompleted(
updater::UpdateService::Result result) {
if (result != updater::UpdateService::Result::kSuccess) {
VLOG(1) << "Updater registration error: " << result;
}
}
void BrowserUpdaterClient::CheckForUpdate(
base::RepeatingCallback<void(updater::UpdateService::UpdateState)>
version_updater_callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
updater::UpdateService::UpdateState update_state;
update_state.state =
updater::UpdateService::UpdateState::State::kCheckingForUpdates;
callback_task_runner_->PostTask(
FROM_HERE, base::BindRepeating(version_updater_callback, update_state));
BeginUpdateCheck(
base::BindRepeating(&BrowserUpdaterClient::HandleStatusUpdate, this,
std::move(version_updater_callback)),
base::BindOnce(&BrowserUpdaterClient::UpdateCompleted, this,
std::move(version_updater_callback)));
}
void BrowserUpdaterClient::HandleStatusUpdate(
base::RepeatingCallback<void(updater::UpdateService::UpdateState)> callback,
updater::UpdateService::UpdateState update_state) {
callback_task_runner_->PostTask(FROM_HERE,
base::BindRepeating(callback, update_state));
}
void BrowserUpdaterClient::UpdateCompleted(
base::RepeatingCallback<void(updater::UpdateService::UpdateState)> callback,
updater::UpdateService::Result result) {
VLOG(1) << "Result of update was: " << result;
if (result != updater::UpdateService::Result::kSuccess) {
updater::UpdateService::UpdateState update_state;
update_state.state =
updater::UpdateService::UpdateState::State::kUpdateError;
update_state.error_category =
updater::UpdateService::ErrorCategory::kUpdateCheck;
update_state.error_code = static_cast<int>(result);
callback_task_runner_->PostTask(
FROM_HERE, base::BindRepeating(callback, update_state));
}
}
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UPDATER_BROWSER_UPDATER_CLIENT_H_
#define CHROME_BROWSER_UPDATER_BROWSER_UPDATER_CLIENT_H_
#include <string>
#include "base/callback.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_refptr.h"
#include "base/sequence_checker.h"
#include "base/sequenced_task_runner.h"
#include "chrome/updater/update_service.h"
// Cross-platform client to communicate between the browser and the Chromium
// updater. It helps the browser register to the Chromium updater and invokes
// on-demand updates.
class BrowserUpdaterClient
: public base::RefCountedThreadSafe<BrowserUpdaterClient> {
public:
static scoped_refptr<BrowserUpdaterClient> Create();
BrowserUpdaterClient();
// Registers the browser to the Chromium updater via IPC registration API.
// When registration is completed, it will call RegistrationCompleted().
void Register();
// Begins the process of an on-demand update from the Chromium updater via IPC
// update API. It will periodically get updates via HandleStatusUpdate(). When
// update is completed, it will call UpdateCompleted().
void CheckForUpdate(
base::RepeatingCallback<void(updater::UpdateService::UpdateState)>
version_updater_callback);
protected:
friend class base::RefCountedThreadSafe<BrowserUpdaterClient>;
virtual ~BrowserUpdaterClient();
scoped_refptr<base::SequencedTaskRunner> task_runner() {
return callback_task_runner_;
}
private:
SEQUENCE_CHECKER(sequence_checker_);
// Helper method for Register() to be implemented by each platform to initiate
// the registration.
virtual void BeginRegister(const std::string& brand_code,
const std::string& tag,
const std::string& version,
updater::UpdateService::Callback callback) = 0;
// Helper method for CheckForUpdate() to be implemented by each platform to
// initiate on-demand updates.
virtual void BeginUpdateCheck(
updater::UpdateService::StateChangeCallback state_change,
updater::UpdateService::Callback callback) = 0;
// Handles status updates from the Chromium Updater during an on-demand
// update. The updater::UpdateService::UpdateState is translated into a
// VersionUpdater::StatusCallback.
void HandleStatusUpdate(
base::RepeatingCallback<void(updater::UpdateService::UpdateState)>
callback,
updater::UpdateService::UpdateState update_state);
// Handles status update from Chromium updater when registration is completed.
void RegistrationCompleted(updater::UpdateService::Result result);
// Handles status update from Chromium updater when updates are completed.
void UpdateCompleted(base::RepeatingCallback<
void(updater::UpdateService::UpdateState)> callback,
updater::UpdateService::Result result);
scoped_refptr<base::SequencedTaskRunner> callback_task_runner_;
};
#endif // CHROME_BROWSER_UPDATER_BROWSER_UPDATER_CLIENT_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UPDATER_BROWSER_UPDATER_CLIENT_MAC_H_
#define CHROME_BROWSER_UPDATER_BROWSER_UPDATER_CLIENT_MAC_H_
#include <string>
#include "base/mac/scoped_nsobject.h"
#include "base/sequence_checker.h"
#include "chrome/browser/updater/browser_updater_client.h"
#import "chrome/updater/app/server/mac/service_protocol.h"
#include "chrome/updater/update_service.h"
@interface CRUUpdateClientOnDemandImpl : NSObject <CRUUpdateChecking>
@end
class BrowserUpdaterClientMac : public BrowserUpdaterClient {
public:
BrowserUpdaterClientMac();
explicit BrowserUpdaterClientMac(
base::scoped_nsobject<CRUUpdateClientOnDemandImpl> client);
private:
~BrowserUpdaterClientMac() override;
SEQUENCE_CHECKER(sequence_checker_);
// BrowserUpdaterClient.
void BeginRegister(const std::string& brand_code,
const std::string& tag,
const std::string& version,
updater::UpdateService::Callback callback) override;
void BeginUpdateCheck(
updater::UpdateService::StateChangeCallback state_change,
updater::UpdateService::Callback callback) override;
base::scoped_nsobject<CRUUpdateClientOnDemandImpl> client_;
};
#endif // CHROME_BROWSER_UPDATER_BROWSER_UPDATER_CLIENT_MAC_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/updater/browser_updater_client_mac.h"
#import <Foundation/Foundation.h>
#include <string>
#include <utility>
#include "base/callback.h"
#include "base/logging.h"
#include "base/mac/bundle_locations.h"
#include "base/mac/foundation_util.h"
#include "base/memory/scoped_refptr.h"
#include "base/notreached.h"
#include "base/strings/strcat.h"
#include "base/strings/sys_string_conversions.h"
#include "base/task/post_task.h"
#import "chrome/updater/app/server/mac/update_service_wrappers.h"
#include "chrome/updater/update_service.h"
@interface CRUUpdateClientOnDemandImpl () {
base::scoped_nsobject<NSXPCConnection> _xpcConnection;
}
@end
namespace {
NSString* const kLaunchdServiceName = @"org.chromium.ChromiumUpdater.service";
NSString* GetMachServiceName() {
return [kLaunchdServiceName
stringByAppendingFormat:@".%lu", [kLaunchdServiceName hash]];
}
NSString* GetAppIdForUpdaterAsNSString() {
return base::SysUTF8ToNSString(base::mac::BaseBundleID());
}
} // namespace
@implementation CRUUpdateClientOnDemandImpl
- (instancetype)init {
if (self = [super init]) {
_xpcConnection.reset([[NSXPCConnection alloc]
initWithMachServiceName:GetMachServiceName()
options:0]);
_xpcConnection.get().remoteObjectInterface =
updater::GetXPCUpdateCheckingInterface();
_xpcConnection.get().interruptionHandler = ^{
LOG(WARNING)
<< "CRUUpdateClientOnDemandImpl: XPC connection interrupted.";
};
_xpcConnection.get().invalidationHandler = ^{
LOG(WARNING)
<< "CRUUpdateClientOnDemandImpl: XPC connection invalidated.";
};
[_xpcConnection resume];
}
return self;
}
- (void)getVersionWithReply:
(void (^_Nonnull)(NSString* _Nullable version))reply {
auto errorHandler = ^(NSError* xpcError) {
LOG(ERROR) << "XPC Connection failed: "
<< base::SysNSStringToUTF8([xpcError description]);
reply(nil);
};
[[_xpcConnection remoteObjectProxyWithErrorHandler:errorHandler]
getVersionWithReply:reply];
}
- (void)registerForUpdatesWithAppId:(NSString* _Nullable)appId
brandCode:(NSString* _Nullable)brandCode
tag:(NSString* _Nullable)tag
version:(NSString* _Nullable)version
existenceCheckerPath:(NSString* _Nullable)existenceCheckerPath
reply:(void (^_Nonnull)(int rc))reply {
auto errorHandler = ^(NSError* xpcError) {
LOG(ERROR) << "XPC Connection failed: "
<< base::SysNSStringToUTF8([xpcError description]);
reply(
static_cast<int>(updater::UpdateService::Result::kIPCConnectionFailed));
};
[[_xpcConnection remoteObjectProxyWithErrorHandler:errorHandler]
registerForUpdatesWithAppId:appId
brandCode:brandCode
tag:tag
version:version
existenceCheckerPath:existenceCheckerPath
reply:reply];
}
// Checks for updates and returns the result in the reply block.
- (void)checkForUpdatesWithUpdateState:
(CRUUpdateStateObserver* _Nonnull)updateState
reply:(void (^_Nonnull)(int rc))reply {
// The method stub needs to be implemented as it is part of the
// CRUUpdateChecking protocol. However, this method does not need to be
// implemented because checkForUpdatesWithAppId:priority:updateState:reply:
// gives us all the functionality we need for on-demand updates.
NOTIMPLEMENTED();
}
// Checks for update of a given app, with specified priority. Sends repeated
// updates of progress and returns the result in the reply block.
- (void)checkForUpdateWithAppID:(NSString* _Nonnull)appID
priority:(CRUPriorityWrapper* _Nonnull)priority
updateState:(CRUUpdateStateObserver* _Nonnull)updateState
reply:(void (^_Nonnull)(int rc))reply {
auto errorHandler = ^(NSError* xpcError) {
LOG(ERROR) << "XPC Connection failed: "
<< base::SysNSStringToUTF8([xpcError description]);
reply(
static_cast<int>(updater::UpdateService::Result::kIPCConnectionFailed));
};
[[_xpcConnection remoteObjectProxyWithErrorHandler:errorHandler]
checkForUpdateWithAppID:appID
priority:priority
updateState:updateState
reply:reply];
}
@end
BrowserUpdaterClientMac::BrowserUpdaterClientMac()
: BrowserUpdaterClientMac(
base::scoped_nsobject<CRUUpdateClientOnDemandImpl>(
[[CRUUpdateClientOnDemandImpl alloc] init])) {}
BrowserUpdaterClientMac::BrowserUpdaterClientMac(
base::scoped_nsobject<CRUUpdateClientOnDemandImpl> client)
: client_(client) {}
BrowserUpdaterClientMac::~BrowserUpdaterClientMac() = default;
void BrowserUpdaterClientMac::BeginRegister(
const std::string& brand_code,
const std::string& tag,
const std::string& version,
updater::UpdateService::Callback callback) {
__block updater::UpdateService::Callback block_callback = std::move(callback);
auto reply = ^(int error) {
task_runner()->PostTask(
FROM_HERE,
base::BindOnce(std::move(block_callback),
static_cast<updater::UpdateService::Result>(error)));
};
[client_ registerForUpdatesWithAppId:GetAppIdForUpdaterAsNSString()
brandCode:base::SysUTF8ToNSString(brand_code)
tag:base::SysUTF8ToNSString(tag)
version:base::SysUTF8ToNSString(version)
existenceCheckerPath:base::mac::FilePathToNSString(
base::mac::OuterBundlePath())
reply:reply];
}
void BrowserUpdaterClientMac::BeginUpdateCheck(
updater::UpdateService::StateChangeCallback state_update,
updater::UpdateService::Callback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
__block updater::UpdateService::Callback block_callback = std::move(callback);
auto reply = ^(int error) {
task_runner()->PostTask(
FROM_HERE,
base::BindOnce(std::move(block_callback),
static_cast<updater::UpdateService::Result>(error)));
};
base::scoped_nsobject<CRUPriorityWrapper> priority_wrapper(
[[CRUPriorityWrapper alloc]
initWithPriority:updater::UpdateService::Priority::kForeground]);
base::scoped_nsprotocol<id<CRUUpdateStateObserving>> state_observer(
[[CRUUpdateStateObserver alloc] initWithRepeatingCallback:state_update
callbackRunner:task_runner()]);
[client_ checkForUpdateWithAppID:GetAppIdForUpdaterAsNSString()
priority:priority_wrapper.get()
updateState:state_observer.get()
reply:reply];
}
// static
scoped_refptr<BrowserUpdaterClient> BrowserUpdaterClient::Create() {
return base::MakeRefCounted<BrowserUpdaterClientMac>();
}
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/updater/browser_updater_client_mac.h"
#import <Foundation/Foundation.h>
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/mac/foundation_util.h"
#include "base/mac/scoped_nsobject.h"
#include "base/run_loop.h"
#include "base/strings/string16.h"
#include "base/strings/sys_string_conversions.h"
#include "base/test/bind_test_util.h"
#include "base/test/task_environment.h"
#include "chrome/browser/ui/webui/help/version_updater.h"
#include "chrome/browser/updater/browser_updater_client_util.h"
#import "chrome/updater/app/server/mac/service_protocol.h"
#import "chrome/updater/app/server/mac/update_service_wrappers.h"
#include "chrome/updater/update_service.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
constexpr char kUnittestAppId[] = "cr-unknown";
constexpr char kUnittestVersion[] = "0.0.0.0";
constexpr char kFakeAppId[] = "fakebundleid";
base::FilePath GetPerUserUpdaterPath() {
return base::mac::GetUserLibraryPath().Append(GetUpdaterFolderName());
}
} // namespace
@interface TestCRUUpdateClientOnDemandImpl : CRUUpdateClientOnDemandImpl {
bool _useStateChange;
updater::UpdateService::UpdateState::State _statusValue;
}
- (instancetype)initWithStatusValue:
(updater::UpdateService::UpdateState::State)statusValue
useStateUpdate:(bool)useStateChange;
@end
@implementation TestCRUUpdateClientOnDemandImpl
- (instancetype)initWithStatusValue:
(updater::UpdateService::UpdateState::State)statusValue
useStateUpdate:(bool)useStateChange {
if (self = [super init]) {
_statusValue = statusValue;
_useStateChange = useStateChange;
}
return self;
}
- (void)getUpdaterVersionWithReply:(void (^_Nonnull)(NSString* version))reply {
NOTIMPLEMENTED();
}
- (void)registerForUpdatesWithAppId:(NSString* _Nullable)appId
brandCode:(NSString* _Nullable)brandCode
tag:(NSString* _Nullable)tag
version:(NSString* _Nullable)version
existenceCheckerPath:(NSString* _Nullable)existenceCheckerPath
reply:(void (^_Nonnull)(int rc))reply {
reply(static_cast<int>(_statusValue));
}
// Checks for updates and returns the result in the reply block.
- (void)checkForUpdatesWithUpdateState:
(CRUUpdateStateObserver* _Nonnull)updateState
reply:(void (^_Nonnull)(int rc))reply {
NOTIMPLEMENTED();
}
// Checks for update of a given app, with specified priority. Sends repeated
// updates of progress and returns the result in the reply block.
- (void)checkForUpdateWithAppID:(NSString* _Nonnull)appID
priority:(CRUPriorityWrapper* _Nonnull)priority
updateState:(CRUUpdateStateObserver* _Nonnull)updateState
reply:(void (^_Nonnull)(int rc))reply {
if (_useStateChange) {
base::scoped_nsobject<CRUUpdateStateStateWrapper> stateWrapper(
[[CRUUpdateStateStateWrapper alloc]
initWithUpdateStateState:_statusValue]);
base::scoped_nsobject<CRUErrorCategoryWrapper> errorCategoryWrapper(
[[CRUErrorCategoryWrapper alloc]
initWithErrorCategory:updater::UpdateService::ErrorCategory::
kNone]);
base::scoped_nsobject<CRUUpdateStateWrapper> wrapper(
[[CRUUpdateStateWrapper alloc]
initWithAppId:base::SysUTF8ToNSString(kUnittestAppId)
state:stateWrapper
version:base::SysUTF8ToNSString(kUnittestVersion)
downloadedBytes:0
totalBytes:100 // use a fake total bytes to curb FPE_INTDIV
installProgress:0
errorCategory:errorCategoryWrapper
errorCode:5
extraCode:12]);
[updateState observeUpdateState:wrapper];
} else {
reply(static_cast<int>(_statusValue));
}
}
- (void)haltForUpdateToVersion:(NSString* _Nonnull)version
reply:(void (^_Nonnull)(BOOL shouldUpdate))reply {
NOTIMPLEMENTED();
}
@end
class UpdateClientMacTest : public ::testing::Test {
public:
scoped_refptr<BrowserUpdaterClientMac> update_client() const {
return update_client_;
}
void set_update_client(scoped_refptr<BrowserUpdaterClientMac> client) {
update_client_ = client;
}
protected:
void SetUp() override {
base::mac::SetBaseBundleID(kFakeAppId);
if (!base::PathExists(GetPerUserUpdaterPath()))
ASSERT_TRUE(base::CreateDirectory(GetPerUserUpdaterPath()));
}
void TearDown() override {
if (base::PathExists(GetPerUserUpdaterPath()))
ASSERT_TRUE(base::DeletePathRecursively(GetPerUserUpdaterPath()));
}
private:
base::test::TaskEnvironment task_environment_;
scoped_refptr<BrowserUpdaterClientMac> update_client_;
};
TEST_F(UpdateClientMacTest, SuccessfullyUpdatedStatus) {
base::RunLoop run_loop;
updater::UpdateService::UpdateState::State expected_status =
updater::UpdateService::UpdateState::State::kUpdated;
set_update_client(base::MakeRefCounted<BrowserUpdaterClientMac>(
base::scoped_nsobject<CRUUpdateClientOnDemandImpl>(
[[TestCRUUpdateClientOnDemandImpl alloc]
initWithStatusValue:expected_status
useStateUpdate:true])));
update_client()->CheckForUpdate(
base::BindRepeating(base::BindLambdaForTesting(
[&](updater::UpdateService::UpdateState update_state) {
// Ignore checking for updates - this is the initial status from
// running CheckForUpdates.
if (update_state.state ==
updater::UpdateService::UpdateState::State::kCheckingForUpdates)
return;
EXPECT_EQ(expected_status, update_state.state);
run_loop.Quit();
})));
run_loop.Run();
}
TEST_F(UpdateClientMacTest, UpdateDownloadingStatus) {
base::RunLoop run_loop;
updater::UpdateService::UpdateState::State expected_status =
updater::UpdateService::UpdateState::State::kDownloading;
set_update_client(base::MakeRefCounted<BrowserUpdaterClientMac>(
base::scoped_nsobject<CRUUpdateClientOnDemandImpl>(
[[TestCRUUpdateClientOnDemandImpl alloc]
initWithStatusValue:expected_status
useStateUpdate:true])));
update_client()->CheckForUpdate(
base::BindRepeating(base::BindLambdaForTesting(
[&](updater::UpdateService::UpdateState update_state) {
// Ignore checking for updates - this is the initial status from
// running CheckForUpdates.
if (update_state.state ==
updater::UpdateService::UpdateState::State::kCheckingForUpdates)
return;
EXPECT_EQ(expected_status, update_state.state);
run_loop.Quit();
})));
run_loop.Run();
}
TEST_F(UpdateClientMacTest, NoUpdateStatus) {
base::RunLoop run_loop;
updater::UpdateService::UpdateState::State expected_status =
updater::UpdateService::UpdateState::State::kNoUpdate;
set_update_client(base::MakeRefCounted<BrowserUpdaterClientMac>(
base::scoped_nsobject<CRUUpdateClientOnDemandImpl>(
[[TestCRUUpdateClientOnDemandImpl alloc]
initWithStatusValue:expected_status
useStateUpdate:true])));
update_client()->CheckForUpdate(
base::BindRepeating(base::BindLambdaForTesting(
[&](updater::UpdateService::UpdateState update_state) {
// Ignore checking for updates - this is the initial status from
// running CheckForUpdates.
if (update_state.state ==
updater::UpdateService::UpdateState::State::kCheckingForUpdates)
return;
EXPECT_EQ(expected_status, update_state.state);
run_loop.Quit();
})));
run_loop.Run();
}
TEST_F(UpdateClientMacTest, ErrorStatus) {
base::RunLoop run_loop;
updater::UpdateService::UpdateState::State expected_status =
updater::UpdateService::UpdateState::State::kUpdateError;
set_update_client(base::MakeRefCounted<BrowserUpdaterClientMac>(
base::scoped_nsobject<CRUUpdateClientOnDemandImpl>(
[[TestCRUUpdateClientOnDemandImpl alloc]
initWithStatusValue:expected_status
useStateUpdate:true])));
update_client()->CheckForUpdate(
base::BindRepeating(base::BindLambdaForTesting(
[&](updater::UpdateService::UpdateState update_state) {
// Ignore checking for updates - this is the initial status from
// running CheckForUpdates.
if (update_state.state ==
updater::UpdateService::UpdateState::State::kCheckingForUpdates)
return;
EXPECT_EQ(expected_status, update_state.state);
run_loop.Quit();
})));
run_loop.Run();
}
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/updater/browser_updater_client_util.h"
// TODO(crbug.com/1134631): Make updater branding files mergeable with browser
// branding files.
const char kUpdaterName[] = "ChromiumUpdater";
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UPDATER_BROWSER_UPDATER_CLIENT_UTIL_H_
#define CHROME_BROWSER_UPDATER_BROWSER_UPDATER_CLIENT_UTIL_H_
#include <string>
namespace base {
class FilePath;
}
extern const char kUpdaterName[];
// Gets the FilePath to the updater folder (e.g. Chromium/ChromiumUpdater).
base::FilePath GetUpdaterFolderName();
// Get the current installed version of the browser.
std::string CurrentlyInstalledVersion();
#endif // CHROME_BROWSER_UPDATER_BROWSER_UPDATER_CLIENT_UTIL_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/updater/browser_updater_client_util.h"
#include <Foundation/Foundation.h>
#include "base/files/file_path.h"
#include "base/mac/bundle_locations.h"
#include "base/mac/foundation_util.h"
#include "base/strings/sys_string_conversions.h"
#include "chrome/common/chrome_version.h"
base::FilePath GetUpdaterFolderName() {
return base::FilePath(COMPANY_SHORTNAME_STRING).Append(kUpdaterName);
}
std::string CurrentlyInstalledVersion() {
base::FilePath outer_bundle = base::mac::OuterBundlePath();
base::FilePath plist_path =
outer_bundle.Append("Contents").Append("Info.plist");
NSDictionary* info_plist = [NSDictionary
dictionaryWithContentsOfFile:base::mac::FilePathToNSString(plist_path)];
return base::SysNSStringToUTF8(
base::mac::ObjCCast<NSString>(info_plist[@"CFBundleShortVersionString"]));
}
......@@ -5,9 +5,20 @@
#include "chrome/browser/upgrade_detector/get_installed_version.h"
#include "base/strings/utf_string_conversions.h"
#include "base/version.h"
#include "chrome/browser/buildflags.h"
#include "chrome/browser/mac/keystone_glue.h"
#if BUILDFLAG(ENABLE_CHROMIUM_UPDATER)
#include "chrome/browser/updater/browser_updater_client_util.h"
#endif // BUILDFLAG(ENABLE_CHROMIUM_UPDATER)
InstalledAndCriticalVersion GetInstalledVersion() {
#if BUILDFLAG(ENABLE_CHROMIUM_UPDATER)
return InstalledAndCriticalVersion(
base::Version(CurrentlyInstalledVersion()));
#else
return InstalledAndCriticalVersion(base::Version(
base::UTF16ToASCII(keystone_glue::CurrentlyInstalledVersion())));
#endif // BUILDFLAG(ENABLE_CHROMIUM_UPDATER)
}
......@@ -27,6 +27,7 @@
#include "build/branding_buildflags.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/buildflags.h"
#include "chrome/browser/google/google_brand.h"
#include "chrome/browser/obsolete_system/obsolete_system.h"
#include "chrome/browser/upgrade_detector/build_state.h"
......@@ -455,7 +456,8 @@ void UpgradeDetectorImpl::Init() {
// On Windows, only enable upgrade notifications for Google Chrome builds.
// Chromium does not use an auto-updater.
#if !defined(OS_WIN) || BUILDFLAG(GOOGLE_CHROME_BRANDING)
#if !defined(OS_WIN) || BUILDFLAG(GOOGLE_CHROME_BRANDING) || \
BUILDFLAG(ENABLE_CHROMIUM_UPDATER)
// On macOS, only enable upgrade notifications if the updater (Keystone) is
// present.
......
......@@ -4717,6 +4717,11 @@ test("unit_tests") {
"../utility/importer/firefox_importer_unittest_utils_mac.cc",
"../utility/importer/safari_importer_unittest.mm",
]
if (enable_chromium_updater) {
sources +=
[ "../browser/updater/browser_updater_client_mac_unittest.mm" ]
}
}
deps += [
......
......@@ -44,6 +44,7 @@ if (is_win || is_mac) {
"crash_client.h",
"crash_reporter.cc",
"crash_reporter.h",
"enum_traits.h",
"patcher.cc",
"patcher.h",
"persisted_data.cc",
......@@ -255,12 +256,44 @@ if (is_win || is_mac) {
]
}
source_set("browser_sources") {
sources = [
"//chrome/updater/constants.cc",
"//chrome/updater/constants.h",
"//chrome/updater/enum_traits.h",
"//chrome/updater/registration_data.cc",
"//chrome/updater/registration_data.h",
"//chrome/updater/update_service.cc",
"//chrome/updater/update_service.h",
]
deps = [
"//base",
"//components/update_client",
]
if (is_mac) {
sources += [
"//chrome/updater/app/server/mac/service_protocol.h",
"//chrome/updater/app/server/mac/service_protocol.mm",
"//chrome/updater/app/server/mac/update_service_wrappers.h",
"//chrome/updater/app/server/mac/update_service_wrappers.mm",
]
}
visibility = [
"//chrome/browser/ui:ui",
"//chrome/browser/updater:*",
]
}
# These tests are run serially since they mutate system state.
test("updater_tests") {
testonly = true
sources = [
"app/app_server_unittest.cc",
"enum_traits_unittest.cc",
"external_constants_unittest.cc",
"external_constants_unittest.h",
"lib_util_unittest.cc",
......@@ -275,7 +308,6 @@ if (is_win || is_mac) {
"test/integration_tests.h",
"unittest_util_unittest.cc",
"updater_unittest.cc",
"util_unittest.cc",
]
deps = [
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_UPDATER_ENUM_TRAITS_H_
#define CHROME_UPDATER_ENUM_TRAITS_H_
#include <ostream>
#include <type_traits>
#include "base/optional.h"
namespace updater {
// Provides a way to safely convert numeric types to enumerated values.
// To use this facility, the enum definition must be annotated with traits to
// specify the range of the enum values. Due to how specialization of class
// templates works in C++14, the |EnumTraits| specialization of the primary
// template must be defined inside the |updater| namespace, where the
// primary template is defined. Traits for enum types defined inside
// other scopes, such as nested classes or other namespaces, may not work.
//
// enum class MyEnum {
// kVal1 = -1,
// kVal2 = 0,
// kVal3 = 1,
// };
//
// template <>
// struct EnumTraits<MyEnum> {
// static constexpr MyEnum first_elem = MyEnum::kVal1;
// static constexpr MyEnum last_elem = MyEnum::kVal3;
// };
//
// MyEnum val = *CheckedCastToEnum<MyEnum>(-1);
template <typename T>
struct EnumTraits {};
// Returns an optional value of an enun type T if the conversion from an
// integral type V is safe, meaning that |v| is within the bounds of the enum.
// The enum type must be annotated with traits to specify the lower and upper
// bounds of the enum values.
template <typename T, typename V>
base::Optional<T> CheckedCastToEnum(V v) {
static_assert(std::is_enum<T>::value, "T must be an enum type.");
static_assert(std::is_integral<V>::value, "V must be an integral type.");
using Traits = EnumTraits<T>;
return (static_cast<V>(Traits::first_elem) <= v &&
v <= static_cast<V>(Traits::last_elem))
? base::make_optional(static_cast<T>(v))
: base::nullopt;
}
} // namespace updater
#endif // CHROME_UPDATER_ENUM_TRAITS_H_
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/updater/util.h"
#include "chrome/updater/enum_traits.h"
#include <ostream>
......
......@@ -11,7 +11,7 @@
#include "base/callback_forward.h"
#include "base/memory/ref_counted.h"
#include "base/version.h"
#include "chrome/updater/util.h"
#include "chrome/updater/enum_traits.h"
namespace updater {
......@@ -60,6 +60,10 @@ class UpdateService : public base::RefCountedThreadSafe<UpdateService> {
kInactive = 8,
// Change the EnumTraits class in this file when adding new values.
// IPC connection to the remote process failed for some reason.
kIPCConnectionFailed = 9,
// Change the traits class in this file when adding new values.
};
// Run time errors are organized in specific categories to indicate the
......
......@@ -5,11 +5,7 @@
#ifndef CHROME_UPDATER_UTIL_H_
#define CHROME_UPDATER_UTIL_H_
#include <ostream>
#include <type_traits>
#include "base/files/file_path.h"
#include "base/optional.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
......@@ -69,46 +65,6 @@ GURL AppendQueryParameter(const GURL& url,
const std::string& name,
const std::string& value);
// Provides a way to safely convert numeric types to enumerated values.
// To use this facility, the enum definition must be annotated with traits to
// specify the range of the enum values. Due to how specialization of class
// templates works in C++14, the |EnumTraits| specialization of the primary
// template must be defined inside the |updater| namespace, where the
// primary template is defined. Traits for enum types defined inside
// other scopes, such as nested classes or other namespaces, may not work.
//
// enum class MyEnum {
// kVal1 = -1,
// kVal2 = 0,
// kVal3 = 1,
// };
//
// template <>
// struct EnumTraits<MyEnum> {
// static constexpr MyEnum first_elem = MyEnum::kVal1;
// static constexpr MyEnum last_elem = MyEnum::kVal3;
// };
//
// MyEnum val = *CheckedCastToEnum<MyEnum>(-1);
template <typename T>
struct EnumTraits {};
// Returns an optional value of an enun type T if the conversion from an
// integral type V is safe, meaning that |v| is within the bounds of the enum.
// The enum type must be annotated with traits to specify the lower and upper
// bounds of the enum values.
template <typename T, typename V>
base::Optional<T> CheckedCastToEnum(V v) {
static_assert(std::is_enum<T>::value, "T must be an enum type.");
static_assert(std::is_integral<V>::value, "V must be an integral type.");
using Traits = EnumTraits<T>;
return (static_cast<V>(Traits::first_elem) <= v &&
v <= static_cast<V>(Traits::last_elem))
? base::make_optional(static_cast<T>(v))
: base::nullopt;
}
} // namespace updater
#endif // CHROME_UPDATER_UTIL_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment