diff --git a/DEPS b/DEPS index b85e1d9..b47566b 100644 --- a/DEPS +++ b/DEPS
@@ -199,11 +199,11 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Skia # and whatever else without interference from each other. - 'skia_revision': '88883c4ca259aa1340286c3a0f85fe97a911292d', + 'skia_revision': '960bd2dbaa6a2130330c5f693e2a07a24aeca392', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. - 'v8_revision': 'b96d7e2a4be85264b9cd7ba26d91f7fb98baa9a5', + 'v8_revision': 'ab90f8251bdc6bd9dc6033afdd2374af4883794f', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling swarming_client # and whatever else without interference from each other. @@ -211,7 +211,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling ANGLE # and whatever else without interference from each other. - 'angle_revision': '4ebcee3eb02ffdd586e20965b9c4bb609876db36', + 'angle_revision': '4e2b6d6b01028728634d65d0f69523da145420e7', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. @@ -250,7 +250,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling freetype # and whatever else without interference from each other. - 'freetype_revision': '7bdf386e758cb7c01392e625f4d723e5abb3f9a6', + 'freetype_revision': '0d5f1dd37c056b4460a460d16fd1fbb06740e891', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling freetype # and whatever else without interference from each other. @@ -274,7 +274,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling devtools-frontend # and whatever else without interference from each other. - 'devtools_frontend_revision': 'cde5182f7aa484bea3ea9d2ba2a5f49b2cfd738a', + 'devtools_frontend_revision': '37fb5ece29fd09e5da06b508477b84b8bce58df3', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libprotobuf-mutator # and whatever else without interference from each other. @@ -314,7 +314,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. - 'dawn_revision': '5d4fd88a7403aa255d49919d011af54c691b344a', + 'dawn_revision': 'ec56b90ceab0c16e3c9f5603cc26562ec7bc2430', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -1262,7 +1262,7 @@ }, 'src/third_party/perfetto': - Var('android_git') + '/platform/external/perfetto.git' + '@' + 'c65f224a7259d87bf1f980e7424d73fc0cfca795', + Var('android_git') + '/platform/external/perfetto.git' + '@' + '8986f3ce67ec65a72629404b45d639ca37e5ea2b', 'src/third_party/perl': { 'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3', @@ -1340,7 +1340,7 @@ 'packages': [ { 'package': 'fuchsia/third_party/aemu/linux-amd64', - 'version': 'qMq36BPvKEIxjpVFBefO08HoyM51jARe3EuX0vcgzWsC' + 'version': 'GI8QGXYA44_uicQ1AX9ecXBzIK4dImUA90X-kMlohyIC' }, ], 'condition': 'host_os == "linux" and checkout_fuchsia', @@ -1514,7 +1514,7 @@ Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '3c2fe3888658d82b47ca831d59a2e07579619c2d', 'src/third_party/webrtc': - Var('webrtc_git') + '/src.git' + '@' + 'ef9c4e07a351c9396c72e72efaa1be3df728c440', + Var('webrtc_git') + '/src.git' + '@' + '8649e49d10e6e6efb5a98920f0b19a77abe8f070', 'src/third_party/libgifcodec': Var('skia_git') + '/libgifcodec' + '@'+ Var('libgifcodec_revision'), @@ -1586,7 +1586,7 @@ Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@3974e673643526eb5e4050be7da7abcc86e70699', + 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@ec26b8d2b2f85f4c7f9c4a186daabac73172985f', 'condition': 'checkout_src_internal', }, @@ -1594,7 +1594,7 @@ 'packages': [ { 'package': 'chromeos_internal/apps/help_app/app', - 'version': 'Lhu0LG_M8M2Pn2h_RBp-1HjjF6xbNUOUOu6DedtsKlQC', + 'version': 'ArjDoksx4veFQalheiqMNhRYccNS84igngDuMSvLD-0C', }, ], 'condition': 'checkout_chromeos and checkout_src_internal', @@ -1605,7 +1605,7 @@ 'packages': [ { 'package': 'chromeos_internal/apps/media_app/app', - 'version': '97cSjc_Tjv810DAVmAh-xqhwbphGqNsTmBJluRhxfZ8C', + 'version': 'JzkQbgu707ehmRMJSwGBX0gMotKpUKiewC8zdP_xHngC', }, ], 'condition': 'checkout_chromeos and checkout_src_internal', @@ -3361,7 +3361,7 @@ 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_guava_guava', - 'version': 'version:27.1-jre-cr0', + 'version': 'version:30.1-jre-cr0', }, ], 'condition': 'checkout_android', @@ -3372,7 +3372,7 @@ 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_guava_guava_android', - 'version': 'version:25.1-android-cr0', + 'version': 'version:30.1-android-cr0', }, ], 'condition': 'checkout_android', @@ -3394,7 +3394,7 @@ 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/com_google_j2objc_j2objc_annotations', - 'version': 'version:1.1-cr0', + 'version': 'version:1.3-cr0', }, ], 'condition': 'checkout_android', @@ -3735,7 +3735,7 @@ 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_checkerframework_checker_compat_qual', - 'version': 'version:2.5.3-cr0', + 'version': 'version:2.5.5-cr0', }, ], 'condition': 'checkout_android', @@ -3746,7 +3746,7 @@ 'packages': [ { 'package': 'chromium/third_party/android_deps/libs/org_checkerframework_checker_qual', - 'version': 'version:2.10.0-cr0', + 'version': 'version:3.5.0-cr0', }, ], 'condition': 'checkout_android',
diff --git a/ash/BUILD.gn b/ash/BUILD.gn index bed617c..11a1d86 100644 --- a/ash/BUILD.gn +++ b/ash/BUILD.gn
@@ -1678,6 +1678,10 @@ "wm/window_cycle_event_filter.h", "wm/window_cycle_list.cc", "wm/window_cycle_list.h", + "wm/window_cycle_tab_slider.cc", + "wm/window_cycle_tab_slider.h", + "wm/window_cycle_tab_slider_button.cc", + "wm/window_cycle_tab_slider_button.h", "wm/window_dimmer.cc", "wm/window_dimmer.h", "wm/window_finder.cc", @@ -1965,6 +1969,7 @@ "accessibility/touch_exploration_controller_unittest.cc", "accessibility/touch_exploration_manager_unittest.cc", "ambient/ambient_controller_unittest.cc", + "ambient/ambient_photo_cache_unittest.cc", "ambient/ambient_photo_controller_unittest.cc", "ambient/autotest_ambient_api_unittest.cc", "ambient/model/ambient_backend_model_unittest.cc",
diff --git a/ash/accelerators/accelerator_controller_impl.cc b/ash/accelerators/accelerator_controller_impl.cc index d6f879a..efa0c34 100644 --- a/ash/accelerators/accelerator_controller_impl.cc +++ b/ash/accelerators/accelerator_controller_impl.cc
@@ -794,7 +794,10 @@ void HandleTakeScreenshot() { base::RecordAction(UserMetricsAction("Accel_Take_Screenshot")); - Shell::Get()->screenshot_controller()->TakeScreenshotForAllRootWindows(); + if (!features::IsCaptureModeEnabled()) + Shell::Get()->screenshot_controller()->TakeScreenshotForAllRootWindows(); + else + CaptureModeController::Get()->CaptureScreenshotsOfAllDisplays(); } void HandleToggleSystemTrayBubbleInternal(bool focus_message_center) {
diff --git a/ash/ambient/ambient_controller.cc b/ash/ambient/ambient_controller.cc index 3079b24..868d0bb 100644 --- a/ash/ambient/ambient_controller.cc +++ b/ash/ambient/ambient_controller.cc
@@ -509,6 +509,10 @@ } void AmbientController::OnEnabledPrefChanged() { + // TODO(b/176094707) conditionally create/destroy photo_controller and cache + // if Ambient is enabled + ambient_photo_controller_.InitCache(); + if (IsAmbientModeEnabled()) { DVLOG(1) << "Ambient mode enabled";
diff --git a/ash/ambient/ambient_photo_cache.cc b/ash/ambient/ambient_photo_cache.cc index c30b9db8..bdcdad7 100644 --- a/ash/ambient/ambient_photo_cache.cc +++ b/ash/ambient/ambient_photo_cache.cc
@@ -8,7 +8,10 @@ #include "ash/public/cpp/ambient/ambient_client.h" #include "base/files/file_util.h" #include "base/memory/weak_ptr.h" +#include "base/path_service.h" #include "base/sequenced_task_runner.h" +#include "base/strings/string_number_conversions.h" +#include "base/system/sys_info.h" #include "base/task/task_traits.h" #include "base/task/thread_pool.h" #include "services/data_decoder/public/cpp/decode_image.h" @@ -52,11 +55,82 @@ NO_TRAFFIC_ANNOTATION_YET); } -// Implementation of |AmbientPhotoCache|. +bool CreateDirIfNotExists(const base::FilePath& path) { + return base::DirectoryExists(path) || base::CreateDirectory(path); +} + +// Writes |data| to |path| if |data| is not nullptr and is not empty. If |data| +// is nullptr or empty, will delete any existing file at |path|. +bool WriteOrDeleteFile(const base::FilePath& path, + const std::string* const data) { + if (!data || data->empty()) + return base::DeleteFile(path); + + if (!CreateDirIfNotExists(path.DirName())) { + LOG(ERROR) << "Cannot create ambient mode directory."; + return false; + } + + if (base::SysInfo::AmountOfFreeDiskSpace(path.DirName()) < + kMaxReservedAvailableDiskSpaceByte) { + LOG(ERROR) << "Not enough disk space left."; + return false; + } + + // Create a temp file. + base::FilePath temp_file; + if (!base::CreateTemporaryFileInDir(path.DirName(), &temp_file)) { + LOG(ERROR) << "Cannot create a temporary file."; + return false; + } + + // Write to the tmp file. + const int size = data->size(); + int written_size = base::WriteFile(temp_file, data->data(), size); + if (written_size != size) { + LOG(ERROR) << "Cannot write the temporary file."; + base::DeleteFile(temp_file); + return false; + } + + // Replace the current file with the temp file. + if (!base::ReplaceFile(temp_file, path, /*error=*/nullptr)) { + LOG(ERROR) << "Cannot replace the temporary file."; + base::DeleteFile(temp_file); + return false; + } + + return true; +} + +base::FilePath GetPhotoPath(int cache_index, + const base::FilePath& root_path, + bool is_related = false) { + std::string file_ext; + + // "_r.img" for related files, ".img" otherwise + if (is_related) + file_ext += kRelatedPhotoSuffix; + + file_ext += kPhotoFileExt; + + return root_path.Append(base::NumberToString(cache_index) + file_ext); +} + +base::FilePath GetDetailsPath(int cache_index, + const base::FilePath& root_path) { + return GetPhotoPath(cache_index, root_path) + .RemoveExtension() + .AddExtension(kPhotoDetailsFileExt); +} + +// -----------------AmbientPhotoCacheImpl--------------------------------------- + class AmbientPhotoCacheImpl : public AmbientPhotoCache { public: - AmbientPhotoCacheImpl() - : task_runner_(base::ThreadPool::CreateSequencedTaskRunner( + explicit AmbientPhotoCacheImpl(base::FilePath path) + : root_directory_(path), + task_runner_(base::ThreadPool::CreateSequencedTaskRunner( {base::MayBlock(), base::TaskPriority::BEST_EFFORT, base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})) {} ~AmbientPhotoCacheImpl() override = default; @@ -65,9 +139,12 @@ void DownloadPhoto(const std::string& url, base::OnceCallback<void(std::unique_ptr<std::string>)> callback) override { - auto simple_loader = CreateSimpleURLLoader(url); + std::unique_ptr<network::SimpleURLLoader> simple_loader = + CreateSimpleURLLoader(url); + scoped_refptr<network::SharedURLLoaderFactory> loader_factory = + AmbientClient::Get()->GetURLLoaderFactory(); auto* loader_ptr = simple_loader.get(); - auto loader_factory = AmbientClient::Get()->GetURLLoaderFactory(); + loader_ptr->DownloadToString( loader_factory.get(), base::BindOnce(&AmbientPhotoCacheImpl::OnUrlDownloaded, @@ -77,10 +154,120 @@ } void DownloadPhotoToFile(const std::string& url, - base::OnceCallback<void(base::FilePath)> callback, - const base::FilePath& file_path) override { - auto simple_loader = CreateSimpleURLLoader(url); - auto loader_factory = AmbientClient::Get()->GetURLLoaderFactory(); + int cache_index, + bool is_related, + base::OnceCallback<void(bool)> callback) override { + auto file_path = GetPhotoPath(cache_index, root_directory_, is_related); + task_runner_->PostTaskAndReply( + FROM_HERE, + base::BindOnce( + [](const base::FilePath& path) { + if (!CreateDirIfNotExists(path)) + LOG(ERROR) << "Cannot create ambient mode directory"; + }, + root_directory_), + base::BindOnce(&AmbientPhotoCacheImpl::DownloadPhotoToFileInternal, + weak_factory_.GetWeakPtr(), url, std::move(callback), + file_path)); + } + + void DecodePhoto( + std::unique_ptr<std::string> data, + base::OnceCallback<void(const gfx::ImageSkia&)> callback) override { + std::vector<uint8_t> image_bytes(data->begin(), data->end()); + data_decoder::DecodeImageIsolated( + image_bytes, data_decoder::mojom::ImageCodec::DEFAULT, + /*shrink_to_fit=*/true, data_decoder::kDefaultMaxSizeInBytes, + /*desired_image_frame_size=*/gfx::Size(), + base::BindOnce(&ToImageSkia, std::move(callback))); + } + + void WriteFiles(int cache_index, + const std::string* const image, + const std::string* const details, + const std::string* const related_image, + base::OnceClosure callback) override { + DCHECK_LT(cache_index, kMaxNumberOfCachedImages); + task_runner_->PostTaskAndReply( + FROM_HERE, + base::BindOnce( + [](int cache_index, const base::FilePath& root_path, + const std::string* const image, const std::string* const details, + const std::string* const related_image) { + bool success = true; + + auto image_path = GetPhotoPath(cache_index, root_path); + success = success && WriteOrDeleteFile(image_path, image); + + auto details_path = GetDetailsPath(cache_index, root_path); + success = success && WriteOrDeleteFile(details_path, details); + + auto related_image_path = + GetPhotoPath(cache_index, root_path, /*is_related=*/true); + success = success && + WriteOrDeleteFile(related_image_path, related_image); + + if (!success) { + LOG(WARNING) << "Error writing files"; + base::DeleteFile(image_path); + base::DeleteFile(details_path); + base::DeleteFile(related_image_path); + } + }, + cache_index, root_directory_, image, details, related_image), + std::move(callback)); + } + + void ReadFiles(int cache_index, + base::OnceCallback<void(PhotoCacheEntry)> callback) override { + task_runner_->PostTaskAndReplyWithResult( + FROM_HERE, + base::BindOnce( + [](int cache_index, const base::FilePath& root_path) { + auto image = std::make_unique<std::string>(); + auto details = std::make_unique<std::string>(); + auto related_image = std::make_unique<std::string>(); + + auto image_path = GetPhotoPath(cache_index, root_path); + + if (!base::ReadFileToString(image_path, image.get())) + image->clear(); + + auto details_path = GetDetailsPath(cache_index, root_path); + + if (!base::ReadFileToString(details_path, details.get())) + details->clear(); + + auto related_path = + GetPhotoPath(cache_index, root_path, /*is_related=*/true); + + if (!base::ReadFileToString(related_path, related_image.get())) + related_image->clear(); + + return PhotoCacheEntry(std::move(image), std::move(details), + std::move(related_image)); + }, + cache_index, root_directory_), + std::move(callback)); + } + + void Clear() override { + task_runner_->PostTask(FROM_HERE, + base::BindOnce( + [](const base::FilePath& file_path) { + base::DeletePathRecursively(file_path); + }, + root_directory_)); + } + + private: + void DownloadPhotoToFileInternal(const std::string& url, + base::OnceCallback<void(bool)> callback, + const base::FilePath& file_path) { + std::unique_ptr<network::SimpleURLLoader> simple_loader = + CreateSimpleURLLoader(url); + scoped_refptr<network::SharedURLLoaderFactory> loader_factory = + AmbientClient::Get()->GetURLLoaderFactory(); auto* loader_ptr = simple_loader.get(); auto* loader_factory_ptr = loader_factory.get(); @@ -100,18 +287,6 @@ temp_path); } - void DecodePhoto( - std::unique_ptr<std::string> data, - base::OnceCallback<void(const gfx::ImageSkia&)> callback) override { - std::vector<uint8_t> image_bytes(data->begin(), data->end()); - data_decoder::DecodeImageIsolated( - image_bytes, data_decoder::mojom::ImageCodec::DEFAULT, - /*shrink_to_fit=*/true, data_decoder::kDefaultMaxSizeInBytes, - /*desired_image_frame_size=*/gfx::Size(), - base::BindOnce(&ToImageSkia, std::move(callback))); - } - - private: void OnUrlDownloaded( base::OnceCallback<void(std::unique_ptr<std::string>)> callback, std::unique_ptr<network::SimpleURLLoader> simple_loader, @@ -129,7 +304,7 @@ } void OnUrlDownloadedToFile( - base::OnceCallback<void(base::FilePath)> callback, + base::OnceCallback<void(bool)> callback, std::unique_ptr<network::SimpleURLLoader> simple_loader, scoped_refptr<network::SharedURLLoaderFactory> loader_factory, const base::FilePath& desired_path, @@ -147,7 +322,7 @@ }, temp_path)); } - std::move(callback).Run(base::FilePath()); + std::move(callback).Run(false); return; } @@ -163,23 +338,48 @@ // Clean up the files. base::DeleteFile(from_path); base::DeleteFile(to_path); - return base::FilePath(); + return false; } - return to_path; + return true; }, desired_path, temp_path), std::move(callback)); } + const base::FilePath root_directory_; scoped_refptr<base::SequencedTaskRunner> task_runner_; base::WeakPtrFactory<AmbientPhotoCacheImpl> weak_factory_{this}; }; } // namespace +// ---------------- PhotoCacheRead -------------------------------------------- + +PhotoCacheEntry::PhotoCacheEntry() = default; + +PhotoCacheEntry::PhotoCacheEntry(std::unique_ptr<std::string> image, + std::unique_ptr<std::string> details, + std::unique_ptr<std::string> related_image) + : image(std::move(image)), + details(std::move(details)), + related_image(std::move(related_image)) {} + +PhotoCacheEntry::PhotoCacheEntry(PhotoCacheEntry&&) = default; + +PhotoCacheEntry::~PhotoCacheEntry() = default; + +void PhotoCacheEntry::reset() { + image.reset(); + details.reset(); + related_image.reset(); +} + +// -------------- AmbientPhotoCache -------------------------------------------- + // static -std::unique_ptr<AmbientPhotoCache> AmbientPhotoCache::Create() { - return std::make_unique<AmbientPhotoCacheImpl>(); +std::unique_ptr<AmbientPhotoCache> AmbientPhotoCache::Create( + base::FilePath root_path) { + return std::make_unique<AmbientPhotoCacheImpl>(root_path); } } // namespace ash
diff --git a/ash/ambient/ambient_photo_cache.h b/ash/ambient/ambient_photo_cache.h index 9c3f6cb..56eceb7 100644 --- a/ash/ambient/ambient_photo_cache.h +++ b/ash/ambient/ambient_photo_cache.h
@@ -8,6 +8,7 @@ #include <memory> #include <string> +#include "ash/ash_export.h" #include "base/callback_forward.h" #include "base/files/file_path.h" @@ -17,31 +18,77 @@ namespace ash { +// Holds the return value for |AmbientPhotoCache::ReadFiles| to use with +// |task_runner_->PostTaskAndReplyWithResult|. +// Represented on disk by a file for each of |image|, |details|, and +// |related_image|. +struct ASH_EXPORT PhotoCacheEntry { + PhotoCacheEntry(); + + PhotoCacheEntry(std::unique_ptr<std::string> image, + std::unique_ptr<std::string> details, + std::unique_ptr<std::string> related_image); + + PhotoCacheEntry(const PhotoCacheEntry&) = delete; + PhotoCacheEntry& operator=(const PhotoCacheEntry&) = delete; + PhotoCacheEntry(PhotoCacheEntry&&); + + ~PhotoCacheEntry(); + + void reset(); + + std::unique_ptr<std::string> image; + std::unique_ptr<std::string> details; + std::unique_ptr<std::string> related_image; +}; + // Interface for downloading and decoding photos for Ambient mode. Mocked for -// testing to isolate from network. -class AmbientPhotoCache { +// testing to isolate from network and file system. +// Each cache entry is written to disk as three files in the |root_path|, with +// filenames prefixed by |cache_index|. +class ASH_EXPORT AmbientPhotoCache { public: AmbientPhotoCache() = default; AmbientPhotoCache(const AmbientPhotoCache&) = delete; AmbientPhotoCache& operator=(const AmbientPhotoCache&) = delete; virtual ~AmbientPhotoCache() = default; - static std::unique_ptr<AmbientPhotoCache> Create(); + static std::unique_ptr<AmbientPhotoCache> Create(base::FilePath root_path); virtual void DownloadPhoto( const std::string& url, base::OnceCallback<void(std::unique_ptr<std::string>)> callback) = 0; - // Saves the photo to |file_path| and calls |callback| when complete. If an - // error occurs, will call |callback| with an empty path. - virtual void DownloadPhotoToFile( - const std::string& url, - base::OnceCallback<void(base::FilePath)> callback, - const base::FilePath& file_path) = 0; + // Saves the photo at |url| to |cache_index| and calls |callback| with a + // boolean that indicates success. Setting |is_related| will change the + // filename to indicate that this is a paired photo. + virtual void DownloadPhotoToFile(const std::string& url, + int cache_index, + bool is_related, + base::OnceCallback<void(bool)> callback) = 0; virtual void DecodePhoto( std::unique_ptr<std::string> data, base::OnceCallback<void(const gfx::ImageSkia&)> callback) = 0; + + // Write files to disk at |cache_index| and call |callback| when complete. + // |image| and |related_image| are encoded jpg images that must be decoded + // with |DecodePhoto| to display. |details| is human readable text. + virtual void WriteFiles(int cache_index, + const std::string* const image, + const std::string* const details, + const std::string* const related_image, + base::OnceClosure callback) = 0; + + // Read the files at |cache_index| and call |callback| with a struct + // containing the contents of each file. If a particular file fails to be + // read, it may be represented as nullptr or empty string. + virtual void ReadFiles( + int cache_index, + base::OnceCallback<void(PhotoCacheEntry)> callback) = 0; + + // Erase all stored files from disk. + virtual void Clear() = 0; }; } // namespace ash
diff --git a/ash/ambient/ambient_photo_cache_unittest.cc b/ash/ambient/ambient_photo_cache_unittest.cc new file mode 100644 index 0000000..bf72c123 --- /dev/null +++ b/ash/ambient/ambient_photo_cache_unittest.cc
@@ -0,0 +1,135 @@ +// 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 "ash/ambient/ambient_photo_cache.h" + +#include "ash/ambient/ambient_constants.h" +#include "base/callback_forward.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/path_service.h" +#include "base/run_loop.h" +#include "base/test/task_environment.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace ash { + +namespace { + +base::FilePath GetTestPath() { + base::FilePath path; + EXPECT_TRUE(base::PathService::Get(base::DIR_TEMP, &path)); + path = path.Append(FILE_PATH_LITERAL(kAmbientModeDirectoryName)); + return path; +} + +} // namespace + +class AmbientPhotoCacheTest : public testing::Test { + public: + void SetUp() override { + auto test_path = GetTestPath(); + base::DeletePathRecursively(test_path); + photo_cache_ = AmbientPhotoCache::Create(test_path); + } + + void TearDown() override { base::DeletePathRecursively(GetTestPath()); } + + AmbientPhotoCache* photo_cache() { return photo_cache_.get(); } + + protected: + base::test::TaskEnvironment task_environment_; + + private: + std::unique_ptr<AmbientPhotoCache> photo_cache_; +}; + +TEST_F(AmbientPhotoCacheTest, ReadsBackWrittenFiles) { + int cache_index = 0; + std::string image("image"); + std::string details("details"); + std::string related_image("related image"); + + { + base::RunLoop loop; + photo_cache()->WriteFiles(cache_index, &image, &details, &related_image, + loop.QuitClosure()); + loop.Run(); + } + + { + base::RunLoop loop; + // Read the files back using photo cache. + photo_cache()->ReadFiles( + cache_index, + base::BindOnce( + [](base::OnceClosure done, PhotoCacheEntry cache_read) { + EXPECT_EQ(*cache_read.image, "image"); + EXPECT_EQ(*cache_read.details, "details"); + EXPECT_EQ(*cache_read.related_image, "related image"); + std::move(done).Run(); + }, + loop.QuitClosure())); + loop.Run(); + } +} + +TEST_F(AmbientPhotoCacheTest, WritesFileToDisk) { + base::FilePath test_path = GetTestPath(); + + int cache_index = 5; + std::string image("image 5"); + std::string details("details 5"); + std::string related_image("related image 5"); + + // Make sure files are not on disk. + EXPECT_FALSE(base::PathExists(test_path.Append(FILE_PATH_LITERAL("5.img")))); + EXPECT_FALSE(base::PathExists(test_path.Append(FILE_PATH_LITERAL("5.txt")))); + EXPECT_FALSE( + base::PathExists(test_path.Append(FILE_PATH_LITERAL("5_r.img")))); + + // Write the data to the cache. + { + base::RunLoop loop; + photo_cache()->WriteFiles(cache_index, &image, &details, &related_image, + loop.QuitClosure()); + loop.Run(); + } + + // Verify that expected files are written to disk. + std::string actual_image; + EXPECT_TRUE(base::ReadFileToString( + test_path.Append(FILE_PATH_LITERAL("5.img")), &actual_image)); + EXPECT_EQ(actual_image, image); + + std::string actual_details; + EXPECT_TRUE(base::ReadFileToString( + test_path.Append(FILE_PATH_LITERAL("5.txt")), &actual_details)); + EXPECT_EQ(actual_details, details); + + std::string actual_related_image; + EXPECT_TRUE(base::ReadFileToString( + test_path.Append(FILE_PATH_LITERAL("5_r.img")), &actual_related_image)); + EXPECT_EQ(actual_related_image, related_image); +} + +TEST_F(AmbientPhotoCacheTest, SetsDataToEmptyStringWhenFilesMissing) { + base::FilePath test_path = GetTestPath(); + EXPECT_FALSE(base::DirectoryExists(test_path)); + { + base::RunLoop loop; + photo_cache()->ReadFiles( + /*cache_index=*/1, + base::BindOnce( + [](base::OnceClosure done, PhotoCacheEntry cache_read) { + EXPECT_TRUE(cache_read.image->empty()); + EXPECT_TRUE(cache_read.details->empty()); + EXPECT_TRUE(cache_read.related_image->empty()); + std::move(done).Run(); + }, + loop.QuitClosure())); + } +} + +} // namespace ash
diff --git a/ash/ambient/ambient_photo_controller.cc b/ash/ambient/ambient_photo_controller.cc index 767fc69..e414d18 100644 --- a/ash/ambient/ambient_photo_controller.cc +++ b/ash/ambient/ambient_photo_controller.cc
@@ -85,69 +85,6 @@ base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}; } -// Get the root path for ambient mode. -base::FilePath GetRootPath() { - base::FilePath home_dir; - CHECK(base::PathService::Get(base::DIR_HOME, &home_dir)); - return home_dir.Append(FILE_PATH_LITERAL(kAmbientModeDirectoryName)); -} - -base::FilePath GetCachePath() { - return GetRootPath().Append( - FILE_PATH_LITERAL(kAmbientModeCacheDirectoryName)); -} - -base::FilePath GetBackupCachePath() { - return GetRootPath().Append( - FILE_PATH_LITERAL(kAmbientModeBackupCacheDirectoryName)); -} - -base::FilePath GetBackupFilePath(size_t index) { - return GetBackupCachePath().Append(base::NumberToString(index) + - kPhotoFileExt); -} - -base::FilePath GetRelatedFilePath(const std::string& file_name) { - return GetCachePath().Append(file_name + kRelatedPhotoSuffix + kPhotoFileExt); -} - -bool CreateDirIfNotExists(const base::FilePath& path) { - return base::DirectoryExists(path) || base::CreateDirectory(path); -} - -void WriteFile(const base::FilePath& path, const std::string& data) { - if (!CreateDirIfNotExists(GetCachePath())) { - LOG(ERROR) << "Cannot create ambient mode directory."; - return; - } - - if (base::SysInfo::AmountOfFreeDiskSpace(GetRootPath()) < - kMaxReservedAvailableDiskSpaceByte) { - LOG(WARNING) << "Not enough disk space left."; - return; - } - - // Create a temp file. - base::FilePath temp_file; - if (!base::CreateTemporaryFileInDir(path.DirName(), &temp_file)) { - LOG(ERROR) << "Cannot create a temporary file."; - return; - } - - // Write to the tmp file. - const int size = data.size(); - int written_size = base::WriteFile(temp_file, data.data(), size); - if (written_size != size) { - LOG(ERROR) << "Cannot write the temporary file."; - base::DeleteFile(temp_file); - return; - } - - // Replace the current file with the temp file. - if (!base::ReplaceFile(temp_file, path, /*error=*/nullptr)) - LOG(ERROR) << "Cannot replace the temporary file."; -} - const std::array<const char*, 2>& GetBackupPhotoUrls() { return Shell::Get() ->ambient_controller() @@ -155,12 +92,18 @@ ->GetBackupPhotoUrls(); } +// Get the cache root path for ambient mode. +base::FilePath GetCacheRootPath() { + base::FilePath home_dir; + CHECK(base::PathService::Get(base::DIR_HOME, &home_dir)); + return home_dir.Append(FILE_PATH_LITERAL(kAmbientModeDirectoryName)); +} + } // namespace AmbientPhotoController::AmbientPhotoController() : fetch_topic_retry_backoff_(&kFetchTopicRetryBackoffPolicy), resume_fetch_image_backoff_(&kResumeFetchImageBackoffPolicy), - photo_cache_(AmbientPhotoCache::Create()), task_runner_( base::ThreadPool::CreateSequencedTaskRunner(GetTaskTraits())) { ambient_backend_model_observer_.Add(&ambient_backend_model_); @@ -179,7 +122,7 @@ // Would use |timer_.FireNow()| but this does not execute if screen is // locked. Manually call the expected callback instead. backup_photo_refresh_timer_.Stop(); - PrepareFetchBackupImages(); + FetchBackupImages(); } } @@ -204,7 +147,7 @@ FROM_HERE, std::max(kBackupPhotoRefreshDelay, resume_fetch_image_backoff_.GetTimeUntilRelease()), - base::BindOnce(&AmbientPhotoController::PrepareFetchBackupImages, + base::BindOnce(&AmbientPhotoController::FetchBackupImages, weak_factory_.GetWeakPtr())); } @@ -238,12 +181,21 @@ } void AmbientPhotoController::ClearCache() { - task_runner_->PostTask(FROM_HERE, - base::BindOnce( - [](const base::FilePath& file_path) { - base::DeletePathRecursively(file_path); - }, - GetCachePath())); + DCHECK(photo_cache_); + DCHECK(backup_photo_cache_); + photo_cache_->Clear(); + backup_photo_cache_->Clear(); +} + +void AmbientPhotoController::InitCache() { + if (!photo_cache_) { + photo_cache_ = AmbientPhotoCache::Create(GetCacheRootPath().Append( + FILE_PATH_LITERAL(kAmbientModeCacheDirectoryName))); + } + if (!backup_photo_cache_) { + backup_photo_cache_ = AmbientPhotoCache::Create(GetCacheRootPath().Append( + FILE_PATH_LITERAL(kAmbientModeBackupCacheDirectoryName))); + } } void AmbientPhotoController::ScheduleFetchTopics(bool backoff) { @@ -265,28 +217,21 @@ weak_factory_.GetWeakPtr())); } -void AmbientPhotoController::PrepareFetchBackupImages() { - task_runner_->PostTaskAndReply( - FROM_HERE, - base::BindOnce([]() { CreateDirIfNotExists(GetBackupCachePath()); }), - base::BindOnce(&AmbientPhotoController::FetchBackupImages, - weak_factory_.GetWeakPtr())); -} - void AmbientPhotoController::FetchBackupImages() { const auto& backup_photo_urls = GetBackupPhotoUrls(); backup_retries_to_read_from_cache_ = backup_photo_urls.size(); for (size_t i = 0; i < backup_photo_urls.size(); i++) { - photo_cache_->DownloadPhotoToFile( + backup_photo_cache_->DownloadPhotoToFile( backup_photo_urls.at(i), + /*cache_index=*/i, + /*is_related=*/false, base::BindOnce(&AmbientPhotoController::OnBackupImageFetched, - weak_factory_.GetWeakPtr()), - GetBackupFilePath(i)); + weak_factory_.GetWeakPtr())); } } -void AmbientPhotoController::OnBackupImageFetched(base::FilePath file_path) { - if (file_path.empty()) { +void AmbientPhotoController::OnBackupImageFetched(bool success) { + if (!success) { // TODO(b/169807068) Change to retry individual failed images. resume_fetch_image_backoff_.InformOfRequest(/*succeeded=*/false); LOG(WARNING) << "Downloading backup image failed."; @@ -299,10 +244,8 @@ const AmbientModeTopic* AmbientPhotoController::GetNextTopic() { const auto& topics = ambient_backend_model_.topics(); // If no more topics, will read from cache. - if (topic_index_ == topics.size()) { - DVLOG(3) << "No more topics"; + if (topic_index_ == topics.size()) return nullptr; - } return &topics[topic_index_++]; } @@ -328,9 +271,7 @@ } void AmbientPhotoController::ResetImageData() { - image_data_.reset(); - related_image_data_.reset(); - image_details_.reset(); + cache_entry_.reset(); image_ = gfx::ImageSkia(); related_image_ = gfx::ImageSkia(); @@ -344,24 +285,21 @@ const int num_callbacks = (topic->related_image_url) ? 2 : 1; auto on_done = base::BarrierClosure( num_callbacks, - base::BindOnce(&AmbientPhotoController::OnAllPhotoRawDataAvailable, - weak_factory_.GetWeakPtr(), - /*from_downloading=*/true)); + base::BindOnce(&AmbientPhotoController::OnAllPhotoRawDataDownloaded, + weak_factory_.GetWeakPtr())); photo_cache_->DownloadPhoto( topic->url, - base::BindOnce(&AmbientPhotoController::OnPhotoRawDataAvailable, + base::BindOnce(&AmbientPhotoController::OnPhotoRawDataDownloaded, weak_factory_.GetWeakPtr(), - /*from_downloading=*/true, /*is_related_image=*/false, on_done, std::make_unique<std::string>(topic->details))); if (topic->related_image_url) { photo_cache_->DownloadPhoto( *(topic->related_image_url), - base::BindOnce(&AmbientPhotoController::OnPhotoRawDataAvailable, + base::BindOnce(&AmbientPhotoController::OnPhotoRawDataDownloaded, weak_factory_.GetWeakPtr(), - /*from_downloading=*/true, /*is_related_image=*/true, on_done, std::make_unique<std::string>(topic->details))); } @@ -400,28 +338,14 @@ } --backup_retries_to_read_from_cache_; - // Try to read a backup image. - auto photo_data = std::make_unique<std::string>(); - auto* photo_data_ptr = photo_data.get(); - auto on_done = base::BindRepeating( - &AmbientPhotoController::OnAllPhotoRawDataAvailable, - weak_factory_.GetWeakPtr(), /*from_downloading=*/false); - task_runner_->PostTaskAndReply( - FROM_HERE, - base::BindOnce( - [](size_t index, std::string* data) { - if (!base::ReadFileToString(GetBackupFilePath(index), data)) { - LOG(ERROR) << "Unable to read from backup cache."; - data->clear(); - } - }, - backup_cache_index_for_display_, photo_data_ptr), - base::BindOnce(&AmbientPhotoController::OnPhotoRawDataAvailable, - weak_factory_.GetWeakPtr(), /*from_downloading=*/false, - /*is_related_image=*/false, std::move(on_done), - /*details=*/std::make_unique<std::string>(), - std::move(photo_data))); + DVLOG(3) << "Read from backup cache index: " + << backup_cache_index_for_display_; + // Try to read a backup image. + backup_photo_cache_->ReadFiles( + /*cache_index=*/backup_cache_index_for_display_, + base::BindOnce(&AmbientPhotoController::OnAllPhotoRawDataAvailable, + weak_factory_.GetWeakPtr(), /*from_downloading=*/false)); backup_cache_index_for_display_++; if (backup_cache_index_for_display_ == GetBackupPhotoUrls().size()) @@ -430,82 +354,43 @@ } --retries_to_read_from_cache_; - std::string file_name = base::NumberToString(cache_index_for_display_); + int current_cache_index = cache_index_for_display_; ++cache_index_for_display_; if (cache_index_for_display_ == kMaxNumberOfCachedImages) cache_index_for_display_ = 0; - auto photo_data = std::make_unique<std::string>(); - auto photo_details = std::make_unique<std::string>(); - auto* photo_data_ptr = photo_data.get(); - auto* photo_details_ptr = photo_details.get(); - - auto on_done = base::BarrierClosure( - /*num_closures=*/2, + DVLOG(3) << "Read from cache index: " << current_cache_index; + photo_cache_->ReadFiles( + current_cache_index, base::BindOnce(&AmbientPhotoController::OnAllPhotoRawDataAvailable, - weak_factory_.GetWeakPtr(), - /*from_downloading=*/false)); - - task_runner_->PostTaskAndReply( - FROM_HERE, - base::BindOnce( - [](const std::string& file_name, std::string* photo_data, - std::string* photo_details) { - if (!base::ReadFileToString( - GetCachePath().Append(file_name + kPhotoFileExt), - photo_data)) { - photo_data->clear(); - } - if (!base::ReadFileToString( - GetCachePath().Append(file_name + kPhotoDetailsFileExt), - photo_details)) { - photo_details->clear(); - } - }, - file_name, photo_data_ptr, photo_details_ptr), - base::BindOnce(&AmbientPhotoController::OnPhotoRawDataAvailable, - weak_factory_.GetWeakPtr(), /*from_downloading=*/false, - /*is_related_image=*/false, on_done, - std::move(photo_details), std::move(photo_data))); - - auto related_photo_data = std::make_unique<std::string>(); - auto* related_photo_data_ptr = related_photo_data.get(); - task_runner_->PostTaskAndReply( - FROM_HERE, - base::BindOnce( - [](const std::string& file_name, std::string* related_photo_data) { - const base::FilePath& file = GetRelatedFilePath(file_name); - if (!base::PathExists(file) || - !base::ReadFileToString(file, related_photo_data)) { - related_photo_data->clear(); - } - }, - file_name, related_photo_data_ptr), - base::BindOnce(&AmbientPhotoController::OnPhotoRawDataAvailable, - weak_factory_.GetWeakPtr(), /*from_downloading=*/false, - /*is_related_image=*/true, on_done, - /*details=*/std::make_unique<std::string>(), - std::move(related_photo_data))); + weak_factory_.GetWeakPtr(), /*from_downloading=*/false)); } -void AmbientPhotoController::OnPhotoRawDataAvailable( - bool from_downloading, +void AmbientPhotoController::OnPhotoRawDataDownloaded( bool is_related_image, base::RepeatingClosure on_done, std::unique_ptr<std::string> details, std::unique_ptr<std::string> data) { - if (is_related_image) { - related_image_data_ = std::move(data); - } else { - image_data_ = std::move(data); - image_details_ = std::move(details); - } + cache_entry_.details = std::move(details); + + if (is_related_image) + cache_entry_.related_image = std::move(data); + else + cache_entry_.image = std::move(data); + std::move(on_done).Run(); } -void AmbientPhotoController::OnAllPhotoRawDataAvailable(bool from_downloading) { - if (!image_data_ || image_data_->empty()) { +void AmbientPhotoController::OnAllPhotoRawDataDownloaded() { + OnAllPhotoRawDataAvailable(/*from_downloading=*/true, + std::move(cache_entry_)); +} + +void AmbientPhotoController::OnAllPhotoRawDataAvailable( + bool from_downloading, + PhotoCacheEntry cache_entry) { + if (!cache_entry.image || cache_entry.image->empty()) { if (from_downloading) { LOG(ERROR) << "Failed to download image"; resume_fetch_image_backoff_.InformOfRequest(/*succeeded=*/false); @@ -514,57 +399,49 @@ TryReadPhotoRawData(); return; } - DVLOG_IF(3, from_downloading) - << "Save photo to cache index: " << cache_index_for_store_; - const std::string file_name = base::NumberToString(cache_index_for_store_); - // If the data is fetched from downloading, write to disk. - // Note: WriteFile() could fail. The saved file name may not be continuous. - if (from_downloading) - ++cache_index_for_store_; - if (cache_index_for_store_ == kMaxNumberOfCachedImages) - cache_index_for_store_ = 0; - const int num_callbacks = related_image_data_ ? 2 : 1; + if (from_downloading) { + // If the data is fetched from downloading, write to disk. + // Note: WriteFiles could fail. The saved file name may not be continuous. + DVLOG(3) << "Save photo to cache index: " << cache_index_for_store_; + auto current_cache_index = cache_index_for_store_; + ++cache_index_for_store_; + if (cache_index_for_store_ == kMaxNumberOfCachedImages) + cache_index_for_store_ = 0; + + auto* image = cache_entry.image.get(); + auto* details = cache_entry.details.get(); + auto* related_image = cache_entry.related_image.get(); + + photo_cache_->WriteFiles( + /*cache_index=*/current_cache_index, image, details, related_image, + base::BindOnce(&AmbientPhotoController::OnPhotoRawDataSaved, + weak_factory_.GetWeakPtr(), from_downloading, + std::move(cache_entry))); + } else { + OnPhotoRawDataSaved(from_downloading, std::move(cache_entry)); + } +} + +void AmbientPhotoController::OnPhotoRawDataSaved(bool from_downloading, + PhotoCacheEntry cache_entry) { + bool has_related = + cache_entry.related_image && !cache_entry.related_image->empty(); + const int num_callbacks = has_related ? 2 : 1; + auto on_done = base::BarrierClosure( num_callbacks, base::BindOnce(&AmbientPhotoController::OnAllPhotoDecoded, weak_factory_.GetWeakPtr(), from_downloading, - /*hash=*/base::SHA1HashString(*image_data_))); + /*hash=*/base::SHA1HashString(*cache_entry.image))); - base::Optional<std::string> related_image_data; - if (related_image_data_) - related_image_data = *related_image_data_; - - task_runner_->PostTaskAndReply( - FROM_HERE, - base::BindOnce( - [](const std::string& file_name, bool need_to_save, - const std::string& data, const std::string& details, - const base::Optional<std::string>& related_data) { - if (need_to_save) { - WriteFile(GetCachePath().Append(file_name + kPhotoFileExt), data); - WriteFile(GetCachePath().Append(file_name + kPhotoDetailsFileExt), - details); - const base::FilePath& related_data_file = - GetRelatedFilePath(file_name); - if (related_data) { - WriteFile(related_data_file, *related_data); - } else { - if (base::PathExists(related_data_file)) - base::DeleteFile(related_data_file); - } - } - }, - file_name, from_downloading, *image_data_, *image_details_, - std::move(related_image_data)), - base::BindOnce(&AmbientPhotoController::DecodePhotoRawData, - weak_factory_.GetWeakPtr(), from_downloading, + DecodePhotoRawData(from_downloading, /*is_related_image=*/false, on_done, - std::move(image_data_))); + std::move(cache_entry.image)); - if (related_image_data_) { + if (has_related) { DecodePhotoRawData(from_downloading, /*is_related_image=*/true, on_done, - std::move(related_image_data_)); + std::move(cache_entry.related_image)); } } @@ -617,7 +494,8 @@ PhotoWithDetails detailed_photo; detailed_photo.photo = image_; detailed_photo.related_photo = related_image_; - detailed_photo.details = *image_details_; + if (cache_entry_.details) + detailed_photo.details = *cache_entry_.details; detailed_photo.hash = hash; ResetImageData(); @@ -678,7 +556,7 @@ } void AmbientPhotoController::FetchBackupImagesForTesting() { - PrepareFetchBackupImages(); + FetchBackupImages(); } } // namespace ash
diff --git a/ash/ambient/ambient_photo_controller.h b/ash/ambient/ambient_photo_controller.h index 39996e7b..81f1abcf 100644 --- a/ash/ambient/ambient_photo_controller.h +++ b/ash/ambient/ambient_photo_controller.h
@@ -78,6 +78,8 @@ // Clear cache when Settings changes. void ClearCache(); + void InitCache(); + private: friend class AmbientAshTestBase; @@ -89,13 +91,10 @@ void ScheduleRefreshImage(); - // Create the backup cache directory and start downloading images. - void PrepareFetchBackupImages(); - // Download backup cache images. void FetchBackupImages(); - void OnBackupImageFetched(base::FilePath file_path); + void OnBackupImageFetched(bool success); void GetScreenUpdateInfo(); @@ -114,13 +113,17 @@ // Try to read photo raw data from cache. void TryReadPhotoRawData(); - void OnPhotoRawDataAvailable(bool from_downloading, - bool is_related_image, - base::RepeatingClosure on_done, - std::unique_ptr<std::string> details, - std::unique_ptr<std::string> data); + void OnPhotoRawDataDownloaded(bool is_related_image, + base::RepeatingClosure on_done, + std::unique_ptr<std::string> details, + std::unique_ptr<std::string> data); - void OnAllPhotoRawDataAvailable(bool from_downloading); + void OnAllPhotoRawDataDownloaded(); + + void OnAllPhotoRawDataAvailable(bool from_downloading, + PhotoCacheEntry cache_entry); + + void OnPhotoRawDataSaved(bool from_downloading, PhotoCacheEntry cache_entry); void DecodePhotoRawData(bool from_downloading, bool is_related_image, @@ -152,6 +155,15 @@ return photo_cache_.get(); } + void set_backup_photo_cache_for_testing( + std::unique_ptr<AmbientPhotoCache> photo_cache) { + backup_photo_cache_ = std::move(photo_cache); + } + + AmbientPhotoCache* get_backup_photo_cache_for_testing() { + return backup_photo_cache_.get(); + } + void FetchTopicsForTesting(); void FetchImageForTesting(); @@ -206,13 +218,12 @@ ambient_backend_model_observer_{this}; std::unique_ptr<AmbientPhotoCache> photo_cache_; + std::unique_ptr<AmbientPhotoCache> backup_photo_cache_; scoped_refptr<base::SequencedTaskRunner> task_runner_; // Temporary data store when fetching images and details. - std::unique_ptr<std::string> image_data_; - std::unique_ptr<std::string> related_image_data_; - std::unique_ptr<std::string> image_details_; + PhotoCacheEntry cache_entry_; gfx::ImageSkia image_; gfx::ImageSkia related_image_;
diff --git a/ash/ambient/ambient_photo_controller_unittest.cc b/ash/ambient/ambient_photo_controller_unittest.cc index 0f07d63..748588da9 100644 --- a/ash/ambient/ambient_photo_controller_unittest.cc +++ b/ash/ambient/ambient_photo_controller_unittest.cc
@@ -26,6 +26,7 @@ #include "base/path_service.h" #include "base/run_loop.h" #include "base/scoped_observer.h" +#include "base/stl_util.h" #include "base/system/sys_info.h" #include "base/test/bind.h" #include "base/timer/timer.h" @@ -49,44 +50,35 @@ class AmbientPhotoControllerTest : public AmbientAshTestBase { public: - // AmbientAshTestBase: - void SetUp() override { - AmbientAshTestBase::SetUp(); - CleanupAmbientDir(); - } - void TearDown() override { - AmbientAshTestBase::TearDown(); - CleanupAmbientDir(); - } - - void CleanupAmbientDir() { base::DeletePathRecursively(GetRootDir()); } - - std::vector<base::FilePath> GetFilePathsInDir(const base::FilePath& dir) { - std::vector<base::FilePath> result; - base::FileEnumerator files( - dir, /*recursive=*/false, - base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES); - for (base::FilePath current = files.Next(); !current.empty(); - current = files.Next()) { - result.emplace_back(current); + std::vector<int> GetSavedCacheIndices(bool backup = false) { + std::vector<int> result; + const auto& map = backup ? GetBackupCachedFiles() : GetCachedFiles(); + for (auto& it : map) { + result.push_back(it.first); } return result; } - base::FilePath GetRootDir() { - base::FilePath home_dir; - base::PathService::Get(base::DIR_HOME, &home_dir); - return home_dir.Append(FILE_PATH_LITERAL(kAmbientModeDirectoryName)); + const PhotoCacheEntry* GetCacheEntryAtIndex(int cache_index, + bool backup = false) { + const auto& files = backup ? GetBackupCachedFiles() : GetCachedFiles(); + auto it = files.find(cache_index); + if (it == files.end()) + return nullptr; + else + return &(it->second); } - base::FilePath GetCacheDir() { - return GetRootDir().Append( - FILE_PATH_LITERAL(kAmbientModeCacheDirectoryName)); - } - - base::FilePath GetBackupCacheDir() { - return GetRootDir().Append( - FILE_PATH_LITERAL(kAmbientModeBackupCacheDirectoryName)); + void WriteCacheDataBlocking(int cache_index, + const std::string* image = nullptr, + const std::string* details = nullptr, + const std::string* related_image = nullptr) { + base::RunLoop loop; + photo_cache()->WriteFiles(/*cache_index=*/cache_index, /*image=*/image, + /*details=*/details, + /*related_image=*/related_image, + loop.QuitClosure()); + loop.Run(); } }; @@ -158,38 +150,27 @@ // Test that image is saved. TEST_F(AmbientPhotoControllerTest, ShouldSaveImagesOnDisk) { - base::FilePath ambient_image_path = GetCacheDir(); - // Start to refresh images. It will download two images immediately and write // them in |ambient_image_path|. It will also download one more image after // fast forward. It will also download the related images and not cache them. photo_controller()->StartScreenUpdate(); FastForwardToNextImage(); - // Count files and directories in ambient_image_path. There should be six - // files that were just created to save image files for this ambient mode - // session. - EXPECT_TRUE(base::PathExists(ambient_image_path)); - auto file_paths = GetFilePathsInDir(ambient_image_path); - // Three image files, three related image files, and three attribution files. - EXPECT_EQ(file_paths.size(), 9u); - for (auto& path : file_paths) { - // No sub directories. - EXPECT_FALSE(base::DirectoryExists(path)); - } + // Count number of writes to cache. There should be three cache writes during + // this ambient mode session. + auto file_paths = GetSavedCacheIndices(); + EXPECT_EQ(file_paths.size(), 3u); } // Test that image is save and will not be deleted when stopping ambient mode. TEST_F(AmbientPhotoControllerTest, ShouldNotDeleteImagesOnDisk) { - base::FilePath ambient_image_path = GetCacheDir(); - // Start to refresh images. It will download two images immediately and write // them in |ambient_image_path|. It will also download one more image after // fast forward. It will also download the related images and not cache them. photo_controller()->StartScreenUpdate(); FastForwardToNextImage(); - EXPECT_TRUE(base::PathExists(ambient_image_path)); + EXPECT_EQ(GetSavedCacheIndices().size(), 3u); auto image = photo_controller()->ambient_backend_model()->GetNextImage(); EXPECT_FALSE(image.IsNull()); @@ -198,29 +179,14 @@ photo_controller()->StopScreenUpdate(); FastForwardToNextImage(); - EXPECT_TRUE(base::PathExists(ambient_image_path)); - EXPECT_FALSE(base::IsDirectoryEmpty(ambient_image_path)); + EXPECT_EQ(GetSavedCacheIndices().size(), 3u); image = photo_controller()->ambient_backend_model()->GetNextImage(); EXPECT_TRUE(image.IsNull()); - - // Count files and directories in ambient_image_path. There should be six - // files that were just created to save image files for the prior ambient mode - // session. - EXPECT_TRUE(base::PathExists(ambient_image_path)); - auto file_paths = GetFilePathsInDir(ambient_image_path); - // Three image files, three related image files, and three attribution files. - EXPECT_EQ(file_paths.size(), 9u); - for (auto& path : file_paths) { - // No sub directories. - EXPECT_FALSE(base::DirectoryExists(path)); - } } // Test that image is read from disk when no more topics. TEST_F(AmbientPhotoControllerTest, ShouldReadCacheWhenNoMoreTopics) { - base::FilePath ambient_image_path = GetCacheDir(); - FetchImage(); FastForwardToNextImage(); // Topics is empty. Will read from cache, which is empty. @@ -228,9 +194,8 @@ EXPECT_TRUE(image.IsNull()); // Save a file to check if it gets read for display. - auto cached_image = ambient_image_path.Append("0.img"); - base::CreateDirectory(ambient_image_path); - base::WriteFile(cached_image, "cached image"); + std::string data("cached image"); + WriteCacheDataBlocking(/*cache_index=*/0, &data); // Reset variables in photo controller. photo_controller()->StopScreenUpdate(); @@ -238,27 +203,21 @@ FastForwardToNextImage(); image = photo_controller()->ambient_backend_model()->GetCurrentImage(); EXPECT_FALSE(image.IsNull()); - - // Clean up. - base::DeletePathRecursively(ambient_image_path); } // Test that will try 100 times to read image from disk when no more topics. TEST_F(AmbientPhotoControllerTest, ShouldTry100TimesToReadCacheWhenNoMoreTopics) { - base::FilePath ambient_image_path = GetCacheDir(); - FetchImage(); FastForwardToNextImage(); // Topics is empty. Will read from cache, which is empty. auto image = photo_controller()->ambient_backend_model()->GetCurrentImage(); EXPECT_TRUE(image.IsNull()); - // The initial file name to be read is 0. Save a file with 99.img to check if - // it gets read for display. - auto cached_image = ambient_image_path.Append("99.img"); - base::CreateDirectory(ambient_image_path); - base::WriteFile(cached_image, "cached image"); + // The initial file name to be read is 0. Save a file with 99.img to check + // if it gets read for display. + std::string data("cached image"); + WriteCacheDataBlocking(/*cache_index=*/99, &data); // Reset variables in photo controller. photo_controller()->StopScreenUpdate(); @@ -270,8 +229,6 @@ // Test that image is read from disk when image downloading failed. TEST_F(AmbientPhotoControllerTest, ShouldReadCacheWhenImageDownloadingFailed) { - base::FilePath ambient_image_path = GetCacheDir(); - SetDownloadPhotoData(""); FetchTopics(); // Forward a little bit time. FetchTopics() will succeed. Downloading should @@ -281,9 +238,8 @@ EXPECT_TRUE(image.IsNull()); // Save a file to check if it gets read for display. - auto cached_image = ambient_image_path.Append("0.img"); - base::CreateDirectory(ambient_image_path); - base::WriteFile(cached_image, "cached image"); + std::string data("cached image"); + WriteCacheDataBlocking(/*cache_index=*/0, &data); // Reset variables in photo controller. photo_controller()->StopScreenUpdate(); @@ -297,8 +253,6 @@ // Test that image is read from disk when image decoding failed. TEST_F(AmbientPhotoControllerTest, ShouldReadCacheWhenImageDecodingFailed) { - base::FilePath ambient_image_path = GetCacheDir(); - SetDecodePhotoImage(gfx::ImageSkia()); FetchTopics(); // Forward a little bit time. FetchTopics() will succeed. @@ -311,8 +265,6 @@ // Test that image will refresh when have more topics. TEST_F(AmbientPhotoControllerTest, ShouldResumWhenHaveMoreTopics) { - base::FilePath ambient_image_path = GetCacheDir(); - FetchImage(); FastForwardToNextImage(); // Topics is empty. Will read from cache, which is empty. @@ -327,37 +279,31 @@ } TEST_F(AmbientPhotoControllerTest, ShouldDownloadBackupImagesWhenScheduled) { - base::FilePath backup_image_path = GetBackupCacheDir(); - std::string expected_data = "backup data"; - SetDownloadPhotoData(expected_data); + SetBackupDownloadPhotoData(expected_data); photo_controller()->ScheduleFetchBackupImages(); EXPECT_TRUE( photo_controller()->backup_photo_refresh_timer_for_testing().IsRunning()); - // TImer is running but download has not started yet. - EXPECT_FALSE(base::DirectoryExists(GetBackupCacheDir())); + // Timer is running but download has not started yet. + EXPECT_TRUE(GetSavedCacheIndices(/*backup=*/true).empty()); task_environment()->FastForwardBy(kBackupPhotoRefreshDelay); // Timer should have stopped. EXPECT_FALSE( photo_controller()->backup_photo_refresh_timer_for_testing().IsRunning()); - // Download has triggered and backup cache directory is created. - EXPECT_TRUE(base::DirectoryExists(backup_image_path)); - - // Should be two files in backup cache directory. - auto paths = GetFilePathsInDir(backup_image_path); - std::sort(paths.begin(), paths.end()); - EXPECT_EQ(paths.size(), 2u); - EXPECT_EQ(paths[0].BaseName().value(), "0.img"); - EXPECT_EQ(paths[1].BaseName().value(), "1.img"); - for (const auto& path : paths) { - std::string data; - base::ReadFileToString(path, &data); - EXPECT_EQ(data, expected_data); + // Should have been two cache writes to backup data. + const auto& backup_data = GetBackupCachedFiles(); + EXPECT_EQ(backup_data.size(), 2u); + EXPECT_TRUE(base::Contains(backup_data, 0)); + EXPECT_TRUE(base::Contains(backup_data, 1)); + for (const auto& i : backup_data) { + EXPECT_EQ(*(i.second.image), expected_data); + EXPECT_FALSE(i.second.details); + EXPECT_FALSE(i.second.related_image); } } @@ -371,11 +317,7 @@ ClearDownloadPhotoData(); task_environment()->FastForwardBy(kBackupPhotoRefreshDelay); - // Directory should have been created, but with no files in it. - EXPECT_TRUE(base::DirectoryExists(GetBackupCacheDir())); - - auto paths = GetFilePathsInDir(GetBackupCacheDir()); - EXPECT_EQ(paths.size(), 0u); + EXPECT_TRUE(GetBackupCachedFiles().empty()); // Timer should have restarted. EXPECT_TRUE( @@ -389,7 +331,7 @@ EXPECT_TRUE( photo_controller()->backup_photo_refresh_timer_for_testing().IsRunning()); - SetDownloadPhotoData("image data"); + SetBackupDownloadPhotoData("image data"); photo_controller()->StartScreenUpdate(); @@ -399,19 +341,16 @@ task_environment()->RunUntilIdle(); - // Download has triggered and backup cache directory is created. - EXPECT_TRUE(base::DirectoryExists(GetBackupCacheDir())); - - // Should be two files in backup cache directory. - auto paths = GetFilePathsInDir(GetBackupCacheDir()); - std::sort(paths.begin(), paths.end()); - EXPECT_EQ(paths.size(), 2u); - EXPECT_EQ(paths[0].BaseName().value(), "0.img"); - EXPECT_EQ(paths[1].BaseName().value(), "1.img"); - for (const auto& path : paths) { - std::string data; - base::ReadFileToString(path, &data); - EXPECT_EQ(data, "image data"); + // Download has triggered and backup cache directory is created. Should be + // two cache writes to backup cache. + const auto& backup_data = GetBackupCachedFiles(); + EXPECT_EQ(backup_data.size(), 2u); + EXPECT_TRUE(base::Contains(backup_data, 0)); + EXPECT_TRUE(base::Contains(backup_data, 1)); + for (const auto& i : backup_data) { + EXPECT_EQ(*(i.second.image), "image data"); + EXPECT_FALSE(i.second.details); + EXPECT_FALSE(i.second.related_image); } }
diff --git a/ash/ambient/test/ambient_ash_test_base.cc b/ash/ambient/test/ambient_ash_test_base.cc index 458182a..ed10e79 100644 --- a/ash/ambient/test/ambient_ash_test_base.cc +++ b/ash/ambient/test/ambient_ash_test_base.cc
@@ -4,12 +4,14 @@ #include "ash/ambient/test/ambient_ash_test_base.h" +#include <map> #include <memory> #include <utility> #include <vector> #include "ash/ambient/ambient_access_token_controller.h" #include "ash/ambient/ambient_constants.h" +#include "ash/ambient/ambient_photo_cache.h" #include "ash/ambient/ambient_photo_controller.h" #include "ash/ambient/test/ambient_ash_test_helper.h" #include "ash/ambient/ui/ambient_background_image_view.h" @@ -24,9 +26,7 @@ #include "ash/session/session_controller_impl.h" #include "ash/shell.h" #include "base/callback.h" -#include "base/files/file_util.h" #include "base/memory/ptr_util.h" -#include "base/notreached.h" #include "base/run_loop.h" #include "base/sequenced_task_runner.h" #include "base/threading/scoped_blocking_call.h" @@ -35,7 +35,6 @@ #include "chromeos/dbus/power/fake_power_manager_client.h" #include "chromeos/dbus/power/power_manager_client.h" #include "chromeos/dbus/power_manager/idle.pb.h" -#include "ui/display/screen.h" #include "ui/gfx/image/image_skia.h" #include "ui/gfx/image/image_unittest_util.h" #include "ui/views/controls/label.h" @@ -66,26 +65,30 @@ FROM_HERE, base::BindOnce(std::move(callback), std::move(data)), base::TimeDelta::FromMilliseconds(1)); } + void DownloadPhotoToFile(const std::string& url, - base::OnceCallback<void(base::FilePath)> callback, - const base::FilePath& file_path) override { + int cache_index, + bool is_related, + base::OnceCallback<void(bool)> callback) override { if (!download_data_) { base::SequencedTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::BindOnce(std::move(callback), base::FilePath())); + FROM_HERE, base::BindOnce(std::move(callback), /*success=*/false)); return; } - if (!WriteFile(file_path, *download_data_)) { - LOG(WARNING) << "error writing file to file_path: " << file_path; - - base::SequencedTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::BindOnce(std::move(callback), base::FilePath())); - return; - } + files_.insert(std::pair<int, PhotoCacheEntry>( + cache_index, + PhotoCacheEntry( + is_related ? nullptr + : std::make_unique<std::string>(*download_data_), + /*details=*/nullptr, + is_related ? std::make_unique<std::string>(*download_data_) + : nullptr))); base::SequencedTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::BindOnce(std::move(callback), file_path)); + FROM_HERE, base::BindOnce(std::move(callback), /*success=*/true)); } + void DecodePhoto( std::unique_ptr<std::string> data, base::OnceCallback<void(const gfx::ImageSkia&)> callback) override { @@ -101,6 +104,47 @@ FROM_HERE, base::BindOnce(std::move(callback), image)); } + void WriteFiles(int cache_index, + const std::string* const image, + const std::string* const details, + const std::string* const related_image, + base::OnceClosure callback) override { + files_.insert(std::pair<int, PhotoCacheEntry>( + cache_index, + PhotoCacheEntry( + image ? std::make_unique<std::string>(*image) : nullptr, + details ? std::make_unique<std::string>(*details) : nullptr, + related_image ? std::make_unique<std::string>(*related_image) + : nullptr))); + std::move(callback).Run(); + } + + void ReadFiles(int cache_index, + base::OnceCallback<void(PhotoCacheEntry)> callback) override { + auto it = files_.find(cache_index); + if (it == files_.end()) { + std::move(callback).Run(PhotoCacheEntry()); + return; + } + + std::move(callback).Run(PhotoCacheEntry( + it->second.image ? std::make_unique<std::string>(*(it->second.image)) + : nullptr, + it->second.details + ? std::make_unique<std::string>(*(it->second.details)) + : nullptr, + it->second.related_image + ? std::make_unique<std::string>(*(it->second.related_image)) + : nullptr)); + } + void Clear() override { + download_count_ = 0; + download_data_.reset(); + decoded_size_ = gfx::Size(10, 20); + decoded_image_.reset(); + files_.clear(); + } + void SetDownloadData(std::unique_ptr<std::string> download_data) { download_data_ = std::move(download_data); } @@ -112,6 +156,8 @@ void SetDecodedPhoto(const gfx::ImageSkia& image) { decoded_image_ = image; } + const std::map<int, PhotoCacheEntry>& get_files() { return files_; } + private: int download_count_ = 0; @@ -122,6 +168,8 @@ gfx::Size decoded_size_{10, 20}; // If set, will replay this image. base::Optional<gfx::ImageSkia> decoded_image_; + + std::map<int, PhotoCacheEntry> files_; }; AmbientAshTestBase::AmbientAshTestBase() @@ -139,6 +187,8 @@ std::make_unique<FakeAmbientBackendControllerImpl>()); photo_controller()->set_photo_cache_for_testing( std::make_unique<TestAmbientPhotoCacheImpl>()); + photo_controller()->set_backup_photo_cache_for_testing( + std::make_unique<TestAmbientPhotoCacheImpl>()); token_controller()->SetTokenUsageBufferForTesting( base::TimeDelta::FromSeconds(30)); SetAmbientModeEnabled(true); @@ -380,6 +430,21 @@ return token_controller()->GetTimeUntilReleaseForTesting(); } +const std::map<int, PhotoCacheEntry>& AmbientAshTestBase::GetCachedFiles() { + auto* photo_cache = static_cast<TestAmbientPhotoCacheImpl*>( + photo_controller()->get_photo_cache_for_testing()); + + return photo_cache->get_files(); +} + +const std::map<int, PhotoCacheEntry>& +AmbientAshTestBase::GetBackupCachedFiles() { + auto* photo_cache = static_cast<TestAmbientPhotoCacheImpl*>( + photo_controller()->get_backup_photo_cache_for_testing()); + + return photo_cache->get_files(); +} + AmbientController* AmbientAshTestBase::ambient_controller() { return Shell::Get()->ambient_controller(); } @@ -388,6 +453,10 @@ return ambient_controller()->ambient_photo_controller(); } +AmbientPhotoCache* AmbientAshTestBase::photo_cache() { + return photo_controller()->get_photo_cache_for_testing(); +} + std::vector<AmbientContainerView*> AmbientAshTestBase::GetContainerViews() { std::vector<AmbientContainerView*> result; for (auto* ctrl : RootWindowController::root_window_controllers()) { @@ -449,6 +518,20 @@ photo_cache->SetDownloadData(nullptr); } +void AmbientAshTestBase::SetBackupDownloadPhotoData(std::string data) { + auto* backup_cache = static_cast<TestAmbientPhotoCacheImpl*>( + photo_controller()->get_backup_photo_cache_for_testing()); + + backup_cache->SetDownloadData(std::make_unique<std::string>(std::move(data))); +} + +void AmbientAshTestBase::ClearBackupDownloadPhotoData() { + auto* backup_cache = static_cast<TestAmbientPhotoCacheImpl*>( + photo_controller()->get_backup_photo_cache_for_testing()); + + backup_cache->SetDownloadData(nullptr); +} + void AmbientAshTestBase::SetDecodePhotoImage(const gfx::ImageSkia& image) { auto* photo_cache = static_cast<TestAmbientPhotoCacheImpl*>( photo_controller()->get_photo_cache_for_testing());
diff --git a/ash/ambient/test/ambient_ash_test_base.h b/ash/ambient/test/ambient_ash_test_base.h index f4d69ff3..0828bcc 100644 --- a/ash/ambient/test/ambient_ash_test_base.h +++ b/ash/ambient/test/ambient_ash_test_base.h
@@ -137,10 +137,15 @@ // Returns the media string view for the default display. MediaStringView* GetMediaStringView(); + const std::map<int, PhotoCacheEntry>& GetCachedFiles(); + const std::map<int, PhotoCacheEntry>& GetBackupCachedFiles(); + AmbientController* ambient_controller(); AmbientPhotoController* photo_controller(); + AmbientPhotoCache* photo_cache(); + // Returns the top-level views which contains all the ambient components. std::vector<AmbientContainerView*> GetContainerViews(); // Returns the top level ambient container view for the primary root window. @@ -160,6 +165,10 @@ void ClearDownloadPhotoData(); + void SetBackupDownloadPhotoData(std::string data); + + void ClearBackupDownloadPhotoData(); + void SetDecodePhotoImage(const gfx::ImageSkia& image); private:
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd index d6a5fcab..e8ef0b8f 100644 --- a/ash/ash_strings.grd +++ b/ash/ash_strings.grd
@@ -554,6 +554,12 @@ <message name="IDS_DIALOG_MESSAGE_SLOW_BOOT" desc="The body of the Slow Boot Shutdown Confirmation Dialog."> Your screen will go blank for longer than usual (up to a minute) during this update. Please don't press the power button while the update is in progress. </message> + <message name="IDS_UPDATE_NOTIFICATION_TITLE_LACROS" desc="The title of the notification to notify that the user should restart to get an update for the experimental 'Lacros' browser."> + Lacros update available + </message> + <message name="IDS_UPDATE_NOTIFICATION_MESSAGE_LACROS" desc="The body of the notification to notify that the user should restart to get an update for the experimental 'Lacros' browser."> + Device restart is required to apply the update. + </message> <message name="IDS_ASH_FLOATING_ACCESSIBILITY_DETAILED_MENU" desc="The name of the quick settings view of floating accessibility menu."> Accessibility settings @@ -3125,6 +3131,14 @@ Window <ph name="WINDOW_TITLE">$1<ex>Files</ex></ph> moved from Desk <ph name="ACTIVE_DESK">$2</ph> to Desk <ph name="TARGET_DESK">$3</ph> </message> + <!-- Alt Tab --> + <message name="IDS_ASH_ALT_TAB_ALL_DESKS_MODE" desc="Alt-tab shows windows from all desks."> + All desks + </message> + <message name="IDS_ASH_ALT_TAB_CURRENT_DESK_MODE" desc="Alt-tab shows only windows from the current desk."> + Current desk + </message> + <!-- Back gesture --> <message name="IDS_ASH_BACK_GESTURE_CONTEXTUAL_NUDGE" desc="Information shown in the suggestion label for the contextual nudge of the back gesture in tablet mode"> Swipe from the left to go back
diff --git a/ash/ash_strings_grd/IDS_ASH_ALT_TAB_ALL_DESKS_MODE.png.sha1 b/ash/ash_strings_grd/IDS_ASH_ALT_TAB_ALL_DESKS_MODE.png.sha1 new file mode 100644 index 0000000..106a80a --- /dev/null +++ b/ash/ash_strings_grd/IDS_ASH_ALT_TAB_ALL_DESKS_MODE.png.sha1
@@ -0,0 +1 @@ +a497fea9619c636ff78f2f5bba3724d5e1ad84d4 \ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_ASH_ALT_TAB_CURRENT_DESK_MODE.png.sha1 b/ash/ash_strings_grd/IDS_ASH_ALT_TAB_CURRENT_DESK_MODE.png.sha1 new file mode 100644 index 0000000..b29053b --- /dev/null +++ b/ash/ash_strings_grd/IDS_ASH_ALT_TAB_CURRENT_DESK_MODE.png.sha1
@@ -0,0 +1 @@ +df4b84a84d9cad8a10afc284876e6a4d279435e0 \ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_UPDATE_NOTIFICATION_MESSAGE_LACROS.png.sha1 b/ash/ash_strings_grd/IDS_UPDATE_NOTIFICATION_MESSAGE_LACROS.png.sha1 new file mode 100644 index 0000000..0dc2187 --- /dev/null +++ b/ash/ash_strings_grd/IDS_UPDATE_NOTIFICATION_MESSAGE_LACROS.png.sha1
@@ -0,0 +1 @@ +3a69208c8b754548e419e8c106a0c8df33715c49 \ No newline at end of file
diff --git a/ash/ash_strings_grd/IDS_UPDATE_NOTIFICATION_TITLE_LACROS.png.sha1 b/ash/ash_strings_grd/IDS_UPDATE_NOTIFICATION_TITLE_LACROS.png.sha1 new file mode 100644 index 0000000..0dc2187 --- /dev/null +++ b/ash/ash_strings_grd/IDS_UPDATE_NOTIFICATION_TITLE_LACROS.png.sha1
@@ -0,0 +1 @@ +3a69208c8b754548e419e8c106a0c8df33715c49 \ No newline at end of file
diff --git a/ash/capture_mode/capture_mode_controller.cc b/ash/capture_mode/capture_mode_controller.cc index e0ba962..ad92055 100644 --- a/ash/capture_mode/capture_mode_controller.cc +++ b/ash/capture_mode/capture_mode_controller.cc
@@ -16,6 +16,7 @@ #include "ash/public/cpp/holding_space/holding_space_controller.h" #include "ash/public/cpp/notification_utils.h" #include "ash/resources/vector_icons/vector_icons.h" +#include "ash/root_window_controller.h" #include "ash/session/session_controller_impl.h" #include "ash/shell.h" #include "ash/strings/grit/ash_strings.h" @@ -68,8 +69,8 @@ // The format strings of the file names of captured images. // TODO(afakhry): Discuss with UX localizing "Screenshot" and "Screen // recording". -constexpr char kScreenshotFileNameFmtStr[] = "Screenshot %s %s.png"; -constexpr char kVideoFileNameFmtStr[] = "Screen recording %s %s.webm"; +constexpr char kScreenshotFileNameFmtStr[] = "Screenshot %s %s"; +constexpr char kVideoFileNameFmtStr[] = "Screen recording %s %s"; constexpr char kDateFmtStr[] = "%d-%02d-%02d"; constexpr char k24HourTimeFmtStr[] = "%02d.%02d.%02d"; constexpr char kAmPmTimeFmtStr[] = "%d.%02d.%02d"; @@ -347,6 +348,29 @@ last_capture_region_update_time_ = base::TimeTicks::Now(); } +void CaptureModeController::CaptureScreenshotsOfAllDisplays() { + if (delegate_->IsCaptureModeInitRestricted()) { + ShowDisabledNotification(); + return; + } + // Get a vector of RootWindowControllers with primary root window at first. + const std::vector<RootWindowController*> controllers = + RootWindowController::root_window_controllers(); + // Capture screenshot for each individual display. + int display_index = 1; + for (RootWindowController* controller : controllers) { + // TODO(shidi): Check with UX what notification should show if + // some (but not all) of the displays have restricted content and + // whether we should localize the display name. + const CaptureParams capture_params{controller->GetRootWindow(), + controller->GetRootWindow()->bounds()}; + CaptureImage(capture_params, controllers.size() == 1 + ? BuildImagePath() + : BuildImagePathForDisplay(display_index)); + ++display_index; + } +} + void CaptureModeController::PerformCapture() { DCHECK(IsActive()); const base::Optional<CaptureParams> capture_params = GetCaptureParams(); @@ -362,8 +386,10 @@ DCHECK(capture_mode_session_); capture_mode_session_->ReportSessionHistograms(); - if (type_ == CaptureModeType::kImage) - CaptureImage(*capture_params); + if (type_ == CaptureModeType::kImage) { + CaptureImage(*capture_params, BuildImagePath()); + } + else CaptureVideo(*capture_params); } @@ -608,20 +634,21 @@ video_recording_watcher_.reset(); } -void CaptureModeController::CaptureImage(const CaptureParams& capture_params) { +void CaptureModeController::CaptureImage(const CaptureParams& capture_params, + const base::FilePath& path) { DCHECK_EQ(CaptureModeType::kImage, type_); DCHECK(IsCaptureAllowed(capture_params)); // Stop the capture session now, so as not to take a screenshot of the capture // bar. - Stop(); + if (IsActive()) + Stop(); DCHECK(!capture_params.bounds.IsEmpty()); - ui::GrabWindowSnapshotAsyncPNG( capture_params.window, capture_params.bounds, base::BindOnce(&CaptureModeController::OnImageCaptured, - weak_ptr_factory_.GetWeakPtr(), base::Time::Now())); + weak_ptr_factory_.GetWeakPtr(), path)); ++num_screenshots_taken_in_last_day_; ++num_screenshots_taken_in_last_week_; @@ -645,7 +672,7 @@ } void CaptureModeController::OnImageCaptured( - base::Time timestamp, + const base::FilePath& path, scoped_refptr<base::RefCountedMemory> png_bytes) { if (!png_bytes || !png_bytes->size()) { LOG(ERROR) << "Failed to capture image."; @@ -653,7 +680,6 @@ return; } - const base::FilePath path = BuildImagePath(timestamp); blocking_task_runner_->PostTaskAndReplyWithResult( FROM_HERE, base::BindOnce(&SaveFile, png_bytes, path), base::BindOnce(&CaptureModeController::OnImageFileSaved, @@ -677,8 +703,11 @@ CopyImageToClipboard(image); ShowPreviewNotification(path, image, CaptureModeType::kImage); - if (features::IsTemporaryHoldingSpaceEnabled()) - HoldingSpaceController::Get()->client()->AddScreenshot(path); + if (features::IsTemporaryHoldingSpaceEnabled()) { + HoldingSpaceClient* client = HoldingSpaceController::Get()->client(); + if (client) // May be `nullptr` in tests. + client->AddScreenshot(path); + } } void CaptureModeController::OnVideoFileStatus(bool success) { @@ -703,8 +732,9 @@ (base::TimeTicks::Now() - recording_start_time_).InSeconds()); if (features::IsTemporaryHoldingSpaceEnabled()) { - HoldingSpaceController::Get()->client()->AddScreenRecording( - current_video_file_path_); + HoldingSpaceClient* client = HoldingSpaceController::Get()->client(); + if (client) // May be `nullptr` in tests. + client->AddScreenRecording(current_video_file_path_); } } @@ -757,6 +787,7 @@ if (!button_index.has_value()) { // Show the item in the folder. delegate_->ShowScreenCaptureItemInFolder(screen_capture_path); + RecordScreenshotNotificationQuickAction(CaptureQuickAction::kFiles); } else { const int button_index_value = button_index.value(); if (type == CaptureModeType::kVideo) { @@ -768,9 +799,12 @@ switch (button_index_value) { case ScreenshotNotificationButtonIndex::BUTTON_EDIT: delegate_->OpenScreenshotInImageEditor(screen_capture_path); + RecordScreenshotNotificationQuickAction( + CaptureQuickAction::kBacklight); break; case ScreenshotNotificationButtonIndex::BUTTON_DELETE: DeleteFileAsync(blocking_task_runner_, screen_capture_path); + RecordScreenshotNotificationQuickAction(CaptureQuickAction::kDelete); break; default: NOTREACHED(); @@ -787,18 +821,29 @@ kScreenCaptureNotificationId, /*by_user=*/false); } -base::FilePath CaptureModeController::BuildImagePath( - base::Time timestamp) const { - return BuildPath(kScreenshotFileNameFmtStr, timestamp); +base::FilePath CaptureModeController::BuildImagePath() const { + return BuildPathNoExtension(kScreenshotFileNameFmtStr, base::Time::Now()) + .AddExtension("png"); } -base::FilePath CaptureModeController::BuildVideoPath( - base::Time timestamp) const { - return BuildPath(kVideoFileNameFmtStr, timestamp); +base::FilePath CaptureModeController::BuildVideoPath() const { + return BuildPathNoExtension(kVideoFileNameFmtStr, base::Time::Now()) + .AddExtension("webm"); } -base::FilePath CaptureModeController::BuildPath(const char* const format_string, - base::Time timestamp) const { +base::FilePath CaptureModeController::BuildImagePathForDisplay( + int display_index) const { + auto path_str = + BuildPathNoExtension(kScreenshotFileNameFmtStr, base::Time::Now()) + .value(); + auto full_path = base::StringPrintf("%s - Display %d.png", path_str.c_str(), + display_index); + return base::FilePath(full_path); +} + +base::FilePath CaptureModeController::BuildPathNoExtension( + const char* const format_string, + base::Time timestamp) const { const base::FilePath path = delegate_->GetActiveUserDownloadsDir(); base::Time::Exploded exploded_time; timestamp.LocalExplode(&exploded_time); @@ -861,7 +906,7 @@ DCHECK(current_video_file_path_.empty()); recording_start_time_ = base::TimeTicks::Now(); - current_video_file_path_ = BuildVideoPath(base::Time::Now()); + current_video_file_path_ = BuildVideoPath(); video_file_handler_ = VideoFileHandler::Create( blocking_task_runner_, current_video_file_path_, kVideoBufferCapacityBytes, kLowDiskSpaceThresholdInBytes,
diff --git a/ash/capture_mode/capture_mode_controller.h b/ash/capture_mode/capture_mode_controller.h index 84cccffe..80f84c9 100644 --- a/ash/capture_mode/capture_mode_controller.h +++ b/ash/capture_mode/capture_mode_controller.h
@@ -84,6 +84,11 @@ // update |last_capture_region_update_time_|. void SetUserCaptureRegion(const gfx::Rect& region, bool by_user); + // Full screen capture for each available display if no restricted + // content exists on that display, each capture is saved as an individual + // file. Note: this won't start a capture mode session. + void CaptureScreenshotsOfAllDisplays(); + // Called only while a capture session is in progress to perform the actual // capture depending on the current selected |source_| and |type_|, and ends // the capture session. @@ -151,14 +156,15 @@ // the capture session is still active when called, so they can retrieve the // capture parameters they need. They will end the sessions themselves. // They should never be called if IsCaptureAllowed() returns false. - void CaptureImage(const CaptureParams& capture_params); + void CaptureImage(const CaptureParams& capture_params, + const base::FilePath& path); void CaptureVideo(const CaptureParams& capture_params); // Called back when an image has been captured to trigger an attempt to save // the image as a file. |timestamp| is the time at which the capture was - // triggered, and |png_bytes| is the buffer containing the captured image in a + // triggered, |png_bytes| is the buffer containing the captured image in a // PNG format. - void OnImageCaptured(base::Time timestamp, + void OnImageCaptured(const base::FilePath& path, scoped_refptr<base::RefCountedMemory> png_bytes); // Called back when an attempt to save the image file has been completed, with @@ -190,13 +196,17 @@ base::Optional<int> button_index); // Builds a path for a file of an image screenshot, or a video screen - // recording, which were taken at |timestamp|. - base::FilePath BuildImagePath(base::Time timestamp) const; - base::FilePath BuildVideoPath(base::Time timestamp) const; - // Used by the above two functions by providing the corresponding file name - // |format_string| to a capture type (image or video). - base::FilePath BuildPath(const char* const format_string, - base::Time timestamp) const; + // recording, builds with display index if there are + // multiple displays. + base::FilePath BuildImagePath() const; + base::FilePath BuildVideoPath() const; + base::FilePath BuildImagePathForDisplay(int display_index) const; + // Used by the above three functions by providing the corresponding file name + // |format_string| to a capture type (image or video). The returned file path + // excludes the file extension. The above functions are responsible for adding + // it. + base::FilePath BuildPathNoExtension(const char* const format_string, + base::Time timestamp) const; // Records the number of screenshots taken. void RecordAndResetScreenshotsTakenInLastDay();
diff --git a/ash/capture_mode/capture_mode_metrics.cc b/ash/capture_mode/capture_mode_metrics.cc index 25a8f61..ce9cc2c 100644 --- a/ash/capture_mode/capture_mode_metrics.cc +++ b/ash/capture_mode/capture_mode_metrics.cc
@@ -22,6 +22,8 @@ constexpr char kConsecutiveScreenshotHistogramName[] = "Ash.CaptureModeController.ConsecutiveScreenshots"; constexpr char kEntryHistogramName[] = "Ash.CaptureModeController.EntryPoint"; +constexpr char kQuickActionHistogramName[] = + "Ash.CaptureModeController.QuickAction"; constexpr char kRecordTimeHistogramName[] = "Ash.CaptureModeController.ScreenRecordingLength"; constexpr char kScreenshotsPerDayHistogramName[] = @@ -123,4 +125,8 @@ num_screenshots_taken_in_last_week); } +void RecordScreenshotNotificationQuickAction(CaptureQuickAction action) { + base::UmaHistogramEnumeration(kQuickActionHistogramName, action); +} + } // namespace ash
diff --git a/ash/capture_mode/capture_mode_metrics.h b/ash/capture_mode/capture_mode_metrics.h index 950bf5c..462ad32 100644 --- a/ash/capture_mode/capture_mode_metrics.h +++ b/ash/capture_mode/capture_mode_metrics.h
@@ -66,6 +66,16 @@ kMaxValue = kPowerMenu, }; +// Enumeration of quick actions on screenshot notification. Note that these +// values are persisted to histograms so existing values should remain +// unchanged and new values should be added to the end. +enum class CaptureQuickAction { + kBacklight, + kFiles, + kDelete, + kMaxValue = kDelete, +}; + // Records the |reason| for which screen recording was ended. void RecordEndRecordingReason(EndRecordingReason reason); @@ -104,6 +114,9 @@ void RecordNumberOfScreenshotsTakenInLastWeek( int num_screenshots_taken_in_last_week); +// Records the action taken on screen notification. +void RecordScreenshotNotificationQuickAction(CaptureQuickAction action); + } // namespace ash #endif // ASH_CAPTURE_MODE_CAPTURE_MODE_METRICS_H_
diff --git a/ash/capture_mode/capture_mode_unittests.cc b/ash/capture_mode/capture_mode_unittests.cc index 427fda1..ef3337d 100644 --- a/ash/capture_mode/capture_mode_unittests.cc +++ b/ash/capture_mode/capture_mode_unittests.cc
@@ -19,6 +19,7 @@ #include "ash/display/cursor_window_controller.h" #include "ash/display/screen_orientation_controller_test_api.h" #include "ash/display/window_tree_host_manager.h" +#include "ash/home_screen/home_screen_controller.h" #include "ash/magnifier/magnifier_glass.h" #include "ash/public/cpp/ash_features.h" #include "ash/root_window_controller.h" @@ -43,6 +44,10 @@ #include "ui/gfx/geometry/insets.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/vector2d.h" +#include "ui/message_center/message_center.h" +#include "ui/message_center/message_center_observer.h" +#include "ui/message_center/public/cpp/notification.h" +#include "ui/message_center/public/cpp/notification_delegate.h" #include "ui/views/view.h" #include "ui/views/widget/widget_observer.h" @@ -52,6 +57,7 @@ constexpr char kEndRecordingReasonInClamshellHistogramName[] = "Ash.CaptureModeController.EndRecordingReason.ClamshellMode"; +constexpr char kScreenCaptureNotificationId[] = "capture_mode_notification"; // Returns true if the software-composited cursor is enabled. bool IsCursorCompositingEnabled() { @@ -79,6 +85,22 @@ event_generator->ReleaseKey(key_code, flags); } +const message_center::Notification* GetPreviewNotification() { + const message_center::NotificationList::Notifications notifications = + message_center::MessageCenter::Get()->GetVisibleNotifications(); + for (const auto* notification : notifications) { + if (notification->id() == kScreenCaptureNotificationId) + return notification; + } + return nullptr; +} + +void ClickNotification(base::Optional<int> button_index) { + const message_center::Notification* notification = GetPreviewNotification(); + DCHECK(notification); + notification->delegate()->Click(button_index, base::nullopt); +} + // Moves the mouse and updates the cursor's display manually to imitate what a // real mouse move event does in shell. void MoveMouseToAndUpdateCursorDisplay( @@ -324,6 +346,27 @@ base::ScopedObservation<views::Widget, views::WidgetObserver> observer_{this}; }; +class CaptureNotificationWaiter : public message_center::MessageCenterObserver { + public: + CaptureNotificationWaiter() { + message_center::MessageCenter::Get()->AddObserver(this); + } + ~CaptureNotificationWaiter() override { + message_center::MessageCenter::Get()->RemoveObserver(this); + } + + void Wait() { run_loop_.Run(); } + + // message_center::MessageCenterObserver: + void OnNotificationAdded(const std::string& notification_id) override { + if (notification_id == kScreenCaptureNotificationId) + run_loop_.Quit(); + } + + private: + base::RunLoop run_loop_; +}; + TEST_F(CaptureModeTest, StartStop) { auto* controller = CaptureModeController::Get(); controller->Start(CaptureModeEntryType::kQuickSettings); @@ -1883,9 +1926,10 @@ event_generator->PressTouch(); event_generator->ReleaseTouch(); - // There are no windows so the window finder algorithm will find the app list - // window and take a picture of that, ending capture mode. - EXPECT_FALSE(controller->IsActive()); + // There are no windows and home screen window is excluded from window capture + // mode, so capture mode will still remain active. + EXPECT_TRUE(Shell::Get()->home_screen_controller()->IsHomeScreenVisible()); + EXPECT_TRUE(controller->IsActive()); } // Tests that after rotating a display, the capture session widgets are updated @@ -2145,4 +2189,54 @@ EXPECT_EQ(1.f, capture_bar_layer->GetTargetOpacity()); } +// Tests that the quick action histogram is recorded properly. +TEST_F(CaptureModeTest, QuickActionHistograms) { + constexpr char kQuickActionHistogramName[] = + "Ash.CaptureModeController.QuickAction"; + base::HistogramTester histogram_tester; + + auto* controller = StartCaptureSession(CaptureModeSource::kFullscreen, + CaptureModeType::kImage); + EXPECT_TRUE(controller->IsActive()); + { + CaptureNotificationWaiter waiter; + controller->PerformCapture(); + waiter.Wait(); + } + // Verify clicking delete on screenshot notification. + const int delete_button = 1; + ClickNotification(delete_button); + EXPECT_FALSE(GetPreviewNotification()); + histogram_tester.ExpectBucketCount(kQuickActionHistogramName, + CaptureQuickAction::kDelete, 1); + + controller = StartCaptureSession(CaptureModeSource::kFullscreen, + CaptureModeType::kImage); + { + CaptureNotificationWaiter waiter; + controller->PerformCapture(); + waiter.Wait(); + } + // Click on the notification body. This should take us to the files app. + ClickNotification(base::nullopt); + EXPECT_FALSE(GetPreviewNotification()); + histogram_tester.ExpectBucketCount(kQuickActionHistogramName, + CaptureQuickAction::kFiles, 1); + + controller = StartCaptureSession(CaptureModeSource::kFullscreen, + CaptureModeType::kImage); + + { + CaptureNotificationWaiter waiter; + controller->PerformCapture(); + waiter.Wait(); + } + const int edit_button = 0; + // Verify clicking edit on screenshot notification. + ClickNotification(edit_button); + EXPECT_FALSE(GetPreviewNotification()); + histogram_tester.ExpectBucketCount(kQuickActionHistogramName, + CaptureQuickAction::kBacklight, 1); +} + } // namespace ash
diff --git a/ash/capture_mode/capture_window_observer.cc b/ash/capture_mode/capture_window_observer.cc index 5730f37..fe8d9b4 100644 --- a/ash/capture_mode/capture_window_observer.cc +++ b/ash/capture_mode/capture_window_observer.cc
@@ -5,6 +5,8 @@ #include "ash/capture_mode/capture_window_observer.h" #include "ash/capture_mode/capture_mode_session.h" +#include "ash/home_screen/home_screen_controller.h" +#include "ash/home_screen/home_screen_delegate.h" #include "ash/public/cpp/shell_window_ids.h" #include "ash/public/cpp/window_finder.h" #include "ash/shell.h" @@ -40,6 +42,14 @@ window = nullptr; } + // Don't capture home screen window. + if (window && window == Shell::Get() + ->home_screen_controller() + ->delegate() + ->GetHomeScreenWindow()) { + window = nullptr; + } + // Stop observing the current selected window if there is one. StopObserving(); if (window)
diff --git a/ash/events/event_rewriter_controller_impl.cc b/ash/events/event_rewriter_controller_impl.cc index 062dd4c..bc1fcca5b1 100644 --- a/ash/events/event_rewriter_controller_impl.cc +++ b/ash/events/event_rewriter_controller_impl.cc
@@ -60,6 +60,7 @@ std::make_unique<ui::EventRewriterChromeOS>( event_rewriter_delegate, Shell::Get()->sticky_keys_controller(), privacy_screen_supported); + event_rewriter_chromeos_ = event_rewriter_chromeos.get(); std::unique_ptr<AccessibilityEventRewriter> accessibility_event_rewriter = std::make_unique<AccessibilityEventRewriter>( @@ -112,6 +113,12 @@ accessibility_event_rewriter_->set_send_mouse_events(value); } +void EventRewriterControllerImpl::SetAltLeftClickRemappingEnabled( + bool enabled) { + if (event_rewriter_chromeos_) + event_rewriter_chromeos_->set_alt_left_click_remapping_enabled(enabled); +} + void EventRewriterControllerImpl::OnHostInitialized( aura::WindowTreeHost* host) { for (const auto& rewriter : rewriters_)
diff --git a/ash/events/event_rewriter_controller_impl.h b/ash/events/event_rewriter_controller_impl.h index ab0a828..664abf4 100644 --- a/ash/events/event_rewriter_controller_impl.h +++ b/ash/events/event_rewriter_controller_impl.h
@@ -45,6 +45,11 @@ // aura::EnvObserver: void OnHostInitialized(aura::WindowTreeHost* host) override; + // Enable/disable Alt + left click mapping in EventRewriterChromeOS. This + // only applies if the feature + // `chromeos::features::kUseSearchClickForRightClick` is not enabled. + void SetAltLeftClickRemappingEnabled(bool enabled); + private: // The |EventRewriter|s managed by this controller. std::vector<std::unique_ptr<ui::EventRewriter>> rewriters_; @@ -52,6 +57,7 @@ // Owned by |rewriters_|. AccessibilityEventRewriter* accessibility_event_rewriter_ = nullptr; KeyboardDrivenEventRewriter* keyboard_driven_event_rewriter_ = nullptr; + ui::EventRewriterChromeOS* event_rewriter_chromeos_ = nullptr; DISALLOW_COPY_AND_ASSIGN(EventRewriterControllerImpl); };
diff --git a/ash/public/cpp/ash_features.cc b/ash/public/cpp/ash_features.cc index e96cce3..3dbae26 100644 --- a/ash/public/cpp/ash_features.cc +++ b/ash/public/cpp/ash_features.cc
@@ -144,7 +144,7 @@ "NotificationsInContextMenu", base::FEATURE_DISABLED_BY_DEFAULT}; const base::Feature kTemporaryHoldingSpace{"TemporaryHoldingSpace", - base::FEATURE_DISABLED_BY_DEFAULT}; + base::FEATURE_ENABLED_BY_DEFAULT}; const base::Feature kTemporaryHoldingSpacePreviews{ "TemporaryHoldingSpacePreviews", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/ash/public/cpp/update_types.h b/ash/public/cpp/update_types.h index f7508fa..3b91972 100644 --- a/ash/public/cpp/update_types.h +++ b/ash/public/cpp/update_types.h
@@ -27,7 +27,7 @@ // The type of update being applied. Sets the string in the system tray. enum class UpdateType { - // TODO(https://crbug.com/1154427): Add Lacros update type. + kLacros, // Lacros browser, see //docs/lacros.md kSystem, };
diff --git a/ash/system/model/update_model.h b/ash/system/model/update_model.h index 6f381e42..d2ca82f 100644 --- a/ash/system/model/update_model.h +++ b/ash/system/model/update_model.h
@@ -75,7 +75,9 @@ bool rollback_ = false; UpdateType update_type_ = UpdateType::kSystem; NotificationStyle notification_style_ = NotificationStyle::kDefault; + // Custom title for an OS update, usually due to RelaunchNotification policy. base::string16 notification_title_; + // Custom body for an OS update, usually due to RelaunchNotification policy. base::string16 notification_body_; bool update_over_cellular_available_ = false;
diff --git a/ash/system/update/update_notification_controller.cc b/ash/system/update/update_notification_controller.cc index b09e6cb..85855b4 100644 --- a/ash/system/update/update_notification_controller.cc +++ b/ash/system/update/update_notification_controller.cc
@@ -31,6 +31,8 @@ const char kNotifierId[] = "ash.update"; +const char kNotificationId[] = "chrome://update"; + bool CheckForSlowBoot(const base::FilePath& slow_boot_file_path) { if (base::PathExists(slow_boot_file_path)) { return true; @@ -40,9 +42,6 @@ } // namespace -// static -const char UpdateNotificationController::kNotificationId[] = "chrome://update"; - UpdateNotificationController::UpdateNotificationController() : model_(Shell::Get()->system_tray_model()->update_model()), slow_boot_file_path_("/mnt/stateful_partition/etc/slow_boot_required") { @@ -123,6 +122,9 @@ } base::string16 UpdateNotificationController::GetNotificationMessage() const { + if (model_->update_type() == UpdateType::kLacros) + return l10n_util::GetStringUTF16(IDS_UPDATE_NOTIFICATION_MESSAGE_LACROS); + base::string16 system_app_name = l10n_util::GetStringUTF16(IDS_ASH_MESSAGE_CENTER_SYSTEM_APP_NAME); if (model_->rollback()) { @@ -151,6 +153,9 @@ } base::string16 UpdateNotificationController::GetNotificationTitle() const { + if (model_->update_type() == UpdateType::kLacros) + return l10n_util::GetStringUTF16(IDS_UPDATE_NOTIFICATION_TITLE_LACROS); + const base::string16 notification_title = model_->notification_title(); if (!notification_title.empty()) return notification_title;
diff --git a/ash/system/update/update_notification_controller.h b/ash/system/update/update_notification_controller.h index 896869f..e4bfea860 100644 --- a/ash/system/update/update_notification_controller.h +++ b/ash/system/update/update_notification_controller.h
@@ -39,8 +39,6 @@ void GenerateUpdateNotification( base::Optional<bool> slow_boot_file_path_exists); - static const char kNotificationId[]; - UpdateModel* const model_; base::FilePath slow_boot_file_path_;
diff --git a/ash/system/update/update_notification_controller_unittest.cc b/ash/system/update/update_notification_controller_unittest.cc index 8d9ae1a..e7c0925 100644 --- a/ash/system/update/update_notification_controller_unittest.cc +++ b/ash/system/update/update_notification_controller_unittest.cc
@@ -13,9 +13,11 @@ #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" +#include "base/run_loop.h" #include "base/strings/utf_string_conversions.h" #include "build/branding_buildflags.h" #include "ui/message_center/message_center.h" +#include "ui/message_center/message_center_observer.h" #if BUILDFLAG(GOOGLE_CHROME_BRANDING) #define SYSTEM_APP_NAME "Chrome OS" @@ -24,6 +26,33 @@ #endif namespace ash { +namespace { + +const char kNotificationId[] = "chrome://update"; + +// Waits for the notification to be added. Needed because the controller posts a +// task to check for slow boot request before showing the notification. +class AddNotificationWaiter : public message_center::MessageCenterObserver { + public: + AddNotificationWaiter() { + message_center::MessageCenter::Get()->AddObserver(this); + } + ~AddNotificationWaiter() override { + message_center::MessageCenter::Get()->RemoveObserver(this); + } + + void Wait() { run_loop_.Run(); } + + // message_center::MessageCenterObserver: + void OnNotificationAdded(const std::string& notification_id) override { + if (notification_id == kNotificationId) + run_loop_.Quit(); + } + + base::RunLoop run_loop_; +}; + +} // namespace class UpdateNotificationControllerTest : public AshTestBase { public: @@ -33,47 +62,39 @@ protected: bool HasNotification() { return message_center::MessageCenter::Get()->FindVisibleNotificationById( - UpdateNotificationController::kNotificationId); + kNotificationId); } std::string GetNotificationTitle() { - return base::UTF16ToUTF8( - message_center::MessageCenter::Get() - ->FindVisibleNotificationById( - UpdateNotificationController::kNotificationId) - ->title()); + return base::UTF16ToUTF8(message_center::MessageCenter::Get() + ->FindVisibleNotificationById(kNotificationId) + ->title()); } std::string GetNotificationMessage() { - return base::UTF16ToUTF8( - message_center::MessageCenter::Get() - ->FindVisibleNotificationById( - UpdateNotificationController::kNotificationId) - ->message()); + return base::UTF16ToUTF8(message_center::MessageCenter::Get() + ->FindVisibleNotificationById(kNotificationId) + ->message()); } std::string GetNotificationButton(int index) { - return base::UTF16ToUTF8( - message_center::MessageCenter::Get() - ->FindVisibleNotificationById( - UpdateNotificationController::kNotificationId) - ->buttons() - .at(index) - .title); + return base::UTF16ToUTF8(message_center::MessageCenter::Get() + ->FindVisibleNotificationById(kNotificationId) + ->buttons() + .at(index) + .title); } int GetNotificationButtonCount() { return message_center::MessageCenter::Get() - ->FindVisibleNotificationById( - UpdateNotificationController::kNotificationId) + ->FindVisibleNotificationById(kNotificationId) ->buttons() .size(); } int GetNotificationPriority() { return message_center::MessageCenter::Get() - ->FindVisibleNotificationById( - UpdateNotificationController::kNotificationId) + ->FindVisibleNotificationById(kNotificationId) ->priority(); } @@ -85,10 +106,6 @@ ->update_->slow_boot_file_path_ = file_path; } - const char* GetUpdateNotificationId() { - return UpdateNotificationController::kNotificationId; - } - ShutdownConfirmationDialog* GetSlowBootConfirmationDialog() { return Shell::Get() ->system_notification_controller() @@ -158,7 +175,7 @@ // Trigger Click on "Restart to Update" button in Notification. message_center::MessageCenter::Get()->ClickOnNotificationButton( - GetUpdateNotificationId(), 0); + kNotificationId, 0); // Ensure Slow Boot Dialog is open and notification is removed. ASSERT_TRUE(GetSlowBootConfirmationDialog()); @@ -350,4 +367,23 @@ GetNotificationPriority()); } +TEST_F(UpdateNotificationControllerTest, VisibilityAfterLacrosUpdate) { + // The system starts with no update pending, so the notification isn't + // visible. + EXPECT_FALSE(HasNotification()); + + // Simulate an update. + AddNotificationWaiter waiter; + Shell::Get()->system_tray_model()->ShowUpdateIcon(UpdateSeverity::kLow, false, + false, UpdateType::kLacros); + waiter.Wait(); + + // The notification is now visible. + ASSERT_TRUE(HasNotification()); + EXPECT_EQ("Lacros update available", GetNotificationTitle()); + EXPECT_EQ("Device restart is required to apply the update.", + GetNotificationMessage()); + EXPECT_EQ("Restart to update", GetNotificationButton(0)); +} + } // namespace ash
diff --git a/ash/wm/desks/desks_controller.cc b/ash/wm/desks/desks_controller.cc index 4f68cb7..23973bd2 100644 --- a/ash/wm/desks/desks_controller.cc +++ b/ash/wm/desks/desks_controller.cc
@@ -697,10 +697,9 @@ // If in the middle of a window cycle gesture, reset the window cycle list // contents so it contains the new active desk's windows. auto* shell = Shell::Get(); - if (features::IsAltTabLimitedToActiveDesk()) { - auto* window_cycle_controller = shell->window_cycle_controller(); + auto* window_cycle_controller = shell->window_cycle_controller(); + if (window_cycle_controller->IsAltTabPerActiveDesk()) window_cycle_controller->MaybeResetCycleList(); - } for (auto& observer : observers_) observer.OnDeskActivationChanged(active_desk_, old_active);
diff --git a/ash/wm/window_cycle_controller.cc b/ash/wm/window_cycle_controller.cc index e88a2ba3..0a6423f 100644 --- a/ash/wm/window_cycle_controller.cc +++ b/ash/wm/window_cycle_controller.cc
@@ -4,6 +4,7 @@ #include "ash/wm/window_cycle_controller.h" +#include "ash/events/event_rewriter_controller_impl.h" #include "ash/metrics/task_switch_metrics_recorder.h" #include "ash/metrics/task_switch_source.h" #include "ash/metrics/user_metrics_recorder.h" @@ -88,6 +89,8 @@ if (!IsCycling()) StartCycling(); + // TODO(crbug.com/1157100): Handle window highlighting after switching + // alt-tab mode. Step(direction); } @@ -107,6 +110,8 @@ // the window view item for the preview is transparent // (http://crbug.com/895265). Shell::Get()->wallpaper_controller()->MaybeClosePreviewWallpaper(); + Shell::Get()->event_rewriter_controller()->SetAltLeftClickRemappingEnabled( + false); WindowCycleController::WindowList window_list = CreateWindowList(); SaveCurrentActiveDeskAndWindow(window_list); @@ -160,7 +165,7 @@ WindowCycleController::WindowList WindowCycleController::CreateWindowList() { WindowCycleController::WindowList window_list = Shell::Get()->mru_window_tracker()->BuildWindowForCycleWithPipList( - features::IsAltTabLimitedToActiveDesk() ? kActiveDesk : kAllDesks); + IsAltTabPerActiveDesk() ? kActiveDesk : kAllDesks); // Window cycle list windows will handle showing their transient related // windows, so if a window in |window_list| has a transient root also in // |window_list|, we can remove it as the transient root will handle showing @@ -169,6 +174,18 @@ return window_list; } +void WindowCycleController::SetAltTabMode(DesksMruType alt_tab_mode) { + if (alt_tab_mode_ == alt_tab_mode) + return; + alt_tab_mode_ = alt_tab_mode; + MaybeResetCycleList(); +} + +bool WindowCycleController::IsAltTabPerActiveDesk() { + return features::IsBentoEnabled() ? alt_tab_mode_ == kActiveDesk + : features::IsAltTabLimitedToActiveDesk(); +} + void WindowCycleController::SaveCurrentActiveDeskAndWindow( const WindowCycleController::WindowList& window_list) { active_desk_container_id_before_cycle_ = @@ -204,6 +221,8 @@ active_window_before_window_cycle_ = nullptr; active_desk_container_id_before_cycle_ = kShellWindowId_Invalid; + Shell::Get()->event_rewriter_controller()->SetAltLeftClickRemappingEnabled( + true); } } // namespace ash
diff --git a/ash/wm/window_cycle_controller.h b/ash/wm/window_cycle_controller.h index 8ba6efd9..d24fd15a 100644 --- a/ash/wm/window_cycle_controller.h +++ b/ash/wm/window_cycle_controller.h
@@ -9,6 +9,7 @@ #include "ash/ash_export.h" #include "ash/public/cpp/shell_window_ids.h" +#include "ash/wm/mru_window_tracker.h" #include "base/macros.h" #include "base/time/time.h" @@ -86,6 +87,14 @@ return window_cycle_list_.get(); } + // Sets alt-tab mode to all desks (default) or active desk. + void SetAltTabMode(DesksMruType alt_tab_mode_); + + // Checks if alt-tab should be per active desk. If Bento is enabled, alt-tab + // mode depends on users' alt_tab_mode_ selection. Otherwise, it'll default + // to all desk unless LimitAltTabToActiveDesk flag is explicitly enabled. + bool IsAltTabPerActiveDesk(); + private: // Gets a list of windows from the currently open windows, removing windows // with transient roots already in the list. The returned list of windows @@ -115,6 +124,12 @@ // Non-null while actively cycling. std::unique_ptr<WindowCycleEventFilter> event_filter_; + // Tracks alt-tab mode to display all windows from all desks by default. + // An alternative mode is active desk where only windows from the current desk + // are shown in alt-tab. + // TODO(crbug.com/1157105): Store per-desk mode in primary user pref. + DesksMruType alt_tab_mode_ = DesksMruType::kAllDesks; + DISALLOW_COPY_AND_ASSIGN(WindowCycleController); };
diff --git a/ash/wm/window_cycle_controller_unittest.cc b/ash/wm/window_cycle_controller_unittest.cc index bc67c14..ad53fcb 100644 --- a/ash/wm/window_cycle_controller_unittest.cc +++ b/ash/wm/window_cycle_controller_unittest.cc
@@ -124,6 +124,20 @@ ->GetWindowCycleItemViewsForTesting(); } + const views::View::Views& GetWindowCycleTabSliderViews() const { + return Shell::Get() + ->window_cycle_controller() + ->window_cycle_list() + ->GetWindowCycleTabSliderViewsForTesting(); + } + + const views::Label* GetWindowCycleNoRecentItemsLabel() const { + return Shell::Get() + ->window_cycle_controller() + ->window_cycle_list() + ->GetWindowCycleNoRecentItemsLabelForTesting(); + } + const aura::Window* GetTargetWindow() const { return Shell::Get() ->window_cycle_controller() @@ -1337,4 +1351,189 @@ EXPECT_EQ(win0.get(), window_util::GetActiveWindow()); } +class ModeSelectionWindowCycleControllerTest + : public WindowCycleControllerTest { + public: + ModeSelectionWindowCycleControllerTest() = default; + ModeSelectionWindowCycleControllerTest( + const ModeSelectionWindowCycleControllerTest&) = delete; + ModeSelectionWindowCycleControllerTest& operator=( + const ModeSelectionWindowCycleControllerTest&) = delete; + ~ModeSelectionWindowCycleControllerTest() override = default; + + // WindowCycleControllerTest: + void SetUp() override { + scoped_feature_list_.InitAndEnableFeature(features::kBento); + WindowCycleControllerTest::SetUp(); + generator_ = GetEventGenerator(); + } + + void SwitchPerDeskAltTabMode(bool per_desk_mode) { + gfx::Point button_center = + GetWindowCycleTabSliderViews()[per_desk_mode ? 1 : 0] + ->GetBoundsInScreen() + .CenterPoint(); + generator_->MoveMouseTo(button_center); + generator_->ClickLeftButton(); + EXPECT_EQ(per_desk_mode, + Shell::Get()->window_cycle_controller()->IsAltTabPerActiveDesk()); + } + + private: + base::test::ScopedFeatureList scoped_feature_list_; + ui::test::EventGenerator* generator_; +}; + +// Tests that alt-tab shows all windows in an all-desk mode by default and +// shows only windows in the current desk in a current-desk mode. Switching +// between two modes should refresh the window list, while re-entering alt-tab +// should display the most recently selected mode. +TEST_F(ModeSelectionWindowCycleControllerTest, CycleShowsWindowsPerMode) { + WindowCycleController* cycle_controller = + Shell::Get()->window_cycle_controller(); + + // Create two windows for desk1 and three windows for desk2. + auto win0 = CreateAppWindow(gfx::Rect(0, 0, 250, 100)); + auto win1 = CreateAppWindow(gfx::Rect(50, 50, 200, 200)); + auto* desks_controller = DesksController::Get(); + desks_controller->NewDesk(DesksCreationRemovalSource::kButton); + ASSERT_EQ(2u, desks_controller->desks().size()); + const Desk* desk_2 = desks_controller->desks()[1].get(); + ActivateDesk(desk_2); + EXPECT_EQ(desk_2, desks_controller->active_desk()); + auto win2 = CreateAppWindow(gfx::Rect(0, 0, 300, 200)); + auto win3 = CreateAppWindow(gfx::Rect(10, 30, 400, 200)); + auto win4 = CreateAppWindow(gfx::Rect(10, 30, 400, 200)); + + // By default should contain windows from all desks. + auto* generator = GetEventGenerator(); + // Press and hold an alt key to test that alt + left clicking a button works. + generator->PressKey(ui::VKEY_MENU, ui::EF_NONE); + cycle_controller->StartCycling(); + EXPECT_FALSE(cycle_controller->IsAltTabPerActiveDesk()); + auto cycle_windows = GetWindows(cycle_controller); + EXPECT_EQ(5u, cycle_windows.size()); + EXPECT_EQ(cycle_windows.size(), GetWindowCycleItemViews().size()); + EXPECT_TRUE(base::Contains(cycle_windows, win0.get())); + EXPECT_TRUE(base::Contains(cycle_windows, win1.get())); + EXPECT_TRUE(base::Contains(cycle_windows, win2.get())); + EXPECT_TRUE(base::Contains(cycle_windows, win3.get())); + EXPECT_TRUE(base::Contains(cycle_windows, win4.get())); + + // Switching alt-tab to the current-desk mode should show windows in the + // active desk. + SwitchPerDeskAltTabMode(true); + cycle_windows = GetWindows(cycle_controller); + EXPECT_EQ(3u, GetWindowCycleItemViews().size()); + EXPECT_EQ(cycle_windows.size(), GetWindowCycleItemViews().size()); + EXPECT_TRUE(base::Contains(cycle_windows, win2.get())); + EXPECT_TRUE(base::Contains(cycle_windows, win3.get())); + EXPECT_TRUE(base::Contains(cycle_windows, win4.get())); + cycle_controller->CompleteCycling(); + + // Activate desk1 and start alt-tab. + const Desk* desk_1 = desks_controller->desks()[0].get(); + ActivateDesk(desk_1); + cycle_controller->StartCycling(); + // Should start alt-tab with the current-desk mode and show only two windows + // from desk1. + EXPECT_TRUE(cycle_controller->IsAltTabPerActiveDesk()); + cycle_windows = GetWindows(cycle_controller); + EXPECT_EQ(2u, GetWindowCycleItemViews().size()); + EXPECT_EQ(cycle_windows.size(), GetWindowCycleItemViews().size()); + EXPECT_TRUE(base::Contains(cycle_windows, win0.get())); + EXPECT_TRUE(base::Contains(cycle_windows, win1.get())); + + // Switch to the all-desks mode, check and stop alt-tab. + SwitchPerDeskAltTabMode(false); + cycle_windows = GetWindows(cycle_controller); + EXPECT_EQ(5u, cycle_windows.size()); + EXPECT_EQ(cycle_windows.size(), GetWindowCycleItemViews().size()); + cycle_controller->CompleteCycling(); + generator->ReleaseKey(ui::VKEY_MENU, ui::EF_NONE); +} + +// For one window display, tests that alt-tab does not show up if there is only +// one window to be shown, but would continue to show a window in alt-tab if +// switching from the all-desks mode with multiple windows. +TEST_F(ModeSelectionWindowCycleControllerTest, OneWindowInActiveDesk) { + WindowCycleController* cycle_controller = + Shell::Get()->window_cycle_controller(); + + // Create two windows for desk1 and one window for desk2. + auto win0 = CreateAppWindow(gfx::Rect(0, 0, 250, 100)); + auto win1 = CreateAppWindow(gfx::Rect(50, 50, 200, 200)); + auto* desks_controller = DesksController::Get(); + desks_controller->NewDesk(DesksCreationRemovalSource::kButton); + ASSERT_EQ(2u, desks_controller->desks().size()); + const Desk* desk_2 = desks_controller->desks()[1].get(); + ActivateDesk(desk_2); + EXPECT_EQ(desk_2, desks_controller->active_desk()); + auto win2 = CreateAppWindow(gfx::Rect(0, 0, 300, 200)); + + // Starting alt-tab should shows all desks. + cycle_controller->StartCycling(); + auto cycle_windows = GetWindows(cycle_controller); + EXPECT_EQ(3u, GetWindowCycleItemViews().size()); + EXPECT_EQ(cycle_windows.size(), GetWindowCycleItemViews().size()); + + // Switching to an active desk mode should shows a single window in desk2. + SwitchPerDeskAltTabMode(true); + EXPECT_TRUE(cycle_controller->IsCycling()); + cycle_windows = GetWindows(cycle_controller); + EXPECT_EQ(1u, GetWindowCycleItemViews().size()); + EXPECT_EQ(cycle_windows.size(), GetWindowCycleItemViews().size()); + EXPECT_TRUE(base::Contains(cycle_windows, win2.get())); + cycle_controller->CompleteCycling(); + + // Closing alt-tab and trying to re-open again in the current-desk mode + // should not work because there's only one window. + cycle_controller->StartCycling(); + EXPECT_TRUE(cycle_controller->IsCycling()); + EXPECT_FALSE(CycleViewExists()); + cycle_controller->CompleteCycling(); +} + +// Similar to OneWindowInActiveDesk, tests that alt-tab will not work if there +// is no window to be shown. However, if users already open alt-tab with +// multiple windows in the all-desks mode and later switch to the current- +// desk mode, which has no window, it'll display "No recent items". +TEST_F(ModeSelectionWindowCycleControllerTest, NoWindowInActiveDesk) { + WindowCycleController* cycle_controller = + Shell::Get()->window_cycle_controller(); + + // Create two desks with all two windows in desk1. + auto win0 = CreateAppWindow(gfx::Rect(0, 0, 250, 100)); + auto win1 = CreateAppWindow(gfx::Rect(50, 50, 200, 200)); + auto* desks_controller = DesksController::Get(); + desks_controller->NewDesk(DesksCreationRemovalSource::kButton); + ASSERT_EQ(2u, desks_controller->desks().size()); + const Desk* desk_2 = desks_controller->desks()[1].get(); + + // Activate desk2. + ActivateDesk(desk_2); + EXPECT_EQ(desk_2, desks_controller->active_desk()); + + // Starting alt-tab should show all windows from all desks. + cycle_controller->StartCycling(); + auto cycle_windows = GetWindows(cycle_controller); + EXPECT_EQ(2u, GetWindowCycleItemViews().size()); + EXPECT_EQ(cycle_windows.size(), GetWindowCycleItemViews().size()); + EXPECT_FALSE(GetWindowCycleNoRecentItemsLabel()->GetVisible()); + + // Switching to an active-desk mode should not show any mirror window + // and should display "no recent items" label. + SwitchPerDeskAltTabMode(true); + EXPECT_TRUE(cycle_controller->IsCycling()); + cycle_windows = GetWindows(cycle_controller); + EXPECT_EQ(0u, GetWindowCycleItemViews().size()); + EXPECT_EQ(cycle_windows.size(), GetWindowCycleItemViews().size()); + EXPECT_TRUE(GetWindowCycleNoRecentItemsLabel()->GetVisible()); + + // Switching back to an all-desks mode should hide the label. + SwitchPerDeskAltTabMode(false); + EXPECT_FALSE(GetWindowCycleNoRecentItemsLabel()->GetVisible()); + cycle_controller->CompleteCycling(); +} + } // namespace ash
diff --git a/ash/wm/window_cycle_list.cc b/ash/wm/window_cycle_list.cc index 993898c..95c443b5 100644 --- a/ash/wm/window_cycle_list.cc +++ b/ash/wm/window_cycle_list.cc
@@ -17,7 +17,11 @@ #include "ash/public/cpp/window_properties.h" #include "ash/shell.h" #include "ash/shell_delegate.h" +#include "ash/strings/grit/ash_strings.h" #include "ash/style/ash_color_provider.h" +#include "ash/style/default_colors.h" +#include "ash/wm/window_cycle_tab_slider.h" +#include "ash/wm/window_cycle_tab_slider_button.h" #include "ash/wm/window_mini_view.h" #include "ash/wm/window_preview_view.h" #include "ash/wm/window_state.h" @@ -29,12 +33,15 @@ #include "ui/aura/scoped_window_targeter.h" #include "ui/aura/window.h" #include "ui/aura/window_targeter.h" +#include "ui/base/l10n/l10n_util.h" #include "ui/compositor/animation_throughput_reporter.h" #include "ui/compositor/layer_animation_sequence.h" #include "ui/compositor/scoped_layer_animation_settings.h" #include "ui/display/display.h" #include "ui/gfx/geometry/insets.h" #include "ui/views/animation/bounds_animator.h" +#include "ui/views/background.h" +#include "ui/views/controls/button/label_button.h" #include "ui/views/layout/box_layout.h" #include "ui/views/view.h" #include "ui/views/widget/widget.h" @@ -70,9 +77,19 @@ constexpr int kInsideBorderHorizontalPaddingDp = 64; constexpr int kInsideBorderVerticalPaddingDp = 60; +// Padding between the alt-tab bandshield and the tab slider container. +constexpr int kMirrorContainerVerticalPaddingDp = 24; + // Padding between the window previews within the alt-tab bandshield. constexpr int kBetweenChildPaddingDp = 10; +// Padding between the tab slider button and the tab slider container. +constexpr int kTabSliderContainerVerticalPaddingDp = 32; + +// The font size of "No recent items" string when there's no window in the +// window cycle list. +constexpr int kNoRecentItemsLabelFontSizeDp = 14; + // The UMA histogram that logs smoothness of the fade-in animation. constexpr char kShowAnimationSmoothness[] = "Ash.WindowCycleView.AnimationSmoothness.Show"; @@ -262,6 +279,30 @@ layer->SetBackdropFilterQuality(kBackgroundBlurQuality); layer->SetName("WindowCycleView"); + if (features::IsBentoEnabled()) { + tab_slider_container_ = + AddChildView(std::make_unique<WindowCycleTabSlider>()); + + no_recent_items_label_ = AddChildView(std::make_unique<views::Label>( + l10n_util::GetStringUTF16(IDS_ASH_OVERVIEW_NO_RECENT_ITEMS))); + + no_recent_items_label_->SetPaintToLayer(); + no_recent_items_label_->layer()->SetFillsBoundsOpaquely(false); + no_recent_items_label_->SetHorizontalAlignment(gfx::ALIGN_CENTER); + no_recent_items_label_->SetVerticalAlignment(gfx::ALIGN_MIDDLE); + + no_recent_items_label_->SetEnabledColor( + AshColorProvider::Get()->GetContentLayerColor( + AshColorProvider::ContentLayerType::kIconColorSecondary)); + no_recent_items_label_->SetFontList( + no_recent_items_label_->font_list() + .DeriveWithSizeDelta( + kNoRecentItemsLabelFontSizeDp - + no_recent_items_label_->font_list().GetFontSize()) + .DeriveWithWeight(gfx::Font::Weight::NORMAL)); + no_recent_items_label_->SetVisible(false); + } + // |mirror_container_| may be larger than |this|. In this case, it will be // shifted along the x-axis when the user tabs through. It is a container // for the previews and has no rendered content. @@ -271,7 +312,11 @@ views::BoxLayout* layout = mirror_container_->SetLayoutManager(std::make_unique<views::BoxLayout>( views::BoxLayout::Orientation::kHorizontal, - gfx::Insets(kInsideBorderVerticalPaddingDp, + gfx::Insets(features::IsBentoEnabled() + ? kMirrorContainerVerticalPaddingDp + : kInsideBorderVerticalPaddingDp, + kInsideBorderHorizontalPaddingDp, + kInsideBorderVerticalPaddingDp, kInsideBorderHorizontalPaddingDp), kBetweenChildPaddingDp)); layout->set_cross_axis_alignment( @@ -301,6 +346,15 @@ ~WindowCycleView() override = default; void UpdateWindows(const WindowCycleList::WindowList& windows) { + const bool no_windows = windows.empty(); + if (features::IsBentoEnabled()) { + no_recent_items_label_->SetVisible(no_windows); + } + if (no_windows) + return; + + if (features::IsBentoEnabled()) + no_recent_items_label_->SetVisible(false); for (auto* window : windows) { auto* view = mirror_container_->AddChildView( std::make_unique<WindowCycleItemView>(window)); @@ -394,7 +448,12 @@ // views::WidgetDelegateView: gfx::Size CalculatePreferredSize() const override { - return mirror_container_->GetPreferredSize(); + gfx::Size size = mirror_container_->GetPreferredSize(); + if (features::IsBentoEnabled()) { + size.Enlarge(0, tab_slider_container_->GetPreferredSize().height() + + kTabSliderContainerVerticalPaddingDp); + } + return size; } void Layout() override { @@ -436,6 +495,32 @@ } container_bounds.set_x(x_offset); + // Layout a tab slider if Bento is enabled. + if (features::IsBentoEnabled()) { + // Layout the tab slider. + const gfx::Size tab_slider_size = + tab_slider_container_->GetPreferredSize(); + const gfx::Rect tab_slider_container_bounds( + (width() - tab_slider_size.width()) / 2, + kTabSliderContainerVerticalPaddingDp, tab_slider_size.width(), + tab_slider_size.height()); + tab_slider_container_->SetBoundsRect(tab_slider_container_bounds); + + // Move window cycle container down. + container_bounds.set_y(tab_slider_container_->y() + + tab_slider_container_->height()); + + // Unlike the bounds of scrollable mirror container, the bounds of label + // should not overflow out of the screen. + if (no_previews_set_.empty()) { + const gfx::Rect no_recent_item_bounds_( + std::max(0, container_bounds.x()), container_bounds.y(), + std::min(width(), container_bounds.width()), + container_bounds.height()); + no_recent_items_label_->SetBoundsRect(no_recent_item_bounds_); + } + } + // Enable animations only after the first Layout() pass. std::unique_ptr<ui::ScopedLayerAnimationSettings> settings; base::Optional<ui::AnimationThroughputReporter> reporter; @@ -478,6 +563,13 @@ return mirror_container_->children(); } + const views::View::Views& GetTabSliderViewsForTesting() const { + return tab_slider_container_->children(); + } + + const views::Label* GetNoRecentItemsLabelForTesting() const { + return no_recent_items_label_; + } const aura::Window* GetTargetWindowForTesting() const { return target_window_; } @@ -491,6 +583,10 @@ std::map<aura::Window*, WindowCycleItemView*> window_view_map_; views::View* mirror_container_ = nullptr; + // Tab slider and no recent items are only used when Bento is enabled. + views::View* tab_slider_container_ = nullptr; + views::Label* no_recent_items_label_ = nullptr; + // The |target_window_| is the window that has the focus ring. When the user // completes cycling the |target_window_| is activated. aura::Window* target_window_ = nullptr; @@ -572,8 +668,6 @@ } void WindowCycleList::ReplaceWindows(const WindowList& windows) { - if (windows.empty()) - return; RemoveAllWindows(); windows_ = windows; @@ -581,7 +675,7 @@ for (auto* new_window : windows_) new_window->AddObserver(this); - if (ShouldShowUi() && cycle_view_) + if (cycle_view_) cycle_view_->UpdateWindows(windows_); } @@ -819,11 +913,21 @@ const views::View::Views& WindowCycleList::GetWindowCycleItemViewsForTesting() const { - return cycle_view_->GetPreviewViewsForTesting(); + return cycle_view_->GetPreviewViewsForTesting(); // IN-TEST +} + +const views::View::Views& +WindowCycleList::GetWindowCycleTabSliderViewsForTesting() const { + return cycle_view_->GetTabSliderViewsForTesting(); // IN-TEST +} + +const views::Label* +WindowCycleList::GetWindowCycleNoRecentItemsLabelForTesting() const { + return cycle_view_->GetNoRecentItemsLabelForTesting(); // IN-TEST } const aura::Window* WindowCycleList::GetTargetWindowForTesting() const { - return cycle_view_->GetTargetWindowForTesting(); + return cycle_view_->GetTargetWindowForTesting(); // IN-TEST } } // namespace ash
diff --git a/ash/wm/window_cycle_list.h b/ash/wm/window_cycle_list.h index b31fd413..d97e0a2 100644 --- a/ash/wm/window_cycle_list.h +++ b/ash/wm/window_cycle_list.h
@@ -15,6 +15,7 @@ #include "ui/aura/window_observer.h" #include "ui/display/display_observer.h" #include "ui/display/screen.h" +#include "ui/views/controls/label.h" #include "ui/views/view.h" namespace aura { @@ -115,6 +116,12 @@ // Returns the views for the window cycle list. const views::View::Views& GetWindowCycleItemViewsForTesting() const; + // Returns the views for the window cycle tab slider buttons. + const views::View::Views& GetWindowCycleTabSliderViewsForTesting() const; + + // Returns no recent items label. + const views::Label* GetWindowCycleNoRecentItemsLabelForTesting() const; + // Returns the window cycle list's target window. const aura::Window* GetTargetWindowForTesting() const;
diff --git a/ash/wm/window_cycle_tab_slider.cc b/ash/wm/window_cycle_tab_slider.cc new file mode 100644 index 0000000..d6726c9 --- /dev/null +++ b/ash/wm/window_cycle_tab_slider.cc
@@ -0,0 +1,72 @@ +// 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 "ash/wm/window_cycle_tab_slider.h" + +#include "ash/shell.h" +#include "ash/strings/grit/ash_strings.h" +#include "ash/style/ash_color_provider.h" +#include "ash/style/default_colors.h" +#include "ash/wm/mru_window_tracker.h" +#include "ash/wm/window_cycle_controller.h" +#include "base/strings/utf_string_conversions.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/gfx/canvas.h" +#include "ui/views/background.h" +#include "ui/views/layout/box_layout.h" +#include "ui/views/metadata/metadata_impl_macros.h" + +namespace ash { + +WindowCycleTabSlider::WindowCycleTabSlider() + : all_desks_tab_slider_button_( + AddChildView(std::make_unique<WindowCycleTabSliderButton>( + base::BindRepeating(&WindowCycleTabSlider::OnModeChanged, + base::Unretained(this), + false), + l10n_util::GetStringUTF16(IDS_ASH_ALT_TAB_ALL_DESKS_MODE)))), + current_desk_tab_slider_button_( + AddChildView(std::make_unique<WindowCycleTabSliderButton>( + base::BindRepeating(&WindowCycleTabSlider::OnModeChanged, + base::Unretained(this), + true), + l10n_util::GetStringUTF16(IDS_ASH_ALT_TAB_CURRENT_DESK_MODE)))) { + SetPaintToLayer(); + layer()->SetFillsBoundsOpaquely(false); + SetLayoutManager(std::make_unique<views::BoxLayout>( + views::BoxLayout::Orientation::kHorizontal, gfx::Insets(0, 0), 0)); + + // All buttons should have the same width and height. + gfx::Size common_size = all_desks_tab_slider_button_->GetPreferredSize(); + common_size.SetToMax(current_desk_tab_slider_button_->GetPreferredSize()); + all_desks_tab_slider_button_->SetPreferredSize(common_size); + current_desk_tab_slider_button_->SetPreferredSize(common_size); + + const int tab_slider_round_radius = int{common_size.height() / 2}; + SetBackground(views::CreateRoundedRectBackground( + AshColorProvider::Get()->GetControlsLayerColor( + AshColorProvider::ControlsLayerType::kControlBackgroundColorInactive), + tab_slider_round_radius)); + + per_desk_mode_ = + Shell::Get()->window_cycle_controller()->IsAltTabPerActiveDesk(); + + all_desks_tab_slider_button_->SetToggled(!per_desk_mode_); + current_desk_tab_slider_button_->SetToggled(per_desk_mode_); +} + +void WindowCycleTabSlider::OnModeChanged(bool per_desk) { + if (per_desk_mode_ == per_desk) + return; + per_desk_mode_ = per_desk; + all_desks_tab_slider_button_->SetToggled(!per_desk_mode_); + current_desk_tab_slider_button_->SetToggled(per_desk_mode_); + Shell::Get()->window_cycle_controller()->SetAltTabMode( + per_desk_mode_ ? DesksMruType::kActiveDesk : DesksMruType::kAllDesks); +} + +BEGIN_METADATA(WindowCycleTabSlider, views::View) +END_METADATA + +} // namespace ash
diff --git a/ash/wm/window_cycle_tab_slider.h b/ash/wm/window_cycle_tab_slider.h new file mode 100644 index 0000000..17d25e3 --- /dev/null +++ b/ash/wm/window_cycle_tab_slider.h
@@ -0,0 +1,38 @@ +// 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 ASH_WM_WINDOW_CYCLE_TAB_SLIDER_H_ +#define ASH_WM_WINDOW_CYCLE_TAB_SLIDER_H_ + +#include "ash/ash_export.h" +#include "ash/wm/window_cycle_tab_slider_button.h" +#include "ui/views/metadata/metadata_header_macros.h" + +namespace ash { + +// A WindowCycleTabSlider containing two buttons to switch between +// all desks and current desks mode. +class ASH_EXPORT WindowCycleTabSlider : public views::View { + public: + METADATA_HEADER(WindowCycleTabSlider); + + WindowCycleTabSlider(); + WindowCycleTabSlider(const WindowCycleTabSlider&) = delete; + WindowCycleTabSlider& operator=(const WindowCycleTabSlider&) = delete; + ~WindowCycleTabSlider() override = default; + + void OnModeChanged(bool per_desk); + + // TODO(crbug.com/1157087): Add tab slider animation. + + private: + bool per_desk_mode_ = false; + + WindowCycleTabSliderButton* all_desks_tab_slider_button_; + WindowCycleTabSliderButton* current_desk_tab_slider_button_; +}; + +} // namespace ash + +#endif // ASH_WM_WINDOW_CYCLE_TAB_SLIDER_H_
diff --git a/ash/wm/window_cycle_tab_slider_button.cc b/ash/wm/window_cycle_tab_slider_button.cc new file mode 100644 index 0000000..887f7c9 --- /dev/null +++ b/ash/wm/window_cycle_tab_slider_button.cc
@@ -0,0 +1,84 @@ +// 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 "ash/wm/window_cycle_tab_slider_button.h" + +#include "ash/style/ash_color_provider.h" +#include "base/strings/utf_string_conversions.h" +#include "ui/gfx/canvas.h" +#include "ui/views/background.h" +#include "ui/views/controls/label.h" +#include "ui/views/layout/box_layout.h" +#include "ui/views/metadata/metadata_impl_macros.h" + +namespace ash { + +namespace { + +// The height of the tab slider button. +constexpr int kTabSliderButtonHeight = 32; + +// The round radius of the slider, which is half of its diameter (height). +constexpr int kTabSliderRoundRadius = int{kTabSliderButtonHeight / 2}; + +// The horizontal insets between the label and the button. +constexpr int kTabSliderButtonHorizontalInsets = 20; + +// The font size of the button label. +constexpr int kTabSliderButtonLabelFontSizeDp = 13; + +} // namespace + +WindowCycleTabSliderButton::WindowCycleTabSliderButton( + views::Button::PressedCallback callback, + const base::string16& label_text) + : LabelButton(std::move(callback), label_text) { + SetHorizontalAlignment(gfx::ALIGN_CENTER); + + label()->SetFontList( + label() + ->font_list() + .DeriveWithSizeDelta(kTabSliderButtonLabelFontSizeDp - + label()->font_list().GetFontSize()) + .DeriveWithWeight(gfx::Font::Weight::MEDIUM)); + + SetEnabledTextColors(AshColorProvider::Get()->GetContentLayerColor( + AshColorProvider::ContentLayerType::kTextColorPrimary)); + + SetBorder(views::CreateEmptyBorder(gfx::Insets())); +} + +void WindowCycleTabSliderButton::SetToggled(bool is_toggled) { + if (is_toggled == toggled_) + return; + toggled_ = is_toggled; + SetEnabledTextColors(AshColorProvider::Get()->GetContentLayerColor( + toggled_ ? AshColorProvider::ContentLayerType::kButtonLabelColorPrimary + : AshColorProvider::ContentLayerType::kTextColorPrimary)); + // SchedulePaint triggers OnPaintBackground + SchedulePaint(); +} + +void WindowCycleTabSliderButton::OnPaintBackground(gfx::Canvas* canvas) { + if (!toggled_) + return; + + cc::PaintFlags flags; + flags.setAntiAlias(true); + flags.setStyle(cc::PaintFlags::kFill_Style); + flags.setColor(AshColorProvider::Get()->GetControlsLayerColor( + AshColorProvider::ControlsLayerType::kControlBackgroundColorActive)); + canvas->DrawRoundRect(GetContentsBounds(), kTabSliderRoundRadius, flags); +} + +gfx::Size WindowCycleTabSliderButton::CalculatePreferredSize() const { + return gfx::Size(label()->GetPreferredSize().width() + + 2 * kTabSliderButtonHorizontalInsets, + kTabSliderButtonHeight); +} + +BEGIN_METADATA(WindowCycleTabSliderButton, views::LabelButton) +END_METADATA + +} // namespace ash
diff --git a/ash/wm/window_cycle_tab_slider_button.h b/ash/wm/window_cycle_tab_slider_button.h new file mode 100644 index 0000000..b94f607 --- /dev/null +++ b/ash/wm/window_cycle_tab_slider_button.h
@@ -0,0 +1,39 @@ +// 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 ASH_WM_WINDOW_CYCLE_TAB_SLIDER_BUTTON_H_ +#define ASH_WM_WINDOW_CYCLE_TAB_SLIDER_BUTTON_H_ + +#include "ash/ash_export.h" +#include "ui/views/controls/button/label_button.h" +#include "ui/views/metadata/metadata_header_macros.h" + +namespace ash { + +// A button used in the WindowCycleTabSlider to choose between +// all desks and current desks mode. +class ASH_EXPORT WindowCycleTabSliderButton : public views::LabelButton { + public: + METADATA_HEADER(WindowCycleTabSliderButton); + + WindowCycleTabSliderButton(views::Button::PressedCallback callback, + const base::string16& label); + WindowCycleTabSliderButton(const WindowCycleTabSliderButton&) = delete; + WindowCycleTabSliderButton& operator=(const WindowCycleTabSliderButton&) = + delete; + ~WindowCycleTabSliderButton() override = default; + + // views::View: + void OnPaintBackground(gfx::Canvas* canvas) override; + gfx::Size CalculatePreferredSize() const override; + + void SetToggled(bool is_toggled); + + private: + bool toggled_ = false; +}; + +} // namespace ash + +#endif // ASH_WM_WINDOW_CYCLE_TAB_SLIDER_BUTTON_H_
diff --git a/base/fuchsia/startup_context.cc b/base/fuchsia/startup_context.cc index 7d1e0c2..3655845c 100644 --- a/base/fuchsia/startup_context.cc +++ b/base/fuchsia/startup_context.cc
@@ -13,7 +13,6 @@ #include "base/macros.h" namespace base { -namespace fuchsia { StartupContext::StartupContext(::fuchsia::sys::StartupInfo startup_info) { std::unique_ptr<sys::ServiceDirectory> incoming_services; @@ -98,5 +97,4 @@ component_context_->outgoing()->Serve(std::move(outgoing_directory_request_)); } -} // namespace fuchsia } // namespace base
diff --git a/base/fuchsia/startup_context.h b/base/fuchsia/startup_context.h index a2d1f5c..eeffd4f 100644 --- a/base/fuchsia/startup_context.h +++ b/base/fuchsia/startup_context.h
@@ -19,7 +19,6 @@ } // namespace sys namespace base { -namespace fuchsia { // Helper for unpacking a fuchsia.sys.StartupInfo and creating convenience // wrappers for the various fields (e.g. the incoming & outgoing service @@ -68,7 +67,6 @@ zx::channel outgoing_directory_request_; }; -} // namespace fuchsia } // namespace base #endif // BASE_FUCHSIA_STARTUP_CONTEXT_H_
diff --git a/base/win/windows_types.h b/base/win/windows_types.h index d1f3e8d..17a20aad 100644 --- a/base/win/windows_types.h +++ b/base/win/windows_types.h
@@ -236,6 +236,9 @@ WINBASEAPI VOID WINAPI SetLastError(_In_ DWORD dwErrCode); +WINBASEAPI BOOL WINAPI TerminateProcess(_In_ HANDLE hProcess, + _In_ UINT uExitCode); + #ifdef __cplusplus } #endif
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1 index a0251159..f075674e 100644 --- a/build/fuchsia/linux.sdk.sha1 +++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@ -0.20201221.0.1 +0.20201221.2.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1 index a0251159..f075674e 100644 --- a/build/fuchsia/mac.sdk.sha1 +++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@ -0.20201221.0.1 +0.20201221.2.1
diff --git a/chrome/VERSION b/chrome/VERSION index 95be3f3..5b0aabc 100644 --- a/chrome/VERSION +++ b/chrome/VERSION
@@ -1,4 +1,4 @@ MAJOR=89 MINOR=0 -BUILD=4363 +BUILD=4364 PATCH=0
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java index 736b2c4..8166ec2 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchManager.java
@@ -1057,7 +1057,7 @@ } if (mSearchRequest != null && (!mDidStartLoadingResolvedSearchRequest || mShouldLoadDelayedSearch)) { - // mShouldLoadDelayedSearch is used in the long-press case to load content. + // mShouldLoadDelayedSearch is used in the non-preloading case to load content. // Since content is now created and destroyed for each request, was impossible // to know if content was already loaded or recently needed to be; this is for // the case where it needed to be.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchPolicy.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchPolicy.java index cb33f42..07d93a9 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchPolicy.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchPolicy.java
@@ -142,10 +142,7 @@ // We never preload unless we have sent page context (done through a Resolve request). // Only some gestures can resolve, and only when resolve privacy rules are met. - return (mSelectionController.getSelectionType() == SelectionType.TAP - || mSelectionController.getSelectionType() - == SelectionType.RESOLVING_LONG_PRESS) - && shouldPreviousGestureResolve(); + return isResolvingGesture() && shouldPreviousGestureResolve(); } /**
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java index 668e76e..732f1b04 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientTest.java
@@ -8,7 +8,6 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -23,7 +22,7 @@ import androidx.browser.trusted.TrustedWebActivityServiceConnection; import androidx.browser.trusted.TrustedWebActivityServiceConnectionPool; -import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.SettableFuture; import org.junit.Before; import org.junit.Test; @@ -31,7 +30,6 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; -import org.mockito.stubbing.Answer; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; @@ -55,9 +53,12 @@ private static final int SERVICE_SMALL_ICON_ID = 1; private static final String CLIENT_PACKAGE_NAME = "com.example.app"; - @Mock private TrustedWebActivityServiceConnectionPool mConnectionPool; + private SettableFuture<TrustedWebActivityServiceConnection> mServiceFuture = + SettableFuture.create(); + + @Mock + private TrustedWebActivityServiceConnectionPool mConnectionPool; @Mock private TrustedWebActivityServiceConnection mService; - @Mock private ListenableFuture<TrustedWebActivityServiceConnection> mServiceFuture; @Mock private NotificationBuilderBase mNotificationBuilder; @Mock private TrustedWebActivityUmaRecorder mRecorder; @Mock private NotificationUmaTracker mNotificationUmaTracker; @@ -73,12 +74,7 @@ public void setUp() throws ExecutionException, InterruptedException, RemoteException { MockitoAnnotations.initMocks(this); - when(mServiceFuture.get()).thenReturn(mService); - doAnswer((Answer<Void>) invocation -> { - Runnable runnable = invocation.getArgument(0); - runnable.run(); - return null; - }).when(mServiceFuture).addListener(any(), any()); + mServiceFuture.set(mService); when(mConnectionPool.connect(any(), any(), any())).thenReturn(mServiceFuture); when(mService.getSmallIconId()).thenReturn(SERVICE_SMALL_ICON_ID);
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index 5777322..d139e1b 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -4191,6 +4191,7 @@ "//components/web_modal", "//components/zoom", "//courgette:courgette_lib", + "//services/device/public/cpp/hid", "//third_party/sqlite", ] if (is_chromeos_ash) { @@ -4424,8 +4425,6 @@ "nearby_sharing/share_target_info.h", "nearby_sharing/sharesheet/nearby_share_action.cc", "nearby_sharing/sharesheet/nearby_share_action.h", - "nearby_sharing/sharesheet/nearby_share_web_view.cc", - "nearby_sharing/sharesheet/nearby_share_web_view.h", "nearby_sharing/transfer_metadata.cc", "nearby_sharing/transfer_metadata.h", "nearby_sharing/transfer_metadata_builder.cc",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 6c85d11..f5d1ab7 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -2834,6 +2834,9 @@ {"bluetooth-wbs-dogfood", flag_descriptions::kBluetoothWbsDogfoodName, flag_descriptions::kBluetoothWbsDogfoodDescription, kOsCrOS, FEATURE_VALUE_TYPE(chromeos::features::kBluetoothWbsDogfood)}, + {"cellular-use-attach-apn", flag_descriptions::kCellularUseAttachApnName, + flag_descriptions::kCellularUseAttachApnDescription, kOsCrOS, + FEATURE_VALUE_TYPE(chromeos::features::kCellularUseAttachApn)}, {"cryptauth-v2-device-activity-status", flag_descriptions::kCryptAuthV2DeviceActivityStatusName, flag_descriptions::kCryptAuthV2DeviceActivityStatusDescription, kOsCrOS,
diff --git a/chrome/browser/browser_commands_unittest.cc b/chrome/browser/browser_commands_unittest.cc index fdc962a..139e2d7 100644 --- a/chrome/browser/browser_commands_unittest.cc +++ b/chrome/browser/browser_commands_unittest.cc
@@ -503,18 +503,34 @@ } } -TEST_F(BrowserCommandsTest, TabSearchDisabled) { - EXPECT_FALSE(chrome::IsCommandEnabled(browser(), IDC_TAB_SEARCH)); - EXPECT_FALSE(chrome::IsCommandEnabled(browser(), IDC_TAB_SEARCH_CLOSE)); +class TabSearchCommandTest : public BrowserCommandsTest, + public ::testing::WithParamInterface<bool> { + public: + void SetUp() override { + if (GetParam()) { + scoped_feature_list_.InitWithFeatures({features::kTabSearch}, {}); + } else { + scoped_feature_list_.InitWithFeatures({}, {features::kTabSearch}); + } + BrowserCommandsTest::SetUp(); + } + + private: + base::test::ScopedFeatureList scoped_feature_list_; +}; + +TEST_P(TabSearchCommandTest, TabSearchCommandStatus) { + if (base::FeatureList::IsEnabled(features::kTabSearch)) { + EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_TAB_SEARCH)); + EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_TAB_SEARCH_CLOSE)); + } else { + EXPECT_FALSE(chrome::IsCommandEnabled(browser(), IDC_TAB_SEARCH)); + EXPECT_FALSE(chrome::IsCommandEnabled(browser(), IDC_TAB_SEARCH_CLOSE)); + } } -TEST_F(BrowserCommandsTest, TabSearchEnabled) { - base::test::ScopedFeatureList feature_list; - feature_list.InitAndEnableFeature(features::kTabSearch); - auto browser = - CreateBrowser(profile(), Browser::TYPE_NORMAL, false, window()); - EXPECT_TRUE(chrome::IsCommandEnabled(browser.get(), IDC_TAB_SEARCH)); - EXPECT_TRUE(chrome::IsCommandEnabled(browser.get(), IDC_TAB_SEARCH_CLOSE)); -} +INSTANTIATE_TEST_SUITE_P(All, + TabSearchCommandTest, + ::testing::Values(true, false)); } // namespace
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn index c744f28..aa521b5 100644 --- a/chrome/browser/chromeos/BUILD.gn +++ b/chrome/browser/chromeos/BUILD.gn
@@ -3489,6 +3489,7 @@ "chrome_content_browser_client_chromeos_part_unittest.cc", "concierge_helper_service_unittest.cc", "crosapi/account_manager_ash_unittest.cc", + "crosapi/browser_loader_unittest.cc", "crosapi/browser_util_unittest.cc", "crosapi/message_center_ash_unittest.cc", "crosapi/metrics_reporting_ash_unittest.cc",
diff --git a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker.cc b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker.cc index ed81e120..485aca8 100644 --- a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker.cc +++ b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker.cc
@@ -70,6 +70,10 @@ *output_algo = chromeos::platform_keys::HashAlgorithm::HASH_ALGORITHM_SHA256; return true; + case em::HashingAlgorithm::NO_HASH: + *output_algo = + chromeos::platform_keys::HashAlgorithm::HASH_ALGORITHM_NONE; + return true; case em::HashingAlgorithm::HASHING_ALGORITHM_UNSPECIFIED: return false; } @@ -566,6 +570,15 @@ return; } + if (hashing_algorithm_ == + chromeos::platform_keys::HashAlgorithm::HASH_ALGORITHM_NONE) { + platform_keys_service_->SignRSAPKCS1Raw( + GetPlatformKeysTokenId(cert_scope_), csr_, public_key_, + base::BindRepeating(&CertProvisioningWorkerImpl::OnSignCsrDone, + weak_factory_.GetWeakPtr(), + base::TimeTicks::Now())); + return; + } platform_keys_service_->SignRSAPKCS1Digest( GetPlatformKeysTokenId(cert_scope_), csr_, public_key_, hashing_algorithm_.value(),
diff --git a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker_unittest.cc b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker_unittest.cc index 14202f6..24bb88a 100644 --- a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker_unittest.cc +++ b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker_unittest.cc
@@ -99,9 +99,6 @@ constexpr char kCertScopeStrDevice[] = "google/chromeos/device"; constexpr char kInvalidationTopic[] = "fake_invalidation_topic_1"; constexpr char kDataToSign[] = "fake_data_to_sign_1"; -constexpr em::HashingAlgorithm kProtoHashAlgo = em::HashingAlgorithm::SHA256; -constexpr platform_keys::HashAlgorithm kPkHashAlgo = - platform_keys::HashAlgorithm::HASH_ALGORITHM_SHA256; constexpr char kChallenge[] = "fake_va_challenge_1"; constexpr char kChallengeResponse[] = "fake_va_challenge_response_1"; constexpr char kSignature[] = "fake_signature_1"; @@ -162,7 +159,7 @@ .WillOnce(RunOnceCallback<0>(register_key_result)); \ } -#define EXPECT_START_CSR_OK(START_CSR_FUNC) \ +#define EXPECT_START_CSR_OK(START_CSR_FUNC, HASHING_ALGO) \ { \ EXPECT_CALL(cloud_policy_client_, START_CSR_FUNC) \ .Times(1) \ @@ -170,10 +167,10 @@ policy::DeviceManagementStatus::DM_STATUS_SUCCESS, \ /*response_error=*/base::nullopt, \ /*try_again_later_ms=*/base::nullopt, kInvalidationTopic, \ - kChallenge, kProtoHashAlgo, kDataToSign)); \ + kChallenge, HASHING_ALGO, kDataToSign)); \ } -#define EXPECT_START_CSR_OK_WITHOUT_VA(START_CSR_FUNC) \ +#define EXPECT_START_CSR_OK_WITHOUT_VA(START_CSR_FUNC, HASHING_ALGO) \ { \ EXPECT_CALL(cloud_policy_client_, START_CSR_FUNC) \ .Times(1) \ @@ -181,7 +178,7 @@ policy::DeviceManagementStatus::DM_STATUS_SUCCESS, \ /*response_error=*/base::nullopt, \ /*try_again_later_ms=*/base::nullopt, kInvalidationTopic, \ - /*va_challenge=*/"", kProtoHashAlgo, kDataToSign)); \ + /*va_challenge=*/"", HASHING_ALGO, kDataToSign)); \ } #define EXPECT_START_CSR_TRY_LATER(START_CSR_FUNC, DELAY_MS) \ @@ -318,6 +315,14 @@ RunOnceCallback<4>(kSignature, platform_keys::Status::kSuccess)); \ } +#define EXPECT_SIGN_RSAPKC1_RAW_OK(SIGN_FUNC) \ + { \ + EXPECT_CALL(*platform_keys_service_, SIGN_FUNC) \ + .Times(1) \ + .WillOnce( \ + RunOnceCallback<3>(kSignature, platform_keys::Status::kSuccess)); \ + } + #define EXPECT_SIGN_RSAPKC1_DIGEST_FAIL(SIGN_FUNC) \ { \ EXPECT_CALL(*platform_keys_service_, SIGN_FUNC) \ @@ -492,9 +497,11 @@ /*callback=*/_)); EXPECT_CALL(state_change_callback_observer_, StateChangeCallback()); - EXPECT_START_CSR_OK(ClientCertProvisioningStartCsr( - kCertScopeStrUser, kCertProfileId, kCertProfileVersion, GetPublicKey(), - /*callback=*/_)); + EXPECT_START_CSR_OK( + ClientCertProvisioningStartCsr(kCertScopeStrUser, kCertProfileId, + kCertProfileVersion, GetPublicKey(), + /*callback=*/_), + em::HashingAlgorithm::SHA256); EXPECT_CALL(state_change_callback_observer_, StateChangeCallback()); EXPECT_CALL(*mock_invalidator, Register(kInvalidationTopic, _)).Times(1); @@ -520,7 +527,8 @@ EXPECT_SIGN_RSAPKC1_DIGEST_OK(SignRSAPKCS1Digest( ::testing::Optional(platform_keys::TokenId::kUser), kDataToSign, - GetPublicKey(), kPkHashAlgo, /*callback=*/_)); + GetPublicKey(), platform_keys::HashAlgorithm::HASH_ALGORITHM_SHA256, + /*callback=*/_)); EXPECT_CALL(state_change_callback_observer_, StateChangeCallback()); EXPECT_FINISH_CSR_OK(ClientCertProvisioningFinishCsr( @@ -582,9 +590,11 @@ .WillOnce(RunOnceCallback<2>(GetPublicKey(), platform_keys::Status::kSuccess)); - EXPECT_START_CSR_OK_WITHOUT_VA(ClientCertProvisioningStartCsr( - kCertScopeStrUser, kCertProfileId, kCertProfileVersion, GetPublicKey(), - /*callback=*/_)); + EXPECT_START_CSR_OK_WITHOUT_VA( + ClientCertProvisioningStartCsr(kCertScopeStrUser, kCertProfileId, + kCertProfileVersion, GetPublicKey(), + /*callback=*/_), + em::HashingAlgorithm::SHA256); EXPECT_CALL( *key_permissions_manager_, @@ -598,7 +608,8 @@ EXPECT_SIGN_RSAPKC1_DIGEST_OK(SignRSAPKCS1Digest( ::testing::Optional(platform_keys::TokenId::kUser), kDataToSign, - GetPublicKey(), kPkHashAlgo, /*callback=*/_)); + GetPublicKey(), platform_keys::HashAlgorithm::HASH_ALGORITHM_SHA256, + /*callback=*/_)); EXPECT_FINISH_CSR_OK(ClientCertProvisioningFinishCsr( kCertScopeStrUser, kCertProfileId, kCertProfileVersion, GetPublicKey(), @@ -619,6 +630,88 @@ worker.DoStep(); } +// Checks that the worker correctly forwards a request with +// hashing_algorithm=NO_HASH to platform_keys. +TEST_F(CertProvisioningWorkerTest, NoHashInStartCsr) { + CertProfile cert_profile(kCertProfileId, kCertProfileName, + kCertProfileVersion, + /*is_va_enabled=*/true, kCertProfileRenewalPeriod); + + MockTpmChallengeKeySubtle* mock_tpm_challenge_key = PrepareTpmChallengeKey(); + MockCertProvisioningInvalidator* mock_invalidator = nullptr; + CertProvisioningWorkerImpl worker( + CertScope::kUser, GetProfile(), &testing_pref_service_, cert_profile, + &cloud_policy_client_, MakeInvalidator(&mock_invalidator), + GetStateChangeCallback(), GetResultCallback()); + + { + testing::InSequence seq; + + EXPECT_PREPARE_KEY_OK( + *mock_tpm_challenge_key, + StartPrepareKeyStep(attestation::AttestationKeyType::KEY_USER, + /*will_register_key=*/true, + GetKeyName(kCertProfileId), + /*profile=*/_, + /*callback=*/_)); + EXPECT_CALL(state_change_callback_observer_, StateChangeCallback()); + + EXPECT_START_CSR_OK( + ClientCertProvisioningStartCsr(kCertScopeStrUser, kCertProfileId, + kCertProfileVersion, GetPublicKey(), + /*callback=*/_), + em::HashingAlgorithm::NO_HASH); + EXPECT_CALL(state_change_callback_observer_, StateChangeCallback()); + + EXPECT_CALL(*mock_invalidator, Register(kInvalidationTopic, _)).Times(1); + + EXPECT_SIGN_CHALLENGE_OK(*mock_tpm_challenge_key, + StartSignChallengeStep(kChallenge, + /*callback=*/_)); + EXPECT_CALL(state_change_callback_observer_, StateChangeCallback()); + + EXPECT_REGISTER_KEY_OK(*mock_tpm_challenge_key, StartRegisterKeyStep); + EXPECT_CALL(state_change_callback_observer_, StateChangeCallback()); + + EXPECT_CALL( + *key_permissions_manager_, + AllowKeyForUsage(/*callback=*/_, platform_keys::KeyUsage::kCorporate, + GetPublicKey())); + + EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(SetAttributeForKey( + platform_keys::TokenId::kUser, GetPublicKey(), + platform_keys::KeyAttributeType::kCertificateProvisioningId, + kCertProfileId, _)); + EXPECT_CALL(state_change_callback_observer_, StateChangeCallback()); + + EXPECT_SIGN_RSAPKC1_RAW_OK( + SignRSAPKCS1Raw(::testing::Optional(platform_keys::TokenId::kUser), + kDataToSign, GetPublicKey(), /*callback=*/_)); + EXPECT_CALL(state_change_callback_observer_, StateChangeCallback()); + + EXPECT_FINISH_CSR_OK(ClientCertProvisioningFinishCsr( + kCertScopeStrUser, kCertProfileId, kCertProfileVersion, GetPublicKey(), + kChallengeResponse, kSignature, /*callback=*/_)); + EXPECT_CALL(state_change_callback_observer_, StateChangeCallback()); + + EXPECT_DOWNLOAD_CERT_OK(ClientCertProvisioningDownloadCert( + kCertScopeStrUser, kCertProfileId, kCertProfileVersion, GetPublicKey(), + /*callback=*/_)); + + EXPECT_IMPORT_CERTIFICATE_OK(ImportCertificate( + platform_keys::TokenId::kUser, /*certificate=*/_, /*callback=*/_)); + EXPECT_CALL(state_change_callback_observer_, StateChangeCallback()); + + EXPECT_CALL(*mock_invalidator, Unregister()).Times(1); + + EXPECT_CALL(callback_observer_, + Callback(cert_profile, CertProvisioningWorkerState::kSucceeded)) + .Times(1); + } + + worker.DoStep(); +} + // Checks that when the server returns try_again_later field, the worker will // retry a request when it asked to continue the provisioning. TEST_F(CertProvisioningWorkerTest, TryLaterManualRetry) { @@ -663,7 +756,8 @@ EXPECT_START_CSR_OK( ClientCertProvisioningStartCsr(kCertScopeStrDevice, kCertProfileId, kCertProfileVersion, GetPublicKey(), - /*callback=*/_)); + /*callback=*/_), + em::HashingAlgorithm::SHA256); EXPECT_SIGN_CHALLENGE_OK(*mock_tpm_challenge_key, StartSignChallengeStep(kChallenge, @@ -777,9 +871,11 @@ { testing::InSequence seq; - EXPECT_START_CSR_OK(ClientCertProvisioningStartCsr( - kCertScopeStrUser, kCertProfileId, kCertProfileVersion, GetPublicKey(), - /*callback=*/_)); + EXPECT_START_CSR_OK( + ClientCertProvisioningStartCsr(kCertScopeStrUser, kCertProfileId, + kCertProfileVersion, GetPublicKey(), + /*callback=*/_), + em::HashingAlgorithm::SHA256); EXPECT_SIGN_CHALLENGE_OK(*mock_tpm_challenge_key, StartSignChallengeStep(kChallenge, @@ -799,7 +895,8 @@ EXPECT_SIGN_RSAPKC1_DIGEST_OK(SignRSAPKCS1Digest( ::testing::Optional(platform_keys::TokenId::kUser), kDataToSign, - GetPublicKey(), kPkHashAlgo, /*callback=*/_)); + GetPublicKey(), platform_keys::HashAlgorithm::HASH_ALGORITHM_SHA256, + /*callback=*/_)); EXPECT_FINISH_CSR_TRY_LATER( ClientCertProvisioningFinishCsr( @@ -898,9 +995,11 @@ { testing::InSequence seq; - EXPECT_START_CSR_OK(ClientCertProvisioningStartCsr( - kCertScopeStrUser, kCertProfileId, kCertProfileVersion, GetPublicKey(), - /*callback=*/_)); + EXPECT_START_CSR_OK( + ClientCertProvisioningStartCsr(kCertScopeStrUser, kCertProfileId, + kCertProfileVersion, GetPublicKey(), + /*callback=*/_), + em::HashingAlgorithm::SHA256); EXPECT_CALL(*mock_invalidator, Register(kInvalidationTopic, _)) .WillOnce(SaveArg<1>(&on_invalidation_callback)); @@ -922,7 +1021,8 @@ EXPECT_SIGN_RSAPKC1_DIGEST_OK(SignRSAPKCS1Digest( ::testing::Optional(platform_keys::TokenId::kUser), kDataToSign, - GetPublicKey(), kPkHashAlgo, /*callback=*/_)); + GetPublicKey(), platform_keys::HashAlgorithm::HASH_ALGORITHM_SHA256, + /*callback=*/_)); EXPECT_FINISH_CSR_TRY_LATER( ClientCertProvisioningFinishCsr( @@ -1198,9 +1298,11 @@ /*profile=*/_, /*callback=*/_)); - EXPECT_START_CSR_OK(ClientCertProvisioningStartCsr( - kCertScopeStrUser, kCertProfileId, kCertProfileVersion, GetPublicKey(), - /*callback=*/_)); + EXPECT_START_CSR_OK( + ClientCertProvisioningStartCsr(kCertScopeStrUser, kCertProfileId, + kCertProfileVersion, GetPublicKey(), + /*callback=*/_), + em::HashingAlgorithm::SHA256); EXPECT_CALL(*mock_invalidator, Register(kInvalidationTopic, _)).Times(1); @@ -1357,9 +1459,11 @@ { testing::InSequence seq; - EXPECT_START_CSR_OK(ClientCertProvisioningStartCsr( - kCertScopeStrUser, kCertProfileId, kCertProfileVersion, GetPublicKey(), - /*callback=*/_)); + EXPECT_START_CSR_OK( + ClientCertProvisioningStartCsr(kCertScopeStrUser, kCertProfileId, + kCertProfileVersion, GetPublicKey(), + /*callback=*/_), + em::HashingAlgorithm::SHA256); pref_val = ParseJson("{}"); EXPECT_CALL(pref_observer, OnPrefValueUpdated(IsJson(pref_val))).Times(1); @@ -1384,7 +1488,8 @@ EXPECT_SIGN_RSAPKC1_DIGEST_OK(SignRSAPKCS1Digest( ::testing::Optional(platform_keys::TokenId::kUser), kDataToSign, - GetPublicKey(), kPkHashAlgo, /*callback=*/_)); + GetPublicKey(), platform_keys::HashAlgorithm::HASH_ALGORITHM_SHA256, + /*callback=*/_)); EXPECT_FINISH_CSR_OK(ClientCertProvisioningFinishCsr( kCertScopeStrUser, kCertProfileId, kCertProfileVersion, GetPublicKey(),
diff --git a/chrome/browser/chromeos/crosapi/browser_loader.cc b/chrome/browser/chromeos/crosapi/browser_loader.cc index a6693f7..f586d832 100644 --- a/chrome/browser/chromeos/crosapi/browser_loader.cc +++ b/chrome/browser/chromeos/crosapi/browser_loader.cc
@@ -8,15 +8,19 @@ #include "base/bind.h" #include "base/command_line.h" +#include "base/feature_list.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/logging.h" #include "base/task/task_traits.h" #include "base/task/thread_pool.h" +#include "chrome/browser/browser_process.h" #include "chrome/browser/chromeos/crosapi/browser_util.h" +#include "chrome/browser/ui/ash/system_tray_client.h" #include "chromeos/constants/chromeos_features.h" #include "chromeos/constants/chromeos_switches.h" #include "chromeos/cryptohome/system_salt_getter.h" +#include "components/component_updater/component_updater_service.h" namespace crosapi { @@ -31,22 +35,52 @@ const base::Feature kLacrosPreferDogfoodOverFishfood{ "LacrosPreferDogfoodOverFishfood", base::FEATURE_DISABLED_BY_DEFAULT}; -std::string GetLacrosComponentName() { +// Emergency kill switch in case the notification code doesn't work properly. +const base::Feature kLacrosShowUpdateNotifications{ + "LacrosShowUpdateNotifications", base::FEATURE_ENABLED_BY_DEFAULT}; + +struct ComponentInfo { + // The client-side component name. + const char* const name; + // The CRX "extension" ID for component updater. + // Must match the Omaha console. + const char* const crx_id; +}; + +// NOTE: If you change the lacros component names, you must also update +// chrome/browser/component_updater/cros_component_installer_chromeos.cc +constexpr ComponentInfo kLacrosFishfoodInfo = { + "lacros-fishfood", "hkifppleldbgkdlijbdfkdpedggaopda"}; +constexpr ComponentInfo kLacrosDogfoodDevInfo = { + "lacros-dogfood-dev", "ldobopbhiamakmncndpkeelenhdmgfhk"}; +constexpr ComponentInfo kLacrosDogfoodStableInfo = { + "lacros-dogfood-stable", "hnfmbeciphpghlfgpjfbcdifbknombnk"}; + +ComponentInfo GetLacrosComponentInfo() { if (!base::FeatureList::IsEnabled(kLacrosPreferDogfoodOverFishfood)) - return "lacros-fishfood"; + return kLacrosFishfoodInfo; const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess(); if (cmdline->HasSwitch(browser_util::kLacrosStabilitySwitch)) { std::string value = cmdline->GetSwitchValueASCII(browser_util::kLacrosStabilitySwitch); if (value == browser_util::kLacrosStabilityLessStable) { - return "lacros-dogfood-dev"; + return kLacrosDogfoodDevInfo; } else if (value == browser_util::kLacrosStabilityMoreStable) { - return "lacros-dogfood-stable"; + return kLacrosDogfoodStableInfo; } } // Use more frequent updates by default. - return "lacros-dogfood-dev"; + return kLacrosDogfoodDevInfo; +} + +std::string GetLacrosComponentName() { + return GetLacrosComponentInfo().name; +} + +// Returns the CRX "extension" ID for a lacros component. +std::string GetLacrosComponentCrxId() { + return GetLacrosComponentInfo().crx_id; } // Returns whether lacros-fishfood component is already installed. @@ -67,15 +101,46 @@ return true; } +// Production delegate implementation. +class DelegateImpl : public BrowserLoader::Delegate { + public: + DelegateImpl() = default; + DelegateImpl(const DelegateImpl&) = delete; + DelegateImpl& operator=(const DelegateImpl&) = delete; + ~DelegateImpl() override = default; + + // BrowserLoader::Delegate: + void SetLacrosUpdateAvailable() override { + if (base::FeatureList::IsEnabled(kLacrosShowUpdateNotifications)) { + // Show the update notification in ash. + SystemTrayClient::Get()->SetLacrosUpdateAvailable(); + } + } +}; + } // namespace BrowserLoader::BrowserLoader( scoped_refptr<component_updater::CrOSComponentManager> manager) - : component_manager_(manager) { + : BrowserLoader(std::make_unique<DelegateImpl>(), manager) {} + +BrowserLoader::BrowserLoader( + std::unique_ptr<Delegate> delegate, + scoped_refptr<component_updater::CrOSComponentManager> manager) + : delegate_(std::move(delegate)), + component_manager_(manager), + component_update_service_(g_browser_process->component_updater()) { + DCHECK(delegate_); DCHECK(component_manager_); } -BrowserLoader::~BrowserLoader() = default; +BrowserLoader::~BrowserLoader() { + // May be null in tests. + if (component_update_service_) { + // Removing an observer is a no-op if the observer wasn't added. + component_update_service_->RemoveObserver(this); + } +} void BrowserLoader::Load(LoadCompletionCallback callback) { DCHECK(browser_util::IsLacrosEnabled()); @@ -113,19 +178,35 @@ weak_factory_.GetWeakPtr())); } +void BrowserLoader::OnEvent(Events event, const std::string& id) { + // Check for the Lacros component being updated. + if (event == Events::COMPONENT_UPDATED && id == GetLacrosComponentCrxId()) { + delegate_->SetLacrosUpdateAvailable(); + } +} + void BrowserLoader::OnLoadComplete( LoadCompletionCallback callback, component_updater::CrOSComponentManager::Error error, const base::FilePath& path) { - bool success = - (error == component_updater::CrOSComponentManager::Error::NONE); - if (success) { - LOG(WARNING) << "Loaded lacros image at " << path.MaybeAsASCII(); - } else { + // Bail out on error. + if (error != component_updater::CrOSComponentManager::Error::NONE) { LOG(WARNING) << "Error loading lacros component image: " << static_cast<int>(error); + std::move(callback).Run(base::FilePath()); + return; } - std::move(callback).Run(success ? path : base::FilePath()); + // Log the path on success. + LOG(WARNING) << "Loaded lacros image at " << path.MaybeAsASCII(); + std::move(callback).Run(path); + + // May be null in tests. + if (component_update_service_) { + // Now that we have the initial component download, start observing for + // future updates. We don't do this in the constructor because we don't want + // to show the "update available" notification for the initial load. + component_update_service_->AddObserver(this); + } } void BrowserLoader::OnCheckInstalled(bool was_installed) {
diff --git a/chrome/browser/chromeos/crosapi/browser_loader.h b/chrome/browser/chromeos/crosapi/browser_loader.h index d06d065..296263cc 100644 --- a/chrome/browser/chromeos/crosapi/browser_loader.h +++ b/chrome/browser/chromeos/crosapi/browser_loader.h
@@ -10,20 +10,34 @@ #include "base/memory/scoped_refptr.h" #include "base/memory/weak_ptr.h" #include "chrome/browser/component_updater/cros_component_manager.h" +#include "components/component_updater/component_updater_service.h" namespace crosapi { -// Manages download of the lacros-chrome binary. This class is a part of -// ash-chrome. -class BrowserLoader { +// Manages download of the lacros-chrome binary. After the initial component is +// downloaded and mounted, observes the component updater for future updates. +// If it detects a new update, triggers a user-visible notification. +// This class is a part of ash-chrome. +class BrowserLoader + : public component_updater::ComponentUpdateService::Observer { public: + // Delete for testing. + class Delegate { + public: + virtual void SetLacrosUpdateAvailable() = 0; + virtual ~Delegate() = default; + }; + // Contructor for production. explicit BrowserLoader( scoped_refptr<component_updater::CrOSComponentManager> manager); + // Constructor for testing. + BrowserLoader(std::unique_ptr<Delegate> delegate, + scoped_refptr<component_updater::CrOSComponentManager> manager); BrowserLoader(const BrowserLoader&) = delete; BrowserLoader& operator=(const BrowserLoader&) = delete; - ~BrowserLoader(); + ~BrowserLoader() override; // Starts to load lacros-chrome binary. // |callback| is called on completion with the path to the lacros-chrome on @@ -36,6 +50,9 @@ // Note that this triggers to remove the user directory for lacros-chrome. void Unload(); + // component_updater::ComponentUpdateService::Observer: + void OnEvent(Events event, const std::string& id) override; + private: // Called on the completion of loading. void OnLoadComplete(LoadCompletionCallback callback, @@ -49,9 +66,14 @@ // Unloads the component. Called after system salt is available. void UnloadAfterCleanUp(const std::string& ignored_salt); - // May be null in tests. + // Allows stubbing out some methods for testing. + std::unique_ptr<Delegate> delegate_; + scoped_refptr<component_updater::CrOSComponentManager> component_manager_; + // May be null in tests. + component_updater::ComponentUpdateService* const component_update_service_; + base::WeakPtrFactory<BrowserLoader> weak_factory_{this}; };
diff --git a/chrome/browser/chromeos/crosapi/browser_loader_unittest.cc b/chrome/browser/chromeos/crosapi/browser_loader_unittest.cc new file mode 100644 index 0000000..ea66094 --- /dev/null +++ b/chrome/browser/chromeos/crosapi/browser_loader_unittest.cc
@@ -0,0 +1,102 @@ +// 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/chromeos/crosapi/browser_loader.h" + +#include "base/run_loop.h" +#include "base/test/bind.h" +#include "base/test/scoped_feature_list.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/chromeos/crosapi/browser_util.h" +#include "chrome/browser/component_updater/fake_cros_component_manager.h" +#include "chrome/test/base/browser_process_platform_part_test_api_chromeos.h" +#include "chromeos/constants/chromeos_features.h" +#include "components/update_client/update_client.h" +#include "content/public/test/browser_task_environment.h" +#include "testing/gtest/include/gtest/gtest.h" + +using update_client::UpdateClient; + +namespace crosapi { +namespace { + +// Delegate for testing. +class DelegateImpl : public BrowserLoader::Delegate { + public: + DelegateImpl() = default; + DelegateImpl(const DelegateImpl&) = delete; + DelegateImpl& operator=(const DelegateImpl&) = delete; + ~DelegateImpl() override = default; + + // BrowserLoader::Delegate: + void SetLacrosUpdateAvailable() override { ++set_lacros_update_available_; } + + // Public because this is test code. + int set_lacros_update_available_ = 0; +}; + +class BrowserLoaderTest : public testing::Test { + public: + BrowserLoaderTest() { browser_util::SetLacrosEnabledForTest(true); } + + ~BrowserLoaderTest() override { + browser_util::SetLacrosEnabledForTest(false); + } + + // Public because this is test code. + content::BrowserTaskEnvironment task_environment_; +}; + +TEST_F(BrowserLoaderTest, ShowUpdateNotification) { + // Create dependencies for object under test. + scoped_refptr<component_updater::FakeCrOSComponentManager> component_manager = + base::MakeRefCounted<component_updater::FakeCrOSComponentManager>(); + component_manager->set_supported_components({"lacros-fishfood"}); + component_manager->ResetComponentState( + "lacros-fishfood", + component_updater::FakeCrOSComponentManager::ComponentInfo( + component_updater::CrOSComponentManager::Error::NONE, + base::FilePath("/install/path"), base::FilePath("/mount/path"))); + BrowserProcessPlatformPartTestApi browser_part( + g_browser_process->platform_part()); + browser_part.InitializeCrosComponentManager(component_manager); + + // Create object under test. + auto delegate_ptr = std::make_unique<DelegateImpl>(); + DelegateImpl* delegate = delegate_ptr.get(); + BrowserLoader browser_loader(std::move(delegate_ptr), component_manager); + + // Creating the loader does not trigger an update notification. + EXPECT_EQ(0, delegate->set_lacros_update_available_); + + // The initial load of the component does not trigger an update notification. + base::RunLoop run_loop; + browser_loader.Load(base::BindLambdaForTesting( + [&](const base::FilePath&) { run_loop.Quit(); })); + run_loop.Run(); + EXPECT_EQ(0, delegate->set_lacros_update_available_); + + // Update check does not trigger an update notification. + constexpr char kLacrosFishfoodId[] = "hkifppleldbgkdlijbdfkdpedggaopda"; + browser_loader.OnEvent( + UpdateClient::Observer::Events::COMPONENT_CHECKING_FOR_UPDATES, + kLacrosFishfoodId); + EXPECT_EQ(0, delegate->set_lacros_update_available_); + + // Update download does not trigger an update notification. + browser_loader.OnEvent( + UpdateClient::Observer::Events::COMPONENT_UPDATE_DOWNLOADING, + kLacrosFishfoodId); + EXPECT_EQ(0, delegate->set_lacros_update_available_); + + // Update completion trigger the notification. + browser_loader.OnEvent(UpdateClient::Observer::Events::COMPONENT_UPDATED, + kLacrosFishfoodId); + EXPECT_EQ(1, delegate->set_lacros_update_available_); + + browser_part.ShutdownCrosComponentManager(); +} + +} // namespace +} // namespace crosapi
diff --git a/chrome/browser/chromeos/crosapi/browser_util.cc b/chrome/browser/chromeos/crosapi/browser_util.cc index 4542404..47b3046 100644 --- a/chrome/browser/chromeos/crosapi/browser_util.cc +++ b/chrome/browser/chromeos/crosapi/browser_util.cc
@@ -48,6 +48,8 @@ namespace browser_util { namespace { +bool g_lacros_enabled_for_test = false; + // Some account types require features that aren't yet supported by lacros. // See https://crbug.com/1080693 bool IsUserTypeAllowed(const User* user) { @@ -135,6 +137,11 @@ } bool IsLacrosEnabled(Channel channel) { + // Allows tests to avoid enabling the flag, constructing a fake user manager, + // creating g_browser_process->local_state(), etc. + if (g_lacros_enabled_for_test) + return true; + if (!base::FeatureList::IsEnabled(chromeos::features::kLacrosSupport)) { LOG(WARNING) << "Lacros-chrome is not supported"; return false; @@ -175,6 +182,10 @@ } } +void SetLacrosEnabledForTest(bool force_enabled) { + g_lacros_enabled_for_test = force_enabled; +} + bool IsLacrosWindow(const aura::Window* window) { const std::string* app_id = exo::GetShellApplicationId(window); if (!app_id)
diff --git a/chrome/browser/chromeos/crosapi/browser_util.h b/chrome/browser/chromeos/crosapi/browser_util.h index fa7b983f..668b272 100644 --- a/chrome/browser/chromeos/crosapi/browser_util.h +++ b/chrome/browser/chromeos/crosapi/browser_util.h
@@ -60,6 +60,9 @@ // As above, but takes a channel. Exposed for testing. bool IsLacrosEnabled(version_info::Channel channel); +// Forces IsLacrosEnabled() to return true for testing. +void SetLacrosEnabledForTest(bool force_enabled); + // Returns true if |window| is an exo ShellSurface window representing a Lacros // browser. bool IsLacrosWindow(const aura::Window* window);
diff --git a/chrome/browser/chromeos/crostini/termina_installer.cc b/chrome/browser/chromeos/crostini/termina_installer.cc index cd8d6de..7a37f262 100644 --- a/chrome/browser/chromeos/crostini/termina_installer.cc +++ b/chrome/browser/chromeos/crostini/termina_installer.cc
@@ -137,6 +137,12 @@ LOG(ERROR) << "Failed to install the cros-termina component with error code: " << static_cast<int>(error); + + if (error == + component_updater::CrOSComponentManager::Error::MOUNT_FAILURE) { + ReinstallComponent(std::move(callback)); + return; + } if (is_update_checked) { scoped_refptr<component_updater::CrOSComponentManager> component_manager = g_browser_process->platform_part()->cros_component_manager(); @@ -188,6 +194,38 @@ std::move(callback).Run(result); } +void TerminaInstaller::ReinstallComponent( + base::OnceCallback<void(InstallResult)> callback) { + scoped_refptr<component_updater::CrOSComponentManager> component_manager = + g_browser_process->platform_part()->cros_component_manager(); + if (component_manager->Unload(imageloader::kTerminaComponentName)) { + component_manager->Load( + imageloader::kTerminaComponentName, + component_updater::CrOSComponentManager::MountPolicy::kMount, + UpdatePolicy::kDontForce, + base::BindOnce(&TerminaInstaller::OnReinstallComponent, + weak_ptr_factory_.GetWeakPtr(), std::move(callback), + false)); + } else { + std::move(callback).Run(InstallResult::Failure); + } +} + +void TerminaInstaller::OnReinstallComponent( + base::OnceCallback<void(InstallResult)> callback, + bool is_update_checked, + component_updater::CrOSComponentManager::Error error, + const base::FilePath& path) { + LOG(ERROR) << "Attempting to re-install cros-termina component."; + if (error != component_updater::CrOSComponentManager::Error::MOUNT_FAILURE) { + OnInstallComponent(std::move(callback), is_update_checked, error, path); + return; + } + // Give up with a permanent failure. The newly downloaded component failed to + // mount. + std::move(callback).Run(InstallResult::Failure); +} + void TerminaInstaller::Uninstall(base::OnceCallback<void(bool)> callback) { // Unset |termina_location_| now since it will become invalid at some point // soon.
diff --git a/chrome/browser/chromeos/crostini/termina_installer.h b/chrome/browser/chromeos/crostini/termina_installer.h index b60676f..742a4de 100644 --- a/chrome/browser/chromeos/crostini/termina_installer.h +++ b/chrome/browser/chromeos/crostini/termina_installer.h
@@ -67,6 +67,12 @@ bool is_update_checked, component_updater::CrOSComponentManager::Error error, const base::FilePath& path); + void ReinstallComponent(base::OnceCallback<void(InstallResult)> callback); + void OnReinstallComponent( + base::OnceCallback<void(InstallResult)> callback, + bool is_update_checked, + component_updater::CrOSComponentManager::Error error, + const base::FilePath& path); void RemoveComponentIfPresent(base::OnceCallback<void()> callback, UninstallResult* result);
diff --git a/chrome/browser/chromeos/login/session/user_session_initializer.cc b/chrome/browser/chromeos/login/session/user_session_initializer.cc index 27ee924..753687d 100644 --- a/chrome/browser/chromeos/login/session/user_session_initializer.cc +++ b/chrome/browser/chromeos/login/session/user_session_initializer.cc
@@ -30,7 +30,9 @@ #include "chrome/browser/component_updater/sth_set_component_remover.h" #include "chrome/browser/google/google_brand_chromeos.h" #include "chrome/browser/net/nss_context.h" +#include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/ui/ash/clipboard_image_model_factory_impl.h" +#include "chrome/browser/ui/ash/holding_space/holding_space_keyed_service_factory.h" #include "chrome/browser/ui/ash/media_client_impl.h" #include "chrome/common/pref_names.h" #include "chromeos/constants/chromeos_features.h" @@ -205,8 +207,14 @@ } void UserSessionInitializer::OnUserSessionStarted(bool is_primary_user) { + Profile* profile = ProfileManager::GetActiveUserProfile(); + DCHECK(profile); + + // Ensure that the `HoldingSpaceKeyedService` for `profile` is created. + ash::HoldingSpaceKeyedServiceFactory::GetInstance()->GetService(profile); + if (is_primary_user) { - DCHECK_NE(primary_profile_, nullptr); + DCHECK_EQ(primary_profile_, profile); plugin_vm::PluginVmManager* plugin_vm_manager = plugin_vm::PluginVmManagerFactory::GetForProfile(primary_profile_);
diff --git a/chrome/browser/chromeos/platform_keys/platform_keys.cc b/chrome/browser/chromeos/platform_keys/platform_keys.cc index 00dffe9..e06264d 100644 --- a/chrome/browser/chromeos/platform_keys/platform_keys.cc +++ b/chrome/browser/chromeos/platform_keys/platform_keys.cc
@@ -55,6 +55,8 @@ return "Algorithm not supported."; case Status::kErrorCertificateNotFound: return "Certificate could not be found."; + case Status::kErrorInputTooLong: + return "Input too long."; case Status::kErrorGrantKeyPermissionForExtension: return "Tried to grant permission for a key although prohibited (either " "key is a corporate key or this account is managed).";
diff --git a/chrome/browser/chromeos/platform_keys/platform_keys.h b/chrome/browser/chromeos/platform_keys/platform_keys.h index 66fabc46..880c1cfa 100644 --- a/chrome/browser/chromeos/platform_keys/platform_keys.h +++ b/chrome/browser/chromeos/platform_keys/platform_keys.h
@@ -43,6 +43,7 @@ kSuccess, kErrorAlgorithmNotSupported, kErrorCertificateNotFound, + kErrorInputTooLong, kErrorGrantKeyPermissionForExtension, kErrorInternal, kErrorKeyAttributeRetrievalFailed,
diff --git a/chrome/browser/chromeos/platform_keys/platform_keys_service.h b/chrome/browser/chromeos/platform_keys/platform_keys_service.h index 2d95482..3f1feb5 100644 --- a/chrome/browser/chromeos/platform_keys/platform_keys_service.h +++ b/chrome/browser/chromeos/platform_keys/platform_keys_service.h
@@ -156,11 +156,12 @@ SignCallback callback) = 0; // Applies PKCS1 padding and afterwards signs the data with the private key - // matching |public_key_spki_der|. |data| is not digested. If the key is not - // found in that |token_id| (or in none of the available tokens if |token_id| - // is not specified), the operation aborts. The size of |data| (number of - // octets) must be smaller than k - 11, where k is the key size in octets. - // |callback| will be invoked with the signature or an error status. + // matching |public_key_spki_der|. |data| is not digested, PKCS1 DigestInfo is + // not prepended. If the key is not found in that |token_id| (or in none of + // the available tokens if |token_id| is not specified), the operation aborts. + // The size of |data| (number of octets) must be smaller than k - 11, where k + // is the key size in octets. |callback| will be invoked with the signature or + // an error status. virtual void SignRSAPKCS1Raw(base::Optional<TokenId> token_id, const std::string& data, const std::string& public_key_spki_der,
diff --git a/chrome/browser/chromeos/platform_keys/platform_keys_service_browsertest.cc b/chrome/browser/chromeos/platform_keys/platform_keys_service_browsertest.cc index dfabd7a..8512e375 100644 --- a/chrome/browser/chromeos/platform_keys/platform_keys_service_browsertest.cc +++ b/chrome/browser/chromeos/platform_keys/platform_keys_service_browsertest.cc
@@ -16,7 +16,10 @@ #include "base/memory/weak_ptr.h" #include "base/optional.h" #include "base/run_loop.h" +#include "base/stl_util.h" +#include "base/strings/strcat.h" #include "base/strings/string_number_conversions.h" +#include "base/strings/string_piece.h" #include "base/strings/stringprintf.h" #include "base/task/post_task.h" #include "base/threading/thread_restrictions.h" @@ -47,6 +50,7 @@ #include "content/public/test/browser_test.h" #include "crypto/nss_key_util.h" #include "crypto/scoped_nss_types.h" +#include "crypto/sha2.h" #include "crypto/signature_verifier.h" #include "net/cert/nss_cert_database.h" #include "net/cert/x509_certificate.h" @@ -95,6 +99,24 @@ TokenId token_id; }; +// Returns |hash| prefixed with DER-encoded PKCS#1 DigestInfo with +// AlgorithmIdentifier=id-sha256. +// This is useful for testing PlatformKeysService::SignRSAPKCS1Raw which only +// appends PKCS#1 v1.5 padding before signing. +std::string PrependSHA256DigestInfo(base::StringPiece hash) { + // DER-encoded PKCS#1 DigestInfo "prefix" with + // AlgorithmIdentifier=id-sha256. + // The encoding is taken from https://tools.ietf.org/html/rfc3447#page-43 + const uint8_t kDigestInfoSha256DerData[] = { + 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, + 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20}; + const base::StringPiece kDigestInfoSha256Der( + reinterpret_cast<const char*>(kDigestInfoSha256DerData), + base::size(kDigestInfoSha256DerData)); + + return base::StrCat({kDigestInfoSha256Der, hash}); +} + } // namespace class PlatformKeysServiceBrowserTestBase @@ -171,17 +193,23 @@ // Generates a key pair in the given |token_id| using platform keys service // and returns the SubjectPublicKeyInfo string encoded in DER format. - std::string GenerateKeyPair(TokenId token_id) { - const unsigned int kKeySize = 2048; - + std::string GenerateKeyPair(TokenId token_id, unsigned int key_size) { test_util::GenerateKeyExecutionWaiter generate_key_waiter; - platform_keys_service()->GenerateRSAKey(token_id, kKeySize, + platform_keys_service()->GenerateRSAKey(token_id, key_size, generate_key_waiter.GetCallback()); generate_key_waiter.Wait(); return generate_key_waiter.public_key_spki_der(); } + // Generates a key pair with a default size in the given |token_id| using + // platform keys service and returns the SubjectPublicKeyInfo string encoded + // in DER format. + std::string GenerateKeyPair(TokenId token_id) { + const unsigned int kDefaultKeySize = 2048; + return GenerateKeyPair(token_id, kDefaultKeySize); + } + // Imports the certificate and key described by the |cert_filename| and // |key_filename| files in |source_dir| into the Token |token_id|, then stores // the resulting certificate in *|out_cert| and the SPKI of the public key in @@ -426,6 +454,79 @@ EXPECT_TRUE(signature_verifier.VerifyFinal()); } +// Generates a Rsa key pair and tests signing using the SignRSAPKCS1Raw +// function. +IN_PROC_BROWSER_TEST_P(PlatformKeysServicePerTokenBrowserTest, + GenerateRsaAndSignRaw) { + const unsigned int kKeySize = 2048; + const TokenId token_id = GetParam().token_id; + + // SignRSAPKCS1Raw only performs PKCS#1.5 padding. To get a correct PKCS#1 + // signature of |kDataToSign|, it is necessary to pass + // (DigestInfo + hash(kDataToSign)) to SignRSAPKCS1Raw, where DigestInfo + // describes the hash function. + const std::string kDataToSign = "test"; + const std::string kDataToSignHash = crypto::SHA256HashString(kDataToSign); + const std::string kDigestInfoAndDataToSignHash = + PrependSHA256DigestInfo(kDataToSignHash); + + const crypto::SignatureVerifier::SignatureAlgorithm kSignatureAlgorithm = + crypto::SignatureVerifier::RSA_PKCS1_SHA256; + + const std::string public_key_spki_der = GenerateKeyPair(token_id, kKeySize); + + test_util::SignExecutionWaiter sign_waiter; + platform_keys_service()->SignRSAPKCS1Raw( + token_id, kDigestInfoAndDataToSignHash, public_key_spki_der, + sign_waiter.GetCallback()); + sign_waiter.Wait(); + EXPECT_EQ(sign_waiter.status(), Status::kSuccess); + + crypto::SignatureVerifier signature_verifier; + ASSERT_TRUE(signature_verifier.VerifyInit( + kSignatureAlgorithm, + base::as_bytes(base::make_span(sign_waiter.signature())), + base::as_bytes(base::make_span(public_key_spki_der)))); + signature_verifier.VerifyUpdate(base::as_bytes(base::make_span(kDataToSign))); + EXPECT_TRUE(signature_verifier.VerifyFinal()); +} + +// Generates a Rsa key pair and tests expected limits of the input length of the +// SignRSAPKCS1Raw function. +IN_PROC_BROWSER_TEST_P(PlatformKeysServicePerTokenBrowserTest, + SignRawInputTooLong) { + const unsigned int kKeySize = 2048; + const TokenId token_id = GetParam().token_id; + + const std::string public_key_spki_der = GenerateKeyPair(token_id, kKeySize); + + // SignRSAPKCS1Raw performs PKCS#11 padding which adds at least 11 bytes. + { + // An input of |kKeySize in bytes - 11| should be fine. + std::string data_to_sign; + data_to_sign.resize(kKeySize / 8 - 11); + + test_util::SignExecutionWaiter sign_waiter; + platform_keys_service()->SignRSAPKCS1Raw( + token_id, data_to_sign, public_key_spki_der, sign_waiter.GetCallback()); + sign_waiter.Wait(); + EXPECT_EQ(sign_waiter.status(), Status::kSuccess); + } + + { + // An input of |kKeySize in bytes - 10| should be too long. + std::string data_to_sign_too_long; + data_to_sign_too_long.resize(kKeySize / 8 - 10); + + test_util::SignExecutionWaiter sign_waiter; + platform_keys_service()->SignRSAPKCS1Raw(token_id, data_to_sign_too_long, + public_key_spki_der, + sign_waiter.GetCallback()); + sign_waiter.Wait(); + EXPECT_EQ(sign_waiter.status(), Status::kErrorInputTooLong); + } +} + IN_PROC_BROWSER_TEST_P(PlatformKeysServicePerTokenBrowserTest, SetAndGetKeyAttribute) { // The attribute type to be set and retrieved using platform keys service.
diff --git a/chrome/browser/chromeos/platform_keys/platform_keys_service_nss.cc b/chrome/browser/chromeos/platform_keys/platform_keys_service_nss.cc index a559c17..2a816b96 100644 --- a/chrome/browser/chromeos/platform_keys/platform_keys_service_nss.cc +++ b/chrome/browser/chromeos/platform_keys/platform_keys_service_nss.cc
@@ -826,6 +826,29 @@ base::BindOnce(&GenerateECKeyOnWorkerThread, std::move(state))); } +// Checks whether |input_length| is lower or equal to the maximum input length +// for a RSA PKCS#1 v1.5 signature generated using |private_key| with PK11_Sign. +// Returns false if |input_length| is too large. +// If the maximum input length can not be determined (which is possible because +// it queries the PKCS#11 module), returns true and logs a warning. +bool CheckRSAPKCS1SignRawInputLength(SECKEYPrivateKey* private_key, + size_t input_length) { + // For RSA keys, PK11_Sign will perform PKCS#1 v1.5 padding, which needs at + // least 11 bytes. RSA Sign can process an input of max. modulus length. + // Thus the maximum input length for the sign operation is + // |modulus_length - 11|. + int modulus_length_bytes = PK11_GetPrivateModulusLen(private_key); + if (modulus_length_bytes <= 0) { + LOG(WARNING) << "Could not determine modulus length"; + return true; + } + size_t max_input_length_after_padding = + static_cast<size_t>(modulus_length_bytes); + // PKCS#1 v1.5 Padding needs at least this many bytes. + size_t kMinPaddingLength = 11u; + return input_length + kMinPaddingLength <= max_input_length_after_padding; +} + // Performs "raw" PKCS1 v1.5 padding + signing by calling PK11_Sign on // |rsa_key|. void SignRSAPKCS1RawOnWorkerThread(std::unique_ptr<SignState> state, @@ -848,6 +871,17 @@ std::vector<unsigned char> signature(signature_len); SECItem signature_output = {siBuffer, signature.data(), signature.size()}; if (PK11_Sign(rsa_key.get(), &signature_output, &input) != SECSuccess) { + // Input size is checked after a failure - obtaining max input size + // involves extracting key modulus length which is not a free operation, so + // don't bother if signing succeeded. + // Note: It would be better if this could be determined from some library + // return code (e.g. PORT_GetError), but this was not possible with + // NSS+chaps at this point. + if (!CheckRSAPKCS1SignRawInputLength(rsa_key.get(), state->data_.size())) { + LOG(ERROR) << "Couldn't sign - input too long."; + state->OnError(FROM_HERE, Status::kErrorInputTooLong); + return; + } LOG(ERROR) << "Couldn't sign."; state->OnError(FROM_HERE, Status::kErrorInternal); return;
diff --git a/chrome/browser/component_updater/cros_component_installer_chromeos.cc b/chrome/browser/component_updater/cros_component_installer_chromeos.cc index ab0f0d5..a43b245e 100644 --- a/chrome/browser/component_updater/cros_component_installer_chromeos.cc +++ b/chrome/browser/component_updater/cros_component_installer_chromeos.cc
@@ -48,6 +48,8 @@ "5714811c04f0a63aac96b39096faa759ace4c04e9b68291e7c9716128f5a2722"}, {"demo-mode-resources", "1.0", "93c093ebac788581389015e9c59c5af111d2fa5174d206eb795042e6376cbd10"}, + // NOTE: If you change the lacros component names, you must also update + // chrome/browser/chromeos/crosapi/browser_loader.cc. {"lacros-fishfood", "", "7a85ffb4b316a3b89135a3f43660ef3049950a61a2f8df4237e1ec213852b848"}, {"lacros-dogfood-dev", "",
diff --git a/chrome/browser/diagnostics/sqlite_diagnostics.cc b/chrome/browser/diagnostics/sqlite_diagnostics.cc index 8c3d45c..1511c83e 100644 --- a/chrome/browser/diagnostics/sqlite_diagnostics.cc +++ b/chrome/browser/diagnostics/sqlite_diagnostics.cc
@@ -103,8 +103,8 @@ int errors = 0; { // Scope the statement and database so they close properly. - sql::Database database; - database.set_exclusive_locking(); + sql::Database database( + {.exclusive_locking = true, .page_size = 4096, .cache_size = 500}); scoped_refptr<ErrorRecorder> recorder(new ErrorRecorder); // Set the error callback so that we can get useful results in a debug
diff --git a/chrome/browser/download/download_browsertest.cc b/chrome/browser/download/download_browsertest.cc index fbc815a..0ffebfa 100644 --- a/chrome/browser/download/download_browsertest.cc +++ b/chrome/browser/download/download_browsertest.cc
@@ -4268,7 +4268,50 @@ // Testing the behavior of resuming with only in-progress download manager. class InProgressDownloadTest : public DownloadTest { public: - void SetUpOnMainThread() override { EXPECT_TRUE(CheckTestDir()); } + InProgressDownloadTest() { + // The in progress download manager will be released from + // `DownloadManagerUtils` during creation of the `DownloadManagerImpl`. As + // the `DownloadManagerImpl` may be created before test bodies can run, + // register a callback to cache a pointer before release occurs. + DownloadManagerUtils:: + SetRetrieveInProgressDownloadManagerCallbackForTesting( + base::BindRepeating( + &InProgressDownloadTest::set_in_progress_manager, + base::Unretained(this))); + } + + // DownloadTest: + void SetUpOnMainThread() override { + EXPECT_TRUE(CheckTestDir()); + + if (!in_progress_manager_) { + // This will only occur if `DownloadManagerImpl` has not already been + // created in which case the in progress download manager has not yet been + // released from `DownloadManagerUtils`. + set_in_progress_manager( + DownloadManagerUtils::GetInProgressDownloadManager( + browser()->profile()->GetProfileKey())); + } + + // As a pointer to the in progress download manager has now been cached, + // watching for release from `DownloadManagerUtils` (if it has not already + // occurred) is no longer necessary. + DownloadManagerUtils:: + SetRetrieveInProgressDownloadManagerCallbackForTesting( + base::NullCallback()); + } + + download::InProgressDownloadManager* in_progress_manager() { + return in_progress_manager_; + } + + void set_in_progress_manager( + download::InProgressDownloadManager* in_progress_manager) { + in_progress_manager_ = in_progress_manager; + } + + private: + download::InProgressDownloadManager* in_progress_manager_ = nullptr; }; // Check that if a download exists in both in-progress and history DB, @@ -4290,9 +4333,6 @@ std::string guid = base::GenerateGUID(); // Wait for in-progress download manager to initialize. - download::InProgressDownloadManager* in_progress_manager = - DownloadManagerUtils::GetInProgressDownloadManager( - browser()->profile()->GetProfileKey()); download::SimpleDownloadManagerCoordinator* coordinator = SimpleDownloadManagerCoordinatorFactory::GetForKey( browser()->profile()->GetProfileKey()); @@ -4307,9 +4347,9 @@ std::vector<GURL> url_chain; url_chain.emplace_back(url); base::Time current_time = base::Time::Now(); - in_progress_manager->AddInProgressDownloadForTest( + in_progress_manager()->AddInProgressDownloadForTest( std::make_unique<download::DownloadItemImpl>( - in_progress_manager, guid, 1 /* id */, + in_progress_manager(), guid, 1 /* id */, target_path.AddExtensionASCII("crdownload"), target_path, url_chain, GURL() /* referrer_url */, GURL() /* site_url */, GURL() /* tab_url */, GURL() /* tab_referrer_url */, @@ -4359,9 +4399,6 @@ std::string guid = base::GenerateGUID(); // Wait for in-progress download manager to initialize. - download::InProgressDownloadManager* in_progress_manager = - DownloadManagerUtils::GetInProgressDownloadManager( - browser()->profile()->GetProfileKey()); download::SimpleDownloadManagerCoordinator* coordinator = SimpleDownloadManagerCoordinatorFactory::GetForKey( browser()->profile()->GetProfileKey()); @@ -4382,14 +4419,14 @@ params->set_file_path(target_path); params->set_transient(true); params->set_require_safety_checks(false); - in_progress_manager->DownloadUrl(std::move(params)); + in_progress_manager()->DownloadUrl(std::move(params)); auto params2 = std::make_unique<DownloadUrlParameters>( url, TRAFFIC_ANNOTATION_FOR_TESTS); params2->set_guid(guid); params2->set_file_path(target_path); params2->set_transient(true); params2->set_require_safety_checks(false); - in_progress_manager->DownloadUrl(std::move(params2)); + in_progress_manager()->DownloadUrl(std::move(params2)); coordinator_waiter.WaitForDownloadCreation(1); download::DownloadItem* download = coordinator->GetDownloadByGuid(guid); ASSERT_TRUE(download);
diff --git a/chrome/browser/download/download_manager_utils.cc b/chrome/browser/download/download_manager_utils.cc index c0f0485..6fe38b5 100644 --- a/chrome/browser/download/download_manager_utils.cc +++ b/chrome/browser/download/download_manager_utils.cc
@@ -43,6 +43,17 @@ return *map; } +// Returns a callback to be invoked during `RetrieveInProgressDownloadManager()` +// to provide an opportunity to cache a pointer to the in progress download +// manager being released. +base::RepeatingCallback<void(download::InProgressDownloadManager*)>& +GetRetrieveInProgressDownloadManagerCallback() { + static base::NoDestructor< + base::RepeatingCallback<void(download::InProgressDownloadManager*)>> + callback; + return *callback; +} + // Ignores origin security check. DownloadManagerImpl will provide its own // implementation when InProgressDownloadManager object is passed to it. bool IgnoreOriginSecurityCheck(const GURL& url) { @@ -71,6 +82,8 @@ ProfileKey* key = profile->GetProfileKey(); GetInProgressDownloadManager(key); auto& map = GetInProgressManagerMap(); + if (GetRetrieveInProgressDownloadManagerCallback()) + GetRetrieveInProgressDownloadManagerCallback().Run(map[key].get()); return map[key].release(); } @@ -129,3 +142,11 @@ } return map[key].get(); } + +// static +void DownloadManagerUtils:: + SetRetrieveInProgressDownloadManagerCallbackForTesting( + base::RepeatingCallback<void(download::InProgressDownloadManager*)> + callback) { + GetRetrieveInProgressDownloadManagerCallback() = callback; +}
diff --git a/chrome/browser/download/download_manager_utils.h b/chrome/browser/download/download_manager_utils.h index 79dc4a1..7d23ad32 100644 --- a/chrome/browser/download/download_manager_utils.h +++ b/chrome/browser/download/download_manager_utils.h
@@ -5,6 +5,7 @@ #ifndef CHROME_BROWSER_DOWNLOAD_DOWNLOAD_MANAGER_UTILS_H_ #define CHROME_BROWSER_DOWNLOAD_DOWNLOAD_MANAGER_UTILS_H_ +#include "base/callback_forward.h" #include "base/macros.h" class Profile; @@ -29,6 +30,13 @@ static download::InProgressDownloadManager* GetInProgressDownloadManager( ProfileKey* key); + // Registers a `callback` to be run during subsequent invocations of + // `RetrieveInProgressDownloadManager()`, providing an opportunity to cache + // a pointer to the in progress download manager being released. + static void SetRetrieveInProgressDownloadManagerCallbackForTesting( + base::RepeatingCallback<void(download::InProgressDownloadManager*)> + callback); + private: DISALLOW_COPY_AND_ASSIGN(DownloadManagerUtils); };
diff --git a/chrome/browser/download/save_page_browsertest.cc b/chrome/browser/download/save_page_browsertest.cc index 0a48741..a5bcd21 100644 --- a/chrome/browser/download/save_page_browsertest.cc +++ b/chrome/browser/download/save_page_browsertest.cc
@@ -802,15 +802,13 @@ #endif // Save the file as MHTML. Run until save completes. + base::RunLoop run_loop; + content::SavePackageFinishedObserver observer( + content::BrowserContext::GetDownloadManager(browser()->profile()), + run_loop.QuitClosure()); ASSERT_TRUE(select_file_dialog_factory->GetLastDialog()->CallFileSelected( full_file_name, "mhtml")); - { - base::RunLoop run_loop; - content::SavePackageFinishedObserver observer( - content::BrowserContext::GetDownloadManager(browser()->profile()), - run_loop.QuitClosure()); - run_loop.Run(); - } + run_loop.Run(); ASSERT_TRUE(VerifySavePackageExpectations(browser(), url)); persisted.WaitForPersisted();
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index d5a5f37d..1b92f5e 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json
@@ -478,6 +478,11 @@ "expiry_milestone": 91 }, { + "name": "cellular-use-attach-apn", + "owners": [ "vpalatin", "ejcaruso", "chromeos-cellular-platform@google.com" ], + "expiry_milestone": 95 + }, + { "name": "change-password-affiliation", "owners": [ "vsemeniuk", "vasilii"], "expiry_milestone": 90
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index 40e1f3b..a33443a 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -3829,6 +3829,12 @@ const char kCdmFactoryDaemonDescription[] = "Use the CDM daemon instead of the library CDM"; +const char kCellularUseAttachApnName[] = "Cellular use Attach APN"; +const char kCellularUseAttachApnDescription[] = + "Use the mobile operator database to set explicitly an Attach APN " + "for the LTE connections rather than letting the modem decide which " + "attach APN to use or retrieve it from the network"; + const char kConnectivityDiagnosticsWebUiName[] = "Connectivity Diagnostics WebUI"; const char kConnectivityDiagnosticsWebUiDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index 6847b214..1abf9c5 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -2214,6 +2214,9 @@ extern const char kCdmFactoryDaemonName[]; extern const char kCdmFactoryDaemonDescription[]; +extern const char kCellularUseAttachApnName[]; +extern const char kCellularUseAttachApnDescription[]; + extern const char kConnectivityDiagnosticsWebUiName[]; extern const char kConnectivityDiagnosticsWebUiDescription[];
diff --git a/chrome/browser/hid/chrome_hid_delegate.cc b/chrome/browser/hid/chrome_hid_delegate.cc index 9ed76ebc..78c22ad 100644 --- a/chrome/browser/hid/chrome_hid_delegate.cc +++ b/chrome/browser/hid/chrome_hid_delegate.cc
@@ -13,7 +13,6 @@ #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/hid/hid_chooser.h" #include "chrome/browser/ui/hid/hid_chooser_controller.h" -#include "chrome/browser/usb/usb_blocklist.h" #include "content/public/browser/web_contents.h" namespace {
diff --git a/chrome/browser/hid/hid_chooser_context.cc b/chrome/browser/hid/hid_chooser_context.cc index a015303..3173e0f 100644 --- a/chrome/browser/hid/hid_chooser_context.cc +++ b/chrome/browser/hid/hid_chooser_context.cc
@@ -11,10 +11,10 @@ #include "base/values.h" #include "chrome/browser/content_settings/host_content_settings_map_factory.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/usb/usb_blocklist.h" #include "chrome/grit/generated_resources.h" #include "components/content_settings/core/common/content_settings_types.h" #include "content/public/browser/device_service.h" +#include "services/device/public/cpp/hid/hid_blocklist.h" #include "ui/base/l10n/l10n_util.h" namespace { @@ -215,10 +215,8 @@ const url::Origin& requesting_origin, const url::Origin& embedding_origin, const device::mojom::HidDeviceInfo& device) { - if (UsbBlocklist::Get().IsExcluded( - {device.vendor_id, device.product_id, 0})) { + if (device::HidBlocklist::IsDeviceExcluded(device)) return false; - } if (!CanRequestObjectPermission(requesting_origin, embedding_origin)) return false;
diff --git a/chrome/browser/media/router/BUILD.gn b/chrome/browser/media/router/BUILD.gn index 51b0562..f908520 100644 --- a/chrome/browser/media/router/BUILD.gn +++ b/chrome/browser/media/router/BUILD.gn
@@ -250,7 +250,12 @@ "providers/extension/extension_media_route_provider_proxy_unittest.cc", "providers/wired_display/wired_display_media_route_provider_unittest.cc", ] - deps += [ ":test_support" ] + deps += [ + ":test_support", + "//chrome/test:test_support", + "//components/sync_preferences:test_support", + "//content/test:test_support", + ] } if (enable_openscreen) {
diff --git a/chrome/browser/media/router/media_router_feature.cc b/chrome/browser/media/router/media_router_feature.cc index 52716b2..4875422 100644 --- a/chrome/browser/media/router/media_router_feature.cc +++ b/chrome/browser/media/router/media_router_feature.cc
@@ -4,8 +4,12 @@ #include "chrome/browser/media/router/media_router_feature.h" +#include <utility> + #include "base/base64.h" +#include "base/containers/flat_map.h" #include "base/feature_list.h" +#include "base/no_destructor.h" #include "base/strings/string_util.h" #include "build/build_config.h" #include "chrome/browser/profiles/profile.h" @@ -66,11 +70,22 @@ #endif // !defined(OFFICIAL_BUILD) && !defined(OS_ANDROID) #if defined(OS_ANDROID) || BUILDFLAG(ENABLE_EXTENSIONS) + static base::NoDestructor<base::flat_map<content::BrowserContext*, bool>> + stored_pref_values; + + // If the Media Router was already enabled or disabled for |context|, then it + // must remain so. The Media Router does not support dynamic + // enabling/disabling. + auto const it = stored_pref_values->find(context); + if (it != stored_pref_values->end()) + return it->second; + + // Check the enterprise policy. const PrefService::Preference* pref = GetMediaRouterPref(context); - // Only use the pref value if it set from a mandatory policy. if (pref->IsManaged() && !pref->IsDefaultValue()) { - bool allowed = false; + bool allowed; CHECK(pref->GetValue()->GetAsBoolean(&allowed)); + stored_pref_values->insert(std::make_pair(context, allowed)); return allowed; }
diff --git a/chrome/browser/media/router/media_router_feature_unittest.cc b/chrome/browser/media/router/media_router_feature_unittest.cc index e61e9d4..d81a9e4 100644 --- a/chrome/browser/media/router/media_router_feature_unittest.cc +++ b/chrome/browser/media/router/media_router_feature_unittest.cc
@@ -4,11 +4,23 @@ #include "chrome/browser/media/router/media_router_feature.h" +#include <memory> + #include "base/test/scoped_feature_list.h" +#include "build/build_config.h" #include "components/prefs/pref_registry_simple.h" #include "components/prefs/testing_pref_service.h" +#include "extensions/buildflags/buildflags.h" #include "testing/gtest/include/gtest/gtest.h" +#if defined(OS_ANDROID) || BUILDFLAG(ENABLE_EXTENSIONS) +#include "base/values.h" +#include "chrome/common/pref_names.h" +#include "chrome/test/base/testing_profile.h" +#include "components/sync_preferences/testing_pref_service_syncable.h" +#include "content/public/test/browser_task_environment.h" +#endif // defined(OS_ANDROID) || BUILDFLAG(ENABLE_EXTENSIONS) + namespace media_router { TEST(MediaRouterFeatureTest, GetCastAllowAllIPsPref) { @@ -42,4 +54,41 @@ EXPECT_EQ(token, GetReceiverIdHashToken(pref_service.get())); } +#if defined(OS_ANDROID) || BUILDFLAG(ENABLE_EXTENSIONS) +class MediaRouterEnabledTest : public ::testing::Test { + public: + MediaRouterEnabledTest() = default; + MediaRouterEnabledTest(const MediaRouterEnabledTest&) = delete; + ~MediaRouterEnabledTest() override = default; + MediaRouterEnabledTest& operator=(const MediaRouterEnabledTest&) = delete; + + protected: + content::BrowserTaskEnvironment test_environment; + TestingProfile enabled_profile; + TestingProfile disabled_profile; +}; + +TEST_F(MediaRouterEnabledTest, TestEnabledByPolicy) { + enabled_profile.GetTestingPrefService()->SetManagedPref( + ::prefs::kEnableMediaRouter, std::make_unique<base::Value>(true)); + EXPECT_TRUE(MediaRouterEnabled(&enabled_profile)); + + enabled_profile.GetTestingPrefService()->SetManagedPref( + ::prefs::kEnableMediaRouter, std::make_unique<base::Value>(false)); + // Runtime changes are not supported. + EXPECT_TRUE(MediaRouterEnabled(&enabled_profile)); +} + +TEST_F(MediaRouterEnabledTest, TestDisabledByPolicy) { + disabled_profile.GetTestingPrefService()->SetManagedPref( + ::prefs::kEnableMediaRouter, std::make_unique<base::Value>(false)); + EXPECT_FALSE(MediaRouterEnabled(&disabled_profile)); + + disabled_profile.GetTestingPrefService()->SetManagedPref( + ::prefs::kEnableMediaRouter, std::make_unique<base::Value>(true)); + // Runtime changes are not supported. + EXPECT_FALSE(MediaRouterEnabled(&disabled_profile)); +} +#endif // defined(OS_ANDROID) || BUILDFLAG(ENABLE_EXTENSIONS) + } // namespace media_router
diff --git a/chrome/browser/media_galleries/fileapi/media_file_validator_browsertest.cc b/chrome/browser/media_galleries/fileapi/media_file_validator_browsertest.cc index 28e4938..082f309 100644 --- a/chrome/browser/media_galleries/fileapi/media_file_validator_browsertest.cc +++ b/chrome/browser/media_galleries/fileapi/media_file_validator_browsertest.cc
@@ -47,22 +47,18 @@ const int64_t kNoFileSize = -1; void HandleCheckFileResult(int64_t expected_size, - const base::Callback<void(bool success)>& callback, + base::OnceCallback<void(bool success)> callback, base::File::Error result, const base::File::Info& file_info) { if (result == base::File::FILE_OK) { if (!file_info.is_directory && expected_size != kNoFileSize && file_info.size == expected_size) { - callback.Run(true); - return; - } - } else { - if (expected_size == kNoFileSize) { - callback.Run(true); + std::move(callback).Run(true); return; } } - callback.Run(false); + + std::move(callback).Run(expected_size == kNoFileSize); } base::FilePath GetMediaTestDir() { @@ -165,10 +161,10 @@ content::GetIOThreadTaskRunner({})->PostTask( FROM_HERE, - base::BindOnce(&MediaFileValidatorTest::CheckFiles, - base::Unretained(this), true, - base::Bind(&MediaFileValidatorTest::OnTestFilesReady, - base::Unretained(this), expected_result))); + base::BindOnce( + &MediaFileValidatorTest::CheckFiles, base::Unretained(this), true, + base::BindOnce(&MediaFileValidatorTest::OnTestFilesReady, + base::Unretained(this), expected_result))); } void SetupFromFileBlocking(const std::string& filename, @@ -183,10 +179,11 @@ // |src_expected| indicates which one should exist. When complete, // |callback| is called with success/failure. void CheckFiles(bool src_expected, - const base::Callback<void(bool success)>& callback) { + base::OnceCallback<void(bool success)> callback) { CheckFile(move_src_, src_expected ? test_file_size_ : kNoFileSize, - base::Bind(&MediaFileValidatorTest::OnCheckFilesFirstResult, - base::Unretained(this), !src_expected, callback)); + base::BindOnce(&MediaFileValidatorTest::OnCheckFilesFirstResult, + base::Unretained(this), !src_expected, + std::move(callback))); } // Helper that checks a file has the |expected_size|, which may be @@ -194,24 +191,25 @@ // with success/failure. void CheckFile(storage::FileSystemURL url, int64_t expected_size, - const base::Callback<void(bool success)>& callback) { + base::OnceCallback<void(bool success)> callback) { operation_runner()->GetMetadata( url, storage::FileSystemOperation::GET_METADATA_FIELD_SIZE, - base::BindOnce(&HandleCheckFileResult, expected_size, callback)); + base::BindOnce(&HandleCheckFileResult, expected_size, + std::move(callback))); } // Helper that checks the result of |move_src_| lookup and then checks // |move_dest_| if all is as expected. void OnCheckFilesFirstResult(bool dest_expected, - const base::Callback<void(bool)>& callback, + base::OnceCallback<void(bool)> callback, bool src_result) { EXPECT_TRUE(src_result); if (!src_result) { - callback.Run(false); + std::move(callback).Run(false); return; } CheckFile(move_dest_, dest_expected ? test_file_size_ : kNoFileSize, - callback); + std::move(callback)); } // Assert |test_files_ready| and then do the actual test of moving @@ -232,8 +230,8 @@ else EXPECT_EQ(base::File::FILE_ERROR_SECURITY, result); CheckFiles(!expected_result, - base::Bind(&MediaFileValidatorTest::OnTestFilesCheckResult, - base::Unretained(this))); + base::BindOnce(&MediaFileValidatorTest::OnTestFilesCheckResult, + base::Unretained(this))); } // Check that the correct test file exists and then allow the main-thread
diff --git a/chrome/browser/nearby_sharing/sharesheet/nearby_share_action.cc b/chrome/browser/nearby_sharing/sharesheet/nearby_share_action.cc index 9ab2ba3..dfbe112 100644 --- a/chrome/browser/nearby_sharing/sharesheet/nearby_share_action.cc +++ b/chrome/browser/nearby_sharing/sharesheet/nearby_share_action.cc
@@ -16,9 +16,11 @@ #include "chrome/browser/nearby_sharing/file_attachment.h" #include "chrome/browser/nearby_sharing/nearby_sharing_service.h" #include "chrome/browser/nearby_sharing/nearby_sharing_service_factory.h" -#include "chrome/browser/nearby_sharing/sharesheet/nearby_share_web_view.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/sharesheet/sharesheet_types.h" +#include "chrome/browser/ui/browser_navigator.h" +#include "chrome/browser/ui/browser_navigator_params.h" +#include "chrome/browser/ui/scoped_tabbed_browser_displayer.h" #include "chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.h" #include "chrome/common/webui_url_constants.h" #include "chrome/grit/generated_resources.h" @@ -96,21 +98,22 @@ controller->SetSharesheetSize(size.width(), size.height()); auto* profile = controller->GetProfile(); - auto view = std::make_unique<NearbyShareWebView>(profile); + auto view = std::make_unique<views::WebView>(profile); // If this is not done, we don't see anything in our view. view->SetPreferredSize(size); - views::WebView* web_view = root_view->AddChildView(std::move(view)); + web_view_ = root_view->AddChildView(std::move(view)); + web_view_->GetWebContents()->SetDelegate(this); // TODO(vecore): Query this from the container view - web_view->holder()->SetCornerRadii(gfx::RoundedCornersF(kCornerRadius)); + web_view_->holder()->SetCornerRadii(gfx::RoundedCornersF(kCornerRadius)); // load chrome://nearby into the webview - web_view->LoadInitialURL(GURL(chrome::kChromeUINearbyShareURL)); + web_view_->LoadInitialURL(GURL(chrome::kChromeUINearbyShareURL)); // Without requesting focus, the sharesheet will launch in an unfocused state // which raises accessibility issues with the "Device name" input. - web_view->RequestFocus(); + web_view_->RequestFocus(); - auto* webui = web_view->GetWebContents()->GetWebUI(); + auto* webui = web_view_->GetWebContents()->GetWebUI(); DCHECK(webui != nullptr); nearby_ui_ = @@ -154,3 +157,24 @@ nearby_ui_ = nullptr; } } + +bool NearbyShareAction::HandleKeyboardEvent( + content::WebContents* source, + const content::NativeWebKeyboardEvent& event) { + return unhandled_keyboard_event_handler_.HandleKeyboardEvent( + event, web_view_->GetFocusManager()); +} + +void NearbyShareAction::WebContentsCreated( + content::WebContents* source_contents, + int opener_render_process_id, + int opener_render_frame_id, + const std::string& frame_name, + const GURL& target_url, + content::WebContents* new_contents) { + chrome::ScopedTabbedBrowserDisplayer displayer( + Profile::FromBrowserContext(web_view_->GetBrowserContext())); + NavigateParams nav_params(displayer.browser(), target_url, + ui::PageTransition::PAGE_TRANSITION_LINK); + Navigate(&nav_params); +}
diff --git a/chrome/browser/nearby_sharing/sharesheet/nearby_share_action.h b/chrome/browser/nearby_sharing/sharesheet/nearby_share_action.h index c29437b..7b5ad19 100644 --- a/chrome/browser/nearby_sharing/sharesheet/nearby_share_action.h +++ b/chrome/browser/nearby_sharing/sharesheet/nearby_share_action.h
@@ -7,9 +7,16 @@ #include "chrome/browser/sharesheet/share_action.h" #include "chrome/browser/ui/webui/nearby_share/nearby_share_dialog_ui.h" +#include "content/public/browser/web_contents_delegate.h" +#include "ui/views/controls/webview/unhandled_keyboard_event_handler.h" + +namespace views { +class WebView; +} // namespace views class NearbyShareAction : public sharesheet::ShareAction, - nearby_share::NearbyShareDialogUI::Observer { + nearby_share::NearbyShareDialogUI::Observer, + content::WebContentsDelegate { public: NearbyShareAction(); ~NearbyShareAction() override; @@ -29,9 +36,22 @@ // nearby_share::NearbyShareDialogUI::Observer: void OnClose() override; + // content::WebContentsDelegate: + bool HandleKeyboardEvent( + content::WebContents* source, + const content::NativeWebKeyboardEvent& event) override; + void WebContentsCreated(content::WebContents* source_contents, + int opener_render_process_id, + int opener_render_frame_id, + const std::string& frame_name, + const GURL& target_url, + content::WebContents* new_contents) override; + private: sharesheet::SharesheetController* controller_ = nullptr; nearby_share::NearbyShareDialogUI* nearby_ui_ = nullptr; + views::WebView* web_view_; + views::UnhandledKeyboardEventHandler unhandled_keyboard_event_handler_; }; #endif // CHROME_BROWSER_NEARBY_SHARING_SHARESHEET_NEARBY_SHARE_ACTION_H_
diff --git a/chrome/browser/nearby_sharing/sharesheet/nearby_share_web_view.cc b/chrome/browser/nearby_sharing/sharesheet/nearby_share_web_view.cc deleted file mode 100644 index 17291d1..0000000 --- a/chrome/browser/nearby_sharing/sharesheet/nearby_share_web_view.cc +++ /dev/null
@@ -1,27 +0,0 @@ -// 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/nearby_sharing/sharesheet/nearby_share_web_view.h" - -#include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/browser_navigator.h" -#include "chrome/browser/ui/browser_navigator_params.h" -#include "chrome/browser/ui/scoped_tabbed_browser_displayer.h" - -NearbyShareWebView::NearbyShareWebView(content::BrowserContext* browser_context) - : WebView(browser_context) {} - -void NearbyShareWebView::WebContentsCreated( - content::WebContents* source_contents, - int opener_render_process_id, - int opener_render_frame_id, - const std::string& frame_name, - const GURL& target_url, - content::WebContents* new_contents) { - chrome::ScopedTabbedBrowserDisplayer displayer( - Profile::FromBrowserContext(GetBrowserContext())); - NavigateParams nav_params(displayer.browser(), target_url, - ui::PageTransition::PAGE_TRANSITION_LINK); - Navigate(&nav_params); -}
diff --git a/chrome/browser/nearby_sharing/sharesheet/nearby_share_web_view.h b/chrome/browser/nearby_sharing/sharesheet/nearby_share_web_view.h deleted file mode 100644 index 70768cb..0000000 --- a/chrome/browser/nearby_sharing/sharesheet/nearby_share_web_view.h +++ /dev/null
@@ -1,32 +0,0 @@ -// 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_NEARBY_SHARING_SHARESHEET_NEARBY_SHARE_WEB_VIEW_H_ -#define CHROME_BROWSER_NEARBY_SHARING_SHARESHEET_NEARBY_SHARE_WEB_VIEW_H_ - -#include "ui/views/controls/webview/webview.h" - -namespace content { -class BrowserContext; -class WebContents; -} // namespace content - -// NearbyShareWebView is used in place of the general views::WebView when -// creating the UI for the sharesheet action so that we can handle navigation -// to open a new tab when a link is clicked. -class NearbyShareWebView : public views::WebView { - public: - explicit NearbyShareWebView(content::BrowserContext* browser_context); - ~NearbyShareWebView() override = default; - - // content::WebContentsDelegate: - void WebContentsCreated(content::WebContents* source_contents, - int opener_render_process_id, - int opener_render_frame_id, - const std::string& frame_name, - const GURL& target_url, - content::WebContents* new_contents) override; -}; - -#endif // CHROME_BROWSER_NEARBY_SHARING_SHARESHEET_NEARBY_SHARE_WEB_VIEW_H_
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc b/chrome/browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc index fdb5e5d..457b3ba 100644 --- a/chrome/browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc +++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc
@@ -483,7 +483,6 @@ PushHintsComponentAndWaitForCompletion(); RegisterWithKeyedService(); - ukm::TestAutoSetUkmRecorder ukm_recorder; base::HistogramTester histogram_tester; ui_test_utils::NavigateToURL(browser(), url_that_redirects_to_hints()); @@ -505,7 +504,6 @@ PushHintsComponentAndWaitForCompletion(); RegisterWithKeyedService(); - ukm::TestAutoSetUkmRecorder ukm_recorder; base::HistogramTester histogram_tester; ui_test_utils::NavigateToURL(browser(), GURL("https://nohints.com/")); @@ -526,6 +524,49 @@ 1); } +IN_PROC_BROWSER_TEST_F(OptimizationGuideKeyedServiceBrowserTest, + CheckForBlocklistFilter) { + PushHintsComponentAndWaitForCompletion(); + + OptimizationGuideKeyedService* ogks = + OptimizationGuideKeyedServiceFactory::GetForProfile(browser()->profile()); + + // Register an optimization type with an optimization filter. + ogks->RegisterOptimizationTypes({optimization_guide::proto::FAST_HOST_HINTS}); + // Wait until filter is loaded. + base::RunLoop().RunUntilIdle(); + + base::HistogramTester histogram_tester; + + EXPECT_EQ(optimization_guide::OptimizationGuideDecision::kFalse, + ogks->CanApplyOptimization( + GURL("https://blockedhost.com/whatever"), + optimization_guide::proto::FAST_HOST_HINTS, nullptr)); + histogram_tester.ExpectUniqueSample( + "OptimizationGuide.ApplyDecision.FastHostHints", + static_cast<int>(optimization_guide::OptimizationTypeDecision:: + kNotAllowedByOptimizationFilter), + 1); + + // Register another type with optimization filter. + ogks->RegisterOptimizationTypes( + {optimization_guide::proto::LITE_PAGE_REDIRECT}); + // Wait until filter is loaded. + base::RunLoop().RunUntilIdle(); + + // The previously loaded filter should still be loaded and give the same + // result. + EXPECT_EQ(optimization_guide::OptimizationGuideDecision::kFalse, + ogks->CanApplyOptimization( + GURL("https://blockedhost.com/whatever"), + optimization_guide::proto::FAST_HOST_HINTS, nullptr)); + histogram_tester.ExpectUniqueSample( + "OptimizationGuide.ApplyDecision.FastHostHints", + static_cast<int>(optimization_guide::OptimizationTypeDecision:: + kNotAllowedByOptimizationFilter), + 2); +} + class OptimizationGuideKeyedServiceDataSaverUserWithInfobarShownTest : public OptimizationGuideKeyedServiceBrowserTest { public:
diff --git a/chrome/browser/policy/messaging_layer/upload/record_handler_impl.cc b/chrome/browser/policy/messaging_layer/upload/record_handler_impl.cc index 64af268..c2cb82b9 100644 --- a/chrome/browser/policy/messaging_layer/upload/record_handler_impl.cc +++ b/chrome/browser/policy/messaging_layer/upload/record_handler_impl.cc
@@ -33,6 +33,27 @@ #include "content/public/browser/browser_thread.h" namespace reporting { +namespace { + +// Priority could come back as an int or as a std::string, this function handles +// both situations. +base::Optional<Priority> GetPriorityProtoFromSequencingInformationValue( + const base::Value& sequencing_information) { + const base::Optional<int> int_priority_result = + sequencing_information.FindIntKey("priority"); + if (int_priority_result.has_value()) { + return Priority(int_priority_result.value()); + } + + const std::string* str_priority_result = + sequencing_information.FindStringKey("priority"); + Priority priority; + if (!Priority_Parse(*str_priority_result, &priority)) { + return base::nullopt; + } + return priority; +} +} // namespace // ReportUploader handles enqueuing events on the |report_queue_|, // and uploading those events with the |client_|. @@ -344,14 +365,15 @@ const base::Value& value) { const std::string* sequencing_id = value.FindStringKey("sequencingId"); const std::string* generation_id = value.FindStringKey("generationId"); - const auto priority = value.FindIntKey("priority"); + const auto priority_result = + GetPriorityProtoFromSequencingInformationValue(value); // If any of the previous values don't exist, or are malformed, return error. int64_t seq_id; int64_t gen_id; if (!sequencing_id || sequencing_id->empty() || !generation_id || - generation_id->empty() || !priority.has_value() || - !Priority_IsValid(priority.value()) || + generation_id->empty() || !priority_result.has_value() || + !Priority_IsValid(priority_result.value()) || !base::StringToInt64(*sequencing_id, &seq_id) || !base::StringToInt64(*generation_id, &gen_id)) { return Status(error::INVALID_ARGUMENT, @@ -363,7 +385,7 @@ SequencingInformation proto; proto.set_sequencing_id(seq_id); proto.set_generation_id(gen_id); - proto.set_priority(Priority(priority.value())); + proto.set_priority(Priority(priority_result.value())); return proto; }
diff --git a/chrome/browser/policy/messaging_layer/upload/record_handler_impl_unittest.cc b/chrome/browser/policy/messaging_layer/upload/record_handler_impl_unittest.cc index 9ad1094..42f6580 100644 --- a/chrome/browser/policy/messaging_layer/upload/record_handler_impl_unittest.cc +++ b/chrome/browser/policy/messaging_layer/upload/record_handler_impl_unittest.cc
@@ -86,8 +86,8 @@ // Helper function for retrieving and processing the SequencingInformation from // a request. -void RetrieveFinalSequencingInforamation(const base::Value& request, - base::Value& sequencing_info) { +void RetrieveFinalSequencingInformation(const base::Value& request, + base::Value& sequencing_info) { ASSERT_TRUE(request.is_dict()); // Retrieve and process sequencing information @@ -98,10 +98,22 @@ const auto* seq_info = encrypted_record_list->GetList().rbegin()->FindDictKey( "sequencingInformation"); ASSERT_TRUE(seq_info != nullptr); - ASSERT_TRUE(seq_info->FindStringKey("sequencingId")); - ASSERT_TRUE(seq_info->FindStringKey("generationId")); + ASSERT_TRUE(!seq_info->FindStringKey("sequencingId")->empty()); + ASSERT_TRUE(!seq_info->FindStringKey("generationId")->empty()); ASSERT_TRUE(seq_info->FindIntKey("priority")); + sequencing_info.MergeDictionary(seq_info); + // Set half of sequencing information to return a string instead of an int for + // priority. + int64_t sequencing_id; + ASSERT_TRUE(base::StringToInt64( + *sequencing_info.FindStringKey("sequencingId"), &sequencing_id)); + if (sequencing_id % 2) { + const auto int_result = sequencing_info.FindIntKey("priority"); + ASSERT_TRUE(int_result.has_value()); + sequencing_info.RemoveKey("priority"); + sequencing_info.SetStringKey("priority", Priority_Name(int_result.value())); + } } base::Optional<base::Value> BuildEncryptionSettingsFromRequest( @@ -132,7 +144,7 @@ void SucceedResponseFromRequest(const base::Value& request, base::Value& response) { base::Value seq_info{base::Value::Type::DICTIONARY}; - RetrieveFinalSequencingInforamation(request, seq_info); + RetrieveFinalSequencingInformation(request, seq_info); response.SetPath("lastSucceedUploadedRecord", std::move(seq_info)); // If attach_encryption_settings it true, process that. @@ -149,21 +161,17 @@ void FailedResponseFromRequest(const base::Value& request, base::Value& response) { base::Value seq_info{base::Value::Type::DICTIONARY}; - RetrieveFinalSequencingInforamation(request, seq_info); + RetrieveFinalSequencingInformation(request, seq_info); - // |seq_info| has been built by RetrieveFinalSequencingInforamation and is - // guaranteed to have these keys. + response.SetPath("lastSucceedUploadedRecord", seq_info.Clone()); + // The lastSucceedUploadedRecord should be the record before the one indicated + // in seq_info. |seq_info| has been built by + // RetrieveFinalSequencingInforamation and is guaranteed to have this key. int64_t sequencing_id; ASSERT_TRUE(base::StringToInt64(*seq_info.FindStringKey("sequencingId"), &sequencing_id)); - // The lastSucceedUploadedRecord should be the record before the one - // indicated in seq_info. response.SetStringPath("lastSucceedUploadedRecord.sequencingId", base::NumberToString(sequencing_id - 1)); - response.SetStringPath("lastSucceedUploadedRecord.generationId", - *seq_info.FindStringKey("generationId")); - response.SetIntPath("lastSucceedUploadedRecord.priority", - seq_info.FindIntKey("priority").value()); // The firstFailedUploadedRecord.failedUploadedRecord should be the one // indicated in seq_info.
diff --git a/chrome/browser/prefetch/no_state_prefetch/prerender_unittest.cc b/chrome/browser/prefetch/no_state_prefetch/prerender_unittest.cc index 92c664b..a600c09 100644 --- a/chrome/browser/prefetch/no_state_prefetch/prerender_unittest.cc +++ b/chrome/browser/prefetch/no_state_prefetch/prerender_unittest.cc
@@ -538,6 +538,20 @@ url, nullptr, gfx::Size())); } +// Verify that link-rel:next URLs are not prefetched. +TEST_F(PrerenderTest, LinkRelNextWithNSPDisabled) { + GURL url("http://www.notgoogle.com/"); + prerender_manager()->CreateNextPrerenderContents( + url, url::Origin::Create(GURL("www.notgoogle.com")), ORIGIN_LINK_REL_NEXT, + FINAL_STATUS_PROFILE_DESTROYED); + EXPECT_EQ(nullptr, + prerender_manager()->AddPrerenderWithPreconnectFallbackForTesting( + ORIGIN_LINK_REL_NEXT, url, + url::Origin::Create(GURL("www.notgoogle.com")))); + histogram_tester().ExpectUniqueSample( + "Prerender.FinalStatus", FINAL_STATUS_LINK_REL_NEXT_NOT_ALLOWED, 1); +} + TEST_F(PrerenderTest, PredictorPrefetchHoldbackOffNonPredictorReferrer) { GURL url("http://www.notgoogle.com/"); base::test::ScopedFeatureList scoped_feature_list;
diff --git a/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper_browsertest.mm b/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper_browsertest.mm index 1457565..6af002a 100644 --- a/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper_browsertest.mm +++ b/chrome/browser/renderer_host/chrome_render_widget_host_view_mac_history_swiper_browsertest.mm
@@ -744,7 +744,7 @@ // Initial movements are vertical, and scroll the iframe. Subsequent movements // are horizontal, and should not trigger history swiping. IN_PROC_BROWSER_TEST_F(ChromeRenderWidgetHostViewMacHistorySwiperTest, - TestIframeHistorySwiping) { + DISABLED_TestIframeHistorySwiping) { if (!IsHistorySwipingSupported()) return;
diff --git a/chrome/browser/resources/tab_search/tab_search_resources.grd b/chrome/browser/resources/tab_search/tab_search_resources.grd deleted file mode 100644 index 8c32d63..0000000 --- a/chrome/browser/resources/tab_search/tab_search_resources.grd +++ /dev/null
@@ -1,48 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<grit latest_public_release="0" current_release="1" output_all_resource_defines="false"> - <outputs> - <output filename="grit/tab_search_resources.h" type="rc_header"> - <emit emit_type='prepend'></emit> - </output> - <output filename="grit/tab_search_resources_map.cc" - type="resource_file_map_source" /> - <output filename="grit/tab_search_resources_map.h" - type="resource_map_header" /> - <output filename="tab_search_resources.pak" type="data_package" /> - </outputs> - <release seq="1"> - <includes> - <include name="IDR_APP_JS" - file="${root_gen_dir}/chrome/browser/resources/tab_search/app.js" - type="BINDATA" - use_base_dir="false" /> - <include name="IDR_FUSE_JS" - file="../../../../third_party/fusejs/dist/fuse.basic.esm.min.js" - type="BINDATA"/> - <include name="IDR_FUZZY_SEARCH_JS" - file="fuzzy_search.js" - type="BINDATA"/> - <include name="IDR_TAB_DATA_JS" - file="tab_data.js" - type="BINDATA" /> - <include name="IDR_TAB_SEARCH_API_PROXY_JS" - file="tab_search_api_proxy.js" - type="BINDATA" /> - <include name="IDR_TAB_SEARCH_ITEM_JS" - file="${root_gen_dir}/chrome/browser/resources/tab_search/tab_search_item.js" - type="BINDATA" - use_base_dir="false"/> - <include name="IDR_TAB_SEARCH_MOJOM_WEBUI_JS" - file="${root_gen_dir}/mojom-webui/chrome/browser/ui/webui/tab_search/tab_search.mojom-webui.js" - type="BINDATA" - use_base_dir="false"/> - <include name="IDR_TAB_SEARCH_PAGE_HTML" - file="tab_search_page.html" - type="BINDATA" /> - <include name="IDR_TAB_SEARCH_SEARCH_FIELD_JS" - file="${root_gen_dir}/chrome/browser/resources/tab_search/tab_search_search_field.js" - type="BINDATA" - use_base_dir="false" /> - </includes> - </release> -</grit>
diff --git a/chrome/browser/subresource_redirect/https_image_compression_infobar_decider.cc b/chrome/browser/subresource_redirect/https_image_compression_infobar_decider.cc index 10b1d83..bc875d4 100644 --- a/chrome/browser/subresource_redirect/https_image_compression_infobar_decider.cc +++ b/chrome/browser/subresource_redirect/https_image_compression_infobar_decider.cc
@@ -31,9 +31,9 @@ // The time used to compare and identify recent LiteMode users. Users who // enabled LiteMode before this time are treated as non-recent and the one-time -// https image compression InfoBar is shown for them. Set approximate as M85 -// release date, which is the target for https image compression feature. -constexpr char kRecentLiteModeUserEnableTime[] = "2020-08-25T00:00:01Z"; +// https image compression InfoBar is shown for them. Set approximate as M88 +// release date, which is the target for https image compression V2 feature. +constexpr char kRecentLiteModeUserEnableTime[] = "2021-01-19T00:00:01Z"; } // namespace
diff --git a/chrome/browser/subresource_redirect/https_image_compression_infobar_decider_unittest.cc b/chrome/browser/subresource_redirect/https_image_compression_infobar_decider_unittest.cc index 0c30b0f..9e1e8d3a 100644 --- a/chrome/browser/subresource_redirect/https_image_compression_infobar_decider_unittest.cc +++ b/chrome/browser/subresource_redirect/https_image_compression_infobar_decider_unittest.cc
@@ -130,7 +130,7 @@ } TEST_F(HttpsImageCompressionInfoBarDeciderPrefTest, TestRecentLiteModeUser) { - SetLiteModeLastEnableDate("2020-12-01T00:00:01Z"); + SetLiteModeLastEnableDate("2021-12-01T00:00:01Z"); HttpsImageCompressionInfoBarDecider* decider = GetDeciderWithDRPEnabled(true); EXPECT_FALSE(decider->NeedToShowInfoBar()); @@ -143,7 +143,7 @@ TEST_F(HttpsImageCompressionInfoBarDeciderPrefTest, TestNonRecentLiteModeUser) { HttpsImageCompressionInfoBarDecider* decider = GetDeciderWithDRPEnabled(true); - SetLiteModeLastEnableDate("2020-01-01T00:00:01Z"); + SetLiteModeLastEnableDate("2021-01-01T00:00:01Z"); EXPECT_TRUE(decider->NeedToShowInfoBar()); decider->SetUserHasSeenInfoBar(); EXPECT_FALSE(decider->NeedToShowInfoBar());
diff --git a/chrome/browser/sync/device_info_sync_service_factory.cc b/chrome/browser/sync/device_info_sync_service_factory.cc index 2eb956e..56fcb64 100644 --- a/chrome/browser/sync/device_info_sync_service_factory.cc +++ b/chrome/browser/sync/device_info_sync_service_factory.cc
@@ -72,22 +72,28 @@ } // syncer::DeviceInfoSyncClient: - std::string GetFCMRegistrationToken() const override { + base::Optional<std::string> GetFCMRegistrationToken() const override { syncer::SyncInvalidationsService* service = SyncInvalidationsServiceFactory::GetForProfile(profile_); if (service) { return service->GetFCMRegistrationToken(); } + // If the service is not enabled, then the registration token must be empty, + // not unknown (base::nullopt). This is needed to reset previous token if + // the invalidations have been turned off. return std::string(); } // syncer::DeviceInfoSyncClient: - syncer::ModelTypeSet GetInterestedDataTypes() const override { + base::Optional<syncer::ModelTypeSet> GetInterestedDataTypes() const override { syncer::SyncInvalidationsService* service = SyncInvalidationsServiceFactory::GetForProfile(profile_); if (service) { return service->GetInterestedDataTypes(); } + // If the service is not enabled, then the list of types must be empty, not + // unknown (base::nullopt). This is needed to reset previous types if the + // invalidations have been turned off. return syncer::ModelTypeSet(); }
diff --git a/chrome/browser/sync/test/integration/single_client_device_info_sync_test.cc b/chrome/browser/sync/test/integration/single_client_device_info_sync_test.cc index d5347f0..9af51e72 100644 --- a/chrome/browser/sync/test/integration/single_client_device_info_sync_test.cc +++ b/chrome/browser/sync/test/integration/single_client_device_info_sync_test.cc
@@ -5,6 +5,7 @@ #include <string> #include "base/strings/stringprintf.h" +#include "base/test/bind.h" #include "build/build_config.h" #include "build/chromeos_buildflags.h" #include "chrome/browser/sync/test/integration/device_info_helper.h" @@ -15,6 +16,7 @@ #include "components/sync/base/sync_prefs.h" #include "components/sync/base/time.h" #include "components/sync/driver/sync_driver_switches.h" +#include "components/sync/invalidations/switches.h" #include "components/sync/protocol/proto_value_conversions.h" #include "components/sync/protocol/sync.pb.h" #include "components/sync/test/fake_server/fake_server.h" @@ -201,4 +203,46 @@ EXPECT_FALSE(message.commit().config_params().single_client()); } +IN_PROC_BROWSER_TEST_F(SingleClientDeviceInfoSyncTest, + PRE_ShouldNotSendDeviceInfoAfterBrowserRestart) { + ASSERT_TRUE(SetupSync()); + EXPECT_TRUE( + ServerDeviceInfoMatchChecker( + GetFakeServer(), ElementsAre(HasCacheGuid(GetLocalCacheGuid()))) + .Wait()); +} + +IN_PROC_BROWSER_TEST_F(SingleClientDeviceInfoSyncTest, + ShouldNotSendDeviceInfoAfterBrowserRestart) { + const std::vector<sync_pb::SyncEntity> entities_before = + fake_server_->GetSyncEntitiesByModelType(syncer::DEVICE_INFO); + ASSERT_TRUE(SetupClients()); +#if BUILDFLAG(IS_CHROMEOS_ASH) + // signin::SetRefreshTokenForPrimaryAccount() is needed on ChromeOS in order + // to get a non-empty refresh token on startup. + GetClient(0)->SignInPrimaryAccount(); +#endif // BUILDFLAG(IS_CHROMEOS_ASH) + ASSERT_TRUE(GetClient(0)->AwaitEngineInitialization()); + ASSERT_TRUE(GetClient(0)->AwaitSyncSetupCompletion()); + + bool has_local_changes = false; + base::RunLoop run_loop; + GetSyncService(0)->HasUnsyncedItemsForTest( + base::BindLambdaForTesting([&has_local_changes, &run_loop](bool result) { + has_local_changes = result; + run_loop.Quit(); + })); + run_loop.Run(); + + const std::vector<sync_pb::SyncEntity> entities_after = + fake_server_->GetSyncEntitiesByModelType(syncer::DEVICE_INFO); + ASSERT_EQ(1U, entities_before.size()); + ASSERT_EQ(1U, entities_after.size()); + + // Check that there are no local changes and nothing has been committed to the + // server. + EXPECT_FALSE(has_local_changes); + EXPECT_EQ(entities_before.front().mtime(), entities_after.front().mtime()); +} + } // namespace
diff --git a/chrome/browser/sync/test/integration/single_client_sync_invalidations_test.cc b/chrome/browser/sync/test/integration/single_client_sync_invalidations_test.cc index 55de306..f048331 100644 --- a/chrome/browser/sync/test/integration/single_client_sync_invalidations_test.cc +++ b/chrome/browser/sync/test/integration/single_client_sync_invalidations_test.cc
@@ -100,8 +100,9 @@ syncer::SyncInvalidationsService* sync_invalidations_service = SyncInvalidationsServiceFactory::GetForProfile(GetProfile(0)); ASSERT_THAT(sync_invalidations_service, NotNull()); - syncer::ModelTypeSet interested_data_types = - sync_invalidations_service->GetInterestedDataTypes(); + ASSERT_TRUE(sync_invalidations_service->GetInterestedDataTypes()); + const syncer::ModelTypeSet interested_data_types = + *sync_invalidations_service->GetInterestedDataTypes(); // Check that some "standard" data types are included. EXPECT_TRUE( @@ -146,9 +147,12 @@ syncer::SyncInvalidationsService* sync_invalidations_service = SyncInvalidationsServiceFactory::GetForProfile(GetProfile(0)); ASSERT_THAT(sync_invalidations_service, NotNull()); - syncer::ModelTypeSet interested_data_types = - sync_invalidations_service->GetInterestedDataTypes(); - std::string fcm_token = sync_invalidations_service->GetFCMRegistrationToken(); + ASSERT_TRUE(sync_invalidations_service->GetInterestedDataTypes()); + ASSERT_TRUE(sync_invalidations_service->GetFCMRegistrationToken()); + const syncer::ModelTypeSet interested_data_types = + *sync_invalidations_service->GetInterestedDataTypes(); + const std::string fcm_token = + *sync_invalidations_service->GetFCMRegistrationToken(); // Check that some "standard" data types are included. EXPECT_TRUE( @@ -205,9 +209,12 @@ syncer::SyncInvalidationsService* sync_invalidations_service = SyncInvalidationsServiceFactory::GetForProfile(GetProfile(0)); ASSERT_THAT(sync_invalidations_service, NotNull()); - syncer::ModelTypeSet interested_data_types = - sync_invalidations_service->GetInterestedDataTypes(); - std::string fcm_token = sync_invalidations_service->GetFCMRegistrationToken(); + ASSERT_TRUE(sync_invalidations_service->GetInterestedDataTypes()); + ASSERT_TRUE(sync_invalidations_service->GetFCMRegistrationToken()); + const syncer::ModelTypeSet interested_data_types = + *sync_invalidations_service->GetInterestedDataTypes(); + const std::string fcm_token = + *sync_invalidations_service->GetFCMRegistrationToken(); // Check that some "standard" data types are included. EXPECT_TRUE( @@ -274,24 +281,30 @@ // The local device should eventually be committed to the server. The FCM // token should be present in device info. - std::string old_token = - SyncInvalidationsServiceFactory::GetForProfile(GetProfile(0)) - ->GetFCMRegistrationToken(); + ASSERT_TRUE(SyncInvalidationsServiceFactory::GetForProfile(GetProfile(0)) + ->GetFCMRegistrationToken()); + const std::string old_token = + *SyncInvalidationsServiceFactory::GetForProfile(GetProfile(0)) + ->GetFCMRegistrationToken(); EXPECT_TRUE(ServerDeviceInfoMatchChecker( GetFakeServer(), ElementsAre(HasInstanceIdToken(old_token))) .Wait()); // Sign out. The FCM token should be cleared. GetClient(0)->SignOutPrimaryAccount(); + ASSERT_TRUE(SyncInvalidationsServiceFactory::GetForProfile(GetProfile(0)) + ->GetFCMRegistrationToken()); EXPECT_TRUE(SyncInvalidationsServiceFactory::GetForProfile(GetProfile(0)) ->GetFCMRegistrationToken() - .empty()); + ->empty()); // Sign in again. ASSERT_TRUE(GetClient(0)->SignInPrimaryAccount()); - std::string new_token = - SyncInvalidationsServiceFactory::GetForProfile(GetProfile(0)) - ->GetFCMRegistrationToken(); + ASSERT_TRUE(SyncInvalidationsServiceFactory::GetForProfile(GetProfile(0)) + ->GetFCMRegistrationToken()); + const std::string new_token = + *SyncInvalidationsServiceFactory::GetForProfile(GetProfile(0)) + ->GetFCMRegistrationToken(); EXPECT_NE(new_token, old_token); EXPECT_FALSE(new_token.empty()); // New device info should eventually be committed to the server (but the old
diff --git a/chrome/browser/themes/theme_syncable_service.cc b/chrome/browser/themes/theme_syncable_service.cc index 757c3da..e95832e 100644 --- a/chrome/browser/themes/theme_syncable_service.cc +++ b/chrome/browser/themes/theme_syncable_service.cc
@@ -43,7 +43,6 @@ : profile_(profile), theme_service_(theme_service), use_system_theme_by_default_(false) { - DCHECK(profile_); DCHECK(theme_service_); } @@ -61,6 +60,22 @@ } } +void ThemeSyncableService::AddObserver( + ThemeSyncableService::Observer* observer) { + observer_list_.AddObserver(observer); + if (sync_processor_) + observer->OnThemeSyncStarted(); +} + +void ThemeSyncableService::RemoveObserver( + ThemeSyncableService::Observer* observer) { + observer_list_.RemoveObserver(observer); +} + +void ThemeSyncableService::NotifyOnSyncStartedForTesting() { + NotifyOnSyncStarted(); +} + void ThemeSyncableService::WaitUntilReadyToSync(base::OnceClosure done) { extensions::ExtensionSystem::Get(profile_)->ready().Post(FROM_HERE, std::move(done)); @@ -91,6 +106,7 @@ if (!GetThemeSpecificsFromCurrentTheme(¤t_specifics)) { // Current theme is unsyncable - don't overwrite from sync data, and don't // save the unsyncable theme to sync data. + NotifyOnSyncStarted(); return base::nullopt; } @@ -103,13 +119,17 @@ if (!HasNonDefaultTheme(current_specifics) || HasNonDefaultTheme(sync_data->GetSpecifics().theme())) { MaybeSetTheme(current_specifics, *sync_data); + NotifyOnSyncStarted(); return base::nullopt; } } } // No theme specifics are found. Create one according to current theme. - return ProcessNewTheme(syncer::SyncChange::ACTION_ADD, current_specifics); + base::Optional<syncer::ModelError> error = + ProcessNewTheme(syncer::SyncChange::ACTION_ADD, current_specifics); + NotifyOnSyncStarted(); + return error; } void ThemeSyncableService::StopSyncing(syncer::ModelType type) { @@ -369,3 +389,8 @@ return sync_processor_->ProcessSyncChanges(FROM_HERE, changes); } + +void ThemeSyncableService::NotifyOnSyncStarted() { + for (Observer& observer : observer_list_) + observer.OnThemeSyncStarted(); +}
diff --git a/chrome/browser/themes/theme_syncable_service.h b/chrome/browser/themes/theme_syncable_service.h index 083d8c1..a43d4d7 100644 --- a/chrome/browser/themes/theme_syncable_service.h +++ b/chrome/browser/themes/theme_syncable_service.h
@@ -9,6 +9,8 @@ #include "base/gtest_prod_util.h" #include "base/macros.h" +#include "base/observer_list.h" +#include "base/observer_list_types.h" #include "base/threading/thread_checker.h" #include "components/sync/model/sync_change.h" #include "components/sync/model/sync_data.h" @@ -26,8 +28,16 @@ class ThemeSyncableService : public syncer::SyncableService { public: - ThemeSyncableService(Profile* profile, // Same profile used by theme_service. - ThemeService* theme_service); + class Observer : public base::CheckedObserver { + public: + // Called when theme sync gets started. Observers that register after theme + // sync gets started are called right away when they register. + virtual void OnThemeSyncStarted() = 0; + }; + + // `profile` may be nullptr in tests (and is the one used by theme_service, + // otherwise). + ThemeSyncableService(Profile* profile, ThemeService* theme_service); ~ThemeSyncableService() override; static syncer::ModelType model_type() { return syncer::THEMES; } @@ -35,6 +45,10 @@ // Called by ThemeService when user changes theme. void OnThemeChange(); + void AddObserver(Observer* observer); + void RemoveObserver(Observer* observer); + void NotifyOnSyncStartedForTesting(); + // syncer::SyncableService implementation. void WaitUntilReadyToSync(base::OnceClosure done) override; base::Optional<syncer::ModelError> MergeDataAndStartSyncing( @@ -81,9 +95,13 @@ syncer::SyncChange::SyncChangeType change_type, const sync_pb::ThemeSpecifics& theme_specifics); + void NotifyOnSyncStarted(); + Profile* const profile_; ThemeService* const theme_service_; + base::ObserverList<Observer> observer_list_; + std::unique_ptr<syncer::SyncChangeProcessor> sync_processor_; std::unique_ptr<syncer::SyncErrorFactory> sync_error_handler_;
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index 549167e..e6a3c9ab 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn
@@ -1570,6 +1570,7 @@ "//device/bluetooth/strings:strings_grit", "//device/fido", "//ppapi/c", + "//services/device/public/cpp/hid", "//services/device/public/mojom", "//services/metrics/public/cpp:metrics_cpp", "//services/preferences/public/mojom:mojom", @@ -2954,6 +2955,8 @@ "signin/dice_web_signin_interceptor_delegate.h", "views/profiles/dice_web_signin_interception_bubble_view.cc", "views/profiles/dice_web_signin_interception_bubble_view.h", + "views/profiles/profile_customization_bubble_sync_controller.cc", + "views/profiles/profile_customization_bubble_sync_controller.h", "views/profiles/profile_customization_bubble_view.cc", "views/profiles/profile_customization_bubble_view.h", "webui/signin/dice_turn_sync_on_helper.cc",
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.cc b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.cc index 9f3fc9f..b783985f1 100644 --- a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.cc +++ b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.cc
@@ -90,9 +90,12 @@ // the first time that holding space became available, this will no-op. holding_space_prefs::MarkTimeOfFirstAvailability(profile_->GetPrefs()); + ProfileManager* const profile_manager = GetProfileManager(); + if (!profile_manager) // May be `nullptr` in tests. + return; + // The associated profile may not be ready yet. If it is, we can immediately // proceed with profile dependent initialization. - ProfileManager* const profile_manager = GetProfileManager(); if (profile_manager->IsValidProfile(profile)) { OnProfileReady(); return; @@ -103,9 +106,10 @@ } HoldingSpaceKeyedService::~HoldingSpaceKeyedService() { - if (HoldingSpaceController::Get()) { - // For BrowserWithTestWindowTest that releases profile and its keyed - // services before ash Shell. + if (chromeos::PowerManagerClient::Get()) + chromeos::PowerManagerClient::Get()->RemoveObserver(this); + + if (HoldingSpaceController::Get()) { // May be `nullptr` in tests. HoldingSpaceController::Get()->RegisterClientAndModelForUser( account_id_, /*client=*/nullptr, /*model=*/nullptr); } @@ -286,12 +290,14 @@ void HoldingSpaceKeyedService::OnProfileReady() { // Observe suspend status - the delegates will be shutdown during suspend. if (chromeos::PowerManagerClient::Get()) - power_manager_observer_.Observe(chromeos::PowerManagerClient::Get()); + chromeos::PowerManagerClient::Get()->AddObserver(this); InitializeDelegates(); - HoldingSpaceController::Get()->RegisterClientAndModelForUser( - account_id_, &holding_space_client_, &holding_space_model_); + if (HoldingSpaceController::Get()) { // May be `nullptr` in tests. + HoldingSpaceController::Get()->RegisterClientAndModelForUser( + account_id_, &holding_space_client_, &holding_space_model_); + } } void HoldingSpaceKeyedService::SuspendImminent(
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.h b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.h index 4d1eb2a..e0130a8 100644 --- a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.h +++ b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service.h
@@ -143,10 +143,6 @@ base::ScopedObservation<ProfileManager, ProfileManagerObserver> profile_manager_observer_{this}; - base::ScopedObservation<chromeos::PowerManagerClient, - chromeos::PowerManagerClient::Observer> - power_manager_observer_{this}; - base::WeakPtrFactory<HoldingSpaceKeyedService> weak_factory_{this}; };
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_factory.cc b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_factory.cc index 9ea5631..4479580 100644 --- a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_factory.cc +++ b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_factory.cc
@@ -54,11 +54,6 @@ return new HoldingSpaceKeyedService(profile, user->GetAccountId()); } -bool HoldingSpaceKeyedServiceFactory::ServiceIsCreatedWithBrowserContext() - const { - return true; -} - void HoldingSpaceKeyedServiceFactory::RegisterProfilePrefs( user_prefs::PrefRegistrySyncable* registry) { HoldingSpaceKeyedService::RegisterProfilePrefs(registry);
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_factory.h b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_factory.h index beb004d6..ac91afa 100644 --- a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_factory.h +++ b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_factory.h
@@ -28,7 +28,6 @@ // BrowserContextKeyedServiceFactory: KeyedService* BuildServiceInstanceFor( content::BrowserContext* context) const override; - bool ServiceIsCreatedWithBrowserContext() const override; void RegisterProfilePrefs( user_prefs::PrefRegistrySyncable* registry) override;
diff --git a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_unittest.cc b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_unittest.cc index bd6e27ea..56b2ae5 100644 --- a/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_unittest.cc +++ b/chrome/browser/ui/ash/holding_space/holding_space_keyed_service_unittest.cc
@@ -1359,6 +1359,8 @@ TEST_F(HoldingSpaceKeyedServiceTest, AddDownloadItem) { TestingProfile* profile = GetProfile(); + HoldingSpaceModelAttachedWaiter(profile).Wait(); + // Create a test downloads mount point. std::unique_ptr<ScopedTestMountPoint> downloads_mount = ScopedTestMountPoint::CreateAndMountDownloads(profile);
diff --git a/chrome/browser/ui/ash/system_tray_client.cc b/chrome/browser/ui/ash/system_tray_client.cc index 76098303..642e2eb6 100644 --- a/chrome/browser/ui/ash/system_tray_client.cc +++ b/chrome/browser/ui/ash/system_tray_client.cc
@@ -75,8 +75,14 @@ ProfileManager::GetActiveUserProfile(), sub_page); } -// Returns the severity of a pending Chrome / Chrome OS update. -ash::UpdateSeverity GetUpdateSeverity(UpgradeDetector* detector) { +// Returns the severity of a pending update. +ash::UpdateSeverity GetUpdateSeverity(ash::UpdateType update_type, + UpgradeDetector* detector) { + // Lacros is always "low", which is the same severity OS updates start with. + if (update_type == ash::UpdateType::kLacros) + return ash::UpdateSeverity::kLow; + + // OS updates use UpgradeDetector's severity mapping. switch (detector->upgrade_notification_stage()) { case UpgradeDetector::UPGRADE_ANNOYANCE_NONE: return ash::UpdateSeverity::kNone; @@ -89,11 +95,8 @@ case UpgradeDetector::UPGRADE_ANNOYANCE_HIGH: return ash::UpdateSeverity::kHigh; case UpgradeDetector::UPGRADE_ANNOYANCE_CRITICAL: - break; + return ash::UpdateSeverity::kCritical; } - DCHECK_EQ(detector->upgrade_notification_stage(), - UpgradeDetector::UPGRADE_ANNOYANCE_CRITICAL); - return ash::UpdateSeverity::kCritical; } const chromeos::NetworkState* GetNetworkState(const std::string& network_id) { @@ -124,7 +127,7 @@ // If an upgrade is available at startup then tell ash about it. if (UpgradeDetector::GetInstance()->notify_upgrade()) - HandleUpdateAvailable(); + HandleUpdateAvailable(ash::UpdateType::kSystem); // If the device is enterprise managed then send ash the enterprise domain. policy::BrowserPolicyConnectorChromeOS* policy_connector = @@ -171,7 +174,11 @@ update_notification_style_ = style; update_notification_title_ = notification_title; update_notification_body_ = notification_body; - HandleUpdateAvailable(); + HandleUpdateAvailable(ash::UpdateType::kSystem); +} + +void SystemTrayClient::SetLacrosUpdateAvailable() { + HandleUpdateAvailable(ash::UpdateType::kLacros); } void SystemTrayClient::SetPrimaryTrayEnabled(bool enabled) { @@ -460,20 +467,15 @@ chrome::AttemptUserExit(); } -void SystemTrayClient::HandleUpdateAvailable() { - // Show an update icon for Chrome OS updates. +void SystemTrayClient::HandleUpdateAvailable(ash::UpdateType update_type) { UpgradeDetector* detector = UpgradeDetector::GetInstance(); - bool update_available = detector->notify_upgrade(); - DCHECK(update_available); - if (!update_available) + if (update_type == ash::UpdateType::kSystem && !detector->notify_upgrade()) { + LOG(ERROR) << "Tried to show update notification when no update available"; return; + } - // Get the Chrome update severity. - ash::UpdateSeverity severity = GetUpdateSeverity(detector); - - // TODO(https://crbug.com/1154427): Add Lacros update type. - ash::UpdateType update_type = ash::UpdateType::kSystem; - + // Show the system tray icon. + ash::UpdateSeverity severity = GetUpdateSeverity(update_type, detector); system_tray_->ShowUpdateIcon(severity, detector->is_factory_reset_required(), detector->is_rollback(), update_type); @@ -506,7 +508,7 @@ } void SystemTrayClient::OnUpgradeRecommended() { - HandleUpdateAvailable(); + HandleUpdateAvailable(ash::UpdateType::kSystem); } ////////////////////////////////////////////////////////////////////////////////
diff --git a/chrome/browser/ui/ash/system_tray_client.h b/chrome/browser/ui/ash/system_tray_client.h index 97e9237..5d426ec3 100644 --- a/chrome/browser/ui/ash/system_tray_client.h +++ b/chrome/browser/ui/ash/system_tray_client.h
@@ -16,6 +16,7 @@ class SystemTray; enum class LoginStatus; enum class NotificationStyle; +enum class UpdateType; } // namespace ash // Handles method calls delegated back to chrome from ash. Also notifies ash of @@ -33,10 +34,14 @@ // Specifies if notification is recommended or required by administrator and // triggers the notification to be shown with the given body and title. + // Only applies to OS updates. void SetUpdateNotificationState(ash::NotificationStyle style, const base::string16& notification_title, const base::string16& notification_body); + // Shows a notification that a Lacros browser update is available. + void SetLacrosUpdateAvailable(); + // Wrappers around ash::mojom::SystemTray interface: void SetPrimaryTrayEnabled(bool enabled); void SetPrimaryTrayVisible(bool visible); @@ -83,7 +88,7 @@ bool show_configure); // Requests that ash show the update available icon. - void HandleUpdateAvailable(); + void HandleUpdateAvailable(ash::UpdateType update_type); // chromeos::system::SystemClockObserver: void OnSystemClockChanged(chromeos::system::SystemClock* clock) override;
diff --git a/chrome/browser/ui/hid/hid_chooser_controller.cc b/chrome/browser/ui/hid/hid_chooser_controller.cc index a8e836f..e7a8322 100644 --- a/chrome/browser/ui/hid/hid_chooser_controller.cc +++ b/chrome/browser/ui/hid/hid_chooser_controller.cc
@@ -7,14 +7,12 @@ #include <utility> #include "base/bind.h" -#include "base/strings/stringprintf.h" -#include "base/strings/utf_string_conversions.h" #include "chrome/browser/hid/hid_chooser_context.h" #include "chrome/browser/hid/hid_chooser_context_factory.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/usb/usb_blocklist.h" #include "chrome/grit/generated_resources.h" #include "content/public/browser/web_contents.h" +#include "services/device/public/cpp/hid/hid_blocklist.h" #include "ui/base/l10n/l10n_util.h" namespace { @@ -193,8 +191,8 @@ bool HidChooserController::DisplayDevice( const device::mojom::HidDeviceInfo& device) const { - // Do not pass the device to the chooser if it is on the USB blocklist. - if (UsbBlocklist::Get().IsExcluded({device.vendor_id, device.product_id, 0})) + // Do not pass the device to the chooser if it is excluded by the blocklist. + if (device::HidBlocklist::IsDeviceExcluded(device)) return false; // Do not pass the device to the chooser if it has a top-level collection with
diff --git a/chrome/browser/ui/startup/bad_flags_prompt.cc b/chrome/browser/ui/startup/bad_flags_prompt.cc index 5a40403..6c831a8 100644 --- a/chrome/browser/ui/startup/bad_flags_prompt.cc +++ b/chrome/browser/ui/startup/bad_flags_prompt.cc
@@ -38,6 +38,7 @@ #include "media/base/media_switches.h" #include "media/media_buildflags.h" #include "sandbox/policy/switches.h" +#include "services/device/public/cpp/hid/hid_switches.h" #include "services/network/public/cpp/network_switches.h" #include "third_party/blink/public/common/features.h" #include "ui/base/l10n/l10n_util.h" @@ -143,6 +144,9 @@ // A flag to support local file based WebBundle loading, only for testing // purpose. switches::kTrustableWebBundleFileUrl, + + // A flag to bypass the WebHID blocklist for testing purposes. + switches::kDisableHidBlocklist, }; #endif // OS_ANDROID
diff --git a/chrome/browser/ui/views/extensions/extensions_menu_view.cc b/chrome/browser/ui/views/extensions/extensions_menu_view.cc index 91bd7c3..1f8b20e 100644 --- a/chrome/browser/ui/views/extensions/extensions_menu_view.cc +++ b/chrome/browser/ui/views/extensions/extensions_menu_view.cc
@@ -79,6 +79,11 @@ IDS_EXTENSIONS_MENU_ACCESSING_SITE_DATA_SHORT, IDS_EXTENSIONS_MENU_ACCESSING_SITE_DATA, ToolbarActionViewController::PageInteractionStatus::kActive} { + // Ensure layer masking is used for the extensions menu to ensure buttons with + // layer effects sitting flush with the bottom of the bubble are clipped + // appropriately. + SetPaintClientToLayer(true); + toolbar_model_observation_.Observe(toolbar_model_); browser_->tab_strip_model()->AddObserver(this); set_margins(gfx::Insets(0));
diff --git a/chrome/browser/ui/views/frame/tab_strip_region_view.cc b/chrome/browser/ui/views/frame/tab_strip_region_view.cc index a058f50..1fd4387 100644 --- a/chrome/browser/ui/views/frame/tab_strip_region_view.cc +++ b/chrome/browser/ui/views/frame/tab_strip_region_view.cc
@@ -132,8 +132,9 @@ views::FlexSpecification(views::MinimumFlexSizeRule::kPreferred, views::MaximumFlexSizeRule::kUnbounded)); - if (base::FeatureList::IsEnabled(features::kTabSearch) && - tab_strip_->controller()->GetBrowser()->is_type_normal()) { + const Browser* browser = tab_strip_->controller()->GetBrowser(); + if (base::FeatureList::IsEnabled(features::kTabSearch) && browser && + browser->is_type_normal()) { auto tab_search_button = std::make_unique<TabSearchButton>(tab_strip_); tab_search_button->SetTooltipText( l10n_util::GetStringUTF16(IDS_TOOLTIP_TAB_SEARCH));
diff --git a/chrome/browser/ui/views/profiles/profile_customization_bubble_sync_controller.cc b/chrome/browser/ui/views/profiles/profile_customization_bubble_sync_controller.cc new file mode 100644 index 0000000..83f00cf6 --- /dev/null +++ b/chrome/browser/ui/views/profiles/profile_customization_bubble_sync_controller.cc
@@ -0,0 +1,139 @@ +// 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/ui/views/profiles/profile_customization_bubble_sync_controller.h" + +#include "chrome/browser/sync/profile_sync_service_factory.h" +#include "chrome/browser/themes/theme_service.h" +#include "chrome/browser/themes/theme_service_factory.h" +#include "chrome/browser/ui/views/profiles/profile_customization_bubble_view.h" +#include "components/sync/driver/sync_user_settings.h" + +namespace { + +bool CanSyncStart(syncer::SyncService* sync_service) { + if (!sync_service || !sync_service->CanSyncFeatureStart()) + return false; + if (!sync_service->GetUserSettings()->GetSelectedTypes().Has( + syncer::UserSelectableType::kThemes)) { + return false; + } + return true; +} + +void ShowBubble(Profile* profile, views::View* anchor_view, bool should_show) { + if (!should_show) + return; + ProfileCustomizationBubbleView::CreateBubble(profile, anchor_view); +} + +} // namespace + +// static +void ProfileCustomizationBubbleSyncController:: + ApplyColorAndShowBubbleWhenNoValueSynced(Profile* profile, + views::View* anchor_view, + SkColor suggested_profile_color) { + // The controller is owned by itself. + auto* controller = new ProfileCustomizationBubbleSyncController( + ProfileSyncServiceFactory::GetForProfile(profile), + ThemeServiceFactory::GetForProfile(profile), + base::BindOnce(&ShowBubble, profile, anchor_view), + suggested_profile_color); + controller->Init(); +} + +// static +void ProfileCustomizationBubbleSyncController:: + ApplyColorAndShowBubbleWhenNoValueSyncedForTesting( + syncer::SyncService* sync_service, + ThemeService* theme_service, + base::OnceCallback<void(bool)> show_bubble_callback, + SkColor suggested_profile_color) { + // The controller is owned by itself. + auto* controller = new ProfileCustomizationBubbleSyncController( + sync_service, theme_service, std::move(show_bubble_callback), + suggested_profile_color); + controller->Init(); +} + +// static +bool ProfileCustomizationBubbleSyncController::CanThemeSyncStart( + Profile* profile) { + syncer::SyncService* sync_service = + ProfileSyncServiceFactory::GetForProfile(profile); + return CanSyncStart(sync_service); +} + +ProfileCustomizationBubbleSyncController:: + ProfileCustomizationBubbleSyncController( + syncer::SyncService* sync_service, + ThemeService* theme_service, + base::OnceCallback<void(bool)> show_bubble_callback, + SkColor suggested_profile_color) + : sync_service_(sync_service), + theme_service_(theme_service), + show_bubble_callback_(std::move(show_bubble_callback)), + suggested_profile_color_(suggested_profile_color) { + DCHECK(sync_service_); + DCHECK(theme_service_); + DCHECK(show_bubble_callback_); +} + +ProfileCustomizationBubbleSyncController:: + ~ProfileCustomizationBubbleSyncController() = default; + +void ProfileCustomizationBubbleSyncController::Init() { + if (!CanSyncStart(sync_service_)) { + ApplyDefaultColorAndShowBubble(); + return; + } + + theme_observation_.Observe(theme_service_->GetThemeSyncableService()); + + // Observe also the sync service to abort waiting for theme sync if the user + // hits any error or if custom passphrase is needed. + sync_observation_.Observe(sync_service_); +} + +void ProfileCustomizationBubbleSyncController::OnStateChanged( + syncer::SyncService* sync) { + // If we figure out sync cannot start (soon), skip the check and show the + // bubble. + if (!CanSyncStart(sync)) { + ApplyDefaultColorAndShowBubble(); + return; + } + + if (sync->GetUserSettings()->IsPassphraseRequired()) { + // Keep the default color and do not show the bubble. The reason is that the + // custom passphrase user may have a color in their sync but Chrome will + // figure that out later (once the user enter their passphrase) so no prior + // customization makes sense. + SkipBubble(); + } +} + +void ProfileCustomizationBubbleSyncController::OnThemeSyncStarted() { + // Skip the bubble (and not use the default color) if the user got a + // non-default value from sync. + if (!theme_service_->UsingDefaultTheme() && + !theme_service_->UsingSystemTheme()) { + SkipBubble(); + return; + } + ApplyDefaultColorAndShowBubble(); +} + +void ProfileCustomizationBubbleSyncController:: + ApplyDefaultColorAndShowBubble() { + theme_service_->BuildAutogeneratedThemeFromColor(suggested_profile_color_); + std::move(show_bubble_callback_).Run(true); + delete this; +} + +void ProfileCustomizationBubbleSyncController::SkipBubble() { + std::move(show_bubble_callback_).Run(false); + delete this; +}
diff --git a/chrome/browser/ui/views/profiles/profile_customization_bubble_sync_controller.h b/chrome/browser/ui/views/profiles/profile_customization_bubble_sync_controller.h new file mode 100644 index 0000000..2b97d833 --- /dev/null +++ b/chrome/browser/ui/views/profiles/profile_customization_bubble_sync_controller.h
@@ -0,0 +1,90 @@ +// 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_UI_VIEWS_PROFILES_PROFILE_CUSTOMIZATION_BUBBLE_SYNC_CONTROLLER_H_ +#define CHROME_BROWSER_UI_VIEWS_PROFILES_PROFILE_CUSTOMIZATION_BUBBLE_SYNC_CONTROLLER_H_ + +#include "base/callback.h" +#include "base/scoped_observation.h" +#include "chrome/browser/themes/theme_syncable_service.h" +#include "components/sync/driver/sync_service.h" +#include "components/sync/driver/sync_service_observer.h" +#include "third_party/skia/include/core/SkColor.h" + +namespace views { +class View; +} // namespace views + +class Profile; + +// Helper class for logic to show / delay showing the profile customization +// bubble. Owns itself. +class ProfileCustomizationBubbleSyncController + : public syncer::SyncServiceObserver, + public ThemeSyncableService::Observer { + public: + ~ProfileCustomizationBubbleSyncController() override; + + ProfileCustomizationBubbleSyncController( + const ProfileCustomizationBubbleSyncController& other) = delete; + ProfileCustomizationBubbleSyncController& operator=( + const ProfileCustomizationBubbleSyncController& other) = delete; + + // Applies `suggested_profile_color` and shows the profile customization + // bubble if either (a) theme sync cannot start (see `CanThemeSyncStart()`) or + // (b) theme sync is successful and results in the default theme in this + // profile (either no value was synced before or the default theme was + // synced). In all other cases, the call has no visible impact. This also + // includes the case when sync can start but is blocked on the user to enter + // the custom passphrase. + static void ApplyColorAndShowBubbleWhenNoValueSynced( + Profile* profile, + views::View* anchor_view, + SkColor suggested_profile_color); + + // A version of ApplyColorAndShowBubbleWhenNoValueSynced() that allows simpler + // mocking. + static void ApplyColorAndShowBubbleWhenNoValueSyncedForTesting( + syncer::SyncService* sync_service, + ThemeService* theme_service, + base::OnceCallback<void(bool)> show_bubble_callback, + SkColor suggested_profile_color); + + // Returns whether theme sync can start (i.e. is not disabled by policy, + // theme sync is enabled, ...). + static bool CanThemeSyncStart(Profile* profile); + + private: + ProfileCustomizationBubbleSyncController( + syncer::SyncService* sync_service, + ThemeService* theme_service, + base::OnceCallback<void(bool)> show_bubble_callback, + SkColor suggested_profile_color); + + // SyncServiceObserver: + void OnStateChanged(syncer::SyncService* sync) override; + + // ThemeSyncableService::Observer: + void OnThemeSyncStarted() override; + + // This function may delete the object. + void Init(); + + // Functions that finalize the control logic by either showing or skipping the + // bubble and deleting itself. + void ApplyDefaultColorAndShowBubble(); + void SkipBubble(); + + syncer::SyncService* const sync_service_; + ThemeService* const theme_service_; + base::OnceCallback<void(bool)> show_bubble_callback_; + SkColor const suggested_profile_color_; + + base::ScopedObservation<syncer::SyncService, syncer::SyncServiceObserver> + sync_observation_{this}; + base::ScopedObservation<ThemeSyncableService, ThemeSyncableService::Observer> + theme_observation_{this}; +}; + +#endif // CHROME_BROWSER_UI_VIEWS_PROFILES_PROFILE_CUSTOMIZATION_BUBBLE_SYNC_CONTROLLER_H_
diff --git a/chrome/browser/ui/views/profiles/profile_customization_bubble_sync_controller_unittest.cc b/chrome/browser/ui/views/profiles/profile_customization_bubble_sync_controller_unittest.cc new file mode 100644 index 0000000..4908272 --- /dev/null +++ b/chrome/browser/ui/views/profiles/profile_customization_bubble_sync_controller_unittest.cc
@@ -0,0 +1,149 @@ +// 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/ui/views/profiles/profile_customization_bubble_sync_controller.h" + +#include "base/test/mock_callback.h" +#include "chrome/browser/themes/theme_service.h" +#include "chrome/browser/themes/theme_syncable_service.h" +#include "components/sync/driver/sync_service.h" +#include "components/sync/driver/test_sync_service.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/core/SkColor.h" + +namespace { + +constexpr SkColor kNewProfileColor = SK_ColorRED; +constexpr SkColor kSyncedProfileColor = SK_ColorBLUE; + +class FakeThemeService : public ThemeService { + public: + explicit FakeThemeService(const ThemeHelper& theme_helper) + : ThemeService(nullptr, theme_helper) {} + + void SetThemeSyncableService(ThemeSyncableService* theme_syncable_service) { + theme_syncable_service_ = theme_syncable_service; + } + + // ThemeService: + void DoSetTheme(const extensions::Extension* extension, + bool suppress_infobar) override { + using_default_theme_ = false; + color_ = 0; + } + + void BuildAutogeneratedThemeFromColor(SkColor color) override { + color_ = color; + using_default_theme_ = false; + } + + void UseDefaultTheme() override { + using_default_theme_ = true; + color_ = 0; + } + + bool UsingDefaultTheme() const override { return using_default_theme_; } + + SkColor GetAutogeneratedThemeColor() const override { return color_; } + + ThemeSyncableService* GetThemeSyncableService() const override { + return theme_syncable_service_; + } + + private: + ThemeSyncableService* theme_syncable_service_ = nullptr; + bool using_default_theme_ = true; + SkColor color_ = 0; +}; + +class ProfileCustomizationBubbleSyncControllerTest : public testing::Test { + public: + ProfileCustomizationBubbleSyncControllerTest() + : fake_theme_service_(theme_helper_), + theme_syncable_service_(nullptr, &fake_theme_service_) { + fake_theme_service_.SetThemeSyncableService(&theme_syncable_service_); + } + + void ApplyColorAndShowBubbleWhenNoValueSynced( + base::OnceCallback<void(bool)> show_bubble_callback) { + ProfileCustomizationBubbleSyncController:: + ApplyColorAndShowBubbleWhenNoValueSyncedForTesting( + &test_sync_service_, &fake_theme_service_, + std::move(show_bubble_callback), kNewProfileColor); + } + + void SetSyncedProfileColor() { + fake_theme_service_.BuildAutogeneratedThemeFromColor(kSyncedProfileColor); + } + + void SetSyncedProfileTheme() { + fake_theme_service_.DoSetTheme(nullptr, false); + } + + syncer::TestSyncService* test_sync_service() { return &test_sync_service_; } + + void NotifyOnSyncStarted() { + theme_syncable_service_.NotifyOnSyncStartedForTesting(); + } + + protected: + syncer::TestSyncService test_sync_service_; + + private: + FakeThemeService fake_theme_service_; + ThemeSyncableService theme_syncable_service_; + ThemeHelper theme_helper_; +}; + +TEST_F(ProfileCustomizationBubbleSyncControllerTest, + ShouldShowWhenSyncGetsDefaultTheme) { + base::MockCallback<base::OnceCallback<void(bool)>> show_bubble; + EXPECT_CALL(show_bubble, Run(true)); + + ApplyColorAndShowBubbleWhenNoValueSynced(show_bubble.Get()); + NotifyOnSyncStarted(); +} + +TEST_F(ProfileCustomizationBubbleSyncControllerTest, + ShouldShowWhenSyncDisabled) { + base::MockCallback<base::OnceCallback<void(bool)>> show_bubble; + EXPECT_CALL(show_bubble, Run(true)); + + test_sync_service_.SetDisableReasons( + syncer::SyncService::DISABLE_REASON_ENTERPRISE_POLICY); + ApplyColorAndShowBubbleWhenNoValueSynced(show_bubble.Get()); +} + +TEST_F(ProfileCustomizationBubbleSyncControllerTest, + ShouldNotShowWhenSyncGetsCustomColor) { + base::MockCallback<base::OnceCallback<void(bool)>> show_bubble; + EXPECT_CALL(show_bubble, Run(false)); + + ApplyColorAndShowBubbleWhenNoValueSynced(show_bubble.Get()); + SetSyncedProfileColor(); + NotifyOnSyncStarted(); +} + +TEST_F(ProfileCustomizationBubbleSyncControllerTest, + ShouldNotShowWhenSyncGetsCustomTheme) { + base::MockCallback<base::OnceCallback<void(bool)>> show_bubble; + EXPECT_CALL(show_bubble, Run(false)); + + ApplyColorAndShowBubbleWhenNoValueSynced(show_bubble.Get()); + SetSyncedProfileTheme(); + NotifyOnSyncStarted(); +} + +TEST_F(ProfileCustomizationBubbleSyncControllerTest, + ShouldNotShowWhenSyncHasCustomPasshrase) { + base::MockCallback<base::OnceCallback<void(bool)>> show_bubble; + EXPECT_CALL(show_bubble, Run(false)); + + test_sync_service_.SetPassphraseRequired(true); + ApplyColorAndShowBubbleWhenNoValueSynced(show_bubble.Get()); + test_sync_service_.FireStateChanged(); +} + +} // namespace
diff --git a/chrome/browser/ui/views/profiles/profile_picker_view.cc b/chrome/browser/ui/views/profiles/profile_picker_view.cc index c59791d..94662f8 100644 --- a/chrome/browser/ui/views/profiles/profile_picker_view.cc +++ b/chrome/browser/ui/views/profiles/profile_picker_view.cc
@@ -30,6 +30,11 @@ #include "chrome/browser/ui/browser_navigator_params.h" #include "chrome/browser/ui/user_manager.h" #include "chrome/browser/ui/views/accelerator_table.h" +#include "chrome/browser/ui/views/frame/browser_view.h" +#include "chrome/browser/ui/views/frame/toolbar_button_provider.h" +#include "chrome/browser/ui/views/profiles/avatar_toolbar_button.h" +#include "chrome/browser/ui/views/profiles/profile_customization_bubble_sync_controller.h" +#include "chrome/browser/ui/views/profiles/profile_customization_bubble_view.h" #include "chrome/browser/ui/views/profiles/profile_picker_view_sync_delegate.h" #include "chrome/browser/ui/webui/signin/dice_turn_sync_on_helper.h" #include "chrome/browser/ui/webui/signin/profile_picker_ui.h" @@ -81,6 +86,28 @@ IDC_CLOSE_TAB, IDC_CLOSE_WINDOW, IDC_EXIT, IDC_FULLSCREEN, IDC_MINIMIZE_WINDOW}; +void ShowCustomizationBubble(SkColor new_profile_color, Browser* browser) { + views::View* anchor_view = BrowserView::GetBrowserViewForBrowser(browser) + ->toolbar_button_provider() + ->GetAvatarToolbarButton(); + DCHECK(anchor_view); + + if (ProfileCustomizationBubbleSyncController::CanThemeSyncStart( + browser->profile())) { + // For sync users, their profile color has not been applied yet. Call a + // helper class that applies the color and shows the bubble only if there is + // no conflict with a synced theme / color. + ProfileCustomizationBubbleSyncController:: + ApplyColorAndShowBubbleWhenNoValueSynced( + browser->profile(), anchor_view, + /*suggested_profile_color=*/new_profile_color); + } else { + // For non syncing users, simply show the bubble. + ProfileCustomizationBubbleView::CreateBubble(browser->profile(), + anchor_view); + } +} + GURL CreateURLForEntryPoint(ProfilePicker::EntryPoint entry_point) { GURL base_url = GURL(chrome::kChromeUIProfilePickerUrl); switch (entry_point) { @@ -298,8 +325,11 @@ void ProfilePickerView::SwitchToSignIn( SkColor profile_color, base::OnceCallback<void(bool)> switch_finished_callback) { + profile_color_ = profile_color; + DCHECK(!switch_finished_callback_); switch_finished_callback_ = std::move(switch_finished_callback); + size_t icon_index = profiles::GetPlaceholderAvatarIndex(); // Silently create the new profile for browsing on GAIA (so that the sign-in // cookies are stored in the right profile). @@ -309,11 +339,10 @@ .ChooseNameForNewProfile(icon_index), profiles::GetDefaultAvatarIconUrl(icon_index), base::BindRepeating(&ProfilePickerView::OnProfileForSigninCreated, - weak_ptr_factory_.GetWeakPtr(), profile_color)); + weak_ptr_factory_.GetWeakPtr())); } void ProfilePickerView::OnProfileForSigninCreated( - SkColor profile_color, Profile* profile, Profile::CreateStatus status) { if (status == Profile::CREATE_STATUS_LOCAL_FAIL) { @@ -335,10 +364,6 @@ return; } - // Apply a new color to the profile. - auto* theme_service = ThemeServiceFactory::GetForProfile(profile); - theme_service->BuildAutogeneratedThemeFromColor(profile_color); - if (signin_util::IsForceSigninEnabled()) { // Show the embedded sign-in flow if the force signin is enabled. // TODO(https://crbug.com/1156096): set the local profile name at the end of @@ -539,7 +564,9 @@ base::OnceClosure sync_consent_completed_closure = base::BindOnce(&ProfilePickerView::FinishSignedInCreationFlow, - weak_ptr_factory_.GetWeakPtr(), BrowserOpenedCallback()); + weak_ptr_factory_.GetWeakPtr(), + base::BindOnce(&ShowCustomizationBubble, profile_color_), + /*enterprise_sync_consent_needed=*/false); // Stop with the sign-in navigation, it is not needed any more and this avoids // any glitches of the redirect page getting displayed. This is needed because @@ -606,7 +633,8 @@ } void ProfilePickerView::FinishSignedInCreationFlow( - BrowserOpenedCallback callback) { + BrowserOpenedCallback callback, + bool enterprise_sync_consent_needed) { // This can get called first time from a special case handling (such as the // Settings link) and than second time when the consent flow finishes. We need // to make sure only the first call gets handled. @@ -617,15 +645,18 @@ if (name_for_signed_in_profile_.empty()) { on_profile_name_available_ = base::BindOnce(&ProfilePickerView::FinishSignedInCreationFlowImpl, - weak_ptr_factory_.GetWeakPtr(), std::move(callback)); + weak_ptr_factory_.GetWeakPtr(), std::move(callback), + enterprise_sync_consent_needed); return; } - FinishSignedInCreationFlowImpl(std::move(callback)); + FinishSignedInCreationFlowImpl(std::move(callback), + enterprise_sync_consent_needed); } void ProfilePickerView::FinishSignedInCreationFlowImpl( - BrowserOpenedCallback callback) { + BrowserOpenedCallback callback, + bool enterprise_sync_consent_needed) { DCHECK(!name_for_signed_in_profile_.empty()); ProfileAttributesEntry* entry = nullptr; @@ -641,6 +672,17 @@ entry->SetIsEphemeral(false); entry->SetLocalProfileName(name_for_signed_in_profile_); + // If sync is not enabled (and will not likely be enabled with an enterprise + // consent), apply a new color to the profile (otherwise, a more complicated + // logic gets triggered in ShowCustomizationBubble()). + if (!enterprise_sync_consent_needed && + !ProfileCustomizationBubbleSyncController::CanThemeSyncStart( + signed_in_profile_being_created_)) { + auto* theme_service = + ThemeServiceFactory::GetForProfile(signed_in_profile_being_created_); + theme_service->BuildAutogeneratedThemeFromColor(profile_color_); + } + // Skip the FRE for this profile as it's replaced by profile creation flow. signed_in_profile_being_created_->GetPrefs()->SetBoolean( prefs::kHasSeenWelcomePage, true);
diff --git a/chrome/browser/ui/views/profiles/profile_picker_view.h b/chrome/browser/ui/views/profiles/profile_picker_view.h index 79943de..3028b636 100644 --- a/chrome/browser/ui/views/profiles/profile_picker_view.h +++ b/chrome/browser/ui/views/profiles/profile_picker_view.h
@@ -68,8 +68,7 @@ void SwitchToSignIn(SkColor profile_color, base::OnceCallback<void(bool)> switch_finished_callback); // On creation success for the sign-in profile, it rebuilds the view. - void OnProfileForSigninCreated(SkColor profile_color, - Profile* new_profile, + void OnProfileForSigninCreated(Profile* new_profile, Profile::CreateStatus status); // Switches the layout to the sync confirmation screen. void SwitchToSyncConfirmation(); @@ -116,8 +115,10 @@ // Finishes the creation flow by marking `profile_being_created_` as fully // created, opening a browser window for this profile and calling `callback`. - void FinishSignedInCreationFlow(BrowserOpenedCallback callback); - void FinishSignedInCreationFlowImpl(BrowserOpenedCallback callback); + void FinishSignedInCreationFlow(BrowserOpenedCallback callback, + bool enterprise_sync_consent_needed); + void FinishSignedInCreationFlowImpl(BrowserOpenedCallback callback, + bool enterprise_sync_consent_needed); // Internal callback to finish the last steps of the signed-in creation flow. void OnBrowserOpened(BrowserOpenedCallback finish_flow_callback, @@ -171,6 +172,11 @@ // same lifetime. This is used for displaying the sign-in flow. std::unique_ptr<content::WebContents> new_profile_contents_; + // Assigned a value at the beginning of a signed-in profile creation, set for + // the profile at the very end to avoid coloring the simple toolbar for GAIA + // sign-in (that uses the ThemeProvider of the current profile). + SkColor profile_color_; + base::string16 name_for_signed_in_profile_; base::OnceClosure on_profile_name_available_; base::TimeDelta extended_account_info_timeout_;
diff --git a/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc b/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc index e4414e1..2672a0ad 100644 --- a/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc +++ b/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc
@@ -271,8 +271,6 @@ // Add an account - simulate a successful Gaia sign-in. Profile* profile_being_created = static_cast<Profile*>(web_contents()->GetBrowserContext()); - syncer::SyncService* sync_service = - ProfileSyncServiceFactory::GetForProfile(profile_being_created); signin::IdentityManager* identity_manager = IdentityManagerFactory::GetForProfile(profile_being_created); CoreAccountInfo core_account_info = @@ -287,9 +285,9 @@ // confirmation screen getting displayed. WaitForFirstPaint(web_contents(), GURL("chrome://sync-confirmation/")); - // Simulate closing the UI with "Yes, I'm in". + // Simulate closing the UI with "No, thanks". LoginUIServiceFactory::GetForProfile(profile_being_created) - ->SyncConfirmationUIClosed(LoginUIService::SYNC_WITH_DEFAULT_SETTINGS); + ->SyncConfirmationUIClosed(LoginUIService::ABORT_SYNC); Browser* new_browser = BrowserAddedWaiter(2u).Wait(); WaitForFirstPaint(new_browser->tab_strip_model()->GetActiveWebContents(), GURL("chrome://newtab/")); @@ -303,13 +301,12 @@ .GetProfileAttributesWithPath( profile_being_created->GetPath(), &entry)); EXPECT_FALSE(entry->IsEphemeral()); - EXPECT_TRUE(entry->IsAuthenticated()); + EXPECT_FALSE(entry->IsAuthenticated()); EXPECT_EQ(entry->GetLocalProfileName(), base::UTF8ToUTF16("Joe")); + EXPECT_EQ(ThemeServiceFactory::GetForProfile(profile_being_created) ->GetAutogeneratedThemeColor(), kProfileColor); - EXPECT_TRUE(sync_service->GetUserSettings()->IsSyncRequested()); - EXPECT_TRUE(sync_service->GetUserSettings()->IsFirstSetupComplete()); } IN_PROC_BROWSER_TEST_F(ProfilePickerCreationFlowBrowserTest, @@ -432,9 +429,9 @@ // cannot be told. EXPECT_TRUE(entry->IsAuthenticated()); EXPECT_EQ(entry->GetLocalProfileName(), base::UTF8ToUTF16("Joe")); - EXPECT_EQ(ThemeServiceFactory::GetForProfile(profile_being_created) - ->GetAutogeneratedThemeColor(), - kProfileColor); + // The color is not applied if the user enters settings. + EXPECT_FALSE(ThemeServiceFactory::GetForProfile(profile_being_created) + ->UsingAutogeneratedTheme()); } IN_PROC_BROWSER_TEST_F(ProfilePickerCreationFlowBrowserTest, @@ -586,7 +583,7 @@ // Simulate closing the UI with "Yes, I'm in". LoginUIServiceFactory::GetForProfile(profile_being_created) - ->SyncConfirmationUIClosed(LoginUIService::SYNC_WITH_DEFAULT_SETTINGS); + ->SyncConfirmationUIClosed(LoginUIService::ABORT_SYNC); Browser* new_browser = BrowserAddedWaiter(2u).Wait(); WaitForFirstPaint(new_browser->tab_strip_model()->GetActiveWebContents(), GURL("chrome://newtab/")); @@ -600,7 +597,7 @@ .GetProfileAttributesWithPath( profile_being_created->GetPath(), &entry)); EXPECT_FALSE(entry->IsEphemeral()); - EXPECT_TRUE(entry->IsAuthenticated()); + EXPECT_FALSE(entry->IsAuthenticated()); // Since the given name is not provided, the email address is used instead as // a profile name. EXPECT_EQ(entry->GetLocalProfileName(), @@ -676,10 +673,10 @@ GURL("chrome://newtab/")); EXPECT_FALSE(ProfilePicker::IsOpen()); - // Now the sync consent screen is shown, simulate closing the UI with "Yes, - // I'm in". + // Now the sync consent screen is shown, simulate closing the UI with "No, + // thanks". LoginUIServiceFactory::GetForProfile(profile_being_created) - ->SyncConfirmationUIClosed(LoginUIService::SYNC_WITH_DEFAULT_SETTINGS); + ->SyncConfirmationUIClosed(LoginUIService::ABORT_SYNC); // Check expectations when the profile creation flow is done. ProfileAttributesEntry* entry = nullptr; @@ -689,9 +686,10 @@ profile_being_created->GetPath(), &entry)); EXPECT_FALSE(entry->IsEphemeral()); EXPECT_EQ(entry->GetLocalProfileName(), base::UTF8ToUTF16(kWork)); - EXPECT_EQ(ThemeServiceFactory::GetForProfile(profile_being_created) - ->GetAutogeneratedThemeColor(), - kProfileColor); + + // The color is not applied for enterprise users. + EXPECT_FALSE(ThemeServiceFactory::GetForProfile(profile_being_created) + ->UsingAutogeneratedTheme()); } IN_PROC_BROWSER_TEST_F(ProfilePickerEnterpriseCreationFlowBrowserTest, @@ -756,9 +754,9 @@ profile_being_created->GetPath(), &entry)); EXPECT_FALSE(entry->IsEphemeral()); EXPECT_EQ(entry->GetLocalProfileName(), base::UTF8ToUTF16(kWork)); - EXPECT_EQ(ThemeServiceFactory::GetForProfile(profile_being_created) - ->GetAutogeneratedThemeColor(), - kProfileColor); + // The color is not applied if the user enters settings. + EXPECT_FALSE(ThemeServiceFactory::GetForProfile(profile_being_created) + ->UsingAutogeneratedTheme()); } } // namespace
diff --git a/chrome/browser/ui/views/profiles/profile_picker_view_sync_delegate.cc b/chrome/browser/ui/views/profiles/profile_picker_view_sync_delegate.cc index a42a42bc..30c29da 100644 --- a/chrome/browser/ui/views/profiles/profile_picker_view_sync_delegate.cc +++ b/chrome/browser/ui/views/profiles/profile_picker_view_sync_delegate.cc
@@ -49,8 +49,9 @@ // switch or to start sign-in once again. std::move(open_browser_callback_) .Run(base::BindOnce( - &DiceTurnSyncOnHelper::Delegate::ShowLoginErrorForBrowser, email, - error_message)); + &DiceTurnSyncOnHelper::Delegate::ShowLoginErrorForBrowser, email, + error_message), + /*enterprise_sync_consent_needed=*/false); } void ProfilePickerViewSyncDelegate::ShowMergeSyncDataConfirmation( @@ -69,7 +70,8 @@ std::move(open_browser_callback_) .Run(base::BindOnce(&DiceTurnSyncOnHelper::Delegate:: ShowEnterpriseAccountConfirmationForBrowser, - email, std::move(callback))); + email, std::move(callback)), + /*enterprise_sync_consent_needed=*/true); } void ProfilePickerViewSyncDelegate::ShowSyncConfirmation( @@ -101,7 +103,8 @@ // Open the browser and when it's done, show the confirmation dialog. std::move(open_browser_callback_) - .Run(base::BindOnce(&OpenSyncConfirmationDialogInBrowser)); + .Run(base::BindOnce(&OpenSyncConfirmationDialogInBrowser), + /*enterprise_sync_consent_needed=*/false); } void ProfilePickerViewSyncDelegate::ShowSyncSettings() { @@ -114,7 +117,9 @@ } // Open the browser and when it's done, open settings in the browser. - std::move(open_browser_callback_).Run(base::BindOnce(&OpenSettingsInBrowser)); + std::move(open_browser_callback_) + .Run(base::BindOnce(&OpenSettingsInBrowser), + /*enterprise_sync_consent_needed=*/false); } void ProfilePickerViewSyncDelegate::SwitchToProfile(Profile* new_profile) {
diff --git a/chrome/browser/ui/views/profiles/profile_picker_view_sync_delegate.h b/chrome/browser/ui/views/profiles/profile_picker_view_sync_delegate.h index 69aad8e..87197fc 100644 --- a/chrome/browser/ui/views/profiles/profile_picker_view_sync_delegate.h +++ b/chrome/browser/ui/views/profiles/profile_picker_view_sync_delegate.h
@@ -18,7 +18,8 @@ public LoginUIService::Observer { public: using OpenBrowserCallback = - base::OnceCallback<void(ProfilePickerView::BrowserOpenedCallback)>; + base::OnceCallback<void(ProfilePickerView::BrowserOpenedCallback, + bool enterprise_sync_consent_needed)>; ProfilePickerViewSyncDelegate(Profile* profile, OpenBrowserCallback open_browser_callback);
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc index 4b6e0e4..0ff04054 100644 --- a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc +++ b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
@@ -2052,8 +2052,10 @@ } // namespace -#if defined(OS_MAC) && defined(ARCH_CPU_ARM64) +#if defined(OS_MAC) /* && defined(ARCH_CPU_ARM64) */ // Bulk-disabled for arm64 bot stabilization: https://crbug.com/1154345 +// These were flaking on all macs, so commented out ARCH_ above for +// crbug.com/1160917 too. #define MAYBE_DragAllToSeparateWindow DISABLED_DragAllToSeparateWindow #else #define MAYBE_DragAllToSeparateWindow DragAllToSeparateWindow @@ -2561,8 +2563,10 @@ } // namespace -#if defined(OS_MAC) && defined(ARCH_CPU_ARM64) +#if defined(OS_MAC) /* && defined(ARCH_CPU_ARM64) */ // Bulk-disabled for arm64 bot stabilization: https://crbug.com/1154345 +// These were flaking on all macs, so commented out ARCH_ above for +// crbug.com/1160917 too. #define MAYBE_DragAllToSeparateWindowAndCancel \ DISABLED_DragAllToSeparateWindowAndCancel #else
diff --git a/chrome/browser/ui/webui/help/test_version_updater.cc b/chrome/browser/ui/webui/help/test_version_updater.cc index b159078..32e2acd2 100644 --- a/chrome/browser/ui/webui/help/test_version_updater.cc +++ b/chrome/browser/ui/webui/help/test_version_updater.cc
@@ -8,7 +8,7 @@ TestVersionUpdater::~TestVersionUpdater() = default; -void TestVersionUpdater::CheckForUpdate(const StatusCallback& callback, +void TestVersionUpdater::CheckForUpdate(StatusCallback callback, const PromoteCallback&) { callback.Run(status_, progress_, rollback_, powerwash_, version_, update_size_, message_);
diff --git a/chrome/browser/ui/webui/help/test_version_updater.h b/chrome/browser/ui/webui/help/test_version_updater.h index 8a521dd4..6de58a7a 100644 --- a/chrome/browser/ui/webui/help/test_version_updater.h +++ b/chrome/browser/ui/webui/help/test_version_updater.h
@@ -19,8 +19,7 @@ TestVersionUpdater(); ~TestVersionUpdater() override; - void CheckForUpdate(const StatusCallback& callback, - const PromoteCallback&) override; + void CheckForUpdate(StatusCallback callback, const PromoteCallback&) override; void SetReturnedStatus(Status status) { status_ = status; } @@ -34,7 +33,7 @@ void GetChannel(bool get_current_channel, const ChannelCallback& callback) override {} void GetEolInfo(EolInfoCallback callback) override {} - void SetUpdateOverCellularOneTimePermission(const StatusCallback& callback, + void SetUpdateOverCellularOneTimePermission(StatusCallback callback, const std::string& update_version, int64_t update_size) override {} #endif
diff --git a/chrome/browser/ui/webui/help/version_updater.h b/chrome/browser/ui/webui/help/version_updater.h index 937a28b3..1c1277b 100644 --- a/chrome/browser/ui/webui/help/version_updater.h +++ b/chrome/browser/ui/webui/help/version_updater.h
@@ -65,13 +65,13 @@ // |update_size| is the size of the available update in bytes and should be 0 // when update is not available. // |message| is a message explaining a failure. - typedef base::Callback<void(Status status, - int progress, - bool rollback, - bool powerwash, - const std::string& version, - int64_t update_size, - const base::string16& message)> + typedef base::RepeatingCallback<void(Status status, + int progress, + bool rollback, + bool powerwash, + const std::string& version, + int64_t update_size, + const base::string16& message)> StatusCallback; // Used to show or hide the promote UI elements. Mac-only. @@ -89,7 +89,7 @@ // |status_callback| is called for each status update. |promote_callback| // (which is only used on the Mac) can be used to show or hide the promote UI // elements. - virtual void CheckForUpdate(const StatusCallback& status_callback, + virtual void CheckForUpdate(StatusCallback status_callback, const PromoteCallback& promote_callback) = 0; #if defined(OS_MAC) @@ -115,7 +115,7 @@ // there's a new update available or a delta update becomes a full update with // a larger size. virtual void SetUpdateOverCellularOneTimePermission( - const StatusCallback& callback, + StatusCallback callback, const std::string& update_version, int64_t update_size) = 0; #endif
diff --git a/chrome/browser/ui/webui/help/version_updater_basic.cc b/chrome/browser/ui/webui/help/version_updater_basic.cc index 2a4c334..0b0a8c2 100644 --- a/chrome/browser/ui/webui/help/version_updater_basic.cc +++ b/chrome/browser/ui/webui/help/version_updater_basic.cc
@@ -7,9 +7,8 @@ #include "base/strings/string16.h" #include "chrome/browser/upgrade_detector/upgrade_detector.h" -void VersionUpdaterBasic::CheckForUpdate( - const StatusCallback& status_callback, - const PromoteCallback&) { +void VersionUpdaterBasic::CheckForUpdate(StatusCallback status_callback, + const PromoteCallback&) { const Status status = UpgradeDetector::GetInstance()->notify_upgrade() ? NEARLY_UPDATED : DISABLED;
diff --git a/chrome/browser/ui/webui/help/version_updater_basic.h b/chrome/browser/ui/webui/help/version_updater_basic.h index 8fbb47a..d5cc437 100644 --- a/chrome/browser/ui/webui/help/version_updater_basic.h +++ b/chrome/browser/ui/webui/help/version_updater_basic.h
@@ -13,8 +13,8 @@ class VersionUpdaterBasic : public VersionUpdater { public: // VersionUpdater implementation. - void CheckForUpdate(const StatusCallback& callback, - const PromoteCallback&) override; + void CheckForUpdate(StatusCallback callback, const PromoteCallback&) override; + protected: friend class VersionUpdater;
diff --git a/chrome/browser/ui/webui/help/version_updater_chromeos.cc b/chrome/browser/ui/webui/help/version_updater_chromeos.cc index 3651443..bf58f02 100644 --- a/chrome/browser/ui/webui/help/version_updater_chromeos.cc +++ b/chrome/browser/ui/webui/help/version_updater_chromeos.cc
@@ -136,11 +136,11 @@ return new VersionUpdaterCros(web_contents); } -void VersionUpdaterCros::GetUpdateStatus(const StatusCallback& callback) { - callback_ = callback; +void VersionUpdaterCros::GetUpdateStatus(StatusCallback callback) { + callback_ = std::move(callback); // User is not actively checking for updates. - if (!EnsureCanUpdate(false /* interactive */, callback)) + if (!EnsureCanUpdate(false /* interactive */, callback_)) return; UpdateEngineClient* update_engine_client = @@ -152,12 +152,12 @@ DBusThreadManager::Get()->GetUpdateEngineClient()->GetLastStatus()); } -void VersionUpdaterCros::CheckForUpdate(const StatusCallback& callback, +void VersionUpdaterCros::CheckForUpdate(StatusCallback callback, const PromoteCallback&) { - callback_ = callback; + callback_ = std::move(callback); // User is actively checking for updates. - if (!EnsureCanUpdate(true /* interactive */, callback)) + if (!EnsureCanUpdate(true /* interactive */, callback_)) return; UpdateEngineClient* update_engine_client = @@ -195,10 +195,10 @@ } void VersionUpdaterCros::SetUpdateOverCellularOneTimePermission( - const StatusCallback& callback, + StatusCallback callback, const std::string& update_version, int64_t update_size) { - callback_ = callback; + callback_ = std::move(callback); DBusThreadManager::Get() ->GetUpdateEngineClient() ->SetUpdateOverCellularOneTimePermission(
diff --git a/chrome/browser/ui/webui/help/version_updater_chromeos.h b/chrome/browser/ui/webui/help/version_updater_chromeos.h index 0578815..39ac0f4 100644 --- a/chrome/browser/ui/webui/help/version_updater_chromeos.h +++ b/chrome/browser/ui/webui/help/version_updater_chromeos.h
@@ -19,19 +19,18 @@ public chromeos::UpdateEngineClient::Observer { public: // VersionUpdater implementation. - void CheckForUpdate(const StatusCallback& callback, - const PromoteCallback&) override; + void CheckForUpdate(StatusCallback callback, const PromoteCallback&) override; void SetChannel(const std::string& channel, bool is_powerwash_allowed) override; void GetChannel(bool get_current_channel, const ChannelCallback& callback) override; void GetEolInfo(EolInfoCallback callback) override; - void SetUpdateOverCellularOneTimePermission(const StatusCallback& callback, + void SetUpdateOverCellularOneTimePermission(StatusCallback callback, const std::string& update_version, int64_t update_size) override; // Gets the last update status, without triggering a new check or download. - void GetUpdateStatus(const StatusCallback& callback); + void GetUpdateStatus(StatusCallback callback); protected: friend class VersionUpdater;
diff --git a/chrome/browser/ui/webui/help/version_updater_chromeos_unittest.cc b/chrome/browser/ui/webui/help/version_updater_chromeos_unittest.cc index 999a1a6..c0b1623 100644 --- a/chrome/browser/ui/webui/help/version_updater_chromeos_unittest.cc +++ b/chrome/browser/ui/webui/help/version_updater_chromeos_unittest.cc
@@ -126,7 +126,7 @@ EXPECT_EQ(0, fake_update_engine_client_->request_update_check_call_count()); // IDLE -> DOWNLOADING transition after update check. - version_updater_->CheckForUpdate(base::Bind(&CheckNotification), + version_updater_->CheckForUpdate(base::BindRepeating(&CheckNotification), VersionUpdater::PromoteCallback()); EXPECT_EQ(1, fake_update_engine_client_->request_update_check_call_count()); @@ -150,7 +150,7 @@ fake_update_engine_client_->NotifyObserversThatStatusChanged(status); } - version_updater_->CheckForUpdate(base::Bind(&CheckNotification), + version_updater_->CheckForUpdate(base::BindRepeating(&CheckNotification), VersionUpdater::PromoteCallback()); EXPECT_EQ(1, fake_update_engine_client_->request_update_check_call_count()); @@ -172,7 +172,7 @@ TEST_F(VersionUpdaterCrosTest, InteractiveCellularUpdateAllowed) { SetCellularService(); EXPECT_EQ(0, fake_update_engine_client_->request_update_check_call_count()); - version_updater_->CheckForUpdate(base::Bind(&CheckNotification), + version_updater_->CheckForUpdate(base::BindRepeating(&CheckNotification), VersionUpdater::PromoteCallback()); EXPECT_EQ(1, fake_update_engine_client_->request_update_check_call_count()); } @@ -185,7 +185,7 @@ const std::string& update_version = "9999.0.0"; const int64_t update_size = 99999; version_updater_->SetUpdateOverCellularOneTimePermission( - base::Bind(&CheckNotification), update_version, update_size); + base::BindRepeating(&CheckNotification), update_version, update_size); EXPECT_EQ(1, fake_update_engine_client_->request_update_check_call_count()); }
diff --git a/chrome/browser/ui/webui/help/version_updater_mac.h b/chrome/browser/ui/webui/help/version_updater_mac.h index f00e08b..eab161d 100644 --- a/chrome/browser/ui/webui/help/version_updater_mac.h +++ b/chrome/browser/ui/webui/help/version_updater_mac.h
@@ -27,7 +27,7 @@ class VersionUpdaterMac : public VersionUpdater { public: // VersionUpdater implementation. - void CheckForUpdate(const StatusCallback& status_callback, + void CheckForUpdate(StatusCallback status_callback, const PromoteCallback& promote_callback) override; void PromoteUpdater() const override;
diff --git a/chrome/browser/ui/webui/help/version_updater_mac.mm b/chrome/browser/ui/webui/help/version_updater_mac.mm index 0cdd1f8..5818db0d 100644 --- a/chrome/browser/ui/webui/help/version_updater_mac.mm +++ b/chrome/browser/ui/webui/help/version_updater_mac.mm
@@ -78,7 +78,7 @@ } void UpdateStatusFromChromiumUpdater( - const VersionUpdater::StatusCallback& status_callback, + VersionUpdater::StatusCallback status_callback, updater::UpdateService::UpdateState update_state) { VersionUpdater::Status status = VersionUpdater::Status::CHECKING; int progress = 0; @@ -136,17 +136,17 @@ VersionUpdaterMac::~VersionUpdaterMac() {} void VersionUpdaterMac::CheckForUpdate( - const StatusCallback& status_callback, + StatusCallback status_callback, const PromoteCallback& promote_callback) { #if BUILDFLAG(ENABLE_CHROMIUM_UPDATER) if (!update_client_) update_client_ = BrowserUpdaterClient::Create(); - update_client_->CheckForUpdate( - base::BindRepeating(&UpdateStatusFromChromiumUpdater, status_callback)); + update_client_->CheckForUpdate(base::BindRepeating( + &UpdateStatusFromChromiumUpdater, std::move(status_callback))); return; #else - status_callback_ = status_callback; + status_callback_ = std::move(status_callback); promote_callback_ = promote_callback; KeystoneGlue* keystone_glue = [KeystoneGlue defaultKeystoneGlue];
diff --git a/chrome/browser/ui/webui/help/version_updater_win.cc b/chrome/browser/ui/webui/help/version_updater_win.cc index be910614..9101776 100644 --- a/chrome/browser/ui/webui/help/version_updater_win.cc +++ b/chrome/browser/ui/webui/help/version_updater_win.cc
@@ -26,10 +26,10 @@ VersionUpdaterWin::~VersionUpdaterWin() { } -void VersionUpdaterWin::CheckForUpdate(const StatusCallback& callback, +void VersionUpdaterWin::CheckForUpdate(StatusCallback callback, const PromoteCallback&) { // There is no supported integration with Google Update for Chromium. - callback_ = callback; + callback_ = std::move(callback); callback_.Run(CHECKING, 0, false, false, std::string(), 0, base::string16()); DoBeginUpdateCheck(false /* !install_update_if_possible */);
diff --git a/chrome/browser/ui/webui/help/version_updater_win.h b/chrome/browser/ui/webui/help/version_updater_win.h index f225a23..94398857 100644 --- a/chrome/browser/ui/webui/help/version_updater_win.h +++ b/chrome/browser/ui/webui/help/version_updater_win.h
@@ -25,8 +25,7 @@ ~VersionUpdaterWin() override; // VersionUpdater: - void CheckForUpdate(const StatusCallback& callback, - const PromoteCallback&) override; + void CheckForUpdate(StatusCallback callback, const PromoteCallback&) override; // UpdateCheckDelegate: void OnUpdateCheckComplete(const base::string16& new_version) override;
diff --git a/chrome/browser/ui/webui/settings/about_handler.cc b/chrome/browser/ui/webui/settings/about_handler.cc index d4522c4e..9a55940 100644 --- a/chrome/browser/ui/webui/settings/about_handler.cc +++ b/chrome/browser/ui/webui/settings/about_handler.cc
@@ -470,7 +470,8 @@ if (user_manager::UserManager::Get()->IsCurrentUserOwner()) { // Check for update after switching release channel. version_updater_->CheckForUpdate( - base::Bind(&AboutHandler::SetUpdateStatus, base::Unretained(this)), + base::BindRepeating(&AboutHandler::SetUpdateStatus, + base::Unretained(this)), VersionUpdater::PromoteCallback()); } } @@ -570,7 +571,8 @@ void AboutHandler::RequestUpdateOverCellular(const std::string& update_version, int64_t update_size) { version_updater_->SetUpdateOverCellularOneTimePermission( - base::Bind(&AboutHandler::SetUpdateStatus, base::Unretained(this)), + base::BindRepeating(&AboutHandler::SetUpdateStatus, + base::Unretained(this)), update_version, update_size); } @@ -625,9 +627,11 @@ void AboutHandler::RequestUpdate() { version_updater_->CheckForUpdate( - base::Bind(&AboutHandler::SetUpdateStatus, base::Unretained(this)), + base::BindRepeating(&AboutHandler::SetUpdateStatus, + base::Unretained(this)), #if defined(OS_MAC) - base::Bind(&AboutHandler::SetPromotionState, base::Unretained(this))); + base::BindRepeating(&AboutHandler::SetPromotionState, + base::Unretained(this))); #else VersionUpdater::PromoteCallback()); #endif // OS_MAC
diff --git a/chrome/browser/ui/webui/settings/safety_check_handler.cc b/chrome/browser/ui/webui/settings/safety_check_handler.cc index 0d00091..39e80de 100644 --- a/chrome/browser/ui/webui/settings/safety_check_handler.cc +++ b/chrome/browser/ui/webui/settings/safety_check_handler.cc
@@ -388,8 +388,8 @@ void SafetyCheckHandler::CheckUpdates() { // Usage of base::Unretained(this) is safe, because we own `version_updater_`. version_updater_->CheckForUpdate( - base::Bind(&SafetyCheckHandler::OnVersionUpdaterResult, - base::Unretained(this)), + base::BindRepeating(&SafetyCheckHandler::OnVersionUpdaterResult, + base::Unretained(this)), VersionUpdater::PromoteCallback()); }
diff --git a/chrome/browser/ui/webui/settings/safety_check_handler_unittest.cc b/chrome/browser/ui/webui/settings/safety_check_handler_unittest.cc index 6e291145..a8eb9e7 100644 --- a/chrome/browser/ui/webui/settings/safety_check_handler_unittest.cc +++ b/chrome/browser/ui/webui/settings/safety_check_handler_unittest.cc
@@ -99,7 +99,7 @@ public: ~TestDestructionVersionUpdater() override { destructor_invoked_ = true; } - void CheckForUpdate(const StatusCallback& callback, + void CheckForUpdate(StatusCallback callback, const PromoteCallback&) override {} static bool GetDestructorInvoked() { return destructor_invoked_; }
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt index 19f2c9b..b5faf77 100644 --- a/chrome/build/linux.pgo.txt +++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@ -chrome-linux-master-1608332413-e74bf19c468b4f240b918ee99bd718a9ec1e581b.profdata +chrome-linux-master-1608573074-24f850c78c572e5bf0ca93d4771b7966907fde32.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt index b391599..9d84b146 100644 --- a/chrome/build/mac.pgo.txt +++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@ -chrome-mac-master-1608529993-82d6d40e289e60063d3bd5c2eebe97dc9bb53838.profdata +chrome-mac-master-1608573074-c037592ba2f8332cc5c933010a8eab8414fcad34.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt index 41d839e..afce3112 100644 --- a/chrome/build/win64.pgo.txt +++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@ -chrome-win64-master-1608508387-093abbef3f1d2d94b454a0cdd869bb44780245e6.profdata +chrome-win64-master-1608560380-1035bf8a36b405614508f1a548443341a120ef85.profdata
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index c4ce2aa3..a4ee1117 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -4309,6 +4309,7 @@ "../browser/signin/process_dice_header_delegate_impl_unittest.cc", "../browser/signin/signin_manager_unittest.cc", "../browser/ui/views/profiles/dice_web_signin_interception_bubble_view_unittest.cc", + "../browser/ui/views/profiles/profile_customization_bubble_sync_controller_unittest.cc", "../browser/ui/webui/signin/dice_turn_sync_on_helper_unittest.cc", ] }
diff --git a/chromeos/components/diagnostics_ui/backend/system_routine_controller.cc b/chromeos/components/diagnostics_ui/backend/system_routine_controller.cc index c389ede..49d14b2 100644 --- a/chromeos/components/diagnostics_ui/backend/system_routine_controller.cc +++ b/chromeos/components/diagnostics_ui/backend/system_routine_controller.cc
@@ -34,6 +34,7 @@ constexpr uint32_t kRoutineResultRefreshIntervalInSeconds = 1; constexpr char kChargePercentKey[] = "chargePercent"; +constexpr char kDischargePercentKey[] = "dischargePercent"; constexpr char kResultDetailsKey[] = "resultDetails"; mojom::RoutineResultInfoPtr ConstructStandardRoutineResultInfoPtr( @@ -197,7 +198,19 @@ inflight_routine_timer_ = std::make_unique<base::OneShotTimer>(); } -SystemRoutineController::~SystemRoutineController() = default; +SystemRoutineController::~SystemRoutineController() { + if (!inflight_routine_runner_) { + return; + } + + // Since SystemRoutineController is torn down at the same time as the + // frontend, there's no guarantee that the disconnect handler will be called. + // If there's a routine inflight, cancel it but do not pass a callback. + BindCrosHealthdDiagnosticsServiceIfNeccessary(); + diagnostics_service_->GetRoutineUpdate( + inflight_routine_id_, healthd::DiagnosticRoutineCommandEnum::kCancel, + /*should_include_output=*/false, base::DoNothing()); +} void SystemRoutineController::RunRoutine( mojom::RoutineType type, @@ -214,6 +227,9 @@ inflight_routine_runner_ = mojo::Remote<mojom::RoutineRunner>(std::move(runner)); + inflight_routine_runner_.set_disconnect_handler(base::BindOnce( + &SystemRoutineController::OnInflightRoutineRunnerDisconnected, + base::Unretained(this))); ExecuteRoutine(type); } @@ -566,7 +582,9 @@ } base::Optional<double> charge_percent_opt = - result_details_dict->FindDoubleKey(kChargePercentKey); + routine_type == mojom::RoutineType::kBatteryCharge + ? result_details_dict->FindDoubleKey(kChargePercentKey) + : result_details_dict->FindDoubleKey(kDischargePercentKey); if (!charge_percent_opt.has_value()) { OnPowerRoutineResult(routine_type, mojom::StandardRoutineResult::kExecutionError, @@ -621,8 +639,29 @@ } void SystemRoutineController::OnInflightRoutineRunnerDisconnected() { + // Reset `inflight_routine_runner_` since the other side of the pipe is + // already disconnected. inflight_routine_runner_.reset(); - // TODO(baileyberro): Implement routine cancellation. + + // Make a best effort attempt to remove the routine. + BindCrosHealthdDiagnosticsServiceIfNeccessary(); + diagnostics_service_->GetRoutineUpdate( + inflight_routine_id_, healthd::DiagnosticRoutineCommandEnum::kCancel, + /*should_include_output=*/false, + base::BindOnce(&SystemRoutineController::OnRoutineCancelAttempted, + base::Unretained(this))); +} + +void SystemRoutineController::OnRoutineCancelAttempted( + healthd::RoutineUpdatePtr update_ptr) { + const healthd::NonInteractiveRoutineUpdate* update = + GetNonInteractiveRoutineUpdate(*update_ptr); + + if (!update || + update->status != healthd::DiagnosticRoutineStatusEnum::kCancelled) { + DVLOG(2) << "Failed to cancel routine."; + return; + } } } // namespace diagnostics
diff --git a/chromeos/components/diagnostics_ui/backend/system_routine_controller.h b/chromeos/components/diagnostics_ui/backend/system_routine_controller.h index 9c80f33..77adbfd 100644 --- a/chromeos/components/diagnostics_ui/backend/system_routine_controller.h +++ b/chromeos/components/diagnostics_ui/backend/system_routine_controller.h
@@ -112,6 +112,9 @@ void OnInflightRoutineRunnerDisconnected(); + void OnRoutineCancelAttempted( + cros_healthd::mojom::RoutineUpdatePtr update_ptr); + // Keeps track of the id created by CrosHealthd for the currently running // routine. int32_t inflight_routine_id_ = kInvalidRoutineId;
diff --git a/chromeos/components/diagnostics_ui/backend/system_routine_controller_unittest.cc b/chromeos/components/diagnostics_ui/backend/system_routine_controller_unittest.cc index 625c822..6d9ecc5e 100644 --- a/chromeos/components/diagnostics_ui/backend/system_routine_controller_unittest.cc +++ b/chromeos/components/diagnostics_ui/backend/system_routine_controller_unittest.cc
@@ -13,6 +13,7 @@ #include "base/test/bind.h" #include "base/test/task_environment.h" #include "chromeos/dbus/cros_healthd/fake_cros_healthd_client.h" +#include "chromeos/dbus/cros_healthd/fake_cros_healthd_service.h" #include "chromeos/services/cros_healthd/public/mojom/cros_healthd.mojom.h" #include "chromeos/services/cros_healthd/public/mojom/cros_healthd_diagnostics.mojom.h" #include "mojo/public/cpp/system/platform_handle.h" @@ -26,6 +27,7 @@ namespace healthd = cros_healthd::mojom; constexpr char kChargePercentKey[] = "chargePercent"; +constexpr char kDischargePercentKey[] = "dischargePercent"; constexpr char kResultDetailsKey[] = "resultDetails"; void SetCrosHealthdRunRoutineResponse( @@ -96,9 +98,17 @@ time_elapsed_seconds); } -std::string ConstructPowerRoutineResultJson(double charge_percent) { +// Constructs the Power Routine Result json. If `charge_percent` is negative, +// the discharge field will be used. +std::string ConstructPowerRoutineResultJson(double charge_percent, + bool charge) { base::Value result_dict(base::Value::Type::DICTIONARY); - result_dict.SetKey(kChargePercentKey, base::Value(charge_percent)); + if (charge) { + result_dict.SetKey(kChargePercentKey, base::Value(charge_percent)); + + } else { + result_dict.SetKey(kDischargePercentKey, base::Value(charge_percent)); + } base::Value output_dict(base::Value::Type::DICTIONARY); output_dict.SetKey(kResultDetailsKey, std::move(result_dict)); @@ -144,8 +154,10 @@ } protected: - mojo::ScopedHandle CreateMojoHandleForPowerRoutine(double charge_percent) { - return CreateMojoHandle(ConstructPowerRoutineResultJson(charge_percent)); + mojo::ScopedHandle CreateMojoHandleForPowerRoutine(double charge_percent, + bool charge) { + return CreateMojoHandle( + ConstructPowerRoutineResultJson(charge_percent, charge)); } base::test::TaskEnvironment task_environment_{ @@ -422,7 +434,8 @@ SetNonInteractiveRoutineUpdateResponse( /*percent_complete=*/100, healthd::DiagnosticRoutineStatusEnum::kPassed, - CreateMojoHandleForPowerRoutine(expected_percent_charge)); + CreateMojoHandleForPowerRoutine(expected_percent_charge, + /*charge=*/true)); task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(31)); EXPECT_FALSE(routine_runner.result.is_null()); @@ -433,6 +446,39 @@ expected_time_elapsed_seconds)); } +TEST_F(SystemRoutineControllerTest, DischargeRoutineSuccess) { + SetRunRoutineResponse(/*id=*/1, + healthd::DiagnosticRoutineStatusEnum::kWaiting); + SetNonInteractiveRoutineUpdateResponse( + /*percent_complete=*/10, healthd::DiagnosticRoutineStatusEnum::kRunning, + mojo::ScopedHandle()); + + FakeRoutineRunner routine_runner; + system_routine_controller_->RunRoutine( + mojom::RoutineType::kBatteryDischarge, + routine_runner.receiver.BindNewPipeAndPassRemote()); + base::RunLoop().RunUntilIdle(); + + // Assert that the routine is not complete. + EXPECT_TRUE(routine_runner.result.is_null()); + + const uint8_t expected_percent_discharge = 5; + const uint32_t expected_time_elapsed_seconds = 30; + + SetNonInteractiveRoutineUpdateResponse( + /*percent_complete=*/100, healthd::DiagnosticRoutineStatusEnum::kPassed, + CreateMojoHandleForPowerRoutine(expected_percent_discharge, + /*charge=*/false)); + task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(31)); + + EXPECT_FALSE(routine_runner.result.is_null()); + VerifyRoutineResult( + *routine_runner.result, mojom::RoutineType::kBatteryDischarge, + ConstructPowerRoutineResult(mojom::StandardRoutineResult::kTestPassed, + expected_percent_discharge, + expected_time_elapsed_seconds)); +} + TEST_F(SystemRoutineControllerTest, AvailableRoutines) { SetAvailableRoutines({healthd::DiagnosticRoutineEnum::kFloatingPointAccuracy, healthd::DiagnosticRoutineEnum::kMemory, @@ -464,5 +510,75 @@ run_loop.Run(); } +TEST_F(SystemRoutineControllerTest, CancelRoutine) { + const int32_t expected_id = 1; + SetRunRoutineResponse(expected_id, + healthd::DiagnosticRoutineStatusEnum::kRunning); + + std::unique_ptr<FakeRoutineRunner> routine_runner = + std::make_unique<FakeRoutineRunner>(); + system_routine_controller_->RunRoutine( + mojom::RoutineType::kCpuStress, + routine_runner->receiver.BindNewPipeAndPassRemote()); + base::RunLoop().RunUntilIdle(); + + // Assert that the first routine is not complete. + EXPECT_TRUE(routine_runner->result.is_null()); + + // Update the status on cros_healthd. + SetNonInteractiveRoutineUpdateResponse( + /*percent_complete=*/0, healthd::DiagnosticRoutineStatusEnum::kCancelled, + mojo::ScopedHandle()); + + // Close the routine_runner + routine_runner.reset(); + base::RunLoop().RunUntilIdle(); + + // Verify that CrosHealthd is called with the correct parameters. + base::Optional<cros_healthd::FakeCrosHealthdService::RoutineUpdateParams> + update_params = + cros_healthd::FakeCrosHealthdClient::Get()->GetRoutineUpdateParams(); + + ASSERT_TRUE(update_params.has_value()); + EXPECT_EQ(expected_id, update_params->id); + EXPECT_EQ(healthd::DiagnosticRoutineCommandEnum::kCancel, + update_params->command); +} + +TEST_F(SystemRoutineControllerTest, CancelRoutineDtor) { + const int32_t expected_id = 2; + SetRunRoutineResponse(expected_id, + healthd::DiagnosticRoutineStatusEnum::kRunning); + + std::unique_ptr<FakeRoutineRunner> routine_runner = + std::make_unique<FakeRoutineRunner>(); + system_routine_controller_->RunRoutine( + mojom::RoutineType::kCpuStress, + routine_runner->receiver.BindNewPipeAndPassRemote()); + base::RunLoop().RunUntilIdle(); + + // Assert that the first routine is not complete. + EXPECT_TRUE(routine_runner->result.is_null()); + + // Update the status on cros_healthd. + SetNonInteractiveRoutineUpdateResponse( + /*percent_complete=*/0, healthd::DiagnosticRoutineStatusEnum::kCancelled, + mojo::ScopedHandle()); + + // Destroy the SystemRoutineController + system_routine_controller_.reset(); + base::RunLoop().RunUntilIdle(); + + // Verify that CrosHealthd is called with the correct parameters. + base::Optional<cros_healthd::FakeCrosHealthdService::RoutineUpdateParams> + update_params = + cros_healthd::FakeCrosHealthdClient::Get()->GetRoutineUpdateParams(); + + ASSERT_TRUE(update_params.has_value()); + EXPECT_EQ(expected_id, update_params->id); + EXPECT_EQ(healthd::DiagnosticRoutineCommandEnum::kCancel, + update_params->command); +} + } // namespace diagnostics } // namespace chromeos
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc index 6ebbe211..ea0a86f3 100644 --- a/chromeos/constants/chromeos_features.cc +++ b/chromeos/constants/chromeos_features.cc
@@ -168,6 +168,10 @@ const base::Feature kCdmFactoryDaemon{"CdmFactoryDaemon", base::FEATURE_DISABLED_BY_DEFAULT}; +// If enabled, send the LTE attach APN configuration to the modem. +const base::Feature kCellularUseAttachApn{"CellularUseAttachApn", + base::FEATURE_DISABLED_BY_DEFAULT}; + // Enables or disables entry point for child account sign in or creation. const base::Feature kChildSpecificSignin{"ChildSpecificSignin", base::FEATURE_ENABLED_BY_DEFAULT};
diff --git a/chromeos/constants/chromeos_features.h b/chromeos/constants/chromeos_features.h index fedbf41..b7fda07 100644 --- a/chromeos/constants/chromeos_features.h +++ b/chromeos/constants/chromeos_features.h
@@ -90,6 +90,8 @@ COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const base::Feature kCdmFactoryDaemon; COMPONENT_EXPORT(CHROMEOS_CONSTANTS) +extern const base::Feature kCellularUseAttachApn; +COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const base::Feature kChildSpecificSignin; COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const base::Feature kConnectivityDiagnosticsWebUi;
diff --git a/chromeos/network/network_device_handler_impl.cc b/chromeos/network/network_device_handler_impl.cc index 01aacf6..2ef4687 100644 --- a/chromeos/network/network_device_handler_impl.cc +++ b/chromeos/network/network_device_handler_impl.cc
@@ -22,6 +22,7 @@ #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" #include "base/values.h" +#include "chromeos/constants/chromeos_features.h" #include "chromeos/dbus/shill/shill_device_client.h" #include "chromeos/dbus/shill/shill_ipconfig_client.h" #include "chromeos/network/device_state.h" @@ -332,6 +333,7 @@ ApplyCellularAllowRoamingToShill(); ApplyMACAddressRandomizationToShill(); ApplyUsbEthernetMacAddressSourceToShill(); + ApplyUseAttachApnToShill(); } void NetworkDeviceHandlerImpl::DevicePropertiesUpdated( @@ -439,6 +441,27 @@ usb_ethernet_mac_address_source_, network_handler::ErrorCallback())); } +void NetworkDeviceHandlerImpl::ApplyUseAttachApnToShill() { + NetworkStateHandler::DeviceStateList list; + bool use_attach_apn = + base::FeatureList::IsEnabled(chromeos::features::kCellularUseAttachApn); + network_state_handler_->GetDeviceListByType(NetworkTypePattern::Cellular(), + &list); + if (list.empty()) { + NET_LOG(DEBUG) << "No cellular device available."; + return; + } + for (NetworkStateHandler::DeviceStateList::const_iterator it = list.begin(); + it != list.end(); ++it) { + const DeviceState* device_state = *it; + + SetDevicePropertyInternal(device_state->path(), + shill::kUseAttachAPNProperty, + base::Value(use_attach_apn), base::DoNothing(), + network_handler::ErrorCallback()); + } +} + void NetworkDeviceHandlerImpl::OnSetUsbEthernetMacAddressSourceError( const std::string& device_path, const std::string& device_mac_address,
diff --git a/chromeos/network/network_device_handler_impl.h b/chromeos/network/network_device_handler_impl.h index acae3b6..386ddf2 100644 --- a/chromeos/network/network_device_handler_impl.h +++ b/chromeos/network/network_device_handler_impl.h
@@ -138,6 +138,10 @@ // specified yet. void ApplyUsbEthernetMacAddressSourceToShill(); + // Applies the current value of the |cellular-use-attach-apn| flag to all + // existing cellular devices of Shill. + void ApplyUseAttachApnToShill(); + // Callback to be called on MAC address source change request failure. // The request was called on device with |device_path| path and // |device_mac_address| MAC address to change MAC address source to the new
diff --git a/chromeos/services/assistant/BUILD.gn b/chromeos/services/assistant/BUILD.gn index 7334fd360..680d850 100644 --- a/chromeos/services/assistant/BUILD.gn +++ b/chromeos/services/assistant/BUILD.gn
@@ -124,7 +124,6 @@ "//chromeos/resources", "//chromeos/services/assistant/proxy", "//chromeos/services/assistant/public/cpp/migration", - "//chromeos/services/assistant/public/cpp/migration", "//chromeos/services/libassistant", "//chromeos/services/network_config/public/mojom", "//chromeos/strings", @@ -235,6 +234,10 @@ "//testing/gmock", "//testing/gtest", ] + + if (enable_cros_libassistant) { + deps += [ "//chromeos/services/assistant/public/cpp/migration" ] + } } buildflag_header("buildflags") {
diff --git a/chromeos/services/assistant/assistant_manager_service_impl_unittest.cc b/chromeos/services/assistant/assistant_manager_service_impl_unittest.cc index a95b9b3..13d4b23d 100644 --- a/chromeos/services/assistant/assistant_manager_service_impl_unittest.cc +++ b/chromeos/services/assistant/assistant_manager_service_impl_unittest.cc
@@ -196,6 +196,13 @@ } void Stop() override { service_->Unbind(); } + void SetInitializeCallback( + base::OnceCallback<void(assistant_client::AssistantManager*, + assistant_client::AssistantManagerInternal*)> + callback) override { + service_->service_controller().SetInitializeCallback(std::move(callback)); + } + private: FakeLibassistantService* service_; };
diff --git a/chromeos/services/assistant/libassistant_service_host_impl.cc b/chromeos/services/assistant/libassistant_service_host_impl.cc index 68a2d4d..d1fc222 100644 --- a/chromeos/services/assistant/libassistant_service_host_impl.cc +++ b/chromeos/services/assistant/libassistant_service_host_impl.cc
@@ -5,6 +5,7 @@ #include "chromeos/services/assistant/libassistant_service_host_impl.h" #include "base/check.h" +#include "base/synchronization/lock.h" #include "chromeos/services/libassistant/libassistant_service.h" namespace chromeos { @@ -23,14 +24,37 @@ void LibassistantServiceHostImpl::Launch( mojo::PendingReceiver<LibassistantServiceMojom> receiver) { DCHECK_EQ(libassistant_service_, nullptr); + + base::AutoLock lock(libassistant_service_lock_); libassistant_service_ = std::make_unique<chromeos::libassistant::LibassistantService>( std::move(receiver), platform_api_, delegate_); + + if (pending_initialize_callback_) { + libassistant_service_->SetInitializeCallback( + std::move(pending_initialize_callback_)); + } } void LibassistantServiceHostImpl::Stop() { + base::AutoLock lock(libassistant_service_lock_); libassistant_service_ = nullptr; } +void LibassistantServiceHostImpl::SetInitializeCallback( + InitializeCallback callback) { + base::AutoLock lock(libassistant_service_lock_); + + if (libassistant_service_) { + libassistant_service_->SetInitializeCallback(std::move(callback)); + } else { + // Launch() is called on the background thread and SetInitializeCallback() + // on the main thread, so it is possible we come here before Launch() has + // had a chance to run. If that happens we remember the callback and pass + // it to the service in Launch(). + pending_initialize_callback_ = std::move(callback); + } +} + } // namespace assistant } // namespace chromeos
diff --git a/chromeos/services/assistant/libassistant_service_host_impl.h b/chromeos/services/assistant/libassistant_service_host_impl.h index eb115ff..6337092 100644 --- a/chromeos/services/assistant/libassistant_service_host_impl.h +++ b/chromeos/services/assistant/libassistant_service_host_impl.h
@@ -7,6 +7,7 @@ #include <memory> +#include "base/synchronization/lock.h" #include "chromeos/services/assistant/proxy/libassistant_service_host.h" namespace assistant_client { @@ -14,12 +15,22 @@ } // namespace assistant_client namespace chromeos { +namespace libassistant { +class LibassistantService; +} // namespace libassistant +} // namespace chromeos + +namespace chromeos { namespace assistant { class AssistantManagerServiceDelegate; class LibassistantServiceHostImpl : public LibassistantServiceHost { public: + using InitializeCallback = + base::OnceCallback<void(assistant_client::AssistantManager*, + assistant_client::AssistantManagerInternal*)>; + LibassistantServiceHostImpl(assistant_client::PlatformApi* platform_api, AssistantManagerServiceDelegate* delegate); LibassistantServiceHostImpl(LibassistantServiceHostImpl&) = delete; @@ -30,6 +41,7 @@ void Launch( mojo::PendingReceiver<LibassistantServiceMojom> receiver) override; void Stop() override; + void SetInitializeCallback(InitializeCallback) override; private: // Owned by |AssistantManagerServiceImpl| which also owns |this|. @@ -37,7 +49,14 @@ // Owned by |AssistantManagerServiceImpl| which also owns |this|. AssistantManagerServiceDelegate* const delegate_; - std::unique_ptr<LibassistantServiceMojom> libassistant_service_; + // Protects access to |libassistant_service_|. This is required because the + // service will be launched/stopped on a background thread, where the other + // methods will be called from the main thread. + base::Lock libassistant_service_lock_; + std::unique_ptr<chromeos::libassistant::LibassistantService> + libassistant_service_; + // Used when SetInitializeCallback() is called before Launch(). + InitializeCallback pending_initialize_callback_; }; } // namespace assistant
diff --git a/chromeos/services/assistant/proxy/assistant_proxy.cc b/chromeos/services/assistant/proxy/assistant_proxy.cc index a3feb582..6cee87f 100644 --- a/chromeos/services/assistant/proxy/assistant_proxy.cc +++ b/chromeos/services/assistant/proxy/assistant_proxy.cc
@@ -28,8 +28,8 @@ libassistant_service_host_ = host; LaunchLibassistantService(); - service_controller_proxy_ = std::make_unique<ServiceControllerProxy>( - background_task_runner(), BindServiceController()); + service_controller_proxy_ = + std::make_unique<ServiceControllerProxy>(host, BindServiceController()); } void AssistantProxy::LaunchLibassistantService() {
diff --git a/chromeos/services/assistant/proxy/libassistant_service_host.h b/chromeos/services/assistant/proxy/libassistant_service_host.h index 7097cff5..6191d37d 100644 --- a/chromeos/services/assistant/proxy/libassistant_service_host.h +++ b/chromeos/services/assistant/proxy/libassistant_service_host.h
@@ -9,6 +9,11 @@ #include "mojo/public/cpp/bindings/pending_receiver.h" +namespace assistant_client { +class AssistantManager; +class AssistantManagerInternal; +} // namespace assistant_client + namespace chromeos { namespace libassistant { namespace mojom { @@ -36,9 +41,18 @@ // |Stop| is called. virtual void Launch( mojo::PendingReceiver<LibassistantServiceMojom> receiver) = 0; - // Stop the mojom service. + // Stop the mojom service. virtual void Stop() = 0; + + // Set a callback to initialize |AssistantManager| and + // |AssistantManagerInternal|. This callback will be invoked before + // AssistantManager::Start() is called. This is temporary until we've migrated + // all initialization code to the mojom service. + virtual void SetInitializeCallback( + base::OnceCallback< + void(assistant_client::AssistantManager*, + assistant_client::AssistantManagerInternal*)>) = 0; }; } // namespace assistant
diff --git a/chromeos/services/assistant/proxy/service_controller_proxy.cc b/chromeos/services/assistant/proxy/service_controller_proxy.cc index 0dc1660..8390fc7 100644 --- a/chromeos/services/assistant/proxy/service_controller_proxy.cc +++ b/chromeos/services/assistant/proxy/service_controller_proxy.cc
@@ -11,6 +11,7 @@ #include "chromeos/assistant/internal/cros_display_connection.h" #include "chromeos/assistant/internal/internal_util.h" #include "chromeos/constants/chromeos_features.h" +#include "chromeos/services/assistant/proxy/libassistant_service_host.h" #include "chromeos/services/assistant/public/cpp/features.h" #include "chromeos/services/assistant/public/cpp/migration/assistant_manager_service_delegate.h" #include "chromeos/services/assistant/public/cpp/migration/libassistant_v1_api.h" @@ -36,6 +37,25 @@ constexpr char kServersideOpenAppExperimentId[] = "39651593"; constexpr char kServersideResponseProcessingV2ExperimentId[] = "1793869"; +struct StartArguments { + StartArguments() = default; + StartArguments(StartArguments&&) = default; + StartArguments& operator=(StartArguments&&) = default; + ~StartArguments() = default; + + assistant_client::ActionModule* action_module; + assistant_client::FuchsiaApiDelegate* fuchsia_api_delegate; + assistant_client::AssistantManagerDelegate* assistant_manager_delegate; + assistant_client::ConversationStateListener* conversation_state_listener; + assistant_client::DeviceStateListener* device_state_listener; + CrosDisplayConnection* display_connection; + std::string libassistant_config; + std::string locale; + std::string locale_override; + bool spoken_feedback_enabled; + ServiceControllerProxy::AuthTokens auth_tokens; +}; + void FillServerExperimentIds(std::vector<std::string>* server_experiment_ids) { if (base::FeatureList::IsEnabled(kChromeOSAssistantDogfood)) { server_experiment_ids->emplace_back(kServersideDogfoodExperimentId); @@ -58,17 +78,59 @@ } } +void SetInternalOptions( + assistant_client::AssistantManagerInternal* assistant_manager_internal, + const std::string& locale, + bool spoken_feedback_enabled) { + auto* internal_options = + assistant_manager_internal->CreateDefaultInternalOptions(); + SetAssistantOptions(internal_options, locale, spoken_feedback_enabled); + + internal_options->SetClientControlEnabled( + assistant::features::IsRoutinesEnabled()); + + if (!features::IsVoiceMatchDisabled()) + internal_options->EnableRequireVoiceMatchVerification(); + + assistant_manager_internal->SetOptions(*internal_options, [](bool success) { + DVLOG(2) << "set options: " << success; + }); +} + +// TODO(b/171748795): This should all be migrated to the mojom service, which +// should be responsible for the complete creation of the Libassistant +// objects. +// Note: this method will be called from the mojom (background) thread. +void InitializeAssistantManager( + StartArguments arguments, + assistant_client::AssistantManager* assistant_manager, + assistant_client::AssistantManagerInternal* assistant_manager_internal) { + SetInternalOptions(assistant_manager_internal, arguments.locale, + arguments.spoken_feedback_enabled); + assistant_manager_internal->SetDisplayConnection( + arguments.display_connection); + assistant_manager_internal->SetLocaleOverride(arguments.locale_override); + assistant_manager_internal->RegisterActionModule(arguments.action_module); + assistant_manager_internal->SetAssistantManagerDelegate( + arguments.assistant_manager_delegate); + assistant_manager_internal->GetFuchsiaApiHelperOrDie()->SetFuchsiaApiDelegate( + arguments.fuchsia_api_delegate); + assistant_manager->AddConversationStateListener( + arguments.conversation_state_listener); + assistant_manager->AddDeviceStateListener(arguments.device_state_listener); + SetServerExperiments(assistant_manager_internal); + assistant_manager->SetAuthTokens(arguments.auth_tokens); +} + } // namespace ServiceControllerProxy::ServiceControllerProxy( - scoped_refptr<base::SingleThreadTaskRunner> background_task_runner, + LibassistantServiceHost* host, mojo::PendingRemote<chromeos::libassistant::mojom::ServiceController> client) - : background_task_runner_(std::move(background_task_runner)), + : host_(host), service_controller_remote_(std::move(client)), state_observer_receiver_(this) { - DCHECK(background_task_runner_); - service_controller_remote_->AddAndFireStateObserver( state_observer_receiver_.BindNewPipeAndPassRemote()); } @@ -92,24 +154,31 @@ DCHECK_EQ(state_, State::kStopped); state_ = State::kStarting; + pending_display_connection_ = std::make_unique<CrosDisplayConnection>( + event_observer, /*feedback_ui_enabled=*/true, + assistant::features::IsMediaSessionIntegrationEnabled()); + // The mojom service will create the |AssistantManager|. service_controller_remote_->Start(libassistant_config); - // We need to finalize (and start) the |AssistantManager| once it's created, - // so we have to store all the required arguments for that. + // We need to initialize the |AssistantManager| once it's created and before + // it's started, so we register a callback to do just that. StartArguments arguments; arguments.action_module = action_module; arguments.fuchsia_api_delegate = fuchsia_api_delegate; arguments.assistant_manager_delegate = assistant_manager_delegate; arguments.conversation_state_listener = conversation_state_listener; arguments.device_state_listener = device_state_listener; - arguments.event_observer = event_observer; + arguments.display_connection = pending_display_connection_.get(); arguments.locale = locale; arguments.locale_override = locale_override; arguments.spoken_feedback_enabled = spoken_feedback_enabled; arguments.auth_tokens = auth_tokens; - arguments.done_callback = std::move(done_callback); - pending_start_argument_ = std::move(arguments); + + host_->SetInitializeCallback( + base::BindOnce(InitializeAssistantManager, std::move(arguments))); + + on_start_done_callback_ = std::move(done_callback); } void ServiceControllerProxy::Stop() { @@ -126,21 +195,8 @@ void ServiceControllerProxy::UpdateInternalOptions( const std::string& locale, bool spoken_feedback_enabled) { - // NOTE: this method is called on multiple threads, it needs to be - // thread-safe. - auto* internal_options = - assistant_manager_internal()->CreateDefaultInternalOptions(); - SetAssistantOptions(internal_options, locale, spoken_feedback_enabled); - - internal_options->SetClientControlEnabled( - assistant::features::IsRoutinesEnabled()); - - if (!features::IsVoiceMatchDisabled()) - internal_options->EnableRequireVoiceMatchVerification(); - - assistant_manager_internal()->SetOptions(*internal_options, [](bool success) { - DVLOG(2) << "set options: " << success; - }); + SetInternalOptions(assistant_manager_internal(), locale, + spoken_feedback_enabled); } void ServiceControllerProxy::SetAuthTokens(const AuthTokens& tokens) { @@ -181,45 +237,13 @@ } void ServiceControllerProxy::FinishCreatingAssistant() { - // TODO(b/171748795): This should all be migrated to the mojom service, which - // should be responsible for the complete creation of the Libassistant - // objects. - DCHECK(pending_start_argument_.has_value()); - - auto arguments = std::move(pending_start_argument_.value()); - - display_connection_ = std::make_unique<CrosDisplayConnection>( - arguments.event_observer, /*feedback_ui_enabled=*/true, - assistant::features::IsMediaSessionIntegrationEnabled()); - - UpdateInternalOptions(arguments.locale, arguments.spoken_feedback_enabled); - - assistant_manager_internal()->SetDisplayConnection(display_connection()); - assistant_manager_internal()->SetLocaleOverride(arguments.locale_override); - assistant_manager_internal()->RegisterActionModule(arguments.action_module); - assistant_manager_internal()->SetAssistantManagerDelegate( - arguments.assistant_manager_delegate); - assistant_manager_internal() - ->GetFuchsiaApiHelperOrDie() - ->SetFuchsiaApiDelegate(arguments.fuchsia_api_delegate); - assistant_manager()->AddConversationStateListener( - arguments.conversation_state_listener); - assistant_manager()->AddDeviceStateListener(arguments.device_state_listener); - SetServerExperiments(assistant_manager_internal()); - SetAuthTokens(arguments.auth_tokens); - - assistant_manager()->Start(); + DCHECK(on_start_done_callback_.has_value()); + DCHECK_NE(pending_display_connection_, nullptr); state_ = State::kStarted; - std::move(arguments.done_callback).Run(); + display_connection_ = std::move(pending_display_connection_); + std::move(on_start_done_callback_.value()).Run(); } -ServiceControllerProxy::StartArguments::StartArguments() = default; -ServiceControllerProxy::StartArguments::StartArguments(StartArguments&&) = - default; -ServiceControllerProxy::StartArguments& -ServiceControllerProxy::StartArguments::operator=(StartArguments&&) = default; -ServiceControllerProxy::StartArguments::~StartArguments() = default; - } // namespace assistant } // namespace chromeos
diff --git a/chromeos/services/assistant/proxy/service_controller_proxy.h b/chromeos/services/assistant/proxy/service_controller_proxy.h index d98f1a1..83a1a2b 100644 --- a/chromeos/services/assistant/proxy/service_controller_proxy.h +++ b/chromeos/services/assistant/proxy/service_controller_proxy.h
@@ -11,9 +11,7 @@ #include <vector> #include "base/check.h" -#include "base/memory/scoped_refptr.h" #include "base/memory/weak_ptr.h" -#include "base/single_thread_task_runner.h" #include "chromeos/services/libassistant/public/mojom/service_controller.mojom.h" #include "mojo/public/cpp/bindings/remote.h" @@ -34,6 +32,7 @@ class AssistantEventObserver; class CrosDisplayConnection; +class LibassistantServiceHost; // Component managing the lifecycle of Libassistant, // exposing methods to start/stop and configure Libassistant. @@ -43,7 +42,7 @@ using AuthTokens = std::vector<std::pair<std::string, std::string>>; ServiceControllerProxy( - scoped_refptr<base::SingleThreadTaskRunner> background_task_runner, + LibassistantServiceHost* host, mojo::PendingRemote<chromeos::libassistant::mojom::ServiceController> client); @@ -52,8 +51,6 @@ ~ServiceControllerProxy() override; // Can not be invoked before Start() has finished. - // Both LibAssistant and Chrome threads may access |display_connection|. - // |display_connection| is thread safe. CrosDisplayConnection* display_connection() { DCHECK(display_connection_); return display_connection_.get(); @@ -112,25 +109,6 @@ // Can not be invoked before Start() has finished. assistant_client::AssistantManagerInternal* assistant_manager_internal(); - struct StartArguments { - StartArguments(); - StartArguments(StartArguments&&); - StartArguments& operator=(StartArguments&&); - ~StartArguments(); - assistant_client::ActionModule* action_module; - assistant_client::FuchsiaApiDelegate* fuchsia_api_delegate; - assistant_client::AssistantManagerDelegate* assistant_manager_delegate; - assistant_client::ConversationStateListener* conversation_state_listener; - assistant_client::DeviceStateListener* device_state_listener; - AssistantEventObserver* event_observer; - std::string libassistant_config; - std::string locale; - std::string locale_override; - bool spoken_feedback_enabled; - AuthTokens auth_tokens; - base::OnceClosure done_callback; - }; - void FinishCreatingAssistant(); // libassistant::mojom::StateObserver implementation: @@ -141,20 +119,22 @@ // Used internally for consistency checks. State state_ = State::kStopped; - scoped_refptr<base::SingleThreadTaskRunner> background_task_runner_; + // Owned by |AssistantManagerServiceImpl| which (indirectly) also owns us. + LibassistantServiceHost* const host_; mojo::Remote<chromeos::libassistant::mojom::ServiceController> service_controller_remote_; mojo::Receiver<chromeos::libassistant::mojom::StateObserver> state_observer_receiver_; - // Arguments passed to the last Start() call. - // Used to finish starting Libassistant after the Libassistant mojom service - // signals it has created the required objects. - // Unset once we've finished starting. - base::Optional<StartArguments> pending_start_argument_; + // Callback passed to Start(). Will be invoked once the Libassistant service + // has started. + base::Optional<base::OnceClosure> on_start_done_callback_; std::unique_ptr<CrosDisplayConnection> display_connection_; + // Populated when we're starting but not started yet, so after Start() has + // been called but before the mojom service signalled it has started. + std::unique_ptr<CrosDisplayConnection> pending_display_connection_; base::WeakPtrFactory<ServiceControllerProxy> weak_factory_{this}; };
diff --git a/chromeos/services/assistant/test_support/fake_service_controller.cc b/chromeos/services/assistant/test_support/fake_service_controller.cc index 6feaaff..235d54b 100644 --- a/chromeos/services/assistant/test_support/fake_service_controller.cc +++ b/chromeos/services/assistant/test_support/fake_service_controller.cc
@@ -4,6 +4,7 @@ #include "chromeos/services/assistant/test_support/fake_service_controller.h" +#include "chromeos/services/assistant/public/cpp/migration/libassistant_v1_api.h" #include "testing/gtest/include/gtest/gtest.h" namespace chromeos { @@ -35,6 +36,10 @@ state_observers_.Clear(); } +void FakeServiceController::SetInitializeCallback(InitializeCallback callback) { + initialize_callback_ = std::move(callback); +} + void FakeServiceController::BlockStartCalls() { // This lock will be release in |UnblockStartCalls|. start_mutex_.lock(); @@ -50,6 +55,12 @@ // Will block if |BlockStartCalls| was invoked. std::lock_guard<std::mutex> lock(start_mutex_); + if (initialize_callback_) { + std::move(initialize_callback_) + .Run(LibassistantV1Api::Get()->assistant_manager(), + LibassistantV1Api::Get()->assistant_manager_internal()); + } + SetState(State::kStarted); }
diff --git a/chromeos/services/assistant/test_support/fake_service_controller.h b/chromeos/services/assistant/test_support/fake_service_controller.h index 59f2bae..9dc561a 100644 --- a/chromeos/services/assistant/test_support/fake_service_controller.h +++ b/chromeos/services/assistant/test_support/fake_service_controller.h
@@ -12,6 +12,11 @@ #include "mojo/public/cpp/bindings/receiver.h" #include "mojo/public/cpp/bindings/remote_set.h" +namespace assistant_client { +class AssistantManager; +class AssistantManagerInternal; +} // namespace assistant_client + namespace chromeos { namespace assistant { @@ -21,6 +26,9 @@ class FakeServiceController : public libassistant::mojom::ServiceController { public: using State = libassistant::mojom::ServiceState; + using InitializeCallback = + base::OnceCallback<void(assistant_client::AssistantManager*, + assistant_client::AssistantManagerInternal*)>; FakeServiceController(); FakeServiceController(FakeServiceController&) = delete; @@ -39,6 +47,8 @@ pending_receiver); void Unbind(); + void SetInitializeCallback(InitializeCallback callback); + // Call this to block any call to |Start|. The observers will not be invoked // as long as the start call is blocked. Unblock these calls using // |UnblockStartCalls|. This is not enabled by default, so unless you call @@ -61,6 +71,8 @@ // Config passed to LibAssistant when it was started. std::string libassistant_config_; + InitializeCallback initialize_callback_; + State state_ = State::kStopped; mojo::Receiver<libassistant::mojom::ServiceController> receiver_; mojo::RemoteSet<libassistant::mojom::StateObserver> state_observers_;
diff --git a/chromeos/services/assistant/utils.cc b/chromeos/services/assistant/utils.cc index 82bbf877..ace2adf 100644 --- a/chromeos/services/assistant/utils.cc +++ b/chromeos/services/assistant/utils.cc
@@ -148,10 +148,8 @@ GetBaseAssistantDir().AsUTF8Unsafe()); } - if (features::IsLibAssistantBetaBackendEnabled() || - features::IsAssistantDebuggingEnabled()) { + if (features::IsLibAssistantBetaBackendEnabled()) config.SetStringPath("internal.backend_type", "BETA_DOGFOOD"); - } // Use http unless we're using the fake s3 server, which requires grpc. if (s3_server_uri_override)
diff --git a/chromeos/services/libassistant/libassistant_service.cc b/chromeos/services/libassistant/libassistant_service.cc index 6bf0ea1..481fb6d 100644 --- a/chromeos/services/libassistant/libassistant_service.cc +++ b/chromeos/services/libassistant/libassistant_service.cc
@@ -29,5 +29,9 @@ service_controller_->Bind(std::move(receiver)); } +void LibassistantService::SetInitializeCallback(InitializeCallback callback) { + service_controller().SetInitializeCallback(std::move(callback)); +} + } // namespace libassistant } // namespace chromeos
diff --git a/chromeos/services/libassistant/libassistant_service.h b/chromeos/services/libassistant/libassistant_service.h index fed2dfa..38c5c05 100644 --- a/chromeos/services/libassistant/libassistant_service.h +++ b/chromeos/services/libassistant/libassistant_service.h
@@ -12,6 +12,8 @@ #include "mojo/public/cpp/bindings/receiver.h" namespace assistant_client { +class AssistantManager; +class AssistantManagerInternal; class PlatformApi; } // namespace assistant_client @@ -29,6 +31,10 @@ class COMPONENT_EXPORT(LIBASSISTANT_SERVICE) LibassistantService : public mojom::LibassistantService { public: + using InitializeCallback = + base::OnceCallback<void(assistant_client::AssistantManager*, + assistant_client::AssistantManagerInternal*)>; + explicit LibassistantService( mojo::PendingReceiver<mojom::LibassistantService> receiver, assistant_client::PlatformApi* platform_api, @@ -37,7 +43,11 @@ LibassistantService& operator=(LibassistantService&) = delete; ~LibassistantService() override; + void SetInitializeCallback(InitializeCallback callback); + private: + ServiceController& service_controller() { return *service_controller_; } + // mojom::LibassistantService implementation: void BindServiceController( mojo::PendingReceiver<mojom::ServiceController> receiver) override;
diff --git a/chromeos/services/libassistant/service_controller.cc b/chromeos/services/libassistant/service_controller.cc index 68ceb36..24a235e 100644 --- a/chromeos/services/libassistant/service_controller.cc +++ b/chromeos/services/libassistant/service_controller.cc
@@ -32,6 +32,10 @@ receiver_.Bind(std::move(receiver)); } +void ServiceController::SetInitializeCallback(InitializeCallback callback) { + initialize_callback_ = std::move(callback); +} + void ServiceController::Start(const std::string& libassistant_config) { if (state_ != ServiceState::kStopped) return; @@ -43,6 +47,13 @@ libassistant_v1_api_ = std::make_unique<assistant::LibassistantV1Api>( assistant_manager_.get(), assistant_manager_internal_); + if (initialize_callback_) { + std::move(initialize_callback_) + .Run(assistant_manager(), assistant_manager_internal()); + } + + assistant_manager()->Start(); + SetStateAndInformObservers(ServiceState::kStarted); for (auto& observer : assistant_manager_observers_) {
diff --git a/chromeos/services/libassistant/service_controller.h b/chromeos/services/libassistant/service_controller.h index 26ffd61..c93bfba 100644 --- a/chromeos/services/libassistant/service_controller.h +++ b/chromeos/services/libassistant/service_controller.h
@@ -43,6 +43,10 @@ class COMPONENT_EXPORT(LIBASSISTANT_SERVICE) ServiceController : public mojom::ServiceController { public: + using InitializeCallback = + base::OnceCallback<void(assistant_client::AssistantManager*, + assistant_client::AssistantManagerInternal*)>; + ServiceController(assistant::AssistantManagerServiceDelegate* delegate, assistant_client::PlatformApi* platform_api); ServiceController(ServiceController&) = delete; @@ -51,6 +55,12 @@ void Bind(mojo::PendingReceiver<mojom::ServiceController> receiver); + // Set a callback to initialize |AssistantManager| and + // |AssistantManagerInternal|. This callback will be invoked before + // AssistantManager::Start() is called. This is temporary until we've migrated + // all initialization code to this class. + void SetInitializeCallback(InitializeCallback callback); + // mojom::ServiceController implementation: void Start(const std::string& libassistant_config) override; void Stop() override; @@ -77,6 +87,9 @@ // Owned by |AssistantManagerServiceImpl| which indirectly owns us. assistant_client::PlatformApi* const platform_api_; + // Callback called to initialize |AssistantManager| before it's started. + InitializeCallback initialize_callback_; + std::unique_ptr<assistant_client::AssistantManager> assistant_manager_; assistant_client::AssistantManagerInternal* assistant_manager_internal_ = nullptr;
diff --git a/components/download/internal/common/in_progress_download_manager.cc b/components/download/internal/common/in_progress_download_manager.cc index fdd22a1..c8a3830 100644 --- a/components/download/internal/common/in_progress_download_manager.cc +++ b/components/download/internal/common/in_progress_download_manager.cc
@@ -680,6 +680,7 @@ void InProgressDownloadManager::AddInProgressDownloadForTest( std::unique_ptr<download::DownloadItemImpl> download) { in_progress_downloads_.push_back(std::move(download)); + NotifyDownloadsInitialized(); } void InProgressDownloadManager::CancelUrlDownload(
diff --git a/components/exo/data_device.cc b/components/exo/data_device.cc index 3787f7d..091ebfb 100644 --- a/components/exo/data_device.cc +++ b/components/exo/data_device.cc
@@ -124,14 +124,18 @@ delegate_->OnDrop(); - // TODO(tetsui): Avoid using nested loop by adding asynchronous callback to - // aura::client::DragDropDelegate. + // TODO(crbug.com/1160925): Avoid using nested loop by adding asynchronous + // callback to aura::client::DragDropDelegate. + base::WeakPtr<DataDevice> alive(weak_factory_.GetWeakPtr()); base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed); base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, run_loop.QuitClosure(), kDataOfferDestructionTimeout); quit_closure_ = run_loop.QuitClosure(); run_loop.Run(); + if (!alive) + return ui::DragDropTypes::DRAG_NONE; + if (quit_closure_) { // DataOffer not destroyed by the client until the timeout. quit_closure_.Reset();
diff --git a/components/exo/data_device.h b/components/exo/data_device.h index b0b647b..cf66a18 100644 --- a/components/exo/data_device.h +++ b/components/exo/data_device.h
@@ -8,6 +8,7 @@ #include <cstdint> #include "base/macros.h" +#include "base/memory/weak_ptr.h" #include "components/exo/data_offer_observer.h" #include "components/exo/seat_observer.h" #include "components/exo/surface.h" @@ -87,6 +88,7 @@ base::OnceClosure quit_closure_; bool drop_succeeded_; + base::WeakPtrFactory<DataDevice> weak_factory_{this}; DISALLOW_COPY_AND_ASSIGN(DataDevice); };
diff --git a/components/exo/data_device_unittest.cc b/components/exo/data_device_unittest.cc index 09c94d8..fbe7d49f 100644 --- a/components/exo/data_device_unittest.cc +++ b/components/exo/data_device_unittest.cc
@@ -11,6 +11,7 @@ #include "ash/shell.h" #include "base/strings/string16.h" #include "base/strings/utf_string_conversions.h" +#include "base/test/bind.h" #include "components/exo/data_device_delegate.h" #include "components/exo/data_exchange_delegate.h" #include "components/exo/data_offer.h" @@ -199,6 +200,17 @@ EXPECT_EQ(DataEvent::kLeave, events[0]); } +TEST_F(DataDeviceTest, DeleteDataDeviceDuringDrop) { + ui::DropTargetEvent event(data_, gfx::PointF(), gfx::PointF(), + ui::DragDropTypes::DRAG_MOVE); + ui::Event::DispatcherApi(&event).set_target(surface_->window()); + device_->OnDragEntered(event); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindLambdaForTesting([&]() { device_.reset(); })); + int result = device_->OnPerformDrop(event); + EXPECT_EQ(ui::DragDropTypes::DRAG_NONE, result); +} + TEST_F(DataDeviceTest, DeleteDataOfferDuringDrag) { ui::DropTargetEvent event(data_, gfx::PointF(), gfx::PointF(), ui::DragDropTypes::DRAG_MOVE);
diff --git a/components/no_state_prefetch/browser/prerender_manager.cc b/components/no_state_prefetch/browser/prerender_manager.cc index 76fbf6b0..ae248992 100644 --- a/components/no_state_prefetch/browser/prerender_manager.cc +++ b/components/no_state_prefetch/browser/prerender_manager.cc
@@ -499,6 +499,14 @@ return nullptr; } + // Disallow NSPing link-rel:next URLs. + // See https://bugs.chromium.org/p/chromium/issues/detail?id=1158209. + if (origin == ORIGIN_LINK_REL_NEXT) { + SkipPrerenderContentsAndMaybePreconnect( + url_arg, origin, FINAL_STATUS_LINK_REL_NEXT_NOT_ALLOWED); + return nullptr; + } + // Disallow prerendering on low end devices. if (IsLowEndDevice()) { SkipPrerenderContentsAndMaybePreconnect(url_arg, origin, @@ -923,6 +931,9 @@ return; } + if (origin == ORIGIN_LINK_REL_NEXT) + return; + if (final_status == FINAL_STATUS_LOW_END_DEVICE || final_status == FINAL_STATUS_CELLULAR_NETWORK || final_status == FINAL_STATUS_DUPLICATE || @@ -931,7 +942,7 @@ } static_assert( - FINAL_STATUS_MAX == FINAL_STATUS_SINGLE_PROCESS + 1, + FINAL_STATUS_MAX == FINAL_STATUS_LINK_REL_NEXT_NOT_ALLOWED + 1, "Consider whether a failed prerender should fallback to preconnect"); } @@ -977,6 +988,15 @@ prefetches_.clear(); } +std::unique_ptr<PrerenderHandle> +PrerenderManager::AddPrerenderWithPreconnectFallbackForTesting( + Origin origin, + const GURL& url, + const base::Optional<url::Origin>& initiator_origin) { + return AddPrerenderWithPreconnectFallback( + origin, url, content::Referrer(), initiator_origin, gfx::Rect(), nullptr); +} + void PrerenderManager::SetPrerenderContentsFactoryForTest( PrerenderContents::Factory* prerender_contents_factory) { DCHECK_CURRENTLY_ON(BrowserThread::UI);
diff --git a/components/no_state_prefetch/browser/prerender_manager.h b/components/no_state_prefetch/browser/prerender_manager.h index 1795bc89..5921099 100644 --- a/components/no_state_prefetch/browser/prerender_manager.h +++ b/components/no_state_prefetch/browser/prerender_manager.h
@@ -266,6 +266,14 @@ // Returns true iff the |url| is found in the list of recent prefetches. bool HasRecentlyPrefetchedUrlForTesting(const GURL& url); + // Adds a prerender for |url| from |initiator_origin|. The |origin| specifies + // how the prerender was added. Returns a PrerenderHandle or nullptr. Only for + // testing. + std::unique_ptr<PrerenderHandle> AddPrerenderWithPreconnectFallbackForTesting( + Origin origin, + const GURL& url, + const base::Optional<url::Origin>& initiator_origin); + protected: class PrerenderData : public base::SupportsWeakPtr<PrerenderData> { public:
diff --git a/components/no_state_prefetch/common/prerender_final_status.cc b/components/no_state_prefetch/common/prerender_final_status.cc index 0029e8c..be22d0f 100644 --- a/components/no_state_prefetch/common/prerender_final_status.cc +++ b/components/no_state_prefetch/common/prerender_final_status.cc
@@ -75,6 +75,7 @@ "Unknown", "Navigation Predictor Holdback", "Single Process Mode", + "Link Rel Next Not Allowed", "Max", }; static_assert(base::size(kFinalStatusNames) == FINAL_STATUS_MAX + 1,
diff --git a/components/no_state_prefetch/common/prerender_final_status.h b/components/no_state_prefetch/common/prerender_final_status.h index e827c2d..f21979b 100644 --- a/components/no_state_prefetch/common/prerender_final_status.h +++ b/components/no_state_prefetch/common/prerender_final_status.h
@@ -80,6 +80,7 @@ FINAL_STATUS_UNKNOWN = 60, FINAL_STATUS_NAVIGATION_PREDICTOR_HOLDBACK = 61, FINAL_STATUS_SINGLE_PROCESS = 62, + FINAL_STATUS_LINK_REL_NEXT_NOT_ALLOWED = 63, FINAL_STATUS_MAX, };
diff --git a/components/optimization_guide/bloom_filter.cc b/components/optimization_guide/bloom_filter.cc index 20e6fb19..33155175 100644 --- a/components/optimization_guide/bloom_filter.cc +++ b/components/optimization_guide/bloom_filter.cc
@@ -51,9 +51,7 @@ memcpy(&bytes_[0], filter_data.data(), filter_data.size()); } -BloomFilter::~BloomFilter() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); -} +BloomFilter::~BloomFilter() = default; bool BloomFilter::Contains(const std::string& str) const { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
diff --git a/components/optimization_guide/optimization_filter.cc b/components/optimization_guide/optimization_filter.cc index f977fc1..a67ae4e0 100644 --- a/components/optimization_guide/optimization_filter.cc +++ b/components/optimization_guide/optimization_filter.cc
@@ -28,11 +28,10 @@ DETACH_FROM_SEQUENCE(sequence_checker_); } -OptimizationFilter::~OptimizationFilter() { - DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); -} +OptimizationFilter::~OptimizationFilter() = default; bool OptimizationFilter::Matches(const GURL& url) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); return ContainsHostSuffix(url) || MatchesRegexp(url); }
diff --git a/components/optimization_guide/test_hints_component_creator.cc b/components/optimization_guide/test_hints_component_creator.cc index 6edfb2c3..bfcde05 100644 --- a/components/optimization_guide/test_hints_component_creator.cc +++ b/components/optimization_guide/test_hints_component_creator.cc
@@ -11,6 +11,7 @@ #include "base/strings/string_number_conversions.h" #include "base/threading/thread_restrictions.h" #include "base/version.h" +#include "components/optimization_guide/bloom_filter.h" #include "testing/gtest/include/gtest/gtest.h" namespace { @@ -58,7 +59,6 @@ optimization_guide::proto::Optimization* optimization = page_hint->add_whitelisted_optimizations(); optimization->set_optimization_type(optimization_type); - } // Always stick something with no hint version in here. @@ -73,6 +73,37 @@ bad_version_hint->set_version("notaversion"); bad_version_hint->add_page_hints()->set_page_pattern("*"); + // Always stick an allowlist optimization filter in here. + optimization_guide::BloomFilter allowlist_bloom_filter(7, 511); + allowlist_bloom_filter.Add("allowedhost.com"); + std::string allowlist_bloom_filter_data( + reinterpret_cast<const char*>(&allowlist_bloom_filter.bytes()[0]), + allowlist_bloom_filter.bytes().size()); + optimization_guide::proto::OptimizationFilter* allowlist_optimization_filter = + config.add_optimization_allowlists(); + allowlist_optimization_filter->set_optimization_type( + optimization_guide::proto::LITE_PAGE_REDIRECT); + allowlist_optimization_filter->mutable_bloom_filter()->set_num_hash_functions( + 7); + allowlist_optimization_filter->mutable_bloom_filter()->set_num_bits(511); + allowlist_optimization_filter->mutable_bloom_filter()->set_data( + allowlist_bloom_filter_data); + // Always stick a blocklist optimization filter in here. + optimization_guide::BloomFilter blocklist_bloom_filter(7, 511); + blocklist_bloom_filter.Add("blockedhost.com"); + std::string blocklist_bloom_filter_data( + reinterpret_cast<const char*>(&blocklist_bloom_filter.bytes()[0]), + blocklist_bloom_filter.bytes().size()); + optimization_guide::proto::OptimizationFilter* blocklist_optimization_filter = + config.add_optimization_blacklists(); + blocklist_optimization_filter->set_optimization_type( + optimization_guide::proto::FAST_HOST_HINTS); + blocklist_optimization_filter->mutable_bloom_filter()->set_num_hash_functions( + 7); + blocklist_optimization_filter->mutable_bloom_filter()->set_num_bits(511); + blocklist_optimization_filter->mutable_bloom_filter()->set_data( + blocklist_bloom_filter_data); + return WriteConfigToFileAndReturnHintsComponentInfo(config); }
diff --git a/components/password_manager/core/browser/sync/password_sync_bridge.cc b/components/password_manager/core/browser/sync/password_sync_bridge.cc index 35dd2d8..d015c0f 100644 --- a/components/password_manager/core/browser/sync/password_sync_bridge.cc +++ b/components/password_manager/core/browser/sync/password_sync_bridge.cc
@@ -15,6 +15,7 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" +#include "components/password_manager/core/browser/insecure_credentials_table.h" #include "components/password_manager/core/browser/password_form.h" #include "components/password_manager/core/browser/password_manager_metrics_util.h" #include "components/password_manager/core/common/password_manager_features.h" @@ -55,6 +56,13 @@ net::EscapePath(password_data.signon_realm()); } +base::Time ConvertToBaseTime(uint64_t time) { + return base::Time::FromDeltaSinceWindowsEpoch( + // Use FromDeltaSinceWindowsEpoch because create_time_us has + // always used the Windows epoch. + base::TimeDelta::FromMicroseconds(time)); +} + sync_pb::PasswordSpecifics SpecificsFromPassword( const PasswordForm& password_form) { sync_pb::PasswordSpecifics specifics; @@ -107,18 +115,14 @@ password.username_value = base::UTF8ToUTF16(password_data.username_value()); password.password_value = base::UTF8ToUTF16(password_data.password_value()); if (password_data.has_date_last_used()) { - password.date_last_used = base::Time::FromDeltaSinceWindowsEpoch( - base::TimeDelta::FromMicroseconds(password_data.date_last_used())); + password.date_last_used = ConvertToBaseTime(password_data.date_last_used()); } else if (password_data.preferred()) { // For legacy passwords that don't have the |date_last_used| field set, we // should it similar to the logic in login database migration. password.date_last_used = base::Time::FromDeltaSinceWindowsEpoch(base::TimeDelta::FromDays(1)); } - password.date_created = base::Time::FromDeltaSinceWindowsEpoch( - // Use FromDeltaSinceWindowsEpoch because create_time_us has - // always used the Windows epoch. - base::TimeDelta::FromMicroseconds(password_data.date_created())); + password.date_created = ConvertToBaseTime(password_data.date_created()); password.blocked_by_user = password_data.blacklisted(); password.type = static_cast<PasswordForm::Type>(password_data.type()); password.times_used = password_data.times_used(); @@ -130,6 +134,58 @@ return password; } +CompromisedCredentials CreateCompromisedCredentials( + const std::string& signon_realm, + const base::string16& username, + CompromiseType compromise_type, + const sync_pb::PasswordSpecificsData::PasswordIssues::PasswordIssue& + issue) { + return CompromisedCredentials( + signon_realm, username, + ConvertToBaseTime(issue.date_first_detection_microseconds()), + compromise_type, IsMuted(issue.is_muted())); +} + +std::vector<CompromisedCredentials> CompromisedCredentialsFromEntityChange( + const syncer::EntityChange& entity_change) { + DCHECK(entity_change.data().specifics.has_password()); + + const sync_pb::PasswordSpecificsData& password_data = + entity_change.data().specifics.password().client_only_encrypted_data(); + + std::vector<CompromisedCredentials> issues; + + if (!password_data.has_password_issues()) + return issues; + + const std::string& signon_realm = password_data.signon_realm(); + const base::string16& username = + base::UTF8ToUTF16(password_data.username_value()); + + const auto& password_issues = password_data.password_issues(); + if (password_issues.has_leaked_password_issue()) { + issues.emplace_back(CreateCompromisedCredentials( + signon_realm, username, CompromiseType::kLeaked, + password_issues.leaked_password_issue())); + } + if (password_issues.has_reused_password_issue()) { + issues.emplace_back(CreateCompromisedCredentials( + signon_realm, username, CompromiseType::kReused, + password_issues.reused_password_issue())); + } + if (password_issues.has_weak_password_issue()) { + issues.emplace_back(CreateCompromisedCredentials( + signon_realm, username, CompromiseType::kWeak, + password_issues.weak_password_issue())); + } + if (password_issues.has_phished_password_issue()) { + issues.emplace_back(CreateCompromisedCredentials( + signon_realm, username, CompromiseType::kPhished, + password_issues.phished_password_issue())); + } + return issues; +} + std::unique_ptr<syncer::EntityData> CreateEntityData(const PasswordForm& form) { auto entity_data = std::make_unique<syncer::EntityData>(); *entity_data->specifics.mutable_password() = SpecificsFromPassword(form); @@ -165,13 +221,9 @@ base::UTF16ToUTF8(password_form.password_value) == password_specifics.password_value() && password_form.date_last_used == - base::Time::FromDeltaSinceWindowsEpoch( - base::TimeDelta::FromMicroseconds( - password_specifics.date_last_used())) && + ConvertToBaseTime(password_specifics.date_last_used()) && password_form.date_created == - base::Time::FromDeltaSinceWindowsEpoch( - base::TimeDelta::FromMicroseconds( - password_specifics.date_created())) && + ConvertToBaseTime(password_specifics.date_created()) && password_form.blocked_by_user == password_specifics.blacklisted() && static_cast<int>(password_form.type) == password_specifics.type() && password_form.times_used == password_specifics.times_used() && @@ -422,9 +474,7 @@ } // Passwords aren't identical, pick the most recently created one. - if (base::Time::FromDeltaSinceWindowsEpoch( - base::TimeDelta::FromMicroseconds( - remote_password_specifics.date_created())) < + if (ConvertToBaseTime(remote_password_specifics.date_created()) < local_password_form.date_created) { // The local password is more recent, update the processor. change_processor()->Put( @@ -476,6 +526,8 @@ PasswordStoreChangeList changes = password_store_sync_->AddLoginSync( PasswordFromEntityChange(*entity_change, /*sync_time=*/time_now), &add_login_error); + password_store_sync_->AddCompromisedCredentialsSync( + CompromisedCredentialsFromEntityChange(*entity_change)); base::UmaHistogramEnumeration( "PasswordManager.MergeSyncData.AddLoginSyncError", add_login_error); @@ -590,6 +642,8 @@ changes = password_store_sync_->AddLoginSync( PasswordFromEntityChange(*entity_change, /*sync_time=*/time_now), &add_login_error); + password_store_sync_->AddCompromisedCredentialsSync( + CompromisedCredentialsFromEntityChange(*entity_change)); base::UmaHistogramEnumeration( "PasswordManager.ApplySyncChanges.AddLoginSyncError", add_login_error);
diff --git a/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc b/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc index 4c8845a..4fdcf023 100644 --- a/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc +++ b/components/password_manager/core/browser/sync/password_sync_bridge_unittest.cc
@@ -39,6 +39,7 @@ using testing::NotNull; using testing::Return; using testing::UnorderedElementsAre; +using testing::UnorderedElementsAreArray; constexpr char kSignonRealm1[] = "abc"; constexpr char kSignonRealm2[] = "def"; @@ -67,11 +68,39 @@ ->GetMetadataStoreForTesting() == expected_metadata_store; } -sync_pb::PasswordSpecifics CreateSpecifics(const std::string& origin, - const std::string& username_element, - const std::string& username_value, - const std::string& password_element, - const std::string& signon_realm) { +sync_pb::PasswordSpecificsData_PasswordIssues CreateSpecificsIssues( + const std::vector<CompromiseType>& issue_types) { + sync_pb::PasswordSpecificsData_PasswordIssues remote_issues; + for (auto type : issue_types) { + sync_pb::PasswordSpecificsData_PasswordIssues_PasswordIssue remote_issue; + remote_issue.set_date_first_detection_microseconds( + base::Time().ToDeltaSinceWindowsEpoch().InMicroseconds()); + remote_issue.set_is_muted(false); + switch (type) { + case CompromiseType::kLeaked: + *remote_issues.mutable_leaked_password_issue() = remote_issue; + break; + case CompromiseType::kPhished: + *remote_issues.mutable_phished_password_issue() = remote_issue; + break; + case CompromiseType::kWeak: + *remote_issues.mutable_weak_password_issue() = remote_issue; + break; + case CompromiseType::kReused: + *remote_issues.mutable_reused_password_issue() = remote_issue; + break; + } + } + return remote_issues; +} + +sync_pb::PasswordSpecifics CreateSpecifics( + const std::string& origin, + const std::string& username_element, + const std::string& username_value, + const std::string& password_element, + const std::string& signon_realm, + const std::vector<CompromiseType>& issue_types) { sync_pb::EntitySpecifics password_specifics; sync_pb::PasswordSpecificsData* password_data = password_specifics.mutable_password() @@ -81,13 +110,25 @@ password_data->set_username_value(username_value); password_data->set_password_element(password_element); password_data->set_signon_realm(signon_realm); + if (!issue_types.empty()) + *password_data->mutable_password_issues() = + CreateSpecificsIssues(issue_types); return password_specifics.password(); } sync_pb::PasswordSpecifics CreateSpecificsWithSignonRealm( const std::string& signon_realm) { return CreateSpecifics("http://www.origin.com", "username_element", - "username_value", "password_element", signon_realm); + "username_value", "password_element", signon_realm, + {}); +} + +sync_pb::PasswordSpecifics CreateSpecificsWithSignonRealmAndIssues( + const std::string& signon_realm, + const std::vector<CompromiseType>& issue_types) { + return CreateSpecifics("http://www.origin.com", "username_element", + "username_value", "password_element", signon_realm, + issue_types); } PasswordForm MakePasswordForm(const std::string& signon_realm) { @@ -108,6 +149,19 @@ return form; } +std::vector<CompromisedCredentials> MakeCompromisedCredentials( + const PasswordForm& form, + const std::vector<CompromiseType>& types) { + std::vector<CompromisedCredentials> issues; + + for (auto type : types) { + issues.emplace_back( + CompromisedCredentials(form.signon_realm, form.username_value, + base::Time(), type, IsMuted(false))); + } + return issues; +} + // A mini database class the supports Add/Update/Remove functionality. It also // supports an auto increment primary key that starts from 1. It will be used to // empower the MockPasswordStoreSync be forwarding all database calls to an @@ -358,9 +412,9 @@ TEST_F(PasswordSyncBridgeTest, ShouldComputeClientTagHash) { syncer::EntityData data; - *data.specifics.mutable_password() = - CreateSpecifics("http://www.origin.com", "username_element", - "username_value", "password_element", "signon_realm"); + *data.specifics.mutable_password() = CreateSpecifics( + "http://www.origin.com", "username_element", "username_value", + "password_element", "signon_realm", /*issue_types=*/{}); EXPECT_THAT( bridge()->GetClientTag(data), @@ -1078,4 +1132,69 @@ "PasswordManager.AccountStoreBlocklistedEntriesAfterOptIn", 1, 1); } +TEST_F(PasswordSyncBridgeTest, + ShouldAddRemoteCompromisedCredentilasUponRemoteCreation) { + ON_CALL(mock_processor(), IsTrackingMetadata()).WillByDefault(Return(true)); + const std::vector<CompromiseType> kIssuesTypes = {CompromiseType::kLeaked, + CompromiseType::kWeak}; + const std::vector<CompromisedCredentials> kExpectedIssues = + MakeCompromisedCredentials(MakePasswordForm(kSignonRealm1), kIssuesTypes); + + sync_pb::PasswordSpecifics specifics = + CreateSpecificsWithSignonRealmAndIssues(kSignonRealm1, kIssuesTypes); + + testing::InSequence in_sequence; + EXPECT_CALL(*mock_password_store_sync(), BeginTransaction()); + EXPECT_CALL(*mock_password_store_sync(), + AddLoginSync(FormHasSignonRealm(kSignonRealm1), _)); + + EXPECT_CALL(*mock_password_store_sync(), + AddCompromisedCredentialsSync( + UnorderedElementsAreArray(kExpectedIssues))); + + EXPECT_CALL(*mock_password_store_sync(), CommitTransaction()); + + syncer::EntityChangeList entity_change_list; + entity_change_list.push_back(syncer::EntityChange::CreateAdd( + /*storage_key=*/"", SpecificsToEntity(specifics))); + base::Optional<syncer::ModelError> error = bridge()->ApplySyncChanges( + bridge()->CreateMetadataChangeList(), std::move(entity_change_list)); + EXPECT_FALSE(error); +} + +TEST_F(PasswordSyncBridgeTest, + ShouldAddRemoteCompromisedCredentilasDuringInitialMerge) { + ON_CALL(mock_processor(), IsTrackingMetadata()).WillByDefault(Return(true)); + const std::vector<CompromiseType> kIssuesTypes = {CompromiseType::kLeaked, + CompromiseType::kReused}; + const std::vector<CompromisedCredentials> kIssues = + MakeCompromisedCredentials(MakePasswordForm(kSignonRealm1), kIssuesTypes); + sync_pb::PasswordSpecifics specifics = + CreateSpecificsWithSignonRealmAndIssues(kSignonRealm1, kIssuesTypes); + + // Form will be added to the password store sync. We use sequence since the + // order is important. The form itself should be added before we add the + // compromised credentials. + + testing::Sequence in_sequence; + EXPECT_CALL(*mock_password_store_sync(), BeginTransaction()); + + EXPECT_CALL(*mock_password_store_sync(), + AddLoginSync(FormHasSignonRealm(kSignonRealm1), _)); + + EXPECT_CALL( + *mock_password_store_sync(), + AddCompromisedCredentialsSync(UnorderedElementsAreArray(kIssues))); + + EXPECT_CALL(*mock_password_store_sync(), CommitTransaction()); + + syncer::EntityChangeList entity_change_list; + entity_change_list.push_back(syncer::EntityChange::CreateAdd( + /*storage_key=*/"", SpecificsToEntity(specifics))); + + base::Optional<syncer::ModelError> error = bridge()->MergeSyncData( + bridge()->CreateMetadataChangeList(), std::move(entity_change_list)); + EXPECT_EQ(error, base::nullopt); +} + } // namespace password_manager
diff --git a/components/policy/proto/device_management_backend.proto b/components/policy/proto/device_management_backend.proto index 4cf5991..34e189b 100644 --- a/components/policy/proto/device_management_backend.proto +++ b/components/policy/proto/device_management_backend.proto
@@ -3485,6 +3485,17 @@ SHA1 = 1; SHA256 = 2; + + // Do not hash the input data - assume it is input to the signature algorithm. + // The client supports this starting with M-89. + // + // For SigningAlgorithm RSA_PKCS1_V1_5, the client will perform PKCS#1 v1.5 + // padding on |data_to_sign|. |data_to_sign| is expected to already contain a + // PKCS#1 DigestInfo prefix - the client will not attempt to add such a + // prefix. Also |data_to_sign| must be shorter than (key_size-11) bytes. If no + // other key size was specified in the + // RequiredClientCertificateFor{User,Device} policy, 2048 bytes is assumed. + NO_HASH = 3; } // Signing Algorithm for Client Certificate Provisioning Flow.
diff --git a/components/signin/core/browser/account_reconcilor.cc b/components/signin/core/browser/account_reconcilor.cc index 7aab3de..c5e4ef4 100644 --- a/components/signin/core/browser/account_reconcilor.cc +++ b/components/signin/core/browser/account_reconcilor.cc
@@ -236,6 +236,7 @@ AccountReconcilor::~AccountReconcilor() { VLOG(1) << "AccountReconcilor::~AccountReconcilor"; // Make sure shutdown was called first. + DCHECK(WasShutDown()); DCHECK(!registered_with_identity_manager_); } @@ -280,8 +281,10 @@ void AccountReconcilor::Shutdown() { VLOG(1) << "AccountReconcilor::Shutdown"; + was_shut_down_ = true; DisableReconcile(false /* logout_all_accounts */); delegate_.reset(); + DCHECK(WasShutDown()); } void AccountReconcilor::RegisterWithContentSettings() { @@ -434,6 +437,9 @@ } void AccountReconcilor::StartReconcile() { + if (WasShutDown()) + return; + if (is_reconcile_started_) return; @@ -1083,3 +1089,7 @@ for (auto& observer : observer_list_) observer.OnStateChanged(state_); } + +bool AccountReconcilor::WasShutDown() const { + return was_shut_down_; +}
diff --git a/components/signin/core/browser/account_reconcilor.h b/components/signin/core/browser/account_reconcilor.h index 5a1a867c..8aeb6f9b 100644 --- a/components/signin/core/browser/account_reconcilor.h +++ b/components/signin/core/browser/account_reconcilor.h
@@ -221,6 +221,8 @@ TableRowTestMergeSession); FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTestActiveDirectory, TableRowTestMultilogin); + FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTest, ReconcileAfterShutdown); + FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTest, UnlockAfterShutdown); void set_timer_for_testing(std::unique_ptr<base::OneShotTimer> timer); @@ -309,6 +311,9 @@ // Sets the reconcilor state and calls Observer::OnStateChanged() if needed. void SetState(signin_metrics::AccountReconcilorState state); + // Returns whether Shutdown() was called. + bool WasShutDown() const; + std::unique_ptr<signin::AccountReconcilorDelegate> delegate_; // The IdentityManager associated with this reconcilor. @@ -373,6 +378,9 @@ signin_metrics::AccountReconcilorState state_; + // Set to true when Shutdown() is called. + bool was_shut_down_ = false; + base::WeakPtrFactory<AccountReconcilor> weak_factory_{this}; DISALLOW_COPY_AND_ASSIGN(AccountReconcilor);
diff --git a/components/signin/core/browser/account_reconcilor_unittest.cc b/components/signin/core/browser/account_reconcilor_unittest.cc index 83e91923..48c603e 100644 --- a/components/signin/core/browser/account_reconcilor_unittest.cc +++ b/components/signin/core/browser/account_reconcilor_unittest.cc
@@ -245,7 +245,7 @@ base::HistogramTester* histogram_tester() { return &histogram_tester_; } MockAccountReconcilor* GetMockReconcilor(); - MockAccountReconcilor* GetMockReconcilor( + MockAccountReconcilor* CreateMockReconcilor( std::unique_ptr<signin::AccountReconcilorDelegate> delegate); AccountInfo ConnectProfileToAccount(const std::string& email); @@ -275,7 +275,11 @@ PrefService* pref_service() { return &pref_service_; } - void DeleteReconcilor() { mock_reconcilor_.reset(); } + void DeleteReconcilor() { + if (mock_reconcilor_) + mock_reconcilor_->Shutdown(); + mock_reconcilor_.reset(); + } network::TestURLLoaderFactory test_url_loader_factory_; @@ -347,12 +351,12 @@ return mock_reconcilor_.get(); } -MockAccountReconcilor* AccountReconcilorTest::GetMockReconcilor( +MockAccountReconcilor* AccountReconcilorTest::CreateMockReconcilor( std::unique_ptr<signin::AccountReconcilorDelegate> delegate) { + DCHECK(!mock_reconcilor_); mock_reconcilor_ = std::make_unique<MockAccountReconcilor>( identity_test_env_.identity_manager(), &test_signin_client_, std::move(delegate)); - return mock_reconcilor_.get(); } @@ -1809,7 +1813,8 @@ SetupTokens(GetParam().tokens); testing::InSequence mock_sequence; - MockAccountReconcilor* reconcilor = GetMockReconcilor( + DeleteReconcilor(); + MockAccountReconcilor* reconcilor = CreateMockReconcilor( std::make_unique<signin::ActiveDirectoryAccountReconcilorDelegate>()); // Setup expectations. @@ -2716,7 +2721,8 @@ AccountInfo account_info = ConnectProfileToAccount("user@gmail.com"); auto spy_delegate0 = std::make_unique<SpyReconcilorDelegate>(); SpyReconcilorDelegate* spy_delegate = spy_delegate0.get(); - AccountReconcilor* reconcilor = GetMockReconcilor(std::move(spy_delegate0)); + AccountReconcilor* reconcilor = + CreateMockReconcilor(std::move(spy_delegate0)); ASSERT_TRUE(reconcilor); auto timer0 = std::make_unique<base::MockOneShotTimer>(); base::MockOneShotTimer* timer = timer0.get(); @@ -2756,7 +2762,8 @@ account_info.email, account_info.gaia, &test_url_loader_factory_); auto spy_delegate0 = std::make_unique<SpyReconcilorDelegate>(); SpyReconcilorDelegate* spy_delegate = spy_delegate0.get(); - AccountReconcilor* reconcilor = GetMockReconcilor(std::move(spy_delegate0)); + AccountReconcilor* reconcilor = + CreateMockReconcilor(std::move(spy_delegate0)); ASSERT_TRUE(reconcilor); auto timer0 = std::make_unique<base::MockOneShotTimer>(); base::MockOneShotTimer* timer = timer0.get(); @@ -2815,7 +2822,7 @@ }; MockAccountReconcilor* reconcilor = - GetMockReconcilor(std::make_unique<MultiloginLogoutDelegate>()); + CreateMockReconcilor(std::make_unique<MultiloginLogoutDelegate>()); signin::SetListAccountsResponseOneAccount("user@gmail.com", "123456", &test_url_loader_factory_); @@ -2830,3 +2837,33 @@ EXPECT_FALSE(reconcilor->is_reconcile_started_); ASSERT_EQ(signin_metrics::ACCOUNT_RECONCILOR_OK, reconcilor->GetState()); } + +// Reconcilor does not start after being shutdown. Regression test for +// https://crbug.com/923094 +TEST_F(AccountReconcilorTest, ReconcileAfterShutdown) { + AccountReconcilor* reconcilor = GetMockReconcilor(); + ASSERT_TRUE(reconcilor); + EXPECT_FALSE(reconcilor->WasShutDown()); + reconcilor->Shutdown(); + EXPECT_TRUE(reconcilor->WasShutDown()); + reconcilor->StartReconcile(); // This should not crash. + EXPECT_FALSE(reconcilor->is_reconcile_started_); +} + +// Reconcilor does not unlock after being shutdown. Regression test for +// https://crbug.com/923094 +TEST_F(AccountReconcilorTest, UnlockAfterShutdown) { + AccountReconcilor* reconcilor = GetMockReconcilor(); + ASSERT_TRUE(reconcilor); + std::unique_ptr<AccountReconcilor::Lock> lock = + std::make_unique<AccountReconcilor::Lock>(reconcilor); + + // Reconcile does not start now because of the Lock, but is scheduled to start + // when the lock is released. + reconcilor->StartReconcile(); + EXPECT_FALSE(reconcilor->is_reconcile_started_); + + reconcilor->Shutdown(); + lock.reset(); // This should not crash. + EXPECT_FALSE(reconcilor->is_reconcile_started_); +}
diff --git a/components/signin/ios/browser/account_consistency_service.h b/components/signin/ios/browser/account_consistency_service.h index 798460a..3722472 100644 --- a/components/signin/ios/browser/account_consistency_service.h +++ b/components/signin/ios/browser/account_consistency_service.h
@@ -30,7 +30,6 @@ } class AccountReconcilor; -class PrefService; // Handles actions necessary for keeping the list of Google accounts available // on the web and those available on the iOS device from first-party Google apps @@ -48,21 +47,13 @@ // Name of the Google authentication cookie. static const char kGaiaCookieName[]; - // Name of the preference property that persists the domains that have a - // CHROME_CONNECTED cookie set by this service. - static const char kDomainsWithCookiePref[]; - AccountConsistencyService( web::BrowserState* browser_state, - PrefService* prefs, AccountReconcilor* account_reconcilor, scoped_refptr<content_settings::CookieSettings> cookie_settings, signin::IdentityManager* identity_manager); ~AccountConsistencyService() override; - // Registers the preferences used by AccountConsistencyService. - static void RegisterPrefs(PrefRegistrySimple* registry); - // Sets the handler for |web_state| that reacts on Gaia responses with the // X-Chrome-Manage-Accounts header and notifies |delegate|. void SetWebStateHandler(web::WebState* web_state, @@ -99,9 +90,6 @@ private: friend class AccountConsistencyServiceTest; - // Loads the domains with a CHROME_CONNECTED cookie from the prefs. - void LoadFromPrefs(); - // KeyedService implementation. void Shutdown() override; @@ -110,7 +98,6 @@ // Called when the request to set CHROME_CONNECTED cookie is done. void OnChromeConnectedCookieFinished( - const std::string& domain, net::CookieAccessResult cookie_access_result); // Called when cookie deletion is completed with the number of cookies that @@ -118,21 +105,6 @@ void OnDeleteCookiesFinished(base::OnceClosure callback, uint32_t num_cookies_deleted); - // Returns whether the CHROME_CONNECTED cookie should be added to |domain|. - // If the cookie is not already on |domain|, it will return true. If the - // cookie is time constrained, |cookie_refresh_interval| is present, then a - // cookie older than |cookie_refresh_interval| returns true. - bool ShouldSetChromeConnectedCookieToDomain( - const std::string& domain, - const base::TimeDelta& cookie_refresh_interval); - - // Enqueues a request to set the CHROME_CONNECTED cookie for the |url|'s - // domain. The cookie is set if it is not already on domain or if it is too - // old compared to the given |cookie_refresh_interval|. - void SetChromeConnectedCookieWithUrls( - const std::vector<const GURL>& urls, - const base::TimeDelta& cookie_refresh_interval); - // Triggers a Gaia cookie update on the Google domain. Calls // |cookies_restored_callback| if the Gaia cookies were restored. void TriggerGaiaCookieChangeIfDeleted( @@ -153,8 +125,6 @@ // Browser state associated with the service. web::BrowserState* browser_state_; - // Used to update kDomainsWithCookiePref. - PrefService* prefs_; // Service managing accounts reconciliation, notified of GAIA responses with // the X-Chrome-Manage-Accounts header AccountReconcilor* account_reconcilor_; @@ -168,9 +138,6 @@ // The number of cookie manager requests that are being processed. // Used for testing purposes only. int64_t active_cookie_manager_requests_for_testing_; - // The map between domains where a CHROME_CONNECTED cookie is present and - // the time when the cookie was last updated. - std::map<std::string, base::Time> last_cookie_update_map_; // Last time Gaia cookie was updated for the Google domain. base::Time last_gaia_cookie_verification_time_;
diff --git a/components/signin/ios/browser/account_consistency_service.mm b/components/signin/ios/browser/account_consistency_service.mm index 1aaae8f..537a327 100644 --- a/components/signin/ios/browser/account_consistency_service.mm +++ b/components/signin/ios/browser/account_consistency_service.mm
@@ -16,9 +16,6 @@ #include "base/strings/sys_string_conversions.h" #include "components/content_settings/core/browser/cookie_settings.h" #include "components/google/core/common/google_util.h" -#include "components/prefs/pref_registry_simple.h" -#include "components/prefs/pref_service.h" -#include "components/prefs/scoped_user_pref_update.h" #include "components/signin/core/browser/account_reconcilor.h" #include "components/signin/core/browser/signin_header_helper.h" #include "components/signin/ios/browser/features.h" @@ -42,11 +39,6 @@ namespace { -// The validity of CHROME_CONNECTED cookies is one day maximum as a -// precaution to ensure that the cookie is regenerated in the case that it -// is removed or invalidated. -constexpr base::TimeDelta kDelayThresholdToUpdateChromeConnectedCookie = - base::TimeDelta::FromHours(24); // The validity of the Gaia cookie on the Google domain is one hour to // ensure that Mirror account consistency is respected in light of the more // restrictive Intelligent Tracking Prevention (ITP) guidelines in iOS 14 @@ -364,23 +356,17 @@ const char AccountConsistencyService::kGaiaCookieName[] = "SAPISID"; -const char AccountConsistencyService::kDomainsWithCookiePref[] = - "signin.domains_with_cookie"; - AccountConsistencyService::AccountConsistencyService( web::BrowserState* browser_state, - PrefService* prefs, AccountReconcilor* account_reconcilor, scoped_refptr<content_settings::CookieSettings> cookie_settings, signin::IdentityManager* identity_manager) : browser_state_(browser_state), - prefs_(prefs), account_reconcilor_(account_reconcilor), cookie_settings_(cookie_settings), identity_manager_(identity_manager), active_cookie_manager_requests_for_testing_(0) { identity_manager_->AddObserver(this); - LoadFromPrefs(); if (identity_manager_->HasPrimaryAccount()) { AddChromeConnectedCookies(); } else { @@ -391,12 +377,6 @@ AccountConsistencyService::~AccountConsistencyService() { } -// static -void AccountConsistencyService::RegisterPrefs(PrefRegistrySimple* registry) { - registry->RegisterDictionaryPref( - AccountConsistencyService::kDomainsWithCookiePref); -} - void AccountConsistencyService::SetWebStateHandler( web::WebState* web_state, id<ManageAccountsDelegate> delegate) { @@ -470,11 +450,6 @@ void AccountConsistencyService::RemoveAllChromeConnectedCookies( base::OnceClosure callback) { DCHECK(!browser_state_->IsOffTheRecord()); - if (last_cookie_update_map_.empty()) { - if (!callback.is_null()) - std::move(callback).Run(); - return; - } network::mojom::CookieManager* cookie_manager = browser_state_->GetCookieManager(); @@ -488,7 +463,7 @@ std::move(filter), base::BindOnce(&AccountConsistencyService::OnDeleteCookiesFinished, base::Unretained(this), std::move(callback))); - ResetInternalState(); + last_gaia_cookie_verification_time_ = base::Time(); } void AccountConsistencyService::OnDeleteCookiesFinished( @@ -502,41 +477,11 @@ void AccountConsistencyService::SetChromeConnectedCookieWithUrls( const std::vector<const GURL>& urls) { - SetChromeConnectedCookieWithUrls( - urls, kDelayThresholdToUpdateChromeConnectedCookie); -} - -void AccountConsistencyService::SetChromeConnectedCookieWithUrls( - const std::vector<const GURL>& urls, - const base::TimeDelta& cookie_refresh_interval) { for (const GURL& url : urls) { - const std::string domain = GetDomainFromUrl(url); - if (!ShouldSetChromeConnectedCookieToDomain(domain, - cookie_refresh_interval)) { - continue; - } - last_cookie_update_map_[domain] = base::Time::Now(); SetChromeConnectedCookieWithUrl(url); } } -bool AccountConsistencyService::ShouldSetChromeConnectedCookieToDomain( - const std::string& domain, - const base::TimeDelta& cookie_refresh_interval) { - auto domain_iterator = last_cookie_update_map_.find(domain); - bool domain_not_found = domain_iterator == last_cookie_update_map_.end(); - return domain_not_found || ((base::Time::Now() - domain_iterator->second) > - cookie_refresh_interval); -} - -void AccountConsistencyService::LoadFromPrefs() { - const base::DictionaryValue* dict = - prefs_->GetDictionary(kDomainsWithCookiePref); - for (base::DictionaryValue::Iterator it(*dict); !it.IsAtEnd(); it.Advance()) { - last_cookie_update_map_[it.key()] = base::Time(); - } -} - void AccountConsistencyService::Shutdown() { identity_manager_->RemoveObserver(this); web_state_handlers_.clear(); @@ -550,7 +495,6 @@ signin::AccountConsistencyMethod::kMirror, cookie_settings_.get(), signin::PROFILE_MODE_DEFAULT); if (cookie_value.empty()) { - last_cookie_update_map_.erase(domain); return; } @@ -582,40 +526,27 @@ *cookie, url, options, base::BindOnce( &AccountConsistencyService::OnChromeConnectedCookieFinished, - base::Unretained(this), domain)); + base::Unretained(this))); } void AccountConsistencyService::OnChromeConnectedCookieFinished( - const std::string& domain, net::CookieAccessResult cookie_access_result) { DCHECK(cookie_access_result.status.IsInclude()); - DictionaryPrefUpdate update( - prefs_, AccountConsistencyService::kDomainsWithCookiePref); - // Add request.domain to prefs, use |true| as a dummy value (that is - // never used), as the dictionary is used as a set. - update->SetKey(domain, base::Value(true)); --active_cookie_manager_requests_for_testing_; } void AccountConsistencyService::AddChromeConnectedCookies() { DCHECK(!browser_state_->IsOffTheRecord()); - // These cookie requests are preventive and not a strong signal (unlike - // navigation to a domain). Don't force update the old cookies in this case. - SetChromeConnectedCookieWithUrls({GURL(kGoogleUrl), GURL(kYoutubeUrl)}, - base::TimeDelta::Max()); -} - -void AccountConsistencyService::ResetInternalState() { - last_cookie_update_map_.clear(); - last_gaia_cookie_verification_time_ = base::Time(); - base::DictionaryValue dict; - prefs_->Set(kDomainsWithCookiePref, dict); + // These cookie requests are preventive. Chrome cannot be sure that + // CHROME_CONNECTED cookies are set on google.com and youtube.com domains due + // to ITP restrictions. + SetChromeConnectedCookieWithUrls({GURL(kGoogleUrl), GURL(kYoutubeUrl)}); } void AccountConsistencyService::OnBrowsingDataRemoved() { // CHROME_CONNECTED cookies have been removed, update internal state // accordingly. - ResetInternalState(); + last_gaia_cookie_verification_time_ = base::Time(); // SAPISID cookie has been removed, notify the GCMS. // TODO(https://crbug.com/930582) : Remove the need to expose this method
diff --git a/components/signin/ios/browser/account_consistency_service_unittest.mm b/components/signin/ios/browser/account_consistency_service_unittest.mm index d984bb4..385562e 100644 --- a/components/signin/ios/browser/account_consistency_service_unittest.mm +++ b/components/signin/ios/browser/account_consistency_service_unittest.mm
@@ -142,7 +142,7 @@ protected: void SetUp() override { PlatformTest::SetUp(); - AccountConsistencyService::RegisterPrefs(prefs_.registry()); + content_settings::CookieSettings::RegisterProfilePrefs(prefs_.registry()); HostContentSettingsMap::RegisterProfilePrefs(prefs_.registry()); @@ -168,6 +168,7 @@ account_consistency_service_->Shutdown(); settings_map_->ShutdownOnUIThread(); + account_reconcilor_->Shutdown(); identity_test_env_.reset(); PlatformTest::TearDown(); } @@ -177,7 +178,7 @@ account_consistency_service_->Shutdown(); } account_consistency_service_.reset(new AccountConsistencyService( - &browser_state_, &prefs_, account_reconcilor_.get(), cookie_settings_, + &browser_state_, account_reconcilor_.get(), cookie_settings_, identity_test_env_->identity_manager())); } @@ -215,14 +216,6 @@ /*domain=*/std::string())); } - void CheckDomainHasChromeConnectedCookieWithUpdateTime( - const std::string& domain, - base::Time time) { - CheckDomainHasChromeConnectedCookie(domain); - EXPECT_EQ(time, - account_consistency_service_->last_cookie_update_map_[domain]); - } - // Verifies the time that the Gaia cookie was last updated for google.com. void CheckGaiaCookieWithUpdateTime(base::Time time) { EXPECT_EQ( @@ -348,12 +341,6 @@ SignIn(); CheckDomainHasChromeConnectedCookie(kGoogleDomain); CheckDomainHasChromeConnectedCookie(kYoutubeDomain); - - const base::DictionaryValue* dict = - prefs_.GetDictionary(AccountConsistencyService::kDomainsWithCookiePref); - EXPECT_EQ(2u, dict->size()); - EXPECT_TRUE(dict->GetBooleanWithoutPathExpansion(kGoogleDomain, nullptr)); - EXPECT_TRUE(dict->GetBooleanWithoutPathExpansion(kYoutubeDomain, nullptr)); } // Tests that cookies that are added during SignIn and subsequent navigations @@ -611,10 +598,6 @@ SignIn(); CheckDomainHasChromeConnectedCookie(kGoogleDomain); CheckDomainHasChromeConnectedCookie(kYoutubeDomain); - EXPECT_EQ( - 2u, - prefs_.GetDictionary(AccountConsistencyService::kDomainsWithCookiePref) - ->size()); // Sets Response to get IdentityManager::Observer::OnAccountsInCookieUpdated // through GaiaCookieManagerService::OnCookieChange. @@ -627,57 +610,34 @@ // AccountsCookieMutator::ForceTriggerOnCookieChange and finally // IdentityManager::Observer::OnAccountsInCookieUpdated is called. account_consistency_service_->OnBrowsingDataRemoved(); - EXPECT_EQ( - 0u, - prefs_.GetDictionary(AccountConsistencyService::kDomainsWithCookiePref) - ->size()); + run_loop.Run(); // AccountConsistency service is supposed to rebuild the CHROME_CONNECTED // cookies when browsing data is removed. CheckDomainHasChromeConnectedCookie(kGoogleDomain); CheckDomainHasChromeConnectedCookie(kYoutubeDomain); - EXPECT_EQ( - 2u, - prefs_.GetDictionary(AccountConsistencyService::kDomainsWithCookiePref) - ->size()); } -// Tests that the CHROME_CONNECTED cookie is set on Google-associated domains, -// but not on Google domains if the account consistency service runs before the -// scheduled cookie update time. +// Tests that google.com domain cookies can be regenerated after an external +// source removes these cookies. TEST_F(AccountConsistencyServiceTest, - SetChromeConnectedCookieBeforeUpdateTime) { + AddChromeConnectedCookiesOnCookiesRemoved) { SignIn(); + CheckDomainHasChromeConnectedCookie(kGoogleDomain); - id delegate = - [OCMockObject mockForProtocol:@protocol(ManageAccountsDelegate)]; - NSDictionary* headers = [NSDictionary dictionary]; - - // HTTP response URL is eligible for Mirror (the test does not use google.com - // since the CHROME_CONNECTED cookie is generated for it by default. - NSHTTPURLResponse* response = [[NSHTTPURLResponse alloc] - initWithURL:[NSURL URLWithString:@"https://youtube.com"] - statusCode:200 - HTTPVersion:@"HTTP/1.1" - headerFields:headers]; - - SimulateNavigateToURL(response, delegate); SimulateExternalSourceRemovesAllGoogleDomainCookies(); - - // Advance clock before 24-hour CHROME_CONNECTED update time. - task_environment_.FastForwardBy(base::TimeDelta::FromHours(2)); - SimulateNavigateToURL(response, delegate); - - CheckDomainHasChromeConnectedCookieWithUpdateTime( - kYoutubeDomain, base::Time::Now() - base::TimeDelta::FromHours(2)); CheckNoChromeConnectedCookieForDomain(kGoogleDomain); + + // Forcibly rebuild the CHROME_CONNECTED cookies. + account_consistency_service_->AddChromeConnectedCookies(); + + CheckDomainHasChromeConnectedCookie(kGoogleDomain); } // Tests that the CHROME_CONNECTED cookie is set on Google and Google-associated -// domains when the -// account consistency service runs at the scheduled cookie update time. -TEST_F(AccountConsistencyServiceTest, SetChromeConnectedCookieAtUpdateTime) { +// domains when the account consistency service runs. +TEST_F(AccountConsistencyServiceTest, SetChromeConnectedCookie) { SignIn(); id delegate = @@ -695,14 +655,10 @@ SimulateNavigateToURL(response, delegate); SimulateExternalSourceRemovesAllGoogleDomainCookies(); - // Advance clock past 24-hour CHROME_CONNECTED update time. - task_environment_.FastForwardBy(base::TimeDelta::FromDays(2)); SimulateNavigateToURL(response, delegate); - CheckDomainHasChromeConnectedCookieWithUpdateTime(kGoogleDomain, - base::Time::Now()); - CheckDomainHasChromeConnectedCookieWithUpdateTime(kYoutubeDomain, - base::Time::Now()); + CheckDomainHasChromeConnectedCookie(kGoogleDomain); + CheckDomainHasChromeConnectedCookie(kYoutubeDomain); } // Tests that the GAIA cookie update time is not updated before the scheduled
diff --git a/components/sync/invalidations/fcm_handler.cc b/components/sync/invalidations/fcm_handler.cc index 32f9b10..31268e5 100644 --- a/components/sync/invalidations/fcm_handler.cc +++ b/components/sync/invalidations/fcm_handler.cc
@@ -42,12 +42,16 @@ DCHECK(!IsListening()); DCHECK(base::FeatureList::IsEnabled(switches::kUseSyncInvalidations)); gcm_driver_->AddAppHandler(app_id_, this); + waiting_for_token_ = true; StartTokenFetch(base::BindOnce(&FCMHandler::DidRetrieveToken, weak_ptr_factory_.GetWeakPtr())); } void FCMHandler::StopListening() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // StopListening() may be called after StartListening() right away and + // DidRetrieveToken() won't be called. + waiting_for_token_ = false; if (IsListening()) { gcm_driver_->RemoveAppHandler(app_id_); token_validation_timer_.AbandonAndStop(); @@ -72,6 +76,11 @@ return fcm_registration_token_; } +bool FCMHandler::IsWaitingForToken() const { + DCHECK(!waiting_for_token_ || IsListening()); + return waiting_for_token_; +} + void FCMHandler::ShutdownHandler() { // Shutdown() should come before and it removes us from the list of app // handlers of gcm::GCMDriver so this shouldn't ever been called. @@ -141,6 +150,8 @@ void FCMHandler::DidRetrieveToken(const std::string& subscription_token, instance_id::InstanceID::Result result) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + waiting_for_token_ = false; + if (!IsListening()) { // After we requested the token, |StopListening| has been called. Thus, // ignore the token.
diff --git a/components/sync/invalidations/fcm_handler.h b/components/sync/invalidations/fcm_handler.h index 1786d9e..1894b6c 100644 --- a/components/sync/invalidations/fcm_handler.h +++ b/components/sync/invalidations/fcm_handler.h
@@ -71,6 +71,10 @@ // been received yet, or if the handler has stopped listening permanently. const std::string& GetFCMRegistrationToken() const; + // Returns true if an FCM registration token has never been retreived after + // the last StartListening() call. + bool IsWaitingForToken() const; + // GCMAppHandler overrides. void ShutdownHandler() override; void OnStoreReset() override; @@ -105,6 +109,8 @@ base::OneShotTimer token_validation_timer_; + bool waiting_for_token_ = false; + // Contains all listeners to notify about each incoming message in OnMessage // method. base::ObserverList<InvalidationsListener,
diff --git a/components/sync/invalidations/fcm_handler_unittest.cc b/components/sync/invalidations/fcm_handler_unittest.cc index 3a305e8..c02505af 100644 --- a/components/sync/invalidations/fcm_handler_unittest.cc +++ b/components/sync/invalidations/fcm_handler_unittest.cc
@@ -132,13 +132,16 @@ TEST_F(FCMHandlerTest, ShouldReturnValidToken) { // Check that the handler gets the token through GetToken. EXPECT_CALL(mock_instance_id_, GetToken) - .WillOnce(WithArg<4>(Invoke([](InstanceID::GetTokenCallback callback) { - std::move(callback).Run("token", InstanceID::Result::SUCCESS); - }))); + .WillOnce( + WithArg<4>(Invoke([this](InstanceID::GetTokenCallback callback) { + EXPECT_TRUE(fcm_handler_.IsWaitingForToken()); + std::move(callback).Run("token", InstanceID::Result::SUCCESS); + }))); fcm_handler_.StartListening(); EXPECT_EQ("token", fcm_handler_.GetFCMRegistrationToken()); + EXPECT_FALSE(fcm_handler_.IsWaitingForToken()); } TEST_F(FCMHandlerTest, ShouldPropagatePayloadToListener) {
diff --git a/components/sync/invalidations/interested_data_types_manager.cc b/components/sync/invalidations/interested_data_types_manager.cc index d83d8152..4d30ce5 100644 --- a/components/sync/invalidations/interested_data_types_manager.cc +++ b/components/sync/invalidations/interested_data_types_manager.cc
@@ -37,6 +37,11 @@ interested_data_types_handler_->OnInterestedDataTypesChanged( base::BindOnce(std::move(callback), new_data_types)); } + initialized_ = true; +} + +bool InterestedDataTypesManager::IsInitialized() const { + return initialized_; } } // namespace syncer
diff --git a/components/sync/invalidations/interested_data_types_manager.h b/components/sync/invalidations/interested_data_types_manager.h index a0796f1..864f5dd5 100644 --- a/components/sync/invalidations/interested_data_types_manager.h +++ b/components/sync/invalidations/interested_data_types_manager.h
@@ -24,15 +24,24 @@ // unregister any existing handler. There can be at most one handler. void SetInterestedDataTypesHandler(InterestedDataTypesHandler* handler); - // Get or set the interested data types. + // Get the interested data types. const ModelTypeSet& GetInterestedDataTypes() const; + + // Set interested data types. The first call of the method initializes this + // object. void SetInterestedDataTypes( const ModelTypeSet& data_types, SyncInvalidationsService::InterestedDataTypesAppliedCallback callback); + // Returns true if SetInterestedDataTypes() has been called at least once. + // Before that this object is considered to be uninitialized. + bool IsInitialized() const; + private: InterestedDataTypesHandler* interested_data_types_handler_ = nullptr; + bool initialized_ = false; + ModelTypeSet data_types_; };
diff --git a/components/sync/invalidations/interested_data_types_manager_unittest.cc b/components/sync/invalidations/interested_data_types_manager_unittest.cc index 6e890c8..4d2aef1e 100644 --- a/components/sync/invalidations/interested_data_types_manager_unittest.cc +++ b/components/sync/invalidations/interested_data_types_manager_unittest.cc
@@ -47,5 +47,16 @@ manager_.SetInterestedDataTypesHandler(nullptr); } +TEST_F(InterestedDataTypesManagerTest, + ShouldInitializeOnFirstSetInterestedDataTypes) { + EXPECT_FALSE(manager_.IsInitialized()); + manager_.SetInterestedDataTypes(ModelTypeSet(BOOKMARKS, PREFERENCES), + base::DoNothing()); + EXPECT_TRUE(manager_.IsInitialized()); + manager_.SetInterestedDataTypes(ModelTypeSet(BOOKMARKS, PREFERENCES, NIGORI), + base::DoNothing()); + EXPECT_TRUE(manager_.IsInitialized()); +} + } // namespace } // namespace syncer
diff --git a/components/sync/invalidations/mock_sync_invalidations_service.h b/components/sync/invalidations/mock_sync_invalidations_service.h index 742f581f..406cb05 100644 --- a/components/sync/invalidations/mock_sync_invalidations_service.h +++ b/components/sync/invalidations/mock_sync_invalidations_service.h
@@ -26,11 +26,17 @@ MOCK_METHOD(void, RemoveTokenObserver, (FCMRegistrationTokenObserver * observer)); - MOCK_METHOD(const std::string&, GetFCMRegistrationToken, (), (const)); + MOCK_METHOD(base::Optional<std::string>, + GetFCMRegistrationToken, + (), + (const)); MOCK_METHOD(void, SetInterestedDataTypesHandler, (InterestedDataTypesHandler * handler)); - MOCK_METHOD(const ModelTypeSet&, GetInterestedDataTypes, (), (const)); + MOCK_METHOD(base::Optional<ModelTypeSet>, + GetInterestedDataTypes, + (), + (const)); MOCK_METHOD(void, SetInterestedDataTypes, (const ModelTypeSet& data_types,
diff --git a/components/sync/invalidations/sync_invalidations_service.h b/components/sync/invalidations/sync_invalidations_service.h index 730565f..58b8619e 100644 --- a/components/sync/invalidations/sync_invalidations_service.h +++ b/components/sync/invalidations/sync_invalidations_service.h
@@ -8,6 +8,7 @@ #include <string> #include "base/callback.h" +#include "base/optional.h" #include "components/keyed_service/core/keyed_service.h" #include "components/sync/base/model_type.h" @@ -41,9 +42,10 @@ virtual void AddTokenObserver(FCMRegistrationTokenObserver* observer) = 0; virtual void RemoveTokenObserver(FCMRegistrationTokenObserver* observer) = 0; - // Used to get an obtained FCM token. Returns empty string if it hasn't been - // received yet, or if the device has stopped listening to invalidations. - virtual const std::string& GetFCMRegistrationToken() const = 0; + // Used to get an obtained FCM token. base::nullopt is returned if the token + // has been requested but hasn't been received yet. Returns an empty string if + // the device is not listening to invalidations. + virtual base::Optional<std::string> GetFCMRegistrationToken() const = 0; // Set the interested data types change handler. |handler| can be nullptr to // unregister any existing handler. There can be at most one handler. @@ -51,7 +53,9 @@ InterestedDataTypesHandler* handler) = 0; // Get or set for which data types should the device receive invalidations. - virtual const ModelTypeSet& GetInterestedDataTypes() const = 0; + // GetInterestedDataTypes() will return base::nullptr until + // SetInterestedDataTypes() has been called at least once. + virtual base::Optional<ModelTypeSet> GetInterestedDataTypes() const = 0; virtual void SetInterestedDataTypes( const ModelTypeSet& data_types, InterestedDataTypesAppliedCallback callback) = 0;
diff --git a/components/sync/invalidations/sync_invalidations_service_impl.cc b/components/sync/invalidations/sync_invalidations_service_impl.cc index dc6c526..dd83d0f2 100644 --- a/components/sync/invalidations/sync_invalidations_service_impl.cc +++ b/components/sync/invalidations/sync_invalidations_service_impl.cc
@@ -61,8 +61,11 @@ fcm_handler_->RemoveTokenObserver(observer); } -const std::string& SyncInvalidationsServiceImpl::GetFCMRegistrationToken() - const { +base::Optional<std::string> +SyncInvalidationsServiceImpl::GetFCMRegistrationToken() const { + if (fcm_handler_->IsWaitingForToken()) { + return base::nullopt; + } return fcm_handler_->GetFCMRegistrationToken(); } @@ -71,9 +74,12 @@ data_types_manager_.SetInterestedDataTypesHandler(handler); } -const ModelTypeSet& SyncInvalidationsServiceImpl::GetInterestedDataTypes() - const { - return data_types_manager_.GetInterestedDataTypes(); +base::Optional<ModelTypeSet> +SyncInvalidationsServiceImpl::GetInterestedDataTypes() const { + if (data_types_manager_.IsInitialized()) { + return data_types_manager_.GetInterestedDataTypes(); + } + return base::nullopt; } void SyncInvalidationsServiceImpl::SetInterestedDataTypes(
diff --git a/components/sync/invalidations/sync_invalidations_service_impl.h b/components/sync/invalidations/sync_invalidations_service_impl.h index 9cdc52e..ad8722cf 100644 --- a/components/sync/invalidations/sync_invalidations_service_impl.h +++ b/components/sync/invalidations/sync_invalidations_service_impl.h
@@ -37,10 +37,10 @@ void RemoveListener(InvalidationsListener* listener) override; void AddTokenObserver(FCMRegistrationTokenObserver* observer) override; void RemoveTokenObserver(FCMRegistrationTokenObserver* observer) override; - const std::string& GetFCMRegistrationToken() const override; + base::Optional<std::string> GetFCMRegistrationToken() const override; void SetInterestedDataTypesHandler( InterestedDataTypesHandler* handler) override; - const ModelTypeSet& GetInterestedDataTypes() const override; + base::Optional<ModelTypeSet> GetInterestedDataTypes() const override; void SetInterestedDataTypes( const ModelTypeSet& data_types, InterestedDataTypesAppliedCallback callback) override;
diff --git a/components/sync_device_info/device_info_sync_bridge.cc b/components/sync_device_info/device_info_sync_bridge.cc index de1454a..4be3d8d 100644 --- a/components/sync_device_info/device_info_sync_bridge.cc +++ b/components/sync_device_info/device_info_sync_bridge.cc
@@ -199,6 +199,22 @@ return base::nullopt; } +ModelTypeSet ExtractInterestedDataTypes(const DeviceInfoSpecifics& specifics) { + ModelTypeSet interested_data_types; + for (int data_type_id : + specifics.invalidation_fields().interested_data_type_ids()) { + const ModelType model_type = + GetModelTypeFromSpecificsFieldNumber(data_type_id); + + // This is possible if the browser has been updated and a data type has been + // removed. + if (model_type != ModelType::UNSPECIFIED) { + interested_data_types.Put(model_type); + } + } + return interested_data_types; +} + } // namespace DeviceInfoSyncBridge::DeviceInfoSyncBridge( @@ -226,15 +242,22 @@ return local_device_info_provider_.get(); } -void DeviceInfoSyncBridge::RefreshLocalDeviceInfo(base::OnceClosure callback) { - if (!callback.is_null()) { - device_info_synced_callback_list_.push_back(std::move(callback)); - } - +void DeviceInfoSyncBridge::RefreshLocalDeviceInfoIfNeeded( + base::OnceClosure callback) { // Device info cannot be synced if the provider is not initialized. When it // gets initialized, local device info will be sent. - if (local_device_info_provider_->GetLocalDeviceInfo()) { - SendLocalData(); + if (!local_device_info_provider_->GetLocalDeviceInfo()) { + if (!callback.is_null()) { + device_info_synced_callback_list_.push_back(std::move(callback)); + } + return; + } + + if (ReconcileLocalAndStored()) { + // The device info has been changed. + if (!callback.is_null()) { + device_info_synced_callback_list_.push_back(std::move(callback)); + } } } @@ -275,7 +298,8 @@ local_device_info_provider_->Initialize( local_cache_guid_, GetLocalClientName(), local_device_name_info_.manufacturer_name, - local_device_name_info_.model_name); + local_device_name_info_.model_name, + /*last_fcm_registration_token=*/std::string(), ModelTypeSet()); std::unique_ptr<WriteBatch> batch = store_->CreateWriteBatch(); for (const auto& change : entity_data) { @@ -571,10 +595,19 @@ // If sync already enabled (usual case without data corruption), we can // initialize the provider immediately. local_cache_guid_ = local_cache_guid_in_metadata; + + // Get stored sync invalidation fields to initialize local device info. This + // is needed to prevent an unnecessary DeviceInfo commit on browser startup + // when the SyncInvalidationsService is not initialized. + auto iter = all_data_.find(local_cache_guid_); + DCHECK(iter != all_data_.end()); + local_device_info_provider_->Initialize( local_cache_guid_, GetLocalClientName(), local_device_name_info_.manufacturer_name, - local_device_name_info_.model_name); + local_device_name_info_.model_name, + iter->second->invalidation_fields().instance_id_token(), + ExtractInterestedDataTypes(*iter->second)); // This probably isn't strictly needed, but in case the cache_guid has changed // we save the new one to prefs. @@ -590,7 +623,7 @@ } } -void DeviceInfoSyncBridge::ReconcileLocalAndStored() { +bool DeviceInfoSyncBridge::ReconcileLocalAndStored() { const DeviceInfo* current_info = local_device_info_provider_->GetLocalDeviceInfo(); DCHECK(current_info); @@ -600,16 +633,24 @@ // Convert to DeviceInfo for Equals function. if (current_info->Equals(*SpecificsToModel(*iter->second))) { + if (pulse_timer_.IsRunning()) { + // No need to update the |pulse_timer| since nothing has changed. + return false; + } + const TimeDelta pulse_delay(DeviceInfoUtil::CalculatePulseDelay( GetLastUpdateTime(*iter->second), Time::Now())); if (!pulse_delay.is_zero()) { pulse_timer_.Start(FROM_HERE, pulse_delay, base::BindOnce(&DeviceInfoSyncBridge::SendLocalData, base::Unretained(this))); - return; + return false; } } + + // Either the local data was updated, or it's time for a pulse update. SendLocalData(); + return true; } void DeviceInfoSyncBridge::SendLocalData() {
diff --git a/components/sync_device_info/device_info_sync_bridge.h b/components/sync_device_info/device_info_sync_bridge.h index a89b6c3..1211d682 100644 --- a/components/sync_device_info/device_info_sync_bridge.h +++ b/components/sync_device_info/device_info_sync_bridge.h
@@ -52,8 +52,10 @@ // change. Used when the caller knows a property of local device info has // changed (e.g. SharingInfo), and must be sync-ed to other devices as soon as // possible, without waiting for the periodic commits. |callback| will be - // called when device info is synced. - void RefreshLocalDeviceInfo(base::OnceClosure callback); + // called when device info is synced. The device info will be compared with + // the local copy. If the data has been updated, then it will be committed. + // Otherwise nothing happens and the |callback| will be never called. + void RefreshLocalDeviceInfoIfNeeded(base::OnceClosure callback); // ModelTypeSyncBridge implementation. void OnSyncStarting(const DataTypeActivationRequest& request) override; @@ -113,14 +115,16 @@ LocalDeviceNameInfo local_device_name_info); void OnReadAllData(std::unique_ptr<ClientIdToSpecifics> all_data, const base::Optional<syncer::ModelError>& error); + void OnSyncInvalidationsInitialized(); void OnReadAllMetadata(const base::Optional<syncer::ModelError>& error, std::unique_ptr<MetadataBatch> metadata_batch); void OnCommit(const base::Optional<syncer::ModelError>& error); // Performs reconciliation between the locally provided device info and the // stored device info data. If the sets of data differ, then we consider this - // a local change and we send it to the processor. - void ReconcileLocalAndStored(); + // a local change and we send it to the processor. Returns true if the local + // data has been changed and sent to the processor. + bool ReconcileLocalAndStored(); // Stores the updated version of the local copy of device info in durable // storage, in memory, and informs sync of the change. Must not be called
diff --git a/components/sync_device_info/device_info_sync_bridge_unittest.cc b/components/sync_device_info/device_info_sync_bridge_unittest.cc index 028b64c..67e7bcd 100644 --- a/components/sync_device_info/device_info_sync_bridge_unittest.cc +++ b/components/sync_device_info/device_info_sync_bridge_unittest.cc
@@ -44,6 +44,7 @@ using testing::IsEmpty; using testing::IsNull; using testing::Matcher; +using testing::NiceMock; using testing::NotNull; using testing::Pair; using testing::Return; @@ -338,7 +339,9 @@ void Initialize(const std::string& cache_guid, const std::string& session_name, const std::string& manufacturer_name, - const std::string& model_name) override { + const std::string& model_name, + const std::string& last_fcm_registration_token, + const ModelTypeSet& last_interested_data_types) override { std::set<sync_pb::SharingSpecificFields::EnabledFeatures> sharing_enabled_features{SharingEnabledFeaturesForSuffix(kLocalSuffix)}; local_device_info_ = std::make_unique<DeviceInfo>( @@ -356,8 +359,7 @@ SharingSenderIdP256dhForSuffix(kLocalSuffix), SharingSenderIdAuthSecretForSuffix(kLocalSuffix)}, sharing_enabled_features), - SyncInvalidationsInstanceIdTokenForSuffix(kLocalSuffix), - SyncInvalidationsInterestedDataTypes()); + last_fcm_registration_token, last_interested_data_types); } void Clear() override { local_device_info_.reset(); } @@ -372,6 +374,15 @@ } const DeviceInfo* GetLocalDeviceInfo() const override { + if (local_device_info_) { + if (fcm_registration_token_) { + local_device_info_->set_fcm_registration_token( + *fcm_registration_token_); + } + if (interested_data_types_) { + local_device_info_->set_interested_data_types(*interested_data_types_); + } + } return local_device_info_.get(); } @@ -381,8 +392,18 @@ return {}; } + void UpdateFCMRegistrationToken(const std::string& fcm_registration_token) { + fcm_registration_token_ = fcm_registration_token; + } + + void UpdateInterestedDataTypes(const ModelTypeSet& data_types) { + interested_data_types_ = data_types; + } + private: std::unique_ptr<DeviceInfo> local_device_info_; + base::Optional<std::string> fcm_registration_token_; + base::Optional<ModelTypeSet> interested_data_types_; DISALLOW_COPY_AND_ASSIGN(TestLocalDeviceInfoProvider); }; // namespace @@ -425,8 +446,12 @@ // Initialized the bridge based on the current local device and store. void InitializeBridge() { + auto local_device_info_provider = + std::make_unique<TestLocalDeviceInfoProvider>(); + // Store a pointer to be able to update the local device info fields. + local_device_info_provider_ = local_device_info_provider.get(); bridge_ = std::make_unique<DeviceInfoSyncBridge>( - std::make_unique<TestLocalDeviceInfoProvider>(), + std::move(local_device_info_provider), ModelTypeStoreTestUtil::FactoryForForwardingStore(store_.get()), mock_processor_.CreateForwardingProcessor(), std::make_unique<DeviceInfoPrefs>(&pref_service_, &clock_)); @@ -440,10 +465,10 @@ task_environment_.RunUntilIdle(); } - // Creates the bridge with no prior data on the store, and mimics sync being - // enabled by the user with no remote data. - void InitializeAndMergeInitialData(SyncMode sync_mode) { - InitializeAndPump(); + // Mimics sync being enabled by the user with no remote data. Must be called + // when the bridge is initialized. + void EnableSyncAndMergeInitialData(SyncMode sync_mode) { + DCHECK(bridge_); bridge()->OnSyncStarting(TestDataTypeActivationRequest(sync_mode)); std::unique_ptr<MetadataChangeList> metadata_change_list = @@ -455,6 +480,13 @@ EntityChangeList()); } + // Creates the bridge with no prior data on the store, and mimics sync being + // enabled by the user with no remote data. + void InitializeAndMergeInitialData(SyncMode sync_mode) { + InitializeAndPump(); + EnableSyncAndMergeInitialData(sync_mode); + } + // Allows access to the store before that will ultimately be used to // initialize the bridge. ModelTypeStore* store() { @@ -465,8 +497,8 @@ // Get the number of times the bridge notifies observers of changes. int change_count() { return change_count_; } - LocalDeviceInfoProvider* local_device() { - return bridge_->GetLocalDeviceInfoProvider(); + TestLocalDeviceInfoProvider* local_device() { + return local_device_info_provider_; } // Allows access to the bridge after InitializeBridge() is called. @@ -493,7 +525,7 @@ void ForcePulse() { bridge()->ForcePulseForTest(); } void RefreshLocalDeviceInfo() { - bridge()->RefreshLocalDeviceInfo(base::DoNothing()); + bridge()->RefreshLocalDeviceInfoIfNeeded(base::DoNothing()); } void CommitToStoreAndWait(std::unique_ptr<WriteBatch> batch) { @@ -602,7 +634,7 @@ // In memory model type store needs to be able to post tasks. base::test::TaskEnvironment task_environment_; - testing::NiceMock<MockModelTypeChangeProcessor> mock_processor_; + NiceMock<MockModelTypeChangeProcessor> mock_processor_; // Holds the store. const std::unique_ptr<ModelTypeStore> store_; @@ -611,9 +643,12 @@ const LocalDeviceNameInfo local_device_name_info_; TestingPrefServiceSimple pref_service_; + // Not initialized immediately (upon test's constructor). This allows each // test case to modify the dependencies the bridge will be constructed with. std::unique_ptr<DeviceInfoSyncBridge> bridge_; + + TestLocalDeviceInfoProvider* local_device_info_provider_ = nullptr; }; TEST_F(DeviceInfoSyncBridgeTest, BeforeSyncEnabled) { @@ -1193,8 +1228,16 @@ EXPECT_EQ(1, change_count()); testing::Mock::VerifyAndClearExpectations(processor()); + // Check that the device is not updated if nothing has been changed. + RefreshLocalDeviceInfo(); + EXPECT_EQ(1, change_count()); + // Ensure |last_updated| is about now, plus or minus a little bit. EXPECT_CALL(*processor(), Put(_, HasSpecifics(HasLastUpdatedAboutNow()), _)); + ASSERT_THAT(local_device()->GetLocalDeviceInfo()->fcm_registration_token(), + IsEmpty()); + local_device()->UpdateFCMRegistrationToken( + SyncInvalidationsInstanceIdTokenForSuffix(kLocalSuffix)); RefreshLocalDeviceInfo(); EXPECT_EQ(2, change_count()); } @@ -1292,14 +1335,23 @@ HasSpecifics( AllOf(HasInstanceIdToken(), HasAnyInterestedDataTypes())), _)); - InitializeAndMergeInitialData(SyncMode::kFull); + InitializeAndPump(); + local_device()->UpdateFCMRegistrationToken( + SyncInvalidationsInstanceIdTokenForSuffix(kLocalSuffix)); + local_device()->UpdateInterestedDataTypes( + SyncInvalidationsInterestedDataTypes()); + EnableSyncAndMergeInitialData(SyncMode::kFull); } TEST_F(DeviceInfoSyncBridgeTest, ShouldNotifyWhenDeviceInfoIsSynced) { InitializeAndMergeInitialData(SyncMode::kFull); base::MockOnceClosure callback; - bridge()->RefreshLocalDeviceInfo(callback.Get()); + ASSERT_THAT(local_device()->GetLocalDeviceInfo()->fcm_registration_token(), + IsEmpty()); + local_device()->UpdateFCMRegistrationToken( + SyncInvalidationsInstanceIdTokenForSuffix(kLocalSuffix)); + bridge()->RefreshLocalDeviceInfoIfNeeded(callback.Get()); std::string guid = local_device()->GetLocalDeviceInfo()->guid(); EXPECT_CALL(*processor(), IsEntityUnsynced(guid)).WillOnce(Return(true)); @@ -1313,6 +1365,19 @@ EntityChangeList()); } +TEST_F(DeviceInfoSyncBridgeTest, ShouldNotNotifyWhenDeviceInfoIsUnchanged) { + InitializeAndMergeInitialData(SyncMode::kFull); + + base::MockOnceClosure callback; + bridge()->RefreshLocalDeviceInfoIfNeeded(callback.Get()); + + std::string guid = local_device()->GetLocalDeviceInfo()->guid(); + EXPECT_CALL(*processor(), IsEntityUnsynced(guid)).WillOnce(Return(false)); + EXPECT_CALL(callback, Run()).Times(0); + bridge()->ApplySyncChanges(bridge()->CreateMetadataChangeList(), + EntityChangeList()); +} + // This test mimics the case when OnSyncStarting is called before the metadata // is loaded from the storage. TEST_F(DeviceInfoSyncBridgeTest,
diff --git a/components/sync_device_info/device_info_sync_client.h b/components/sync_device_info/device_info_sync_client.h index 230539e..1f9a094 100644 --- a/components/sync_device_info/device_info_sync_client.h +++ b/components/sync_device_info/device_info_sync_client.h
@@ -23,8 +23,15 @@ virtual bool GetSendTabToSelfReceivingEnabled() const = 0; virtual base::Optional<DeviceInfo::SharingInfo> GetLocalSharingInfo() const = 0; - virtual std::string GetFCMRegistrationToken() const = 0; - virtual ModelTypeSet GetInterestedDataTypes() const = 0; + + // Returns current FCM registration token if known, empty if the invalidation + // service is not enabled. base::nullopt will be returned if the token has + // been requested but hasn't been retrieved yet. + virtual base::Optional<std::string> GetFCMRegistrationToken() const = 0; + + // A list of enabled data types, base::nullopt if the invalidation service is + // not initialized yet. + virtual base::Optional<ModelTypeSet> GetInterestedDataTypes() const = 0; private: DISALLOW_COPY_AND_ASSIGN(DeviceInfoSyncClient);
diff --git a/components/sync_device_info/device_info_sync_service.h b/components/sync_device_info/device_info_sync_service.h index d76c7d4..6ca64fc 100644 --- a/components/sync_device_info/device_info_sync_service.h +++ b/components/sync_device_info/device_info_sync_service.h
@@ -39,8 +39,11 @@ // Interface to refresh local copy of device info in memory, and informs sync // of the change. Used when the caller knows a property of local device info // has changed (e.g. SharingInfo), and must be sync-ed to other devices as - // soon as possible, without waiting for the periodic commits. |callback| will - // be called when device info is synced. + // soon as possible, without waiting for the periodic commits. The device info + // will be compared to the local copy. If the device info has been actually + // changed, then it will be committed and the |callback| will be called when + // device info is synced. Otherwise nothing happens and the |callback| will + // never be called. virtual void RefreshLocalDeviceInfo( base::OnceClosure callback = base::DoNothing()) = 0; };
diff --git a/components/sync_device_info/device_info_sync_service_impl.cc b/components/sync_device_info/device_info_sync_service_impl.cc index a24ee0c..cf97cc9 100644 --- a/components/sync_device_info/device_info_sync_service_impl.cc +++ b/components/sync_device_info/device_info_sync_service_impl.cc
@@ -68,7 +68,7 @@ void DeviceInfoSyncServiceImpl::RefreshLocalDeviceInfo( base::OnceClosure callback) { - bridge_->RefreshLocalDeviceInfo(std::move(callback)); + bridge_->RefreshLocalDeviceInfoIfNeeded(std::move(callback)); } void DeviceInfoSyncServiceImpl::OnFCMRegistrationTokenChanged() {
diff --git a/components/sync_device_info/local_device_info_provider.h b/components/sync_device_info/local_device_info_provider.h index abf9c29..f4c4e28b 100644 --- a/components/sync_device_info/local_device_info_provider.h +++ b/components/sync_device_info/local_device_info_provider.h
@@ -9,6 +9,7 @@ #include <string> #include "base/callback_list.h" +#include "components/sync/base/model_type.h" #include "components/version_info/version_info.h" namespace syncer { @@ -41,7 +42,9 @@ virtual void Initialize(const std::string& cache_guid, const std::string& client_name, const std::string& manufacturer_name, - const std::string& model_name) = 0; + const std::string& model_name, + const std::string& last_fcm_registration_token, + const ModelTypeSet& last_interested_data_types) = 0; virtual void Clear() = 0; // Updates the local device's client name. Initialize() must be called before
diff --git a/components/sync_device_info/local_device_info_provider_impl.cc b/components/sync_device_info/local_device_info_provider_impl.cc index 8baf8b1..28fa7c0 100644 --- a/components/sync_device_info/local_device_info_provider_impl.cc +++ b/components/sync_device_info/local_device_info_provider_impl.cc
@@ -37,13 +37,25 @@ return nullptr; } + // Pull new values for settings that aren't automatically updated. local_device_info_->set_send_tab_to_self_receiving_enabled( sync_client_->GetSendTabToSelfReceivingEnabled()); local_device_info_->set_sharing_info(sync_client_->GetLocalSharingInfo()); - local_device_info_->set_fcm_registration_token( - sync_client_->GetFCMRegistrationToken()); - local_device_info_->set_interested_data_types( - sync_client_->GetInterestedDataTypes()); + + // Do not update previous values if the service is not fully initialized. + // base::nullopt means that the value is unknown yet and the previous value + // should be kept. + const base::Optional<std::string> fcm_token = + sync_client_->GetFCMRegistrationToken(); + if (fcm_token) { + local_device_info_->set_fcm_registration_token(*fcm_token); + } + + const base::Optional<ModelTypeSet> interested_data_types = + sync_client_->GetInterestedDataTypes(); + if (interested_data_types) { + local_device_info_->set_interested_data_types(*interested_data_types); + } return local_device_info_.get(); } @@ -60,7 +72,9 @@ const std::string& cache_guid, const std::string& client_name, const std::string& manufacturer_name, - const std::string& model_name) { + const std::string& model_name, + const std::string& last_fcm_registration_token, + const ModelTypeSet& last_interested_data_types) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(!cache_guid.empty()); @@ -73,9 +87,8 @@ /*last_updated_timestamp=*/base::Time(), DeviceInfoUtil::GetPulseInterval(), sync_client_->GetSendTabToSelfReceivingEnabled(), - sync_client_->GetLocalSharingInfo(), - sync_client_->GetFCMRegistrationToken(), - sync_client_->GetInterestedDataTypes()); + sync_client_->GetLocalSharingInfo(), last_fcm_registration_token, + last_interested_data_types); // Notify observers. callback_list_.Notify();
diff --git a/components/sync_device_info/local_device_info_provider_impl.h b/components/sync_device_info/local_device_info_provider_impl.h index 08a023e..d6e12fff 100644 --- a/components/sync_device_info/local_device_info_provider_impl.h +++ b/components/sync_device_info/local_device_info_provider_impl.h
@@ -32,7 +32,9 @@ void Initialize(const std::string& cache_guid, const std::string& client_name, const std::string& manufacturer_name, - const std::string& model_name) override; + const std::string& model_name, + const std::string& last_fcm_registration_token, + const ModelTypeSet& last_interested_data_types) override; void Clear() override; void UpdateClientName(const std::string& client_name) override; version_info::Channel GetChannel() const override;
diff --git a/components/sync_device_info/local_device_info_provider_impl_unittest.cc b/components/sync_device_info/local_device_info_provider_impl_unittest.cc index 68f5207..55227c1e 100644 --- a/components/sync_device_info/local_device_info_provider_impl_unittest.cc +++ b/components/sync_device_info/local_device_info_provider_impl_unittest.cc
@@ -47,8 +47,14 @@ GetLocalSharingInfo, (), (const override)); - MOCK_METHOD(std::string, GetFCMRegistrationToken, (), (const override)); - MOCK_METHOD(ModelTypeSet, GetInterestedDataTypes, (), (const override)); + MOCK_METHOD(base::Optional<std::string>, + GetFCMRegistrationToken, + (), + (const override)); + MOCK_METHOD(base::Optional<ModelTypeSet>, + GetInterestedDataTypes, + (), + (const override)); private: DISALLOW_COPY_AND_ASSIGN(MockDeviceInfoSyncClient); @@ -73,7 +79,9 @@ void InitializeProvider(const std::string& guid) { provider_->Initialize(guid, kLocalDeviceClientName, - kLocalDeviceManufacturerName, kLocalDeviceModelName); + kLocalDeviceManufacturerName, kLocalDeviceModelName, + /*last_fcm_registration_token=*/std::string(), + ModelTypeSet()); } testing::NiceMock<MockDeviceInfoSyncClient> device_info_sync_client_; @@ -179,7 +187,7 @@ const std::string kFCMRegistrationToken = "token"; EXPECT_CALL(device_info_sync_client_, GetFCMRegistrationToken()) - .WillOnce(Return(kFCMRegistrationToken)); + .WillRepeatedly(Return(kFCMRegistrationToken)); EXPECT_EQ(provider_->GetLocalDeviceInfo()->fcm_registration_token(), kFCMRegistrationToken); @@ -192,10 +200,27 @@ const ModelTypeSet kTypes = ModelTypeSet(BOOKMARKS); EXPECT_CALL(device_info_sync_client_, GetInterestedDataTypes()) - .WillOnce(Return(kTypes)); + .WillRepeatedly(Return(kTypes)); EXPECT_EQ(provider_->GetLocalDeviceInfo()->interested_data_types(), kTypes); } +TEST_F(LocalDeviceInfoProviderImplTest, ShouldKeepStoredInvalidationFields) { + const std::string kFCMRegistrationToken = "fcm_token"; + const ModelTypeSet kInterestedDataTypes(BOOKMARKS); + provider_->Initialize(kLocalDeviceGuid, kLocalDeviceClientName, + kLocalDeviceManufacturerName, kLocalDeviceModelName, + kFCMRegistrationToken, kInterestedDataTypes); + + EXPECT_CALL(device_info_sync_client_, GetFCMRegistrationToken()) + .WillOnce(Return(base::nullopt)); + EXPECT_CALL(device_info_sync_client_, GetInterestedDataTypes()) + .WillOnce(Return(base::nullopt)); + + const DeviceInfo* local_device_info = provider_->GetLocalDeviceInfo(); + EXPECT_EQ(local_device_info->interested_data_types(), kInterestedDataTypes); + EXPECT_EQ(local_device_info->fcm_registration_token(), kFCMRegistrationToken); +} + } // namespace } // namespace syncer
diff --git a/components/viz/service/display_embedder/output_presenter_x11.cc b/components/viz/service/display_embedder/output_presenter_x11.cc index 59ee389..e16a2b4 100644 --- a/components/viz/service/display_embedder/output_presenter_x11.cc +++ b/components/viz/service/display_embedder/output_presenter_x11.cc
@@ -423,7 +423,7 @@ DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); auto* connection = x11::Connection::Get(); event_source_ = std::make_unique<ui::X11EventSource>(connection); - connection->RemoveEventObserver(this); + connection->AddEventObserver(this); auto* present = &connection->present(); event_id_ = connection->GenerateId<uint32_t>();
diff --git a/content/browser/accessibility/accessibility_tree_formatter_mac.h b/content/browser/accessibility/accessibility_tree_formatter_mac.h index bbcd197..5500a598 100644 --- a/content/browser/accessibility/accessibility_tree_formatter_mac.h +++ b/content/browser/accessibility/accessibility_tree_formatter_mac.h
@@ -54,7 +54,6 @@ const ui::AXPropertyNode& property_node, const a11y::LineIndexer* line_indexer) const; - base::Value PopulateSize(const BrowserAccessibilityCocoa*) const; base::Value PopulatePosition(const BrowserAccessibilityCocoa*) const; base::Value PopulatePoint(NSPoint) const; base::Value PopulateSize(NSSize) const;
diff --git a/content/browser/accessibility/accessibility_tree_formatter_mac.mm b/content/browser/accessibility/accessibility_tree_formatter_mac.mm index 8d3b32c..ed70b7dd 100644 --- a/content/browser/accessibility/accessibility_tree_formatter_mac.mm +++ b/content/browser/accessibility/accessibility_tree_formatter_mac.mm
@@ -45,9 +45,6 @@ const char kPositionDictAttr[] = "position"; const char kXCoordDictAttr[] = "x"; const char kYCoordDictAttr[] = "y"; -const char kSizeDictAttr[] = "size"; -const char kWidthDictAttr[] = "width"; -const char kHeightDictAttr[] = "height"; const char kRangeLocDictAttr[] = "loc"; const char kRangeLenDictAttr[] = "len"; @@ -171,9 +168,8 @@ dict->SetKey("id", base::Value(base::NumberToString16(owner_node->GetId()))); - // Position and size + // Position (no size since it's exposed as standard AXSize attribute) dict->SetPath(kPositionDictAttr, PopulatePosition(cocoa_node)); - dict->SetPath(kSizeDictAttr, PopulateSize(cocoa_node)); } // Dump all attributes if match-all filter is specified. @@ -206,15 +202,6 @@ } } -base::Value AccessibilityTreeFormatterMac::PopulateSize( - const BrowserAccessibilityCocoa* cocoa_node) const { - base::Value size(base::Value::Type::DICTIONARY); - NSSize node_size = [[cocoa_node size] sizeValue]; - size.SetIntPath(kHeightDictAttr, static_cast<int>(node_size.height)); - size.SetIntPath(kWidthDictAttr, static_cast<int>(node_size.width)); - return size; -} - base::Value AccessibilityTreeFormatterMac::PopulatePosition( const BrowserAccessibilityCocoa* cocoa_node) const { BrowserAccessibility* node = [cocoa_node owner]; @@ -261,10 +248,14 @@ return base::Value([value intValue]); } - // NSRange - if ([value isKindOfClass:[NSValue class]] && - 0 == strcmp([value objCType], @encode(NSRange))) { - return PopulateRange([value rangeValue]); + // NSRange, NSSize + if ([value isKindOfClass:[NSValue class]]) { + if (0 == strcmp([value objCType], @encode(NSRange))) { + return PopulateRange([value rangeValue]); + } + if (0 == strcmp([value objCType], @encode(NSSize))) { + return PopulateSize([value sizeValue]); + } } // AXTextMarker @@ -465,14 +456,6 @@ &line); continue; } - // Special case: size. - if (item.first == kSizeDictAttr) { - WriteAttribute(false, - FormatCoordinates(item.second, kSizeDictAttr, - kWidthDictAttr, kHeightDictAttr), - &line); - continue; - } // Write formatted value. std::string formatted_value = FormatAttributeValue(item.second);
diff --git a/content/browser/accessibility/browser_accessibility_android.cc b/content/browser/accessibility/browser_accessibility_android.cc index 1e02e70..55a236e0 100644 --- a/content/browser/accessibility/browser_accessibility_android.cc +++ b/content/browser/accessibility/browser_accessibility_android.cc
@@ -181,6 +181,7 @@ return (ui::IsTableLike(GetRole()) || GetRole() == ax::mojom::Role::kList || GetRole() == ax::mojom::Role::kListBox || GetRole() == ax::mojom::Role::kDescriptionList || + GetRole() == ax::mojom::Role::kDirectory || GetRole() == ax::mojom::Role::kTree); }
diff --git a/content/browser/accessibility/browser_accessibility_cocoa.mm b/content/browser/accessibility/browser_accessibility_cocoa.mm index 6ad3a7d..b30b7d4c 100644 --- a/content/browser/accessibility/browser_accessibility_cocoa.mm +++ b/content/browser/accessibility/browser_accessibility_cocoa.mm
@@ -2374,8 +2374,10 @@ if ([self internalRole] == ax::mojom::Role::kDescriptionList) return NSAccessibilityDefinitionListSubrole; - if ([self internalRole] == ax::mojom::Role::kList) + if ([self internalRole] == ax::mojom::Role::kDirectory || + [self internalRole] == ax::mojom::Role::kList) { return NSAccessibilityContentListSubrole; + } return [AXPlatformNodeCocoa nativeSubroleFromAXRole:[self internalRole]]; }
diff --git a/content/browser/hid/hid_browsertest.cc b/content/browser/hid/hid_browsertest.cc index 3a6c6af..0b426177 100644 --- a/content/browser/hid/hid_browsertest.cc +++ b/content/browser/hid/hid_browsertest.cc
@@ -23,7 +23,6 @@ #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/mojom/hid/hid.mojom.h" -using testing::_; using testing::ByMove; using testing::Exactly; using testing::Return; @@ -32,6 +31,22 @@ namespace { +// Create a device with a single collection containing an input report and an +// output report. Both reports have report ID 0. +device::mojom::HidDeviceInfoPtr CreateTestDeviceWithInputAndOutputReports() { + auto collection = device::mojom::HidCollectionInfo::New(); + collection->usage = device::mojom::HidUsageAndPage::New(0x0001, 0xff00); + collection->input_reports.push_back( + device::mojom::HidReportDescription::New()); + collection->output_reports.push_back( + device::mojom::HidReportDescription::New()); + + auto device = device::mojom::HidDeviceInfo::New(); + device->guid = "test-guid"; + device->collections.push_back(std::move(collection)); + return device; +} + class HidTest : public ContentBrowserTest { public: HidTest() { @@ -71,12 +86,12 @@ // Three devices are added but only two will have permission granted. for (int i = 0; i < 3; i++) { - auto device = device::mojom::HidDeviceInfo::New(); + auto device = CreateTestDeviceWithInputAndOutputReports(); device->guid = base::StringPrintf("test-guid-%02d", i); hid_manager()->AddDevice(std::move(device)); } - EXPECT_CALL(delegate(), HasDevicePermission(_, _, _)) + EXPECT_CALL(delegate(), HasDevicePermission) .WillOnce(Return(true)) .WillOnce(Return(false)) .WillOnce(Return(true)); @@ -90,13 +105,10 @@ IN_PROC_BROWSER_TEST_F(HidTest, RequestDevice) { EXPECT_TRUE(NavigateToURL(shell(), GetTestUrl(nullptr, "simple_page.html"))); - EXPECT_CALL(delegate(), CanRequestDevicePermission(_, _)) - .WillOnce(Return(true)); + EXPECT_CALL(delegate(), CanRequestDevicePermission).WillOnce(Return(true)); - auto device = device::mojom::HidDeviceInfo::New(); - device->guid = "test-guid"; std::vector<device::mojom::HidDeviceInfoPtr> devices; - devices.push_back(std::move(device)); + devices.push_back(CreateTestDeviceWithInputAndOutputReports()); EXPECT_CALL(delegate(), RunChooserInternal) .WillOnce(Return(ByMove(std::move(devices)))); @@ -112,8 +124,7 @@ IN_PROC_BROWSER_TEST_F(HidTest, DisallowRequestDevice) { EXPECT_TRUE(NavigateToURL(shell(), GetTestUrl(nullptr, "simple_page.html"))); - EXPECT_CALL(delegate(), CanRequestDevicePermission(_, _)) - .WillOnce(Return(false)); + EXPECT_CALL(delegate(), CanRequestDevicePermission).WillOnce(Return(false)); EXPECT_CALL(delegate(), RunChooserInternal).Times(Exactly(0)); EXPECT_EQ(0, EvalJs(shell(), @@ -123,4 +134,50 @@ })())")); } +IN_PROC_BROWSER_TEST_F(HidTest, ProtectedReportsAreFiltered) { + LOG(ERROR) << "HidTest.ProtectedReportsAreFiltered"; + EXPECT_TRUE(NavigateToURL(shell(), GetTestUrl(nullptr, "simple_page.html"))); + + auto device = CreateTestDeviceWithInputAndOutputReports(); + + // Mark the input report as protected. + device->protected_input_report_ids = std::vector<uint8_t>{0}; + + hid_manager()->AddDevice(std::move(device)); + + EXPECT_CALL(delegate(), HasDevicePermission).WillOnce(Return(true)); + + EXPECT_EQ(true, EvalJs(shell(), + R"((async () => { + let devices = await navigator.hid.getDevices(); + return devices instanceof Array + && devices.length == 1 + && devices[0] instanceof HIDDevice + && devices[0].collections instanceof Array + && devices[0].collections.length == 1 + && devices[0].collections[0].inputReports instanceof Array + && devices[0].collections[0].inputReports.length == 0 + && devices[0].collections[0].outputReports instanceof Array + && devices[0].collections[0].outputReports.length == 1; + })())")); +} + +IN_PROC_BROWSER_TEST_F(HidTest, DeviceWithAllProtectedReportsIsExcluded) { + EXPECT_TRUE(NavigateToURL(shell(), GetTestUrl(nullptr, "simple_page.html"))); + + auto device = CreateTestDeviceWithInputAndOutputReports(); + + // Mark both the input and output reports as protected. + device->protected_input_report_ids = std::vector<uint8_t>{0}; + device->protected_output_report_ids = std::vector<uint8_t>{0}; + + hid_manager()->AddDevice(std::move(device)); + + EXPECT_EQ(true, EvalJs(shell(), + R"((async () => { + let devices = await navigator.hid.getDevices(); + return devices instanceof Array && devices.length == 0; + })())")); +} + } // namespace content
diff --git a/content/browser/hid/hid_service.cc b/content/browser/hid/hid_service.cc index c99b249..eb42cccc 100644 --- a/content/browser/hid/hid_service.cc +++ b/content/browser/hid/hid_service.cc
@@ -21,6 +21,52 @@ namespace content { +namespace { + +// Removes reports from |device| if the report IDs match the IDs in the +// protected report ID lists. If all of the reports are removed from a +// collection, the collection is also removed. +void RemoveProtectedReports(device::mojom::HidDeviceInfo& device) { + std::vector<device::mojom::HidCollectionInfoPtr> collections; + for (auto& collection : device.collections) { + std::vector<device::mojom::HidReportDescriptionPtr> input_reports; + for (auto& report : collection->input_reports) { + if (!device.protected_input_report_ids.has_value() || + !base::Contains(*device.protected_input_report_ids, + report->report_id)) { + input_reports.push_back(std::move(report)); + } + } + std::vector<device::mojom::HidReportDescriptionPtr> output_reports; + for (auto& report : collection->output_reports) { + if (!device.protected_output_report_ids.has_value() || + !base::Contains(*device.protected_output_report_ids, + report->report_id)) { + output_reports.push_back(std::move(report)); + } + } + std::vector<device::mojom::HidReportDescriptionPtr> feature_reports; + for (auto& report : collection->feature_reports) { + if (!device.protected_feature_report_ids.has_value() || + !base::Contains(*device.protected_feature_report_ids, + report->report_id)) { + feature_reports.push_back(std::move(report)); + } + } + // Only keep the collection if it has at least one report. + if (!input_reports.empty() || !output_reports.empty() || + !feature_reports.empty()) { + collection->input_reports = std::move(input_reports); + collection->output_reports = std::move(output_reports); + collection->feature_reports = std::move(feature_reports); + collections.push_back(std::move(collection)); + } + } + device.collections = std::move(collections); +} + +} // namespace + HidService::HidService(RenderFrameHost* render_frame_host, mojo::PendingReceiver<blink::mojom::HidService> receiver) : FrameServiceBase(render_frame_host, std::move(receiver)), @@ -121,6 +167,7 @@ ->GetHidManager(WebContents::FromRenderFrameHost(render_frame_host())) ->Connect( device_guid, std::move(client), std::move(watcher), + /*allow_protected_reports=*/false, base::BindOnce(&HidService::FinishConnect, weak_factory_.GetWeakPtr(), std::move(callback))); } @@ -151,8 +198,13 @@ return; } + auto filtered_device_info = device_info.Clone(); + RemoveProtectedReports(*filtered_device_info); + if (filtered_device_info->collections.empty()) + return; + for (auto& client : clients_) - client->DeviceAdded(device_info.Clone()); + client->DeviceAdded(filtered_device_info->Clone()); } void HidService::OnDeviceRemoved( @@ -163,8 +215,13 @@ return; } + auto filtered_device_info = device_info.Clone(); + RemoveProtectedReports(*filtered_device_info); + if (filtered_device_info->collections.empty()) + return; + for (auto& client : clients_) - client->DeviceRemoved(device_info.Clone()); + client->DeviceRemoved(filtered_device_info->Clone()); } void HidService::OnHidManagerConnectionError() { @@ -210,6 +267,10 @@ std::vector<device::mojom::HidDeviceInfoPtr> result; HidDelegate* delegate = GetContentClient()->browser()->GetHidDelegate(); for (auto& device : devices) { + RemoveProtectedReports(*device); + if (device->collections.empty()) + continue; + if (delegate->HasDevicePermission( WebContents::FromRenderFrameHost(render_frame_host()), origin(), *device))
diff --git a/content/browser/hid/hid_service_unittest.cc b/content/browser/hid/hid_service_unittest.cc index 7c4b9c9..8ca4b56 100644 --- a/content/browser/hid/hid_service_unittest.cc +++ b/content/browser/hid/hid_service_unittest.cc
@@ -128,8 +128,13 @@ contents()->GetMainFrame()->GetHidService( service.BindNewPipeAndPassReceiver()); + auto collection = device::mojom::HidCollectionInfo::New(); + collection->usage = device::mojom::HidUsageAndPage::New(0xff00, 0x0001); + collection->input_reports.push_back( + device::mojom::HidReportDescription::New()); auto device_info = device::mojom::HidDeviceInfo::New(); device_info->guid = kTestGuid; + device_info->collections.push_back(std::move(collection)); ConnectDevice(*device_info); EXPECT_CALL(hid_delegate(), HasDevicePermission).WillOnce(Return(true)); @@ -152,8 +157,13 @@ contents()->GetMainFrame()->GetHidService( service.BindNewPipeAndPassReceiver()); + auto collection = device::mojom::HidCollectionInfo::New(); + collection->usage = device::mojom::HidUsageAndPage::New(0xff00, 0x0001); + collection->input_reports.push_back( + device::mojom::HidReportDescription::New()); auto device_info = device::mojom::HidDeviceInfo::New(); device_info->guid = kTestGuid; + device_info->collections.push_back(std::move(collection)); ConnectDevice(*device_info); EXPECT_CALL(hid_delegate(), HasDevicePermission).WillOnce(Return(false)); @@ -326,8 +336,13 @@ EXPECT_TRUE(devices.empty()); // 2. Connect a device and wait for DeviceAdded. + auto collection = device::mojom::HidCollectionInfo::New(); + collection->usage = device::mojom::HidUsageAndPage::New(0xff00, 0x0001); + collection->input_reports.push_back( + device::mojom::HidReportDescription::New()); auto device_info = device::mojom::HidDeviceInfo::New(); device_info->guid = kTestGuid; + device_info->collections.push_back(std::move(collection)); ConnectDevice(*device_info); device_added_loop.Run();
diff --git a/content/browser/media/media_web_contents_observer.cc b/content/browser/media/media_web_contents_observer.cc index e144c94..4172a48 100644 --- a/content/browser/media/media_web_contents_observer.cc +++ b/content/browser/media/media_web_contents_observer.cc
@@ -599,19 +599,6 @@ std::move(player_receiver)); } -void MediaWebContentsObserver::SetMediaPlayerObserverForMediaPlayer( - const MediaPlayerId& player_id) { - if (!media_player_observer_hosts_.contains(player_id)) { - media_player_observer_hosts_[player_id] = - std::make_unique<MediaPlayerObserverHostImpl>(player_id, this); - } - - DCHECK(media_player_remotes_[player_id]); - media_player_remotes_[player_id]->SetMediaPlayerObserver( - media_player_observer_hosts_[player_id] - ->BindMediaPlayerObserverReceiverAndPassRemote()); -} - void MediaWebContentsObserver::OnMediaPlayerAdded( mojo::PendingRemote<media::mojom::MediaPlayer> player_remote, MediaPlayerId player_id) { @@ -628,9 +615,16 @@ }, base::Unretained(this), player_id)); - // Create a new MediaPlayerObserverHostImpl to be able to receive messages - // from the renderer process via the MediaPlayerObserver mojo interface. - SetMediaPlayerObserverForMediaPlayer(player_id); + // Create a new MediaPlayerObserverHostImpl for |player_id|, implementing the + // media::mojom::MediaPlayerObserver mojo interface, to handle messages sent + // from the MediaPlayer element in the renderer process. + if (!media_player_observer_hosts_.contains(player_id)) { + media_player_observer_hosts_[player_id] = + std::make_unique<MediaPlayerObserverHostImpl>(player_id, this); + } + media_player_remotes_[player_id]->AddMediaPlayerObserver( + media_player_observer_hosts_[player_id] + ->BindMediaPlayerObserverReceiverAndPassRemote()); } #if defined(OS_ANDROID)
diff --git a/content/browser/media/media_web_contents_observer.h b/content/browser/media/media_web_contents_observer.h index db237acd..7795fc7 100644 --- a/content/browser/media/media_web_contents_observer.h +++ b/content/browser/media/media_web_contents_observer.h
@@ -122,10 +122,6 @@ RenderFrameHost* host, mojo::PendingReceiver<media::mojom::MediaPlayerHost> player_receiver); - // Establishes a MediaPlayerObserver for |player_id|, allowing the MediaPlayer - // element in the renderer process to communicate back with the browser. - void SetMediaPlayerObserverForMediaPlayer(const MediaPlayerId& player_id); - // Communicates with the MediaSessionControllerManager to find or create (if // needed) a MediaSessionController identified by |player_id|, in order to // bind its mojo remote for media::mojom::MediaPlayer.
diff --git a/content/browser/media/session/media_session_controller_unittest.cc b/content/browser/media/session/media_session_controller_unittest.cc index b40d4cb..ef2931e 100644 --- a/content/browser/media/session/media_session_controller_unittest.cc +++ b/content/browser/media/session/media_session_controller_unittest.cc
@@ -82,7 +82,7 @@ } // media::mojom::MediaPlayer implementation. - void SetMediaPlayerObserver( + void AddMediaPlayerObserver( mojo::PendingRemote<media::mojom::MediaPlayerObserver>) override {} void RequestPlay() override {
diff --git a/content/browser/picture_in_picture/picture_in_picture_service_impl_unittest.cc b/content/browser/picture_in_picture/picture_in_picture_service_impl_unittest.cc index fd64054b..c7bffe79 100644 --- a/content/browser/picture_in_picture/picture_in_picture_service_impl_unittest.cc +++ b/content/browser/picture_in_picture/picture_in_picture_service_impl_unittest.cc
@@ -113,7 +113,7 @@ } // media::mojom::MediaPlayer implementation. - void SetMediaPlayerObserver( + void AddMediaPlayerObserver( mojo::PendingRemote<media::mojom::MediaPlayerObserver>) override {} void RequestPlay() override {} void RequestPause(bool triggered_by_user) override {}
diff --git a/content/test/data/accessibility/aria/aria-directory-expected-android.txt b/content/test/data/accessibility/aria/aria-directory-expected-android.txt index 53c5958..0780a21 100644 --- a/content/test/data/accessibility/aria/aria-directory-expected-android.txt +++ b/content/test/data/accessibility/aria/aria-directory-expected-android.txt
@@ -1,2 +1,2 @@ android.webkit.WebView focusable focused scrollable -++android.widget.ListView collection +++android.widget.ListView role_description='directory' collection
diff --git a/content/test/data/accessibility/aria/aria-directory-expected-blink.txt b/content/test/data/accessibility/aria/aria-directory-expected-blink.txt index 063e3ab..909bafc 100644 --- a/content/test/data/accessibility/aria/aria-directory-expected-blink.txt +++ b/content/test/data/accessibility/aria/aria-directory-expected-blink.txt
@@ -1,4 +1,4 @@ rootWebArea ++genericContainer ignored ++++genericContainer ignored -++++++list +++++++directory
diff --git a/content/test/data/accessibility/aria/aria-expanded-roles-supported-expected-blink.txt b/content/test/data/accessibility/aria/aria-expanded-roles-supported-expected-blink.txt index f1974bc..e43859fd 100644 --- a/content/test/data/accessibility/aria/aria-expanded-roles-supported-expected-blink.txt +++ b/content/test/data/accessibility/aria/aria-expanded-roles-supported-expected-blink.txt
@@ -16,7 +16,7 @@ ++++++definition ++++++contentDeletion ++++++dialog -++++++list +++++++directory ++++++document ++++++emphasis ++++++feed @@ -83,4 +83,4 @@ ++++++tooltip ++++++tree ++++++treeGrid -++++++treeItem expanded +++++++treeItem expanded \ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-owns-list-expected-mac.txt b/content/test/data/accessibility/aria/aria-owns-list-expected-mac.txt index a2f7408..74bd6ac 100644 --- a/content/test/data/accessibility/aria/aria-owns-list-expected-mac.txt +++ b/content/test/data/accessibility/aria/aria-owns-list-expected-mac.txt
@@ -1,6 +1,6 @@ AXWebArea -++AXList size=(400, 400) -++++AXGroup size=(400, 200) +++AXList AXSize={h: 400, w: 400} +++++AXGroup AXSize={h: 200, w: 400} ++++++AXStaticText AXValue='One' -++++AXGroup size=(400, 200) +++++AXGroup AXSize={h: 200, w: 400} ++++++AXStaticText AXValue='Two'
diff --git a/content/test/data/accessibility/aria/aria-owns-list.html b/content/test/data/accessibility/aria/aria-owns-list.html index b8587971..3c4ad3c 100644 --- a/content/test/data/accessibility/aria/aria-owns-list.html +++ b/content/test/data/accessibility/aria/aria-owns-list.html
@@ -1,5 +1,5 @@ <!-- -@MAC-ALLOW:size=(400* +@MAC-ALLOW:AXSize={h: *, w: 400} @BLINK-ALLOW:pageSize=(400* --> <html>
diff --git a/content/test/data/accessibility/html/iframe-coordinates-cross-process-expected-mac.txt b/content/test/data/accessibility/html/iframe-coordinates-cross-process-expected-mac.txt index 5466623..6a54699 100644 --- a/content/test/data/accessibility/html/iframe-coordinates-cross-process-expected-mac.txt +++ b/content/test/data/accessibility/html/iframe-coordinates-cross-process-expected-mac.txt
@@ -1,15 +1,15 @@ -AXWebArea position=(0, 0) size=(800, 600) -++AXGroup position=(0, 0) size=(300, 150) -++++AXButton position=(25, 25) size=(250, 50) -++AXGroup position=(0, 150) size=(300, 150) -++++AXButton position=(25, 175) size=(250, 50) -++AXGroup position=(0, 300) size=(300, 150) -++++AXGroup position=(0, 300) size=(300, 100) -++++++AXWebArea position=(0, 300) size=(300, 100) -++++++++AXGroup position=(0, 300) size=(300, 100) -++++++++++AXButton position=(25, 325) size=(250, 50) -++AXGroup position=(0, 450) size=(300, 150) -++++AXGroup position=(0, 450) size=(150, 50) -++++++AXWebArea position=(0, 450) size=(150, 50) -++++++++AXGroup position=(0, 450) size=(150, 50) -++++++++++AXButton position=(0, 450) size=(125, 25) \ No newline at end of file +AXWebArea position=(0, 0) AXSize={h: 600, w: 800} +++AXGroup position=(0, 0) AXSize={h: 150, w: 300} +++++AXButton position=(25, 25) AXSize={h: 50, w: 250} +++AXGroup position=(0, 150) AXSize={h: 150, w: 300} +++++AXButton position=(25, 175) AXSize={h: 50, w: 250} +++AXGroup position=(0, 300) AXSize={h: 150, w: 300} +++++AXGroup position=(0, 300) AXSize={h: 100, w: 300} +++++++AXWebArea position=(0, 300) AXSize={h: 100, w: 300} +++++++++AXGroup position=(0, 300) AXSize={h: 100, w: 300} +++++++++++AXButton position=(25, 325) AXSize={h: 50, w: 250} +++AXGroup position=(0, 450) AXSize={h: 150, w: 300} +++++AXGroup position=(0, 450) AXSize={h: 50, w: 150} +++++++AXWebArea position=(0, 450) AXSize={h: 50, w: 150} +++++++++AXGroup position=(0, 450) AXSize={h: 50, w: 150} +++++++++++AXButton position=(0, 450) AXSize={h: 25, w: 125} \ No newline at end of file
diff --git a/content/test/data/accessibility/html/iframe-coordinates-cross-process.html b/content/test/data/accessibility/html/iframe-coordinates-cross-process.html index ce07e220..d2b2491 100644 --- a/content/test/data/accessibility/html/iframe-coordinates-cross-process.html +++ b/content/test/data/accessibility/html/iframe-coordinates-cross-process.html
@@ -2,7 +2,7 @@ @MAC-DENY:AXTitle @MAC-DENY:AXValue @MAC-ALLOW:position* -@MAC-ALLOW:size* +@MAC-ALLOW:AXSize @WIN-DENY:title* @WIN-ALLOW:location*
diff --git a/content/test/data/accessibility/html/iframe-coordinates-expected-mac.txt b/content/test/data/accessibility/html/iframe-coordinates-expected-mac.txt index 6b37577..205cd1b 100644 --- a/content/test/data/accessibility/html/iframe-coordinates-expected-mac.txt +++ b/content/test/data/accessibility/html/iframe-coordinates-expected-mac.txt
@@ -1,15 +1,15 @@ -AXWebArea position=(0, 0) size=(800, 600) -++AXGroup position=(0, 0) size=(300, 150) -++++AXButton position=(25, 25) size=(250, 50) -++AXGroup position=(0, 150) size=(300, 150) -++++AXButton position=(25, 175) size=(250, 50) -++AXGroup position=(0, 300) size=(300, 150) -++++AXGroup position=(0, 300) size=(300, 100) -++++++AXWebArea position=(0, 300) size=(300, 100) -++++++++AXGroup position=(0, 300) size=(300, 100) -++++++++++AXButton position=(25, 325) size=(250, 50) -++AXGroup position=(0, 450) size=(300, 150) -++++AXGroup position=(0, 450) size=(150, 50) -++++++AXWebArea position=(0, 450) size=(150, 50) -++++++++AXGroup position=(0, 450) size=(150, 50) -++++++++++AXButton position=(0, 450) size=(125, 25) +AXWebArea AXSize={h: 600, w: 800} position=(0, 0) +++AXGroup AXSize={h: 150, w: 300} position=(0, 0) +++++AXButton AXSize={h: 50, w: 250} position=(25, 25) +++AXGroup AXSize={h: 150, w: 300} position=(0, 150) +++++AXButton AXSize={h: 50, w: 250} position=(25, 175) +++AXGroup AXSize={h: 150, w: 300} position=(0, 300) +++++AXGroup AXSize={h: 100, w: 300} position=(0, 300) +++++++AXWebArea AXSize={h: 100, w: 300} position=(0, 300) +++++++++AXGroup AXSize={h: 100, w: 300} position=(0, 300) +++++++++++AXButton AXSize={h: 50, w: 250} position=(25, 325) +++AXGroup AXSize={h: 150, w: 300} position=(0, 450) +++++AXGroup AXSize={h: 50, w: 150} position=(0, 450) +++++++AXWebArea AXSize={h: 50, w: 150} position=(0, 450) +++++++++AXGroup AXSize={h: 50, w: 150} position=(0, 450) +++++++++++AXButton AXSize={h: 25, w: 125} position=(0, 450)
diff --git a/content/test/data/accessibility/html/iframe-coordinates.html b/content/test/data/accessibility/html/iframe-coordinates.html index aefb22d0..3632d213 100644 --- a/content/test/data/accessibility/html/iframe-coordinates.html +++ b/content/test/data/accessibility/html/iframe-coordinates.html
@@ -2,7 +2,7 @@ @MAC-DENY:AXTitle @MAC-DENY:AXValue @MAC-ALLOW:position* -@MAC-ALLOW:size* +@MAC-ALLOW:AXSize @WIN-DENY:title* @WIN-ALLOW:location*
diff --git a/content/test/data/accessibility/html/transition-expected-mac.txt b/content/test/data/accessibility/html/transition-expected-mac.txt index f23daed2..fc0cbe5 100644 --- a/content/test/data/accessibility/html/transition-expected-mac.txt +++ b/content/test/data/accessibility/html/transition-expected-mac.txt
@@ -1,4 +1,4 @@ AXWebArea ++AXGroup -++++AXButton AXTitle='GrowButton' size=(600, 300) +++++AXButton AXTitle='GrowButton' AXSize={h: 300, w: 600} ++++AXStaticText AXValue='Done'
diff --git a/content/test/data/accessibility/html/transition.html b/content/test/data/accessibility/html/transition.html index 68a36b3..6cf08d8 100644 --- a/content/test/data/accessibility/html/transition.html +++ b/content/test/data/accessibility/html/transition.html
@@ -7,8 +7,7 @@ not diff the dump against the expectations until the text "Done" appears in the dump. -@MAC-ALLOW:size=(400, 200) -@MAC-ALLOW:size=(600, 300) +@MAC-ALLOW:AXSize={h: 300, w: 600} @WIN-ALLOW:size=(400, 200) @WIN-ALLOW:size=(600, 300) @WAIT-FOR:Done
diff --git a/device/fido/BUILD.gn b/device/fido/BUILD.gn index 69e2279..d2d153b 100644 --- a/device/fido/BUILD.gn +++ b/device/fido/BUILD.gn
@@ -422,6 +422,7 @@ "hid/fake_hid_impl_for_testing.cc", "hid/fake_hid_impl_for_testing.h", ] + deps += [ "//services/device/public/cpp/hid" ] } if (!is_android) {
diff --git a/device/fido/hid/fake_hid_impl_for_testing.cc b/device/fido/hid/fake_hid_impl_for_testing.cc index 794cd4f..911da13 100644 --- a/device/fido/hid/fake_hid_impl_for_testing.cc +++ b/device/fido/hid/fake_hid_impl_for_testing.cc
@@ -11,6 +11,7 @@ #include "device/fido/hid/fido_hid_discovery.h" #include "mojo/public/cpp/bindings/pending_associated_remote.h" #include "mojo/public/cpp/bindings/pending_receiver.h" +#include "services/device/public/cpp/hid/hid_blocklist.h" #include "services/device/public/mojom/hid.mojom.h" namespace device { @@ -136,6 +137,7 @@ void FakeFidoHidManager::AddFidoHidDevice(std::string guid) { auto c_info = device::mojom::HidCollectionInfo::New(); c_info->usage = device::mojom::HidUsageAndPage::New(1, 0xf1d0); + c_info->input_reports.push_back(device::mojom::HidReportDescription::New()); auto device = device::mojom::HidDeviceInfo::New(); device->guid = std::move(guid); device->product_name = "Test Fido Device"; @@ -144,6 +146,18 @@ device->collections.push_back(std::move(c_info)); device->max_input_report_size = 64; device->max_output_report_size = 64; + device->protected_input_report_ids = + HidBlocklist::Get().GetProtectedReportIds( + HidBlocklist::kReportTypeInput, device->vendor_id, device->product_id, + device->collections); + device->protected_output_report_ids = + HidBlocklist::Get().GetProtectedReportIds( + HidBlocklist::kReportTypeOutput, device->vendor_id, + device->product_id, device->collections); + device->protected_feature_report_ids = + HidBlocklist::Get().GetProtectedReportIds( + HidBlocklist::kReportTypeFeature, device->vendor_id, + device->product_id, device->collections); AddDevice(std::move(device)); } @@ -167,6 +181,7 @@ const std::string& device_guid, mojo::PendingRemote<mojom::HidConnectionClient> connection_client, mojo::PendingRemote<mojom::HidConnectionWatcher> watcher, + bool allow_protected_reports, ConnectCallback callback) { auto device_it = devices_.find(device_guid); auto connection_it = connections_.find(device_guid);
diff --git a/device/fido/hid/fake_hid_impl_for_testing.h b/device/fido/hid/fake_hid_impl_for_testing.h index 894efab..34ba5bd 100644 --- a/device/fido/hid/fake_hid_impl_for_testing.h +++ b/device/fido/hid/fake_hid_impl_for_testing.h
@@ -110,6 +110,7 @@ const std::string& device_guid, mojo::PendingRemote<mojom::HidConnectionClient> connection_client, mojo::PendingRemote<mojom::HidConnectionWatcher> watcher, + bool allow_protected_reports, ConnectCallback callback) override; void AddReceiver( mojo::PendingReceiver<device::mojom::HidManager> receiver) override;
diff --git a/device/fido/hid/fido_hid_device.cc b/device/fido/hid/fido_hid_device.cc index 885a8cd..72f938ac 100644 --- a/device/fido/hid/fido_hid_device.cc +++ b/device/fido/hid/fido_hid_device.cc
@@ -177,7 +177,8 @@ DCHECK(hid_manager_); hid_manager_->Connect(device_info_->guid, /*connection_client=*/mojo::NullRemote(), - /*watcher=*/mojo::NullRemote(), std::move(callback)); + /*watcher=*/mojo::NullRemote(), + /*allow_protected_reports=*/true, std::move(callback)); } void FidoHidDevice::OnConnect(
diff --git a/device/gamepad/nintendo_controller.cc b/device/gamepad/nintendo_controller.cc index 66c0a0c..f18fdfa 100644 --- a/device/gamepad/nintendo_controller.cc +++ b/device/gamepad/nintendo_controller.cc
@@ -1188,7 +1188,8 @@ DCHECK(hid_manager_); hid_manager_->Connect(device_info_->guid, /*connection_client=*/mojo::NullRemote(), - /*watcher=*/mojo::NullRemote(), std::move(callback)); + /*watcher=*/mojo::NullRemote(), + /*allow_protected_reports=*/false, std::move(callback)); } void NintendoController::OnConnect(
diff --git a/docs/mac_lld.md b/docs/mac_lld.md index 923f030b..6268ddc0 100644 --- a/docs/mac_lld.md +++ b/docs/mac_lld.md
@@ -60,18 +60,18 @@ ([bug](https://llvm.org/PR48395), fixed upstream) - LLD-linked `protoc` crashes when it runs as part of the build ([bug](https://llvm.org/PR48491), fixed upstream) +- LLD cannot yet link swiftshader binaries + ([bug](https://llvm.org/PR48332), fixed upstream) - LLD-linked `v8_context_snapshot_generator` crashes when it runs as part of - the build ([bug](https://llvm.org/PR48511), - [in-progress patch](https://reviews.llvm.org/D93369)) -- verify\_framework\_order fails in LLD builds (FIXME: file bug) + the build ([bug](https://llvm.org/PR48511), fixed upstream) +- verify\_framework\_order fails in LLD builds + ([bug](https://llvm.org/PR48536), + [in-progress patch](https://reviews.llvm.org/D93609)) - LLD-built Chromium.app crashes with `malloc: *** error for object 0x7fa46941f20a: pointer being freed was not allocated` at starutp (FIXME: file bug) - LLD does not yet have any ARM support ([in-progress patch](https://reviews.llvm.org/D88629)) -- LLD cannot yet link swiftshader binaries ([bug](https://llvm.org/PR48332), - [in-progress patch](https://reviews.llvm.org/D93267)) -- - need to locally hack up LLD to warn instead of error on this for now - LLD likely produces bad debug info, and LLD-linked binaries likely don't yet work in a debugger - LLD-linked base\_unittests fails some unwind-related tests
diff --git a/extensions/browser/api/device_permissions_prompt.cc b/extensions/browser/api/device_permissions_prompt.cc index 74de730a..463ca83 100644 --- a/extensions/browser/api/device_permissions_prompt.cc +++ b/extensions/browser/api/device_permissions_prompt.cc
@@ -290,7 +290,7 @@ bool HasUnprotectedCollections(const device::mojom::HidDeviceInfo& device) { for (const auto& collection : device.collections) { - if (!device::IsProtected(*collection->usage)) { + if (!device::IsAlwaysProtected(*collection->usage)) { return true; } }
diff --git a/extensions/browser/api/hid/hid_apitest.cc b/extensions/browser/api/hid/hid_apitest.cc index 27fe98dc..3b0f109 100644 --- a/extensions/browser/api/hid/hid_apitest.cc +++ b/extensions/browser/api/hid/hid_apitest.cc
@@ -164,7 +164,10 @@ serial_number, device::mojom::HidBusType::kHIDBusTypeUSB, report_descriptor, std::move(collections), has_report_id, max_input_report_size, max_output_report_size, max_feature_report_size, - ""); + /*device_path=*/"", + /*protected_input_report_ids=*/std::vector<uint8_t>{}, + /*protected_output_report_ids=*/std::vector<uint8_t>{}, + /*protected_feature_report_ids=*/std::vector<uint8_t>{}); fake_hid_manager_->AddDevice(std::move(device)); }
diff --git a/extensions/browser/api/hid/hid_device_manager.cc b/extensions/browser/api/hid/hid_device_manager.cc index 82c8db94..0abb3962 100644 --- a/extensions/browser/api/hid/hid_device_manager.cc +++ b/extensions/browser/api/hid/hid_device_manager.cc
@@ -49,7 +49,7 @@ for (const auto& collection : input.collections) { // Don't expose sensitive data. - if (device::IsProtected(*collection->usage)) { + if (device::IsAlwaysProtected(*collection->usage)) { continue; } @@ -178,6 +178,7 @@ hid_manager_->Connect(device_guid, /*connection_client=*/mojo::NullRemote(), /*watcher=*/mojo::NullRemote(), + /*allow_protected_reports=*/true, mojo::WrapCallbackWithDefaultInvokeIfNotRun( std::move(callback), mojo::NullRemote())); }
diff --git a/fuchsia/runners/cast/cast_component.h b/fuchsia/runners/cast/cast_component.h index 0a127d8..e575e04 100644 --- a/fuchsia/runners/cast/cast_component.h +++ b/fuchsia/runners/cast/cast_component.h
@@ -40,7 +40,7 @@ bool AreComplete() const; // Parameters populated directly from the StartComponent() arguments. - std::unique_ptr<base::fuchsia::StartupContext> startup_context; + std::unique_ptr<base::StartupContext> startup_context; fidl::InterfaceRequest<fuchsia::sys::ComponentController> controller_request;
diff --git a/fuchsia/runners/cast/cast_runner.cc b/fuchsia/runners/cast/cast_runner.cc index cd2db638..b84b3ffc 100644 --- a/fuchsia/runners/cast/cast_runner.cc +++ b/fuchsia/runners/cast/cast_runner.cc
@@ -182,7 +182,7 @@ // it to connect to the MetricsRecorder. static base::WeakPtr<const sys::ServiceDirectory> StartAndReturnIncomingServiceDirectory( - std::unique_ptr<base::fuchsia::StartupContext> startup_context, + std::unique_ptr<base::StartupContext> startup_context, fidl::InterfaceRequest<fuchsia::sys::ComponentController> controller_request, fuchsia::web::FrameHost* const frame_host_impl) { @@ -194,11 +194,10 @@ } private: - FrameHostComponent( - std::unique_ptr<base::fuchsia::StartupContext> startup_context, - fidl::InterfaceRequest<fuchsia::sys::ComponentController> - controller_request, - fuchsia::web::FrameHost* const frame_host_impl) + FrameHostComponent(std::unique_ptr<base::StartupContext> startup_context, + fidl::InterfaceRequest<fuchsia::sys::ComponentController> + controller_request, + fuchsia::web::FrameHost* const frame_host_impl) : startup_context_(std::move(startup_context)), frame_host_binding_(startup_context_->outgoing(), frame_host_impl), weak_incoming_services_(startup_context_->svc()) { @@ -215,7 +214,7 @@ delete this; } - const std::unique_ptr<base::fuchsia::StartupContext> startup_context_; + const std::unique_ptr<base::StartupContext> startup_context_; const base::fuchsia::ScopedServiceBinding<fuchsia::web::FrameHost> frame_host_binding_; fidl::Binding<fuchsia::sys::ComponentController> binding_{this}; @@ -229,22 +228,20 @@ public chromium::cast::DataReset { public: // Creates a DataResetComponent with lifetime managed by |controller_request|. - static void Start( - base::OnceCallback<bool()> delete_persistent_data, - std::unique_ptr<base::fuchsia::StartupContext> startup_context, - fidl::InterfaceRequest<fuchsia::sys::ComponentController> - controller_request) { + static void Start(base::OnceCallback<bool()> delete_persistent_data, + std::unique_ptr<base::StartupContext> startup_context, + fidl::InterfaceRequest<fuchsia::sys::ComponentController> + controller_request) { new DataResetComponent(std::move(delete_persistent_data), std::move(startup_context), std::move(controller_request)); } private: - DataResetComponent( - base::OnceCallback<bool()> delete_persistent_data, - std::unique_ptr<base::fuchsia::StartupContext> startup_context, - fidl::InterfaceRequest<fuchsia::sys::ComponentController> - controller_request) + DataResetComponent(base::OnceCallback<bool()> delete_persistent_data, + std::unique_ptr<base::StartupContext> startup_context, + fidl::InterfaceRequest<fuchsia::sys::ComponentController> + controller_request) : delete_persistent_data_(std::move(delete_persistent_data)), startup_context_(std::move(startup_context)), data_reset_handler_binding_(startup_context_->outgoing(), this) { @@ -273,7 +270,7 @@ } base::OnceCallback<bool()> delete_persistent_data_; - std::unique_ptr<base::fuchsia::StartupContext> startup_context_; + std::unique_ptr<base::StartupContext> startup_context_; const base::fuchsia::ScopedServiceBinding<chromium::cast::DataReset> data_reset_handler_binding_; fidl::Binding<fuchsia::sys::ComponentController> binding_{this}; @@ -336,7 +333,7 @@ } auto startup_context = - std::make_unique<base::fuchsia::StartupContext>(std::move(startup_info)); + std::make_unique<base::StartupContext>(std::move(startup_info)); if (cors_exempt_headers_) { StartComponentInternal(cast_url, std::move(startup_context), @@ -675,7 +672,7 @@ void CastRunner::StartComponentInternal( const GURL& url, - std::unique_ptr<base::fuchsia::StartupContext> startup_context, + std::unique_ptr<base::StartupContext> startup_context, fidl::InterfaceRequest<fuchsia::sys::ComponentController> controller_request) { // TODO(crbug.com/1120914): Remove this once Component Framework v2 can be
diff --git a/fuchsia/runners/cast/cast_runner.h b/fuchsia/runners/cast/cast_runner.h index 5159d95..e92b9dc 100644 --- a/fuchsia/runners/cast/cast_runner.h +++ b/fuchsia/runners/cast/cast_runner.h
@@ -107,7 +107,7 @@ // component URL and ensuring that CORS-exempt headers have been fetched. void StartComponentInternal( const GURL& url, - std::unique_ptr<base::fuchsia::StartupContext> startup_context, + std::unique_ptr<base::StartupContext> startup_context, fidl::InterfaceRequest<fuchsia::sys::ComponentController> controller_request);
diff --git a/fuchsia/runners/cast/pending_cast_component.cc b/fuchsia/runners/cast/pending_cast_component.cc index f15ac9e0..aaf6b80 100644 --- a/fuchsia/runners/cast/pending_cast_component.cc +++ b/fuchsia/runners/cast/pending_cast_component.cc
@@ -11,7 +11,7 @@ PendingCastComponent::PendingCastComponent( Delegate* delegate, - std::unique_ptr<base::fuchsia::StartupContext> startup_context, + std::unique_ptr<base::StartupContext> startup_context, fidl::InterfaceRequest<fuchsia::sys::ComponentController> controller_request, base::StringPiece app_id)
diff --git a/fuchsia/runners/cast/pending_cast_component.h b/fuchsia/runners/cast/pending_cast_component.h index 28c0735..dca6f7af 100644 --- a/fuchsia/runners/cast/pending_cast_component.h +++ b/fuchsia/runners/cast/pending_cast_component.h
@@ -39,12 +39,11 @@ virtual void CancelPendingComponent(PendingCastComponent* component) = 0; }; - PendingCastComponent( - Delegate* delegate, - std::unique_ptr<base::fuchsia::StartupContext> startup_context, - fidl::InterfaceRequest<fuchsia::sys::ComponentController> - controller_request, - base::StringPiece app_id); + PendingCastComponent(Delegate* delegate, + std::unique_ptr<base::StartupContext> startup_context, + fidl::InterfaceRequest<fuchsia::sys::ComponentController> + controller_request, + base::StringPiece app_id); ~PendingCastComponent(); PendingCastComponent(const PendingCastComponent&) = delete;
diff --git a/fuchsia/runners/common/web_component.cc b/fuchsia/runners/common/web_component.cc index 8e5000a..73b7ad6 100644 --- a/fuchsia/runners/common/web_component.cc +++ b/fuchsia/runners/common/web_component.cc
@@ -21,7 +21,7 @@ WebComponent::WebComponent( base::StringPiece debug_name, WebContentRunner* runner, - std::unique_ptr<base::fuchsia::StartupContext> context, + std::unique_ptr<base::StartupContext> context, fidl::InterfaceRequest<fuchsia::sys::ComponentController> controller_request) : debug_name_(debug_name.as_string()),
diff --git a/fuchsia/runners/common/web_component.h b/fuchsia/runners/common/web_component.h index 9216637f..97cd53b 100644 --- a/fuchsia/runners/common/web_component.h +++ b/fuchsia/runners/common/web_component.h
@@ -43,7 +43,7 @@ // lifetime of this component instance. WebComponent(base::StringPiece debug_name, WebContentRunner* runner, - std::unique_ptr<base::fuchsia::StartupContext> context, + std::unique_ptr<base::StartupContext> context, fidl::InterfaceRequest<fuchsia::sys::ComponentController> controller_request); @@ -67,7 +67,7 @@ // Returns the component's startup context (e.g. incoming services, public // service directory, etc). - base::fuchsia::StartupContext* startup_context() const { + base::StartupContext* startup_context() const { return startup_context_.get(); } @@ -108,7 +108,7 @@ WebContentRunner* const runner_ = nullptr; // Component context for this instance, including incoming services. - const std::unique_ptr<base::fuchsia::StartupContext> startup_context_; + const std::unique_ptr<base::StartupContext> startup_context_; fuchsia::web::FramePtr frame_;
diff --git a/fuchsia/runners/common/web_content_runner.cc b/fuchsia/runners/common/web_content_runner.cc index da13c89..9fdd51b 100644 --- a/fuchsia/runners/common/web_content_runner.cc +++ b/fuchsia/runners/common/web_content_runner.cc
@@ -78,7 +78,7 @@ std::unique_ptr<WebComponent> component = std::make_unique<WebComponent>( std::string(), this, - std::make_unique<base::fuchsia::StartupContext>(std::move(startup_info)), + std::make_unique<base::StartupContext>(std::move(startup_info)), std::move(controller_request)); #if BUILDFLAG(WEB_RUNNER_REMOTE_DEBUGGING_PORT) != 0 component->EnableRemoteDebugging();
diff --git a/gpu/ipc/service/pass_through_image_transport_surface.cc b/gpu/ipc/service/pass_through_image_transport_surface.cc index 8db6374..6863844 100644 --- a/gpu/ipc/service/pass_through_image_transport_surface.cc +++ b/gpu/ipc/service/pass_through_image_transport_surface.cc
@@ -8,14 +8,13 @@ #include "base/bind.h" #include "base/callback_helpers.h" -#include "base/command_line.h" #include "base/metrics/histogram_macros.h" #include "base/metrics/histogram_macros_local.h" #include "build/build_config.h" #include "gpu/command_buffer/common/swap_buffers_complete_params.h" #include "ui/gfx/vsync_provider.h" #include "ui/gl/gl_context.h" -#include "ui/gl/gl_switches.h" +#include "ui/gl/gl_features.h" namespace gpu { @@ -28,10 +27,6 @@ int g_num_swaps_in_current_swap_generation_ = 0; int g_last_multi_window_swap_generation_ = 0; -bool HasSwitch(const char switch_constant[]) { - return base::CommandLine::ForCurrentProcess()->HasSwitch(switch_constant); -} - } // anonymous namespace PassThroughImageTransportSurface::PassThroughImageTransportSurface( @@ -39,13 +34,12 @@ gl::GLSurface* surface, bool override_vsync_for_multi_window_swap) : GLSurfaceAdapter(surface), - is_gpu_vsync_disabled_(HasSwitch(switches::kDisableGpuVsync)), + is_gpu_vsync_disabled_(!features::UseGpuVsync()), is_multi_window_swap_vsync_override_enabled_( override_vsync_for_multi_window_swap), delegate_(delegate) {} -PassThroughImageTransportSurface::~PassThroughImageTransportSurface() { -} +PassThroughImageTransportSurface::~PassThroughImageTransportSurface() = default; bool PassThroughImageTransportSurface::Initialize(gl::GLSurfaceFormat format) { // The surface is assumed to have already been initialized.
diff --git a/headless/app/headless_shell.cc b/headless/app/headless_shell.cc index 17e23c3..af0460d 100644 --- a/headless/app/headless_shell.cc +++ b/headless/app/headless_shell.cc
@@ -51,6 +51,7 @@ #include "ui/gfx/geometry/size.h" #if defined(OS_WIN) +#include <base/win/windows_types.h> #include "components/crash/core/app/crash_switches.h" #include "components/crash/core/app/run_as_crashpad_handler_win.h" #include "sandbox/win/src/sandbox_types.h" @@ -836,8 +837,15 @@ builder.SetUserAgent(ua); } - exit(RunContentMain(builder.Build(), - base::OnceCallback<void(HeadlessBrowser*)>())); + int rc = RunContentMain(builder.Build(), + base::OnceCallback<void(HeadlessBrowser*)>()); +#if defined(OS_WIN) + // Use TerminateProcess instead of exit to avoid shutdown crashes and + // slowdowns on shutdown. + ::TerminateProcess(::GetCurrentProcess(), rc); +#else // defined(OS_WIN) + exit(rc); +#endif // defined(OS_WIN) } int HeadlessBrowserMain(
diff --git a/ios/chrome/browser/application_context_impl.h b/ios/chrome/browser/application_context_impl.h index 15217f9..db51baea 100644 --- a/ios/chrome/browser/application_context_impl.h +++ b/ios/chrome/browser/application_context_impl.h
@@ -95,10 +95,6 @@ // Logger which observers and logs application wide events to // |breadcrumb_manager_|. Will be null if breadcrumbs feature is not enabled. std::unique_ptr<ApplicationBreadcrumbsLogger> application_breadcrumbs_logger_; - // Persistent storage manager to write breadcrumbs to disk for storage - // between sessions. Will be null if breadcrumbs feature is not enabled. - std::unique_ptr<BreadcrumbPersistentStorageManager> - breadcrumb_persistent_storage_manager_; // Must be destroyed after |local_state_|. BrowserStatePolicyConnector isn't a // keyed service because the pref service, which isn't a keyed service, has a
diff --git a/ios/chrome/browser/application_context_impl.mm b/ios/chrome/browser/application_context_impl.mm index eb58be1..eef21df 100644 --- a/ios/chrome/browser/application_context_impl.mm +++ b/ios/chrome/browser/application_context_impl.mm
@@ -165,11 +165,12 @@ base::FilePath storage_dir; bool result = base::PathService::Get(ios::DIR_USER_DATA, &storage_dir); DCHECK(result); - breadcrumb_persistent_storage_manager_ = + + auto breadcrumb_persistent_storage_manager = std::make_unique<BreadcrumbPersistentStorageManager>(storage_dir); application_breadcrumbs_logger_->SetPersistentStorageManager( - breadcrumb_persistent_storage_manager_.get()); + std::move(breadcrumb_persistent_storage_manager)); } } @@ -207,6 +208,12 @@ sessions::SessionIdGenerator::GetInstance()->Shutdown(); } + // The ApplicationBreadcrumbsLogger tries to log event via a task when it + // is destroyed, so it needs to be notified of the app tear down now when + // the task tracker is still valid (will be destroyed after StartTearDown + // returns). + application_breadcrumbs_logger_.reset(); + ios_chrome_io_thread_->NetworkTearDown(); } @@ -466,7 +473,9 @@ BreadcrumbPersistentStorageManager* ApplicationContextImpl::GetBreadcrumbPersistentStorageManager() { DCHECK(thread_checker_.CalledOnValidThread()); - return breadcrumb_persistent_storage_manager_.get(); + return application_breadcrumbs_logger_ + ? application_breadcrumbs_logger_->GetPersistentStorageManager() + : nullptr; } void ApplicationContextImpl::SetApplicationLocale(const std::string& locale) {
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/application_breadcrumbs_logger.h b/ios/chrome/browser/crash_report/breadcrumbs/application_breadcrumbs_logger.h index c3d5004..08f7769 100644 --- a/ios/chrome/browser/crash_report/breadcrumbs/application_breadcrumbs_logger.h +++ b/ios/chrome/browser/crash_report/breadcrumbs/application_breadcrumbs_logger.h
@@ -32,7 +32,12 @@ // Sets a BreadcrumbPersistentStorageManager to persist application breadcrumb // events logged by this ApplicationBreadcrumbsLogger instance. void SetPersistentStorageManager( - BreadcrumbPersistentStorageManager* persistent_storage_manager); + std::unique_ptr<BreadcrumbPersistentStorageManager> + persistent_storage_manager); + + // Returns a pointer to the BreadcrumbPersistentStorageManager owned by this + // instance. May be null. + BreadcrumbPersistentStorageManager* GetPersistentStorageManager() const; private: ApplicationBreadcrumbsLogger(const ApplicationBreadcrumbsLogger&) = delete; @@ -58,9 +63,10 @@ // Observes device orientation. id<NSObject> orientation_observer_; - // A weak reference to the persistent breadcrumb manager listening for events + // A strong pointer to the persistent breadcrumb manager listening for events // from |breadcrumb_manager_| to store to disk. - BreadcrumbPersistentStorageManager* persistent_storage_manager_ = nullptr; + std::unique_ptr<BreadcrumbPersistentStorageManager> + persistent_storage_manager_; // Used to avoid logging the same orientation twice. base::Optional<UIDeviceOrientation> last_orientation_;
diff --git a/ios/chrome/browser/crash_report/breadcrumbs/application_breadcrumbs_logger.mm b/ios/chrome/browser/crash_report/breadcrumbs/application_breadcrumbs_logger.mm index ef8ae51..4698402 100644 --- a/ios/chrome/browser/crash_report/breadcrumbs/application_breadcrumbs_logger.mm +++ b/ios/chrome/browser/crash_report/breadcrumbs/application_breadcrumbs_logger.mm
@@ -81,14 +81,20 @@ } void ApplicationBreadcrumbsLogger::SetPersistentStorageManager( - BreadcrumbPersistentStorageManager* persistent_storage_manager) { + std::unique_ptr<BreadcrumbPersistentStorageManager> + persistent_storage_manager) { if (persistent_storage_manager_) { persistent_storage_manager_->StopMonitoringBreadcrumbManager( breadcrumb_manager_); } - persistent_storage_manager_ = persistent_storage_manager; - persistent_storage_manager->MonitorBreadcrumbManager(breadcrumb_manager_); + persistent_storage_manager_ = std::move(persistent_storage_manager); + persistent_storage_manager_->MonitorBreadcrumbManager(breadcrumb_manager_); +} + +BreadcrumbPersistentStorageManager* +ApplicationBreadcrumbsLogger::GetPersistentStorageManager() const { + return persistent_storage_manager_.get(); } void ApplicationBreadcrumbsLogger::OnUserAction(const std::string& action,
diff --git a/ios/chrome/browser/prefs/browser_prefs.mm b/ios/chrome/browser/prefs/browser_prefs.mm index 840deeac9..801b133 100644 --- a/ios/chrome/browser/prefs/browser_prefs.mm +++ b/ios/chrome/browser/prefs/browser_prefs.mm
@@ -105,6 +105,9 @@ "profile.was_pwm_onboarding_feature_checked_before"; } +// Deprecated 12/2020 +const char kDomainsWithCookiePref[] = "signin.domains_with_cookie"; + void RegisterLocalStatePrefs(PrefRegistrySimple* registry) { BrowserStateInfoCache::RegisterPrefs(registry); flags_ui::PrefServiceFlagsStorage::RegisterPrefs(registry); @@ -244,6 +247,7 @@ registry->RegisterIntegerPref(kPasswordManagerOnboardingState, 0); registry->RegisterBooleanPref(kWasOnboardingFeatureCheckedBefore, false); + registry->RegisterDictionaryPref(kDomainsWithCookiePref); } // This method should be periodically pruned of year+ old migrations. @@ -293,4 +297,7 @@ prefs->ClearPref(kPasswordManagerOnboardingState); prefs->ClearPref(kWasOnboardingFeatureCheckedBefore); prerender_prefs::MigrateNetworkPredictionPrefs(prefs); + + // Added 12/2020. + prefs->ClearPref(kDomainsWithCookiePref); }
diff --git a/ios/chrome/browser/signin/account_consistency_service_factory.h b/ios/chrome/browser/signin/account_consistency_service_factory.h index 248a99d..fe6513f 100644 --- a/ios/chrome/browser/signin/account_consistency_service_factory.h +++ b/ios/chrome/browser/signin/account_consistency_service_factory.h
@@ -37,8 +37,6 @@ ~AccountConsistencyServiceFactory() override; // BrowserStateKeyedServiceFactory: - void RegisterBrowserStatePrefs( - user_prefs::PrefRegistrySyncable* registry) override; std::unique_ptr<KeyedService> BuildServiceInstanceFor( web::BrowserState* context) const override;
diff --git a/ios/chrome/browser/signin/account_consistency_service_factory.mm b/ios/chrome/browser/signin/account_consistency_service_factory.mm index 5eb675ac..be30317 100644 --- a/ios/chrome/browser/signin/account_consistency_service_factory.mm +++ b/ios/chrome/browser/signin/account_consistency_service_factory.mm
@@ -7,7 +7,6 @@ #include "base/no_destructor.h" #include "components/content_settings/core/browser/cookie_settings.h" #include "components/keyed_service/ios/browser_state_dependency_manager.h" -#include "components/pref_registry/pref_registry_syncable.h" #include "components/signin/ios/browser/account_consistency_service.h" #include "ios/chrome/browser/browser_state/chrome_browser_state.h" #include "ios/chrome/browser/content_settings/cookie_settings_factory.h" @@ -45,18 +44,13 @@ return instance.get(); } -void AccountConsistencyServiceFactory::RegisterBrowserStatePrefs( - user_prefs::PrefRegistrySyncable* registry) { - AccountConsistencyService::RegisterPrefs(registry); -} - std::unique_ptr<KeyedService> AccountConsistencyServiceFactory::BuildServiceInstanceFor( web::BrowserState* context) const { ChromeBrowserState* chrome_browser_state = ChromeBrowserState::FromBrowserState(context); return std::make_unique<AccountConsistencyService>( - chrome_browser_state, chrome_browser_state->GetPrefs(), + chrome_browser_state, ios::AccountReconcilorFactory::GetForBrowserState(chrome_browser_state), ios::CookieSettingsFactory::GetForBrowserState(chrome_browser_state), IdentityManagerFactory::GetForBrowserState(chrome_browser_state));
diff --git a/ios/chrome/browser/sync/device_info_sync_service_factory.mm b/ios/chrome/browser/sync/device_info_sync_service_factory.mm index b310d58f..db957af 100644 --- a/ios/chrome/browser/sync/device_info_sync_service_factory.mm +++ b/ios/chrome/browser/sync/device_info_sync_service_factory.mm
@@ -52,10 +52,12 @@ } // syncer::DeviceInfoSyncClient: - std::string GetFCMRegistrationToken() const override { return std::string(); } + base::Optional<std::string> GetFCMRegistrationToken() const override { + return std::string(); + } // syncer::DeviceInfoSyncClient: - syncer::ModelTypeSet GetInterestedDataTypes() const override { + base::Optional<syncer::ModelTypeSet> GetInterestedDataTypes() const override { return syncer::ModelTypeSet(); }
diff --git a/ios/chrome/browser/ui/main/scene_controller.mm b/ios/chrome/browser/ui/main/scene_controller.mm index 0b811564..8f9184bf 100644 --- a/ios/chrome/browser/ui/main/scene_controller.mm +++ b/ios/chrome/browser/ui/main/scene_controller.mm
@@ -1582,6 +1582,10 @@ [self finishActivatingBrowserDismissingTabSwitcher:YES]; } +- (TabGridPage)activePageForTabGrid:(TabGridCoordinator*)tabGrid { + return self.activePage; +} + // Begins the process of activating the given current model, switching which BVC // is suspended if necessary. If |dismissTabSwitcher| is set, the tab switcher // will also be dismissed. Note that this means that a browser can be activated
diff --git a/ios/chrome/browser/ui/settings/settings_table_view_controller.mm b/ios/chrome/browser/ui/settings/settings_table_view_controller.mm index 296d228..84c4b0f 100644 --- a/ios/chrome/browser/ui/settings/settings_table_view_controller.mm +++ b/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
@@ -1438,7 +1438,8 @@ [self.tableViewModel itemAtIndexPath:googleServicesCellIndexPath]); DCHECK(googleServicesItem); [self updateSyncAndGoogleServicesItem:googleServicesItem]; - [self reconfigureCellsForItems:@[ googleServicesItem ]]; + [self reloadCellsForItems:@[ googleServicesItem ] + withRowAnimation:UITableViewRowAnimationNone]; return; }
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator.mm index 161da22..32a1f03d 100644 --- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator.mm +++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator.mm
@@ -60,7 +60,8 @@ RecentTabsContextMenuDelegate, RecentTabsPresentationDelegate, TabGridMediatorDelegate, - TabPresentationDelegate> { + TabPresentationDelegate, + TabGridViewControllerDelegate> { // Use an explicit ivar instead of synthesizing as the setter isn't using the // ivar. Browser* _incognitoBrowser; @@ -252,7 +253,6 @@ if (shouldCloseTabGrid) { [self.thumbStripCoordinator.panHandler setState:ViewRevealState::Hidden animated:YES]; - [self.delegate tabGridDismissTransitionDidEnd:self]; // Record when the tab switcher is dismissed. base::RecordAction(base::UserMetricsAction("MobileTabGridExited")); @@ -334,6 +334,7 @@ baseViewController.reauthHandler = HandlerForProtocol(self.dispatcher, IncognitoReauthCommands); baseViewController.tabPresentationDelegate = self; + baseViewController.delegate = self; _baseViewController = baseViewController; self.regularTabsMediator = [[TabGridMediator alloc] @@ -559,6 +560,18 @@ [self.actionSheetCoordinator start]; } +#pragma mark - TabGridViewControllerDelegate + +- (TabGridPage)activePageForTabGridViewController: + (TabGridViewController*)tabGridViewController { + return [self.delegate activePageForTabGrid:self]; +} + +- (void)tabGridViewControllerDidDismiss: + (TabGridViewController*)tabGridViewController { + [self.delegate tabGridDismissTransitionDidEnd:self]; +} + #pragma mark - RecentTabsPresentationDelegate - (void)showHistoryFromRecentTabs {
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator_delegate.h b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator_delegate.h index 7a198bd..f24c1c0 100644 --- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator_delegate.h +++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator_delegate.h
@@ -7,6 +7,8 @@ #import <Foundation/Foundation.h> +#import "ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_paging.h" + class Browser; @class TabGridCoordinator; @@ -26,6 +28,9 @@ // Informs the delegate that the tab switcher is done and should be dismissed. - (void)tabGridDismissTransitionDidEnd:(TabGridCoordinator*)tabGrid; +// Asks the delegate for the page that should currently be active. +- (TabGridPage)activePageForTabGrid:(TabGridCoordinator*)tabGrid; + @end #endif // IOS_CHROME_BROWSER_UI_TAB_SWITCHER_TAB_GRID_TAB_GRID_COORDINATOR_DELEGATE_H_
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator_unittest.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator_unittest.mm index 921053e5..26cdc76 100644 --- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator_unittest.mm +++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator_unittest.mm
@@ -55,6 +55,10 @@ - (void)tabGridDismissTransitionDidEnd:(TabGridCoordinator*)tabGrid { self.didEndCalled = YES; } + +- (TabGridPage)activePageForTabGrid:(TabGridCoordinator*)tabGrid { + return TabGridPageRegularTabs; +} @end namespace {
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_view_controller.h b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_view_controller.h index b8b6bddb..6e26c32 100644 --- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_view_controller.h +++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_view_controller.h
@@ -21,6 +21,7 @@ @protocol GridImageDataSource; @protocol RecentTabsConsumer; @class RecentTabsTableViewController; +@class TabGridViewController; // Delegate protocol for an object that can handle presenting ("opening") tabs // from the tab grid. @@ -36,6 +37,19 @@ closeTabGrid:(BOOL)closeTabGrid; @end +@protocol TabGridViewControllerDelegate <NSObject> + +// Asks the delegate for the page that should currently be active. +- (TabGridPage)activePageForTabGridViewController: + (TabGridViewController*)tabGridViewController; + +// Notifies the delegate that the tab grid was dismissed via the +// ViewRevealingAnimatee. +- (void)tabGridViewControllerDidDismiss: + (TabGridViewController*)tabGridViewController; + +@end + // View controller representing a tab switcher. The tab switcher has an // incognito tab grid, regular tab grid, and remote tabs. @interface TabGridViewController @@ -50,6 +64,8 @@ // Delegate for this view controller to handle presenting tab UI. @property(nonatomic, weak) id<TabPresentationDelegate> tabPresentationDelegate; +@property(nonatomic, weak) id<TabGridViewControllerDelegate> delegate; + // Consumers send updates from the model layer to the UI layer. @property(nonatomic, readonly) id<GridConsumer> regularTabsConsumer; @property(nonatomic, readonly) id<GridConsumer, IncognitoReauthConsumer>
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_view_controller.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_view_controller.mm index 8cd42b9..28478ab 100644 --- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_view_controller.mm +++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_view_controller.mm
@@ -501,10 +501,20 @@ #pragma mark - ViewRevealingAnimatee - (void)willAnimateViewReveal:(ViewRevealState)currentViewRevealState { - DCHECK_NE(TabGridPageRemoteTabs, self.currentPage); self.scrollView.scrollEnabled = NO; switch (currentViewRevealState) { case ViewRevealState::Hidden: { + // If the tab grid is just showing up, make sure that the active page is + // current. This can happen when the user closes the tab grid using the + // done button on RecentTabs. The current page would stay RecentTabs, but + // the active page comes from the currently displayed BVC. + if (self.delegate) { + self.activePage = + [self.delegate activePageForTabGridViewController:self]; + } + if (self.currentPage != self.activePage) { + [self scrollToPage:self.activePage animated:NO]; + } self.topToolbar.transform = CGAffineTransformMakeTranslation( 0, [self hiddenTopToolbarYTranslation]); GridViewController* regularViewController = @@ -580,9 +590,17 @@ } - (void)didAnimateViewReveal:(ViewRevealState)viewRevealState { - if (viewRevealState == ViewRevealState::Revealed) { - self.scrollView.scrollEnabled = YES; - [self setInsetForRemoteTabs]; + switch (viewRevealState) { + case ViewRevealState::Hidden: + [self.delegate tabGridViewControllerDidDismiss:self]; + break; + case ViewRevealState::Peeked: + // No-op. + break; + case ViewRevealState::Revealed: + self.scrollView.scrollEnabled = YES; + [self setInsetForRemoteTabs]; + break; } }
diff --git a/ios/chrome/credential_provider_extension/BUILD.gn b/ios/chrome/credential_provider_extension/BUILD.gn index dece710..20e5944 100644 --- a/ios/chrome/credential_provider_extension/BUILD.gn +++ b/ios/chrome/credential_provider_extension/BUILD.gn
@@ -64,6 +64,7 @@ ":password_util", ":reauthentication_handler", ":system_strings", + "//base", "//ios/chrome/common/app_group", "//ios/chrome/common/app_group:client", "//ios/chrome/common/credential_provider",
diff --git a/ios/chrome/credential_provider_extension/credential_provider_view_controller.mm b/ios/chrome/credential_provider_extension/credential_provider_view_controller.mm index 4fd6f3e..487da1b 100644 --- a/ios/chrome/credential_provider_extension/credential_provider_view_controller.mm +++ b/ios/chrome/credential_provider_extension/credential_provider_view_controller.mm
@@ -6,11 +6,13 @@ #import <Foundation/Foundation.h> +#include "base/check.h" #include "ios/chrome/common/app_group/app_group_constants.h" #include "ios/chrome/common/app_group/app_group_metrics.h" #import "ios/chrome/common/credential_provider/archivable_credential_store.h" #import "ios/chrome/common/credential_provider/constants.h" #import "ios/chrome/common/credential_provider/credential.h" +#import "ios/chrome/common/ui/colors/semantic_color_names.h" #import "ios/chrome/common/ui/confirmation_alert/confirmation_alert_action_handler.h" #import "ios/chrome/common/ui/reauthentication/reauthentication_module.h" #import "ios/chrome/credential_provider_extension/account_verification_provider.h" @@ -51,6 +53,9 @@ // Interface for verified that accounts are still valid. @property(nonatomic, strong) AccountVerificationProvider* accountVerificator; +// Loading indicator used for user validation, which APIs can take a long time. +@property(nonatomic, strong) UIActivityIndicatorView* activityIndicatorView; + @end @implementation CredentialProviderViewController @@ -191,11 +196,36 @@ [self exitWithErrorCode:ASExtensionErrorCodeCredentialIdentityNotFound]; } +// Shows a loading indicator, +- (void)showLoadingIndicator { + DCHECK(!self.activityIndicatorView); + self.activityIndicatorView = [[UIActivityIndicatorView alloc] init]; + UIActivityIndicatorView* activityView = self.activityIndicatorView; + activityView.translatesAutoresizingMaskIntoConstraints = NO; + [self.view addSubview:activityView]; + [NSLayoutConstraint activateConstraints:@[ + [activityView.centerXAnchor + constraintEqualToAnchor:self.view.centerXAnchor], + [activityView.centerYAnchor + constraintEqualToAnchor:self.view.centerYAnchor], + ]]; + [activityView startAnimating]; + activityView.color = [UIColor colorNamed:kBlueColor]; +} + +// Hides the loading indicator. +- (void)hideLoadingIndicator { + [self.activityIndicatorView removeFromSuperview]; + self.activityIndicatorView = nil; +} + // Verifies that the user is still signed in. // Return NO in the completion when the user is no longer valid. YES otherwise. - (void)validateUserWithCompletion:(void (^)(BOOL))completion { + [self showLoadingIndicator]; auto handler = ^(BOOL isValid) { dispatch_async(dispatch_get_main_queue(), ^{ + [self hideLoadingIndicator]; if (completion) { completion(isValid); }
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1 index 1aa246cf..d9271f4 100644 --- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@ -cfb6628631b0eade9a89c3a9e026153bcb0ff25b \ No newline at end of file +b5dedb7cb4c7611d82b69f2e7843b409f61a9279 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1 index e60d062..6479cfe 100644 --- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@ -f9b2d3ca7121aa9d58a0638b109902715c95f902 \ No newline at end of file +3c61e68c3cfe577c7365ee95c69f0ae0b2dfe434 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1 index 77bd28f..21d7861 100644 --- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1 +++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@ -b307b9973183217fa47e4d6705850225c2f43b14 \ No newline at end of file +9ecccb872f8243b3415ebc5935a7b4c826400eda \ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1 index eed9afe..29eb6f1 100644 --- a/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1 +++ b/ios/google_internal/frameworks/remoting_dogfood_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@ -17af97771d1bcb3e9ccc18a0fbc691aed670c264 \ No newline at end of file +e1a9bc6da2d1da66607e246664c0f15c6e9474b9 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1 index 09d7252..bf56d02 100644 --- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1 +++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@ -133c69e75363e84ce3c453776561f8a552355eee \ No newline at end of file +38f8bbfa89bb81a804a352261def2757c7991205 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1 index 3465aa5..37275356 100644 --- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1 +++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@ -0bc5985329576385fb0f8ba74334dea1f8a857f1 \ No newline at end of file +df76f9fa52c23dafee812abd39a0a2f88b445e5a \ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1 index a925d08..2373179 100644 --- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1 +++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.arm64.zip.sha1
@@ -1 +1 @@ -f6564d72809e153e72815297ba2b55b54db3de19 \ No newline at end of file +f0dc53a17470c060787852161291c153161d87f0 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1 index 5b1b32ef..04d4fc8 100644 --- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1 +++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.x64.zip.sha1
@@ -1 +1 @@ -f3eeaa520ee1c07670e2a1c5ab83f938ed15d086 \ No newline at end of file +6edc0e1e73546ecccc87603382627f8a29b3198b \ No newline at end of file
diff --git a/ios/web_view/internal/sync/web_view_device_info_sync_service_factory.mm b/ios/web_view/internal/sync/web_view_device_info_sync_service_factory.mm index 5cb3324..d095d046 100644 --- a/ios/web_view/internal/sync/web_view_device_info_sync_service_factory.mm +++ b/ios/web_view/internal/sync/web_view_device_info_sync_service_factory.mm
@@ -46,10 +46,12 @@ } // syncer::DeviceInfoSyncClient: - std::string GetFCMRegistrationToken() const override { return std::string(); } + base::Optional<std::string> GetFCMRegistrationToken() const override { + return std::string(); + } // syncer::DeviceInfoSyncClient: - syncer::ModelTypeSet GetInterestedDataTypes() const override { + base::Optional<syncer::ModelTypeSet> GetInterestedDataTypes() const override { return syncer::ModelTypeSet(); }
diff --git a/media/mojo/mojom/media_player.mojom b/media/mojo/mojom/media_player.mojom index 1bd8dbc8..5e62b21f 100644 --- a/media/mojo/mojom/media_player.mojom +++ b/media/mojo/mojom/media_player.mojom
@@ -12,9 +12,7 @@ interface MediaPlayer { // Sends |observer| to the renderer process so that it can be established a // communication channel with the implementor of MediaPlayerObserver. - // TODO(crbug.com/1039252): Make this an AddMediaPlayerObserver method as soon - // as there is a HeapMojoRemoteSet class available for its use in Blink. - SetMediaPlayerObserver(pending_remote<MediaPlayerObserver> observer); + AddMediaPlayerObserver(pending_remote<MediaPlayerObserver> observer); // Requests the media player to start or resume media playback. RequestPlay();
diff --git a/mojo/public/cpp/bindings/remote_set.h b/mojo/public/cpp/bindings/remote_set.h index fd6f9bbd..2e2c325 100644 --- a/mojo/public/cpp/bindings/remote_set.h +++ b/mojo/public/cpp/bindings/remote_set.h
@@ -12,6 +12,8 @@ #include "base/bind.h" #include "base/callback.h" #include "base/macros.h" +#include "base/memory/scoped_refptr.h" +#include "base/sequenced_task_runner.h" #include "base/stl_util.h" #include "base/util/type_safety/id_type.h" #include "mojo/public/cpp/bindings/associated_remote.h" @@ -99,9 +101,14 @@ return id; } - // Same as above but for the equivalent pending remote type, for convenience. - RemoteSetElementId Add(PendingRemoteType<Interface> remote) { - return Add(RemoteType<Interface>(std::move(remote))); + // Same as above but for the equivalent pending remote type. If |task_runner| + // is null, the value of |base::SequencedTaskRunnerHandle::Get()| at the time + // of the |Add()| call will be used to run scheduled tasks for the remote. + RemoteSetElementId Add( + PendingRemoteType<Interface> remote, + scoped_refptr<base::SequencedTaskRunner> task_runner = nullptr) { + return Add( + RemoteType<Interface>(std::move(remote), std::move(task_runner))); } // Removes a remote from the set given |id|, if present.
diff --git a/pdf/out_of_process_instance.cc b/pdf/out_of_process_instance.cc index 59c5a81..a30b1a6 100644 --- a/pdf/out_of_process_instance.cc +++ b/pdf/out_of_process_instance.cc
@@ -18,6 +18,7 @@ #include "base/bind.h" #include "base/callback.h" #include "base/feature_list.h" +#include "base/location.h" #include "base/logging.h" #include "base/memory/weak_ptr.h" #include "base/metrics/histogram_functions.h" @@ -27,6 +28,7 @@ #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" +#include "base/time/time.h" #include "base/values.h" #include "build/chromeos_buildflags.h" #include "net/base/escape.h" @@ -243,7 +245,8 @@ constexpr char kJSSetReadOnlyType[] = "setReadOnly"; constexpr char kJSEnableReadOnly[] = "enableReadOnly"; -constexpr int kFindResultCooldownMs = 100; +constexpr base::TimeDelta kFindResultCooldown = + base::TimeDelta::FromMilliseconds(100); // Do not save files with over 100 MB. This cap should be kept in sync with and // is also enforced in chrome/browser/resources/pdf/pdf_viewer.js. @@ -256,7 +259,8 @@ // A delay to wait between each accessibility page to keep the system // responsive. -constexpr int kAccessibilityPageDelayMs = 100; +constexpr base::TimeDelta kAccessibilityPageDelay = + base::TimeDelta::FromMilliseconds(100); constexpr double kMinZoom = 0.01; @@ -528,10 +532,7 @@ } // namespace OutOfProcessInstance::OutOfProcessInstance(PP_Instance instance) - : pp::Instance(instance), - pp::Find_Private(this), - pp::Printing_Dev(this), - paint_manager_(this) { + : pp::Instance(instance), pp::Find_Private(this), pp::Printing_Dev(this) { pp::Module::Get()->AddPluginInterface(kPPPPdfInterface, &ppp_private); AddPerInstanceObject(kPPPPdfInterface, this); @@ -767,7 +768,7 @@ plugin_size_ = view_device_size; plugin_offset_ = view_rect.point(); - paint_manager_.SetSize(SizeFromPPSize(view_device_size), device_scale_); + paint_manager().SetSize(SizeFromPPSize(view_device_size), device_scale_); const gfx::Size old_image_data_size = SizeFromPPSize(image_data_.size()); gfx::Size new_image_data_size = PaintManager::GetNewContextSize( @@ -867,11 +868,10 @@ SendAccessibilityViewportInfo(); // Schedule loading the first page. - pp::Module::Get()->core()->CallOnMainThread( - kAccessibilityPageDelayMs, - PPCompletionCallbackFromResultCallback( - base::BindOnce(&OutOfProcessInstance::SendNextAccessibilityPage, - weak_factory_.GetWeakPtr())), + ScheduleTaskOnMainThread( + kAccessibilityPageDelay, + base::BindOnce(&OutOfProcessInstance::SendNextAccessibilityPage, + weak_factory_.GetWeakPtr()), 0); } @@ -901,11 +901,10 @@ pp_text_runs, pp_chars, page_objects); // Schedule loading the next page. - pp::Module::Get()->core()->CallOnMainThread( - kAccessibilityPageDelayMs, - PPCompletionCallbackFromResultCallback( - base::BindOnce(&OutOfProcessInstance::SendNextAccessibilityPage, - weak_factory_.GetWeakPtr())), + ScheduleTaskOnMainThread( + kAccessibilityPageDelay, + base::BindOnce(&OutOfProcessInstance::SendNextAccessibilityPage, + weak_factory_.GetWeakPtr()), page_index + 1); } @@ -1210,12 +1209,12 @@ gfx::Rect offset_rect(rect); offset_rect.Offset(VectorFromPPPoint(available_area_.point())); - paint_manager_.InvalidateRect(offset_rect); + paint_manager().InvalidateRect(offset_rect); } void OutOfProcessInstance::DidScroll(const gfx::Vector2d& offset) { if (!image_data_.is_null()) - paint_manager_.ScrollRect(RectFromPPRect(available_area_), offset); + paint_manager().ScrollRect(RectFromPPRect(available_area_), offset); } void OutOfProcessInstance::ScrollToX(int x_in_screen_coords) { @@ -1327,11 +1326,10 @@ NumberOfFindResultsChanged(total, final_result); SetTickmarks(tickmarks_); recently_sent_find_update_ = true; - pp::Module::Get()->core()->CallOnMainThread( - kFindResultCooldownMs, - PPCompletionCallbackFromResultCallback( - base::BindOnce(&OutOfProcessInstance::ResetRecentlySentFindUpdate, - weak_factory_.GetWeakPtr())), + ScheduleTaskOnMainThread( + kFindResultCooldown, + base::BindOnce(&OutOfProcessInstance::ResetRecentlySentFindUpdate, + weak_factory_.GetWeakPtr()), 0); } @@ -1468,9 +1466,10 @@ return; } - pp::Module::Get()->core()->CallOnMainThread( - 0, PPCompletionCallbackFromResultCallback(base::BindOnce( - &OutOfProcessInstance::OnPrint, weak_factory_.GetWeakPtr()))); + ScheduleTaskOnMainThread(base::TimeDelta(), + base::BindOnce(&OutOfProcessInstance::OnPrint, + weak_factory_.GetWeakPtr()), + 0); } void OutOfProcessInstance::SubmitForm(const std::string& url, @@ -1763,7 +1762,7 @@ engine()->SetGrayscale(dict.Get(pp::Var(kJSPrintPreviewGrayscale)).AsBool()); engine()->New(url_.c_str(), /*headers=*/nullptr); - paint_manager_.InvalidateRect(gfx::Rect(SizeFromPPSize(plugin_size_))); + paint_manager().InvalidateRect(gfx::Rect(SizeFromPPSize(plugin_size_))); } void OutOfProcessInstance::HandleSaveAttachmentMessage( @@ -1961,9 +1960,9 @@ (scroll_offset.y() - scroll_offset_at_last_raster_.y() * zoom_ratio)); } - paint_manager_.SetTransform(zoom_ratio, PointFromPPPoint(pinch_center), - pinch_vector + paint_offset + scroll_delta, - true); + paint_manager().SetTransform(zoom_ratio, PointFromPPPoint(pinch_center), + pinch_vector + paint_offset + scroll_delta, + true); needs_reraster_ = false; return; } @@ -1973,7 +1972,7 @@ // that appear after zooming out. // On pinch end the scale is again 1.f and we request a reraster // in the new position. - paint_manager_.ClearTransform(); + paint_manager().ClearTransform(); last_bitmap_smaller_ = false; needs_reraster_ = true; @@ -2021,7 +2020,7 @@ } document_load_state_ = LOAD_STATE_FAILED; - paint_manager_.InvalidateRect(gfx::Rect(SizeFromPPSize(plugin_size_))); + paint_manager().InvalidateRect(gfx::Rect(SizeFromPPSize(plugin_size_))); // Send a progress value of -1 to indicate a failure. SendLoadingProgress(-1); @@ -2130,7 +2129,7 @@ if (document_size_.IsEmpty()) return; - paint_manager_.InvalidateRect(gfx::Rect(SizeFromPPSize(plugin_size_))); + paint_manager().InvalidateRect(gfx::Rect(SizeFromPPSize(plugin_size_))); if (accessibility_state_ == ACCESSIBILITY_STATE_LOADED) SendAccessibilityViewportInfo(); @@ -2287,13 +2286,26 @@ engine()->PostPaint(); if (!deferred_invalidates_.empty()) { - pp::Module::Get()->core()->CallOnMainThread( - 0, PPCompletionCallbackFromResultCallback( - base::BindOnce(&OutOfProcessInstance::InvalidateAfterPaintDone, - weak_factory_.GetWeakPtr()))); + ScheduleTaskOnMainThread( + base::TimeDelta(), + base::BindOnce(&OutOfProcessInstance::InvalidateAfterPaintDone, + weak_factory_.GetWeakPtr()), + 0); } } +void OutOfProcessInstance::ScheduleTaskOnMainThread( + base::TimeDelta delay, + ResultCallback callback, + int32_t result, + const base::Location& from_here) { + int64_t delay_in_msec = delay.InMilliseconds(); + DCHECK(delay_in_msec <= INT32_MAX); + pp::Module::Get()->core()->CallOnMainThread( + static_cast<int32_t>(delay_in_msec), + PPCompletionCallbackFromResultCallback(std::move(callback)), result); +} + void OutOfProcessInstance::ProcessPreviewPageInfo(const std::string& url, int dest_page_index) { DCHECK(IsPrintPreview());
diff --git a/pdf/out_of_process_instance.h b/pdf/out_of_process_instance.h index e3d94324..0a59798 100644 --- a/pdf/out_of_process_instance.h +++ b/pdf/out_of_process_instance.h
@@ -16,8 +16,8 @@ #include "base/callback.h" #include "base/containers/queue.h" +#include "base/location.h" #include "base/memory/weak_ptr.h" -#include "pdf/paint_manager.h" #include "pdf/pdf_view_plugin_base.h" #include "pdf/preview_mode_client.h" #include "ppapi/c/private/ppp_pdf.h" @@ -163,6 +163,11 @@ void OnPaint(const std::vector<gfx::Rect>& paint_rects, std::vector<PaintReadyRect>* ready, std::vector<gfx::Rect>* pending) override; + void ScheduleTaskOnMainThread( + base::TimeDelta delay, + ResultCallback callback, + int32_t result, + const base::Location& from_here = base::Location::Current()) override; // PreviewModeClient::Client: void PreviewDocumentLoadComplete() override; @@ -390,8 +395,6 @@ // True if the plugin is full-page. bool full_ = false; - PaintManager paint_manager_; - // True if we haven't painted the plugin viewport yet. bool first_paint_ = true; // Whether OnPaint() is in progress or not.
diff --git a/pdf/paint_manager.cc b/pdf/paint_manager.cc index 47d896a..fe08ac9 100644 --- a/pdf/paint_manager.cc +++ b/pdf/paint_manager.cc
@@ -13,6 +13,7 @@ #include "base/bind.h" #include "base/callback.h" #include "base/check.h" +#include "base/time/time.h" #include "pdf/paint_ready_rect.h" #include "pdf/ppapi_migration/callback.h" #include "pdf/ppapi_migration/geometry_conversions.h" @@ -161,10 +162,10 @@ if (manual_callback_pending_) return; - pp::Module::Get()->core()->CallOnMainThread( - 0, - PPCompletionCallbackFromResultCallback(base::BindOnce( - &PaintManager::OnManualCallbackComplete, weak_factory_.GetWeakPtr())), + client_->ScheduleTaskOnMainThread( + base::TimeDelta(), + base::BindOnce(&PaintManager::OnManualCallbackComplete, + weak_factory_.GetWeakPtr()), 0); manual_callback_pending_ = true; }
diff --git a/pdf/paint_manager.h b/pdf/paint_manager.h index a630811..06e91f3 100644 --- a/pdf/paint_manager.h +++ b/pdf/paint_manager.h
@@ -10,10 +10,16 @@ #include <memory> #include <vector> +#include "base/location.h" #include "base/memory/weak_ptr.h" #include "pdf/paint_aggregator.h" +#include "pdf/ppapi_migration/callback.h" #include "ui/gfx/geometry/size.h" +namespace base { +class TimeDelta; +} // namespace base + namespace gfx { class Point; class Rect; @@ -66,6 +72,18 @@ std::vector<PaintReadyRect>* ready, std::vector<gfx::Rect>* pending) = 0; + // Schedules work to be executed on a main thread after a specific delay. + // The `result` parameter will be passed as the argument to the `callback`. + // `result` is needed sometimes to emulate calls of some callbacks, but it's + // not always needed. `delay` should be no longer than `INT32_MAX` + // milliseconds for the Pepper plugin implementation to prevent integer + // overflow. + virtual void ScheduleTaskOnMainThread( + base::TimeDelta delay, + ResultCallback callback, + int32_t result, + const base::Location& from_here = base::Location::Current()) = 0; + protected: // You shouldn't be doing deleting through this interface. ~Client() = default;
diff --git a/pdf/pdf_engine.h b/pdf/pdf_engine.h index 392c0576..9017dee0 100644 --- a/pdf/pdf_engine.h +++ b/pdf/pdf_engine.h
@@ -13,6 +13,7 @@ #include "base/callback.h" #include "base/containers/span.h" +#include "base/location.h" #include "base/optional.h" #include "base/strings/string16.h" #include "base/time/time.h" @@ -20,6 +21,7 @@ #include "build/build_config.h" #include "build/chromeos_buildflags.h" #include "pdf/document_layout.h" +#include "pdf/ppapi_migration/callback.h" #include "ppapi/c/dev/pp_cursor_type_dev.h" #include "ppapi/c/dev/ppp_printing_dev.h" #include "ppapi/cpp/completion_callback.h" @@ -286,6 +288,18 @@ // viewers. // See https://crbug.com/312882 for an example. virtual bool IsValidLink(const std::string& url) = 0; + + // Schedules work to be executed on a main thread after a specific delay. + // The `result` parameter will be passed as the argument to the `callback`. + // `result` is needed sometimes to emulate calls of some callbacks, but it's + // not always needed. `delay` should be no longer than `INT32_MAX` + // milliseconds for the Pepper plugin implementation to prevent integer + // overflow. + virtual void ScheduleTaskOnMainThread( + base::TimeDelta delay, + ResultCallback callback, + int32_t result, + const base::Location& from_here = base::Location::Current()) = 0; }; struct AccessibilityLinkInfo {
diff --git a/pdf/pdf_view_plugin_base.h b/pdf/pdf_view_plugin_base.h index dd5677f..e9ecfa0b 100644 --- a/pdf/pdf_view_plugin_base.h +++ b/pdf/pdf_view_plugin_base.h
@@ -42,6 +42,8 @@ PDFiumEngine* engine() { return engine_.get(); } + PaintManager& paint_manager() { return paint_manager_; } + // Starts loading `url`. If `is_print_preview` is `true`, load for print // preview instead of normal PDF viewing. void LoadUrl(const std::string& url, bool is_print_preview); @@ -62,6 +64,7 @@ private: std::unique_ptr<PDFiumEngine> engine_; + PaintManager paint_manager_{this}; }; } // namespace chrome_pdf
diff --git a/pdf/pdf_view_web_plugin.cc b/pdf/pdf_view_web_plugin.cc index 4143faa..8c246c4 100644 --- a/pdf/pdf_view_web_plugin.cc +++ b/pdf/pdf_view_web_plugin.cc
@@ -12,9 +12,11 @@ #include <vector> #include "base/check_op.h" +#include "base/location.h" #include "base/notreached.h" #include "base/thread_annotations.h" #include "base/threading/thread_checker.h" +#include "base/time/time.h" #include "cc/paint/paint_canvas.h" #include "net/cookies/site_for_cookies.h" #include "pdf/pdf_engine.h" @@ -329,6 +331,15 @@ NOTIMPLEMENTED_LOG_ONCE(); } +void PdfViewWebPlugin::ScheduleTaskOnMainThread( + base::TimeDelta delay, + ResultCallback callback, + int32_t result, + const base::Location& from_here) { + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + from_here, base::BindOnce(std::move(callback), result), delay); +} + bool PdfViewWebPlugin::IsValid() const { return container_ && container_->GetDocument().GetFrame(); }
diff --git a/pdf/pdf_view_web_plugin.h b/pdf/pdf_view_web_plugin.h index 00ecc52..ff329c4 100644 --- a/pdf/pdf_view_web_plugin.h +++ b/pdf/pdf_view_web_plugin.h
@@ -5,8 +5,8 @@ #ifndef PDF_PDF_VIEW_WEB_PLUGIN_H_ #define PDF_PDF_VIEW_WEB_PLUGIN_H_ +#include "base/location.h" #include "base/memory/weak_ptr.h" -#include "pdf/paint_manager.h" #include "pdf/pdf_view_plugin_base.h" #include "pdf/ppapi_migration/url_loader.h" #include "third_party/blink/public/web/web_plugin.h" @@ -109,6 +109,11 @@ void OnPaint(const std::vector<gfx::Rect>& paint_rects, std::vector<PaintReadyRect>* ready, std::vector<gfx::Rect>* pending) override; + void ScheduleTaskOnMainThread( + base::TimeDelta delay, + ResultCallback callback, + int32_t result, + const base::Location& from_here = base::Location::Current()) override; // BlinkUrlLoader::Client: bool IsValid() const override; @@ -134,8 +139,6 @@ blink::WebPluginParams initial_params_; blink::WebPluginContainer* container_ = nullptr; - PaintManager paint_manager_{this}; - // The background color of the PDF viewer. uint32_t background_color_ = 0; // The blank space above the first page of the document reserved for the
diff --git a/pdf/pdfium/pdfium_engine.cc b/pdf/pdfium/pdfium_engine.cc index 28e3ae9..baa08e3 100644 --- a/pdf/pdfium/pdfium_engine.cc +++ b/pdf/pdfium/pdfium_engine.cc
@@ -26,6 +26,7 @@ #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/threading/thread_task_runner_handle.h" +#include "base/time/time.h" #include "build/build_config.h" #include "gin/array_buffer.h" #include "gin/public/gin_embedders.h" @@ -1745,10 +1746,10 @@ if (doc_loader_set_for_testing_) { ContinueFind(case_sensitive ? 1 : 0); } else { - pp::Module::Get()->core()->CallOnMainThread( - 0, - PPCompletionCallbackFromResultCallback(base::BindOnce( - &PDFiumEngine::ContinueFind, find_weak_factory_.GetWeakPtr())), + client_->ScheduleTaskOnMainThread( + base::TimeDelta(), + base::BindOnce(&PDFiumEngine::ContinueFind, + find_weak_factory_.GetWeakPtr()), case_sensitive ? 1 : 0); } }
diff --git a/pdf/preview_mode_client.cc b/pdf/preview_mode_client.cc index bdafe06..850ef83 100644 --- a/pdf/preview_mode_client.cc +++ b/pdf/preview_mode_client.cc
@@ -11,7 +11,9 @@ #include <utility> #include "base/callback.h" +#include "base/location.h" #include "base/notreached.h" +#include "base/time/time.h" #include "pdf/document_layout.h" #include "pdf/ppapi_migration/url_loader.h" @@ -178,4 +180,12 @@ return false; } +void PreviewModeClient::ScheduleTaskOnMainThread( + base::TimeDelta delay, + ResultCallback callback, + int32_t result, + const base::Location& from_here) { + NOTREACHED(); +} + } // namespace chrome_pdf
diff --git a/pdf/preview_mode_client.h b/pdf/preview_mode_client.h index a5250c4..cc8b289 100644 --- a/pdf/preview_mode_client.h +++ b/pdf/preview_mode_client.h
@@ -11,6 +11,7 @@ #include <vector> #include "base/callback_forward.h" +#include "base/location.h" #include "pdf/pdf_engine.h" namespace gfx { @@ -78,6 +79,11 @@ void SetSelectedText(const std::string& selected_text) override; void SetLinkUnderCursor(const std::string& link_under_cursor) override; bool IsValidLink(const std::string& url) override; + void ScheduleTaskOnMainThread( + base::TimeDelta delay, + ResultCallback callback, + int32_t result, + const base::Location& from_here = base::Location::Current()) override; private: Client* const client_;
diff --git a/pdf/test/test_client.cc b/pdf/test/test_client.cc index 2fd87a42..55b8e40 100644 --- a/pdf/test/test_client.cc +++ b/pdf/test/test_client.cc
@@ -6,6 +6,8 @@ #include <memory> +#include "base/location.h" +#include "base/time/time.h" #include "pdf/document_layout.h" #include "pdf/ppapi_migration/url_loader.h" @@ -71,4 +73,9 @@ return !url.empty(); } +void TestClient::ScheduleTaskOnMainThread(base::TimeDelta delay, + ResultCallback callback, + int32_t result, + const base::Location& from_here) {} + } // namespace chrome_pdf
diff --git a/pdf/test/test_client.h b/pdf/test/test_client.h index 6f62f17d..bd0a9c0 100644 --- a/pdf/test/test_client.h +++ b/pdf/test/test_client.h
@@ -8,6 +8,7 @@ #include <string> #include <vector> +#include "base/location.h" #include "pdf/pdf_engine.h" namespace chrome_pdf { @@ -41,6 +42,11 @@ void SetSelectedText(const std::string& selected_text) override; void SetLinkUnderCursor(const std::string& link_under_cursor) override; bool IsValidLink(const std::string& url) override; + void ScheduleTaskOnMainThread( + base::TimeDelta delay, + ResultCallback callback, + int32_t result, + const base::Location& from_here = base::Location::Current()) override; private: // Not owned. Expected to dangle briefly, as the engine usually is destroyed
diff --git a/remoting/host/linux/linux_me2me_host.py b/remoting/host/linux/linux_me2me_host.py index 42d62f3..9b49a9f 100755 --- a/remoting/host/linux/linux_me2me_host.py +++ b/remoting/host/linux/linux_me2me_host.py
@@ -101,6 +101,7 @@ CONFIG_DIR = os.path.join(HOME_DIR, ".config/chrome-remote-desktop") SESSION_FILE_PATH = os.path.join(HOME_DIR, ".chrome-remote-desktop-session") SYSTEM_SESSION_FILE_PATH = "/etc/chrome-remote-desktop-session" +SYSTEM_PRE_SESSION_FILE_PATH = "/etc/chrome-remote-desktop-pre-session" DEBIAN_XSESSION_PATH = "/etc/X11/Xsession" @@ -388,13 +389,16 @@ class SessionOutputFilterThread(threading.Thread): - """Reads session log from a pipe and logs the output for amount of time - defined by SESSION_OUTPUT_TIME_LIMIT_SECONDS.""" + """Reads session log from a pipe and logs the output with the provided prefix + for amount of time defined by time_limit, or indefinitely if time_limit is + None.""" - def __init__(self, stream): + def __init__(self, stream, prefix, time_limit): threading.Thread.__init__(self) self.stream = stream self.daemon = True + self.prefix = prefix + self.time_limit = time_limit def run(self): started_time = time.time() @@ -413,13 +417,12 @@ if not is_logging: continue - if time.time() - started_time >= SESSION_OUTPUT_TIME_LIMIT_SECONDS: + if self.time_limit and time.time() - started_time >= self.time_limit: is_logging = False print("Suppressing rest of the session output.", flush=True) else: # Pass stream bytes through as is instead of decoding and encoding. - sys.stdout.buffer.write( - "Session output: ".encode(sys.stdout.encoding) + line); + sys.stdout.buffer.write(self.prefix.encode(sys.stdout.encoding) + line); sys.stdout.flush() @@ -428,6 +431,7 @@ def __init__(self, sizes): self.x_proc = None + self.pre_session_proc = None self.session_proc = None self.host_proc = None self.child_env = None @@ -719,7 +723,32 @@ subprocess.Popen(args, env=self.child_env, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) - def _launch_x_session(self): + def _launch_pre_session(self): + # Launch the pre-session script, if it exists. Returns true if the script + # was launched, false if it didn't exist. + if os.path.exists(SYSTEM_PRE_SESSION_FILE_PATH): + pre_session_command = bash_invocation_for_script( + SYSTEM_PRE_SESSION_FILE_PATH) + + logging.info("Launching pre-session: %s" % pre_session_command) + self.pre_session_proc = subprocess.Popen(pre_session_command, + stdin=subprocess.DEVNULL, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + cwd=HOME_DIR, + env=self.child_env) + + if not self.pre_session_proc.pid: + raise Exception("Could not start pre-session") + + output_filter_thread = SessionOutputFilterThread( + self.pre_session_proc.stdout, "Pre-session output: ", None) + output_filter_thread.start() + + return True + return False + + def launch_x_session(self): # Start desktop session. # The /dev/null input redirection is necessary to prevent the X session # reading from stdin. If this code runs as a shell background job in a @@ -738,18 +767,21 @@ cwd=HOME_DIR, env=self.child_env) - output_filter_thread = SessionOutputFilterThread(self.session_proc.stdout) - output_filter_thread.start() - if not self.session_proc.pid: raise Exception("Could not start X session") + output_filter_thread = SessionOutputFilterThread(self.session_proc.stdout, + "Session output: ", SESSION_OUTPUT_TIME_LIMIT_SECONDS) + output_filter_thread.start() + def launch_session(self, x_args): self._init_child_env() self._setup_pulseaudio() self._setup_gnubby() self._launch_x_server(x_args) - self._launch_x_session() + if not self._launch_pre_session(): + # If there was no pre-session script, launch the session immediately. + self.launch_x_session() def launch_host(self, host_config, extra_start_host_args): # Start remoting host @@ -797,6 +829,7 @@ SIGKILL if a process doesn't exit within 10 seconds. """ for proc, name in [(self.x_proc, "X server"), + (self.pre_session_proc, "pre-session"), (self.session_proc, "session"), (self.host_proc, "host")]: if proc is not None: @@ -814,6 +847,7 @@ except psutil.Error: logging.error("Error terminating process") self.x_proc = None + self.pre_session_proc = None self.session_proc = None self.host_proc = None @@ -926,6 +960,20 @@ return non_child_process if not require_child_process else None +def bash_invocation_for_script(script): + """Chooses the appropriate bash command to run the provided script.""" + if os.path.exists(script): + if os.access(script, os.X_OK): + # "/bin/sh -c" is smart about how to execute the session script and + # works in cases where plain exec() fails (for example, if the file is + # marked executable, but is a plain script with no shebang line). + return ["/bin/sh", "-c", pipes.quote(script)] + else: + # If this is a system-wide session script, it should be run using the + # system shell, ignoring any login shell that might be set for the + # current user. + return ["/bin/sh", script] + def choose_x_session(): """Chooses the most appropriate X session command for this system. @@ -941,16 +989,7 @@ for startup_file in XSESSION_FILES: startup_file = os.path.expanduser(startup_file) if os.path.exists(startup_file): - if os.access(startup_file, os.X_OK): - # "/bin/sh -c" is smart about how to execute the session script and - # works in cases where plain exec() fails (for example, if the file is - # marked executable, but is a plain script with no shebang line). - return ["/bin/sh", "-c", pipes.quote(startup_file)] - else: - # If this is a system-wide session script, it should be run using the - # system shell, ignoring any login shell that might be set for the - # current user. - return ["/bin/sh", startup_file] + return bash_invocation_for_script(startup_file) # If there's no configuration, show the user a session chooser. return [HOST_BINARY_PATH, "--type=xsession_chooser"] @@ -1708,7 +1747,8 @@ # launching things in the wrong order due to differing relaunch times. logging.info("Waiting before relaunching") else: - if desktop.x_proc is None and desktop.session_proc is None: + if (desktop.x_proc is None and desktop.pre_session_proc is None and + desktop.session_proc is None): logging.info("Launching X server and X session.") desktop.launch_session(options.args) x_server_inhibitor.record_started(MINIMUM_PROCESS_LIFETIME, @@ -1742,6 +1782,25 @@ x_server_inhibitor.record_stopped(False) tear_down = True + if (desktop.pre_session_proc is not None and + pid == desktop.pre_session_proc.pid): + desktop.pre_session_proc = None + if status == 0: + logging.info("Pre-session terminated successfully. Starting session.") + desktop.launch_x_session() + else: + logging.info("Pre-session failed. Tearing down.") + # The pre-session may have exited on its own or been brought down by the + # X server dying. Check if the X server is still running so we know whom + # to penalize. + if desktop.check_x_responding(): + # Pre-session and session use the same inhibitor. + session_inhibitor.record_stopped(False) + else: + x_server_inhibitor.record_stopped(False) + # Either way, we want to tear down the session. + tear_down = True + if desktop.session_proc is not None and pid == desktop.session_proc.pid: logging.info("Session process terminated") desktop.session_proc = None
diff --git a/remoting/protocol/webrtc_transport_unittest.cc b/remoting/protocol/webrtc_transport_unittest.cc index edc2fef..10ff018 100644 --- a/remoting/protocol/webrtc_transport_unittest.cc +++ b/remoting/protocol/webrtc_transport_unittest.cc
@@ -495,7 +495,8 @@ } TEST_F(WebrtcTransportTest, - ThreadJoinNotBlockedDuringConnectionTeardown_WatchdogNotFired) { + DISABLED_ThreadJoinNotBlockedDuringConnectionTeardown_WatchdogNotFired) { + // Test disabled for crbug.com/1160702 InitializeConnection(); int counter = 2;
diff --git a/services/device/BUILD.gn b/services/device/BUILD.gn index 7c36810c..2f56d1d 100644 --- a/services/device/BUILD.gn +++ b/services/device/BUILD.gn
@@ -162,6 +162,7 @@ "//base/test:test_support", "//base/third_party/dynamic_annotations", "//build:chromeos_buildflags", + "//components/variations", "//device/base/synchronization", "//mojo/public/cpp/bindings", "//net", @@ -245,8 +246,10 @@ sources += [ "battery/battery_monitor_impl_unittest.cc", "hid/hid_manager_unittest.cc", + "public/cpp/hid/hid_blocklist_unittest.cc", ] deps += [ + "//components/variations:test_support", "//services/device/battery", "//services/device/hid:mocks", "//services/device/public/cpp/hid",
diff --git a/services/device/DEPS b/services/device/DEPS index 243aae8..12e8777 100644 --- a/services/device/DEPS +++ b/services/device/DEPS
@@ -1,6 +1,7 @@ include_rules = [ "+chromeos/crosapi", "+chromeos/lacros", + "+components/variations", "+device", "+services/device/usb/jni_headers", "+services/network/public/cpp",
diff --git a/services/device/hid/hid_connection.cc b/services/device/hid/hid_connection.cc index c513132..8319493 100644 --- a/services/device/hid/hid_connection.cc +++ b/services/device/hid/hid_connection.cc
@@ -36,9 +36,9 @@ }; // Functor returning true if collection has a protected usage. -struct CollectionIsProtected { +struct CollectionIsAlwaysProtected { bool operator()(const mojom::HidCollectionInfoPtr& info) const { - return IsProtected(*info->usage); + return IsAlwaysProtected(*info->usage); } }; @@ -55,18 +55,21 @@ return nullptr; } -bool HasProtectedCollection( +bool HasAlwaysProtectedCollection( const std::vector<mojom::HidCollectionInfoPtr>& collections) { return std::find_if(collections.begin(), collections.end(), - CollectionIsProtected()) != collections.end(); + CollectionIsAlwaysProtected()) != collections.end(); } } // namespace -HidConnection::HidConnection(scoped_refptr<HidDeviceInfo> device_info) - : device_info_(device_info), closed_(false) { - has_protected_collection_ = - HasProtectedCollection(device_info->collections()); +HidConnection::HidConnection(scoped_refptr<HidDeviceInfo> device_info, + bool allow_protected_reports) + : device_info_(device_info), + allow_protected_reports_(allow_protected_reports), + closed_(false) { + has_always_protected_collection_ = + HasAlwaysProtectedCollection(device_info->collections()); } HidConnection::~HidConnection() { @@ -125,7 +128,7 @@ std::move(callback).Run(false); return; } - if (IsReportIdProtected(report_id)) { + if (IsReportIdProtected(report_id, kOutput)) { HID_LOG(USER) << "Attempt to set a protected output report."; std::move(callback).Run(false); return; @@ -146,7 +149,7 @@ std::move(callback).Run(false, nullptr, 0); return; } - if (IsReportIdProtected(report_id)) { + if (IsReportIdProtected(report_id, kFeature)) { HID_LOG(USER) << "Attempt to get a protected feature report."; std::move(callback).Run(false, nullptr, 0); return; @@ -171,7 +174,7 @@ std::move(callback).Run(false); return; } - if (IsReportIdProtected(report_id)) { + if (IsReportIdProtected(report_id, kFeature)) { HID_LOG(USER) << "Attempt to set a protected feature report."; std::move(callback).Run(false); return; @@ -180,14 +183,40 @@ PlatformSendFeatureReport(buffer, std::move(callback)); } -bool HidConnection::IsReportIdProtected(uint8_t report_id) { +bool HidConnection::IsReportIdProtected(uint8_t report_id, + HidReportType report_type) { + if (!allow_protected_reports_) { + // Deny access to reports that match HID blocklist rules. + if (report_type == kInput) { + if (device_info_->device()->protected_input_report_ids.has_value() && + base::Contains(*device_info_->device()->protected_input_report_ids, + report_id)) { + return true; + } + } else if (report_type == kOutput) { + if (device_info_->device()->protected_output_report_ids.has_value() && + base::Contains(*device_info_->device()->protected_output_report_ids, + report_id)) { + return true; + } + } else if (report_type == kFeature) { + if (device_info_->device()->protected_feature_report_ids.has_value() && + base::Contains(*device_info_->device()->protected_feature_report_ids, + report_id)) { + return true; + } + } + } + + // Some types of reports are always blocked regardless of + // |allow_protected_reports_|. auto* collection_info = FindCollectionByReportId(device_info_->collections(), report_id); if (collection_info) { - return IsProtected(*collection_info->usage); + return IsAlwaysProtected(*collection_info->usage); } - return has_protected_collection(); + return has_always_protected_collection(); } void HidConnection::ProcessInputReport( @@ -197,7 +226,7 @@ DCHECK_GE(size, 1u); uint8_t report_id = buffer->data()[0]; - if (IsReportIdProtected(report_id)) + if (IsReportIdProtected(report_id, kInput)) return; if (client_) {
diff --git a/services/device/hid/hid_connection.h b/services/device/hid/hid_connection.h index ac6a788..f244cb4 100644 --- a/services/device/hid/hid_connection.h +++ b/services/device/hid/hid_connection.h
@@ -49,7 +49,9 @@ void SetClient(Client* client); scoped_refptr<HidDeviceInfo> device_info() const { return device_info_; } - bool has_protected_collection() const { return has_protected_collection_; } + bool has_always_protected_collection() const { + return has_always_protected_collection_; + } bool closed() const { return closed_; } // Closes the connection. This must be called before the object is freed. @@ -75,9 +77,16 @@ WriteCallback callback); protected: + enum HidReportType { + kInput, + kOutput, + kFeature, + }; + friend class base::RefCountedThreadSafe<HidConnection>; - explicit HidConnection(scoped_refptr<HidDeviceInfo> device_info); + HidConnection(scoped_refptr<HidDeviceInfo> device_info, + bool allow_protected_reports); virtual ~HidConnection(); virtual void PlatformClose() = 0; @@ -89,15 +98,16 @@ scoped_refptr<base::RefCountedBytes> buffer, WriteCallback callback) = 0; - bool IsReportIdProtected(uint8_t report_id); + bool IsReportIdProtected(uint8_t report_id, HidReportType report_type); void ProcessInputReport(scoped_refptr<base::RefCountedBytes> buffer, size_t size); void ProcessReadQueue(); private: scoped_refptr<HidDeviceInfo> device_info_; + bool allow_protected_reports_; Client* client_ = nullptr; - bool has_protected_collection_; + bool has_always_protected_collection_; bool closed_; base::queue<std::tuple<scoped_refptr<base::RefCountedBytes>, size_t>>
diff --git a/services/device/hid/hid_connection_impl_unittest.cc b/services/device/hid/hid_connection_impl_unittest.cc index 2cea116c..edd52b1 100644 --- a/services/device/hid/hid_connection_impl_unittest.cc +++ b/services/device/hid/hid_connection_impl_unittest.cc
@@ -37,8 +37,8 @@ // input report. class FakeHidConnection : public HidConnection { public: - FakeHidConnection(scoped_refptr<HidDeviceInfo> device) - : HidConnection(device) {} + explicit FakeHidConnection(scoped_refptr<HidDeviceInfo> device) + : HidConnection(device, /*allow_protected_reports=*/false) {} // HidConnection implementation. void PlatformClose() override {}
diff --git a/services/device/hid/hid_connection_linux.cc b/services/device/hid/hid_connection_linux.cc index 208bdec..870f5d22 100644 --- a/services/device/hid/hid_connection_linux.cc +++ b/services/device/hid/hid_connection_linux.cc
@@ -184,8 +184,9 @@ HidConnectionLinux::HidConnectionLinux( scoped_refptr<HidDeviceInfo> device_info, base::ScopedFD fd, - scoped_refptr<base::SequencedTaskRunner> blocking_task_runner) - : HidConnection(device_info), + scoped_refptr<base::SequencedTaskRunner> blocking_task_runner, + bool allow_protected_reports) + : HidConnection(device_info, allow_protected_reports), helper_(nullptr, base::OnTaskRunnerDeleter(blocking_task_runner)), blocking_task_runner_(std::move(blocking_task_runner)) { helper_.reset(new BlockingTaskRunnerHelper(std::move(fd), device_info,
diff --git a/services/device/hid/hid_connection_linux.h b/services/device/hid/hid_connection_linux.h index ce5df85..6db774a7b 100644 --- a/services/device/hid/hid_connection_linux.h +++ b/services/device/hid/hid_connection_linux.h
@@ -25,7 +25,8 @@ HidConnectionLinux( scoped_refptr<HidDeviceInfo> device_info, base::ScopedFD fd, - scoped_refptr<base::SequencedTaskRunner> blocking_task_runner); + scoped_refptr<base::SequencedTaskRunner> blocking_task_runner, + bool allow_protected_reports); private: friend class base::RefCountedThreadSafe<HidConnectionLinux>;
diff --git a/services/device/hid/hid_connection_mac.cc b/services/device/hid/hid_connection_mac.cc index 9e6cecf..84d25ab 100644 --- a/services/device/hid/hid_connection_mac.cc +++ b/services/device/hid/hid_connection_mac.cc
@@ -28,8 +28,9 @@ } // namespace HidConnectionMac::HidConnectionMac(base::ScopedCFTypeRef<IOHIDDeviceRef> device, - scoped_refptr<HidDeviceInfo> device_info) - : HidConnection(device_info), + scoped_refptr<HidDeviceInfo> device_info, + bool allow_protected_reports) + : HidConnection(device_info, allow_protected_reports), device_(std::move(device)), task_runner_(base::ThreadTaskRunnerHandle::Get()), blocking_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
diff --git a/services/device/hid/hid_connection_mac.h b/services/device/hid/hid_connection_mac.h index f891e4ee..06af473 100644 --- a/services/device/hid/hid_connection_mac.h +++ b/services/device/hid/hid_connection_mac.h
@@ -24,7 +24,8 @@ class HidConnectionMac : public HidConnection { public: HidConnectionMac(base::ScopedCFTypeRef<IOHIDDeviceRef> device, - scoped_refptr<HidDeviceInfo> device_info); + scoped_refptr<HidDeviceInfo> device_info, + bool allow_protected_reports); private: ~HidConnectionMac() override;
diff --git a/services/device/hid/hid_connection_unittest.cc b/services/device/hid/hid_connection_unittest.cc index ed9a18c..a78c298 100644 --- a/services/device/hid/hid_connection_unittest.cc +++ b/services/device/hid/hid_connection_unittest.cc
@@ -185,7 +185,8 @@ return; TestConnectCallback connect_callback; - service_->Connect(device_guid_, connect_callback.GetCallback()); + service_->Connect(device_guid_, /*allow_protected_reports=*/false, + connect_callback.GetCallback()); scoped_refptr<HidConnection> conn = connect_callback.WaitForConnection(); ASSERT_TRUE(conn.get());
diff --git a/services/device/hid/hid_connection_win.cc b/services/device/hid/hid_connection_win.cc index c1a5210e..98f7fbe 100644 --- a/services/device/hid/hid_connection_win.cc +++ b/services/device/hid/hid_connection_win.cc
@@ -89,16 +89,19 @@ // static scoped_refptr<HidConnection> HidConnectionWin::Create( scoped_refptr<HidDeviceInfo> device_info, - base::win::ScopedHandle file) { - scoped_refptr<HidConnectionWin> connection( - new HidConnectionWin(std::move(device_info), std::move(file))); + base::win::ScopedHandle file, + bool allow_protected_reports) { + scoped_refptr<HidConnectionWin> connection(new HidConnectionWin( + std::move(device_info), std::move(file), allow_protected_reports)); connection->ReadNextInputReport(); return std::move(connection); } HidConnectionWin::HidConnectionWin(scoped_refptr<HidDeviceInfo> device_info, - base::win::ScopedHandle file) - : HidConnection(std::move(device_info)), file_(std::move(file)) {} + base::win::ScopedHandle file, + bool allow_protected_reports) + : HidConnection(std::move(device_info), allow_protected_reports), + file_(std::move(file)) {} HidConnectionWin::~HidConnectionWin() { DCHECK(!file_.IsValid()); @@ -194,7 +197,7 @@ } uint8_t report_id = buffer->data()[0]; - if (IsReportIdProtected(report_id)) { + if (IsReportIdProtected(report_id, HidReportType::kInput)) { ReadNextInputReport(); return; }
diff --git a/services/device/hid/hid_connection_win.h b/services/device/hid/hid_connection_win.h index ecdd7c2..e068cac 100644 --- a/services/device/hid/hid_connection_win.h +++ b/services/device/hid/hid_connection_win.h
@@ -23,14 +23,16 @@ public: static scoped_refptr<HidConnection> Create( scoped_refptr<HidDeviceInfo> device_info, - base::win::ScopedHandle file); + base::win::ScopedHandle file, + bool allow_protected_reports); private: friend class HidServiceWin; friend class PendingHidTransfer; HidConnectionWin(scoped_refptr<HidDeviceInfo> device_info, - base::win::ScopedHandle file); + base::win::ScopedHandle file, + bool allow_protected_reports); ~HidConnectionWin() override; // HidConnection implementation.
diff --git a/services/device/hid/hid_device_info.cc b/services/device/hid/hid_device_info.cc index e58a902..93cf696 100644 --- a/services/device/hid/hid_device_info.cc +++ b/services/device/hid/hid_device_info.cc
@@ -6,6 +6,7 @@ #include "base/guid.h" #include "build/build_config.h" +#include "services/device/public/cpp/hid/hid_blocklist.h" #include "services/device/public/cpp/hid/hid_report_descriptor.h" namespace device { @@ -31,11 +32,20 @@ &max_input_report_size, &max_output_report_size, &max_feature_report_size); + auto protected_input_report_ids = HidBlocklist::Get().GetProtectedReportIds( + HidBlocklist::kReportTypeInput, vendor_id, product_id, collections); + auto protected_output_report_ids = HidBlocklist::Get().GetProtectedReportIds( + HidBlocklist::kReportTypeOutput, vendor_id, product_id, collections); + auto protected_feature_report_ids = HidBlocklist::Get().GetProtectedReportIds( + HidBlocklist::kReportTypeFeature, vendor_id, product_id, collections); + device_ = mojom::HidDeviceInfo::New( base::GenerateGUID(), physical_device_id, vendor_id, product_id, product_name, serial_number, bus_type, report_descriptor, std::move(collections), has_report_id, max_input_report_size, - max_output_report_size, max_feature_report_size, device_node); + max_output_report_size, max_feature_report_size, device_node, + protected_input_report_ids, protected_output_report_ids, + protected_feature_report_ids); } HidDeviceInfo::HidDeviceInfo(const HidPlatformDeviceId& platform_device_id, @@ -54,14 +64,23 @@ bool has_report_id = !collection->report_ids.empty(); collections.push_back(std::move(collection)); + auto protected_input_report_ids = HidBlocklist::Get().GetProtectedReportIds( + HidBlocklist::kReportTypeInput, vendor_id, product_id, collections); + auto protected_output_report_ids = HidBlocklist::Get().GetProtectedReportIds( + HidBlocklist::kReportTypeOutput, vendor_id, product_id, collections); + auto protected_feature_report_ids = HidBlocklist::Get().GetProtectedReportIds( + HidBlocklist::kReportTypeFeature, vendor_id, product_id, collections); + std::vector<uint8_t> report_descriptor; device_ = mojom::HidDeviceInfo::New( base::GenerateGUID(), physical_device_id, vendor_id, product_id, product_name, serial_number, bus_type, report_descriptor, std::move(collections), has_report_id, max_input_report_size, - max_output_report_size, max_feature_report_size, ""); + max_output_report_size, max_feature_report_size, "", + protected_input_report_ids, protected_output_report_ids, + protected_feature_report_ids); } -HidDeviceInfo::~HidDeviceInfo() {} +HidDeviceInfo::~HidDeviceInfo() = default; } // namespace device
diff --git a/services/device/hid/hid_manager_impl.cc b/services/device/hid/hid_manager_impl.cc index bcdb44d..003d3d9 100644 --- a/services/device/hid/hid_manager_impl.cc +++ b/services/device/hid/hid_manager_impl.cc
@@ -78,8 +78,9 @@ const std::string& device_guid, mojo::PendingRemote<mojom::HidConnectionClient> connection_client, mojo::PendingRemote<mojom::HidConnectionWatcher> watcher, + bool allow_protected_reports, ConnectCallback callback) { - hid_service_->Connect(device_guid, + hid_service_->Connect(device_guid, allow_protected_reports, base::AdaptCallbackForRepeating(base::BindOnce( &HidManagerImpl::CreateConnection, weak_factory_.GetWeakPtr(), std::move(callback),
diff --git a/services/device/hid/hid_manager_impl.h b/services/device/hid/hid_manager_impl.h index a9cfaba..cc9be19 100644 --- a/services/device/hid/hid_manager_impl.h +++ b/services/device/hid/hid_manager_impl.h
@@ -48,6 +48,7 @@ const std::string& device_guid, mojo::PendingRemote<mojom::HidConnectionClient> connection_client, mojo::PendingRemote<mojom::HidConnectionWatcher> watcher, + bool allow_protected_reports, ConnectCallback callback) override; private:
diff --git a/services/device/hid/hid_manager_unittest.cc b/services/device/hid/hid_manager_unittest.cc index 8040dcf..ad3a5728 100644 --- a/services/device/hid/hid_manager_unittest.cc +++ b/services/device/hid/hid_manager_unittest.cc
@@ -264,6 +264,7 @@ device->device_guid(), /*connection_client=*/mojo::NullRemote(), /*watcher=*/mojo::NullRemote(), + /*allow_protected_reports=*/false, base::BindOnce(&OnConnect, run_loop.QuitClosure(), client.get())); run_loop.Run(); }
diff --git a/services/device/hid/hid_service.h b/services/device/hid/hid_service.h index 0784fd4..a6c4790b 100644 --- a/services/device/hid/hid_service.h +++ b/services/device/hid/hid_service.h
@@ -68,6 +68,7 @@ // Opens a connection to a device. The callback will be run with null on // failure. virtual void Connect(const std::string& device_guid, + bool allow_protected_reports, ConnectCallback callback) = 0; protected:
diff --git a/services/device/hid/hid_service_linux.cc b/services/device/hid/hid_service_linux.cc index ba0a694..de3637e 100644 --- a/services/device/hid/hid_service_linux.cc +++ b/services/device/hid/hid_service_linux.cc
@@ -176,8 +176,10 @@ struct HidServiceLinux::ConnectParams { ConnectParams(scoped_refptr<HidDeviceInfo> device_info, + bool allow_protected_reports, ConnectCallback callback) : device_info(std::move(device_info)), + allow_protected_reports(allow_protected_reports), callback(std::move(callback)), task_runner(base::SequencedTaskRunnerHandle::Get()), blocking_task_runner( @@ -185,6 +187,7 @@ ~ConnectParams() {} scoped_refptr<HidDeviceInfo> device_info; + bool allow_protected_reports; ConnectCallback callback; scoped_refptr<base::SequencedTaskRunner> task_runner; scoped_refptr<base::SequencedTaskRunner> blocking_task_runner; @@ -344,6 +347,7 @@ } void HidServiceLinux::Connect(const std::string& device_guid, + bool allow_protected_reports, ConnectCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); @@ -363,12 +367,13 @@ device_info->device_node(), base::BindOnce( &HidServiceLinux::OnPathOpenComplete, - std::make_unique<ConnectParams>(device_info, copyable_callback)), + std::make_unique<ConnectParams>(device_info, allow_protected_reports, + copyable_callback)), base::BindOnce(&HidServiceLinux::OnPathOpenError, device_info->device_node(), copyable_callback)); #else - auto params = - std::make_unique<ConnectParams>(device_info, std::move(callback)); + auto params = std::make_unique<ConnectParams>( + device_info, allow_protected_reports, std::move(callback)); scoped_refptr<base::SequencedTaskRunner> blocking_task_runner = params->blocking_task_runner; blocking_task_runner->PostTask( @@ -449,7 +454,8 @@ std::move(params->callback) .Run(base::MakeRefCounted<HidConnectionLinux>( std::move(params->device_info), std::move(params->fd), - std::move(params->blocking_task_runner))); + std::move(params->blocking_task_runner), + params->allow_protected_reports)); } } // namespace device
diff --git a/services/device/hid/hid_service_linux.h b/services/device/hid/hid_service_linux.h index 5239016b..5b05a04c 100644 --- a/services/device/hid/hid_service_linux.h +++ b/services/device/hid/hid_service_linux.h
@@ -25,7 +25,9 @@ ~HidServiceLinux() override; // HidService: - void Connect(const std::string& device_id, ConnectCallback callback) override; + void Connect(const std::string& device_id, + bool allow_protected_reports, + ConnectCallback callback) override; base::WeakPtr<HidService> GetWeakPtr() override; private:
diff --git a/services/device/hid/hid_service_mac.cc b/services/device/hid/hid_service_mac.cc index 5ef38b1..72ba607 100644 --- a/services/device/hid/hid_service_mac.cc +++ b/services/device/hid/hid_service_mac.cc
@@ -131,6 +131,7 @@ HidServiceMac::~HidServiceMac() {} void HidServiceMac::Connect(const std::string& device_guid, + bool allow_protected_reports, ConnectCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); @@ -145,7 +146,8 @@ FROM_HERE, kBlockingTaskTraits, base::BindOnce(&HidServiceMac::OpenOnBlockingThread, map_entry->second), base::BindOnce(&HidServiceMac::DeviceOpened, weak_factory_.GetWeakPtr(), - map_entry->second, std::move(callback))); + map_entry->second, allow_protected_reports, + std::move(callback))); } base::WeakPtr<HidService> HidServiceMac::GetWeakPtr() { @@ -191,11 +193,13 @@ void HidServiceMac::DeviceOpened( scoped_refptr<HidDeviceInfo> device_info, + bool allow_protected_reports, ConnectCallback callback, base::ScopedCFTypeRef<IOHIDDeviceRef> hid_device) { if (hid_device) { std::move(callback).Run(base::MakeRefCounted<HidConnectionMac>( - std::move(hid_device), std::move(device_info))); + std::move(hid_device), std::move(device_info), + allow_protected_reports)); } else { std::move(callback).Run(nullptr); }
diff --git a/services/device/hid/hid_service_mac.h b/services/device/hid/hid_service_mac.h index abe4e33..3fd9f06 100644 --- a/services/device/hid/hid_service_mac.h +++ b/services/device/hid/hid_service_mac.h
@@ -26,13 +26,16 @@ HidServiceMac(); ~HidServiceMac() override; - void Connect(const std::string& device_id, ConnectCallback connect) override; + void Connect(const std::string& device_id, + bool allow_protected_reports, + ConnectCallback connect) override; base::WeakPtr<HidService> GetWeakPtr() override; private: static base::ScopedCFTypeRef<IOHIDDeviceRef> OpenOnBlockingThread( scoped_refptr<HidDeviceInfo> device_info); void DeviceOpened(scoped_refptr<HidDeviceInfo> device_info, + bool allow_protected_reports, ConnectCallback callback, base::ScopedCFTypeRef<IOHIDDeviceRef> hid_device);
diff --git a/services/device/hid/hid_service_win.cc b/services/device/hid/hid_service_win.cc index f0d7e939..34a2000 100644 --- a/services/device/hid/hid_service_win.cc +++ b/services/device/hid/hid_service_win.cc
@@ -353,6 +353,7 @@ HidServiceWin::~HidServiceWin() = default; void HidServiceWin::Connect(const std::string& device_guid, + bool allow_protected_reports, ConnectCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); const auto& map_entry = devices().find(device_guid); @@ -374,7 +375,8 @@ task_runner_->PostTask( FROM_HERE, base::BindOnce(std::move(callback), - HidConnectionWin::Create(device_info, std::move(file)))); + HidConnectionWin::Create(device_info, std::move(file), + allow_protected_reports))); } base::WeakPtr<HidService> HidServiceWin::GetWeakPtr() {
diff --git a/services/device/hid/hid_service_win.h b/services/device/hid/hid_service_win.h index 0e99fe41..2ff89c3 100644 --- a/services/device/hid/hid_service_win.h +++ b/services/device/hid/hid_service_win.h
@@ -129,7 +129,9 @@ HidServiceWin& operator=(const HidServiceWin&) = delete; ~HidServiceWin() override; - void Connect(const std::string& device_id, ConnectCallback callback) override; + void Connect(const std::string& device_id, + bool allow_protected_reports, + ConnectCallback callback) override; base::WeakPtr<HidService> GetWeakPtr() override; private:
diff --git a/services/device/hid/mock_hid_connection.cc b/services/device/hid/mock_hid_connection.cc index 40093cec..90b450f 100644 --- a/services/device/hid/mock_hid_connection.cc +++ b/services/device/hid/mock_hid_connection.cc
@@ -10,7 +10,7 @@ namespace device { MockHidConnection::MockHidConnection(scoped_refptr<HidDeviceInfo> device) - : HidConnection(device) {} + : HidConnection(device, /*allow_protected_reports=*/false) {} MockHidConnection::~MockHidConnection() {}
diff --git a/services/device/hid/mock_hid_service.cc b/services/device/hid/mock_hid_service.cc index 364fc82..b58259d 100644 --- a/services/device/hid/mock_hid_service.cc +++ b/services/device/hid/mock_hid_service.cc
@@ -32,6 +32,7 @@ } void MockHidService::Connect(const std::string& device_id, + bool allow_protected_reports, ConnectCallback callback) { const auto& map_entry = devices().find(device_id); if (map_entry == devices().end()) {
diff --git a/services/device/hid/mock_hid_service.h b/services/device/hid/mock_hid_service.h index 1f35cfb..c06a129 100644 --- a/services/device/hid/mock_hid_service.h +++ b/services/device/hid/mock_hid_service.h
@@ -20,7 +20,9 @@ void FirstEnumerationComplete(); const std::map<std::string, scoped_refptr<HidDeviceInfo>>& devices() const; - void Connect(const std::string& device_id, ConnectCallback callback) override; + void Connect(const std::string& device_id, + bool allow_protected_reports, + ConnectCallback callback) override; private: base::WeakPtr<HidService> GetWeakPtr() override;
diff --git a/services/device/public/cpp/hid/BUILD.gn b/services/device/public/cpp/hid/BUILD.gn index d246bd0..fe1a873f 100644 --- a/services/device/public/cpp/hid/BUILD.gn +++ b/services/device/public/cpp/hid/BUILD.gn
@@ -11,6 +11,8 @@ assert(!is_android) sources = [ + "hid_blocklist.cc", + "hid_blocklist.h", "hid_collection.cc", "hid_collection.h", "hid_device_filter.cc", @@ -23,11 +25,14 @@ "hid_report_descriptor_item.h", "hid_report_item.cc", "hid_report_item.h", + "hid_switches.cc", + "hid_switches.h", "hid_usage_and_page.cc", "hid_usage_and_page.h", ] deps = [ + "//components/variations", "//services/device/public/mojom", "//services/service_manager/public/cpp", ] @@ -50,6 +55,7 @@ public_deps = [ "//base", + "//services/device/public/cpp/hid", "//services/device/public/mojom", ] }
diff --git a/services/device/public/cpp/hid/fake_hid_manager.cc b/services/device/public/cpp/hid/fake_hid_manager.cc index 69617c8..bcde2a69 100644 --- a/services/device/public/cpp/hid/fake_hid_manager.cc +++ b/services/device/public/cpp/hid/fake_hid_manager.cc
@@ -10,6 +10,7 @@ #include "base/guid.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/self_owned_receiver.h" +#include "services/device/public/cpp/hid/hid_blocklist.h" namespace device { @@ -143,6 +144,7 @@ const std::string& device_guid, mojo::PendingRemote<mojom::HidConnectionClient> connection_client, mojo::PendingRemote<mojom::HidConnectionWatcher> watcher, + bool allow_protected_reports, ConnectCallback callback) { if (!base::Contains(devices_, device_guid)) { std::move(callback).Run(mojo::NullRemote()); @@ -164,16 +166,10 @@ const std::string& product_name, const std::string& serial_number, mojom::HidBusType bus_type) { - mojom::HidDeviceInfoPtr device = mojom::HidDeviceInfo::New(); - device->guid = base::GenerateGUID(); - device->physical_device_id = physical_device_id; - device->vendor_id = vendor_id; - device->product_id = product_id; - device->product_name = product_name; - device->serial_number = serial_number; - device->bus_type = bus_type; - AddDevice(device.Clone()); - return device; + return CreateAndAddDeviceWithTopLevelUsage( + physical_device_id, vendor_id, product_id, product_name, serial_number, + bus_type, /*usage_page=*/0xff00, + /*usage=*/0x0001); } mojom::HidDeviceInfoPtr FakeHidManager::CreateAndAddDeviceWithTopLevelUsage( @@ -185,7 +181,12 @@ mojom::HidBusType bus_type, uint16_t usage_page, uint16_t usage) { - mojom::HidDeviceInfoPtr device = mojom::HidDeviceInfo::New(); + auto collection = mojom::HidCollectionInfo::New(); + collection->usage = mojom::HidUsageAndPage::New(usage, usage_page); + collection->collection_type = mojom::kHIDCollectionTypeApplication; + collection->input_reports.push_back(mojom::HidReportDescription::New()); + + auto device = mojom::HidDeviceInfo::New(); device->guid = base::GenerateGUID(); device->physical_device_id = physical_device_id; device->vendor_id = vendor_id; @@ -193,16 +194,19 @@ device->product_name = product_name; device->serial_number = serial_number; device->bus_type = bus_type; - - std::vector<mojom::HidReportDescriptionPtr> input_reports; - std::vector<mojom::HidReportDescriptionPtr> output_reports; - std::vector<mojom::HidReportDescriptionPtr> feature_reports; - std::vector<mojom::HidCollectionInfoPtr> children; - device->collections.push_back(mojom::HidCollectionInfo::New( - mojom::HidUsageAndPage::New(usage, usage_page), std::vector<uint8_t>(), - mojom::kHIDCollectionTypeApplication, std::move(input_reports), - std::move(output_reports), std::move(feature_reports), - std::move(children))); + device->collections.push_back(std::move(collection)); + device->protected_input_report_ids = + HidBlocklist::Get().GetProtectedReportIds(HidBlocklist::kReportTypeInput, + vendor_id, product_id, + device->collections); + device->protected_output_report_ids = + HidBlocklist::Get().GetProtectedReportIds(HidBlocklist::kReportTypeOutput, + vendor_id, product_id, + device->collections); + device->protected_feature_report_ids = + HidBlocklist::Get().GetProtectedReportIds( + HidBlocklist::kReportTypeFeature, vendor_id, product_id, + device->collections); AddDevice(device.Clone()); return device; }
diff --git a/services/device/public/cpp/hid/fake_hid_manager.h b/services/device/public/cpp/hid/fake_hid_manager.h index 623efab4..ba17870 100644 --- a/services/device/public/cpp/hid/fake_hid_manager.h +++ b/services/device/public/cpp/hid/fake_hid_manager.h
@@ -66,6 +66,7 @@ const std::string& device_guid, mojo::PendingRemote<mojom::HidConnectionClient> connection_client, mojo::PendingRemote<mojom::HidConnectionWatcher> watcher, + bool allow_protected_reports, ConnectCallback callback) override; mojom::HidDeviceInfoPtr CreateAndAddDevice(
diff --git a/services/device/public/cpp/hid/hid_blocklist.cc b/services/device/public/cpp/hid/hid_blocklist.cc new file mode 100644 index 0000000..053dbc6c --- /dev/null +++ b/services/device/public/cpp/hid/hid_blocklist.cc
@@ -0,0 +1,357 @@ +// 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 "services/device/public/cpp/hid/hid_blocklist.h" + +#include "base/command_line.h" +#include "base/no_destructor.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_split.h" +#include "components/variations/variations_associated_data.h" +#include "services/device/public/cpp/hid/hid_switches.h" + +namespace device { + +namespace { + +#define VENDOR_PRODUCT_RULE(vid, pid) \ + { \ + true, (vid), true, (pid), false, 0, false, 0, false, 0, \ + HidBlocklist::ReportType::kReportTypeAny \ + } + +constexpr HidBlocklist::Entry kStaticEntries[] = { + // Block all top-level collections with the FIDO usage page. + {false, 0, false, 0, true, mojom::kPageFido, false, 0, false, 0, + HidBlocklist::ReportType::kReportTypeAny}, + + // KEY-ID + VENDOR_PRODUCT_RULE(0x096e, 0x0850), + // Feitian devices + VENDOR_PRODUCT_RULE(0x096e, 0x0852), + VENDOR_PRODUCT_RULE(0x096e, 0x0853), + VENDOR_PRODUCT_RULE(0x096e, 0x0854), + VENDOR_PRODUCT_RULE(0x096e, 0x0856), + VENDOR_PRODUCT_RULE(0x096e, 0x0858), + VENDOR_PRODUCT_RULE(0x096e, 0x085a), + VENDOR_PRODUCT_RULE(0x096e, 0x085b), + // HyperFIDO + VENDOR_PRODUCT_RULE(0x096e, 0x0880), + // HID Global BlueTrust Token + VENDOR_PRODUCT_RULE(0x09c3, 0x0023), + // Yubikey devices + VENDOR_PRODUCT_RULE(0x1050, 0x0010), + VENDOR_PRODUCT_RULE(0x1050, 0x0018), + VENDOR_PRODUCT_RULE(0x1050, 0x0030), + VENDOR_PRODUCT_RULE(0x1050, 0x0110), + VENDOR_PRODUCT_RULE(0x1050, 0x0111), + VENDOR_PRODUCT_RULE(0x1050, 0x0112), + VENDOR_PRODUCT_RULE(0x1050, 0x0113), + VENDOR_PRODUCT_RULE(0x1050, 0x0114), + VENDOR_PRODUCT_RULE(0x1050, 0x0115), + VENDOR_PRODUCT_RULE(0x1050, 0x0116), + VENDOR_PRODUCT_RULE(0x1050, 0x0120), + VENDOR_PRODUCT_RULE(0x1050, 0x0200), + VENDOR_PRODUCT_RULE(0x1050, 0x0211), + VENDOR_PRODUCT_RULE(0x1050, 0x0401), + VENDOR_PRODUCT_RULE(0x1050, 0x0402), + VENDOR_PRODUCT_RULE(0x1050, 0x0403), + VENDOR_PRODUCT_RULE(0x1050, 0x0404), + VENDOR_PRODUCT_RULE(0x1050, 0x0405), + VENDOR_PRODUCT_RULE(0x1050, 0x0406), + VENDOR_PRODUCT_RULE(0x1050, 0x0407), + VENDOR_PRODUCT_RULE(0x1050, 0x0410), + // U2F Zero + VENDOR_PRODUCT_RULE(0x10c4, 0x8acf), + // Mooltipass Mini-BLE + VENDOR_PRODUCT_RULE(0x1209, 0x4321), + // Mooltipass Arduino sketch + VENDOR_PRODUCT_RULE(0x1209, 0x4322), + // Titan + VENDOR_PRODUCT_RULE(0x18d1, 0x5026), + // VASCO + VENDOR_PRODUCT_RULE(0x1a44, 0x00bb), + // Keydo AES + VENDOR_PRODUCT_RULE(0x1e0d, 0xf1ae), + // Neowave Keydo + VENDOR_PRODUCT_RULE(0x1e0d, 0xf1d0), + // Thetis + VENDOR_PRODUCT_RULE(0x1ea8, 0xf025), + // Nitrokey + VENDOR_PRODUCT_RULE(0x20a0, 0x4287), + // JaCarta + VENDOR_PRODUCT_RULE(0x24dc, 0x0101), + // Happlink + VENDOR_PRODUCT_RULE(0x2581, 0xf1d0), + // Bluink + VENDOR_PRODUCT_RULE(0x2abe, 0x1002), + // Feitian USB, HyperFIDO + VENDOR_PRODUCT_RULE(0x2ccf, 0x0880), +}; + +bool IsValidBlocklistEntry(const HidBlocklist::Entry& entry) { + // An entry with a product ID parameter must also specify a vendor ID. + if (!entry.has_vendor_id && entry.has_product_id) + return false; + + // An entry with a usage ID parameter must also specify a usage page. + if (!entry.has_usage_page && entry.has_usage) + return false; + + return true; +} + +const std::vector<mojom::HidReportDescriptionPtr>& GetReportsForType( + HidBlocklist::ReportType report_type, + const mojom::HidCollectionInfo& collection) { + switch (report_type) { + case HidBlocklist::kReportTypeInput: + return collection.input_reports; + case HidBlocklist::kReportTypeOutput: + return collection.output_reports; + case HidBlocklist::kReportTypeFeature: + return collection.feature_reports; + case HidBlocklist::kReportTypeAny: + NOTREACHED(); + return collection.input_reports; + } +} + +// Iterates over |collections| to find reports of type |report_type| that should +// be protected according to the blocklist rule |entry|. |vendor_id| and +// |product_id| are the vendor and product IDs of the device with these reports. +// The report IDs of the protected reports are inserted into |protected_ids|. +void CheckBlocklistEntry( + const HidBlocklist::Entry& entry, + HidBlocklist::ReportType report_type, + uint16_t vendor_id, + uint16_t product_id, + const std::vector<mojom::HidCollectionInfoPtr>& collections, + std::set<uint8_t>& protected_ids) { + DCHECK_NE(report_type, HidBlocklist::kReportTypeAny); + if (entry.report_type != HidBlocklist::kReportTypeAny && + entry.report_type != report_type) { + return; + } + + if (entry.has_vendor_id) { + if (entry.vendor_id != vendor_id) + return; + + if (entry.has_product_id && entry.product_id != product_id) + return; + } + + for (const auto& collection : collections) { + if (entry.has_usage_page) { + if (entry.usage_page != collection->usage->usage_page) + continue; + + if (entry.has_usage && entry.usage != collection->usage->usage) + continue; + } + + const auto& reports = GetReportsForType(report_type, *collection); + for (const auto& report : reports) { + if (!entry.has_report_id || entry.report_id == report->report_id) + protected_ids.insert(report->report_id); + } + } +} + +// Returns true if the passed string is exactly |digits| digits long and only +// contains valid hexadecimal characters (no leading 0x). +bool IsHexComponent(base::StringPiece string, size_t digits) { + if (string.length() != digits) + return false; + + // This is necessary because base::HexStringToUInt allows whitespace and the + // "0x" prefix in its input. + for (char c : string) { + if (c >= '0' && c <= '9') + continue; + if (c >= 'a' && c <= 'f') + continue; + if (c >= 'A' && c <= 'F') + continue; + return false; + } + return true; +} + +// Returns true if the passed string is "I" (input report), "O" (output report), +// "F" (feature report), or "" (any report type). +bool IsReportTypeComponent(base::StringPiece string) { + return string.empty() || + (string.length() == 1 && + (string[0] == 'I' || string[0] == 'O' || string[0] == 'F')); +} + +} // namespace + +// static +HidBlocklist& HidBlocklist::Get() { + static base::NoDestructor<HidBlocklist> instance; + return *instance; +} + +// static +bool HidBlocklist::IsDeviceExcluded(const mojom::HidDeviceInfo& device_info) { + // A device should only be excluded if all its reports are protected. + for (const auto& collection : device_info.collections) { + if (device_info.protected_input_report_ids) { + for (const auto& report : collection->input_reports) { + if (!base::Contains(*device_info.protected_input_report_ids, + report->report_id)) { + return false; + } + } + } else if (!collection->input_reports.empty()) { + return false; + } + if (device_info.protected_output_report_ids) { + for (const auto& report : collection->output_reports) { + if (!base::Contains(*device_info.protected_output_report_ids, + report->report_id)) { + return false; + } + } + } else if (!collection->output_reports.empty()) { + return false; + } + if (device_info.protected_feature_report_ids) { + for (const auto& report : collection->feature_reports) { + if (!base::Contains(*device_info.protected_feature_report_ids, + report->report_id)) { + return false; + } + } + } else if (!collection->feature_reports.empty()) { + return false; + } + } + return true; +} + +std::vector<uint8_t> HidBlocklist::GetProtectedReportIds( + HidBlocklist::ReportType report_type, + uint16_t vendor_id, + uint16_t product_id, + const std::vector<mojom::HidCollectionInfoPtr>& collections) { + DCHECK_NE(report_type, ReportType::kReportTypeAny); + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisableHidBlocklist)) { + return {}; + } + + std::set<uint8_t> protected_ids; + for (const auto& entry : kStaticEntries) { + CheckBlocklistEntry(entry, report_type, vendor_id, product_id, collections, + protected_ids); + } + for (const auto& entry : dynamic_entries_) { + CheckBlocklistEntry(entry, report_type, vendor_id, product_id, collections, + protected_ids); + } + return std::vector<uint8_t>(protected_ids.begin(), protected_ids.end()); +} + +void HidBlocklist::PopulateWithServerProvidedValues() { + std::string blocklist_string = variations::GetVariationParamValue( + "WebHIDBlocklist", "blocklist_additions"); + DLOG(WARNING) << "HID blocklist additions: " << blocklist_string; + for (const auto& blocklist_rule : + base::SplitStringPiece(blocklist_string, ",", base::TRIM_WHITESPACE, + base::SPLIT_WANT_NONEMPTY)) { + std::vector<base::StringPiece> components = base::SplitStringPiece( + blocklist_rule, ":", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); + if (components.size() != 6) { + DLOG(WARNING) << "Wrong number of components in HID blocklist rule: " + << blocklist_rule; + continue; + } + + // The vendor ID, product ID, usage page, and usage must be specified as + // either an empty string or a 16-bit hexadecimal value. + if ((!components[0].empty() && !IsHexComponent(components[0], 4)) || + (!components[1].empty() && !IsHexComponent(components[1], 4)) || + (!components[2].empty() && !IsHexComponent(components[2], 4)) || + (!components[3].empty() && !IsHexComponent(components[3], 4))) { + DLOG(WARNING) << "Bad component format in HID blocklist rule: " + << blocklist_rule; + continue; + } + + // The report ID must be specified as either an empty string or an 8-bit + // hexadecimal value. + if (!components[4].empty() && !IsHexComponent(components[4], 2)) { + DLOG(WARNING) << "Bad component format in HID blocklist rule: " + << blocklist_rule; + continue; + } + + // The report type must be specified as either an empty string or a single + // character 'I', 'O', or 'F'. + if (!components[5].empty() && !IsReportTypeComponent(components[5])) { + DLOG(WARNING) << "Bad component format in HID blocklist rule: " + << blocklist_rule; + continue; + } + + Entry entry = {}; + uint32_t int_value; + if (!components[0].empty()) { + base::HexStringToUInt(components[0], &int_value); + entry.has_vendor_id = true; + entry.vendor_id = static_cast<uint16_t>(int_value); + } + if (!components[1].empty()) { + base::HexStringToUInt(components[1], &int_value); + entry.has_product_id = true; + entry.product_id = static_cast<uint16_t>(int_value); + } + if (!components[2].empty()) { + base::HexStringToUInt(components[2], &int_value); + entry.has_usage_page = true; + entry.usage_page = static_cast<uint16_t>(int_value); + } + if (!components[3].empty()) { + base::HexStringToUInt(components[3], &int_value); + entry.has_usage = true; + entry.usage = static_cast<uint16_t>(int_value); + } + if (!components[4].empty()) { + base::HexStringToUInt(components[4], &int_value); + entry.has_report_id = true; + entry.report_id = static_cast<uint16_t>(int_value); + } + if (components[5] == "I") + entry.report_type = HidBlocklist::kReportTypeInput; + else if (components[5] == "O") + entry.report_type = HidBlocklist::kReportTypeOutput; + else if (components[5] == "F") + entry.report_type = HidBlocklist::kReportTypeFeature; + + if (!IsValidBlocklistEntry(entry)) { + DLOG(WARNING) << "Ivalid HID blocklist rule: " << blocklist_rule; + continue; + } + + dynamic_entries_.push_back(entry); + } +} + +void HidBlocklist::ResetToDefaultValuesForTest() { + dynamic_entries_.clear(); + PopulateWithServerProvidedValues(); +} + +HidBlocklist::HidBlocklist() { +#if DCHECK_IS_ON() + for (const auto& entry : kStaticEntries) + DCHECK(IsValidBlocklistEntry(entry)); +#endif +} + +} // namespace device
diff --git a/services/device/public/cpp/hid/hid_blocklist.h b/services/device/public/cpp/hid/hid_blocklist.h new file mode 100644 index 0000000..e0d145f --- /dev/null +++ b/services/device/public/cpp/hid/hid_blocklist.h
@@ -0,0 +1,103 @@ +// 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 SERVICES_DEVICE_PUBLIC_CPP_HID_HID_BLOCKLIST_H_ +#define SERVICES_DEVICE_PUBLIC_CPP_HID_HID_BLOCKLIST_H_ + +#include "services/device/public/mojom/hid.mojom.h" + +namespace base { +template <typename T> +class NoDestructor; +} // namespace base + +namespace device { + +class HidBlocklist final { + public: + enum ReportType { + kReportTypeAny = 0, + kReportTypeInput, + kReportTypeOutput, + kReportTypeFeature, + }; + + struct Entry { + bool has_vendor_id; + uint16_t vendor_id; + + bool has_product_id; + uint16_t product_id; + + bool has_usage_page; + uint16_t usage_page; + + bool has_usage; + uint16_t usage; + + bool has_report_id; + uint8_t report_id; + + ReportType report_type; + }; + + HidBlocklist(const HidBlocklist&) = delete; + HidBlocklist& operator=(const HidBlocklist&) = delete; + ~HidBlocklist(); + + // Returns a singleton instance of the blocklist. + static HidBlocklist& Get(); + + // Returns true if a device is excluded from access. A device is excluded if + // all of its reports are blocked. + static bool IsDeviceExcluded(const device::mojom::HidDeviceInfo& device_info); + + // Given the |vendor_id|, |product_id|, and |collections| for a HID device, + // returns a vector of protected report IDs for reports of type |report_type|. + std::vector<uint8_t> GetProtectedReportIds( + ReportType report_type, + uint16_t vendor_id, + uint16_t product_id, + const std::vector<mojom::HidCollectionInfoPtr>& collections); + + // Returns the number of dynamic blocklist entries. + size_t GetDynamicEntryCountForTest() const { return dynamic_entries_.size(); } + + // Reloads the blocklist for testing purposes. + void ResetToDefaultValuesForTest(); + + private: + // Friend NoDestructor to permit access to the private constructor. + friend class base::NoDestructor<HidBlocklist>; + + HidBlocklist(); + + // Populates the blocklist with values set via a Finch experiment which allows + // the set of blocked devices to be updated without shipping new executable + // versions. + // + // The variation string must be a comma-separated list of blocklist rules, + // where each rule is composed of six properties of the form: + // + // vendor_id:product_id:usage_page:usage:report_id:report_type + // + // Each property may be empty, indicating that the rule should match any value + // for that property. When vendor_id, product_id, usage_page, or usage are + // specified, they must be a 16-bit integer written as exactly 4 hexadecimal + // digits. When report_id is specified, it must be an 8-bit integer written as + // exactly 2 hexadecimal digits. When report_type is specified, it must be a + // single character I, O, or F. + // + // Invalid entries in the comma-separated list will be ignored. + // + // Example: + // "::f1d0:::, 1234:5678::::, abcd:0001:::01:I" + void PopulateWithServerProvidedValues(); + + std::vector<Entry> dynamic_entries_; +}; + +} // namespace device + +#endif // SERVICES_DEVICE_PUBLIC_CPP_HID_HID_BLOCKLIST_H_
diff --git a/services/device/public/cpp/hid/hid_blocklist_unittest.cc b/services/device/public/cpp/hid/hid_blocklist_unittest.cc new file mode 100644 index 0000000..46bae57b --- /dev/null +++ b/services/device/public/cpp/hid/hid_blocklist_unittest.cc
@@ -0,0 +1,417 @@ +// 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 "services/device/public/cpp/hid/hid_blocklist.h" + +#include "base/command_line.h" +#include "base/guid.h" +#include "components/variations/variations_params_manager.h" +#include "services/device/public/cpp/hid/hid_switches.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace device { + +namespace { + +using ::testing::ElementsAre; + +constexpr uint16_t kTestVendorId = 0x1234; +constexpr uint16_t kTestProductId = 0x0001; +constexpr uint16_t kTestUsagePage = 0xff00; +constexpr uint16_t kTestUsage = 0x0001; +constexpr uint8_t kNoReportId = 0x00; +constexpr uint8_t kTestReportId = 0x01; + +class HidBlocklistTest : public testing::Test { + public: + HidBlocklistTest() : blocklist_(HidBlocklist::Get()) {} + + const HidBlocklist& list() { return blocklist_; } + + void SetDynamicBlocklist(base::StringPiece list) { + params_manager_.ClearAllVariationParams(); + + std::map<std::string, std::string> params; + params["blocklist_additions"] = list.as_string(); + params_manager_.SetVariationParams("WebHIDBlocklist", params); + + blocklist_.ResetToDefaultValuesForTest(); + } + + mojom::HidDeviceInfoPtr CreateTestDeviceWithOneReport( + uint16_t vendor_id, + uint16_t product_id, + uint16_t usage_page, + uint16_t usage, + uint8_t report_id, + HidBlocklist::ReportType report_type) { + const bool has_report_id = (report_id != 0); + auto report = mojom::HidReportDescription::New(); + report->report_id = report_id; + + auto collection = mojom::HidCollectionInfo::New(); + collection->usage = mojom::HidUsageAndPage::New(usage, usage_page); + collection->collection_type = mojom::kHIDCollectionTypeApplication; + if (has_report_id) + collection->report_ids.push_back(report_id); + if (report_type == HidBlocklist::kReportTypeInput) + collection->input_reports.push_back(std::move(report)); + else if (report_type == HidBlocklist::kReportTypeOutput) + collection->output_reports.push_back(std::move(report)); + else if (report_type == HidBlocklist::kReportTypeFeature) + collection->feature_reports.push_back(std::move(report)); + + auto device = mojom::HidDeviceInfo::New(); + device->guid = base::GenerateGUID(); + device->vendor_id = vendor_id; + device->product_id = product_id; + device->has_report_id = has_report_id; + device->collections.push_back(std::move(collection)); + device->protected_input_report_ids = blocklist_.GetProtectedReportIds( + HidBlocklist::kReportTypeInput, vendor_id, product_id, + device->collections); + device->protected_output_report_ids = blocklist_.GetProtectedReportIds( + HidBlocklist::kReportTypeOutput, vendor_id, product_id, + device->collections); + device->protected_feature_report_ids = blocklist_.GetProtectedReportIds( + HidBlocklist::kReportTypeFeature, vendor_id, product_id, + device->collections); + return device; + } + + // Returns a mojom::HidDeviceInfoPtr with |num_collections| collections, each + // with different usage pages and report IDs. Each collection contains one + // report of each type (input/output/feature). + mojom::HidDeviceInfoPtr CreateTestDeviceWithMultipleCollections( + uint16_t vendor_id, + uint16_t product_id, + size_t num_collections) { + std::vector<mojom::HidCollectionInfoPtr> collections; + uint16_t usage_page = kTestUsagePage; + uint8_t report_id = kTestReportId; + for (size_t i = 0; i < num_collections; ++i) { + auto collection = mojom::HidCollectionInfo::New(); + collection->usage = mojom::HidUsageAndPage::New(kTestUsage, usage_page++); + collection->collection_type = mojom::kHIDCollectionTypeApplication; + + auto input_report = mojom::HidReportDescription::New(); + collection->report_ids.push_back(report_id); + input_report->report_id = report_id++; + collection->input_reports.push_back(std::move(input_report)); + + auto output_report = mojom::HidReportDescription::New(); + collection->report_ids.push_back(report_id); + output_report->report_id = report_id++; + collection->output_reports.push_back(std::move(output_report)); + + auto feature_report = mojom::HidReportDescription::New(); + collection->report_ids.push_back(report_id); + feature_report->report_id = report_id++; + collection->feature_reports.push_back(std::move(feature_report)); + + collections.push_back(std::move(collection)); + } + + auto device = mojom::HidDeviceInfo::New(); + device->guid = base::GenerateGUID(); + device->vendor_id = vendor_id; + device->product_id = product_id; + device->has_report_id = true; + device->collections = std::move(collections); + device->protected_input_report_ids = blocklist_.GetProtectedReportIds( + HidBlocklist::kReportTypeInput, vendor_id, product_id, + device->collections); + device->protected_output_report_ids = blocklist_.GetProtectedReportIds( + HidBlocklist::kReportTypeOutput, vendor_id, product_id, + device->collections); + device->protected_feature_report_ids = blocklist_.GetProtectedReportIds( + HidBlocklist::kReportTypeFeature, vendor_id, product_id, + device->collections); + return device; + } + + private: + void TearDown() override { + // Because HidBlocklist is a singleton it must be cleared after tests run + // to prevent leakage between tests. + params_manager_.ClearAllVariationParams(); + blocklist_.ResetToDefaultValuesForTest(); + } + + variations::testing::VariationParamsManager params_manager_; + HidBlocklist& blocklist_; +}; + +} // namespace + +TEST_F(HidBlocklistTest, StringsWithNoValidEntries) { + SetDynamicBlocklist(""); + EXPECT_EQ(0u, list().GetDynamicEntryCountForTest()); + + SetDynamicBlocklist("~!@#$%^&*()-_=+[]{}/*-"); + EXPECT_EQ(0u, list().GetDynamicEntryCountForTest()); + + SetDynamicBlocklist(":"); + EXPECT_EQ(0u, list().GetDynamicEntryCountForTest()); + + SetDynamicBlocklist(","); + EXPECT_EQ(0u, list().GetDynamicEntryCountForTest()); + + SetDynamicBlocklist(",,"); + EXPECT_EQ(0u, list().GetDynamicEntryCountForTest()); + + SetDynamicBlocklist("1:2:3:4:5:I"); + EXPECT_EQ(0u, list().GetDynamicEntryCountForTest()); + + SetDynamicBlocklist("18d1:2:ff00:::"); + EXPECT_EQ(0u, list().GetDynamicEntryCountForTest()); + + SetDynamicBlocklist("0x18d1:0x0000::::"); + EXPECT_EQ(0u, list().GetDynamicEntryCountForTest()); + + SetDynamicBlocklist("0000: 0::::"); + EXPECT_EQ(0u, list().GetDynamicEntryCountForTest()); + + SetDynamicBlocklist("000g:0000::::"); + EXPECT_EQ(0u, list().GetDynamicEntryCountForTest()); + + SetDynamicBlocklist("::::255:I"); + EXPECT_EQ(0u, list().GetDynamicEntryCountForTest()); + + SetDynamicBlocklist("::::0xff:I"); + EXPECT_EQ(0u, list().GetDynamicEntryCountForTest()); + + SetDynamicBlocklist(":::::i"); + EXPECT_EQ(0u, list().GetDynamicEntryCountForTest()); + + SetDynamicBlocklist(":::::o"); + EXPECT_EQ(0u, list().GetDynamicEntryCountForTest()); + + SetDynamicBlocklist(":::::f"); + EXPECT_EQ(0u, list().GetDynamicEntryCountForTest()); + + SetDynamicBlocklist(":::::A"); + EXPECT_EQ(0u, list().GetDynamicEntryCountForTest()); + + SetDynamicBlocklist("☯"); + EXPECT_EQ(0u, list().GetDynamicEntryCountForTest()); +} + +TEST_F(HidBlocklistTest, UnexcludedDevice) { + auto device = CreateTestDeviceWithOneReport( + kTestVendorId, kTestProductId, kTestUsagePage, kTestUsage, kTestReportId, + HidBlocklist::kReportTypeInput); + EXPECT_FALSE(HidBlocklist::IsDeviceExcluded(*device)); + EXPECT_TRUE(device->protected_input_report_ids->empty()); + EXPECT_TRUE(device->protected_output_report_ids->empty()); + EXPECT_TRUE(device->protected_feature_report_ids->empty()); +} + +TEST_F(HidBlocklistTest, VendorRule) { + // Exclude all devices with matching vendor ID. + SetDynamicBlocklist("1234:::::"); + + // A device with matching vendor IDs is excluded. + auto device1 = CreateTestDeviceWithOneReport( + kTestVendorId, kTestProductId, kTestUsagePage, kTestUsage, kTestReportId, + HidBlocklist::kReportTypeInput); + EXPECT_TRUE(HidBlocklist::IsDeviceExcluded(*device1)); + EXPECT_THAT(*device1->protected_input_report_ids, ElementsAre(kTestReportId)); + + // A device with a different vendor ID is not excluded. + auto device2 = CreateTestDeviceWithOneReport( + kTestVendorId + 1, kTestProductId, kTestUsagePage, kTestUsage, + kTestReportId, HidBlocklist::kReportTypeInput); + EXPECT_FALSE(HidBlocklist::IsDeviceExcluded(*device2)); + EXPECT_TRUE(device2->protected_input_report_ids->empty()); +} + +TEST_F(HidBlocklistTest, VendorProductRule) { + // Exclude devices with matching vendor and product ID. + SetDynamicBlocklist("1234:0001::::"); + + // A device with matching vendor/product IDs is excluded. + auto device1 = CreateTestDeviceWithOneReport( + kTestVendorId, kTestProductId, kTestUsagePage, kTestUsage, kTestReportId, + HidBlocklist::kReportTypeInput); + EXPECT_TRUE(HidBlocklist::IsDeviceExcluded(*device1)); + EXPECT_THAT(*device1->protected_input_report_ids, ElementsAre(kTestReportId)); + + // A device with matching vendor ID but different product ID is not excluded. + auto device2 = CreateTestDeviceWithOneReport( + kTestVendorId, kTestProductId + 1, kTestUsagePage, kTestUsage, + kTestReportId, HidBlocklist::kReportTypeInput); + EXPECT_FALSE(HidBlocklist::IsDeviceExcluded(*device2)); + EXPECT_TRUE(device2->protected_input_report_ids->empty()); +} + +TEST_F(HidBlocklistTest, ExcludedDeviceAllowedWithFlag) { + base::CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kDisableHidBlocklist); + + // Exclude devices with matching vendor and product ID. + SetDynamicBlocklist("1234:0001::::"); + + // A device with matching vendor/product IDs is not excluded because the + // blocklist is disabled. + auto device = CreateTestDeviceWithOneReport( + kTestVendorId, kTestProductId, kTestUsagePage, kTestUsage, kTestReportId, + HidBlocklist::kReportTypeInput); + EXPECT_FALSE(HidBlocklist::IsDeviceExcluded(*device)); + EXPECT_TRUE(device->protected_input_report_ids->empty()); + EXPECT_TRUE(device->protected_output_report_ids->empty()); + EXPECT_TRUE(device->protected_feature_report_ids->empty()); +} + +TEST_F(HidBlocklistTest, ProductRuleWithoutVendorDoesNothing) { + // Add an invalid rule with a product ID but no vendor ID. + SetDynamicBlocklist(":0001::::"); + + // A device with matching product ID is not excluded. + auto device = CreateTestDeviceWithOneReport( + kTestVendorId, kTestProductId, kTestUsagePage, kTestUsage, kTestReportId, + HidBlocklist::kReportTypeInput); + EXPECT_FALSE(HidBlocklist::IsDeviceExcluded(*device)); + EXPECT_TRUE(device->protected_input_report_ids->empty()); +} + +TEST_F(HidBlocklistTest, UsagePageRule) { + // Protect reports by the usage page of the top-level collection. + SetDynamicBlocklist("::ff00:::"); + + // A device with matching usage page is excluded. + auto device1 = CreateTestDeviceWithOneReport( + kTestVendorId, kTestProductId, kTestUsagePage, kTestUsage, kTestReportId, + HidBlocklist::kReportTypeInput); + EXPECT_TRUE(HidBlocklist::IsDeviceExcluded(*device1)); + EXPECT_THAT(*device1->protected_input_report_ids, ElementsAre(kTestReportId)); + + // A device with a different usage page is not excluded. + auto device2 = CreateTestDeviceWithOneReport( + kTestVendorId, kTestProductId, kTestUsagePage + 1, kTestUsage, + kTestReportId, HidBlocklist::kReportTypeInput); + EXPECT_FALSE(HidBlocklist::IsDeviceExcluded(*device2)); + EXPECT_TRUE(device2->protected_input_report_ids->empty()); +} + +TEST_F(HidBlocklistTest, UsagePageAndUsageRule) { + // Protect reports by the usage page and usage ID of the top-level collection. + SetDynamicBlocklist("::ff00:0001::"); + + // A device with matching usage page is excluded. + auto device1 = CreateTestDeviceWithOneReport( + kTestVendorId, kTestProductId, kTestUsagePage, kTestUsage, kTestReportId, + HidBlocklist::kReportTypeInput); + EXPECT_TRUE(HidBlocklist::IsDeviceExcluded(*device1)); + EXPECT_THAT(*device1->protected_input_report_ids, ElementsAre(kTestReportId)); + + // A device with a different usage page is not excluded. + auto device2 = CreateTestDeviceWithOneReport( + kTestVendorId, kTestProductId, kTestUsagePage, kTestUsage + 1, + kTestReportId, HidBlocklist::kReportTypeInput); + EXPECT_FALSE(HidBlocklist::IsDeviceExcluded(*device2)); + EXPECT_TRUE(device2->protected_input_report_ids->empty()); +} + +TEST_F(HidBlocklistTest, UsageRuleWithoutUsagePageDoesNothing) { + // Add an invalid rule with a usage ID but no usage page. + SetDynamicBlocklist(":::0001::"); + + // A device with matching usage ID is not excluded. + auto device = CreateTestDeviceWithOneReport( + kTestVendorId, kTestProductId, kTestUsagePage, kTestUsage, kTestReportId, + HidBlocklist::kReportTypeInput); + EXPECT_FALSE(HidBlocklist::IsDeviceExcluded(*device)); + EXPECT_TRUE(device->protected_input_report_ids->empty()); +} + +TEST_F(HidBlocklistTest, NonZeroReportIdRule) { + // Protect reports by report ID. + SetDynamicBlocklist("::::01:"); + + // A device with matching report ID is excluded. + auto device1 = CreateTestDeviceWithOneReport( + kTestVendorId, kTestProductId, kTestUsagePage, kTestUsage, kTestReportId, + HidBlocklist::kReportTypeInput); + EXPECT_TRUE(HidBlocklist::IsDeviceExcluded(*device1)); + EXPECT_THAT(*device1->protected_input_report_ids, ElementsAre(kTestReportId)); + + // A device with a different report ID is not excluded. + auto device2 = CreateTestDeviceWithOneReport( + kTestVendorId, kTestProductId, kTestUsagePage, kTestUsage, + kTestReportId + 1, HidBlocklist::kReportTypeInput); + EXPECT_FALSE(HidBlocklist::IsDeviceExcluded(*device2)); + EXPECT_TRUE(device2->protected_input_report_ids->empty()); +} + +TEST_F(HidBlocklistTest, ZeroReportIdRule) { + // Protect reports from devices that do not use report IDs. + SetDynamicBlocklist("::::00:"); + + // A device that does not use report IDs is excluded. + auto device1 = CreateTestDeviceWithOneReport( + kTestVendorId, kTestProductId, kTestUsagePage, kTestUsage, kNoReportId, + HidBlocklist::kReportTypeInput); + EXPECT_TRUE(HidBlocklist::IsDeviceExcluded(*device1)); + EXPECT_THAT(*device1->protected_input_report_ids, ElementsAre(kNoReportId)); + + // A device that uses report IDs is not excluded. + auto device2 = CreateTestDeviceWithOneReport( + kTestVendorId, kTestProductId, kTestUsagePage, kTestUsage, kTestReportId, + HidBlocklist::kReportTypeInput); + EXPECT_FALSE(HidBlocklist::IsDeviceExcluded(*device2)); + EXPECT_TRUE(device2->protected_input_report_ids->empty()); +} + +TEST_F(HidBlocklistTest, ReportTypeRule) { + // Protect reports by report type. + SetDynamicBlocklist(":::::I"); + + // A device with only an input report is excluded. + auto device1 = CreateTestDeviceWithOneReport( + kTestVendorId, kTestProductId, kTestUsagePage, kTestUsage, kTestReportId, + HidBlocklist::kReportTypeInput); + EXPECT_TRUE(HidBlocklist::IsDeviceExcluded(*device1)); + EXPECT_THAT(*device1->protected_input_report_ids, ElementsAre(kTestReportId)); + + // A device with an output report is not excluded. + auto device2 = CreateTestDeviceWithOneReport( + kTestVendorId, kTestProductId, kTestUsagePage, kTestUsage, kTestReportId, + HidBlocklist::kReportTypeOutput); + EXPECT_FALSE(HidBlocklist::IsDeviceExcluded(*device2)); + EXPECT_TRUE(device2->protected_output_report_ids->empty()); +} + +TEST_F(HidBlocklistTest, DeviceWithAnyUnprotectedReportsNotExcluded) { + // Protect input report 0x01. + SetDynamicBlocklist("::::01:I"); + + // Create a device with six reports divided into two collections. One of the + // reports matches the blocklist entry and should be protected, but the other + // reports should not be protected. + auto device = + CreateTestDeviceWithMultipleCollections(kTestVendorId, kTestProductId, 2); + EXPECT_FALSE(HidBlocklist::IsDeviceExcluded(*device)); + EXPECT_THAT(*device->protected_input_report_ids, ElementsAre(0x01)); + EXPECT_TRUE(device->protected_output_report_ids->empty()); + EXPECT_TRUE(device->protected_feature_report_ids->empty()); +} + +TEST_F(HidBlocklistTest, DeviceWithAllProtectedReportsIsExcluded) { + // Protect six reports by report ID and report type. + SetDynamicBlocklist( + "::::01:I, ::::02:O, ::::03:F, ::::04:I, ::::05:O, ::::06:F"); + + // Create a device with six reports divided into two collections. All of the + // reports match the above blocklist rules and should be protected. + auto device = + CreateTestDeviceWithMultipleCollections(kTestVendorId, kTestProductId, 2); + EXPECT_TRUE(HidBlocklist::IsDeviceExcluded(*device)); + EXPECT_THAT(*device->protected_input_report_ids, ElementsAre(0x01, 0x04)); + EXPECT_THAT(*device->protected_output_report_ids, ElementsAre(0x02, 0x05)); + EXPECT_THAT(*device->protected_feature_report_ids, ElementsAre(0x03, 0x06)); +} + +} // namespace device
diff --git a/services/device/public/cpp/hid/hid_report_descriptor_unittest.cc b/services/device/public/cpp/hid/hid_report_descriptor_unittest.cc index 766aac13..f2d3d61 100644 --- a/services/device/public/cpp/hid/hid_report_descriptor_unittest.cc +++ b/services/device/public/cpp/hid/hid_report_descriptor_unittest.cc
@@ -458,7 +458,7 @@ TEST_F(HidReportDescriptorTest, ValidateDetails_Digitizer) { auto digitizer = HidCollectionInfo::New(); digitizer->usage = HidUsageAndPage::New(0x01, mojom::kPageDigitizer); - ASSERT_EQ(IsProtected(*digitizer->usage), false); + ASSERT_FALSE(IsAlwaysProtected(*digitizer->usage)); digitizer->report_ids = {0x01, 0x02, 0x03}; AddTopCollectionInfo(std::move(digitizer)); ValidateDetails(true, 6, 0, 0, kDigitizer, kDigitizerSize); @@ -527,7 +527,7 @@ auto keyboard = HidCollectionInfo::New(); keyboard->usage = HidUsageAndPage::New(mojom::kGenericDesktopKeyboard, mojom::kPageGenericDesktop); - ASSERT_EQ(IsProtected(*keyboard->usage), true); + ASSERT_TRUE(IsAlwaysProtected(*keyboard->usage)); AddTopCollectionInfo(std::move(keyboard)); ValidateDetails(false, 8, 1, 0, kKeyboard, kKeyboardSize); } @@ -556,7 +556,7 @@ TEST_F(HidReportDescriptorTest, ValidateDetails_Monitor) { auto monitor = HidCollectionInfo::New(); monitor->usage = HidUsageAndPage::New(0x01, mojom::kPageMonitor0); - ASSERT_EQ(IsProtected(*monitor->usage), false); + ASSERT_FALSE(IsAlwaysProtected(*monitor->usage)); monitor->report_ids = {0x01, 0x02, 0x03, 0x04, 0x05}; AddTopCollectionInfo(std::move(monitor)); ValidateDetails(true, 0, 0, 243, kMonitor, kMonitorSize); @@ -600,7 +600,7 @@ auto mouse = HidCollectionInfo::New(); mouse->usage = HidUsageAndPage::New(mojom::kGenericDesktopMouse, mojom::kPageGenericDesktop); - ASSERT_EQ(IsProtected(*mouse->usage), true); + ASSERT_TRUE(IsAlwaysProtected(*mouse->usage)); AddTopCollectionInfo(std::move(mouse)); ValidateDetails(false, 3, 0, 0, kMouse, kMouseSize); } @@ -626,15 +626,15 @@ TEST_F(HidReportDescriptorTest, ValidateDetails_LogitechUnifyingReceiver) { auto hidpp_short = HidCollectionInfo::New(); hidpp_short->usage = HidUsageAndPage::New(0x01, mojom::kPageVendor); - ASSERT_EQ(IsProtected(*hidpp_short->usage), false); + ASSERT_FALSE(IsAlwaysProtected(*hidpp_short->usage)); hidpp_short->report_ids = {0x10}; auto hidpp_long = HidCollectionInfo::New(); hidpp_long->usage = HidUsageAndPage::New(0x02, mojom::kPageVendor); - ASSERT_EQ(IsProtected(*hidpp_long->usage), false); + ASSERT_FALSE(IsAlwaysProtected(*hidpp_long->usage)); hidpp_long->report_ids = {0x11}; auto hidpp_dj = HidCollectionInfo::New(); hidpp_dj->usage = HidUsageAndPage::New(0x04, mojom::kPageVendor); - ASSERT_EQ(IsProtected(*hidpp_dj->usage), false); + ASSERT_FALSE(IsAlwaysProtected(*hidpp_dj->usage)); hidpp_dj->report_ids = {0x20, 0x21}; AddTopCollectionInfo(std::move(hidpp_short)); AddTopCollectionInfo(std::move(hidpp_long)); @@ -682,7 +682,7 @@ auto top_info = HidCollectionInfo::New(); top_info->usage = HidUsageAndPage::New(mojom::kGenericDesktopJoystick, mojom::kPageGenericDesktop); - ASSERT_EQ(IsProtected(*top_info->usage), false); + ASSERT_FALSE(IsAlwaysProtected(*top_info->usage)); top_info->report_ids = {0x01, 0x02, 0xee, 0xef}; AddTopCollectionInfo(std::move(top_info)); ValidateDetails(true, 48, 48, 48, kSonyDualshock3, kSonyDualshock3Size); @@ -739,7 +739,7 @@ auto top_info = HidCollectionInfo::New(); top_info->usage = HidUsageAndPage::New(mojom::kGenericDesktopGamePad, mojom::kPageGenericDesktop); - ASSERT_EQ(IsProtected(*top_info->usage), false); + ASSERT_FALSE(IsAlwaysProtected(*top_info->usage)); top_info->report_ids = {0x01, 0x05, 0x04, 0x02, 0x08, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x90, 0x91, 0x92, 0x93, 0xa0, 0xa1, @@ -907,7 +907,7 @@ auto top_info = HidCollectionInfo::New(); top_info->usage = HidUsageAndPage::New(mojom::kGenericDesktopGamePad, mojom::kPageGenericDesktop); - ASSERT_EQ(IsProtected(*top_info->usage), false); + ASSERT_FALSE(IsAlwaysProtected(*top_info->usage)); top_info->report_ids = {0x01, 0x02, 0x03, 0x04}; AddTopCollectionInfo(std::move(top_info)); ValidateDetails(true, 15, 8, 0, kMicrosoftXboxWirelessController, @@ -1000,7 +1000,7 @@ auto top_info = HidCollectionInfo::New(); top_info->usage = HidUsageAndPage::New(mojom::kGenericDesktopJoystick, mojom::kPageGenericDesktop); - ASSERT_EQ(IsProtected(*top_info->usage), false); + ASSERT_FALSE(IsAlwaysProtected(*top_info->usage)); top_info->report_ids = {0x30, 0x21, 0x81, 0x01, 0x10, 0x80, 0x82}; AddTopCollectionInfo(std::move(top_info)); ValidateDetails(true, 63, 63, 0, kNintendoSwitchProController, @@ -1061,13 +1061,13 @@ auto gamepad_info = HidCollectionInfo::New(); gamepad_info->usage = HidUsageAndPage::New(mojom::kGenericDesktopGamePad, mojom::kPageGenericDesktop); - ASSERT_EQ(IsProtected(*gamepad_info->usage), false); + ASSERT_FALSE(IsAlwaysProtected(*gamepad_info->usage)); gamepad_info->report_ids = {0x01, 0x02, 0x03, 0x04, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b}; auto keyboard_info = HidCollectionInfo::New(); keyboard_info->usage = HidUsageAndPage::New(mojom::kGenericDesktopKeyboard, mojom::kPageGenericDesktop); - ASSERT_EQ(IsProtected(*keyboard_info->usage), true); + ASSERT_TRUE(IsAlwaysProtected(*keyboard_info->usage)); keyboard_info->report_ids = {0x05}; AddTopCollectionInfo(std::move(gamepad_info)); AddTopCollectionInfo(std::move(keyboard_info)); @@ -1356,12 +1356,12 @@ auto gamepad_info = HidCollectionInfo::New(); gamepad_info->usage = HidUsageAndPage::New(mojom::kGenericDesktopGamePad, mojom::kPageGenericDesktop); - ASSERT_EQ(IsProtected(*gamepad_info->usage), false); + ASSERT_FALSE(IsAlwaysProtected(*gamepad_info->usage)); gamepad_info->report_ids = {0x01, 0x02}; auto status_info = HidCollectionInfo::New(); status_info->usage = HidUsageAndPage::New(mojom::kGenericDesktopGamePad, mojom::kPageGenericDesktop); - ASSERT_EQ(IsProtected(*status_info->usage), false); + ASSERT_FALSE(IsAlwaysProtected(*status_info->usage)); status_info->report_ids = {0x03}; AddTopCollectionInfo(std::move(gamepad_info)); AddTopCollectionInfo(std::move(status_info)); @@ -1420,7 +1420,7 @@ auto info = HidCollectionInfo::New(); info->usage = HidUsageAndPage::New(mojom::kGenericDesktopKeyboard, mojom::kPageGenericDesktop); - ASSERT_EQ(IsProtected(*info->usage), true); + ASSERT_TRUE(IsAlwaysProtected(*info->usage)); AddTopCollectionInfo(std::move(info)); ValidateDetails(false, 8, 1, 0, kSteamControllerKeyboard, kSteamControllerKeyboardSize); @@ -1451,7 +1451,7 @@ auto info = HidCollectionInfo::New(); info->usage = HidUsageAndPage::New(mojom::kGenericDesktopMouse, mojom::kPageGenericDesktop); - ASSERT_EQ(IsProtected(*info->usage), true); + ASSERT_TRUE(IsAlwaysProtected(*info->usage)); AddTopCollectionInfo(std::move(info)); ValidateDetails(false, 4, 0, 0, kSteamControllerMouse, kSteamControllerMouseSize); @@ -1479,7 +1479,7 @@ TEST_F(HidReportDescriptorTest, ValidateDetails_SteamControllerVendor) { auto info = HidCollectionInfo::New(); info->usage = HidUsageAndPage::New(0x01, mojom::kPageVendor); - ASSERT_EQ(IsProtected(*info->usage), false); + ASSERT_FALSE(IsAlwaysProtected(*info->usage)); AddTopCollectionInfo(std::move(info)); ValidateDetails(false, 64, 64, 64, kSteamControllerVendor, kSteamControllerVendorSize); @@ -1499,7 +1499,7 @@ auto info = HidCollectionInfo::New(); info->usage = HidUsageAndPage::New(mojom::kGenericDesktopJoystick, mojom::kPageGenericDesktop); - ASSERT_EQ(IsProtected(*info->usage), false); + ASSERT_FALSE(IsAlwaysProtected(*info->usage)); AddTopCollectionInfo(std::move(info)); ValidateDetails(false, 7, 4, 0, kXSkillsUsbAdapter, kXSkillsUsbAdapterSize); } @@ -1532,7 +1532,7 @@ auto info = HidCollectionInfo::New(); info->usage = HidUsageAndPage::New(mojom::kGenericDesktopKeyboard, mojom::kPageGenericDesktop); - ASSERT_EQ(IsProtected(*info->usage), true); + ASSERT_TRUE(IsAlwaysProtected(*info->usage)); AddTopCollectionInfo(std::move(info)); ValidateDetails(false, 8, 0, 0, kBelkinNostromoKeyboard, kBelkinNostromoKeyboardSize); @@ -1558,7 +1558,7 @@ auto info = HidCollectionInfo::New(); info->usage = HidUsageAndPage::New(mojom::kGenericDesktopMouse, mojom::kPageGenericDesktop); - ASSERT_EQ(IsProtected(*info->usage), true); + ASSERT_TRUE(IsAlwaysProtected(*info->usage)); AddTopCollectionInfo(std::move(info)); ValidateDetails(false, 4, 1, 0, kBelkinNostromoMouseAndExtra, kBelkinNostromoMouseAndExtraSize);
diff --git a/services/device/public/cpp/hid/hid_switches.cc b/services/device/public/cpp/hid/hid_switches.cc new file mode 100644 index 0000000..e4d5b7ab0 --- /dev/null +++ b/services/device/public/cpp/hid/hid_switches.cc
@@ -0,0 +1,12 @@ +// 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 "services/device/public/cpp/hid/hid_switches.h" + +namespace switches { + +// Disable the HID blocklist. +const char kDisableHidBlocklist[] = "disable-hid-blocklist"; + +} // namespace switches
diff --git a/services/device/public/cpp/hid/hid_switches.h b/services/device/public/cpp/hid/hid_switches.h new file mode 100644 index 0000000..1aa505e --- /dev/null +++ b/services/device/public/cpp/hid/hid_switches.h
@@ -0,0 +1,14 @@ +// 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 SERVICES_DEVICE_PUBLIC_CPP_HID_HID_SWITCHES_H_ +#define SERVICES_DEVICE_PUBLIC_CPP_HID_HID_SWITCHES_H_ + +namespace switches { + +extern const char kDisableHidBlocklist[]; + +} // namespace switches + +#endif // SERVICES_DEVICE_PUBLIC_CPP_HID_HID_SWITCHES_H_
diff --git a/services/device/public/cpp/hid/hid_usage_and_page.cc b/services/device/public/cpp/hid/hid_usage_and_page.cc index c3b6178..9ce7f520 100644 --- a/services/device/public/cpp/hid/hid_usage_and_page.cc +++ b/services/device/public/cpp/hid/hid_usage_and_page.cc
@@ -6,7 +6,7 @@ namespace device { -bool IsProtected(const mojom::HidUsageAndPage& hid_usage_and_page) { +bool IsAlwaysProtected(const mojom::HidUsageAndPage& hid_usage_and_page) { const uint16_t usage = hid_usage_and_page.usage; const uint16_t usage_page = hid_usage_and_page.usage_page;
diff --git a/services/device/public/cpp/hid/hid_usage_and_page.h b/services/device/public/cpp/hid/hid_usage_and_page.h index b080797..422c6a5 100644 --- a/services/device/public/cpp/hid/hid_usage_and_page.h +++ b/services/device/public/cpp/hid/hid_usage_and_page.h
@@ -9,8 +9,8 @@ namespace device { -// Indicates whether this usage is protected by Chrome. -bool IsProtected(const mojom::HidUsageAndPage& hid_usage_and_page); +// Indicates whether this usage is always protected by Chrome. +bool IsAlwaysProtected(const mojom::HidUsageAndPage& hid_usage_and_page); } // namespace device
diff --git a/services/device/public/mojom/hid.mojom b/services/device/public/mojom/hid.mojom index b93b66c..ca840837 100644 --- a/services/device/public/mojom/hid.mojom +++ b/services/device/public/mojom/hid.mojom
@@ -341,6 +341,11 @@ // A platform-specific string identifier for the logical device. string device_node@13; + + // Reports that should not be accessible from Javascript. + [MinVersion=1] array<uint8>? protected_input_report_ids@14; + [MinVersion=1] array<uint8>? protected_output_report_ids@15; + [MinVersion=1] array<uint8>? protected_feature_report_ids@16; }; // A client interface for receiving a notification when HID devices are @@ -379,9 +384,13 @@ // open. When the HID connection is closed, the watcher is also closed. This // is useful when the connection closure should be handled somewhere other // than where the |connection| and |connection_client| are held. + // + // If |allow_protected_reports| is true, this connection is exempted from + // the HID blocklist so that protected reports may be sent and received. Connect@2(string device_guid, pending_remote<HidConnectionClient>? connection_client, - pending_remote<HidConnectionWatcher>? watcher) + pending_remote<HidConnectionWatcher>? watcher, + [MinVersion=1] bool allow_protected_reports) => (pending_remote<HidConnection>? connection); // Binds a HidManager endpoint.
diff --git a/services/network/resource_scheduler/resource_scheduler.cc b/services/network/resource_scheduler/resource_scheduler.cc index c5831e4..7609fb9 100644 --- a/services/network/resource_scheduler/resource_scheduler.cc +++ b/services/network/resource_scheduler/resource_scheduler.cc
@@ -16,6 +16,7 @@ #include "base/metrics/field_trial_params.h" #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" +#include "base/metrics/histogram_macros_local.h" #include "base/optional.h" #include "base/sequenced_task_runner.h" #include "base/strings/string_number_conversions.h" @@ -823,10 +824,12 @@ } } else { if (last_non_delayable_request_end_) { - UMA_HISTOGRAM_MEDIUM_TIMES( + LOCAL_HISTOGRAM_CUSTOM_TIMES( "ResourceScheduler.NonDelayableLastEndToNonDelayableStart." "NonDelayableNotInFlight", - ticks_now - last_non_delayable_request_end_.value()); + ticks_now - last_non_delayable_request_end_.value(), + base::TimeDelta::FromMilliseconds(10), + base::TimeDelta::FromMinutes(3), 50); } } @@ -839,9 +842,11 @@ ticks_now - last_non_delayable_request_start_.value()); } if (last_non_delayable_request_end_.has_value()) { - UMA_HISTOGRAM_MEDIUM_TIMES( + LOCAL_HISTOGRAM_CUSTOM_TIMES( "ResourceScheduler.NonDelayableLastEndToNonDelayableStart", - ticks_now - last_non_delayable_request_end_.value()); + ticks_now - last_non_delayable_request_end_.value(), + base::TimeDelta::FromMilliseconds(10), + base::TimeDelta::FromMinutes(3), 50); } // Record time since last non-delayable request start or end, whichever @@ -1264,10 +1269,11 @@ request.url_request()->creation_time(); } - UMA_HISTOGRAM_MEDIUM_TIMES( + LOCAL_HISTOGRAM_CUSTOM_TIMES( "ResourceScheduler.DelayableRequests." "WaitTimeToAvoidContentionWithNonDelayableRequest", - ideal_duration_to_wait); + ideal_duration_to_wait, base::TimeDelta::FromMilliseconds(10), + base::TimeDelta::FromMinutes(3), 50); } RequestQueue pending_requests_;
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json index 023a480..d075b1ea 100644 --- a/testing/buildbot/chromium.android.fyi.json +++ b/testing/buildbot/chromium.android.fyi.json
@@ -240,11 +240,11 @@ "--bucket", "chromium-result-details", "--test-name", - "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.123" + "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.125" ], "script": "//build/android/pylib/results/presentation/test_results_presentation.py" }, - "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.123", + "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.125", "resultdb": { "enable": true }, @@ -254,7 +254,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M87", - "revision": "version:87.0.4280.123" + "revision": "version:87.0.4280.125" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -317,11 +317,11 @@ "--bucket", "chromium-result-details", "--test-name", - "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.56" + "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.58" ], "script": "//build/android/pylib/results/presentation/test_results_presentation.py" }, - "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.56", + "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.58", "resultdb": { "enable": true }, @@ -331,7 +331,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M88", - "revision": "version:88.0.4324.56" + "revision": "version:88.0.4324.58" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -394,11 +394,11 @@ "--bucket", "chromium-result-details", "--test-name", - "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.123" + "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.125" ], "script": "//build/android/pylib/results/presentation/test_results_presentation.py" }, - "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.123", + "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.125", "resultdb": { "enable": true }, @@ -408,7 +408,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M87", - "revision": "version:87.0.4280.123" + "revision": "version:87.0.4280.125" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -471,11 +471,11 @@ "--bucket", "chromium-result-details", "--test-name", - "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.56" + "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.58" ], "script": "//build/android/pylib/results/presentation/test_results_presentation.py" }, - "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.56", + "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.58", "resultdb": { "enable": true }, @@ -485,7 +485,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M88", - "revision": "version:88.0.4324.56" + "revision": "version:88.0.4324.58" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -769,11 +769,11 @@ "--bucket", "chromium-result-details", "--test-name", - "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.123" + "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.125" ], "script": "//build/android/pylib/results/presentation/test_results_presentation.py" }, - "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.123", + "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.125", "resultdb": { "enable": true }, @@ -783,7 +783,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M87", - "revision": "version:87.0.4280.123" + "revision": "version:87.0.4280.125" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -846,11 +846,11 @@ "--bucket", "chromium-result-details", "--test-name", - "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.56" + "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.58" ], "script": "//build/android/pylib/results/presentation/test_results_presentation.py" }, - "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.56", + "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.58", "resultdb": { "enable": true }, @@ -860,7 +860,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M88", - "revision": "version:88.0.4324.56" + "revision": "version:88.0.4324.58" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -923,11 +923,11 @@ "--bucket", "chromium-result-details", "--test-name", - "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.123" + "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.125" ], "script": "//build/android/pylib/results/presentation/test_results_presentation.py" }, - "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.123", + "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.125", "resultdb": { "enable": true }, @@ -937,7 +937,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M87", - "revision": "version:87.0.4280.123" + "revision": "version:87.0.4280.125" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -1000,11 +1000,11 @@ "--bucket", "chromium-result-details", "--test-name", - "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.56" + "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.58" ], "script": "//build/android/pylib/results/presentation/test_results_presentation.py" }, - "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.56", + "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.58", "resultdb": { "enable": true }, @@ -1014,7 +1014,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M88", - "revision": "version:88.0.4324.56" + "revision": "version:88.0.4324.58" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -1298,11 +1298,11 @@ "--bucket", "chromium-result-details", "--test-name", - "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.123" + "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.125" ], "script": "//build/android/pylib/results/presentation/test_results_presentation.py" }, - "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.123", + "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.125", "resultdb": { "enable": true }, @@ -1312,7 +1312,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M87", - "revision": "version:87.0.4280.123" + "revision": "version:87.0.4280.125" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -1375,11 +1375,11 @@ "--bucket", "chromium-result-details", "--test-name", - "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.56" + "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.58" ], "script": "//build/android/pylib/results/presentation/test_results_presentation.py" }, - "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.56", + "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.58", "resultdb": { "enable": true }, @@ -1389,7 +1389,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M88", - "revision": "version:88.0.4324.56" + "revision": "version:88.0.4324.58" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -1452,11 +1452,11 @@ "--bucket", "chromium-result-details", "--test-name", - "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.123" + "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.125" ], "script": "//build/android/pylib/results/presentation/test_results_presentation.py" }, - "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.123", + "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.125", "resultdb": { "enable": true }, @@ -1466,7 +1466,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M87", - "revision": "version:87.0.4280.123" + "revision": "version:87.0.4280.125" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -1529,11 +1529,11 @@ "--bucket", "chromium-result-details", "--test-name", - "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.56" + "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.58" ], "script": "//build/android/pylib/results/presentation/test_results_presentation.py" }, - "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.56", + "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.58", "resultdb": { "enable": true }, @@ -1543,7 +1543,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M88", - "revision": "version:88.0.4324.56" + "revision": "version:88.0.4324.58" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -1827,11 +1827,11 @@ "--bucket", "chromium-result-details", "--test-name", - "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.123" + "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.125" ], "script": "//build/android/pylib/results/presentation/test_results_presentation.py" }, - "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.123", + "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 87.0.4280.125", "resultdb": { "enable": true }, @@ -1841,7 +1841,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M87", - "revision": "version:87.0.4280.123" + "revision": "version:87.0.4280.125" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -1904,11 +1904,11 @@ "--bucket", "chromium-result-details", "--test-name", - "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.56" + "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.58" ], "script": "//build/android/pylib/results/presentation/test_results_presentation.py" }, - "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.56", + "name": "weblayer_instrumentation_test_versions_apk_Client Tests For 88.0.4324.58", "resultdb": { "enable": true }, @@ -1918,7 +1918,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M88", - "revision": "version:88.0.4324.56" + "revision": "version:88.0.4324.58" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -1981,11 +1981,11 @@ "--bucket", "chromium-result-details", "--test-name", - "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.123" + "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.125" ], "script": "//build/android/pylib/results/presentation/test_results_presentation.py" }, - "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.123", + "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 87.0.4280.125", "resultdb": { "enable": true }, @@ -1995,7 +1995,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M87", - "revision": "version:87.0.4280.123" + "revision": "version:87.0.4280.125" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}", @@ -2058,11 +2058,11 @@ "--bucket", "chromium-result-details", "--test-name", - "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.56" + "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.58" ], "script": "//build/android/pylib/results/presentation/test_results_presentation.py" }, - "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.56", + "name": "weblayer_instrumentation_test_versions_apk_Implementation Tests For 88.0.4324.58", "resultdb": { "enable": true }, @@ -2072,7 +2072,7 @@ { "cipd_package": "chromium/testing/weblayer-x86", "location": "weblayer_instrumentation_test_M88", - "revision": "version:88.0.4324.56" + "revision": "version:88.0.4324.58" }, { "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
diff --git a/testing/buildbot/variants.pyl b/testing/buildbot/variants.pyl index 54ec3921..eed5a67e 100644 --- a/testing/buildbot/variants.pyl +++ b/testing/buildbot/variants.pyl
@@ -319,13 +319,13 @@ '../../weblayer/browser/android/javatests/skew/expectations.txt', '--impl-version=88', ], - 'identifier': 'Implementation Tests For 88.0.4324.56', + 'identifier': 'Implementation Tests For 88.0.4324.58', 'swarming': { 'cipd_packages': [ { 'cipd_package': 'chromium/testing/weblayer-x86', 'location': 'weblayer_instrumentation_test_M88', - 'revision': 'version:88.0.4324.56', + 'revision': 'version:88.0.4324.58', } ], }, @@ -342,13 +342,13 @@ '../../weblayer/browser/android/javatests/skew/expectations.txt', '--impl-version=87', ], - 'identifier': 'Implementation Tests For 87.0.4280.123', + 'identifier': 'Implementation Tests For 87.0.4280.125', 'swarming': { 'cipd_packages': [ { 'cipd_package': 'chromium/testing/weblayer-x86', 'location': 'weblayer_instrumentation_test_M87', - 'revision': 'version:87.0.4280.123', + 'revision': 'version:87.0.4280.125', } ], }, @@ -388,13 +388,13 @@ '../../weblayer/browser/android/javatests/skew/expectations.txt', '--client-version=88', ], - 'identifier': 'Client Tests For 88.0.4324.56', + 'identifier': 'Client Tests For 88.0.4324.58', 'swarming': { 'cipd_packages': [ { 'cipd_package': 'chromium/testing/weblayer-x86', 'location': 'weblayer_instrumentation_test_M88', - 'revision': 'version:88.0.4324.56', + 'revision': 'version:88.0.4324.58', } ], }, @@ -411,13 +411,13 @@ '../../weblayer/browser/android/javatests/skew/expectations.txt', '--client-version=87', ], - 'identifier': 'Client Tests For 87.0.4280.123', + 'identifier': 'Client Tests For 87.0.4280.125', 'swarming': { 'cipd_packages': [ { 'cipd_package': 'chromium/testing/weblayer-x86', 'location': 'weblayer_instrumentation_test_M87', - 'revision': 'version:87.0.4280.123', + 'revision': 'version:87.0.4280.125', } ], },
diff --git a/third_party/android_deps/BUILD.gn b/third_party/android_deps/BUILD.gn index 64ef5f0..0ce9667 100644 --- a/third_party/android_deps/BUILD.gn +++ b/third_party/android_deps/BUILD.gn
@@ -1568,7 +1568,7 @@ # This is generated, do not edit. Update BuildConfigGenerator.groovy instead. java_prebuilt("com_google_guava_guava_java") { - jar_path = "libs/com_google_guava_guava/guava-27.1-jre.jar" + jar_path = "libs/com_google_guava_guava/guava-30.1-jre.jar" output_name = "com_google_guava_guava" enable_bytecode_checks = false deps = [ @@ -1578,7 +1578,6 @@ ":com_google_guava_listenablefuture_java", ":com_google_j2objc_j2objc_annotations_java", ":org_checkerframework_checker_qual_java", - ":org_codehaus_mojo_animal_sniffer_annotations_java", ] # Need to exclude class and replace it with class library as @@ -1589,15 +1588,16 @@ # This is generated, do not edit. Update BuildConfigGenerator.groovy instead. java_prebuilt("com_google_guava_guava_android_java") { - jar_path = "libs/com_google_guava_guava_android/guava-25.1-android.jar" + jar_path = "libs/com_google_guava_guava_android/guava-30.1-android.jar" output_name = "com_google_guava_guava_android" supports_android = true deps = [ ":com_google_code_findbugs_jsr305_java", ":com_google_errorprone_error_prone_annotations_java", + ":com_google_guava_failureaccess_java", + ":com_google_guava_listenablefuture_java", ":com_google_j2objc_j2objc_annotations_java", ":org_checkerframework_checker_compat_qual_java", - ":org_codehaus_mojo_animal_sniffer_annotations_java", ] # Add a dep to com_google_guava_listenablefuture_java @@ -1620,7 +1620,7 @@ # This is generated, do not edit. Update BuildConfigGenerator.groovy instead. java_prebuilt("com_google_j2objc_j2objc_annotations_java") { jar_path = - "libs/com_google_j2objc_j2objc_annotations/j2objc-annotations-1.1.jar" + "libs/com_google_j2objc_j2objc_annotations/j2objc-annotations-1.3.jar" output_name = "com_google_j2objc_j2objc_annotations" supports_android = true @@ -1715,7 +1715,7 @@ # This is generated, do not edit. Update BuildConfigGenerator.groovy instead. java_prebuilt("org_checkerframework_checker_compat_qual_java") { - jar_path = "libs/org_checkerframework_checker_compat_qual/checker-compat-qual-2.5.3.jar" + jar_path = "libs/org_checkerframework_checker_compat_qual/checker-compat-qual-2.5.5.jar" output_name = "org_checkerframework_checker_compat_qual" supports_android = true } @@ -2896,7 +2896,7 @@ # This is generated, do not edit. Update BuildConfigGenerator.groovy instead. java_prebuilt("org_checkerframework_checker_qual_java") { - jar_path = "libs/org_checkerframework_checker_qual/checker-qual-2.10.0.jar" + jar_path = "libs/org_checkerframework_checker_qual/checker-qual-3.5.0.jar" output_name = "org_checkerframework_checker_qual" enable_bytecode_checks = false
diff --git a/third_party/android_deps/build.gradle b/third_party/android_deps/build.gradle index ca71391..9004c61 100644 --- a/third_party/android_deps/build.gradle +++ b/third_party/android_deps/build.gradle
@@ -156,7 +156,6 @@ compile "com.google.code.findbugs:jsr305:3.0.2" compile "com.google.guava:failureaccess:1.0.1" - compile "com.google.guava:listenablefuture:1.0" compile "com.google.j2objc:j2objc-annotations:1.1" compile "com.google.protobuf:protobuf-javalite:3.13.0" compile "javax.annotation:javax.annotation-api:1.3.2" @@ -174,10 +173,10 @@ // Upstream guava introduced versions with -android suffix starting with version // 22 to remove incompatible code with android. Thus we keep two jars, one for // the full guava and one that supports android. - compile "com.google.guava:guava:25.1-android" + compile "com.google.guava:guava:30.1-android" // buildCompile targets have supports_android = false. - buildCompile "com.google.guava:guava:27.0.1-jre" + buildCompile "com.google.guava:guava:30.1-jre" def daggerVersion = '2.30' compile "com.google.dagger:dagger:${daggerVersion}" @@ -192,6 +191,9 @@ compile "org.checkerframework:checker-compat-qual:2.5.3" compile "org.codehaus.mojo:animal-sniffer-annotations:1.17" + // Dedicated configuration to avoid using higher version number. The 9999 version is empty. + compileListenableFuture "com.google.guava:listenablefuture:1.0" + buildCompile "com.google.errorprone:error_prone_core:${errorproneVersion}" buildCompile "com.google.errorprone:error_prone_check_api:${errorproneVersion}" buildCompile "com.google.errorprone:error_prone_annotation:${errorproneVersion}"
diff --git a/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy b/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy index f00aeaff..4ccf3f72 100644 --- a/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy +++ b/third_party/android_deps/buildSrc/src/main/groovy/ChromiumDepGraph.groovy
@@ -19,12 +19,8 @@ */ class ChromiumDepGraph { final def dependencies = new HashMap<String, DependencyDescription>() + final def lowerVersionOverride = new HashSet<String>() - // Override to use the lower version of the library when - // resolving which library version to use. - final def LOWER_VERSION_OVERRIDE = [ - 'com_google_guava_listenablefuture', - ] // Some libraries don't properly fill their POM with the appropriate licensing information. // It is provided here from manual lookups. Note that licenseUrl must provide textual content // rather than be an html page. @@ -290,6 +286,8 @@ void collectDependencies() { def compileConfig = project.configurations.getByName('compile').resolvedConfiguration + def compileListenableFutureConfig = project.configurations.getByName( + 'compileListenableFuture').resolvedConfiguration def buildCompileConfig = project.configurations.getByName('buildCompile').resolvedConfiguration def testCompileConfig = project.configurations.getByName('testCompile').resolvedConfiguration def androidTestCompileConfig = project.configurations.getByName( @@ -297,10 +295,15 @@ List<String> topLevelIds = [] Set<ResolvedConfiguration> deps = [] deps += compileConfig.firstLevelModuleDependencies + deps += compileListenableFutureConfig.firstLevelModuleDependencies deps += buildCompileConfig.firstLevelModuleDependencies deps += testCompileConfig.firstLevelModuleDependencies deps += androidTestCompileConfig.firstLevelModuleDependencies + compileListenableFutureConfig.firstLevelModuleDependencies.each { dependency -> + lowerVersionOverride.add(makeModuleId(dependency.module)) + } + deps.each { dependency -> topLevelIds.add(makeModuleId(dependency.module)) collectDependenciesInternal(dependency) @@ -329,7 +332,9 @@ dep.testOnly = false } - compileConfig.resolvedArtifacts.each { artifact -> + def compileResolvedArtifacts = compileConfig.resolvedArtifacts + compileResolvedArtifacts += compileListenableFutureConfig.resolvedArtifacts + compileResolvedArtifacts.each { artifact -> def id = makeModuleId(artifact) def dep = dependencies.get(id) assert dep != null : "No dependency collected for artifact ${artifact.name}" @@ -362,13 +367,16 @@ private void collectDependenciesInternal(ResolvedDependency dependency) { def id = makeModuleId(dependency.module) if (dependencies.containsKey(id)) { - if (id in LOWER_VERSION_OVERRIDE && - dependencies.get(id).version <= dependency.module.id.version) { + if (dependencies.get(id).version == dependency.module.id.version) return + + // Default to using largest version for version conflict resolution. See + // crbug.com/1040958 + // https://docs.gradle.org/current/userguide/dependency_resolution.html#sec:version-conflict + def useLowerVersion = (id in lowerVersionOverride) + def versionIsLower = dependency.module.id.version < dependencies.get(id).version + if (useLowerVersion != versionIsLower) { return } - // Use largest version for version conflict resolution. See crbug.com/1040958 - // https://docs.gradle.org/current/userguide/dependency_resolution.html#sec:version-conflict - if (dependencies.get(id).version >= dependency.module.id.version) return } def childModules = []
diff --git a/third_party/android_deps/buildSrc/src/main/groovy/ChromiumPlugin.groovy b/third_party/android_deps/buildSrc/src/main/groovy/ChromiumPlugin.groovy index c19885c..0814b69 100644 --- a/third_party/android_deps/buildSrc/src/main/groovy/ChromiumPlugin.groovy +++ b/third_party/android_deps/buildSrc/src/main/groovy/ChromiumPlugin.groovy
@@ -17,6 +17,12 @@ /** Main type of configuration, use it for libraries that the APK depends on. */ compile + /** + * Dedicated com_google_guava_listenablefuture configuration so that other libraries do + * not affect the resolved listenablefuture version. + */ + compileListenableFuture + /** Libraries that are for testing only. */ testCompile
diff --git a/third_party/android_deps/libs/com_google_guava_guava/README.chromium b/third_party/android_deps/libs/com_google_guava_guava/README.chromium index 11a90e8..cea878f 100644 --- a/third_party/android_deps/libs/com_google_guava_guava/README.chromium +++ b/third_party/android_deps/libs/com_google_guava_guava/README.chromium
@@ -1,13 +1,13 @@ Name: Guava: Google Core Libraries for Java Short Name: guava URL: https://github.com/google/guava -Version: 27.1-jre +Version: 30.1-jre License: Apache 2.0 License File: NOT_SHIPPED Security Critical: no Description: -Guava is a suite of core and expanded libraries that include utility classes, google's collections, io classes, and much much more. +Guava is a suite of core and expanded libraries that include utility classes, Google's collections, I/O classes, and much more. Local Modifications: No modifications.
diff --git a/third_party/android_deps/libs/com_google_guava_guava/cipd.yaml b/third_party/android_deps/libs/com_google_guava_guava/cipd.yaml index b64d6c9..9d267da 100644 --- a/third_party/android_deps/libs/com_google_guava_guava/cipd.yaml +++ b/third_party/android_deps/libs/com_google_guava_guava/cipd.yaml
@@ -3,8 +3,8 @@ # found in the LICENSE file. # To create CIPD package run the following command. -# cipd create --pkg-def cipd.yaml -tag version:27.1-jre-cr0 +# cipd create --pkg-def cipd.yaml -tag version:30.1-jre-cr0 package: chromium/third_party/android_deps/libs/com_google_guava_guava description: "Guava: Google Core Libraries for Java" data: -- file: guava-27.1-jre.jar +- file: guava-30.1-jre.jar
diff --git a/third_party/android_deps/libs/com_google_j2objc_j2objc_annotations/README.chromium b/third_party/android_deps/libs/com_google_j2objc_j2objc_annotations/README.chromium index f3fd069..d084704 100644 --- a/third_party/android_deps/libs/com_google_j2objc_j2objc_annotations/README.chromium +++ b/third_party/android_deps/libs/com_google_j2objc_j2objc_annotations/README.chromium
@@ -1,7 +1,7 @@ Name: J2ObjC Annotations Short Name: j2objc-annotations URL: https://github.com/google/j2objc/ -Version: 1.1 +Version: 1.3 License: Apache Version 2.0 License File: LICENSE Security Critical: yes
diff --git a/third_party/android_deps/libs/com_google_j2objc_j2objc_annotations/cipd.yaml b/third_party/android_deps/libs/com_google_j2objc_j2objc_annotations/cipd.yaml index fea1c80..aaa9c53 100644 --- a/third_party/android_deps/libs/com_google_j2objc_j2objc_annotations/cipd.yaml +++ b/third_party/android_deps/libs/com_google_j2objc_j2objc_annotations/cipd.yaml
@@ -3,8 +3,8 @@ # found in the LICENSE file. # To create CIPD package run the following command. -# cipd create --pkg-def cipd.yaml -tag version:1.1-cr0 +# cipd create --pkg-def cipd.yaml -tag version:1.3-cr0 package: chromium/third_party/android_deps/libs/com_google_j2objc_j2objc_annotations description: "J2ObjC Annotations" data: -- file: j2objc-annotations-1.1.jar +- file: j2objc-annotations-1.3.jar
diff --git a/third_party/android_deps/libs/org_checkerframework_checker_compat_qual/LICENSE b/third_party/android_deps/libs/org_checkerframework_checker_compat_qual/LICENSE index 70d6a70f..0e64188 100644 --- a/third_party/android_deps/libs/org_checkerframework_checker_compat_qual/LICENSE +++ b/third_party/android_deps/libs/org_checkerframework_checker_compat_qual/LICENSE
@@ -16,8 +16,9 @@ I18nFormatUtil.java, NullnessUtil.java, Opt.java, PurityUnqualified.java, RegexUtil.java, SignednessUtil.java, SignednessUtilExtra.java, and UnitsTools.java. It also applies to the cleanroom implementations of - third-party annotations (in checker/src/testannotations/ and in - framework/src/main/java/org/jmlspecs/). + third-party annotations (in checker/src/testannotations/, + framework/src/main/java/org/jmlspecs/, and + framework/src/main/java/com/google/). The Checker Framework includes annotations for some libraries. Those in .astub files use the MIT License. Those in https://github.com/typetools/jdk
diff --git a/third_party/android_deps/libs/org_checkerframework_checker_compat_qual/README.chromium b/third_party/android_deps/libs/org_checkerframework_checker_compat_qual/README.chromium index d3995a2..180ea276 100644 --- a/third_party/android_deps/libs/org_checkerframework_checker_compat_qual/README.chromium +++ b/third_party/android_deps/libs/org_checkerframework_checker_compat_qual/README.chromium
@@ -1,7 +1,7 @@ Name: Checker Qual Short Name: checker-compat-qual URL: https://checkerframework.org -Version: 2.5.3 +Version: 2.5.5 License: GPL v2 with the classpath exception License File: LICENSE Security Critical: yes
diff --git a/third_party/android_deps/libs/org_checkerframework_checker_compat_qual/cipd.yaml b/third_party/android_deps/libs/org_checkerframework_checker_compat_qual/cipd.yaml index 6b0f4f9..33fdfdd 100644 --- a/third_party/android_deps/libs/org_checkerframework_checker_compat_qual/cipd.yaml +++ b/third_party/android_deps/libs/org_checkerframework_checker_compat_qual/cipd.yaml
@@ -3,8 +3,8 @@ # found in the LICENSE file. # To create CIPD package run the following command. -# cipd create --pkg-def cipd.yaml -tag version:2.5.3-cr0 +# cipd create --pkg-def cipd.yaml -tag version:2.5.5-cr0 package: chromium/third_party/android_deps/libs/org_checkerframework_checker_compat_qual description: "Checker Qual" data: -- file: checker-compat-qual-2.5.3.jar +- file: checker-compat-qual-2.5.5.jar
diff --git a/third_party/android_deps/libs/org_checkerframework_checker_qual/LICENSE b/third_party/android_deps/libs/org_checkerframework_checker_qual/LICENSE index 70d6a70f..0e64188 100644 --- a/third_party/android_deps/libs/org_checkerframework_checker_qual/LICENSE +++ b/third_party/android_deps/libs/org_checkerframework_checker_qual/LICENSE
@@ -16,8 +16,9 @@ I18nFormatUtil.java, NullnessUtil.java, Opt.java, PurityUnqualified.java, RegexUtil.java, SignednessUtil.java, SignednessUtilExtra.java, and UnitsTools.java. It also applies to the cleanroom implementations of - third-party annotations (in checker/src/testannotations/ and in - framework/src/main/java/org/jmlspecs/). + third-party annotations (in checker/src/testannotations/, + framework/src/main/java/org/jmlspecs/, and + framework/src/main/java/com/google/). The Checker Framework includes annotations for some libraries. Those in .astub files use the MIT License. Those in https://github.com/typetools/jdk
diff --git a/third_party/android_deps/libs/org_checkerframework_checker_qual/README.chromium b/third_party/android_deps/libs/org_checkerframework_checker_qual/README.chromium index daf9719..6a38eb4 100644 --- a/third_party/android_deps/libs/org_checkerframework_checker_qual/README.chromium +++ b/third_party/android_deps/libs/org_checkerframework_checker_qual/README.chromium
@@ -1,7 +1,7 @@ Name: Checker Qual Short Name: checker-qual URL: https://checkerframework.org -Version: 2.10.0 +Version: 3.5.0 License: GPL v2 with the classpath exception License File: NOT_SHIPPED Security Critical: no
diff --git a/third_party/android_deps/libs/org_checkerframework_checker_qual/cipd.yaml b/third_party/android_deps/libs/org_checkerframework_checker_qual/cipd.yaml index dc14dc8..2068dba 100644 --- a/third_party/android_deps/libs/org_checkerframework_checker_qual/cipd.yaml +++ b/third_party/android_deps/libs/org_checkerframework_checker_qual/cipd.yaml
@@ -3,8 +3,8 @@ # found in the LICENSE file. # To create CIPD package run the following command. -# cipd create --pkg-def cipd.yaml -tag version:2.10.0-cr0 +# cipd create --pkg-def cipd.yaml -tag version:3.5.0-cr0 package: chromium/third_party/android_deps/libs/org_checkerframework_checker_qual description: "Checker Qual" data: -- file: checker-qual-2.10.0.jar +- file: checker-qual-3.5.0.jar
diff --git a/third_party/android_deps/libs/org_checkerframework_dataflow_shaded/LICENSE b/third_party/android_deps/libs/org_checkerframework_dataflow_shaded/LICENSE index 70d6a70f..0e64188 100644 --- a/third_party/android_deps/libs/org_checkerframework_dataflow_shaded/LICENSE +++ b/third_party/android_deps/libs/org_checkerframework_dataflow_shaded/LICENSE
@@ -16,8 +16,9 @@ I18nFormatUtil.java, NullnessUtil.java, Opt.java, PurityUnqualified.java, RegexUtil.java, SignednessUtil.java, SignednessUtilExtra.java, and UnitsTools.java. It also applies to the cleanroom implementations of - third-party annotations (in checker/src/testannotations/ and in - framework/src/main/java/org/jmlspecs/). + third-party annotations (in checker/src/testannotations/, + framework/src/main/java/org/jmlspecs/, and + framework/src/main/java/com/google/). The Checker Framework includes annotations for some libraries. Those in .astub files use the MIT License. Those in https://github.com/typetools/jdk
diff --git a/third_party/android_deps/util/org/chromium/gms/ChromiumPlayServicesAvailability.java b/third_party/android_deps/util/org/chromium/gms/ChromiumPlayServicesAvailability.java index 8d61f6d..b0e2815 100644 --- a/third_party/android_deps/util/org/chromium/gms/ChromiumPlayServicesAvailability.java +++ b/third_party/android_deps/util/org/chromium/gms/ChromiumPlayServicesAvailability.java
@@ -8,33 +8,19 @@ import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.GoogleApiAvailability; +// Refer to go/doubledown-play-services#new-apis for more detail. public final class ChromiumPlayServicesAvailability { /** * The minimum GMS version we're requesting. isGooglePlayServicesAvailable will fail if the - * found version on the devices is lower than this number. This number should only be updated - * when using an API introduced in a newer GMS version, and that API usage is gated by this - * class. To see how this number originated, see + * found version on the devices is lower than this number. This number should never be updated; + * if you need to check for a higher number, use + * {@link GoogleApiAvailability#isGooglePlayServicesAvailable(Context, int))} instead. + * To see how this number originated, see * https://bugs.chromium.org/p/chromium/issues/detail?id=1145211#c3. */ public static final int GMS_VERSION_NUMBER = 20415000; /** - * Checks, with an appropriate version number, if Play Services is available in this context. - * This is intended to replace anyone manually calling isGooglePlayServicesAvailable(context), - * as it causes bugs without an appropriate version number (crbug.com/1145211). - * - * If at all possible, do not use this. From a GMSCore team member: "we would not recommend - * checking availability upfront. You should be able to just call the API directly, and it - * should handle the availability for you. If the API is not available, it should either prompt - * the user to update GMS Core or fail with exception." If in doubt, please consult with your - * PM/UX. - */ - public static int getGooglePlayServicesConnectionResult(final Context context) { - return GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable( - context, GMS_VERSION_NUMBER); - } - - /** * Checks if Play Services is available in this context. * * If at all possible, do not use this. From a GMSCore team member: "we would not recommend @@ -48,4 +34,23 @@ context, GMS_VERSION_NUMBER) == ConnectionResult.SUCCESS; } + + /** + * Gets Play Services connection result, to be used to see if Play Services are available. + * + * This is intended to replace anyone manually calling + * {@link GoogleApiAvailability#isGooglePlayServicesAvailable(context)}, + * as it causes bugs without an appropriate version number (crbug.com/1145211). Use + * isGooglePlayServicesAvailable if you don't explicitly need the connection result. + * + * If at all possible, do not use this. From a GMSCore team member: "we would not recommend + * checking availability upfront. You should be able to just call the API directly, and it + * should handle the availability for you. If the API is not available, it should either prompt + * the user to update GMS Core or fail with exception." If in doubt, please consult with your + * PM/UX. + */ + public static int getGooglePlayServicesConnectionResult(final Context context) { + return GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable( + context, GMS_VERSION_NUMBER); + } }
diff --git a/third_party/android_deps/vulnerability_supressions.xml b/third_party/android_deps/vulnerability_supressions.xml index 1043b90..5212fd3 100644 --- a/third_party/android_deps/vulnerability_supressions.xml +++ b/third_party/android_deps/vulnerability_supressions.xml
@@ -14,4 +14,20 @@ <packageUrl regex="true">^pkg:maven/org\.jetbrains\.kotlin/kotlin\-stdlib\-common@.*$</packageUrl> <cve>CVE-2020-15824</cve> </suppress> + <suppress until="2021-12-01Z"> + <notes><![CDATA[ + https://nvd.nist.gov/vuln/detail/CVE-2020-8908 should not apply to all guava libraries. + ]]></notes> + <packageUrl regex="true">^pkg:maven/com\.google\.guava/failureaccess@.*$</packageUrl> + <cpe>cpe:/a:google:guava</cpe> + + </suppress> + <suppress until="2021-12-01Z"> + <notes><![CDATA[ + https://nvd.nist.gov/vuln/detail/CVE-2020-8908 should not apply to all guava libraries. + ]]></notes> + <packageUrl regex="true">^pkg:maven/com\.google\.guava/listenablefuture@1.0$</packageUrl> + <cpe>cpe:/a:google:guava</cpe> + + </suppress> </suppressions>
diff --git a/third_party/blink/renderer/core/html/canvas/image_data.cc b/third_party/blink/renderer/core/html/canvas/image_data.cc index 19593e7..a061ae6 100644 --- a/third_party/blink/renderer/core/html/canvas/image_data.cc +++ b/third_party/blink/renderer/core/html/canvas/image_data.cc
@@ -42,69 +42,76 @@ // required to describe a pixel, namely, red, green, blue and alpha. namespace { -bool RaiseDOMExceptionAndReturnFalse(ExceptionState* exception_state, - DOMExceptionCode exception_code, - const char* message) { +ImageData* RaiseDOMExceptionAndReturnNull(ExceptionState* exception_state, + DOMExceptionCode exception_code, + const char* message) { if (exception_state) exception_state->ThrowDOMException(exception_code, message); - return false; + return nullptr; } } // namespace -bool ImageData::ValidateConstructorArguments( - const IntSize* size, - const unsigned* width, - const unsigned* height, - const NotShared<DOMArrayBufferView>* data, - const ImageDataSettings* settings, - ExceptionState* exception_state) { - // We accept all the combinations of colorSpace and storageFormat in an - // ImageDataSettings to be stored in an ImageData. Therefore, we don't - // check the color settings in this function. - - if (width && !*width) { - return RaiseDOMExceptionAndReturnFalse( - exception_state, DOMExceptionCode::kIndexSizeError, - "The source width is zero or not a number."); - } - - if (height && !*height) { - return RaiseDOMExceptionAndReturnFalse( - exception_state, DOMExceptionCode::kIndexSizeError, - "The source height is zero or not a number."); - } - - if (width || height) { - base::CheckedNumeric<unsigned> data_size = - StorageFormatBytesPerPixel(kUint8ClampedArrayStorageFormatName); - if (settings) { - data_size = StorageFormatBytesPerPixel(settings->storageFormat()); +ImageData* ImageData::ValidateAndCreate(const IntSize* input_size, + const unsigned* width, + const unsigned* height, + NotShared<DOMArrayBufferView>* data, + const ImageDataSettings* settings, + ExceptionState* exception_state) { + IntSize size; + if (width) { + DCHECK(!input_size); + if (!*width) { + return RaiseDOMExceptionAndReturnNull( + exception_state, DOMExceptionCode::kIndexSizeError, + "The source width is zero or not a number."); } - data_size *= width ? *width : 0; - data_size *= height ? *height : 0; - if (!data_size.IsValid()) { - return RaiseDOMExceptionAndReturnFalse( + size.SetWidth(*width); + } + if (height) { + DCHECK(width); + if (!*height) { + return RaiseDOMExceptionAndReturnNull( + exception_state, DOMExceptionCode::kIndexSizeError, + "The source height is zero or not a number."); + } + size.SetHeight(*height); + } + + // TODO(https://crbug.com/1160105): An |input_size| of 0x0 is accepted, but + // |width| of 0 or |height| of 0 is not. Is this intentional? + if (input_size) + size = *input_size; + + // Ensure the size does not overflow. + unsigned size_in_elements = 0; + { + base::CheckedNumeric<unsigned> size_in_elements_checked = 4; + size_in_elements_checked *= size.Width(); + size_in_elements_checked *= size.Height(); + if (!size_in_elements_checked.IsValid()) { + return RaiseDOMExceptionAndReturnNull( exception_state, DOMExceptionCode::kIndexSizeError, "The requested image size exceeds the supported range."); } - - if (data_size.ValueOrDie() > v8::TypedArray::kMaxLength) { + if (size_in_elements_checked.ValueOrDie() > v8::TypedArray::kMaxLength) { if (exception_state) { exception_state->ThrowRangeError( "Out of memory at ImageData creation."); } - return false; + return nullptr; } + size_in_elements = size_in_elements_checked.ValueOrDie(); } - unsigned data_length = 0; + // If |data| is provided, ensure it is a reasonable format, and that it can + // work with |size|. if (data) { DCHECK(data); if ((*data)->GetType() != DOMArrayBufferView::ViewType::kTypeUint8Clamped && (*data)->GetType() != DOMArrayBufferView::ViewType::kTypeUint16 && (*data)->GetType() != DOMArrayBufferView::ViewType::kTypeFloat32) { - return RaiseDOMExceptionAndReturnFalse( + return RaiseDOMExceptionAndReturnNull( exception_state, DOMExceptionCode::kNotSupportedError, "The input data type is not supported."); } @@ -114,52 +121,71 @@ std::numeric_limits<uint32_t>::max(), "We use UINT32_MAX as the upper bound of the input size and expect " "that the result fits into an `unsigned`."); + + unsigned data_length_in_bytes = 0; if (!base::CheckedNumeric<uint32_t>((*data)->byteLength()) - .AssignIfValid(&data_length)) { - return RaiseDOMExceptionAndReturnFalse( + .AssignIfValid(&data_length_in_bytes)) { + return RaiseDOMExceptionAndReturnNull( exception_state, DOMExceptionCode::kNotSupportedError, "The input data is too large. The maximum size is 4294967295."); } - if (!data_length) { - return RaiseDOMExceptionAndReturnFalse( + if (!data_length_in_bytes) { + return RaiseDOMExceptionAndReturnNull( exception_state, DOMExceptionCode::kInvalidStateError, "The input data has zero elements."); } - data_length /= (*data)->TypeSize(); - if (data_length % 4) { - return RaiseDOMExceptionAndReturnFalse( + + const unsigned data_length_in_elements = + data_length_in_bytes / (*data)->TypeSize(); + if (data_length_in_elements % 4) { + return RaiseDOMExceptionAndReturnNull( exception_state, DOMExceptionCode::kInvalidStateError, "The input data length is not a multiple of 4."); } - if (width && (data_length / 4) % *width) { - return RaiseDOMExceptionAndReturnFalse( - exception_state, DOMExceptionCode::kIndexSizeError, - "The input data length is not a multiple of (4 * width)."); + const unsigned data_length_in_pixels = data_length_in_elements / 4; + // TODO(https://crbug.com/1160105): This code historically does not ensure + // that |size| satisfy the same requirements when specified by |input_size| + // as compared when when it is specified by |width| and |height|. + if (width) { + if (data_length_in_pixels % *width) { + return RaiseDOMExceptionAndReturnNull( + exception_state, DOMExceptionCode::kIndexSizeError, + "The input data length is not a multiple of (4 * width)."); + } + + unsigned expected_height = data_length_in_pixels / *width; + if (height) { + if (*height != expected_height) { + return RaiseDOMExceptionAndReturnNull( + exception_state, DOMExceptionCode::kIndexSizeError, + "The input data length is not equal to (4 * width * height)."); + } + } else { + size.SetHeight(expected_height); + } } - - if (width && height && *height != data_length / (4 * *width)) - return RaiseDOMExceptionAndReturnFalse( - exception_state, DOMExceptionCode::kIndexSizeError, - "The input data length is not equal to (4 * width * height)."); - } - - if (size) { - if (size->Width() <= 0 || size->Height() <= 0) - return false; - base::CheckedNumeric<unsigned> data_size = 4; - data_size *= size->Width(); - data_size *= size->Height(); - if (!data_size.IsValid() || - data_size.ValueOrDie() > v8::TypedArray::kMaxLength) - return false; - if (data) { - if (data_size.ValueOrDie() > data_length) - return false; + // As referenced above, this is is the only check that has been made when + // size is specified by |input_size|. + if (input_size) { + if (size_in_elements > data_length_in_elements) + return nullptr; } } - return true; + NotShared<DOMArrayBufferView> allocated_data; + if (!data) { + ImageDataStorageFormat storage_format = + settings ? GetImageDataStorageFormat(settings->storageFormat()) + : kUint8ClampedArrayStorageFormat; + allocated_data = AllocateAndValidateDataArray( + size_in_elements, storage_format, exception_state); + if (!allocated_data) + return nullptr; + } + + return MakeGarbageCollected<ImageData>(size, data ? *data : allocated_data, + settings); } NotShared<DOMArrayBufferView> ImageData::AllocateAndValidateDataArray( @@ -201,20 +227,7 @@ ImageData* ImageData::Create(const IntSize& size, const ImageDataSettings* settings) { - if (!ValidateConstructorArguments(&size, nullptr, nullptr, nullptr, settings)) - return nullptr; - ImageDataStorageFormat storage_format = kUint8ClampedArrayStorageFormat; - if (settings) { - storage_format = - ImageData::GetImageDataStorageFormat(settings->storageFormat()); - } - NotShared<DOMArrayBufferView> data_array = - AllocateAndValidateDataArray(4 * static_cast<unsigned>(size.Width()) * - static_cast<unsigned>(size.Height()), - storage_format); - return data_array - ? MakeGarbageCollected<ImageData>(size, data_array, settings) - : nullptr; + return ValidateAndCreate(&size, nullptr, nullptr, nullptr, settings, nullptr); } ImageData* ImageData::Create(const IntSize& size, @@ -252,36 +265,23 @@ NotShared<DOMArrayBufferView> data_array, const ImageDataSettings* settings) { NotShared<DOMArrayBufferView> buffer_view = data_array; - if (!ValidateConstructorArguments(&size, nullptr, nullptr, &buffer_view, - settings)) - return nullptr; - return MakeGarbageCollected<ImageData>(size, data_array, settings); + return ValidateAndCreate(&size, nullptr, nullptr, &buffer_view, settings, + nullptr); } ImageData* ImageData::Create(unsigned width, unsigned height, ExceptionState& exception_state) { - if (!ValidateConstructorArguments(nullptr, &width, &height, nullptr, nullptr, - &exception_state)) - return nullptr; - - NotShared<DOMArrayBufferView> byte_array = AllocateAndValidateDataArray( - 4 * width * height, kUint8ClampedArrayStorageFormat, &exception_state); - return byte_array ? MakeGarbageCollected<ImageData>(IntSize(width, height), - byte_array) - : nullptr; + return ValidateAndCreate(nullptr, &width, &height, nullptr, nullptr, + &exception_state); } ImageData* ImageData::Create(NotShared<DOMUint8ClampedArray> data, unsigned width, ExceptionState& exception_state) { NotShared<DOMArrayBufferView> buffer_view = data; - if (!ValidateConstructorArguments(nullptr, &width, nullptr, &buffer_view, - nullptr, &exception_state)) - return nullptr; - - unsigned height = base::checked_cast<unsigned>(data->length()) / (width * 4); - return MakeGarbageCollected<ImageData>(IntSize(width, height), data); + return ValidateAndCreate(nullptr, &width, nullptr, &buffer_view, nullptr, + &exception_state); } ImageData* ImageData::Create(NotShared<DOMUint8ClampedArray> data, @@ -289,31 +289,16 @@ unsigned height, ExceptionState& exception_state) { NotShared<DOMArrayBufferView> buffer_view = data; - if (!ValidateConstructorArguments(nullptr, &width, &height, &buffer_view, - nullptr, &exception_state)) - return nullptr; - - return MakeGarbageCollected<ImageData>(IntSize(width, height), data); + return ValidateAndCreate(nullptr, &width, &height, &buffer_view, nullptr, + &exception_state); } ImageData* ImageData::CreateImageData(unsigned width, unsigned height, const ImageDataSettings* settings, ExceptionState& exception_state) { - if (!ValidateConstructorArguments(nullptr, &width, &height, nullptr, settings, - &exception_state)) - return nullptr; - - ImageDataStorageFormat storage_format = - GetImageDataStorageFormat(settings->storageFormat()); - NotShared<DOMArrayBufferView> buffer_view = AllocateAndValidateDataArray( - 4 * width * height, storage_format, &exception_state); - - if (!buffer_view) - return nullptr; - - return MakeGarbageCollected<ImageData>(IntSize(width, height), buffer_view, - settings); + return ValidateAndCreate(nullptr, &width, &height, nullptr, settings, + &exception_state); } ImageData* ImageData::CreateImageData(ImageDataArray& data, @@ -344,12 +329,8 @@ if (settings->storageFormat() != storage_format_name) settings->setStorageFormat(storage_format_name); - if (!ValidateConstructorArguments(nullptr, &width, &height, &buffer_view, - settings, &exception_state)) - return nullptr; - - return MakeGarbageCollected<ImageData>(IntSize(width, height), buffer_view, - settings); + return ValidateAndCreate(nullptr, &width, &height, &buffer_view, settings, + &exception_state); } // This function accepts size (0, 0) and always returns the ImageData in
diff --git a/third_party/blink/renderer/core/html/canvas/image_data.h b/third_party/blink/renderer/core/html/canvas/image_data.h index 8f0977b..92ad9d8 100644 --- a/third_party/blink/renderer/core/html/canvas/image_data.h +++ b/third_party/blink/renderer/core/html/canvas/image_data.h
@@ -151,13 +151,12 @@ NotShared<DOMUint16Array> data_u16_; NotShared<DOMFloat32Array> data_f32_; - static bool ValidateConstructorArguments( - const IntSize* size, - const unsigned* width, - const unsigned* height, - const NotShared<DOMArrayBufferView>* data, - const ImageDataSettings* settings, - ExceptionState* = nullptr); + static ImageData* ValidateAndCreate(const IntSize* size, + const unsigned* width, + const unsigned* height, + NotShared<DOMArrayBufferView>* data, + const ImageDataSettings* settings, + ExceptionState*); static NotShared<DOMArrayBufferView> AllocateAndValidateDataArray( const unsigned&,
diff --git a/third_party/blink/renderer/core/html/media/html_media_element.cc b/third_party/blink/renderer/core/html/media/html_media_element.cc index d2f5a72f..10d1703 100644 --- a/third_party/blink/renderer/core/html/media/html_media_element.cc +++ b/third_party/blink/renderer/core/html/media/html_media_element.cc
@@ -536,7 +536,7 @@ controls_list_(MakeGarbageCollected<HTMLMediaElementControlsList>(this)), lazy_load_intersection_observer_(nullptr), media_player_host_remote_(GetExecutionContext()), - media_player_observer_remote_(GetExecutionContext()), + media_player_observer_remote_set_(GetExecutionContext()), media_player_receiver_set_(this, GetExecutionContext()) { DVLOG(1) << "HTMLMediaElement(" << *this << ")"; @@ -635,13 +635,6 @@ return !IsFullscreen() && SupportsFocus(); } -media::mojom::blink::MediaPlayerObserver* -HTMLMediaElement::GetMediaPlayerObserverRemote() { - if (!media_player_observer_remote_.is_bound()) - return nullptr; - return media_player_observer_remote_.get(); -} - void HTMLMediaElement::ParseAttribute( const AttributeModificationParams& params) { const QualifiedName& name = params.name; @@ -1469,9 +1462,9 @@ !GetWebMediaPlayer()->PausedWhenHidden(); } -void HTMLMediaElement::SetMediaPlayerObserverForTesting( +void HTMLMediaElement::AddMediaPlayerObserverForTesting( mojo::PendingRemote<media::mojom::blink::MediaPlayerObserver> observer) { - SetMediaPlayerObserver(std::move(observer)); + AddMediaPlayerObserver(std::move(observer)); } bool HTMLMediaElement::TextTracksAreReady() const { @@ -3674,7 +3667,7 @@ // The lifetime of the mojo endpoints are tied to the WebMediaPlayer's, so // we need to reset those as well. media_player_receiver_set_.Clear(); - media_player_observer_remote_.reset(); + media_player_observer_remote_set_.Clear(); } OnWebMediaPlayerCleared(); } @@ -4150,7 +4143,7 @@ visitor->Trace(controls_list_); visitor->Trace(lazy_load_intersection_observer_); visitor->Trace(media_player_host_remote_); - visitor->Trace(media_player_observer_remote_); + visitor->Trace(media_player_observer_remote_set_); visitor->Trace(media_player_receiver_set_); Supplementable<HTMLMediaElement>::Trace(visitor); HTMLElement::Trace(visitor); @@ -4392,61 +4385,61 @@ } void HTMLMediaElement::DidPlayerMutedStatusChange(bool muted) { - // The remote to the MediaPlayerObserver could be not set yet. - if (!media_player_observer_remote_.is_bound()) - return; - - media_player_observer_remote_->OnMutedStatusChanged(muted); + for (auto& observer : media_player_observer_remote_set_) { + if (!observer.is_bound()) + continue; + observer->OnMutedStatusChanged(muted); + } } void HTMLMediaElement::DidPlayerMediaPositionStateChange( double playback_rate, base::TimeDelta duration, base::TimeDelta position) { - // The remote to the MediaPlayerObserver could be not set yet. - if (!media_player_observer_remote_.is_bound()) - return; - - media_player_observer_remote_->OnMediaPositionStateChanged( - media_session::mojom::blink::MediaPosition::New( - playback_rate, duration, position, base::TimeTicks::Now())); + for (auto& observer : media_player_observer_remote_set_) { + if (!observer.is_bound()) + continue; + observer->OnMediaPositionStateChanged( + media_session::mojom::blink::MediaPosition::New( + playback_rate, duration, position, base::TimeTicks::Now())); + } } void HTMLMediaElement::DidDisableAudioOutputSinkChanges() { - // The remote to the MediaPlayerObserver could be not set yet. - if (!media_player_observer_remote_.is_bound()) - return; - - media_player_observer_remote_->OnAudioOutputSinkChangingDisabled(); + for (auto& observer : media_player_observer_remote_set_) { + if (!observer.is_bound()) + continue; + observer->OnAudioOutputSinkChangingDisabled(); + } } void HTMLMediaElement::DidPlayerSizeChange(const gfx::Size& size) { - // The remote to the MediaPlayerObserver could be not set yet. - if (!media_player_observer_remote_.is_bound()) - return; - - media_player_observer_remote_->OnMediaSizeChanged(size); + for (auto& observer : media_player_observer_remote_set_) { + if (!observer.is_bound()) + continue; + observer->OnMediaSizeChanged(size); + } } void HTMLMediaElement::DidBufferUnderflow() { - // The remote to the MediaPlayerObserver could be not set yet. - if (!media_player_observer_remote_.is_bound()) - return; - - media_player_observer_remote_->OnBufferUnderflow(); + for (auto& observer : media_player_observer_remote_set_) { + if (!observer.is_bound()) + continue; + observer->OnBufferUnderflow(); + } } void HTMLMediaElement::DidSeek() { - // The remote to the MediaPlayerObserver could be not set yet. - if (!media_player_observer_remote_.is_bound()) - return; - // Send the seek updates to the browser process only once per second. if (last_seek_update_time_.is_null() || (base::TimeTicks::Now() - last_seek_update_time_ >= base::TimeDelta::FromSeconds(1))) { last_seek_update_time_ = base::TimeTicks::Now(); - media_player_observer_remote_->OnSeek(); + for (auto& observer : media_player_observer_remote_set_) { + if (!observer.is_bound()) + continue; + observer->OnSeek(); + } } } @@ -4462,12 +4455,18 @@ return *media_player_host_remote_.get(); } -void HTMLMediaElement::SetMediaPlayerObserver( +void HTMLMediaElement::AddMediaPlayerObserver( mojo::PendingRemote<media::mojom::blink::MediaPlayerObserver> observer) { - DCHECK(!media_player_observer_remote_.is_bound()); - media_player_observer_remote_.Bind( + media_player_observer_remote_set_.Add( std::move(observer), GetDocument().GetTaskRunner(TaskType::kInternalMedia)); + + media_player_observer_remote_set_.set_disconnect_handler(WTF::BindRepeating( + [](HTMLMediaElement* html_media_element, + mojo::RemoteSetElementId remote_id) { + html_media_element->media_player_observer_remote_set_.Remove(remote_id); + }, + WrapWeakPersistent(this))); } void HTMLMediaElement::RequestPlay() {
diff --git a/third_party/blink/renderer/core/html/media/html_media_element.h b/third_party/blink/renderer/core/html/media/html_media_element.h index 56896b6..1587116b 100644 --- a/third_party/blink/renderer/core/html/media/html_media_element.h +++ b/third_party/blink/renderer/core/html/media/html_media_element.h
@@ -48,6 +48,7 @@ #include "third_party/blink/renderer/platform/media/web_audio_source_provider_client.h" #include "third_party/blink/renderer/platform/mojo/heap_mojo_receiver_set.h" #include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h" +#include "third_party/blink/renderer/platform/mojo/heap_mojo_remote_set.h" #include "third_party/blink/renderer/platform/network/mime/mime_type_registry.h" #include "third_party/blink/renderer/platform/scheduler/public/post_cancellable_task.h" #include "third_party/blink/renderer/platform/supplementable.h" @@ -349,7 +350,7 @@ void SetCcLayerForTesting(cc::Layer* layer) { SetCcLayer(layer); } // Required by tests set mock receivers to check that messages are delivered. - void SetMediaPlayerObserverForTesting( + void AddMediaPlayerObserverForTesting( mojo::PendingRemote<media::mojom::blink::MediaPlayerObserver> observer); bool IsShowPosterFlagSet() const { return show_poster_flag_; } @@ -362,9 +363,13 @@ ~HTMLMediaElement() override; void Dispose(); - // Returns a pointer to the media::mojom::blink::MediaPlayerObserver remote if - // already bound, or nullptr otherwise. Used from subclasses as well. - media::mojom::blink::MediaPlayerObserver* GetMediaPlayerObserverRemote(); + // Returns a constant reference to the HeapMojoRemoteSet holding all the bound + // remotes for the media::mojom::blink::MediaPlayerObserver interface. Needed + // to allow sending messages directly from HTMLMediaElement's subclasses. + const HeapMojoRemoteSet<media::mojom::blink::MediaPlayerObserver>& + GetMediaPlayerObserverRemoteSet() { + return media_player_observer_remote_set_; + } void ParseAttribute(const AttributeModificationParams&) override; void FinishParsingChildren() final; @@ -486,7 +491,7 @@ media::mojom::blink::MediaPlayerHost& GetMediaPlayerHostRemote(); // media::mojom::MediaPlayer implementation. - void SetMediaPlayerObserver( + void AddMediaPlayerObserver( mojo::PendingRemote<media::mojom::blink::MediaPlayerObserver> observer) override; void RequestPlay() override; @@ -817,8 +822,11 @@ HeapMojoRemote<media::mojom::blink::MediaPlayerHost> media_player_host_remote_; - HeapMojoRemote<media::mojom::blink::MediaPlayerObserver> - media_player_observer_remote_; + + // Multiple objects outside of the renderer process can register as observers, + // so we need to store the remotes in a set here. + HeapMojoRemoteSet<media::mojom::blink::MediaPlayerObserver> + media_player_observer_remote_set_; // A receiver set is needed here as there will be different objects in the // browser communicating with this object. This is done this way to avoid
diff --git a/third_party/blink/renderer/core/html/media/html_media_element_test.cc b/third_party/blink/renderer/core/html/media/html_media_element_test.cc index d194090..8e66871 100644 --- a/third_party/blink/renderer/core/html/media/html_media_element_test.cc +++ b/third_party/blink/renderer/core/html/media/html_media_element_test.cc
@@ -85,7 +85,7 @@ // interface to allow checking that messages sent over mojo are received with // the right values in the other end. // -// Note this relies on HTMLMediaElement::SetMediaPlayerObserverForTesting() to +// Note this relies on HTMLMediaElement::AddMediaPlayerObserverForTesting() to // provide the HTMLMediaElement instance owned by the test with a valid mojo // remote, that will be bound to the mojo receiver provided by this class // instead of the real one used in production that would be owned by @@ -97,7 +97,7 @@ HTMLMediaElement* html_media_element) { // Bind the remote to the receiver, so that we can intercept incoming // messages sent via the different methods that use the remote. - html_media_element->SetMediaPlayerObserverForTesting( + html_media_element->AddMediaPlayerObserverForTesting( receiver_.BindNewPipeAndPassRemote()); }
diff --git a/third_party/blink/renderer/core/html/media/html_video_element.cc b/third_party/blink/renderer/core/html/media/html_video_element.cc index d79da0f..57b8048 100644 --- a/third_party/blink/renderer/core/html/media/html_video_element.cc +++ b/third_party/blink/renderer/core/html/media/html_video_element.cc
@@ -259,10 +259,10 @@ if (!web_media_player_) return; - auto* media_player_observer_remote = GetMediaPlayerObserverRemote(); - if (media_player_observer_remote) { - media_player_observer_remote->OnPictureInPictureAvailabilityChanged( - SupportsPictureInPicture()); + for (auto& observer : GetMediaPlayerObserverRemoteSet()) { + if (!observer.is_bound()) + continue; + observer->OnPictureInPictureAvailabilityChanged(SupportsPictureInPicture()); } }
diff --git a/third_party/blink/renderer/core/layout/svg/svg_layout_tree_as_text.cc b/third_party/blink/renderer/core/layout/svg/svg_layout_tree_as_text.cc index 785956e..6980d18 100644 --- a/third_party/blink/renderer/core/layout/svg/svg_layout_tree_as_text.cc +++ b/third_party/blink/renderer/core/layout/svg/svg_layout_tree_as_text.cc
@@ -685,7 +685,6 @@ WriteStandardPrefix(ts, text, indent); WritePositionAndStyle(ts, text); ts << "\n"; - WriteResources(ts, text, indent); WriteSVGInlineTextBoxes(ts, text, indent); }
diff --git a/third_party/blink/renderer/core/loader/modulescript/module_script_creation_params.h b/third_party/blink/renderer/core/loader/modulescript/module_script_creation_params.h index 8fdfb2a..e8972b8 100644 --- a/third_party/blink/renderer/core/loader/modulescript/module_script_creation_params.h +++ b/third_party/blink/renderer/core/loader/modulescript/module_script_creation_params.h
@@ -79,8 +79,10 @@ ModuleScriptCreationParams CopyWithClearedSourceText() const { return ModuleScriptCreationParams( - source_url_, base_url_, module_type_, ParkableString(), cache_handler_, - credentials_mode_, script_streamer_, not_streaming_reason_); + source_url_, base_url_, module_type_, ParkableString(), + /*cache_handler=*/nullptr, credentials_mode_, + /*script_streamer=*/nullptr, + ScriptStreamer::NotStreamingReason::kStreamingDisabled); } SingleCachedMetadataHandler* CacheHandler() const { return cache_handler_; }
diff --git a/third_party/blink/renderer/modules/accessibility/BUILD.gn b/third_party/blink/renderer/modules/accessibility/BUILD.gn index c9b2ea07..e7dabc4 100644 --- a/third_party/blink/renderer/modules/accessibility/BUILD.gn +++ b/third_party/blink/renderer/modules/accessibility/BUILD.gn
@@ -14,8 +14,6 @@ "ax_inline_text_box.h", "ax_layout_object.cc", "ax_layout_object.h", - "ax_list.cc", - "ax_list.h", "ax_list_box.cc", "ax_list_box.h", "ax_list_box_option.cc",
diff --git a/third_party/blink/renderer/modules/accessibility/ax_list.cc b/third_party/blink/renderer/modules/accessibility/ax_list.cc deleted file mode 100644 index 576bea1..0000000 --- a/third_party/blink/renderer/modules/accessibility/ax_list.cc +++ /dev/null
@@ -1,61 +0,0 @@ -/* - * Copyright (C) 2008 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "third_party/blink/renderer/modules/accessibility/ax_list.h" - -#include "third_party/blink/renderer/core/html/html_ulist_element.h" -#include "third_party/blink/renderer/core/layout/layout_object.h" -#include "third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.h" - -namespace blink { - -AXList::AXList(LayoutObject* layout_object, AXObjectCacheImpl& ax_object_cache) - : AXLayoutObject(layout_object, ax_object_cache) {} - -AXList::~AXList() = default; - -bool AXList::ComputeAccessibilityIsIgnored( - IgnoredReasons* ignored_reasons) const { - return AccessibilityIsIgnoredByDefault(ignored_reasons); -} - -bool AXList::IsDescriptionList() const { - if (!layout_object_) - return false; - - Node* node = layout_object_->GetNode(); - return node && node->HasTagName(html_names::kDlTag); -} - -ax::mojom::Role AXList::RoleValue() const { - if (IsDescriptionList()) - return ax::mojom::Role::kDescriptionList; - - return ax::mojom::Role::kList; -} -} // namespace blink
diff --git a/third_party/blink/renderer/modules/accessibility/ax_list.h b/third_party/blink/renderer/modules/accessibility/ax_list.h deleted file mode 100644 index 3fb9a7f..0000000 --- a/third_party/blink/renderer/modules/accessibility/ax_list.h +++ /dev/null
@@ -1,57 +0,0 @@ -/* - * Copyright (C) 2008 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_ACCESSIBILITY_AX_LIST_H_ -#define THIRD_PARTY_BLINK_RENDERER_MODULES_ACCESSIBILITY_AX_LIST_H_ - -#include "base/macros.h" -#include "third_party/blink/renderer/modules/accessibility/ax_layout_object.h" - -namespace blink { - -class AXObjectCacheImpl; - -class AXList final : public AXLayoutObject { - public: - AXList(LayoutObject*, AXObjectCacheImpl&); - ~AXList() override; - - bool IsList() const override { return true; } - - ax::mojom::Role RoleValue() const final; - - private: - bool IsDescriptionList() const; - bool ComputeAccessibilityIsIgnored(IgnoredReasons* = nullptr) const override; - - DISALLOW_COPY_AND_ASSIGN(AXList); -}; - -} // namespace blink - -#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_ACCESSIBILITY_AX_LIST_H_
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc index f056d5a..a405cb7 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -367,6 +367,9 @@ ax::mojom::blink::Role::kContentDeletion, ax::mojom::blink::Role::kContentInsertion, ax::mojom::blink::Role::kDetails, + ax::mojom::blink::Role::kDescriptionList, + ax::mojom::blink::Role::kDescriptionListDetail, + ax::mojom::blink::Role::kDescriptionListTerm, ax::mojom::blink::Role::kDialog, ax::mojom::blink::Role::kFigcaption, ax::mojom::blink::Role::kFigure, @@ -856,7 +859,8 @@ if (IsA<HTMLDivElement>(*GetNode())) return RoleFromLayoutObject(ax::mojom::blink::Role::kGenericContainer); - if (IsA<HTMLMenuElement>(*GetNode())) { + if (IsA<HTMLMenuElement>(*GetNode()) || IsA<HTMLUListElement>(*GetNode()) || + IsA<HTMLOListElement>(*GetNode())) { // <menu> is a deprecated feature of HTML 5, but is included for semantic // compatibility with HTML3, and may contain list items. Exposing it as an // unordered list works better than the current HTML-AAM recommendaton of
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc index 9b0611f..5201d0d 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_object_cache_impl.cc
@@ -76,7 +76,6 @@ #include "third_party/blink/renderer/modules/accessibility/ax_image_map_link.h" #include "third_party/blink/renderer/modules/accessibility/ax_inline_text_box.h" #include "third_party/blink/renderer/modules/accessibility/ax_layout_object.h" -#include "third_party/blink/renderer/modules/accessibility/ax_list.h" #include "third_party/blink/renderer/modules/accessibility/ax_list_box.h" #include "third_party/blink/renderer/modules/accessibility/ax_list_box_option.h" #include "third_party/blink/renderer/modules/accessibility/ax_media_element.h" @@ -416,30 +415,10 @@ return objects_.at(ax_id); } -// FIXME: This probably belongs on Node. -// FIXME: This should take a const char*, but one caller passes g_null_atom. -static bool NodeHasRole(Node* node, const String& role) { - auto* element = DynamicTo<Element>(node); - if (!element) - return false; - - // TODO(accessibility) support role strings with multiple roles. - return EqualIgnoringASCIICase( - element->FastGetAttribute(html_names::kRoleAttr), role); -} - AXObject* AXObjectCacheImpl::CreateFromRenderer(LayoutObject* layout_object) { // FIXME: How could layoutObject->node() ever not be an Element? Node* node = layout_object->GetNode(); - // If the node is aria role="list" or the aria role is empty and its a - // ul/ol/dl type (it shouldn't be a list if aria says otherwise). - if (NodeHasRole(node, "list") || NodeHasRole(node, "directory") || - (NodeHasRole(node, g_null_atom) && - (IsA<HTMLUListElement>(node) || IsA<HTMLOListElement>(node) || - IsA<HTMLDListElement>(node)))) - return MakeGarbageCollected<AXList>(layout_object, *this); - // media element if (node && node->IsMediaElement()) return AccessibilityMediaElement::Create(layout_object, *this);
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn index 8c7a7c4..2f13be3b 100644 --- a/third_party/blink/renderer/platform/BUILD.gn +++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -1243,6 +1243,7 @@ "mojo/heap_mojo_receiver.h", "mojo/heap_mojo_receiver_set.h", "mojo/heap_mojo_remote.h", + "mojo/heap_mojo_remote_set.h", "mojo/heap_mojo_unique_receiver_set.h", "mojo/heap_mojo_wrapper_mode.h", "mojo/kurl_mojom_traits.h", @@ -2056,6 +2057,7 @@ "mojo/heap_mojo_associated_remote_test.cc", "mojo/heap_mojo_receiver_set_test.cc", "mojo/heap_mojo_receiver_test.cc", + "mojo/heap_mojo_remote_set_test.cc", "mojo/heap_mojo_remote_test.cc", "mojo/heap_mojo_unique_receiver_set_test.cc", "mojo/kurl_security_origin_test.cc",
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker.cc b/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker.cc index 45d7563..76b953d1 100644 --- a/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker.cc +++ b/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker.cc
@@ -157,8 +157,11 @@ for (;; offset--) { offset = break_iterator_->PreviousBreakOpportunity(offset, start); if (offset <= start || offset >= text.length() || - text[offset - 1] != kSoftHyphenCharacter) + text[offset - 1] != kSoftHyphenCharacter) { + if (IsBreakableSpace(text[offset - 1])) + return {offset, FindNonHangableEnd(text, offset - 1), false}; return {offset, false}; + } } } @@ -183,8 +186,11 @@ if (UNLIKELY(!IsSoftHyphenEnabled())) { for (;; offset++) { offset = break_iterator_->NextBreakOpportunity(offset); - if (offset >= text.length() || text[offset - 1] != kSoftHyphenCharacter) + if (offset >= text.length() || text[offset - 1] != kSoftHyphenCharacter) { + if (IsBreakableSpace(text[offset - 1])) + return {offset, FindNonHangableEnd(text, offset - 1), false}; return {offset, false}; + } } }
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource.cc b/third_party/blink/renderer/platform/loader/fetch/resource.cc index 3dd5655..b59317f 100644 --- a/third_party/blink/renderer/platform/loader/fetch/resource.cc +++ b/third_party/blink/renderer/platform/loader/fetch/resource.cc
@@ -661,11 +661,9 @@ // from volatile storage as promptly as possible" // "... History buffers MAY store such responses as part of their normal // operation." - // We allow non-secure content to be reused in history, but we do not allow - // secure content to be reused. - if (HasCacheControlNoStoreHeader() && Url().ProtocolIs("https") && - IsMainThread()) + if (HasCacheControlNoStoreHeader() && IsMainThread()) { GetMemoryCache()->Remove(this); + } } }
diff --git a/third_party/blink/renderer/platform/mojo/heap_mojo_remote_set.h b/third_party/blink/renderer/platform/mojo/heap_mojo_remote_set.h new file mode 100644 index 0000000..0fcf98eb --- /dev/null +++ b/third_party/blink/renderer/platform/mojo/heap_mojo_remote_set.h
@@ -0,0 +1,122 @@ +// 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 THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_HEAP_MOJO_REMOTE_SET_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_HEAP_MOJO_REMOTE_SET_H_ + +#include <utility> + +#include "base/memory/scoped_refptr.h" +#include "base/sequenced_task_runner.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "mojo/public/cpp/bindings/remote_set.h" +#include "third_party/blink/renderer/platform/context_lifecycle_observer.h" +#include "third_party/blink/renderer/platform/heap/heap.h" +#include "third_party/blink/renderer/platform/mojo/features.h" +#include "third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h" + +namespace blink { + +// HeapMojoRemoteSet is a wrapper for mojo::RemoteSet to be owned by a +// garbage-collected object. Blink is expected to use HeapMojoRemoteSet by +// default. HeapMojoRemoteSet must be associated with context. +// HeapMojoRemoteSet's constructor takes context as a mandatory parameter. +// HeapMojoRemoteSet resets the mojo connection when the associated +// ExecutionContext is detached. + +// TODO(crbug.com/1058076) HeapMojoWrapperMode should be removed once we ensure +// that the interface is not used after ContextDestroyed(). +template <typename Interface, + HeapMojoWrapperMode Mode = HeapMojoWrapperMode::kWithContextObserver> +class HeapMojoRemoteSet { + DISALLOW_NEW(); + + public: + using DisconnectHandler = + typename mojo::RemoteSet<Interface>::DisconnectHandler; + using Iterator = typename mojo::RemoteSet<Interface>::Iterator; + + explicit HeapMojoRemoteSet(ContextLifecycleNotifier* notifier) + : wrapper_(MakeGarbageCollected<Wrapper>(notifier)) {} + HeapMojoRemoteSet(const HeapMojoRemoteSet&) = delete; + HeapMojoRemoteSet& operator=(const HeapMojoRemoteSet&) = delete; + HeapMojoRemoteSet(HeapMojoRemoteSet&&) = default; + HeapMojoRemoteSet& operator=(HeapMojoRemoteSet&&) = default; + + // Methods to redirect to mojo::RemoteSet: + void set_disconnect_handler(DisconnectHandler handler) { + wrapper_->remote_set().set_disconnect_handler(std::move(handler)); + } + + mojo::RemoteSetElementId Add(mojo::Remote<Interface> remote) { + return wrapper_->remote_set().Add(std::move(remote)); + } + + mojo::RemoteSetElementId Add( + mojo::PendingRemote<Interface> remote, + scoped_refptr<base::SequencedTaskRunner> task_runner) { + DCHECK(task_runner); + return wrapper_->remote_set().Add(std::move(remote), task_runner); + } + + void Remove(mojo::RemoteSetElementId id) { + wrapper_->remote_set().Remove(id); + } + + bool Contains(mojo::RemoteSetElementId id) { + return wrapper_->remote_set().Contains(id); + } + + void Clear() { wrapper_->remote_set().Clear(); } + + bool empty() const { return wrapper_->remote_set().empty(); } + size_t size() const { return wrapper_->remote_set().size(); } + + Iterator begin() { return wrapper_->remote_set().begin(); } + Iterator begin() const { return wrapper_->remote_set().begin(); } + Iterator end() { return wrapper_->remote_set().end(); } + Iterator end() const { return wrapper_->remote_set().end(); } + + void Trace(Visitor* visitor) const { visitor->Trace(wrapper_); } + + private: + FRIEND_TEST_ALL_PREFIXES(HeapMojoRemoteSetGCWithContextObserverTest, + NoClearOnConservativeGC); + + // Garbage collected wrapper class to add ContextLifecycleObserver. + class Wrapper final : public GarbageCollected<Wrapper>, + public ContextLifecycleObserver { + public: + explicit Wrapper(ContextLifecycleNotifier* notifier) { + SetContextLifecycleNotifier(notifier); + } + Wrapper(const Wrapper&) = delete; + Wrapper& operator=(const Wrapper&) = delete; + Wrapper(Wrapper&&) = default; + Wrapper& operator=(Wrapper&&) = default; + + void Trace(Visitor* visitor) const override { + ContextLifecycleObserver::Trace(visitor); + } + + mojo::RemoteSet<Interface>& remote_set() { return remote_set_; } + + // ContextLifecycleObserver methods + void ContextDestroyed() override { + if (Mode == HeapMojoWrapperMode::kWithContextObserver || + (Mode == HeapMojoWrapperMode::kWithoutContextObserver && + base::FeatureList::IsEnabled(kHeapMojoUseContextObserver))) + remote_set_.Clear(); + } + + private: + mojo::RemoteSet<Interface> remote_set_; + }; + + Member<Wrapper> wrapper_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_HEAP_MOJO_REMOTE_SET_H_
diff --git a/third_party/blink/renderer/platform/mojo/heap_mojo_remote_set_test.cc b/third_party/blink/renderer/platform/mojo/heap_mojo_remote_set_test.cc new file mode 100644 index 0000000..609eabb --- /dev/null +++ b/third_party/blink/renderer/platform/mojo/heap_mojo_remote_set_test.cc
@@ -0,0 +1,232 @@ +// 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 "third_party/blink/renderer/platform/mojo/heap_mojo_remote_set.h" + +#include <utility> + +#include <string> +#include "base/test/null_task_runner.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/remote_set.h" +#include "mojo/public/interfaces/bindings/tests/sample_service.mojom-blink.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/platform/context_lifecycle_notifier.h" +#include "third_party/blink/renderer/platform/heap/heap_test_utilities.h" +#include "third_party/blink/renderer/platform/heap/persistent.h" +#include "third_party/blink/renderer/platform/heap_observer_set.h" +#include "third_party/blink/renderer/platform/mojo/heap_mojo_wrapper_mode.h" + +namespace blink { + +namespace { + +class FakeContextNotifier final : public GarbageCollected<FakeContextNotifier>, + public ContextLifecycleNotifier { + public: + FakeContextNotifier() = default; + + void AddContextLifecycleObserver( + ContextLifecycleObserver* observer) override { + observers_.AddObserver(observer); + } + void RemoveContextLifecycleObserver( + ContextLifecycleObserver* observer) override { + observers_.RemoveObserver(observer); + } + + void NotifyContextDestroyed() { + observers_.ForEachObserver([](ContextLifecycleObserver* observer) { + observer->ContextDestroyed(); + }); + } + + void Trace(Visitor* visitor) const override { + visitor->Trace(observers_); + ContextLifecycleNotifier::Trace(visitor); + } + + private: + HeapObserverSet<ContextLifecycleObserver> observers_; +}; + +template <HeapMojoWrapperMode Mode> +class HeapMojoRemoteSetGCBaseTest; + +template <HeapMojoWrapperMode Mode> +class GCOwner : public GarbageCollected<GCOwner<Mode>> { + public: + explicit GCOwner(FakeContextNotifier* context, + HeapMojoRemoteSetGCBaseTest<Mode>* test) + : remote_set_(context), test_(test) { + test_->set_is_owner_alive(true); + } + void Dispose() { test_->set_is_owner_alive(false); } + void Trace(Visitor* visitor) const { visitor->Trace(remote_set_); } + + HeapMojoRemoteSet<sample::blink::Service, Mode>& remote_set() { + return remote_set_; + } + + private: + HeapMojoRemoteSet<sample::blink::Service, Mode> remote_set_; + HeapMojoRemoteSetGCBaseTest<Mode>* test_; +}; + +template <HeapMojoWrapperMode Mode> +class HeapMojoRemoteSetGCBaseTest : public TestSupportingGC { + public: + FakeContextNotifier* context() { return context_; } + scoped_refptr<base::NullTaskRunner> task_runner() { + return null_task_runner_; + } + GCOwner<Mode>* owner() { return owner_; } + void set_is_owner_alive(bool alive) { is_owner_alive_ = alive; } + + void ClearOwner() { owner_ = nullptr; } + + protected: + void SetUp() override { + context_ = MakeGarbageCollected<FakeContextNotifier>(); + owner_ = MakeGarbageCollected<GCOwner<Mode>>(context(), this); + } + void TearDown() override { + owner_ = nullptr; + PreciselyCollectGarbage(); + } + + Persistent<FakeContextNotifier> context_; + Persistent<GCOwner<Mode>> owner_; + bool is_owner_alive_ = false; + scoped_refptr<base::NullTaskRunner> null_task_runner_ = + base::MakeRefCounted<base::NullTaskRunner>(); +}; + +} // namespace + +class HeapMojoRemoteSetGCWithContextObserverTest + : public HeapMojoRemoteSetGCBaseTest< + HeapMojoWrapperMode::kWithContextObserver> {}; +class HeapMojoRemoteSetGCWithoutContextObserverTest + : public HeapMojoRemoteSetGCBaseTest< + HeapMojoWrapperMode::kForceWithoutContextObserver> {}; + +// GC the HeapMojoRemoteSet with context observer and verify that the remote +// is no longer part of the set, and that the service was deleted. +TEST_F(HeapMojoRemoteSetGCWithContextObserverTest, RemovesRemote) { + auto& remote_set = owner()->remote_set(); + auto remote = mojo::PendingRemote<sample::blink::Service>( + mojo::MessagePipe().handle0, 0); + + mojo::RemoteSetElementId rid = + remote_set.Add(std::move(remote), task_runner()); + EXPECT_TRUE(remote_set.Contains(rid)); + + remote_set.Remove(rid); + + EXPECT_FALSE(remote_set.Contains(rid)); +} + +// Check that the wrapper does not outlive the owner when ConservativeGC finds +// the wrapper. +TEST_F(HeapMojoRemoteSetGCWithContextObserverTest, NoClearOnConservativeGC) { + auto* wrapper = owner_->remote_set().wrapper_.Get(); + + auto remote = mojo::PendingRemote<sample::blink::Service>( + mojo::MessagePipe().handle0, 0); + + mojo::RemoteSetElementId rid = + owner()->remote_set().Add(std::move(remote), task_runner()); + EXPECT_TRUE(wrapper->remote_set().Contains(rid)); + + ClearOwner(); + EXPECT_TRUE(is_owner_alive_); + + ConservativelyCollectGarbage(); + + EXPECT_TRUE(wrapper->remote_set().Contains(rid)); + EXPECT_TRUE(is_owner_alive_); +} + +// GC the HeapMojoRemoteSet without context observer and verify that the +// remote is no longer part of the set, and that the service was deleted. +TEST_F(HeapMojoRemoteSetGCWithoutContextObserverTest, RemovesRemote) { + auto& remote_set = owner()->remote_set(); + auto remote = mojo::PendingRemote<sample::blink::Service>( + mojo::MessagePipe().handle0, 0); + + mojo::RemoteSetElementId rid = + remote_set.Add(std::move(remote), task_runner()); + EXPECT_TRUE(remote_set.Contains(rid)); + + remote_set.Remove(rid); + + EXPECT_FALSE(remote_set.Contains(rid)); +} + +// GC the HeapMojoRemoteSet with context observer and verify that the remote +// is no longer part of the set, and that the service was deleted. +TEST_F(HeapMojoRemoteSetGCWithContextObserverTest, ClearLeavesSetEmpty) { + auto& remote_set = owner()->remote_set(); + auto remote = mojo::PendingRemote<sample::blink::Service>( + mojo::MessagePipe().handle0, 0); + + mojo::RemoteSetElementId rid = + remote_set.Add(std::move(remote), task_runner()); + EXPECT_TRUE(remote_set.Contains(rid)); + + remote_set.Clear(); + + EXPECT_FALSE(remote_set.Contains(rid)); +} + +// GC the HeapMojoRemoteSet without context observer and verify that the +// remote is no longer part of the set, and that the service was deleted. +TEST_F(HeapMojoRemoteSetGCWithoutContextObserverTest, ClearLeavesSetEmpty) { + auto& remote_set = owner()->remote_set(); + auto remote = mojo::PendingRemote<sample::blink::Service>( + mojo::MessagePipe().handle0, 0); + + mojo::RemoteSetElementId rid = + remote_set.Add(std::move(remote), task_runner()); + EXPECT_TRUE(remote_set.Contains(rid)); + + remote_set.Clear(); + + EXPECT_FALSE(remote_set.Contains(rid)); +} + +// Add several remote and confirm that remote_set holds properly. +TEST_F(HeapMojoRemoteSetGCWithContextObserverTest, AddSeveralRemoteSet) { + auto& remote_set = owner()->remote_set(); + + EXPECT_TRUE(remote_set.empty()); + EXPECT_EQ(remote_set.size(), 0u); + + auto remote_1 = mojo::PendingRemote<sample::blink::Service>( + mojo::MessagePipe().handle0, 0); + mojo::RemoteSetElementId rid_1 = + remote_set.Add(std::move(remote_1), task_runner()); + EXPECT_TRUE(remote_set.Contains(rid_1)); + EXPECT_FALSE(remote_set.empty()); + EXPECT_EQ(remote_set.size(), 1u); + + auto remote_2 = mojo::PendingRemote<sample::blink::Service>( + mojo::MessagePipe().handle0, 0); + mojo::RemoteSetElementId rid_2 = + remote_set.Add(std::move(remote_2), task_runner()); + EXPECT_TRUE(remote_set.Contains(rid_1)); + EXPECT_TRUE(remote_set.Contains(rid_2)); + EXPECT_FALSE(remote_set.empty()); + EXPECT_EQ(remote_set.size(), 2u); + + remote_set.Clear(); + + EXPECT_FALSE(remote_set.Contains(rid_1)); + EXPECT_FALSE(remote_set.Contains(rid_2)); + EXPECT_TRUE(remote_set.empty()); + EXPECT_EQ(remote_set.size(), 0u); +} + +} // namespace blink
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index 8153f40..6e9e6f79 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -2471,11 +2471,7 @@ # ====== New tests from wpt-importer added here ====== crbug.com/626703 external/wpt/html/interaction/focus/document-level-focus-apis/document-has-system-focus.html [ Timeout ] -crbug.com/626703 external/wpt/css/css-backgrounds/box-shadow-radius-000.html [ Failure ] -crbug.com/626703 virtual/composite-bgcolor-animation/external/wpt/css/css-backgrounds/box-shadow-radius-000.html [ Failure ] -crbug.com/626703 virtual/composite-bgcolor-animation/external/wpt/css/css-backgrounds/box-shadow-radius-001.html [ Failure ] crbug.com/626703 [ Mac11.0 ] virtual/threaded/external/wpt/animation-worklet/worklet-animation-set-keyframes.https.html [ Failure ] -crbug.com/626703 external/wpt/css/css-backgrounds/box-shadow-radius-001.html [ Failure ] crbug.com/626703 external/wpt/css/css-images/image-orientation/image-orientation-exif-png.html [ Failure ] crbug.com/626703 external/wpt/css/css-conditional/css-supports-040.xht [ Failure ] crbug.com/626703 external/wpt/css/css-conditional/css-supports-042.xht [ Failure ] @@ -3233,13 +3229,22 @@ crbug.com/697971 [ Mac10.12 ] virtual/text-antialias/selection/flexbox-selection-nested.html [ Skip ] crbug.com/697971 [ Mac10.12 ] virtual/text-antialias/selection/flexbox-selection.html [ Skip ] -# [composite-bgcolor-animation] -crbug.com/1148369 virtual/composite-bgcolor-animation/external/wpt/css/css-backgrounds/border-bottom-left-radius-010.xht [ Failure Pass ] -crbug.com/1148369 virtual/composite-bgcolor-animation/external/wpt/css/css-backgrounds/border-bottom-right-radius-010.xht [ Failure Pass ] -crbug.com/1148369 virtual/composite-bgcolor-animation/external/wpt/css/css-backgrounds/border-radius-clip-001.html [ Failure Pass ] -crbug.com/1148369 virtual/composite-bgcolor-animation/external/wpt/css/css-backgrounds/border-radius-clip-002.htm [ Failure Pass ] -crbug.com/1148369 virtual/composite-bgcolor-animation/external/wpt/css/css-backgrounds/border-top-left-radius-010.xht [ Failure Pass ] -crbug.com/1148369 virtual/composite-bgcolor-animation/external/wpt/css/css-backgrounds/border-top-right-radius-010.xht [ Failure Pass ] +crbug.com/1160916 virtual/composite-bgcolor-animation/external/wpt/css/css-backgrounds/border-bottom-left-radius-010.xht [ Failure Pass ] +crbug.com/1160916 external/wpt/css/css-backgrounds/border-bottom-left-radius-010.xht [ Failure Pass ] +crbug.com/1160916 virtual/composite-bgcolor-animation/external/wpt/css/css-backgrounds/border-bottom-right-radius-010.xht [ Failure Pass ] +crbug.com/1160916 external/wpt/css/css-backgrounds/border-bottom-right-radius-010.xht [ Failure Pass ] +crbug.com/1160916 virtual/composite-bgcolor-animation/external/wpt/css/css-backgrounds/border-radius-clip-001.html [ Failure Pass ] +crbug.com/1160916 external/wpt/css/css-backgrounds/border-radius-clip-001.html [ Failure Pass ] +crbug.com/1160916 virtual/composite-bgcolor-animation/external/wpt/css/css-backgrounds/border-radius-clip-002.htm [ Failure Pass ] +crbug.com/1160916 external/wpt/css/css-backgrounds/border-radius-clip-002.htm [ Failure Pass ] +crbug.com/1160916 virtual/composite-bgcolor-animation/external/wpt/css/css-backgrounds/border-top-left-radius-010.xht [ Failure Pass ] +crbug.com/1160916 external/wpt/css/css-backgrounds/border-top-left-radius-010.xht [ Failure Pass ] +crbug.com/1160916 virtual/composite-bgcolor-animation/external/wpt/css/css-backgrounds/border-top-right-radius-010.xht [ Failure Pass ] +crbug.com/1160916 external/wpt/css/css-backgrounds/border-top-right-radius-010.xht [ Failure Pass ] +crbug.com/1160916 virtual/composite-bgcolor-animation/external/wpt/css/css-backgrounds/box-shadow-radius-000.html [ Failure ] +crbug.com/1160916 external/wpt/css/css-backgrounds/box-shadow-radius-000.html [ Failure ] +crbug.com/1160916 virtual/composite-bgcolor-animation/external/wpt/css/css-backgrounds/box-shadow-radius-001.html [ Failure ] +crbug.com/1160916 external/wpt/css/css-backgrounds/box-shadow-radius-001.html [ Failure ] # [css-grid] crbug.com/921722 external/wpt/css/css-grid/abspos/descendant-static-position-001.html [ Failure ] @@ -5884,10 +5889,6 @@ crbug.com/681468 fast/forms/suggestion-picker/date-suggestion-picker-appearance-zoom125.html [ Failure Pass ] crbug.com/681468 fast/forms/suggestion-picker/date-suggestion-picker-appearance-zoom200.html [ Failure Pass ] -# Other fast/forms/suggestion-picker/time-suggestion-picker-appearance.html -# failures on Win7 caused by focus appearing on the wrong element. -crbug.com/1160594 [ Win7 ] fast/forms/suggestion-picker/time-suggestion-picker-appearance.html [ Failure ] - # These tests will only run in the virtual test suite where the frequency # capping for overlay popup detection is disabled. This eliminates the need # for waitings in web tests to trigger a detection event. @@ -5920,5 +5921,3 @@ # Sheriff 2020-12-14 crbug.com/1046784 http/tests/devtools/console/console-context-selector.js [ Pass Timeout ] - -
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json index 968f3e6..997b5f4 100644 --- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json +++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -6376,6 +6376,20 @@ ] }, "selectors": { + "focus-visible-006-manual.html": [ + "974abc0024e3153008e944ff706156efcb9948e2", + [ + null, + {} + ] + ], + "focus-visible-008-manual.html": [ + "53306a90bbcae67ca65feefce3e4d3758d1a4ecd", + [ + null, + {} + ] + ], "hover-001-manual.html": [ "87d7af91b6994371b72ff33923ea1cacc65c8b5b", [ @@ -185743,6 +185757,14 @@ "2b0f294c3441f1cf25a6678b9ce0748258582ea1", [] ], + "flex-item-compressible-001-expected.txt": [ + "4ddf85ea6cc7a830f0afc262097795e35b13ef4d", + [] + ], + "flex-item-compressible-002-expected.txt": [ + "cd5115045af41b8997e8261e2923b4c8affcfabe", + [] + ], "flex-lines": { "multi-line-wrap-reverse-column-reverse-ref.html": [ "38366a62f7e6b7df30b2da814cf178ebba5a83fa", @@ -222355,6 +222377,10 @@ "a3ffdd005afadb784b1b64a12caa190a66271018", [] ], + "location-prevent-extensions-expected.txt": [ + "9f79369fbc41bf564547199727d8a4b8256b7247", + [] + ], "location-protocol-setter-non-broken-expected.txt": [ "4d1f8a9abd4d46f445ceb66ec1f65fe65425aabd", [] @@ -263878,10 +263904,6 @@ "905836ca6bc3388a39e073ec478bf50db6982407", [] ], - "002.worker-expected.txt": [ - "d73864c4701f9428e561ccbf2b64702ba87fe845", - [] - ], "003-expected.txt": [ "e72a68ecc1b03d21f201b48630cea1a481978c8d", [] @@ -263893,10 +263915,6 @@ "004-expected.txt": [ "d834acc47ce940bf6d1c9eeb9c36bd76d49b2a33", [] - ], - "004.any.sharedworker-expected.txt": [ - "ba2bc24d5f5458cc472838cd8698f2a3117a1f5c", - [] ] }, "multiple-workers": { @@ -264085,6 +264103,10 @@ "aae86a0b055346f8439cbec4f98df8d08d472368", [] ], + "Worker-run-forever-during-top-level-await.js": [ + "a1c8b32b619753941999c7f41e9169816c396d36", + [] + ], "Worker-run-forever.js": [ "2d822d407f4c83fe7321cbd4cb1168626de05eea", [] @@ -293155,6 +293177,20 @@ {} ] ], + "flex-item-compressible-001.html": [ + "9725512cd8027b8b8fe4bd966f96db1b5242456e", + [ + null, + {} + ] + ], + "flex-item-compressible-002.html": [ + "d1a14b9bfe3cbb05f598dee987ccd41ec5d4013e", + [ + null, + {} + ] + ], "flex-item-contains-strict.html": [ "25849cc64b2f10cf2d319e1a2f750f32d64359b5", [ @@ -315005,15 +315041,6 @@ } ] ], - "focus-visible-006.html": [ - "4b034b2e27962810bd9357ff88e5fe4587e56d25", - [ - null, - { - "testdriver": true - } - ] - ], "focus-visible-007.html": [ "95a27006e62076909f63f9946e1f2d7f8331732a", [ @@ -315023,15 +315050,6 @@ } ] ], - "focus-visible-008.html": [ - "75f676f53f3319cb672195343c77cc7ebd94eee4", - [ - null, - { - "testdriver": true - } - ] - ], "focus-visible-009.html": [ "4dae6407adac3971db6064822643332cf7f1722d", [ @@ -346182,6 +346200,13 @@ {} ] ], + "location-non-configurable-toString-valueOf.html": [ + "80760ac9e44e2513017b583329e1a9cddeccdb60", + [ + null, + {} + ] + ], "location-origin-idna.sub.window.js": [ "83b030f88651aeb66998b724ffcec31ad72801bc", [ @@ -346196,6 +346221,13 @@ {} ] ], + "location-prevent-extensions.html": [ + "a8c777aded82c384e8106db824b2b175bfe15774", + [ + null, + {} + ] + ], "location-protocol-setter-non-broken-weird.html": [ "78ba5f6e401dadf43034c772118bba466e678131", [ @@ -346224,6 +346256,13 @@ {} ] ], + "location-prototype-no-toString-valueOf.html": [ + "56316320af59dd1531c661fb854643736cf3ef6e", + [ + null, + {} + ] + ], "location-prototype-setting-cross-origin-domain.sub.html": [ "1e677b03653a7d391653865a02ab45096fcff4e9", [ @@ -346604,7 +346643,7 @@ ] ], "cross-origin-objects.html": [ - "3b7c7832d7e7f215ccc810ae583f8cfdf925d497", + "577129502426989888a1791f78df46b6ed23c55e", [ null, { @@ -347284,6 +347323,13 @@ ] }, "the-windowproxy-exotic-object": { + "windowproxy-prevent-extensions.html": [ + "97a156290a1c5ad15a36f24b0009d9f599c8be3e", + [ + null, + {} + ] + ], "windowproxy-prototype-setting-cross-origin-domain.sub.html": [ "a5ae78cc87dc8698e819b5132fb1514ac4f25860", [ @@ -446148,7 +446194,7 @@ ] ], "Worker-terminate-forever-during-evaluation.html": [ - "4513c5b53790d11b4e26b0fd38a4e9bee9f1c9ad", + "ab66f29289f21d6821a3edccdec9ff3512f86919", [ null, {} @@ -448176,7 +448222,7 @@ ] ], "002.worker.js": [ - "065cc6e6f1705fc0157ee80ccd8daee4af4ac534", + "8eb41c23fdcfc2a81a135582648b93ebfde40d22", [ "workers/semantics/interface-objects/002.worker.html", {} @@ -448197,7 +448243,7 @@ ] ], "004.any.js": [ - "2cbb4b54256b4587a9bdc139e03f5e7b07756915", + "963a9962d11f36f2a7fc01854b30d3a7477b7ef8", [ "workers/semantics/interface-objects/004.any.sharedworker.html", {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/flex-item-compressible-001-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-flexbox/flex-item-compressible-001-expected.txt new file mode 100644 index 0000000..4ddf85e --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/flex-item-compressible-001-expected.txt
@@ -0,0 +1,28 @@ +This is a testharness.js-based test. +PASS .flexbox 1 +PASS .flexbox 2 +PASS .flexbox 3 +PASS .flexbox 4 +PASS .flexbox 5 +PASS .flexbox 6 +PASS .flexbox 7 +PASS .flexbox 8 +PASS .flexbox 9 +PASS .flexbox 10 +FAIL .flexbox 11 assert_equals: +<div class="flexbox"> + <div class="spacer"></div> + <input type="text" class="test3" data-expected-width="140"> + </div> +width expected 140 but got 100 +FAIL .flexbox 12 assert_equals: +<div class="flexbox"> + <div class="spacer"></div> + <input type="range" class="test3" data-expected-width="140"> + </div> +width expected 140 but got 100 +PASS .flexbox 13 +PASS .flexbox 14 +PASS .flexbox 15 +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/flex-item-compressible-001.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/flex-item-compressible-001.html new file mode 100644 index 0000000..9725512c --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/flex-item-compressible-001.html
@@ -0,0 +1,149 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> + <meta charset="utf-8"> + <title>CSS Flexbox Test: Testing automatic minimun size of <input> flex items in a row flex container</title> + <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com"> + <link rel="author" title="Mozilla" href="https://www.mozilla.org/"> + <link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#min-size-auto"> + <link rel="help" href="https://drafts.csswg.org/css-sizing-3/#replaced-percentage-min-contribution"> + <link rel="help" href="https://drafts.csswg.org/css-sizing-3/#min-content-zero"> + <link rel="stylesheet" href="/fonts/ahem.css"> + <meta name="assert" content="This test verifies that an <input> flex item should resolve its percentage part of main size to zero when computing specified size suggestion."> + + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/resources/check-layout-th.js"></script> + + <style> + .flexbox { + display: flex; + width: 300px; + height: 40px; + border: 1px solid black; + margin-bottom: 5px; + } + .spacer { + /* Just to occupy some space, so that the flex algorithm will try to shrink + the <input> element below its percentage specified width. */ + flex: 0 0 200px; + background: lightgray; + } + input { + font: 20px/1 Ahem; + background: lightblue; + /* Get rid of native theming and UA default styles. */ + appearance: none; + border: 0; + padding: 0; + margin: 0; + } + .test1 { + width: 100%; + } + .test2 { + width: calc(100%); + } + .test3 { + width: calc(140px + 100%); + } + </style> + + <body onload="checkLayout('.flexbox')"> + <p>Test1: "width: 100%"</p> + <div class="flexbox"> + <div class="spacer"></div> + <input type="text" + class="test1" data-expected-width="100"> + </div> + + <div class="flexbox"> + <div class="spacer"></div> + <input type="range" + class="test1" data-expected-width="100"> + </div> + + <div class="flexbox"> + <div class="spacer"></div> + <input type="button" value="XXXXXXX" + class="test1" data-expected-width="140"> + </div> + + <div class="flexbox"> + <div class="spacer"></div> + <input type="submit" value="XXXXXXX" + class="test1" data-expected-width="140"> + </div> + + <div class="flexbox"> + <div class="spacer"></div> + <input type="reset" value="XXXXXXX" + class="test1" data-expected-width="140"> + </div> + + <p>Test2: "width: calc(100%)"</p> + <div class="flexbox"> + <div class="spacer"></div> + <input type="text" + class="test2" data-expected-width="100"> + </div> + + <div class="flexbox"> + <div class="spacer"></div> + <input type="range" + class="test2" data-expected-width="100"> + </div> + + <div class="flexbox"> + <div class="spacer"></div> + <input type="button" value="XXXXXXX" + class="test2" data-expected-width="140"> + </div> + + <div class="flexbox"> + <div class="spacer"></div> + <input type="submit" value="XXXXXXX" + class="test2" data-expected-width="140"> + </div> + + <div class="flexbox"> + <div class="spacer"></div> + <input type="reset" value="XXXXXXX" + class="test2" data-expected-width="140"> + </div> + + <p>Test3: "width: calc(140px + 100%)"</p> + <div class="flexbox"> + <div class="spacer"></div> + <input type="text" + class="test3" data-expected-width="140"> + </div> + + <div class="flexbox"> + <div class="spacer"></div> + <input type="range" + class="test3" data-expected-width="140"> + </div> + + <div class="flexbox"> + <div class="spacer"></div> + <input type="button" value="XXXXXXX" + class="test3" data-expected-width="140"> + </div> + + <div class="flexbox"> + <div class="spacer"></div> + <input type="submit" value="XXXXXXX" + class="test3" data-expected-width="140"> + </div> + + <div class="flexbox"> + <div class="spacer"></div> + <input type="reset" value="XXXXXXX" + class="test3" data-expected-width="140"> + </div> + </body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/flex-item-compressible-002-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-flexbox/flex-item-compressible-002-expected.txt new file mode 100644 index 0000000..cd51150 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/flex-item-compressible-002-expected.txt
@@ -0,0 +1,73 @@ +This is a testharness.js-based test. +PASS .flexbox 1 +PASS .flexbox 2 +FAIL .flexbox 3 assert_equals: +<div class="flexbox"> + <div class="spacer"></div> + <input type="button" value="XXXXXXX" class="test1" data-expected-height="140"> + </div> +height expected 140 but got 100 +FAIL .flexbox 4 assert_equals: +<div class="flexbox"> + <div class="spacer"></div> + <input type="submit" value="XXXXXXX" class="test1" data-expected-height="140"> + </div> +height expected 140 but got 100 +FAIL .flexbox 5 assert_equals: +<div class="flexbox"> + <div class="spacer"></div> + <input type="reset" value="XXXXXXX" class="test1" data-expected-height="140"> + </div> +height expected 140 but got 100 +PASS .flexbox 6 +PASS .flexbox 7 +FAIL .flexbox 8 assert_equals: +<div class="flexbox"> + <div class="spacer"></div> + <input type="button" value="XXXXXXX" class="test2" data-expected-height="140"> + </div> +height expected 140 but got 100 +FAIL .flexbox 9 assert_equals: +<div class="flexbox"> + <div class="spacer"></div> + <input type="submit" value="XXXXXXX" class="test2" data-expected-height="140"> + </div> +height expected 140 but got 100 +FAIL .flexbox 10 assert_equals: +<div class="flexbox"> + <div class="spacer"></div> + <input type="reset" value="XXXXXXX" class="test2" data-expected-height="140"> + </div> +height expected 140 but got 100 +FAIL .flexbox 11 assert_equals: +<div class="flexbox"> + <div class="spacer"></div> + <input type="text" class="test3" data-expected-height="140"> + </div> +height expected 140 but got 100 +FAIL .flexbox 12 assert_equals: +<div class="flexbox"> + <div class="spacer"></div> + <input type="range" class="test3" data-expected-height="140"> + </div> +height expected 140 but got 100 +FAIL .flexbox 13 assert_equals: +<div class="flexbox"> + <div class="spacer"></div> + <input type="button" value="XXXXXXX" class="test3" data-expected-height="140"> + </div> +height expected 140 but got 100 +FAIL .flexbox 14 assert_equals: +<div class="flexbox"> + <div class="spacer"></div> + <input type="submit" value="XXXXXXX" class="test3" data-expected-height="140"> + </div> +height expected 140 but got 100 +FAIL .flexbox 15 assert_equals: +<div class="flexbox"> + <div class="spacer"></div> + <input type="reset" value="XXXXXXX" class="test3" data-expected-height="140"> + </div> +height expected 140 but got 100 +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/flex-item-compressible-002.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/flex-item-compressible-002.html new file mode 100644 index 0000000..d1a14b9 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/flex-item-compressible-002.html
@@ -0,0 +1,151 @@ +<!DOCTYPE html> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html> + <meta charset="utf-8"> + <title>CSS Flexbox Test: Testing automatic minimun size of <input> flex items in a column flex container</title> + <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com"> + <link rel="author" title="Mozilla" href="https://www.mozilla.org/"> + <link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#min-size-auto"> + <link rel="help" href="https://drafts.csswg.org/css-sizing-3/#replaced-percentage-min-contribution"> + <link rel="help" href="https://drafts.csswg.org/css-sizing-3/#min-content-zero"> + <link rel="stylesheet" href="/fonts/ahem.css"> + <meta name="assert" content="This test verifies that an <input> flex item should resolve its percentage part of main size to zero when computing specified size suggestion."> + + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/resources/check-layout-th.js"></script> + + <style> + .flexbox { + display: inline-flex; + flex-direction: column; + width: 40px; + height: 300px; + border: 1px solid black; + margin-bottom: 40px; + } + .spacer { + /* Just to occupy some space, so that the flex algorithm will try to shrink + the <input> element below its percentage specified height. */ + flex: 0 0 200px; + background: lightgray; + } + input { + writing-mode: vertical-lr; + font: 20px/1 Ahem; + background: lightblue; + /* Get rid of native theming and UA default styles. */ + appearance: none; + border: 0; + padding: 0; + margin: 0; + } + .test1 { + height: 100%; + } + .test2 { + height: calc(100%); + } + .test3 { + height: calc(140px + 100%); + } + </style> + + <body onload="checkLayout('.flexbox')"> + <p>Test1: "height: 100%"</p> + <div class="flexbox"> + <div class="spacer"></div> + <input type="text" + class="test1" data-expected-height="100"> + </div> + + <div class="flexbox"> + <div class="spacer"></div> + <input type="range" + class="test1" data-expected-height="100"> + </div> + + <div class="flexbox"> + <div class="spacer"></div> + <input type="button" value="XXXXXXX" + class="test1" data-expected-height="140"> + </div> + + <div class="flexbox"> + <div class="spacer"></div> + <input type="submit" value="XXXXXXX" + class="test1" data-expected-height="140"> + </div> + + <div class="flexbox"> + <div class="spacer"></div> + <input type="reset" value="XXXXXXX" + class="test1" data-expected-height="140"> + </div> + + <p>Test2: "height: calc(100%)"</p> + <div class="flexbox"> + <div class="spacer"></div> + <input type="text" + class="test2" data-expected-height="100"> + </div> + + <div class="flexbox"> + <div class="spacer"></div> + <input type="range" + class="test2" data-expected-height="100"> + </div> + + <div class="flexbox"> + <div class="spacer"></div> + <input type="button" value="XXXXXXX" + class="test2" data-expected-height="140"> + </div> + + <div class="flexbox"> + <div class="spacer"></div> + <input type="submit" value="XXXXXXX" + class="test2" data-expected-height="140"> + </div> + + <div class="flexbox"> + <div class="spacer"></div> + <input type="reset" value="XXXXXXX" + class="test2" data-expected-height="140"> + </div> + + <p>Test3: "height: calc(140px + 100%)"</p> + <div class="flexbox"> + <div class="spacer"></div> + <input type="text" + class="test3" data-expected-height="140"> + </div> + + <div class="flexbox"> + <div class="spacer"></div> + <input type="range" + class="test3" data-expected-height="140"> + </div> + + <div class="flexbox"> + <div class="spacer"></div> + <input type="button" value="XXXXXXX" + class="test3" data-expected-height="140"> + </div> + + <div class="flexbox"> + <div class="spacer"></div> + <input type="submit" value="XXXXXXX" + class="test3" data-expected-height="140"> + </div> + + <div class="flexbox"> + <div class="spacer"></div> + <input type="reset" value="XXXXXXX" + class="test3" data-expected-height="140"> + </div> + </body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/hyphens/hyphens-none-014.html b/third_party/blink/web_tests/external/wpt/css/css-text/hyphens/hyphens-none-014.html new file mode 100644 index 0000000..9f5c688 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-text/hyphens/hyphens-none-014.html
@@ -0,0 +1,32 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Text level 3 Test: Line breaking with floats and disabled hyphenation </title> +<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com"/> +<link rel="help" title="5.4. Hyphenation: the hyphens property" href="https://drafts.csswg.org/css-text-3/#hyphenation"> +<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-hyphens-none"> +<link rel="match" href="../../reference/ref-filled-green-100px-square-only.html"> +<meta name="assert" content="A span with hypens 'none' is wrapped based on the available space left by a float image."> +<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" /> +<style> +div { font: 20px/1 Ahem; } +img { float:right; } +.test { + max-width: 100px; + color: green; +} +span { hyphens: none; } +.ref { + position: absolute; + background: green linear-gradient(red, red) 2ch 0/3ch 3ch no-repeat; + color: red; + width: 100px; + height: 100px; + z-index: -1; +} +</style> +<p>Test passes if there is a filled green square.</p> +<div class="ref">XX<br>X</br></div> +<div class="test"> + <img src="/css/support/60x60-green.png" alt=""> + <span>XX X</span> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-text/hyphens/hyphens-none-015.html b/third_party/blink/web_tests/external/wpt/css/css-text/hyphens/hyphens-none-015.html new file mode 100644 index 0000000..10c90b1 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-text/hyphens/hyphens-none-015.html
@@ -0,0 +1,32 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Text level 3 Test: Line breaking with floats and disabled hyphenation </title> +<link rel="author" title="Javier Fernandez Garcia-Boente" href="mailto:jfernandez@igalia.com"/> +<link rel="help" title="5.4. Hyphenation: the hyphens property" href="https://drafts.csswg.org/css-text-3/#hyphenation"> +<link rel="help" href="https://drafts.csswg.org/css-text-3/#valdef-hyphens-manual"> +<link rel="match" href="../../reference/ref-filled-green-200px-square.html"> +<meta name="assert" content="A span with hypens 'none' is wrapped in multiple lines based on the available space left by a float image."> +<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" /> +<style> +div { font: 20px/1 Ahem; } +img { float:right; } +.test { + max-width: 200px; + color: green; +} +span { hyphens: none; } +.ref { + position: absolute; + background: green linear-gradient(red, red) 7ch 0/3ch 3ch no-repeat; + color: red; + width: 200px; + height: 200px; + z-index: -1; +} +</style> +<p>Test passes if there is a filled green square and <strong>no red</strong>.</p> +<div class="ref">XX X<br>XXX</br>XXXX XX<br>XXX</div> +<div class="test"> + <img src="/css/support/60x60-green.png" alt=""> + <span>XX X XXX XXXX XX XXX</span> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/html/browsers/history/the-location-interface/location-non-configurable-toString-valueOf.html b/third_party/blink/web_tests/external/wpt/html/browsers/history/the-location-interface/location-non-configurable-toString-valueOf.html new file mode 100644 index 0000000..80760ac9 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/html/browsers/history/the-location-interface/location-non-configurable-toString-valueOf.html
@@ -0,0 +1,42 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Same-origin Location objects have non-configurable "toString" and "valueOf" properties</title> +<link rel="help" href="https://html.spec.whatwg.org/multipage/history.html#location-defineownproperty"> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<script> +"use strict"; + +test(() => { + assert_own_property(location, "toString"); + const origToString = location.toString; + + assert_throws_js(TypeError, () => { + Object.defineProperty(location, "toString", { + get() {}, + set(_v) {}, + enumerable: true, + configurable: true, + }); + }); + + assert_equals(location.toString, origToString); +}, "'toString' redefinition with accessor fails"); + +test(() => { + assert_own_property(location, "valueOf"); + const origValueOf = location.valueOf; + + assert_throws_js(TypeError, () => { + Object.defineProperty(location, "valueOf", { + get() {}, + enumerable: false, + configurable: true, + }); + }); + + assert_equals(location.valueOf, origValueOf); +}, "'valueOf' redefinition with accessor fails"); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/browsers/history/the-location-interface/location-prevent-extensions-expected.txt b/third_party/blink/web_tests/external/wpt/html/browsers/history/the-location-interface/location-prevent-extensions-expected.txt new file mode 100644 index 0000000..9f79369 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/html/browsers/history/the-location-interface/location-prevent-extensions-expected.txt
@@ -0,0 +1,7 @@ +This is a testharness.js-based test. +FAIL Object.preventExtensions throws a TypeError assert_throws_js: function "() => { + Object.preventExtensions(location); + }" did not throw +FAIL Reflect.preventExtensions returns false assert_false: expected false got true +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/external/wpt/html/browsers/history/the-location-interface/location-prevent-extensions.html b/third_party/blink/web_tests/external/wpt/html/browsers/history/the-location-interface/location-prevent-extensions.html new file mode 100644 index 0000000..a8c777a --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/html/browsers/history/the-location-interface/location-prevent-extensions.html
@@ -0,0 +1,21 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>[[PreventExtensions]] on a Location object should return false</title> +<link rel="help" href="https://html.spec.whatwg.org/multipage/history.html#location-preventextensions"> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<script> +"use strict"; + +test(() => { + assert_throws_js(TypeError, () => { + Object.preventExtensions(location); + }); +}, "Object.preventExtensions throws a TypeError"); + +test(() => { + assert_false(Reflect.preventExtensions(location)); +}, "Reflect.preventExtensions returns false"); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/browsers/history/the-location-interface/location-prototype-no-toString-valueOf.html b/third_party/blink/web_tests/external/wpt/html/browsers/history/the-location-interface/location-prototype-no-toString-valueOf.html new file mode 100644 index 0000000..5631632 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/html/browsers/history/the-location-interface/location-prototype-no-toString-valueOf.html
@@ -0,0 +1,55 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Location.prototype objects don't have own "toString" and "valueOf" properties</title> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<script> +"use strict"; + +test(t => { + assert_not_own_property(Location.prototype, "toString"); + t.add_cleanup(() => { delete Location.prototype.toString; }); + + let val; + Object.defineProperty(Location.prototype, "toString", { + get: () => val, + set: newVal => { val = newVal; }, + enumerable: false, + configurable: true, + }); + + Location.prototype.toString = 2; + assert_equals(Location.prototype.toString, 2); +}, "'toString' accessor property is defined"); + +test(t => { + assert_not_own_property(Location.prototype, "toString"); + t.add_cleanup(() => { delete Location.prototype.toString; }); + + Location.prototype.toString = 4; + assert_equals(Location.prototype.toString, 4); +}, "'toString' data property is created via [[Set]]"); + +test(t => { + assert_not_own_property(Location.prototype, "valueOf"); + t.add_cleanup(() => { delete Location.prototype.valueOf; }); + + Object.defineProperty(Location.prototype, "valueOf", { + get: () => 6, + enumerable: true, + configurable: true, + }); + + assert_equals(Location.prototype.valueOf, 6); +}, "'valueOf' accessor property is defined"); + +test(t => { + assert_not_own_property(Location.prototype, "valueOf"); + t.add_cleanup(() => { delete Location.prototype.valueOf; }); + + Location.prototype.valueOf = 8; + assert_equals(Object.getOwnPropertyDescriptor(Location.prototype, "valueOf").value, 8); +}, "'valueOf' data property is created via [[Set]]"); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/html/browsers/origin/cross-origin-objects/cross-origin-objects.html b/third_party/blink/web_tests/external/wpt/html/browsers/origin/cross-origin-objects/cross-origin-objects.html index 3b7c783..57712950 100644 --- a/third_party/blink/web_tests/external/wpt/html/browsers/origin/cross-origin-objects/cross-origin-objects.html +++ b/third_party/blink/web_tests/external/wpt/html/browsers/origin/cross-origin-objects/cross-origin-objects.html
@@ -379,7 +379,11 @@ "preventExtensions on cross-origin Window should throw"); assert_throws_js(TypeError, function() { Object.preventExtensions(win.location) }, "preventExtensions on cross-origin Location should throw"); -}, "[[PreventExtensions]] should throw for cross-origin objects"); + assert_false(Reflect.preventExtensions(win), + "Reflect.preventExtensions on cross-origin Window"); + assert_false(Reflect.preventExtensions(win.location), + "Reflect.preventExtensions on cross-origin Location"); +}, "[[PreventExtensions]] should return false cross-origin objects"); /* * [[GetOwnProperty]]
diff --git a/third_party/blink/web_tests/external/wpt/html/browsers/the-windowproxy-exotic-object/windowproxy-prevent-extensions.html b/third_party/blink/web_tests/external/wpt/html/browsers/the-windowproxy-exotic-object/windowproxy-prevent-extensions.html new file mode 100644 index 0000000..97a1562 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/html/browsers/the-windowproxy-exotic-object/windowproxy-prevent-extensions.html
@@ -0,0 +1,21 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>[[PreventExtensions]] on a WindowProxy object should return false</title> +<link rel="help" href="https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-preventextensions"> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<script> +"use strict"; + +test(() => { + assert_throws_js(TypeError, () => { + Object.preventExtensions(window); + }); +}, "Object.preventExtensions throws a TypeError"); + +test(() => { + assert_false(Reflect.preventExtensions(window)); +}, "Reflect.preventExtensions returns false"); +</script>
diff --git a/third_party/blink/web_tests/http/tests/cache/history-only-cached-subresource-loads-expected.txt b/third_party/blink/web_tests/http/tests/cache/history-only-cached-subresource-loads-expected.txt index 1125eff..d1a264e 100644 --- a/third_party/blink/web_tests/http/tests/cache/history-only-cached-subresource-loads-expected.txt +++ b/third_party/blink/web_tests/http/tests/cache/history-only-cached-subresource-loads-expected.txt
@@ -1,8 +1,6 @@ -This test checks that loading a subresource with "Cache-Control: no-store" is cached and reused in back navigation when the page is not in the page cache. +This test checks that a subresource with "Cache-Control: no-store" is not cached. -We then test that loading the same subresource is refetched when used in non-stale loads such as refreshes or normal navigation. - -PASS - no-store subresource was cached and used for a back navigation +PASS - no-store subresource was refetched for a back navigation PASS - no-store subresource was refetched with a reload PASS - no-store subresource was refetched with a normal navigation
diff --git a/third_party/blink/web_tests/http/tests/cache/history-only-cached-subresource-loads.html b/third_party/blink/web_tests/http/tests/cache/history-only-cached-subresource-loads.html index d248f70..9ed5494 100644 --- a/third_party/blink/web_tests/http/tests/cache/history-only-cached-subresource-loads.html +++ b/third_party/blink/web_tests/http/tests/cache/history-only-cached-subresource-loads.html
@@ -5,12 +5,7 @@ </head> <body> <p> - This test checks that loading a subresource with "Cache-Control: no-store" is - cached and reused in back navigation when the page is not in the page cache. - </p> - <p> - We then test that loading the same subresource is refetched when used in - non-stale loads such as refreshes or normal navigation. + This test checks that a subresource with "Cache-Control: no-store" is not cached. </p> <pre id="console"></pre> <script> @@ -46,9 +41,9 @@ backLoadRandomNumber = event.data; var wasCached = (backLoadRandomNumber === originalRandomNumber); if (wasCached) - pre.appendChild(document.createTextNode('PASS - no-store subresource was cached and used for a back navigation\n')); + pre.appendChild(document.createTextNode('FAIL - no-store subresource should have been refetched for a back navigation\n')); else - pre.appendChild(document.createTextNode('FAIL - no-store subresource should have been cached and used in a back navigation\n')); + pre.appendChild(document.createTextNode('PASS - no-store subresource was refetched for a back navigation\n')); target.postMessage('reload', '*'); return; }
diff --git a/third_party/blink/web_tests/platform/win7/fast/forms/suggestion-picker/time-suggestion-picker-appearance-expected.png b/third_party/blink/web_tests/platform/win7/fast/forms/suggestion-picker/time-suggestion-picker-appearance-expected.png deleted file mode 100644 index a26bc7a..0000000 --- a/third_party/blink/web_tests/platform/win7/fast/forms/suggestion-picker/time-suggestion-picker-appearance-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/freetype/README.chromium b/third_party/freetype/README.chromium index f75c27f..b6f8922 100644 --- a/third_party/freetype/README.chromium +++ b/third_party/freetype/README.chromium
@@ -1,7 +1,7 @@ Name: FreeType URL: http://www.freetype.org/ -Version: VER-2-10-4-52-g7bdf386e7 -Revision: 7bdf386e758cb7c01392e625f4d723e5abb3f9a6 +Version: VER-2-10-4-53-g0d5f1dd37 +Revision: 0d5f1dd37c056b4460a460d16fd1fbb06740e891 CPEPrefix: cpe:/a:freetype:freetype:2.10.1 License: Custom license "inspired by the BSD, Artistic, and IJG (Independent JPEG Group) licenses"
diff --git a/tools/binary_size/generate_milestone_reports.py b/tools/binary_size/generate_milestone_reports.py index e31306f..9d18ab2e 100755 --- a/tools/binary_size/generate_milestone_reports.py +++ b/tools/binary_size/generate_milestone_reports.py
@@ -74,8 +74,9 @@ '83.0.4103.60', '84.0.4147.89', '85.0.4183.81', - '86.0.4240.11', # Canary - '87.0.4280.13', # Canary + '86.0.4240.198', + '87.0.4280.66', + '88.0.4324.51', # Beta ]
diff --git a/tools/binary_size/milestone_apk_sizes.py b/tools/binary_size/milestone_apk_sizes.py index e98b06d..6d6efc7 100755 --- a/tools/binary_size/milestone_apk_sizes.py +++ b/tools/binary_size/milestone_apk_sizes.py
@@ -96,7 +96,7 @@ def AddDfmSizes(self, metrics): for k, v in sorted(self._resource_sizes_json['charts'].items()): - if k.startswith('DFM_'): + if k.startswith('DFM_') and k != 'DFM_base': metrics['DFM: ' + k[4:]] = v['Size with hindi']['value'] def PrintLibraryCompression(self):
diff --git a/tools/clang/scripts/build.py b/tools/clang/scripts/build.py index 527e118..ee3396e3 100755 --- a/tools/clang/scripts/build.py +++ b/tools/clang/scripts/build.py
@@ -571,8 +571,6 @@ '-DLIBCXX_INCLUDE_TESTS=OFF', '-DLIBCXX_ENABLE_EXPERIMENTAL_LIBRARY=OFF', ]) - # Prefer Python 2. TODO(crbug.com/1076834): Remove this. - base_cmake_args.append('-DPython3_EXECUTABLE=/nonexistent') if args.gcc_toolchain: # Force compiler-rt tests to use our gcc toolchain (including libstdc++.so)
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index 21c65a8..6d21e337 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -9416,6 +9416,12 @@ <int value="9" label="TEXTURE"/> </enum> +<enum name="CaptureQuickAction"> + <int value="0" label="Edit file in backlight"/> + <int value="1" label="Go to file location"/> + <int value="2" label="Delete file"/> +</enum> + <enum name="CaptureStartupResult"> <obsolete> Deprecated as of 02/2017. @@ -44481,6 +44487,7 @@ label="SafeBrowsingEnhancedProtectionMessageInInterstitials:enabled"/> <int value="151022756" label="ArcAvailableForChildAccount:disabled"/> <int value="151101719" label="HtmlBaseUsernameDetector:enabled"/> + <int value="151173516" label="CellularUseAttachApn:enabled"/> <int value="151630887" label="WebUIA11yEnhancements:disabled"/> <int value="153347646" label="SmartDimModelV3:disabled"/> <int value="155977192" label="EnableFileManagerFormatDialog:disabled"/> @@ -45491,6 +45498,7 @@ <int value="1140541604" label="WinrtGeolocationImplementation:enabled"/> <int value="1141918949" label="ContextualSearchLongpressPanelHelp:enabled"/> <int value="1142515376" label="enable-nacl"/> + <int value="1142752111" label="CellularUseAttachApn:disabled"/> <int value="1142788238" label="FontCacheScaling:disabled"/> <int value="1142970266" label="SignedExchangePrefetchCacheForNavigations:disabled"/> @@ -59947,6 +59955,8 @@ <int value="59" label="FINAL_STATUS_GWS_HOLDBACK"/> <int value="60" label="FINAL_STATUS_UNKNOWN"/> <int value="61" label="FINAL_STATUS_NAVIGATION_PREDICTOR_HOLDBACK"/> + <int value="62" label="SINGLE PROCESS"/> + <int value="63" label="LINK REL NEXT NOT ALLOWED"/> </enum> <enum name="PrerenderHoverEvent">
diff --git a/tools/metrics/histograms/histograms_xml/ash/histograms.xml b/tools/metrics/histograms/histograms_xml/ash/histograms.xml index c21483ca..7760ac2 100644 --- a/tools/metrics/histograms/histograms_xml/ash/histograms.xml +++ b/tools/metrics/histograms/histograms_xml/ash/histograms.xml
@@ -279,6 +279,16 @@ <token key="TabletOrClamshell" variants="DisplayModes"/> </histogram> +<histogram name="Ash.CaptureModeController.QuickAction" + enum="CaptureQuickAction" expires_after="2021-11-19"> + <owner>shidi@chromium.org</owner> + <owner>chinsenj@chromium.org</owner> + <summary> + Track all quick actions on screenshot notification. Including: Edit in + backlight, Go to Files, Delete File. + </summary> +</histogram> + <histogram name="Ash.CaptureModeController.ScreenRecordingLength" units="seconds" expires_after="2021-11-04"> <owner>afakhry@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms_xml/others/histograms.xml b/tools/metrics/histograms/histograms_xml/others/histograms.xml index 7aa55e3..194c8f5 100644 --- a/tools/metrics/histograms/histograms_xml/others/histograms.xml +++ b/tools/metrics/histograms/histograms_xml/others/histograms.xml
@@ -12899,6 +12899,9 @@ <histogram name="ResourceScheduler.DelayableRequests.WaitTimeToAvoidContentionWithNonDelayableRequest" units="ms" expires_after="M85"> + <obsolete> + Obsoleted M85. + </obsolete> <owner>tbansal@chromium.org</owner> <summary> Records how long after the start of a delayable resource request, a @@ -12910,6 +12913,9 @@ <histogram name="ResourceScheduler.NonDelayableLastEndToNonDelayableStart" units="ms" expires_after="M85"> + <obsolete> + Obsoleted M85. + </obsolete> <owner>tbansal@chromium.org</owner> <owner>dougarnett@chromium.org</owner> <summary> @@ -12922,7 +12928,10 @@ <histogram name="ResourceScheduler.NonDelayableLastEndToNonDelayableStart.NonDelayableNotInFlight" - units="ms" expires_after="2021-09-01"> + units="ms" expires_after="2020-12-14"> + <obsolete> + Obsoleted M89. + </obsolete> <owner>tbansal@chromium.org</owner> <owner>dougarnett@chromium.org</owner> <summary> @@ -14641,7 +14650,7 @@ </histogram> <histogram name="SpellCheck.SpellingService.RequestDuration" units="ms" - expires_after="2021-01-31"> + expires_after="2021-04-04"> <owner>yyushkina@google.com</owner> <owner>gujen@google.com</owner> <owner>chrome-language@google.com</owner> @@ -14653,7 +14662,7 @@ </histogram> <histogram name="SpellCheck.SpellingService.RequestHttpResponseCode" - enum="HttpResponseCode" expires_after="2021-04-11"> + enum="HttpResponseCode" expires_after="2021-04-04"> <owner>yyushkina@google.com</owner> <owner>gujen@google.com</owner> <owner>chrome-language@google.com</owner>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json index 17b15d9..a87ef188 100644 --- a/tools/perf/core/perfetto_binary_roller/binary_deps.json +++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -1,12 +1,12 @@ { "trace_processor_shell": { "win": { - "hash": "338c190945851de759c1aa74422592480695a218", - "remote_path": "perfetto_binaries/trace_processor_shell/win/0d23e521cc40b7a795ad98247a5d5b20121a5cf4/trace_processor_shell.exe" + "hash": "23fc83b6e05c1e0608bdc0e182a199f449d240f0", + "remote_path": "perfetto_binaries/trace_processor_shell/win/c65f224a7259d87bf1f980e7424d73fc0cfca795/trace_processor_shell.exe" }, "mac": { - "hash": "607eed89d7285563dbfdddc37e6e3e2642b7eb8c", - "remote_path": "perfetto_binaries/trace_processor_shell/mac/0d23e521cc40b7a795ad98247a5d5b20121a5cf4/trace_processor_shell" + "hash": "e70767fa3e8f88563e8bb4e50672468b9d759ac7", + "remote_path": "perfetto_binaries/trace_processor_shell/mac/c65f224a7259d87bf1f980e7424d73fc0cfca795/trace_processor_shell" }, "linux": { "hash": "72c98fdee445bb652f16c2965bddc2d8cf1f0325",
diff --git a/tools/perf/expectations.config b/tools/perf/expectations.config index b493412..8405487 100644 --- a/tools/perf/expectations.config +++ b/tools/perf/expectations.config
@@ -246,6 +246,11 @@ crbug.com/1124237 [ android-nexus-5x android-webview ] rendering.mobile/toBlob_duration.html [ Skip ] crbug.com/1150490 [ android-nexus-5x android-webview ] rendering.mobile/microgame_fps [ Skip ] crbug.com/1153607 [ android-pixel-2 ] rendering.mobile/many_images [ Skip ] +crbug.com/1160628 [ android-pixel-2 ] rendering.mobile/idle_power_animated_gif [ Skip ] +crbug.com/1160628 [ android-pixel-2 ] rendering.mobile/idle_power_css_animation [ Skip ] +crbug.com/1160628 [ android-pixel-2 ] rendering.mobile/idle_power_request_animation_frame [ Skip ] +crbug.com/1160628 [ android-pixel-2 ] rendering.mobile/idle_power_set_timeout_long [ Skip ] +crbug.com/1160628 [ android-pixel-2 ] rendering.mobile/idle_power_set_timetout [ Skip ] # Benchmark: rasterize_and_record_micro.top_25 crbug.com/764543 rasterize_and_record_micro.top_25/file://static_top_25/wikipedia.html [ Skip ] @@ -462,6 +467,7 @@ crbug.com/1036143 [ android-pixel-2 ] v8.browsing_mobile-future/browse:chrome:omnibox:2019 [ Skip ] crbug.com/1064658 [ android ] v8.browsing_mobile-future/browse:tech:discourse_infinite_scroll:2018 [ Skip ] crbug.com/1112337 [ android ] v8.browsing_mobile-future/browse:news:cricbuzz:2019 [ Skip ] +crbug.com/1160630 [ android-pixel-2 ] v8.browsing_mobile-future/browse:social:pinterest_infinite_scroll:2019 [ Skip ] # Benchmark: v8.runtime_stats.top_25 crbug.com/954229 [ mac ] v8.runtime_stats.top_25/* [ Skip ]
diff --git a/ui/accessibility/ax_assistant_structure.cc b/ui/accessibility/ax_assistant_structure.cc index 7cef392..de239c5e 100644 --- a/ui/accessibility/ax_assistant_structure.cc +++ b/ui/accessibility/ax_assistant_structure.cc
@@ -456,6 +456,7 @@ case ax::mojom::Role::kList: case ax::mojom::Role::kListBox: case ax::mojom::Role::kDescriptionList: + case ax::mojom::Role::kDirectory: return kAXListViewClassname; case ax::mojom::Role::kDialog: return kAXDialogClassname;
diff --git a/ui/accessibility/ax_role_properties.cc b/ui/accessibility/ax_role_properties.cc index 36b48d5..c09f6364 100644 --- a/ui/accessibility/ax_role_properties.cc +++ b/ui/accessibility/ax_role_properties.cc
@@ -729,6 +729,7 @@ case ax::mojom::Role::kDefinition: case ax::mojom::Role::kDescriptionList: case ax::mojom::Role::kDescriptionListTerm: + case ax::mojom::Role::kDirectory: case ax::mojom::Role::kDocument: case ax::mojom::Role::kGraphicsDocument: case ax::mojom::Role::kImage:
diff --git a/ui/accessibility/test_ax_tree_manager.cc b/ui/accessibility/test_ax_tree_manager.cc index 70d872d8..3acce8f 100644 --- a/ui/accessibility/test_ax_tree_manager.cc +++ b/ui/accessibility/test_ax_tree_manager.cc
@@ -14,7 +14,27 @@ TestAXTreeManager::TestAXTreeManager(std::unique_ptr<AXTree> tree) : tree_(std::move(tree)) { - AXTreeManagerMap::GetInstance().AddTreeManager(GetTreeID(), this); + if (tree_) + AXTreeManagerMap::GetInstance().AddTreeManager(GetTreeID(), this); +} + +TestAXTreeManager::TestAXTreeManager(TestAXTreeManager&& manager) + : tree_(std::move(manager.tree_)) { + if (tree_) { + AXTreeManagerMap::GetInstance().RemoveTreeManager(GetTreeID()); + AXTreeManagerMap::GetInstance().AddTreeManager(GetTreeID(), this); + } +} + +TestAXTreeManager& TestAXTreeManager::operator=(TestAXTreeManager&& manager) { + if (this == &manager) + return *this; + if (manager.tree_) + AXTreeManagerMap::GetInstance().RemoveTreeManager(manager.GetTreeID()); + // std::move(nullptr) == nullptr, so no need to check if `manager.tree_` is + // assigned. + SetTree(std::move(manager.tree_)); + return *this; } TestAXTreeManager::~TestAXTreeManager() { @@ -40,7 +60,8 @@ AXTreeManagerMap::GetInstance().RemoveTreeManager(GetTreeID()); tree_ = std::move(tree); - AXTreeManagerMap::GetInstance().AddTreeManager(GetTreeID(), this); + if (tree_) + AXTreeManagerMap::GetInstance().AddTreeManager(GetTreeID(), this); } AXNode* TestAXTreeManager::GetNodeFromTree(const AXTreeID tree_id,
diff --git a/ui/accessibility/test_ax_tree_manager.h b/ui/accessibility/test_ax_tree_manager.h index 117f020..5fec7c7 100644 --- a/ui/accessibility/test_ax_tree_manager.h +++ b/ui/accessibility/test_ax_tree_manager.h
@@ -34,6 +34,9 @@ TestAXTreeManager(const TestAXTreeManager& manager) = delete; TestAXTreeManager& operator=(const TestAXTreeManager& manager) = delete; + TestAXTreeManager(TestAXTreeManager&& manager); + TestAXTreeManager& operator=(TestAXTreeManager&& manager); + void DestroyTree(); AXTree* GetTree() const; // Takes ownership of |tree|.
diff --git a/ui/chromeos/events/event_rewriter_chromeos.cc b/ui/chromeos/events/event_rewriter_chromeos.cc index f759593f..a13542f 100644 --- a/ui/chromeos/events/event_rewriter_chromeos.cc +++ b/ui/chromeos/events/event_rewriter_chromeos.cc
@@ -965,7 +965,8 @@ *matched_mask = kSearchLeftButton; } } else { - if (AreFlagsSet(flags, kAltLeftButton)) { + if (AreFlagsSet(flags, kAltLeftButton) && + is_alt_left_click_remapping_enabled_) { *matched_mask = kAltLeftButton; } }
diff --git a/ui/chromeos/events/event_rewriter_chromeos.h b/ui/chromeos/events/event_rewriter_chromeos.h index cef089aa..68bb1e2 100644 --- a/ui/chromeos/events/event_rewriter_chromeos.h +++ b/ui/chromeos/events/event_rewriter_chromeos.h
@@ -150,6 +150,13 @@ privacy_screen_supported_ = supported; } + // Enable/disable alt + left click mapping to a right click. This only + // applies if the feature `chromeos::features::kUseSearchClickForRightClick` + // is not enabled. + void set_alt_left_click_remapping_enabled(bool enabled) { + is_alt_left_click_remapping_enabled_ = enabled; + } + // EventRewriter overrides: EventDispatchDetails RewriteEvent(const Event& event, const Continuation continuation) override; @@ -215,7 +222,8 @@ // Returns true if this event should be remapped to a right-click. // |matched_mask| will be set to the variant (Alt+Click or Search+Click) // that was used to match based on flag/feature settings. |matched_mask| - // only has a valid value when returning true. + // only has a valid value when returning true. However, Alt+Click will not + // be remapped if |is_alt_left_click_remapping_enabled_| is false. bool ShouldRemapToRightClick(const MouseEvent& mouse_event, int flags, int* matched_mask) const; @@ -325,6 +333,11 @@ int latched_modifier_latches_; int used_modifier_latches_; + // Alt + left click maps to a right click by default. In some scenario, such + // as clicking a button in the Alt-Tab UI, this remapping undesirably prevents + // button clicking. + bool is_alt_left_click_remapping_enabled_ = true; + DISALLOW_COPY_AND_ASSIGN(EventRewriterChromeOS); };
diff --git a/ui/gl/direct_composition_surface_win.cc b/ui/gl/direct_composition_surface_win.cc index 8dbb0a7..9b36519 100644 --- a/ui/gl/direct_composition_surface_win.cc +++ b/ui/gl/direct_composition_surface_win.cc
@@ -20,6 +20,7 @@ #include "ui/gl/dc_layer_tree.h" #include "ui/gl/direct_composition_child_surface_win.h" #include "ui/gl/gl_angle_util_win.h" +#include "ui/gl/gl_features.h" #include "ui/gl/gl_implementation.h" #include "ui/gl/gl_switches.h" #include "ui/gl/gpu_switching_manager.h" @@ -652,8 +653,7 @@ // static bool DirectCompositionSurfaceWin::AllowTearing() { // Swap chain tearing is used only if vsync is disabled explicitly. - return base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kDisableGpuVsync) && + return features::UseGpuVsync() && DirectCompositionSurfaceWin::IsSwapChainTearingSupported(); }
diff --git a/ui/gl/gl_features.cc b/ui/gl/gl_features.cc index cbae797..c71e70c 100644 --- a/ui/gl/gl_features.cc +++ b/ui/gl/gl_features.cc
@@ -4,9 +4,17 @@ #include "ui/gl/gl_features.h" +#include "base/command_line.h" +#include "base/feature_list.h" #include "build/chromeos_buildflags.h" +#include "ui/gl/gl_switches.h" namespace features { +namespace { + +const base::Feature kGpuVsync{"GpuVsync", base::FEATURE_ENABLED_BY_DEFAULT}; + +} // namespace // Use the passthrough command decoder by default. This can be overridden with // the --use-cmd-decoder=passthrough or --use-cmd-decoder=validating flags. @@ -24,4 +32,10 @@ #endif }; +bool UseGpuVsync() { + return !base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisableGpuVsync) && + base::FeatureList::IsEnabled(kGpuVsync); +} + } // namespace features
diff --git a/ui/gl/gl_features.h b/ui/gl/gl_features.h index 3513df1..e84a953c 100644 --- a/ui/gl/gl_features.h +++ b/ui/gl/gl_features.h
@@ -11,6 +11,9 @@ namespace features { +// Controls if GPU should synchronize presentation with vsync. +GL_EXPORT bool UseGpuVsync(); + // All features in alphabetical order. The features should be documented // alongside the definition of their values in the .cc file. GL_EXPORT extern const base::Feature kDefaultPassthroughCommandDecoder;
diff --git a/ui/webui/resources/cr_components/customize_themes/customize_themes.html b/ui/webui/resources/cr_components/customize_themes/customize_themes.html index 6ff12c4..1b9005b 100644 --- a/ui/webui/resources/cr_components/customize_themes/customize_themes.html +++ b/ui/webui/resources/cr_components/customize_themes/customize_themes.html
@@ -157,9 +157,9 @@ [[i18n('colorPickerLabel')]] </paper-tooltip> </div> - <div aria-label="[[i18n('defaultThemeLabel')]]" tabindex="0"> + <div aria-label="[[i18n('defaultThemeLabel')]]" + tabindex="0" on-click="onDefaultThemeClick_"> <cr-theme-icon id="defaultTheme" - on-click="onDefaultThemeClick_" selected$="[[isThemeIconSelected_('default', selectedTheme)]]"> </cr-theme-icon> <paper-tooltip offset="0" fit-to-visible-bounds> @@ -167,8 +167,9 @@ </paper-tooltip> </div> <template is="dom-repeat" id="themes" items="[[chromeThemes_]]"> - <div aria-label="[[item.label]]" tabindex="0" class="chrome-theme-wrapper"> - <cr-theme-icon on-click="onChromeThemeClick_" + <div aria-label="[[item.label]]" tabindex="0" + on-click="onChromeThemeClick_" class="chrome-theme-wrapper"> + <cr-theme-icon style="--cr-theme-icon-frame-color: [[skColorToRgba_(item.colors.frame)]]; --cr-theme-icon-active-tab-color:
diff --git a/weblayer/browser/no_state_prefetch/no_state_prefetch_browsertest.cc b/weblayer/browser/no_state_prefetch/no_state_prefetch_browsertest.cc index 56424da..539ba42 100644 --- a/weblayer/browser/no_state_prefetch/no_state_prefetch_browsertest.cc +++ b/weblayer/browser/no_state_prefetch/no_state_prefetch_browsertest.cc
@@ -197,14 +197,12 @@ prerendered_page_fetched_->Run(); } -// link-rel="next" happens even when NoStatePrefetch has been disabled. +// link-rel="next" URLs should not be prefetched. IN_PROC_BROWSER_TEST_F(NoStatePrefetchBrowserTest, LinkRelNextWithNSPDisabled) { - GetProfile()->SetBooleanSetting(SettingType::NETWORK_PREDICTION_ENABLED, - false); NavigateAndWaitForCompletion( GURL(https_server_->GetURL("/link_rel_next_parent.html")), shell()); - - prerendered_page_fetched_->Run(); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(prerendered_page_was_fetched_); } // Non-web initiated prerender succeeds and subsequent navigations reuse