diff --git a/BUILD.gn b/BUILD.gn index 4f10eed1..5b0fa041 100644 --- a/BUILD.gn +++ b/BUILD.gn
@@ -342,6 +342,7 @@ "//base:base_junit_tests", "//base/android/jni_generator:jni_generator_tests", "//base/android/linker:chromium_android_linker", + "//build/android:android_lint_test", "//build/android/gyp/test:hello_world", "//build/android/stacktrace:java_deobfuscate", "//build/config/android/test/proto:test_build_protos",
diff --git a/DEPS b/DEPS index 4e82d31f..be17f65 100644 --- a/DEPS +++ b/DEPS
@@ -199,7 +199,7 @@ # 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': 'f10f8691483116f1f08eafc7483c1edf4511dc00', + 'v8_revision': '8727abed3a840e39bbebeaa541d756c21e996776', # 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. @@ -207,7 +207,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': '83c7e1ae8626b8e2c1e3a2d2637abe277ba4999c', + 'angle_revision': '37457d086ed36644b29336b786d1ada4d87817e0', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. @@ -258,7 +258,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': '249019878c55d92e9c53a0b7a3f9ef188d8c29f7', + 'catapult_revision': '0de9874718993c990ed13321720d6065a2f7dcaa', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other. @@ -266,7 +266,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling devtools-frontend # and whatever else without interference from each other. - 'devtools_frontend_revision': 'd5044ddf05e65c5c16a75f60b05ecd4105bc05ec', + 'devtools_frontend_revision': '11880b30c36028e6d7c1d923d3c9a605d01790c9', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libprotobuf-mutator # and whatever else without interference from each other. @@ -318,11 +318,11 @@ # 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': '6419bddd9b0c1869b2b6f7ae258fa177e870ba9f', + 'dawn_revision': '4099f6552592575360ad630e5c00e4978335f89d', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. - 'quiche_revision': 'a5c3383880999828b47f2dfaff3ad86cf35c886a', + 'quiche_revision': 'f555d99a084cdd086a349548c70fb558ac5847cf', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling ios_webkit # and whatever else without interference from each other. @@ -896,7 +896,7 @@ }, 'src/third_party/depot_tools': - Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'a787cd5e158b0a9cc99f5a83e3ba06d4a8aaffbd', + Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '8d7201bc3c3cac1a5262639b47f1835d4d7a9255', 'src/third_party/devtools-frontend/src': Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'), @@ -1327,7 +1327,7 @@ 'packages': [ { 'package': 'fuchsia/third_party/aemu/linux-amd64', - 'version': 'ZuvbaFn4WQjlhZAWvjta7blkkX6FDGdZynRPTwiRzqMC' + 'version': 'MmVx5bSYBPwzS-Yz8BzRNOj0ReywWVFMBNUn8Nk37YkC' }, ], 'condition': 'host_os == "linux" and checkout_fuchsia', @@ -1543,7 +1543,7 @@ Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@6f0602162fdda14e1dcc824868ea7672ec583d1a', + 'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@e1e4781e5ec2c96eb56e98781eb8e91907f6a507', 'condition': 'checkout_src_internal', },
diff --git a/ash/BUILD.gn b/ash/BUILD.gn index 5891bc85..f88bbd94 100644 --- a/ash/BUILD.gn +++ b/ash/BUILD.gn
@@ -205,6 +205,8 @@ "ambient/util/ambient_util.cc", "ambient/util/ambient_util.h", "animation/animation_change_type.h", + "app_list/app_list_color_provider_impl.cc", + "app_list/app_list_color_provider_impl.h", "app_list/app_list_controller_impl.cc", "app_list/app_list_controller_impl.h", "app_list/app_list_presenter_delegate_impl.cc",
diff --git a/ash/app_list/BUILD.gn b/ash/app_list/BUILD.gn index 96b2531..41a7208 100644 --- a/ash/app_list/BUILD.gn +++ b/ash/app_list/BUILD.gn
@@ -174,6 +174,8 @@ "test/app_list_test_model.h", "test/app_list_test_view_delegate.cc", "test/app_list_test_view_delegate.h", + "test/test_app_list_color_provider.cc", + "test/test_app_list_color_provider.h", "test/test_search_result.cc", "test/test_search_result.h", "views/test/apps_grid_view_test_api.cc", @@ -242,6 +244,7 @@ ":app_list", ":test_support", "//ash/keyboard/ui", + "//ash/public/cpp", "//ash/public/cpp/app_list/vector_icons", "//base", "//base/test:test_support",
diff --git a/ash/app_list/app_list_color_provider_impl.cc b/ash/app_list/app_list_color_provider_impl.cc new file mode 100644 index 0000000..d9389e5 --- /dev/null +++ b/ash/app_list/app_list_color_provider_impl.cc
@@ -0,0 +1,67 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/app_list/app_list_color_provider_impl.h" + +#include "ash/style/ash_color_provider.h" + +namespace ash { + +AppListColorProviderImpl::AppListColorProviderImpl() + : ash_color_provider_(AshColorProvider::Get()) {} + +AppListColorProviderImpl::~AppListColorProviderImpl() = default; + +SkColor AppListColorProviderImpl::GetExpandArrowInkDropBaseColor() const { + return ash_color_provider_ + ->GetRippleAttributes(GetExpandArrowIconBackgroundColor()) + .base_color; +} + +SkColor AppListColorProviderImpl::GetExpandArrowIconBaseColor() const { + return ash_color_provider_->GetContentLayerColor( + AshColorProvider::ContentLayerType::kButtonIconColor); +} + +SkColor AppListColorProviderImpl::GetExpandArrowIconBackgroundColor() const { + return ash_color_provider_->GetControlsLayerColor( + AshColorProvider::ControlsLayerType::kControlBackgroundColorInactive); +} + +SkColor AppListColorProviderImpl::GetAppListBackgroundColor() const { + return ash_color_provider_->GetShieldLayerColor( + AshColorProvider::ShieldLayerType::kShield80); +} + +SkColor AppListColorProviderImpl::GetSearchBoxBackgroundColor() const { + return ash_color_provider_->GetControlsLayerColor( + AshColorProvider::ControlsLayerType::kControlBackgroundColorInactive); +} + +SkColor AppListColorProviderImpl::GetSearchBoxPlaceholderTextColor() const { + return ash_color_provider_->GetContentLayerColor( + AshColorProvider::ContentLayerType::kTextColorSecondary); +} + +SkColor AppListColorProviderImpl::GetSearchBoxTextColor() const { + return ash_color_provider_->GetContentLayerColor( + AshColorProvider::ContentLayerType::kTextColorPrimary); +} + +SkColor AppListColorProviderImpl::GetSuggestionChipBackgroundColor() const { + return ash_color_provider_->GetControlsLayerColor( + AshColorProvider::ControlsLayerType::kControlBackgroundColorInactive); +} + +SkColor AppListColorProviderImpl::GetSuggestionChipTextColor() const { + return ash_color_provider_->GetContentLayerColor( + AshColorProvider::ContentLayerType::kTextColorPrimary); +} + +SkColor AppListColorProviderImpl::GetAppListItemTextColor() const { + return ash_color_provider_->GetContentLayerColor( + AshColorProvider::ContentLayerType::kTextColorPrimary); +} + +} // namespace ash
diff --git a/ash/app_list/app_list_color_provider_impl.h b/ash/app_list/app_list_color_provider_impl.h new file mode 100644 index 0000000..6d4b411 --- /dev/null +++ b/ash/app_list/app_list_color_provider_impl.h
@@ -0,0 +1,37 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ASH_APP_LIST_APP_LIST_COLOR_PROVIDER_IMPL_H_ +#define ASH_APP_LIST_APP_LIST_COLOR_PROVIDER_IMPL_H_ + +#include "ash/public/cpp/app_list/app_list_color_provider.h" + +namespace ash { + +class AshColorProvider; + +class AppListColorProviderImpl : public AppListColorProvider { + public: + AppListColorProviderImpl(); + ~AppListColorProviderImpl() override; + // AppListColorProvider: + SkColor GetExpandArrowInkDropBaseColor() const override; + SkColor GetExpandArrowIconBaseColor() const override; + SkColor GetExpandArrowIconBackgroundColor() const override; + SkColor GetAppListBackgroundColor() const override; + SkColor GetSearchBoxBackgroundColor() const override; + SkColor GetSearchBoxPlaceholderTextColor() const override; + SkColor GetSearchBoxTextColor() const override; + SkColor GetSuggestionChipBackgroundColor() const override; + SkColor GetSuggestionChipTextColor() const override; + SkColor GetAppListItemTextColor() const override; + + private: + // Unowned. + AshColorProvider* const ash_color_provider_; +}; + +} // namespace ash + +#endif // ASH_APP_LIST_APP_LIST_COLOR_PROVIDER_IMPL_H_
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc index d68fd934..f859c87 100644 --- a/ash/app_list/app_list_controller_impl.cc +++ b/ash/app_list/app_list_controller_impl.cc
@@ -190,11 +190,11 @@ AppListControllerImpl::AppListControllerImpl() : model_(std::make_unique<AppListModel>()), + color_provider_(AppListColorProviderImpl()), presenter_(std::make_unique<AppListPresenterDelegateImpl>(this)), is_notification_indicator_enabled_( ::features::IsNotificationIndicatorEnabled()) { model_->AddObserver(this); - SessionControllerImpl* session_controller = Shell::Get()->session_controller(); session_controller->AddObserver(this); @@ -1510,6 +1510,10 @@ return Shell::Get()->tablet_mode_controller()->InTabletMode(); } +AppListColorProviderImpl* AppListControllerImpl::GetColorProvider() { + return &color_provider_; +} + void AppListControllerImpl::RecordAppLaunched( AppListLaunchedFrom launched_from) { RecordAppListAppLaunched(launched_from, GetAppListViewState(), IsTabletMode(),
diff --git a/ash/app_list/app_list_controller_impl.h b/ash/app_list/app_list_controller_impl.h index 6a0d60cb..439d8f6 100644 --- a/ash/app_list/app_list_controller_impl.h +++ b/ash/app_list/app_list_controller_impl.h
@@ -10,6 +10,7 @@ #include <utility> #include <vector> +#include "ash/app_list/app_list_color_provider_impl.h" #include "ash/app_list/app_list_metrics.h" #include "ash/app_list/app_list_presenter_impl.h" #include "ash/app_list/app_list_view_delegate.h" @@ -224,6 +225,7 @@ gfx::Rect SnapBoundsToDisplayEdge(const gfx::Rect& bounds) override; int GetShelfSize() override; bool IsInTabletMode() override; + AppListColorProviderImpl* GetColorProvider(); // Notifies observers of AppList visibility changes. void OnVisibilityChanged(bool visible, int64_t display_id); @@ -414,6 +416,10 @@ std::unique_ptr<AppListModel> model_; SearchModel search_model_; + // Used to fetch colors from AshColorProvider. Should be destructed after + // |presenter_| and UI. + AppListColorProviderImpl color_provider_; + // |presenter_| should be put below |client_| and |model_| to prevent a crash // in destruction. AppListPresenterImpl presenter_;
diff --git a/ash/app_list/app_list_presenter_delegate_unittest.cc b/ash/app_list/app_list_presenter_delegate_unittest.cc index 13edd060..f8a80d7 100644 --- a/ash/app_list/app_list_presenter_delegate_unittest.cc +++ b/ash/app_list/app_list_presenter_delegate_unittest.cc
@@ -32,6 +32,7 @@ #include "ash/keyboard/keyboard_controller_impl.h" #include "ash/keyboard/ui/keyboard_ui_controller.h" #include "ash/keyboard/ui/test/keyboard_test_util.h" +#include "ash/public/cpp/app_list/app_list_color_provider.h" #include "ash/public/cpp/app_list/app_list_config.h" #include "ash/public/cpp/app_list/app_list_features.h" #include "ash/public/cpp/app_list/app_list_switches.h" @@ -3787,10 +3788,11 @@ // The opacity should be set on the color, not the layer. Setting opacity on // the layer will change the opacity of the blur effect, which is not desired. - const U8CPU clamshell_background_opacity = static_cast<U8CPU>(255 * 0.74); - EXPECT_EQ(SkColorSetA(AppListView::kDefaultBackgroundColor, - clamshell_background_opacity), - GetAppListView()->GetAppListBackgroundShieldColorForTest()); + const U8CPU clamshell_background_opacity = static_cast<U8CPU>(255 * 0.8); + EXPECT_EQ( + SkColorSetA(AppListColorProvider::Get()->GetAppListBackgroundColor(), + clamshell_background_opacity), + GetAppListView()->GetAppListBackgroundShieldColorForTest()); EXPECT_EQ(1, GetAppListView() ->GetAppListBackgroundShieldForTest() ->layer() @@ -3800,9 +3802,10 @@ EnableTabletMode(true); const U8CPU tablet_background_opacity = static_cast<U8CPU>(0); - EXPECT_EQ(SkColorSetA(AppListView::kDefaultBackgroundColor, - tablet_background_opacity), - GetAppListView()->GetAppListBackgroundShieldColorForTest()); + EXPECT_EQ( + SkColorSetA(AppListColorProvider::Get()->GetAppListBackgroundColor(), + tablet_background_opacity), + GetAppListView()->GetAppListBackgroundShieldColorForTest()); EXPECT_EQ(1, GetAppListView() ->GetAppListBackgroundShieldForTest() ->layer()
diff --git a/ash/app_list/app_list_presenter_impl_unittest.cc b/ash/app_list/app_list_presenter_impl_unittest.cc index 8a66e0a7..43411d4 100644 --- a/ash/app_list/app_list_presenter_impl_unittest.cc +++ b/ash/app_list/app_list_presenter_impl_unittest.cc
@@ -9,6 +9,7 @@ #include <vector> #include "ash/app_list/test/app_list_test_view_delegate.h" +#include "ash/app_list/test/test_app_list_color_provider.h" #include "ash/app_list/views/app_list_item_view.h" #include "ash/app_list/views/app_list_view.h" #include "ash/app_list/views/apps_grid_view.h" @@ -103,6 +104,7 @@ std::unique_ptr<AppListPresenterImpl> presenter_; AppListPresenterDelegateTest* presenter_delegate_ = nullptr; std::unique_ptr<aura::Window> container_; + std::unique_ptr<TestAppListColorProvider> app_list_color_provider_; DISALLOW_COPY_AND_ASSIGN(AppListPresenterImplTest); }; @@ -120,10 +122,12 @@ presenter_delegate_ = presenter_delegate.get(); presenter_ = std::make_unique<AppListPresenterImpl>(std::move(presenter_delegate)); + app_list_color_provider_ = std::make_unique<TestAppListColorProvider>(); } void AppListPresenterImplTest::TearDown() { container_.reset(); + app_list_color_provider_.reset(); AuraTestBase::TearDown(); }
diff --git a/ash/app_list/test/run_all_unittests.cc b/ash/app_list/test/run_all_unittests.cc index 004533c..78bc743 100644 --- a/ash/app_list/test/run_all_unittests.cc +++ b/ash/app_list/test/run_all_unittests.cc
@@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "ash/app_list/test/test_app_list_color_provider.h" #include "base/bind.h" #include "base/command_line.h" #include "base/compiler_specific.h" @@ -43,11 +44,14 @@ base::DiscardableMemoryAllocator::SetInstance( &discardable_memory_allocator_); + app_list_color_provider_ = + std::make_unique<ash::TestAppListColorProvider>(); env_ = aura::Env::CreateInstance(); } void Shutdown() override { env_.reset(); + app_list_color_provider_.reset(); ui::ResourceBundle::CleanupSharedInstance(); base::TestSuite::Shutdown(); } @@ -55,6 +59,7 @@ private: std::unique_ptr<aura::Env> env_; base::TestDiscardableMemoryAllocator discardable_memory_allocator_; + std::unique_ptr<ash::TestAppListColorProvider> app_list_color_provider_; DISALLOW_COPY_AND_ASSIGN(AppListTestSuite); };
diff --git a/ash/app_list/test/test_app_list_color_provider.cc b/ash/app_list/test/test_app_list_color_provider.cc new file mode 100644 index 0000000..2fd84042 --- /dev/null +++ b/ash/app_list/test/test_app_list_color_provider.cc
@@ -0,0 +1,51 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/app_list/test/test_app_list_color_provider.h" + +#include "ui/gfx/color_palette.h" + +namespace ash { + +SkColor TestAppListColorProvider::GetExpandArrowInkDropBaseColor() const { + return SK_ColorWHITE; +} + +SkColor TestAppListColorProvider::GetExpandArrowIconBaseColor() const { + return gfx::kGoogleGrey200; +} + +SkColor TestAppListColorProvider::GetExpandArrowIconBackgroundColor() const { + return SkColorSetA(SK_ColorWHITE, 0x1A); +} + +SkColor TestAppListColorProvider::GetAppListBackgroundColor() const { + return gfx::kGoogleGrey900; +} + +SkColor TestAppListColorProvider::GetSearchBoxBackgroundColor() const { + return SK_ColorWHITE; +} + +SkColor TestAppListColorProvider::GetSearchBoxPlaceholderTextColor() const { + return gfx::kGoogleGrey500; +} + +SkColor TestAppListColorProvider::GetSearchBoxTextColor() const { + return gfx::kGoogleGrey900; +} + +SkColor TestAppListColorProvider::GetSuggestionChipBackgroundColor() const { + return SkColorSetA(SK_ColorWHITE, 0x1A); +} + +SkColor TestAppListColorProvider::GetSuggestionChipTextColor() const { + return gfx::kGoogleGrey900; +} + +SkColor TestAppListColorProvider::GetAppListItemTextColor() const { + return gfx::kGoogleGrey900; +} + +} // namespace ash
diff --git a/ash/app_list/test/test_app_list_color_provider.h b/ash/app_list/test/test_app_list_color_provider.h new file mode 100644 index 0000000..047240a --- /dev/null +++ b/ash/app_list/test/test_app_list_color_provider.h
@@ -0,0 +1,33 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ASH_APP_LIST_TEST_TEST_APP_LIST_COLOR_PROVIDER_H_ +#define ASH_APP_LIST_TEST_TEST_APP_LIST_COLOR_PROVIDER_H_ + +#include "ash/public/cpp/app_list/app_list_color_provider.h" + +namespace ash { + +class TestAppListColorProvider : public AppListColorProvider { + public: + TestAppListColorProvider() = default; + ~TestAppListColorProvider() override = default; + + public: + // AppListColorProvider: + SkColor GetExpandArrowInkDropBaseColor() const override; + SkColor GetExpandArrowIconBaseColor() const override; + SkColor GetExpandArrowIconBackgroundColor() const override; + SkColor GetAppListBackgroundColor() const override; + SkColor GetSearchBoxBackgroundColor() const override; + SkColor GetSearchBoxPlaceholderTextColor() const override; + SkColor GetSearchBoxTextColor() const override; + SkColor GetSuggestionChipBackgroundColor() const override; + SkColor GetSuggestionChipTextColor() const override; + SkColor GetAppListItemTextColor() const override; +}; + +} // namespace ash + +#endif // ASH_APP_LIST_TEST_TEST_APP_LIST_COLOR_PROVIDER_H_
diff --git a/ash/app_list/views/app_list_item_view.cc b/ash/app_list/views/app_list_item_view.cc index 23442da0..4c9ae3b 100644 --- a/ash/app_list/views/app_list_item_view.cc +++ b/ash/app_list/views/app_list_item_view.cc
@@ -14,6 +14,7 @@ #include "ash/app_list/model/app_list_item.h" #include "ash/app_list/views/app_list_menu_model_adapter.h" #include "ash/app_list/views/apps_grid_view.h" +#include "ash/public/cpp/app_list/app_list_color_provider.h" #include "ash/public/cpp/app_list/app_list_config.h" #include "ash/public/cpp/app_list/app_list_switches.h" #include "ash/public/cpp/app_list/app_list_types.h" @@ -69,9 +70,6 @@ // The drag and drop icon scaling up or down animation transition duration. constexpr int kDragDropAppIconScaleTransitionInMs = 200; -// The color of the title for the tiles within folder. -constexpr SkColor kFolderGridTitleColor = SK_ColorBLACK; - // The color of the focus ring within a folder. constexpr SkColor kFolderGridFocusRingColor = gfx::kGoogleBlue600; @@ -335,9 +333,9 @@ title->SetHandlesTooltips(false); title->SetFontList(GetAppListConfig().app_title_font()); title->SetHorizontalAlignment(gfx::ALIGN_CENTER); - title->SetEnabledColor(apps_grid_view_->is_in_folder() - ? kFolderGridTitleColor - : GetAppListConfig().grid_title_color()); + title->SetEnabledColor( + AppListColorProvider::Get()->GetAppListItemTextColor()); + if (!is_in_folder) { gfx::ShadowValues title_shadow = gfx::ShadowValues( 1,
diff --git a/ash/app_list/views/app_list_view.cc b/ash/app_list/views/app_list_view.cc index 3def40b..121107a 100644 --- a/ash/app_list/views/app_list_view.cc +++ b/ash/app_list/views/app_list_view.cc
@@ -19,6 +19,7 @@ #include "ash/app_list/views/search_box_view.h" #include "ash/assistant/ui/assistant_ui_constants.h" #include "ash/keyboard/ui/keyboard_ui_controller.h" +#include "ash/public/cpp/app_list/app_list_color_provider.h" #include "ash/public/cpp/app_list/app_list_config.h" #include "ash/public/cpp/app_list/app_list_config_provider.h" #include "ash/public/cpp/app_list/app_list_features.h" @@ -168,25 +169,8 @@ SkColor GetBackgroundShieldColor(const std::vector<SkColor>& colors, float color_opacity) { const U8CPU sk_opacity_value = static_cast<U8CPU>(255 * color_opacity); - - const SkColor default_color = - SkColorSetA(AppListView::kDefaultBackgroundColor, sk_opacity_value); - - if (colors.empty()) - return default_color; - - DCHECK_EQ(static_cast<size_t>(ColorProfileType::NUM_OF_COLOR_PROFILES), - colors.size()); - const SkColor dark_muted = - colors[static_cast<int>(ColorProfileType::DARK_MUTED)]; - if (SK_ColorTRANSPARENT == dark_muted) - return default_color; - - return SkColorSetA( - color_utils::GetResultingPaintColor( - SkColorSetA(SK_ColorBLACK, AppListView::kAppListColorDarkenAlpha), - dark_muted), - sk_opacity_value); + return SkColorSetA(AppListColorProvider::Get()->GetAppListBackgroundColor(), + sk_opacity_value); } DEFINE_UI_CLASS_PROPERTY_KEY(bool, kExcludeWindowFromEventHandling, false) @@ -447,7 +431,7 @@ class AppListBackgroundShieldView : public views::View { public: explicit AppListBackgroundShieldView(int shelf_background_corner_radius) - : color_(AppListView::kDefaultBackgroundColor), + : color_(AppListColorProvider::Get()->GetAppListBackgroundColor()), shelf_background_corner_radius_(shelf_background_corner_radius) { SetPaintToLayer(ui::LAYER_SOLID_COLOR); layer()->SetFillsBoundsOpaquely(false);
diff --git a/ash/app_list/views/app_list_view.h b/ash/app_list/views/app_list_view.h index 831ba63..73f2cf65 100644 --- a/ash/app_list/views/app_list_view.h +++ b/ash/app_list/views/app_list_view.h
@@ -87,14 +87,11 @@ static constexpr float kAppListOpacity = 0.95; // The opacity of the app list background with blur. - static constexpr float kAppListOpacityWithBlur = 0.74; + static constexpr float kAppListOpacityWithBlur = 0.8; // The preferred blend alpha with wallpaper color for background. static constexpr int kAppListColorDarkenAlpha = 178; - // The defualt color of the app list background. - static constexpr SkColor kDefaultBackgroundColor = gfx::kGoogleGrey900; - // The duration the AppListView ignores scroll events which could transition // its state. static constexpr int kScrollIgnoreTimeMs = 500;
diff --git a/ash/app_list/views/expand_arrow_view.cc b/ash/app_list/views/expand_arrow_view.cc index 0ff5b7e..59deca2 100644 --- a/ash/app_list/views/expand_arrow_view.cc +++ b/ash/app_list/views/expand_arrow_view.cc
@@ -11,6 +11,7 @@ #include "ash/app_list/views/app_list_view.h" #include "ash/app_list/views/apps_container_view.h" #include "ash/app_list/views/contents_view.h" +#include "ash/public/cpp/app_list/app_list_color_provider.h" #include "ash/public/cpp/app_list/app_list_config.h" #include "ash/public/cpp/app_list/vector_icons/vector_icons.h" #include "base/bind.h" @@ -74,11 +75,6 @@ constexpr auto kCycleDuration = base::TimeDelta::FromMilliseconds(1000); constexpr auto kCycleInterval = base::TimeDelta::FromMilliseconds(500); -constexpr SkColor kExpandArrowColor = SK_ColorWHITE; -constexpr SkColor kPulseColor = SK_ColorWHITE; -constexpr SkColor kBackgroundColor = SkColorSetARGB(0xF, 0xFF, 0xFF, 0xFF); -constexpr SkColor kInkDropRippleColor = SkColorSetARGB(0x14, 0xFF, 0xFF, 0xFF); - constexpr SkColor kFocusRingColor = gfx::kGoogleBlue300; constexpr int kFocusRingWidth = 2; @@ -165,14 +161,15 @@ gfx::PointF arrow_points[kPointCount]; for (size_t i = 0; i < kPointCount; ++i) arrow_points[i] = kPeekingPoints[i]; - SkColor circle_color = kBackgroundColor; + SkColor circle_color = + AppListColorProvider::Get()->GetExpandArrowIconBackgroundColor(); const float progress = app_list_view_->GetAppListTransitionProgress( AppListView::kProgressFlagNone); circle_center.set_y(GetCircleCenterYForAppListProgress(progress)); arrow_origin.set_y(GetArrowYForAppListProgress(progress)); - // If transition progress is between peeking and fullscreen state, change the - // shape of the arrow and the opacity of the circle in addition to changing - // the circle and arrow position. + // If transition progress is between peeking and fullscreen state, change + // the shape of the arrow and the opacity of the circle in addition to + // changing the circle and arrow position. if (progress > 1) { const float peeking_to_full_progress = progress - 1; for (size_t i = 0; i < kPointCount; ++i) { @@ -213,7 +210,8 @@ cc::PaintFlags pulse_flags; pulse_flags.setStyle(cc::PaintFlags::kStroke_Style); pulse_flags.setColor( - SkColorSetA(kPulseColor, static_cast<U8CPU>(255 * pulse_opacity_))); + SkColorSetA(AppListColorProvider::Get()->GetExpandArrowIconBaseColor(), + static_cast<U8CPU>(255 * pulse_opacity_))); pulse_flags.setAntiAlias(true); canvas->DrawCircle(circle_center, pulse_radius_, pulse_flags); } @@ -231,7 +229,9 @@ cc::PaintFlags arrow_flags; arrow_flags.setAntiAlias(true); - arrow_flags.setColor(kExpandArrowColor); + + arrow_flags.setColor( + AppListColorProvider::Get()->GetExpandArrowIconBaseColor()); arrow_flags.setStrokeWidth(kExpandArrowStrokeWidth); arrow_flags.setStrokeCap(cc::PaintFlags::Cap::kRound_Cap); arrow_flags.setStrokeJoin(cc::PaintFlags::Join::kRound_Join); @@ -290,7 +290,8 @@ const { return std::make_unique<views::FloodFillInkDropRipple>( size(), GetLocalBounds().InsetsFrom(GetCircleBounds()), - GetInkDropCenterBasedOnLastEvent(), kInkDropRippleColor, 1.0f); + GetInkDropCenterBasedOnLastEvent(), + AppListColorProvider::Get()->GetExpandArrowInkDropBaseColor(), 1.0f); } void ExpandArrowView::AnimationProgressed(const gfx::Animation* animation) {
diff --git a/ash/app_list/views/search_box_view.cc b/ash/app_list/views/search_box_view.cc index 2adf8dec..7e088fdb 100644 --- a/ash/app_list/views/search_box_view.cc +++ b/ash/app_list/views/search_box_view.cc
@@ -21,6 +21,7 @@ #include "ash/app_list/views/search_result_base_view.h" #include "ash/app_list/views/search_result_page_view.h" #include "ash/keyboard/ui/keyboard_ui_controller.h" +#include "ash/public/cpp/app_list/app_list_color_provider.h" #include "ash/public/cpp/app_list/app_list_config.h" #include "ash/public/cpp/app_list/app_list_features.h" #include "ash/public/cpp/app_list/vector_icons/vector_icons.h" @@ -372,7 +373,7 @@ SkColor SearchBoxView::GetBackgroundColorForState(AppListState state) const { if (state == AppListState::kStateSearchResults) return AppListConfig::instance().card_background_color(); - return search_box::kSearchBoxBackgroundDefault; + return AppListColorProvider::Get()->GetSearchBoxBackgroundColor(); } void SearchBoxView::ShowZeroStateSuggestions() { @@ -392,8 +393,11 @@ SetSearchBoxColor(colors[static_cast<int>(ColorProfileType::DARK_MUTED)]); UpdateSearchIcon(); - search_box()->set_placeholder_text_color(search_box_color()); - UpdateBackgroundColor(search_box::kSearchBoxBackgroundDefault); + AppListColorProvider* app_list_color_provider = AppListColorProvider::Get(); + search_box()->set_placeholder_text_color( + app_list_color_provider->GetSearchBoxPlaceholderTextColor()); + search_box()->SetTextColor(app_list_color_provider->GetSearchBoxTextColor()); + UpdateBackgroundColor(app_list_color_provider->GetSearchBoxBackgroundColor()); SchedulePaint(); }
diff --git a/ash/app_list/views/search_result_suggestion_chip_view.cc b/ash/app_list/views/search_result_suggestion_chip_view.cc index d0780d9..b7f1d71 100644 --- a/ash/app_list/views/search_result_suggestion_chip_view.cc +++ b/ash/app_list/views/search_result_suggestion_chip_view.cc
@@ -10,6 +10,7 @@ #include "ash/app_list/app_list_metrics.h" #include "ash/app_list/app_list_view_delegate.h" #include "ash/app_list/model/search/search_result.h" +#include "ash/public/cpp/app_list/app_list_color_provider.h" #include "ash/public/cpp/app_list/app_list_config.h" #include "ash/public/cpp/app_list/app_list_types.h" #include "ash/public/cpp/app_list/internal_app_id_constants.h" @@ -32,8 +33,6 @@ namespace { -constexpr SkColor kBackgroundColor = SkColorSetA(gfx::kGoogleGrey100, 0x14); -constexpr SkColor kTextColor = gfx::kGoogleGrey100; constexpr SkColor kRippleColor = SkColorSetA(gfx::kGoogleGrey100, 0x0F); constexpr SkColor kFocusRingColor = gfx::kGoogleBlue300; constexpr int kMaxTextWidth = 192; @@ -133,7 +132,8 @@ gfx::Rect bounds = GetContentsBounds(); // Background. - flags.setColor(kBackgroundColor); + flags.setColor( + AppListColorProvider::Get()->GetSuggestionChipBackgroundColor()); canvas->DrawRoundRect(bounds, height() / 2, flags); // Focus Ring should only be visible when keyboard traversal is occurring. @@ -240,10 +240,11 @@ // Text. text_view_->SetAutoColorReadabilityEnabled(false); - text_view_->SetEnabledColor(kTextColor); text_view_->SetSubpixelRenderingEnabled(false); text_view_->SetFontList(AppListConfig::instance().app_title_font()); SetText(base::string16()); + text_view_->SetEnabledColor( + AppListColorProvider::Get()->GetSuggestionChipTextColor()); AddChildView(text_view_); }
diff --git a/ash/public/cpp/BUILD.gn b/ash/public/cpp/BUILD.gn index cbdf588..d2cd3fa 100644 --- a/ash/public/cpp/BUILD.gn +++ b/ash/public/cpp/BUILD.gn
@@ -35,6 +35,8 @@ "android_intent_helper.cc", "android_intent_helper.h", "app_list/app_list_client.h", + "app_list/app_list_color_provider.cc", + "app_list/app_list_color_provider.h", "app_list/app_list_config.cc", "app_list/app_list_config.h", "app_list/app_list_config_provider.cc",
diff --git a/ash/public/cpp/app_list/app_list_color_provider.cc b/ash/public/cpp/app_list/app_list_color_provider.cc new file mode 100644 index 0000000..41c49eea --- /dev/null +++ b/ash/public/cpp/app_list/app_list_color_provider.cc
@@ -0,0 +1,32 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ash/public/cpp/app_list/app_list_color_provider.h" + +#include "base/check_op.h" + +namespace ash { + +namespace { + +AppListColorProvider* g_instance = nullptr; + +} // namespace + +// static +AppListColorProvider* AppListColorProvider::Get() { + return g_instance; +} + +AppListColorProvider::AppListColorProvider() { + DCHECK_EQ(nullptr, g_instance); + g_instance = this; +} + +AppListColorProvider::~AppListColorProvider() { + DCHECK_EQ(this, g_instance); + g_instance = nullptr; +} + +} // namespace ash
diff --git a/ash/public/cpp/app_list/app_list_color_provider.h b/ash/public/cpp/app_list/app_list_color_provider.h new file mode 100644 index 0000000..867626df --- /dev/null +++ b/ash/public/cpp/app_list/app_list_color_provider.h
@@ -0,0 +1,36 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ASH_PUBLIC_CPP_APP_LIST_APP_LIST_COLOR_PROVIDER_H_ +#define ASH_PUBLIC_CPP_APP_LIST_APP_LIST_COLOR_PROVIDER_H_ + +#include "ash/public/cpp/ash_public_export.h" +#include "third_party/skia/include/core/SkColor.h" + +namespace ash { + +class ASH_PUBLIC_EXPORT AppListColorProvider { + public: + // Returns the singleton instance. + static AppListColorProvider* Get(); + + virtual SkColor GetExpandArrowInkDropBaseColor() const = 0; + virtual SkColor GetExpandArrowIconBaseColor() const = 0; + virtual SkColor GetExpandArrowIconBackgroundColor() const = 0; + virtual SkColor GetAppListBackgroundColor() const = 0; + virtual SkColor GetSearchBoxBackgroundColor() const = 0; + virtual SkColor GetSearchBoxPlaceholderTextColor() const = 0; + virtual SkColor GetSearchBoxTextColor() const = 0; + virtual SkColor GetSuggestionChipBackgroundColor() const = 0; + virtual SkColor GetSuggestionChipTextColor() const = 0; + virtual SkColor GetAppListItemTextColor() const = 0; + + protected: + AppListColorProvider(); + virtual ~AppListColorProvider(); +}; + +} // namespace ash + +#endif // ASH_PUBLIC_CPP_APP_LIST_APP_LIST_COLOR_PROVIDER_H_
diff --git a/ash/system/bluetooth/bluetooth_detailed_view.cc b/ash/system/bluetooth/bluetooth_detailed_view.cc index 0e9cbf7..bf269ed 100644 --- a/ash/system/bluetooth/bluetooth_detailed_view.cc +++ b/ash/system/bluetooth/bluetooth_detailed_view.cc
@@ -254,7 +254,7 @@ SetupConnectingScrollListItem(container); break; case BluetoothDeviceInfo::ConnectionState::kConnected: - SetupConnectedScrollListItem( + SetupNeutralColorConnectedScrollListItem( container, device->battery_info ? base::make_optional<uint8_t>( device->battery_info->battery_percentage)
diff --git a/ash/system/tray/tray_detailed_view.cc b/ash/system/tray/tray_detailed_view.cc index f408f17..cb83191 100644 --- a/ash/system/tray/tray_detailed_view.cc +++ b/ash/system/tray/tray_detailed_view.cc
@@ -353,10 +353,19 @@ } void TrayDetailedView::SetupConnectedScrollListItem(HoverHighlightView* view) { - SetupConnectedScrollListItem(view, base::nullopt /* battery_percentage */); + DCHECK(view->is_populated()); + + base::string16 status; + + view->SetSubText( + l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_NETWORK_STATUS_CONNECTED)); + + TrayPopupItemStyle style(TrayPopupItemStyle::FontStyle::CAPTION); + style.set_color_style(TrayPopupItemStyle::ColorStyle::CONNECTED); + style.SetupLabel(view->sub_text_label()); } -void TrayDetailedView::SetupConnectedScrollListItem( +void TrayDetailedView::SetupNeutralColorConnectedScrollListItem( HoverHighlightView* view, base::Optional<uint8_t> battery_percentage) { DCHECK(view->is_populated()); @@ -373,7 +382,14 @@ } TrayPopupItemStyle style(TrayPopupItemStyle::FontStyle::CAPTION); - style.set_color_style(TrayPopupItemStyle::ColorStyle::CONNECTED); + // The subtext is used for the "Connected" status and also the battery + // indicator percentage value. Using fixed green color may give the impression + // that the battery is in good state even though the charge value is low. In + // the future the UI will be re-implemented using a battery icon and different + // colors depending on the charge value (b/167556800). For now, we use the + // neutral gray color for both the "Connected" text and the battery value to + // avoid this confusion. + style.set_color_style(TrayPopupItemStyle::ColorStyle::INACTIVE); style.SetupLabel(view->sub_text_label()); }
diff --git a/ash/system/tray/tray_detailed_view.h b/ash/system/tray/tray_detailed_view.h index 7c2014b..d70405a 100644 --- a/ash/system/tray/tray_detailed_view.h +++ b/ash/system/tray/tray_detailed_view.h
@@ -97,9 +97,14 @@ void SetupConnectedScrollListItem(HoverHighlightView* view); // Adds connected sub label with the device's battery percentage to the |view| - // with appropriate style and updates accessibility label. - void SetupConnectedScrollListItem(HoverHighlightView* view, - base::Optional<uint8_t> battery_percentage); + // with appropriate style and updates accessibility label. Exclusively used + // for Bluetooth since its Connected state also contains the battery charge + // value and the color needs to be gray (neutral for all battery values). + // TODO(b/167556800): Remove this Bluetooth-specific view once the battery + // indicator has been re-implemented as an icon with its separate color. + void SetupNeutralColorConnectedScrollListItem( + HoverHighlightView* view, + base::Optional<uint8_t> battery_percentage); // Adds connecting sub label to the |view| with appropriate style and updates // accessibility label.
diff --git a/build/android/BUILD.gn b/build/android/BUILD.gn index 7fd790d6..4e06809 100644 --- a/build/android/BUILD.gn +++ b/build/android/BUILD.gn
@@ -16,6 +16,27 @@ create_cache = true } + android_library("lint_test_java") { + sources = [ "java/test/LintTest.java" ] + } + + android_apk("lint_test_apk") { + # This cannot be marked testonly since lint has special ignores for testonly + # targets. We need to test linting a normal apk target. + apk_name = "LintTest" + deps = [ ":lint_test_java" ] + android_manifest = "//build/android/AndroidManifest.xml" + } + + android_lint("android_lint_test") { + lint_expected_warnings = "InlinedApi,NewApi" + _test_apk_target = ":lint_test_apk" + deps = [ "${_test_apk_target}__java" ] + build_config_dep = "$_test_apk_target$build_config_target_suffix" + build_config = get_label_info(_test_apk_target, "target_gen_dir") + "/" + + get_label_info(_test_apk_target, "name") + ".build_config" + } + if (enable_jdk_library_desugaring) { dex_jdk_libs("all_jdk_libs") { output = "$target_out_dir/$target_name.l8.dex"
diff --git a/build/android/gyp/lint.py b/build/android/gyp/lint.py index df2389f..df416062 100755 --- a/build/android/gyp/lint.py +++ b/build/android/gyp/lint.py
@@ -8,6 +8,7 @@ from __future__ import print_function import argparse +import functools import logging import os import re @@ -149,6 +150,17 @@ root, encoding='utf-8')).toprettyxml(indent=' ')) +def _CheckLintWarning(expected_warnings, lint_output): + for expected_warning in expected_warnings.split(','): + expected_str = '[{}]'.format(expected_warning) + if expected_str not in lint_output: + raise Exception('Expected {!r} warning in lint output:\n{}.'.format( + expected_str, lint_output)) + + # Do not print warning + return '' + + def _RunLint(lint_binary_path, config_path, manifest_path, @@ -164,6 +176,7 @@ android_sdk_root, lint_gen_dir, baseline, + expected_warnings, testonly_target=False, warnings_as_errors=False): logging.info('Lint starting') @@ -249,6 +262,8 @@ # This filter is necessary for JDK11. stderr_filter = build_utils.FilterReflectiveAccessJavaWarnings stdout_filter = lambda x: build_utils.FilterLines(x, 'No issues found') + if expected_warnings: + stdout_filter = functools.partial(_CheckLintWarning, expected_warnings) start = time.time() logging.debug('Lint command %s', ' '.join(cmd)) @@ -339,6 +354,9 @@ parser.add_argument('--baseline', help='Baseline file to ignore existing errors and fail ' 'on new errors.') + parser.add_argument('--expected-warnings', + help='Comma separated list of warnings to test for in ' + 'the output, failing if not found.') args = parser.parse_args(build_utils.ExpandFileArgs(argv)) args.java_sources = build_utils.ParseGnList(args.java_sources) @@ -383,6 +401,7 @@ args.android_sdk_root, args.lint_gen_dir, args.baseline, + args.expected_warnings, testonly_target=args.testonly, warnings_as_errors=args.warnings_as_errors) logging.info('Creating stamp file')
diff --git a/build/android/java/test/LintTest.java b/build/android/java/test/LintTest.java new file mode 100644 index 0000000..898785f --- /dev/null +++ b/build/android/java/test/LintTest.java
@@ -0,0 +1,20 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package test; + +import android.app.Application; +import android.content.Context; + +public class LintTest extends Application { + public static String testTriggerInlinedApiCheck() { + // This was added in API level 30. + return Context.CONNECTIVITY_DIAGNOSTICS_SERVICE; + } + + public String testTriggerNewApiCheck() { + // This was added in API level 30. + return getApplicationContext().getAttributionTag(); + } +}
diff --git a/build/android/lint/suppress.py b/build/android/lint/suppress.py deleted file mode 100755 index 5e36d289..0000000 --- a/build/android/lint/suppress.py +++ /dev/null
@@ -1,131 +0,0 @@ -#!/usr/bin/env python -# -# Copyright (c) 2013 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. - -"""Add all generated lint_result.xml files to suppressions.xml""" - -# pylint: disable=no-member - -from __future__ import print_function - -import argparse -import os -import re -import sys -from xml.dom import minidom - -_BUILD_ANDROID_DIR = os.path.join(os.path.dirname(__file__), '..') -sys.path.append(_BUILD_ANDROID_DIR) - -_TMP_DIR_RE = re.compile(r'^/tmp/.*/(SRC_ROOT[0-9]+|PRODUCT_DIR)/') -_THIS_FILE = os.path.abspath(__file__) -_DEFAULT_CONFIG_PATH = os.path.join(os.path.dirname(_THIS_FILE), - 'suppressions.xml') -_INSERT_COMMENT = ('TODO: This line was added by suppress.py,' - ' please add an explanation.') - - -class _Issue(object): - - def __init__(self, dom_element): - self.regexps = set() - self.dom_element = dom_element - - -def _CollectIssuesFromDom(dom): - issues_dict = {} - for issue_element in dom.getElementsByTagName('issue'): - issue_id = issue_element.attributes['id'].value - issue = _Issue(issue_element) - issues_dict[issue_id] = issue - for child in issue_element.childNodes: - if child.nodeType != minidom.Node.ELEMENT_NODE: - continue - if child.tagName == 'ignore' and child.getAttribute('regexp'): - issue.regexps.add(child.getAttribute('regexp')) - - return issues_dict - - -def _TrimWhitespaceNodes(n): - """Remove all whitespace-only TEXT_NODEs.""" - rm_children = [] - for c in n.childNodes: - if c.nodeType == minidom.Node.TEXT_NODE and c.data.strip() == '': - rm_children.append(c) - else: - _TrimWhitespaceNodes(c) - - for c in rm_children: - n.removeChild(c) - - -def _ParseAndInsertNewSuppressions(result_path, config_path): - print('Parsing %s' % config_path) - config_dom = minidom.parse(config_path) - issues_dict = _CollectIssuesFromDom(config_dom) - print('Parsing and merging %s' % result_path) - dom = minidom.parse(result_path) - for issue_element in dom.getElementsByTagName('issue'): - issue_id = issue_element.attributes['id'].value - severity = issue_element.attributes['severity'].value - path = issue_element.getElementsByTagName( - 'location')[0].attributes['file'].value - # Strip temporary file path. - path = re.sub(_TMP_DIR_RE, '', path) - # Escape Java inner class name separator and suppress with regex instead - # of path. Doesn't use re.escape() as it is a bit too aggressive and - # escapes '_', causing trouble with PRODUCT_DIR. - regexp = path.replace('$', r'\$') - if issue_id not in issues_dict: - element = config_dom.createElement('issue') - element.attributes['id'] = issue_id - element.attributes['severity'] = severity - config_dom.documentElement.appendChild(element) - issue = _Issue(element) - issues_dict[issue_id] = issue - else: - issue = issues_dict[issue_id] - if issue.dom_element.getAttribute('severity') == 'ignore': - continue - - if regexp not in issue.regexps: - issue.regexps.add(regexp) - ignore_element = config_dom.createElement('ignore') - ignore_element.attributes['regexp'] = regexp - issue.dom_element.appendChild(config_dom.createComment(_INSERT_COMMENT)) - issue.dom_element.appendChild(ignore_element) - - for issue_id, issue in issues_dict.iteritems(): - if issue.dom_element.getAttribute('severity') == 'ignore': - print('Warning: [%s] is suppressed globally.' % issue_id) - - # toprettyxml inserts whitespace, so delete whitespace first. - _TrimWhitespaceNodes(config_dom.documentElement) - - with open(config_path, 'w') as f: - f.write(config_dom.toprettyxml(indent=' ', encoding='utf-8')) - print('Updated %s' % config_path) - - -def _Suppress(config_path, result_path): - _ParseAndInsertNewSuppressions(result_path, config_path) - - -def main(): - parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument('--config', - help='Path to suppression.xml config file', - default=_DEFAULT_CONFIG_PATH) - parser.add_argument('result_path', - help='Lint results xml file', - metavar='RESULT_FILE') - args = parser.parse_args() - - _Suppress(args.config, args.result_path) - - -if __name__ == '__main__': - main()
diff --git a/build/android/pylib/instrumentation/instrumentation_test_instance.py b/build/android/pylib/instrumentation/instrumentation_test_instance.py index f1a7a0b..3cc482f4 100644 --- a/build/android/pylib/instrumentation/instrumentation_test_instance.py +++ b/build/android/pylib/instrumentation/instrumentation_test_instance.py
@@ -556,6 +556,9 @@ self._replace_system_package = None self._initializeReplaceSystemPackageAttributes(args) + self._system_packages_to_remove = None + self._initializeSystemPackagesToRemoveAttributes(args) + self._use_webview_provider = None self._initializeUseWebviewProviderAttributes(args) @@ -767,6 +770,12 @@ return self._replace_system_package = args.replace_system_package + def _initializeSystemPackagesToRemoveAttributes(self, args): + if (not hasattr(args, 'system_packages_to_remove') + or not args.system_packages_to_remove): + return + self._system_packages_to_remove = args.system_packages_to_remove + def _initializeUseWebviewProviderAttributes(self, args): if (not hasattr(args, 'use_webview_provider') or not args.use_webview_provider): @@ -873,6 +882,10 @@ return self._symbolizer @property + def system_packages_to_remove(self): + return self._system_packages_to_remove + + @property def test_apk(self): return self._test_apk
diff --git a/build/android/pylib/local/device/local_device_instrumentation_test_run.py b/build/android/pylib/local/device/local_device_instrumentation_test_run.py index d696a04..bd587bc 100644 --- a/build/android/pylib/local/device/local_device_instrumentation_test_run.py +++ b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
@@ -193,6 +193,21 @@ steps.append(replace_package) + if self._test_instance.system_packages_to_remove: + + @trace_event.traced + def remove_packages(dev): + logging.info('Attempting to remove system packages %s', + self._test_instance.system_packages_to_remove) + system_app.RemoveSystemApps( + dev, self._test_instance.system_packages_to_remove) + logging.info('Done removing system packages') + + # This should be at the front in case we're removing the package to make + # room for another APK installation later on. Since we disallow + # concurrent adb with this option specified, this should be safe. + steps.insert(0, remove_packages) + if self._test_instance.use_webview_provider: @trace_event.traced def use_webview_provider(dev):
diff --git a/build/android/pylib/local/emulator/local_emulator_environment.py b/build/android/pylib/local/emulator/local_emulator_environment.py index 15f3c92..4981522 100644 --- a/build/android/pylib/local/emulator/local_emulator_environment.py +++ b/build/android/pylib/local/emulator/local_emulator_environment.py
@@ -32,7 +32,9 @@ self._writable_system = ((hasattr(args, 'use_webview_provider') and args.use_webview_provider) or (hasattr(args, 'replace_system_package') - and args.replace_system_package)) + and args.replace_system_package) + or (hasattr(args, 'system_packages_to_remove') + and args.system_packages_to_remove)) self._emulator_instances = [] self._device_serials = []
diff --git a/build/android/test_runner.py b/build/android/test_runner.py index 521afea..4a76eef 100755 --- a/build/android/test_runner.py +++ b/build/android/test_runner.py
@@ -496,6 +496,15 @@ 'the first element being the package and the second the path to the ' 'replacement APK. Only supports replacing one package. Example: ' '--replace-system-package com.example.app,path/to/some.apk') + parser.add_argument( + '--remove-system-package', + default=[], + action='append', + dest='system_packages_to_remove', + help='Specifies a system package to remove before testing if it exists ' + 'on the system. WARNING: THIS WILL PERMANENTLY REMOVE THE SYSTEM APP. ' + 'Unlike --replace-system-package, the app will not be restored after ' + 'tests are finished.') parser.add_argument( '--use-webview-provider', @@ -1061,13 +1070,18 @@ else: parser.error('unrecognized arguments: %s' % ' '.join(unknown_args)) - # --replace-system-package has the potential to cause issues if - # --enable-concurrent-adb is set, so disallow that combination - if (hasattr(args, 'replace_system_package') and - hasattr(args, 'enable_concurrent_adb') and args.replace_system_package and - args.enable_concurrent_adb): - parser.error('--replace-system-package and --enable-concurrent-adb cannot ' - 'be used together') + # --replace-system-package/--remove-system-package has the potential to cause + # issues if --enable-concurrent-adb is set, so disallow that combination. + concurrent_adb_enabled = (hasattr(args, 'enable_concurrent_adb') + and args.enable_concurrent_adb) + replacing_system_packages = (hasattr(args, 'replace_system_package') + and args.replace_system_package) + removing_system_packages = (hasattr(args, 'system_packages_to_remove') + and args.system_packages_to_remove) + if (concurrent_adb_enabled + and (replacing_system_packages or removing_system_packages)): + parser.error('--enable-concurrent-adb cannot be used with either ' + '--replace-system-package or --remove-system-package') # --use-webview-provider has the potential to cause issues if # --enable-concurrent-adb is set, so disallow that combination
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni index 6aad6e7d..1003633 100644 --- a/build/config/android/internal_rules.gni +++ b/build/config/android/internal_rules.gni
@@ -988,11 +988,9 @@ pool = "//build/toolchain:link_pool($default_toolchain)" } - # Lint only requires generated sources and generated resources from the - # build. Since turbine __header targets already depend on and generate all - # the generated sources, and the __assetres targets include all generated - # resources, lint only needs to depend on the header and assetres targets - # rather than the top level java targets. + # Lint requires generated sources and generated resources from the build. + # Turbine __header targets depend on all generated sources, and the + # __assetres targets depend on all generated resources. if (defined(invoker.deps)) { foreach(_dep, invoker.deps) { _target_label = get_label_info(_dep, "label_no_toolchain") @@ -1000,16 +998,12 @@ filter_exclude([ _target_label ], _java_resource_patterns) != []) { deps += [ - # Strictly speaking the __header target is sufficient, since they - # already depend on resources (due to srcjar deps), but prefer to - # be more explicit here since if/when __header targets stop - # depending on resources (e.g. if R.java generation moves to java - # targets), lint will not be affected. "${_target_label}__assetres", "${_target_label}__header", ] } else { - # Keep non-java deps as they may generate files used by lint. + # Keep non-java deps as they may generate files used only by lint. + # e.g. generated suppressions.xml files. deps += [ _dep ] } } @@ -1112,6 +1106,13 @@ # The full classpath is required for annotation checks like @IntDef. "--classpath=@FileArg($_rebased_build_config:deps_info:javac_full_interface_classpath)", ] + + if (defined(invoker.lint_expected_warnings)) { + args += [ + "--expected-warnings", + invoker.lint_expected_warnings, + ] + } } outputs = [ _stamp_path ]
diff --git a/build/mac_toolchain.py b/build/mac_toolchain.py index f851503..4874184 100755 --- a/build/mac_toolchain.py +++ b/build/mac_toolchain.py
@@ -34,9 +34,9 @@ # This contains binaries from Xcode 11.2.1, along with the 10.15 SDKs (aka # 11B53). 'default': 'wXywrnOhzFxwLYlwO62UzRxVCjnu6DoSI2D2jrCd00gC', - # This contains binaries from Xcode 12 beta 6, along with the - # 11 SDK (aka 12A8189n). - 'xcode_12_beta': 'ryuntti0oS9S1LFoiPfcSQ7ax5RkYU2kz788XMbEnSUC', + # This contains binaries from Xcode 12.2 beta, along with the + # 11 SDK (aka 12B5018i). + 'xcode_12_beta': 'WYCYb9qqIJtWJk4y23RGbsd7FLJPflS6weNRH3DnNLkC', } # The toolchain will not be downloaded if the minimum OS version is not met.
diff --git a/build/sanitizers/lsan_suppressions.cc b/build/sanitizers/lsan_suppressions.cc index c8053c21..9c0fec4 100644 --- a/build/sanitizers/lsan_suppressions.cc +++ b/build/sanitizers/lsan_suppressions.cc
@@ -55,9 +55,6 @@ // impossible, i.e. when enabling leak detection for the first time for a // test target with pre-existing leaks. - // http://crbug.com/356306 - "leak:content::SetProcessTitleFromCommandLine\n" - // https://crbug.com/755670 "leak:third_party/yasm/\n"
diff --git a/build/xcode_binaries.yaml b/build/xcode_binaries.yaml index 895cbbf..b77cc0d 100644 --- a/build/xcode_binaries.yaml +++ b/build/xcode_binaries.yaml
@@ -2,7 +2,7 @@ # To use this: # 1) Move Xcode.app to the same directory as this file. # 2) Rename Xcode.app to xcode_binaries -# 3) Call `cipd create --pkg-def xcode_binaries.yaml` +# 3) Call `cipd create --pkg-def build/xcode_binaries.yaml` # To deploy the newly created cipd package across the fleet, modify # mac_toolchain.py to point to the new cipd hash. #
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc index 738ddc04..dcc20fa9 100644 --- a/cc/trees/layer_tree_host_impl.cc +++ b/cc/trees/layer_tree_host_impl.cc
@@ -3927,12 +3927,16 @@ if (browser_controls_offset_manager_->HasAnimation()) SetNeedsOneBeginImplFrame(); - if (active_tree_->TotalScrollOffset().y() == 0.f) + if (active_tree_->TotalScrollOffset().y() == 0.f || + OnlyExpandTopControlsAtPageTop()) { return false; + } if (scroll_delta.IsZero()) return false; + // This counter-scrolls the page to keep the appearance of the page content + // being fixed while the browser controls animate. viewport().ScrollBy(scroll_delta, /*viewport_point=*/gfx::Point(), /*is_wheel_scroll=*/false,
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc index 10c398ec..1426c939 100644 --- a/cc/trees/layer_tree_host_impl_unittest.cc +++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -11998,6 +11998,37 @@ } protected: + void Scroll(float y) { + ASSERT_EQ(ScrollThread::SCROLL_ON_IMPL_THREAD, + GetInputHandler() + .ScrollBegin(BeginState(gfx::Point(), gfx::Vector2dF(0, 50), + ui::ScrollInputType::kTouchscreen) + .get(), + ui::ScrollInputType::kTouchscreen) + .thread); + ASSERT_TRUE( + GetInputHandler() + .ScrollUpdate(UpdateState(gfx::Point(), gfx::Vector2dF(0, y), + ui::ScrollInputType::kTouchscreen) + .get()) + .did_scroll); + GetInputHandler().ScrollEnd(); + } + + void RunAnimation() { + viz::BeginFrameArgs begin_frame_args = viz::CreateBeginFrameArgsForTesting( + BEGINFRAME_FROM_HERE, 0, 1, base::TimeTicks::Now()); + do { + did_request_next_frame_ = false; + host_impl_->WillBeginImplFrame(begin_frame_args); + host_impl_->Animate(); + host_impl_->DidFinishImplFrame(begin_frame_args); + + begin_frame_args.frame_time += base::TimeDelta::FromMilliseconds(5); + begin_frame_args.frame_id.sequence_number++; + } while (did_request_next_frame_); + } + static const int top_controls_height_; }; @@ -12456,6 +12487,89 @@ GetInputHandler().ScrollEnd(); } +// Tests that when animating the top controls down, the viewport doesn't counter +// scroll if it's not already scrolled. +TEST_F(LayerTreeHostImplWithBrowserControlsTest, + AnimationDoesntScrollUnscrolledViewport) { + // Initialize with 50px browser controls, 200px contents, and 100px viewport. + SetupViewportLayersInnerScrolls(gfx::Size(100, 100), gfx::Size(100, 200)); + + LayerTreeImpl* layer_tree_impl = host_impl_->active_tree(); + ASSERT_EQ(1, layer_tree_impl->CurrentTopControlsShownRatio()); + + // Scroll down 10px, which should partially hide the browser controls, but + // not scroll the viewport. + Scroll(10.f); + EXPECT_EQ(0.8f, host_impl_->active_tree()->CurrentTopControlsShownRatio()); + EXPECT_EQ(0.f, host_impl_->viewport().TotalScrollOffset().y()); + + // Let the top controls animate back down. + RunAnimation(); + + EXPECT_EQ(1, layer_tree_impl->CurrentTopControlsShownRatio()); + EXPECT_EQ(0.f, host_impl_->viewport().TotalScrollOffset().y()); +} + +// Tests that when animating the top controls down, the viewport counter scrolls +// if it's partially scrolled down. +TEST_F(LayerTreeHostImplWithBrowserControlsTest, + AnimationScrollsScrolledViewport) { + // Initialize with 50px browser controls, 200px contents, and 100px viewport. + SetupViewportLayersInnerScrolls(gfx::Size(100, 100), gfx::Size(100, 200)); + + LayerTreeImpl* layer_tree_impl = host_impl_->active_tree(); + ASSERT_EQ(1, layer_tree_impl->CurrentTopControlsShownRatio()); + + // Scroll down 60px, then up 40px, which should hide the browser controls and + // scroll the viewport 10px, then bring the browser controls down 40px. + Scroll(60.f); + EXPECT_EQ(0.f, host_impl_->active_tree()->CurrentTopControlsShownRatio()); + EXPECT_EQ(10.f, host_impl_->viewport().TotalScrollOffset().y()); + + Scroll(-40.f); + EXPECT_EQ(0.8f, host_impl_->active_tree()->CurrentTopControlsShownRatio()); + EXPECT_EQ(10.f, host_impl_->viewport().TotalScrollOffset().y()); + + // Let the top controls animate back down. + RunAnimation(); + + EXPECT_EQ(1, layer_tree_impl->CurrentTopControlsShownRatio()); + EXPECT_EQ(20.f, host_impl_->viewport().TotalScrollOffset().y()); +} + +// Tests that the page animates down with the top controls when +// BrowserControlParams.only_expand_top_controls_at_page_top is true. +TEST_F(LayerTreeHostImplWithBrowserControlsTest, + AnimationScrollsViewportWhenOnlyExpandTopControlsAtPageTopIsSet) { + // Initialize with 50px browser controls, 200px page contents, a 100px + // viewport, and only_expand_top_controls_at_page_top enabled. + SetupViewportLayersInnerScrolls(gfx::Size(100, 100), gfx::Size(100, 200)); + LayerTreeImpl* layer_tree_impl = host_impl_->active_tree(); + layer_tree_impl->SetBrowserControlsParams( + {/*top_controls_height=*/50.f, 0.f, 0.f, 0.f, false, false, + /*only_expand_top_controls_at_page_top=*/true}); + + ASSERT_EQ(1, layer_tree_impl->CurrentTopControlsShownRatio()); + + // Scroll down 60px, then up 50px, which should hide the browser controls and + // scroll the viewport 10px, then unscroll the viewport and bring the browser + // controls down 40px. + Scroll(60.f); + EXPECT_EQ(0.f, host_impl_->active_tree()->CurrentTopControlsShownRatio()); + EXPECT_EQ(10.f, host_impl_->viewport().TotalScrollOffset().y()); + + Scroll(-50.f); + EXPECT_EQ(0.8f, host_impl_->active_tree()->CurrentTopControlsShownRatio()); + EXPECT_EQ(0.f, host_impl_->viewport().TotalScrollOffset().y()); + + // Let the top controls animate back down. + RunAnimation(); + + // With only_expand_top_controls_at_page_top set, the page shouldn't scroll. + EXPECT_EQ(1, layer_tree_impl->CurrentTopControlsShownRatio()); + EXPECT_EQ(0.f, host_impl_->viewport().TotalScrollOffset().y()); +} + // Tests that when we set a child scroller (e.g. a scrolling div) as the outer // viewport, scrolling it controls the browser controls. TEST_P(LayerTreeHostImplBrowserControlsTest,
diff --git a/chrome/android/expectations/lint-suppressions.xml b/chrome/android/expectations/lint-suppressions.xml index c676756..0f3d357 100644 --- a/chrome/android/expectations/lint-suppressions.xml +++ b/chrome/android/expectations/lint-suppressions.xml
@@ -176,12 +176,8 @@ <ignore regexp="Static interface method requires API level 24"/> <!-- 1: TaskInfo is refactored at API 29. --> <ignore regexp="Field requires API level .*`android.app.TaskInfo"/> - <!-- 1: TODO(crbug.com/1082222): Fix --> - <ignore regexp="chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/header/HeaderView.java"/> <!-- 1: TODO(crbug.com/1085410): Fix --> <ignore regexp="components/content_capture/android/java/src/org/chromium/components/content_capture"/> - <!-- 1: TODO(wnwen): File bugs to fix --> - <ignore regexp="android_webview/support_library/boundary_interfaces/src/org/chromium/support_lib_boundary/util/BoundaryInterfaceReflectionUtil.java"/> <!-- Endnote: Please specify number of suppressions when adding more --> </issue> <!-- This warning just adds a lot of false positives. -->
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/UndoGroupSnackbarController.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/UndoGroupSnackbarController.java index d102a91..4c0dc1f 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/UndoGroupSnackbarController.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/UndoGroupSnackbarController.java
@@ -99,6 +99,12 @@ } @Override + public void willCloseTab(Tab tab, boolean animate) { + mSnackbarManageable.getSnackbarManager().dismissSnackbars( + UndoGroupSnackbarController.this); + } + + @Override public void didCloseTab(int tabId, boolean incognito) { mSnackbarManageable.getSnackbarManager().dismissSnackbars( UndoGroupSnackbarController.this);
diff --git a/chrome/android/java/res/layout/main.xml b/chrome/android/java/res/layout/main.xml index e650357..f9ec479 100644 --- a/chrome/android/java/res/layout/main.xml +++ b/chrome/android/java/res/layout/main.xml
@@ -21,6 +21,13 @@ android:layout_height="match_parent" android:visibility="gone" /> + <org.chromium.components.messages.MessageContainer + android:id="@+id/message_container" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:visibility="gone" + android:layout_gravity="start|top" /> + <org.chromium.chrome.browser.ui.BottomContainer android:id="@+id/bottom_container" android:layout_width="match_parent"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivity.java index 7407d6db..11744a6 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/FirstRunActivity.java
@@ -25,6 +25,7 @@ import org.chromium.chrome.browser.datareduction.DataReductionProxyUma; import org.chromium.chrome.browser.metrics.UmaUtils; import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings; +import org.chromium.chrome.browser.privacy.settings.PrivacyPreferencesManager; import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory; import org.chromium.chrome.browser.searchwidget.SearchWidgetProvider; import org.chromium.ui.base.LocalizationUtils; @@ -452,6 +453,13 @@ // intent. The re-launched intent will still need to know to avoid the FRE. FirstRunStatus.setEphemeralSkipFirstRun(true); + // This pref is written to have a value of true during the FRE's startup. If the user + // presses the accept ToS button, this pref's value is overridden with their choice. + // However, when the FRE is skipped, that initial value is the opposite of what we want, so + // manually set it to false here. + // TODO(https://crbug.com/1128955): Remove this once the default is not written on startup. + PrivacyPreferencesManager.getInstance().setUsageAndCrashReporting(false); + launchPendingIntentAndFinish(); }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationTest.java index 5683993..523ed8d 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/firstrun/FirstRunIntegrationTest.java
@@ -42,6 +42,7 @@ import org.chromium.chrome.browser.locale.LocaleManager; import org.chromium.chrome.browser.locale.LocaleManager.SearchEnginePromoType; import org.chromium.chrome.browser.policy.EnterpriseInfo; +import org.chromium.chrome.browser.privacy.settings.PrivacyPreferencesManager; import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory; import org.chromium.chrome.test.ChromeJUnit4ClassRunner; import org.chromium.chrome.test.MultiActivityTestRule; @@ -286,6 +287,9 @@ "native never initialized."); waitForActivity(CustomTabActivity.class); + + Assert.assertFalse("Usage and crash reporting pref was set to true after skip", + PrivacyPreferencesManager.getInstance().isUsageAndCrashReportingPermittedByUser()); } @Test
diff --git a/chrome/app/os_settings_search_tag_strings.grdp b/chrome/app/os_settings_search_tag_strings.grdp index 0583987..d3fbffd7 100644 --- a/chrome/app/os_settings_search_tag_strings.grdp +++ b/chrome/app/os_settings_search_tag_strings.grdp
@@ -874,10 +874,10 @@ <message name="IDS_OS_SETTINGS_TAG_INPUT" translateable="false" desc="Text for search result item which, when clicked, navigates the user to input settings."> Input </message> - <message name="IDS_OS_SETTINGS_TAG_LANGUAGES_CHANGE_SYSTEM_LANGUAGE" translateable="false" desc="Text for search result item which, when clicked, navigates the user to languages and input settings, with allows users to change system display language."> - Change system language + <message name="IDS_OS_SETTINGS_TAG_LANGUAGES_CHANGE_DEVICE_LANGUAGE" desc="Text for search result item which, when clicked, navigates the user to languages and input settings, with allows users to change device language."> + Change device language </message> - <message name="IDS_OS_SETTINGS_TAG_LANGUAGES_OFFER_TRANSLATION" translateable="false" desc="Text for search result item which, when clicked, navigates the user to languages and input settings, with a toggle to enable/disable translation prompts."> + <message name="IDS_OS_SETTINGS_TAG_LANGUAGES_OFFER_TRANSLATION" desc="Text for search result item which, when clicked, navigates the user to languages and input settings, with a toggle to enable/disable translation prompts."> Translation suggestion </message> <message name="IDS_OS_SETTINGS_TAG_LANGUAGES_INPUT_ADD_LANGUAGE" desc="Text for search result item which, when clicked, navigates the user to languages and input settings, with an option to add a new language.">
diff --git a/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_LANGUAGES_CHANGE_DEVICE_LANGUAGE.png.sha1 b/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_LANGUAGES_CHANGE_DEVICE_LANGUAGE.png.sha1 new file mode 100644 index 0000000..d81a14f8 --- /dev/null +++ b/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_LANGUAGES_CHANGE_DEVICE_LANGUAGE.png.sha1
@@ -0,0 +1 @@ +4a77b4e19c981cb61e10dc5b518db4d2fb88b67d \ No newline at end of file
diff --git a/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_LANGUAGES_OFFER_TRANSLATION.png.sha1 b/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_LANGUAGES_OFFER_TRANSLATION.png.sha1 new file mode 100644 index 0000000..b1bee00 --- /dev/null +++ b/chrome/app/os_settings_search_tag_strings_grdp/IDS_OS_SETTINGS_TAG_LANGUAGES_OFFER_TRANSLATION.png.sha1
@@ -0,0 +1 @@ +b6fda98218a92e395bd540fc49ae8005b0754149 \ No newline at end of file
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp index 78ee781..a6e37c30 100644 --- a/chrome/app/os_settings_strings.grdp +++ b/chrome/app/os_settings_strings.grdp
@@ -250,41 +250,38 @@ <message name="IDS_OS_SETTINGS_LANGUAGES_INPUT_PAGE_TITLE" translateable="false" desc="A page under “Languages and input” section in the OS settings page that users can add, remove and manage input methods."> Input </message> - <message name="IDS_OS_SETTINGS_LANGUAGES_SYSTEM_LANGUAGE_TITLE" translateable="false" desc="The label for the section where users can see their display language. This language will be used for system surfaces (launcher, shelf, and notifications etc.) and the preferred application language if it’s provided by the application developer."> - System display language + <message name="IDS_OS_SETTINGS_LANGUAGES_DEVICE_LANGUAGE_TITLE" desc="The label for the section where users can see their device language. This language will be used for system surfaces (launcher, shelf, and notifications etc.)."> + Device language </message> - <message name="IDS_OS_SETTINGS_LANGUAGES_SYSTEM_LANGUAGE_DESCRIPTION" translateable="false" desc="The description for the section where users can see their display language. This language will be used for system surfaces (launcher, shelf, and notifications etc.) and the preferred application language if it’s provided by the application developer."> - System features like launcher, Settings and Files will appear in this language. - </message> - <message name="IDS_OS_SETTINGS_LANGUAGES_CHANGE_SYSTEM_LANGUAGE_BUTTON_LABEL" translateable="false" desc="The label for the button where users can change their display language. This language will be used for system surfaces (launcher, shelf, and notifications etc.) and the preferred application language if it’s provided by the application developer."> + <message name="IDS_OS_SETTINGS_LANGUAGES_CHANGE_DEVICE_LANGUAGE_BUTTON_LABEL" desc="The label for the button where users can change their device language. This language will be used for system surfaces (launcher, shelf, and notifications etc.)."> Change </message> - <message name="IDS_OS_SETTINGS_LANGUAGES_CHANGE_SYSTEM_LANGUAGE_BUTTON_DESCRIPTION" translateable="false" desc="The description read by screenreader for the button where users can change their display language. This language will be used for system surfaces (launcher, shelf, and notifications etc.) and the preferred application language if it’s provided by the application developer."> - Change system language. Current language is <ph name="LANGUAGE">$1<ex>English</ex></ph>. System features like launcher, Settings and Files will appear in the language you choose. + <message name="IDS_OS_SETTINGS_LANGUAGES_CHANGE_DEVICE_LANGUAGE_BUTTON_DESCRIPTION" desc="The description read by screenreader for the button where users can change their device language. This language will be used for system surfaces (launcher, shelf, and notifications etc.)."> + Change device language. Current language is <ph name="LANGUAGE">$1<ex>English</ex></ph>. </message> - <message name="IDS_OS_SETTINGS_LANGUAGES_CHANGE_DEVICE_LANGUAGE_DIALOG_TITLE" translateable="false" desc="Title for the dialog to change device language."> + <message name="IDS_OS_SETTINGS_LANGUAGES_CHANGE_DEVICE_LANGUAGE_DIALOG_TITLE" desc="Title for the dialog to change device language."> Change device language </message> - <message name="IDS_OS_SETTINGS_LANGUAGES_CHANGE_DEVICE_LANGUAGE_DIALOG_DESCRIPTION" translateable="false" desc="Description for the dialog to change device language."> - The change in device language requires a restart. <ph name="BEGIN_LINK_LEARN_MORE"><a target="_blank" href="$1"></ph>Learn more<ph name="END_LINK_LEARN_MORE"></a></ph> + <message name="IDS_OS_SETTINGS_LANGUAGES_CHANGE_DEVICE_LANGUAGE_DIALOG_DESCRIPTION" desc="Description for the dialog to change device language."> + You need to restart your Chromebook to change the device language. <ph name="BEGIN_LINK_LEARN_MORE"><a target="_blank" href="$1"></ph>Learn more<ph name="END_LINK_LEARN_MORE"></a></ph> </message> - <message name="IDS_OS_SETTINGS_LANGUAGES_CHANGE_DEVICE_LANGUAGE_CONFIRM_BUTTON_LABEL" translateable="false" desc="Label for the button where users can confirm the selected language and restart the device."> + <message name="IDS_OS_SETTINGS_LANGUAGES_CHANGE_DEVICE_LANGUAGE_CONFIRM_BUTTON_LABEL" desc="Label for the button where users can confirm the selected language and restart the device."> Confirm and restart </message> - <message name="IDS_OS_SETTINGS_LANGUAGES_LANGUAGES_PREFERENCE_TITLE" translateable="false" desc="The label of the section in the language settings page where users can add languages they read to an ordered list. The web content languages will be served in the order of the list."> - Languages you read + <message name="IDS_OS_SETTINGS_LANGUAGES_LANGUAGES_PREFERENCE_TITLE" desc="Title of section that lets users choose the language for apps and websites. The web content languages will be served in the order of the list."> + Languages for apps and websites </message> - <message name="IDS_OS_SETTINGS_LANGUAGES_LANGUAGES_PREFERENCE_DESCRIPTION" translateable="false" desc="The description of the section in the language settings page where users can add languages they read to an ordered list. The web content languages will be served in the order of the list."> - Add and order languages you read. Apps and websites will be displayed in the most preferred language available. <ph name="BEGIN_LINK_LEARN_MORE"><a target="_blank" href="$1"></ph>Learn more<ph name="END_LINK_LEARN_MORE"></a></ph> + <message name="IDS_OS_SETTINGS_LANGUAGES_LANGUAGES_PREFERENCE_DESCRIPTION" desc="Description for section that lets users choose the language for apps and websites. The web content languages will be served in the order of the list."> + Apps and websites that support multiple languages will use the first supported language from this list. Language preferences sync to Chrome browser. <ph name="BEGIN_LINK_LEARN_MORE"><a target="_blank" href="$1"></ph>Learn more<ph name="END_LINK_LEARN_MORE"></a></ph> </message> - <message name="IDS_OS_SETTINGS_LANGUAGES_TRANSLATE_TARGET_LABEL" translateable="false" desc="The label of the toggle that enables the prompt to translate a page to users."> - The language is used when translating pages + <message name="IDS_OS_SETTINGS_LANGUAGES_TRANSLATE_TARGET_LABEL" desc="The label of the toggle that enables the prompt to translate a page to users."> + Language used when translating pages </message> - <message name="IDS_OS_SETTINGS_LANGUAGES_OFFER_TRANSLATION_LABEL" translateable="false" desc="The label of the toggle that enables the prompt to translate a page to users."> + <message name="IDS_OS_SETTINGS_LANGUAGES_OFFER_TRANSLATION_LABEL" desc="Title for setting that allows the system to offer to translate websites that appear in other languages."> Translation suggestion </message> - <message name="IDS_OS_SETTINGS_LANGUAGES_OFFER_TRANSLATION_SUBLABEL" translateable="false" desc="The sublabel of the toggle that enables the prompt to translate a page to users."> - Allow the system to offer to translate web pages when it detects languages you don't read + <message name="IDS_OS_SETTINGS_LANGUAGES_OFFER_TRANSLATION_SUBLABEL" desc="Description for setting that allows the system to offer to translate websites that appear in other languages."> + Offer to translate websites in other languages </message> <message name="IDS_OS_SETTINGS_LANGUAGES_INPUT_METHOD_LIST_TITLE" translateable="false" desc="Title for the current input method in the header for the collapsible list of enabled input methods (keyboard layouts and input method editors)."> Input methods
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_CHANGE_DEVICE_LANGUAGE_BUTTON_DESCRIPTION.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_CHANGE_DEVICE_LANGUAGE_BUTTON_DESCRIPTION.png.sha1 new file mode 100644 index 0000000..053d45f --- /dev/null +++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_CHANGE_DEVICE_LANGUAGE_BUTTON_DESCRIPTION.png.sha1
@@ -0,0 +1 @@ +d76b317a83c1ffbcc7d96e6340c8d49ab5d375b0 \ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_CHANGE_DEVICE_LANGUAGE_BUTTON_LABEL.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_CHANGE_DEVICE_LANGUAGE_BUTTON_LABEL.png.sha1 new file mode 100644 index 0000000..053d45f --- /dev/null +++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_CHANGE_DEVICE_LANGUAGE_BUTTON_LABEL.png.sha1
@@ -0,0 +1 @@ +d76b317a83c1ffbcc7d96e6340c8d49ab5d375b0 \ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_CHANGE_DEVICE_LANGUAGE_CONFIRM_BUTTON_LABEL.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_CHANGE_DEVICE_LANGUAGE_CONFIRM_BUTTON_LABEL.png.sha1 new file mode 100644 index 0000000..6786bb65 --- /dev/null +++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_CHANGE_DEVICE_LANGUAGE_CONFIRM_BUTTON_LABEL.png.sha1
@@ -0,0 +1 @@ +a0403e40d1da513cf4ed448b1efaa29857eb18cb \ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_CHANGE_DEVICE_LANGUAGE_DIALOG_DESCRIPTION.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_CHANGE_DEVICE_LANGUAGE_DIALOG_DESCRIPTION.png.sha1 new file mode 100644 index 0000000..6786bb65 --- /dev/null +++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_CHANGE_DEVICE_LANGUAGE_DIALOG_DESCRIPTION.png.sha1
@@ -0,0 +1 @@ +a0403e40d1da513cf4ed448b1efaa29857eb18cb \ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_CHANGE_DEVICE_LANGUAGE_DIALOG_TITLE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_CHANGE_DEVICE_LANGUAGE_DIALOG_TITLE.png.sha1 new file mode 100644 index 0000000..6786bb65 --- /dev/null +++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_CHANGE_DEVICE_LANGUAGE_DIALOG_TITLE.png.sha1
@@ -0,0 +1 @@ +a0403e40d1da513cf4ed448b1efaa29857eb18cb \ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_DEVICE_LANGUAGE_TITLE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_DEVICE_LANGUAGE_TITLE.png.sha1 new file mode 100644 index 0000000..053d45f --- /dev/null +++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_DEVICE_LANGUAGE_TITLE.png.sha1
@@ -0,0 +1 @@ +d76b317a83c1ffbcc7d96e6340c8d49ab5d375b0 \ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_LANGUAGES_PREFERENCE_DESCRIPTION.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_LANGUAGES_PREFERENCE_DESCRIPTION.png.sha1 new file mode 100644 index 0000000..09614ff --- /dev/null +++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_LANGUAGES_PREFERENCE_DESCRIPTION.png.sha1
@@ -0,0 +1 @@ +1b2aec8cf7557affd2d82a3357d2dbde92311c1d \ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_LANGUAGES_PREFERENCE_TITLE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_LANGUAGES_PREFERENCE_TITLE.png.sha1 new file mode 100644 index 0000000..053d45f --- /dev/null +++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_LANGUAGES_PREFERENCE_TITLE.png.sha1
@@ -0,0 +1 @@ +d76b317a83c1ffbcc7d96e6340c8d49ab5d375b0 \ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_OFFER_TRANSLATION_LABEL.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_OFFER_TRANSLATION_LABEL.png.sha1 new file mode 100644 index 0000000..053d45f --- /dev/null +++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_OFFER_TRANSLATION_LABEL.png.sha1
@@ -0,0 +1 @@ +d76b317a83c1ffbcc7d96e6340c8d49ab5d375b0 \ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_OFFER_TRANSLATION_SUBLABEL.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_OFFER_TRANSLATION_SUBLABEL.png.sha1 new file mode 100644 index 0000000..053d45f --- /dev/null +++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_OFFER_TRANSLATION_SUBLABEL.png.sha1
@@ -0,0 +1 @@ +d76b317a83c1ffbcc7d96e6340c8d49ab5d375b0 \ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_TRANSLATE_TARGET_LABEL.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_TRANSLATE_TARGET_LABEL.png.sha1 new file mode 100644 index 0000000..053d45f --- /dev/null +++ b/chrome/app/os_settings_strings_grdp/IDS_OS_SETTINGS_LANGUAGES_TRANSLATE_TARGET_LABEL.png.sha1
@@ -0,0 +1 @@ +d76b317a83c1ffbcc7d96e6340c8d49ab5d375b0 \ No newline at end of file
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 6a2fd90..05c7bb86 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -3465,15 +3465,6 @@ kOsCrOS, SINGLE_VALUE_TYPE( ::switches::kEnableExperimentalAccessibilityChromeVoxAnnotations)}, - {"disable-experimental-accessibility-chromevox-language-switching", - flag_descriptions:: - kExperimentalAccessibilityChromeVoxLanguageSwitchingName, - flag_descriptions:: - kExperimentalAccessibilityChromeVoxLanguageSwitchingDescription, - kOsCrOS, - SINGLE_VALUE_TYPE( - ::switches:: - kDisableExperimentalAccessibilityChromeVoxLanguageSwitching)}, {"enable-experimental-accessibility-cursor-colors", flag_descriptions::kExperimentalAccessibilityCursorColorsName, flag_descriptions::kExperimentalAccessibilityCursorColorsDescription, @@ -3482,13 +3473,6 @@ flag_descriptions::kKernelnextVMsName, flag_descriptions::kKernelnextVMsDescription, kOsCrOS, FEATURE_VALUE_TYPE(features::kKernelnextVMs)}, - {"disable-experimental-accessibility-chromevox-search-menus", - flag_descriptions::kExperimentalAccessibilityChromeVoxSearchMenusName, - flag_descriptions:: - kExperimentalAccessibilityChromeVoxSearchMenusDescription, - kOsCrOS, - SINGLE_VALUE_TYPE( - ::switches::kDisableExperimentalAccessibilityChromeVoxSearchMenus)}, {"enable-experimental-accessibility-chromevox-tutorial", flag_descriptions::kExperimentalAccessibilityChromeVoxTutorialName, flag_descriptions::kExperimentalAccessibilityChromeVoxTutorialDescription,
diff --git a/chrome/browser/android/vr/arcore_device/arcore_gl.cc b/chrome/browser/android/vr/arcore_device/arcore_gl.cc index 8284fc8..2c1a5c8 100644 --- a/chrome/browser/android/vr/arcore_device/arcore_gl.cc +++ b/chrome/browser/android/vr/arcore_device/arcore_gl.cc
@@ -607,7 +607,7 @@ std::unique_ptr<gl::GLFence> gl_fence = gl::GLFence::CreateForGpuFence(); std::unique_ptr<gfx::GpuFence> gpu_fence2 = gl_fence->GetGpuFence(); submit_client_->OnSubmitFrameGpuFence( - gfx::CloneHandleForIPC(gpu_fence2->GetGpuFenceHandle())); + gpu_fence2->GetGpuFenceHandle().Clone()); } // We finished processing a frame, unblock a potentially waiting next frame. webxr_->TryDeferredProcessing(); @@ -674,7 +674,7 @@ std::unique_ptr<gl::GLFence> gl_fence = gl::GLFence::CreateForGpuFence(); std::unique_ptr<gfx::GpuFence> gpu_fence2 = gl_fence->GetGpuFence(); submit_client_->OnSubmitFrameGpuFence( - gfx::CloneHandleForIPC(gpu_fence2->GetGpuFenceHandle())); + gpu_fence2->GetGpuFenceHandle().Clone()); } // We finished processing a frame, unblock a potentially waiting next frame. webxr_->TryDeferredProcessing();
diff --git a/chrome/browser/android/vr/gvr_scheduler_delegate.cc b/chrome/browser/android/vr/gvr_scheduler_delegate.cc index 9422e005..f31df1c 100644 --- a/chrome/browser/android/vr/gvr_scheduler_delegate.cc +++ b/chrome/browser/android/vr/gvr_scheduler_delegate.cc
@@ -811,7 +811,7 @@ std::unique_ptr<gl::GLFence> gl_fence = gl::GLFence::CreateForGpuFence(); std::unique_ptr<gfx::GpuFence> gpu_fence = gl_fence->GetGpuFence(); submit_client_->OnSubmitFrameGpuFence( - gfx::CloneHandleForIPC(gpu_fence->GetGpuFenceHandle())); + gpu_fence->GetGpuFenceHandle().Clone()); } else { // Renderer is waiting for the previous frame to render, unblock it now. submit_client_->OnSubmitFrameRendered();
diff --git a/chrome/browser/bluetooth/bluetooth_chooser_context.cc b/chrome/browser/bluetooth/bluetooth_chooser_context.cc index cbf16490..7314b392 100644 --- a/chrome/browser/bluetooth/bluetooth_chooser_context.cc +++ b/chrome/browser/bluetooth/bluetooth_chooser_context.cc
@@ -43,6 +43,7 @@ // } constexpr char kDeviceAddressKey[] = "device-address"; constexpr char kDeviceNameKey[] = "name"; +constexpr char kManufacturerDataKey[] = "manufacturer-data"; constexpr char kServicesKey[] = "services"; constexpr char kWebBluetoothDeviceIdKey[] = "web-bluetooth-device-id"; @@ -73,6 +74,34 @@ services_dict.SetBoolKey(uuid.canonical_value(), /*val=*/true); } +void AddManufacturerDataTo( + const blink::mojom::WebBluetoothRequestDeviceOptions* options, + base::Value* permission_object) { + if (!options || options->optional_manufacturer_data.empty()) + return; + + base::flat_set<uint16_t> manufacturer_data_set( + options->optional_manufacturer_data); + + if (!permission_object->FindListKey(kManufacturerDataKey)) { + base::Value manufacturer_data_value(base::Value::Type::LIST); + permission_object->SetKey(kManufacturerDataKey, + std::move(manufacturer_data_value)); + } + + auto& manufacturer_data_list = + *permission_object->FindListKey(kManufacturerDataKey); + for (const auto& manufacturer_data_permission : + manufacturer_data_list.GetList()) { + manufacturer_data_set.insert( + static_cast<uint16_t>(manufacturer_data_permission.GetInt())); + } + + manufacturer_data_list.ClearList(); + for (const uint16_t manufacturer_code : manufacturer_data_set) + manufacturer_data_list.Append(manufacturer_code); +} + base::Value DeviceInfoToDeviceObject( const device::BluetoothDevice* device, const blink::mojom::WebBluetoothRequestDeviceOptions* options, @@ -86,6 +115,10 @@ device_value.SetKey(kServicesKey, std::move(services_value)); AddUnionOfServicesTo(options, &device_value); + base::Value manufacturer_data_value(base::Value::Type::LIST); + device_value.SetKey(kManufacturerDataKey, std::move(manufacturer_data_value)); + AddManufacturerDataTo(options, &device_value); + return device_value; } @@ -131,17 +164,10 @@ const url::Origin& requesting_origin, const url::Origin& embedding_origin, const WebBluetoothDeviceId& device_id) { - const std::vector<std::unique_ptr<permissions::ChooserContextBase::Object>> - object_list = GetGrantedObjects(requesting_origin, embedding_origin); - for (const auto& object : object_list) { - const base::Value& device = object->value; - DCHECK(IsValidObject(device)); - - const WebBluetoothDeviceId web_bluetooth_device_id( - *device.FindStringKey(kWebBluetoothDeviceIdKey)); - if (device_id == web_bluetooth_device_id) - return *device.FindStringKey(kDeviceAddressKey); - } + base::Value device = + FindDeviceObject(requesting_origin, embedding_origin, device_id); + if (!device.is_none()) + return *device.FindStringKey(kDeviceAddressKey); // Check if the device ID corresponds to a device detected via an LE scan. auto scanned_devices_it = @@ -194,6 +220,7 @@ *new_device_object.FindStringKey(kWebBluetoothDeviceIdKey)); AddUnionOfServicesTo(options, &new_device_object); + AddManufacturerDataTo(options, &new_device_object); UpdateObjectPermission(requesting_origin, embedding_origin, device_object, std::move(new_device_object)); return device_id; @@ -234,55 +261,53 @@ const url::Origin& requesting_origin, const url::Origin& embedding_origin, const WebBluetoothDeviceId& device_id) { - const std::vector<std::unique_ptr<permissions::ChooserContextBase::Object>> - object_list = GetGrantedObjects(requesting_origin, embedding_origin); - for (const auto& object : object_list) { - const base::Value& device = object->value; - DCHECK(IsValidObject(device)); - - const WebBluetoothDeviceId web_bluetooth_device_id( - *device.FindStringKey(kWebBluetoothDeviceIdKey)); - if (device_id == web_bluetooth_device_id) - return true; - } - return false; + base::Value device = + FindDeviceObject(requesting_origin, embedding_origin, device_id); + return !device.is_none(); } bool BluetoothChooserContext::IsAllowedToAccessAtLeastOneService( const url::Origin& requesting_origin, const url::Origin& embedding_origin, const WebBluetoothDeviceId& device_id) { - const std::vector<std::unique_ptr<permissions::ChooserContextBase::Object>> - object_list = GetGrantedObjects(requesting_origin, embedding_origin); - for (const auto& object : object_list) { - const base::Value& device = object->value; - DCHECK(IsValidObject(device)); - - const WebBluetoothDeviceId web_bluetooth_device_id( - *device.FindStringKey(kWebBluetoothDeviceIdKey)); - if (device_id == web_bluetooth_device_id) - return !device.FindDictKey(kServicesKey)->DictEmpty(); - } - return false; + base::Value device = + FindDeviceObject(requesting_origin, embedding_origin, device_id); + if (device.is_none()) + return false; + return !device.FindDictKey(kServicesKey)->DictEmpty(); } bool BluetoothChooserContext::IsAllowedToAccessService( const url::Origin& requesting_origin, const url::Origin& embedding_origin, const WebBluetoothDeviceId& device_id, - BluetoothUUID service) { - const std::vector<std::unique_ptr<permissions::ChooserContextBase::Object>> - object_list = GetGrantedObjects(requesting_origin, embedding_origin); - for (const auto& object : object_list) { - const base::Value& device = object->value; - DCHECK(IsValidObject(device)); + const BluetoothUUID& service) { + base::Value device = + FindDeviceObject(requesting_origin, embedding_origin, device_id); + if (device.is_none()) + return false; - const WebBluetoothDeviceId web_bluetooth_device_id( - *device.FindStringKey(kWebBluetoothDeviceIdKey)); - if (device_id == web_bluetooth_device_id) { - const auto& services_dict = *device.FindDictKey(kServicesKey); - return !!services_dict.FindKey(service.canonical_value()); - } + const auto& services_dict = *device.FindDictKey(kServicesKey); + return !!services_dict.FindKey(service.canonical_value()); +} + +bool BluetoothChooserContext::IsAllowedToAccessManufacturerData( + const url::Origin& requesting_origin, + const url::Origin& embedding_origin, + const WebBluetoothDeviceId& device_id, + uint16_t manufacturer_code) { + base::Value device = + FindDeviceObject(requesting_origin, embedding_origin, device_id); + if (device.is_none()) + return false; + + const auto* manufacturer_data_list = device.FindListKey(kManufacturerDataKey); + if (!manufacturer_data_list) + return false; + + for (const auto& manufacturer_data : manufacturer_data_list->GetList()) { + if (manufacturer_code == manufacturer_data.GetInt()) + return true; } return false; } @@ -307,3 +332,21 @@ const base::Value& object) { return base::UTF8ToUTF16(*object.FindStringKey(kDeviceNameKey)); } + +base::Value BluetoothChooserContext::FindDeviceObject( + const url::Origin& requesting_origin, + const url::Origin& embedding_origin, + const blink::WebBluetoothDeviceId& device_id) { + const std::vector<std::unique_ptr<permissions::ChooserContextBase::Object>> + object_list = GetGrantedObjects(requesting_origin, embedding_origin); + for (const auto& object : object_list) { + base::Value device = std::move(object->value); + DCHECK(IsValidObject(device)); + + const WebBluetoothDeviceId web_bluetooth_device_id( + *device.FindStringKey(kWebBluetoothDeviceIdKey)); + if (device_id == web_bluetooth_device_id) + return device; + } + return base::Value(base::Value::Type::NONE); +}
diff --git a/chrome/browser/bluetooth/bluetooth_chooser_context.h b/chrome/browser/bluetooth/bluetooth_chooser_context.h index 5194f87..1489610 100644 --- a/chrome/browser/bluetooth/bluetooth_chooser_context.h +++ b/chrome/browser/bluetooth/bluetooth_chooser_context.h
@@ -75,7 +75,12 @@ bool IsAllowedToAccessService(const url::Origin& requesting_origin, const url::Origin& embedding_origin, const blink::WebBluetoothDeviceId& device_id, - device::BluetoothUUID service); + const device::BluetoothUUID& service); + bool IsAllowedToAccessManufacturerData( + const url::Origin& requesting_origin, + const url::Origin& embedding_origin, + const blink::WebBluetoothDeviceId& device_id, + uint16_t manufacturer_code); static blink::WebBluetoothDeviceId GetObjectDeviceId( const base::Value& object); @@ -85,6 +90,10 @@ base::string16 GetObjectDisplayName(const base::Value& object) override; private: + base::Value FindDeviceObject(const url::Origin& requesting_origin, + const url::Origin& embedding_origin, + const blink::WebBluetoothDeviceId& device_id); + // This map records the generated Web Bluetooth IDs for devices discovered via // the Scanning API. Each requesting/embedding origin pair has its own version // of this map so that IDs cannot be correlated between cross-origin sites.
diff --git a/chrome/browser/bluetooth/bluetooth_chooser_context_unittest.cc b/chrome/browser/bluetooth/bluetooth_chooser_context_unittest.cc index b1fa055..a1ca510 100644 --- a/chrome/browser/bluetooth/bluetooth_chooser_context_unittest.cc +++ b/chrome/browser/bluetooth/bluetooth_chooser_context_unittest.cc
@@ -32,6 +32,7 @@ constexpr char kDeviceAddressKey[] = "device-address"; constexpr char kDeviceNameKey[] = "name"; constexpr char kServicesKey[] = "services"; +constexpr char kManufacturerDataKey[] = "manufacturer-data"; constexpr char kWebBluetoothDeviceIdKey[] = "web-bluetooth-device-id"; const uint32_t kGamepadBluetoothClass = 0x0508; @@ -53,9 +54,10 @@ const BluetoothUUID kBloodPressureUUID(kBloodPressureUUIDString); const BluetoothUUID kCyclingPowerUUID(kCyclingPowerUUIDString); -WebBluetoothRequestDeviceOptionsPtr CreateOptionsForServices( +WebBluetoothRequestDeviceOptionsPtr CreateOptionsForServicesAndManufacturerData( const std::vector<BluetoothUUID>& filter_services, - const std::vector<BluetoothUUID>& optional_services) { + const std::vector<BluetoothUUID>& optional_services, + const std::vector<uint16_t>& optional_manufacturer_data) { auto filter = blink::mojom::WebBluetoothLeScanFilter::New(); filter->services = filter_services; @@ -65,12 +67,26 @@ auto options = blink::mojom::WebBluetoothRequestDeviceOptions::New(); options->filters = std::move(scan_filters); options->optional_services = optional_services; + options->optional_manufacturer_data = optional_manufacturer_data; return options; } WebBluetoothRequestDeviceOptionsPtr CreateOptionsForServices( + const std::vector<BluetoothUUID>& filter_services, + const std::vector<BluetoothUUID>& optional_services) { + return CreateOptionsForServicesAndManufacturerData( + filter_services, optional_services, /*optional_manufacturer_data=*/{}); +} + +WebBluetoothRequestDeviceOptionsPtr CreateOptionsForServices( const std::vector<BluetoothUUID>& filter_services) { - return CreateOptionsForServices(filter_services, {}); + return CreateOptionsForServices(filter_services, /*optional_services=*/{}); +} + +WebBluetoothRequestDeviceOptionsPtr CreateOptionsForManufacturerData( + const std::vector<uint16_t>& optional_manufacturer_data) { + return CreateOptionsForServicesAndManufacturerData( + /*services=*/{}, /*optional_services=*/{}, optional_manufacturer_data); } } // namespace @@ -163,8 +179,10 @@ TEST_F(BluetoothChooserContextTest, CheckGrantAndRevokePermission) { const std::vector<BluetoothUUID> services = {kGlucoseUUID, kBloodPressureUUID}; + const std::vector<uint16_t> manufacturer_codes = {0x0001, 0x0002}; WebBluetoothRequestDeviceOptionsPtr options = - CreateOptionsForServices(services); + CreateOptionsForServicesAndManufacturerData( + services, /*optional_services=*/{}, manufacturer_codes); BluetoothChooserContext* context = GetChooserContext(profile()); @@ -203,6 +221,11 @@ expected_services.SetBoolKey(kGlucoseUUIDString, /*val=*/true); expected_services.SetBoolKey(kBloodPressureUUIDString, /*val=*/true); expected_object.SetKey(kServicesKey, std::move(expected_services)); + base::Value expected_manufacturer_data(base::Value::Type::LIST); + expected_manufacturer_data.Append(0x01); + expected_manufacturer_data.Append(0x02); + expected_object.SetKey(kManufacturerDataKey, + std::move(expected_manufacturer_data)); std::vector<std::unique_ptr<permissions::ChooserContextBase::Object>> origin_objects = context->GetGrantedObjects(foo_origin_, foo_origin_); @@ -411,6 +434,55 @@ } } +// Check that permissions for manufacturer data are granted and updated +// properly. +TEST_F(BluetoothChooserContextTest, CheckGrantWithOptionalManufacturerData) { + BluetoothChooserContext* context = GetChooserContext(profile()); + + // Grant permission with only manufacturer data. + { + const std::vector<uint16_t> optional_manufacturer_data({0x0001, 0x0002}); + WebBluetoothRequestDeviceOptionsPtr options = + CreateOptionsForManufacturerData(optional_manufacturer_data); + EXPECT_CALL(mock_permission_observer_, + OnChooserObjectPermissionChanged( + ContentSettingsType::BLUETOOTH_GUARD, + ContentSettingsType::BLUETOOTH_CHOOSER_DATA)); + blink::WebBluetoothDeviceId device_id = + context->GrantServiceAccessPermission( + foo_origin_, foo_origin_, fake_device1_.get(), options.get()); + + for (const uint16_t manufacturer_code : optional_manufacturer_data) { + EXPECT_TRUE(context->IsAllowedToAccessManufacturerData( + foo_origin_, foo_origin_, device_id, manufacturer_code)); + } + EXPECT_FALSE(context->IsAllowedToAccessManufacturerData( + foo_origin_, foo_origin_, device_id, 0x0003)); + } + + // Grant permission again with different manufacturer data. + { + const std::vector<uint16_t> optional_manufacturer_data({0x0002, 0x0003}); + WebBluetoothRequestDeviceOptionsPtr options = + CreateOptionsForManufacturerData(optional_manufacturer_data); + EXPECT_CALL(mock_permission_observer_, + OnChooserObjectPermissionChanged( + ContentSettingsType::BLUETOOTH_GUARD, + ContentSettingsType::BLUETOOTH_CHOOSER_DATA)); + blink::WebBluetoothDeviceId device_id = + context->GrantServiceAccessPermission( + foo_origin_, foo_origin_, fake_device1_.get(), options.get()); + + for (const uint16_t manufacturer_code : optional_manufacturer_data) { + EXPECT_TRUE(context->IsAllowedToAccessManufacturerData( + foo_origin_, foo_origin_, device_id, manufacturer_code)); + } + // Access permission for manufacturer code 0x0001 should still be available. + EXPECT_TRUE(context->IsAllowedToAccessManufacturerData( + foo_origin_, foo_origin_, device_id, 0x0001)); + } +} + // Check that the Bluetooth guard permission prevents Web Bluetooth from being // used even if permissions exist for a pair of origins. TEST_F(BluetoothChooserContextTest, BluetoothGuardPermission) {
diff --git a/chrome/browser/bluetooth/chrome_bluetooth_delegate.cc b/chrome/browser/bluetooth/chrome_bluetooth_delegate.cc index a081e7ca..37400db5 100644 --- a/chrome/browser/bluetooth/chrome_bluetooth_delegate.cc +++ b/chrome/browser/bluetooth/chrome_bluetooth_delegate.cc
@@ -93,6 +93,16 @@ frame->GetMainFrame()->GetLastCommittedOrigin(), device_id); } +bool ChromeBluetoothDelegate::IsAllowedToAccessManufacturerData( + RenderFrameHost* frame, + const WebBluetoothDeviceId& device_id, + uint16_t manufacturer_code) { + return GetBluetoothChooserContext(frame)->IsAllowedToAccessManufacturerData( + frame->GetLastCommittedOrigin(), + frame->GetMainFrame()->GetLastCommittedOrigin(), device_id, + manufacturer_code); +} + std::vector<blink::mojom::WebBluetoothDevicePtr> ChromeBluetoothDelegate::GetPermittedDevices(content::RenderFrameHost* frame) { auto* context = GetBluetoothChooserContext(frame);
diff --git a/chrome/browser/bluetooth/chrome_bluetooth_delegate.h b/chrome/browser/bluetooth/chrome_bluetooth_delegate.h index cee6f5d..4f16c0b8 100644 --- a/chrome/browser/bluetooth/chrome_bluetooth_delegate.h +++ b/chrome/browser/bluetooth/chrome_bluetooth_delegate.h
@@ -59,6 +59,10 @@ bool IsAllowedToAccessAtLeastOneService( content::RenderFrameHost* frame, const blink::WebBluetoothDeviceId& device_id) override; + bool IsAllowedToAccessManufacturerData( + content::RenderFrameHost* frame, + const blink::WebBluetoothDeviceId& device_id, + uint16_t manufacturer_code) override; std::vector<blink::mojom::WebBluetoothDevicePtr> GetPermittedDevices( content::RenderFrameHost* frame) override; };
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc index fdf6ee9..cebab08 100644 --- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc +++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.cc
@@ -814,9 +814,11 @@ BrowserContext::GetDefaultStoragePartition(profile_) ->GetNetworkContext() - ->ClearHttpAuthCache(delete_begin_, - CreateTaskCompletionClosureForMojo( - TracingDataType::kHttpAuthCache)); + ->ClearHttpAuthCache( + delete_begin_.is_null() ? base::Time::Min() : delete_begin_, + delete_end_.is_null() ? base::Time::Max() : delete_end_, + CreateTaskCompletionClosureForMojo( + TracingDataType::kHttpAuthCache)); #if defined(OS_CHROMEOS) policy::SystemProxyManager* system_proxy_manager = g_browser_process->platform_part()
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn index 6523e0730..fc33ba60 100644 --- a/chrome/browser/chromeos/BUILD.gn +++ b/chrome/browser/chromeos/BUILD.gn
@@ -3390,6 +3390,7 @@ "night_light/night_light_client_unittest.cc", "note_taking_helper_unittest.cc", "ownership/owner_settings_service_chromeos_unittest.cc", + "platform_keys/key_permissions/key_permissions_manager_impl_unittest.cc", "plugin_vm/mock_plugin_vm_manager.cc", "plugin_vm/mock_plugin_vm_manager.h", "plugin_vm/plugin_vm_features_unittest.cc",
diff --git a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker.cc b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker.cc index ab48b2f..29faaba8 100644 --- a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker.cc +++ b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker.cc
@@ -16,6 +16,8 @@ #include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_invalidator.h" #include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_metrics.h" #include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_serializer.h" +#include "chrome/browser/chromeos/platform_keys/key_permissions/key_permissions_manager.h" +#include "chrome/browser/chromeos/platform_keys/key_permissions/key_permissions_manager_user_service.h" #include "chrome/browser/chromeos/platform_keys/platform_keys.h" #include "chrome/browser/chromeos/platform_keys/platform_keys_service.h" #include "chrome/browser/chromeos/platform_keys/platform_keys_service_factory.h" @@ -112,6 +114,26 @@ return res; } +// Marks the key |public_key_spki_der| as corporate. |profile| can be nullptr if +// |scope| is CertScope::kDevice. +void MarkKeyAsCorporate(CertScope scope, + Profile* profile, + const std::string& public_key_spki_der) { + // Device-wide keys are implicitly corporate, and there is currently no + // device-wide KeyPermissionsManager. + // TODO(https://crbug.com/1127284): Use the device-wide KeyPermissionsManager + // when it exists. + if (scope == CertScope::kDevice) { + return; + } + DCHECK(profile); + + platform_keys::KeyPermissionsManagerUserServiceFactory::GetForBrowserContext( + profile) + ->key_permissions_manager() + ->SetCorporateKey(public_key_spki_der, GetPlatformKeysTokenId(scope)); +} + } // namespace // ============= CertProvisioningWorkerFactory ================================= @@ -513,6 +535,8 @@ void CertProvisioningWorkerImpl::MarkKey() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + MarkKeyAsCorporate(cert_scope_, profile_, public_key_); + platform_keys_service_->SetAttributeForKey( GetPlatformKeysTokenId(cert_scope_), public_key_, platform_keys::KeyAttributeType::kCertificateProvisioningId,
diff --git a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker_unittest.cc b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker_unittest.cc index 73c7817f..f53004e 100644 --- a/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker_unittest.cc +++ b/chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker_unittest.cc
@@ -4,6 +4,9 @@ #include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_worker.h" +#include <memory> +#include <string> + #include "base/base64.h" #include "base/callback.h" #include "base/json/json_string_value_serializer.h" @@ -19,6 +22,9 @@ #include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_metrics.h" #include "chrome/browser/chromeos/cert_provisioning/cert_provisioning_test_helpers.h" #include "chrome/browser/chromeos/cert_provisioning/mock_cert_provisioning_invalidator.h" +#include "chrome/browser/chromeos/platform_keys/key_permissions/key_permissions_manager.h" +#include "chrome/browser/chromeos/platform_keys/key_permissions/key_permissions_manager_user_service.h" +#include "chrome/browser/chromeos/platform_keys/key_permissions/mock_key_permissions_manager.h" #include "chrome/browser/chromeos/platform_keys/mock_platform_keys_service.h" #include "chrome/browser/chromeos/platform_keys/platform_keys.h" #include "chrome/browser/chromeos/platform_keys/platform_keys_service.h" @@ -308,6 +314,31 @@ .WillOnce(RunOnceCallback<2>(platform_keys::Status::kSuccess)); \ } +// A fake KeyPermissionsManagerUserService which returns a KeyPermissionsManager +// pointer passed to its constructor. +class FakeKeyPermissionsManagerUserService + : public platform_keys::KeyPermissionsManagerUserService { + public: + FakeKeyPermissionsManagerUserService( + platform_keys::KeyPermissionsManager* key_permissions_manager) + : key_permissions_manager_(key_permissions_manager) {} + ~FakeKeyPermissionsManagerUserService() override = default; + + platform_keys::KeyPermissionsManager* key_permissions_manager() override { + return key_permissions_manager_; + } + + static std::unique_ptr<KeyedService> Build( + platform_keys::KeyPermissionsManager* key_permissions_manager, + content::BrowserContext* browser_context) { + return std::make_unique<FakeKeyPermissionsManagerUserService>( + key_permissions_manager); + } + + private: + platform_keys::KeyPermissionsManager* key_permissions_manager_; +}; + // A mock for observing the result callback of the worker. class CallbackObserver { public: @@ -357,6 +388,12 @@ platform_keys::PlatformKeysServiceFactory::GetInstance() ->SetDeviceWideServiceForTesting(platform_keys_service_); + platform_keys::KeyPermissionsManagerUserServiceFactory::GetInstance() + ->SetTestingFactory( + GetProfile(), + base::BindRepeating(&FakeKeyPermissionsManagerUserService::Build, + &key_permissions_manager_)); + // Only explicitly expected removals are allowed. EXPECT_CALL(*platform_keys_service_, RemoveCertificate).Times(0); EXPECT_CALL(*platform_keys_service_, RemoveKey).Times(0); @@ -419,6 +456,7 @@ policy::MockCloudPolicyClient cloud_policy_client_; platform_keys::MockPlatformKeysService* platform_keys_service_ = nullptr; + StrictMock<platform_keys::MockKeyPermissionsManager> key_permissions_manager_; }; // Checks that the worker makes all necessary requests to other modules during @@ -463,6 +501,9 @@ EXPECT_REGISTER_KEY_OK(*mock_tpm_challenge_key, StartRegisterKeyStep); EXPECT_CALL(state_change_callback_observer_, StateChangeCallback()); + EXPECT_CALL(key_permissions_manager_, + SetCorporateKey(GetPublicKey(), platform_keys::TokenId::kUser)); + EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(SetAttributeForKey( platform_keys::TokenId::kUser, GetPublicKey(), platform_keys::KeyAttributeType::kCertificateProvisioningId, @@ -536,6 +577,9 @@ kCertScopeStrUser, kCertProfileId, kCertProfileVersion, GetPublicKey(), /*callback=*/_)); + EXPECT_CALL(key_permissions_manager_, + SetCorporateKey(GetPublicKey(), platform_keys::TokenId::kUser)); + EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(SetAttributeForKey( platform_keys::TokenId::kUser, GetPublicKey(), platform_keys::KeyAttributeType::kCertificateProvisioningId, @@ -615,6 +659,10 @@ EXPECT_REGISTER_KEY_OK(*mock_tpm_challenge_key, StartRegisterKeyStep); + // Note: No call to KeyPermissionsManager::SetCorporateKey, because it is + // only performed for platform_keys::TokenId::kUser, but this test works + // with the system token. + EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(SetAttributeForKey( platform_keys::TokenId::kSystem, GetPublicKey(), platform_keys::KeyAttributeType::kCertificateProvisioningId, @@ -725,6 +773,9 @@ EXPECT_REGISTER_KEY_OK(*mock_tpm_challenge_key, StartRegisterKeyStep); + EXPECT_CALL(key_permissions_manager_, + SetCorporateKey(GetPublicKey(), platform_keys::TokenId::kUser)); + EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(SetAttributeForKey( platform_keys::TokenId::kUser, GetPublicKey(), platform_keys::KeyAttributeType::kCertificateProvisioningId, @@ -1024,6 +1075,9 @@ EXPECT_REGISTER_KEY_OK(*mock_tpm_challenge_key, StartRegisterKeyStep); + EXPECT_CALL(key_permissions_manager_, + SetCorporateKey(GetPublicKey(), platform_keys::TokenId::kUser)); + EXPECT_SET_ATTRIBUTE_FOR_KEY_FAIL(SetAttributeForKey( platform_keys::TokenId::kUser, GetPublicKey(), platform_keys::KeyAttributeType::kCertificateProvisioningId, @@ -1179,6 +1233,9 @@ EXPECT_REGISTER_KEY_OK(*mock_tpm_challenge_key, StartRegisterKeyStep); + EXPECT_CALL(key_permissions_manager_, + SetCorporateKey(GetPublicKey(), platform_keys::TokenId::kUser)); + EXPECT_SET_ATTRIBUTE_FOR_KEY_OK(SetAttributeForKey( platform_keys::TokenId::kUser, GetPublicKey(), platform_keys::KeyAttributeType::kCertificateProvisioningId,
diff --git a/chrome/browser/chromeos/extensions/document_scan/document_scan_api.cc b/chrome/browser/chromeos/extensions/document_scan/document_scan_api.cc index 3a21eee8..9d5740b 100644 --- a/chrome/browser/chromeos/extensions/document_scan/document_scan_api.cc +++ b/chrome/browser/chromeos/extensions/document_scan/document_scan_api.cc
@@ -80,22 +80,29 @@ browser_context()) ->Scan( scanner_name, properties, - base::BindOnce(&DocumentScanScanFunction::OnResultsReceived, this)); + base::BindRepeating(&DocumentScanScanFunction::OnPageReceived, this), + base::BindOnce(&DocumentScanScanFunction::OnScanCompleted, this)); } -void DocumentScanScanFunction::OnResultsReceived( - base::Optional<std::string> scanned_image) { +void DocumentScanScanFunction::OnPageReceived(std::string scanned_image) { + // Take only the first page of the scan. + if (!scan_data_.has_value()) { + scan_data_ = std::move(scanned_image); + } +} + +void DocumentScanScanFunction::OnScanCompleted(bool success) { // TODO(pstew): Enlist a delegate to display received scan in the UI and // confirm that this scan should be sent to the caller. If this is a // multi-page scan, provide a means for adding additional scanned images up to // the requested limit. - if (!scanned_image.has_value()) { + if (!scan_data_.has_value() || !success) { Respond(Error(kScanImageError)); return; } std::string image_base64; - base::Base64Encode(scanned_image.value(), &image_base64); + base::Base64Encode(scan_data_.value(), &image_base64); document_scan::ScanResults scan_results; scan_results.data_urls.push_back(kPngImageDataUrlPrefix + image_base64); scan_results.mime_type = kScannerImageMimeTypePng;
diff --git a/chrome/browser/chromeos/extensions/document_scan/document_scan_api.h b/chrome/browser/chromeos/extensions/document_scan/document_scan_api.h index c96778d7..f423596 100644 --- a/chrome/browser/chromeos/extensions/document_scan/document_scan_api.h +++ b/chrome/browser/chromeos/extensions/document_scan/document_scan_api.h
@@ -34,8 +34,10 @@ friend class DocumentScanScanFunctionTest; void OnNamesReceived(std::vector<std::string> scanner_names); - void OnResultsReceived(base::Optional<std::string> scanned_image); + void OnPageReceived(std::string scanned_image); + void OnScanCompleted(bool success); + base::Optional<std::string> scan_data_; std::unique_ptr<document_scan::Scan::Params> params_; };
diff --git a/chrome/browser/chromeos/platform_keys/key_permissions/key_permissions_manager.h b/chrome/browser/chromeos/platform_keys/key_permissions/key_permissions_manager.h index edb05b3d..c055039f 100644 --- a/chrome/browser/chromeos/platform_keys/key_permissions/key_permissions_manager.h +++ b/chrome/browser/chromeos/platform_keys/key_permissions/key_permissions_manager.h
@@ -118,6 +118,18 @@ virtual bool CanUserGrantPermissionFor( const std::string& public_key_spki_der, const std::vector<platform_keys::TokenId>& key_locations) const = 0; + + // Returns true if the key identified by |public_key_spki_der| that is + // located on |key_locations| is marked for corporate usage. + virtual bool IsCorporateKey( + const std::string& public_key_spki_der, + const std::vector<platform_keys::TokenId>& key_locations) const = 0; + + // Marks the key identified by |public_key_spki_der| as corporate usage. + // Accepts a single key location because this is intended for usage after key + // generation / import, when exactly one location is relevant. + virtual void SetCorporateKey(const std::string& public_key_spki_der, + platform_keys::TokenId key_location) const = 0; }; } // namespace platform_keys
diff --git a/chrome/browser/chromeos/platform_keys/key_permissions/key_permissions_manager_impl.cc b/chrome/browser/chromeos/platform_keys/key_permissions/key_permissions_manager_impl.cc index 1258457..3adcf22 100644 --- a/chrome/browser/chromeos/platform_keys/key_permissions/key_permissions_manager_impl.cc +++ b/chrome/browser/chromeos/platform_keys/key_permissions/key_permissions_manager_impl.cc
@@ -219,10 +219,8 @@ // Usage of corporate keys is solely determined by policy. The user must not // circumvent this decision. - if (key_permissions_->IsCorporateKey(public_key_spki_der_b64, - key_locations)) { + if (key_permissions_->IsCorporateKey(public_key_spki_der, key_locations)) return PolicyAllowsCorporateKeyUsage(); - } // Only permissions for keys that are not designated for corporate usage are // determined by user decisions. @@ -279,14 +277,7 @@ if (!IsKeyOnUserSlot(key_locations)) return; - DictionaryPrefUpdate update(profile_prefs_, prefs::kPlatformKeys); - - std::unique_ptr<base::DictionaryValue> new_pref_entry( - new base::DictionaryValue); - new_pref_entry->SetKey(kPrefKeyUsage, base::Value(kPrefKeyUsageCorporate)); - - update->SetWithoutPathExpansion(public_key_spki_der_b64, - std::move(new_pref_entry)); + key_permissions_->SetCorporateKey(public_key_spki_der, TokenId::kUser); } void KeyPermissionsManagerImpl::PermissionsForExtensionImpl:: @@ -438,12 +429,49 @@ if (profile_is_managed_) return false; + // If this profile is not managed but we find a corporate key, don't allow + // the user to grant permissions. + return !IsCorporateKey(public_key_spki_der, key_locations); +} + +bool KeyPermissionsManagerImpl::IsCorporateKey( + const std::string& public_key_spki_der, + const std::vector<TokenId>& key_locations) const { std::string public_key_spki_der_b64; base::Base64Encode(public_key_spki_der, &public_key_spki_der_b64); - // If this profile is not managed but we find a corporate key, don't allow - // the user to grant permissions. - return !IsCorporateKey(public_key_spki_der_b64, key_locations); + for (const auto key_location : key_locations) { + switch (key_location) { + case TokenId::kUser: + if (IsCorporateKeyForProfile(public_key_spki_der_b64, profile_prefs_)) + return true; + break; + case TokenId::kSystem: + return true; + } + } + return false; +} + +void KeyPermissionsManagerImpl::SetCorporateKey( + const std::string& public_key_spki_der, + TokenId key_location) const { + if (key_location == TokenId::kSystem) { + // Nothing to do - all system-token keys are currently implicitly corporate. + return; + } + + std::string public_key_spki_der_b64; + base::Base64Encode(public_key_spki_der, &public_key_spki_der_b64); + + DictionaryPrefUpdate update(profile_prefs_, prefs::kPlatformKeys); + + std::unique_ptr<base::DictionaryValue> new_pref_entry( + new base::DictionaryValue); + new_pref_entry->SetKey(kPrefKeyUsage, base::Value(kPrefKeyUsageCorporate)); + + update->SetWithoutPathExpansion(public_key_spki_der_b64, + std::move(new_pref_entry)); } // static @@ -486,25 +514,6 @@ return permissions; } -bool KeyPermissionsManagerImpl::IsCorporateKey( - const std::string& public_key_spki_der_b64, - const std::vector<TokenId>& key_locations) const { - for (const auto key_location : key_locations) { - switch (key_location) { - case TokenId::kUser: - LOG(ERROR) << " user"; - if (IsCorporateKeyForProfile(public_key_spki_der_b64, profile_prefs_)) - return true; - break; - case TokenId::kSystem: - return true; - default: - NOTREACHED(); - } - } - return false; -} - void KeyPermissionsManagerImpl::CreatePermissionObjectAndPassToCallback( const std::string& extension_id, const PermissionsCallback& callback,
diff --git a/chrome/browser/chromeos/platform_keys/key_permissions/key_permissions_manager_impl.h b/chrome/browser/chromeos/platform_keys/key_permissions/key_permissions_manager_impl.h index c3592a6b..4f38d1b 100644 --- a/chrome/browser/chromeos/platform_keys/key_permissions/key_permissions_manager_impl.h +++ b/chrome/browser/chromeos/platform_keys/key_permissions/key_permissions_manager_impl.h
@@ -125,7 +125,16 @@ const std::string& public_key_spki_der, const std::vector<platform_keys::TokenId>& key_locations) const override; + bool IsCorporateKey( + const std::string& public_key_spki_der, + const std::vector<platform_keys::TokenId>& key_locations) const override; + + void SetCorporateKey(const std::string& public_key_spki_der, + platform_keys::TokenId key_location) const override; + // Returns true if |public_key_spki_der_b64| is a corporate usage key. + // TOOD(http://crbug.com/1127284): Remove this and migrate callers to + // IsCorporateKey(). static bool IsCorporateKeyForProfile( const std::string& public_key_spki_der_b64, const PrefService* const profile_prefs); @@ -136,10 +145,6 @@ policy::PolicyService* const profile_policies); private: - bool IsCorporateKey( - const std::string& public_key_spki_der_b64, - const std::vector<platform_keys::TokenId>& key_locations) const; - // Creates a PermissionsForExtension object from |extension_id| and |value| // and passes the object to |callback|. void CreatePermissionObjectAndPassToCallback(
diff --git a/chrome/browser/chromeos/platform_keys/key_permissions/key_permissions_manager_impl_unittest.cc b/chrome/browser/chromeos/platform_keys/key_permissions/key_permissions_manager_impl_unittest.cc new file mode 100644 index 0000000..271e0a0 --- /dev/null +++ b/chrome/browser/chromeos/platform_keys/key_permissions/key_permissions_manager_impl_unittest.cc
@@ -0,0 +1,87 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/chromeos/platform_keys/key_permissions/key_permissions_manager_impl.h" + +#include <memory> + +#include "base/command_line.h" +#include "base/files/file_path.h" +#include "chrome/browser/extensions/test_extension_system.h" +#include "chrome/test/base/testing_profile.h" +#include "components/policy/core/common/mock_policy_service.h" +#include "components/prefs/pref_service.h" +#include "content/public/test/browser_task_environment.h" +#include "extensions/browser/state_store.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromeos { +namespace platform_keys { + +class KeyPermissionsManagerImplTest : public ::testing::Test { + public: + KeyPermissionsManagerImplTest() = default; + KeyPermissionsManagerImplTest(const KeyPermissionsManagerImplTest&) = delete; + KeyPermissionsManagerImplTest& operator=( + const KeyPermissionsManagerImplTest&) = delete; + ~KeyPermissionsManagerImplTest() override = default; + + void SetUp() override { + auto mock_policy_service = std::make_unique<policy::MockPolicyService>(); + policy_service_ = mock_policy_service.get(); + TestingProfile::Builder builder; + builder.SetPolicyService(std::move(mock_policy_service)); + profile_ = builder.Build(); + + extensions::TestExtensionSystem* extension_system = + static_cast<extensions::TestExtensionSystem*>( + extensions::ExtensionSystem::Get(profile_.get())); + extension_system->CreateExtensionService( + base::CommandLine::ForCurrentProcess(), + /*install_directory=*/base::FilePath(), + /*autoupdate_enabled=*/false); + extensions_state_store_ = extension_system->state_store(); + + key_permissions_manager_ = std::make_unique<KeyPermissionsManagerImpl>( + /*profile_is_managed=*/true, profile_->GetPrefs(), policy_service_, + extensions_state_store_); + } + + protected: + content::BrowserTaskEnvironment task_environment_; + std::unique_ptr<TestingProfile> profile_; + // Owned by |profile_|. + policy::MockPolicyService* policy_service_ = nullptr; + // Owned by |profile_|. + extensions::StateStore* extensions_state_store_ = nullptr; + + std::unique_ptr<KeyPermissionsManagerImpl> key_permissions_manager_; +}; + +TEST_F(KeyPermissionsManagerImplTest, SystemTokenKeyIsImplicitlyCorporate) { + EXPECT_TRUE(key_permissions_manager_->IsCorporateKey("some_public_key", + {TokenId::kSystem})); + EXPECT_TRUE(key_permissions_manager_->IsCorporateKey( + "some_public_key", {TokenId::kUser, TokenId::kSystem})); +} + +TEST_F(KeyPermissionsManagerImplTest, CorporateRoundTrip) { + // By default, user-token keys are not corporate. + EXPECT_FALSE(key_permissions_manager_->IsCorporateKey("some_public_key", + {TokenId::kUser})); + + key_permissions_manager_->SetCorporateKey("some_public_key", TokenId::kUser); + + EXPECT_TRUE(key_permissions_manager_->IsCorporateKey("some_public_key", + {TokenId::kUser})); + + // Check that a repeated call doesn't corrupt the stored state. + key_permissions_manager_->SetCorporateKey("some_public_key", TokenId::kUser); + + EXPECT_TRUE(key_permissions_manager_->IsCorporateKey("some_public_key", + {TokenId::kUser})); +} + +} // namespace platform_keys +} // namespace chromeos
diff --git a/chrome/browser/chromeos/scanning/fake_lorgnette_scanner_manager.cc b/chrome/browser/chromeos/scanning/fake_lorgnette_scanner_manager.cc index e36bd04..f8b0110 100644 --- a/chrome/browser/chromeos/scanning/fake_lorgnette_scanner_manager.cc +++ b/chrome/browser/chromeos/scanning/fake_lorgnette_scanner_manager.cc
@@ -33,9 +33,15 @@ void FakeLorgnetteScannerManager::Scan( const std::string& scanner_name, const LorgnetteManagerClient::ScanProperties& scan_properties, + PageCallback page_callback, ScanCallback callback) { + if (scan_data_.has_value()) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(page_callback, scan_data_.value())); + } + base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::BindOnce(std::move(callback), scan_data_)); + FROM_HERE, base::BindOnce(std::move(callback), scan_data_.has_value())); } void FakeLorgnetteScannerManager::SetGetScannerNamesResponse(
diff --git a/chrome/browser/chromeos/scanning/fake_lorgnette_scanner_manager.h b/chrome/browser/chromeos/scanning/fake_lorgnette_scanner_manager.h index 123218a..779441f9 100644 --- a/chrome/browser/chromeos/scanning/fake_lorgnette_scanner_manager.h +++ b/chrome/browser/chromeos/scanning/fake_lorgnette_scanner_manager.h
@@ -30,6 +30,7 @@ GetScannerCapabilitiesCallback callback) override; void Scan(const std::string& scanner_name, const LorgnetteManagerClient::ScanProperties& scan_properties, + PageCallback page_callback, ScanCallback callback) override; // Sets the response returned by GetScannerNames().
diff --git a/chrome/browser/chromeos/scanning/lorgnette_scanner_manager.cc b/chrome/browser/chromeos/scanning/lorgnette_scanner_manager.cc index c97a071..6178cbc 100644 --- a/chrome/browser/chromeos/scanning/lorgnette_scanner_manager.cc +++ b/chrome/browser/chromeos/scanning/lorgnette_scanner_manager.cc
@@ -89,20 +89,18 @@ // LorgnetteScannerManager: void Scan(const std::string& scanner_name, const LorgnetteManagerClient::ScanProperties& scan_properties, + PageCallback page_callback, ScanCallback callback) override { std::string device_name; ScanProtocol protocol; // Unused. if (!GetUsableDeviceNameAndProtocol(scanner_name, device_name, protocol)) { - std::move(callback).Run(base::nullopt); + std::move(callback).Run(false); return; } - GetLorgnetteManagerClient()->StartScan( - device_name, scan_properties, - base::BindOnce( - &LorgnetteScannerManagerImpl::OnScanImageToStringResponse, - weak_ptr_factory_.GetWeakPtr(), std::move(callback)), - base::nullopt); + GetLorgnetteManagerClient()->StartScan(device_name, scan_properties, + std::move(callback), page_callback, + base::nullopt); } private: @@ -150,12 +148,6 @@ std::move(callback).Run(capabilities); } - // Handles the result of calling LorgnetteManagerClient::ScanImageToString(). - void OnScanImageToStringResponse(ScanCallback callback, - base::Optional<std::string> scan_data) { - std::move(callback).Run(scan_data); - } - // Uses |response| and zeroconf_scanners_ to rebuild deduped_scanners_. void RebuildDedupedScanners( base::Optional<lorgnette::ListScannersResponse> response) {
diff --git a/chrome/browser/chromeos/scanning/lorgnette_scanner_manager.h b/chrome/browser/chromeos/scanning/lorgnette_scanner_manager.h index 4fd72d9..9c1fdf2d 100644 --- a/chrome/browser/chromeos/scanning/lorgnette_scanner_manager.h +++ b/chrome/browser/chromeos/scanning/lorgnette_scanner_manager.h
@@ -27,8 +27,8 @@ base::OnceCallback<void(std::vector<std::string> scanner_names)>; using GetScannerCapabilitiesCallback = base::OnceCallback<void( base::Optional<lorgnette::ScannerCapabilities> capabilities)>; - using ScanCallback = - base::OnceCallback<void(base::Optional<std::string> scan_data)>; + using PageCallback = base::RepeatingCallback<void(std::string scan_data)>; + using ScanCallback = base::OnceCallback<void(bool success)>; ~LorgnetteScannerManager() override = default; @@ -46,11 +46,15 @@ GetScannerCapabilitiesCallback callback) = 0; // Performs a scan with the scanner specified by |scanner_name| using the - // given |scan_properties|. If |scanner_name| does not correspond to a known - // scanner, base::nullopt is returned in the callback. + // given |scan_properties|. As each scanned page is completed, + // |page_callback| is called with the image data for that page. If + // |scanner_name| does not correspond to a known scanner, false is returned + // in |callback|. After the scan has completed, |callback| will be called + // with argument success=true. virtual void Scan( const std::string& scanner_name, const LorgnetteManagerClient::ScanProperties& scan_properties, + PageCallback page_callback, ScanCallback callback) = 0; };
diff --git a/chrome/browser/chromeos/scanning/lorgnette_scanner_manager_unittest.cc b/chrome/browser/chromeos/scanning/lorgnette_scanner_manager_unittest.cc index 6ba02218..570b07d 100644 --- a/chrome/browser/chromeos/scanning/lorgnette_scanner_manager_unittest.cc +++ b/chrome/browser/chromeos/scanning/lorgnette_scanner_manager_unittest.cc
@@ -164,6 +164,8 @@ const LorgnetteManagerClient::ScanProperties& scan_properties) { lorgnette_scanner_manager_->Scan( scanner_name, scan_properties, + base::BindRepeating(&LorgnetteScannerManagerTest::PageCallback, + base::Unretained(this)), base::Bind(&LorgnetteScannerManagerTest::ScanCallback, base::Unretained(this))); } @@ -189,7 +191,8 @@ return scanner_capabilities_; } - base::Optional<std::string> scan_data() const { return scan_data_; } + std::vector<std::string> scan_data() const { return scan_data_; } + bool scan_success() const { return scan_success_; } private: // Handles the result of calling LorgnetteScannerManager::GetScannerNames(). @@ -204,9 +207,12 @@ run_loop_->Quit(); } - // Handles the result of calling LorgnetteScannerManager::Scan(). - void ScanCallback(base::Optional<std::string> scan_data) { - scan_data_ = scan_data; + // Handles receiving a page from LorgnetteScannerManager::Scan(). + void PageCallback(std::string page_data) { scan_data_.push_back(page_data); } + + // Handles completion of LorgnetteScannerManager::Scan(). + void ScanCallback(bool success) { + scan_success_ = success; run_loop_->Quit(); } @@ -220,7 +226,8 @@ std::vector<std::string> scanner_names_; base::Optional<lorgnette::ScannerCapabilities> scanner_capabilities_; - base::Optional<std::string> scan_data_; + bool scan_success_ = false; + std::vector<std::string> scan_data_; }; // Test that no scanner names are returned when no scanners have been detected. @@ -396,7 +403,8 @@ chromeos::LorgnetteManagerClient::ScanProperties properties; Scan(kUnknownScannerName, properties); WaitForResult(); - EXPECT_FALSE(scan_data()); + EXPECT_EQ(scan_data().size(), 0); + EXPECT_FALSE(scan_success()); } // Test that scanning fails when the scanner name does not correspond to a known @@ -409,7 +417,8 @@ chromeos::LorgnetteManagerClient::ScanProperties properties; Scan(kUnknownScannerName, properties); WaitForResult(); - EXPECT_FALSE(scan_data()); + EXPECT_EQ(scan_data().size(), 0); + EXPECT_FALSE(scan_success()); } // Test that scanning fails when there is no usable device name. @@ -422,22 +431,44 @@ chromeos::LorgnetteManagerClient::ScanProperties properties; Scan(scanner.display_name, properties); WaitForResult(); - EXPECT_FALSE(scan_data()); + EXPECT_EQ(scan_data().size(), 0); + EXPECT_FALSE(scan_success()); } // Test that scanning succeeds with a valid scanner name. -TEST_F(LorgnetteScannerManagerTest, Scan) { +TEST_F(LorgnetteScannerManagerTest, ScanOnePage) { auto scanner = CreateZeroconfScanner(); fake_zeroconf_scanner_detector()->AddDetections({scanner}); CompleteTasks(); GetScannerNames(); WaitForResult(); - GetLorgnetteManagerClient()->SetScanResponse("TestScanData"); + std::vector<std::string> pages = {"TestScanData"}; + GetLorgnetteManagerClient()->SetScanResponse(pages); chromeos::LorgnetteManagerClient::ScanProperties properties; Scan(scanner.display_name, properties); WaitForResult(); - ASSERT_TRUE(scan_data()); - EXPECT_EQ(scan_data().value(), "TestScanData"); + ASSERT_EQ(scan_data().size(), 1); + EXPECT_EQ(scan_data()[0], "TestScanData"); + EXPECT_TRUE(scan_success()); +} + +TEST_F(LorgnetteScannerManagerTest, ScanMultiplePages) { + auto scanner = CreateZeroconfScanner(); + fake_zeroconf_scanner_detector()->AddDetections({scanner}); + CompleteTasks(); + GetScannerNames(); + WaitForResult(); + std::vector<std::string> pages = {"TestPageOne", "TestPageTwo", + "TestPageThree"}; + GetLorgnetteManagerClient()->SetScanResponse(pages); + chromeos::LorgnetteManagerClient::ScanProperties properties; + Scan(scanner.display_name, properties); + WaitForResult(); + ASSERT_EQ(scan_data().size(), 3); + EXPECT_EQ(scan_data()[0], "TestPageOne"); + EXPECT_EQ(scan_data()[1], "TestPageTwo"); + EXPECT_EQ(scan_data()[2], "TestPageThree"); + EXPECT_TRUE(scan_success()); } } // namespace chromeos
diff --git a/chrome/browser/chromeos/usb/cros_usb_detector.cc b/chrome/browser/chromeos/usb/cros_usb_detector.cc index df75a08c..6b62d17 100644 --- a/chrome/browser/chromeos/usb/cros_usb_detector.cc +++ b/chrome/browser/chromeos/usb/cros_usb_detector.cc
@@ -291,10 +291,6 @@ CrosUsbDeviceInfo::CrosUsbDeviceInfo(const CrosUsbDeviceInfo&) = default; CrosUsbDeviceInfo::~CrosUsbDeviceInfo() = default; -CrosUsbDeviceInfo::VmSharingInfo::VmSharingInfo() = default; -CrosUsbDeviceInfo::VmSharingInfo::VmSharingInfo(const VmSharingInfo&) = default; -CrosUsbDeviceInfo::VmSharingInfo::~VmSharingInfo() = default; - std::string CrosUsbDetector::MakeNotificationId(const std::string& guid) { return "cros:" + guid; } @@ -510,10 +506,8 @@ std::string guid = device_info->guid; for (const auto& device : usb_devices_) { - if (device.guid == guid) { - for (const auto& sharing_info_pair : device.vm_sharing_info) { - DetachUsbDeviceFromVm(sharing_info_pair.first, guid, base::DoNothing()); - } + if (device.guid == guid && device.shared_vm_name) { + DetachUsbDeviceFromVm(*device.shared_vm_name, guid, base::DoNothing()); } } const auto& start = std::remove_if( @@ -536,13 +530,11 @@ const std::string& vm_name) { // Reattach shared devices when the VM becomes available. for (auto& device : usb_devices_) { - for (auto& sharing_pair : device.vm_sharing_info) { - if (sharing_pair.second.shared && sharing_pair.first == vm_name) { - VLOG(1) << "Connecting " << device.label << " to " << vm_name; - // Clear any older guest_port setting. - sharing_pair.second.guest_port = base::nullopt; - AttachUsbDeviceToVm(vm_name, device.guid, base::DoNothing()); - } + if (device.shared_vm_name == vm_name) { + VLOG(1) << "Connecting " << device.label << " to " << vm_name; + // Clear any older guest_port setting. + device.guest_port = base::nullopt; + AttachUsbDeviceToVm(vm_name, device.guid, base::DoNothing()); } } } @@ -554,11 +546,21 @@ uint32_t allowed_interfaces_mask = 0; for (auto& device : usb_devices_) { if (device.guid == guid) { + // Detach first if device is attached elsewhere + if (device.shared_vm_name && device.shared_vm_name != vm_name) { + DetachUsbDeviceFromVm( + *device.shared_vm_name, guid, + base::BindOnce(&CrosUsbDetector::AttachAfterDetach, + weak_ptr_factory_.GetWeakPtr(), vm_name, guid, + std::move(callback))); + return; + } + // Mark the USB device shared so that we know to reattach it on VM // restart. // Setting this flag early also allows the UI not to flicker because of // the notification resulting from the default VM detach below. - device.vm_sharing_info[vm_name].shared = true; + device.shared_vm_name = vm_name; allowed_interfaces_mask = device.allowed_interfaces_mask; // The guest port will be set on completion. break; @@ -566,6 +568,8 @@ } auto it = available_device_info_.find(guid); if (it == available_device_info_.end()) { + LOG(ERROR) << "No device info for " << guid; + std::move(callback).Run(false); return; } @@ -595,22 +599,21 @@ if (it == available_device_info_.end()) { // If there wasn't an existing attachment, then removal is a no-op and // always succeeds + LOG(ERROR) << "No device found to detach " << guid; std::move(callback).Run(/*success=*/true); return; } base::Optional<uint8_t> guest_port; for (const auto& device : usb_devices_) { - if (device.guid == guid) { - const auto it = device.vm_sharing_info.find(vm_name); - if (it != device.vm_sharing_info.end()) { - guest_port = it->second.guest_port; - break; - } + if (device.guid == guid && device.shared_vm_name == vm_name) { + guest_port = device.guest_port; + break; } } if (!guest_port) { + LOG(ERROR) << "No port found to detach " << guid; std::move(callback).Run(/*success=*/true); return; } @@ -652,8 +655,7 @@ } for (const auto& device : usb_devices_) { if (device.guid == device_info->guid) { - const auto it = device.vm_sharing_info.find(vm_name); - if (it != device.vm_sharing_info.end() && it->second.guest_port) { + if (device.shared_vm_name == vm_name && device.guest_port) { LOG(ERROR) << "Device " << device.label << " is already shared"; // The device is already attached. std::move(callback).Run(/*success=*/true); @@ -694,9 +696,8 @@ if (success) { for (auto& device : usb_devices_) { if (device.guid == guid) { - auto& vm_sharing_info = device.vm_sharing_info[vm_name]; - vm_sharing_info.shared = true; - vm_sharing_info.guest_port = response->guest_port(); + device.shared_vm_name = vm_name; + device.guest_port = response->guest_port(); break; } } @@ -721,7 +722,8 @@ for (auto& device : usb_devices_) { if (device.guid == guid) { - device.vm_sharing_info.erase(vm_name); + device.shared_vm_name = base::nullopt; + device.guest_port = base::nullopt; break; } } @@ -729,4 +731,16 @@ std::move(callback).Run(success); } +void CrosUsbDetector::AttachAfterDetach( + const std::string& vm_name, + const std::string& guid, + base::OnceCallback<void(bool success)> callback, + bool success) { + if (!success) { + LOG(ERROR) << "Failed to detatch before attach"; + std::move(callback).Run(false); + return; + } + AttachUsbDeviceToVm(vm_name, guid, std::move(callback)); +} } // namespace chromeos
diff --git a/chrome/browser/chromeos/usb/cros_usb_detector.h b/chrome/browser/chromeos/usb/cros_usb_detector.h index 4356b10..fe6ffd1 100644 --- a/chrome/browser/chromeos/usb/cros_usb_detector.h +++ b/chrome/browser/chromeos/usb/cros_usb_detector.h
@@ -51,25 +51,15 @@ CrosUsbDeviceInfo(const CrosUsbDeviceInfo&); ~CrosUsbDeviceInfo(); - struct VmSharingInfo { - VmSharingInfo(); - VmSharingInfo(const VmSharingInfo&); - ~VmSharingInfo(); - - // Whether the device is shared with the VM. Note that the device may be - // shared but not attached (yet) in which case |guest_port| below would be - // unset. - bool shared = false; - base::Optional<uint8_t> guest_port; - }; - - // Maps a VM name to the sharing/attach information of the device in the VM - // identified by that name. - base::flat_map<std::string, VmSharingInfo> vm_sharing_info; std::string guid; base::string16 label; // Whether the device can be shared with guest OSes. bool sharable_with_crostini = false; + // Name of VM shared with. Unset if not shared. Note that the device may be + // shared but not attached (yet) in which case |guest_port| below would be + // unset. + base::Optional<std::string> shared_vm_name; + base::Optional<uint8_t> guest_port; // Interfaces shareable with guest OSes uint32_t allowed_interfaces_mask = 0; // TODO(nverne): Add current state and errors etc. @@ -182,6 +172,12 @@ base::OnceCallback<void(bool success)> callback, base::Optional<vm_tools::concierge::DetachUsbDeviceResponse> response); + // Devices will be auto-detached if they are attached to another VM. + void AttachAfterDetach(const std::string& vm_name, + const std::string& guid, + base::OnceCallback<void(bool success)> callback, + bool success); + // Returns true when a device should show a notification when attached. bool ShouldShowNotification(const device::mojom::UsbDeviceInfo& device_info, uint32_t allowed_interfaces_mask);
diff --git a/chrome/browser/chromeos/usb/cros_usb_detector_unittest.cc b/chrome/browser/chromeos/usb/cros_usb_detector_unittest.cc index 11622f2..1d2a69fd 100644 --- a/chrome/browser/chromeos/usb/cros_usb_detector_unittest.cc +++ b/chrome/browser/chromeos/usb/cros_usb_detector_unittest.cc
@@ -198,13 +198,6 @@ return devices.front(); } - static bool IsSharedWithCrostini( - const chromeos::CrosUsbDeviceInfo& device_info) { - const auto it = device_info.vm_sharing_info.find( - crostini::kCrostiniDefaultVmName); - return it != device_info.vm_sharing_info.end() && it->second.shared; - } - protected: base::string16 connection_message(const char* product_name) { return base::ASCIIToUTF16(base::StringPrintf( @@ -737,15 +730,12 @@ auto device_info = GetSingleDeviceInfo(); AttachDeviceToVm(crostini::kCrostiniDefaultVmName, device_info.guid); - EXPECT_FALSE( - chromeos::CrosUsbDeviceInfo::VmSharingInfo().guest_port.has_value()); + EXPECT_FALSE(device_info.guest_port.has_value()); device_info = GetSingleDeviceSharableWithCrostini(); - EXPECT_EQ(1U, device_info.vm_sharing_info.size()); - auto crostini_info = - device_info.vm_sharing_info[crostini::kCrostiniDefaultVmName]; - EXPECT_TRUE(crostini_info.shared); - EXPECT_TRUE(crostini_info.guest_port.has_value()); - EXPECT_EQ(0U, *crostini_info.guest_port); + EXPECT_TRUE(device_info.shared_vm_name.has_value()); + EXPECT_EQ(crostini::kCrostiniDefaultVmName, *device_info.shared_vm_name); + EXPECT_TRUE(device_info.guest_port.has_value()); + EXPECT_EQ(0U, *device_info.guest_port); } TEST_F(CrosUsbDetectorTest, AttachingAlreadyAttachedDeviceIsANoOp) { @@ -758,15 +748,15 @@ base::RunLoop().RunUntilIdle(); auto device_info = GetSingleDeviceInfo(); - EXPECT_EQ(0U, device_info.vm_sharing_info.size()); + EXPECT_FALSE(device_info.shared_vm_name.has_value()); AttachDeviceToVm(crostini::kCrostiniDefaultVmName, device_info.guid); cros_usb_detector_->AddUsbDeviceObserver(&usb_device_observer_); AttachDeviceToVm(crostini::kCrostiniDefaultVmName, device_info.guid); EXPECT_EQ(0, usb_device_observer_.notify_count()); device_info = GetSingleDeviceInfo(); - EXPECT_EQ(1U, device_info.vm_sharing_info.size()); - EXPECT_TRUE(IsSharedWithCrostini(device_info)); + EXPECT_TRUE(device_info.shared_vm_name.has_value()); + EXPECT_EQ(crostini::kCrostiniDefaultVmName, *device_info.shared_vm_name); } TEST_F(CrosUsbDetectorTest, DeviceCanBeAttachedToArcVmWhenCrostiniIsDisabled) { @@ -778,8 +768,12 @@ device_manager_.AddDevice(device_1); base::RunLoop().RunUntilIdle(); - AttachDeviceToVm(arc::kArcVmName, GetSingleDeviceInfo().guid); - EXPECT_TRUE(GetSingleDeviceInfo().vm_sharing_info[arc::kArcVmName].shared); + auto device_info = GetSingleDeviceInfo(); + AttachDeviceToVm(arc::kArcVmName, device_info.guid); + base::RunLoop().RunUntilIdle(); + device_info = GetSingleDeviceInfo(); + EXPECT_TRUE(device_info.shared_vm_name.has_value()); + EXPECT_EQ(arc::kArcVmName, *device_info.shared_vm_name); } TEST_F(CrosUsbDetectorTest, SharedDevicesGetAttachedOnStartup) { @@ -796,13 +790,14 @@ base::RunLoop().RunUntilIdle(); // No device is shared with Crostini, yet. EXPECT_EQ(0, usb_device_observer_.notify_count()); - auto device = GetSingleDeviceInfo(); - EXPECT_EQ(0U, device.vm_sharing_info.size()); + auto device_info = GetSingleDeviceInfo(); + EXPECT_FALSE(device_info.shared_vm_name.has_value()); - device = GetSingleDeviceInfo(); - AttachDeviceToVm(crostini::kCrostiniDefaultVmName, device.guid); + AttachDeviceToVm(crostini::kCrostiniDefaultVmName, device_info.guid); base::RunLoop().RunUntilIdle(); - EXPECT_TRUE(IsSharedWithCrostini(GetSingleDeviceInfo())); + device_info = GetSingleDeviceInfo(); + EXPECT_TRUE(device_info.shared_vm_name.has_value()); + EXPECT_EQ(crostini::kCrostiniDefaultVmName, *device_info.shared_vm_name); // Concierge::VmStarted signal should trigger connections. cros_usb_detector_->AddUsbDeviceObserver(&usb_device_observer_); @@ -811,7 +806,9 @@ fake_concierge_client_->NotifyVmStarted(vm_started_signal); base::RunLoop().RunUntilIdle(); EXPECT_EQ(1, usb_device_observer_.notify_count()); - EXPECT_TRUE(IsSharedWithCrostini(GetSingleDeviceInfo())); + device_info = GetSingleDeviceInfo(); + EXPECT_TRUE(device_info.shared_vm_name.has_value()); + EXPECT_EQ(crostini::kCrostiniDefaultVmName, *device_info.shared_vm_name); // VmPluginDispatcherClient::OnVmStateChanged RUNNING should also trigger. vm_tools::plugin_dispatcher::VmStateChangedSignal vm_state_changed_signal; @@ -822,7 +819,9 @@ vm_state_changed_signal); base::RunLoop().RunUntilIdle(); EXPECT_EQ(2, usb_device_observer_.notify_count()); - EXPECT_TRUE(IsSharedWithCrostini(GetSingleDeviceInfo())); + device_info = GetSingleDeviceInfo(); + EXPECT_TRUE(device_info.shared_vm_name.has_value()); + EXPECT_EQ(crostini::kCrostiniDefaultVmName, *device_info.shared_vm_name); } TEST_F(CrosUsbDetectorTest, DeviceAllowedInterfacesMaskSetCorrectly) { @@ -852,3 +851,30 @@ EXPECT_EQ(0x00000006U, device_info.allowed_interfaces_mask); } + +TEST_F(CrosUsbDetectorTest, DetachBeforeAttach) { + ConnectToDeviceManager(); + base::RunLoop().RunUntilIdle(); + + auto device_1 = base::MakeRefCounted<device::FakeUsbDeviceInfo>( + 0, 1, kManufacturerName, kProductName_1, "002"); + device_manager_.AddDevice(device_1); + base::RunLoop().RunUntilIdle(); + + auto device_info = GetSingleDeviceInfo(); + EXPECT_FALSE(device_info.shared_vm_name.has_value()); + + AttachDeviceToVm("VM1", device_info.guid); + base::RunLoop().RunUntilIdle(); + device_info = GetSingleDeviceInfo(); + EXPECT_TRUE(device_info.shared_vm_name.has_value()); + EXPECT_EQ("VM1", *device_info.shared_vm_name); + EXPECT_FALSE(fake_concierge_client_->detach_usb_device_called()); + + AttachDeviceToVm("VM2", device_info.guid); + base::RunLoop().RunUntilIdle(); + device_info = GetSingleDeviceInfo(); + EXPECT_TRUE(device_info.shared_vm_name.has_value()); + EXPECT_EQ("VM2", *device_info.shared_vm_name); + EXPECT_TRUE(fake_concierge_client_->detach_usb_device_called()); +}
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index 68ae7f0..3644f38 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json
@@ -419,7 +419,7 @@ { "name": "block-external-form-redirects-no-gesture", "owners": [ "jochen", "tedchoc" ], - "expiry_milestone": 86 + "expiry_milestone": 89 }, { "name": "bluetooth-aggressive-appearance-filter", @@ -882,16 +882,6 @@ "expiry_milestone": 90 }, { - "name": "disable-experimental-accessibility-chromevox-language-switching", - "owners": [ "akihiroota", "dmazzoni", "dtseng" ], - "expiry_milestone": 86 - }, - { - "name": "disable-experimental-accessibility-chromevox-search-menus", - "owners": [ "akihiroota" ], - "expiry_milestone": 86 - }, - { "name": "disable-explicit-dma-fences", "owners": [ "chromeos-gfx@google.com" ], // This flag is used for QA & debugging on ChromeOS, which has no way to @@ -1485,7 +1475,7 @@ { "name": "enable-experimental-accessibility-chromevox-annotations", "owners": [ "akihiroota" ], - "expiry_milestone": 86 + "expiry_milestone": 89 }, { "name": "enable-experimental-accessibility-chromevox-tutorial", @@ -2476,7 +2466,7 @@ { "name": "explore-sites", "owners": [ "chili", "dewittj" ], - "expiry_milestone": 85 + "expiry_milestone": 95 }, { "name": "extend-open-in-files-support",
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index 58fe0ac..c86c5f21 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -4034,22 +4034,11 @@ const char kExperimentalAccessibilityChromeVoxAnnotationsDescription[] = "Allows users to create custom annotations for elements using ChromeVox."; -const char kExperimentalAccessibilityChromeVoxLanguageSwitchingName[] = - "Disable ChromeVox language switching."; -const char kExperimentalAccessibilityChromeVoxLanguageSwitchingDescription[] = - "Disable ChromeVox language switching, which changes ChromeVox's " - "output language upon detection of new language."; - const char kExperimentalAccessibilityChromeVoxTutorialName[] = "Enable experimental ChromeVox interactive tutorial."; const char kExperimentalAccessibilityChromeVoxTutorialDescription[] = "A comprehensive and interactive tutorial for the ChromeVox screen reader."; -const char kExperimentalAccessibilityChromeVoxSearchMenusName[] = - "Enable experimental ChromeVox search menus feature."; -const char kExperimentalAccessibilityChromeVoxSearchMenusDescription[] = - "Allows users to search for items in the ChromeVox menu."; - const char kExperimentalAccessibilitySwitchAccessName[] = "Experimental feature Switch Access"; const char kExperimentalAccessibilitySwitchAccessDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index df14cd2..5c15b26 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -2332,13 +2332,6 @@ extern const char kExperimentalAccessibilityChromeVoxAnnotationsName[]; extern const char kExperimentalAccessibilityChromeVoxAnnotationsDescription[]; -extern const char kExperimentalAccessibilityChromeVoxLanguageSwitchingName[]; -extern const char - kExperimentalAccessibilityChromeVoxLanguageSwitchingDescription[]; - -extern const char kExperimentalAccessibilityChromeVoxSearchMenusName[]; -extern const char kExperimentalAccessibilityChromeVoxSearchMenusDescription[]; - extern const char kExperimentalAccessibilityChromeVoxTutorialName[]; extern const char kExperimentalAccessibilityChromeVoxTutorialDescription[];
diff --git a/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc b/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc index 18cae7a0..ed4ff2e 100644 --- a/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc +++ b/chrome/browser/lookalikes/lookalike_url_navigation_throttle.cc
@@ -138,14 +138,20 @@ return content::NavigationThrottle::PROCEED; } - // If the URL is in the component updater allowlist, don't show any warning. + // Fetch the component allowlist. const auto* proto = GetSafetyTipsRemoteConfigProto(); - if (proto && - IsUrlAllowlistedBySafetyTipsComponent(proto, url.GetWithEmptyPath())) { + + // When there's no proto (like at browser start), fail-safe and don't block. + if (!proto) { return content::NavigationThrottle::PROCEED; } - // If the URL is in the allowlist, don't show any warning. + // If the URL is in the component allowlist, don't show any warning. + if (IsUrlAllowlistedBySafetyTipsComponent(proto, url.GetWithEmptyPath())) { + return content::NavigationThrottle::PROCEED; + } + + // If the URL is in the local temporary allowlist, don't show any warning. if (tab_storage->IsDomainAllowed(url.host())) { return content::NavigationThrottle::PROCEED; }
diff --git a/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc b/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc index f4bf15e..47b66446 100644 --- a/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc +++ b/chrome/browser/lookalikes/lookalike_url_navigation_throttle_browsertest.cc
@@ -19,6 +19,7 @@ #include "chrome/browser/lookalikes/lookalike_url_service.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/reputation/safety_tip_test_utils.h" +#include "chrome/browser/reputation/safety_tips_config.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" #include "chrome/common/chrome_features.h" @@ -212,6 +213,7 @@ } feature_list_.InitWithFeaturesAndParameters(enabled_features, disabled_features); + InitializeSafetyTipConfig(); InProcessBrowserTest::SetUp(); } @@ -450,6 +452,20 @@ LookalikeUrlMatchType::kSkeletonMatchTop500); } +// Navigate to a domain that would trigger the warning, but doesn't because it +// fails-safe when the allowlist isn't available. +IN_PROC_BROWSER_TEST_P(LookalikeUrlNavigationThrottleBrowserTest, + NoMatchOnAllowlistMissing) { + const GURL kNavigatedUrl = GetURL("googlé.com"); + + // Clear out any existing proto. + SetSafetyTipsRemoteConfigProto(nullptr); + + SetEngagementScore(browser(), kNavigatedUrl, kLowEngagement); + TestInterstitialNotShown(browser(), kNavigatedUrl); + CheckNoUkm(); +} + // Embedding a top domain should show an interstitial when enabled. If disabled // this would trigger safety tips when target embedding feature parameter is // enabled for safety tips.
diff --git a/chrome/browser/lookalikes/lookalike_url_navigation_throttle_unittest.cc b/chrome/browser/lookalikes/lookalike_url_navigation_throttle_unittest.cc index d3674352..587d9c9 100644 --- a/chrome/browser/lookalikes/lookalike_url_navigation_throttle_unittest.cc +++ b/chrome/browser/lookalikes/lookalike_url_navigation_throttle_unittest.cc
@@ -6,6 +6,7 @@ #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" +#include "chrome/browser/reputation/safety_tip_test_utils.h" #include "chrome/test/base/chrome_render_view_host_test_harness.h" #include "components/lookalikes/core/features.h" #include "components/url_formatter/spoof_checks/idn_spoof_checker.h" @@ -89,6 +90,8 @@ TEST_F(LookalikeThrottleTest, SpoofsBlocked) { base::HistogramTester test; + InitializeSafetyTipConfig(); + const struct TestCase { const char* hostname; bool expected_blocked;
diff --git a/chrome/browser/media/kaleidoscope/kaleidoscope_ui.cc b/chrome/browser/media/kaleidoscope/kaleidoscope_ui.cc index b8589ad..0bc55a33 100644 --- a/chrome/browser/media/kaleidoscope/kaleidoscope_ui.cc +++ b/chrome/browser/media/kaleidoscope/kaleidoscope_ui.cc
@@ -311,6 +311,11 @@ untrusted_source->OverrideContentSecurityPolicy( network::mojom::CSPDirectiveName::ConnectSrc, "connect-src *;"); + // Allow YouTube videos to be embedded. + untrusted_source->OverrideContentSecurityPolicy( + network::mojom::CSPDirectiveName::ChildSrc, + "child-src https://www.youtube.com;"); + // Add the URL to the backend. untrusted_source->AddString("googleApiUrl", backend_url.spec());
diff --git a/chrome/browser/nearby_sharing/nearby_connections_manager_impl.cc b/chrome/browser/nearby_sharing/nearby_connections_manager_impl.cc index bc61fda..14d15d2 100644 --- a/chrome/browser/nearby_sharing/nearby_connections_manager_impl.cc +++ b/chrome/browser/nearby_sharing/nearby_connections_manager_impl.cc
@@ -18,6 +18,8 @@ namespace { const char kServiceId[] = "NearbySharing"; +const char kFastAdvertisementServiceUuid[] = + "0000fef3-0000-1000-8000-00805f9b34fb"; const location::nearby::connections::mojom::Strategy kStrategy = location::nearby::connections::mojom::Strategy::kP2pPointToPoint; @@ -91,9 +93,12 @@ incoming_connection_listener_ = listener; nearby_connections_->StartAdvertising( endpoint_info, kServiceId, - AdvertisingOptions::New(kStrategy, std::move(allowed_mediums), - /*auto_upgrade_bandwidth=*/is_high_power, - /*enforce_topology_constraints=*/true), + AdvertisingOptions::New( + kStrategy, std::move(allowed_mediums), + /*auto_upgrade_bandwidth=*/is_high_power, + /*enforce_topology_constraints=*/true, + /*fast_advertisement_service_uuid=*/ + device::BluetoothUUID(kFastAdvertisementServiceUuid)), std::move(lifecycle_listener), std::move(callback)); } @@ -124,6 +129,8 @@ } discovery_listener_ = listener; + // TODO(b/168659459): Inject kFastAdvertisementServiceUuid once BLE scanning + // actually uses it. nearby_connections_->StartDiscovery( kServiceId, DiscoveryOptions::New(kStrategy), endpoint_discovery_listener_.BindNewPipeAndPassRemote(),
diff --git a/chrome/browser/nearby_sharing/nearby_per_session_discovery_manager.cc b/chrome/browser/nearby_sharing/nearby_per_session_discovery_manager.cc index a1b301e..01b167e5 100644 --- a/chrome/browser/nearby_sharing/nearby_per_session_discovery_manager.cc +++ b/chrome/browser/nearby_sharing/nearby_per_session_discovery_manager.cc
@@ -85,12 +85,13 @@ void NearbyPerSessionDiscoveryManager::StartDiscovery( mojo::PendingRemote<nearby_share::mojom::ShareTargetListener> listener, StartDiscoveryCallback callback) { + // Starting discovery again closes any previous discovery session. + share_target_listener_.reset(); share_target_listener_.Bind(std::move(listener)); - // |share_target_listener_| owns the callbacks and is guaranteed to be - // destroyed before |this|, therefore making base::Unretained() safe to use. - share_target_listener_.set_disconnect_handler( - base::BindOnce(&NearbyPerSessionDiscoveryManager::UnregisterSendSurface, - base::Unretained(this))); + // NOTE: Previously we set a disconnect handler here that called + // UnregisterSendSurface, but this causes transfer updates to stop flowing to + // to the UI. Instead, we rely on the destructor's call to + // UnregisterSendSurface which will trigger when the share sheet goes away. if (nearby_sharing_service_->RegisterSendSurface( this, this, NearbySharingService::SendSurfaceState::kForeground) != @@ -100,7 +101,10 @@ std::move(callback).Run(/*success=*/false); return; } - + // Once this object is registered as send surface, we stay registered until + // UnregisterSendSurface is called so that the transfer update listeners can + // get updates even if Discovery is stopped. + registered_as_send_surface_ = true; std::move(callback).Run(/*success=*/true); } @@ -193,13 +197,13 @@ } void NearbyPerSessionDiscoveryManager::UnregisterSendSurface() { - if (!share_target_listener_.is_bound()) - return; + if (registered_as_send_surface_) { + if (nearby_sharing_service_->UnregisterSendSurface(this, this) != + NearbySharingService::StatusCodes::kOk) { + NS_LOG(WARNING) << "Failed to unregister send surface"; + } + registered_as_send_surface_ = false; + } share_target_listener_.reset(); - - if (nearby_sharing_service_->UnregisterSendSurface(this, this) != - NearbySharingService::StatusCodes::kOk) { - NS_LOG(WARNING) << "Failed to unregister send surface"; - } }
diff --git a/chrome/browser/nearby_sharing/nearby_per_session_discovery_manager.h b/chrome/browser/nearby_sharing/nearby_per_session_discovery_manager.h index 3c660fd..10fb0520 100644 --- a/chrome/browser/nearby_sharing/nearby_per_session_discovery_manager.h +++ b/chrome/browser/nearby_sharing/nearby_per_session_discovery_manager.h
@@ -52,6 +52,7 @@ // Unregisters this class from the NearbySharingService. void UnregisterSendSurface(); + bool registered_as_send_surface_ = false; NearbySharingService* nearby_sharing_service_; std::vector<std::unique_ptr<Attachment>> attachments_; mojo::Remote<nearby_share::mojom::ShareTargetListener> share_target_listener_;
diff --git a/chrome/browser/nearby_sharing/nearby_per_session_discovery_manager_unittest.cc b/chrome/browser/nearby_sharing/nearby_per_session_discovery_manager_unittest.cc index fa5216a0..b380568 100644 --- a/chrome/browser/nearby_sharing/nearby_per_session_discovery_manager_unittest.cc +++ b/chrome/browser/nearby_sharing/nearby_per_session_discovery_manager_unittest.cc
@@ -6,7 +6,9 @@ #include <string> +#include "base/callback_forward.h" #include "base/optional.h" +#include "base/run_loop.h" #include "base/test/mock_callback.h" #include "chrome/browser/nearby_sharing/mock_nearby_sharing_service.h" #include "chrome/browser/nearby_sharing/share_target.h" @@ -57,6 +59,8 @@ return receiver_.BindNewPipeAndPassRemote(); } + void reset() { receiver_.reset(); } + // nearby_share::mojom::ShareTargetListener: MOCK_METHOD(void, OnShareTargetDiscovered, (const ShareTarget&), (override)); MOCK_METHOD(void, OnShareTargetLost, (const ShareTarget&), (override)); @@ -131,11 +135,28 @@ RegisterSendSurface(&manager(), &manager(), NearbySharingService::SendSurfaceState::kForeground)) .WillOnce(testing::Return(NearbySharingService::StatusCodes::kOk)); + EXPECT_CALL(sharing_service(), UnregisterSendSurface(&manager(), &manager())) - .WillOnce(testing::Return(NearbySharingService::StatusCodes::kOk)); + .Times(0); // Should not be called! MockShareTargetListener listener; manager().StartDiscovery(listener.Bind(), callback.Get()); + + // Reset the listener here like the UI does when switching pages. + listener.reset(); + // We have to run util idle to give the disconnect handler a chance to run. + // We no longer have a disconnect handler, but we want to very that once the + // mojo connection is torn down, that we don't unregister. + base::RunLoop run_loop; + run_loop.RunUntilIdle(); + // Verify that UnregisterSendSurface was NOT called due to the disconnect. + // This previously failed when disconnect handler was being registered. + EXPECT_TRUE(::testing::Mock::VerifyAndClearExpectations(&sharing_service())); + + // However, we do expect UnregisterSendSurface to be called from the + // destructor. + EXPECT_CALL(sharing_service(), UnregisterSendSurface(&manager(), &manager())) + .WillOnce(testing::Return(NearbySharingService::StatusCodes::kOk)); } TEST_F(NearbyPerSessionDiscoveryManagerTest, StartDiscovery_Error) { @@ -171,6 +192,9 @@ manager().OnShareTargetDiscovered(share_target); run_loop.Run(); + + EXPECT_CALL(sharing_service(), UnregisterSendSurface(&manager(), &manager())) + .WillOnce(testing::Return(NearbySharingService::StatusCodes::kOk)); } TEST_F(NearbyPerSessionDiscoveryManagerTest, OnShareTargetLost) { @@ -190,6 +214,9 @@ manager().OnShareTargetLost(share_target); run_loop.Run(); + + EXPECT_CALL(sharing_service(), UnregisterSendSurface(&manager(), &manager())) + .WillOnce(testing::Return(NearbySharingService::StatusCodes::kOk)); } TEST_F(NearbyPerSessionDiscoveryManagerTest, SelectShareTarget_Invalid) { @@ -206,6 +233,9 @@ testing::IsFalse(), testing::IsFalse())); manager().SelectShareTarget({}, callback.Get()); + + EXPECT_CALL(sharing_service(), UnregisterSendSurface(&manager(), &manager())) + .WillOnce(testing::Return(NearbySharingService::StatusCodes::kOk)); } TEST_F(NearbyPerSessionDiscoveryManagerTest, SelectShareTarget_SendSuccess) { @@ -234,6 +264,9 @@ })); manager().SelectShareTarget(share_target.id, callback.Get()); + + EXPECT_CALL(sharing_service(), UnregisterSendSurface(&manager(), &manager())) + .WillOnce(testing::Return(NearbySharingService::StatusCodes::kOk)); } TEST_F(NearbyPerSessionDiscoveryManagerTest, SelectShareTarget_SendError) { @@ -264,6 +297,9 @@ })); manager().SelectShareTarget(share_target.id, callback.Get()); + + EXPECT_CALL(sharing_service(), UnregisterSendSurface(&manager(), &manager())) + .WillOnce(testing::Return(NearbySharingService::StatusCodes::kOk)); } TEST_F(NearbyPerSessionDiscoveryManagerTest, OnTransferUpdate_WaitRemote) { @@ -275,7 +311,7 @@ .WillOnce(testing::Return(NearbySharingService::StatusCodes::kOk)); base::RunLoop run_loop; - EXPECT_CALL(transfer_listener, OnTransferUpdate) + EXPECT_CALL(transfer_listener, OnTransferUpdate(_, _)) .WillOnce(testing::Invoke( [&run_loop](nearby_share::mojom::TransferStatus status, const base::Optional<std::string>& token) { @@ -288,6 +324,8 @@ manager().StartDiscovery(listener.Bind(), base::DoNothing()); ShareTarget share_target; + EXPECT_CALL(listener, OnShareTargetDiscovered(MatchesTarget(share_target))) + .Times(1); manager().OnShareTargetDiscovered(share_target); MockSelectShareTargetCallback callback; @@ -313,6 +351,8 @@ manager().OnTransferUpdate(share_target, metadata); run_loop.Run(); + EXPECT_CALL(sharing_service(), UnregisterSendSurface(&manager(), &manager())) + .WillOnce(testing::Return(NearbySharingService::StatusCodes::kOk)); } TEST_F(NearbyPerSessionDiscoveryManagerTest, OnTransferUpdate_WaitLocal) { @@ -326,7 +366,7 @@ const std::string expected_token = "Test Token"; base::RunLoop run_loop; - EXPECT_CALL(transfer_listener, OnTransferUpdate) + EXPECT_CALL(transfer_listener, OnTransferUpdate(_, _)) .WillOnce(testing::Invoke([&run_loop, &expected_token]( nearby_share::mojom::TransferStatus status, const base::Optional<std::string>& token) { @@ -339,6 +379,8 @@ manager().StartDiscovery(listener.Bind(), base::DoNothing()); ShareTarget share_target; + EXPECT_CALL(listener, OnShareTargetDiscovered(MatchesTarget(share_target))) + .Times(1); manager().OnShareTargetDiscovered(share_target); MockSelectShareTargetCallback callback; @@ -365,4 +407,7 @@ manager().OnTransferUpdate(share_target, metadata); run_loop.Run(); + + EXPECT_CALL(sharing_service(), UnregisterSendSurface(&manager(), &manager())) + .WillOnce(testing::Return(NearbySharingService::StatusCodes::kOk)); }
diff --git a/chrome/browser/nearby_sharing/nearby_sharing_service_impl.cc b/chrome/browser/nearby_sharing/nearby_sharing_service_impl.cc index 484c648..cd1041d 100644 --- a/chrome/browser/nearby_sharing/nearby_sharing_service_impl.cc +++ b/chrome/browser/nearby_sharing/nearby_sharing_service_impl.cc
@@ -2898,16 +2898,19 @@ text.set_text_body(std::string()); } + // Make sure to call this before calling Disconnect or we risk loosing some + // transfer updates in the receive case due to the Disconnect call cleaning up + // share targets. + ShareTargetInfo* info = GetShareTargetInfo(share_target); + if (info && info->transfer_update_callback()) + info->transfer_update_callback()->OnTransferUpdate(share_target, metadata); + if (TransferMetadata::IsFinalStatus(metadata.status())) { if (metadata.status() != TransferMetadata::Status::kComplete) OnPayloadsFailed(share_target); Disconnect(share_target, metadata); } - - ShareTargetInfo* info = GetShareTargetInfo(share_target); - if (info && info->transfer_update_callback()) - info->transfer_update_callback()->OnTransferUpdate(share_target, metadata); } bool NearbySharingServiceImpl::OnIncomingPayloadsComplete( @@ -3023,13 +3026,21 @@ // Failed to send or receive. No point in continuing, so disconnect // immediately. if (metadata.status() != TransferMetadata::Status::kComplete) { - nearby_connections_manager_->Disconnect(*endpoint_id); + if (share_target_info->connection()) { + share_target_info->connection()->Close(); + } else { + nearby_connections_manager_->Disconnect(*endpoint_id); + } return; } // Files received successfully. Receivers can immediately cancel. if (share_target.is_incoming) { - nearby_connections_manager_->Disconnect(*endpoint_id); + if (share_target_info->connection()) { + share_target_info->connection()->Close(); + } else { + nearby_connections_manager_->Disconnect(*endpoint_id); + } return; }
diff --git a/chrome/browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc b/chrome/browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc index 1f6f1863..7814cfb0 100644 --- a/chrome/browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc +++ b/chrome/browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc
@@ -2485,7 +2485,7 @@ EXPECT_FALSE( fake_nearby_connections_manager_->connection_endpoint_info(kEndpointId)); - EXPECT_TRUE(fake_nearby_connections_manager_->has_incoming_payloads()); + EXPECT_FALSE(fake_nearby_connections_manager_->has_incoming_payloads()); // TODO(crbug.com/1123022): This check is flaky, should be investigated. // EXPECT_TRUE(FileExists(file_path));
diff --git a/chrome/browser/net/variations_http_headers_browsertest.cc b/chrome/browser/net/variations_http_headers_browsertest.cc index 5ee6043c..073c312 100644 --- a/chrome/browser/net/variations_http_headers_browsertest.cc +++ b/chrome/browser/net/variations_http_headers_browsertest.cc
@@ -53,7 +53,6 @@ #include "services/network/public/cpp/shared_url_loader_factory.h" #include "services/network/public/cpp/simple_url_loader.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/blink/public/common/service_worker/service_worker_utils.h" #include "url/gurl.h" namespace { @@ -599,13 +598,11 @@ EXPECT_TRUE( HasReceivedHeader(GetGoogleUrlWithPath(worker_path), "X-Client-Data")); - if (blink::ServiceWorkerUtils::IsImportedScriptUpdateCheckEnabled()) { - // And on import script requests to Google. - EXPECT_TRUE(HasReceivedHeader( - GetGoogleUrlWithPath("/service_worker/empty.js"), "X-Client-Data")); - // But not on requests not to Google. - EXPECT_FALSE(HasReceivedHeader(absolute_import, "X-Client-Data")); - } + // And on import script requests to Google. + EXPECT_TRUE(HasReceivedHeader( + GetGoogleUrlWithPath("/service_worker/empty.js"), "X-Client-Data")); + // But not on requests not to Google. + EXPECT_FALSE(HasReceivedHeader(absolute_import, "X-Client-Data")); } // Verify in an integration test that the variations header (X-Client-Data) is
diff --git a/chrome/browser/payments/abort_payment_handler_browsertest.cc b/chrome/browser/payments/abort_payment_handler_browsertest.cc index cc9d393..a6c8104 100644 --- a/chrome/browser/payments/abort_payment_handler_browsertest.cc +++ b/chrome/browser/payments/abort_payment_handler_browsertest.cc
@@ -15,8 +15,17 @@ class AbortPaymentHandlerTest : public PaymentRequestPlatformBrowserTestBase {}; +// TODO(crbug.com/1129578): fix flakiness and reenable +#if defined(OS_MAC) +#define MAYBE_CanAbortInvokedInstalledPaymentHandler \ + DISABLED_CanAbortInvokedInstalledPaymentHandler +#else +#define MAYBE_CanAbortInvokedInstalledPaymentHandler \ + CanAbortInvokedInstalledPaymentHandler +#endif + IN_PROC_BROWSER_TEST_F(AbortPaymentHandlerTest, - CanAbortInvokedInstalledPaymentHandler) { + MAYBE_CanAbortInvokedInstalledPaymentHandler) { std::string method_name = https_server()->GetURL("a.com", "/").spec(); method_name = method_name.substr(0, method_name.length() - 1); ASSERT_NE('/', method_name[method_name.length() - 1]);
diff --git a/chrome/browser/payments/load_and_remove_iframe_with_many_payment_requests_browsertest.cc b/chrome/browser/payments/load_and_remove_iframe_with_many_payment_requests_browsertest.cc index 79989330..f170cea 100644 --- a/chrome/browser/payments/load_and_remove_iframe_with_many_payment_requests_browsertest.cc +++ b/chrome/browser/payments/load_and_remove_iframe_with_many_payment_requests_browsertest.cc
@@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "build/build_config.h" #include "chrome/test/payments/payment_request_platform_browsertest_base.h" #include "content/public/test/browser_test.h" #include "content/public/test/browser_test_utils.h" @@ -10,7 +11,16 @@ namespace payments { namespace { -class LoadAndRemoveIframeWithManyPaymentRequestsTest +// TODO(crbug.com/1129573): fix flakiness and reenable +#if defined(OS_MAC) +#define MAYBE_LoadAndRemoveIframeWithManyPaymentRequestsTest \ + DISABLED_LoadAndRemoveIframeWithManyPaymentRequestsTest +#else +#define MAYBE_LoadAndRemoveIframeWithManyPaymentRequestsTest \ + LoadAndRemoveIframeWithManyPaymentRequestsTest +#endif + +class MAYBE_LoadAndRemoveIframeWithManyPaymentRequestsTest : public PaymentRequestPlatformBrowserTestBase { public: void RunTest(const std::string& iframe_hostname) { @@ -28,12 +38,12 @@ } }; -IN_PROC_BROWSER_TEST_F(LoadAndRemoveIframeWithManyPaymentRequestsTest, +IN_PROC_BROWSER_TEST_F(MAYBE_LoadAndRemoveIframeWithManyPaymentRequestsTest, CrossOriginNoCrash) { RunTest(/*iframe_hostname=*/"b.com"); } -IN_PROC_BROWSER_TEST_F(LoadAndRemoveIframeWithManyPaymentRequestsTest, +IN_PROC_BROWSER_TEST_F(MAYBE_LoadAndRemoveIframeWithManyPaymentRequestsTest, SameOriginNoCrash) { RunTest(/*iframe_hostname=*/"a.com"); }
diff --git a/chrome/browser/payments/secure_payment_confirmation_browsertest.cc b/chrome/browser/payments/secure_payment_confirmation_browsertest.cc index 8fd7d486..b452646 100644 --- a/chrome/browser/payments/secure_payment_confirmation_browsertest.cc +++ b/chrome/browser/payments/secure_payment_confirmation_browsertest.cc
@@ -15,6 +15,7 @@ #include "base/strings/strcat.h" #include "base/strings/string16.h" #include "base/strings/stringprintf.h" +#include "base/test/metrics/histogram_tester.h" #include "base/threading/thread_restrictions.h" #include "build/build_config.h" #include "chrome/browser/profiles/profile.h" @@ -344,6 +345,7 @@ #endif IN_PROC_BROWSER_TEST_F(SecurePaymentConfirmationCreationTest, MAYBE_CreatePaymentCredential) { + base::HistogramTester histogram_tester; ReplaceFidoDiscoveryFactory(); NavigateTo("a.com", "/payment_handler_status.html"); @@ -354,6 +356,10 @@ kCreatePaymentCredential}), &result)); EXPECT_EQ(result, "paymentCredential: OK"); + + // Verify that credential id size gets recorded. + histogram_tester.ExpectTotalCount( + "PaymentRequest.SecurePaymentConfirmationCredentialIdSizeInBytes", 1U); } IN_PROC_BROWSER_TEST_F(SecurePaymentConfirmationCreationTest,
diff --git a/chrome/browser/resources/bookmarks/BUILD.gn b/chrome/browser/resources/bookmarks/BUILD.gn index da850bf4..1f0cbc7 100644 --- a/chrome/browser/resources/bookmarks/BUILD.gn +++ b/chrome/browser/resources/bookmarks/BUILD.gn
@@ -4,51 +4,64 @@ import("//chrome/common/features.gni") import("//third_party/closure_compiler/compile_js.gni") -import("//tools/grit/grit_rule.gni") +import("//tools/grit/preprocess_grit.gni") import("//tools/polymer/html_to_js.gni") import("../optimize_webui.gni") if (optimize_webui) { - bookmarks_pak_file = "bookmarks_resources.pak" - unpak_folder = "bookmarks_resources.unpak" + preprocess_folder = "preprocessed" optimize_webui("build") { host = "bookmarks" - input = rebase_path("$target_gen_dir/$unpak_folder", root_build_dir) + input = rebase_path("$target_gen_dir/$preprocess_folder", root_build_dir) js_out_files = [ "bookmarks.rollup.js" ] js_module_in_files = [ "bookmarks.js" ] deps = [ - ":unpak", + ":preprocess", + ":preprocess_generated", "../../../../ui/webui/resources:preprocess", ] excludes = [ "chrome://resources/js/cr.m.js" ] } - unpak("unpak") { - pak_file = bookmarks_pak_file - out_folder = unpak_folder - - deps = [ ":flattened_resources" ] + preprocess_grit("preprocess") { + in_folder = "./" + out_folder = "$target_gen_dir/$preprocess_folder" + in_files = [ + "actions.js", + "api_listener.js", + "bookmarks.js", + "browser_proxy.js", + "constants.js", + "debouncer.js", + "dialog_focus_manager.js", + "dnd_manager.js", + "mouse_focus_behavior.js", + "reducers.js", + "store_client.js", + "store.js", + "types.js", + "util.js", + ] } - grit("flattened_resources") { - source = "bookmarks_resources.grd" - - grit_flags = [ - "-E", - "root_gen_dir=" + rebase_path(root_gen_dir, root_build_dir), - ] - + preprocess_grit("preprocess_generated") { deps = [ ":web_components" ] - defines = chrome_grit_defines - outputs = [ - "grit/bookmarks_resources.h", - "grit/bookmarks_resources_map.cc", - "grit/bookmarks_resources_map.h", - bookmarks_pak_file, + in_folder = target_gen_dir + out_folder = "$target_gen_dir/$preprocess_folder" + in_files = [ + "app.js", + "command_manager.js", + "edit_dialog.js", + "folder_node.js", + "item.js", + "list.js", + "router.js", + "shared_style.js", + "shared_vars.js", + "toolbar.js", ] - output_dir = "$root_gen_dir/chrome/browser/resources/bookmarks" } }
diff --git a/chrome/browser/resources/bookmarks/bookmarks_resources.grd b/chrome/browser/resources/bookmarks/bookmarks_resources.grd index 0b56db8..49e6c95 100644 --- a/chrome/browser/resources/bookmarks/bookmarks_resources.grd +++ b/chrome/browser/resources/bookmarks/bookmarks_resources.grd
@@ -14,95 +14,80 @@ <includes> <include name="IDR_BOOKMARKS_APP_JS" file="${root_gen_dir}/chrome/browser/resources/bookmarks/app.js" - use_base_dir="false" compress="false" type="BINDATA" preprocess="true" /> + use_base_dir="false" type="BINDATA" preprocess="true" /> <include name="IDR_BOOKMARKS_COMMAND_MANAGER_JS" file="${root_gen_dir}/chrome/browser/resources/bookmarks/command_manager.js" - use_base_dir="false" compress="false" type="BINDATA" /> + use_base_dir="false" type="BINDATA" /> <include name="IDR_BOOKMARKS_EDIT_DIALOG_JS" file="${root_gen_dir}/chrome/browser/resources/bookmarks/edit_dialog.js" - use_base_dir="false" compress="false" type="BINDATA" /> + use_base_dir="false" type="BINDATA" /> <include name="IDR_BOOKMARKS_FOLDER_NODE_JS" file="${root_gen_dir}/chrome/browser/resources/bookmarks/folder_node.js" - use_base_dir="false" compress="false" type="BINDATA" /> + use_base_dir="false" type="BINDATA" /> <include name="IDR_BOOKMARKS_ITEM_JS" file="${root_gen_dir}/chrome/browser/resources/bookmarks/item.js" - use_base_dir="false" compress="false" type="BINDATA" preprocess="true" /> + use_base_dir="false" type="BINDATA" preprocess="true" /> <include name="IDR_BOOKMARKS_LIST_JS" file="${root_gen_dir}/chrome/browser/resources/bookmarks/list.js" - use_base_dir="false" compress="false" type="BINDATA" /> + use_base_dir="false" type="BINDATA" /> <include name="IDR_BOOKMARKS_ROUTER_JS" file="${root_gen_dir}/chrome/browser/resources/bookmarks/router.js" - use_base_dir="false" compress="false" type="BINDATA" /> + use_base_dir="false" type="BINDATA" /> <include name="IDR_BOOKMARKS_SHARED_STYLE_JS" file="${root_gen_dir}/chrome/browser/resources/bookmarks/shared_style.js" - use_base_dir="false" compress="false" type="BINDATA" preprocess="true" /> + use_base_dir="false" type="BINDATA" preprocess="true" /> <include name="IDR_BOOKMARKS_SHARED_VARS_JS" file="${root_gen_dir}/chrome/browser/resources/bookmarks/shared_vars.js" - use_base_dir="false" compress="false" type="BINDATA" /> + use_base_dir="false" type="BINDATA" /> <include name="IDR_BOOKMARKS_TOOLBAR_JS" file="${root_gen_dir}/chrome/browser/resources/bookmarks/toolbar.js" - use_base_dir="false" compress="false" type="BINDATA" /> + use_base_dir="false" type="BINDATA" /> </includes> <structures> <structure name="IDR_BOOKMARKS_ACTIONS_JS" file="actions.js" - compress="false" type="chrome_html" /> <structure name="IDR_BOOKMARKS_API_LISTENER_JS" file="api_listener.js" - compress="false" type="chrome_html" /> <structure name="IDR_BOOKMARKS_BOOKMARKS_HTML" file="bookmarks.html" - compress="false" type="chrome_html" /> <structure name="IDR_BOOKMARKS_BOOKMARKS_JS" file="bookmarks.js" - compress="false" type="chrome_html" /> <structure name="IDR_BOOKMARKS_BROWSER_PROXY_JS" file="browser_proxy.js" - compress="false" type="chrome_html" /> <structure name="IDR_BOOKMARKS_CONSTANTS_JS" file="constants.js" - compress="false" type="chrome_html" /> <structure name="IDR_BOOKMARKS_DEBOUNCER_JS" file="debouncer.js" - compress="false" type="chrome_html" /> <structure name="IDR_BOOKMARKS_DIALOG_FOCUS_MANAGER_JS" file="dialog_focus_manager.js" - compress="false" type="chrome_html" /> <structure name="IDR_BOOKMARKS_DND_MANAGER_JS" file="dnd_manager.js" - compress="false" type="chrome_html" /> <structure name="IDR_BOOKMARKS_MOUSE_FOCUS_BEHAVIOR_JS" file="mouse_focus_behavior.js" - compress="false" type="chrome_html" /> <structure name="IDR_BOOKMARKS_REDUCERS_JS" file="reducers.js" - compress="false" type="chrome_html" /> <structure name="IDR_BOOKMARKS_STORE_CLIENT_JS" file="store_client.js" - compress="false" type="chrome_html" /> <structure name="IDR_BOOKMARKS_STORE_JS" file="store.js" - compress="false" type="chrome_html" /> <structure name="IDR_BOOKMARKS_TYPES_JS" file="types.js" - compress="false" type="chrome_html" /> <structure name="IDR_BOOKMARKS_UTIL_JS" file="util.js" - compress="false" type="chrome_html" /> </structures> </release>
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js index d909a77a..f295d8a6 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/background_test.js
@@ -3094,3 +3094,19 @@ .replay(); }); }); + +TEST_F('ChromeVoxBackgroundTest', 'VolumeChanges', function() { + const mockFeedback = this.createMockFeedback(); + this.runWithLoadedTree(``, function() { + const bounds = ChromeVoxState.instance.getFocusBounds(); + mockFeedback.call(press(KeyCode.VOLUME_UP)) + .expectSpeech('Volume', 'Slider', /\d+%/) + .call(() => { + // The bounds should not have changed. + assertEquals( + JSON.stringify(bounds), + JSON.stringify(ChromeVoxState.instance.getFocusBounds())); + }) + .replay(); + }); +});
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler.js index f00f31dd..f0f3cc4c 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/desktop_automation_handler.js
@@ -428,6 +428,7 @@ this.lastValueChanged_ = new Date(); const output = new Output(); + output.withoutFocusRing(); if (fromDesktop && (!this.lastValueTarget_ || this.lastValueTarget_ !== t)) {
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output.js index 4515fce..692e571 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output.js
@@ -117,6 +117,9 @@ /** @private {!Object} */ this.initialSpeechProps_ = {}; + + /** @private {boolean} */ + this.drawFocusRing_ = true; } /** @@ -365,6 +368,15 @@ } /** + * Don't draw a focus ring based on this output. + * @return {!Output} + */ + withoutFocusRing() { + this.drawFocusRing_ = false; + return this; + } + + /** * Supply initial speech properties that will be applied to all output. * @param {!Object} speechProps * @return {!Output} @@ -561,7 +573,7 @@ } // Display. - if (this.speechCategory_ != TtsCategory.LIVE) { + if (this.speechCategory_ != TtsCategory.LIVE && this.drawFocusRing_) { ChromeVoxState.instance.setFocusBounds(this.locations_); } }
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output_test.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output_test.js index 5f368e192..82053cd 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/output_test.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/output_test.js
@@ -1335,3 +1335,27 @@ o.speechOutputForTest); }); }); + +TEST_F('ChromeVoxOutputE2ETest', 'WithoutFocusRing', function() { + const site = `<button></button>`; + this.runWithLoadedTree(site, function(root) { + let called = false; + ChromeVoxState.instance.setFocusBounds = this.newCallback(() => { + called = true; + }); + + const button = root.find({role: RoleType.BUTTON}); + + // Triggers drawing of the focus ring. + new Output().withSpeech(cursors.Range.fromNode(button)).go(); + assertTrue(called); + called = false; + + // Does not trigger drawing of the focus ring. + new Output() + .withSpeech(cursors.Range.fromNode(button)) + .withoutFocusRing() + .go(); + assertFalse(called); + }); +});
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/background/prefs.js b/chrome/browser/resources/chromeos/accessibility/chromevox/background/prefs.js index 697a45a..a1079aa 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/background/prefs.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/background/prefs.js
@@ -70,15 +70,6 @@ localStorage[pref] = ChromeVoxPrefs.DEFAULT_PREFS[pref]; } } - // Since language switching is currently an experimental feature, ensure - // that it is off if the feature flag is absent. - chrome.commandLinePrivate.hasSwitch( - 'disable-experimental-accessibility-chromevox-language-switching', - (enabled) => { - if (enabled) { - localStorage['languageSwitching'] = false; - } - }); } /**
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/common/abstract_tts.js b/chrome/browser/resources/chromeos/accessibility/chromevox/common/abstract_tts.js index 6b642a9..f7fa14f 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/common/abstract_tts.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/common/abstract_tts.js
@@ -557,3 +557,6 @@ */ AbstractTts.repetitionRegexp_ = /([-\/\\|!@#$%^&*\(\)=_+\[\]\{\}.?;'":<>\u2022])\1{2,}/g; + +/** TTS phonetic-characters property. @type {string} */ +AbstractTts.PHONETIC_CHARACTERS = 'phoneticCharacters';
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/common/tts_background.js b/chrome/browser/resources/chromeos/accessibility/chromevox/common/tts_background.js index 537358e..4b5bdd4 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/common/tts_background.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/common/tts_background.js
@@ -737,7 +737,7 @@ } // Only pronounce phonetic hints when explicitly requested. - if (!properties['phoneticCharacters']) { + if (!properties[AbstractTts.PHONETIC_CHARACTERS]) { return; }
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/options/options.js b/chrome/browser/resources/chromeos/accessibility/chromevox/options/options.js index 5900832d..9d3b75b 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/options/options.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/options/options.js
@@ -116,14 +116,6 @@ } } - chrome.commandLinePrivate.hasSwitch( - 'disable-experimental-accessibility-chromevox-language-switching', - (enabled) => { - if (enabled) { - $('languageSwitchingOption').hidden = true; - } - }); - $('toggleEventStreamFilters').addEventListener('click', function(evt) { if ($('eventStreamFilters').hidden) { $('eventStreamFilters').hidden = false;
diff --git a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.js b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.js index 2716485..efe5ba85 100644 --- a/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.js +++ b/chrome/browser/resources/chromeos/accessibility/chromevox/panel/panel.js
@@ -181,15 +181,6 @@ Panel.ownerWindow = window; /** @private {boolean} */ - Panel.menuSearchEnabled_ = true; - - chrome.commandLinePrivate.hasSwitch( - 'disable-experimental-accessibility-chromevox-search-menus', - (enabled) => { - Panel.menuSearchEnabled_ = !enabled; - }); - - /** @private {boolean} */ Panel.iTutorialEnabled_ = false; chrome.commandLinePrivate.hasSwitch( 'enable-experimental-accessibility-chromevox-tutorial', (enabled) => { @@ -368,9 +359,7 @@ Panel.pendingCallback_ = null; // Build the top-level menus. - const searchMenu = (Panel.menuSearchEnabled_) ? - Panel.addSearchMenu('panel_search_menu') : - null; + const searchMenu = Panel.addSearchMenu('panel_search_menu'); const jumpMenu = Panel.addMenu('panel_menu_jump'); const speechMenu = Panel.addMenu('panel_menu_speech'); const tabsMenu = Panel.addMenu('panel_menu_tabs');
diff --git a/chrome/browser/resources/chromeos/multidevice_internals/browser_tabs_metadata_form.html b/chrome/browser/resources/chromeos/multidevice_internals/browser_tabs_metadata_form.html index ee75acd..fdb17ea 100644 --- a/chrome/browser/resources/chromeos/multidevice_internals/browser_tabs_metadata_form.html +++ b/chrome/browser/resources/chromeos/multidevice_internals/browser_tabs_metadata_form.html
@@ -4,6 +4,14 @@ flex: 1 0 100%; } + :host(:not([is-valid])) { + background-color: LightGray; + } + + :host([is-valid]) { + background-color: LightGreen; + } + #faviconSelector { align-items: normal; } @@ -11,12 +19,12 @@ <div class="column"> <cr-input value="{{url_}}" id="urlInput" auto-validate required label="URL: " - error-message="This browser tab will be a nullopt if no url input"> + error-message="This row is invalid"> </cr-input> </div> <div class="column"> <cr-input value="{{title_}}" label="Title:" auto-validate required - error-message="This browser tab will be a nullopt if no title input"> + error-message="This row is invalid"> </cr-input> </div> <div class="column">
diff --git a/chrome/browser/resources/chromeos/multidevice_internals/browser_tabs_metadata_form.js b/chrome/browser/resources/chromeos/multidevice_internals/browser_tabs_metadata_form.js index 0565087..c6f96bd 100644 --- a/chrome/browser/resources/chromeos/multidevice_internals/browser_tabs_metadata_form.js +++ b/chrome/browser/resources/chromeos/multidevice_internals/browser_tabs_metadata_form.js
@@ -20,7 +20,15 @@ browserTabMetadata: { type: Object, notify: true, - computed: 'getMetadata_(url_, title_, lastAccessedTimeStamp_, favicon_)', + computed: 'getMetadata_(isValid, url_, title_, lastAccessedTimeStamp_, ' + + 'favicon_)', + }, + + /** True if the fields are all filled out. */ + isValid: { + type: Boolean, + computed: 'getIsValid_(url_, title_, lastAccessedTimeStamp_, favicon_)', + reflectToAttribute: true, }, /** @private */ @@ -81,11 +89,21 @@ }, /** + * @return{boolean} + * @private + */ + getIsValid_() { + return !!this.url_ && !!this.title_ && this.lastAccessedTimeStamp_ > -1 && + this.favicon_ !== ImageType.NONE; + }, + + /** * @return{BrowserTabsMetadataModel} * @private */ getMetadata_() { return { + isValid: this.isValid, url: this.url_, title: this.title_, lastAccessedTimeStamp: this.lastAccessedTimeStamp_,
diff --git a/chrome/browser/resources/chromeos/multidevice_internals/browser_tabs_model_form.html b/chrome/browser/resources/chromeos/multidevice_internals/browser_tabs_model_form.html index d8893a95..c7af229 100644 --- a/chrome/browser/resources/chromeos/multidevice_internals/browser_tabs_model_form.html +++ b/chrome/browser/resources/chromeos/multidevice_internals/browser_tabs_model_form.html
@@ -1,23 +1,31 @@ <style include="cr-shared-style shared-style"> :host { display: flex; - height: 250px; + height: 500px; } #fieldColumn { flex: 2; } + + .updates { + font-size: 15px; + } </style> <div class="column"> + <div class="emphasize updates" hidden="[[!isTabSyncEnabled_]]"> + [[nValidTabs_]] valid metadatas + </div> <cr-button on-click="setFakeBrowserTabModel_" class="internals-button"> <span class="emphasize">Change Browser Tabs Status</span> </cr-button> <div class="label"> - <span class="emphasize">Note:</span> Click the button above to propagate all - browser tab status values on the right hand side to the fake phonehub - manager. Note that if a field is empty, then that corresponding tab will - be nullopt. + <span class="emphasize">Note:</span> Click the button above to propagate + <span class="emphasize">VALID (green)</span> browser tab status values on + the right hand side to the fake phonehub manager. Note that if a field is + empty or favicon is set to NONE, then that corresponding tab will not be + sent, and the background will be grey. </div> </div> <div class="column" id="fieldColumn"> @@ -39,6 +47,18 @@ browser-tab-metadata="{{browserTabTwoMetadata_}}"> </browser-tabs-metadata-form> </div> + <div class="cr-row"> + <div class="column">3: </div> + <browser-tabs-metadata-form + browser-tab-metadata="{{browserTabThreeMetadata_}}"> + </browser-tabs-metadata-form> + </div> + <div class="cr-row"> + <div class="column">4: </div> + <browser-tabs-metadata-form + browser-tab-metadata="{{browserTabFourMetadata_}}"> + </browser-tabs-metadata-form> + </div> </template> </div>
diff --git a/chrome/browser/resources/chromeos/multidevice_internals/browser_tabs_model_form.js b/chrome/browser/resources/chromeos/multidevice_internals/browser_tabs_model_form.js index eaf0a04..d54e4ae 100644 --- a/chrome/browser/resources/chromeos/multidevice_internals/browser_tabs_model_form.js +++ b/chrome/browser/resources/chromeos/multidevice_internals/browser_tabs_model_form.js
@@ -33,6 +33,25 @@ browserTabTwoMetadata_: { type: Object, }, + + /** @private{BrowserTabsMetadataModel} */ + browserTabThreeMetadata_: { + type: Object, + }, + + /** @private{BrowserTabsMetadataModel} */ + browserTabFourMetadata_: { + type: Object, + }, + + /** @type{number} */ + nValidTabs_: { + type: Number, + computed: + 'computeNValidTabs_(isTabSyncEnabled_, browserTabOneMetadata_, ' + + 'browserTabTwoMetadata_, browserTabThreeMetadata_, ' + + 'browserTabFourMetadata_)', + }, }, /** @private{?MultidevicePhoneHubBrowserProxy}*/ @@ -43,14 +62,51 @@ this.browserProxy_ = MultidevicePhoneHubBrowserProxy.getInstance(); }, + /** + * @return {Array<!BrowserTabsMetadataModel>} + * @private + */ + getAllBrowserTabMetadatas_() { + return [ + this.browserTabOneMetadata_, this.browserTabTwoMetadata_, + this.browserTabThreeMetadata_, this.browserTabFourMetadata_ + ]; + }, + + /** + * @return {number} + * @private + */ + computeNValidTabs_() { + if (!this.isTabSyncEnabled_) { + return 0; + } + + return this.getAllBrowserTabMetadatas_().reduce((acc, metadata) => { + return acc + (!!metadata && metadata.isValid); + }, 0); + }, + /** @private */ setFakeBrowserTabModel_() { + if (!this.isTabSyncEnabled_) { + const syncDisabledBrowserTabsModel = { + isTabSyncEnabled: false, + browserTabOneMetadata: null, + browserTabTwoMetadata: null, + browserTabThreeMetadata: null, + browserTabFourMetadata: null, + }; + this.browserProxy_.setBrowserTabs(syncDisabledBrowserTabsModel); + return; + } + const browserTabsModel = { isTabSyncEnabled: this.isTabSyncEnabled_, - browserTabOneMetadata: - this.isTabSyncEnabled_ ? this.browserTabOneMetadata_ : null, - browserTabTwoMetadata: - this.isTabSyncEnabled_ ? this.browserTabTwoMetadata_ : null, + browserTabOneMetadata: this.browserTabOneMetadata_, + browserTabTwoMetadata: this.browserTabTwoMetadata_, + browserTabThreeMetadata: this.browserTabThreeMetadata_, + browserTabFourMetadata: this.browserTabFourMetadata_, }; this.browserProxy_.setBrowserTabs(browserTabsModel); },
diff --git a/chrome/browser/resources/chromeos/multidevice_internals/types.js b/chrome/browser/resources/chromeos/multidevice_internals/types.js index 535c945..3164a226 100644 --- a/chrome/browser/resources/chromeos/multidevice_internals/types.js +++ b/chrome/browser/resources/chromeos/multidevice_internals/types.js
@@ -129,6 +129,7 @@ /** * @typedef {{ + * isValid: boolean, * url: string, * title: string, * lastAccessedTimeStamp: number, @@ -142,6 +143,8 @@ * isTabSyncEnabled: boolean, * browserTabOneMetadata: ?BrowserTabsMetadataModel, * browserTabTwoMetadata: ?BrowserTabsMetadataModel, + * browserTabThreeMetadata: ?BrowserTabsMetadataModel, + * browserTabFourMetadata: ?BrowserTabsMetadataModel, * }} */ export let BrowserTabsModel;
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/multidevice_page/BUILD.gn index b16515c9..ddd9ba9 100644 --- a/chrome/browser/resources/settings/chromeos/multidevice_page/BUILD.gn +++ b/chrome/browser/resources/settings/chromeos/multidevice_page/BUILD.gn
@@ -47,6 +47,7 @@ "..:route_origin_behavior", "../..:router", "../localized_link:localized_link", + "//ui/webui/resources/js:assert", "//ui/webui/resources/js:cr", ] } @@ -183,6 +184,7 @@ "../..:router.m", "../localized_link:localized_link.m", "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled", + "//ui/webui/resources/js:assert.m", ] extra_deps = [ ":multidevice_feature_item_module" ] } @@ -300,7 +302,8 @@ html_type = "dom-module" migrated_imports = os_settings_migrated_imports namespace_rewrites = os_settings_namespace_rewrites - auto_imports = os_settings_auto_imports + auto_imports = os_settings_auto_imports + + [ "ui/webui/resources/html/assert.html|assert" ] } polymer_modulizer("multidevice_feature_toggle") {
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_item.html b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_item.html index 45c5fe6..5cb65c7 100644 --- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_item.html +++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_item.html
@@ -2,6 +2,7 @@ <link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html"> <link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html"> +<link rel="import" href="chrome://resources/html/assert.html"> <link rel="import" href="chrome://resources/html/cr.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html"> <link rel="import" href="../os_route.html">
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_item.js b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_item.js index aeda5d4..8164ff74 100644 --- a/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_item.js +++ b/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_feature_item.js
@@ -42,6 +42,16 @@ this.addFocusConfig_(this.subpageRoute, '#subpageButton'); }, + /** @override */ + focus() { + const slot = this.$$('slot[name="feature-controller"]'); + const elems = slot.assignedElements({flatten: true}); + assert(elems.length > 0); + // Elems contains any elements that override the feature controller. If none + // exist, contains the default toggle elem. + elems[0].focus(); + }, + /** * @return {boolean} * @private
diff --git a/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages_page_v2.html b/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages_page_v2.html index 860fcfd..69e25ca 100644 --- a/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages_page_v2.html +++ b/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages_page_v2.html
@@ -8,6 +8,7 @@ <link rel="import" href="chrome://resources/html/cr.html"> <link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html"> <link rel="import" href="chrome://resources/html/i18n_behavior.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html"> <link rel="import" href="chrome://resources/cr_elements/cr_action_menu/cr_action_menu.html"> <link rel="import" href="chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.html"> @@ -26,7 +27,7 @@ <dom-module id="os-settings-languages-page-v2"> <template> - <style include="settings-shared action-link"> + <style include="settings-shared action-link iron-flex"> h2 { padding-inline-start: var(--cr-section-padding); } @@ -39,40 +40,43 @@ margin-bottom: var(--cr-section-vertical-margin); } + #addLanguages { + margin-top: 16px; + } + #addLanguagesIcon { --iron-icon-fill-color: var(--cr-link-color); - margin-inline-end: 4px; + margin-inline-end: 12px; } </style> <div route-path="default"> - <h2 aria-hidden="true">$i18n{systemLanguageTitle}</h2> - <span class="settings-box first" aria-hidden="true"> - $i18n{systemLanguageDescription} - </span> - <div class="settings-box continuation embedded bottom-margin"> - <div class="start" aria-hidden="true"> - [[getLanguageDisplayName_(languages.prospectiveUILanguage)]] + <div class="cr-row first"> + <div class="flex cr-padded-text" aria-hidden="true"> + $i18n{deviceLanguageTitle} + <div class="secondary"> + [[getLanguageDisplayName_(languages.prospectiveUILanguage)]] + </div> </div> - <cr-button id="changeSystemLanguage" - on-click="onChangeSystemLanguageClick_" - aria-label="[[getChangeSystemLanguageButtonDescription_( + <cr-button id="changeDeviceLanguage" + on-click="onChangeDeviceLanguageClick_" + aria-label="[[getChangeDeviceLanguageButtonDescription_( languages.prospectiveUILanguage)]]" - deep-link-focus-id$="[[Setting.kChangeSystemLanguage]]"> - $i18n{changeSystemLanguageLabel} + deep-link-focus-id$="[[Setting.kChangeDeviceLanguage]]"> + $i18n{changeDeviceLanguageLabel} </cr-button> </div> - <div class="hr"> + <div class="hr bottom-margin"> <h2 aria-hidden="true">$i18n{languagesPreferenceTitle}</h2> - <settings-localized-link class="settings-box first" + <settings-localized-link class="cr-row first bottom-margin" localized-string="[[i18nAdvanced( 'languagesPreferenceDescription')]]"> </settings-localized-link> <div class="list-frame vertical-list" id="languagesList"> <template is="dom-repeat" items="[[languages.enabled]]"> <div class="list-item"> - <div class="start" id="displayText-[[index]]" + <div class="flex" id="displayText-[[index]]" aria-hidden="true"> <div title="[[item.language.nativeDisplayName]]"> [[item.language.displayName]]
diff --git a/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages_page_v2.js b/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages_page_v2.js index 9796ef5..9d347e5 100644 --- a/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages_page_v2.js +++ b/chrome/browser/resources/settings/chromeos/os_languages_page/os_languages_page_v2.js
@@ -63,7 +63,7 @@ type: Object, value: () => new Set([ chromeos.settings.mojom.Setting.kAddLanguage, - chromeos.settings.mojom.Setting.kChangeSystemLanguage, + chromeos.settings.mojom.Setting.kChangeDeviceLanguage, chromeos.settings.mojom.Setting.kOfferTranslation, ]), }, @@ -101,14 +101,14 @@ }, /** @private */ - onChangeSystemLanguageClick_() { + onChangeDeviceLanguageClick_() { this.showChangeDeviceLanguageDialog_ = true; }, /** @private */ onChangeDeviceLanguageDialogClose_() { this.showChangeDeviceLanguageDialog_ = false; - cr.ui.focusWithoutInk(assert(this.$.changeSystemLanguage)); + cr.ui.focusWithoutInk(assert(this.$.changeDeviceLanguage)); }, /** @@ -116,9 +116,9 @@ * @return {string} * @private */ - getChangeSystemLanguageButtonDescription_(language) { + getChangeDeviceLanguageButtonDescription_(language) { return this.i18n( - 'changeSystemLanguageButtonDescription', + 'changeDeviceLanguageButtonDescription', this.getLanguageDisplayName_(language)); },
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service.h b/chrome/browser/safe_browsing/chrome_password_protection_service.h index dff6e0e..97b84cd0 100644 --- a/chrome/browser/safe_browsing/chrome_password_protection_service.h +++ b/chrome/browser/safe_browsing/chrome_password_protection_service.h
@@ -246,9 +246,9 @@ // Returns the profile PasswordStore associated with this instance. password_manager::PasswordStore* GetProfilePasswordStore() const; - // Returns the account PasswordStore associated with this instance. The - // account password store contains passwords stored in the account and is - // accessible only when the user is signed in. + // Returns the GAIA-account-scoped PasswordStore associated with this + // instance. The account password store contains passwords stored in the + // account and is accessible only when the user is signed in and non syncing. password_manager::PasswordStore* GetAccountPasswordStore() const; // Gets the type of sync account associated with current profile or
diff --git a/chrome/browser/safe_browsing/client_side_detection_host.cc b/chrome/browser/safe_browsing/client_side_detection_host.cc index 61ab8ca..b8c9f3d 100644 --- a/chrome/browser/safe_browsing/client_side_detection_host.cc +++ b/chrome/browser/safe_browsing/client_side_detection_host.cc
@@ -389,7 +389,7 @@ void ClientSideDetectionHost::SendModelToRenderFrame() { DCHECK_CURRENTLY_ON(BrowserThread::UI); - if (!web_contents() || web_contents() != tab_) + if (!web_contents() || web_contents() != tab_ || !csd_service_) return; for (content::RenderFrameHost* frame : web_contents()->GetAllFrames()) { @@ -435,8 +435,8 @@ } // Cancel all pending feature extractions. feature_extractor_.reset(); - - csd_service_->RemoveClientSideDetectionHost(this); + if (csd_service_) + csd_service_->RemoveClientSideDetectionHost(this); } void ClientSideDetectionHost::RenderFrameCreated(
diff --git a/chrome/browser/safe_browsing/client_side_detection_service_factory.cc b/chrome/browser/safe_browsing/client_side_detection_service_factory.cc index d1aba1d..ed2b9b13 100644 --- a/chrome/browser/safe_browsing/client_side_detection_service_factory.cc +++ b/chrome/browser/safe_browsing/client_side_detection_service_factory.cc
@@ -10,6 +10,8 @@ #include "chrome/browser/safe_browsing/client_side_detection_service.h" #include "chrome/common/chrome_switches.h" #include "components/keyed_service/content/browser_context_dependency_manager.h" +#include "components/safe_browsing/buildflags.h" +#include "components/safe_browsing/core/features.h" #include "services/network/public/cpp/shared_url_loader_factory.h" namespace safe_browsing { @@ -40,6 +42,16 @@ KeyedService* ClientSideDetectionServiceFactory::BuildServiceInstanceFor( content::BrowserContext* context) const { + bool client_side_detection_enabled = +#if BUILDFLAG(FULL_SAFE_BROWSING) + true; +#else + base::FeatureList::IsEnabled( + safe_browsing::kClientSideDetectionForAndroid); +#endif + if (!client_side_detection_enabled) + return nullptr; + Profile* profile = Profile::FromBrowserContext(context); return new ClientSideDetectionService(profile); }
diff --git a/chrome/browser/safe_browsing/client_side_model_loader.cc b/chrome/browser/safe_browsing/client_side_model_loader.cc index e561529..8be7f7d 100644 --- a/chrome/browser/safe_browsing/client_side_model_loader.cc +++ b/chrome/browser/safe_browsing/client_side_model_loader.cc
@@ -20,6 +20,7 @@ #include "base/task/thread_pool.h" #include "base/threading/sequenced_task_runner_handle.h" #include "base/time/time.h" +#include "components/safe_browsing/buildflags.h" #include "components/safe_browsing/core/db/v4_protocol_manager_util.h" #include "components/safe_browsing/core/proto/client_model.pb.h" #include "components/safe_browsing/core/proto/csd.pb.h" @@ -62,7 +63,11 @@ const char ModelLoader::kClientModelNamePattern[] = "client_model_v5%s_variation_%d.pb"; const char ModelLoader::kClientModelFinchExperiment[] = +#if BUILDFLAG(FULL_SAFE_BROWSING) "ClientSideDetectionModel"; +#else + "ClientSideDetectionModelOnAndroid"; +#endif const char ModelLoader::kClientModelFinchParam[] = "ModelNum"; const char kUmaModelDownloadResponseMetricName[] =
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc index 1db9d02..7de4724 100644 --- a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc +++ b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc
@@ -106,6 +106,73 @@ return base::nullopt; } +net::NetworkTrafficAnnotationTag GetTrafficAnnotationTag(bool is_app) { + if (is_app) { + return net::DefineNetworkTrafficAnnotation( + "safe_browsing_binary_upload_app", R"( + semantics { + sender: "Advanced Protection Program" + description: + "For users part of Google's Advanced Protection Program, when a " + "file is downloaded, Chrome will upload that file to Safe Browsing " + "for detailed scanning." + trigger: + "The browser will upload the file to Google when the user " + "downloads a file, and the browser is enrolled into the " + "Advanced Protection Program." + data: + "The downloaded file." + destination: GOOGLE_OWNED_SERVICE + } + policy { + cookies_allowed: YES + cookies_store: "Safe Browsing Cookie Store" + setting: "This is disabled by default an can only be enabled by " + "policy." + chrome_policy { + AdvancedProtectionAllowed { + AdvancedProtectionAllowed: false + } + } + } + )"); + } else { + return net::DefineNetworkTrafficAnnotation( + "safe_browsing_binary_upload_connector", R"( + semantics { + sender: "Chrome Enterprise Connectors" + description: + "For users with content analysis Chrome Enterprise Connectors " + "enabled, Chrome will upload the data corresponding to the " + "Connector for scanning." + trigger: + "If the OnFileAttachedEnterpriseConnector, " + "OnFileDownloadedEnterpriseConnector or " + "OnBulkDataEntryEnterpriseConnector policy is set, a request is made to " + "scan a file attached to Chrome, a file downloaded by Chrome or " + "data pasted in Chrome respectively." + data: + "The uploaded or downloaded file, or pasted data." + destination: GOOGLE_OWNED_SERVICE + } + policy { + cookies_allowed: YES + cookies_store: "Safe Browsing Cookie Store" + setting: "This is disabled by default an can only be enabled by " + "policy." + chrome_policy { + OnFileAttachedEnterpriseConnector { + } + OnFileDownloadedEnterpriseConnector { + } + OnBulkDataEntryEnterpriseConnector { + } + } + } + )"); + } +} + } // namespace BinaryUploadService::BinaryUploadService(Profile* profile) @@ -247,44 +314,6 @@ return; } - net::NetworkTrafficAnnotationTag traffic_annotation = - net::DefineNetworkTrafficAnnotation("safe_browsing_binary_upload", R"( - semantics { - sender: "Safe Browsing Download Protection" - description: - "For users with the enterprise policy " - "SendFilesForMalwareCheck set, when a file is " - "downloaded, Chrome will upload that file to Safe Browsing for " - "detailed scanning." - trigger: - "The browser will upload the file to Google when " - "the user downloads a file, and the enterprise policy " - "SendFilesForMalwareCheck is set." - data: - "The downloaded file." - destination: GOOGLE_OWNED_SERVICE - } - policy { - cookies_allowed: YES - cookies_store: "Safe Browsing Cookie Store" - setting: "This is disabled by default an can only be enabled by " - "policy." - chrome_policy { - SendFilesForMalwareCheck { - SendFilesForMalwareCheck: 0 - } - } - chrome_policy { - SendFilesForMalwareCheck { - SendFilesForMalwareCheck: 1 - } - } - } - comments: "Setting SendFilesForMalwareCheck to 0 (Do not scan " - "downloads) or 1 (Forbid the scanning of downloads) will disable " - "this feature" - )"); - std::string metadata; request->SerializeToString(&metadata); base::Base64Encode(metadata, &metadata); @@ -294,7 +323,7 @@ url = GetUploadUrl(IsAdvancedProtectionRequest(*request)); auto upload_request = MultipartUploadRequest::Create( url_loader_factory_, std::move(url), metadata, data.contents, - traffic_annotation, + GetTrafficAnnotationTag(IsAdvancedProtectionRequest(*request)), base::BindOnce(&BinaryUploadService::OnUploadComplete, weakptr_factory_.GetWeakPtr(), request));
diff --git a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorTabObserver.java b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorTabObserver.java index 86220ab..64f1f1a 100644 --- a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorTabObserver.java +++ b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabModelSelectorTabObserver.java
@@ -137,7 +137,9 @@ for (int j = 0; j < comprehensiveTabList.getCount(); j++) { Tab tab = comprehensiveTabList.getTabAt(j); tab.removeObserver(this); - CriticalPersistedTabData.from(tab).removeObserver(this); + if (tab.isInitialized()) { + CriticalPersistedTabData.from(tab).removeObserver(this); + } onTabUnregistered(tab); } }
diff --git a/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_phone_hub_handler.cc b/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_phone_hub_handler.cc index 3768366..0651640d 100644 --- a/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_phone_hub_handler.cc +++ b/chrome/browser/ui/webui/chromeos/multidevice_internals/multidevice_internals_phone_hub_handler.cc
@@ -71,38 +71,46 @@ icon); } -base::Optional<phonehub::BrowserTabsModel::BrowserTabMetadata> -DictToBrowserTabMetadataModel( - const base::DictionaryValue* browser_tab_metadata) { +void TryAddingMetadata( + const std::string& key, + const base::DictionaryValue* browser_tab_status_dict, + std::vector<phonehub::BrowserTabsModel::BrowserTabMetadata>& metadatas) { + const base::DictionaryValue* browser_tab_metadata = nullptr; + + if (!browser_tab_status_dict->GetDictionary(key, &browser_tab_metadata)) + return; + std::string url; - if (!browser_tab_metadata->GetString("url", &url) || url.empty()) { - return base::nullopt; - } + if (!browser_tab_metadata->GetString("url", &url) || url.empty()) + return; base::string16 title; - if (!browser_tab_metadata->GetString("title", &title) || title.empty()) { - return base::nullopt; - } + if (!browser_tab_metadata->GetString("title", &title) || title.empty()) + return; // JavaScript time stamps don't fit in int. double last_accessed_timestamp; if (!browser_tab_metadata->GetDouble("lastAccessedTimeStamp", &last_accessed_timestamp)) { - return base::nullopt; + return; } int favicon_image_type_as_int; if (!browser_tab_metadata->GetInteger("favicon", - &favicon_image_type_as_int)) { - return base::nullopt; + &favicon_image_type_as_int) || + !favicon_image_type_as_int) { + return; } auto favicon_image_type = static_cast<ImageType>(favicon_image_type_as_int); gfx::Image favicon = gfx::Image::CreateFrom1xBitmap(ImageTypeToBitmap(favicon_image_type)); - return phonehub::BrowserTabsModel::BrowserTabMetadata( + + auto metadata = phonehub::BrowserTabsModel::BrowserTabMetadata( GURL(url), title, base::Time::FromJsTime(last_accessed_timestamp), favicon); + + metadatas.push_back(metadata); } } // namespace @@ -234,9 +242,6 @@ const base::DictionaryValue* phones_status_dict = nullptr; CHECK(args->GetDictionary(0, &phones_status_dict)); - base::string16 phone_name; - CHECK(phones_status_dict->GetString("phoneName", &phone_name)); - int mobile_status_as_int; CHECK(phones_status_dict->GetInteger("mobileStatus", &mobile_status_as_int)); auto mobile_status = static_cast<phonehub::PhoneStatusModel::MobileStatus>( @@ -303,34 +308,28 @@ return; } - base::Optional<phonehub::BrowserTabsModel::BrowserTabMetadata> metadata_one; - const base::DictionaryValue* browser_tab_one_metadata = nullptr; - if (browser_tab_status_dict->GetDictionary("browserTabOneMetadata", - &browser_tab_one_metadata)) { - metadata_one = DictToBrowserTabMetadataModel(browser_tab_one_metadata); - } - - base::Optional<phonehub::BrowserTabsModel::BrowserTabMetadata> metadata_two; - const base::DictionaryValue* browser_tab_two_metadata = nullptr; - if (browser_tab_status_dict->GetDictionary("browserTabTwoMetadata", - &browser_tab_two_metadata)) { - metadata_two = DictToBrowserTabMetadataModel(browser_tab_two_metadata); - } - - // TODO(hsuregan): Add metadata_three and metadata_four. - std::vector<base::Optional<phonehub::BrowserTabsModel::BrowserTabMetadata>> - metadatas{{metadata_one, metadata_two}}; - std::sort(metadatas.begin(), metadatas.end()); + std::vector<phonehub::BrowserTabsModel::BrowserTabMetadata> metadatas; + TryAddingMetadata("browserTabOneMetadata", browser_tab_status_dict, + metadatas); + TryAddingMetadata("browserTabTwoMetadata", browser_tab_status_dict, + metadatas); + TryAddingMetadata("browserTabThreeMetadata", browser_tab_status_dict, + metadatas); + TryAddingMetadata("browserTabFourMetadata", browser_tab_status_dict, + metadatas); fake_phone_hub_manager_->mutable_phone_model()->SetBrowserTabsModel( - phonehub::BrowserTabsModel(is_tab_sync_enabled, metadatas[1], - metadatas[0])); + phonehub::BrowserTabsModel(is_tab_sync_enabled, metadatas)); - if (metadatas[1].has_value()) - PA_LOG(VERBOSE) << "Set most recent browser tab to" << *metadatas[1]; + auto browser_tabs_model = + fake_phone_hub_manager_->mutable_phone_model()->browser_tabs_model(); + CHECK(browser_tabs_model.has_value()); - if (metadatas[0].has_value()) - PA_LOG(VERBOSE) << "Set second most recent browser tab to" << *metadatas[0]; + // Log the most recently visited browser tab (at index 0) last. + for (int i = metadatas.size() - 1; i > -1; --i) { + PA_LOG(VERBOSE) << "Set most recent browser tab number " << i + << " to: " << browser_tabs_model->most_recent_tabs()[i]; + } } void MultidevicePhoneHubHandler::HandleSetNotification(
diff --git a/chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom b/chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom index febf0fb1..f0842439 100644 --- a/chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom +++ b/chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom
@@ -170,7 +170,7 @@ kShowInputOptionsInShelf = 1201, kShowPersonalInformationSuggestions = 1202, kShowEmojiSuggestions = 1203, - kChangeSystemLanguage = 1204, + kChangeDeviceLanguage = 1204, kOfferTranslation = 1205, kAddInputMethod = 1206, kSpellCheck = 1207,
diff --git a/chrome/browser/ui/webui/settings/chromeos/crostini_handler.cc b/chrome/browser/ui/webui/settings/chromeos/crostini_handler.cc index 72c9b266..550cb7e 100644 --- a/chrome/browser/ui/webui/settings/chromeos/crostini_handler.cc +++ b/chrome/browser/ui/webui/settings/chromeos/crostini_handler.cc
@@ -288,13 +288,12 @@ base::ListValue UsbDevicesToListValue( const std::vector<CrosUsbDeviceInfo> shared_usbs) { base::ListValue usb_devices_list; - for (auto device : shared_usbs) { + for (auto& device : shared_usbs) { base::Value device_info(base::Value::Type::DICTIONARY); - device_info.SetKey("guid", base::Value(device.guid)); - device_info.SetKey("label", base::Value(device.label)); - const bool shared_in_crostini = - device.vm_sharing_info[crostini::kCrostiniDefaultVmName].shared; - device_info.SetKey("shared", base::Value(shared_in_crostini)); + device_info.SetStringKey("guid", device.guid); + device_info.SetStringKey("label", device.label); + device_info.SetBoolKey( + "shared", device.shared_vm_name == crostini::kCrostiniDefaultVmName); usb_devices_list.Append(std::move(device_info)); } return usb_devices_list;
diff --git a/chrome/browser/ui/webui/settings/chromeos/languages_section.cc b/chrome/browser/ui/webui/settings/chromeos/languages_section.cc index 07867b4..10c9e45 100644 --- a/chrome/browser/ui/webui/settings/chromeos/languages_section.cc +++ b/chrome/browser/ui/webui/settings/chromeos/languages_section.cc
@@ -66,12 +66,12 @@ mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSubpage, {.subpage = mojom::Subpage::kLanguages}}, - {IDS_OS_SETTINGS_TAG_LANGUAGES_CHANGE_SYSTEM_LANGUAGE, + {IDS_OS_SETTINGS_TAG_LANGUAGES_CHANGE_DEVICE_LANGUAGE, mojom::kLanguagesSubpagePath, mojom::SearchResultIcon::kGlobe, mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSetting, - {.setting = mojom::Setting::kChangeSystemLanguage}}, + {.setting = mojom::Setting::kChangeDeviceLanguage}}, {IDS_OS_SETTINGS_TAG_LANGUAGES_INPUT_ADD_LANGUAGE, mojom::kLanguagesSubpagePath, mojom::SearchResultIcon::kGlobe, @@ -259,13 +259,11 @@ void AddLanguagesPageStringsV2(content::WebUIDataSource* html_source) { static constexpr webui::LocalizedString kLocalizedStrings[] = { - {"systemLanguageTitle", IDS_OS_SETTINGS_LANGUAGES_SYSTEM_LANGUAGE_TITLE}, - {"systemLanguageDescription", - IDS_OS_SETTINGS_LANGUAGES_SYSTEM_LANGUAGE_DESCRIPTION}, - {"changeSystemLanguageLabel", - IDS_OS_SETTINGS_LANGUAGES_CHANGE_SYSTEM_LANGUAGE_BUTTON_LABEL}, - {"changeSystemLanguageButtonDescription", - IDS_OS_SETTINGS_LANGUAGES_CHANGE_SYSTEM_LANGUAGE_BUTTON_DESCRIPTION}, + {"deviceLanguageTitle", IDS_OS_SETTINGS_LANGUAGES_DEVICE_LANGUAGE_TITLE}, + {"changeDeviceLanguageLabel", + IDS_OS_SETTINGS_LANGUAGES_CHANGE_DEVICE_LANGUAGE_BUTTON_LABEL}, + {"changeDeviceLanguageButtonDescription", + IDS_OS_SETTINGS_LANGUAGES_CHANGE_DEVICE_LANGUAGE_BUTTON_DESCRIPTION}, {"languagesPreferenceTitle", IDS_OS_SETTINGS_LANGUAGES_LANGUAGES_PREFERENCE_TITLE}, {"languagesPreferenceDescription", @@ -451,7 +449,7 @@ mojom::Subpage::kLanguages, mojom::SearchResultIcon::kGlobe, mojom::SearchResultDefaultRank::kMedium, mojom::kLanguagesSubpagePath); static constexpr mojom::Setting kLanguagesPageSettings[] = { - mojom::Setting::kChangeSystemLanguage, + mojom::Setting::kChangeDeviceLanguage, mojom::Setting::kOfferTranslation, }; RegisterNestedSettingBulk(mojom::Subpage::kLanguages, kLanguagesPageSettings,
diff --git a/chrome/browser/ui/webui/settings/chromeos/plugin_vm_handler.cc b/chrome/browser/ui/webui/settings/chromeos/plugin_vm_handler.cc index 39861849..7cb12b0 100644 --- a/chrome/browser/ui/webui/settings/chromeos/plugin_vm_handler.cc +++ b/chrome/browser/ui/webui/settings/chromeos/plugin_vm_handler.cc
@@ -27,9 +27,8 @@ base::Value device_info(base::Value::Type::DICTIONARY); device_info.SetStringKey("guid", device.guid); device_info.SetStringKey("label", device.label); - auto it = device.vm_sharing_info.find(plugin_vm::kPluginVmName); - bool shared = it != device.vm_sharing_info.end() && it->second.shared; - device_info.SetBoolKey("shared", shared); + device_info.SetBoolKey("shared", + device.shared_vm_name == plugin_vm::kPluginVmName); usb_devices_list.Append(std::move(device_info)); } return usb_devices_list;
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt index 7e8bc41..edf6f7a 100644 --- a/chrome/build/mac.pgo.txt +++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@ -chrome-mac-master-1600322062-3a48015e01b1446b1d53601d43c9f7d995fa4dd3.profdata +chrome-mac-master-1600365511-0307b18a9fe145dd28de0ead73e8fb46212de02b.profdata
diff --git a/chrome/services/sharing/nearby/nearby_connections.cc b/chrome/services/sharing/nearby/nearby_connections.cc index 44e2a9e..5599fe1f 100644 --- a/chrome/services/sharing/nearby/nearby_connections.cc +++ b/chrome/services/sharing/nearby/nearby_connections.cc
@@ -203,7 +203,8 @@ .allowed = MediumSelectorFromMojom(options->allowed_mediums.get()), .auto_upgrade_bandwidth = options->auto_upgrade_bandwidth, .enforce_topology_constraints = options->enforce_topology_constraints, - }; + .fast_advertisement_service_uuid = + options->fast_advertisement_service_uuid.canonical_value()}; core_->StartAdvertising( service_id, std::move(connection_options),
diff --git a/chrome/services/sharing/nearby/nearby_connections_unittest.cc b/chrome/services/sharing/nearby/nearby_connections_unittest.cc index 1ba70d1..40e1580 100644 --- a/chrome/services/sharing/nearby/nearby_connections_unittest.cc +++ b/chrome/services/sharing/nearby/nearby_connections_unittest.cc
@@ -34,7 +34,9 @@ namespace { -const char kServiceId[] = "service-id"; +const char kServiceId[] = "NearbySharing"; +const char kFastAdvertisementServiceUuid[] = + "0000fef3-0000-1000-8000-00805f9b34fb"; const char kRemoteEndpointId[] = "remote_endpoint_id"; const char kEndpointInfo[] = {0x0d, 0x07, 0x07, 0x07, 0x07}; const char kRemoteEndpointInfo[] = {0x0d, 0x07, 0x06, 0x08, 0x09}; @@ -48,10 +50,12 @@ auto allowed_mediums = mojom::MediumSelection::New(/*bluetooth=*/true, /*web_rtc=*/false, /*wifi_lan=*/true); - return mojom::AdvertisingOptions::New(mojom::Strategy::kP2pPointToPoint, - std::move(allowed_mediums), - /*auto_upgrade_bandwidth=*/true, - /*enforce_topology_constraints=*/true); + return mojom::AdvertisingOptions::New( + mojom::Strategy::kP2pPointToPoint, std::move(allowed_mediums), + /*auto_upgrade_bandwidth=*/true, + /*enforce_topology_constraints=*/true, + /*fast_advertisement_service_uuid=*/ + device::BluetoothUUID(kFastAdvertisementServiceUuid)); } mojom::ConnectionOptionsPtr CreateConnectionOptions(
diff --git a/chrome/services/sharing/nearby/platform_v2/ble_medium.cc b/chrome/services/sharing/nearby/platform_v2/ble_medium.cc index e8e50dd8..7195a2f5 100644 --- a/chrome/services/sharing/nearby/platform_v2/ble_medium.cc +++ b/chrome/services/sharing/nearby/platform_v2/ble_medium.cc
@@ -23,9 +23,14 @@ const std::string& service_id, const ByteArray& advertisement, const std::string& fast_advertisement_service_uuid) { + // Chrome Nearby BLE cannot support regular advertisements; only Fast + // Advertisements. Ensure |fast_advertisement_service_uuid| is provided. + DCHECK(!fast_advertisement_service_uuid.empty()); + StopAdvertising(service_id); - auto service_uuid = device::BluetoothUUID(service_id); + auto service_uuid = device::BluetoothUUID(fast_advertisement_service_uuid); + mojo::PendingRemote<bluetooth::mojom::Advertisement> pending_advertisement; bool success = adapter_->RegisterAdvertisement( service_uuid, @@ -36,6 +41,9 @@ if (!success || !pending_advertisement.is_valid()) return false; + registered_service_id_to_fast_advertisement_service_uuid_map_.emplace( + service_id, service_uuid); + auto& remote_advertisement = registered_advertisements_map_ .emplace(service_uuid, std::move(pending_advertisement)) @@ -47,13 +55,22 @@ } bool BleMedium::StopAdvertising(const std::string& service_id) { - auto it = - registered_advertisements_map_.find(device::BluetoothUUID(service_id)); - if (it == registered_advertisements_map_.end()) + auto uuid_it = + registered_service_id_to_fast_advertisement_service_uuid_map_.find( + service_id); + if (uuid_it == + registered_service_id_to_fast_advertisement_service_uuid_map_.end()) { + return true; + } + + auto advertisement_it = registered_advertisements_map_.find(uuid_it->second); + registered_service_id_to_fast_advertisement_service_uuid_map_.erase(uuid_it); + + if (advertisement_it == registered_advertisements_map_.end()) return true; - bool success = it->second->Unregister(); - registered_advertisements_map_.erase(it); + bool success = advertisement_it->second->Unregister(); + registered_advertisements_map_.erase(advertisement_it); return success; }
diff --git a/chrome/services/sharing/nearby/platform_v2/ble_medium.h b/chrome/services/sharing/nearby/platform_v2/ble_medium.h index 35ada19..bc9d11e 100644 --- a/chrome/services/sharing/nearby/platform_v2/ble_medium.h +++ b/chrome/services/sharing/nearby/platform_v2/ble_medium.h
@@ -77,6 +77,11 @@ // events we don't care about outside of discovery don't pile up. mojo::Receiver<bluetooth::mojom::AdapterObserver> adapter_observer_{this}; + // A map of service IDs (e.g., "NearbySharing") to their respective fast + // advertisement UUIDs, as informed by active registered advertisements. + std::map<std::string, device::BluetoothUUID> + registered_service_id_to_fast_advertisement_service_uuid_map_; + // Keyed by service UUID of the advertisement. std::map<device::BluetoothUUID, mojo::Remote<bluetooth::mojom::Advertisement>> registered_advertisements_map_;
diff --git a/chrome/services/sharing/nearby/platform_v2/ble_medium_unittest.cc b/chrome/services/sharing/nearby/platform_v2/ble_medium_unittest.cc index d014b56..881194a 100644 --- a/chrome/services/sharing/nearby/platform_v2/ble_medium_unittest.cc +++ b/chrome/services/sharing/nearby/platform_v2/ble_medium_unittest.cc
@@ -24,11 +24,12 @@ namespace { -const char kServiceName[] = "NearbySharing"; -const char kServiceId1[] = "00000000-0000-0000-0000-000000000001"; +const char kServiceId1[] = "NearbySharing"; +const char kServiceId2[] = "PhoneHub"; const char kFastAdvertisementServiceId1[] = - "00000000-0000-0000-0000-000000000011"; -const char kServiceId2[] = "00000000-0000-0000-0000-000000000002"; + "00000000-0000-0000-0000-000000000001"; +const char kFastAdvertisementServiceId2[] = + "00000000-0000-0000-0000-000000000002"; const char kDeviceAddress[] = "DeviceAddress"; const char kDeviceServiceData1Str[] = "Device_Advertisement1"; const char kDeviceServiceData2Str[] = "Device_Advertisement2"; @@ -223,25 +224,25 @@ TEST_F(BleMediumTest, TestAdvertising) { ASSERT_FALSE(fake_adapter_->GetRegisteredAdvertisementServiceData( - device::BluetoothUUID(kServiceId1))); + device::BluetoothUUID(kFastAdvertisementServiceId1))); ASSERT_FALSE(fake_adapter_->GetRegisteredAdvertisementServiceData( - device::BluetoothUUID(kServiceId2))); + device::BluetoothUUID(kFastAdvertisementServiceId2))); ble_medium_->StartAdvertising(kServiceId1, ByteArray(kDeviceServiceData1Str), kFastAdvertisementServiceId1); EXPECT_EQ(GetByteVector(kDeviceServiceData1Str), *fake_adapter_->GetRegisteredAdvertisementServiceData( - device::BluetoothUUID(kServiceId1))); + device::BluetoothUUID(kFastAdvertisementServiceId1))); EXPECT_FALSE(fake_adapter_->GetRegisteredAdvertisementServiceData( - device::BluetoothUUID(kServiceId2))); + device::BluetoothUUID(kFastAdvertisementServiceId2))); ble_medium_->StartAdvertising(kServiceId2, ByteArray(kDeviceServiceData2Str), - kFastAdvertisementServiceId1); + kFastAdvertisementServiceId2); EXPECT_TRUE(fake_adapter_->GetRegisteredAdvertisementServiceData( - device::BluetoothUUID(kServiceId1))); + device::BluetoothUUID(kFastAdvertisementServiceId1))); EXPECT_EQ(GetByteVector(kDeviceServiceData2Str), *fake_adapter_->GetRegisteredAdvertisementServiceData( - device::BluetoothUUID(kServiceId2))); + device::BluetoothUUID(kFastAdvertisementServiceId2))); { base::RunLoop run_loop; @@ -251,9 +252,9 @@ } EXPECT_FALSE(fake_adapter_->GetRegisteredAdvertisementServiceData( - device::BluetoothUUID(kServiceId1))); + device::BluetoothUUID(kFastAdvertisementServiceId1))); EXPECT_TRUE(fake_adapter_->GetRegisteredAdvertisementServiceData( - device::BluetoothUUID(kServiceId2))); + device::BluetoothUUID(kFastAdvertisementServiceId2))); { base::RunLoop run_loop; @@ -263,17 +264,18 @@ } EXPECT_FALSE(fake_adapter_->GetRegisteredAdvertisementServiceData( - device::BluetoothUUID(kServiceId1))); + device::BluetoothUUID(kFastAdvertisementServiceId1))); EXPECT_FALSE(fake_adapter_->GetRegisteredAdvertisementServiceData( - device::BluetoothUUID(kServiceId2))); + device::BluetoothUUID(kFastAdvertisementServiceId2))); } TEST_F(BleMediumTest, TestScanning_OneService) { - StartScanning(kServiceId1); + StartScanning(kFastAdvertisementServiceId1); base::flat_map<device::BluetoothUUID, std::vector<uint8_t>> service_data_map; - service_data_map.insert_or_assign(device::BluetoothUUID(kServiceId1), - GetByteVector(kDeviceServiceData1Str)); + service_data_map.insert_or_assign( + device::BluetoothUUID(kFastAdvertisementServiceId1), + GetByteVector(kDeviceServiceData1Str)); NotifyDeviceAdded(kDeviceAddress, service_data_map, /*num_expected_peripherals_discovered=*/1u); @@ -281,10 +283,11 @@ auto& last_peripheral_discovered_args = last_peripheral_discovered_args_[0]; const auto* first_discovered_ble_peripheral = last_peripheral_discovered_args.first; - EXPECT_EQ(kServiceId1, last_peripheral_discovered_args.second); - VerifyByteArrayEquals( - first_discovered_ble_peripheral->GetAdvertisementBytes(kServiceId1), - kDeviceServiceData1Str); + EXPECT_EQ(kFastAdvertisementServiceId1, + last_peripheral_discovered_args.second); + VerifyByteArrayEquals(first_discovered_ble_peripheral->GetAdvertisementBytes( + kFastAdvertisementServiceId1), + kDeviceServiceData1Str); // The same information should be returned on a DeviceChanged event, with // the same BlePeripheral reference. @@ -294,9 +297,11 @@ last_peripheral_discovered_args = last_peripheral_discovered_args_[0]; EXPECT_EQ(first_discovered_ble_peripheral, last_peripheral_discovered_args.first); - EXPECT_EQ(kServiceId1, last_peripheral_discovered_args.second); + EXPECT_EQ(kFastAdvertisementServiceId1, + last_peripheral_discovered_args.second); VerifyByteArrayEquals( - last_peripheral_discovered_args.first->GetAdvertisementBytes(kServiceId1), + last_peripheral_discovered_args.first->GetAdvertisementBytes( + kFastAdvertisementServiceId1), kDeviceServiceData1Str); // Again, the same BlePeripheral reference should be marked as lost. @@ -305,20 +310,22 @@ const auto& last_peripheral_lost_args = last_peripheral_lost_args_[0]; const auto* lost_ble_peripheral = last_peripheral_lost_args.first; EXPECT_EQ(first_discovered_ble_peripheral, lost_ble_peripheral); - EXPECT_EQ(kServiceId1, last_peripheral_lost_args.second); + EXPECT_EQ(kFastAdvertisementServiceId1, last_peripheral_lost_args.second); - StopScanning(kServiceId1); + StopScanning(kFastAdvertisementServiceId1); } TEST_F(BleMediumTest, TestScanning_MultipleServices) { - StartScanning(kServiceId1); - StartScanning(kServiceId2); + StartScanning(kFastAdvertisementServiceId1); + StartScanning(kFastAdvertisementServiceId2); base::flat_map<device::BluetoothUUID, std::vector<uint8_t>> service_data_map; - service_data_map.insert_or_assign(device::BluetoothUUID(kServiceId1), - GetByteVector(kDeviceServiceData1Str)); - service_data_map.insert_or_assign(device::BluetoothUUID(kServiceId2), - GetByteVector(kDeviceServiceData2Str)); + service_data_map.insert_or_assign( + device::BluetoothUUID(kFastAdvertisementServiceId1), + GetByteVector(kDeviceServiceData1Str)); + service_data_map.insert_or_assign( + device::BluetoothUUID(kFastAdvertisementServiceId2), + GetByteVector(kDeviceServiceData2Str)); // Discovering a device with 2 desired service ids should trigger discovery // callbacks for both. @@ -327,31 +334,31 @@ ASSERT_EQ(2u, last_peripheral_discovered_args_.size()); VerifyByteArrayEquals( last_peripheral_discovered_args_[0].first->GetAdvertisementBytes( - kServiceId1), + kFastAdvertisementServiceId1), kDeviceServiceData1Str); VerifyByteArrayEquals( last_peripheral_discovered_args_[1].first->GetAdvertisementBytes( - kServiceId2), + kFastAdvertisementServiceId2), kDeviceServiceData2Str); NotifyDeviceRemoved(kDeviceAddress, /*num_expected_peripherals_lost=*/2u); ASSERT_EQ(2u, last_peripheral_lost_args_.size()); - StopScanning(kServiceId1); - StopScanning(kServiceId2); + StopScanning(kFastAdvertisementServiceId1); + StopScanning(kFastAdvertisementServiceId2); } TEST_F(BleMediumTest, TestStartAcceptingConnections) { // StartAcceptingConnections() should do nothing but still return true. EXPECT_TRUE( - ble_medium_->StartAcceptingConnections(kServiceName, /*callback=*/{})); + ble_medium_->StartAcceptingConnections(kServiceId1, /*callback=*/{})); } TEST_F(BleMediumTest, TestConnect) { chrome::BlePeripheral ble_peripheral(bluetooth::mojom::DeviceInfo::New()); // Connect() should do nothing and not return a valid api::BleSocket. - EXPECT_FALSE(ble_medium_->Connect(ble_peripheral, kServiceName)); + EXPECT_FALSE(ble_medium_->Connect(ble_peripheral, kServiceId1)); } } // namespace chrome
diff --git a/chrome/services/sharing/public/mojom/nearby_connections_types.mojom b/chrome/services/sharing/public/mojom/nearby_connections_types.mojom index d91b19d..34995ca 100644 --- a/chrome/services/sharing/public/mojom/nearby_connections_types.mojom +++ b/chrome/services/sharing/public/mojom/nearby_connections_types.mojom
@@ -4,6 +4,7 @@ module location.nearby.connections.mojom; +import "device/bluetooth/public/mojom/uuid.mojom"; import "mojo/public/mojom/base/file.mojom"; // Generic result status of NearbyConnections API calls. @@ -131,6 +132,10 @@ // you can initially connect as a kP2pCluster and then trim connections until // you match kP2pStar or kP2pPointToPoint before upgrading the bandwidth. bool enforce_topology_constraints = true; + // Optional. If set, BLE advertisements will be in their "fast advertisement" + // form, use this UUID, and non-connectable; if empty, BLE advertisements + // will otherwise be normal and connectable. + bluetooth.mojom.UUID fast_advertisement_service_uuid; }; // Options for a call to NearbyConnections::StartDiscovery().
diff --git a/chrome/test/data/extensions/api_test/alarms/spanning/background.js b/chrome/test/data/extensions/api_test/alarms/spanning/background.js index eaa2b55..327dc2f2 100644 --- a/chrome/test/data/extensions/api_test/alarms/spanning/background.js +++ b/chrome/test/data/extensions/api_test/alarms/spanning/background.js
@@ -3,6 +3,7 @@ // found in the LICENSE file. let inIncognito = chrome.extension.inIncognitoContext; +let alarmName = inIncognito ? 'incognito' : 'normal'; chrome.alarms.onAlarm.addListener(function(alarm) { chrome.test.assertEq(inIncognito ? 'incognito' : 'normal', alarm.name); @@ -11,8 +12,27 @@ chrome.test.runTests([ // Creates an alarm with the name of the context it was created in. - function createAlarms() { - const alarmName = inIncognito ? 'incognito' : 'normal'; - chrome.alarms.create(alarmName, {delayInMinutes: 0.001}); + function createAlarm() { + chrome.alarms.create(alarmName, {delayInMinutes: 0.001, + periodInMinutes: 60}); + }, + function getAlarm() { + chrome.alarms.get(alarmName, function(alarm) { + chrome.test.assertEq(alarmName, alarm.name); + chrome.test.succeed(); + }); + }, + function getAllAlarms() { + chrome.alarms.getAll(function(alarms) { + chrome.test.assertEq(1, alarms.length); + chrome.test.assertEq(alarmName, alarms[0].name); + chrome.test.succeed(); + }); + }, + function clearAlarm() { + chrome.alarms.clear(alarmName, function(wasCleared) { + chrome.test.assertTrue(wasCleared); + chrome.test.succeed(); + }); } ]);
diff --git a/chrome/test/data/extensions/api_test/alarms/split/background.js b/chrome/test/data/extensions/api_test/alarms/split/background.js index a771e8e..6f8eaf7 100644 --- a/chrome/test/data/extensions/api_test/alarms/split/background.js +++ b/chrome/test/data/extensions/api_test/alarms/split/background.js
@@ -3,6 +3,7 @@ // found in the LICENSE file. let inIncognito = chrome.extension.inIncognitoContext; +let alarmName = inIncognito ? 'incognito' : 'normal'; let alarmTriggered = false; let testEventFired = false; @@ -24,9 +25,30 @@ chrome.test.runTests([ // Creates an alarm with the name of the context it was created in. - function createAlarms() { - const alarmName = inIncognito ? 'incognito' : 'normal'; - chrome.alarms.create(alarmName, {delayInMinutes: 0.001}); + function createAlarm() { + // This test will pass when checkAndComplete() is called + // after the C++ code sends the expected message. + chrome.alarms.create(alarmName, {delayInMinutes: 0.001, + periodInMinutes: 60}); + }, + function getAlarm() { + chrome.alarms.get(alarmName, function(alarm) { + chrome.test.assertEq(alarmName, alarm.name); + chrome.test.succeed(); + }); + }, + function getAllAlarms() { + chrome.alarms.getAll(function(alarms) { + chrome.test.assertEq(1, alarms.length); + chrome.test.assertEq(alarmName, alarms[0].name); + chrome.test.succeed(); + }); + }, + function clearAlarm() { + chrome.alarms.clear(alarmName, function(wasCleared) { + chrome.test.assertTrue(wasCleared); + chrome.test.succeed(); + }); } ]);
diff --git a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/psim_flow_ui_test.js b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/psim_flow_ui_test.js index 204035d..4f4e985b 100644 --- a/chrome/test/data/webui/cr_components/chromeos/cellular_setup/psim_flow_ui_test.js +++ b/chrome/test/data/webui/cr_components/chromeos/cellular_setup/psim_flow_ui_test.js
@@ -6,6 +6,7 @@ // #import 'chrome://os-settings/strings.m.js'; // #import 'chrome://resources/cr_components/chromeos/cellular_setup/psim_flow_ui.m.js'; +// #import {PSimUIState} from 'chrome://resources/cr_components/chromeos/cellular_setup/psim_flow_ui.m.js'; // #import {flush, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; // #import {assertTrue} from '../../../chai_assert.js'; // clang-format on @@ -22,4 +23,13 @@ const ironPage = pSimPage.$$('iron-pages'); assertTrue(!!ironPage); }); + + test('forward navigation test', function() { + pSimPage.state_ = cellularSetup.PSimUIState.WAITING_FOR_PORTAL_TO_LOAD; + Polymer.dom.flush(); + pSimPage.navigateForward(); + assertTrue( + pSimPage.state_ === + cellularSetup.PSimUIState.WAITING_FOR_ACTIVATION_TO_FINISH); + }); });
diff --git a/chrome/test/data/webui/namespace_rewrites.gni b/chrome/test/data/webui/namespace_rewrites.gni index 651acd3..09693f5 100644 --- a/chrome/test/data/webui/namespace_rewrites.gni +++ b/chrome/test/data/webui/namespace_rewrites.gni
@@ -3,6 +3,7 @@ # found in the LICENSE file. test_namespace_rewrites = [ + "cellularSetup.PSimUIState|PSimUIState", "global.traceAssertionsForTesting|window.traceAssertionsForTesting", "MockInteractions.blur|blur", "MockInteractions.downAndUp|downAndUp",
diff --git a/chrome/test/data/webui/settings/chromeos/os_languages_page_v2_tests.js b/chrome/test/data/webui/settings/chromeos/os_languages_page_v2_tests.js index 2b25638a..738bdb0 100644 --- a/chrome/test/data/webui/settings/chromeos/os_languages_page_v2_tests.js +++ b/chrome/test/data/webui/settings/chromeos/os_languages_page_v2_tests.js
@@ -290,7 +290,7 @@ setup(() => { assertFalse( !!languagesPage.$$('os-settings-change-device-language-dialog')); - languagesPage.$$('#changeSystemLanguage').click(); + languagesPage.$$('#changeDeviceLanguage').click(); Polymer.dom.flush(); dialog = languagesPage.$$('os-settings-change-device-language-dialog');
diff --git a/chromeos/components/phonehub/browser_tabs_model.cc b/chromeos/components/phonehub/browser_tabs_model.cc index 6ae370bb..c353175 100644 --- a/chromeos/components/phonehub/browser_tabs_model.cc +++ b/chromeos/components/phonehub/browser_tabs_model.cc
@@ -9,6 +9,8 @@ namespace chromeos { namespace phonehub { +const size_t BrowserTabsModel::kMaxMostRecentTabs = 4; + BrowserTabsModel::BrowserTabMetadata::BrowserTabMetadata( GURL url, const base::string16& title, @@ -36,23 +38,31 @@ bool BrowserTabsModel::BrowserTabMetadata::operator<( const BrowserTabMetadata& other) const { - return std::tie(last_accessed_timestamp) < + // More recently visited tabs should come before earlier visited tabs. + return std::tie(last_accessed_timestamp) > std::tie(other.last_accessed_timestamp); } BrowserTabsModel::BrowserTabsModel( bool is_tab_sync_enabled, - const base::Optional<BrowserTabMetadata>& most_recent_tab, - const base::Optional<BrowserTabMetadata>& second_most_recent_tab) + const std::vector<BrowserTabMetadata>& most_recent_tabs) : is_tab_sync_enabled_(is_tab_sync_enabled), - most_recent_tab_(most_recent_tab), - second_most_recent_tab_(second_most_recent_tab) { - if (!is_tab_sync_enabled_ && - (most_recent_tab_.has_value() || second_most_recent_tab_.has_value())) { + most_recent_tabs_(most_recent_tabs) { + if (!is_tab_sync_enabled_ && !most_recent_tabs_.empty()) { PA_LOG(WARNING) << "Tab sync is not enabled, but tab metadata was " << "provided; clearing metadata."; - most_recent_tab_.reset(); - second_most_recent_tab_.reset(); + most_recent_tabs_.clear(); + return; + } + + std::sort(most_recent_tabs_.begin(), most_recent_tabs_.end()); + if (most_recent_tabs_.size() > kMaxMostRecentTabs) { + PA_LOG(WARNING) << "More than the max number of browser tab metadatas were " + "provided; truncating least recently visited browser " + "tabs' metadatas."; + most_recent_tabs_.erase(most_recent_tabs_.begin() + kMaxMostRecentTabs, + most_recent_tabs_.end()); + return; } } @@ -62,8 +72,7 @@ bool BrowserTabsModel::operator==(const BrowserTabsModel& other) const { return is_tab_sync_enabled_ == other.is_tab_sync_enabled_ && - most_recent_tab_ == other.most_recent_tab_ && - second_most_recent_tab_ == other.second_most_recent_tab_; + most_recent_tabs_ == other.most_recent_tabs_; } bool BrowserTabsModel::operator!=(const BrowserTabsModel& other) const {
diff --git a/chromeos/components/phonehub/browser_tabs_model.h b/chromeos/components/phonehub/browser_tabs_model.h index 321b15f..277180f7 100644 --- a/chromeos/components/phonehub/browser_tabs_model.h +++ b/chromeos/components/phonehub/browser_tabs_model.h
@@ -19,6 +19,8 @@ // Contains metadata about browser tabs that are open on the user's phone. class BrowserTabsModel { public: + static const size_t kMaxMostRecentTabs; + struct BrowserTabMetadata { BrowserTabMetadata(GURL url, const base::string16& title, @@ -37,14 +39,11 @@ }; // |is_tab_sync_enabled| indicates whether the Chrome OS device is currently - // syncing tab metadata. If that parameter is false, the optional tab - // parameters should be null. If it is true, one or both of the parameters may - // still be null if the user does not have browser tabs open on their phone. + // syncing tab metadata. If that parameter is false, |most_recent_tabs_| + // should be empty. If it is true, |most_recent_tabs_| can contain up to four. BrowserTabsModel( bool is_tab_sync_enabled, - const base::Optional<BrowserTabMetadata>& most_recent_tab = base::nullopt, - const base::Optional<BrowserTabMetadata>& second_most_recent_tab = - base::nullopt); + const std::vector<BrowserTabMetadata>& most_recent_tabs = {}); BrowserTabsModel(const BrowserTabsModel& other); ~BrowserTabsModel(); @@ -52,17 +51,16 @@ bool operator!=(const BrowserTabsModel& other) const; bool is_tab_sync_enabled() const { return is_tab_sync_enabled_; } - const base::Optional<BrowserTabMetadata>& most_recent_tab() const { - return most_recent_tab_; - } - const base::Optional<BrowserTabMetadata>& second_most_recent_tab() const { - return second_most_recent_tab_; + + const std::vector<BrowserTabMetadata>& most_recent_tabs() const { + return most_recent_tabs_; } private: bool is_tab_sync_enabled_; - base::Optional<BrowserTabMetadata> most_recent_tab_; - base::Optional<BrowserTabMetadata> second_most_recent_tab_; + + // Sorted from most recently visited to least recently visited. + std::vector<BrowserTabMetadata> most_recent_tabs_; }; std::ostream& operator<<(
diff --git a/chromeos/components/phonehub/browser_tabs_model_unittest.cc b/chromeos/components/phonehub/browser_tabs_model_unittest.cc index e17bc53..01e0d28f 100644 --- a/chromeos/components/phonehub/browser_tabs_model_unittest.cc +++ b/chromeos/components/phonehub/browser_tabs_model_unittest.cc
@@ -11,17 +11,28 @@ namespace phonehub { TEST(BrowserTabsModelTest, Initialization) { - BrowserTabsModel success(/*is_tab_sync_enabled=*/true, - CreateFakeBrowserTabMetadata()); + std::vector<BrowserTabsModel::BrowserTabMetadata> most_recent_tabs( + {CreateFakeBrowserTabMetadata()}); + + BrowserTabsModel success(/*is_tab_sync_enabled=*/true, most_recent_tabs); EXPECT_TRUE(success.is_tab_sync_enabled()); - EXPECT_EQ(CreateFakeBrowserTabMetadata(), *success.most_recent_tab()); - EXPECT_FALSE(success.second_most_recent_tab().has_value()); + EXPECT_EQ(most_recent_tabs, success.most_recent_tabs()); // If tab metadata is provided by tab sync is disabled, the data should be // cleared. BrowserTabsModel invalid_metadata(/*is_tab_sync_enabled=*/false, - CreateFakeBrowserTabMetadata()); - EXPECT_FALSE(invalid_metadata.most_recent_tab().has_value()); + most_recent_tabs); + EXPECT_TRUE(invalid_metadata.most_recent_tabs().empty()); + + // If the number of metadata is provided exceeds |kMaxMostRecentTabs|, the + // metadata vector will be truncated to be of size |kMaxMostRecentTabs|. + const int exceed_max_offset = BrowserTabsModel::kMaxMostRecentTabs + 1; + std::vector<BrowserTabsModel::BrowserTabMetadata> most_recent_tabs_exceeded( + exceed_max_offset, CreateFakeBrowserTabMetadata()); + BrowserTabsModel truncated_metadata(/*is_tab_sync_enabled=*/true, + most_recent_tabs_exceeded); + EXPECT_EQ(BrowserTabsModel::kMaxMostRecentTabs, + truncated_metadata.most_recent_tabs().size()); } } // namespace phonehub
diff --git a/chromeos/components/phonehub/fake_tether_controller.cc b/chromeos/components/phonehub/fake_tether_controller.cc index 86a292c8..8a635f6 100644 --- a/chromeos/components/phonehub/fake_tether_controller.cc +++ b/chromeos/components/phonehub/fake_tether_controller.cc
@@ -35,5 +35,10 @@ } } +void FakeTetherController::Disconnect() { + if (status_ == Status::kConnecting || status_ == Status::kConnected) + SetStatus(Status::kConnecting); +} + } // namespace phonehub } // namespace chromeos
diff --git a/chromeos/components/phonehub/fake_tether_controller.h b/chromeos/components/phonehub/fake_tether_controller.h index d2fd6aa..f0616db 100644 --- a/chromeos/components/phonehub/fake_tether_controller.h +++ b/chromeos/components/phonehub/fake_tether_controller.h
@@ -22,6 +22,7 @@ Status GetStatus() const override; void ScanForAvailableConnection() override; void AttemptConnection() override; + void Disconnect() override; Status status_; };
diff --git a/chromeos/components/phonehub/phone_model_test_util.cc b/chromeos/components/phonehub/phone_model_test_util.cc index 445a28c..c2593bc8 100644 --- a/chromeos/components/phonehub/phone_model_test_util.cc +++ b/chromeos/components/phonehub/phone_model_test_util.cc
@@ -33,26 +33,35 @@ const char kFakeBrowserTabUrl1[] = "https://www.example.com/tab1"; const char kFakeBrowserTabName1[] = "Tab 1"; +const base::Time kFakeBrowserTabLastAccessedTimestamp1 = + base::Time::FromDoubleT(4); + const char kFakeBrowserTabUrl2[] = "https://www.example.com/tab2"; const char kFakeBrowserTabName2[] = "Tab 2"; +const base::Time kFakeBrowserTabLastAccessedTimestamp2 = + base::Time::FromDoubleT(3); const BrowserTabsModel::BrowserTabMetadata& CreateFakeBrowserTabMetadata() { static const base::NoDestructor<BrowserTabsModel::BrowserTabMetadata> - fake_browser_tab_metadata{GURL(kFakeBrowserTabUrl1), - base::UTF8ToUTF16(kFakeBrowserTabName1), - base::Time(), gfx::Image()}; + fake_browser_tab_metadata{ + GURL(kFakeBrowserTabUrl1), base::UTF8ToUTF16(kFakeBrowserTabName1), + kFakeBrowserTabLastAccessedTimestamp1, gfx::Image()}; return *fake_browser_tab_metadata; } const BrowserTabsModel& CreateFakeBrowserTabsModel() { static const base::NoDestructor<BrowserTabsModel::BrowserTabMetadata> - second_browser_tab_metadata{GURL(kFakeBrowserTabUrl2), - base::UTF8ToUTF16(kFakeBrowserTabName2), - base::Time(), gfx::Image()}; + second_browser_tab_metadata{ + GURL(kFakeBrowserTabUrl2), base::UTF8ToUTF16(kFakeBrowserTabName2), + kFakeBrowserTabLastAccessedTimestamp2, gfx::Image()}; + + static const base::NoDestructor< + std::vector<BrowserTabsModel::BrowserTabMetadata>> + most_recent_tabs( + {CreateFakeBrowserTabMetadata(), *second_browser_tab_metadata}); static const base::NoDestructor<BrowserTabsModel> fake_browser_tabs_model{ - /*is_tab_sync_enabled=*/true, CreateFakeBrowserTabMetadata(), - *second_browser_tab_metadata}; + /*is_tab_sync_enabled=*/true, *most_recent_tabs}; return *fake_browser_tabs_model; }
diff --git a/chromeos/components/phonehub/phone_model_test_util.h b/chromeos/components/phonehub/phone_model_test_util.h index 8a831f37..ac3c77c 100644 --- a/chromeos/components/phonehub/phone_model_test_util.h +++ b/chromeos/components/phonehub/phone_model_test_util.h
@@ -25,8 +25,10 @@ // Fake data for browser tabs. extern const char kFakeBrowserTabUrl1[]; extern const char kFakeBrowserTabName1[]; +extern const base::Time kFakeBrowserTabLastAccessedTimestamp1; extern const char kFakeBrowserTabUrl2[]; extern const char kFakeBrowserTabName2[]; +extern const base::Time kFakeBrowserTabLastAccessedTimestamp2; // Creates fake browser tab data for use in tests. const BrowserTabsModel::BrowserTabMetadata& CreateFakeBrowserTabMetadata();
diff --git a/chromeos/components/phonehub/tether_controller.h b/chromeos/components/phonehub/tether_controller.h index a070dfd..323b46dd 100644 --- a/chromeos/components/phonehub/tether_controller.h +++ b/chromeos/components/phonehub/tether_controller.h
@@ -65,6 +65,11 @@ // state is not one of kConnectionUnavailable or kConnectionAvailable. virtual void AttemptConnection() = 0; + // Disconnects from an active Instant Tethering connection or connection + // attempt. This function is a no-op if the state is not one of kConnecting or + // kConnected. + virtual void Disconnect() = 0; + void AddObserver(Observer* observer); void RemoveObserver(Observer* observer);
diff --git a/chromeos/components/phonehub/tether_controller_impl.cc b/chromeos/components/phonehub/tether_controller_impl.cc index b99b33f..93ad75b 100644 --- a/chromeos/components/phonehub/tether_controller_impl.cc +++ b/chromeos/components/phonehub/tether_controller_impl.cc
@@ -48,5 +48,17 @@ // TODO(khorimoto): Actually attempt a connection. } +void TetherControllerImpl::Disconnect() { + if (status_ != Status::kConnecting && status_ != Status::kConnected) { + PA_LOG(WARNING) << "Received request to disconnect, but no connection or " + << "connection attempt is in progress. Current status is " + << status_; + return; + } + + PA_LOG(INFO) << "Attempting disconnection; current status is " << status_; + // TODO(khorimoto): Actually attempt a connection. +} + } // namespace phonehub } // namespace chromeos
diff --git a/chromeos/components/phonehub/tether_controller_impl.h b/chromeos/components/phonehub/tether_controller_impl.h index 578922d..4ece4549 100644 --- a/chromeos/components/phonehub/tether_controller_impl.h +++ b/chromeos/components/phonehub/tether_controller_impl.h
@@ -28,6 +28,7 @@ Status GetStatus() const override; void ScanForAvailableConnection() override; void AttemptConnection() override; + void Disconnect() override; multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client_;
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc index 8f17017..22c11987 100644 --- a/chromeos/constants/chromeos_features.cc +++ b/chromeos/constants/chromeos_features.cc
@@ -80,22 +80,22 @@ // Controls whether to suggest addresses in assistive personal information. This // is only effective when AssistPersonalInfo flag is enabled. const base::Feature kAssistPersonalInfoAddress{ - "AssistPersonalInfoAddress", base::FEATURE_DISABLED_BY_DEFAULT}; + "AssistPersonalInfoAddress", base::FEATURE_ENABLED_BY_DEFAULT}; // Controls whether to suggest emails in assistive personal information. This is // only effective when AssistPersonalInfo flag is enabled. const base::Feature kAssistPersonalInfoEmail{"AssistPersonalInfoEmail", - base::FEATURE_DISABLED_BY_DEFAULT}; + base::FEATURE_ENABLED_BY_DEFAULT}; // Controls whether to suggest names in assistive personal information. This is // only effective when AssistPersonalInfo flag is enabled. const base::Feature kAssistPersonalInfoName{"AssistPersonalInfoName", - base::FEATURE_DISABLED_BY_DEFAULT}; + base::FEATURE_ENABLED_BY_DEFAULT}; // Controls whether to suggest phone numbers in assistive personal information. // This is only effective when AssistPersonalInfo flag is enabled. const base::Feature kAssistPersonalInfoPhoneNumber{ - "AssistPersonalInfoPhoneNumber", base::FEATURE_DISABLED_BY_DEFAULT}; + "AssistPersonalInfoPhoneNumber", base::FEATURE_ENABLED_BY_DEFAULT}; // Displays the avatar toolbar button and the profile menu. // https://crbug.com/1041472
diff --git a/chromeos/crosapi/mojom/crosapi.mojom b/chromeos/crosapi/mojom/crosapi.mojom index 2c96ace1..a8d7002 100644 --- a/chromeos/crosapi/mojom/crosapi.mojom +++ b/chromeos/crosapi/mojom/crosapi.mojom
@@ -15,6 +15,7 @@ // AshChromeService defines the APIs that live in ash-chrome and are // accessed from lacros-chrome. +[Stable] interface AshChromeService { // Binds the KeystoreService interface for challenging keys. BindKeystoreService@2(pending_receiver<KeystoreService> receiver); @@ -35,12 +36,14 @@ // LacrosInitParams is a set of parameters for initialization of lacros-chrome, // which is passed from ash-chrome. +[Stable] struct LacrosInitParams { // This is placeholder, so now it is empty. }; // LacrosChromeService defines the APIs that live in lacros-chrome and // are accessed from ash-chrome. +[Stable] interface LacrosChromeService { // Ash-chrome can pass initialize parameters via this method. // The parameters are available on lacros-chrome startup.
diff --git a/chromeos/crosapi/mojom/screen_manager.mojom b/chromeos/crosapi/mojom/screen_manager.mojom index 4e1b2d8..2f339943 100644 --- a/chromeos/crosapi/mojom/screen_manager.mojom +++ b/chromeos/crosapi/mojom/screen_manager.mojom
@@ -7,12 +7,13 @@ import "chromeos/crosapi/mojom/bitmap.mojom"; // A unique identifier and title for a window. +[Stable] struct WindowDetails { // Guaranteed to be unique and never reused. - uint64 id; + uint64 id@0; // The title of the window in UTF-8 encoding. - string title; + string title@1; }; // This interface is implemented by ash-chrome. It allows lacros-chrome to query @@ -26,6 +27,7 @@ // TODO(https://crbug.com/1094460): This is a very simple interface. We will // likely want to replace it with a more feature-complete and performant // interface in the future. +[Stable] interface ScreenManager { // TODO(https://crbug.com/1094460): We will need to add more methods for // querying screens, windows, etc. Details still TBD.
diff --git a/chromeos/dbus/fake_lorgnette_manager_client.cc b/chromeos/dbus/fake_lorgnette_manager_client.cc index cb897b5a..a6f34fc 100644 --- a/chromeos/dbus/fake_lorgnette_manager_client.cc +++ b/chromeos/dbus/fake_lorgnette_manager_client.cc
@@ -36,19 +36,28 @@ void FakeLorgnetteManagerClient::StartScan( std::string device_name, const ScanProperties& properties, - DBusMethodCallback<std::string> completion_callback, + VoidDBusMethodCallback completion_callback, + base::RepeatingCallback<void(std::string)> page_callback, base::Optional<base::RepeatingCallback<void(int)>> progress_callback) { - // Simulate progress reporting for the scan job. - if (progress_callback.has_value()) { - base::RepeatingCallback<void(int)> callback = progress_callback.value(); - for (int progress : {7, 22, 40, 42, 59, 74, 95}) { - callback.Run(progress); + if (scan_response_.has_value()) { + for (const std::string& page_data : scan_response_.value()) { + // Simulate progress reporting for the scan job. + if (progress_callback.has_value()) { + for (int progress : {7, 22, 40, 42, 59, 74, 95}) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(progress_callback.value(), progress)); + } + } + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(page_callback, page_data)); } } base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::BindOnce(std::move(completion_callback), - std::move(scan_image_response_))); + scan_response_.has_value())); + scan_response_ = base::nullopt; } void FakeLorgnetteManagerClient::SetListScannersResponse( @@ -64,8 +73,8 @@ } void FakeLorgnetteManagerClient::SetScanResponse( - const base::Optional<std::string>& scan_image_response) { - scan_image_response_ = scan_image_response; + const base::Optional<std::vector<std::string>>& scan_response) { + scan_response_ = scan_response; } } // namespace chromeos
diff --git a/chromeos/dbus/fake_lorgnette_manager_client.h b/chromeos/dbus/fake_lorgnette_manager_client.h index 372d3b6..80e560f 100644 --- a/chromeos/dbus/fake_lorgnette_manager_client.h +++ b/chromeos/dbus/fake_lorgnette_manager_client.h
@@ -6,6 +6,7 @@ #define CHROMEOS_DBUS_FAKE_LORGNETTE_MANAGER_CLIENT_H_ #include <string> +#include <vector> #include "base/optional.h" #include "chromeos/dbus/lorgnette/lorgnette_service.pb.h" @@ -31,7 +32,8 @@ DBusMethodCallback<lorgnette::ScannerCapabilities> callback) override; void StartScan(std::string device_name, const ScanProperties& properties, - DBusMethodCallback<std::string> completion_callback, + VoidDBusMethodCallback completion_callback, + base::RepeatingCallback<void(std::string)> page_callback, base::Optional<base::RepeatingCallback<void(int)>> progress_callback) override; @@ -45,13 +47,14 @@ const base::Optional<lorgnette::ScannerCapabilities>& capabilities_response); - // Sets the response returned by ScanImageToString() and StartScan(). - void SetScanResponse(const base::Optional<std::string>& scan_image_response); + // Sets the response returned by StartScan(). + void SetScanResponse( + const base::Optional<std::vector<std::string>>& scan_response); private: base::Optional<lorgnette::ListScannersResponse> list_scanners_response_; base::Optional<lorgnette::ScannerCapabilities> capabilities_response_; - base::Optional<std::string> scan_image_response_; + base::Optional<std::vector<std::string>> scan_response_; }; } // namespace chromeos
diff --git a/chromeos/dbus/lorgnette_manager_client.cc b/chromeos/dbus/lorgnette_manager_client.cc index b819f9bd..9545cb9 100644 --- a/chromeos/dbus/lorgnette_manager_client.cc +++ b/chromeos/dbus/lorgnette_manager_client.cc
@@ -17,6 +17,7 @@ #include "base/logging.h" #include "base/macros.h" #include "base/optional.h" +#include "base/sequence_checker.h" #include "base/task/post_task.h" #include "base/task/thread_pool.h" #include "base/threading/thread_task_runner_handle.h" @@ -67,7 +68,8 @@ // LorgnetteManagerClient override. void StartScan(std::string device_name, const ScanProperties& properties, - DBusMethodCallback<std::string> completion_callback, + VoidDBusMethodCallback completion_callback, + base::RepeatingCallback<void(std::string)> page_callback, base::Optional<base::RepeatingCallback<void(int)>> progress_callback) override { lorgnette::StartScanRequest request; @@ -91,8 +93,7 @@ if (!writer.AppendProtoAsArrayOfBytes(request)) { LOG(ERROR) << "Failed to encode StartScanRequest protobuf"; base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindOnce(std::move(completion_callback), base::nullopt)); + FROM_HERE, base::BindOnce(std::move(completion_callback), false)); return; } @@ -103,6 +104,7 @@ ScanJobState state; state.completion_callback = std::move(completion_callback); state.progress_callback = progress_callback; + state.page_callback = page_callback; state.scan_data_reader = std::move(scan_data_reader); lorgnette_daemon_proxy_->CallMethod( @@ -197,8 +199,9 @@ // as well as a ScanDataReader which is responsible for reading from the pipe // of data into a string. struct ScanJobState { - DBusMethodCallback<std::string> completion_callback; + VoidDBusMethodCallback completion_callback; base::Optional<base::RepeatingCallback<void(int)>> progress_callback; + base::RepeatingCallback<void(std::string)> page_callback; std::unique_ptr<ScanDataReader> scan_data_reader; }; @@ -246,16 +249,28 @@ // Called when scan data read is completed. // This is to maintain the lifetime of ScanDataReader instance. - void OnScanDataCompleted(DBusMethodCallback<std::string> callback, - std::unique_ptr<ScanDataReader> scan_data_reader, - base::Optional<std::string> data) { - std::move(callback).Run(std::move(data)); + void OnScanDataCompleted(std::string uuid, base::Optional<std::string> data) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (!base::Contains(scan_job_state_, uuid)) { + LOG(ERROR) << "Received ScanDataCompleted for unrecognized scan job: " + << uuid; + return; + } + + ScanJobState& state = scan_job_state_[uuid]; + if (data.has_value()) { + state.page_callback.Run(std::move(data.value())); + } + + std::move(state.completion_callback).Run(data.has_value()); + scan_job_state_.erase(uuid); } void OnStartScanResponse(ScanJobState state, dbus::Response* response) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (!response) { LOG(ERROR) << "Failed to obtain StartScanResponse"; - std::move(state.completion_callback).Run(base::nullopt); + std::move(state.completion_callback).Run(false); return; } @@ -263,13 +278,13 @@ dbus::MessageReader reader(response); if (!reader.PopArrayOfBytesAsProto(&response_proto)) { LOG(ERROR) << "Failed to decode StartScanResponse proto"; - std::move(state.completion_callback).Run(base::nullopt); + std::move(state.completion_callback).Run(false); return; } if (response_proto.state() == lorgnette::SCAN_STATE_FAILED) { LOG(ERROR) << "Starting Scan failed: " << response_proto.failure_reason(); - std::move(state.completion_callback).Run(base::nullopt); + std::move(state.completion_callback).Run(false); return; } @@ -277,6 +292,7 @@ } void ScanStatusChangedReceived(dbus::Signal* signal) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); dbus::MessageReader reader(signal); lorgnette::ScanStatusChangedSignal signal_proto; if (!reader.PopArrayOfBytesAsProto(&signal_proto)) { @@ -294,7 +310,7 @@ if (signal_proto.state() == lorgnette::SCAN_STATE_FAILED) { LOG(ERROR) << "Scan job " << signal_proto.scan_uuid() << " failed: " << signal_proto.failure_reason(); - std::move(state.completion_callback).Run(base::nullopt); + std::move(state.completion_callback).Run(false); scan_job_state_.erase(signal_proto.scan_uuid()); } else if (signal_proto.state() == lorgnette::SCAN_STATE_COMPLETED) { VLOG(1) << "Scan job " << signal_proto.scan_uuid() @@ -302,9 +318,7 @@ ScanDataReader* reader = state.scan_data_reader.get(); reader->Wait(base::BindOnce( &LorgnetteManagerClientImpl::OnScanDataCompleted, - weak_ptr_factory_.GetWeakPtr(), std::move(state.completion_callback), - std::move(state.scan_data_reader))); - scan_job_state_.erase(signal_proto.scan_uuid()); + weak_ptr_factory_.GetWeakPtr(), signal_proto.scan_uuid())); } else if (signal_proto.state() == lorgnette::SCAN_STATE_IN_PROGRESS && state.progress_callback.has_value()) { state.progress_callback.value().Run(signal_proto.progress()); @@ -322,7 +336,12 @@ // Map from scan UUIDs to ScanDataReader and callbacks for reporting scan // progress and completion. - base::flat_map<std::string, ScanJobState> scan_job_state_; + base::flat_map<std::string, ScanJobState> scan_job_state_ + GUARDED_BY_CONTEXT(sequence_checker_); + // Ensures that all callbacks are handled on the same sequence, so that it is + // safe to access scan_job_state_ without a lock. + SEQUENCE_CHECKER(sequence_checker_); + base::WeakPtrFactory<LorgnetteManagerClientImpl> weak_ptr_factory_{this}; };
diff --git a/chromeos/dbus/lorgnette_manager_client.h b/chromeos/dbus/lorgnette_manager_client.h index 9ff9428..2df74e9 100644 --- a/chromeos/dbus/lorgnette_manager_client.h +++ b/chromeos/dbus/lorgnette_manager_client.h
@@ -42,16 +42,18 @@ const std::string& device_name, DBusMethodCallback<lorgnette::ScannerCapabilities> callback) = 0; - // Request a scanned image using lorgnette's StartScan API and calls - // |completion_callback| when completed with a string pointing at the scanned - // image data. Image data will be stored in the .png format. + // Request a scanned image using lorgnette's StartScan API. As each page is + // completed, calls |page_callback| with a string containing the image data. + // Calls |completion_callback| when the scan has completed. Image data will + // be stored in the .png format. // // If |progress_callback| is provided, it will be called as scan progress - // increases.The progress will be passed as a value from 0-100. + // increases. The progress will be passed as a value from 0-100. virtual void StartScan( std::string device_name, const ScanProperties& properties, - DBusMethodCallback<std::string> completion_callback, + VoidDBusMethodCallback completion_callback, + base::RepeatingCallback<void(std::string)> page_callback, base::Optional<base::RepeatingCallback<void(int)>> progress_callback) = 0; // Factory function, creates a new instance and returns ownership.
diff --git a/chromeos/lacros/lacros_chrome_service_impl.h b/chromeos/lacros/lacros_chrome_service_impl.h index 6d28b38..f62d2ccb 100644 --- a/chromeos/lacros/lacros_chrome_service_impl.h +++ b/chromeos/lacros/lacros_chrome_service_impl.h
@@ -66,6 +66,12 @@ void BindReceiver( mojo::PendingReceiver<crosapi::mojom::LacrosChromeService> receiver); + // -------------------------------------------------------------------------- + // mojo::Remote is sequence affine. The following methods are convenient + // helpers that expose pre-established Remotes that can only be used from the + // affine sequence (main thread). + // -------------------------------------------------------------------------- + // This must be called on the affine sequence. mojo::Remote<crosapi::mojom::MessageCenter>& message_center_remote() { DCHECK_CALLED_ON_VALID_SEQUENCE(affine_sequence_checker_); @@ -93,6 +99,13 @@ return hid_manager_remote_; } + // -------------------------------------------------------------------------- + // Some clients will want to use mojo::Remotes on arbitrary sequences (e.g. + // background threads). The following methods allow the client to construct a + // mojo::Remote bound to an arbitrary sequence, and pass the other endpoint of + // the Remote (mojo::PendingReceiver) to ash to set up the interface. + // -------------------------------------------------------------------------- + // This may be called on any thread. void BindScreenManagerReceiver( mojo::PendingReceiver<crosapi::mojom::ScreenManager> pending_receiver);
diff --git a/chromeos/printing/ppd_metadata_manager.cc b/chromeos/printing/ppd_metadata_manager.cc index 159f490..16f3193 100644 --- a/chromeos/printing/ppd_metadata_manager.cc +++ b/chromeos/printing/ppd_metadata_manager.cc
@@ -329,102 +329,118 @@ bool is_english_available_; }; -enum class PpdMetadataType { - kLocales, - kManufacturers, // locale-sensitive - kPrinters, // locale-sensitive - kForwardIndex, - kReverseIndex, // locale-sensitive - kUsbIndex, - kUsbVendorIds, -}; +// Represents the basename and containing directory of a piece of PPD +// metadata. Does not own any strings given to its setter methods and +// must not outlive them. +class PpdMetadataPathSpecifier { + public: + enum class Type { + kLocales, + kManufacturers, // locale-sensitive + kPrinters, // locale-sensitive + kForwardIndex, // sharded + kReverseIndex, // locale-sensitive; sharded + kUsbIndex, + kUsbVendorIds, + }; -// Control argument that fully specifies the basename and containing -// directory of a single piece of PPD metadata. -// -// * Fields should be populated appropriate to the |type|. -// * Fields are selectively read or ignored by -// PpdMetadataPathInServingRoot(). -// * This class must not outlive its |optional_tag|. -struct PpdMetadataPathSpecifier { - PpdMetadataType type; + explicit PpdMetadataPathSpecifier(Type type) + : type_(type), + printers_basename_(nullptr), + metadata_locale_(nullptr), + shard_(0), + usb_vendor_id_(0) {} + ~PpdMetadataPathSpecifier() = default; - // Used in two different ways as needed: - // 1. if |type| == kPrinters, - // then caller should populate this with the full basename of the - // target printers metadata file. Or, - // 2. if |type| is locale-sensitive and != kPrinters, - // then caller should populate this with the two-letter target - // locale (as previously advertised by the serving root). - // - // This member is a const char* rather than std::string or StringPiece - // for compatibility with base::StringPrintf(). - const char* optional_tag; + // PpdMetadataPathSpecifier is neither copyable nor movable. + PpdMetadataPathSpecifier(const PpdMetadataPathSpecifier&) = delete; + PpdMetadataPathSpecifier& operator=(const PpdMetadataPathSpecifier&) = delete; - // Used in two different ways as needed: - // 1. if |type| != kUsbIndex, - // then this is the numerical shard of the target metadata - // basename, if needed. Or, - // 2. if |type| == kUsbIndex, - // then this is the vendor ID of the the device manufacturer being - // sought. - int optional_shard; -}; - -// Names a single piece of metadata in the Chrome OS Printing serving -// root specified by |options| - i.e. a metadata basename and its -// enclosing directory (see comment for CachedParsedMetadataMap). -std::string PpdMetadataPathInServingRoot( - const PpdMetadataPathSpecifier& options) { - switch (options.type) { - case PpdMetadataType::kLocales: - return base::StringPrintf("%s/locales.json", kMetadataParentDirectory); - - case PpdMetadataType::kManufacturers: - // This type is locale-sensitive; the tag carries the locale. - DCHECK(!base::StringPiece(options.optional_tag).empty()); - return base::StringPrintf("%s/manufacturers-%s.json", - kMetadataParentDirectory, options.optional_tag); - - case PpdMetadataType::kPrinters: - // This type is locale-sensitive; in this context, the tag carries - // the full basename, which caller will have extracted from a leaf - // in manufacturers metadata. - DCHECK(!base::StringPiece(options.optional_tag).empty()); - return base::StringPrintf("%s/%s", kMetadataParentDirectory, - options.optional_tag); - - case PpdMetadataType::kForwardIndex: - DCHECK(options.optional_shard >= 0 && - options.optional_shard < kNumShards); - return base::StringPrintf("%s/index-%02d.json", kMetadataParentDirectory, - options.optional_shard); - - case PpdMetadataType::kReverseIndex: - // This type is locale-sensitive; the tag carries the locale. - DCHECK(!base::StringPiece(options.optional_tag).empty()); - DCHECK(options.optional_shard >= 0 && - options.optional_shard < kNumShards); - return base::StringPrintf("%s/reverse_index-%s-%02d.json", - kMetadataParentDirectory, options.optional_tag, - options.optional_shard); - - case PpdMetadataType::kUsbIndex: - DCHECK(options.optional_shard >= 0 && - options.optional_shard <= kSixteenBitsMaximum); - return base::StringPrintf("%s/usb-%04x.json", kMetadataParentDirectory, - options.optional_shard); - - case PpdMetadataType::kUsbVendorIds: - return base::StringPrintf("%s/usb_vendor_ids.json", - kMetadataParentDirectory); + void SetPrintersBasename(const char* const basename) { + DCHECK_EQ(type_, Type::kPrinters); + printers_basename_ = basename; } - // This function cannot fail except by maintainer error. - NOTREACHED(); + void SetMetadataLocale(const char* const locale) { + DCHECK(type_ == Type::kManufacturers || type_ == Type::kReverseIndex); + metadata_locale_ = locale; + } - return std::string(); -} + void SetUsbVendorId(const int vendor_id) { + DCHECK_EQ(type_, Type::kUsbIndex); + usb_vendor_id_ = vendor_id; + } + + void SetShard(const int shard) { + DCHECK(type_ == Type::kForwardIndex || type_ == Type::kReverseIndex); + shard_ = shard; + } + + std::string AsString() const { + switch (type_) { + case Type::kLocales: + return base::StringPrintf("%s/locales.json", kMetadataParentDirectory); + + case Type::kManufacturers: + DCHECK(metadata_locale_); + DCHECK(!base::StringPiece(metadata_locale_).empty()); + return base::StringPrintf("%s/manufacturers-%s.json", + kMetadataParentDirectory, metadata_locale_); + + case Type::kPrinters: + DCHECK(printers_basename_); + DCHECK(!base::StringPiece(printers_basename_).empty()); + return base::StringPrintf("%s/%s", kMetadataParentDirectory, + printers_basename_); + + case Type::kForwardIndex: + DCHECK(shard_ >= 0 && shard_ < kNumShards); + return base::StringPrintf("%s/index-%02d.json", + kMetadataParentDirectory, shard_); + + case Type::kReverseIndex: + DCHECK(metadata_locale_); + DCHECK(!base::StringPiece(metadata_locale_).empty()); + DCHECK(shard_ >= 0 && shard_ < kNumShards); + return base::StringPrintf("%s/reverse_index-%s-%02d.json", + kMetadataParentDirectory, metadata_locale_, + shard_); + + case Type::kUsbIndex: + DCHECK(usb_vendor_id_ >= 0 && usb_vendor_id_ <= kSixteenBitsMaximum); + return base::StringPrintf("%s/usb-%04x.json", kMetadataParentDirectory, + usb_vendor_id_); + + case Type::kUsbVendorIds: + return base::StringPrintf("%s/usb_vendor_ids.json", + kMetadataParentDirectory); + } + + // This function cannot fail except by maintainer error. + NOTREACHED(); + + return std::string(); + } + + // Private const char* members are const char* for compatibility with + // base::StringPrintf(). + private: + Type type_; + + // Populated only when |type_| == kPrinters. + // Contains the basename of the target printers metadata file. + const char* printers_basename_; + + // Populated only when |type_| is locale-sensitive and != kPrinters. + // Contains the metadata locale for which we intend to fetch metadata. + const char* metadata_locale_; + + // Populated only when |type_| is sharded. + int shard_; + + // Populated only when |type_| == kUsbIndex. + int usb_vendor_id_; +}; // Note: generally, each Get*() method is segmented into three parts: // 1. check if query can be answered immediately, @@ -460,8 +476,8 @@ return; } - const PpdMetadataPathSpecifier options = {PpdMetadataType::kLocales}; - const std::string metadata_name = PpdMetadataPathInServingRoot(options); + PpdMetadataPathSpecifier path(PpdMetadataPathSpecifier::Type::kLocales); + const std::string metadata_name = path.AsString(); PrinterConfigCache::FetchCallback fetch_cb = base::BindOnce(&PpdMetadataManagerImpl::OnLocalesFetched, @@ -477,9 +493,10 @@ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(!metadata_locale_.empty()); - const PpdMetadataPathSpecifier options = {PpdMetadataType::kManufacturers, - metadata_locale_.c_str()}; - const std::string metadata_name = PpdMetadataPathInServingRoot(options); + PpdMetadataPathSpecifier path( + PpdMetadataPathSpecifier::Type::kManufacturers); + path.SetMetadataLocale(metadata_locale_.c_str()); + const std::string metadata_name = path.AsString(); if (MapHasValueFresherThan(cached_manufacturers_, metadata_name, clock_->Now() - age)) { @@ -553,9 +570,9 @@ return; } - const PpdMetadataPathSpecifier options = {PpdMetadataType::kUsbIndex, - nullptr, vendor_id}; - const std::string metadata_name = PpdMetadataPathInServingRoot(options); + PpdMetadataPathSpecifier path(PpdMetadataPathSpecifier::Type::kUsbIndex); + path.SetUsbVendorId(vendor_id); + const std::string metadata_name = path.AsString(); if (MapHasValueFresherThan(cached_usb_indices_, metadata_name, clock_->Now() - age)) { @@ -572,8 +589,9 @@ void GetUsbManufacturerName(int vendor_id, base::TimeDelta age, GetUsbManufacturerNameCallback cb) override { - const PpdMetadataPathSpecifier options = {PpdMetadataType::kUsbVendorIds}; - const std::string metadata_name = PpdMetadataPathInServingRoot(options); + PpdMetadataPathSpecifier path( + PpdMetadataPathSpecifier::Type::kUsbVendorIds); + const std::string metadata_name = path.AsString(); if (MapHasValueFresherThan(cached_usb_vendor_id_map_, metadata_name, clock_->Now() - age)) { @@ -593,11 +611,11 @@ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(!metadata_locale_.empty()); - const PpdMetadataPathSpecifier reverse_index_options = { - PpdMetadataType::kReverseIndex, metadata_locale_.c_str(), - IndexShard(effective_make_and_model)}; - const std::string metadata_name = - PpdMetadataPathInServingRoot(reverse_index_options); + PpdMetadataPathSpecifier path( + PpdMetadataPathSpecifier::Type::kReverseIndex); + path.SetMetadataLocale(metadata_locale_.c_str()); + path.SetShard(IndexShard(effective_make_and_model)); + const std::string metadata_name = path.AsString(); if (MapHasValueFresherThan(cached_reverse_indices_, metadata_name, clock_->Now() - age)) { @@ -632,10 +650,10 @@ } // We need to name the manufacturers metadata manually to store it. - const PpdMetadataPathSpecifier options = {PpdMetadataType::kManufacturers, - metadata_locale_.c_str()}; - const std::string manufacturers_name = - PpdMetadataPathInServingRoot(options); + PpdMetadataPathSpecifier path( + PpdMetadataPathSpecifier::Type::kManufacturers); + path.SetMetadataLocale(metadata_locale_.c_str()); + const std::string manufacturers_name = path.AsString(); ParsedMetadataWithTimestamp<ParsedManufacturers> value = {clock_->Now(), parsed.value()}; @@ -764,10 +782,11 @@ // |manufacturer|. base::Optional<std::string> GetPrintersMetadataName( base::StringPiece manufacturer) { - const PpdMetadataPathSpecifier manufacturers_options = { - PpdMetadataType::kManufacturers, metadata_locale_.c_str()}; + PpdMetadataPathSpecifier manufacturers_path( + PpdMetadataPathSpecifier::Type::kManufacturers); + manufacturers_path.SetMetadataLocale(metadata_locale_.c_str()); const std::string manufacturers_metadata_name = - PpdMetadataPathInServingRoot(manufacturers_options); + manufacturers_path.AsString(); if (!cached_manufacturers_.contains(manufacturers_metadata_name)) { // This is likely a bug: we don't have the expected manufacturers // metadata. @@ -781,10 +800,11 @@ return base::nullopt; } - const PpdMetadataPathSpecifier printers_options = { - PpdMetadataType::kPrinters, - manufacturers.value.at(manufacturer).c_str()}; - return PpdMetadataPathInServingRoot(printers_options); + PpdMetadataPathSpecifier printers_path( + PpdMetadataPathSpecifier::Type::kPrinters); + printers_path.SetPrintersBasename( + manufacturers.value.at(manufacturer).c_str()); + return printers_path.AsString(); } // Called by one of @@ -887,11 +907,10 @@ ForwardIndexSearchStatus SearchForwardIndicesForOneEmm() { const ForwardIndexSearchContext& context = forward_index_search_queue_.CurrentContext(); - const PpdMetadataPathSpecifier options = {PpdMetadataType::kForwardIndex, - nullptr, - IndexShard(context.CurrentEmm())}; - const std::string forward_index_name = - PpdMetadataPathInServingRoot(options); + PpdMetadataPathSpecifier path( + PpdMetadataPathSpecifier::Type::kForwardIndex); + path.SetShard(IndexShard(context.CurrentEmm())); + const std::string forward_index_name = path.AsString(); if (MapHasValueFresherThan(cached_forward_indices_, forward_index_name, context.MaxAge())) {
diff --git a/chromeos/printing/ppd_metadata_parser.cc b/chromeos/printing/ppd_metadata_parser.cc index 1e85016..7da4dabd 100644 --- a/chromeos/printing/ppd_metadata_parser.cc +++ b/chromeos/printing/ppd_metadata_parser.cc
@@ -240,7 +240,7 @@ // Firstly, we unnest the dictionary keyed by "ppdIndex." base::Optional<base::Value> ppd_index = ParseJsonAndUnnestKey( forward_index_json, "ppdIndex", base::Value::Type::DICTIONARY); - if (!ppd_index || ppd_index->DictSize() == 0) { + if (!ppd_index.has_value()) { return base::nullopt; } @@ -264,7 +264,7 @@ base::Optional<ParsedUsbIndex> ParseUsbIndex(base::StringPiece usb_index_json) { base::Optional<base::Value> usb_index = ParseJsonAndUnnestKey( usb_index_json, "usbIndex", base::Value::Type::DICTIONARY); - if (!usb_index || usb_index->DictSize() == 0) { + if (!usb_index.has_value()) { return base::nullopt; } @@ -293,7 +293,7 @@ base::StringPiece usb_vendor_id_map_json) { base::Optional<base::Value> as_value = ParseJsonAndUnnestKey( usb_vendor_id_map_json, "entries", base::Value::Type::LIST); - if (!as_value.has_value() || as_value->GetList().empty()) { + if (!as_value.has_value()) { return base::nullopt; } @@ -348,7 +348,7 @@ base::StringPiece reverse_index_json) { const base::Optional<base::Value> makes_and_models = ParseJsonAndUnnestKey( reverse_index_json, "reverseIndex", base::Value::Type::DICTIONARY); - if (!makes_and_models.has_value() || makes_and_models->DictSize() == 0) { + if (!makes_and_models.has_value()) { return base::nullopt; }
diff --git a/chromeos/printing/ppd_provider_v3.cc b/chromeos/printing/ppd_provider_v3.cc index 85a3768..ed4afbc2 100644 --- a/chromeos/printing/ppd_provider_v3.cc +++ b/chromeos/printing/ppd_provider_v3.cc
@@ -8,11 +8,15 @@ #include <vector> #include "base/containers/queue.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/memory/scoped_refptr.h" #include "base/notreached.h" +#include "base/strings/strcat.h" #include "base/task/post_task.h" #include "base/task/task_traits.h" #include "base/task/thread_pool.h" +#include "base/threading/scoped_blocking_call.h" #include "base/time/time.h" #include "chromeos/printing/epson_driver_matching.h" #include "chromeos/printing/ppd_cache.h" @@ -20,7 +24,9 @@ #include "chromeos/printing/ppd_provider_v3.h" #include "chromeos/printing/printer_config_cache.h" #include "chromeos/printing/printer_configuration.h" +#include "chromeos/printing/printing_constants.h" #include "net/base/backoff_entry.h" +#include "net/base/filename_util.h" namespace chromeos { namespace { @@ -57,6 +63,38 @@ // using the generic Epson PPD. const char kEpsonGenericEmm[] = "epson generic escpr printer"; +bool PpdReferenceIsWellFormed(const Printer::PpdReference& reference) { + int filled_fields = 0; + if (!reference.user_supplied_ppd_url.empty()) { + ++filled_fields; + GURL tmp_url(reference.user_supplied_ppd_url); + if (!tmp_url.is_valid() || !tmp_url.SchemeIs("file")) { + LOG(ERROR) << "Invalid url for a user-supplied ppd: " + << reference.user_supplied_ppd_url + << " (must be a file:// URL)"; + return false; + } + } + if (!reference.effective_make_and_model.empty()) { + ++filled_fields; + } + + // All effective-make-and-model strings should be lowercased, since v2. + // Since make-and-model strings could include non-Latin chars, only checking + // that it excludes all upper-case chars A-Z. + if (!std::all_of(reference.effective_make_and_model.begin(), + reference.effective_make_and_model.end(), + [](char c) -> bool { return !base::IsAsciiUpper(c); })) { + return false; + } + // Should have exactly one non-empty field. + return filled_fields == 1; +} + +std::string PpdPathInServingRoot(base::StringPiece ppd_basename) { + return base::StrCat({"ppds_for_metadata_v3/", ppd_basename}); +} + // Helper struct for PpdProviderImpl. Allows PpdProviderImpl to defer // its public method calls, which PpdProviderImpl will do when the // PpdMetadataManager is not ready to deal with locale-sensitive PPD @@ -134,7 +172,7 @@ std::unique_ptr<PrinterConfigCache> config_cache) : browser_locale_(std::string(browser_locale)), version_(current_version), - cache_(cache), + ppd_cache_(cache), deferral_context_(std::make_unique<MethodDeferralContext>()), metadata_manager_(std::move(metadata_manager)), config_cache_(std::move(config_cache)), @@ -234,8 +272,14 @@ TryToResolvePpdReferenceFromUsbIndices(std::move(context)); } - // This method depends on a successful prior call to - // ResolvePpdReference(). + // This method invokes |cb| with the contents of a successfully + // retrieved PPD appropriate for |reference|. + // + // As a side effect, this method may attempt + // * to read a PPD from the user's files (if the PPD is + // user-supplied) or + // * to download a PPD from the serving root (if the PPD is not + // user-supplied). void ResolvePpd(const Printer::PpdReference& reference, ResolvePpdCallback cb) override { // In v3 metadata, effective-make-and-model strings are only @@ -243,7 +287,27 @@ Printer::PpdReference lowercased_reference(reference); lowercased_reference.effective_make_and_model = base::ToLowerASCII(lowercased_reference.effective_make_and_model); - // TODO(crbug.com/888189): implement this. + + if (!PpdReferenceIsWellFormed(lowercased_reference)) { + base::SequencedTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(std::move(cb), + CallbackResultCode::INTERNAL_ERROR, "")); + return; + } + + if (!lowercased_reference.user_supplied_ppd_url.empty()) { + ResolveUserSuppliedPpd(lowercased_reference, std::move(cb)); + return; + } + + std::vector<std::string> target_emm = { + lowercased_reference.effective_make_and_model}; + auto callback = + base::BindOnce(&PpdProviderImpl::OnPpdBasenameSoughtFromForwardIndex, + weak_factory_.GetWeakPtr(), + std::move(lowercased_reference), std::move(cb)); + metadata_manager_->FindAllEmmsAvailableInIndex(target_emm, kMaxDataAge, + std::move(callback)); } void ReverseLookup(const std::string& effective_make_and_model, @@ -319,6 +383,39 @@ ResolvePpdReferenceCallback cb; }; + // Used internally in ResolvePpd(). Describes the physical, bitwise + // origin of a PPD. + // + // Example: a PPD previously downloaded from the serving root is saved + // into the local PpdCache. A subsequent call to ResolvePpd() searches + // the local PpdCache and returns this PPD. Internally, the methods + // that comprise ResolvePpd() treat this as kFromPpdCache. + enum class ResolvedPpdOrigin { + kFromServingRoot, + kFromUserSuppliedUrl, + kFromPpdCache, + }; + + // Returns an empty string on failure. + static std::string FetchFile(const GURL& ppd_url) { + DCHECK(ppd_url.is_valid()); + DCHECK(ppd_url.SchemeIs("file")); + base::ScopedBlockingCall scoped_blocking_call( + FROM_HERE, base::BlockingType::MAY_BLOCK); + + base::FilePath path; + if (!net::FileURLToFilePath(ppd_url, &path)) { + LOG(ERROR) << "Not a valid file URL."; + return ""; + } + + std::string file_contents; + if (!base::ReadFileToString(path, &file_contents)) { + return ""; + } + return file_contents; + } + // Readies |metadata_manager_| to call methods which require a // successful callback from PpdMetadataManager::GetLocale(). // @@ -596,6 +693,247 @@ TryToResolvePpdReferenceFromUsbIndices(std::move(context)); } + // Continues a prior call to ResolvePpd(). + // + // Stores a PPD with |ppd_contents| in the PPD Cache. + // Caller must provide nonempty |ppd_basename| when |ppd_origin| + // identifies the PPD as coming from the the serving root. + void StorePpdWithContents(const std::string& ppd_contents, + base::Optional<std::string> ppd_basename, + ResolvedPpdOrigin ppd_origin, + Printer::PpdReference reference) { + switch (ppd_origin) { + case ResolvedPpdOrigin::kFromPpdCache: + // This very PPD was retrieved from the local PpdCache; there's no + // point in storing it again. + return; + + case ResolvedPpdOrigin::kFromServingRoot: + // To service the two-step "dereference" of resolving a PPD from + // the serving root, we need to Store() the basename of this PPD + // in the local PpdCache. + DCHECK(ppd_basename.has_value()); + DCHECK(!ppd_basename->empty()); + DCHECK(!reference.effective_make_and_model.empty()); + + ppd_cache_->Store(PpdBasenameToCacheKey(ppd_basename.value()), + ppd_contents); + ppd_cache_->Store(PpdReferenceToCacheKey(reference), + ppd_basename.value()); + break; + + case ResolvedPpdOrigin::kFromUserSuppliedUrl: + // No special considerations for a user-supplied PPD; we can + // Store() it directly by mapping the user-supplied URI to the + // PPD contents. + DCHECK(!reference.user_supplied_ppd_url.empty()); + + ppd_cache_->Store(PpdReferenceToCacheKey(reference), ppd_contents); + break; + } + } + + // Continues a prior call to ResolvePpd(). + // + // Called when we have the contents of the PPD being resolved; we are + // on the cusp of being able to invoke the |cb|. + void ResolvePpdWithContents(ResolvedPpdOrigin ppd_origin, + base::Optional<std::string> ppd_basename, + std::string ppd_contents, + Printer::PpdReference reference, + ResolvePpdCallback cb) { + DCHECK(!ppd_contents.empty()); + + if (ppd_contents.size() > kMaxPpdSizeBytes) { + base::SequencedTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(std::move(cb), CallbackResultCode::PPD_TOO_LARGE, "")); + return; + } + + StorePpdWithContents(ppd_contents, std::move(ppd_basename), ppd_origin, + std::move(reference)); + base::SequencedTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindOnce(std::move(cb), CallbackResultCode::SUCCESS, + std::move(ppd_contents))); + } + + // Continues a prior call to ResolvePpd(). + // + // Called back by PrinterConfigCache::Fetch() when we've fetched + // a PPD from the serving root. + void OnPpdFetchedFromServingRoot( + Printer::PpdReference reference, + ResolvePpdCallback cb, + const PrinterConfigCache::FetchResult& result) { + if (!result.succeeded || result.contents.empty()) { + base::SequencedTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(std::move(cb), CallbackResultCode::SERVER_ERROR, "")); + return; + } + + ResolvePpdWithContents(ResolvedPpdOrigin::kFromServingRoot, + /*ppd_basename=*/result.key, + /*ppd_contents=*/result.contents, + std::move(reference), std::move(cb)); + } + + // Continues a prior call to ResolvePpd(). + // + // Called when we seek a mapping from an effective-make-and-model + // string to a PPD basename by querying the local PpdCache. + void OnPpdBasenameSoughtInPpdCache(Printer::PpdReference reference, + ResolvePpdCallback cb, + const PpdCache::FindResult& result) { + if (!result.success || result.contents.empty()) { + base::SequencedTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(std::move(cb), CallbackResultCode::NOT_FOUND, "")); + return; + } + + std::string cache_key = PpdBasenameToCacheKey(result.contents); + ppd_cache_->Find( + cache_key, + base::BindOnce(&PpdProviderImpl::OnPpdFromServingRootSoughtInPpdCache, + weak_factory_.GetWeakPtr(), + /*ppd_basename=*/result.contents, std::move(reference), + std::move(cb))); + } + + // Continues a prior call to ResolvePpd(). + // + // Called when we have a PPD basename already and seek its contents + // in the local PpdCache. + void OnPpdFromServingRootSoughtInPpdCache( + const std::string& ppd_basename, + Printer::PpdReference reference, + ResolvePpdCallback cb, + const PpdCache::FindResult& result) { + if (!result.success || result.contents.empty()) { + // We have the PPD basename, but not the contents of the PPD + // itself in our local PpdCache. We must seek out the contents + // from the serving root. + auto callback = base::BindOnce( + &PpdProviderImpl::OnPpdFetchedFromServingRoot, + weak_factory_.GetWeakPtr(), std::move(reference), std::move(cb)); + config_cache_->Fetch(PpdPathInServingRoot(ppd_basename), kMaxDataAge, + std::move(callback)); + return; + } + + ResolvePpdWithContents(ResolvedPpdOrigin::kFromPpdCache, ppd_basename, + result.contents, std::move(reference), + std::move(cb)); + } + + // Continues a prior call to ResolvePpd(). + // + // Called back by PpdMetadataManager::FindAllEmmsAvailableInIndex(). + // + // 1. Maps |reference|::effective_make_and_model to a PPD basename. + // a. Attempts to do so with fresh forward index metadata if + // possible, searching |forward_index_subset| for the best + // available PPD. + // b. Falls back to directly querying the local PpdCache instance, + // e.g. if the network is unreachable. + // 2. Uses basename derived in previous step to retrieve the + // appropriate PPD from the local PpdCache instance. + void OnPpdBasenameSoughtFromForwardIndex( + Printer::PpdReference reference, + ResolvePpdCallback cb, + const base::flat_map<std::string, ParsedIndexValues>& + forward_index_subset) { + const ParsedIndexLeaf* const leaf = FirstAllowableParsedIndexLeaf( + reference.effective_make_and_model, forward_index_subset); + if (!leaf || leaf->ppd_basename.empty()) { + // The forward index doesn't advise what the best fit PPD is for + // |reference|::effective_make_and_model. We can look toward the + // local PpdCache to see if we saved it previously. + std::string cache_key = PpdReferenceToCacheKey(reference); + ppd_cache_->Find( + cache_key, + base::BindOnce(&PpdProviderImpl::OnPpdBasenameSoughtInPpdCache, + weak_factory_.GetWeakPtr(), std::move(reference), + std::move(cb))); + return; + } + + // The forward index does advertise a best-fit PPD basename. We + // check the local PpdCache to see if we already have it. + ppd_cache_->Find( + PpdBasenameToCacheKey(leaf->ppd_basename), + base::BindOnce(&PpdProviderImpl::OnPpdFromServingRootSoughtInPpdCache, + weak_factory_.GetWeakPtr(), leaf->ppd_basename, + std::move(reference), std::move(cb))); + } + + // Continues a prior call to ResolvePpd(). + // + // Called when we finish searching the PpdCache for a user-supplied + // PPD. This contrasts with the slightly more involved two-step + // "dereference" process in searching the PpdCache for a PPD retrieved + // from the serving root. + void OnUserSuppliedPpdSoughtInPpdCache(Printer::PpdReference reference, + ResolvePpdCallback cb, + const PpdCache::FindResult& result) { + if (!result.success) { + base::SequencedTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(std::move(cb), CallbackResultCode::NOT_FOUND, "")); + return; + } + + ResolvePpdWithContents(ResolvedPpdOrigin::kFromPpdCache, + /*ppd_basename=*/base::nullopt, result.contents, + std::move(reference), std::move(cb)); + } + + // Continues a prior call to ResolvePpd(). + // + // Called when we finish fetching a PPD file from device-local storage + // (e.g. from the user's home directory, not from the PpdCache). + void OnUserSuppliedPpdFetched(Printer::PpdReference reference, + ResolvePpdCallback cb, + const std::string& result) { + if (result.empty()) { + // We didn't find a nonempty PPD at the location specified by the + // user. The next step is to try searching the PpdCache. + std::string cache_key = PpdReferenceToCacheKey(reference); + ppd_cache_->Find( + cache_key, + base::BindOnce(&PpdProviderImpl::OnUserSuppliedPpdSoughtInPpdCache, + weak_factory_.GetWeakPtr(), std::move(reference), + std::move(cb))); + return; + } + + ResolvePpdWithContents(ResolvedPpdOrigin::kFromUserSuppliedUrl, + /*ppd_basename=*/base::nullopt, result, + std::move(reference), std::move(cb)); + } + + // Continues a prior call to ResolvePpd(). + // + // 1. Attempts to invoke |cb| with the file named by + // |reference|::user_suplied_ppd_url - i.e. a live fetch from + // wherever the user saved the PPD. + // 2. Attempts to search the local PpdCache instance for the file + // whose cache key was built from + // |reference|::user_supplied_ppd_url. + void ResolveUserSuppliedPpd(Printer::PpdReference reference, + ResolvePpdCallback cb) { + DCHECK(!reference.user_supplied_ppd_url.empty()); + GURL url(reference.user_supplied_ppd_url); + + file_task_runner_->PostTaskAndReplyWithResult( + FROM_HERE, base::BindOnce(&FetchFile, url), + base::BindOnce(&PpdProviderImpl::OnUserSuppliedPpdFetched, + weak_factory_.GetWeakPtr(), std::move(reference), + std::move(cb))); + } + // Continues a prior call to ResolvePpdLicense(). // This callback is fed to // PpdMetadataManager::FindAllEmmsAvailableInIndexCallback(). @@ -632,7 +970,7 @@ const base::Version version_; // Provides PPD storage on-device. - scoped_refptr<PpdCache> cache_; + scoped_refptr<PpdCache> ppd_cache_; // Used to // 1. to determine if |this| should defer locale-sensitive public @@ -655,56 +993,35 @@ base::WeakPtrFactory<PpdProviderImpl> weak_factory_{this}; }; -// Copied directly from v2 PpdProvider -// TODO(crbug.com/888189): figure out where this fits in the big picture -bool PpdReferenceIsWellFormed(const Printer::PpdReference& reference) { - int filled_fields = 0; - if (!reference.user_supplied_ppd_url.empty()) { - ++filled_fields; - GURL tmp_url(reference.user_supplied_ppd_url); - if (!tmp_url.is_valid() || !tmp_url.SchemeIs("file")) { - LOG(ERROR) << "Invalid url for a user-supplied ppd: " - << reference.user_supplied_ppd_url - << " (must be a file:// URL)"; - return false; - } - } - if (!reference.effective_make_and_model.empty()) { - ++filled_fields; - } - - // All effective-make-and-model strings should be lowercased, since v2. - // Since make-and-model strings could include non-Latin chars, only checking - // that it excludes all upper-case chars A-Z. - if (!std::all_of(reference.effective_make_and_model.begin(), - reference.effective_make_and_model.end(), - [](char c) -> bool { return !base::IsAsciiUpper(c); })) { - return false; - } - // Should have exactly one non-empty field. - return filled_fields == 1; -} - } // namespace PrinterSearchData::PrinterSearchData() = default; PrinterSearchData::PrinterSearchData(const PrinterSearchData& other) = default; PrinterSearchData::~PrinterSearchData() = default; -// static; copied directly from v2 PpdProvider -// TODO(crbug.com/888189): figure out where this fits in the big picture +// static +// +// Used in production but also exposed for testing. std::string PpdProvider::PpdReferenceToCacheKey( const Printer::PpdReference& reference) { DCHECK(PpdReferenceIsWellFormed(reference)); // The key prefixes here are arbitrary, but ensure we can't have an (unhashed) // collision between keys generated from different PpdReference fields. if (!reference.effective_make_and_model.empty()) { - return std::string("em:") + reference.effective_make_and_model; + return base::StrCat( + {"emm_for_metadata_v3:", reference.effective_make_and_model}); } else { - return std::string("up:") + reference.user_supplied_ppd_url; + // Retains the legacy salt from the v2 PpdProvider. This is done + // to minimize user breakage when we roll out the v3 PpdProvider. + return base::StrCat({"up:", reference.user_supplied_ppd_url}); } } +// Used in production but also exposed for testing. +std::string PpdBasenameToCacheKey(base::StringPiece ppd_basename) { + return base::StrCat({"ppd_basename_for_metadata_v3:", ppd_basename}); +} + // static scoped_refptr<PpdProvider> PpdProvider::Create( const std::string& browser_locale,
diff --git a/chromeos/printing/ppd_provider_v3.h b/chromeos/printing/ppd_provider_v3.h index 21fcba75..bba29200 100644 --- a/chromeos/printing/ppd_provider_v3.h +++ b/chromeos/printing/ppd_provider_v3.h
@@ -25,6 +25,13 @@ std::unique_ptr<PpdMetadataManager> metadata_manager, std::unique_ptr<PrinterConfigCache> config_cache); +// TODO(crbug.com/888189): make this free function a static method +// +// Used to "dereference" the PPD previously named by the cache key from +// Printer::PpdReference::effective_make_and_model. +CHROMEOS_EXPORT std::string PpdBasenameToCacheKey( + base::StringPiece ppd_basename); + } // namespace chromeos #endif // CHROMEOS_PRINTING_PPD_PROVIDER_V3_H_
diff --git a/chromeos/printing/ppd_provider_v3_unittest.cc b/chromeos/printing/ppd_provider_v3_unittest.cc index 9839ae3..4a71037 100644 --- a/chromeos/printing/ppd_provider_v3_unittest.cc +++ b/chromeos/printing/ppd_provider_v3_unittest.cc
@@ -808,7 +808,7 @@ TEST_F(PpdProviderTest, ResolveServerKeyPpd) { auto provider = CreateProvider({"en", PpdCacheRunLocation::kInBackgroundThreads, - PropagateLocaleToMetadataManager::kDoNotPropagate}); + PropagateLocaleToMetadataManager::kDoPropagate}); StartFakePpdServer(); Printer::PpdReference ref; ref.effective_make_and_model = "printer_b_ref"; @@ -820,10 +820,19 @@ task_environment_.RunUntilIdle(); ASSERT_EQ(2UL, captured_resolve_ppd_.size()); - EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_ppd_[0].code); - EXPECT_EQ(kCupsFilter2PpdContents, captured_resolve_ppd_[0].ppd_contents); - EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_ppd_[1].code); - EXPECT_EQ("c", captured_resolve_ppd_[1].ppd_contents); + + // ResolvePpd() works in several asynchronous steps, so order of + // return is not guaranteed. + EXPECT_THAT( + captured_resolve_ppd_, + UnorderedElementsAre( + AllOf(Field(&CapturedResolvePpdResults::code, + PpdProvider::CallbackResultCode::SUCCESS), + Field(&CapturedResolvePpdResults::ppd_contents, + StrEq(kCupsFilter2PpdContents))), + AllOf(Field(&CapturedResolvePpdResults::code, + PpdProvider::CallbackResultCode::SUCCESS), + Field(&CapturedResolvePpdResults::ppd_contents, StrEq("c"))))); } // Test that we *don't* resolve a ppd URL over non-file schemes. It's not clear @@ -833,12 +842,13 @@ TEST_F(PpdProviderTest, ResolveUserSuppliedUrlPpdFromNetworkFails) { auto provider = CreateProvider({"en", PpdCacheRunLocation::kInBackgroundThreads, - PropagateLocaleToMetadataManager::kDoNotPropagate}); + PropagateLocaleToMetadataManager::kDoPropagate}); StartFakePpdServer(); Printer::PpdReference ref; - // TODO(crbug.com/888189): give |ref| a nonempty - // |user_supplied_ppd_url|. + // PpdProvider::ResolvePpd() shall fail if a user-supplied PPD URL + // does not begin with the "file://" scheme. + ref.user_supplied_ppd_url = "nonfilescheme://unused"; provider->ResolvePpd(ref, base::BindOnce(&PpdProviderTest::CaptureResolvePpd, base::Unretained(this))); task_environment_.RunUntilIdle(); @@ -854,7 +864,7 @@ TEST_F(PpdProviderTest, ResolveUserSuppliedUrlPpdFromFile) { auto provider = CreateProvider({"en", PpdCacheRunLocation::kInBackgroundThreads, - PropagateLocaleToMetadataManager::kDoNotPropagate}); + PropagateLocaleToMetadataManager::kDoPropagate}); base::ScopedTempDir temp_dir; ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); base::FilePath filename = temp_dir.GetPath().Append("my_spiffy.ppd"); @@ -880,7 +890,7 @@ TEST_F(PpdProviderTest, ResolvedPpdsGetCached) { auto provider = CreateProvider({"en", PpdCacheRunLocation::kInBackgroundThreads, - PropagateLocaleToMetadataManager::kDoNotPropagate}); + PropagateLocaleToMetadataManager::kDoPropagate}); std::string user_ppd_contents = "Woohoo"; Printer::PpdReference ref; { @@ -909,9 +919,8 @@ // Recreate the provider to make sure we don't have any memory caches which // would mask problems with disk persistence. - provider = - CreateProvider({"en", PpdCacheRunLocation::kInBackgroundThreads, - PropagateLocaleToMetadataManager::kDoNotPropagate}); + provider = CreateProvider({"en", PpdCacheRunLocation::kInBackgroundThreads, + PropagateLocaleToMetadataManager::kDoPropagate}); // Re-resolve. provider->ResolvePpd(ref, base::BindOnce(&PpdProviderTest::CaptureResolvePpd, @@ -1028,21 +1037,31 @@ EXPECT_EQ(PpdProvider::NOT_FOUND, failed_capture.code); } -// If we have a fresh entry in the cache, we shouldn't need to go out to the -// network at all to successfully resolve a ppd. -TEST_F(PpdProviderTest, FreshCacheHitNoNetworkTraffic) { +// Verifies that we never attempt to re-download a PPD that we +// previously retrieved from the serving root. The Chrome OS Printing +// Team plans to keep PPDs immutable inside the serving root, so +// PpdProvider should always prefer to retrieve a PPD from the PpdCache +// when it's possible to do so. +TEST_F(PpdProviderTest, PreferToResolvePpdFromPpdCacheOverServingRoot) { // Explicitly *not* starting a fake server. std::string cached_ppd_contents = "These cached contents are different from what's being served"; auto provider = CreateProvider({"en", PpdCacheRunLocation::kOnTestThread, - PropagateLocaleToMetadataManager::kDoNotPropagate}); + PropagateLocaleToMetadataManager::kDoPropagate}); Printer::PpdReference ref; ref.effective_make_and_model = "printer_a_ref"; std::string cache_key = PpdProvider::PpdReferenceToCacheKey(ref); + // Cache exists, and is just created, so should be fresh. - ppd_cache_->StoreForTesting(PpdProvider::PpdReferenceToCacheKey(ref), + // + // PPD basename is taken from value specified in forward index shard + // defined in server_contents(). + const std::string ppd_basename = "printer_a.ppd"; + ppd_cache_->StoreForTesting(PpdBasenameToCacheKey(ppd_basename), cached_ppd_contents, base::TimeDelta()); + ppd_cache_->StoreForTesting(PpdProvider::PpdReferenceToCacheKey(ref), + ppd_basename, base::TimeDelta()); task_environment_.RunUntilIdle(); provider->ResolvePpd(ref, base::BindOnce(&PpdProviderTest::CaptureResolvePpd, base::Unretained(this))); @@ -1055,94 +1074,6 @@ EXPECT_EQ(cached_ppd_contents, captured_resolve_ppd_[0].ppd_contents); } -// If we have a stale cache entry and a good network connection, does the cache -// get refreshed during a resolution? -TEST_F(PpdProviderTest, StaleCacheGetsRefreshed) { - std::string cached_ppd_contents = - "These cached contents are different from what's being served"; - auto provider = - CreateProvider({"en", PpdCacheRunLocation::kOnTestThread, - PropagateLocaleToMetadataManager::kDoNotPropagate}); - StartFakePpdServer(); - // printer_ref_a resolves to kCupsFilterPpdContents on the server. - std::string expected_ppd = kCupsFilterPpdContents; - Printer::PpdReference ref; - ref.effective_make_and_model = "printer_a_ref"; - std::string cache_key = PpdProvider::PpdReferenceToCacheKey(ref); - // Cache exists, and is 6 months old, so really stale. - ppd_cache_->StoreForTesting(PpdProvider::PpdReferenceToCacheKey(ref), - cached_ppd_contents, - base::TimeDelta::FromDays(180)); - task_environment_.RunUntilIdle(); - provider->ResolvePpd(ref, base::BindOnce(&PpdProviderTest::CaptureResolvePpd, - base::Unretained(this))); - task_environment_.RunUntilIdle(); - ASSERT_EQ(1UL, captured_resolve_ppd_.size()); - - // Should get the served results back, not the stale cached ones. - EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_ppd_[0].code); - EXPECT_EQ(captured_resolve_ppd_[0].ppd_contents, expected_ppd); - - // Check that the cache was also updated. - PpdCache::FindResult captured_find_result; - // This is just a complicated syntax around the idea "use the Find callback to - // save the result in captured_find_result. - ppd_cache_->Find(PpdProvider::PpdReferenceToCacheKey(ref), - base::BindOnce( - [](PpdCache::FindResult* captured_find_result, - const PpdCache::FindResult& find_result) { - *captured_find_result = find_result; - }, - &captured_find_result)); - task_environment_.RunUntilIdle(); - EXPECT_EQ(captured_find_result.success, true); - EXPECT_EQ(captured_find_result.contents, expected_ppd); - EXPECT_LT(captured_find_result.age, base::TimeDelta::FromDays(1)); -} - -// Test that, if we have an old entry in the cache that needs to be refreshed, -// and we fail to contact the server, we still use the cached version. -TEST_F(PpdProviderTest, StaleCacheGetsUsedIfNetworkFails) { - // Note that we're explicitly *not* starting the Fake ppd server in this test. - std::string cached_ppd_contents = - "These cached contents are different from what's being served"; - auto provider = - CreateProvider({"en", PpdCacheRunLocation::kOnTestThread, - PropagateLocaleToMetadataManager::kDoNotPropagate}); - Printer::PpdReference ref; - ref.effective_make_and_model = "printer_a_ref"; - std::string cache_key = PpdProvider::PpdReferenceToCacheKey(ref); - // Cache exists, and is 6 months old, so really stale. - ppd_cache_->StoreForTesting(PpdProvider::PpdReferenceToCacheKey(ref), - cached_ppd_contents, - base::TimeDelta::FromDays(180)); - task_environment_.RunUntilIdle(); - provider->ResolvePpd(ref, base::BindOnce(&PpdProviderTest::CaptureResolvePpd, - base::Unretained(this))); - task_environment_.RunUntilIdle(); - ASSERT_EQ(1UL, captured_resolve_ppd_.size()); - - // Should successfully resolve from the cache, even though it's stale. - EXPECT_EQ(PpdProvider::SUCCESS, captured_resolve_ppd_[0].code); - EXPECT_EQ(cached_ppd_contents, captured_resolve_ppd_[0].ppd_contents); - - // Check that the cache is *not* updated; it should remain stale. - PpdCache::FindResult captured_find_result; - // This is just a complicated syntax around the idea "use the Find callback to - // save the result in captured_find_result. - ppd_cache_->Find(PpdProvider::PpdReferenceToCacheKey(ref), - base::BindOnce( - [](PpdCache::FindResult* captured_find_result, - const PpdCache::FindResult& find_result) { - *captured_find_result = find_result; - }, - &captured_find_result)); - task_environment_.RunUntilIdle(); - EXPECT_EQ(captured_find_result.success, true); - EXPECT_EQ(captured_find_result.contents, cached_ppd_contents); - EXPECT_GT(captured_find_result.age, base::TimeDelta::FromDays(179)); -} - // For user-provided ppds, we should always use the latest version on // disk if it still exists there. TEST_F(PpdProviderTest, UserPpdAlwaysRefreshedIfAvailable) { @@ -1151,7 +1082,7 @@ std::string disk_ppd_contents = "Updated Ppd Contents"; auto provider = CreateProvider({"en", PpdCacheRunLocation::kOnTestThread, - PropagateLocaleToMetadataManager::kDoNotPropagate}); + PropagateLocaleToMetadataManager::kDoPropagate}); StartFakePpdServer(); ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); base::FilePath filename = temp_dir.GetPath().Append("my_spiffy.ppd");
diff --git a/chromeos/services/secure_channel/BUILD.gn b/chromeos/services/secure_channel/BUILD.gn index 9b3eb95..577fe7b 100644 --- a/chromeos/services/secure_channel/BUILD.gn +++ b/chromeos/services/secure_channel/BUILD.gn
@@ -104,8 +104,16 @@ "multiplexed_channel.h", "multiplexed_channel_impl.cc", "multiplexed_channel_impl.h", + "nearby_connection_manager.cc", + "nearby_connection_manager.h", + "nearby_connection_manager_impl.cc", + "nearby_connection_manager_impl.h", + "nearby_initiator_connection_attempt.cc", + "nearby_initiator_connection_attempt.h", "nearby_initiator_failure_type.cc", "nearby_initiator_failure_type.h", + "nearby_initiator_operation.cc", + "nearby_initiator_operation.h", "pending_ble_connection_request_base.h", "pending_ble_initiator_connection_request.cc", "pending_ble_initiator_connection_request.h", @@ -119,6 +127,8 @@ "pending_connection_request_base.h", "pending_connection_request_delegate.cc", "pending_connection_request_delegate.h", + "pending_nearby_initiator_connection_request.cc", + "pending_nearby_initiator_connection_request.h", "raw_eid_generator.h", "raw_eid_generator_impl.cc", "raw_eid_generator_impl.h", @@ -219,6 +229,8 @@ "fake_message_receiver.h", "fake_multiplexed_channel.cc", "fake_multiplexed_channel.h", + "fake_nearby_connection_manager.cc", + "fake_nearby_connection_manager.h", "fake_one_shot_timer.cc", "fake_one_shot_timer.h", "fake_pending_connection_manager.cc", @@ -287,11 +299,14 @@ "error_tolerant_ble_advertisement_impl_unittest.cc", "foreground_eid_generator_unittest.cc", "multiplexed_channel_impl_unittest.cc", + "nearby_connection_manager_impl_unittest.cc", + "nearby_initiator_operation_unittest.cc", "pending_ble_connection_request_base_unittest.cc", "pending_ble_initiator_connection_request_unittest.cc", "pending_ble_listener_connection_request_unittest.cc", "pending_connection_manager_impl_unittest.cc", "pending_connection_request_base_unittest.cc", + "pending_nearby_initiator_connection_request_unittest.cc", "raw_eid_generator_impl_unittest.cc", "secure_channel_disconnector_impl_unittest.cc", "secure_channel_service_unittest.cc",
diff --git a/chromeos/services/secure_channel/fake_nearby_connection_manager.cc b/chromeos/services/secure_channel/fake_nearby_connection_manager.cc new file mode 100644 index 0000000..99046d6 --- /dev/null +++ b/chromeos/services/secure_channel/fake_nearby_connection_manager.cc
@@ -0,0 +1,23 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/services/secure_channel/fake_nearby_connection_manager.h" + +namespace chromeos { + +namespace secure_channel { + +FakeNearbyConnectionManager::FakeNearbyConnectionManager() = default; + +FakeNearbyConnectionManager::~FakeNearbyConnectionManager() = default; + +void FakeNearbyConnectionManager::PerformAttemptNearbyInitiatorConnection( + const DeviceIdPair& device_id_pair) {} + +void FakeNearbyConnectionManager::PerformCancelNearbyInitiatorConnectionAttempt( + const DeviceIdPair& device_id_pair) {} + +} // namespace secure_channel + +} // namespace chromeos
diff --git a/chromeos/services/secure_channel/fake_nearby_connection_manager.h b/chromeos/services/secure_channel/fake_nearby_connection_manager.h new file mode 100644 index 0000000..5a04956 --- /dev/null +++ b/chromeos/services/secure_channel/fake_nearby_connection_manager.h
@@ -0,0 +1,39 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_SERVICES_SECURE_CHANNEL_FAKE_NEARBY_CONNECTION_MANAGER_H_ +#define CHROMEOS_SERVICES_SECURE_CHANNEL_FAKE_NEARBY_CONNECTION_MANAGER_H_ + +#include "chromeos/services/secure_channel/nearby_connection_manager.h" + +namespace chromeos { + +namespace secure_channel { + +class FakeNearbyConnectionManager : public NearbyConnectionManager { + public: + FakeNearbyConnectionManager(); + FakeNearbyConnectionManager(const FakeNearbyConnectionManager&) = delete; + FakeNearbyConnectionManager& operator=(const FakeNearbyConnectionManager&) = + delete; + ~FakeNearbyConnectionManager() override; + + using NearbyConnectionManager::DoesAttemptExist; + using NearbyConnectionManager::GetDeviceIdPairsForRemoteDevice; + using NearbyConnectionManager::NotifyNearbyInitiatorConnectionSuccess; + using NearbyConnectionManager::NotifyNearbyInitiatorFailure; + + private: + // NearbyConnectionManager: + void PerformAttemptNearbyInitiatorConnection( + const DeviceIdPair& device_id_pair) override; + void PerformCancelNearbyInitiatorConnectionAttempt( + const DeviceIdPair& device_id_pair) override; +}; + +} // namespace secure_channel + +} // namespace chromeos + +#endif // CHROMEOS_SERVICES_SECURE_CHANNEL_FAKE_NEARBY_CONNECTION_MANAGER_H_
diff --git a/chromeos/services/secure_channel/nearby_connection_manager.cc b/chromeos/services/secure_channel/nearby_connection_manager.cc new file mode 100644 index 0000000..cea6743 --- /dev/null +++ b/chromeos/services/secure_channel/nearby_connection_manager.cc
@@ -0,0 +1,124 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/services/secure_channel/nearby_connection_manager.h" + +#include "base/stl_util.h" +#include "chromeos/components/multidevice/logging/logging.h" +#include "chromeos/services/secure_channel/authenticated_channel.h" + +namespace chromeos { + +namespace secure_channel { + +NearbyConnectionManager::InitiatorConnectionAttemptMetadata:: + InitiatorConnectionAttemptMetadata( + ConnectionSuccessCallback success_callback, + const FailureCallback& failure_callback) + : success_callback(std::move(success_callback)), + failure_callback(failure_callback) {} + +NearbyConnectionManager::InitiatorConnectionAttemptMetadata:: + ~InitiatorConnectionAttemptMetadata() = default; + +NearbyConnectionManager::NearbyConnectionManager() = default; + +NearbyConnectionManager::~NearbyConnectionManager() = default; + +void NearbyConnectionManager::AttemptNearbyInitiatorConnection( + const DeviceIdPair& device_id_pair, + ConnectionSuccessCallback success_callback, + const FailureCallback& failure_callback) { + if (base::Contains(id_pair_to_initiator_metadata_map_, device_id_pair)) { + PA_LOG(ERROR) << "Tried to add Nearby initiator connection attempt, but " + << "one was already active. Device IDs: " << device_id_pair; + NOTREACHED(); + return; + } + + id_pair_to_initiator_metadata_map_.emplace(std::make_pair( + device_id_pair, std::make_unique<InitiatorConnectionAttemptMetadata>( + std::move(success_callback), failure_callback))); + remote_device_id_to_id_pair_map_[device_id_pair.remote_device_id()].insert( + device_id_pair); + + PA_LOG(VERBOSE) << "Attempting Nearby connection for: " << device_id_pair; + PerformAttemptNearbyInitiatorConnection(device_id_pair); +} + +void NearbyConnectionManager::CancelNearbyInitiatorConnectionAttempt( + const DeviceIdPair& device_id_pair) { + RemoveRequestMetadata(device_id_pair); + + PA_LOG(VERBOSE) << "Canceling Nearby connection attempt for: " + << device_id_pair; + PerformCancelNearbyInitiatorConnectionAttempt(device_id_pair); +} + +const base::flat_set<DeviceIdPair>& +NearbyConnectionManager::GetDeviceIdPairsForRemoteDevice( + const std::string& remote_device_id) const { + return remote_device_id_to_id_pair_map_.find(remote_device_id)->second; +} + +bool NearbyConnectionManager::DoesAttemptExist( + const DeviceIdPair& device_id_pair) { + return base::Contains(id_pair_to_initiator_metadata_map_, device_id_pair); +} + +void NearbyConnectionManager::NotifyNearbyInitiatorFailure( + const DeviceIdPair& device_id_pair, + NearbyInitiatorFailureType failure_type) { + PA_LOG(VERBOSE) << "Notifying client of Nearby initiator failure: " + << device_id_pair; + GetInitiatorEntry(device_id_pair).failure_callback.Run(failure_type); +} + +void NearbyConnectionManager::NotifyNearbyInitiatorConnectionSuccess( + const DeviceIdPair& device_id_pair, + std::unique_ptr<AuthenticatedChannel> authenticated_channel) { + PA_LOG(VERBOSE) << "Notifying client of successful Neraby connection for: " + << device_id_pair; + + // Retrieve the success callback out of the map first, then remove the + // associated metadata before invoking the callback. + ConnectionSuccessCallback success_callback = + std::move(GetInitiatorEntry(device_id_pair).success_callback); + RemoveRequestMetadata(device_id_pair); + std::move(success_callback).Run(std::move(authenticated_channel)); +} + +NearbyConnectionManager::InitiatorConnectionAttemptMetadata& +NearbyConnectionManager::GetInitiatorEntry(const DeviceIdPair& device_id_pair) { + std::unique_ptr<InitiatorConnectionAttemptMetadata>& entry = + id_pair_to_initiator_metadata_map_[device_id_pair]; + DCHECK(entry); + return *entry; +} + +void NearbyConnectionManager::RemoveRequestMetadata( + const DeviceIdPair& device_id_pair) { + auto metadata_it = id_pair_to_initiator_metadata_map_.find(device_id_pair); + if (metadata_it == id_pair_to_initiator_metadata_map_.end()) { + PA_LOG(ERROR) << "Tried to remove Nearby initiator metadata, but none " + << "existed. Device IDs: " << device_id_pair; + NOTREACHED(); + } else { + id_pair_to_initiator_metadata_map_.erase(metadata_it); + } + + auto id_pair_it = + remote_device_id_to_id_pair_map_.find(device_id_pair.remote_device_id()); + if (id_pair_it == remote_device_id_to_id_pair_map_.end()) { + PA_LOG(ERROR) << "Tried to remove Nearby initiator attempt, but no attempt " + << "existed. Device IDs: " << device_id_pair; + NOTREACHED(); + } else { + id_pair_it->second.erase(device_id_pair); + } +} + +} // namespace secure_channel + +} // namespace chromeos
diff --git a/chromeos/services/secure_channel/nearby_connection_manager.h b/chromeos/services/secure_channel/nearby_connection_manager.h new file mode 100644 index 0000000..d83f55d3 --- /dev/null +++ b/chromeos/services/secure_channel/nearby_connection_manager.h
@@ -0,0 +1,91 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_SERVICES_SECURE_CHANNEL_NEARBY_CONNECTION_MANAGER_H_ +#define CHROMEOS_SERVICES_SECURE_CHANNEL_NEARBY_CONNECTION_MANAGER_H_ + +#include <memory> + +#include "base/callback.h" +#include "base/containers/flat_map.h" +#include "base/containers/flat_set.h" +#include "base/macros.h" +#include "chromeos/services/secure_channel/device_id_pair.h" +#include "chromeos/services/secure_channel/nearby_initiator_failure_type.h" + +namespace chromeos { + +namespace secure_channel { + +class AuthenticatedChannel; + +// Attempts connects to remote devices via the Nearby Connections library. +class NearbyConnectionManager { + public: + NearbyConnectionManager(const NearbyConnectionManager&) = delete; + NearbyConnectionManager& operator=(const NearbyConnectionManager&) = delete; + virtual ~NearbyConnectionManager(); + + using ConnectionSuccessCallback = + base::OnceCallback<void(std::unique_ptr<AuthenticatedChannel>)>; + using FailureCallback = + base::RepeatingCallback<void(NearbyInitiatorFailureType)>; + + // Attempts a connection, invoking the success/failure callback when the + // attempt has finished. + void AttemptNearbyInitiatorConnection( + const DeviceIdPair& device_id_pair, + ConnectionSuccessCallback success_callback, + const FailureCallback& failure_callback); + + // Cancels an active connection attempt; the success/failure callback for this + // attempt will not be invoked. + void CancelNearbyInitiatorConnectionAttempt( + const DeviceIdPair& device_id_pair); + + protected: + NearbyConnectionManager(); + + virtual void PerformAttemptNearbyInitiatorConnection( + const DeviceIdPair& device_id_pair) = 0; + virtual void PerformCancelNearbyInitiatorConnectionAttempt( + const DeviceIdPair& device_id_pair) = 0; + + const base::flat_set<DeviceIdPair>& GetDeviceIdPairsForRemoteDevice( + const std::string& remote_device_id) const; + bool DoesAttemptExist(const DeviceIdPair& device_id_pair); + + void NotifyNearbyInitiatorFailure(const DeviceIdPair& device_id_pair, + NearbyInitiatorFailureType failure_type); + void NotifyNearbyInitiatorConnectionSuccess( + const DeviceIdPair& device_id_pair, + std::unique_ptr<AuthenticatedChannel> authenticated_channel); + + private: + struct InitiatorConnectionAttemptMetadata { + InitiatorConnectionAttemptMetadata( + ConnectionSuccessCallback success_callback, + const FailureCallback& failure_callback); + ~InitiatorConnectionAttemptMetadata(); + + ConnectionSuccessCallback success_callback; + FailureCallback failure_callback; + }; + + InitiatorConnectionAttemptMetadata& GetInitiatorEntry( + const DeviceIdPair& device_id_pair); + void RemoveRequestMetadata(const DeviceIdPair& device_id_pair); + + base::flat_map<std::string, base::flat_set<DeviceIdPair>> + remote_device_id_to_id_pair_map_; + base::flat_map<DeviceIdPair, + std::unique_ptr<InitiatorConnectionAttemptMetadata>> + id_pair_to_initiator_metadata_map_; +}; + +} // namespace secure_channel + +} // namespace chromeos + +#endif // CHROMEOS_SERVICES_SECURE_CHANNEL_NEARBY_CONNECTION_MANAGER_H_
diff --git a/chromeos/services/secure_channel/nearby_connection_manager_impl.cc b/chromeos/services/secure_channel/nearby_connection_manager_impl.cc new file mode 100644 index 0000000..4372c99 --- /dev/null +++ b/chromeos/services/secure_channel/nearby_connection_manager_impl.cc
@@ -0,0 +1,46 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/services/secure_channel/nearby_connection_manager_impl.h" + +#include "base/memory/ptr_util.h" + +namespace chromeos { + +namespace secure_channel { + +// static +NearbyConnectionManagerImpl::Factory* + NearbyConnectionManagerImpl::Factory::test_factory_ = nullptr; + +// static +std::unique_ptr<NearbyConnectionManager> +NearbyConnectionManagerImpl::Factory::Create() { + if (test_factory_) + return test_factory_->CreateInstance(); + + return base::WrapUnique(new NearbyConnectionManagerImpl()); +} + +// static +void NearbyConnectionManagerImpl::Factory::SetFactoryForTesting( + Factory* test_factory) { + test_factory_ = test_factory; +} + +NearbyConnectionManagerImpl::Factory::~Factory() = default; + +NearbyConnectionManagerImpl::NearbyConnectionManagerImpl() = default; + +NearbyConnectionManagerImpl::~NearbyConnectionManagerImpl() = default; + +void NearbyConnectionManagerImpl::PerformAttemptNearbyInitiatorConnection( + const DeviceIdPair& device_id_pair) {} + +void NearbyConnectionManagerImpl::PerformCancelNearbyInitiatorConnectionAttempt( + const DeviceIdPair& device_id_pair) {} + +} // namespace secure_channel + +} // namespace chromeos
diff --git a/chromeos/services/secure_channel/nearby_connection_manager_impl.h b/chromeos/services/secure_channel/nearby_connection_manager_impl.h new file mode 100644 index 0000000..4e782c8 --- /dev/null +++ b/chromeos/services/secure_channel/nearby_connection_manager_impl.h
@@ -0,0 +1,49 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_SERVICES_SECURE_CHANNEL_NEARBY_CONNECTION_MANAGER_IMPL_H_ +#define CHROMEOS_SERVICES_SECURE_CHANNEL_NEARBY_CONNECTION_MANAGER_IMPL_H_ + +#include "chromeos/services/secure_channel/nearby_connection_manager.h" + +namespace chromeos { + +namespace secure_channel { + +// TODO(https://crbug.com/1106937): Add real implementation. +class NearbyConnectionManagerImpl : public NearbyConnectionManager { + public: + class Factory { + public: + static std::unique_ptr<NearbyConnectionManager> Create(); + static void SetFactoryForTesting(Factory* test_factory); + + protected: + virtual ~Factory(); + virtual std::unique_ptr<NearbyConnectionManager> CreateInstance() = 0; + + private: + static Factory* test_factory_; + }; + + NearbyConnectionManagerImpl(const NearbyConnectionManagerImpl&) = delete; + NearbyConnectionManagerImpl& operator=(const NearbyConnectionManagerImpl&) = + delete; + ~NearbyConnectionManagerImpl() override; + + private: + NearbyConnectionManagerImpl(); + + // NearbyConnectionManager: + void PerformAttemptNearbyInitiatorConnection( + const DeviceIdPair& device_id_pair) override; + void PerformCancelNearbyInitiatorConnectionAttempt( + const DeviceIdPair& device_id_pair) override; +}; + +} // namespace secure_channel + +} // namespace chromeos + +#endif // CHROMEOS_SERVICES_SECURE_CHANNEL_NEARBY_CONNECTION_MANAGER_IMPL_H_
diff --git a/chromeos/services/secure_channel/nearby_connection_manager_impl_unittest.cc b/chromeos/services/secure_channel/nearby_connection_manager_impl_unittest.cc new file mode 100644 index 0000000..9f9b4cd --- /dev/null +++ b/chromeos/services/secure_channel/nearby_connection_manager_impl_unittest.cc
@@ -0,0 +1,39 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/services/secure_channel/nearby_connection_manager_impl.h" + +#include <memory> + +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromeos { + +namespace secure_channel { + +class SecureChannelNearbyConnectionManagerImplTest : public testing::Test { + protected: + SecureChannelNearbyConnectionManagerImplTest() = default; + SecureChannelNearbyConnectionManagerImplTest( + const SecureChannelNearbyConnectionManagerImplTest&) = delete; + SecureChannelNearbyConnectionManagerImplTest& operator=( + const SecureChannelNearbyConnectionManagerImplTest&) = delete; + ~SecureChannelNearbyConnectionManagerImplTest() override = default; + + // testing::Test: + void SetUp() override { + manager_ = NearbyConnectionManagerImpl::Factory::Create(); + } + + std::unique_ptr<NearbyConnectionManager> manager_; +}; + +// TODO(https://crbug.com/1106937): Delete when a real test is added. +TEST_F(SecureChannelNearbyConnectionManagerImplTest, Create) { + EXPECT_TRUE(manager_); +} + +} // namespace secure_channel + +} // namespace chromeos
diff --git a/chromeos/services/secure_channel/nearby_initiator_connection_attempt.cc b/chromeos/services/secure_channel/nearby_initiator_connection_attempt.cc new file mode 100644 index 0000000..991cd74 --- /dev/null +++ b/chromeos/services/secure_channel/nearby_initiator_connection_attempt.cc
@@ -0,0 +1,67 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/services/secure_channel/nearby_initiator_connection_attempt.h" + +#include "base/memory/ptr_util.h" +#include "chromeos/services/secure_channel/nearby_initiator_operation.h" + +namespace chromeos { + +namespace secure_channel { + +// static +NearbyInitiatorConnectionAttempt::Factory* + NearbyInitiatorConnectionAttempt::Factory::test_factory_ = nullptr; + +// static +std::unique_ptr<ConnectionAttempt<NearbyInitiatorFailureType>> +NearbyInitiatorConnectionAttempt::Factory::Create( + NearbyConnectionManager* nearby_connection_manager, + ConnectionAttemptDelegate* delegate, + const ConnectionAttemptDetails& connection_attempt_details) { + if (test_factory_) { + return test_factory_->CreateInstance(nearby_connection_manager, delegate, + connection_attempt_details); + } + + return base::WrapUnique(new NearbyInitiatorConnectionAttempt( + nearby_connection_manager, delegate, connection_attempt_details)); +} + +// static +void NearbyInitiatorConnectionAttempt::Factory::SetFactoryForTesting( + Factory* test_factory) { + test_factory_ = test_factory; +} + +NearbyInitiatorConnectionAttempt::Factory::~Factory() = default; + +NearbyInitiatorConnectionAttempt::NearbyInitiatorConnectionAttempt( + NearbyConnectionManager* nearby_connection_manager, + ConnectionAttemptDelegate* delegate, + const ConnectionAttemptDetails& connection_attempt_details) + : ConnectionAttemptBase<NearbyInitiatorFailureType>( + delegate, + connection_attempt_details), + nearby_connection_manager_(nearby_connection_manager) {} + +NearbyInitiatorConnectionAttempt::~NearbyInitiatorConnectionAttempt() = default; + +std::unique_ptr<ConnectToDeviceOperation<NearbyInitiatorFailureType>> +NearbyInitiatorConnectionAttempt::CreateConnectToDeviceOperation( + const DeviceIdPair& device_id_pair, + ConnectionPriority connection_priority, + ConnectToDeviceOperation< + NearbyInitiatorFailureType>::ConnectionSuccessCallback success_callback, + const ConnectToDeviceOperation<NearbyInitiatorFailureType>:: + ConnectionFailedCallback& failure_callback) { + return NearbyInitiatorOperation::Factory::Create( + nearby_connection_manager_, std::move(success_callback), failure_callback, + device_id_pair, connection_priority); +} + +} // namespace secure_channel + +} // namespace chromeos
diff --git a/chromeos/services/secure_channel/nearby_initiator_connection_attempt.h b/chromeos/services/secure_channel/nearby_initiator_connection_attempt.h new file mode 100644 index 0000000..0f3bb42 --- /dev/null +++ b/chromeos/services/secure_channel/nearby_initiator_connection_attempt.h
@@ -0,0 +1,72 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_SERVICES_SECURE_CHANNEL_NEARBY_INITIATOR_CONNECTION_ATTEMPT_H_ +#define CHROMEOS_SERVICES_SECURE_CHANNEL_NEARBY_INITIATOR_CONNECTION_ATTEMPT_H_ + +#include <memory> + +#include "chromeos/services/secure_channel/connection_attempt_base.h" +#include "chromeos/services/secure_channel/nearby_initiator_failure_type.h" + +namespace chromeos { + +namespace secure_channel { + +class NearbyConnectionManager; + +// Attempts to connect to a remote device over Nearby Connections via the +// initiator role. +class NearbyInitiatorConnectionAttempt + : public ConnectionAttemptBase<NearbyInitiatorFailureType> { + public: + class Factory { + public: + static std::unique_ptr<ConnectionAttempt<NearbyInitiatorFailureType>> + Create(NearbyConnectionManager* nearby_connection_manager, + ConnectionAttemptDelegate* delegate, + const ConnectionAttemptDetails& connection_attempt_details); + static void SetFactoryForTesting(Factory* test_factory); + + protected: + virtual ~Factory(); + virtual std::unique_ptr<ConnectionAttempt<NearbyInitiatorFailureType>> + CreateInstance( + NearbyConnectionManager* nearby_connection_manager, + ConnectionAttemptDelegate* delegate, + const ConnectionAttemptDetails& connection_attempt_details) = 0; + + private: + static Factory* test_factory_; + }; + + NearbyInitiatorConnectionAttempt(const NearbyInitiatorConnectionAttempt&) = + delete; + NearbyInitiatorConnectionAttempt& operator=( + const NearbyInitiatorConnectionAttempt&) = delete; + ~NearbyInitiatorConnectionAttempt() override; + + private: + NearbyInitiatorConnectionAttempt( + NearbyConnectionManager* nearby_connection_manager, + ConnectionAttemptDelegate* delegate, + const ConnectionAttemptDetails& connection_attempt_details); + + std::unique_ptr<ConnectToDeviceOperation<NearbyInitiatorFailureType>> + CreateConnectToDeviceOperation( + const DeviceIdPair& device_id_pair, + ConnectionPriority connection_priority, + ConnectToDeviceOperation<NearbyInitiatorFailureType>:: + ConnectionSuccessCallback success_callback, + const ConnectToDeviceOperation<NearbyInitiatorFailureType>:: + ConnectionFailedCallback& failure_callback) override; + + NearbyConnectionManager* nearby_connection_manager_; +}; + +} // namespace secure_channel + +} // namespace chromeos + +#endif // CHROMEOS_SERVICES_SECURE_CHANNEL_NEARBY_INITIATOR_CONNECTION_ATTEMPT_H_
diff --git a/chromeos/services/secure_channel/nearby_initiator_operation.cc b/chromeos/services/secure_channel/nearby_initiator_operation.cc new file mode 100644 index 0000000..12d8005f --- /dev/null +++ b/chromeos/services/secure_channel/nearby_initiator_operation.cc
@@ -0,0 +1,104 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/services/secure_channel/nearby_initiator_operation.h" + +#include "base/bind.h" +#include "base/memory/ptr_util.h" +#include "chromeos/services/secure_channel/authenticated_channel.h" +#include "chromeos/services/secure_channel/nearby_connection_manager.h" + +namespace chromeos { + +namespace secure_channel { + +// static +NearbyInitiatorOperation::Factory* + NearbyInitiatorOperation::Factory::test_factory_ = nullptr; + +// static +std::unique_ptr<ConnectToDeviceOperation<NearbyInitiatorFailureType>> +NearbyInitiatorOperation::Factory::Create( + NearbyConnectionManager* nearby_connection_manager, + ConnectToDeviceOperation< + NearbyInitiatorFailureType>::ConnectionSuccessCallback success_callback, + const ConnectToDeviceOperation< + NearbyInitiatorFailureType>::ConnectionFailedCallback& failure_callback, + const DeviceIdPair& device_id_pair, + ConnectionPriority connection_priority, + scoped_refptr<base::TaskRunner> task_runner) { + if (test_factory_) { + return test_factory_->CreateInstance( + nearby_connection_manager, std::move(success_callback), + std::move(failure_callback), device_id_pair, connection_priority, + std::move(task_runner)); + } + + return base::WrapUnique(new NearbyInitiatorOperation( + nearby_connection_manager, std::move(success_callback), + std::move(failure_callback), device_id_pair, connection_priority, + std::move(task_runner))); +} + +// static +void NearbyInitiatorOperation::Factory::SetFactoryForTesting( + Factory* test_factory) { + test_factory_ = test_factory; +} + +NearbyInitiatorOperation::Factory::~Factory() = default; + +NearbyInitiatorOperation::NearbyInitiatorOperation( + NearbyConnectionManager* nearby_connection_manager, + ConnectToDeviceOperation< + NearbyInitiatorFailureType>::ConnectionSuccessCallback success_callback, + const ConnectToDeviceOperation< + NearbyInitiatorFailureType>::ConnectionFailedCallback& failure_callback, + const DeviceIdPair& device_id_pair, + ConnectionPriority connection_priority, + scoped_refptr<base::TaskRunner> task_runner) + : ConnectToDeviceOperationBase<NearbyInitiatorFailureType>( + std::move(success_callback), + std::move(failure_callback), + device_id_pair, + connection_priority, + task_runner), + nearby_connection_manager_(nearby_connection_manager) {} + +NearbyInitiatorOperation::~NearbyInitiatorOperation() = default; + +void NearbyInitiatorOperation::PerformAttemptConnectionToDevice( + ConnectionPriority connection_priority) { + nearby_connection_manager_->AttemptNearbyInitiatorConnection( + device_id_pair(), + base::BindOnce(&NearbyInitiatorOperation::OnSuccessfulConnection, + weak_ptr_factory_.GetWeakPtr()), + base::BindRepeating(&NearbyInitiatorOperation::OnConnectionFailure, + weak_ptr_factory_.GetWeakPtr())); +} + +void NearbyInitiatorOperation::PerformCancellation() { + nearby_connection_manager_->CancelNearbyInitiatorConnectionAttempt( + device_id_pair()); +} + +void NearbyInitiatorOperation::PerformUpdateConnectionPriority( + ConnectionPriority connection_priority) { + // Note: Nearby Connections are not performed differently based on the + // connection priority, so this function is intentionally empty. +} + +void NearbyInitiatorOperation::OnSuccessfulConnection( + std::unique_ptr<AuthenticatedChannel> authenticated_channel) { + OnSuccessfulConnectionAttempt(std::move(authenticated_channel)); +} + +void NearbyInitiatorOperation::OnConnectionFailure( + NearbyInitiatorFailureType failure_type) { + OnFailedConnectionAttempt(failure_type); +} + +} // namespace secure_channel + +} // namespace chromeos
diff --git a/chromeos/services/secure_channel/nearby_initiator_operation.h b/chromeos/services/secure_channel/nearby_initiator_operation.h new file mode 100644 index 0000000..8070750 --- /dev/null +++ b/chromeos/services/secure_channel/nearby_initiator_operation.h
@@ -0,0 +1,93 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_SERVICES_SECURE_CHANNEL_NEARBY_INITIATOR_OPERATION_H_ +#define CHROMEOS_SERVICES_SECURE_CHANNEL_NEARBY_INITIATOR_OPERATION_H_ + +#include <memory> + +#include "base/memory/weak_ptr.h" +#include "base/threading/thread_task_runner_handle.h" +#include "chromeos/services/secure_channel/connect_to_device_operation.h" +#include "chromeos/services/secure_channel/connect_to_device_operation_base.h" +#include "chromeos/services/secure_channel/nearby_initiator_failure_type.h" +#include "chromeos/services/secure_channel/public/cpp/shared/connection_priority.h" + +namespace chromeos { + +namespace secure_channel { + +class NearbyConnectionManager; + +// Attempts to connect to a remote device over Nearby Connections via the +// initiator role. +class NearbyInitiatorOperation + : public ConnectToDeviceOperationBase<NearbyInitiatorFailureType> { + public: + class Factory { + public: + static std::unique_ptr<ConnectToDeviceOperation<NearbyInitiatorFailureType>> + Create(NearbyConnectionManager* nearby_connection_manager, + ConnectToDeviceOperation<NearbyInitiatorFailureType>:: + ConnectionSuccessCallback success_callback, + const ConnectToDeviceOperation<NearbyInitiatorFailureType>:: + ConnectionFailedCallback& failure_callback, + const DeviceIdPair& device_id_pair, + ConnectionPriority connection_priority, + scoped_refptr<base::TaskRunner> task_runner = + base::ThreadTaskRunnerHandle::Get()); + static void SetFactoryForTesting(Factory* test_factory); + + protected: + virtual ~Factory(); + virtual std::unique_ptr< + ConnectToDeviceOperation<NearbyInitiatorFailureType>> + CreateInstance(NearbyConnectionManager* nearby_connection_manager, + ConnectToDeviceOperation<NearbyInitiatorFailureType>:: + ConnectionSuccessCallback success_callback, + const ConnectToDeviceOperation<NearbyInitiatorFailureType>:: + ConnectionFailedCallback& failure_callback, + const DeviceIdPair& device_id_pair, + ConnectionPriority connection_priority, + scoped_refptr<base::TaskRunner> task_runner) = 0; + + private: + static Factory* test_factory_; + }; + + NearbyInitiatorOperation(const NearbyInitiatorOperation&) = delete; + NearbyInitiatorOperation& operator=(const NearbyInitiatorOperation&) = delete; + ~NearbyInitiatorOperation() override; + + private: + NearbyInitiatorOperation( + NearbyConnectionManager* nearby_connection_manager, + ConnectToDeviceOperation<NearbyInitiatorFailureType>:: + ConnectionSuccessCallback success_callback, + const ConnectToDeviceOperation<NearbyInitiatorFailureType>:: + ConnectionFailedCallback& failure_callback, + const DeviceIdPair& device_id_pair, + ConnectionPriority connection_priority, + scoped_refptr<base::TaskRunner> task_runner); + + // ConnectToDeviceOperationBase<NearbyInitiatorFailureType>: + void PerformAttemptConnectionToDevice( + ConnectionPriority connection_priority) override; + void PerformCancellation() override; + void PerformUpdateConnectionPriority( + ConnectionPriority connection_priority) override; + + void OnSuccessfulConnection( + std::unique_ptr<AuthenticatedChannel> authenticated_channel); + void OnConnectionFailure(NearbyInitiatorFailureType failure_type); + + NearbyConnectionManager* nearby_connection_manager_; + base::WeakPtrFactory<NearbyInitiatorOperation> weak_ptr_factory_{this}; +}; + +} // namespace secure_channel + +} // namespace chromeos + +#endif // CHROMEOS_SERVICES_SECURE_CHANNEL_NEARBY_INITIATOR_OPERATION_H_ \ No newline at end of file
diff --git a/chromeos/services/secure_channel/nearby_initiator_operation_unittest.cc b/chromeos/services/secure_channel/nearby_initiator_operation_unittest.cc new file mode 100644 index 0000000..0fd3328 --- /dev/null +++ b/chromeos/services/secure_channel/nearby_initiator_operation_unittest.cc
@@ -0,0 +1,133 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/services/secure_channel/nearby_initiator_operation.h" + +#include <memory> + +#include "base/bind.h" +#include "base/test/task_environment.h" +#include "base/test/test_simple_task_runner.h" +#include "chromeos/services/secure_channel/device_id_pair.h" +#include "chromeos/services/secure_channel/fake_authenticated_channel.h" +#include "chromeos/services/secure_channel/fake_nearby_connection_manager.h" +#include "chromeos/services/secure_channel/nearby_initiator_failure_type.h" +#include "chromeos/services/secure_channel/public/cpp/shared/connection_priority.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromeos { + +namespace secure_channel { + +const char kTestRemoteDeviceId[] = "testRemoteDeviceId"; +const char kTestLocalDeviceId[] = "testLocalDeviceId"; +constexpr const ConnectionPriority kTestConnectionPriority = + ConnectionPriority::kLow; + +class SecureChannelNearbyInitiatorOperationTest : public testing::Test { + protected: + SecureChannelNearbyInitiatorOperationTest() + : device_id_pair_(kTestRemoteDeviceId, kTestLocalDeviceId) {} + SecureChannelNearbyInitiatorOperationTest( + const SecureChannelNearbyInitiatorOperationTest&) = delete; + SecureChannelNearbyInitiatorOperationTest& operator=( + const SecureChannelNearbyInitiatorOperationTest&) = delete; + ~SecureChannelNearbyInitiatorOperationTest() override = default; + + // testing::Test: + void SetUp() override { + fake_nearby_connection_manager_ = + std::make_unique<FakeNearbyConnectionManager>(); + + auto test_task_runner = base::MakeRefCounted<base::TestSimpleTaskRunner>(); + operation_ = NearbyInitiatorOperation::Factory::Create( + fake_nearby_connection_manager_.get(), + base::BindOnce(&SecureChannelNearbyInitiatorOperationTest:: + OnSuccessfulConnectionAttempt, + base::Unretained(this)), + base::BindRepeating(&SecureChannelNearbyInitiatorOperationTest:: + OnFailedConnectionAttempt, + base::Unretained(this)), + device_id_pair_, kTestConnectionPriority, test_task_runner); + test_task_runner->RunUntilIdle(); + } + + const DeviceIdPair& device_id_pair() { return device_id_pair_; } + + void FailAttempt(NearbyInitiatorFailureType failure_type) { + fake_nearby_connection_manager_->NotifyNearbyInitiatorFailure( + device_id_pair_, failure_type); + EXPECT_EQ(failure_type, failure_type_from_callback_); + } + + FakeNearbyConnectionManager* fake_nearby_connection_manager() { + return fake_nearby_connection_manager_.get(); + } + + AuthenticatedChannel* channel_from_callback() { + return channel_from_callback_.get(); + } + + ConnectToDeviceOperation<NearbyInitiatorFailureType>* operation() { + return operation_.get(); + } + + private: + void OnSuccessfulConnectionAttempt( + std::unique_ptr<AuthenticatedChannel> authenticated_channel) { + EXPECT_FALSE(channel_from_callback_); + channel_from_callback_ = std::move(authenticated_channel); + } + + void OnFailedConnectionAttempt(NearbyInitiatorFailureType failure_type) { + failure_type_from_callback_ = failure_type; + } + + const base::test::TaskEnvironment task_environment_; + + std::unique_ptr<FakeNearbyConnectionManager> fake_nearby_connection_manager_; + DeviceIdPair device_id_pair_; + + std::unique_ptr<AuthenticatedChannel> channel_from_callback_; + base::Optional<NearbyInitiatorFailureType> failure_type_from_callback_; + + std::unique_ptr<ConnectToDeviceOperation<NearbyInitiatorFailureType>> + operation_; +}; + +TEST_F(SecureChannelNearbyInitiatorOperationTest, Succeed) { + auto fake_authenticated_channel = + std::make_unique<FakeAuthenticatedChannel>(); + FakeAuthenticatedChannel* fake_authenticated_channel_raw = + fake_authenticated_channel.get(); + + fake_nearby_connection_manager()->NotifyNearbyInitiatorConnectionSuccess( + device_id_pair(), std::move(fake_authenticated_channel)); + EXPECT_EQ(fake_authenticated_channel_raw, channel_from_callback()); + + // The operation should no longer be present in NearbyConnectionManager. + EXPECT_FALSE( + fake_nearby_connection_manager()->DoesAttemptExist(device_id_pair())); +} + +TEST_F(SecureChannelNearbyInitiatorOperationTest, Fail) { + static const NearbyInitiatorFailureType all_types[] = { + NearbyInitiatorFailureType::kTimeoutDiscoveringDevice, + NearbyInitiatorFailureType::kNearbyApiError, + NearbyInitiatorFailureType::kConnectionRejected, + NearbyInitiatorFailureType::kConnectivityError, + NearbyInitiatorFailureType::kAuthenticationError}; + + for (const auto& failure_type : all_types) { + FailAttempt(failure_type); + } + + operation()->Cancel(); + EXPECT_FALSE( + fake_nearby_connection_manager()->DoesAttemptExist(device_id_pair())); +} + +} // namespace secure_channel + +} // namespace chromeos
diff --git a/chromeos/services/secure_channel/pending_nearby_initiator_connection_request.cc b/chromeos/services/secure_channel/pending_nearby_initiator_connection_request.cc new file mode 100644 index 0000000..a24ef319 --- /dev/null +++ b/chromeos/services/secure_channel/pending_nearby_initiator_connection_request.cc
@@ -0,0 +1,118 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/services/secure_channel/pending_nearby_initiator_connection_request.h" + +#include "base/memory/ptr_util.h" +#include "chromeos/components/multidevice/logging/logging.h" +#include "device/bluetooth/bluetooth_adapter.h" + +namespace chromeos { + +namespace secure_channel { + +namespace { +const char kRequestTypeForLogging[] = "Nearby Initiator"; +} // namespace + +// static +PendingNearbyInitiatorConnectionRequest::Factory* + PendingNearbyInitiatorConnectionRequest::Factory::test_factory_ = nullptr; + +// static +std::unique_ptr<PendingConnectionRequest<NearbyInitiatorFailureType>> +PendingNearbyInitiatorConnectionRequest::Factory::Create( + std::unique_ptr<ClientConnectionParameters> client_connection_parameters, + ConnectionPriority connection_priority, + PendingConnectionRequestDelegate* delegate, + scoped_refptr<device::BluetoothAdapter> bluetooth_adapter) { + if (test_factory_) { + return test_factory_->CreateInstance( + std::move(client_connection_parameters), connection_priority, delegate, + bluetooth_adapter); + } + + return base::WrapUnique(new PendingNearbyInitiatorConnectionRequest( + std::move(client_connection_parameters), connection_priority, delegate, + bluetooth_adapter)); +} + +// static +void PendingNearbyInitiatorConnectionRequest::Factory::SetFactoryForTesting( + Factory* test_factory) { + test_factory_ = test_factory; +} + +PendingNearbyInitiatorConnectionRequest::Factory::~Factory() = default; + +PendingNearbyInitiatorConnectionRequest:: + PendingNearbyInitiatorConnectionRequest( + std::unique_ptr<ClientConnectionParameters> + client_connection_parameters, + ConnectionPriority connection_priority, + PendingConnectionRequestDelegate* delegate, + scoped_refptr<device::BluetoothAdapter> bluetooth_adapter) + : PendingConnectionRequestBase<NearbyInitiatorFailureType>( + std::move(client_connection_parameters), + connection_priority, + kRequestTypeForLogging, + delegate), + bluetooth_adapter_(std::move(bluetooth_adapter)) { + bluetooth_adapter_->AddObserver(this); +} + +PendingNearbyInitiatorConnectionRequest:: + ~PendingNearbyInitiatorConnectionRequest() { + bluetooth_adapter_->RemoveObserver(this); +} + +void PendingNearbyInitiatorConnectionRequest::HandleConnectionFailure( + NearbyInitiatorFailureType failure_detail) { + PA_LOG(INFO) << "Pending Nearby Connection failed : " << failure_detail; + + switch (failure_detail) { + case NearbyInitiatorFailureType::kNearbyApiError: + FALLTHROUGH; + case NearbyInitiatorFailureType::kConnectionRejected: + FALLTHROUGH; + case NearbyInitiatorFailureType::kConnectivityError: + StopRequestDueToConnectionFailures( + mojom::ConnectionAttemptFailureReason::NEARBY_CONNECTION_ERROR); + break; + case NearbyInitiatorFailureType::kTimeoutDiscoveringDevice: + StopRequestDueToConnectionFailures( + mojom::ConnectionAttemptFailureReason::TIMEOUT_FINDING_DEVICE); + break; + case NearbyInitiatorFailureType::kAuthenticationError: + StopRequestDueToConnectionFailures( + mojom::ConnectionAttemptFailureReason::AUTHENTICATION_ERROR); + break; + } +} + +void PendingNearbyInitiatorConnectionRequest::AdapterPoweredChanged( + device::BluetoothAdapter* adapter, + bool powered) { + DCHECK_EQ(bluetooth_adapter_, adapter); + if (powered) + return; + + StopRequestDueToConnectionFailures( + mojom::ConnectionAttemptFailureReason::ADAPTER_DISABLED); +} + +void PendingNearbyInitiatorConnectionRequest::AdapterPresentChanged( + device::BluetoothAdapter* adapter, + bool present) { + DCHECK_EQ(bluetooth_adapter_, adapter); + if (present) + return; + + StopRequestDueToConnectionFailures( + mojom::ConnectionAttemptFailureReason::ADAPTER_NOT_PRESENT); +} + +} // namespace secure_channel + +} // namespace chromeos
diff --git a/chromeos/services/secure_channel/pending_nearby_initiator_connection_request.h b/chromeos/services/secure_channel/pending_nearby_initiator_connection_request.h new file mode 100644 index 0000000..6e51cac --- /dev/null +++ b/chromeos/services/secure_channel/pending_nearby_initiator_connection_request.h
@@ -0,0 +1,83 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROMEOS_SERVICES_SECURE_CHANNEL_PENDING_NEARBY_INITIATOR_CONNECTION_REQUEST_H_ +#define CHROMEOS_SERVICES_SECURE_CHANNEL_PENDING_NEARBY_INITIATOR_CONNECTION_REQUEST_H_ + +#include <memory> +#include <string> +#include <utility> + +#include "chromeos/services/secure_channel/nearby_initiator_failure_type.h" +#include "chromeos/services/secure_channel/pending_connection_request_base.h" +#include "device/bluetooth/bluetooth_adapter.h" + +namespace chromeos { + +namespace secure_channel { + +// Pending request to create a connection via Nearby Connections in the +// initiator role. +class PendingNearbyInitiatorConnectionRequest + : public PendingConnectionRequestBase<NearbyInitiatorFailureType>, + public device::BluetoothAdapter::Observer { + public: + class Factory { + public: + static std::unique_ptr<PendingConnectionRequest<NearbyInitiatorFailureType>> + Create(std::unique_ptr<ClientConnectionParameters> + client_connection_parameters, + ConnectionPriority connection_priority, + PendingConnectionRequestDelegate* delegate, + scoped_refptr<device::BluetoothAdapter> bluetooth_adapter); + static void SetFactoryForTesting(Factory* test_factory); + + protected: + virtual ~Factory(); + virtual std::unique_ptr< + PendingConnectionRequest<NearbyInitiatorFailureType>> + CreateInstance( + std::unique_ptr<ClientConnectionParameters> + client_connection_parameters, + ConnectionPriority connection_priority, + PendingConnectionRequestDelegate* delegate, + scoped_refptr<device::BluetoothAdapter> bluetooth_adapter) = 0; + + private: + static Factory* test_factory_; + }; + + ~PendingNearbyInitiatorConnectionRequest() override; + PendingNearbyInitiatorConnectionRequest( + const PendingNearbyInitiatorConnectionRequest&) = delete; + PendingNearbyInitiatorConnectionRequest& operator=( + const PendingNearbyInitiatorConnectionRequest&) = delete; + + private: + friend class SecureChannelPendingNearbyInitiatorConnectionRequestTest; + + PendingNearbyInitiatorConnectionRequest( + std::unique_ptr<ClientConnectionParameters> client_connection_parameters, + ConnectionPriority connection_priority, + PendingConnectionRequestDelegate* delegate, + scoped_refptr<device::BluetoothAdapter> bluetooth_adapter); + + // PendingConnectionRequest<NearbyInitiatorFailureType>: + void HandleConnectionFailure( + NearbyInitiatorFailureType failure_detail) override; + + // device::BluetoothAdapter::Observer: + void AdapterPoweredChanged(device::BluetoothAdapter* adapter, + bool powered) override; + void AdapterPresentChanged(device::BluetoothAdapter* adapter, + bool present) override; + + scoped_refptr<device::BluetoothAdapter> bluetooth_adapter_; +}; + +} // namespace secure_channel + +} // namespace chromeos + +#endif // CHROMEOS_SERVICES_SECURE_CHANNEL_PENDING_NEARBY_INITIATOR_CONNECTION_REQUEST_H_
diff --git a/chromeos/services/secure_channel/pending_nearby_initiator_connection_request_unittest.cc b/chromeos/services/secure_channel/pending_nearby_initiator_connection_request_unittest.cc new file mode 100644 index 0000000..2fa90e0 --- /dev/null +++ b/chromeos/services/secure_channel/pending_nearby_initiator_connection_request_unittest.cc
@@ -0,0 +1,185 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chromeos/services/secure_channel/pending_nearby_initiator_connection_request.h" + +#include <memory> +#include <utility> + +#include "base/run_loop.h" +#include "base/test/task_environment.h" +#include "base/unguessable_token.h" +#include "chromeos/services/secure_channel/fake_client_connection_parameters.h" +#include "chromeos/services/secure_channel/fake_pending_connection_request_delegate.h" +#include "chromeos/services/secure_channel/public/cpp/shared/connection_priority.h" +#include "device/bluetooth/test/mock_bluetooth_adapter.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromeos { + +namespace secure_channel { + +namespace { +const char kTestFeature[] = "testFeature"; +} // namespace + +class SecureChannelPendingNearbyInitiatorConnectionRequestTest + : public testing::Test { + protected: + SecureChannelPendingNearbyInitiatorConnectionRequestTest() = default; + SecureChannelPendingNearbyInitiatorConnectionRequestTest( + const SecureChannelPendingNearbyInitiatorConnectionRequestTest&) = delete; + SecureChannelPendingNearbyInitiatorConnectionRequestTest& operator=( + const SecureChannelPendingNearbyInitiatorConnectionRequestTest&) = delete; + ~SecureChannelPendingNearbyInitiatorConnectionRequestTest() override = + default; + + // testing::Test: + void SetUp() override { + fake_pending_connection_request_delegate_ = + std::make_unique<FakePendingConnectionRequestDelegate>(); + auto fake_client_connection_parameters = + std::make_unique<FakeClientConnectionParameters>(kTestFeature); + fake_client_connection_parameters_ = + fake_client_connection_parameters.get(); + mock_adapter_ = + base::MakeRefCounted<testing::NiceMock<device::MockBluetoothAdapter>>(); + + pending_nearby_initiator_request_ = + PendingNearbyInitiatorConnectionRequest::Factory::Create( + std::move(fake_client_connection_parameters), + ConnectionPriority::kLow, + fake_pending_connection_request_delegate_.get(), mock_adapter_); + + EXPECT_TRUE(mock_adapter_->GetObservers().HasObserver(GetRequest())); + } + + const base::Optional< + PendingConnectionRequestDelegate::FailedConnectionReason>& + GetFailedConnectionReason() { + return fake_pending_connection_request_delegate_ + ->GetFailedConnectionReasonForId( + pending_nearby_initiator_request_->GetRequestId()); + } + + const base::Optional<mojom::ConnectionAttemptFailureReason>& + GetConnectionAttemptFailureReason() { + return fake_client_connection_parameters_->failure_reason(); + } + + void HandleConnectionFailure(NearbyInitiatorFailureType failure_type) { + pending_nearby_initiator_request_->HandleConnectionFailure(failure_type); + } + + void SimulateAdapterPoweredChanged(bool powered) { + GetRequest()->AdapterPoweredChanged(mock_adapter_.get(), powered); + } + + void SimulateAdapterPresentChanged(bool present) { + GetRequest()->AdapterPresentChanged(mock_adapter_.get(), present); + } + + private: + PendingNearbyInitiatorConnectionRequest* GetRequest() { + return static_cast<PendingNearbyInitiatorConnectionRequest*>( + pending_nearby_initiator_request_.get()); + } + + base::test::TaskEnvironment task_environment_; + + std::unique_ptr<FakePendingConnectionRequestDelegate> + fake_pending_connection_request_delegate_; + FakeClientConnectionParameters* fake_client_connection_parameters_; + scoped_refptr<testing::NiceMock<device::MockBluetoothAdapter>> mock_adapter_; + + std::unique_ptr<PendingConnectionRequest<NearbyInitiatorFailureType>> + pending_nearby_initiator_request_; +}; + +TEST_F(SecureChannelPendingNearbyInitiatorConnectionRequestTest, + HandleAdapterPoweredChanged) { + // Turning the adapter on should do nothing. + SimulateAdapterPoweredChanged(/*powered=*/true); + EXPECT_FALSE(GetFailedConnectionReason()); + EXPECT_FALSE(GetConnectionAttemptFailureReason()); + + // Turning the adapter off should trigger a failure. + SimulateAdapterPoweredChanged(/*powered=*/false); + EXPECT_EQ( + PendingConnectionRequestDelegate::FailedConnectionReason::kRequestFailed, + *GetFailedConnectionReason()); + EXPECT_EQ(mojom::ConnectionAttemptFailureReason::ADAPTER_DISABLED, + *GetConnectionAttemptFailureReason()); +} + +TEST_F(SecureChannelPendingNearbyInitiatorConnectionRequestTest, + HandleAdapterPresentChanged) { + // The adapter appearing should do nothing. + SimulateAdapterPresentChanged(/*present=*/true); + EXPECT_FALSE(GetFailedConnectionReason()); + EXPECT_FALSE(GetConnectionAttemptFailureReason()); + + // The adapter disappearing should trigger a failure. + SimulateAdapterPresentChanged(/*present=*/false); + EXPECT_EQ( + PendingConnectionRequestDelegate::FailedConnectionReason::kRequestFailed, + *GetFailedConnectionReason()); + EXPECT_EQ(mojom::ConnectionAttemptFailureReason::ADAPTER_NOT_PRESENT, + *GetConnectionAttemptFailureReason()); +} + +TEST_F(SecureChannelPendingNearbyInitiatorConnectionRequestTest, + HandleAuthenticationError) { + HandleConnectionFailure(NearbyInitiatorFailureType::kAuthenticationError); + EXPECT_EQ( + PendingConnectionRequestDelegate::FailedConnectionReason::kRequestFailed, + *GetFailedConnectionReason()); + EXPECT_EQ(mojom::ConnectionAttemptFailureReason::AUTHENTICATION_ERROR, + *GetConnectionAttemptFailureReason()); +} + +TEST_F(SecureChannelPendingNearbyInitiatorConnectionRequestTest, + HandleCouldNotGenerateAdvertisement) { + HandleConnectionFailure( + NearbyInitiatorFailureType::kTimeoutDiscoveringDevice); + EXPECT_EQ( + PendingConnectionRequestDelegate::FailedConnectionReason::kRequestFailed, + *GetFailedConnectionReason()); + EXPECT_EQ(mojom::ConnectionAttemptFailureReason::TIMEOUT_FINDING_DEVICE, + *GetConnectionAttemptFailureReason()); +} + +TEST_F(SecureChannelPendingNearbyInitiatorConnectionRequestTest, + HandleNearbyApiError) { + HandleConnectionFailure(NearbyInitiatorFailureType::kNearbyApiError); + EXPECT_EQ( + PendingConnectionRequestDelegate::FailedConnectionReason::kRequestFailed, + *GetFailedConnectionReason()); + EXPECT_EQ(mojom::ConnectionAttemptFailureReason::NEARBY_CONNECTION_ERROR, + *GetConnectionAttemptFailureReason()); +} + +TEST_F(SecureChannelPendingNearbyInitiatorConnectionRequestTest, + HandleConnectionRejected) { + HandleConnectionFailure(NearbyInitiatorFailureType::kConnectionRejected); + EXPECT_EQ( + PendingConnectionRequestDelegate::FailedConnectionReason::kRequestFailed, + *GetFailedConnectionReason()); + EXPECT_EQ(mojom::ConnectionAttemptFailureReason::NEARBY_CONNECTION_ERROR, + *GetConnectionAttemptFailureReason()); +} + +TEST_F(SecureChannelPendingNearbyInitiatorConnectionRequestTest, + HandleConnectivityError) { + HandleConnectionFailure(NearbyInitiatorFailureType::kConnectivityError); + EXPECT_EQ( + PendingConnectionRequestDelegate::FailedConnectionReason::kRequestFailed, + *GetFailedConnectionReason()); + EXPECT_EQ(mojom::ConnectionAttemptFailureReason::NEARBY_CONNECTION_ERROR, + *GetConnectionAttemptFailureReason()); +} + +} // namespace secure_channel + +} // namespace chromeos
diff --git a/chromeos/services/secure_channel/public/mojom/secure_channel.mojom b/chromeos/services/secure_channel/public/mojom/secure_channel.mojom index ad4051f..40f2f8e 100644 --- a/chromeos/services/secure_channel/public/mojom/secure_channel.mojom +++ b/chromeos/services/secure_channel/public/mojom/secure_channel.mojom
@@ -21,6 +21,10 @@ // relating to GATT services being unavailable. GATT_CONNECTION_ERROR, + // A Nearby Connection was attempted, but it failed. Potential causes include + // internal API errors and flaky Bluetooth/WebRTC connections. + NEARBY_CONNECTION_ERROR, + // The provided local device does not have a public key set. LOCAL_DEVICE_INVALID_PUBLIC_KEY,
diff --git a/components/autofill/core/browser/autofill_test_utils.cc b/components/autofill/core/browser/autofill_test_utils.cc index 59461e39..1d35677 100644 --- a/components/autofill/core/browser/autofill_test_utils.cc +++ b/components/autofill/core/browser/autofill_test_utils.cc
@@ -581,20 +581,34 @@ return data; } -AutofillOfferData GetCardLinkedOfferData() { +AutofillOfferData GetCardLinkedOfferData1() { AutofillOfferData data; data.offer_id = 111; data.offer_reward_amount = "5%"; // Sets the expiry to be 45 days later. data.expiry = AutofillClock::Now() + base::TimeDelta::FromDays(45); - data.offer_details_url = GURL("http://www.example.com"); + data.offer_details_url = GURL("http://www.example1.com"); data.merchant_domain = std::vector<GURL>(); - data.merchant_domain.emplace_back(GURL("http://www.example.com")); + data.merchant_domain.emplace_back(GURL("http://www.example1.com")); data.eligible_instrument_id = std::vector<int64_t>(); data.eligible_instrument_id.emplace_back(111111); return data; } +AutofillOfferData GetCardLinkedOfferData2() { + AutofillOfferData data; + data.offer_id = 222; + data.offer_reward_amount = "$10"; + // Sets the expiry to be 40 days later. + data.expiry = AutofillClock::Now() + base::TimeDelta::FromDays(40); + data.offer_details_url = GURL("http://www.example2.com"); + data.merchant_domain = std::vector<GURL>(); + data.merchant_domain.emplace_back(GURL("http://www.example2.com")); + data.eligible_instrument_id = std::vector<int64_t>(); + data.eligible_instrument_id.emplace_back(222222); + return data; +} + void SetProfileInfo(AutofillProfile* profile, const char* first_name, const char* middle_name,
diff --git a/components/autofill/core/browser/autofill_test_utils.h b/components/autofill/core/browser/autofill_test_utils.h index 684feed..66986b9 100644 --- a/components/autofill/core/browser/autofill_test_utils.h +++ b/components/autofill/core/browser/autofill_test_utils.h
@@ -184,7 +184,11 @@ CreditCardCloudTokenData GetCreditCardCloudTokenData2(); // Returns an autofill card linked offer data full of dummy info. -AutofillOfferData GetCardLinkedOfferData(); +AutofillOfferData GetCardLinkedOfferData1(); + +// Returns an autofill card linked offer data full of dummy info, different from +// the one above. +AutofillOfferData GetCardLinkedOfferData2(); // A unit testing utility that is common to a number of the Autofill unit // tests. |SetProfileInfo| provides a quick way to populate a profile with
diff --git a/components/autofill/core/browser/data_model/autofill_offer_data.cc b/components/autofill/core/browser/data_model/autofill_offer_data.cc index 55b231c..752e21ed 100644 --- a/components/autofill/core/browser/data_model/autofill_offer_data.cc +++ b/components/autofill/core/browser/data_model/autofill_offer_data.cc
@@ -15,4 +15,54 @@ AutofillOfferData& AutofillOfferData::operator=(const AutofillOfferData&) = default; +bool AutofillOfferData::operator==( + const AutofillOfferData& other_offer_data) const { + return Compare(other_offer_data) == 0; +} + +bool AutofillOfferData::operator!=( + const AutofillOfferData& other_offer_data) const { + return Compare(other_offer_data) != 0; +} + +int AutofillOfferData::Compare( + const AutofillOfferData& other_offer_data) const { + int comparison = offer_id - other_offer_data.offer_id; + if (comparison != 0) + return comparison; + + comparison = + offer_reward_amount.compare(other_offer_data.offer_reward_amount); + if (comparison != 0) + return comparison; + + if (expiry < other_offer_data.expiry) + return -1; + if (expiry > other_offer_data.expiry) + return 1; + + comparison = offer_details_url.spec().compare( + other_offer_data.offer_details_url.spec()); + if (comparison != 0) + return comparison; + + // TODO(crbug.com/1112095): Change merchant domain in AutofillOfferData to + // string (now it is GURL) and handle its comparison in a separate CL. + + std::vector<int64_t> eligible_instrument_id_copy = eligible_instrument_id; + std::vector<int64_t> other_eligible_instrument_id_copy = + other_offer_data.eligible_instrument_id; + + std::sort(eligible_instrument_id_copy.begin(), + eligible_instrument_id_copy.end()); + std::sort(other_eligible_instrument_id_copy.begin(), + other_eligible_instrument_id_copy.end()); + if (eligible_instrument_id_copy < other_eligible_instrument_id_copy) + return -1; + if (eligible_instrument_id_copy > other_eligible_instrument_id_copy) + return 1; + + return 0; +} + } // namespace autofill \ No newline at end of file
diff --git a/components/autofill/core/browser/data_model/autofill_offer_data.h b/components/autofill/core/browser/data_model/autofill_offer_data.h index 36242f3..b4fb0ba 100644 --- a/components/autofill/core/browser/data_model/autofill_offer_data.h +++ b/components/autofill/core/browser/data_model/autofill_offer_data.h
@@ -22,6 +22,13 @@ ~AutofillOfferData(); AutofillOfferData(const AutofillOfferData&); AutofillOfferData& operator=(const AutofillOfferData&); + bool operator==(const AutofillOfferData& other_offer_data) const; + bool operator!=(const AutofillOfferData& other_offer_data) const; + + // Compares two AutofillOfferData based on their member fields. Returns 0 if + // the two offer data are exactly same. Otherwise returns the comparison + // result of first found difference. + int Compare(const AutofillOfferData& other_offer_data) const; // The unique server ID for this offer data. int64_t offer_id;
diff --git a/components/autofill/core/browser/webdata/autofill_sync_bridge_util.cc b/components/autofill/core/browser/webdata/autofill_sync_bridge_util.cc index 48dc9b3..b5ea8ec 100644 --- a/components/autofill/core/browser/webdata/autofill_sync_bridge_util.cc +++ b/components/autofill/core/browser/webdata/autofill_sync_bridge_util.cc
@@ -311,6 +311,31 @@ } } +AutofillOfferData AutofillOfferDataFromOfferSpecifics( + const sync_pb::AutofillOfferSpecifics& offer_specifics) { + AutofillOfferData offer_data; + offer_data.offer_id = offer_specifics.id(); + if (offer_specifics.has_percentage_reward()) { + offer_data.offer_reward_amount = + offer_specifics.percentage_reward().percentage(); + } else { + offer_data.offer_reward_amount = + offer_specifics.fixed_amount_reward().amount(); + } + offer_data.expiry = + base::Time::UnixEpoch() + + base::TimeDelta::FromSeconds(offer_specifics.offer_expiry_date()); + offer_data.offer_details_url = GURL(offer_specifics.offer_details_url()); + for (const std::string& domain : offer_specifics.merchant_domain()) { + offer_data.merchant_domain.emplace_back(domain); + } + for (int64_t instrument_id : + offer_specifics.card_linked_offer_data().instrument_id()) { + offer_data.eligible_instrument_id.push_back(instrument_id); + } + return offer_data; +} + AutofillProfile ProfileFromSpecifics( const sync_pb::WalletPostalAddress& address) { AutofillProfile profile(AutofillProfile::SERVER_PROFILE, std::string()); @@ -431,4 +456,41 @@ } } +template <class Item> +bool AreAnyItemsDifferent(const std::vector<std::unique_ptr<Item>>& old_data, + const std::vector<Item>& new_data) { + if (old_data.size() != new_data.size()) + return true; + + std::vector<const Item*> old_ptrs; + old_ptrs.reserve(old_data.size()); + for (const std::unique_ptr<Item>& old_item : old_data) + old_ptrs.push_back(old_item.get()); + std::vector<const Item*> new_ptrs; + new_ptrs.reserve(new_data.size()); + for (const Item& new_item : new_data) + new_ptrs.push_back(&new_item); + + // Sort our vectors. + auto compare_less = [](const Item* lhs, const Item* rhs) { + return lhs->Compare(*rhs) < 0; + }; + std::sort(old_ptrs.begin(), old_ptrs.end(), compare_less); + std::sort(new_ptrs.begin(), new_ptrs.end(), compare_less); + + auto compare_equal = [](const Item* lhs, const Item* rhs) { + return lhs->Compare(*rhs) == 0; + }; + return !std::equal(old_ptrs.begin(), old_ptrs.end(), new_ptrs.begin(), + compare_equal); +} + +template bool AreAnyItemsDifferent<>( + const std::vector<std::unique_ptr<AutofillOfferData>>&, + const std::vector<AutofillOfferData>&); + +template bool AreAnyItemsDifferent<>( + const std::vector<std::unique_ptr<CreditCardCloudTokenData>>&, + const std::vector<CreditCardCloudTokenData>&); + } // namespace autofill
diff --git a/components/autofill/core/browser/webdata/autofill_sync_bridge_util.h b/components/autofill/core/browser/webdata/autofill_sync_bridge_util.h index a37d579..05c4539 100644 --- a/components/autofill/core/browser/webdata/autofill_sync_bridge_util.h +++ b/components/autofill/core/browser/webdata/autofill_sync_bridge_util.h
@@ -62,6 +62,10 @@ const AutofillOfferData& offer_data, sync_pb::AutofillOfferSpecifics* offer_specifics); +// Creates an AutofillOfferData from the specified |offer_specifics|. +AutofillOfferData AutofillOfferDataFromOfferSpecifics( + const sync_pb::AutofillOfferSpecifics& offer_specifics); + // Creates an AutofillProfile from the specified |address| specifics. AutofillProfile ProfileFromSpecifics( const sync_pb::WalletPostalAddress& address); @@ -86,6 +90,13 @@ std::vector<PaymentsCustomerData>* customer_data, std::vector<CreditCardCloudTokenData>* cloud_token_data); +// A helper function to compare two sets of data. Returns true if there is +// any difference. It uses the Compare() of the Item class instead of comparison +// operators and does not care about the order of items in the dataset. +template <class Item> +bool AreAnyItemsDifferent(const std::vector<std::unique_ptr<Item>>& old_data, + const std::vector<Item>& new_data); + } // namespace autofill #endif // COMPONENTS_AUTOFILL_CORE_BROWSER_WEBDATA_AUTOFILL_SYNC_BRIDGE_UTIL_H_
diff --git a/components/autofill/core/browser/webdata/autofill_sync_bridge_util_unittest.cc b/components/autofill/core/browser/webdata/autofill_sync_bridge_util_unittest.cc index de2b6b8..ff137b1d 100644 --- a/components/autofill/core/browser/webdata/autofill_sync_bridge_util_unittest.cc +++ b/components/autofill/core/browser/webdata/autofill_sync_bridge_util_unittest.cc
@@ -243,7 +243,7 @@ // AutofillOfferSpecifics. TEST_F(AutofillSyncBridgeUtilTest, OfferSpecificsFromOfferData) { sync_pb::AutofillOfferSpecifics offer_specifics; - AutofillOfferData offer_data = test::GetCardLinkedOfferData(); + AutofillOfferData offer_data = test::GetCardLinkedOfferData1(); SetAutofillOfferSpecificsFromOfferData(offer_data, &offer_specifics); EXPECT_EQ(offer_specifics.id(), offer_data.offer_id); @@ -270,5 +270,39 @@ } } +// Ensures that the ShouldResetAutofillWalletData function works correctly, if +// the two given data sets have the same size. +TEST_F(AutofillSyncBridgeUtilTest, + ShouldResetAutofillWalletData_SameDataSetSize) { + std::vector<std::unique_ptr<AutofillOfferData>> old_offer_data; + std::vector<AutofillOfferData> new_offer_data; + + AutofillOfferData data1 = test::GetCardLinkedOfferData1(); + AutofillOfferData data2 = test::GetCardLinkedOfferData2(); + old_offer_data.push_back(std::make_unique<AutofillOfferData>(data1)); + new_offer_data.push_back(data2); + old_offer_data.push_back(std::make_unique<AutofillOfferData>(data2)); + new_offer_data.push_back(data1); + EXPECT_FALSE(AreAnyItemsDifferent(old_offer_data, new_offer_data)); + + new_offer_data.at(0).offer_id += 456; + EXPECT_TRUE(AreAnyItemsDifferent(old_offer_data, new_offer_data)); +} + +// Ensures that the ShouldResetAutofillWalletData function works correctly, if +// the two given data sets have different size. +TEST_F(AutofillSyncBridgeUtilTest, + ShouldResetAutofillWalletData_DifferentDataSetSize) { + std::vector<std::unique_ptr<AutofillOfferData>> old_offer_data; + std::vector<AutofillOfferData> new_offer_data; + + AutofillOfferData data1 = test::GetCardLinkedOfferData1(); + AutofillOfferData data2 = test::GetCardLinkedOfferData2(); + old_offer_data.push_back(std::make_unique<AutofillOfferData>(data1)); + new_offer_data.push_back(data2); + new_offer_data.push_back(data1); + EXPECT_TRUE(AreAnyItemsDifferent(old_offer_data, new_offer_data)); +} + } // namespace } // namespace autofill
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge.cc b/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge.cc index ffee871..c935d5d 100644 --- a/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge.cc +++ b/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge.cc
@@ -9,9 +9,11 @@ #include "base/logging.h" #include "base/strings/string_number_conversions.h" #include "components/autofill/core/browser/data_model/autofill_offer_data.h" +#include "components/autofill/core/browser/webdata/autofill_sync_bridge_util.h" #include "components/autofill/core/browser/webdata/autofill_table.h" #include "components/autofill/core/browser/webdata/autofill_webdata_backend.h" #include "components/autofill/core/browser/webdata/autofill_webdata_service.h" +#include "components/sync/model/mutable_data_batch.h" #include "components/sync/model_impl/client_tag_based_model_type_processor.h" #include "components/sync/model_impl/sync_metadata_store_change_list.h" @@ -80,7 +82,17 @@ base::Optional<syncer::ModelError> AutofillWalletOfferSyncBridge::MergeSyncData( std::unique_ptr<syncer::MetadataChangeList> metadata_change_list, syncer::EntityChangeList entity_data) { - NOTIMPLEMENTED(); + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // All metadata changes have been already written, return early for an error. + base::Optional<syncer::ModelError> error = + static_cast<syncer::SyncMetadataStoreChangeList*>( + metadata_change_list.get()) + ->TakeError(); + if (error) { + return error; + } + + MergeRemoteData(std::move(entity_data)); return base::nullopt; } @@ -88,18 +100,18 @@ AutofillWalletOfferSyncBridge::ApplySyncChanges( std::unique_ptr<syncer::MetadataChangeList> metadata_change_list, syncer::EntityChangeList entity_data) { - NOTIMPLEMENTED(); + // This bridge does not support incremental updates, so whenever this is + // called, the change list should be empty. + DCHECK(entity_data.empty()) << "Received an unsupported incremental update."; return base::nullopt; } void AutofillWalletOfferSyncBridge::GetData(StorageKeyList storage_keys, - DataCallback callback) { - NOTIMPLEMENTED(); -} + DataCallback callback) {} void AutofillWalletOfferSyncBridge::GetAllDataForDebugging( DataCallback callback) { - NOTIMPLEMENTED(); + GetAllDataImpl(std::move(callback)); } std::string AutofillWalletOfferSyncBridge::GetClientTag( @@ -122,7 +134,66 @@ void AutofillWalletOfferSyncBridge::ApplyStopSyncChanges( std::unique_ptr<syncer::MetadataChangeList> delete_metadata_change_list) { - NOTIMPLEMENTED(); + // If a metadata change list gets passed in, that means sync is actually + // disabled, so we want to delete the payments data. + if (delete_metadata_change_list) { + MergeRemoteData(syncer::EntityChangeList()); + } +} + +void AutofillWalletOfferSyncBridge::GetAllDataImpl(DataCallback callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + std::vector<std::unique_ptr<AutofillOfferData>> offers; + if (!GetAutofillTable()->GetCreditCardOffers(&offers)) { + change_processor()->ReportError( + {FROM_HERE, "Failed to load offer data from table."}); + return; + } + + auto batch = std::make_unique<syncer::MutableDataBatch>(); + for (const std::unique_ptr<AutofillOfferData>& offer : offers) { + auto entity_data = std::make_unique<syncer::EntityData>(); + sync_pb::AutofillOfferSpecifics* offer_specifics = + entity_data->specifics.mutable_autofill_offer(); + SetAutofillOfferSpecificsFromOfferData(*offer, offer_specifics); + + entity_data->name = + "Offer " + + GetBase64EncodedId(GetClientTagFromSpecifics(*offer_specifics)); + + batch->Put(GetStorageKeyFromSpecifics(*offer_specifics), + std::move(entity_data)); + } + std::move(callback).Run(std::move(batch)); +} + +void AutofillWalletOfferSyncBridge::MergeRemoteData( + const syncer::EntityChangeList& entity_data) { + std::vector<AutofillOfferData> offer_data; + for (const std::unique_ptr<syncer::EntityChange>& change : entity_data) { + DCHECK(change->data().specifics.has_autofill_offer()); + // TODO(crbug.com/1112095): Add offer data validation. + offer_data.push_back(AutofillOfferDataFromOfferSpecifics( + change->data().specifics.autofill_offer())); + } + + AutofillTable* table = GetAutofillTable(); + + // Only do a write operation if there is any difference between server data + // and local data. + std::vector<std::unique_ptr<AutofillOfferData>> existing_offers; + table->GetCreditCardOffers(&existing_offers); + + if (AreAnyItemsDifferent(existing_offers, offer_data)) + table->SetCreditCardOffers(offer_data); + + // Commit the transaction to make sure the data and the metadata with the + // new progress marker is written down (especially on Android where we + // cannot rely on committing transactions on shutdown). We need to commit + // even if the wallet data has not changed because the model type state incl. + // the progress marker always changes. + web_data_backend_->CommitChanges(); } AutofillTable* AutofillWalletOfferSyncBridge::GetAutofillTable() {
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge.h b/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge.h index 71ed0373..df1f4d1b 100644 --- a/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge.h +++ b/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge.h
@@ -35,7 +35,7 @@ static syncer::ModelTypeSyncBridge* FromWebDataService( AutofillWebDataService* web_data_service); - explicit AutofillWalletOfferSyncBridge( + AutofillWalletOfferSyncBridge( std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor, AutofillWebDataBackend* web_data_backend); ~AutofillWalletOfferSyncBridge() override; @@ -62,6 +62,12 @@ delete_metadata_change_list) override; private: + // Helper function to send all offer data to the callback. + void GetAllDataImpl(DataCallback callback); + + // Merges synced remote offer data. + void MergeRemoteData(const syncer::EntityChangeList& entity_data); + // Returns the table associated with the |web_data_backend_|. AutofillTable* GetAutofillTable();
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge_unittest.cc b/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge_unittest.cc index 249a7171..950b0f60 100644 --- a/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge_unittest.cc +++ b/components/autofill/core/browser/webdata/autofill_wallet_offer_sync_bridge_unittest.cc
@@ -10,7 +10,9 @@ #include <utility> #include "base/files/scoped_temp_dir.h" +#include "base/strings/strcat.h" #include "base/strings/string_number_conversions.h" +#include "base/test/bind_test_util.h" #include "base/test/task_environment.h" #include "base/time/time.h" #include "components/autofill/core/browser/autofill_test_utils.h" @@ -26,6 +28,7 @@ #include "components/sync/model/mock_model_type_change_processor.h" #include "components/sync/model/sync_data.h" #include "components/sync/model_impl/client_tag_based_model_type_processor.h" +#include "components/sync/model_impl/in_memory_metadata_change_list.h" #include "components/sync/protocol/autofill_specifics.pb.h" #include "components/sync/protocol/sync.pb.h" #include "components/webdata/common/web_database.h" @@ -45,6 +48,57 @@ const char kLocaleString[] = "en-US"; const char kDefaultCacheGuid[] = "CacheGuid"; + +void ExtractAutofillOfferSpecificsFromDataBatch( + std::unique_ptr<syncer::DataBatch> batch, + std::vector<AutofillOfferSpecifics>* output) { + while (batch->HasNext()) { + const syncer::KeyAndData& data_pair = batch->Next(); + output->push_back(data_pair.second->specifics.autofill_offer()); + } +} + +std::string AutofillOfferSpecificsAsDebugString( + const AutofillOfferSpecifics& specifics) { + std::ostringstream output; + + std::string offer_reward_amount_string = + specifics.has_percentage_reward() + ? specifics.percentage_reward().percentage() + : specifics.fixed_amount_reward().amount(); + + std::string domain_string; + for (std::string merchant_domain : specifics.merchant_domain()) { + base::StrAppend(&domain_string, {merchant_domain, ", "}); + } + + std::string instrument_id_string; + for (int64_t eligible_instrument_id : + specifics.card_linked_offer_data().instrument_id()) { + base::StrAppend(&instrument_id_string, + {base::NumberToString(eligible_instrument_id), ", "}); + } + + output << "[id: " << specifics.id() + << ", offer_reward_amount: " << offer_reward_amount_string + << ", offer_expiry_date: " << specifics.offer_expiry_date() + << ", offer_details_url: " << specifics.offer_details_url() + << ", merchant_domain: " << domain_string + << ", eligible_instrument_id: " << instrument_id_string << "]"; + return output.str(); +} + +MATCHER_P(EqualsSpecifics, expected, "") { + if (arg.SerializeAsString() != expected.SerializeAsString()) { + *result_listener << "entry\n" + << AutofillOfferSpecificsAsDebugString(arg) << "\n" + << "did not match expected\n" + << AutofillOfferSpecificsAsDebugString(expected); + return false; + } + return true; +} + } // namespace class AutofillWalletOfferSyncBridgeTest : public testing::Test { @@ -88,6 +142,53 @@ mock_processor_.CreateForwardingProcessor(), &backend_); } + void StartSyncing( + const std::vector<AutofillOfferSpecifics>& remote_data = {}) { + base::RunLoop loop; + syncer::DataTypeActivationRequest request; + request.error_handler = base::DoNothing(); + request.cache_guid = kDefaultCacheGuid; + real_processor_->OnSyncStarting( + request, + base::BindLambdaForTesting( + [&loop](std::unique_ptr<syncer::DataTypeActivationResponse>) { + loop.Quit(); + })); + loop.Run(); + + // Initialize the processor with initial_sync_done. + sync_pb::ModelTypeState state; + state.set_initial_sync_done(true); + state.mutable_progress_marker() + ->mutable_gc_directive() + ->set_version_watermark(1); + syncer::UpdateResponseDataList initial_updates; + for (const AutofillOfferSpecifics& specifics : remote_data) { + initial_updates.push_back(SpecificsToUpdateResponse(specifics)); + } + real_processor_->OnUpdateReceived(state, std::move(initial_updates)); + } + + std::vector<AutofillOfferSpecifics> GetAllLocalData() { + std::vector<AutofillOfferSpecifics> data; + // Perform an async call synchronously for testing. + base::RunLoop loop; + bridge()->GetAllDataForDebugging(base::BindLambdaForTesting( + [&loop, &data](std::unique_ptr<syncer::DataBatch> batch) { + ExtractAutofillOfferSpecificsFromDataBatch(std::move(batch), &data); + loop.Quit(); + })); + loop.Run(); + return data; + } + + syncer::UpdateResponseData SpecificsToUpdateResponse( + const AutofillOfferSpecifics& specifics) { + syncer::UpdateResponseData data; + data.entity = SpecificsToEntity(specifics); + return data; + } + EntityData SpecificsToEntity(const AutofillOfferSpecifics& specifics) { EntityData data; *data.specifics.mutable_autofill_offer() = specifics; @@ -100,6 +201,8 @@ AutofillWalletOfferSyncBridge* bridge() { return bridge_.get(); } + MockAutofillWebDataBackend* backend() { return &backend_; } + private: ScopedTempDir temp_dir_; base::test::SingleThreadTaskEnvironment task_environment_; @@ -113,7 +216,7 @@ TEST_F(AutofillWalletOfferSyncBridgeTest, VerifyGetClientTag) { AutofillOfferSpecifics specifics; - AutofillOfferData data = test::GetCardLinkedOfferData(); + AutofillOfferData data = test::GetCardLinkedOfferData1(); SetAutofillOfferSpecificsFromOfferData(data, &specifics); EXPECT_EQ(bridge()->GetClientTag(SpecificsToEntity(specifics)), base::NumberToString(data.offer_id)); @@ -121,10 +224,78 @@ TEST_F(AutofillWalletOfferSyncBridgeTest, VerifyGetStorageKey) { AutofillOfferSpecifics specifics; - AutofillOfferData data = test::GetCardLinkedOfferData(); + AutofillOfferData data = test::GetCardLinkedOfferData1(); SetAutofillOfferSpecificsFromOfferData(data, &specifics); EXPECT_EQ(bridge()->GetStorageKey(SpecificsToEntity(specifics)), base::NumberToString(data.offer_id)); } +// Tests that when a new offer data is sent by the server, the client only keeps +// the new data. +TEST_F(AutofillWalletOfferSyncBridgeTest, MergeSyncData_NewData) { + // Create one offer data in the client table. + AutofillOfferData old_data = test::GetCardLinkedOfferData1(); + table()->SetCreditCardOffers({old_data}); + + // Create a different one on the server. + AutofillOfferSpecifics offer_specifics; + SetAutofillOfferSpecificsFromOfferData(test::GetCardLinkedOfferData2(), + &offer_specifics); + + EXPECT_CALL(*backend(), CommitChanges()); + StartSyncing({offer_specifics}); + + // Only the server offer should be present on the client. + EXPECT_THAT(GetAllLocalData(), + testing::UnorderedElementsAre(EqualsSpecifics(offer_specifics))); +} + +// Tests that when no data is sent by the server, all local data should be +// deleted. +TEST_F(AutofillWalletOfferSyncBridgeTest, MergeSyncData_NoData) { + // Create one offer data in the client table. + AutofillOfferData client_data = test::GetCardLinkedOfferData1(); + table()->SetCreditCardOffers({client_data}); + + EXPECT_CALL(*backend(), CommitChanges()); + StartSyncing({}); + + EXPECT_TRUE(GetAllLocalData().empty()); +} + +// Tests that when sync is stopped and the data type is disabled, client should +// remove all client data. +TEST_F(AutofillWalletOfferSyncBridgeTest, ApplyStopSyncChanges_ClearAllData) { + // Create one offer data in the client table. + AutofillOfferData client_data = test::GetCardLinkedOfferData1(); + table()->SetCreditCardOffers({client_data}); + + EXPECT_CALL(*backend(), CommitChanges()); + + // Passing in a non-null metadata change list indicates to the bridge that + // sync is stopping but the data type is not disabled. + bridge()->ApplyStopSyncChanges(/*delete_metadata_change_list=*/ + std::make_unique< + syncer::InMemoryMetadataChangeList>()); + + EXPECT_TRUE(GetAllLocalData().empty()); +} + +// Tests that when sync is stopped but the data type is not disabled, client +// should keep all the data. +TEST_F(AutofillWalletOfferSyncBridgeTest, ApplyStopSyncChanges_KeepAllData) { + // Create one offer data in the client table. + AutofillOfferData client_data = test::GetCardLinkedOfferData1(); + table()->SetCreditCardOffers({client_data}); + + // We do not write to DB at all, so we should not commit any changes. + EXPECT_CALL(*backend(), CommitChanges()).Times(0); + + // Passing in a null metadata change list indicates to the bridge that + // sync is stopping and the data type is disabled. + bridge()->ApplyStopSyncChanges(/*delete_metadata_change_list=*/nullptr); + + EXPECT_FALSE(GetAllLocalData().empty()); +} + } // namespace autofill
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge.cc b/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge.cc index 6d580c0..8cafefa1 100644 --- a/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge.cc +++ b/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge.cc
@@ -435,7 +435,7 @@ std::vector<std::unique_ptr<CreditCardCloudTokenData>> existing_data; table->GetCreditCardCloudTokenData(&existing_data); - if (ShouldResetAutofillWalletData(existing_data, cloud_token_data)) { + if (AreAnyItemsDifferent(existing_data, cloud_token_data)) { table->SetCreditCardCloudTokenData(cloud_token_data); return true; } @@ -512,36 +512,6 @@ return result; } -template <class Item> -bool AutofillWalletSyncBridge::ShouldResetAutofillWalletData( - const std::vector<std::unique_ptr<Item>>& old_data, - const std::vector<Item>& new_data) { - std::vector<const Item*> old_ptrs; - old_ptrs.reserve(old_data.size()); - for (const std::unique_ptr<Item>& old_item : old_data) - old_ptrs.push_back(old_item.get()); - std::vector<const Item*> new_ptrs; - new_ptrs.reserve(new_data.size()); - for (const Item& new_item : new_data) - new_ptrs.push_back(&new_item); - - if (old_ptrs.size() != new_ptrs.size()) - return true; - - // Sort our vectors. - auto compare_less = [](const Item* lhs, const Item* rhs) { - return lhs->Compare(*rhs) < 0; - }; - std::sort(old_ptrs.begin(), old_ptrs.end(), compare_less); - std::sort(new_ptrs.begin(), new_ptrs.end(), compare_less); - - auto compare_equal = [](const Item* lhs, const Item* rhs) { - return lhs->Compare(*rhs) == 0; - }; - return !std::equal(old_ptrs.begin(), old_ptrs.end(), new_ptrs.begin(), - compare_equal); -} - AutofillTable* AutofillWalletSyncBridge::GetAutofillTable() { return AutofillTable::FromWebDatabase(web_data_backend_->GetDatabase()); }
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge.h b/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge.h index 853ed8a..4f4975b 100644 --- a/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge.h +++ b/components/autofill/core/browser/webdata/autofill_wallet_sync_bridge.h
@@ -127,13 +127,6 @@ const std::vector<std::unique_ptr<Item>>& old_data, const std::vector<Item>& new_data); - // A simpler version of ComputeAutofillWalletDiff that only returns true if - // there is any difference. - template <class Item> - bool ShouldResetAutofillWalletData( - const std::vector<std::unique_ptr<Item>>& old_data, - const std::vector<Item>& new_data); - // Returns the table associated with the |web_data_backend_|. AutofillTable* GetAutofillTable();
diff --git a/components/autofill/core/common/renderer_id.h b/components/autofill/core/common/renderer_id.h index 511d4d2..cf3941f 100644 --- a/components/autofill/core/common/renderer_id.h +++ b/components/autofill/core/common/renderer_id.h
@@ -12,6 +12,9 @@ namespace autofill { +// A value that can be provided by the renderer in cases when the ID is missing. +constexpr int kNotSetRendererID = -1; + namespace internal { using FormRendererIdType = ::util::IdType<class FormRendererIdMarker,
diff --git a/components/autofill/ios/browser/autofill_util.h b/components/autofill/ios/browser/autofill_util.h index fd8cab3..a533b81 100644 --- a/components/autofill/ios/browser/autofill_util.h +++ b/components/autofill/ios/browser/autofill_util.h
@@ -12,10 +12,6 @@ @class CRWJSInjectionReceiver; class GURL; -namespace { -constexpr int kNotSetRendererID = -1; -} - namespace base { class DictionaryValue; }
diff --git a/components/autofill/ios/form_util/form_activity_params.h b/components/autofill/ios/form_util/form_activity_params.h index 0a6860d..0feafa8 100644 --- a/components/autofill/ios/form_util/form_activity_params.h +++ b/components/autofill/ios/form_util/form_activity_params.h
@@ -6,6 +6,8 @@ #include <string> +#include "components/autofill/core/common/renderer_id.h" + namespace autofill { // Wraps information about event happening in a form. @@ -31,10 +33,10 @@ ~FormActivityParams(); std::string form_name; - uint32_t unique_form_id; + FormRendererId unique_form_id; // Generated by __gCrWeb.form.getFieldIdentifier in form.js. std::string field_identifier; - uint32_t unique_field_id; + FieldRendererId unique_field_id; std::string field_type; std::string type; std::string value;
diff --git a/components/autofill/ios/form_util/form_activity_tab_helper.mm b/components/autofill/ios/form_util/form_activity_tab_helper.mm index d4e4020..bfc333b 100644 --- a/components/autofill/ios/form_util/form_activity_tab_helper.mm +++ b/components/autofill/ios/form_util/form_activity_tab_helper.mm
@@ -19,6 +19,9 @@ #error "This file requires ARC support." #endif +using base::NumberToString; +using base::StringToUint; + namespace autofill { namespace { @@ -97,8 +100,15 @@ !message.GetBoolean("hasUserGesture", ¶ms.has_user_gesture)) { params.input_missing = true; } - base::StringToUint(unique_form_id, ¶ms.unique_form_id); - base::StringToUint(unique_field_id, ¶ms.unique_field_id); + if (unique_form_id != NumberToString(kNotSetRendererID)) + StringToUint(unique_form_id, ¶ms.unique_form_id.value()); + else + params.unique_form_id = FormRendererId(); + + if (unique_field_id != NumberToString(kNotSetRendererID)) + StringToUint(unique_field_id, ¶ms.unique_field_id.value()); + else + params.unique_field_id = FieldRendererId(); params.is_main_frame = form_in_main_frame; if (!sender_frame) {
diff --git a/components/crash/content/browser/error_reporting/send_javascript_error_report.cc b/components/crash/content/browser/error_reporting/send_javascript_error_report.cc index d39c58a..1ce1aeb 100644 --- a/components/crash/content/browser/error_reporting/send_javascript_error_report.cc +++ b/components/crash/content/browser/error_reporting/send_javascript_error_report.cc
@@ -21,7 +21,6 @@ #include "base/system/sys_info.h" #include "base/task/task_traits.h" #include "base/task/thread_pool.h" -#include "build/build_config.h" #include "components/crash/content/browser/error_reporting/javascript_error_report.h" #include "components/crash/core/app/client_upload_info.h" #include "components/feedback/redaction_tool.h" @@ -40,8 +39,6 @@ constexpr char kCrashEndpointUrl[] = ""; #endif -constexpr int kCrashEndpointResponseMaxSizeInBytes = 1024; - std::string& GetCrashEndpoint() { static base::NoDestructor<std::string> crash_endpoint(kCrashEndpointUrl); return *crash_endpoint; @@ -64,6 +61,10 @@ return *testing_override; } +// TODO(crbug.com/1129544) This is currently disabled due to Windows DLL +// thunking issues. Fix & re-enable. +#if !defined(OS_WIN) + void OnRequestComplete(std::unique_ptr<network::SimpleURLLoader> url_loader, base::ScopedClosureRunner callback_runner, std::unique_ptr<std::string> response_body) { @@ -222,6 +223,7 @@ url_loader->AttachStringForUpload(body, "text/plain"); } + constexpr int kCrashEndpointResponseMaxSizeInBytes = 1024; network::SimpleURLLoader* loader = url_loader.get(); loader->DownloadToString( loader_factory, @@ -286,8 +288,14 @@ SendReport(url, body, std::move(callback_runner), loader_factory.get()); } +#endif // !defined(OS_WIN) + } // namespace +// TODO(crbug.com/1129544) This is currently disabled due to Windows DLL +// thunking issues. Fix & re-enable. +#if !defined(OS_WIN) + void SendJavaScriptErrorReport(JavaScriptErrorReport error_report, base::OnceClosure completion_callback, content::BrowserContext* browser_context) { @@ -309,6 +317,8 @@ std::move(loader_factory))); } +#endif // !defined(OS_WIN) + void SetCrashEndpointForTesting(const std::string& endpoint) { GetCrashEndpoint() = endpoint; }
diff --git a/components/crash/content/browser/error_reporting/send_javascript_error_report.h b/components/crash/content/browser/error_reporting/send_javascript_error_report.h index 9eacc65..70ab3b36 100644 --- a/components/crash/content/browser/error_reporting/send_javascript_error_report.h +++ b/components/crash/content/browser/error_reporting/send_javascript_error_report.h
@@ -10,12 +10,17 @@ #include <string> #include "base/callback_forward.h" +#include "build/build_config.h" namespace content { class BrowserContext; } struct JavaScriptErrorReport; +// TODO(crbug.com/1129544) This is currently disabled due to Windows DLL +// thunking issues. Fix & re-enable. +#if !defined(OS_WIN) + // Sends a report of an error in JavaScript (such as an unhandled exception) to // Google's error collection service. This should be called on the UI thread; // it will return after the report sending is started. |completion_callback| is @@ -24,6 +29,8 @@ base::OnceClosure completion_callback, content::BrowserContext* browser_context); +#endif // !defined(OS_WIN) + // Override the URL we send the crashes to. void SetCrashEndpointForTesting(const std::string& endpoint);
diff --git a/components/crash/content/browser/error_reporting/send_javascript_error_report_unittest.cc b/components/crash/content/browser/error_reporting/send_javascript_error_report_unittest.cc index 7e18c8a..7e0417d 100644 --- a/components/crash/content/browser/error_reporting/send_javascript_error_report_unittest.cc +++ b/components/crash/content/browser/error_reporting/send_javascript_error_report_unittest.cc
@@ -18,6 +18,10 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +// TODO(crbug.com/1129544) The SendJavaScriptErrorReport function is currently +// disabled due to Windows DLL thunking issues. Fix & re-enable. +#if !defined(OS_WIN) + using ::testing::AllOf; using ::testing::HasSubstr; @@ -222,3 +226,5 @@ endpoint_->last_report(); EXPECT_FALSE(actual_report); } + +#endif // !defined(OS_WIN)
diff --git a/components/exo/surface.cc b/components/exo/surface.cc index 828520e..218231e 100644 --- a/components/exo/surface.cc +++ b/components/exo/surface.cc
@@ -590,7 +590,7 @@ void Surface::SetAcquireFence(std::unique_ptr<gfx::GpuFence> gpu_fence) { TRACE_EVENT1("exo", "Surface::SetAcquireFence", "fence_fd", - gpu_fence ? gpu_fence->GetGpuFenceHandle().native_fd.fd : -1); + gpu_fence ? gpu_fence->GetGpuFenceHandle().owned_fd.get() : -1); pending_acquire_fence_ = std::move(gpu_fence); }
diff --git a/components/exo/wayland/zwp_linux_explicit_synchronization.cc b/components/exo/wayland/zwp_linux_explicit_synchronization.cc index 55acaf1..7e713c6 100644 --- a/components/exo/wayland/zwp_linux_explicit_synchronization.cc +++ b/components/exo/wayland/zwp_linux_explicit_synchronization.cc
@@ -97,9 +97,9 @@ gfx::GpuFenceHandle handle; handle.type = gfx::GpuFenceHandleType::kAndroidNativeFenceSync; - handle.native_fd = base::FileDescriptor(std::move(fence_fd)); + handle.owned_fd = std::move(fence_fd); - surface->SetAcquireFence(std::make_unique<gfx::GpuFence>(handle)); + surface->SetAcquireFence(std::make_unique<gfx::GpuFence>(std::move(handle))); } void linux_surface_synchronization_get_release(wl_client* client,
diff --git a/components/messages/android/BUILD.gn b/components/messages/android/BUILD.gn index 7fe447c..1473718 100644 --- a/components/messages/android/BUILD.gn +++ b/components/messages/android/BUILD.gn
@@ -9,7 +9,9 @@ "java/src/org/chromium/components/messages/MessageBannerProperties.java", "java/src/org/chromium/components/messages/MessageBannerView.java", "java/src/org/chromium/components/messages/MessageBannerViewBinder.java", + "java/src/org/chromium/components/messages/MessageContainer.java", "java/src/org/chromium/components/messages/MessageStateHandler.java", + "java/src/org/chromium/components/messages/SingleActionMessage.java", ] resources_package = "org.chromium.components.messages" deps = [ @@ -46,6 +48,7 @@ testonly = true sources = [ "java/src/org/chromium/components/messages/MessageBannerRenderTest.java", + "java/src/org/chromium/components/messages/SingleActionMessageTest.java", ] resources_package = "org.chromium.components.messages" deps = [
diff --git a/components/messages/android/DEPS b/components/messages/android/DEPS index cb9a88c..b22bb39e 100644 --- a/components/messages/android/DEPS +++ b/components/messages/android/DEPS
@@ -1,5 +1,6 @@ include_rules = [ "+ui/android", + "+base/test/android", "+content/public/test/android", "+components/browser_ui/banners/android", ]
diff --git a/components/messages/android/java/res/layout/message_banner_view.xml b/components/messages/android/java/res/layout/message_banner_view.xml index c47bf1b..b26aff9 100644 --- a/components/messages/android/java/res/layout/message_banner_view.xml +++ b/components/messages/android/java/res/layout/message_banner_view.xml
@@ -13,6 +13,10 @@ android:layout_height="@dimen/message_banner_height" android:layout_width="match_parent" android:orientation="horizontal" + android:layout_marginTop="@dimen/message_shadow_top_margin" + android:layout_marginBottom="@dimen/message_shadow_bottom_margin" + android:layout_marginStart="@dimen/message_shadow_lateral_margin" + android:layout_marginEnd="@dimen/message_shadow_lateral_margin" android:elevation="@dimen/message_banner_elevation" android:background="@drawable/message_bg_tinted">
diff --git a/components/messages/android/java/res/values/dimens.xml b/components/messages/android/java/res/values/dimens.xml index 02f082b..55eab918 100644 --- a/components/messages/android/java/res/values/dimens.xml +++ b/components/messages/android/java/res/values/dimens.xml
@@ -14,5 +14,8 @@ <dimen name="message_icon_padding">12dp</dimen> <dimen name="message_button_padding">16dp</dimen> <dimen name="message_divider_margin">12dp</dimen> + <dimen name="message_shadow_top_margin">8dp</dimen> + <dimen name="message_shadow_lateral_margin">12dp</dimen> + <dimen name="message_shadow_bottom_margin">16dp</dimen> </resources> \ No newline at end of file
diff --git a/components/messages/android/java/src/org/chromium/components/messages/MessageBannerProperties.java b/components/messages/android/java/src/org/chromium/components/messages/MessageBannerProperties.java index 723789c..bd1fc5d 100644 --- a/components/messages/android/java/src/org/chromium/components/messages/MessageBannerProperties.java +++ b/components/messages/android/java/src/org/chromium/components/messages/MessageBannerProperties.java
@@ -28,8 +28,15 @@ new WritableObjectPropertyKey<>(); public static final WritableObjectPropertyKey<String> SECONDARY_ICON_CONTENT_DESCRIPTION = new WritableObjectPropertyKey<>(); + // TODO(crbug.com/1123947): remove this since on_dismissed is not a property of the view? + public static final WritableObjectPropertyKey<Runnable> ON_DISMISSED = + new WritableObjectPropertyKey<>(); public static final PropertyKey[] ALL_KEYS = new PropertyKey[] {PRIMARY_BUTTON_TEXT, PRIMARY_BUTTON_CLICK_LISTENER, TITLE, DESCRIPTION, ICON, SECONDARY_ICON, SECONDARY_ICON_CONTENT_DESCRIPTION}; + + public static final PropertyKey[] SINGLE_ACTION_MESSAGE_KEYS = + new PropertyKey[] {PRIMARY_BUTTON_TEXT, PRIMARY_BUTTON_CLICK_LISTENER, TITLE, + DESCRIPTION, ICON, ON_DISMISSED}; }
diff --git a/components/messages/android/java/src/org/chromium/components/messages/MessageContainer.java b/components/messages/android/java/src/org/chromium/components/messages/MessageContainer.java new file mode 100644 index 0000000..b50fdb56 --- /dev/null +++ b/components/messages/android/java/src/org/chromium/components/messages/MessageContainer.java
@@ -0,0 +1,46 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.messages; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +/** + * Container holding messages. + */ +public class MessageContainer extends FrameLayout { + public MessageContainer(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + /** + * Show a given message view on the screen. There should be no view inside the container + * before adding a message. + * @param view The message view to display on the screen. + */ + void addMessage(View view) { + if (getChildCount() != 0) { + throw new IllegalStateException( + "Should not contain any view when adding a new message."); + } + addView(view); + } + + /** + * Hide the given message view, which is being shown inside the container. + * @param view The message which should be removed. + */ + void removeMessage(View view) { + if (getChildCount() == 0) { + throw new IllegalStateException("The given view is not being shown."); + } + removeAllViews(); + } +}
diff --git a/components/messages/android/java/src/org/chromium/components/messages/SingleActionMessage.java b/components/messages/android/java/src/org/chromium/components/messages/SingleActionMessage.java new file mode 100644 index 0000000..b233db9 --- /dev/null +++ b/components/messages/android/java/src/org/chromium/components/messages/SingleActionMessage.java
@@ -0,0 +1,59 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.messages; + +import android.view.LayoutInflater; + +import org.chromium.ui.modelutil.PropertyModel; +import org.chromium.ui.modelutil.PropertyModelChangeProcessor; + +/** + * Coordinator to show / hide a banner message on given container and delegate events. + */ +public class SingleActionMessage implements MessageStateHandler { + private MessageBannerView mView; + private final MessageContainer mContainer; + private final PropertyModel mModel; + + /** + * @param container The container holding messages. + * @param model The PropertyModel with {@link + * MessageBannerProperties#SINGLE_ACTION_MESSAGE_KEYS}. + */ + public SingleActionMessage(MessageContainer container, PropertyModel model) { + mModel = model; + mContainer = container; + } + + /** + * Show a message view on the given {@link MessageContainer}. + */ + @Override + public void show() { + if (mView == null) { + mView = (MessageBannerView) LayoutInflater.from(mContainer.getContext()) + .inflate(R.layout.message_banner_view, mContainer, false); + PropertyModelChangeProcessor.create(mModel, mView, MessageBannerViewBinder::bind); + } + mContainer.addMessage(mView); + } + + /** + * Hide the message view shown on the given {@link MessageContainer}. + */ + @Override + public void hide() { + mContainer.removeMessage(mView); + } + + /** + * Remove message from the message queue so that the message will not be shown anymore. + */ + @Override + public void dismiss() { + Runnable onDismissed = mModel.get(MessageBannerProperties.ON_DISMISSED); + if (onDismissed != null) onDismissed.run(); + } +}
diff --git a/components/messages/android/java/src/org/chromium/components/messages/SingleActionMessageTest.java b/components/messages/android/java/src/org/chromium/components/messages/SingleActionMessageTest.java new file mode 100644 index 0000000..09e21a7e --- /dev/null +++ b/components/messages/android/java/src/org/chromium/components/messages/SingleActionMessageTest.java
@@ -0,0 +1,78 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.components.messages; + +import android.app.Activity; + +import androidx.test.filters.SmallTest; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.chromium.base.ApiCompatibilityUtils; +import org.chromium.base.test.BaseJUnit4ClassRunner; +import org.chromium.base.test.util.CallbackHelper; +import org.chromium.ui.modelutil.PropertyModel; +import org.chromium.ui.test.util.DummyUiActivityTestCase; + +/** + * Tests for {@link SingleActionMessage}. + */ +@RunWith(BaseJUnit4ClassRunner.class) +public class SingleActionMessageTest extends DummyUiActivityTestCase { + private CallbackHelper mDismissCallback; + + @Override + public void setUpTest() throws Exception { + super.setUpTest(); + mDismissCallback = new CallbackHelper(); + } + + @Test + @SmallTest + public void testAddAndRemoveSingleActionMessage() throws Exception { + MessageContainer container = new MessageContainer(getActivity(), null); + PropertyModel model = createBasicSingleActionMessageModel(); + SingleActionMessage message = new SingleActionMessage(container, model); + message.show(); + Assert.assertEquals( + "Message container should have one message view after the message is shown.", 1, + container.getChildCount()); + message.hide(); + Assert.assertEquals( + "Message container should not have any view after the message is hidden.", 0, + container.getChildCount()); + message.dismiss(); + mDismissCallback.waitForFirst( + "Dismiss callback should be called when message is dismissed"); + } + + @Test(expected = IllegalStateException.class) + @SmallTest + public void testAddMultipleSingleActionMessage() { + MessageContainer container = new MessageContainer(getActivity(), null); + PropertyModel m1 = createBasicSingleActionMessageModel(); + PropertyModel m2 = createBasicSingleActionMessageModel(); + SingleActionMessage message1 = new SingleActionMessage(container, m1); + SingleActionMessage message2 = new SingleActionMessage(container, m2); + message1.show(); + message2.show(); + } + + private PropertyModel createBasicSingleActionMessageModel() { + Activity activity = getActivity(); + return new PropertyModel.Builder(MessageBannerProperties.SINGLE_ACTION_MESSAGE_KEYS) + .with(MessageBannerProperties.TITLE, "test") + .with(MessageBannerProperties.DESCRIPTION, "Description") + .with(MessageBannerProperties.ICON, + ApiCompatibilityUtils.getDrawable( + activity.getResources(), android.R.drawable.ic_menu_add)) + .with(MessageBannerProperties.PRIMARY_BUTTON_CLICK_LISTENER, (v) -> {}) + .with(MessageBannerProperties.ON_DISMISSED, + () -> { mDismissCallback.notifyCalled(); }) + .build(); + } +}
diff --git a/components/password_manager/ios/js_password_manager.mm b/components/password_manager/ios/js_password_manager.mm index 21ae4d6..f74a844 100644 --- a/components/password_manager/ios/js_password_manager.mm +++ b/components/password_manager/ios/js_password_manager.mm
@@ -20,6 +20,7 @@ using autofill::CreateStringCallback; using autofill::FormRendererId; using autofill::FieldRendererId; +using autofill::kNotSetRendererID; using base::SysNSStringToUTF8; namespace password_manager {
diff --git a/components/password_manager/ios/shared_password_controller.mm b/components/password_manager/ios/shared_password_controller.mm index 4a1126d..d973e40 100644 --- a/components/password_manager/ios/shared_password_controller.mm +++ b/components/password_manager/ios/shared_password_controller.mm
@@ -648,7 +648,7 @@ if (params.type == "password_form_removed") { _passwordManager->OnPasswordFormRemoved( _delegate.passwordManagerDriver, self.formHelper.fieldDataManager.get(), - FormRendererId(params.unique_form_id)); + params.unique_form_id); } }
diff --git a/components/payments/core/secure_payment_confirmation_instrument.cc b/components/payments/core/secure_payment_confirmation_instrument.cc index 1674561..8de0aa43 100644 --- a/components/payments/core/secure_payment_confirmation_instrument.cc +++ b/components/payments/core/secure_payment_confirmation_instrument.cc
@@ -6,6 +6,8 @@ #include <utility> +#include "base/metrics/histogram_functions.h" + namespace payments { SecurePaymentConfirmationInstrument::SecurePaymentConfirmationInstrument() = @@ -19,7 +21,13 @@ : credential_id(std::move(credential_id)), relying_party_id(relying_party_id), label(label), - icon(std::move(icon)) {} + icon(std::move(icon)) { + // Record the size of credential_id to see whether or not hashing is needed + // before storing in DB. crbug.com/1122764 + base::UmaHistogramCounts10000( + "PaymentRequest.SecurePaymentConfirmationCredentialIdSizeInBytes", + credential_id.size()); +} SecurePaymentConfirmationInstrument::~SecurePaymentConfirmationInstrument() = default; @@ -29,4 +37,4 @@ !label.empty() && !icon.empty(); } -} // namespace payments \ No newline at end of file +} // namespace payments
diff --git a/components/variations/BUILD.gn b/components/variations/BUILD.gn index 659a284..0baea092 100644 --- a/components/variations/BUILD.gn +++ b/components/variations/BUILD.gn
@@ -17,6 +17,19 @@ flags = [ "LARGE_VARIATION_KEY_SIZE=$large_variation_key_size_enabled" ] } +component("variations_features") { + output_name = "variations_features" + + defines = [ "IS_VARIATIONS_FEATURES_IMPL" ] + + sources = [ + "variations_features.cc", + "variations_features.h", + ] + + deps = [ "//base" ] +} + static_library("variations") { sources = [ "active_field_trials.cc", @@ -86,6 +99,8 @@ ] } + public_deps = [ ":variations_features" ] + deps = [ ":buildflags", "proto",
diff --git a/components/variations/variations_associated_data.cc b/components/variations/variations_associated_data.cc index 0894ea9f..3d7d621 100644 --- a/components/variations/variations_associated_data.cc +++ b/components/variations/variations_associated_data.cc
@@ -17,13 +17,6 @@ namespace variations { -namespace internal { - -const base::Feature kRestrictGoogleWebVisibility{ - "RestrictGoogleWebVisibility", base::FEATURE_DISABLED_BY_DEFAULT}; - -} // namespace internal - namespace { // The internal singleton accessor for the map, used to keep it thread-safe.
diff --git a/components/variations/variations_associated_data.h b/components/variations/variations_associated_data.h index a34aeb1..214e3189 100644 --- a/components/variations/variations_associated_data.h +++ b/components/variations/variations_associated_data.h
@@ -45,14 +45,6 @@ namespace variations { -namespace internal { -// A feature that supports more finely-grained control over the transmission of -// VariationIDs to Google web properties by allowing some VariationIDs to not be -// transmitted in all contexts. See IsFirstPartyContext() in -// variations_http_headers.cc for more details. -extern const base::Feature kRestrictGoogleWebVisibility; -} // namespace internal - typedef int VariationID; const VariationID EMPTY_ID = 0;
diff --git a/components/variations/variations_features.cc b/components/variations/variations_features.cc new file mode 100644 index 0000000..3a11276 --- /dev/null +++ b/components/variations/variations_features.cc
@@ -0,0 +1,15 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/variations/variations_features.h" + +namespace variations { + +namespace internal { + +const base::Feature kRestrictGoogleWebVisibility{ + "RestrictGoogleWebVisibility", base::FEATURE_DISABLED_BY_DEFAULT}; + +} // namespace internal +} // namespace variations
diff --git a/components/variations/variations_features.h b/components/variations/variations_features.h new file mode 100644 index 0000000..8d15b83 --- /dev/null +++ b/components/variations/variations_features.h
@@ -0,0 +1,24 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_VARIATIONS_VARIATIONS_FEATURES_H_ +#define COMPONENTS_VARIATIONS_VARIATIONS_FEATURES_H_ + +#include "base/component_export.h" +#include "base/metrics/field_trial.h" + +namespace variations { +namespace internal { + +// A feature that supports more finely-grained control over the transmission of +// VariationIDs to Google web properties by allowing some VariationIDs to not be +// transmitted in all contexts. See IsFirstPartyContext() in +// variations_http_headers.cc for more details. +COMPONENT_EXPORT(VARIATIONS_FEATURES) +extern const base::Feature kRestrictGoogleWebVisibility; + +} // namespace internal +} // namespace variations + +#endif // COMPONENTS_VARIATIONS_VARIATIONS_ASSOCIATED_DATA_H_ \ No newline at end of file
diff --git a/components/variations/variations_ids_provider.cc b/components/variations/variations_ids_provider.cc index e0e7a45c..5516cd91 100644 --- a/components/variations/variations_ids_provider.cc +++ b/components/variations/variations_ids_provider.cc
@@ -14,6 +14,7 @@ #include "base/strings/string_util.h" #include "components/variations/proto/client_variations.pb.h" #include "components/variations/variations_client.h" +#include "components/variations/variations_features.h" namespace variations {
diff --git a/components/viz/common/features.cc b/components/viz/common/features.cc index d55f25b..2af0c39 100644 --- a/components/viz/common/features.cc +++ b/components/viz/common/features.cc
@@ -5,6 +5,7 @@ #include "components/viz/common/features.h" #include "base/command_line.h" +#include "base/system/sys_info.h" #include "build/chromecast_buildflags.h" #include "components/viz/common/switches.h" #include "components/viz/common/viz_utils.h" @@ -114,6 +115,10 @@ if (IsUsingVizForWebView()) return true; + // https://crbug.com/1126490 Mali-400 with <= 512 MB is currently broken. + if (base::SysInfo::AmountOfPhysicalMemoryMB() <= 512) + return false; + return base::FeatureList::IsEnabled(kUseSkiaRenderer) || base::FeatureList::IsEnabled(kVulkan); }
diff --git a/components/viz/service/display_embedder/output_presenter_fuchsia.cc b/components/viz/service/display_embedder/output_presenter_fuchsia.cc index db2051e1..d2d0c62f 100644 --- a/components/viz/service/display_embedder/output_presenter_fuchsia.cc +++ b/components/viz/service/display_embedder/output_presenter_fuchsia.cc
@@ -226,7 +226,7 @@ // Create buffer collection with 2 extra tokens: one for Vulkan and one for // the ImagePipe. - fuchsia::sysmem::BufferCollectionTokenPtr collection_token; + fuchsia::sysmem::BufferCollectionTokenSyncPtr collection_token; sysmem_allocator_->AllocateSharedCollection(collection_token.NewRequest()); fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken> @@ -234,16 +234,7 @@ collection_token->Duplicate(ZX_RIGHT_SAME_RIGHTS, token_for_scenic.NewRequest()); - fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken> - token_for_vulkan; - collection_token->Duplicate(ZX_RIGHT_SAME_RIGHTS, - token_for_vulkan.NewRequest()); - - fuchsia::sysmem::BufferCollectionSyncPtr collection; - sysmem_allocator_->BindSharedCollection(std::move(collection_token), - collection.NewRequest()); - - zx_status_t status = collection->Sync(); + zx_status_t status = collection_token->Sync(); if (status != ZX_OK) { ZX_DLOG(ERROR, status) << "fuchsia.sysmem.BufferCollection.Sync()"; return {}; @@ -252,21 +243,6 @@ auto* vulkan = dependency_->GetVulkanContextProvider()->GetVulkanImplementation(); - // Set constraints for the new collection. - fuchsia::sysmem::BufferCollectionConstraints constraints; - constraints.min_buffer_count = num_images; - constraints.usage.none = fuchsia::sysmem::noneUsage; - constraints.image_format_constraints_count = 1; - constraints.image_format_constraints[0].pixel_format.type = - fuchsia::sysmem::PixelFormatType::R8G8B8A8; - constraints.image_format_constraints[0].min_coded_width = frame_size_.width(); - constraints.image_format_constraints[0].min_coded_height = - frame_size_.height(); - constraints.image_format_constraints[0].color_spaces_count = 1; - constraints.image_format_constraints[0].color_space[0].type = - fuchsia::sysmem::ColorSpaceType::SRGB; - collection->SetConstraints(true, constraints); - // Register the new buffer collection with the ImagePipe. last_buffer_collection_id_++; image_pipe_->AddBufferCollection(last_buffer_collection_id_, @@ -280,31 +256,14 @@ ->GetDeviceQueue() ->GetVulkanDevice(); buffer_collection_ = vulkan->RegisterSysmemBufferCollection( - vk_device, buffer_collection_id, token_for_vulkan.TakeChannel(), - buffer_format_, gfx::BufferUsage::SCANOUT); + vk_device, buffer_collection_id, collection_token.Unbind().TakeChannel(), + buffer_format_, gfx::BufferUsage::SCANOUT, frame_size_, num_images); - // Wait for the images to be allocated. - zx_status_t wait_status; - fuchsia::sysmem::BufferCollectionInfo_2 buffers_info; - status = collection->WaitForBuffersAllocated(&wait_status, &buffers_info); - if (status != ZX_OK) { - ZX_DLOG(ERROR, status) << "fuchsia.sysmem.BufferCollection failed"; + if (!buffer_collection_) { + ZX_DLOG(ERROR, status) << "Failed to allocate sysmem buffer collection"; return {}; } - if (wait_status != ZX_OK) { - ZX_DLOG(ERROR, wait_status) - << "Sysmem buffer collection allocation failed."; - return {}; - } - - DCHECK_GE(buffers_info.buffer_count, num_images); - - // We no longer need the BufferCollection connection. Close it to ensure - // ImagePipe can still use the collection after BufferCollection connection - // is dropped below. - collection->Close(); - // Create PresenterImageFuchsia for each buffer in the collection. uint32_t image_usage = gpu::SHARED_IMAGE_USAGE_RASTER | gpu::SHARED_IMAGE_USAGE_SCANOUT;
diff --git a/content/browser/accessibility/browser_accessibility_manager_android.cc b/content/browser/accessibility/browser_accessibility_manager_android.cc index 674da94..c6f7e5c0 100644 --- a/content/browser/accessibility/browser_accessibility_manager_android.cc +++ b/content/browser/accessibility/browser_accessibility_manager_android.cc
@@ -33,15 +33,11 @@ // The Java layer handles the root scroll offset. use_root_scroll_offsets_when_computing_bounds_ = false; - if (web_contents_accessibility_) - web_contents_accessibility_->set_root_manager(this); Initialize(initial_tree); } -BrowserAccessibilityManagerAndroid::~BrowserAccessibilityManagerAndroid() { - if (web_contents_accessibility_) - web_contents_accessibility_->set_root_manager(nullptr); -} +BrowserAccessibilityManagerAndroid::~BrowserAccessibilityManagerAndroid() = + default; // static ui::AXTreeUpdate BrowserAccessibilityManagerAndroid::GetEmptyDocument() {
diff --git a/content/browser/accessibility/web_contents_accessibility_android.cc b/content/browser/accessibility/web_contents_accessibility_android.cc index 120d6e64..597d9687 100644 --- a/content/browser/accessibility/web_contents_accessibility_android.cc +++ b/content/browser/accessibility/web_contents_accessibility_android.cc
@@ -315,7 +315,7 @@ public: Connector(WebContents* web_contents, WebContentsAccessibilityAndroid* accessibility); - ~Connector() override; + ~Connector() override = default; void DeleteEarly(); @@ -335,15 +335,6 @@ Initialize(); } -WebContentsAccessibilityAndroid::Connector::~Connector() { - // Remove accessibility from the BrowserAccessibilityManager or it may - // continue to reference this object which is being destroyed. - auto* manager = static_cast<BrowserAccessibilityManagerAndroid*>( - accessibility_->web_contents_->GetRootBrowserAccessibilityManager()); - if (manager) - manager->set_web_contents_accessibility(nullptr); -} - void WebContentsAccessibilityAndroid::Connector::DeleteEarly() { RenderWidgetHostConnector::DestroyEarly(); } @@ -365,7 +356,6 @@ web_contents_(static_cast<WebContentsImpl*>(web_contents)), frame_info_initialized_(false), use_zoom_for_dsf_enabled_(IsUseZoomForDSFEnabled()), - root_manager_(nullptr), connector_(new Connector(web_contents, this)) { CollectStats(); } @@ -389,22 +379,20 @@ jboolean WebContentsAccessibilityAndroid::IsEnabled( JNIEnv* env, const JavaParamRef<jobject>& obj) { - return root_manager_ != nullptr; + return GetRootBrowserAccessibilityManager() != nullptr; } void WebContentsAccessibilityAndroid::Enable(JNIEnv* env, const JavaParamRef<jobject>& obj) { BrowserAccessibilityStateImpl* accessibility_state = BrowserAccessibilityStateImpl::GetInstance(); - auto* manager = static_cast<BrowserAccessibilityManagerAndroid*>( - web_contents_->GetRootBrowserAccessibilityManager()); + auto* manager = GetRootBrowserAccessibilityManager(); // First check if we already have a BrowserAccessibilityManager that // that needs to be connected to this instance. This can happen if // BAM creation precedes render view updates for the associated // web contents. if (manager) { - set_root_manager(manager); manager->set_web_contents_accessibility(GetWeakPtr()); return; } @@ -567,10 +555,10 @@ // Hover event was consumed by accessibility by now. Return true to // stop the event from proceeding. if (event.GetAction() != ui::MotionEvent::Action::HOVER_EXIT && - root_manager_) { + GetRootBrowserAccessibilityManager()) { gfx::PointF point = event.GetPointPix(); point.Scale(1 / page_scale_); - root_manager_->HitTest(gfx::ToFlooredPoint(point)); + GetRootBrowserAccessibilityManager()->HitTest(gfx::ToFlooredPoint(point)); } return true; } @@ -605,9 +593,9 @@ jint WebContentsAccessibilityAndroid::GetRootId( JNIEnv* env, const JavaParamRef<jobject>& obj) { - if (root_manager_) { + if (auto* root_manager = GetRootBrowserAccessibilityManager()) { auto* root = - static_cast<BrowserAccessibilityAndroid*>(root_manager_->GetRoot()); + static_cast<BrowserAccessibilityAndroid*>(root_manager->GetRoot()); if (root) return static_cast<jint>(root->unique_id()); } @@ -625,8 +613,8 @@ const JavaParamRef<jobject>& obj, jint x, jint y) { - if (root_manager_) - root_manager_->HitTest(gfx::Point(x, y)); + if (auto* root_manager = GetRootBrowserAccessibilityManager()) + root_manager->HitTest(gfx::Point(x, y)); } jboolean WebContentsAccessibilityAndroid::IsEditableText( @@ -691,9 +679,12 @@ const JavaParamRef<jobject>& info, jint unique_id, BrowserAccessibilityAndroid* node) { - float dip_scale = use_zoom_for_dsf_enabled_ - ? 1 / root_manager_->device_scale_factor() - : 1.0; + auto* root_manager = GetRootBrowserAccessibilityManager(); + if (!root_manager) + return; + + float dip_scale = + use_zoom_for_dsf_enabled_ ? 1 / root_manager->device_scale_factor() : 1.0; gfx::Rect absolute_rect = gfx::ScaleToEnclosingRect( node->GetUnclippedRootFrameBoundsRect(), dip_scale, dip_scale); gfx::Rect parent_relative_rect = absolute_rect; @@ -715,6 +706,10 @@ const JavaParamRef<jobject>& obj, const JavaParamRef<jobject>& info, jint unique_id) { + auto* root_manager = GetRootBrowserAccessibilityManager(); + if (!root_manager) + return false; + BrowserAccessibilityAndroid* node = GetAXFromUniqueID(unique_id); if (!node) return false; @@ -730,6 +725,9 @@ const JavaParamRef<jobject>& obj, const JavaParamRef<jobject>& info, jint unique_id) { + if (!GetRootBrowserAccessibilityManager()) + return false; + BrowserAccessibilityAndroid* node = GetAXFromUniqueID(unique_id); if (!node) return false; @@ -924,8 +922,8 @@ void WebContentsAccessibilityAndroid::Blur(JNIEnv* env, const JavaParamRef<jobject>& obj) { - if (root_manager_) - root_manager_->SetFocus(*root_manager_->GetRoot()); + if (auto* root_manager = GetRootBrowserAccessibilityManager()) + root_manager->SetFocus(*root_manager->GetRoot()); } void WebContentsAccessibilityAndroid::ScrollToMakeNodeVisible( @@ -1027,10 +1025,11 @@ if (!start_node) return 0; - if (!root_manager_) + auto* root_manager = GetRootBrowserAccessibilityManager(); + if (!root_manager) return 0; - BrowserAccessibility* root = root_manager_->GetRoot(); + BrowserAccessibility* root = root_manager->GetRoot(); if (!root) return 0; @@ -1081,16 +1080,18 @@ jboolean extend_selection, jint unique_id, jint cursor_index) { - if (!root_manager_) + auto* root_manager = GetRootBrowserAccessibilityManager(); + if (!root_manager) return false; + BrowserAccessibilityAndroid* node = GetAXFromUniqueID(unique_id); if (!node) return false; jint start_index = -1; int end_index = -1; - if (root_manager_->NextAtGranularity(granularity, cursor_index, node, - &start_index, &end_index)) { + if (root_manager->NextAtGranularity(granularity, cursor_index, node, + &start_index, &end_index)) { base::string16 text = node->GetInnerText(); Java_WebContentsAccessibilityImpl_finishGranularityMoveNext( env, obj, base::android::ConvertUTF16ToJavaString(env, text), @@ -1148,16 +1149,18 @@ jboolean extend_selection, jint unique_id, jint cursor_index) { - if (!root_manager_) + auto* root_manager = GetRootBrowserAccessibilityManager(); + if (!root_manager) return false; + BrowserAccessibilityAndroid* node = GetAXFromUniqueID(unique_id); if (!node) return false; jint start_index = -1; int end_index = -1; - if (root_manager_->PreviousAtGranularity(granularity, cursor_index, node, - &start_index, &end_index)) { + if (root_manager->PreviousAtGranularity(granularity, cursor_index, node, + &start_index, &end_index)) { Java_WebContentsAccessibilityImpl_finishGranularityMovePrevious( env, obj, base::android::ConvertUTF16ToJavaString(env, node->GetInnerText()), @@ -1202,11 +1205,12 @@ void WebContentsAccessibilityAndroid::OnAutofillPopupDisplayed( JNIEnv* env, const JavaParamRef<jobject>& obj) { - if (!root_manager_ || + auto* root_manager = GetRootBrowserAccessibilityManager(); + if (!root_manager || !base::FeatureList::IsEnabled(features::kAndroidAutofillAccessibility)) return; - BrowserAccessibility* current_focus = root_manager_->GetFocus(); + BrowserAccessibility* current_focus = root_manager->GetFocus(); if (current_focus == nullptr) { return; } @@ -1222,7 +1226,7 @@ ax_node_data.AddState(ax::mojom::State::kFocusable); ax_node_data.AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, false); g_autofill_popup_proxy_node_ax_node->SetData(ax_node_data); - g_autofill_popup_proxy_node->Init(root_manager_, + g_autofill_popup_proxy_node->Init(root_manager, g_autofill_popup_proxy_node_ax_node); auto* android_node = static_cast<BrowserAccessibilityAndroid*>(current_focus); @@ -1327,6 +1331,10 @@ jint unique_id, jint start, jint len) { + auto* root_manager = GetRootBrowserAccessibilityManager(); + if (!root_manager) + return nullptr; + BrowserAccessibilityAndroid* node = GetAXFromUniqueID(unique_id); if (!node) return nullptr; @@ -1338,9 +1346,8 @@ return nullptr; } - float dip_scale = use_zoom_for_dsf_enabled_ - ? 1 / root_manager_->device_scale_factor() - : 1.0; + float dip_scale = + use_zoom_for_dsf_enabled_ ? 1 / root_manager->device_scale_factor() : 1.0; gfx::Rect object_bounds = node->GetUnclippedRootFrameBoundsRect(); int coords[4 * len]; @@ -1361,10 +1368,15 @@ static_cast<size_t>(4 * len)); } +BrowserAccessibilityManagerAndroid* +WebContentsAccessibilityAndroid::GetRootBrowserAccessibilityManager() { + return static_cast<BrowserAccessibilityManagerAndroid*>( + web_contents_->GetRootBrowserAccessibilityManager()); +} + BrowserAccessibilityAndroid* WebContentsAccessibilityAndroid::GetAXFromUniqueID( int32_t unique_id) { - return static_cast<BrowserAccessibilityAndroid*>( - BrowserAccessibilityAndroid::GetFromUniqueId(unique_id)); + return BrowserAccessibilityAndroid::GetFromUniqueId(unique_id); } void WebContentsAccessibilityAndroid::UpdateFrameInfo(float page_scale) {
diff --git a/content/browser/accessibility/web_contents_accessibility_android.h b/content/browser/accessibility/web_contents_accessibility_android.h index d421678e..10f36ee 100644 --- a/content/browser/accessibility/web_contents_accessibility_android.h +++ b/content/browser/accessibility/web_contents_accessibility_android.h
@@ -24,7 +24,7 @@ // Bridges BrowserAccessibilityManagerAndroid and Java WebContentsAccessibility. // A RenderWidgetHostConnector runs behind to manage the connection. Referenced -// by BrowserAccessibilityManagerAndroid for main frame (root manager) only. +// by BrowserAccessibilityManagerAndroid for main frame only. // The others for subframes should acquire this instance through the root // manager to access Java layer. // @@ -244,10 +244,6 @@ void UpdateFrameInfo(float page_scale); - void set_root_manager(BrowserAccessibilityManagerAndroid* manager) { - root_manager_ = manager; - } - // -------------------------------------------------------------------------- // Methods called from the BrowserAccessibilityManager // -------------------------------------------------------------------------- @@ -274,6 +270,8 @@ base::WeakPtr<WebContentsAccessibilityAndroid> GetWeakPtr(); private: + BrowserAccessibilityManagerAndroid* GetRootBrowserAccessibilityManager(); + BrowserAccessibilityAndroid* GetAXFromUniqueID(int32_t unique_id); void CollectStats(); @@ -295,8 +293,6 @@ bool use_zoom_for_dsf_enabled_; - BrowserAccessibilityManagerAndroid* root_manager_; - // Manages the connection between web contents and the RenderFrameHost that // receives accessibility events. // Owns itself, and destroyed upon WebContentsObserver::WebContentsDestroyed.
diff --git a/content/browser/bluetooth/bluetooth_allowed_devices.cc b/content/browser/bluetooth/bluetooth_allowed_devices.cc index ae07c9d..be47f41 100644 --- a/content/browser/bluetooth/bluetooth_allowed_devices.cc +++ b/content/browser/bluetooth/bluetooth_allowed_devices.cc
@@ -27,6 +27,7 @@ const blink::mojom::WebBluetoothRequestDeviceOptionsPtr& options) { auto& device_id = AddDevice(device_address); AddUnionOfServicesTo(options, &device_id_to_services_map_[device_id]); + AddManufacturerDataTo(options, &device_id_to_manufacturers_map_[device_id]); // Currently, devices that are added with WebBluetoothRequestDeviceOptionsPtr // |options| come from RequestDevice() and therefore have the ablity to be @@ -89,7 +90,6 @@ const std::string& BluetoothAllowedDevices::GetDeviceAddress( const blink::WebBluetoothDeviceId& device_id) { auto id_iter = device_id_to_address_map_.find(device_id); - return id_iter == device_id_to_address_map_.end() ? base::EmptyString() : id_iter->second; } @@ -97,7 +97,6 @@ bool BluetoothAllowedDevices::IsAllowedToAccessAtLeastOneService( const blink::WebBluetoothDeviceId& device_id) const { auto id_iter = device_id_to_services_map_.find(device_id); - return id_iter == device_id_to_services_map_.end() ? false : !id_iter->second.empty(); } @@ -105,12 +104,10 @@ bool BluetoothAllowedDevices::IsAllowedToAccessService( const blink::WebBluetoothDeviceId& device_id, const BluetoothUUID& service_uuid) const { - if (BluetoothBlocklist::Get().IsExcluded(service_uuid)) { + if (BluetoothBlocklist::Get().IsExcluded(service_uuid)) return false; - } auto id_iter = device_id_to_services_map_.find(device_id); - return id_iter == device_id_to_services_map_.end() ? false : base::Contains(id_iter->second, service_uuid); @@ -124,6 +121,15 @@ return id_iter->second; } +bool BluetoothAllowedDevices::IsAllowedToAccessManufacturerData( + const blink::WebBluetoothDeviceId& device_id, + const uint16_t manufacturer_code) const { + auto id_iter = device_id_to_manufacturers_map_.find(device_id); + return id_iter == device_id_to_manufacturers_map_.end() + ? false + : base::Contains(id_iter->second, manufacturer_code); +} + blink::WebBluetoothDeviceId BluetoothAllowedDevices::GenerateUniqueDeviceId() { blink::WebBluetoothDeviceId device_id = blink::WebBluetoothDeviceId::Create(); while (base::Contains(device_id_set_, device_id)) { @@ -139,19 +145,23 @@ unionOfServices) { if (options->filters) { for (const auto& filter : options->filters.value()) { - if (!filter->services) { + if (!filter->services) continue; - } - for (const BluetoothUUID& uuid : filter->services.value()) { + for (const BluetoothUUID& uuid : filter->services.value()) unionOfServices->insert(uuid); - } } } - for (const BluetoothUUID& uuid : options->optional_services) { + for (const BluetoothUUID& uuid : options->optional_services) unionOfServices->insert(uuid); - } +} + +void BluetoothAllowedDevices::AddManufacturerDataTo( + const blink::mojom::WebBluetoothRequestDeviceOptionsPtr& options, + base::flat_set<uint16_t>* manufacturer_codes) { + for (const uint16_t manufacturer_code : options->optional_manufacturer_data) + manufacturer_codes->insert(manufacturer_code); } } // namespace content
diff --git a/content/browser/bluetooth/bluetooth_allowed_devices.h b/content/browser/bluetooth/bluetooth_allowed_devices.h index ba53d188..9eb01622 100644 --- a/content/browser/bluetooth/bluetooth_allowed_devices.h +++ b/content/browser/bluetooth/bluetooth_allowed_devices.h
@@ -11,6 +11,7 @@ #include <unordered_set> #include <vector> +#include "base/containers/flat_set.h" #include "base/optional.h" #include "content/common/content_export.h" #include "third_party/blink/public/common/bluetooth/web_bluetooth_device_id.h" @@ -69,6 +70,12 @@ const blink::WebBluetoothDeviceId& device_id, const device::BluetoothUUID& service_uuid) const; + // Returns true if access has been previously granted for manufacturer data + // corresponding to |manufacturer_code|. + bool IsAllowedToAccessManufacturerData( + const blink::WebBluetoothDeviceId& device_id, + const uint16_t manufacturer_code) const; + bool IsAllowedToGATTConnect( const blink::WebBluetoothDeviceId& device_id) const; @@ -88,6 +95,10 @@ bool, blink::WebBluetoothDeviceIdHash> DeviceIdToConnectableMap; + using DeviceIdToManufacturerDataMap = + std::unordered_map<blink::WebBluetoothDeviceId, + base::flat_set<uint16_t>, + blink::WebBluetoothDeviceIdHash>; // Returns an id guaranteed to be unique for the map. The id is randomly // generated so that an origin can't guess the id used in another origin. @@ -96,10 +107,14 @@ const blink::mojom::WebBluetoothRequestDeviceOptionsPtr& options, std::unordered_set<device::BluetoothUUID, device::BluetoothUUIDHash>* unionOfServices); + void AddManufacturerDataTo( + const blink::mojom::WebBluetoothRequestDeviceOptionsPtr& options, + base::flat_set<uint16_t>* manufacturer_codes); DeviceAddressToIdMap device_address_to_id_map_; DeviceIdToAddressMap device_id_to_address_map_; DeviceIdToServicesMap device_id_to_services_map_; + DeviceIdToManufacturerDataMap device_id_to_manufacturers_map_; DeviceIdToConnectableMap device_id_to_connectable_map_; // Keep track of all device_ids in the map.
diff --git a/content/browser/bluetooth/web_bluetooth_service_impl.cc b/content/browser/bluetooth/web_bluetooth_service_impl.cc index 159d1d70..baef546 100644 --- a/content/browser/bluetooth/web_bluetooth_service_impl.cc +++ b/content/browser/bluetooth/web_bluetooth_service_impl.cc
@@ -174,7 +174,8 @@ class WebBluetoothServiceImpl::AdvertisementClient { public: - virtual void SendEvent(blink::mojom::WebBluetoothAdvertisingEvent& event) = 0; + virtual void SendEvent( + const blink::mojom::WebBluetoothAdvertisingEvent& event) = 0; bool is_connected() { return client_.is_connected(); } @@ -185,7 +186,8 @@ blink::mojom::WebBluetoothAdvertisementClient> client_info) : client_(std::move(client_info)), web_contents_(static_cast<WebContentsImpl*>( - WebContents::FromRenderFrameHost(service->render_frame_host_))) { + WebContents::FromRenderFrameHost(service->render_frame_host_))), + service_(service) { // Using base::Unretained() is safe here because all instances of this class // will be owned by |service|. client_.set_disconnect_handler( @@ -197,6 +199,7 @@ mojo::AssociatedRemote<blink::mojom::WebBluetoothAdvertisementClient> client_; WebContentsImpl* web_contents_; + WebBluetoothServiceImpl* service_; }; class WebBluetoothServiceImpl::WatchAdvertisementsClient @@ -217,9 +220,29 @@ } // AdvertisementClient implementation: - void SendEvent(blink::mojom::WebBluetoothAdvertisingEvent& event) override { - if (event.device->id == device_id_) - client_->AdvertisingEvent(event.Clone()); + void SendEvent( + const blink::mojom::WebBluetoothAdvertisingEvent& event) override { + if (event.device->id != device_id_) + return; + + auto filtered_event = event.Clone(); + base::EraseIf( + filtered_event->uuids, [this](const device::BluetoothUUID& uuid) { + return !service_->IsAllowedToAccessService(device_id_, uuid); + }); + base::EraseIf( + filtered_event->service_data, + [this](const std::pair<device::BluetoothUUID, std::vector<uint8_t>>& + entry) { + return !service_->IsAllowedToAccessService(device_id_, entry.first); + }); + base::EraseIf( + filtered_event->manufacturer_data, + [this](const std::pair<uint16_t, std::vector<uint8_t>>& entry) { + return !service_->IsAllowedToAccessManufacturerData(device_id_, + entry.first); + }); + client_->AdvertisingEvent(std::move(filtered_event)); } blink::WebBluetoothDeviceId device_id() const { return device_id_; } @@ -253,13 +276,18 @@ } // AdvertisingClient implementation: - void SendEvent(blink::mojom::WebBluetoothAdvertisingEvent& event) override { + void SendEvent( + const blink::mojom::WebBluetoothAdvertisingEvent& event) override { + // TODO(https://crbug.com/1108958): Filter out advertisement data if not + // included in the filters, optionalServices, or optionalManufacturerData. + auto filtered_event = event.Clone(); if (options_->accept_all_advertisements) { if (prompt_controller_) - AddFilteredDeviceToPrompt(event.device->id.str(), event.name); + AddFilteredDeviceToPrompt(filtered_event->device->id.str(), + filtered_event->name); if (allow_send_event_) - client_->AdvertisingEvent(event.Clone()); + client_->AdvertisingEvent(std::move(filtered_event)); return; } @@ -276,16 +304,17 @@ for (auto& filter : options_->filters.value()) { // Check to see if there is a direct match against the advertisement name if (filter->name.has_value()) { - if (!event.name.has_value() || - filter->name.value() != event.name.value()) { + if (!filtered_event->name.has_value() || + filter->name.value() != filtered_event->name.value()) { continue; } } // Check if there is a name prefix match if (filter->name_prefix.has_value()) { - if (!event.name.has_value() || - !base::StartsWith(event.name.value(), filter->name_prefix.value(), + if (!filtered_event->name.has_value() || + !base::StartsWith(filtered_event->name.value(), + filter->name_prefix.value(), base::CompareCase::SENSITIVE)) { continue; } @@ -295,8 +324,8 @@ if (filter->services.has_value()) { auto it = std::find_if( filter->services.value().begin(), filter->services.value().end(), - [&event](const BluetoothUUID& filter_uuid) { - return base::Contains(event.uuids, filter_uuid); + [&filtered_event](const BluetoothUUID& filter_uuid) { + return base::Contains(filtered_event->uuids, filter_uuid); }); if (it == filter->services.value().end()) continue; @@ -306,10 +335,11 @@ // filters. if (prompt_controller_) - AddFilteredDeviceToPrompt(event.device->id.str(), event.name); + AddFilteredDeviceToPrompt(filtered_event->device->id.str(), + filtered_event->name); if (allow_send_event_) - client_->AdvertisingEvent(event.Clone()); + client_->AdvertisingEvent(std::move(filtered_event)); return; } } @@ -651,11 +681,8 @@ manufacturer_data.insert(manufacturer_data_map.begin(), manufacturer_data_map.end()); - std::vector<std::pair<std::string, std::vector<uint8_t>>> services; - for (auto& it : service_data_map) - services.emplace_back(it.first.canonical_value(), it.second); - result->service_data = base::flat_map<std::string, std::vector<uint8_t>>( - services.begin(), services.end()); + auto& service_data = result->service_data; + service_data.insert(service_data_map.begin(), service_data_map.end()); // TODO(https://crbug.com/1087007): These two classes can potentially be // combined into the same container. @@ -2206,9 +2233,8 @@ return false; return delegate->IsAllowedToAccessAtLeastOneService(render_frame_host_, device_id); - } else { - return allowed_devices().IsAllowedToAccessAtLeastOneService(device_id); } + return allowed_devices().IsAllowedToAccessAtLeastOneService(device_id); } bool WebBluetoothServiceImpl::IsAllowedToAccessService( @@ -2222,9 +2248,24 @@ return false; return delegate->IsAllowedToAccessService(render_frame_host_, device_id, service); - } else { - return allowed_devices().IsAllowedToAccessService(device_id, service); } + return allowed_devices().IsAllowedToAccessService(device_id, service); +} + +bool WebBluetoothServiceImpl::IsAllowedToAccessManufacturerData( + const blink::WebBluetoothDeviceId& device_id, + uint16_t manufacturer_code) { + if (base::FeatureList::IsEnabled( + features::kWebBluetoothNewPermissionsBackend)) { + BluetoothDelegate* delegate = + GetContentClient()->browser()->GetBluetoothDelegate(); + if (!delegate) + return false; + return delegate->IsAllowedToAccessManufacturerData( + render_frame_host_, device_id, manufacturer_code); + } + return allowed_devices().IsAllowedToAccessManufacturerData(device_id, + manufacturer_code); } bool WebBluetoothServiceImpl::HasActiveDiscoverySession() {
diff --git a/content/browser/bluetooth/web_bluetooth_service_impl.h b/content/browser/bluetooth/web_bluetooth_service_impl.h index 219bf42a..96282911 100644 --- a/content/browser/bluetooth/web_bluetooth_service_impl.h +++ b/content/browser/bluetooth/web_bluetooth_service_impl.h
@@ -382,6 +382,9 @@ const blink::WebBluetoothDeviceId& device_id); bool IsAllowedToAccessService(const blink::WebBluetoothDeviceId& device_id, const device::BluetoothUUID& service); + bool IsAllowedToAccessManufacturerData( + const blink::WebBluetoothDeviceId& device_id, + uint16_t manufacturer_code); // Returns true if at least |ble_scan_discovery_session_| or // |watch_advertisements_discovery_session_| is active.
diff --git a/content/browser/browsing_data/browsing_data_remover_impl.cc b/content/browser/browsing_data/browsing_data_remover_impl.cc index 531a886b..fd752a4 100644 --- a/content/browser/browsing_data/browsing_data_remover_impl.cc +++ b/content/browser/browsing_data/browsing_data_remover_impl.cc
@@ -537,8 +537,10 @@ !(remove_mask & DATA_TYPE_AVOID_CLOSING_CONNECTIONS)) { BrowserContext::GetDefaultStoragePartition(browser_context_) ->GetNetworkContext() - ->ClearHttpAuthCache(delete_begin, CreateTaskCompletionClosureForMojo( - TracingDataType::kAuthCache)); + ->ClearHttpAuthCache( + delete_begin_.is_null() ? base::Time::Min() : delete_begin_, + delete_end_.is_null() ? base::Time::Max() : delete_end_, + CreateTaskCompletionClosureForMojo(TracingDataType::kAuthCache)); } //////////////////////////////////////////////////////////////////////////////
diff --git a/content/browser/child_process_security_policy_impl.cc b/content/browser/child_process_security_policy_impl.cc index 73123b35..c9ca4b2b 100644 --- a/content/browser/child_process_security_policy_impl.cc +++ b/content/browser/child_process_security_policy_impl.cc
@@ -2109,7 +2109,7 @@ void ChildProcessSecurityPolicyImpl::AddNonIsolatedOriginIfNeeded( const IsolationContext& isolation_context, const url::Origin& origin, - bool is_global_walk) { + bool is_global_walk_or_frame_removal) { DCHECK_CURRENTLY_ON(BrowserThread::UI); // Origin Policy only exists for HTTPS, and header-based opt-in requests are // also HTTPS-only, so nothing we isolate will be HTTP. @@ -2122,23 +2122,27 @@ base::AutoLock origins_isolation_opt_in_lock(origins_isolation_opt_in_lock_); - if (!is_global_walk) { - // Commits of origins that have ever requested isolation are tracked in - // every BrowsingInstance, to avoid having to do multiple global walks. If - // the origin isn't in the list of such origins (i.e., the common case), - // return early to avoid unnecessary work, since this is called on every - // commit. - if (!base::Contains(origin_isolation_opt_ins_, origin)) - return; + // Commits of origins that have ever requested isolation are tracked in + // every BrowsingInstance, to avoid having to do multiple global walks. If + // the origin isn't in the list of such origins (i.e., the common case), + // return early to avoid unnecessary work, since this is called on every + // commit. Skip this during global walks and frame removals, since we do want + // to track the non-isolated origin in those cases. + if (!is_global_walk_or_frame_removal && + !base::Contains(origin_isolation_opt_ins_, origin)) { + return; + } - // If |origin| is already in the opt-in list, then we don't want to add it - // to the opt-out list. - auto it_opt_in = - origin_isolation_by_browsing_instance_.find(browsing_instance_id); - if (it_opt_in != origin_isolation_by_browsing_instance_.end() && - base::Contains(it_opt_in->second, origin)) { - return; - } + // If |origin| is already in the opt-in list, then we don't want to add it + // to the opt-out list. Technically this check is unnecessary during global + // walks (when the origin won't be in this list yet), but it matters during + // frame removal (when we don't want to add an opted-in origin to the + // non-isolated list when its frame is removed). + auto it_opt_in = + origin_isolation_by_browsing_instance_.find(browsing_instance_id); + if (it_opt_in != origin_isolation_by_browsing_instance_.end() && + base::Contains(it_opt_in->second, origin)) { + return; } auto it = origin_isolation_non_isolated_by_browsing_instance_.find(
diff --git a/content/browser/child_process_security_policy_impl.h b/content/browser/child_process_security_policy_impl.h index e486142..57d9198e 100644 --- a/content/browser/child_process_security_policy_impl.h +++ b/content/browser/child_process_security_policy_impl.h
@@ -599,13 +599,16 @@ bool HasOriginEverRequestedOptInIsolation(const url::Origin& origin); // Adds |origin| to the non-isolated list for the BrowsingInstance specified - // by |isolation_context|, if it's not already in the list. |is_global_walk| - // should only be set to true during the global walk that is triggered when - // |origin| first requests opt-in isolation, so that the function can skip - // safety checks that will be unnecessary during the global walk. + // by |isolation_context|, if we need to track it and it's not already in the + // list. |is_global_walk_or_frame_removal| should be set to true during the + // global walk that is triggered when |origin| first requests opt-in + // isolation, so that the function can skip safety checks that will be + // unnecessary during the global walk. It is also set to true if this function + // is called when removing a FrameNavigationEntry, since that entry won't be + // available to any subsequent global walks. void AddNonIsolatedOriginIfNeeded(const IsolationContext& isolation_context, const url::Origin& origin, - bool is_global_walk); + bool is_global_walk_or_frame_removal); private: friend class ChildProcessSecurityPolicyInProcessBrowserTest;
diff --git a/content/browser/network_service_restart_browsertest.cc b/content/browser/network_service_restart_browsertest.cc index 02b2749..c1ee84f 100644 --- a/content/browser/network_service_restart_browsertest.cc +++ b/content/browser/network_service_restart_browsertest.cc
@@ -1227,8 +1227,18 @@ // triggers sending a Commit IPC to the renderer process, but before a DidCommit // IPC from the renderer process is handled. See also // https://crbug.com/1056949#c75. +// +// TODO(lukasza): https://crbug.com/1129592: Flaky on Android. No flakiness +// observed whatsoever on Windows, Linux or CrOS. +#if defined(OS_ANDROID) +#define MAYBE_BetweenCommitNavigationAndDidCommit \ + DISABLED_BetweenCommitNavigationAndDidCommit +#else +#define MAYBE_BetweenCommitNavigationAndDidCommit \ + BetweenCommitNavigationAndDidCommit +#endif IN_PROC_BROWSER_TEST_F(NetworkServiceRestartBrowserTest, - BetweenCommitNavigationAndDidCommit) { + MAYBE_BetweenCommitNavigationAndDidCommit) { if (IsInProcessNetworkService()) return;
diff --git a/content/browser/renderer_host/navigation_entry_impl.cc b/content/browser/renderer_host/navigation_entry_impl.cc index e623110..08316cf1 100644 --- a/content/browser/renderer_host/navigation_entry_impl.cc +++ b/content/browser/renderer_host/navigation_entry_impl.cc
@@ -19,6 +19,7 @@ #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" #include "components/url_formatter/url_formatter.h" +#include "content/browser/child_process_security_policy_impl.h" #include "content/browser/renderer_host/navigation_controller_impl.h" #include "content/browser/web_package/web_bundle_navigation_info.h" #include "content/common/content_constants_internal.h" @@ -967,6 +968,20 @@ // FrameNavigationEntries and the FrameTree. if (!only_if_different_position || !InSameTreePosition(frame_tree_node, node)) { + auto* frame_entry = node->frame_entry.get(); + if (frame_entry && frame_entry->committed_origin()) { + // Normally non-isolated origins are tracked through their presence in + // session history, which is consulted whenever an origin newly requests + // isolation. If we remove a frame_entry, its origin won't be available + // to any future global walk if the same origin later wants to opt-in. So + // we add it to the non-opt-in list here to be spec compliant (unless it's + // currently opted-in, in which case this call will do nothing). + ChildProcessSecurityPolicyImpl::GetInstance() + ->AddNonIsolatedOriginIfNeeded( + frame_entry->site_instance()->GetIsolationContext(), + frame_entry->committed_origin().value(), + true /* global_ walk_or_frame_removal */); + } NavigationEntryImpl::TreeNode* parent_node = node->parent; auto it = std::find_if( parent_node->children.begin(), parent_node->children.end(),
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc index f1a81fc..166b389 100644 --- a/content/browser/renderer_host/navigation_request.cc +++ b/content/browser/renderer_host/navigation_request.cc
@@ -2389,7 +2389,7 @@ // possible to compute the right origin here. ChildProcessSecurityPolicyImpl::GetInstance()->AddNonIsolatedOriginIfNeeded( isolation_context, url::Origin::Create(common_params().url), - false /* is_global_walk */); + false /* is_global_walk_or_frame_removal */); // Replace the SiteInstance of the previously committed entry if it's for a // url that doesn't require a site assignment, since this new commit is
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc index d4d3c775..934ab26 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.cc +++ b/content/browser/renderer_host/render_widget_host_view_aura.cc
@@ -1857,13 +1857,13 @@ void RenderWidgetHostViewAura::OnWindowFocused(aura::Window* gained_focus, aura::Window* lost_focus) { - // We need to honor input bypass if the associated tab is does not want - // input. This gives the current focused window a chance to be the text - // input client and handle events. - if (host()->IsIgnoringInputEvents()) - return; - if (window_ == gained_focus) { + // We need to honor input bypass if the associated tab does not want input. + // This gives the current focused window a chance to be the text input + // client and handle events. + if (host()->IsIgnoringInputEvents()) + return; + host()->GotFocus(); host()->SetActive(true); @@ -1886,10 +1886,15 @@ return; } - host()->SetActive(false); - host()->LostFocus(); + // Only lose focus if the associated tab doesn't want input. This ensures + // focus losses from clicking on a page while it has a modal dialog won't + // break it's text cursor after the dialog goes away. + if (!host()->IsIgnoringInputEvents()) { + host()->SetActive(false); + host()->LostFocus(); - DetachFromInputMethod(false); + DetachFromInputMethod(false); + } // TODO(wjmaclean): Do we need to let TouchSelectionControllerClientAura // handle this, just in case it stomps on a new highlight in another view
diff --git a/content/browser/renderer_host/render_widget_host_view_aura.h b/content/browser/renderer_host/render_widget_host_view_aura.h index b9f5bb3..9b80d70 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.h +++ b/content/browser/renderer_host/render_widget_host_view_aura.h
@@ -433,6 +433,8 @@ DiscardDelegatedFramesWithMemoryPressure); FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraInputMethodTest, OnCaretBoundsChanged); + FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraInputMethodFocusTest, + OnFocusLost); FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraKeyboardTest, KeyboardObserverDestroyed); FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewAuraKeyboardTest,
diff --git a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc index 3b0bbf3a..adbc2b36 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc +++ b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
@@ -6497,9 +6497,7 @@ TEST_F(RenderWidgetHostViewAuraInputMethodTest, OnCaretBoundsChanged) { ui::InputMethod* input_method = parent_view_->GetInputMethod(); if (input_method != input_method_) { - // Some platform doesn't support mocking input method. e.g. InputMethodMus. - // In that case, ignore this test. - // TODO(shuchen): support mocking InputMethodMus, http://crbug.com/905518. + // Some platforms don't support mocking input method. In that case, ignore this test. return; } ActivateViewForTextInputManager(parent_view_, ui::TEXT_INPUT_TYPE_TEXT); @@ -6519,6 +6517,41 @@ input_method->RemoveObserver(this); } +class RenderWidgetHostViewAuraInputMethodFocusTest + : public RenderWidgetHostViewAuraInputMethodTest, + public testing::WithParamInterface<bool> { + public: + RenderWidgetHostViewAuraInputMethodFocusTest() = default; + ~RenderWidgetHostViewAuraInputMethodFocusTest() override = default; + + bool ignore_input_events() { return GetParam(); } +}; + +INSTANTIATE_TEST_SUITE_P(RenderWidgetHostViewAuraInputMethodFocusTest, + RenderWidgetHostViewAuraInputMethodFocusTest, + testing::Bool()); + +TEST_P(RenderWidgetHostViewAuraInputMethodFocusTest, OnFocusLost) { + render_widget_host_delegate()->set_should_ignore_input_events( + ignore_input_events()); + + ui::InputMethod* input_method = view_->GetInputMethod(); + if (input_method != input_method_) { + // Some platforms doesn't support mocking input method. In that case, ignore this test. + return; + } + EXPECT_EQ(input_method, input_method_); + ActivateViewForTextInputManager(view_, ui::TEXT_INPUT_TYPE_TEXT); + input_method->SetFocusedTextInputClient(view_); + + EXPECT_EQ(input_method->GetTextInputClient(), view_); + view_->OnWindowFocused(nullptr, view_->GetNativeView()); + if (ignore_input_events()) + EXPECT_EQ(input_method->GetTextInputClient(), view_); + else + EXPECT_EQ(input_method->GetTextInputClient(), nullptr); +} + #if defined(OS_WIN) class MockInputMethodKeyboardController final : public ui::InputMethodKeyboardController {
diff --git a/content/browser/service_worker/embedded_worker_test_helper.cc b/content/browser/service_worker/embedded_worker_test_helper.cc index ea9fa58..f55f3082 100644 --- a/content/browser/service_worker/embedded_worker_test_helper.cc +++ b/content/browser/service_worker/embedded_worker_test_helper.cc
@@ -17,7 +17,6 @@ #include "content/public/test/test_browser_context.h" #include "content/test/fake_network_url_loader_factory.h" #include "mojo/public/cpp/bindings/pending_receiver.h" -#include "third_party/blink/public/common/service_worker/service_worker_utils.h" #include "third_party/blink/public/common/user_agent/user_agent_metadata.h" namespace content { @@ -48,11 +47,8 @@ user_data_directory, std::move(database_task_runner), /*quota_manager_proxy=*/nullptr, special_storage_policy, nullptr, url_loader_factory_getter_.get(), - blink::ServiceWorkerUtils::IsImportedScriptUpdateCheckEnabled() - ? wrapper_ - ->CreateNonNetworkPendingURLLoaderFactoryBundleForUpdateCheck( - browser_context_.get()) - : nullptr); + wrapper_->CreateNonNetworkPendingURLLoaderFactoryBundleForUpdateCheck( + browser_context_.get())); wrapper_->process_manager()->SetProcessIdForTest(mock_render_process_id()); wrapper_->process_manager()->SetNewProcessIdForTest(new_render_process_id()); if (!ServiceWorkerContext::IsServiceWorkerOnUIEnabled())
diff --git a/content/browser/service_worker/service_worker_context_wrapper.cc b/content/browser/service_worker/service_worker_context_wrapper.cc index c07b4f69..a7bfb72 100644 --- a/content/browser/service_worker/service_worker_context_wrapper.cc +++ b/content/browser/service_worker/service_worker_context_wrapper.cc
@@ -49,7 +49,6 @@ #include "storage/browser/quota/quota_manager_proxy.h" #include "storage/browser/quota/special_storage_policy.h" #include "third_party/blink/public/common/service_worker/service_worker_status_code.h" -#include "third_party/blink/public/common/service_worker/service_worker_utils.h" #include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h" namespace content { @@ -247,11 +246,9 @@ {base::MayBlock(), base::TaskShutdownBehavior::BLOCK_SHUTDOWN}); std::unique_ptr<blink::PendingURLLoaderFactoryBundle> non_network_pending_loader_factory_bundle_for_update_check; - if (blink::ServiceWorkerUtils::IsImportedScriptUpdateCheckEnabled()) { - non_network_pending_loader_factory_bundle_for_update_check = - CreateNonNetworkPendingURLLoaderFactoryBundleForUpdateCheck( - storage_partition_->browser_context()); - } + non_network_pending_loader_factory_bundle_for_update_check = + CreateNonNetworkPendingURLLoaderFactoryBundleForUpdateCheck( + storage_partition_->browser_context()); RunOrPostTaskOnCoreThread( FROM_HERE, @@ -1886,7 +1883,6 @@ ServiceWorkerContextWrapper:: CreateNonNetworkPendingURLLoaderFactoryBundleForUpdateCheck( BrowserContext* browser_context) { - DCHECK(blink::ServiceWorkerUtils::IsImportedScriptUpdateCheckEnabled()); ContentBrowserClient::NonNetworkURLLoaderFactoryDeprecatedMap non_network_factories; GetContentClient()
diff --git a/content/browser/service_worker/service_worker_updated_script_loader.cc b/content/browser/service_worker/service_worker_updated_script_loader.cc index bcb55f13..fc9e7d6 100644 --- a/content/browser/service_worker/service_worker_updated_script_loader.cc +++ b/content/browser/service_worker/service_worker_updated_script_loader.cc
@@ -31,7 +31,6 @@ #include "net/cert/cert_status_flags.h" #include "services/network/public/mojom/url_response_head.mojom.h" #include "third_party/blink/public/common/loader/throttling_url_loader.h" -#include "third_party/blink/public/common/service_worker/service_worker_utils.h" namespace content { @@ -172,7 +171,6 @@ const network::ResourceRequest& original_request, mojo::PendingRemote<network::mojom::URLLoaderClient> client, scoped_refptr<ServiceWorkerVersion> version) { - DCHECK(blink::ServiceWorkerUtils::IsImportedScriptUpdateCheckEnabled()); return base::WrapUnique(new ServiceWorkerUpdatedScriptLoader( options, original_request, std::move(client), version)); }
diff --git a/content/browser/site_instance_impl.cc b/content/browser/site_instance_impl.cc index 7985dab..1c4c4194 100644 --- a/content/browser/site_instance_impl.cc +++ b/content/browser/site_instance_impl.cc
@@ -968,9 +968,9 @@ void SiteInstanceImpl::PreventOptInOriginIsolation( const url::Origin& previously_visited_origin) { auto* policy = ChildProcessSecurityPolicyImpl::GetInstance(); - policy->AddNonIsolatedOriginIfNeeded(GetIsolationContext(), - previously_visited_origin, - true /* is_global_walk */); + policy->AddNonIsolatedOriginIfNeeded( + GetIsolationContext(), previously_visited_origin, + true /* is_global_walk_or_frame_removal */); } // static
diff --git a/content/common/set_process_title.cc b/content/common/set_process_title.cc index c0c612cb..8b829a4 100644 --- a/content/common/set_process_title.cc +++ b/content/common/set_process_title.cc
@@ -32,6 +32,7 @@ #include "base/files/file_path.h" #include "base/files/file_util.h" +#include "base/no_destructor.h" #include "base/process/process_metrics.h" #include "base/strings/string_util.h" #include "base/threading/platform_thread.h" @@ -83,7 +84,9 @@ // This prevents program_invocation_short_name from being broken by // setproctitle(). - program_invocation_short_name = strdup(base_name.c_str()); + static base::NoDestructor<base::FilePath::StringType> base_name_storage; + *base_name_storage = std::move(base_name); + program_invocation_short_name = &(*base_name_storage)[0]; } #endif // defined(OS_LINUX) || defined(OS_CHROMEOS)
diff --git a/content/common/set_process_title_linux.cc b/content/common/set_process_title_linux.cc index 138dd47..d2da42a 100644 --- a/content/common/set_process_title_linux.cc +++ b/content/common/set_process_title_linux.cc
@@ -47,15 +47,17 @@ #include <unistd.h> #include <string> +#include <vector> #include "base/files/file_util.h" +#include "base/no_destructor.h" extern char** environ; // g_orig_argv0 is the original process name found in argv[0]. // It is set to a copy of argv[0] in setproctitle_init. It is nullptr if // setproctitle_init was unsuccessful or not called. -static char* g_orig_argv0 = nullptr; +static const char* g_orig_argv0 = nullptr; // Following pointers hold the initial argv/envp memory range. // They are initialized in setproctitle_init and are used to overwrite the @@ -146,7 +148,8 @@ p += strlen(p) + 1; } char* argv_end = p; - for (size_t i = 0; environ[i]; ++i) { + size_t environ_size = 0; + for (size_t i = 0; environ[i]; ++i, ++environ_size) { if (p != environ[i]) return; p += strlen(p) + 1; @@ -154,19 +157,22 @@ char* envp_end = p; // Move the environment out of the way. Note that we are moving the values, - // not the environment array itself. + // not the environment array itself. Also note that we preallocate the entire + // vector, because a string's underlying data pointer is not stable under + // move operations, which could otherwise occur if building up the vector + // incrementally. + static base::NoDestructor<std::vector<std::string>> environ_copy( + environ_size); for (size_t i = 0; environ[i]; ++i) { - char* copy = strdup(environ[i]); - if (!copy) - return; - environ[i] = copy; + (*environ_copy)[i] = environ[i]; + environ[i] = &(*environ_copy)[i][0]; } - char* argv0 = strdup(argv[0]); if (!argv[0]) return; - g_orig_argv0 = argv0; + static base::NoDestructor<std::string> argv0_storage(argv[0]); + g_orig_argv0 = argv0_storage->data(); g_argv_start = argv_start; g_argv_end = argv_end; g_envp_end = envp_end;
diff --git a/content/public/browser/bluetooth_delegate.h b/content/public/browser/bluetooth_delegate.h index 18218fdf..c3505ff 100644 --- a/content/public/browser/bluetooth_delegate.h +++ b/content/public/browser/bluetooth_delegate.h
@@ -87,6 +87,14 @@ RenderFrameHost* frame, const blink::WebBluetoothDeviceId& device_id) = 0; + // This should return true if |frame| has permission to access data associated + // with |manufacturer_code| from advertisement packets from the device with + // |device_id|. + virtual bool IsAllowedToAccessManufacturerData( + RenderFrameHost* frame, + const blink::WebBluetoothDeviceId& device_id, + uint16_t manufacturer_code) = 0; + // This should return a list of devices that the origin in |frame| has been // allowed to access. Access permission is granted with // GrantServiceAccessPermission() and can be revoked by the user in the
diff --git a/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt b/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt index b4d2e550..89230ec 100644 --- a/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt +++ b/content/test/gpu/gpu_tests/test_expectations/trace_test_expectations.txt
@@ -108,3 +108,24 @@ # Fuchsia Flakes. crbug.com/1058255 [ fuchsia ] TraceTest_WebGLGreenTriangle_AA_Alpha [ Skip ] + +# Software compositing is not supported on Android/ChromeOS/Fuchsia: we skip the +# tests that disable GPU compositing (--disable-gpu-compositing). +[ android ] TraceTest_RepeatedWebGLTo2D_SoftwareCompositing [ Skip ] +[ android ] TraceTest_Canvas2DTabSwitch_SoftwareCompositing [ Skip ] +[ android ] TraceTest_WebGLReadPixelsTabSwitch_SoftwareCompositing [ Skip ] +[ android ] DeviceTraceTest_RepeatedWebGLTo2D_SoftwareCompositing [ Skip ] +[ android ] DeviceTraceTest_Canvas2DTabSwitch_SoftwareCompositing [ Skip ] +[ android ] DeviceTraceTest_WebGLReadPixelsTabSwitch_SoftwareCompositing [ Skip ] +[ chromeos ] TraceTest_RepeatedWebGLTo2D_SoftwareCompositing [ Skip ] +[ chromeos ] TraceTest_Canvas2DTabSwitch_SoftwareCompositing [ Skip ] +[ chromeos ] TraceTest_WebGLReadPixelsTabSwitch_SoftwareCompositing [ Skip ] +[ chromeos ] DeviceTraceTest_RepeatedWebGLTo2D_SoftwareCompositing [ Skip ] +[ chromeos ] DeviceTraceTest_Canvas2DTabSwitch_SoftwareCompositing [ Skip ] +[ chromeos ] DeviceTraceTest_WebGLReadPixelsTabSwitch_SoftwareCompositing [ Skip ] +[ fuchsia ] TraceTest_RepeatedWebGLTo2D_SoftwareCompositing [ Skip ] +[ fuchsia ] TraceTest_Canvas2DTabSwitch_SoftwareCompositing [ Skip ] +[ fuchsia ] TraceTest_WebGLReadPixelsTabSwitch_SoftwareCompositing [ Skip ] +[ fuchsia ] DeviceTraceTest_RepeatedWebGLTo2D_SoftwareCompositing [ Skip ] +[ fuchsia ] DeviceTraceTest_Canvas2DTabSwitch_SoftwareCompositing [ Skip ] +[ fuchsia ] DeviceTraceTest_WebGLReadPixelsTabSwitch_SoftwareCompositing [ Skip ]
diff --git a/content/test/gpu/gpu_tests/trace_integration_test.py b/content/test/gpu/gpu_tests/trace_integration_test.py index e8f2c6ef..bf1a4b4b 100644 --- a/content/test/gpu/gpu_tests/trace_integration_test.py +++ b/content/test/gpu/gpu_tests/trace_integration_test.py
@@ -93,13 +93,8 @@ """Struct-like object for passing trace test arguments instead of dicts.""" def __init__( # pylint: disable=too-many-arguments - self, - browser_args, - category, - test_harness_script, - finish_js_condition, - success_eval_func, - other_args=None): + self, browser_args, category, test_harness_script, finish_js_condition, + success_eval_func, other_args): self.browser_args = browser_args self.category = category self.test_harness_script = test_harness_script @@ -126,19 +121,21 @@ for p in namespace.DefaultPages('TraceTest'): yield (p.name, gpu_relative_path + p.url, _TraceTestArguments( - browser_args=[], + browser_args=p.browser_args, category=cls._DisabledByDefaultTraceCategory('gpu.service'), test_harness_script=webgl_test_harness_script, finish_js_condition='domAutomationController._finished', - success_eval_func='CheckGLCategory')) + success_eval_func='CheckGLCategory', + other_args=p.other_args)) for p in namespace.DefaultPages('DeviceTraceTest'): yield (p.name, gpu_relative_path + p.url, _TraceTestArguments( - browser_args=[], + browser_args=p.browser_args, category=cls._DisabledByDefaultTraceCategory('gpu.device'), test_harness_script=webgl_test_harness_script, finish_js_condition='domAutomationController._finished', - success_eval_func='CheckGLCategory')) + success_eval_func='CheckGLCategory', + other_args=p.other_args)) for p in namespace.DirectCompositionPages('VideoPathTraceTest'): yield (p.name, gpu_relative_path + p.url, _TraceTestArguments(
diff --git a/content/test/mock_render_widget_host_delegate.cc b/content/test/mock_render_widget_host_delegate.cc index 9cc6832..a3c836f 100644 --- a/content/test/mock_render_widget_host_delegate.cc +++ b/content/test/mock_render_widget_host_delegate.cc
@@ -81,4 +81,8 @@ return frame_tree_; } +bool MockRenderWidgetHostDelegate::ShouldIgnoreInputEvents() { + return should_ignore_input_events_; +} + } // namespace content
diff --git a/content/test/mock_render_widget_host_delegate.h b/content/test/mock_render_widget_host_delegate.h index c64c743..d43503d 100644 --- a/content/test/mock_render_widget_host_delegate.h +++ b/content/test/mock_render_widget_host_delegate.h
@@ -35,6 +35,9 @@ pre_handle_keyboard_event_result_ = result; } void set_frame_tree(FrameTree* frame_tree) { frame_tree_ = frame_tree; } + void set_should_ignore_input_events(bool ignore) { + should_ignore_input_events_ = ignore; + } void CreateInputEventRouter(); // RenderWidgetHostDelegate: @@ -59,6 +62,7 @@ bool IsFullscreen() override; RenderViewHostDelegateView* GetDelegateView() override; FrameTree* GetFrameTree() override; + bool ShouldIgnoreInputEvents() override; private: std::unique_ptr<NativeWebKeyboardEvent> last_event_; @@ -71,6 +75,7 @@ KeyboardEventProcessingResult::NOT_HANDLED; StubRenderViewHostDelegateView rvh_delegate_view_; FrameTree* frame_tree_ = nullptr; + bool should_ignore_input_events_ = false; DISALLOW_COPY_AND_ASSIGN(MockRenderWidgetHostDelegate); };
diff --git a/content/web_test/browser/fake_bluetooth_delegate.cc b/content/web_test/browser/fake_bluetooth_delegate.cc index 4244b1c..dd7a1648 100644 --- a/content/web_test/browser/fake_bluetooth_delegate.cc +++ b/content/web_test/browser/fake_bluetooth_delegate.cc
@@ -55,7 +55,7 @@ GetOrCreateDeviceIdForDeviceAddress(frame, device->GetAddress()); device_id_to_name_map_[device_id] = device->GetName() ? *device->GetName() : std::string(); - GrantUnionOfServicesForDevice(device_id, options); + GrantUnionOfServicesAndManufacturerDataForDevice(device_id, options); return device_id; } @@ -86,6 +86,18 @@ return !id_to_services_it->second.empty(); } +bool FakeBluetoothDelegate::IsAllowedToAccessManufacturerData( + RenderFrameHost* frame, + const blink::WebBluetoothDeviceId& device_id, + const uint16_t manufacturer_code) { + auto id_to_manufacturer_data_it = + device_id_to_manufacturer_code_map_.find(device_id); + if (id_to_manufacturer_data_it == device_id_to_manufacturer_code_map_.end()) + return false; + + return base::Contains(id_to_manufacturer_data_it->second, manufacturer_code); +} + std::vector<blink::mojom::WebBluetoothDevicePtr> FakeBluetoothDelegate::GetPermittedDevices(RenderFrameHost* frame) { std::vector<blink::mojom::WebBluetoothDevicePtr> permitted_devices; @@ -115,7 +127,7 @@ return device_id; } -void FakeBluetoothDelegate::GrantUnionOfServicesForDevice( +void FakeBluetoothDelegate::GrantUnionOfServicesAndManufacturerDataForDevice( const WebBluetoothDeviceId& device_id, const blink::mojom::WebBluetoothRequestDeviceOptions* options) { if (!options) @@ -139,6 +151,11 @@ for (const BluetoothUUID& uuid : options->optional_services) granted_services.insert(uuid); + + base::flat_set<uint16_t>& granted_manufacturer_data = + device_id_to_manufacturer_code_map_[device_id]; + for (const uint16_t manufacturer_code : options->optional_manufacturer_data) + granted_manufacturer_data.insert(manufacturer_code); } FakeBluetoothDelegate::AddressToIdMap&
diff --git a/content/web_test/browser/fake_bluetooth_delegate.h b/content/web_test/browser/fake_bluetooth_delegate.h index 13447b54..02ca5ea 100644 --- a/content/web_test/browser/fake_bluetooth_delegate.h +++ b/content/web_test/browser/fake_bluetooth_delegate.h
@@ -10,6 +10,7 @@ #include <utility> #include <vector> +#include "base/containers/flat_map.h" #include "base/containers/flat_set.h" #include "content/public/browser/bluetooth_delegate.h" #include "third_party/blink/public/mojom/bluetooth/web_bluetooth.mojom-forward.h" @@ -64,15 +65,22 @@ bool IsAllowedToAccessAtLeastOneService( RenderFrameHost* frame, const blink::WebBluetoothDeviceId& device_id) override; + bool IsAllowedToAccessManufacturerData( + RenderFrameHost* frame, + const blink::WebBluetoothDeviceId& device_id, + const uint16_t manufacturer_code) override; std::vector<blink::mojom::WebBluetoothDevicePtr> GetPermittedDevices( RenderFrameHost* frame) override; private: - using AddressToIdMap = std::map<std::string, blink::WebBluetoothDeviceId>; + using AddressToIdMap = + base::flat_map<std::string, blink::WebBluetoothDeviceId>; using OriginPair = std::pair<url::Origin, url::Origin>; - using IdToServicesMap = std::map<blink::WebBluetoothDeviceId, - base::flat_set<device::BluetoothUUID>>; - using IdToNameMap = std::map<blink::WebBluetoothDeviceId, std::string>; + using IdToServicesMap = base::flat_map<blink::WebBluetoothDeviceId, + base::flat_set<device::BluetoothUUID>>; + using IdToNameMap = base::flat_map<blink::WebBluetoothDeviceId, std::string>; + using IdToManufacturerCodesMap = + base::flat_map<blink::WebBluetoothDeviceId, base::flat_set<uint16_t>>; // Finds an existing WebBluetoothDeviceId for |device_address| for |frame| or // creates a new ID for the Bluetooth device on the current frame. @@ -82,7 +90,7 @@ // Adds the union of |options->filters->services| and // |options->optional_services| to the allowed services for |device_id|. - void GrantUnionOfServicesForDevice( + void GrantUnionOfServicesAndManufacturerDataForDevice( const blink::WebBluetoothDeviceId& device_id, const blink::mojom::WebBluetoothRequestDeviceOptions* options); AddressToIdMap& GetAddressToIdMapForOrigin(RenderFrameHost* frame); @@ -98,6 +106,7 @@ // the service permissions and device names from all of the origins. IdToServicesMap device_id_to_services_map_; IdToNameMap device_id_to_name_map_; + IdToManufacturerCodesMap device_id_to_manufacturer_code_map_; }; } // namespace content
diff --git a/device/fido/BUILD.gn b/device/fido/BUILD.gn index b6dbc06..f45a9cb 100644 --- a/device/fido/BUILD.gn +++ b/device/fido/BUILD.gn
@@ -19,6 +19,8 @@ "cable/v2_constants.h", "cable/v2_handshake.cc", "cable/v2_handshake.h", + "cable/websocket_adapter.cc", + "cable/websocket_adapter.h", "cbor_extract.cc", "ed25519_public_key.cc", "ed25519_public_key.h", @@ -114,8 +116,6 @@ "cable/fido_cable_handshake_handler.h", "cable/fido_tunnel_device.cc", "cable/fido_tunnel_device.h", - "cable/websocket_adapter.cc", - "cable/websocket_adapter.h", "client_data.cc", "client_data.h", "credential_management.cc", @@ -279,6 +279,19 @@ ] } +static_library("cablev2_authenticator") { + sources = [ + "cable/v2_authenticator.cc", + "cable/v2_authenticator.h", + ] + deps = [ + ":fido", + "//components/cbor", + "//components/device_event_log", + "//services/network/public/mojom", + ] +} + if (is_chromeos) { proto_library("u2f_proto") { sources = [ "//third_party/cros_system_api/dbus/u2f/u2f_interface.proto" ] @@ -368,6 +381,7 @@ testonly = true sources = [ "test_callback_receiver.h" ] deps = [ + ":cablev2_authenticator", "//base", "//components/apdu", "//device/fido",
diff --git a/device/fido/DEPS b/device/fido/DEPS index 7df6a75c..abccfd8 100644 --- a/device/fido/DEPS +++ b/device/fido/DEPS
@@ -6,6 +6,7 @@ "+dbus", "+net/base", "+net/cert", + "+net/cookies", "+net/traffic_annotation", "+services/network", "+third_party/boringssl/src/include",
diff --git a/device/fido/cable/fido_tunnel_device.cc b/device/fido/cable/fido_tunnel_device.cc index ce2d9816..4ace265a 100644 --- a/device/fido/cable/fido_tunnel_device.cc +++ b/device/fido/cable/fido_tunnel_device.cc
@@ -67,7 +67,7 @@ "triggered by significant user action." policy_exception_justification: "No policy provided because the operation is triggered by " - " significant user action." + " significant user action. No background activity occurs." })"); FidoTunnelDevice::FidoTunnelDevice(
diff --git a/device/fido/cable/v2_authenticator.cc b/device/fido/cable/v2_authenticator.cc new file mode 100644 index 0000000..4499263 --- /dev/null +++ b/device/fido/cable/v2_authenticator.cc
@@ -0,0 +1,889 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "device/fido/cable/v2_authenticator.h" + +#include "base/memory/weak_ptr.h" +#include "base/sequence_checker.h" +#include "base/strings/string_number_conversions.h" +#include "components/cbor/diagnostic_writer.h" +#include "components/cbor/reader.h" +#include "components/cbor/values.h" +#include "components/cbor/writer.h" +#include "components/device_event_log/device_event_log.h" +#include "crypto/random.h" +#include "device/fido/cable/v2_handshake.h" +#include "device/fido/cable/websocket_adapter.h" +#include "device/fido/cbor_extract.h" +#include "device/fido/fido_constants.h" +#include "device/fido/fido_parsing_utils.h" +#include "net/base/isolation_info.h" +#include "net/cookies/site_for_cookies.h" +#include "net/traffic_annotation/network_traffic_annotation.h" +#include "services/network/public/mojom/network_context.mojom.h" +#include "third_party/boringssl/src/include/openssl/aes.h" +#include "third_party/boringssl/src/include/openssl/ec_key.h" +#include "third_party/boringssl/src/include/openssl/obj.h" + +namespace device { +namespace cablev2 { +namespace authenticator { + +using device::CtapDeviceResponseCode; +using device::CtapRequestCommand; +using device::cbor_extract::IntKey; +using device::cbor_extract::Is; +using device::cbor_extract::Map; +using device::cbor_extract::StepOrByte; +using device::cbor_extract::Stop; +using device::cbor_extract::StringKey; + +namespace { + +constexpr net::NetworkTrafficAnnotationTag kTrafficAnnotation = + net::DefineNetworkTrafficAnnotation("cablev2_websocket_from_authenticator", + R"(semantics { + sender: "Phone as a Security Key" + description: + "Chrome on a phone can communicate with other devices for the " + "purpose of using the phone as a security key. This WebSocket " + "connection is made to a Google service that aids in the exchange " + "of data with the other device. The service carries only " + "end-to-end encrypted data where the keys are shared directly " + "between the two devices via QR code and Bluetooth broadcast." + trigger: + "The user scans a QR code, displayed on the other device, and " + "confirms their desire to communicate with it." + data: "Only encrypted data that the service does not have the keys " + "for." + destination: GOOGLE_OWNED_SERVICE + } + policy { + cookies_allowed: NO + setting: "Not controlled by a setting because the operation is " + "triggered by significant user action." + policy_exception_justification: + "No policy provided because the operation is triggered by " + " significant user action. No background activity occurs." + })"); + +// kTunnelServer is the hardcoded tunnel server that phones will use for network +// communication. This specifies a Google service and the short domain need is +// necessary to fit within a BLE advert. +constexpr uint32_t kTunnelServer = device::cablev2::tunnelserver::EncodeDomain( + "xyi3", + device::cablev2::tunnelserver::TLD::COM); + +struct MakeCredRequest { + const std::vector<uint8_t>* client_data_hash; + const std::string* rp_id; + const std::vector<uint8_t>* user_id; + const cbor::Value::ArrayValue* cred_params; + const cbor::Value::ArrayValue* excluded_credentials; + const std::string* origin; + const std::vector<uint8_t>* challenge; +}; + +static constexpr StepOrByte<MakeCredRequest> kMakeCredParseSteps[] = { + // clang-format off + ELEMENT(Is::kRequired, MakeCredRequest, client_data_hash), + IntKey<MakeCredRequest>(1), + + Map<MakeCredRequest>(), + IntKey<MakeCredRequest>(2), + ELEMENT(Is::kRequired, MakeCredRequest, rp_id), + StringKey<MakeCredRequest>(), 'i', 'd', '\0', + Stop<MakeCredRequest>(), + + Map<MakeCredRequest>(), + IntKey<MakeCredRequest>(3), + ELEMENT(Is::kRequired, MakeCredRequest, user_id), + StringKey<MakeCredRequest>(), 'i', 'd', '\0', + Stop<MakeCredRequest>(), + + ELEMENT(Is::kRequired, MakeCredRequest, cred_params), + IntKey<MakeCredRequest>(4), + ELEMENT(Is::kOptional, MakeCredRequest, excluded_credentials), + IntKey<MakeCredRequest>(5), + + // TODO: remove once the FIDO API can handle clientDataJSON + Map<MakeCredRequest>(), + IntKey<MakeCredRequest>(6), + Map<MakeCredRequest>(), + StringKey<MakeCredRequest>(), + 'g', 'o', 'o', 'g', 'l', 'e', 'A', 'n', 'd', 'r', 'o', 'i', 'd', + 'C', 'l', 'i', 'e', 'n', 't', 'D', 'a', 't', 'a', '\0', + ELEMENT(Is::kRequired, MakeCredRequest, origin), + IntKey<MakeCredRequest>(2), + + ELEMENT(Is::kRequired, MakeCredRequest, challenge), + IntKey<MakeCredRequest>(3), + Stop<MakeCredRequest>(), + Stop<MakeCredRequest>(), + + Stop<MakeCredRequest>(), + // clang-format on +}; + +struct AttestationObject { + const std::string* fmt; + const std::vector<uint8_t>* auth_data; + const cbor::Value* statement; +}; + +static constexpr StepOrByte<AttestationObject> kAttObjParseSteps[] = { + // clang-format off + ELEMENT(Is::kRequired, AttestationObject, fmt), + StringKey<AttestationObject>(), 'f', 'm', 't', '\0', + + ELEMENT(Is::kRequired, AttestationObject, auth_data), + StringKey<AttestationObject>(), 'a', 'u', 't', 'h', 'D', 'a', 't', 'a', + '\0', + + ELEMENT(Is::kRequired, AttestationObject, statement), + StringKey<AttestationObject>(), 'a', 't', 't', 'S', 't', 'm', 't', '\0', + Stop<AttestationObject>(), + // clang-format on +}; + +struct GetAssertionRequest { + const std::string* rp_id; + const std::vector<uint8_t>* client_data_hash; + const cbor::Value::ArrayValue* allowed_credentials; + const std::string* origin; + const std::vector<uint8_t>* challenge; +}; + +static constexpr StepOrByte<GetAssertionRequest> kGetAssertionParseSteps[] = { + // clang-format off + ELEMENT(Is::kRequired, GetAssertionRequest, rp_id), + IntKey<GetAssertionRequest>(1), + + ELEMENT(Is::kRequired, GetAssertionRequest, client_data_hash), + IntKey<GetAssertionRequest>(2), + + ELEMENT(Is::kOptional, GetAssertionRequest, allowed_credentials), + IntKey<GetAssertionRequest>(3), + + // TODO: remove once the FIDO API can handle clientDataJSON + Map<GetAssertionRequest>(), + IntKey<GetAssertionRequest>(4), + Map<GetAssertionRequest>(), + StringKey<GetAssertionRequest>(), + 'g', 'o', 'o', 'g', 'l', 'e', 'A', 'n', 'd', 'r', 'o', 'i', 'd', + 'C', 'l', 'i', 'e', 'n', 't', 'D', 'a', 't', 'a', '\0', + ELEMENT(Is::kRequired, GetAssertionRequest, origin), + IntKey<GetAssertionRequest>(2), + + ELEMENT(Is::kRequired, GetAssertionRequest, challenge), + IntKey<GetAssertionRequest>(3), + Stop<GetAssertionRequest>(), + Stop<GetAssertionRequest>(), + + Stop<GetAssertionRequest>(), + // clang-format on +}; + +// BuildGetInfoResponse returns a CBOR-encoded getInfo response. +std::vector<uint8_t> BuildGetInfoResponse() { + std::array<uint8_t, device::kAaguidLength> aaguid{}; + std::vector<cbor::Value> versions; + versions.emplace_back("FIDO_2_0"); + std::vector<cbor::Value> extensions; + extensions.emplace_back(device::kExtensionAndroidClientData); + // TODO: should be based on whether a screen-lock is enabled. + cbor::Value::MapValue options; + options.emplace("uv", true); + + cbor::Value::MapValue response_map; + response_map.emplace(1, std::move(versions)); + response_map.emplace(2, std::move(extensions)); + response_map.emplace(3, aaguid); + response_map.emplace(4, std::move(options)); + + return cbor::Writer::Write(cbor::Value(std::move(response_map))).value(); +} + +std::array<uint8_t, device::cablev2::kNonceSize> RandomNonce() { + std::array<uint8_t, device::cablev2::kNonceSize> ret; + crypto::RandBytes(ret); + return ret; +} + +using GeneratePairingDataCallback = base::OnceCallback<std::vector<uint8_t>( + base::span<const uint8_t, device::kP256X962Length> peer_public_key_x962, + device::cablev2::HandshakeHash)>; + +// TunnelTransport is a transport that uses WebSockets to talk to a cloud +// service and uses BLE adverts to show proximity. +class TunnelTransport : public Transport { + public: + TunnelTransport( + Platform* platform, + network::mojom::NetworkContext* network_context, + base::span<const uint8_t> secret, + base::span<const uint8_t, device::kP256X962Length> peer_identity, + GeneratePairingDataCallback generate_pairing_data) + : platform_(platform), + nonce_(RandomNonce()), + tunnel_id_(device::cablev2::Derive<EXTENT(tunnel_id_)>( + secret, + nonce_, + DerivedValueType::kTunnelID)), + eid_key_(device::cablev2::Derive<EXTENT(eid_key_)>( + secret, + base::span<const uint8_t>(), + device::cablev2::DerivedValueType::kEIDKey)), + network_context_(network_context), + peer_identity_(device::fido_parsing_utils::Materialize(peer_identity)), + generate_pairing_data_(std::move(generate_pairing_data)) { + DCHECK_EQ(state_, State::kNone); + + state_ = State::kConnecting; + + std::array<uint8_t, device::cablev2::kPSKSize> psk; + psk = device::cablev2::Derive<EXTENT(psk)>( + secret, nonce_, device::cablev2::DerivedValueType::kPSK); + handshaker_ = std::make_unique<device::cablev2::HandshakeInitiator>( + psk, peer_identity, /*local_identity=*/nullptr); + + websocket_client_ = std::make_unique<device::cablev2::WebSocketAdapter>( + base::BindOnce(&TunnelTransport::OnTunnelReady, base::Unretained(this)), + base::BindRepeating(&TunnelTransport::OnTunnelData, + base::Unretained(this))); + target_ = device::cablev2::tunnelserver::GetNewTunnelURL(kTunnelServer, + tunnel_id_); + } + + TunnelTransport( + Platform* platform, + network::mojom::NetworkContext* network_context, + base::span<const uint8_t> secret, + base::span<const uint8_t, device::cablev2::kClientNonceSize> client_nonce, + std::array<uint8_t, device::cablev2::kRoutingIdSize> routing_id, + base::span<const uint8_t, 16> tunnel_id, + bssl::UniquePtr<EC_KEY> local_identity) + : platform_(platform), + nonce_(RandomNonce()), + tunnel_id_(fido_parsing_utils::Materialize(tunnel_id)), + eid_key_(device::cablev2::Derive<EXTENT(eid_key_)>( + secret, + client_nonce, + device::cablev2::DerivedValueType::kEIDKey)), + network_context_(network_context) { + DCHECK_EQ(state_, State::kNone); + + state_ = State::kConnectingPaired; + + std::array<uint8_t, device::cablev2::kPSKSize> psk; + psk = device::cablev2::Derive<EXTENT(psk)>( + secret, nonce_, device::cablev2::DerivedValueType::kPSK); + handshaker_ = std::make_unique<device::cablev2::HandshakeInitiator>( + psk, /*peer_identity=*/base::nullopt, std::move(local_identity)); + + websocket_client_ = std::make_unique<device::cablev2::WebSocketAdapter>( + base::BindOnce(&TunnelTransport::OnTunnelReady, base::Unretained(this)), + base::BindRepeating(&TunnelTransport::OnTunnelData, + base::Unretained(this))); + target_ = device::cablev2::tunnelserver::GetConnectURL( + kTunnelServer, routing_id, tunnel_id); + } + + // Transport: + + void StartReading( + base::RepeatingCallback<void(base::Optional<std::vector<uint8_t>>)> + read_callback) override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(!read_callback_); + + read_callback_ = std::move(read_callback); + + network_context_->CreateWebSocket( + target_, {device::kCableWebSocketProtocol}, net::SiteForCookies(), + net::IsolationInfo(), /*headers=*/{}, network::mojom::kBrowserProcessId, + /*render_frame_id=*/0, url::Origin::Create(target_), + network::mojom::kWebSocketOptionBlockAllCookies, + net::MutableNetworkTrafficAnnotationTag(kTrafficAnnotation), + websocket_client_->BindNewHandshakeClientPipe(), mojo::NullRemote(), + mojo::NullRemote()); + FIDO_LOG(DEBUG) << "Creating WebSocket to " << target_.spec(); + } + + void Write(std::vector<uint8_t> data) override { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK_EQ(state_, kReady); + + if (!crypter_->Encrypt(&data)) { + FIDO_LOG(ERROR) << "Failed to encrypt response"; + return; + } + websocket_client_->Write(data); + } + + private: + enum State { + kNone, + kConnecting, + kConnectingPaired, + kConnected, + kConnectedPaired, + kReady, + }; + + // This is a dummy function to allow things to compile at each step of a + // multi-CL sequence. + void OnTunnelReady(bool ok, base::Optional<uint8_t> routing_id) {} + + void OnTunnelReady_Future( + bool ok, + base::Optional<std::array<uint8_t, device::cablev2::kRoutingIdSize>> + routing_id) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK(state_ == State::kConnecting || state_ == State::kConnectingPaired); + + if (ok && state_ == State::kConnecting && !routing_id) { + FIDO_LOG(ERROR) << "Tunnel server did not specify routing ID"; + ok = false; + } + + if (!ok) { + FIDO_LOG(ERROR) << "Failed to connect to tunnel server"; + read_callback_.Run(base::nullopt); + return; + } + + FIDO_LOG(DEBUG) << "WebSocket connection established."; + + if (state_ == State::kConnecting) { + state_ = State::kConnected; + } else { + DCHECK_EQ(state_, State::kConnectingPaired); + state_ = State::kConnectedPaired; + } + + static constexpr std::array<uint8_t, device::cablev2::kRoutingIdSize> + kZeroRoutingID = {0, 0, 0}; + const device::CableEidArray eid = + StartAdvertising(routing_id.value_or(kZeroRoutingID)); + std::vector<uint8_t> msg = + handshaker_->BuildInitialMessage(eid, BuildGetInfoResponse()); + websocket_client_->Write(msg); + } + + void OnTunnelData(base::Optional<base::span<const uint8_t>> msg) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + if (!msg) { + read_callback_.Run(base::nullopt); + return; + } + + switch (state_) { + case State::kConnectedPaired: + case State::kConnected: { + base::Optional<std::pair<std::unique_ptr<device::cablev2::Crypter>, + device::cablev2::HandshakeHash>> + result = handshaker_->ProcessResponse(*msg); + handshaker_.reset(); + if (!result) { + FIDO_LOG(ERROR) << "caBLE handshake failure"; + read_callback_.Run(base::nullopt); + return; + } + FIDO_LOG(DEBUG) << "caBLE handshake complete"; + crypter_ = std::move(result->first); + + if (state_ == State::kConnected) { + std::vector<uint8_t> pairing_data = + std::move(generate_pairing_data_) + .Run(*peer_identity_, result->second); + if (!crypter_->Encrypt(&pairing_data)) { + FIDO_LOG(ERROR) << "failed to encode pairing data"; + return; + } + + websocket_client_->Write(pairing_data); + } + + state_ = State::kReady; + break; + } + + case State::kReady: { + std::vector<uint8_t> plaintext; + if (!crypter_->Decrypt(*msg, &plaintext)) { + FIDO_LOG(ERROR) << "failed to decrypt caBLE message"; + read_callback_.Run(base::nullopt); + return; + } + + read_callback_.Run(plaintext); + break; + } + + default: + NOTREACHED(); + } + } + + device::CableEidArray StartAdvertising( + std::array<uint8_t, device::cablev2::kRoutingIdSize> routing_id) { + const device::cablev2::eid::Components components{ + .tunnel_server_domain = kTunnelServer, + .routing_id = routing_id, + .nonce = nonce_, + }; + const device::CableEidArray eid_plaintext = + device::cablev2::eid::FromComponents(components); + + AES_KEY key; + CHECK(AES_set_encrypt_key(eid_key_.data(), + /*bits=*/8 * eid_key_.size(), &key) == 0); + std::array<uint8_t, AES_BLOCK_SIZE> eid; + static_assert(EXTENT(eid_plaintext) == AES_BLOCK_SIZE, + "EIDs are not AES blocks"); + AES_encrypt(/*in=*/eid_plaintext.data(), /*out=*/eid.data(), &key); + + ble_advert_ = platform_->SendBLEAdvert(eid); + return eid; + } + + Platform* const platform_; + State state_ = State::kNone; + const std::array<uint8_t, kNonceSize> nonce_; + const std::array<uint8_t, kTunnelIdSize> tunnel_id_; + const std::array<uint8_t, kEIDKeySize> eid_key_; + std::unique_ptr<WebSocketAdapter> websocket_client_; + std::unique_ptr<HandshakeInitiator> handshaker_; + std::unique_ptr<Crypter> crypter_; + network::mojom::NetworkContext* const network_context_; + const base::Optional<std::array<uint8_t, kP256X962Length>> peer_identity_; + GeneratePairingDataCallback generate_pairing_data_; + GURL target_; + std::unique_ptr<Platform::BLEAdvert> ble_advert_; + base::RepeatingCallback<void(base::Optional<std::vector<uint8_t>>)> + read_callback_; + + SEQUENCE_CHECKER(sequence_checker_); +}; + +class CTAP2Processor : public Transaction { + public: + CTAP2Processor(std::unique_ptr<Transport> transport, + std::unique_ptr<Platform> platform, + Transaction::CompleteCallback complete_callback) + : transport_(std::move(transport)), + platform_(std::move(platform)), + complete_callback_(std::move(complete_callback)) { + transport_->StartReading( + base::BindRepeating(&CTAP2Processor::OnData, base::Unretained(this))); + } + + private: + void OnData(base::Optional<std::vector<uint8_t>> msg) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + if (!msg) { + std::move(complete_callback_).Run(); + return; + } + + base::Optional<std::vector<uint8_t>> response = ProcessCTAPMessage(*msg); + if (!response) { + // Fatal error. + // TODO: need to signal this to the UI. + std::move(complete_callback_).Run(); + return; + } + + if (response->empty()) { + // Response is pending. + return; + } + + transport_->Write(std::move(*response)); + } + + base::Optional<std::vector<uint8_t>> ProcessCTAPMessage( + base::span<const uint8_t> message_bytes) { + if (message_bytes.empty()) { + return base::nullopt; + } + const auto command = message_bytes[0]; + const auto cbor_bytes = message_bytes.subspan(1); + + base::Optional<cbor::Value> payload; + if (!cbor_bytes.empty()) { + payload = cbor::Reader::Read(cbor_bytes); + if (!payload) { + FIDO_LOG(ERROR) << "CBOR decoding failed for " + << base::HexEncode(cbor_bytes); + return base::nullopt; + } + FIDO_LOG(DEBUG) << "<- (" << base::HexEncode(&command, 1) << ") " + << cbor::DiagnosticWriter::Write(*payload); + } else { + FIDO_LOG(DEBUG) << "<- (" << base::HexEncode(&command, 1) + << ") <no payload>"; + } + + switch (command) { + case static_cast<uint8_t>( + device::CtapRequestCommand::kAuthenticatorGetInfo): { + if (payload) { + FIDO_LOG(ERROR) << "getInfo command incorrectly contained payload"; + return base::nullopt; + } + + base::Optional<std::vector<uint8_t>> response = BuildGetInfoResponse(); + if (!response) { + return base::nullopt; + } + response->insert( + response->begin(), + static_cast<uint8_t>(CtapDeviceResponseCode::kSuccess)); + return response; + } + + case static_cast<uint8_t>( + device::CtapRequestCommand::kAuthenticatorMakeCredential): { + if (!payload || !payload->is_map()) { + FIDO_LOG(ERROR) << "Invalid makeCredential payload"; + return base::nullopt; + } + + MakeCredRequest make_cred_request; + if (!device::cbor_extract::Extract<MakeCredRequest>( + &make_cred_request, kMakeCredParseSteps, payload->GetMap())) { + FIDO_LOG(ERROR) << "Failed to parse makeCredential request"; + return base::nullopt; + } + + std::vector<int> algorithms; + if (!device::cbor_extract::ForEachPublicKeyEntry( + *make_cred_request.cred_params, cbor::Value("alg"), + base::BindRepeating( + [](std::vector<int>* out, + const cbor::Value& value) -> bool { + if (!value.is_integer()) { + return false; + } + const int64_t alg = value.GetInteger(); + + if (alg > std::numeric_limits<int>::max() || + alg < std::numeric_limits<int>::min()) { + return false; + } + out->push_back(static_cast<int>(alg)); + return true; + }, + base::Unretained(&algorithms)))) { + return base::nullopt; + } + + std::vector<std::vector<uint8_t>> excluded_credential_ids; + if (make_cred_request.excluded_credentials && + !device::cbor_extract::ForEachPublicKeyEntry( + *make_cred_request.excluded_credentials, cbor::Value("id"), + base::BindRepeating( + [](std::vector<std::vector<uint8_t>>* out, + const cbor::Value& value) -> bool { + if (!value.is_bytestring()) { + return false; + } + out->push_back(value.GetBytestring()); + return true; + }, + base::Unretained(&excluded_credential_ids)))) { + return base::nullopt; + } + + // TODO: plumb the rk flag through once GmsCore supports resident + // keys. This will require support for optional maps in |Extract|. + platform_->MakeCredential( + *make_cred_request.origin, *make_cred_request.rp_id, + *make_cred_request.challenge, *make_cred_request.user_id, + algorithms, excluded_credential_ids, + /*resident_key_required=*/false, + base::BindOnce(&CTAP2Processor::OnMakeCredentialResponse, + weak_factory_.GetWeakPtr())); + return std::vector<uint8_t>(); + } + + case static_cast<uint8_t>( + device::CtapRequestCommand::kAuthenticatorGetAssertion): { + if (!payload || !payload->is_map()) { + FIDO_LOG(ERROR) << "Invalid makeCredential payload"; + return base::nullopt; + } + GetAssertionRequest get_assertion_request; + if (!device::cbor_extract::Extract<GetAssertionRequest>( + &get_assertion_request, kGetAssertionParseSteps, + payload->GetMap())) { + FIDO_LOG(ERROR) << "Failed to parse getAssertion request"; + return base::nullopt; + } + + std::vector<std::vector<uint8_t>> allowed_credential_ids; + if (get_assertion_request.allowed_credentials && + !device::cbor_extract::ForEachPublicKeyEntry( + *get_assertion_request.allowed_credentials, cbor::Value("id"), + base::BindRepeating( + [](std::vector<std::vector<uint8_t>>* out, + const cbor::Value& value) -> bool { + if (!value.is_bytestring()) { + return false; + } + out->push_back(value.GetBytestring()); + return true; + }, + base::Unretained(&allowed_credential_ids)))) { + return base::nullopt; + } + + platform_->GetAssertion( + *get_assertion_request.origin, *get_assertion_request.rp_id, + *get_assertion_request.challenge, allowed_credential_ids, + base::BindOnce(&CTAP2Processor::OnGetAssertionResponse, + weak_factory_.GetWeakPtr())); + + return std::vector<uint8_t>(); + } + + default: + FIDO_LOG(ERROR) << "Received unknown command " + << static_cast<unsigned>(command); + return base::nullopt; + } + } + + void OnMakeCredentialResponse(uint32_t ctap_status, + base::span<const uint8_t> client_data_json, + base::span<const uint8_t> attestation_object) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK_LE(ctap_status, 0xFFu); + + std::vector<uint8_t> response = {base::checked_cast<uint8_t>(ctap_status)}; + if (ctap_status == static_cast<uint8_t>(CtapDeviceResponseCode::kSuccess)) { + // TODO: pass response parameters from the Java side. + base::Optional<cbor::Value> cbor_attestation_object = + cbor::Reader::Read(attestation_object); + if (!cbor_attestation_object || !cbor_attestation_object->is_map()) { + FIDO_LOG(ERROR) << "invalid CBOR attestation object"; + return; + } + + AttestationObject attestation_object; + if (!device::cbor_extract::Extract<AttestationObject>( + &attestation_object, kAttObjParseSteps, + cbor_attestation_object->GetMap())) { + FIDO_LOG(ERROR) << "attestation object parse failed"; + return; + } + + cbor::Value::MapValue response_map; + response_map.emplace(1, base::StringPiece(*attestation_object.fmt)); + response_map.emplace( + 2, base::span<const uint8_t>(*attestation_object.auth_data)); + response_map.emplace(3, attestation_object.statement->Clone()); + response_map.emplace(device::kAndroidClientDataExtOutputKey, + client_data_json); + + base::Optional<std::vector<uint8_t>> response_payload = + cbor::Writer::Write(cbor::Value(std::move(response_map))); + if (!response_payload) { + return; + } + response.insert(response.end(), response_payload->begin(), + response_payload->end()); + } + + transport_->Write(std::move(response)); + } + + void OnGetAssertionResponse(uint32_t ctap_status, + base::span<const uint8_t> client_data_json, + base::span<const uint8_t> credential_id, + base::span<const uint8_t> authenticator_data, + base::span<const uint8_t> signature) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + DCHECK_LE(ctap_status, 0xFFu); + std::vector<uint8_t> response = {base::checked_cast<uint8_t>(ctap_status)}; + + if (ctap_status == static_cast<uint8_t>(CtapDeviceResponseCode::kSuccess)) { + cbor::Value::MapValue credential_descriptor; + credential_descriptor.emplace("type", device::kPublicKey); + credential_descriptor.emplace("id", credential_id); + cbor::Value::ArrayValue transports; + transports.emplace_back("internal"); + transports.emplace_back("cable"); + credential_descriptor.emplace("transports", std::move(transports)); + cbor::Value::MapValue response_map; + response_map.emplace(1, std::move(credential_descriptor)); + response_map.emplace(2, authenticator_data); + response_map.emplace(3, signature); + // TODO: add user entity to support resident keys. + response_map.emplace(device::kAndroidClientDataExtOutputKey, + client_data_json); + + base::Optional<std::vector<uint8_t>> response_payload = + cbor::Writer::Write(cbor::Value(std::move(response_map))); + if (!response_payload) { + return; + } + response.insert(response.end(), response_payload->begin(), + response_payload->end()); + } + + transport_->Write(std::move(response)); + } + + const std::unique_ptr<Transport> transport_; + const std::unique_ptr<Platform> platform_; + Transaction::CompleteCallback complete_callback_; + SEQUENCE_CHECKER(sequence_checker_); + base::WeakPtrFactory<CTAP2Processor> weak_factory_{this}; +}; + +static bssl::UniquePtr<EC_KEY> IdentityKey( + base::span<const uint8_t, 32> root_secret) { + std::array<uint8_t, 32> seed; + seed = device::cablev2::Derive<EXTENT(seed)>( + root_secret, /*nonce=*/base::span<uint8_t>(), + device::cablev2::DerivedValueType::kIdentityKeySeed); + bssl::UniquePtr<EC_GROUP> p256( + EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)); + return bssl::UniquePtr<EC_KEY>( + EC_KEY_derive_from_secret(p256.get(), seed.data(), seed.size())); +} + +class PairingDataGenerator { + public: + static base::OnceCallback< + std::vector<uint8_t>(base::span<const uint8_t, device::kP256X962Length>, + device::cablev2::HandshakeHash)> + GetClosure(base::span<const uint8_t, kRootSecretSize> root_secret, + const std::string& name, + base::Optional<std::vector<uint8_t>> contact_id) { + auto* generator = + new PairingDataGenerator(root_secret, name, std::move(contact_id)); + return base::BindOnce(&PairingDataGenerator::Generate, + base::Owned(generator)); + } + + private: + PairingDataGenerator(base::span<const uint8_t, kRootSecretSize> root_secret, + const std::string& name, + base::Optional<std::vector<uint8_t>> contact_id) + : root_secret_(fido_parsing_utils::Materialize(root_secret)), + name_(name), + contact_id_(std::move(contact_id)) {} + + std::vector<uint8_t> Generate( + base::span<const uint8_t, device::kP256X962Length> peer_public_key_x962, + device::cablev2::HandshakeHash handshake_hash) { + cbor::Value::MapValue map; + + if (contact_id_) { + map.emplace(1, std::move(*contact_id_)); + + std::array<uint8_t, device::cablev2::kNonceSize> pairing_id; + crypto::RandBytes(pairing_id); + + map.emplace(2, pairing_id); + + std::array<uint8_t, 32> paired_secret; + paired_secret = device::cablev2::Derive<EXTENT(paired_secret)>( + root_secret_, pairing_id, + device::cablev2::DerivedValueType::kPairedSecret); + + map.emplace(3, paired_secret); + + bssl::UniquePtr<EC_KEY> identity_key(IdentityKey(root_secret_)); + device::CableAuthenticatorIdentityKey public_key; + CHECK_EQ( + public_key.size(), + EC_POINT_point2oct(EC_KEY_get0_group(identity_key.get()), + EC_KEY_get0_public_key(identity_key.get()), + POINT_CONVERSION_UNCOMPRESSED, public_key.data(), + public_key.size(), /*ctx=*/nullptr)); + + map.emplace(4, public_key); + map.emplace(5, name_); + + map.emplace( + 6, device::cablev2::CalculatePairingSignature( + identity_key.get(), peer_public_key_x962, handshake_hash)); + } + + std::vector<uint8_t> empty_vector; + return device::cablev2::EncodePaddedCBORMap(std::move(map)) + .value_or(empty_vector); + } + + const std::array<uint8_t, kRootSecretSize> root_secret_; + const std::string name_; + base::Optional<std::vector<uint8_t>> contact_id_; +}; + +} // namespace + +Platform::BLEAdvert::~BLEAdvert() = default; +Platform::~Platform() = default; +Transport::~Transport() = default; +Transaction::~Transaction() = default; + +std::unique_ptr<Transaction> TransactWithPlaintextTransport( + std::unique_ptr<Platform> platform, + std::unique_ptr<Transport> transport, + Transaction::CompleteCallback complete_callback) { + return std::make_unique<CTAP2Processor>( + std::move(transport), std::move(platform), std::move(complete_callback)); +} + +std::unique_ptr<Transaction> TransactFromQRCode( + std::unique_ptr<Platform> platform, + network::mojom::NetworkContext* network_context, + base::span<const uint8_t, kRootSecretSize> root_secret, + const std::string& authenticator_name, + base::span<const uint8_t, 16> qr_secret, + base::span<const uint8_t, kP256X962Length> peer_identity, + base::Optional<std::vector<uint8_t>> contact_id, + Transaction::CompleteCallback complete_callback) { + auto generate_pairing_data = PairingDataGenerator::GetClosure( + root_secret, authenticator_name, contact_id); + + Platform* const platform_ptr = platform.get(); + return std::make_unique<CTAP2Processor>( + std::make_unique<TunnelTransport>(platform_ptr, network_context, + qr_secret, peer_identity, + std::move(generate_pairing_data)), + std::move(platform), std::move(complete_callback)); +} + +std::unique_ptr<Transaction> TransactFromFCM( + std::unique_ptr<Platform> platform, + network::mojom::NetworkContext* network_context, + base::span<const uint8_t, kRootSecretSize> root_secret, + std::array<uint8_t, kRoutingIdSize> routing_id, + base::span<const uint8_t, kTunnelIdSize> tunnel_id, + base::span<const uint8_t> pairing_id, + base::span<const uint8_t, kClientNonceSize> client_nonce, + Transaction::CompleteCallback complete_callback) { + std::array<uint8_t, 32> paired_secret; + paired_secret = Derive<EXTENT(paired_secret)>( + root_secret, pairing_id, DerivedValueType::kPairedSecret); + + Platform* const platform_ptr = platform.get(); + return std::make_unique<CTAP2Processor>( + std::make_unique<TunnelTransport>(platform_ptr, network_context, + paired_secret, client_nonce, routing_id, + tunnel_id, IdentityKey(root_secret)), + std::move(platform), std::move(complete_callback)); +} + +} // namespace authenticator +} // namespace cablev2 +} // namespace device
diff --git a/device/fido/cable/v2_authenticator.h b/device/fido/cable/v2_authenticator.h new file mode 100644 index 0000000..28810230 --- /dev/null +++ b/device/fido/cable/v2_authenticator.h
@@ -0,0 +1,128 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef DEVICE_FIDO_CABLE_V2_AUTHENTICATOR_H_ +#define DEVICE_FIDO_CABLE_V2_AUTHENTICATOR_H_ + +#include <string> +#include <vector> + +#include <stdint.h> + +#include "base/callback_forward.h" +#include "base/containers/span.h" +#include "base/optional.h" +#include "device/fido/cable/v2_constants.h" +#include "device/fido/fido_constants.h" +#include "services/network/public/mojom/network_context.mojom-forward.h" + +namespace device { +namespace cablev2 { +namespace authenticator { + +// Platform abstracts the actions taken by the platform, i.e. the +// credential-store operations themselves, plus an interface for BLE +// advertising. +class Platform { + public: + // BLEAdvert represents a currently-transmitting advert. Destroying the object + // stops the transmission. + class BLEAdvert { + public: + virtual ~BLEAdvert(); + }; + + virtual ~Platform(); + + using MakeCredentialCallback = + base::OnceCallback<void(uint32_t status, + base::span<const uint8_t> client_data_json, + base::span<const uint8_t> attestation_obj)>; + using GetAssertionCallback = + base::OnceCallback<void(uint32_t status, + base::span<const uint8_t> client_data_json, + base::span<const uint8_t> cred_id, + base::span<const uint8_t> auth_data, + base::span<const uint8_t> sig)>; + + virtual void MakeCredential( + const std::string& origin, + const std::string& rp_id, + base::span<const uint8_t> challenge, + base::span<const uint8_t> user_id, + base::span<const int> algorithms, + base::span<const std::vector<uint8_t>> excluded_cred_ids, + bool resident_key_required, + MakeCredentialCallback callback) = 0; + + virtual void GetAssertion( + const std::string& origin, + const std::string& rp_id, + base::span<const uint8_t> challenge, + base::span<const std::vector<uint8_t>> allowed_cred_ids, + GetAssertionCallback callback) = 0; + + virtual std::unique_ptr<BLEAdvert> SendBLEAdvert( + base::span<uint8_t, 16> payload) = 0; +}; + +// Transport abstracts a way of transmitting to, and receiving from, the peer. +// The framing of messages must be preserved. +class Transport { + public: + virtual ~Transport(); + + // StartReading requests that the given callback be called whenever a message + // arrives from the peer. + virtual void StartReading( + base::RepeatingCallback<void(base::Optional<std::vector<uint8_t>>)> + read_callback) = 0; + virtual void Write(std::vector<uint8_t> data) = 0; +}; + +// A Transaction is a handle to an ongoing caBLEv2 transaction with a peer. +class Transaction { + public: + using CompleteCallback = base::OnceCallback<void()>; + + virtual ~Transaction(); +}; + +// TransactWithPlaintextTransport allows an arbitrary transport to be used for a +// caBLEv2 transaction. +std::unique_ptr<Transaction> TransactWithPlaintextTransport( + std::unique_ptr<Platform> platform, + std::unique_ptr<Transport> transport, + Transaction::CompleteCallback complete_callback); + +// TransactFromQRCode starts a network-based transaction based on the decoded +// contents of a QR code. +std::unique_ptr<Transaction> TransactFromQRCode( + std::unique_ptr<Platform> platform, + network::mojom::NetworkContext* network_context, + base::span<const uint8_t, kRootSecretSize> root_secret, + const std::string& authenticator_name, + // TODO: name this constant. + base::span<const uint8_t, 16> qr_secret, + base::span<const uint8_t, kP256X962Length> peer_identity, + base::Optional<std::vector<uint8_t>> contact_id, + Transaction::CompleteCallback complete_callback); + +// TransactFromQRCode starts a network-based transaction based on the decoded +// contents of a cloud message. +std::unique_ptr<Transaction> TransactFromFCM( + std::unique_ptr<Platform> platform, + network::mojom::NetworkContext* network_context, + base::span<const uint8_t, kRootSecretSize> root_secret, + std::array<uint8_t, kRoutingIdSize> routing_id, + base::span<const uint8_t, kTunnelIdSize> tunnel_id, + base::span<const uint8_t> pairing_id, + base::span<const uint8_t, kClientNonceSize> client_nonce, + Transaction::CompleteCallback complete_callback); + +} // namespace authenticator +} // namespace cablev2 +} // namespace device + +#endif // DEVICE_FIDO_CABLE_V2_AUTHENTICATOR_H_
diff --git a/device/fido/fido_device_authenticator_unittest.cc b/device/fido/fido_device_authenticator_unittest.cc index b9076a8..ed26e96 100644 --- a/device/fido/fido_device_authenticator_unittest.cc +++ b/device/fido/fido_device_authenticator_unittest.cc
@@ -15,6 +15,7 @@ #include "device/fido/test_callback_receiver.h" #include "device/fido/virtual_ctap2_device.h" #include "device/fido/virtual_fido_device.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" namespace device { @@ -27,8 +28,11 @@ CtapDeviceResponseCode, base::Optional<std::vector<std::pair<LargeBlobKey, std::vector<uint8_t>>>>>; -constexpr LargeBlobKey kDummyKey = {{0x01}}; -constexpr std::array<uint8_t, 4> kSmallBlob = {'l', 'u', 'm', 'a'}; +constexpr LargeBlobKey kDummyKey1 = {{0x01}}; +constexpr LargeBlobKey kDummyKey2 = {{0x02}}; +constexpr std::array<uint8_t, 4> kSmallBlob1 = {'r', 'o', 's', 'a'}; +constexpr std::array<uint8_t, 4> kSmallBlob2 = {'l', 'u', 'm', 'a'}; +constexpr std::array<uint8_t, 4> kSmallBlob3 = {'s', 't', 'a', 'r'}; constexpr size_t kMaxStorageSize = 4096; class FidoDeviceAuthenticatorTest : public testing::Test { @@ -66,7 +70,7 @@ TEST_F(FidoDeviceAuthenticatorTest, TestReadEmptyLargeBlob) { ReadCallback callback; - authenticator_->ReadLargeBlob({kDummyKey}, base::nullopt, + authenticator_->ReadLargeBlob({kDummyKey1}, base::nullopt, callback.callback()); callback.WaitForCallback(); @@ -77,7 +81,7 @@ TEST_F(FidoDeviceAuthenticatorTest, TestReadInvalidLargeBlob) { authenticator_state_->large_blob[0] += 1; ReadCallback callback; - authenticator_->ReadLargeBlob({kDummyKey}, base::nullopt, + authenticator_->ReadLargeBlob({kDummyKey1}, base::nullopt, callback.callback()); callback.WaitForCallback(); @@ -88,23 +92,24 @@ // Test reading and writing a blob that fits in a single fragment. TEST_F(FidoDeviceAuthenticatorTest, TestWriteSmallBlob) { - std::vector<uint8_t> small_blob = fido_parsing_utils::Materialize(kSmallBlob); + std::vector<uint8_t> small_blob = + fido_parsing_utils::Materialize(kSmallBlob1); WriteCallback write_callback; - authenticator_->WriteLargeBlob(small_blob, {kDummyKey}, base::nullopt, + authenticator_->WriteLargeBlob(small_blob, {kDummyKey1}, base::nullopt, write_callback.callback()); write_callback.WaitForCallback(); ASSERT_EQ(CtapDeviceResponseCode::kSuccess, write_callback.value()); ReadCallback read_callback; - authenticator_->ReadLargeBlob({kDummyKey}, base::nullopt, + authenticator_->ReadLargeBlob({kDummyKey1}, base::nullopt, read_callback.callback()); read_callback.WaitForCallback(); ASSERT_EQ(CtapDeviceResponseCode::kSuccess, read_callback.status()); auto large_blob_array = read_callback.value(); ASSERT_TRUE(large_blob_array); ASSERT_EQ(1u, large_blob_array->size()); - EXPECT_EQ(kDummyKey, large_blob_array->at(0).first); + EXPECT_EQ(kDummyKey1, large_blob_array->at(0).first); EXPECT_EQ(small_blob, large_blob_array->at(0).second); } @@ -117,24 +122,63 @@ } WriteCallback write_callback; - authenticator_->WriteLargeBlob(large_blob, {kDummyKey}, base::nullopt, + authenticator_->WriteLargeBlob(large_blob, {kDummyKey1}, base::nullopt, write_callback.callback()); write_callback.WaitForCallback(); ASSERT_EQ(CtapDeviceResponseCode::kSuccess, write_callback.value()); ReadCallback read_callback; - authenticator_->ReadLargeBlob({kDummyKey}, base::nullopt, + authenticator_->ReadLargeBlob({kDummyKey1}, base::nullopt, read_callback.callback()); read_callback.WaitForCallback(); ASSERT_EQ(CtapDeviceResponseCode::kSuccess, read_callback.status()); auto large_blob_array = read_callback.value(); ASSERT_TRUE(large_blob_array); ASSERT_EQ(1u, large_blob_array->size()); - EXPECT_EQ(kDummyKey, large_blob_array->at(0).first); + EXPECT_EQ(kDummyKey1, large_blob_array->at(0).first); EXPECT_EQ(large_blob, large_blob_array->at(0).second); } +// Test updating a large blob in an array with multiple entries corresponding to +// other keys. +TEST_F(FidoDeviceAuthenticatorTest, TestUpdateLargeBlob) { + WriteCallback write_callback1; + authenticator_->WriteLargeBlob(fido_parsing_utils::Materialize(kSmallBlob1), + {kDummyKey1}, base::nullopt, + write_callback1.callback()); + write_callback1.WaitForCallback(); + ASSERT_EQ(CtapDeviceResponseCode::kSuccess, write_callback1.value()); + + WriteCallback write_callback2; + std::vector<uint8_t> small_blob2 = + fido_parsing_utils::Materialize(kSmallBlob2); + authenticator_->WriteLargeBlob(small_blob2, {kDummyKey2}, base::nullopt, + write_callback2.callback()); + write_callback2.WaitForCallback(); + ASSERT_EQ(CtapDeviceResponseCode::kSuccess, write_callback2.value()); + + // Update the first entry. + WriteCallback write_callback3; + std::vector<uint8_t> small_blob3 = + fido_parsing_utils::Materialize(kSmallBlob3); + authenticator_->WriteLargeBlob(small_blob3, {kDummyKey1}, base::nullopt, + write_callback3.callback()); + write_callback3.WaitForCallback(); + ASSERT_EQ(CtapDeviceResponseCode::kSuccess, write_callback3.value()); + + ReadCallback read_callback; + authenticator_->ReadLargeBlob({kDummyKey1, kDummyKey2}, base::nullopt, + read_callback.callback()); + read_callback.WaitForCallback(); + ASSERT_EQ(CtapDeviceResponseCode::kSuccess, read_callback.status()); + auto large_blob_array = read_callback.value(); + ASSERT_TRUE(large_blob_array); + EXPECT_THAT(*large_blob_array, testing::UnorderedElementsAre( + std::make_pair(kDummyKey1, small_blob3), + std::make_pair(kDummyKey2, small_blob2))); +} + } // namespace } // namespace device
diff --git a/device/fido/fido_parsing_utils.h b/device/fido/fido_parsing_utils.h index 43136148..6304cf9f 100644 --- a/device/fido/fido_parsing_utils.h +++ b/device/fido/fido_parsing_utils.h
@@ -139,6 +139,17 @@ return ExtractArray(bytestring, /*pos=*/0, out); } +constexpr std::array<uint8_t, 4> Uint32LittleEndian(uint32_t value) { + return {value & 0xFF, value >> 8 & 0xFF, value >> 16 & 0xFF, + value >> 24 & 0xFF}; +} + +constexpr std::array<uint8_t, 8> Uint64LittleEndian(uint64_t value) { + return {value & 0xFF, value >> 8 & 0xFF, value >> 16 & 0xFF, + value >> 24 & 0xFF, value >> 32 & 0xFF, value >> 40 & 0xFF, + value >> 48 & 0xFF, value >> 56 & 0xFF}; +} + } // namespace fido_parsing_utils } // namespace device
diff --git a/device/fido/large_blob.cc b/device/fido/large_blob.cc index 47aba0a6..48300bb8 100644 --- a/device/fido/large_blob.cc +++ b/device/fido/large_blob.cc
@@ -6,6 +6,8 @@ #include "base/containers/span.h" #include "components/cbor/reader.h" #include "components/cbor/writer.h" +#include "crypto/aead.h" +#include "crypto/random.h" #include "crypto/sha2.h" #include "device/fido/fido_parsing_utils.h" #include "device/fido/pin.h" @@ -15,6 +17,21 @@ namespace { // The number of bytes the large blob validation hash is truncated to. constexpr size_t kTruncatedHashBytes = 16; +constexpr std::array<uint8_t, 4> kLargeBlobADPrefix = {'b', 'l', 'o', 'b'}; +constexpr size_t kAssociatedDataLength = kLargeBlobADPrefix.size() + 8; + +std::array<uint8_t, kAssociatedDataLength> GenerateLargeBlobAdditionalData( + size_t size) { + std::array<uint8_t, kAssociatedDataLength> additional_data; + const std::array<uint8_t, 8>& size_array = + fido_parsing_utils::Uint64LittleEndian(size); + std::copy(kLargeBlobADPrefix.begin(), kLargeBlobADPrefix.end(), + additional_data.begin()); + std::copy(size_array.begin(), size_array.end(), + additional_data.begin() + kLargeBlobADPrefix.size()); + return additional_data; +} + } // namespace LargeBlobArrayFragment::LargeBlobArrayFragment(const std::vector<uint8_t> bytes, @@ -150,7 +167,8 @@ } auto nonce_it = map.find(cbor::Value(static_cast<int>(LargeBlobDataKeys::kNonce))); - if (nonce_it == map.end() || !nonce_it->second.is_bytestring()) { + if (nonce_it == map.end() || !nonce_it->second.is_bytestring() || + nonce_it->second.GetBytestring().size() != kLargeBlobArrayNonceLength) { return base::nullopt; } auto orig_size_it = @@ -159,21 +177,25 @@ return base::nullopt; } return LargeBlobData(ciphertext_it->second.GetBytestring(), - nonce_it->second.GetBytestring(), + base::make_span<kLargeBlobArrayNonceLength>( + nonce_it->second.GetBytestring()), orig_size_it->second.GetUnsigned()); } -LargeBlobData::LargeBlobData(std::vector<uint8_t> ciphertext, - std::vector<uint8_t> nonce, - int64_t orig_size) - : ciphertext_(std::move(ciphertext)), - nonce_(std::move(nonce)), - orig_size_(std::move(orig_size)) {} +LargeBlobData::LargeBlobData( + std::vector<uint8_t> ciphertext, + base::span<const uint8_t, kLargeBlobArrayNonceLength> nonce, + int64_t orig_size) + : ciphertext_(std::move(ciphertext)), orig_size_(std::move(orig_size)) { + std::copy(nonce.begin(), nonce.end(), nonce_.begin()); +} LargeBlobData::LargeBlobData(LargeBlobKey key, std::vector<uint8_t> blob) { - // TODO(nsatragno): implement encrypting the data. For now, just store and - // return the blob as plaintext. orig_size_ = blob.size(); - ciphertext_ = blob; + crypto::Aead aead(crypto::Aead::AeadAlgorithm::AES_256_GCM); + aead.Init(key); + crypto::RandBytes(nonce_); + ciphertext_ = + aead.Seal(blob, nonce_, GenerateLargeBlobAdditionalData(orig_size_)); } LargeBlobData::LargeBlobData(LargeBlobData&&) = default; LargeBlobData& LargeBlobData::operator=(LargeBlobData&&) = default; @@ -186,9 +208,10 @@ base::Optional<std::vector<uint8_t>> LargeBlobData::Decrypt( LargeBlobKey key) const { - // TODO(nsatragno): implement decrypting the data. For now, store and return - // the blob as plaintext. - return ciphertext_; + crypto::Aead aead(crypto::Aead::AeadAlgorithm::AES_256_GCM); + aead.Init(key); + return aead.Open(ciphertext_, nonce_, + GenerateLargeBlobAdditionalData(orig_size_)); } cbor::Value::MapValue LargeBlobData::AsCBOR() const {
diff --git a/device/fido/large_blob.h b/device/fido/large_blob.h index 3ed5036..d46789e9 100644 --- a/device/fido/large_blob.h +++ b/device/fido/large_blob.h
@@ -47,6 +47,7 @@ constexpr size_t kLargeBlobDefaultMaxFragmentLength = 960; constexpr size_t kLargeBlobReadEncodingOverhead = 64; +constexpr size_t kLargeBlobArrayNonceLength = 12; struct COMPONENT_EXPORT(DEVICE_FIDO) LargeBlobArrayFragment { LargeBlobArrayFragment(std::vector<uint8_t> bytes, size_t offset); @@ -130,10 +131,10 @@ private: LargeBlobData(std::vector<uint8_t> ciphertext, - std::vector<uint8_t> nonce, + base::span<const uint8_t, kLargeBlobArrayNonceLength> nonce, int64_t orig_size); std::vector<uint8_t> ciphertext_; - std::vector<uint8_t> nonce_; + std::array<uint8_t, kLargeBlobArrayNonceLength> nonce_; int64_t orig_size_; };
diff --git a/device/fido/large_blob_unittest.cc b/device/fido/large_blob_unittest.cc index 408f5fe2..7fda7ed 100644 --- a/device/fido/large_blob_unittest.cc +++ b/device/fido/large_blob_unittest.cc
@@ -27,10 +27,11 @@ // An "valid" CBOR large blob array with two entries. The first entry is not a // valid large blob map structure. The second entry is valid. -const std::array<uint8_t, 35> kValidLargeBlobArray = { +const std::array<uint8_t, 45> kValidLargeBlobArray = { 0x82, 0xA2, 0x02, 0x42, 0x11, 0x11, 0x03, 0x02, 0xA3, 0x01, 0x42, 0x22, - 0x22, 0x02, 0x42, 0x33, 0x33, 0x03, 0x02, 0x53, 0x5b, 0xaa, 0xd2, 0x7a, - 0x26, 0x68, 0x34, 0x9e, 0xc3, 0x90, 0xd3, 0x9a, 0x1c, 0x0a, 0xae}; + 0x22, 0x02, 0x4C, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x30, 0x31, 0x32, 0x03, 0x02, 0x9b, 0x33, 0x75, 0x6c, 0x0a, 0x84, 0xdf, + 0x32, 0xcc, 0xd0, 0xc8, 0x96, 0xea, 0xa7, 0x99, 0x13}; TEST_F(FidoLargeBlobTest, VerifyLargeBlobArrayIntegrityValid) { std::vector<uint8_t> large_blob_array = @@ -113,7 +114,7 @@ // Test popping the large blob array in a fragment size that evenly divides the // length of the array. TEST_F(FidoLargeBlobTest, LargeBlobArrayFragments_PopEvenly) { - const size_t fragment_size = 7; + const size_t fragment_size = 9; const size_t expected_fragments = kValidLargeBlobArray.size() / fragment_size; size_t fragments = 0; ASSERT_EQ(0u, kValidLargeBlobArray.size() % fragment_size);
diff --git a/device/fido/virtual_ctap2_device.cc b/device/fido/virtual_ctap2_device.cc index 61fbcb5..a04699a 100644 --- a/device/fido/virtual_ctap2_device.cc +++ b/device/fido/virtual_ctap2_device.cc
@@ -70,11 +70,6 @@ static_cast<uint8_t>(pin::Permissions::kCredentialManagement) | static_cast<uint8_t>(pin::Permissions::kBioEnrollment); -constexpr std::array<uint8_t, 4> Uint32LittleEndian(int64_t value) { - return {value & 0xFF, value >> 8 & 0xFF, value >> 16 & 0xFF, - value >> 24 & 0xFF}; -} - struct PinUvAuthTokenPermissions { uint8_t permissions; base::Optional<std::string> rp_id; @@ -2199,7 +2194,7 @@ if (offset_it == request_map.end() || !offset_it->second.is_unsigned()) { return CtapDeviceResponseCode::kCtap1ErrInvalidParameter; } - const size_t offset = offset_it->second.GetUnsigned(); + const uint64_t offset = offset_it->second.GetUnsigned(); const auto get_it = request_map.find( cbor::Value(static_cast<uint8_t>(LargeBlobsRequestKey::kGet))); @@ -2221,7 +2216,7 @@ if (length_it != request_map.end()) { return CtapDeviceResponseCode::kCtap1ErrInvalidParameter; } - const size_t get = get_it->second.GetUnsigned(); + const uint64_t get = get_it->second.GetUnsigned(); if (get > max_fragment_length) { return CtapDeviceResponseCode::kCtap1ErrInvalidLength; } @@ -2246,7 +2241,7 @@ if (length_it == request_map.end() || !length_it->second.is_unsigned()) { return CtapDeviceResponseCode::kCtap1ErrInvalidParameter; } - const size_t length = length_it->second.GetUnsigned(); + const uint64_t length = length_it->second.GetUnsigned(); if (length > config_.available_large_blob_storage) { return CtapDeviceResponseCode::kCtap2ErrLargeBlobStorageFull; } @@ -2302,7 +2297,7 @@ kPinUvAuthTokenSafetyPadding.begin(), kPinUvAuthTokenSafetyPadding.end()); pinauth_bytes.insert(pinauth_bytes.end(), {0x0c, 0x00}); - auto offset_vec = Uint32LittleEndian(offset); + auto offset_vec = fido_parsing_utils::Uint32LittleEndian(offset); pinauth_bytes.insert(pinauth_bytes.end(), offset_vec.begin(), offset_vec.end()); pinauth_bytes.insert(pinauth_bytes.end(), set.begin(), set.end());
diff --git a/gpu/command_buffer/service/feature_info.cc b/gpu/command_buffer/service/feature_info.cc index 422e1bd7..49c21672 100644 --- a/gpu/command_buffer/service/feature_info.cc +++ b/gpu/command_buffer/service/feature_info.cc
@@ -10,6 +10,7 @@ #include <vector> #include "base/command_line.h" +#include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" @@ -1856,23 +1857,37 @@ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, width, width, 0, GL_RGB, GL_FLOAT, nullptr); GLenum status_rgb = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER); + base::UmaHistogramBoolean("GPU.RenderableFormat.RGBA32F.FLOAT", + status_rgba == GL_FRAMEBUFFER_COMPLETE); + base::UmaHistogramBoolean("GPU.RenderableFormat.RGB32F.FLOAT", + status_rgb == GL_FRAMEBUFFER_COMPLETE); // For desktop systems, check to see if we support rendering to the full // range of formats supported by EXT_color_buffer_float if (status_rgba == GL_FRAMEBUFFER_COMPLETE && enable_es3) { bool full_float_support = true; - GLenum internal_formats[] = { + const GLenum kInternalFormats[] = { GL_R16F, GL_RG16F, GL_RGBA16F, GL_R32F, GL_RG32F, GL_R11F_G11F_B10F, }; - GLenum formats[] = { + const GLenum kFormats[] = { GL_RED, GL_RG, GL_RGBA, GL_RED, GL_RG, GL_RGB, }; - DCHECK_EQ(base::size(internal_formats), base::size(formats)); - for (size_t i = 0; i < base::size(formats); ++i) { - glTexImage2D(GL_TEXTURE_2D, 0, internal_formats[i], width, width, 0, - formats[i], GL_FLOAT, nullptr); - full_float_support &= glCheckFramebufferStatusEXT(GL_FRAMEBUFFER) == - GL_FRAMEBUFFER_COMPLETE; + const char* kInternalFormatHistogramNames[] = { + "GPU.RenderableFormat.R16F.FLOAT", + "GPU.RenderableFormat.RG16F.FLOAT", + "GPU.RenderableFormat.RGBA16F.FLOAT", + "GPU.RenderableFormat.R32F.FLOAT", + "GPU.RenderableFormat.RG32F.FLOAT", + "GPU.RenderableFormat.R11F_G11F_B10F.FLOAT", + }; + DCHECK_EQ(base::size(kInternalFormats), base::size(kFormats)); + for (size_t i = 0; i < base::size(kFormats); ++i) { + glTexImage2D(GL_TEXTURE_2D, 0, kInternalFormats[i], width, width, 0, + kFormats[i], GL_FLOAT, nullptr); + bool supported = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER) == + GL_FRAMEBUFFER_COMPLETE; + base::UmaHistogramBoolean(kInternalFormatHistogramNames[i], supported); + full_float_support &= supported; } enable_ext_color_buffer_float = full_float_support; } @@ -1890,9 +1905,11 @@ GLenum data_type = GL_HALF_FLOAT; glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, width, 0, format, data_type, nullptr); - enable_ext_color_buffer_half_float = - (glCheckFramebufferStatusEXT(GL_FRAMEBUFFER) == - GL_FRAMEBUFFER_COMPLETE); + bool supported = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER) == + GL_FRAMEBUFFER_COMPLETE; + base::UmaHistogramBoolean("GPU.RenderableFormat.RGBA16F.HALF_FLOAT", + supported); + enable_ext_color_buffer_half_float = supported; } glDeleteFramebuffersEXT(1, &fb_id);
diff --git a/gpu/command_buffer/service/gpu_fence_manager.cc b/gpu/command_buffer/service/gpu_fence_manager.cc index c7e11638..b70675a 100644 --- a/gpu/command_buffer/service/gpu_fence_manager.cc +++ b/gpu/command_buffer/service/gpu_fence_manager.cc
@@ -40,9 +40,8 @@ return true; } -bool GpuFenceManager::CreateGpuFenceFromHandle( - uint32_t client_id, - const gfx::GpuFenceHandle& handle) { +bool GpuFenceManager::CreateGpuFenceFromHandle(uint32_t client_id, + gfx::GpuFenceHandle handle) { // The handle must be valid. The fallback kEmpty type cannot be duplicated. if (handle.is_null()) return false; @@ -52,7 +51,7 @@ if (it != gpu_fence_entries_.end()) return false; - gfx::GpuFence gpu_fence(handle); + gfx::GpuFence gpu_fence(std::move(handle)); auto entry = std::make_unique<GpuFenceEntry>(); entry->gl_fence_ = gl::GLFence::CreateFromGpuFence(gpu_fence); if (!entry->gl_fence_)
diff --git a/gpu/command_buffer/service/gpu_fence_manager.h b/gpu/command_buffer/service/gpu_fence_manager.h index c6e03a8..ee50d6f 100644 --- a/gpu/command_buffer/service/gpu_fence_manager.h +++ b/gpu/command_buffer/service/gpu_fence_manager.h
@@ -48,8 +48,7 @@ bool CreateGpuFence(uint32_t client_id); - bool CreateGpuFenceFromHandle(uint32_t client_id, - const gfx::GpuFenceHandle& handle); + bool CreateGpuFenceFromHandle(uint32_t client_id, gfx::GpuFenceHandle handle); bool IsValidGpuFence(uint32_t client_id);
diff --git a/gpu/command_buffer/service/gpu_fence_manager_unittest.cc b/gpu/command_buffer/service/gpu_fence_manager_unittest.cc index cc96195..1ddd461 100644 --- a/gpu/command_buffer/service/gpu_fence_manager_unittest.cc +++ b/gpu/command_buffer/service/gpu_fence_manager_unittest.cc
@@ -172,10 +172,10 @@ .RetiresOnSaturation(); std::unique_ptr<gfx::GpuFence> gpu_fence = manager_->GetGpuFence(kClient1Id); EXPECT_TRUE(gpu_fence); - gfx::GpuFenceHandle handle = gpu_fence->GetGpuFenceHandle(); + const gfx::GpuFenceHandle& handle = gpu_fence->GetGpuFenceHandle(); EXPECT_EQ(handle.type, gfx::GpuFenceHandleType::kAndroidNativeFenceSync); - EXPECT_EQ(handle.native_fd.fd, kFenceFD); + EXPECT_EQ(handle.owned_fd.get(), kFenceFD); // Removing the fence marks it invalid. EXPECT_CALL(*egl_, DestroySyncKHR(_, kDummySync)) @@ -195,7 +195,7 @@ // Create a handle. gfx::GpuFenceHandle handle; handle.type = gfx::GpuFenceHandleType::kAndroidNativeFenceSync; - handle.native_fd = base::FileDescriptor(kFenceFD, true); + handle.owned_fd = base::ScopedFD(kFenceFD); // Create a duplicate fence object from it. EXPECT_CALL(*egl_, CreateSyncKHR(_, EGL_SYNC_NATIVE_FENCE_ANDROID, _)) @@ -203,7 +203,8 @@ .WillOnce(Return(kDummySync)) .RetiresOnSaturation(); EXPECT_CALL(*gl_, Flush()).Times(1).RetiresOnSaturation(); - EXPECT_TRUE(manager_->CreateGpuFenceFromHandle(kClient1Id, handle)); + EXPECT_TRUE( + manager_->CreateGpuFenceFromHandle(kClient1Id, std::move(handle))); EXPECT_TRUE(manager_->IsValidGpuFence(kClient1Id)); // Try a server wait on it.
diff --git a/gpu/command_buffer/service/shared_image_factory.cc b/gpu/command_buffer/service/shared_image_factory.cc index 45f7a5c..6b42ed63 100644 --- a/gpu/command_buffer/service/shared_image_factory.cc +++ b/gpu/command_buffer/service/shared_image_factory.cc
@@ -362,9 +362,10 @@ VkDevice device = vulkan_context_provider_->GetDeviceQueue()->GetVulkanDevice(); DCHECK(device != VK_NULL_HANDLE); - it->second = vulkan_context_provider_->GetVulkanImplementation() - ->RegisterSysmemBufferCollection( - device, id, std::move(token), format, usage); + it->second = + vulkan_context_provider_->GetVulkanImplementation() + ->RegisterSysmemBufferCollection(device, id, std::move(token), format, + usage, gfx::Size(), 0); return true; }
diff --git a/gpu/ipc/client/command_buffer_proxy_impl.cc b/gpu/ipc/client/command_buffer_proxy_impl.cc index d09ac5e..6dcd1c9 100644 --- a/gpu/ipc/client/command_buffer_proxy_impl.cc +++ b/gpu/ipc/client/command_buffer_proxy_impl.cc
@@ -579,9 +579,11 @@ return; } + // IPC accepts handles by const reference. However, on platforms where the + // handle is backed by base::ScopedFD, const is casted away and the handle is + // forcibly taken from you. gfx::GpuFence* gpu_fence = gfx::GpuFence::FromClientGpuFence(source); - gfx::GpuFenceHandle handle = - gfx::CloneHandleForIPC(gpu_fence->GetGpuFenceHandle()); + gfx::GpuFenceHandle handle = gpu_fence->GetGpuFenceHandle().Clone(); Send(new GpuCommandBufferMsg_CreateGpuFenceFromHandle(route_id_, gpu_fence_id, handle)); } @@ -607,9 +609,9 @@ void CommandBufferProxyImpl::OnGetGpuFenceHandleComplete( uint32_t gpu_fence_id, - const gfx::GpuFenceHandle& handle) { + gfx::GpuFenceHandle handle) { // Always consume the provided handle to avoid leaks on error. - auto gpu_fence = std::make_unique<gfx::GpuFence>(handle); + auto gpu_fence = std::make_unique<gfx::GpuFence>(std::move(handle)); GetGpuFenceTaskMap::iterator it = get_gpu_fence_tasks_.find(gpu_fence_id); if (it == get_gpu_fence_tasks_.end()) {
diff --git a/gpu/ipc/client/command_buffer_proxy_impl.h b/gpu/ipc/client/command_buffer_proxy_impl.h index d7d9ca7..22443973 100644 --- a/gpu/ipc/client/command_buffer_proxy_impl.h +++ b/gpu/ipc/client/command_buffer_proxy_impl.h
@@ -188,8 +188,7 @@ void OnSwapBuffersCompleted(const SwapBuffersCompleteParams& params); void OnBufferPresented(uint64_t swap_id, const gfx::PresentationFeedback& feedback); - void OnGetGpuFenceHandleComplete(uint32_t gpu_fence_id, - const gfx::GpuFenceHandle&); + void OnGetGpuFenceHandleComplete(uint32_t gpu_fence_id, gfx::GpuFenceHandle); void OnReturnData(const std::vector<uint8_t>& data); // Try to read an updated copy of the state from shared memory, and calls
diff --git a/gpu/ipc/client/shared_image_interface_proxy.cc b/gpu/ipc/client/shared_image_interface_proxy.cc index 061026e7..b27061ea 100644 --- a/gpu/ipc/client/shared_image_interface_proxy.cc +++ b/gpu/ipc/client/shared_image_interface_proxy.cc
@@ -243,10 +243,13 @@ GenerateDependenciesFromSyncToken(std::move(sync_token), host_); { base::AutoLock lock(lock_); + + // IPC accepts handles by const reference. However, on platforms where the + // handle is backed by base::ScopedFD, const is casted away and the handle + // is forcibly taken from you. gfx::GpuFenceHandle acquire_fence_handle; if (acquire_fence) { - acquire_fence_handle = - gfx::CloneHandleForIPC(acquire_fence->GetGpuFenceHandle()); + acquire_fence_handle = acquire_fence->GetGpuFenceHandle().Clone(); // TODO(dcastagna): This message will be wrapped, handles can't be passed // in inner messages. Use EnqueueDeferredMessage if it will be possible to // have handles in inner messages in the future.
diff --git a/gpu/ipc/common/android/android_image_reader_utils.cc b/gpu/ipc/common/android/android_image_reader_utils.cc index e2d7b4ea..ab2ba24 100644 --- a/gpu/ipc/common/android/android_image_reader_utils.cc +++ b/gpu/ipc/common/android/android_image_reader_utils.cc
@@ -25,13 +25,12 @@ LOG(ERROR) << "Unable to get a gpu fence object."; return base::ScopedFD(); } - gfx::GpuFenceHandle fence_handle = - gfx::CloneHandleForIPC(gpu_fence->GetGpuFenceHandle()); + gfx::GpuFenceHandle fence_handle = gpu_fence->GetGpuFenceHandle().Clone(); if (fence_handle.is_null()) { LOG(ERROR) << "Gpu fence handle is null"; return base::ScopedFD(); } - return base::ScopedFD(fence_handle.native_fd.fd); + return std::move(fence_handle.owned_fd); } bool DeleteAImageAsync(AImage* image,
diff --git a/gpu/ipc/in_process_command_buffer.cc b/gpu/ipc/in_process_command_buffer.cc index 2e1d047..43659617 100644 --- a/gpu/ipc/in_process_command_buffer.cc +++ b/gpu/ipc/in_process_command_buffer.cc
@@ -1287,17 +1287,17 @@ // Pass a cloned handle to the GPU process since the source ClientGpuFence // may go out of scope before the queued task runs. gfx::GpuFence* gpu_fence = gfx::GpuFence::FromClientGpuFence(source); - gfx::GpuFenceHandle handle = - gfx::CloneHandleForIPC(gpu_fence->GetGpuFenceHandle()); + gfx::GpuFenceHandle handle = gpu_fence->GetGpuFenceHandle().Clone(); - ScheduleGpuTask(base::BindOnce( - &InProcessCommandBuffer::CreateGpuFenceOnGpuThread, - gpu_thread_weak_ptr_factory_.GetWeakPtr(), gpu_fence_id, handle)); + ScheduleGpuTask( + base::BindOnce(&InProcessCommandBuffer::CreateGpuFenceOnGpuThread, + gpu_thread_weak_ptr_factory_.GetWeakPtr(), gpu_fence_id, + std::move(handle))); } void InProcessCommandBuffer::CreateGpuFenceOnGpuThread( uint32_t gpu_fence_id, - const gfx::GpuFenceHandle& handle) { + gfx::GpuFenceHandle handle) { DCHECK_CALLED_ON_VALID_SEQUENCE(gpu_sequence_checker_); UpdateActiveUrl(); @@ -1310,7 +1310,8 @@ gles2::GpuFenceManager* gpu_fence_manager = decoder_->GetGpuFenceManager(); DCHECK(gpu_fence_manager); - if (gpu_fence_manager->CreateGpuFenceFromHandle(gpu_fence_id, handle)) + if (gpu_fence_manager->CreateGpuFenceFromHandle(gpu_fence_id, + std::move(handle))) return; // The insertion failed. This shouldn't happen, force context loss to avoid
diff --git a/gpu/ipc/in_process_command_buffer.h b/gpu/ipc/in_process_command_buffer.h index 815ca67..8910250b 100644 --- a/gpu/ipc/in_process_command_buffer.h +++ b/gpu/ipc/in_process_command_buffer.h
@@ -323,7 +323,7 @@ void SetGetBufferOnGpuThread(int32_t shm_id, base::WaitableEvent* completion); void CreateGpuFenceOnGpuThread(uint32_t gpu_fence_id, - const gfx::GpuFenceHandle& handle); + gfx::GpuFenceHandle handle); void GetGpuFenceOnGpuThread( uint32_t gpu_fence_id, base::OnceCallback<void(std::unique_ptr<gfx::GpuFence>)> callback);
diff --git a/gpu/ipc/service/gles2_command_buffer_stub.cc b/gpu/ipc/service/gles2_command_buffer_stub.cc index 93b0e93f..9ccf7ef 100644 --- a/gpu/ipc/service/gles2_command_buffer_stub.cc +++ b/gpu/ipc/service/gles2_command_buffer_stub.cc
@@ -473,7 +473,7 @@ void GLES2CommandBufferStub::OnCreateGpuFenceFromHandle( uint32_t gpu_fence_id, - const gfx::GpuFenceHandle& handle) { + gfx::GpuFenceHandle handle) { if (!context_group_->feature_info()->feature_flags().chromium_gpu_fence) { DLOG(ERROR) << "CHROMIUM_gpu_fence unavailable"; command_buffer_->SetParseError(error::kLostContext); @@ -481,7 +481,7 @@ } if (gles2_decoder_->GetGpuFenceManager()->CreateGpuFenceFromHandle( - gpu_fence_id, handle)) + gpu_fence_id, std::move(handle))) return; // The insertion failed. This shouldn't happen, force context loss to avoid @@ -502,7 +502,7 @@ if (manager->IsValidGpuFence(gpu_fence_id)) { std::unique_ptr<gfx::GpuFence> gpu_fence = manager->GetGpuFence(gpu_fence_id); - handle = gfx::CloneHandleForIPC(gpu_fence->GetGpuFenceHandle()); + handle = gpu_fence->GetGpuFenceHandle().Clone(); } else { // Retrieval failed. This shouldn't happen, force context loss to avoid // inconsistent state. @@ -510,6 +510,10 @@ command_buffer_->SetParseError(error::kLostContext); CheckContextLost(); } + + // IPC accepts handles by const reference. However, on platforms where the + // handle is backed by base::ScopedFD, const is casted away and the handle is + // forcibly taken from you. Send(new GpuCommandBufferMsg_GetGpuFenceHandleComplete(route_id_, gpu_fence_id, handle)); }
diff --git a/gpu/ipc/service/gles2_command_buffer_stub.h b/gpu/ipc/service/gles2_command_buffer_stub.h index 953919d..fbcde76 100644 --- a/gpu/ipc/service/gles2_command_buffer_stub.h +++ b/gpu/ipc/service/gles2_command_buffer_stub.h
@@ -60,7 +60,7 @@ void OnTakeFrontBuffer(const Mailbox& mailbox); void OnReturnFrontBuffer(const Mailbox& mailbox, bool is_lost); void OnCreateGpuFenceFromHandle(uint32_t gpu_fence_id, - const gfx::GpuFenceHandle& handle); + gfx::GpuFenceHandle handle); void OnGetGpuFenceHandle(uint32_t gpu_fence_id); void OnCreateImage(GpuCommandBufferMsg_CreateImage_Params params); void OnDestroyImage(int32_t id);
diff --git a/gpu/ipc/service/shared_image_stub.cc b/gpu/ipc/service/shared_image_stub.cc index ac0bf33..5da9d6a 100644 --- a/gpu/ipc/service/shared_image_stub.cc +++ b/gpu/ipc/service/shared_image_stub.cc
@@ -6,6 +6,8 @@ #include <inttypes.h> +#include <memory> + #include "base/memory/ptr_util.h" #include "base/trace_event/memory_dump_manager.h" #include "base/trace_event/trace_event.h" @@ -128,13 +130,12 @@ return true; } -bool SharedImageStub::UpdateSharedImage( - const Mailbox& mailbox, - const gfx::GpuFenceHandle& in_fence_handle) { +bool SharedImageStub::UpdateSharedImage(const Mailbox& mailbox, + gfx::GpuFenceHandle in_fence_handle) { TRACE_EVENT0("gpu", "SharedImageStub::UpdateSharedImage"); std::unique_ptr<gfx::GpuFence> in_fence; if (!in_fence_handle.is_null()) - in_fence.reset(new gfx::GpuFence(in_fence_handle)); + in_fence = std::make_unique<gfx::GpuFence>(std::move(in_fence_handle)); if (!mailbox.IsSharedImage()) { LOG(ERROR) << "SharedImageStub: Trying to access a SharedImage with a " "non-SharedImage mailbox."; @@ -291,13 +292,12 @@ sync_point_client_state_->ReleaseFenceSync(params.release_id); } -void SharedImageStub::OnUpdateSharedImage( - const Mailbox& mailbox, - uint32_t release_id, - const gfx::GpuFenceHandle& in_fence_handle) { +void SharedImageStub::OnUpdateSharedImage(const Mailbox& mailbox, + uint32_t release_id, + gfx::GpuFenceHandle in_fence_handle) { TRACE_EVENT0("gpu", "SharedImageStub::OnUpdateSharedImage"); - if (!UpdateSharedImage(mailbox, in_fence_handle)) + if (!UpdateSharedImage(mailbox, std::move(in_fence_handle))) return; SyncToken sync_token(sync_point_client_state_->namespace_id(),
diff --git a/gpu/ipc/service/shared_image_stub.h b/gpu/ipc/service/shared_image_stub.h index eb8102d..b03a9d56 100644 --- a/gpu/ipc/service/shared_image_stub.h +++ b/gpu/ipc/service/shared_image_stub.h
@@ -75,7 +75,7 @@ #endif bool UpdateSharedImage(const Mailbox& mailbox, - const gfx::GpuFenceHandle& in_fence_handle); + gfx::GpuFenceHandle in_fence_handle); private: SharedImageStub(GpuChannel* channel, int32_t route_id); @@ -87,7 +87,7 @@ void OnCreateGMBSharedImage(GpuChannelMsg_CreateGMBSharedImage_Params params); void OnUpdateSharedImage(const Mailbox& mailbox, uint32_t release_id, - const gfx::GpuFenceHandle& in_fence_handle); + gfx::GpuFenceHandle in_fence_handle); #if defined(OS_ANDROID) void OnCreateSharedImageWithAHB(const Mailbox& out_mailbox, const Mailbox& in_mailbox,
diff --git a/gpu/vulkan/vulkan_implementation.h b/gpu/vulkan/vulkan_implementation.h index 42380494..96f13b7 100644 --- a/gpu/vulkan/vulkan_implementation.h +++ b/gpu/vulkan/vulkan_implementation.h
@@ -136,7 +136,9 @@ gfx::SysmemBufferCollectionId id, zx::channel token, gfx::BufferFormat format, - gfx::BufferUsage usage) = 0; + gfx::BufferUsage usage, + gfx::Size size, + size_t min_buffer_count) = 0; #endif // defined(OS_FUCHSIA) bool use_swiftshader() const { return use_swiftshader_; }
diff --git a/headless/lib/headless_web_contents_browsertest.cc b/headless/lib/headless_web_contents_browsertest.cc index 3464057..59fcd9c 100644 --- a/headless/lib/headless_web_contents_browsertest.cc +++ b/headless/lib/headless_web_contents_browsertest.cc
@@ -1163,7 +1163,10 @@ <html> <body> <script> -window.open('/page2.html'); +const win = window.open('/page2.html'); +if (!win) + console.error('ready'); +win.addEventListener('load', () => console.log('ready')); </script> </body> </html> @@ -1178,18 +1181,18 @@ )"; } // namespace -class WebContentsOpenTest : public page::Observer, +class WebContentsOpenTest : public runtime::Observer, public HeadlessAsyncDevTooledBrowserTest { public: void RunDevTooledTest() override { - devtools_client_->GetPage()->AddObserver(this); interceptor_->InsertResponse("http://foo.com/index.html", {kPageWhichOpensAWindow, "text/html"}); interceptor_->InsertResponse("http://foo.com/page2.html", {kPage2, "text/html"}); base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed); - devtools_client_->GetPage()->Enable(run_loop.QuitClosure()); + devtools_client_->GetRuntime()->AddObserver(this); + devtools_client_->GetRuntime()->Enable(run_loop.QuitClosure()); run_loop.Run(); devtools_client_->GetPage()->Navigate("http://foo.com/index.html"); @@ -1203,7 +1206,8 @@ builder.SetBlockNewWebContents(false); } - void OnLoadEventFired(const page::LoadEventFiredParams&) override { + void OnConsoleAPICalled( + const runtime::ConsoleAPICalledParams& params) override { EXPECT_THAT( interceptor_->urls_requested(), ElementsAre("http://foo.com/index.html", "http://foo.com/page2.html")); @@ -1211,10 +1215,7 @@ } }; -// TODO(crbug.com/1045980): Disabled due to flakiness. -// TODO(crbug.com/1078405): Disabled due to flakiness. -// TODO(crbug.com/1090936): Disabled due to flakiness. -DISABLED_HEADLESS_ASYNC_DEVTOOLED_TEST_F(DontBlockWebContentsOpenTest); +HEADLESS_ASYNC_DEVTOOLED_TEST_F(DontBlockWebContentsOpenTest); class BlockWebContentsOpenTest : public WebContentsOpenTest { public: @@ -1223,18 +1224,14 @@ builder.SetBlockNewWebContents(true); } - void OnLoadEventFired(const page::LoadEventFiredParams&) override { + void OnConsoleAPICalled( + const runtime::ConsoleAPICalledParams& params) override { EXPECT_THAT(interceptor_->urls_requested(), ElementsAre("http://foo.com/index.html")); FinishAsynchronousTest(); } }; -#if defined(OS_WIN) -// TODO(crbug.com/1045980): Disabled due to flakiness. -DISABLED_HEADLESS_ASYNC_DEVTOOLED_TEST_F(BlockWebContentsOpenTest); -#else HEADLESS_ASYNC_DEVTOOLED_TEST_F(BlockWebContentsOpenTest); -#endif } // namespace headless
diff --git a/infra/config/generated/commit-queue.cfg b/infra/config/generated/commit-queue.cfg index 9ce2635..d0dec20 100644 --- a/infra/config/generated/commit-queue.cfg +++ b/infra/config/generated/commit-queue.cfg
@@ -279,7 +279,7 @@ } builders { name: "chromium/try/android-pie-arm64-rel" - experiment_percentage: 20 + experiment_percentage: 40 location_regexp: ".*" location_regexp_exclude: ".+/[+]/docs/.+" location_regexp_exclude: ".+/[+]/infra/config/.+"
diff --git a/infra/config/generated/cq-builders.md b/infra/config/generated/cq-builders.md index c74f08c5..ea42218 100644 --- a/infra/config/generated/cq-builders.md +++ b/infra/config/generated/cq-builders.md
@@ -351,7 +351,7 @@ * Experiment percentage: 5 * [android-pie-arm64-rel](https://ci.chromium.org/p/chromium/builders/try/android-pie-arm64-rel) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+android-pie-arm64-rel)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+android-pie-arm64-rel)) - * Experiment percentage: 20 + * Experiment percentage: 40 * [fuchsia-compile-x64-dbg](https://ci.chromium.org/p/chromium/builders/try/fuchsia-compile-x64-dbg) ([definition](https://cs.chromium.org/search?q=package:%5Echromium$+file:/cq.star$+-file:/beta/+-file:/stable/+fuchsia-compile-x64-dbg)) ([matching builders](https://cs.chromium.org/search?q=+file:trybots.py+fuchsia-compile-x64-dbg)) * Experiment percentage: 50
diff --git a/infra/config/subprojects/chromium/try.star b/infra/config/subprojects/chromium/try.star index 3bce43bf..73921db 100644 --- a/infra/config/subprojects/chromium/try.star +++ b/infra/config/subprojects/chromium/try.star
@@ -324,7 +324,7 @@ # TODO(crbug.com/1111436): Enable on CQ fully once the tests run fine. main_list_view = settings.main_list_view_name, tryjob = try_.job( - experiment_percentage = 20, + experiment_percentage = 40, ), )
diff --git a/ios/chrome/browser/autofill/form_suggestion_controller.mm b/ios/chrome/browser/autofill/form_suggestion_controller.mm index 5bf2f77..e6a36bb 100644 --- a/ios/chrome/browser/autofill/form_suggestion_controller.mm +++ b/ios/chrome/browser/autofill/form_suggestion_controller.mm
@@ -194,9 +194,9 @@ FormSuggestionProviderQuery* formQuery = [[FormSuggestionProviderQuery alloc] initWithFormName:base::SysUTF8ToNSString(params.form_name) - uniqueFormID:FormRendererId(params.unique_form_id) + uniqueFormID:params.unique_form_id fieldIdentifier:base::SysUTF8ToNSString(params.field_identifier) - uniqueFieldID:FieldRendererId(params.unique_field_id) + uniqueFieldID:params.unique_field_id fieldType:base::SysUTF8ToNSString(params.field_type) type:base::SysUTF8ToNSString(params.type) typedValue:base::SysUTF8ToNSString( @@ -346,9 +346,8 @@ (FormSuggestionsReadyCompletion)accessoryViewUpdateBlock { [self processPage:webState]; _suggestionState.reset(new AutofillSuggestionState( - params.form_name, FormRendererId(params.unique_form_id), - params.field_identifier, FieldRendererId(params.unique_field_id), - params.frame_id, params.value)); + params.form_name, params.unique_form_id, params.field_identifier, + params.unique_field_id, params.frame_id, params.value)); _accessoryViewUpdateBlock = [accessoryViewUpdateBlock copy]; [self retrieveSuggestionsForForm:params webState:webState]; }
diff --git a/ios/chrome/browser/passwords/password_controller_unittest.mm b/ios/chrome/browser/passwords/password_controller_unittest.mm index 5d61e4d..1e04b18d 100644 --- a/ios/chrome/browser/passwords/password_controller_unittest.mm +++ b/ios/chrome/browser/passwords/password_controller_unittest.mm
@@ -1964,7 +1964,7 @@ WebFrame* frame = web::GetWebFrameWithId(web_state(), mainFrameID); FormActivityParams params; params.type = "password_form_removed"; - params.unique_form_id = 0; + params.unique_form_id = FormRendererId(0); params.frame_id = mainFrameID; [passwordController_.sharedPasswordController webState:web_state() @@ -2003,7 +2003,7 @@ WebFrame* frame = web::GetWebFrameWithId(web_state(), mainFrameID); FormActivityParams params; params.type = "password_form_removed"; - params.unique_form_id = 0; + params.unique_form_id = FormRendererId(0); params.frame_id = mainFrameID; [passwordController_.sharedPasswordController webState:web_state()
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller_egtest.mm b/ios/chrome/browser/ui/browser_view/browser_view_controller_egtest.mm index 1a3e7d1..0b0db63 100644 --- a/ios/chrome/browser/ui/browser_view/browser_view_controller_egtest.mm +++ b/ios/chrome/browser/ui/browser_view/browser_view_controller_egtest.mm
@@ -5,6 +5,7 @@ #include <map> #include "base/feature_list.h" +#import "base/ios/ios_util.h" #include "base/strings/sys_string_conversions.h" #include "components/strings/grit/components_strings.h" #include "ios/chrome/grit/ios_strings.h" @@ -33,6 +34,10 @@ // Tests that the NTP is interactable even when multiple NTP are opened during // the animation of the first NTP opening. See crbug.com/1032544. - (void)testPageInteractable { + // TODO(crbug.com/1129588): Test disabled in iOS14. + if (base::ios::IsRunningOnIOS14OrLater()) { + EARL_GREY_TEST_DISABLED(@"Fails on iOS14."); + } // Scope for the synchronization disabled. { ScopedSynchronizationDisabler syncDisabler;
diff --git a/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm b/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm index 8c80978..9c075a1 100644 --- a/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm +++ b/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
@@ -153,6 +153,10 @@ // Tests that the collections shortcut are displayed and working. - (void)testCollectionShortcuts { + // TODO(crbug.com/1129588): Test disabled in iOS14. + if (base::ios::IsRunningOnIOS14OrLater()) { + EARL_GREY_TEST_DISABLED(@"Fails on iOS14."); + } // Check the Bookmarks. [[EarlGrey selectElementWithMatcher:chrome_test_util::ButtonWithAccessibilityLabelId(
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm b/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm index add1e4d..a1ddbc2 100644 --- a/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm +++ b/ios/chrome/browser/ui/reading_list/reading_list_egtest.mm
@@ -1008,6 +1008,10 @@ // Tests that the VC can be dismissed by swiping down. - (void)testSwipeDownDismiss { + // TODO(crbug.com/1129589): Test disabled on iOS14 iPhones. + if (base::ios::IsRunningOnIOS14OrLater() && ![ChromeEarlGrey isIPadIdiom]) { + EARL_GREY_TEST_DISABLED(@"Fails on iOS14 iPhones."); + } if (!base::ios::IsRunningOnOrLater(13, 0, 0)) { EARL_GREY_TEST_SKIPPED(@"Test disabled on iOS 12 and lower."); }
diff --git a/ios/chrome/browser/ui/recent_tabs/recent_tabs_egtest.mm b/ios/chrome/browser/ui/recent_tabs/recent_tabs_egtest.mm index 77f4bc1b..f2b291b 100644 --- a/ios/chrome/browser/ui/recent_tabs/recent_tabs_egtest.mm +++ b/ios/chrome/browser/ui/recent_tabs/recent_tabs_egtest.mm
@@ -207,6 +207,10 @@ // Tests that the VC can be dismissed by swiping down. - (void)testSwipeDownDismiss { + // TODO(crbug.com/1129589): Test disabled on iOS14 iPhones. + if (base::ios::IsRunningOnIOS14OrLater() && ![ChromeEarlGrey isIPadIdiom]) { + EARL_GREY_TEST_DISABLED(@"Fails on iOS14 iPhones."); + } if (!base::ios::IsRunningOnOrLater(13, 0, 0)) { EARL_GREY_TEST_SKIPPED(@"Test disabled on iOS 12 and lower."); }
diff --git a/ios/web_view/internal/autofill/cwv_autofill_controller.mm b/ios/web_view/internal/autofill/cwv_autofill_controller.mm index d1b29dc5..edaf4c70 100644 --- a/ios/web_view/internal/autofill/cwv_autofill_controller.mm +++ b/ios/web_view/internal/autofill/cwv_autofill_controller.mm
@@ -477,10 +477,10 @@ DCHECK_EQ(_webState, webState); NSString* nsFormName = base::SysUTF8ToNSString(params.form_name); - _lastFormActivityUniqueFormID = FormRendererId(params.unique_form_id); + _lastFormActivityUniqueFormID = params.unique_form_id; NSString* nsFieldIdentifier = base::SysUTF8ToNSString(params.field_identifier); - _lastFormActivityUniqueFieldID = FieldRendererId(params.unique_field_id); + _lastFormActivityUniqueFieldID = params.unique_field_id; NSString* nsFieldType = base::SysUTF8ToNSString(params.field_type); NSString* nsFrameID = base::SysUTF8ToNSString(GetWebFrameId(frame)); NSString* nsValue = base::SysUTF8ToNSString(params.value);
diff --git a/ios/web_view/internal/autofill/cwv_autofill_controller_unittest.mm b/ios/web_view/internal/autofill/cwv_autofill_controller_unittest.mm index 7b2eea47..26ff24a 100644 --- a/ios/web_view/internal/autofill/cwv_autofill_controller_unittest.mm +++ b/ios/web_view/internal/autofill/cwv_autofill_controller_unittest.mm
@@ -55,6 +55,8 @@ #error "This file requires ARC support." #endif +using autofill::FormRendererId; +using autofill::FieldRendererId; using base::test::ios::kWaitForActionTimeout; using base::test::ios::WaitUntilConditionOrTimeout; @@ -64,9 +66,9 @@ const char kApplicationLocale[] = "en-US"; NSString* const kTestFormName = @"FormName"; -uint32_t kTestUniqueFormID = 0; +FormRendererId kTestUniqueFormID = FormRendererId(0); NSString* const kTestFieldIdentifier = @"FieldIdentifier"; -uint32_t kTestUniqueFieldID = 1; +FieldRendererId kTestUniqueFieldID = FieldRendererId(1); NSString* const kTestFieldValue = @"FieldValue"; NSString* const kTestDisplayDescription = @"DisplayDescription";
diff --git a/media/cdm/cdm_paths_unittest.cc b/media/cdm/cdm_paths_unittest.cc index 02eca59..ca554e2 100644 --- a/media/cdm/cdm_paths_unittest.cc +++ b/media/cdm/cdm_paths_unittest.cc
@@ -12,11 +12,12 @@ #include "testing/gtest/include/gtest/gtest.h" // Only verify platform specific path on some platforms. +// Note: The condition list here must be consistent with condition on +// "cdm_platform_specific_path" in cdm_paths.gni. // TODO(crbug.com/971433). Move the CDMs out of the install directory on // ChromeOS. -#if (defined(OS_MAC) || defined(OS_WIN) || \ - (defined(OS_LINUX) && !defined(OS_CHROMEOS))) && \ - (defined(ARCH_CPU_X86) || defined(ARCH_CPU_X86_64)) +#if (defined(OS_MAC) || defined(OS_WIN) || \ + (defined(OS_LINUX) && !defined(OS_CHROMEOS))) #define CDM_USE_PLATFORM_SPECIFIC_PATH #endif
diff --git a/media/cdm/library_cdm/cdm_paths.gni b/media/cdm/library_cdm/cdm_paths.gni index dc4ca4f..9329175e 100644 --- a/media/cdm/library_cdm/cdm_paths.gni +++ b/media/cdm/library_cdm/cdm_paths.gni
@@ -25,7 +25,10 @@ # Enable platform specific paths. This is required when the CDMs are Chrome # components, but is optional for other platforms. -# Note: |cdm_platform_specific_path| is exported as a BUILDFLAG to cdm_paths.cc. +# Note: +# - |cdm_platform_specific_path| is exported as a BUILDFLAG to cdm_paths.cc. +# - When updating the condition here, also update the condition on the define +# of CDM_USE_PLATFORM_SPECIFIC_PATH in cdm_paths_unittest.cc. if (is_win || is_mac || is_desktop_linux) { cdm_platform_specific_path = "_platform_specific/$component_os" + "_" + "$component_arch"
diff --git a/media/gpu/vaapi/vaapi_video_decode_accelerator.cc b/media/gpu/vaapi/vaapi_video_decode_accelerator.cc index e1c507a..223e193 100644 --- a/media/gpu/vaapi/vaapi_video_decode_accelerator.cc +++ b/media/gpu/vaapi/vaapi_video_decode_accelerator.cc
@@ -491,6 +491,11 @@ required_num_of_pictures = std::max(kMinNumOfPics, required_num_of_pictures); } + + // Notify |decoder_delegate_| of an imminent VAContextID destruction, so + // it can destroy any internal structures making use of it. + decoder_delegate_->OnVAContextDestructionSoon(); + task_runner_->PostTask( FROM_HERE, base::BindOnce( @@ -605,9 +610,8 @@ // All surfaces released, destroy them and dismiss all PictureBuffers. awaiting_va_surfaces_recycle_ = false; - VideoCodecProfile new_profile = decoder_->GetProfile(); + const VideoCodecProfile new_profile = decoder_->GetProfile(); if (profile_ != new_profile) { - DCHECK(decoder_delegate_); profile_ = new_profile; auto new_vaapi_wrapper = VaapiWrapper::CreateForVideoCodec( VaapiWrapper::kDecode, profile_, @@ -1060,6 +1064,10 @@ if (buffer_allocation_mode_ != BufferAllocationMode::kNone) available_va_surfaces_.clear(); + // Notify |decoder_delegate_| of an imminent VAContextID destruction, so it + // can destroy any internal structures making use of it. At this point + // |decoder_thread_| is stopped so we can access these from |task_runner_|. + decoder_delegate_->OnVAContextDestructionSoon(); vaapi_wrapper_->DestroyContext(); if (vpp_vaapi_wrapper_)
diff --git a/media/gpu/vaapi/vaapi_video_decode_accelerator_unittest.cc b/media/gpu/vaapi/vaapi_video_decode_accelerator_unittest.cc index b2e67b96..447185c 100644 --- a/media/gpu/vaapi/vaapi_video_decode_accelerator_unittest.cc +++ b/media/gpu/vaapi/vaapi_video_decode_accelerator_unittest.cc
@@ -12,6 +12,7 @@ #include "media/gpu/accelerated_video_decoder.h" #include "media/gpu/vaapi/vaapi_picture.h" #include "media/gpu/vaapi/vaapi_picture_factory.h" +#include "media/gpu/vaapi/vaapi_video_decoder_delegate.h" #include "media/gpu/vaapi/vaapi_wrapper.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -167,8 +168,12 @@ vda_.decoder_thread_task_runner_ = decoder_thread_.task_runner(); + decoder_delegate_ = + std::make_unique<VaapiVideoDecoderDelegate>(&vda_, mock_vaapi_wrapper_); + // Plug in all the mocks and ourselves as the |client_|. vda_.decoder_.reset(mock_decoder_); + vda_.decoder_delegate_ = decoder_delegate_.get(); vda_.client_ = weak_ptr_factory_.GetWeakPtr(); vda_.vaapi_wrapper_ = mock_vaapi_wrapper_; vda_.vpp_vaapi_wrapper_ = mock_vpp_vaapi_wrapper_; @@ -377,6 +382,8 @@ base::test::TaskEnvironment task_environment_; + std::unique_ptr<VaapiVideoDecoderDelegate> decoder_delegate_; + // The class under test and a worker thread for it. VaapiVideoDecodeAccelerator vda_; base::Thread decoder_thread_;
diff --git a/media/gpu/vaapi/vaapi_video_decoder.cc b/media/gpu/vaapi/vaapi_video_decoder.cc index 7716c4ac..a0d0e62 100644 --- a/media/gpu/vaapi/vaapi_video_decoder.cc +++ b/media/gpu/vaapi/vaapi_video_decoder.cc
@@ -100,6 +100,11 @@ weak_this_factory_.InvalidateWeakPtrs(); + // Notify |decoder_delegate_| of an imminent VAContextID destruction, so it + // can destroy any internal structures making use of it. + if (decoder_delegate_) + decoder_delegate_->OnVAContextDestructionSoon(); + // Destroy explicitly to DCHECK() that |vaapi_wrapper_| references are held // inside the accelerator in |decoder_|, by the |allocated_va_surfaces_| and // of course by this class. To clear |allocated_va_surfaces_| we have to first @@ -138,6 +143,10 @@ if (state_ != State::kUninitialized) { DVLOGF(3) << "Reinitializing decoder"; + // Notify |decoder_delegate_| of an imminent VAContextID destruction, so it + // can destroy any internal structures making use of it. + decoder_delegate_->OnVAContextDestructionSoon(); + decoder_ = nullptr; DCHECK(vaapi_wrapper_); // To clear |allocated_va_surfaces_| we have to first DestroyContext(). @@ -151,7 +160,7 @@ } // Initialize VAAPI wrapper. - VideoCodecProfile profile = config.profile(); + const VideoCodecProfile profile = config.profile(); vaapi_wrapper_ = VaapiWrapper::CreateForVideoCodec( VaapiWrapper::kDecode, profile, base::Bind(&ReportVaapiErrorToUMA, "Media.VaapiVideoDecoder.VAAPIError")); @@ -452,6 +461,10 @@ return; } + // Notify |decoder_delegate_| of an imminent VAContextID destruction, so it + // can destroy any internal structures making use of it. + decoder_delegate_->OnVAContextDestructionSoon(); + // All pending decode operations will be completed before triggering a // resolution change, so we can safely DestroyContext() here; that, in turn, // allows for clearing the |allocated_va_surfaces_|. @@ -554,8 +567,12 @@ } if (state_ == State::kChangingResolution) { - // If we reset during resolution change, re-create AVD. Then the new AVD - // will trigger resolution change again after reset. + // Recreate |decoder_| and |decoder_delegate_| if we are Reset() in the + // interim between calling |client_|s PrepareChangeResolution() and being + // called back on ApplyResolutionChange(), so the latter will find a fresh + // |decoder_|. Also give a chance to |decoder_delegate_| to release its + // internal data structures. + decoder_delegate_->OnVAContextDestructionSoon(); if (!CreateAcceleratedVideoDecoder().is_ok()) { SetState(State::kError); std::move(reset_cb).Run();
diff --git a/media/gpu/vaapi/vaapi_video_decoder_delegate.cc b/media/gpu/vaapi/vaapi_video_decoder_delegate.cc index 1c7ef80d..d69f3250 100644 --- a/media/gpu/vaapi/vaapi_video_decoder_delegate.cc +++ b/media/gpu/vaapi/vaapi_video_decoder_delegate.cc
@@ -30,4 +30,6 @@ vaapi_wrapper_ = std::move(vaapi_wrapper); } +void VaapiVideoDecoderDelegate::OnVAContextDestructionSoon() {} + } // namespace media
diff --git a/media/gpu/vaapi/vaapi_video_decoder_delegate.h b/media/gpu/vaapi/vaapi_video_decoder_delegate.h index f8b825a..56d9a43 100644 --- a/media/gpu/vaapi/vaapi_video_decoder_delegate.h +++ b/media/gpu/vaapi/vaapi_video_decoder_delegate.h
@@ -25,6 +25,7 @@ virtual ~VaapiVideoDecoderDelegate(); void set_vaapi_wrapper(scoped_refptr<VaapiWrapper> vaapi_wrapper); + virtual void OnVAContextDestructionSoon(); VaapiVideoDecoderDelegate(const VaapiVideoDecoderDelegate&) = delete; VaapiVideoDecoderDelegate& operator=(const VaapiVideoDecoderDelegate&) =
diff --git a/media/gpu/vaapi/vaapi_wrapper.cc b/media/gpu/vaapi/vaapi_wrapper.cc index e30d1df..edbd8e6c 100644 --- a/media/gpu/vaapi/vaapi_wrapper.cc +++ b/media/gpu/vaapi/vaapi_wrapper.cc
@@ -1860,11 +1860,34 @@ bool VaapiWrapper::ExecuteAndDestroyPendingBuffers(VASurfaceID va_surface_id) { base::AutoLock auto_lock(*va_lock_); - bool result = Execute_Locked(va_surface_id); + bool result = Execute_Locked(va_surface_id, pending_va_buffers_); DestroyPendingBuffers_Locked(); return result; } +bool VaapiWrapper::MapAndCopyAndExecute( + VASurfaceID va_surface_id, + const std::vector<std::pair<VABufferID, VABufferDescriptor>>& va_buffers) { + DCHECK_NE(va_surface_id, VA_INVALID_SURFACE); + + TRACE_EVENT0("media,gpu", "VaapiWrapper::MapAndCopyAndExecute"); + base::AutoLock auto_lock(*va_lock_); + std::vector<VABufferID> va_buffer_ids; + + for (const auto& va_buffer : va_buffers) { + const VABufferID va_buffer_id = va_buffer.first; + const VABufferDescriptor& descriptor = va_buffer.second; + DCHECK_NE(va_buffer_id, VA_INVALID_ID); + + if (!MapAndCopy_Locked(va_buffer_id, descriptor)) + return false; + + va_buffer_ids.push_back(va_buffer_id); + } + + return Execute_Locked(va_surface_id, va_buffer_ids); +} + #if defined(USE_X11) bool VaapiWrapper::PutSurfaceIntoPixmap(VASurfaceID va_surface_id, Pixmap x_pixmap, @@ -2434,13 +2457,8 @@ VA_LOG_ON_ERROR(va_res, VaapiFunctions::kVADestroySurfaces); } -bool VaapiWrapper::Execute(VASurfaceID va_surface_id) { - TRACE_EVENT0("media,gpu", "VaapiWrapper::Execute"); - base::AutoLock auto_lock(*va_lock_); - return Execute_Locked(va_surface_id); -} - -bool VaapiWrapper::Execute_Locked(VASurfaceID va_surface_id) { +bool VaapiWrapper::Execute_Locked(VASurfaceID va_surface_id, + const std::vector<VABufferID>& va_buffers) { TRACE_EVENT0("media,gpu", "VaapiWrapper::Execute_Locked"); va_lock_->AssertAcquired(); @@ -2452,11 +2470,11 @@ VAStatus va_res = vaBeginPicture(va_display_, va_context_id_, va_surface_id); VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVABeginPicture, false); - if (pending_va_buffers_.size() > 0) { - // Commit parameter and slice buffers. - va_res = - vaRenderPicture(va_display_, va_context_id_, &pending_va_buffers_[0], - pending_va_buffers_.size()); + if (!va_buffers.empty()) { + // vaRenderPicture() needs a non-const pointer, possibly unnecessarily. + VABufferID* va_buffers_data = const_cast<VABufferID*>(va_buffers.data()); + va_res = vaRenderPicture(va_display_, va_context_id_, va_buffers_data, + base::checked_cast<int>(va_buffers.size())); VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVARenderPicture_VABuffers, false); } @@ -2496,16 +2514,28 @@ VA_SUCCESS_OR_RETURN(va_res, VaapiFunctions::kVACreateBuffer, false); } + if (!MapAndCopy_Locked(buffer_id, va_buffer)) + return false; + + pending_va_buffers_.push_back(buffer_id); + return true; +} + +bool VaapiWrapper::MapAndCopy_Locked(VABufferID va_buffer_id, + const VABufferDescriptor& va_buffer) { + va_lock_->AssertAcquired(); + + DCHECK_NE(va_buffer_id, VA_INVALID_ID); + DCHECK_LT(va_buffer.type, VABufferTypeMax); + DCHECK(va_buffer.data); + ScopedVABufferMapping mapping( - va_lock_, va_display_, buffer_id, + va_lock_, va_display_, va_buffer_id, base::BindOnce(base::IgnoreResult(&vaDestroyBuffer), va_display_)); if (!mapping.IsValid()) return false; - memcpy(mapping.data(), va_buffer.data, va_buffer.size); - - pending_va_buffers_.push_back(buffer_id); - return true; + return memcpy(mapping.data(), va_buffer.data, va_buffer.size); } } // namespace media
diff --git a/media/gpu/vaapi/vaapi_wrapper.h b/media/gpu/vaapi/vaapi_wrapper.h index 508513dec6..18a2898 100644 --- a/media/gpu/vaapi/vaapi_wrapper.h +++ b/media/gpu/vaapi/vaapi_wrapper.h
@@ -331,14 +331,20 @@ size_t size, const void* buffer); - // Cancel and destroy all buffers queued to the HW codec via SubmitBuffer(). - // Useful when a pending job is to be cancelled (on reset or error). + // Destroys all |pending_va_buffers_| sent via SubmitBuffer*(). Useful when a + // pending job is to be cancelled (on reset or error). void DestroyPendingBuffers(); // Executes job in hardware on target |va_surface_id| and destroys pending // buffers. Returns false if Execute() fails. virtual bool ExecuteAndDestroyPendingBuffers(VASurfaceID va_surface_id); + // Maps each |va_buffers| ID and copies the data described by the associated + // VABufferDescriptor into it; then calls Execute_Locked() on |va_surface_id|. + bool MapAndCopyAndExecute( + VASurfaceID va_surface_id, + const std::vector<std::pair<VABufferID, VABufferDescriptor>>& va_buffers); + #if defined(USE_X11) // Put data from |va_surface_id| into |x_pixmap| of size // |dest_size|, converting/scaling to it. @@ -437,20 +443,25 @@ size_t num_surfaces, std::vector<VASurfaceID>* va_surfaces); - // Execute pending job in hardware and destroy pending buffers. Return false - // if vaapi driver refuses to accept parameter or slice buffers submitted - // by client, or if execution fails in hardware. - bool Execute(VASurfaceID va_surface_id); - bool Execute_Locked(VASurfaceID va_surface_id) + // Carries out the vaBeginPicture()-vaRenderPicture()-vaEndPicture() on target + // |va_surface_id|. Returns false if any of these calls fails. + bool Execute_Locked(VASurfaceID va_surface_id, + const std::vector<VABufferID>& va_buffers) EXCLUSIVE_LOCKS_REQUIRED(va_lock_); void DestroyPendingBuffers_Locked() EXCLUSIVE_LOCKS_REQUIRED(va_lock_); - // Requests libva to allocate a new VABufferID of type |va_buffer.type|, maps - // it and copies |va_buffer.size| contents of |va_buffer.data| to it. + // Requests libva to allocate a new VABufferID of type |va_buffer.type|, then + // maps-and-copies |va_buffer.size| contents of |va_buffer.data| to it. bool SubmitBuffer_Locked(const VABufferDescriptor& va_buffer) EXCLUSIVE_LOCKS_REQUIRED(va_lock_); + // Maps |va_buffer_id| and, if successful, copies the contents of |va_buffer| + // into it. + bool MapAndCopy_Locked(VABufferID va_buffer_id, + const VABufferDescriptor& va_buffer) + EXCLUSIVE_LOCKS_REQUIRED(va_lock_); + const CodecMode mode_; // Pointer to VADisplayState's member |va_lock_|. Guaranteed to be valid for
diff --git a/media/gpu/vaapi/vp9_vaapi_video_decoder_delegate.cc b/media/gpu/vaapi/vp9_vaapi_video_decoder_delegate.cc index 0c5837cb..1ba6136 100644 --- a/media/gpu/vaapi/vp9_vaapi_video_decoder_delegate.cc +++ b/media/gpu/vaapi/vp9_vaapi_video_decoder_delegate.cc
@@ -21,7 +21,11 @@ scoped_refptr<VaapiWrapper> vaapi_wrapper) : VaapiVideoDecoderDelegate(vaapi_dec, std::move(vaapi_wrapper)) {} -VP9VaapiVideoDecoderDelegate::~VP9VaapiVideoDecoderDelegate() = default; +VP9VaapiVideoDecoderDelegate::~VP9VaapiVideoDecoderDelegate() { + DCHECK(!picture_params_); + DCHECK(!slice_params_); + DCHECK(!encoded_data_); +} scoped_refptr<VP9Picture> VP9VaapiVideoDecoderDelegate::CreateVP9Picture() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); @@ -43,12 +47,33 @@ // |done_cb| should be null as we return false from IsFrameContextRequired(). DCHECK(!done_cb); - VADecPictureParameterBufferVP9 pic_param; - memset(&pic_param, 0, sizeof(pic_param)); - const Vp9FrameHeader* frame_hdr = pic->frame_hdr.get(); DCHECK(frame_hdr); + VADecPictureParameterBufferVP9 pic_param{}; + VASliceParameterBufferVP9 slice_param{}; + + if (!picture_params_) { + picture_params_ = vaapi_wrapper_->CreateVABuffer( + VAPictureParameterBufferType, sizeof(pic_param)); + if (!picture_params_) + return false; + } + if (!slice_params_) { + slice_params_ = vaapi_wrapper_->CreateVABuffer(VASliceParameterBufferType, + sizeof(slice_param)); + if (!slice_params_) + return false; + } + // |encoded_data_| has to match perfectly |frame_hdr->frame_size| or decoding + // will have horrific artifacts. + if (!encoded_data_ || encoded_data_->size() != frame_hdr->frame_size) { + encoded_data_ = vaapi_wrapper_->CreateVABuffer(VASliceDataBufferType, + frame_hdr->frame_size); + if (!encoded_data_) + return false; + } + pic_param.frame_width = base::checked_cast<uint16_t>(frame_hdr->frame_width); pic_param.frame_height = base::checked_cast<uint16_t>(frame_hdr->frame_height); @@ -108,8 +133,6 @@ DCHECK((pic_param.profile == 0 && pic_param.bit_depth == 8) || (pic_param.profile == 2 && pic_param.bit_depth == 10)); - VASliceParameterBufferVP9 slice_param; - memset(&slice_param, 0, sizeof(slice_param)); slice_param.slice_data_size = frame_hdr->frame_size; slice_param.slice_data_offset = 0; slice_param.slice_data_flag = VA_SLICE_DATA_FLAG_ALL; @@ -138,15 +161,14 @@ seg_param.chroma_ac_quant_scale = seg.uv_dequant[i][1]; } - if (!vaapi_wrapper_->SubmitBuffers( - {{VAPictureParameterBufferType, sizeof(pic_param), &pic_param}, - {VASliceParameterBufferType, sizeof(slice_param), &slice_param}, - {VASliceDataBufferType, frame_hdr->frame_size, frame_hdr->data}})) { - return false; - } - - return vaapi_wrapper_->ExecuteAndDestroyPendingBuffers( - pic->AsVaapiVP9Picture()->va_surface()->id()); + return vaapi_wrapper_->MapAndCopyAndExecute( + pic->AsVaapiVP9Picture()->va_surface()->id(), + {{picture_params_->id(), + {picture_params_->type(), picture_params_->size(), &pic_param}}, + {slice_params_->id(), + {slice_params_->type(), slice_params_->size(), &slice_param}}, + {encoded_data_->id(), + {encoded_data_->type(), frame_hdr->frame_size, frame_hdr->data}}}); } bool VP9VaapiVideoDecoderDelegate::OutputPicture( @@ -172,4 +194,12 @@ return false; } +void VP9VaapiVideoDecoderDelegate::OnVAContextDestructionSoon() { + // Destroy the member ScopedVABuffers below since they refer to a VAContextID + // that will be destroyed soon. + picture_params_.reset(); + slice_params_.reset(); + encoded_data_.reset(); +} + } // namespace media
diff --git a/media/gpu/vaapi/vp9_vaapi_video_decoder_delegate.h b/media/gpu/vaapi/vp9_vaapi_video_decoder_delegate.h index f3d89db..0d93f8e 100644 --- a/media/gpu/vaapi/vp9_vaapi_video_decoder_delegate.h +++ b/media/gpu/vaapi/vp9_vaapi_video_decoder_delegate.h
@@ -13,6 +13,7 @@ namespace media { +class ScopedVABuffer; class VP9Picture; class VP9VaapiVideoDecoderDelegate : public VP9Decoder::VP9Accelerator, @@ -35,6 +36,14 @@ bool GetFrameContext(scoped_refptr<VP9Picture> pic, Vp9FrameContext* frame_ctx) override; + // VaapiVideoDecoderDelegate impl. + void OnVAContextDestructionSoon() override; + + private: + std::unique_ptr<ScopedVABuffer> picture_params_; + std::unique_ptr<ScopedVABuffer> slice_params_; + std::unique_ptr<ScopedVABuffer> encoded_data_; + DISALLOW_COPY_AND_ASSIGN(VP9VaapiVideoDecoderDelegate); };
diff --git a/mojo/docs/mojolpm.md b/mojo/docs/mojolpm.md index a3b1c8e2..6e0acd07 100644 --- a/mojo/docs/mojolpm.md +++ b/mojo/docs/mojolpm.md
@@ -1,6 +1,6 @@ # Getting started with MojoLPM -*** note +*** **Note:** Using MojoLPM to fuzz your Mojo interfaces is intended to be simple, but there are edge-cases that may require a very detailed understanding of the Mojo implementation to fix. If you run into problems that you can't understand @@ -45,6 +45,10 @@ there isn't a very simple way to enumerate these, so you may need to look through some of the source code to find an interesting one. +A few of the places which bind many of these cross-privilege interfaces are +`content/browser/browser_interface_binders.cc` and +`content/browser/render_process_host_impl.cc`, specifically `RenderProcessHostImpl::RegisterMojoInterfaces`. + For the rest of this guide, we'll write a new fuzzer for `blink.mojom.CodeCacheHost`, which is defined in `third_party/blink/public/mojom/loader/code_cache.mojom`. @@ -67,11 +71,17 @@ ## Find the unittest for the implementation +Specifically, we're looking for a browser-process side unittest (so not in +`//third_party/blink`). We want the unittest for the browser side implementation +of that Mojo interface - in many cases if such exists, it will be directly next +to the implementation source, ie. in this case we would be most likely to find +them in `content/browser/renderer_host/code_cache_host_impl_unittest.cc`. + Unfortunately, it doesn't look like `CodeCacheHostImpl` has a unittest, so we'll have to go through the process of understanding how to create a valid instance ourselves in order to fuzz this interface. -Since this interface runs in the Browser process, and is part of `/content`, +Since this implementation runs in the Browser process, and is part of `/content`, we're going to create our new fuzzer in `/content/test/fuzzer`. ## Add our testcase proto @@ -83,17 +93,27 @@ file: ``` +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Message format for the MojoLPM fuzzer for the CodeCacheHost interface. + syntax = "proto2"; package content.fuzzing.code_cache_host.proto; import "third_party/blink/public/mojom/loader/code_cache.mojom.mojolpm.proto"; -message NewCodeCacheHost { +// Bind a new CodeCacheHost remote +message NewCodeCacheHostAction { required uint32 id = 1; } -message RunUntilIdle { +// Run the specific sequence for (an indeterminate) period. This is not +// intended to create a specific ordering, but to allow the fuzzer to delay a +// later task until previous tasks have completed. +message RunThreadAction { enum ThreadId { IO = 0; UI = 1; @@ -102,22 +122,27 @@ required ThreadId id = 1; } +// Actions that can be performed by the fuzzer. message Action { oneof action { - NewCodeCacheHost new_code_cache_host = 1; - RunUntilIdle run_until_idle = 2; - mojolpm.blink.mojom.CodeCacheHost.RemoteMethodCall code_cache_host_call = 3; + NewCodeCacheHostAction new_code_cache_host = 1; + RunThreadAction run_thread = 2; + mojolpm.blink.mojom.CodeCacheHost.RemoteAction + code_cache_host_remote_action = 3; } } +// Sequence provides a level of indirection which allows Testcase to compactly +// express repeated sequences of actions. message Sequence { - repeated uint32 action_indexes = 1 [packed=true]; + repeated uint32 action_indexes = 1 [packed = true]; } +// Testcase is the top-level message type interpreted by the fuzzer. message Testcase { repeated Action actions = 1; repeated Sequence sequences = 2; - repeated uint32 sequence_indexes = 3 [packed=true]; + repeated uint32 sequence_indexes = 3 [packed = true]; } ``` @@ -186,15 +211,53 @@ ``` You should now be able to build and run this fuzzer (it, of course, won't do -very much) to check that everything is lined up right so far. +very much) to check that everything is lined up right so far. Recommended GN +arguments: + +``` +# DCHECKS are really useful when getting your fuzzer up and running correctly, +# but will often get in the way when running actual fuzzing, so we will disable +# this later. +dcheck_always_on = true +# Without this flag, our fuzzer target won't exist. +enable_mojom_fuzzer = true +# ASAN is super useful for fuzzing, but in this case we just want it to help us +# debug the inevitable lifetime issues while we get everything set-up correctly! +is_asan = true +is_component_build = true +is_debug = false +optimize_for_fuzzing = true +use_goma = true +use_libfuzzer = true +``` + ## Handle global process setup Now we need to add some basic setup code so that our process has something that mostly resembles a normal Browser process; if you look in the file this is -`CodeCacheHostFuzzerEnvironment`, which adds a global environment instance that +`ContentFuzzerEnvironment`, which adds a global environment instance that will handle setting up this basic environment, which will be reused for all of -our testcases, since starting threads is expensive and slow. +our testcases, since starting threads is expensive and slow. This code should +also be responsible for setting up any "stateless" (or more-or-less stateless) +code that is required for your interface to run - examples are initializing the +Mojo Core, and loading ICU datafiles. + +A key difference between our needs here and those of a normal unittest is that +we very likely do not want to be running in a special single-threaded mode. We +want to be able to trigger issues related to threading, sequencing and ordering, +and making sure that the UI, IO and threadpool threads behave as close to a +normal browser process as possible is desirable. + +It's likely better to be conservative here - while it might appear that an +interface to be tested has no interaction with the UI thread, and so we could +save some resources by only having a real IO thread, it's often very difficult +to establish this with certainty. + +In practice, the most efficient way forward will be to copy the existing +`Environment` setup from another MojoLPM fuzzer and adapting that to the +context in which the interface to be fuzzed will actually run. + ## Handle per-testcase setup @@ -244,6 +307,17 @@ Browser-process objects have specific expectations, and will end up with very different behaviour if they are created or used from the wrong context. +**The most important thing to be careful of here is that everything happens on +the correct thread/sequence. Many Browser-process objects have specific +expectations, and will end up with very different behaviour if they are created +or used from the wrong context. Test code doesn't always behave the same way, so +try to check the behaviour in the real Browser.** + +**The second most important thing to be aware of is to make sure that the fuzzer +has the same control over lifetimes of objects that a renderer process would +normally have - the best way to check this is to make sure that you've found and +understood the browser process code that would usually bind that interface.** + ## Integrate with the generated MojoLPM fuzzer code Finally, we need to do a little bit more plumbing, to rig up this infrastructure @@ -262,28 +336,31 @@ return; } const auto& action = - testcase_.actions(action_idx % testcase_.actions_size()); + testcase_.actions(action_idx % testcase_.actions_size()); + if (action.ByteSizeLong() > max_action_size_) { + return; + } switch (action.action_case()) { - case content::fuzzing::code_cache_host::proto::Action::kNewCodeCacheHost: { + case Action::kNewCodeCacheHost: cch_context_.AddCodeCacheHost( action.new_code_cache_host().id(), action.new_code_cache_host().render_process_id(), action.new_code_cache_host().origin_id()); - } break; + break; - case content::fuzzing::code_cache_host::proto::Action::kRunUntilIdle: { + case Action::kRunUntilIdle: if (action.run_until_idle().id()) { content::RunUIThreadUntilIdle(); } else { content::RunIOThreadUntilIdle(); } - } break; + break; - case content::fuzzing::code_cache_host::proto::Action::kCodeCacheHostCall: { + case Action::kCodeCacheHostCall: mojolpm::HandleRemoteMethodCall(action.code_cache_host_call()); - } break; + break; - case content::fuzzing::code_cache_host::proto::Action::ACTION_NOT_SET: + case Action::ACTION_NOT_SET: break; } } @@ -328,16 +405,133 @@ fallen over. It's likely you'll have made a few mistakes somewhere along the way but hopefully soon you'll have the fuzzer running 'clean' for a few hours. +If you run into DCHECK failures in deserialization, see the section below marked +[triage]. + +## Expand it to include all relevant interfaces + +`CodeCacheHost` is a very simple interface, and it doesn't have any dependencies +on other interfaces. In reality, most Mojo interfaces are much more complex, and +fuzzing their implementations thoroughly will require more work. We'll take a +quick look at a more complex interface, the `BlobRegistry`. If we look at +`blob_registry.mojom`: + +``` +// This interface is the primary access point from renderer to the browser's +// blob system. This interface provides methods to register new blobs and get +// references to existing blobs. +interface BlobRegistry { + // Registers a new blob with the blob registry. + // TODO(mek): Make this method non-sync and get rid of the UUID parameter once + // enough of the rest of the system doesn't rely on the UUID anymore. + [Sync] Register(pending_receiver<blink.mojom.Blob> blob, string uuid, + string content_type, string content_disposition, + array<DataElement> elements) => (); + + // Creates a new blob out of a data pipe. + // |length_hint| is only used as a hint, to decide if the blob should be + // stored in memory or on disk. Registration will still succeed even if less + // or more bytes are read from the pipe. The resulting SerializedBlob can be + // inspected to see how many bytes actually did end up being read from + // the pipe. Pass 0 if nothing is known about the expected size. + // If something goes wrong (for example the blob system doesn't have enough + // available space to store all the data from the stream) null will be + // returned. + RegisterFromStream(string content_type, string content_disposition, + uint64 length_hint, + handle<data_pipe_consumer> data, + pending_associated_remote<ProgressClient>? progress_client) + => (SerializedBlob? blob); + + // Returns a reference to an existing blob. Should not be used by new code, + // is only exposed to make converting existing blob using code easier. + // TODO(mek): Remove when crbug.com/740744 is resolved. + [Sync] GetBlobFromUUID(pending_receiver<Blob> blob, string uuid) => (); + + // Returns a BlobURLStore for a specific origin. + URLStoreForOrigin(url.mojom.Origin origin, + pending_associated_receiver<blink.mojom.BlobURLStore> url_store); +}; +``` + +We can see that this interface references multiple other interfaces; there are +several different kinds of reference that we need to worry about: + +**Additional fuzzable interfaces** - if an interface method can return a +pending_remote<> or take a pending_receiver<> to an interface Foo, then we +want our fuzzer to fuzz those interfaces too. + +Here we would want to add `blink.mojom.Blob.RemoteAction` and +`blink.mojom.BlobURLStore.AssociatedRemoteAction` to the possible actions +that our fuzzer protobufs can take. + +**Renderer-hosted interfaces** - if an interface method takes a pending_remote<> +(or returns a pending_receiver<>), then we'll also want to add response handling +to our fuzzer. This lets the fuzzer send fuzzer-side implementations of mojo +interfaces, and handle fuzzing the values returned if those methods are called. + +Here we can see `blink.mojom.ProgressClient` is needed, but we can also see that +we pass `blink.mojom.DataElement` structures to `BlobRegistry.Register`. These +can contain `remote<blink.mojom.Blob>`, so we also need to support +`blink.mojom.Blob`. + +These are handled similarly to the `RemoteAction`s, but the type that we need to +add to our proto is instead `blink.mojom.ProgressClient.ReceiverAction`, and so +on. + +We can continue applying this logic recursively to all of the interfaces that +might be accessed - this comes down to a question of what dependencies are most +likely to be important in getting good coverage, so the later step of examining +code coverage may also help in guiding the addition of new interfaces here. + +`blob_registry_mojolpm_fuzzer.proto` illustrates how these responses can be added +to the testcase proto. + +## Start fuzzing + +Once the fuzzer is up and running, we probably want to remove dcheck_always_on. + +``` +enable_mojom_fuzzer = true +is_asan = true +is_component_build = true +is_debug = false +optimize_for_fuzzing = true +use_goma = true +use_libfuzzer = true +``` + +The reason for this is that while DCHECKs are often useful when fuzzing (and a +good indication of potential bugs), the Mojo serialization code often contains +quite a few DCHECKs, and our fuzzer is essentially serializing untrusted data +before it can deserialize that data on the Browser-process side. This means +that we can easily get blocked by a "completely valid" DCHECK during +serialisation that a compromised renderer would bypass. Removing DCHECKs will +sometimes let the fuzzer continue in these situations, and will reduce spurious +results, but if your fuzzer doesn't trigger any of these cases it may be +beneficial to also fuzz with DCHECKs enabled. We'll discuss this below under +[triage](#triage-notes). + If your coverage isn't going up at all, then you've probably made a mistake and it likely isn't managing to actually interact with the interface you're trying to fuzz - try using the code coverage output from the next step to debug what's going wrong. + ## (Optional) Run coverage In many cases it's useful to check the code coverage to see if we can benefit from adding some manual testcases to get deeper coverage. For this example I -used the following command: +used the following gn arguments and command: + +``` +enable_mojom_fuzzer = true +is_component_build = false +is_debug = false +use_clang_coverage = true +use_goma = true +use_libfuzzer = true +``` ``` python tools/code_coverage/coverage.py code_cache_host_mojolpm_fuzzer -b out/Coverage -o ManualReport -c "out/Coverage/code_cache_host_mojolpm_fuzzer -ignore_timeouts=1 -timeout=4 -runs=0 /dev/shm/corpus" -f content @@ -380,10 +574,8 @@ } } actions { - code_cache_host_call { - remote { - id: 1 - } + code_cache_host_remote_action { + id: 1 m_did_generate_cacheable_metadata { m_cache_type: CodeCacheType_kJavascript m_url { @@ -398,6 +590,7 @@ m_bytes { } } + } m_expected_response_time { } } @@ -462,7 +655,48 @@ Much better! -[markbrand@google.com]: mailto:markbrand@google.com?subject=[MojoLPM%20Help]:%20&cc=fuzzing@chromium.org +## Triage notes + +MojoLPM fuzzers have a number of common failure modes that are fairly easy to +distinguish from real bugs in the implementation being fuzzed. + +The first of these is any crash on the `fuzzer_thread`. Code in the +implementation should never, under any circumstances be running on this thread, +so any crash on this thread is the result of a bug in the fuzzer itself, or +one of the other causes mentioned below. + +The second is DCHECK or other failures during Mojo serialization. Various traits +assert that they are serializing reasonable values - since we need to reuse this +serialization code in the fuzzer to produce input to the implementation, we can +trigger these on the `fuzzer_thread` while processing input to send to the +implementation. + +The example ASAN error output below illustrates an example of both of these +cases - the error happens on the `fuzzer_thread`, and during serialization. + +``` +==2940792==ERROR: AddressSanitizer: ILL on unknown address 0x7fbd9391d0f9 (pc 0x7fbd9391d0f9 bp 0x7fbd24deb3e0 sp 0x7fbd24deb3e0 T5) + #0 0x7fbd9391d0f9 in unsigned int base::internal::CheckOnFailure::HandleFailure<unsigned int>() base/numerics/safe_conversions_impl.h:122:5 + #1 0x7fbd9391ba78 in unsigned int base::internal::checked_cast<unsigned int, base::internal::CheckOnFailure, unsigned long>(unsigned long) base/numerics/safe_conversions.h:114:16 + #2 0x7fbd9391ba28 in mojo::StructTraits<mojo_base::mojom::BigBufferSharedMemoryRegionDataView, mojo_base::internal::BigBufferSharedMemoryRegion>::size(mojo_base::internal::BigBufferSharedMemoryRegion const&) mojo/public/cpp/base/big_buffer_mojom_traits.cc:17:10 + #3 0x7fbd7f62fc2e in mojo::internal::Serializer<mojo_base::mojom::BigBufferSharedMemoryRegionDataView, mojo_base::internal::BigBufferSharedMemoryRegion>::Serialize(mojo_base::internal::BigBufferSharedMemoryRegion&, mojo::internal::Buffer*, mojo_base::mojom::internal::BigBufferSharedMemoryRegion_Data::BufferWriter*, mojo::internal::SerializationContext*) gen/mojo/public/mojom/base/big_buffer.mojom-shared.h:182:23 +... + #41 0x7fbd955376e8 in base::RunLoop::Run() base/run_loop.cc:124:14 + #42 0x7fbd95707f83 in base::Thread::Run(base::RunLoop*) base/threading/thread.cc:311:13 + #43 0x7fbd95708427 in base::Thread::ThreadMain() base/threading/thread.cc:382:3 + #44 0x7fbd957dfb40 in base::(anonymous namespace)::ThreadFunc(void*) base/threading/platform_thread_posix.cc:81:13 + #45 0x7fbd403866b9 in start_thread /build/glibc-LK5gWL/glibc-2.23/nptl/pthread_create.c:333 +AddressSanitizer can not provide additional info. +SUMMARY: AddressSanitizer: ILL (/mnt/scratch0/clusterfuzz/bot/builds/chromium-browser-libfuzzer_linux-release-asan_ae530a86793cd6b8b56ce9af9159ac101396e802/revisions/libfuzzer-linux-release-807440/libmojo_base_shared_typemap_traits.so+0x190f9) +Thread T5 (fuzzer_thread) created by T0 here: + #0 0x56433ef70b3a in pthread_create third_party/llvm/compiler-rt/lib/asan/asan_interceptors.cpp:214:3 +... + #14 0x56433f15380c in main third_party/libFuzzer/src/FuzzerMain.cpp:19:10 + #15 0x7fbd3c38a82f in __libc_start_main /build/glibc-LK5gWL/glibc-2.23/csu/libc-start.c:291 +==2940792==ABORTING +``` + +[markbrand@google.com]:mailto:markbrand@google.com?subject=[MojoLPM%20Help]:%20&cc=fuzzing@chromium.org [libfuzzer]: https://source.chromium.org/chromium/chromium/src/+/master:testing/libfuzzer/getting_started.md [Protocol Buffers]: https://developers.google.com/protocol-buffers/docs/cpptutorial [libprotobuf-mutator]: https://source.chromium.org/chromium/chromium/src/+/master:testing/libfuzzer/libprotobuf-mutator.md
diff --git a/net/http/http_auth_cache.cc b/net/http/http_auth_cache.cc index 720c9760..6c5f4ff 100644 --- a/net/http/http_auth_cache.cc +++ b/net/http/http_auth_cache.cc
@@ -300,15 +300,18 @@ return false; } -void HttpAuthCache::ClearEntriesAddedSince(base::Time begin_time) { - if (begin_time.is_null()) { +void HttpAuthCache::ClearEntriesAddedBetween(base::Time begin_time, + base::Time end_time) { + if (begin_time.is_min() && end_time.is_max()) { ClearAllEntries(); - } else { - base::EraseIf(entries_, [begin_time](EntryMap::value_type& entry_map_pair) { - Entry& entry = entry_map_pair.second; - return entry.creation_time_ >= begin_time; - }); + return; } + base::EraseIf(entries_, + [begin_time, end_time](EntryMap::value_type& entry_map_pair) { + Entry& entry = entry_map_pair.second; + return entry.creation_time_ >= begin_time && + entry.creation_time_ < end_time; + }); } void HttpAuthCache::ClearAllEntries() {
diff --git a/net/http/http_auth_cache.h b/net/http/http_auth_cache.h index c9c5d79..a664f393 100644 --- a/net/http/http_auth_cache.h +++ b/net/http/http_auth_cache.h
@@ -197,9 +197,10 @@ const NetworkIsolationKey& network_isolation_key, const AuthCredentials& credentials); - // Clears cache entries added since |begin_time| or all entries if - // |begin_time| is null. - void ClearEntriesAddedSince(base::Time begin_time); + // Clears cache entries added between |begin_time| inclusively and |end_time| + // exclusively. Clears all entries if |begin_time| and |end_time| are equal to + // base::Time::Min() and base::Time::Max() respectively. + void ClearEntriesAddedBetween(base::Time begin_time, base::Time end_time); // Clears all added entries. void ClearAllEntries();
diff --git a/net/http/http_auth_cache_unittest.cc b/net/http/http_auth_cache_unittest.cc index 2e496c3d..6cd9654 100644 --- a/net/http/http_auth_cache_unittest.cc +++ b/net/http/http_auth_cache_unittest.cc
@@ -29,9 +29,11 @@ const char kRealm5[] = "Realm5"; const base::string16 k123(ASCIIToUTF16("123")); const base::string16 k1234(ASCIIToUTF16("1234")); +const base::string16 k12345(ASCIIToUTF16("12345")); const base::string16 kAdmin(ASCIIToUTF16("admin")); const base::string16 kAlice(ASCIIToUTF16("alice")); const base::string16 kAlice2(ASCIIToUTF16("alice2")); +const base::string16 kAlice3(ASCIIToUTF16("alice3")); const base::string16 kPassword(ASCIIToUTF16("password")); const base::string16 kRoot(ASCIIToUTF16("root")); const base::string16 kUsername(ASCIIToUTF16("username")); @@ -696,7 +698,7 @@ EXPECT_FALSE(nullptr == entry); } -TEST(HttpAuthCacheTest, ClearEntriesAddedSince) { +TEST(HttpAuthCacheTest, ClearEntriesAddedBetween) { GURL origin("http://foobar.com"); base::Time start_time; @@ -726,9 +728,16 @@ NetworkIsolationKey(), "basic realm=Realm2", AuthCredentials(kAdmin, kPassword), "/baz/"); - base::Time test_time; - ASSERT_TRUE(base::Time::FromString("30 May 2018 12:00:05", &test_time)); - cache.ClearEntriesAddedSince(test_time); + test_clock.Advance(base::TimeDelta::FromSeconds(10)); // Time now 12:00:20 + cache.Add(origin, HttpAuth::AUTH_SERVER, kRealm5, HttpAuth::AUTH_SCHEME_BASIC, + NetworkIsolationKey(), "basic realm=Realm5", + AuthCredentials(kAlice3, k12345), "/"); + + base::Time test_time1; + ASSERT_TRUE(base::Time::FromString("30 May 2018 12:00:05", &test_time1)); + base::Time test_time2; + ASSERT_TRUE(base::Time::FromString("30 May 2018 12:00:15", &test_time2)); + cache.ClearEntriesAddedBetween(test_time1, test_time2); // Realms 1 and 2 are older than 12:00:05 and should not be cleared EXPECT_NE(nullptr, @@ -737,12 +746,18 @@ EXPECT_NE(nullptr, cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm2, HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey())); + + // Realms 5 is newer than 12:00:15 and should not be cleared + EXPECT_NE(nullptr, + cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm5, + HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey())); + // Creation time is set for a whole entry rather than for a particular path. // Path added within the requested duration isn't be removed. EXPECT_NE(nullptr, cache.LookupByPath(origin, HttpAuth::AUTH_SERVER, NetworkIsolationKey(), "/baz/")); - // Realms 3 and 4 are newer than 12:00:05 and should be cleared. + // Realms 3 and 4 are between 12:00:05 and 12:00:10 and should be cleared. EXPECT_EQ(nullptr, cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm3, HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey())); @@ -750,7 +765,8 @@ cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm4, HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey())); - cache.ClearEntriesAddedSince(start_time - base::TimeDelta::FromSeconds(1)); + cache.ClearEntriesAddedBetween(start_time - base::TimeDelta::FromSeconds(1), + base::Time::Max()); EXPECT_EQ(nullptr, cache.Lookup(origin, HttpAuth::AUTH_SERVER, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, NetworkIsolationKey())); @@ -761,7 +777,7 @@ NetworkIsolationKey(), "/baz/")); } -TEST(HttpAuthCacheTest, ClearEntriesAddedSinceWithNullTime) { +TEST(HttpAuthCacheTest, ClearEntriesAddedBetweenWithAllTimeValues) { GURL origin("http://foobar.com"); base::SimpleTestClock test_clock; @@ -789,7 +805,7 @@ NetworkIsolationKey(), "basic realm=Realm2", AuthCredentials(kAdmin, kPassword), "/baz/"); - cache.ClearEntriesAddedSince(base::Time()); + cache.ClearEntriesAddedBetween(base::Time::Min(), base::Time::Max()); // All entries should be cleared. EXPECT_EQ(nullptr,
diff --git a/net/quic/quic_flags_list.h b/net/quic/quic_flags_list.h index 327897d..8e76c9d8 100644 --- a/net/quic/quic_flags_list.h +++ b/net/quic/quic_flags_list.h
@@ -412,3 +412,34 @@ bool, FLAGS_quic_reloadable_flag_quic_give_sent_packet_to_debug_visitor_after_sent, false) + +// If true, abort async QPACK header decompression in QuicSpdyStream::OnClose(). +QUIC_FLAG(bool, + FLAGS_quic_reloadable_flag_quic_abort_qpack_on_stream_close, + false) + +// If true, do not arm PTO for application data until handshake confirmed. +QUIC_FLAG(bool, + FLAGS_quic_reloadable_flag_quic_fix_arm_pto_for_application_data, + true) + +// If true, cap client suggested initial RTT to 1s if it is longer than 1s. +QUIC_FLAG(bool, + FLAGS_quic_reloadable_flag_quic_cap_large_client_initial_rtt, + true) + +// If true, fix a potential out of order sending caused by handshake gets +// confirmed while the coalescer is not empty. +QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_fix_out_of_order_sending, true) + +// If true, remove processed undecryptable packets. +QUIC_FLAG(bool, + FLAGS_quic_reloadable_flag_quic_fix_undecryptable_packets2, + false) + +// If true, QUIC BBRv2 will use inflight byte after congestion event to detect +// queuing during PROBE_UP. +QUIC_FLAG( + bool, + FLAGS_quic_reloadable_flag_quic_bbr2_use_post_inflight_to_detect_queuing, + false)
diff --git a/net/quic/quic_stream_factory_test.cc b/net/quic/quic_stream_factory_test.cc index c99397a..9f0d098 100644 --- a/net/quic/quic_stream_factory_test.cc +++ b/net/quic/quic_stream_factory_test.cc
@@ -1366,7 +1366,7 @@ EXPECT_TRUE(stream.get()); QuicChromiumClientSession* session = GetActiveSession(host_port_pair_); - EXPECT_EQ(1200000u, session->connection()->GetStats().srtt_us); + EXPECT_EQ(1000000u, session->connection()->GetStats().srtt_us); ASSERT_TRUE(session->config()->HasInitialRoundTripTimeUsToSend()); EXPECT_EQ(1200000u, session->config()->GetInitialRoundTripTimeUsToSend()); }
diff --git a/net/third_party/quiche/BUILD.gn b/net/third_party/quiche/BUILD.gn index 7ae52c65..ffab81e 100644 --- a/net/third_party/quiche/BUILD.gn +++ b/net/third_party/quiche/BUILD.gn
@@ -27,7 +27,6 @@ "src/common/platform/api/quiche_endian.h", "src/common/platform/api/quiche_export.h", "src/common/platform/api/quiche_logging.h", - "src/common/platform/api/quiche_map_util.h", "src/common/platform/api/quiche_optional.h", "src/common/platform/api/quiche_ptr_util.h", "src/common/platform/api/quiche_str_cat.h", @@ -1377,7 +1376,6 @@ "src/quic/core/quic_versions_test.cc", "src/quic/core/quic_write_blocked_list_test.cc", "src/quic/core/tls_chlo_extractor_test.cc", - "src/quic/core/tls_handshaker_test.cc", "src/quic/core/uber_quic_stream_id_manager_test.cc", "src/quic/core/uber_received_packet_manager_test.cc", "src/quic/platform/api/quic_containers_test.cc",
diff --git a/remoting/base/protobuf_http_client_unittest.cc b/remoting/base/protobuf_http_client_unittest.cc index 84c6429a9..737fea1 100644 --- a/remoting/base/protobuf_http_client_unittest.cc +++ b/remoting/base/protobuf_http_client_unittest.cc
@@ -433,6 +433,31 @@ // Stream request tests. +TEST_F(ProtobufHttpClientTest, + StreamRequestFailedToFetchAuthToken_RejectsWithUnauthorizedError) { + base::MockOnceClosure stream_ready_callback; + MockEchoMessageCallback message_callback; + MockStreamClosedCallback stream_closed_callback; + + base::RunLoop run_loop; + + ExpectCallWithToken(/* success= */ false); + + MockEchoResponseCallback response_callback; + EXPECT_CALL(stream_closed_callback, + Run(HasErrorCode(ProtobufHttpStatus::Code::UNAUTHENTICATED))) + .WillOnce([&]() { run_loop.Quit(); }); + + auto request = CreateDefaultTestStreamRequest(); + request->SetStreamReadyCallback(stream_ready_callback.Get()); + request->SetMessageCallback(message_callback.Get()); + request->SetStreamClosedCallback(stream_closed_callback.Get()); + client_.ExecuteRequest(std::move(request)); + + run_loop.Run(); + ASSERT_FALSE(client_.HasPendingRequests()); +} + TEST_F(ProtobufHttpClientTest, StartStreamRequestAndDecodeMessages) { base::MockOnceClosure stream_ready_callback; MockEchoMessageCallback message_callback;
diff --git a/remoting/base/protobuf_http_request_base.h b/remoting/base/protobuf_http_request_base.h index f06aedf..f448165 100644 --- a/remoting/base/protobuf_http_request_base.h +++ b/remoting/base/protobuf_http_request_base.h
@@ -46,7 +46,11 @@ const ProtobufHttpRequestConfig& config() const { return *config_; } protected: + // Called when the protobuf HTTP client failed to get the access token. Note + // that the subclass implementation should not invoke |invalidator_| since the + // request has never been started. virtual void OnAuthFailed(const ProtobufHttpStatus& status) = 0; + virtual void StartRequestInternal( network::mojom::URLLoaderFactory* loader_factory) = 0;
diff --git a/remoting/base/protobuf_http_stream_request.cc b/remoting/base/protobuf_http_stream_request.cc index b042363..eb0f49b 100644 --- a/remoting/base/protobuf_http_stream_request.cc +++ b/remoting/base/protobuf_http_stream_request.cc
@@ -58,7 +58,8 @@ } void ProtobufHttpStreamRequest::OnAuthFailed(const ProtobufHttpStatus& status) { - OnStreamClosed(status); + // Can't call OnStreamClosed here since it invokes the |invalidator_|. + std::move(stream_closed_callback_).Run(status); } void ProtobufHttpStreamRequest::StartRequestInternal(
diff --git a/services/device/public/mojom/hid.mojom b/services/device/public/mojom/hid.mojom index ef5952b8..90bcf2e 100644 --- a/services/device/public/mojom/hid.mojom +++ b/services/device/public/mojom/hid.mojom
@@ -7,6 +7,7 @@ module device.mojom; +[Stable, Extensible] enum HidBusType { kHIDBusTypeUSB = 0, kHIDBusTypeBluetooth = 1, @@ -134,6 +135,7 @@ const uint32 kHIDCollectionTypeVendorMin = 0x80; const uint32 kHIDCollectionTypeVendorMax = 0xff; +[Stable] struct HidUsageAndPage { uint16 usage@0; uint16 usage_page@1; @@ -149,6 +151,7 @@ // See section 6.2.2 of the Device Class Definition for HID for additional // information about the HID report descriptor and parser behavior. // https://www.usb.org/sites/default/files/documents/hid1_11.pdf +[Stable] struct HidReportItem { // True if the usages for this item are defined by |usage_minimum| and // |usage_maximum|. False if the usages for this item are defined by |usages|. @@ -237,6 +240,7 @@ // (bi-directional). When a device requires multiple reports of a single // category, each report is assigned a unique 8-bit ID. Devices that do not use // more than one report of any type may omit report IDs. +[Stable] struct HidReportDescription { // Report ID associated with this report, or zero if the device does not use // report IDs. @@ -255,6 +259,7 @@ // See section 6.2.2.6 of the Device Class Definition for HID for more // information about HID collections. // https://www.usb.org/sites/default/files/documents/hid1_11.pdf +[Stable] struct HidCollectionInfo { // Collection's usage ID. HidUsageAndPage usage@0; @@ -283,6 +288,7 @@ // single physical device may expose multiple logical devices, for instance a // keyboard/mouse combo device would expose separate logical HID devices for // keyboard and mouse functionality. +[Stable] struct HidDeviceInfo { // A random GUID assigned to the device during enumeration. The device GUID is // stable as long as the application is running and the device remains @@ -339,6 +345,7 @@ // A client interface for receiving a notification when HID devices are // physically connected or disconnected. +[Stable] interface HidManagerClient { // Notifies the client that a device is added. DeviceAdded@0(HidDeviceInfo device_info); @@ -351,6 +358,7 @@ // Provides an interface for enumerating available HID devices, registering for // device connection and disconnection notifications, and opening a connection // for reading from and writing to a HID device. +[Stable] interface HidManager { // Enumerates available devices and set as a client of HidManager. // The implementation of HidManager guarantees that the returned callback @@ -384,6 +392,7 @@ // allows a device to define its own packet formats (reports) for sending or // receiving data. The methods in HidConnection send or receive data and specify // which report is used. +[Stable] interface HidConnection { // A |report_id| of 0 is returned via callback if report IDs are not // supported by the device. @@ -403,6 +412,7 @@ // A client interface for receiving a notification when input reports are // received. +[Stable] interface HidConnectionClient { // Notifies the client that an input report was received. A |report_id| of 0 // is passed if report IDs are not used by the device. @@ -419,5 +429,6 @@ // itself as a watcher so it can be notified when the connection is closed. This // allows the service to keep track of whether there are open connections for // the purpose of drawing an indicator icon when a tab is using a HID device. +[Stable] interface HidConnectionWatcher { };
diff --git a/services/network/network_context.cc b/services/network/network_context.cc index 9515038..591c018 100644 --- a/services/network/network_context.cc +++ b/services/network/network_context.cc
@@ -765,12 +765,14 @@ } void NetworkContext::ClearHttpAuthCache(base::Time start_time, + base::Time end_time, ClearHttpAuthCacheCallback callback) { net::HttpNetworkSession* http_session = url_request_context_->http_transaction_factory()->GetSession(); DCHECK(http_session); - http_session->http_auth_cache()->ClearEntriesAddedSince(start_time); + http_session->http_auth_cache()->ClearEntriesAddedBetween(start_time, + end_time); // TODO(mmenke): Use another error code for this, as ERR_ABORTED has somewhat // magical handling with respect to navigations. http_session->CloseAllConnections(net::ERR_ABORTED, "Clearing auth cache");
diff --git a/services/network/network_context.h b/services/network/network_context.h index 664db83..615d42e 100644 --- a/services/network/network_context.h +++ b/services/network/network_context.h
@@ -221,6 +221,7 @@ void ClearHostCache(mojom::ClearDataFilterPtr filter, ClearHostCacheCallback callback) override; void ClearHttpAuthCache(base::Time start_time, + base::Time end_time, ClearHttpAuthCacheCallback callback) override; void ClearReportingCacheReports( mojom::ClearDataFilterPtr filter,
diff --git a/services/network/network_context_unittest.cc b/services/network/network_context_unittest.cc index c0ef9fc..df66f69 100644 --- a/services/network/network_context_unittest.cc +++ b/services/network/network_context_unittest.cc
@@ -1766,20 +1766,38 @@ ASSERT_NE(nullptr, cache->Lookup(origin, net::HttpAuth::AUTH_PROXY, "Realm2", net::HttpAuth::AUTH_SCHEME_BASIC, net::NetworkIsolationKey())); + { + base::RunLoop run_loop; + base::Time test_time; + ASSERT_TRUE(base::Time::FromString("30 May 2018 12:30:00", &test_time)); + network_context->ClearHttpAuthCache(base::Time(), test_time, + run_loop.QuitClosure()); + run_loop.Run(); - base::RunLoop run_loop; - base::Time test_time; - ASSERT_TRUE(base::Time::FromString("30 May 2018 12:30:00", &test_time)); - network_context->ClearHttpAuthCache(test_time, run_loop.QuitClosure()); - run_loop.Run(); + EXPECT_EQ(1u, cache->GetEntriesSizeForTesting()); + EXPECT_EQ(nullptr, cache->Lookup(origin, net::HttpAuth::AUTH_SERVER, + "Realm1", net::HttpAuth::AUTH_SCHEME_BASIC, + net::NetworkIsolationKey())); + EXPECT_NE(nullptr, cache->Lookup(origin, net::HttpAuth::AUTH_PROXY, + "Realm2", net::HttpAuth::AUTH_SCHEME_BASIC, + net::NetworkIsolationKey())); + } + { + base::RunLoop run_loop; + base::Time test_time; + ASSERT_TRUE(base::Time::FromString("30 May 2018 12:30:00", &test_time)); + network_context->ClearHttpAuthCache(test_time, base::Time::Max(), + run_loop.QuitClosure()); + run_loop.Run(); - EXPECT_EQ(1u, cache->GetEntriesSizeForTesting()); - EXPECT_NE(nullptr, cache->Lookup(origin, net::HttpAuth::AUTH_SERVER, "Realm1", - net::HttpAuth::AUTH_SCHEME_BASIC, - net::NetworkIsolationKey())); - EXPECT_EQ(nullptr, cache->Lookup(origin, net::HttpAuth::AUTH_PROXY, "Realm2", - net::HttpAuth::AUTH_SCHEME_BASIC, - net::NetworkIsolationKey())); + EXPECT_EQ(0u, cache->GetEntriesSizeForTesting()); + EXPECT_EQ(nullptr, cache->Lookup(origin, net::HttpAuth::AUTH_SERVER, + "Realm1", net::HttpAuth::AUTH_SCHEME_BASIC, + net::NetworkIsolationKey())); + EXPECT_EQ(nullptr, cache->Lookup(origin, net::HttpAuth::AUTH_PROXY, + "Realm2", net::HttpAuth::AUTH_SCHEME_BASIC, + net::NetworkIsolationKey())); + } } TEST_F(NetworkContextTest, ClearAllHttpAuthCache) { @@ -1817,7 +1835,8 @@ net::NetworkIsolationKey())); base::RunLoop run_loop; - network_context->ClearHttpAuthCache(base::Time(), run_loop.QuitClosure()); + network_context->ClearHttpAuthCache(base::Time(), base::Time::Max(), + run_loop.QuitClosure()); run_loop.Run(); EXPECT_EQ(0u, cache->GetEntriesSizeForTesting()); @@ -1841,6 +1860,7 @@ base::RunLoop run_loop; network_context->ClearHttpAuthCache(base::Time::UnixEpoch(), + base::Time::Max(), base::BindOnce(run_loop.QuitClosure())); run_loop.Run();
diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom index 6dbe137..4e4731b 100644 --- a/services/network/public/mojom/network_context.mojom +++ b/services/network/public/mojom/network_context.mojom
@@ -989,9 +989,10 @@ // non-null, |filter.origins| must be empty. ClearHostCache(ClearDataFilter? filter) => (); - // Clears all entries from the HTTP Auth cache that were added since - // |start_time|. Supports unbounded delete using null Time value. - ClearHttpAuthCache(mojo_base.mojom.Time start_time) => (); + // Clears all entries from the HTTP Auth cache that were between |start_time| + // and |end_time|, with |start_time| being inclusive and |end_time| exclusive. + ClearHttpAuthCache(mojo_base.mojom.Time start_time, + mojo_base.mojom.Time end_time) => (); // Clears all report entries from the reporting cache. Should not be called if // the ENABLE_REPORTING build flag is false.
diff --git a/services/network/test/test_network_context.h b/services/network/test/test_network_context.h index d313473..789be83 100644 --- a/services/network/test/test_network_context.h +++ b/services/network/test/test_network_context.h
@@ -77,6 +77,7 @@ void ClearHostCache(mojom::ClearDataFilterPtr filter, ClearHostCacheCallback callback) override {} void ClearHttpAuthCache(base::Time start_time, + base::Time end_time, ClearHttpAuthCacheCallback callback) override {} void ClearReportingCacheReports( mojom::ClearDataFilterPtr filter,
diff --git a/services/tracing/public/cpp/BUILD.gn b/services/tracing/public/cpp/BUILD.gn index 26f14902..2b2042e53 100644 --- a/services/tracing/public/cpp/BUILD.gn +++ b/services/tracing/public/cpp/BUILD.gn
@@ -13,10 +13,6 @@ flags = [ "ENABLE_LOADER_LOCK_SAMPLING=$enable_loader_lock_sampling" ] } -config("wrap_find_exidx") { - ldflags = [ "-Wl,-wrap,dl_unwind_find_exidx" ] -} - source_set("traced_process") { sources = [ "traced_process.cc", @@ -162,9 +158,6 @@ ] deps += [ "//buildtools/third_party/libunwind" ] include_dirs = [ "//buildtools/third_party/libunwind/trunk/include" ] - - # stack_unwinder_android.cc overrides the dl_unwind_find_exidx function. - all_dependent_configs += [ ":wrap_find_exidx" ] } } # !is_ios && !is_nacl }
diff --git a/services/tracing/public/cpp/stack_sampling/stack_unwinder_android.cc b/services/tracing/public/cpp/stack_sampling/stack_unwinder_android.cc index 2373479..b179af6 100644 --- a/services/tracing/public/cpp/stack_sampling/stack_unwinder_android.cc +++ b/services/tracing/public/cpp/stack_sampling/stack_unwinder_android.cc
@@ -440,43 +440,8 @@ *params->success = true; } -// ARM EXIDX table contains addresses in sorted order with unwind data, each of -// 32 bits. -struct FakeExidx { - uintptr_t pc; - uintptr_t index_data; -}; - } // namespace -extern "C" { - -_Unwind_Ptr __real_dl_unwind_find_exidx(_Unwind_Ptr, int*); - -// Override the default |dl_unwind_find_exidx| function used by libunwind to -// give a fake unwind table just for the handler function. Otherwise call the -// original function. Libunwind marks the cursor invalid if it finds even one -// frame without unwind info. Mocking the info keeps the unwind cursor valid -// after unwind_init_local() within ThreadSignalHandler(). -__attribute__((visibility("default"), noinline)) _Unwind_Ptr -__wrap_dl_unwind_find_exidx(_Unwind_Ptr pc, int* length) { - if (!CFIBacktraceAndroid::is_chrome_address(pc)) { - return __real_dl_unwind_find_exidx(pc, length); - } - // Fake exidx table that is passed to libunwind to work with chrome functions. - // 0x80000000 has high bit set to 1. This means the unwind data is inline and - // not in exception table (section 5 EHABI). 0 on the second high byte causes - // a 0 proceedure to be lsda. But this is never executed since the pc and sp - // will be overridden, before calling unw_step. - static const FakeExidx chrome_exidx_data[] = { - {CFIBacktraceAndroid::executable_start_addr(), 0x80000000}, - {CFIBacktraceAndroid::executable_end_addr(), 0x80000000}}; - *length = base::size(chrome_exidx_data); - return reinterpret_cast<_Unwind_Ptr>(chrome_exidx_data); -} - -} // extern "C" - namespace tracing { // static
diff --git a/testing/buildbot/chromium.android.json b/testing/buildbot/chromium.android.json index 9e8b11b..cde8572e 100644 --- a/testing/buildbot/chromium.android.json +++ b/testing/buildbot/chromium.android.json
@@ -15936,9 +15936,10 @@ { "args": [ "--shared-prefs-file=//chrome/android/shared_preference_files/test/vr_cardboard_skipdon_setupcomplete.json", - "--replace-system-package=com.google.vr.vrcore,//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk", "--gs-results-bucket=chromium-result-details", - "--recover-devices" + "--recover-devices", + "--remove-system-package=com.google.vr.vrcore", + "--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk" ], "merge": { "args": [ @@ -15977,11 +15978,12 @@ { "args": [ "--shared-prefs-file=//chrome/android/shared_preference_files/test/vr_ddview_skipdon_setupcomplete.json", - "--replace-system-package=com.google.vr.vrcore,//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk", "--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_keyboard/vr_keyboard_current.apk", "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--git-revision=${got_revision}" + "--git-revision=${got_revision}", + "--remove-system-package=com.google.vr.vrcore", + "--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk" ], "merge": { "args": [ @@ -16025,12 +16027,13 @@ { "args": [ "--shared-prefs-file=//chrome/android/shared_preference_files/test/vr_ddview_don_setupcomplete.json", - "--replace-system-package=com.google.vr.vrcore,//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk", "--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_keyboard/vr_keyboard_current.apk", "--annotation=Restriction=VR_DON_Enabled", "--vr-don-enabled", "--gs-results-bucket=chromium-result-details", - "--recover-devices" + "--recover-devices", + "--remove-system-package=com.google.vr.vrcore", + "--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk" ], "merge": { "args": [ @@ -16321,9 +16324,10 @@ { "args": [ "--shared-prefs-file=//chrome/android/shared_preference_files/test/vr_cardboard_skipdon_setupcomplete.json", - "--replace-system-package=com.google.vr.vrcore,//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk", "--gs-results-bucket=chromium-result-details", - "--recover-devices" + "--recover-devices", + "--remove-system-package=com.google.vr.vrcore", + "--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk" ], "merge": { "args": [ @@ -16371,11 +16375,12 @@ { "args": [ "--shared-prefs-file=//chrome/android/shared_preference_files/test/vr_ddview_skipdon_setupcomplete.json", - "--replace-system-package=com.google.vr.vrcore,//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk", "--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_keyboard/vr_keyboard_current.apk", "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--git-revision=${got_revision}" + "--git-revision=${got_revision}", + "--remove-system-package=com.google.vr.vrcore", + "--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk" ], "merge": { "args": [ @@ -16428,12 +16433,13 @@ { "args": [ "--shared-prefs-file=//chrome/android/shared_preference_files/test/vr_ddview_don_setupcomplete.json", - "--replace-system-package=com.google.vr.vrcore,//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk", "--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_keyboard/vr_keyboard_current.apk", "--annotation=Restriction=VR_DON_Enabled", "--vr-don-enabled", "--gs-results-bucket=chromium-result-details", - "--recover-devices" + "--recover-devices", + "--remove-system-package=com.google.vr.vrcore", + "--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk" ], "merge": { "args": [ @@ -16769,7 +16775,8 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--replace-system-package=com.google.ar.core,//third_party/arcore-android-sdk/test-apks/arcore/arcore_current.apk" + "--remove-system-package=com.google.ar.core", + "--additional-apk=//third_party/arcore-android-sdk/test-apks/arcore/arcore_current.apk" ], "merge": { "args": [ @@ -23310,6 +23317,7 @@ }, "android-lollipop-arm-rel": { "additional_compile_targets": [ + "android_lint_test", "cronet_test_instrumentation_apk", "errorprone_plugin_tests", "monochrome_static_initializers" @@ -34977,7 +34985,8 @@ "--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk", "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--replace-system-package=com.google.vr.vrcore,//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk" + "--remove-system-package=com.google.vr.vrcore", + "--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk" ], "merge": { "args": [ @@ -37790,9 +37799,10 @@ { "args": [ "--shared-prefs-file=//chrome/android/shared_preference_files/test/vr_cardboard_skipdon_setupcomplete.json", - "--replace-system-package=com.google.vr.vrcore,//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk", "--gs-results-bucket=chromium-result-details", - "--recover-devices" + "--recover-devices", + "--remove-system-package=com.google.vr.vrcore", + "--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk" ], "merge": { "args": [ @@ -37841,11 +37851,12 @@ { "args": [ "--shared-prefs-file=//chrome/android/shared_preference_files/test/vr_ddview_skipdon_setupcomplete.json", - "--replace-system-package=com.google.vr.vrcore,//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk", "--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_keyboard/vr_keyboard_current.apk", "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--git-revision=${got_revision}" + "--git-revision=${got_revision}", + "--remove-system-package=com.google.vr.vrcore", + "--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk" ], "merge": { "args": [ @@ -37899,12 +37910,13 @@ { "args": [ "--shared-prefs-file=//chrome/android/shared_preference_files/test/vr_ddview_don_setupcomplete.json", - "--replace-system-package=com.google.vr.vrcore,//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk", "--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_keyboard/vr_keyboard_current.apk", "--annotation=Restriction=VR_DON_Enabled", "--vr-don-enabled", "--gs-results-bucket=chromium-result-details", - "--recover-devices" + "--recover-devices", + "--remove-system-package=com.google.vr.vrcore", + "--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk" ], "merge": { "args": [ @@ -38094,7 +38106,8 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--replace-system-package=com.google.ar.core,//third_party/arcore-android-sdk/test-apks/arcore/arcore_current.apk" + "--remove-system-package=com.google.ar.core", + "--additional-apk=//third_party/arcore-android-sdk/test-apks/arcore/arcore_current.apk" ], "merge": { "args": [
diff --git a/testing/buildbot/chromium.ci.json b/testing/buildbot/chromium.ci.json index adca557..887e5bc 100644 --- a/testing/buildbot/chromium.ci.json +++ b/testing/buildbot/chromium.ci.json
@@ -91268,9 +91268,10 @@ { "args": [ "--shared-prefs-file=//chrome/android/shared_preference_files/test/vr_cardboard_skipdon_setupcomplete.json", - "--replace-system-package=com.google.vr.vrcore,//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk", "--gs-results-bucket=chromium-result-details", - "--recover-devices" + "--recover-devices", + "--remove-system-package=com.google.vr.vrcore", + "--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk" ], "merge": { "args": [ @@ -91309,11 +91310,12 @@ { "args": [ "--shared-prefs-file=//chrome/android/shared_preference_files/test/vr_ddview_skipdon_setupcomplete.json", - "--replace-system-package=com.google.vr.vrcore,//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk", "--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_keyboard/vr_keyboard_current.apk", "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--git-revision=${got_revision}" + "--git-revision=${got_revision}", + "--remove-system-package=com.google.vr.vrcore", + "--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk" ], "merge": { "args": [ @@ -91357,12 +91359,13 @@ { "args": [ "--shared-prefs-file=//chrome/android/shared_preference_files/test/vr_ddview_don_setupcomplete.json", - "--replace-system-package=com.google.vr.vrcore,//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk", "--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_keyboard/vr_keyboard_current.apk", "--annotation=Restriction=VR_DON_Enabled", "--vr-don-enabled", "--gs-results-bucket=chromium-result-details", - "--recover-devices" + "--recover-devices", + "--remove-system-package=com.google.vr.vrcore", + "--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk" ], "merge": { "args": [ @@ -94478,9 +94481,10 @@ { "args": [ "--shared-prefs-file=//chrome/android/shared_preference_files/test/vr_cardboard_skipdon_setupcomplete.json", - "--replace-system-package=com.google.vr.vrcore,//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk", "--gs-results-bucket=chromium-result-details", - "--recover-devices" + "--recover-devices", + "--remove-system-package=com.google.vr.vrcore", + "--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk" ], "merge": { "args": [ @@ -94528,11 +94532,12 @@ { "args": [ "--shared-prefs-file=//chrome/android/shared_preference_files/test/vr_ddview_skipdon_setupcomplete.json", - "--replace-system-package=com.google.vr.vrcore,//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk", "--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_keyboard/vr_keyboard_current.apk", "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--git-revision=${got_revision}" + "--git-revision=${got_revision}", + "--remove-system-package=com.google.vr.vrcore", + "--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk" ], "merge": { "args": [ @@ -94585,12 +94590,13 @@ { "args": [ "--shared-prefs-file=//chrome/android/shared_preference_files/test/vr_ddview_don_setupcomplete.json", - "--replace-system-package=com.google.vr.vrcore,//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk", "--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_keyboard/vr_keyboard_current.apk", "--annotation=Restriction=VR_DON_Enabled", "--vr-don-enabled", "--gs-results-bucket=chromium-result-details", - "--recover-devices" + "--recover-devices", + "--remove-system-package=com.google.vr.vrcore", + "--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk" ], "merge": { "args": [ @@ -162272,7 +162278,8 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--replace-system-package=com.google.ar.core,//third_party/arcore-android-sdk/test-apks/arcore/arcore_current.apk" + "--remove-system-package=com.google.ar.core", + "--additional-apk=//third_party/arcore-android-sdk/test-apks/arcore/arcore_current.apk" ], "merge": { "args": [ @@ -177931,6 +177938,7 @@ }, "android-lollipop-arm-rel": { "additional_compile_targets": [ + "android_lint_test", "cronet_test_instrumentation_apk", "errorprone_plugin_tests", "monochrome_static_initializers" @@ -189853,7 +189861,8 @@ "--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk", "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--replace-system-package=com.google.vr.vrcore,//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk" + "--remove-system-package=com.google.vr.vrcore", + "--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk" ], "merge": { "args": [ @@ -192671,9 +192680,10 @@ { "args": [ "--shared-prefs-file=//chrome/android/shared_preference_files/test/vr_cardboard_skipdon_setupcomplete.json", - "--replace-system-package=com.google.vr.vrcore,//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk", "--gs-results-bucket=chromium-result-details", - "--recover-devices" + "--recover-devices", + "--remove-system-package=com.google.vr.vrcore", + "--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk" ], "merge": { "args": [ @@ -192722,11 +192732,12 @@ { "args": [ "--shared-prefs-file=//chrome/android/shared_preference_files/test/vr_ddview_skipdon_setupcomplete.json", - "--replace-system-package=com.google.vr.vrcore,//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk", "--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_keyboard/vr_keyboard_current.apk", "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--git-revision=${got_revision}" + "--git-revision=${got_revision}", + "--remove-system-package=com.google.vr.vrcore", + "--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk" ], "merge": { "args": [ @@ -192780,12 +192791,13 @@ { "args": [ "--shared-prefs-file=//chrome/android/shared_preference_files/test/vr_ddview_don_setupcomplete.json", - "--replace-system-package=com.google.vr.vrcore,//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk", "--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_keyboard/vr_keyboard_current.apk", "--annotation=Restriction=VR_DON_Enabled", "--vr-don-enabled", "--gs-results-bucket=chromium-result-details", - "--recover-devices" + "--recover-devices", + "--remove-system-package=com.google.vr.vrcore", + "--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk" ], "merge": { "args": [ @@ -192975,7 +192987,8 @@ "args": [ "--gs-results-bucket=chromium-result-details", "--recover-devices", - "--replace-system-package=com.google.ar.core,//third_party/arcore-android-sdk/test-apks/arcore/arcore_current.apk" + "--remove-system-package=com.google.ar.core", + "--additional-apk=//third_party/arcore-android-sdk/test-apks/arcore/arcore_current.apk" ], "merge": { "args": [
diff --git a/testing/buildbot/client.openscreen.chromium.json b/testing/buildbot/client.openscreen.chromium.json index 8aff5afe..5072a07 100644 --- a/testing/buildbot/client.openscreen.chromium.json +++ b/testing/buildbot/client.openscreen.chromium.json
@@ -1,4 +1,36 @@ { "AAAAA1 AUTOGENERATED FILE DO NOT EDIT": {}, - "AAAAA2 See generate_buildbot_json.py to make changes": {} + "AAAAA2 See generate_buildbot_json.py to make changes": {}, + "chromium_linux64_debug": { + "additional_compile_targets": [ + "chrome/browser/media/router", + "chrome/browser/media/router:openscreen_unittests", + "chrome/browser/media/router:unittests", + "components/cast_certificate", + "components/cast_certificate:unit_tests", + "components/cast_channel", + "components/cast_channel:unit_tests", + "components/mirroring/browser", + "components/mirroring/service:mirroring_service", + "components/mirroring:mirroring_tests", + "components/mirroring:mirroring_unittests", + "components/openscreen_platform" + ] + }, + "chromium_mac_debug": { + "additional_compile_targets": [ + "chrome/browser/media/router", + "chrome/browser/media/router:openscreen_unittests", + "chrome/browser/media/router:unittests", + "components/cast_certificate", + "components/cast_certificate:unit_tests", + "components/cast_channel", + "components/cast_channel:unit_tests", + "components/mirroring/browser", + "components/mirroring/service:mirroring_service", + "components/mirroring:mirroring_tests", + "components/mirroring:mirroring_unittests", + "components/openscreen_platform" + ] + } }
diff --git a/testing/buildbot/generate_buildbot_json.py b/testing/buildbot/generate_buildbot_json.py index 7fb870f..7f95d8d 100755 --- a/testing/buildbot/generate_buildbot_json.py +++ b/testing/buildbot/generate_buildbot_json.py
@@ -1479,6 +1479,8 @@ 'tryserver.devtools-frontend', 'chromium.devtools-frontend']: continue # pragma: no cover + if waterfall['name'] in ['client.openscreen.chromium']: + continue # pragma: no cover raise self.unknown_bot(bot_name, waterfall['name']) # All test suites must be referenced.
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl index 7c8dfe91..c430d4e 100644 --- a/testing/buildbot/gn_isolate_map.pyl +++ b/testing/buildbot/gn_isolate_map.pyl
@@ -96,6 +96,10 @@ "label": "//chrome/test:android_browsertests", "type": "windowed_test_launcher", }, + "android_lint_test": { + "label": "//build/android:android_lint_test", + "type": "additional_compile_target", + }, "android_sync_integration_tests": { "label": "//chrome/test:android_sync_integration_tests", "type": "windowed_test_launcher", @@ -539,6 +543,18 @@ "type": "script", "script": "//testing/scripts/run_wpt_tests.py", }, + "chrome/browser/media/router": { + "label": "//chrome/browser/media/router:router", + "type": "additional_compile_target", + }, + "chrome/browser/media/router:openscreen_unittests": { + "label": "//chrome/browser/media/router:openscreen_unittests", + "type": "additional_compile_target", + }, + "chrome/browser/media/router:unittests": { + "label": "//chrome/browser/media/router:unittests", + "type": "additional_compile_target", + }, "chrome/installer/linux": { "label": "//chrome/installer/linux:linux", "type": "additional_compile_target", @@ -583,6 +599,42 @@ "script": "//testing/scripts/run_performance_tests.py", "type": "script", }, + "components/cast_certificate": { + "label": "//components/cast_certificate:cast_certificate", + "type": "additional_compile_target", + }, + "components/cast_certificate:unit_tests": { + "label": "//components/cast_certificate:unit_tests", + "type": "additional_compile_target", + }, + "components/cast_channel": { + "label": "//components/cast_channel:cast_channel", + "type": "additional_compile_target", + }, + "components/cast_channel:unit_tests": { + "label": "//components/cast_channel:unit_tests", + "type": "additional_compile_target", + }, + "components/mirroring/browser": { + "label": "//components/mirroring/browser:browser", + "type": "additional_compile_target", + }, + "components/mirroring/service:mirroring_service": { + "label": "//components/mirroring/service:mirrroring_service", + "type": "additional_compile_target", + }, + "components/mirroring:mirroring_tests": { + "label": "//components/mirroring:mirroring_tests", + "type": "additional_compile_target", + }, + "components/mirroring:mirroring_unittests": { + "label": "//components/mirroring:mirroring_unittests", + "type": "additional_compile_target", + }, + "components/openscreen_platform": { + "label": "//components/openscreen_platform:openscreen_platform", + "type": "additional_compile_target", + }, "components_browsertests": { "label": "//components:components_browsertests", "type": "windowed_test_launcher",
diff --git a/testing/buildbot/mixins.pyl b/testing/buildbot/mixins.pyl index 22614fb..9053ee0 100644 --- a/testing/buildbot/mixins.pyl +++ b/testing/buildbot/mixins.pyl
@@ -749,6 +749,14 @@ 'io_timeout': 900, }, }, + 'vr_instrumentation_test': { + '$mixin_append': { + 'args': [ + '--remove-system-package=com.google.vr.vrcore', + '--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk', + ], + }, + }, 'walleye': { # Pixel 2 'swarming': {
diff --git a/testing/buildbot/openscreen.ci.json b/testing/buildbot/openscreen.ci.json new file mode 100644 index 0000000..5072a07 --- /dev/null +++ b/testing/buildbot/openscreen.ci.json
@@ -0,0 +1,36 @@ +{ + "AAAAA1 AUTOGENERATED FILE DO NOT EDIT": {}, + "AAAAA2 See generate_buildbot_json.py to make changes": {}, + "chromium_linux64_debug": { + "additional_compile_targets": [ + "chrome/browser/media/router", + "chrome/browser/media/router:openscreen_unittests", + "chrome/browser/media/router:unittests", + "components/cast_certificate", + "components/cast_certificate:unit_tests", + "components/cast_channel", + "components/cast_channel:unit_tests", + "components/mirroring/browser", + "components/mirroring/service:mirroring_service", + "components/mirroring:mirroring_tests", + "components/mirroring:mirroring_unittests", + "components/openscreen_platform" + ] + }, + "chromium_mac_debug": { + "additional_compile_targets": [ + "chrome/browser/media/router", + "chrome/browser/media/router:openscreen_unittests", + "chrome/browser/media/router:unittests", + "components/cast_certificate", + "components/cast_certificate:unit_tests", + "components/cast_channel", + "components/cast_channel:unit_tests", + "components/mirroring/browser", + "components/mirroring/service:mirroring_service", + "components/mirroring:mirroring_tests", + "components/mirroring:mirroring_unittests", + "components/openscreen_platform" + ] + } +}
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl index 9f1ff52a..09fb4e6 100644 --- a/testing/buildbot/test_suite_exceptions.pyl +++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -790,11 +790,14 @@ 'android-code-coverage-native', # https://crbug.com/1018780 ], 'modifications': { - # Use "--replace-system-package" according to crbug.com/931947#c1 + # Use "--remove-system-package" according to crbug.com/931947#c1 'android-nougat-arm64-rel': { 'args': [ - '--replace-system-package=com.google.vr.vrcore,//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk' - ] + # Applying a mixin via an exception doesn't work, so manually apply + # the same arguments as the 'vr_instrumentation_test' mixin. + '--remove-system-package=com.google.vr.vrcore', + '--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk', + ], } }, }, @@ -1669,7 +1672,8 @@ }, 'android-10-arm64-rel': { 'args': [ - '--replace-system-package=com.google.ar.core,//third_party/arcore-android-sdk/test-apks/arcore/arcore_current.apk' + '--remove-system-package=com.google.ar.core', + '--additional-apk=//third_party/arcore-android-sdk/test-apks/arcore/arcore_current.apk', ] }, 'android-nougat-arm64-rel': { @@ -1681,7 +1685,8 @@ }, 'android-pie-arm64-dbg': { 'args': [ - '--replace-system-package=com.google.ar.core,//third_party/arcore-android-sdk/test-apks/arcore/arcore_current.apk' + '--remove-system-package=com.google.ar.core', + '--additional-apk=//third_party/arcore-android-sdk/test-apks/arcore/arcore_current.apk', ] } },
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl index 49ba6121..24f76c49 100644 --- a/testing/buildbot/test_suites.pyl +++ b/testing/buildbot/test_suites.pyl
@@ -27,7 +27,9 @@ 'chrome_public_test_vr_apk-ddready-cardboard': { 'args': [ '--shared-prefs-file=//chrome/android/shared_preference_files/test/vr_cardboard_skipdon_setupcomplete.json', - '--replace-system-package=com.google.vr.vrcore,//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk', + ], + 'mixins': [ + 'vr_instrumentation_test', ], 'swarming': { 'shards': 2, @@ -37,12 +39,12 @@ 'chrome_public_test_vr_apk-ddready-ddview': { 'args': [ '--shared-prefs-file=//chrome/android/shared_preference_files/test/vr_ddview_skipdon_setupcomplete.json', - '--replace-system-package=com.google.vr.vrcore,//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk', '--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_keyboard/vr_keyboard_current.apk', ], 'mixins': [ 'chrome-gold-service-account', 'skia_gold_test', + 'vr_instrumentation_test', ], 'swarming': { 'shards': 4, @@ -52,11 +54,13 @@ 'chrome_public_test_vr_apk-ddready-don-enabled': { 'args': [ '--shared-prefs-file=//chrome/android/shared_preference_files/test/vr_ddview_don_setupcomplete.json', - '--replace-system-package=com.google.vr.vrcore,//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk', '--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_keyboard/vr_keyboard_current.apk', '--annotation=Restriction=VR_DON_Enabled', '--vr-don-enabled', ], + 'mixins': [ + 'vr_instrumentation_test', + ], 'test': 'chrome_public_test_vr_apk', }, },
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl index 4e9c3e4..ef437c33 100644 --- a/testing/buildbot/waterfalls.pyl +++ b/testing/buildbot/waterfalls.pyl
@@ -761,6 +761,7 @@ 'lollipop', ], 'additional_compile_targets': [ + 'android_lint_test', 'cronet_test_instrumentation_apk', 'errorprone_plugin_tests', 'monochrome_static_initializers', @@ -5297,7 +5298,40 @@ 'project': 'openscreen', 'bucket': 'ci', 'name': 'client.openscreen.chromium', - 'machines': {}, + 'machines': { + 'chromium_linux64_debug': { + 'additional_compile_targets': [ + 'chrome/browser/media/router', + 'chrome/browser/media/router:openscreen_unittests', + 'chrome/browser/media/router:unittests', + 'components/cast_certificate', + 'components/cast_certificate:unit_tests', + 'components/cast_channel', + 'components/cast_channel:unit_tests', + 'components/mirroring/browser', + 'components/mirroring/service:mirroring_service', + 'components/mirroring:mirroring_tests', + 'components/mirroring:mirroring_unittests', + 'components/openscreen_platform', + ], + }, + 'chromium_mac_debug': { + 'additional_compile_targets': [ + 'chrome/browser/media/router', + 'chrome/browser/media/router:openscreen_unittests', + 'chrome/browser/media/router:unittests', + 'components/cast_certificate', + 'components/cast_certificate:unit_tests', + 'components/cast_channel', + 'components/cast_channel:unit_tests', + 'components/mirroring/browser', + 'components/mirroring/service:mirroring_service', + 'components/mirroring:mirroring_tests', + 'components/mirroring:mirroring_unittests', + 'components/openscreen_platform', + ], + }, + }, }, { 'project': 'v8',
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index 0e773bc..70913c6f 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -1,4 +1,19 @@ { + "AVFoundationCaptureV2": [ + { + "platforms": [ + "mac" + ], + "experiments": [ + { + "name": "Enabled", + "enable_features": [ + "AVFoundationCaptureV2" + ] + } + ] + } + ], "AddToHomescreenMessaging": [ { "platforms": [
diff --git a/third_party/.gitignore b/third_party/.gitignore index 9c570c2..9a829dce 100644 --- a/third_party/.gitignore +++ b/third_party/.gitignore
@@ -91,7 +91,7 @@ /google_benchmark/src /google_toolbox_for_mac/src /googlemac -/grpc +/grpc/src /gvr-android-sdk/common_library.aar /gvr-android-sdk/test-libraries/controller_test_api.aar /gvr-android-sdk/libgvr_shim_static_*.a
diff --git a/third_party/blink/common/BUILD.gn b/third_party/blink/common/BUILD.gn index cc9b0d16..644f9e77 100644 --- a/third_party/blink/common/BUILD.gn +++ b/third_party/blink/common/BUILD.gn
@@ -149,7 +149,6 @@ "scheduler/web_scheduler_tracked_feature.cc", "service_worker/service_worker_status_code.cc", "service_worker/service_worker_type_converters.cc", - "service_worker/service_worker_utils.cc", "switches.cc", "thread_safe_browser_interface_broker_proxy.cc", "tokens/tokens_mojom_traits.cc",
diff --git a/third_party/blink/common/service_worker/service_worker_utils.cc b/third_party/blink/common/service_worker/service_worker_utils.cc deleted file mode 100644 index 27b770b..0000000 --- a/third_party/blink/common/service_worker/service_worker_utils.cc +++ /dev/null
@@ -1,16 +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/service_worker/service_worker_utils.h" - -#include "base/feature_list.h" -#include "third_party/blink/public/common/features.h" - -namespace blink { - -bool ServiceWorkerUtils::IsImportedScriptUpdateCheckEnabled() { - return true; -} - -} // namespace blink
diff --git a/third_party/blink/public/common/service_worker/service_worker_utils.h b/third_party/blink/public/common/service_worker/service_worker_utils.h index 007ead86..50c5c14 100644 --- a/third_party/blink/public/common/service_worker/service_worker_utils.h +++ b/third_party/blink/public/common/service_worker/service_worker_utils.h
@@ -38,8 +38,6 @@ class ServiceWorkerUtils { public: - static bool BLINK_COMMON_EXPORT IsImportedScriptUpdateCheckEnabled(); - static EagerCodeCacheStrategy BLINK_COMMON_EXPORT GetEagerCodeCacheStrategy(); };
diff --git a/third_party/blink/public/mojom/bluetooth/web_bluetooth.mojom b/third_party/blink/public/mojom/bluetooth/web_bluetooth.mojom index 8c96988..ee8e9dcc 100644 --- a/third_party/blink/public/mojom/bluetooth/web_bluetooth.mojom +++ b/third_party/blink/public/mojom/bluetooth/web_bluetooth.mojom
@@ -110,6 +110,7 @@ struct WebBluetoothRequestDeviceOptions { array<WebBluetoothLeScanFilter>? filters; array<bluetooth.mojom.UUID> optional_services; + array<uint16> optional_manufacturer_data; bool accept_all_devices; }; @@ -161,7 +162,7 @@ bool rssi_is_set; uint8 rssi; map<uint16, array<uint8>> manufacturer_data; - map<string, array<uint8>> service_data; + map<bluetooth.mojom.UUID, array<uint8>> service_data; }; struct WebBluetoothRemoteGATTDescriptor {
diff --git a/third_party/blink/renderer/bindings/core/v8/module_record.cc b/third_party/blink/renderer/bindings/core/v8/module_record.cc index a7d216ef..18d2cab 100644 --- a/third_party/blink/renderer/bindings/core/v8/module_record.cc +++ b/third_party/blink/renderer/bindings/core/v8/module_record.cc
@@ -57,9 +57,12 @@ ScriptPromise ModuleEvaluationResult::GetPromise( ScriptState* script_state) const { DCHECK(base::FeatureList::IsEnabled(features::kTopLevelAwait)); - DCHECK(IsSuccess()); DCHECK(!value_.IsEmpty()); - return ScriptPromise(script_state, value_); + if (IsSuccess()) { + return ScriptPromise(script_state, value_); + } else { + return ScriptPromise::Reject(script_state, value_); + } } ModuleRecordProduceCacheData::ModuleRecordProduceCacheData(
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc index 6fa35cc0..f777aec7 100644 --- a/third_party/blink/renderer/core/frame/local_frame_view.cc +++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -30,6 +30,7 @@ #include <memory> #include <utility> +#include "base/callback.h" #include "base/feature_list.h" #include "base/memory/ptr_util.h" #include "base/metrics/field_trial_params.h" @@ -359,10 +360,8 @@ // updated, consider updating |ForAllNonThrottledLocalFrameViews| too. template <typename Function> void LocalFrameView::ForAllThrottledLocalFrameViews(const Function& function) { - if (!ShouldThrottleRendering()) - return; - - function(*this); + if (ShouldThrottleRendering()) + function(*this); for (Frame* child = frame_->Tree().FirstChild(); child; child = child->Tree().NextSibling()) { @@ -374,6 +373,12 @@ } } +void LocalFrameView::ForAllThrottledLocalFrameViewsForTesting( + base::RepeatingCallback<void(LocalFrameView&)> callback) { + ForAllThrottledLocalFrameViews( + [&callback](LocalFrameView& view) { callback.Run(view); }); +} + template <typename Function> void LocalFrameView::ForAllRemoteFrameViews(const Function& function) { for (Frame* child = frame_->Tree().FirstChild(); child;
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.h b/third_party/blink/renderer/core/frame/local_frame_view.h index 80c0934..72eb54b 100644 --- a/third_party/blink/renderer/core/frame/local_frame_view.h +++ b/third_party/blink/renderer/core/frame/local_frame_view.h
@@ -28,6 +28,7 @@ #include <memory> +#include "base/callback_forward.h" #include "third_party/blink/public/common/metrics/document_update_reason.h" #include "third_party/blink/public/mojom/frame/lifecycle.mojom-blink-forward.h" #include "third_party/blink/public/mojom/scroll/scroll_into_view_params.mojom-blink-forward.h" @@ -860,6 +861,9 @@ template <typename Function> void ForAllThrottledLocalFrameViews(const Function&); + void ForAllThrottledLocalFrameViewsForTesting( + base::RepeatingCallback<void(LocalFrameView&)>); + template <typename Function> void ForAllRemoteFrameViews(const Function&); @@ -1064,6 +1068,7 @@ FRIEND_TEST_ALL_PREFIXES(WebViewTest, DeviceEmulationResetScrollbars); FRIEND_TEST_ALL_PREFIXES(FrameThrottlingTest, GraphicsLayerCollection); + FRIEND_TEST_ALL_PREFIXES(FrameThrottlingTest, ForAllThrottledLocalFrameViews); }; inline void LocalFrameView::IncrementVisuallyNonEmptyCharacterCount(
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_base.cc b/third_party/blink/renderer/core/frame/web_frame_widget_base.cc index fa86fbd..55a8ae57 100644 --- a/third_party/blink/renderer/core/frame/web_frame_widget_base.cc +++ b/third_party/blink/renderer/core/frame/web_frame_widget_base.cc
@@ -1713,8 +1713,26 @@ ui::ScrollGranularity granularity, cc::ElementId scrollable_area_element_id, blink::WebInputEvent::Type injected_type) { - widget_base_->input_handler().InjectGestureScrollEvent( - device, delta, granularity, scrollable_area_element_id, injected_type); + if (RuntimeEnabledFeatures::ScrollUnificationEnabled()) { + // create a GestureScroll Event and post it to the compositor thread + // TODO(crbug.com/1126098) use original input event's timestamp. + // TODO(crbug.com/1082590) ensure continuity in scroll metrics collection + base::TimeTicks now = base::TimeTicks::Now(); + std::unique_ptr<WebGestureEvent> gesture_event = + WebGestureEvent::GenerateInjectedScrollGesture( + injected_type, now, device, gfx::PointF(0, 0), delta, granularity); + if (injected_type == WebInputEvent::Type::kGestureScrollBegin) { + gesture_event->data.scroll_begin.scrollable_area_element_id = + scrollable_area_element_id.GetStableId(); + gesture_event->data.scroll_begin.main_thread_hit_tested = true; + } + + widget_base_->widget_input_handler_manager() + ->DispatchScrollGestureToCompositor(std::move(gesture_event)); + } else { + widget_base_->input_handler().InjectGestureScrollEvent( + device, delta, granularity, scrollable_area_element_id, injected_type); + } } void WebFrameWidgetBase::DidChangeCursor(const ui::Cursor& cursor) {
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc index ef222d5..d4e9fce1 100644 --- a/third_party/blink/renderer/core/layout/layout_box.cc +++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -677,7 +677,7 @@ void LayoutBox::LayoutSubtreeRoot() { if (RuntimeEnabledFeatures::LayoutNGEnabled() && - !NGBlockNode::CanUseNewLayout(*this) && GetCachedLayoutResult()) { + !NGBlockNode::CanUseNewLayout(*this)) { // If this object is laid out by the legacy engine, while its containing // block is laid out by NG, it means that we normally (when laying out // starting at the real root, i.e. LayoutView) enter layout of this object @@ -685,43 +685,25 @@ // structure, which makes legacy layout behave when managed by NG. Make a // short detour via NG just to set things up to re-enter legacy layout // correctly. - DCHECK_EQ(PhysicalFragmentCount(), 1u); - LayoutPoint old_location = Location(); + if (const NGLayoutResult* result = GetCachedLayoutResult()) { + DCHECK_EQ(PhysicalFragmentCount(), 1u); + LayoutPoint old_location = Location(); - // Make a copy of the cached constraint space, since we'll overwrite the - // layout result object as part of performing layout. - auto constraint_space = - GetCachedLayoutResult()->GetConstraintSpaceForCaching(); + // Make a copy of the cached constraint space, since we'll overwrite the + // layout result object as part of performing layout. + auto constraint_space = result->GetConstraintSpaceForCaching(); - NGBlockNode(this).Layout(constraint_space); + NGBlockNode(this).Layout(constraint_space); - // Restore the old location. While it's usually the job of the containing - // block to position its children, out-of-flow positioned objects set - // their own position, which could be wrong in this case. - SetLocation(old_location); - } else { - UpdateLayout(); - } - - // If this box has an associated layout-result, rebuild the spine of the - // fragment-tree to ensure consistency. - if (PhysicalFragmentCount() && - RuntimeEnabledFeatures::LayoutNGFragmentItemEnabled()) { - LayoutBlock* cb = ContainingBlock(); - while (NGBlockNode::CanUseNewLayout(*cb)) { - if (cb->measure_result_) { - cb->measure_result_ = - NGLayoutResult::CloneWithPostLayoutFragments(*cb->measure_result_); - } - for (scoped_refptr<const NGLayoutResult>& layout_result : - cb->layout_results_) { - // Create and set a new identical result. - layout_result = - NGLayoutResult::CloneWithPostLayoutFragments(*layout_result); - } - cb = cb->ContainingBlock(); + // Restore the old location. While it's usually the job of the containing + // block to position its children, out-of-flow positioned objects set + // their own position, which could be wrong in this case. + SetLocation(old_location); + return; } } + + UpdateLayout(); } void LayoutBox::UpdateLayout() {
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.cc index 92d4ece..ff5c702fd 100644 --- a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.cc +++ b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.cc
@@ -134,7 +134,7 @@ NGFragmentItem::NGFragmentItem(const NGPhysicalBoxFragment& box, TextDirection resolved_direction) : layout_object_(box.GetLayoutObject()), - box_(&box, /* descendants_count */ 1), + box_({&box, /* descendants_count */ 1}), rect_({PhysicalOffset(), box.Size()}), type_(kBox), style_variant_(static_cast<unsigned>(box.StyleVariant())), @@ -214,7 +214,10 @@ break; } - if (source.IsInkOverflowComputed()) { + // Copy |ink_overflow_| only for text items, because ink overflow for other + // items may be chnaged even in simplified layout or when reusing lines, and + // that they need to be re-computed anyway. + if (IsText() && source.IsInkOverflowComputed()) { ink_overflow_type_ = source.InkOverflowType(); new (&ink_overflow_) NGInkOverflow(source.InkOverflowType(), source.ink_overflow_); @@ -332,16 +335,6 @@ return false; } -NGFragmentItem::BoxItem::BoxItem(const BoxItem& other) - : box_fragment(other.box_fragment->PostLayout()), - descendants_count(other.descendants_count) {} - -NGFragmentItem::BoxItem::BoxItem( - scoped_refptr<const NGPhysicalBoxFragment> box_fragment, - wtf_size_t descendants_count) - : box_fragment(std::move(box_fragment)), - descendants_count(descendants_count) {} - const NGPhysicalBoxFragment* NGFragmentItem::BoxItem::PostLayout() const { if (box_fragment) return box_fragment->PostLayout();
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h index 20bd154..e8b5dd9 100644 --- a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h +++ b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item.h
@@ -33,8 +33,9 @@ // Represents regular text that exists in the DOM. struct TextItem { scoped_refptr<const ShapeResultView> shape_result; - // TODO(kojii): |text_offset| should match to the offset in |shape_result|. - // Consider if we should remove them, or if keeping them is easier. + // TODO(kojii): |start_offset| and |end_offset| should match to the offset + // in |shape_result|. Consider if we should remove them, or if keeping them + // is easier. const NGTextOffset text_offset; }; // Represents text generated by the layout engine, e.g., hyphen or ellipsis. @@ -50,11 +51,6 @@ // Represents a box fragment appeared in a line. This includes inline boxes // (e.g., <span>text</span>) and atomic inlines. struct BoxItem { - // This copy constructor looks up the "post-layout" fragment. - BoxItem(const BoxItem&); - BoxItem(scoped_refptr<const NGPhysicalBoxFragment>, - wtf_size_t descendants_count); - // If this item is an inline box, its children are stored as following // items. |descendants_count_| has the number of such items. //
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item_test.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item_test.cc index 235e78a7..3b5711d7 100644 --- a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item_test.cc +++ b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_item_test.cc
@@ -106,14 +106,15 @@ EXPECT_NE(line_item->LineBoxFragment(), nullptr); NGFragmentItem copy_of_line(*line_item); EXPECT_EQ(copy_of_line.LineBoxFragment(), line_item->LineBoxFragment()); - EXPECT_TRUE(copy_of_line.IsInkOverflowComputed()); + // Ink overflow is not copied for line items. See |NGFragmentItem| copy ctor. + EXPECT_FALSE(copy_of_line.IsInkOverflowComputed()); // Test moving a line item. NGFragmentItem move_of_line(std::move(copy_of_line)); EXPECT_EQ(move_of_line.LineBoxFragment(), line_item->LineBoxFragment()); // After the move, the source fragment should be released. EXPECT_EQ(copy_of_line.LineBoxFragment(), nullptr); - EXPECT_TRUE(move_of_line.IsInkOverflowComputed()); + EXPECT_FALSE(move_of_line.IsInkOverflowComputed()); // To test moving ink overflow, add an ink overflow to |move_of_line|. PhysicalRect not_small_ink_overflow_rect(0, 0, 5000, 100);
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.cc index 754d0f5..882fbec1 100644 --- a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.cc +++ b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.cc
@@ -44,22 +44,13 @@ size_(builder->items_.size()), size_of_earlier_fragments_(0) { NGFragmentItemsBuilder::ItemWithOffsetList& source_items = builder->items_; - for (wtf_size_t i = 0; i < size_; ++i) { + for (unsigned i = 0; i < size_; ++i) { // Call the move constructor to move without |AddRef|. Items in // |NGFragmentItemsBuilder| are not used after |this| was constructed. new (&items_[i]) NGFragmentItem(std::move(source_items[i].item)); } } -NGFragmentItems::NGFragmentItems(const NGFragmentItems& other) - : text_content_(other.text_content_), - first_line_text_content_(other.first_line_text_content_), - size_(other.size_), - size_of_earlier_fragments_(other.size_of_earlier_fragments_) { - for (wtf_size_t i = 0; i < size_; ++i) - new (&items_[i]) NGFragmentItem(other.items_[i]); -} - NGFragmentItems::~NGFragmentItems() { for (unsigned i = 0; i < size_; ++i) items_[i].~NGFragmentItem();
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.h b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.h index a3dca82..a8e65cd 100644 --- a/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.h +++ b/third_party/blink/renderer/core/layout/ng/inline/ng_fragment_items.h
@@ -19,7 +19,6 @@ // transformed to a flat list of |NGFragmentItem| and stored in this class. class CORE_EXPORT NGFragmentItems { public: - NGFragmentItems(const NGFragmentItems& other); explicit NGFragmentItems(NGFragmentItemsBuilder* builder); ~NGFragmentItems();
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc b/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc index 5be0d44..7689a4d 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc +++ b/third_party/blink/renderer/core/layout/ng/ng_layout_result.cc
@@ -38,14 +38,6 @@ } // namespace -// static -scoped_refptr<const NGLayoutResult> -NGLayoutResult::CloneWithPostLayoutFragments(const NGLayoutResult& other) { - return base::AdoptRef(new NGLayoutResult( - other, NGPhysicalBoxFragment::CloneWithPostLayoutFragments( - To<NGPhysicalBoxFragment>(other.PhysicalFragment())))); -} - NGLayoutResult::NGLayoutResult( NGBoxFragmentBuilderPassKey passkey, scoped_refptr<const NGPhysicalContainerFragment> physical_fragment, @@ -168,29 +160,6 @@ } NGLayoutResult::NGLayoutResult( - const NGLayoutResult& other, - scoped_refptr<const NGPhysicalContainerFragment> physical_fragment) - : space_(other.space_), - physical_fragment_(std::move(physical_fragment)), - intrinsic_block_size_(other.intrinsic_block_size_), - bitfields_(other.bitfields_) { - if (HasRareData()) { - rare_data_ = new RareData(*other.rare_data_); - } else if (!bitfields_.has_oof_positioned_offset) { - bfc_offset_ = other.bfc_offset_; - } else { - DCHECK(physical_fragment_->IsOutOfFlowPositioned()); - oof_positioned_offset_ = other.oof_positioned_offset_; - } - - DCHECK_EQ(physical_fragment_->Size(), other.physical_fragment_->Size()); - -#if DCHECK_IS_ON() - has_valid_space_ = other.has_valid_space_; -#endif -} - -NGLayoutResult::NGLayoutResult( scoped_refptr<const NGPhysicalContainerFragment> physical_fragment, NGContainerFragmentBuilder* builder) : space_(builder->space_ ? NGConstraintSpace(*builder->space_)
diff --git a/third_party/blink/renderer/core/layout/ng/ng_layout_result.h b/third_party/blink/renderer/core/layout/ng/ng_layout_result.h index ce1ae7d..815c7933 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_layout_result.h +++ b/third_party/blink/renderer/core/layout/ng/ng_layout_result.h
@@ -47,11 +47,6 @@ // large enough to store. }; - // Creates a copy of |other| but uses the "post-layout" fragments to ensure - // fragment-tree consistency. - static scoped_refptr<const NGLayoutResult> CloneWithPostLayoutFragments( - const NGLayoutResult& other); - // Create a copy of NGLayoutResult with |BfcBlockOffset| replaced by the given // parameter. Note, when |bfc_block_offset| is |nullopt|, |BfcBlockOffset| is // still replaced with |nullopt|. @@ -345,11 +340,6 @@ private: friend class MutableForOutOfFlow; - // Creates a copy of NGLayoutResult with a new (but "identical") fragment. - NGLayoutResult( - const NGLayoutResult& other, - scoped_refptr<const NGPhysicalContainerFragment> physical_fragment); - // We don't need the copy constructor, move constructor, copy // assigmnment-operator, or move assignment-operator today. // Delete these to clarify that they will not work because a |RefCounted|
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc index e3702e5..37ae37f 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc +++ b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.cc
@@ -38,7 +38,6 @@ } // namespace -// static scoped_refptr<const NGPhysicalBoxFragment> NGPhysicalBoxFragment::Create( NGBoxFragmentBuilder* builder, WritingMode block_or_line_writing_mode) { @@ -55,12 +54,16 @@ builder->table_collapsed_borders_ || builder->table_collapsed_borders_geometry_ || builder->table_cell_column_index_; - - wtf_size_t num_fragment_items = - builder->ItemsBuilder() ? builder->ItemsBuilder()->Size() : 0; - size_t byte_size = - ByteSize(num_fragment_items, builder->children_.size(), !borders.IsZero(), - !padding.IsZero(), has_rare_data); + size_t byte_size = sizeof(NGPhysicalBoxFragment) + + sizeof(NGLink) * builder->children_.size() + + (borders.IsZero() ? 0 : sizeof(borders)) + + (padding.IsZero() ? 0 : sizeof(padding)) + + (has_rare_data ? sizeof(RareData) : 0); + if (const NGFragmentItemsBuilder* items_builder = builder->ItemsBuilder()) { + // Omit |NGFragmentItems| if there were no items; e.g., display-lock. + if (items_builder->Size()) + byte_size += NGFragmentItems::ByteSizeFor(items_builder->Size()); + } // We store the children list inline in the fragment as a flexible // array. Therefore, we need to make sure to allocate enough space for @@ -74,36 +77,6 @@ return base::AdoptRef(static_cast<NGPhysicalBoxFragment*>(data)); } -// static -scoped_refptr<const NGPhysicalBoxFragment> -NGPhysicalBoxFragment::CloneWithPostLayoutFragments( - const NGPhysicalBoxFragment& other) { - // The size of the new fragment shouldn't differ from the old one. - wtf_size_t num_fragment_items = other.Items() ? other.Items()->Size() : 0; - size_t byte_size = - ByteSize(num_fragment_items, other.num_children_, other.has_borders_, - other.has_padding_, other.has_rare_data_); - - void* data = ::WTF::Partitions::FastMalloc( - byte_size, ::WTF::GetStringWithTypeName<NGPhysicalBoxFragment>()); - new (data) NGPhysicalBoxFragment(PassKey(), other); - return base::AdoptRef(static_cast<NGPhysicalBoxFragment*>(data)); -} - -// static -size_t NGPhysicalBoxFragment::ByteSize(wtf_size_t num_fragment_items, - wtf_size_t num_children, - bool has_borders, - bool has_padding, - bool has_rare_data) { - return sizeof(NGPhysicalBoxFragment) + - NGFragmentItems::ByteSizeFor(num_fragment_items) + - sizeof(NGLink) * num_children + - (has_borders ? sizeof(NGPhysicalBoxStrut) : 0) + - (has_padding ? sizeof(NGPhysicalBoxStrut) : 0) + - (has_rare_data ? sizeof(NGPhysicalBoxFragment::RareData) : 0); -} - NGPhysicalBoxFragment::NGPhysicalBoxFragment( PassKey key, NGBoxFragmentBuilder* builder, @@ -182,30 +155,6 @@ #endif } -NGPhysicalBoxFragment::NGPhysicalBoxFragment(PassKey key, - const NGPhysicalBoxFragment& other) - : NGPhysicalContainerFragment(other, children_), - baseline_(other.baseline_), - last_baseline_(other.last_baseline_) { - if (has_fragment_items_) { - NGFragmentItems* items = - const_cast<NGFragmentItems*>(ComputeItemsAddress()); - new (items) NGFragmentItems(*other.ComputeItemsAddress()); - } - if (has_borders_) { - *const_cast<NGPhysicalBoxStrut*>(ComputeBordersAddress()) = - *other.ComputeBordersAddress(); - } - if (has_padding_) { - *const_cast<NGPhysicalBoxStrut*>(ComputePaddingAddress()) = - *other.ComputePaddingAddress(); - } - if (has_rare_data_) { - new (const_cast<RareData*>(ComputeRareDataAddress())) - RareData(*other.ComputeRareDataAddress()); - } -} - NGPhysicalBoxFragment::RareData::RareData(NGBoxFragmentBuilder* builder, PhysicalSize size) : mathml_paint_info(std::move(builder->mathml_paint_info_)) { @@ -241,22 +190,6 @@ table_cell_column_index = *builder->table_cell_column_index_; } -NGPhysicalBoxFragment::RareData::RareData(const RareData& other) - : oof_positioned_fragmentainer_descendants( - other.oof_positioned_fragmentainer_descendants), - mathml_paint_info(other.mathml_paint_info - ? new NGMathMLPaintInfo(*other.mathml_paint_info) - : nullptr), - table_grid_rect(other.table_grid_rect), - table_column_geometries(other.table_column_geometries), - table_collapsed_borders(other.table_collapsed_borders), - table_collapsed_borders_geometry( - other.table_collapsed_borders_geometry - ? new NGTableFragmentData::CollapsedBordersGeometry( - *other.table_collapsed_borders_geometry) - : nullptr), - table_cell_column_index(other.table_cell_column_index) {} - scoped_refptr<const NGLayoutResult> NGPhysicalBoxFragment::CloneAsHiddenForPaint() const { const ComputedStyle& style = Style();
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h index f323308..66da3dd 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h +++ b/third_party/blink/renderer/core/layout/ng/ng_physical_box_fragment.h
@@ -27,10 +27,6 @@ static scoped_refptr<const NGPhysicalBoxFragment> Create( NGBoxFragmentBuilder* builder, WritingMode block_or_line_writing_mode); - // Creates a copy of |other| but uses the "post-layout" fragments to ensure - // fragment-tree consistency. - static scoped_refptr<const NGPhysicalBoxFragment> - CloneWithPostLayoutFragments(const NGPhysicalBoxFragment& other); using PassKey = util::PassKey<NGPhysicalBoxFragment>; NGPhysicalBoxFragment(PassKey, @@ -40,8 +36,6 @@ bool has_rare_data, WritingMode block_or_line_writing_mode); - NGPhysicalBoxFragment(PassKey, const NGPhysicalBoxFragment& other); - scoped_refptr<const NGLayoutResult> CloneAsHiddenForPaint() const; ~NGPhysicalBoxFragment() { @@ -230,19 +224,12 @@ } private: - static size_t ByteSize(wtf_size_t num_fragment_items, - wtf_size_t num_children, - bool has_borders, - bool has_padding, - bool has_rare_data); - struct RareData { - RareData(const RareData&); RareData(NGBoxFragmentBuilder*, PhysicalSize size); Vector<NGPhysicalOutOfFlowPositionedNode> oof_positioned_fragmentainer_descendants; - const std::unique_ptr<const NGMathMLPaintInfo> mathml_paint_info; + const std::unique_ptr<NGMathMLPaintInfo> mathml_paint_info; // TablesNG rare data. PhysicalRect table_grid_rect;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.cc b/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.cc index 7998a21..c9ddcab 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.cc +++ b/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.cc
@@ -89,35 +89,6 @@ } } -NGPhysicalContainerFragment::NGPhysicalContainerFragment( - const NGPhysicalContainerFragment& other, - NGLink* buffer) - : NGPhysicalFragment(other), - num_children_(other.num_children_), - break_token_(other.break_token_), - oof_positioned_descendants_( - other.oof_positioned_descendants_ - ? new Vector<NGPhysicalOutOfFlowPositionedNode>( - *other.oof_positioned_descendants_) - : nullptr), - buffer_(buffer) { - // To ensure the fragment tree is consistent, use the post-layout fragment. - for (wtf_size_t i = 0; i < num_children_; ++i) { - buffer[i].offset = other.buffer_[i].offset; - scoped_refptr<const NGPhysicalFragment> post_layout = - other.buffer_[i]->PostLayout(); - // While making the fragment tree consistent, we need to also clone any - // fragmentainer fragments, as they don't nessecerily have their result - // stored on the layout-object tree. - if (post_layout->IsFragmentainerBox()) { - post_layout = NGPhysicalBoxFragment::CloneWithPostLayoutFragments( - To<NGPhysicalBoxFragment>(*post_layout)); - } - new (&buffer[i].fragment) - scoped_refptr<const NGPhysicalFragment>(std::move(post_layout)); - } -} - NGPhysicalContainerFragment::~NGPhysicalContainerFragment() = default; // additional_offset must be offset from the containing_block.
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h b/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h index badf7606..99c2110 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h +++ b/third_party/blink/renderer/core/layout/ng/ng_physical_container_fragment.h
@@ -155,9 +155,6 @@ NGFragmentType, unsigned sub_type); - NGPhysicalContainerFragment(const NGPhysicalContainerFragment& other, - NGLink* buffer); - void AddScrollableOverflowForInlineChild( const NGPhysicalBoxFragment& container, const ComputedStyle& container_style,
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc index 986c0fa..7065f85b 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc +++ b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.cc
@@ -303,46 +303,6 @@ CHECK(layout_object); } -// Even though the other constructors don't initialize many of these fields -// (instead set by their super-classes), the copy constructor does. -NGPhysicalFragment::NGPhysicalFragment(const NGPhysicalFragment& other) - : has_floating_descendants_for_paint_( - other.has_floating_descendants_for_paint_), - has_adjoining_object_descendants_( - other.has_adjoining_object_descendants_), - depends_on_percentage_block_size_( - other.depends_on_percentage_block_size_), - has_propagated_descendants_(other.has_propagated_descendants_), - has_hanging_(other.has_hanging_), - is_inline_formatting_context_(other.is_inline_formatting_context_), - has_fragment_items_(other.has_fragment_items_), - include_border_top_(other.include_border_top_), - include_border_right_(other.include_border_right_), - include_border_bottom_(other.include_border_bottom_), - include_border_left_(other.include_border_left_), - has_borders_(other.has_borders_), - has_padding_(other.has_padding_), - is_first_for_node_(other.is_first_for_node_), - has_rare_data_(other.has_rare_data_), - layout_object_(other.layout_object_), - size_(other.size_), - type_(other.type_), - sub_type_(other.sub_type_), - style_variant_(other.style_variant_), - is_hidden_for_paint_(other.is_hidden_for_paint_), - is_math_fraction_(other.is_math_fraction_), - base_or_resolved_direction_(other.base_or_resolved_direction_), - may_have_descendant_above_block_start_( - other.may_have_descendant_above_block_start_), - is_fieldset_container_(other.is_fieldset_container_), - is_legacy_layout_root_(other.is_legacy_layout_root_), - is_painted_atomically_(other.is_painted_atomically_), - has_baseline_(other.has_baseline_), - has_last_baseline_(other.has_last_baseline_), - ink_overflow_computed_(other.ink_overflow_computed_) { - CHECK(layout_object_); -} - // Keep the implementation of the destructor here, to avoid dependencies on // ComputedStyle in the header file. NGPhysicalFragment::~NGPhysicalFragment() = default;
diff --git a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h index 409e55a..55dcacbf 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h +++ b/third_party/blink/renderer/core/layout/ng/ng_physical_fragment.h
@@ -428,8 +428,6 @@ NGFragmentType type, unsigned sub_type); - NGPhysicalFragment(const NGPhysicalFragment& other); - const ComputedStyle& SlowEffectiveStyle() const; const Vector<NGInlineItem>& InlineItemsOfContainingBlock() const;
diff --git a/third_party/blink/renderer/core/mathml/mathml_operator_element.cc b/third_party/blink/renderer/core/mathml/mathml_operator_element.cc index 6aa7ca5..f00f9d2 100644 --- a/third_party/blink/renderer/core/mathml/mathml_operator_element.cc +++ b/third_party/blink/renderer/core/mathml/mathml_operator_element.cc
@@ -61,16 +61,6 @@ {0, 3, kOperatorPropertyFlagsNone}, // Category M }; -MathMLOperatorElement::OperatorContent ParseOperatorContent( - const String& string) { - MathMLOperatorElement::OperatorContent operator_content; - operator_content.characters = string; - operator_content.characters.Ensure16Bit(); - operator_content.is_vertical = Character::IsVerticalMathCharacter( - OperatorCodepoint(operator_content.characters)); - return operator_content; -} - static const QualifiedName& OperatorPropertyFlagToAttributeName( MathMLOperatorElement::OperatorPropertyFlag flag) { switch (flag) { @@ -97,6 +87,27 @@ properties_.dirty_flags = kOperatorPropertyFlagsAll; } +MathMLOperatorElement::OperatorContent +MathMLOperatorElement::ParseOperatorContent() { + MathMLOperatorElement::OperatorContent operator_content; + if (HasOneTextChild()) { + operator_content.characters = textContent(); + operator_content.characters.Ensure16Bit(); + operator_content.is_vertical = Character::IsVerticalMathCharacter( + OperatorCodepoint(operator_content.characters)); + } + return operator_content; +} + +void MathMLOperatorElement::ChildrenChanged( + const ChildrenChange& children_change) { + operator_content_ = base::nullopt; + properties_.dictionary_category = + MathMLOperatorDictionaryCategory::kUndefined; + properties_.dirty_flags = kOperatorPropertyFlagsAll; + MathMLElement::ChildrenChanged(children_change); +} + void MathMLOperatorElement::SetOperatorPropertyDirtyFlagIfNeeded( const AttributeModificationParams& param, const OperatorPropertyFlag& flag, @@ -141,6 +152,11 @@ if (properties_.dictionary_category != MathMLOperatorDictionaryCategory::kUndefined) return; + if (GetOperatorContent().characters.IsEmpty()) { + properties_.dictionary_category = MathMLOperatorDictionaryCategory::kNone; + return; + } + // We first determine the form attribute and use the default spacing and // properties. // https://mathml-refresh.github.io/mathml-core/#dfn-form @@ -226,7 +242,7 @@ const MathMLOperatorElement::OperatorContent& MathMLOperatorElement::GetOperatorContent() { if (!operator_content_) - operator_content_ = ParseOperatorContent(textContent()); + operator_content_ = ParseOperatorContent(); return operator_content_.value(); }
diff --git a/third_party/blink/renderer/core/mathml/mathml_operator_element.h b/third_party/blink/renderer/core/mathml/mathml_operator_element.h index 4a56f53..44f23a9 100644 --- a/third_party/blink/renderer/core/mathml/mathml_operator_element.h +++ b/third_party/blink/renderer/core/mathml/mathml_operator_element.h
@@ -61,6 +61,8 @@ void SetOperatorPropertyDirtyFlagIfNeeded(const AttributeModificationParams&, const OperatorPropertyFlag&, bool& needs_layout); + OperatorContent ParseOperatorContent(); + void ChildrenChanged(const ChildrenChange&) final; }; } // namespace blink
diff --git a/third_party/blink/renderer/core/paint/paint_invalidator.cc b/third_party/blink/renderer/core/paint/paint_invalidator.cc index 32115906..72a7f64 100644 --- a/third_party/blink/renderer/core/paint/paint_invalidator.cc +++ b/third_party/blink/renderer/core/paint/paint_invalidator.cc
@@ -208,7 +208,7 @@ const auto& box = ToLayoutBox(object); PhysicalRect new_rect = box.PhysicalBorderBoxRect(); - if (!box.IsScrollContainer()) + if (!box.ShouldClipOverflow()) new_rect.Unite(box.PhysicalLayoutOverflowRect()); PhysicalRect old_rect = PhysicalRect(PhysicalOffset(), box.PreviousSize()); if (!box.PreviouslyHadNonVisibleOverflow())
diff --git a/third_party/blink/renderer/core/scheduler_integration_tests/frame_throttling_test.cc b/third_party/blink/renderer/core/scheduler_integration_tests/frame_throttling_test.cc index d2d6a05a..45a5c42c 100644 --- a/third_party/blink/renderer/core/scheduler_integration_tests/frame_throttling_test.cc +++ b/third_party/blink/renderer/core/scheduler_integration_tests/frame_throttling_test.cc
@@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/callback.h" +#include "base/test/bind_test_util.h" #include "cc/layers/picture_layer.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/web/web_frame_content_dumper.h" @@ -367,6 +369,37 @@ EXPECT_TRUE(frame_document->View()->ShouldThrottleRendering()); } +TEST_P(FrameThrottlingTest, ForAllThrottledLocalFrameViews) { + // Create a document with a hidden cross-origin subframe. + SimRequest main_resource("https://example.com/", "text/html"); + SimRequest frame_resource("https://example.com/iframe.html", "text/html"); + LoadURL("https://example.com/"); + main_resource.Complete(R"HTML( + <iframe id="frame" sandbox src="iframe.html" + style="transform: translateY(480px)"> + )HTML"); + frame_resource.Complete("<!doctype html>"); + + DocumentLifecycle::AllowThrottlingScope throttling_scope( + GetDocument().Lifecycle()); + CompositeFrame(); + + auto* frame_element = + To<HTMLIFrameElement>(GetDocument().getElementById("frame")); + auto* frame_document = frame_element->contentDocument(); + // Hidden cross origin frames are throttled. + EXPECT_TRUE(frame_document->View()->ShouldThrottleRendering()); + // Main frame is not throttled. + EXPECT_FALSE(GetDocument().View()->ShouldThrottleRendering()); + + unsigned throttled_count = 0; + auto throttled_callback = base::BindLambdaForTesting( + [&throttled_count](LocalFrameView&) { throttled_count++; }); + GetDocument().View()->ForAllThrottledLocalFrameViewsForTesting( + throttled_callback); + EXPECT_EQ(1u, throttled_count); +} + TEST_P(FrameThrottlingTest, HiddenCrossOriginZeroByZeroFramesAreNotThrottled) { // Create a document with doubly nested iframes. SimRequest main_resource("https://example.com/", "text/html");
diff --git a/third_party/blink/renderer/core/script/modulator_impl_base.cc b/third_party/blink/renderer/core/script/modulator_impl_base.cc index 62fe7d4b..6ce90f6a 100644 --- a/third_party/blink/renderer/core/script/modulator_impl_base.cc +++ b/third_party/blink/renderer/core/script/modulator_impl_base.cc
@@ -8,6 +8,7 @@ #include "third_party/blink/public/mojom/devtools/console_message.mojom-blink.h" #include "third_party/blink/public/platform/task_type.h" #include "third_party/blink/renderer/bindings/core/v8/module_record.h" +#include "third_party/blink/renderer/bindings/core/v8/script_function.h" #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h" #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h" #include "third_party/blink/renderer/core/execution_context/execution_context.h" @@ -48,6 +49,24 @@ ModulatorImplBase::~ModulatorImplBase() {} +class ModuleEvaluationRejectionCallback final : public ScriptFunction { + public: + explicit ModuleEvaluationRejectionCallback(ScriptState* script_state) + : ScriptFunction(script_state) {} + + static v8::Local<v8::Function> CreateFunction(ScriptState* script_state) { + ModuleEvaluationRejectionCallback* self = + MakeGarbageCollected<ModuleEvaluationRejectionCallback>(script_state); + return self->BindToV8Function(); + } + + private: + ScriptValue Call(ScriptValue value) override { + ModuleRecord::ReportException(GetScriptState(), value.V8Value()); + return ScriptValue(); + } +}; + bool ModulatorImplBase::IsScriptingDisabled() const { return !GetExecutionContext()->CanExecuteScripts(kAboutToExecuteScript); } @@ -351,15 +370,17 @@ v8::MicrotasksScope::kRunMicrotasks); ScriptState::EscapableScope scope(script_state_); - // <spec step="5">Let evaluationStatus be null.</spec> - // - // |result| corresponds to "evaluationStatus of [[Type]]: throw". + // Without TLA: <spec step="5">Let evaluationStatus be null.</spec> ModuleEvaluationResult result = ModuleEvaluationResult::Empty(); // <spec step="6">If script's error to rethrow is not null, ...</spec> if (module_script->HasErrorToRethrow()) { - // <spec step="6">... then set evaluationStatus to Completion { [[Type]]: - // throw, [[Value]]: script's error to rethrow, [[Target]]: empty }.</spec> + // Without TLA: <spec step="6">... then set evaluationStatus to Completion + // { [[Type]]: throw, [[Value]]: script's error to rethrow, + // [[Target]]: empty }.</spec> + // With TLA: <spec step="5">If script's error to rethrow is not null, + // then let valuationPromise be a promise rejected with script's error + // to rethrow.</spec> result = ModuleEvaluationResult::FromException( module_script->CreateErrorToRethrow().V8Value()); } else { @@ -387,25 +408,36 @@ } } - // <spec step="8">If evaluationStatus is an abrupt completion, then:</spec> - if (result.IsException()) { - // <spec step="8.1">If rethrow errors is true, rethrow the exception given - // by evaluationStatus.[[Value]].</spec> - if (capture_error == CaptureEvalErrorFlag::kCapture) - return result.Escape(&scope); - - // <spec step="8.2">Otherwise, report the exception given by - // evaluationStatus.[[Value]] for script.</spec> - ModuleRecord::ReportException(script_state_, result.GetException()); - } - - // <spec step="9">Clean up after running script with settings.</spec> - // - Partially implemented in MicrotaskScope destructor and the - // - ScriptState::EscapableScope destructor. - if (base::FeatureList::IsEnabled(features::kTopLevelAwait)) + if (base::FeatureList::IsEnabled(features::kTopLevelAwait)) { + if (capture_error == CaptureEvalErrorFlag::kReport) { + // <spec step="7"> If report errors is true, then upon rejection of + // evaluationPromise with reason, report the exception given by reason + // for script.</spec> + v8::Local<v8::Function> callback_failure = + ModuleEvaluationRejectionCallback::CreateFunction(script_state_); + // Add a rejection handler to report back errors once the result promise + // is rejected. + result.GetPromise(script_state_) + .Then(v8::Local<v8::Function>(), callback_failure); + } return result.Escape(&scope); - else + } else { + // <spec step="8">If evaluationStatus is an abrupt completion, then:</spec> + if (result.IsException()) { + // <spec step="8.1">If rethrow errors is true, rethrow the exception given + // by evaluationStatus.[[Value]].</spec> + if (capture_error == CaptureEvalErrorFlag::kCapture) + return result.Escape(&scope); + + // <spec step="8.2">Otherwise, report the exception given by + // evaluationStatus.[[Value]] for script.</spec> + ModuleRecord::ReportException(script_state_, result.GetException()); + } + // <spec step="8">Clean up after running script with settings.</spec> + // - Partially implement in MicrotaskScope destructor and the + // - ScriptState::EscapableScope destructor. return ModuleEvaluationResult::Empty(); + } } void ModulatorImplBase::Trace(Visitor* visitor) const {
diff --git a/third_party/blink/renderer/modules/bluetooth/bluetooth.cc b/third_party/blink/renderer/modules/bluetooth/bluetooth.cc index 4433bf5..22c2f8c5 100644 --- a/third_party/blink/renderer/modules/bluetooth/bluetooth.cc +++ b/third_party/blink/renderer/modules/bluetooth/bluetooth.cc
@@ -160,6 +160,13 @@ result->optional_services.push_back(validated_optional_service); } } + + if (options->hasOptionalManufacturerData()) { + for (const uint16_t manufacturer_code : + options->optionalManufacturerData()) { + result->optional_manufacturer_data.push_back(manufacturer_code); + } + } } ScriptPromise Bluetooth::getAvailability(ScriptState* script_state,
diff --git a/third_party/blink/renderer/modules/bluetooth/request_device_options.idl b/third_party/blink/renderer/modules/bluetooth/request_device_options.idl index 6987c61..ebc8f95 100644 --- a/third_party/blink/renderer/modules/bluetooth/request_device_options.idl +++ b/third_party/blink/renderer/modules/bluetooth/request_device_options.idl
@@ -7,5 +7,6 @@ dictionary RequestDeviceOptions { sequence<BluetoothLEScanFilterInit> filters; sequence<BluetoothServiceUUID> optionalServices = []; + [RuntimeEnabled=WebBluetoothWatchAdvertisements] sequence<unsigned short> optionalManufacturerData = []; boolean acceptAllDevices = false; };
diff --git a/third_party/blink/renderer/modules/webaudio/audio_buffer_source_node.cc b/third_party/blink/renderer/modules/webaudio/audio_buffer_source_node.cc index f3ed5a6..574bdd9 100644 --- a/third_party/blink/renderer/modules/webaudio/audio_buffer_source_node.cc +++ b/third_party/blink/renderer/modules/webaudio/audio_buffer_source_node.cc
@@ -406,7 +406,8 @@ const struct InterpolationInfo& interp_info, int frames_processed, unsigned write_index, - unsigned number_of_channels) const { + unsigned number_of_channels, + unsigned buffer_length) const { unsigned* read0 = interp_info.read0; unsigned* read1 = interp_info.read1; float* interp_factor = interp_info.interp_factor; @@ -421,12 +422,21 @@ if (read0[k] == read1[k] && read0[k] >= 1) { // We're at the end of the buffer, so just linearly extrapolate from // the last two samples. + DCHECK_LT(read0[k], buffer_length); float sample1 = source[read0[k] - 1]; float sample2 = source[read0[k]]; sample = sample2 + (sample2 - sample1) * interp_factor[k]; } else { + DCHECK_LT(read0[k], buffer_length); + DCHECK_LT(read1[k], buffer_length); + // TODO(crbug.com/1116104). If read1[k] is out-of-bounds, just return + // 0. Remove this when the underlying problem is fixed. + if (read1[k] >= buffer_length) { + VLOG(1) << "k = " << k << "frames = " << frames_processed + << "read1 = " << read1[k]; + } float sample1 = source[read0[k]]; - float sample2 = source[read1[k]]; + float sample2 = read1[k] < buffer_length ? source[read1[k]] : 0; sample = sample1 + interp_factor[k] * (sample2 - sample1); } destination[write_index] = sample; @@ -465,9 +475,13 @@ ComputeIndices(interp_info, indices_info, frames_to_process, buffer_length, Loop()); - write_index = - ComputeOutput(destination_channels, source_channels, interp_info, - frames_processed, write_index, number_of_channels); + // TODO(crbug.com/1116104): Debugging possible error cases. Remove this (or + // convert to DCHECK) when the underlying issue is fixed. + CHECK_LE(frames_processed, + static_cast<int>(audio_utilities::kRenderQuantumFrames)); + write_index = ComputeOutput(destination_channels, source_channels, + interp_info, frames_processed, write_index, + number_of_channels, buffer_length); if (end_of_buffer_reached) { RenderSilenceAndFinishIfNotLooping(bus, write_index,
diff --git a/third_party/blink/renderer/modules/webaudio/audio_buffer_source_node.h b/third_party/blink/renderer/modules/webaudio/audio_buffer_source_node.h index 7b35c14..4b49a12 100644 --- a/third_party/blink/renderer/modules/webaudio/audio_buffer_source_node.h +++ b/third_party/blink/renderer/modules/webaudio/audio_buffer_source_node.h
@@ -179,7 +179,8 @@ const struct InterpolationInfo& interp_info, int frames_processed, unsigned write_index, - unsigned number_of_channels) const; + unsigned number_of_channels, + unsigned buffer_length) const; // Render silence starting from "index" frame in AudioBus. inline bool RenderSilenceAndFinishIfNotLooping(AudioBus*,
diff --git a/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.cc b/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.cc index 20dd28f..761e65b 100644 --- a/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.cc +++ b/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.cc
@@ -230,11 +230,10 @@ return base::TimeTicks::Now() - start; } -void XRFrameTransport::OnSubmitFrameGpuFence( - const gfx::GpuFenceHandle& handle) { +void XRFrameTransport::OnSubmitFrameGpuFence(gfx::GpuFenceHandle handle) { // We just received a GpuFence, unblock WaitForGpuFenceReceived. waiting_for_previous_frame_fence_ = false; - previous_frame_fence_ = std::make_unique<gfx::GpuFence>(handle); + previous_frame_fence_ = std::make_unique<gfx::GpuFence>(std::move(handle)); } base::TimeDelta XRFrameTransport::WaitForGpuFenceReceived() {
diff --git a/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.h b/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.h index ea42ba83..1f987a2 100644 --- a/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.h +++ b/third_party/blink/renderer/platform/graphics/gpu/xr_frame_transport.h
@@ -72,7 +72,7 @@ // XRPresentationClient void OnSubmitFrameTransferred(bool success) override; void OnSubmitFrameRendered() override; - void OnSubmitFrameGpuFence(const gfx::GpuFenceHandle&) override; + void OnSubmitFrameGpuFence(gfx::GpuFenceHandle) override; HeapMojoReceiver<device::mojom::blink::XRPresentationClient, XRFrameTransport,
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc index d07149df6..f924537d 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc +++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl.cc
@@ -1005,11 +1005,18 @@ if (queue_priority_pair != resource_loading_task_queue_priorities_.end()) return queue_priority_pair->value; - base::Optional<TaskQueue::QueuePriority> fixed_priority = - task_queue->FixedPriority(); - - if (fixed_priority) - return fixed_priority.value(); + // TODO(kdillon): Ordering here is relative to the experiments below. Cleanup + // unused experiment logic so that this switch can be merged with the + // prioritisation type decisions below. + switch (task_queue->GetPrioritisationType()) { + case MainThreadTaskQueue::QueueTraits::PrioritisationType:: + kInternalScriptContinuation: + return TaskQueue::QueuePriority::kVeryHighPriority; + case MainThreadTaskQueue::QueueTraits::PrioritisationType::kBestEffort: + return TaskQueue::QueuePriority::kBestEffortPriority; + default: + break; + } // TODO(shaseley): This should use lower priorities if the frame is // deprioritized. Change this once we refactor and add frame policy/priorities
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc index 87cc1280..60b78aa3 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc +++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_scheduler_impl_unittest.cc
@@ -2428,9 +2428,8 @@ TEST_F(FrameSchedulerImplTest, ContentCaptureHasIdleTaskQueue) { auto task_queue = GetTaskQueue(TaskType::kInternalContentCapture); - EXPECT_TRUE(task_queue->FixedPriority().has_value()); EXPECT_EQ(TaskQueue::QueuePriority::kBestEffortPriority, - task_queue->FixedPriority().value()); + task_queue->GetQueuePriority()); } TEST_F(FrameSchedulerImplTest, ComputePriorityForDetachedFrame) {
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller.cc index 2beb5af..7f982b86 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller.cc +++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller.cc
@@ -102,19 +102,6 @@ .SetFreezeWhenKeepActive(queue_traits.can_be_throttled) .SetFrameScheduler(frame_scheduler_impl_); - switch (queue_traits.prioritisation_type) { - case QueueTraits::PrioritisationType::kInternalScriptContinuation: - queue_creation_params = queue_creation_params.SetFixedPriority( - TaskQueue::QueuePriority::kVeryHighPriority); - break; - case QueueTraits::PrioritisationType::kBestEffort: - queue_creation_params = queue_creation_params.SetFixedPriority( - TaskQueue::QueuePriority::kBestEffortPriority); - break; - default: - break; - } - scoped_refptr<MainThreadTaskQueue> task_queue = main_thread_scheduler_impl_->NewTaskQueue(queue_creation_params); TaskQueueCreated(task_queue);
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller_unittest.cc index 155132e..a09d42f 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller_unittest.cc +++ b/third_party/blink/renderer/platform/scheduler/main_thread/frame_task_queue_controller_unittest.cc
@@ -332,7 +332,8 @@ QueueTraits::PrioritisationType::kFindInPage, QueueTraits::PrioritisationType::kExperimentalDatabase, QueueTraits::PrioritisationType::kJavaScriptTimer, - QueueTraits::PrioritisationType::kHighPriorityLocalFrame)); + QueueTraits::PrioritisationType::kHighPriorityLocalFrame, + QueueTraits::PrioritisationType::kInput)); TEST_P(TaskQueueCreationFromQueueTraitsTest, AddAndRetrieveAllTaskQueues) {
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_helper.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_helper.cc index 882c9af..4bec7b6 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_helper.cc +++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_helper.cc
@@ -78,8 +78,6 @@ scoped_refptr<MainThreadTaskQueue> task_queue = sequence_manager_->CreateTaskQueueWithType<MainThreadTaskQueue>( params.spec, params, main_thread_scheduler_); - if (params.fixed_priority) - task_queue->SetQueuePriority(params.fixed_priority.value()); return task_queue; }
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc index fae9510..a5c96ab0 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc +++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
@@ -183,15 +183,16 @@ base::Optional<base::Time> initial_virtual_time) : sequence_manager_(std::move(sequence_manager)), helper_(sequence_manager_.get(), this), - idle_helper_(&helper_, - this, - "MainThreadSchedulerIdlePeriod", - base::TimeDelta(), - helper_.NewTaskQueue( - MainThreadTaskQueue::QueueCreationParams( - MainThreadTaskQueue::QueueType::kIdle) - .SetFixedPriority( - TaskQueue::QueuePriority::kBestEffortPriority))), + idle_helper_( + &helper_, + this, + "MainThreadSchedulerIdlePeriod", + base::TimeDelta(), + helper_.NewTaskQueue( + MainThreadTaskQueue::QueueCreationParams( + MainThreadTaskQueue::QueueType::kIdle) + .SetPrioritisationType(MainThreadTaskQueue::QueueTraits:: + PrioritisationType::kBestEffort))), render_widget_scheduler_signals_(this), find_in_page_budget_pool_controller_( new FindInPageBudgetPoolController(this)), @@ -207,8 +208,8 @@ memory_purge_task_queue_(helper_.NewTaskQueue( MainThreadTaskQueue::QueueCreationParams( MainThreadTaskQueue::QueueType::kIdle) - .SetFixedPriority( - TaskQueue::QueuePriority::kBestEffortPriority))), + .SetPrioritisationType(MainThreadTaskQueue::QueueTraits:: + PrioritisationType::kBestEffort))), memory_purge_manager_(memory_purge_task_queue_->CreateTaskRunner( TaskType::kMainThreadTaskQueueMemoryPurge)), non_waking_time_domain_(tick_clock()), @@ -296,6 +297,11 @@ find_in_page_budget_pool_controller_->CurrentTaskPriority(); g_main_thread_scheduler = this; + + // Explicitly set the priority of this queue since it is not managed by + // the main thread scheduler. + memory_purge_task_queue_->SetQueuePriority( + ComputePriority(memory_purge_task_queue_.get())); } MainThreadSchedulerImpl::~MainThreadSchedulerImpl() { @@ -2614,19 +2620,19 @@ return frame_scheduler->ComputePriority(task_queue); } - base::Optional<TaskQueue::QueuePriority> fixed_priority = - task_queue->FixedPriority(); - if (fixed_priority) { - return fixed_priority.value(); + switch (task_queue->GetPrioritisationType()) { + case MainThreadTaskQueue::QueueTraits::PrioritisationType::kCompositor: + return main_thread_only().compositor_priority; + case MainThreadTaskQueue::QueueTraits::PrioritisationType::kInput: + return TaskQueue::QueuePriority::kHighestPriority; + case MainThreadTaskQueue::QueueTraits::PrioritisationType::kBestEffort: + return TaskQueue::QueuePriority::kBestEffortPriority; + case MainThreadTaskQueue::QueueTraits::PrioritisationType::kRegular: + return TaskQueue::QueuePriority::kNormalPriority; + default: + NOTREACHED(); + return TaskQueue::QueuePriority::kNormalPriority; } - - if (task_queue->GetPrioritisationType() == - MainThreadTaskQueue::QueueTraits::PrioritisationType::kCompositor) { - return main_thread_only().compositor_priority; - } - - // Default priority. - return TaskQueue::QueuePriority::kNormalPriority; } void MainThreadSchedulerImpl::OnBeginNestedRunLoop() {
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc index d1ad03d..4a1f5b2 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc +++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.cc
@@ -107,7 +107,6 @@ MainThreadSchedulerImpl* main_thread_scheduler) : TaskQueue(std::move(impl), spec), queue_type_(params.queue_type), - fixed_priority_(params.fixed_priority), queue_traits_(params.queue_traits), freeze_when_keep_active_(params.freeze_when_keep_active), web_scheduling_priority_(params.web_scheduling_priority),
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h index 3f28a23..1ee7eeb 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h +++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h
@@ -121,8 +121,9 @@ kJavaScriptTimer = 7, kHighPriorityLocalFrame = 8, kCompositor = 9, // Main-thread only. + kInput = 10, - kCount = 10 + kCount = 11 }; // kPrioritisationTypeWidthBits is the number of bits required @@ -226,13 +227,6 @@ frame_scheduler(nullptr), freeze_when_keep_active(false) {} - QueueCreationParams SetFixedPriority( - base::Optional<base::sequence_manager::TaskQueue::QueuePriority> - priority) { - fixed_priority = priority; - return *this; - } - QueueCreationParams SetFreezeWhenKeepActive(bool value) { freeze_when_keep_active = value; return *this; @@ -319,8 +313,6 @@ QueueType queue_type; base::sequence_manager::TaskQueue::Spec spec; - base::Optional<base::sequence_manager::TaskQueue::QueuePriority> - fixed_priority; FrameSchedulerImpl* frame_scheduler; QueueTraits queue_traits; bool freeze_when_keep_active; @@ -336,11 +328,6 @@ QueueType queue_type() const { return queue_type_; } - base::Optional<base::sequence_manager::TaskQueue::QueuePriority> - FixedPriority() const { - return fixed_priority_; - } - bool CanBeDeferred() const { return queue_traits_.can_be_deferred; } bool CanBeThrottled() const { return queue_traits_.can_be_throttled; } @@ -437,8 +424,6 @@ void ClearReferencesToSchedulers(); const QueueType queue_type_; - const base::Optional<base::sequence_manager::TaskQueue::QueuePriority> - fixed_priority_; const QueueTraits queue_traits_; const bool freeze_when_keep_active_;
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/widget_scheduler.cc b/third_party/blink/renderer/platform/scheduler/main_thread/widget_scheduler.cc index 0e7bc1f..4a2555f 100644 --- a/third_party/blink/renderer/platform/scheduler/main_thread/widget_scheduler.cc +++ b/third_party/blink/renderer/platform/scheduler/main_thread/widget_scheduler.cc
@@ -15,8 +15,8 @@ MainThreadTaskQueue::QueueCreationParams( MainThreadTaskQueue::QueueType::kInput) .SetShouldMonitorQuiescence(true) - .SetFixedPriority( - base::make_optional(TaskQueue::QueuePriority::kHighestPriority))); + .SetPrioritisationType( + MainThreadTaskQueue::QueueTraits::PrioritisationType::kInput)); input_task_runner_ = input_task_queue_->CreateTaskRunner(TaskType::kMainThreadTaskQueueInput); input_task_queue_enabled_voter_ =
diff --git a/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.cc b/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.cc index 9dbd0700..e96ceba 100644 --- a/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.cc +++ b/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.cc
@@ -8,6 +8,7 @@ #include "base/bind.h" #include "base/check_op.h" +#include "base/feature_list.h" #include "base/metrics/histogram_macros.h" #include "base/notreached.h" #include "cc/base/features.h" @@ -389,6 +390,27 @@ } } +void WidgetInputHandlerManager::DispatchScrollGestureToCompositor( + std::unique_ptr<WebGestureEvent> event) { + DCHECK(base::FeatureList::IsEnabled(features::kScrollUnification)); + std::unique_ptr<WebCoalescedInputEvent> web_scoped_gesture_event = + std::make_unique<WebCoalescedInputEvent>(std::move(event), + ui::LatencyInfo()); + DCHECK(compositor_task_runner_); + compositor_task_runner_->PostTask( + FROM_HERE, base::BindOnce(&WidgetInputHandlerManager:: + HandleInputEventWithLatencyInfoOnCompositor, + this, std::move(web_scoped_gesture_event))); +} + +void WidgetInputHandlerManager::HandleInputEventWithLatencyInfoOnCompositor( + std::unique_ptr<WebCoalescedInputEvent> event) { + DCHECK(base::FeatureList::IsEnabled(features::kScrollUnification)); + DCHECK(input_handler_proxy_); + input_handler_proxy_->HandleInputEventWithLatencyInfo(std::move(event), + base::DoNothing()); +} + void WidgetInputHandlerManager::DispatchEvent( std::unique_ptr<WebCoalescedInputEvent> event, mojom::blink::WidgetInputHandler::DispatchEventCallback callback) { @@ -398,7 +420,7 @@ if (!event_is_move) LogInputTimingUMA(); - // Drop input if we are deferring a rendring pipeline phase, unless it's a + // Drop input if we are deferring a rendering pipeline phase, unless it's a // move event. // We don't want users interacting with stuff they can't see, so we drop it. // We allow moves because we need to keep the current pointer location up @@ -436,7 +458,7 @@ return; } - // The InputHandlerProxy will be the first to try handing the event on the + // The InputHandlerProxy will be the first to try handling the event on the // compositor thread. It will respond to this class by calling // DidHandleInputEventSentToCompositor with the result of its attempt. Based // on the resulting disposition, DidHandleInputEventSentToCompositor will
diff --git a/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.h b/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.h index 694a81f..516e5725 100644 --- a/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.h +++ b/third_party/blink/renderer/platform/widget/input/widget_input_handler_manager.h
@@ -98,6 +98,8 @@ const WebGestureEvent& gesture_event, const cc::InputHandlerScrollResult& scroll_result); + void DispatchScrollGestureToCompositor( + std::unique_ptr<WebGestureEvent> event); void DispatchEvent( std::unique_ptr<blink::WebCoalescedInputEvent> event, mojom::blink::WidgetInputHandler::DispatchEventCallback callback); @@ -225,6 +227,9 @@ const WebGestureEvent& gesture_event, const cc::InputHandlerScrollResult& scroll_result); + void HandleInputEventWithLatencyInfoOnCompositor( + std::unique_ptr<WebCoalescedInputEvent>); + // Returns the task runner for the thread that receives input. i.e. the // "Mojo-bound" thread. const scoped_refptr<base::SingleThreadTaskRunner>& InputThreadTaskRunner()
diff --git a/third_party/blink/tools/blinkpy/common/config/builders.json b/third_party/blink/tools/blinkpy/common/config/builders.json index 31786c49..c6d42d28 100644 --- a/third_party/blink/tools/blinkpy/common/config/builders.json +++ b/third_party/blink/tools/blinkpy/common/config/builders.json
@@ -114,9 +114,11 @@ "specifiers": ["Win10", "Release"], "is_try_builder": true }, - "android-webview-pie-arm64-fyi-rel": { + "android-pie-arm64-wpt-rel-non-cq": { "port_name": "android-android-pie", - "specifiers": ["Android", "android_webview", "chrome_android", "Release"], + "specifiers": [ + "Android", "android_weblayer", "android_webview", + "chrome_android", "Release"], "is_try_builder": true } }
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index df42cb4..976f32d 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -1672,6 +1672,11 @@ crbug.com/1022182 external/wpt/html/webappapis/dynamic-markup-insertion/document-write/module-tla-immediate-promise.html [ Skip ] crbug.com/1022182 external/wpt/html/webappapis/dynamic-markup-insertion/document-write/module-tla-import.html [ Skip ] crbug.com/1022182 external/wpt/html/webappapis/dynamic-markup-insertion/document-write/module-tla-promise.html [ Skip ] +crbug.com/1022182 virtual/module-top-level-await/external/wpt/html/semantics/scripting-1/the-script-element/module/evaluation-order-4-tla.html [ Pass ] +crbug.com/1022182 virtual/module-top-level-await/external/wpt/html/webappapis/dynamic-markup-insertion/document-write/module-tla-delayed.html [ Pass ] +crbug.com/1022182 virtual/module-top-level-await/external/wpt/html/webappapis/dynamic-markup-insertion/document-write/module-tla-immediate-promise.html [ Pass ] +crbug.com/1022182 virtual/module-top-level-await/external/wpt/html/webappapis/dynamic-markup-insertion/document-write/module-tla-import.html [ Pass ] +crbug.com/1022182 virtual/module-top-level-await/external/wpt/html/webappapis/dynamic-markup-insertion/document-write/module-tla-promise.html [ Pass ] crbug.com/676270 [ Mac ] external/wpt/css/css-text/hyphens/hyphens-auto-001.html [ Failure ] crbug.com/1022415 [ Mac ] external/wpt/css/css-text/hyphens/hyphens-auto-010.html [ Failure ] @@ -6731,3 +6736,9 @@ # Sheriff 2020-09-17 crbug.com/1129347 [ Mac10.13 Debug ] http/tests/devtools/persistence/persistence-external-change-breakpoints.js [ Pass Failure ] +### virtual/scroll-unification/fast/scrolling/scrollbars/ +virtual/scroll-unification/fast/scrolling/scrollbars/mouse-autoscrolling-on-scrollbar.html [ Failure ] +virtual/scroll-unification/fast/scrolling/scrollbars/mouse-scrolling-on-div-scrollbar.html [ Failure ] +virtual/scroll-unification/fast/scrolling/scrollbars/scrollbar-occluded-by-div.html [ Failure ] +virtual/scroll-unification/fast/scrolling/scrollbars/scrollbar-rtl-manipulation.html [ Failure ] +virtual/scroll-unification/fast/scrolling/scrollbars/dsf-ready/mouse-interactions-dsf-2.html [ Failure ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites index 65c9263..0a84b05 100644 --- a/third_party/blink/web_tests/VirtualTestSuites +++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -1,7 +1,14 @@ [ { "prefix": "module-top-level-await", - "bases": [], + "bases": [ + "external/wpt/html/webappapis/dynamic-markup-insertion/document-write", + "external/wpt/html/semantics/scripting-1/the-script-element/module", + "fast/dom/HTMLScriptElement/module-script.html", + "fast/dom/script-module-with-export-leak.html", + "fast/loader/invalid-module-specifier.html", + "http/tests/devtools/isolated-code-cache/" + ], "args": ["--enable-features=TopLevelAwait"] }, { @@ -743,5 +750,11 @@ "args": ["--enable-threaded-compositing", "--enable-features=CompositeCrossOriginIframes", "--disable-auto-wpt-origin-isolation"] + }, + { + "prefix": "scroll-unification", + "bases": ["fast/scrolling/scrollbars"], + "args": ["--enable-features=ScrollUnification", + "--enable-threaded-compositing"] } ]
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json index 4ab1301..d50e74f 100644 --- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json +++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -168539,7 +168539,7 @@ }, "resources": { "cookie-helper.sub.js": [ - "1a163332e3c1821e690e1659842cd30202440f06", + "50dcdfc5ecbb6dd023eef9ce64beab5e277db406", [] ], "drop.py": [ @@ -168639,6 +168639,14 @@ ] } }, + "schemeful-same-site": { + "resources": { + "navigateToInsecurePostToParent.html": [ + "b81b722bf602035188e819de903fa3b3b1ac109a", + [] + ] + } + }, "secure": { "set-from-http.https.sub.html.headers": [ "f4c9147fac3a1599c9a88dd98caaea54896ced68", @@ -197278,6 +197286,10 @@ "59617c5d72914839cb9b0d5200a38219f64167bc", [] ], + "logical-expected.txt": [ + "0f32b2d70c3d07b5f03bef59d58114fe1b4b865a", + [] + ], "margin-expected.txt": [ "be788697a0fc9f09152ea59fad2a63be5f03be44", [] @@ -275116,6 +275128,40 @@ ] ] }, + "schemeful-same-site": { + "schemeful-iframe-subresource.tentative.html": [ + "13397d241ade942ec2376ac99c6d4f75f11df7f3", + [ + null, + {} + ] + ], + "schemeful-navigation.tentative.html": [ + "5ead2a54c7f989353a1b496eea2ab5b1ff4b3b05", + [ + null, + { + "timeout": "long" + } + ] + ], + "schemeful-subresource.tentative.html": [ + "4ba9286c259f9892020a688bb3c8ca1cecf29508", + [ + null, + { + "timeout": "long" + } + ] + ], + "schemeful-websockets.sub.tentative.html": [ + "8e7c073c997ad4a60b7f6f551328d879c887a2b8", + [ + null, + {} + ] + ] + }, "secure": { "set-from-dom.https.sub.html": [ "46997db18ac738b5c3041e0010c0562db3813616", @@ -296052,7 +296098,7 @@ ] ], "logical.html": [ - "e8085b02787a17c3ccd17d7c0dc8c94d9156b426", + "c068edf751cc36a3bd6de38587bdbfa1660430a5", [ null, {} @@ -367570,7 +367616,7 @@ ] ], "GUM-empty-option-param.https.html": [ - "c7904cb4c7d7b810874c93ff23dcda228c57a369", + "9f79a6dca6d04aa7e293129ade88573dd32eab7c", [ null, {} @@ -367604,6 +367650,13 @@ {} ] ], + "GUM-required-constraint-with-ideal-value.https.html": [ + "705836f1e3cf726bae7f5bdd9e37ae4e8ca83c15", + [ + null, + {} + ] + ], "GUM-trivial-constraint.https.html": [ "ad3776589e5b6ed5ffc37e2b0fee282b4d2643a3", [ @@ -367612,7 +367665,7 @@ ] ], "GUM-unknownkey-option-param.https.html": [ - "b27927222012bf79c31f78a28c4df86ea500ca83", + "965145a2b1905dc3f21740913be460c4970ce5a8", [ null, {}
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/device/watchAdvertisements/service-and-manufacturer-data-filtered-from-event.https.window.js b/third_party/blink/web_tests/external/wpt/bluetooth/device/watchAdvertisements/service-and-manufacturer-data-filtered-from-event.https.window.js new file mode 100644 index 0000000..f6b93ff --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/bluetooth/device/watchAdvertisements/service-and-manufacturer-data-filtered-from-event.https.window.js
@@ -0,0 +1,41 @@ +// META: script=/resources/testdriver.js +// META: script=/resources/testdriver-vendor.js +// META: script=/bluetooth/resources/bluetooth-test.js +// META: script=/bluetooth/resources/bluetooth-fake-devices.js +'use strict'; +const test_desc = `Service and Manufacturer that were not granted with ` + + `requestDevice() are filtered from the advertisement event.`; + +bluetooth_test(async (t) => { + let {device} = await setUpPreconnectedFakeDevice({ + fakeDeviceOptions: { + address: '07:07:07:07:07:07', + knownServiceUUIDs: [uuid1234, uuid5678, uuidABCD], + }, + requestDeviceOptions: { + filters: [{services: [uuid1234]}], + optionalServices: [uuid5678], + optionalManufacturerData: [0x0001] + } + }); + const watcher = new EventWatcher(t, device, ['advertisementreceived']); + + await device.watchAdvertisements(); + assert_true(device.watchingAdvertisements); + + let advertisementreceivedPromise = watcher.wait_for('advertisementreceived'); + await fake_central.simulateAdvertisementReceived( + service_and_manufacturer_data_ad_packet); + let evt = await advertisementreceivedPromise; + assert_equals(evt.device, device); + + // Check that service data is filtered out properly. + assert_data_maps_equal(evt.serviceData, uuid1234, uuid1234Data); + assert_data_maps_equal(evt.serviceData, uuid5678, uuid5678Data); + assert_false(evt.serviceData.has(uuidABCD)); + + // Check that manufacturer data is filtered out properly. + assert_data_maps_equal( + evt.manufacturerData, /*expected_key=*/ 0x0001, manufacturer1Data); + assert_false(evt.manufacturerData.has(0x0002)); +}, test_desc);
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/resources/bluetooth-fake-devices.js b/third_party/blink/web_tests/external/wpt/bluetooth/resources/bluetooth-fake-devices.js index 6d4b30e5..c4d699a 100644 --- a/third_party/blink/web_tests/external/wpt/bluetooth/resources/bluetooth-fake-devices.js +++ b/third_party/blink/web_tests/external/wpt/bluetooth/resources/bluetooth-fake-devices.js
@@ -127,6 +127,34 @@ }, }; +const uuid1234 = BluetoothUUID.getService(0x1234); +const uuid5678 = BluetoothUUID.getService(0x5678); +const uuidABCD = BluetoothUUID.getService(0xABCD); +const manufacturer1Data = new Uint8Array([1, 2]); +const manufacturer2Data = new Uint8Array([3, 4]); +const uuid1234Data = new Uint8Array([5, 6]); +const uuid5678Data = new Uint8Array([7, 8]); +const uuidABCDData = new Uint8Array([9, 10]); +/** + * An advertisement packet object that simulates a device that advertises + * service and manufacturer data. + * @type {ScanResult} + */ +const service_and_manufacturer_data_ad_packet = { + deviceAddress: '07:07:07:07:07:07', + rssi: -10, + scanRecord: { + name: 'LE Device', + uuids: [uuid1234], + manufacturerData: {0x0001: manufacturer1Data, 0x0002: manufacturer2Data}, + serviceData: { + [uuid1234]: uuid1234Data, + [uuid5678]: uuid5678Data, + [uuidABCD]: uuidABCDData + } + } +}; + /** Bluetooth Helpers */ /**
diff --git a/third_party/blink/web_tests/external/wpt/bluetooth/resources/bluetooth-test.js b/third_party/blink/web_tests/external/wpt/bluetooth/resources/bluetooth-test.js index dd38568..b99564b 100644 --- a/third_party/blink/web_tests/external/wpt/bluetooth/resources/bluetooth-test.js +++ b/third_party/blink/web_tests/external/wpt/bluetooth/resources/bluetooth-test.js
@@ -363,3 +363,17 @@ assert_equals(properties[key], expected_properties[key]); } } + +/** + * Asserts that |data_map| contains |expected_key|, and that the uint8 values + * for |expected_key| matches |expected_value|. + */ +function assert_data_maps_equal(data_map, expected_key, expected_value) { + assert_true(data_map.has(expected_key)); + + const value = new Uint8Array(data_map.get(expected_key).buffer); + assert_equals(value.length, expected_value.length); + for (let i = 0; i < value.length; ++i) { + assert_equals(value[i], expected_value[i]); + } +}
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/the-stylepropertymap/properties/logical-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-typed-om/the-stylepropertymap/properties/logical-expected.txt new file mode 100644 index 0000000..0f32b2d --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/the-stylepropertymap/properties/logical-expected.txt
@@ -0,0 +1,552 @@ +This is a testharness.js-based test. +Found 548 tests; 436 PASS, 112 FAIL, 0 TIMEOUT, 0 NOTRUN. +PASS Can set 'margin-block-start' to CSS-wide keywords +PASS Can set 'margin-block-start' to var() references +PASS Can set 'margin-block-start' to a percent +PASS Can set 'margin-block-start' to a length +PASS Setting 'margin-block-start' to a time throws TypeError +PASS Setting 'margin-block-start' to an angle throws TypeError +PASS Setting 'margin-block-start' to a flexible length throws TypeError +PASS Setting 'margin-block-start' to a number throws TypeError +PASS Setting 'margin-block-start' to a position throws TypeError +PASS Setting 'margin-block-start' to a URL throws TypeError +PASS Setting 'margin-block-start' to a transform throws TypeError +PASS Can set 'margin-block-end' to CSS-wide keywords +PASS Can set 'margin-block-end' to var() references +PASS Can set 'margin-block-end' to a percent +PASS Can set 'margin-block-end' to a length +PASS Setting 'margin-block-end' to a time throws TypeError +PASS Setting 'margin-block-end' to an angle throws TypeError +PASS Setting 'margin-block-end' to a flexible length throws TypeError +PASS Setting 'margin-block-end' to a number throws TypeError +PASS Setting 'margin-block-end' to a position throws TypeError +PASS Setting 'margin-block-end' to a URL throws TypeError +PASS Setting 'margin-block-end' to a transform throws TypeError +PASS Can set 'margin-inline-start' to CSS-wide keywords +PASS Can set 'margin-inline-start' to var() references +PASS Can set 'margin-inline-start' to a percent +PASS Can set 'margin-inline-start' to a length +PASS Setting 'margin-inline-start' to a time throws TypeError +PASS Setting 'margin-inline-start' to an angle throws TypeError +PASS Setting 'margin-inline-start' to a flexible length throws TypeError +PASS Setting 'margin-inline-start' to a number throws TypeError +PASS Setting 'margin-inline-start' to a position throws TypeError +PASS Setting 'margin-inline-start' to a URL throws TypeError +PASS Setting 'margin-inline-start' to a transform throws TypeError +PASS Can set 'margin-inline-end' to CSS-wide keywords +PASS Can set 'margin-inline-end' to var() references +PASS Can set 'margin-inline-end' to a percent +PASS Can set 'margin-inline-end' to a length +PASS Setting 'margin-inline-end' to a time throws TypeError +PASS Setting 'margin-inline-end' to an angle throws TypeError +PASS Setting 'margin-inline-end' to a flexible length throws TypeError +PASS Setting 'margin-inline-end' to a number throws TypeError +PASS Setting 'margin-inline-end' to a position throws TypeError +PASS Setting 'margin-inline-end' to a URL throws TypeError +PASS Setting 'margin-inline-end' to a transform throws TypeError +FAIL Can set 'margin-block' to CSS-wide keywords assert_equals: expected "CSSKeywordValue" but got "CSSStyleValue" +FAIL Can set 'margin-block' to var() references assert_equals: expected "CSSUnparsedValue" but got "CSSStyleValue" +FAIL Can set 'margin-block' to a percent Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +FAIL Can set 'margin-block' to a length Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +PASS Setting 'margin-block' to a time throws TypeError +PASS Setting 'margin-block' to an angle throws TypeError +PASS Setting 'margin-block' to a flexible length throws TypeError +PASS Setting 'margin-block' to a number throws TypeError +PASS Setting 'margin-block' to a position throws TypeError +PASS Setting 'margin-block' to a URL throws TypeError +PASS Setting 'margin-block' to a transform throws TypeError +FAIL Can set 'margin-inline' to CSS-wide keywords assert_equals: expected "CSSKeywordValue" but got "CSSStyleValue" +FAIL Can set 'margin-inline' to var() references assert_equals: expected "CSSUnparsedValue" but got "CSSStyleValue" +FAIL Can set 'margin-inline' to a percent Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +FAIL Can set 'margin-inline' to a length Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +PASS Setting 'margin-inline' to a time throws TypeError +PASS Setting 'margin-inline' to an angle throws TypeError +PASS Setting 'margin-inline' to a flexible length throws TypeError +PASS Setting 'margin-inline' to a number throws TypeError +PASS Setting 'margin-inline' to a position throws TypeError +PASS Setting 'margin-inline' to a URL throws TypeError +PASS Setting 'margin-inline' to a transform throws TypeError +PASS Can set 'inset-block-start' to CSS-wide keywords +PASS Can set 'inset-block-start' to var() references +PASS Can set 'inset-block-start' to a percent +PASS Can set 'inset-block-start' to a length +PASS Setting 'inset-block-start' to a time throws TypeError +PASS Setting 'inset-block-start' to an angle throws TypeError +PASS Setting 'inset-block-start' to a flexible length throws TypeError +PASS Setting 'inset-block-start' to a number throws TypeError +PASS Setting 'inset-block-start' to a position throws TypeError +PASS Setting 'inset-block-start' to a URL throws TypeError +PASS Setting 'inset-block-start' to a transform throws TypeError +PASS Can set 'inset-block-end' to CSS-wide keywords +PASS Can set 'inset-block-end' to var() references +PASS Can set 'inset-block-end' to a percent +PASS Can set 'inset-block-end' to a length +PASS Setting 'inset-block-end' to a time throws TypeError +PASS Setting 'inset-block-end' to an angle throws TypeError +PASS Setting 'inset-block-end' to a flexible length throws TypeError +PASS Setting 'inset-block-end' to a number throws TypeError +PASS Setting 'inset-block-end' to a position throws TypeError +PASS Setting 'inset-block-end' to a URL throws TypeError +PASS Setting 'inset-block-end' to a transform throws TypeError +PASS Can set 'inset-inline-start' to CSS-wide keywords +PASS Can set 'inset-inline-start' to var() references +PASS Can set 'inset-inline-start' to a percent +PASS Can set 'inset-inline-start' to a length +PASS Setting 'inset-inline-start' to a time throws TypeError +PASS Setting 'inset-inline-start' to an angle throws TypeError +PASS Setting 'inset-inline-start' to a flexible length throws TypeError +PASS Setting 'inset-inline-start' to a number throws TypeError +PASS Setting 'inset-inline-start' to a position throws TypeError +PASS Setting 'inset-inline-start' to a URL throws TypeError +PASS Setting 'inset-inline-start' to a transform throws TypeError +PASS Can set 'inset-inline-end' to CSS-wide keywords +PASS Can set 'inset-inline-end' to var() references +PASS Can set 'inset-inline-end' to a percent +PASS Can set 'inset-inline-end' to a length +PASS Setting 'inset-inline-end' to a time throws TypeError +PASS Setting 'inset-inline-end' to an angle throws TypeError +PASS Setting 'inset-inline-end' to a flexible length throws TypeError +PASS Setting 'inset-inline-end' to a number throws TypeError +PASS Setting 'inset-inline-end' to a position throws TypeError +PASS Setting 'inset-inline-end' to a URL throws TypeError +PASS Setting 'inset-inline-end' to a transform throws TypeError +FAIL Can set 'inset-block' to CSS-wide keywords assert_equals: expected "CSSKeywordValue" but got "CSSStyleValue" +FAIL Can set 'inset-block' to var() references assert_equals: expected "CSSUnparsedValue" but got "CSSStyleValue" +FAIL Can set 'inset-block' to a percent Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +FAIL Can set 'inset-block' to a length Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +PASS Setting 'inset-block' to a time throws TypeError +PASS Setting 'inset-block' to an angle throws TypeError +PASS Setting 'inset-block' to a flexible length throws TypeError +PASS Setting 'inset-block' to a number throws TypeError +PASS Setting 'inset-block' to a position throws TypeError +PASS Setting 'inset-block' to a URL throws TypeError +PASS Setting 'inset-block' to a transform throws TypeError +FAIL Can set 'inset-inline' to CSS-wide keywords assert_equals: expected "CSSKeywordValue" but got "CSSStyleValue" +FAIL Can set 'inset-inline' to var() references assert_equals: expected "CSSUnparsedValue" but got "CSSStyleValue" +FAIL Can set 'inset-inline' to a percent Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +FAIL Can set 'inset-inline' to a length Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +PASS Setting 'inset-inline' to a time throws TypeError +PASS Setting 'inset-inline' to an angle throws TypeError +PASS Setting 'inset-inline' to a flexible length throws TypeError +PASS Setting 'inset-inline' to a number throws TypeError +PASS Setting 'inset-inline' to a position throws TypeError +PASS Setting 'inset-inline' to a URL throws TypeError +PASS Setting 'inset-inline' to a transform throws TypeError +PASS Can set 'padding-block-start' to CSS-wide keywords +PASS Can set 'padding-block-start' to var() references +PASS Can set 'padding-block-start' to a percent +PASS Can set 'padding-block-start' to a length +PASS Setting 'padding-block-start' to a time throws TypeError +PASS Setting 'padding-block-start' to an angle throws TypeError +PASS Setting 'padding-block-start' to a flexible length throws TypeError +PASS Setting 'padding-block-start' to a number throws TypeError +PASS Setting 'padding-block-start' to a position throws TypeError +PASS Setting 'padding-block-start' to a URL throws TypeError +PASS Setting 'padding-block-start' to a transform throws TypeError +PASS Can set 'padding-block-end' to CSS-wide keywords +PASS Can set 'padding-block-end' to var() references +PASS Can set 'padding-block-end' to a percent +PASS Can set 'padding-block-end' to a length +PASS Setting 'padding-block-end' to a time throws TypeError +PASS Setting 'padding-block-end' to an angle throws TypeError +PASS Setting 'padding-block-end' to a flexible length throws TypeError +PASS Setting 'padding-block-end' to a number throws TypeError +PASS Setting 'padding-block-end' to a position throws TypeError +PASS Setting 'padding-block-end' to a URL throws TypeError +PASS Setting 'padding-block-end' to a transform throws TypeError +PASS Can set 'padding-inline-start' to CSS-wide keywords +PASS Can set 'padding-inline-start' to var() references +PASS Can set 'padding-inline-start' to a percent +PASS Can set 'padding-inline-start' to a length +PASS Setting 'padding-inline-start' to a time throws TypeError +PASS Setting 'padding-inline-start' to an angle throws TypeError +PASS Setting 'padding-inline-start' to a flexible length throws TypeError +PASS Setting 'padding-inline-start' to a number throws TypeError +PASS Setting 'padding-inline-start' to a position throws TypeError +PASS Setting 'padding-inline-start' to a URL throws TypeError +PASS Setting 'padding-inline-start' to a transform throws TypeError +PASS Can set 'padding-inline-end' to CSS-wide keywords +PASS Can set 'padding-inline-end' to var() references +PASS Can set 'padding-inline-end' to a percent +PASS Can set 'padding-inline-end' to a length +PASS Setting 'padding-inline-end' to a time throws TypeError +PASS Setting 'padding-inline-end' to an angle throws TypeError +PASS Setting 'padding-inline-end' to a flexible length throws TypeError +PASS Setting 'padding-inline-end' to a number throws TypeError +PASS Setting 'padding-inline-end' to a position throws TypeError +PASS Setting 'padding-inline-end' to a URL throws TypeError +PASS Setting 'padding-inline-end' to a transform throws TypeError +FAIL Can set 'padding-block' to CSS-wide keywords assert_equals: expected "CSSKeywordValue" but got "CSSStyleValue" +FAIL Can set 'padding-block' to var() references assert_equals: expected "CSSUnparsedValue" but got "CSSStyleValue" +FAIL Can set 'padding-block' to a percent Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +FAIL Can set 'padding-block' to a length Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +PASS Setting 'padding-block' to a time throws TypeError +PASS Setting 'padding-block' to an angle throws TypeError +PASS Setting 'padding-block' to a flexible length throws TypeError +PASS Setting 'padding-block' to a number throws TypeError +PASS Setting 'padding-block' to a position throws TypeError +PASS Setting 'padding-block' to a URL throws TypeError +PASS Setting 'padding-block' to a transform throws TypeError +FAIL Can set 'padding-inline' to CSS-wide keywords assert_equals: expected "CSSKeywordValue" but got "CSSStyleValue" +FAIL Can set 'padding-inline' to var() references assert_equals: expected "CSSUnparsedValue" but got "CSSStyleValue" +FAIL Can set 'padding-inline' to a percent Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +FAIL Can set 'padding-inline' to a length Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +PASS Setting 'padding-inline' to a time throws TypeError +PASS Setting 'padding-inline' to an angle throws TypeError +PASS Setting 'padding-inline' to a flexible length throws TypeError +PASS Setting 'padding-inline' to a number throws TypeError +PASS Setting 'padding-inline' to a position throws TypeError +PASS Setting 'padding-inline' to a URL throws TypeError +PASS Setting 'padding-inline' to a transform throws TypeError +FAIL Can set 'border-block-start' to CSS-wide keywords assert_equals: expected "CSSKeywordValue" but got "CSSStyleValue" +FAIL Can set 'border-block-start' to var() references assert_equals: expected "CSSUnparsedValue" but got "CSSStyleValue" +FAIL Can set 'border-block-start' to the 'none' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +PASS Setting 'border-block-start' to a length throws TypeError +PASS Setting 'border-block-start' to a percent throws TypeError +PASS Setting 'border-block-start' to a time throws TypeError +PASS Setting 'border-block-start' to an angle throws TypeError +PASS Setting 'border-block-start' to a flexible length throws TypeError +PASS Setting 'border-block-start' to a number throws TypeError +PASS Setting 'border-block-start' to a position throws TypeError +PASS Setting 'border-block-start' to a URL throws TypeError +PASS Setting 'border-block-start' to a transform throws TypeError +PASS Can set 'border-block-start-width' to CSS-wide keywords +PASS Can set 'border-block-start-width' to var() references +FAIL Can set 'border-block-start-width' to the 'thin' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +FAIL Can set 'border-block-start-width' to the 'medium' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +FAIL Can set 'border-block-start-width' to the 'thick' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +FAIL Can set 'border-block-start-width' to a length Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +PASS Setting 'border-block-start-width' to a percent throws TypeError +PASS Setting 'border-block-start-width' to a time throws TypeError +PASS Setting 'border-block-start-width' to an angle throws TypeError +PASS Setting 'border-block-start-width' to a flexible length throws TypeError +PASS Setting 'border-block-start-width' to a number throws TypeError +PASS Setting 'border-block-start-width' to a position throws TypeError +PASS Setting 'border-block-start-width' to a URL throws TypeError +PASS Setting 'border-block-start-width' to a transform throws TypeError +PASS Can set 'border-block-start-color' to CSS-wide keywords +PASS Can set 'border-block-start-color' to var() references +FAIL Can set 'border-block-start-color' to the 'currentcolor' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +PASS Setting 'border-block-start-color' to a length throws TypeError +PASS Setting 'border-block-start-color' to a percent throws TypeError +PASS Setting 'border-block-start-color' to a time throws TypeError +PASS Setting 'border-block-start-color' to an angle throws TypeError +PASS Setting 'border-block-start-color' to a flexible length throws TypeError +PASS Setting 'border-block-start-color' to a number throws TypeError +PASS Setting 'border-block-start-color' to a position throws TypeError +PASS Setting 'border-block-start-color' to a URL throws TypeError +PASS Setting 'border-block-start-color' to a transform throws TypeError +PASS Can set 'border-block-start-style' to CSS-wide keywords +PASS Can set 'border-block-start-style' to var() references +FAIL Can set 'border-block-start-style' to the 'none' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +FAIL Can set 'border-block-start-style' to the 'solid' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +PASS Setting 'border-block-start-style' to a length throws TypeError +PASS Setting 'border-block-start-style' to a percent throws TypeError +PASS Setting 'border-block-start-style' to a time throws TypeError +PASS Setting 'border-block-start-style' to an angle throws TypeError +PASS Setting 'border-block-start-style' to a flexible length throws TypeError +PASS Setting 'border-block-start-style' to a number throws TypeError +PASS Setting 'border-block-start-style' to a position throws TypeError +PASS Setting 'border-block-start-style' to a URL throws TypeError +PASS Setting 'border-block-start-style' to a transform throws TypeError +FAIL Can set 'border-block-end' to CSS-wide keywords assert_equals: expected "CSSKeywordValue" but got "CSSStyleValue" +FAIL Can set 'border-block-end' to var() references assert_equals: expected "CSSUnparsedValue" but got "CSSStyleValue" +FAIL Can set 'border-block-end' to the 'none' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +PASS Setting 'border-block-end' to a length throws TypeError +PASS Setting 'border-block-end' to a percent throws TypeError +PASS Setting 'border-block-end' to a time throws TypeError +PASS Setting 'border-block-end' to an angle throws TypeError +PASS Setting 'border-block-end' to a flexible length throws TypeError +PASS Setting 'border-block-end' to a number throws TypeError +PASS Setting 'border-block-end' to a position throws TypeError +PASS Setting 'border-block-end' to a URL throws TypeError +PASS Setting 'border-block-end' to a transform throws TypeError +PASS Can set 'border-block-end-width' to CSS-wide keywords +PASS Can set 'border-block-end-width' to var() references +FAIL Can set 'border-block-end-width' to the 'thin' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +FAIL Can set 'border-block-end-width' to the 'medium' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +FAIL Can set 'border-block-end-width' to the 'thick' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +FAIL Can set 'border-block-end-width' to a length Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +PASS Setting 'border-block-end-width' to a percent throws TypeError +PASS Setting 'border-block-end-width' to a time throws TypeError +PASS Setting 'border-block-end-width' to an angle throws TypeError +PASS Setting 'border-block-end-width' to a flexible length throws TypeError +PASS Setting 'border-block-end-width' to a number throws TypeError +PASS Setting 'border-block-end-width' to a position throws TypeError +PASS Setting 'border-block-end-width' to a URL throws TypeError +PASS Setting 'border-block-end-width' to a transform throws TypeError +PASS Can set 'border-block-end-color' to CSS-wide keywords +PASS Can set 'border-block-end-color' to var() references +FAIL Can set 'border-block-end-color' to the 'currentcolor' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +PASS Setting 'border-block-end-color' to a length throws TypeError +PASS Setting 'border-block-end-color' to a percent throws TypeError +PASS Setting 'border-block-end-color' to a time throws TypeError +PASS Setting 'border-block-end-color' to an angle throws TypeError +PASS Setting 'border-block-end-color' to a flexible length throws TypeError +PASS Setting 'border-block-end-color' to a number throws TypeError +PASS Setting 'border-block-end-color' to a position throws TypeError +PASS Setting 'border-block-end-color' to a URL throws TypeError +PASS Setting 'border-block-end-color' to a transform throws TypeError +PASS Can set 'border-block-end-style' to CSS-wide keywords +PASS Can set 'border-block-end-style' to var() references +FAIL Can set 'border-block-end-style' to the 'none' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +FAIL Can set 'border-block-end-style' to the 'solid' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +PASS Setting 'border-block-end-style' to a length throws TypeError +PASS Setting 'border-block-end-style' to a percent throws TypeError +PASS Setting 'border-block-end-style' to a time throws TypeError +PASS Setting 'border-block-end-style' to an angle throws TypeError +PASS Setting 'border-block-end-style' to a flexible length throws TypeError +PASS Setting 'border-block-end-style' to a number throws TypeError +PASS Setting 'border-block-end-style' to a position throws TypeError +PASS Setting 'border-block-end-style' to a URL throws TypeError +PASS Setting 'border-block-end-style' to a transform throws TypeError +FAIL Can set 'border-inline-start' to CSS-wide keywords assert_equals: expected "CSSKeywordValue" but got "CSSStyleValue" +FAIL Can set 'border-inline-start' to var() references assert_equals: expected "CSSUnparsedValue" but got "CSSStyleValue" +FAIL Can set 'border-inline-start' to the 'none' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +PASS Setting 'border-inline-start' to a length throws TypeError +PASS Setting 'border-inline-start' to a percent throws TypeError +PASS Setting 'border-inline-start' to a time throws TypeError +PASS Setting 'border-inline-start' to an angle throws TypeError +PASS Setting 'border-inline-start' to a flexible length throws TypeError +PASS Setting 'border-inline-start' to a number throws TypeError +PASS Setting 'border-inline-start' to a position throws TypeError +PASS Setting 'border-inline-start' to a URL throws TypeError +PASS Setting 'border-inline-start' to a transform throws TypeError +PASS Can set 'border-inline-start-width' to CSS-wide keywords +PASS Can set 'border-inline-start-width' to var() references +FAIL Can set 'border-inline-start-width' to the 'thin' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +FAIL Can set 'border-inline-start-width' to the 'medium' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +FAIL Can set 'border-inline-start-width' to the 'thick' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +FAIL Can set 'border-inline-start-width' to a length Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +PASS Setting 'border-inline-start-width' to a percent throws TypeError +PASS Setting 'border-inline-start-width' to a time throws TypeError +PASS Setting 'border-inline-start-width' to an angle throws TypeError +PASS Setting 'border-inline-start-width' to a flexible length throws TypeError +PASS Setting 'border-inline-start-width' to a number throws TypeError +PASS Setting 'border-inline-start-width' to a position throws TypeError +PASS Setting 'border-inline-start-width' to a URL throws TypeError +PASS Setting 'border-inline-start-width' to a transform throws TypeError +PASS Can set 'border-inline-start-color' to CSS-wide keywords +PASS Can set 'border-inline-start-color' to var() references +FAIL Can set 'border-inline-start-color' to the 'currentcolor' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +PASS Setting 'border-inline-start-color' to a length throws TypeError +PASS Setting 'border-inline-start-color' to a percent throws TypeError +PASS Setting 'border-inline-start-color' to a time throws TypeError +PASS Setting 'border-inline-start-color' to an angle throws TypeError +PASS Setting 'border-inline-start-color' to a flexible length throws TypeError +PASS Setting 'border-inline-start-color' to a number throws TypeError +PASS Setting 'border-inline-start-color' to a position throws TypeError +PASS Setting 'border-inline-start-color' to a URL throws TypeError +PASS Setting 'border-inline-start-color' to a transform throws TypeError +PASS Can set 'border-inline-start-style' to CSS-wide keywords +PASS Can set 'border-inline-start-style' to var() references +FAIL Can set 'border-inline-start-style' to the 'none' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +FAIL Can set 'border-inline-start-style' to the 'solid' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +PASS Setting 'border-inline-start-style' to a length throws TypeError +PASS Setting 'border-inline-start-style' to a percent throws TypeError +PASS Setting 'border-inline-start-style' to a time throws TypeError +PASS Setting 'border-inline-start-style' to an angle throws TypeError +PASS Setting 'border-inline-start-style' to a flexible length throws TypeError +PASS Setting 'border-inline-start-style' to a number throws TypeError +PASS Setting 'border-inline-start-style' to a position throws TypeError +PASS Setting 'border-inline-start-style' to a URL throws TypeError +PASS Setting 'border-inline-start-style' to a transform throws TypeError +FAIL Can set 'border-inline-end' to CSS-wide keywords assert_equals: expected "CSSKeywordValue" but got "CSSStyleValue" +FAIL Can set 'border-inline-end' to var() references assert_equals: expected "CSSUnparsedValue" but got "CSSStyleValue" +FAIL Can set 'border-inline-end' to the 'none' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +PASS Setting 'border-inline-end' to a length throws TypeError +PASS Setting 'border-inline-end' to a percent throws TypeError +PASS Setting 'border-inline-end' to a time throws TypeError +PASS Setting 'border-inline-end' to an angle throws TypeError +PASS Setting 'border-inline-end' to a flexible length throws TypeError +PASS Setting 'border-inline-end' to a number throws TypeError +PASS Setting 'border-inline-end' to a position throws TypeError +PASS Setting 'border-inline-end' to a URL throws TypeError +PASS Setting 'border-inline-end' to a transform throws TypeError +PASS Can set 'border-inline-end-width' to CSS-wide keywords +PASS Can set 'border-inline-end-width' to var() references +FAIL Can set 'border-inline-end-width' to the 'thin' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +FAIL Can set 'border-inline-end-width' to the 'medium' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +FAIL Can set 'border-inline-end-width' to the 'thick' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +FAIL Can set 'border-inline-end-width' to a length Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +PASS Setting 'border-inline-end-width' to a percent throws TypeError +PASS Setting 'border-inline-end-width' to a time throws TypeError +PASS Setting 'border-inline-end-width' to an angle throws TypeError +PASS Setting 'border-inline-end-width' to a flexible length throws TypeError +PASS Setting 'border-inline-end-width' to a number throws TypeError +PASS Setting 'border-inline-end-width' to a position throws TypeError +PASS Setting 'border-inline-end-width' to a URL throws TypeError +PASS Setting 'border-inline-end-width' to a transform throws TypeError +PASS Can set 'border-inline-end-color' to CSS-wide keywords +PASS Can set 'border-inline-end-color' to var() references +FAIL Can set 'border-inline-end-color' to the 'currentcolor' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +PASS Setting 'border-inline-end-color' to a length throws TypeError +PASS Setting 'border-inline-end-color' to a percent throws TypeError +PASS Setting 'border-inline-end-color' to a time throws TypeError +PASS Setting 'border-inline-end-color' to an angle throws TypeError +PASS Setting 'border-inline-end-color' to a flexible length throws TypeError +PASS Setting 'border-inline-end-color' to a number throws TypeError +PASS Setting 'border-inline-end-color' to a position throws TypeError +PASS Setting 'border-inline-end-color' to a URL throws TypeError +PASS Setting 'border-inline-end-color' to a transform throws TypeError +PASS Can set 'border-inline-end-style' to CSS-wide keywords +PASS Can set 'border-inline-end-style' to var() references +FAIL Can set 'border-inline-end-style' to the 'none' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +FAIL Can set 'border-inline-end-style' to the 'solid' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +PASS Setting 'border-inline-end-style' to a length throws TypeError +PASS Setting 'border-inline-end-style' to a percent throws TypeError +PASS Setting 'border-inline-end-style' to a time throws TypeError +PASS Setting 'border-inline-end-style' to an angle throws TypeError +PASS Setting 'border-inline-end-style' to a flexible length throws TypeError +PASS Setting 'border-inline-end-style' to a number throws TypeError +PASS Setting 'border-inline-end-style' to a position throws TypeError +PASS Setting 'border-inline-end-style' to a URL throws TypeError +PASS Setting 'border-inline-end-style' to a transform throws TypeError +FAIL Can set 'border-block' to CSS-wide keywords assert_equals: expected "CSSKeywordValue" but got "CSSStyleValue" +FAIL Can set 'border-block' to var() references assert_equals: expected "CSSUnparsedValue" but got "CSSStyleValue" +FAIL Can set 'border-block' to the 'none' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +PASS Setting 'border-block' to a length throws TypeError +PASS Setting 'border-block' to a percent throws TypeError +PASS Setting 'border-block' to a time throws TypeError +PASS Setting 'border-block' to an angle throws TypeError +PASS Setting 'border-block' to a flexible length throws TypeError +PASS Setting 'border-block' to a number throws TypeError +PASS Setting 'border-block' to a position throws TypeError +PASS Setting 'border-block' to a URL throws TypeError +PASS Setting 'border-block' to a transform throws TypeError +FAIL Can set 'border-block-width' to CSS-wide keywords assert_equals: expected "CSSKeywordValue" but got "CSSStyleValue" +FAIL Can set 'border-block-width' to var() references assert_equals: expected "CSSUnparsedValue" but got "CSSStyleValue" +FAIL Can set 'border-block-width' to the 'thin' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +FAIL Can set 'border-block-width' to the 'medium' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +FAIL Can set 'border-block-width' to the 'thick' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +FAIL Can set 'border-block-width' to a length Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +PASS Setting 'border-block-width' to a percent throws TypeError +PASS Setting 'border-block-width' to a time throws TypeError +PASS Setting 'border-block-width' to an angle throws TypeError +PASS Setting 'border-block-width' to a flexible length throws TypeError +PASS Setting 'border-block-width' to a number throws TypeError +PASS Setting 'border-block-width' to a position throws TypeError +PASS Setting 'border-block-width' to a URL throws TypeError +PASS Setting 'border-block-width' to a transform throws TypeError +FAIL Can set 'border-block-color' to CSS-wide keywords assert_equals: expected "CSSKeywordValue" but got "CSSStyleValue" +FAIL Can set 'border-block-color' to var() references assert_equals: expected "CSSUnparsedValue" but got "CSSStyleValue" +FAIL Can set 'border-block-color' to the 'currentcolor' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +PASS Setting 'border-block-color' to a length throws TypeError +PASS Setting 'border-block-color' to a percent throws TypeError +PASS Setting 'border-block-color' to a time throws TypeError +PASS Setting 'border-block-color' to an angle throws TypeError +PASS Setting 'border-block-color' to a flexible length throws TypeError +PASS Setting 'border-block-color' to a number throws TypeError +PASS Setting 'border-block-color' to a position throws TypeError +PASS Setting 'border-block-color' to a URL throws TypeError +PASS Setting 'border-block-color' to a transform throws TypeError +FAIL Can set 'border-block-style' to CSS-wide keywords assert_equals: expected "CSSKeywordValue" but got "CSSStyleValue" +FAIL Can set 'border-block-style' to var() references assert_equals: expected "CSSUnparsedValue" but got "CSSStyleValue" +FAIL Can set 'border-block-style' to the 'none' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +FAIL Can set 'border-block-style' to the 'solid' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +PASS Setting 'border-block-style' to a length throws TypeError +PASS Setting 'border-block-style' to a percent throws TypeError +PASS Setting 'border-block-style' to a time throws TypeError +PASS Setting 'border-block-style' to an angle throws TypeError +PASS Setting 'border-block-style' to a flexible length throws TypeError +PASS Setting 'border-block-style' to a number throws TypeError +PASS Setting 'border-block-style' to a position throws TypeError +PASS Setting 'border-block-style' to a URL throws TypeError +PASS Setting 'border-block-style' to a transform throws TypeError +FAIL Can set 'border-inline' to CSS-wide keywords assert_equals: expected "CSSKeywordValue" but got "CSSStyleValue" +FAIL Can set 'border-inline' to var() references assert_equals: expected "CSSUnparsedValue" but got "CSSStyleValue" +FAIL Can set 'border-inline' to the 'none' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +PASS Setting 'border-inline' to a length throws TypeError +PASS Setting 'border-inline' to a percent throws TypeError +PASS Setting 'border-inline' to a time throws TypeError +PASS Setting 'border-inline' to an angle throws TypeError +PASS Setting 'border-inline' to a flexible length throws TypeError +PASS Setting 'border-inline' to a number throws TypeError +PASS Setting 'border-inline' to a position throws TypeError +PASS Setting 'border-inline' to a URL throws TypeError +PASS Setting 'border-inline' to a transform throws TypeError +FAIL Can set 'border-inline-width' to CSS-wide keywords assert_equals: expected "CSSKeywordValue" but got "CSSStyleValue" +FAIL Can set 'border-inline-width' to var() references assert_equals: expected "CSSUnparsedValue" but got "CSSStyleValue" +FAIL Can set 'border-inline-width' to the 'thin' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +FAIL Can set 'border-inline-width' to the 'medium' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +FAIL Can set 'border-inline-width' to the 'thick' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +FAIL Can set 'border-inline-width' to a length Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +PASS Setting 'border-inline-width' to a percent throws TypeError +PASS Setting 'border-inline-width' to a time throws TypeError +PASS Setting 'border-inline-width' to an angle throws TypeError +PASS Setting 'border-inline-width' to a flexible length throws TypeError +PASS Setting 'border-inline-width' to a number throws TypeError +PASS Setting 'border-inline-width' to a position throws TypeError +PASS Setting 'border-inline-width' to a URL throws TypeError +PASS Setting 'border-inline-width' to a transform throws TypeError +FAIL Can set 'border-inline-color' to CSS-wide keywords assert_equals: expected "CSSKeywordValue" but got "CSSStyleValue" +FAIL Can set 'border-inline-color' to var() references assert_equals: expected "CSSUnparsedValue" but got "CSSStyleValue" +FAIL Can set 'border-inline-color' to the 'currentcolor' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +PASS Setting 'border-inline-color' to a length throws TypeError +PASS Setting 'border-inline-color' to a percent throws TypeError +PASS Setting 'border-inline-color' to a time throws TypeError +PASS Setting 'border-inline-color' to an angle throws TypeError +PASS Setting 'border-inline-color' to a flexible length throws TypeError +PASS Setting 'border-inline-color' to a number throws TypeError +PASS Setting 'border-inline-color' to a position throws TypeError +PASS Setting 'border-inline-color' to a URL throws TypeError +PASS Setting 'border-inline-color' to a transform throws TypeError +FAIL Can set 'border-inline-style' to CSS-wide keywords assert_equals: expected "CSSKeywordValue" but got "CSSStyleValue" +FAIL Can set 'border-inline-style' to var() references assert_equals: expected "CSSUnparsedValue" but got "CSSStyleValue" +FAIL Can set 'border-inline-style' to the 'none' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +FAIL Can set 'border-inline-style' to the 'solid' keyword Failed to execute 'set' on 'StylePropertyMap': Invalid type for property +PASS Setting 'border-inline-style' to a length throws TypeError +PASS Setting 'border-inline-style' to a percent throws TypeError +PASS Setting 'border-inline-style' to a time throws TypeError +PASS Setting 'border-inline-style' to an angle throws TypeError +PASS Setting 'border-inline-style' to a flexible length throws TypeError +PASS Setting 'border-inline-style' to a number throws TypeError +PASS Setting 'border-inline-style' to a position throws TypeError +PASS Setting 'border-inline-style' to a URL throws TypeError +PASS Setting 'border-inline-style' to a transform throws TypeError +FAIL Can set 'border-start-start-radius' to CSS-wide keywords Failed to execute 'set' on 'StylePropertyMap': Invalid propertyName: border-start-start-radius +FAIL Can set 'border-start-start-radius' to var() references Failed to execute 'set' on 'StylePropertyMap': Invalid propertyName: border-start-start-radius +FAIL Can set 'border-start-start-radius' to a percent Failed to execute 'set' on 'StylePropertyMap': Invalid propertyName: border-start-start-radius +FAIL Can set 'border-start-start-radius' to a length Failed to execute 'set' on 'StylePropertyMap': Invalid propertyName: border-start-start-radius +PASS Setting 'border-start-start-radius' to a time throws TypeError +PASS Setting 'border-start-start-radius' to an angle throws TypeError +PASS Setting 'border-start-start-radius' to a flexible length throws TypeError +PASS Setting 'border-start-start-radius' to a number throws TypeError +PASS Setting 'border-start-start-radius' to a position throws TypeError +PASS Setting 'border-start-start-radius' to a URL throws TypeError +PASS Setting 'border-start-start-radius' to a transform throws TypeError +FAIL Can set 'border-start-end-radius' to CSS-wide keywords Failed to execute 'set' on 'StylePropertyMap': Invalid propertyName: border-start-end-radius +FAIL Can set 'border-start-end-radius' to var() references Failed to execute 'set' on 'StylePropertyMap': Invalid propertyName: border-start-end-radius +FAIL Can set 'border-start-end-radius' to a percent Failed to execute 'set' on 'StylePropertyMap': Invalid propertyName: border-start-end-radius +FAIL Can set 'border-start-end-radius' to a length Failed to execute 'set' on 'StylePropertyMap': Invalid propertyName: border-start-end-radius +PASS Setting 'border-start-end-radius' to a time throws TypeError +PASS Setting 'border-start-end-radius' to an angle throws TypeError +PASS Setting 'border-start-end-radius' to a flexible length throws TypeError +PASS Setting 'border-start-end-radius' to a number throws TypeError +PASS Setting 'border-start-end-radius' to a position throws TypeError +PASS Setting 'border-start-end-radius' to a URL throws TypeError +PASS Setting 'border-start-end-radius' to a transform throws TypeError +FAIL Can set 'border-end-start-radius' to CSS-wide keywords Failed to execute 'set' on 'StylePropertyMap': Invalid propertyName: border-end-start-radius +FAIL Can set 'border-end-start-radius' to var() references Failed to execute 'set' on 'StylePropertyMap': Invalid propertyName: border-end-start-radius +FAIL Can set 'border-end-start-radius' to a percent Failed to execute 'set' on 'StylePropertyMap': Invalid propertyName: border-end-start-radius +FAIL Can set 'border-end-start-radius' to a length Failed to execute 'set' on 'StylePropertyMap': Invalid propertyName: border-end-start-radius +PASS Setting 'border-end-start-radius' to a time throws TypeError +PASS Setting 'border-end-start-radius' to an angle throws TypeError +PASS Setting 'border-end-start-radius' to a flexible length throws TypeError +PASS Setting 'border-end-start-radius' to a number throws TypeError +PASS Setting 'border-end-start-radius' to a position throws TypeError +PASS Setting 'border-end-start-radius' to a URL throws TypeError +PASS Setting 'border-end-start-radius' to a transform throws TypeError +FAIL Can set 'border-end-end-radius' to CSS-wide keywords Failed to execute 'set' on 'StylePropertyMap': Invalid propertyName: border-end-end-radius +FAIL Can set 'border-end-end-radius' to var() references Failed to execute 'set' on 'StylePropertyMap': Invalid propertyName: border-end-end-radius +FAIL Can set 'border-end-end-radius' to a percent Failed to execute 'set' on 'StylePropertyMap': Invalid propertyName: border-end-end-radius +FAIL Can set 'border-end-end-radius' to a length Failed to execute 'set' on 'StylePropertyMap': Invalid propertyName: border-end-end-radius +PASS Setting 'border-end-end-radius' to a time throws TypeError +PASS Setting 'border-end-end-radius' to an angle throws TypeError +PASS Setting 'border-end-end-radius' to a flexible length throws TypeError +PASS Setting 'border-end-end-radius' to a number throws TypeError +PASS Setting 'border-end-end-radius' to a position throws TypeError +PASS Setting 'border-end-end-radius' to a URL throws TypeError +PASS Setting 'border-end-end-radius' to a transform throws TypeError +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/external/wpt/css/css-typed-om/the-stylepropertymap/properties/logical.html b/third_party/blink/web_tests/external/wpt/css/css-typed-om/the-stylepropertymap/properties/logical.html index e8085b0..c068edf 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-typed-om/the-stylepropertymap/properties/logical.html +++ b/third_party/blink/web_tests/external/wpt/css/css-typed-om/the-stylepropertymap/properties/logical.html
@@ -1,6 +1,6 @@ <!doctype html> <meta charset="utf-8"> -<title>margin properties</title> +<title>logical margin, inset, padding & border properties</title> <link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-get"> <link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#dom-stylepropertymap-set"> <link rel="help" href="https://drafts.css-houdini.org/css-typed-om-1/#property-stle-value-normalization"> @@ -13,9 +13,15 @@ <script> 'use strict'; +const logical = { + axes: ['block', 'inline'], + sides: ['block-start', 'block-end', 'inline-start', 'inline-end'], + corners: ['start-start', 'start-end', 'end-start', 'end-end'], +}; + for (const prefix of ['margin-', 'inset-', 'padding-']) { - for (const suffix of ['block-start', 'block-end', 'inline-start', 'inline-end']) { - runPropertyTests(prefix + suffix, [ + for (const side of [...logical.sides, ...logical.axes]) { + runPropertyTests(prefix + side, [ // TODO: Test 'auto' { syntax: '<percentage>' }, { syntax: '<length>' }, @@ -23,5 +29,38 @@ } } -// TODO: test shorthands and other logical properties. +// BORDERS +for (const side of [...logical.sides, ...logical.axes]) { + runPropertyTests('border-' + side, [ + //{ syntax: 'thin solid green' }, + //{ syntax: 'thin solid' }, + //{ syntax: 'thick' }, + { syntax: 'none' }, + ]); + + runPropertyTests(`border-${side}-width`, [ + { syntax: 'thin' }, + { syntax: 'medium' }, + { syntax: 'thick' }, + { syntax: '<length>' }, + ]); + + runPropertyTests(`border-${side}-color`, [ + { syntax: 'currentcolor' }, + //{ syntax: '<color>' }, + ]); + + runPropertyTests(`border-${side}-style`, [ + { syntax: 'none' }, + { syntax: 'solid' }, + ]); +} + +// border radius +for (const side of logical.corners) { + runPropertyTests(`border-${side}-radius`, [ + { syntax: '<percentage>' }, + { syntax: '<length>' }, + ]); +} </script>
diff --git a/third_party/blink/web_tests/external/wpt/layout-instability/contain-paint-fully-clipped.html b/third_party/blink/web_tests/external/wpt/layout-instability/contain-paint-fully-clipped.html new file mode 100644 index 0000000..bb02b6d --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/layout-instability/contain-paint-fully-clipped.html
@@ -0,0 +1,26 @@ +<!DOCTYPE html> +<title>Layout Instability: fully clipped by contain:paint</title> +<link rel="help" href="https://wicg.github.io/layout-instability/" /> +<div style="contain: paint; height: 0; position: relative"> + <div id="target" style="position: absolute; top: 0; width: 400px; height: 400px"></div> +</div> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="resources/util.js"></script> +<script> + +promise_test(async () => { + const watcher = new ScoreWatcher; + + // Wait for the initial render to complete. + await waitForAnimationFrames(2); + + // Shift target, for which no shift should be reported because it's hidden. + document.querySelector("#target").style.top = '200px'; + + await waitForAnimationFrames(2); + // No shift should be reported. + assert_equals(watcher.score, 0); +}, 'fully clipped by contain:paint'); + +</script>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-single-char-and-children-ref.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-single-char-and-children-ref.html new file mode 100644 index 0000000..5a290283 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-single-char-and-children-ref.html
@@ -0,0 +1,24 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <title><mo> with a single character and children (reference)</title> + <link rel="stylesheet" type="text/css" href="/fonts/ahem.css"/> + <style> + math { font: 50px/1 Ahem; } + </style> + </head> + <body> + <p>There should be 5/18em horizontal gaps around the middle rectangles:</p> + <p><math><mn>p</mn><mo><span>X</span></mo><mn>p</mn></math></p> + <p><math><mn>p</mn><mo>X<span></span></mo><mn>p</mn></math></p> + <p><math><mn>p</mn><mo><span></span>X</mo><mn>p</mn></math></p> + <p><math><mn>p</mn><mo id="dynamic-add">X</mo><mn>p</mn></math></p> + <p><math><mn>p</mn><mo id="dynamic-text-add">XX</mo><mn>p</mn></math></p> + + <p>There should be no horizontal gap around the middle rectangles:</p> + + <p><math><mn>p</mn><mo stretchy="false" lspace="0" rspace="0">X</mo><mn>p</mn></math></p> + <p><math><mn>p</mn><mo id="dynamic-remove" stretchy="false" lspace="0" rspace="0"><span></span>X</mo><mn>p</mn></math></p> + </body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-single-char-and-children.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-single-char-and-children.html new file mode 100644 index 0000000..79cb1744 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-single-char-and-children.html
@@ -0,0 +1,43 @@ +<!DOCTYPE html> +<html class="reftest-wait"> + <head> + <meta charset="utf-8"> + <title><mo> with a single character and children</title> + <link rel="help" href="https://mathml-refresh.github.io/mathml-core/#dfn-algorithm-for-determining-the-properties-of-an-embellished-operator"> + <meta name="assert" content="Verify that the default operator properties are used for an mo element whose text is a single character but which contains children."> + <link rel="stylesheet" type="text/css" href="/fonts/ahem.css"/> + <link rel="match" href="mo-single-char-and-children-ref.html"> + <style> + math { font: 50px/1 Ahem; } + </style> + </head> + <body> + <p>There should be 5/18em horizontal gaps around the middle rectangles:</p> + <p><math><mn>p</mn><mo><span>(</span></mo><mn>p</mn></math></p> + <p><math><mn>p</mn><mo>(<span></span></mo><mn>p</mn></math></p> + <p><math><mn>p</mn><mo><span></span>(</mo><mn>p</mn></math></p> + <p><math><mn>p</mn><mo id="dynamic-add">(</mo><mn>p</mn></math></p> + <p><math><mn>p</mn><mo id="dynamic-text-add">|</mo><mn>p</mn></math></p> + + <p>There should be no horizontal gap around the middle rectangles:</p> + + <p><math><mn>p</mn><mo stretchy="false">(</mo><mn>p</mn></math></p> + <p><math><mn>p</mn><mo id="dynamic-remove" stretchy="false"><span></span>(</mo><mn>p</mn></math></p> + + <script src="/mathml/support/feature-detection.js"></script> + <script> + MathMLFeatureDetection.ensure_for_match_reftest("has_operator_spacing"); + + let mo = document.getElementById("dynamic-add"); + mo.appendChild(document.createElement("span")); + + mo = document.getElementById("dynamic-text-add"); + mo.appendChild(document.createTextNode("|")); + + mo = document.getElementById("dynamic-remove"); + mo.removeChild(mo.firstElementChild); + + document.documentElement.classList.remove('reftest-wait'); + </script> + </body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/mediacapture-streams/GUM-empty-option-param.https.html b/third_party/blink/web_tests/external/wpt/mediacapture-streams/GUM-empty-option-param.https.html index c7904cb..9f79a6d 100644 --- a/third_party/blink/web_tests/external/wpt/mediacapture-streams/GUM-empty-option-param.https.html +++ b/third_party/blink/web_tests/external/wpt/mediacapture-streams/GUM-empty-option-param.https.html
@@ -8,7 +8,7 @@ <body> <h1 class="instructions">Description</h1> <p class="instructions">This test checks that getUserMedia with no value in the -options parameter raises a NOT_SUPPORTED_ERR exception.</p> +options parameter raises a TypeError exception.</p> <div id='log'></div> <script src=/resources/testharness.js></script>
diff --git a/third_party/blink/web_tests/external/wpt/mediacapture-streams/GUM-required-constraint-with-ideal-value.https.html b/third_party/blink/web_tests/external/wpt/mediacapture-streams/GUM-required-constraint-with-ideal-value.https.html new file mode 100644 index 0000000..705836f1 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/mediacapture-streams/GUM-required-constraint-with-ideal-value.https.html
@@ -0,0 +1,29 @@ +<!doctype html> +<html> +<head> +<title>Ideal value in required constraint in getUserMedia</title> +<link rel="author" title="Intel" href="http://www.intel.com"/> +<link rel="help" href="https://w3c.github.io/mediacapture-main/#dfn-fitness-distance"> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +</head> +<body> +<p class="instructions">When prompted, accept to share your video stream.</p> +<h1 class="instructions">Description</h1> +<p class="instructions">This test checks that setting a required constraint +with an ideal value in getUserMedia works</p> +<div id='log'></div> +<script> +promise_test(async t => { + const stream = await navigator.mediaDevices.getUserMedia({video: {width: {ideal: 320, min: 160}}}); + assert_equals(stream.getVideoTracks().length, 1, "the media stream has exactly one video track"); + assert_equals(stream.getVideoTracks()[0].getSettings().width, 320, 'ideal width is selected for getUserMedia() video tracks'); + const video = document.createElement('video'); + video.srcObject = stream; + await video.play(); + assert_equals(video.videoWidth, 320, 'video width equals to track width'); + stream.getVideoTracks()[0].stop(); +}, "Tests that setting a required constraint with an ideal value in getUserMedia works"); +</script> +</body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/mediacapture-streams/GUM-unknownkey-option-param.https.html b/third_party/blink/web_tests/external/wpt/mediacapture-streams/GUM-unknownkey-option-param.https.html index b279272..965145a 100644 --- a/third_party/blink/web_tests/external/wpt/mediacapture-streams/GUM-unknownkey-option-param.https.html +++ b/third_party/blink/web_tests/external/wpt/mediacapture-streams/GUM-unknownkey-option-param.https.html
@@ -16,7 +16,7 @@ <script> var t = async_test("Tests that getUserMedia is rejected with a TypeError when used with an unknown constraint"); t.step(function () { - navigator.mediaDevices.getUserMedia({}) + navigator.mediaDevices.getUserMedia({doesnotexist:true}) .then(t.step_func(function () { assert_unreached("This should never be triggered since the constraints parameter only contains an unrecognized constraint"); }), t.step_func(function (error) {
diff --git a/third_party/blink/web_tests/external/wpt/resources/chromium/web-bluetooth-test.js b/third_party/blink/web_tests/external/wpt/resources/chromium/web-bluetooth-test.js index b63658e..aa4b80d2 100644 --- a/third_party/blink/web_tests/external/wpt/resources/chromium/web-bluetooth-test.js +++ b/third_party/blink/web_tests/external/wpt/resources/chromium/web-bluetooth-test.js
@@ -38,10 +38,17 @@ // Converts WebIDL a record<DOMString, BufferSource> to a map<K, array<uint8>> to // use for Mojo, where the value for K is calculated using keyFn. -function convertToMojoMap(record, keyFn) { +function convertToMojoMap(record, keyFn, isNumberKey = false) { let map = new Map(); for (const [key, value] of Object.entries(record)) { let buffer = ArrayBuffer.isView(value) ? value.buffer : value; + if (isNumberKey) { + let numberKey = parseInt(key); + if (Number.isNaN(numberKey)) + throw `Map key ${key} is not a number`; + map.set(keyFn(numberKey), Array.from(new Uint8Array(buffer))); + continue; + } map.set(keyFn(key), Array.from(new Uint8Array(buffer))); } return map; @@ -211,15 +218,17 @@ // Convert manufacturerData from a record<DOMString, BufferSource> into a // map<uint8, array<uint8>> for Mojo. if ('manufacturerData' in scanResult.scanRecord) { - clonedScanResult.scanRecord.manufacturerData = - convertToMojoMap(scanResult.scanRecord.manufacturerData, Number); + clonedScanResult.scanRecord.manufacturerData = convertToMojoMap( + scanResult.scanRecord.manufacturerData, Number, + true /* isNumberKey */); } // Convert serviceData from a record<DOMString, BufferSource> into a // map<string, array<uint8>> for Mojo. if ('serviceData' in scanResult.scanRecord) { clonedScanResult.scanRecord.serviceData.serviceData = convertToMojoMap( - scanResult.scanRecord.serviceData, BluetoothUUID.getService); + scanResult.scanRecord.serviceData, BluetoothUUID.getService, + false /* isNumberKey */); } await this.fake_central_ptr_.simulateAdvertisementReceived(
diff --git a/third_party/blink/web_tests/virtual/module-top-level-await/external/wpt/html/semantics/scripting-1/the-script-element/module/evaluation-order-1-expected.txt b/third_party/blink/web_tests/virtual/module-top-level-await/external/wpt/html/semantics/scripting-1/the-script-element/module/evaluation-order-1-expected.txt new file mode 100644 index 0000000..cf2aed88 --- /dev/null +++ b/third_party/blink/web_tests/virtual/module-top-level-await/external/wpt/html/semantics/scripting-1/the-script-element/module/evaluation-order-1-expected.txt
@@ -0,0 +1,4 @@ +This is a testharness.js-based test. +FAIL Test evaluation order of modules assert_array_equals: expected property 2 to be "global-error" but got "microtask" (expected array ["step-1-1", "step-1-2", "global-error", "error", "microtask", "script-load", "global-load"] got ["step-1-1", "step-1-2", "microtask", "global-error", "error", "script-load", "global-load"]) +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/virtual/module-top-level-await/external/wpt/html/semantics/scripting-1/the-script-element/module/evaluation-order-1-sharedworker-expected.txt b/third_party/blink/web_tests/virtual/module-top-level-await/external/wpt/html/semantics/scripting-1/the-script-element/module/evaluation-order-1-sharedworker-expected.txt new file mode 100644 index 0000000..7ce88a5 --- /dev/null +++ b/third_party/blink/web_tests/virtual/module-top-level-await/external/wpt/html/semantics/scripting-1/the-script-element/module/evaluation-order-1-sharedworker-expected.txt
@@ -0,0 +1,4 @@ +This is a testharness.js-based test. +FAIL Test evaluation order of modules assert_array_equals: expected property 2 to be "global-error" but got "microtask" (expected array ["step-1-1", "step-1-2", "global-error", "error", "microtask"] got ["step-1-1", "step-1-2", "microtask", "global-error", "error"]) +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/virtual/module-top-level-await/external/wpt/html/semantics/scripting-1/the-script-element/module/evaluation-order-1-worker-expected.txt b/third_party/blink/web_tests/virtual/module-top-level-await/external/wpt/html/semantics/scripting-1/the-script-element/module/evaluation-order-1-worker-expected.txt new file mode 100644 index 0000000..7ce88a5 --- /dev/null +++ b/third_party/blink/web_tests/virtual/module-top-level-await/external/wpt/html/semantics/scripting-1/the-script-element/module/evaluation-order-1-worker-expected.txt
@@ -0,0 +1,4 @@ +This is a testharness.js-based test. +FAIL Test evaluation order of modules assert_array_equals: expected property 2 to be "global-error" but got "microtask" (expected array ["step-1-1", "step-1-2", "global-error", "error", "microtask"] got ["step-1-1", "step-1-2", "microtask", "global-error", "error"]) +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/virtual/module-top-level-await/external/wpt/html/semantics/scripting-1/the-script-element/module/evaluation-order-2-import-expected.txt b/third_party/blink/web_tests/virtual/module-top-level-await/external/wpt/html/semantics/scripting-1/the-script-element/module/evaluation-order-2-import-expected.txt new file mode 100644 index 0000000..7433b031 --- /dev/null +++ b/third_party/blink/web_tests/virtual/module-top-level-await/external/wpt/html/semantics/scripting-1/the-script-element/module/evaluation-order-2-import-expected.txt
@@ -0,0 +1,4 @@ +This is a testharness.js-based test. +FAIL Test evaluation order of modules assert_array_equals: expected property 2 to be "global-error" but got "microtask-2.2" (expected array ["step-2.2-1", "step-2.2-2", "global-error", "error", "microtask-2.2", "script-load", "global-load"] got ["step-2.2-1", "step-2.2-2", "microtask-2.2", "global-error", "error", "script-load", "global-load"]) +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/virtual/module-top-level-await/external/wpt/html/semantics/scripting-1/the-script-element/module/evaluation-order-2-import-sharedworker-expected.txt b/third_party/blink/web_tests/virtual/module-top-level-await/external/wpt/html/semantics/scripting-1/the-script-element/module/evaluation-order-2-import-sharedworker-expected.txt new file mode 100644 index 0000000..269cc86 --- /dev/null +++ b/third_party/blink/web_tests/virtual/module-top-level-await/external/wpt/html/semantics/scripting-1/the-script-element/module/evaluation-order-2-import-sharedworker-expected.txt
@@ -0,0 +1,4 @@ +This is a testharness.js-based test. +FAIL Test evaluation order of modules assert_array_equals: expected property 2 to be "global-error" but got "microtask-2.2" (expected array ["step-2.2-1", "step-2.2-2", "global-error", "error", "microtask-2.2"] got ["step-2.2-1", "step-2.2-2", "microtask-2.2", "global-error", "error"]) +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/virtual/module-top-level-await/external/wpt/html/semantics/scripting-1/the-script-element/module/evaluation-order-2-import-worker-expected.txt b/third_party/blink/web_tests/virtual/module-top-level-await/external/wpt/html/semantics/scripting-1/the-script-element/module/evaluation-order-2-import-worker-expected.txt new file mode 100644 index 0000000..269cc86 --- /dev/null +++ b/third_party/blink/web_tests/virtual/module-top-level-await/external/wpt/html/semantics/scripting-1/the-script-element/module/evaluation-order-2-import-worker-expected.txt
@@ -0,0 +1,4 @@ +This is a testharness.js-based test. +FAIL Test evaluation order of modules assert_array_equals: expected property 2 to be "global-error" but got "microtask-2.2" (expected array ["step-2.2-1", "step-2.2-2", "global-error", "error", "microtask-2.2"] got ["step-2.2-1", "step-2.2-2", "microtask-2.2", "global-error", "error"]) +Harness: the test ran to completion. +
diff --git a/third_party/blink/web_tests/virtual/no-auto-wpt-origin-isolation/external/wpt/origin-isolation/removing-iframes.sub.https-expected.txt b/third_party/blink/web_tests/virtual/no-auto-wpt-origin-isolation/external/wpt/origin-isolation/removing-iframes.sub.https-expected.txt deleted file mode 100644 index 51fdc7a5..0000000 --- a/third_party/blink/web_tests/virtual/no-auto-wpt-origin-isolation/external/wpt/origin-isolation/removing-iframes.sub.https-expected.txt +++ /dev/null
@@ -1,18 +0,0 @@ -This is a testharness.js-based test. -PASS Before: messageerror event must occur -PASS Before: setting document.domain must not give sync access -PASS parent: originIsolated must equal true -PASS child1: originIsolated must equal false -PASS Remove the iframe and insert new ones -PASS Parent to child2: messageerror event must occur -PASS Parent to child2: setting document.domain must not give sync access -PASS Parent to child3: messageerror event must occur -PASS Parent to child3: setting document.domain must not give sync access -FAIL child2 to child3: message event must occur assert_equals: expected "WebAssembly.Module message received" but got "messageerror" -FAIL child2 to child3: setting document.domain must give sync access assert_equals: expected "accessed document successfully" but got "SecurityError" -FAIL child3 to child2: message event must occur assert_equals: expected "WebAssembly.Module message received" but got "messageerror" -FAIL child3 to child2: setting document.domain must give sync access assert_equals: expected "accessed document successfully" but got "SecurityError" -FAIL child2: originIsolated must equal false assert_equals: expected false but got true -PASS child3: originIsolated must equal false -Harness: the test ran to completion. -
diff --git a/third_party/blink/web_tests/virtual/scroll-unification/README.md b/third_party/blink/web_tests/virtual/scroll-unification/README.md new file mode 100644 index 0000000..85dade7 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scroll-unification/README.md
@@ -0,0 +1 @@ +This directory is dedicated for testing the "ScrollUnification" feature.
diff --git a/third_party/blink/web_tests/virtual/scroll-unification/README.txt b/third_party/blink/web_tests/virtual/scroll-unification/README.txt new file mode 100644 index 0000000..85dade7 --- /dev/null +++ b/third_party/blink/web_tests/virtual/scroll-unification/README.txt
@@ -0,0 +1 @@ +This directory is dedicated for testing the "ScrollUnification" feature.
diff --git a/third_party/grpc/grpc_library.gni b/third_party/grpc/grpc_library.gni index 09170662..8e09753 100644 --- a/third_party/grpc/grpc_library.gni +++ b/third_party/grpc/grpc_library.gni
@@ -22,6 +22,7 @@ "sources", "use_protobuf_full", "import_dirs", + "proto_in_dir", ]) remove_configs = [ "//build/config/compiler:chromium_code" ] extra_configs = [
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl index 9ade26d..b972237 100644 --- a/tools/mb/mb_config.pyl +++ b/tools/mb/mb_config.pyl
@@ -169,7 +169,7 @@ 'ToTLinuxThinLTO': 'clang_tot_release_minimal_symbols_thin_lto_opt_static', 'ToTLinuxUBSanVptr': 'clang_tot_ubsan_no_recover_hack_static_release', 'ToTMac': 'clang_tot_minimal_symbols_shared_release', - 'ToTMacOfficial': 'clang_tot_official', + 'ToTMacOfficial': 'mac_clang_tot_official', 'ToTMacCoverage': 'clang_tot_coverage_minimal_symbols_release', 'ToTMac (dbg)': 'clang_tot_shared_debug', 'ToTMacASan': 'asan_disable_nacl_clang_tot_minimal_symbols_static_release', @@ -2137,6 +2137,9 @@ 'release_trybot', 'arm64', ], + 'mac_clang_tot_official': [ + 'clang_tot', 'official', 'no_widevine_cdm_host_verification', + ], 'msan_no_origins_release_bot': [ 'msan_no_origins', 'release_bot',
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index d5d1f52..673a733 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -40593,8 +40593,6 @@ <int value="-2047822258" label="enable-avfoundation"/> <int value="-2047190657" label="SyncUserConsentSeparateType:enabled"/> <int value="-2047071651" label="NearbySharing:enabled"/> - <int value="-2044001553" - label="disable-experimental-accessibility-chromevox-language-switching"/> <int value="-2043128632" label="enable-tab-switcher-in-document-mode"/> <int value="-2042278511" label="SmartSuggestionForLargeDownloads:disabled"/> <int value="-2041281386" label="MultiPaste:disabled"/> @@ -41003,8 +41001,6 @@ <int value="-1648216169" label="NewOmniboxAnswerTypes:disabled"/> <int value="-1646016597" label="IsolatePrerenders:disabled"/> <int value="-1645071473" label="ChromeColors:disabled"/> - <int value="-1644721118" - label="disable-experimental-accessibility-chromevox-search-menus"/> <int value="-1643933608" label="SyncAutofillWalletOfferData:enabled"/> <int value="-1641832607" label="DragToPinTabs:enabled"/> <int value="-1638815914" label="enable-experimental-productivity-features"/> @@ -53353,7 +53349,7 @@ <int value="1201" label="Show Input Options In Shelf"/> <int value="1202" label="Show Personal Information Suggestions"/> <int value="1203" label="Show Emoji Suggestions"/> - <int value="1204" label="Change System Language"/> + <int value="1204" label="Change Device Language"/> <int value="1205" label="Offer Translation"/> <int value="1206" label="Add Input Method"/> <int value="1207" label="Spell Check"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 899c921..d1042f9 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -40561,7 +40561,7 @@ </summary> </histogram> -<histogram name="DevTools.PanelClosed" enum="DevToolsPanel" expires_after="M87"> +<histogram name="DevTools.PanelClosed" enum="DevToolsPanel" expires_after="M94"> <owner>yangguo@chromium.org</owner> <owner>joselea@microsoft.com</owner> <owner>shanejc@microsoft.com</owner> @@ -68045,6 +68045,26 @@ </summary> </histogram> +<histogram name="GPU.RenderableFormat.{FORMAT_TYPE}" enum="Boolean" + expires_after="2021-09-15"> + <owner>zmo@chromium.org</owner> + <owner>graphics-dev@chromium.org</owner> + <summary> + Whether or not a {FORMAT_TYPE} fbo is complete and renderable. + </summary> + <token key="FORMAT_TYPE"> + <variant name="R11F_G11F_B10F.FLOAT" summary="R11F_G11F_B10F/FLOAT"/> + <variant name="R16F.FLOAT" summary="R16F/FLOAT"/> + <variant name="R32F.FLOAT" summary="R32F/FLOAT"/> + <variant name="RG16F.FLOAT" summary="RG16F/FLOAT"/> + <variant name="RG32F.FLOAT" summary="RG32F/FLOAT"/> + <variant name="RGB32F.FLOAT" summary="RGB32F/FLOAT"/> + <variant name="RGBA16F.FLOAT" summary="RGBA16F/FLOAT"/> + <variant name="RGBA16F.HALF_FLOAT" summary="RGBA16F/HALF_FLOAT"/> + <variant name="RGBA32F.FLOAT" summary="RGBA32F/FLOAT"/> + </token> +</histogram> + <histogram name="GPU.Sandbox.InitializedSuccessfully" enum="BooleanSuccess" expires_after="2020-08-30"> <owner>vmiura@chromium.org</owner> @@ -131802,6 +131822,17 @@ </summary> </histogram> +<histogram + name="PaymentRequest.SecurePaymentConfirmationCredentialIdSizeInBytes" + units="bytes" expires_after="M93"> + <owner>danyao@chromium.org</owner> + <owner>web-payments-team@google.com</owner> + <summary> + Records the size of Credential ID for secure-payment-confirmation method in + bytes. + </summary> +</histogram> + <histogram name="PaymentRequest.SelectedPaymentMethod" enum="PaymentRequestPaymentMethods" expires_after="2017-08-15"> <obsolete>
diff --git a/tools/perf/page_sets/simple_canvas/video_to_sub_texture.html b/tools/perf/page_sets/simple_canvas/video_to_sub_texture.html index faaa48f..6a5dc8d8 100644 --- a/tools/perf/page_sets/simple_canvas/video_to_sub_texture.html +++ b/tools/perf/page_sets/simple_canvas/video_to_sub_texture.html
@@ -62,7 +62,6 @@ console.logFatalError("video/webm is unsupported"); throw 'video/webm is unsupported'; }; - // preRun(); CanvasRunner.startPlayingAndWaitForVideo(videoElement, startPerfTest); }
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml index a06af23..4333e03 100644 --- a/tools/traffic_annotation/summary/annotations.xml +++ b/tools/traffic_annotation/summary/annotations.xml
@@ -30,7 +30,8 @@ <item id="bluetooth_socket" added_in_milestone="65" hash_code="94099818" type="0" content_hash_code="30932349" os_list="linux,windows" file_path="device/bluetooth/bluetooth_socket_net.cc"/> <item id="brandcode_config" added_in_milestone="62" hash_code="109679553" type="0" content_hash_code="128843792" os_list="linux,windows" file_path="chrome/browser/profile_resetter/brandcode_config_fetcher.cc"/> <item id="browser_switcher_ieem_sitelist" added_in_milestone="72" hash_code="97159948" type="0" content_hash_code="129062966" os_list="linux,windows" file_path="chrome/browser/browser_switcher/browser_switcher_service.cc"/> - <item id="cablev2_websocket_from_client" added_in_milestone="86" hash_code="3464399" type="0" content_hash_code="46324469" os_list="windows,linux" file_path="device/fido/cable/fido_tunnel_device.cc"/> + <item id="cablev2_websocket_from_authenticator" added_in_milestone="87" hash_code="28613769" type="0" content_hash_code="119863612" os_list="linux,windows" file_path="device/fido/cable/v2_authenticator.cc"/> + <item id="cablev2_websocket_from_client" added_in_milestone="86" hash_code="3464399" type="0" content_hash_code="116618103" os_list="windows,linux" file_path="device/fido/cable/fido_tunnel_device.cc"/> <item id="captive_portal_service" added_in_milestone="62" hash_code="88754904" type="0" content_hash_code="70737580" os_list="linux,windows" file_path="components/captive_portal/content/captive_portal_service.cc"/> <item id="cast_channel_send" added_in_milestone="66" hash_code="103172229" type="0" deprecated="2018-08-23" content_hash_code="33946302" file_path=""/> <item id="cast_keep_alive_delegate" added_in_milestone="66" hash_code="134755844" type="0" deprecated="2018-08-23" content_hash_code="66118796" file_path=""/> @@ -278,7 +279,9 @@ <item id="resource_prefetch" added_in_milestone="62" hash_code="110815970" type="0" deprecated="2018-02-28" content_hash_code="39251261" file_path=""/> <item id="rlz_ping" added_in_milestone="63" hash_code="99279418" type="0" content_hash_code="102108802" os_list="windows" file_path="rlz/lib/financial_ping.cc"/> <item id="safe_browsing_backup_request" added_in_milestone="62" hash_code="106980485" type="0" deprecated="2018-08-14" content_hash_code="101760679" file_path=""/> - <item id="safe_browsing_binary_upload" added_in_milestone="78" hash_code="71663319" type="0" content_hash_code="105913171" os_list="linux,windows" file_path="chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc"/> + <item id="safe_browsing_binary_upload" added_in_milestone="78" hash_code="71663319" type="0" deprecated="2020-09-16" content_hash_code="105913171" file_path=""/> + <item id="safe_browsing_binary_upload_app" added_in_milestone="87" hash_code="4306022" type="0" content_hash_code="64626873" os_list="linux,windows" file_path="chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc"/> + <item id="safe_browsing_binary_upload_connector" added_in_milestone="87" hash_code="59568147" type="0" content_hash_code="69870179" os_list="linux,windows" file_path="chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc"/> <item id="safe_browsing_cache_collector" added_in_milestone="62" hash_code="115907811" type="0" content_hash_code="62296373" os_list="linux,windows" file_path="components/safe_browsing/content/browser/threat_details_cache.cc"/> <item id="safe_browsing_certificate_error_reporting" added_in_milestone="62" hash_code="66590631" type="0" content_hash_code="26108454" os_list="linux,windows" file_path="chrome/browser/ssl/certificate_error_reporter.cc"/> <item id="safe_browsing_chunk_backup_request" added_in_milestone="62" hash_code="79957943" type="0" deprecated="2018-08-14" content_hash_code="133850277" file_path=""/>
diff --git a/tools/traffic_annotation/summary/grouping.xml b/tools/traffic_annotation/summary/grouping.xml index 588ea5e..fd6c7270 100644 --- a/tools/traffic_annotation/summary/grouping.xml +++ b/tools/traffic_annotation/summary/grouping.xml
@@ -208,7 +208,8 @@ <traffic_annotation unique_id="safe_browsing_module_loader"/> <traffic_annotation unique_id="safe_browsing_v4_get_hash"/> <traffic_annotation unique_id="safe_browsing_v4_update"/> - <traffic_annotation unique_id="safe_browsing_binary_upload"/> + <traffic_annotation unique_id="safe_browsing_binary_upload_app"/> + <traffic_annotation unique_id="safe_browsing_binary_upload_connector"/> <traffic_annotation unique_id="safe_browsing_realtime_url_lookup"/> <traffic_annotation unique_id="unwanted_software_report"/> <traffic_annotation unique_id="ppapi_download_request"/> @@ -413,6 +414,7 @@ </sender> <sender name="Phone as a Security Key"> <traffic_annotation unique_id="cablev2_websocket_from_client"/> + <traffic_annotation unique_id="cablev2_websocket_from_authenticator"/> </sender> </group> </groups>
diff --git a/ui/accessibility/accessibility_switches.cc b/ui/accessibility/accessibility_switches.cc index 07dafef7..3fa41e0 100644 --- a/ui/accessibility/accessibility_switches.cc +++ b/ui/accessibility/accessibility_switches.cc
@@ -36,14 +36,6 @@ const char kEnableExperimentalAccessibilityChromeVoxAnnotations[] = "enable-experimental-accessibility-chromevox-annotations"; -// Disables ChromeVox language switching feature. -const char kDisableExperimentalAccessibilityChromeVoxLanguageSwitching[] = - "disable-experimental-accessibility-chromevox-language-switching"; - -// Disables ChromeVox search menus feature. -const char kDisableExperimentalAccessibilityChromeVoxSearchMenus[] = - "disable-experimental-accessibility-chromevox-search-menus"; - // Enables interactive tutorial for ChromeVox. const char kEnableExperimentalAccessibilityChromeVoxTutorial[] = "enable-experimental-accessibility-chromevox-tutorial";
diff --git a/ui/accessibility/accessibility_switches.h b/ui/accessibility/accessibility_switches.h index 0a9f3ab..9142a13 100644 --- a/ui/accessibility/accessibility_switches.h +++ b/ui/accessibility/accessibility_switches.h
@@ -23,10 +23,6 @@ AX_BASE_EXPORT extern const char kEnableExperimentalAccessibilityChromeVoxAnnotations[]; AX_BASE_EXPORT extern const char - kDisableExperimentalAccessibilityChromeVoxLanguageSwitching[]; -AX_BASE_EXPORT extern const char - kDisableExperimentalAccessibilityChromeVoxSearchMenus[]; -AX_BASE_EXPORT extern const char kEnableExperimentalAccessibilityChromeVoxTutorial[]; AX_BASE_EXPORT extern const char kEnableSwitchAccessPointScanning[];
diff --git a/ui/gfx/gpu_fence.cc b/ui/gfx/gpu_fence.cc index a8c2422..027e4c62 100644 --- a/ui/gfx/gpu_fence.cc +++ b/ui/gfx/gpu_fence.cc
@@ -14,38 +14,13 @@ namespace gfx { -GpuFence::GpuFence(const GpuFenceHandle& handle) : type_(handle.type) { - switch (type_) { - case GpuFenceHandleType::kEmpty: - break; - case GpuFenceHandleType::kAndroidNativeFenceSync: -#if defined(OS_POSIX) - owned_fd_.reset(handle.native_fd.fd); -#else - NOTREACHED(); -#endif - break; - } -} +GpuFence::GpuFence(GpuFenceHandle fence_handle) + : fence_handle_(std::move(fence_handle)) {} GpuFence::~GpuFence() = default; -GpuFenceHandle GpuFence::GetGpuFenceHandle() const { - gfx::GpuFenceHandle handle; - switch (type_) { - case GpuFenceHandleType::kEmpty: - break; - case GpuFenceHandleType::kAndroidNativeFenceSync: -#if defined(OS_POSIX) - handle.type = gfx::GpuFenceHandleType::kAndroidNativeFenceSync; - handle.native_fd = base::FileDescriptor(owned_fd_.get(), - /*auto_close=*/false); -#else - NOTREACHED(); -#endif - break; - } - return handle; +const GpuFenceHandle& GpuFence::GetGpuFenceHandle() const { + return fence_handle_; } ClientGpuFence GpuFence::AsClientGpuFence() { @@ -58,14 +33,15 @@ } void GpuFence::Wait() { - switch (type_) { + switch (fence_handle_.type) { case GpuFenceHandleType::kEmpty: break; case GpuFenceHandleType::kAndroidNativeFenceSync: #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID) static const int kInfiniteSyncWaitTimeout = -1; - DCHECK_GE(owned_fd_.get(), 0); - if (sync_wait(owned_fd_.get(), kInfiniteSyncWaitTimeout) < 0) { + DCHECK_GE(fence_handle_.owned_fd.get(), 0); + if (sync_wait(fence_handle_.owned_fd.get(), kInfiniteSyncWaitTimeout) < + 0) { LOG(FATAL) << "Failed while waiting for gpu fence fd"; } #else @@ -112,7 +88,8 @@ base::TimeTicks GpuFence::GetMaxTimestamp() const { base::TimeTicks timestamp; #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_ANDROID) - FenceStatus status = GetStatusChangeTime(owned_fd_.get(), ×tamp); + FenceStatus status = + GetStatusChangeTime(fence_handle_.owned_fd.get(), ×tamp); DCHECK_EQ(status, FenceStatus::kSignaled); return timestamp; #endif
diff --git a/ui/gfx/gpu_fence.h b/ui/gfx/gpu_fence.h index 2d2a24b..968cf168 100644 --- a/ui/gfx/gpu_fence.h +++ b/ui/gfx/gpu_fence.h
@@ -23,14 +23,14 @@ class GFX_EXPORT GpuFence { public: // Constructor takes ownership of the source handle's resources. - explicit GpuFence(const GpuFenceHandle& handle); + explicit GpuFence(GpuFenceHandle handle); GpuFence() = delete; ~GpuFence(); - // This handle is an unowned view of the resources owned by this class for - // use with CloneHandleForIPC. Don't pass this to a consuming method such as - // GpuFence(handle) or to IPC, that would cause duplicate resource release. - GpuFenceHandle GetGpuFenceHandle() const; + // Returns a const reference to the underlying GpuFenceHandle + // owned by GpuFence. If you'd like a duplicated handle for use + // with IPC, call the Clone method on the returned handle. + const GpuFenceHandle& GetGpuFenceHandle() const; // Casts for use with the GLES interface. ClientGpuFence AsClientGpuFence(); @@ -45,10 +45,7 @@ base::TimeTicks GetMaxTimestamp() const; private: - gfx::GpuFenceHandleType type_; -#if defined(OS_POSIX) - base::ScopedFD owned_fd_; -#endif + gfx::GpuFenceHandle fence_handle_; DISALLOW_COPY_AND_ASSIGN(GpuFence); };
diff --git a/ui/gfx/gpu_fence_handle.cc b/ui/gfx/gpu_fence_handle.cc index f8cce06c..7c5e38c 100644 --- a/ui/gfx/gpu_fence_handle.cc +++ b/ui/gfx/gpu_fence_handle.cc
@@ -14,28 +14,26 @@ namespace gfx { -GpuFenceHandle::GpuFenceHandle() : type(GpuFenceHandleType::kEmpty) {} +GpuFenceHandle::GpuFenceHandle() = default; -GpuFenceHandle::GpuFenceHandle(const GpuFenceHandle& other) = default; +GpuFenceHandle::GpuFenceHandle(GpuFenceHandle&& other) = default; -GpuFenceHandle& GpuFenceHandle::operator=(const GpuFenceHandle& other) = - default; +GpuFenceHandle& GpuFenceHandle::operator=(GpuFenceHandle&& other) = default; -GpuFenceHandle::~GpuFenceHandle() {} +GpuFenceHandle::~GpuFenceHandle() = default; -GpuFenceHandle CloneHandleForIPC(const GpuFenceHandle& source_handle) { - switch (source_handle.type) { +GpuFenceHandle GpuFenceHandle::Clone() const { + switch (type) { case GpuFenceHandleType::kEmpty: - NOTREACHED(); - return source_handle; + break; case GpuFenceHandleType::kAndroidNativeFenceSync: { gfx::GpuFenceHandle handle; #if defined(OS_POSIX) handle.type = GpuFenceHandleType::kAndroidNativeFenceSync; - int duped_handle = HANDLE_EINTR(dup(source_handle.native_fd.fd)); + const int duped_handle = HANDLE_EINTR(dup(owned_fd.get())); if (duped_handle < 0) return GpuFenceHandle(); - handle.native_fd = base::FileDescriptor(duped_handle, true); + handle.owned_fd = base::ScopedFD(duped_handle); #endif return handle; }
diff --git a/ui/gfx/gpu_fence_handle.h b/ui/gfx/gpu_fence_handle.h index c79c23b..0e1bf08e 100644 --- a/ui/gfx/gpu_fence_handle.h +++ b/ui/gfx/gpu_fence_handle.h
@@ -5,11 +5,12 @@ #ifndef UI_GFX_GPU_FENCE_HANDLE_H_ #define UI_GFX_GPU_FENCE_HANDLE_H_ +#include "base/macros.h" #include "build/build_config.h" #include "ui/gfx/gfx_export.h" #if defined(OS_POSIX) || defined(OS_FUCHSIA) -#include "base/file_descriptor_posix.h" +#include "base/files/scoped_file.h" #endif namespace gfx { @@ -27,24 +28,27 @@ }; struct GFX_EXPORT GpuFenceHandle { + GpuFenceHandle(const GpuFenceHandle&) = delete; + GpuFenceHandle& operator=(const GpuFenceHandle&) = delete; + GpuFenceHandle(); - GpuFenceHandle(const GpuFenceHandle& other); - GpuFenceHandle& operator=(const GpuFenceHandle& other); + GpuFenceHandle(GpuFenceHandle&& other); + GpuFenceHandle& operator=(GpuFenceHandle&& other); ~GpuFenceHandle(); bool is_null() const { return type == GpuFenceHandleType::kEmpty; } - GpuFenceHandleType type; + // Returns an instance of |handle| which can be sent over IPC. This duplicates + // the handle so that IPC code can take ownership of it without invalidating + // |handle| itself. + GpuFenceHandle Clone() const; + + GpuFenceHandleType type = GpuFenceHandleType::kEmpty; #if defined(OS_POSIX) || defined(OS_FUCHSIA) - base::FileDescriptor native_fd; + base::ScopedFD owned_fd; #endif }; -// Returns an instance of |handle| which can be sent over IPC. This duplicates -// the file-handles as appropriate, so that the IPC code take ownership of them, -// without invalidating |handle| itself. -GFX_EXPORT GpuFenceHandle CloneHandleForIPC(const GpuFenceHandle& handle); - } // namespace gfx #endif // UI_GFX_GPU_FENCE_HANDLE_H_
diff --git a/ui/gfx/ipc/gfx_param_traits_macros.h b/ui/gfx/ipc/gfx_param_traits_macros.h index f311c8d..5f20bc6e 100644 --- a/ui/gfx/ipc/gfx_param_traits_macros.h +++ b/ui/gfx/ipc/gfx_param_traits_macros.h
@@ -111,7 +111,7 @@ IPC_STRUCT_TRAITS_BEGIN(gfx::GpuFenceHandle) IPC_STRUCT_TRAITS_MEMBER(type) #if defined(OS_POSIX) - IPC_STRUCT_TRAITS_MEMBER(native_fd) + IPC_STRUCT_TRAITS_MEMBER(owned_fd) #endif IPC_STRUCT_TRAITS_END()
diff --git a/ui/gfx/mojom/BUILD.gn b/ui/gfx/mojom/BUILD.gn index 723312a2..a90212c 100644 --- a/ui/gfx/mojom/BUILD.gn +++ b/ui/gfx/mojom/BUILD.gn
@@ -79,6 +79,8 @@ { mojom = "gfx.mojom.GpuFenceHandle" cpp = "::gfx::GpuFenceHandle" + move_only = true + nullable_is_same_type = true }, ] traits_headers = [ "gpu_fence_handle_mojom_traits.h" ]
diff --git a/ui/gfx/mojom/gpu_fence_handle_mojom_traits.cc b/ui/gfx/mojom/gpu_fence_handle_mojom_traits.cc index c5b500f..033d74b 100644 --- a/ui/gfx/mojom/gpu_fence_handle_mojom_traits.cc +++ b/ui/gfx/mojom/gpu_fence_handle_mojom_traits.cc
@@ -9,13 +9,13 @@ namespace mojo { -mojo::PlatformHandle StructTraits< - gfx::mojom::GpuFenceHandleDataView, - gfx::GpuFenceHandle>::native_fd(const gfx::GpuFenceHandle& handle) { +mojo::PlatformHandle +StructTraits<gfx::mojom::GpuFenceHandleDataView, + gfx::GpuFenceHandle>::native_fd(gfx::GpuFenceHandle& handle) { #if defined(OS_POSIX) if (handle.type != gfx::GpuFenceHandleType::kAndroidNativeFenceSync) return mojo::PlatformHandle(); - return mojo::PlatformHandle(base::ScopedFD(handle.native_fd.fd)); + return mojo::PlatformHandle(std::move(handle.owned_fd)); #else return mojo::PlatformHandle(); #endif @@ -28,9 +28,7 @@ if (out->type == gfx::GpuFenceHandleType::kAndroidNativeFenceSync) { #if defined(OS_POSIX) - constexpr bool auto_close = true; - out->native_fd = - base::FileDescriptor(data.TakeNativeFd().ReleaseFD(), auto_close); + out->owned_fd = data.TakeNativeFd().TakeFD(); return true; #else NOTREACHED(); @@ -40,4 +38,12 @@ return true; } +void StructTraits<gfx::mojom::GpuFenceHandleDataView, + gfx::GpuFenceHandle>::SetToNull(gfx::GpuFenceHandle* handle) { + handle->type = gfx::GpuFenceHandleType::kEmpty; +#if defined(OS_POSIX) + handle->owned_fd.reset(); +#endif +} + } // namespace mojo
diff --git a/ui/gfx/mojom/gpu_fence_handle_mojom_traits.h b/ui/gfx/mojom/gpu_fence_handle_mojom_traits.h index f688b2cb..504fcdc 100644 --- a/ui/gfx/mojom/gpu_fence_handle_mojom_traits.h +++ b/ui/gfx/mojom/gpu_fence_handle_mojom_traits.h
@@ -45,9 +45,10 @@ static gfx::GpuFenceHandleType type(const gfx::GpuFenceHandle& handle) { return handle.type; } - static mojo::PlatformHandle native_fd(const gfx::GpuFenceHandle& handle); + static mojo::PlatformHandle native_fd(gfx::GpuFenceHandle& handle); static bool Read(gfx::mojom::GpuFenceHandleDataView data, gfx::GpuFenceHandle* handle); + static void SetToNull(gfx::GpuFenceHandle* handle); }; } // namespace mojo
diff --git a/ui/gl/gl_fence_android_native_fence_sync.cc b/ui/gl/gl_fence_android_native_fence_sync.cc index 64d1e6e1d..09b25f3 100644 --- a/ui/gl/gl_fence_android_native_fence_sync.cc +++ b/ui/gl/gl_fence_android_native_fence_sync.cc
@@ -49,26 +49,25 @@ std::unique_ptr<GLFenceAndroidNativeFenceSync> GLFenceAndroidNativeFenceSync::CreateFromGpuFence( const gfx::GpuFence& gpu_fence) { - gfx::GpuFenceHandle handle = - gfx::CloneHandleForIPC(gpu_fence.GetGpuFenceHandle()); - DCHECK_GE(handle.native_fd.fd, 0); - EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, handle.native_fd.fd, - EGL_NONE}; + gfx::GpuFenceHandle handle = gpu_fence.GetGpuFenceHandle().Clone(); + DCHECK_GE(handle.owned_fd.get(), 0); + EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, + handle.owned_fd.release(), EGL_NONE}; return CreateInternal(EGL_SYNC_NATIVE_FENCE_ANDROID, attribs); } std::unique_ptr<gfx::GpuFence> GLFenceAndroidNativeFenceSync::GetGpuFence() { DCHECK(GLSurfaceEGL::IsAndroidNativeFenceSyncSupported()); - EGLint sync_fd = eglDupNativeFenceFDANDROID(display_, sync_); + const EGLint sync_fd = eglDupNativeFenceFDANDROID(display_, sync_); if (sync_fd < 0) return nullptr; gfx::GpuFenceHandle handle; handle.type = gfx::GpuFenceHandleType::kAndroidNativeFenceSync; - handle.native_fd = base::FileDescriptor(sync_fd, /*auto_close=*/true); + handle.owned_fd = base::ScopedFD(sync_fd); - return std::make_unique<gfx::GpuFence>(handle); + return std::make_unique<gfx::GpuFence>(std::move(handle)); } base::TimeTicks GLFenceAndroidNativeFenceSync::GetStatusChangeTime() {
diff --git a/ui/gl/gl_image_ahardwarebuffer.cc b/ui/gl/gl_image_ahardwarebuffer.cc index 93c0e1a..8b42d42 100644 --- a/ui/gl/gl_image_ahardwarebuffer.cc +++ b/ui/gl/gl_image_ahardwarebuffer.cc
@@ -76,9 +76,8 @@ gfx::GpuFenceHandle handle; handle.type = gfx::GpuFenceHandleType::kAndroidNativeFenceSync; - handle.native_fd = - base::FileDescriptor(fence_fd.release(), /*auto_close=*/true); - gfx::GpuFence gpu_fence(handle); + handle.owned_fd = std::move(fence_fd); + gfx::GpuFence gpu_fence(std::move(handle)); auto gl_fence = GLFence::CreateFromGpuFence(gpu_fence); gl_fence->ServerWait(); }
diff --git a/ui/gl/gl_surface_egl_surface_control.cc b/ui/gl/gl_surface_egl_surface_control.cc index 24d9bc98..6ba5d24 100644 --- a/ui/gl/gl_surface_egl_surface_control.cc +++ b/ui/gl/gl_surface_egl_surface_control.cc
@@ -347,11 +347,10 @@ surface_state.hardware_buffer = hardware_buffer; if (gpu_fence && surface_state.hardware_buffer) { - auto fence_handle = - gfx::CloneHandleForIPC(gpu_fence->GetGpuFenceHandle()); + auto fence_handle = gpu_fence->GetGpuFenceHandle().Clone(); DCHECK(!fence_handle.is_null()); - fence_fd = MergeFDs(std::move(fence_fd), - base::ScopedFD(fence_handle.native_fd.fd)); + fence_fd = + MergeFDs(std::move(fence_fd), std::move(fence_handle.owned_fd)); } if (is_primary_plane) {
diff --git a/ui/ozone/platform/drm/gpu/drm_overlay_plane.cc b/ui/ozone/platform/drm/gpu/drm_overlay_plane.cc index 72a00b7d..b88db11 100644 --- a/ui/ozone/platform/drm/gpu/drm_overlay_plane.cc +++ b/ui/ozone/platform/drm/gpu/drm_overlay_plane.cc
@@ -20,7 +20,7 @@ if (!gpu_fence) return nullptr; return std::make_unique<gfx::GpuFence>( - gfx::CloneHandleForIPC(gpu_fence->GetGpuFenceHandle())); + gpu_fence->GetGpuFenceHandle().Clone()); } } // namespace
diff --git a/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc b/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc index a868bb51..90d7a20 100644 --- a/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc +++ b/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc
@@ -244,7 +244,7 @@ for (auto& overlay : unsubmitted_frames_.front()->overlays) { if (overlay.z_order() == 0 && overlay.gpu_fence()) { submitted_frame_gpu_fence_ = std::make_unique<gfx::GpuFence>( - gfx::CloneHandleForIPC(overlay.gpu_fence()->GetGpuFenceHandle())); + overlay.gpu_fence()->GetGpuFenceHandle().Clone()); break; } }
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc index 589cbc8..50dc7dbe 100644 --- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc +++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc
@@ -47,12 +47,14 @@ HardwareDisplayPlaneManager::~HardwareDisplayPlaneManager() = default; bool HardwareDisplayPlaneManager::Initialize() { -// Try to get all of the planes if possible, so we don't have to try to -// discover hidden primary planes. -#if defined(DRM_CLIENT_CAP_UNIVERSAL_PLANES) + // Try to get all of the planes if possible, so we don't have to try to + // discover hidden primary planes. has_universal_planes_ = drm_->SetCapability(DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); -#endif + + // This is to test whether or not it is safe to remove non-universal planes + // supporting code in a following CL. See crbug.com/1129546 for more details. + CHECK(has_universal_planes_); if (!InitializeCrtcState()) return false;
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc index 35c6e2b..72831671 100644 --- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc +++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc
@@ -45,8 +45,8 @@ if (merged_fd.is_valid()) { gfx::GpuFenceHandle handle; handle.type = gfx::GpuFenceHandleType::kAndroidNativeFenceSync; - handle.native_fd = base::FileDescriptor(std::move(merged_fd)); - return std::make_unique<gfx::GpuFence>(handle); + handle.owned_fd = std::move(merged_fd); + return std::make_unique<gfx::GpuFence>(std::move(handle)); } return nullptr; @@ -305,7 +305,7 @@ LOG(ERROR) << "Received invalid gpu fence"; return false; } - fence_fd = gpu_fence_handle.native_fd.fd; + fence_fd = gpu_fence_handle.owned_fd.get(); } if (!atomic_plane->SetPlaneData(plane_list->atomic_property_set.get(),
diff --git a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc index 6a85493..94ea747 100644 --- a/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc +++ b/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc
@@ -1037,9 +1037,8 @@ std::unique_ptr<gfx::GpuFence> FakeFenceFD::GetGpuFence() const { gfx::GpuFenceHandle handle; handle.type = gfx::GpuFenceHandleType::kAndroidNativeFenceSync; - handle.native_fd = - base::FileDescriptor(HANDLE_EINTR(dup(read_fd.get())), true); - return std::make_unique<gfx::GpuFence>(handle); + handle.owned_fd = base::ScopedFD(HANDLE_EINTR(dup(read_fd.get()))); + return std::make_unique<gfx::GpuFence>(std::move(handle)); } void FakeFenceFD::Signal() const {
diff --git a/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.cc b/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.cc index e3792ee..7ed853e 100644 --- a/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.cc +++ b/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.cc
@@ -135,9 +135,8 @@ gfx::GpuFenceHandle gpu_fence_handle; gpu_fence_handle.type = gfx::GpuFenceHandleType::kAndroidNativeFenceSync; - gpu_fence_handle.native_fd = - base::FileDescriptor(fence_fd, true /* auto_close */); - return std::make_unique<gfx::GpuFence>(gpu_fence_handle); + gpu_fence_handle.owned_fd = base::ScopedFD(fence_fd); + return std::make_unique<gfx::GpuFence>(std::move(gpu_fence_handle)); } VkSemaphore VulkanImplementationGbm::CreateExternalSemaphore(
diff --git a/ui/ozone/platform/scenic/sysmem_buffer_collection.cc b/ui/ozone/platform/scenic/sysmem_buffer_collection.cc index d320987..300e188 100644 --- a/ui/ozone/platform/scenic/sysmem_buffer_collection.cc +++ b/ui/ozone/platform/scenic/sysmem_buffer_collection.cc
@@ -129,11 +129,13 @@ bool SysmemBufferCollection::Initialize( fuchsia::sysmem::Allocator_Sync* allocator, + zx::channel token_handle, gfx::Size size, gfx::BufferFormat format, gfx::BufferUsage usage, VkDevice vk_device, - size_t num_buffers) { + size_t min_buffer_count, + bool force_protected) { DCHECK(IsNativePixmapConfigSupported(format, usage)); DCHECK(!collection_); DCHECK(!vk_buffer_collection_); @@ -143,51 +145,41 @@ if (vk_device == VK_NULL_HANDLE) return false; - min_size_ = size; + if (size.IsEmpty()) { + // Buffer collection that doesn't have explicit size is expected to be + // shared with other participants, who will determine the actual image size. + DCHECK(token_handle); + + // Set nominal size of 1x1, which will be used only for + // vkSetBufferCollectionConstraintsFUCHSIA(). The actual size of the + // allocated buffers is determined by constraints set by other sysmem + // clients for the same collection. Size of the Vulkan image is determined + // by the values passed to CreateVkImage(). + min_size_ = gfx::Size(1, 1); + } else { + min_size_ = size; + } + format_ = format; usage_ = usage; vk_device_ = vk_device; + is_protected_ = force_protected; fuchsia::sysmem::BufferCollectionTokenSyncPtr collection_token; - zx_status_t status = - allocator->AllocateSharedCollection(collection_token.NewRequest()); - if (status != ZX_OK) { - ZX_DLOG(ERROR, status) - << "fuchsia.sysmem.Allocator.AllocateSharedCollection()"; - return false; + if (token_handle) { + collection_token.Bind(std::move(token_handle)); + } else { + zx_status_t status = + allocator->AllocateSharedCollection(collection_token.NewRequest()); + if (status != ZX_OK) { + ZX_DLOG(ERROR, status) + << "fuchsia.sysmem.Allocator.AllocateSharedCollection()"; + return false; + } } return InitializeInternal(allocator, std::move(collection_token), - num_buffers); -} - -bool SysmemBufferCollection::Initialize( - fuchsia::sysmem::Allocator_Sync* allocator, - VkDevice vk_device, - zx::channel token_handle, - gfx::BufferFormat format, - gfx::BufferUsage usage, - bool force_protected) { - DCHECK(!collection_); - DCHECK(!vk_buffer_collection_); - - // Set nominal size of 1x1, which will be used only for - // vkSetBufferCollectionConstraintsFUCHSIA(). The actual size of the allocated - // buffers is determined by constraints set by other sysmem clients for the - // same collection. Size of the Vulkan image is determined by the valus passed - // to CreateVkImage(). - min_size_ = gfx::Size(1, 1); - - vk_device_ = vk_device; - format_ = format; - usage_ = usage; - is_protected_ = force_protected; - - fuchsia::sysmem::BufferCollectionTokenSyncPtr token; - token.Bind(std::move(token_handle)); - - return InitializeInternal(allocator, std::move(token), - /*buffers_for_camping=*/0); + min_buffer_count); } scoped_refptr<gfx::NativePixmap> SysmemBufferCollection::CreateNativePixmap(
diff --git a/ui/ozone/platform/scenic/sysmem_buffer_collection.h b/ui/ozone/platform/scenic/sysmem_buffer_collection.h index 3462868..38f9e12 100644 --- a/ui/ozone/platform/scenic/sysmem_buffer_collection.h +++ b/ui/ozone/platform/scenic/sysmem_buffer_collection.h
@@ -39,18 +39,18 @@ SysmemBufferCollection(); explicit SysmemBufferCollection(gfx::SysmemBufferCollectionId id); + // Initializes the buffer collection and registers it with Vulkan using the + // specified |vk_device|. If |token_handle| is null then a new collection + // collection is created. |size| may be empty. In that case |token_handle| + // must not be null and the image size is determined by the other sysmem + // participants. bool Initialize(fuchsia::sysmem::Allocator_Sync* allocator, + zx::channel token_handle, gfx::Size size, gfx::BufferFormat format, gfx::BufferUsage usage, VkDevice vk_device, - size_t num_buffers); - - bool Initialize(fuchsia::sysmem::Allocator_Sync* allocator, - VkDevice vk_device, - zx::channel token, - gfx::BufferFormat format, - gfx::BufferUsage usage, + size_t min_buffer_count, bool force_protected); // Must not be called more than once.
diff --git a/ui/ozone/platform/scenic/sysmem_buffer_manager.cc b/ui/ozone/platform/scenic/sysmem_buffer_manager.cc index b1bdfb23..8138ded 100644 --- a/ui/ozone/platform/scenic/sysmem_buffer_manager.cc +++ b/ui/ozone/platform/scenic/sysmem_buffer_manager.cc
@@ -37,10 +37,11 @@ gfx::Size size, gfx::BufferFormat format, gfx::BufferUsage usage, - size_t num_buffers) { + size_t min_buffer_count) { auto result = base::MakeRefCounted<SysmemBufferCollection>(); - if (!result->Initialize(allocator_.get(), size, format, usage, vk_device, - num_buffers)) { + if (!result->Initialize(allocator_.get(), /*token_channel=*/zx::channel(), + size, format, usage, vk_device, min_buffer_count, + /*force_protected=*/false)) { return nullptr; } RegisterCollection(result.get()); @@ -52,12 +53,15 @@ VkDevice vk_device, gfx::SysmemBufferCollectionId id, zx::channel token, + gfx::Size size, gfx::BufferFormat format, gfx::BufferUsage usage, + size_t min_buffer_count, bool force_protected) { auto result = base::MakeRefCounted<SysmemBufferCollection>(id); - if (!result->Initialize(allocator_.get(), vk_device, std::move(token), format, - usage, force_protected)) { + if (!result->Initialize(allocator_.get(), std::move(token), size, format, + usage, vk_device, min_buffer_count, + force_protected)) { return nullptr; } RegisterCollection(result.get());
diff --git a/ui/ozone/platform/scenic/sysmem_buffer_manager.h b/ui/ozone/platform/scenic/sysmem_buffer_manager.h index 9bc6fe9b..6845d8d 100644 --- a/ui/ozone/platform/scenic/sysmem_buffer_manager.h +++ b/ui/ozone/platform/scenic/sysmem_buffer_manager.h
@@ -47,8 +47,10 @@ VkDevice vk_device, gfx::SysmemBufferCollectionId id, zx::channel token, + gfx::Size size, gfx::BufferFormat format, gfx::BufferUsage usage, + size_t min_buffer_count, bool force_protected); scoped_refptr<SysmemBufferCollection> GetCollectionById(
diff --git a/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc b/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc index 8085dc7..6f3d239 100644 --- a/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc +++ b/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc
@@ -300,14 +300,21 @@ gfx::SysmemBufferCollectionId id, zx::channel token, gfx::BufferFormat format, - gfx::BufferUsage usage) { + gfx::BufferUsage usage, + gfx::Size size, + size_t min_buffer_count) { // SCANOUT images must be protected in protected mode. bool force_protected = usage == gfx::BufferUsage::SCANOUT && enforce_protected_memory(); + auto buffer_collection = sysmem_buffer_manager_->ImportSysmemBufferCollection( + device, id, std::move(token), size, format, usage, min_buffer_count, + force_protected); + if (!buffer_collection) + return nullptr; + return std::make_unique<SysmemBufferCollectionImpl>( - sysmem_buffer_manager_->ImportSysmemBufferCollection( - device, id, std::move(token), format, usage, force_protected)); + std::move(buffer_collection)); } } // namespace ui
diff --git a/ui/ozone/platform/scenic/vulkan_implementation_scenic.h b/ui/ozone/platform/scenic/vulkan_implementation_scenic.h index cbc7e0d..d92869e7 100644 --- a/ui/ozone/platform/scenic/vulkan_implementation_scenic.h +++ b/ui/ozone/platform/scenic/vulkan_implementation_scenic.h
@@ -58,7 +58,9 @@ gfx::SysmemBufferCollectionId id, zx::channel token, gfx::BufferFormat format, - gfx::BufferUsage usage) override; + gfx::BufferUsage usage, + gfx::Size size, + size_t min_buffer_count) override; private: ScenicSurfaceFactory* const scenic_surface_factory_;
diff --git a/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc b/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc index 42fd819..9897b51 100644 --- a/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc +++ b/ui/ozone/platform/wayland/gpu/wayland_buffer_manager_gpu.cc
@@ -28,9 +28,8 @@ wayland_overlay_config->enable_blend = input.enable_blend; wayland_overlay_config->access_fence_handle = !input.gpu_fence || input.gpu_fence->GetGpuFenceHandle().is_null() - ? base::Optional<gfx::GpuFenceHandle>() - : base::Optional<gfx::GpuFenceHandle>( - gfx::CloneHandleForIPC(input.gpu_fence->GetGpuFenceHandle())); + ? gfx::GpuFenceHandle() + : input.gpu_fence->GetGpuFenceHandle().Clone(); return wayland_overlay_config; }
diff --git a/ui/views/controls/button/button.cc b/ui/views/controls/button/button.cc index d63761c..f0ea28f 100644 --- a/ui/views/controls/button/button.cc +++ b/ui/views/controls/button/button.cc
@@ -140,11 +140,19 @@ tooltip_text_ = tooltip_text; OnSetTooltipText(tooltip_text); TooltipTextChanged(); + OnPropertyChanged(&tooltip_text_, kPropertyEffectsNone); NotifyAccessibilityEvent(ax::mojom::Event::kTextChanged, true); } +base::string16 Button::GetTooltipText() const { + return tooltip_text_; +} + void Button::SetAccessibleName(const base::string16& name) { + if (name == accessible_name_) + return; accessible_name_ = name; + OnPropertyChanged(&accessible_name_, kPropertyEffectsNone); NotifyAccessibilityEvent(ax::mojom::Event::kTextChanged, true); } @@ -599,7 +607,9 @@ {Button::STATE_DISABLED, base::ASCIIToUTF16("STATE_DISABLED")}) BEGIN_METADATA(Button, InkDropHostView) +ADD_PROPERTY_METADATA(base::string16, AccessibleName) ADD_PROPERTY_METADATA(ButtonState, State) +ADD_PROPERTY_METADATA(base::string16, TooltipText) END_METADATA } // namespace views
diff --git a/ui/views/controls/button/button.h b/ui/views/controls/button/button.h index 88ca586..1e60d5d 100644 --- a/ui/views/controls/button/button.h +++ b/ui/views/controls/button/button.h
@@ -106,6 +106,7 @@ void SetFocusForPlatform(); void SetTooltipText(const base::string16& tooltip_text); + base::string16 GetTooltipText() const; int tag() const { return tag_; } void set_tag(int tag) { tag_ = tag; }
diff --git a/ui/webui/resources/cr_components/chromeos/cellular_setup/psim_flow_ui.js b/ui/webui/resources/cr_components/chromeos/cellular_setup/psim_flow_ui.js index f2d14f1..ceb48a75c 100644 --- a/ui/webui/resources/cr_components/chromeos/cellular_setup/psim_flow_ui.js +++ b/ui/webui/resources/cr_components/chromeos/cellular_setup/psim_flow_ui.js
@@ -4,14 +4,14 @@ cr.define('cellularSetup', function() { /** @enum{string} */ - const PSimPageName = { + /* #export */ const PSimPageName = { SIM_DETECT: 'sim-detect-page', PROVISIONING: 'provisioning-page', FINAL: 'final-page', }; /** @enum{string} */ - const PSimUIState = { + /* #export */ const PSimUIState = { IDLE: 'idle', STARTING_ACTIVATION: 'starting-activation', WAITING_FOR_ACTIVATION_TO_START: 'waiting-for-activation-to-start', @@ -184,6 +184,21 @@ this.updateButtonBarState_(); }, + navigateForward() { + // Navigate forward is only called by clicking next button + // from the provisioning page. + assert(this.selectedPSimPageName_ === PSimPageName.PROVISIONING); + this.state_ = PSimUIState.WAITING_FOR_ACTIVATION_TO_FINISH; + }, + + /** + * @returns {boolean} true if backward navigation was handled + */ + attemptBackwardNavigation() { + // Back navigation for pSIM flow always goes back to selection page + return false; + }, + /** @private */ updateButtonBarState_() { let buttonState;
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/BrowserControlsContainerView.java b/weblayer/browser/java/org/chromium/weblayer_private/BrowserControlsContainerView.java index 50b260ff..ffba77c 100644 --- a/weblayer/browser/java/org/chromium/weblayer_private/BrowserControlsContainerView.java +++ b/weblayer/browser/java/org/chromium/weblayer_private/BrowserControlsContainerView.java
@@ -130,13 +130,19 @@ // Used to delay processing fullscreen requests. private Runnable mSystemUiFullscreenResizeRunnable; - // Used to delay updating the image for the layer. + // Used to delay updating the image for the layer. private final Runnable mRefreshResourceIdRunnable = () -> { if (mView == null || mViewResourceAdapter == null) return; BrowserControlsContainerViewJni.get().updateControlsResource( mNativeBrowserControlsContainerView); }; + // Used to delay hiding the controls. + private final Runnable mHideControlsRunnable = this::hideControlsNow; + + // Used to delay showing the controls. + private final Runnable mShowControlsRunnable = this::showControlsNow; + public interface Delegate { /** * Requests that the page height be recalculated due to browser controls height changes. @@ -268,10 +274,13 @@ addView(view, new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.UNSPECIFIED_GRAVITY)); - // We always want to hide the real controls so they don't flash for a frame before we've - // figured out where to position them. - removeCallbacks(this::showControls); - hideControls(); + // The controls will be positioned in onLayout, which will result in showControls or + // hideControls being called. hideControls may delay the hide by a frame, resulting in the + // View flashing during the current frame. To work around this, we hide the controls here. + // showControls will also delay the showing by a frame, but that doesn't cause a flash + // because the bitmap will be visible until the setVisibility call completes. + hideControlsNow(); + mContentViewRenderView.removeCallbacks(mShowControlsRunnable); mDelegate.setAnimationConstraint(BrowserControlsState.BOTH); } @@ -350,24 +359,29 @@ mLastHeight = height; if (mLastWidth > 0 && mLastHeight > 0 && mViewResourceAdapter == null) { createAdapterAndLayer(); - if (prevHeight == 0) { - assert heightChanged; + if (prevHeight == 0 && mSavedState != null) { // If there wasn't a View before and we have non-empty saved state from a previous // BrowserControlsContainerView instance, apply those saved offsets now. We can't // rely on BrowserControlsOffsetManager to notify us of the correct location as we // usually do because it only notifies us when offsets change, but it likely didn't // get destroyed when the BrowserFragment got recreated, so it won't notify us // because it thinks we already have the correct offsets. - if (mSavedState != null) { - onOffsetsChanged(mSavedState.mControlsOffset, mSavedState.mContentOffset); - } else { - // If there wasn't a View before (or it had 0 height) and there's no state from - // a previous instance of this class, move the new View off the screen until - // BrowserControlsOffsetManager tells us where to position it. - moveControlsOffScreen(); - } - mSavedState = null; + onOffsetsChanged(mSavedState.mControlsOffset, mSavedState.mContentOffset); + } else { + // Position the new controls so the same amount of them is visible as with the + // previous controls (which will be 0 if there weren't controls or they were + // hidden). Ideally we'd hide the controls and wait for BrowserControlsOffsetManager + // to tell us where it wants them, but it communicates with us via frame metadata + // that is generated when the renderer's compositor submits a frame to viz, which is + // paused during page loads. Because of this, we might not get positioning + // information from the renderer for several seconds during page loads, so we need + // to attempt to position the controls ourselves here. This could in theory cause a + // flicker if we decide on a different position than the renderer does, but this + // hasn't been an issue in practice. + int delta = mIsTop ? height - prevHeight : prevHeight - height; + onOffsetsChanged(mControlsOffset - delta, mContentOffset); } + mSavedState = null; } else if (mViewResourceAdapter != null) { BrowserControlsContainerViewJni.get().setControlsSize( mNativeBrowserControlsContainerView, mLastWidth, mLastHeight); @@ -466,29 +480,41 @@ private void prepareForScroll() { mInScroll = true; - if (BrowserControlsContainerViewJni.get().shouldDelayVisibilityChange()) { - mContentViewRenderView.postOnAnimation(this::hideControls); - } else { - hideControls(); - } + hideControls(); } private void finishScroll() { mInScroll = false; - if (BrowserControlsContainerViewJni.get().shouldDelayVisibilityChange()) { - mContentViewRenderView.postOnAnimation(this::showControls); - } else { - showControls(); - } + showControls(); } private void hideControls() { - if (mView != null) mView.setVisibility(View.INVISIBLE); + if (BrowserControlsContainerViewJni.get().shouldDelayVisibilityChange()) { + mContentViewRenderView.postOnAnimation(mHideControlsRunnable); + } else { + hideControlsNow(); + } + } + + private void hideControlsNow() { + if (mView != null) { + mView.setVisibility(View.INVISIBLE); + } } private void showControls() { + if (BrowserControlsContainerViewJni.get().shouldDelayVisibilityChange()) { + mContentViewRenderView.postOnAnimation(mShowControlsRunnable); + } else { + showControlsNow(); + } + } + + private void showControlsNow() { if (mView != null) { - if (mIsTop) mView.setTranslationY(mControlsOffset); + if (mIsTop) { + mView.setTranslationY(mControlsOffset); + } mView.setVisibility(View.VISIBLE); } }