diff --git a/DEPS b/DEPS index b93476c..f0844a446 100644 --- a/DEPS +++ b/DEPS
@@ -167,11 +167,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': '1ebaa15a4c62453f5eeac71a5ba33e4dd6da566e', + 'skia_revision': 'cf0a9a628490ccb52f7257e8c22e1bbf432252f2', # 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': '23dacd6b32b6767f9bde088e80e70c054c825c22', + 'v8_revision': 'f45ddb9b49eab6d363a7ffa52fdd265fe14984a7', # 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. @@ -179,7 +179,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': 'f22f16d3c0a80ce7bbc4a8eeff81fde63aa6ad40', + 'angle_revision': 'f03259ad720556908d46ea139b99804b1039550e', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. @@ -230,7 +230,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling catapult # and whatever else without interference from each other. - 'catapult_revision': '017b54db6bea7cae9c65896a4f4846209485212d', + 'catapult_revision': 'dbfa96532ab0841252db9df65ef2d7fe3ce41393', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other. @@ -286,7 +286,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. - 'spv_tools_revision': '85c67b5e08eea1818767f174515612464740a53c', + 'spv_tools_revision': '44b32176ee48936dc212de0624b3d848510338a9', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -302,7 +302,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': 'dc9f1e68069917f29d552d9cb0b7993fcb7c4059', + 'dawn_revision': '867f72058a4f6b901524efe484611e1e319c80fb', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -862,7 +862,7 @@ # Build tools for Chrome OS. Note: This depends on third_party/pyelftools. 'src/third_party/chromite': { - 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '98592f6c5dbe68b65645be7d0f0277757f0a09c7', + 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '929f3ba81e6e0047f04f51b143efe5f3f974137d', 'condition': 'checkout_linux', }, @@ -887,7 +887,7 @@ }, 'src/third_party/depot_tools': - Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '5eac9d301390c5ff8afdaa95f46e68bb84e20575', + Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '2458b31208f87c2bd54befb7d701ecf60b9f75e9', 'src/third_party/devtools-node-modules': Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'), @@ -1269,7 +1269,7 @@ }, 'src/third_party/perfetto': - Var('android_git') + '/platform/external/perfetto.git' + '@' + 'fa5193a394a7597a3a52c6cb05a71183b45fa170', + Var('android_git') + '/platform/external/perfetto.git' + '@' + '3ad1e2151d99792d5a2d1280cd4a1bbddd475edd', 'src/third_party/perl': { 'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3', @@ -1459,7 +1459,7 @@ Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '7c4e67ff117d6c640e6dd17989afe2fb7da7eecb', 'src/third_party/webrtc': - Var('webrtc_git') + '/src.git' + '@' + 'f4e0c29ed1d74dd192e745c7b2a7d6806b5d8e4f', + Var('webrtc_git') + '/src.git' + '@' + 'f1e97b9ebd23c12d12ffd6b18bdf3eb4951153b4', 'src/third_party/xdg-utils': { 'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d', @@ -1521,7 +1521,7 @@ Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@eb06828e0343cd99de93721890618db0490ab8cd', + 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@9bdb26527d654e36041b0926eee626c4b0fdab00', 'condition': 'checkout_src_internal', },
diff --git a/android_webview/browser/js_java_interaction/js_java_configurator_host.cc b/android_webview/browser/js_java_interaction/js_java_configurator_host.cc index aecc3a2..8b4c33b2 100644 --- a/android_webview/browser/js_java_interaction/js_java_configurator_host.cc +++ b/android_webview/browser/js_java_interaction/js_java_configurator_host.cc
@@ -128,9 +128,9 @@ render_frame_host, pending_remote.InitWithNewEndpointAndPassReceiver(), js_object.listener_ref_, js_object.allowed_origin_rules_)); - js_objects.push_back(std::move( - mojom::JsObject::New(js_object.name_, std::move(pending_remote), - js_object.allowed_origin_rules_))); + js_objects.push_back(mojom::JsObject::New(js_object.name_, + std::move(pending_remote), + js_object.allowed_origin_rules_)); } configurator_remote->SetJsObjects(std::move(js_objects)); }
diff --git a/ash/app_list/views/search_result_page_view.cc b/ash/app_list/views/search_result_page_view.cc index eb14c81..e28addf9 100644 --- a/ash/app_list/views/search_result_page_view.cc +++ b/ash/app_list/views/search_result_page_view.cc
@@ -182,7 +182,8 @@ // contents' size. Using zeroes doesn't prevent it from scrolling and sizing // correctly. scroller->ClipHeightTo(0, 0); - scroller->SetVerticalScrollBar(new ZeroWidthVerticalScrollBar()); + scroller->SetVerticalScrollBar( + std::make_unique<ZeroWidthVerticalScrollBar>()); scroller->SetBackgroundColor(SK_ColorTRANSPARENT); AddChildView(std::move(scroller));
diff --git a/ash/assistant/ui/base/assistant_scroll_view.cc b/ash/assistant/ui/base/assistant_scroll_view.cc index 9f65819..e0b18a5 100644 --- a/ash/assistant/ui/base/assistant_scroll_view.cc +++ b/ash/assistant/ui/base/assistant_scroll_view.cc
@@ -88,11 +88,11 @@ content_view_ = SetContents(std::move(content_view)); // Scroll bars. - horizontal_scroll_bar_ = new InvisibleScrollBar(/*horizontal=*/true); - SetHorizontalScrollBar(horizontal_scroll_bar_); + horizontal_scroll_bar_ = SetHorizontalScrollBar( + std::make_unique<InvisibleScrollBar>(/*horizontal=*/true)); - vertical_scroll_bar_ = new InvisibleScrollBar(/*horizontal=*/false); - SetVerticalScrollBar(vertical_scroll_bar_); + vertical_scroll_bar_ = SetVerticalScrollBar( + std::make_unique<InvisibleScrollBar>(/*horizontal=*/false)); } } // namespace ash
diff --git a/ash/login/ui/lock_debug_view.cc b/ash/login/ui/lock_debug_view.cc index 69e1568a..382ad699 100644 --- a/ash/login/ui/lock_debug_view.cc +++ b/ash/login/ui/lock_debug_view.cc
@@ -752,8 +752,10 @@ scroll->SetPreferredSize(gfx::Size(600, height)); scroll->SetContents(base::WrapUnique(content)); scroll->SetBackgroundColor(SK_ColorTRANSPARENT); - scroll->SetVerticalScrollBar(new views::OverlayScrollBar(false)); - scroll->SetHorizontalScrollBar(new views::OverlayScrollBar(true)); + scroll->SetVerticalScrollBar( + std::make_unique<views::OverlayScrollBar>(false)); + scroll->SetHorizontalScrollBar( + std::make_unique<views::OverlayScrollBar>(true)); return scroll; }; container_->AddChildView(make_scroll(global_action_view_container_, 110));
diff --git a/ash/login/ui/login_menu_view.cc b/ash/login/ui/login_menu_view.cc index aa2b28b8..da16546 100644 --- a/ash/login/ui/login_menu_view.cc +++ b/ash/login/ui/login_menu_view.cc
@@ -41,19 +41,19 @@ views::BoxLayout::Orientation::kHorizontal)); SetPreferredSize(gfx::Size(kMenuItemWidthDp, kMenuItemHeightDp)); - auto* spacing = new NonAccessibleView(); + auto spacing = std::make_unique<NonAccessibleView>(); spacing->SetPreferredSize(gfx::Size(item.is_group ? kRegularMenuItemLeftPaddingDp : kGroupMenuItemLeftPaddingDp, kNonEmptyHeight)); - AddChildView(spacing); + AddChildView(std::move(spacing)); - views::Label* label = new views::Label(base::UTF8ToUTF16(item.title)); + auto label = std::make_unique<views::Label>(base::UTF8ToUTF16(item.title)); label->SetEnabledColor(SK_ColorWHITE); label->SetSubpixelRenderingEnabled(false); label->SetAutoColorReadabilityEnabled(false); label->SetHorizontalAlignment(gfx::ALIGN_LEFT); - AddChildView(label); + AddChildView(std::move(label)); if (item.selected) SetBackground(views::CreateSolidBackground(SK_ColorGRAY)); @@ -163,7 +163,7 @@ selected_index_ = i; } contents_ = scroller_->SetContents(std::move(contents)); - scroller_->SetVerticalScrollBar(new LoginScrollBar()); + scroller_->SetVerticalScrollBar(std::make_unique<LoginScrollBar>()); } LoginMenuView::~LoginMenuView() = default;
diff --git a/ash/login/ui/scrollable_users_list_view.cc b/ash/login/ui/scrollable_users_list_view.cc index 2c61bb4b..28d58e9 100644 --- a/ash/login/ui/scrollable_users_list_view.cc +++ b/ash/login/ui/scrollable_users_list_view.cc
@@ -334,8 +334,8 @@ SetBackgroundColor(SK_ColorTRANSPARENT); SetDrawOverflowIndicator(false); - SetVerticalScrollBar(new UsersListScrollBar(false)); - SetHorizontalScrollBar(new UsersListScrollBar(true)); + SetVerticalScrollBar(std::make_unique<UsersListScrollBar>(false)); + SetHorizontalScrollBar(std::make_unique<UsersListScrollBar>(true)); observer_.Add(Shell::Get()->wallpaper_controller()); }
diff --git a/ash/system/message_center/notifier_settings_view.cc b/ash/system/message_center/notifier_settings_view.cc index c446042f..b928183e 100644 --- a/ash/system/message_center/notifier_settings_view.cc +++ b/ash/system/message_center/notifier_settings_view.cc
@@ -414,14 +414,7 @@ // NotifierSettingsView ------------------------------------------------------- -NotifierSettingsView::NotifierSettingsView() - : quiet_mode_icon_(nullptr), - quiet_mode_toggle_(nullptr), - header_view_(nullptr), - top_label_(nullptr), - scroll_bar_(nullptr), - scroller_(nullptr), - no_notifiers_view_(nullptr) { +NotifierSettingsView::NotifierSettingsView() { SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY); SetPaintToLayer(); layer()->SetFillsBoundsOpaquely(false); @@ -487,11 +480,10 @@ header_view_ = AddChildView(std::move(header_view)); - scroll_bar_ = new views::OverlayScrollBar(/*horizontal=*/false); - auto scroller = std::make_unique<views::ScrollView>(); scroller->SetBackgroundColor(SK_ColorTRANSPARENT); - scroller->SetVerticalScrollBar(scroll_bar_); + scroll_bar_ = scroller->SetVerticalScrollBar( + std::make_unique<views::OverlayScrollBar>(/*horizontal=*/false)); scroller->SetDrawOverflowIndicator(false); scroller_ = AddChildView(std::move(scroller));
diff --git a/ash/system/message_center/notifier_settings_view.h b/ash/system/message_center/notifier_settings_view.h index 124e66b..d61886e 100644 --- a/ash/system/message_center/notifier_settings_view.h +++ b/ash/system/message_center/notifier_settings_view.h
@@ -96,13 +96,13 @@ // Overridden from views::ButtonListener: void ButtonPressed(views::Button* sender, const ui::Event& event) override; - views::ImageView* quiet_mode_icon_; - views::ToggleButton* quiet_mode_toggle_; - views::View* header_view_; - views::Label* top_label_; - views::ScrollBar* scroll_bar_; - views::ScrollView* scroller_; - views::View* no_notifiers_view_; + views::ImageView* quiet_mode_icon_ = nullptr; + views::ToggleButton* quiet_mode_toggle_ = nullptr; + views::View* header_view_ = nullptr; + views::Label* top_label_ = nullptr; + views::ScrollBar* scroll_bar_ = nullptr; + views::ScrollView* scroller_ = nullptr; + views::View* no_notifiers_view_ = nullptr; std::set<NotifierButton*> buttons_; DISALLOW_COPY_AND_ASSIGN(NotifierSettingsView);
diff --git a/ash/system/message_center/unified_message_center_view.cc b/ash/system/message_center/unified_message_center_view.cc index 41ed6624..166fde6 100644 --- a/ash/system/message_center/unified_message_center_view.cc +++ b/ash/system/message_center/unified_message_center_view.cc
@@ -23,6 +23,7 @@ #include "ash/system/unified/unified_system_tray_model.h" #include "ash/system/unified/unified_system_tray_view.h" #include "base/i18n/rtl.h" +#include "base/memory/ptr_util.h" #include "base/metrics/user_metrics.h" #include "ui/base/l10n/l10n_util.h" #include "ui/gfx/animation/linear_animation.h" @@ -302,7 +303,7 @@ scroller_->SetContents( std::make_unique<ScrollerContentsView>(message_list_view_, this)); scroller_->SetBackgroundColor(SK_ColorTRANSPARENT); - scroller_->SetVerticalScrollBar(scroll_bar_); + scroller_->SetVerticalScrollBar(base::WrapUnique(scroll_bar_)); scroller_->SetDrawOverflowIndicator(false); AddChildView(scroller_); }
diff --git a/base/BUILD.gn b/base/BUILD.gn index b1cb70e1..27dbe412 100644 --- a/base/BUILD.gn +++ b/base/BUILD.gn
@@ -815,6 +815,8 @@ "task/sequence_manager/work_queue.h", "task/sequence_manager/work_queue_sets.cc", "task/sequence_manager/work_queue_sets.h", + "task/simple_task_executor.cc", + "task/simple_task_executor.h", "task/single_thread_task_executor.cc", "task/single_thread_task_executor.h", "task/single_thread_task_runner_thread_mode.h", @@ -2711,6 +2713,7 @@ "task/sequence_manager/work_deduplicator_unittest.cc", "task/sequence_manager/work_queue_sets_unittest.cc", "task/sequence_manager/work_queue_unittest.cc", + "task/single_thread_task_executor_unittest.cc", "task/task_traits_extension_unittest.cc", "task/task_traits_unittest.cc", "task/thread_pool/can_run_policy_test.h",
diff --git a/base/file_version_info_win.cc b/base/file_version_info_win.cc index c45765d..1572d0c 100644 --- a/base/file_version_info_win.cc +++ b/base/file_version_info_win.cc
@@ -183,6 +183,14 @@ return base::string16(); } +base::Version FileVersionInfoWin::GetFileVersion() const { + return base::Version( + std::vector<uint32_t>{HIWORD(fixed_file_info_->dwFileVersionMS), + LOWORD(fixed_file_info_->dwFileVersionMS), + HIWORD(fixed_file_info_->dwFileVersionLS), + LOWORD(fixed_file_info_->dwFileVersionLS)}); +} + FileVersionInfoWin::FileVersionInfoWin(std::vector<uint8_t>&& data, WORD language, WORD code_page)
diff --git a/base/file_version_info_win.h b/base/file_version_info_win.h index 7c83daf..30d4756c 100644 --- a/base/file_version_info_win.h +++ b/base/file_version_info_win.h
@@ -16,6 +16,7 @@ #include "base/base_export.h" #include "base/file_version_info.h" #include "base/macros.h" +#include "base/version.h" struct tagVS_FIXEDFILEINFO; typedef tagVS_FIXEDFILEINFO VS_FIXEDFILEINFO; @@ -44,8 +45,8 @@ // does not exist). base::string16 GetStringValue(const base::char16* name); - // Get the fixed file info if it exists. Otherwise NULL - const VS_FIXEDFILEINFO* fixed_file_info() const { return fixed_file_info_; } + // Get file version number in dotted version format. + base::Version GetFileVersion() const; // Behaves like CreateFileVersionInfo, but returns a FileVersionInfoWin. static std::unique_ptr<FileVersionInfoWin> CreateFileVersionInfoWin(
diff --git a/base/file_version_info_win_unittest.cc b/base/file_version_info_win_unittest.cc index 7a761ab..36bf4e6 100644 --- a/base/file_version_info_win_unittest.cc +++ b/base/file_version_info_win_unittest.cc
@@ -152,4 +152,7 @@ version_info_win->GetValue(STRING16_LITERAL("Unknown property"), &str)); EXPECT_EQ(base::string16(), version_info_win->GetStringValue( STRING16_LITERAL("Unknown property"))); + + EXPECT_EQ(base::Version(std::vector<uint32_t>{1, 0, 0, 1}), + version_info_win->GetFileVersion()); }
diff --git a/base/task/simple_task_executor.cc b/base/task/simple_task_executor.cc new file mode 100644 index 0000000..ffccf52 --- /dev/null +++ b/base/task/simple_task_executor.cc
@@ -0,0 +1,62 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/task/simple_task_executor.h" + +namespace base { + +SimpleTaskExecutor::SimpleTaskExecutor( + scoped_refptr<SingleThreadTaskRunner> task_queue) + : task_queue_(std::move(task_queue)), + previous_task_executor_(GetTaskExecutorForCurrentThread()) { + DCHECK(task_queue_); + // The TaskExecutor API does not expect nesting, but this can happen in tests + // so we have to work around it here. + if (previous_task_executor_) + SetTaskExecutorForCurrentThread(nullptr); + SetTaskExecutorForCurrentThread(this); +} + +SimpleTaskExecutor::~SimpleTaskExecutor() { + if (previous_task_executor_) + SetTaskExecutorForCurrentThread(nullptr); + SetTaskExecutorForCurrentThread(previous_task_executor_); +} + +bool SimpleTaskExecutor::PostDelayedTask(const Location& from_here, + const TaskTraits& traits, + OnceClosure task, + TimeDelta delay) { + return task_queue_->PostDelayedTask(from_here, std::move(task), delay); +} + +scoped_refptr<TaskRunner> SimpleTaskExecutor::CreateTaskRunner( + const TaskTraits& traits) { + return task_queue_; +} + +scoped_refptr<SequencedTaskRunner> +SimpleTaskExecutor::CreateSequencedTaskRunner(const TaskTraits& traits) { + return task_queue_; +} + +scoped_refptr<SingleThreadTaskRunner> +SimpleTaskExecutor::CreateSingleThreadTaskRunner( + const TaskTraits& traits, + SingleThreadTaskRunnerThreadMode thread_mode) { + return task_queue_; +} + +#if defined(OS_WIN) +scoped_refptr<SingleThreadTaskRunner> +SimpleTaskExecutor::CreateCOMSTATaskRunner( + const TaskTraits& traits, + SingleThreadTaskRunnerThreadMode thread_mode) { + // It seems pretty unlikely this will be used on a comsta task thread. + NOTREACHED(); + return task_queue_; +} +#endif // defined(OS_WIN) + +} // namespace base
diff --git a/base/task/simple_task_executor.h b/base/task/simple_task_executor.h new file mode 100644 index 0000000..7d9a74dc --- /dev/null +++ b/base/task/simple_task_executor.h
@@ -0,0 +1,52 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_TASK_SIMPLE_TASK_EXECUTOR_H_ +#define BASE_TASK_SIMPLE_TASK_EXECUTOR_H_ + +#include "base/task/task_executor.h" +#include "build/build_config.h" + +namespace base { + +// A simple TaskExecutor with exactly one SingleThreadTaskRunner. +// Must be instantiated and destroyed on the thread that runs tasks for the +// SingleThreadTaskRunner. +class BASE_EXPORT SimpleTaskExecutor : public TaskExecutor { + public: + explicit SimpleTaskExecutor(scoped_refptr<SingleThreadTaskRunner> task_queue); + + ~SimpleTaskExecutor() override; + + bool PostDelayedTask(const Location& from_here, + const TaskTraits& traits, + OnceClosure task, + TimeDelta delay) override; + + scoped_refptr<TaskRunner> CreateTaskRunner(const TaskTraits& traits) override; + + scoped_refptr<SequencedTaskRunner> CreateSequencedTaskRunner( + const TaskTraits& traits) override; + + scoped_refptr<SingleThreadTaskRunner> CreateSingleThreadTaskRunner( + const TaskTraits& traits, + SingleThreadTaskRunnerThreadMode thread_mode) override; + +#if defined(OS_WIN) + scoped_refptr<SingleThreadTaskRunner> CreateCOMSTATaskRunner( + const TaskTraits& traits, + SingleThreadTaskRunnerThreadMode thread_mode) override; +#endif // defined(OS_WIN) + + private: + const scoped_refptr<SingleThreadTaskRunner> task_queue_; + + // In tests there may already be a TaskExecutor registered for the thread, we + // keep tack of the previous TaskExecutor and restored it upon destruction. + TaskExecutor* const previous_task_executor_; +}; + +} // namespace base + +#endif // BASE_TASK_SIMPLE_TASK_EXECUTOR_H_
diff --git a/base/task/single_thread_task_executor.cc b/base/task/single_thread_task_executor.cc index 7e12b78..01d993b 100644 --- a/base/task/single_thread_task_executor.cc +++ b/base/task/single_thread_task_executor.cc
@@ -17,7 +17,8 @@ .Build())), default_task_queue_(sequence_manager_->CreateTaskQueue( sequence_manager::TaskQueue::Spec("default_tq"))), - type_(type) { + type_(type), + simple_task_executor_(task_runner()) { sequence_manager_->SetDefaultTaskRunner(default_task_queue_->task_runner()); sequence_manager_->BindToMessagePump(MessagePump::Create(type));
diff --git a/base/task/single_thread_task_executor.h b/base/task/single_thread_task_executor.h index d350420..c048a83 100644 --- a/base/task/single_thread_task_executor.h +++ b/base/task/single_thread_task_executor.h
@@ -11,6 +11,7 @@ #include "base/memory/scoped_refptr.h" #include "base/message_loop/message_pump_type.h" #include "base/single_thread_task_runner.h" +#include "base/task/simple_task_executor.h" namespace base { @@ -40,6 +41,7 @@ std::unique_ptr<sequence_manager::SequenceManager> sequence_manager_; scoped_refptr<sequence_manager::TaskQueue> default_task_queue_; MessagePumpType type_; + SimpleTaskExecutor simple_task_executor_; DISALLOW_COPY_AND_ASSIGN(SingleThreadTaskExecutor); };
diff --git a/base/task/single_thread_task_executor_unittest.cc b/base/task/single_thread_task_executor_unittest.cc new file mode 100644 index 0000000..8919298b --- /dev/null +++ b/base/task/single_thread_task_executor_unittest.cc
@@ -0,0 +1,45 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/task/single_thread_task_executor.h" + +#include "base/run_loop.h" +#include "base/test/bind_test_util.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ::testing::IsNull; +using ::testing::NotNull; + +namespace base { + +TEST(SingleThreadTaskExecutorTest, GetTaskExecutorForCurrentThread) { + EXPECT_THAT(GetTaskExecutorForCurrentThread(), IsNull()); + + { + SingleThreadTaskExecutor single_thread_task_executor; + EXPECT_THAT(GetTaskExecutorForCurrentThread(), NotNull()); + } + + EXPECT_THAT(GetTaskExecutorForCurrentThread(), IsNull()); +} + +TEST(SingleThreadTaskExecutorTest, + GetTaskExecutorForCurrentThreadInPostedTask) { + SingleThreadTaskExecutor single_thread_task_executor; + TaskExecutor* task_executor = GetTaskExecutorForCurrentThread(); + + EXPECT_THAT(task_executor, NotNull()); + + RunLoop run_loop; + single_thread_task_executor.task_runner()->PostTask( + FROM_HERE, BindLambdaForTesting([&]() { + EXPECT_EQ(GetTaskExecutorForCurrentThread(), task_executor); + run_loop.Quit(); + })); + + run_loop.Run(); +} + +} // namespace base
diff --git a/base/threading/thread.cc b/base/threading/thread.cc index 6fc752f..eecd3491 100644 --- a/base/threading/thread.cc +++ b/base/threading/thread.cc
@@ -19,6 +19,7 @@ #include "base/synchronization/waitable_event.h" #include "base/task/sequence_manager/sequence_manager_impl.h" #include "base/task/sequence_manager/task_queue.h" +#include "base/task/simple_task_executor.h" #include "base/third_party/dynamic_annotations/dynamic_annotations.h" #include "base/threading/thread_id_name_manager.h" #include "base/threading/thread_local.h" @@ -86,6 +87,7 @@ sequence_manager_->BindToMessagePump( std::move(message_pump_factory_).Run()); sequence_manager_->SetTimerSlack(timer_slack); + simple_task_executor_.emplace(GetDefaultTaskRunner()); } private: @@ -93,6 +95,7 @@ sequence_manager_; scoped_refptr<sequence_manager::TaskQueue> default_task_queue_; OnceCallback<std::unique_ptr<MessagePump>()> message_pump_factory_; + base::Optional<SimpleTaskExecutor> simple_task_executor_; }; } // namespace
diff --git a/base/threading/thread_unittest.cc b/base/threading/thread_unittest.cc index bdb57cc..ab986326 100644 --- a/base/threading/thread_unittest.cc +++ b/base/threading/thread_unittest.cc
@@ -18,15 +18,19 @@ #include "base/single_thread_task_runner.h" #include "base/synchronization/waitable_event.h" #include "base/task/sequence_manager/sequence_manager_impl.h" +#include "base/task/task_executor.h" +#include "base/test/bind_test_util.h" #include "base/test/gtest_util.h" #include "base/third_party/dynamic_annotations/dynamic_annotations.h" #include "base/threading/platform_thread.h" #include "base/time/time.h" #include "build/build_config.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/platform_test.h" using base::Thread; +using ::testing::NotNull; typedef PlatformTest ThreadTest; @@ -522,6 +526,23 @@ a.FlushForTesting(); } +TEST_F(ThreadTest, GetTaskExecutorForCurrentThread) { + Thread a("GetTaskExecutorForCurrentThread"); + ASSERT_TRUE(a.Start()); + + base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED); + + a.task_runner()->PostTask( + FROM_HERE, base::BindLambdaForTesting([&]() { + EXPECT_THAT(base::GetTaskExecutorForCurrentThread(), NotNull()); + event.Signal(); + })); + + event.Wait(); + a.Stop(); +} + namespace { class SequenceManagerThreadDelegate : public Thread::Delegate {
diff --git a/base/win/windows_version.cc b/base/win/windows_version.cc index 88974fa..476744b 100644 --- a/base/win/windows_version.cc +++ b/base/win/windows_version.cc
@@ -214,15 +214,7 @@ FilePath(FILE_PATH_LITERAL("kernelbase.dll"))); } CHECK(file_version_info); - const int major = - HIWORD(file_version_info->fixed_file_info()->dwFileVersionMS); - const int minor = - LOWORD(file_version_info->fixed_file_info()->dwFileVersionMS); - const int build = - HIWORD(file_version_info->fixed_file_info()->dwFileVersionLS); - const int patch = - LOWORD(file_version_info->fixed_file_info()->dwFileVersionLS); - return base::Version(std::vector<uint32_t>{major, minor, build, patch}); + return file_version_info->GetFileVersion(); }()); return *version; }
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni index 2d5d634..e20e766 100644 --- a/build/config/android/rules.gni +++ b/build/config/android/rules.gni
@@ -373,14 +373,16 @@ template("generate_jni_registration") { action_with_pydeps(target_name) { forward_variables_from(invoker, [ "testonly" ]) - _build_config = get_label_info(invoker.target, "target_gen_dir") + "/" + - get_label_info(invoker.target, "name") + ".build_config" + _build_config = get_label_info("${invoker.target}($default_toolchain)", + "target_gen_dir") + "/" + + get_label_info("${invoker.target}($default_toolchain)", + "name") + ".build_config" _rebased_build_config = rebase_path(_build_config, root_build_dir) _srcjar_output = "$target_gen_dir/$target_name.srcjar" script = "//base/android/jni_generator/jni_registration_generator.py" deps = [ - "${invoker.target}$build_config_target_suffix", + "${invoker.target}$build_config_target_suffix($default_toolchain)", ] inputs = [ _build_config,
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn index e65eee2..2ef8771 100644 --- a/build/config/compiler/BUILD.gn +++ b/build/config/compiler/BUILD.gn
@@ -1546,6 +1546,12 @@ cflags += [ # TODO(https://crbug.com/1010111): Evaluate and possible enable. "-Wno-enum-compare-conditional", + + # TODO(https://crbug.com/1010458): Figure out plan forward, + # see bug. Depending on how many instances there are, either + # turn off warning when rolling this in, then fix and turn back on, + # or fix before the roll. + "-Wno-error=pessimizing-move", ] } }
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1 index d2f4a60..a3cab05 100644 --- a/build/fuchsia/linux.sdk.sha1 +++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@ -8900743524103453968 \ No newline at end of file +8900716279085139440 \ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1 index bc98830..fbe4b5d 100644 --- a/build/fuchsia/mac.sdk.sha1 +++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@ -8900744692874110736 \ No newline at end of file +8900715419223164800 \ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/metrics/VariationsSession.java b/chrome/android/java/src/org/chromium/chrome/browser/metrics/VariationsSession.java index 6f3dd638..a1fa3e1 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/metrics/VariationsSession.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/metrics/VariationsSession.java
@@ -4,9 +4,8 @@ package org.chromium.chrome.browser.metrics; -import android.content.Context; - import org.chromium.base.Callback; +import org.chromium.base.annotations.NativeMethods; /** * Sets up communication with the VariationsService. This is primarily used for @@ -20,14 +19,9 @@ * Triggers to the native VariationsService that the application has entered the foreground. */ public void start() { - start(null); - } - - // TODO(agrieve): Delete after updating downstream. - public void start(Context unused) { // If |mRestrictModeFetchStarted| is true and |mRestrictMode| is null, then async - // initializationn is in progress and nativeStartVariationsSession() will be called - // when it completes. + // initialization is in progress and VariationsSessionJni.get().startVariationsSession() + // will be called when it completes. if (mRestrictModeFetchStarted && mRestrictMode == null) { return; } @@ -36,7 +30,8 @@ getRestrictModeValue(new Callback<String>() { @Override public void onResult(String restrictMode) { - nativeStartVariationsSession(mRestrictMode); + VariationsSessionJni.get().startVariationsSession( + VariationsSession.this, mRestrictMode); } }); } @@ -71,11 +66,6 @@ * restrict values, which must not be null. */ protected void getRestrictMode(Callback<String> callback) { - // TODO(agrieve): Refactor downstream. - getRestrictMode(null, callback); - } - - protected void getRestrictMode(Context unused, Callback<String> callback) { callback.onResult(""); } @@ -83,9 +73,12 @@ * @return The latest country according to the current variations state. Null if not known. */ public String getLatestCountry() { - return nativeGetLatestCountry(); + return VariationsSessionJni.get().getLatestCountry(this); } - protected native void nativeStartVariationsSession(String restrictMode); - protected native String nativeGetLatestCountry(); + @NativeMethods + interface Natives { + void startVariationsSession(VariationsSession caller, String restrictMode); + String getLatestCountry(VariationsSession caller); + } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/sync/ManageSyncPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/sync/ManageSyncPreferences.java index 64848db8..353debc89 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/sync/ManageSyncPreferences.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/sync/ManageSyncPreferences.java
@@ -263,10 +263,10 @@ closeDialogIfOpen(FRAGMENT_ENTER_PASSPHRASE); return; } - if (!mProfileSyncService.isPassphraseRequiredForDecryption()) { + if (!mProfileSyncService.isPassphraseRequiredForPreferredDataTypes()) { closeDialogIfOpen(FRAGMENT_ENTER_PASSPHRASE); } - if (mProfileSyncService.isPassphraseRequiredForDecryption() && isAdded()) { + if (mProfileSyncService.isPassphraseRequiredForPreferredDataTypes() && isAdded()) { mSyncEncryption.setSummary( errorSummary(getString(R.string.sync_need_passphrase), getActivity())); } @@ -344,7 +344,7 @@ @Override public boolean onPassphraseEntered(String passphrase) { if (!mProfileSyncService.isEngineInitialized() - || !mProfileSyncService.isPassphraseRequiredForDecryption()) { + || !mProfileSyncService.isPassphraseRequiredForPreferredDataTypes()) { // If the engine was shut down since the dialog was opened, or the passphrase isn't // required anymore, do nothing. return false; @@ -397,7 +397,7 @@ private void onSyncEncryptionClicked() { if (!mProfileSyncService.isEngineInitialized()) return; - if (mProfileSyncService.isPassphraseRequiredForDecryption()) { + if (mProfileSyncService.isPassphraseRequiredForPreferredDataTypes()) { displayPassphraseDialog(); } else { displayPassphraseTypeDialog();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/sync/SyncAndServicesPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/sync/SyncAndServicesPreferences.java index d03d866f..13f4f5c 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/sync/SyncAndServicesPreferences.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/sync/SyncAndServicesPreferences.java
@@ -401,7 +401,7 @@ @Override public boolean onPassphraseEntered(String passphrase) { if (!mProfileSyncService.isEngineInitialized() - || !mProfileSyncService.isPassphraseRequiredForDecryption()) { + || !mProfileSyncService.isPassphraseRequiredForPreferredDataTypes()) { // If the engine was shut down since the dialog was opened, or the passphrase isn't // required anymore, do nothing. return false; @@ -438,7 +438,7 @@ } if (mProfileSyncService.isEngineInitialized() - && mProfileSyncService.isPassphraseRequiredForDecryption()) { + && mProfileSyncService.isPassphraseRequiredForPreferredDataTypes()) { return SyncError.PASSPHRASE_REQUIRED; } @@ -557,7 +557,7 @@ private void updateSyncPreferences() { if (!mProfileSyncService.isEngineInitialized() - || !mProfileSyncService.isPassphraseRequiredForDecryption()) { + || !mProfileSyncService.isPassphraseRequiredForPreferredDataTypes()) { closeDialogIfOpen(FRAGMENT_ENTER_PASSPHRASE); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/sync/SyncPreferenceUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/sync/SyncPreferenceUtils.java index 0539e9e0..bee34685 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/sync/SyncPreferenceUtils.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/sync/SyncPreferenceUtils.java
@@ -60,7 +60,7 @@ } if (profileSyncService.isSyncActive() - && profileSyncService.isPassphraseRequiredForDecryption()) { + && profileSyncService.isPassphraseRequiredForPreferredDataTypes()) { return true; } } @@ -115,7 +115,7 @@ return res.getString(R.string.sync_setup_progress); } - if (profileSyncService.isPassphraseRequiredForDecryption()) { + if (profileSyncService.isPassphraseRequiredForPreferredDataTypes()) { return res.getString(R.string.sync_need_passphrase); } return context.getString(R.string.sync_and_services_summary_sync_on); @@ -149,7 +149,7 @@ if (profileSyncService.isEngineInitialized() && (profileSyncService.hasUnrecoverableError() || profileSyncService.getAuthError() != GoogleServiceAuthError.State.NONE - || profileSyncService.isPassphraseRequiredForDecryption())) { + || profileSyncService.isPassphraseRequiredForPreferredDataTypes())) { return UiUtils.getTintedDrawable( context, R.drawable.ic_sync_error_40dp, R.color.default_red); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java index 4d31065..78e2d95 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/ProfileSyncService.java
@@ -371,7 +371,7 @@ * running (isEngineInitialized() returns true) before calling this function. * <p/> * This method should only be used if you want to know the raw value. For checking whether - * we should ask the user for a passphrase, use isPassphraseRequiredForDecryption(). + * we should ask the user for a passphrase, use isPassphraseRequiredForPreferredDataTypes(). */ public @Passphrase.Type int getPassphraseType() { assert isEngineInitialized(); @@ -442,9 +442,9 @@ * * @return true if we need a passphrase. */ - public boolean isPassphraseRequiredForDecryption() { + public boolean isPassphraseRequiredForPreferredDataTypes() { assert isEngineInitialized(); - return ProfileSyncServiceJni.get().isPassphraseRequiredForDecryption( + return ProfileSyncServiceJni.get().isPassphraseRequiredForPreferredDataTypes( mNativeProfileSyncServiceAndroid, ProfileSyncService.this); } @@ -646,7 +646,7 @@ long nativeProfileSyncServiceAndroid, ProfileSyncService caller); void enableEncryptEverything( long nativeProfileSyncServiceAndroid, ProfileSyncService caller); - boolean isPassphraseRequiredForDecryption( + boolean isPassphraseRequiredForPreferredDataTypes( long nativeProfileSyncServiceAndroid, ProfileSyncService caller); boolean isUsingSecondaryPassphrase( long nativeProfileSyncServiceAndroid, ProfileSyncService caller);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncNotificationController.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncNotificationController.java index 7a1eee0..a12fbbbd 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncNotificationController.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncNotificationController.java
@@ -62,7 +62,7 @@ GoogleServiceAuthError.getMessageID(mProfileSyncService.getAuthError()), createSettingsIntent()); } else if (mProfileSyncService.isEngineInitialized() - && mProfileSyncService.isPassphraseRequiredForDecryption()) { + && mProfileSyncService.isPassphraseRequiredForPreferredDataTypes()) { if (mProfileSyncService.isPassphrasePrompted()) { return; }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeProfileSyncService.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeProfileSyncService.java index 8ae0ee6..846514f 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeProfileSyncService.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/FakeProfileSyncService.java
@@ -56,7 +56,7 @@ } @Override - public boolean isPassphraseRequiredForDecryption() { + public boolean isPassphraseRequiredForPreferredDataTypes() { return mPassphraseRequiredForDecryption; }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/metrics/VariationsSessionTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/metrics/VariationsSessionTest.java index 3bfbf8c..71dea3cf 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/metrics/VariationsSessionTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/metrics/VariationsSessionTest.java
@@ -4,19 +4,23 @@ package org.chromium.chrome.browser.metrics; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import org.robolectric.annotation.Config; import org.chromium.base.Callback; import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.base.test.util.JniMocker; /** * Tests for VariationsSession @@ -24,6 +28,11 @@ @RunWith(BaseRobolectricTestRunner.class) @Config(manifest = Config.NONE) public class VariationsSessionTest { + @Rule + public JniMocker mocker = new JniMocker(); + @Mock + private VariationsSession.Natives mVariationsSessionJniMock; + private TestVariationsSession mSession; private static class TestVariationsSession extends VariationsSession { @@ -37,26 +46,24 @@ public void runCallback(String value) { mCallback.onResult(value); } - - @Override - protected void nativeStartVariationsSession(String restrictMode) { - // No-op for tests. - } } @Before public void setUp() { - mSession = spy(new TestVariationsSession()); + MockitoAnnotations.initMocks(this); + mocker.mock(VariationsSessionJni.TEST_HOOKS, mVariationsSessionJniMock); + mSession = new TestVariationsSession(); } @Test public void testStart() { mSession.start(); - verify(mSession, never()).nativeStartVariationsSession(any(String.class)); + verify(mVariationsSessionJniMock, never()) + .startVariationsSession(eq(mSession), any(String.class)); String restrictValue = "test"; mSession.runCallback(restrictValue); - verify(mSession, times(1)).nativeStartVariationsSession(restrictValue); + verify(mVariationsSessionJniMock, times(1)).startVariationsSession(mSession, restrictValue); } @Test @@ -67,9 +74,10 @@ }); String restrictValue = "test"; mSession.runCallback(restrictValue); - verify(mSession, never()).nativeStartVariationsSession(any(String.class)); + verify(mVariationsSessionJniMock, never()) + .startVariationsSession(eq(mSession), any(String.class)); mSession.start(); - verify(mSession, times(1)).nativeStartVariationsSession(restrictValue); + verify(mVariationsSessionJniMock, times(1)).startVariationsSession(mSession, restrictValue); } }
diff --git a/chrome/android/modules/test_dummy/internal/BUILD.gn b/chrome/android/modules/test_dummy/internal/BUILD.gn index a361216..0547ad5 100644 --- a/chrome/android/modules/test_dummy/internal/BUILD.gn +++ b/chrome/android/modules/test_dummy/internal/BUILD.gn
@@ -30,6 +30,7 @@ "entrypoints.cc", ] deps = [ + ":jni_registration", "//base", "//chrome/android/features/test_dummy/internal:native", ] @@ -38,12 +39,6 @@ if (use_native_modules) { cflags = [ "-fsymbol-partition=libtest_dummy.so" ] } - - if (current_toolchain != default_toolchain) { - deps += [ ":jni_registration_secondary($default_toolchain)" ] - } else { - deps += [ ":jni_registration($default_toolchain)" ] - } } # TODO(https://crbug.com/1008109): Adapt JNI registration to point at a Java @@ -55,26 +50,9 @@ # including a registration stub, as Chrome's base library does. That requires # two sets of registration targets, so that the feature module template can # selectively pull in the real version or a stub. -if (current_toolchain == default_toolchain) { - generate_jni_registration("jni_registration") { - target = - "//chrome/android:chrome_modern_public_bundle__test_dummy_bundle_module" - header_output = "$target_gen_dir/jni_registration.h" - namespace = "test_dummy" - } - - # Note also: We cannot currently build JNI registration on secondary ABI - # toolchain (dependency issues). Therefore, for Monochrome's 32-bit library, - # we need to use the 64-bit side registration header that ChromeModern uses. - # However, it's in the 64-bit output directory. We need to also generate a copy - # for the 32-bit directory. - if (android_64bit_target_cpu) { - generate_jni_registration("jni_registration_secondary") { - target = "//chrome/android:chrome_modern_public_bundle__test_dummy_bundle_module" - header_output = - get_label_info(":native($android_secondary_abi_toolchain)", - "target_gen_dir") + "/jni_registration.h" - namespace = "test_dummy" - } - } +generate_jni_registration("jni_registration") { + target = + "//chrome/android:chrome_modern_public_bundle__test_dummy_bundle_module" + header_output = "$target_gen_dir/jni_registration.h" + namespace = "test_dummy" }
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 56ceb04..2d5043e6 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd
@@ -8490,21 +8490,6 @@ <message name="IDS_MANAGE_PASSWORDS_AUTO_SIGNIN_TITLE_MD" desc="The title of the auto-signin toast in Material Design mode."> Signing in as </message> - <message name="IDS_CREDENTIAL_LEAK_TITLE" desc="The title of the credential leak dialog."> - Data breach reported - </message> - <message name="IDS_CREDENTIAL_LEAK_CHECK_PASSWORDS_MESSAGE" desc="The text that is used in credential leak detection dialog when saved credentials were used on multiple sites."> - A data breach on a site or app you use exposed your password. Chrome recommends checking your saved passwords now. - </message> - <message name="IDS_CREDENTIAL_LEAK_CHANGE_PASSWORD_MESSAGE" desc="The text that is used in credential leak detection dialog when credentials were leaked on current site only."> - A data breach on a site or app you use exposed your password. Chrome recommends <ph name="BOLD">$1<ex>changing your password</ex></ph> on <ph name="ORIGIN">$2<ex>example.com</ex></ph> now. - </message> - <message name="IDS_CREDENTIAL_LEAK_CHANGE_PASSWORD_BOLD_MESSAGE" desc="The text that is written in bold in leak dialog message when credentials were leaked on current site only."> - changing your password - </message> - <message name="IDS_CREDENTIAL_LEAK_CHANGE_AND_CHECK_PASSWORDS_MESSAGE" desc="The text that is used in credential leak detection dialog when credentials were not saved in chrome, but used on multiple sites."> - A data breach on a site or app you use exposed your password. Chrome recommends checking your saved passwords and changing your password on <ph name="ORIGIN">$1<ex>example.com</ex></ph>. - </message> <message name="IDS_AUTO_SIGNIN_FIRST_RUN_TITLE_MANY_DEVICES" desc="The title of the dialog during the autosign-in first run experience for the Chrome syncing users."> Sign in easily across devices </message> @@ -8524,16 +8509,6 @@ OK, got it </message> </if> - <if expr="use_titlecase"> - <message name="IDS_LEAK_CHECK_CREDENTIALS" desc="The text of the OK button in the dialog for credentials leaked on multiple sites."> - Check Passwords - </message> - </if> - <if expr="not use_titlecase"> - <message name="IDS_LEAK_CHECK_CREDENTIALS" desc="The text of the OK button in the dialog for credentials leaked on multiple sites."> - Check passwords - </message> - </if> <!-- Extra Mac UI Strings --> <if expr="is_macosx">
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc index ea130fa..59e12d6 100644 --- a/chrome/browser/android/chrome_feature_list.cc +++ b/chrome/browser/android/chrome_feature_list.cc
@@ -35,7 +35,6 @@ #include "components/signin/public/base/account_consistency_method.h" #include "components/subresource_filter/core/browser/subresource_filter_features.h" #include "components/sync/driver/sync_driver_switches.h" -#include "components/unified_consent/feature.h" #include "content/public/common/content_features.h" #include "media/base/media_switches.h" #include "services/device/public/cpp/device_features.h" @@ -223,7 +222,6 @@ &safe_browsing::kCaptureSafetyNetId, &signin::kMiceFeature, &switches::kSyncManualStartAndroid, - &unified_consent::kUnifiedConsent, &subresource_filter::kSafeBrowsingSubresourceFilter, };
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn index 2629898..b37e3f4 100644 --- a/chrome/browser/chromeos/BUILD.gn +++ b/chrome/browser/chromeos/BUILD.gn
@@ -672,6 +672,8 @@ "attestation/platform_verification_dialog.h", "attestation/platform_verification_flow.cc", "attestation/platform_verification_flow.h", + "attestation/tpm_challenge_key.cc", + "attestation/tpm_challenge_key.h", "authpolicy/auth_policy_credentials_manager.cc", "authpolicy/auth_policy_credentials_manager.h", "authpolicy/authpolicy_helper.cc", @@ -2487,7 +2489,10 @@ "attestation/fake_certificate.cc", "attestation/fake_certificate.h", "attestation/machine_certificate_uploader_impl_unittest.cc", + "attestation/mock_tpm_challenge_key.cc", + "attestation/mock_tpm_challenge_key.h", "attestation/platform_verification_flow_unittest.cc", + "attestation/tpm_challenge_key_unittest.cc", "authpolicy/auth_policy_credentials_manager_unittest.cc", "authpolicy/authpolicy_helper.unittest.cc", "base/file_flusher_unittest.cc",
diff --git a/chrome/browser/chromeos/account_manager/account_manager_util.cc b/chrome/browser/chromeos/account_manager/account_manager_util.cc index 93cd26c..597e19ce 100644 --- a/chrome/browser/chromeos/account_manager/account_manager_util.cc +++ b/chrome/browser/chromeos/account_manager/account_manager_util.cc
@@ -13,16 +13,12 @@ #include "chrome/browser/profiles/profiles_state.h" #include "chromeos/components/account_manager/account_manager.h" #include "chromeos/components/account_manager/account_manager_factory.h" -#include "chromeos/constants/chromeos_features.h" #include "chromeos/tpm/install_attributes.h" #include "services/network/public/cpp/shared_url_loader_factory.h" namespace chromeos { bool IsAccountManagerAvailable(const Profile* const profile) { - if (!features::IsAccountManagerEnabled()) - return false; - // Signin Profile does not have any accounts associated with it. if (chromeos::ProfileHelper::IsSigninProfile(profile)) return false;
diff --git a/chrome/browser/chromeos/attestation/OWNERS b/chrome/browser/chromeos/attestation/OWNERS index 9c8d156..57c540d2 100644 --- a/chrome/browser/chromeos/attestation/OWNERS +++ b/chrome/browser/chromeos/attestation/OWNERS
@@ -3,3 +3,7 @@ dkrahn@chromium.org mnissler@chromium.org bartfab@chromium.org + +per-file *tpm_challenge_key*=pmarko@chromium.org +per-file *tpm_challenge_key*=emaxx@chromium.org +per-file *tpm_challenge_key*=drcrash@chromium.org
diff --git a/chrome/browser/chromeos/attestation/mock_tpm_challenge_key.cc b/chrome/browser/chromeos/attestation/mock_tpm_challenge_key.cc new file mode 100644 index 0000000..b862be8 --- /dev/null +++ b/chrome/browser/chromeos/attestation/mock_tpm_challenge_key.cc
@@ -0,0 +1,28 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/chromeos/attestation/mock_tpm_challenge_key.h" +#include <utility> + +using ::testing::Invoke; +using ::testing::WithArgs; + +namespace chromeos { +namespace attestation { + +MockTpmChallengeKey::MockTpmChallengeKey() = default; +MockTpmChallengeKey::~MockTpmChallengeKey() = default; + +void MockTpmChallengeKey::EnableFake() { + ON_CALL(*this, Run) + .WillByDefault( + WithArgs<2>(Invoke(this, &MockTpmChallengeKey::FakeRunSuccess))); +} + +void MockTpmChallengeKey::FakeRunSuccess(TpmChallengeKeyCallback callback) { + std::move(callback).Run(TpmChallengeKeyResult::MakeResult("response")); +} + +} // namespace attestation +} // namespace chromeos
diff --git a/chrome/browser/chromeos/attestation/mock_tpm_challenge_key.h b/chrome/browser/chromeos/attestation/mock_tpm_challenge_key.h new file mode 100644 index 0000000..f69e55b --- /dev/null +++ b/chrome/browser/chromeos/attestation/mock_tpm_challenge_key.h
@@ -0,0 +1,40 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_CHROMEOS_ATTESTATION_MOCK_TPM_CHALLENGE_KEY_H_ +#define CHROME_BROWSER_CHROMEOS_ATTESTATION_MOCK_TPM_CHALLENGE_KEY_H_ + +#include <string> + +#include "chrome/browser/chromeos/attestation/tpm_challenge_key.h" +#include "chromeos/dbus/constants/attestation_constants.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace chromeos { +namespace attestation { + +class MockTpmChallengeKey : public TpmChallengeKey { + public: + MockTpmChallengeKey(); + ~MockTpmChallengeKey() override; + + void EnableFake(); + + MOCK_METHOD(void, + Run, + (chromeos::attestation::AttestationKeyType key_type, + Profile* profile, + TpmChallengeKeyCallback callback, + const std::string& challenge, + bool register_key, + const std::string& key_name_for_spkac), + (override)); + + void FakeRunSuccess(TpmChallengeKeyCallback callback); +}; + +} // namespace attestation +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_ATTESTATION_MOCK_TPM_CHALLENGE_KEY_H_
diff --git a/chrome/browser/chromeos/attestation/tpm_challenge_key.cc b/chrome/browser/chromeos/attestation/tpm_challenge_key.cc new file mode 100644 index 0000000..c2c8725 --- /dev/null +++ b/chrome/browser/chromeos/attestation/tpm_challenge_key.cc
@@ -0,0 +1,528 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/chromeos/attestation/tpm_challenge_key.h" + +#include "base/base64.h" +#include "base/bind.h" +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "base/values.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/chromeos/attestation/attestation_ca_client.h" +#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h" +#include "chrome/browser/chromeos/profiles/profile_helper.h" +#include "chrome/browser/chromeos/settings/cros_settings.h" +#include "chrome/browser/extensions/chrome_extension_function_details.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/common/pref_names.h" +#include "chromeos/cryptohome/async_method_caller.h" +#include "chromeos/cryptohome/cryptohome_parameters.h" +#include "chromeos/settings/cros_settings_names.h" +#include "chromeos/tpm/install_attributes.h" +#include "components/pref_registry/pref_registry_syncable.h" +#include "components/prefs/pref_service.h" + +namespace chromeos { +namespace attestation { + +//========================= TpmChallengeKeyFactory ============================= + +TpmChallengeKey* TpmChallengeKeyFactory::next_result_for_testing_ = nullptr; + +// static +std::unique_ptr<TpmChallengeKey> TpmChallengeKeyFactory::Create() { + if (LIKELY(!next_result_for_testing_)) { + return std::make_unique<TpmChallengeKeyImpl>(); + } + + std::unique_ptr<TpmChallengeKey> result(next_result_for_testing_); + next_result_for_testing_ = nullptr; + return result; +} + +// static +void TpmChallengeKeyFactory::SetForTesting( + std::unique_ptr<TpmChallengeKey> next_result) { + // unique_ptr itself cannot be stored in a static variable because of its + // complex destructor. + next_result_for_testing_ = next_result.release(); +} + +//============================ TpmChallengeKeyResult =========================== + +// static +TpmChallengeKeyResult TpmChallengeKeyResult::MakeResult( + const std::string& success_result) { + return TpmChallengeKeyResult{/*is_success=*/true, + /*data=*/success_result, + /*error_message=*/""}; +} + +// static +TpmChallengeKeyResult TpmChallengeKeyResult::MakeError( + const std::string& error_message) { + return TpmChallengeKeyResult{/*is_success=*/false, + /*data=*/"", + /*error_message=*/error_message}; +} + +//=========================== TpmChallengeKeyImpl ============================== + +namespace { +// Returns true if the device is enterprise managed. +bool IsEnterpriseDevice() { + return InstallAttributes::Get()->IsEnterpriseManaged(); +} + +// For personal devices, we don't need to check if remote attestation is +// enabled in the device, but we need to ask for user consent if the key +// does not exist. +bool IsUserConsentRequired() { + return !IsEnterpriseDevice(); +} +} // namespace + +const char TpmChallengeKeyImpl::kDevicePolicyDisabledError[] = + "Remote attestation is not enabled for your device."; +const char TpmChallengeKeyImpl::kSignChallengeFailedError[] = + "Failed to sign the challenge."; +const char TpmChallengeKeyImpl::kUserNotManaged[] = + "The user account is not enterprise managed."; +const char TpmChallengeKeyImpl::kKeyRegistrationFailedError[] = + "Key registration failed."; +const char TpmChallengeKeyImpl::kGetCertificateFailedError[] = + "Failed to get Enterprise certificate. Error code = %d"; +const char TpmChallengeKeyImpl::kUserPolicyDisabledError[] = + "Remote attestation is not enabled for your account."; +const char TpmChallengeKeyImpl::kUserKeyNotAvailable[] = + "User keys cannot be challenged in this profile."; +const char TpmChallengeKeyImpl::kNonEnterpriseDeviceError[] = + "The device is not enterprise enrolled."; + +void TpmChallengeKey::RegisterProfilePrefs( + user_prefs::PrefRegistrySyncable* registry) { + registry->RegisterBooleanPref(prefs::kAttestationEnabled, false); +} + +TpmChallengeKeyImpl::TpmChallengeKeyImpl() + : default_attestation_flow_(std::make_unique<AttestationFlow>( + cryptohome::AsyncMethodCaller::GetInstance(), + CryptohomeClient::Get(), + std::make_unique<AttestationCAClient>())), + attestation_flow_(default_attestation_flow_.get()) {} + +TpmChallengeKeyImpl::TpmChallengeKeyImpl( + AttestationFlow* attestation_flow_for_testing) + : attestation_flow_(attestation_flow_for_testing) {} + +TpmChallengeKeyImpl::~TpmChallengeKeyImpl() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +} + +void TpmChallengeKeyImpl::Run(AttestationKeyType key_type, + Profile* profile, + TpmChallengeKeyCallback callback, + const std::string& challenge, + bool register_key, + const std::string& key_name_for_spkac) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + // |key_name_for_spkac| was designed to only be used with KEY_DEVICE. + DCHECK((key_type != KEY_USER) || key_name_for_spkac.empty()) + << "Key name for SPKAC will be unused."; + + // For device key: if |register_key| is true, |key_name_for_spkac| should not + // be empty; if |register_key| is false, |key_name_for_spkac| is not used. + DCHECK((key_type != KEY_DEVICE) || + (register_key == !key_name_for_spkac.empty())) + << "Invalid arguments: " << register_key << " " + << !key_name_for_spkac.empty(); + + challenge_ = challenge; + profile_ = profile; + callback_ = std::move(callback); + key_type_ = key_type; + register_key_ = register_key; + key_name_for_spkac_ = key_name_for_spkac; + + switch (key_type_) { + case KEY_DEVICE: + ChallengeMachineKey(); + return; + case KEY_USER: + ChallengeUserKey(); + return; + } + NOTREACHED(); +} + +void TpmChallengeKeyImpl::ChallengeMachineKey() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + // Check if the device is enterprise enrolled. + if (!IsEnterpriseDevice()) { + std::move(callback_).Run( + TpmChallengeKeyResult::MakeError(kNonEnterpriseDeviceError)); + return; + } + + // Check whether the user is managed unless the signin profile is used. + if (GetUser() && !IsUserAffiliated()) { + std::move(callback_).Run(TpmChallengeKeyResult::MakeError(kUserNotManaged)); + return; + } + + // Check if remote attestation is enabled in the device policy. + GetDeviceAttestationEnabled(base::BindRepeating( + &TpmChallengeKeyImpl::GetDeviceAttestationEnabledCallback, + weak_factory_.GetWeakPtr())); +} + +void TpmChallengeKeyImpl::ChallengeUserKey() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + // Check if user keys are available in this profile. + if (!GetUser()) { + std::move(callback_).Run( + TpmChallengeKeyResult::MakeError(kUserKeyNotAvailable)); + return; + } + + if (!IsRemoteAttestationEnabledForUser()) { + std::move(callback_).Run( + TpmChallengeKeyResult::MakeError(kUserPolicyDisabledError)); + return; + } + + if (IsEnterpriseDevice()) { + if (!IsUserAffiliated()) { + std::move(callback_).Run( + TpmChallengeKeyResult::MakeError(kUserNotManaged)); + return; + } + + // Check if remote attestation is enabled in the device policy. + GetDeviceAttestationEnabled(base::BindRepeating( + &TpmChallengeKeyImpl::GetDeviceAttestationEnabledCallback, + weak_factory_.GetWeakPtr())); + } else { + GetDeviceAttestationEnabledCallback(true); + } +} + +bool TpmChallengeKeyImpl::IsUserAffiliated() const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + const user_manager::User* const user = GetUser(); + if (user) { + return user->IsAffiliated(); + } + return false; +} + +bool TpmChallengeKeyImpl::IsRemoteAttestationEnabledForUser() const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + PrefService* prefs = profile_->GetPrefs(); + if (prefs && prefs->IsManagedPreference(prefs::kAttestationEnabled)) { + return prefs->GetBoolean(prefs::kAttestationEnabled); + } + return false; +} + +std::string TpmChallengeKeyImpl::GetEmail() const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + switch (key_type_) { + case KEY_DEVICE: + return InstallAttributes::Get()->GetDomain(); + case KEY_USER: + return GetAccountId().GetUserEmail(); + } + NOTREACHED(); +} + +const char* TpmChallengeKeyImpl::GetKeyName() const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + switch (key_type_) { + case KEY_DEVICE: + return kEnterpriseMachineKey; + case KEY_USER: + return kEnterpriseUserKey; + } + NOTREACHED(); +} + +AttestationCertificateProfile TpmChallengeKeyImpl::GetCertificateProfile() + const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + switch (key_type_) { + case KEY_DEVICE: + return PROFILE_ENTERPRISE_MACHINE_CERTIFICATE; + case KEY_USER: + return PROFILE_ENTERPRISE_USER_CERTIFICATE; + } + NOTREACHED(); +} + +std::string TpmChallengeKeyImpl::GetKeyNameForRegister() const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + switch (key_type_) { + case KEY_DEVICE: + return key_name_for_spkac_; + case KEY_USER: + return GetKeyName(); + } + NOTREACHED(); +} + +const user_manager::User* TpmChallengeKeyImpl::GetUser() const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + return ProfileHelper::Get()->GetUserByProfile(profile_); +} + +AccountId TpmChallengeKeyImpl::GetAccountId() const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + const user_manager::User* user = GetUser(); + if (user) { + return user->GetAccountId(); + } + // Signin profile doesn't have associated user. + return EmptyAccountId(); +} + +void TpmChallengeKeyImpl::GetDeviceAttestationEnabled( + const base::RepeatingCallback<void(bool)>& callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + CrosSettings* settings = CrosSettings::Get(); + CrosSettingsProvider::TrustedStatus status = settings->PrepareTrustedValues( + base::BindRepeating(&TpmChallengeKeyImpl::GetDeviceAttestationEnabled, + weak_factory_.GetWeakPtr(), callback)); + + bool value = false; + switch (status) { + case CrosSettingsProvider::TRUSTED: + if (!settings->GetBoolean(kDeviceAttestationEnabled, &value)) { + value = false; + } + break; + case CrosSettingsProvider::TEMPORARILY_UNTRUSTED: + // Do nothing. This function will be called again when the values are + // ready. + return; + case CrosSettingsProvider::PERMANENTLY_UNTRUSTED: + // If the value cannot be trusted, we assume that the device attestation + // is false to be on the safe side. + break; + } + + callback.Run(value); +} + +void TpmChallengeKeyImpl::GetDeviceAttestationEnabledCallback(bool enabled) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + if (!enabled) { + std::move(callback_).Run( + TpmChallengeKeyResult::MakeError(kDevicePolicyDisabledError)); + return; + } + + PrepareKey(); +} + +void TpmChallengeKeyImpl::PrepareKey() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + CryptohomeClient::Get()->TpmAttestationIsPrepared( + base::BindOnce(&TpmChallengeKeyImpl::IsAttestationPreparedCallback, + weak_factory_.GetWeakPtr())); +} + +void TpmChallengeKeyImpl::IsAttestationPreparedCallback( + base::Optional<bool> result) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + if (!result.has_value()) { + PrepareKeyFinished(PrepareKeyResult::kDbusError); + return; + } + if (!result.value()) { + CryptohomeClient::Get()->TpmIsEnabled( + base::BindOnce(&TpmChallengeKeyImpl::PrepareKeyErrorHandlerCallback, + weak_factory_.GetWeakPtr())); + return; + } + + if (!key_name_for_spkac_.empty()) { + // Generate a new key and have it signed by PCA. + attestation_flow_->GetCertificate( + GetCertificateProfile(), GetAccountId(), + std::string(), // Not used. + true, // Force a new key to be generated. + key_name_for_spkac_, + base::BindRepeating(&TpmChallengeKeyImpl::GetCertificateCallback, + weak_factory_.GetWeakPtr())); + return; + } + + // Attestation is available, see if the key we need already exists. + CryptohomeClient::Get()->TpmAttestationDoesKeyExist( + key_type_, + cryptohome::CreateAccountIdentifierFromAccountId(GetAccountId()), + GetKeyName(), + base::BindRepeating(&TpmChallengeKeyImpl::DoesKeyExistCallback, + weak_factory_.GetWeakPtr())); +} + +void TpmChallengeKeyImpl::PrepareKeyErrorHandlerCallback( + base::Optional<bool> is_tpm_enabled) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + if (!is_tpm_enabled.has_value()) { + PrepareKeyFinished(PrepareKeyResult::kDbusError); + return; + } + + if (is_tpm_enabled.value()) { + PrepareKeyFinished(PrepareKeyResult::kResetRequired); + } else { + PrepareKeyFinished(PrepareKeyResult::kAttestationUnsupported); + } +} + +void TpmChallengeKeyImpl::DoesKeyExistCallback(base::Optional<bool> result) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + if (!result.has_value()) { + PrepareKeyFinished(PrepareKeyResult::kDbusError); + return; + } + + if (result.value()) { + // The key exists. Do nothing more. + PrepareKeyFinished(PrepareKeyResult::kOk); + return; + } + + // The key does not exist. Create a new key and have it signed by PCA. + if (IsUserConsentRequired()) { + // We should ask the user explicitly before sending any private + // information to PCA. + AskForUserConsent( + base::BindOnce(&TpmChallengeKeyImpl::AskForUserConsentCallback, + weak_factory_.GetWeakPtr())); + } else { + // User consent is not required. Skip to the next step. + AskForUserConsentCallback(true); + } +} + +void TpmChallengeKeyImpl::AskForUserConsent( + base::OnceCallback<void(bool)> callback) const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + // TODO(davidyu): right now we just simply reject the request before we have + // a way to ask for user consent. + std::move(callback).Run(false); +} + +void TpmChallengeKeyImpl::AskForUserConsentCallback(bool result) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + if (!result) { + // The user rejects the request. + PrepareKeyFinished(PrepareKeyResult::kUserRejected); + return; + } + + // Generate a new key and have it signed by PCA. + attestation_flow_->GetCertificate( + GetCertificateProfile(), GetAccountId(), + std::string(), // Not used. + true, // Force a new key to be generated. + std::string(), // Leave key name empty to generate a default name. + base::BindRepeating(&TpmChallengeKeyImpl::GetCertificateCallback, + weak_factory_.GetWeakPtr())); +} + +void TpmChallengeKeyImpl::GetCertificateCallback( + AttestationStatus status, + const std::string& pem_certificate_chain) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + if (status != ATTESTATION_SUCCESS) { + PrepareKeyFinished(PrepareKeyResult::kGetCertificateFailed); + return; + } + + PrepareKeyFinished(PrepareKeyResult::kOk); +} + +void TpmChallengeKeyImpl::PrepareKeyFinished(PrepareKeyResult result) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + if (result != PrepareKeyResult::kOk) { + std::move(callback_).Run(TpmChallengeKeyResult::MakeError( + base::StringPrintf(kGetCertificateFailedError, result))); + return; + } + + // Everything is checked. Sign the challenge. + cryptohome::AsyncMethodCaller::GetInstance() + ->TpmAttestationSignEnterpriseChallenge( + key_type_, cryptohome::Identification(GetAccountId()), GetKeyName(), + GetEmail(), InstallAttributes::Get()->GetDeviceId(), + register_key_ ? CHALLENGE_INCLUDE_SIGNED_PUBLIC_KEY + : CHALLENGE_OPTION_NONE, + challenge_, key_name_for_spkac_, + base::BindRepeating(&TpmChallengeKeyImpl::SignChallengeCallback, + weak_factory_.GetWeakPtr(), register_key_)); +} + +void TpmChallengeKeyImpl::SignChallengeCallback(bool register_key, + bool success, + const std::string& response) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + if (!success) { + std::move(callback_).Run( + TpmChallengeKeyResult::MakeError(kSignChallengeFailedError)); + return; + } + + if (register_key) { + cryptohome::AsyncMethodCaller::GetInstance()->TpmAttestationRegisterKey( + key_type_, cryptohome::Identification(GetAccountId()), + GetKeyNameForRegister(), + base::BindRepeating(&TpmChallengeKeyImpl::RegisterKeyCallback, + weak_factory_.GetWeakPtr(), response)); + } else { + RegisterKeyCallback(response, true, cryptohome::MOUNT_ERROR_NONE); + } +} + +void TpmChallengeKeyImpl::RegisterKeyCallback( + const std::string& response, + bool success, + cryptohome::MountError return_code) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + if (!success || return_code != cryptohome::MOUNT_ERROR_NONE) { + std::move(callback_).Run( + TpmChallengeKeyResult::MakeError(kKeyRegistrationFailedError)); + return; + } + std::move(callback_).Run(TpmChallengeKeyResult::MakeResult(response)); +} + +} // namespace attestation +} // namespace chromeos
diff --git a/chrome/browser/chromeos/attestation/tpm_challenge_key.h b/chrome/browser/chromeos/attestation/tpm_challenge_key.h new file mode 100644 index 0000000..aaf154c --- /dev/null +++ b/chrome/browser/chromeos/attestation/tpm_challenge_key.h
@@ -0,0 +1,196 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_CHROMEOS_ATTESTATION_TPM_CHALLENGE_KEY_H_ +#define CHROME_BROWSER_CHROMEOS_ATTESTATION_TPM_CHALLENGE_KEY_H_ + +#include <memory> +#include <string> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "base/optional.h" +#include "base/sequence_checker.h" +#include "chromeos/attestation/attestation_flow.h" +#include "chromeos/dbus/constants/attestation_constants.h" +#include "chromeos/dbus/cryptohome/cryptohome_client.h" +#include "components/account_id/account_id.h" +#include "components/user_manager/user.h" +#include "third_party/cros_system_api/dbus/cryptohome/dbus-constants.h" + +class Profile; + +namespace user_prefs { +class PrefRegistrySyncable; +} + +namespace chromeos { +namespace attestation { + +//========================= TpmChallengeKeyFactory ============================= + +class TpmChallengeKey; + +class TpmChallengeKeyFactory { + public: + static std::unique_ptr<TpmChallengeKey> Create(); + static void SetForTesting(std::unique_ptr<TpmChallengeKey> next_result); + + private: + static TpmChallengeKey* next_result_for_testing_; +}; + +//========================= TpmChallengeKeyResult ============================== + +// If |is_success| is true then |data| contains a challenge response and +// |error_message| is empty. If |is_success| is false then |error_message| +// contains an error message and |data| is empty. +struct TpmChallengeKeyResult { + static TpmChallengeKeyResult MakeResult(const std::string& success_result); + static TpmChallengeKeyResult MakeError(const std::string& error_message); + + bool is_success = false; + std::string data; + std::string error_message; +}; + +//=========================== TpmChallengeKey ================================== + +using TpmChallengeKeyCallback = + base::OnceCallback<void(const TpmChallengeKeyResult& result)>; + +// Asynchronously run the flow to challenge a key in the caller context. +class TpmChallengeKey { + public: + TpmChallengeKey(const TpmChallengeKey&) = delete; + TpmChallengeKey& operator=(const TpmChallengeKey&) = delete; + virtual ~TpmChallengeKey() = default; + + static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry); + + // Should be called only once for every instance. |TpmChallengeKey| object + // should live as long as response from |Run| function via |callback| is + // expected. On destruction it stops challenge process and silently discards + // callback. |key_name_for_spkac| the name of the key used for + // SignedPublicKeyAndChallenge when sending a challenge machine key request + // with |registerKey|=true. + virtual void Run(AttestationKeyType key_type, + Profile* profile, + TpmChallengeKeyCallback callback, + const std::string& challenge, + bool register_key, + const std::string& key_name_for_spkac) = 0; + + protected: + // Use TpmChallengeKeyFactory for creation. + TpmChallengeKey() = default; +}; + +//=========================== TpmChallengeKeyImpl ============================== + +class TpmChallengeKeyImpl : public TpmChallengeKey { + public: + static const char kDevicePolicyDisabledError[]; + static const char kSignChallengeFailedError[]; + static const char kUserNotManaged[]; + static const char kKeyRegistrationFailedError[]; + static const char kGetCertificateFailedError[]; + static const char kUserKeyNotAvailable[]; + static const char kUserPolicyDisabledError[]; + static const char kNonEnterpriseDeviceError[]; + + // Use TpmChallengeKeyFactory for creation. + TpmChallengeKeyImpl(); + // Use only for testing. + explicit TpmChallengeKeyImpl(AttestationFlow* attestation_flow_for_testing); + TpmChallengeKeyImpl(const TpmChallengeKeyImpl&) = delete; + TpmChallengeKeyImpl& operator=(const TpmChallengeKeyImpl&) = delete; + ~TpmChallengeKeyImpl() override; + + // TpmChallengeKey + void Run(AttestationKeyType key_type, + Profile* profile, + TpmChallengeKeyCallback callback, + const std::string& challenge, + bool register_key, + const std::string& key_name_for_spkac) override; + + private: + enum class PrepareKeyResult { + kOk, + kDbusError, + kUserRejected, + kGetCertificateFailed, + kResetRequired, + kAttestationUnsupported + }; + + void ChallengeUserKey(); + void ChallengeMachineKey(); + + // Returns true if the user is managed and is affiliated with the domain the + // device is enrolled to. + bool IsUserAffiliated() const; + // Returns true if remote attestation is allowed and the setting is managed. + bool IsRemoteAttestationEnabledForUser() const; + + // Returns the enterprise domain the device is enrolled to or user email. + std::string GetEmail() const; + const char* GetKeyName() const; + AttestationCertificateProfile GetCertificateProfile() const; + std::string GetKeyNameForRegister() const; + const user_manager::User* GetUser() const; + AccountId GetAccountId() const; + + // Prepares the key for signing. It will first check if a new key should be + // generated, i.e. |key_name_for_spkac_| is not empty or the key doesn't exist + // and, if necessary, call AttestationFlow::GetCertificate() to get a new one. + // If |IsUserConsentRequired()| is true, it will explicitly ask for user + // consent before calling GetCertificate(). Ends up calling + // |PrepareKeyFinished| function. + void PrepareKey(); + void PrepareKeyFinished(PrepareKeyResult result); + + void SignChallengeCallback(bool register_key, + bool success, + const std::string& response); + void RegisterKeyCallback(const std::string& response, + bool success, + cryptohome::MountError return_code); + // Returns a trusted value from CrosSettings indicating if the device + // attestation is enabled. + void GetDeviceAttestationEnabled( + const base::RepeatingCallback<void(bool)>& callback); + void GetDeviceAttestationEnabledCallback(bool enabled); + + void IsAttestationPreparedCallback(base::Optional<bool> result); + void PrepareKeyErrorHandlerCallback(base::Optional<bool> is_tpm_enabled); + void DoesKeyExistCallback(base::Optional<bool> result); + void AskForUserConsent(base::OnceCallback<void(bool)> callback) const; + void AskForUserConsentCallback(bool result); + void GetCertificateCallback(AttestationStatus status, + const std::string& pem_certificate_chain); + + std::unique_ptr<AttestationFlow> default_attestation_flow_; + AttestationFlow* attestation_flow_ = nullptr; + + TpmChallengeKeyCallback callback_; + Profile* profile_ = nullptr; + + AttestationKeyType key_type_ = AttestationKeyType::KEY_DEVICE; + std::string challenge_; + + bool register_key_ = false; + std::string key_name_for_spkac_; + + SEQUENCE_CHECKER(sequence_checker_); + + base::WeakPtrFactory<TpmChallengeKeyImpl> weak_factory_{this}; +}; + +} // namespace attestation +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_ATTESTATION_TPM_CHALLENGE_KEY_H_
diff --git a/chrome/browser/chromeos/attestation/tpm_challenge_key_unittest.cc b/chrome/browser/chromeos/attestation/tpm_challenge_key_unittest.cc new file mode 100644 index 0000000..be0ec53 --- /dev/null +++ b/chrome/browser/chromeos/attestation/tpm_challenge_key_unittest.cc
@@ -0,0 +1,746 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/chromeos/attestation/tpm_challenge_key.h" + +#include <mutex> +#include <string> +#include <utility> + +#include "base/bind.h" +#include "base/location.h" +#include "base/memory/ptr_util.h" +#include "base/strings/stringprintf.h" +#include "base/threading/thread_task_runner_handle.h" +#include "base/values.h" +#include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h" +#include "chrome/browser/chromeos/profiles/profile_helper.h" +#include "chrome/browser/chromeos/settings/cros_settings.h" +#include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h" +#include "chrome/browser/extensions/extension_function_test_utils.h" +#include "chrome/browser/signin/identity_manager_factory.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/common/chrome_constants.h" +#include "chrome/common/pref_names.h" +#include "chrome/test/base/browser_with_test_window_test.h" +#include "chrome/test/base/testing_browser_process.h" +#include "chrome/test/base/testing_profile_manager.h" +#include "chromeos/attestation/mock_attestation_flow.h" +#include "chromeos/cryptohome/async_method_caller.h" +#include "chromeos/cryptohome/cryptohome_parameters.h" +#include "chromeos/cryptohome/mock_async_method_caller.h" +#include "chromeos/dbus/constants/attestation_constants.h" +#include "chromeos/dbus/cryptohome/fake_cryptohome_client.h" +#include "chromeos/tpm/stub_install_attributes.h" +#include "components/account_id/account_id.h" +#include "components/policy/core/common/cloud/cloud_policy_constants.h" +#include "components/prefs/pref_service.h" +#include "components/signin/public/identity_manager/identity_manager.h" +#include "components/signin/public/identity_manager/identity_test_utils.h" +#include "components/sync_preferences/testing_pref_service_syncable.h" +#include "components/user_manager/scoped_user_manager.h" +#include "extensions/common/extension_builder.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +using testing::_; +using testing::Invoke; +using testing::NiceMock; +using testing::Return; +using testing::WithArgs; + +namespace utils = extension_function_test_utils; + +namespace chromeos { +namespace attestation { + +namespace { + +// Certificate errors as reported to the calling extension. +const int kDBusError = 1; +const int kUserRejected = 2; +const int kGetCertificateFailed = 3; +const int kResetRequired = 4; +const int kPrepareKeyAttestationUnsupported = 5; + +const char kUserEmail[] = "test@google.com"; +const char kChallenge[] = "challenge"; +const char kResponse[] = "response"; +const char kKeyNameForSpkac[] = "attest-ent-machine-123456"; + +void RegisterKeyCallbackTrue( + chromeos::attestation::AttestationKeyType key_type, + const cryptohome::Identification& user_id, + const std::string& key_name, + const cryptohome::AsyncMethodCaller::Callback& callback) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(callback, true, cryptohome::MOUNT_ERROR_NONE)); +} + +void RegisterKeyCallbackFalse( + chromeos::attestation::AttestationKeyType key_type, + const cryptohome::Identification& user_id, + const std::string& key_name, + const cryptohome::AsyncMethodCaller::Callback& callback) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(callback, false, cryptohome::MOUNT_ERROR_NONE)); +} + +void SignChallengeCallbackTrue( + chromeos::attestation::AttestationKeyType key_type, + const cryptohome::Identification& user_id, + const std::string& key_name, + const std::string& domain, + const std::string& device_id, + chromeos::attestation::AttestationChallengeOptions options, + const std::string& challenge, + const std::string& key_name_for_spkac, + const cryptohome::AsyncMethodCaller::DataCallback& callback) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(callback, true, "response")); +} + +void SignChallengeCallbackFalse( + chromeos::attestation::AttestationKeyType key_type, + const cryptohome::Identification& user_id, + const std::string& key_name, + const std::string& domain, + const std::string& device_id, + chromeos::attestation::AttestationChallengeOptions options, + const std::string& challenge, + const std::string& key_name_for_spkac, + const cryptohome::AsyncMethodCaller::DataCallback& callback) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(callback, false, "")); +} + +void GetCertificateCallbackTrue( + chromeos::attestation::AttestationCertificateProfile certificate_profile, + const AccountId& account_id, + const std::string& request_origin, + bool force_new_key, + const std::string& key_name, + const chromeos::attestation::AttestationFlow::CertificateCallback& + callback) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindRepeating(callback, chromeos::attestation::ATTESTATION_SUCCESS, + "certificate")); +} + +void GetCertificateCallbackUnspecifiedFailure( + chromeos::attestation::AttestationCertificateProfile certificate_profile, + const AccountId& account_id, + const std::string& request_origin, + bool force_new_key, + const std::string& key_name, + const chromeos::attestation::AttestationFlow::CertificateCallback& + callback) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindRepeating( + callback, chromeos::attestation::ATTESTATION_UNSPECIFIED_FAILURE, + "")); +} + +void GetCertificateCallbackBadRequestFailure( + chromeos::attestation::AttestationCertificateProfile certificate_profile, + const AccountId& account_id, + const std::string& request_origin, + bool force_new_key, + const std::string& key_name, + const chromeos::attestation::AttestationFlow::CertificateCallback& + callback) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindRepeating( + callback, + chromeos::attestation::ATTESTATION_SERVER_BAD_REQUEST_FAILURE, "")); +} + +class TpmChallengeKeyTestBase : public BrowserWithTestWindowTest { + public: + enum class ProfileType { kUserProfile, kSigninProfile }; + + protected: + TpmChallengeKeyTestBase(ProfileType profile_type, + chromeos::attestation::AttestationKeyType key_type) + : profile_type_(profile_type), + fake_user_manager_(new chromeos::FakeChromeUserManager()), + user_manager_enabler_(base::WrapUnique(fake_user_manager_)), + key_type_(key_type) { + mock_async_method_caller_ = + new NiceMock<cryptohome::MockAsyncMethodCaller>(); + // Ownership of mock_async_method_caller_ is transferred to + // AsyncMethodCaller::InitializeForTesting. + cryptohome::AsyncMethodCaller::InitializeForTesting( + mock_async_method_caller_); + + challenge_key_impl_ = + std::make_unique<TpmChallengeKeyImpl>(&mock_attestation_flow_); + + // Set up the default behavior of mocks. + ON_CALL(*mock_async_method_caller_, TpmAttestationRegisterKey) + .WillByDefault(Invoke(RegisterKeyCallbackTrue)); + ON_CALL(*mock_async_method_caller_, TpmAttestationSignEnterpriseChallenge) + .WillByDefault(Invoke(SignChallengeCallbackTrue)); + ON_CALL(mock_attestation_flow_, GetCertificate) + .WillByDefault(Invoke(GetCertificateCallbackTrue)); + + GetInstallAttributes()->SetCloudManaged("google.com", "device_id"); + + GetCrosSettingsHelper()->ReplaceDeviceSettingsProviderWithStub(); + GetCrosSettingsHelper()->SetBoolean(chromeos::kDeviceAttestationEnabled, + true); + } + + ~TpmChallengeKeyTestBase() { cryptohome::AsyncMethodCaller::Shutdown(); } + + void SetUp() override { + BrowserWithTestWindowTest::SetUp(); + if (profile_type_ == ProfileType::kUserProfile) { + prefs_ = GetProfile()->GetPrefs(); + SetAuthenticatedUser(); + } + } + + void TearDown() override { BrowserWithTestWindowTest::TearDown(); } + + // This will be called by BrowserWithTestWindowTest::SetUp(); + TestingProfile* CreateProfile() override { + switch (profile_type_) { + case ProfileType::kUserProfile: + fake_user_manager_->AddUserWithAffiliation( + AccountId::FromUserEmail(kUserEmail), true); + return profile_manager()->CreateTestingProfile(kUserEmail); + + case ProfileType::kSigninProfile: + return profile_manager()->CreateTestingProfile(chrome::kInitialProfile); + } + NOTREACHED() << "Invalid profile type: " << static_cast<int>(profile_type_); + } + + // Derived classes can override this method to set the required authenticated + // user in the IdentityManager class. + virtual void SetAuthenticatedUser() { + auto* identity_manager = + IdentityManagerFactory::GetForProfile(GetProfile()); + signin::MakePrimaryAccountAvailable(identity_manager, kUserEmail); + } + + // Returns an error string for the given code. + static std::string GetCertificateError(int error_code) { + return base::StringPrintf(TpmChallengeKeyImpl::kGetCertificateFailedError, + error_code); + } + + void RunFunc(const std::string& challenge, + bool register_key, + const std::string& key_name_for_spkac, + TpmChallengeKeyResult* res) { + auto callback = [](base::OnceClosure done_closure, + TpmChallengeKeyResult* res, + const TpmChallengeKeyResult& tpm_result) { + *res = tpm_result; + std::move(done_closure).Run(); + }; + + base::RunLoop loop; + challenge_key_impl_->Run(key_type_, GetProfile(), + base::Bind(callback, loop.QuitClosure(), res), + challenge, register_key, key_name_for_spkac); + loop.Run(); + } + + chromeos::FakeCryptohomeClient cryptohome_client_; + cryptohome::MockAsyncMethodCaller* mock_async_method_caller_ = nullptr; + NiceMock<chromeos::attestation::MockAttestationFlow> mock_attestation_flow_; + ProfileType profile_type_; + // fake_user_manager_ is owned by user_manager_enabler_. + chromeos::FakeChromeUserManager* fake_user_manager_; + user_manager::ScopedUserManager user_manager_enabler_; + PrefService* prefs_ = nullptr; + std::unique_ptr<TpmChallengeKey> challenge_key_impl_; + chromeos::attestation::AttestationKeyType key_type_; +}; + +class TpmChallengeMachineKeyTest : public TpmChallengeKeyTestBase { + protected: + explicit TpmChallengeMachineKeyTest( + ProfileType profile_type = ProfileType::kUserProfile) + : TpmChallengeKeyTestBase(profile_type, + chromeos::attestation::KEY_DEVICE) {} +}; + +TEST_F(TpmChallengeMachineKeyTest, NonEnterpriseDevice) { + GetInstallAttributes()->SetConsumerOwned(); + + TpmChallengeKeyResult res; + RunFunc(kChallenge, /*register_key=*/false, "", &res); + + EXPECT_FALSE(res.is_success); + EXPECT_EQ("", res.data); + EXPECT_EQ(TpmChallengeKeyImpl::kNonEnterpriseDeviceError, res.error_message); +} + +TEST_F(TpmChallengeMachineKeyTest, DevicePolicyDisabled) { + GetCrosSettingsHelper()->SetBoolean(chromeos::kDeviceAttestationEnabled, + false); + + TpmChallengeKeyResult res; + RunFunc(kChallenge, /*register_key=*/false, "", &res); + + EXPECT_FALSE(res.is_success); + EXPECT_EQ("", res.data); + EXPECT_EQ(TpmChallengeKeyImpl::kDevicePolicyDisabledError, res.error_message); +} + +TEST_F(TpmChallengeMachineKeyTest, DoesKeyExistDbusFailed) { + cryptohome_client_.set_tpm_attestation_does_key_exist_should_succeed(false); + + TpmChallengeKeyResult res; + RunFunc(kChallenge, /*register_key=*/false, "", &res); + + EXPECT_FALSE(res.is_success); + EXPECT_EQ("", res.data); + EXPECT_EQ(GetCertificateError(kDBusError), res.error_message); +} + +TEST_F(TpmChallengeMachineKeyTest, GetCertificateFailed) { + EXPECT_CALL(mock_attestation_flow_, GetCertificate) + .WillRepeatedly(Invoke(GetCertificateCallbackUnspecifiedFailure)); + + TpmChallengeKeyResult res; + RunFunc(kChallenge, /*register_key=*/false, "", &res); + + EXPECT_FALSE(res.is_success); + EXPECT_EQ("", res.data); + EXPECT_EQ(GetCertificateError(kGetCertificateFailed), res.error_message); +} + +TEST_F(TpmChallengeMachineKeyTest, SignChallengeFailed) { + EXPECT_CALL(*mock_async_method_caller_, TpmAttestationSignEnterpriseChallenge) + .WillRepeatedly(Invoke(SignChallengeCallbackFalse)); + + TpmChallengeKeyResult res; + RunFunc(kChallenge, /*register_key=*/false, "", &res); + + EXPECT_FALSE(res.is_success); + EXPECT_EQ("", res.data); + EXPECT_EQ(TpmChallengeKeyImpl::kSignChallengeFailedError, res.error_message); +} + +TEST_F(TpmChallengeMachineKeyTest, KeyExists) { + cryptohome_client_.SetTpmAttestationDeviceCertificate("attest-ent-machine", + std::string()); + // GetCertificate must not be called if the key exists. + EXPECT_CALL(mock_attestation_flow_, GetCertificate).Times(0); + + TpmChallengeKeyResult res; + RunFunc(kChallenge, /*register_key=*/false, "", &res); + + EXPECT_TRUE(res.is_success); + EXPECT_EQ(kResponse, res.data); + EXPECT_EQ("", res.error_message); +} + +TEST_F(TpmChallengeMachineKeyTest, AttestationNotPrepared) { + cryptohome_client_.set_tpm_attestation_is_prepared(false); + + TpmChallengeKeyResult res; + RunFunc(kChallenge, /*register_key=*/false, "", &res); + + EXPECT_FALSE(res.is_success); + EXPECT_EQ("", res.data); + EXPECT_EQ(GetCertificateError(kResetRequired), res.error_message); +} + +// Test that we get proper error message in case we don't have TPM. +TEST_F(TpmChallengeMachineKeyTest, AttestationUnsupported) { + cryptohome_client_.set_tpm_attestation_is_prepared(false); + cryptohome_client_.set_tpm_is_enabled(false); + + TpmChallengeKeyResult res; + RunFunc(kChallenge, /*register_key=*/false, "", &res); + + EXPECT_FALSE(res.is_success); + EXPECT_EQ("", res.data); + EXPECT_EQ(GetCertificateError(kPrepareKeyAttestationUnsupported), + res.error_message); +} + +TEST_F(TpmChallengeMachineKeyTest, AttestationPreparedDbusFailed) { + cryptohome_client_.SetServiceIsAvailable(false); + + TpmChallengeKeyResult res; + RunFunc(kChallenge, /*register_key=*/false, "", &res); + + EXPECT_FALSE(res.is_success); + EXPECT_EQ("", res.data); + EXPECT_EQ(GetCertificateError(kDBusError), res.error_message); +} + +TEST_F(TpmChallengeMachineKeyTest, KeyRegistrationFailed) { + EXPECT_CALL(*mock_async_method_caller_, TpmAttestationRegisterKey) + .WillRepeatedly(Invoke(RegisterKeyCallbackFalse)); + + TpmChallengeKeyResult res; + RunFunc(kChallenge, /*register_key=*/true, kKeyNameForSpkac, &res); + + EXPECT_FALSE(res.is_success); + EXPECT_EQ("", res.data); + EXPECT_EQ(TpmChallengeKeyImpl::kKeyRegistrationFailedError, + res.error_message); +} + +TEST_F(TpmChallengeMachineKeyTest, KeyNotRegisteredSuccess) { + EXPECT_CALL(*mock_async_method_caller_, TpmAttestationRegisterKey).Times(0); + + TpmChallengeKeyResult res; + RunFunc(kChallenge, /*register_key=*/false, "", &res); + + EXPECT_TRUE(res.is_success); + EXPECT_EQ(kResponse, res.data); + EXPECT_EQ("", res.error_message); +} + +TEST_F(TpmChallengeMachineKeyTest, KeyRegisteredSuccess) { + // GetCertificate must be called exactly once. + EXPECT_CALL(mock_attestation_flow_, + GetCertificate( + chromeos::attestation::PROFILE_ENTERPRISE_MACHINE_CERTIFICATE, + _, _, _, _, _)) + .Times(1); + // TpmAttestationRegisterKey must be called exactly once. + EXPECT_CALL(*mock_async_method_caller_, + TpmAttestationRegisterKey(chromeos::attestation::KEY_DEVICE, + _ /* Unused by the API. */, + kKeyNameForSpkac, _)) + .Times(1); + // SignEnterpriseChallenge must be called exactly once. + EXPECT_CALL( + *mock_async_method_caller_, + TpmAttestationSignEnterpriseChallenge( + chromeos::attestation::KEY_DEVICE, _, "attest-ent-machine", + "google.com", "device_id", _, "challenge", kKeyNameForSpkac, _)) + .Times(1); + + TpmChallengeKeyResult res; + RunFunc(kChallenge, /*register_key=*/true, kKeyNameForSpkac, &res); + + EXPECT_TRUE(res.is_success); + EXPECT_EQ(kResponse, res.data); + EXPECT_EQ("", res.error_message); +} + +// Tests the API with all profiles types as determined by the test parameter. +class TpmChallengeMachineKeyAllProfilesTest + : public TpmChallengeMachineKeyTest, + public ::testing::WithParamInterface< + TpmChallengeKeyTestBase::ProfileType> { + protected: + TpmChallengeMachineKeyAllProfilesTest() + : TpmChallengeMachineKeyTest(GetParam()) {} +}; + +TEST_P(TpmChallengeMachineKeyAllProfilesTest, Success) { + // GetCertificate must be called exactly once. + EXPECT_CALL(mock_attestation_flow_, + GetCertificate( + chromeos::attestation::PROFILE_ENTERPRISE_MACHINE_CERTIFICATE, + _, _, _, _, _)) + .Times(1); + // SignEnterpriseChallenge must be called exactly once. + EXPECT_CALL(*mock_async_method_caller_, + TpmAttestationSignEnterpriseChallenge( + chromeos::attestation::KEY_DEVICE, _, "attest-ent-machine", + "google.com", "device_id", _, "challenge", _, _)) + .Times(1); + + TpmChallengeKeyResult res; + RunFunc(kChallenge, /*register_key=*/false, "", &res); + + EXPECT_TRUE(res.is_success); + EXPECT_EQ(kResponse, res.data); + EXPECT_EQ("", res.error_message); +} + +INSTANTIATE_TEST_SUITE_P( + AllProfiles, + TpmChallengeMachineKeyAllProfilesTest, + ::testing::Values(TpmChallengeKeyTestBase::ProfileType::kUserProfile, + TpmChallengeKeyTestBase::ProfileType::kSigninProfile)); + +class TpmChallengeUserKeyTest : public TpmChallengeKeyTestBase { + protected: + explicit TpmChallengeUserKeyTest( + ProfileType profile_type = ProfileType::kUserProfile) + : TpmChallengeKeyTestBase(profile_type, chromeos::attestation::KEY_USER) { + } + + void SetUp() override { + TpmChallengeKeyTestBase::SetUp(); + + if (profile_type_ == ProfileType::kUserProfile) { + GetProfile()->GetTestingPrefService()->SetManagedPref( + prefs::kAttestationEnabled, std::make_unique<base::Value>(true)); + } + } +}; + +TEST_F(TpmChallengeUserKeyTest, UserPolicyDisabled) { + GetProfile()->GetTestingPrefService()->SetManagedPref( + prefs::kAttestationEnabled, std::make_unique<base::Value>(false)); + + TpmChallengeKeyResult res; + RunFunc(kChallenge, /*register_key=*/true, "", &res); + + EXPECT_FALSE(res.is_success); + EXPECT_EQ("", res.data); + EXPECT_EQ(TpmChallengeKeyImpl::kUserPolicyDisabledError, res.error_message); +} + +TEST_F(TpmChallengeUserKeyTest, DevicePolicyDisabled) { + GetCrosSettingsHelper()->SetBoolean(chromeos::kDeviceAttestationEnabled, + false); + + TpmChallengeKeyResult res; + RunFunc(kChallenge, /*register_key=*/true, "", &res); + + EXPECT_FALSE(res.is_success); + EXPECT_EQ("", res.data); + EXPECT_EQ(TpmChallengeKeyImpl::kDevicePolicyDisabledError, res.error_message); +} + +TEST_F(TpmChallengeUserKeyTest, DoesKeyExistDbusFailed) { + cryptohome_client_.set_tpm_attestation_does_key_exist_should_succeed(false); + + TpmChallengeKeyResult res; + RunFunc(kChallenge, /*register_key=*/true, "", &res); + + EXPECT_FALSE(res.is_success); + EXPECT_EQ("", res.data); + EXPECT_EQ(GetCertificateError(kDBusError), res.error_message); +} + +TEST_F(TpmChallengeUserKeyTest, GetCertificateFailedWithUnspecifiedFailure) { + EXPECT_CALL(mock_attestation_flow_, GetCertificate) + .WillRepeatedly(Invoke(GetCertificateCallbackUnspecifiedFailure)); + + TpmChallengeKeyResult res; + RunFunc(kChallenge, /*register_key=*/true, "", &res); + + EXPECT_FALSE(res.is_success); + EXPECT_EQ("", res.data); + EXPECT_EQ(GetCertificateError(kGetCertificateFailed), res.error_message); +} + +TEST_F(TpmChallengeUserKeyTest, GetCertificateFailedWithBadRequestFailure) { + EXPECT_CALL(mock_attestation_flow_, GetCertificate) + .WillRepeatedly(Invoke(GetCertificateCallbackBadRequestFailure)); + + TpmChallengeKeyResult res; + RunFunc(kChallenge, /*register_key=*/true, "", &res); + + EXPECT_FALSE(res.is_success); + EXPECT_EQ("", res.data); + EXPECT_EQ(GetCertificateError(kGetCertificateFailed), res.error_message); +} + +TEST_F(TpmChallengeUserKeyTest, SignChallengeFailed) { + EXPECT_CALL(*mock_async_method_caller_, TpmAttestationSignEnterpriseChallenge) + .WillRepeatedly(Invoke(SignChallengeCallbackFalse)); + + TpmChallengeKeyResult res; + RunFunc(kChallenge, /*register_key=*/true, "", &res); + + EXPECT_FALSE(res.is_success); + EXPECT_EQ("", res.data); + EXPECT_EQ(TpmChallengeKeyImpl::kSignChallengeFailedError, res.error_message); +} + +TEST_F(TpmChallengeUserKeyTest, KeyRegistrationFailed) { + EXPECT_CALL(*mock_async_method_caller_, TpmAttestationRegisterKey) + .WillRepeatedly(Invoke(RegisterKeyCallbackFalse)); + + TpmChallengeKeyResult res; + RunFunc(kChallenge, /*register_key=*/true, "", &res); + + EXPECT_FALSE(res.is_success); + EXPECT_EQ("", res.data); + EXPECT_EQ(TpmChallengeKeyImpl::kKeyRegistrationFailedError, + res.error_message); +} + +TEST_F(TpmChallengeUserKeyTest, KeyExists) { + cryptohome_client_.SetTpmAttestationUserCertificate( + cryptohome::CreateAccountIdentifierFromAccountId( + AccountId::FromUserEmail(kUserEmail)), + "attest-ent-user", std::string()); + // GetCertificate must not be called if the key exists. + EXPECT_CALL(mock_attestation_flow_, GetCertificate).Times(0); + + TpmChallengeKeyResult res; + RunFunc(kChallenge, /*register_key=*/true, "", &res); + + EXPECT_TRUE(res.is_success); + EXPECT_EQ(kResponse, res.data); + EXPECT_EQ("", res.error_message); +} + +TEST_F(TpmChallengeUserKeyTest, KeyNotRegisteredSuccess) { + EXPECT_CALL(*mock_async_method_caller_, TpmAttestationRegisterKey).Times(0); + + TpmChallengeKeyResult res; + RunFunc(kChallenge, /*register_key=*/false, "", &res); + + EXPECT_TRUE(res.is_success); + EXPECT_EQ(kResponse, res.data); + EXPECT_EQ("", res.error_message); +} + +TEST_F(TpmChallengeUserKeyTest, PersonalDevice) { + GetInstallAttributes()->SetConsumerOwned(); + + TpmChallengeKeyResult res; + RunFunc(kChallenge, /*register_key=*/true, "", &res); + + // Currently personal devices are not supported. + EXPECT_FALSE(res.is_success); + EXPECT_EQ("", res.data); + EXPECT_EQ(GetCertificateError(kUserRejected), res.error_message); +} + +TEST_F(TpmChallengeUserKeyTest, Success) { + // GetCertificate must be called exactly once. + EXPECT_CALL( + mock_attestation_flow_, + GetCertificate(chromeos::attestation::PROFILE_ENTERPRISE_USER_CERTIFICATE, + _, _, _, _, _)) + .Times(1); + const AccountId account_id = AccountId::FromUserEmail(kUserEmail); + // SignEnterpriseChallenge must be called exactly once. + EXPECT_CALL(*mock_async_method_caller_, + TpmAttestationSignEnterpriseChallenge( + chromeos::attestation::KEY_USER, + cryptohome::Identification(account_id), "attest-ent-user", + cryptohome::Identification(account_id).id(), "device_id", _, + "challenge", _, _)) + .Times(1); + // RegisterKey must be called exactly once. + EXPECT_CALL(*mock_async_method_caller_, + TpmAttestationRegisterKey(chromeos::attestation::KEY_USER, + cryptohome::Identification(account_id), + "attest-ent-user", _)) + .Times(1); + + TpmChallengeKeyResult res; + RunFunc(kChallenge, /*register_key=*/true, "", &res); + + EXPECT_TRUE(res.is_success); + EXPECT_EQ(kResponse, res.data); + EXPECT_EQ("", res.error_message); +} + +TEST_F(TpmChallengeUserKeyTest, AttestationNotPrepared) { + cryptohome_client_.set_tpm_attestation_is_prepared(false); + + TpmChallengeKeyResult res; + RunFunc(kChallenge, /*register_key=*/true, "", &res); + + EXPECT_FALSE(res.is_success); + EXPECT_EQ("", res.data); + EXPECT_EQ(GetCertificateError(kResetRequired), res.error_message); +} + +TEST_F(TpmChallengeUserKeyTest, AttestationPreparedDbusFailed) { + cryptohome_client_.SetServiceIsAvailable(false); + + TpmChallengeKeyResult res; + RunFunc(kChallenge, /*register_key=*/true, "", &res); + + EXPECT_FALSE(res.is_success); + EXPECT_EQ("", res.data); + EXPECT_EQ(GetCertificateError(kDBusError), res.error_message); +} + +class TpmChallengeUserKeySigninProfileTest : public TpmChallengeUserKeyTest { + protected: + TpmChallengeUserKeySigninProfileTest() + : TpmChallengeUserKeyTest(ProfileType::kSigninProfile) {} +}; + +TEST_F(TpmChallengeUserKeySigninProfileTest, UserKeyNotAvailable) { + GetCrosSettingsHelper()->SetBoolean(chromeos::kDeviceAttestationEnabled, + false); + + TpmChallengeKeyResult res; + RunFunc(kChallenge, /*register_key=*/true, "", &res); + + EXPECT_FALSE(res.is_success); + EXPECT_EQ("", res.data); + EXPECT_EQ(TpmChallengeKeyImpl::kUserKeyNotAvailable, res.error_message); +} + +class TpmChallengeMachineKeyUnmanagedUserTest + : public TpmChallengeMachineKeyTest { + protected: + void SetAuthenticatedUser() override { + signin::MakePrimaryAccountAvailable( + IdentityManagerFactory::GetForProfile(GetProfile()), + account_id_.GetUserEmail()); + } + + TestingProfile* CreateProfile() override { + fake_user_manager_->AddUser(account_id_); + return profile_manager()->CreateTestingProfile(account_id_.GetUserEmail()); + } + + const std::string email = "test@chromium.com"; + const AccountId account_id_ = + AccountId::FromUserEmailGaiaId(email, + signin::GetTestGaiaIdForEmail(email)); +}; + +TEST_F(TpmChallengeMachineKeyUnmanagedUserTest, UserNotManaged) { + TpmChallengeKeyResult res; + RunFunc(kChallenge, /*register_key=*/false, "", &res); + + EXPECT_FALSE(res.is_success); + EXPECT_EQ("", res.data); + EXPECT_EQ(TpmChallengeKeyImpl::kUserNotManaged, res.error_message); +} + +class TpmChallengeUserKeyUnmanagedUserTest : public TpmChallengeUserKeyTest { + protected: + void SetAuthenticatedUser() override { + signin::MakePrimaryAccountAvailable( + IdentityManagerFactory::GetForProfile(GetProfile()), + account_id_.GetUserEmail()); + } + + TestingProfile* CreateProfile() override { + fake_user_manager_->AddUser(account_id_); + return profile_manager()->CreateTestingProfile(account_id_.GetUserEmail()); + } + + const std::string email = "test@chromium.com"; + const AccountId account_id_ = + AccountId::FromUserEmailGaiaId(email, + signin::GetTestGaiaIdForEmail(email)); +}; + +TEST_F(TpmChallengeUserKeyUnmanagedUserTest, UserNotManaged) { + TpmChallengeKeyResult res; + RunFunc(kChallenge, /*register_key=*/true, "", &res); + + EXPECT_FALSE(res.is_success); + EXPECT_EQ("", res.data); + EXPECT_EQ(TpmChallengeKeyImpl::kUserNotManaged, res.error_message); +} + +} // namespace +} // namespace attestation +} // namespace chromeos
diff --git a/chrome/browser/extensions/api/certificate_provider/certificate_provider_api.cc b/chrome/browser/extensions/api/certificate_provider/certificate_provider_api.cc index 533bdee..cdc5786 100644 --- a/chrome/browser/extensions/api/certificate_provider/certificate_provider_api.cc +++ b/chrome/browser/extensions/api/certificate_provider/certificate_provider_api.cc
@@ -77,7 +77,8 @@ } // namespace -const int api::certificate_provider::kMaxClosedDialogsPer10Mins = 2; +const int api::certificate_provider::kMaxClosedDialogsPerMinute = 10; +const int api::certificate_provider::kMaxClosedDialogsPer10Minutes = 30; CertificateProviderInternalReportCertificatesFunction:: ~CertificateProviderInternalReportCertificatesFunction() {} @@ -258,10 +259,17 @@ void CertificateProviderRequestPinFunction::GetQuotaLimitHeuristics( extensions::QuotaLimitHeuristics* heuristics) const { QuotaLimitHeuristic::Config short_limit_config = { - api::certificate_provider::kMaxClosedDialogsPer10Mins, - base::TimeDelta::FromMinutes(10)}; + api::certificate_provider::kMaxClosedDialogsPerMinute, + base::TimeDelta::FromMinutes(1)}; heuristics->push_back(std::make_unique<QuotaService::TimedLimit>( short_limit_config, new QuotaLimitHeuristic::SingletonBucketMapper(), + "MAX_PIN_DIALOGS_CLOSED_PER_MINUTE")); + + QuotaLimitHeuristic::Config long_limit_config = { + api::certificate_provider::kMaxClosedDialogsPer10Minutes, + base::TimeDelta::FromMinutes(10)}; + heuristics->push_back(std::make_unique<QuotaService::TimedLimit>( + long_limit_config, new QuotaLimitHeuristic::SingletonBucketMapper(), "MAX_PIN_DIALOGS_CLOSED_PER_10_MINUTES")); }
diff --git a/chrome/browser/extensions/api/certificate_provider/certificate_provider_api.h b/chrome/browser/extensions/api/certificate_provider/certificate_provider_api.h index b61e947..623b666 100644 --- a/chrome/browser/extensions/api/certificate_provider/certificate_provider_api.h +++ b/chrome/browser/extensions/api/certificate_provider/certificate_provider_api.h
@@ -19,9 +19,10 @@ namespace api { namespace certificate_provider { -// The maximum number of times per 10 minutes, extension is allowed to show PIN -// dialog again after user closed the previous one. -extern const int kMaxClosedDialogsPer10Mins; +// The maximum number of times in the given interval the extension is allowed to +// show the PIN dialog again after user closed the previous one. +extern const int kMaxClosedDialogsPerMinute; +extern const int kMaxClosedDialogsPer10Minutes; struct CertificateInfo; }
diff --git a/chrome/browser/extensions/api/certificate_provider/certificate_provider_apitest.cc b/chrome/browser/extensions/api/certificate_provider/certificate_provider_apitest.cc index 1611eb4a..6a95de1 100644 --- a/chrome/browser/extensions/api/certificate_provider/certificate_provider_apitest.cc +++ b/chrome/browser/extensions/api/certificate_provider/certificate_provider_apitest.cc
@@ -382,14 +382,14 @@ EXPECT_FALSE(GetActivePinDialogView()); } -// User closes the dialog kMaxClosedDialogsPer10Mins times, and the extension +// User closes the dialog kMaxClosedDialogsPerMinute times, and the extension // should be blocked from showing it again. IN_PROC_BROWSER_TEST_F(CertificateProviderRequestPinTest, ShowPinDialogClose) { AddFakeSignRequest(); NavigateTo("basic.html"); for (int i = 0; - i < extensions::api::certificate_provider::kMaxClosedDialogsPer10Mins; + i < extensions::api::certificate_provider::kMaxClosedDialogsPerMinute; i++) { ExtensionTestMessageListener listener("User closed the dialog", false); GetActivePinDialogWindow()->Close(); @@ -401,7 +401,7 @@ ASSERT_TRUE(close_listener.WaitUntilSatisfied()); close_listener.Reply("GetLastError"); ExtensionTestMessageListener last_error_listener( - "This request exceeds the MAX_PIN_DIALOGS_CLOSED_PER_10_MINUTES quota.", + "This request exceeds the MAX_PIN_DIALOGS_CLOSED_PER_MINUTE quota.", false); ASSERT_TRUE(last_error_listener.WaitUntilSatisfied()); EXPECT_FALSE(GetActivePinDialogView()); @@ -462,7 +462,7 @@ EXPECT_FALSE(GetActivePinDialogView()); } -// Extension closes the dialog kMaxClosedDialogsPer10Mins times after the user +// Extension closes the dialog kMaxClosedDialogsPerMinute times after the user // inputs some value, and it should be blocked from showing it again. IN_PROC_BROWSER_TEST_F(CertificateProviderRequestPinTest, RepeatedProgrammaticCloseAfterInput) { @@ -470,7 +470,7 @@ for (int i = 0; i < - extensions::api::certificate_provider::kMaxClosedDialogsPer10Mins + 1; + extensions::api::certificate_provider::kMaxClosedDialogsPerMinute + 1; i++) { AddFakeSignRequest(); EXPECT_TRUE(SendCommandAndWaitForMessage( @@ -487,8 +487,8 @@ "Request", base::StringPrintf( "request%d:error:This request exceeds the " - "MAX_PIN_DIALOGS_CLOSED_PER_10_MINUTES quota.", - extensions::api::certificate_provider::kMaxClosedDialogsPer10Mins + + "MAX_PIN_DIALOGS_CLOSED_PER_MINUTE quota.", + extensions::api::certificate_provider::kMaxClosedDialogsPerMinute + 2))); EXPECT_FALSE(GetActivePinDialogView()); } @@ -505,7 +505,7 @@ EXPECT_FALSE(GetActivePinDialogView()); } -// Extension closes the dialog kMaxClosedDialogsPer10Mins times before the user +// Extension closes the dialog kMaxClosedDialogsPerMinute times before the user // inputs anything, and it should be blocked from showing it again. IN_PROC_BROWSER_TEST_F(CertificateProviderRequestPinTest, RepeatedProgrammaticCloseBeforeInput) { @@ -513,7 +513,7 @@ for (int i = 0; i < - extensions::api::certificate_provider::kMaxClosedDialogsPer10Mins + 1; + extensions::api::certificate_provider::kMaxClosedDialogsPerMinute + 1; i++) { AddFakeSignRequest(); EXPECT_TRUE(SendCommand("Request")); @@ -527,8 +527,8 @@ "Request", base::StringPrintf( "request%d:error:This request exceeds the " - "MAX_PIN_DIALOGS_CLOSED_PER_10_MINUTES quota.", - extensions::api::certificate_provider::kMaxClosedDialogsPer10Mins + + "MAX_PIN_DIALOGS_CLOSED_PER_MINUTE quota.", + extensions::api::certificate_provider::kMaxClosedDialogsPerMinute + 2))); EXPECT_FALSE(GetActivePinDialogView()); }
diff --git a/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.cc b/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.cc index 2932c70..dfcc812 100644 --- a/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.cc +++ b/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.cc
@@ -45,8 +45,7 @@ } // namespace EnterprisePlatformKeysInternalGenerateKeyFunction:: - ~EnterprisePlatformKeysInternalGenerateKeyFunction() { -} + ~EnterprisePlatformKeysInternalGenerateKeyFunction() = default; ExtensionFunction::ResponseAction EnterprisePlatformKeysInternalGenerateKeyFunction::Run() { @@ -64,9 +63,7 @@ DCHECK(service); service->GenerateRSAKey( - platform_keys_token_id, - params->modulus_length, - extension_id(), + platform_keys_token_id, params->modulus_length, extension_id(), base::Bind( &EnterprisePlatformKeysInternalGenerateKeyFunction::OnGeneratedKey, this)); @@ -86,8 +83,7 @@ } EnterprisePlatformKeysGetCertificatesFunction:: - ~EnterprisePlatformKeysGetCertificatesFunction() { -} + ~EnterprisePlatformKeysGetCertificatesFunction() {} ExtensionFunction::ResponseAction EnterprisePlatformKeysGetCertificatesFunction::Run() { @@ -118,8 +114,7 @@ std::unique_ptr<base::ListValue> client_certs(new base::ListValue()); for (net::CertificateList::const_iterator it = certs->begin(); - it != certs->end(); - ++it) { + it != certs->end(); ++it) { base::StringPiece cert_der = net::x509_util::CryptoBufferAsStringPiece((*it)->cert_buffer()); client_certs->Append(std::make_unique<base::Value>( @@ -132,8 +127,7 @@ } EnterprisePlatformKeysImportCertificateFunction:: - ~EnterprisePlatformKeysImportCertificateFunction() { -} + ~EnterprisePlatformKeysImportCertificateFunction() {} ExtensionFunction::ResponseAction EnterprisePlatformKeysImportCertificateFunction::Run() { @@ -157,8 +151,7 @@ return RespondNow(Error(kEnterprisePlatformErrorInvalidX509Cert)); chromeos::platform_keys::ImportCertificate( - platform_keys_token_id, - cert_x509, + platform_keys_token_id, cert_x509, base::Bind(&EnterprisePlatformKeysImportCertificateFunction:: OnImportedCertificate, this), @@ -176,8 +169,7 @@ } EnterprisePlatformKeysRemoveCertificateFunction:: - ~EnterprisePlatformKeysRemoveCertificateFunction() { -} + ~EnterprisePlatformKeysRemoveCertificateFunction() {} ExtensionFunction::ResponseAction EnterprisePlatformKeysRemoveCertificateFunction::Run() { @@ -201,8 +193,7 @@ return RespondNow(Error(kEnterprisePlatformErrorInvalidX509Cert)); chromeos::platform_keys::RemoveCertificate( - platform_keys_token_id, - cert_x509, + platform_keys_token_id, cert_x509, base::Bind(&EnterprisePlatformKeysRemoveCertificateFunction:: OnRemovedCertificate, this), @@ -220,8 +211,7 @@ } EnterprisePlatformKeysInternalGetTokensFunction:: - ~EnterprisePlatformKeysInternalGetTokensFunction() { -} + ~EnterprisePlatformKeysInternalGetTokensFunction() {} ExtensionFunction::ResponseAction EnterprisePlatformKeysInternalGetTokensFunction::Run() { @@ -246,8 +236,7 @@ std::vector<std::string> token_ids; for (std::vector<std::string>::const_iterator it = platform_keys_token_ids->begin(); - it != platform_keys_token_ids->end(); - ++it) { + it != platform_keys_token_ids->end(); ++it) { std::string token_id = platform_keys::PlatformKeysTokenIdToApiId(*it); if (token_id.empty()) { Respond(Error(kEnterprisePlatformErrorInternal)); @@ -260,13 +249,7 @@ } EnterprisePlatformKeysChallengeMachineKeyFunction:: - EnterprisePlatformKeysChallengeMachineKeyFunction() - : default_impl_(new EPKPChallengeMachineKey), impl_(default_impl_.get()) {} - -EnterprisePlatformKeysChallengeMachineKeyFunction:: - EnterprisePlatformKeysChallengeMachineKeyFunction( - EPKPChallengeMachineKey* impl_for_testing) - : impl_(impl_for_testing) {} + EnterprisePlatformKeysChallengeMachineKeyFunction() = default; EnterprisePlatformKeysChallengeMachineKeyFunction:: ~EnterprisePlatformKeysChallengeMachineKeyFunction() = default; @@ -276,39 +259,32 @@ std::unique_ptr<api_epk::ChallengeMachineKey::Params> params( api_epk::ChallengeMachineKey::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params); - ChallengeKeyCallback callback = base::Bind( + chromeos::attestation::TpmChallengeKeyCallback callback = base::BindOnce( &EnterprisePlatformKeysChallengeMachineKeyFunction::OnChallengedKey, this); // base::Unretained is safe on impl_ since its life-cycle matches |this| and // |callback| holds a reference to |this|. - base::Closure task = - base::Bind(&EPKPChallengeMachineKey::Run, base::Unretained(impl_), - scoped_refptr<ExtensionFunction>(this), callback, - StringFromVector(params->challenge), - params->register_key ? *params->register_key : false); - base::PostTask(FROM_HERE, {content::BrowserThread::UI}, task); + base::OnceClosure task = base::BindOnce( + &EPKPChallengeKey::Run, base::Unretained(&impl_), + chromeos::attestation::KEY_DEVICE, scoped_refptr<ExtensionFunction>(this), + std::move(callback), StringFromVector(params->challenge), + params->register_key ? *params->register_key : false); + base::PostTask(FROM_HERE, {content::BrowserThread::UI}, std::move(task)); return RespondLater(); } void EnterprisePlatformKeysChallengeMachineKeyFunction::OnChallengedKey( - bool success, - const std::string& data) { - if (success) { - Respond(ArgumentList( - api_epk::ChallengeMachineKey::Results::Create(VectorFromString(data)))); + const chromeos::attestation::TpmChallengeKeyResult& result) { + if (result.is_success) { + Respond(ArgumentList(api_epk::ChallengeMachineKey::Results::Create( + VectorFromString(result.data)))); } else { - Respond(Error(data)); + Respond(Error(result.error_message)); } } EnterprisePlatformKeysChallengeUserKeyFunction:: - EnterprisePlatformKeysChallengeUserKeyFunction() - : default_impl_(new EPKPChallengeUserKey), impl_(default_impl_.get()) {} - -EnterprisePlatformKeysChallengeUserKeyFunction:: - EnterprisePlatformKeysChallengeUserKeyFunction( - EPKPChallengeUserKey* impl_for_testing) - : impl_(impl_for_testing) {} + EnterprisePlatformKeysChallengeUserKeyFunction() = default; EnterprisePlatformKeysChallengeUserKeyFunction:: ~EnterprisePlatformKeysChallengeUserKeyFunction() = default; @@ -318,26 +294,26 @@ std::unique_ptr<api_epk::ChallengeUserKey::Params> params( api_epk::ChallengeUserKey::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params); - ChallengeKeyCallback callback = base::Bind( + chromeos::attestation::TpmChallengeKeyCallback callback = base::Bind( &EnterprisePlatformKeysChallengeUserKeyFunction::OnChallengedKey, this); // base::Unretained is safe on impl_ since its life-cycle matches |this| and // |callback| holds a reference to |this|. - base::Closure task = - base::Bind(&EPKPChallengeUserKey::Run, base::Unretained(impl_), - scoped_refptr<ExtensionFunction>(this), callback, - StringFromVector(params->challenge), params->register_key); - base::PostTask(FROM_HERE, {content::BrowserThread::UI}, task); + base::OnceClosure task = base::BindOnce( + &EPKPChallengeKey::Run, base::Unretained(&impl_), + chromeos::attestation::KEY_USER, scoped_refptr<ExtensionFunction>(this), + std::move(callback), StringFromVector(params->challenge), + params->register_key); + base::PostTask(FROM_HERE, {content::BrowserThread::UI}, std::move(task)); return RespondLater(); } void EnterprisePlatformKeysChallengeUserKeyFunction::OnChallengedKey( - bool success, - const std::string& data) { - if (success) { - Respond(ArgumentList( - api_epk::ChallengeUserKey::Results::Create(VectorFromString(data)))); + const chromeos::attestation::TpmChallengeKeyResult& result) { + if (result.is_success) { + Respond(ArgumentList(api_epk::ChallengeUserKey::Results::Create( + VectorFromString(result.data)))); } else { - Respond(Error(data)); + Respond(Error(result.error_message)); } }
diff --git a/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.h b/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.h index 9ec384a2..598990b1 100644 --- a/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.h +++ b/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.h
@@ -15,8 +15,8 @@ namespace net { class X509Certificate; -typedef std::vector<scoped_refptr<X509Certificate> > CertificateList; -} +using CertificateList = std::vector<scoped_refptr<X509Certificate>>; +} // namespace net namespace extensions { @@ -96,20 +96,16 @@ : public ExtensionFunction { public: EnterprisePlatformKeysChallengeMachineKeyFunction(); - explicit EnterprisePlatformKeysChallengeMachineKeyFunction( - EPKPChallengeMachineKey* impl_for_testing); private: ~EnterprisePlatformKeysChallengeMachineKeyFunction() override; ResponseAction Run() override; - // Called when the challenge operation is complete. If the operation succeeded - // |success| will be true and |data| will contain the challenge response data. - // Otherwise |success| will be false and |data| is an error message. - void OnChallengedKey(bool success, const std::string& data); + // Called when the challenge operation is complete. + void OnChallengedKey( + const chromeos::attestation::TpmChallengeKeyResult& result); - std::unique_ptr<EPKPChallengeMachineKey> default_impl_; - EPKPChallengeMachineKey* impl_; + EPKPChallengeKey impl_; DECLARE_EXTENSION_FUNCTION("enterprise.platformKeys.challengeMachineKey", ENTERPRISE_PLATFORMKEYS_CHALLENGEMACHINEKEY) @@ -119,20 +115,16 @@ : public ExtensionFunction { public: EnterprisePlatformKeysChallengeUserKeyFunction(); - explicit EnterprisePlatformKeysChallengeUserKeyFunction( - EPKPChallengeUserKey* impl_for_testing); private: ~EnterprisePlatformKeysChallengeUserKeyFunction() override; ResponseAction Run() override; - // Called when the challenge operation is complete. If the operation succeeded - // |success| will be true and |data| will contain the challenge response data. - // Otherwise |success| will be false and |data| is an error message. - void OnChallengedKey(bool success, const std::string& data); + // Called when the challenge operation is complete. + void OnChallengedKey( + const chromeos::attestation::TpmChallengeKeyResult& result); - std::unique_ptr<EPKPChallengeUserKey> default_impl_; - EPKPChallengeUserKey* impl_; + EPKPChallengeKey impl_; DECLARE_EXTENSION_FUNCTION("enterprise.platformKeys.challengeUserKey", ENTERPRISE_PLATFORMKEYS_CHALLENGEUSERKEY)
diff --git a/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_unittest.cc b/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_unittest.cc index bffca7db..386feaea 100644 --- a/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_unittest.cc +++ b/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_unittest.cc
@@ -4,170 +4,71 @@ #include "chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.h" -#include <string> +#include <utility> -#include "base/bind.h" -#include "base/location.h" -#include "base/memory/ptr_util.h" -#include "base/strings/stringprintf.h" -#include "base/threading/thread_task_runner_handle.h" #include "base/values.h" +#include "chrome/browser/chromeos/attestation/mock_tpm_challenge_key.h" #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h" -#include "chrome/browser/chromeos/settings/cros_settings.h" -#include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h" #include "chrome/browser/extensions/extension_function_test_utils.h" #include "chrome/browser/signin/identity_manager_factory.h" -#include "chrome/browser/ui/browser.h" #include "chrome/common/pref_names.h" #include "chrome/test/base/browser_with_test_window_test.h" -#include "chrome/test/base/testing_browser_process.h" #include "chrome/test/base/testing_profile_manager.h" -#include "chromeos/attestation/mock_attestation_flow.h" -#include "chromeos/cryptohome/async_method_caller.h" -#include "chromeos/cryptohome/cryptohome_parameters.h" -#include "chromeos/cryptohome/mock_async_method_caller.h" -#include "chromeos/dbus/constants/attestation_constants.h" -#include "chromeos/dbus/cryptohome/fake_cryptohome_client.h" -#include "chromeos/dbus/dbus_method_call_status.h" -#include "chromeos/tpm/stub_install_attributes.h" -#include "components/account_id/account_id.h" -#include "components/policy/core/common/cloud/cloud_policy_constants.h" -#include "components/prefs/pref_service.h" +#include "components/signin/public/identity_manager/identity_manager.h" #include "components/signin/public/identity_manager/identity_test_utils.h" #include "components/user_manager/scoped_user_manager.h" #include "extensions/common/extension_builder.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/cros_system_api/dbus/service_constants.h" -using testing::_; using testing::Invoke; using testing::NiceMock; -using testing::Return; -using testing::WithArgs; namespace utils = extension_function_test_utils; namespace extensions { namespace { -// Certificate errors as reported to the calling extension. -const int kDBusError = 1; -const int kUserRejected = 2; -const int kGetCertificateFailed = 3; -const int kResetRequired = 4; - const char kUserEmail[] = "test@google.com"; -void RegisterKeyCallbackTrue( +void FakeRunCheckNotRegister( chromeos::attestation::AttestationKeyType key_type, - const cryptohome::Identification& user_id, - const std::string& key_name, - const cryptohome::AsyncMethodCaller::Callback& callback) { - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::BindOnce(callback, true, cryptohome::MOUNT_ERROR_NONE)); -} - -void RegisterKeyCallbackFalse( - chromeos::attestation::AttestationKeyType key_type, - const cryptohome::Identification& user_id, - const std::string& key_name, - const cryptohome::AsyncMethodCaller::Callback& callback) { - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::BindOnce(callback, false, cryptohome::MOUNT_ERROR_NONE)); -} - -void SignChallengeCallbackTrue( - chromeos::attestation::AttestationKeyType key_type, - const cryptohome::Identification& user_id, - const std::string& key_name, - const std::string& domain, - const std::string& device_id, - chromeos::attestation::AttestationChallengeOptions options, + Profile* profile, + chromeos::attestation::TpmChallengeKeyCallback callback, const std::string& challenge, - const std::string& key_name_for_spkac, - const cryptohome::AsyncMethodCaller::DataCallback& callback) { - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::BindOnce(callback, true, "response")); -} - -void SignChallengeCallbackFalse( - chromeos::attestation::AttestationKeyType key_type, - const cryptohome::Identification& user_id, - const std::string& key_name, - const std::string& domain, - const std::string& device_id, - chromeos::attestation::AttestationChallengeOptions options, - const std::string& challenge, - const std::string& key_name_for_spkac, - const cryptohome::AsyncMethodCaller::DataCallback& callback) { - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::BindOnce(callback, false, "")); -} - -void GetCertificateCallbackTrue( - chromeos::attestation::AttestationCertificateProfile certificate_profile, - const AccountId& account_id, - const std::string& request_origin, - bool force_new_key, - const std::string& key_name, - const chromeos::attestation::AttestationFlow::CertificateCallback& - callback) { - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindOnce(callback, chromeos::attestation::ATTESTATION_SUCCESS, - "certificate")); -} - -void GetCertificateCallbackFalse( - chromeos::attestation::AttestationCertificateProfile certificate_profile, - const AccountId& account_id, - const std::string& request_origin, - bool force_new_key, - const std::string& key_name, - const chromeos::attestation::AttestationFlow::CertificateCallback& - callback) { - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindOnce(callback, - chromeos::attestation::ATTESTATION_UNSPECIFIED_FAILURE, - "")); + bool register_key, + const std::string& key_name_for_spkac) { + EXPECT_FALSE(register_key); + std::move(callback).Run( + chromeos::attestation::TpmChallengeKeyResult::MakeResult("response")); } class EPKChallengeKeyTestBase : public BrowserWithTestWindowTest { protected: EPKChallengeKeyTestBase() - : settings_helper_(false), - extension_(ExtensionBuilder("Test").Build()), + : extension_(ExtensionBuilder("Test").Build()), fake_user_manager_(new chromeos::FakeChromeUserManager), user_manager_enabler_(base::WrapUnique(fake_user_manager_)) { - // Set up the default behavior of mocks. - ON_CALL(mock_async_method_caller_, TpmAttestationRegisterKey(_, _, _, _)) - .WillByDefault(Invoke(RegisterKeyCallbackTrue)); - ON_CALL(mock_async_method_caller_, - TpmAttestationSignEnterpriseChallenge(_, _, _, _, _, _, _, _, _)) - .WillByDefault(Invoke(SignChallengeCallbackTrue)); - ON_CALL(mock_attestation_flow_, GetCertificate(_, _, _, _, _, _)) - .WillByDefault(Invoke(GetCertificateCallbackTrue)); - stub_install_attributes_.SetCloudManaged("google.com", "device_id"); - - settings_helper_.ReplaceDeviceSettingsProviderWithStub(); - settings_helper_.SetBoolean(chromeos::kDeviceAttestationEnabled, true); } void SetUp() override { BrowserWithTestWindowTest::SetUp(); - - // Set the user preferences. prefs_ = browser()->profile()->GetPrefs(); - base::ListValue whitelist; - whitelist.AppendString(extension_->id()); - prefs_->Set(prefs::kAttestationExtensionWhitelist, whitelist); - SetAuthenticatedUser(); } + void SetMockTpmChallenger() { + auto mock_tpm_challenge_key = std::make_unique< + NiceMock<chromeos::attestation::MockTpmChallengeKey>>(); + // Will be used with EXPECT_CALL. + mock_tpm_challenge_key_ = mock_tpm_challenge_key.get(); + mock_tpm_challenge_key->EnableFake(); + // transfer ownership inside factory + chromeos::attestation::TpmChallengeKeyFactory::SetForTesting( + std::move(mock_tpm_challenge_key)); + } + // This will be called by BrowserWithTestWindowTest::SetUp(); TestingProfile* CreateProfile() override { fake_user_manager_->AddUserWithAffiliation( @@ -205,8 +106,8 @@ function->set_has_callback(true); utils::RunFunction(function, std::move(args), browser, extensions::api_test_utils::NONE); - EXPECT_TRUE(function->GetError().empty()) << "Unexpected error: " - << function->GetError(); + EXPECT_TRUE(function->GetError().empty()) + << "Unexpected error: " << function->GetError(); const base::Value* single_result = NULL; if (function->GetResultList() != NULL && function->GetResultList()->Get(0, &single_result)) { @@ -215,33 +116,27 @@ return NULL; } - chromeos::FakeCryptohomeClient cryptohome_client_; - NiceMock<cryptohome::MockAsyncMethodCaller> mock_async_method_caller_; - NiceMock<chromeos::attestation::MockAttestationFlow> mock_attestation_flow_; - chromeos::ScopedCrosSettingsTestHelper settings_helper_; scoped_refptr<const extensions::Extension> extension_; chromeos::StubInstallAttributes stub_install_attributes_; // fake_user_manager_ is owned by user_manager_enabler_. - chromeos::FakeChromeUserManager* fake_user_manager_; + chromeos::FakeChromeUserManager* fake_user_manager_ = nullptr; user_manager::ScopedUserManager user_manager_enabler_; PrefService* prefs_ = nullptr; + chromeos::attestation::MockTpmChallengeKey* mock_tpm_challenge_key_ = nullptr; }; class EPKChallengeMachineKeyTest : public EPKChallengeKeyTestBase { protected: EPKChallengeMachineKeyTest() - : impl_(&cryptohome_client_, - &mock_async_method_caller_, - &mock_attestation_flow_, - &stub_install_attributes_), - func_(new EnterprisePlatformKeysChallengeMachineKeyFunction(&impl_)) { + : func_(new EnterprisePlatformKeysChallengeMachineKeyFunction()) { func_->set_extension(extension_.get()); } // Returns an error string for the given code. std::string GetCertificateError(int error_code) { return base::StringPrintf( - EPKPChallengeMachineKey::kGetCertificateFailedError, error_code); + chromeos::attestation::TpmChallengeKeyImpl::kGetCertificateFailedError, + error_code); } std::unique_ptr<base::ListValue> CreateArgs() { @@ -266,170 +161,51 @@ return args; } - EPKPChallengeMachineKey impl_; scoped_refptr<EnterprisePlatformKeysChallengeMachineKeyFunction> func_; base::ListValue args_; }; -TEST_F(EPKChallengeMachineKeyTest, NonEnterpriseDevice) { - stub_install_attributes_.SetConsumerOwned(); - - EXPECT_EQ(EPKPChallengeMachineKey::kNonEnterpriseDeviceError, - RunFunctionAndReturnError(func_.get(), CreateArgs(), browser())); -} - TEST_F(EPKChallengeMachineKeyTest, ExtensionNotWhitelisted) { base::ListValue empty_whitelist; prefs_->Set(prefs::kAttestationExtensionWhitelist, empty_whitelist); - EXPECT_EQ(EPKPChallengeKeyBase::kExtensionNotWhitelistedError, + EXPECT_EQ(EPKPChallengeKey::kExtensionNotWhitelistedError, RunFunctionAndReturnError(func_.get(), CreateArgs(), browser())); } -TEST_F(EPKChallengeMachineKeyTest, DevicePolicyDisabled) { - settings_helper_.SetBoolean(chromeos::kDeviceAttestationEnabled, false); - - EXPECT_EQ(EPKPChallengeKeyBase::kDevicePolicyDisabledError, - RunFunctionAndReturnError(func_.get(), CreateArgs(), browser())); -} - -TEST_F(EPKChallengeMachineKeyTest, DoesKeyExistDbusFailed) { - cryptohome_client_.set_tpm_attestation_does_key_exist_should_succeed(false); - - EXPECT_EQ(GetCertificateError(kDBusError), - RunFunctionAndReturnError(func_.get(), CreateArgs(), browser())); -} - -TEST_F(EPKChallengeMachineKeyTest, GetCertificateFailed) { - EXPECT_CALL(mock_attestation_flow_, GetCertificate(_, _, _, _, _, _)) - .WillRepeatedly(Invoke(GetCertificateCallbackFalse)); - - EXPECT_EQ(GetCertificateError(kGetCertificateFailed), - RunFunctionAndReturnError(func_.get(), CreateArgs(), browser())); -} - -TEST_F(EPKChallengeMachineKeyTest, SignChallengeFailed) { - EXPECT_CALL(mock_async_method_caller_, - TpmAttestationSignEnterpriseChallenge(_, _, _, _, _, _, _, _, _)) - .WillRepeatedly(Invoke(SignChallengeCallbackFalse)); - - EXPECT_EQ(EPKPChallengeKeyBase::kSignChallengeFailedError, - RunFunctionAndReturnError(func_.get(), CreateArgs(), browser())); -} - -TEST_F(EPKChallengeMachineKeyTest, KeyRegistrationFailed) { - EXPECT_CALL(mock_async_method_caller_, TpmAttestationRegisterKey(_, _, _, _)) - .WillRepeatedly(Invoke(RegisterKeyCallbackFalse)); - - EXPECT_EQ( - EPKPChallengeMachineKey::kKeyRegistrationFailedError, - RunFunctionAndReturnError(func_.get(), CreateArgsRegister(), browser())); -} - -TEST_F(EPKChallengeMachineKeyTest, KeyExists) { - cryptohome_client_.SetTpmAttestationDeviceCertificate("attest-ent-machine", - std::string()); - - // GetCertificate must not be called if the key exists. - EXPECT_CALL(mock_attestation_flow_, GetCertificate(_, _, _, _, _, _)) - .Times(0); - - EXPECT_TRUE(utils::RunFunction(func_.get(), CreateArgs(), browser(), - extensions::api_test_utils::NONE)); -} - -TEST_F(EPKChallengeMachineKeyTest, KeyNotRegisteredByDefault) { - EXPECT_CALL(mock_async_method_caller_, TpmAttestationRegisterKey(_, _, _, _)) - .Times(0); - - EXPECT_TRUE(utils::RunFunction(func_.get(), CreateArgs(), browser(), - extensions::api_test_utils::NONE)); -} - -TEST_F(EPKChallengeMachineKeyTest, KeyNotRegistered) { - EXPECT_CALL(mock_async_method_caller_, TpmAttestationRegisterKey(_, _, _, _)) - .Times(0); - - EXPECT_TRUE(utils::RunFunction(func_.get(), CreateArgsNoRegister(), browser(), - extensions::api_test_utils::NONE)); -} - TEST_F(EPKChallengeMachineKeyTest, Success) { - // GetCertificate must be called exactly once. - EXPECT_CALL(mock_attestation_flow_, - GetCertificate( - chromeos::attestation::PROFILE_ENTERPRISE_MACHINE_CERTIFICATE, - _, _, _, _, _)) - .Times(1); - // SignEnterpriseChallenge must be called exactly once. - EXPECT_CALL(mock_async_method_caller_, - TpmAttestationSignEnterpriseChallenge( - chromeos::attestation::KEY_DEVICE, - cryptohome::Identification(), "attest-ent-machine", - "google.com", "device_id", _, "challenge", _, _)) - .Times(1); + SetMockTpmChallenger(); + + base::Value whitelist(base::Value::Type::LIST); + whitelist.Append(extension_->id()); + prefs_->Set(prefs::kAttestationExtensionWhitelist, whitelist); std::unique_ptr<base::Value> value( RunFunctionAndReturnSingleResult(func_.get(), CreateArgs(), browser())); ASSERT_TRUE(value->is_blob()); - EXPECT_EQ("response", - std::string(value->GetBlob().begin(), value->GetBlob().end())); + std::string response(value->GetBlob().begin(), value->GetBlob().end()); + EXPECT_EQ("response", response); } -TEST_F(EPKChallengeMachineKeyTest, KeyRegisteredSuccess) { - std::string key_name_for_spkac = "attest-ent-machine-" + extension_->id(); - // GetCertificate must be called exactly once. - EXPECT_CALL(mock_attestation_flow_, - GetCertificate( - chromeos::attestation::PROFILE_ENTERPRISE_MACHINE_CERTIFICATE, - _, _, _, _, _)) - .Times(1); - // TpmAttestationRegisterKey must be called exactly once. - EXPECT_CALL(mock_async_method_caller_, - TpmAttestationRegisterKey(chromeos::attestation::KEY_DEVICE, - _ /* Unused by the API. */, - key_name_for_spkac, _)) - .Times(1); - // SignEnterpriseChallenge must be called exactly once. - EXPECT_CALL( - mock_async_method_caller_, - TpmAttestationSignEnterpriseChallenge( - chromeos::attestation::KEY_DEVICE, cryptohome::Identification(), - "attest-ent-machine", "google.com", "device_id", _, "challenge", - key_name_for_spkac, _)) - .Times(1); +TEST_F(EPKChallengeMachineKeyTest, KeyNotRegisteredByDefault) { + SetMockTpmChallenger(); - std::unique_ptr<base::Value> value(RunFunctionAndReturnSingleResult( - func_.get(), CreateArgsRegister(), browser())); + base::ListValue whitelist; + whitelist.AppendString(extension_->id()); + prefs_->Set(prefs::kAttestationExtensionWhitelist, whitelist); - ASSERT_TRUE(value->is_blob()); - EXPECT_EQ("response", - std::string(value->GetBlob().begin(), value->GetBlob().end())); -} + EXPECT_CALL(*mock_tpm_challenge_key_, Run) + .WillOnce(Invoke(FakeRunCheckNotRegister)); -TEST_F(EPKChallengeMachineKeyTest, AttestationNotPrepared) { - cryptohome_client_.set_tpm_attestation_is_prepared(false); - - EXPECT_EQ(GetCertificateError(kResetRequired), - RunFunctionAndReturnError(func_.get(), CreateArgs(), browser())); -} - -TEST_F(EPKChallengeMachineKeyTest, AttestationPreparedDbusFailed) { - cryptohome_client_.SetServiceIsAvailable(false); - - EXPECT_EQ(GetCertificateError(kDBusError), - RunFunctionAndReturnError(func_.get(), CreateArgs(), browser())); + EXPECT_TRUE(utils::RunFunction(func_.get(), CreateArgs(), browser(), + extensions::api_test_utils::NONE)); } class EPKChallengeUserKeyTest : public EPKChallengeKeyTestBase { protected: EPKChallengeUserKeyTest() - : impl_(&cryptohome_client_, - &mock_async_method_caller_, - &mock_attestation_flow_, - &stub_install_attributes_), - func_(new EnterprisePlatformKeysChallengeUserKeyFunction(&impl_)) { + : func_(new EnterprisePlatformKeysChallengeUserKeyFunction()) { func_->set_extension(extension_.get()); } @@ -440,12 +216,6 @@ prefs_->SetBoolean(prefs::kAttestationEnabled, true); } - // Returns an error string for the given code. - std::string GetCertificateError(int error_code) { - return base::StringPrintf(EPKPChallengeUserKey::kGetCertificateFailedError, - error_code); - } - std::unique_ptr<base::ListValue> CreateArgs() { return CreateArgsInternal(true); } @@ -461,183 +231,15 @@ return args; } - EPKPChallengeUserKey impl_; + EPKPChallengeKey impl_; scoped_refptr<EnterprisePlatformKeysChallengeUserKeyFunction> func_; }; -TEST_F(EPKChallengeUserKeyTest, UserPolicyDisabled) { - prefs_->SetBoolean(prefs::kAttestationEnabled, false); - - EXPECT_EQ(EPKPChallengeUserKey::kUserPolicyDisabledError, - RunFunctionAndReturnError(func_.get(), CreateArgs(), browser())); -} - TEST_F(EPKChallengeUserKeyTest, ExtensionNotWhitelisted) { base::ListValue empty_whitelist; prefs_->Set(prefs::kAttestationExtensionWhitelist, empty_whitelist); - EXPECT_EQ(EPKPChallengeKeyBase::kExtensionNotWhitelistedError, - RunFunctionAndReturnError(func_.get(), CreateArgs(), browser())); -} - -TEST_F(EPKChallengeUserKeyTest, DevicePolicyDisabled) { - settings_helper_.SetBoolean(chromeos::kDeviceAttestationEnabled, false); - - EXPECT_EQ(EPKPChallengeKeyBase::kDevicePolicyDisabledError, - RunFunctionAndReturnError(func_.get(), CreateArgs(), browser())); -} - -TEST_F(EPKChallengeUserKeyTest, DoesKeyExistDbusFailed) { - cryptohome_client_.set_tpm_attestation_does_key_exist_should_succeed(false); - - EXPECT_EQ(GetCertificateError(kDBusError), - RunFunctionAndReturnError(func_.get(), CreateArgs(), browser())); -} - -TEST_F(EPKChallengeUserKeyTest, GetCertificateFailed) { - EXPECT_CALL(mock_attestation_flow_, GetCertificate(_, _, _, _, _, _)) - .WillRepeatedly(Invoke(GetCertificateCallbackFalse)); - - EXPECT_EQ(GetCertificateError(kGetCertificateFailed), - RunFunctionAndReturnError(func_.get(), CreateArgs(), browser())); -} - -TEST_F(EPKChallengeUserKeyTest, SignChallengeFailed) { - EXPECT_CALL(mock_async_method_caller_, - TpmAttestationSignEnterpriseChallenge(_, _, _, _, _, _, _, _, _)) - .WillRepeatedly(Invoke(SignChallengeCallbackFalse)); - - EXPECT_EQ(EPKPChallengeKeyBase::kSignChallengeFailedError, - RunFunctionAndReturnError(func_.get(), CreateArgs(), browser())); -} - -TEST_F(EPKChallengeUserKeyTest, KeyRegistrationFailed) { - EXPECT_CALL(mock_async_method_caller_, TpmAttestationRegisterKey(_, _, _, _)) - .WillRepeatedly(Invoke(RegisterKeyCallbackFalse)); - - EXPECT_EQ(EPKPChallengeUserKey::kKeyRegistrationFailedError, - RunFunctionAndReturnError(func_.get(), CreateArgs(), browser())); -} - -TEST_F(EPKChallengeUserKeyTest, KeyExists) { - cryptohome_client_.SetTpmAttestationUserCertificate( - cryptohome::CreateAccountIdentifierFromAccountId( - AccountId::FromUserEmail(kUserEmail)), - "attest-ent-user", std::string()); - // GetCertificate must not be called if the key exists. - EXPECT_CALL(mock_attestation_flow_, GetCertificate(_, _, _, _, _, _)) - .Times(0); - - EXPECT_TRUE(utils::RunFunction(func_.get(), CreateArgs(), browser(), - extensions::api_test_utils::NONE)); -} - -TEST_F(EPKChallengeUserKeyTest, KeyNotRegistered) { - EXPECT_CALL(mock_async_method_caller_, TpmAttestationRegisterKey(_, _, _, _)) - .Times(0); - - EXPECT_TRUE(utils::RunFunction(func_.get(), CreateArgsNoRegister(), browser(), - extensions::api_test_utils::NONE)); -} - -TEST_F(EPKChallengeUserKeyTest, PersonalDevice) { - stub_install_attributes_.SetConsumerOwned(); - - // Currently personal devices are not supported. - EXPECT_EQ(GetCertificateError(kUserRejected), - RunFunctionAndReturnError(func_.get(), CreateArgs(), browser())); -} - -TEST_F(EPKChallengeUserKeyTest, Success) { - // GetCertificate must be called exactly once. - EXPECT_CALL( - mock_attestation_flow_, - GetCertificate(chromeos::attestation::PROFILE_ENTERPRISE_USER_CERTIFICATE, - _, _, _, _, _)) - .Times(1); - const cryptohome::Identification cryptohome_id( - AccountId::FromUserEmail(kUserEmail)); - // SignEnterpriseChallenge must be called exactly once. - EXPECT_CALL( - mock_async_method_caller_, - TpmAttestationSignEnterpriseChallenge( - chromeos::attestation::KEY_USER, cryptohome_id, "attest-ent-user", - kUserEmail, "device_id", _, "challenge", _, _)) - .Times(1); - // RegisterKey must be called exactly once. - EXPECT_CALL(mock_async_method_caller_, - TpmAttestationRegisterKey(chromeos::attestation::KEY_USER, - cryptohome_id, "attest-ent-user", _)) - .Times(1); - - std::unique_ptr<base::Value> value( - RunFunctionAndReturnSingleResult(func_.get(), CreateArgs(), browser())); - - ASSERT_TRUE(value->is_blob()); - EXPECT_EQ("response", - std::string(value->GetBlob().begin(), value->GetBlob().end())); -} - -TEST_F(EPKChallengeUserKeyTest, AttestationNotPrepared) { - cryptohome_client_.set_tpm_attestation_is_prepared(false); - - EXPECT_EQ(GetCertificateError(kResetRequired), - RunFunctionAndReturnError(func_.get(), CreateArgs(), browser())); -} - -TEST_F(EPKChallengeUserKeyTest, AttestationPreparedDbusFailed) { - cryptohome_client_.SetServiceIsAvailable(false); - - EXPECT_EQ(GetCertificateError(kDBusError), - RunFunctionAndReturnError(func_.get(), CreateArgs(), browser())); -} - -class EPKChallengeMachineKeyUnmanagedUserTest - : public EPKChallengeMachineKeyTest { - protected: - void SetAuthenticatedUser() override { - signin::MakePrimaryAccountAvailable( - IdentityManagerFactory::GetForProfile(browser()->profile()), - account_id_.GetUserEmail()); - } - - TestingProfile* CreateProfile() override { - fake_user_manager_->AddUser(account_id_); - return profile_manager()->CreateTestingProfile(account_id_.GetUserEmail()); - } - - const std::string email = "test@chromium.com"; - const AccountId account_id_ = - AccountId::FromUserEmailGaiaId(email, - signin::GetTestGaiaIdForEmail(email)); -}; - -TEST_F(EPKChallengeMachineKeyUnmanagedUserTest, UserNotManaged) { - EXPECT_EQ(EPKPChallengeKeyBase::kUserNotManaged, - RunFunctionAndReturnError(func_.get(), CreateArgs(), browser())); -} - -class EPKChallengeUserKeyUnmanagedUserTest : public EPKChallengeUserKeyTest { - protected: - void SetAuthenticatedUser() override { - signin::MakePrimaryAccountAvailable( - IdentityManagerFactory::GetForProfile(browser()->profile()), - account_id_.GetUserEmail()); - } - - TestingProfile* CreateProfile() override { - fake_user_manager_->AddUser(account_id_); - return profile_manager()->CreateTestingProfile(account_id_.GetUserEmail()); - } - - const std::string email = "test@chromium.com"; - const AccountId account_id_ = - AccountId::FromUserEmailGaiaId(email, - signin::GetTestGaiaIdForEmail(email)); -}; - -TEST_F(EPKChallengeUserKeyUnmanagedUserTest, UserNotManaged) { - EXPECT_EQ(EPKPChallengeKeyBase::kUserNotManaged, + EXPECT_EQ(EPKPChallengeKey::kExtensionNotWhitelistedError, RunFunctionAndReturnError(func_.get(), CreateArgs(), browser())); }
diff --git a/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.cc b/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.cc index c68aaa5..a7dfd04 100644 --- a/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.cc +++ b/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.cc
@@ -4,44 +4,20 @@ #include "chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.h" -#include <string> -#include <utility> - #include "base/base64.h" #include "base/bind.h" -#include "base/callback.h" -#include "base/strings/stringprintf.h" #include "base/task/post_task.h" #include "base/values.h" -#include "chrome/browser/browser_process.h" -#include "chrome/browser/chromeos/attestation/attestation_ca_client.h" -#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h" #include "chrome/browser/chromeos/profiles/profile_helper.h" -#include "chrome/browser/chromeos/settings/cros_settings.h" #include "chrome/browser/extensions/chrome_extension_function_details.h" #include "chrome/browser/profiles/profile.h" #include "chrome/common/extensions/api/enterprise_platform_keys_private.h" #include "chrome/common/pref_names.h" -#include "chromeos/attestation/attestation_flow.h" -#include "chromeos/cryptohome/async_method_caller.h" -#include "chromeos/cryptohome/cryptohome_parameters.h" -#include "chromeos/dbus/constants/attestation_constants.h" -#include "chromeos/dbus/cryptohome/cryptohome_client.h" -#include "chromeos/dbus/dbus_method_call_status.h" -#include "chromeos/dbus/dbus_thread_manager.h" -#include "chromeos/settings/cros_settings_names.h" -#include "chromeos/tpm/install_attributes.h" -#include "components/account_id/account_id.h" #include "components/pref_registry/pref_registry_syncable.h" #include "components/prefs/pref_service.h" -#include "components/user_manager/known_user.h" -#include "components/user_manager/user.h" -#include "components/user_manager/user_manager.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "extensions/common/manifest.h" -#include "google_apis/gaia/gaia_auth_util.h" -#include "third_party/cros_system_api/dbus/service_constants.h" namespace { // Prefix for naming machine keys used for SignedPublicKeyAndChallenge when @@ -53,615 +29,68 @@ namespace api_epkp = api::enterprise_platform_keys_private; -// Base class +EPKPChallengeKey::EPKPChallengeKey() = default; +EPKPChallengeKey::~EPKPChallengeKey() = default; -const char EPKPChallengeKeyBase::kChallengeBadBase64Error[] = - "Challenge is not base64 encoded."; -const char EPKPChallengeKeyBase::kDevicePolicyDisabledError[] = - "Remote attestation is not enabled for your device."; -const char EPKPChallengeKeyBase::kExtensionNotWhitelistedError[] = +void EPKPChallengeKey::RegisterProfilePrefs( + user_prefs::PrefRegistrySyncable* registry) { + registry->RegisterListPref(prefs::kAttestationExtensionWhitelist); +} + +const char EPKPChallengeKey::kExtensionNotWhitelistedError[] = "The extension does not have permission to call this function."; -const char EPKPChallengeKeyBase::kResponseBadBase64Error[] = - "Response cannot be encoded in base64."; -const char EPKPChallengeKeyBase::kSignChallengeFailedError[] = - "Failed to sign the challenge."; -const char EPKPChallengeKeyBase::kUserNotManaged[] = - "The user account is not enterprise managed."; +const char EPKPChallengeKey::kChallengeBadBase64Error[] = + "Challenge is not base64 encoded."; -EPKPChallengeKeyBase::PrepareKeyContext::PrepareKeyContext( - chromeos::attestation::AttestationKeyType key_type, - const AccountId& account_id, - const std::string& key_name, - chromeos::attestation::AttestationCertificateProfile certificate_profile, - bool require_user_consent, - const std::string& key_name_for_spkac, - const base::Callback<void(PrepareKeyResult)>& callback) - : key_type(key_type), - account_id(account_id), - key_name(key_name), - certificate_profile(certificate_profile), - require_user_consent(require_user_consent), - key_name_for_spkac(key_name_for_spkac), - callback(callback) {} - -EPKPChallengeKeyBase::PrepareKeyContext::PrepareKeyContext( - const PrepareKeyContext& other) = default; - -EPKPChallengeKeyBase::PrepareKeyContext::~PrepareKeyContext() { -} - -EPKPChallengeKeyBase::EPKPChallengeKeyBase() - : cryptohome_client_(chromeos::CryptohomeClient::Get()), - async_caller_(cryptohome::AsyncMethodCaller::GetInstance()), - install_attributes_(g_browser_process->platform_part() - ->browser_policy_connector_chromeos() - ->GetInstallAttributes()) { - std::unique_ptr<chromeos::attestation::ServerProxy> ca_client( - new chromeos::attestation::AttestationCAClient()); - default_attestation_flow_.reset(new chromeos::attestation::AttestationFlow( - async_caller_, cryptohome_client_, std::move(ca_client))); - attestation_flow_ = default_attestation_flow_.get(); -} - -EPKPChallengeKeyBase::EPKPChallengeKeyBase( - chromeos::CryptohomeClient* cryptohome_client, - cryptohome::AsyncMethodCaller* async_caller, - chromeos::attestation::AttestationFlow* attestation_flow, - chromeos::InstallAttributes* install_attributes) : - cryptohome_client_(cryptohome_client), - async_caller_(async_caller), - attestation_flow_(attestation_flow), - install_attributes_(install_attributes) { -} - -EPKPChallengeKeyBase::~EPKPChallengeKeyBase() { -} - -void EPKPChallengeKeyBase::GetDeviceAttestationEnabled( - const base::Callback<void(bool)>& callback) const { - chromeos::CrosSettings* settings = chromeos::CrosSettings::Get(); - chromeos::CrosSettingsProvider::TrustedStatus status = - settings->PrepareTrustedValues( - base::Bind(&EPKPChallengeKeyBase::GetDeviceAttestationEnabled, - base::Unretained(this), callback)); - - bool value = false; - switch (status) { - case chromeos::CrosSettingsProvider::TRUSTED: - if (!settings->GetBoolean(chromeos::kDeviceAttestationEnabled, &value)) - value = false; - break; - case chromeos::CrosSettingsProvider::TEMPORARILY_UNTRUSTED: - // Do nothing. This function will be called again when the values are - // ready. - return; - case chromeos::CrosSettingsProvider::PERMANENTLY_UNTRUSTED: - // If the value cannot be trusted, we assume that the device attestation - // is false to be on the safe side. - break; - } - - callback.Run(value); -} - -bool EPKPChallengeKeyBase::IsEnterpriseDevice() const { - return install_attributes_->IsEnterpriseManaged(); -} - -bool EPKPChallengeKeyBase::IsExtensionWhitelisted() const { - if (!chromeos::ProfileHelper::Get()->GetUserByProfile(profile_)) { +// Check if the extension is whitelisted in the user policy. +bool EPKPChallengeKey::IsExtensionWhitelisted( + Profile* profile, + scoped_refptr<const Extension> extension) { + if (!chromeos::ProfileHelper::Get()->GetUserByProfile(profile)) { // Only allow remote attestation for apps that were force-installed on the // login/signin screen. // TODO(drcrash): Use a separate device-wide policy for the API. - return Manifest::IsPolicyLocation(extension_->location()); + return Manifest::IsPolicyLocation(extension->location()); } - if (Manifest::IsComponentLocation(extension_->location())) { + if (Manifest::IsComponentLocation(extension->location())) { // Note: For this to even be called, the component extension must also be // whitelisted in chrome/common/extensions/api/_permission_features.json return true; } const base::ListValue* list = - profile_->GetPrefs()->GetList(prefs::kAttestationExtensionWhitelist); - base::Value value(extension_->id()); + profile->GetPrefs()->GetList(prefs::kAttestationExtensionWhitelist); + base::Value value(extension->id()); return list->Find(value) != list->end(); } -AccountId EPKPChallengeKeyBase::GetAccountId() const { - const user_manager::User* user = - chromeos::ProfileHelper::Get()->GetUserByProfile(profile_); - - // Signin profile doesn't have associated user. - if (!user) { - return EmptyAccountId(); - } - - return user->GetAccountId(); -} - -bool EPKPChallengeKeyBase::IsUserAffiliated() const { - const user_manager::User* const user = - user_manager::UserManager::Get()->FindUser(GetAccountId()); - - if (user) { - return user->IsAffiliated(); - } - - return false; -} - -std::string EPKPChallengeKeyBase::GetEnterpriseDomain() const { - return install_attributes_->GetDomain(); -} - -std::string EPKPChallengeKeyBase::GetUserEmail() const { - return GetAccountId().GetUserEmail(); -} - -std::string EPKPChallengeKeyBase::GetDeviceId() const { - return install_attributes_->GetDeviceId(); -} - -void EPKPChallengeKeyBase::PrepareKey( - chromeos::attestation::AttestationKeyType key_type, - const AccountId& account_id, - const std::string& key_name, - chromeos::attestation::AttestationCertificateProfile certificate_profile, - bool require_user_consent, - const std::string& key_name_for_spkac, - const base::Callback<void(PrepareKeyResult)>& callback) { - const PrepareKeyContext context = - PrepareKeyContext(key_type, account_id, key_name, certificate_profile, - require_user_consent, key_name_for_spkac, callback); - cryptohome_client_->TpmAttestationIsPrepared( - base::BindOnce(&EPKPChallengeKeyBase::IsAttestationPreparedCallback, - base::Unretained(this), context)); -} - -void EPKPChallengeKeyBase::IsAttestationPreparedCallback( - const PrepareKeyContext& context, - base::Optional<bool> result) { - if (!result.has_value()) { - context.callback.Run(PREPARE_KEY_DBUS_ERROR); - return; - } - if (!result.value()) { - cryptohome_client_->TpmIsEnabled( - base::BindOnce(&EPKPChallengeKeyBase::PrepareKeyErrorHandlerCallback, - base::Unretained(this), context)); - return; - } - - if (!context.key_name_for_spkac.empty()) { - // Generate a new key and have it signed by PCA. - attestation_flow_->GetCertificate( - context.certificate_profile, context.account_id, - std::string(), // Not used. - true, // Force a new key to be generated. - context.key_name_for_spkac, - base::Bind(&EPKPChallengeKeyBase::GetCertificateCallback, - base::Unretained(this), context.callback)); - return; - } - // Attestation is available, see if the key we need already exists. - cryptohome_client_->TpmAttestationDoesKeyExist( - context.key_type, - cryptohome::CreateAccountIdentifierFromAccountId(context.account_id), - context.key_name, - base::BindOnce(&EPKPChallengeKeyBase::DoesKeyExistCallback, - base::Unretained(this), context)); -} - -void EPKPChallengeKeyBase::PrepareKeyErrorHandlerCallback( - const PrepareKeyContext& context, - base::Optional<bool> is_tpm_enabled) { - if (!is_tpm_enabled.has_value()) { - context.callback.Run(PREPARE_KEY_DBUS_ERROR); - return; - } - - if (is_tpm_enabled.value()) { - context.callback.Run(PREPARE_KEY_RESET_REQUIRED); - } else { - context.callback.Run(PREPARE_KEY_ATTESTATION_UNSUPPORTED); - } -} - -void EPKPChallengeKeyBase::DoesKeyExistCallback( - const PrepareKeyContext& context, - base::Optional<bool> result) { - if (!result.has_value()) { - context.callback.Run(PREPARE_KEY_DBUS_ERROR); - return; - } - - if (result.value()) { - // The key exists. Do nothing more. - context.callback.Run(PREPARE_KEY_OK); - } else { - // The key does not exist. Create a new key and have it signed by PCA. - if (context.require_user_consent) { - // We should ask the user explicitly before sending any private - // information to PCA. - AskForUserConsent( - base::Bind(&EPKPChallengeKeyBase::AskForUserConsentCallback, - base::Unretained(this), context)); - } else { - // User consent is not required. Skip to the next step. - AskForUserConsentCallback(context, true); - } - } -} - -void EPKPChallengeKeyBase::AskForUserConsent( - const base::Callback<void(bool)>& callback) const { - // TODO(davidyu): right now we just simply reject the request before we have - // a way to ask for user consent. - callback.Run(false); -} - -void EPKPChallengeKeyBase::AskForUserConsentCallback( - const PrepareKeyContext& context, - bool result) { - if (!result) { - // The user rejects the request. - context.callback.Run(PREPARE_KEY_USER_REJECTED); - return; - } - - // Generate a new key and have it signed by PCA. - attestation_flow_->GetCertificate( - context.certificate_profile, context.account_id, - std::string(), // Not used. - true, // Force a new key to be generated. - std::string(), // Leave key name empty to generate a default name. - base::Bind(&EPKPChallengeKeyBase::GetCertificateCallback, - base::Unretained(this), context.callback)); -} - -void EPKPChallengeKeyBase::GetCertificateCallback( - const base::Callback<void(PrepareKeyResult)>& callback, - chromeos::attestation::AttestationStatus status, - const std::string& pem_certificate_chain) { - if (status != chromeos::attestation::ATTESTATION_SUCCESS) { - callback.Run(PREPARE_KEY_GET_CERTIFICATE_FAILED); - return; - } - - callback.Run(PREPARE_KEY_OK); -} - -// Implementation of ChallengeMachineKey() - -const char EPKPChallengeMachineKey::kGetCertificateFailedError[] = - "Failed to get Enterprise machine certificate. Error code = %d"; -const char EPKPChallengeMachineKey::kKeyRegistrationFailedError[] = - "Machine key registration failed."; -const char EPKPChallengeMachineKey::kNonEnterpriseDeviceError[] = - "The device is not enterprise enrolled."; - -EPKPChallengeMachineKey::EPKPChallengeMachineKey() : EPKPChallengeKeyBase() { -} - -EPKPChallengeMachineKey::EPKPChallengeMachineKey( - chromeos::CryptohomeClient* cryptohome_client, - cryptohome::AsyncMethodCaller* async_caller, - chromeos::attestation::AttestationFlow* attestation_flow, - chromeos::InstallAttributes* install_attributes) : - EPKPChallengeKeyBase(cryptohome_client, - async_caller, - attestation_flow, - install_attributes) { -} - -EPKPChallengeMachineKey::~EPKPChallengeMachineKey() { -} - -void EPKPChallengeMachineKey::Run(scoped_refptr<ExtensionFunction> caller, - const ChallengeKeyCallback& callback, - const std::string& challenge, - bool register_key) { - callback_ = callback; - profile_ = ChromeExtensionFunctionDetails(caller.get()).GetProfile(); - extension_ = scoped_refptr<const Extension>(caller->extension()); - - // Check if the device is enterprise enrolled. - if (!IsEnterpriseDevice()) { - callback_.Run(false, kNonEnterpriseDeviceError); - return; - } - - // Check if the extension is whitelisted in the user policy. - if (!IsExtensionWhitelisted()) { - callback_.Run(false, kExtensionNotWhitelistedError); - return; - } - - // Check whether the user is managed unless the signin profile is used. - if (chromeos::ProfileHelper::Get()->GetUserByProfile(profile_) && - !IsUserAffiliated()) { - callback_.Run(false, kUserNotManaged); - return; - } - - // Check if RA is enabled in the device policy. - GetDeviceAttestationEnabled( - base::Bind(&EPKPChallengeMachineKey::GetDeviceAttestationEnabledCallback, - base::Unretained(this), challenge, register_key)); -} - -void EPKPChallengeMachineKey::DecodeAndRun( +void EPKPChallengeKey::Run( + chromeos::attestation::AttestationKeyType type, scoped_refptr<ExtensionFunction> caller, - const ChallengeKeyCallback& callback, - const std::string& encoded_challenge, + chromeos::attestation::TpmChallengeKeyCallback callback, + const std::string& challenge, bool register_key) { - std::string challenge; - if (!base::Base64Decode(encoded_challenge, &challenge)) { - callback.Run(false, kChallengeBadBase64Error); - return; - } - Run(caller, callback, challenge, register_key); -} + Profile* profile = ChromeExtensionFunctionDetails(caller.get()).GetProfile(); -void EPKPChallengeMachineKey::GetDeviceAttestationEnabledCallback( - const std::string& challenge, - bool register_key, - bool enabled) { - if (!enabled) { - callback_.Run(false, kDevicePolicyDisabledError); + if (!IsExtensionWhitelisted(profile, caller->extension())) { + std::move(callback).Run( + chromeos::attestation::TpmChallengeKeyResult::MakeError( + kExtensionNotWhitelistedError)); return; } - // The EMK cannot be registered as that would relinquish it and the DMServer - // relies on it to remain stable. If register_key = true, generate a new - // machine key to side-load into the system-wide token. This key will be - // used for SignedPublicKeyAndChallenge but the challenge response will still - // be singed using the stable EMK. std::string key_name_for_spkac; - if (register_key) { - key_name_for_spkac = kEnterpriseMachineKeyForSpkacPrefix + extension_->id(); - } - PrepareKey(chromeos::attestation::KEY_DEVICE, - EmptyAccountId(), // Not used. - chromeos::attestation::kEnterpriseMachineKey, - chromeos::attestation::PROFILE_ENTERPRISE_MACHINE_CERTIFICATE, - false, // user consent is not required. - key_name_for_spkac, - base::Bind(&EPKPChallengeMachineKey::PrepareKeyCallback, - base::Unretained(this), challenge, register_key, - key_name_for_spkac)); -} - -void EPKPChallengeMachineKey::PrepareKeyCallback( - const std::string& challenge, - bool register_key, - const std::string& key_name_for_spkac, - PrepareKeyResult result) { - if (result != PREPARE_KEY_OK) { - callback_.Run(false, - base::StringPrintf(kGetCertificateFailedError, result)); - return; + if (register_key && (type == chromeos::attestation::KEY_DEVICE)) { + key_name_for_spkac = + kEnterpriseMachineKeyForSpkacPrefix + caller->extension()->id(); } - // Everything is checked. Sign the challenge. - async_caller_->TpmAttestationSignEnterpriseChallenge( - chromeos::attestation::KEY_DEVICE, - cryptohome::Identification(), // Not used. - chromeos::attestation::kEnterpriseMachineKey, GetEnterpriseDomain(), - GetDeviceId(), - register_key ? chromeos::attestation::CHALLENGE_INCLUDE_SIGNED_PUBLIC_KEY - : chromeos::attestation::CHALLENGE_OPTION_NONE, - challenge, key_name_for_spkac, - base::Bind(&EPKPChallengeMachineKey::SignChallengeCallback, - base::Unretained(this), register_key)); -} - -void EPKPChallengeMachineKey::SignChallengeCallback( - bool register_key, - bool success, - const std::string& response) { - if (!success) { - callback_.Run(false, kSignChallengeFailedError); - return; - } - if (register_key) { - std::string key_name_for_spkac = - kEnterpriseMachineKeyForSpkacPrefix + extension_->id(); - async_caller_->TpmAttestationRegisterKey( - chromeos::attestation::KEY_DEVICE, - cryptohome::Identification(), // Not used. - key_name_for_spkac, - base::Bind(&EPKPChallengeMachineKey::RegisterKeyCallback, - base::Unretained(this), response)); - } else { - RegisterKeyCallback(response, true, cryptohome::MOUNT_ERROR_NONE); - } -} - -void EPKPChallengeMachineKey::RegisterKeyCallback( - const std::string& response, - bool success, - cryptohome::MountError return_code) { - if (!success || return_code != cryptohome::MOUNT_ERROR_NONE) { - callback_.Run(false, kKeyRegistrationFailedError); - return; - } - callback_.Run(true, response); -} - -// Implementation of ChallengeUserKey() - -const char EPKPChallengeUserKey::kGetCertificateFailedError[] = - "Failed to get Enterprise user certificate. Error code = %d"; -const char EPKPChallengeUserKey::kKeyRegistrationFailedError[] = - "Key registration failed."; -const char EPKPChallengeUserKey::kUserPolicyDisabledError[] = - "Remote attestation is not enabled for your account."; -const char EPKPChallengeUserKey::kUserKeyNotAvailable[] = - "User keys cannot be challenged in this profile."; - -EPKPChallengeUserKey::EPKPChallengeUserKey() : EPKPChallengeKeyBase() { -} - -EPKPChallengeUserKey::EPKPChallengeUserKey( - chromeos::CryptohomeClient* cryptohome_client, - cryptohome::AsyncMethodCaller* async_caller, - chromeos::attestation::AttestationFlow* attestation_flow, - chromeos::InstallAttributes* install_attributes) : - EPKPChallengeKeyBase(cryptohome_client, - async_caller, - attestation_flow, - install_attributes) { -} - -EPKPChallengeUserKey::~EPKPChallengeUserKey() { -} - -void EPKPChallengeUserKey::RegisterProfilePrefs( - user_prefs::PrefRegistrySyncable* registry) { - registry->RegisterBooleanPref(prefs::kAttestationEnabled, false); - registry->RegisterListPref(prefs::kAttestationExtensionWhitelist); -} - -void EPKPChallengeUserKey::Run(scoped_refptr<ExtensionFunction> caller, - const ChallengeKeyCallback& callback, - const std::string& challenge, - bool register_key) { - callback_ = callback; - profile_ = ChromeExtensionFunctionDetails(caller.get()).GetProfile(); - extension_ = scoped_refptr<const Extension>(caller->extension()); - - // Check if user keys are available in this profile. - if (!chromeos::ProfileHelper::Get()->GetUserByProfile(profile_)) { - callback_.Run(false, EPKPChallengeUserKey::kUserKeyNotAvailable); - return; - } - - // Check if RA is enabled in the user policy. - if (!IsRemoteAttestationEnabledForUser()) { - callback_.Run(false, kUserPolicyDisabledError); - return; - } - - // Check if the extension is whitelisted in the user policy. - if (!IsExtensionWhitelisted()) { - callback_.Run(false, kExtensionNotWhitelistedError); - return; - } - - if (IsEnterpriseDevice()) { - if (!IsUserAffiliated()) { - callback_.Run(false, kUserNotManaged); - return; - } - - // Check if RA is enabled in the device policy. - GetDeviceAttestationEnabled( - base::Bind(&EPKPChallengeUserKey::GetDeviceAttestationEnabledCallback, - base::Unretained(this), challenge, register_key, - false)); // user consent is not required. - } else { - // For personal devices, we don't need to check if RA is enabled in the - // device, but we need to ask for user consent if the key does not exist. - GetDeviceAttestationEnabledCallback(challenge, register_key, - true, // user consent is required. - true); // attestation is enabled. - } -} - -void EPKPChallengeUserKey::DecodeAndRun(scoped_refptr<ExtensionFunction> caller, - const ChallengeKeyCallback& callback, - const std::string& encoded_challenge, - bool register_key) { - std::string challenge; - if (!base::Base64Decode(encoded_challenge, &challenge)) { - callback.Run(false, kChallengeBadBase64Error); - return; - } - Run(caller, callback, challenge, register_key); -} - -void EPKPChallengeUserKey::GetDeviceAttestationEnabledCallback( - const std::string& challenge, - bool register_key, - bool require_user_consent, - bool enabled) { - if (!enabled) { - callback_.Run(false, kDevicePolicyDisabledError); - return; - } - - PrepareKey(chromeos::attestation::KEY_USER, GetAccountId(), - chromeos::attestation::kEnterpriseUserKey, - chromeos::attestation::PROFILE_ENTERPRISE_USER_CERTIFICATE, - require_user_consent, std::string() /* key_name_for_spkac */, - base::Bind(&EPKPChallengeUserKey::PrepareKeyCallback, - base::Unretained(this), challenge, register_key)); -} - -void EPKPChallengeUserKey::PrepareKeyCallback(const std::string& challenge, - bool register_key, - PrepareKeyResult result) { - if (result != PREPARE_KEY_OK) { - callback_.Run(false, - base::StringPrintf(kGetCertificateFailedError, result)); - return; - } - - // Everything is checked. Sign the challenge. - async_caller_->TpmAttestationSignEnterpriseChallenge( - chromeos::attestation::KEY_USER, - cryptohome::Identification(GetAccountId()), - chromeos::attestation::kEnterpriseUserKey, GetUserEmail(), GetDeviceId(), - register_key ? chromeos::attestation::CHALLENGE_INCLUDE_SIGNED_PUBLIC_KEY - : chromeos::attestation::CHALLENGE_OPTION_NONE, - challenge, std::string() /* key_name_for_spkac */, - base::Bind(&EPKPChallengeUserKey::SignChallengeCallback, - base::Unretained(this), register_key)); -} - -void EPKPChallengeUserKey::SignChallengeCallback(bool register_key, - bool success, - const std::string& response) { - if (!success) { - callback_.Run(false, kSignChallengeFailedError); - return; - } - - if (register_key) { - async_caller_->TpmAttestationRegisterKey( - chromeos::attestation::KEY_USER, - cryptohome::Identification(GetAccountId()), - chromeos::attestation::kEnterpriseUserKey, - base::Bind(&EPKPChallengeUserKey::RegisterKeyCallback, - base::Unretained(this), response)); - } else { - RegisterKeyCallback(response, true, cryptohome::MOUNT_ERROR_NONE); - } -} - -void EPKPChallengeUserKey::RegisterKeyCallback( - const std::string& response, - bool success, - cryptohome::MountError return_code) { - if (!success || return_code != cryptohome::MOUNT_ERROR_NONE) { - callback_.Run(false, kKeyRegistrationFailedError); - return; - } - callback_.Run(true, response); -} - -bool EPKPChallengeUserKey::IsRemoteAttestationEnabledForUser() const { - return profile_->GetPrefs()->GetBoolean(prefs::kAttestationEnabled); + impl_ = chromeos::attestation::TpmChallengeKeyFactory::Create(); + impl_->Run(type, profile, std::move(callback), challenge, register_key, + key_name_for_spkac); } EnterprisePlatformKeysPrivateChallengeMachineKeyFunction:: - EnterprisePlatformKeysPrivateChallengeMachineKeyFunction() - : default_impl_(new EPKPChallengeMachineKey), impl_(default_impl_.get()) {} - -EnterprisePlatformKeysPrivateChallengeMachineKeyFunction:: - EnterprisePlatformKeysPrivateChallengeMachineKeyFunction( - EPKPChallengeMachineKey* impl_for_testing) - : impl_(impl_for_testing) {} + EnterprisePlatformKeysPrivateChallengeMachineKeyFunction() = default; EnterprisePlatformKeysPrivateChallengeMachineKeyFunction:: ~EnterprisePlatformKeysPrivateChallengeMachineKeyFunction() = default; @@ -671,41 +100,41 @@ std::unique_ptr<api_epkp::ChallengeMachineKey::Params> params( api_epkp::ChallengeMachineKey::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params); - ChallengeKeyCallback callback = + chromeos::attestation::TpmChallengeKeyCallback callback = base::Bind(&EnterprisePlatformKeysPrivateChallengeMachineKeyFunction:: OnChallengedKey, this); + + std::string challenge; + if (!base::Base64Decode(params->challenge, &challenge)) { + return RespondNow(Error(EPKPChallengeKey::kChallengeBadBase64Error)); + } + // base::Unretained is safe on impl_ since its life-cycle matches |this| and // |callback| holds a reference to |this|. - base::Closure task = base::Bind(&EPKPChallengeMachineKey::DecodeAndRun, - base::Unretained(impl_), - scoped_refptr<ExtensionFunction>(this), - callback, params->challenge, false); - base::PostTask(FROM_HERE, {content::BrowserThread::UI}, task); + base::OnceClosure task = base::BindOnce( + &EPKPChallengeKey::Run, base::Unretained(&impl_), + chromeos::attestation::KEY_DEVICE, scoped_refptr<ExtensionFunction>(this), + std::move(callback), challenge, + /*register_key=*/false); + base::PostTask(FROM_HERE, {content::BrowserThread::UI}, std::move(task)); return RespondLater(); } void EnterprisePlatformKeysPrivateChallengeMachineKeyFunction::OnChallengedKey( - bool success, - const std::string& data) { - if (success) { + const chromeos::attestation::TpmChallengeKeyResult& result) { + if (result.is_success) { std::string encoded_response; - base::Base64Encode(data, &encoded_response); + base::Base64Encode(result.data, &encoded_response); Respond(ArgumentList( api_epkp::ChallengeMachineKey::Results::Create(encoded_response))); } else { - Respond(Error(data)); + Respond(Error(result.error_message)); } } EnterprisePlatformKeysPrivateChallengeUserKeyFunction:: - EnterprisePlatformKeysPrivateChallengeUserKeyFunction() - : default_impl_(new EPKPChallengeUserKey), impl_(default_impl_.get()) {} - -EnterprisePlatformKeysPrivateChallengeUserKeyFunction:: - EnterprisePlatformKeysPrivateChallengeUserKeyFunction( - EPKPChallengeUserKey* impl_for_testing) - : impl_(impl_for_testing) {} + EnterprisePlatformKeysPrivateChallengeUserKeyFunction() = default; EnterprisePlatformKeysPrivateChallengeUserKeyFunction:: ~EnterprisePlatformKeysPrivateChallengeUserKeyFunction() = default; @@ -715,29 +144,34 @@ std::unique_ptr<api_epkp::ChallengeUserKey::Params> params( api_epkp::ChallengeUserKey::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params); - ChallengeKeyCallback callback = base::Bind( + chromeos::attestation::TpmChallengeKeyCallback callback = base::Bind( &EnterprisePlatformKeysPrivateChallengeUserKeyFunction::OnChallengedKey, this); + + std::string challenge; + if (!base::Base64Decode(params->challenge, &challenge)) { + return RespondNow(Error(EPKPChallengeKey::kChallengeBadBase64Error)); + } + // base::Unretained is safe on impl_ since its life-cycle matches |this| and // |callback| holds a reference to |this|. - base::Closure task = - base::Bind(&EPKPChallengeUserKey::DecodeAndRun, base::Unretained(impl_), - scoped_refptr<ExtensionFunction>(this), callback, - params->challenge, params->register_key); - base::PostTask(FROM_HERE, {content::BrowserThread::UI}, task); + base::OnceClosure task = base::BindOnce( + &EPKPChallengeKey::Run, base::Unretained(&impl_), + chromeos::attestation::KEY_USER, scoped_refptr<ExtensionFunction>(this), + std::move(callback), challenge, params->register_key); + base::PostTask(FROM_HERE, {content::BrowserThread::UI}, std::move(task)); return RespondLater(); } void EnterprisePlatformKeysPrivateChallengeUserKeyFunction::OnChallengedKey( - bool success, - const std::string& data) { - if (success) { + const chromeos::attestation::TpmChallengeKeyResult& result) { + if (result.is_success) { std::string encoded_response; - base::Base64Encode(data, &encoded_response); + base::Base64Encode(result.data, &encoded_response); Respond(ArgumentList( api_epkp::ChallengeUserKey::Results::Create(encoded_response))); } else { - Respond(Error(data)); + Respond(Error(result.error_message)); } }
diff --git a/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.h b/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.h index e8e9fa0..8223117 100644 --- a/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.h +++ b/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.h
@@ -11,272 +11,60 @@ #include <memory> #include <string> -#include "base/callback.h" -#include "base/compiler_specific.h" -#include "base/optional.h" -#include "chrome/common/extensions/api/enterprise_platform_keys_private.h" -#include "chromeos/attestation/attestation_flow.h" +#include "chrome/browser/chromeos/attestation/tpm_challenge_key.h" #include "chromeos/dbus/constants/attestation_constants.h" -#include "chromeos/dbus/cryptohome/cryptohome_client.h" -#include "components/account_id/account_id.h" #include "extensions/browser/extension_function.h" #include "extensions/common/extension.h" -#include "third_party/cros_system_api/dbus/service_constants.h" class Profile; -namespace chromeos { -class CryptohomeClient; -class InstallAttributes; -} - -namespace cryptohome { -class AsyncMethodCaller; -} - namespace user_prefs { class PrefRegistrySyncable; } namespace extensions { -// A callback for challenge key operations. If the operation succeeded, -// |success| is true and |data| is the challenge response. Otherwise, |success| -// is false and |data| is an error message. -using ChallengeKeyCallback = - base::Callback<void(bool success, const std::string& data)>; - -class EPKPChallengeKeyBase { +class EPKPChallengeKey { public: - static const char kChallengeBadBase64Error[]; - static const char kDevicePolicyDisabledError[]; static const char kExtensionNotWhitelistedError[]; - static const char kResponseBadBase64Error[]; - static const char kSignChallengeFailedError[]; - static const char kUserNotManaged[]; - - protected: - enum PrepareKeyResult { - PREPARE_KEY_OK = 0, - PREPARE_KEY_DBUS_ERROR, - PREPARE_KEY_USER_REJECTED, - PREPARE_KEY_GET_CERTIFICATE_FAILED, - PREPARE_KEY_RESET_REQUIRED, - PREPARE_KEY_ATTESTATION_UNSUPPORTED - }; - - EPKPChallengeKeyBase(); - EPKPChallengeKeyBase( - chromeos::CryptohomeClient* cryptohome_client, - cryptohome::AsyncMethodCaller* async_caller, - chromeos::attestation::AttestationFlow* attestation_flow, - chromeos::InstallAttributes* install_attributes); - virtual ~EPKPChallengeKeyBase(); - - // Returns a trusted value from CroSettings indicating if the device - // attestation is enabled. - void GetDeviceAttestationEnabled( - const base::Callback<void(bool)>& callback) const; - - // Returns true if the device is enterprise managed. - bool IsEnterpriseDevice() const; - - // Returns true if the extension is white-listed in the user policy. - bool IsExtensionWhitelisted() const; - - // Returns true if the user is managed and is affiliated with the domain the - // device is enrolled to. - bool IsUserAffiliated() const; - - // Returns the enterprise domain the device is enrolled to. - std::string GetEnterpriseDomain() const; - - // Returns the user email. - std::string GetUserEmail() const; - - // Returns account id. - AccountId GetAccountId() const; - - // Returns the enterprise virtual device ID. - std::string GetDeviceId() const; - - // Prepares the key for signing. It will first check if a new key should be - // generated, i.e. |key_name_for_spkac| is not empty or the key doesn't - // exist and, if necessary, call AttestationFlow::GetCertificate() to get a - // new one. If require_user_consent is true, it will explicitly ask for user - // consent before calling GetCertificate(). - void PrepareKey( - chromeos::attestation::AttestationKeyType key_type, - const AccountId& account_id, - const std::string& key_name, - chromeos::attestation::AttestationCertificateProfile certificate_profile, - bool require_user_consent, - const std::string& key_name_for_spkac, - const base::Callback<void(PrepareKeyResult)>& callback); - - chromeos::CryptohomeClient* cryptohome_client_; - cryptohome::AsyncMethodCaller* async_caller_; - chromeos::attestation::AttestationFlow* attestation_flow_; - std::unique_ptr<chromeos::attestation::AttestationFlow> - default_attestation_flow_; - ChallengeKeyCallback callback_; - Profile* profile_; - scoped_refptr<const Extension> extension_; - - private: - // Holds the context of a PrepareKey() operation. - struct PrepareKeyContext { - PrepareKeyContext(chromeos::attestation::AttestationKeyType key_type, - const AccountId& account_id, - const std::string& key_name, - chromeos::attestation::AttestationCertificateProfile - certificate_profile, - bool require_user_consent, - const std::string& key_name_for_spkac, - const base::Callback<void(PrepareKeyResult)>& callback); - PrepareKeyContext(const PrepareKeyContext& other); - ~PrepareKeyContext(); - - chromeos::attestation::AttestationKeyType key_type; - const AccountId account_id; - const std::string key_name; - chromeos::attestation::AttestationCertificateProfile certificate_profile; - bool require_user_consent; - std::string key_name_for_spkac; - const base::Callback<void(PrepareKeyResult)> callback; - }; - - void IsAttestationPreparedCallback(const PrepareKeyContext& context, - base::Optional<bool> result); - void PrepareKeyErrorHandlerCallback(const PrepareKeyContext& context, - base::Optional<bool> is_tpm_enabled); - void DoesKeyExistCallback(const PrepareKeyContext& context, - base::Optional<bool> result); - void AskForUserConsent(const base::Callback<void(bool)>& callback) const; - void AskForUserConsentCallback( - const PrepareKeyContext& context, - bool result); - void GetCertificateCallback( - const base::Callback<void(PrepareKeyResult)>& callback, - chromeos::attestation::AttestationStatus status, - const std::string& pem_certificate_chain); - - chromeos::InstallAttributes* install_attributes_; -}; - -class EPKPChallengeMachineKey : public EPKPChallengeKeyBase { - public: - static const char kGetCertificateFailedError[]; - static const char kKeyRegistrationFailedError[]; - static const char kNonEnterpriseDeviceError[]; - - EPKPChallengeMachineKey(); - EPKPChallengeMachineKey( - chromeos::CryptohomeClient* cryptohome_client, - cryptohome::AsyncMethodCaller* async_caller, - chromeos::attestation::AttestationFlow* attestation_flow, - chromeos::InstallAttributes* install_attributes); - ~EPKPChallengeMachineKey() override; - - // Asynchronously run the flow to challenge a machine key in the |caller| - // context. - void Run(scoped_refptr<ExtensionFunction> caller, - const ChallengeKeyCallback& callback, - const std::string& encoded_challenge, - bool register_key); - - // Like |Run| but expects a Base64 |encoded_challenge|. - void DecodeAndRun(scoped_refptr<ExtensionFunction> caller, - const ChallengeKeyCallback& callback, - const std::string& encoded_challenge, - bool register_key); - - private: - static const char kKeyName[]; - - void GetDeviceAttestationEnabledCallback(const std::string& challenge, - bool register_key, - bool enabled); - void PrepareKeyCallback(const std::string& challenge, - bool register_key, - const std::string& key_name_for_spkac, - PrepareKeyResult result); - void SignChallengeCallback(bool register_key, - bool success, - const std::string& response); - void RegisterKeyCallback(const std::string& response, - bool success, - cryptohome::MountError return_code); -}; - -class EPKPChallengeUserKey : public EPKPChallengeKeyBase { - public: - static const char kGetCertificateFailedError[]; - static const char kKeyRegistrationFailedError[]; - static const char kUserKeyNotAvailable[]; - static const char kUserPolicyDisabledError[]; - - EPKPChallengeUserKey(); - EPKPChallengeUserKey( - chromeos::CryptohomeClient* cryptohome_client, - cryptohome::AsyncMethodCaller* async_caller, - chromeos::attestation::AttestationFlow* attestation_flow, - chromeos::InstallAttributes* install_attributes); - ~EPKPChallengeUserKey() override; + static const char kChallengeBadBase64Error[]; + EPKPChallengeKey(); + EPKPChallengeKey(const EPKPChallengeKey&) = delete; + EPKPChallengeKey& operator=(const EPKPChallengeKey&) = delete; + ~EPKPChallengeKey(); static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry); - // Asynchronously run the flow to challenge a user key in the |caller| + // Asynchronously run the flow to challenge a key in the |caller| // context. - void Run(scoped_refptr<ExtensionFunction> caller, - const ChallengeKeyCallback& callback, + void Run(chromeos::attestation::AttestationKeyType type, + scoped_refptr<ExtensionFunction> caller, + chromeos::attestation::TpmChallengeKeyCallback callback, const std::string& challenge, bool register_key); - // Like |Run| but expects a Base64 |encoded_challenge|. - void DecodeAndRun(scoped_refptr<ExtensionFunction> caller, - const ChallengeKeyCallback& callback, - const std::string& encoded_challenge, - bool register_key); - private: - static const char kKeyName[]; + // Check if the extension is whitelisted in the user policy. + bool IsExtensionWhitelisted(Profile* profile, + scoped_refptr<const Extension> extension); - void GetDeviceAttestationEnabledCallback(const std::string& challenge, - bool register_key, - bool require_user_consent, - bool enabled); - void PrepareKeyCallback(const std::string& challenge, - bool register_key, - PrepareKeyResult result); - void SignChallengeCallback(bool register_key, - bool success, - const std::string& response); - void RegisterKeyCallback(const std::string& response, - bool success, - cryptohome::MountError return_code); - - bool IsRemoteAttestationEnabledForUser() const; + std::unique_ptr<chromeos::attestation::TpmChallengeKey> impl_; }; class EnterprisePlatformKeysPrivateChallengeMachineKeyFunction : public ExtensionFunction { public: EnterprisePlatformKeysPrivateChallengeMachineKeyFunction(); - explicit EnterprisePlatformKeysPrivateChallengeMachineKeyFunction( - EPKPChallengeMachineKey* impl_for_testing); private: ~EnterprisePlatformKeysPrivateChallengeMachineKeyFunction() override; ResponseAction Run() override; - // Called when the challenge operation is complete. If the operation succeeded - // |success| will be true and |data| will contain the challenge response data. - // Otherwise |success| will be false and |data| is an error message. - void OnChallengedKey(bool success, const std::string& data); + // Called when the challenge operation is complete. + void OnChallengedKey( + const chromeos::attestation::TpmChallengeKeyResult& result); - std::unique_ptr<EPKPChallengeMachineKey> default_impl_; - EPKPChallengeMachineKey* impl_; + EPKPChallengeKey impl_; DECLARE_EXTENSION_FUNCTION( "enterprise.platformKeysPrivate.challengeMachineKey", @@ -287,20 +75,16 @@ : public ExtensionFunction { public: EnterprisePlatformKeysPrivateChallengeUserKeyFunction(); - explicit EnterprisePlatformKeysPrivateChallengeUserKeyFunction( - EPKPChallengeUserKey* impl_for_testing); private: ~EnterprisePlatformKeysPrivateChallengeUserKeyFunction() override; ResponseAction Run() override; - // Called when the challenge operation is complete. If the operation succeeded - // |success| will be true and |data| will contain the challenge response data. - // Otherwise |success| will be false and |data| is an error message. - void OnChallengedKey(bool success, const std::string& data); + // Called when the challenge operation is complete. + void OnChallengedKey( + const chromeos::attestation::TpmChallengeKeyResult& result); - std::unique_ptr<EPKPChallengeUserKey> default_impl_; - EPKPChallengeUserKey* impl_; + EPKPChallengeKey impl_; DECLARE_EXTENSION_FUNCTION("enterprise.platformKeysPrivate.challengeUserKey", ENTERPRISE_PLATFORMKEYSPRIVATE_CHALLENGEUSERKEY)
diff --git a/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api_unittest.cc b/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api_unittest.cc index 6230610..db219faef 100644 --- a/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api_unittest.cc +++ b/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api_unittest.cc
@@ -4,206 +4,59 @@ #include "chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.h" -#include <string> +#include <utility> -#include "base/bind.h" -#include "base/location.h" -#include "base/memory/ptr_util.h" -#include "base/strings/stringprintf.h" -#include "base/threading/thread_task_runner_handle.h" #include "base/values.h" +#include "chrome/browser/chromeos/attestation/mock_tpm_challenge_key.h" #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h" -#include "chrome/browser/chromeos/profiles/profile_helper.h" -#include "chrome/browser/chromeos/settings/cros_settings.h" -#include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h" #include "chrome/browser/extensions/extension_function_test_utils.h" #include "chrome/browser/signin/identity_manager_factory.h" -#include "chrome/browser/ui/browser.h" -#include "chrome/common/chrome_constants.h" #include "chrome/common/pref_names.h" #include "chrome/test/base/browser_with_test_window_test.h" -#include "chrome/test/base/testing_browser_process.h" #include "chrome/test/base/testing_profile_manager.h" -#include "chromeos/attestation/mock_attestation_flow.h" -#include "chromeos/cryptohome/async_method_caller.h" -#include "chromeos/cryptohome/cryptohome_parameters.h" -#include "chromeos/cryptohome/mock_async_method_caller.h" -#include "chromeos/dbus/constants/attestation_constants.h" -#include "chromeos/dbus/cryptohome/fake_cryptohome_client.h" -#include "chromeos/tpm/stub_install_attributes.h" -#include "components/account_id/account_id.h" -#include "components/policy/core/common/cloud/cloud_policy_constants.h" -#include "components/prefs/pref_service.h" #include "components/signin/public/identity_manager/identity_manager.h" #include "components/signin/public/identity_manager/identity_test_utils.h" #include "components/user_manager/scoped_user_manager.h" #include "extensions/common/extension_builder.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/cros_system_api/dbus/service_constants.h" -using testing::_; -using testing::Invoke; using testing::NiceMock; -using testing::Return; -using testing::WithArgs; namespace utils = extension_function_test_utils; namespace extensions { namespace { -// Certificate errors as reported to the calling extension. -const int kDBusError = 1; -const int kUserRejected = 2; -const int kGetCertificateFailed = 3; -const int kResetRequired = 4; -const int kPrepareKeyAttestationUnsupported = 5; - const char kUserEmail[] = "test@google.com"; -void RegisterKeyCallbackTrue( - chromeos::attestation::AttestationKeyType key_type, - const cryptohome::Identification& user_id, - const std::string& key_name, - const cryptohome::AsyncMethodCaller::Callback& callback) { - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::BindOnce(callback, true, cryptohome::MOUNT_ERROR_NONE)); -} - -void RegisterKeyCallbackFalse( - chromeos::attestation::AttestationKeyType key_type, - const cryptohome::Identification& user_id, - const std::string& key_name, - const cryptohome::AsyncMethodCaller::Callback& callback) { - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::BindOnce(callback, false, cryptohome::MOUNT_ERROR_NONE)); -} - -void SignChallengeCallbackTrue( - chromeos::attestation::AttestationKeyType key_type, - const cryptohome::Identification& user_id, - const std::string& key_name, - const std::string& domain, - const std::string& device_id, - chromeos::attestation::AttestationChallengeOptions options, - const std::string& challenge, - const std::string& key_name_for_spkac, - const cryptohome::AsyncMethodCaller::DataCallback& callback) { - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::BindOnce(callback, true, "response")); -} - -void SignChallengeCallbackFalse( - chromeos::attestation::AttestationKeyType key_type, - const cryptohome::Identification& user_id, - const std::string& key_name, - const std::string& domain, - const std::string& device_id, - chromeos::attestation::AttestationChallengeOptions options, - const std::string& challenge, - const std::string& key_name_for_spkac, - const cryptohome::AsyncMethodCaller::DataCallback& callback) { - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::BindOnce(callback, false, "")); -} - -void GetCertificateCallbackTrue( - chromeos::attestation::AttestationCertificateProfile certificate_profile, - const AccountId& account_id, - const std::string& request_origin, - bool force_new_key, - const std::string& key_name, - const chromeos::attestation::AttestationFlow::CertificateCallback& - callback) { - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindRepeating(callback, chromeos::attestation::ATTESTATION_SUCCESS, - "certificate")); -} - -void GetCertificateCallbackUnspecifiedFailure( - chromeos::attestation::AttestationCertificateProfile certificate_profile, - const AccountId& account_id, - const std::string& request_origin, - bool force_new_key, - const std::string& key_name, - const chromeos::attestation::AttestationFlow::CertificateCallback& - callback) { - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindRepeating( - callback, chromeos::attestation::ATTESTATION_UNSPECIFIED_FAILURE, - "")); -} - -void GetCertificateCallbackBadRequestFailure( - chromeos::attestation::AttestationCertificateProfile certificate_profile, - const AccountId& account_id, - const std::string& request_origin, - bool force_new_key, - const std::string& key_name, - const chromeos::attestation::AttestationFlow::CertificateCallback& - callback) { - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindRepeating( - callback, - chromeos::attestation::ATTESTATION_SERVER_BAD_REQUEST_FAILURE, "")); -} - class EPKPChallengeKeyTestBase : public BrowserWithTestWindowTest { - public: - enum class ProfileType { USER_PROFILE, SIGNIN_PROFILE }; - protected: - explicit EPKPChallengeKeyTestBase(ProfileType profile_type) - : settings_helper_(false), - profile_type_(profile_type), - fake_user_manager_(new chromeos::FakeChromeUserManager), + EPKPChallengeKeyTestBase() + : fake_user_manager_(new chromeos::FakeChromeUserManager()), user_manager_enabler_(base::WrapUnique(fake_user_manager_)) { - // Create the extension. - extension_ = CreateExtension(); - - // Set up the default behavior of mocks. - ON_CALL(mock_async_method_caller_, TpmAttestationRegisterKey(_, _, _, _)) - .WillByDefault(Invoke(RegisterKeyCallbackTrue)); - ON_CALL(mock_async_method_caller_, - TpmAttestationSignEnterpriseChallenge(_, _, _, _, _, _, _, _, _)) - .WillByDefault(Invoke(SignChallengeCallbackTrue)); - ON_CALL(mock_attestation_flow_, GetCertificate(_, _, _, _, _, _)) - .WillByDefault(Invoke(GetCertificateCallbackTrue)); - - stub_install_attributes_.SetCloudManaged("google.com", "device_id"); - - settings_helper_.ReplaceDeviceSettingsProviderWithStub(); - settings_helper_.SetBoolean(chromeos::kDeviceAttestationEnabled, true); + extension_ = ExtensionBuilder("Test").Build(); } void SetUp() override { BrowserWithTestWindowTest::SetUp(); - if (profile_type_ == ProfileType::USER_PROFILE) { - // Set the user preferences. - prefs_ = browser()->profile()->GetPrefs(); - base::ListValue whitelist; - whitelist.AppendString(extension_->id()); - prefs_->Set(prefs::kAttestationExtensionWhitelist, whitelist); + prefs_ = browser()->profile()->GetPrefs(); + SetAuthenticatedUser(); + } - SetAuthenticatedUser(); - } + void SetMockTpmChallenger() { + auto mock_tpm_challenge_key = std::make_unique< + NiceMock<chromeos::attestation::MockTpmChallengeKey>>(); + mock_tpm_challenge_key->EnableFake(); + chromeos::attestation::TpmChallengeKeyFactory::SetForTesting( + std::move(mock_tpm_challenge_key)); } // This will be called by BrowserWithTestWindowTest::SetUp(); TestingProfile* CreateProfile() override { - switch (profile_type_) { - case ProfileType::USER_PROFILE: - fake_user_manager_->AddUserWithAffiliation( - AccountId::FromUserEmail(kUserEmail), true); - return profile_manager()->CreateTestingProfile(kUserEmail); - - case ProfileType::SIGNIN_PROFILE: - return profile_manager()->CreateTestingProfile(chrome::kInitialProfile); - } + fake_user_manager_->AddUserWithAffiliation( + AccountId::FromUserEmail(kUserEmail), true); + return profile_manager()->CreateTestingProfile(kUserEmail); } // Derived classes can override this method to set the required authenticated @@ -214,432 +67,90 @@ signin::MakePrimaryAccountAvailable(identity_manager, kUserEmail); } - chromeos::FakeCryptohomeClient cryptohome_client_; - NiceMock<cryptohome::MockAsyncMethodCaller> mock_async_method_caller_; - NiceMock<chromeos::attestation::MockAttestationFlow> mock_attestation_flow_; - chromeos::ScopedCrosSettingsTestHelper settings_helper_; scoped_refptr<const Extension> extension_; - chromeos::StubInstallAttributes stub_install_attributes_; - ProfileType profile_type_; // fake_user_manager_ is owned by user_manager_enabler_. - chromeos::FakeChromeUserManager* fake_user_manager_; + chromeos::FakeChromeUserManager* fake_user_manager_ = nullptr; user_manager::ScopedUserManager user_manager_enabler_; PrefService* prefs_ = nullptr; - - private: - scoped_refptr<const Extension> CreateExtension() { - switch (profile_type_) { - case ProfileType::USER_PROFILE: - return ExtensionBuilder("Test").Build(); - - case ProfileType::SIGNIN_PROFILE: - return ExtensionBuilder("Test", ExtensionBuilder::Type::PLATFORM_APP) - .SetLocation(Manifest::Location::EXTERNAL_POLICY) - .Build(); - } - } }; class EPKPChallengeMachineKeyTest : public EPKPChallengeKeyTestBase { protected: - static const char kArgs[]; + static const char kFuncArgs[]; - explicit EPKPChallengeMachineKeyTest( - ProfileType profile_type = ProfileType::USER_PROFILE) - : EPKPChallengeKeyTestBase(profile_type), - impl_(&cryptohome_client_, - &mock_async_method_caller_, - &mock_attestation_flow_, - &stub_install_attributes_), - func_(new EnterprisePlatformKeysPrivateChallengeMachineKeyFunction( - &impl_)) { + EPKPChallengeMachineKeyTest() + : func_(new EnterprisePlatformKeysPrivateChallengeMachineKeyFunction()) { func_->set_extension(extension_.get()); } - // Returns an error string for the given code. - std::string GetCertificateError(int error_code) { - return base::StringPrintf( - EPKPChallengeMachineKey::kGetCertificateFailedError, - error_code); - } - - EPKPChallengeMachineKey impl_; scoped_refptr<EnterprisePlatformKeysPrivateChallengeMachineKeyFunction> func_; }; // Base 64 encoding of 'challenge'. -const char EPKPChallengeMachineKeyTest::kArgs[] = "[\"Y2hhbGxlbmdl\"]"; - -TEST_F(EPKPChallengeMachineKeyTest, ChallengeBadBase64) { - EXPECT_EQ(EPKPChallengeKeyBase::kChallengeBadBase64Error, - utils::RunFunctionAndReturnError( - func_.get(), "[\"****\"]", browser())); -} - -TEST_F(EPKPChallengeMachineKeyTest, NonEnterpriseDevice) { - stub_install_attributes_.SetConsumerOwned(); - - EXPECT_EQ(EPKPChallengeMachineKey::kNonEnterpriseDeviceError, - utils::RunFunctionAndReturnError(func_.get(), kArgs, browser())); -} +const char EPKPChallengeMachineKeyTest::kFuncArgs[] = "[\"Y2hhbGxlbmdl\"]"; TEST_F(EPKPChallengeMachineKeyTest, ExtensionNotWhitelisted) { base::ListValue empty_whitelist; prefs_->Set(prefs::kAttestationExtensionWhitelist, empty_whitelist); - EXPECT_EQ(EPKPChallengeKeyBase::kExtensionNotWhitelistedError, - utils::RunFunctionAndReturnError(func_.get(), kArgs, browser())); + EXPECT_EQ( + EPKPChallengeKey::kExtensionNotWhitelistedError, + utils::RunFunctionAndReturnError(func_.get(), kFuncArgs, browser())); } -TEST_F(EPKPChallengeMachineKeyTest, DevicePolicyDisabled) { - settings_helper_.SetBoolean(chromeos::kDeviceAttestationEnabled, false); +TEST_F(EPKPChallengeMachineKeyTest, Success) { + SetMockTpmChallenger(); - EXPECT_EQ(EPKPChallengeKeyBase::kDevicePolicyDisabledError, - utils::RunFunctionAndReturnError(func_.get(), kArgs, browser())); -} - -TEST_F(EPKPChallengeMachineKeyTest, DoesKeyExistDbusFailed) { - cryptohome_client_.set_tpm_attestation_does_key_exist_should_succeed(false); - - EXPECT_EQ(GetCertificateError(kDBusError), - utils::RunFunctionAndReturnError(func_.get(), kArgs, browser())); -} - -TEST_F(EPKPChallengeMachineKeyTest, GetCertificateFailed) { - EXPECT_CALL(mock_attestation_flow_, GetCertificate(_, _, _, _, _, _)) - .WillRepeatedly(Invoke(GetCertificateCallbackUnspecifiedFailure)); - - EXPECT_EQ(GetCertificateError(kGetCertificateFailed), - utils::RunFunctionAndReturnError(func_.get(), kArgs, browser())); -} - -TEST_F(EPKPChallengeMachineKeyTest, SignChallengeFailed) { - EXPECT_CALL(mock_async_method_caller_, - TpmAttestationSignEnterpriseChallenge(_, _, _, _, _, _, _, _, _)) - .WillRepeatedly(Invoke(SignChallengeCallbackFalse)); - - EXPECT_EQ(EPKPChallengeKeyBase::kSignChallengeFailedError, - utils::RunFunctionAndReturnError(func_.get(), kArgs, browser())); -} - -TEST_F(EPKPChallengeMachineKeyTest, KeyExists) { - cryptohome_client_.SetTpmAttestationDeviceCertificate("attest-ent-machine", - std::string()); - // GetCertificate must not be called if the key exists. - EXPECT_CALL(mock_attestation_flow_, GetCertificate(_, _, _, _, _, _)) - .Times(0); - - EXPECT_TRUE(utils::RunFunction(func_.get(), kArgs, browser(), - extensions::api_test_utils::NONE)); -} - -TEST_F(EPKPChallengeMachineKeyTest, AttestationNotPrepared) { - cryptohome_client_.set_tpm_attestation_is_prepared(false); - - EXPECT_EQ(GetCertificateError(kResetRequired), - utils::RunFunctionAndReturnError(func_.get(), kArgs, browser())); -} - -// Test that we get proper error message in case we don't have TPM. -TEST_F(EPKPChallengeMachineKeyTest, AttestationUnsupported) { - cryptohome_client_.set_tpm_attestation_is_prepared(false); - cryptohome_client_.set_tpm_is_enabled(false); - - EXPECT_EQ(GetCertificateError(kPrepareKeyAttestationUnsupported), - utils::RunFunctionAndReturnError(func_.get(), kArgs, browser())); -} - -TEST_F(EPKPChallengeMachineKeyTest, AttestationPreparedDbusFailed) { - cryptohome_client_.SetServiceIsAvailable(false); - - EXPECT_EQ(GetCertificateError(kDBusError), - utils::RunFunctionAndReturnError(func_.get(), kArgs, browser())); -} - -// Tests the API with all profiles types as determined by the test parameter. -class EPKPChallengeMachineKeyAllProfilesTest - : public EPKPChallengeMachineKeyTest, - public ::testing::WithParamInterface< - EPKPChallengeKeyTestBase::ProfileType> { - protected: - EPKPChallengeMachineKeyAllProfilesTest() - : EPKPChallengeMachineKeyTest(GetParam()) {} -}; - -TEST_P(EPKPChallengeMachineKeyAllProfilesTest, Success) { - // GetCertificate must be called exactly once. - EXPECT_CALL(mock_attestation_flow_, - GetCertificate( - chromeos::attestation::PROFILE_ENTERPRISE_MACHINE_CERTIFICATE, - _, _, _, _, _)) - .Times(1); - // SignEnterpriseChallenge must be called exactly once. - EXPECT_CALL(mock_async_method_caller_, - TpmAttestationSignEnterpriseChallenge( - chromeos::attestation::KEY_DEVICE, - cryptohome::Identification(), "attest-ent-machine", - "google.com", "device_id", _, "challenge", _, _)) - .Times(1); + base::ListValue whitelist; + whitelist.AppendString(extension_->id()); + prefs_->Set(prefs::kAttestationExtensionWhitelist, whitelist); std::unique_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult( - func_.get(), kArgs, browser(), extensions::api_test_utils::NONE)); + func_.get(), kFuncArgs, browser(), extensions::api_test_utils::NONE)); std::string response; value->GetAsString(&response); EXPECT_EQ("cmVzcG9uc2U=" /* Base64 encoding of 'response' */, response); } -INSTANTIATE_TEST_SUITE_P( - AllProfiles, - EPKPChallengeMachineKeyAllProfilesTest, - ::testing::Values(EPKPChallengeKeyTestBase::ProfileType::USER_PROFILE, - EPKPChallengeKeyTestBase::ProfileType::SIGNIN_PROFILE)); - class EPKPChallengeUserKeyTest : public EPKPChallengeKeyTestBase { protected: - static const char kArgs[]; + static const char kFuncArgs[]; - explicit EPKPChallengeUserKeyTest( - ProfileType profile_type = ProfileType::USER_PROFILE) - : EPKPChallengeKeyTestBase(profile_type), - impl_(&cryptohome_client_, - &mock_async_method_caller_, - &mock_attestation_flow_, - &stub_install_attributes_), - func_( - new EnterprisePlatformKeysPrivateChallengeUserKeyFunction(&impl_)) { + EPKPChallengeUserKeyTest() + : func_(new EnterprisePlatformKeysPrivateChallengeUserKeyFunction()) { func_->set_extension(extension_.get()); } - void SetUp() override { - EPKPChallengeKeyTestBase::SetUp(); - - if (profile_type_ == ProfileType::USER_PROFILE) { - // Set the user preferences. - prefs_->SetBoolean(prefs::kAttestationEnabled, true); - } - } - - // Returns an error string for the given code. - std::string GetCertificateError(int error_code) { - return base::StringPrintf(EPKPChallengeUserKey::kGetCertificateFailedError, - error_code); - } - - EPKPChallengeUserKey impl_; scoped_refptr<EnterprisePlatformKeysPrivateChallengeUserKeyFunction> func_; }; -// Base 64 encoding of 'challenge' -const char EPKPChallengeUserKeyTest::kArgs[] = "[\"Y2hhbGxlbmdl\", true]"; - -TEST_F(EPKPChallengeUserKeyTest, ChallengeBadBase64) { - EXPECT_EQ(EPKPChallengeKeyBase::kChallengeBadBase64Error, - utils::RunFunctionAndReturnError( - func_.get(), "[\"****\", true]", browser())); -} - -TEST_F(EPKPChallengeUserKeyTest, UserPolicyDisabled) { - prefs_->SetBoolean(prefs::kAttestationEnabled, false); - - EXPECT_EQ(EPKPChallengeUserKey::kUserPolicyDisabledError, - utils::RunFunctionAndReturnError(func_.get(), kArgs, browser())); -} +// Base 64 encoding of 'challenge', register_key required. +const char EPKPChallengeUserKeyTest::kFuncArgs[] = "[\"Y2hhbGxlbmdl\", true]"; TEST_F(EPKPChallengeUserKeyTest, ExtensionNotWhitelisted) { base::ListValue empty_whitelist; prefs_->Set(prefs::kAttestationExtensionWhitelist, empty_whitelist); - EXPECT_EQ(EPKPChallengeKeyBase::kExtensionNotWhitelistedError, - utils::RunFunctionAndReturnError(func_.get(), kArgs, browser())); -} - -TEST_F(EPKPChallengeUserKeyTest, DevicePolicyDisabled) { - settings_helper_.SetBoolean(chromeos::kDeviceAttestationEnabled, false); - - EXPECT_EQ(EPKPChallengeKeyBase::kDevicePolicyDisabledError, - utils::RunFunctionAndReturnError(func_.get(), kArgs, browser())); -} - -TEST_F(EPKPChallengeUserKeyTest, DoesKeyExistDbusFailed) { - cryptohome_client_.set_tpm_attestation_does_key_exist_should_succeed(false); - - EXPECT_EQ(GetCertificateError(kDBusError), - utils::RunFunctionAndReturnError(func_.get(), kArgs, browser())); -} - -TEST_F(EPKPChallengeUserKeyTest, GetCertificateFailedWithUnspecifiedFailure) { - EXPECT_CALL(mock_attestation_flow_, GetCertificate(_, _, _, _, _, _)) - .WillRepeatedly(Invoke(GetCertificateCallbackUnspecifiedFailure)); - - EXPECT_EQ(GetCertificateError(kGetCertificateFailed), - utils::RunFunctionAndReturnError(func_.get(), kArgs, browser())); -} - -TEST_F(EPKPChallengeUserKeyTest, GetCertificateFailedWithBadRequestFailure) { - EXPECT_CALL(mock_attestation_flow_, GetCertificate(_, _, _, _, _, _)) - .WillRepeatedly(Invoke(GetCertificateCallbackBadRequestFailure)); - - EXPECT_EQ(GetCertificateError(kGetCertificateFailed), - utils::RunFunctionAndReturnError(func_.get(), kArgs, browser())); -} - -TEST_F(EPKPChallengeUserKeyTest, SignChallengeFailed) { - EXPECT_CALL(mock_async_method_caller_, - TpmAttestationSignEnterpriseChallenge(_, _, _, _, _, _, _, _, _)) - .WillRepeatedly(Invoke(SignChallengeCallbackFalse)); - - EXPECT_EQ(EPKPChallengeKeyBase::kSignChallengeFailedError, - utils::RunFunctionAndReturnError(func_.get(), kArgs, browser())); -} - -TEST_F(EPKPChallengeUserKeyTest, KeyRegistrationFailed) { - EXPECT_CALL(mock_async_method_caller_, TpmAttestationRegisterKey(_, _, _, _)) - .WillRepeatedly(Invoke(RegisterKeyCallbackFalse)); - - EXPECT_EQ(EPKPChallengeUserKey::kKeyRegistrationFailedError, - utils::RunFunctionAndReturnError(func_.get(), kArgs, browser())); -} - -TEST_F(EPKPChallengeUserKeyTest, KeyExists) { - cryptohome_client_.SetTpmAttestationUserCertificate( - cryptohome::CreateAccountIdentifierFromAccountId( - AccountId::FromUserEmail(kUserEmail)), - "attest-ent-user", std::string()); - // GetCertificate must not be called if the key exists. - EXPECT_CALL(mock_attestation_flow_, GetCertificate(_, _, _, _, _, _)) - .Times(0); - - EXPECT_TRUE(utils::RunFunction(func_.get(), kArgs, browser(), - extensions::api_test_utils::NONE)); -} - -TEST_F(EPKPChallengeUserKeyTest, KeyNotRegistered) { - EXPECT_CALL(mock_async_method_caller_, TpmAttestationRegisterKey(_, _, _, _)) - .Times(0); - - EXPECT_TRUE(utils::RunFunction(func_.get(), "[\"Y2hhbGxlbmdl\", false]", - browser(), extensions::api_test_utils::NONE)); -} - -TEST_F(EPKPChallengeUserKeyTest, PersonalDevice) { - stub_install_attributes_.SetConsumerOwned(); - - // Currently personal devices are not supported. - EXPECT_EQ(GetCertificateError(kUserRejected), - utils::RunFunctionAndReturnError(func_.get(), kArgs, browser())); + EXPECT_EQ( + EPKPChallengeKey::kExtensionNotWhitelistedError, + utils::RunFunctionAndReturnError(func_.get(), kFuncArgs, browser())); } TEST_F(EPKPChallengeUserKeyTest, Success) { - // GetCertificate must be called exactly once. - EXPECT_CALL( - mock_attestation_flow_, - GetCertificate(chromeos::attestation::PROFILE_ENTERPRISE_USER_CERTIFICATE, - _, _, _, _, _)) - .Times(1); - const AccountId account_id = AccountId::FromUserEmail(kUserEmail); - // SignEnterpriseChallenge must be called exactly once. - EXPECT_CALL(mock_async_method_caller_, - TpmAttestationSignEnterpriseChallenge( - chromeos::attestation::KEY_USER, - cryptohome::Identification(account_id), "attest-ent-user", - cryptohome::Identification(account_id).id(), "device_id", _, - "challenge", _, _)) - .Times(1); - // RegisterKey must be called exactly once. - EXPECT_CALL(mock_async_method_caller_, - TpmAttestationRegisterKey(chromeos::attestation::KEY_USER, - cryptohome::Identification(account_id), - "attest-ent-user", _)) - .Times(1); + SetMockTpmChallenger(); + + base::ListValue whitelist; + whitelist.AppendString(extension_->id()); + prefs_->Set(prefs::kAttestationExtensionWhitelist, whitelist); std::unique_ptr<base::Value> value(utils::RunFunctionAndReturnSingleResult( - func_.get(), kArgs, browser(), extensions::api_test_utils::NONE)); + func_.get(), kFuncArgs, browser(), extensions::api_test_utils::NONE)); std::string response; value->GetAsString(&response); EXPECT_EQ("cmVzcG9uc2U=" /* Base64 encoding of 'response' */, response); } -TEST_F(EPKPChallengeUserKeyTest, AttestationNotPrepared) { - cryptohome_client_.set_tpm_attestation_is_prepared(false); - - EXPECT_EQ(GetCertificateError(kResetRequired), - utils::RunFunctionAndReturnError(func_.get(), kArgs, browser())); -} - -TEST_F(EPKPChallengeUserKeyTest, AttestationPreparedDbusFailed) { - cryptohome_client_.SetServiceIsAvailable(false); - - EXPECT_EQ(GetCertificateError(kDBusError), - utils::RunFunctionAndReturnError(func_.get(), kArgs, browser())); -} - -class EPKPChallengeUserKeySigninProfileTest : public EPKPChallengeUserKeyTest { - protected: - EPKPChallengeUserKeySigninProfileTest() - : EPKPChallengeUserKeyTest(ProfileType::SIGNIN_PROFILE) {} -}; - -TEST_F(EPKPChallengeUserKeySigninProfileTest, UserKeyNotAvailable) { - settings_helper_.SetBoolean(chromeos::kDeviceAttestationEnabled, false); - - EXPECT_EQ(EPKPChallengeUserKey::kUserKeyNotAvailable, - utils::RunFunctionAndReturnError(func_.get(), kArgs, browser())); -} - -class EPKPChallengeMachineKeyUnmanagedUserTest - : public EPKPChallengeMachineKeyTest { - protected: - void SetAuthenticatedUser() override { - auto* identity_manager = - IdentityManagerFactory::GetForProfile(browser()->profile()); - signin::MakePrimaryAccountAvailable(identity_manager, - account_id_.GetUserEmail()); - } - - TestingProfile* CreateProfile() override { - fake_user_manager_->AddUser(account_id_); - return profile_manager()->CreateTestingProfile(account_id_.GetUserEmail()); - } - - private: - const std::string kOtherEmail = "test@chromium.com"; - const AccountId account_id_ = AccountId::FromUserEmailGaiaId( - kOtherEmail, - signin::GetTestGaiaIdForEmail(kOtherEmail)); -}; - -TEST_F(EPKPChallengeMachineKeyUnmanagedUserTest, UserNotManaged) { - EXPECT_EQ(EPKPChallengeKeyBase::kUserNotManaged, - utils::RunFunctionAndReturnError(func_.get(), kArgs, browser())); -} - -class EPKPChallengeUserKeyUnmanagedUserTest : public EPKPChallengeUserKeyTest { - protected: - void SetAuthenticatedUser() override { - auto* identity_manager = - IdentityManagerFactory::GetForProfile(browser()->profile()); - signin::MakePrimaryAccountAvailable(identity_manager, - account_id_.GetUserEmail()); - } - - TestingProfile* CreateProfile() override { - fake_user_manager_->AddUser(account_id_); - return profile_manager()->CreateTestingProfile(account_id_.GetUserEmail()); - } - - private: - const std::string kOtherEmail = "test@chromium.com"; - const AccountId account_id_ = AccountId::FromUserEmailGaiaId( - kOtherEmail, - signin::GetTestGaiaIdForEmail(kOtherEmail)); -}; - -TEST_F(EPKPChallengeUserKeyUnmanagedUserTest, UserNotManaged) { - EXPECT_EQ(EPKPChallengeKeyBase::kUserNotManaged, - utils::RunFunctionAndReturnError(func_.get(), kArgs, browser())); -} - } // namespace } // namespace extensions
diff --git a/chrome/browser/extensions/api/input_ime/input_ime_api.cc b/chrome/browser/extensions/api/input_ime/input_ime_api.cc index 006075d..f4157fa 100644 --- a/chrome/browser/extensions/api/input_ime/input_ime_api.cc +++ b/chrome/browser/extensions/api/input_ime/input_ime_api.cc
@@ -80,7 +80,7 @@ void ImeObserver::OnKeyEvent( const std::string& component_id, const InputMethodEngineBase::KeyboardEvent& event, - IMEEngineHandlerInterface::KeyEventDoneCallback key_data) { + IMEEngineHandlerInterface::KeyEventDoneCallback callback) { if (extension_id_.empty()) return; @@ -89,7 +89,7 @@ if (!ShouldForwardKeyEvent()) { // Continue processing the key event so that the physical keyboard can // still work. - std::move(key_data).Run(false); + std::move(callback).Run(false); return; } @@ -99,7 +99,7 @@ return; const std::string request_id = event_router->GetEngineIfActive(extension_id_) - ->AddRequest(component_id, std::move(key_data)); + ->AddPendingKeyEvent(component_id, std::move(callback)); input_ime::KeyboardEvent key_data_value; key_data_value.type = input_ime::ParseKeyboardEventType(event.type);
diff --git a/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.cc index b5d850a4..c0ebc7ef 100644 --- a/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.cc +++ b/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.cc
@@ -120,9 +120,9 @@ const char kHistogramFirstInputTimestampSkipFilteringComparison[] = "PageLoad.InteractiveTiming.FirstInputTimestamp.SkipFilteringComparison"; const char kHistogramLongestInputDelay[] = - "PageLoad.InteractiveTiming.LongestInputDelay3"; + "PageLoad.InteractiveTiming.LongestInputDelay4"; const char kHistogramLongestInputTimestamp[] = - "PageLoad.InteractiveTiming.LongestInputTimestamp3"; + "PageLoad.InteractiveTiming.LongestInputTimestamp4"; const char kHistogramParseStartToFirstMeaningfulPaint[] = "PageLoad.Experimental.PaintTiming.ParseStartToFirstMeaningfulPaint"; const char kHistogramParseStartToFirstContentfulPaint[] =
diff --git a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc index 3849662..afb4d9e 100644 --- a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc +++ b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer.cc
@@ -378,13 +378,13 @@ if (timing.interactive_timing->longest_input_delay) { base::TimeDelta longest_input_delay = timing.interactive_timing->longest_input_delay.value(); - builder.SetInteractiveTiming_LongestInputDelay3( + builder.SetInteractiveTiming_LongestInputDelay4( longest_input_delay.InMilliseconds()); } if (timing.interactive_timing->longest_input_timestamp) { base::TimeDelta longest_input_timestamp = timing.interactive_timing->longest_input_timestamp.value(); - builder.SetInteractiveTiming_LongestInputTimestamp3( + builder.SetInteractiveTiming_LongestInputTimestamp4( longest_input_timestamp.InMilliseconds()); }
diff --git a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc index d05d9f2..4137ad85 100644 --- a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc +++ b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc
@@ -863,11 +863,11 @@ test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(), GURL(kTestUrl1)); test_ukm_recorder().ExpectEntryMetric( - kv.second.get(), PageLoad::kInteractiveTiming_LongestInputDelay3Name, + kv.second.get(), PageLoad::kInteractiveTiming_LongestInputDelay4Name, 50); test_ukm_recorder().ExpectEntryMetric( kv.second.get(), - PageLoad::kInteractiveTiming_LongestInputTimestamp3Name, 712); + PageLoad::kInteractiveTiming_LongestInputTimestamp4Name, 712); } }
diff --git a/chrome/browser/password_manager/credential_leak_controller_android.cc b/chrome/browser/password_manager/credential_leak_controller_android.cc index 46efed6f..ce79541 100644 --- a/chrome/browser/password_manager/credential_leak_controller_android.cc +++ b/chrome/browser/password_manager/credential_leak_controller_android.cc
@@ -8,8 +8,8 @@ #include "base/android/jni_string.h" #include "chrome/android/chrome_jni_headers/PasswordCheckupLauncher_jni.h" #include "chrome/browser/ui/android/passwords/credential_leak_dialog_view_android.h" -#include "chrome/browser/ui/passwords/credential_leak_dialog_utils.h" #include "chrome/common/url_constants.h" +#include "components/password_manager/core/browser/leak_detection_dialog_utils.h" #include "ui/android/window_android.h" using password_manager::metrics_util::LeakDialogDismissalReason; @@ -30,14 +30,14 @@ void CredentialLeakControllerAndroid::OnCancelDialog() { LogLeakDialogTypeAndDismissalReason( - leak_dialog_utils::GetLeakDialogType(leak_type_), + password_manager::GetLeakDialogType(leak_type_), LeakDialogDismissalReason::kClickedClose); delete this; } void CredentialLeakControllerAndroid::OnAcceptDialog() { LogLeakDialogTypeAndDismissalReason( - leak_dialog_utils::GetLeakDialogType(leak_type_), + password_manager::GetLeakDialogType(leak_type_), ShouldCheckPasswords() ? LeakDialogDismissalReason::kClickedCheckPasswords : LeakDialogDismissalReason::kClickedOk); @@ -47,7 +47,7 @@ Java_PasswordCheckupLauncher_launchCheckup( env, base::android::ConvertUTF8ToJavaString( - env, leak_dialog_utils::GetPasswordCheckupURL().spec()), + env, password_manager::GetPasswordCheckupURL().spec()), window_android_->GetJavaObject()); } @@ -56,35 +56,35 @@ void CredentialLeakControllerAndroid::OnCloseDialog() { LogLeakDialogTypeAndDismissalReason( - leak_dialog_utils::GetLeakDialogType(leak_type_), + password_manager::GetLeakDialogType(leak_type_), LeakDialogDismissalReason::kNoDirectInteraction); delete this; } base::string16 CredentialLeakControllerAndroid::GetAcceptButtonLabel() const { - return leak_dialog_utils::GetAcceptButtonLabel(leak_type_); + return password_manager::GetAcceptButtonLabel(leak_type_); } base::string16 CredentialLeakControllerAndroid::GetCancelButtonLabel() const { - return leak_dialog_utils::GetCancelButtonLabel(); + return password_manager::GetCancelButtonLabel(); } base::string16 CredentialLeakControllerAndroid::GetDescription() const { - return leak_dialog_utils::GetDescription(leak_type_, origin_); + return password_manager::GetDescription(leak_type_, origin_); } base::string16 CredentialLeakControllerAndroid::GetTitle() const { - return leak_dialog_utils::GetTitle(leak_type_); + return password_manager::GetTitle(leak_type_); } gfx::Range CredentialLeakControllerAndroid::GetDescriptionBoldRange() const { - return leak_dialog_utils::GetChangePasswordBoldRange(leak_type_, origin_); + return password_manager::GetChangePasswordBoldRange(leak_type_, origin_); } bool CredentialLeakControllerAndroid::ShouldCheckPasswords() const { - return leak_dialog_utils::ShouldCheckPasswords(leak_type_); + return password_manager::ShouldCheckPasswords(leak_type_); } bool CredentialLeakControllerAndroid::ShouldShowCancelButton() const { - return leak_dialog_utils::ShouldShowCancelButton(leak_type_); + return password_manager::ShouldShowCancelButton(leak_type_); }
diff --git a/chrome/browser/performance_manager/decorators/process_metrics_decorator_unittest.cc b/chrome/browser/performance_manager/decorators/process_metrics_decorator_unittest.cc index b6fa075..cdcbe66 100644 --- a/chrome/browser/performance_manager/decorators/process_metrics_decorator_unittest.cc +++ b/chrome/browser/performance_manager/decorators/process_metrics_decorator_unittest.cc
@@ -134,11 +134,11 @@ MockSystemNodeObserver sys_node_observer; graph()->AddSystemNodeObserver(&sys_node_observer); - auto memory_dump = base::make_optional(std::move( + auto memory_dump = base::make_optional( GenerateMemoryDump({{mock_graph()->process->process_id(), kFakeResidentSetKb, kFakePrivateFootprintKb}, {mock_graph()->other_process->process_id(), - kFakeResidentSetKb, kFakePrivateFootprintKb}}))); + kFakeResidentSetKb, kFakePrivateFootprintKb}})); EXPECT_CALL(*decorator(), GetMemoryDump()) .WillOnce(testing::Return(testing::ByMove(std::move(memory_dump))));
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc index bafc4d06..086c510 100644 --- a/chrome/browser/prefs/browser_prefs.cc +++ b/chrome/browser/prefs/browser_prefs.cc
@@ -931,7 +931,8 @@ chromeos::ServicesCustomizationDocument::RegisterProfilePrefs(registry); chromeos::UserImageSyncObserver::RegisterProfilePrefs(registry); crostini::prefs::RegisterProfilePrefs(registry); - extensions::EPKPChallengeUserKey::RegisterProfilePrefs(registry); + chromeos::attestation::TpmChallengeKey::RegisterProfilePrefs(registry); + extensions::EPKPChallengeKey::RegisterProfilePrefs(registry); flags_ui::PrefServiceFlagsStorage::RegisterProfilePrefs(registry); guest_os::prefs::RegisterProfilePrefs(registry); lock_screen_apps::StateController::RegisterProfilePrefs(registry);
diff --git a/chrome/browser/resources/chromeos/camera/BUILD.gn b/chrome/browser/resources/chromeos/camera/BUILD.gn index 83a2fb78..01cfbca1 100644 --- a/chrome/browser/resources/chromeos/camera/BUILD.gn +++ b/chrome/browser/resources/chromeos/camera/BUILD.gn
@@ -79,6 +79,9 @@ "src/images/camera_button_timer_on_10s.svg", "src/images/camera_button_timer_on_3s.svg", "src/images/camera_focus_aim.svg", + "src/images/camera_intent_play_video.svg", + "src/images/camera_intent_result_cancel.svg", + "src/images/camera_intent_result_confirm.svg", "src/images/camera_mode_photo.svg", "src/images/camera_mode_portrait.svg", "src/images/camera_mode_square.svg", @@ -145,6 +148,7 @@ "src/js/device/camera3_device_info.js", "src/js/device/constraints_preferrer.js", "src/js/device/device_info_updater.js", + "src/js/device/error.js", ] outputs = [ @@ -154,11 +158,13 @@ copy("chrome_camera_app_js_models") { sources = [ + "src/js/models/file_video_saver.js", "src/js/models/filenamer.js", "src/js/models/filesystem.js", "src/js/models/gallery.js", + "src/js/models/intent_video_saver.js", "src/js/models/result_saver.js", - "src/js/models/video_saver.js", + "src/js/models/video_saver_interface.js", ] outputs = [ @@ -182,6 +188,7 @@ sources = [ "src/js/views/browser.js", "src/js/views/camera.js", + "src/js/views/camera_intent.js", "src/js/views/dialog.js", "src/js/views/gallery_base.js", "src/js/views/settings.js", @@ -201,6 +208,7 @@ "src/js/views/camera/options.js", "src/js/views/camera/preview.js", "src/js/views/camera/recordtime.js", + "src/js/views/camera/review_result.js", "src/js/views/camera/timertick.js", ]
diff --git a/chrome/browser/resources/chromeos/camera/Makefile b/chrome/browser/resources/chromeos/camera/Makefile index 721778c..9956660 100644 --- a/chrome/browser/resources/chromeos/camera/Makefile +++ b/chrome/browser/resources/chromeos/camera/Makefile
@@ -93,6 +93,9 @@ src/images/camera_button_timer_on_10s.svg \ src/images/camera_button_timer_on_3s.svg \ src/images/camera_focus_aim.svg \ + src/images/camera_intent_play_video.svg \ + src/images/camera_intent_result_cancel.svg \ + src/images/camera_intent_result_confirm.svg \ src/images/camera_mode_photo.svg \ src/images/camera_mode_portrait.svg \ src/images/camera_mode_square.svg \ @@ -119,16 +122,19 @@ src/js/device/camera3_device_info.js \ src/js/device/constraints_preferrer.js \ src/js/device/device_info_updater.js \ + src/js/device/error.js \ src/js/gallerybutton.js \ src/js/google-analytics-bundle.js \ src/js/intent.js \ src/js/main.js \ src/js/metrics.js \ + src/js/models/file_video_saver.js \ src/js/models/filenamer.js \ src/js/models/filesystem.js \ src/js/models/gallery.js \ + src/js/models/intent_video_saver.js \ src/js/models/result_saver.js \ - src/js/models/video_saver.js \ + src/js/models/video_saver_interface.js \ src/js/mojo/chrome_helper.js \ src/js/mojo/device_operator.js \ src/js/mojo/image_capture.js \ @@ -142,11 +148,13 @@ src/js/util.js \ src/js/views/browser.js \ src/js/views/camera.js \ + src/js/views/camera_intent.js \ src/js/views/camera/layout.js \ src/js/views/camera/modes.js \ src/js/views/camera/options.js \ src/js/views/camera/preview.js \ src/js/views/camera/recordtime.js \ + src/js/views/camera/review_result.js \ src/js/views/camera/timertick.js \ src/js/views/dialog.js \ src/js/views/gallery_base.js \
diff --git a/chrome/browser/resources/chromeos/camera/camera_resources.grd b/chrome/browser/resources/chromeos/camera/camera_resources.grd index 59f7a2a..947be1d 100644 --- a/chrome/browser/resources/chromeos/camera/camera_resources.grd +++ b/chrome/browser/resources/chromeos/camera/camera_resources.grd
@@ -17,11 +17,13 @@ <structure name="IDR_CAMERA_BUNDLE_JS" file="src/js/google-analytics-bundle.js" type="chrome_html" /> <structure name="IDR_CAMERA_CAMERA3_DEVICE_INFO_JS" file="src/js/device/camera3_device_info.js" type="chrome_html" /> <structure name="IDR_CAMERA_CAMERA_JS" file="src/js/views/camera.js" type="chrome_html" /> + <structure name="IDR_CAMERA_CAMERA_INTENT_JS" file="src/js/views/camera_intent.js" type="chrome_html" /> <structure name="IDR_CAMERA_CONSTRAINTS_PREFERRER_JS" file="src/js/device/constraints_preferrer.js" type="chrome_html" /> <structure name="IDR_CAMERA_CHROME_HELPER_JS" file="src/js/mojo/chrome_helper.js" type="chrome_html" /> <structure name="IDR_CAMERA_DEVICE_OPERATOR_JS" file="src/js/mojo/device_operator.js" type="chrome_html" /> <structure name="IDR_CAMERA_DEVICE_INFO_UPDATER_JS" file="src/js/device/device_info_updater.js" type="chrome_html" /> <structure name="IDR_CAMERA_DIALOG_JS" file="src/js/views/dialog.js" type="chrome_html" /> + <structure name="IDR_CAMERA_ERROR_JS" file="src/js/device/error.js" type="chrome_html" /> <structure name="IDR_CAMERA_FILENAMER_JS" file="src/js/models/filenamer.js" type="chrome_html" /> <structure name="IDR_CAMERA_FILESYSTEM_JS" file="src/js/models/filesystem.js" type="chrome_html" /> <structure name="IDR_CAMERA_GALLERY_BASE_JS" file="src/js/views/gallery_base.js" type="chrome_html" /> @@ -42,6 +44,7 @@ <structure name="IDR_CAMERA_RECORDTIME_JS" file="src/js/views/camera/recordtime.js" type="chrome_html" /> <structure name="IDR_CAMERA_RESOLUTION_EVENT_BROKER_JS" file="src/js/resolution_event_broker.js" type="chrome_html" /> <structure name="IDR_CAMERA_RESULT_SAVER_JS" file="src/js/models/result_saver.js" type="chrome_html" /> + <structure name="IDR_CAMERA_REVIEW_RESULT_JS" file="src/js/views/camera/review_result.js" type="chrome_html" /> <structure name="IDR_CAMERA_SCROLLBAR_JS" file="src/js/scrollbar.js" type="chrome_html" /> <structure name="IDR_CAMERA_SETTINGS_JS" file="src/js/views/settings.js" type="chrome_html" /> <structure name="IDR_CAMERA_SOUND_JS" file="src/js/sound.js" type="chrome_html" /> @@ -50,7 +53,9 @@ <structure name="IDR_CAMERA_TOAST_JS" file="src/js/toast.js" type="chrome_html" /> <structure name="IDR_CAMERA_TOOLTIP_JS" file="src/js/tooltip.js" type="chrome_html" /> <structure name="IDR_CAMERA_UTIL_JS" file="src/js/util.js" type="chrome_html" /> - <structure name="IDR_CAMERA_VIDEO_SAVER_JS" file="src/js/models/video_saver.js" type="chrome_html" /> + <structure name="IDR_CAMERA_VIDEO_SAVER_INTERFACE_JS" file="src/js/models/video_saver_interface.js" type="chrome_html" /> + <structure name="IDR_CAMERA_INTENT_VIDEO_SAVER_JS" file="src/js/models/file_video_saver.js" type="chrome_html" /> + <structure name="IDR_CAMERA_FILE_VIDEO_SAVER_JS" file="src/js/models/intent_video_saver.js" type="chrome_html" /> <structure name="IDR_CAMERA_VIEW_JS" file="src/js/views/view.js" type="chrome_html" /> <structure name="IDR_CAMERA_WARNING_JS" file="src/js/views/warning.js" type="chrome_html" /> <structure name="IDR_CAMERA_WEBUI_BROWSER_PROXY" file="src/js/browser_proxy/webui_browser_proxy.js" type="chrome_html" />
diff --git a/chrome/browser/resources/chromeos/camera/src/css/main.css b/chrome/browser/resources/chromeos/camera/src/css/main.css index b8ba077..9b5deb8 100644 --- a/chrome/browser/resources/chromeos/camera/src/css/main.css +++ b/chrome/browser/resources/chromeos/camera/src/css/main.css
@@ -214,6 +214,14 @@ bottom: calc((var(--modes-bottom) + var(--modes-height)) + 16px); } +body.review-result #shutters-group { + display: none; +} + +body.should-handle-intent-result #shutters-group { + bottom: var(--modes-bottom); +} + body.tablet-landscape .actions-group { flex-direction: column-reverse; } @@ -239,6 +247,52 @@ padding: var(--modes-gradient-padding) 8px; } +body.should-handle-intent-result #modes-group { + display: none; +} + +.preview-content { + height: 0; /* Calculate at runtime. */ + width: 0; /* Calculate at runtime. */ +} + +#play-result-video { + background-image: url(../images/camera_intent_play_video.svg); + height: 80px; + width: 80px; +} + +#confirm-result-groups { + bottom: calc(var(--bottom-line) - 36px); + display: flex; + flex-direction: column; +} + +#confirm-result-groups>button { + flex: 0 0 76px; + height: 72px; + width: 72px; +} + +body.review-result #preview-video, +body:not(.review-result) #review-photo-result, +body:not(.review-result) #review-video-result, +body:not(.review-photo-result) #review-photo-result, +body.review-photo-result #review-video-result, +body.review-photo-result #play-result-video, +body.playing-result-video #play-result-video, +body:not(.review-result) #confirm-result-groups { + display: none; +} + +#confirm-result { + background-image: url(../images/camera_intent_result_confirm.svg); +} + +#cancel-result { + background-image: url(../images/camera_intent_result_cancel.svg); +} + .mode-item { flex: 0 0 var(--mode-item-height); position: relative; @@ -415,6 +469,10 @@ width: var(--big-icon); } +body.should-handle-intent-result #gallery-enter { + display: none; +} + .centered-overlay { left: 50%; position: absolute; @@ -495,6 +553,10 @@ background-image: url(../images/camera_button_settings.svg); } +body.should-handle-intent-result #open-settings { + display: none; +} + #camera, #settings, #gridsettings, @@ -606,7 +668,7 @@ } #preview-wrapper, -#preview-video { +.preview-content { flex-shrink: 0; pointer-events: none; position: relative; @@ -625,17 +687,17 @@ width: 0; /* Calculate at runtime. */ } -body.square-preview #preview-video { +body.square-preview .preview-content { left: 0; /* Calculate at runtime. */ position: absolute; top: 0; /* Calculate at runtime. */ } -body.streaming #preview-video { +body.streaming .preview-content { pointer-events: auto; } -body.mirror #preview-video, +body.mirror .preview-content , body.mirror #preview-focus { transform: scaleX(-1); } @@ -1270,6 +1332,6 @@ z-index: 1; } -body:not(.mode-switching):not(.streaming) #spinner { +body:not(.mode-switching):not(.streaming):not(.review-result) #spinner { visibility: visible; }
diff --git a/chrome/browser/resources/chromeos/camera/src/images/camera_intent_play_video.svg b/chrome/browser/resources/chromeos/camera/src/images/camera_intent_play_video.svg new file mode 100644 index 0000000..e57b29fe --- /dev/null +++ b/chrome/browser/resources/chromeos/camera/src/images/camera_intent_play_video.svg
@@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg width="72px" height="72px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + <!-- Generator: Sketch 57.1 (83088) - https://sketch.com --> + <title>btn_play_video_1x</title> + <desc>Created with Sketch.</desc> + <g id="btn_play_video_1x" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> + <g id="camera-button" transform="translate(6.127660, 6.127660)" opacity="0.9"> + <circle id="Oval-3" fill="#FFFFFF" cx="30.0154866" cy="30.0154866" r="30.0154866"></circle> + <circle id="Oval-3" fill-opacity="0.1" fill="#5F6368" cx="30.0154866" cy="30.0154866" r="30.0154866"></circle> + </g> + <g id="dense/av/play" transform="translate(29.412766, 24.817021)" fill="#5F6368"> + <polygon id="↳Color-fill" points="0 22.7345051 19.4867186 11.3672525 0 0"></polygon> + </g> + </g> +</svg> \ No newline at end of file
diff --git a/chrome/browser/resources/chromeos/camera/src/images/camera_intent_result_cancel.svg b/chrome/browser/resources/chromeos/camera/src/images/camera_intent_result_cancel.svg new file mode 100644 index 0000000..367f1617 --- /dev/null +++ b/chrome/browser/resources/chromeos/camera/src/images/camera_intent_result_cancel.svg
@@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg width="72px" height="72px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + <!-- Generator: Sketch 57.1 (83088) - https://sketch.com --> + <title>btn_stop_timer_1x</title> + <desc>Created with Sketch.</desc> + <defs> + <polygon id="path-1" points="44 29.6114286 42.3885714 28 36 34.3885714 29.6114286 28 28 29.6114286 34.3885714 36 28 42.3885714 29.6114286 44 36 37.6114286 42.3885714 44 44 42.3885714 37.6114286 36"></polygon> + </defs> + <g id="btn_stop_timer_1x" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> + <circle id="Oval-3" fill="#BDC1C6" opacity="0.9" cx="36" cy="36" r="30"></circle> + <mask id="mask-2" fill="white"> + <use xlink:href="#path-1"></use> + </mask> + <use id="ic_close_24px" fill="#FFFFFF" fill-rule="nonzero" xlink:href="#path-1"></use> + </g> +</svg> \ No newline at end of file
diff --git a/chrome/browser/resources/chromeos/camera/src/images/camera_intent_result_confirm.svg b/chrome/browser/resources/chromeos/camera/src/images/camera_intent_result_confirm.svg new file mode 100644 index 0000000..ae2c4f9 --- /dev/null +++ b/chrome/browser/resources/chromeos/camera/src/images/camera_intent_result_confirm.svg
@@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg width="72px" height="72px" viewBox="0 0 72 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> + <!-- Generator: Sketch 57.1 (83088) - https://sketch.com --> + <title>btn_confirm_1x</title> + <desc>Created with Sketch.</desc> + <defs> + <polygon id="path-1" points="14.3166667 25.9666667 7.36666667 19.0166667 5 21.3666667 14.3166667 30.6833333 34.3166667 10.6833333 31.9666667 8.33333333"></polygon> + </defs> + <g id="btn_confirm_1x" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> + <g id="camera-button" transform="translate(6.127660, 6.127660)" fill="#1A73E8" opacity="0.9"> + <circle id="Oval-3" cx="30.0154866" cy="30.0154866" r="30.0154866"></circle> + <circle id="Oval-3" cx="30.0154866" cy="30.0154866" r="30.0154866"></circle> + </g> + <g id="ic_check_24px" transform="translate(16.000000, 16.000000)"> + <mask id="mask-2" fill="white"> + <use xlink:href="#path-1"></use> + </mask> + <use fill="#FFFFFF" fill-rule="nonzero" xlink:href="#path-1"></use> + </g> + </g> +</svg> \ No newline at end of file
diff --git a/chrome/browser/resources/chromeos/camera/src/js/device/BUILD.gn b/chrome/browser/resources/chromeos/camera/src/js/device/BUILD.gn index cc01ba9b3..a54df96 100644 --- a/chrome/browser/resources/chromeos/camera/src/js/device/BUILD.gn +++ b/chrome/browser/resources/chromeos/camera/src/js/device/BUILD.gn
@@ -9,6 +9,7 @@ ":camera3_device_info", ":constraints_preferrer", ":device_info_updater", + ":error", ] } @@ -31,6 +32,10 @@ deps = [ ":camera3_device_info", ":constraints_preferrer", + ":error", "..:state", ] } + +js_library("error") { +}
diff --git a/chrome/browser/resources/chromeos/camera/src/js/device/device_info_updater.js b/chrome/browser/resources/chromeos/camera/src/js/device/device_info_updater.js index eb055b6..949bb3f 100644 --- a/chrome/browser/resources/chromeos/camera/src/js/device/device_info_updater.js +++ b/chrome/browser/resources/chromeos/camera/src/js/device/device_info_updater.js
@@ -231,7 +231,7 @@ async getDeviceResolutions(deviceId) { const devices = await this.getCamera3DevicesInfo(); if (!devices) { - throw new Error('HALv1-api'); + throw new cca.device.LegacyVCDError(); } const info = devices.find((info) => info.deviceId === deviceId); return [info.photoResols, info.videoResols];
diff --git a/chrome/browser/resources/chromeos/camera/src/js/device/error.js b/chrome/browser/resources/chromeos/camera/src/js/device/error.js new file mode 100644 index 0000000..3e817dec --- /dev/null +++ b/chrome/browser/resources/chromeos/camera/src/js/device/error.js
@@ -0,0 +1,32 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +'use strict'; + +/** + * Namespace for the Camera app. + */ +var cca = cca || {}; + +/** + * Namespace for device. + */ +cca.device = cca.device || {}; + +/** + * Throws from calls to methods requiring mojo supporting VCD on HALv1 device + * equipped with legacy VCD implementation. + */ +cca.device.LegacyVCDError = class extends Error { + /** + * @param {string=} message + * @public + */ + constructor( + message = + 'Call to unsupported mojo operation on legacy VCD implementation.') { + super(message); + this.name = 'LegacyVCDError'; + } +};
diff --git a/chrome/browser/resources/chromeos/camera/src/js/main.js b/chrome/browser/resources/chromeos/camera/src/js/main.js index 71539b16..af99ae9 100644 --- a/chrome/browser/resources/chromeos/camera/src/js/main.js +++ b/chrome/browser/resources/chromeos/camera/src/js/main.js
@@ -14,6 +14,10 @@ * @constructor */ cca.App = function() { + const shouldHandleIntentResult = + window.intent !== null && window.intent.shouldHandleResult; + cca.state.set('should-handle-intent-result', shouldHandleIntentResult); + /** * @type {cca.models.Gallery} * @private @@ -63,9 +67,13 @@ * @type {cca.views.Camera} * @private */ - this.cameraView_ = new cca.views.Camera( - this.gallery_, this.infoUpdater_, this.photoPreferrer_, - this.videoPreferrer_); + this.cameraView_ = shouldHandleIntentResult ? + new cca.views.CameraIntent( + window.intent, this.infoUpdater_, this.photoPreferrer_, + this.videoPreferrer_) : + new cca.views.Camera( + this.gallery_, this.infoUpdater_, this.photoPreferrer_, + this.videoPreferrer_); // End of properties. Seal the object. Object.seal(this); @@ -110,8 +118,10 @@ cca.proxy.browserProxy.localStorageGet( {expert: false}, ({expert}) => cca.state.set('expert', expert)); document.querySelectorAll('input').forEach((element) => { - element.addEventListener('keypress', (event) => - cca.util.getShortcutIdentifier(event) == 'Enter' && element.click()); + element.addEventListener( + 'keypress', + (event) => cca.util.getShortcutIdentifier(event) == 'Enter' && + element.click()); var css = element.getAttribute('data-state'); var key = element.getAttribute('data-key'); @@ -129,14 +139,15 @@ if (element.type == 'radio' && element.checked) { // Handle unchecked grouped sibling radios. var grouped = `input[type=radio][name=${element.name}]:not(:checked)`; - document.querySelectorAll(grouped).forEach((radio) => - radio.dispatchEvent(new Event('change')) && radio.save()); + document.querySelectorAll(grouped).forEach( + (radio) => + radio.dispatchEvent(new Event('change')) && radio.save()); } } }); element.toggleChecked = (checked) => { element.checked = checked; - element.dispatchEvent(new Event('change')); // Trigger toggling css. + element.dispatchEvent(new Event('change')); // Trigger toggling css. }; element.save = () => { return key && cca.proxy.browserProxy.localStorageSet(payload()); @@ -154,34 +165,38 @@ */ cca.App.prototype.start = function() { var ackMigrate = false; - cca.models.FileSystem.initialize(() => { - // Prompt to migrate pictures if needed. - var message = chrome.i18n.getMessage('migrate_pictures_msg'); - return cca.nav.open('message-dialog', {message, cancellable: false}) - .then((acked) => { - if (!acked) { - throw new Error('no-migrate'); - } - ackMigrate = true; - }); - }).then((external) => { - cca.state.set('ext-fs', external); - this.gallery_.addObserver(this.galleryButton_); - if (!cca.App.useGalleryApp()) { - this.gallery_.addObserver(this.browserView_); - } - this.gallery_.load(); - cca.nav.open('camera'); - }).catch((error) => { - console.error(error); - if (error && error.message == 'no-migrate') { - chrome.app.window.current().close(); - return; - } - cca.nav.open('warning', 'filesystem-failure'); - }).finally(() => { - cca.metrics.log(cca.metrics.Type.LAUNCH, ackMigrate); - }); + cca.models.FileSystem + .initialize(() => { + // Prompt to migrate pictures if needed. + var message = chrome.i18n.getMessage('migrate_pictures_msg'); + return cca.nav.open('message-dialog', {message, cancellable: false}) + .then((acked) => { + if (!acked) { + throw new Error('no-migrate'); + } + ackMigrate = true; + }); + }) + .then((external) => { + cca.state.set('ext-fs', external); + this.gallery_.addObserver(this.galleryButton_); + if (!cca.App.useGalleryApp()) { + this.gallery_.addObserver(this.browserView_); + } + this.gallery_.load(); + cca.nav.open('camera'); + }) + .catch((error) => { + console.error(error); + if (error && error.message == 'no-migrate') { + chrome.app.window.current().close(); + return; + } + cca.nav.open('warning', 'filesystem-failure'); + }) + .finally(() => { + cca.metrics.log(cca.metrics.Type.LAUNCH, ackMigrate); + }); }; /** @@ -190,7 +205,7 @@ * @private */ cca.App.prototype.onKeyPressed_ = function(event) { - cca.tooltip.hide(); // Hide shown tooltip on any keypress. + cca.tooltip.hide(); // Hide shown tooltip on any keypress. cca.nav.onKeyPressed(event); };
diff --git a/chrome/browser/resources/chromeos/camera/src/js/models/BUILD.gn b/chrome/browser/resources/chromeos/camera/src/js/models/BUILD.gn index db0f77e3..39e6022 100644 --- a/chrome/browser/resources/chromeos/camera/src/js/models/BUILD.gn +++ b/chrome/browser/resources/chromeos/camera/src/js/models/BUILD.gn
@@ -33,4 +33,12 @@ } js_library("video_saver") { + sources = [ + "file_video_saver.js", + "intent_video_saver.js", + "video_saver_interface.js", + ] + deps = [ + "..:intent", + ] }
diff --git a/chrome/browser/resources/chromeos/camera/src/js/models/video_saver.js b/chrome/browser/resources/chromeos/camera/src/js/models/file_video_saver.js similarity index 71% rename from chrome/browser/resources/chromeos/camera/src/js/models/video_saver.js rename to chrome/browser/resources/chromeos/camera/src/js/models/file_video_saver.js index d4ade9a..91a724e 100644 --- a/chrome/browser/resources/chromeos/camera/src/js/models/video_saver.js +++ b/chrome/browser/resources/chromeos/camera/src/js/models/file_video_saver.js
@@ -16,8 +16,9 @@ /** * Used to save captured video. + * @implements {cca.models.VideoSaver} */ -cca.models.VideoSaver = class { +cca.models.FileVideoSaver = class { /** * @param {!FileEntry} file * @param {!FileWriter} writer @@ -42,9 +43,7 @@ } /** - * Writes video data to result video. - * @param {!Blob} blob Video data to be written. - * @return {!Promise} + * @override */ async write(blob) { this.curWrite_ = (async () => { @@ -58,8 +57,7 @@ } /** - * Finishes the write of video data parts and returns result video file. - * @return {!Promise<!FileEntry>} Result video file. + * @override */ async endWrite() { await this.curWrite_; @@ -67,14 +65,14 @@ } /** - * Create VideoSaver. - * @param {!FileEntry} file The file which VideoSaver saves the result video - * into. - * @return {!Promise<!cca.models.VideoSaver>} + * Creates FileVideoSaver. + * @param {!FileEntry} file The file which FileVideoSaver saves the result + * video into. + * @return {!Promise<!cca.models.FileVideoSaver>} */ static async create(file) { const writer = await new Promise( (resolve, reject) => file.createWriter(resolve, reject)); - return new cca.models.VideoSaver(file, writer); + return new cca.models.FileVideoSaver(file, writer); } };
diff --git a/chrome/browser/resources/chromeos/camera/src/js/models/filesystem.js b/chrome/browser/resources/chromeos/camera/src/js/models/filesystem.js index eee6686..1db6dde 100644 --- a/chrome/browser/resources/chromeos/camera/src/js/models/filesystem.js +++ b/chrome/browser/resources/chromeos/camera/src/js/models/filesystem.js
@@ -37,6 +37,12 @@ cca.models.FileSystem.internalDir = null; /** + * Temporary directory in the internal file system. + * @type {DirectoryEntry} + */ +cca.models.FileSystem.internalTempDir = null; + +/** * Directory in the external file system. * @type {DirectoryEntry} */ @@ -49,7 +55,21 @@ */ cca.models.FileSystem.initInternalDir_ = function() { return new Promise((resolve, reject) => { - webkitRequestFileSystem(window.PERSISTENT, 768 * 1024 * 1024 /* 768MB */, + webkitRequestFileSystem( + window.PERSISTENT, 768 * 1024 * 1024 /* 768MB */, + (fs) => resolve(fs.root), reject); + }); +}; + +/** + * Initializes the temporary directory in the internal file system. + * @return {!Promise<DirectoryEntry>} Promise for the directory result. + * @private + */ +cca.models.FileSystem.initInternalTempDir_ = function() { + return new Promise((resolve, reject) => { + webkitRequestFileSystem( + window.TEMPORARY, 768 * 1024 * 1024 /* 768MB */, (fs) => resolve(fs.root), reject); }); }; @@ -118,48 +138,59 @@ var doneMigrate = () => chrome.chromeosInfoPrivate && chrome.chromeosInfoPrivate.set('cameraMediaConsolidated', true); - return Promise.all([ - cca.models.FileSystem.initInternalDir_(), - cca.models.FileSystem.initExternalDir_(), - checkAcked, - checkMigrated, - ]).then(([internalDir, externalDir, acked, migrated]) => { - cca.models.FileSystem.internalDir = internalDir; - cca.models.FileSystem.externalDir = externalDir; - if (migrated && !externalDir) { - throw new Error('External file system should be available.'); - } - // Check if acknowledge-prompt and migrate-pictures are needed. - if (migrated || !cca.models.FileSystem.externalDir) { - return [false, false]; - } - // Check if any internal picture other than thumbnail needs migration. - // Pictures taken by old Camera App may not have IMG_ or VID_ prefix. - var dir = cca.models.FileSystem.internalDir; - return cca.models.FileSystem.readDir_(dir).then((entries) => { - return entries.some( - (entry) => !cca.models.FileSystem.hasThumbnailPrefix_(entry)); - }).then((migrateNeeded) => { - if (migrateNeeded) { - return [!acked, true]; - } - // If the external file system is supported and there is already no - // picture in the internal file system, it implies done migration and - // then doesn't need acknowledge-prompt. - ackMigrate(); - doneMigrate(); - return [false, false]; - }); - }).then(([promptNeeded, migrateNeeded]) => { // Prompt to migrate if needed. - return !promptNeeded ? migrateNeeded : promptMigrate().then(() => { - ackMigrate(); - return migrateNeeded; - }); - }).then((migrateNeeded) => { // Migrate pictures if needed. - const external = cca.models.FileSystem.externalDir != null; - return !migrateNeeded ? external : cca.models.FileSystem.migratePictures() - .then(doneMigrate).then(() => external); - }); + return Promise + .all([ + cca.models.FileSystem.initInternalDir_(), + cca.models.FileSystem.initInternalTempDir_(), + cca.models.FileSystem.initExternalDir_(), + checkAcked, + checkMigrated, + ]) + .then(([internalDir, internalTempDir, externalDir, acked, migrated]) => { + cca.models.FileSystem.internalDir = internalDir; + cca.models.FileSystem.internalTempDir = internalTempDir; + cca.models.FileSystem.externalDir = externalDir; + if (migrated && !externalDir) { + throw new Error('External file system should be available.'); + } + // Check if acknowledge-prompt and migrate-pictures are needed. + if (migrated || !cca.models.FileSystem.externalDir) { + return [false, false]; + } + // Check if any internal picture other than thumbnail needs migration. + // Pictures taken by old Camera App may not have IMG_ or VID_ prefix. + var dir = cca.models.FileSystem.internalDir; + return cca.models.FileSystem.readDir_(dir) + .then((entries) => { + return entries.some( + (entry) => !cca.models.FileSystem.hasThumbnailPrefix_(entry)); + }) + .then((migrateNeeded) => { + if (migrateNeeded) { + return [!acked, true]; + } + // If the external file system is supported and there is already + // no picture in the internal file system, it implies done + // migration and then doesn't need acknowledge-prompt. + ackMigrate(); + doneMigrate(); + return [false, false]; + }); + }) + .then( + ([promptNeeded, migrateNeeded]) => { // Prompt to migrate if needed. + return !promptNeeded ? migrateNeeded : promptMigrate().then(() => { + ackMigrate(); + return migrateNeeded; + }); + }) + .then((migrateNeeded) => { // Migrate pictures if needed. + const external = cca.models.FileSystem.externalDir != null; + return !migrateNeeded ? external : + cca.models.FileSystem.migratePictures() + .then(doneMigrate) + .then(() => external); + }); }; /** @@ -303,6 +334,26 @@ }; /** + * @const {string} + */ +cca.models.FileSystem.PRIVATE_TEMPFILE_NAME = 'video-intent.mkv'; + +/** + * @return {!Promise<!FileEntry>} Newly created temporary file. + * @throws {Error} If failed to create video temp file. + */ +cca.models.FileSystem.createPrivateTempVideoFile = async function() { + // TODO(inker): Handles running out of space case. + const dir = cca.models.FileSystem.internalTempDir; + const file = await cca.models.FileSystem.getFile( + dir, cca.models.FileSystem.PRIVATE_TEMPFILE_NAME, true); + if (file === null) { + throw new Error('Failed to create private video temp file.'); + } + return file; +}; + +/** * Saves temporary video file to predefined default location. * @param {FileEntry} tempfile Temporary video file to be saved. * @param {string} filename Filename to be saved.
diff --git a/chrome/browser/resources/chromeos/camera/src/js/models/gallery.js b/chrome/browser/resources/chromeos/camera/src/js/models/gallery.js index 8edc3e0..d3ac1a7d 100644 --- a/chrome/browser/resources/chromeos/camera/src/js/models/gallery.js +++ b/chrome/browser/resources/chromeos/camera/src/js/models/gallery.js
@@ -88,11 +88,15 @@ var name = cca.models.FileSystem.regulatePictureName(pictureEntry); // Match numeric parts from filenames, e.g. IMG_'yyyyMMdd_HHmmss (n)'.jpg. // Assume no more than one picture taken within one millisecond. + // Use String.raw instead of /...regex.../ here to avoid breaking syntax + // highlight on gerrit. var match = name.match( - /_(\d{4})(\d{2})(\d{2})_(\d{2})(\d{2})(\d{2})(?: \((\d+)\))?/); - return match ? new Date(num(match[1]), num(match[2]) - 1, num(match[3]), - num(match[4]), num(match[5]), num(match[6]), - match[7] ? num(match[7]) : 0) : new Date(0); + String.raw`_(\d{4})(\d{2})(\d{2})_(\d{2})(\d{2})(\d{2})(?: \((\d+)\))?/`); + return match ? + new Date( + num(match[1]), num(match[2]) - 1, num(match[3]), num(match[4]), + num(match[5]), num(match[6]), match[7] ? num(match[7]) : 0) : + new Date(0); }; cca.models.Gallery.Picture.prototype = { @@ -313,7 +317,7 @@ */ cca.models.Gallery.prototype.startSaveVideo = async function() { const tempFile = await cca.models.FileSystem.createTempVideoFile(); - return cca.models.VideoSaver.create(tempFile); + return cca.models.FileVideoSaver.create(tempFile); }; /**
diff --git a/chrome/browser/resources/chromeos/camera/src/js/models/intent_video_saver.js b/chrome/browser/resources/chromeos/camera/src/js/models/intent_video_saver.js new file mode 100644 index 0000000..d7c5e91 --- /dev/null +++ b/chrome/browser/resources/chromeos/camera/src/js/models/intent_video_saver.js
@@ -0,0 +1,67 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +'use strict'; + +/** + * Namespace for the Camera app. + */ +var cca = cca || {}; + +/** + * Namespace for models. + */ +cca.models = cca.models || {}; + +/** + * Used to save captured video into a preview file and forward to intent result. + * @implements {cca.models.VideoSaver} + */ +cca.models.IntentVideoSaver = class { + /** + * @param {!cca.intent.Intent} intent + * @param {!cca.models.FileVideoSaver} fileSaver + * @private + */ + constructor(intent, fileSaver) { + /** + * @const {!cca.intent.Intent} intent + * @private + */ + this.intent_ = intent; + + /** + * @const {!cca.models.FileVideoSaver} + * @private + */ + this.fileSaver_ = fileSaver; + } + + /** + * @override + */ + async write(blob) { + await this.fileSaver_.write(blob); + const arrayBuffer = await blob.arrayBuffer(); + this.intent_.appendData(new Uint8Array(arrayBuffer)); + } + + /** + * @override + */ + async endWrite() { + return this.fileSaver_.endWrite(); + } + + /** + * Creates IntentVideoSaver. + * @param {!cca.intent.Intent} intent + * @return {!Promise<!cca.models.IntentVideoSaver>} + */ + static async create(intent) { + const tmpFile = await cca.models.FileSystem.createPrivateTempVideoFile(); + const fileSaver = await cca.models.FileVideoSaver.create(tmpFile); + return new cca.models.IntentVideoSaver(intent, fileSaver); + } +};
diff --git a/chrome/browser/resources/chromeos/camera/src/js/models/video_saver_interface.js b/chrome/browser/resources/chromeos/camera/src/js/models/video_saver_interface.js new file mode 100644 index 0000000..7b56956 --- /dev/null +++ b/chrome/browser/resources/chromeos/camera/src/js/models/video_saver_interface.js
@@ -0,0 +1,34 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +'use strict'; + +/** + * Namespace for the Camera app. + */ +var cca = cca || {}; + +/** + * Namespace for models. + */ +cca.models = cca.models || {}; + +/** + * Used to save captured video. + * @interface + */ +cca.models.VideoSaver = class { + /** + * Writes video data to result video. + * @param {!Blob} blob Video data to be written. + * @return {!Promise} + */ + async write(blob) {} + + /** + * Finishes the write of video data parts and returns result video file. + * @return {!Promise<!FileEntry>} Result video file. + */ + async endWrite() {} +};
diff --git a/chrome/browser/resources/chromeos/camera/src/js/views/BUILD.gn b/chrome/browser/resources/chromeos/camera/src/js/views/BUILD.gn index 84560373..d52516bb 100644 --- a/chrome/browser/resources/chromeos/camera/src/js/views/BUILD.gn +++ b/chrome/browser/resources/chromeos/camera/src/js/views/BUILD.gn
@@ -7,6 +7,7 @@ group("closure_compile") { deps = [ ":compile_resources", + "camera:compile_resources", ] }
diff --git a/chrome/browser/resources/chromeos/camera/src/js/views/camera.js b/chrome/browser/resources/chromeos/camera/src/js/views/camera.js index bee9b77a..d21b3f9 100644 --- a/chrome/browser/resources/chromeos/camera/src/js/views/camera.js +++ b/chrome/browser/resources/chromeos/camera/src/js/views/camera.js
@@ -129,13 +129,10 @@ /** * Promise for the current take of photo or recording. * @type {?Promise} - * @private + * @protected */ this.take_ = null; - // End of properties, seal the object. - Object.seal(this); - document.querySelectorAll('#start-takephoto, #start-recordvideo') .forEach((btn) => btn.addEventListener('click', () => this.beginTake_())); @@ -189,11 +186,13 @@ /** * Begins to take photo or recording with the current options, e.g. timer. - * @private + * @return {?Promise} Promise resolved when take action completes. Returns null + * if CCA can't start take action. + * @protected */ cca.views.Camera.prototype.beginTake_ = function() { if (!cca.state.get('streaming') || cca.state.get('taking')) { - return; + return null; } cca.state.set('taking', true); @@ -214,6 +213,7 @@ this.focus(); // Refocus the visible shutter button for ChromeVox. } })(); + return this.take_; }; /** @@ -273,69 +273,70 @@ }; /** - * Try start stream reconfiguration with specified device id. - * @async + * Try start stream reconfiguration with specified mode and device id. * @param {?string} deviceId - * @return {boolean} If found suitable stream and reconfigure successfully. + * @param {string} mode + * @return {!Promise<boolean>} If found suitable stream and reconfigure + * successfully. */ -cca.views.Camera.prototype.startWithDevice_ = async function(deviceId) { - let supportedModes = null; - for (const mode of this.modes_.getModeCandidates()) { - try { - if (!deviceId) { - // Null for requesting default camera on HALv1. - throw new Error('HALv1-api'); - } +cca.views.Camera.prototype.startWithMode_ = async function(deviceId, mode) { + const deviceOperator = await cca.mojo.DeviceOperator.getInstance(); + let resolCandidates = null; + if (deviceOperator !== null) { + if (deviceId !== null) { const previewRs = (await this.infoUpdater_.getDeviceResolutions(deviceId))[1]; - var resolCandidates = + resolCandidates = this.modes_.getResolutionCandidates(mode, deviceId, previewRs); - } catch (e) { - // Assume the exception here is thrown from error of HALv1 not support - // resolution query, fallback to use v1 constraints-candidates. - if (e.message == 'HALv1-api') { - resolCandidates = - await this.modes_.getResolutionCandidatesV1(mode, deviceId); - } else { - throw e; + } else { + console.error('Null device id present on HALv3 device. Fallback to v1.'); + } + } + if (resolCandidates === null) { + resolCandidates = + await this.modes_.getResolutionCandidatesV1(mode, deviceId); + } + for (const [captureResolution, previewCandidates] of resolCandidates) { + for (const constraints of previewCandidates) { + if (this.suspended) { + throw new cca.views.CameraSuspendedError(); + } + try { + if (deviceOperator !== null) { + await deviceOperator.setFpsRange(deviceId, constraints); + await deviceOperator.setCaptureIntent( + deviceId, this.modes_.getCaptureIntent(mode)); + } + const stream = await navigator.mediaDevices.getUserMedia(constraints); + await this.preview_.start(stream); + this.facingMode_ = + await this.options_.updateValues(constraints, stream); + await this.modes_.updateModeSelectionUI(deviceId); + await this.modes_.updateMode(mode, stream, deviceId, captureResolution); + cca.nav.close('warning', 'no-camera'); + return true; + } catch (e) { + this.preview_.stop(); + console.error(e); } } - for (const [captureResolution, previewCandidates] of resolCandidates) { - if (supportedModes && !supportedModes.includes(mode)) { - break; - } - for (const constraints of previewCandidates) { - if (this.suspended) { - throw new cca.views.CameraSuspendedError(); - } - try { - const deviceOperator = await cca.mojo.DeviceOperator.getInstance(); - if (deviceOperator) { - await deviceOperator.setFpsRange(deviceId, constraints); - await deviceOperator.setCaptureIntent( - deviceId, this.modes_.getCaptureIntent(mode)); - } - const stream = await navigator.mediaDevices.getUserMedia(constraints); - if (!supportedModes) { - supportedModes = await this.modes_.getSupportedModes(stream); - if (!supportedModes.includes(mode)) { - stream.getTracks()[0].stop(); - break; - } - } - await this.preview_.start(stream); - this.facingMode_ = - await this.options_.updateValues(constraints, stream); - await this.modes_.updateModeSelectionUI(supportedModes); - await this.modes_.updateMode( - mode, stream, deviceId, captureResolution); - cca.nav.close('warning', 'no-camera'); - return true; - } catch (e) { - this.preview_.stop(); - console.error(e); - } - } + } + return false; +}; + +/** + * Try start stream reconfiguration with specified device id. + * @param {?string} deviceId + * @return {!Promise<boolean>} If found suitable stream and reconfigure + * successfully. + */ +cca.views.Camera.prototype.startWithDevice_ = async function(deviceId) { + const supportedModes = await this.modes_.getSupportedModes(deviceId); + const modes = + this.modes_.getModeCandidates().filter((m) => supportedModes.includes(m)); + for (const mode of modes) { + if (await this.startWithMode_(deviceId, mode)) { + return true; } } return false;
diff --git a/chrome/browser/resources/chromeos/camera/src/js/views/camera/BUILD.gn b/chrome/browser/resources/chromeos/camera/src/js/views/camera/BUILD.gn new file mode 100644 index 0000000..38a22c26 --- /dev/null +++ b/chrome/browser/resources/chromeos/camera/src/js/views/camera/BUILD.gn
@@ -0,0 +1,24 @@ +# Copyright 2019 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//third_party/closure_compiler/compile_js.gni") + +group("closure_compile") { + deps = [ + ":compile_resources", + ] +} + +js_type_check("compile_resources") { + deps = [ + ":review_result", + ] +} + +js_library("review_result") { + deps = [ + "../..:state", + "../..:util", + ] +}
diff --git a/chrome/browser/resources/chromeos/camera/src/js/views/camera/layout.js b/chrome/browser/resources/chromeos/camera/src/js/views/camera/layout.js index 43afec7..f6fd00d 100644 --- a/chrome/browser/resources/chromeos/camera/src/js/views/camera/layout.js +++ b/chrome/browser/resources/chromeos/camera/src/js/views/camera/layout.js
@@ -38,7 +38,14 @@ * @private */ this.squareVideo_ = - cca.views.camera.Layout.cssStyle_('body.square-preview #preview-video'); + cca.views.camera.Layout.cssStyle_('body.square-preview .preview-content'); + + /** + * CSS style of what is currently put as camera preview. + * @type {CSSStyleDeclaration} + * @private + */ + this.previewContent_ = cca.views.camera.Layout.cssStyle_('.preview-content'); // End of properties, seal the object. Object.seal(this); @@ -73,7 +80,9 @@ // it may fill up the window or be letterboxed when fullscreen/maximized. // Don't use app-window.innerBounds' width/height properties during resizing // as they are not updated immediately. - var video = document.querySelector('#preview-video'); + const video = document.querySelector('#preview-video'); + let contentWidth = 0; + let contentHeight = 0; if (video.videoHeight) { var scale = cca.state.get('square-mode') ? Math.min(window.innerHeight, window.innerWidth) / @@ -81,15 +90,19 @@ Math.min( window.innerHeight / video.videoHeight, window.innerWidth / video.videoWidth); - video.width = scale * video.videoWidth; - video.height = scale * video.videoHeight; + contentWidth = scale * video.videoWidth; + contentHeight = scale * video.videoHeight; + this.previewContent_.setProperty('width', `${contentWidth}px`); + this.previewContent_.setProperty('height', `${contentHeight}px`); } - var [viewportW, viewportH] = [video.width, video.height]; + var [viewportW, viewportH] = [contentWidth, contentHeight]; cca.state.set('square-preview', cca.state.get('square-mode')); if (cca.state.get('square-mode')) { - viewportW = viewportH = Math.min(video.width, video.height); - this.squareVideo_.setProperty('left', `${(viewportW - video.width) / 2}px`); - this.squareVideo_.setProperty('top', `${(viewportH - video.height) / 2}px`); + viewportW = viewportH = Math.min(contentWidth, contentHeight); + this.squareVideo_.setProperty( + 'left', `${(viewportW - contentWidth) / 2}px`); + this.squareVideo_.setProperty( + 'top', `${(viewportH - contentHeight) / 2}px`); this.squareViewport_.setProperty('width', `${viewportW}px`); this.squareViewport_.setProperty('height', `${viewportH}px`); }
diff --git a/chrome/browser/resources/chromeos/camera/src/js/views/camera/modes.js b/chrome/browser/resources/chromeos/camera/src/js/views/camera/modes.js index 43693e5..59ddb8a 100644 --- a/chrome/browser/resources/chromeos/camera/src/js/views/camera/modes.js +++ b/chrome/browser/resources/chromeos/camera/src/js/views/camera/modes.js
@@ -162,12 +162,14 @@ 'portrait-mode': { captureFactory: () => new cca.views.camera.Portrait( this.stream_, doSavePhoto, this.captureResolution_), - isSupported: async (stream) => { - const deviceOperator = await cca.mojo.DeviceOperator.getInstance(); - if (!deviceOperator) { + isSupported: async (deviceId) => { + if (deviceId === null) { return false; } - const deviceId = stream.getVideoTracks()[0].getSettings().deviceId; + const deviceOperator = await cca.mojo.DeviceOperator.getInstance(); + if (deviceOperator === null) { + return false; + } return await deviceOperator.isPortraitModeSupported(deviceId); }, resolutionConfig: photoResolPreferrer, @@ -273,7 +275,7 @@ /** * Gets all mode candidates. Desired trying sequence of candidate modes is * reflected in the order of the returned array. - * @return {Array<string>} Mode candidates to be tried out. + * @return {!Array<string>} Mode candidates to be tried out. */ cca.views.camera.Modes.prototype.getModeCandidates = function() { const tried = {}; @@ -326,14 +328,15 @@ }; /** - * Gets supported modes for video device of the given stream. - * @param {MediaStream} stream Stream of the video device. - * @return {Array<string>} Names of all supported mode for the video device. + * Gets supported modes for video device of given device id. + * @param {?string} deviceId Device id of the video device. + * @return {!Promise<!Array<cca.views.camera.Mode>>} All supported mode for the + * video device. */ -cca.views.camera.Modes.prototype.getSupportedModes = async function(stream) { +cca.views.camera.Modes.prototype.getSupportedModes = async function(deviceId) { let supportedModes = []; for (const [mode, obj] of Object.entries(this.allModes_)) { - if (await obj.isSupported(stream)) { + if (await obj.isSupported(deviceId)) { supportedModes.push(mode); } } @@ -341,12 +344,13 @@ }; /** - * Updates mode selection UI according to given supported modes. - * @param {Array<string>} supportedModes Supported mode names to be updated - * with. + * Updates mode selection UI according to given device id. + * @param {?string} deviceId + * @return {!Promise} */ -cca.views.camera.Modes.prototype.updateModeSelectionUI = function( - supportedModes) { +cca.views.camera.Modes.prototype.updateModeSelectionUI = + async function(deviceId) { + const supportedModes = await this.getSupportedModes(deviceId); document.querySelectorAll('.mode-item').forEach((element) => { const radio = element.querySelector('input[type=radio]'); element.classList.toggle(
diff --git a/chrome/browser/resources/chromeos/camera/src/js/views/camera/preview.js b/chrome/browser/resources/chromeos/camera/src/js/views/camera/preview.js index d0317660..da40f94 100644 --- a/chrome/browser/resources/chromeos/camera/src/js/views/camera/preview.js +++ b/chrome/browser/resources/chromeos/camera/src/js/views/camera/preview.js
@@ -123,6 +123,7 @@ cca.views.camera.Preview.prototype.setSource_ = function(stream) { var video = document.createElement('video'); video.id = 'preview-video'; + video.classList = this.video_.classList; video.muted = true; // Mute to avoid echo from the captured audio. return new Promise((resolve) => { var handler = () => { @@ -187,27 +188,6 @@ }; /** - * Creates an image blob of the current frame. - * @return {!Promise<Blob>} Promise for the result. - */ -cca.views.camera.Preview.prototype.toImage = function() { - var canvas = document.createElement('canvas'); - var ctx = canvas.getContext('2d'); - canvas.width = this.video_.videoWidth; - canvas.height = this.video_.videoHeight; - ctx.drawImage(this.video_, 0, 0); - return new Promise((resolve, reject) => { - canvas.toBlob((blob) => { - if (blob) { - resolve(blob); - } else { - reject(new Error('Photo blob error.')); - } - }, 'image/jpeg'); - }); -}; - -/** * Checks preview whether to show preview metadata or not. * @private */
diff --git a/chrome/browser/resources/chromeos/camera/src/js/views/camera/review_result.js b/chrome/browser/resources/chromeos/camera/src/js/views/camera/review_result.js new file mode 100644 index 0000000..64b6412 --- /dev/null +++ b/chrome/browser/resources/chromeos/camera/src/js/views/camera/review_result.js
@@ -0,0 +1,126 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +'use strict'; + +/** + * Namespace for the Camera app. + */ +var cca = cca || {}; + +/** + * Namespace for views. + */ +cca.views = cca.views || {}; + +/** + * Namespace for Camera view. + */ +cca.views.camera = cca.views.camera || {}; + +/** + * Creates a controller for reviewing intent result in Camera view. + */ +cca.views.camera.ReviewResult = class { + /** + * @public + */ + constructor() { + /** + * @const {!HTMLImageElement} + * @private + */ + this.reviewPhotoResult_ = /** @type {!HTMLImageElement} */ ( + document.querySelector('#review-photo-result')); + + /** + * @const {!HTMLVideoElement} + * @private + */ + this.reviewVideoResult_ = /** @type {!HTMLVideoElement} */ ( + document.querySelector('#review-video-result')); + + /** + * Function resolving open result call called with whether user confirms + * after reviewing intent result. + * @type {?function(boolean)} + * @private + */ + this.resolveOpen_ = null; + + this.reviewVideoResult_.onended = () => { + this.reviewVideoResult_.currentTime = 0; + cca.state.set('playing-result-video', false); + }; + + const addClickListener = (selector, handler) => + document.querySelector(selector).addEventListener('click', handler); + addClickListener('#confirm-result', () => this.close_(true)); + addClickListener('#cancel-result', () => this.close_(false)); + addClickListener('#play-result-video', () => this.playResultVideo_()); + } + + /** + * Starts playing result video. + * @private + */ + playResultVideo_() { + if (cca.state.get('playing-result-video')) { + return; + } + cca.state.set('playing-result-video', true); + this.reviewVideoResult_.play(); + } + + /** + * Closes review result UI and resolves its open promise with whether user + * confirms after reviewing the result. + * @param {boolean} confirmed + * @private + */ + close_(confirmed) { + if (this.resolveOpen_ === null) { + console.error('Close review result with no unresolved open.'); + return; + } + const resolve = this.resolveOpen_; + this.resolveOpen_ = null; + cca.state.set('review-result', false); + cca.state.set('playing-result-video', false); + this.reviewPhotoResult_.src = ''; + this.reviewVideoResult_.src = ''; + resolve(confirmed); + } + + /** + * Opens photo result blob and shows photo on review result UI. + * @param {!Blob} blob Photo result blob. + * @return {!Promise<boolean>} Promise resolved with whether user confirms + * with the photo result. + */ + async openPhoto(blob) { + const img = await cca.util.blobToImage(blob); + this.reviewPhotoResult_.src = img.src; + cca.state.set('review-photo-result', true); + cca.state.set('review-result', true); + return new Promise((resolve) => { + this.resolveOpen_ = resolve; + }); + } + + /** + * Opens video result file and shows video on review result UI. + * @param {!FileEntry} fileEntry Video result file. + * @return {!Promise<boolean>} Promise resolved with whether user confirms + * with the video result. + */ + async openVideo(fileEntry) { + this.reviewVideoResult_.src = fileEntry.toURL(); + cca.state.set('review-photo-result', false); + cca.state.set('review-result', true); + return new Promise((resolve) => { + this.resolveOpen_ = resolve; + }); + } +};
diff --git a/chrome/browser/resources/chromeos/camera/src/js/views/camera_intent.js b/chrome/browser/resources/chromeos/camera/src/js/views/camera_intent.js new file mode 100644 index 0000000..bb3baca4 --- /dev/null +++ b/chrome/browser/resources/chromeos/camera/src/js/views/camera_intent.js
@@ -0,0 +1,112 @@ +// Copyright (c) 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +'use strict'; + +/** + * Namespace for the Camera app. + */ +var cca = cca || {}; + +/** + * Namespace for views. + */ +cca.views = cca.views || {}; + +/** + * Creates the camera-intent-view controller. + */ +cca.views.CameraIntent = class extends cca.views.Camera { + /** + * @param {!cca.intent.Intent} intent + * @param {!cca.device.DeviceInfoUpdater} infoUpdater + * @param {!cca.device.PhotoResolPreferrer} photoPreferrer + * @param {!cca.device.VideoConstraintsPreferrer} videoPreferrer + */ + constructor(intent, infoUpdater, photoPreferrer, videoPreferrer) { + const resultSaver = { + savePhoto: async (blob) => { + this.photoResult_ = blob; + const buf = await blob.arrayBuffer(); + await this.intent_.appendData(new Uint8Array(buf)); + }, + startSaveVideo: async () => { + return await cca.models.IntentVideoSaver.create(intent); + }, + finishSaveVideo: async (video, savedName) => { + this.videoResult_ = await video.endWrite(); + }, + }; + super(resultSaver, infoUpdater, photoPreferrer, videoPreferrer); + + /** + * @type {!cca.intent.Intent} + * @private + */ + this.intent_ = intent; + + /** + * @type {?Blob} + * @private + */ + this.photoResult_ = null; + + /** + * @type {?FileEntry} + * @private + */ + this.videoResult_ = null; + + /** + * @type {!cca.views.camera.ReviewResult} + * @private + */ + this.reviewResult_ = new cca.views.camera.ReviewResult(); + } + + /** + * @override + */ + beginTake_() { + if (this.photoResult_ !== null) { + URL.revokeObjectURL(this.photoResult_); + } + this.photoResult_ = null; + this.videoResult_ = null; + + const take = super.beginTake_(); + if (take === null) { + return null; + } + return (async () => { + await take; + + if (this.photoResult_ === null && this.videoResult_ === null) { + console.warn('End take without intent result.'); + return; + } + cca.state.set('suspend', true); + await this.restart(); + const confirmed = await ( + this.photoResult_ !== null ? + this.reviewResult_.openPhoto(this.photoResult_) : + this.reviewResult_.openVideo(this.videoResult_)); + if (confirmed) { + await this.intent_.finish(); + window.close(); + return; + } + cca.state.set('suspend', false); + await this.intent_.clearData(); + await this.restart(); + })(); + } + + /** + * @override + */ + async startWithDevice_(deviceId) { + return this.startWithMode_(deviceId, this.defaultMode); + } +};
diff --git a/chrome/browser/resources/chromeos/camera/src/views/main.html b/chrome/browser/resources/chromeos/camera/src/views/main.html index fc78682..3f36521 100644 --- a/chrome/browser/resources/chromeos/camera/src/views/main.html +++ b/chrome/browser/resources/chromeos/camera/src/views/main.html
@@ -21,6 +21,7 @@ <script src="../js/sound.js"></script> <script src="../js/scrollbar.js"></script> <script src="../js/gallerybutton.js"></script> + <script src="../js/device/error.js"></script> <script src="../js/device/camera3_device_info.js"></script> <script src="../js/device/constraints_preferrer.js"></script> <script src="../js/device/device_info_updater.js"></script> @@ -28,7 +29,9 @@ <script src="../js/models/gallery.js"></script> <script src="../js/models/filesystem.js"></script> <script src="../js/models/result_saver.js"></script> - <script src="../js/models/video_saver.js"></script> + <script src="../js/models/video_saver_interface.js"></script> + <script src="../js/models/file_video_saver.js"></script> + <script src="../js/models/intent_video_saver.js"></script> <script src="../js/mojo/mojo_bindings_lite.js"></script> <script src="../js/mojo/camera_metadata_tags.mojom-lite.js"></script> <script src="../js/mojo/camera_metadata.mojom-lite.js"></script> @@ -45,10 +48,12 @@ <script src="../js/views/view.js"></script> <script src="../js/views/gallery_base.js"></script> <script src="../js/views/camera.js"></script> + <script src="../js/views/camera_intent.js"></script> <script src="../js/views/camera/layout.js"></script> <script src="../js/views/camera/options.js"></script> <script src="../js/views/camera/preview.js"></script> <script src="../js/views/camera/recordtime.js"></script> + <script src="../js/views/camera/review_result.js"></script> <script src="../js/views/camera/timertick.js"></script> <script src="../js/views/camera/modes.js"></script> <script src="../js/views/dialog.js"></script> @@ -61,7 +66,12 @@ <body class="sound mirror mic _3x3"> <div id="camera"> <div id="preview-wrapper" aria-hidden="true"> - <video id="preview-video"></video> + <img id="review-photo-result" class="preview-content"> + <video id="review-video-result" class="preview-content"></video> + <div class="buttons circle centered-overlay"> + <button id="play-result-video"></button> + </div> + <video id="preview-video" class="preview-content"></video> <div id="preview-metadata"> <div id="preview-stat" class="metadata-row mode-on"> <span class="metadata-category">Stat</span> @@ -140,6 +150,10 @@ <button id="gallery-enter" tabindex="0" i18n-label="gallery_button" hidden></button> </div> + <div id="confirm-result-groups" class="buttons right-stripe circle"> + <button id="confirm-result"></button> + <button id="cancel-result"></button> + </div> <div class="bottom-stripe left-stripe buttons circle"> <button id="switch-device" tabindex="0" i18n-label="switch_camera_button"></button>
diff --git a/chrome/browser/resources/chromeos/login/md_screen_container.css b/chrome/browser/resources/chromeos/login/md_screen_container.css index 1d559ae..2c6f3493 100644 --- a/chrome/browser/resources/chromeos/login/md_screen_container.css +++ b/chrome/browser/resources/chromeos/login/md_screen_container.css
@@ -14,8 +14,6 @@ display: flex; justify-content: center; left: 0; - /* This enables scrolling. Min resolution: 1024x768 */ - min-height: calc(768px - var(--shelf-area-height)); perspective: 1px; /* Workaround, see http://crbug.com/360567 */ position: absolute; right: 0;
diff --git a/chrome/browser/resources/print_preview/ui/pin_settings.js b/chrome/browser/resources/print_preview/ui/pin_settings.js index e6c1c40..8e56e89 100644 --- a/chrome/browser/resources/print_preview/ui/pin_settings.js +++ b/chrome/browser/resources/print_preview/ui/pin_settings.js
@@ -66,7 +66,7 @@ /** @private */ onCollapseChanged_: function() { if (this.pinEnabled_) { - /** @type {!CrInputElement} */ (this.$.pinValue).inputElement.focus(); + /** @type {!CrInputElement} */ (this.$.pinValue).focusInput(); } },
diff --git a/chrome/browser/resources/settings/people_page/sync_page.js b/chrome/browser/resources/settings/people_page/sync_page.js index fd28dad6..e65b7ef 100644 --- a/chrome/browser/resources/settings/people_page/sync_page.js +++ b/chrome/browser/resources/settings/people_page/sync_page.js
@@ -506,7 +506,10 @@ case settings.PageStatus.PASSPHRASE_FAILED: if (this.pageStatus_ == this.pages_.CONFIGURE && this.syncPrefs && this.syncPrefs.passphraseRequired) { - this.$$('#existingPassphraseInput').invalid = true; + const passphraseInput = /** @type {!CrInputElement} */ ( + this.$$('#existingPassphraseInput')); + passphraseInput.invalid = true; + passphraseInput.focusInput(); } return; }
diff --git a/chrome/browser/resources/settings/site_settings/site_data.html b/chrome/browser/resources/settings/site_settings/site_data.html index 9fc485d..f1a028c9 100644 --- a/chrome/browser/resources/settings/site_settings/site_data.html +++ b/chrome/browser/resources/settings/site_settings/site_data.html
@@ -44,7 +44,7 @@ </cr-button> <cr-button disabled$="[[isLoading_]]" id="removeThirdPartyCookies" on-click="onRemoveThirdPartyCookiesTap_" - hidden$="[[!enableRemovingAllThirdPartyCookies_]]"> + hidden$="[[!showRemoveThirdPartyCookies_(sites.length, filter)]]"> $i18n{siteSettingsCookieRemoveAllThirdParty} </cr-button> </div>
diff --git a/chrome/browser/resources/settings/site_settings/site_data.js b/chrome/browser/resources/settings/site_settings/site_data.js index 644aedc4..cd6370aa 100644 --- a/chrome/browser/resources/settings/site_settings/site_data.js +++ b/chrome/browser/resources/settings/site_settings/site_data.js
@@ -75,15 +75,6 @@ /** @private */ listBlurred_: Boolean, - - /** @private */ - enableRemovingAllThirdPartyCookies_: { - type: Boolean, - value: function() { - return loadTimeData.getBoolean('enableRemovingAllThirdPartyCookies') && - (this.sites.length > 0); - } - }, }, /** @private {settings.LocalDataBrowserProxy} */ @@ -287,4 +278,13 @@ new URLSearchParams('site=' + event.model.item.site)); this.lastSelected_ = event.model; }, + + /** + * @private + * @return {boolean} + */ + showRemoveThirdPartyCookies_: function() { + return loadTimeData.getBoolean('enableRemovingAllThirdPartyCookies') && + this.sites.length > 0 && this.filter.length == 0; + }, });
diff --git a/chrome/browser/sharing/sharing_sync_preference.cc b/chrome/browser/sharing/sharing_sync_preference.cc index cf535b02..0b6d3d9 100644 --- a/chrome/browser/sharing/sharing_sync_preference.cc +++ b/chrome/browser/sharing/sharing_sync_preference.cc
@@ -22,7 +22,6 @@ const char kDeviceP256dh[] = "device_p256dh"; const char kDeviceAuthSecret[] = "device_auth_secret"; const char kDeviceCapabilities[] = "device_capabilities"; -const char kDeviceLastUpdated[] = "device_last_updated"; const char kRegistrationAuthorizedEntity[] = "registration_authorized_entity"; const char kRegistrationFcmToken[] = "registration_fcm_token"; @@ -162,16 +161,12 @@ void SharingSyncPreference::SetSyncDevice(const std::string& guid, const Device& device) { DictionaryPrefUpdate update(prefs_, prefs::kSharingSyncedDevices); - update->SetKey(guid, DeviceToValue(device, base::Time::Now())); + update->SetKey(guid, DeviceToValue(device)); } void SharingSyncPreference::RemoveDevice(const std::string& guid) { DictionaryPrefUpdate update(prefs_, prefs::kSharingSyncedDevices); - // Clear all values of device with |guid| by setting its value to an empty - // entry that only contains a timestamp so other devices can merge it. - base::Value cleared(base::Value::Type::DICTIONARY); - cleared.SetKey(kDeviceLastUpdated, base::CreateTimeValue(base::Time::Now())); - update->SetKey(guid, std::move(cleared)); + update->RemoveKey(guid); } base::Optional<SharingSyncPreference::FCMRegistration> @@ -226,8 +221,7 @@ } // static -base::Value SharingSyncPreference::DeviceToValue(const Device& device, - base::Time timestamp) { +base::Value SharingSyncPreference::DeviceToValue(const Device& device) { std::string base64_p256dh, base64_auth_secret; base::Base64Encode(device.p256dh, &base64_p256dh); base::Base64Encode(device.auth_secret, &base64_auth_secret); @@ -243,7 +237,6 @@ result.SetStringKey(kDeviceP256dh, base64_p256dh); result.SetStringKey(kDeviceAuthSecret, base64_auth_secret); result.SetIntKey(kDeviceCapabilities, capabilities); - result.SetKey(kDeviceLastUpdated, base::CreateTimeValue(timestamp)); return result; }
diff --git a/chrome/browser/sharing/sharing_sync_preference.h b/chrome/browser/sharing/sharing_sync_preference.h index 01fbbdef..9240fb76 100644 --- a/chrome/browser/sharing/sharing_sync_preference.h +++ b/chrome/browser/sharing/sharing_sync_preference.h
@@ -125,7 +125,7 @@ private: friend class SharingSyncPreferenceTest; - static base::Value DeviceToValue(const Device& device, base::Time timestamp); + static base::Value DeviceToValue(const Device& device); static base::Optional<Device> ValueToDevice(const base::Value& value);
diff --git a/chrome/browser/sharing/sharing_sync_preference_unittest.cc b/chrome/browser/sharing/sharing_sync_preference_unittest.cc index 6d51d49..4b7164f 100644 --- a/chrome/browser/sharing/sharing_sync_preference_unittest.cc +++ b/chrome/browser/sharing/sharing_sync_preference_unittest.cc
@@ -44,13 +44,6 @@ kDeviceAuthToken, kClickToCallEnabled)); } - static base::Value CreateRandomDevice(base::Time timestamp) { - return SharingSyncPreference::DeviceToValue( - {base::GenerateGUID(), kDeviceP256dh, kDeviceAuthToken, - kClickToCallEnabled}, - timestamp); - } - sync_preferences::TestingPrefServiceSyncable prefs_; SharingSyncPreference sharing_sync_preference_; };
diff --git a/chrome/browser/signin/account_reconcilor_factory.cc b/chrome/browser/signin/account_reconcilor_factory.cc index 61fd5f7a..115dfd4 100644 --- a/chrome/browser/signin/account_reconcilor_factory.cc +++ b/chrome/browser/signin/account_reconcilor_factory.cc
@@ -29,7 +29,6 @@ #include "chrome/browser/chromeos/account_manager/account_manager_util.h" #include "chrome/browser/chromeos/account_manager/account_migration_runner.h" #include "chrome/browser/lifetime/application_lifetime.h" -#include "chromeos/constants/chromeos_features.h" #include "chromeos/tpm/install_attributes.h" #include "components/signin/core/browser/active_directory_account_reconcilor_delegate.h" #include "components/user_manager/user_manager.h" @@ -181,15 +180,12 @@ signin::ActiveDirectoryAccountReconcilorDelegate>(); } - // TODO(sinhak): Remove the if-condition (and use - // |MirrorAccountReconcilorDelegate|) when all Chrome OS users have been - // migrated to Account Manager. - if (chromeos::features::IsAccountManagerEnabled()) { - return std::make_unique<ChromeOSAccountReconcilorDelegate>( - IdentityManagerFactory::GetForProfile(profile), - chromeos::AccountManagerMigratorFactory::GetForBrowserContext( - profile)); - } + // TODO(sinhak): Use |MirrorAccountReconcilorDelegate|) when all Chrome OS + // users have been migrated to Account Manager. + return std::make_unique<ChromeOSAccountReconcilorDelegate>( + IdentityManagerFactory::GetForProfile(profile), + chromeos::AccountManagerMigratorFactory::GetForBrowserContext( + profile)); #elif defined(OS_ANDROID) if (base::FeatureList::IsEnabled(signin::kMiceFeature)) return std::make_unique<signin::MiceAccountReconcilorDelegate>();
diff --git a/chrome/browser/signin/chrome_signin_helper.cc b/chrome/browser/signin/chrome_signin_helper.cc index 703983c5..c276278 100644 --- a/chrome/browser/signin/chrome_signin_helper.cc +++ b/chrome/browser/signin/chrome_signin_helper.cc
@@ -58,7 +58,6 @@ #include "chrome/browser/supervised_user/supervised_user_service_factory.h" #include "chrome/browser/ui/settings_window_manager_chromeos.h" #include "chrome/browser/ui/webui/signin/inline_login_handler_dialog_chromeos.h" -#include "chromeos/constants/chromeos_features.h" #endif namespace signin { @@ -185,58 +184,45 @@ AccountReconcilorFactory::GetForProfile(profile); account_reconcilor->OnReceivedManageAccountsResponse(service_type); #if defined(OS_CHROMEOS) - if (chrome::FindBrowserWithWebContents(web_contents) && - service_type == GAIA_SERVICE_TYPE_INCOGNITO) { - chrome::NewIncognitoWindow(profile); - return; - } signin_metrics::LogAccountReconcilorStateOnGaiaResponse( account_reconcilor->GetState()); - if (chromeos::features::IsAccountManagerEnabled()) { - // Chrome OS Account Manager is available. The only allowed operations - // are: - // - // - Going Incognito (already handled in above switch-case). - // - Displaying the Account Manager for managing accounts. - // - Displaying a reauthentication window: Enterprise GSuite Accounts could - // have been forced through an online in-browser sign-in for sensitive - // webpages, thereby decreasing their session validity. After their session - // expires, they will receive a "Mirror" re-authentication request for all - // Google web properties. + // Do not do anything if the navigation happened in the "background". + if (!chrome::FindBrowserWithWebContents(web_contents)) + return; - // Do not display Account Manager if the navigation happened in the - // "background". - if (!chrome::FindBrowserWithWebContents(web_contents)) - return; + // The only allowed operations are: + // - Going Incognito. + // - Displaying the Account Manager for managing accounts. + // - Displaying a reauthentication window: Enterprise GSuite Accounts could + // have been forced through an online in-browser sign-in for sensitive + // webpages, thereby decreasing their session validity. After their session + // expires, they will receive a "Mirror" re-authentication request for all + // Google web properties. - if (manage_accounts_params.email.empty()) { - // Display Account Manager for managing accounts. - chrome::SettingsWindowManager::GetInstance()->ShowOSSettings( - profile, chrome::kAccountManagerSubPage); - } else { - // Do not display the re-authentication dialog if this event was triggered - // by supervision being enabled for an account. In this situation, a - // complete signout is required. - SupervisedUserService* service = - SupervisedUserServiceFactory::GetForProfile(profile); - if (service && service->signout_required_after_supervision_enabled()) { - return; - } - - // Display a re-authentication dialog. - chromeos::InlineLoginHandlerDialogChromeOS::Show( - manage_accounts_params.email); - } + if (service_type == GAIA_SERVICE_TYPE_INCOGNITO) { + chrome::NewIncognitoWindow(profile); return; } - // TODO(sinhak): Remove this when Chrome OS Account Manager is released. - // Chrome OS does not have an account picker right now. To fix - // https://crbug.com/807568, this is a no-op here. This is OK because in - // the limited cases where Mirror is available on Chrome OS, 1:1 account - // consistency is enforced and adding/removing accounts is not allowed, - // GAIA_SERVICE_TYPE_INCOGNITO may be allowed though. + if (manage_accounts_params.email.empty()) { + // Display Account Manager for managing accounts. + chrome::SettingsWindowManager::GetInstance()->ShowOSSettings( + profile, chrome::kAccountManagerSubPage); + } else { + // Do not display the re-authentication dialog if this event was triggered + // by supervision being enabled for an account. In this situation, a + // complete signout is required. + SupervisedUserService* service = + SupervisedUserServiceFactory::GetForProfile(profile); + if (service && service->signout_required_after_supervision_enabled()) { + return; + } + + // Display a re-authentication dialog. + chromeos::InlineLoginHandlerDialogChromeOS::Show( + manage_accounts_params.email); + } return; #else // !defined(OS_CHROMEOS)
diff --git a/chrome/browser/sync/profile_sync_service_android.cc b/chrome/browser/sync/profile_sync_service_android.cc index 30863e6e8..e0195f0f 100644 --- a/chrome/browser/sync/profile_sync_service_android.cc +++ b/chrome/browser/sync/profile_sync_service_android.cc
@@ -278,11 +278,12 @@ sync_service_->GetUserSettings()->EnableEncryptEverything(); } -jboolean ProfileSyncServiceAndroid::IsPassphraseRequiredForDecryption( +jboolean ProfileSyncServiceAndroid::IsPassphraseRequiredForPreferredDataTypes( JNIEnv* env, const JavaParamRef<jobject>& obj) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - return sync_service_->GetUserSettings()->IsPassphraseRequiredForDecryption(); + return sync_service_->GetUserSettings() + ->IsPassphraseRequiredForPreferredDataTypes(); } jboolean ProfileSyncServiceAndroid::IsUsingSecondaryPassphrase(
diff --git a/chrome/browser/sync/profile_sync_service_android.h b/chrome/browser/sync/profile_sync_service_android.h index e5cbed0..69edabe 100644 --- a/chrome/browser/sync/profile_sync_service_android.h +++ b/chrome/browser/sync/profile_sync_service_android.h
@@ -85,7 +85,7 @@ const base::android::JavaParamRef<jobject>& obj); void EnableEncryptEverything(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj); - jboolean IsPassphraseRequiredForDecryption( + jboolean IsPassphraseRequiredForPreferredDataTypes( JNIEnv* env, const base::android::JavaParamRef<jobject>& obj); jboolean IsUsingSecondaryPassphrase(
diff --git a/chrome/browser/sync/sync_ui_util.cc b/chrome/browser/sync/sync_ui_util.cc index 7ee628d..5014a8de 100644 --- a/chrome/browser/sync/sync_ui_util.cc +++ b/chrome/browser/sync/sync_ui_util.cc
@@ -161,7 +161,8 @@ if (service->GetUserSettings()->IsFirstSetupComplete()) { // Check for a passphrase error. - if (service->GetUserSettings()->IsPassphraseRequiredForDecryption()) { + if (service->GetUserSettings() + ->IsPassphraseRequiredForPreferredDataTypes()) { if (status_label) { *status_label = l10n_util::GetStringUTF16(IDS_SYNC_STATUS_NEEDS_PASSWORD); @@ -344,7 +345,8 @@ bool ShouldShowPassphraseError(const syncer::SyncService* service) { return service->GetUserSettings()->IsFirstSetupComplete() && - service->GetUserSettings()->IsPassphraseRequiredForDecryption(); + service->GetUserSettings() + ->IsPassphraseRequiredForPreferredDataTypes(); } } // namespace sync_ui_util
diff --git a/chrome/browser/sync/test/integration/encryption_helper.cc b/chrome/browser/sync/test/integration/encryption_helper.cc index 6205c96..497cddd 100644 --- a/chrome/browser/sync/test/integration/encryption_helper.cc +++ b/chrome/browser/sync/test/integration/encryption_helper.cc
@@ -169,8 +169,9 @@ : SingleClientStatusChangeChecker(service), desired_state_(desired_state) {} bool PassphraseRequiredStateChecker::IsExitConditionSatisfied() { - return service()->GetUserSettings()->IsPassphraseRequiredForDecryption() == - desired_state_; + return service() + ->GetUserSettings() + ->IsPassphraseRequiredForPreferredDataTypes() == desired_state_; } std::string PassphraseRequiredStateChecker::GetDebugMessage() const {
diff --git a/chrome/browser/sync/test/integration/profile_sync_service_harness.cc b/chrome/browser/sync/test/integration/profile_sync_service_harness.cc index 514365f05..edc13b0 100644 --- a/chrome/browser/sync/test/integration/profile_sync_service_harness.cc +++ b/chrome/browser/sync/test/integration/profile_sync_service_harness.cc
@@ -6,6 +6,7 @@ #include <cstddef> #include <sstream> +#include <utility> #include "base/command_line.h" #include "base/json/json_writer.h" @@ -98,8 +99,13 @@ if (HasAuthError(service())) { return true; } - if (service()->GetPassphraseRequiredReasonForTest() == - syncer::REASON_DECRYPTION) { + // TODO(crbug.com/1010397): The verification of INITIALIZING is only needed + // due to SyncEncryptionHandlerImpl issuing an unnecessary + // OnPassphraseRequired() during initialization. + if (service() + ->GetUserSettings() + ->IsPassphraseRequiredForPreferredDataTypes() && + transport_state != syncer::SyncService::TransportState::INITIALIZING) { LOG(FATAL) << "A passphrase is required for decryption but was not provided. " "Waiting for sync to become available won't succeed. Make sure " @@ -658,9 +664,8 @@ << ", server conflicts: " << snap.num_server_conflicts() << ", num_updates_downloaded : " << snap.model_neutral_state().num_updates_downloaded_total - << ", passphrase_required_reason: " - << syncer::PassphraseRequiredReasonToString( - service()->GetPassphraseRequiredReasonForTest()) + << ", passphrase_required: " + << service()->GetUserSettings()->IsPassphraseRequired() << ", notifications_enabled: " << status.notifications_enabled << ", service_is_active: " << service()->IsSyncFeatureActive(); } else {
diff --git a/chrome/browser/sync/test/integration/two_client_custom_passphrase_sync_test.cc b/chrome/browser/sync/test/integration/two_client_custom_passphrase_sync_test.cc index 7833d79..6fbdee3 100644 --- a/chrome/browser/sync/test/integration/two_client_custom_passphrase_sync_test.cc +++ b/chrome/browser/sync/test/integration/two_client_custom_passphrase_sync_test.cc
@@ -67,7 +67,7 @@ ->SetDecryptionPassphrase("incorrect passphrase")); EXPECT_TRUE(GetSyncService(kDecryptingClientId) ->GetUserSettings() - ->IsPassphraseRequiredForDecryption()); + ->IsPassphraseRequiredForPreferredDataTypes()); } IN_PROC_BROWSER_TEST_F(TwoClientCustomPassphraseSyncTest, ClientsCanSyncData) {
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index 08271819..f4656df9 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn
@@ -135,8 +135,6 @@ "page_info/page_info_ui.h", "passwords/account_avatar_fetcher.cc", "passwords/account_avatar_fetcher.h", - "passwords/credential_leak_dialog_utils.cc", - "passwords/credential_leak_dialog_utils.h", "passwords/manage_passwords_state.cc", "passwords/manage_passwords_state.h", "passwords/manage_passwords_view_utils.cc",
diff --git a/chrome/browser/ui/input_method/input_method_engine_base.cc b/chrome/browser/ui/input_method/input_method_engine_base.cc index 0736114..10e5bed8 100644 --- a/chrome/browser/ui/input_method/input_method_engine_base.cc +++ b/chrome/browser/ui/input_method/input_method_engine_base.cc
@@ -151,7 +151,6 @@ context_id_(0), next_context_id_(1), profile_(nullptr), - next_request_id_(1), composition_changed_(false), text_(""), commit_text_changed_(false), @@ -458,27 +457,38 @@ composition_changed_ = false; } - auto request = request_map_.find(request_id); - if (request == request_map_.end()) { + const auto it = pending_key_events_.find(request_id); + if (it == pending_key_events_.end()) { LOG(ERROR) << "Request ID not found: " << request_id; return; } - std::move(request->second.second).Run(handled); - request_map_.erase(request); + std::move(it->second.callback).Run(handled); + pending_key_events_.erase(it); } -std::string InputMethodEngineBase::AddRequest( +std::string InputMethodEngineBase::AddPendingKeyEvent( const std::string& component_id, - ui::IMEEngineHandlerInterface::KeyEventDoneCallback key_data) { + ui::IMEEngineHandlerInterface::KeyEventDoneCallback callback) { std::string request_id = base::NumberToString(next_request_id_); ++next_request_id_; - request_map_[request_id] = std::make_pair(component_id, std::move(key_data)); + pending_key_events_.emplace( + request_id, PendingKeyEvent(component_id, std::move(callback))); return request_id; } +InputMethodEngineBase::PendingKeyEvent::PendingKeyEvent( + const std::string& component_id, + ui::IMEEngineHandlerInterface::KeyEventDoneCallback callback) + : component_id(component_id), callback(std::move(callback)) {} + +InputMethodEngineBase::PendingKeyEvent::PendingKeyEvent( + PendingKeyEvent&& other) = default; + +InputMethodEngineBase::PendingKeyEvent::~PendingKeyEvent() = default; + void InputMethodEngineBase::DeleteSurroundingTextToInputContext( int offset, size_t number_of_chars) {
diff --git a/chrome/browser/ui/input_method/input_method_engine_base.h b/chrome/browser/ui/input_method/input_method_engine_base.h index ac7b934..2a7aff5 100644 --- a/chrome/browser/ui/input_method/input_method_engine_base.h +++ b/chrome/browser/ui/input_method/input_method_engine_base.h
@@ -197,10 +197,10 @@ const std::string& request_id, bool handled); - // Adds unprocessed key event to |request_map_|. - std::string AddRequest( + // Returns the request ID for this key event. + std::string AddPendingKeyEvent( const std::string& component_id, - ui::IMEEngineHandlerInterface::KeyEventDoneCallback key_data); + ui::IMEEngineHandlerInterface::KeyEventDoneCallback callback); int GetContextIdForTesting() const { return context_id_; } @@ -210,6 +210,20 @@ } protected: + struct PendingKeyEvent { + PendingKeyEvent( + const std::string& component_id, + ui::IMEEngineHandlerInterface::KeyEventDoneCallback callback); + PendingKeyEvent(PendingKeyEvent&& other); + ~PendingKeyEvent(); + + std::string component_id; + ui::IMEEngineHandlerInterface::KeyEventDoneCallback callback; + + private: + DISALLOW_COPY_AND_ASSIGN(PendingKeyEvent); + }; + // Returns true if this IME is active, false if not. virtual bool IsActive() const = 0; @@ -253,13 +267,8 @@ Profile* profile_; - using RequestMap = - std::map<std::string, - std::pair<std::string, - ui::IMEEngineHandlerInterface::KeyEventDoneCallback>>; - - unsigned int next_request_id_; - RequestMap request_map_; + unsigned int next_request_id_ = 1; + std::map<std::string, PendingKeyEvent> pending_key_events_; // The composition text to be set from calling input.ime.setComposition API. ui::CompositionText composition_;
diff --git a/chrome/browser/ui/passwords/credential_leak_dialog_controller_impl.cc b/chrome/browser/ui/passwords/credential_leak_dialog_controller_impl.cc index b029f07d..6f0ddad 100644 --- a/chrome/browser/ui/passwords/credential_leak_dialog_controller_impl.cc +++ b/chrome/browser/ui/passwords/credential_leak_dialog_controller_impl.cc
@@ -4,9 +4,9 @@ #include "chrome/browser/ui/passwords/credential_leak_dialog_controller_impl.h" -#include "chrome/browser/ui/passwords/credential_leak_dialog_utils.h" #include "chrome/browser/ui/passwords/password_dialog_prompts.h" #include "chrome/browser/ui/passwords/passwords_leak_dialog_delegate.h" +#include "components/password_manager/core/browser/leak_detection_dialog_utils.h" #include "components/password_manager/core/browser/password_manager_metrics_util.h" using password_manager::CredentialLeakFlags; @@ -37,7 +37,7 @@ void CredentialLeakDialogControllerImpl::OnCancelDialog() { LogLeakDialogTypeAndDismissalReason( - leak_dialog_utils::GetLeakDialogType(leak_type_), + password_manager::GetLeakDialogType(leak_type_), LeakDialogDismissalReason::kClickedClose); delegate_->OnLeakDialogHidden(); } @@ -45,12 +45,12 @@ void CredentialLeakDialogControllerImpl::OnAcceptDialog() { if (ShouldCheckPasswords()) { LogLeakDialogTypeAndDismissalReason( - leak_dialog_utils::GetLeakDialogType(leak_type_), + password_manager::GetLeakDialogType(leak_type_), LeakDialogDismissalReason::kClickedCheckPasswords); delegate_->NavigateToPasswordCheckup(); } else { LogLeakDialogTypeAndDismissalReason( - leak_dialog_utils::GetLeakDialogType(leak_type_), + password_manager::GetLeakDialogType(leak_type_), LeakDialogDismissalReason::kClickedOk); } delegate_->OnLeakDialogHidden(); @@ -58,40 +58,40 @@ void CredentialLeakDialogControllerImpl::OnCloseDialog() { LogLeakDialogTypeAndDismissalReason( - leak_dialog_utils::GetLeakDialogType(leak_type_), + password_manager::GetLeakDialogType(leak_type_), LeakDialogDismissalReason::kNoDirectInteraction); delegate_->OnLeakDialogHidden(); } base::string16 CredentialLeakDialogControllerImpl::GetAcceptButtonLabel() const { - return leak_dialog_utils::GetAcceptButtonLabel(leak_type_); + return password_manager::GetAcceptButtonLabel(leak_type_); } base::string16 CredentialLeakDialogControllerImpl::GetCancelButtonLabel() const { - return leak_dialog_utils::GetCancelButtonLabel(); + return password_manager::GetCancelButtonLabel(); } base::string16 CredentialLeakDialogControllerImpl::GetDescription() const { - return leak_dialog_utils::GetDescription(leak_type_, origin_); + return password_manager::GetDescription(leak_type_, origin_); } base::string16 CredentialLeakDialogControllerImpl::GetTitle() const { - return leak_dialog_utils::GetTitle(leak_type_); + return password_manager::GetTitle(leak_type_); } bool CredentialLeakDialogControllerImpl::ShouldCheckPasswords() const { - return leak_dialog_utils::ShouldCheckPasswords(leak_type_); + return password_manager::ShouldCheckPasswords(leak_type_); } bool CredentialLeakDialogControllerImpl::ShouldShowCancelButton() const { - return leak_dialog_utils::ShouldShowCancelButton(leak_type_); + return password_manager::ShouldShowCancelButton(leak_type_); } gfx::Range CredentialLeakDialogControllerImpl::GetChangePasswordBoldRange() const { - return leak_dialog_utils::GetChangePasswordBoldRange(leak_type_, origin_); + return password_manager::GetChangePasswordBoldRange(leak_type_, origin_); } void CredentialLeakDialogControllerImpl::ResetDialog() {
diff --git a/chrome/browser/ui/passwords/credential_leak_dialog_utils.cc b/chrome/browser/ui/passwords/credential_leak_dialog_utils.cc deleted file mode 100644 index 6ed1e39..0000000 --- a/chrome/browser/ui/passwords/credential_leak_dialog_utils.cc +++ /dev/null
@@ -1,108 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/ui/passwords/credential_leak_dialog_utils.h" - -#include "base/metrics/field_trial_params.h" -#include "base/strings/utf_string_conversions.h" -#include "chrome/common/url_constants.h" -#include "chrome/grit/generated_resources.h" -#include "components/password_manager/core/browser/leak_detection_dialog_utils.h" -#include "components/password_manager/core/common/password_manager_features.h" -#include "components/strings/grit/components_strings.h" -#include "components/url_formatter/elide_url.h" -#include "ui/base/l10n/l10n_util.h" -#include "url/gurl.h" -#include "url/origin.h" - -using password_manager::CredentialLeakFlags; -using password_manager::CredentialLeakType; -using password_manager::metrics_util::LeakDialogType; - -namespace leak_dialog_utils { -namespace { - -// Formats the |origin| to a human-friendly url string. -base::string16 GetFormattedUrl(const GURL& origin) { - return url_formatter::FormatUrlForSecurityDisplay( - origin, url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS); -} - -} // namespace - -base::string16 GetAcceptButtonLabel(CredentialLeakType leak_type) { - return l10n_util::GetStringUTF16( - ShouldCheckPasswords(leak_type) ? IDS_LEAK_CHECK_CREDENTIALS : IDS_OK); -} - -base::string16 GetCancelButtonLabel() { - return l10n_util::GetStringUTF16(IDS_CLOSE); -} - -base::string16 GetDescription(CredentialLeakType leak_type, - const GURL& origin) { - const base::string16 formatted = GetFormattedUrl(origin); - if (!ShouldCheckPasswords(leak_type)) { - std::vector<size_t> offsets; - base::string16 bold_message = l10n_util::GetStringUTF16( - IDS_CREDENTIAL_LEAK_CHANGE_PASSWORD_BOLD_MESSAGE); - return l10n_util::GetStringFUTF16( - IDS_CREDENTIAL_LEAK_CHANGE_PASSWORD_MESSAGE, bold_message, formatted, - &offsets); - } else if (password_manager::IsPasswordSaved(leak_type)) { - return l10n_util::GetStringUTF16( - IDS_CREDENTIAL_LEAK_CHECK_PASSWORDS_MESSAGE); - } else { - return l10n_util::GetStringFUTF16( - IDS_CREDENTIAL_LEAK_CHANGE_AND_CHECK_PASSWORDS_MESSAGE, formatted); - } -} - -base::string16 GetTitle(CredentialLeakType leak_type) { - return l10n_util::GetStringUTF16(IDS_CREDENTIAL_LEAK_TITLE); -} - -bool ShouldCheckPasswords(CredentialLeakType leak_type) { - return password_manager::IsPasswordUsedOnOtherSites(leak_type) && - password_manager::IsSyncingPasswordsNormally(leak_type); -} - -bool ShouldShowCancelButton(CredentialLeakType leak_type) { - return ShouldCheckPasswords(leak_type); -} - -LeakDialogType GetLeakDialogType(CredentialLeakType leak_type) { - if (!ShouldCheckPasswords(leak_type)) - return LeakDialogType::kChange; - - return password_manager::IsPasswordSaved(leak_type) - ? LeakDialogType::kCheckup - : LeakDialogType::kCheckupAndChange; -} - -GURL GetPasswordCheckupURL() { - std::string value = base::GetFieldTrialParamValueByFeature( - password_manager::features::kLeakDetection, "leak-check-url"); - if (value.empty()) - return GURL(chrome::kPasswordCheckupURL); - return GURL(value); -} - -gfx::Range GetChangePasswordBoldRange(CredentialLeakType leak_type, - const GURL& origin) { - if (ShouldCheckPasswords(leak_type)) - return gfx::Range(); - - std::vector<size_t> offsets; - const base::string16 bold_message = l10n_util::GetStringUTF16( - IDS_CREDENTIAL_LEAK_CHANGE_PASSWORD_BOLD_MESSAGE); - const base::string16 change_password_message = l10n_util::GetStringFUTF16( - IDS_CREDENTIAL_LEAK_CHANGE_PASSWORD_MESSAGE, bold_message, - GetFormattedUrl(origin), &offsets); - return offsets.empty() - ? gfx::Range() - : gfx::Range(offsets[0], offsets[0] + bold_message.length()); -} - -} // namespace leak_dialog_utils
diff --git a/chrome/browser/ui/passwords/credential_leak_dialog_utils.h b/chrome/browser/ui/passwords/credential_leak_dialog_utils.h deleted file mode 100644 index 54bd37b..0000000 --- a/chrome/browser/ui/passwords/credential_leak_dialog_utils.h +++ /dev/null
@@ -1,52 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_UI_PASSWORDS_CREDENTIAL_LEAK_DIALOG_UTILS_H_ -#define CHROME_BROWSER_UI_PASSWORDS_CREDENTIAL_LEAK_DIALOG_UTILS_H_ - -#include "base/macros.h" -#include "base/strings/string16.h" -#include "components/password_manager/core/browser/leak_detection_dialog_utils.h" -#include "components/password_manager/core/browser/password_manager_metrics_util.h" -#include "ui/gfx/range/range.h" -#include "url/gurl.h" - -namespace leak_dialog_utils { - -// Returns the label for the leak dialog accept button. -base::string16 GetAcceptButtonLabel( - password_manager::CredentialLeakType leak_type); - -// Returns the label for the leak dialog cancel button. -base::string16 GetCancelButtonLabel(); - -// Returns the leak dialog message based on leak type. -base::string16 GetDescription(password_manager::CredentialLeakType leak_type, - const GURL& origin); - -// Returns the leak dialog title based on leak type. -base::string16 GetTitle(password_manager::CredentialLeakType leak_type); - -// Checks whether the leak dialog should prompt user to password checkup. -bool ShouldCheckPasswords(password_manager::CredentialLeakType leak_type); - -// Checks whether the leak dialog should show cancel button. -bool ShouldShowCancelButton(password_manager::CredentialLeakType leak_type); - -// Returns the LeakDialogType corresponding to |leak_type|. -password_manager::metrics_util::LeakDialogType GetLeakDialogType( - password_manager::CredentialLeakType leak_type); - -// Returns the URL used to launch the password checkup. -GURL GetPasswordCheckupURL(); - -// Returns the range of the bold part of the leak dialog message when -// credentials were leaked only on current site. -gfx::Range GetChangePasswordBoldRange( - password_manager::CredentialLeakType leak_type, - const GURL& origin); - -} // namespace leak_dialog_utils - -#endif // CHROME_BROWSER_UI_PASSWORDS_CREDENTIAL_LEAK_DIALOG_UTILS_H_
diff --git a/chrome/browser/ui/passwords/manage_passwords_view_utils.cc b/chrome/browser/ui/passwords/manage_passwords_view_utils.cc index dfbd6c20..6baae5e 100644 --- a/chrome/browser/ui/passwords/manage_passwords_view_utils.cc +++ b/chrome/browser/ui/passwords/manage_passwords_view_utils.cc
@@ -17,13 +17,13 @@ #include "chrome/browser/ui/browser_navigator.h" #include "chrome/browser/ui/browser_navigator_params.h" #include "chrome/browser/ui/chrome_pages.h" -#include "chrome/browser/ui/passwords/credential_leak_dialog_utils.h" #include "chrome/common/url_constants.h" #include "chrome/common/webui_url_constants.h" #include "chrome/grit/chromium_strings.h" #include "chrome/grit/generated_resources.h" #include "components/autofill/core/common/password_form.h" #include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h" +#include "components/password_manager/core/browser/leak_detection_dialog_utils.h" #include "components/password_manager/core/browser/password_manager_client.h" #include "components/password_manager/core/browser/password_manager_constants.h" #include "components/password_manager/core/browser/password_manager_util.h" @@ -257,7 +257,7 @@ } void NavigateToPasswordCheckupPage(Profile* profile) { - NavigateParams params(profile, leak_dialog_utils::GetPasswordCheckupURL(), + NavigateParams params(profile, password_manager::GetPasswordCheckupURL(), ui::PAGE_TRANSITION_LINK); params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB; Navigate(¶ms);
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_unittest.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_unittest.cc index 56273c0..6587564 100644 --- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_unittest.cc +++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_unittest.cc
@@ -51,14 +51,7 @@ : BrowserNonClientFrameViewTest(Browser::TYPE_POPUP) {} }; -// TODO(crbug.com/998369): Flaky on Linux TSAN and ASAN. -#if defined(OS_LINUX) && \ - (defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER)) -#define MAYBE_HitTestPopupTopChrome DISABLED_HitTestPopupTopChrome -#else -#define MAYBE_HitTestPopupTopChrome HitTestPopupTopChrome -#endif -TEST_F(BrowserNonClientFrameViewPopupTest, MAYBE_HitTestPopupTopChrome) { +TEST_F(BrowserNonClientFrameViewPopupTest, HitTestPopupTopChrome) { EXPECT_FALSE(frame_view_->HitTestRect(gfx::Rect(-1, 4, 1, 1))); EXPECT_FALSE(frame_view_->HitTestRect(gfx::Rect(4, -1, 1, 1))); const int top_inset = frame_view_->GetTopInset(false);
diff --git a/chrome/browser/ui/views/global_media_controls/media_notification_list_view.cc b/chrome/browser/ui/views/global_media_controls/media_notification_list_view.cc index d29747b..8139413e 100644 --- a/chrome/browser/ui/views/global_media_controls/media_notification_list_view.cc +++ b/chrome/browser/ui/views/global_media_controls/media_notification_list_view.cc
@@ -21,8 +21,10 @@ views::BoxLayout::Orientation::kVertical)); ClipHeightTo(0, kMediaListMaxHeight); - SetVerticalScrollBar(new views::OverlayScrollBar(/*horizontal=*/false)); - SetHorizontalScrollBar(new views::OverlayScrollBar(/*horizontal=*/true)); + SetVerticalScrollBar( + std::make_unique<views::OverlayScrollBar>(/*horizontal=*/false)); + SetHorizontalScrollBar( + std::make_unique<views::OverlayScrollBar>(/*horizontal=*/true)); } MediaNotificationListView::~MediaNotificationListView() = default;
diff --git a/chrome/browser/ui/webui/settings/people_handler.cc b/chrome/browser/ui/webui/settings/people_handler.cc index 176db09..3ec355f 100644 --- a/chrome/browser/ui/webui/settings/people_handler.cc +++ b/chrome/browser/ui/webui/settings/people_handler.cc
@@ -619,8 +619,8 @@ bool passphrase_failed = false; if (!configuration.passphrase.empty()) { // We call IsPassphraseRequired() here (instead of - // IsPassphraseRequiredForDecryption()) because the user may try to enter - // a passphrase even though no encrypted data types are enabled. + // IsPassphraseRequiredForPreferredDataTypes()) because the user may try to + // enter a passphrase even though no encrypted data types are enabled. if (service->GetUserSettings()->IsPassphraseRequired()) { // If we have pending keys, try to decrypt them with the provided // passphrase. We track if this succeeds or fails because a failed @@ -642,7 +642,7 @@ } if (passphrase_failed || - service->GetUserSettings()->IsPassphraseRequiredForDecryption()) { + service->GetUserSettings()->IsPassphraseRequiredForPreferredDataTypes()) { // If the user doesn't enter any passphrase, we won't call // SetDecryptionPassphrase() (passphrase_failed == false), but we still // want to display an error message to let the user know that their blank @@ -1097,8 +1097,8 @@ sync_user_settings->IsEncryptEverythingAllowed()); // We call IsPassphraseRequired() here, instead of calling - // IsPassphraseRequiredForDecryption(), because we want to show the passphrase - // UI even if no encrypted data types are enabled. + // IsPassphraseRequiredForPreferredDataTypes(), because we want to show the + // passphrase UI even if no encrypted data types are enabled. args.SetBoolean("passphraseRequired", sync_user_settings->IsPassphraseRequired());
diff --git a/chrome/browser/ui/webui/settings/people_handler_unittest.cc b/chrome/browser/ui/webui/settings/people_handler_unittest.cc index d3dee7a..833ac7b 100644 --- a/chrome/browser/ui/webui/settings/people_handler_unittest.cc +++ b/chrome/browser/ui/webui/settings/people_handler_unittest.cc
@@ -816,7 +816,7 @@ list_args.AppendString(kTestCallbackId); list_args.AppendString(args); ON_CALL(*mock_sync_service_->GetMockUserSettings(), - IsPassphraseRequiredForDecryption()) + IsPassphraseRequiredForPreferredDataTypes()) .WillByDefault(Return(false)); ON_CALL(*mock_sync_service_->GetMockUserSettings(), IsPassphraseRequired()) .WillByDefault(Return(false)); @@ -835,7 +835,7 @@ list_args.AppendString(kTestCallbackId); list_args.AppendString(args); ON_CALL(*mock_sync_service_->GetMockUserSettings(), - IsPassphraseRequiredForDecryption()) + IsPassphraseRequiredForPreferredDataTypes()) .WillByDefault(Return(true)); ON_CALL(*mock_sync_service_->GetMockUserSettings(), IsPassphraseRequired()) .WillByDefault(Return(true)); @@ -864,7 +864,7 @@ IsPassphraseRequired()) .WillOnce(Return(true)); ON_CALL(*mock_sync_service_->GetMockUserSettings(), - IsPassphraseRequiredForDecryption()) + IsPassphraseRequiredForPreferredDataTypes()) .WillByDefault(Return(false)); ON_CALL(*mock_sync_service_->GetMockUserSettings(), IsUsingSecondaryPassphrase()) @@ -890,7 +890,7 @@ IsEncryptEverythingAllowed()) .WillByDefault(Return(true)); ON_CALL(*mock_sync_service_->GetMockUserSettings(), - IsPassphraseRequiredForDecryption()) + IsPassphraseRequiredForPreferredDataTypes()) .WillByDefault(Return(false)); ON_CALL(*mock_sync_service_->GetMockUserSettings(), IsPassphraseRequired()) .WillByDefault(Return(false)); @@ -914,7 +914,7 @@ list_args.AppendString(kTestCallbackId); list_args.AppendString(args); ON_CALL(*mock_sync_service_->GetMockUserSettings(), - IsPassphraseRequiredForDecryption()) + IsPassphraseRequiredForPreferredDataTypes()) .WillByDefault(Return(true)); ON_CALL(*mock_sync_service_->GetMockUserSettings(), IsPassphraseRequired()) .WillByDefault(Return(true)); @@ -945,7 +945,7 @@ list_args.AppendString(kTestCallbackId); list_args.AppendString(args); ON_CALL(*mock_sync_service_->GetMockUserSettings(), - IsPassphraseRequiredForDecryption()) + IsPassphraseRequiredForPreferredDataTypes()) .WillByDefault(Return(true)); ON_CALL(*mock_sync_service_->GetMockUserSettings(), IsPassphraseRequired()) .WillByDefault(Return(true)); @@ -976,7 +976,7 @@ list_args.AppendString(kTestCallbackId); list_args.AppendString(args); ON_CALL(*mock_sync_service_->GetMockUserSettings(), - IsPassphraseRequiredForDecryption()) + IsPassphraseRequiredForPreferredDataTypes()) .WillByDefault(Return(false)); ON_CALL(*mock_sync_service_->GetMockUserSettings(), IsPassphraseRequired()) .WillByDefault(Return(false)); @@ -1001,7 +1001,7 @@ list_args.AppendString(kTestCallbackId); list_args.AppendString(args); ON_CALL(*mock_sync_service_->GetMockUserSettings(), - IsPassphraseRequiredForDecryption()) + IsPassphraseRequiredForPreferredDataTypes()) .WillByDefault(Return(false)); ON_CALL(*mock_sync_service_->GetMockUserSettings(), IsPassphraseRequired()) .WillByDefault(Return(false)); @@ -1240,7 +1240,7 @@ TEST_F(PeopleHandlerTest, TurnOnEncryptAllDisallowed) { ON_CALL(*mock_sync_service_->GetMockUserSettings(), - IsPassphraseRequiredForDecryption()) + IsPassphraseRequiredForPreferredDataTypes()) .WillByDefault(Return(false)); ON_CALL(*mock_sync_service_->GetMockUserSettings(), IsPassphraseRequired()) .WillByDefault(Return(false));
diff --git a/chrome/browser/web_applications/components/app_registrar.cc b/chrome/browser/web_applications/components/app_registrar.cc index 9025044..eb4df2b 100644 --- a/chrome/browser/web_applications/components/app_registrar.cc +++ b/chrome/browser/web_applications/components/app_registrar.cc
@@ -24,6 +24,11 @@ return IsLocallyInstalled(GenerateAppIdFromURL(start_url)); } +bool AppRegistrar::IsPlaceholderApp(const AppId& app_id) const { + return ExternallyInstalledWebAppPrefs(profile_->GetPrefs()) + .IsPlaceholderApp(app_id); +} + void AppRegistrar::AddObserver(AppRegistrarObserver* observer) { observers_.AddObserver(observer); }
diff --git a/chrome/browser/web_applications/components/app_registrar.h b/chrome/browser/web_applications/components/app_registrar.h index 06597c1..f7564e66 100644 --- a/chrome/browser/web_applications/components/app_registrar.h +++ b/chrome/browser/web_applications/components/app_registrar.h
@@ -93,6 +93,10 @@ // that fall within the scope. bool IsLocallyInstalled(const GURL& start_url) const; + // Returns whether the app is pending successful navigation in order to + // complete installation via the PendingAppManager. + bool IsPlaceholderApp(const AppId& app_id) const; + void AddObserver(AppRegistrarObserver* observer); void RemoveObserver(AppRegistrarObserver* observer);
diff --git a/chrome/browser/web_applications/components/externally_installed_web_app_prefs.cc b/chrome/browser/web_applications/components/externally_installed_web_app_prefs.cc index a398fc3..6872183 100644 --- a/chrome/browser/web_applications/components/externally_installed_web_app_prefs.cc +++ b/chrome/browser/web_applications/components/externally_installed_web_app_prefs.cc
@@ -210,4 +210,14 @@ app_entry->SetBoolKey(kIsPlaceholder, is_placeholder); } +bool ExternallyInstalledWebAppPrefs::IsPlaceholderApp( + const AppId& app_id) const { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + const base::Value* app_prefs = GetPreferenceValue(pref_service_, app_id); + if (!app_prefs || !app_prefs->is_dict()) + return false; + return app_prefs->FindBoolKey(kIsPlaceholder).value_or(false); +} + } // namespace web_app
diff --git a/chrome/browser/web_applications/components/externally_installed_web_app_prefs.h b/chrome/browser/web_applications/components/externally_installed_web_app_prefs.h index a2cb288..c871a08 100644 --- a/chrome/browser/web_applications/components/externally_installed_web_app_prefs.h +++ b/chrome/browser/web_applications/components/externally_installed_web_app_prefs.h
@@ -57,6 +57,7 @@ // *placeholder app*. base::Optional<AppId> LookupPlaceholderAppId(const GURL& url) const; void SetIsPlaceholder(const GURL& url, bool is_placeholder); + bool IsPlaceholderApp(const AppId& app_id) const; private: PrefService* pref_service_;
diff --git a/chrome/browser/web_applications/components/manifest_update_manager.cc b/chrome/browser/web_applications/components/manifest_update_manager.cc index 1f8849e3..1bedfdb 100644 --- a/chrome/browser/web_applications/components/manifest_update_manager.cc +++ b/chrome/browser/web_applications/components/manifest_update_manager.cc
@@ -40,8 +40,12 @@ return; } - std::unique_ptr<ManifestUpdateTask>& current_task = tasks_[app_id]; - if (current_task) + if (registrar_->IsPlaceholderApp(app_id)) { + NotifyResult(url, ManifestUpdateResult::kAppIsPlaceholder); + return; + } + + if (base::Contains(tasks_, app_id)) return; if (!MaybeConsumeUpdateCheck(app_id)) { @@ -49,12 +53,13 @@ return; } - current_task = std::make_unique<ManifestUpdateTask>( - url, app_id, web_contents, - base::Bind(&ManifestUpdateManager::OnUpdateStopped, - base::Unretained(this)), - hang_update_checks_for_testing_, *registrar_, ui_manager_, - install_manager_); + tasks_.insert_or_assign( + app_id, std::make_unique<ManifestUpdateTask>( + url, app_id, web_contents, + base::Bind(&ManifestUpdateManager::OnUpdateStopped, + base::Unretained(this)), + hang_update_checks_for_testing_, *registrar_, ui_manager_, + install_manager_)); } // AppRegistrarObserver:
diff --git a/chrome/browser/web_applications/components/manifest_update_manager_browsertest.cc b/chrome/browser/web_applications/components/manifest_update_manager_browsertest.cc index d4473909..f1ebd46 100644 --- a/chrome/browser/web_applications/components/manifest_update_manager_browsertest.cc +++ b/chrome/browser/web_applications/components/manifest_update_manager_browsertest.cc
@@ -17,6 +17,7 @@ #include "chrome/browser/web_applications/components/app_registry_controller.h" #include "chrome/browser/web_applications/components/install_finalizer.h" #include "chrome/browser/web_applications/components/install_manager.h" +#include "chrome/browser/web_applications/components/pending_app_manager.h" #include "chrome/browser/web_applications/components/web_app_provider_base.h" #include "chrome/browser/web_applications/components/web_app_tab_helper.h" #include "chrome/common/chrome_features.h" @@ -111,20 +112,27 @@ std::unique_ptr<net::test_server::HttpResponse> RequestHandlerOverride( const net::test_server::HttpRequest& request) { - if (request.GetURL() != override_url_) - return nullptr; - auto http_response = - std::make_unique<net::test_server::BasicHttpResponse>(); - http_response->set_code(net::HTTP_FOUND); - http_response->set_content(override_content_); - return std::move(http_response); + if (request_override_) + return request_override_.Run(request); + return nullptr; } void OverrideManifest(const char* manifest_template, const std::vector<std::string>& substitutions) { - override_url_ = GetManifestURL(); - override_content_ = base::ReplaceStringPlaceholders(manifest_template, - substitutions, nullptr); + std::string content = base::ReplaceStringPlaceholders( + manifest_template, substitutions, nullptr); + request_override_ = base::BindLambdaForTesting( + [this, content = std::move(content)]( + const net::test_server::HttpRequest& request) + -> std::unique_ptr<net::test_server::HttpResponse> { + if (request.GetURL() != GetManifestURL()) + return nullptr; + auto http_response = + std::make_unique<net::test_server::BasicHttpResponse>(); + http_response->set_code(net::HTTP_FOUND); + http_response->set_content(content); + return std::move(http_response); + }); } GURL GetAppURL() const { @@ -176,12 +184,12 @@ return *WebAppProviderBase::GetProviderBase(browser()->profile()); } + net::EmbeddedTestServer::HandleRequestCallback request_override_; + private: base::test::ScopedFeatureList scoped_feature_list_; net::EmbeddedTestServer http_server_; - GURL override_url_; - std::string override_content_; DISALLOW_COPY_AND_ASSIGN(ManifestUpdateManagerBrowserTest); }; @@ -396,6 +404,61 @@ } IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, + CheckIgnoresPlaceholderApps) { + // Set up app URL to redirect to force placeholder app to install. + const GURL app_url = GetAppURL(); + request_override_ = base::BindLambdaForTesting( + [&app_url](const net::test_server::HttpRequest& request) + -> std::unique_ptr<net::test_server::HttpResponse> { + if (request.GetURL() != app_url) + return nullptr; + auto http_response = + std::make_unique<net::test_server::BasicHttpResponse>(); + http_response->set_code(net::HTTP_TEMPORARY_REDIRECT); + http_response->AddCustomHeader("Location", "/defaultresponse"); + http_response->set_content("redirect page"); + return std::move(http_response); + }); + + // Install via PendingAppManager, the redirect should cause it to install a + // placeholder app. + base::RunLoop run_loop; + ExternalInstallOptions install_options( + app_url, LaunchContainer::kWindow, + ExternalInstallSource::kExternalPolicy); + install_options.add_to_applications_menu = false; + install_options.add_to_desktop = false; + install_options.add_to_quick_launch_bar = false; + install_options.install_placeholder = true; + GetProvider().pending_app_manager().Install( + std::move(install_options), + base::BindLambdaForTesting( + [&](const GURL& installed_app_url, InstallResultCode code) { + EXPECT_EQ(installed_app_url, app_url); + EXPECT_EQ(code, InstallResultCode::kSuccessNewInstall); + run_loop.Quit(); + })); + run_loop.Run(); + AppId app_id = GetProvider().registrar().LookupExternalAppId(app_url).value(); + EXPECT_TRUE(GetProvider().registrar().IsPlaceholderApp(app_id)); + + // Manifest updating should ignore non-redirect loads for placeholder apps + // because the PendingAppManager will handle these. + const char* manifest_template = R"( + { + "name": "Test app name", + "start_url": ".", + "scope": "/", + "display": "standalone", + "icons": $1 + } + )"; + OverrideManifest(manifest_template, {kInstallableIconList}); + EXPECT_EQ(GetResultAfterPageLoad(app_url, &app_id), + ManifestUpdateResult::kAppIsPlaceholder); +} + +IN_PROC_BROWSER_TEST_F(ManifestUpdateManagerBrowserTest, CheckFindsThemeColorChange) { const char* manifest_template = R"( {
diff --git a/chrome/browser/web_applications/components/manifest_update_task.h b/chrome/browser/web_applications/components/manifest_update_task.h index aa9f53a..5d9e783 100644 --- a/chrome/browser/web_applications/components/manifest_update_task.h +++ b/chrome/browser/web_applications/components/manifest_update_task.h
@@ -25,6 +25,7 @@ kThrottled, kWebContentsDestroyed, kAppUninstalled, + kAppIsPlaceholder, kAppUpToDate, kAppDataInvalid, kAppUpdateFailed,
diff --git a/chrome/browser/web_applications/extensions/externally_installed_web_app_prefs_unittest.cc b/chrome/browser/web_applications/extensions/externally_installed_web_app_prefs_unittest.cc index 79b6bd2a..27848e5 100644 --- a/chrome/browser/web_applications/extensions/externally_installed_web_app_prefs_unittest.cc +++ b/chrome/browser/web_applications/extensions/externally_installed_web_app_prefs_unittest.cc
@@ -176,4 +176,26 @@ GetAppUrls(ExternalInstallSource::kExternalPolicy)); } +TEST_F(ExternallyInstalledWebAppPrefsTest, IsPlaceholderApp) { + const GURL url("https://example.com"); + const AppId app_id = "app_id_string"; + ExternallyInstalledWebAppPrefs prefs(profile()->GetPrefs()); + prefs.Insert(url, app_id, ExternalInstallSource::kExternalPolicy); + EXPECT_FALSE(ExternallyInstalledWebAppPrefs(profile()->GetPrefs()) + .IsPlaceholderApp(app_id)); + prefs.SetIsPlaceholder(url, true); + EXPECT_TRUE(ExternallyInstalledWebAppPrefs(profile()->GetPrefs()) + .IsPlaceholderApp(app_id)); +} + +TEST_F(ExternallyInstalledWebAppPrefsTest, OldPrefFormat) { + // Set up the old format for this pref {url -> app_id}. + DictionaryPrefUpdate update(profile()->GetPrefs(), + prefs::kWebAppsExtensionIDs); + update->SetKey("https://example.com", base::Value("add_id_string")); + // This should not crash on invalid pref data. + EXPECT_FALSE(ExternallyInstalledWebAppPrefs(profile()->GetPrefs()) + .IsPlaceholderApp("app_id_string")); +} + } // namespace web_app
diff --git a/chrome/common/url_constants.cc b/chrome/common/url_constants.cc index 3b3a967..fab12ef 100644 --- a/chrome/common/url_constants.cc +++ b/chrome/common/url_constants.cc
@@ -156,17 +156,6 @@ const char kGooglePasswordManagerURL[] = "https://passwords.google.com"; -const char kPasswordCheckupURL[] = -#if defined(OS_ANDROID) - "https://passwords.google.com/checkup/" - "start?utm_source=chrome&utm_medium=android&utm_campaign=leak_dialog&crch=" - "true"; -#else - "https://passwords.google.com/checkup/" - "start?utm_source=chrome&utm_medium=desktop&utm_campaign=leak_dialog&crch=" - "true"; -#endif - const char kLearnMoreReportingURL[] = "https://support.google.com/chrome/?p=ui_usagestat";
diff --git a/chrome/common/url_constants.h b/chrome/common/url_constants.h index 51445591..d91e2b7 100644 --- a/chrome/common/url_constants.h +++ b/chrome/common/url_constants.h
@@ -150,9 +150,6 @@ // URL of the Google Password Manager. extern const char kGooglePasswordManagerURL[]; -// URL for Password Checkup. -extern const char kPasswordCheckupURL[]; - // The URL for the "Learn more" page for the usage/crash reporting option in the // first run dialog. extern const char kLearnMoreReportingURL[];
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index 76f6649..8e65b99 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -3283,7 +3283,6 @@ "../browser/ui/find_bar/find_backend_unittest.cc", "../browser/ui/login/login_handler_unittest.cc", "../browser/ui/page_info/page_info_unittest.cc", - "../browser/ui/passwords/credential_leak_dialog_utils_unittest.cc", "../browser/ui/passwords/manage_passwords_state_unittest.cc", "../browser/ui/passwords/manage_passwords_view_utils_unittest.cc", "../browser/ui/passwords/password_generation_popup_controller_impl_unittest.cc",
diff --git a/chrome/test/base/browser_with_test_window_test.cc b/chrome/test/base/browser_with_test_window_test.cc index b54a78f..737123f85 100644 --- a/chrome/test/base/browser_with_test_window_test.cc +++ b/chrome/test/base/browser_with_test_window_test.cc
@@ -212,6 +212,18 @@ return std::make_unique<Browser>(params); } +#if defined(OS_CHROMEOS) +chromeos::ScopedCrosSettingsTestHelper* +BrowserWithTestWindowTest::GetCrosSettingsHelper() { + return &cros_settings_test_helper_; +} + +chromeos::StubInstallAttributes* +BrowserWithTestWindowTest::GetInstallAttributes() { + return GetCrosSettingsHelper()->InstallAttributes(); +} +#endif // defined(OS_CHROMEOS) + BrowserWithTestWindowTest::BrowserWithTestWindowTest( std::unique_ptr<content::BrowserTaskEnvironment> task_environment, Browser::Type browser_type,
diff --git a/chrome/test/base/browser_with_test_window_test.h b/chrome/test/base/browser_with_test_window_test.h index a8226f86..4fed95c 100644 --- a/chrome/test/base/browser_with_test_window_test.h +++ b/chrome/test/base/browser_with_test_window_test.h
@@ -24,6 +24,7 @@ #include "ash/test/ash_test_views_delegate.h" #include "chrome/browser/chromeos/login/users/scoped_test_user_manager.h" #include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h" +#include "chromeos/tpm/stub_install_attributes.h" #else #include "ui/views/test/scoped_views_test_helper.h" #endif @@ -192,6 +193,11 @@ } #endif +#if defined(OS_CHROMEOS) + chromeos::ScopedCrosSettingsTestHelper* GetCrosSettingsHelper(); + chromeos::StubInstallAttributes* GetInstallAttributes(); +#endif + private: // The template constructor has to be in the header but it delegates to this // constructor to initialize all other members out-of-line.
diff --git a/chromecast/media/audio/cast_audio_device_factory.cc b/chromecast/media/audio/cast_audio_device_factory.cc index d8eddb4..898f085 100644 --- a/chromecast/media/audio/cast_audio_device_factory.cc +++ b/chromecast/media/audio/cast_audio_device_factory.cc
@@ -65,7 +65,7 @@ void Flush() override { output_device_->Flush(); } protected: - ~NonSwitchableAudioRendererSink() override = default; + ~NonSwitchableAudioRendererSink() override { output_device_->Stop(); } private: scoped_refptr<::media::AudioOutputDevice> output_device_;
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM index 00ef50e1..b1014158 100644 --- a/chromeos/CHROMEOS_LKGM +++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@ -12557.0.0 \ No newline at end of file +12560.0.0 \ No newline at end of file
diff --git a/components/bookmarks/browser/bookmark_node_data_unittest.cc b/components/bookmarks/browser/bookmark_node_data_unittest.cc index 86be83c2..3dd0f0a 100644 --- a/components/bookmarks/browser/bookmark_node_data_unittest.cc +++ b/components/bookmarks/browser/bookmark_node_data_unittest.cc
@@ -367,7 +367,9 @@ EXPECT_EQ(base::ASCIIToUTF16("g1"), clipboard_result); } -TEST_F(BookmarkNodeDataTest, WriteToClipboardFolderAndURL) { +// TODO(https://crbug.com/1010415): This test is flaky on various platforms, fix +// and re-enable it. +TEST_F(BookmarkNodeDataTest, DISABLED_WriteToClipboardFolderAndURL) { BookmarkNodeData data; GURL url(GURL("http://foo.com")); const base::string16 title(ASCIIToUTF16("blah"));
diff --git a/components/bookmarks/browser/bookmark_utils_unittest.cc b/components/bookmarks/browser/bookmark_utils_unittest.cc index a6777e0..394bba5f 100644 --- a/components/bookmarks/browser/bookmark_utils_unittest.cc +++ b/components/bookmarks/browser/bookmark_utils_unittest.cc
@@ -291,7 +291,13 @@ ASCIIToUTF16(new_folder->children().front()->url().spec())); } -TEST_F(BookmarkUtilsTest, CopyPaste) { +// TODO(https://crbug.com/1010182): Fix flakes and re-enable this test. +#if defined(OS_WIN) || defined(OS_MACOSX) +#define MAYBE_CopyPaste DISABLED_CopyPaste +#else +#define MAYBE_CopyPaste CopyPaste +#endif +TEST_F(BookmarkUtilsTest, MAYBE_CopyPaste) { std::unique_ptr<BookmarkModel> model(TestBookmarkClient::CreateModel()); const BookmarkNode* node = model->AddURL(model->other_node(), 0,
diff --git a/components/content_capture/browser/content_capture_receiver_test.cc b/components/content_capture/browser/content_capture_receiver_test.cc index 2bb600c..b88f710 100644 --- a/components/content_capture/browser/content_capture_receiver_test.cc +++ b/components/content_capture/browser/content_capture_receiver_test.cc
@@ -9,6 +9,7 @@ #include "base/strings/utf_string_conversions.h" #include "base/task/post_task.h" +#include "build/build_config.h" #include "components/content_capture/browser/content_capture_receiver_manager.h" #include "content/public/browser/content_browser_client.h" #include "content/public/browser/web_contents.h" @@ -492,7 +493,15 @@ DidRemoveContent(expected_removed_ids()); } -TEST_F(ContentCaptureReceiverTest, ChildFrameCaptureContentFirst) { +// TODO(https://crbug.com/1010416): Fix flakes on win10_chromium_x64_rel_ng and +// re-enable this test. +#if defined(OS_WIN) +#define MAYBE_ChildFrameCaptureContentFirst \ + DISABLED_ChildFrameCaptureContentFirst +#else +#define MAYBE_ChildFrameCaptureContentFirst ChildFrameCaptureContentFirst +#endif +TEST_F(ContentCaptureReceiverTest, MAYBE_ChildFrameCaptureContentFirst) { // Simulate add child frame. SetupChildFrame(); // Simulate to capture the content from child frame. @@ -635,8 +644,16 @@ void TearDown() override { content::RenderViewHostTestHarness::TearDown(); } }; +// TODO(https://crbug.com/1010417): Fix flakes on win10_chromium_x64_rel_ng and +// re-enable this test. +#if defined(OS_WIN) +#define MAYBE_ReceiverCreatedForExistingFrame \ + DISABLED_ReceiverCreatedForExistingFrame +#else +#define MAYBE_ReceiverCreatedForExistingFrame ReceiverCreatedForExistingFrame +#endif TEST_F(ContentCaptureReceiverMultipleFrameTest, - ReceiverCreatedForExistingFrame) { + MAYBE_ReceiverCreatedForExistingFrame) { EXPECT_EQ( 2u, content_capture_receiver_manager_helper()->GetFrameMapSizeForTesting());
diff --git a/components/favicon/core/favicon_handler.cc b/components/favicon/core/favicon_handler.cc index 08c9c16..95a979c8 100644 --- a/components/favicon/core/favicon_handler.cc +++ b/components/favicon/core/favicon_handler.cc
@@ -226,8 +226,9 @@ // we get <link rel="icon"> candidates (FaviconHandler::OnUpdateCandidates()). service_->GetFaviconForPageURL( last_page_url_, icon_types_, preferred_icon_size(), - base::Bind(&FaviconHandler::OnFaviconDataForInitialURLFromFaviconService, - base::Unretained(this)), + base::BindOnce( + &FaviconHandler::OnFaviconDataForInitialURLFromFaviconService, + base::Unretained(this)), &cancelable_task_tracker_for_page_url_); } @@ -360,8 +361,9 @@ // mappings only if the manifest URL is cached. GetFaviconAndUpdateMappingsUnlessIncognito( /*icon_url=*/manifest_url_, favicon_base::IconType::kWebManifestIcon, - base::Bind(&FaviconHandler::OnFaviconDataForManifestFromFaviconService, - base::Unretained(this))); + base::BindOnce( + &FaviconHandler::OnFaviconDataForManifestFromFaviconService, + base::Unretained(this))); } void FaviconHandler::OnFaviconDataForManifestFromFaviconService( @@ -621,7 +623,7 @@ } else { GetFaviconAndUpdateMappingsUnlessIncognito( icon_url, icon_type, - base::Bind(&FaviconHandler::OnFaviconData, base::Unretained(this))); + base::BindOnce(&FaviconHandler::OnFaviconData, base::Unretained(this))); } }
diff --git a/components/favicon/ios/web_favicon_driver.mm b/components/favicon/ios/web_favicon_driver.mm index 37bc123..6efaf65 100644 --- a/components/favicon/ios/web_favicon_driver.mm +++ b/components/favicon/ios/web_favicon_driver.mm
@@ -23,14 +23,6 @@ #error "This file requires ARC support." #endif -// Callback for the download of favicon. -using ImageDownloadCallback = - base::Callback<void(int image_id, - int http_status_code, - const GURL& image_url, - const std::vector<SkBitmap>& bitmaps, - const std::vector<gfx::Size>& sizes)>; - namespace favicon { // static
diff --git a/components/flags_ui/resources/flags.css b/components/flags_ui/resources/flags.css index 0481846..58482c95 100644 --- a/components/flags_ui/resources/flags.css +++ b/components/flags_ui/resources/flags.css
@@ -37,7 +37,6 @@ --warning-color: var(--google-red-700); --input-background: var(--google-grey-100); - --input-placeholder-color: rgb(117, 117, 117); --keyboard-focus-ring: rgba(var(--google-blue-500-rgb), 0.4); --link-color: var(--google-blue-700); --separator-color: rgba(0, 0, 0, .06); @@ -179,10 +178,6 @@ width: 100%; } -#search::placeholder { - color: var(--input-placeholder-color); -} - @media (prefers-color-scheme: dark) { #search { background-image: url(../../../ui/webui/resources/images/dark/icon_search.svg);
diff --git a/components/neterror/resources/offline.js b/components/neterror/resources/offline.js index 504b2a89..f170440 100644 --- a/components/neterror/resources/offline.js +++ b/components/neterror/resources/offline.js
@@ -93,7 +93,14 @@ var IS_HIDPI = window.devicePixelRatio > 1; /** @const */ -var IS_IOS = /iPad|iPhone|iPod/.test(window.navigator.platform); +// iPads are returning "MacIntel" for iOS 13 (devices & simulators). +// Chrome on macOS also returns "MacIntel" for navigator.platform, +// but navigator.userAgent includes /Safari/. +// TODO(crbug.com/998999): Fix navigator.userAgent such that it reliably +// returns an agent string containing "CriOS". +var IS_IOS = /iPad|iPhone|iPod|MacIntel/.test(navigator.platform) && + !(/Safari/.test(navigator.userAgent)); + /** @const */ var IS_MOBILE = /Android/.test(window.navigator.userAgent) || IS_IOS;
diff --git a/components/omnibox/browser/location_bar_model_impl_unittest.cc b/components/omnibox/browser/location_bar_model_impl_unittest.cc index 5b9ec65a..32fdc79c 100644 --- a/components/omnibox/browser/location_bar_model_impl_unittest.cc +++ b/components/omnibox/browser/location_bar_model_impl_unittest.cc
@@ -7,6 +7,7 @@ #include "base/strings/utf_string_conversions.h" #include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" +#include "build/build_config.h" #include "components/omnibox/browser/location_bar_model_delegate.h" #include "components/omnibox/browser/test_omnibox_client.h" #include "components/omnibox/common/omnibox_features.h" @@ -110,7 +111,14 @@ model()->GetURLForDisplay()); } -TEST_F(LocationBarModelImplTest, PreventElisionWorks) { +// TODO(https://crbug.com/1010418): Fix flakes on linux_chromium_asan_rel_ng and +// re-enable this test. +#if defined(OS_LINUX) +#define MAYBE_PreventElisionWorks DISABLED_PreventElisionWorks +#else +#define MAYBE_PreventElisionWorks PreventElisionWorks +#endif +TEST_F(LocationBarModelImplTest, MAYBE_PreventElisionWorks) { base::test::ScopedFeatureList feature_list; feature_list.InitWithFeatures( {omnibox::kHideSteadyStateUrlScheme,
diff --git a/components/password_manager/core/browser/BUILD.gn b/components/password_manager/core/browser/BUILD.gn index 97a1dad6..b7dd6d7 100644 --- a/components/password_manager/core/browser/BUILD.gn +++ b/components/password_manager/core/browser/BUILD.gn
@@ -102,6 +102,8 @@ "import/password_csv_reader.h", "import/password_importer.cc", "import/password_importer.h", + "leak_detection_dialog_utils.cc", + "leak_detection_dialog_utils.h", "leaked_credentials_table.cc", "leaked_credentials_table.h", "login_database.cc", @@ -231,6 +233,7 @@ ":password_generator", ":password_hash_data", ":proto", + "//base", "//base:i18n", "//components/autofill/core/browser", "//components/autofill/core/browser/proto", @@ -247,6 +250,7 @@ "//components/security_state/core", "//components/signin/public/identity_manager", "//components/strings", + "//components/strings:components_strings", "//components/sync", "//components/sync_preferences", "//components/url_formatter", @@ -262,6 +266,7 @@ "//third_party/re2", "//ui/base", "//ui/gfx", + "//ui/gfx/range", "//url", ] @@ -275,8 +280,6 @@ "leak_detection_delegate.h", "leak_detection_delegate_helper.cc", "leak_detection_delegate_helper.h", - "leak_detection_dialog_utils.cc", - "leak_detection_dialog_utils.h", ] deps += [ "//components/password_manager/core/browser/leak_detection:leak_detection", @@ -514,6 +517,7 @@ "import/csv_reader_unittest.cc", "import/password_csv_reader_unittest.cc", "import/password_importer_unittest.cc", + "leak_detection_dialog_utils_unittest.cc", "leaked_credentials_table_unittest.cc", "login_database_unittest.cc", "multi_store_form_fetcher_unittest.cc", @@ -603,6 +607,7 @@ "//components/sync:test_support", "//components/sync_preferences:test_support", "//components/ukm:test_support", + "//components/url_formatter", "//components/variations", "//google_apis:google_apis", "//net:test_support",
diff --git a/components/password_manager/core/browser/leak_detection_dialog_utils.cc b/components/password_manager/core/browser/leak_detection_dialog_utils.cc index 654f5a5..40c89fcb 100644 --- a/components/password_manager/core/browser/leak_detection_dialog_utils.cc +++ b/components/password_manager/core/browser/leak_detection_dialog_utils.cc
@@ -4,8 +4,36 @@ #include "components/password_manager/core/browser/leak_detection_dialog_utils.h" +#include "base/metrics/field_trial_params.h" +#include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" +#include "components/password_manager/core/browser/leak_detection_dialog_utils.h" +#include "components/password_manager/core/common/password_manager_features.h" +#include "components/strings/grit/components_strings.h" +#include "components/url_formatter/elide_url.h" +#include "ui/base/l10n/l10n_util.h" +#include "url/gurl.h" +#include "url/origin.h" + namespace password_manager { +using metrics_util::LeakDialogType; + +constexpr char kPasswordCheckupURL[] = +#if defined(OS_ANDROID) + "https://passwords.google.com/checkup/" + "start?utm_source=chrome&utm_medium=android&utm_campaign=leak_dialog&crch=" + "true"; +#elif defined(OS_IOS) + "https://passwords.google.com/checkup/" + "start?utm_source=chrome&utm_medium=ios&utm_campaign=leak_dialog&crch=" + "true"; +#else + "https://passwords.google.com/checkup/" + "start?utm_source=chrome&utm_medium=desktop&utm_campaign=leak_dialog&crch=" + "true"; +#endif + CredentialLeakType CreateLeakType(IsSaved is_saved, IsReused is_reused, IsSyncing is_syncing) { @@ -31,4 +59,84 @@ return leak_type & CredentialLeakFlags::kSyncingPasswordsNormally; } +// Formats the |origin| to a human-friendly url string. +base::string16 GetFormattedUrl(const GURL& origin) { + return url_formatter::FormatUrlForSecurityDisplay( + origin, url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS); +} + +base::string16 GetAcceptButtonLabel(CredentialLeakType leak_type) { + return l10n_util::GetStringUTF16( + ShouldCheckPasswords(leak_type) ? IDS_LEAK_CHECK_CREDENTIALS : IDS_OK); +} + +base::string16 GetCancelButtonLabel() { + return l10n_util::GetStringUTF16(IDS_CLOSE); +} + +base::string16 GetDescription(CredentialLeakType leak_type, + const GURL& origin) { + const base::string16 formatted = GetFormattedUrl(origin); + if (!ShouldCheckPasswords(leak_type)) { + std::vector<size_t> offsets; + base::string16 bold_message = l10n_util::GetStringUTF16( + IDS_CREDENTIAL_LEAK_CHANGE_PASSWORD_BOLD_MESSAGE); + return l10n_util::GetStringFUTF16( + IDS_CREDENTIAL_LEAK_CHANGE_PASSWORD_MESSAGE, bold_message, formatted, + &offsets); + } else if (password_manager::IsPasswordSaved(leak_type)) { + return l10n_util::GetStringUTF16( + IDS_CREDENTIAL_LEAK_CHECK_PASSWORDS_MESSAGE); + } else { + return l10n_util::GetStringFUTF16( + IDS_CREDENTIAL_LEAK_CHANGE_AND_CHECK_PASSWORDS_MESSAGE, formatted); + } +} + +base::string16 GetTitle(CredentialLeakType leak_type) { + return l10n_util::GetStringUTF16(IDS_CREDENTIAL_LEAK_TITLE); +} + +bool ShouldCheckPasswords(CredentialLeakType leak_type) { + return password_manager::IsPasswordUsedOnOtherSites(leak_type) && + password_manager::IsSyncingPasswordsNormally(leak_type); +} + +bool ShouldShowCancelButton(CredentialLeakType leak_type) { + return ShouldCheckPasswords(leak_type); +} + +LeakDialogType GetLeakDialogType(CredentialLeakType leak_type) { + if (!ShouldCheckPasswords(leak_type)) + return LeakDialogType::kChange; + + return password_manager::IsPasswordSaved(leak_type) + ? LeakDialogType::kCheckup + : LeakDialogType::kCheckupAndChange; +} + +GURL GetPasswordCheckupURL() { + std::string value = base::GetFieldTrialParamValueByFeature( + password_manager::features::kLeakDetection, "leak-check-url"); + if (value.empty()) + return GURL(password_manager::kPasswordCheckupURL); + return GURL(value); +} + +gfx::Range GetChangePasswordBoldRange(CredentialLeakType leak_type, + const GURL& origin) { + if (ShouldCheckPasswords(leak_type)) + return gfx::Range(); + + std::vector<size_t> offsets; + const base::string16 bold_message = l10n_util::GetStringUTF16( + IDS_CREDENTIAL_LEAK_CHANGE_PASSWORD_BOLD_MESSAGE); + const base::string16 change_password_message = l10n_util::GetStringFUTF16( + IDS_CREDENTIAL_LEAK_CHANGE_PASSWORD_MESSAGE, bold_message, + GetFormattedUrl(origin), &offsets); + return offsets.empty() + ? gfx::Range() + : gfx::Range(offsets[0], offsets[0] + bold_message.length()); +} + } // namespace password_manager
diff --git a/components/password_manager/core/browser/leak_detection_dialog_utils.h b/components/password_manager/core/browser/leak_detection_dialog_utils.h index 867d864c..6b0a574 100644 --- a/components/password_manager/core/browser/leak_detection_dialog_utils.h +++ b/components/password_manager/core/browser/leak_detection_dialog_utils.h
@@ -7,7 +7,13 @@ #include <type_traits> +#include "base/macros.h" +#include "base/strings/string16.h" #include "base/util/type_safety/strong_alias.h" +#include "components/password_manager/core/browser/leak_detection_dialog_utils.h" +#include "components/password_manager/core/browser/password_manager_metrics_util.h" +#include "ui/gfx/range/range.h" +#include "url/gurl.h" namespace password_manager { @@ -42,6 +48,39 @@ // Checks whether user is syncing passwords with normal encryption. bool IsSyncingPasswordsNormally(CredentialLeakType leak_type); +// Returns the label for the leak dialog accept button. +base::string16 GetAcceptButtonLabel( + password_manager::CredentialLeakType leak_type); + +// Returns the label for the leak dialog cancel button. +base::string16 GetCancelButtonLabel(); + +// Returns the leak dialog message based on leak type. +base::string16 GetDescription(password_manager::CredentialLeakType leak_type, + const GURL& origin); + +// Returns the leak dialog title based on leak type. +base::string16 GetTitle(password_manager::CredentialLeakType leak_type); + +// Checks whether the leak dialog should prompt user to password checkup. +bool ShouldCheckPasswords(password_manager::CredentialLeakType leak_type); + +// Checks whether the leak dialog should show cancel button. +bool ShouldShowCancelButton(password_manager::CredentialLeakType leak_type); + +// Returns the LeakDialogType corresponding to |leak_type|. +password_manager::metrics_util::LeakDialogType GetLeakDialogType( + password_manager::CredentialLeakType leak_type); + +// Returns the URL used to launch the password checkup. +GURL GetPasswordCheckupURL(); + +// Returns the range of the bold part of the leak dialog message when +// credentials were leaked only on current site. +gfx::Range GetChangePasswordBoldRange( + password_manager::CredentialLeakType leak_type, + const GURL& origin); + } // namespace password_manager #endif // COMPONENTS_PASSWORD_MANAGER_CORE_BROWSER_LEAK_DETECTION_DIALOG_UTILS_H_
diff --git a/chrome/browser/ui/passwords/credential_leak_dialog_utils_unittest.cc b/components/password_manager/core/browser/leak_detection_dialog_utils_unittest.cc similarity index 96% rename from chrome/browser/ui/passwords/credential_leak_dialog_utils_unittest.cc rename to components/password_manager/core/browser/leak_detection_dialog_utils_unittest.cc index e79239f..7da6287 100644 --- a/chrome/browser/ui/passwords/credential_leak_dialog_utils_unittest.cc +++ b/components/password_manager/core/browser/leak_detection_dialog_utils_unittest.cc
@@ -2,10 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/ui/passwords/credential_leak_dialog_utils.h" -#include "base/strings/utf_string_conversions.h" -#include "chrome/grit/generated_resources.h" #include "components/password_manager/core/browser/leak_detection_dialog_utils.h" +#include "base/strings/utf_string_conversions.h" #include "components/strings/grit/components_strings.h" #include "components/url_formatter/elide_url.h" #include "testing/gtest/include/gtest/gtest.h" @@ -20,7 +18,7 @@ using password_manager::IsSaved; using password_manager::IsSyncing; -namespace leak_dialog_utils { +namespace password_manager { namespace { @@ -153,4 +151,4 @@ } } -} // namespace leak_dialog_utils +} // namespace password_manager
diff --git a/components/password_manager_strings.grdp b/components/password_manager_strings.grdp index c7985c1..0a89046 100644 --- a/components/password_manager_strings.grdp +++ b/components/password_manager_strings.grdp
@@ -1,6 +1,30 @@ <?xml version="1.0" encoding="utf-8"?> <grit-part> - + <if expr="use_titlecase"> + <message name="IDS_LEAK_CHECK_CREDENTIALS" desc="The text of the OK button in the dialog for credentials leaked on multiple sites."> + Check Passwords + </message> + </if> + <if expr="not use_titlecase"> + <message name="IDS_LEAK_CHECK_CREDENTIALS" desc="The text of the OK button in the dialog for credentials leaked on multiple sites."> + Check passwords + </message> + </if> + <message name="IDS_CREDENTIAL_LEAK_TITLE" desc="The title of the credential leak dialog."> + Data breach reported + </message> + <message name="IDS_CREDENTIAL_LEAK_CHECK_PASSWORDS_MESSAGE" desc="The text that is used in credential leak detection dialog when saved credentials were used on multiple sites."> + A data breach on a site or app you use exposed your password. Chrome recommends checking your saved passwords now. + </message> + <message name="IDS_CREDENTIAL_LEAK_CHANGE_PASSWORD_MESSAGE" desc="The text that is used in credential leak detection dialog when credentials were leaked on current site only."> + A data breach on a site or app you use exposed your password. Chrome recommends <ph name="BOLD">$1<ex>changing your password</ex></ph> on <ph name="ORIGIN">$2<ex>example.com</ex></ph> now. + </message> + <message name="IDS_CREDENTIAL_LEAK_CHANGE_PASSWORD_BOLD_MESSAGE" desc="The text that is written in bold in leak dialog message when credentials were leaked on current site only."> + changing your password + </message> + <message name="IDS_CREDENTIAL_LEAK_CHANGE_AND_CHECK_PASSWORDS_MESSAGE" desc="The text that is used in credential leak detection dialog when credentials were not saved in chrome, but used on multiple sites."> + A data breach on a site or app you use exposed your password. Chrome recommends checking your saved passwords and changing your password on <ph name="ORIGIN">$1<ex>example.com</ex></ph>. + </message> <message name="IDS_MANAGE_PASSWORDS_AUTO_SIGNIN_TITLE" desc="The title of the auto-signin toast."> Signing in as <ph name="username">$1<ex>chef@google.com</ex></ph> </message> @@ -35,5 +59,4 @@ <message name="IDS_PASSWORD_MANAGER_DEFAULT_EXPORT_FILENAME" desc="Chrome suggests this file name when user chooses to export their passwords saved with Chrome."> Chrome Passwords </message> - </grit-part>
diff --git a/components/policy/core/common/cloud/cloud_policy_client_unittest.cc b/components/policy/core/common/cloud/cloud_policy_client_unittest.cc index 8caff10..16ef0349 100644 --- a/components/policy/core/common/cloud/cloud_policy_client_unittest.cc +++ b/components/policy/core/common/cloud/cloud_policy_client_unittest.cc
@@ -1473,8 +1473,8 @@ event_list.Append(std::move(event)); client_->UploadRealtimeReport( - std::move(policy::RealtimeReportingJobConfiguration::BuildReport( - std::move(event_list), std::move(context))), + policy::RealtimeReportingJobConfiguration::BuildReport( + std::move(event_list), std::move(context)), callback); base::RunLoop().RunUntilIdle(); EXPECT_EQ(
diff --git a/components/security_interstitials/core/browser/resources/interstitial_safebrowsing.css b/components/security_interstitials/core/browser/resources/interstitial_safebrowsing.css index 98d78624..cfc256e 100644 --- a/components/security_interstitials/core/browser/resources/interstitial_safebrowsing.css +++ b/components/security_interstitials/core/browser/resources/interstitial_safebrowsing.css
@@ -64,7 +64,7 @@ (max-height: 560px) { body.safe-browsing .nav-wrapper { background: var(--google-red-600); - box-shadow: 0 -22px 40px var(--google-red-600); + box-shadow: 0 -12px 24px var(--google-red-600); } }
diff --git a/components/security_interstitials/core/common/resources/interstitial_common.css b/components/security_interstitials/core/common/resources/interstitial_common.css index f43f92c..0ca3c2ba 100644 --- a/components/security_interstitials/core/common/resources/interstitial_common.css +++ b/components/security_interstitials/core/common/resources/interstitial_common.css
@@ -132,7 +132,7 @@ #extended-reporting-opt-in { font-size: .875em; - margin-top: 39px; + margin-top: 32px; } #extended-reporting-opt-in label { @@ -167,7 +167,7 @@ display: block; height: 1em; left: -1em; - padding: var(--padding); + padding-inline-start: var(--padding); position: absolute; right: 0; top: -.5em; @@ -293,7 +293,7 @@ body .nav-wrapper { background: var(--background-color); bottom: 0; - box-shadow: 0 -22px 40px var(--background-color); + box-shadow: 0 -12px 24px var(--background-color); left: 0; margin: 0 auto; max-width: 736px; @@ -331,7 +331,8 @@ button, [dir='rtl'] button, - button.small-link { + button.small-link, + .nav-wrapper .secondary-button { font-family: Roboto-Regular,Helvetica; font-size: .933em; margin: 6px 0; @@ -418,6 +419,12 @@ } } +@media (max-height: 560px) and (min-height: 240px) and (orientation:landscape) { + .extended-reporting-has-checkbox #details { + padding-bottom: 80px; + } +} + @media (min-height: 500px) and (max-height: 650px) and (max-width: 414px) and (orientation: portrait) { .interstitial-wrapper { @@ -461,7 +468,8 @@ width: 100%; } - button { + button, + .nav-wrapper .secondary-button { padding: 16px 24px; }
diff --git a/components/signin/internal/identity_manager/profile_oauth2_token_service_builder.cc b/components/signin/internal/identity_manager/profile_oauth2_token_service_builder.cc index 4ccaa74..1a3546be 100644 --- a/components/signin/internal/identity_manager/profile_oauth2_token_service_builder.cc +++ b/components/signin/internal/identity_manager/profile_oauth2_token_service_builder.cc
@@ -23,7 +23,6 @@ #if defined(OS_CHROMEOS) #include "chromeos/components/account_manager/account_manager.h" -#include "chromeos/constants/chromeos_features.h" #include "components/signin/internal/identity_manager/profile_oauth2_token_service_delegate_chromeos.h" #include "components/user_manager/user_manager.h" #endif // defined(OS_CHROMEOS) @@ -57,8 +56,7 @@ signin_client, std::move(device_accounts_provider), account_tracker_service); } -#else // !defined(OS_ANDROID) && !defined(OS_IOS) -#if defined(OS_CHROMEOS) +#elif defined(OS_CHROMEOS) std::unique_ptr<signin::ProfileOAuth2TokenServiceDelegateChromeOS> CreateCrOsOAuthDelegate( AccountTrackerService* account_tracker_service, @@ -70,25 +68,7 @@ account_tracker_service, network_connection_tracker, account_manager, is_regular_profile); } -#endif // defined(OS_CHROMEOS) - -// Supervised users cannot revoke credentials. -bool CanRevokeCredentials() { -#if defined(OS_CHROMEOS) - // UserManager may not exist in unit_tests. - if (user_manager::UserManager::IsInitialized() && - user_manager::UserManager::Get()->IsLoggedInAsSupervisedUser()) { - // Don't allow revoking credentials for Chrome OS supervised users. - // See http://crbug.com/332032 - LOG(ERROR) << "Attempt to revoke supervised user refresh " - << "token detected, ignoring."; - return false; - } -#endif - - return true; -} - +#else std::unique_ptr<MutableProfileOAuth2TokenServiceDelegate> CreateMutableProfileOAuthDelegate( AccountTrackerService* account_tracker_service, @@ -110,7 +90,7 @@ return std::make_unique<MutableProfileOAuth2TokenServiceDelegate>( signin_client, account_tracker_service, network_connection_tracker, token_web_data, account_consistency, revoke_all_tokens_on_load, - CanRevokeCredentials(), + true /* can_revoke_credentials */, #if defined(OS_WIN) reauth_callback #else @@ -147,24 +127,19 @@ return CreateIOSOAuthDelegate(signin_client, std::move(device_accounts_provider), account_tracker_service); -#else // !defined(OS_ANDROID) && !defined(OS_IOS) -#if defined(OS_CHROMEOS) - if (chromeos::features::IsAccountManagerEnabled()) { - return CreateCrOsOAuthDelegate(account_tracker_service, - network_connection_tracker, account_manager, - is_regular_profile); - } -#endif // defined(OS_CHROMEOS) - // Fall back to |MutableProfileOAuth2TokenServiceDelegate|: - // 1. On all platforms other than Android and Chrome OS. - // 2. On Chrome OS, if Account Manager has not been switched on yet - // (chromeos::features::IsAccountManagerEnabled). +#elif defined(OS_CHROMEOS) + return CreateCrOsOAuthDelegate(account_tracker_service, + network_connection_tracker, account_manager, + is_regular_profile); +#else + // Fall back to |MutableProfileOAuth2TokenServiceDelegate| on all platforms + // other than Android, iOS, and Chrome OS. return CreateMutableProfileOAuthDelegate( account_tracker_service, account_consistency, delete_signin_cookies_on_exit, token_web_data, signin_client, #if defined(OS_WIN) reauth_callback, -#endif +#endif // defined(OS_WIN) network_connection_tracker); #endif // defined(OS_ANDROID)
diff --git a/components/sync/driver/glue/sync_engine_backend.cc b/components/sync/driver/glue/sync_engine_backend.cc index 99269df2..9e76e8d 100644 --- a/components/sync/driver/glue/sync_engine_backend.cc +++ b/components/sync/driver/glue/sync_engine_backend.cc
@@ -433,6 +433,12 @@ sync_manager_->GetEncryptionHandler()->SetEncryptionPassphrase(passphrase); } +void SyncEngineBackend::DoAddTrustedVaultDecryptionKeys( + const std::vector<std::string>& keys) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + sync_manager_->GetEncryptionHandler()->AddTrustedVaultDecryptionKeys(keys); +} + void SyncEngineBackend::DoInitialProcessControlTypes() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
diff --git a/components/sync/driver/glue/sync_engine_backend.h b/components/sync/driver/glue/sync_engine_backend.h index ade6f25..2b4ac88 100644 --- a/components/sync/driver/glue/sync_engine_backend.h +++ b/components/sync/driver/glue/sync_engine_backend.h
@@ -116,9 +116,12 @@ // Called to set the passphrase for encryption. void DoSetEncryptionPassphrase(const std::string& passphrase); - // Called to decrypt the pending keys. + // Called to decrypt the pending keys using user-entered passphrases. void DoSetDecryptionPassphrase(const std::string& passphrase); + // Called to decrypt the pending keys using trusted vault keys. + void DoAddTrustedVaultDecryptionKeys(const std::vector<std::string>& keys); + // Called to turn on encryption of all sync data as well as // reencrypt everything. void DoEnableEncryptEverything();
diff --git a/components/sync/driver/glue/sync_engine_impl.cc b/components/sync/driver/glue/sync_engine_impl.cc index fd54656..60c3bd7 100644 --- a/components/sync/driver/glue/sync_engine_impl.cc +++ b/components/sync/driver/glue/sync_engine_impl.cc
@@ -119,6 +119,15 @@ backend_, passphrase)); } +void SyncEngineImpl::AddTrustedVaultDecryptionKeys( + const std::vector<std::string>& keys) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + sync_task_runner_->PostTask( + FROM_HERE, + base::BindOnce(&SyncEngineBackend::DoAddTrustedVaultDecryptionKeys, + backend_, keys)); +} + void SyncEngineImpl::StopSyncingForShutdown() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
diff --git a/components/sync/driver/glue/sync_engine_impl.h b/components/sync/driver/glue/sync_engine_impl.h index e7a9c1f..71d2db1b 100644 --- a/components/sync/driver/glue/sync_engine_impl.h +++ b/components/sync/driver/glue/sync_engine_impl.h
@@ -64,6 +64,8 @@ void StartSyncingWithServer() override; void SetEncryptionPassphrase(const std::string& passphrase) override; void SetDecryptionPassphrase(const std::string& passphrase) override; + void AddTrustedVaultDecryptionKeys( + const std::vector<std::string>& keys) override; void StopSyncingForShutdown() override; void Shutdown(ShutdownReason reason) override; void ConfigureDataTypes(ConfigureParams params) override;
diff --git a/components/sync/driver/profile_sync_service.cc b/components/sync/driver/profile_sync_service.cc index 42e3b29..19c3d8f0 100644 --- a/components/sync/driver/profile_sync_service.cc +++ b/components/sync/driver/profile_sync_service.cc
@@ -1045,7 +1045,7 @@ // We should never get in a state where we have no encrypted datatypes // enabled, and yet we still think we require a passphrase for decryption. - DCHECK(!user_settings_->IsPassphraseRequiredForDecryption() || + DCHECK(!user_settings_->IsPassphraseRequiredForPreferredDataTypes() || user_settings_->IsEncryptedDatatypeEnabled()); // Notify listeners that configuration is done. @@ -1334,11 +1334,6 @@ return last_snapshot_; } -PassphraseRequiredReason -ProfileSyncService::GetPassphraseRequiredReasonForTest() const { - return crypto_.passphrase_required_reason(); -} - void ProfileSyncService::HasUnsyncedItemsForTest( base::OnceCallback<void(bool)> cb) const { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
diff --git a/components/sync/driver/profile_sync_service.h b/components/sync/driver/profile_sync_service.h index 19694857..4e68e16 100644 --- a/components/sync/driver/profile_sync_service.h +++ b/components/sync/driver/profile_sync_service.h
@@ -222,8 +222,6 @@ bool IsPassphrasePrompted() const; void SetPassphrasePrompted(bool prompted); - PassphraseRequiredReason GetPassphraseRequiredReasonForTest() const; - // Returns whether or not the underlying sync engine has made any // local changes to items that have not yet been synced with the // server.
diff --git a/components/sync/driver/sync_service_crypto.cc b/components/sync/driver/sync_service_crypto.cc index 799e52e..7e0838f 100644 --- a/components/sync/driver/sync_service_crypto.cc +++ b/components/sync/driver/sync_service_crypto.cc
@@ -48,6 +48,22 @@ observer_)); } + void OnTrustedVaultKeyRequired() override { + task_runner_->PostTask( + FROM_HERE, + base::BindOnce( + &SyncEncryptionHandler::Observer::OnTrustedVaultKeyRequired, + observer_)); + } + + void OnTrustedVaultKeyAccepted() override { + task_runner_->PostTask( + FROM_HERE, + base::BindOnce( + &SyncEncryptionHandler::Observer::OnTrustedVaultKeyAccepted, + observer_)); + } + void OnBootstrapTokenUpdated(const std::string& bootstrap_token, BootstrapTokenType type) override { task_runner_->PostTask( @@ -151,6 +167,22 @@ return state_.cached_explicit_passphrase_time; } +bool SyncServiceCrypto::IsPassphraseRequired() const { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + switch (state_.required_user_action) { + case RequiredUserAction::kNone: + break; + case RequiredUserAction::kPassphraseRequiredForDecryption: + case RequiredUserAction::kPassphraseRequiredForEncryption: + return true; + case RequiredUserAction::kTrustedVaultKeyRequired: + // TODO(crbug.com/1010189): This should return false and get exposed + // differently to upper layers. + return true; + } + return false; +} + bool SyncServiceCrypto::IsUsingSecondaryPassphrase() const { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); return IsExplicitPassphrase(state_.cached_passphrase_type); @@ -176,17 +208,20 @@ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); // This should only be called when the engine has been initialized. DCHECK(state_.engine); - DCHECK(state_.passphrase_required_reason != REASON_DECRYPTION) + DCHECK_NE(state_.required_user_action, + RequiredUserAction::kPassphraseRequiredForDecryption) << "Can not set explicit passphrase when decryption is needed."; DVLOG(1) << "Setting explicit passphrase for encryption."; - if (state_.passphrase_required_reason == REASON_ENCRYPTION) { - // REASON_ENCRYPTION implies that the cryptographer does not have pending - // keys. Hence, as long as we're not trying to do an invalid passphrase - // change (e.g. explicit -> explicit or explicit -> implicit), we know this - // will succeed. If for some reason a new encryption key arrives via - // sync later, the SBH will trigger another OnPassphraseRequired(). - state_.passphrase_required_reason = REASON_PASSPHRASE_NOT_REQUIRED; + if (state_.required_user_action == + RequiredUserAction::kPassphraseRequiredForEncryption) { + // |kPassphraseRequiredForEncryption| implies that the cryptographer does + // not have pending keys. Hence, as long as we're not trying to do an + // invalid passphrase change (e.g. explicit -> explicit or explicit -> + // implicit), we know this will succeed. If for some reason a new + // encryption key arrives via sync later, the SyncEncryptionHandler will + // trigger another OnPassphraseRequired(). + state_.required_user_action = RequiredUserAction::kNone; notify_observers_.Run(); } @@ -203,6 +238,13 @@ bool SyncServiceCrypto::SetDecryptionPassphrase(const std::string& passphrase) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // TODO(crbug.com/1010189): Move this logic to a separate function. + if (state_.required_user_action == + RequiredUserAction::kTrustedVaultKeyRequired) { + state_.engine->AddTrustedVaultDecryptionKeys({passphrase}); + return true; + } + // We should never be called with an empty passphrase. DCHECK(!passphrase.empty()); @@ -270,7 +312,17 @@ DVLOG(1) << "Passphrase required with reason: " << PassphraseRequiredReasonToString(reason); - state_.passphrase_required_reason = reason; + + switch (reason) { + case REASON_ENCRYPTION: + state_.required_user_action = + RequiredUserAction::kPassphraseRequiredForEncryption; + break; + case REASON_DECRYPTION: + state_.required_user_action = + RequiredUserAction::kPassphraseRequiredForDecryption; + break; + } // Reconfigure without the encrypted types (excluded implicitly via the // failed datatypes handler). @@ -283,15 +335,46 @@ // Clear our cache of the cryptographer's pending keys. state_.cached_pending_keys.clear_blob(); - // Reset |passphrase_required_reason| since we know we no longer require the + // Reset |required_user_action| since we know we no longer require the // passphrase. - state_.passphrase_required_reason = REASON_PASSPHRASE_NOT_REQUIRED; + state_.required_user_action = RequiredUserAction::kNone; // Make sure the data types that depend on the passphrase are started at // this time. reconfigure_.Run(CONFIGURE_REASON_CRYPTO); } +void SyncServiceCrypto::OnTrustedVaultKeyRequired() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + // To be on the safe since, if a passphrase is required, we avoid overriding + // |state_.required_user_action|. + if (state_.required_user_action != RequiredUserAction::kNone) { + return; + } + + state_.required_user_action = RequiredUserAction::kTrustedVaultKeyRequired; + + // Reconfigure without the encrypted types (excluded implicitly via the + // failed datatypes handler). + reconfigure_.Run(CONFIGURE_REASON_CRYPTO); +} + +void SyncServiceCrypto::OnTrustedVaultKeyAccepted() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + if (state_.required_user_action != + RequiredUserAction::kTrustedVaultKeyRequired) { + return; + } + + state_.required_user_action = RequiredUserAction::kNone; + + // Make sure the data types that depend on the decryption key are started at + // this time. + reconfigure_.Run(CONFIGURE_REASON_CRYPTO); +} + void SyncServiceCrypto::OnBootstrapTokenUpdated( const std::string& bootstrap_token, BootstrapTokenType type) {
diff --git a/components/sync/driver/sync_service_crypto.h b/components/sync/driver/sync_service_crypto.h index a9f08fdd..576a7060 100644 --- a/components/sync/driver/sync_service_crypto.h +++ b/components/sync/driver/sync_service_crypto.h
@@ -7,6 +7,7 @@ #include <memory> #include <string> +#include <vector> #include "base/callback.h" #include "base/memory/weak_ptr.h" @@ -35,11 +36,13 @@ // See the SyncService header. base::Time GetExplicitPassphraseTime() const; + bool IsPassphraseRequired() const; bool IsUsingSecondaryPassphrase() const; void EnableEncryptEverything(); bool IsEncryptEverythingEnabled() const; void SetEncryptionPassphrase(const std::string& passphrase); bool SetDecryptionPassphrase(const std::string& passphrase); + void AddTrustedVaultDecryptionKeys(const std::vector<std::string>& keys); // Returns the actual passphrase type being used for encryption. PassphraseType GetPassphraseType() const; @@ -53,6 +56,8 @@ const KeyDerivationParams& key_derivation_params, const sync_pb::EncryptedData& pending_keys) override; void OnPassphraseAccepted() override; + void OnTrustedVaultKeyRequired() override; + void OnTrustedVaultKeyAccepted() override; void OnBootstrapTokenUpdated(const std::string& bootstrap_token, BootstrapTokenType type) override; void OnEncryptedTypesChanged(ModelTypeSet encrypted_types, @@ -69,12 +74,16 @@ // Creates a proxy observer object that will post calls to this thread. std::unique_ptr<SyncEncryptionHandler::Observer> GetEncryptionObserverProxy(); - PassphraseRequiredReason passphrase_required_reason() const { - return state_.passphrase_required_reason; - } bool encryption_pending() const { return state_.encryption_pending; } private: + enum class RequiredUserAction { + kNone, + kPassphraseRequiredForDecryption, + kPassphraseRequiredForEncryption, + kTrustedVaultKeyRequired, + }; + // Calls SyncServiceBase::NotifyObservers(). Never null. const base::RepeatingClosure notify_observers_; @@ -95,11 +104,7 @@ // Not-null when the engine is initialized. SyncEngine* engine = nullptr; - // Was the last SYNC_PASSPHRASE_REQUIRED notification sent because it - // was required for encryption, decryption with a cached passphrase, or - // because a new passphrase is required? - PassphraseRequiredReason passphrase_required_reason = - REASON_PASSPHRASE_NOT_REQUIRED; + RequiredUserAction required_user_action = RequiredUserAction::kNone; // The current set of encrypted types. Always a superset of // Cryptographer::SensitiveTypes().
diff --git a/components/sync/driver/sync_user_settings.h b/components/sync/driver/sync_user_settings.h index d099c4d..0b05ede 100644 --- a/components/sync/driver/sync_user_settings.h +++ b/components/sync/driver/sync_user_settings.h
@@ -93,7 +93,7 @@ bool IsPassphraseRequired() const override = 0; // Whether a passphrase is required to decrypt the data for any currently // enabled data type. - virtual bool IsPassphraseRequiredForDecryption() const = 0; + virtual bool IsPassphraseRequiredForPreferredDataTypes() const = 0; // Whether a "secondary" passphrase is in use (aka explicit passphrase), which // means either a custom or a frozen implicit passphrase. virtual bool IsUsingSecondaryPassphrase() const = 0; @@ -108,6 +108,8 @@ // Asynchronously decrypts pending keys using |passphrase|. Returns false // immediately if the passphrase could not be used to decrypt a locally cached // copy of encrypted keys; returns true otherwise. + // TODO(crbug.com/1010189): Introduce a dedicated API for trusted vault + // decryption keys. virtual bool SetDecryptionPassphrase(const std::string& passphrase) WARN_UNUSED_RESULT = 0; };
diff --git a/components/sync/driver/sync_user_settings_impl.cc b/components/sync/driver/sync_user_settings_impl.cc index 30c1ea9..695d6d0 100644 --- a/components/sync/driver/sync_user_settings_impl.cc +++ b/components/sync/driver/sync_user_settings_impl.cc
@@ -125,11 +125,10 @@ } bool SyncUserSettingsImpl::IsPassphraseRequired() const { - return crypto_->passphrase_required_reason() != - REASON_PASSPHRASE_NOT_REQUIRED; + return crypto_->IsPassphraseRequired(); } -bool SyncUserSettingsImpl::IsPassphraseRequiredForDecryption() const { +bool SyncUserSettingsImpl::IsPassphraseRequiredForPreferredDataTypes() const { // If there is an encrypted datatype enabled and we don't have the proper // passphrase, we must prompt the user for a passphrase. The only way for the // user to avoid entering their passphrase is to disable the encrypted types.
diff --git a/components/sync/driver/sync_user_settings_impl.h b/components/sync/driver/sync_user_settings_impl.h index 56252b4..6fe27b3e 100644 --- a/components/sync/driver/sync_user_settings_impl.h +++ b/components/sync/driver/sync_user_settings_impl.h
@@ -53,7 +53,7 @@ ModelTypeSet GetEncryptedDataTypes() const override; bool IsPassphraseRequired() const override; - bool IsPassphraseRequiredForDecryption() const override; + bool IsPassphraseRequiredForPreferredDataTypes() const override; bool IsUsingSecondaryPassphrase() const override; base::Time GetExplicitPassphraseTime() const override; PassphraseType GetPassphraseType() const override;
diff --git a/components/sync/driver/sync_user_settings_mock.h b/components/sync/driver/sync_user_settings_mock.h index fd72dd3..96dc21a 100644 --- a/components/sync/driver/sync_user_settings_mock.h +++ b/components/sync/driver/sync_user_settings_mock.h
@@ -38,7 +38,7 @@ MOCK_CONST_METHOD0(GetEncryptedDataTypes, ModelTypeSet()); MOCK_CONST_METHOD0(IsPassphraseRequired, bool()); - MOCK_CONST_METHOD0(IsPassphraseRequiredForDecryption, bool()); + MOCK_CONST_METHOD0(IsPassphraseRequiredForPreferredDataTypes, bool()); MOCK_CONST_METHOD0(IsUsingSecondaryPassphrase, bool()); MOCK_CONST_METHOD0(GetExplicitPassphraseTime, base::Time()); MOCK_CONST_METHOD0(GetPassphraseType, PassphraseType());
diff --git a/components/sync/driver/test_sync_user_settings.cc b/components/sync/driver/test_sync_user_settings.cc index ce94b10..12c9a57 100644 --- a/components/sync/driver/test_sync_user_settings.cc +++ b/components/sync/driver/test_sync_user_settings.cc
@@ -122,7 +122,7 @@ return passphrase_required_; } -bool TestSyncUserSettings::IsPassphraseRequiredForDecryption() const { +bool TestSyncUserSettings::IsPassphraseRequiredForPreferredDataTypes() const { return passphrase_required_for_decryption_; }
diff --git a/components/sync/driver/test_sync_user_settings.h b/components/sync/driver/test_sync_user_settings.h index ba387ae..7019410 100644 --- a/components/sync/driver/test_sync_user_settings.h +++ b/components/sync/driver/test_sync_user_settings.h
@@ -42,7 +42,7 @@ syncer::ModelTypeSet GetEncryptedDataTypes() const override; bool IsPassphraseRequired() const override; - bool IsPassphraseRequiredForDecryption() const override; + bool IsPassphraseRequiredForPreferredDataTypes() const override; bool IsUsingSecondaryPassphrase() const override; base::Time GetExplicitPassphraseTime() const override; PassphraseType GetPassphraseType() const override;
diff --git a/components/sync/engine/fake_sync_engine.cc b/components/sync/engine/fake_sync_engine.cc index 9fee2c2..9318db8 100644 --- a/components/sync/engine/fake_sync_engine.cc +++ b/components/sync/engine/fake_sync_engine.cc
@@ -46,6 +46,9 @@ void FakeSyncEngine::SetDecryptionPassphrase(const std::string& passphrase) {} +void FakeSyncEngine::AddTrustedVaultDecryptionKeys( + const std::vector<std::string>& keys) {} + void FakeSyncEngine::StopSyncingForShutdown() {} void FakeSyncEngine::Shutdown(ShutdownReason reason) {}
diff --git a/components/sync/engine/fake_sync_engine.h b/components/sync/engine/fake_sync_engine.h index 6f858a8..0665e80 100644 --- a/components/sync/engine/fake_sync_engine.h +++ b/components/sync/engine/fake_sync_engine.h
@@ -7,6 +7,7 @@ #include <memory> #include <string> +#include <vector> #include "base/callback.h" #include "base/compiler_specific.h" @@ -45,6 +46,9 @@ void SetDecryptionPassphrase(const std::string& passphrase) override; + void AddTrustedVaultDecryptionKeys( + const std::vector<std::string>& keys) override; + void StopSyncingForShutdown() override; void Shutdown(ShutdownReason reason) override;
diff --git a/components/sync/engine/mock_sync_engine.h b/components/sync/engine/mock_sync_engine.h index 57705236..808207d 100644 --- a/components/sync/engine/mock_sync_engine.h +++ b/components/sync/engine/mock_sync_engine.h
@@ -7,6 +7,7 @@ #include <memory> #include <string> +#include <vector> #include "components/sync/engine/data_type_activation_response.h" #include "components/sync/engine/sync_engine.h" @@ -44,6 +45,8 @@ MOCK_METHOD0(StartSyncingWithServer, void()); MOCK_METHOD1(SetEncryptionPassphrase, void(const std::string&)); MOCK_METHOD1(SetDecryptionPassphrase, void(const std::string&)); + MOCK_METHOD1(AddTrustedVaultDecryptionKeys, + void(const std::vector<std::string>&)); MOCK_METHOD0(StopSyncingForShutdown, void()); MOCK_METHOD1(Shutdown, void(ShutdownReason)); MOCK_METHOD0(EnableEncryptEverything, void());
diff --git a/components/sync/engine/sync_encryption_handler.h b/components/sync/engine/sync_encryption_handler.h index c4d77cfb..c8c90651 100644 --- a/components/sync/engine/sync_encryption_handler.h +++ b/components/sync/engine/sync_encryption_handler.h
@@ -6,6 +6,7 @@ #define COMPONENTS_SYNC_ENGINE_SYNC_ENCRYPTION_HANDLER_H_ #include <string> +#include <vector> #include "base/time/time.h" #include "components/sync/base/model_type.h" @@ -21,7 +22,6 @@ // Reasons due to which Cryptographer might require a passphrase. enum PassphraseRequiredReason { - REASON_PASSPHRASE_NOT_REQUIRED = 0, // Initial value. REASON_ENCRYPTION = 1, // The cryptographer requires a // passphrase for its first attempt at // encryption. Happens only during @@ -77,6 +77,16 @@ // now used to encrypt sync data. virtual void OnPassphraseAccepted() = 0; + // Called when decryption keys are required in order to decrypt pending + // Nigori keys and resume sync, for the TRUSTED_VAULT_PASSPHRASE case. This + // can be resolved by calling AddTrustedVaultDecryptionKeys() with the + // appropriate keys. + virtual void OnTrustedVaultKeyRequired() = 0; + + // Called when the keys provided via AddTrustedVaultDecryptionKeys have been + // accepted and there are no longer pending keys. + virtual void OnTrustedVaultKeyAccepted() = 0; + // |bootstrap_token| is an opaque base64 encoded representation of the key // generated by the current passphrase, and is provided to the observer for // persistence purposes and use in a future initialization of sync (e.g. @@ -162,6 +172,14 @@ // be empty. virtual void SetDecryptionPassphrase(const std::string& passphrase) = 0; + // Analogous to SetDecryptionPassphrase but specifically for + // TRUSTED_VAULT_PASSPHRASE: it provides new decryption keys that could + // allow decrypting pending Nigori keys. Notifies observers of the result of + // the operation via OnTrustedVaultKeyAccepted if the provided keys + // successfully decrypted pending keys. + virtual void AddTrustedVaultDecryptionKeys( + const std::vector<std::string>& keys) = 0; + // Enables encryption of all datatypes. virtual void EnableEncryptEverything() = 0;
diff --git a/components/sync/engine/sync_engine.h b/components/sync/engine/sync_engine.h index 0cd3776e..5037d65 100644 --- a/components/sync/engine/sync_engine.h +++ b/components/sync/engine/sync_engine.h
@@ -8,6 +8,7 @@ #include <map> #include <memory> #include <string> +#include <vector> #include "base/callback.h" #include "base/compiler_specific.h" @@ -134,6 +135,14 @@ // are no pending keys. virtual void SetDecryptionPassphrase(const std::string& passphrase) = 0; + // Analogous to SetDecryptionPassphrase but specifically for + // TRUSTED_VAULT_PASSPHRASE: it provides new decryption keys that could + // allow decrypting pending Nigori keys. Notifies observers of the result of + // the operation via OnTrustedVaultKeyAccepted if the provided keys + // successfully decrypted pending keys. + virtual void AddTrustedVaultDecryptionKeys( + const std::vector<std::string>& keys) = 0; + // Kick off shutdown procedure. Attempts to cut short any long-lived or // blocking sync thread tasks so that the shutdown on sync thread task that // we're about to post won't have to wait very long.
diff --git a/components/sync/engine/sync_string_conversions.cc b/components/sync/engine/sync_string_conversions.cc index f168555..0315bde 100644 --- a/components/sync/engine/sync_string_conversions.cc +++ b/components/sync/engine/sync_string_conversions.cc
@@ -25,7 +25,6 @@ // Helper function that converts a PassphraseRequiredReason value to a string. const char* PassphraseRequiredReasonToString(PassphraseRequiredReason reason) { switch (reason) { - ENUM_CASE(REASON_PASSPHRASE_NOT_REQUIRED); ENUM_CASE(REASON_ENCRYPTION); ENUM_CASE(REASON_DECRYPTION); }
diff --git a/components/sync/engine_impl/debug_info_event_listener.cc b/components/sync/engine_impl/debug_info_event_listener.cc index f9f3b11..7ff9037 100644 --- a/components/sync/engine_impl/debug_info_event_listener.cc +++ b/components/sync/engine_impl/debug_info_event_listener.cc
@@ -74,6 +74,16 @@ CreateAndAddEvent(sync_pb::SyncEnums::PASSPHRASE_ACCEPTED); } +void DebugInfoEventListener::OnTrustedVaultKeyRequired() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + CreateAndAddEvent(sync_pb::SyncEnums::TRUSTED_VAULT_KEY_REQUIRED); +} + +void DebugInfoEventListener::OnTrustedVaultKeyAccepted() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + CreateAndAddEvent(sync_pb::SyncEnums::TRUSTED_VAULT_KEY_ACCEPTED); +} + void DebugInfoEventListener::OnBootstrapTokenUpdated( const std::string& bootstrap_token, BootstrapTokenType type) {
diff --git a/components/sync/engine_impl/debug_info_event_listener.h b/components/sync/engine_impl/debug_info_event_listener.h index 5510a27..e3d89ae 100644 --- a/components/sync/engine_impl/debug_info_event_listener.h +++ b/components/sync/engine_impl/debug_info_event_listener.h
@@ -58,6 +58,8 @@ const KeyDerivationParams& key_derivation_params, const sync_pb::EncryptedData& pending_keys) override; void OnPassphraseAccepted() override; + void OnTrustedVaultKeyRequired() override; + void OnTrustedVaultKeyAccepted() override; void OnBootstrapTokenUpdated(const std::string& bootstrap_token, BootstrapTokenType type) override; void OnEncryptedTypesChanged(ModelTypeSet encrypted_types,
diff --git a/components/sync/engine_impl/js_sync_encryption_handler_observer.cc b/components/sync/engine_impl/js_sync_encryption_handler_observer.cc index 490ac6b0..2ad5e6ef 100644 --- a/components/sync/engine_impl/js_sync_encryption_handler_observer.cc +++ b/components/sync/engine_impl/js_sync_encryption_handler_observer.cc
@@ -48,6 +48,24 @@ HandleJsEvent(FROM_HERE, "onPassphraseAccepted", JsEventDetails(&details)); } +void JsSyncEncryptionHandlerObserver::OnTrustedVaultKeyRequired() { + if (!event_handler_.IsInitialized()) { + return; + } + base::DictionaryValue details; + HandleJsEvent(FROM_HERE, "OnTrustedVaultKeyRequired", + JsEventDetails(&details)); +} + +void JsSyncEncryptionHandlerObserver::OnTrustedVaultKeyAccepted() { + if (!event_handler_.IsInitialized()) { + return; + } + base::DictionaryValue details; + HandleJsEvent(FROM_HERE, "OnTrustedVaultKeyAccepted", + JsEventDetails(&details)); +} + void JsSyncEncryptionHandlerObserver::OnBootstrapTokenUpdated( const std::string& boostrap_token, BootstrapTokenType type) {
diff --git a/components/sync/engine_impl/js_sync_encryption_handler_observer.h b/components/sync/engine_impl/js_sync_encryption_handler_observer.h index 07bab58..51188b05 100644 --- a/components/sync/engine_impl/js_sync_encryption_handler_observer.h +++ b/components/sync/engine_impl/js_sync_encryption_handler_observer.h
@@ -36,6 +36,8 @@ const KeyDerivationParams& key_derivation_params, const sync_pb::EncryptedData& pending_keys) override; void OnPassphraseAccepted() override; + void OnTrustedVaultKeyRequired() override; + void OnTrustedVaultKeyAccepted() override; void OnBootstrapTokenUpdated(const std::string& bootstrap_token, BootstrapTokenType type) override; void OnEncryptedTypesChanged(ModelTypeSet encrypted_types,
diff --git a/components/sync/engine_impl/js_sync_encryption_handler_observer_unittest.cc b/components/sync/engine_impl/js_sync_encryption_handler_observer_unittest.cc index 2654b0d..702d87e 100644 --- a/components/sync/engine_impl/js_sync_encryption_handler_observer_unittest.cc +++ b/components/sync/engine_impl/js_sync_encryption_handler_observer_unittest.cc
@@ -63,9 +63,6 @@ base::DictionaryValue reason_encryption_details; base::DictionaryValue reason_decryption_details; - reason_passphrase_not_required_details.SetString( - "reason", - PassphraseRequiredReasonToString(REASON_PASSPHRASE_NOT_REQUIRED)); reason_encryption_details.SetString( "reason", PassphraseRequiredReasonToString(REASON_ENCRYPTION)); reason_decryption_details.SetString( @@ -73,19 +70,12 @@ EXPECT_CALL(mock_js_event_handler_, HandleJsEvent("onPassphraseRequired", - HasDetailsAsDictionary( - reason_passphrase_not_required_details))); - EXPECT_CALL(mock_js_event_handler_, - HandleJsEvent("onPassphraseRequired", HasDetailsAsDictionary(reason_encryption_details))); EXPECT_CALL(mock_js_event_handler_, HandleJsEvent("onPassphraseRequired", HasDetailsAsDictionary(reason_decryption_details))); js_sync_encryption_handler_observer_.OnPassphraseRequired( - REASON_PASSPHRASE_NOT_REQUIRED, KeyDerivationParams::CreateForPbkdf2(), - sync_pb::EncryptedData()); - js_sync_encryption_handler_observer_.OnPassphraseRequired( REASON_ENCRYPTION, KeyDerivationParams::CreateForPbkdf2(), sync_pb::EncryptedData()); js_sync_encryption_handler_observer_.OnPassphraseRequired(
diff --git a/components/sync/engine_impl/model_type_registry.cc b/components/sync/engine_impl/model_type_registry.cc index eb6ba28..e134cea 100644 --- a/components/sync/engine_impl/model_type_registry.cc +++ b/components/sync/engine_impl/model_type_registry.cc
@@ -329,6 +329,16 @@ } } +void ModelTypeRegistry::OnTrustedVaultKeyRequired() {} + +void ModelTypeRegistry::OnTrustedVaultKeyAccepted() { + for (const auto& worker : model_type_workers_) { + if (encrypted_types_.Has(worker->GetModelType())) { + worker->EncryptionAcceptedMaybeApplyUpdates(); + } + } +} + void ModelTypeRegistry::OnBootstrapTokenUpdated( const std::string& bootstrap_token, BootstrapTokenType type) {}
diff --git a/components/sync/engine_impl/model_type_registry.h b/components/sync/engine_impl/model_type_registry.h index 2c3cdb1..79ce5f09 100644 --- a/components/sync/engine_impl/model_type_registry.h +++ b/components/sync/engine_impl/model_type_registry.h
@@ -78,6 +78,8 @@ const KeyDerivationParams& key_derivation_params, const sync_pb::EncryptedData& pending_keys) override; void OnPassphraseAccepted() override; + void OnTrustedVaultKeyRequired() override; + void OnTrustedVaultKeyAccepted() override; void OnBootstrapTokenUpdated(const std::string& bootstrap_token, BootstrapTokenType type) override; void OnEncryptedTypesChanged(ModelTypeSet encrypted_types,
diff --git a/components/sync/engine_impl/sync_encryption_handler_impl.cc b/components/sync/engine_impl/sync_encryption_handler_impl.cc index 5d93f3e..c43ed337 100644 --- a/components/sync/engine_impl/sync_encryption_handler_impl.cc +++ b/components/sync/engine_impl/sync_encryption_handler_impl.cc
@@ -766,6 +766,12 @@ FinishSetPassphrase(success, bootstrap_token, &trans, &node); } +void SyncEncryptionHandlerImpl::AddTrustedVaultDecryptionKeys( + const std::vector<std::string>& keys) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + NOTIMPLEMENTED(); +} + void SyncEncryptionHandlerImpl::EnableEncryptEverything() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); WriteTransaction trans(FROM_HERE, user_share_);
diff --git a/components/sync/engine_impl/sync_encryption_handler_impl.h b/components/sync/engine_impl/sync_encryption_handler_impl.h index 4fc51829..db85ef6 100644 --- a/components/sync/engine_impl/sync_encryption_handler_impl.h +++ b/components/sync/engine_impl/sync_encryption_handler_impl.h
@@ -67,6 +67,8 @@ bool Init() override; void SetEncryptionPassphrase(const std::string& passphrase) override; void SetDecryptionPassphrase(const std::string& passphrase) override; + void AddTrustedVaultDecryptionKeys( + const std::vector<std::string>& keys) override; void EnableEncryptEverything() override; bool IsEncryptEverythingEnabled() const override; base::Time GetKeystoreMigrationTime() const override;
diff --git a/components/sync/engine_impl/sync_encryption_handler_impl_unittest.cc b/components/sync/engine_impl/sync_encryption_handler_impl_unittest.cc index 80c17edc..0c93b3a 100644 --- a/components/sync/engine_impl/sync_encryption_handler_impl_unittest.cc +++ b/components/sync/engine_impl/sync_encryption_handler_impl_unittest.cc
@@ -72,6 +72,8 @@ const KeyDerivationParams&, const sync_pb::EncryptedData&)); // NOLINT MOCK_METHOD0(OnPassphraseAccepted, void()); // NOLINT + MOCK_METHOD0(OnTrustedVaultKeyRequired, void()); // NOLINT + MOCK_METHOD0(OnTrustedVaultKeyAccepted, void()); // NOLINT MOCK_METHOD2(OnBootstrapTokenUpdated, void(const std::string&, BootstrapTokenType type)); // NOLINT MOCK_METHOD2(OnEncryptedTypesChanged, void(ModelTypeSet, bool)); // NOLINT
diff --git a/components/sync/engine_impl/sync_manager_impl.cc b/components/sync/engine_impl/sync_manager_impl.cc index 3aa864ce..5d88716c 100644 --- a/components/sync/engine_impl/sync_manager_impl.cc +++ b/components/sync/engine_impl/sync_manager_impl.cc
@@ -430,6 +430,14 @@ // Does nothing. } +void SyncManagerImpl::OnTrustedVaultKeyRequired() { + // Does nothing. +} + +void SyncManagerImpl::OnTrustedVaultKeyAccepted() { + // Does nothing. +} + void SyncManagerImpl::OnBootstrapTokenUpdated( const std::string& bootstrap_token, BootstrapTokenType type) {
diff --git a/components/sync/engine_impl/sync_manager_impl.h b/components/sync/engine_impl/sync_manager_impl.h index 150b3173..4f2eb97 100644 --- a/components/sync/engine_impl/sync_manager_impl.h +++ b/components/sync/engine_impl/sync_manager_impl.h
@@ -117,6 +117,8 @@ const KeyDerivationParams& key_derivation_params, const sync_pb::EncryptedData& pending_keys) override; void OnPassphraseAccepted() override; + void OnTrustedVaultKeyRequired() override; + void OnTrustedVaultKeyAccepted() override; void OnBootstrapTokenUpdated(const std::string& bootstrap_token, BootstrapTokenType type) override; void OnEncryptedTypesChanged(ModelTypeSet encrypted_types,
diff --git a/components/sync/engine_impl/sync_manager_impl_unittest.cc b/components/sync/engine_impl/sync_manager_impl_unittest.cc index c1fd8ba..d1acbc2 100644 --- a/components/sync/engine_impl/sync_manager_impl_unittest.cc +++ b/components/sync/engine_impl/sync_manager_impl_unittest.cc
@@ -911,6 +911,8 @@ const KeyDerivationParams&, const sync_pb::EncryptedData&)); // NOLINT MOCK_METHOD0(OnPassphraseAccepted, void()); // NOLINT + MOCK_METHOD0(OnTrustedVaultKeyRequired, void()); // NOLINT + MOCK_METHOD0(OnTrustedVaultKeyAccepted, void()); // NOLINT MOCK_METHOD2(OnBootstrapTokenUpdated, void(const std::string&, BootstrapTokenType type)); // NOLINT MOCK_METHOD2(OnEncryptedTypesChanged, void(ModelTypeSet, bool)); // NOLINT
diff --git a/components/sync/nigori/nigori_sync_bridge_impl.cc b/components/sync/nigori/nigori_sync_bridge_impl.cc index 6d4c2b66..0f7b8db6 100644 --- a/components/sync/nigori/nigori_sync_bridge_impl.cc +++ b/components/sync/nigori/nigori_sync_bridge_impl.cc
@@ -70,18 +70,22 @@ return cryptographer.Encrypt(proto.key_bag(), encrypted); } -// TODO(crbug.com/922900): This should be revisited because the encryption key -// should be determined by keystore keys, which does not always match the -// default encryption key. bool EncryptKeystoreDecryptorToken( const CryptographerImpl& cryptographer, - sync_pb::EncryptedData* keystore_decryptor_token) { + sync_pb::EncryptedData* keystore_decryptor_token, + const std::vector<std::string>& keystore_keys) { DCHECK(keystore_decryptor_token); const sync_pb::NigoriKey default_key = cryptographer.ExportDefaultKey(); - return cryptographer.EncryptString(default_key.SerializeAsString(), - keystore_decryptor_token); + // TODO(crbug.com/922900): consider maintaining cached version of this + // |keystore_cryptographer| to avoid its creation here and inside + // DecryptKestoreDecryptorToken(). + std::unique_ptr<CryptographerImpl> keystore_cryptographer = + CreateCryptographerFromKeystoreKeys(keystore_keys); + + return keystore_cryptographer->EncryptString(default_key.SerializeAsString(), + keystore_decryptor_token); } // Attempts to decrypt |keystore_decryptor_token| with |keystore_keys|. Returns @@ -131,7 +135,8 @@ NigoriSpecifics specifics; if (!EncryptKeystoreDecryptorToken( - *cryptographer, specifics.mutable_keystore_decryptor_token())) { + *cryptographer, specifics.mutable_keystore_decryptor_token(), + keystore_keys)) { DLOG(ERROR) << "Failed to encrypt default key as keystore_decryptor_token."; return base::nullopt; } @@ -677,9 +682,7 @@ return; } - sync_pb::NigoriKeyBag decrypted_pending_keys; - if (!state_.cryptographer->Decrypt(*state_.pending_keys, - &decrypted_pending_keys)) { + if (!TryDecryptPendingKeys()) { // TODO(crbug.com/922900): old implementation assumes that pending keys // encryption key may change in between of OnPassphraseRequired() and // SetDecryptionPassphrase() calls, verify whether it's really possible. @@ -691,10 +694,7 @@ return; } - state_.cryptographer->EmplaceKeysFrom( - NigoriKeyBag::CreateFromProto(decrypted_pending_keys)); state_.cryptographer->SelectDefaultEncryptionKey(new_key_name); - state_.pending_keys.reset(); storage_->StoreData(SerializeAsNigoriLocalData()); for (auto& observer : observers_) { @@ -711,6 +711,45 @@ NOTIMPLEMENTED(); } +void NigoriSyncBridgeImpl::AddTrustedVaultDecryptionKeys( + const std::vector<std::string>& keys) { + // This API gets plumbed and ultimately exposed to layers outside the sync + // codebase and even outside the browser, so there are no preconditions and + // instead we ignore invalid or partially invalid input. + if (state_.passphrase_type != NigoriSpecifics::TRUSTED_VAULT_PASSPHRASE || + !state_.pending_keys || keys.empty()) { + return; + } + + for (const std::string& key : keys) { + if (!key.empty()) { + state_.cryptographer->EmplaceKey(key, + GetKeyDerivationParamsForPendingKeys()); + } + } + + const std::string pending_key_name = state_.pending_keys->key_name(); + + if (TryDecryptPendingKeys()) { + state_.cryptographer->SelectDefaultEncryptionKey(pending_key_name); + } + + storage_->StoreData(SerializeAsNigoriLocalData()); + + for (auto& observer : observers_) { + observer.OnCryptographerStateChanged(state_.cryptographer.get(), + state_.pending_keys.has_value()); + } + + if (!state_.pending_keys) { + for (auto& observer : observers_) { + observer.OnTrustedVaultKeyAccepted(); + } + } + + MaybeNotifyBootstrapTokenUpdated(); +} + void NigoriSyncBridgeImpl::EnableEncryptEverything() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); NOTIMPLEMENTED(); @@ -910,15 +949,37 @@ observer.OnCryptographerStateChanged(state_.cryptographer.get(), state_.pending_keys.has_value()); } - if (state_.pending_keys.has_value()) { - // Update with keystore Nigori shouldn't reach this point, since it should - // report model error if it has pending keys. - for (auto& observer : observers_) { - observer.OnPassphraseRequired(REASON_DECRYPTION, - GetKeyDerivationParamsForPendingKeys(), - *state_.pending_keys); - } + + if (!state_.pending_keys.has_value()) { + return base::nullopt; } + + switch (state_.passphrase_type) { + case NigoriSpecifics::UNKNOWN: + case NigoriSpecifics::IMPLICIT_PASSPHRASE: + NOTREACHED(); + break; + case NigoriSpecifics::KEYSTORE_PASSPHRASE: { + // Update with keystore Nigori shouldn't reach this point, since it should + // report model error if it has pending keys. + NOTREACHED(); + break; + } + case NigoriSpecifics::CUSTOM_PASSPHRASE: + case NigoriSpecifics::FROZEN_IMPLICIT_PASSPHRASE: + for (auto& observer : observers_) { + observer.OnPassphraseRequired(REASON_DECRYPTION, + GetKeyDerivationParamsForPendingKeys(), + *state_.pending_keys); + } + break; + case NigoriSpecifics::TRUSTED_VAULT_PASSPHRASE: + for (auto& observer : observers_) { + observer.OnTrustedVaultKeyRequired(); + } + break; + } + return base::nullopt; } @@ -979,6 +1040,18 @@ state_.cryptographer->EmplaceKeysFrom(NigoriKeyBag::CreateFromProto(key_bag)); } +bool NigoriSyncBridgeImpl::TryDecryptPendingKeys() { + sync_pb::NigoriKeyBag decrypted_pending_keys; + if (!state_.cryptographer->Decrypt(*state_.pending_keys, + &decrypted_pending_keys)) { + return false; + } + state_.cryptographer->EmplaceKeysFrom( + NigoriKeyBag::CreateFromProto(decrypted_pending_keys)); + state_.pending_keys.reset(); + return true; +} + std::unique_ptr<EntityData> NigoriSyncBridgeImpl::GetData() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (state_.passphrase_type == NigoriSpecifics::UNKNOWN) { @@ -1013,7 +1086,8 @@ } if (state_.passphrase_type == NigoriSpecifics::KEYSTORE_PASSPHRASE) { EncryptKeystoreDecryptorToken(*state_.cryptographer, - specifics.mutable_keystore_decryptor_token()); + specifics.mutable_keystore_decryptor_token(), + state_.keystore_keys); } if (!state_.keystore_migration_time.is_null()) { specifics.set_keystore_migration_time(
diff --git a/components/sync/nigori/nigori_sync_bridge_impl.h b/components/sync/nigori/nigori_sync_bridge_impl.h index fbf29e0b..7f05601 100644 --- a/components/sync/nigori/nigori_sync_bridge_impl.h +++ b/components/sync/nigori/nigori_sync_bridge_impl.h
@@ -60,6 +60,8 @@ bool Init() override; void SetEncryptionPassphrase(const std::string& passphrase) override; void SetDecryptionPassphrase(const std::string& passphrase) override; + void AddTrustedVaultDecryptionKeys( + const std::vector<std::string>& keys) override; void EnableEncryptEverything() override; bool IsEncryptEverythingEnabled() const override; base::Time GetKeystoreMigrationTime() const override; @@ -102,6 +104,12 @@ void UpdateCryptographerFromNonKeystoreNigori( const sync_pb::EncryptedData& keybag); + // Uses the cryptographer to try to decrypt pending keys. If success, the + // newly decrypted keys are put in the cryptographer's keybag, pending keys + // are cleared and the function returns true. Otherwise, it returns false and + // the state remains unchanged. It does not change the default key. + bool TryDecryptPendingKeys(); + base::Time GetExplicitPassphraseTime() const; // Returns key derivation params based on |passphrase_type_| and
diff --git a/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc b/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc index 6fc8832..24782a0 100644 --- a/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc +++ b/components/sync/nigori/nigori_sync_bridge_impl_unittest.cc
@@ -108,6 +108,21 @@ given.blob() == expected.blob(); } +MATCHER_P3(EncryptedDataEqAfterDecryption, + expected, + password, + derivation_params, + "") { + const sync_pb::EncryptedData& given = arg; + std::unique_ptr<CryptographerImpl> cryptographer = + CryptographerImpl::FromSingleKeyForTesting(password, derivation_params); + std::string decrypted_given; + EXPECT_TRUE(cryptographer->DecryptToString(given, &decrypted_given)); + std::string decrypted_expected; + EXPECT_TRUE(cryptographer->DecryptToString(expected, &decrypted_expected)); + return decrypted_given == decrypted_expected; +} + MATCHER_P2(IsDummyNigoriMetadataBatchWithTokenAndSequenceNumber, expected_token, expected_sequence_number, @@ -293,6 +308,8 @@ const KeyDerivationParams&, const sync_pb::EncryptedData&)); MOCK_METHOD0(OnPassphraseAccepted, void()); + MOCK_METHOD0(OnTrustedVaultKeyRequired, void()); + MOCK_METHOD0(OnTrustedVaultKeyAccepted, void()); MOCK_METHOD2(OnBootstrapTokenUpdated, void(const std::string&, BootstrapTokenType type)); MOCK_METHOD2(OnEncryptedTypesChanged, void(ModelTypeSet, bool)); @@ -450,6 +467,42 @@ EXPECT_THAT(cryptographer, HasDefaultKeyDerivedFrom(kGaiaKeyParams)); } +TEST_F(NigoriSyncBridgeImplTest, ShouldExposeBackwardCompatibleKeystoreNigori) { + const KeyParams kGaiaKeyParams = Pbkdf2KeyParams("gaia_key"); + const std::string kRawKeystoreKey = "raw_keystore_key"; + const KeyParams kKeystoreKeyParams = KeystoreKeyParams(kRawKeystoreKey); + EntityData entity_data; + *entity_data.specifics.mutable_nigori() = BuildKeystoreNigoriSpecifics( + /*keybag_keys_params=*/{kGaiaKeyParams, kKeystoreKeyParams}, + /*keystore_decryptor_params=*/kGaiaKeyParams, + /*keystore_key_params=*/kKeystoreKeyParams); + sync_pb::EncryptedData original_encryption_keybag = + entity_data.specifics.nigori().encryption_keybag(); + sync_pb::EncryptedData original_keystore_decryptor_token = + entity_data.specifics.nigori().keystore_decryptor_token(); + + ASSERT_TRUE(bridge()->SetKeystoreKeys({kRawKeystoreKey})); + ASSERT_THAT(bridge()->MergeSyncData(std::move(entity_data)), + Eq(base::nullopt)); + + std::unique_ptr<EntityData> local_entity_data = bridge()->GetData(); + ASSERT_TRUE(local_entity_data); + ASSERT_TRUE(local_entity_data->specifics.has_nigori()); + // Note: EncryptedDataEqAfterDecryption() exercises more strict requirements + // than bridge must support, because there is nothing wrong with reordering + // of the keys in encryption_keybag, which will lead to failing this + // expectation. + EXPECT_THAT(local_entity_data->specifics.nigori().encryption_keybag(), + EncryptedDataEqAfterDecryption(original_encryption_keybag, + kGaiaKeyParams.password, + kGaiaKeyParams.derivation_params)); + EXPECT_THAT( + local_entity_data->specifics.nigori().keystore_decryptor_token(), + EncryptedDataEqAfterDecryption(original_keystore_decryptor_token, + kKeystoreKeyParams.password, + kKeystoreKeyParams.derivation_params)); +} + // Tests that we can successfully use old keys from encryption_keybag in // backward compatible mode. TEST_F(NigoriSyncBridgeImplTest, @@ -799,10 +852,10 @@ // the sync protocol. TEST_F(NigoriSyncBridgeImplTest, ShouldRequireUserActionIfInitiallyUsingTrustedVault) { - const std::string kTrustedVaultPassphrase = "trusted_vault_passphrase"; + const std::string kTrustedVaultKey = "trusted_vault_key"; EntityData entity_data; - *entity_data.specifics.mutable_nigori() = BuildTrustedVaultNigoriSpecifics( - {Pbkdf2KeyParams(kTrustedVaultPassphrase)}); + *entity_data.specifics.mutable_nigori() = + BuildTrustedVaultNigoriSpecifics({Pbkdf2KeyParams(kTrustedVaultKey)}); ASSERT_TRUE(bridge()->SetKeystoreKeys({"keystore_key"})); @@ -811,9 +864,7 @@ EXPECT_CALL(*observer(), OnPassphraseTypeChanged(PassphraseType::kTrustedVaultPassphrase, NullTime())); - EXPECT_CALL(*observer(), OnPassphraseRequired(/*reason=*/REASON_DECRYPTION, - /*key_derivation_params=*/_, - /*pending_keys=*/_)); + EXPECT_CALL(*observer(), OnTrustedVaultKeyRequired()); EXPECT_THAT(bridge()->MergeSyncData(std::move(entity_data)), Eq(base::nullopt)); EXPECT_THAT(bridge()->GetPassphraseTypeForTesting(), @@ -822,11 +873,11 @@ Eq(SyncEncryptionHandler::SensitiveTypes())); EXPECT_TRUE(bridge()->HasPendingKeysForTesting()); - EXPECT_CALL(*observer(), OnPassphraseAccepted()); + EXPECT_CALL(*observer(), OnTrustedVaultKeyAccepted()); EXPECT_CALL(*observer(), OnCryptographerStateChanged( NotNull(), /*has_pending_keys=*/false)); EXPECT_CALL(*observer(), OnBootstrapTokenUpdated(_, _)).Times(0); - bridge()->SetDecryptionPassphrase(kTrustedVaultPassphrase); + bridge()->AddTrustedVaultDecryptionKeys({kTrustedVaultKey}); EXPECT_FALSE(bridge()->HasPendingKeysForTesting()); } @@ -835,7 +886,7 @@ // passphrase by means other than the sync protocol. TEST_F(NigoriSyncBridgeImplTest, ShouldProcessRemoteTransitionFromKeystoreToTrustedVault) { - const std::string kTrustedVaultPassphrase = "trusted_vault_passphrase"; + const std::string kTrustedVaultKey = "trusted_vault_key"; EntityData default_entity_data; *default_entity_data.specifics.mutable_nigori() = @@ -849,8 +900,7 @@ EntityData new_entity_data; *new_entity_data.specifics.mutable_nigori() = - BuildTrustedVaultNigoriSpecifics( - {Pbkdf2KeyParams(kTrustedVaultPassphrase)}); + BuildTrustedVaultNigoriSpecifics({Pbkdf2KeyParams(kTrustedVaultKey)}); EXPECT_CALL(*observer(), OnEncryptedTypesChanged(_, _)).Times(0); EXPECT_CALL(*observer(), OnBootstrapTokenUpdated(_, _)).Times(0); @@ -859,9 +909,7 @@ EXPECT_CALL(*observer(), OnPassphraseTypeChanged(PassphraseType::kTrustedVaultPassphrase, NullTime())); - EXPECT_CALL(*observer(), OnPassphraseRequired(/*reason=*/REASON_DECRYPTION, - /*key_derivation_params=*/_, - /*pending_keys=*/_)); + EXPECT_CALL(*observer(), OnTrustedVaultKeyRequired()); EXPECT_THAT(bridge()->ApplySyncChanges(std::move(new_entity_data)), Eq(base::nullopt)); EXPECT_THAT(bridge()->GetPassphraseTypeForTesting(), @@ -870,11 +918,11 @@ Eq(SyncEncryptionHandler::SensitiveTypes())); EXPECT_TRUE(bridge()->HasPendingKeysForTesting()); - EXPECT_CALL(*observer(), OnPassphraseAccepted()); + EXPECT_CALL(*observer(), OnTrustedVaultKeyAccepted()); EXPECT_CALL(*observer(), OnCryptographerStateChanged( NotNull(), /*has_pending_keys=*/false)); EXPECT_CALL(*observer(), OnBootstrapTokenUpdated(_, _)).Times(0); - bridge()->SetDecryptionPassphrase(kTrustedVaultPassphrase); + bridge()->AddTrustedVaultDecryptionKeys({kTrustedVaultKey}); EXPECT_FALSE(bridge()->HasPendingKeysForTesting()); } @@ -882,18 +930,18 @@ // vault passphrase. TEST_F(NigoriSyncBridgeImplTest, ShouldProcessRemoteKeyRotationForTrustedVault) { - const std::string kTrustedVaultPassphrase = "trusted_vault_passphrase"; - const std::string kRotatedTrustedVaultPassphrase = "rotated_vault_passphrase"; + const std::string kTrustedVaultKey = "trusted_vault_key"; + const std::string kRotatedTrustedVaultKey = "rotated_vault_key"; EntityData entity_data; - *entity_data.specifics.mutable_nigori() = BuildTrustedVaultNigoriSpecifics( - {Pbkdf2KeyParams(kTrustedVaultPassphrase)}); + *entity_data.specifics.mutable_nigori() = + BuildTrustedVaultNigoriSpecifics({Pbkdf2KeyParams(kTrustedVaultKey)}); ASSERT_TRUE(bridge()->SetKeystoreKeys({"keystore_key"})); ASSERT_THAT(bridge()->MergeSyncData(std::move(entity_data)), Eq(base::nullopt)); ASSERT_TRUE(bridge()->HasPendingKeysForTesting()); - bridge()->SetDecryptionPassphrase(kTrustedVaultPassphrase); + bridge()->AddTrustedVaultDecryptionKeys({kTrustedVaultKey}); ASSERT_FALSE(bridge()->HasPendingKeysForTesting()); ASSERT_THAT(bridge()->GetPassphraseTypeForTesting(), Eq(sync_pb::NigoriSpecifics::TRUSTED_VAULT_PASSPHRASE)); @@ -901,25 +949,23 @@ EntityData new_entity_data; *new_entity_data.specifics.mutable_nigori() = BuildTrustedVaultNigoriSpecifics( - {Pbkdf2KeyParams(kTrustedVaultPassphrase), - Pbkdf2KeyParams(kRotatedTrustedVaultPassphrase)}); + {Pbkdf2KeyParams(kTrustedVaultKey), + Pbkdf2KeyParams(kRotatedTrustedVaultKey)}); EXPECT_CALL(*observer(), OnEncryptedTypesChanged(_, _)).Times(0); EXPECT_CALL(*observer(), OnBootstrapTokenUpdated(_, _)).Times(0); EXPECT_CALL(*observer(), OnPassphraseTypeChanged(_, _)).Times(0); EXPECT_CALL(*observer(), OnCryptographerStateChanged( NotNull(), /*has_pending_keys=*/true)); - EXPECT_CALL(*observer(), OnPassphraseRequired(/*reason=*/REASON_DECRYPTION, - /*key_derivation_params=*/_, - /*pending_keys=*/_)); + EXPECT_CALL(*observer(), OnTrustedVaultKeyRequired()); EXPECT_THAT(bridge()->ApplySyncChanges(std::move(new_entity_data)), Eq(base::nullopt)); EXPECT_TRUE(bridge()->HasPendingKeysForTesting()); - EXPECT_CALL(*observer(), OnPassphraseAccepted()); + EXPECT_CALL(*observer(), OnTrustedVaultKeyAccepted()); EXPECT_CALL(*observer(), OnCryptographerStateChanged( NotNull(), /*has_pending_keys=*/false)); - bridge()->SetDecryptionPassphrase(kRotatedTrustedVaultPassphrase); + bridge()->AddTrustedVaultDecryptionKeys({kRotatedTrustedVaultKey}); EXPECT_FALSE(bridge()->HasPendingKeysForTesting()); } @@ -927,18 +973,18 @@ // passphrase. TEST_F(NigoriSyncBridgeImplTest, ShouldTransitionLocallyFromTrustedVaultToCustomPassphrase) { - const std::string kTrustedVaultPassphrase = "trusted_vault_passphrase"; + const std::string kTrustedVaultKey = "trusted_vault_key"; const std::string kCustomPassphrase = "custom_passphrase"; EntityData entity_data; - *entity_data.specifics.mutable_nigori() = BuildTrustedVaultNigoriSpecifics( - {Pbkdf2KeyParams(kTrustedVaultPassphrase)}); + *entity_data.specifics.mutable_nigori() = + BuildTrustedVaultNigoriSpecifics({Pbkdf2KeyParams(kTrustedVaultKey)}); ASSERT_TRUE(bridge()->SetKeystoreKeys({"keystore_key"})); ASSERT_THAT(bridge()->MergeSyncData(std::move(entity_data)), Eq(base::nullopt)); ASSERT_TRUE(bridge()->HasPendingKeysForTesting()); - bridge()->SetDecryptionPassphrase(kTrustedVaultPassphrase); + bridge()->AddTrustedVaultDecryptionKeys({kTrustedVaultKey}); ASSERT_FALSE(bridge()->HasPendingKeysForTesting()); ASSERT_THAT(bridge()->GetPassphraseTypeForTesting(), Eq(sync_pb::NigoriSpecifics::TRUSTED_VAULT_PASSPHRASE));
diff --git a/components/sync/protocol/proto_enum_conversions.cc b/components/sync/protocol/proto_enum_conversions.cc index 2235a55..344ce275a 100644 --- a/components/sync/protocol/proto_enum_conversions.cc +++ b/components/sync/protocol/proto_enum_conversions.cc
@@ -255,7 +255,7 @@ const char* ProtoEnumToString( sync_pb::SyncEnums::SingletonDebugEventType type) { ASSERT_ENUM_BOUNDS(sync_pb::SyncEnums, SingletonDebugEventType, - CONNECTION_STATUS_CHANGE, BOOTSTRAP_TOKEN_UPDATED); + CONNECTION_STATUS_CHANGE, TRUSTED_VAULT_KEY_ACCEPTED); switch (type) { ENUM_CASE(sync_pb::SyncEnums, CONNECTION_STATUS_CHANGE); ENUM_CASE(sync_pb::SyncEnums, UPDATED_TOKEN); @@ -270,6 +270,8 @@ ENUM_CASE(sync_pb::SyncEnums, KEYSTORE_TOKEN_UPDATED); ENUM_CASE(sync_pb::SyncEnums, CONFIGURE_COMPLETE); ENUM_CASE(sync_pb::SyncEnums, BOOTSTRAP_TOKEN_UPDATED); + ENUM_CASE(sync_pb::SyncEnums, TRUSTED_VAULT_KEY_REQUIRED); + ENUM_CASE(sync_pb::SyncEnums, TRUSTED_VAULT_KEY_ACCEPTED); } NOTREACHED(); return "";
diff --git a/components/sync/protocol/sync_enums.proto b/components/sync/protocol/sync_enums.proto index 5017d263..d13cfba1 100644 --- a/components/sync/protocol/sync_enums.proto +++ b/components/sync/protocol/sync_enums.proto
@@ -19,32 +19,39 @@ message SyncEnums { // These events are sent by the DebugInfo class for singleton events. enum SingletonDebugEventType { - CONNECTION_STATUS_CHANGE = 1; // Connection status change. Note this - // gets generated even during a successful - // connection. - UPDATED_TOKEN = 2; // Client received an updated token. - PASSPHRASE_REQUIRED = 3; // Cryptographer needs passphrase. - PASSPHRASE_ACCEPTED = 4; // Passphrase was accepted by cryptographer. - INITIALIZATION_COMPLETE = 5; // Sync Initialization is complete. - - // |STOP_SYNCING_PERMANENTLY| event should never be seen by the server in - // the absence of bugs. - STOP_SYNCING_PERMANENTLY = 6; // Server sent stop syncing permanently. - - ENCRYPTION_COMPLETE = 7; // Client has finished encrypting all data. - ACTIONABLE_ERROR = 8; // Client received an actionable error. - ENCRYPTED_TYPES_CHANGED = 9; // Set of encrypted types has changed. - // NOTE: until m25 bootstrap token updated also - // shared this field (erroneously). - PASSPHRASE_TYPE_CHANGED = 10; // The encryption passphrase state changed. - KEYSTORE_TOKEN_UPDATED = 11; // A new keystore encryption token was - // persisted. - CONFIGURE_COMPLETE = 12; // The datatype manager has finished an - // at least partially successful - // configuration and is once again syncing - // with the server. - BOOTSTRAP_TOKEN_UPDATED = 13; // A new cryptographer bootstrap token was - // generated. + // Connection status change. Note this gets generated even during a + // successful connection. + CONNECTION_STATUS_CHANGE = 1; + // Client received an updated token. + UPDATED_TOKEN = 2; + // Cryptographer needs passphrase. + PASSPHRASE_REQUIRED = 3; + // Passphrase was accepted by cryptographer. + PASSPHRASE_ACCEPTED = 4; + // Sync Initialization is complete. + INITIALIZATION_COMPLETE = 5; + // Server sent stop syncing permanently. This event should never be seen by + // the server in the absence of bugs. + STOP_SYNCING_PERMANENTLY = 6; + // Client has finished encrypting all data. + ENCRYPTION_COMPLETE = 7; + // Client received an actionable error. + ACTIONABLE_ERROR = 8; + // Set of encrypted types has changed. + ENCRYPTED_TYPES_CHANGED = 9; + // The encryption passphrase state changed. + PASSPHRASE_TYPE_CHANGED = 10; + // A new keystore encryption token was persisted. + KEYSTORE_TOKEN_UPDATED = 11; + // The datatype manager has finished an at least partially successful + // configuration and is once again syncing with the server. + CONFIGURE_COMPLETE = 12; + // A new cryptographer bootstrap token was generated. + BOOTSTRAP_TOKEN_UPDATED = 13; + // Cryptographer needs trusted vault decryption keys. + TRUSTED_VAULT_KEY_REQUIRED = 14; + // Cryptographer no longer needs trusted vault decryption keys. + TRUSTED_VAULT_KEY_ACCEPTED = 15; } // See ui/base/page_transition_types.h for detailed information on the
diff --git a/components/sync/syncable/nigori_handler_proxy.cc b/components/sync/syncable/nigori_handler_proxy.cc index f2b7ebdf..e644fc8 100644 --- a/components/sync/syncable/nigori_handler_proxy.cc +++ b/components/sync/syncable/nigori_handler_proxy.cc
@@ -36,6 +36,14 @@ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); } +void NigoriHandlerProxy::OnTrustedVaultKeyRequired() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +} + +void NigoriHandlerProxy::OnTrustedVaultKeyAccepted() { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); +} + void NigoriHandlerProxy::OnBootstrapTokenUpdated( const std::string& bootrstrap_token, BootstrapTokenType type) {
diff --git a/components/sync/syncable/nigori_handler_proxy.h b/components/sync/syncable/nigori_handler_proxy.h index 89ea905..8c847197 100644 --- a/components/sync/syncable/nigori_handler_proxy.h +++ b/components/sync/syncable/nigori_handler_proxy.h
@@ -41,6 +41,8 @@ const KeyDerivationParams& key_derivation_params, const sync_pb::EncryptedData& pending_keys) override; void OnPassphraseAccepted() override; + void OnTrustedVaultKeyRequired() override; + void OnTrustedVaultKeyAccepted() override; void OnBootstrapTokenUpdated(const std::string& bootrstrap_token, BootstrapTokenType type) override; void OnEncryptedTypesChanged(ModelTypeSet encrypted_types,
diff --git a/components/sync/test/fake_sync_encryption_handler.cc b/components/sync/test/fake_sync_encryption_handler.cc index bf48204..e250953 100644 --- a/components/sync/test/fake_sync_encryption_handler.cc +++ b/components/sync/test/fake_sync_encryption_handler.cc
@@ -119,6 +119,11 @@ // Do nothing. } +void FakeSyncEncryptionHandler::AddTrustedVaultDecryptionKeys( + const std::vector<std::string>& encryption_keys) { + // Do nothing. +} + void FakeSyncEncryptionHandler::EnableEncryptEverything() { if (encrypt_everything_) return;
diff --git a/components/sync/test/fake_sync_encryption_handler.h b/components/sync/test/fake_sync_encryption_handler.h index 122b32e..a2958af 100644 --- a/components/sync/test/fake_sync_encryption_handler.h +++ b/components/sync/test/fake_sync_encryption_handler.h
@@ -37,6 +37,8 @@ bool Init() override; void SetEncryptionPassphrase(const std::string& passphrase) override; void SetDecryptionPassphrase(const std::string& passphrase) override; + void AddTrustedVaultDecryptionKeys( + const std::vector<std::string>& keys) override; void EnableEncryptEverything() override; bool IsEncryptEverythingEnabled() const override; base::Time GetKeystoreMigrationTime() const override;
diff --git a/components/ukm/content/source_url_recorder_browsertest.cc b/components/ukm/content/source_url_recorder_browsertest.cc index 72969135..1f842d0 100644 --- a/components/ukm/content/source_url_recorder_browsertest.cc +++ b/components/ukm/content/source_url_recorder_browsertest.cc
@@ -6,7 +6,6 @@ #include "base/files/scoped_temp_dir.h" #include "base/test/scoped_feature_list.h" -#include "build/build_config.h" #include "components/ukm/content/source_url_recorder.h" #include "components/ukm/test_ukm_recorder.h" #include "content/public/browser/web_contents.h" @@ -83,13 +82,7 @@ base::ScopedTempDir downloads_directory_; }; -#if defined(OS_WIN) -#define MAYBE_Basic DISABLED_Basic -#else -#define MAYBE_Basic Basic -#endif -IN_PROC_BROWSER_TEST_F(SourceUrlRecorderWebContentsObserverBrowserTest, - MAYBE_Basic) { +IN_PROC_BROWSER_TEST_F(SourceUrlRecorderWebContentsObserverBrowserTest, Basic) { using Entry = ukm::builders::DocumentCreated; GURL url = embedded_test_server()->GetURL("/title1.html"); @@ -114,13 +107,8 @@ EXPECT_NE(source->id(), ukm_entries[0]->source_id); } -#if defined(OS_WIN) -#define MAYBE_IgnoreUrlInSubframe DISABLED_IgnoreUrlInSubframe -#else -#define MAYBE_IgnoreUrlInSubframe IgnoreUrlInSubframe -#endif IN_PROC_BROWSER_TEST_F(SourceUrlRecorderWebContentsObserverBrowserTest, - MAYBE_IgnoreUrlInSubframe) { + IgnoreUrlInSubframe) { using Entry = ukm::builders::DocumentCreated; GURL main_url = embedded_test_server()->GetURL("/page_with_iframe.html");
diff --git a/components/viz/service/display/software_renderer.cc b/components/viz/service/display/software_renderer.cc index dc382c9..76a7693 100644 --- a/components/viz/service/display/software_renderer.cc +++ b/components/viz/service/display/software_renderer.cc
@@ -649,9 +649,12 @@ } bool SoftwareRenderer::ShouldApplyBackdropFilters( - const cc::FilterOperations* backdrop_filters) const { + const cc::FilterOperations* backdrop_filters, + const RenderPassDrawQuad* quad) const { if (!backdrop_filters) return false; + if (quad->shared_quad_state->opacity == 0.f) + return false; DCHECK(!backdrop_filters->IsEmpty()); return true; } @@ -755,7 +758,7 @@ SkTileMode content_tile_mode) const { const cc::FilterOperations* backdrop_filters = BackdropFiltersForPass(quad->render_pass_id); - if (!ShouldApplyBackdropFilters(backdrop_filters)) + if (!ShouldApplyBackdropFilters(backdrop_filters, quad)) return nullptr; base::Optional<gfx::RRectF> backdrop_filter_bounds_input = BackdropFilterBoundsForPass(quad->render_pass_id);
diff --git a/components/viz/service/display/software_renderer.h b/components/viz/service/display/software_renderer.h index c9d67650..11b3998 100644 --- a/components/viz/service/display/software_renderer.h +++ b/components/viz/service/display/software_renderer.h
@@ -79,8 +79,8 @@ void DrawTextureQuad(const TextureDrawQuad* quad); void DrawTileQuad(const TileDrawQuad* quad); void DrawUnsupportedQuad(const DrawQuad* quad); - bool ShouldApplyBackdropFilters( - const cc::FilterOperations* backdrop_filters) const; + bool ShouldApplyBackdropFilters(const cc::FilterOperations* backdrop_filters, + const RenderPassDrawQuad* quad) const; sk_sp<SkImage> ApplyImageFilter(SkImageFilter* filter, const RenderPassDrawQuad* quad, const SkBitmap& to_filter,
diff --git a/content/browser/back_forward_cache_browsertest.cc b/content/browser/back_forward_cache_browsertest.cc index 44ca3d6..68763d3 100644 --- a/content/browser/back_forward_cache_browsertest.cc +++ b/content/browser/back_forward_cache_browsertest.cc
@@ -36,6 +36,7 @@ #include "third_party/blink/public/common/scheduler/web_scheduler_tracked_feature.h" using testing::Each; +using testing::ElementsAre; using testing::Not; namespace content { @@ -82,8 +83,36 @@ return web_contents()->GetFrameTree()->root()->render_manager(); } + void ExpectOutcome(BackForwardCacheMetrics::HistoryNavigationOutcome outcome, + base::Location location) { + base::HistogramBase::Sample sample = base::HistogramBase::Sample(outcome); + auto it = std::find_if( + expected_outcomes_.begin(), expected_outcomes_.end(), + [sample](const base::Bucket& bucket) { return bucket.min == sample; }); + if (it == expected_outcomes_.end()) { + expected_outcomes_.push_back(base::Bucket(sample, 1)); + } else { + it->count++; + } + + EXPECT_EQ(expected_outcomes_, + histogram_tester_.GetAllSamples( + "BackForwardCache.HistoryNavigationOutcome")) + << location.ToString(); + } + + void ExpectOutcomeIsEmpty(base::Location location) { + EXPECT_THAT(histogram_tester_.GetAllSamples( + "BackForwardCache.HistoryNavigationOutcome"), + ElementsAre()) + << location.ToString(); + } + private: base::test::ScopedFeatureList feature_list_; + + base::HistogramTester histogram_tester_; + std::vector<base::Bucket> expected_outcomes_; }; // Match RenderFrameHostImpl* that are in the BackForwardCache. @@ -166,6 +195,9 @@ EXPECT_EQ(rfh_a->GetVisibilityState(), PageVisibilityState::kVisible); EXPECT_TRUE(rfh_b->is_in_back_forward_cache()); EXPECT_EQ(rfh_b->GetVisibilityState(), PageVisibilityState::kHidden); + + ExpectOutcome(BackForwardCacheMetrics::HistoryNavigationOutcome::kRestored, + FROM_HERE); } // Navigate from A to B and go back. @@ -200,6 +232,9 @@ EXPECT_EQ(rfh_a, current_frame_host()); EXPECT_FALSE(rfh_a->is_in_back_forward_cache()); EXPECT_TRUE(rfh_b->is_in_back_forward_cache()); + + ExpectOutcome(BackForwardCacheMetrics::HistoryNavigationOutcome::kRestored, + FROM_HERE); } // Navigate from back and forward repeatedly. @@ -229,6 +264,9 @@ EXPECT_FALSE(rfh_a->is_in_back_forward_cache()); EXPECT_TRUE(rfh_b->is_in_back_forward_cache()); + ExpectOutcome(BackForwardCacheMetrics::HistoryNavigationOutcome::kRestored, + FROM_HERE); + // 4) Go forward to B. web_contents()->GetController().GoForward(); EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); @@ -237,6 +275,9 @@ EXPECT_TRUE(rfh_a->is_in_back_forward_cache()); EXPECT_FALSE(rfh_b->is_in_back_forward_cache()); + ExpectOutcome(BackForwardCacheMetrics::HistoryNavigationOutcome::kRestored, + FROM_HERE); + // 5) Go back to A. web_contents()->GetController().GoBack(); EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); @@ -245,6 +286,9 @@ EXPECT_FALSE(rfh_a->is_in_back_forward_cache()); EXPECT_TRUE(rfh_b->is_in_back_forward_cache()); + ExpectOutcome(BackForwardCacheMetrics::HistoryNavigationOutcome::kRestored, + FROM_HERE); + // 6) Go forward to B. web_contents()->GetController().GoForward(); EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); @@ -255,6 +299,9 @@ EXPECT_FALSE(delete_observer_rfh_a.deleted()); EXPECT_FALSE(delete_observer_rfh_b.deleted()); + + ExpectOutcome(BackForwardCacheMetrics::HistoryNavigationOutcome::kRestored, + FROM_HERE); } // The current page can't enter the BackForwardCache if another page can script @@ -355,6 +402,9 @@ EXPECT_FALSE(rfh_a->is_in_back_forward_cache()); EXPECT_FALSE(rfh_b->is_in_back_forward_cache()); EXPECT_TRUE(rfh_c->is_in_back_forward_cache()); + + ExpectOutcome(BackForwardCacheMetrics::HistoryNavigationOutcome::kRestored, + FROM_HERE); } // Ensure flushing the BackForwardCache works properly. @@ -439,6 +489,11 @@ // If/when the cache size is increased, this can be tested iteratively, see // deleted code in: https://crrev.com/c/1782902. + + web_contents()->GetController().GoToOffset(-2); + EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); + ExpectOutcome(BackForwardCacheMetrics::HistoryNavigationOutcome::kEvicted, + FROM_HERE); } // Test documents are evicted from the BackForwardCache at some point. @@ -511,6 +566,9 @@ // Even after a new IPC round trip with the renderer, b2 must still be alive. EXPECT_EQ("I am alive", EvalJs(b2, "window.alive")); EXPECT_FALSE(b2_observer.deleted()); + + ExpectOutcome(BackForwardCacheMetrics::HistoryNavigationOutcome::kRestored, + FROM_HERE); } // Similar to BackForwardCacheBrowserTest.SubframeSurviveCache* @@ -551,6 +609,9 @@ // Even after a new IPC round trip with the renderer, b2 must still be alive. EXPECT_EQ("I am alive", EvalJs(b2, "window.alive")); EXPECT_FALSE(b2_observer.deleted()); + + ExpectOutcome(BackForwardCacheMetrics::HistoryNavigationOutcome::kRestored, + FROM_HERE); } // Similar to BackForwardCacheBrowserTest.tSubframeSurviveCache* @@ -595,6 +656,9 @@ EXPECT_EQ("I am alive", EvalJs(b2, "window.alive")); EXPECT_FALSE(b2_observer.deleted()); + ExpectOutcome(BackForwardCacheMetrics::HistoryNavigationOutcome::kRestored, + FROM_HERE); + // 4) Go forward to b3(a4). web_contents()->GetController().GoForward(); EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); @@ -606,6 +670,9 @@ // Even after a new IPC round trip with the renderer, a4 must still be alive. EXPECT_EQ("I am alive", EvalJs(a4, "window.alive")); EXPECT_FALSE(a4_observer.deleted()); + + ExpectOutcome(BackForwardCacheMetrics::HistoryNavigationOutcome::kRestored, + FROM_HERE); } // Similar to BackForwardCacheBrowserTest.SubframeSurviveCache* @@ -730,6 +797,9 @@ // Page B should be deleted (not cached). delete_observer_rfh_b.WaitUntilDeleted(); + ExpectOutcome(BackForwardCacheMetrics::HistoryNavigationOutcome::kRestored, + FROM_HERE); + // 4. Navigate from a cacheable page to a cacheable page (A->C). EXPECT_TRUE(NavigateToURL(shell(), url_c)); EXPECT_EQ(web_contents()->GetLastCommittedURL(), url_c); @@ -748,6 +818,9 @@ // Page C should be in the cache. EXPECT_FALSE(delete_observer_rfh_c.deleted()); EXPECT_TRUE(rfh_c->is_in_back_forward_cache()); + + ExpectOutcome(BackForwardCacheMetrics::HistoryNavigationOutcome::kRestored, + FROM_HERE); } IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, @@ -1296,6 +1369,12 @@ // RenderFrameHost A is evicted from the BackForwardCache: delete_observer_rfh_a.WaitUntilDeleted(); + + // 4) Go back to A. + web_contents()->GetController().GoBack(); + EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); + ExpectOutcome(BackForwardCacheMetrics::HistoryNavigationOutcome::kEvicted, + FROM_HERE); } // Similar to BackForwardCacheBrowserTest.EvictionOnJavaScriptExecution. @@ -1334,6 +1413,12 @@ // The A(B) page is evicted. So A and B are removed: delete_observer_rfh_a.WaitUntilDeleted(); delete_observer_rfh_b.WaitUntilDeleted(); + + // 4) Go back to A. + web_contents()->GetController().GoBack(); + EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); + ExpectOutcome(BackForwardCacheMetrics::HistoryNavigationOutcome::kEvicted, + FROM_HERE); } IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, @@ -1368,6 +1453,12 @@ // RenderFrameHost A is evicted from the BackForwardCache: delete_observer_rfh_a.WaitUntilDeleted(); + + // 5) Go back to A. + web_contents()->GetController().GoBack(); + EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); + ExpectOutcome(BackForwardCacheMetrics::HistoryNavigationOutcome::kEvicted, + FROM_HERE); } // Similar to BackForwardCacheBrowserTest.EvictionOnJavaScriptExecution, but @@ -1451,6 +1542,9 @@ EXPECT_FALSE(rfh_a->is_in_back_forward_cache()); EXPECT_TRUE(rfh_b->is_in_back_forward_cache()); EXPECT_NE("initial document", EvalJs(rfh_a, "window.foo")); + + ExpectOutcome(BackForwardCacheMetrics::HistoryNavigationOutcome::kRestored, + FROM_HERE); } // Tests the events are fired when going back from the cache. @@ -1708,6 +1802,9 @@ RenderFrameHostImpl* rfh_a2 = current_frame_host(); EXPECT_NE(rfh_a2, rfh_b); EXPECT_EQ(rfh_a2->GetLastCommittedURL(), url_a); + + ExpectOutcome(BackForwardCacheMetrics::HistoryNavigationOutcome::kEvicted, + FROM_HERE); } // Test that if the renderer process crashes while a document is in the @@ -1744,6 +1841,12 @@ // rfh_b should still be the current frame. EXPECT_EQ(current_frame_host(), rfh_b); EXPECT_FALSE(delete_observer_rfh_b.deleted()); + + // 4) Go back to A. + web_contents()->GetController().GoBack(); + EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); + ExpectOutcome(BackForwardCacheMetrics::HistoryNavigationOutcome::kEvicted, + FROM_HERE); } // The test is simulating a race condition. The scheduler tracked features are @@ -1969,6 +2072,8 @@ // The page is controlled by a service worker, so it shouldn't have been // cached. deleted.WaitUntilDeleted(); + + ExpectOutcomeIsEmpty(FROM_HERE); } class BackForwardCacheBrowserTestWithServiceWorkerEnabled @@ -2335,4 +2440,35 @@ delete_observer_rfh_a.WaitUntilDeleted(); } +// Confirm that same-document navigation and not history-navigation does not +// record metrics. +IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest, MetricsNotRecorded) { + ASSERT_TRUE(embedded_test_server()->Start()); + const GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html")); + const GURL url_b(embedded_test_server()->GetURL("b.com", "/title1.html")); + const GURL url_b2(embedded_test_server()->GetURL("b.com", "/title1.html#2")); + + // 1) Navigate to A. + EXPECT_TRUE(NavigateToURL(shell(), url_a)); + EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); + + // 2) Navigate to B. + EXPECT_TRUE(NavigateToURL(shell(), url_b)); + EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); + + // 3) Navigate to B#2 (same document navigation). + EXPECT_TRUE(ExecJs(shell(), JsReplace("location = $1", url_b2.spec()))); + EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); + + // 4) Go back to B. + web_contents()->GetController().GoBack(); + EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); + ExpectOutcomeIsEmpty(FROM_HERE); + + // 5) Navigate to A. + EXPECT_TRUE(NavigateToURL(shell(), url_a)); + EXPECT_TRUE(WaitForLoadStop(shell()->web_contents())); + ExpectOutcomeIsEmpty(FROM_HERE); +} + } // namespace content
diff --git a/content/browser/dom_storage/session_storage_context_mojo_unittest.cc b/content/browser/dom_storage/session_storage_context_mojo_unittest.cc index f5826f06..6f73b9d 100644 --- a/content/browser/dom_storage/session_storage_context_mojo_unittest.cc +++ b/content/browser/dom_storage/session_storage_context_mojo_unittest.cc
@@ -613,7 +613,9 @@ ShutdownContext(); } -TEST_F(SessionStorageContextMojoTest, CorruptionOnDisk) { +// TODO(https://crbug.com/1010414): This test is flaky on various Linux bots and +// fuchsia_x64 (does that count as OS_LINUX or not?); fix and re-enable. +TEST_F(SessionStorageContextMojoTest, DISABLED_CorruptionOnDisk) { std::string namespace_id = base::GenerateGUID(); url::Origin origin = url::Origin::Create(GURL("http://foobar.com"));
diff --git a/content/browser/frame_host/back_forward_cache_impl.cc b/content/browser/frame_host/back_forward_cache_impl.cc index aba51a2a..094a8390 100644 --- a/content/browser/frame_host/back_forward_cache_impl.cc +++ b/content/browser/frame_host/back_forward_cache_impl.cc
@@ -128,26 +128,6 @@ : render_frame_host(std::move(rfh)), proxy_hosts(std::move(proxies)) {} BackForwardCacheImpl::Entry::~Entry() {} -int BackForwardCacheImpl::Entry::GetNavigationEntryId() { - CHECK(render_frame_host); - return render_frame_host->nav_entry_id(); -} - -bool BackForwardCacheImpl::Entry::IsEvictedFromBackForwardCache() { - CHECK(render_frame_host); - return render_frame_host->is_evicted_from_back_forward_cache(); -} - -void BackForwardCacheImpl::Entry::EvictFromBackForwardCache() { - CHECK(render_frame_host); - return render_frame_host->EvictFromBackForwardCache(); -} - -void BackForwardCacheImpl::Entry::LeaveBackForwardCache() { - CHECK(render_frame_host); - return render_frame_host->LeaveBackForwardCache(); -} - std::string BackForwardCacheImpl::CanStoreDocumentResult::ToString() { using Reason = BackForwardCacheMetrics::CanNotStoreDocumentReason; @@ -324,10 +304,10 @@ // full. size_t available_count = 0; for (auto& stored_entry : entries_) { - if (stored_entry->IsEvictedFromBackForwardCache()) + if (stored_entry->render_frame_host->is_evicted_from_back_forward_cache()) continue; if (++available_count > size_limit) - stored_entry->EvictFromBackForwardCache(); + stored_entry->render_frame_host->EvictFromBackForwardCache(); } } @@ -353,7 +333,7 @@ auto matching_entry = std::find_if( entries_.begin(), entries_.end(), [navigation_entry_id](std::unique_ptr<Entry>& entry) { - return entry->GetNavigationEntryId() == navigation_entry_id; + return entry->render_frame_host->nav_entry_id() == navigation_entry_id; }); // Not found. @@ -361,12 +341,13 @@ return nullptr; // Don't restore an evicted frame. - if ((*matching_entry)->IsEvictedFromBackForwardCache()) + if ((*matching_entry) + ->render_frame_host->is_evicted_from_back_forward_cache()) return nullptr; std::unique_ptr<Entry> entry = std::move(*matching_entry); entries_.erase(matching_entry); - entry->LeaveBackForwardCache(); + entry->render_frame_host->LeaveBackForwardCache(); return entry; } @@ -403,14 +384,15 @@ auto matching_entry = std::find_if( entries_.begin(), entries_.end(), [navigation_entry_id](std::unique_ptr<Entry>& entry) { - return entry->GetNavigationEntryId() == navigation_entry_id; + return entry->render_frame_host->nav_entry_id() == navigation_entry_id; }); if (matching_entry == entries_.end()) return nullptr; // Don't return the frame if it is evicted. - if ((*matching_entry)->IsEvictedFromBackForwardCache()) + if ((*matching_entry) + ->render_frame_host->is_evicted_from_back_forward_cache()) return nullptr; return (*matching_entry).get(); @@ -420,9 +402,9 @@ TRACE_EVENT0("navigation", "BackForwardCache::DestroyEvictedFrames"); if (entries_.empty()) return; - entries_.erase(std::remove_if(entries_.begin(), entries_.end(), - [](std::unique_ptr<Entry>& entry) { - return entry->IsEvictedFromBackForwardCache(); - })); + entries_.erase(std::remove_if( + entries_.begin(), entries_.end(), [](std::unique_ptr<Entry>& entry) { + return entry->render_frame_host->is_evicted_from_back_forward_cache(); + })); } } // namespace content
diff --git a/content/browser/frame_host/back_forward_cache_impl.h b/content/browser/frame_host/back_forward_cache_impl.h index 3bae916..8c9bcea 100644 --- a/content/browser/frame_host/back_forward_cache_impl.h +++ b/content/browser/frame_host/back_forward_cache_impl.h
@@ -43,14 +43,6 @@ RenderFrameProxyHostMap proxy_hosts); ~Entry(); - // These functions forward to the underlying render_frame_host. Do not call - // just before storing or right after restoring, as the Entry will be in an - // invalid state. - int GetNavigationEntryId(); - bool IsEvictedFromBackForwardCache(); - void EvictFromBackForwardCache(); - void LeaveBackForwardCache(); - // The main document being stored. std::unique_ptr<RenderFrameHostImpl> render_frame_host;
diff --git a/content/browser/frame_host/back_forward_cache_metrics.cc b/content/browser/frame_host/back_forward_cache_metrics.cc index c004112..9b20ac30 100644 --- a/content/browser/frame_host/back_forward_cache_metrics.cc +++ b/content/browser/frame_host/back_forward_cache_metrics.cc
@@ -4,6 +4,7 @@ #include "content/browser/frame_host/back_forward_cache_metrics.h" +#include "base/metrics/histogram_macros.h" #include "content/browser/frame_host/navigation_entry_impl.h" #include "content/browser/frame_host/navigation_request.h" #include "content/public/browser/browser_thread.h" @@ -93,6 +94,24 @@ void BackForwardCacheMetrics::DidCommitNavigation( NavigationRequest* navigation) { + bool is_history_navigation = + navigation->GetPageTransition() & ui::PAGE_TRANSITION_FORWARD_BACK; + if (navigation->IsInMainFrame() && !navigation->IsSameDocument() && + is_history_navigation) { + // TODO(hajimehoshi): Use kNotCachedDueToExperimentCondition if the + // experiment condition does not match. + HistoryNavigationOutcome outcome = HistoryNavigationOutcome::kNotCached; + if (navigation->is_served_from_back_forward_cache()) + outcome = HistoryNavigationOutcome::kRestored; + if (evicted_ || last_committed_main_frame_navigation_id_ == -1) { + DCHECK(!navigation->is_served_from_back_forward_cache()); + outcome = HistoryNavigationOutcome::kEvicted; + } + UMA_HISTOGRAM_ENUMERATION("BackForwardCache.HistoryNavigationOutcome", + outcome, HistoryNavigationOutcome::kMaxValue); + evicted_ = false; + } + if (last_committed_main_frame_navigation_id_ != -1 && navigation->IsInMainFrame()) { // We've visited an entry associated with this main frame document before, @@ -166,4 +185,8 @@ } } +void BackForwardCacheMetrics::MarkEvictedFromBackForwardCache() { + evicted_ = true; +} + } // namespace content
diff --git a/content/browser/frame_host/back_forward_cache_metrics.h b/content/browser/frame_host/back_forward_cache_metrics.h index f424666..dbc663a 100644 --- a/content/browser/frame_host/back_forward_cache_metrics.h +++ b/content/browser/frame_host/back_forward_cache_metrics.h
@@ -43,6 +43,16 @@ kDisableForRenderFrameHostCalled }; + // Please keep in sync with BackForwardCacheHistoryNavigationOutcome in + // tools/metrics/histograms/enums.xml. + enum class HistoryNavigationOutcome { + kRestored = 0, + kNotCached = 1, + kEvicted = 2, + kNotCachedDueToExperimentCondition = 3, + kMaxValue = kNotCachedDueToExperimentCondition, + }; + // Creates a potential new metrics object for the navigation. // Note that this object will not be used if the entry we are navigating to // already has the BackForwardCacheMetrics object (which happens for history @@ -81,6 +91,10 @@ // placed in the back-forward cache. void RecordFeatureUsage(RenderFrameHostImpl* main_frame); + // Marks when the page is evicted. + // TODO(hajimehoshi): Add the parameter representing the reason. + void MarkEvictedFromBackForwardCache(); + // Injects a clock for mocking time. // Should be called only from the UI thread. CONTENT_EXPORT static void OverrideTimeForTesting(base::TickClock* clock); @@ -120,6 +134,8 @@ base::Optional<base::TimeTicks> started_navigation_timestamp_; base::Optional<base::TimeTicks> navigated_away_from_main_document_timestamp_; + bool evicted_ = false; + DISALLOW_COPY_AND_ASSIGN(BackForwardCacheMetrics); };
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc index 4e3e596..35b7d36 100644 --- a/content/browser/frame_host/navigation_request.cc +++ b/content/browser/frame_host/navigation_request.cc
@@ -2308,7 +2308,7 @@ std::unique_ptr<BackForwardCacheImpl::Entry> restored_bfcache_entry = controller->GetBackForwardCache().RestoreEntry(nav_entry_id_); - // The only time restored_rfh can be nullptr here, is if the + // The only time restored_bfcache_entry can be nullptr here, is if the // document was evicted from the BackForwardCache since this navigation // started. //
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc index 1c232cde..d6d88e7b 100644 --- a/content/browser/frame_host/render_frame_host_impl.cc +++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -3519,6 +3519,11 @@ DCHECK_EQ(top_document->is_in_back_forward_cache(), in_back_forward_cache); } + if (BackForwardCacheMetrics* metrics = + top_document->GetBackForwardCacheMetrics()) { + metrics->MarkEvictedFromBackForwardCache(); + } + if (!in_back_forward_cache) { // A document is evicted from the BackForwardCache, but it has already been // restored. The current document should be reloaded, because it is not @@ -7668,4 +7673,14 @@ frame_bindings_control_->EnableMojoJsBindings(); } +BackForwardCacheMetrics* RenderFrameHostImpl::GetBackForwardCacheMetrics() { + NavigationEntryImpl* navigation_entry = + static_cast<NavigationControllerImpl*>( + frame_tree_node_->navigator()->GetController()) + ->GetEntryWithUniqueID(nav_entry_id()); + if (!navigation_entry) + return nullptr; + return navigation_entry->back_forward_cache_metrics(); +} + } // namespace content
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h index 456a163..a59c120 100644 --- a/content/browser/frame_host/render_frame_host_impl.h +++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -151,6 +151,7 @@ namespace content { class AppCacheNavigationHandle; class AuthenticatorImpl; +class BackForwardCacheMetrics; class BundledExchangesHandle; class FrameTree; class FrameTreeNode; @@ -1896,6 +1897,10 @@ // and ineligible for caching. void MaybeEvictFromBackForwardCache(); + // Returns the BackForwardCacheMetrics associated with the last + // NavigationEntry this RenderFrameHostImpl committed. + BackForwardCacheMetrics* GetBackForwardCacheMetrics(); + // The RenderViewHost that this RenderFrameHost is associated with. // // It is kept alive as long as any RenderFrameHosts or RenderFrameProxyHosts
diff --git a/content/renderer/media/webrtc/media_stream_track_metrics.cc b/content/renderer/media/webrtc/media_stream_track_metrics.cc index e2b4c01..31a5b5c5 100644 --- a/content/renderer/media/webrtc/media_stream_track_metrics.cc +++ b/content/renderer/media/webrtc/media_stream_track_metrics.cc
@@ -8,10 +8,10 @@ #include <string> #include "base/hash/md5.h" -#include "base/threading/thread_task_runner_handle.h" -#include "content/child/child_thread_impl.h" -#include "content/public/common/service_names.mojom.h" -#include "content/renderer/render_thread_impl.h" +#include "base/strings/stringprintf.h" +#include "base/threading/thread_checker.h" +#include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h" +#include "third_party/blink/public/platform/platform.h" namespace content { @@ -213,19 +213,14 @@ Kind kind, LifetimeEvent event, Direction direction) { - RenderThreadImpl* render_thread = RenderThreadImpl::current(); - // |render_thread| can be NULL in certain cases when running as part - // |of a unit test. - if (render_thread) { - if (event == LifetimeEvent::kConnected) { - GetMediaStreamTrackMetricsHost()->AddTrack( - MakeUniqueId(track_id, direction), kind == Kind::kAudio, - direction == Direction::kReceive); - } else { - DCHECK_EQ(LifetimeEvent::kDisconnected, event); - GetMediaStreamTrackMetricsHost()->RemoveTrack( - MakeUniqueId(track_id, direction)); - } + if (event == LifetimeEvent::kConnected) { + GetMediaStreamTrackMetricsHost()->AddTrack( + MakeUniqueId(track_id, direction), kind == Kind::kAudio, + direction == Direction::kReceive); + } else { + DCHECK_EQ(LifetimeEvent::kDisconnected, event); + GetMediaStreamTrackMetricsHost()->RemoveTrack( + MakeUniqueId(track_id, direction)); } } @@ -264,7 +259,7 @@ mojo::Remote<blink::mojom::MediaStreamTrackMetricsHost>& MediaStreamTrackMetrics::GetMediaStreamTrackMetricsHost() { if (!track_metrics_host_) { - ChildThreadImpl::current()->BindHostReceiver( + blink::Platform::Current()->GetBrowserInterfaceBrokerProxy()->GetInterface( track_metrics_host_.BindNewPipeAndPassReceiver()); } return track_metrics_host_;
diff --git a/content/renderer/service_worker/embedded_worker_instance_client_impl.cc b/content/renderer/service_worker/embedded_worker_instance_client_impl.cc index 3f26eba..0a6359d1 100644 --- a/content/renderer/service_worker/embedded_worker_instance_client_impl.cc +++ b/content/renderer/service_worker/embedded_worker_instance_client_impl.cc
@@ -175,9 +175,6 @@ ? blink::WebEmbeddedWorkerStartData::kWaitForDebugger : blink::WebEmbeddedWorkerStartData::kDontWaitForDebugger; start_data->devtools_worker_token = params.devtools_worker_token; - start_data->privacy_preferences = blink::PrivacyPreferences( - params.renderer_preferences->enable_do_not_track, - params.renderer_preferences->enable_referrers); return start_data; }
diff --git a/content/renderer/service_worker/embedded_worker_instance_client_impl.h b/content/renderer/service_worker/embedded_worker_instance_client_impl.h index 37ecbe1..65326601 100644 --- a/content/renderer/service_worker/embedded_worker_instance_client_impl.h +++ b/content/renderer/service_worker/embedded_worker_instance_client_impl.h
@@ -11,7 +11,6 @@ #include "content/common/content_export.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/receiver.h" -#include "third_party/blink/public/common/privacy_preferences.h" #include "third_party/blink/public/mojom/service_worker/embedded_worker.mojom.h" #include "third_party/blink/public/mojom/service_worker/service_worker_installed_scripts_manager.mojom.h" #include "third_party/blink/public/mojom/worker/worker_content_settings_proxy.mojom.h"
diff --git a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt index 20ca850..c8d9e64 100644 --- a/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt +++ b/content/test/gpu/gpu_tests/test_expectations/webgl_conformance_expectations.txt
@@ -319,6 +319,18 @@ crbug.com/990368 [ mac passthrough ] conformance/canvas/draw-static-webgl-to-multiple-canvas-test.html [ Failure ] crbug.com/990368 [ mac passthrough ] conformance/canvas/draw-webgl-to-canvas-test.html [ Failure ] crbug.com/990368 [ mac passthrough ] conformance/canvas/to-data-url-test.html [ Failure ] +crbug.com/990368 [ mac passthrough ] conformance/extensions/oes-texture-float-linear.html [ Failure ] +crbug.com/990368 [ mac passthrough ] conformance/extensions/oes-texture-float-with-canvas.html [ Failure ] +crbug.com/990368 [ mac passthrough ] conformance/glsl/samplers/glsl-function-texture2dproj.html [ Failure ] +crbug.com/990368 [ mac passthrough ] conformance/textures/canvas/tex-2d-rgb-rgb-unsigned_byte.html [ Failure ] +crbug.com/990368 [ mac passthrough ] conformance/textures/canvas/tex-2d-rgb-rgb-unsigned_short_5_6_5.html [ Failure ] +crbug.com/990368 [ mac passthrough ] conformance/textures/canvas/tex-2d-rgba-rgba-unsigned_byte.html [ Failure ] +crbug.com/990368 [ mac passthrough ] conformance/textures/canvas/tex-2d-rgba-rgba-unsigned_short_4_4_4_4.html [ Failure ] +crbug.com/990368 [ mac passthrough ] conformance/textures/misc/canvas-teximage-after-multiple-drawimages.html [ Failure ] +crbug.com/990368 [ mac passthrough ] conformance/textures/misc/texparameter-test.html [ Failure ] +crbug.com/990368 [ mac passthrough ] conformance/textures/misc/texture-active-bind-2.html [ Failure ] +crbug.com/990368 [ mac passthrough ] conformance/textures/misc/texture-active-bind.html [ Failure ] +crbug.com/990368 [ mac passthrough ] conformance/textures/misc/texture-npot.html [ Failure ] crbug.com/angleproject/3812 [ mac passthrough ] conformance/context/context-attribute-preserve-drawing-buffer.html [ Failure ] crbug.com/990368 [ mac passthrough ] conformance/context/premultiplyalpha-test.html [ Failure ] crbug.com/989194 [ mac passthrough ] conformance/extensions/oes-texture-float-with-video.html [ Failure ]
diff --git a/device/vr/openxr/openxr_controller.cc b/device/vr/openxr/openxr_controller.cc index 67d9f1a..f8e64e1 100644 --- a/device/vr/openxr/openxr_controller.cc +++ b/device/vr/openxr/openxr_controller.cc
@@ -232,7 +232,7 @@ base::Optional<GamepadButton> button = GetButton(static_cast<OpenXrButtonType>(i)); if (button) { - buttons.push_back(std::move(GetXRGamepadButtonPtr(button.value()))); + buttons.push_back(GetXRGamepadButtonPtr(button.value())); } else { return {}; }
diff --git a/extensions/browser/content_verifier.cc b/extensions/browser/content_verifier.cc index 3cef80c7..1f1ab63 100644 --- a/extensions/browser/content_verifier.cc +++ b/extensions/browser/content_verifier.cc
@@ -588,7 +588,7 @@ ExtensionId extension_id = content_hash->extension_id(); if (g_content_verifier_test_observer) { g_content_verifier_test_observer->OnFetchComplete( - extension_id, content_hash->has_verified_contents()); + extension_id, content_hash->succeeded()); } VLOG(1) << "OnFetchComplete " << extension_id
diff --git a/extensions/browser/content_verifier/content_hash.cc b/extensions/browser/content_verifier/content_hash.cc index bdb2a99..1531b9a 100644 --- a/extensions/browser/content_verifier/content_hash.cc +++ b/extensions/browser/content_verifier/content_hash.cc
@@ -121,7 +121,7 @@ ContentHash::TreeHashVerificationResult ContentHash::VerifyTreeHashRoot( const base::FilePath& relative_path, const std::string* root) const { - DCHECK(status_ >= Status::kHasVerifiedContents && verified_contents_); + DCHECK(verified_contents_); if (!verified_contents_->HasTreeHashRoot(relative_path)) return TreeHashVerificationResult::NO_ENTRY; @@ -132,7 +132,7 @@ } const ComputedHashes::Reader& ContentHash::computed_hashes() const { - DCHECK(status_ == Status::kSucceeded && computed_hashes_); + DCHECK(succeeded_ && computed_hashes_); return *computed_hashes_; } @@ -145,12 +145,7 @@ extension_root_(root), verified_contents_(std::move(verified_contents)), computed_hashes_(std::move(computed_hashes)) { - if (!verified_contents_) - status_ = Status::kInvalid; - else if (!computed_hashes_) - status_ = Status::kHasVerifiedContents; - else - status_ = Status::kSucceeded; + succeeded_ = verified_contents_ != nullptr && computed_hashes_ != nullptr; } ContentHash::~ContentHash() = default; @@ -309,7 +304,7 @@ timer.Elapsed()); if (result) - status_ = Status::kSucceeded; + succeeded_ = true; return result; } @@ -344,7 +339,7 @@ // will_create = true; } else { // Read successful. - status_ = Status::kSucceeded; + succeeded_ = true; computed_hashes_ = std::move(computed_hashes); return; } @@ -364,7 +359,7 @@ return; // Read successful. - status_ = Status::kSucceeded; + succeeded_ = true; computed_hashes_ = std::move(computed_hashes); }
diff --git a/extensions/browser/content_verifier/content_hash.h b/extensions/browser/content_verifier/content_hash.h index 8863fc9..93818a2 100644 --- a/extensions/browser/content_verifier/content_hash.h +++ b/extensions/browser/content_verifier/content_hash.h
@@ -120,11 +120,9 @@ const ComputedHashes::Reader& computed_hashes() const; - bool has_verified_contents() const { - return status_ >= Status::kHasVerifiedContents; - } - - bool succeeded() const { return status_ >= Status::kSucceeded; } + // Returns whether or not computed_hashes.json (and, if needed, + // verified_contents.json too) was read correctly and is ready to use. + bool succeeded() const { return succeeded_; } // If ContentHash creation writes computed_hashes.json, then this returns the // FilePaths whose content hash didn't match expected hashes. @@ -138,24 +136,13 @@ // for |this| to succeed. // TODO(lazyboy): Remove this once https://crbug.com/819832 is fixed. bool might_require_computed_hashes_force_creation() const { - return !succeeded() && has_verified_contents() && + return !succeeded() && verified_contents_ != nullptr && !did_attempt_creating_computed_hashes_; } private: friend class base::RefCountedThreadSafe<ContentHash>; - enum class Status { - // Retrieving hashes failed. - kInvalid, - // Retrieved valid verified_contents.json, but there was no - // computed_hashes.json. - kHasVerifiedContents, - // Both verified_contents.json and computed_hashes.json were read - // correctly. - kSucceeded, - }; - ContentHash(const ExtensionId& id, const base::FilePath& root, std::unique_ptr<VerifiedContents> verified_contents, @@ -198,7 +185,7 @@ const ExtensionId extension_id_; const base::FilePath extension_root_; - Status status_ = Status::kInvalid; + bool succeeded_ = false; bool did_attempt_creating_computed_hashes_ = false;
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc index 8d5aec4..fc98910 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough.cc
@@ -1747,11 +1747,6 @@ if (!image) return; - // Because the binding is deferred, this texture may not be currently bound - // any more. Bind it again. - GLenum texture_type = TextureTargetToTextureType(target); - api()->glBindTextureFn(texture_type, texture->service_id()); - // TODO: internalformat? if (image->ShouldBindOrCopy() == gl::GLImage::BIND) image->BindTexImage(target); @@ -1762,14 +1757,6 @@ // However, for now, we only try once. texture->set_is_bind_pending(false); - // Re-bind the previous texture - const BoundTexture& bound_texture = - bound_textures_[static_cast<size_t>(GLenumToTextureTarget(texture_type))] - [active_texture_unit_]; - GLuint prev_texture = - bound_texture.texture ? bound_texture.texture->service_id() : 0; - api()->glBindTextureFn(texture_type, prev_texture); - // Update any binding points that are currently bound for this texture. RebindTexture(texture);
diff --git a/gpu/config/gpu_info_collector_win.cc b/gpu/config/gpu_info_collector_win.cc index 483b4f20..b7ff063d 100644 --- a/gpu/config/gpu_info_collector_win.cc +++ b/gpu/config/gpu_info_collector_win.cc
@@ -229,21 +229,13 @@ return false; } - const VS_FIXEDFILEINFO* fixed_file_info = - file_version_info->fixed_file_info(); - const int major = HIWORD(fixed_file_info->dwFileVersionMS); - const int minor = LOWORD(fixed_file_info->dwFileVersionMS); - const int minor_1 = HIWORD(fixed_file_info->dwFileVersionLS); - // From the Canary crash logs, the broken amdvlk64.dll versions // are 1.0.39.0, 1.0.51.0 and 1.0.54.0. In the manual test, version // 9.2.10.1 dated 12/6/2017 works and version 1.0.54.0 dated 11/2/1017 // crashes. All version numbers small than 1.0.54.0 will be marked as // broken. - if (major == 1 && minor == 0 && minor_1 <= 54) { - return true; - } - return false; + const base::Version kBadAMDVulkanDriverVersion("1.0.54.0"); + return file_version_info->GetFileVersion() <= kBadAMDVulkanDriverVersion; } bool BadVulkanDllVersion() { @@ -253,13 +245,6 @@ if (!file_version_info) return false; - const VS_FIXEDFILEINFO* fixed_file_info = - file_version_info->fixed_file_info(); - const int major = HIWORD(fixed_file_info->dwFileVersionMS); - const int minor = LOWORD(fixed_file_info->dwFileVersionMS); - const int build_1 = HIWORD(fixed_file_info->dwFileVersionLS); - const int build_2 = LOWORD(fixed_file_info->dwFileVersionLS); - // From the logs, most vulkan-1.dll crashs are from the following versions. // As of 7/23/2018. // 0.0.0.0 - # of crashes: 6556 @@ -271,13 +256,12 @@ // The GPU could be from any vendor, but only some certain models would crash. // For those that don't crash, they usually return failures upon GPU vulkan // support querying even though the GPU drivers can support it. - if ((major == 0 && minor == 0 && build_1 == 0 && build_2 == 0) || - (major == 1 && minor == 0 && build_1 == 26 && build_2 == 0) || - (major == 1 && minor == 0 && build_1 == 33 && build_2 == 0) || - (major == 1 && minor == 0 && build_1 == 42 && build_2 == 0) || - (major == 1 && minor == 0 && build_1 == 42 && build_2 == 1) || - (major == 1 && minor == 0 && build_1 == 51 && build_2 == 0)) { - return true; + base::Version fv = file_version_info->GetFileVersion(); + const char* const kBadVulkanDllVersion[] = { + "0.0.0.0", "1.0.26.0", "1.0.33.0", "1.0.42.0", "1.0.42.1", "1.0.51.0"}; + for (const char* bad_version : kBadVulkanDllVersion) { + if (fv == base::Version(bad_version)) + return true; } return false; }
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm index 6f3184227..3c0be83 100644 --- a/ios/chrome/app/main_controller.mm +++ b/ios/chrome/app/main_controller.mm
@@ -1684,7 +1684,7 @@ DCHECK_EQ(self.currentBVC, self.mainCoordinator.activeViewController); baseViewController = self.currentBVC; } - DCHECK(![baseViewController presentedViewController]); + if ([self currentBrowserState]->IsOffTheRecord()) { NOTREACHED(); return; @@ -1710,7 +1710,7 @@ DCHECK_EQ(self.currentBVC, self.mainCoordinator.activeViewController); baseViewController = self.currentBVC; } - DCHECK(![baseViewController presentedViewController]); + if (_settingsNavigationController) { // Navigate to the Google services settings if the settings dialog is // already opened.
diff --git a/ios/chrome/browser/infobars/infobar_badge_tab_helper.mm b/ios/chrome/browser/infobars/infobar_badge_tab_helper.mm index 7b88589..7ab5cb2a 100644 --- a/ios/chrome/browser/infobars/infobar_badge_tab_helper.mm +++ b/ios/chrome/browser/infobars/infobar_badge_tab_helper.mm
@@ -33,7 +33,8 @@ void InfobarBadgeTabHelper::UpdateBadgeForInfobarAccepted( InfobarType infobar_type) { - infobar_badge_models_[infobar_type].badgeState |= BadgeStateAccepted; + infobar_badge_models_[infobar_type].badgeState |= + BadgeStateAccepted | BadgeStateRead; [delegate_ updateInfobarBadge:infobar_badge_models_[infobar_type]]; }
diff --git a/ios/chrome/browser/infobars/infobar_badge_tab_helper_unittest.mm b/ios/chrome/browser/infobars/infobar_badge_tab_helper_unittest.mm index 8043cc5..09d9488 100644 --- a/ios/chrome/browser/infobars/infobar_badge_tab_helper_unittest.mm +++ b/ios/chrome/browser/infobars/infobar_badge_tab_helper_unittest.mm
@@ -237,7 +237,10 @@ tab_helper()->UpdateBadgeForInfobarAccepted( InfobarType::kInfobarTypePasswordSave); EXPECT_TRUE(infobar_badge_tab_delegate_.badgeIsTappable); - EXPECT_TRUE(infobar_badge_tab_delegate_.badgeState &= BadgeStateAccepted); + EXPECT_EQ(BadgeStateAccepted, + infobar_badge_tab_delegate_.badgeState & BadgeStateAccepted); + EXPECT_EQ(BadgeStateRead, + infobar_badge_tab_delegate_.badgeState & BadgeStateRead); } // Test the badge state after doesn't change after adding an Infobar with no
diff --git a/ios/chrome/browser/prerender/preload_controller.mm b/ios/chrome/browser/prerender/preload_controller.mm index a79762a1..37f3274 100644 --- a/ios/chrome/browser/prerender/preload_controller.mm +++ b/ios/chrome/browser/prerender/preload_controller.mm
@@ -32,6 +32,7 @@ #import "ios/web/public/navigation/web_state_policy_decider_bridge.h" #include "ios/web/public/thread/web_thread.h" #import "ios/web/public/ui/java_script_dialog_presenter.h" +#include "ios/web/public/web_client.h" #import "ios/web/public/web_state.h" #import "ios/web/public/web_state_observer_bridge.h" #import "ios/web/web_state/ui/crw_web_controller.h" @@ -322,7 +323,8 @@ } - (std::unique_ptr<web::WebState>)releasePrerenderContents { - if (!_webState) + if (!_webState || + _webState->GetNavigationManager()->IsRestoreSessionInProgress()) return nullptr; self.successfulPrerendersPerSessionCount++; @@ -495,13 +497,20 @@ self.prerenderedURL = self.scheduledURL; std::unique_ptr<PrerenderRequest> request = std::move(_scheduledRequest); - if (!self.prerenderedURL.is_valid()) { + web::WebState* webStateToReplace = [self.delegate webStateToReplace]; + if (!self.prerenderedURL.is_valid() || !webStateToReplace) { [self destroyPreviewContents]; return; } web::WebState::CreateParams createParams(self.browserState); - _webState = web::WebState::Create(createParams); + if (web::GetWebClient()->IsSlimNavigationManagerEnabled()) { + _webState = web::WebState::CreateWithStorageSession( + createParams, webStateToReplace->BuildSessionStorage()); + } else { + _webState = web::WebState::Create(createParams); + } + // Add the preload controller as a policyDecider before other tab helpers, so // that it can block the navigation if needed before other policy deciders // execute thier side effects (eg. AppLauncherTabHelper launching app).
diff --git a/ios/chrome/browser/prerender/preload_controller_delegate.h b/ios/chrome/browser/prerender/preload_controller_delegate.h index 691cbcd3..856d97d7 100644 --- a/ios/chrome/browser/prerender/preload_controller_delegate.h +++ b/ios/chrome/browser/prerender/preload_controller_delegate.h
@@ -12,6 +12,10 @@ // A protocol implemented by a delegate of PreloadController @protocol PreloadControllerDelegate +// WebState from which preload controller should copy the session history. +// This web state will be replaced on successful preload. +- (web::WebState*)webStateToReplace; + // Should preload controller request a desktop site. - (BOOL)preloadShouldUseDesktopUserAgent;
diff --git a/ios/chrome/browser/prerender/prerender_egtest.mm b/ios/chrome/browser/prerender/prerender_egtest.mm index c435a66f..42a5e47 100644 --- a/ios/chrome/browser/prerender/prerender_egtest.mm +++ b/ios/chrome/browser/prerender/prerender_egtest.mm
@@ -65,11 +65,6 @@ @"Disabled for iPad due to alternate letters educational screen."); } - if ([ChromeEarlGrey isSlimNavigationManagerEnabled]) { - // TODO(crbug.com/834116): Fix and enable this test. - EARL_GREY_TEST_DISABLED(@"Prerender is not supported by slim-nav yet."); - } - [ChromeEarlGrey clearBrowsingHistory]; // Set server up. int visitCounter = 0;
diff --git a/ios/chrome/browser/prerender/prerender_service.mm b/ios/chrome/browser/prerender/prerender_service.mm index 45961002..692441d8 100644 --- a/ios/chrome/browser/prerender/prerender_service.mm +++ b/ios/chrome/browser/prerender/prerender_service.mm
@@ -39,13 +39,6 @@ const web::Referrer& referrer, ui::PageTransition transition, bool immediately) { - // PrerenderService is not compatible with WKBasedNavigationManager because it - // loads the URL in a new WKWebView, which doesn't have the current session - // history. TODO(crbug.com/814789): decide whether PrerenderService needs to - // be supported after evaluating the performance impact in Finch experiment. - if (web::GetWebClient()->IsSlimNavigationManagerEnabled()) - return; - [controller_ prerenderURL:url referrer:referrer transition:transition @@ -64,6 +57,11 @@ std::unique_ptr<web::WebState> new_web_state = [controller_ releasePrerenderContents]; + if (!new_web_state) { + CancelPrerender(); + return false; + } + DCHECK_NE(WebStateList::kInvalidIndex, web_state_list->active_index()); web::NavigationManager* active_navigation_manager = @@ -79,8 +77,13 @@ web::NavigationManager* new_navigation_manager = new_web_state->GetNavigationManager(); - if (new_navigation_manager->CanPruneAllButLastCommittedItem()) { - new_navigation_manager->CopyStateFromAndPrune(active_navigation_manager); + bool slim_navigation_manager_enabled = + web::GetWebClient()->IsSlimNavigationManagerEnabled(); + if (new_navigation_manager->CanPruneAllButLastCommittedItem() || + slim_navigation_manager_enabled) { + if (!slim_navigation_manager_enabled) { + new_navigation_manager->CopyStateFromAndPrune(active_navigation_manager); + } loading_prerender_ = true; web_state_list->ReplaceWebStateAt(web_state_list->active_index(), std::move(new_web_state));
diff --git a/ios/chrome/browser/sync/sync_setup_service.cc b/ios/chrome/browser/sync/sync_setup_service.cc index b4ccaba..70b53b79 100644 --- a/ios/chrome/browser/sync/sync_setup_service.cc +++ b/ios/chrome/browser/sync/sync_setup_service.cc
@@ -149,7 +149,8 @@ } if (sync_service_->HasUnrecoverableError()) return kSyncServiceUnrecoverableError; - if (sync_service_->GetUserSettings()->IsPassphraseRequiredForDecryption()) + if (sync_service_->GetUserSettings() + ->IsPassphraseRequiredForPreferredDataTypes()) return kSyncServiceNeedsPassphrase; if (!IsFirstSetupComplete() && IsSyncEnabled()) return kSyncSettingsNotConfirmed;
diff --git a/ios/chrome/browser/ui/authentication/signin_promo_view_mediator.h b/ios/chrome/browser/ui/authentication/signin_promo_view_mediator.h index 0d57d9e9..47e9ea2 100644 --- a/ios/chrome/browser/ui/authentication/signin_promo_view_mediator.h +++ b/ios/chrome/browser/ui/authentication/signin_promo_view_mediator.h
@@ -26,8 +26,9 @@ // way to go backwards. All states can be skipped except |NeverVisible| and // |Invalid|. enum class SigninPromoViewState { - // Initial state. When -[SigninPromoViewMediator signinPromoViewRemoved] is - // called with that state, no metrics is recorded. + // Initial state. When -[SigninPromoViewMediator + // signinPromoViewIsRemoved] is called with that state, no metrics is + // recorded. NeverVisible = 0, // None of the buttons has been used yet. Unused, @@ -55,13 +56,18 @@ // Chrome identity used to configure the view in a warm state mode. Otherwise // contains nil. -@property(nonatomic, readonly) ChromeIdentity* defaultIdentity; +@property(nonatomic, strong, readonly) ChromeIdentity* defaultIdentity; // Sign-in promo view state. -@property(nonatomic) ios::SigninPromoViewState signinPromoViewState; +@property(nonatomic, assign) ios::SigninPromoViewState signinPromoViewState; // YES if the sign-in interaction controller is shown. -@property(nonatomic, readonly, getter=isSigninInProgress) BOOL signinInProgress; +@property(nonatomic, assign, readonly, getter=isSigninInProgress) + BOOL signinInProgress; + +// Returns YES if the sign-in promo view is |Invalid|, |Closed| or invisible. +@property(nonatomic, assign, readonly, getter=isInvalidClosedOrNeverVisible) + BOOL invalidClosedOrNeverVisible; // Registers the feature preferences. + (void)registerBrowserStatePrefs:(user_prefs::PrefRegistrySyncable*)registry; @@ -90,19 +96,16 @@ // Increments the "shown" counter used for histograms. Called when the signin // promo view is visible. If the sign-in promo is already visible, this method // does nothing. -- (void)signinPromoViewVisible; +- (void)signinPromoViewIsVisible; // Called when the sign-in promo view is hidden. If the sign-in promo view has // never been shown, or it is already hidden, this method does nothing. -- (void)signinPromoViewHidden; +- (void)signinPromoViewIsHidden; // Called when the sign-in promo view is removed from the view hierarchy (it or // one of its superviews is removed). The mediator should not be used after this // called. -- (void)signinPromoViewRemoved; - -// Returns YES if the sign-in promo view is |Invalid|, |Closed| or |Invisible|. -- (BOOL)isInvalidClosedOrNeverVisible; +- (void)signinPromoViewIsRemoved; @end
diff --git a/ios/chrome/browser/ui/authentication/signin_promo_view_mediator.mm b/ios/chrome/browser/ui/authentication/signin_promo_view_mediator.mm index 62b194d..dd95fe9 100644 --- a/ios/chrome/browser/ui/authentication/signin_promo_view_mediator.mm +++ b/ios/chrome/browser/ui/authentication/signin_promo_view_mediator.mm
@@ -35,8 +35,12 @@ #endif namespace { + +// Number of times the sign-in promo should be displayed until it is +// automatically dismissed. const int kAutomaticSigninPromoViewDismissCount = 20; +// Returns true if the sign-in promo is supported for |access_point|. bool IsSupportedAccessPoint(signin_metrics::AccessPoint access_point) { switch (access_point) { case signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS: @@ -44,11 +48,36 @@ case signin_metrics::AccessPoint::ACCESS_POINT_RECENT_TABS: case signin_metrics::AccessPoint::ACCESS_POINT_TAB_SWITCHER: return true; - default: + case signin_metrics::AccessPoint::ACCESS_POINT_START_PAGE: + case signin_metrics::AccessPoint::ACCESS_POINT_NTP_LINK: + case signin_metrics::AccessPoint::ACCESS_POINT_MENU: + case signin_metrics::AccessPoint::ACCESS_POINT_SUPERVISED_USER: + case signin_metrics::AccessPoint::ACCESS_POINT_EXTENSION_INSTALL_BUBBLE: + case signin_metrics::AccessPoint::ACCESS_POINT_EXTENSIONS: + case signin_metrics::AccessPoint::ACCESS_POINT_APPS_PAGE_LINK: + case signin_metrics::AccessPoint::ACCESS_POINT_BOOKMARK_BUBBLE: + case signin_metrics::AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN: + case signin_metrics::AccessPoint::ACCESS_POINT_USER_MANAGER: + case signin_metrics::AccessPoint::ACCESS_POINT_DEVICES_PAGE: + case signin_metrics::AccessPoint::ACCESS_POINT_CLOUD_PRINT: + case signin_metrics::AccessPoint::ACCESS_POINT_CONTENT_AREA: + case signin_metrics::AccessPoint::ACCESS_POINT_SIGNIN_PROMO: + case signin_metrics::AccessPoint::ACCESS_POINT_UNKNOWN: + case signin_metrics::AccessPoint::ACCESS_POINT_PASSWORD_BUBBLE: + case signin_metrics::AccessPoint::ACCESS_POINT_AUTOFILL_DROPDOWN: + case signin_metrics::AccessPoint::ACCESS_POINT_NTP_CONTENT_SUGGESTIONS: + case signin_metrics::AccessPoint::ACCESS_POINT_RESIGNIN_INFOBAR: + case signin_metrics::AccessPoint::ACCESS_POINT_SAVE_CARD_BUBBLE: + case signin_metrics::AccessPoint::ACCESS_POINT_MANAGE_CARDS_BUBBLE: + case signin_metrics::AccessPoint::ACCESS_POINT_MACHINE_LOGON: + case signin_metrics::AccessPoint::ACCESS_POINT_GOOGLE_SERVICES_SETTINGS: + case signin_metrics::AccessPoint::ACCESS_POINT_MAX: return false; } } +// Records in histogram, the number of times the sign-in promo is displayed +// before the sign-in button is pressed. void RecordImpressionsTilSigninButtonsHistogramForAccessPoint( signin_metrics::AccessPoint access_point, int displayed_count) { @@ -63,13 +92,40 @@ "MobileSignInPromo.SettingsManager.ImpressionsTilSigninButtons", displayed_count); break; - default: + case signin_metrics::AccessPoint::ACCESS_POINT_RECENT_TABS: + case signin_metrics::AccessPoint::ACCESS_POINT_TAB_SWITCHER: + case signin_metrics::AccessPoint::ACCESS_POINT_START_PAGE: + case signin_metrics::AccessPoint::ACCESS_POINT_NTP_LINK: + case signin_metrics::AccessPoint::ACCESS_POINT_MENU: + case signin_metrics::AccessPoint::ACCESS_POINT_SUPERVISED_USER: + case signin_metrics::AccessPoint::ACCESS_POINT_EXTENSION_INSTALL_BUBBLE: + case signin_metrics::AccessPoint::ACCESS_POINT_EXTENSIONS: + case signin_metrics::AccessPoint::ACCESS_POINT_APPS_PAGE_LINK: + case signin_metrics::AccessPoint::ACCESS_POINT_BOOKMARK_BUBBLE: + case signin_metrics::AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN: + case signin_metrics::AccessPoint::ACCESS_POINT_USER_MANAGER: + case signin_metrics::AccessPoint::ACCESS_POINT_DEVICES_PAGE: + case signin_metrics::AccessPoint::ACCESS_POINT_CLOUD_PRINT: + case signin_metrics::AccessPoint::ACCESS_POINT_CONTENT_AREA: + case signin_metrics::AccessPoint::ACCESS_POINT_SIGNIN_PROMO: + case signin_metrics::AccessPoint::ACCESS_POINT_UNKNOWN: + case signin_metrics::AccessPoint::ACCESS_POINT_PASSWORD_BUBBLE: + case signin_metrics::AccessPoint::ACCESS_POINT_AUTOFILL_DROPDOWN: + case signin_metrics::AccessPoint::ACCESS_POINT_NTP_CONTENT_SUGGESTIONS: + case signin_metrics::AccessPoint::ACCESS_POINT_RESIGNIN_INFOBAR: + case signin_metrics::AccessPoint::ACCESS_POINT_SAVE_CARD_BUBBLE: + case signin_metrics::AccessPoint::ACCESS_POINT_MANAGE_CARDS_BUBBLE: + case signin_metrics::AccessPoint::ACCESS_POINT_MACHINE_LOGON: + case signin_metrics::AccessPoint::ACCESS_POINT_GOOGLE_SERVICES_SETTINGS: + case signin_metrics::AccessPoint::ACCESS_POINT_MAX: NOTREACHED() << "Unexpected value for access point " << static_cast<int>(access_point); break; } } +// Records in histogram, the number of times the sign-in promo is displayed +// before the cancel button is pressed. void RecordImpressionsTilDismissHistogramForAccessPoint( signin_metrics::AccessPoint access_point, int displayed_count) { @@ -84,13 +140,40 @@ "MobileSignInPromo.SettingsManager.ImpressionsTilDismiss", displayed_count); break; - default: + case signin_metrics::AccessPoint::ACCESS_POINT_RECENT_TABS: + case signin_metrics::AccessPoint::ACCESS_POINT_TAB_SWITCHER: + case signin_metrics::AccessPoint::ACCESS_POINT_START_PAGE: + case signin_metrics::AccessPoint::ACCESS_POINT_NTP_LINK: + case signin_metrics::AccessPoint::ACCESS_POINT_MENU: + case signin_metrics::AccessPoint::ACCESS_POINT_SUPERVISED_USER: + case signin_metrics::AccessPoint::ACCESS_POINT_EXTENSION_INSTALL_BUBBLE: + case signin_metrics::AccessPoint::ACCESS_POINT_EXTENSIONS: + case signin_metrics::AccessPoint::ACCESS_POINT_APPS_PAGE_LINK: + case signin_metrics::AccessPoint::ACCESS_POINT_BOOKMARK_BUBBLE: + case signin_metrics::AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN: + case signin_metrics::AccessPoint::ACCESS_POINT_USER_MANAGER: + case signin_metrics::AccessPoint::ACCESS_POINT_DEVICES_PAGE: + case signin_metrics::AccessPoint::ACCESS_POINT_CLOUD_PRINT: + case signin_metrics::AccessPoint::ACCESS_POINT_CONTENT_AREA: + case signin_metrics::AccessPoint::ACCESS_POINT_SIGNIN_PROMO: + case signin_metrics::AccessPoint::ACCESS_POINT_UNKNOWN: + case signin_metrics::AccessPoint::ACCESS_POINT_PASSWORD_BUBBLE: + case signin_metrics::AccessPoint::ACCESS_POINT_AUTOFILL_DROPDOWN: + case signin_metrics::AccessPoint::ACCESS_POINT_NTP_CONTENT_SUGGESTIONS: + case signin_metrics::AccessPoint::ACCESS_POINT_RESIGNIN_INFOBAR: + case signin_metrics::AccessPoint::ACCESS_POINT_SAVE_CARD_BUBBLE: + case signin_metrics::AccessPoint::ACCESS_POINT_MANAGE_CARDS_BUBBLE: + case signin_metrics::AccessPoint::ACCESS_POINT_MACHINE_LOGON: + case signin_metrics::AccessPoint::ACCESS_POINT_GOOGLE_SERVICES_SETTINGS: + case signin_metrics::AccessPoint::ACCESS_POINT_MAX: NOTREACHED() << "Unexpected value for access point " << static_cast<int>(access_point); break; } } +// Records in histogram, the number of times the sign-in promo is displayed +// before the close button is pressed. void RecordImpressionsTilXButtonHistogramForAccessPoint( signin_metrics::AccessPoint access_point, int displayed_count) { @@ -105,13 +188,39 @@ "MobileSignInPromo.SettingsManager.ImpressionsTilXButton", displayed_count); break; - default: + case signin_metrics::AccessPoint::ACCESS_POINT_RECENT_TABS: + case signin_metrics::AccessPoint::ACCESS_POINT_TAB_SWITCHER: + case signin_metrics::AccessPoint::ACCESS_POINT_START_PAGE: + case signin_metrics::AccessPoint::ACCESS_POINT_NTP_LINK: + case signin_metrics::AccessPoint::ACCESS_POINT_MENU: + case signin_metrics::AccessPoint::ACCESS_POINT_SUPERVISED_USER: + case signin_metrics::AccessPoint::ACCESS_POINT_EXTENSION_INSTALL_BUBBLE: + case signin_metrics::AccessPoint::ACCESS_POINT_EXTENSIONS: + case signin_metrics::AccessPoint::ACCESS_POINT_APPS_PAGE_LINK: + case signin_metrics::AccessPoint::ACCESS_POINT_BOOKMARK_BUBBLE: + case signin_metrics::AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN: + case signin_metrics::AccessPoint::ACCESS_POINT_USER_MANAGER: + case signin_metrics::AccessPoint::ACCESS_POINT_DEVICES_PAGE: + case signin_metrics::AccessPoint::ACCESS_POINT_CLOUD_PRINT: + case signin_metrics::AccessPoint::ACCESS_POINT_CONTENT_AREA: + case signin_metrics::AccessPoint::ACCESS_POINT_SIGNIN_PROMO: + case signin_metrics::AccessPoint::ACCESS_POINT_UNKNOWN: + case signin_metrics::AccessPoint::ACCESS_POINT_PASSWORD_BUBBLE: + case signin_metrics::AccessPoint::ACCESS_POINT_AUTOFILL_DROPDOWN: + case signin_metrics::AccessPoint::ACCESS_POINT_NTP_CONTENT_SUGGESTIONS: + case signin_metrics::AccessPoint::ACCESS_POINT_RESIGNIN_INFOBAR: + case signin_metrics::AccessPoint::ACCESS_POINT_SAVE_CARD_BUBBLE: + case signin_metrics::AccessPoint::ACCESS_POINT_MANAGE_CARDS_BUBBLE: + case signin_metrics::AccessPoint::ACCESS_POINT_MACHINE_LOGON: + case signin_metrics::AccessPoint::ACCESS_POINT_GOOGLE_SERVICES_SETTINGS: + case signin_metrics::AccessPoint::ACCESS_POINT_MAX: NOTREACHED() << "Unexpected value for access point " << static_cast<int>(access_point); break; } } +// Returns the DisplayedCount preference key string for |access_point|. const char* DisplayedCountPreferenceKey( signin_metrics::AccessPoint access_point) { switch (access_point) { @@ -119,11 +228,37 @@ return prefs::kIosBookmarkSigninPromoDisplayedCount; case signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS: return prefs::kIosSettingsSigninPromoDisplayedCount; - default: + case signin_metrics::AccessPoint::ACCESS_POINT_RECENT_TABS: + case signin_metrics::AccessPoint::ACCESS_POINT_TAB_SWITCHER: + case signin_metrics::AccessPoint::ACCESS_POINT_START_PAGE: + case signin_metrics::AccessPoint::ACCESS_POINT_NTP_LINK: + case signin_metrics::AccessPoint::ACCESS_POINT_MENU: + case signin_metrics::AccessPoint::ACCESS_POINT_SUPERVISED_USER: + case signin_metrics::AccessPoint::ACCESS_POINT_EXTENSION_INSTALL_BUBBLE: + case signin_metrics::AccessPoint::ACCESS_POINT_EXTENSIONS: + case signin_metrics::AccessPoint::ACCESS_POINT_APPS_PAGE_LINK: + case signin_metrics::AccessPoint::ACCESS_POINT_BOOKMARK_BUBBLE: + case signin_metrics::AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN: + case signin_metrics::AccessPoint::ACCESS_POINT_USER_MANAGER: + case signin_metrics::AccessPoint::ACCESS_POINT_DEVICES_PAGE: + case signin_metrics::AccessPoint::ACCESS_POINT_CLOUD_PRINT: + case signin_metrics::AccessPoint::ACCESS_POINT_CONTENT_AREA: + case signin_metrics::AccessPoint::ACCESS_POINT_SIGNIN_PROMO: + case signin_metrics::AccessPoint::ACCESS_POINT_UNKNOWN: + case signin_metrics::AccessPoint::ACCESS_POINT_PASSWORD_BUBBLE: + case signin_metrics::AccessPoint::ACCESS_POINT_AUTOFILL_DROPDOWN: + case signin_metrics::AccessPoint::ACCESS_POINT_NTP_CONTENT_SUGGESTIONS: + case signin_metrics::AccessPoint::ACCESS_POINT_RESIGNIN_INFOBAR: + case signin_metrics::AccessPoint::ACCESS_POINT_SAVE_CARD_BUBBLE: + case signin_metrics::AccessPoint::ACCESS_POINT_MANAGE_CARDS_BUBBLE: + case signin_metrics::AccessPoint::ACCESS_POINT_MACHINE_LOGON: + case signin_metrics::AccessPoint::ACCESS_POINT_GOOGLE_SERVICES_SETTINGS: + case signin_metrics::AccessPoint::ACCESS_POINT_MAX: return nullptr; } } +// Returns AlreadySeen preference key string for |access_point|. const char* AlreadySeenSigninViewPreferenceKey( signin_metrics::AccessPoint access_point) { switch (access_point) { @@ -131,36 +266,72 @@ return prefs::kIosBookmarkPromoAlreadySeen; case signin_metrics::AccessPoint::ACCESS_POINT_SETTINGS: return prefs::kIosSettingsPromoAlreadySeen; - default: + case signin_metrics::AccessPoint::ACCESS_POINT_RECENT_TABS: + case signin_metrics::AccessPoint::ACCESS_POINT_TAB_SWITCHER: + case signin_metrics::AccessPoint::ACCESS_POINT_START_PAGE: + case signin_metrics::AccessPoint::ACCESS_POINT_NTP_LINK: + case signin_metrics::AccessPoint::ACCESS_POINT_MENU: + case signin_metrics::AccessPoint::ACCESS_POINT_SUPERVISED_USER: + case signin_metrics::AccessPoint::ACCESS_POINT_EXTENSION_INSTALL_BUBBLE: + case signin_metrics::AccessPoint::ACCESS_POINT_EXTENSIONS: + case signin_metrics::AccessPoint::ACCESS_POINT_APPS_PAGE_LINK: + case signin_metrics::AccessPoint::ACCESS_POINT_BOOKMARK_BUBBLE: + case signin_metrics::AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN: + case signin_metrics::AccessPoint::ACCESS_POINT_USER_MANAGER: + case signin_metrics::AccessPoint::ACCESS_POINT_DEVICES_PAGE: + case signin_metrics::AccessPoint::ACCESS_POINT_CLOUD_PRINT: + case signin_metrics::AccessPoint::ACCESS_POINT_CONTENT_AREA: + case signin_metrics::AccessPoint::ACCESS_POINT_SIGNIN_PROMO: + case signin_metrics::AccessPoint::ACCESS_POINT_UNKNOWN: + case signin_metrics::AccessPoint::ACCESS_POINT_PASSWORD_BUBBLE: + case signin_metrics::AccessPoint::ACCESS_POINT_AUTOFILL_DROPDOWN: + case signin_metrics::AccessPoint::ACCESS_POINT_NTP_CONTENT_SUGGESTIONS: + case signin_metrics::AccessPoint::ACCESS_POINT_RESIGNIN_INFOBAR: + case signin_metrics::AccessPoint::ACCESS_POINT_SAVE_CARD_BUBBLE: + case signin_metrics::AccessPoint::ACCESS_POINT_MANAGE_CARDS_BUBBLE: + case signin_metrics::AccessPoint::ACCESS_POINT_MACHINE_LOGON: + case signin_metrics::AccessPoint::ACCESS_POINT_GOOGLE_SERVICES_SETTINGS: + case signin_metrics::AccessPoint::ACCESS_POINT_MAX: return nullptr; } } + } // namespace -@interface SigninPromoViewMediator ()<ChromeIdentityServiceObserver, - ChromeBrowserProviderObserver> -// Presenter which can show signin UI. -@property(nonatomic, readonly, weak) id<SigninPresenter> presenter; - -// Redefined to be readwrite. -@property(nonatomic, readwrite, getter=isSigninInProgress) - BOOL signinInProgress; -@end - -@implementation SigninPromoViewMediator { - ios::ChromeBrowserState* _browserState; - signin_metrics::AccessPoint _accessPoint; +@interface SigninPromoViewMediator () <ChromeBrowserProviderObserver, + ChromeIdentityServiceObserver> { std::unique_ptr<ChromeIdentityServiceObserverBridge> _identityServiceObserver; std::unique_ptr<ChromeBrowserProviderObserverBridge> _browserProviderObserver; - UIImage* _identityAvatar; - BOOL _isSigninPromoViewVisible; } -@synthesize consumer = _consumer; -@synthesize defaultIdentity = _defaultIdentity; -@synthesize signinPromoViewState = _signinPromoViewState; -@synthesize presenter = _presenter; -@synthesize signinInProgress = _signinInProgress; +// Redefined to be readwrite. +@property(nonatomic, strong, readwrite) ChromeIdentity* defaultIdentity; +@property(nonatomic, assign, readwrite, getter=isSigninInProgress) + BOOL signinInProgress; + +// Presenter which can show signin UI. +@property(nonatomic, weak, readonly) id<SigninPresenter> presenter; + +// The coordinator's BrowserState. +@property(nonatomic, assign, readonly) ios::ChromeBrowserState* browserState; + +// The access point for the sign-in promo view. +@property(nonatomic, assign, readonly) signin_metrics::AccessPoint accessPoint; + +// Identity avatar. +@property(nonatomic, strong) UIImage* identityAvatar; + +// YES if the sign-in promo is currently visible by the user. +@property(nonatomic, assign, getter=isSigninPromoViewVisible) + BOOL signinPromoViewVisible; + +// YES if the sign-in promo is either invalid or closed. +@property(nonatomic, assign, readonly, getter=isInvalidOrClosed) + BOOL invalidOrClosed; + +@end + +@implementation SigninPromoViewMediator + (void)registerBrowserStatePrefs:(user_prefs::PrefRegistrySyncable*)registry { // Bookmarks @@ -221,24 +392,14 @@ DCHECK_EQ(ios::SigninPromoViewState::Invalid, _signinPromoViewState); } -- (BOOL)isInvalidOrClosed { - return _signinPromoViewState == ios::SigninPromoViewState::Closed || - _signinPromoViewState == ios::SigninPromoViewState::Invalid; -} - -- (BOOL)isInvalidClosedOrNeverVisible { - return [self isInvalidOrClosed] || - _signinPromoViewState == ios::SigninPromoViewState::NeverVisible; -} - - (SigninPromoViewConfigurator*)createConfigurator { BOOL hasCloseButton = - AlreadySeenSigninViewPreferenceKey(_accessPoint) != nullptr; + AlreadySeenSigninViewPreferenceKey(self.accessPoint) != nullptr; if (_defaultIdentity) { return [[SigninPromoViewConfigurator alloc] initWithUserEmail:_defaultIdentity.userEmail userFullName:_defaultIdentity.userFullName - userImage:_identityAvatar + userImage:self.identityAvatar hasCloseButton:hasCloseButton]; } return [[SigninPromoViewConfigurator alloc] initWithUserEmail:nil @@ -247,10 +408,83 @@ hasCloseButton:hasCloseButton]; } +- (void)signinPromoViewIsVisible { + DCHECK(!self.invalidOrClosed); + if (self.signinPromoViewVisible) + return; + if (self.signinPromoViewState == ios::SigninPromoViewState::NeverVisible) + self.signinPromoViewState = ios::SigninPromoViewState::Unused; + self.signinPromoViewVisible = YES; + signin_metrics::RecordSigninImpressionUserActionForAccessPoint( + self.accessPoint); + signin_metrics::RecordSigninImpressionWithAccountUserActionForAccessPoint( + self.accessPoint, !!_defaultIdentity); + const char* displayedCountPreferenceKey = + DisplayedCountPreferenceKey(self.accessPoint); + if (!displayedCountPreferenceKey) + return; + PrefService* prefs = self.browserState->GetPrefs(); + int displayedCount = prefs->GetInteger(displayedCountPreferenceKey); + ++displayedCount; + prefs->SetInteger(displayedCountPreferenceKey, displayedCount); +} + +- (void)signinPromoViewIsHidden { + DCHECK(!self.invalidOrClosed); + self.signinPromoViewVisible = NO; +} + +- (void)signinPromoViewIsRemoved { + DCHECK_NE(ios::SigninPromoViewState::Invalid, self.signinPromoViewState); + DCHECK(!self.signinInProgress); + BOOL wasNeverVisible = + self.signinPromoViewState == ios::SigninPromoViewState::NeverVisible; + BOOL wasUnused = + self.signinPromoViewState == ios::SigninPromoViewState::Unused; + self.signinPromoViewState = ios::SigninPromoViewState::Invalid; + self.signinPromoViewVisible = NO; + if (wasNeverVisible) + return; + // If the sign-in promo view has been used at least once, it should not be + // counted as dismissed (even if the sign-in has been canceled). + const char* displayedCountPreferenceKey = + DisplayedCountPreferenceKey(self.accessPoint); + if (!displayedCountPreferenceKey || !wasUnused) + return; + // If the sign-in view is removed when the user is authenticated, then the + // sign-in has been done by another view, and this mediator cannot be counted + // as being dismissed. + AuthenticationService* authService = + AuthenticationServiceFactory::GetForBrowserState(self.browserState); + if (authService->IsAuthenticated()) + return; + PrefService* prefs = self.browserState->GetPrefs(); + int displayedCount = prefs->GetInteger(displayedCountPreferenceKey); + RecordImpressionsTilDismissHistogramForAccessPoint(self.accessPoint, + displayedCount); +} + +#pragma mark - Public properties + +- (BOOL)isInvalidClosedOrNeverVisible { + return self.invalidOrClosed || + self.signinPromoViewState == ios::SigninPromoViewState::NeverVisible; +} + +#pragma mark - Private properties + +- (BOOL)isInvalidOrClosed { + return self.signinPromoViewState == ios::SigninPromoViewState::Closed || + self.signinPromoViewState == ios::SigninPromoViewState::Invalid; +} + +#pragma mark - Private + +// Sets the Chrome identity to display in the sign-in promo. - (void)selectIdentity:(ChromeIdentity*)identity { _defaultIdentity = identity; if (!_defaultIdentity) { - _identityAvatar = nil; + self.identityAvatar = nil; } else { __weak SigninPromoViewMediator* weakSelf = self; ios::GetChromeBrowserProvider() @@ -264,8 +498,9 @@ } } +// Updates the Chrome identity avatar in the sign-in promo. - (void)identityAvatarUpdated:(UIImage*)identityAvatar { - _identityAvatar = identityAvatar; + self.identityAvatar = identityAvatar; [self sendConsumerNotificationWithIdentityChanged:NO]; } @@ -276,87 +511,39 @@ if (self.signinInProgress) return; SigninPromoViewConfigurator* configurator = [self createConfigurator]; - [_consumer configureSigninPromoWithConfigurator:configurator - identityChanged:identityChanged]; + [self.consumer configureSigninPromoWithConfigurator:configurator + identityChanged:identityChanged]; } +// Records in histogram, the number of time the sign-in promo is displayed +// before the sign-in button is pressed, if the current access point supports +// it. - (void)sendImpressionsTillSigninButtonsHistogram { - DCHECK(![self isInvalidClosedOrNeverVisible]); + DCHECK(!self.invalidClosedOrNeverVisible); const char* displayedCountPreferenceKey = - DisplayedCountPreferenceKey(_accessPoint); + DisplayedCountPreferenceKey(self.accessPoint); if (!displayedCountPreferenceKey) return; - PrefService* prefs = _browserState->GetPrefs(); + PrefService* prefs = self.browserState->GetPrefs(); int displayedCount = prefs->GetInteger(displayedCountPreferenceKey); - RecordImpressionsTilSigninButtonsHistogramForAccessPoint(_accessPoint, + RecordImpressionsTilSigninButtonsHistogramForAccessPoint(self.accessPoint, displayedCount); } -- (void)signinPromoViewVisible { - DCHECK(![self isInvalidOrClosed]); - if (_isSigninPromoViewVisible) - return; - if (_signinPromoViewState == ios::SigninPromoViewState::NeverVisible) - _signinPromoViewState = ios::SigninPromoViewState::Unused; - _isSigninPromoViewVisible = YES; - signin_metrics::RecordSigninImpressionUserActionForAccessPoint(_accessPoint); - signin_metrics::RecordSigninImpressionWithAccountUserActionForAccessPoint( - _accessPoint, !!_defaultIdentity); - const char* displayedCountPreferenceKey = - DisplayedCountPreferenceKey(_accessPoint); - if (!displayedCountPreferenceKey) - return; - PrefService* prefs = _browserState->GetPrefs(); - int displayedCount = prefs->GetInteger(displayedCountPreferenceKey); - ++displayedCount; - prefs->SetInteger(displayedCountPreferenceKey, displayedCount); -} - -- (void)signinPromoViewHidden { - DCHECK(![self isInvalidOrClosed]); - _isSigninPromoViewVisible = NO; -} - -- (void)signinPromoViewRemoved { - DCHECK_NE(ios::SigninPromoViewState::Invalid, _signinPromoViewState); - DCHECK(!self.signinInProgress); - BOOL wasNeverVisible = - _signinPromoViewState == ios::SigninPromoViewState::NeverVisible; - BOOL wasUnused = _signinPromoViewState == ios::SigninPromoViewState::Unused; - _signinPromoViewState = ios::SigninPromoViewState::Invalid; - _isSigninPromoViewVisible = NO; - if (wasNeverVisible) - return; - // If the sign-in promo view has been used at least once, it should not be - // counted as dismissed (even if the sign-in has been canceled). - const char* displayedCountPreferenceKey = - DisplayedCountPreferenceKey(_accessPoint); - if (!displayedCountPreferenceKey || !wasUnused) - return; - // If the sign-in view is removed when the user is authenticated, then the - // sign-in has been done by another view, and this mediator cannot be counted - // as being dismissed. - AuthenticationService* authService = - AuthenticationServiceFactory::GetForBrowserState(_browserState); - if (authService->IsAuthenticated()) - return; - PrefService* prefs = _browserState->GetPrefs(); - int displayedCount = prefs->GetInteger(displayedCountPreferenceKey); - RecordImpressionsTilDismissHistogramForAccessPoint(_accessPoint, - displayedCount); -} - +// Finishes the sign-in process. - (void)signinCallback { - DCHECK_EQ(ios::SigninPromoViewState::UsedAtLeastOnce, _signinPromoViewState); + DCHECK_EQ(ios::SigninPromoViewState::UsedAtLeastOnce, + self.signinPromoViewState); DCHECK(self.signinInProgress); self.signinInProgress = NO; - if ([_consumer respondsToSelector:@selector(signinDidFinish)]) - [_consumer signinDidFinish]; + if ([self.consumer respondsToSelector:@selector(signinDidFinish)]) + [self.consumer signinDidFinish]; } +// Starts sign-in process with the Chrome identity from |identity|. - (void)showSigninWithIdentity:(ChromeIdentity*)identity promoAction:(signin_metrics::PromoAction)promoAction { - _signinPromoViewState = ios::SigninPromoViewState::UsedAtLeastOnce; + self.signinPromoViewState = ios::SigninPromoViewState::UsedAtLeastOnce; self.signinInProgress = YES; __weak SigninPromoViewMediator* weakSelf = self; ShowSigninCommandCompletionCallback completion = ^(BOOL succeeded) { @@ -373,7 +560,7 @@ ShowSigninCommand* command = [[ShowSigninCommand alloc] initWithOperation:AUTHENTICATION_OPERATION_SIGNIN identity:identity - accessPoint:_accessPoint + accessPoint:self.accessPoint promoAction:promoAction callback:completion]; [self.presenter showSignin:command]; @@ -421,15 +608,15 @@ - (void)signinPromoViewDidTapSigninWithNewAccount: (SigninPromoView*)signinPromoView { DCHECK(!_defaultIdentity); - DCHECK(_isSigninPromoViewVisible); - DCHECK(![self isInvalidClosedOrNeverVisible]); + DCHECK(self.signinPromoViewVisible); + DCHECK(!self.invalidClosedOrNeverVisible); [self sendImpressionsTillSigninButtonsHistogram]; // On iOS, the promo does not have a button to add and account when there is // already an account on the device. That flow goes through the NOT_DEFAULT // promo instead. Always use the NO_EXISTING_ACCOUNT variant. signin_metrics::PromoAction promo_action = signin_metrics::PromoAction::PROMO_ACTION_NEW_ACCOUNT_NO_EXISTING_ACCOUNT; - signin_metrics::RecordSigninUserActionForAccessPoint(_accessPoint, + signin_metrics::RecordSigninUserActionForAccessPoint(self.accessPoint, promo_action); [self showSigninWithIdentity:nil promoAction:promo_action]; } @@ -437,12 +624,12 @@ - (void)signinPromoViewDidTapSigninWithDefaultAccount: (SigninPromoView*)signinPromoView { DCHECK(_defaultIdentity); - DCHECK(_isSigninPromoViewVisible); - DCHECK(![self isInvalidClosedOrNeverVisible]); + DCHECK(self.signinPromoViewVisible); + DCHECK(!self.invalidClosedOrNeverVisible); [self sendImpressionsTillSigninButtonsHistogram]; signin_metrics::PromoAction promo_action = signin_metrics::PromoAction::PROMO_ACTION_WITH_DEFAULT; - signin_metrics::RecordSigninUserActionForAccessPoint(_accessPoint, + signin_metrics::RecordSigninUserActionForAccessPoint(self.accessPoint, promo_action); [self showSigninWithIdentity:_defaultIdentity promoAction:promo_action]; } @@ -450,35 +637,35 @@ - (void)signinPromoViewDidTapSigninWithOtherAccount: (SigninPromoView*)signinPromoView { DCHECK(_defaultIdentity); - DCHECK(_isSigninPromoViewVisible); - DCHECK(![self isInvalidClosedOrNeverVisible]); + DCHECK(self.signinPromoViewVisible); + DCHECK(!self.invalidClosedOrNeverVisible); [self sendImpressionsTillSigninButtonsHistogram]; signin_metrics::PromoAction promo_action = signin_metrics::PromoAction::PROMO_ACTION_NOT_DEFAULT; - signin_metrics::RecordSigninUserActionForAccessPoint(_accessPoint, + signin_metrics::RecordSigninUserActionForAccessPoint(self.accessPoint, promo_action); [self showSigninWithIdentity:nil promoAction:promo_action]; } - (void)signinPromoViewCloseButtonWasTapped:(SigninPromoView*)view { - DCHECK(_isSigninPromoViewVisible); - DCHECK(![self isInvalidClosedOrNeverVisible]); - _signinPromoViewState = ios::SigninPromoViewState::Closed; + DCHECK(self.signinPromoViewVisible); + DCHECK(!self.invalidClosedOrNeverVisible); + self.signinPromoViewState = ios::SigninPromoViewState::Closed; const char* alreadySeenSigninViewPreferenceKey = - AlreadySeenSigninViewPreferenceKey(_accessPoint); + AlreadySeenSigninViewPreferenceKey(self.accessPoint); DCHECK(alreadySeenSigninViewPreferenceKey); - PrefService* prefs = _browserState->GetPrefs(); + PrefService* prefs = self.browserState->GetPrefs(); prefs->SetBoolean(alreadySeenSigninViewPreferenceKey, true); const char* displayedCountPreferenceKey = - DisplayedCountPreferenceKey(_accessPoint); + DisplayedCountPreferenceKey(self.accessPoint); if (displayedCountPreferenceKey) { int displayedCount = prefs->GetInteger(displayedCountPreferenceKey); - RecordImpressionsTilXButtonHistogramForAccessPoint(_accessPoint, + RecordImpressionsTilXButtonHistogramForAccessPoint(self.accessPoint, displayedCount); } - if ([_consumer respondsToSelector:@selector - (signinPromoViewMediatorCloseButtonWasTapped:)]) { - [_consumer signinPromoViewMediatorCloseButtonWasTapped:self]; + if ([self.consumer respondsToSelector:@selector + (signinPromoViewMediatorCloseButtonWasTapped:)]) { + [self.consumer signinPromoViewMediatorCloseButtonWasTapped:self]; } }
diff --git a/ios/chrome/browser/ui/authentication/signin_promo_view_mediator_unittest.mm b/ios/chrome/browser/ui/authentication/signin_promo_view_mediator_unittest.mm index fe3eb914..858a1e8 100644 --- a/ios/chrome/browser/ui/authentication/signin_promo_view_mediator_unittest.mm +++ b/ios/chrome/browser/ui/authentication/signin_promo_view_mediator_unittest.mm
@@ -47,7 +47,7 @@ // in the test. EXPECT_FALSE(ios::FakeChromeIdentityService::GetInstanceFromChromeProvider() ->HasPendingCallback()); - [mediator_ signinPromoViewRemoved]; + [mediator_ signinPromoViewIsRemoved]; EXPECT_EQ(ios::SigninPromoViewState::Invalid, mediator_.signinPromoViewState); mediator_ = nil; @@ -267,13 +267,13 @@ } // Tests the view state before and after calling -[SigninPromoViewMediator -// signinPromoViewVisible]. +// signinPromoViewIsVisible]. TEST_F(SigninPromoViewMediatorTest, SigninPromoViewStateVisible) { CreateMediator(signin_metrics::AccessPoint::ACCESS_POINT_RECENT_TABS); // Test initial state. EXPECT_EQ(ios::SigninPromoViewState::NeverVisible, mediator_.signinPromoViewState); - [mediator_ signinPromoViewVisible]; + [mediator_ signinPromoViewIsVisible]; // Test state once the sign-in promo view is visible. EXPECT_EQ(ios::SigninPromoViewState::Unused, mediator_.signinPromoViewState); } @@ -281,7 +281,7 @@ // Tests the view state while signing in. TEST_F(SigninPromoViewMediatorTest, SigninPromoViewStateSignedin) { CreateMediator(signin_metrics::AccessPoint::ACCESS_POINT_RECENT_TABS); - [mediator_ signinPromoViewVisible]; + [mediator_ signinPromoViewIsVisible]; __block ShowSigninCommandCompletionCallback completion; ShowSigninCommandCompletionCallback completion_arg = [OCMArg checkWithBlock:^BOOL(ShowSigninCommandCompletionCallback value) { @@ -297,14 +297,14 @@ PROMO_ACTION_NEW_ACCOUNT_NO_EXISTING_ACCOUNT completion:completion_arg]); [mediator_ signinPromoViewDidTapSigninWithNewAccount:signin_promo_view_]; - EXPECT_TRUE(mediator_.isSigninInProgress); + EXPECT_TRUE(mediator_.signinInProgress); EXPECT_EQ(ios::SigninPromoViewState::UsedAtLeastOnce, mediator_.signinPromoViewState); EXPECT_NE(nil, (id)completion); // Stop sign-in. OCMExpect([consumer_ signinDidFinish]); completion(YES); - EXPECT_FALSE(mediator_.isSigninInProgress); + EXPECT_FALSE(mediator_.signinInProgress); EXPECT_EQ(ios::SigninPromoViewState::UsedAtLeastOnce, mediator_.signinPromoViewState); } @@ -314,7 +314,7 @@ TEST_F(SigninPromoViewMediatorTest, SigninPromoViewNoUpdateNotificationWhileSignin) { CreateMediator(signin_metrics::AccessPoint::ACCESS_POINT_RECENT_TABS); - [mediator_ signinPromoViewVisible]; + [mediator_ signinPromoViewIsVisible]; __block ShowSigninCommandCompletionCallback completion; ShowSigninCommandCompletionCallback completion_arg = [OCMArg checkWithBlock:^BOOL(ShowSigninCommandCompletionCallback value) { @@ -345,7 +345,7 @@ SigninPromoViewNoUpdateNotificationWhileSignin2) { AddDefaultIdentity(); CreateMediator(signin_metrics::AccessPoint::ACCESS_POINT_RECENT_TABS); - [mediator_ signinPromoViewVisible]; + [mediator_ signinPromoViewIsVisible]; __block ShowSigninCommandCompletionCallback completion; ShowSigninCommandCompletionCallback completion_arg = [OCMArg checkWithBlock:^BOOL(ShowSigninCommandCompletionCallback value) {
diff --git a/ios/chrome/browser/ui/badges/badge_constants.h b/ios/chrome/browser/ui/badges/badge_constants.h index 99db012..0fb1223b 100644 --- a/ios/chrome/browser/ui/badges/badge_constants.h +++ b/ios/chrome/browser/ui/badges/badge_constants.h
@@ -16,4 +16,7 @@ // A11y identifier for the Badge Popup Menu Table View. extern NSString* const kBadgePopupMenuTableViewAccessibilityIdentifier; +// A11y identifier for the unread indicator above the displayed badge. +extern NSString* const kBadgeUnreadIndicatorAccessibilityIdentifier; + #endif // IOS_CHROME_BROWSER_UI_BADGES_BADGE_CONSTANTS_H_
diff --git a/ios/chrome/browser/ui/badges/badge_constants.mm b/ios/chrome/browser/ui/badges/badge_constants.mm index 803194f..a49b4b5 100644 --- a/ios/chrome/browser/ui/badges/badge_constants.mm +++ b/ios/chrome/browser/ui/badges/badge_constants.mm
@@ -22,3 +22,6 @@ NSString* const kBadgePopupMenuTableViewAccessibilityIdentifier = @"badgePopupMenuOverflowAXID"; + +NSString* const kBadgeUnreadIndicatorAccessibilityIdentifier = + @"badgeUnreadIndicatorAXID";
diff --git a/ios/chrome/browser/ui/badges/badge_consumer.h b/ios/chrome/browser/ui/badges/badge_consumer.h index e55d3830..a02638f 100644 --- a/ios/chrome/browser/ui/badges/badge_consumer.h +++ b/ios/chrome/browser/ui/badges/badge_consumer.h
@@ -17,6 +17,9 @@ // |displayedBadgeItem| and |fullscreenBadgeItem|. - (void)updateDisplayedBadge:(id<BadgeItem>)displayedBadgeItem fullScreenBadge:(id<BadgeItem>)fullscreenBadgeItem; +// Notifies the consumer whether or not there are unread badges. See +// BadgeStateRead for more information. +- (void)markDisplayedBadgeAsRead:(BOOL)read; @end
diff --git a/ios/chrome/browser/ui/badges/badge_item.h b/ios/chrome/browser/ui/badges/badge_item.h index 86287287..63f2f75 100644 --- a/ios/chrome/browser/ui/badges/badge_item.h +++ b/ios/chrome/browser/ui/badges/badge_item.h
@@ -11,13 +11,18 @@ // States for the InfobarBadge. typedef NS_OPTIONS(NSUInteger, BadgeState) { - // The badge is not accepted. + // The badge has not been accepted nor has it been read. BadgeStateNone = 0, + // This property is set if it is read (i.e. the menu is opened, if it is set + // as the displayed badge, or if the user has accepted the badge action). + // Not set if the user has not seen the badge yet (e.g. the badge is in the + // overflow menu and the user has yet to open the menu). + BadgeStateRead = 1 << 0, // The badge's banner is currently being presented. - BadgeStatePresented = 1 << 0, + BadgeStatePresented = 1 << 1, // The Infobar Badge is accepted. e.g. The Infobar was accepted/confirmed, and // the Infobar action has taken place. - BadgeStateAccepted = 1 << 1, + BadgeStateAccepted = 1 << 2, }; // Holds properties and values the UI needs to configure a badge button.
diff --git a/ios/chrome/browser/ui/badges/badge_mediator.mm b/ios/chrome/browser/ui/badges/badge_mediator.mm index 7586443..bd174f67 100644 --- a/ios/chrome/browser/ui/badges/badge_mediator.mm +++ b/ios/chrome/browser/ui/badges/badge_mediator.mm
@@ -146,10 +146,14 @@ // Get all non-fullscreen badges. for (id<BadgeItem> item in self.badges) { if (!item.fullScreen) { + // Mark each badge as read since the overflow menu is about to be + // displayed. + item.badgeState |= BadgeStateRead; [popupMenuBadges addObject:item]; } } [self.dispatcher displayPopupMenuWithBadgeItems:popupMenuBadges]; + [self updateConsumerReadStatus]; // TODO(crbug.com/976901): Add metric for this action. } @@ -178,6 +182,18 @@ #pragma mark - Private +// Directs consumer to update read status depending on the state of the +// non-fullscreen badges. +- (void)updateConsumerReadStatus { + for (id<BadgeItem> item in self.badges) { + if (!item.fullScreen && item.badgeState & BadgeStateRead) { + [self.consumer markDisplayedBadgeAsRead:NO]; + return; + } + } + [self.consumer markDisplayedBadgeAsRead:YES]; +} + // Gets the last fullscreen and non-fullscreen badges. // This assumes that there is only ever one fullscreen badge, so the last badge // in |badges| should be the only one. @@ -198,7 +214,7 @@ if (item.fullScreen) { fullScreenBadge = item; } else { - if (item.badgeState == BadgeStatePresented) { + if (item.badgeState & BadgeStatePresented) { presentingBadge = item; } displayedBadge = item; @@ -218,9 +234,14 @@ ? presentingBadge : [[BadgeTappableItem alloc] initWithBadgeType:BadgeType::kBadgeTypeOverflow]; + } else { + // Since there is only one non-fullscreen badge, it will be fixed as the + // displayed badge, so mark it as read. + displayedBadge.badgeState |= BadgeStateRead; } [self.consumer updateDisplayedBadge:displayedBadge fullScreenBadge:fullScreenBadge]; + [self updateConsumerReadStatus]; } - (void)updateNewWebState:(web::WebState*)newWebState
diff --git a/ios/chrome/browser/ui/badges/badge_mediator_unittest.mm b/ios/chrome/browser/ui/badges/badge_mediator_unittest.mm index e2e770914..f6545cc 100644 --- a/ios/chrome/browser/ui/badges/badge_mediator_unittest.mm +++ b/ios/chrome/browser/ui/badges/badge_mediator_unittest.mm
@@ -59,6 +59,7 @@ @interface FakeBadgeConsumer : NSObject <BadgeConsumer> @property(nonatomic, strong) id<BadgeItem> displayedBadge; @property(nonatomic, assign) BOOL hasIncognitoBadge; +@property(nonatomic, assign) BOOL hasUnreadBadge; @end @implementation FakeBadgeConsumer @@ -72,6 +73,9 @@ self.hasIncognitoBadge = fullscreenBadgeItem != nil; self.displayedBadge = displayedBadgeItem; } +- (void)markDisplayedBadgeAsRead:(BOOL)read { + self.hasUnreadBadge = !read; +} @end class BadgeMediatorTest : public PlatformTest { @@ -165,6 +169,15 @@ BadgeType::kBadgeTypePasswordSave); } +TEST_F(BadgeMediatorTest, BadgeMediatorTestMarkAsRead) { + AddAndActivateWebState(/*index=*/0, /*incognito=*/false); + AddInfobar(); + AddSecondInfobar(); + ASSERT_EQ(BadgeType::kBadgeTypeOverflow, + badge_consumer_.displayedBadge.badgeType); + EXPECT_TRUE(badge_consumer_.hasUnreadBadge); +} + // Test that the BadgeMediator updates the current badges to none when switching // to a second WebState after an infobar is added to the first WebState. TEST_F(BadgeMediatorTest, BadgeMediatorTestSwitchWebState) {
diff --git a/ios/chrome/browser/ui/badges/badge_view_controller.mm b/ios/chrome/browser/ui/badges/badge_view_controller.mm index ccb00aa..fe2314bf 100644 --- a/ios/chrome/browser/ui/badges/badge_view_controller.mm +++ b/ios/chrome/browser/ui/badges/badge_view_controller.mm
@@ -7,9 +7,11 @@ #include "base/logging.h" #import "ios/chrome/browser/ui/badges/badge_button.h" #import "ios/chrome/browser/ui/badges/badge_button_factory.h" +#import "ios/chrome/browser/ui/badges/badge_constants.h" #import "ios/chrome/browser/ui/badges/badge_item.h" #import "ios/chrome/browser/ui/elements/extended_touch_target_button.h" #import "ios/chrome/browser/ui/util/named_guide.h" +#import "ios/chrome/common/colors/semantic_color_names.h" #import "ios/chrome/common/ui_util/constraints_ui_util.h" #if !defined(__has_feature) || !__has_feature(objc_arc) @@ -20,7 +22,14 @@ // FullScreen progress threshold in which to toggle between full screen on and // off mode for the badge view. -const double kFullScreenProgressThreshold = 0.85; +const CGFloat kFullScreenProgressThreshold = 0.85; + +// Spacing between the top and trailing anchors of |unreadIndicatorView| and +// |displayedBadge|. +const CGFloat kUnreadIndicatorViewSpacing = 10.0; + +// Height of |unreadIndicatorView|. +const CGFloat kUnreadIndicatorViewHeight = 6.0; } // namespace @@ -43,6 +52,10 @@ // StackView holding the displayedBadge and fullScreenBadge. @property(nonatomic, strong) UIStackView* stackView; +// View that displays a blue dot on the top-right corner of the displayed badge +// if there are unread badges to be shown in the overflow menu. +@property(nonatomic, strong) UIView* unreadIndicatorView; + @end @implementation BadgeViewController @@ -117,6 +130,37 @@ } } +- (void)markDisplayedBadgeAsRead:(BOOL)read { + // Lazy init if the unread indicator needs to be shown. + if (!self.unreadIndicatorView && !read) { + // Add unread indicator to the displayed badge. + self.unreadIndicatorView = [[UIView alloc] init]; + self.unreadIndicatorView.layer.cornerRadius = + kUnreadIndicatorViewHeight / 2; + self.unreadIndicatorView.backgroundColor = + [UIColor colorNamed:kToolbarButtonColor]; + self.unreadIndicatorView.translatesAutoresizingMaskIntoConstraints = NO; + self.unreadIndicatorView.accessibilityIdentifier = + kBadgeUnreadIndicatorAccessibilityIdentifier; + [_displayedBadge addSubview:self.unreadIndicatorView]; + [NSLayoutConstraint activateConstraints:@[ + [self.unreadIndicatorView.trailingAnchor + constraintEqualToAnchor:_displayedBadge.trailingAnchor + constant:-kUnreadIndicatorViewSpacing], + [self.unreadIndicatorView.topAnchor + constraintEqualToAnchor:_displayedBadge.topAnchor + constant:kUnreadIndicatorViewSpacing], + [self.unreadIndicatorView.heightAnchor + constraintEqualToConstant:kUnreadIndicatorViewHeight], + [self.unreadIndicatorView.heightAnchor + constraintEqualToAnchor:self.unreadIndicatorView.widthAnchor] + ]]; + } + if (self.unreadIndicatorView) { + self.unreadIndicatorView.hidden = read; + } +} + #pragma mark FullscreenUIElement - (void)updateForFullscreenProgress:(CGFloat)progress { @@ -149,6 +193,7 @@ [_displayedBadge removeFromSuperview]; if (!badgeButton) { _displayedBadge = nil; + self.unreadIndicatorView = nil; return; } _displayedBadge = badgeButton;
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_home_mediator.mm b/ios/chrome/browser/ui/bookmarks/bookmark_home_mediator.mm index de671eb2..73e74ae 100644 --- a/ios/chrome/browser/ui/bookmarks/bookmark_home_mediator.mm +++ b/ios/chrome/browser/ui/bookmarks/bookmark_home_mediator.mm
@@ -277,12 +277,13 @@ [self.sharedState.tableViewModel addItem:item toSectionWithIdentifier:BookmarkHomeSectionIdentifierPromo]; - [mediator signinPromoViewVisible]; + [mediator signinPromoViewIsVisible]; } else { - if (![mediator isInvalidClosedOrNeverVisible]) { + if (!mediator.invalidClosedOrNeverVisible) { // When the sign-in view is closed, the promo state changes, but - // -[SigninPromoViewMediator signinPromoViewHidden] should not be called. - [mediator signinPromoViewHidden]; + // -[SigninPromoViewMediator signinPromoViewIsHidden] should not be + // called. + [mediator signinPromoViewIsHidden]; } DCHECK([self.sharedState.tableViewModel
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_promo_controller.mm b/ios/chrome/browser/ui/bookmarks/bookmark_promo_controller.mm index 0ebe73b..dca6c695 100644 --- a/ios/chrome/browser/ui/bookmarks/bookmark_promo_controller.mm +++ b/ios/chrome/browser/ui/bookmarks/bookmark_promo_controller.mm
@@ -67,7 +67,7 @@ } - (void)dealloc { - [_signinPromoViewMediator signinPromoViewRemoved]; + [_signinPromoViewMediator signinPromoViewIsRemoved]; } - (void)hidePromoCell { @@ -103,7 +103,7 @@ // Called when a user signs into Google services such as sync. - (void)onPrimaryAccountSet:(const CoreAccountInfo&)primaryAccountInfo { - if (!self.signinPromoViewMediator.isSigninInProgress) + if (!self.signinPromoViewMediator.signinInProgress) self.shouldShowSigninPromo = NO; }
diff --git a/ios/chrome/browser/ui/bookmarks/cells/bookmark_home_promo_item.mm b/ios/chrome/browser/ui/bookmarks/cells/bookmark_home_promo_item.mm index 1c6734e..1f08583 100644 --- a/ios/chrome/browser/ui/bookmarks/cells/bookmark_home_promo_item.mm +++ b/ios/chrome/browser/ui/bookmarks/cells/bookmark_home_promo_item.mm
@@ -45,7 +45,7 @@ [[mediator createConfigurator] configureSigninPromoView:signinPromoCell.signinPromoView]; signinPromoCell.selectionStyle = UITableViewCellSelectionStyleNone; - [mediator signinPromoViewVisible]; + [mediator signinPromoViewIsVisible]; } @end
diff --git a/ios/chrome/browser/ui/browser_view/BUILD.gn b/ios/chrome/browser/ui/browser_view/BUILD.gn index cfa78c6..27f483d 100644 --- a/ios/chrome/browser/ui/browser_view/BUILD.gn +++ b/ios/chrome/browser/ui/browser_view/BUILD.gn
@@ -120,6 +120,7 @@ "//ios/chrome/browser/ui/overscroll_actions", "//ios/chrome/browser/ui/page_info:coordinator", "//ios/chrome/browser/ui/page_info/requirements", + "//ios/chrome/browser/ui/passwords", "//ios/chrome/browser/ui/payments", "//ios/chrome/browser/ui/popup_menu", "//ios/chrome/browser/ui/presenters",
diff --git a/ios/chrome/browser/ui/browser_view/browser_coordinator.mm b/ios/chrome/browser/ui/browser_view/browser_coordinator.mm index 0b69cf98..e3be0d4 100644 --- a/ios/chrome/browser/ui/browser_view/browser_coordinator.mm +++ b/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
@@ -35,6 +35,7 @@ #import "ios/chrome/browser/ui/download/pass_kit_coordinator.h" #import "ios/chrome/browser/ui/open_in/open_in_mediator.h" #import "ios/chrome/browser/ui/page_info/page_info_legacy_coordinator.h" +#import "ios/chrome/browser/ui/passwords/password_breach_coordinator.h" #import "ios/chrome/browser/ui/print/print_controller.h" #import "ios/chrome/browser/ui/qr_scanner/qr_scanner_legacy_coordinator.h" #import "ios/chrome/browser/ui/reading_list/reading_list_coordinator.h" @@ -115,6 +116,10 @@ // Coordinator for the PassKit UI presentation. @property(nonatomic, strong) PassKitCoordinator* passKitCoordinator; +// Coordinator for the password breach UI presentation. +@property(nonatomic, strong) + PasswordBreachCoordinator* passwordBreachCoordinator; + // Used to display the Print UI. Nil if not visible. // TODO(crbug.com/910017): Convert to coordinator. @property(nonatomic, strong) PrintController* printController; @@ -215,6 +220,8 @@ [self.readingListCoordinator stop]; self.readingListCoordinator = nil; + [self.passwordBreachCoordinator stop]; + [self.viewController clearPresentedStateWithCompletion:completion dismissOmnibox:dismissOmnibox]; } @@ -313,6 +320,9 @@ self.passKitCoordinator = [[PassKitCoordinator alloc] initWithBaseViewController:self.viewController]; + self.passwordBreachCoordinator = [[PasswordBreachCoordinator alloc] + initWithBaseViewController:self.viewController]; + self.printController = [[PrintController alloc] initWithContextGetter:self.browserState->GetRequestContext()]; @@ -361,6 +371,9 @@ [self.passKitCoordinator stop]; self.passKitCoordinator = nil; + [self.passwordBreachCoordinator stop]; + self.passwordBreachCoordinator = nil; + self.printController = nil; [self.qrScannerCoordinator stop];
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm index cb501f03..ad3ac22 100644 --- a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm +++ b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
@@ -4757,6 +4757,10 @@ return [self userAgentType] == web::UserAgentType::DESKTOP; } +- (web::WebState*)webStateToReplace { + return self.currentWebState; +} + #pragma mark - NetExportTabHelperDelegate - (void)netExportTabHelper:(NetExportTabHelper*)tabHelper
diff --git a/ios/chrome/browser/ui/commands/application_commands.h b/ios/chrome/browser/ui/commands/application_commands.h index b3fdbb4..bc2edd1 100644 --- a/ios/chrome/browser/ui/commands/application_commands.h +++ b/ios/chrome/browser/ui/commands/application_commands.h
@@ -18,11 +18,15 @@ // may also be forwarded directly to a settings navigation controller. @protocol ApplicationSettingsCommands -// Shows the accounts settings UI, presenting from |baseViewController|. +// TODO(crbug.com/779791) : Do not pass baseViewController through dispatcher. +// Shows the accounts settings UI, presenting from |baseViewController|. If +// |baseViewController| is nil BVC will be used as presenterViewController. - (void)showAccountsSettingsFromViewController: (UIViewController*)baseViewController; +// TODO(crbug.com/779791) : Do not pass baseViewController through dispatcher. // Shows the Google services settings UI, presenting from |baseViewController|. +// If |baseViewController| is nil BVC will be used as presenterViewController. - (void)showGoogleServicesSettingsFromViewController: (UIViewController*)baseViewController;
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view_controller.mm b/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view_controller.mm index 4b78ef5..fe52ac3 100644 --- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view_controller.mm +++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view_controller.mm
@@ -366,7 +366,7 @@ - (void)identityDiscTapped { base::RecordAction(base::UserMetricsAction("MobileNTPIdentityDiscTapped")); - [self.dispatcher showGoogleServicesSettingsFromViewController:self]; + [self.dispatcher showGoogleServicesSettingsFromViewController:nil]; } // TODO(crbug.com/807330) The fakebox is currently a collection of views spread
diff --git a/ios/chrome/browser/ui/passwords/BUILD.gn b/ios/chrome/browser/ui/passwords/BUILD.gn new file mode 100644 index 0000000..b5f4c5f --- /dev/null +++ b/ios/chrome/browser/ui/passwords/BUILD.gn
@@ -0,0 +1,24 @@ +# Copyright 2019 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +source_set("passwords") { + configs += [ "//build/config/compiler:enable_arc" ] + sources = [ + "password_breach_coordinator.h", + "password_breach_coordinator.mm", + "password_breach_view_controller.h", + "password_breach_view_controller.mm", + ] + deps = [ + "resources", + "//base", + "//components/password_manager/core/browser", + "//ios/chrome/app/strings", + "//ios/chrome/browser/ui/coordinators:chrome_coordinators", + "//ios/chrome/common/colors", + "//ios/chrome/common/ui_util", + "//ui/base", + ] + libs = [ "UIKit.framework" ] +}
diff --git a/ios/chrome/browser/ui/passwords/OWNERS b/ios/chrome/browser/ui/passwords/OWNERS new file mode 100644 index 0000000..747ac7d3 --- /dev/null +++ b/ios/chrome/browser/ui/passwords/OWNERS
@@ -0,0 +1,4 @@ +javierrobles@chromium.org + +# TEAM: ios-directory-owners@chromium.org +# OS: iOS \ No newline at end of file
diff --git a/ios/chrome/browser/ui/passwords/password_breach_coordinator.h b/ios/chrome/browser/ui/passwords/password_breach_coordinator.h new file mode 100644 index 0000000..7f95a649 --- /dev/null +++ b/ios/chrome/browser/ui/passwords/password_breach_coordinator.h
@@ -0,0 +1,16 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IOS_CHROME_BROWSER_UI_PASSWORDS_PASSWORD_BREACH_COORDINATOR_H_ +#define IOS_CHROME_BROWSER_UI_PASSWORDS_PASSWORD_BREACH_COORDINATOR_H_ + +#import "ios/chrome/browser/ui/coordinators/chrome_coordinator.h" + +// Presents and stops the Password Breach feature, which consists in alerting +// the user that Chrome detected a leaked credential. In some scenarios it +// prompts for a checkup of the stored passwords. +@interface PasswordBreachCoordinator : ChromeCoordinator +@end + +#endif // IOS_CHROME_BROWSER_UI_PASSWORDS_PASSWORD_BREACH_COORDINATOR_H_
diff --git a/ios/chrome/browser/ui/passwords/password_breach_coordinator.mm b/ios/chrome/browser/ui/passwords/password_breach_coordinator.mm new file mode 100644 index 0000000..d202e16a --- /dev/null +++ b/ios/chrome/browser/ui/passwords/password_breach_coordinator.mm
@@ -0,0 +1,38 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "ios/chrome/browser/ui/passwords/password_breach_coordinator.h" + +#import "ios/chrome/browser/ui/passwords/password_breach_view_controller.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +@interface PasswordBreachCoordinator () + +// The main view controller for this coordinator. +@property(nonatomic, strong) PasswordBreachViewController* viewController; + +@end + +@implementation PasswordBreachCoordinator + +- (void)start { + [super start]; + self.viewController = [[PasswordBreachViewController alloc] init]; + [self.baseViewController presentViewController:self.viewController + animated:YES + completion:nil]; +} + +- (void)stop { + [self.viewController.presentingViewController + dismissViewControllerAnimated:YES + completion:nil]; + self.viewController = nil; + [super stop]; +} + +@end
diff --git a/ios/chrome/browser/ui/passwords/password_breach_view_controller.h b/ios/chrome/browser/ui/passwords/password_breach_view_controller.h new file mode 100644 index 0000000..328b6f5 --- /dev/null +++ b/ios/chrome/browser/ui/passwords/password_breach_view_controller.h
@@ -0,0 +1,13 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IOS_CHROME_BROWSER_UI_PASSWORDS_PASSWORD_BREACH_VIEW_CONTROLLER_H_ +#define IOS_CHROME_BROWSER_UI_PASSWORDS_PASSWORD_BREACH_VIEW_CONTROLLER_H_ + +#import <UIKit/UIKit.h> + +@interface PasswordBreachViewController : UIViewController +@end + +#endif // IOS_CHROME_BROWSER_UI_PASSWORDS_PASSWORD_BREACH_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/passwords/password_breach_view_controller.mm b/ios/chrome/browser/ui/passwords/password_breach_view_controller.mm new file mode 100644 index 0000000..94248f6 --- /dev/null +++ b/ios/chrome/browser/ui/passwords/password_breach_view_controller.mm
@@ -0,0 +1,120 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "ios/chrome/browser/ui/passwords/password_breach_view_controller.h" + +#include "base/strings/sys_string_conversions.h" +#include "components/password_manager/core/browser/leak_detection_dialog_utils.h" +#import "ios/chrome/common/colors/semantic_color_names.h" +#import "ios/chrome/common/ui_util/constraints_ui_util.h" +#include "ios/chrome/grit/ios_strings.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +using base::SysUTF16ToNSString; +using password_manager::GetAcceptButtonLabel; +using password_manager::GetCancelButtonLabel; +using password_manager::GetDescription; +using password_manager::GetTitle; +using password_manager::CredentialLeakType; + +namespace { +constexpr CGFloat kStackViewSpacing = 16.0; +} // namespace + +@implementation PasswordBreachViewController + +#pragma mark - Public + +- (void)viewDidLoad { + [super viewDidLoad]; + self.view.backgroundColor = [UIColor colorNamed:kBackgroundColor]; + + // TODO(crbug.com/1008862): Pass these at init instead of using these empty + // ones. + CredentialLeakType leakType = CredentialLeakType(); + GURL URL = GURL(); + + UIButton* doneButton = [UIButton buttonWithType:UIButtonTypeSystem]; + [doneButton addTarget:self + action:@selector(didTapDoneButton) + forControlEvents:UIControlEventTouchUpInside]; + NSString* doneTitle = SysUTF16ToNSString(GetCancelButtonLabel()); + [doneButton setTitle:doneTitle forState:UIControlStateNormal]; + doneButton.translatesAutoresizingMaskIntoConstraints = NO; + [self.view addSubview:doneButton]; + + UIImage* image = [UIImage imageNamed:@"password_breach_illustration"]; + UIImageView* imageView = [[UIImageView alloc] initWithImage:image]; + imageView.contentMode = UIViewContentModeScaleAspectFit; + imageView.translatesAutoresizingMaskIntoConstraints = NO; + + UILabel* title = [[UILabel alloc] init]; + title.numberOfLines = 0; + title.font = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline]; + title.textColor = [UIColor colorNamed:kTextPrimaryColor]; + title.text = SysUTF16ToNSString(GetTitle(leakType)); + title.translatesAutoresizingMaskIntoConstraints = NO; + + UILabel* subtitle = [[UILabel alloc] init]; + subtitle.numberOfLines = 0; + subtitle.textColor = [UIColor colorNamed:kTextSecondaryColor]; + subtitle.text = SysUTF16ToNSString(GetDescription(leakType, URL)); + subtitle.translatesAutoresizingMaskIntoConstraints = NO; + + UIStackView* stackView = [[UIStackView alloc] + initWithArrangedSubviews:@[ imageView, title, subtitle ]]; + stackView.axis = UILayoutConstraintAxisVertical; + stackView.alignment = UIStackViewAlignmentFill; + stackView.translatesAutoresizingMaskIntoConstraints = NO; + stackView.spacing = kStackViewSpacing; + [self.view addSubview:stackView]; + + UIButton* primaryActionButton = [UIButton buttonWithType:UIButtonTypeSystem]; + [primaryActionButton addTarget:self + action:@selector(didTapPrimaryActionButton) + forControlEvents:UIControlEventTouchUpInside]; + NSString* primaryActionTitle = + SysUTF16ToNSString(GetAcceptButtonLabel(leakType)); + [primaryActionButton setTitle:primaryActionTitle + forState:UIControlStateNormal]; + primaryActionButton.translatesAutoresizingMaskIntoConstraints = NO; + [self.view addSubview:primaryActionButton]; + + UILayoutGuide* margins = self.view.layoutMarginsGuide; + + // Primary Action Button constraints. + AddSameConstraintsToSides(margins, doneButton, + LayoutSides::kTrailing | LayoutSides::kTop); + + // Stack View (and its contents) constraints. + CGFloat imageAspectRatio = image.size.width / image.size.height; + [imageView.widthAnchor constraintEqualToAnchor:imageView.heightAnchor + multiplier:imageAspectRatio] + .active = YES; + AddSameCenterConstraints(margins, stackView); + AddSameConstraintsToSides(margins, stackView, + LayoutSides::kLeading | LayoutSides::kTrailing); + + // Primary Action Button constraints. + AddSameConstraintsToSides( + margins, primaryActionButton, + LayoutSides::kLeading | LayoutSides::kTrailing | LayoutSides::kBottom); +} + +#pragma mark - Private + +// Handle taps on the done button. +- (void)didTapDoneButton { + // TODO(crbug.com/1008862): Hook up with a mediator. +} + +// Handle taps on the primary action button. +- (void)didTapPrimaryActionButton { + // TODO(crbug.com/1008862): Hook up with a mediator. +} + +@end
diff --git a/ios/chrome/browser/ui/passwords/resources/BUILD.gn b/ios/chrome/browser/ui/passwords/resources/BUILD.gn new file mode 100644 index 0000000..1cf482d9c --- /dev/null +++ b/ios/chrome/browser/ui/passwords/resources/BUILD.gn
@@ -0,0 +1,20 @@ +# Copyright 2019 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/ios/asset_catalog.gni") +import("//build/config/ios/rules.gni") + +group("resources") { + deps = [ + ":password_breach_illustration", + ] +} + +imageset("password_breach_illustration") { + sources = [ + "password_breach_illustration.imageset/Contents.json", + "password_breach_illustration.imageset/illustration_dark.png", + "password_breach_illustration.imageset/illustration_light.png", + ] +}
diff --git a/ios/chrome/browser/ui/passwords/resources/password_breach_illustration.imageset/Contents.json b/ios/chrome/browser/ui/passwords/resources/password_breach_illustration.imageset/Contents.json new file mode 100644 index 0000000..e5b1b4d6 --- /dev/null +++ b/ios/chrome/browser/ui/passwords/resources/password_breach_illustration.imageset/Contents.json
@@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "illustration_light.png" + }, + { + "idiom" : "universal", + "filename" : "illustration_dark.png", + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ] + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file
diff --git a/ios/chrome/browser/ui/passwords/resources/password_breach_illustration.imageset/illustration_dark.png b/ios/chrome/browser/ui/passwords/resources/password_breach_illustration.imageset/illustration_dark.png new file mode 100644 index 0000000..5eb2ae9 --- /dev/null +++ b/ios/chrome/browser/ui/passwords/resources/password_breach_illustration.imageset/illustration_dark.png Binary files differ
diff --git a/ios/chrome/browser/ui/passwords/resources/password_breach_illustration.imageset/illustration_light.png b/ios/chrome/browser/ui/passwords/resources/password_breach_illustration.imageset/illustration_light.png new file mode 100644 index 0000000..75f9b19 --- /dev/null +++ b/ios/chrome/browser/ui/passwords/resources/password_breach_illustration.imageset/illustration_light.png Binary files differ
diff --git a/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm b/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm index d319c29..4bea287 100644 --- a/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm +++ b/ios/chrome/browser/ui/recent_tabs/recent_tabs_table_view_controller.mm
@@ -141,7 +141,7 @@ } - (void)dealloc { - [_signinPromoViewMediator signinPromoViewRemoved]; + [_signinPromoViewMediator signinPromoViewIsRemoved]; } - (void)viewDidLoad { @@ -541,7 +541,7 @@ if ((newSessionState == self.sessionState && self.sessionState != SessionsSyncUserState::USER_SIGNED_IN_SYNC_ON_WITH_SESSIONS) || - self.signinPromoViewMediator.isSigninInProgress) { + self.signinPromoViewMediator.signinInProgress) { // No need to refresh the sections since all states other than // USER_SIGNED_IN_SYNC_ON_WITH_SESSIONS only have static content. This means // that if the previous State is the same as the new one the static content @@ -576,7 +576,7 @@ // table updates rely on knowing the previous state. self.sessionState = newSessionState; if (self.sessionState != SessionsSyncUserState::USER_SIGNED_OUT) { - [self.signinPromoViewMediator signinPromoViewRemoved]; + [self.signinPromoViewMediator signinPromoViewIsRemoved]; self.signinPromoViewMediator.consumer = nil; self.signinPromoViewMediator = nil; } @@ -665,7 +665,7 @@ [self.tableViewModel itemTypeForIndexPath:indexPath]; // If SigninPromo will be shown, |self.signinPromoViewMediator| must know. if (itemTypeSelected == ItemTypeOtherDevicesSigninPromo) { - [self.signinPromoViewMediator signinPromoViewVisible]; + [self.signinPromoViewMediator signinPromoViewIsVisible]; } // Retrieve favicons for closed tabs and remote sessions. if (itemTypeSelected == ItemTypeRecentlyClosed ||
diff --git a/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_edit_table_view_controller.mm b/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_edit_table_view_controller.mm index 5d96196..5c2b63c4 100644 --- a/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_edit_table_view_controller.mm +++ b/ios/chrome/browser/ui/settings/autofill/autofill_credit_card_edit_table_view_controller.mm
@@ -303,9 +303,10 @@ } } -#pragma mark - SettingsRootTableViewController +#pragma mark - UIAdaptivePresentationControllerDelegate -- (BOOL)shouldDismissViewControllerBySwipeDown { +- (BOOL)presentationControllerShouldDismiss: + (UIPresentationController*)presentationController { return !self.tableView.editing; }
diff --git a/ios/chrome/browser/ui/settings/autofill/autofill_edit_table_view_controller.mm b/ios/chrome/browser/ui/settings/autofill/autofill_edit_table_view_controller.mm index 70a668a..1a5d379 100644 --- a/ios/chrome/browser/ui/settings/autofill/autofill_edit_table_view_controller.mm +++ b/ios/chrome/browser/ui/settings/autofill/autofill_edit_table_view_controller.mm
@@ -73,7 +73,10 @@ return YES; } -- (BOOL)shouldDismissViewControllerBySwipeDown { +#pragma mark - UIAdaptivePresentationControllerDelegate + +- (BOOL)presentationControllerShouldDismiss: + (UIPresentationController*)presentationController { return !self.tableView.editing; }
diff --git a/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_table_view_controller.mm b/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_table_view_controller.mm index b04fccb..0c50cc60 100644 --- a/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_table_view_controller.mm +++ b/ios/chrome/browser/ui/settings/clear_browsing_data/clear_browsing_data_table_view_controller.mm
@@ -334,12 +334,6 @@ } } -#pragma mark - SettingsRootTableViewController - -- (BOOL)shouldDismissViewControllerBySwipeDown { - return !self.chromeActivityOverlayCoordinator.started; -} - #pragma mark - TableViewTextLinkCellDelegate - (void)tableViewTextLinkCell:(TableViewTextLinkCell*)cell @@ -436,8 +430,8 @@ - (void)presentationControllerDidDismiss: (UIPresentationController*)presentationController { - // Call dismiss to clean up state and stop the Coordinator. - [self dismiss]; + // Call prepareForDismissal to clean up state and stop the Coordinator. + [self prepareForDismissal]; } - (BOOL)presentationControllerShouldDismiss:
diff --git a/ios/chrome/browser/ui/settings/settings_navigation_controller.mm b/ios/chrome/browser/ui/settings/settings_navigation_controller.mm index 637e7f6d..2f546650 100644 --- a/ios/chrome/browser/ui/settings/settings_navigation_controller.mm +++ b/ios/chrome/browser/ui/settings/settings_navigation_controller.mm
@@ -47,11 +47,13 @@ @property(nonatomic, strong) GoogleServicesSettingsCoordinator* googleServicesSettingsCoordinator; -// Current SettingsViewController being presented by this Navigation Controller. +// Current UIViewController being presented by this Navigation Controller. // If nil it means the Navigation Controller is not presenting anything, or the -// VC being presented is not a SettingsRootTableViewController. +// VC being presented doesn't conform to +// UIAdaptivePresentationControllerDelegate. @property(nonatomic, weak) - SettingsRootTableViewController* currentPresentedSettingsViewController; + UIViewController<UIAdaptivePresentationControllerDelegate>* + currentPresentedViewController; // The SettingsNavigationControllerDelegate for this NavigationController. @property(nonatomic, weak) id<SettingsNavigationControllerDelegate> @@ -376,16 +378,53 @@ - (BOOL)presentationControllerShouldDismiss: (UIPresentationController*)presentationController { - return [self.currentPresentedSettingsViewController - shouldDismissViewControllerBySwipeDown]; + if (@available(iOS 13, *)) { + if ([self.currentPresentedViewController + respondsToSelector:@selector + (presentationControllerShouldDismiss:)]) { + return [self.currentPresentedViewController + presentationControllerShouldDismiss:presentationController]; + } + } + return NO; } - (void)presentationControllerDidDismiss: (UIPresentationController*)presentationController { + if (@available(iOS 13, *)) { + if ([self.currentPresentedViewController + respondsToSelector:@selector(presentationControllerDidDismiss:)]) { + [self.currentPresentedViewController + presentationControllerDidDismiss:presentationController]; + } + } // Call settingsWasDismissed to make sure any necessary cleanup is performed. [self.settingsNavigationDelegate settingsWasDismissed]; } +- (void)presentationControllerDidAttemptToDismiss: + (UIPresentationController*)presentationController { + if (@available(iOS 13, *)) { + if ([self.currentPresentedViewController + respondsToSelector:@selector + (presentationControllerDidAttemptToDismiss:)]) { + [self.currentPresentedViewController + presentationControllerDidAttemptToDismiss:presentationController]; + } + } +} + +- (void)presentationControllerWillDismiss: + (UIPresentationController*)presentationController { + if (@available(iOS 13, *)) { + if ([self.currentPresentedViewController + respondsToSelector:@selector(presentationControllerWillDismiss:)]) { + [self.currentPresentedViewController + presentationControllerWillDismiss:presentationController]; + } + } +} + #pragma mark - Accessibility - (BOOL)accessibilityPerformEscape { @@ -407,8 +446,9 @@ - (void)navigationController:(UINavigationController*)navigationController willShowViewController:(UIViewController*)viewController animated:(BOOL)animated { - self.currentPresentedSettingsViewController = - base::mac::ObjCCast<SettingsRootTableViewController>(viewController); + self.currentPresentedViewController = base::mac::ObjCCast< + UIViewController<UIAdaptivePresentationControllerDelegate>>( + viewController); } #pragma mark - UIResponder
diff --git a/ios/chrome/browser/ui/settings/settings_root_table_view_controller.h b/ios/chrome/browser/ui/settings/settings_root_table_view_controller.h index e70a6e2..86ff7a0 100644 --- a/ios/chrome/browser/ui/settings/settings_root_table_view_controller.h +++ b/ios/chrome/browser/ui/settings/settings_root_table_view_controller.h
@@ -19,7 +19,8 @@ // AppBar. @interface SettingsRootTableViewController : ChromeTableViewController <SettingsRootViewControlling, - TableViewLinkHeaderFooterItemDelegate> + TableViewLinkHeaderFooterItemDelegate, + UIAdaptivePresentationControllerDelegate> // Delete button for the toolbar. @property(nonatomic, strong, readonly) UIBarButtonItem* deleteButton; @@ -81,11 +82,6 @@ // * Removes the transparent veil. - (void)allowUserInteraction; -// Returns YES. Subclasses can override to prevent a swipe down dismissal. This -// is useful when the ViewController contains editable fields and accidental -// dismissals want to be avoided. -- (BOOL)shouldDismissViewControllerBySwipeDown; - @end #endif // IOS_CHROME_BROWSER_UI_SETTINGS_SETTINGS_ROOT_TABLE_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/settings/settings_root_table_view_controller.mm b/ios/chrome/browser/ui/settings/settings_root_table_view_controller.mm index 5a98c85..1fbc3c0f 100644 --- a/ios/chrome/browser/ui/settings/settings_root_table_view_controller.mm +++ b/ios/chrome/browser/ui/settings/settings_root_table_view_controller.mm
@@ -353,7 +353,10 @@ self.savedBarButtonItemPosition = kUndefinedBarButtonItemPosition; } -- (BOOL)shouldDismissViewControllerBySwipeDown { +#pragma mark - UIAdaptivePresentationControllerDelegate + +- (BOOL)presentationControllerShouldDismiss: + (UIPresentationController*)presentationController { return YES; }
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 2c5a9ed..b7267ac 100644 --- a/ios/chrome/browser/ui/settings/settings_table_view_controller.mm +++ b/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
@@ -368,7 +368,7 @@ _signinPromoViewMediator.consumer = self; } } else { - [_signinPromoViewMediator signinPromoViewRemoved]; + [_signinPromoViewMediator signinPromoViewIsRemoved]; _signinPromoViewMediator = nil; } [model addItem:[self signInTextItem] @@ -377,7 +377,7 @@ // Account section [model addSectionWithIdentifier:SectionIdentifierAccount]; _hasRecordedSigninImpression = NO; - [_signinPromoViewMediator signinPromoViewRemoved]; + [_signinPromoViewMediator signinPromoViewIsRemoved]; _signinPromoViewMediator = nil; [model addItem:[self accountCellItem] toSectionWithIdentifier:SectionIdentifierAccount]; @@ -456,7 +456,7 @@ signinPromoItem.configurator = [_signinPromoViewMediator createConfigurator]; signinPromoItem.delegate = _signinPromoViewMediator; - [_signinPromoViewMediator signinPromoViewVisible]; + [_signinPromoViewMediator signinPromoViewIsVisible]; return signinPromoItem; } if (!_hasRecordedSigninImpression) { @@ -1096,7 +1096,7 @@ _googleServicesSettingsCoordinator = nil; _settingsHasBeenDismissed = YES; [self.signinInteractionCoordinator cancel]; - [_signinPromoViewMediator signinPromoViewRemoved]; + [_signinPromoViewMediator signinPromoViewIsRemoved]; _signinPromoViewMediator = nil; [self stopBrowserStateServiceObservers]; }
diff --git a/ios/chrome/browser/ui/settings/sync/sync_encryption_passphrase_table_view_controller.mm b/ios/chrome/browser/ui/settings/sync/sync_encryption_passphrase_table_view_controller.mm index a49bf946..8a9a5ab 100644 --- a/ios/chrome/browser/ui/settings/sync/sync_encryption_passphrase_table_view_controller.mm +++ b/ios/chrome/browser/ui/settings/sync/sync_encryption_passphrase_table_view_controller.mm
@@ -189,7 +189,10 @@ forSectionWithIdentifier:SectionIdentifierPassphrase]; } -- (BOOL)shouldDismissViewControllerBySwipeDown { +#pragma mark - UIAdaptivePresentationControllerDelegate + +- (BOOL)presentationControllerShouldDismiss: + (UIPresentationController*)presentationController { return ![passphrase_.text length]; }
diff --git a/ios/chrome/browser/web/font_size_js_unittest.mm b/ios/chrome/browser/web/font_size_js_unittest.mm index b9426d71..f1d29937e 100644 --- a/ios/chrome/browser/web/font_size_js_unittest.mm +++ b/ios/chrome/browser/web/font_size_js_unittest.mm
@@ -59,16 +59,8 @@ // Tests that __gCrWeb.accessibility.adjustFontSize works for any scale. TEST_F(FontSizeJsTest, TestAdjustFontSizeForScale) { - // TODO(crbug.com/983776): This test fails when compiled with Xcode 10 and - // running on iOS 13 because expected font size and actual font size don't - // match. Re-enable this test when Xcode 11 is used for compiling. -#if !defined(__IPHONE_13_0) || (__IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_13_0) - if (base::ios::IsRunningOnIOS13OrLater()) { - return; - } -#endif - // TODO(crbug.com/983776): This test also appears to be generally broken on - // iPads with beta 5. It appears to be a simulator bug. Re-enable on beta 6. + // TODO(crbug.com/983776): This test fails on ipad since beta5 due to a + // simulator bug. Re-enable this once the bug is fixed. if (base::ios::IsRunningOnIOS13OrLater() && ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_TABLET) { return; @@ -188,16 +180,8 @@ // Tests that __gCrWeb.accessibility.adjustFontSize works for any CSS unit. TEST_F(FontSizeJsTest, TestAdjustFontSizeForUnit) { - // TODO(crbug.com/983776): This test fails when compiled with Xcode 10 and - // running on iOS 13 because expected font size and actual font size don't - // match. Re-enable this test when Xcode 11 is used for compiling. -#if !defined(__IPHONE_13_0) || (__IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_13_0) - if (base::ios::IsRunningOnIOS13OrLater()) { - return; - } -#endif - // TODO(crbug.com/983776): This test also appears to be generally broken on - // iPads with beta 5. It appears to be a simulator bug. Re-enable on beta 6. + // TODO(crbug.com/983776): This test fails on ipad since beta5 due to a + // simulator bug. Re-enable this once the bug is fixed. if (base::ios::IsRunningOnIOS13OrLater() && ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_TABLET) { return; @@ -269,16 +253,8 @@ // Tests that __gCrWeb.accessibility.adjustFontSize works for nested elements. TEST_F(FontSizeJsTest, TestAdjustFontSizeForNestedElements) { - // TODO(crbug.com/983776): This test fails when compiled with Xcode 10 and - // running on iOS 13 because expected font size and actual font size don't - // match. Re-enable this test when Xcode 11 is used for compiling. -#if !defined(__IPHONE_13_0) || (__IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_13_0) - if (base::ios::IsRunningOnIOS13OrLater()) { - return; - } -#endif - // TODO(crbug.com/983776): This test also appears to be generally broken on - // iPads with beta 5. It appears to be a simulator bug. Re-enable on beta 6. + // TODO(crbug.com/983776): This test fails on ipad since beta5 due to a + // simulator bug. Re-enable this once the bug is fixed. if (base::ios::IsRunningOnIOS13OrLater() && ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_TABLET) { return;
diff --git a/ios/showcase/badges/sc_badge_coordinator.mm b/ios/showcase/badges/sc_badge_coordinator.mm index ba71dee7d..6441788 100644 --- a/ios/showcase/badges/sc_badge_coordinator.mm +++ b/ios/showcase/badges/sc_badge_coordinator.mm
@@ -81,6 +81,7 @@ initWithBadgeType:BadgeType::kBadgeTypeOverflow]; [self.consumer setupWithDisplayedBadge:displayedBadge fullScreenBadge:incognitoItem]; + [self.consumer markDisplayedBadgeAsRead:NO]; } @end @@ -121,6 +122,7 @@ initWithInfobarType:InfobarType::kInfobarTypePasswordSave] ]; [self.badgePopupMenuCoordinator setBadgeItemsToShow:badgeItems]; [self.badgePopupMenuCoordinator start]; + [self.consumer markDisplayedBadgeAsRead:YES]; } @end
diff --git a/ios/showcase/badges/sc_badge_egtest.mm b/ios/showcase/badges/sc_badge_egtest.mm index f008253..ff5d7413 100644 --- a/ios/showcase/badges/sc_badge_egtest.mm +++ b/ios/showcase/badges/sc_badge_egtest.mm
@@ -59,12 +59,17 @@ kSCDisplayedBadgeToggleButton)] performAction:grey_tap()]; - // Assert that overflow badge is shown and tap on it. + // Assert that overflow badge and the unread indicator is shown and tap on it. [[EarlGrey selectElementWithMatcher: grey_allOf(grey_accessibilityID( kBadgeButtonOverflowAccessibilityIdentifier), grey_sufficientlyVisible(), nil)] assertWithMatcher:grey_sufficientlyVisible()]; + [[EarlGrey selectElementWithMatcher: + grey_allOf(grey_accessibilityID( + kBadgeUnreadIndicatorAccessibilityIdentifier), + grey_sufficientlyVisible(), nil)] + assertWithMatcher:grey_sufficientlyVisible()]; [[EarlGrey selectElementWithMatcher:grey_accessibilityID( kBadgeButtonOverflowAccessibilityIdentifier)] @@ -77,6 +82,18 @@ kBadgePopupMenuTableViewAccessibilityIdentifier), grey_sufficientlyVisible(), nil)] assertWithMatcher:grey_sufficientlyVisible()]; + + // Dismiss popup menu by tapping outside of the menu. Tapping the displayed + // badge is sufficient here. Assert that the unread indicator is not there + // anymore. + [[EarlGrey selectElementWithMatcher:grey_accessibilityID( + kSCDisplayedBadgeToggleButton)] + performAction:grey_tap()]; + [[EarlGrey selectElementWithMatcher: + grey_allOf(grey_accessibilityID( + kBadgeUnreadIndicatorAccessibilityIdentifier), + grey_sufficientlyVisible(), nil)] + assertWithMatcher:grey_notVisible()]; } @end
diff --git a/ios/web_view/internal/sync/cwv_sync_controller.mm b/ios/web_view/internal/sync/cwv_sync_controller.mm index 33f7586..f1112d4 100644 --- a/ios/web_view/internal/sync/cwv_sync_controller.mm +++ b/ios/web_view/internal/sync/cwv_sync_controller.mm
@@ -178,7 +178,8 @@ #pragma mark - Public Methods - (BOOL)isPassphraseNeeded { - return _syncService->GetUserSettings()->IsPassphraseRequiredForDecryption(); + return _syncService->GetUserSettings() + ->IsPassphraseRequiredForPreferredDataTypes(); } - (BOOL)isConsentNeeded {
diff --git a/ios/web_view/internal/sync/cwv_sync_controller_unittest.mm b/ios/web_view/internal/sync/cwv_sync_controller_unittest.mm index e3bcad2e..b681d2b 100644 --- a/ios/web_view/internal/sync/cwv_sync_controller_unittest.mm +++ b/ios/web_view/internal/sync/cwv_sync_controller_unittest.mm
@@ -233,7 +233,7 @@ // Verifies CWVSyncController's passphrase API. TEST_F(CWVSyncControllerTest, Passphrase) { EXPECT_CALL(*mock_sync_service()->GetMockUserSettings(), - IsPassphraseRequiredForDecryption()) + IsPassphraseRequiredForPreferredDataTypes()) .WillOnce(Return(true)); EXPECT_TRUE(sync_controller_.passphraseNeeded); EXPECT_CALL(*mock_sync_service()->GetMockUserSettings(),
diff --git a/ipc/ipc_message_start.h b/ipc/ipc_message_start.h index 93bc399..9fa28c6 100644 --- a/ipc/ipc_message_start.h +++ b/ipc/ipc_message_start.h
@@ -24,10 +24,7 @@ GpuChannelMsgStart, MediaMsgStart, PpapiMsgStart, - DOMStorageMsgStart, ResourceMsgStart, - BlobMsgStart, - MidiMsgStart, ChromeMsgStart, DragMsgStart, PrintMsgStart, @@ -40,10 +37,8 @@ BrowserPluginMsgStart, AndroidWebViewMsgStart, MediaPlayerMsgStart, - TracingMsgStart, PeerConnectionTrackerMsgStart, WebRtcLoggingMsgStart, - TtsMsgStart, NaClHostMsgStart, EncryptedMediaMsgStart, CastMsgStart,
diff --git a/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc b/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc index 496c819..64e33c7 100644 --- a/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc +++ b/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.cc
@@ -1375,9 +1375,12 @@ DCHECK(output_wait_map_.empty()); output_buffer_map_.resize(buffers.size()); - if (image_processor_device_ && !CreateImageProcessor()) { - NOTIFY_ERROR(PLATFORM_FAILURE); - return; + // In import mode we will create the IP when importing the first buffer. + if (image_processor_device_ && output_mode_ == Config::OutputMode::ALLOCATE) { + if (!CreateImageProcessor()) { + NOTIFY_ERROR(PLATFORM_FAILURE); + return; + } } // Reserve all buffers until ImportBufferForPictureTask() is called @@ -1426,8 +1429,11 @@ } } - ImportBufferForPictureTask(output_record.picture_id, - std::move(passed_dmabuf_fds)); + int plane_horiz_bits_per_pixel = VideoFrame::PlaneHorizontalBitsPerPixel( + V4L2Device::V4L2PixFmtToVideoPixelFormat(gl_image_format_fourcc_), 0); + ImportBufferForPictureTask( + output_record.picture_id, std::move(passed_dmabuf_fds), + gl_image_size_.width() * plane_horiz_bits_per_pixel / 8); } // else we'll get triggered via ImportBufferForPicture() from client. DVLOGF(3) << "buffer[" << i << "]: picture_id=" << output_record.picture_id; } @@ -1544,12 +1550,14 @@ dmabuf_fds.pop_back(); } - ImportBufferForPictureTask(picture_buffer_id, std::move(dmabuf_fds)); + ImportBufferForPictureTask(picture_buffer_id, std::move(dmabuf_fds), + handle.planes[0].stride); } void V4L2SliceVideoDecodeAccelerator::ImportBufferForPictureTask( int32_t picture_buffer_id, - std::vector<base::ScopedFD> passed_dmabuf_fds) { + std::vector<base::ScopedFD> passed_dmabuf_fds, + int32_t stride) { DVLOGF(3) << "picture_buffer_id=" << picture_buffer_id; DCHECK(decoder_thread_task_runner_->BelongsToCurrentThread()); @@ -1577,6 +1585,34 @@ return; } + // TODO(crbug.com/982172): This must be done in AssignPictureBuffers(). + // However the size of PictureBuffer might not be adjusted by ARC++. So we + // keep this until ARC++ side is fixed. + int plane_horiz_bits_per_pixel = VideoFrame::PlaneHorizontalBitsPerPixel( + V4L2Device::V4L2PixFmtToVideoPixelFormat(gl_image_format_fourcc_), 0); + if (plane_horiz_bits_per_pixel == 0 || + (stride * 8) % plane_horiz_bits_per_pixel != 0) { + VLOGF(1) << "Invalid format " << gl_image_format_fourcc_ << " or stride " + << stride; + NOTIFY_ERROR(INVALID_ARGUMENT); + return; + } + int adjusted_coded_width = stride * 8 / plane_horiz_bits_per_pixel; + if (image_processor_device_ && !image_processor_) { + DCHECK_EQ(kAwaitingPictureBuffers, state_); + // This is the first buffer import. Create the image processor and change + // the decoder state. The client may adjust the coded width. We don't have + // the final coded size in AssignPictureBuffers yet. Use the adjusted coded + // width to create the image processor. + DVLOGF(3) << "Original gl_image_size=" << gl_image_size_.ToString() + << ", adjusted coded width=" << adjusted_coded_width; + DCHECK_GE(adjusted_coded_width, gl_image_size_.width()); + gl_image_size_.set_width(adjusted_coded_width); + if (!CreateImageProcessor()) + return; + } + DCHECK_EQ(gl_image_size_.width(), adjusted_coded_width); + // If in import mode, build output_frame from the passed DMABUF FDs. if (output_mode_ == Config::OutputMode::IMPORT) { DCHECK_EQ(gl_image_planes_count_, passed_dmabuf_fds.size());
diff --git a/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.h b/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.h index 73e0fab..685837b 100644 --- a/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.h +++ b/media/gpu/v4l2/v4l2_slice_video_decode_accelerator.h
@@ -265,10 +265,11 @@ // Use buffer backed by dmabuf file descriptors in |passed_dmabuf_fds| for the // OutputRecord associated with |picture_buffer_id|, taking ownership of the - // file descriptors. - void ImportBufferForPictureTask( - int32_t picture_buffer_id, - std::vector<base::ScopedFD> passed_dmabuf_fds); + // file descriptors. |stride| is the number of bytes from one row of pixels + // to the next row. + void ImportBufferForPictureTask(int32_t picture_buffer_id, + std::vector<base::ScopedFD> passed_dmabuf_fds, + int32_t stride); // Check that |planes| and |dmabuf_fds| are valid in import mode and call // ImportBufferForPictureTask.
diff --git a/net/socket/ssl_client_socket_impl.cc b/net/socket/ssl_client_socket_impl.cc index 179e4a3..99ff309 100644 --- a/net/socket/ssl_client_socket_impl.cc +++ b/net/socket/ssl_client_socket_impl.cc
@@ -1009,6 +1009,9 @@ } int SSLClientSocketImpl::DoHandshakeComplete(int result) { + if (in_confirm_handshake_) + MaybeRecordEarlyDataResult(); + if (result < 0) return result; @@ -1411,6 +1414,8 @@ DCHECK_NE(kSSLClientSocketNoPendingResult, signature_result_); pending_read_error_ = ERR_IO_PENDING; } else { + if (pending_read_ssl_error_ == SSL_ERROR_EARLY_DATA_REJECTED) + MaybeRecordEarlyDataResult(); pending_read_error_ = MapLastOpenSSLError( pending_read_ssl_error_, err_tracer, &pending_read_error_info_); } @@ -1428,6 +1433,8 @@ // next call of DoPayloadRead. rv = total_bytes_read; + MaybeRecordEarlyDataResult(); + // Do not treat insufficient data as an error to return in the next call to // DoPayloadRead() - instead, let the call fall through to check SSL_read() // again. The transport may have data available by then. @@ -1834,6 +1841,22 @@ negotiated_protocol_, kProtoLast + 1); } +void SSLClientSocketImpl::MaybeRecordEarlyDataResult() { + DCHECK(ssl_); + if (!ssl_config_.early_data_enabled || recorded_early_data_result_) + return; + + recorded_early_data_result_ = true; + // Since the two-parameter version of the macro (which asks for a max + // value) requires that the max value sentinel be named |kMaxValue|, + // transform the max-value sentinel into a one-past-the-end ("boundary") + // sentinel by adding 1, in order to be able to use the three-parameter + // macro. + UMA_HISTOGRAM_ENUMERATION("Net.SSLHandshakeEarlyDataReason", + SSL_get_early_data_reason(ssl_.get()), + ssl_early_data_reason_max_value + 1); +} + int SSLClientSocketImpl::MapLastOpenSSLError( int ssl_error, const crypto::OpenSSLErrStackTracer& tracer,
diff --git a/net/socket/ssl_client_socket_impl.h b/net/socket/ssl_client_socket_impl.h index 85f0ec7..789b420 100644 --- a/net/socket/ssl_client_socket_impl.h +++ b/net/socket/ssl_client_socket_impl.h
@@ -200,6 +200,11 @@ // in a UMA histogram. void RecordNegotiatedProtocol() const; + // Records the result of a handshake where early data was requested + // in the corresponding UMA histogram. This will happen at most once + // during the lifetime of the socket. + void MaybeRecordEarlyDataResult(); + // Returns the net error corresponding to the most recent OpenSSL // error. ssl_error is the output of SSL_get_error. int MapLastOpenSSLError(int ssl_error, @@ -219,6 +224,10 @@ int user_write_buf_len_; bool first_post_handshake_write_ = true; + // True if we've already recorded the result of our attempt to + // use early data. + bool recorded_early_data_result_ = false; + // Used by DoPayloadRead() when attempting to fill the caller's buffer with // as much data as possible without blocking. // If DoPayloadRead() encounters an error after having read some data, stores
diff --git a/net/socket/ssl_client_socket_unittest.cc b/net/socket/ssl_client_socket_unittest.cc index c9fefcb5..12f340b 100644 --- a/net/socket/ssl_client_socket_unittest.cc +++ b/net/socket/ssl_client_socket_unittest.cc
@@ -87,6 +87,7 @@ #include "third_party/boringssl/src/include/openssl/bio.h" #include "third_party/boringssl/src/include/openssl/evp.h" #include "third_party/boringssl/src/include/openssl/pem.h" +#include "third_party/boringssl/src/include/openssl/ssl.h" #include "url/gurl.h" #include "url/origin.h" @@ -830,6 +831,11 @@ return embedded_test_server_.get(); } + void SetServerConfig(SSLServerConfig server_config) { + embedded_test_server()->ResetSSLConfig(net::EmbeddedTestServer::CERT_OK, + server_config); + } + // The SpawnedTestServer object, after calling StartTestServer(). const SpawnedTestServer* spawned_test_server() const { return spawned_test_server_.get(); @@ -1355,11 +1361,6 @@ server->RegisterRequestHandler(base::BindRepeating(&HandleZeroRTTRequest)); } - void SetServerConfig(SSLServerConfig server_config) { - embedded_test_server()->ResetSSLConfig(net::EmbeddedTestServer::CERT_OK, - server_config); - } - FakeBlockingStreamSocket* MakeClient(bool early_data_enabled) { SSLConfig ssl_config; ssl_config.early_data_enabled = early_data_enabled; @@ -5787,6 +5788,142 @@ } } +TEST_F(SSLClientSocketTest, EarlyDataReason) { + const char kReasonHistogram[] = "Net.SSLHandshakeEarlyDataReason"; + + // Enable all test features in the server. + SSLServerConfig server_config; + server_config.version_max = SSL_PROTOCOL_VERSION_TLS1_3; + server_config.early_data_enabled = true; + ASSERT_TRUE( + StartEmbeddedTestServer(EmbeddedTestServer::CERT_OK, server_config)); + + SSLContextConfig client_context_config; + client_context_config.version_max = SSL_PROTOCOL_VERSION_TLS1_3; + ssl_config_service_->UpdateSSLConfigAndNotify(client_context_config); + + SSLConfig client_config; + client_config.early_data_enabled = true; + + // Make the initial connection. + { + base::HistogramTester histograms; + int rv; + ASSERT_TRUE(CreateAndConnectSSLClientSocket(client_config, &rv)); + EXPECT_THAT(rv, IsOk()); + + // Sanity check + SSLInfo info; + ASSERT_TRUE(sock_->GetSSLInfo(&info)); + EXPECT_EQ(SSLInfo::HANDSHAKE_FULL, info.handshake_type); + + // TLS 1.2 with False Start and TLS 1.3 cause the ticket to arrive later, so + // use the socket to ensure the session ticket has been picked up. + EXPECT_THAT(MakeHTTPRequest(sock_.get()), IsOk()); + + histograms.ExpectUniqueSample(kReasonHistogram, + ssl_early_data_no_session_offered, 1); + } + + // Make a resumption connection. + { + int rv; + ASSERT_TRUE(CreateAndConnectSSLClientSocket(client_config, &rv)); + EXPECT_THAT(rv, IsOk()); + + // Sanity check + SSLInfo info; + ASSERT_TRUE(sock_->GetSSLInfo(&info)); + EXPECT_EQ(SSLInfo::HANDSHAKE_RESUME, info.handshake_type); + + base::HistogramTester histograms; + TestCompletionCallback callback; + EXPECT_THAT( + callback.GetResult(sock_->ConfirmHandshake(callback.callback())), + IsOk()); + histograms.ExpectUniqueSample(kReasonHistogram, ssl_early_data_accepted, 1); + } + + // Reset the server's state: this will mean the server declines to resume + // the session and, in particular, 0-RTT. + SetServerConfig(server_config); + + { + int rv; + ASSERT_TRUE(CreateAndConnectSSLClientSocket(client_config, &rv)); + EXPECT_THAT(rv, IsOk()); + + // Sanity check + SSLInfo info; + ASSERT_TRUE(sock_->GetSSLInfo(&info)); + EXPECT_EQ(SSLInfo::HANDSHAKE_RESUME, info.handshake_type); + + base::HistogramTester histograms; + TestCompletionCallback callback; + EXPECT_EQ(callback.GetResult(sock_->ConfirmHandshake(callback.callback())), + ERR_EARLY_DATA_REJECTED); + histograms.ExpectUniqueSample(kReasonHistogram, + ssl_early_data_session_not_resumed, 1); + } +} + +// Test that we correctly log 0-RTT handshake results when +// the handshake concludes while we're reading the ServerHello. +TEST_F(SSLClientSocketTest, EarlyDataReasonReadServerHello) { + const char kReasonHistogram[] = "Net.SSLHandshakeEarlyDataReason"; + + // Enable all test features in the server. + SSLServerConfig server_config; + server_config.version_max = SSL_PROTOCOL_VERSION_TLS1_3; + server_config.early_data_enabled = true; + ASSERT_TRUE( + StartEmbeddedTestServer(EmbeddedTestServer::CERT_OK, server_config)); + + SSLContextConfig client_context_config; + client_context_config.version_max = SSL_PROTOCOL_VERSION_TLS1_3; + ssl_config_service_->UpdateSSLConfigAndNotify(client_context_config); + + SSLConfig client_config; + client_config.early_data_enabled = true; + + // Make the initial connection. + { + base::HistogramTester histograms; + int rv; + ASSERT_TRUE(CreateAndConnectSSLClientSocket(client_config, &rv)); + EXPECT_THAT(rv, IsOk()); + + // Sanity check + SSLInfo info; + ASSERT_TRUE(sock_->GetSSLInfo(&info)); + EXPECT_EQ(SSLInfo::HANDSHAKE_FULL, info.handshake_type); + + // TLS 1.2 with False Start and TLS 1.3 cause the ticket to arrive later, so + // use the socket to ensure the session ticket has been picked up. + EXPECT_THAT(MakeHTTPRequest(sock_.get()), IsOk()); + } + + // Make a resumption connection with early data. + { + int rv; + ASSERT_TRUE(CreateAndConnectSSLClientSocket(client_config, &rv)); + EXPECT_THAT(rv, IsOk()); + + // Sanity check + SSLInfo info; + ASSERT_TRUE(sock_->GetSSLInfo(&info)); + EXPECT_EQ(SSLInfo::HANDSHAKE_RESUME, info.handshake_type); + + base::HistogramTester histograms; + TestCompletionCallback callback; + + // Conclude the handshake by reading the ServerHello and make sure + // we still logged the result. + EXPECT_THAT(MakeHTTPRequest(sock_.get()), IsOk()); + histograms.ExpectUniqueSample(kReasonHistogram, ssl_early_data_accepted, 1); + } +} + TEST_F(SSLClientSocketTest, VersionOverride) { // Enable all test features in the server. SSLServerConfig server_config;
diff --git a/services/tracing/perfetto/json_trace_exporter.cc b/services/tracing/perfetto/json_trace_exporter.cc index c058b06..7ff1e98 100644 --- a/services/tracing/perfetto/json_trace_exporter.cc +++ b/services/tracing/perfetto/json_trace_exporter.cc
@@ -359,6 +359,17 @@ metadata_->SetDictionary("perfetto_trace_stats", std::move(dict)); } +void JSONTraceExporter::AddMetadata(const std::string& entry_name, + std::unique_ptr<base::Value> value) { + if (!metadata_filter_predicate_.is_null() && + !metadata_filter_predicate_.Run(entry_name)) { + metadata_->SetString(entry_name, kStrippedArgument); + return; + } + + metadata_->Set(entry_name, std::move(value)); +} + JSONTraceExporter::ScopedJSONTraceEventAppender JSONTraceExporter::AddTraceEvent(const char* name, const char* categories,
diff --git a/services/tracing/perfetto/json_trace_exporter.h b/services/tracing/perfetto/json_trace_exporter.h index c309fa5..cfb107b 100644 --- a/services/tracing/perfetto/json_trace_exporter.h +++ b/services/tracing/perfetto/json_trace_exporter.h
@@ -255,6 +255,9 @@ int32_t pid, int32_t tid); + void AddMetadata(const std::string& entry_name, + std::unique_ptr<base::Value> value); + private: // Used by the implementation to ensure the proper separators exist between // trace events in the array.
diff --git a/services/tracing/perfetto/track_event_json_exporter.cc b/services/tracing/perfetto/track_event_json_exporter.cc index f44b1e31..9acebe52 100644 --- a/services/tracing/perfetto/track_event_json_exporter.cc +++ b/services/tracing/perfetto/track_event_json_exporter.cc
@@ -94,17 +94,17 @@ // and wait for the first state_clear before emitting anything. if (packet.trusted_packet_sequence_id() != current_state_->trusted_packet_sequence_id) { + stats_.sequences_seen++; StartNewState(packet.trusted_packet_sequence_id(), packet.incremental_state_cleared()); } else if (packet.incremental_state_cleared()) { + stats_.incremental_state_resets++; ResetIncrementalState(); } else if (packet.previous_packet_dropped()) { // If we've lost packets we can no longer trust any timestamp data and // other state which might have been dropped. We will keep skipping events // until we start a new sequence. - LOG_IF(ERROR, current_state_->incomplete) - << "Previous packet was dropped. Skipping TraceEvents until reset or " - << "new sequence."; + stats_.packets_with_previous_packet_dropped++; current_state_->incomplete = true; } @@ -136,6 +136,7 @@ } if (!has_more) { EmitThreadDescriptorIfNeeded(); + EmitStats(); } } @@ -227,6 +228,7 @@ // InternedData is only emitted on sequences with incremental state. if (current_state_->incomplete) { + stats_.packets_dropped_invalid_incremental_state++; return; } @@ -311,6 +313,7 @@ // ProcessDescriptor is only emitted on sequences with incremental state. if (current_state_->incomplete) { + stats_.packets_dropped_invalid_incremental_state++; return; } @@ -388,6 +391,7 @@ // ThreadDescriptor is only emitted on sequences with incremental state. if (current_state_->incomplete) { + stats_.packets_dropped_invalid_incremental_state++; return; } @@ -472,6 +476,7 @@ // TrackEvents need incremental state. if (current_state_->incomplete) { + stats_.packets_dropped_invalid_incremental_state++; return; } @@ -500,12 +505,8 @@ const std::string joined_categories = base::JoinString(all_categories, ","); DCHECK(track.has_legacy_event()) << "required field legacy_event missing"; - auto maybe_builder = + auto builder = HandleLegacyEvent(track.legacy_event(), joined_categories, timestamp_us); - if (!maybe_builder) { - return; - } - auto& builder = *maybe_builder; if (thread_time_us) { builder.AddThreadTimestamp(*thread_time_us); @@ -532,7 +533,12 @@ void TrackEventJSONExporter::HandleStreamingProfilePacket( const perfetto::protos::StreamingProfilePacket& profile_packet) { - if (current_state_->incomplete || !ShouldOutputTraceEvents()) { + if (current_state_->incomplete) { + stats_.packets_dropped_invalid_incremental_state++; + return; + } + + if (!ShouldOutputTraceEvents()) { return; } @@ -701,7 +707,7 @@ } } -base::Optional<JSONTraceExporter::ScopedJSONTraceEventAppender> +JSONTraceExporter::ScopedJSONTraceEventAppender TrackEventJSONExporter::HandleLegacyEvent(const TrackEvent::LegacyEvent& event, const std::string& categories, int64_t timestamp_us) { @@ -791,4 +797,17 @@ builder.AddFlags(flags, id, event.id_scope()); return builder; } + +void TrackEventJSONExporter::EmitStats() { + auto value = std::make_unique<base::DictionaryValue>(); + value->SetInteger("sequences_seen", stats_.sequences_seen); + value->SetInteger("incremental_state_resets", + stats_.incremental_state_resets); + value->SetInteger("packets_dropped_invalid_incremental_state", + stats_.packets_dropped_invalid_incremental_state); + value->SetInteger("packets_with_previous_packet_dropped", + stats_.packets_with_previous_packet_dropped); + AddMetadata("json_exporter_stats", std::move(value)); +} + } // namespace tracing
diff --git a/services/tracing/perfetto/track_event_json_exporter.h b/services/tracing/perfetto/track_event_json_exporter.h index 5583cea..8565f3a 100644 --- a/services/tracing/perfetto/track_event_json_exporter.h +++ b/services/tracing/perfetto/track_event_json_exporter.h
@@ -154,17 +154,27 @@ ArgumentBuilder* args_builder); // Used to handle the LegacyEvent message found inside the TrackEvent proto. - base::Optional<ScopedJSONTraceEventAppender> HandleLegacyEvent( + ScopedJSONTraceEventAppender HandleLegacyEvent( const perfetto::protos::TrackEvent_LegacyEvent& event, const std::string& categories, int64_t timestamp_us); + void EmitStats(); + // Tracks all the interned state in the current sequence. std::unique_ptr<ProducerWriterState> current_state_; // Tracks out-of-order seqeuence data. std::map<uint32_t, UnorderedProducerWriterState> unordered_state_data_; + struct Stats { + int sequences_seen = 0; + int incremental_state_resets = 0; + int packets_dropped_invalid_incremental_state = 0; + int packets_with_previous_packet_dropped = 0; + }; + Stats stats_; + DISALLOW_COPY_AND_ASSIGN(TrackEventJSONExporter); };
diff --git a/services/tracing/perfetto/track_event_json_exporter_unittest.cc b/services/tracing/perfetto/track_event_json_exporter_unittest.cc index 0fbb8c4..8d9403d 100644 --- a/services/tracing/perfetto/track_event_json_exporter_unittest.cc +++ b/services/tracing/perfetto/track_event_json_exporter_unittest.cc
@@ -414,7 +414,8 @@ FinalizePackets(trace_packet_protos); // No traceEvents or data was emitted but a process descriptor without extra // data should just be an empty array and not cause crashes. - EXPECT_EQ("{\"traceEvents\":[]}", unparsed_trace_data_); + EXPECT_THAT(unparsed_trace_data_, + testing::StartsWith("{\"traceEvents\":[],")); } TEST_F(TrackEventJsonExporterTest, SortIndexProcessDescriptor) { @@ -575,7 +576,8 @@ FinalizePackets(trace_packet_protos); // No traceEvents or data was emitted but a thread descriptor should be an // empty array and not cause crashes. - EXPECT_EQ("{\"traceEvents\":[]}", unparsed_trace_data_); + EXPECT_THAT(unparsed_trace_data_, + testing::StartsWith("{\"traceEvents\":[],")); } TEST_F(TrackEventJsonExporterTest, SortIndexThreadDescriptor) { @@ -715,7 +717,8 @@ &trace_packet_protos); FinalizePackets(trace_packet_protos); // Interned data by itself does not call any trace events to be emitted. - EXPECT_EQ("{\"traceEvents\":[]}", unparsed_trace_data_); + EXPECT_THAT(unparsed_trace_data_, + testing::StartsWith("{\"traceEvents\":[],")); } TEST_F(TrackEventJsonExporterTest, LegacyEventBasicTest) { @@ -1758,10 +1761,11 @@ // base class. See json_trace_exporter_unittest for a more complete test. trace_packet_protos.back().mutable_trace_stats(); FinalizePackets(trace_packet_protos); - EXPECT_TRUE(base::StartsWith(unparsed_trace_data_, - "{\"traceEvents\":[]," - "\"metadata\":{\"perfetto_trace_stats\":{\"", - base::CompareCase::SENSITIVE)); + + EXPECT_THAT(unparsed_trace_data_, testing::StartsWith("{\"traceEvents\":[]," + "\"metadata\":{")); + EXPECT_THAT(unparsed_trace_data_, + testing::HasSubstr("\"perfetto_trace_stats\":{\"")); } TEST_F(TrackEventJsonExporterTest, ComplexLongSequenceWithDroppedPackets) {
diff --git a/testing/buildbot/filters/bfcache.chrome_public_test_apk.filter b/testing/buildbot/filters/bfcache.chrome_public_test_apk.filter index 25aa7788..df82a83e 100644 --- a/testing/buildbot/filters/bfcache.chrome_public_test_apk.filter +++ b/testing/buildbot/filters/bfcache.chrome_public_test_apk.filter
@@ -14,11 +14,15 @@ -org.chromium.chrome.browser.customtabs.DetachedResourceRequestTest.testSafeBrowsingSubresource -org.chromium.chrome.browser.customtabs.DetachedResourceRequestTest.testSafeBrowsingSubresourceBeforeNative -org.chromium.chrome.browser.externalnav.UrlOverridingTest.testRedirectionFromIntent +-org.chromium.chrome.browser.keyboard_accessory.PasswordGenerationIntegrationTest.testAutomaticGenerationUsePassword +-org.chromium.chrome.browser.keyboard_accessory.PasswordGenerationIntegrationTest.testManualGenerationUsePassword -org.chromium.chrome.browser.incognito.IncognitoTabLauncherTest.testLaunchIncognitoNewTab -org.chromium.chrome.browser.incognito.IncognitoTabLauncherTest.testLaunchIncognitoNewTab_omniboxFocused -org.chromium.chrome.browser.infobar.InfoBarTest.testInfoBarForGeolocationDisappearsOnBack -org.chromium.chrome.browser.media.MediaLauncherActivityTest.testHandleImageIntent -org.chromium.chrome.browser.media.MediaLauncherActivityTest.testHandleVideoIntent +-org.chromium.chrome.browser.notifications.NotificationPlatformBridgeIntentTest.testLaunchNotificationPreferencesForCategory +-org.chromium.chrome.browser.notifications.NotificationPlatformBridgeIntentTest.testLaunchNotificationPreferencesForWebsite -org.chromium.chrome.browser.offlinepages.indicator.OfflineIndicatorControllerTest.testDoNotShowOfflineIndicatorOnErrorPageWhenOffline -org.chromium.chrome.browser.password_manager.OnboardingDialogIntegrationTest.testOnboardingAccepted -org.chromium.chrome.browser.password_manager.OnboardingDialogIntegrationTest.testOnboardingDismissedPressedBack
diff --git a/testing/buildbot/filters/bfcache.content_browsertests.filter b/testing/buildbot/filters/bfcache.content_browsertests.filter index 0940316..4b48dad 100644 --- a/testing/buildbot/filters/bfcache.content_browsertests.filter +++ b/testing/buildbot/filters/bfcache.content_browsertests.filter
@@ -80,3 +80,17 @@ # In debug mode. The FrameHostInterceptor fails, because it doesn't take into # account pages in the BackForwardCache. -SecurityExploitBrowserTest.InvalidBeginNavigationInitiator + +# NOTREACHED() is hit in RenderFrameHostManager::GetFrameHostForNavigation, +# because, "A frame that's pending deletion should never be navigated.". +-NavigationControllerBrowserTest.PageStateWithIframeAfterForwardInCompetingFrames +-SitePerProcessBrowserTest.TwoCrossSitePendingNavigationsAndMainFrameWins + +# Failing on android only, need to be triaged, see https://crbug.com/1006267. +-BackForwardCacheMetricsBrowserTest.Fetch +-BackForwardCacheMetricsBrowserTest.XHR +-MSE_ClearKey/EncryptedMediaTest.ConfigChangeVideo_ClearToEncrypted/0 +-TouchpadPinchBrowserTest.WheelListenerPreventingPinch/1 +-WebRtcBrowserTest.CanSetupH264VideoCallOnSupportedDevice +-WithoutCORBProtectionSniffing/CrossSiteDocumentBlockingTest.AllowImagesWithSniffing/0 +-WithCORBProtectionSniffing/CrossSiteDocumentBlockingTest.AllowImagesWithSniffing/0
diff --git a/testing/buildbot/filters/bfcache.content_shell_test_apk.filter b/testing/buildbot/filters/bfcache.content_shell_test_apk.filter index 2d8f82a..caa6dfa 100644 --- a/testing/buildbot/filters/bfcache.content_shell_test_apk.filter +++ b/testing/buildbot/filters/bfcache.content_shell_test_apk.filter
@@ -1 +1,4 @@ # These tests currently fail when run with --enable-features=BackForwardCache + +# Fails for unknown reason. +-org.chromium.content.browser.InterstitialPageTest.testCloseInterstitial
diff --git a/testing/buildbot/filters/bfcache.content_unittests.filter b/testing/buildbot/filters/bfcache.content_unittests.filter index d230600..3a35d00 100644 --- a/testing/buildbot/filters/bfcache.content_unittests.filter +++ b/testing/buildbot/filters/bfcache.content_unittests.filter
@@ -39,3 +39,20 @@ # to exist. -WebContentsImplTest.CrossSiteBoundaries -WebContentsImplTest.NavigateFromSitelessUrl + +# Tests below are failing on android only. +# Need to be triaged, see https://crbug.com/1007276. + +# Numerous tests from these suites are crashing with: +# [FATAL:deferred_sequenced_task_runner.cc(83)] Check failed: !target_task_runner_ +-AudibleMetricsTest.* +-GeolocationServiceTest.* +-MediaInternalsAudioLogTest/MediaInternalsAudioLogTest.* +-MediaInternalsVideoCaptureDeviceTest.NotifyVideoCaptureDeviceCapabilitiesEnumerated +-MediaSessionControllerTest.* +-MediaSessionEnabledTestInstances/MediaSessionControllersManagerTest.* +-MediaSessionImplServiceRoutingTest.* +-MediaSessionImplTest.* +-MediaSessionImplUmaTest.* +-MediaStreamManagerTest.* +-RenderFrameAudioOutputStreamFactoryTest.*
diff --git a/testing/buildbot/filters/bfcache.unit_tests.filter b/testing/buildbot/filters/bfcache.unit_tests.filter index 2d8f82a..c54a330d 100644 --- a/testing/buildbot/filters/bfcache.unit_tests.filter +++ b/testing/buildbot/filters/bfcache.unit_tests.filter
@@ -1 +1,16 @@ # These tests currently fail when run with --enable-features=BackForwardCache + +# Tests below are failing on android only. +# Need to be triaged, see https://crbug.com/1007276. + +# Crashing with: +# [FATAL:deferred_sequenced_task_runner.cc(83)] Check failed: !target_task_runner_ +-ChromeBrowserMainExtraPartsMetricsTest.VerifyTouchEventsEnabledIsRecordedAfterPostBrowserStart +-ServicesTest.ConnectToFilePatch +-SoundContentSettingObserverTest.AudioMutingUpdatesWithNavigation + +# Crashing with: +# [FATAL:navigation_url_loader.cc(55)] Check failed: g_loader_factory == nullptr || factory == nullptr +-AddressAccessoryControllerTest.RefreshSuggestionsCallsUI +-CreditCardAccessoryControllerTest.PreventsFillingInsecureContexts +
diff --git a/third_party/blink/common/BUILD.gn b/third_party/blink/common/BUILD.gn index a41aeb7..853d173 100644 --- a/third_party/blink/common/BUILD.gn +++ b/third_party/blink/common/BUILD.gn
@@ -79,7 +79,6 @@ "origin_trials/trial_token_validator.cc", "page/page_zoom.cc", "peerconnection/webrtc_ip_handling_policy.cc", - "privacy_preferences.cc", "scheduler/web_scheduler_tracked_feature.cc", "service_worker/service_worker_status_code.cc", "service_worker/service_worker_type_converters.cc",
diff --git a/third_party/blink/common/privacy_preferences.cc b/third_party/blink/common/privacy_preferences.cc deleted file mode 100644 index 48351fa..0000000 --- a/third_party/blink/common/privacy_preferences.cc +++ /dev/null
@@ -1,14 +0,0 @@ -// Copyright 2018 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/public/common/privacy_preferences.h" - -namespace blink { - -PrivacyPreferences::PrivacyPreferences(bool enable_do_not_track, - bool enable_referrers) - : enable_do_not_track(enable_do_not_track), - enable_referrers(enable_referrers) {} - -} // namespace blink
diff --git a/third_party/blink/public/common/BUILD.gn b/third_party/blink/public/common/BUILD.gn index c341439..a3093c09 100644 --- a/third_party/blink/public/common/BUILD.gn +++ b/third_party/blink/public/common/BUILD.gn
@@ -115,7 +115,6 @@ "page/page_zoom.h", "peerconnection/webrtc_ip_handling_policy.h", "prerender/prerender_rel_type.h", - "privacy_preferences.h", "scheduler/web_scheduler_tracked_feature.h", "screen_orientation/web_screen_orientation_lock_type.h", "screen_orientation/web_screen_orientation_type.h",
diff --git a/third_party/blink/public/common/privacy_preferences.h b/third_party/blink/public/common/privacy_preferences.h deleted file mode 100644 index efcadde..0000000 --- a/third_party/blink/public/common/privacy_preferences.h +++ /dev/null
@@ -1,30 +0,0 @@ -// Copyright 2018 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_PUBLIC_COMMON_PRIVACY_PREFERENCES_H_ -#define THIRD_PARTY_BLINK_PUBLIC_COMMON_PRIVACY_PREFERENCES_H_ - -#include "third_party/blink/public/common/common_export.h" - -namespace blink { - -// Subset of content::RendererPreferences for passing the security info to -// blink. -// TODO(crbug.com/869748): Move this into a mojom struct and use the new struct -// as a part of RendererPreferences once RendererPreferences becomes a mojom -// struct. -struct BLINK_COMMON_EXPORT PrivacyPreferences { - PrivacyPreferences() = default; - PrivacyPreferences(bool enable_do_not_track, - bool enable_referrers); - - // These default values are coming from the defaults in - // content::RendererPreferences. - bool enable_do_not_track = false; - bool enable_referrers = true; -}; - -} // namespace blink - -#endif // THIRD_PARTY_BLINK_PUBLIC_COMMON_PRIVACY_PREFERENCES_H_
diff --git a/third_party/blink/public/mojom/renderer_preferences.mojom b/third_party/blink/public/mojom/renderer_preferences.mojom index 26c3aa13..e614a3b 100644 --- a/third_party/blink/public/mojom/renderer_preferences.mojom +++ b/third_party/blink/public/mojom/renderer_preferences.mojom
@@ -63,7 +63,6 @@ bool use_custom_colors = true; // Set to false to not send referrers. - // The default value should be in sync with blink::PrivacyPreferences. bool enable_referrers = true; // Set to true to allow third-party sub-content to pop-up HTTP basic auth @@ -71,7 +70,6 @@ bool allow_cross_origin_auth_prompt = false; // Set to true to indicate that the preference to set DNT to 1 is enabled. - // The default value should be in sync with blink::PrivacyPreferences. bool enable_do_not_track = false; // Whether to allow the use of Encrypted Media Extensions (EME), except for
diff --git a/third_party/blink/public/web/web_embedded_worker_start_data.h b/third_party/blink/public/web/web_embedded_worker_start_data.h index be3cf43..6d6952a 100644 --- a/third_party/blink/public/web/web_embedded_worker_start_data.h +++ b/third_party/blink/public/web/web_embedded_worker_start_data.h
@@ -33,7 +33,6 @@ #include "base/unguessable_token.h" #include "services/network/public/mojom/ip_address_space.mojom-shared.h" -#include "third_party/blink/public/common/privacy_preferences.h" #include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom-shared.h" #include "third_party/blink/public/platform/web_content_security_policy.h" #include "third_party/blink/public/platform/web_string.h" @@ -56,8 +55,6 @@ network::mojom::IPAddressSpace address_space; - PrivacyPreferences privacy_preferences; - WebEmbeddedWorkerStartData() : wait_for_debugger_mode(kDontWaitForDebugger) {} };
diff --git a/third_party/blink/renderer/bindings/core/v8/to_v8_for_core.h b/third_party/blink/renderer/bindings/core/v8/to_v8_for_core.h index 5087a684..bdf622c 100644 --- a/third_party/blink/renderer/bindings/core/v8/to_v8_for_core.h +++ b/third_party/blink/renderer/bindings/core/v8/to_v8_for_core.h
@@ -12,6 +12,7 @@ #include "third_party/blink/renderer/bindings/core/v8/script_value.h" #include "third_party/blink/renderer/core/typed_arrays/array_buffer_view_helpers.h" #include "third_party/blink/renderer/platform/bindings/to_v8.h" +#include "third_party/blink/renderer/platform/heap/disallow_new_wrapper.h" #include "v8/include/v8.h" namespace blink { @@ -64,6 +65,14 @@ return value.V8Value(); } +inline v8::Local<v8::Value> ToV8(const DisallowNewWrapper<ScriptValue>* value, + v8::Local<v8::Object> creation_context, + v8::Isolate* isolate) { + if (value->Value().IsEmpty()) + return v8::Undefined(isolate); + return value->Value().V8Value(); +} + // Cannot define in ScriptValue because of the circular dependency between toV8 // and ScriptValue template <typename T>
diff --git a/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc b/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc index 57106f4..1463b2b 100644 --- a/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc +++ b/third_party/blink/renderer/bindings/core/v8/worker_or_worklet_script_controller.cc
@@ -114,9 +114,8 @@ rejected_promises_->Dispose(); rejected_promises_ = nullptr; - world_->Dispose(); - DisposeContextIfNeeded(); + world_->Dispose(); } void WorkerOrWorkletScriptController::DisposeContextIfNeeded() { @@ -132,9 +131,23 @@ { ScriptState::Scope scope(script_state_); + v8::Local<v8::Context> context = script_state_->GetContext(); + // After disposing the world, all Blink->V8 references are gone. Blink + // stand-alone GCs may collect the WorkerOrWorkletGlobalScope because there + // are no more roots (V8->Blink references that are actually found by + // iterating Blink->V8 references). Clear the back pointers to avoid + // referring to cleared memory on the next GC in case the JS wrapper objects + // survived. + v8::Local<v8::Object> global_proxy_object = context->Global(); + v8::Local<v8::Object> global_object = + global_proxy_object->GetPrototype().As<v8::Object>(); + DCHECK(!global_object.IsEmpty()); + V8DOMWrapper::ClearNativeInfo(isolate_, global_object); + V8DOMWrapper::ClearNativeInfo(isolate_, global_proxy_object); + // This detaches v8::MicrotaskQueue pointer from v8::Context, so that we can // destroy EventLoop safely. - script_state_->GetContext()->DetachGlobal(); + context->DetachGlobal(); } script_state_->DisposePerContextData();
diff --git a/third_party/blink/renderer/bindings/modules/v8/generated.gni b/third_party/blink/renderer/bindings/modules/v8/generated.gni index 60255a8..ee03000 100644 --- a/third_party/blink/renderer/bindings/modules/v8/generated.gni +++ b/third_party/blink/renderer/bindings/modules/v8/generated.gni
@@ -96,6 +96,8 @@ "$bindings_modules_v8_output_dir/unsigned_long_or_unsigned_long_sequence.h", "$bindings_modules_v8_output_dir/unsigned_long_sequence_or_gpu_extent_3d_dict.cc", "$bindings_modules_v8_output_dir/unsigned_long_sequence_or_gpu_extent_3d_dict.h", + "$bindings_modules_v8_output_dir/unsigned_long_sequence_or_gpu_origin_3d_dict.cc", + "$bindings_modules_v8_output_dir/unsigned_long_sequence_or_gpu_origin_3d_dict.h", "$bindings_modules_v8_output_dir/webgl_rendering_context_or_webgl2_rendering_context.cc", "$bindings_modules_v8_output_dir/webgl_rendering_context_or_webgl2_rendering_context.h", ]
diff --git a/third_party/blink/renderer/core/animation_frame/worker_animation_frame_provider.cc b/third_party/blink/renderer/core/animation_frame/worker_animation_frame_provider.cc index 3590b42..6152182 100644 --- a/third_party/blink/renderer/core/animation_frame/worker_animation_frame_provider.cc +++ b/third_party/blink/renderer/core/animation_frame/worker_animation_frame_provider.cc
@@ -56,6 +56,7 @@ // we abort the whole process. if (!inside_raf_scope.AddOffscreenCanvas(offscreen_canvas)) { provider->begin_frame_provider_->FinishBeginFrame(args); + provider->begin_frame_provider_->RequestBeginFrame(); return; } }
diff --git a/third_party/blink/renderer/core/css/style_recalc_root.cc b/third_party/blink/renderer/core/css/style_recalc_root.cc index 0a251fb..255d93f 100644 --- a/third_party/blink/renderer/core/css/style_recalc_root.cc +++ b/third_party/blink/renderer/core/css/style_recalc_root.cc
@@ -36,7 +36,7 @@ #if DCHECK_IS_ON() ContainerNode* StyleRecalcRoot::Parent(const Node& node) const { - return node.ParentOrShadowHostNode(); + return node.GetStyleRecalcParent(); } bool StyleRecalcRoot::IsChildDirty(const ContainerNode& node) const { @@ -50,7 +50,7 @@ void StyleRecalcRoot::ClearChildDirtyForAncestors(ContainerNode& parent) const { for (ContainerNode* ancestor = &parent; ancestor; - ancestor = ancestor->ParentOrShadowHostNode()) { + ancestor = ancestor->GetStyleRecalcParent()) { ancestor->ClearChildNeedsStyleRecalc(); DCHECK(!ancestor->NeedsStyleRecalc()); }
diff --git a/third_party/blink/renderer/core/dom/flat_tree_node_data.h b/third_party/blink/renderer/core/dom/flat_tree_node_data.h index 1c95ad2f..a991349 100644 --- a/third_party/blink/renderer/core/dom/flat_tree_node_data.h +++ b/third_party/blink/renderer/core/dom/flat_tree_node_data.h
@@ -47,6 +47,7 @@ friend class FlatTreeTraversal; friend class HTMLSlotElement; friend HTMLSlotElement* Node::AssignedSlot() const; + friend Element* Node::FlatTreeParentForChildDirty() const; WeakMember<HTMLSlotElement> assigned_slot_; WeakMember<Node> previous_in_assigned_nodes_;
diff --git a/third_party/blink/renderer/core/dom/node.cc b/third_party/blink/renderer/core/dom/node.cc index 2d68f98d..d60f5f9 100644 --- a/third_party/blink/renderer/core/dom/node.cc +++ b/third_party/blink/renderer/core/dom/node.cc
@@ -1209,10 +1209,10 @@ } void Node::MarkAncestorsWithChildNeedsStyleRecalc() { - ContainerNode* ancestor = ParentOrShadowHostNode(); + ContainerNode* ancestor = GetStyleRecalcParent(); bool parent_dirty = ancestor && ancestor->NeedsStyleRecalc(); for (; ancestor && !ancestor->ChildNeedsStyleRecalc(); - ancestor = ancestor->ParentOrShadowHostNode()) { + ancestor = ancestor->GetStyleRecalcParent()) { if (!ancestor->isConnected()) return; ancestor->SetChildNeedsStyleRecalc(); @@ -1242,7 +1242,7 @@ if (RuntimeEnabledFeatures::DisplayLockingEnabled() && GetDocument().LockedDisplayLockCount() > 0) { for (auto* ancestor_copy = ancestor; ancestor_copy; - ancestor_copy = ancestor_copy->ParentOrShadowHostNode()) { + ancestor_copy = ancestor_copy->GetStyleRecalcParent()) { auto* ancestor_copy_element = DynamicTo<Element>(ancestor_copy); if (ancestor_copy_element && ancestor_copy_element->StyleRecalcBlockedByDisplayLock( @@ -1256,12 +1256,12 @@ GetDocument().ScheduleLayoutTreeUpdateIfNeeded(); } -Element* Node::GetReattachParent() const { +Element* Node::FlatTreeParentForChildDirty() const { if (IsPseudoElement()) return ParentOrShadowHostElement(); if (IsChildOfV1ShadowHost()) { - if (HTMLSlotElement* slot = AssignedSlot()) - return slot; + if (auto* data = GetFlatTreeNodeData()) + return data->AssignedSlot(); } if (IsInV0ShadowTree() || IsChildOfV0ShadowHost()) { if (ShadowRootWhereNodeCanBeDistributedForV0(*this)) { @@ -1274,6 +1274,12 @@ return ParentOrShadowHostElement(); } +ContainerNode* Node::GetStyleRecalcParent() const { + if (RuntimeEnabledFeatures::FlatTreeStyleRecalcEnabled()) + return FlatTreeParentForChildDirty(); + return ParentOrShadowHostNode(); +} + void Node::MarkAncestorsWithChildNeedsReattachLayoutTree() { DCHECK(isConnected()); Element* ancestor = GetReattachParent();
diff --git a/third_party/blink/renderer/core/dom/node.h b/third_party/blink/renderer/core/dom/node.h index cfb63a5e..7493a2f 100644 --- a/third_party/blink/renderer/core/dom/node.h +++ b/third_party/blink/renderer/core/dom/node.h
@@ -627,7 +627,9 @@ bool IsChildOfV1ShadowHost() const; bool IsChildOfV0ShadowHost() const; ShadowRoot* V1ShadowRootOfParent() const; - Element* GetReattachParent() const; + Element* FlatTreeParentForChildDirty() const; + ContainerNode* GetStyleRecalcParent() const; + Element* GetReattachParent() const { return FlatTreeParentForChildDirty(); } bool IsDocumentTypeNode() const { return getNodeType() == kDocumentTypeNode; } virtual bool ChildTypeAllowed(NodeType) const { return false; }
diff --git a/third_party/blink/renderer/core/editing/finder/find_task_controller.cc b/third_party/blink/renderer/core/editing/finder/find_task_controller.cc index a67f6e0..ce853a5 100644 --- a/third_party/blink/renderer/core/editing/finder/find_task_controller.cc +++ b/third_party/blink/renderer/core/editing/finder/find_task_controller.cc
@@ -4,6 +4,7 @@ #include "third_party/blink/renderer/core/editing/finder/find_task_controller.h" +#include "third_party/blink/public/mojom/frame/find_in_page.mojom-blink.h" #include "third_party/blink/renderer/core/dom/document.h" #include "third_party/blink/renderer/core/dom/idle_request_options.h" #include "third_party/blink/renderer/core/dom/range.h"
diff --git a/third_party/blink/renderer/core/editing/finder/find_task_controller.h b/third_party/blink/renderer/core/editing/finder/find_task_controller.h index f38c2662..58e6c839 100644 --- a/third_party/blink/renderer/core/editing/finder/find_task_controller.h +++ b/third_party/blink/renderer/core/editing/finder/find_task_controller.h
@@ -5,7 +5,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_FINDER_FIND_TASK_CONTROLLER_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_FINDER_FIND_TASK_CONTROLLER_H_ -#include "third_party/blink/public/mojom/frame/find_in_page.mojom-blink.h" +#include "third_party/blink/public/mojom/frame/find_in_page.mojom-blink-forward.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/editing/position.h" #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
diff --git a/third_party/blink/renderer/core/editing/finder/text_finder.h b/third_party/blink/renderer/core/editing/finder/text_finder.h index cf2233af..c025a32 100644 --- a/third_party/blink/renderer/core/editing/finder/text_finder.h +++ b/third_party/blink/renderer/core/editing/finder/text_finder.h
@@ -32,7 +32,7 @@ #define THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_FINDER_TEXT_FINDER_H_ #include "base/macros.h" -#include "third_party/blink/public/mojom/frame/find_in_page.mojom-blink.h" +#include "third_party/blink/public/mojom/frame/find_in_page.mojom-blink-forward.h" #include "third_party/blink/public/platform/web_float_point.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/platform/geometry/float_rect.h"
diff --git a/third_party/blink/renderer/core/editing/finder/text_finder_test.cc b/third_party/blink/renderer/core/editing/finder/text_finder_test.cc index f56e897..f45477f 100644 --- a/third_party/blink/renderer/core/editing/finder/text_finder_test.cc +++ b/third_party/blink/renderer/core/editing/finder/text_finder_test.cc
@@ -5,6 +5,7 @@ #include "third_party/blink/renderer/core/editing/finder/text_finder.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/mojom/frame/find_in_page.mojom-blink.h" #include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/web_float_rect.h" #include "third_party/blink/public/web/web_document.h"
diff --git a/third_party/blink/renderer/core/fetch/body.cc b/third_party/blink/renderer/core/fetch/body.cc index 8bc79a6..162d2d5 100644 --- a/third_party/blink/renderer/core/fetch/body.cc +++ b/third_party/blink/renderer/core/fetch/body.cc
@@ -22,6 +22,7 @@ #include "third_party/blink/renderer/platform/bindings/exception_state.h" #include "third_party/blink/renderer/platform/bindings/script_state.h" #include "third_party/blink/renderer/platform/bindings/v8_throw_exception.h" +#include "third_party/blink/renderer/platform/heap/disallow_new_wrapper.h" #include "third_party/blink/renderer/platform/heap/heap.h" #include "third_party/blink/renderer/platform/network/parsed_content_type.h" #include "third_party/blink/renderer/platform/wtf/functional.h" @@ -146,8 +147,8 @@ if (v8::JSON::Parse(Resolver()->GetScriptState()->GetContext(), input_string) .ToLocal(&parsed)) { - ResolveLater( - ScriptValue(Resolver()->GetScriptState()->GetIsolate(), parsed)); + ResolveLater(WrapPersistent(WrapDisallowNew( + ScriptValue(Resolver()->GetScriptState()->GetIsolate(), parsed)))); } else Resolver()->Reject(trycatch.Exception()); }
diff --git a/third_party/blink/renderer/core/frame/web_local_frame_impl.h b/third_party/blink/renderer/core/frame/web_local_frame_impl.h index ab52cef..769973a 100644 --- a/third_party/blink/renderer/core/frame/web_local_frame_impl.h +++ b/third_party/blink/renderer/core/frame/web_local_frame_impl.h
@@ -40,7 +40,7 @@ #include "mojo/public/cpp/bindings/pending_associated_remote.h" #include "third_party/blink/public/mojom/ad_tagging/ad_frame.mojom-blink.h" #include "third_party/blink/public/mojom/devtools/devtools_agent.mojom-blink.h" -#include "third_party/blink/public/mojom/frame/find_in_page.mojom-blink.h" +#include "third_party/blink/public/mojom/frame/find_in_page.mojom-blink-forward.h" #include "third_party/blink/public/mojom/frame/lifecycle.mojom-blink.h" #include "third_party/blink/public/mojom/portal/portal.mojom-blink.h" #include "third_party/blink/public/platform/web_file_system_type.h"
diff --git a/third_party/blink/renderer/core/script/resources/layered_api/elements/switch/index.mjs b/third_party/blink/renderer/core/script/resources/layered_api/elements/switch/index.mjs index 81a02e5..6732ffd 100644 --- a/third_party/blink/renderer/core/script/resources/layered_api/elements/switch/index.mjs +++ b/third_party/blink/renderer/core/script/resources/layered_api/elements/switch/index.mjs
@@ -9,10 +9,37 @@ import {SwitchTrack} from './track.mjs'; const generateStyleSheet = style.styleSheetFactory(); +const generateMaterialStyleSheet = style.materialStyleSheetFactory(); // https://github.com/tkent-google/std-switch/issues/2 const STATE_ATTR = 'on'; +function parentOrHostElement(element) { + const parent = element.parentNode; + if (!parent) { + return null; + } + if (parent.nodeType === Node.ELEMENT_NODE) { + return parent; + } + if (parent.nodeType === Node.DOCUMENT_FRAGMENT_NODE) { + return parent.host; + } + return null; +} + +function shouldUsePlatformTheme(element) { + for (; element; element = parentOrHostElement(element)) { + const themeValue = element.getAttribute('theme'); + if (themeValue === 'match-platform') { + return true; + } else if (themeValue === 'platform-agnostic') { + return false; + } + } + return false; +} + export class StdSwitchElement extends HTMLElement { // TODO(tkent): The following should be |static fooBar = value;| // after enabling babel-eslint. @@ -27,6 +54,7 @@ #track; #containerElement; #inUserAction = false; + #shadowRoot; constructor() { super(); @@ -62,6 +90,11 @@ } connectedCallback() { + // The element might have been disconnected when the callback is invoked. + if (!this.isConnected) { + return; + } + // TODO(tkent): We should not add tabindex attribute. // https://github.com/w3c/webcomponents/issues/762 if (!this.hasAttribute('tabindex')) { @@ -76,6 +109,15 @@ this.setAttribute('role', 'switch'); } } + + if (shouldUsePlatformTheme(this)) { + // TODO(tkent): Should we apply Cocoa-like on macOS and Fluent-like + // on Windows? + this.#shadowRoot.adoptedStyleSheets = + [generateStyleSheet(), generateMaterialStyleSheet()]; + } else { + this.#shadowRoot.adoptedStyleSheets = [generateStyleSheet()]; + } } formResetCallback() { @@ -100,7 +142,7 @@ thumbElement.id = 'thumb'; thumbElement.part.add('thumb'); - root.adoptedStyleSheets = [generateStyleSheet()]; + this.#shadowRoot = root; }; #onClick = () => {
diff --git a/third_party/blink/renderer/core/script/resources/layered_api/elements/switch/style.mjs b/third_party/blink/renderer/core/script/resources/layered_api/elements/switch/style.mjs index ba213120a..ded00d0 100644 --- a/third_party/blink/renderer/core/script/resources/layered_api/elements/switch/style.mjs +++ b/third_party/blink/renderer/core/script/resources/layered_api/elements/switch/style.mjs
@@ -11,6 +11,10 @@ const THUMB_MARGIN_START = '2px'; const THUMB_MARGIN_END = '2px'; +const MATERIAL_THUMB_SIZE = '20px'; +const RIPPLE_COLOR = 'rgba(100,100,100,0.3)'; +const RIPPLE_MAX_SIZE = '48px'; + // Returns a function returning a CSSStyleSheet(). // TODO(tkent): Share this stylesheet factory feature with elements/toast/. export function styleSheetFactory() { @@ -151,6 +155,122 @@ }; } +export function materialStyleSheetFactory() { + let styleSheet; + return () => { + if (!styleSheet) { + styleSheet = new CSSStyleSheet(); + styleSheet.replaceSync(` +:host { + block-size: 20px; + inline-size: 36px; +} + +#track, +:host(:active) #track { + background: rgba(0,0,0,0.4); + block-size: 14px; + border: none; + box-shadow: none; +} + +:host([on]) #track, +:host([on]:active) #track, +:host([on]:focus) #track { + border: none; + box-shadow: none; +} + +#trackFill, +:host([on]:active) #trackFill { + background: rgba(63,81,181,0.5); +} + +#thumb, +:host(:focus) #thumb { + block-size: ${MATERIAL_THUMB_SIZE}; + border-radius: calc(${MATERIAL_THUMB_SIZE} / 2); + border: none; + box-shadow: 0 1px 5px 0 rgba(0,0,0,0.6); + inline-size: ${MATERIAL_THUMB_SIZE}; + margin-inline-start: -100%; +} + +:host([on]) #thumb, +:host([on]:focus) #thumb, +:host([on]:not(:disabled):hover) #thumb { + background: rgb(63,81,181); + border: none; + margin-inline-start: calc(0px - 20px); +} + +:host(:not(:disabled):hover) #thumb { + inline-size: ${MATERIAL_THUMB_SIZE}; +} + +/* + * Ripple effect + * + * Translucent circle is painted on the thumb if the element is :active or + * :focus-visible. It has + * - Size transition when it appears + * - Opacity transition when it disappears + * part(thumb)::before represents the former, and part(thumb)::after represents + * the latter. + */ +#thumb::before { + background: ${RIPPLE_COLOR}; + block-size: 0px; + border-radius: 0px; + content: ""; + display: inline-block; + inline-size: 0px; + left: calc(${MATERIAL_THUMB_SIZE} / 2); + position: relative; + top: calc(${MATERIAL_THUMB_SIZE} / 2); + transition: none; +} + +:host(:active) #thumb::before, +:host(:focus-visible) #thumb::before { + block-size: ${RIPPLE_MAX_SIZE}; + border-radius: calc(${RIPPLE_MAX_SIZE} / 2); + inline-size: ${RIPPLE_MAX_SIZE}; + left: calc((${MATERIAL_THUMB_SIZE} - ${RIPPLE_MAX_SIZE}) / 2); + top: calc((${MATERIAL_THUMB_SIZE} - ${RIPPLE_MAX_SIZE}) / 2); + transition: all linear 0.1s; +} + +#thumb::after { + background: ${RIPPLE_COLOR}; + block-size: ${RIPPLE_MAX_SIZE}; + border-radius: calc(${RIPPLE_MAX_SIZE} / 2); + content: ""; + display: inline-block; + inline-size: ${RIPPLE_MAX_SIZE}; + left: calc((${MATERIAL_THUMB_SIZE} - ${RIPPLE_MAX_SIZE}) / 2); + opacity: 0; + position: relative; + /* Why 18px? */ + top: calc((${MATERIAL_THUMB_SIZE} - ${RIPPLE_MAX_SIZE}) / 2 - 18px); + transition: opacity linear 0.3s; +} + +:host(:active) #thumb::after, +:host(:focus-visible) #thumb::after { + block-size: 0px; + content: ""; + inline-size: 0px; + opacity: 1; + transition: none; +} + +`); + } + return styleSheet; + }; +} + /** * Add '$part-transitioning' part to the element if the element already has * a part name.
diff --git a/third_party/blink/renderer/core/svg/animation/smil_animation_sandwich.h b/third_party/blink/renderer/core/svg/animation/smil_animation_sandwich.h index d5ef9ab..b4fd552 100644 --- a/third_party/blink/renderer/core/svg/animation/smil_animation_sandwich.h +++ b/third_party/blink/renderer/core/svg/animation/smil_animation_sandwich.h
@@ -50,6 +50,70 @@ // This class implements/helps with implementing the "sandwich model" from SMIL. // https://www.w3.org/TR/SMIL3/smil-animation.html#animationNS-AnimationSandwichModel +// +// A "sandwich" contains all the animation elements (actually timed elements in +// our case because of how we handle <discard>) that targets a specific +// attribute (or property) on a certain element. +// +// Consider the following simple example: +// +// <svg> +// <rect id="foo" width="100" height="100" fill="yellow"> +// <set id="s1" attributeName="fill" to="blue" begin="1s; 3s" dur="1s"/> +// <set id="s2" attributeName="fill" to="lightblue" begin="1.5s" dur="2s"/> +// </rect> +// </svg> +// +// In this case there is only one sandwich: <#foo, "fill"> +// +// The sandwich is priority-sorted with the priority being derived from when +// the currently active interval began - later is higher. In the above example +// there are three intervals: [1s 2s) and [3s 4s) for the first <set> element +// (in tree-order) and [1.5s 3.5s) for the second <set> element. The animation +// elements are only active within the intervals defined (no fill="freeze"). +// +// When the first interval of the first <set> starts (at 1s), it is the only +// active animation and thus the only one to apply. When the second interval +// starts (at 1.5s) its animation gets a higher priority and replaces the lower +// priority animation from the first <set>. The first <set> then ends at 2s, +// leaving the second <set> as the only active animation. When the first <set> +// then starts again at 3s it gets a higher priority because of the later begin +// time and replaces the animation from the second <set>. When the second <set> +// ends at 3.5s nothing changes because the first <set> is still active. When +// the second <set> ends again at 4s, no animation apply and the target reverts +// to the base value (yellow) again. +// +// Schematically (right hand side exclusive): +// +// 0s -> 1s: No animations apply (fill=yellow) +// Sandwich order: (s1) (s2) [both inactive] +// 1s -> 1.5s: The first <set> apply (fill=blue) +// Sandwich order: s1 (s2) [only s1 active] +// 1.5s -> 2s: The second <set> apply (fill=lightblue) +// Sandwich order: s1 s2 +// 2s -> 3s: The second <set> apply (fill=lightblue) +// Sandwich order: (s1) s2 +// 3s -> 3.5s: The first <set> apply (fill=blue) +// Sandwich order: s2 s1 +// 3.5s -> 4s: The first <set> apply (fill=blue) +// Sandwich order: (s2) s1 +// 4s -> ...: No animations apply (fill=yellow) +// +// ----- +// +// Implementation details: +// +// UpdateTiming() handles updates to interval and transitions the active state. +// +// UpdateSyncBases() handles the sorting described above (as well notifying +// about new intervals). +// +// UpdateActiveAnimationStack() constructs a vector containing only the active +// elements. +// +// ApplyAnimationValues() computes the actual animation value based on the +// vector of active elements and applies it to the target element. +// class SMILAnimationSandwich : public GarbageCollected<SMILAnimationSandwich> { public: using ScheduledVector = HeapVector<Member<SVGSMILElement>>;
diff --git a/third_party/blink/renderer/devtools/front_end/common/SegmentedRange.js b/third_party/blink/renderer/devtools/front_end/common/SegmentedRange.js index f11db7c..97447fe 100644 --- a/third_party/blink/renderer/devtools/front_end/common/SegmentedRange.js +++ b/third_party/blink/renderer/devtools/front_end/common/SegmentedRange.js
@@ -12,7 +12,7 @@ */ constructor(begin, end, data) { if (begin > end) { - console.assert(false, 'Invalid segment'); + throw new Error('Invalid segment'); } this.begin = begin; this.end = end;
diff --git a/third_party/blink/renderer/devtools/front_end/sdk/NetworkRequest.js b/third_party/blink/renderer/devtools/front_end/sdk/NetworkRequest.js index 1ba954cb..5e74e634 100644 --- a/third_party/blink/renderer/devtools/front_end/sdk/NetworkRequest.js +++ b/third_party/blink/renderer/devtools/front_end/sdk/NetworkRequest.js
@@ -1485,7 +1485,7 @@ `This cookie didn't specify a SameSite attribute when it was stored and was defaulted to "SameSite=Lax" and broke the same rules specified in the SameSiteLax value. The cookie had to have been set with "SameSite=None" to enable third-party usage.`; case Protocol.Network.CookieBlockedReason.SameSiteNoneInsecure: return ls - `This cookie had the "SameSite=None" attribute and the connection was not secure. Cookies without SameSite restrictions must be sent over a secure connection.`; + `This cookie had the "SameSite=None" attribute but was not marked "Secure". Cookies without SameSite restrictions must be marked "Secure" and sent over a secure connection.`; case Protocol.Network.CookieBlockedReason.UserPreferences: return ls`This cookie was not sent due to user preferences.`; case Protocol.Network.CookieBlockedReason.UnknownError:
diff --git a/third_party/blink/renderer/devtools/front_end/sdk/sdk_strings.grdp b/third_party/blink/renderer/devtools/front_end/sdk/sdk_strings.grdp index 9e4c73df..e7921c2b 100644 --- a/third_party/blink/renderer/devtools/front_end/sdk/sdk_strings.grdp +++ b/third_party/blink/renderer/devtools/front_end/sdk/sdk_strings.grdp
@@ -3,6 +3,9 @@ <message name="IDS_DEVTOOLS_021aa7730980fe55529e460ee1367179" desc="Tooltip to explain why a cookie was blocked"> This cookie had the "SameSite=Extended" attribute and the request was made on a different site. The different site is outside of the cookie's trusted first-party set. </message> + <message name="IDS_DEVTOOLS_04608e209233c72a33b9a80e8ff2bd58" desc="Tooltip to explain why a cookie was blocked"> + This cookie had the "SameSite=None" attribute but was not marked "Secure". Cookies without SameSite restrictions must be marked "Secure" and sent over a secure connection. + </message> <message name="IDS_DEVTOOLS_07553a11db31a4433684be32cc4716e3" desc="Text in Network Manager"> Cross-Origin Read Blocking (CORB) blocked cross-origin response <ph name="NETWORKREQUEST_URL__">$1s<ex>https://example.com</ex></ph> with MIME type <ph name="NETWORKREQUEST_MIMETYPE">$2s<ex>application</ex></ph>. See https://www.chromestatus.com/feature/5629709824032768 for more details. </message> @@ -204,9 +207,6 @@ <message name="IDS_DEVTOOLS_794f64c7f20487f6e13679201deeab3d" desc="Text in DOMDebugger Model"> Picture-in-Picture </message> - <message name="IDS_DEVTOOLS_7a3a5ae00a1133b1042cf4edf671736a" desc="Tooltip to explain why a cookie was blocked"> - This cookie had the "SameSite=None" attribute and the connection was not secure. Cookies without SameSite restrictions must be sent over a secure connection. - </message> <message name="IDS_DEVTOOLS_7c2bc755363ab11a1611bfa369654ff8" desc="Title of a setting under the Rendering category that can be invoked through the Command Menu"> Show frames per second (FPS) meter </message>
diff --git a/third_party/blink/renderer/devtools/front_end/sources/DebuggerPlugin.js b/third_party/blink/renderer/devtools/front_end/sources/DebuggerPlugin.js index b277a0d..0cd0eff1 100644 --- a/third_party/blink/renderer/devtools/front_end/sources/DebuggerPlugin.js +++ b/third_party/blink/renderer/devtools/front_end/sources/DebuggerPlugin.js
@@ -435,6 +435,12 @@ let startHighlight; let endHighlight; + const selectedCallFrame = + /** @type {!SDK.DebuggerModel.CallFrame} */ (UI.context.flavor(SDK.DebuggerModel.CallFrame)); + if (!selectedCallFrame) { + return null; + } + if (textSelection && !textSelection.isEmpty()) { if (textSelection.startLine !== textSelection.endLine || textSelection.startLine !== mouseLine || mouseColumn < textSelection.startColumn || mouseColumn > textSelection.endColumn) { @@ -476,24 +482,53 @@ if (tokenBefore.type === 'js-meta') { break; } + if (tokenBefore.type === 'js-string-2') { + // If we hit a template literal, find the opening ` in this line. + // TODO(bmeurer): We should eventually replace this tokenization + // approach with a proper soluation based on parsing, maybe reusing + // the Parser and AST inside V8 for this (or potentially relying on + // acorn to do the job). + if (tokenBefore.endColumn < 2) { + return null; + } + startHighlight = line.lastIndexOf('`', tokenBefore.endColumn - 2); + if (startHighlight < 0) { + return null; + } + break; + } startHighlight = tokenBefore.startColumn; } } + // The eager evaluation on works sort of reliably within the top-most scope of + // the selected call frame, so don't even try outside the top-most scope. + const [scope] = selectedCallFrame.scopeChain(); + if (scope && scope.startLocation() && scope.endLocation()) { + if (editorLineNumber < scope.startLocation().lineNumber) { + return null; + } + if (editorLineNumber === scope.startLocation().lineNumber && + startHighlight < scope.startLocation().columnNumber) { + return null; + } + if (editorLineNumber > scope.endLocation().lineNumber) { + return null; + } + if (editorLineNumber === scope.endLocation().lineNumber && endHighlight > scope.endLocation().columnNumber) { + return null; + } + } + let objectPopoverHelper; let highlightDescriptor; return { box: anchorBox, show: async popover => { - const selectedCallFrame = UI.context.flavor(SDK.DebuggerModel.CallFrame); - if (!selectedCallFrame) { - return false; - } const evaluationText = this._textEditor.line(editorLineNumber).substring(startHighlight, endHighlight + 1); const resolvedText = await Sources.SourceMapNamesResolver.resolveExpression( - /** @type {!SDK.DebuggerModel.CallFrame} */ (selectedCallFrame), evaluationText, this._uiSourceCode, - editorLineNumber, startHighlight, endHighlight); + selectedCallFrame, evaluationText, this._uiSourceCode, editorLineNumber, startHighlight, endHighlight); const result = await selectedCallFrame.evaluate({ expression: resolvedText || evaluationText, objectGroup: 'popover',
diff --git a/third_party/blink/renderer/devtools/front_end/timeline/UIDevtoolsUtils.js b/third_party/blink/renderer/devtools/front_end/timeline/UIDevtoolsUtils.js index 9fc9ae96..f9f7214 100644 --- a/third_party/blink/renderer/devtools/front_end/timeline/UIDevtoolsUtils.js +++ b/third_party/blink/renderer/devtools/front_end/timeline/UIDevtoolsUtils.js
@@ -36,7 +36,7 @@ * @return {boolean} */ static isUiDevTools() { - return Runtime.queryParam('uiDevTools') === 'true'; + return Root.Runtime.queryParam('uiDevTools') === 'true'; } /**
diff --git a/third_party/blink/renderer/devtools/tests/front_end/common/SegmentedRange.ts b/third_party/blink/renderer/devtools/tests/front_end/common/SegmentedRange.ts new file mode 100644 index 0000000..ce0f789f7 --- /dev/null +++ b/third_party/blink/renderer/devtools/tests/front_end/common/SegmentedRange.ts
@@ -0,0 +1,180 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +const { assert } = chai; + +import { default as SegmentedRange, Segment } from '../../../front_end/common/SegmentedRange.js'; + +describe('Segment', () => { + it('calculates intersections', () => { + const segmentA = new Segment(1, 2, 'A'); + const segmentB = new Segment(1.5, 2.5, 'B'); + const segmentC = new Segment(3, 5, 'C'); + + assert.isTrue(segmentA.intersects(segmentB)); + assert.isFalse(segmentA.intersects(segmentC)); + }); + + it('throws for invalid segments', () => { + assert.throws(() => new Segment(3, 2, 'V')); + }); +}); + +describe('SegmentedRange', () => { + let segmentedRange: SegmentedRange; + + function mergeSegments(first, second) { + const inOrder = first.end >= second.begin; + const matchingData = first.data === second.data; + return inOrder && matchingData ? first : null; + } + + beforeEach(() => { + segmentedRange = new SegmentedRange(mergeSegments); + }); + + it('handles single ranges', () => { + segmentedRange.append(new Segment(0, 1, 'A')); + + assert.deepEqual(segmentedRange.segments(), [{ begin: 0, end: 1, data: 'A' }]); + }); + + it('handles two adjacent ranges', () => { + const segmentA = new Segment(1, 2, 'A'); + const segmentB = new Segment(2, 3, 'B'); + + segmentedRange.append(segmentA); + segmentedRange.append(segmentB); + + assert.deepEqual(segmentedRange.segments(), [{ begin: 1, end: 2, data: 'A' }, { begin: 2, end: 3, data: 'B' }]); + }); + + it('handles two overlapping mergeable ranges', () => { + const segmentA = new Segment(1, 2, 'A'); + const segmentB = new Segment(1.5, 3, 'A'); + + segmentedRange.append(segmentA); + segmentedRange.append(segmentB); + + assert.deepEqual(segmentedRange.segments(), [{ begin: 1, end: 3, data: 'A' }]); + }); + + it('handles multiple overlapping mergeable ranges', () => { + const segmentA = new Segment(1, 2, 'A'); + const segmentB = new Segment(3, 5, 'A'); + const segmentC = new Segment(1.5, 3.5, 'A'); + + segmentedRange.append(segmentA); + segmentedRange.append(segmentB); + segmentedRange.append(segmentC); + + assert.deepEqual(segmentedRange.segments(), [{ begin: 1, end: 5, data: 'A' }]); + }); + + it('handles multiple overlapping non-mergeable ranges', () => { + const segmentA = new Segment(1, 2, 'A'); + const segmentB = new Segment(3, 5, 'A'); + const segmentC = new Segment(1.5, 3.5, 'B'); + + segmentedRange.append(segmentA); + segmentedRange.append(segmentB); + segmentedRange.append(segmentC); + + assert.deepEqual(segmentedRange.segments(), [{ begin: 1, end: 1.5, data: 'A' }, { begin: 1.5, end: 3.5, data: 'B' }, + { begin: 3.5, end: 5, data: 'A' }]); + }); + + it('handles two overlapping non-mergeable ranges', () => { + const segmentA = new Segment(1, 2, 'A'); + const segmentB = new Segment(1.5, 3, 'B'); + + segmentedRange.append(segmentA); + segmentedRange.append(segmentB); + + assert.deepEqual(segmentedRange.segments(), [{ begin: 1, end: 1.5, data: 'A' }, { begin: 1.5, end: 3, data: 'B' }]); + }); + + it('handles nested, mergeable ranges', () => { + const segmentA = new Segment(0, 4, 'A'); + const segmentB = new Segment(2, 3, 'A'); + + segmentedRange.append(segmentA); + segmentedRange.append(segmentB); + + assert.deepEqual(segmentedRange.segments(), [{ begin: 0, end: 4, data: 'A' }]); + }); + + it('handles nested, non-mergeable ranges', () => { + const segmentA = new Segment(0, 4, 'A'); + const segmentB = new Segment(2, 3, 'B'); + + segmentedRange.append(segmentA); + segmentedRange.append(segmentB); + + assert.deepEqual(segmentedRange.segments(), [{ begin: 0, end: 2, data: 'A' }, { begin: 2, end: 3, data: 'B' }, + { begin: 3, end: 4, data: 'A' }]); + }); + + it('handles out-of-order, mergeable ranges', () => { + const segmentA = new Segment(0, 2, 'A'); + const segmentB = new Segment(3, 5, 'A'); + const segmentC = new Segment(2, 3, 'A'); + + segmentedRange.append(segmentA); + segmentedRange.append(segmentB); + segmentedRange.append(segmentC); + + assert.deepEqual(segmentedRange.segments(), [{ begin: 0, end: 5, data: 'A' }]); + }); + + it('handles out-of-order, non-mergeable ranges', () => { + const segmentA = new Segment(0, 2, 'A'); + const segmentB = new Segment(3, 5, 'A'); + const segmentC = new Segment(2, 3, 'B'); + + segmentedRange.append(segmentA); + segmentedRange.append(segmentB); + segmentedRange.append(segmentC); + + assert.deepEqual(segmentedRange.segments(), [{ begin: 0, end: 2, data: 'A' }, { begin: 2, end: 3, data: 'B' }, + { begin: 3, end: 5, data: 'A' }]); + }); + + it('handles one segment consuming many mergeable ranges', () => { + const segmentA = new Segment(0, 1, 'A'); + const segmentB = new Segment(2, 3, 'A'); + const segmentC = new Segment(4, 5, 'A'); + const segmentD = new Segment(6, 7, 'A'); + + // E merges A through D. + const segmentE = new Segment(2, 6, 'A'); + + segmentedRange.append(segmentA); + segmentedRange.append(segmentB); + segmentedRange.append(segmentC); + segmentedRange.append(segmentD); + segmentedRange.append(segmentE); + + assert.deepEqual(segmentedRange.segments(), [{ begin: 0, end: 1, data: 'A' }, { begin: 2, end: 7, data: 'A' }]); + }); + + it('handles one segment consuming many non-mergeable ranges', () => { + const segmentA = new Segment(0, 1, 'A'); + const segmentB = new Segment(2, 3, 'A'); + const segmentC = new Segment(4, 5, 'A'); + const segmentD = new Segment(6, 7, 'A'); + + // E merges A through D. + const segmentE = new Segment(2, 6, 'B'); + + segmentedRange.append(segmentA); + segmentedRange.append(segmentB); + segmentedRange.append(segmentC); + segmentedRange.append(segmentD); + segmentedRange.append(segmentE); + + assert.deepEqual(segmentedRange.segments(), [{ begin: 0, end: 1, data: 'A' }, { begin: 2, end: 6, data: 'B' }, + { begin: 6, end: 7, data: 'A' }]); + }); +});
diff --git a/third_party/blink/renderer/modules/modules_idl_files.gni b/third_party/blink/renderer/modules/modules_idl_files.gni index c3425692..b95055b 100644 --- a/third_party/blink/renderer/modules/modules_idl_files.gni +++ b/third_party/blink/renderer/modules/modules_idl_files.gni
@@ -854,7 +854,7 @@ "webgpu/gpu_fence_descriptor.idl", "webgpu/gpu_limits.idl", "webgpu/gpu_object_descriptor_base.idl", - "webgpu/gpu_origin_3d.idl", + "webgpu/gpu_origin_3d_dict.idl", "webgpu/gpu_pipeline_descriptor_base.idl", "webgpu/gpu_pipeline_layout_descriptor.idl", "webgpu/gpu_pipeline_stage_descriptor.idl",
diff --git a/third_party/blink/renderer/modules/speech/speech_recognition.cc b/third_party/blink/renderer/modules/speech/speech_recognition.cc index 013294d..b1b94b6 100644 --- a/third_party/blink/renderer/modules/speech/speech_recognition.cc +++ b/third_party/blink/renderer/modules/speech/speech_recognition.cc
@@ -60,9 +60,8 @@ GetExecutionContext()->GetTaskRunner(blink::TaskType::kMiscPlatformAPI); mojo::PendingRemote<mojom::blink::SpeechRecognitionSessionClient> session_client; - binding_.Bind(session_client.InitWithNewPipeAndPassReceiver(), - GetExecutionContext()->GetInterfaceInvalidator(), task_runner); - binding_.set_connection_error_handler(WTF::Bind( + receiver_.Bind(session_client.InitWithNewPipeAndPassReceiver(), task_runner); + receiver_.set_disconnect_handler(WTF::Bind( &SpeechRecognition::OnConnectionError, WrapWeakPersistent(this))); mojo::PendingReceiver<mojom::blink::SpeechRecognitionSession> @@ -175,7 +174,7 @@ started_ = false; stopping_ = false; session_.reset(); - binding_.Close(); + receiver_.reset(); DispatchEvent(*Event::Create(event_type_names::kEnd)); } @@ -189,6 +188,7 @@ void SpeechRecognition::ContextDestroyed(ExecutionContext*) { controller_ = nullptr; + receiver_.reset(); } bool SpeechRecognition::HasPendingActivity() const { @@ -222,7 +222,7 @@ controller_(SpeechRecognitionController::From(frame)), started_(false), stopping_(false), - binding_(this) {} + receiver_(this) {} SpeechRecognition::~SpeechRecognition() = default;
diff --git a/third_party/blink/renderer/modules/speech/speech_recognition.h b/third_party/blink/renderer/modules/speech/speech_recognition.h index fb936e1..46e890c 100644 --- a/third_party/blink/renderer/modules/speech/speech_recognition.h +++ b/third_party/blink/renderer/modules/speech/speech_recognition.h
@@ -26,6 +26,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_SPEECH_SPEECH_RECOGNITION_H_ #define THIRD_PARTY_BLINK_RENDERER_MODULES_SPEECH_SPEECH_RECOGNITION_H_ +#include "mojo/public/cpp/bindings/receiver.h" #include "third_party/blink/public/mojom/speech/speech_recognizer.mojom-blink.h" #include "third_party/blink/public/platform/web_private_ptr.h" #include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h" @@ -36,7 +37,6 @@ #include "third_party/blink/renderer/modules/speech/speech_grammar_list.h" #include "third_party/blink/renderer/modules/speech/speech_recognition_result.h" #include "third_party/blink/renderer/platform/heap/handle.h" -#include "third_party/blink/renderer/platform/mojo/revocable_binding.h" #include "third_party/blink/renderer/platform/mojo/revocable_interface_ptr.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" @@ -135,7 +135,7 @@ bool started_; bool stopping_; HeapVector<Member<SpeechRecognitionResult>> final_results_; - RevocableBinding<mojom::blink::SpeechRecognitionSessionClient> binding_; + mojo::Receiver<mojom::blink::SpeechRecognitionSessionClient> receiver_; RevocableInterfacePtr<mojom::blink::SpeechRecognitionSession> session_; };
diff --git a/third_party/blink/renderer/modules/webgpu/dawn_conversions.cc b/third_party/blink/renderer/modules/webgpu/dawn_conversions.cc index 804bd054..96aa518 100644 --- a/third_party/blink/renderer/modules/webgpu/dawn_conversions.cc +++ b/third_party/blink/renderer/modules/webgpu/dawn_conversions.cc
@@ -8,7 +8,7 @@ #include "third_party/blink/renderer/bindings/modules/v8/double_sequence_or_gpu_color_dict.h" #include "third_party/blink/renderer/bindings/modules/v8/unsigned_long_sequence_or_gpu_extent_3d_dict.h" -#include "third_party/blink/renderer/modules/webgpu/gpu_origin_3d.h" +#include "third_party/blink/renderer/bindings/modules/v8/unsigned_long_sequence_or_gpu_origin_3d_dict.h" #include "third_party/blink/renderer/modules/webgpu/gpu_pipeline_stage_descriptor.h" #include "third_party/blink/renderer/modules/webgpu/gpu_shader_module.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" @@ -767,13 +767,30 @@ return dawn_extent; } -DawnOrigin3D AsDawnType(const GPUOrigin3D* webgpu_origin) { +DawnOrigin3D AsDawnType( + const UnsignedLongSequenceOrGPUOrigin3DDict* webgpu_origin) { DCHECK(webgpu_origin); DawnOrigin3D dawn_origin = {}; - dawn_origin.x = webgpu_origin->x(); - dawn_origin.y = webgpu_origin->y(); - dawn_origin.z = webgpu_origin->z(); + + if (webgpu_origin->IsUnsignedLongSequence()) { + const Vector<uint32_t>& webgpu_origin_sequence = + webgpu_origin->GetAsUnsignedLongSequence(); + DCHECK_EQ(webgpu_origin_sequence.size(), 3UL); + dawn_origin.x = webgpu_origin_sequence[0]; + dawn_origin.y = webgpu_origin_sequence[1]; + dawn_origin.z = webgpu_origin_sequence[2]; + + } else if (webgpu_origin->IsGPUOrigin3DDict()) { + const GPUOrigin3DDict* webgpu_origin_3d_dict = + webgpu_origin->GetAsGPUOrigin3DDict(); + dawn_origin.x = webgpu_origin_3d_dict->x(); + dawn_origin.y = webgpu_origin_3d_dict->y(); + dawn_origin.z = webgpu_origin_3d_dict->z(); + + } else { + NOTREACHED(); + } return dawn_origin; }
diff --git a/third_party/blink/renderer/modules/webgpu/dawn_conversions.h b/third_party/blink/renderer/modules/webgpu/dawn_conversions.h index d1600a8..bffacb6 100644 --- a/third_party/blink/renderer/modules/webgpu/dawn_conversions.h +++ b/third_party/blink/renderer/modules/webgpu/dawn_conversions.h
@@ -21,9 +21,9 @@ class DoubleSequenceOrGPUColorDict; class GPUColorDict; -class GPUOrigin3D; class GPUPipelineStageDescriptor; class UnsignedLongSequenceOrGPUExtent3DDict; +class UnsignedLongSequenceOrGPUOrigin3DDict; // Convert WebGPU bitfield values to Dawn enums. These have the same value. template <typename DawnEnum> @@ -42,7 +42,7 @@ DawnColor AsDawnType(const GPUColorDict*); DawnColor AsDawnType(const DoubleSequenceOrGPUColorDict*); DawnExtent3D AsDawnType(const UnsignedLongSequenceOrGPUExtent3DDict*); -DawnOrigin3D AsDawnType(const GPUOrigin3D*); +DawnOrigin3D AsDawnType(const UnsignedLongSequenceOrGPUOrigin3DDict*); using OwnedPipelineStageDescriptor = std::tuple<DawnPipelineStageDescriptor, std::unique_ptr<char[]>>;
diff --git a/third_party/blink/renderer/modules/webgpu/gpu.idl b/third_party/blink/renderer/modules/webgpu/gpu.idl index c2cf76db..ac2e51f 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu.idl +++ b/third_party/blink/renderer/modules/webgpu/gpu.idl
@@ -7,6 +7,7 @@ typedef unsigned long long GPUBufferSize; typedef (sequence<double> or GPUColorDict) GPUColor; typedef (sequence<unsigned long> or GPUExtent3DDict) GPUExtent3D; +typedef (sequence<unsigned long> or GPUOrigin3DDict) GPUOrigin3D; [ RuntimeEnabled=WebGPU
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.cc b/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.cc index b783cb42..4bbe51b 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.cc +++ b/third_party/blink/renderer/modules/webgpu/gpu_command_encoder.cc
@@ -6,6 +6,7 @@ #include "third_party/blink/renderer/bindings/modules/v8/double_sequence_or_gpu_color_dict.h" #include "third_party/blink/renderer/bindings/modules/v8/unsigned_long_sequence_or_gpu_extent_3d_dict.h" +#include "third_party/blink/renderer/bindings/modules/v8/unsigned_long_sequence_or_gpu_origin_3d_dict.h" #include "third_party/blink/renderer/modules/webgpu/dawn_conversions.h" #include "third_party/blink/renderer/modules/webgpu/gpu_buffer.h" #include "third_party/blink/renderer/modules/webgpu/gpu_buffer_copy_view.h" @@ -15,7 +16,6 @@ #include "third_party/blink/renderer/modules/webgpu/gpu_compute_pass_descriptor.h" #include "third_party/blink/renderer/modules/webgpu/gpu_compute_pass_encoder.h" #include "third_party/blink/renderer/modules/webgpu/gpu_device.h" -#include "third_party/blink/renderer/modules/webgpu/gpu_origin_3d.h" #include "third_party/blink/renderer/modules/webgpu/gpu_render_pass_color_attachment_descriptor.h" #include "third_party/blink/renderer/modules/webgpu/gpu_render_pass_depth_stencil_attachment_descriptor.h" #include "third_party/blink/renderer/modules/webgpu/gpu_render_pass_descriptor.h" @@ -36,6 +36,23 @@ return true; } +bool ValidateTextureCopyView(GPUTextureCopyView* texture_copy_view, + ExceptionState& exception_state) { + DCHECK(texture_copy_view); + + if (texture_copy_view->hasOrigin()) { + const UnsignedLongSequenceOrGPUOrigin3DDict origin = + texture_copy_view->origin(); + if (origin.IsUnsignedLongSequence() && + origin.GetAsUnsignedLongSequence().size() != 3) { + exception_state.ThrowRangeError( + "texture copy view origin length must be 3"); + return false; + } + } + return true; +} + DawnRenderPassColorAttachmentDescriptor AsDawnType( const GPURenderPassColorAttachmentDescriptor* webgpu_desc) { DCHECK(webgpu_desc); @@ -139,7 +156,11 @@ dawn_view.texture = webgpu_view->texture()->GetHandle(); dawn_view.mipLevel = webgpu_view->mipLevel(); dawn_view.arrayLayer = webgpu_view->arrayLayer(); - dawn_view.origin = AsDawnType(webgpu_view->origin()); + if (webgpu_view->hasOrigin()) { + dawn_view.origin = AsDawnType(&webgpu_view->origin()); + } else { + dawn_view.origin = DawnOrigin3D{}; + } return dawn_view; } @@ -262,7 +283,8 @@ GPUTextureCopyView* destination, UnsignedLongSequenceOrGPUExtent3DDict& copy_size, ExceptionState& exception_state) { - if (!ValidateCopySize(copy_size, exception_state)) { + if (!ValidateCopySize(copy_size, exception_state) || + !ValidateTextureCopyView(destination, exception_state)) { return; } @@ -279,7 +301,8 @@ GPUBufferCopyView* destination, UnsignedLongSequenceOrGPUExtent3DDict& copy_size, ExceptionState& exception_state) { - if (!ValidateCopySize(copy_size, exception_state)) { + if (!ValidateCopySize(copy_size, exception_state) || + !ValidateTextureCopyView(source, exception_state)) { return; } @@ -296,7 +319,9 @@ GPUTextureCopyView* destination, UnsignedLongSequenceOrGPUExtent3DDict& copy_size, ExceptionState& exception_state) { - if (!ValidateCopySize(copy_size, exception_state)) { + if (!ValidateCopySize(copy_size, exception_state) || + !ValidateTextureCopyView(source, exception_state) || + !ValidateTextureCopyView(destination, exception_state)) { return; }
diff --git a/third_party/blink/renderer/modules/webgpu/gpu_origin_3d.idl b/third_party/blink/renderer/modules/webgpu/gpu_origin_3d_dict.idl similarity index 90% rename from third_party/blink/renderer/modules/webgpu/gpu_origin_3d.idl rename to third_party/blink/renderer/modules/webgpu/gpu_origin_3d_dict.idl index fbfb79b..741be63 100644 --- a/third_party/blink/renderer/modules/webgpu/gpu_origin_3d.idl +++ b/third_party/blink/renderer/modules/webgpu/gpu_origin_3d_dict.idl
@@ -4,7 +4,7 @@ // https://gpuweb.github.io/gpuweb/ -dictionary GPUOrigin3D { +dictionary GPUOrigin3DDict { unsigned long x = 0; unsigned long y = 0; unsigned long z = 0;
diff --git a/third_party/blink/renderer/modules/websockets/mock_websocket_channel.h b/third_party/blink/renderer/modules/websockets/mock_websocket_channel.h index 3c9e0aa..de129bd 100644 --- a/third_party/blink/renderer/modules/websockets/mock_websocket_channel.h +++ b/third_party/blink/renderer/modules/websockets/mock_websocket_channel.h
@@ -52,6 +52,7 @@ FailMock(reason, level, location.get()); } MOCK_METHOD0(Disconnect, void()); + MOCK_METHOD0(CancelHandshake, void()); MOCK_METHOD0(ApplyBackpressure, void()); MOCK_METHOD0(RemoveBackpressure, void()); };
diff --git a/third_party/blink/renderer/modules/websockets/websocket_channel.h b/third_party/blink/renderer/modules/websockets/websocket_channel.h index d2f1df6a..5b315322 100644 --- a/third_party/blink/renderer/modules/websockets/websocket_channel.h +++ b/third_party/blink/renderer/modules/websockets/websocket_channel.h
@@ -102,6 +102,10 @@ // Do not call any methods after calling this method. virtual void Disconnect() = 0; // Will suppress didClose(). + // Cancel the WebSocket handshake. Does nothing if the connection is already + // established. Do not call any other methods after this one. + virtual void CancelHandshake() = 0; + // Clients can call ApplyBackpressure() to indicate that they want to stop // receiving new messages. WebSocketChannelClient::DidReceive*Message() may // still be called after this, until existing flow control quota is used up.
diff --git a/third_party/blink/renderer/modules/websockets/websocket_channel_impl.cc b/third_party/blink/renderer/modules/websockets/websocket_channel_impl.cc index 4d58e824..da930a59 100644 --- a/third_party/blink/renderer/modules/websockets/websocket_channel_impl.cc +++ b/third_party/blink/renderer/modules/websockets/websocket_channel_impl.cc
@@ -417,6 +417,18 @@ Dispose(); } +void WebSocketChannelImpl::CancelHandshake() { + NETWORK_DVLOG(1) << this << " CancelHandshake()"; + if (GetState() != State::kConnecting) + return; + + // This may still disconnect even if the handshake is complete if we haven't + // got the message yet. + // TODO(ricea): Plumb it through to the network stack to fix the race + // condition. + Disconnect(); +} + void WebSocketChannelImpl::ApplyBackpressure() { backpressure_ = true; }
diff --git a/third_party/blink/renderer/modules/websockets/websocket_channel_impl.h b/third_party/blink/renderer/modules/websockets/websocket_channel_impl.h index 317ddb5a..4d5de65 100644 --- a/third_party/blink/renderer/modules/websockets/websocket_channel_impl.h +++ b/third_party/blink/renderer/modules/websockets/websocket_channel_impl.h
@@ -107,6 +107,7 @@ mojom::ConsoleMessageLevel, std::unique_ptr<SourceLocation>) override; void Disconnect() override; + void CancelHandshake() override; void ApplyBackpressure() override; void RemoveBackpressure() override;
diff --git a/third_party/blink/renderer/modules/websockets/websocket_stream.cc b/third_party/blink/renderer/modules/websockets/websocket_stream.cc index 49252d33..053fb14 100644 --- a/third_party/blink/renderer/modules/websockets/websocket_stream.cc +++ b/third_party/blink/renderer/modules/websockets/websocket_stream.cc
@@ -14,6 +14,7 @@ #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h" #include "third_party/blink/renderer/bindings/core/v8/v8_throw_dom_exception.h" #include "third_party/blink/renderer/bindings/modules/v8/v8_websocket_close_info.h" +#include "third_party/blink/renderer/core/dom/abort_signal.h" #include "third_party/blink/renderer/core/execution_context/execution_context.h" #include "third_party/blink/renderer/core/streams/readable_stream.h" #include "third_party/blink/renderer/core/streams/readable_stream_default_controller_interface.h" @@ -598,7 +599,21 @@ DVLOG(1) << "WebSocketStream " << this << " Connect() url=" << url << " options=" << options; - // TODO(ricea): Support AbortSignal. + auto* signal = options->signal(); + if (signal && signal->aborted()) { + auto exception = V8ThrowDOMException::CreateOrEmpty( + script_state->GetIsolate(), DOMExceptionCode::kAbortError, + "WebSocket handshake was aborted"); + connection_resolver_->Reject(exception); + closed_resolver_->Reject(exception); + return; + } + + if (signal) { + signal->AddAlgorithm( + WTF::Bind(&WebSocketStream::OnAbort, WrapWeakPersistent(this))); + } + auto result = common_.Connect( ExecutionContext::From(script_state), url, options->hasProtocols() ? options->protocols() : Vector<String>(), @@ -680,6 +695,22 @@ "A network error occurred"); } +void WebSocketStream::OnAbort() { + DVLOG(1) << "WebSocketStream " << this << " OnAbort()"; + + if (was_ever_connected_ || !channel_) + return; + + channel_->CancelHandshake(); + channel_ = nullptr; + + auto exception = V8ThrowDOMException::CreateOrEmpty( + script_state_->GetIsolate(), DOMExceptionCode::kAbortError, + "WebSocket handshake was aborted"); + connection_resolver_->Reject(exception); + closed_resolver_->Reject(exception); +} + WebSocketCloseInfo* WebSocketStream::MakeCloseInfo(uint16_t code, const String& reason) { DVLOG(1) << "WebSocketStream MakeCloseInfo() code=" << code
diff --git a/third_party/blink/renderer/modules/websockets/websocket_stream.h b/third_party/blink/renderer/modules/websockets/websocket_stream.h index 1438614e..df0ecc29 100644 --- a/third_party/blink/renderer/modules/websockets/websocket_stream.h +++ b/third_party/blink/renderer/modules/websockets/websocket_stream.h
@@ -108,6 +108,7 @@ void CloseInternal(int code, const String& reason, ExceptionState& exception_state); + void OnAbort(); v8::Local<v8::Value> CreateNetworkErrorDOMException(); static WebSocketCloseInfo* MakeCloseInfo(uint16_t code, const String& reason);
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn index c103b596..854d6c6 100644 --- a/third_party/blink/renderer/platform/BUILD.gn +++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -1227,9 +1227,7 @@ "mojo/interface_invalidator.cc", "mojo/interface_invalidator.h", "mojo/mojo_helper.h", - "mojo/revocable_binding.h", "mojo/revocable_interface_ptr.h", - "mojo/revocable_strong_binding.h", "mojo/string16_mojom_traits.cc", "mojo/string16_mojom_traits.h", "p2p/empty_network_manager.cc",
diff --git a/third_party/blink/renderer/platform/heap/BUILD.gn b/third_party/blink/renderer/platform/heap/BUILD.gn index 01955c9..d696d261 100644 --- a/third_party/blink/renderer/platform/heap/BUILD.gn +++ b/third_party/blink/renderer/platform/heap/BUILD.gn
@@ -30,6 +30,7 @@ "blink_gc_memory_dump_provider.h", "cancelable_task_scheduler.cc", "cancelable_task_scheduler.h", + "disallow_new_wrapper.h", "finalizer_traits.h", "garbage_collected.h", "gc_info.cc",
diff --git a/third_party/blink/renderer/platform/heap/disallow_new_wrapper.h b/third_party/blink/renderer/platform/heap/disallow_new_wrapper.h new file mode 100644 index 0000000..118cadb --- /dev/null +++ b/third_party/blink/renderer/platform/heap/disallow_new_wrapper.h
@@ -0,0 +1,39 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_DISALLOW_NEW_WRAPPER_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_DISALLOW_NEW_WRAPPER_H_ + +#include "third_party/blink/renderer/platform/heap/garbage_collected.h" + +namespace blink { + +// DisallowNewWrapper wraps a disallow new type in a GarbageCollected class. +template <typename T> +class DisallowNewWrapper : public GarbageCollected<DisallowNewWrapper<T>> { + public: + explicit DisallowNewWrapper(const T& value) : value_(value) { + static_assert(WTF::IsDisallowNew<T>::value, + "T needs to be a disallow new type"); + static_assert(WTF::IsTraceable<T>::value, "T needs to be traceable"); + } + + const T& Value() const { return value_; } + + void Trace(Visitor* visitor) { visitor->Trace(value_); } + + private: + T value_; +}; + +// Wraps a disallow new type in a GarbageCollected class, making it possible to +// be referenced off heap from a Persistent. +template <typename T> +DisallowNewWrapper<T>* WrapDisallowNew(const T& value) { + return MakeGarbageCollected<DisallowNewWrapper<T>>(value); +} + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_HEAP_DISALLOW_NEW_WRAPPER_H_
diff --git a/third_party/blink/renderer/platform/mojo/interface_invalidator_test.cc b/third_party/blink/renderer/platform/mojo/interface_invalidator_test.cc index 901b04af..4f6d275 100644 --- a/third_party/blink/renderer/platform/mojo/interface_invalidator_test.cc +++ b/third_party/blink/renderer/platform/mojo/interface_invalidator_test.cc
@@ -16,9 +16,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h" #include "third_party/blink/renderer/platform/mojo/interface_invalidator.h" -#include "third_party/blink/renderer/platform/mojo/revocable_binding.h" #include "third_party/blink/renderer/platform/mojo/revocable_interface_ptr.h" -#include "third_party/blink/renderer/platform/mojo/revocable_strong_binding.h" namespace blink { @@ -341,279 +339,6 @@ EXPECT_FALSE(result); } -class RevocablePingServiceImpl : public PingServiceImplBase { - public: - RevocablePingServiceImpl( - mojo::InterfaceRequest<mojo::test::blink::PingService> request, - InterfaceInvalidator* invalidator, - bool send_response = true) - : PingServiceImplBase(send_response), - error_handler_called_(false), - binding_(this, std::move(request), invalidator) { - binding_.set_connection_error_handler( - base::BindOnce(DoSetFlag, &error_handler_called_)); - } - - ~RevocablePingServiceImpl() override {} - - bool error_handler_called() { return error_handler_called_; } - - RevocableBinding<mojo::test::blink::PingService>* binding() { - return &binding_; - } - - private: - bool error_handler_called_; - RevocableBinding<mojo::test::blink::PingService> binding_; - - DISALLOW_COPY_AND_ASSIGN(RevocablePingServiceImpl); -}; - -TEST_F(InterfaceInvalidatorTest, DestroyInvalidatesRevocableBinding) { - mojo::test::blink::PingServicePtr ptr; - auto invalidator = std::make_unique<InterfaceInvalidator>(); - RevocablePingServiceImpl impl(MakeRequest(&ptr), invalidator.get()); - - bool ping_called = false; - ptr->Ping(base::BindRepeating(DoSetFlag, &ping_called)); - base::RunLoop().RunUntilIdle(); - ASSERT_TRUE(ping_called); - - bool error_handler_called = false; - ptr.set_connection_error_handler( - base::BindOnce(DoSetFlag, &error_handler_called)); - - invalidator.reset(); - impl.set_ping_handler(base::BindRepeating([] { FAIL(); })); - ptr->Ping(base::BindRepeating([] { FAIL(); })); - base::RunLoop().RunUntilIdle(); - - EXPECT_TRUE(error_handler_called); - EXPECT_TRUE(impl.error_handler_called()); - EXPECT_TRUE(ptr.encountered_error()); - EXPECT_TRUE(ptr); - EXPECT_TRUE(*impl.binding()); -} - -TEST_F(InterfaceInvalidatorTest, InvalidateBindingBeforeResponse) { - mojo::test::blink::PingServicePtr ptr; - auto invalidator = std::make_unique<InterfaceInvalidator>(); - RevocablePingServiceImpl impl(MakeRequest(&ptr), invalidator.get()); - impl.set_ping_handler( - base::BindLambdaForTesting([&] { invalidator.reset(); })); - - bool ptr_error_handler_called = false; - ptr.set_connection_error_handler( - base::BindOnce(DoSetFlag, &ptr_error_handler_called)); - ptr->Ping(base::BindRepeating([] { FAIL(); })); - base::RunLoop().RunUntilIdle(); - - EXPECT_TRUE(ptr_error_handler_called); - EXPECT_TRUE(impl.error_handler_called()); - EXPECT_TRUE(*impl.binding()); -} - -TEST_F(InterfaceInvalidatorTest, InvalidateBindingAfterResponse) { - mojo::test::blink::PingServicePtr ptr; - auto invalidator = std::make_unique<InterfaceInvalidator>(); - RevocablePingServiceImpl impl(MakeRequest(&ptr), invalidator.get()); - impl.set_post_ping_handler(base::BindLambdaForTesting([&] { - invalidator.reset(); - impl.set_ping_handler(base::BindRepeating([] { FAIL(); })); - })); - - bool ptr_error_handler_called = false; - ptr.set_connection_error_handler( - base::BindOnce(DoSetFlag, &ptr_error_handler_called)); - bool ping_called = false; - ptr->Ping(base::BindRepeating(DoSetFlag, &ping_called)); - ptr->Ping(base::BindRepeating([] { FAIL(); })); - base::RunLoop().RunUntilIdle(); - - EXPECT_TRUE(ping_called); - EXPECT_TRUE(ptr_error_handler_called); - EXPECT_TRUE(impl.error_handler_called()); - EXPECT_TRUE(*impl.binding()); -} - -TEST_F(InterfaceInvalidatorTest, UnbindThenInvalidate) { - mojo::test::blink::PingServicePtr ptr; - auto invalidator = std::make_unique<InterfaceInvalidator>(); - RevocablePingServiceImpl impl(MakeRequest(&ptr), invalidator.get()); - ptr.set_connection_error_handler(base::BindOnce([] { FAIL(); })); - - PingServiceImpl impl2(impl.binding()->Unbind()); - invalidator.reset(); - bool ping_called = false; - ptr->Ping(base::BindRepeating(DoSetFlag, &ping_called)); - base::RunLoop().RunUntilIdle(); - - EXPECT_TRUE(ping_called); - EXPECT_FALSE(impl.error_handler_called()); -} - -TEST_F(InterfaceInvalidatorTest, UnbindInvalidatedRevocableBinding) { - mojo::test::blink::PingServicePtr ptr; - auto invalidator = std::make_unique<InterfaceInvalidator>(); - RevocablePingServiceImpl impl(MakeRequest(&ptr), invalidator.get()); - - bool ptr_error_handler_called = false; - ptr.set_connection_error_handler( - base::BindOnce(DoSetFlag, &ptr_error_handler_called)); - - invalidator.reset(); - base::RunLoop().RunUntilIdle(); - ASSERT_TRUE(ptr_error_handler_called); - ASSERT_TRUE(impl.error_handler_called()); - - PingServiceImpl impl2(impl.binding()->Unbind()); - impl2.set_ping_handler(base::BindRepeating([] { FAIL(); })); - ptr->Ping(base::BindRepeating([] { FAIL(); })); - base::RunLoop().RunUntilIdle(); - EXPECT_TRUE(impl2.error_handler_called()); -} - -TEST_F(InterfaceInvalidatorTest, UnbindBeforeConnectionErrorNotification) { - mojo::test::blink::PingServicePtr ptr; - auto invalidator = std::make_unique<InterfaceInvalidator>(); - RevocablePingServiceImpl impl(MakeRequest(&ptr), invalidator.get()); - - bool ptr_error_handler_called = false; - ptr.set_connection_error_handler( - base::BindOnce(DoSetFlag, &ptr_error_handler_called)); - - invalidator.reset(); - PingServiceImpl impl2(impl.binding()->Unbind()); - impl2.set_ping_handler(base::BindRepeating([] { FAIL(); })); - ptr->Ping(base::BindRepeating([] { FAIL(); })); - base::RunLoop().RunUntilIdle(); - - EXPECT_FALSE(impl.error_handler_called()); - EXPECT_TRUE(impl2.error_handler_called()); - EXPECT_TRUE(ptr_error_handler_called); -} - -TEST_F(InterfaceInvalidatorTest, InvalidateClosedRevocableBinding) { - mojo::test::blink::PingServicePtr ptr; - auto invalidator = std::make_unique<InterfaceInvalidator>(); - RevocablePingServiceImpl impl(MakeRequest(&ptr), invalidator.get()); - - impl.binding()->Close(); - invalidator.reset(); - base::RunLoop().RunUntilIdle(); - - EXPECT_FALSE(impl.error_handler_called()); - EXPECT_FALSE(*impl.binding()); -} - -TEST_F(InterfaceInvalidatorTest, CloseInvalidatedRevocableBinding) { - mojo::test::blink::PingServicePtr ptr; - auto invalidator = std::make_unique<InterfaceInvalidator>(); - RevocablePingServiceImpl impl(MakeRequest(&ptr), invalidator.get()); - - invalidator.reset(); - impl.binding()->Close(); - base::RunLoop().RunUntilIdle(); - - EXPECT_FALSE(impl.error_handler_called()); - EXPECT_FALSE(*impl.binding()); -} - -TEST_F(InterfaceInvalidatorTest, InvalidateErroredRevocableBinding) { - mojo::test::blink::PingServicePtr ptr; - auto invalidator = std::make_unique<InterfaceInvalidator>(); - RevocablePingServiceImpl impl(MakeRequest(&ptr), invalidator.get()); - - int called = 0; - impl.binding()->set_connection_error_handler( - base::BindLambdaForTesting([&] { called++; })); - - ptr.reset(); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(1, called); - invalidator.reset(); - base::RunLoop().RunUntilIdle(); - EXPECT_EQ(1, called); -} - -TEST_F(InterfaceInvalidatorTest, InvalidateWhileRevocableBindingPaused) { - mojo::test::blink::PingServicePtr ptr; - auto invalidator = std::make_unique<InterfaceInvalidator>(); - RevocablePingServiceImpl impl(MakeRequest(&ptr), invalidator.get()); - - impl.binding()->PauseIncomingMethodCallProcessing(); - invalidator.reset(); - base::RunLoop().RunUntilIdle(); - EXPECT_FALSE(impl.error_handler_called()); - impl.binding()->ResumeIncomingMethodCallProcessing(); - base::RunLoop().RunUntilIdle(); - EXPECT_TRUE(impl.error_handler_called()); -} - -TEST_F(InterfaceInvalidatorTest, InvalidateRevocableBindingDuringSyncIPC) { - mojo::test::blink::PingServicePtr ptr; - auto invalidator = std::make_unique<InterfaceInvalidator>(); - RevocablePingServiceImpl impl(MakeRequest(&ptr), invalidator.get()); - - impl.set_ping_handler( - base::BindLambdaForTesting([&] { invalidator.reset(); })); - bool result = ptr->Ping(); - EXPECT_FALSE(result); -} - -TEST_F(InterfaceInvalidatorTest, - InvalidateRevocableBindingDuringSyncIPCWithoutResponse) { - mojo::test::blink::PingServicePtr ptr; - auto invalidator = std::make_unique<InterfaceInvalidator>(); - RevocablePingServiceImpl impl(MakeRequest(&ptr), invalidator.get(), - false /* send_response */); - - impl.set_ping_handler( - base::BindLambdaForTesting([&] { invalidator.reset(); })); - bool result = ptr->Ping(); - EXPECT_FALSE(result); -} - -TEST_F(InterfaceInvalidatorTest, InvalidateStrongBinding) { - mojo::test::blink::PingServicePtr ptr; - auto invalidator = std::make_unique<InterfaceInvalidator>(); - auto impl_ptr = - MakeRevocableStrongBinding(std::make_unique<PingServiceImplBase>(), - MakeRequest(&ptr), invalidator.get()); - auto* impl = reinterpret_cast<PingServiceImplBase*>(impl_ptr->impl()); - - bool impl_called = false; - impl->set_ping_handler(base::BindRepeating(DoSetFlag, &impl_called)); - bool ping_called = false; - ptr->Ping(base::BindRepeating(DoSetFlag, &ping_called)); - base::RunLoop().RunUntilIdle(); - ASSERT_TRUE(impl_called); - ASSERT_TRUE(ping_called); - - impl->set_ping_handler(base::BindRepeating([] { FAIL(); })); - invalidator.reset(); - ptr->Ping(base::BindRepeating([] { FAIL(); })); - base::RunLoop().RunUntilIdle(); - - ASSERT_FALSE(impl_ptr); -} - -TEST_F(InterfaceInvalidatorTest, InvalidateStrongBindingAfterError) { - mojo::test::blink::PingServicePtr ptr; - auto invalidator = std::make_unique<InterfaceInvalidator>(); - auto impl_ptr = - MakeRevocableStrongBinding(std::make_unique<PingServiceImplBase>(), - MakeRequest(&ptr), invalidator.get()); - ptr.set_connection_error_handler(base::BindOnce([] { FAIL(); })); - - ptr.reset(); - base::RunLoop().RunUntilIdle(); - invalidator.reset(); - base::RunLoop().RunUntilIdle(); - - ASSERT_FALSE(impl_ptr); -} - } // namespace } // namespace blink
diff --git a/third_party/blink/renderer/platform/mojo/revocable_binding.h b/third_party/blink/renderer/platform/mojo/revocable_binding.h deleted file mode 100644 index a467727..0000000 --- a/third_party/blink/renderer/platform/mojo/revocable_binding.h +++ /dev/null
@@ -1,182 +0,0 @@ -// Copyright 2018 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_REVOCABLE_BINDING_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_REVOCABLE_BINDING_H_ - -#include <utility> - -#include "base/callback_forward.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/single_thread_task_runner.h" -#include "mojo/public/cpp/bindings/binding.h" -#include "mojo/public/cpp/bindings/connection_error_callback.h" -#include "mojo/public/cpp/bindings/interface_request.h" -#include "mojo/public/cpp/bindings/lib/binding_state.h" -#include "mojo/public/cpp/bindings/raw_ptr_impl_ref_traits.h" -#include "mojo/public/cpp/system/core.h" -#include "third_party/blink/renderer/platform/mojo/interface_invalidator.h" - -namespace blink { - -class MessageReceiver; - -// RevocableBinding is a wrapper around a Binding that has to be tied to an -// InterfaceInvalidator when bound to a message pipe. The underlying connection -// is automatically closed once the InterfaceInvalidator is destroyed, and the -// binding will behave as if its peer had closed the connection. This is useful -// for tying the lifetime of mojo interfaces to another object. -// -// TODO(austinct): Add set_connection_error_with_reason_handler(), -// CloseWithReason(), ReportBadMessage() and GetBadMessageCallback() methods if -// needed. Undesirable for now because of the std::string parameter. -template <typename Interface, - typename ImplRefTraits = mojo::RawPtrImplRefTraits<Interface>> -class RevocableBinding : public InterfaceInvalidator::Observer { - public: - using ImplPointerType = typename ImplRefTraits::PointerType; - - // Constructs an incomplete binding that will use the implementation |impl|. - // The binding may be completed with a subsequent call to the |Bind| method. - // Does not take ownership of |impl|, which must outlive the binding. - explicit RevocableBinding(ImplPointerType impl) : binding_(std::move(impl)) {} - - // Constructs a completed binding of |impl| to the message pipe endpoint in - // |request|, taking ownership of the endpoint. Does not take ownership of - // |impl|, which must outlive the binding. Ties the lifetime of the binding to - // |invalidator|. - RevocableBinding(ImplPointerType impl, - mojo::InterfaceRequest<Interface> request, - InterfaceInvalidator* invalidator, - scoped_refptr<base::SingleThreadTaskRunner> runner = nullptr) - : RevocableBinding(std::move(impl)) { - Bind(std::move(request), invalidator, std::move(runner)); - } - - // Tears down the binding, closing the message pipe and leaving the interface - // implementation unbound. - ~RevocableBinding() { SetInvalidator(nullptr); } - - // Completes a binding that was constructed with only an interface - // implementation by removing the message pipe endpoint from |request| and - // binding it to the previously specified implementation. Ties the lifetime of - // the binding to |invalidator|. - void Bind(mojo::InterfaceRequest<Interface> request, - InterfaceInvalidator* invalidator, - scoped_refptr<base::SingleThreadTaskRunner> runner = nullptr) { - DCHECK(invalidator); - binding_.Bind(std::move(request), std::move(runner)); - SetInvalidator(invalidator); - } - - // Adds a message filter to be notified of each incoming message before - // dispatch. If a filter returns |false| from Accept(), the message is not - // dispatched and the pipe is closed. Filters cannot be removed. - void AddFilter(std::unique_ptr<MessageReceiver> filter) { - binding_.AddFilter(std::move(filter)); - } - - // Whether there are any associated interfaces running on the pipe currently. - bool HasAssociatedInterfaces() const { - return binding_.HasAssociatedInterfaces(); - } - - // Stops processing incoming messages until - // ResumeIncomingMethodCallProcessing(). - // Outgoing messages are still sent. - // - // No errors are detected on the message pipe while paused. - // - // This method may only be called if the object has been bound to a message - // pipe and there are no associated interfaces running. - void PauseIncomingMethodCallProcessing() { - binding_.PauseIncomingMethodCallProcessing(); - } - void ResumeIncomingMethodCallProcessing() { - binding_.ResumeIncomingMethodCallProcessing(); - } - - // Closes the message pipe that was previously bound. Put this object into a - // state where it can be rebound to a new pipe. - void Close() { - SetInvalidator(nullptr); - binding_.Close(); - } - - // Unbinds the underlying pipe from this binding and returns it so it can be - // used in another context, such as on another sequence or with a different - // implementation. Put this object into a state where it can be rebound to a - // new pipe. - // - // This method may only be called if the object has been bound to a message - // pipe and there are no associated interfaces running. - // - // TODO(yzshen): For now, users need to make sure there is no one holding - // on to associated interface endpoint handles at both sides of the - // message pipe in order to call this method. We need a way to forcefully - // invalidate associated interface endpoint handles. - mojo::InterfaceRequest<Interface> Unbind() { - SetInvalidator(nullptr); - return binding_.Unbind(); - } - - // Sets an error handler that will be called if a connection error occurs on - // the bound message pipe. - // - // This method may only be called after this RevocableBinding has been bound - // to a message pipe. The error handler will be reset when this - // RevocableBinding is unbound, closed or invalidated. - void set_connection_error_handler(base::OnceClosure error_handler) { - binding_.set_connection_error_handler(std::move(error_handler)); - } - - // Returns the interface implementation that was previously specified. Caller - // does not take ownership. - Interface* impl() { return binding_.impl(); } - - // Indicates whether the binding has been completed (i.e., whether a message - // pipe has been bound to the implementation). - explicit operator bool() const { return static_cast<bool>(binding_); } - - // Sends a no-op message on the underlying message pipe and runs the current - // message loop until its response is received. This can be used in tests to - // verify that no message was sent on a message pipe in response to some - // stimulus. - void FlushForTesting() { binding_.FlushForTesting(); } - - private: - // InterfaceInvalidator::Observer - void OnInvalidate() override { - if (binding_) { - binding_.internal_state()->RaiseError(); - } - if (invalidator_) { - invalidator_->RemoveObserver(this); - } - invalidator_.reset(); - } - - // Replaces the existing invalidator with a new invalidator and changes the - // invalidator being observed. - void SetInvalidator(InterfaceInvalidator* invalidator) { - if (invalidator_) - invalidator_->RemoveObserver(this); - - invalidator_.reset(); - if (invalidator) { - invalidator_ = invalidator->GetWeakPtr(); - invalidator_->AddObserver(this); - } - } - - mojo::Binding<Interface, ImplRefTraits> binding_; - base::WeakPtr<InterfaceInvalidator> invalidator_; - - DISALLOW_COPY_AND_ASSIGN(RevocableBinding); -}; - -} // namespace blink - -#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_REVOCABLE_BINDING_H_
diff --git a/third_party/blink/renderer/platform/mojo/revocable_strong_binding.h b/third_party/blink/renderer/platform/mojo/revocable_strong_binding.h deleted file mode 100644 index 80286873..0000000 --- a/third_party/blink/renderer/platform/mojo/revocable_strong_binding.h +++ /dev/null
@@ -1,134 +0,0 @@ -// Copyright 2018 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_REVOCABLE_STRONG_BINDING_H_ -#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_REVOCABLE_STRONG_BINDING_H_ - -#include <memory> -#include <utility> - -#include "base/bind.h" -#include "base/callback.h" -#include "base/macros.h" -#include "base/memory/weak_ptr.h" -#include "mojo/public/cpp/bindings/connection_error_callback.h" -#include "mojo/public/cpp/bindings/filter_chain.h" -#include "mojo/public/cpp/bindings/interface_request.h" -#include "mojo/public/cpp/bindings/message_header_validator.h" -#include "mojo/public/cpp/system/core.h" -#include "third_party/blink/renderer/platform/mojo/revocable_binding.h" - -namespace blink { - -class InterfaceInvalidator; - -template <typename Interface> -class RevocableStrongBinding; - -template <typename Interface> -using RevocableStrongBindingPtr = - base::WeakPtr<RevocableStrongBinding<Interface>>; - -// This is a wrapper around a StrongBinding that binds it to an interface -// invalidator. When the invalidator is destroyed or a connection error is -// detected, the interface implementation is deleted. If the task runner that a -// RevocableStrongBinding is bound on is stopped, the connection error handler -// will not be invoked and the implementation will not be deleted. -// -// To use, call RevocableStrongBinding<T>::Create() (see below) or the helper -// MakeRevocableStrongBinding function: -// -// MakeRevocableStrongBinding(std::make_unique<FooImpl>(), -// std::move(foo_request)); -// -template <typename Interface> -class RevocableStrongBinding { - public: - // Create a new RevocableStrongBinding instance. The instance owns itself, - // cleaning up only in the event of a pipe connection error or invalidation. - // Returns a WeakPtr to the new RevocableStrongBinding instance. - static RevocableStrongBindingPtr<Interface> Create( - std::unique_ptr<Interface> impl, - mojo::InterfaceRequest<Interface> request, - InterfaceInvalidator* invalidator) { - RevocableStrongBinding* binding = new RevocableStrongBinding( - std::move(impl), std::move(request), invalidator); - return binding->weak_factory_.GetWeakPtr(); - } - - // Note: The error handler must not delete the interface implementation. - // - // This method may only be called after this RevocableStrongBinding has been - // bound to a message pipe. - void set_connection_error_handler(base::OnceClosure error_handler) { - DCHECK(binding_.is_bound()); - connection_error_handler_ = std::move(error_handler); - } - - // Stops processing incoming messages until - // ResumeIncomingMethodCallProcessing(). - // Outgoing messages are still sent. - // - // No errors are detected on the message pipe while paused. - // - // This method may only be called if the object has been bound to a message - // pipe and there are no associated interfaces running. - void PauseIncomingMethodCallProcessing() { - binding_.PauseIncomingMethodCallProcessing(); - } - void ResumeIncomingMethodCallProcessing() { - binding_.ResumeIncomingMethodCallProcessing(); - } - - // Forces the binding to close. This destroys the RevocableStrongBinding - // instance. - void Close() { delete this; } - - Interface* impl() { return impl_.get(); } - - // Sends a message on the underlying message pipe and runs the current - // message loop until its response is received. This can be used in tests to - // verify that no message was sent on a message pipe in response to some - // stimulus. - void FlushForTesting() { binding_.FlushForTesting(); } - - private: - RevocableStrongBinding(std::unique_ptr<Interface> impl, - mojo::InterfaceRequest<Interface> request, - InterfaceInvalidator* invalidator) - : impl_(std::move(impl)), - binding_(impl_.get(), std::move(request), invalidator) { - binding_.set_connection_error_handler(base::BindOnce( - &RevocableStrongBinding::OnConnectionError, base::Unretained(this))); - } - - ~RevocableStrongBinding() = default; - - void OnConnectionError() { - if (connection_error_handler_) { - std::move(connection_error_handler_).Run(); - } - Close(); - } - - std::unique_ptr<Interface> impl_; - base::OnceClosure connection_error_handler_; - RevocableBinding<Interface> binding_; - base::WeakPtrFactory<RevocableStrongBinding> weak_factory_{this}; - - DISALLOW_COPY_AND_ASSIGN(RevocableStrongBinding); -}; - -template <typename Interface, typename Impl> -RevocableStrongBindingPtr<Interface> MakeRevocableStrongBinding( - std::unique_ptr<Impl> impl, - mojo::InterfaceRequest<Interface> request, - InterfaceInvalidator* invalidator) { - return RevocableStrongBinding<Interface>::Create( - std::move(impl), std::move(request), invalidator); -} - -} // namespace blink - -#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MOJO_REVOCABLE_STRONG_BINDING_H_
diff --git a/third_party/blink/renderer/platform/wtf/functional.h b/third_party/blink/renderer/platform/wtf/functional.h index 5e4b373..51a0479 100644 --- a/third_party/blink/renderer/platform/wtf/functional.h +++ b/third_party/blink/renderer/platform/wtf/functional.h
@@ -209,6 +209,9 @@ "GCed types are forbidden as bound parameters."); static_assert(!WTF::IsStackAllocatedType<T>::value, "Stack allocated types are forbidden as bound parameters."); + static_assert( + !(WTF::IsDisallowNew<T>::value && WTF::IsTraceable<T>::value), + "Traceable disallow new types are forbidden as bound parameters."); }; template <typename Index, typename... Args>
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index 13c9b0f0..4ae8ec8 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -3388,11 +3388,11 @@ crbug.com/626703 external/wpt/css/css-writing-modes/text-combine-upright-digits-002-manual.html [ Skip ] crbug.com/626703 external/wpt/css/css-ui/webkit-appearance-button-bevel-001.html [ Failure ] crbug.com/626703 external/wpt/css/css-writing-modes/text-combine-upright-all-002-manual.html [ Skip ] -crbug.com/626703 virtual/streaming-preload/external/wpt/html/semantics/scripting-1/the-script-element/json-module/parse-error.html [ Timeout ] +crbug.com/626703 virtual/streaming-preload/external/wpt/html/semantics/scripting-1/the-script-element/json-module/parse-error.tentative.html [ Timeout ] crbug.com/626703 external/wpt/css/css-ui/webkit-appearance-auto-001.html [ Failure ] crbug.com/626703 external/wpt/web-animations/interfaces/Animation/persist.html [ Timeout ] crbug.com/626703 external/wpt/css/css-writing-modes/text-combine-upright-digits-004-manual.html [ Skip ] -crbug.com/626703 external/wpt/html/semantics/scripting-1/the-script-element/json-module/parse-error.html [ Timeout ] +crbug.com/626703 external/wpt/html/semantics/scripting-1/the-script-element/json-module/parse-error.tentative.html [ Timeout ] crbug.com/626703 [ Mac10.13 ] external/wpt/preload/preload-with-type.html [ Failure Timeout ] crbug.com/626703 [ Retina ] external/wpt/preload/preload-with-type.html [ Timeout ] crbug.com/626703 [ Mac10.13 ] external/wpt/preload/onload-event.html [ Failure Timeout ] @@ -5483,7 +5483,6 @@ crbug.com/923244 external/wpt/webrtc/RTCDTMFSender-ontonechange.https.html [ Failure Pass ] # WebRTC Plan B crbug.com/920979 virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-remote-track-mute.https.html [ Timeout ] -crbug.com/997201 virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https.html [ Pass Failure ] crbug.com/920979 virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCTrackEvent-fire.html [ Timeout ] # WebRTC codec tests - software H.264 not present on webkit bot family crbug.com/840659 external/wpt/webrtc/protocol/video-codecs.https.html [ Pass Failure ] @@ -5843,6 +5842,7 @@ # Allow failure until its appearance gets stable. crbug.com/972476 std-switch/switch-appearance.html [ Failure ] crbug.com/972476 std-switch/switch-appearance-customization.html [ Failure ] +crbug.com/972476 std-switch/switch-appearance-theme.html [ Failure ] # Sheriff 2019-05-20 crbug.com/965389 [ Mac ] media/track/track-cue-rendering-position-auto.html [ Pass Failure ] @@ -6013,7 +6013,6 @@ crbug.com/988074 [ Linux ] virtual/omt-worker-fetch/http/tests/security/cors-rfc1918/addressspace-serviceworker-basic.html [ Pass Failure ] # Sheriff 2019-08-05 -crbug.com/981522 [ Linux ] external/wpt/svg/animations/correct-events-for-short-animations-with-syncbases.html [ Pass Failure Timeout ] crbug.com/991243 [ Linux ] external/wpt/workers/semantics/multiple-workers/003.html [ Pass Timeout ] crbug.com/991243 [ Linux ] virtual/omt-worker-fetch/external/wpt/workers/semantics/multiple-workers/003.html [ Pass Timeout ] @@ -6196,3 +6195,9 @@ crbug.com/1010032 [ Win7 ] fast/writing-mode/Kusa-Makura-background-canvas.html [ Failure ] crbug.com/1010170 [ Mac ] media/video-played-reset.html [ Failure ] +# Sheriff 2019-10-02 +crbug.com/1010472 [ Linux Mac Debug ] fast/canvas/color-space/canvas-drawImage-offscreenCanvas.html [ Timeout Pass ] +crbug.com/1010483 [ Win ] fast/parser/residual-style-dom.html [ Crash Pass ] +crbug.com/1010483 [ Win ] fast/parser/residual-style-hang.html [ Crash Pass ] +crbug.com/1010483 [ Win ] fast/selectors/specificity-overflow.html [ Crash Pass ] +
diff --git a/third_party/blink/web_tests/W3CImportExpectations b/third_party/blink/web_tests/W3CImportExpectations index 1d035abb..5e1ff29 100644 --- a/third_party/blink/web_tests/W3CImportExpectations +++ b/third_party/blink/web_tests/W3CImportExpectations
@@ -10,8 +10,8 @@ # https://chromium.googlesource.com/chromium/src/+/master/docs/testing/web_test_expectations.md external/wpt/.codecov.yml [ Skip ] +external/wpt/.github [ Skip ] external/wpt/.gitmodules [ Skip ] -external/wpt/.travis.yml [ Skip ] external/wpt/CODEOWNERS [ Skip ] external/wpt/WebIDL/invalid [ Skip ] external/wpt/WebIDL/readme.txt [ Skip ]
diff --git a/third_party/blink/web_tests/external/wpt/.github/META.yml b/third_party/blink/web_tests/external/wpt/.github/META.yml deleted file mode 100644 index 06083d1..0000000 --- a/third_party/blink/web_tests/external/wpt/.github/META.yml +++ /dev/null
@@ -1,3 +0,0 @@ -suggested_reviewers: - - jgraham - - jugglinmike
diff --git a/third_party/blink/web_tests/external/wpt/.github/workflows/pull_request.yml b/third_party/blink/web_tests/external/wpt/.github/workflows/pull_request.yml deleted file mode 100644 index 81a53c67..0000000 --- a/third_party/blink/web_tests/external/wpt/.github/workflows/pull_request.yml +++ /dev/null
@@ -1,17 +0,0 @@ -on: pull_request -name: Synchronize the Pull Request Preview -jobs: - update-pr-preview: - runs-on: ubuntu-18.04 - steps: - - uses: actions/checkout@v1 - with: - ref: refs/heads/master - fetch-depth: 50 - - name: update-pr-preview - uses: ./tools/docker/github - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - entrypoint: python - args: tools/ci/update_pr_preview.py https://api.github.com
diff --git a/third_party/blink/web_tests/external/wpt/.github/workflows/push-build-publish-documentation-website.yml b/third_party/blink/web_tests/external/wpt/.github/workflows/push-build-publish-documentation-website.yml deleted file mode 100644 index f6a1401..0000000 --- a/third_party/blink/web_tests/external/wpt/.github/workflows/push-build-publish-documentation-website.yml +++ /dev/null
@@ -1,19 +0,0 @@ -on: - push: - branches: - - master -name: Build & Publish Documentation Website -jobs: - website-build-and-publish: - runs-on: ubuntu-18.04 - steps: - - uses: actions/checkout@v1 - with: - fetch-depth: 50 - - name: website-build-and-publish - uses: ./tools/docker/documentation - env: - DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }} - with: - entrypoint: /bin/bash - args: tools/ci/website_build.sh
diff --git a/third_party/blink/web_tests/external/wpt/.github/workflows/push-build-release-manifest.yml b/third_party/blink/web_tests/external/wpt/.github/workflows/push-build-release-manifest.yml deleted file mode 100644 index baa06cc..0000000 --- a/third_party/blink/web_tests/external/wpt/.github/workflows/push-build-release-manifest.yml +++ /dev/null
@@ -1,19 +0,0 @@ -on: - push: - branches: - - master -name: Build & Release Manifest -jobs: - manifest-build-and-tag: - runs-on: ubuntu-18.04 - steps: - - uses: actions/checkout@v1 - with: - fetch-depth: 50 - - name: manifest-build-and-tag - uses: ./tools/docker/github - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - entrypoint: python - args: tools/ci/manifest_build.py
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/README.md b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/README.md new file mode 100644 index 0000000..204fd59d --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/README.md
@@ -0,0 +1,2 @@ +This directory contains an experimental feature that is not currently standardized due to a security +issue. Support was removed in https://github.com/whatwg/html/pull/4943.
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/invalid-content-type.html b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/invalid-content-type.tentative.html similarity index 100% rename from third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/invalid-content-type.html rename to third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/invalid-content-type.tentative.html
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/module-expected.txt b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/module.tentative-expected.txt similarity index 100% rename from third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/module-expected.txt rename to third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/module.tentative-expected.txt
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/module.html b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/module.tentative.html similarity index 100% rename from third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/module.html rename to third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/module.tentative.html
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/non-object.any-expected.txt b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/non-object.tentative.any-expected.txt similarity index 100% rename from third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/non-object.any-expected.txt rename to third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/non-object.tentative.any-expected.txt
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/non-object.any.js b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/non-object.tentative.any.js similarity index 100% rename from third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/non-object.any.js rename to third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/non-object.tentative.any.js
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/non-object.any.serviceworker-expected.txt b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/non-object.tentative.any.serviceworker-expected.txt similarity index 100% rename from third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/non-object.any.serviceworker-expected.txt rename to third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/non-object.tentative.any.serviceworker-expected.txt
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/non-object.any.sharedworker-expected.txt b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/non-object.tentative.any.sharedworker-expected.txt similarity index 100% rename from third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/non-object.any.sharedworker-expected.txt rename to third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/non-object.tentative.any.sharedworker-expected.txt
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/non-object.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/non-object.tentative.any.worker-expected.txt similarity index 100% rename from third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/non-object.any.worker-expected.txt rename to third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/non-object.tentative.any.worker-expected.txt
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/parse-error.html b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/parse-error.tentative.html similarity index 100% rename from third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/parse-error.html rename to third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/parse-error.tentative.html
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/utf8-expected.txt b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/utf8.tentative-expected.txt similarity index 100% rename from third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/utf8-expected.txt rename to third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/utf8.tentative-expected.txt
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/utf8.html b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/utf8.tentative.html similarity index 100% rename from third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/utf8.html rename to third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/utf8.tentative.html
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/valid-content-type-expected.txt b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/valid-content-type.tentative-expected.txt similarity index 100% rename from third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/valid-content-type-expected.txt rename to third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/valid-content-type.tentative-expected.txt
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/valid-content-type.html b/third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/valid-content-type.tentative.html similarity index 100% rename from third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/valid-content-type.html rename to third_party/blink/web_tests/external/wpt/html/semantics/scripting-1/the-script-element/json-module/valid-content-type.tentative.html
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Document/getAnimations-expected.txt b/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Document/getAnimations-expected.txt index e91647a..8598275 100644 --- a/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Document/getAnimations-expected.txt +++ b/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Document/getAnimations-expected.txt
@@ -4,7 +4,7 @@ PASS Test the order of document.getAnimations with script generated animations PASS Test document.getAnimations for a disconnected node PASS Test document.getAnimations with null target -FAIL Test document.getAnimations for elements inside same-origin iframes assert_true: Not expecting event, but got load event expected true got false +FAIL Test document.getAnimations for elements inside same-origin iframes assert_equals: expected 1 but got 0 PASS Triggers a style change event Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Document/getAnimations.html b/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Document/getAnimations.html index f5df980c..f6a6c3e 100644 --- a/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Document/getAnimations.html +++ b/third_party/blink/web_tests/external/wpt/web-animations/interfaces/Document/getAnimations.html
@@ -68,11 +68,12 @@ const iframe = document.createElement('iframe'); const eventWatcher = new EventWatcher(t, iframe, ['load']); + const event_promise = eventWatcher.wait_for('load'); document.body.appendChild(iframe); t.add_cleanup(() => { document.body.removeChild(iframe); }); - await eventWatcher.wait_for('load'); + await event_promise; const div = createDiv(t, iframe.contentDocument) const effect = new KeyframeEffect(div, null, 100 * MS_PER_SEC);
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https.html b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https.html index 8e853e4..9dd364ed 100644 --- a/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https.html +++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https.html
@@ -360,7 +360,14 @@ delete pc1.candidateBuffer; await listenToIceConnected(pc1); await listenToIceConnected(pc2); - assert_array_equals(pc1.iceStates, ['new', 'checking', 'connected']); + // While we're waiting for pc2, pc1 may or may not have transitioned + // to "completed" state, so allow for both cases. + if (pc1.iceStates.length == 3) { + assert_array_equals(pc1.iceStates, ['new', 'checking', 'connected']); + } else { + assert_array_equals(pc1.iceStates, ['new', 'checking', 'connected', + 'completed']); + } assert_array_equals(pc2.iceStates, ['new', 'checking', 'connected']); }, 'Responder ICE connection state behaves as expected');
diff --git a/third_party/blink/web_tests/external/wpt/websockets/stream-tentative/abort.any.js b/third_party/blink/web_tests/external/wpt/websockets/stream-tentative/abort.any.js new file mode 100644 index 0000000..2392188 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/websockets/stream-tentative/abort.any.js
@@ -0,0 +1,46 @@ +// META: script=../websocket.sub.js +// META: script=resources/url-constants.js +// META: script=/common/utils.js +// META: global=window,worker + +promise_test(async t => { + const controller = new AbortController(); + controller.abort(); + const key = token(); + const wsUrl = new URL( + `/fetch/api/resources/stash-put.py?key=${key}&value=connected`, + location.href); + wsUrl.protocol = wsUrl.protocol.replace('http', 'ws'); + // We intentionally use the port for the HTTP server, not the WebSocket + // server, because we don't expect the connection to be performed. + const wss = new WebSocketStream(wsUrl, { signal: controller.signal }); + await promise_rejects(t, 'AbortError', wss.connection, + 'connection should reject'); + await promise_rejects(t, 'AbortError', wss.closed, 'closed should reject'); + // An incorrect implementation could pass this test due a race condition, + // but it is hard to completely eliminate the possibility. + const response = await fetch(`/fetch/api/resources/stash-take.py?key=${key}`); + assert_equals(await response.text(), 'null', 'response should be null'); +}, 'abort before constructing should prevent connection'); + +promise_test(async t => { + const controller = new AbortController(); + const wss = new WebSocketStream(`${BASEURL}/handshake_sleep_2`, + { signal: controller.signal }); + // Give the connection a chance to start. + await new Promise(resolve => t.step_timeout(resolve, 0)); + controller.abort(); + await promise_rejects(t, 'AbortError', wss.connection, + 'connection should reject'); + await promise_rejects(t, 'AbortError', wss.closed, 'closed should reject'); +}, 'abort during handshake should work'); + +promise_test(async t => { + const controller = new AbortController(); + const wss = new WebSocketStream(ECHOURL, { signal: controller.signal }); + const { readable, writable } = await wss.connection; + controller.abort(); + writable.getWriter().write('connected'); + const { value } = await readable.getReader().read(); + assert_equals(value, 'connected', 'value should match'); +}, 'abort after connect should do nothing');
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt deleted file mode 100644 index 64dbbdf..0000000 --- a/third_party/blink/web_tests/platform/mac-mac10.10/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt +++ /dev/null
@@ -1,14 +0,0 @@ -This is a testharness.js-based test. -PASS Initial iceConnectionState should be new -PASS Closing the connection should set iceConnectionState to closed -PASS connection with one data channel should eventually have connected or completed connection state -FAIL connection with one data channel should eventually have connected connection state Cannot read property 'transport' of null -PASS connection with audio track should eventually have connected connection state -PASS connection with audio and video tracks should eventually have connected connection state -FAIL ICE can connect in a recvonly usecase promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'." -FAIL iceConnectionState changes at the right time, with bundle policy balanced promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'sender' of undefined" -FAIL iceConnectionState changes at the right time, with bundle policy max-bundle promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'sender' of undefined" -FAIL iceConnectionState changes at the right time, with bundle policy max-compat promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'sender' of undefined" -PASS Responder ICE connection state behaves as expected -Harness: the test ran to completion. -
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.11/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt deleted file mode 100644 index 9fd6106..0000000 --- a/third_party/blink/web_tests/platform/mac-mac10.11/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt +++ /dev/null
@@ -1,14 +0,0 @@ -This is a testharness.js-based test. -PASS Initial iceConnectionState should be new -PASS Closing the connection should set iceConnectionState to closed -PASS connection with one data channel should eventually have connected or completed connection state -FAIL connection with one data channel should eventually have connected connection state Cannot read property 'transport' of null -PASS connection with audio track should eventually have connected connection state -PASS connection with audio and video tracks should eventually have connected connection state -FAIL ICE can connect in a recvonly usecase promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'." -FAIL iceConnectionState changes at the right time, with bundle policy balanced promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'sender' of undefined" -FAIL iceConnectionState changes at the right time, with bundle policy max-bundle promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'sender' of undefined" -FAIL iceConnectionState changes at the right time, with bundle policy max-compat promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'sender' of undefined" -FAIL Responder ICE connection state behaves as expected assert_array_equals: lengths differ, expected 3 got 4 -Harness: the test ran to completion. -
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.12/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt deleted file mode 100644 index 64dbbdf..0000000 --- a/third_party/blink/web_tests/platform/mac-mac10.12/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt +++ /dev/null
@@ -1,14 +0,0 @@ -This is a testharness.js-based test. -PASS Initial iceConnectionState should be new -PASS Closing the connection should set iceConnectionState to closed -PASS connection with one data channel should eventually have connected or completed connection state -FAIL connection with one data channel should eventually have connected connection state Cannot read property 'transport' of null -PASS connection with audio track should eventually have connected connection state -PASS connection with audio and video tracks should eventually have connected connection state -FAIL ICE can connect in a recvonly usecase promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'." -FAIL iceConnectionState changes at the right time, with bundle policy balanced promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'sender' of undefined" -FAIL iceConnectionState changes at the right time, with bundle policy max-bundle promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'sender' of undefined" -FAIL iceConnectionState changes at the right time, with bundle policy max-compat promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'sender' of undefined" -PASS Responder ICE connection state behaves as expected -Harness: the test ran to completion. -
diff --git a/third_party/blink/web_tests/platform/mac/std-switch/switch-appearance-theme-expected.png b/third_party/blink/web_tests/platform/mac/std-switch/switch-appearance-theme-expected.png new file mode 100644 index 0000000..aadbe3b --- /dev/null +++ b/third_party/blink/web_tests/platform/mac/std-switch/switch-appearance-theme-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt b/third_party/blink/web_tests/platform/mac/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt deleted file mode 100644 index 9fd6106..0000000 --- a/third_party/blink/web_tests/platform/mac/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/RTCPeerConnection-iceConnectionState.https-expected.txt +++ /dev/null
@@ -1,14 +0,0 @@ -This is a testharness.js-based test. -PASS Initial iceConnectionState should be new -PASS Closing the connection should set iceConnectionState to closed -PASS connection with one data channel should eventually have connected or completed connection state -FAIL connection with one data channel should eventually have connected connection state Cannot read property 'transport' of null -PASS connection with audio track should eventually have connected connection state -PASS connection with audio and video tracks should eventually have connected connection state -FAIL ICE can connect in a recvonly usecase promise_test: Unhandled rejection with value: object "InvalidStateError: Failed to execute 'addTransceiver' on 'RTCPeerConnection': This operation is only supported in 'unified-plan'." -FAIL iceConnectionState changes at the right time, with bundle policy balanced promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'sender' of undefined" -FAIL iceConnectionState changes at the right time, with bundle policy max-bundle promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'sender' of undefined" -FAIL iceConnectionState changes at the right time, with bundle policy max-compat promise_test: Unhandled rejection with value: object "TypeError: Cannot read property 'sender' of undefined" -FAIL Responder ICE connection state behaves as expected assert_array_equals: lengths differ, expected 3 got 4 -Harness: the test ran to completion. -
diff --git a/third_party/blink/web_tests/platform/win/std-switch/switch-appearance-theme-expected.png b/third_party/blink/web_tests/platform/win/std-switch/switch-appearance-theme-expected.png new file mode 100644 index 0000000..aadbe3b --- /dev/null +++ b/third_party/blink/web_tests/platform/win/std-switch/switch-appearance-theme-expected.png Binary files differ
diff --git a/third_party/blink/web_tests/std-switch/switch-appearance-theme.html b/third_party/blink/web_tests/std-switch/switch-appearance-theme.html new file mode 100644 index 0000000..c226ca5 --- /dev/null +++ b/third_party/blink/web_tests/std-switch/switch-appearance-theme.html
@@ -0,0 +1,38 @@ +<!DOCTYPE html> +<body> +<script type="module"> +import 'std:elements/switch'; + +document.body.insertAdjacentHTML('beforeend', ` +<h2>Attribute on the element</h2> +<std-switch theme="platform-agnostic"></std-switch> +<std-switch theme="match-platform"></std-switch>`); + +document.body.insertAdjacentHTML('beforeend', ` +<h2>Attribute on an ancestor</h2> +<span theme="platform-agnostic"><std-switch></std-switch></span> +<i theme="match-platform"><b><std-switch></std-switch></b></i>`); + +document.body.insertAdjacentHTML('beforeend', ` +<h2>Nested attributes</h2> +<s theme="match-platform"><span theme="platform-agnostic"><code theme="foo" +><std-switch></std-switch></code></span></s> +<kbd theme="platform-agnostic"><i theme="match-platform"><b><std-switch></std-switch></b></i></kbd>`); + +document.body.insertAdjacentHTML('beforeend', ` +<h2>Attribute over shadow-boundary</h2> +<span theme="platform-agnostic"><span id="host1"></span></span> +<i theme="match-platform"><b><span id="host2"></span></b></i>`); +let root1 = document.querySelector('#host1').attachShadow({mode:'open'}); +root1.innerHTML = '<std-switch></std-switch>'; +let root2 = document.querySelector('#host2').attachShadow({mode:'closed'}); +root2.innerHTML = '<std-switch></std-switch>'; + +document.body.insertAdjacentHTML('beforeend', ` +<h2>NO support for updating attribute values for now</h2> +<std-switch id="update1" theme="match-platform"></std-switch> +<std-switch id="update2" theme="platform-agnostic"></std-switch>`); +document.querySelector('#update1').setAttribute('theme', 'platform-agnostic'); +document.querySelector('#update2').setAttribute('theme', 'match-platform'); +</script> +</body>
diff --git a/third_party/blink/web_tests/virtual/json-modules/external/wpt/html/semantics/scripting-1/the-script-element/json-module/invalid-content-type-expected.txt b/third_party/blink/web_tests/virtual/json-modules/external/wpt/html/semantics/scripting-1/the-script-element/json-module/invalid-content-type.tentative-expected.txt similarity index 100% rename from third_party/blink/web_tests/virtual/json-modules/external/wpt/html/semantics/scripting-1/the-script-element/json-module/invalid-content-type-expected.txt rename to third_party/blink/web_tests/virtual/json-modules/external/wpt/html/semantics/scripting-1/the-script-element/json-module/invalid-content-type.tentative-expected.txt
diff --git a/third_party/blink/web_tests/virtual/json-modules/external/wpt/html/semantics/scripting-1/the-script-element/json-module/module-expected.txt b/third_party/blink/web_tests/virtual/json-modules/external/wpt/html/semantics/scripting-1/the-script-element/json-module/module.tentative-expected.txt similarity index 100% rename from third_party/blink/web_tests/virtual/json-modules/external/wpt/html/semantics/scripting-1/the-script-element/json-module/module-expected.txt rename to third_party/blink/web_tests/virtual/json-modules/external/wpt/html/semantics/scripting-1/the-script-element/json-module/module.tentative-expected.txt
diff --git a/third_party/blink/web_tests/virtual/json-modules/external/wpt/html/semantics/scripting-1/the-script-element/json-module/non-object.any-expected.txt b/third_party/blink/web_tests/virtual/json-modules/external/wpt/html/semantics/scripting-1/the-script-element/json-module/non-object.tentative.any-expected.txt similarity index 100% rename from third_party/blink/web_tests/virtual/json-modules/external/wpt/html/semantics/scripting-1/the-script-element/json-module/non-object.any-expected.txt rename to third_party/blink/web_tests/virtual/json-modules/external/wpt/html/semantics/scripting-1/the-script-element/json-module/non-object.tentative.any-expected.txt
diff --git a/third_party/blink/web_tests/virtual/json-modules/external/wpt/html/semantics/scripting-1/the-script-element/json-module/non-object.any.worker-expected.txt b/third_party/blink/web_tests/virtual/json-modules/external/wpt/html/semantics/scripting-1/the-script-element/json-module/non-object.tentative.any.worker-expected.txt similarity index 100% rename from third_party/blink/web_tests/virtual/json-modules/external/wpt/html/semantics/scripting-1/the-script-element/json-module/non-object.any.worker-expected.txt rename to third_party/blink/web_tests/virtual/json-modules/external/wpt/html/semantics/scripting-1/the-script-element/json-module/non-object.tentative.any.worker-expected.txt
diff --git a/third_party/blink/web_tests/virtual/json-modules/external/wpt/html/semantics/scripting-1/the-script-element/json-module/parse-error-expected.txt b/third_party/blink/web_tests/virtual/json-modules/external/wpt/html/semantics/scripting-1/the-script-element/json-module/parse-error.tentative-expected.txt similarity index 91% rename from third_party/blink/web_tests/virtual/json-modules/external/wpt/html/semantics/scripting-1/the-script-element/json-module/parse-error-expected.txt rename to third_party/blink/web_tests/virtual/json-modules/external/wpt/html/semantics/scripting-1/the-script-element/json-module/parse-error.tentative-expected.txt index 166837e4..2fcf3eb 100644 --- a/third_party/blink/web_tests/virtual/json-modules/external/wpt/html/semantics/scripting-1/the-script-element/json-module/parse-error-expected.txt +++ b/third_party/blink/web_tests/virtual/json-modules/external/wpt/html/semantics/scripting-1/the-script-element/json-module/parse-error.tentative-expected.txt
@@ -1,4 +1,4 @@ This is a testharness.js-based test. -FAIL JSON modules: parse error assert_equals: expected "http://web-platform.test:8001/html/semantics/scripting-1/the-script-element/json-module/parse-error.json" but got "http://web-platform.test:8001/html/semantics/scripting-1/the-script-element/json-module/parse-error.html" +FAIL JSON modules: parse error assert_equals: expected "http://web-platform.test:8001/html/semantics/scripting-1/the-script-element/json-module/parse-error.json" but got "http://web-platform.test:8001/html/semantics/scripting-1/the-script-element/json-module/parse-error.tentative.html" Harness: the test ran to completion.
diff --git a/third_party/blink/web_tests/virtual/json-modules/external/wpt/html/semantics/scripting-1/the-script-element/json-module/valid-content-type-expected.txt b/third_party/blink/web_tests/virtual/json-modules/external/wpt/html/semantics/scripting-1/the-script-element/json-module/valid-content-type.tentative-expected.txt similarity index 100% rename from third_party/blink/web_tests/virtual/json-modules/external/wpt/html/semantics/scripting-1/the-script-element/json-module/valid-content-type-expected.txt rename to third_party/blink/web_tests/virtual/json-modules/external/wpt/html/semantics/scripting-1/the-script-element/json-module/valid-content-type.tentative-expected.txt
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index 56d8613..b6dd0c0 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -4691,6 +4691,13 @@ <int value="5" label="Overlay Insecure Non-player-element Fullscreen"/> </enum> +<enum name="BackForwardCacheHistoryNavigationOutcome"> + <int value="0" label="Restored"/> + <int value="1" label="Not cached"/> + <int value="2" label="Evicted"/> + <int value="3" label="Not cached due to the experiment condition"/> +</enum> + <enum name="BackForwardNavigationType"> <int value="0" label="Fast back navigation with WKBackForwardList"/> <int value="1" label="Slow back navigation"/> @@ -57740,6 +57747,34 @@ <int value="5" label="TLS 1.3 0-RTT handshake (0-RTT)"/> </enum> +<enum name="SSLHandshakeEarlyDataReason"> +<!-- Corresponds to //third_party/boringssl's ssl_early_data_reason_t --> + + <int value="0" + label="The handshake has not progressed far enough for the 0-RTT status + to be known."/> + <int value="1" label="0-RTT is disabled for this connection."/> + <int value="2" label="0-RTT was accepted."/> + <int value="3" + label="The negotiated protocol version does not support 0-RTT."/> + <int value="4" + label="The peer declined to offer or accept 0-RTT for an unknown + reason."/> + <int value="5" label="The client did not offer a session."/> + <int value="6" label="The server declined to resume the session."/> + <int value="7" label="The session does not support 0-RTT."/> + <int value="8" label="The server sent a HelloRetryRequest."/> + <int value="9" + label="The negotiated ALPN protocol did not match the session."/> + <int value="10" + label="The connection negotiated Channel ID, which is incompatible with + 0-RTT."/> + <int value="11" + label="The connection negotiated token binding, which is incompatible + with 0-RTT."/> + <int value="12" label="The client and server ticket age were too far apart."/> +</enum> + <enum name="SSLHashAlgorithm"> <obsolete> Removed June 2016.
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 8c7e3c2..a129957 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -13045,6 +13045,18 @@ </summary> </histogram> +<histogram name="BackForwardCache.HistoryNavigationOutcome" + enum="BackForwardCacheHistoryNavigationOutcome" expires_after="2020-10-01"> + <owner>bfcache-dev@chromium.org</owner> + <owner>hajimehoshi@chromium.org</owner> + <summary> + When navigating back to a page in the session history, this records whether + the page was restored from the BackForwardCache or not. + + This recording starts as of M79. + </summary> +</histogram> + <histogram base="true" name="BackgroundFetch.EventDispatchFailure.Dispatch" enum="ServiceWorkerStatusCode"> <!-- Name completed by histogram_suffixes name="BackgroundFetchEvents" --> @@ -82384,6 +82396,19 @@ </summary> </histogram> +<histogram name="Net.SSLHandshakeEarlyDataReason" + enum="SSLHandshakeEarlyDataReason"> +<!-- expires-never: Used to keep track of the TLS ecosystem. --> + + <owner>davidben@chromium.org</owner> + <owner>svaldez@chromium.org</owner> + <summary> + Indicates whether a TLS 1.3 connection with 0-RTT enabled ended up using + 0-RTT or not, and why; this includes reasons such as the server declining to + resume the connection and the client not having enough tickets available. + </summary> +</histogram> + <histogram name="Net.SSLHostInfoDNSLookup" units="ms" expires_after="2015-11-11"> <obsolete>
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml index 8df77ef..237fabc 100644 --- a/tools/metrics/ukm/ukm.xml +++ b/tools/metrics/ukm/ukm.xml
@@ -5569,6 +5569,25 @@ </summary> </metric> <metric name="InteractiveTiming.LongestInputDelay3"> + <obsolete> + Removed on September 2019 in favor of InteractiveTiming.LongestInputDelay4 + which includes events which otherwise would have been filtered. + </obsolete> + <summary> + Measures longest Input Delay, the longest duration between the hardware + timestamp and the start of event processing on the main thread for the + meaningful input per navigation. In ms. + </summary> + <aggregation> + <history> + <index fields="profile.country"/> + <statistics> + <quantiles type="std-percentiles"/> + </statistics> + </history> + </aggregation> + </metric> + <metric name="InteractiveTiming.LongestInputDelay4"> <summary> Measures longest Input Delay, the longest duration between the hardware timestamp and the start of event processing on the main thread for the @@ -5606,6 +5625,17 @@ </summary> </metric> <metric name="InteractiveTiming.LongestInputTimestamp3"> + <obsolete> + Removed on February 2019 in favor of + InteractiveTiming.LongestInputTimestamp4 which includes inputs which would + have previously been filtered. + </obsolete> + <summary> + The duration between navigation start and the hardware timestamp of the + meaningful input with longest queuing delay per navigation. In ms. + </summary> + </metric> + <metric name="InteractiveTiming.LongestInputTimestamp4"> <summary> The duration between navigation start and the hardware timestamp of the meaningful input with longest queuing delay per navigation. In ms.
diff --git a/tools/perf/core/results_processor/processor.py b/tools/perf/core/results_processor/processor.py index 32f47a4..9eeef263 100644 --- a/tools/perf/core/results_processor/processor.py +++ b/tools/perf/core/results_processor/processor.py
@@ -9,8 +9,12 @@ """ import json +import logging import os +import random +import re +from py_utils import cloud_storage from core.results_processor import command_line from core.results_processor import compute_metrics from core.results_processor import formatters @@ -43,7 +47,8 @@ _AggregateTraces(intermediate_results) - _UploadArtifacts(intermediate_results, options.upload_bucket) + UploadArtifacts( + intermediate_results, options.upload_bucket, options.results_label) if any(fmt in FORMATS_WITH_METRICS for fmt in options.output_formats): histogram_dicts = _ComputeMetrics(intermediate_results, @@ -88,20 +93,48 @@ del artifacts[trace] -def _UploadArtifacts(intermediate_results, upload_bucket): +def _RemoteName(results_label, start_time, test_path, artifact_name): + """Construct a name for a given artifact, under which it will be + stored in the cloud. + """ + if results_label: + identifier_parts = [re.sub(r'\W+', '_', results_label)] + else: + identifier_parts = [] + # Time is rounded to seconds and delimiters are removed. + # The first 19 chars of the string match 'YYYY-MM-DDTHH:MM:SS'. + identifier_parts.append(re.sub(r'\W+', '', start_time[:19])) + identifier_parts.append(str(random.randint(1, 1e5))) + run_identifier = '_'.join(identifier_parts) + return '/'.join([run_identifier, test_path, artifact_name]) + + +def UploadArtifacts(intermediate_results, upload_bucket, results_label): """Upload all artifacts to cloud. For each test run, uploads all its artifacts to cloud and sets remoteUrl fields in intermediate_results. """ - if upload_bucket is not None: - for result in intermediate_results['testResults']: - artifacts = result.get('artifacts', {}) - for artifact in artifacts.values(): - # For now, the uploading is done by Telemetry, so we just check that - # remoteUrls are set. - # TODO(crbug.com/981349): replace this with actual uploading code - assert 'remoteUrl' in artifact + if upload_bucket is None: + return + + start_time = intermediate_results['benchmarkRun']['startTime'] + for result in intermediate_results['testResults']: + artifacts = result.get('artifacts', {}) + for name, artifact in artifacts.iteritems(): + if 'remoteUrl' in artifact: + continue + # TODO(crbug.com/981349): Remove this check after Telemetry does not + # save histograms as an artifact anymore. + if name == compute_metrics.HISTOGRAM_DICTS_FILE: + continue + artifact['remoteUrl'] = cloud_storage.Insert( + upload_bucket, + _RemoteName(results_label, start_time, result['testPath'], name), + artifact['filePath'], + ) + logging.info('Uploaded %s of %s to %s\n' % ( + name, result['testPath'], artifact['remoteUrl'])) def _ComputeMetrics(intermediate_results, results_label):
diff --git a/tools/perf/core/results_processor/processor_unittest.py b/tools/perf/core/results_processor/processor_unittest.py index 1df87c2..aa872e5 100644 --- a/tools/perf/core/results_processor/processor_unittest.py +++ b/tools/perf/core/results_processor/processor_unittest.py
@@ -6,6 +6,8 @@ import unittest +import mock + from core.results_processor import processor from core.results_processor import testing @@ -37,3 +39,56 @@ self.assertIn(['linux'], diag_values) self.assertIn([['documentation', 'url']], diag_values) self.assertIn(['label'], diag_values) + + def testUploadArtifacts(self): + in_results = testing.IntermediateResults( + test_results=[ + testing.TestResult( + 'benchmark/story', + artifacts={'log': testing.Artifact('/log.log')}, + ), + testing.TestResult( + 'benchmark/story', + artifacts={ + 'trace.html': testing.Artifact('/trace.html'), + 'screenshot': testing.Artifact('/screenshot.png'), + }, + ), + ], + ) + + with mock.patch('py_utils.cloud_storage.Insert') as cloud_patch: + cloud_patch.return_value = 'gs://url' + processor.UploadArtifacts(in_results, 'bucket', None) + cloud_patch.assert_has_calls([ + mock.call('bucket', mock.ANY, '/log.log'), + mock.call('bucket', mock.ANY, '/trace.html'), + mock.call('bucket', mock.ANY, '/screenshot.png'), + ], + any_order=True, + ) + + for result in in_results['testResults']: + for artifact in result['artifacts'].itervalues(): + self.assertEqual(artifact['remoteUrl'], 'gs://url') + + def testUploadArtifacts_CheckRemoteUrl(self): + in_results = testing.IntermediateResults( + test_results=[ + testing.TestResult( + 'benchmark/story', + artifacts={'trace.html': testing.Artifact('/trace.html')}, + ), + ], + start_time='2019-10-01T12:00:00.123456Z', + ) + + with mock.patch('py_utils.cloud_storage.Insert') as cloud_patch: + with mock.patch('random.randint') as randint_patch: + randint_patch.return_value = 54321 + processor.UploadArtifacts(in_results, 'bucket', 'src@abc + 123') + cloud_patch.assert_called_once_with( + 'bucket', + 'src_abc_123_20191001T120000_54321/benchmark/story/trace.html', + '/trace.html' + )
diff --git a/tools/perf/page_sets/data/octane.json b/tools/perf/page_sets/data/octane.json index 382d879..24494d6a 100644 --- a/tools/perf/page_sets/data/octane.json +++ b/tools/perf/page_sets/data/octane.json
@@ -1,9 +1,9 @@ { "archives": { - "http://chromium.github.io/octane/index.html?auto=1": { + "Octane": { "DEFAULT": "octane_002.wprgo" } }, "description": "Describes the Web Page Replay archives for a story set. Don't edit by hand! Use record_wpr for updating.", "platform_specific": true -} \ No newline at end of file +}
diff --git a/tools/perf/page_sets/octane_pages.py b/tools/perf/page_sets/octane_pages.py index e3f8a32..35457ef 100644 --- a/tools/perf/page_sets/octane_pages.py +++ b/tools/perf/page_sets/octane_pages.py
@@ -53,7 +53,8 @@ class OctaneStory(press_story.PressStory): - URL='http://chromium.github.io/octane/index.html?auto=1' + URL = 'http://chromium.github.io/octane/index.html?auto=1' + NAME = 'Octane' def RunNavigateSteps(self, action_runner): total_memory = ( @@ -97,8 +98,8 @@ # Collect all test scores to compute geometric mean. all_scores.append(score) total = statistics.GeometricMean(all_scores) - self.AddJavascriptMetricSummaryValue( - scalar.ScalarValue(None, 'Total.Score', 'score', total, + self.AddJavascriptMetricValue( + scalar.ScalarValue(self, 'Total.Score', 'score', total, description='Geometric mean of the scores of each ' 'individual benchmark in the Octane ' 'benchmark collection.'))
diff --git a/ui/display/mac/screen_mac.mm b/ui/display/mac/screen_mac.mm index 170963e..85545ec 100644 --- a/ui/display/mac/screen_mac.mm +++ b/ui/display/mac/screen_mac.mm
@@ -143,8 +143,8 @@ ScreenMac() : configure_timer_(FROM_HERE, base::TimeDelta::FromMilliseconds(kConfigureDelayMs), - base::Bind(&ScreenMac::ConfigureTimerFired, - base::Unretained(this))) { + base::BindRepeating(&ScreenMac::ConfigureTimerFired, + base::Unretained(this))) { old_displays_ = displays_ = BuildDisplaysFromQuartz(); CGDisplayRegisterReconfigurationCallback( ScreenMac::DisplayReconfigurationCallBack, this);
diff --git a/ui/display/manager/apply_content_protection_task_unittest.cc b/ui/display/manager/apply_content_protection_task_unittest.cc index ec7494c..864c2cd 100644 --- a/ui/display/manager/apply_content_protection_task_unittest.cc +++ b/ui/display/manager/apply_content_protection_task_unittest.cc
@@ -64,8 +64,8 @@ request[1] = CONTENT_PROTECTION_METHOD_HDCP; ApplyContentProtectionTask task( &layout_manager, &display_delegate_, request, - base::Bind(&ApplyContentProtectionTaskTest::ResponseCallback, - base::Unretained(this))); + base::BindOnce(&ApplyContentProtectionTaskTest::ResponseCallback, + base::Unretained(this))); task.Run(); EXPECT_EQ(Response::SUCCESS, response_); @@ -82,8 +82,8 @@ request[1] = CONTENT_PROTECTION_METHOD_HDCP; ApplyContentProtectionTask task( &layout_manager, &display_delegate_, request, - base::Bind(&ApplyContentProtectionTaskTest::ResponseCallback, - base::Unretained(this))); + base::BindOnce(&ApplyContentProtectionTaskTest::ResponseCallback, + base::Unretained(this))); task.Run(); EXPECT_EQ(Response::SUCCESS, response_); @@ -101,8 +101,8 @@ request[1] = CONTENT_PROTECTION_METHOD_HDCP; ApplyContentProtectionTask task( &layout_manager, &display_delegate_, request, - base::Bind(&ApplyContentProtectionTaskTest::ResponseCallback, - base::Unretained(this))); + base::BindOnce(&ApplyContentProtectionTaskTest::ResponseCallback, + base::Unretained(this))); task.Run(); EXPECT_EQ(Response::FAILURE, response_); @@ -120,8 +120,8 @@ request[1] = CONTENT_PROTECTION_METHOD_HDCP; ApplyContentProtectionTask task( &layout_manager, &display_delegate_, request, - base::Bind(&ApplyContentProtectionTaskTest::ResponseCallback, - base::Unretained(this))); + base::BindOnce(&ApplyContentProtectionTaskTest::ResponseCallback, + base::Unretained(this))); task.Run(); EXPECT_EQ(Response::FAILURE, response_); @@ -139,8 +139,8 @@ request[1] = CONTENT_PROTECTION_METHOD_HDCP; ApplyContentProtectionTask task( &layout_manager, &display_delegate_, request, - base::Bind(&ApplyContentProtectionTaskTest::ResponseCallback, - base::Unretained(this))); + base::BindOnce(&ApplyContentProtectionTaskTest::ResponseCallback, + base::Unretained(this))); task.Run(); EXPECT_EQ(Response::FAILURE, response_); @@ -159,8 +159,8 @@ request[1] = CONTENT_PROTECTION_METHOD_NONE; ApplyContentProtectionTask task( &layout_manager, &display_delegate_, request, - base::Bind(&ApplyContentProtectionTaskTest::ResponseCallback, - base::Unretained(this))); + base::BindOnce(&ApplyContentProtectionTaskTest::ResponseCallback, + base::Unretained(this))); task.Run(); EXPECT_EQ(Response::SUCCESS, response_);
diff --git a/ui/display/manager/configure_displays_task.cc b/ui/display/manager/configure_displays_task.cc index b70753f..06d1212 100644 --- a/ui/display/manager/configure_displays_task.cc +++ b/ui/display/manager/configure_displays_task.cc
@@ -46,10 +46,10 @@ ConfigureDisplaysTask::ConfigureDisplaysTask( NativeDisplayDelegate* delegate, const std::vector<DisplayConfigureRequest>& requests, - const ResponseCallback& callback) + ResponseCallback callback) : delegate_(delegate), requests_(requests), - callback_(callback), + callback_(std::move(callback)), is_configuring_(false), num_displays_configured_(0), task_status_(SUCCESS) { @@ -76,16 +76,17 @@ size_t index = pending_request_indexes_.front(); DisplayConfigureRequest* request = &requests_[index]; pending_request_indexes_.pop(); - delegate_->Configure(*request->display, request->mode, request->origin, - base::Bind(&ConfigureDisplaysTask::OnConfigured, - weak_ptr_factory_.GetWeakPtr(), index)); + delegate_->Configure( + *request->display, request->mode, request->origin, + base::BindOnce(&ConfigureDisplaysTask::OnConfigured, + weak_ptr_factory_.GetWeakPtr(), index)); } } // Nothing should be modified after the |callback_| is called since the // task may be deleted in the callback. if (num_displays_configured_ == requests_.size()) - callback_.Run(task_status_); + std::move(callback_).Run(task_status_); } void ConfigureDisplaysTask::OnConfigurationChanged() {}
diff --git a/ui/display/manager/configure_displays_task.h b/ui/display/manager/configure_displays_task.h index 81dd612..86368b3e 100644 --- a/ui/display/manager/configure_displays_task.h +++ b/ui/display/manager/configure_displays_task.h
@@ -50,11 +50,11 @@ PARTIAL_SUCCESS, }; - typedef base::Callback<void(Status)> ResponseCallback; + using ResponseCallback = base::OnceCallback<void(Status)>; ConfigureDisplaysTask(NativeDisplayDelegate* delegate, const std::vector<DisplayConfigureRequest>& requests, - const ResponseCallback& callback); + ResponseCallback callback); ~ConfigureDisplaysTask() override; // Starts the configuration task.
diff --git a/ui/display/manager/configure_displays_task_unittest.cc b/ui/display/manager/configure_displays_task_unittest.cc index ea95f9a..a032da4 100644 --- a/ui/display/manager/configure_displays_task_unittest.cc +++ b/ui/display/manager/configure_displays_task_unittest.cc
@@ -67,11 +67,11 @@ } // namespace TEST_F(ConfigureDisplaysTaskTest, ConfigureWithNoDisplays) { - ConfigureDisplaysTask::ResponseCallback callback = base::Bind( + ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); ConfigureDisplaysTask task(&delegate_, std::vector<DisplayConfigureRequest>(), - callback); + std::move(callback)); task.Run(); @@ -81,13 +81,13 @@ } TEST_F(ConfigureDisplaysTaskTest, ConfigureWithOneDisplay) { - ConfigureDisplaysTask::ResponseCallback callback = base::Bind( + ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); std::vector<DisplayConfigureRequest> requests( 1, DisplayConfigureRequest(displays_[0].get(), &small_mode_, gfx::Point())); - ConfigureDisplaysTask task(&delegate_, requests, callback); + ConfigureDisplaysTask task(&delegate_, requests, std::move(callback)); task.Run(); EXPECT_TRUE(callback_called_); @@ -97,7 +97,7 @@ } TEST_F(ConfigureDisplaysTaskTest, ConfigureWithTwoDisplay) { - ConfigureDisplaysTask::ResponseCallback callback = base::Bind( + ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); std::vector<DisplayConfigureRequest> requests; @@ -106,7 +106,7 @@ displays_[i].get(), displays_[i]->native_mode(), gfx::Point())); } - ConfigureDisplaysTask task(&delegate_, requests, callback); + ConfigureDisplaysTask task(&delegate_, requests, std::move(callback)); task.Run(); EXPECT_TRUE(callback_called_); @@ -120,14 +120,14 @@ } TEST_F(ConfigureDisplaysTaskTest, DisableDisplayFails) { - ConfigureDisplaysTask::ResponseCallback callback = base::Bind( + ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); delegate_.set_max_configurable_pixels(1); std::vector<DisplayConfigureRequest> requests( 1, DisplayConfigureRequest(displays_[0].get(), nullptr, gfx::Point())); - ConfigureDisplaysTask task(&delegate_, requests, callback); + ConfigureDisplaysTask task(&delegate_, requests, std::move(callback)); task.Run(); EXPECT_TRUE(callback_called_); @@ -139,14 +139,14 @@ } TEST_F(ConfigureDisplaysTaskTest, ConfigureWithOneDisplayFails) { - ConfigureDisplaysTask::ResponseCallback callback = base::Bind( + ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); delegate_.set_max_configurable_pixels(1); std::vector<DisplayConfigureRequest> requests( 1, DisplayConfigureRequest(displays_[1].get(), &big_mode_, gfx::Point())); - ConfigureDisplaysTask task(&delegate_, requests, callback); + ConfigureDisplaysTask task(&delegate_, requests, std::move(callback)); task.Run(); EXPECT_TRUE(callback_called_); @@ -160,7 +160,7 @@ } TEST_F(ConfigureDisplaysTaskTest, ConfigureWithTwoDisplayFails) { - ConfigureDisplaysTask::ResponseCallback callback = base::Bind( + ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); delegate_.set_max_configurable_pixels(1); @@ -171,7 +171,7 @@ displays_[i].get(), displays_[i]->native_mode(), gfx::Point())); } - ConfigureDisplaysTask task(&delegate_, requests, callback); + ConfigureDisplaysTask task(&delegate_, requests, std::move(callback)); task.Run(); EXPECT_TRUE(callback_called_); @@ -186,7 +186,7 @@ } TEST_F(ConfigureDisplaysTaskTest, ConfigureWithTwoDisplaysPartialSuccess) { - ConfigureDisplaysTask::ResponseCallback callback = base::Bind( + ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); delegate_.set_max_configurable_pixels(small_mode_.size().GetArea()); @@ -197,7 +197,7 @@ displays_[i].get(), displays_[i]->native_mode(), gfx::Point())); } - ConfigureDisplaysTask task(&delegate_, requests, callback); + ConfigureDisplaysTask task(&delegate_, requests, std::move(callback)); task.Run(); EXPECT_TRUE(callback_called_); @@ -212,7 +212,7 @@ } TEST_F(ConfigureDisplaysTaskTest, AsyncConfigureWithTwoDisplaysPartialSuccess) { - ConfigureDisplaysTask::ResponseCallback callback = base::Bind( + ConfigureDisplaysTask::ResponseCallback callback = base::BindOnce( &ConfigureDisplaysTaskTest::ConfigureCallback, base::Unretained(this)); delegate_.set_run_async(true); @@ -224,7 +224,7 @@ displays_[i].get(), displays_[i]->native_mode(), gfx::Point())); } - ConfigureDisplaysTask task(&delegate_, requests, callback); + ConfigureDisplaysTask task(&delegate_, requests, std::move(callback)); task.Run(); EXPECT_FALSE(callback_called_);
diff --git a/ui/display/manager/display_configurator.cc b/ui/display/manager/display_configurator.cc index 754f6a67..b474d32c 100644 --- a/ui/display/manager/display_configurator.cc +++ b/ui/display/manager/display_configurator.cc
@@ -661,8 +661,8 @@ display_control_changing_ = true; native_display_delegate_->TakeDisplayControl( - base::Bind(&DisplayConfigurator::OnDisplayControlTaken, - weak_ptr_factory_.GetWeakPtr(), base::Passed(&callback))); + base::BindOnce(&DisplayConfigurator::OnDisplayControlTaken, + weak_ptr_factory_.GetWeakPtr(), base::Passed(&callback))); } void DisplayConfigurator::OnDisplayControlTaken(DisplayControlCallback callback, @@ -705,8 +705,8 @@ // them for output. SetDisplayPowerInternal( chromeos::DISPLAY_POWER_ALL_OFF, kSetDisplayPowerNoFlags, - base::Bind(&DisplayConfigurator::SendRelinquishDisplayControl, - weak_ptr_factory_.GetWeakPtr(), base::Passed(&callback))); + base::BindOnce(&DisplayConfigurator::SendRelinquishDisplayControl, + weak_ptr_factory_.GetWeakPtr(), base::Passed(&callback))); } void DisplayConfigurator::SendRelinquishDisplayControl( @@ -716,9 +716,9 @@ // Set the flag early such that an incoming configuration event won't start // while we're releasing control of the displays. display_externally_controlled_ = true; - native_display_delegate_->RelinquishDisplayControl( - base::Bind(&DisplayConfigurator::OnDisplayControlRelinquished, - weak_ptr_factory_.GetWeakPtr(), base::Passed(&callback))); + native_display_delegate_->RelinquishDisplayControl(base::BindOnce( + &DisplayConfigurator::OnDisplayControlRelinquished, + weak_ptr_factory_.GetWeakPtr(), base::Passed(&callback))); } else { display_control_changing_ = false; std::move(callback).Run(false); @@ -753,8 +753,8 @@ native_display_delegate_.get(), layout_manager_.get(), requested_display_state_, GetRequestedPowerState(), kSetDisplayPowerForceProbe, /*force_configure=*/true, - base::Bind(&DisplayConfigurator::OnConfigured, - weak_ptr_factory_.GetWeakPtr())); + base::BindOnce(&DisplayConfigurator::OnConfigured, + weak_ptr_factory_.GetWeakPtr())); configuration_task_->Run(); } @@ -794,7 +794,7 @@ void DisplayConfigurator::SetDisplayPowerInternal( chromeos::DisplayPowerState power_state, int flags, - const ConfigurationCallback& callback) { + ConfigurationCallback callback) { // Only skip if the current power state is the same and the latest requested // power state is the same. If |pending_power_state_ != current_power_state_| // then there is a current task pending or the last configuration failed. In @@ -803,14 +803,14 @@ if (power_state == current_power_state_ && power_state == pending_power_state_ && !(flags & kSetDisplayPowerForceProbe)) { - callback.Run(true); + std::move(callback).Run(true); return; } pending_power_state_ = power_state; has_pending_power_state_ = true; pending_power_flags_ = flags; - queued_configuration_callbacks_.push_back(callback); + queued_configuration_callbacks_.push_back(std::move(callback)); if (configure_timer_.IsRunning()) { // If there is a configuration task scheduled, avoid performing @@ -826,9 +826,9 @@ void DisplayConfigurator::SetDisplayPower( chromeos::DisplayPowerState power_state, int flags, - const ConfigurationCallback& callback) { + ConfigurationCallback callback) { if (configurator_disabled()) { - callback.Run(false); + std::move(callback).Run(false); return; } @@ -838,7 +838,7 @@ << (configure_timer_.IsRunning() ? "Running" : "Stopped"); requested_power_state_ = power_state; - SetDisplayPowerInternal(*requested_power_state_, flags, callback); + SetDisplayPowerInternal(*requested_power_state_, flags, std::move(callback)); } void DisplayConfigurator::SetDisplayMode(MultipleDisplayState new_state) { @@ -892,10 +892,9 @@ observers_.RemoveObserver(observer); } -void DisplayConfigurator::SuspendDisplays( - const ConfigurationCallback& callback) { +void DisplayConfigurator::SuspendDisplays(ConfigurationCallback callback) { if (configurator_disabled()) { - callback.Run(false); + std::move(callback).Run(false); return; } @@ -910,7 +909,7 @@ // unless explicitly requested by lucid sleep code). Use // SetDisplayPowerInternal so requested_power_state_ is maintained. SetDisplayPowerInternal(chromeos::DISPLAY_POWER_ALL_OFF, - kSetDisplayPowerNoFlags, callback); + kSetDisplayPowerNoFlags, std::move(callback)); } void DisplayConfigurator::ResumeDisplays() { @@ -968,8 +967,8 @@ native_display_delegate_.get(), layout_manager_.get(), requested_display_state_, pending_power_state_, pending_power_flags_, force_configure_, - base::Bind(&DisplayConfigurator::OnConfigured, - weak_ptr_factory_.GetWeakPtr())); + base::BindOnce(&DisplayConfigurator::OnConfigured, + weak_ptr_factory_.GetWeakPtr())); // Reset the flags before running the task; otherwise it may end up scheduling // another configuration. @@ -1049,15 +1048,15 @@ } void DisplayConfigurator::CallAndClearInProgressCallbacks(bool success) { - for (const auto& callback : in_progress_configuration_callbacks_) - callback.Run(success); + for (auto& callback : in_progress_configuration_callbacks_) + std::move(callback).Run(success); in_progress_configuration_callbacks_.clear(); } void DisplayConfigurator::CallAndClearQueuedCallbacks(bool success) { - for (const auto& callback : queued_configuration_callbacks_) - callback.Run(success); + for (auto& callback : queued_configuration_callbacks_) + std::move(callback).Run(success); queued_configuration_callbacks_.clear(); }
diff --git a/ui/display/manager/display_configurator.h b/ui/display/manager/display_configurator.h index d8e6755..f6b5d23 100644 --- a/ui/display/manager/display_configurator.h +++ b/ui/display/manager/display_configurator.h
@@ -47,7 +47,7 @@ class DISPLAY_MANAGER_EXPORT DisplayConfigurator : public NativeDisplayObserver { public: - using ConfigurationCallback = base::Callback<void(bool /* success */)>; + using ConfigurationCallback = base::OnceCallback<void(bool /* success */)>; using DisplayControlCallback = base::OnceCallback<void(bool success)>; using DisplayStateList = std::vector<DisplaySnapshot*>; @@ -227,7 +227,7 @@ // operation. void SetDisplayPower(chromeos::DisplayPowerState power_state, int flags, - const ConfigurationCallback& callback); + ConfigurationCallback callback); // Force switching the display mode to |new_state|. Returns false if // switching failed (possibly because |new_state| is invalid for the @@ -245,7 +245,7 @@ // configure them for their resume state. This allows faster resume on // machines where display configuration is slow. On completion of the display // configuration |callback| is executed synchronously or asynchronously. - void SuspendDisplays(const ConfigurationCallback& callback); + void SuspendDisplays(ConfigurationCallback callback); // Reprobes displays to handle changes made while the system was // suspended. @@ -291,7 +291,7 @@ // invoked (perhaps synchronously) on completion. void SetDisplayPowerInternal(chromeos::DisplayPowerState power_state, int flags, - const ConfigurationCallback& callback); + ConfigurationCallback callback); // Configures displays. Invoked by |configure_timer_|. void ConfigureDisplays();
diff --git a/ui/display/manager/display_configurator_unittest.cc b/ui/display/manager/display_configurator_unittest.cc index d9c2cb2..e4563cb5 100644 --- a/ui/display/manager/display_configurator_unittest.cc +++ b/ui/display/manager/display_configurator_unittest.cc
@@ -152,16 +152,13 @@ class ConfigurationWaiter { public: explicit ConfigurationWaiter(DisplayConfigurator::TestApi* test_api) - : on_configured_callback_(base::Bind(&ConfigurationWaiter::OnConfigured, - base::Unretained(this))), - test_api_(test_api), - callback_result_(CALLBACK_NOT_CALLED) {} + : test_api_(test_api), callback_result_(CALLBACK_NOT_CALLED) {} ~ConfigurationWaiter() = default; - const DisplayConfigurator::ConfigurationCallback& on_configuration_callback() - const { - return on_configured_callback_; + DisplayConfigurator::ConfigurationCallback on_configuration_callback() { + return base::BindOnce(&ConfigurationWaiter::OnConfigured, + base::Unretained(this)); } CallbackResult callback_result() const { return callback_result_; } @@ -190,9 +187,6 @@ callback_result_ = status ? CALLBACK_SUCCESS : CALLBACK_FAILURE; } - // Passed with configuration requests to run OnConfigured(). - const DisplayConfigurator::ConfigurationCallback on_configured_callback_; - DisplayConfigurator::TestApi* test_api_; // Not owned. // The status of the display configuration.
diff --git a/ui/display/manager/display_util.cc b/ui/display/manager/display_util.cc index 25a4aac..57ce085 100644 --- a/ui/display/manager/display_util.cc +++ b/ui/display/manager/display_util.cc
@@ -49,11 +49,11 @@ // zoom values that includes a zoom level to go to the native resolution of the // display. Ensure that the list of DSFs are in sync with the list of default // device scale factors in display_change_observer.cc. -constexpr std::array<ZoomListBucketDsf, 7> kZoomListBucketsForDsf{{ +constexpr std::array<ZoomListBucketDsf, 6> kZoomListBucketsForDsf{{ {1.25f, {0.7f, 1.f / 1.25f, 0.85f, 0.9f, 0.95f, 1.f, 1.1f, 1.2f, 1.3f}}, {1.6f, {1.f / 1.6f, 0.7f, 0.75f, 0.8f, 0.85f, 0.9f, 1.f, 1.15f, 1.3f}}, - {1.6f, {1.f / 1.6f, 0.7f, 0.75f, 0.8f, 0.85f, 0.9f, 1.f, 1.15f, 1.3f}}, - {1.77777f, {1.f / 1.77777f, 0.7f, 0.8f, 0.9f, 1.f, 1.2f, 1.35f}}, + {1.77777f, + {1.f / 1.77777f, 0.65f, 0.75f, 0.8f, 0.9f, 1.f, 1.1f, 1.2f, 1.3f}}, {2.f, {1.f / 2.f, 0.6f, 0.7f, 0.8f, 0.9f, 1.f, 1.1f, 1.25f, 1.5f}}, {2.25f, {1.f / 2.25f, 0.6f, 0.7f, 0.8f, 0.9f, 1.f, 1.15f, 1.3f, 1.5f}}, {2.66666f,
diff --git a/ui/display/manager/display_utils_unittest.cc b/ui/display/manager/display_utils_unittest.cc index 3468b89..9f0f09f 100644 --- a/ui/display/manager/display_utils_unittest.cc +++ b/ui/display/manager/display_utils_unittest.cc
@@ -69,6 +69,7 @@ checks |= 0x01; if (WithinEpsilon(zoom_values[j], 1.f)) checks |= 0x02; + EXPECT_LT(0.0f, zoom_values[j]); } EXPECT_TRUE(checks & 0x01) << "Inverse of " << dsf << " not on the list."; EXPECT_TRUE(checks & 0x02) << "Zoom level of unity is not on the list.";
diff --git a/ui/display/manager/query_content_protection_task_unittest.cc b/ui/display/manager/query_content_protection_task_unittest.cc index 7404e3b8..26faf97 100644 --- a/ui/display/manager/query_content_protection_task_unittest.cc +++ b/ui/display/manager/query_content_protection_task_unittest.cc
@@ -74,8 +74,8 @@ QueryContentProtectionTask task( &layout_manager, &display_delegate_, 1, - base::Bind(&QueryContentProtectionTaskTest::ResponseCallback, - base::Unretained(this))); + base::BindOnce(&QueryContentProtectionTaskTest::ResponseCallback, + base::Unretained(this))); task.Run(); ASSERT_TRUE(response_); @@ -92,8 +92,8 @@ QueryContentProtectionTask task( &layout_manager, &display_delegate_, 1, - base::Bind(&QueryContentProtectionTaskTest::ResponseCallback, - base::Unretained(this))); + base::BindOnce(&QueryContentProtectionTaskTest::ResponseCallback, + base::Unretained(this))); task.Run(); ASSERT_TRUE(response_); @@ -111,8 +111,8 @@ QueryContentProtectionTask task( &layout_manager, &display_delegate_, 1, - base::Bind(&QueryContentProtectionTaskTest::ResponseCallback, - base::Unretained(this))); + base::BindOnce(&QueryContentProtectionTaskTest::ResponseCallback, + base::Unretained(this))); task.Run(); ASSERT_TRUE(response_); @@ -128,8 +128,8 @@ QueryContentProtectionTask task( &layout_manager, &display_delegate_, 1, - base::Bind(&QueryContentProtectionTaskTest::ResponseCallback, - base::Unretained(this))); + base::BindOnce(&QueryContentProtectionTaskTest::ResponseCallback, + base::Unretained(this))); task.Run(); ASSERT_TRUE(response_); @@ -147,8 +147,8 @@ QueryContentProtectionTask task( &layout_manager, &display_delegate_, 1, - base::Bind(&QueryContentProtectionTaskTest::ResponseCallback, - base::Unretained(this))); + base::BindOnce(&QueryContentProtectionTaskTest::ResponseCallback, + base::Unretained(this))); task.Run(); ASSERT_TRUE(response_); @@ -166,8 +166,8 @@ QueryContentProtectionTask task( &layout_manager, &display_delegate_, 1, - base::Bind(&QueryContentProtectionTaskTest::ResponseCallback, - base::Unretained(this))); + base::BindOnce(&QueryContentProtectionTaskTest::ResponseCallback, + base::Unretained(this))); task.Run(); ASSERT_TRUE(response_); @@ -185,8 +185,8 @@ QueryContentProtectionTask task( &layout_manager, &display_delegate_, 1, - base::Bind(&QueryContentProtectionTaskTest::ResponseCallback, - base::Unretained(this))); + base::BindOnce(&QueryContentProtectionTaskTest::ResponseCallback, + base::Unretained(this))); task.Run(); ASSERT_TRUE(response_); @@ -205,8 +205,8 @@ QueryContentProtectionTask task( &layout_manager, &display_delegate_, 1, - base::Bind(&QueryContentProtectionTaskTest::ResponseCallback, - base::Unretained(this))); + base::BindOnce(&QueryContentProtectionTaskTest::ResponseCallback, + base::Unretained(this))); task.Run(); ASSERT_TRUE(response_); @@ -226,8 +226,8 @@ QueryContentProtectionTask task1( &layout_manager, &display_delegate_, 1, - base::Bind(&QueryContentProtectionTaskTest::ResponseCallback, - base::Unretained(this))); + base::BindOnce(&QueryContentProtectionTaskTest::ResponseCallback, + base::Unretained(this))); task1.Run(); ASSERT_TRUE(response_); @@ -241,8 +241,8 @@ QueryContentProtectionTask task2( &layout_manager, &display_delegate_, 2, - base::Bind(&QueryContentProtectionTaskTest::ResponseCallback, - base::Unretained(this))); + base::BindOnce(&QueryContentProtectionTaskTest::ResponseCallback, + base::Unretained(this))); task2.Run(); ASSERT_TRUE(response_);
diff --git a/ui/display/manager/update_display_configuration_task.cc b/ui/display/manager/update_display_configuration_task.cc index d7e51730..1c35b18 100644 --- a/ui/display/manager/update_display_configuration_task.cc +++ b/ui/display/manager/update_display_configuration_task.cc
@@ -20,14 +20,14 @@ chromeos::DisplayPowerState new_power_state, int power_flags, bool force_configure, - const ResponseCallback& callback) + ResponseCallback callback) : delegate_(delegate), layout_manager_(layout_manager), new_display_state_(new_display_state), new_power_state_(new_power_state), power_flags_(power_flags), force_configure_(force_configure), - callback_(callback), + callback_(std::move(callback)), requesting_displays_(false) { delegate_->AddObserver(this); } @@ -39,8 +39,8 @@ void UpdateDisplayConfigurationTask::Run() { requesting_displays_ = true; delegate_->GetDisplays( - base::Bind(&UpdateDisplayConfigurationTask::OnDisplaysUpdated, - weak_ptr_factory_.GetWeakPtr())); + base::BindOnce(&UpdateDisplayConfigurationTask::OnDisplaysUpdated, + weak_ptr_factory_.GetWeakPtr())); } void UpdateDisplayConfigurationTask::OnConfigurationChanged() {} @@ -72,8 +72,8 @@ << " force_configure=" << force_configure_ << " display_count=" << cached_displays_.size(); if (ShouldConfigure()) { - EnterState(base::Bind(&UpdateDisplayConfigurationTask::OnStateEntered, - weak_ptr_factory_.GetWeakPtr())); + EnterState(base::BindOnce(&UpdateDisplayConfigurationTask::OnStateEntered, + weak_ptr_factory_.GetWeakPtr())); } else { // If we don't have to configure then we're sticking with the old // configuration. Update it such that it reflects in the reported value. @@ -83,21 +83,21 @@ } void UpdateDisplayConfigurationTask::EnterState( - const ConfigureDisplaysTask::ResponseCallback& callback) { + ConfigureDisplaysTask::ResponseCallback callback) { VLOG(2) << "EnterState"; std::vector<DisplayConfigureRequest> requests; if (!layout_manager_->GetDisplayLayout(cached_displays_, new_display_state_, new_power_state_, &requests)) { - callback.Run(ConfigureDisplaysTask::ERROR); + std::move(callback).Run(ConfigureDisplaysTask::ERROR); return; } if (!requests.empty()) { configure_task_.reset( - new ConfigureDisplaysTask(delegate_, requests, callback)); + new ConfigureDisplaysTask(delegate_, requests, std::move(callback))); configure_task_->Run(); } else { VLOG(2) << "No displays"; - callback.Run(ConfigureDisplaysTask::SUCCESS); + std::move(callback).Run(ConfigureDisplaysTask::SUCCESS); } } @@ -116,7 +116,7 @@ layout_manager_->GetPowerState() != new_power_state_ || force_configure_) { new_display_state_ = MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED; - EnterState(base::Bind( + EnterState(base::BindOnce( &UpdateDisplayConfigurationTask::OnEnableSoftwareMirroring, weak_ptr_factory_.GetWeakPtr())); return; @@ -145,8 +145,9 @@ } void UpdateDisplayConfigurationTask::FinishConfiguration(bool success) { - callback_.Run(success, cached_displays_, cached_unassociated_displays_, - new_display_state_, new_power_state_); + std::move(callback_).Run(success, cached_displays_, + cached_unassociated_displays_, new_display_state_, + new_power_state_); } bool UpdateDisplayConfigurationTask::ShouldForceDpms() const {
diff --git a/ui/display/manager/update_display_configuration_task.h b/ui/display/manager/update_display_configuration_task.h index 79cc57a..846f344 100644 --- a/ui/display/manager/update_display_configuration_task.h +++ b/ui/display/manager/update_display_configuration_task.h
@@ -24,7 +24,7 @@ class DISPLAY_MANAGER_EXPORT UpdateDisplayConfigurationTask : public NativeDisplayObserver { public: - using ResponseCallback = base::RepeatingCallback<void( + using ResponseCallback = base::OnceCallback<void( /*success=*/bool, /*displays=*/const std::vector<DisplaySnapshot*>&, /*unassociated_displays=*/const std::vector<DisplaySnapshot*>&, @@ -37,7 +37,7 @@ chromeos::DisplayPowerState new_power_state, int power_flags, bool force_configure, - const ResponseCallback& callback); + ResponseCallback callback); ~UpdateDisplayConfigurationTask() override; void Run(); @@ -62,7 +62,7 @@ // Starts the configuration process. |callback| is used to continue the task // after |configure_taks_| finishes executing. - void EnterState(const ConfigureDisplaysTask::ResponseCallback& callback); + void EnterState(ConfigureDisplaysTask::ResponseCallback callback); // Finishes display configuration and runs |callback_|. void FinishConfiguration(bool success);
diff --git a/ui/display/manager/update_display_configuration_task_unittest.cc b/ui/display/manager/update_display_configuration_task_unittest.cc index 58e5788..c851883 100644 --- a/ui/display/manager/update_display_configuration_task_unittest.cc +++ b/ui/display/manager/update_display_configuration_task_unittest.cc
@@ -217,8 +217,8 @@ UpdateDisplayConfigurationTask task( &delegate_, &layout_manager_, MULTIPLE_DISPLAY_STATE_HEADLESS, chromeos::DISPLAY_POWER_ALL_ON, 0, false, - base::Bind(&UpdateDisplayConfigurationTaskTest::ResponseCallback, - base::Unretained(this))); + base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, + base::Unretained(this))); task.Run(); } @@ -236,8 +236,8 @@ UpdateDisplayConfigurationTask task( &delegate_, &layout_manager_, MULTIPLE_DISPLAY_STATE_SINGLE, chromeos::DISPLAY_POWER_ALL_ON, 0, false, - base::Bind(&UpdateDisplayConfigurationTaskTest::ResponseCallback, - base::Unretained(this))); + base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, + base::Unretained(this))); task.Run(); } @@ -259,8 +259,8 @@ UpdateDisplayConfigurationTask task( &delegate_, &layout_manager_, MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED, chromeos::DISPLAY_POWER_ALL_ON, 0, false, - base::Bind(&UpdateDisplayConfigurationTaskTest::ResponseCallback, - base::Unretained(this))); + base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, + base::Unretained(this))); task.Run(); } @@ -285,8 +285,8 @@ UpdateDisplayConfigurationTask task( &delegate_, &layout_manager_, MULTIPLE_DISPLAY_STATE_MULTI_MIRROR, chromeos::DISPLAY_POWER_ALL_ON, 0, false, - base::Bind(&UpdateDisplayConfigurationTaskTest::ResponseCallback, - base::Unretained(this))); + base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, + base::Unretained(this))); task.Run(); } @@ -310,8 +310,8 @@ UpdateDisplayConfigurationTask task( &delegate_, &layout_manager_, MULTIPLE_DISPLAY_STATE_MULTI_MIRROR, chromeos::DISPLAY_POWER_ALL_ON, 0, false, - base::Bind(&UpdateDisplayConfigurationTaskTest::ResponseCallback, - base::Unretained(this))); + base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, + base::Unretained(this))); task.Run(); } @@ -328,8 +328,8 @@ UpdateDisplayConfigurationTask task( &delegate_, &layout_manager_, MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED, chromeos::DISPLAY_POWER_ALL_ON, 0, false, - base::Bind(&UpdateDisplayConfigurationTaskTest::ResponseCallback, - base::Unretained(this))); + base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, + base::Unretained(this))); task.Run(); } @@ -355,8 +355,8 @@ UpdateDisplayConfigurationTask task( &delegate_, &layout_manager_, MULTIPLE_DISPLAY_STATE_SINGLE, chromeos::DISPLAY_POWER_ALL_ON, 0, false, - base::Bind(&UpdateDisplayConfigurationTaskTest::ResponseCallback, - base::Unretained(this))); + base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, + base::Unretained(this))); task.Run(); } @@ -375,8 +375,8 @@ UpdateDisplayConfigurationTask task( &delegate_, &layout_manager_, MULTIPLE_DISPLAY_STATE_SINGLE, chromeos::DISPLAY_POWER_ALL_OFF, 0, false, - base::Bind(&UpdateDisplayConfigurationTaskTest::ResponseCallback, - base::Unretained(this))); + base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, + base::Unretained(this))); task.Run(); } @@ -399,8 +399,8 @@ UpdateDisplayConfigurationTask task( &delegate_, &layout_manager_, MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED, chromeos::DISPLAY_POWER_ALL_ON, 0, false, - base::Bind(&UpdateDisplayConfigurationTaskTest::ResponseCallback, - base::Unretained(this))); + base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, + base::Unretained(this))); task.Run(); } @@ -410,8 +410,8 @@ UpdateDisplayConfigurationTask task( &delegate_, &layout_manager_, MULTIPLE_DISPLAY_STATE_MULTI_MIRROR, chromeos::DISPLAY_POWER_ALL_ON, 0, false, - base::Bind(&UpdateDisplayConfigurationTaskTest::ResponseCallback, - base::Unretained(this))); + base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, + base::Unretained(this))); task.Run(); } @@ -433,8 +433,8 @@ UpdateDisplayConfigurationTask task( &delegate_, &layout_manager_, MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED, chromeos::DISPLAY_POWER_ALL_ON, 0, false, - base::Bind(&UpdateDisplayConfigurationTaskTest::ResponseCallback, - base::Unretained(this))); + base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, + base::Unretained(this))); task.Run(); } @@ -444,8 +444,8 @@ UpdateDisplayConfigurationTask task( &delegate_, &layout_manager_, MULTIPLE_DISPLAY_STATE_MULTI_MIRROR, chromeos::DISPLAY_POWER_ALL_ON, 0, true /* force_configure */, - base::Bind(&UpdateDisplayConfigurationTaskTest::ResponseCallback, - base::Unretained(this))); + base::BindOnce(&UpdateDisplayConfigurationTaskTest::ResponseCallback, + base::Unretained(this))); task.Run(); }
diff --git a/ui/display/win/color_profile_reader.cc b/ui/display/win/color_profile_reader.cc index 71af8e68..fdfcacb 100644 --- a/ui/display/win/color_profile_reader.cc +++ b/ui/display/win/color_profile_reader.cc
@@ -63,10 +63,10 @@ base::PostTaskAndReplyWithResult( FROM_HERE, {base::ThreadPool(), base::MayBlock(), base::TaskPriority::BEST_EFFORT}, - base::Bind(&ColorProfileReader::ReadProfilesOnBackgroundThread, - new_device_to_path_map), - base::Bind(&ColorProfileReader::ReadProfilesCompleted, - weak_factory_.GetWeakPtr())); + base::BindOnce(&ColorProfileReader::ReadProfilesOnBackgroundThread, + new_device_to_path_map), + base::BindOnce(&ColorProfileReader::ReadProfilesCompleted, + weak_factory_.GetWeakPtr())); } // static
diff --git a/ui/display/win/screen_win.cc b/ui/display/win/screen_win.cc index 200c336..53b6124 100644 --- a/ui/display/win/screen_win.cc +++ b/ui/display/win/screen_win.cc
@@ -679,9 +679,8 @@ void ScreenWin::Initialize() { color_profile_reader_->UpdateIfNeeded(); - singleton_hwnd_observer_.reset( - new gfx::SingletonHwndObserver( - base::Bind(&ScreenWin::OnWndProc, base::Unretained(this)))); + singleton_hwnd_observer_.reset(new gfx::SingletonHwndObserver( + base::BindRepeating(&ScreenWin::OnWndProc, base::Unretained(this)))); UpdateFromDisplayInfos(GetDisplayInfosFromSystem()); RecordDisplayScaleFactors();
diff --git a/ui/gl/yuv_to_rgb_converter.cc b/ui/gl/yuv_to_rgb_converter.cc index 0dc4d466..e85c85dd 100644 --- a/ui/gl/yuv_to_rgb_converter.cc +++ b/ui/gl/yuv_to_rgb_converter.cc
@@ -14,6 +14,11 @@ namespace gl { namespace { +const char kVertexHeaderES2[] = + "precision mediump float;\n" + "#define ATTRIBUTE attribute\n" + "#define VARYING varying\n"; + const char kVertexHeaderES3[] = "#version 300 es\n" "precision mediump float;\n" @@ -30,6 +35,13 @@ "#define ATTRIBUTE in\n" "#define VARYING out\n"; +const char kFragmentHeaderES2[] = + "#extension GL_ARB_texture_rectangle : require\n" + "precision mediump float;\n" + "#define VARYING varying\n" + "#define FRAGCOLOR gl_FragColor\n" + "#define TEX texture2DRect\n"; + const char kFragmentHeaderES3[] = "#version 300 es\n" "precision mediump float;\n" @@ -89,27 +101,33 @@ DCHECK(color_transform->CanGetShaderSource()); std::string do_color_conversion = color_transform->GetShaderSource(); - bool use_es3 = gl_version_info.is_es3; - bool use_core_profile = gl_version_info.is_desktop_core_profile; + const char* fragment_header = nullptr; + const char* vertex_header = nullptr; + if (gl_version_info.is_es2) { + vertex_header = kVertexHeaderES2; + fragment_header = kFragmentHeaderES2; + } else if (gl_version_info.is_es3) { + vertex_header = kVertexHeaderES3; + fragment_header = kFragmentHeaderES3; + } else if (gl_version_info.is_desktop_core_profile) { + vertex_header = kVertexHeaderCoreProfile; + fragment_header = kFragmentHeaderCoreProfile; + } else { + DCHECK(!gl_version_info.is_es); + vertex_header = kVertexHeaderCompatiblityProfile; + fragment_header = kFragmentHeaderCompatiblityProfile; + } + DCHECK(vertex_header && fragment_header); + glGenFramebuffersEXT(1, &framebuffer_); vertex_buffer_ = GLHelper::SetupQuadVertexBuffer(); vertex_shader_ = GLHelper::LoadShader( GL_VERTEX_SHADER, - base::StringPrintf( - "%s\n%s", - use_es3 ? kVertexHeaderES3 - : (use_core_profile ? kVertexHeaderCoreProfile - : kVertexHeaderCompatiblityProfile), - kVertexShader) - .c_str()); + base::StringPrintf("%s\n%s", vertex_header, kVertexShader).c_str()); fragment_shader_ = GLHelper::LoadShader( GL_FRAGMENT_SHADER, - base::StringPrintf( - "%s\n%s\n%s", - use_es3 ? kFragmentHeaderES3 - : (use_core_profile ? kFragmentHeaderCoreProfile - : kFragmentHeaderCompatiblityProfile), - do_color_conversion.c_str(), kFragmentShader) + base::StringPrintf("%s\n%s\n%s", fragment_header, + do_color_conversion.c_str(), kFragmentShader) .c_str()); program_ = GLHelper::SetupProgram(vertex_shader_, fragment_shader_); @@ -127,7 +145,9 @@ glUniform1i(y_sampler_location, 0); glUniform1i(uv_sampler_location, 1); - if (use_es3 || use_core_profile) { + bool has_vertex_array_objects = + gl_version_info.is_es3 || gl_version_info.is_desktop_core_profile; + if (has_vertex_array_objects) { glGenVertexArraysOES(1, &vertex_array_object_); } }
diff --git a/ui/login/display_manager.js b/ui/login/display_manager.js index d72f89a..9f0942b 100644 --- a/ui/login/display_manager.js +++ b/ui/login/display_manager.js
@@ -293,9 +293,11 @@ * @param {number} height client area height */ setClientAreaSize: function(width, height) { - var clientArea = $('outer-container'); - var bottom = parseInt(window.getComputedStyle(clientArea).bottom); - clientArea.style.minHeight = cr.ui.toCssPx(height - bottom); + if (!cr.isChromeOS) { + var clientArea = $('outer-container'); + var bottom = parseInt(window.getComputedStyle(clientArea).bottom); + clientArea.style.minHeight = cr.ui.toCssPx(height - bottom); + } }, /**
diff --git a/ui/ozone/platform/wayland/BUILD.gn b/ui/ozone/platform/wayland/BUILD.gn index 24a14654..6c553dd 100644 --- a/ui/ozone/platform/wayland/BUILD.gn +++ b/ui/ozone/platform/wayland/BUILD.gn
@@ -292,6 +292,7 @@ "//ui/ozone:test_support", "//ui/ozone/common/linux:drm", "//ui/ozone/common/linux:gbm", + "//ui/platform_window/platform_window_handler", ] import("//ui/base/ui_features.gni")
diff --git a/ui/ozone/platform/wayland/DEPS b/ui/ozone/platform/wayland/DEPS index 32f3223e..60eec9e 100644 --- a/ui/ozone/platform/wayland/DEPS +++ b/ui/ozone/platform/wayland/DEPS
@@ -5,6 +5,7 @@ "+mojo/public", "+ui/base/clipboard/clipboard_constants.h", "+ui/base/dragdrop/drag_drop_types.h", + "+ui/base/dragdrop/file_info.h", "+ui/base/dragdrop/os_exchange_data.h", "+ui/base/dragdrop/os_exchange_data_provider_aura.h", ]
diff --git a/ui/ozone/platform/wayland/common/wayland_util.cc b/ui/ozone/platform/wayland/common/wayland_util.cc index 1bc9349..068ce0f8 100644 --- a/ui/ozone/platform/wayland/common/wayland_util.cc +++ b/ui/ozone/platform/wayland/common/wayland_util.cc
@@ -131,12 +131,12 @@ return true; } -void ReadDataFromFD(base::ScopedFD fd, std::string* contents) { +void ReadDataFromFD(base::ScopedFD fd, std::vector<uint8_t>* contents) { DCHECK(contents); - char buffer[1 << 10]; // 1 kB in bytes. + uint8_t buffer[1 << 10]; // 1 kB in bytes. ssize_t length; while ((length = read(fd.get(), buffer, sizeof(buffer))) > 0) - contents->append(buffer, length); + contents->insert(contents->end(), buffer, buffer + length); } } // namespace wl
diff --git a/ui/ozone/platform/wayland/common/wayland_util.h b/ui/ozone/platform/wayland/common/wayland_util.h index dd7ec71..4b69969 100644 --- a/ui/ozone/platform/wayland/common/wayland_util.h +++ b/ui/ozone/platform/wayland/common/wayland_util.h
@@ -5,7 +5,7 @@ #ifndef UI_OZONE_PLATFORM_WAYLAND_COMMON_WAYLAND_UTIL_H_ #define UI_OZONE_PLATFORM_WAYLAND_COMMON_WAYLAND_UTIL_H_ -#include <string> +#include <vector> #include <wayland-client.h> @@ -43,7 +43,7 @@ bool DrawBitmap(const SkBitmap& bitmap, ui::WaylandShmBuffer* out_buffer); // Helper function to read data from a file. -void ReadDataFromFD(base::ScopedFD fd, std::string* contents); +void ReadDataFromFD(base::ScopedFD fd, std::vector<uint8_t>* contents); } // namespace wl
diff --git a/ui/ozone/platform/wayland/host/gtk_primary_selection_device.cc b/ui/ozone/platform/wayland/host/gtk_primary_selection_device.cc index 70be8577..d1e49b7 100644 --- a/ui/ozone/platform/wayland/host/gtk_primary_selection_device.cc +++ b/ui/ozone/platform/wayland/host/gtk_primary_selection_device.cc
@@ -53,7 +53,7 @@ self->ResetDataOffer(); // Clear Clipboard cache. - self->connection()->clipboard()->SetData(std::string(), std::string()); + self->connection()->clipboard()->SetData({}, {}); return; }
diff --git a/ui/ozone/platform/wayland/host/internal/wayland_data_device_base.cc b/ui/ozone/platform/wayland/host/internal/wayland_data_device_base.cc index bfaa6d7..ca94ce9 100644 --- a/ui/ozone/platform/wayland/host/internal/wayland_data_device_base.cc +++ b/ui/ozone/platform/wayland/host/internal/wayland_data_device_base.cc
@@ -54,7 +54,7 @@ void WaylandDataDeviceBase::ReadClipboardDataFromFD( base::ScopedFD fd, const std::string& mime_type) { - std::string contents; + std::vector<uint8_t> contents; wl::ReadDataFromFD(std::move(fd), &contents); connection_->clipboard()->SetData(contents, mime_type); } @@ -89,8 +89,14 @@ void WaylandDataDeviceBase::DeferredReadCallbackInternal(struct wl_callback* cb, uint32_t time) { DCHECK(!deferred_read_closure_.is_null()); - std::move(deferred_read_closure_).Run(); + + // The callback must be reset before invoking the closure because the latter + // may want to set another callback. That typically happens when non-trivial + // data types are dropped; they have fallbacks to plain text so several + // roundtrips to data are chained. deferred_read_callback_.reset(); + + std::move(deferred_read_closure_).Run(); } } // namespace internal
diff --git a/ui/ozone/platform/wayland/host/internal/wayland_data_device_base.h b/ui/ozone/platform/wayland/host/internal/wayland_data_device_base.h index 824bfc8..f5ff764 100644 --- a/ui/ozone/platform/wayland/host/internal/wayland_data_device_base.h +++ b/ui/ozone/platform/wayland/host/internal/wayland_data_device_base.h
@@ -58,8 +58,7 @@ struct wl_callback* cb, uint32_t time); - void DeferredReadCallbackInternal(struct wl_callback* cb, - uint32_t time); + void DeferredReadCallbackInternal(struct wl_callback* cb, uint32_t time); // Used to call out to WaylandConnection once clipboard data // has been successfully read.
diff --git a/ui/ozone/platform/wayland/host/wayland_clipboard.cc b/ui/ozone/platform/wayland/host/wayland_clipboard.cc index 1b07e98..dd8c2326 100644 --- a/ui/ozone/platform/wayland/host/wayland_clipboard.cc +++ b/ui/ozone/platform/wayland/host/wayland_clipboard.cc
@@ -108,13 +108,12 @@ } } -void WaylandClipboard::SetData(const std::string& contents, +void WaylandClipboard::SetData(const std::vector<uint8_t>& contents, const std::string& mime_type) { if (!data_map_) return; - (*data_map_)[mime_type] = - std::vector<uint8_t>(contents.begin(), contents.end()); + (*data_map_)[mime_type] = contents; if (!read_clipboard_closure_.is_null()) { auto it = data_map_->find(mime_type);
diff --git a/ui/ozone/platform/wayland/host/wayland_clipboard.h b/ui/ozone/platform/wayland/host/wayland_clipboard.h index e720bdd2..cd95b60 100644 --- a/ui/ozone/platform/wayland/host/wayland_clipboard.h +++ b/ui/ozone/platform/wayland/host/wayland_clipboard.h
@@ -5,6 +5,9 @@ #ifndef UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_CLIPBOARD_H_ #define UI_OZONE_PLATFORM_WAYLAND_HOST_WAYLAND_CLIPBOARD_H_ +#include <string> +#include <vector> + #include "base/callback.h" #include "base/macros.h" #include "ui/ozone/platform/wayland/host/wayland_data_source.h" @@ -49,7 +52,8 @@ PlatformClipboard::SequenceNumberUpdateCb cb) override; void DataSourceCancelled(ClipboardBuffer buffer); - void SetData(const std::string& contents, const std::string& mime_type); + void SetData(const std::vector<uint8_t>& contents, + const std::string& mime_type); void UpdateSequenceNumber(ClipboardBuffer buffer); private:
diff --git a/ui/ozone/platform/wayland/host/wayland_connection.cc b/ui/ozone/platform/wayland/host/wayland_connection.cc index 232f853..03d575a 100644 --- a/ui/ozone/platform/wayland/host/wayland_connection.cc +++ b/ui/ozone/platform/wayland/host/wayland_connection.cc
@@ -169,7 +169,7 @@ void WaylandConnection::RequestDragData( const std::string& mime_type, - base::OnceCallback<void(const std::string&)> callback) { + base::OnceCallback<void(const std::vector<uint8_t>&)> callback) { data_device_->RequestDragData(mime_type, std::move(callback)); }
diff --git a/ui/ozone/platform/wayland/host/wayland_connection.h b/ui/ozone/platform/wayland/host/wayland_connection.h index e26d191f..a241c70c 100644 --- a/ui/ozone/platform/wayland/host/wayland_connection.h +++ b/ui/ozone/platform/wayland/host/wayland_connection.h
@@ -119,8 +119,9 @@ // Requests the data to the platform when Chromium gets drag-and-drop started // by others. Once reading the data from platform is done, |callback| should // be called with the data. - void RequestDragData(const std::string& mime_type, - base::OnceCallback<void(const std::string&)> callback); + void RequestDragData( + const std::string& mime_type, + base::OnceCallback<void(const std::vector<uint8_t>&)> callback); // Returns true when dragging is entered or started. bool IsDragInProgress();
diff --git a/ui/ozone/platform/wayland/host/wayland_data_device.cc b/ui/ozone/platform/wayland/host/wayland_data_device.cc index 5c56235..20a347a 100644 --- a/ui/ozone/platform/wayland/host/wayland_data_device.cc +++ b/ui/ozone/platform/wayland/host/wayland_data_device.cc
@@ -9,58 +9,182 @@ #include "base/bind.h" #include "base/strings/string16.h" +#include "base/strings/string_split.h" #include "base/strings/utf_string_conversions.h" #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkCanvas.h" #include "ui/base/clipboard/clipboard_constants.h" #include "ui/base/dragdrop/drag_drop_types.h" +#include "ui/base/dragdrop/file_info.h" #include "ui/base/dragdrop/os_exchange_data.h" #include "ui/base/dragdrop/os_exchange_data_provider_aura.h" #include "ui/ozone/platform/wayland/common/wayland_util.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" #include "ui/ozone/platform/wayland/host/wayland_window.h" +#include "url/gurl.h" +#include "url/url_canon.h" +#include "url/url_util.h" namespace ui { namespace { -int GetOperation(uint32_t source_actions, uint32_t dnd_action) { +constexpr OSExchangeData::FilenameToURLPolicy kFilenameToURLPolicy = + OSExchangeData::FilenameToURLPolicy::CONVERT_FILENAMES; + +// Converts raw data to either narrow or wide string. +template <typename StringType> +StringType BytesTo(const PlatformClipboard::Data& bytes) { + if (bytes.size() % sizeof(typename StringType::value_type) != 0U) { + // This is suspicious. + LOG(WARNING) + << "Data is possibly truncated, or a wrong conversion is requested."; + } + + StringType result; + result.assign(reinterpret_cast<typename StringType::const_pointer>(&bytes[0]), + bytes.size() / sizeof(typename StringType::value_type)); + return result; +} + +// Returns actions possible with the given source and drag'n'drop actions. +// Also converts enums: input params are wl_data_device_manager_dnd_action but +// the result is ui::DragDropTypes. +int GetPossibleActions(uint32_t source_actions, uint32_t dnd_action) { + // If drag'n'drop action is set, use it but check for ASK action (see below). uint32_t action = dnd_action != WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE ? dnd_action : source_actions; + // We accept any action except ASK (see below). int operation = DragDropTypes::DRAG_NONE; if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) operation |= DragDropTypes::DRAG_COPY; if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE) operation |= DragDropTypes::DRAG_MOVE; - // TODO(jkim): Implement branch for WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK - if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK) - operation |= DragDropTypes::DRAG_COPY; + if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK) { + // This is very rare and non-standard. Chromium doesn't set this when + // anything is dragged from it, neither it provides any UI for asking + // the user about the desired drag'n'drop action when data is dragged + // from an external source. + // We are safe with not adding anything here. However, keep NOTIMPLEMENTED + // for an (unlikely) event of this being hit in distant future. + NOTIMPLEMENTED_LOG_ONCE(); + } return operation; } -void AddStringToOSExchangeData(const std::string& data, - OSExchangeData* os_exchange_data) { +void AddString(const PlatformClipboard::Data& data, + OSExchangeData* os_exchange_data) { DCHECK(os_exchange_data); + if (data.empty()) return; - base::string16 string16 = base::UTF8ToUTF16(data); - os_exchange_data->SetString(string16); + os_exchange_data->SetString(base::UTF8ToUTF16(BytesTo<std::string>(data))); } -void AddToOSExchangeData(const std::string& data, +void AddHtml(const PlatformClipboard::Data& data, + OSExchangeData* os_exchange_data) { + DCHECK(os_exchange_data); + + if (data.empty()) + return; + + os_exchange_data->SetHtml(base::UTF8ToUTF16(BytesTo<std::string>(data)), + GURL()); +} + +// Parses |data| as if it had text/uri-list format. Its brief spec is: +// 1. Any lines beginning with the '#' character are comment lines. +// 2. Non-comment lines shall be URIs (URNs or URLs). +// 3. Lines are terminated with a CRLF pair. +// 4. URL encoding is used. +void AddFiles(const PlatformClipboard::Data& data, + OSExchangeData* os_exchange_data) { + DCHECK(os_exchange_data); + + std::string data_as_string = BytesTo<std::string>(data); + + const auto lines = base::SplitString( + data_as_string, "\r\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); + std::vector<FileInfo> filenames; + for (const auto& line : lines) { + if (line.empty() || line[0] == '#') + continue; + GURL url(line); + if (!url.is_valid() || !url.SchemeIsFile()) { + LOG(WARNING) << "Invalid URI found: " << line; + continue; + } + + std::string url_path = url.path(); + url::RawCanonOutputT<base::char16> unescaped; + url::DecodeURLEscapeSequences(url_path.data(), url_path.size(), + url::DecodeURLMode::kUTF8OrIsomorphic, + &unescaped); + + std::string path8; + base::UTF16ToUTF8(unescaped.data(), unescaped.length(), &path8); + const base::FilePath path(path8); + filenames.push_back({path, path.BaseName()}); + } + if (filenames.empty()) + return; + + os_exchange_data->SetFilenames(filenames); +} + +// Parses |data| as if it had text/x-moz-url format, which is basically +// two lines separated with newline, where the first line is the URL and +// the second one is page title. The unpleasant feature of text/x-moz-url is +// that the URL has UTF-16 encoding. +void AddUrl(const PlatformClipboard::Data& data, + OSExchangeData* os_exchange_data) { + DCHECK(os_exchange_data); + + if (data.empty()) + return; + + base::string16 data_as_string16 = BytesTo<base::string16>(data); + + const auto lines = + base::SplitString(data_as_string16, base::ASCIIToUTF16("\r\n"), + base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); + if (lines.size() != 2U) { + LOG(WARNING) << "Invalid data passed as text/x-moz-url; it must contain " + << "exactly 2 lines but has " << lines.size() << " instead."; + return; + } + GURL url(lines[0]); + if (!url.is_valid()) { + LOG(WARNING) << "Invalid data passed as text/x-moz-url; the first line " + << "must contain a valid URL but it doesn't."; + return; + } + + os_exchange_data->SetURL(url, lines[1]); +} + +void AddToOSExchangeData(const PlatformClipboard::Data& data, const std::string& mime_type, OSExchangeData* os_exchange_data) { DCHECK(os_exchange_data); if ((mime_type == kMimeTypeText || mime_type == kMimeTypeTextUtf8)) { DCHECK(!os_exchange_data->HasString()); - AddStringToOSExchangeData(data, os_exchange_data); - return; + AddString(data, os_exchange_data); + } else if (mime_type == kMimeTypeHTML) { + DCHECK(!os_exchange_data->HasHtml()); + AddHtml(data, os_exchange_data); + } else if (mime_type == kMimeTypeMozillaURL) { + DCHECK(!os_exchange_data->HasURL(kFilenameToURLPolicy)); + AddUrl(data, os_exchange_data); + } else if (mime_type == kMimeTypeURIList) { + DCHECK(!os_exchange_data->HasFile()); + AddFiles(data, os_exchange_data); + } else { + LOG(WARNING) << "Unhandled MIME type: " << mime_type; } - // TODO(crbug.com/875164): Fix mime types support. - NOTREACHED(); } } // namespace @@ -80,7 +204,7 @@ void WaylandDataDevice::RequestDragData( const std::string& mime_type, - base::OnceCallback<void(const std::string&)> callback) { + base::OnceCallback<void(const PlatformClipboard::Data&)> callback) { base::ScopedFD fd = drag_offer_->Receive(mime_type); if (!fd.is_valid()) { LOG(ERROR) << "Failed to open file descriptor."; @@ -100,25 +224,24 @@ DCHECK(buffer); DCHECK(source_data_); - if (mime_type != kMimeTypeText && mime_type != kMimeTypeTextUtf8) - return; - - const OSExchangeData::FilenameToURLPolicy policy = - OSExchangeData::FilenameToURLPolicy::DO_NOT_CONVERT_FILENAMES; - // TODO(jkim): Handle other data format as well. - if (source_data_->HasURL(policy)) { + if (mime_type == kMimeTypeMozillaURL && + source_data_->HasURL(kFilenameToURLPolicy)) { GURL url; base::string16 title; - source_data_->GetURLAndTitle(policy, &url, &title); + source_data_->GetURLAndTitle(kFilenameToURLPolicy, &url, &title); buffer->append(url.spec()); - return; - } - - if (source_data_->HasString()) { + } else if (mime_type == kMimeTypeHTML && source_data_->HasHtml()) { + base::string16 data; + GURL base_url; + source_data_->GetHtml(&data, &base_url); + buffer->append(base::UTF16ToUTF8(data)); + } else if (source_data_->HasString()) { base::string16 data; source_data_->GetString(&data); buffer->append(base::UTF16ToUTF8(data)); - return; + } else { + LOG(WARNING) << "Cannot deliver data of type " << mime_type + << " and no text representation is available."; } } @@ -147,8 +270,8 @@ void WaylandDataDevice::ReadDragDataFromFD( base::ScopedFD fd, - base::OnceCallback<void(const std::string&)> callback) { - std::string contents; + base::OnceCallback<void(const PlatformClipboard::Data&)> callback) { + PlatformClipboard::Data contents; wl::ReadDataFromFD(std::move(fd), &contents); std::move(callback).Run(contents); } @@ -193,7 +316,7 @@ self->drag_offer_ = std::move(self->new_offer_); self->window_ = window; - // TODO(crbug.com/875164): Set mime type the client can accept. Now it sets + // TODO(crbug.com/1004715): Set mime type the client can accept. Now it sets // all mime types offered because current implementation doesn't decide // action based on mime type. self->unprocessed_mime_types_.clear(); @@ -202,17 +325,18 @@ self->drag_offer_->Accept(serial, mime); } - int operation = GetOperation(self->drag_offer_->source_actions(), - self->drag_offer_->dnd_action()); gfx::PointF point(wl_fixed_to_double(x), wl_fixed_to_double(y)); // If |source_data_| is set, it means that dragging is started from the // same window and it's not needed to read data through Wayland. - std::unique_ptr<OSExchangeData> pdata; + std::unique_ptr<OSExchangeData> dragged_data; if (!self->IsDraggingExternalData()) - pdata = std::make_unique<OSExchangeData>( + dragged_data = std::make_unique<OSExchangeData>( self->source_data_->provider().Clone()); - self->window_->OnDragEnter(point, std::move(pdata), operation); + self->window_->OnDragEnter( + point, std::move(dragged_data), + GetPossibleActions(self->drag_offer_->source_actions(), + self->drag_offer_->dnd_action())); } void WaylandDataDevice::OnMotion(void* data, @@ -226,10 +350,11 @@ return; } - int operation = GetOperation(self->drag_offer_->source_actions(), - self->drag_offer_->dnd_action()); gfx::PointF point(wl_fixed_to_double(x), wl_fixed_to_double(y)); - int client_operation = self->window_->OnDragMotion(point, time, operation); + int client_operation = self->window_->OnDragMotion( + point, time, + GetPossibleActions(self->drag_offer_->source_actions(), + self->drag_offer_->dnd_action())); self->SetOperation(client_operation); } @@ -239,25 +364,21 @@ LOG(ERROR) << "Failed to get window."; return; } - if (!self->IsDraggingExternalData()) { - // When the drag session started from a chromium window, source_data_ - // already holds the data and already forwarded it to delegate through - // OnDragEnter, so at this point (onDragDrop) the delegate expects a - // nullptr and the data will be read internally with no need to read it - // through Wayland pipe and so on. - self->HandleReceivedData(nullptr); - } else { - // Creates buffer to receive data from Wayland. + if (self->IsDraggingExternalData()) { + // We are about to accept data dragged from another application. + // Reading all the data may take some time so we set + // |is_handling_dropped_data_| that will postpone handling of OnLeave + // until reading is completed. + self->is_handling_dropped_data_ = true; self->received_data_ = std::make_unique<OSExchangeData>( std::make_unique<OSExchangeDataProviderAura>()); - // In order to guarantee all data received, it sets - // |is_handling_dropped_data_| and defers OnLeave event handling if it gets - // OnLeave event before completing to read the data. - self->is_handling_dropped_data_ = true; - // Starts to read the data on Drop event because read(..) API blocks - // awaiting data to be sent to pipe if we try to read the data on OnEnter. - // 'Weston' also reads data on OnDrop event and other examples do as well. self->HandleUnprocessedMimeTypes(); + } else { + // If the drag session had been started internally by chromium, + // |source_data_| already holds the data, and it is already forwarded to the + // delegate through OnDragEnter, so here we short-cut the data transfer by + // sending nullptr. + self->HandleReceivedData(nullptr); } } @@ -345,7 +466,8 @@ } } -void WaylandDataDevice::OnDragDataReceived(const std::string& contents) { +void WaylandDataDevice::OnDragDataReceived( + const PlatformClipboard::Data& contents) { if (!contents.empty()) { AddToOSExchangeData(contents, unprocessed_mime_types_.front(), received_data_.get()); @@ -359,7 +481,6 @@ void WaylandDataDevice::HandleReceivedData( std::unique_ptr<ui::OSExchangeData> received_data) { - // TODO(crbug.com/875164): Fix mime types support. unprocessed_mime_types_.clear(); window_->OnDragDrop(std::move(received_data)); @@ -370,12 +491,21 @@ std::string WaylandDataDevice::SelectNextMimeType() { while (!unprocessed_mime_types_.empty()) { - std::string& mime_type = unprocessed_mime_types_.front(); + const std::string& mime_type = unprocessed_mime_types_.front(); if ((mime_type == kMimeTypeText || mime_type == kMimeTypeTextUtf8) && !received_data_->HasString()) { return mime_type; } - // TODO(crbug.com/875164): Fix mime types support. + if (mime_type == kMimeTypeURIList && !received_data_->HasFile()) { + return mime_type; + } + if (mime_type == kMimeTypeMozillaURL && + !received_data_->HasURL(kFilenameToURLPolicy)) { + return mime_type; + } + if (mime_type == kMimeTypeHTML && !received_data_->HasHtml()) { + return mime_type; + } unprocessed_mime_types_.pop_front(); } return {};
diff --git a/ui/ozone/platform/wayland/host/wayland_data_device.h b/ui/ozone/platform/wayland/host/wayland_data_device.h index e22edd8..0bd74651 100644 --- a/ui/ozone/platform/wayland/host/wayland_data_device.h +++ b/ui/ozone/platform/wayland/host/wayland_data_device.h
@@ -20,6 +20,7 @@ #include "ui/ozone/platform/wayland/host/internal/wayland_data_device_base.h" #include "ui/ozone/platform/wayland/host/wayland_data_offer.h" #include "ui/ozone/platform/wayland/host/wayland_shm_buffer.h" +#include "ui/ozone/public/platform_clipboard.h" class SkBitmap; @@ -40,8 +41,9 @@ // Requests the data to the platform when Chromium gets drag-and-drop started // by others. Once reading the data from platform is done, |callback| should // be called with the data. - void RequestDragData(const std::string& mime_type, - base::OnceCallback<void(const std::string&)> callback); + void RequestDragData( + const std::string& mime_type, + base::OnceCallback<void(const PlatformClipboard::Data&)> callback); // Delivers the data owned by Chromium which initiates drag-and-drop. |buffer| // is an output parameter and it should be filled with the data corresponding // to mime_type. @@ -59,7 +61,7 @@ private: void ReadDragDataFromFD( base::ScopedFD fd, - base::OnceCallback<void(const std::string&)> callback); + base::OnceCallback<void(const PlatformClipboard::Data&)> callback); // If source_data_ is not set, data is being dragged from an external // application (non-chromium). @@ -105,7 +107,7 @@ const SkBitmap* PrepareDragIcon(const OSExchangeData& data); void DrawDragIcon(const SkBitmap* bitmap); - void OnDragDataReceived(const std::string& contents); + void OnDragDataReceived(const PlatformClipboard::Data& contents); // HandleUnprocessedMimeTypes asynchronously request and read data for every // negotiated mime type, one after another (OnDragDataReceived calls back
diff --git a/ui/ozone/platform/wayland/host/wayland_data_device_unittest.cc b/ui/ozone/platform/wayland/host/wayland_data_device_unittest.cc index 8ab31d7..4726a86 100644 --- a/ui/ozone/platform/wayland/host/wayland_data_device_unittest.cc +++ b/ui/ozone/platform/wayland/host/wayland_data_device_unittest.cc
@@ -5,10 +5,17 @@ #include <wayland-server.h> #include <memory> +#include <string> +#include <vector> #include "base/bind.h" +#include "base/containers/flat_set.h" +#include "base/strings/string16.h" +#include "base/strings/utf_string_conversions.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/base/clipboard/clipboard_constants.h" #include "ui/base/dragdrop/drag_drop_types.h" +#include "ui/base/dragdrop/file_info.h" #include "ui/base/dragdrop/os_exchange_data.h" #include "ui/events/base_event_utils.h" #include "ui/ozone/platform/wayland/test/constants.h" @@ -20,9 +27,65 @@ #include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h" #include "ui/ozone/platform/wayland/test/wayland_test.h" #include "ui/ozone/public/platform_clipboard.h" +#include "ui/platform_window/platform_window_handler/wm_drop_handler.h" +#include "url/gurl.h" + +using testing::_; +using testing::Mock; namespace ui { +namespace { + +constexpr OSExchangeData::FilenameToURLPolicy kFilenameToURLPolicy = + OSExchangeData::FilenameToURLPolicy::CONVERT_FILENAMES; + +template <typename StringType> +ui::PlatformClipboard::Data ToClipboardData(const StringType& data_string) { + ui::PlatformClipboard::Data result; + auto* begin = + reinterpret_cast<typename ui::PlatformClipboard::Data::const_pointer>( + data_string.data()); + result.assign(begin, begin + (data_string.size() * + sizeof(typename StringType::value_type))); + return result; +} + +} // namespace + +class MockDropHandler : public WmDropHandler { + public: + MockDropHandler() = default; + ~MockDropHandler() override {} + + MOCK_METHOD3(OnDragEnter, + void(const gfx::PointF& point, + std::unique_ptr<OSExchangeData> data, + int operation)); + MOCK_METHOD2(OnDragMotion, int(const gfx::PointF& point, int operation)); + MOCK_METHOD0(MockOnDragDrop, void()); + MOCK_METHOD0(OnDragLeave, void()); + + void SetOnDropClosure(base::RepeatingClosure closure) { + on_drop_closure_ = closure; + } + + ui::OSExchangeData* dropped_data() { return dropped_data_.get(); } + + protected: + void OnDragDrop(std::unique_ptr<ui::OSExchangeData> data) override { + dropped_data_ = std::move(data); + MockOnDragDrop(); + on_drop_closure_.Run(); + on_drop_closure_.Reset(); + } + + private: + base::RepeatingClosure on_drop_closure_; + + std::unique_ptr<ui::OSExchangeData> dropped_data_; +}; + // This class mocks how a real clipboard/ozone client would // hook to PlatformClipboard, with one difference: real clients // have no access to the WaylandConnection instance like this @@ -42,15 +105,10 @@ ~MockClipboardClient() = default; // Fill the clipboard backing store with sample data. - void SetData(const std::string& utf8_text, + void SetData(const PlatformClipboard::Data& data, const std::string& mime_type, PlatformClipboard::OfferDataClosure callback) { - // This mimics how Mus' ClipboardImpl writes data to the DataMap. - std::vector<char> object_map(utf8_text.begin(), utf8_text.end()); - char* object_data = &object_map.front(); - data_types_[mime_type] = - std::vector<uint8_t>(object_data, object_data + object_map.size()); - + data_types_[mime_type] = data; delegate_->OfferClipboardData(ClipboardBuffer::kCopyPaste, data_types_, std::move(callback)); } @@ -86,11 +144,15 @@ clipboard_client_ = std::make_unique<MockClipboardClient>(connection_.get()); + + drop_handler_ = std::make_unique<MockDropHandler>(); + SetWmDropHandler(window_.get(), drop_handler_.get()); } protected: wl::TestDataDeviceManager* data_device_manager_; std::unique_ptr<MockClipboardClient> clipboard_client_; + std::unique_ptr<MockDropHandler> drop_handler_; private: DISALLOW_COPY_AND_ASSIGN(WaylandDataDeviceManagerTest); @@ -98,14 +160,17 @@ TEST_P(WaylandDataDeviceManagerTest, WriteToClipboard) { // The client writes data to the clipboard ... - clipboard_client_->SetData(wl::kSampleClipboardText, wl::kTextMimeTypeUtf8, + PlatformClipboard::Data data; + data.assign(wl::kSampleClipboardText, + wl::kSampleClipboardText + strlen(wl::kSampleClipboardText)); + clipboard_client_->SetData(data, wl::kTextMimeTypeUtf8, base::BindOnce([]() {})); Sync(); // ... and the server reads it. base::RunLoop run_loop; auto callback = base::BindOnce( - [](base::RunLoop* loop, std::vector<uint8_t>&& data) { + [](base::RunLoop* loop, PlatformClipboard::Data&& data) { std::string string_data(data.begin(), data.end()); EXPECT_EQ(wl::kSampleClipboardText, string_data); loop->Quit(); @@ -120,7 +185,8 @@ // TODO(nickdiego): implement this in terms of an actual wl_surface that // gets focused and compositor sends data_device data to it. auto* data_offer = data_device_manager_->data_device()->OnDataOffer(); - data_offer->OnOffer(wl::kTextMimeTypeUtf8); + data_offer->OnOffer(wl::kTextMimeTypeUtf8, + ToClipboardData(std::string(wl::kSampleClipboardText))); data_device_manager_->data_device()->OnSelection(data_offer); Sync(); @@ -128,7 +194,7 @@ // The Server writes in some sample data, and we check it matches // expectation. auto callback = - base::BindOnce([](const base::Optional<std::vector<uint8_t>>& data) { + base::BindOnce([](const base::Optional<PlatformClipboard::Data>& data) { std::string string_data = std::string(data->begin(), data->end()); EXPECT_EQ(wl::kSampleClipboardText, string_data); }); @@ -141,7 +207,7 @@ // from the server, the response callback should be gracefully called with // an empty string. auto callback = - base::BindOnce([](const base::Optional<std::vector<uint8_t>>& data) { + base::BindOnce([](const base::Optional<PlatformClipboard::Data>& data) { std::string string_data = std::string(data->begin(), data->end()); EXPECT_EQ("", string_data); }); @@ -150,8 +216,10 @@ TEST_P(WaylandDataDeviceManagerTest, IsSelectionOwner) { auto callback = base::BindOnce([]() {}); - clipboard_client_->SetData(wl::kSampleClipboardText, wl::kTextMimeTypeUtf8, - std::move(callback)); + PlatformClipboard::Data data; + data.assign(wl::kSampleClipboardText, + wl::kSampleClipboardText + strlen(wl::kSampleClipboardText)); + clipboard_client_->SetData(data, wl::kTextMimeTypeUtf8, std::move(callback)); Sync(); ASSERT_TRUE(clipboard_client_->IsSelectionOwner()); @@ -181,7 +249,7 @@ // The server reads the data and the callback gets it. base::RunLoop run_loop; auto callback = base::BindOnce( - [](base::RunLoop* loop, std::vector<uint8_t>&& data) { + [](base::RunLoop* loop, PlatformClipboard::Data&& data) { std::string result(data.begin(), data.end()); EXPECT_EQ(wl::kSampleTextForDragAndDrop, result); loop->Quit(); @@ -212,13 +280,13 @@ // when trying to read it. base::RunLoop run_loop; auto callback = base::BindOnce( - [](base::RunLoop* loop, std::vector<uint8_t>&& data) { + [](base::RunLoop* loop, PlatformClipboard::Data&& data) { std::string result(data.begin(), data.end()); EXPECT_EQ("", result); loop->Quit(); }, &run_loop); - data_device_manager_->data_source()->ReadData(wl::kTextMimeTypeText, + data_device_manager_->data_source()->ReadData(ui::kMimeTypeText, std::move(callback)); run_loop.Run(); window_->set_pointer_focus(restored_focus); @@ -226,7 +294,9 @@ TEST_P(WaylandDataDeviceManagerTest, ReceiveDrag) { auto* data_offer = data_device_manager_->data_device()->OnDataOffer(); - data_offer->OnOffer(wl::kTextMimeTypeText); + data_offer->OnOffer( + ui::kMimeTypeText, + ToClipboardData(std::string(wl::kSampleTextForDragAndDrop))); gfx::Point entered_point(10, 10); // The server sends an enter event. @@ -245,17 +315,172 @@ Sync(); - auto callback = base::BindOnce([](const std::string& contents) { - EXPECT_EQ(wl::kSampleTextForDragAndDrop, contents); + auto callback = base::BindOnce([](const PlatformClipboard::Data& contents) { + std::string result; + result.assign(reinterpret_cast<std::string::const_pointer>(&contents[0]), + contents.size()); + EXPECT_EQ(wl::kSampleTextForDragAndDrop, result); }); // The client requests the data and gets callback with it. - connection_->RequestDragData(wl::kTextMimeTypeText, std::move(callback)); + connection_->RequestDragData(ui::kMimeTypeText, std::move(callback)); Sync(); data_device_manager_->data_device()->OnLeave(); } +TEST_P(WaylandDataDeviceManagerTest, DropSeveralMimeTypes) { + auto* data_offer = data_device_manager_->data_device()->OnDataOffer(); + data_offer->OnOffer( + ui::kMimeTypeText, + ToClipboardData(std::string(wl::kSampleTextForDragAndDrop))); + data_offer->OnOffer( + ui::kMimeTypeMozillaURL, + ToClipboardData(base::UTF8ToUTF16("https://sample.com/\r\n" + "Sample"))); + data_offer->OnOffer( + ui::kMimeTypeURIList, + ToClipboardData(std::string("file:///home/user/file\r\n"))); + + EXPECT_CALL(*drop_handler_, OnDragEnter(_, _, _)).Times(1); + gfx::Point entered_point(10, 10); + data_device_manager_->data_device()->OnEnter( + 1002, surface_->resource(), wl_fixed_from_int(entered_point.x()), + wl_fixed_from_int(entered_point.y()), data_offer); + Sync(); + Mock::VerifyAndClearExpectations(drop_handler_.get()); + + EXPECT_CALL(*drop_handler_, MockOnDragDrop()).Times(1); + base::RunLoop loop; + drop_handler_->SetOnDropClosure(loop.QuitClosure()); + data_device_manager_->data_device()->OnDrop(); + + // Here we are expecting three data items, so there will be three roundtrips + // to the Wayland and back. Hence Sync() three times. + Sync(); + Sync(); + Sync(); + loop.Run(); + Mock::VerifyAndClearExpectations(drop_handler_.get()); + + EXPECT_TRUE(drop_handler_->dropped_data()->HasString()); + EXPECT_TRUE(drop_handler_->dropped_data()->HasFile()); + EXPECT_TRUE(drop_handler_->dropped_data()->HasURL(kFilenameToURLPolicy)); + + data_device_manager_->data_device()->OnLeave(); +} + +// Tests URI validation for text/uri-list MIME type. Log warnings rendered in +// the console when this test is running are the expected and valid side effect. +TEST_P(WaylandDataDeviceManagerTest, ValidateDroppedUriList) { + const struct { + std::string content; + base::flat_set<std::string> expected_uris; + } kCases[] = {{{}, {}}, + {"file:///home/user/file\r\n", {"/home/user/file"}}, + {"# Comment\r\n" + "file:///home/user/file\r\n" + "file:///home/guest/file\r\n" + "not a filename at all\r\n" + "https://valid.url/but/scheme/is/not/file/so/invalid\r\n", + {"/home/user/file", "/home/guest/file"}}}; + + for (const auto& kCase : kCases) { + auto* data_offer = data_device_manager_->data_device()->OnDataOffer(); + data_offer->OnOffer(ui::kMimeTypeURIList, ToClipboardData(kCase.content)); + + EXPECT_CALL(*drop_handler_, OnDragEnter(_, _, _)).Times(1); + gfx::Point entered_point(10, 10); + data_device_manager_->data_device()->OnEnter( + 1002, surface_->resource(), wl_fixed_from_int(entered_point.x()), + wl_fixed_from_int(entered_point.y()), data_offer); + Sync(); + Mock::VerifyAndClearExpectations(drop_handler_.get()); + + EXPECT_CALL(*drop_handler_, MockOnDragDrop()).Times(1); + base::RunLoop loop; + drop_handler_->SetOnDropClosure(loop.QuitClosure()); + data_device_manager_->data_device()->OnDrop(); + + Sync(); + loop.Run(); + Mock::VerifyAndClearExpectations(drop_handler_.get()); + + if (kCase.expected_uris.empty()) { + EXPECT_FALSE(drop_handler_->dropped_data()->HasFile()); + } else { + EXPECT_TRUE(drop_handler_->dropped_data()->HasFile()); + std::vector<FileInfo> filenames; + EXPECT_TRUE(drop_handler_->dropped_data()->GetFilenames(&filenames)); + EXPECT_EQ(filenames.size(), kCase.expected_uris.size()); + for (const auto& filename : filenames) + EXPECT_EQ(kCase.expected_uris.count(filename.path.AsUTF8Unsafe()), 1U); + } + + EXPECT_CALL(*drop_handler_, OnDragLeave()).Times(1); + data_device_manager_->data_device()->OnLeave(); + Sync(); + Mock::VerifyAndClearExpectations(drop_handler_.get()); + } +} + +// Tests URI validation for text/x-moz-url MIME type. Log warnings rendered in +// the console when this test is running are the expected and valid side effect. +TEST_P(WaylandDataDeviceManagerTest, ValidateDroppedXMozUrl) { + const struct { + std::string content; + std::string expected_url; + std::string expected_title; + } kCases[] = { + {{}, {}, {}}, + {"http://sample.com/\r\nSample", "http://sample.com/", "Sample"}, + {"http://title.must.be.set/", {}, {}}, + {"url.must.be.valid/and/have.scheme\r\nInvalid URL", {}, {}}, + {"file:///files/are/ok\r\nThe policy allows that", "file:///files/are/ok", + "The policy allows that"}}; + + for (const auto& kCase : kCases) { + auto* data_offer = data_device_manager_->data_device()->OnDataOffer(); + data_offer->OnOffer(ui::kMimeTypeMozillaURL, + ToClipboardData(base::UTF8ToUTF16(kCase.content))); + + EXPECT_CALL(*drop_handler_, OnDragEnter(_, _, _)).Times(1); + gfx::Point entered_point(10, 10); + data_device_manager_->data_device()->OnEnter( + 1002, surface_->resource(), wl_fixed_from_int(entered_point.x()), + wl_fixed_from_int(entered_point.y()), data_offer); + Sync(); + Mock::VerifyAndClearExpectations(drop_handler_.get()); + + EXPECT_CALL(*drop_handler_, MockOnDragDrop()).Times(1); + base::RunLoop loop; + drop_handler_->SetOnDropClosure(loop.QuitClosure()); + data_device_manager_->data_device()->OnDrop(); + + Sync(); + loop.Run(); + Mock::VerifyAndClearExpectations(drop_handler_.get()); + + const auto* const dropped_data = drop_handler_->dropped_data(); + if (kCase.expected_url.empty()) { + EXPECT_FALSE(dropped_data->HasURL(kFilenameToURLPolicy)); + } else { + EXPECT_TRUE(dropped_data->HasURL(kFilenameToURLPolicy)); + GURL url; + base::string16 title; + EXPECT_TRUE( + dropped_data->GetURLAndTitle(kFilenameToURLPolicy, &url, &title)); + EXPECT_EQ(url.spec(), kCase.expected_url); + EXPECT_EQ(title, base::UTF8ToUTF16(kCase.expected_title)); + } + + EXPECT_CALL(*drop_handler_, OnDragLeave()).Times(1); + data_device_manager_->data_device()->OnLeave(); + Sync(); + Mock::VerifyAndClearExpectations(drop_handler_.get()); + } +} + INSTANTIATE_TEST_SUITE_P(XdgVersionV5Test, WaylandDataDeviceManagerTest, ::testing::Values(kXdgShellV5));
diff --git a/ui/ozone/platform/wayland/host/wayland_data_source.cc b/ui/ozone/platform/wayland/host/wayland_data_source.cc index 4dcbeb9..e35746e 100644 --- a/ui/ozone/platform/wayland/host/wayland_data_source.cc +++ b/ui/ozone/platform/wayland/host/wayland_data_source.cc
@@ -7,6 +7,7 @@ #include "base/files/file_util.h" #include "ui/base/clipboard/clipboard_constants.h" #include "ui/base/dragdrop/drag_drop_types.h" +#include "ui/base/dragdrop/os_exchange_data.h" #include "ui/ozone/platform/wayland/host/wayland_connection.h" #include "ui/ozone/platform/wayland/host/wayland_window.h" @@ -38,10 +39,24 @@ } void WaylandDataSource::Offer(const ui::OSExchangeData& data) { - // TODO(jkim): Handle mime types based on data. + // Drag'n'drop manuals usually suggest putting data in order so the more + // specific a MIME type is, the earlier it occurs in the list. Wayland specs + // don't say anything like that, but here we follow that common practice: + // begin with URIs and end with plain text. Just in case. std::vector<std::string> mime_types; - mime_types.push_back(kMimeTypeText); - mime_types.push_back(kMimeTypeTextUtf8); + if (data.HasFile()) { + mime_types.push_back(kMimeTypeURIList); + } + if (data.HasURL(ui::OSExchangeData::FilenameToURLPolicy::CONVERT_FILENAMES)) { + mime_types.push_back(kMimeTypeMozillaURL); + } + if (data.HasHtml()) { + mime_types.push_back(kMimeTypeHTML); + } + if (data.HasString()) { + mime_types.push_back(kMimeTypeTextUtf8); + mime_types.push_back(kMimeTypeText); + } source_window_ = connection_->wayland_window_manager()->GetCurrentFocusedWindow();
diff --git a/ui/ozone/platform/wayland/test/test_data_offer.cc b/ui/ozone/platform/wayland/test/test_data_offer.cc index 64e8b4f..456945f 100644 --- a/ui/ozone/platform/wayland/test/test_data_offer.cc +++ b/ui/ozone/platform/wayland/test/test_data_offer.cc
@@ -19,9 +19,12 @@ namespace { -void WriteDataOnWorkerThread(base::ScopedFD fd, const std::string& utf8_text) { - if (!base::WriteFileDescriptor(fd.get(), utf8_text.data(), utf8_text.size())) +void WriteDataOnWorkerThread(base::ScopedFD fd, + const ui::PlatformClipboard::Data& data) { + if (!base::WriteFileDescriptor( + fd.get(), reinterpret_cast<const char*>(data.data()), data.size())) { LOG(ERROR) << "Failed to write selection data to clipboard."; + } } void DataOfferAccept(wl_client* client, @@ -70,17 +73,15 @@ void TestDataOffer::Receive(const std::string& mime_type, base::ScopedFD fd) { DCHECK(fd.is_valid()); - std::string text_data; - if (mime_type == kTextMimeTypeUtf8) - text_data = kSampleClipboardText; - else if (mime_type == kTextMimeTypeText) - text_data = kSampleTextForDragAndDrop; - task_runner_->PostTask(FROM_HERE, base::BindOnce(&WriteDataOnWorkerThread, - std::move(fd), text_data)); + task_runner_->PostTask(FROM_HERE, + base::BindOnce(&WriteDataOnWorkerThread, std::move(fd), + data_to_offer_[mime_type])); } -void TestDataOffer::OnOffer(const std::string& mime_type) { +void TestDataOffer::OnOffer(const std::string& mime_type, + const ui::PlatformClipboard::Data& data) { + data_to_offer_[mime_type] = data; wl_data_offer_send_offer(resource(), mime_type.c_str()); }
diff --git a/ui/ozone/platform/wayland/test/test_data_offer.h b/ui/ozone/platform/wayland/test/test_data_offer.h index 7595e4e..4ca6c58 100644 --- a/ui/ozone/platform/wayland/test/test_data_offer.h +++ b/ui/ozone/platform/wayland/test/test_data_offer.h
@@ -15,6 +15,7 @@ #include "base/memory/weak_ptr.h" #include "base/threading/thread.h" #include "ui/ozone/platform/wayland/test/server_object.h" +#include "ui/ozone/public/platform_clipboard.h" struct wl_resource; @@ -32,10 +33,13 @@ ~TestDataOffer() override; void Receive(const std::string& mime_type, base::ScopedFD fd); - void OnOffer(const std::string& mime_type); + void OnOffer(const std::string& mime_type, + const ui::PlatformClipboard::Data& data); private: const scoped_refptr<base::SequencedTaskRunner> task_runner_; + ui::PlatformClipboard::DataMap data_to_offer_; + base::WeakPtrFactory<TestDataOffer> write_data_weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(TestDataOffer);
diff --git a/ui/views/controls/scroll_view.cc b/ui/views/controls/scroll_view.cc index 78806f645..d95cd8a 100644 --- a/ui/views/controls/scroll_view.cc +++ b/ui/views/controls/scroll_view.cc
@@ -18,7 +18,6 @@ #include "ui/views/background.h" #include "ui/views/border.h" #include "ui/views/controls/focus_ring.h" -#include "ui/views/controls/separator.h" #include "ui/views/style/platform_style.h" #include "ui/views/widget/widget.h" @@ -160,21 +159,15 @@ }; ScrollView::ScrollView() - : contents_viewport_(new Viewport(this)), - header_viewport_(new Viewport(this)), - horiz_sb_(PlatformStyle::CreateScrollBar(true).release()), - vert_sb_(PlatformStyle::CreateScrollBar(false).release()), - corner_view_(new ScrollCornerView()), - more_content_left_(std::make_unique<Separator>()), - more_content_top_(std::make_unique<Separator>()), - more_content_right_(std::make_unique<Separator>()), - more_content_bottom_(std::make_unique<Separator>()), + : horiz_sb_(PlatformStyle::CreateScrollBar(true)), + vert_sb_(PlatformStyle::CreateScrollBar(false)), + corner_view_(std::make_unique<ScrollCornerView>()), scroll_with_layers_enabled_(base::FeatureList::IsEnabled( ::features::kUiCompositorScrollWithLayers)) { set_notify_enter_exit_on_child(true); - AddChildView(contents_viewport_); - AddChildView(header_viewport_); + contents_viewport_ = AddChildView(std::make_unique<Viewport>(this)); + header_viewport_ = AddChildView(std::make_unique<Viewport>(this)); // Don't add the scrollbars as children until we discover we need them // (ShowOrHideScrollBar). @@ -211,13 +204,7 @@ }); } -ScrollView::~ScrollView() { - // The scrollbars may not have been added, delete them to ensure they get - // deleted. - delete horiz_sb_; - delete vert_sb_; - delete corner_view_; -} +ScrollView::~ScrollView() = default; // static std::unique_ptr<ScrollView> ScrollView::CreateScrollViewWithBorder() { @@ -326,20 +313,22 @@ : 0; } -void ScrollView::SetHorizontalScrollBar(ScrollBar* horiz_sb) { +ScrollBar* ScrollView::SetHorizontalScrollBar( + std::unique_ptr<ScrollBar> horiz_sb) { DCHECK(horiz_sb); horiz_sb->SetVisible(horiz_sb_->GetVisible()); - delete horiz_sb_; horiz_sb->set_controller(this); - horiz_sb_ = horiz_sb; + horiz_sb_ = std::move(horiz_sb); + return horiz_sb_.get(); } -void ScrollView::SetVerticalScrollBar(ScrollBar* vert_sb) { +ScrollBar* ScrollView::SetVerticalScrollBar( + std::unique_ptr<ScrollBar> vert_sb) { DCHECK(vert_sb); vert_sb->SetVisible(vert_sb_->GetVisible()); - delete vert_sb_; vert_sb->set_controller(this); - vert_sb_ = vert_sb; + vert_sb_ = std::move(vert_sb); + return vert_sb_.get(); } void ScrollView::SetHasFocusIndicator(bool has_focus_indicator) { @@ -466,9 +455,9 @@ bool corner_view_required = horiz_sb_required && vert_sb_required && !vert_sb_->OverlapsContent(); // Take action. - SetControlVisibility(horiz_sb_, horiz_sb_required); - SetControlVisibility(vert_sb_, vert_sb_required); - SetControlVisibility(corner_view_, corner_view_required); + SetControlVisibility(horiz_sb_.get(), horiz_sb_required); + SetControlVisibility(vert_sb_.get(), vert_sb_required); + SetControlVisibility(corner_view_.get(), corner_view_required); // Default. if (!horiz_sb_required) { @@ -648,13 +637,13 @@ return; gfx::ScrollOffset offset = CurrentOffset(); - if (source == horiz_sb_ && horiz_sb_->GetVisible()) { + if (source == horiz_sb_.get() && horiz_sb_->GetVisible()) { position = AdjustPosition(offset.x(), position, contents_->width(), contents_viewport_->width()); if (offset.x() == position) return; offset.set_x(position); - } else if (source == vert_sb_ && vert_sb_->GetVisible()) { + } else if (source == vert_sb_.get() && vert_sb_->GetVisible()) { position = AdjustPosition(offset.y(), position, contents_->height(), contents_viewport_->height()); if (offset.y() == position)
diff --git a/ui/views/controls/scroll_view.h b/ui/views/controls/scroll_view.h index cebafcd..53a9fe13 100644 --- a/ui/views/controls/scroll_view.h +++ b/ui/views/controls/scroll_view.h
@@ -15,6 +15,7 @@ #include "ui/native_theme/native_theme.h" #include "ui/views/controls/focus_ring.h" #include "ui/views/controls/scrollbar/scroll_bar.h" +#include "ui/views/controls/separator.h" namespace gfx { class ScrollOffset; @@ -116,14 +117,15 @@ int GetScrollBarLayoutWidth() const; int GetScrollBarLayoutHeight() const; - // Returns the horizontal/vertical scrollbar. This may return NULL. - const ScrollBar* horizontal_scroll_bar() const { return horiz_sb_; } - const ScrollBar* vertical_scroll_bar() const { return vert_sb_; } + // Returns the horizontal/vertical scrollbar. This may return null. + ScrollBar* horizontal_scroll_bar() { return horiz_sb_.get(); } + const ScrollBar* horizontal_scroll_bar() const { return horiz_sb_.get(); } + ScrollBar* vertical_scroll_bar() { return vert_sb_.get(); } + const ScrollBar* vertical_scroll_bar() const { return vert_sb_.get(); } - // Customize the scrollbar design. ScrollView takes the ownership of the - // specified ScrollBar. |horiz_sb| and |vert_sb| cannot be NULL. - void SetHorizontalScrollBar(ScrollBar* horiz_sb); - void SetVerticalScrollBar(ScrollBar* vert_sb); + // Customize the scrollbar design. |horiz_sb| and |vert_sb| cannot be null. + ScrollBar* SetHorizontalScrollBar(std::unique_ptr<ScrollBar> horiz_sb); + ScrollBar* SetVerticalScrollBar(std::unique_ptr<ScrollBar> vert_sb); // Gets/Sets whether this ScrollView has a focus indicator or not. bool GetHasFocusIndicator() const { return draw_focus_indicator_; } @@ -233,19 +235,21 @@ View* header_viewport_; // Horizontal scrollbar. - ScrollBar* horiz_sb_; + std::unique_ptr<ScrollBar> horiz_sb_; // Vertical scrollbar. - ScrollBar* vert_sb_; + std::unique_ptr<ScrollBar> vert_sb_; // Corner view. - View* corner_view_; + std::unique_ptr<View> corner_view_; // Hidden content indicators - std::unique_ptr<Separator> more_content_left_; - std::unique_ptr<Separator> more_content_top_; - std::unique_ptr<Separator> more_content_right_; - std::unique_ptr<Separator> more_content_bottom_; + std::unique_ptr<Separator> more_content_left_ = std::make_unique<Separator>(); + std::unique_ptr<Separator> more_content_top_ = std::make_unique<Separator>(); + std::unique_ptr<Separator> more_content_right_ = + std::make_unique<Separator>(); + std::unique_ptr<Separator> more_content_bottom_ = + std::make_unique<Separator>(); // The min and max height for the bounded scroll view. These are negative // values if the view is not bounded.
diff --git a/ui/views/controls/scroll_view_unittest.cc b/ui/views/controls/scroll_view_unittest.cc index 685261b..73af6e5 100644 --- a/ui/views/controls/scroll_view_unittest.cc +++ b/ui/views/controls/scroll_view_unittest.cc
@@ -44,9 +44,10 @@ : scroll_view_(scroll_view) {} ScrollBar* GetScrollBar(ScrollBarOrientation orientation) { - ScrollBar* scroll_bar = orientation == VERTICAL ? scroll_view_->vert_sb_ - : scroll_view_->horiz_sb_; - return static_cast<ScrollBar*>(scroll_bar); + ScrollBar* scroll_bar = orientation == VERTICAL + ? scroll_view_->vertical_scroll_bar() + : scroll_view_->horizontal_scroll_bar(); + return scroll_bar; } const base::OneShotTimer& GetScrollBarTimer( @@ -69,7 +70,7 @@ return ScrollBar::GetHideTimerForTesting(GetScrollBar(orientation)); } - View* corner_view() { return scroll_view_->corner_view_; } + View* corner_view() { return scroll_view_->corner_view_.get(); } View* contents_viewport() { return scroll_view_->contents_viewport_; } Separator* more_content_left() { @@ -1507,10 +1508,10 @@ constexpr int kThickness = 1; // Assume horizontal scroll bar is the default and is overlapping. - scroll_view_->SetHorizontalScrollBar(new TestScrollBar( + scroll_view_->SetHorizontalScrollBar(std::make_unique<TestScrollBar>( /* horizontal */ true, /* overlaps_content */ true, kThickness)); // Assume vertical scroll bar is custom and it we want it to not overlap. - scroll_view_->SetVerticalScrollBar(new TestScrollBar( + scroll_view_->SetVerticalScrollBar(std::make_unique<TestScrollBar>( /* horizontal */ false, /* overlaps_content */ false, kThickness)); // Also, let's turn off horizontal scroll bar.
diff --git a/ui/webui/resources/cr_elements/cr_input/cr_input.js b/ui/webui/resources/cr_elements/cr_input/cr_input.js index 8604791..ffe566a 100644 --- a/ui/webui/resources/cr_elements/cr_input/cr_input.js +++ b/ui/webui/resources/cr_elements/cr_input/cr_input.js
@@ -225,7 +225,7 @@ /** @private */ onFocus_: function() { - if (!this.focusInput_()) { + if (!this.focusInput()) { return; } // Always select the <input> element on focus. TODO(stevenjb/scottchen): @@ -235,10 +235,12 @@ }, /** + * Focuses the input element. + * TODO(crbug.com/882612): Replace this with focus() after resolving the text + * selection issue described in onFocus_(). * @return {boolean} Whether the <input> element was focused. - * @private */ - focusInput_: function() { + focusInput: function() { if (this.shadowRoot.activeElement == this.inputElement) { return false; } @@ -346,7 +348,7 @@ * @param {number=} end */ select: function(start, end) { - this.focusInput_(); + this.focusInput(); if (start !== undefined && end !== undefined) { this.inputElement.setSelectionRange(start, end); } else {