diff --git a/DEPS b/DEPS index fc29170..dce639c 100644 --- a/DEPS +++ b/DEPS
@@ -285,15 +285,15 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Skia # and whatever else without interference from each other. - 'skia_revision': '6270c3445a6500438a36b37ac354725940c98d12', + 'skia_revision': '5754b81988b80b27fb932d10eaef6e19e949d0be', # 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': 'cad9e2824b7ef03cb0eed85eb34aadc5c6d11e59', + 'v8_revision': 'c272b7f666719df9d92a448af368cc3ef4c30baf', # 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': '77d86c4a7ecc62356fde5ddcc83a25e2e51747bd', + 'angle_revision': '6bae26f6f0f3e61e2fe057d72615f1f6219e79bb', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. @@ -312,7 +312,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling Fuchsia sdk # and whatever else without interference from each other. - 'fuchsia_version': 'version:12.20230420.3.1', + 'fuchsia_version': 'version:12.20230421.1.1', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling google-toolbox-for-mac # and whatever else without interference from each other. @@ -372,7 +372,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': 'e25dbdff09521277a4334ae16347de1c884b63c5', + 'devtools_frontend_revision': '06bb4dadbfdaaca44c1c9f47712d14206fb0e77b', # 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. @@ -412,7 +412,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. - 'dawn_revision': 'f9719b7588cce8175147facb6ae90dc4e8af6371', + 'dawn_revision': 'c421a4b0ce2ba9ff9a238e2116fc457ea33febf0', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -456,7 +456,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. - 'libcxxabi_revision': 'a64df6cce230b9d4931b32eeae44ec1af2b63caa', + 'libcxxabi_revision': '64d1adcc575cb29aaac48eb7e8ed04be8504ebf6', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -782,7 +782,7 @@ 'src/clank': { 'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' + - '347919bee49bb0aed5f092bdc37707fad5695101', + 'df891523144cf2bdd37ba0a7bc077c2b9d8e7c6a', 'condition': 'checkout_android and checkout_src_internal', }, @@ -971,7 +971,7 @@ 'packages': [ { 'package': 'chromium/third_party/androidx', - 'version': 'ca8ma1XtK1oGxFaro9M1bU8jeyZR1YXcKhgdwc8a6gMC', + 'version': 'SwHpo0FCFofvEZpR__XSzcR2q_dGqrg1jn9rKk_39F0C', }, ], 'condition': 'checkout_android', @@ -1212,7 +1212,7 @@ Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'), 'src/third_party/devtools-frontend-internal': { - 'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + '6dca7f0c299f97ca962885ebf5fed0f68141fdb6', + 'url': Var('chrome_git') + '/devtools/devtools-internal.git' + '@' + '05992653342396430d9d745ce50c6af0911b5039', 'condition': 'checkout_src_internal', }, @@ -1687,7 +1687,7 @@ }, 'src/third_party/perfetto': - Var('android_git') + '/platform/external/perfetto.git' + '@' + '4f9f0278b3d294d668cae35171e5070be160536a', + Var('android_git') + '/platform/external/perfetto.git' + '@' + '26b08771635aa44efa337d344336008dbcb65cdf', 'src/third_party/perl': { 'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3', @@ -1872,7 +1872,7 @@ Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'bc8d8ae5463bffc5a106bcaaeb68b1ea12a130dc', 'src/third_party/webrtc': - Var('webrtc_git') + '/src.git' + '@' + '7c0525b98b26ef3187393b5e227f33f392a89744', + Var('webrtc_git') + '/src.git' + '@' + 'ff6cd5330367e067f0435ba462077c52f313c67d', # Wuffs' canonical repository is at github.com/google/wuffs, but we use # Skia's mirror of Wuffs, the same as in upstream Skia's DEPS file. @@ -1962,7 +1962,7 @@ Var('chromium_git') + '/v8/v8.git' + '@' + Var('v8_revision'), 'src-internal': { - 'url': Var('chrome_git') + '/chrome/src-internal.git@4d7135104ee8f5ca40f7bbca9dc408b4ff40e932', + 'url': Var('chrome_git') + '/chrome/src-internal.git@34ef198e41f39ff49879f4be54c4c719248e850e', 'condition': 'checkout_src_internal', }, @@ -2025,7 +2025,7 @@ 'packages': [ { 'package': 'chromeos_internal/apps/projector_app/app', - 'version': 'PFEX-b7wU-xlJMKma6qPYQPM4JmReHBVtbcq5O5zJjIC', + 'version': '1ai6JoXMH2OqOfVl73jeWAFmvP2AYkFVji4wl65yIT0C', }, ], 'condition': 'checkout_chromeos and checkout_src_internal',
diff --git a/PRESUBMIT.py b/PRESUBMIT.py index 6f3b72a..02b94315 100644 --- a/PRESUBMIT.py +++ b/PRESUBMIT.py
@@ -1174,7 +1174,12 @@ ' char and std::string instead?', ), True, - [_THIRD_PARTY_EXCEPT_BLINK], # Don't warn in third_party folders. + [ + # The demangler does not use this type but needs to know about it. + 'base/third_party/symbolize/demangle\.cc', + # Don't warn in third_party folders. + _THIRD_PARTY_EXCEPT_BLINK + ], ), BanRule( r'/(\b(co_await|co_return|co_yield)\b|#include <coroutine>)',
diff --git a/ash/rounded_display/rounded_display_frame_factory_unittest.cc b/ash/rounded_display/rounded_display_frame_factory_unittest.cc index 2b526f5f..bfa5a2c9 100644 --- a/ash/rounded_display/rounded_display_frame_factory_unittest.cc +++ b/ash/rounded_display/rounded_display_frame_factory_unittest.cc
@@ -101,17 +101,6 @@ } } - // Creates non overlay gutters and appends them to `gutters_`. - void AppendNonOverlayGutters(const gfx::Size& display_size_in_pixels, - const gfx::RoundedCornersF& panel_radii) { - auto non_overlay_gutters = gutter_factory_->CreateNonOverlayGutters( - display_size_in_pixels, panel_radii); - - for (auto& gutter : non_overlay_gutters) { - gutters_.push_back(std::move(gutter)); - } - } - protected: std::unique_ptr<RoundedDisplayGutterFactory> gutter_factory_; std::unique_ptr<RoundedDisplayFrameFactory> frame_factory_; @@ -212,9 +201,9 @@ TEST_F(RoundedDisplayFrameFactoryTest, CorrectRoundedDisplayInfo_GuttersWithOneCorner) { const auto panel_radii = gfx::RoundedCornersF(10, 0, 0, 0); - AppendNonOverlayGutters(kTestDisplaySize, panel_radii); + AppendHorizontalOverlayGutters(kTestDisplaySize, panel_radii); - // `gutter_factory_` will only create upper-left non-overlay gutter. + // `gutter_factory_` will only create upper overlay gutter. EXPECT_EQ(gutters_.size(), 1u); auto frame = frame_factory_->CreateCompositorFrame( @@ -235,7 +224,6 @@ TEST_F(RoundedDisplayFrameFactoryTest, OnlyCreateNewResourcesWhenNecessary) { AppendVerticalOverlayGutters(kTestDisplaySize, kTestPanelRadii); - AppendNonOverlayGutters(kTestDisplaySize, kTestPanelRadii); const auto& gutters = GetGutters(); @@ -248,7 +236,7 @@ /*is_overlay=*/false)); } - EXPECT_EQ(resource_manager_.available_resources_count(), 6u); + EXPECT_EQ(resource_manager_.available_resources_count(), 2u); frame_factory_->CreateCompositorFrame( viz::BeginFrameAck::CreateManualAckWithDamage(), *host_window_, @@ -256,13 +244,13 @@ // Should have reused all the resources. EXPECT_EQ(resource_manager_.available_resources_count(), 0u); - // Should have exported six resources as we have six gutters. - EXPECT_EQ(resource_manager_.exported_resources_count(), 6u); + // Should have exported two resources as we have two gutters. + EXPECT_EQ(resource_manager_.exported_resources_count(), 2u); resource_manager_.LostExportedResources(); // Adding more resources. - for (int index : {0, 0, 4, 5}) { + for (int index : {0, 0}) { const auto* gutter = gutters.at(index); resource_manager_.OfferResource( RoundedDisplayFrameFactory::CreateUiResource(gutter->bounds().size(), @@ -271,7 +259,7 @@ /*is_overlay=*/false)); } - EXPECT_EQ(resource_manager_.available_resources_count(), 4u); + EXPECT_EQ(resource_manager_.available_resources_count(), 2u); frame_factory_->CreateCompositorFrame( viz::BeginFrameAck::CreateManualAckWithDamage(), *host_window_, @@ -279,11 +267,11 @@ // We end up using the available resources and are left with the extra // resource that was available. We also must have created resources for - // gutters for which we did not have any available resources. + // gutter for which we did not have any available resources. EXPECT_EQ(resource_manager_.available_resources_count(), 1u); - // Should have exported six resources as we have six gutters. - EXPECT_EQ(resource_manager_.exported_resources_count(), 6u); + // Should have exported two resources as we have two gutters. + EXPECT_EQ(resource_manager_.exported_resources_count(), 2u); } } // namespace
diff --git a/ash/rounded_display/rounded_display_gutter_factory.cc b/ash/rounded_display/rounded_display_gutter_factory.cc index 2c84dec5..7d96a6d6 100644 --- a/ash/rounded_display/rounded_display_gutter_factory.cc +++ b/ash/rounded_display/rounded_display_gutter_factory.cc
@@ -126,28 +126,4 @@ return gutters; } -std::vector<std::unique_ptr<RoundedDisplayGutter>> -RoundedDisplayGutterFactory::CreateNonOverlayGutters( - const gfx::Size& panel_size, - const gfx::RoundedCornersF& panel_radii) { - std::vector<std::unique_ptr<RoundedDisplayGutter>> gutters; - - MaybeAppendGutter(gutters, CreateGutter(panel_size, panel_radii, - 0 | RoundedCornerPosition::kUpperLeft, - /*is_overlay_gutter=*/false)); - MaybeAppendGutter(gutters, - CreateGutter(panel_size, panel_radii, - 0 | RoundedCornerPosition::kUpperRight, - /*is_overlay_gutter=*/false)); - MaybeAppendGutter(gutters, CreateGutter(panel_size, panel_radii, - 0 | RoundedCornerPosition::kLowerLeft, - /*is_overlay_gutter=*/false)); - MaybeAppendGutter(gutters, - CreateGutter(panel_size, panel_radii, - 0 | RoundedCornerPosition::kLowerRight, - /*is_overlay_gutter=*/false)); - - return gutters; -} - } // namespace ash
diff --git a/ash/rounded_display/rounded_display_gutter_factory.h b/ash/rounded_display/rounded_display_gutter_factory.h index dd40e339..c59edea5 100644 --- a/ash/rounded_display/rounded_display_gutter_factory.h +++ b/ash/rounded_display/rounded_display_gutter_factory.h
@@ -35,12 +35,6 @@ const gfx::Size& panel_size, const gfx::RoundedCornersF& panel_radii, bool create_vertical_gutters); - - // Creates drawable non-overlay gutters. A non-overlay gutter is considered - // drawable if it has at least one RoundedDisplayCorners with non-zero radius. - std::vector<std::unique_ptr<RoundedDisplayGutter>> CreateNonOverlayGutters( - const gfx::Size& panel_size, - const gfx::RoundedCornersF& panel_radii); }; } // namespace ash
diff --git a/ash/rounded_display/rounded_display_gutter_factory_unittest.cc b/ash/rounded_display/rounded_display_gutter_factory_unittest.cc index 793698ef..83bc4d1 100644 --- a/ash/rounded_display/rounded_display_gutter_factory_unittest.cc +++ b/ash/rounded_display/rounded_display_gutter_factory_unittest.cc
@@ -90,31 +90,6 @@ } TEST_F(RoundedDisplayGutterFactoryTest, - NonOverlayGuttersAreNotCreatedIfNotNeeded) { - constexpr gfx::RoundedCornersF radii(10, 12, 0, 0); - - auto non_overlay_gutters = - factory_->CreateNonOverlayGutters(kTestDisplaySize, radii); - - EXPECT_EQ(non_overlay_gutters.size(), 2u); - - EXPECT_THAT(non_overlay_gutters, testing::Contains(GutterWithMatchingCorners( - RoundedCornerPosition::kUpperLeft))); - - EXPECT_THAT(non_overlay_gutters, testing::Contains(GutterWithMatchingCorners( - RoundedCornerPosition::kUpperRight))); - - // We do not create these non-overlay gutters as lower_right and - // lower_left corners have zero radius. - EXPECT_THAT(non_overlay_gutters, - testing::Not(testing::Contains(GutterWithMatchingCorners( - RoundedCornerPosition::kLowerLeft)))); - EXPECT_THAT(non_overlay_gutters, - testing::Not(testing::Contains(GutterWithMatchingCorners( - RoundedCornerPosition::kLowerRight)))); -} - -TEST_F(RoundedDisplayGutterFactoryTest, OverlayGuttersAreNotCreatedIfNotNeeded) { { const gfx::RoundedCornersF radii(10, 10, 0, 0);
diff --git a/ash/rounded_display/rounded_display_provider.cc b/ash/rounded_display/rounded_display_provider.cc index 3767ce6..4535b56 100644 --- a/ash/rounded_display/rounded_display_provider.cc +++ b/ash/rounded_display/rounded_display_provider.cc
@@ -184,10 +184,6 @@ for (const auto& gutter : overlay_gutters_) { gutters.push_back(gutter.get()); } - - for (const auto& gutter : non_overlay_gutters_) { - gutters.push_back(gutter.get()); - } } bool RoundedDisplayProvider::CreateGutters( @@ -203,9 +199,6 @@ overlay_gutters_ = gutter_factory_->CreateOverlayGutters( panel_size, panel_radii, create_vertical_gutters); - non_overlay_gutters_ = - gutter_factory_->CreateNonOverlayGutters(panel_size, panel_radii); - return true; }
diff --git a/ash/rounded_display/rounded_display_provider.h b/ash/rounded_display/rounded_display_provider.h index 15658599..30ec1740 100644 --- a/ash/rounded_display/rounded_display_provider.h +++ b/ash/rounded_display/rounded_display_provider.h
@@ -76,10 +76,11 @@ // Creates RoundedDisplayGutters if needed and returns true if we created // gutters else returns false. - // As an optimization, we create gutters only for non-zero corners of the - // display. We can have displays that don't have rounded bottom edges, so we - // skip creation of lower-left and lower-right NonOverlayGutters and for the - // horizontal orientation, we skip the creation of the lower OverlayGutter. + // To minimize the use of overlay planes, we only create a gutter if it has + // at least a single non-zero corner mask drawn into it. + // For example, for a display that doesn't have rounded bottom edges, and + // based on the `strategy_`, we need to create upper and lower OverlayGutters, + // we will skip the creation of the lower OverlayGutter. bool CreateGutters(const display::Display& display, const gfx::RoundedCornersF& panel_radii); @@ -100,12 +101,9 @@ // The specified strategy to determine direction of overlay gutters. Strategy strategy_ = Strategy::kScanout; - // Stores the overlay gutters based on the `overlay_gutters_orientation_`. + // Stores the overlay gutters that are created based on the `strategy_`. std::vector<std::unique_ptr<RoundedDisplayGutter>> overlay_gutters_; - // Stores the non-overlay gutters. - std::vector<std::unique_ptr<RoundedDisplayGutter>> non_overlay_gutters_; - // OverlayRoundedDisplayGutter creation is delegated to this factory. std::unique_ptr<RoundedDisplayGutterFactory> gutter_factory_;
diff --git a/ash/rounded_display/rounded_display_provider_unittest.cc b/ash/rounded_display/rounded_display_provider_unittest.cc index efab93a..ebafbe3 100644 --- a/ash/rounded_display/rounded_display_provider_unittest.cc +++ b/ash/rounded_display/rounded_display_provider_unittest.cc
@@ -94,9 +94,9 @@ RoundedDisplayProviderTestApi test_api(provider_.get()); const gfx::RoundedCornersF radii = CreateHorizontallyUniformRadii(10, 12); - // We expect 4 non-overlays and 2 overlay gutters to be created. + // We expect 2 overlay gutters to be created. provider_->Init(radii, kDefaultTestStrategy); - EXPECT_EQ(test_api.GetGutters().size(), 6u); + EXPECT_EQ(test_api.GetGutters().size(), 2u); } TEST_F(RoundedDisplayProviderTest, @@ -108,7 +108,7 @@ const auto& gutters = test_api.GetGutters(); - EXPECT_EQ(gutters.size(), 6u); + EXPECT_EQ(gutters.size(), 2u); // Check that we have two overlay gutters that in the scanout direction. EXPECT_THAT(gutters, testing::Contains(GutterWithMatchingCorners( @@ -128,7 +128,7 @@ const auto& gutters = test_api.GetGutters(); - EXPECT_EQ(gutters.size(), 6u); + EXPECT_EQ(gutters.size(), 2u); // Check that we have two overlay gutters that in the scanout direction. EXPECT_THAT(gutters, testing::Contains(GutterWithMatchingCorners(
diff --git a/ash/webui/common/chrome_os_webui_config.h b/ash/webui/common/chrome_os_webui_config.h index f84b8e97..4eb7ffc5 100644 --- a/ash/webui/common/chrome_os_webui_config.h +++ b/ash/webui/common/chrome_os_webui_config.h
@@ -32,7 +32,20 @@ host, [](content::WebUI* web_ui, const GURL& url) -> std::unique_ptr<content::WebUIController> { - return std::make_unique<T>(web_ui); + // We need to determine the correct WebUIController constructor to + // use at compile time, depending on whether it requires only + // WebUI* or both WebUI* and GURL. We currently don't support + // WebUIControllers that have two constructors where one has a + // single WebUI* arg and the other has both WebUI* and GURL + // params. + static_assert( + !(std::is_constructible_v<T, content::WebUI*> && + std::is_constructible_v<T, content::WebUI*, GURL>)); + if constexpr (std::is_constructible_v<T, content::WebUI*, GURL>) { + return std::make_unique<T>(web_ui, url); + } else { + return std::make_unique<T>(web_ui); + } }) {} // Same as above, but takes in an extra `create_controller_func` argument that
diff --git a/ash/webui/guest_os_installer/BUILD.gn b/ash/webui/guest_os_installer/BUILD.gn index a9ac8a6..19dac1b8 100644 --- a/ash/webui/guest_os_installer/BUILD.gn +++ b/ash/webui/guest_os_installer/BUILD.gn
@@ -15,6 +15,7 @@ ] deps = [ + "//ash/webui/common:chrome_os_webui_config", "//ash/webui/guest_os_installer/mojom", "//ash/webui/guest_os_installer/resources:resources", "//content/public/browser",
diff --git a/ash/webui/guest_os_installer/guest_os_installer_ui.cc b/ash/webui/guest_os_installer/guest_os_installer_ui.cc index 07d9ac7..c7cefca 100644 --- a/ash/webui/guest_os_installer/guest_os_installer_ui.cc +++ b/ash/webui/guest_os_installer/guest_os_installer_ui.cc
@@ -12,11 +12,8 @@ namespace ash { GuestOSInstallerUI::GuestOSInstallerUI(content::WebUI* web_ui, - const GURL& url, DelegateFactory delegate_factory) - : ui::MojoWebDialogUI(web_ui), - url_(url), - delegate_factory_(delegate_factory) { + : ui::MojoWebDialogUI(web_ui), delegate_factory_(delegate_factory) { auto* source = content::WebUIDataSource::CreateAndAdd( web_ui->GetWebContents()->GetBrowserContext(), ash::kChromeUIGuestOSInstallerHost); @@ -45,7 +42,7 @@ // this we delegate actually picking a backend to this delegate factory // callback, which lives in //chrome and is passed to us by our constructor // (which also lives in //chrome). - handler_ = delegate_factory_.Run(this, url_, std::move(pending_page), + handler_ = delegate_factory_.Run(this, std::move(pending_page), std::move(pending_page_handler)); }
diff --git a/ash/webui/guest_os_installer/guest_os_installer_ui.h b/ash/webui/guest_os_installer/guest_os_installer_ui.h index bd1cc9a..a5e96f0 100644 --- a/ash/webui/guest_os_installer/guest_os_installer_ui.h +++ b/ash/webui/guest_os_installer/guest_os_installer_ui.h
@@ -7,8 +7,11 @@ #include <memory> +#include "ash/webui/common/chrome_os_webui_config.h" #include "ash/webui/guest_os_installer/mojom/guest_os_installer.mojom.h" +#include "ash/webui/guest_os_installer/url_constants.h" #include "base/functional/bind.h" +#include "content/public/common/url_constants.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/receiver.h" @@ -16,6 +19,19 @@ namespace ash { +class GuestOSInstallerUI; + +// The WebUIConfig for chrome://guest-os-installer +class GuestOSInstallerUIConfig + : public ChromeOSWebUIConfig<GuestOSInstallerUI> { + public: + explicit GuestOSInstallerUIConfig( + CreateWebUIControllerFunc create_controller_func) + : ChromeOSWebUIConfig(content::kChromeUIScheme, + ash::kChromeUIGuestOSInstallerHost, + create_controller_func) {} +}; + // The WebUI for chrome://guest-os-installer class GuestOSInstallerUI : public ui::MojoWebDialogUI, @@ -24,13 +40,10 @@ using DelegateFactory = base::RepeatingCallback< std::unique_ptr<ash::guest_os_installer::mojom::PageHandler>( GuestOSInstallerUI*, - const GURL&, mojo::PendingRemote<ash::guest_os_installer::mojom::Page>, mojo::PendingReceiver<ash::guest_os_installer::mojom::PageHandler>)>; - GuestOSInstallerUI(content::WebUI* web_ui, - const GURL& url, - DelegateFactory delegate_factory); + GuestOSInstallerUI(content::WebUI* web_ui, DelegateFactory delegate_factory); GuestOSInstallerUI(const GuestOSInstallerUI&) = delete; GuestOSInstallerUI& operator=(const GuestOSInstallerUI&) = delete; @@ -53,8 +66,6 @@ std::unique_ptr<ash::guest_os_installer::mojom::PageHandler> handler_; - const GURL url_; - const DelegateFactory delegate_factory_; WEB_UI_CONTROLLER_TYPE_DECL();
diff --git a/base/allocator/dispatcher/tls.cc b/base/allocator/dispatcher/tls.cc index c33ac1f..ae9fd7d7 100644 --- a/base/allocator/dispatcher/tls.cc +++ b/base/allocator/dispatcher/tls.cc
@@ -12,11 +12,26 @@ #include <sys/mman.h> +#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) +#include <sys/prctl.h> +#endif + namespace base::allocator::dispatcher::internal { void* MMapAllocator::AllocateMemory(size_t size_in_bytes) { void* const mmap_res = mmap(nullptr, size_in_bytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); +#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) +#if defined(PR_SET_VMA) && defined(PR_SET_VMA_ANON_NAME) + if (mmap_res != MAP_FAILED) { + // Allow the anonymous memory region allocated by mmap(MAP_ANONYMOUS) to + // be identified in /proc/$PID/smaps. This helps improve visibility into + // Chromium's memory usage on Android. + prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, mmap_res, size_in_bytes, + "tls-mmap-allocator"); + } +#endif +#endif return (mmap_res != MAP_FAILED) ? mmap_res : nullptr; } @@ -81,4 +96,4 @@ } // namespace base::allocator::dispatcher::internal -#endif // USE_LOCAL_TLS_EMULATION() \ No newline at end of file +#endif // USE_LOCAL_TLS_EMULATION()
diff --git a/base/third_party/symbolize/README.chromium b/base/third_party/symbolize/README.chromium index c7700fec..d797349 100644 --- a/base/third_party/symbolize/README.chromium +++ b/base/third_party/symbolize/README.chromium
@@ -32,4 +32,6 @@ platforms. - 008-include-cstdlib.patch: include <cstdlib> for abort() rather than relying on transitive includes. -- 009-clang-format.patch: format the source files using Chrome formatting rules. +- 009-clone-absl-demangle.patch: Clone the demangling implementation from + abseil-cpp, which is itself a fork of https://github.com/google/glog/. +- 010-clang-format.patch: format the source files using Chrome formatting rules.
diff --git a/base/third_party/symbolize/demangle.cc b/base/third_party/symbolize/demangle.cc index da4cbf6a..8db75f0 100644 --- a/base/third_party/symbolize/demangle.cc +++ b/base/third_party/symbolize/demangle.cc
@@ -34,13 +34,14 @@ // // Note that we only have partial C++0x support yet. -#include <cstdio> // for NULL - #include "demangle.h" -#include "utilities.h" #if defined(GLOG_OS_WINDOWS) #include <dbghelp.h> +#else +#include <cstdint> +#include <cstdio> +#include <limits> #endif _START_GOOGLE_NAMESPACE_ @@ -49,117 +50,199 @@ typedef struct { const char *abbrev; const char *real_name; + // Number of arguments in <expression> context, or 0 if disallowed. + int arity; } AbbrevPair; // List of operators from Itanium C++ ABI. static const AbbrevPair kOperatorList[] = { - { "nw", "new" }, - { "na", "new[]" }, - { "dl", "delete" }, - { "da", "delete[]" }, - { "ps", "+" }, - { "ng", "-" }, - { "ad", "&" }, - { "de", "*" }, - { "co", "~" }, - { "pl", "+" }, - { "mi", "-" }, - { "ml", "*" }, - { "dv", "/" }, - { "rm", "%" }, - { "an", "&" }, - { "or", "|" }, - { "eo", "^" }, - { "aS", "=" }, - { "pL", "+=" }, - { "mI", "-=" }, - { "mL", "*=" }, - { "dV", "/=" }, - { "rM", "%=" }, - { "aN", "&=" }, - { "oR", "|=" }, - { "eO", "^=" }, - { "ls", "<<" }, - { "rs", ">>" }, - { "lS", "<<=" }, - { "rS", ">>=" }, - { "eq", "==" }, - { "ne", "!=" }, - { "lt", "<" }, - { "gt", ">" }, - { "le", "<=" }, - { "ge", ">=" }, - { "nt", "!" }, - { "aa", "&&" }, - { "oo", "||" }, - { "pp", "++" }, - { "mm", "--" }, - { "cm", "," }, - { "pm", "->*" }, - { "pt", "->" }, - { "cl", "()" }, - { "ix", "[]" }, - { "qu", "?" }, - { "st", "sizeof" }, - { "sz", "sizeof" }, - { NULL, NULL }, + // New has special syntax (not currently supported). + {"nw", "new", 0}, + {"na", "new[]", 0}, + + // Works except that the 'gs' prefix is not supported. + {"dl", "delete", 1}, + {"da", "delete[]", 1}, + + {"ps", "+", 1}, // "positive" + {"ng", "-", 1}, // "negative" + {"ad", "&", 1}, // "address-of" + {"de", "*", 1}, // "dereference" + {"co", "~", 1}, + + {"pl", "+", 2}, + {"mi", "-", 2}, + {"ml", "*", 2}, + {"dv", "/", 2}, + {"rm", "%", 2}, + {"an", "&", 2}, + {"or", "|", 2}, + {"eo", "^", 2}, + {"aS", "=", 2}, + {"pL", "+=", 2}, + {"mI", "-=", 2}, + {"mL", "*=", 2}, + {"dV", "/=", 2}, + {"rM", "%=", 2}, + {"aN", "&=", 2}, + {"oR", "|=", 2}, + {"eO", "^=", 2}, + {"ls", "<<", 2}, + {"rs", ">>", 2}, + {"lS", "<<=", 2}, + {"rS", ">>=", 2}, + {"eq", "==", 2}, + {"ne", "!=", 2}, + {"lt", "<", 2}, + {"gt", ">", 2}, + {"le", "<=", 2}, + {"ge", ">=", 2}, + {"nt", "!", 1}, + {"aa", "&&", 2}, + {"oo", "||", 2}, + {"pp", "++", 1}, + {"mm", "--", 1}, + {"cm", ",", 2}, + {"pm", "->*", 2}, + {"pt", "->", 0}, // Special syntax + {"cl", "()", 0}, // Special syntax + {"ix", "[]", 2}, + {"qu", "?", 3}, + {"st", "sizeof", 0}, // Special syntax + {"sz", "sizeof", 1}, // Not a real operator name, but used in expressions. + {nullptr, nullptr, 0}, }; // List of builtin types from Itanium C++ ABI. +// +// Invariant: only one- or two-character type abbreviations here. static const AbbrevPair kBuiltinTypeList[] = { - { "v", "void" }, - { "w", "wchar_t" }, - { "b", "bool" }, - { "c", "char" }, - { "a", "signed char" }, - { "h", "unsigned char" }, - { "s", "short" }, - { "t", "unsigned short" }, - { "i", "int" }, - { "j", "unsigned int" }, - { "l", "long" }, - { "m", "unsigned long" }, - { "x", "long long" }, - { "y", "unsigned long long" }, - { "n", "__int128" }, - { "o", "unsigned __int128" }, - { "f", "float" }, - { "d", "double" }, - { "e", "long double" }, - { "g", "__float128" }, - { "z", "ellipsis" }, - { NULL, NULL } + {"v", "void", 0}, + {"w", "wchar_t", 0}, + {"b", "bool", 0}, + {"c", "char", 0}, + {"a", "signed char", 0}, + {"h", "unsigned char", 0}, + {"s", "short", 0}, + {"t", "unsigned short", 0}, + {"i", "int", 0}, + {"j", "unsigned int", 0}, + {"l", "long", 0}, + {"m", "unsigned long", 0}, + {"x", "long long", 0}, + {"y", "unsigned long long", 0}, + {"n", "__int128", 0}, + {"o", "unsigned __int128", 0}, + {"f", "float", 0}, + {"d", "double", 0}, + {"e", "long double", 0}, + {"g", "__float128", 0}, + {"z", "ellipsis", 0}, + + {"De", "decimal128", 0}, // IEEE 754r decimal floating point (128 bits) + {"Dd", "decimal64", 0}, // IEEE 754r decimal floating point (64 bits) + {"Dc", "decltype(auto)", 0}, + {"Da", "auto", 0}, + {"Dn", "std::nullptr_t", 0}, // i.e., decltype(nullptr) + {"Df", "decimal32", 0}, // IEEE 754r decimal floating point (32 bits) + {"Di", "char32_t", 0}, + {"Du", "char8_t", 0}, + {"Ds", "char16_t", 0}, + {"Dh", "float16", 0}, // IEEE 754r half-precision float (16 bits) + {nullptr, nullptr, 0}, }; // List of substitutions Itanium C++ ABI. static const AbbrevPair kSubstitutionList[] = { - { "St", "" }, - { "Sa", "allocator" }, - { "Sb", "basic_string" }, - // std::basic_string<char, std::char_traits<char>,std::allocator<char> > - { "Ss", "string"}, - // std::basic_istream<char, std::char_traits<char> > - { "Si", "istream" }, - // std::basic_ostream<char, std::char_traits<char> > - { "So", "ostream" }, - // std::basic_iostream<char, std::char_traits<char> > - { "Sd", "iostream" }, - { NULL, NULL } + {"St", "", 0}, + {"Sa", "allocator", 0}, + {"Sb", "basic_string", 0}, + // std::basic_string<char, std::char_traits<char>,std::allocator<char> > + {"Ss", "string", 0}, + // std::basic_istream<char, std::char_traits<char> > + {"Si", "istream", 0}, + // std::basic_ostream<char, std::char_traits<char> > + {"So", "ostream", 0}, + // std::basic_iostream<char, std::char_traits<char> > + {"Sd", "iostream", 0}, + {nullptr, nullptr, 0}, }; -// State needed for demangling. +// State needed for demangling. This struct is copied in almost every stack +// frame, so every byte counts. typedef struct { - const char* mangled_cur; // Cursor of mangled name. - char* out_cur; // Cursor of output string. - const char* out_begin; // Beginning of output string. - const char* out_end; // End of output string. - const char* prev_name; // For constructors/destructors. - ssize_t prev_name_length; // For constructors/destructors. - short nest_level; // For nested names. - bool append; // Append flag. - bool overflowed; // True if output gets overflowed. + int mangled_idx; // Cursor of mangled name. + int out_cur_idx; // Cursor of output string. + int prev_name_idx; // For constructors/destructors. + unsigned int prev_name_length : 16; // For constructors/destructors. + signed int nest_level : 15; // For nested names. + unsigned int append : 1; // Append flag. + // Note: for some reason MSVC can't pack "bool append : 1" into the same int + // with the above two fields, so we use an int instead. Amusingly it can pack + // "signed bool" as expected, but relying on that to continue to be a legal + // type seems ill-advised (as it's illegal in at least clang). +} ParseState; + +static_assert(sizeof(ParseState) == 4 * sizeof(int), + "unexpected size of ParseState"); + +// One-off state for demangling that's not subject to backtracking -- either +// constant data, data that's intentionally immune to backtracking (steps), or +// data that would never be changed by backtracking anyway (recursion_depth). +// +// Only one copy of this exists for each call to Demangle, so the size of this +// struct is nearly inconsequential. +typedef struct { + const char* mangled_begin; // Beginning of input string. + char* out; // Beginning of output string. + int out_end_idx; // One past last allowed output character. + int recursion_depth; // For stack exhaustion prevention. + int steps; // Cap how much work we'll do, regardless of depth. + ParseState parse_state; // Backtrackable state copied for most frames. } State; +namespace { +// Prevent deep recursion / stack exhaustion. +// Also prevent unbounded handling of complex inputs. +class ComplexityGuard { + public: + explicit ComplexityGuard(State* state) : state_(state) { + ++state->recursion_depth; + ++state->steps; + } + ~ComplexityGuard() { --state_->recursion_depth; } + + // 256 levels of recursion seems like a reasonable upper limit on depth. + // 128 is not enough to demagle synthetic tests from demangle_unittest.txt: + // "_ZaaZZZZ..." and "_ZaaZcvZcvZ..." + static constexpr int kRecursionDepthLimit = 256; + + // We're trying to pick a charitable upper-limit on how many parse steps are + // necessary to handle something that a human could actually make use of. + // This is mostly in place as a bound on how much work we'll do if we are + // asked to demangle an mangled name from an untrusted source, so it should be + // much larger than the largest expected symbol, but much smaller than the + // amount of work we can do in, e.g., a second. + // + // Some real-world symbols from an arbitrary binary started failing between + // 2^12 and 2^13, so we multiply the latter by an extra factor of 16 to set + // the limit. + // + // Spending one second on 2^17 parse steps would require each step to take + // 7.6us, or ~30000 clock cycles, so it's safe to say this can be done in + // under a second. + static constexpr int kParseStepsLimit = 1 << 17; + + bool IsTooComplex() const { + return state_->recursion_depth > kRecursionDepthLimit || + state_->steps > kParseStepsLimit; + } + + private: + State* state_; +}; +} // namespace + // We don't use strlen() in libc since it's not guaranteed to be async // signal safe. static size_t StrLen(const char *str) { @@ -172,8 +255,8 @@ } // Returns true if "str" has at least "n" characters remaining. -static bool AtLeastNumCharsRemaining(const char* str, ssize_t n) { - for (ssize_t i = 0; i < n; ++i) { +static bool AtLeastNumCharsRemaining(const char* str, size_t n) { + for (size_t i = 0; i < n; ++i) { if (str[i] == '\0') { return false; } @@ -184,8 +267,7 @@ // Returns true if "str" has "prefix" as a prefix. static bool StrPrefix(const char *str, const char *prefix) { size_t i = 0; - while (str[i] != '\0' && prefix[i] != '\0' && - str[i] == prefix[i]) { + while (str[i] != '\0' && prefix[i] != '\0' && str[i] == prefix[i]) { ++i; } return prefix[i] == '\0'; // Consumed everything in "prefix". @@ -195,23 +277,34 @@ const char* mangled, char* out, size_t out_size) { - state->mangled_cur = mangled; - state->out_cur = out; - state->out_begin = out; - state->out_end = out + out_size; - state->prev_name = NULL; - state->prev_name_length = -1; - state->nest_level = -1; - state->append = true; - state->overflowed = false; + state->mangled_begin = mangled; + state->out = out; + state->out_end_idx = static_cast<int>(out_size); + state->recursion_depth = 0; + state->steps = 0; + + state->parse_state.mangled_idx = 0; + state->parse_state.out_cur_idx = 0; + state->parse_state.prev_name_idx = 0; + state->parse_state.prev_name_length = 0; + state->parse_state.nest_level = -1; + state->parse_state.append = true; } -// Returns true and advances "mangled_cur" if we find "one_char_token" -// at "mangled_cur" position. It is assumed that "one_char_token" does +static inline const char* RemainingInput(State* state) { + return &state->mangled_begin[state->parse_state.mangled_idx]; +} + +// Returns true and advances "mangled_idx" if we find "one_char_token" +// at "mangled_idx" position. It is assumed that "one_char_token" does // not contain '\0'. static bool ParseOneCharToken(State *state, const char one_char_token) { - if (state->mangled_cur[0] == one_char_token) { - ++state->mangled_cur; + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } + if (RemainingInput(state)[0] == one_char_token) { + ++state->parse_state.mangled_idx; return true; } return false; @@ -221,9 +314,13 @@ // at "mangled_cur" position. It is assumed that "two_char_token" does // not contain '\0'. static bool ParseTwoCharToken(State *state, const char *two_char_token) { - if (state->mangled_cur[0] == two_char_token[0] && - state->mangled_cur[1] == two_char_token[1]) { - state->mangled_cur += 2; + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } + if (RemainingInput(state)[0] == two_char_token[0] && + RemainingInput(state)[1] == two_char_token[1]) { + state->parse_state.mangled_idx += 2; return true; } return false; @@ -232,18 +329,36 @@ // Returns true and advances "mangled_cur" if we find any character in // "char_class" at "mangled_cur" position. static bool ParseCharClass(State *state, const char *char_class) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } + if (RemainingInput(state)[0] == '\0') { + return false; + } const char *p = char_class; for (; *p != '\0'; ++p) { - if (state->mangled_cur[0] == *p) { - ++state->mangled_cur; + if (RemainingInput(state)[0] == *p) { + ++state->parse_state.mangled_idx; return true; } } return false; } +static bool ParseDigit(State* state, int* digit) { + char c = RemainingInput(state)[0]; + if (ParseCharClass(state, "0123456789")) { + if (digit != nullptr) { + *digit = c - '0'; + } + return true; + } + return false; +} + // This function is used for handling an optional non-terminal. -static bool Optional(bool) { +static bool Optional(bool /*status*/) { return true; } @@ -268,21 +383,23 @@ return true; } -// Append "str" at "out_cur". If there is an overflow, "overflowed" -// is set to true for later use. The output string is ensured to +// Append "str" at "out_cur_idx". If there is an overflow, out_cur_idx is +// set to out_end_idx+1. The output string is ensured to // always terminate with '\0' as long as there is no overflow. -static void Append(State* state, const char* const str, ssize_t length) { - for (ssize_t i = 0; i < length; ++i) { - if (state->out_cur + 1 < state->out_end) { // +1 for '\0' - *state->out_cur = str[i]; - ++state->out_cur; +static void Append(State* state, const char* const str, const size_t length) { + for (size_t i = 0; i < length; ++i) { + if (state->parse_state.out_cur_idx + 1 < + state->out_end_idx) { // +1 for '\0' + state->out[state->parse_state.out_cur_idx++] = str[i]; } else { - state->overflowed = true; + // signal overflow + state->parse_state.out_cur_idx = state->out_end_idx + 1; break; } } - if (!state->overflowed) { - *state->out_cur = '\0'; // Terminate it with '\0' + if (state->parse_state.out_cur_idx < state->out_end_idx) { + state->out[state->parse_state.out_cur_idx] = + '\0'; // Terminate it with '\0' } } @@ -300,140 +417,176 @@ } // Returns true if "str" is a function clone suffix. These suffixes are used -// by GCC 4.5.x and later versions to indicate functions which have been -// cloned during optimization. We treat any sequence (.<alpha>+.<digit>+)+ as -// a function clone suffix. +// by GCC 4.5.x and later versions (and our locally-modified version of GCC +// 4.4.x) to indicate functions which have been cloned during optimization. +// We treat any sequence (.<alpha>+.<digit>+)+ as a function clone suffix. +// Additionally, '_' is allowed along with the alphanumeric sequence. static bool IsFunctionCloneSuffix(const char *str) { size_t i = 0; while (str[i] != '\0') { - // Consume a single .<alpha>+.<digit>+ sequence. - if (str[i] != '.' || !IsAlpha(str[i + 1])) { + bool parsed = false; + // Consume a single [.<alpha> | _]*[.<digit>]* sequence. + if (str[i] == '.' && (IsAlpha(str[i + 1]) || str[i + 1] == '_')) { + parsed = true; + i += 2; + while (IsAlpha(str[i]) || str[i] == '_') { + ++i; + } + } + if (str[i] == '.' && IsDigit(str[i + 1])) { + parsed = true; + i += 2; + while (IsDigit(str[i])) { + ++i; + } + } + if (!parsed) { return false; } - i += 2; - while (IsAlpha(str[i])) { - ++i; - } - if (str[i] != '.' || !IsDigit(str[i + 1])) { - return false; - } - i += 2; - while (IsDigit(str[i])) { - ++i; - } } return true; // Consumed everything in "str". } +static bool EndsWith(State* state, const char chr) { + return state->parse_state.out_cur_idx > 0 && + state->parse_state.out_cur_idx < state->out_end_idx && + chr == state->out[state->parse_state.out_cur_idx - 1]; +} + // Append "str" with some tweaks, iff "append" state is true. -// Returns true so that it can be placed in "if" conditions. static void MaybeAppendWithLength(State* state, const char* const str, - ssize_t length) { - if (state->append && length > 0) { + const size_t length) { + if (state->parse_state.append && length > 0) { // Append a space if the output buffer ends with '<' and "str" // starts with '<' to avoid <<<. - if (str[0] == '<' && state->out_begin < state->out_cur && - state->out_cur[-1] == '<') { + if (str[0] == '<' && EndsWith(state, '<')) { Append(state, " ", 1); } - // Remember the last identifier name for ctors/dtors. - if (IsAlpha(str[0]) || str[0] == '_') { - state->prev_name = state->out_cur; - state->prev_name_length = length; + // Remember the last identifier name for ctors/dtors, + // but only if we haven't yet overflown the buffer. + if (state->parse_state.out_cur_idx < state->out_end_idx && + (IsAlpha(str[0]) || str[0] == '_')) { + state->parse_state.prev_name_idx = state->parse_state.out_cur_idx; + state->parse_state.prev_name_length = static_cast<unsigned int>(length); } Append(state, str, length); } } -// A convenient wrapper arount MaybeAppendWithLength(). -static bool MaybeAppend(State *state, const char * const str) { - if (state->append) { +// Appends a positive decimal number to the output if appending is enabled. +static bool MaybeAppendDecimal(State* state, int val) { + // Max {32-64}-bit unsigned int is 20 digits. + constexpr size_t kMaxLength = 20; + char buf[kMaxLength]; + + // We can't use itoa or sprintf as neither is specified to be + // async-signal-safe. + if (state->parse_state.append) { + // We can't have a one-before-the-beginning pointer, so instead start with + // one-past-the-end and manipulate one character before the pointer. + char* p = &buf[kMaxLength]; + do { // val=0 is the only input that should write a leading zero digit. + *--p = static_cast<char>((val % 10) + '0'); + val /= 10; + } while (p > buf && val != 0); + + // 'p' landed on the last character we set. How convenient. + Append(state, p, kMaxLength - static_cast<size_t>(p - buf)); + } + + return true; +} + +// A convenient wrapper around MaybeAppendWithLength(). +// Returns true so that it can be placed in "if" conditions. +static bool MaybeAppend(State* state, const char* const str) { + if (state->parse_state.append) { size_t length = StrLen(str); - MaybeAppendWithLength(state, str, static_cast<ssize_t>(length)); + MaybeAppendWithLength(state, str, length); } return true; } // This function is used for handling nested names. static bool EnterNestedName(State *state) { - state->nest_level = 0; + state->parse_state.nest_level = 0; return true; } // This function is used for handling nested names. -static bool LeaveNestedName(State *state, short prev_value) { - state->nest_level = prev_value; +static bool LeaveNestedName(State* state, int16_t prev_value) { + state->parse_state.nest_level = prev_value; return true; } // Disable the append mode not to print function parameters, etc. static bool DisableAppend(State *state) { - state->append = false; + state->parse_state.append = false; return true; } // Restore the append mode to the previous state. static bool RestoreAppend(State *state, bool prev_value) { - state->append = prev_value; + state->parse_state.append = prev_value; return true; } // Increase the nest level for nested names. static void MaybeIncreaseNestLevel(State *state) { - if (state->nest_level > -1) { - ++state->nest_level; + if (state->parse_state.nest_level > -1) { + ++state->parse_state.nest_level; } } // Appends :: for nested names if necessary. static void MaybeAppendSeparator(State *state) { - if (state->nest_level >= 1) { + if (state->parse_state.nest_level >= 1) { MaybeAppend(state, "::"); } } // Cancel the last separator if necessary. static void MaybeCancelLastSeparator(State *state) { - if (state->nest_level >= 1 && state->append && - state->out_begin <= state->out_cur - 2) { - state->out_cur -= 2; - *state->out_cur = '\0'; + if (state->parse_state.nest_level >= 1 && state->parse_state.append && + state->parse_state.out_cur_idx >= 2) { + state->parse_state.out_cur_idx -= 2; + state->out[state->parse_state.out_cur_idx] = '\0'; } } // Returns true if the identifier of the given length pointed to by // "mangled_cur" is anonymous namespace. -static bool IdentifierIsAnonymousNamespace(State* state, ssize_t length) { +static bool IdentifierIsAnonymousNamespace(State* state, size_t length) { + // Returns true if "anon_prefix" is a proper prefix of "mangled_cur". static const char anon_prefix[] = "_GLOBAL__N_"; - return (length > static_cast<ssize_t>(sizeof(anon_prefix)) - - 1 && // Should be longer. - StrPrefix(state->mangled_cur, anon_prefix)); + return (length > (sizeof(anon_prefix) - 1) && + StrPrefix(RemainingInput(state), anon_prefix)); } // Forward declarations of our parsing functions. static bool ParseMangledName(State *state); static bool ParseEncoding(State *state); static bool ParseName(State *state); -static bool ParseUnscopedName(State *state); -static bool ParseUnscopedTemplateName(State *state); +static bool ParseUnscopedName(State* state); static bool ParseNestedName(State *state); static bool ParsePrefix(State *state); static bool ParseUnqualifiedName(State *state); static bool ParseSourceName(State *state); static bool ParseLocalSourceName(State *state); +static bool ParseUnnamedTypeName(State* state); static bool ParseNumber(State *state, int *number_out); static bool ParseFloatNumber(State *state); static bool ParseSeqId(State *state); -static bool ParseIdentifier(State* state, ssize_t length); -static bool ParseAbiTags(State *state); -static bool ParseAbiTag(State *state); -static bool ParseOperatorName(State *state); +static bool ParseIdentifier(State* state, size_t length); +static bool ParseOperatorName(State* state, int* arity); static bool ParseSpecialName(State *state); static bool ParseCallOffset(State *state); static bool ParseNVOffset(State *state); static bool ParseVOffset(State *state); +static bool ParseAbiTags(State* state); static bool ParseCtorDtorName(State *state); +static bool ParseDecltype(State* state); static bool ParseType(State *state); static bool ParseCVQualifiers(State *state); static bool ParseBuiltinType(State *state); @@ -446,11 +599,15 @@ static bool ParseTemplateTemplateParam(State *state); static bool ParseTemplateArgs(State *state); static bool ParseTemplateArg(State *state); +static bool ParseBaseUnresolvedName(State* state); +static bool ParseUnresolvedName(State* state); static bool ParseExpression(State *state); static bool ParseExprPrimary(State *state); +static bool ParseExprCastValue(State* state); static bool ParseLocalName(State *state); +static bool ParseLocalNameSuffix(State* state); static bool ParseDiscriminator(State *state); -static bool ParseSubstitution(State *state); +static bool ParseSubstitution(State* state, bool accept_std); // Implementation note: the following code is a straightforward // translation of the Itanium C++ ABI defined in BNF with a couple of @@ -462,11 +619,12 @@ // - Reorder patterns to give greedier functions precedence // We'll mark "Less greedy than" for these cases in the code // -// Each parsing function changes the state and returns true on -// success. Otherwise, don't change the state and returns false. To -// ensure that the state isn't changed in the latter case, we save the -// original state before we call more than one parsing functions -// consecutively with &&, and restore the state if unsuccessful. See +// Each parsing function changes the parse state and returns true on +// success, or returns false and doesn't change the parse state (note: +// the parse-steps counter increases regardless of success or failure). +// To ensure that the parse state isn't changed in the latter case, we +// save the original state before we call multiple parsing functions +// consecutively with &&, and restore it if unsuccessful. See // ParseEncoding() as an example of this convention. We follow the // convention throughout the code. // @@ -480,10 +638,14 @@ // // Reference: // - Itanium C++ ABI -// <http://www.codesourcery.com/cxx-abi/abi.html#mangling> +// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling> // <mangled-name> ::= _Z <encoding> static bool ParseMangledName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } return ParseTwoCharToken(state, "_Z") && ParseEncoding(state); } @@ -491,13 +653,20 @@ // ::= <(data) name> // ::= <special-name> static bool ParseEncoding(State *state) { - State copy = *state; - if (ParseName(state) && ParseBareFunctionType(state)) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } + // Implementing the first two productions together as <name> + // [<bare-function-type>] avoids exponential blowup of backtracking. + // + // Since Optional(...) can't fail, there's no need to copy the state for + // backtracking. + if (ParseName(state) && Optional(ParseBareFunctionType(state))) { return true; } - *state = copy; - if (ParseName(state) || ParseSpecialName(state)) { + if (ParseSpecialName(state)) { return true; } return false; @@ -508,60 +677,79 @@ // ::= <unscoped-name> // ::= <local-name> static bool ParseName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } if (ParseNestedName(state) || ParseLocalName(state)) { return true; } - State copy = *state; - if (ParseUnscopedTemplateName(state) && + // We reorganize the productions to avoid re-parsing unscoped names. + // - Inline <unscoped-template-name> productions: + // <name> ::= <substitution> <template-args> + // ::= <unscoped-name> <template-args> + // ::= <unscoped-name> + // - Merge the two productions that start with unscoped-name: + // <name> ::= <unscoped-name> [<template-args>] + + ParseState copy = state->parse_state; + // "std<...>" isn't a valid name. + if (ParseSubstitution(state, /*accept_std=*/false) && ParseTemplateArgs(state)) { return true; } - *state = copy; + state->parse_state = copy; - // Less greedy than <unscoped-template-name> <template-args>. - if (ParseUnscopedName(state)) { - return true; - } - return false; + // Note there's no need to restore state after this since only the first + // subparser can fail. + return ParseUnscopedName(state) && Optional(ParseTemplateArgs(state)); } // <unscoped-name> ::= <unqualified-name> // ::= St <unqualified-name> static bool ParseUnscopedName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } if (ParseUnqualifiedName(state)) { return true; } - State copy = *state; - if (ParseTwoCharToken(state, "St") && - MaybeAppend(state, "std::") && + ParseState copy = state->parse_state; + if (ParseTwoCharToken(state, "St") && MaybeAppend(state, "std::") && ParseUnqualifiedName(state)) { return true; } - *state = copy; + state->parse_state = copy; return false; } -// <unscoped-template-name> ::= <unscoped-name> -// ::= <substitution> -static bool ParseUnscopedTemplateName(State *state) { - return ParseUnscopedName(state) || ParseSubstitution(state); +// <ref-qualifer> ::= R // lvalue method reference qualifier +// ::= O // rvalue method reference qualifier +static inline bool ParseRefQualifier(State* state) { + return ParseCharClass(state, "OR"); } -// <nested-name> ::= N [<CV-qualifiers>] <prefix> <unqualified-name> E -// ::= N [<CV-qualifiers>] <template-prefix> <template-args> E +// <nested-name> ::= N [<CV-qualifiers>] [<ref-qualifier>] <prefix> +// <unqualified-name> E +// ::= N [<CV-qualifiers>] [<ref-qualifier>] <template-prefix> +// <template-args> E static bool ParseNestedName(State *state) { - State copy = *state; - if (ParseOneCharToken(state, 'N') && - EnterNestedName(state) && + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'N') && EnterNestedName(state) && Optional(ParseCVQualifiers(state)) && - ParsePrefix(state) && + Optional(ParseRefQualifier(state)) && ParsePrefix(state) && LeaveNestedName(state, copy.nest_level) && ParseOneCharToken(state, 'E')) { return true; } - *state = copy; + state->parse_state = copy; return false; } @@ -577,12 +765,17 @@ // ::= <template-param> // ::= <substitution> static bool ParsePrefix(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } bool has_something = false; while (true) { MaybeAppendSeparator(state); if (ParseTemplateParam(state) || - ParseSubstitution(state) || - ParseUnscopedName(state)) { + ParseSubstitution(state, /*accept_std=*/true) || + ParseUnscopedName(state) || + (ParseOneCharToken(state, 'M') && ParseUnnamedTypeName(state))) { has_something = true; MaybeIncreaseNestLevel(state); continue; @@ -597,40 +790,122 @@ return true; } -// <unqualified-name> ::= <operator-name> -// ::= <ctor-dtor-name> +// <unqualified-name> ::= <operator-name> [<abi-tags>] +// ::= <ctor-dtor-name> [<abi-tags>] // ::= <source-name> [<abi-tags>] // ::= <local-source-name> [<abi-tags>] +// ::= <unnamed-type-name> [<abi-tags>] +// +// <local-source-name> is a GCC extension; see below. static bool ParseUnqualifiedName(State *state) { - return (ParseOperatorName(state) || - ParseCtorDtorName(state) || - (ParseSourceName(state) && Optional(ParseAbiTags(state))) || - (ParseLocalSourceName(state) && Optional(ParseAbiTags(state)))); + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } + if (ParseOperatorName(state, nullptr) || ParseCtorDtorName(state) || + ParseSourceName(state) || ParseLocalSourceName(state) || + ParseUnnamedTypeName(state)) { + return ParseAbiTags(state); + } + return false; +} + +// <abi-tags> ::= <abi-tag> [<abi-tags>] +// <abi-tag> ::= B <source-name> +static bool ParseAbiTags(State* state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } + + while (ParseOneCharToken(state, 'B')) { + ParseState copy = state->parse_state; + MaybeAppend(state, "[abi:"); + + if (!ParseSourceName(state)) { + state->parse_state = copy; + return false; + } + MaybeAppend(state, "]"); + } + + return true; } // <source-name> ::= <positive length number> <identifier> static bool ParseSourceName(State *state) { - State copy = *state; + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } + ParseState copy = state->parse_state; int length = -1; - if (ParseNumber(state, &length) && ParseIdentifier(state, length)) { + if (ParseNumber(state, &length) && + ParseIdentifier(state, static_cast<size_t>(length))) { return true; } - *state = copy; + state->parse_state = copy; return false; } // <local-source-name> ::= L <source-name> [<discriminator>] // // References: -// http://gcc.gnu.org/bugzilla/show_bug.cgi?id=31775 -// http://gcc.gnu.org/viewcvs?view=rev&revision=124467 +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=31775 +// https://gcc.gnu.org/viewcvs?view=rev&revision=124467 static bool ParseLocalSourceName(State *state) { - State copy = *state; + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } + ParseState copy = state->parse_state; if (ParseOneCharToken(state, 'L') && ParseSourceName(state) && Optional(ParseDiscriminator(state))) { return true; } - *state = copy; + state->parse_state = copy; + return false; +} + +// <unnamed-type-name> ::= Ut [<(nonnegative) number>] _ +// ::= <closure-type-name> +// <closure-type-name> ::= Ul <lambda-sig> E [<(nonnegative) number>] _ +// <lambda-sig> ::= <(parameter) type>+ +static bool ParseUnnamedTypeName(State* state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } + ParseState copy = state->parse_state; + // Type's 1-based index n is encoded as { "", n == 1; itoa(n-2), otherwise }. + // Optionally parse the encoded value into 'which' and add 2 to get the index. + int which = -1; + + // Unnamed type local to function or class. + if (ParseTwoCharToken(state, "Ut") && Optional(ParseNumber(state, &which)) && + which <= std::numeric_limits<int>::max() - 2 && // Don't overflow. + ParseOneCharToken(state, '_')) { + MaybeAppend(state, "{unnamed type#"); + MaybeAppendDecimal(state, 2 + which); + MaybeAppend(state, "}"); + return true; + } + state->parse_state = copy; + + // Closure type. + which = -1; + if (ParseTwoCharToken(state, "Ul") && DisableAppend(state) && + OneOrMore(ParseType, state) && RestoreAppend(state, copy.append) && + ParseOneCharToken(state, 'E') && Optional(ParseNumber(state, &which)) && + which <= std::numeric_limits<int>::max() - 2 && // Don't overflow. + ParseOneCharToken(state, '_')) { + MaybeAppend(state, "{lambda()#"); + MaybeAppendDecimal(state, 2 + which); + MaybeAppend(state, "}"); + return true; + } + state->parse_state = copy; + return false; } @@ -638,23 +913,34 @@ // If "number_out" is non-null, then *number_out is set to the value of the // parsed number on success. static bool ParseNumber(State *state, int *number_out) { - int sign = 1; - if (ParseOneCharToken(state, 'n')) { - sign = -1; + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; } - const char *p = state->mangled_cur; - int number = 0; - for (;*p != '\0'; ++p) { + bool negative = false; + if (ParseOneCharToken(state, 'n')) { + negative = true; + } + const char* p = RemainingInput(state); + uint64_t number = 0; + for (; *p != '\0'; ++p) { if (IsDigit(*p)) { - number = number * 10 + (*p - '0'); + number = number * 10 + static_cast<uint64_t>(*p - '0'); } else { break; } } - if (p != state->mangled_cur) { // Conversion succeeded. - state->mangled_cur = p; - if (number_out != NULL) { - *number_out = number * sign; + // Apply the sign with uint64_t arithmetic so overflows aren't UB. Gives + // "incorrect" results for out-of-range inputs, but negative values only + // appear for literals, which aren't printed. + if (negative) { + number = ~number + 1; + } + if (p != RemainingInput(state)) { // Conversion succeeded. + state->parse_state.mangled_idx += p - RemainingInput(state); + if (number_out != nullptr) { + // Note: possibly truncate "number". + *number_out = static_cast<int>(number); } return true; } @@ -664,14 +950,18 @@ // Floating-point literals are encoded using a fixed-length lowercase // hexadecimal string. static bool ParseFloatNumber(State *state) { - const char *p = state->mangled_cur; - for (;*p != '\0'; ++p) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } + const char* p = RemainingInput(state); + for (; *p != '\0'; ++p) { if (!IsDigit(*p) && !(*p >= 'a' && *p <= 'f')) { break; } } - if (p != state->mangled_cur) { // Conversion succeeded. - state->mangled_cur = p; + if (p != RemainingInput(state)) { // Conversion succeeded. + state->parse_state.mangled_idx += p - RemainingInput(state); return true; } return false; @@ -680,93 +970,91 @@ // The <seq-id> is a sequence number in base 36, // using digits and upper case letters static bool ParseSeqId(State *state) { - const char *p = state->mangled_cur; - for (;*p != '\0'; ++p) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } + const char* p = RemainingInput(state); + for (; *p != '\0'; ++p) { if (!IsDigit(*p) && !(*p >= 'A' && *p <= 'Z')) { break; } } - if (p != state->mangled_cur) { // Conversion succeeded. - state->mangled_cur = p; + if (p != RemainingInput(state)) { // Conversion succeeded. + state->parse_state.mangled_idx += p - RemainingInput(state); return true; } return false; } // <identifier> ::= <unqualified source code identifier> (of given length) -static bool ParseIdentifier(State* state, ssize_t length) { - if (length == -1 || - !AtLeastNumCharsRemaining(state->mangled_cur, length)) { +static bool ParseIdentifier(State* state, size_t length) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } + if (!AtLeastNumCharsRemaining(RemainingInput(state), length)) { return false; } if (IdentifierIsAnonymousNamespace(state, length)) { MaybeAppend(state, "(anonymous namespace)"); } else { - MaybeAppendWithLength(state, state->mangled_cur, length); + MaybeAppendWithLength(state, RemainingInput(state), length); } - state->mangled_cur += length; + state->parse_state.mangled_idx += length; return true; } -// <abi-tags> ::= <abi-tag> [<abi-tags>] -static bool ParseAbiTags(State *state) { - State copy = *state; - DisableAppend(state); - if (OneOrMore(ParseAbiTag, state)) { - RestoreAppend(state, copy.append); - return true; - } - *state = copy; - return false; -} - -// <abi-tag> ::= B <source-name> -static bool ParseAbiTag(State *state) { - return ParseOneCharToken(state, 'B') && ParseSourceName(state); -} - // <operator-name> ::= nw, and other two letters cases // ::= cv <type> # (cast) // ::= v <digit> <source-name> # vendor extended operator -static bool ParseOperatorName(State *state) { - if (!AtLeastNumCharsRemaining(state->mangled_cur, 2)) { +static bool ParseOperatorName(State* state, int* arity) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } + if (!AtLeastNumCharsRemaining(RemainingInput(state), 2)) { return false; } // First check with "cv" (cast) case. - State copy = *state; - if (ParseTwoCharToken(state, "cv") && - MaybeAppend(state, "operator ") && - EnterNestedName(state) && - ParseType(state) && + ParseState copy = state->parse_state; + if (ParseTwoCharToken(state, "cv") && MaybeAppend(state, "operator ") && + EnterNestedName(state) && ParseType(state) && LeaveNestedName(state, copy.nest_level)) { + if (arity != nullptr) { + *arity = 1; + } return true; } - *state = copy; + state->parse_state = copy; // Then vendor extended operators. - if (ParseOneCharToken(state, 'v') && ParseCharClass(state, "0123456789") && + if (ParseOneCharToken(state, 'v') && ParseDigit(state, arity) && ParseSourceName(state)) { return true; } - *state = copy; + state->parse_state = copy; // Other operator names should start with a lower alphabet followed // by a lower/upper alphabet. - if (!(IsLower(state->mangled_cur[0]) && - IsAlpha(state->mangled_cur[1]))) { + if (!(IsLower(RemainingInput(state)[0]) && + IsAlpha(RemainingInput(state)[1]))) { return false; } // We may want to perform a binary search if we really need speed. const AbbrevPair *p; - for (p = kOperatorList; p->abbrev != NULL; ++p) { - if (state->mangled_cur[0] == p->abbrev[0] && - state->mangled_cur[1] == p->abbrev[1]) { + for (p = kOperatorList; p->abbrev != nullptr; ++p) { + if (RemainingInput(state)[0] == p->abbrev[0] && + RemainingInput(state)[1] == p->abbrev[1]) { + if (arity != nullptr) { + *arity = p->arity; + } MaybeAppend(state, "operator"); if (IsLower(*p->real_name)) { // new, delete, etc. MaybeAppend(state, " "); } MaybeAppend(state, p->real_name); - state->mangled_cur += 2; + state->parse_state.mangled_idx += 2; return true; } } @@ -777,6 +1065,7 @@ // ::= TT <type> // ::= TI <type> // ::= TS <type> +// ::= TH <type> # thread-local // ::= Tc <call-offset> <call-offset> <(base) encoding> // ::= GV <(object) name> // ::= T <call-offset> <(base) encoding> @@ -792,123 +1081,168 @@ // Note: we don't care much about them since they don't appear in // stack traces. The are special data. static bool ParseSpecialName(State *state) { - State copy = *state; - if (ParseOneCharToken(state, 'T') && - ParseCharClass(state, "VTIS") && + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "VTISH") && ParseType(state)) { return true; } - *state = copy; + state->parse_state = copy; if (ParseTwoCharToken(state, "Tc") && ParseCallOffset(state) && ParseCallOffset(state) && ParseEncoding(state)) { return true; } - *state = copy; + state->parse_state = copy; - if (ParseTwoCharToken(state, "GV") && - ParseName(state)) { + if (ParseTwoCharToken(state, "GV") && ParseName(state)) { return true; } - *state = copy; + state->parse_state = copy; if (ParseOneCharToken(state, 'T') && ParseCallOffset(state) && ParseEncoding(state)) { return true; } - *state = copy; + state->parse_state = copy; // G++ extensions if (ParseTwoCharToken(state, "TC") && ParseType(state) && - ParseNumber(state, NULL) && ParseOneCharToken(state, '_') && - DisableAppend(state) && - ParseType(state)) { + ParseNumber(state, nullptr) && ParseOneCharToken(state, '_') && + DisableAppend(state) && ParseType(state)) { RestoreAppend(state, copy.append); return true; } - *state = copy; + state->parse_state = copy; if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "FJ") && ParseType(state)) { return true; } - *state = copy; + state->parse_state = copy; if (ParseTwoCharToken(state, "GR") && ParseName(state)) { return true; } - *state = copy; + state->parse_state = copy; if (ParseTwoCharToken(state, "GA") && ParseEncoding(state)) { return true; } - *state = copy; + state->parse_state = copy; if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "hv") && ParseCallOffset(state) && ParseEncoding(state)) { return true; } - *state = copy; + state->parse_state = copy; return false; } // <call-offset> ::= h <nv-offset> _ // ::= v <v-offset> _ static bool ParseCallOffset(State *state) { - State copy = *state; - if (ParseOneCharToken(state, 'h') && - ParseNVOffset(state) && ParseOneCharToken(state, '_')) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'h') && ParseNVOffset(state) && + ParseOneCharToken(state, '_')) { return true; } - *state = copy; + state->parse_state = copy; - if (ParseOneCharToken(state, 'v') && - ParseVOffset(state) && ParseOneCharToken(state, '_')) { + if (ParseOneCharToken(state, 'v') && ParseVOffset(state) && + ParseOneCharToken(state, '_')) { return true; } - *state = copy; + state->parse_state = copy; return false; } // <nv-offset> ::= <(offset) number> static bool ParseNVOffset(State *state) { - return ParseNumber(state, NULL); + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } + return ParseNumber(state, nullptr); } // <v-offset> ::= <(offset) number> _ <(virtual offset) number> static bool ParseVOffset(State *state) { - State copy = *state; - if (ParseNumber(state, NULL) && ParseOneCharToken(state, '_') && - ParseNumber(state, NULL)) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } + ParseState copy = state->parse_state; + if (ParseNumber(state, nullptr) && ParseOneCharToken(state, '_') && + ParseNumber(state, nullptr)) { return true; } - *state = copy; + state->parse_state = copy; return false; } -// <ctor-dtor-name> ::= C1 | C2 | C3 +// <ctor-dtor-name> ::= C1 | C2 | C3 | CI1 <base-class-type> | CI2 +// <base-class-type> // ::= D0 | D1 | D2 +// # GCC extensions: "unified" constructor/destructor. See +// # +// https://github.com/gcc-mirror/gcc/blob/7ad17b583c3643bd4557f29b8391ca7ef08391f5/gcc/cp/mangle.c#L1847 +// ::= C4 | D4 static bool ParseCtorDtorName(State *state) { - State copy = *state; - if (ParseOneCharToken(state, 'C') && - ParseCharClass(state, "123")) { - const char * const prev_name = state->prev_name; - const ssize_t prev_name_length = state->prev_name_length; - MaybeAppendWithLength(state, prev_name, prev_name_length); - return true; + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; } - *state = copy; + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'C')) { + if (ParseCharClass(state, "1234")) { + const char* const prev_name = + state->out + state->parse_state.prev_name_idx; + MaybeAppendWithLength(state, prev_name, + state->parse_state.prev_name_length); + return true; + } else if (ParseOneCharToken(state, 'I') && ParseCharClass(state, "12") && + ParseClassEnumType(state)) { + return true; + } + } + state->parse_state = copy; - if (ParseOneCharToken(state, 'D') && - ParseCharClass(state, "012")) { - const char * const prev_name = state->prev_name; - const ssize_t prev_name_length = state->prev_name_length; + if (ParseOneCharToken(state, 'D') && ParseCharClass(state, "0124")) { + const char* const prev_name = state->out + state->parse_state.prev_name_idx; MaybeAppend(state, "~"); - MaybeAppendWithLength(state, prev_name, prev_name_length); + MaybeAppendWithLength(state, prev_name, + state->parse_state.prev_name_length); return true; } - *state = copy; + state->parse_state = copy; + return false; +} + +// <decltype> ::= Dt <expression> E # decltype of an id-expression or class +// # member access (C++0x) +// ::= DT <expression> E # decltype of an expression (C++0x) +static bool ParseDecltype(State* state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } + + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'D') && ParseCharClass(state, "tT") && + ParseExpression(state) && ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; + return false; } @@ -921,67 +1255,93 @@ // ::= U <source-name> <type> # vendor extended type qualifier // ::= <builtin-type> // ::= <function-type> -// ::= <class-enum-type> +// ::= <class-enum-type> # note: just an alias for <name> // ::= <array-type> // ::= <pointer-to-member-type> // ::= <template-template-param> <template-args> // ::= <template-param> +// ::= <decltype> // ::= <substitution> // ::= Dp <type> # pack expansion of (C++0x) -// ::= Dt <expression> E # decltype of an id-expression or class -// # member access (C++0x) -// ::= DT <expression> E # decltype of an expression (C++0x) +// ::= Dv <num-elems> _ # GNU vector extension // static bool ParseType(State *state) { - // We should check CV-qualifers, and PRGC things first. - State copy = *state; - if (ParseCVQualifiers(state) && ParseType(state)) { - return true; + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; } - *state = copy; + ParseState copy = state->parse_state; - if (ParseCharClass(state, "OPRCG") && ParseType(state)) { - return true; + // We should check CV-qualifers, and PRGC things first. + // + // CV-qualifiers overlap with some operator names, but an operator name is not + // valid as a type. To avoid an ambiguity that can lead to exponential time + // complexity, refuse to backtrack the CV-qualifiers. + // + // _Z4aoeuIrMvvE + // => _Z 4aoeuI rM v v E + // aoeu<operator%=, void, void> + // => _Z 4aoeuI r Mv v E + // aoeu<void void::* restrict> + // + // By consuming the CV-qualifiers first, the former parse is disabled. + if (ParseCVQualifiers(state)) { + const bool result = ParseType(state); + if (!result) { + state->parse_state = copy; + } + return result; } - *state = copy; + state->parse_state = copy; + + // Similarly, these tag characters can overlap with other <name>s resulting in + // two different parse prefixes that land on <template-args> in the same + // place, such as "C3r1xI...". So, disable the "ctor-name = C3" parse by + // refusing to backtrack the tag characters. + if (ParseCharClass(state, "OPRCG")) { + const bool result = ParseType(state); + if (!result) { + state->parse_state = copy; + } + return result; + } + state->parse_state = copy; if (ParseTwoCharToken(state, "Dp") && ParseType(state)) { return true; } - *state = copy; - - if (ParseOneCharToken(state, 'D') && ParseCharClass(state, "tT") && - ParseExpression(state) && ParseOneCharToken(state, 'E')) { - return true; - } - *state = copy; + state->parse_state = copy; if (ParseOneCharToken(state, 'U') && ParseSourceName(state) && ParseType(state)) { return true; } - *state = copy; + state->parse_state = copy; - if (ParseBuiltinType(state) || - ParseFunctionType(state) || - ParseClassEnumType(state) || - ParseArrayType(state) || - ParsePointerToMemberType(state) || - ParseSubstitution(state)) { + if (ParseBuiltinType(state) || ParseFunctionType(state) || + ParseClassEnumType(state) || ParseArrayType(state) || + ParsePointerToMemberType(state) || ParseDecltype(state) || + // "std" on its own isn't a type. + ParseSubstitution(state, /*accept_std=*/false)) { return true; } - if (ParseTemplateTemplateParam(state) && - ParseTemplateArgs(state)) { + if (ParseTemplateTemplateParam(state) && ParseTemplateArgs(state)) { return true; } - *state = copy; + state->parse_state = copy; // Less greedy than <template-template-param> <template-args>. if (ParseTemplateParam(state)) { return true; } + if (ParseTwoCharToken(state, "Dv") && ParseNumber(state, nullptr) && + ParseOneCharToken(state, '_')) { + return true; + } + state->parse_state = copy; + return false; } @@ -989,6 +1349,10 @@ // We don't allow empty <CV-qualifiers> to avoid infinite loop in // ParseType(). static bool ParseCVQualifiers(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } int num_cv_qualifiers = 0; num_cv_qualifiers += ParseOneCharToken(state, 'r'); num_cv_qualifiers += ParseOneCharToken(state, 'V'); @@ -996,208 +1360,529 @@ return num_cv_qualifiers > 0; } -// <builtin-type> ::= v, etc. +// <builtin-type> ::= v, etc. # single-character builtin types // ::= u <source-name> +// ::= Dd, etc. # two-character builtin types +// +// Not supported: +// ::= DF <number> _ # _FloatN (N bits) +// static bool ParseBuiltinType(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } const AbbrevPair *p; - for (p = kBuiltinTypeList; p->abbrev != NULL; ++p) { - if (state->mangled_cur[0] == p->abbrev[0]) { + for (p = kBuiltinTypeList; p->abbrev != nullptr; ++p) { + // Guaranteed only 1- or 2-character strings in kBuiltinTypeList. + if (p->abbrev[1] == '\0') { + if (ParseOneCharToken(state, p->abbrev[0])) { + MaybeAppend(state, p->real_name); + return true; + } + } else if (p->abbrev[2] == '\0' && ParseTwoCharToken(state, p->abbrev)) { MaybeAppend(state, p->real_name); - ++state->mangled_cur; return true; } } - State copy = *state; + ParseState copy = state->parse_state; if (ParseOneCharToken(state, 'u') && ParseSourceName(state)) { return true; } - *state = copy; + state->parse_state = copy; return false; } -// <function-type> ::= F [Y] <bare-function-type> E -static bool ParseFunctionType(State *state) { - State copy = *state; - if (ParseOneCharToken(state, 'F') && - Optional(ParseOneCharToken(state, 'Y')) && - ParseBareFunctionType(state) && ParseOneCharToken(state, 'E')) { +// <exception-spec> ::= Do # non-throwing +// exception-specification (e.g., +// noexcept, throw()) +// ::= DO <expression> E # computed (instantiation-dependent) +// noexcept +// ::= Dw <type>+ E # dynamic exception specification +// with instantiation-dependent types +static bool ParseExceptionSpec(State* state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } + + if (ParseTwoCharToken(state, "Do")) { return true; } - *state = copy; + + ParseState copy = state->parse_state; + if (ParseTwoCharToken(state, "DO") && ParseExpression(state) && + ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; + if (ParseTwoCharToken(state, "Dw") && OneOrMore(ParseType, state) && + ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; + + return false; +} + +// <function-type> ::= [exception-spec] F [Y] <bare-function-type> [O] E +static bool ParseFunctionType(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } + ParseState copy = state->parse_state; + if (Optional(ParseExceptionSpec(state)) && ParseOneCharToken(state, 'F') && + Optional(ParseOneCharToken(state, 'Y')) && ParseBareFunctionType(state) && + Optional(ParseOneCharToken(state, 'O')) && + ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; return false; } // <bare-function-type> ::= <(signature) type>+ static bool ParseBareFunctionType(State *state) { - State copy = *state; + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } + ParseState copy = state->parse_state; DisableAppend(state); if (OneOrMore(ParseType, state)) { RestoreAppend(state, copy.append); MaybeAppend(state, "()"); return true; } - *state = copy; + state->parse_state = copy; return false; } // <class-enum-type> ::= <name> static bool ParseClassEnumType(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } return ParseName(state); } // <array-type> ::= A <(positive dimension) number> _ <(element) type> // ::= A [<(dimension) expression>] _ <(element) type> static bool ParseArrayType(State *state) { - State copy = *state; - if (ParseOneCharToken(state, 'A') && ParseNumber(state, NULL) && + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'A') && ParseNumber(state, nullptr) && ParseOneCharToken(state, '_') && ParseType(state)) { return true; } - *state = copy; + state->parse_state = copy; if (ParseOneCharToken(state, 'A') && Optional(ParseExpression(state)) && ParseOneCharToken(state, '_') && ParseType(state)) { return true; } - *state = copy; + state->parse_state = copy; return false; } // <pointer-to-member-type> ::= M <(class) type> <(member) type> static bool ParsePointerToMemberType(State *state) { - State copy = *state; - if (ParseOneCharToken(state, 'M') && ParseType(state) && - ParseType(state)) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'M') && ParseType(state) && ParseType(state)) { return true; } - *state = copy; + state->parse_state = copy; return false; } // <template-param> ::= T_ // ::= T <parameter-2 non-negative number> _ static bool ParseTemplateParam(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } if (ParseTwoCharToken(state, "T_")) { MaybeAppend(state, "?"); // We don't support template substitutions. return true; } - State copy = *state; - if (ParseOneCharToken(state, 'T') && ParseNumber(state, NULL) && + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'T') && ParseNumber(state, nullptr) && ParseOneCharToken(state, '_')) { MaybeAppend(state, "?"); // We don't support template substitutions. return true; } - *state = copy; + state->parse_state = copy; return false; } - // <template-template-param> ::= <template-param> // ::= <substitution> static bool ParseTemplateTemplateParam(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } return (ParseTemplateParam(state) || - ParseSubstitution(state)); + // "std" on its own isn't a template. + ParseSubstitution(state, /*accept_std=*/false)); } // <template-args> ::= I <template-arg>+ E static bool ParseTemplateArgs(State *state) { - State copy = *state; + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } + ParseState copy = state->parse_state; DisableAppend(state); - if (ParseOneCharToken(state, 'I') && - OneOrMore(ParseTemplateArg, state) && + if (ParseOneCharToken(state, 'I') && OneOrMore(ParseTemplateArg, state) && ParseOneCharToken(state, 'E')) { RestoreAppend(state, copy.append); MaybeAppend(state, "<>"); return true; } - *state = copy; + state->parse_state = copy; return false; } // <template-arg> ::= <type> // ::= <expr-primary> -// ::= I <template-arg>* E # argument pack // ::= J <template-arg>* E # argument pack // ::= X <expression> E static bool ParseTemplateArg(State *state) { - State copy = *state; - if ((ParseOneCharToken(state, 'I') || ParseOneCharToken(state, 'J')) && - ZeroOrMore(ParseTemplateArg, state) && + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'J') && ZeroOrMore(ParseTemplateArg, state) && ParseOneCharToken(state, 'E')) { return true; } - *state = copy; + state->parse_state = copy; - if (ParseType(state) || - ParseExprPrimary(state)) { + // There can be significant overlap between the following leading to + // exponential backtracking: + // + // <expr-primary> ::= L <type> <expr-cast-value> E + // e.g. L 2xxIvE 1 E + // <type> ==> <local-source-name> <template-args> + // e.g. L 2xx IvE + // + // This means parsing an entire <type> twice, and <type> can contain + // <template-arg>, so this can generate exponential backtracking. There is + // only overlap when the remaining input starts with "L <source-name>", so + // parse all cases that can start this way jointly to share the common prefix. + // + // We have: + // + // <template-arg> ::= <type> + // ::= <expr-primary> + // + // First, drop all the productions of <type> that must start with something + // other than 'L'. All that's left is <class-enum-type>; inline it. + // + // <type> ::= <nested-name> # starts with 'N' + // ::= <unscoped-name> + // ::= <unscoped-template-name> <template-args> + // ::= <local-name> # starts with 'Z' + // + // Drop and inline again: + // + // <type> ::= <unscoped-name> + // ::= <unscoped-name> <template-args> + // ::= <substitution> <template-args> # starts with 'S' + // + // Merge the first two, inline <unscoped-name>, drop last: + // + // <type> ::= <unqualified-name> [<template-args>] + // ::= St <unqualified-name> [<template-args>] # starts with 'S' + // + // Drop and inline: + // + // <type> ::= <operator-name> [<template-args>] # starts with lowercase + // ::= <ctor-dtor-name> [<template-args>] # starts with 'C' or 'D' + // ::= <source-name> [<template-args>] # starts with digit + // ::= <local-source-name> [<template-args>] + // ::= <unnamed-type-name> [<template-args>] # starts with 'U' + // + // One more time: + // + // <type> ::= L <source-name> [<template-args>] + // + // Likewise with <expr-primary>: + // + // <expr-primary> ::= L <type> <expr-cast-value> E + // ::= LZ <encoding> E # cannot overlap; drop + // ::= L <mangled_name> E # cannot overlap; drop + // + // By similar reasoning as shown above, the only <type>s starting with + // <source-name> are "<source-name> [<template-args>]". Inline this. + // + // <expr-primary> ::= L <source-name> [<template-args>] <expr-cast-value> E + // + // Now inline both of these into <template-arg>: + // + // <template-arg> ::= L <source-name> [<template-args>] + // ::= L <source-name> [<template-args>] <expr-cast-value> E + // + // Merge them and we're done: + // <template-arg> + // ::= L <source-name> [<template-args>] [<expr-cast-value> E] + if (ParseLocalSourceName(state) && Optional(ParseTemplateArgs(state))) { + copy = state->parse_state; + if (ParseExprCastValue(state) && ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy; return true; } - *state = copy; + + // Now that the overlapping cases can't reach this code, we can safely call + // both of these. + if (ParseType(state) || ParseExprPrimary(state)) { + return true; + } + state->parse_state = copy; if (ParseOneCharToken(state, 'X') && ParseExpression(state) && ParseOneCharToken(state, 'E')) { return true; } - *state = copy; + state->parse_state = copy; return false; } -// <expression> ::= <template-param> -// ::= <expr-primary> -// ::= <unary operator-name> <expression> -// ::= <binary operator-name> <expression> <expression> -// ::= <trinary operator-name> <expression> <expression> -// <expression> +// <unresolved-type> ::= <template-param> [<template-args>] +// ::= <decltype> +// ::= <substitution> +static inline bool ParseUnresolvedType(State* state) { + // No ComplexityGuard because we don't copy the state in this stack frame. + return (ParseTemplateParam(state) && Optional(ParseTemplateArgs(state))) || + ParseDecltype(state) || ParseSubstitution(state, /*accept_std=*/false); +} + +// <simple-id> ::= <source-name> [<template-args>] +static inline bool ParseSimpleId(State* state) { + // No ComplexityGuard because we don't copy the state in this stack frame. + + // Note: <simple-id> cannot be followed by a parameter pack; see comment in + // ParseUnresolvedType. + return ParseSourceName(state) && Optional(ParseTemplateArgs(state)); +} + +// <base-unresolved-name> ::= <source-name> [<template-args>] +// ::= on <operator-name> [<template-args>] +// ::= dn <destructor-name> +static bool ParseBaseUnresolvedName(State* state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } + + if (ParseSimpleId(state)) { + return true; + } + + ParseState copy = state->parse_state; + if (ParseTwoCharToken(state, "on") && ParseOperatorName(state, nullptr) && + Optional(ParseTemplateArgs(state))) { + return true; + } + state->parse_state = copy; + + if (ParseTwoCharToken(state, "dn") && + (ParseUnresolvedType(state) || ParseSimpleId(state))) { + return true; + } + state->parse_state = copy; + + return false; +} + +// <unresolved-name> ::= [gs] <base-unresolved-name> +// ::= sr <unresolved-type> <base-unresolved-name> +// ::= srN <unresolved-type> <unresolved-qualifier-level>+ E +// <base-unresolved-name> +// ::= [gs] sr <unresolved-qualifier-level>+ E +// <base-unresolved-name> +static bool ParseUnresolvedName(State* state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } + + ParseState copy = state->parse_state; + if (Optional(ParseTwoCharToken(state, "gs")) && + ParseBaseUnresolvedName(state)) { + return true; + } + state->parse_state = copy; + + if (ParseTwoCharToken(state, "sr") && ParseUnresolvedType(state) && + ParseBaseUnresolvedName(state)) { + return true; + } + state->parse_state = copy; + + if (ParseTwoCharToken(state, "sr") && ParseOneCharToken(state, 'N') && + ParseUnresolvedType(state) && + OneOrMore(/* <unresolved-qualifier-level> ::= */ ParseSimpleId, state) && + ParseOneCharToken(state, 'E') && ParseBaseUnresolvedName(state)) { + return true; + } + state->parse_state = copy; + + if (Optional(ParseTwoCharToken(state, "gs")) && + ParseTwoCharToken(state, "sr") && + OneOrMore(/* <unresolved-qualifier-level> ::= */ ParseSimpleId, state) && + ParseOneCharToken(state, 'E') && ParseBaseUnresolvedName(state)) { + return true; + } + state->parse_state = copy; + + return false; +} + +// <expression> ::= <1-ary operator-name> <expression> +// ::= <2-ary operator-name> <expression> <expression> +// ::= <3-ary operator-name> <expression> <expression> <expression> +// ::= cl <expression>+ E +// ::= cp <simple-id> <expression>* E # Clang-specific. +// ::= cv <type> <expression> # type (expression) +// ::= cv <type> _ <expression>* E # type (expr-list) // ::= st <type> +// ::= <template-param> +// ::= <function-param> +// ::= <expr-primary> +// ::= dt <expression> <unresolved-name> # expr.name +// ::= pt <expression> <unresolved-name> # expr->name +// ::= sp <expression> # argument pack expansion // ::= sr <type> <unqualified-name> <template-args> // ::= sr <type> <unqualified-name> +// <function-param> ::= fp <(top-level) CV-qualifiers> _ +// ::= fp <(top-level) CV-qualifiers> <number> _ +// ::= fL <number> p <(top-level) CV-qualifiers> _ +// ::= fL <number> p <(top-level) CV-qualifiers> <number> _ static bool ParseExpression(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } if (ParseTemplateParam(state) || ParseExprPrimary(state)) { return true; } - State copy = *state; - if (ParseOperatorName(state) && - ParseExpression(state) && - ParseExpression(state) && - ParseExpression(state)) { + ParseState copy = state->parse_state; + + // Object/function call expression. + if (ParseTwoCharToken(state, "cl") && OneOrMore(ParseExpression, state) && + ParseOneCharToken(state, 'E')) { return true; } - *state = copy; + state->parse_state = copy; - if (ParseOperatorName(state) && - ParseExpression(state) && - ParseExpression(state)) { + // Clang-specific "cp <simple-id> <expression>* E" + // https://clang.llvm.org/doxygen/ItaniumMangle_8cpp_source.html#l04338 + if (ParseTwoCharToken(state, "cp") && ParseSimpleId(state) && + ZeroOrMore(ParseExpression, state) && ParseOneCharToken(state, 'E')) { return true; } - *state = copy; + state->parse_state = copy; - if (ParseOperatorName(state) && - ParseExpression(state)) { + // Function-param expression (level 0). + if (ParseTwoCharToken(state, "fp") && Optional(ParseCVQualifiers(state)) && + Optional(ParseNumber(state, nullptr)) && ParseOneCharToken(state, '_')) { return true; } - *state = copy; + state->parse_state = copy; + // Function-param expression (level 1+). + if (ParseTwoCharToken(state, "fL") && Optional(ParseNumber(state, nullptr)) && + ParseOneCharToken(state, 'p') && Optional(ParseCVQualifiers(state)) && + Optional(ParseNumber(state, nullptr)) && ParseOneCharToken(state, '_')) { + return true; + } + state->parse_state = copy; + + // Parse the conversion expressions jointly to avoid re-parsing the <type> in + // their common prefix. Parsed as: + // <expression> ::= cv <type> <conversion-args> + // <conversion-args> ::= _ <expression>* E + // ::= <expression> + // + // Also don't try ParseOperatorName after seeing "cv", since ParseOperatorName + // also needs to accept "cv <type>" in other contexts. + if (ParseTwoCharToken(state, "cv")) { + if (ParseType(state)) { + ParseState copy2 = state->parse_state; + if (ParseOneCharToken(state, '_') && ZeroOrMore(ParseExpression, state) && + ParseOneCharToken(state, 'E')) { + return true; + } + state->parse_state = copy2; + if (ParseExpression(state)) { + return true; + } + } + } else { + // Parse unary, binary, and ternary operator expressions jointly, taking + // care not to re-parse subexpressions repeatedly. Parse like: + // <expression> ::= <operator-name> <expression> + // [<one-to-two-expressions>] + // <one-to-two-expressions> ::= <expression> [<expression>] + int arity = -1; + if (ParseOperatorName(state, &arity) && + arity > 0 && // 0 arity => disabled. + (arity < 3 || ParseExpression(state)) && + (arity < 2 || ParseExpression(state)) && + (arity < 1 || ParseExpression(state))) { + return true; + } + } + state->parse_state = copy; + + // sizeof type if (ParseTwoCharToken(state, "st") && ParseType(state)) { return true; } - *state = copy; + state->parse_state = copy; - if (ParseTwoCharToken(state, "sr") && ParseType(state) && - ParseUnqualifiedName(state) && - ParseTemplateArgs(state)) { + // Object and pointer member access expressions. + if ((ParseTwoCharToken(state, "dt") || ParseTwoCharToken(state, "pt")) && + ParseExpression(state) && ParseType(state)) { return true; } - *state = copy; + state->parse_state = copy; - if (ParseTwoCharToken(state, "sr") && ParseType(state) && - ParseUnqualifiedName(state)) { + // Pointer-to-member access expressions. This parses the same as a binary + // operator, but it's implemented separately because "ds" shouldn't be + // accepted in other contexts that parse an operator name. + if (ParseTwoCharToken(state, "ds") && ParseExpression(state) && + ParseExpression(state)) { return true; } - *state = copy; - return false; + state->parse_state = copy; + + // Parameter pack expansion + if (ParseTwoCharToken(state, "sp") && ParseExpression(state)) { + return true; + } + state->parse_state = copy; + + return ParseUnresolvedName(state); } // <expr-primary> ::= L <type> <(value) number> E @@ -1205,116 +1890,208 @@ // ::= L <mangled-name> E // // A bug in g++'s C++ ABI version 2 (-fabi-version=2). // ::= LZ <encoding> E +// +// Warning, subtle: the "bug" LZ production above is ambiguous with the first +// production where <type> starts with <local-name>, which can lead to +// exponential backtracking in two scenarios: +// +// - When whatever follows the E in the <local-name> in the first production is +// not a name, we backtrack the whole <encoding> and re-parse the whole thing. +// +// - When whatever follows the <local-name> in the first production is not a +// number and this <expr-primary> may be followed by a name, we backtrack the +// <name> and re-parse it. +// +// Moreover this ambiguity isn't always resolved -- for example, the following +// has two different parses: +// +// _ZaaILZ4aoeuE1x1EvE +// => operator&&<aoeu, x, E, void> +// => operator&&<(aoeu::x)(1), void> +// +// To resolve this, we just do what GCC's demangler does, and refuse to parse +// casts to <local-name> types. static bool ParseExprPrimary(State *state) { - State copy = *state; - if (ParseOneCharToken(state, 'L') && ParseType(state) && - ParseNumber(state, NULL) && - ParseOneCharToken(state, 'E')) { - return true; + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; } - *state = copy; + ParseState copy = state->parse_state; + // The "LZ" special case: if we see LZ, we commit to accept "LZ <encoding> E" + // or fail, no backtracking. + if (ParseTwoCharToken(state, "LZ")) { + if (ParseEncoding(state) && ParseOneCharToken(state, 'E')) { + return true; + } + + state->parse_state = copy; + return false; + } + + // The merged cast production. if (ParseOneCharToken(state, 'L') && ParseType(state) && - ParseFloatNumber(state) && - ParseOneCharToken(state, 'E')) { + ParseExprCastValue(state)) { return true; } - *state = copy; + state->parse_state = copy; if (ParseOneCharToken(state, 'L') && ParseMangledName(state) && ParseOneCharToken(state, 'E')) { return true; } - *state = copy; - - if (ParseTwoCharToken(state, "LZ") && ParseEncoding(state) && - ParseOneCharToken(state, 'E')) { - return true; - } - *state = copy; + state->parse_state = copy; return false; } -// <local-name> := Z <(function) encoding> E <(entity) name> -// [<discriminator>] -// := Z <(function) encoding> E s [<discriminator>] -static bool ParseLocalName(State *state) { - State copy = *state; - if (ParseOneCharToken(state, 'Z') && ParseEncoding(state) && - ParseOneCharToken(state, 'E') && MaybeAppend(state, "::") && - ParseName(state) && Optional(ParseDiscriminator(state))) { +// <number> or <float>, followed by 'E', as described above ParseExprPrimary. +static bool ParseExprCastValue(State* state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } + // We have to be able to backtrack after accepting a number because we could + // have e.g. "7fffE", which will accept "7" as a number but then fail to find + // the 'E'. + ParseState copy = state->parse_state; + if (ParseNumber(state, nullptr) && ParseOneCharToken(state, 'E')) { return true; } - *state = copy; + state->parse_state = copy; - if (ParseOneCharToken(state, 'Z') && ParseEncoding(state) && - ParseTwoCharToken(state, "Es") && Optional(ParseDiscriminator(state))) { + if (ParseFloatNumber(state) && ParseOneCharToken(state, 'E')) { return true; } - *state = copy; + state->parse_state = copy; + + return false; +} + +// <local-name> ::= Z <(function) encoding> E <(entity) name> [<discriminator>] +// ::= Z <(function) encoding> E s [<discriminator>] +// +// Parsing a common prefix of these two productions together avoids an +// exponential blowup of backtracking. Parse like: +// <local-name> := Z <encoding> E <local-name-suffix> +// <local-name-suffix> ::= s [<discriminator>] +// ::= <name> [<discriminator>] + +static bool ParseLocalNameSuffix(State* state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } + + if (MaybeAppend(state, "::") && ParseName(state) && + Optional(ParseDiscriminator(state))) { + return true; + } + + // Since we're not going to overwrite the above "::" by re-parsing the + // <encoding> (whose trailing '\0' byte was in the byte now holding the + // first ':'), we have to rollback the "::" if the <name> parse failed. + if (state->parse_state.append) { + state->out[state->parse_state.out_cur_idx - 2] = '\0'; + } + + return ParseOneCharToken(state, 's') && Optional(ParseDiscriminator(state)); +} + +static bool ParseLocalName(State* state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'Z') && ParseEncoding(state) && + ParseOneCharToken(state, 'E') && ParseLocalNameSuffix(state)) { + return true; + } + state->parse_state = copy; return false; } // <discriminator> := _ <(non-negative) number> static bool ParseDiscriminator(State *state) { - State copy = *state; - if (ParseOneCharToken(state, '_') && ParseNumber(state, NULL)) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, '_') && ParseNumber(state, nullptr)) { return true; } - *state = copy; + state->parse_state = copy; return false; } // <substitution> ::= S_ // ::= S <seq-id> _ // ::= St, etc. -static bool ParseSubstitution(State *state) { +// +// "St" is special in that it's not valid as a standalone name, and it *is* +// allowed to precede a name without being wrapped in "N...E". This means that +// if we accept it on its own, we can accept "St1a" and try to parse +// template-args, then fail and backtrack, accept "St" on its own, then "1a" as +// an unqualified name and re-parse the same template-args. To block this +// exponential backtracking, we disable it with 'accept_std=false' in +// problematic contexts. +static bool ParseSubstitution(State* state, bool accept_std) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } if (ParseTwoCharToken(state, "S_")) { MaybeAppend(state, "?"); // We don't support substitutions. return true; } - State copy = *state; + ParseState copy = state->parse_state; if (ParseOneCharToken(state, 'S') && ParseSeqId(state) && ParseOneCharToken(state, '_')) { MaybeAppend(state, "?"); // We don't support substitutions. return true; } - *state = copy; + state->parse_state = copy; // Expand abbreviations like "St" => "std". if (ParseOneCharToken(state, 'S')) { const AbbrevPair *p; - for (p = kSubstitutionList; p->abbrev != NULL; ++p) { - if (state->mangled_cur[0] == p->abbrev[1]) { + for (p = kSubstitutionList; p->abbrev != nullptr; ++p) { + if (RemainingInput(state)[0] == p->abbrev[1] && + (accept_std || p->abbrev[1] != 't')) { MaybeAppend(state, "std"); if (p->real_name[0] != '\0') { MaybeAppend(state, "::"); MaybeAppend(state, p->real_name); } - ++state->mangled_cur; + ++state->parse_state.mangled_idx; return true; } } } - *state = copy; + state->parse_state = copy; return false; } // Parse <mangled-name>, optionally followed by either a function-clone suffix // or version suffix. Returns true only if all of "mangled_cur" was consumed. static bool ParseTopLevelMangledName(State *state) { + ComplexityGuard guard(state); + if (guard.IsTooComplex()) { + return false; + } if (ParseMangledName(state)) { - if (state->mangled_cur[0] != '\0') { + if (RemainingInput(state)[0] != '\0') { // Drop trailing function clone suffix, if any. - if (IsFunctionCloneSuffix(state->mangled_cur)) { + if (IsFunctionCloneSuffix(RemainingInput(state))) { return true; } // Append trailing version suffix if any. // ex. _Z3foo@@GLIBCXX_3.4 - if (state->mangled_cur[0] == '@') { - MaybeAppend(state, state->mangled_cur); + if (RemainingInput(state)[0] == '@') { + MaybeAppend(state, RemainingInput(state)); return true; } return false; // Unconsumed suffix. @@ -1323,6 +2100,10 @@ } return false; } + +static bool Overflowed(const State* state) { + return state->parse_state.out_cur_idx >= state->out_end_idx; +} #endif // The demangler entry point. @@ -1359,7 +2140,8 @@ #else State state; InitState(&state, mangled, out, out_size); - return ParseTopLevelMangledName(&state) && !state.overflowed; + return ParseTopLevelMangledName(&state) && !Overflowed(&state) && + state.parse_state.out_cur_idx > 0; #endif }
diff --git a/base/third_party/symbolize/demangle.h b/base/third_party/symbolize/demangle.h index 46200c8..7d5cfaa 100644 --- a/base/third_party/symbolize/demangle.h +++ b/base/third_party/symbolize/demangle.h
@@ -70,6 +70,8 @@ #ifndef BASE_DEMANGLE_H_ #define BASE_DEMANGLE_H_ +#include <stddef.h> + #include "config.h" #include "glog/logging.h"
diff --git a/base/third_party/symbolize/patches/009-clang-format.patch b/base/third_party/symbolize/patches/009-clang-format.patch deleted file mode 100644 index 1ae3ab7..0000000 --- a/base/third_party/symbolize/patches/009-clang-format.patch +++ /dev/null
@@ -1,382 +0,0 @@ -diff --git a/base/third_party/symbolize/demangle.cc b/base/third_party/symbolize/demangle.cc -index 9276c5b879a8c..da4cbf6aeffa5 100644 ---- a/base/third_party/symbolize/demangle.cc -+++ b/base/third_party/symbolize/demangle.cc -@@ -149,11 +149,11 @@ static const AbbrevPair kSubstitutionList[] = { - - // State needed for demangling. - typedef struct { -- const char *mangled_cur; // Cursor of mangled name. -- char *out_cur; // Cursor of output string. -- const char *out_begin; // Beginning of output string. -- const char *out_end; // End of output string. -- const char *prev_name; // For constructors/destructors. -+ const char* mangled_cur; // Cursor of mangled name. -+ char* out_cur; // Cursor of output string. -+ const char* out_begin; // Beginning of output string. -+ const char* out_end; // End of output string. -+ const char* prev_name; // For constructors/destructors. - ssize_t prev_name_length; // For constructors/destructors. - short nest_level; // For nested names. - bool append; // Append flag. -@@ -172,7 +172,7 @@ static size_t StrLen(const char *str) { - } - - // Returns true if "str" has at least "n" characters remaining. --static bool AtLeastNumCharsRemaining(const char *str, ssize_t n) { -+static bool AtLeastNumCharsRemaining(const char* str, ssize_t n) { - for (ssize_t i = 0; i < n; ++i) { - if (str[i] == '\0') { - return false; -@@ -191,8 +191,10 @@ static bool StrPrefix(const char *str, const char *prefix) { - return prefix[i] == '\0'; // Consumed everything in "prefix". - } - --static void InitState(State *state, const char *mangled, -- char *out, size_t out_size) { -+static void InitState(State* state, -+ const char* mangled, -+ char* out, -+ size_t out_size) { - state->mangled_cur = mangled; - state->out_cur = out; - state->out_begin = out; -@@ -269,7 +271,7 @@ static bool ZeroOrMore(ParseFunc parse_func, State *state) { - // Append "str" at "out_cur". If there is an overflow, "overflowed" - // is set to true for later use. The output string is ensured to - // always terminate with '\0' as long as there is no overflow. --static void Append(State *state, const char * const str, ssize_t length) { -+static void Append(State* state, const char* const str, ssize_t length) { - for (ssize_t i = 0; i < length; ++i) { - if (state->out_cur + 1 < state->out_end) { // +1 for '\0' - *state->out_cur = str[i]; -@@ -325,7 +327,8 @@ static bool IsFunctionCloneSuffix(const char *str) { - - // Append "str" with some tweaks, iff "append" state is true. - // Returns true so that it can be placed in "if" conditions. --static void MaybeAppendWithLength(State *state, const char * const str, -+static void MaybeAppendWithLength(State* state, -+ const char* const str, - ssize_t length) { - if (state->append && length > 0) { - // Append a space if the output buffer ends with '<' and "str" -@@ -401,7 +404,7 @@ static void MaybeCancelLastSeparator(State *state) { - - // Returns true if the identifier of the given length pointed to by - // "mangled_cur" is anonymous namespace. --static bool IdentifierIsAnonymousNamespace(State *state, ssize_t length) { -+static bool IdentifierIsAnonymousNamespace(State* state, ssize_t length) { - static const char anon_prefix[] = "_GLOBAL__N_"; - return (length > static_cast<ssize_t>(sizeof(anon_prefix)) - - 1 && // Should be longer. -@@ -422,7 +425,7 @@ static bool ParseLocalSourceName(State *state); - static bool ParseNumber(State *state, int *number_out); - static bool ParseFloatNumber(State *state); - static bool ParseSeqId(State *state); --static bool ParseIdentifier(State *state, ssize_t length); -+static bool ParseIdentifier(State* state, ssize_t length); - static bool ParseAbiTags(State *state); - static bool ParseAbiTag(State *state); - static bool ParseOperatorName(State *state); -@@ -691,7 +694,7 @@ static bool ParseSeqId(State *state) { - } - - // <identifier> ::= <unqualified source code identifier> (of given length) --static bool ParseIdentifier(State *state, ssize_t length) { -+static bool ParseIdentifier(State* state, ssize_t length) { - if (length == -1 || - !AtLeastNumCharsRemaining(state->mangled_cur, length)) { - return false; -@@ -1323,7 +1326,7 @@ static bool ParseTopLevelMangledName(State *state) { - #endif - - // The demangler entry point. --bool Demangle(const char *mangled, char *out, size_t out_size) { -+bool Demangle(const char* mangled, char* out, size_t out_size) { - #if defined(GLOG_OS_WINDOWS) - #if defined(HAVE_DBGHELP) - // When built with incremental linking, the Windows debugger -diff --git a/base/third_party/symbolize/demangle.h b/base/third_party/symbolize/demangle.h -index 416f7ee153560..46200c8387d82 100644 ---- a/base/third_party/symbolize/demangle.h -+++ b/base/third_party/symbolize/demangle.h -@@ -78,7 +78,7 @@ _START_GOOGLE_NAMESPACE_ - // Demangle "mangled". On success, return true and write the - // demangled symbol name to "out". Otherwise, return false. - // "out" is modified even if demangling is unsuccessful. --bool GLOG_EXPORT Demangle(const char *mangled, char *out, size_t out_size); -+bool GLOG_EXPORT Demangle(const char* mangled, char* out, size_t out_size); - - _END_GOOGLE_NAMESPACE_ - -diff --git a/base/third_party/symbolize/glog/logging.h b/base/third_party/symbolize/glog/logging.h -index 46869226024da..b935e3ec9cded 100644 ---- a/base/third_party/symbolize/glog/logging.h -+++ b/base/third_party/symbolize/glog/logging.h -@@ -38,4 +38,4 @@ - - // Not needed in Chrome. - --#endif // GLOG_LOGGING_H -+#endif // GLOG_LOGGING_H -diff --git a/base/third_party/symbolize/symbolize.cc b/base/third_party/symbolize/symbolize.cc -index b6ddc85d57185..a3b8399f411bf 100644 ---- a/base/third_party/symbolize/symbolize.cc -+++ b/base/third_party/symbolize/symbolize.cc -@@ -97,7 +97,7 @@ void InstallSymbolizeOpenObjectFileCallback( - // where the input symbol is demangled in-place. - // To keep stack consumption low, we would like this function to not - // get inlined. --static ATTRIBUTE_NOINLINE void DemangleInplace(char *out, size_t out_size) { -+static ATTRIBUTE_NOINLINE void DemangleInplace(char* out, size_t out_size) { - char demangled[256]; // Big enough for sane demangled symbols. - if (Demangle(out, demangled, sizeof(demangled))) { - // Demangling succeeded. Copy to out if the space allows. -@@ -121,17 +121,17 @@ _END_GOOGLE_NAMESPACE_ - #else - #include <elf.h> - #endif -+#include <fcntl.h> -+#include <stdint.h> -+#include <sys/stat.h> -+#include <sys/types.h> -+#include <unistd.h> - #include <cerrno> - #include <climits> - #include <cstddef> - #include <cstdio> - #include <cstdlib> - #include <cstring> --#include <fcntl.h> --#include <stdint.h> --#include <sys/stat.h> --#include <sys/types.h> --#include <unistd.h> - - #include "symbolize.h" - #include "config.h" -@@ -153,7 +153,8 @@ ssize_t ReadFromOffset(const int fd, - const size_t count, - const size_t offset) { - SAFE_ASSERT(fd >= 0); -- SAFE_ASSERT(count <= static_cast<size_t>(std::numeric_limits<ssize_t>::max())); -+ SAFE_ASSERT(count <= -+ static_cast<size_t>(std::numeric_limits<ssize_t>::max())); - char *buf0 = reinterpret_cast<char *>(buf); - size_t num_bytes = 0; - while (num_bytes < count) { -@@ -176,8 +177,10 @@ ssize_t ReadFromOffset(const int fd, - // pointed by "fd" into the buffer starting at "buf" while handling - // short reads and EINTR. On success, return true. Otherwise, return - // false. --static bool ReadFromOffsetExact(const int fd, void *buf, -- const size_t count, const size_t offset) { -+static bool ReadFromOffsetExact(const int fd, -+ void* buf, -+ const size_t count, -+ const size_t offset) { - ssize_t len = ReadFromOffset(fd, buf, count, offset); - return static_cast<size_t>(len) == count; - } -@@ -199,9 +202,11 @@ static int FileGetElfType(const int fd) { - // and return true. Otherwise, return false. - // To keep stack consumption low, we would like this function to not get - // inlined. --static ATTRIBUTE_NOINLINE bool --GetSectionHeaderByType(const int fd, ElfW(Half) sh_num, const size_t sh_offset, -- ElfW(Word) type, ElfW(Shdr) *out) { -+static ATTRIBUTE_NOINLINE bool GetSectionHeaderByType(const int fd, -+ ElfW(Half) sh_num, -+ const size_t sh_offset, -+ ElfW(Word) type, -+ ElfW(Shdr) * out) { - // Read at most 16 section headers at a time to save read calls. - ElfW(Shdr) buf[16]; - for (size_t i = 0; i < sh_num;) { -@@ -248,8 +253,8 @@ bool GetSectionHeaderByName(int fd, const char *name, size_t name_len, - } - - for (size_t i = 0; i < elf_header.e_shnum; ++i) { -- size_t section_header_offset = (elf_header.e_shoff + -- elf_header.e_shentsize * i); -+ size_t section_header_offset = -+ (elf_header.e_shoff + elf_header.e_shentsize * i); - if (!ReadFromOffsetExact(fd, out, sizeof(*out), section_header_offset)) { - return false; - } -@@ -281,10 +286,13 @@ bool GetSectionHeaderByName(int fd, const char *name, size_t name_len, - // to out. Otherwise, return false. - // To keep stack consumption low, we would like this function to not get - // inlined. --static ATTRIBUTE_NOINLINE bool --FindSymbol(uint64_t pc, const int fd, char *out, size_t out_size, -- uint64_t symbol_offset, const ElfW(Shdr) *strtab, -- const ElfW(Shdr) *symtab) { -+static ATTRIBUTE_NOINLINE bool FindSymbol(uint64_t pc, -+ const int fd, -+ char* out, -+ size_t out_size, -+ uint64_t symbol_offset, -+ const ElfW(Shdr) * strtab, -+ const ElfW(Shdr) * symtab) { - if (symtab == NULL) { - return false; - } -@@ -384,7 +392,7 @@ namespace { - // and snprintf(). - class LineReader { - public: -- explicit LineReader(int fd, char *buf, size_t buf_len, size_t offset) -+ explicit LineReader(int fd, char* buf, size_t buf_len, size_t offset) - : fd_(fd), - buf_(buf), - buf_len_(buf_len), -@@ -449,11 +457,12 @@ class LineReader { - } - - private: -- LineReader(const LineReader &); -+ LineReader(const LineReader&); - void operator=(const LineReader&); - - char *FindLineFeed() { -- return reinterpret_cast<char *>(memchr(bol_, '\n', static_cast<size_t>(eod_ - bol_))); -+ return reinterpret_cast<char*>( -+ memchr(bol_, '\n', static_cast<size_t>(eod_ - bol_))); - } - - bool BufferIsEmpty() { -@@ -483,7 +492,8 @@ static char *GetHex(const char *start, const char *end, uint64_t *hex) { - int ch = *p; - if ((ch >= '0' && ch <= '9') || - (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f')) { -- *hex = (*hex << 4U) | (ch < 'A' ? static_cast<uint64_t>(ch - '0') : (ch & 0xF) + 9U); -+ *hex = (*hex << 4U) | -+ (ch < 'A' ? static_cast<uint64_t>(ch - '0') : (ch & 0xF) + 9U); - } else { // Encountered the first non-hex character. - break; - } -@@ -647,12 +657,11 @@ static int OpenObjectFileContainingPcAndGetStartAddressNoHook( - } - } - --int OpenObjectFileContainingPcAndGetStartAddress( -- uint64_t pc, -- uint64_t& start_address, -- uint64_t& base_address, -- char* out_file_name, -- size_t out_file_name_size) { -+int OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc, -+ uint64_t& start_address, -+ uint64_t& base_address, -+ char* out_file_name, -+ size_t out_file_name_size) { - if (g_symbolize_open_object_file_callback) { - return g_symbolize_open_object_file_callback( - pc, start_address, base_address, out_file_name, out_file_name_size); -@@ -668,7 +677,11 @@ int OpenObjectFileContainingPcAndGetStartAddress( - // bytes. Output will be truncated as needed, and a NUL character is always - // appended. - // NOTE: code from sandbox/linux/seccomp-bpf/demo.cc. --static char *itoa_r(uintptr_t i, char *buf, size_t sz, unsigned base, size_t padding) { -+static char* itoa_r(uintptr_t i, -+ char* buf, -+ size_t sz, -+ unsigned base, -+ size_t padding) { - // Make sure we can write at least one NUL byte. - size_t n = 1; - if (n > sz) { -@@ -745,7 +758,8 @@ static void SafeAppendHexNumber(uint64_t value, char* dest, size_t dest_size) { - // and "out" is used as its output. - // To keep stack consumption low, we would like this function to not - // get inlined. --static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out, -+static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void* pc, -+ char* out, - size_t out_size) { - uint64_t pc0 = reinterpret_cast<uintptr_t>(pc); - uint64_t start_address = 0; -@@ -822,14 +836,16 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out, - - _END_GOOGLE_NAMESPACE_ - --#elif (defined(GLOG_OS_MACOSX) || defined(GLOG_OS_EMSCRIPTEN)) && defined(HAVE_DLADDR) -+#elif (defined(GLOG_OS_MACOSX) || defined(GLOG_OS_EMSCRIPTEN)) && \ -+ defined(HAVE_DLADDR) - - #include <dlfcn.h> - #include <cstring> - - _START_GOOGLE_NAMESPACE_ - --static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out, -+static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void* pc, -+ char* out, - size_t out_size) { - Dl_info info; - if (dladdr(pc, &info)) { -@@ -883,7 +899,8 @@ private: - SymInitializer& operator=(const SymInitializer&); - }; - --static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out, -+static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void* pc, -+ char* out, - size_t out_size) { - const static SymInitializer symInitializer; - if (!symInitializer.ready) { -@@ -918,7 +935,7 @@ _END_GOOGLE_NAMESPACE_ - - _START_GOOGLE_NAMESPACE_ - --bool Symbolize(void *pc, char *out, size_t out_size) { -+bool Symbolize(void* pc, char* out, size_t out_size) { - return SymbolizeAndDemangle(pc, out, out_size); - } - -diff --git a/base/third_party/symbolize/symbolize.h b/base/third_party/symbolize/symbolize.h -index 64ff0509ce57d..987569fdde67f 100644 ---- a/base/third_party/symbolize/symbolize.h -+++ b/base/third_party/symbolize/symbolize.h -@@ -127,7 +127,7 @@ ATTRIBUTE_NOINLINE int OpenObjectFileContainingPcAndGetStartAddress( - - _END_GOOGLE_NAMESPACE_ - --#endif /* __ELF__ */ -+#endif /* __ELF__ */ - - _START_GOOGLE_NAMESPACE_ - -@@ -140,7 +140,7 @@ struct FileDescriptor { - int get() { return fd_; } - - private: -- FileDescriptor(const FileDescriptor &); -+ FileDescriptor(const FileDescriptor&); - void operator=(const FileDescriptor&); - }; - -diff --git a/base/third_party/symbolize/utilities.h b/base/third_party/symbolize/utilities.h -index 8c61380fad159..bb206a8020315 100644 ---- a/base/third_party/symbolize/utilities.h -+++ b/base/third_party/symbolize/utilities.h -@@ -35,13 +35,13 @@ - #define UTILITIES_H__ - - #ifdef HAVE___ATTRIBUTE__ --# define ATTRIBUTE_NOINLINE __attribute__ ((noinline)) --# define HAVE_ATTRIBUTE_NOINLINE -+#define ATTRIBUTE_NOINLINE __attribute__((noinline)) -+#define HAVE_ATTRIBUTE_NOINLINE - #elif defined(GLOG_OS_WINDOWS) --# define ATTRIBUTE_NOINLINE __declspec(noinline) --# define HAVE_ATTRIBUTE_NOINLINE -+#define ATTRIBUTE_NOINLINE __declspec(noinline) -+#define HAVE_ATTRIBUTE_NOINLINE - #else --# define ATTRIBUTE_NOINLINE -+#define ATTRIBUTE_NOINLINE - #endif - - #endif // UTILITIES_H__
diff --git a/base/third_party/symbolize/patches/009-clone-absl-demangle.patch b/base/third_party/symbolize/patches/009-clone-absl-demangle.patch new file mode 100644 index 0000000..3adae98 --- /dev/null +++ b/base/third_party/symbolize/patches/009-clone-absl-demangle.patch
@@ -0,0 +1,2388 @@ +diff --git a/base/third_party/symbolize/demangle.cc b/base/third_party/symbolize/demangle.cc +index 9276c5b879a8c..2632646dd4072 100644 +--- a/base/third_party/symbolize/demangle.cc ++++ b/base/third_party/symbolize/demangle.cc +@@ -34,13 +34,14 @@ + // + // Note that we only have partial C++0x support yet. + +-#include <cstdio> // for NULL +- + #include "demangle.h" +-#include "utilities.h" + + #if defined(GLOG_OS_WINDOWS) + #include <dbghelp.h> ++#else ++#include <cstdint> ++#include <cstdio> ++#include <limits> + #endif + + _START_GOOGLE_NAMESPACE_ +@@ -49,117 +50,199 @@ _START_GOOGLE_NAMESPACE_ + typedef struct { + const char *abbrev; + const char *real_name; ++ // Number of arguments in <expression> context, or 0 if disallowed. ++ int arity; + } AbbrevPair; + + // List of operators from Itanium C++ ABI. + static const AbbrevPair kOperatorList[] = { +- { "nw", "new" }, +- { "na", "new[]" }, +- { "dl", "delete" }, +- { "da", "delete[]" }, +- { "ps", "+" }, +- { "ng", "-" }, +- { "ad", "&" }, +- { "de", "*" }, +- { "co", "~" }, +- { "pl", "+" }, +- { "mi", "-" }, +- { "ml", "*" }, +- { "dv", "/" }, +- { "rm", "%" }, +- { "an", "&" }, +- { "or", "|" }, +- { "eo", "^" }, +- { "aS", "=" }, +- { "pL", "+=" }, +- { "mI", "-=" }, +- { "mL", "*=" }, +- { "dV", "/=" }, +- { "rM", "%=" }, +- { "aN", "&=" }, +- { "oR", "|=" }, +- { "eO", "^=" }, +- { "ls", "<<" }, +- { "rs", ">>" }, +- { "lS", "<<=" }, +- { "rS", ">>=" }, +- { "eq", "==" }, +- { "ne", "!=" }, +- { "lt", "<" }, +- { "gt", ">" }, +- { "le", "<=" }, +- { "ge", ">=" }, +- { "nt", "!" }, +- { "aa", "&&" }, +- { "oo", "||" }, +- { "pp", "++" }, +- { "mm", "--" }, +- { "cm", "," }, +- { "pm", "->*" }, +- { "pt", "->" }, +- { "cl", "()" }, +- { "ix", "[]" }, +- { "qu", "?" }, +- { "st", "sizeof" }, +- { "sz", "sizeof" }, +- { NULL, NULL }, ++ // New has special syntax (not currently supported). ++ {"nw", "new", 0}, ++ {"na", "new[]", 0}, ++ ++ // Works except that the 'gs' prefix is not supported. ++ {"dl", "delete", 1}, ++ {"da", "delete[]", 1}, ++ ++ {"ps", "+", 1}, // "positive" ++ {"ng", "-", 1}, // "negative" ++ {"ad", "&", 1}, // "address-of" ++ {"de", "*", 1}, // "dereference" ++ {"co", "~", 1}, ++ ++ {"pl", "+", 2}, ++ {"mi", "-", 2}, ++ {"ml", "*", 2}, ++ {"dv", "/", 2}, ++ {"rm", "%", 2}, ++ {"an", "&", 2}, ++ {"or", "|", 2}, ++ {"eo", "^", 2}, ++ {"aS", "=", 2}, ++ {"pL", "+=", 2}, ++ {"mI", "-=", 2}, ++ {"mL", "*=", 2}, ++ {"dV", "/=", 2}, ++ {"rM", "%=", 2}, ++ {"aN", "&=", 2}, ++ {"oR", "|=", 2}, ++ {"eO", "^=", 2}, ++ {"ls", "<<", 2}, ++ {"rs", ">>", 2}, ++ {"lS", "<<=", 2}, ++ {"rS", ">>=", 2}, ++ {"eq", "==", 2}, ++ {"ne", "!=", 2}, ++ {"lt", "<", 2}, ++ {"gt", ">", 2}, ++ {"le", "<=", 2}, ++ {"ge", ">=", 2}, ++ {"nt", "!", 1}, ++ {"aa", "&&", 2}, ++ {"oo", "||", 2}, ++ {"pp", "++", 1}, ++ {"mm", "--", 1}, ++ {"cm", ",", 2}, ++ {"pm", "->*", 2}, ++ {"pt", "->", 0}, // Special syntax ++ {"cl", "()", 0}, // Special syntax ++ {"ix", "[]", 2}, ++ {"qu", "?", 3}, ++ {"st", "sizeof", 0}, // Special syntax ++ {"sz", "sizeof", 1}, // Not a real operator name, but used in expressions. ++ {nullptr, nullptr, 0}, + }; + + // List of builtin types from Itanium C++ ABI. ++// ++// Invariant: only one- or two-character type abbreviations here. + static const AbbrevPair kBuiltinTypeList[] = { +- { "v", "void" }, +- { "w", "wchar_t" }, +- { "b", "bool" }, +- { "c", "char" }, +- { "a", "signed char" }, +- { "h", "unsigned char" }, +- { "s", "short" }, +- { "t", "unsigned short" }, +- { "i", "int" }, +- { "j", "unsigned int" }, +- { "l", "long" }, +- { "m", "unsigned long" }, +- { "x", "long long" }, +- { "y", "unsigned long long" }, +- { "n", "__int128" }, +- { "o", "unsigned __int128" }, +- { "f", "float" }, +- { "d", "double" }, +- { "e", "long double" }, +- { "g", "__float128" }, +- { "z", "ellipsis" }, +- { NULL, NULL } ++ {"v", "void", 0}, ++ {"w", "wchar_t", 0}, ++ {"b", "bool", 0}, ++ {"c", "char", 0}, ++ {"a", "signed char", 0}, ++ {"h", "unsigned char", 0}, ++ {"s", "short", 0}, ++ {"t", "unsigned short", 0}, ++ {"i", "int", 0}, ++ {"j", "unsigned int", 0}, ++ {"l", "long", 0}, ++ {"m", "unsigned long", 0}, ++ {"x", "long long", 0}, ++ {"y", "unsigned long long", 0}, ++ {"n", "__int128", 0}, ++ {"o", "unsigned __int128", 0}, ++ {"f", "float", 0}, ++ {"d", "double", 0}, ++ {"e", "long double", 0}, ++ {"g", "__float128", 0}, ++ {"z", "ellipsis", 0}, ++ ++ {"De", "decimal128", 0}, // IEEE 754r decimal floating point (128 bits) ++ {"Dd", "decimal64", 0}, // IEEE 754r decimal floating point (64 bits) ++ {"Dc", "decltype(auto)", 0}, ++ {"Da", "auto", 0}, ++ {"Dn", "std::nullptr_t", 0}, // i.e., decltype(nullptr) ++ {"Df", "decimal32", 0}, // IEEE 754r decimal floating point (32 bits) ++ {"Di", "char32_t", 0}, ++ {"Du", "char8_t", 0}, ++ {"Ds", "char16_t", 0}, ++ {"Dh", "float16", 0}, // IEEE 754r half-precision float (16 bits) ++ {nullptr, nullptr, 0}, + }; + + // List of substitutions Itanium C++ ABI. + static const AbbrevPair kSubstitutionList[] = { +- { "St", "" }, +- { "Sa", "allocator" }, +- { "Sb", "basic_string" }, +- // std::basic_string<char, std::char_traits<char>,std::allocator<char> > +- { "Ss", "string"}, +- // std::basic_istream<char, std::char_traits<char> > +- { "Si", "istream" }, +- // std::basic_ostream<char, std::char_traits<char> > +- { "So", "ostream" }, +- // std::basic_iostream<char, std::char_traits<char> > +- { "Sd", "iostream" }, +- { NULL, NULL } ++ {"St", "", 0}, ++ {"Sa", "allocator", 0}, ++ {"Sb", "basic_string", 0}, ++ // std::basic_string<char, std::char_traits<char>,std::allocator<char> > ++ {"Ss", "string", 0}, ++ // std::basic_istream<char, std::char_traits<char> > ++ {"Si", "istream", 0}, ++ // std::basic_ostream<char, std::char_traits<char> > ++ {"So", "ostream", 0}, ++ // std::basic_iostream<char, std::char_traits<char> > ++ {"Sd", "iostream", 0}, ++ {nullptr, nullptr, 0}, + }; + +-// State needed for demangling. ++// State needed for demangling. This struct is copied in almost every stack ++// frame, so every byte counts. ++typedef struct { ++ int mangled_idx; // Cursor of mangled name. ++ int out_cur_idx; // Cursor of output string. ++ int prev_name_idx; // For constructors/destructors. ++ unsigned int prev_name_length : 16; // For constructors/destructors. ++ signed int nest_level : 15; // For nested names. ++ unsigned int append : 1; // Append flag. ++ // Note: for some reason MSVC can't pack "bool append : 1" into the same int ++ // with the above two fields, so we use an int instead. Amusingly it can pack ++ // "signed bool" as expected, but relying on that to continue to be a legal ++ // type seems ill-advised (as it's illegal in at least clang). ++} ParseState; ++ ++static_assert(sizeof(ParseState) == 4 * sizeof(int), ++ "unexpected size of ParseState"); ++ ++// One-off state for demangling that's not subject to backtracking -- either ++// constant data, data that's intentionally immune to backtracking (steps), or ++// data that would never be changed by backtracking anyway (recursion_depth). ++// ++// Only one copy of this exists for each call to Demangle, so the size of this ++// struct is nearly inconsequential. + typedef struct { +- const char *mangled_cur; // Cursor of mangled name. +- char *out_cur; // Cursor of output string. +- const char *out_begin; // Beginning of output string. +- const char *out_end; // End of output string. +- const char *prev_name; // For constructors/destructors. +- ssize_t prev_name_length; // For constructors/destructors. +- short nest_level; // For nested names. +- bool append; // Append flag. +- bool overflowed; // True if output gets overflowed. ++ const char *mangled_begin; // Beginning of input string. ++ char *out; // Beginning of output string. ++ int out_end_idx; // One past last allowed output character. ++ int recursion_depth; // For stack exhaustion prevention. ++ int steps; // Cap how much work we'll do, regardless of depth. ++ ParseState parse_state; // Backtrackable state copied for most frames. + } State; + ++namespace { ++// Prevent deep recursion / stack exhaustion. ++// Also prevent unbounded handling of complex inputs. ++class ComplexityGuard { ++ public: ++ explicit ComplexityGuard(State *state) : state_(state) { ++ ++state->recursion_depth; ++ ++state->steps; ++ } ++ ~ComplexityGuard() { --state_->recursion_depth; } ++ ++ // 256 levels of recursion seems like a reasonable upper limit on depth. ++ // 128 is not enough to demagle synthetic tests from demangle_unittest.txt: ++ // "_ZaaZZZZ..." and "_ZaaZcvZcvZ..." ++ static constexpr int kRecursionDepthLimit = 256; ++ ++ // We're trying to pick a charitable upper-limit on how many parse steps are ++ // necessary to handle something that a human could actually make use of. ++ // This is mostly in place as a bound on how much work we'll do if we are ++ // asked to demangle an mangled name from an untrusted source, so it should be ++ // much larger than the largest expected symbol, but much smaller than the ++ // amount of work we can do in, e.g., a second. ++ // ++ // Some real-world symbols from an arbitrary binary started failing between ++ // 2^12 and 2^13, so we multiply the latter by an extra factor of 16 to set ++ // the limit. ++ // ++ // Spending one second on 2^17 parse steps would require each step to take ++ // 7.6us, or ~30000 clock cycles, so it's safe to say this can be done in ++ // under a second. ++ static constexpr int kParseStepsLimit = 1 << 17; ++ ++ bool IsTooComplex() const { ++ return state_->recursion_depth > kRecursionDepthLimit || ++ state_->steps > kParseStepsLimit; ++ } ++ ++ private: ++ State *state_; ++}; ++} // namespace ++ + // We don't use strlen() in libc since it's not guaranteed to be async + // signal safe. + static size_t StrLen(const char *str) { +@@ -172,8 +255,8 @@ static size_t StrLen(const char *str) { + } + + // Returns true if "str" has at least "n" characters remaining. +-static bool AtLeastNumCharsRemaining(const char *str, ssize_t n) { +- for (ssize_t i = 0; i < n; ++i) { ++static bool AtLeastNumCharsRemaining(const char *str, size_t n) { ++ for (size_t i = 0; i < n; ++i) { + if (str[i] == '\0') { + return false; + } +@@ -184,32 +267,42 @@ static bool AtLeastNumCharsRemaining(const char *str, ssize_t n) { + // Returns true if "str" has "prefix" as a prefix. + static bool StrPrefix(const char *str, const char *prefix) { + size_t i = 0; +- while (str[i] != '\0' && prefix[i] != '\0' && +- str[i] == prefix[i]) { ++ while (str[i] != '\0' && prefix[i] != '\0' && str[i] == prefix[i]) { + ++i; + } + return prefix[i] == '\0'; // Consumed everything in "prefix". + } + +-static void InitState(State *state, const char *mangled, +- char *out, size_t out_size) { +- state->mangled_cur = mangled; +- state->out_cur = out; +- state->out_begin = out; +- state->out_end = out + out_size; +- state->prev_name = NULL; +- state->prev_name_length = -1; +- state->nest_level = -1; +- state->append = true; +- state->overflowed = false; ++static void InitState(State* state, ++ const char* mangled, ++ char* out, ++ size_t out_size) { ++ state->mangled_begin = mangled; ++ state->out = out; ++ state->out_end_idx = static_cast<int>(out_size); ++ state->recursion_depth = 0; ++ state->steps = 0; ++ ++ state->parse_state.mangled_idx = 0; ++ state->parse_state.out_cur_idx = 0; ++ state->parse_state.prev_name_idx = 0; ++ state->parse_state.prev_name_length = 0; ++ state->parse_state.nest_level = -1; ++ state->parse_state.append = true; ++} ++ ++static inline const char *RemainingInput(State *state) { ++ return &state->mangled_begin[state->parse_state.mangled_idx]; + } + +-// Returns true and advances "mangled_cur" if we find "one_char_token" +-// at "mangled_cur" position. It is assumed that "one_char_token" does ++// Returns true and advances "mangled_idx" if we find "one_char_token" ++// at "mangled_idx" position. It is assumed that "one_char_token" does + // not contain '\0'. + static bool ParseOneCharToken(State *state, const char one_char_token) { +- if (state->mangled_cur[0] == one_char_token) { +- ++state->mangled_cur; ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; ++ if (RemainingInput(state)[0] == one_char_token) { ++ ++state->parse_state.mangled_idx; + return true; + } + return false; +@@ -219,9 +312,11 @@ static bool ParseOneCharToken(State *state, const char one_char_token) { + // at "mangled_cur" position. It is assumed that "two_char_token" does + // not contain '\0'. + static bool ParseTwoCharToken(State *state, const char *two_char_token) { +- if (state->mangled_cur[0] == two_char_token[0] && +- state->mangled_cur[1] == two_char_token[1]) { +- state->mangled_cur += 2; ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; ++ if (RemainingInput(state)[0] == two_char_token[0] && ++ RemainingInput(state)[1] == two_char_token[1]) { ++ state->parse_state.mangled_idx += 2; + return true; + } + return false; +@@ -230,21 +325,35 @@ static bool ParseTwoCharToken(State *state, const char *two_char_token) { + // Returns true and advances "mangled_cur" if we find any character in + // "char_class" at "mangled_cur" position. + static bool ParseCharClass(State *state, const char *char_class) { ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; ++ if (RemainingInput(state)[0] == '\0') { ++ return false; ++ } + const char *p = char_class; + for (; *p != '\0'; ++p) { +- if (state->mangled_cur[0] == *p) { +- ++state->mangled_cur; ++ if (RemainingInput(state)[0] == *p) { ++ ++state->parse_state.mangled_idx; + return true; + } + } + return false; + } + +-// This function is used for handling an optional non-terminal. +-static bool Optional(bool) { +- return true; ++static bool ParseDigit(State *state, int *digit) { ++ char c = RemainingInput(state)[0]; ++ if (ParseCharClass(state, "0123456789")) { ++ if (digit != nullptr) { ++ *digit = c - '0'; ++ } ++ return true; ++ } ++ return false; + } + ++// This function is used for handling an optional non-terminal. ++static bool Optional(bool /*status*/) { return true; } ++ + // This function is used for handling <non-terminal>+ syntax. + typedef bool (*ParseFunc)(State *); + static bool OneOrMore(ParseFunc parse_func, State *state) { +@@ -266,146 +375,179 @@ static bool ZeroOrMore(ParseFunc parse_func, State *state) { + return true; + } + +-// Append "str" at "out_cur". If there is an overflow, "overflowed" +-// is set to true for later use. The output string is ensured to ++// Append "str" at "out_cur_idx". If there is an overflow, out_cur_idx is ++// set to out_end_idx+1. The output string is ensured to + // always terminate with '\0' as long as there is no overflow. +-static void Append(State *state, const char * const str, ssize_t length) { +- for (ssize_t i = 0; i < length; ++i) { +- if (state->out_cur + 1 < state->out_end) { // +1 for '\0' +- *state->out_cur = str[i]; +- ++state->out_cur; ++static void Append(State *state, const char *const str, const size_t length) { ++ for (size_t i = 0; i < length; ++i) { ++ if (state->parse_state.out_cur_idx + 1 < ++ state->out_end_idx) { // +1 for '\0' ++ state->out[state->parse_state.out_cur_idx++] = str[i]; + } else { +- state->overflowed = true; ++ // signal overflow ++ state->parse_state.out_cur_idx = state->out_end_idx + 1; + break; + } + } +- if (!state->overflowed) { +- *state->out_cur = '\0'; // Terminate it with '\0' ++ if (state->parse_state.out_cur_idx < state->out_end_idx) { ++ state->out[state->parse_state.out_cur_idx] = ++ '\0'; // Terminate it with '\0' + } + } + + // We don't use equivalents in libc to avoid locale issues. +-static bool IsLower(char c) { +- return c >= 'a' && c <= 'z'; +-} ++static bool IsLower(char c) { return c >= 'a' && c <= 'z'; } + + static bool IsAlpha(char c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); + } + +-static bool IsDigit(char c) { +- return c >= '0' && c <= '9'; +-} ++static bool IsDigit(char c) { return c >= '0' && c <= '9'; } + + // Returns true if "str" is a function clone suffix. These suffixes are used +-// by GCC 4.5.x and later versions to indicate functions which have been +-// cloned during optimization. We treat any sequence (.<alpha>+.<digit>+)+ as +-// a function clone suffix. ++// by GCC 4.5.x and later versions (and our locally-modified version of GCC ++// 4.4.x) to indicate functions which have been cloned during optimization. ++// We treat any sequence (.<alpha>+.<digit>+)+ as a function clone suffix. ++// Additionally, '_' is allowed along with the alphanumeric sequence. + static bool IsFunctionCloneSuffix(const char *str) { + size_t i = 0; + while (str[i] != '\0') { +- // Consume a single .<alpha>+.<digit>+ sequence. +- if (str[i] != '.' || !IsAlpha(str[i + 1])) { +- return false; ++ bool parsed = false; ++ // Consume a single [.<alpha> | _]*[.<digit>]* sequence. ++ if (str[i] == '.' && (IsAlpha(str[i + 1]) || str[i + 1] == '_')) { ++ parsed = true; ++ i += 2; ++ while (IsAlpha(str[i]) || str[i] == '_') { ++ ++i; ++ } + } +- i += 2; +- while (IsAlpha(str[i])) { +- ++i; ++ if (str[i] == '.' && IsDigit(str[i + 1])) { ++ parsed = true; ++ i += 2; ++ while (IsDigit(str[i])) { ++ ++i; ++ } + } +- if (str[i] != '.' || !IsDigit(str[i + 1])) { ++ if (!parsed) + return false; +- } +- i += 2; +- while (IsDigit(str[i])) { +- ++i; +- } + } + return true; // Consumed everything in "str". + } + ++static bool EndsWith(State *state, const char chr) { ++ return state->parse_state.out_cur_idx > 0 && ++ state->parse_state.out_cur_idx < state->out_end_idx && ++ chr == state->out[state->parse_state.out_cur_idx - 1]; ++} ++ + // Append "str" with some tweaks, iff "append" state is true. +-// Returns true so that it can be placed in "if" conditions. +-static void MaybeAppendWithLength(State *state, const char * const str, +- ssize_t length) { +- if (state->append && length > 0) { ++static void MaybeAppendWithLength(State *state, const char *const str, ++ const size_t length) { ++ if (state->parse_state.append && length > 0) { + // Append a space if the output buffer ends with '<' and "str" + // starts with '<' to avoid <<<. +- if (str[0] == '<' && state->out_begin < state->out_cur && +- state->out_cur[-1] == '<') { ++ if (str[0] == '<' && EndsWith(state, '<')) { + Append(state, " ", 1); + } +- // Remember the last identifier name for ctors/dtors. +- if (IsAlpha(str[0]) || str[0] == '_') { +- state->prev_name = state->out_cur; +- state->prev_name_length = length; ++ // Remember the last identifier name for ctors/dtors, ++ // but only if we haven't yet overflown the buffer. ++ if (state->parse_state.out_cur_idx < state->out_end_idx && ++ (IsAlpha(str[0]) || str[0] == '_')) { ++ state->parse_state.prev_name_idx = state->parse_state.out_cur_idx; ++ state->parse_state.prev_name_length = static_cast<unsigned int>(length); + } + Append(state, str, length); + } + } + +-// A convenient wrapper arount MaybeAppendWithLength(). +-static bool MaybeAppend(State *state, const char * const str) { +- if (state->append) { ++// Appends a positive decimal number to the output if appending is enabled. ++static bool MaybeAppendDecimal(State *state, int val) { ++ // Max {32-64}-bit unsigned int is 20 digits. ++ constexpr size_t kMaxLength = 20; ++ char buf[kMaxLength]; ++ ++ // We can't use itoa or sprintf as neither is specified to be ++ // async-signal-safe. ++ if (state->parse_state.append) { ++ // We can't have a one-before-the-beginning pointer, so instead start with ++ // one-past-the-end and manipulate one character before the pointer. ++ char *p = &buf[kMaxLength]; ++ do { // val=0 is the only input that should write a leading zero digit. ++ *--p = static_cast<char>((val % 10) + '0'); ++ val /= 10; ++ } while (p > buf && val != 0); ++ ++ // 'p' landed on the last character we set. How convenient. ++ Append(state, p, kMaxLength - static_cast<size_t>(p - buf)); ++ } ++ ++ return true; ++} ++ ++// A convenient wrapper around MaybeAppendWithLength(). ++// Returns true so that it can be placed in "if" conditions. ++static bool MaybeAppend(State *state, const char *const str) { ++ if (state->parse_state.append) { + size_t length = StrLen(str); +- MaybeAppendWithLength(state, str, static_cast<ssize_t>(length)); ++ MaybeAppendWithLength(state, str, length); + } + return true; + } + + // This function is used for handling nested names. + static bool EnterNestedName(State *state) { +- state->nest_level = 0; ++ state->parse_state.nest_level = 0; + return true; + } + + // This function is used for handling nested names. +-static bool LeaveNestedName(State *state, short prev_value) { +- state->nest_level = prev_value; ++static bool LeaveNestedName(State *state, int16_t prev_value) { ++ state->parse_state.nest_level = prev_value; + return true; + } + + // Disable the append mode not to print function parameters, etc. + static bool DisableAppend(State *state) { +- state->append = false; ++ state->parse_state.append = false; + return true; + } + + // Restore the append mode to the previous state. + static bool RestoreAppend(State *state, bool prev_value) { +- state->append = prev_value; ++ state->parse_state.append = prev_value; + return true; + } + + // Increase the nest level for nested names. + static void MaybeIncreaseNestLevel(State *state) { +- if (state->nest_level > -1) { +- ++state->nest_level; ++ if (state->parse_state.nest_level > -1) { ++ ++state->parse_state.nest_level; + } + } + + // Appends :: for nested names if necessary. + static void MaybeAppendSeparator(State *state) { +- if (state->nest_level >= 1) { ++ if (state->parse_state.nest_level >= 1) { + MaybeAppend(state, "::"); + } + } + + // Cancel the last separator if necessary. + static void MaybeCancelLastSeparator(State *state) { +- if (state->nest_level >= 1 && state->append && +- state->out_begin <= state->out_cur - 2) { +- state->out_cur -= 2; +- *state->out_cur = '\0'; ++ if (state->parse_state.nest_level >= 1 && state->parse_state.append && ++ state->parse_state.out_cur_idx >= 2) { ++ state->parse_state.out_cur_idx -= 2; ++ state->out[state->parse_state.out_cur_idx] = '\0'; + } + } + + // Returns true if the identifier of the given length pointed to by + // "mangled_cur" is anonymous namespace. +-static bool IdentifierIsAnonymousNamespace(State *state, ssize_t length) { ++static bool IdentifierIsAnonymousNamespace(State *state, size_t length) { ++ // Returns true if "anon_prefix" is a proper prefix of "mangled_cur". + static const char anon_prefix[] = "_GLOBAL__N_"; +- return (length > static_cast<ssize_t>(sizeof(anon_prefix)) - +- 1 && // Should be longer. +- StrPrefix(state->mangled_cur, anon_prefix)); ++ return (length > (sizeof(anon_prefix) - 1) && ++ StrPrefix(RemainingInput(state), anon_prefix)); + } + + // Forward declarations of our parsing functions. +@@ -413,24 +555,24 @@ static bool ParseMangledName(State *state); + static bool ParseEncoding(State *state); + static bool ParseName(State *state); + static bool ParseUnscopedName(State *state); +-static bool ParseUnscopedTemplateName(State *state); + static bool ParseNestedName(State *state); + static bool ParsePrefix(State *state); + static bool ParseUnqualifiedName(State *state); + static bool ParseSourceName(State *state); + static bool ParseLocalSourceName(State *state); ++static bool ParseUnnamedTypeName(State *state); + static bool ParseNumber(State *state, int *number_out); + static bool ParseFloatNumber(State *state); + static bool ParseSeqId(State *state); +-static bool ParseIdentifier(State *state, ssize_t length); +-static bool ParseAbiTags(State *state); +-static bool ParseAbiTag(State *state); +-static bool ParseOperatorName(State *state); ++static bool ParseIdentifier(State *state, size_t length); ++static bool ParseOperatorName(State *state, int *arity); + static bool ParseSpecialName(State *state); + static bool ParseCallOffset(State *state); + static bool ParseNVOffset(State *state); + static bool ParseVOffset(State *state); ++static bool ParseAbiTags(State *state); + static bool ParseCtorDtorName(State *state); ++static bool ParseDecltype(State *state); + static bool ParseType(State *state); + static bool ParseCVQualifiers(State *state); + static bool ParseBuiltinType(State *state); +@@ -443,11 +585,15 @@ static bool ParseTemplateParam(State *state); + static bool ParseTemplateTemplateParam(State *state); + static bool ParseTemplateArgs(State *state); + static bool ParseTemplateArg(State *state); ++static bool ParseBaseUnresolvedName(State *state); ++static bool ParseUnresolvedName(State *state); + static bool ParseExpression(State *state); + static bool ParseExprPrimary(State *state); ++static bool ParseExprCastValue(State *state); + static bool ParseLocalName(State *state); ++static bool ParseLocalNameSuffix(State *state); + static bool ParseDiscriminator(State *state); +-static bool ParseSubstitution(State *state); ++static bool ParseSubstitution(State *state, bool accept_std); + + // Implementation note: the following code is a straightforward + // translation of the Itanium C++ ABI defined in BNF with a couple of +@@ -459,11 +605,12 @@ static bool ParseSubstitution(State *state); + // - Reorder patterns to give greedier functions precedence + // We'll mark "Less greedy than" for these cases in the code + // +-// Each parsing function changes the state and returns true on +-// success. Otherwise, don't change the state and returns false. To +-// ensure that the state isn't changed in the latter case, we save the +-// original state before we call more than one parsing functions +-// consecutively with &&, and restore the state if unsuccessful. See ++// Each parsing function changes the parse state and returns true on ++// success, or returns false and doesn't change the parse state (note: ++// the parse-steps counter increases regardless of success or failure). ++// To ensure that the parse state isn't changed in the latter case, we ++// save the original state before we call multiple parsing functions ++// consecutively with &&, and restore it if unsuccessful. See + // ParseEncoding() as an example of this convention. We follow the + // convention throughout the code. + // +@@ -477,10 +624,12 @@ static bool ParseSubstitution(State *state); + // + // Reference: + // - Itanium C++ ABI +-// <http://www.codesourcery.com/cxx-abi/abi.html#mangling> ++// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling> + + // <mangled-name> ::= _Z <encoding> + static bool ParseMangledName(State *state) { ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; + return ParseTwoCharToken(state, "_Z") && ParseEncoding(state); + } + +@@ -488,13 +637,18 @@ static bool ParseMangledName(State *state) { + // ::= <(data) name> + // ::= <special-name> + static bool ParseEncoding(State *state) { +- State copy = *state; +- if (ParseName(state) && ParseBareFunctionType(state)) { ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; ++ // Implementing the first two productions together as <name> ++ // [<bare-function-type>] avoids exponential blowup of backtracking. ++ // ++ // Since Optional(...) can't fail, there's no need to copy the state for ++ // backtracking. ++ if (ParseName(state) && Optional(ParseBareFunctionType(state))) { + return true; + } +- *state = copy; + +- if (ParseName(state) || ParseSpecialName(state)) { ++ if (ParseSpecialName(state)) { + return true; + } + return false; +@@ -505,60 +659,73 @@ static bool ParseEncoding(State *state) { + // ::= <unscoped-name> + // ::= <local-name> + static bool ParseName(State *state) { ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; + if (ParseNestedName(state) || ParseLocalName(state)) { + return true; + } + +- State copy = *state; +- if (ParseUnscopedTemplateName(state) && ++ // We reorganize the productions to avoid re-parsing unscoped names. ++ // - Inline <unscoped-template-name> productions: ++ // <name> ::= <substitution> <template-args> ++ // ::= <unscoped-name> <template-args> ++ // ::= <unscoped-name> ++ // - Merge the two productions that start with unscoped-name: ++ // <name> ::= <unscoped-name> [<template-args>] ++ ++ ParseState copy = state->parse_state; ++ // "std<...>" isn't a valid name. ++ if (ParseSubstitution(state, /*accept_std=*/false) && + ParseTemplateArgs(state)) { + return true; + } +- *state = copy; ++ state->parse_state = copy; + +- // Less greedy than <unscoped-template-name> <template-args>. +- if (ParseUnscopedName(state)) { +- return true; +- } +- return false; ++ // Note there's no need to restore state after this since only the first ++ // subparser can fail. ++ return ParseUnscopedName(state) && Optional(ParseTemplateArgs(state)); + } + + // <unscoped-name> ::= <unqualified-name> + // ::= St <unqualified-name> + static bool ParseUnscopedName(State *state) { ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; + if (ParseUnqualifiedName(state)) { + return true; + } + +- State copy = *state; +- if (ParseTwoCharToken(state, "St") && +- MaybeAppend(state, "std::") && ++ ParseState copy = state->parse_state; ++ if (ParseTwoCharToken(state, "St") && MaybeAppend(state, "std::") && + ParseUnqualifiedName(state)) { + return true; + } +- *state = copy; ++ state->parse_state = copy; + return false; + } + +-// <unscoped-template-name> ::= <unscoped-name> +-// ::= <substitution> +-static bool ParseUnscopedTemplateName(State *state) { +- return ParseUnscopedName(state) || ParseSubstitution(state); ++// <ref-qualifer> ::= R // lvalue method reference qualifier ++// ::= O // rvalue method reference qualifier ++static inline bool ParseRefQualifier(State *state) { ++ return ParseCharClass(state, "OR"); + } + +-// <nested-name> ::= N [<CV-qualifiers>] <prefix> <unqualified-name> E +-// ::= N [<CV-qualifiers>] <template-prefix> <template-args> E ++// <nested-name> ::= N [<CV-qualifiers>] [<ref-qualifier>] <prefix> ++// <unqualified-name> E ++// ::= N [<CV-qualifiers>] [<ref-qualifier>] <template-prefix> ++// <template-args> E + static bool ParseNestedName(State *state) { +- State copy = *state; +- if (ParseOneCharToken(state, 'N') && +- EnterNestedName(state) && ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; ++ ParseState copy = state->parse_state; ++ if (ParseOneCharToken(state, 'N') && EnterNestedName(state) && + Optional(ParseCVQualifiers(state)) && +- ParsePrefix(state) && ++ Optional(ParseRefQualifier(state)) && ParsePrefix(state) && + LeaveNestedName(state, copy.nest_level) && + ParseOneCharToken(state, 'E')) { + return true; + } +- *state = copy; ++ state->parse_state = copy; + return false; + } + +@@ -574,12 +741,15 @@ static bool ParseNestedName(State *state) { + // ::= <template-param> + // ::= <substitution> + static bool ParsePrefix(State *state) { ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; + bool has_something = false; + while (true) { + MaybeAppendSeparator(state); + if (ParseTemplateParam(state) || +- ParseSubstitution(state) || +- ParseUnscopedName(state)) { ++ ParseSubstitution(state, /*accept_std=*/true) || ++ ParseUnscopedName(state) || ++ (ParseOneCharToken(state, 'M') && ParseUnnamedTypeName(state))) { + has_something = true; + MaybeIncreaseNestLevel(state); + continue; +@@ -594,40 +764,112 @@ static bool ParsePrefix(State *state) { + return true; + } + +-// <unqualified-name> ::= <operator-name> +-// ::= <ctor-dtor-name> ++// <unqualified-name> ::= <operator-name> [<abi-tags>] ++// ::= <ctor-dtor-name> [<abi-tags>] + // ::= <source-name> [<abi-tags>] + // ::= <local-source-name> [<abi-tags>] ++// ::= <unnamed-type-name> [<abi-tags>] ++// ++// <local-source-name> is a GCC extension; see below. + static bool ParseUnqualifiedName(State *state) { +- return (ParseOperatorName(state) || +- ParseCtorDtorName(state) || +- (ParseSourceName(state) && Optional(ParseAbiTags(state))) || +- (ParseLocalSourceName(state) && Optional(ParseAbiTags(state)))); ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; ++ if (ParseOperatorName(state, nullptr) || ParseCtorDtorName(state) || ++ ParseSourceName(state) || ParseLocalSourceName(state) || ++ ParseUnnamedTypeName(state)) { ++ return ParseAbiTags(state); ++ } ++ return false; ++} ++ ++// <abi-tags> ::= <abi-tag> [<abi-tags>] ++// <abi-tag> ::= B <source-name> ++static bool ParseAbiTags(State *state) { ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; ++ ++ while (ParseOneCharToken(state, 'B')) { ++ ParseState copy = state->parse_state; ++ MaybeAppend(state, "[abi:"); ++ ++ if (!ParseSourceName(state)) { ++ state->parse_state = copy; ++ return false; ++ } ++ MaybeAppend(state, "]"); ++ } ++ ++ return true; + } + + // <source-name> ::= <positive length number> <identifier> + static bool ParseSourceName(State *state) { +- State copy = *state; ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; ++ ParseState copy = state->parse_state; + int length = -1; +- if (ParseNumber(state, &length) && ParseIdentifier(state, length)) { ++ if (ParseNumber(state, &length) && ++ ParseIdentifier(state, static_cast<size_t>(length))) { + return true; + } +- *state = copy; ++ state->parse_state = copy; + return false; + } + + // <local-source-name> ::= L <source-name> [<discriminator>] + // + // References: +-// http://gcc.gnu.org/bugzilla/show_bug.cgi?id=31775 +-// http://gcc.gnu.org/viewcvs?view=rev&revision=124467 ++// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=31775 ++// https://gcc.gnu.org/viewcvs?view=rev&revision=124467 + static bool ParseLocalSourceName(State *state) { +- State copy = *state; ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; ++ ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'L') && ParseSourceName(state) && + Optional(ParseDiscriminator(state))) { + return true; + } +- *state = copy; ++ state->parse_state = copy; ++ return false; ++} ++ ++// <unnamed-type-name> ::= Ut [<(nonnegative) number>] _ ++// ::= <closure-type-name> ++// <closure-type-name> ::= Ul <lambda-sig> E [<(nonnegative) number>] _ ++// <lambda-sig> ::= <(parameter) type>+ ++static bool ParseUnnamedTypeName(State *state) { ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; ++ ParseState copy = state->parse_state; ++ // Type's 1-based index n is encoded as { "", n == 1; itoa(n-2), otherwise }. ++ // Optionally parse the encoded value into 'which' and add 2 to get the index. ++ int which = -1; ++ ++ // Unnamed type local to function or class. ++ if (ParseTwoCharToken(state, "Ut") && Optional(ParseNumber(state, &which)) && ++ which <= std::numeric_limits<int>::max() - 2 && // Don't overflow. ++ ParseOneCharToken(state, '_')) { ++ MaybeAppend(state, "{unnamed type#"); ++ MaybeAppendDecimal(state, 2 + which); ++ MaybeAppend(state, "}"); ++ return true; ++ } ++ state->parse_state = copy; ++ ++ // Closure type. ++ which = -1; ++ if (ParseTwoCharToken(state, "Ul") && DisableAppend(state) && ++ OneOrMore(ParseType, state) && RestoreAppend(state, copy.append) && ++ ParseOneCharToken(state, 'E') && Optional(ParseNumber(state, &which)) && ++ which <= std::numeric_limits<int>::max() - 2 && // Don't overflow. ++ ParseOneCharToken(state, '_')) { ++ MaybeAppend(state, "{lambda()#"); ++ MaybeAppendDecimal(state, 2 + which); ++ MaybeAppend(state, "}"); ++ return true; ++ } ++ state->parse_state = copy; ++ + return false; + } + +@@ -635,23 +877,32 @@ static bool ParseLocalSourceName(State *state) { + // If "number_out" is non-null, then *number_out is set to the value of the + // parsed number on success. + static bool ParseNumber(State *state, int *number_out) { +- int sign = 1; ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; ++ bool negative = false; + if (ParseOneCharToken(state, 'n')) { +- sign = -1; ++ negative = true; + } +- const char *p = state->mangled_cur; +- int number = 0; +- for (;*p != '\0'; ++p) { ++ const char *p = RemainingInput(state); ++ uint64_t number = 0; ++ for (; *p != '\0'; ++p) { + if (IsDigit(*p)) { +- number = number * 10 + (*p - '0'); ++ number = number * 10 + static_cast<uint64_t>(*p - '0'); + } else { + break; + } + } +- if (p != state->mangled_cur) { // Conversion succeeded. +- state->mangled_cur = p; +- if (number_out != NULL) { +- *number_out = number * sign; ++ // Apply the sign with uint64_t arithmetic so overflows aren't UB. Gives ++ // "incorrect" results for out-of-range inputs, but negative values only ++ // appear for literals, which aren't printed. ++ if (negative) { ++ number = ~number + 1; ++ } ++ if (p != RemainingInput(state)) { // Conversion succeeded. ++ state->parse_state.mangled_idx += p - RemainingInput(state); ++ if (number_out != nullptr) { ++ // Note: possibly truncate "number". ++ *number_out = static_cast<int>(number); + } + return true; + } +@@ -661,14 +912,16 @@ static bool ParseNumber(State *state, int *number_out) { + // Floating-point literals are encoded using a fixed-length lowercase + // hexadecimal string. + static bool ParseFloatNumber(State *state) { +- const char *p = state->mangled_cur; +- for (;*p != '\0'; ++p) { ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; ++ const char *p = RemainingInput(state); ++ for (; *p != '\0'; ++p) { + if (!IsDigit(*p) && !(*p >= 'a' && *p <= 'f')) { + break; + } + } +- if (p != state->mangled_cur) { // Conversion succeeded. +- state->mangled_cur = p; ++ if (p != RemainingInput(state)) { // Conversion succeeded. ++ state->parse_state.mangled_idx += p - RemainingInput(state); + return true; + } + return false; +@@ -677,93 +930,85 @@ static bool ParseFloatNumber(State *state) { + // The <seq-id> is a sequence number in base 36, + // using digits and upper case letters + static bool ParseSeqId(State *state) { +- const char *p = state->mangled_cur; +- for (;*p != '\0'; ++p) { ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; ++ const char *p = RemainingInput(state); ++ for (; *p != '\0'; ++p) { + if (!IsDigit(*p) && !(*p >= 'A' && *p <= 'Z')) { + break; + } + } +- if (p != state->mangled_cur) { // Conversion succeeded. +- state->mangled_cur = p; ++ if (p != RemainingInput(state)) { // Conversion succeeded. ++ state->parse_state.mangled_idx += p - RemainingInput(state); + return true; + } + return false; + } + + // <identifier> ::= <unqualified source code identifier> (of given length) +-static bool ParseIdentifier(State *state, ssize_t length) { +- if (length == -1 || +- !AtLeastNumCharsRemaining(state->mangled_cur, length)) { ++static bool ParseIdentifier(State *state, size_t length) { ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; ++ if (!AtLeastNumCharsRemaining(RemainingInput(state), length)) { + return false; + } + if (IdentifierIsAnonymousNamespace(state, length)) { + MaybeAppend(state, "(anonymous namespace)"); + } else { +- MaybeAppendWithLength(state, state->mangled_cur, length); ++ MaybeAppendWithLength(state, RemainingInput(state), length); + } +- state->mangled_cur += length; ++ state->parse_state.mangled_idx += length; + return true; + } + +-// <abi-tags> ::= <abi-tag> [<abi-tags>] +-static bool ParseAbiTags(State *state) { +- State copy = *state; +- DisableAppend(state); +- if (OneOrMore(ParseAbiTag, state)) { +- RestoreAppend(state, copy.append); +- return true; +- } +- *state = copy; +- return false; +-} +- +-// <abi-tag> ::= B <source-name> +-static bool ParseAbiTag(State *state) { +- return ParseOneCharToken(state, 'B') && ParseSourceName(state); +-} +- + // <operator-name> ::= nw, and other two letters cases + // ::= cv <type> # (cast) + // ::= v <digit> <source-name> # vendor extended operator +-static bool ParseOperatorName(State *state) { +- if (!AtLeastNumCharsRemaining(state->mangled_cur, 2)) { ++static bool ParseOperatorName(State *state, int *arity) { ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; ++ if (!AtLeastNumCharsRemaining(RemainingInput(state), 2)) { + return false; + } + // First check with "cv" (cast) case. +- State copy = *state; +- if (ParseTwoCharToken(state, "cv") && +- MaybeAppend(state, "operator ") && +- EnterNestedName(state) && +- ParseType(state) && ++ ParseState copy = state->parse_state; ++ if (ParseTwoCharToken(state, "cv") && MaybeAppend(state, "operator ") && ++ EnterNestedName(state) && ParseType(state) && + LeaveNestedName(state, copy.nest_level)) { ++ if (arity != nullptr) { ++ *arity = 1; ++ } + return true; + } +- *state = copy; ++ state->parse_state = copy; + + // Then vendor extended operators. +- if (ParseOneCharToken(state, 'v') && ParseCharClass(state, "0123456789") && ++ if (ParseOneCharToken(state, 'v') && ParseDigit(state, arity) && + ParseSourceName(state)) { + return true; + } +- *state = copy; ++ state->parse_state = copy; + + // Other operator names should start with a lower alphabet followed + // by a lower/upper alphabet. +- if (!(IsLower(state->mangled_cur[0]) && +- IsAlpha(state->mangled_cur[1]))) { ++ if (!(IsLower(RemainingInput(state)[0]) && ++ IsAlpha(RemainingInput(state)[1]))) { + return false; + } + // We may want to perform a binary search if we really need speed. + const AbbrevPair *p; +- for (p = kOperatorList; p->abbrev != NULL; ++p) { +- if (state->mangled_cur[0] == p->abbrev[0] && +- state->mangled_cur[1] == p->abbrev[1]) { ++ for (p = kOperatorList; p->abbrev != nullptr; ++p) { ++ if (RemainingInput(state)[0] == p->abbrev[0] && ++ RemainingInput(state)[1] == p->abbrev[1]) { ++ if (arity != nullptr) { ++ *arity = p->arity; ++ } + MaybeAppend(state, "operator"); + if (IsLower(*p->real_name)) { // new, delete, etc. + MaybeAppend(state, " "); + } + MaybeAppend(state, p->real_name); +- state->mangled_cur += 2; ++ state->parse_state.mangled_idx += 2; + return true; + } + } +@@ -774,6 +1019,7 @@ static bool ParseOperatorName(State *state) { + // ::= TT <type> + // ::= TI <type> + // ::= TS <type> ++// ::= TH <type> # thread-local + // ::= Tc <call-offset> <call-offset> <(base) encoding> + // ::= GV <(object) name> + // ::= T <call-offset> <(base) encoding> +@@ -789,123 +1035,156 @@ static bool ParseOperatorName(State *state) { + // Note: we don't care much about them since they don't appear in + // stack traces. The are special data. + static bool ParseSpecialName(State *state) { +- State copy = *state; +- if (ParseOneCharToken(state, 'T') && +- ParseCharClass(state, "VTIS") && ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; ++ ParseState copy = state->parse_state; ++ if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "VTISH") && + ParseType(state)) { + return true; + } +- *state = copy; ++ state->parse_state = copy; + + if (ParseTwoCharToken(state, "Tc") && ParseCallOffset(state) && + ParseCallOffset(state) && ParseEncoding(state)) { + return true; + } +- *state = copy; ++ state->parse_state = copy; + +- if (ParseTwoCharToken(state, "GV") && +- ParseName(state)) { ++ if (ParseTwoCharToken(state, "GV") && ParseName(state)) { + return true; + } +- *state = copy; ++ state->parse_state = copy; + + if (ParseOneCharToken(state, 'T') && ParseCallOffset(state) && + ParseEncoding(state)) { + return true; + } +- *state = copy; ++ state->parse_state = copy; + + // G++ extensions + if (ParseTwoCharToken(state, "TC") && ParseType(state) && +- ParseNumber(state, NULL) && ParseOneCharToken(state, '_') && +- DisableAppend(state) && +- ParseType(state)) { ++ ParseNumber(state, nullptr) && ParseOneCharToken(state, '_') && ++ DisableAppend(state) && ParseType(state)) { + RestoreAppend(state, copy.append); + return true; + } +- *state = copy; ++ state->parse_state = copy; + + if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "FJ") && + ParseType(state)) { + return true; + } +- *state = copy; ++ state->parse_state = copy; + + if (ParseTwoCharToken(state, "GR") && ParseName(state)) { + return true; + } +- *state = copy; ++ state->parse_state = copy; + + if (ParseTwoCharToken(state, "GA") && ParseEncoding(state)) { + return true; + } +- *state = copy; ++ state->parse_state = copy; + + if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "hv") && + ParseCallOffset(state) && ParseEncoding(state)) { + return true; + } +- *state = copy; ++ state->parse_state = copy; + return false; + } + + // <call-offset> ::= h <nv-offset> _ + // ::= v <v-offset> _ + static bool ParseCallOffset(State *state) { +- State copy = *state; +- if (ParseOneCharToken(state, 'h') && +- ParseNVOffset(state) && ParseOneCharToken(state, '_')) { ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; ++ ParseState copy = state->parse_state; ++ if (ParseOneCharToken(state, 'h') && ParseNVOffset(state) && ++ ParseOneCharToken(state, '_')) { + return true; + } +- *state = copy; ++ state->parse_state = copy; + +- if (ParseOneCharToken(state, 'v') && +- ParseVOffset(state) && ParseOneCharToken(state, '_')) { ++ if (ParseOneCharToken(state, 'v') && ParseVOffset(state) && ++ ParseOneCharToken(state, '_')) { + return true; + } +- *state = copy; ++ state->parse_state = copy; + + return false; + } + + // <nv-offset> ::= <(offset) number> + static bool ParseNVOffset(State *state) { +- return ParseNumber(state, NULL); ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; ++ return ParseNumber(state, nullptr); + } + + // <v-offset> ::= <(offset) number> _ <(virtual offset) number> + static bool ParseVOffset(State *state) { +- State copy = *state; +- if (ParseNumber(state, NULL) && ParseOneCharToken(state, '_') && +- ParseNumber(state, NULL)) { ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; ++ ParseState copy = state->parse_state; ++ if (ParseNumber(state, nullptr) && ParseOneCharToken(state, '_') && ++ ParseNumber(state, nullptr)) { + return true; + } +- *state = copy; ++ state->parse_state = copy; + return false; + } + +-// <ctor-dtor-name> ::= C1 | C2 | C3 ++// <ctor-dtor-name> ::= C1 | C2 | C3 | CI1 <base-class-type> | CI2 ++// <base-class-type> + // ::= D0 | D1 | D2 ++// # GCC extensions: "unified" constructor/destructor. See ++// # ++// https://github.com/gcc-mirror/gcc/blob/7ad17b583c3643bd4557f29b8391ca7ef08391f5/gcc/cp/mangle.c#L1847 ++// ::= C4 | D4 + static bool ParseCtorDtorName(State *state) { +- State copy = *state; +- if (ParseOneCharToken(state, 'C') && +- ParseCharClass(state, "123")) { +- const char * const prev_name = state->prev_name; +- const ssize_t prev_name_length = state->prev_name_length; +- MaybeAppendWithLength(state, prev_name, prev_name_length); +- return true; ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; ++ ParseState copy = state->parse_state; ++ if (ParseOneCharToken(state, 'C')) { ++ if (ParseCharClass(state, "1234")) { ++ const char *const prev_name = ++ state->out + state->parse_state.prev_name_idx; ++ MaybeAppendWithLength(state, prev_name, ++ state->parse_state.prev_name_length); ++ return true; ++ } else if (ParseOneCharToken(state, 'I') && ParseCharClass(state, "12") && ++ ParseClassEnumType(state)) { ++ return true; ++ } + } +- *state = copy; ++ state->parse_state = copy; + +- if (ParseOneCharToken(state, 'D') && +- ParseCharClass(state, "012")) { +- const char * const prev_name = state->prev_name; +- const ssize_t prev_name_length = state->prev_name_length; ++ if (ParseOneCharToken(state, 'D') && ParseCharClass(state, "0124")) { ++ const char *const prev_name = state->out + state->parse_state.prev_name_idx; + MaybeAppend(state, "~"); +- MaybeAppendWithLength(state, prev_name, prev_name_length); ++ MaybeAppendWithLength(state, prev_name, ++ state->parse_state.prev_name_length); + return true; + } +- *state = copy; ++ state->parse_state = copy; ++ return false; ++} ++ ++// <decltype> ::= Dt <expression> E # decltype of an id-expression or class ++// # member access (C++0x) ++// ::= DT <expression> E # decltype of an expression (C++0x) ++static bool ParseDecltype(State *state) { ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; ++ ++ ParseState copy = state->parse_state; ++ if (ParseOneCharToken(state, 'D') && ParseCharClass(state, "tT") && ++ ParseExpression(state) && ParseOneCharToken(state, 'E')) { ++ return true; ++ } ++ state->parse_state = copy; ++ + return false; + } + +@@ -918,67 +1197,87 @@ static bool ParseCtorDtorName(State *state) { + // ::= U <source-name> <type> # vendor extended type qualifier + // ::= <builtin-type> + // ::= <function-type> +-// ::= <class-enum-type> ++// ::= <class-enum-type> # note: just an alias for <name> + // ::= <array-type> + // ::= <pointer-to-member-type> + // ::= <template-template-param> <template-args> + // ::= <template-param> ++// ::= <decltype> + // ::= <substitution> + // ::= Dp <type> # pack expansion of (C++0x) +-// ::= Dt <expression> E # decltype of an id-expression or class +-// # member access (C++0x) +-// ::= DT <expression> E # decltype of an expression (C++0x) ++// ::= Dv <num-elems> _ # GNU vector extension + // + static bool ParseType(State *state) { ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; ++ ParseState copy = state->parse_state; ++ + // We should check CV-qualifers, and PRGC things first. +- State copy = *state; +- if (ParseCVQualifiers(state) && ParseType(state)) { +- return true; ++ // ++ // CV-qualifiers overlap with some operator names, but an operator name is not ++ // valid as a type. To avoid an ambiguity that can lead to exponential time ++ // complexity, refuse to backtrack the CV-qualifiers. ++ // ++ // _Z4aoeuIrMvvE ++ // => _Z 4aoeuI rM v v E ++ // aoeu<operator%=, void, void> ++ // => _Z 4aoeuI r Mv v E ++ // aoeu<void void::* restrict> ++ // ++ // By consuming the CV-qualifiers first, the former parse is disabled. ++ if (ParseCVQualifiers(state)) { ++ const bool result = ParseType(state); ++ if (!result) state->parse_state = copy; ++ return result; + } +- *state = copy; ++ state->parse_state = copy; + +- if (ParseCharClass(state, "OPRCG") && ParseType(state)) { +- return true; ++ // Similarly, these tag characters can overlap with other <name>s resulting in ++ // two different parse prefixes that land on <template-args> in the same ++ // place, such as "C3r1xI...". So, disable the "ctor-name = C3" parse by ++ // refusing to backtrack the tag characters. ++ if (ParseCharClass(state, "OPRCG")) { ++ const bool result = ParseType(state); ++ if (!result) state->parse_state = copy; ++ return result; + } +- *state = copy; ++ state->parse_state = copy; + + if (ParseTwoCharToken(state, "Dp") && ParseType(state)) { + return true; + } +- *state = copy; +- +- if (ParseOneCharToken(state, 'D') && ParseCharClass(state, "tT") && +- ParseExpression(state) && ParseOneCharToken(state, 'E')) { +- return true; +- } +- *state = copy; ++ state->parse_state = copy; + + if (ParseOneCharToken(state, 'U') && ParseSourceName(state) && + ParseType(state)) { + return true; + } +- *state = copy; ++ state->parse_state = copy; + +- if (ParseBuiltinType(state) || +- ParseFunctionType(state) || +- ParseClassEnumType(state) || +- ParseArrayType(state) || +- ParsePointerToMemberType(state) || +- ParseSubstitution(state)) { ++ if (ParseBuiltinType(state) || ParseFunctionType(state) || ++ ParseClassEnumType(state) || ParseArrayType(state) || ++ ParsePointerToMemberType(state) || ParseDecltype(state) || ++ // "std" on its own isn't a type. ++ ParseSubstitution(state, /*accept_std=*/false)) { + return true; + } + +- if (ParseTemplateTemplateParam(state) && +- ParseTemplateArgs(state)) { ++ if (ParseTemplateTemplateParam(state) && ParseTemplateArgs(state)) { + return true; + } +- *state = copy; ++ state->parse_state = copy; + + // Less greedy than <template-template-param> <template-args>. + if (ParseTemplateParam(state)) { + return true; + } + ++ if (ParseTwoCharToken(state, "Dv") && ParseNumber(state, nullptr) && ++ ParseOneCharToken(state, '_')) { ++ return true; ++ } ++ state->parse_state = copy; ++ + return false; + } + +@@ -986,6 +1285,8 @@ static bool ParseType(State *state) { + // We don't allow empty <CV-qualifiers> to avoid infinite loop in + // ParseType(). + static bool ParseCVQualifiers(State *state) { ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; + int num_cv_qualifiers = 0; + num_cv_qualifiers += ParseOneCharToken(state, 'r'); + num_cv_qualifiers += ParseOneCharToken(state, 'V'); +@@ -993,208 +1294,499 @@ static bool ParseCVQualifiers(State *state) { + return num_cv_qualifiers > 0; + } + +-// <builtin-type> ::= v, etc. ++// <builtin-type> ::= v, etc. # single-character builtin types + // ::= u <source-name> ++// ::= Dd, etc. # two-character builtin types ++// ++// Not supported: ++// ::= DF <number> _ # _FloatN (N bits) ++// + static bool ParseBuiltinType(State *state) { ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; + const AbbrevPair *p; +- for (p = kBuiltinTypeList; p->abbrev != NULL; ++p) { +- if (state->mangled_cur[0] == p->abbrev[0]) { ++ for (p = kBuiltinTypeList; p->abbrev != nullptr; ++p) { ++ // Guaranteed only 1- or 2-character strings in kBuiltinTypeList. ++ if (p->abbrev[1] == '\0') { ++ if (ParseOneCharToken(state, p->abbrev[0])) { ++ MaybeAppend(state, p->real_name); ++ return true; ++ } ++ } else if (p->abbrev[2] == '\0' && ParseTwoCharToken(state, p->abbrev)) { + MaybeAppend(state, p->real_name); +- ++state->mangled_cur; + return true; + } + } + +- State copy = *state; ++ ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'u') && ParseSourceName(state)) { + return true; + } +- *state = copy; ++ state->parse_state = copy; + return false; + } + +-// <function-type> ::= F [Y] <bare-function-type> E ++// <exception-spec> ::= Do # non-throwing ++// exception-specification (e.g., ++// noexcept, throw()) ++// ::= DO <expression> E # computed (instantiation-dependent) ++// noexcept ++// ::= Dw <type>+ E # dynamic exception specification ++// with instantiation-dependent types ++static bool ParseExceptionSpec(State *state) { ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; ++ ++ if (ParseTwoCharToken(state, "Do")) return true; ++ ++ ParseState copy = state->parse_state; ++ if (ParseTwoCharToken(state, "DO") && ParseExpression(state) && ++ ParseOneCharToken(state, 'E')) { ++ return true; ++ } ++ state->parse_state = copy; ++ if (ParseTwoCharToken(state, "Dw") && OneOrMore(ParseType, state) && ++ ParseOneCharToken(state, 'E')) { ++ return true; ++ } ++ state->parse_state = copy; ++ ++ return false; ++} ++ ++// <function-type> ::= [exception-spec] F [Y] <bare-function-type> [O] E + static bool ParseFunctionType(State *state) { +- State copy = *state; +- if (ParseOneCharToken(state, 'F') && +- Optional(ParseOneCharToken(state, 'Y')) && +- ParseBareFunctionType(state) && ParseOneCharToken(state, 'E')) { ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; ++ ParseState copy = state->parse_state; ++ if (Optional(ParseExceptionSpec(state)) && ParseOneCharToken(state, 'F') && ++ Optional(ParseOneCharToken(state, 'Y')) && ParseBareFunctionType(state) && ++ Optional(ParseOneCharToken(state, 'O')) && ++ ParseOneCharToken(state, 'E')) { + return true; + } +- *state = copy; ++ state->parse_state = copy; + return false; + } + + // <bare-function-type> ::= <(signature) type>+ + static bool ParseBareFunctionType(State *state) { +- State copy = *state; ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; ++ ParseState copy = state->parse_state; + DisableAppend(state); + if (OneOrMore(ParseType, state)) { + RestoreAppend(state, copy.append); + MaybeAppend(state, "()"); + return true; + } +- *state = copy; ++ state->parse_state = copy; + return false; + } + + // <class-enum-type> ::= <name> + static bool ParseClassEnumType(State *state) { ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; + return ParseName(state); + } + + // <array-type> ::= A <(positive dimension) number> _ <(element) type> + // ::= A [<(dimension) expression>] _ <(element) type> + static bool ParseArrayType(State *state) { +- State copy = *state; +- if (ParseOneCharToken(state, 'A') && ParseNumber(state, NULL) && ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; ++ ParseState copy = state->parse_state; ++ if (ParseOneCharToken(state, 'A') && ParseNumber(state, nullptr) && + ParseOneCharToken(state, '_') && ParseType(state)) { + return true; + } +- *state = copy; ++ state->parse_state = copy; + + if (ParseOneCharToken(state, 'A') && Optional(ParseExpression(state)) && + ParseOneCharToken(state, '_') && ParseType(state)) { + return true; + } +- *state = copy; ++ state->parse_state = copy; + return false; + } + + // <pointer-to-member-type> ::= M <(class) type> <(member) type> + static bool ParsePointerToMemberType(State *state) { +- State copy = *state; +- if (ParseOneCharToken(state, 'M') && ParseType(state) && +- ParseType(state)) { ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; ++ ParseState copy = state->parse_state; ++ if (ParseOneCharToken(state, 'M') && ParseType(state) && ParseType(state)) { + return true; + } +- *state = copy; ++ state->parse_state = copy; + return false; + } + + // <template-param> ::= T_ + // ::= T <parameter-2 non-negative number> _ + static bool ParseTemplateParam(State *state) { ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; + if (ParseTwoCharToken(state, "T_")) { + MaybeAppend(state, "?"); // We don't support template substitutions. + return true; + } + +- State copy = *state; +- if (ParseOneCharToken(state, 'T') && ParseNumber(state, NULL) && ++ ParseState copy = state->parse_state; ++ if (ParseOneCharToken(state, 'T') && ParseNumber(state, nullptr) && + ParseOneCharToken(state, '_')) { + MaybeAppend(state, "?"); // We don't support template substitutions. + return true; + } +- *state = copy; ++ state->parse_state = copy; + return false; + } + +- + // <template-template-param> ::= <template-param> + // ::= <substitution> + static bool ParseTemplateTemplateParam(State *state) { ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; + return (ParseTemplateParam(state) || +- ParseSubstitution(state)); ++ // "std" on its own isn't a template. ++ ParseSubstitution(state, /*accept_std=*/false)); + } + + // <template-args> ::= I <template-arg>+ E + static bool ParseTemplateArgs(State *state) { +- State copy = *state; ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; ++ ParseState copy = state->parse_state; + DisableAppend(state); +- if (ParseOneCharToken(state, 'I') && +- OneOrMore(ParseTemplateArg, state) && ++ if (ParseOneCharToken(state, 'I') && OneOrMore(ParseTemplateArg, state) && + ParseOneCharToken(state, 'E')) { + RestoreAppend(state, copy.append); + MaybeAppend(state, "<>"); + return true; + } +- *state = copy; ++ state->parse_state = copy; + return false; + } + + // <template-arg> ::= <type> + // ::= <expr-primary> +-// ::= I <template-arg>* E # argument pack + // ::= J <template-arg>* E # argument pack + // ::= X <expression> E + static bool ParseTemplateArg(State *state) { +- State copy = *state; +- if ((ParseOneCharToken(state, 'I') || ParseOneCharToken(state, 'J')) && +- ZeroOrMore(ParseTemplateArg, state) && ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; ++ ParseState copy = state->parse_state; ++ if (ParseOneCharToken(state, 'J') && ZeroOrMore(ParseTemplateArg, state) && + ParseOneCharToken(state, 'E')) { + return true; + } +- *state = copy; ++ state->parse_state = copy; ++ ++ // There can be significant overlap between the following leading to ++ // exponential backtracking: ++ // ++ // <expr-primary> ::= L <type> <expr-cast-value> E ++ // e.g. L 2xxIvE 1 E ++ // <type> ==> <local-source-name> <template-args> ++ // e.g. L 2xx IvE ++ // ++ // This means parsing an entire <type> twice, and <type> can contain ++ // <template-arg>, so this can generate exponential backtracking. There is ++ // only overlap when the remaining input starts with "L <source-name>", so ++ // parse all cases that can start this way jointly to share the common prefix. ++ // ++ // We have: ++ // ++ // <template-arg> ::= <type> ++ // ::= <expr-primary> ++ // ++ // First, drop all the productions of <type> that must start with something ++ // other than 'L'. All that's left is <class-enum-type>; inline it. ++ // ++ // <type> ::= <nested-name> # starts with 'N' ++ // ::= <unscoped-name> ++ // ::= <unscoped-template-name> <template-args> ++ // ::= <local-name> # starts with 'Z' ++ // ++ // Drop and inline again: ++ // ++ // <type> ::= <unscoped-name> ++ // ::= <unscoped-name> <template-args> ++ // ::= <substitution> <template-args> # starts with 'S' ++ // ++ // Merge the first two, inline <unscoped-name>, drop last: ++ // ++ // <type> ::= <unqualified-name> [<template-args>] ++ // ::= St <unqualified-name> [<template-args>] # starts with 'S' ++ // ++ // Drop and inline: ++ // ++ // <type> ::= <operator-name> [<template-args>] # starts with lowercase ++ // ::= <ctor-dtor-name> [<template-args>] # starts with 'C' or 'D' ++ // ::= <source-name> [<template-args>] # starts with digit ++ // ::= <local-source-name> [<template-args>] ++ // ::= <unnamed-type-name> [<template-args>] # starts with 'U' ++ // ++ // One more time: ++ // ++ // <type> ::= L <source-name> [<template-args>] ++ // ++ // Likewise with <expr-primary>: ++ // ++ // <expr-primary> ::= L <type> <expr-cast-value> E ++ // ::= LZ <encoding> E # cannot overlap; drop ++ // ::= L <mangled_name> E # cannot overlap; drop ++ // ++ // By similar reasoning as shown above, the only <type>s starting with ++ // <source-name> are "<source-name> [<template-args>]". Inline this. ++ // ++ // <expr-primary> ::= L <source-name> [<template-args>] <expr-cast-value> E ++ // ++ // Now inline both of these into <template-arg>: ++ // ++ // <template-arg> ::= L <source-name> [<template-args>] ++ // ::= L <source-name> [<template-args>] <expr-cast-value> E ++ // ++ // Merge them and we're done: ++ // <template-arg> ++ // ::= L <source-name> [<template-args>] [<expr-cast-value> E] ++ if (ParseLocalSourceName(state) && Optional(ParseTemplateArgs(state))) { ++ copy = state->parse_state; ++ if (ParseExprCastValue(state) && ParseOneCharToken(state, 'E')) { ++ return true; ++ } ++ state->parse_state = copy; ++ return true; ++ } + +- if (ParseType(state) || +- ParseExprPrimary(state)) { ++ // Now that the overlapping cases can't reach this code, we can safely call ++ // both of these. ++ if (ParseType(state) || ParseExprPrimary(state)) { + return true; + } +- *state = copy; ++ state->parse_state = copy; + + if (ParseOneCharToken(state, 'X') && ParseExpression(state) && + ParseOneCharToken(state, 'E')) { + return true; + } +- *state = copy; ++ state->parse_state = copy; + return false; + } + +-// <expression> ::= <template-param> +-// ::= <expr-primary> +-// ::= <unary operator-name> <expression> +-// ::= <binary operator-name> <expression> <expression> +-// ::= <trinary operator-name> <expression> <expression> +-// <expression> ++// <unresolved-type> ::= <template-param> [<template-args>] ++// ::= <decltype> ++// ::= <substitution> ++static inline bool ParseUnresolvedType(State *state) { ++ // No ComplexityGuard because we don't copy the state in this stack frame. ++ return (ParseTemplateParam(state) && Optional(ParseTemplateArgs(state))) || ++ ParseDecltype(state) || ParseSubstitution(state, /*accept_std=*/false); ++} ++ ++// <simple-id> ::= <source-name> [<template-args>] ++static inline bool ParseSimpleId(State *state) { ++ // No ComplexityGuard because we don't copy the state in this stack frame. ++ ++ // Note: <simple-id> cannot be followed by a parameter pack; see comment in ++ // ParseUnresolvedType. ++ return ParseSourceName(state) && Optional(ParseTemplateArgs(state)); ++} ++ ++// <base-unresolved-name> ::= <source-name> [<template-args>] ++// ::= on <operator-name> [<template-args>] ++// ::= dn <destructor-name> ++static bool ParseBaseUnresolvedName(State *state) { ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; ++ ++ if (ParseSimpleId(state)) { ++ return true; ++ } ++ ++ ParseState copy = state->parse_state; ++ if (ParseTwoCharToken(state, "on") && ParseOperatorName(state, nullptr) && ++ Optional(ParseTemplateArgs(state))) { ++ return true; ++ } ++ state->parse_state = copy; ++ ++ if (ParseTwoCharToken(state, "dn") && ++ (ParseUnresolvedType(state) || ParseSimpleId(state))) { ++ return true; ++ } ++ state->parse_state = copy; ++ ++ return false; ++} ++ ++// <unresolved-name> ::= [gs] <base-unresolved-name> ++// ::= sr <unresolved-type> <base-unresolved-name> ++// ::= srN <unresolved-type> <unresolved-qualifier-level>+ E ++// <base-unresolved-name> ++// ::= [gs] sr <unresolved-qualifier-level>+ E ++// <base-unresolved-name> ++static bool ParseUnresolvedName(State *state) { ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; ++ ++ ParseState copy = state->parse_state; ++ if (Optional(ParseTwoCharToken(state, "gs")) && ++ ParseBaseUnresolvedName(state)) { ++ return true; ++ } ++ state->parse_state = copy; ++ ++ if (ParseTwoCharToken(state, "sr") && ParseUnresolvedType(state) && ++ ParseBaseUnresolvedName(state)) { ++ return true; ++ } ++ state->parse_state = copy; ++ ++ if (ParseTwoCharToken(state, "sr") && ParseOneCharToken(state, 'N') && ++ ParseUnresolvedType(state) && ++ OneOrMore(/* <unresolved-qualifier-level> ::= */ ParseSimpleId, state) && ++ ParseOneCharToken(state, 'E') && ParseBaseUnresolvedName(state)) { ++ return true; ++ } ++ state->parse_state = copy; ++ ++ if (Optional(ParseTwoCharToken(state, "gs")) && ++ ParseTwoCharToken(state, "sr") && ++ OneOrMore(/* <unresolved-qualifier-level> ::= */ ParseSimpleId, state) && ++ ParseOneCharToken(state, 'E') && ParseBaseUnresolvedName(state)) { ++ return true; ++ } ++ state->parse_state = copy; ++ ++ return false; ++} ++ ++// <expression> ::= <1-ary operator-name> <expression> ++// ::= <2-ary operator-name> <expression> <expression> ++// ::= <3-ary operator-name> <expression> <expression> <expression> ++// ::= cl <expression>+ E ++// ::= cp <simple-id> <expression>* E # Clang-specific. ++// ::= cv <type> <expression> # type (expression) ++// ::= cv <type> _ <expression>* E # type (expr-list) + // ::= st <type> ++// ::= <template-param> ++// ::= <function-param> ++// ::= <expr-primary> ++// ::= dt <expression> <unresolved-name> # expr.name ++// ::= pt <expression> <unresolved-name> # expr->name ++// ::= sp <expression> # argument pack expansion + // ::= sr <type> <unqualified-name> <template-args> + // ::= sr <type> <unqualified-name> ++// <function-param> ::= fp <(top-level) CV-qualifiers> _ ++// ::= fp <(top-level) CV-qualifiers> <number> _ ++// ::= fL <number> p <(top-level) CV-qualifiers> _ ++// ::= fL <number> p <(top-level) CV-qualifiers> <number> _ + static bool ParseExpression(State *state) { ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; + if (ParseTemplateParam(state) || ParseExprPrimary(state)) { + return true; + } + +- State copy = *state; +- if (ParseOperatorName(state) && +- ParseExpression(state) && +- ParseExpression(state) && +- ParseExpression(state)) { ++ ParseState copy = state->parse_state; ++ ++ // Object/function call expression. ++ if (ParseTwoCharToken(state, "cl") && OneOrMore(ParseExpression, state) && ++ ParseOneCharToken(state, 'E')) { + return true; + } +- *state = copy; ++ state->parse_state = copy; + +- if (ParseOperatorName(state) && +- ParseExpression(state) && +- ParseExpression(state)) { ++ // Clang-specific "cp <simple-id> <expression>* E" ++ // https://clang.llvm.org/doxygen/ItaniumMangle_8cpp_source.html#l04338 ++ if (ParseTwoCharToken(state, "cp") && ParseSimpleId(state) && ++ ZeroOrMore(ParseExpression, state) && ParseOneCharToken(state, 'E')) { + return true; + } +- *state = copy; ++ state->parse_state = copy; + +- if (ParseOperatorName(state) && +- ParseExpression(state)) { ++ // Function-param expression (level 0). ++ if (ParseTwoCharToken(state, "fp") && Optional(ParseCVQualifiers(state)) && ++ Optional(ParseNumber(state, nullptr)) && ParseOneCharToken(state, '_')) { + return true; + } +- *state = copy; ++ state->parse_state = copy; + ++ // Function-param expression (level 1+). ++ if (ParseTwoCharToken(state, "fL") && Optional(ParseNumber(state, nullptr)) && ++ ParseOneCharToken(state, 'p') && Optional(ParseCVQualifiers(state)) && ++ Optional(ParseNumber(state, nullptr)) && ParseOneCharToken(state, '_')) { ++ return true; ++ } ++ state->parse_state = copy; ++ ++ // Parse the conversion expressions jointly to avoid re-parsing the <type> in ++ // their common prefix. Parsed as: ++ // <expression> ::= cv <type> <conversion-args> ++ // <conversion-args> ::= _ <expression>* E ++ // ::= <expression> ++ // ++ // Also don't try ParseOperatorName after seeing "cv", since ParseOperatorName ++ // also needs to accept "cv <type>" in other contexts. ++ if (ParseTwoCharToken(state, "cv")) { ++ if (ParseType(state)) { ++ ParseState copy2 = state->parse_state; ++ if (ParseOneCharToken(state, '_') && ZeroOrMore(ParseExpression, state) && ++ ParseOneCharToken(state, 'E')) { ++ return true; ++ } ++ state->parse_state = copy2; ++ if (ParseExpression(state)) { ++ return true; ++ } ++ } ++ } else { ++ // Parse unary, binary, and ternary operator expressions jointly, taking ++ // care not to re-parse subexpressions repeatedly. Parse like: ++ // <expression> ::= <operator-name> <expression> ++ // [<one-to-two-expressions>] ++ // <one-to-two-expressions> ::= <expression> [<expression>] ++ int arity = -1; ++ if (ParseOperatorName(state, &arity) && ++ arity > 0 && // 0 arity => disabled. ++ (arity < 3 || ParseExpression(state)) && ++ (arity < 2 || ParseExpression(state)) && ++ (arity < 1 || ParseExpression(state))) { ++ return true; ++ } ++ } ++ state->parse_state = copy; ++ ++ // sizeof type + if (ParseTwoCharToken(state, "st") && ParseType(state)) { + return true; + } +- *state = copy; ++ state->parse_state = copy; + +- if (ParseTwoCharToken(state, "sr") && ParseType(state) && +- ParseUnqualifiedName(state) && +- ParseTemplateArgs(state)) { ++ // Object and pointer member access expressions. ++ if ((ParseTwoCharToken(state, "dt") || ParseTwoCharToken(state, "pt")) && ++ ParseExpression(state) && ParseType(state)) { + return true; + } +- *state = copy; ++ state->parse_state = copy; + +- if (ParseTwoCharToken(state, "sr") && ParseType(state) && +- ParseUnqualifiedName(state)) { ++ // Pointer-to-member access expressions. This parses the same as a binary ++ // operator, but it's implemented separately because "ds" shouldn't be ++ // accepted in other contexts that parse an operator name. ++ if (ParseTwoCharToken(state, "ds") && ParseExpression(state) && ++ ParseExpression(state)) { + return true; + } +- *state = copy; +- return false; ++ state->parse_state = copy; ++ ++ // Parameter pack expansion ++ if (ParseTwoCharToken(state, "sp") && ParseExpression(state)) { ++ return true; ++ } ++ state->parse_state = copy; ++ ++ return ParseUnresolvedName(state); + } + + // <expr-primary> ::= L <type> <(value) number> E +@@ -1202,116 +1794,194 @@ static bool ParseExpression(State *state) { + // ::= L <mangled-name> E + // // A bug in g++'s C++ ABI version 2 (-fabi-version=2). + // ::= LZ <encoding> E ++// ++// Warning, subtle: the "bug" LZ production above is ambiguous with the first ++// production where <type> starts with <local-name>, which can lead to ++// exponential backtracking in two scenarios: ++// ++// - When whatever follows the E in the <local-name> in the first production is ++// not a name, we backtrack the whole <encoding> and re-parse the whole thing. ++// ++// - When whatever follows the <local-name> in the first production is not a ++// number and this <expr-primary> may be followed by a name, we backtrack the ++// <name> and re-parse it. ++// ++// Moreover this ambiguity isn't always resolved -- for example, the following ++// has two different parses: ++// ++// _ZaaILZ4aoeuE1x1EvE ++// => operator&&<aoeu, x, E, void> ++// => operator&&<(aoeu::x)(1), void> ++// ++// To resolve this, we just do what GCC's demangler does, and refuse to parse ++// casts to <local-name> types. + static bool ParseExprPrimary(State *state) { +- State copy = *state; +- if (ParseOneCharToken(state, 'L') && ParseType(state) && +- ParseNumber(state, NULL) && +- ParseOneCharToken(state, 'E')) { +- return true; ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; ++ ParseState copy = state->parse_state; ++ ++ // The "LZ" special case: if we see LZ, we commit to accept "LZ <encoding> E" ++ // or fail, no backtracking. ++ if (ParseTwoCharToken(state, "LZ")) { ++ if (ParseEncoding(state) && ParseOneCharToken(state, 'E')) { ++ return true; ++ } ++ ++ state->parse_state = copy; ++ return false; + } +- *state = copy; + ++ // The merged cast production. + if (ParseOneCharToken(state, 'L') && ParseType(state) && +- ParseFloatNumber(state) && +- ParseOneCharToken(state, 'E')) { ++ ParseExprCastValue(state)) { + return true; + } +- *state = copy; ++ state->parse_state = copy; + + if (ParseOneCharToken(state, 'L') && ParseMangledName(state) && + ParseOneCharToken(state, 'E')) { + return true; + } +- *state = copy; ++ state->parse_state = copy; + +- if (ParseTwoCharToken(state, "LZ") && ParseEncoding(state) && +- ParseOneCharToken(state, 'E')) { ++ return false; ++} ++ ++// <number> or <float>, followed by 'E', as described above ParseExprPrimary. ++static bool ParseExprCastValue(State *state) { ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; ++ // We have to be able to backtrack after accepting a number because we could ++ // have e.g. "7fffE", which will accept "7" as a number but then fail to find ++ // the 'E'. ++ ParseState copy = state->parse_state; ++ if (ParseNumber(state, nullptr) && ParseOneCharToken(state, 'E')) { + return true; + } +- *state = copy; ++ state->parse_state = copy; ++ ++ if (ParseFloatNumber(state) && ParseOneCharToken(state, 'E')) { ++ return true; ++ } ++ state->parse_state = copy; + + return false; + } + +-// <local-name> := Z <(function) encoding> E <(entity) name> +-// [<discriminator>] +-// := Z <(function) encoding> E s [<discriminator>] +-static bool ParseLocalName(State *state) { +- State copy = *state; +- if (ParseOneCharToken(state, 'Z') && ParseEncoding(state) && +- ParseOneCharToken(state, 'E') && MaybeAppend(state, "::") && +- ParseName(state) && Optional(ParseDiscriminator(state))) { ++// <local-name> ::= Z <(function) encoding> E <(entity) name> [<discriminator>] ++// ::= Z <(function) encoding> E s [<discriminator>] ++// ++// Parsing a common prefix of these two productions together avoids an ++// exponential blowup of backtracking. Parse like: ++// <local-name> := Z <encoding> E <local-name-suffix> ++// <local-name-suffix> ::= s [<discriminator>] ++// ::= <name> [<discriminator>] ++ ++static bool ParseLocalNameSuffix(State *state) { ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; ++ ++ if (MaybeAppend(state, "::") && ParseName(state) && ++ Optional(ParseDiscriminator(state))) { + return true; + } +- *state = copy; + ++ // Since we're not going to overwrite the above "::" by re-parsing the ++ // <encoding> (whose trailing '\0' byte was in the byte now holding the ++ // first ':'), we have to rollback the "::" if the <name> parse failed. ++ if (state->parse_state.append) { ++ state->out[state->parse_state.out_cur_idx - 2] = '\0'; ++ } ++ ++ return ParseOneCharToken(state, 's') && Optional(ParseDiscriminator(state)); ++} ++ ++static bool ParseLocalName(State *state) { ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; ++ ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'Z') && ParseEncoding(state) && +- ParseTwoCharToken(state, "Es") && Optional(ParseDiscriminator(state))) { ++ ParseOneCharToken(state, 'E') && ParseLocalNameSuffix(state)) { + return true; + } +- *state = copy; ++ state->parse_state = copy; + return false; + } + + // <discriminator> := _ <(non-negative) number> + static bool ParseDiscriminator(State *state) { +- State copy = *state; +- if (ParseOneCharToken(state, '_') && ParseNumber(state, NULL)) { ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; ++ ParseState copy = state->parse_state; ++ if (ParseOneCharToken(state, '_') && ParseNumber(state, nullptr)) { + return true; + } +- *state = copy; ++ state->parse_state = copy; + return false; + } + + // <substitution> ::= S_ + // ::= S <seq-id> _ + // ::= St, etc. +-static bool ParseSubstitution(State *state) { ++// ++// "St" is special in that it's not valid as a standalone name, and it *is* ++// allowed to precede a name without being wrapped in "N...E". This means that ++// if we accept it on its own, we can accept "St1a" and try to parse ++// template-args, then fail and backtrack, accept "St" on its own, then "1a" as ++// an unqualified name and re-parse the same template-args. To block this ++// exponential backtracking, we disable it with 'accept_std=false' in ++// problematic contexts. ++static bool ParseSubstitution(State *state, bool accept_std) { ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; + if (ParseTwoCharToken(state, "S_")) { + MaybeAppend(state, "?"); // We don't support substitutions. + return true; + } + +- State copy = *state; ++ ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'S') && ParseSeqId(state) && + ParseOneCharToken(state, '_')) { + MaybeAppend(state, "?"); // We don't support substitutions. + return true; + } +- *state = copy; ++ state->parse_state = copy; + + // Expand abbreviations like "St" => "std". + if (ParseOneCharToken(state, 'S')) { + const AbbrevPair *p; +- for (p = kSubstitutionList; p->abbrev != NULL; ++p) { +- if (state->mangled_cur[0] == p->abbrev[1]) { ++ for (p = kSubstitutionList; p->abbrev != nullptr; ++p) { ++ if (RemainingInput(state)[0] == p->abbrev[1] && ++ (accept_std || p->abbrev[1] != 't')) { + MaybeAppend(state, "std"); + if (p->real_name[0] != '\0') { + MaybeAppend(state, "::"); + MaybeAppend(state, p->real_name); + } +- ++state->mangled_cur; ++ ++state->parse_state.mangled_idx; + return true; + } + } + } +- *state = copy; ++ state->parse_state = copy; + return false; + } + + // Parse <mangled-name>, optionally followed by either a function-clone suffix + // or version suffix. Returns true only if all of "mangled_cur" was consumed. + static bool ParseTopLevelMangledName(State *state) { ++ ComplexityGuard guard(state); ++ if (guard.IsTooComplex()) return false; + if (ParseMangledName(state)) { +- if (state->mangled_cur[0] != '\0') { ++ if (RemainingInput(state)[0] != '\0') { + // Drop trailing function clone suffix, if any. +- if (IsFunctionCloneSuffix(state->mangled_cur)) { ++ if (IsFunctionCloneSuffix(RemainingInput(state))) { + return true; + } + // Append trailing version suffix if any. + // ex. _Z3foo@@GLIBCXX_3.4 +- if (state->mangled_cur[0] == '@') { +- MaybeAppend(state, state->mangled_cur); ++ if (RemainingInput(state)[0] == '@') { ++ MaybeAppend(state, RemainingInput(state)); + return true; + } + return false; // Unconsumed suffix. +@@ -1320,6 +1990,10 @@ static bool ParseTopLevelMangledName(State *state) { + } + return false; + } ++ ++static bool Overflowed(const State *state) { ++ return state->parse_state.out_cur_idx >= state->out_end_idx; ++} + #endif + + // The demangler entry point. +@@ -1356,7 +2030,8 @@ bool Demangle(const char *mangled, char *out, size_t out_size) { + #else + State state; + InitState(&state, mangled, out, out_size); +- return ParseTopLevelMangledName(&state) && !state.overflowed; ++ return ParseTopLevelMangledName(&state) && !Overflowed(&state) && ++ state.parse_state.out_cur_idx > 0; + #endif + } + +diff --git a/base/third_party/symbolize/demangle.h b/base/third_party/symbolize/demangle.h +index 416f7ee153560..26e821a53c2cb 100644 +--- a/base/third_party/symbolize/demangle.h ++++ b/base/third_party/symbolize/demangle.h +@@ -70,6 +70,8 @@ + #ifndef BASE_DEMANGLE_H_ + #define BASE_DEMANGLE_H_ + ++#include <stddef.h> ++ + #include "config.h" + #include "glog/logging.h" +
diff --git a/base/third_party/symbolize/patches/010-clang-format.patch b/base/third_party/symbolize/patches/010-clang-format.patch new file mode 100644 index 0000000..3e786f0 --- /dev/null +++ b/base/third_party/symbolize/patches/010-clang-format.patch
@@ -0,0 +1,1184 @@ +diff --git a/base/third_party/symbolize/demangle.cc b/base/third_party/symbolize/demangle.cc +index 2632646dd4072..8db75f01071e2 100644 +--- a/base/third_party/symbolize/demangle.cc ++++ b/base/third_party/symbolize/demangle.cc +@@ -139,8 +139,8 @@ static const AbbrevPair kBuiltinTypeList[] = { + {"g", "__float128", 0}, + {"z", "ellipsis", 0}, + +- {"De", "decimal128", 0}, // IEEE 754r decimal floating point (128 bits) +- {"Dd", "decimal64", 0}, // IEEE 754r decimal floating point (64 bits) ++ {"De", "decimal128", 0}, // IEEE 754r decimal floating point (128 bits) ++ {"Dd", "decimal64", 0}, // IEEE 754r decimal floating point (64 bits) + {"Dc", "decltype(auto)", 0}, + {"Da", "auto", 0}, + {"Dn", "std::nullptr_t", 0}, // i.e., decltype(nullptr) +@@ -148,7 +148,7 @@ static const AbbrevPair kBuiltinTypeList[] = { + {"Di", "char32_t", 0}, + {"Du", "char8_t", 0}, + {"Ds", "char16_t", 0}, +- {"Dh", "float16", 0}, // IEEE 754r half-precision float (16 bits) ++ {"Dh", "float16", 0}, // IEEE 754r half-precision float (16 bits) + {nullptr, nullptr, 0}, + }; + +@@ -193,8 +193,8 @@ static_assert(sizeof(ParseState) == 4 * sizeof(int), + // Only one copy of this exists for each call to Demangle, so the size of this + // struct is nearly inconsequential. + typedef struct { +- const char *mangled_begin; // Beginning of input string. +- char *out; // Beginning of output string. ++ const char* mangled_begin; // Beginning of input string. ++ char* out; // Beginning of output string. + int out_end_idx; // One past last allowed output character. + int recursion_depth; // For stack exhaustion prevention. + int steps; // Cap how much work we'll do, regardless of depth. +@@ -206,7 +206,7 @@ namespace { + // Also prevent unbounded handling of complex inputs. + class ComplexityGuard { + public: +- explicit ComplexityGuard(State *state) : state_(state) { ++ explicit ComplexityGuard(State* state) : state_(state) { + ++state->recursion_depth; + ++state->steps; + } +@@ -239,7 +239,7 @@ class ComplexityGuard { + } + + private: +- State *state_; ++ State* state_; + }; + } // namespace + +@@ -255,7 +255,7 @@ static size_t StrLen(const char *str) { + } + + // Returns true if "str" has at least "n" characters remaining. +-static bool AtLeastNumCharsRemaining(const char *str, size_t n) { ++static bool AtLeastNumCharsRemaining(const char* str, size_t n) { + for (size_t i = 0; i < n; ++i) { + if (str[i] == '\0') { + return false; +@@ -291,7 +291,7 @@ static void InitState(State* state, + state->parse_state.append = true; + } + +-static inline const char *RemainingInput(State *state) { ++static inline const char* RemainingInput(State* state) { + return &state->mangled_begin[state->parse_state.mangled_idx]; + } + +@@ -300,7 +300,9 @@ static inline const char *RemainingInput(State *state) { + // not contain '\0'. + static bool ParseOneCharToken(State *state, const char one_char_token) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + if (RemainingInput(state)[0] == one_char_token) { + ++state->parse_state.mangled_idx; + return true; +@@ -313,7 +315,9 @@ static bool ParseOneCharToken(State *state, const char one_char_token) { + // not contain '\0'. + static bool ParseTwoCharToken(State *state, const char *two_char_token) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + if (RemainingInput(state)[0] == two_char_token[0] && + RemainingInput(state)[1] == two_char_token[1]) { + state->parse_state.mangled_idx += 2; +@@ -326,7 +330,9 @@ static bool ParseTwoCharToken(State *state, const char *two_char_token) { + // "char_class" at "mangled_cur" position. + static bool ParseCharClass(State *state, const char *char_class) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + if (RemainingInput(state)[0] == '\0') { + return false; + } +@@ -340,7 +346,7 @@ static bool ParseCharClass(State *state, const char *char_class) { + return false; + } + +-static bool ParseDigit(State *state, int *digit) { ++static bool ParseDigit(State* state, int* digit) { + char c = RemainingInput(state)[0]; + if (ParseCharClass(state, "0123456789")) { + if (digit != nullptr) { +@@ -352,7 +358,9 @@ static bool ParseDigit(State *state, int *digit) { + } + + // This function is used for handling an optional non-terminal. +-static bool Optional(bool /*status*/) { return true; } ++static bool Optional(bool /*status*/) { ++ return true; ++} + + // This function is used for handling <non-terminal>+ syntax. + typedef bool (*ParseFunc)(State *); +@@ -378,7 +386,7 @@ static bool ZeroOrMore(ParseFunc parse_func, State *state) { + // Append "str" at "out_cur_idx". If there is an overflow, out_cur_idx is + // set to out_end_idx+1. The output string is ensured to + // always terminate with '\0' as long as there is no overflow. +-static void Append(State *state, const char *const str, const size_t length) { ++static void Append(State* state, const char* const str, const size_t length) { + for (size_t i = 0; i < length; ++i) { + if (state->parse_state.out_cur_idx + 1 < + state->out_end_idx) { // +1 for '\0' +@@ -396,13 +404,17 @@ static void Append(State *state, const char *const str, const size_t length) { + } + + // We don't use equivalents in libc to avoid locale issues. +-static bool IsLower(char c) { return c >= 'a' && c <= 'z'; } ++static bool IsLower(char c) { ++ return c >= 'a' && c <= 'z'; ++} + + static bool IsAlpha(char c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); + } + +-static bool IsDigit(char c) { return c >= '0' && c <= '9'; } ++static bool IsDigit(char c) { ++ return c >= '0' && c <= '9'; ++} + + // Returns true if "str" is a function clone suffix. These suffixes are used + // by GCC 4.5.x and later versions (and our locally-modified version of GCC +@@ -428,20 +440,22 @@ static bool IsFunctionCloneSuffix(const char *str) { + ++i; + } + } +- if (!parsed) ++ if (!parsed) { + return false; ++ } + } + return true; // Consumed everything in "str". + } + +-static bool EndsWith(State *state, const char chr) { ++static bool EndsWith(State* state, const char chr) { + return state->parse_state.out_cur_idx > 0 && + state->parse_state.out_cur_idx < state->out_end_idx && + chr == state->out[state->parse_state.out_cur_idx - 1]; + } + + // Append "str" with some tweaks, iff "append" state is true. +-static void MaybeAppendWithLength(State *state, const char *const str, ++static void MaybeAppendWithLength(State* state, ++ const char* const str, + const size_t length) { + if (state->parse_state.append && length > 0) { + // Append a space if the output buffer ends with '<' and "str" +@@ -461,7 +475,7 @@ static void MaybeAppendWithLength(State *state, const char *const str, + } + + // Appends a positive decimal number to the output if appending is enabled. +-static bool MaybeAppendDecimal(State *state, int val) { ++static bool MaybeAppendDecimal(State* state, int val) { + // Max {32-64}-bit unsigned int is 20 digits. + constexpr size_t kMaxLength = 20; + char buf[kMaxLength]; +@@ -471,7 +485,7 @@ static bool MaybeAppendDecimal(State *state, int val) { + if (state->parse_state.append) { + // We can't have a one-before-the-beginning pointer, so instead start with + // one-past-the-end and manipulate one character before the pointer. +- char *p = &buf[kMaxLength]; ++ char* p = &buf[kMaxLength]; + do { // val=0 is the only input that should write a leading zero digit. + *--p = static_cast<char>((val % 10) + '0'); + val /= 10; +@@ -486,7 +500,7 @@ static bool MaybeAppendDecimal(State *state, int val) { + + // A convenient wrapper around MaybeAppendWithLength(). + // Returns true so that it can be placed in "if" conditions. +-static bool MaybeAppend(State *state, const char *const str) { ++static bool MaybeAppend(State* state, const char* const str) { + if (state->parse_state.append) { + size_t length = StrLen(str); + MaybeAppendWithLength(state, str, length); +@@ -501,7 +515,7 @@ static bool EnterNestedName(State *state) { + } + + // This function is used for handling nested names. +-static bool LeaveNestedName(State *state, int16_t prev_value) { ++static bool LeaveNestedName(State* state, int16_t prev_value) { + state->parse_state.nest_level = prev_value; + return true; + } +@@ -543,7 +557,7 @@ static void MaybeCancelLastSeparator(State *state) { + + // Returns true if the identifier of the given length pointed to by + // "mangled_cur" is anonymous namespace. +-static bool IdentifierIsAnonymousNamespace(State *state, size_t length) { ++static bool IdentifierIsAnonymousNamespace(State* state, size_t length) { + // Returns true if "anon_prefix" is a proper prefix of "mangled_cur". + static const char anon_prefix[] = "_GLOBAL__N_"; + return (length > (sizeof(anon_prefix) - 1) && +@@ -554,25 +568,25 @@ static bool IdentifierIsAnonymousNamespace(State *state, size_t length) { + static bool ParseMangledName(State *state); + static bool ParseEncoding(State *state); + static bool ParseName(State *state); +-static bool ParseUnscopedName(State *state); ++static bool ParseUnscopedName(State* state); + static bool ParseNestedName(State *state); + static bool ParsePrefix(State *state); + static bool ParseUnqualifiedName(State *state); + static bool ParseSourceName(State *state); + static bool ParseLocalSourceName(State *state); +-static bool ParseUnnamedTypeName(State *state); ++static bool ParseUnnamedTypeName(State* state); + static bool ParseNumber(State *state, int *number_out); + static bool ParseFloatNumber(State *state); + static bool ParseSeqId(State *state); +-static bool ParseIdentifier(State *state, size_t length); +-static bool ParseOperatorName(State *state, int *arity); ++static bool ParseIdentifier(State* state, size_t length); ++static bool ParseOperatorName(State* state, int* arity); + static bool ParseSpecialName(State *state); + static bool ParseCallOffset(State *state); + static bool ParseNVOffset(State *state); + static bool ParseVOffset(State *state); +-static bool ParseAbiTags(State *state); ++static bool ParseAbiTags(State* state); + static bool ParseCtorDtorName(State *state); +-static bool ParseDecltype(State *state); ++static bool ParseDecltype(State* state); + static bool ParseType(State *state); + static bool ParseCVQualifiers(State *state); + static bool ParseBuiltinType(State *state); +@@ -585,15 +599,15 @@ static bool ParseTemplateParam(State *state); + static bool ParseTemplateTemplateParam(State *state); + static bool ParseTemplateArgs(State *state); + static bool ParseTemplateArg(State *state); +-static bool ParseBaseUnresolvedName(State *state); +-static bool ParseUnresolvedName(State *state); ++static bool ParseBaseUnresolvedName(State* state); ++static bool ParseUnresolvedName(State* state); + static bool ParseExpression(State *state); + static bool ParseExprPrimary(State *state); +-static bool ParseExprCastValue(State *state); ++static bool ParseExprCastValue(State* state); + static bool ParseLocalName(State *state); +-static bool ParseLocalNameSuffix(State *state); ++static bool ParseLocalNameSuffix(State* state); + static bool ParseDiscriminator(State *state); +-static bool ParseSubstitution(State *state, bool accept_std); ++static bool ParseSubstitution(State* state, bool accept_std); + + // Implementation note: the following code is a straightforward + // translation of the Itanium C++ ABI defined in BNF with a couple of +@@ -629,7 +643,9 @@ static bool ParseSubstitution(State *state, bool accept_std); + // <mangled-name> ::= _Z <encoding> + static bool ParseMangledName(State *state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + return ParseTwoCharToken(state, "_Z") && ParseEncoding(state); + } + +@@ -638,7 +654,9 @@ static bool ParseMangledName(State *state) { + // ::= <special-name> + static bool ParseEncoding(State *state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + // Implementing the first two productions together as <name> + // [<bare-function-type>] avoids exponential blowup of backtracking. + // +@@ -660,7 +678,9 @@ static bool ParseEncoding(State *state) { + // ::= <local-name> + static bool ParseName(State *state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + if (ParseNestedName(state) || ParseLocalName(state)) { + return true; + } +@@ -690,7 +710,9 @@ static bool ParseName(State *state) { + // ::= St <unqualified-name> + static bool ParseUnscopedName(State *state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + if (ParseUnqualifiedName(state)) { + return true; + } +@@ -706,7 +728,7 @@ static bool ParseUnscopedName(State *state) { + + // <ref-qualifer> ::= R // lvalue method reference qualifier + // ::= O // rvalue method reference qualifier +-static inline bool ParseRefQualifier(State *state) { ++static inline bool ParseRefQualifier(State* state) { + return ParseCharClass(state, "OR"); + } + +@@ -716,7 +738,9 @@ static inline bool ParseRefQualifier(State *state) { + // <template-args> E + static bool ParseNestedName(State *state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'N') && EnterNestedName(state) && + Optional(ParseCVQualifiers(state)) && +@@ -742,7 +766,9 @@ static bool ParseNestedName(State *state) { + // ::= <substitution> + static bool ParsePrefix(State *state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + bool has_something = false; + while (true) { + MaybeAppendSeparator(state); +@@ -773,7 +799,9 @@ static bool ParsePrefix(State *state) { + // <local-source-name> is a GCC extension; see below. + static bool ParseUnqualifiedName(State *state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + if (ParseOperatorName(state, nullptr) || ParseCtorDtorName(state) || + ParseSourceName(state) || ParseLocalSourceName(state) || + ParseUnnamedTypeName(state)) { +@@ -784,9 +812,11 @@ static bool ParseUnqualifiedName(State *state) { + + // <abi-tags> ::= <abi-tag> [<abi-tags>] + // <abi-tag> ::= B <source-name> +-static bool ParseAbiTags(State *state) { ++static bool ParseAbiTags(State* state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + + while (ParseOneCharToken(state, 'B')) { + ParseState copy = state->parse_state; +@@ -805,7 +835,9 @@ static bool ParseAbiTags(State *state) { + // <source-name> ::= <positive length number> <identifier> + static bool ParseSourceName(State *state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + ParseState copy = state->parse_state; + int length = -1; + if (ParseNumber(state, &length) && +@@ -823,7 +855,9 @@ static bool ParseSourceName(State *state) { + // https://gcc.gnu.org/viewcvs?view=rev&revision=124467 + static bool ParseLocalSourceName(State *state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'L') && ParseSourceName(state) && + Optional(ParseDiscriminator(state))) { +@@ -837,9 +871,11 @@ static bool ParseLocalSourceName(State *state) { + // ::= <closure-type-name> + // <closure-type-name> ::= Ul <lambda-sig> E [<(nonnegative) number>] _ + // <lambda-sig> ::= <(parameter) type>+ +-static bool ParseUnnamedTypeName(State *state) { ++static bool ParseUnnamedTypeName(State* state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + ParseState copy = state->parse_state; + // Type's 1-based index n is encoded as { "", n == 1; itoa(n-2), otherwise }. + // Optionally parse the encoded value into 'which' and add 2 to get the index. +@@ -878,12 +914,14 @@ static bool ParseUnnamedTypeName(State *state) { + // parsed number on success. + static bool ParseNumber(State *state, int *number_out) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + bool negative = false; + if (ParseOneCharToken(state, 'n')) { + negative = true; + } +- const char *p = RemainingInput(state); ++ const char* p = RemainingInput(state); + uint64_t number = 0; + for (; *p != '\0'; ++p) { + if (IsDigit(*p)) { +@@ -913,8 +951,10 @@ static bool ParseNumber(State *state, int *number_out) { + // hexadecimal string. + static bool ParseFloatNumber(State *state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; +- const char *p = RemainingInput(state); ++ if (guard.IsTooComplex()) { ++ return false; ++ } ++ const char* p = RemainingInput(state); + for (; *p != '\0'; ++p) { + if (!IsDigit(*p) && !(*p >= 'a' && *p <= 'f')) { + break; +@@ -931,8 +971,10 @@ static bool ParseFloatNumber(State *state) { + // using digits and upper case letters + static bool ParseSeqId(State *state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; +- const char *p = RemainingInput(state); ++ if (guard.IsTooComplex()) { ++ return false; ++ } ++ const char* p = RemainingInput(state); + for (; *p != '\0'; ++p) { + if (!IsDigit(*p) && !(*p >= 'A' && *p <= 'Z')) { + break; +@@ -946,9 +988,11 @@ static bool ParseSeqId(State *state) { + } + + // <identifier> ::= <unqualified source code identifier> (of given length) +-static bool ParseIdentifier(State *state, size_t length) { ++static bool ParseIdentifier(State* state, size_t length) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + if (!AtLeastNumCharsRemaining(RemainingInput(state), length)) { + return false; + } +@@ -964,9 +1008,11 @@ static bool ParseIdentifier(State *state, size_t length) { + // <operator-name> ::= nw, and other two letters cases + // ::= cv <type> # (cast) + // ::= v <digit> <source-name> # vendor extended operator +-static bool ParseOperatorName(State *state, int *arity) { ++static bool ParseOperatorName(State* state, int* arity) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + if (!AtLeastNumCharsRemaining(RemainingInput(state), 2)) { + return false; + } +@@ -1036,7 +1082,9 @@ static bool ParseOperatorName(State *state, int *arity) { + // stack traces. The are special data. + static bool ParseSpecialName(State *state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "VTISH") && + ParseType(state)) { +@@ -1098,7 +1146,9 @@ static bool ParseSpecialName(State *state) { + // ::= v <v-offset> _ + static bool ParseCallOffset(State *state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'h') && ParseNVOffset(state) && + ParseOneCharToken(state, '_')) { +@@ -1118,14 +1168,18 @@ static bool ParseCallOffset(State *state) { + // <nv-offset> ::= <(offset) number> + static bool ParseNVOffset(State *state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + return ParseNumber(state, nullptr); + } + + // <v-offset> ::= <(offset) number> _ <(virtual offset) number> + static bool ParseVOffset(State *state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + ParseState copy = state->parse_state; + if (ParseNumber(state, nullptr) && ParseOneCharToken(state, '_') && + ParseNumber(state, nullptr)) { +@@ -1144,11 +1198,13 @@ static bool ParseVOffset(State *state) { + // ::= C4 | D4 + static bool ParseCtorDtorName(State *state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'C')) { + if (ParseCharClass(state, "1234")) { +- const char *const prev_name = ++ const char* const prev_name = + state->out + state->parse_state.prev_name_idx; + MaybeAppendWithLength(state, prev_name, + state->parse_state.prev_name_length); +@@ -1161,7 +1217,7 @@ static bool ParseCtorDtorName(State *state) { + state->parse_state = copy; + + if (ParseOneCharToken(state, 'D') && ParseCharClass(state, "0124")) { +- const char *const prev_name = state->out + state->parse_state.prev_name_idx; ++ const char* const prev_name = state->out + state->parse_state.prev_name_idx; + MaybeAppend(state, "~"); + MaybeAppendWithLength(state, prev_name, + state->parse_state.prev_name_length); +@@ -1174,9 +1230,11 @@ static bool ParseCtorDtorName(State *state) { + // <decltype> ::= Dt <expression> E # decltype of an id-expression or class + // # member access (C++0x) + // ::= DT <expression> E # decltype of an expression (C++0x) +-static bool ParseDecltype(State *state) { ++static bool ParseDecltype(State* state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'D') && ParseCharClass(state, "tT") && +@@ -1209,7 +1267,9 @@ static bool ParseDecltype(State *state) { + // + static bool ParseType(State *state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + ParseState copy = state->parse_state; + + // We should check CV-qualifers, and PRGC things first. +@@ -1227,7 +1287,9 @@ static bool ParseType(State *state) { + // By consuming the CV-qualifiers first, the former parse is disabled. + if (ParseCVQualifiers(state)) { + const bool result = ParseType(state); +- if (!result) state->parse_state = copy; ++ if (!result) { ++ state->parse_state = copy; ++ } + return result; + } + state->parse_state = copy; +@@ -1238,7 +1300,9 @@ static bool ParseType(State *state) { + // refusing to backtrack the tag characters. + if (ParseCharClass(state, "OPRCG")) { + const bool result = ParseType(state); +- if (!result) state->parse_state = copy; ++ if (!result) { ++ state->parse_state = copy; ++ } + return result; + } + state->parse_state = copy; +@@ -1286,7 +1350,9 @@ static bool ParseType(State *state) { + // ParseType(). + static bool ParseCVQualifiers(State *state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + int num_cv_qualifiers = 0; + num_cv_qualifiers += ParseOneCharToken(state, 'r'); + num_cv_qualifiers += ParseOneCharToken(state, 'V'); +@@ -1303,7 +1369,9 @@ static bool ParseCVQualifiers(State *state) { + // + static bool ParseBuiltinType(State *state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + const AbbrevPair *p; + for (p = kBuiltinTypeList; p->abbrev != nullptr; ++p) { + // Guaranteed only 1- or 2-character strings in kBuiltinTypeList. +@@ -1333,11 +1401,15 @@ static bool ParseBuiltinType(State *state) { + // noexcept + // ::= Dw <type>+ E # dynamic exception specification + // with instantiation-dependent types +-static bool ParseExceptionSpec(State *state) { ++static bool ParseExceptionSpec(State* state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + +- if (ParseTwoCharToken(state, "Do")) return true; ++ if (ParseTwoCharToken(state, "Do")) { ++ return true; ++ } + + ParseState copy = state->parse_state; + if (ParseTwoCharToken(state, "DO") && ParseExpression(state) && +@@ -1357,7 +1429,9 @@ static bool ParseExceptionSpec(State *state) { + // <function-type> ::= [exception-spec] F [Y] <bare-function-type> [O] E + static bool ParseFunctionType(State *state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + ParseState copy = state->parse_state; + if (Optional(ParseExceptionSpec(state)) && ParseOneCharToken(state, 'F') && + Optional(ParseOneCharToken(state, 'Y')) && ParseBareFunctionType(state) && +@@ -1372,7 +1446,9 @@ static bool ParseFunctionType(State *state) { + // <bare-function-type> ::= <(signature) type>+ + static bool ParseBareFunctionType(State *state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + ParseState copy = state->parse_state; + DisableAppend(state); + if (OneOrMore(ParseType, state)) { +@@ -1387,7 +1463,9 @@ static bool ParseBareFunctionType(State *state) { + // <class-enum-type> ::= <name> + static bool ParseClassEnumType(State *state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + return ParseName(state); + } + +@@ -1395,7 +1473,9 @@ static bool ParseClassEnumType(State *state) { + // ::= A [<(dimension) expression>] _ <(element) type> + static bool ParseArrayType(State *state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'A') && ParseNumber(state, nullptr) && + ParseOneCharToken(state, '_') && ParseType(state)) { +@@ -1414,7 +1494,9 @@ static bool ParseArrayType(State *state) { + // <pointer-to-member-type> ::= M <(class) type> <(member) type> + static bool ParsePointerToMemberType(State *state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'M') && ParseType(state) && ParseType(state)) { + return true; +@@ -1427,7 +1509,9 @@ static bool ParsePointerToMemberType(State *state) { + // ::= T <parameter-2 non-negative number> _ + static bool ParseTemplateParam(State *state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + if (ParseTwoCharToken(state, "T_")) { + MaybeAppend(state, "?"); // We don't support template substitutions. + return true; +@@ -1447,7 +1531,9 @@ static bool ParseTemplateParam(State *state) { + // ::= <substitution> + static bool ParseTemplateTemplateParam(State *state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + return (ParseTemplateParam(state) || + // "std" on its own isn't a template. + ParseSubstitution(state, /*accept_std=*/false)); +@@ -1456,7 +1542,9 @@ static bool ParseTemplateTemplateParam(State *state) { + // <template-args> ::= I <template-arg>+ E + static bool ParseTemplateArgs(State *state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + ParseState copy = state->parse_state; + DisableAppend(state); + if (ParseOneCharToken(state, 'I') && OneOrMore(ParseTemplateArg, state) && +@@ -1475,7 +1563,9 @@ static bool ParseTemplateArgs(State *state) { + // ::= X <expression> E + static bool ParseTemplateArg(State *state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'J') && ZeroOrMore(ParseTemplateArg, state) && + ParseOneCharToken(state, 'E')) { +@@ -1578,14 +1668,14 @@ static bool ParseTemplateArg(State *state) { + // <unresolved-type> ::= <template-param> [<template-args>] + // ::= <decltype> + // ::= <substitution> +-static inline bool ParseUnresolvedType(State *state) { ++static inline bool ParseUnresolvedType(State* state) { + // No ComplexityGuard because we don't copy the state in this stack frame. + return (ParseTemplateParam(state) && Optional(ParseTemplateArgs(state))) || + ParseDecltype(state) || ParseSubstitution(state, /*accept_std=*/false); + } + + // <simple-id> ::= <source-name> [<template-args>] +-static inline bool ParseSimpleId(State *state) { ++static inline bool ParseSimpleId(State* state) { + // No ComplexityGuard because we don't copy the state in this stack frame. + + // Note: <simple-id> cannot be followed by a parameter pack; see comment in +@@ -1596,9 +1686,11 @@ static inline bool ParseSimpleId(State *state) { + // <base-unresolved-name> ::= <source-name> [<template-args>] + // ::= on <operator-name> [<template-args>] + // ::= dn <destructor-name> +-static bool ParseBaseUnresolvedName(State *state) { ++static bool ParseBaseUnresolvedName(State* state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + + if (ParseSimpleId(state)) { + return true; +@@ -1626,9 +1718,11 @@ static bool ParseBaseUnresolvedName(State *state) { + // <base-unresolved-name> + // ::= [gs] sr <unresolved-qualifier-level>+ E + // <base-unresolved-name> +-static bool ParseUnresolvedName(State *state) { ++static bool ParseUnresolvedName(State* state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + + ParseState copy = state->parse_state; + if (Optional(ParseTwoCharToken(state, "gs")) && +@@ -1684,7 +1778,9 @@ static bool ParseUnresolvedName(State *state) { + // ::= fL <number> p <(top-level) CV-qualifiers> <number> _ + static bool ParseExpression(State *state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + if (ParseTemplateParam(state) || ParseExprPrimary(state)) { + return true; + } +@@ -1817,7 +1913,9 @@ static bool ParseExpression(State *state) { + // casts to <local-name> types. + static bool ParseExprPrimary(State *state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + ParseState copy = state->parse_state; + + // The "LZ" special case: if we see LZ, we commit to accept "LZ <encoding> E" +@@ -1848,9 +1946,11 @@ static bool ParseExprPrimary(State *state) { + } + + // <number> or <float>, followed by 'E', as described above ParseExprPrimary. +-static bool ParseExprCastValue(State *state) { ++static bool ParseExprCastValue(State* state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + // We have to be able to backtrack after accepting a number because we could + // have e.g. "7fffE", which will accept "7" as a number but then fail to find + // the 'E'. +@@ -1877,9 +1977,11 @@ static bool ParseExprCastValue(State *state) { + // <local-name-suffix> ::= s [<discriminator>] + // ::= <name> [<discriminator>] + +-static bool ParseLocalNameSuffix(State *state) { ++static bool ParseLocalNameSuffix(State* state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + + if (MaybeAppend(state, "::") && ParseName(state) && + Optional(ParseDiscriminator(state))) { +@@ -1896,9 +1998,11 @@ static bool ParseLocalNameSuffix(State *state) { + return ParseOneCharToken(state, 's') && Optional(ParseDiscriminator(state)); + } + +-static bool ParseLocalName(State *state) { ++static bool ParseLocalName(State* state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, 'Z') && ParseEncoding(state) && + ParseOneCharToken(state, 'E') && ParseLocalNameSuffix(state)) { +@@ -1911,7 +2015,9 @@ static bool ParseLocalName(State *state) { + // <discriminator> := _ <(non-negative) number> + static bool ParseDiscriminator(State *state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + ParseState copy = state->parse_state; + if (ParseOneCharToken(state, '_') && ParseNumber(state, nullptr)) { + return true; +@@ -1931,9 +2037,11 @@ static bool ParseDiscriminator(State *state) { + // an unqualified name and re-parse the same template-args. To block this + // exponential backtracking, we disable it with 'accept_std=false' in + // problematic contexts. +-static bool ParseSubstitution(State *state, bool accept_std) { ++static bool ParseSubstitution(State* state, bool accept_std) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + if (ParseTwoCharToken(state, "S_")) { + MaybeAppend(state, "?"); // We don't support substitutions. + return true; +@@ -1971,7 +2079,9 @@ static bool ParseSubstitution(State *state, bool accept_std) { + // or version suffix. Returns true only if all of "mangled_cur" was consumed. + static bool ParseTopLevelMangledName(State *state) { + ComplexityGuard guard(state); +- if (guard.IsTooComplex()) return false; ++ if (guard.IsTooComplex()) { ++ return false; ++ } + if (ParseMangledName(state)) { + if (RemainingInput(state)[0] != '\0') { + // Drop trailing function clone suffix, if any. +@@ -1991,13 +2101,13 @@ static bool ParseTopLevelMangledName(State *state) { + return false; + } + +-static bool Overflowed(const State *state) { ++static bool Overflowed(const State* state) { + return state->parse_state.out_cur_idx >= state->out_end_idx; + } + #endif + + // The demangler entry point. +-bool Demangle(const char *mangled, char *out, size_t out_size) { ++bool Demangle(const char* mangled, char* out, size_t out_size) { + #if defined(GLOG_OS_WINDOWS) + #if defined(HAVE_DBGHELP) + // When built with incremental linking, the Windows debugger +diff --git a/base/third_party/symbolize/demangle.h b/base/third_party/symbolize/demangle.h +index 26e821a53c2cb..7d5cfaaabf0dd 100644 +--- a/base/third_party/symbolize/demangle.h ++++ b/base/third_party/symbolize/demangle.h +@@ -80,7 +80,7 @@ _START_GOOGLE_NAMESPACE_ + // Demangle "mangled". On success, return true and write the + // demangled symbol name to "out". Otherwise, return false. + // "out" is modified even if demangling is unsuccessful. +-bool GLOG_EXPORT Demangle(const char *mangled, char *out, size_t out_size); ++bool GLOG_EXPORT Demangle(const char* mangled, char* out, size_t out_size); + + _END_GOOGLE_NAMESPACE_ + +diff --git a/base/third_party/symbolize/glog/logging.h b/base/third_party/symbolize/glog/logging.h +index 46869226024da..b935e3ec9cded 100644 +--- a/base/third_party/symbolize/glog/logging.h ++++ b/base/third_party/symbolize/glog/logging.h +@@ -38,4 +38,4 @@ + + // Not needed in Chrome. + +-#endif // GLOG_LOGGING_H ++#endif // GLOG_LOGGING_H +diff --git a/base/third_party/symbolize/symbolize.cc b/base/third_party/symbolize/symbolize.cc +index b6ddc85d57185..a3b8399f411bf 100644 +--- a/base/third_party/symbolize/symbolize.cc ++++ b/base/third_party/symbolize/symbolize.cc +@@ -97,7 +97,7 @@ void InstallSymbolizeOpenObjectFileCallback( + // where the input symbol is demangled in-place. + // To keep stack consumption low, we would like this function to not + // get inlined. +-static ATTRIBUTE_NOINLINE void DemangleInplace(char *out, size_t out_size) { ++static ATTRIBUTE_NOINLINE void DemangleInplace(char* out, size_t out_size) { + char demangled[256]; // Big enough for sane demangled symbols. + if (Demangle(out, demangled, sizeof(demangled))) { + // Demangling succeeded. Copy to out if the space allows. +@@ -121,17 +121,17 @@ _END_GOOGLE_NAMESPACE_ + #else + #include <elf.h> + #endif ++#include <fcntl.h> ++#include <stdint.h> ++#include <sys/stat.h> ++#include <sys/types.h> ++#include <unistd.h> + #include <cerrno> + #include <climits> + #include <cstddef> + #include <cstdio> + #include <cstdlib> + #include <cstring> +-#include <fcntl.h> +-#include <stdint.h> +-#include <sys/stat.h> +-#include <sys/types.h> +-#include <unistd.h> + + #include "symbolize.h" + #include "config.h" +@@ -153,7 +153,8 @@ ssize_t ReadFromOffset(const int fd, + const size_t count, + const size_t offset) { + SAFE_ASSERT(fd >= 0); +- SAFE_ASSERT(count <= static_cast<size_t>(std::numeric_limits<ssize_t>::max())); ++ SAFE_ASSERT(count <= ++ static_cast<size_t>(std::numeric_limits<ssize_t>::max())); + char *buf0 = reinterpret_cast<char *>(buf); + size_t num_bytes = 0; + while (num_bytes < count) { +@@ -176,8 +177,10 @@ ssize_t ReadFromOffset(const int fd, + // pointed by "fd" into the buffer starting at "buf" while handling + // short reads and EINTR. On success, return true. Otherwise, return + // false. +-static bool ReadFromOffsetExact(const int fd, void *buf, +- const size_t count, const size_t offset) { ++static bool ReadFromOffsetExact(const int fd, ++ void* buf, ++ const size_t count, ++ const size_t offset) { + ssize_t len = ReadFromOffset(fd, buf, count, offset); + return static_cast<size_t>(len) == count; + } +@@ -199,9 +202,11 @@ static int FileGetElfType(const int fd) { + // and return true. Otherwise, return false. + // To keep stack consumption low, we would like this function to not get + // inlined. +-static ATTRIBUTE_NOINLINE bool +-GetSectionHeaderByType(const int fd, ElfW(Half) sh_num, const size_t sh_offset, +- ElfW(Word) type, ElfW(Shdr) *out) { ++static ATTRIBUTE_NOINLINE bool GetSectionHeaderByType(const int fd, ++ ElfW(Half) sh_num, ++ const size_t sh_offset, ++ ElfW(Word) type, ++ ElfW(Shdr) * out) { + // Read at most 16 section headers at a time to save read calls. + ElfW(Shdr) buf[16]; + for (size_t i = 0; i < sh_num;) { +@@ -248,8 +253,8 @@ bool GetSectionHeaderByName(int fd, const char *name, size_t name_len, + } + + for (size_t i = 0; i < elf_header.e_shnum; ++i) { +- size_t section_header_offset = (elf_header.e_shoff + +- elf_header.e_shentsize * i); ++ size_t section_header_offset = ++ (elf_header.e_shoff + elf_header.e_shentsize * i); + if (!ReadFromOffsetExact(fd, out, sizeof(*out), section_header_offset)) { + return false; + } +@@ -281,10 +286,13 @@ bool GetSectionHeaderByName(int fd, const char *name, size_t name_len, + // to out. Otherwise, return false. + // To keep stack consumption low, we would like this function to not get + // inlined. +-static ATTRIBUTE_NOINLINE bool +-FindSymbol(uint64_t pc, const int fd, char *out, size_t out_size, +- uint64_t symbol_offset, const ElfW(Shdr) *strtab, +- const ElfW(Shdr) *symtab) { ++static ATTRIBUTE_NOINLINE bool FindSymbol(uint64_t pc, ++ const int fd, ++ char* out, ++ size_t out_size, ++ uint64_t symbol_offset, ++ const ElfW(Shdr) * strtab, ++ const ElfW(Shdr) * symtab) { + if (symtab == NULL) { + return false; + } +@@ -384,7 +392,7 @@ namespace { + // and snprintf(). + class LineReader { + public: +- explicit LineReader(int fd, char *buf, size_t buf_len, size_t offset) ++ explicit LineReader(int fd, char* buf, size_t buf_len, size_t offset) + : fd_(fd), + buf_(buf), + buf_len_(buf_len), +@@ -449,11 +457,12 @@ class LineReader { + } + + private: +- LineReader(const LineReader &); ++ LineReader(const LineReader&); + void operator=(const LineReader&); + + char *FindLineFeed() { +- return reinterpret_cast<char *>(memchr(bol_, '\n', static_cast<size_t>(eod_ - bol_))); ++ return reinterpret_cast<char*>( ++ memchr(bol_, '\n', static_cast<size_t>(eod_ - bol_))); + } + + bool BufferIsEmpty() { +@@ -483,7 +492,8 @@ static char *GetHex(const char *start, const char *end, uint64_t *hex) { + int ch = *p; + if ((ch >= '0' && ch <= '9') || + (ch >= 'A' && ch <= 'F') || (ch >= 'a' && ch <= 'f')) { +- *hex = (*hex << 4U) | (ch < 'A' ? static_cast<uint64_t>(ch - '0') : (ch & 0xF) + 9U); ++ *hex = (*hex << 4U) | ++ (ch < 'A' ? static_cast<uint64_t>(ch - '0') : (ch & 0xF) + 9U); + } else { // Encountered the first non-hex character. + break; + } +@@ -647,12 +657,11 @@ static int OpenObjectFileContainingPcAndGetStartAddressNoHook( + } + } + +-int OpenObjectFileContainingPcAndGetStartAddress( +- uint64_t pc, +- uint64_t& start_address, +- uint64_t& base_address, +- char* out_file_name, +- size_t out_file_name_size) { ++int OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc, ++ uint64_t& start_address, ++ uint64_t& base_address, ++ char* out_file_name, ++ size_t out_file_name_size) { + if (g_symbolize_open_object_file_callback) { + return g_symbolize_open_object_file_callback( + pc, start_address, base_address, out_file_name, out_file_name_size); +@@ -668,7 +677,11 @@ int OpenObjectFileContainingPcAndGetStartAddress( + // bytes. Output will be truncated as needed, and a NUL character is always + // appended. + // NOTE: code from sandbox/linux/seccomp-bpf/demo.cc. +-static char *itoa_r(uintptr_t i, char *buf, size_t sz, unsigned base, size_t padding) { ++static char* itoa_r(uintptr_t i, ++ char* buf, ++ size_t sz, ++ unsigned base, ++ size_t padding) { + // Make sure we can write at least one NUL byte. + size_t n = 1; + if (n > sz) { +@@ -745,7 +758,8 @@ static void SafeAppendHexNumber(uint64_t value, char* dest, size_t dest_size) { + // and "out" is used as its output. + // To keep stack consumption low, we would like this function to not + // get inlined. +-static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out, ++static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void* pc, ++ char* out, + size_t out_size) { + uint64_t pc0 = reinterpret_cast<uintptr_t>(pc); + uint64_t start_address = 0; +@@ -822,14 +836,16 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out, + + _END_GOOGLE_NAMESPACE_ + +-#elif (defined(GLOG_OS_MACOSX) || defined(GLOG_OS_EMSCRIPTEN)) && defined(HAVE_DLADDR) ++#elif (defined(GLOG_OS_MACOSX) || defined(GLOG_OS_EMSCRIPTEN)) && \ ++ defined(HAVE_DLADDR) + + #include <dlfcn.h> + #include <cstring> + + _START_GOOGLE_NAMESPACE_ + +-static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out, ++static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void* pc, ++ char* out, + size_t out_size) { + Dl_info info; + if (dladdr(pc, &info)) { +@@ -883,7 +899,8 @@ private: + SymInitializer& operator=(const SymInitializer&); + }; + +-static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out, ++static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void* pc, ++ char* out, + size_t out_size) { + const static SymInitializer symInitializer; + if (!symInitializer.ready) { +@@ -918,7 +935,7 @@ _END_GOOGLE_NAMESPACE_ + + _START_GOOGLE_NAMESPACE_ + +-bool Symbolize(void *pc, char *out, size_t out_size) { ++bool Symbolize(void* pc, char* out, size_t out_size) { + return SymbolizeAndDemangle(pc, out, out_size); + } + +diff --git a/base/third_party/symbolize/symbolize.h b/base/third_party/symbolize/symbolize.h +index 64ff0509ce57d..987569fdde67f 100644 +--- a/base/third_party/symbolize/symbolize.h ++++ b/base/third_party/symbolize/symbolize.h +@@ -127,7 +127,7 @@ ATTRIBUTE_NOINLINE int OpenObjectFileContainingPcAndGetStartAddress( + + _END_GOOGLE_NAMESPACE_ + +-#endif /* __ELF__ */ ++#endif /* __ELF__ */ + + _START_GOOGLE_NAMESPACE_ + +@@ -140,7 +140,7 @@ struct FileDescriptor { + int get() { return fd_; } + + private: +- FileDescriptor(const FileDescriptor &); ++ FileDescriptor(const FileDescriptor&); + void operator=(const FileDescriptor&); + }; + +diff --git a/base/third_party/symbolize/utilities.h b/base/third_party/symbolize/utilities.h +index 8c61380fad159..bb206a8020315 100644 +--- a/base/third_party/symbolize/utilities.h ++++ b/base/third_party/symbolize/utilities.h +@@ -35,13 +35,13 @@ + #define UTILITIES_H__ + + #ifdef HAVE___ATTRIBUTE__ +-# define ATTRIBUTE_NOINLINE __attribute__ ((noinline)) +-# define HAVE_ATTRIBUTE_NOINLINE ++#define ATTRIBUTE_NOINLINE __attribute__((noinline)) ++#define HAVE_ATTRIBUTE_NOINLINE + #elif defined(GLOG_OS_WINDOWS) +-# define ATTRIBUTE_NOINLINE __declspec(noinline) +-# define HAVE_ATTRIBUTE_NOINLINE ++#define ATTRIBUTE_NOINLINE __declspec(noinline) ++#define HAVE_ATTRIBUTE_NOINLINE + #else +-# define ATTRIBUTE_NOINLINE ++#define ATTRIBUTE_NOINLINE + #endif + + #endif // UTILITIES_H__
diff --git a/build/config/siso/clang_linux.star b/build/config/siso/clang_linux.star index d46a641b..b88af3b 100644 --- a/build/config/siso/clang_linux.star +++ b/build/config/siso/clang_linux.star
@@ -56,7 +56,7 @@ step_config["rules"].extend([ { "name": "clang/cxx", - "action": "cxx", + "action": "(.*_)?cxx", "command_prefix": "../../third_party/llvm-build/Release+Asserts/bin/clang++ ", "inputs": [ "third_party/llvm-build/Release+Asserts/bin/clang++", @@ -66,7 +66,7 @@ }, { "name": "clang/cc", - "action": "cc", + "action": "(.*_)?cc", "command_prefix": "../../third_party/llvm-build/Release+Asserts/bin/clang ", "inputs": [ "third_party/llvm-build/Release+Asserts/bin/clang", @@ -76,7 +76,7 @@ }, { "name": "clang-coverage/cxx", - "action": "cxx", + "action": "(.*_)?cxx", "command_prefix": "\"python3\" ../../build/toolchain/clang_code_coverage_wrapper.py", "inputs": [ "build/toolchain/clang_code_coverage_wrapper.py", @@ -88,7 +88,7 @@ }, { "name": "clang-coverage/cc", - "action": "cc", + "action": "(.*_)?cc", "command_prefix": "\"python3\" ../../build/toolchain/clang_code_coverage_wrapper.py", "inputs": [ "build/toolchain/clang_code_coverage_wrapper.py",
diff --git a/cc/metrics/average_lag_tracking_manager_unittest.cc b/cc/metrics/average_lag_tracking_manager_unittest.cc index c374d8ce..7548c0f9 100644 --- a/cc/metrics/average_lag_tracking_manager_unittest.cc +++ b/cc/metrics/average_lag_tracking_manager_unittest.cc
@@ -97,7 +97,7 @@ ui::ET_GESTURE_SCROLL_UPDATE, ui::ScrollInputType::kTouchscreen, kScrollIsNotInertial, scroll_update_type, delta, event_time, arrived_in_browser_main_timestamp, - base::IdType64<class ui::LatencyInfo>(trace_id)); + base::IdType64<class ui::LatencyInfo>(trace_id), base::TimeTicks()); } AverageLagTrackingManager average_lag_tracking_manager_;
diff --git a/cc/metrics/compositor_frame_reporter_unittest.cc b/cc/metrics/compositor_frame_reporter_unittest.cc index 7a3c45d..0d8d6a1 100644 --- a/cc/metrics/compositor_frame_reporter_unittest.cc +++ b/cc/metrics/compositor_frame_reporter_unittest.cc
@@ -103,7 +103,7 @@ if (stage_durations[i] >= 0) { AdvanceNowByUs(stage_durations[i]); metrics->SetDispatchStageTimestamp( - EventMetrics::DispatchStage(i + 1)); + EventMetrics::DispatchStage(i + 2)); } } } @@ -112,9 +112,11 @@ std::unique_ptr<EventMetrics> CreateEventMetrics(ui::EventType type) { const base::TimeTicks event_time = AdvanceNowByUs(3); + const base::TimeTicks arrived_in_browser_main_timestamp = AdvanceNowByUs(2); AdvanceNowByUs(3); - return SetupEventMetrics( - EventMetrics::CreateForTesting(type, event_time, &test_tick_clock_)); + return SetupEventMetrics(EventMetrics::CreateForTesting( + type, event_time, arrived_in_browser_main_timestamp, + &test_tick_clock_)); } // Creates EventMetrics with elements in stage_durations representing each @@ -1584,13 +1586,15 @@ // Test with no previous stage predictions. std::vector<base::TimeDelta> expected_predictions1(kNumDispatchStages, base::Microseconds(-1)); - IntToTimeDeltaVector(expected_predictions1, - std::vector<int>{/*kArrivedInBrowserMain=*/300, - /*kArrivedInRendererCompositor=*/300, - /*kRendererCompositorStarted=*/300, - /*kRendererCompositorFinished=*/300, - /*kRendererMainStarted=*/300, - /*kRendererMainFinished=*/300}); + IntToTimeDeltaVector( + expected_predictions1, + std::vector<int>{/*kScrollsBlockingTouchDispatchedToRenderer=*/-1, + /*kArrivedInBrowserMain=*/300, + /*kArrivedInRendererCompositor=*/300, + /*kRendererCompositorStarted=*/300, + /*kRendererCompositorFinished=*/300, + /*kRendererMainStarted=*/300, + /*kRendererMainFinished=*/300}); base::TimeDelta expected_transition1 = base::Microseconds(300); base::TimeDelta expected_total1 = base::Microseconds(2400); CompositorFrameReporter::EventLatencyInfo actual_predictions1 = @@ -1603,14 +1607,14 @@ std::vector<base::TimeDelta> expected_predictions2(kNumDispatchStages, base::Microseconds(-1)); IntToTimeDeltaVector(expected_predictions2, - std::vector<int>{262, 262, 300, 412, 225, 450}); + std::vector<int>{300, 262, 262, 300, 412, 225, 450}); base::TimeDelta expected_transition2 = base::Microseconds(390); - base::TimeDelta expected_total2 = base::Microseconds(2601); + base::TimeDelta expected_total2 = base::Microseconds(2901); CompositorFrameReporter::EventLatencyInfo actual_predictions2 = CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, kNumOfCompositorStages); IntToTimeDeltaVector(actual_predictions2.dispatch_durations, - std::vector<int>{250, 250, 300, 450, 200, 500}); + std::vector<int>{300, 250, 250, 300, 450, 200, 500}); actual_predictions2.transition_duration = base::Microseconds(420); pipeline_reporter_->CalculateEventLatencyPrediction( actual_predictions2, kLatencyPredictionDeviationThreshold); @@ -1619,14 +1623,14 @@ std::vector<base::TimeDelta> expected_predictions3(kNumDispatchStages, base::Microseconds(-1)); IntToTimeDeltaVector(expected_predictions3, - std::vector<int>{300, 375, 450, 300, 300, 300}); + std::vector<int>{300, 300, 375, 450, 300, 300, 300}); base::TimeDelta expected_transition3 = base::Microseconds(270); - base::TimeDelta expected_total3 = base::Microseconds(2595); + base::TimeDelta expected_total3 = base::Microseconds(2895); CompositorFrameReporter::EventLatencyInfo actual_predictions3 = CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, kNumOfCompositorStages); IntToTimeDeltaVector(actual_predictions3.dispatch_durations, - std::vector<int>{-1, 400, 500, 300, -1, -1}); + std::vector<int>{300, -1, 400, 500, 300, -1, -1}); actual_predictions3.transition_duration = base::Microseconds(260); pipeline_reporter_->CalculateEventLatencyPrediction( actual_predictions3, kLatencyPredictionDeviationThreshold); @@ -1688,7 +1692,7 @@ std::vector<base::TimeDelta> expected_predictions1(kNumDispatchStages, base::Microseconds(-1)); IntToTimeDeltaVector(expected_predictions1, - std::vector<int>{200, 400, 600, 700, -1, -1}); + std::vector<int>{-1, 200, 400, 600, 700, -1, -1}); base::TimeDelta expected_transition1 = base::Microseconds(470); base::TimeDelta expected_total1 = base::Microseconds(2670); CompositorFrameReporter::EventLatencyInfo actual_predictions1 = @@ -1701,14 +1705,14 @@ std::vector<base::TimeDelta> expected_predictions2(kNumDispatchStages, base::Microseconds(-1)); IntToTimeDeltaVector(expected_predictions2, - std::vector<int>{125, 250, 375, 475, 200, 500}); + std::vector<int>{100, 125, 250, 375, 475, 200, 500}); base::TimeDelta expected_transition2 = base::Microseconds(402); - base::TimeDelta expected_total2 = base::Microseconds(2627); + base::TimeDelta expected_total2 = base::Microseconds(2727); CompositorFrameReporter::EventLatencyInfo actual_predictions2 = CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, kNumOfCompositorStages); IntToTimeDeltaVector(actual_predictions2.dispatch_durations, - std::vector<int>{100, 200, 300, 400, 200, 500}); + std::vector<int>{100, 100, 200, 300, 400, 200, 500}); actual_predictions2.transition_duration = base::Microseconds(380); pipeline_reporter_->CalculateEventLatencyPrediction( actual_predictions2, kLatencyPredictionDeviationThreshold); @@ -1717,14 +1721,14 @@ std::vector<base::TimeDelta> expected_predictions3(kNumDispatchStages, base::Microseconds(-1)); IntToTimeDeltaVector(expected_predictions3, - std::vector<int>{143, 400, 525, 745, -1, -1}); + std::vector<int>{125, 143, 400, 525, 745, -1, -1}); base::TimeDelta expected_transition3 = base::Microseconds(492); - base::TimeDelta expected_total3 = base::Microseconds(2605); + base::TimeDelta expected_total3 = base::Microseconds(2730); CompositorFrameReporter::EventLatencyInfo actual_predictions3 = CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, kNumOfCompositorStages); IntToTimeDeltaVector(actual_predictions3.dispatch_durations, - std::vector<int>{125, 400, 500, 760, -1, -1}); + std::vector<int>{125, 125, 400, 500, 760, -1, -1}); actual_predictions3.transition_duration = base::Microseconds(500); pipeline_reporter_->CalculateEventLatencyPrediction( actual_predictions3, kLatencyPredictionDeviationThreshold); @@ -1796,13 +1800,15 @@ // Test with no previous stage predictions. std::vector<base::TimeDelta> expected_dispatch1(kNumDispatchStages, base::Microseconds(-1)); - IntToTimeDeltaVector(expected_dispatch1, - std::vector<int>{/*kArrivedInBrowserMain=*/300, - /*kArrivedInRendererCompositor=*/300, - /*kRendererCompositorStarted=*/300, - /*kRendererCompositorFinished=*/300, - /*kRendererMainStarted=*/300, - /*kRendererMainFinished=*/300}); + IntToTimeDeltaVector( + expected_dispatch1, + std::vector<int>{/*kScrollsBlockingTouchDispatchedToRenderer=*/-1, + /*kArrivedInBrowserMain=*/300, + /*kArrivedInRendererCompositor=*/300, + /*kRendererCompositorStarted=*/300, + /*kRendererCompositorFinished=*/300, + /*kRendererMainStarted=*/300, + /*kRendererMainFinished=*/300}); base::TimeDelta expected_transition1 = base::Microseconds(300); std::vector<base::TimeDelta> expected_compositor1(kNumOfCompositorStages, base::Microseconds(-1)); @@ -1819,18 +1825,18 @@ std::vector<base::TimeDelta> expected_dispatch2(kNumDispatchStages, base::Microseconds(-1)); IntToTimeDeltaVector(expected_dispatch2, - std::vector<int>{262, 262, 300, 412, 225, 450}); + std::vector<int>{250, 262, 262, 300, 412, 225, 450}); base::TimeDelta expected_transition2 = base::Microseconds(390); std::vector<base::TimeDelta> expected_compositor2(kNumOfCompositorStages, base::Microseconds(-1)); IntToTimeDeltaVector(expected_compositor2, std::vector<int>{465, 500, 90, 720, 410, 742, 390}); - base::TimeDelta expected_total2 = base::Microseconds(5618); + base::TimeDelta expected_total2 = base::Microseconds(5868); CompositorFrameReporter::EventLatencyInfo actual_predictions2 = CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, kNumOfCompositorStages); IntToTimeDeltaVector(actual_predictions2.dispatch_durations, - std::vector<int>{250, 250, 300, 450, 200, 500}); + std::vector<int>{250, 250, 250, 300, 450, 200, 500}); actual_predictions2.transition_duration = base::Microseconds(420); IntToTimeDeltaVector(actual_predictions2.compositor_durations, std::vector<int>{520, 500, 90, 720, 410, 890, 420}); @@ -1841,18 +1847,18 @@ std::vector<base::TimeDelta> expected_dispatch3(kNumDispatchStages, base::Microseconds(-1)); IntToTimeDeltaVector(expected_dispatch3, - std::vector<int>{375, 375, 450, 300, 300, 300}); + std::vector<int>{400, 375, 375, 450, 300, 300, 300}); base::TimeDelta expected_transition3 = base::Microseconds(270); std::vector<base::TimeDelta> expected_compositor3(kNumOfCompositorStages, base::Microseconds(-1)); IntToTimeDeltaVector(expected_compositor3, std::vector<int>{300, 500, -1, -1, 410, 742, 390}); - base::TimeDelta expected_total3 = base::Microseconds(4712); + base::TimeDelta expected_total3 = base::Microseconds(5112); CompositorFrameReporter::EventLatencyInfo actual_predictions3 = CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, kNumOfCompositorStages); IntToTimeDeltaVector(actual_predictions3.dispatch_durations, - std::vector<int>{400, 400, 500, 300, -1, -1}); + std::vector<int>{400, 400, 400, 500, 300, -1, -1}); actual_predictions3.transition_duration = base::Microseconds(260); IntToTimeDeltaVector(actual_predictions3.compositor_durations, std::vector<int>{-1, 500, -1, -1, 410, 890, 420}); @@ -1939,21 +1945,23 @@ // Test with no previous stage predictions. std::vector<base::TimeDelta> expected_dispatch1(kNumDispatchStages, base::Microseconds(-1)); - IntToTimeDeltaVector(expected_dispatch1, - std::vector<int>{/*kArrivedInBrowserMain=*/300, - /*kArrivedInRendererCompositor=*/300, - /*kRendererCompositorStarted=*/300, - /*kRendererCompositorFinished=*/300, - /*kRendererMainStarted=*/300, - /*kRendererMainFinished=*/300}); + IntToTimeDeltaVector( + expected_dispatch1, + std::vector<int>{/*kScrollsBlockingTouchDispatchedToRenderer=*/-1, + /*kArrivedInBrowserMain=*/300, + /*kArrivedInRendererCompositor=*/300, + /*kRendererCompositorStarted=*/300, + /*kRendererCompositorFinished=*/300, + /*kRendererMainStarted=*/300, + /*kRenderePrMainFinished=*/300}); base::TimeDelta expected_transition1 = - base::Microseconds(300) + kTouchEventTransition; + base::Microseconds(302) + kTouchEventTransition; std::vector<base::TimeDelta> expected_compositor1(kNumOfCompositorStages, base::Microseconds(-1)); IntToTimeDeltaVector(expected_compositor1, std::vector<int>{300, -1, -1, -1, -1, 300, 300}); base::TimeDelta expected_total1 = - base::Microseconds(3000) + kTouchEventTransition; + base::Microseconds(3002) + kTouchEventTransition; CompositorFrameReporter::EventLatencyInfo actual_predictions1 = CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, kNumOfCompositorStages); @@ -1964,18 +1972,18 @@ std::vector<base::TimeDelta> expected_dispatch2(kNumDispatchStages, base::Microseconds(-1)); IntToTimeDeltaVector(expected_dispatch2, - std::vector<int>{262, 262, 300, 412, 225, 450}); + std::vector<int>{250, 262, 262, 300, 412, 225, 450}); base::TimeDelta expected_transition2 = base::Microseconds(393); std::vector<base::TimeDelta> expected_compositor2(kNumOfCompositorStages, base::Microseconds(-1)); IntToTimeDeltaVector(expected_compositor2, std::vector<int>{465, 500, 90, 720, 410, 742, 390}); - base::TimeDelta expected_total2 = base::Microseconds(5621); + base::TimeDelta expected_total2 = base::Microseconds(5871); CompositorFrameReporter::EventLatencyInfo actual_predictions2 = CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, kNumOfCompositorStages); IntToTimeDeltaVector(actual_predictions2.dispatch_durations, - std::vector<int>{250, 250, 300, 450, 200, 500}); + std::vector<int>{250, 250, 250, 300, 450, 200, 500}); actual_predictions2.transition_duration = base::Microseconds(420); IntToTimeDeltaVector(actual_predictions2.compositor_durations, std::vector<int>{520, 500, 90, 720, 410, 890, 420}); @@ -2052,7 +2060,7 @@ CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, kNumOfCompositorStages); IntToTimeDeltaVector(expected_predictions1.dispatch_durations, - std::vector<int>{300, 300, 300, 300, 50000, 300}); + std::vector<int>{-1, 300, 300, 300, 300, 50000, 300}); expected_predictions1.transition_duration = base::Microseconds(300); IntToTimeDeltaVector(expected_predictions1.compositor_durations, std::vector<int>{300, -1, -1, -1, -1, 50000, 300}); @@ -2075,17 +2083,17 @@ CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, kNumOfCompositorStages); IntToTimeDeltaVector(expected_predictions2.dispatch_durations, - std::vector<int>{300, 300, 300, 300, 12725, 300}); + std::vector<int>{300, 300, 300, 300, 300, 12725, 300}); expected_predictions2.transition_duration = base::Microseconds(300); IntToTimeDeltaVector(expected_predictions2.compositor_durations, std::vector<int>{300, -1, -1, -1, -1, 50000, 300}); - expected_predictions2.total_duration = base::Microseconds(65125); + expected_predictions2.total_duration = base::Microseconds(65425); CompositorFrameReporter::EventLatencyInfo actual_predictions2 = CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, kNumOfCompositorStages); IntToTimeDeltaVector(actual_predictions2.dispatch_durations, - std::vector<int>{300, 300, 300, 300, 300, 300}); + std::vector<int>{300, 300, 300, 300, 300, 300, 300}); actual_predictions2.transition_duration = base::Microseconds(300); IntToTimeDeltaVector(actual_predictions2.compositor_durations, std::vector<int>{300, -1, -1, -1, -1, 50000, 300}); @@ -2103,17 +2111,17 @@ CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, kNumOfCompositorStages); IntToTimeDeltaVector(expected_predictions3.dispatch_durations, - std::vector<int>{300, 300, 300, 300, 12725, 300}); + std::vector<int>{300, 300, 300, 300, 300, 12725, 300}); expected_predictions3.transition_duration = base::Microseconds(300); IntToTimeDeltaVector(expected_predictions3.compositor_durations, std::vector<int>{300, -1, -1, -1, -1, 12725, 300}); - expected_predictions3.total_duration = base::Microseconds(27850); + expected_predictions3.total_duration = base::Microseconds(28150); CompositorFrameReporter::EventLatencyInfo actual_predictions3 = CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, kNumOfCompositorStages); IntToTimeDeltaVector(actual_predictions3.dispatch_durations, - std::vector<int>{300, 300, 300, 300, 300, 300}); + std::vector<int>{300, 300, 300, 300, 300, 300, 300}); actual_predictions3.transition_duration = base::Microseconds(300); IntToTimeDeltaVector(actual_predictions3.compositor_durations, std::vector<int>{300, -1, -1, -1, -1, 300, 300}); @@ -2132,21 +2140,16 @@ actual_predictions1.dispatch_durations[i]); EXPECT_EQ(expected_predictions2.dispatch_durations[i], actual_predictions2.dispatch_durations[i]); - ; EXPECT_EQ(expected_predictions3.dispatch_durations[i], actual_predictions3.dispatch_durations[i]); - ; } for (int i = 0; i < kNumOfCompositorStages; i++) { EXPECT_EQ(expected_predictions1.compositor_durations[i], actual_predictions1.compositor_durations[i]); - ; EXPECT_EQ(expected_predictions2.compositor_durations[i], actual_predictions2.compositor_durations[i]); - ; EXPECT_EQ(expected_predictions3.compositor_durations[i], actual_predictions3.compositor_durations[i]); - ; } EXPECT_EQ(expected_predictions1.transition_duration, actual_predictions1.transition_duration); @@ -2217,7 +2220,7 @@ CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, kNumOfCompositorStages); IntToTimeDeltaVector(expected_predictions1.dispatch_durations, - std::vector<int>{10300, 262, -1, -1, 262, 42500}); + std::vector<int>{-1, 10300, 262, -1, -1, 262, 42500}); expected_predictions1.transition_duration = base::Microseconds(300); IntToTimeDeltaVector(expected_predictions1.compositor_durations, std::vector<int>{300, -1, -1, -1, -1, 15200, 300}); @@ -2227,7 +2230,7 @@ CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, kNumOfCompositorStages); IntToTimeDeltaVector(actual_predictions1.dispatch_durations, - std::vector<int>{400, 300, -1, -1, 300, 40000}); + std::vector<int>{-1, 400, 300, -1, -1, 300, 40000}); actual_predictions1.transition_duration = base::Microseconds(300); IntToTimeDeltaVector(actual_predictions1.compositor_durations, std::vector<int>{300, -1, -1, -1, -1, 3600, 300}); @@ -2248,18 +2251,18 @@ CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, kNumOfCompositorStages); IntToTimeDeltaVector(expected_predictions2.dispatch_durations, - std::vector<int>{10225, 262, -1, -1, 262, 12725}); + std::vector<int>{300, 10225, 262, -1, -1, 262, 12725}); expected_predictions2.transition_duration = base::Microseconds(300); IntToTimeDeltaVector(expected_predictions2.compositor_durations, std::vector<int>{300, -1, -1, -1, -1, 12725, 300}); - expected_predictions2.total_duration = base::Microseconds(37099); + expected_predictions2.total_duration = base::Microseconds(37399); CompositorFrameReporter::EventLatencyInfo actual_predictions2 = CompositorFrameReporter::EventLatencyInfo(kNumDispatchStages, kNumOfCompositorStages); IntToTimeDeltaVector(actual_predictions2.dispatch_durations, - std::vector<int>{300, 300, -1, -1, 300, 300}); + std::vector<int>{300, 300, 300, -1, -1, 300, 300}); actual_predictions2.transition_duration = base::Microseconds(300); IntToTimeDeltaVector(actual_predictions2.compositor_durations, std::vector<int>{300, -1, -1, -1, -1, 300, 300});
diff --git a/cc/metrics/compositor_frame_reporting_controller_unittest.cc b/cc/metrics/compositor_frame_reporting_controller_unittest.cc index 654e1dc..142335b 100644 --- a/cc/metrics/compositor_frame_reporting_controller_unittest.cc +++ b/cc/metrics/compositor_frame_reporting_controller_unittest.cc
@@ -253,9 +253,11 @@ std::unique_ptr<EventMetrics> CreateEventMetrics(ui::EventType type) { const base::TimeTicks event_time = AdvanceNowByMs(10); + const base::TimeTicks arrived_in_browser_main_timestamp = AdvanceNowByMs(3); AdvanceNowByMs(10); - return SetupEventMetrics( - EventMetrics::CreateForTesting(type, event_time, &test_tick_clock_)); + return SetupEventMetrics(EventMetrics::CreateForTesting( + type, event_time, arrived_in_browser_main_timestamp, + &test_tick_clock_)); } std::unique_ptr<EventMetrics> CreateScrollBeginEventMetrics(
diff --git a/cc/metrics/event_latency_tracing_recorder.cc b/cc/metrics/event_latency_tracing_recorder.cc index 6877c9d6..7d8d11ac0 100644 --- a/cc/metrics/event_latency_tracing_recorder.cc +++ b/cc/metrics/event_latency_tracing_recorder.cc
@@ -63,12 +63,26 @@ switch (start_stage) { case EventMetrics::DispatchStage::kGenerated: switch (end_stage) { + case EventMetrics::DispatchStage:: + kScrollsBlockingTouchDispatchedToRenderer: case EventMetrics::DispatchStage::kArrivedInBrowserMain: return "GenerationToBrowserMain"; case EventMetrics::DispatchStage::kArrivedInRendererCompositor: return "GenerationToRendererCompositor"; default: - NOTREACHED(); + NOTREACHED() << static_cast<int>(end_stage); + return ""; + } + case EventMetrics::DispatchStage::kScrollsBlockingTouchDispatchedToRenderer: + switch (end_stage) { + case EventMetrics::DispatchStage::kArrivedInBrowserMain: + // This stage can only be in a Scroll EventLatency. It means a path of + // a corresponding blocking TouchMove from BrowserMain To Renderer To + // BrowserMain. Look at the corresponding TouchMove EventLatency for + // a more detailed breakdown of this stage. + return "TouchRendererHandlingToBrowserMain"; + default: + NOTREACHED() << static_cast<int>(end_stage); return ""; } case EventMetrics::DispatchStage::kArrivedInBrowserMain: @@ -82,7 +96,7 @@ case EventMetrics::DispatchStage::kRendererMainStarted: return "RendererCompositorToMain"; default: - NOTREACHED(); + NOTREACHED() << static_cast<int>(end_stage); return ""; } case EventMetrics::DispatchStage::kRendererCompositorStarted:
diff --git a/cc/metrics/event_metrics.cc b/cc/metrics/event_metrics.cc index 14d56dd6..882896b 100644 --- a/cc/metrics/event_metrics.cc +++ b/cc/metrics/event_metrics.cc
@@ -213,6 +213,14 @@ // static std::unique_ptr<EventMetrics> EventMetrics::Create(ui::EventType type, base::TimeTicks timestamp) { + return Create(type, timestamp, base::TimeTicks()); +} + +// static +std::unique_ptr<EventMetrics> EventMetrics::Create( + ui::EventType type, + base::TimeTicks timestamp, + base::TimeTicks arrived_in_browser_main_timestamp) { // TODO(crbug.com/1157090): We expect that `timestamp` is not null, but there // seems to be some tests that are emitting events with null timestamp. We // should investigate and try to fix those cases and add a `DCHECK` here to @@ -221,7 +229,8 @@ DCHECK(!IsGestureScroll(type) && !IsGesturePinch(type)); std::unique_ptr<EventMetrics> metrics = - CreateInternal(type, timestamp, base::DefaultTickClock::GetInstance()); + CreateInternal(type, timestamp, arrived_in_browser_main_timestamp, + base::DefaultTickClock::GetInstance()); if (!metrics) return nullptr; @@ -234,11 +243,12 @@ std::unique_ptr<EventMetrics> EventMetrics::CreateForTesting( ui::EventType type, base::TimeTicks timestamp, + base::TimeTicks arrived_in_browser_main_timestamp, const base::TickClock* tick_clock) { DCHECK(!timestamp.is_null()); std::unique_ptr<EventMetrics> metrics = - CreateInternal(type, timestamp, tick_clock); + CreateInternal(type, timestamp, base::TimeTicks(), tick_clock); if (!metrics) return nullptr; @@ -260,8 +270,8 @@ if (!existing) return nullptr; - std::unique_ptr<EventMetrics> metrics = - CreateInternal(type, base::TimeTicks(), existing->tick_clock_); + std::unique_ptr<EventMetrics> metrics = CreateInternal( + type, base::TimeTicks(), base::TimeTicks(), existing->tick_clock_); if (!metrics) return nullptr; @@ -276,14 +286,16 @@ std::unique_ptr<EventMetrics> EventMetrics::CreateInternal( ui::EventType type, base::TimeTicks timestamp, + base::TimeTicks arrived_in_browser_main_timestamp, const base::TickClock* tick_clock) { absl::optional<EventType> interesting_type = ToInterestingEventType(type, /*scroll_is_inertial=*/absl::nullopt, /*scroll_update_type=*/absl::nullopt); if (!interesting_type) return nullptr; - return base::WrapUnique( - new EventMetrics(*interesting_type, timestamp, tick_clock)); + return base::WrapUnique(new EventMetrics(*interesting_type, timestamp, + arrived_in_browser_main_timestamp, + tick_clock)); } EventMetrics::EventMetrics(EventType type, @@ -343,6 +355,13 @@ tick_clock_->NowTicks(); } +void EventMetrics::SetDispatchStageTimestamp(DispatchStage stage, + base::TimeTicks timestamp) { + DCHECK(dispatch_stage_timestamps_[static_cast<size_t>(stage)].is_null()); + + dispatch_stage_timestamps_[static_cast<size_t>(stage)] = timestamp; +} + base::TimeTicks EventMetrics::GetDispatchStageTimestamp( DispatchStage stage) const { return dispatch_stage_timestamps_[static_cast<size_t>(stage)]; @@ -405,7 +424,8 @@ ui::ScrollInputType input_type, bool is_inertial, base::TimeTicks timestamp, - base::TimeTicks arrived_in_browser_main_timestamp) { + base::TimeTicks arrived_in_browser_main_timestamp, + base::TimeTicks blocking_touch_dispatched_to_renderer) { // TODO(crbug.com/1157090): We expect that `timestamp` is not null, but there // seems to be some tests that are emitting events with null timestamp. We // should investigate and try to fix those cases and add a `DCHECK` here to @@ -421,6 +441,9 @@ metrics->SetDispatchStageTimestamp( DispatchStage::kArrivedInRendererCompositor); + metrics->SetDispatchStageTimestamp( + DispatchStage::kScrollsBlockingTouchDispatchedToRenderer, + blocking_touch_dispatched_to_renderer); return metrics; } @@ -430,7 +453,9 @@ ui::ScrollInputType input_type, bool is_inertial, base::TimeTicks timestamp) { - return Create(type, input_type, is_inertial, timestamp, base::TimeTicks()); + return Create(type, input_type, is_inertial, timestamp, + /*arrived_in_browser_main_timestamp=*/base::TimeTicks(), + /*blocking_touch_dispatched_to_renderer=*/base::TimeTicks()); } // static @@ -544,7 +569,8 @@ float delta, base::TimeTicks timestamp, base::TimeTicks arrived_in_browser_main_timestamp, - TraceId trace_id) { + TraceId trace_id, + base::TimeTicks blocking_touch_dispatched_to_renderer) { // TODO(crbug.com/1157090): We expect that `timestamp` is not null, but there // seems to be some tests that are emitting events with null timestamp. We // should investigate and try to fix those cases and add a `DCHECK` here to @@ -561,6 +587,9 @@ metrics->SetDispatchStageTimestamp( DispatchStage::kArrivedInRendererCompositor); + metrics->SetDispatchStageTimestamp( + DispatchStage::kScrollsBlockingTouchDispatchedToRenderer, + blocking_touch_dispatched_to_renderer); return metrics; } @@ -573,8 +602,10 @@ float delta, base::TimeTicks timestamp, TraceId trace_id) { - return Create(type, input_type, is_inertial, scroll_update_type, delta, - timestamp, base::TimeTicks(), trace_id); + return Create( + type, input_type, is_inertial, scroll_update_type, delta, timestamp, + /*arrived_in_browser_main_timestamp=*/base::TimeTicks(), trace_id, + /*blocking_touch_dispatched_to_renderer=*/base::TimeTicks()); } // static
diff --git a/cc/metrics/event_metrics.h b/cc/metrics/event_metrics.h index 1b1d68b..480fe35 100644 --- a/cc/metrics/event_metrics.h +++ b/cc/metrics/event_metrics.h
@@ -68,6 +68,11 @@ // Stages of event dispatch in different processes/threads. enum class DispatchStage { kGenerated, + // 'kScrollsBlockingTouchDispatchedToRenderer' is used by Scroll events to + // understand when a corresponding TouchMove event arrived in the Browser + // Main. If the related TouchMove wasn't blocking, this stage field is not + // set. + kScrollsBlockingTouchDispatchedToRenderer, kArrivedInBrowserMain, kArrivedInRendererCompositor, kRendererCompositorStarted, @@ -77,16 +82,22 @@ kMaxValue = kRendererMainFinished, }; + static std::unique_ptr<EventMetrics> Create(ui::EventType type, + base::TimeTicks timestamp); + // Returns a new instance if the event is of a type we are interested in. // Otherwise, returns `nullptr`. For scroll and pinch events, use the // appropriate subcalss instead. - static std::unique_ptr<EventMetrics> Create(ui::EventType type, - base::TimeTicks timestamp); + static std::unique_ptr<EventMetrics> Create( + ui::EventType type, + base::TimeTicks timestamp, + base::TimeTicks arrived_in_browser_main_timestamp); // Similar to `Create()` with an extra `base::TickClock` to use in tests. static std::unique_ptr<EventMetrics> CreateForTesting( ui::EventType type, base::TimeTicks timestamp, + base::TimeTicks arrived_in_browser_main_timestamp, const base::TickClock* tick_clock); // Used to create an instance for an event generated based on an existing @@ -182,6 +193,9 @@ void CopyTimestampsFrom(const EventMetrics& other, DispatchStage last_dispatch_stage); + void SetDispatchStageTimestamp(DispatchStage stage, + base::TimeTicks timestamp); + private: friend class ScrollEventMetrics; friend class ScrollUpdateEventMetrics; @@ -189,6 +203,7 @@ static std::unique_ptr<EventMetrics> CreateInternal( ui::EventType type, base::TimeTicks timestamp, + base::TimeTicks arrived_in_browser_main_timestamp, const base::TickClock* tick_clock); EventType type_; @@ -232,6 +247,8 @@ // Returns a new instance if the event is of a type we are interested in. // Otherwise, returns `nullptr`. Should only be used for scroll events other // than scroll-update. + // The |blocking_touch_dispatched_to_renderer| must be not null only for + // scrolls which corresponding TouchMove was blocking. // // TODO(b/224960731): Fix tests and stop supporting the case when // `arrived_in_browser_main_timestamp` is null. @@ -240,11 +257,13 @@ ui::ScrollInputType input_type, bool is_inertial, base::TimeTicks timestamp, - base::TimeTicks arrived_in_browser_main_timestamp); + base::TimeTicks arrived_in_browser_main_timestamp, + base::TimeTicks blocking_touch_dispatched_to_renderer); // Prefer to use `Create()` above. This method is used only by the Browser // process which have own breakdowns. - // Similar to `Create()` above but doesn't set kArrivedInBrowserMain. + // Similar to `Create()` above but doesn't set kArrivedInBrowserMain and + // kScrollsBlockingTouchDispatchedToRenderer. static std::unique_ptr<ScrollEventMetrics> CreateForBrowser( ui::EventType type, ui::ScrollInputType input_type, @@ -319,6 +338,8 @@ // Returns a new instance if the event is of a type we are interested in. // Otherwise, returns `nullptr`. Should only be used for scroll-update events. + // The |blocking_touch_dispatched_to_renderer| must be not null only for + // scrolls which corresponding TouchMove was blocking. // // TODO(b/224960731): Fix tests and stop supporting the case when // `arrived_in_browser_main_timestamp` is null. @@ -330,11 +351,13 @@ float delta, base::TimeTicks timestamp, base::TimeTicks arrived_in_browser_main_timestamp, - TraceId trace_id); + TraceId trace_id, + base::TimeTicks blocking_touch_dispatched_to_renderer); // Prefer to use `Create()` above. This method is used only by the Browser // process which have own breakdowns. - // Similar to `Create()` above but doesn't set kArrivedInBrowserMain. + // Similar to `Create()` above but doesn't set kArrivedInBrowserMain and + // kScrollsBlockingTouchDispatchedToRenderer. static std::unique_ptr<ScrollUpdateEventMetrics> CreateForBrowser( ui::EventType type, ui::ScrollInputType input_type,
diff --git a/cc/metrics/event_metrics_unittest.cc b/cc/metrics/event_metrics_unittest.cc index 758e9c7..81e88c7 100644 --- a/cc/metrics/event_metrics_unittest.cc +++ b/cc/metrics/event_metrics_unittest.cc
@@ -25,6 +25,7 @@ TEST_F(EventMetricsTest, ScrollBeginCreateWithNullBeginRwhTime) { // Arrange base::TimeTicks event_time = base::TimeTicks::Now() - base::Microseconds(100); + base::TimeTicks blocking_touch_dispatched_to_renderer_timestamp; base::TimeTicks arrived_in_browser_main_timestamp; base::TimeTicks now = base::TimeTicks::Now(); @@ -32,7 +33,8 @@ std::unique_ptr<ScrollEventMetrics> scroll_event_metric = ScrollEventMetrics::Create( ui::ET_GESTURE_SCROLL_BEGIN, ui::ScrollInputType::kTouchscreen, - /*is_inertial=*/false, event_time, arrived_in_browser_main_timestamp); + /*is_inertial=*/false, event_time, arrived_in_browser_main_timestamp, + blocking_touch_dispatched_to_renderer_timestamp); // Assert EXPECT_EQ(event_time, scroll_event_metric->GetDispatchStageTimestamp( @@ -43,6 +45,11 @@ // not set EXPECT_TRUE(scroll_event_metric ->GetDispatchStageTimestamp( + EventMetrics::DispatchStage:: + kScrollsBlockingTouchDispatchedToRenderer) + .is_null()); + EXPECT_TRUE(scroll_event_metric + ->GetDispatchStageTimestamp( EventMetrics::DispatchStage::kArrivedInBrowserMain) .is_null()); EXPECT_TRUE(scroll_event_metric @@ -66,6 +73,8 @@ TEST_F(EventMetricsTest, ScrollBeginCreate) { // Arrange base::TimeTicks event_time = base::TimeTicks::Now() - base::Microseconds(100); + base::TimeTicks blocking_touch_dispatched_to_renderer_timestamp = + base::TimeTicks::Now() - base::Microseconds(70); base::TimeTicks arrived_in_browser_main_timestamp = base::TimeTicks::Now() - base::Microseconds(50); base::TimeTicks now = base::TimeTicks::Now(); @@ -74,11 +83,16 @@ std::unique_ptr<ScrollEventMetrics> scroll_event_metric = ScrollEventMetrics::Create( ui::ET_GESTURE_SCROLL_BEGIN, ui::ScrollInputType::kTouchscreen, - /*is_inertial=*/false, event_time, arrived_in_browser_main_timestamp); + /*is_inertial=*/false, event_time, arrived_in_browser_main_timestamp, + blocking_touch_dispatched_to_renderer_timestamp); // Assert EXPECT_EQ(event_time, scroll_event_metric->GetDispatchStageTimestamp( EventMetrics::DispatchStage::kGenerated)); + EXPECT_EQ(blocking_touch_dispatched_to_renderer_timestamp, + scroll_event_metric->GetDispatchStageTimestamp( + EventMetrics::DispatchStage:: + kScrollsBlockingTouchDispatchedToRenderer)); EXPECT_EQ(arrived_in_browser_main_timestamp, scroll_event_metric->GetDispatchStageTimestamp( EventMetrics::DispatchStage::kArrivedInBrowserMain)); @@ -107,12 +121,15 @@ TEST_F(EventMetricsTest, ScrollBeginCreateFromExisting) { // Arrange base::TimeTicks event_time = base::TimeTicks::Now() - base::Microseconds(100); + base::TimeTicks blocking_touch_dispatched_to_renderer_timestamp = + base::TimeTicks::Now() - base::Microseconds(70); base::TimeTicks arrived_in_browser_main_timestamp = base::TimeTicks::Now() - base::Microseconds(50); std::unique_ptr<ScrollEventMetrics> scroll_metric = ScrollEventMetrics::Create( ui::ET_GESTURE_SCROLL_BEGIN, ui::ScrollInputType::kTouchscreen, - /*is_inertial=*/false, event_time, arrived_in_browser_main_timestamp); + /*is_inertial=*/false, event_time, arrived_in_browser_main_timestamp, + blocking_touch_dispatched_to_renderer_timestamp); // Act std::unique_ptr<ScrollEventMetrics> copy_scroll_metric = @@ -128,6 +145,12 @@ copy_scroll_metric->GetDispatchStageTimestamp( EventMetrics::DispatchStage::kGenerated)); EXPECT_EQ(scroll_metric->GetDispatchStageTimestamp( + EventMetrics::DispatchStage:: + kScrollsBlockingTouchDispatchedToRenderer), + copy_scroll_metric->GetDispatchStageTimestamp( + EventMetrics::DispatchStage:: + kScrollsBlockingTouchDispatchedToRenderer)); + EXPECT_EQ(scroll_metric->GetDispatchStageTimestamp( EventMetrics::DispatchStage::kArrivedInBrowserMain), copy_scroll_metric->GetDispatchStageTimestamp( EventMetrics::DispatchStage::kArrivedInBrowserMain)); @@ -161,6 +184,7 @@ TEST_F(EventMetricsTest, ScrollUpdateCreateWithNullBeginRwhTime) { // Arrange base::TimeTicks event_time = base::TimeTicks::Now() - base::Microseconds(100); + base::TimeTicks blocking_touch_dispatched_to_renderer_timestamp; base::TimeTicks arrived_in_browser_main_timestamp; base::TimeTicks now = base::TimeTicks::Now(); TraceId trace_id(123); @@ -171,7 +195,8 @@ ui::ET_GESTURE_SCROLL_UPDATE, ui::ScrollInputType::kTouchscreen, /*is_inertial=*/false, ScrollUpdateEventMetrics::ScrollUpdateType::kContinued, /*delta=*/0.4, - event_time, arrived_in_browser_main_timestamp, trace_id); + event_time, arrived_in_browser_main_timestamp, trace_id, + blocking_touch_dispatched_to_renderer_timestamp); // Assert EXPECT_EQ(trace_id, scroll_event_metric->trace_id()); @@ -183,6 +208,11 @@ // not set EXPECT_TRUE(scroll_event_metric ->GetDispatchStageTimestamp( + EventMetrics::DispatchStage:: + kScrollsBlockingTouchDispatchedToRenderer) + .is_null()); + EXPECT_TRUE(scroll_event_metric + ->GetDispatchStageTimestamp( EventMetrics::DispatchStage::kArrivedInBrowserMain) .is_null()); EXPECT_TRUE(scroll_event_metric @@ -206,6 +236,8 @@ TEST_F(EventMetricsTest, ScrollUpdateCreate) { // Arrange base::TimeTicks event_time = base::TimeTicks::Now() - base::Microseconds(100); + base::TimeTicks blocking_touch_dispatched_to_renderer_timestamp = + base::TimeTicks::Now() - base::Microseconds(70); base::TimeTicks arrived_in_browser_main_timestamp = base::TimeTicks::Now() - base::Microseconds(50); base::TimeTicks now = base::TimeTicks::Now(); @@ -217,12 +249,17 @@ ui::ET_GESTURE_SCROLL_UPDATE, ui::ScrollInputType::kTouchscreen, /*is_inertial=*/false, ScrollUpdateEventMetrics::ScrollUpdateType::kContinued, /*delta=*/0.4, - event_time, arrived_in_browser_main_timestamp, TraceId(trace_id)); + event_time, arrived_in_browser_main_timestamp, TraceId(trace_id), + blocking_touch_dispatched_to_renderer_timestamp); // Assert EXPECT_EQ(trace_id, scroll_event_metric->trace_id()); EXPECT_EQ(event_time, scroll_event_metric->GetDispatchStageTimestamp( EventMetrics::DispatchStage::kGenerated)); + EXPECT_EQ(blocking_touch_dispatched_to_renderer_timestamp, + scroll_event_metric->GetDispatchStageTimestamp( + EventMetrics::DispatchStage:: + kScrollsBlockingTouchDispatchedToRenderer)); EXPECT_EQ(arrived_in_browser_main_timestamp, scroll_event_metric->GetDispatchStageTimestamp( EventMetrics::DispatchStage::kArrivedInBrowserMain)); @@ -251,6 +288,8 @@ TEST_F(EventMetricsTest, ScrollUpdateCreateFromExisting) { // Arrange base::TimeTicks event_time = base::TimeTicks::Now() - base::Microseconds(100); + base::TimeTicks blocking_touch_dispatched_to_renderer_timestamp = + base::TimeTicks::Now() - base::Microseconds(70); base::TimeTicks arrived_in_browser_main_timestamp = base::TimeTicks::Now() - base::Microseconds(50); TraceId trace_id(123); @@ -259,7 +298,8 @@ ui::ET_GESTURE_SCROLL_UPDATE, ui::ScrollInputType::kTouchscreen, /*is_inertial=*/false, ScrollUpdateEventMetrics::ScrollUpdateType::kContinued, /*delta=*/0.4, - event_time, arrived_in_browser_main_timestamp, trace_id); + event_time, arrived_in_browser_main_timestamp, trace_id, + blocking_touch_dispatched_to_renderer_timestamp); // Act std::unique_ptr<ScrollUpdateEventMetrics> copy_scroll_metric = @@ -277,6 +317,12 @@ copy_scroll_metric->GetDispatchStageTimestamp( EventMetrics::DispatchStage::kGenerated)); EXPECT_EQ(scroll_metric->GetDispatchStageTimestamp( + EventMetrics::DispatchStage:: + kScrollsBlockingTouchDispatchedToRenderer), + copy_scroll_metric->GetDispatchStageTimestamp( + EventMetrics::DispatchStage:: + kScrollsBlockingTouchDispatchedToRenderer)); + EXPECT_EQ(scroll_metric->GetDispatchStageTimestamp( EventMetrics::DispatchStage::kArrivedInBrowserMain), copy_scroll_metric->GetDispatchStageTimestamp( EventMetrics::DispatchStage::kArrivedInBrowserMain)); @@ -307,5 +353,95 @@ EventMetrics::DispatchStage::kRendererMainFinished)); } +TEST_F(EventMetricsTest, Create) { + // Arrange + base::TimeTicks event_time = base::TimeTicks::Now() - base::Microseconds(100); + base::TimeTicks arrived_in_browser_main_timestamp = + base::TimeTicks::Now() - base::Microseconds(50); + base::TimeTicks now = base::TimeTicks::Now(); + + // Act + std::unique_ptr<EventMetrics> event_metric = EventMetrics::Create( + ui::ET_TOUCH_MOVED, event_time, arrived_in_browser_main_timestamp); + + // Assert + EXPECT_EQ(event_time, event_metric->GetDispatchStageTimestamp( + EventMetrics::DispatchStage::kGenerated)); + EXPECT_EQ(arrived_in_browser_main_timestamp, + event_metric->GetDispatchStageTimestamp( + EventMetrics::DispatchStage::kArrivedInBrowserMain)); + EXPECT_LE(now, + event_metric->GetDispatchStageTimestamp( + EventMetrics::DispatchStage::kArrivedInRendererCompositor)); + // not set + EXPECT_TRUE(event_metric + ->GetDispatchStageTimestamp( + EventMetrics::DispatchStage::kRendererCompositorStarted) + .is_null()); + EXPECT_TRUE(event_metric + ->GetDispatchStageTimestamp( + EventMetrics::DispatchStage::kRendererCompositorFinished) + .is_null()); + EXPECT_TRUE(event_metric + ->GetDispatchStageTimestamp( + EventMetrics::DispatchStage::kRendererMainStarted) + .is_null()); + EXPECT_TRUE(event_metric + ->GetDispatchStageTimestamp( + EventMetrics::DispatchStage::kRendererMainFinished) + .is_null()); +} + +TEST_F(EventMetricsTest, CreateFromExisting) { + // Arrange + base::TimeTicks event_time = base::TimeTicks::Now() - base::Microseconds(100); + base::TimeTicks arrived_in_browser_main_timestamp = + base::TimeTicks::Now() - base::Microseconds(50); + std::unique_ptr<EventMetrics> event_metric = EventMetrics::Create( + ui::ET_TOUCH_MOVED, event_time, arrived_in_browser_main_timestamp); + + // Act + std::unique_ptr<EventMetrics> copy_event_metric = + EventMetrics::CreateFromExisting( + ui::ET_TOUCH_MOVED, + EventMetrics::DispatchStage::kRendererMainFinished, + event_metric.get()); + + // Assert + EXPECT_EQ(event_metric->GetDispatchStageTimestamp( + EventMetrics::DispatchStage::kGenerated), + copy_event_metric->GetDispatchStageTimestamp( + EventMetrics::DispatchStage::kGenerated)); + EXPECT_EQ(event_metric->GetDispatchStageTimestamp( + EventMetrics::DispatchStage::kArrivedInBrowserMain), + copy_event_metric->GetDispatchStageTimestamp( + EventMetrics::DispatchStage::kArrivedInBrowserMain)); + + EXPECT_EQ(event_metric->GetDispatchStageTimestamp( + EventMetrics::DispatchStage::kArrivedInRendererCompositor), + copy_event_metric->GetDispatchStageTimestamp( + EventMetrics::DispatchStage::kArrivedInRendererCompositor)); + + EXPECT_EQ(event_metric->GetDispatchStageTimestamp( + EventMetrics::DispatchStage::kRendererCompositorStarted), + copy_event_metric->GetDispatchStageTimestamp( + EventMetrics::DispatchStage::kRendererCompositorStarted)); + + EXPECT_EQ(event_metric->GetDispatchStageTimestamp( + EventMetrics::DispatchStage::kRendererCompositorFinished), + copy_event_metric->GetDispatchStageTimestamp( + EventMetrics::DispatchStage::kRendererCompositorFinished)); + + EXPECT_EQ(event_metric->GetDispatchStageTimestamp( + EventMetrics::DispatchStage::kRendererMainStarted), + copy_event_metric->GetDispatchStageTimestamp( + EventMetrics::DispatchStage::kRendererMainStarted)); + + EXPECT_EQ(event_metric->GetDispatchStageTimestamp( + EventMetrics::DispatchStage::kRendererMainFinished), + copy_event_metric->GetDispatchStageTimestamp( + EventMetrics::DispatchStage::kRendererMainFinished)); +} + } // namespace } // namespace cc
diff --git a/cc/metrics/events_metrics_manager_unittest.cc b/cc/metrics/events_metrics_manager_unittest.cc index 2692020..713705c 100644 --- a/cc/metrics/events_metrics_manager_unittest.cc +++ b/cc/metrics/events_metrics_manager_unittest.cc
@@ -54,8 +54,12 @@ std::unique_ptr<EventMetrics> CreateEventMetrics(ui::EventType type) { test_tick_clock_.Advance(base::Microseconds(10)); base::TimeTicks event_time = test_tick_clock_.NowTicks(); + test_tick_clock_.Advance(base::Microseconds(5)); + base::TimeTicks arrived_in_browser_main_timestamp = + test_tick_clock_.NowTicks(); test_tick_clock_.Advance(base::Microseconds(10)); - return EventMetrics::CreateForTesting(type, event_time, &test_tick_clock_); + return EventMetrics::CreateForTesting( + type, event_time, arrived_in_browser_main_timestamp, &test_tick_clock_); } EventsMetricsManager manager_;
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc index 4b3b837..1c4fb260 100644 --- a/cc/trees/layer_tree_host_impl_unittest.cc +++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -14370,7 +14370,8 @@ : ScrollUpdateEventMetrics::ScrollUpdateType::kContinued, /*delta=*/10.0f, base::TimeTicks::Now(), base::TimeTicks::Now() + base::Milliseconds(1), - /*trace_id*/ base::IdType64<class ui::LatencyInfo>(123))); + /*trace_id*/ base::IdType64<class ui::LatencyInfo>(123), + base::TimeTicks())); host_impl_->active_tree()->AppendEventsMetricsFromMainThread( std::move(events_metrics));
diff --git a/cc/trees/ukm_manager.cc b/cc/trees/ukm_manager.cc index d5c2704..fa6f393 100644 --- a/cc/trees/ukm_manager.cc +++ b/cc/trees/ukm_manager.cc
@@ -206,6 +206,8 @@ switch (dispatch_stage) { case EventMetrics::DispatchStage::kGenerated: switch (end_stage) { + case EventMetrics::DispatchStage:: + kScrollsBlockingTouchDispatchedToRenderer: case EventMetrics::DispatchStage::kArrivedInBrowserMain: // Will build the `GenerationToRendererCompositor` metric on the // `kArrivedInBrowserMain` stage. @@ -218,6 +220,9 @@ break; } break; + case EventMetrics::DispatchStage:: + kScrollsBlockingTouchDispatchedToRenderer: + break; case EventMetrics::DispatchStage::kArrivedInBrowserMain: DCHECK_EQ(end_stage, EventMetrics::DispatchStage::kArrivedInRendererCompositor);
diff --git a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/PasswordGenerationIntegrationTest.java b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/PasswordGenerationIntegrationTest.java index fc92d45..2d55ec1b 100644 --- a/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/PasswordGenerationIntegrationTest.java +++ b/chrome/android/features/keyboard_accessory/javatests/src/org/chromium/chrome/browser/keyboard_accessory/PasswordGenerationIntegrationTest.java
@@ -242,9 +242,7 @@ } private void focusField(String node) throws TimeoutException, InterruptedException { - DOMUtils.focusNode(mHelper.getWebContents(), node); - TestThreadUtils.runOnUiThreadBlocking( - () -> { mHelper.getWebContents().scrollFocusedEditableNodeIntoView(); }); + DOMUtils.clickNode(mHelper.getWebContents(), node); } private void clickNode(String node) throws InterruptedException, TimeoutException {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncErrorNotifier.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncErrorNotifier.java index 7e0ba50..0ca6d610 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncErrorNotifier.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/SyncErrorNotifier.java
@@ -28,7 +28,6 @@ import org.chromium.components.browser_ui.notifications.NotificationMetadata; import org.chromium.components.browser_ui.notifications.NotificationWrapper; import org.chromium.components.browser_ui.notifications.PendingIntentProvider; -import org.chromium.components.sync.PassphraseType; import org.chromium.components.sync.TrustedVaultUserActionTriggerForUMA; import java.lang.annotation.Retention; @@ -160,39 +159,30 @@ } private @NotificationState int computeGoalNotificationState() { - // TODO(crbug.com/1402252): Remove returns with else branches (left them for a better diff). if (!mSyncService.isSyncFeatureEnabled()) { + // Error notifications are only currently shown to syncing users, even though passphrase + // and trusted vault key errors still apply to signed-in users. return NotificationState.HIDDEN; - } else if (mSyncService.isEngineInitialized() - && mSyncService.isPassphraseRequiredForPreferredDataTypes()) { - assert (!mSyncService.isTrustedVaultKeyRequiredForPreferredDataTypes()); + } - if (mSyncService.isPassphrasePromptMutedForCurrentProductVersion()) { - return NotificationState.HIDDEN; - } + if (!mSyncService.isEngineInitialized()) { + // The notifications expose encryption errors and those can only be detected once the + // engine is up. In the meantime, don't show anything. + return NotificationState.HIDDEN; + } - switch (mSyncService.getPassphraseType()) { - case PassphraseType.IMPLICIT_PASSPHRASE: - case PassphraseType.FROZEN_IMPLICIT_PASSPHRASE: - case PassphraseType.CUSTOM_PASSPHRASE: - return NotificationState.REQUIRE_PASSPHRASE; - case PassphraseType.TRUSTED_VAULT_PASSPHRASE: - assert false : "Passphrase cannot be required with trusted vault passphrase"; - return NotificationState.HIDDEN; - case PassphraseType.KEYSTORE_PASSPHRASE: - return NotificationState.HIDDEN; - default: - assert false : "Unknown passphrase type"; - return NotificationState.HIDDEN; - } - } else if (mSyncService.isEngineInitialized() - && mSyncService.isTrustedVaultKeyRequiredForPreferredDataTypes()) { + if (mSyncService.isPassphraseRequiredForPreferredDataTypes() + && !mSyncService.isPassphrasePromptMutedForCurrentProductVersion()) { + return NotificationState.REQUIRE_PASSPHRASE; + } + + if (mSyncService.isTrustedVaultKeyRequiredForPreferredDataTypes()) { return mSyncService.isEncryptEverythingEnabled() ? NotificationState.REQUIRE_TRUSTED_VAULT_KEY_FOR_EVERYTHING : NotificationState.REQUIRE_TRUSTED_VAULT_KEY_FOR_PASSWORDS; - } else { - return NotificationState.HIDDEN; } + + return NotificationState.HIDDEN; } /**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/TouchToFillCreditCardTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/TouchToFillCreditCardTest.java index ace044eb..8e9e4b1 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/TouchToFillCreditCardTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/TouchToFillCreditCardTest.java
@@ -31,7 +31,6 @@ import org.chromium.base.test.util.Batch; import org.chromium.base.test.util.CommandLineFlags; -import org.chromium.base.test.util.DisabledTest; import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard; import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.flags.ChromeSwitches; @@ -113,7 +112,6 @@ @Test @MediumTest - @DisabledTest(message = "https://crbug.com/1433074") public void testSelectingLocalCard() throws TimeoutException { // Focus the field to bring up the touch to fill for credit cards. DOMUtils.clickNode(mWebContents, CREDIT_CARD_NUMBER_FIELD_ID);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/sync/SyncErrorNotifierTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/sync/SyncErrorNotifierTest.java index e127d8f..4aa0c99 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/sync/SyncErrorNotifierTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/sync/SyncErrorNotifierTest.java
@@ -34,7 +34,6 @@ import org.chromium.components.browser_ui.notifications.NotificationManagerProxy; import org.chromium.components.browser_ui.notifications.NotificationWrapper; import org.chromium.components.signin.base.CoreAccountInfo; -import org.chromium.components.sync.PassphraseType; /** Unit tests for {@link SyncErrorNotifier}. */ @RunWith(BaseRobolectricTestRunner.class) @@ -64,7 +63,6 @@ when(mSyncService.isPassphraseRequiredForPreferredDataTypes()).thenReturn(false); when(mSyncService.isPassphrasePromptMutedForCurrentProductVersion()).thenReturn(false); when(mSyncService.isTrustedVaultKeyRequiredForPreferredDataTypes()).thenReturn(false); - when(mSyncService.getPassphraseType()).thenReturn(PassphraseType.IMPLICIT_PASSPHRASE); SyncErrorNotifier notifier = new SyncErrorNotifier(mNotificationManagerProxy, mSyncService, mTrustedVaultClient); @@ -85,7 +83,6 @@ when(mSyncService.isPassphraseRequiredForPreferredDataTypes()).thenReturn(true); when(mSyncService.isPassphrasePromptMutedForCurrentProductVersion()).thenReturn(false); when(mSyncService.isTrustedVaultKeyRequiredForPreferredDataTypes()).thenReturn(false); - when(mSyncService.getPassphraseType()).thenReturn(PassphraseType.CUSTOM_PASSPHRASE); SyncErrorNotifier notifier = new SyncErrorNotifier(mNotificationManagerProxy, mSyncService, mTrustedVaultClient); @@ -125,7 +122,6 @@ when(mSyncService.isPassphraseRequiredForPreferredDataTypes()).thenReturn(true); when(mSyncService.isPassphrasePromptMutedForCurrentProductVersion()).thenReturn(true); when(mSyncService.isTrustedVaultKeyRequiredForPreferredDataTypes()).thenReturn(false); - when(mSyncService.getPassphraseType()).thenReturn(PassphraseType.CUSTOM_PASSPHRASE); SyncErrorNotifier notifier = new SyncErrorNotifier(mNotificationManagerProxy, mSyncService, mTrustedVaultClient); @@ -146,7 +142,6 @@ when(mSyncService.isPassphraseRequiredForPreferredDataTypes()).thenReturn(false); when(mSyncService.isPassphrasePromptMutedForCurrentProductVersion()).thenReturn(false); when(mSyncService.isTrustedVaultKeyRequiredForPreferredDataTypes()).thenReturn(true); - when(mSyncService.getPassphraseType()).thenReturn(PassphraseType.TRUSTED_VAULT_PASSPHRASE); Promise<PendingIntent> intentPromise = new Promise<>(); when(mTrustedVaultClient.createKeyRetrievalIntent(any())).thenReturn(intentPromise); @@ -204,7 +199,6 @@ when(mSyncService.isPassphraseRequiredForPreferredDataTypes()).thenReturn(false); when(mSyncService.isPassphrasePromptMutedForCurrentProductVersion()).thenReturn(false); when(mSyncService.isTrustedVaultKeyRequiredForPreferredDataTypes()).thenReturn(true); - when(mSyncService.getPassphraseType()).thenReturn(PassphraseType.TRUSTED_VAULT_PASSPHRASE); when(mTrustedVaultClient.createKeyRetrievalIntent(any())) .thenReturn(Promise.fulfilled(null)); @@ -234,7 +228,6 @@ when(mSyncService.isPassphraseRequiredForPreferredDataTypes()).thenReturn(false); when(mSyncService.isPassphrasePromptMutedForCurrentProductVersion()).thenReturn(false); when(mSyncService.isTrustedVaultKeyRequiredForPreferredDataTypes()).thenReturn(true); - when(mSyncService.getPassphraseType()).thenReturn(PassphraseType.TRUSTED_VAULT_PASSPHRASE); when(mTrustedVaultClient.createKeyRetrievalIntent(any())).thenReturn(Promise.rejected()); SyncErrorNotifier notifier =
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index e7c2d411..9b7d098 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd
@@ -5002,7 +5002,7 @@ </message> <if expr="reven"> <message name="IDS_EXTENSION_PROMPT_WARNING_CHROMEOS_ATTACHED_DEVICE_INFO" desc="Permission string for accessing information of attached devices."> - Read attached device information and data + Read attached devices information and data </message> <message name="IDS_EXTENSION_PROMPT_WARNING_CHROMEOS_DIAGNOSTICS" desc="Permission string for chrome.os.diagnostcs API."> Run ChromeOS Flex diagnostic tests @@ -5022,7 +5022,7 @@ </if> <if expr="not reven"> <message name="IDS_EXTENSION_PROMPT_WARNING_CHROMEOS_ATTACHED_DEVICE_INFO" desc="Permission string for accessing information of attached devices."> - Read attached device information and data + Read attached devices information and data </message> <message name="IDS_EXTENSION_PROMPT_WARNING_CHROMEOS_DIAGNOSTICS" desc="Permission string for chrome.os.diagnostcs API."> Run ChromeOS diagnostic tests
diff --git a/chrome/app/generated_resources_grd/IDS_EXTENSION_PROMPT_WARNING_CHROMEOS_ATTACHED_DEVICE_INFO.png.sha1 b/chrome/app/generated_resources_grd/IDS_EXTENSION_PROMPT_WARNING_CHROMEOS_ATTACHED_DEVICE_INFO.png.sha1 index 37889de1..d31282b8 100644 --- a/chrome/app/generated_resources_grd/IDS_EXTENSION_PROMPT_WARNING_CHROMEOS_ATTACHED_DEVICE_INFO.png.sha1 +++ b/chrome/app/generated_resources_grd/IDS_EXTENSION_PROMPT_WARNING_CHROMEOS_ATTACHED_DEVICE_INFO.png.sha1
@@ -1 +1 @@ -2e89dcf6650d0e271a0e0ca43104eca5cf4d7719 \ No newline at end of file +66d6cbbd49303428ff5973f3c1f47477aedb0581 \ No newline at end of file
diff --git a/chrome/app/os_settings_strings.grdp b/chrome/app/os_settings_strings.grdp index 75d8d34a6..0eab143 100644 --- a/chrome/app/os_settings_strings.grdp +++ b/chrome/app/os_settings_strings.grdp
@@ -3767,6 +3767,36 @@ <message name="IDS_SETTINGS_INTERNET_PASSPOINT_PROVIDER" desc="Label of the link to the Passpoint provider details page."> Passpoint provider </message> + <message name="IDS_SETTINGS_INTERNET_PASSPOINT_REMOVE_SUBSCRIPTION" desc="Label of the button used to remove a subscription from the Passpoint detail page."> + Remove + </message> + <message name="IDS_SETTINGS_INTERNET_PASSPOINT_HEADLINE" desc="Headline text describing how Passpoint subscription is synced with the user account."> + Subscription is installed on this device only and not synced with other devices under your account. <ph name="LINK_BEGIN"><a></ph>Learn more<ph name="LINK_END"></a></ph> + </message> + <message name="IDS_SETTINGS_INTERNET_PASSPOINT_SUBSCRIPTION_EXPIRATION" desc="Label of the subscription expiration date."> + Expiry date + </message> + <message name="IDS_SETTINGS_INTERNET_PASSPOINT_SOURCE" desc="Label of the source that installed the Passpoint subscription."> + Passpoint provider + </message> + <message name="IDS_SETTINGS_INTERNET_PASSPOINT_TRUSTED_CA" desc="Label of the subscription trusted certificate authority used to authenticate the server."> + Trusted CA + </message> + <message name="IDS_SETTINGS_INTERNET_PASSPOINT_SYSTEM_CA" desc="Text displayed when the subscription relies on system CA certificates for server authentication."> + System CAs + </message> + <message name="IDS_SETTINGS_INTERNET_PASSPOINT_DOMAINS" desc="Label for the list of domains included in the Passpoint subscription."> + Domains + </message> + <message name="IDS_SETTINGS_INTERNET_PASSPOINT_DOMAINS_A11Y_LABEL" desc="Label for the button that toggles showing the Passpoint domains. Only visible by screen reader software."> + Show domains + </message> + <message name="IDS_SETTINGS_INTERNET_PASSPOINT_REMOVAL_TITLE" desc="Title of the dialog displayed when the remove button is clicked on Passpoint details page."> + Remove subscription from device + </message> + <message name="IDS_SETTINGS_INTERNET_PASSPOINT_REMOVAL_DESCRIPTION" desc="Text of the dialog displayed when the remove button is clicked on Passpoint details page."> + <ph name="SUBSCRIPTION_NAME">$1<ex>My Passpoint Provider</ex></ph> will be removed from this device only. To make changes to your subscription, contact the subscription provider. <ph name="LINK_BEGIN"><a></ph>Learn more<ph name="LINK_END"></a></ph> + </message> <!-- Users Page (OS Settings) --> <message name="IDS_SETTINGS_USERS_MODIFIED_BY_OWNER_LABEL" desc="Label saying settings may only be modified by the device owner.">
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_PASSPOINT_DOMAINS.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_PASSPOINT_DOMAINS.png.sha1 new file mode 100644 index 0000000..7bc4500 --- /dev/null +++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_PASSPOINT_DOMAINS.png.sha1
@@ -0,0 +1 @@ +240cf141f63e5382455e9df329cc1749e9b1cece \ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_PASSPOINT_DOMAINS_A11Y_LABEL.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_PASSPOINT_DOMAINS_A11Y_LABEL.png.sha1 new file mode 100644 index 0000000..7bc4500 --- /dev/null +++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_PASSPOINT_DOMAINS_A11Y_LABEL.png.sha1
@@ -0,0 +1 @@ +240cf141f63e5382455e9df329cc1749e9b1cece \ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_PASSPOINT_HEADLINE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_PASSPOINT_HEADLINE.png.sha1 new file mode 100644 index 0000000..7bc4500 --- /dev/null +++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_PASSPOINT_HEADLINE.png.sha1
@@ -0,0 +1 @@ +240cf141f63e5382455e9df329cc1749e9b1cece \ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_PASSPOINT_REMOVAL_DESCRIPTION.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_PASSPOINT_REMOVAL_DESCRIPTION.png.sha1 new file mode 100644 index 0000000..03c6ca0b --- /dev/null +++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_PASSPOINT_REMOVAL_DESCRIPTION.png.sha1
@@ -0,0 +1 @@ +5fd570af72fc242f12aba2cba1815e1cf82b1c86 \ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_PASSPOINT_REMOVAL_TITLE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_PASSPOINT_REMOVAL_TITLE.png.sha1 new file mode 100644 index 0000000..03c6ca0b --- /dev/null +++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_PASSPOINT_REMOVAL_TITLE.png.sha1
@@ -0,0 +1 @@ +5fd570af72fc242f12aba2cba1815e1cf82b1c86 \ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_PASSPOINT_REMOVE_SUBSCRIPTION.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_PASSPOINT_REMOVE_SUBSCRIPTION.png.sha1 new file mode 100644 index 0000000..31a855a --- /dev/null +++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_PASSPOINT_REMOVE_SUBSCRIPTION.png.sha1
@@ -0,0 +1 @@ +12de7cebe17dd3baa67ac269cf74e6a6f7efa287 \ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_PASSPOINT_SECTION_LABEL.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_PASSPOINT_SECTION_LABEL.png.sha1 index c0f5c93..b634ba6 100644 --- a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_PASSPOINT_SECTION_LABEL.png.sha1 +++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_PASSPOINT_SECTION_LABEL.png.sha1
@@ -1 +1 @@ -9efa3c921bf5a986407ca2002268f2d5cf52f33e \ No newline at end of file +9efa3c921bf5a986407ca2002268f2d5cf52f33e
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_PASSPOINT_SOURCE.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_PASSPOINT_SOURCE.png.sha1 new file mode 100644 index 0000000..7bc4500 --- /dev/null +++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_PASSPOINT_SOURCE.png.sha1
@@ -0,0 +1 @@ +240cf141f63e5382455e9df329cc1749e9b1cece \ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_PASSPOINT_SUBSCRIPTION_EXPIRATION.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_PASSPOINT_SUBSCRIPTION_EXPIRATION.png.sha1 new file mode 100644 index 0000000..7bc4500 --- /dev/null +++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_PASSPOINT_SUBSCRIPTION_EXPIRATION.png.sha1
@@ -0,0 +1 @@ +240cf141f63e5382455e9df329cc1749e9b1cece \ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_PASSPOINT_SYSTEM_CA.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_PASSPOINT_SYSTEM_CA.png.sha1 new file mode 100644 index 0000000..7bc4500 --- /dev/null +++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_PASSPOINT_SYSTEM_CA.png.sha1
@@ -0,0 +1 @@ +240cf141f63e5382455e9df329cc1749e9b1cece \ No newline at end of file
diff --git a/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_PASSPOINT_TRUSTED_CA.png.sha1 b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_PASSPOINT_TRUSTED_CA.png.sha1 new file mode 100644 index 0000000..7bc4500 --- /dev/null +++ b/chrome/app/os_settings_strings_grdp/IDS_SETTINGS_INTERNET_PASSPOINT_TRUSTED_CA.png.sha1
@@ -0,0 +1 @@ +240cf141f63e5382455e9df329cc1749e9b1cece \ No newline at end of file
diff --git a/chrome/app/password_manager_ui_strings.grdp b/chrome/app/password_manager_ui_strings.grdp index d1de8ce..be5c1e29 100644 --- a/chrome/app/password_manager_ui_strings.grdp +++ b/chrome/app/password_manager_ui_strings.grdp
@@ -126,6 +126,9 @@ <message name="IDS_PASSWORD_MANAGER_UI_IMPORT_COMPLETE_TITLE" desc="The title of a dialog, which offers the ability for the user to import passwords into Password Manager. Shown when import has successfully finished, but some errors were found in the imported file."> Import complete </message> + <message name="IDS_PASSWORD_MANAGER_UI_IMPORT_DELETE_FILE_OPTION" desc="Message is shown on the dialog for importing passwords, after a successful import. It offers a user to delete the file, which they used for importing passwords from, by clicking at the checkbox and 'Done' button afterwards."> + Delete <ph name="FILENAME"><span class="bold-text">$1</span><ex><span class="bold-text">filename.csv</span></ex></ph>, so others who use this device can't see your passwords + </message> <message name="IDS_PASSWORD_MANAGER_UI_IMPORT_VIEW_PASSWORDS" desc="A button on the import dialog that redirects the user to the page with the passwords list."> View passwords </message>
diff --git a/chrome/app/password_manager_ui_strings_grdp/IDS_PASSWORD_MANAGER_UI_IMPORT_DELETE_FILE_OPTION.png.sha1 b/chrome/app/password_manager_ui_strings_grdp/IDS_PASSWORD_MANAGER_UI_IMPORT_DELETE_FILE_OPTION.png.sha1 new file mode 100644 index 0000000..ed5bd824 --- /dev/null +++ b/chrome/app/password_manager_ui_strings_grdp/IDS_PASSWORD_MANAGER_UI_IMPORT_DELETE_FILE_OPTION.png.sha1
@@ -0,0 +1 @@ +b94cf6b0e291f8280e557dc878858c2e72f277e8 \ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index 012e030..b9c28c6 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -3106,6 +3106,8 @@ "password_manager/android/password_manager_error_message_helper_bridge_impl.h", "password_manager/android/password_manager_launcher_android.cc", "password_manager/android/password_manager_launcher_android.h", + "password_manager/android/password_manager_ui_util_android.cc", + "password_manager/android/password_manager_ui_util_android.h", "password_manager/android/password_store_bridge.cc", "password_manager/android/password_store_bridge.h", "password_manager/android/password_ui_view_android.cc",
diff --git a/chrome/browser/ash/BUILD.gn b/chrome/browser/ash/BUILD.gn index c40fc762..59fc6f6 100644 --- a/chrome/browser/ash/BUILD.gn +++ b/chrome/browser/ash/BUILD.gn
@@ -5328,7 +5328,6 @@ "login/ui/login_screen_extension_ui/web_dialog_view_unittest.cc", "login/ui/oobe_dialog_size_utils_unittest.cc", "login/user_online_signin_notifier_unittest.cc", - "login/users/affiliation_unittest.cc", "login/users/avatar/user_image_loader_unittest.cc", "login/users/default_user_image/default_user_images_unittest.cc", "login/users/multi_profile_user_controller_unittest.cc",
diff --git a/chrome/browser/ash/file_manager/file_manager_jstest.cc b/chrome/browser/ash/file_manager/file_manager_jstest.cc index 66040b2..ce21b7e 100644 --- a/chrome/browser/ash/file_manager/file_manager_jstest.cc +++ b/chrome/browser/ash/file_manager/file_manager_jstest.cc
@@ -354,6 +354,10 @@ RunTestURL("state/reducers/bulk_pinning_unittest.js"); } +IN_PROC_BROWSER_TEST_F(FileManagerJsTest, ReducerPreferences) { + RunTestURL("state/reducers/preferences_unittest.js"); +} + IN_PROC_BROWSER_TEST_F(FileManagerJsTest, NudgeContainer) { RunTestURL("containers/nudge_container_unittest.js"); }
diff --git a/chrome/browser/ash/guest_os/public/installer_delegate_factory.cc b/chrome/browser/ash/guest_os/public/installer_delegate_factory.cc index 8e8c5edf1..8f52c6b 100644 --- a/chrome/browser/ash/guest_os/public/installer_delegate_factory.cc +++ b/chrome/browser/ash/guest_os/public/installer_delegate_factory.cc
@@ -11,7 +11,6 @@ std::unique_ptr<ash::guest_os_installer::mojom::PageHandler> InstallerDelegateFactory( ash::GuestOSInstallerUI* webui, - const GURL& url, mojo::PendingRemote<ash::guest_os_installer::mojom::Page> pending_page, mojo::PendingReceiver<ash::guest_os_installer::mojom::PageHandler> pending_page_handler) {
diff --git a/chrome/browser/ash/guest_os/public/installer_delegate_factory.h b/chrome/browser/ash/guest_os/public/installer_delegate_factory.h index 93720bf2..71ff0a4 100644 --- a/chrome/browser/ash/guest_os/public/installer_delegate_factory.h +++ b/chrome/browser/ash/guest_os/public/installer_delegate_factory.h
@@ -11,8 +11,6 @@ #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/pending_remote.h" -class GURL; - namespace ash { class GuestOSInstallerUI; } @@ -22,10 +20,8 @@ std::unique_ptr<ash::guest_os_installer::mojom::PageHandler> InstallerDelegateFactory( ash::GuestOSInstallerUI*, - const GURL&, mojo::PendingRemote<ash::guest_os_installer::mojom::Page>, mojo::PendingReceiver<ash::guest_os_installer::mojom::PageHandler>); - } #endif
diff --git a/chrome/browser/ash/login/saml/saml_browsertest.cc b/chrome/browser/ash/login/saml/saml_browsertest.cc index 10f1e5e..25909ec 100644 --- a/chrome/browser/ash/login/saml/saml_browsertest.cc +++ b/chrome/browser/ash/login/saml/saml_browsertest.cc
@@ -1184,7 +1184,7 @@ user_manager::User::OAUTH2_TOKEN_STATUS_VALID); // Give affiliated users appropriate affiliation IDs. - std::set<std::string> user_affiliation_ids; + base::flat_set<std::string> user_affiliation_ids; user_affiliation_ids.insert(kAffiliationID); ChromeUserManager::Get()->SetUserAffiliation( AccountId::FromUserEmailGaiaId(
diff --git a/chrome/browser/ash/login/users/affiliation.cc b/chrome/browser/ash/login/users/affiliation.cc index dd06ade..3c80a7d 100644 --- a/chrome/browser/ash/login/users/affiliation.cc +++ b/chrome/browser/ash/login/users/affiliation.cc
@@ -4,16 +4,16 @@ #include "chrome/browser/ash/login/users/affiliation.h" +#include "base/containers/flat_set.h" #include "base/functional/bind.h" #include "base/functional/callback.h" #include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h" -#include "chrome/browser/ash/policy/core/device_local_account.h" #include "chrome/browser/ash/settings/device_settings_service.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process_platform_part.h" #include "components/account_id/account_id.h" +#include "components/policy/core/common/cloud/affiliation.h" #include "components/policy/proto/device_management_backend.pb.h" -#include "google_apis/gaia/gaia_auth_util.h" namespace ash { namespace { @@ -21,14 +21,13 @@ std::string GetDeviceDMTokenIfAffiliated( const AccountId& account_id, const std::vector<std::string>& user_affiliation_ids) { - const AffiliationIDSet set_of_user_affiliation_ids( - user_affiliation_ids.begin(), user_affiliation_ids.end()); const policy::BrowserPolicyConnectorAsh* connector = g_browser_process->platform_part()->browser_policy_connector_ash(); DCHECK(connector); - const bool is_affiliated = IsUserAffiliated( - set_of_user_affiliation_ids, connector->GetDeviceAffiliationIDs(), - account_id.GetUserEmail()); + const bool is_affiliated = policy::IsUserAffiliated( + base::flat_set<std::string>(user_affiliation_ids.begin(), + user_affiliation_ids.end()), + connector->device_affiliation_ids(), account_id.GetUserEmail()); if (is_affiliated) { const enterprise_management::PolicyData* policy_data = DeviceSettingsService::Get()->policy_data(); @@ -40,44 +39,6 @@ } // namespace -bool HaveCommonElement(const std::set<std::string>& set1, - const std::set<std::string>& set2) { - std::set<std::string>::const_iterator it1 = set1.begin(); - std::set<std::string>::const_iterator it2 = set2.begin(); - - while (it1 != set1.end() && it2 != set2.end()) { - if (*it1 == *it2) - return true; - if (*it1 < *it2) { - ++it1; - } else { - ++it2; - } - } - return false; -} - -bool IsUserAffiliated(const AffiliationIDSet& user_affiliation_ids, - const AffiliationIDSet& device_affiliation_ids, - const std::string& email) { - // An empty username means incognito user in case of Chrome OS and no - // logged-in user in case of Chrome (SigninService). Many tests use nonsense - // email addresses (e.g. 'test') so treat those as non-enterprise users. - if (email.empty() || email.find('@') == std::string::npos) { - return false; - } - - if (policy::IsDeviceLocalAccountUser(email, nullptr)) { - return true; - } - - if (!device_affiliation_ids.empty() && !user_affiliation_ids.empty()) { - return HaveCommonElement(user_affiliation_ids, device_affiliation_ids); - } - - return false; -} - base::RepeatingCallback<std::string(const std::vector<std::string>&)> GetDeviceDMTokenForUserPolicyGetter(const AccountId& account_id) { return base::BindRepeating(&GetDeviceDMTokenIfAffiliated, account_id);
diff --git a/chrome/browser/ash/login/users/affiliation.h b/chrome/browser/ash/login/users/affiliation.h index 389cd84..6134a45 100644 --- a/chrome/browser/ash/login/users/affiliation.h +++ b/chrome/browser/ash/login/users/affiliation.h
@@ -17,21 +17,6 @@ typedef std::set<std::string> AffiliationIDSet; -// Returns true if there is at least one common element in two sets. -// Complexity: O(n + m), where n - size of the first set, m - size of -// the second set. -bool HaveCommonElement(const std::set<std::string>& set1, - const std::set<std::string>& set2); - -// TODO(peletskyi): Remove email after affiliation based implementation will -// fully work. http://crbug.com/515476 -// The function makes a decision if user with `user_affiliation_ids` and -// `email` is affiliated on the device with `device_affiliation_ids` and -// `enterprise_domain`. -bool IsUserAffiliated(const AffiliationIDSet& user_affiliation_ids, - const AffiliationIDSet& device_affiliation_ids, - const std::string& email); - // Returns a callback to retrieve device DMToken if the user with // given `account_id` is affiliated on the device. base::RepeatingCallback<std::string(const std::vector<std::string>&)>
diff --git a/chrome/browser/ash/login/users/affiliation_unittest.cc b/chrome/browser/ash/login/users/affiliation_unittest.cc deleted file mode 100644 index 74b3800..0000000 --- a/chrome/browser/ash/login/users/affiliation_unittest.cc +++ /dev/null
@@ -1,99 +0,0 @@ -// Copyright 2015 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/ash/login/users/affiliation.h" - -#include <set> -#include <string> - -#include "testing/gtest/include/gtest/gtest.h" - -namespace ash { - -TEST(AffiliationTest, HaveCommonElementEmptySet) { - // Empty sets don't have common elements. - EXPECT_FALSE(HaveCommonElement(AffiliationIDSet(), AffiliationIDSet())); - - AffiliationIDSet not_empty_set; - not_empty_set.insert("a"); - - // Only first set is empty. No common elements and no crash. - EXPECT_FALSE(HaveCommonElement(AffiliationIDSet(), not_empty_set)); - // Now the second set is empty. - EXPECT_FALSE(HaveCommonElement(not_empty_set, AffiliationIDSet())); -} - -TEST(AffiliationTest, HaveCommonElementNoOverlap) { - AffiliationIDSet set1; - AffiliationIDSet set2; - - // No common elements. - set1.insert("a"); - set1.insert("b"); - set1.insert("c"); - - set2.insert("d"); - set2.insert("e"); - set2.insert("f"); - EXPECT_FALSE(HaveCommonElement(set1, set2)); -} - -TEST(AffiliationTest, HaveCommonElementFirstAndLastIsCommon) { - AffiliationIDSet set1; - AffiliationIDSet set2; - - // The common element is last in set1 and first in set2. - set1.insert("a"); - set1.insert("b"); - set1.insert("c"); - set1.insert("d"); // String "d" is common. - - set2.insert("d"); - set2.insert("e"); - set2.insert("f"); - - EXPECT_TRUE(HaveCommonElement(set1, set2)); - EXPECT_TRUE(HaveCommonElement(set2, set1)); -} - -TEST(AffiliationTest, HaveCommonElementCommonInTheMiddle) { - AffiliationIDSet set1; - AffiliationIDSet set2; - - // The common element is in the middle of the two sets. - set1.insert("b"); - set1.insert("f"); // String "f" is common. - set1.insert("k"); - - set2.insert("c"); - set2.insert("f"); - set2.insert("j"); - - EXPECT_TRUE(HaveCommonElement(set1, set2)); - EXPECT_TRUE(HaveCommonElement(set2, set1)); -} - -TEST(AffiliationTest, Generic) { - AffiliationIDSet user_ids; // User affiliation IDs. - AffiliationIDSet device_ids; // Device affiliation IDs. - - // Empty affiliation IDs. - EXPECT_FALSE(IsUserAffiliated(user_ids, device_ids, "user@managed.com")); - - user_ids.insert("aaaa"); // Only user affiliation IDs present. - EXPECT_FALSE(IsUserAffiliated(user_ids, device_ids, "user@managed.com")); - - device_ids.insert("bbbb"); // Device and user IDs do not overlap. - EXPECT_FALSE(IsUserAffiliated(user_ids, device_ids, "user@managed.com")); - - user_ids.insert("cccc"); // Device and user IDs do overlap. - device_ids.insert("cccc"); - EXPECT_TRUE(IsUserAffiliated(user_ids, device_ids, "user@managed.com")); - - // Invalid email overrides match of affiliation IDs. - EXPECT_FALSE(IsUserAffiliated(user_ids, device_ids, "")); - EXPECT_FALSE(IsUserAffiliated(user_ids, device_ids, "user")); -} - -} // namespace ash
diff --git a/chrome/browser/ash/login/users/chrome_user_manager.h b/chrome/browser/ash/login/users/chrome_user_manager.h index 1dbfc3a..7ce63e0 100644 --- a/chrome/browser/ash/login/users/chrome_user_manager.h +++ b/chrome/browser/ash/login/users/chrome_user_manager.h
@@ -5,9 +5,9 @@ #ifndef CHROME_BROWSER_ASH_LOGIN_USERS_CHROME_USER_MANAGER_H_ #define CHROME_BROWSER_ASH_LOGIN_USERS_CHROME_USER_MANAGER_H_ +#include "base/containers/flat_set.h" #include "base/memory/ref_counted.h" #include "base/task/single_thread_task_runner.h" -#include "chrome/browser/ash/login/users/affiliation.h" #include "chrome/browser/ash/login/users/user_manager_interface.h" #include "chrome/browser/ash/policy/core/device_local_account_policy_service.h" #include "chrome/browser/ash/policy/core/reporting_user_tracker.h" @@ -58,7 +58,7 @@ // judging by `user_affiliation_ids` and device affiliation IDs. virtual void SetUserAffiliation( const AccountId& account_id, - const AffiliationIDSet& user_affiliation_ids) = 0; + const base::flat_set<std::string>& user_affiliation_ids) = 0; // Return whether the given user should be reported (see // policy::DeviceStatusCollector).
diff --git a/chrome/browser/ash/login/users/chrome_user_manager_impl.cc b/chrome/browser/ash/login/users/chrome_user_manager_impl.cc index 6956eac5..2d366eb 100644 --- a/chrome/browser/ash/login/users/chrome_user_manager_impl.cc +++ b/chrome/browser/ash/login/users/chrome_user_manager_impl.cc
@@ -93,6 +93,7 @@ #include "chromeos/dbus/common/dbus_method_call_status.h" #include "components/account_id/account_id.h" #include "components/crash/core/common/crash_key.h" +#include "components/policy/core/common/cloud/affiliation.h" #include "components/policy/core/common/policy_details.h" #include "components/policy/policy_constants.h" #include "components/prefs/pref_registry_simple.h" @@ -1262,14 +1263,14 @@ void ChromeUserManagerImpl::SetUserAffiliation( const AccountId& account_id, - const AffiliationIDSet& user_affiliation_ids) { + const base::flat_set<std::string>& user_affiliation_ids) { user_manager::User* user = FindUserAndModify(account_id); if (user) { policy::BrowserPolicyConnectorAsh const* const connector = g_browser_process->platform_part()->browser_policy_connector_ash(); - const bool is_affiliated = IsUserAffiliated( - user_affiliation_ids, connector->GetDeviceAffiliationIDs(), + const bool is_affiliated = policy::IsUserAffiliated( + user_affiliation_ids, connector->device_affiliation_ids(), account_id.GetUserEmail()); user->SetAffiliation(is_affiliated); reporting_user_tracker_.OnSetUserAffiliation(*user);
diff --git a/chrome/browser/ash/login/users/chrome_user_manager_impl.h b/chrome/browser/ash/login/users/chrome_user_manager_impl.h index 7a71779..21150465 100644 --- a/chrome/browser/ash/login/users/chrome_user_manager_impl.h +++ b/chrome/browser/ash/login/users/chrome_user_manager_impl.h
@@ -136,7 +136,7 @@ bool IsEnterpriseManaged() const override; void SetUserAffiliation( const AccountId& account_id, - const AffiliationIDSet& user_affiliation_ids) override; + const base::flat_set<std::string>& user_affiliation_ids) override; bool IsFullManagementDisclosureNeeded( policy::DeviceLocalAccountPolicyBroker* broker) const override;
diff --git a/chrome/browser/ash/login/users/fake_chrome_user_manager.cc b/chrome/browser/ash/login/users/fake_chrome_user_manager.cc index c1153ded..b8b77c0 100644 --- a/chrome/browser/ash/login/users/fake_chrome_user_manager.cc +++ b/chrome/browser/ash/login/users/fake_chrome_user_manager.cc
@@ -725,7 +725,7 @@ void FakeChromeUserManager::SetUserAffiliation( const AccountId& account_id, - const AffiliationIDSet& user_affiliation_ids) {} + const base::flat_set<std::string>& user_affiliation_ids) {} void FakeChromeUserManager::SetUserAffiliationForTesting( const AccountId& account_id,
diff --git a/chrome/browser/ash/login/users/fake_chrome_user_manager.h b/chrome/browser/ash/login/users/fake_chrome_user_manager.h index 5b0bd5cb..dae55eb 100644 --- a/chrome/browser/ash/login/users/fake_chrome_user_manager.h +++ b/chrome/browser/ash/login/users/fake_chrome_user_manager.h
@@ -172,7 +172,7 @@ // ChromeUserManager override. void SetUserAffiliation( const AccountId& account_id, - const AffiliationIDSet& user_affiliation_ids) override; + const base::flat_set<std::string>& user_affiliation_ids) override; bool IsFullManagementDisclosureNeeded( policy::DeviceLocalAccountPolicyBroker* broker) const override;
diff --git a/chrome/browser/ash/policy/active_directory/active_directory_policy_manager.cc b/chrome/browser/ash/policy/active_directory/active_directory_policy_manager.cc index b36430c..9fa72586 100644 --- a/chrome/browser/ash/policy/active_directory/active_directory_policy_manager.cc +++ b/chrome/browser/ash/policy/active_directory/active_directory_policy_manager.cc
@@ -327,16 +327,15 @@ void UserActiveDirectoryPolicyManager::OnPublishPolicy() { const em::PolicyData* policy_data = store()->policy(); - if (!policy_data) + if (!policy_data) { return; + } // Update user affiliation IDs. - ash::AffiliationIDSet set_of_user_affiliation_ids( - policy_data->user_affiliation_ids().begin(), - policy_data->user_affiliation_ids().end()); - ash::ChromeUserManager::Get()->SetUserAffiliation( - account_id_, set_of_user_affiliation_ids); + account_id_, + base::flat_set<std::string>(policy_data->user_affiliation_ids().begin(), + policy_data->user_affiliation_ids().end())); } void UserActiveDirectoryPolicyManager::OnBlockingFetchTimeout() {
diff --git a/chrome/browser/ash/policy/arc/android_management_client.cc b/chrome/browser/ash/policy/arc/android_management_client.cc index 5449c22..1b139a0 100644 --- a/chrome/browser/ash/policy/arc/android_management_client.cc +++ b/chrome/browser/ash/policy/arc/android_management_client.cc
@@ -8,8 +8,8 @@ #include "base/functional/bind.h" #include "base/functional/callback_helpers.h" -#include "base/guid.h" #include "base/logging.h" +#include "base/uuid.h" #include "components/policy/core/common/cloud/device_management_service.h" #include "components/policy/core/common/cloud/dm_auth.h" #include "components/policy/core/common/cloud/dmserver_job_configurations.h" @@ -86,7 +86,7 @@ DMServerJobConfiguration>( device_management_service_, DeviceManagementService::JobConfiguration::TYPE_ANDROID_MANAGEMENT_CHECK, - /*client_id=*/base::GenerateGUID(), + /*client_id=*/base::Uuid::GenerateRandomV4().AsLowercaseString(), /*critical=*/false, DMAuth::NoAuth(), access_token, url_loader_factory_, base::BindOnce(&AndroidManagementClientImpl::OnAndroidManagementChecked, weak_ptr_factory_.GetWeakPtr()));
diff --git a/chrome/browser/ash/policy/core/browser_policy_connector_ash.cc b/chrome/browser/ash/policy/core/browser_policy_connector_ash.cc index 7364b056..92b38ff 100644 --- a/chrome/browser/ash/policy/core/browser_policy_connector_ash.cc +++ b/chrome/browser/ash/policy/core/browser_policy_connector_ash.cc
@@ -594,12 +594,6 @@ return {}; } -ash::AffiliationIDSet BrowserPolicyConnectorAsh::GetDeviceAffiliationIDs() - const { - base::flat_set<std::string> affiliation_ids = device_affiliation_ids(); - return {affiliation_ids.begin(), affiliation_ids.end()}; -} - const em::PolicyData* BrowserPolicyConnectorAsh::GetDevicePolicy() const { if (device_cloud_policy_manager_) return device_cloud_policy_manager_->device_store()->policy();
diff --git a/chrome/browser/ash/policy/core/browser_policy_connector_ash.h b/chrome/browser/ash/policy/core/browser_policy_connector_ash.h index d587392..0e95546 100644 --- a/chrome/browser/ash/policy/core/browser_policy_connector_ash.h +++ b/chrome/browser/ash/policy/core/browser_policy_connector_ash.h
@@ -14,7 +14,6 @@ #include "base/memory/weak_ptr.h" #include "base/task/sequenced_task_runner.h" #include "chrome/browser/ash/cert_provisioning/cert_provisioning_scheduler.h" -#include "chrome/browser/ash/login/users/affiliation.h" #include "chrome/browser/ash/policy/core/device_cloud_policy_manager_ash.h" #include "chrome/browser/policy/chrome_browser_policy_connector.h" #include "components/policy/core/common/cloud/cloud_policy_constants.h" @@ -241,10 +240,7 @@ void OnDeviceCloudPolicyManagerConnected() override; void OnDeviceCloudPolicyManagerGotRegistry() override; - // TODO(crbug.com/1187628): Combine the following two functions into one to - // simplify the API. base::flat_set<std::string> device_affiliation_ids() const override; - ash::AffiliationIDSet GetDeviceAffiliationIDs() const; // BrowserPolicyConnector: // Always returns true as command line flag can be set under dev mode only.
diff --git a/chrome/browser/ash/policy/core/user_cloud_policy_manager_ash.cc b/chrome/browser/ash/policy/core/user_cloud_policy_manager_ash.cc index 61930a2..acf50ed 100644 --- a/chrome/browser/ash/policy/core/user_cloud_policy_manager_ash.cc +++ b/chrome/browser/ash/policy/core/user_cloud_policy_manager_ash.cc
@@ -67,22 +67,8 @@ namespace { // UMA histogram names. -const char kUMADelayInitialization[] = - "Enterprise.UserPolicyChromeOS.DelayInitialization"; -const char kUMAInitialFetchClientError[] = - "Enterprise.UserPolicyChromeOS.InitialFetch.ClientError"; -const char kUMAInitialFetchDelayClientRegister[] = - "Enterprise.UserPolicyChromeOS.InitialFetch.DelayClientRegister"; -const char kUMAInitialFetchDelayOAuth2Token[] = - "Enterprise.UserPolicyChromeOS.InitialFetch.DelayOAuth2Token"; -const char kUMAInitialFetchDelayPolicyFetch[] = - "Enterprise.UserPolicyChromeOS.InitialFetch.DelayPolicyFetch"; -const char kUMAInitialFetchDelayTotal[] = - "Enterprise.UserPolicyChromeOS.InitialFetch.DelayTotal"; const char kUMAInitialFetchOAuth2Error[] = "Enterprise.UserPolicyChromeOS.InitialFetch.OAuth2Error"; -const char kUMAInitialFetchOAuth2NetworkError[] = - "Enterprise.UserPolicyChromeOS.InitialFetch.OAuth2NetworkError"; const char kUMAReregistrationResult[] = "Enterprise.UserPolicyChromeOS.ReregistrationResult"; @@ -169,7 +155,6 @@ fatal_error_callback_(std::move(fatal_error_callback)) { DCHECK(profile_); DCHECK(local_state_); - time_init_started_ = base::Time::Now(); // Some tests don't want to complete policy initialization until they have // manually injected policy even though the profile itself is synchronously @@ -370,10 +355,6 @@ void UserCloudPolicyManagerAsh::OnCloudPolicyServiceInitializationCompleted() { service()->RemoveObserver(this); - time_init_completed_ = base::Time::Now(); - UMA_HISTOGRAM_MEDIUM_TIMES(kUMADelayInitialization, - time_init_completed_ - time_init_started_); - // If the CloudPolicyClient isn't registered at this stage then it needs an // OAuth token for the initial registration (there's no cached policy). // @@ -440,13 +421,6 @@ } if (waiting_for_policy_fetch_) { - time_client_registered_ = base::Time::Now(); - if (!time_token_available_.is_null()) { - UMA_HISTOGRAM_MEDIUM_TIMES( - kUMAInitialFetchDelayClientRegister, - time_client_registered_ - time_token_available_); - } - // If we're blocked on the policy fetch, now is a good time to issue it. if (client()->is_registered()) { service()->RefreshPolicy(base::BindOnce( @@ -463,10 +437,6 @@ void UserCloudPolicyManagerAsh::OnClientError( CloudPolicyClient* cloud_policy_client) { DCHECK_EQ(client(), cloud_policy_client); - if (waiting_for_policy_fetch_) { - base::UmaHistogramSparse(kUMAInitialFetchClientError, - cloud_policy_client->last_dm_status()); - } switch (client()->last_dm_status()) { case DM_STATUS_SERVICE_MANAGEMENT_NOT_SUPPORTED: // If management is not supported for this user, then a registration @@ -532,12 +502,10 @@ enforcement_type_ = PolicyEnforcement::kPolicyOptional; DCHECK(policy_data->has_username()); - ash::AffiliationIDSet set_of_user_affiliation_ids( - policy_data->user_affiliation_ids().begin(), - policy_data->user_affiliation_ids().end()); - ash::ChromeUserManager::Get()->SetUserAffiliation( - account_id_, set_of_user_affiliation_ids); + account_id_, + base::flat_set<std::string>(policy_data->user_affiliation_ids().begin(), + policy_data->user_affiliation_ids().end())); } } @@ -636,11 +604,6 @@ const std::string& policy_token, const GoogleServiceAuthError& error) { DCHECK(!client()->is_registered()); - time_token_available_ = base::Time::Now(); - if (waiting_for_policy_fetch_) { - UMA_HISTOGRAM_MEDIUM_TIMES(kUMAInitialFetchDelayOAuth2Token, - time_token_available_ - time_init_completed_); - } if (error.state() == GoogleServiceAuthError::NONE) { if (RequiresOAuthTokenForChildUser()) @@ -661,12 +624,6 @@ } else { UMA_HISTOGRAM_ENUMERATION(kUMAInitialFetchOAuth2Error, error.state(), GoogleServiceAuthError::NUM_STATES); - if (error.state() == GoogleServiceAuthError::CONNECTION_FAILED) { - // Network errors are negative in the code, but the histogram data type - // expects the corresponding positive value. - base::UmaHistogramSparse(kUMAInitialFetchOAuth2NetworkError, - -error.network_error()); - } // Failed to get a token, stop waiting if policy is not required for this // user. CancelWaitForPolicyFetch( @@ -677,11 +634,6 @@ } void UserCloudPolicyManagerAsh::OnInitialPolicyFetchComplete(bool success) { - const base::Time now = base::Time::Now(); - UMA_HISTOGRAM_MEDIUM_TIMES(kUMAInitialFetchDelayPolicyFetch, - now - time_client_registered_); - UMA_HISTOGRAM_MEDIUM_TIMES(kUMAInitialFetchDelayTotal, - now - time_init_started_); CancelWaitForPolicyFetch( success, "policy fetch complete"
diff --git a/chrome/browser/ash/policy/core/user_cloud_policy_manager_ash.h b/chrome/browser/ash/policy/core/user_cloud_policy_manager_ash.h index 0a24ec1..56fd0ea 100644 --- a/chrome/browser/ash/policy/core/user_cloud_policy_manager_ash.h +++ b/chrome/browser/ash/policy/core/user_cloud_policy_manager_ash.h
@@ -302,12 +302,6 @@ // called later. std::string access_token_; - // Timestamps for collecting timing UMA stats. - base::Time time_init_started_; - base::Time time_init_completed_; - base::Time time_token_available_; - base::Time time_client_registered_; - // The AccountId associated with the user whose policy is being loaded. const AccountId account_id_;
diff --git a/chrome/browser/ash/policy/enrollment/account_status_check_fetcher.cc b/chrome/browser/ash/policy/enrollment/account_status_check_fetcher.cc index c993d844..c4921ef 100644 --- a/chrome/browser/ash/policy/enrollment/account_status_check_fetcher.cc +++ b/chrome/browser/ash/policy/enrollment/account_status_check_fetcher.cc
@@ -8,9 +8,9 @@ #include <utility> #include "base/functional/bind.h" -#include "base/guid.h" #include "base/logging.h" #include "base/metrics/histogram_functions.h" +#include "base/uuid.h" #include "chrome/browser/ash/arc/arc_optin_uma.h" #include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h" #include "chrome/browser/ash/settings/cros_settings.h" @@ -95,7 +95,7 @@ : email_(canonicalized_email), service_(service), url_loader_factory_(url_loader_factory), - random_device_id_(base::GenerateGUID()) {} + random_device_id_(base::Uuid::GenerateRandomV4().AsLowercaseString()) {} AccountStatusCheckFetcher::~AccountStatusCheckFetcher() = default;
diff --git a/chrome/browser/ash/policy/enrollment/auto_enrollment_client_impl.cc b/chrome/browser/ash/policy/enrollment/auto_enrollment_client_impl.cc index f3ba08c..cf792ab 100644 --- a/chrome/browser/ash/policy/enrollment/auto_enrollment_client_impl.cc +++ b/chrome/browser/ash/policy/enrollment/auto_enrollment_client_impl.cc
@@ -11,7 +11,6 @@ #include "base/check.h" #include "base/functional/bind.h" #include "base/functional/callback.h" -#include "base/guid.h" #include "base/location.h" #include "base/logging.h" #include "base/memory/ptr_util.h" @@ -19,6 +18,7 @@ #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" #include "base/time/time.h" +#include "base/uuid.h" #include "base/values.h" #include "chrome/browser/ash/policy/enrollment/auto_enrollment_state_message_processor.h" #include "chrome/browser/ash/policy/enrollment/psm/rlwe_dmserver_client.h" @@ -741,7 +741,8 @@ const std::string& server_backed_state_key, int power_initial, int power_limit) { - const std::string device_id = base::GenerateGUID(); + const std::string device_id = + base::Uuid::GenerateRandomV4().AsLowercaseString(); return base::WrapUnique(new AutoEnrollmentClientImpl( progress_callback, std::make_unique<FREServerStateAvailabilityRequester>( @@ -769,7 +770,8 @@ std::move(psm_rlwe_dmserver_client), local_state), std::make_unique<ServerStateRetriever>( device_management_service, url_loader_factory, local_state, - /*device_id=*/base::GenerateGUID(), kUMASuffixInitialEnrollment, + /*device_id=*/base::Uuid::GenerateRandomV4().AsLowercaseString(), + kUMASuffixInitialEnrollment, AutoEnrollmentStateMessageProcessor::CreateForInitialEnrollment( device_serial_number, device_brand_code)))); }
diff --git a/chrome/browser/ash/policy/enrollment/enrollment_handler.cc b/chrome/browser/ash/policy/enrollment/enrollment_handler.cc index dbfaa90..0eba472 100644 --- a/chrome/browser/ash/policy/enrollment/enrollment_handler.cc +++ b/chrome/browser/ash/policy/enrollment/enrollment_handler.cc
@@ -11,7 +11,6 @@ #include "base/base64.h" #include "base/command_line.h" #include "base/functional/bind.h" -#include "base/guid.h" #include "base/location.h" #include "base/logging.h" #include "base/task/sequenced_task_runner.h"
diff --git a/chrome/browser/ash/policy/enrollment/enrollment_state_fetcher.cc b/chrome/browser/ash/policy/enrollment/enrollment_state_fetcher.cc index 16c1c80..8bd557b3 100644 --- a/chrome/browser/ash/policy/enrollment/enrollment_state_fetcher.cc +++ b/chrome/browser/ash/policy/enrollment/enrollment_state_fetcher.cc
@@ -11,12 +11,12 @@ #include "ash/constants/ash_switches.h" #include "base/check.h" #include "base/functional/callback_forward.h" -#include "base/guid.h" #include "base/memory/weak_ptr.h" #include "base/metrics/histogram_functions.h" #include "base/strings/strcat.h" #include "base/time/time.h" #include "base/types/expected.h" +#include "base/uuid.h" #include "base/values.h" #include "chrome/browser/ash/policy/core/browser_policy_connector_ash.h" #include "chrome/browser/ash/policy/enrollment/auto_enrollment_client.h" @@ -289,7 +289,7 @@ context.device_management_service, DeviceManagementService::JobConfiguration:: TYPE_PSM_HAS_DEVICE_STATE_REQUEST, - base::GUID::GenerateRandomV4().AsLowercaseString(), + base::Uuid::GenerateRandomV4().AsLowercaseString(), /*critical=*/true, DMAuth::NoAuth(), /*oauth_token=*/absl::nullopt, context.url_loader_factory, base::BindOnce(&RlweOprf::OnRequestDone, weak_factory_.GetWeakPtr(), @@ -382,7 +382,7 @@ context.device_management_service, DeviceManagementService::JobConfiguration:: TYPE_PSM_HAS_DEVICE_STATE_REQUEST, - base::GUID::GenerateRandomV4().AsLowercaseString(), + base::Uuid::GenerateRandomV4().AsLowercaseString(), /*critical=*/true, DMAuth::NoAuth(), /*oauth_token=*/absl::nullopt, context.url_loader_factory, base::BindOnce(&RlweQuery::OnRequestDone, weak_factory_.GetWeakPtr(), @@ -493,7 +493,7 @@ auto config = std::make_unique<DMServerJobConfiguration>( context.device_management_service, DeviceManagementService::JobConfiguration::TYPE_DEVICE_STATE_RETRIEVAL, - base::GUID::GenerateRandomV4().AsLowercaseString(), + base::Uuid::GenerateRandomV4().AsLowercaseString(), /*critical=*/true, DMAuth::NoAuth(), /*oauth_token=*/absl::nullopt, context.url_loader_factory, base::BindOnce(&EnrollmentState::OnRequestDone,
diff --git a/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc b/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc index 9e41986..1572ff2e 100644 --- a/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc +++ b/chrome/browser/browsing_data/browsing_data_remover_browsertest.cc
@@ -241,6 +241,19 @@ << GetCookiesTreeModelInfo(model->GetRoot()); } + inline void ExpectCookieTreeModelCount(int expected3PSPDisabled, + int expected3PSPEnabled) { + // TODO(crbug.com/1307796): Use a different approach to determine presence + // of data that does not depend on UI code and has a better resolution when + // 3PSP is fully enabled. + if (base::FeatureList::IsEnabled( + net::features::kThirdPartyStoragePartitioning)) { + ExpectCookieTreeModelCount(expected3PSPEnabled); + } else { + ExpectCookieTreeModelCount(expected3PSPDisabled); + } + } + void OnVideoDecodePerfInfo(base::RunLoop* run_loop, bool* out_is_smooth, bool* out_is_power_efficient, @@ -1475,8 +1488,11 @@ // Expect all datatypes from above except SessionStorage and possibly // MediaLicense. SessionStorage is not supported by the CookieTreeModel yet. // MediaLicense is integrated into the quota node, which is not yet fully - // hooked into CookieTreeModel (see crbug.com/1307796). - ExpectCookieTreeModelCount(kStorageTypes.size() - 2); + // hooked into CookieTreeModel. When 3PSP is enabled, only Cookies and + // LocalStorage are counted. TODO(crbug.com/1307796): Use a different approach + // to determine presence of data that does not depend on UI code and has a + // better resolution when 3PSP is fully enabled. + ExpectCookieTreeModelCount(kStorageTypes.size() - 2, 2); RemoveAndWait(chrome_browsing_data_remover::DATA_TYPE_SITE_DATA | content::BrowsingDataRemover::DATA_TYPE_CACHE | chrome_browsing_data_remover::DATA_TYPE_HISTORY | @@ -1528,8 +1544,11 @@ EXPECT_TRUE(HasDataForType(type)); } // Expect the datatypes from above except SessionStorage. SessionStorage is - // not supported by the CookieTreeModel yet. - ExpectCookieTreeModelCount(kSessionOnlyStorageTestTypes.size() - 1); + // not supported by the CookieTreeModel yet. When 3PSP is enabled, only + // Cookies and LocalStorage are counted. TODO(crbug.com/1307796): Use a + // different approach to determine presence of data that does not depend on UI + // code and has a better resolution when 3PSP is fully enabled. + ExpectCookieTreeModelCount(kSessionOnlyStorageTestTypes.size() - 1, 2); HostContentSettingsMapFactory::GetForProfile(GetBrowser()->profile()) ->SetDefaultContentSetting(ContentSettingsType::COOKIES, CONTENT_SETTING_SESSION_ONLY);
diff --git a/chrome/browser/browsing_data/browsing_data_remover_browsertest_base.cc b/chrome/browser/browsing_data/browsing_data_remover_browsertest_base.cc index 5a24e5b6..6414f21 100644 --- a/chrome/browser/browsing_data/browsing_data_remover_browsertest_base.cc +++ b/chrome/browser/browsing_data/browsing_data_remover_browsertest_base.cc
@@ -379,11 +379,7 @@ int count = 0; for (const auto& node : root->children()) { EXPECT_GE(node->children().size(), 1u); - count += base::ranges::count_if(node->children(), [](const auto& child) { - // TODO(crbug.com/1307796): Include quota nodes. - return child->GetDetailedInfo().node_type != - CookieTreeNode::DetailedInfo::TYPE_QUOTA; - }); + count += node->children().size(); } return count; }
diff --git a/chrome/browser/chrome_service_worker_browsertest.cc b/chrome/browser/chrome_service_worker_browsertest.cc index f074a342..29736d13 100644 --- a/chrome/browser/chrome_service_worker_browsertest.cc +++ b/chrome/browser/chrome_service_worker_browsertest.cc
@@ -537,13 +537,6 @@ InitializeServiceWorkerFetchTestPage(); } - std::string ExecuteScriptAndExtractString(const std::string& js) { - std::string result; - EXPECT_TRUE(content::ExecuteScriptAndExtractString( - browser()->tab_strip_model()->GetActiveWebContents(), js, &result)); - return result; - } - std::string RequestString(const std::string& url, const std::string& mode, const std::string& credentials, @@ -714,7 +707,9 @@ url.c_str())); ExecuteJavaScriptForTests(js); waiter.Wait(); - return ExecuteScriptAndExtractString("reportRequests();"); + return EvalJs(browser()->tab_strip_model()->GetActiveWebContents(), + "reportRequests();", content::EXECUTE_SCRIPT_USE_MANUAL_REPLY) + .ExtractString(); } void CopyTestFile(const std::string& src, const std::string& dst) { @@ -751,9 +746,11 @@ .GetManifest( base::BindOnce(&ManifestCallbackAndRun, run_loop.QuitClosure())); run_loop.Run(); - return ExecuteScriptAndExtractString( - "if (issuedRequests.length != 0) reportRequests();" - "else reportOnFetch = true;"); + return EvalJs(browser()->tab_strip_model()->GetActiveWebContents(), + "if (issuedRequests.length != 0) reportRequests();" + "else reportOnFetch = true;", + content::EXECUTE_SCRIPT_USE_MANUAL_REPLY) + .ExtractString(); } static void ManifestCallbackAndRun(base::OnceClosure continuation, @@ -853,14 +850,20 @@ } std::string ExecutePNACLUrlLoaderTest(const std::string& mode) { - std::string result(ExecuteScriptAndExtractString( - base::StringPrintf("reportOnFetch = false;" - "var iframe = document.createElement('iframe');" - "iframe.src='%s#%s';" - "document.body.appendChild(iframe);", - test_page_url_.c_str(), mode.c_str()))); + std::string result( + EvalJs( + browser()->tab_strip_model()->GetActiveWebContents(), + base::StringPrintf("reportOnFetch = false;" + "var iframe = document.createElement('iframe');" + "iframe.src='%s#%s';" + "document.body.appendChild(iframe);", + test_page_url_.c_str(), mode.c_str()), + content::EXECUTE_SCRIPT_USE_MANUAL_REPLY) + .ExtractString()); EXPECT_EQ(base::StringPrintf("OnOpen%s", mode.c_str()), result); - return ExecuteScriptAndExtractString("reportRequests();"); + return EvalJs(browser()->tab_strip_model()->GetActiveWebContents(), + "reportRequests();") + .ExtractString(); } private:
diff --git a/chrome/browser/chromeos/extensions/telemetry/chromeos_permission_messages_unittest.cc b/chrome/browser/chromeos/extensions/telemetry/chromeos_permission_messages_unittest.cc index eacad2a..5860e23 100644 --- a/chrome/browser/chromeos/extensions/telemetry/chromeos_permission_messages_unittest.cc +++ b/chrome/browser/chromeos/extensions/telemetry/chromeos_permission_messages_unittest.cc
@@ -46,7 +46,7 @@ const std::u16string kTelemetryNetworkInformationPermissionMessage = u"Read ChromeOS network information"; const std::u16string kAttachedDeviceInfo = - u"Read attached device information and data"; + u"Read attached devices information and data"; } // namespace // Tests that ChromePermissionMessageProvider provides not only correct, but
diff --git a/chrome/browser/download/bubble/download_bubble_ui_controller.cc b/chrome/browser/download/bubble/download_bubble_ui_controller.cc index 801aaa5..0f7edd9 100644 --- a/chrome/browser/download/bubble/download_bubble_ui_controller.cc +++ b/chrome/browser/download/bubble/download_bubble_ui_controller.cc
@@ -63,7 +63,6 @@ : browser_(browser), profile_(browser->profile()), update_service_(update_service), - download_manager_(profile_->GetDownloadManager()), offline_manager_( OfflineItemModelManagerFactory::GetForBrowserContext(profile_)) {} @@ -78,10 +77,6 @@ display_controller_->HandleButtonPressed(); } -void DownloadBubbleUIController::OnDownloadManagerGoingDown() { - download_manager_ = nullptr; -} - void DownloadBubbleUIController::OnOfflineItemsAdded( const OfflineContentProvider::OfflineItemList& items) { display_controller_->OnNewItem(/*show_animation=*/false); @@ -147,6 +142,9 @@ std::vector<DownloadUIModelPtr> DownloadBubbleUIController::GetDownloadUIModels( bool is_main_view) { std::vector<DownloadUIModelPtr> all_items; + if (!update_service_->IsInitialized()) { + return all_items; + } update_service_->GetAllModelsToDisplay( all_items, /*force_backfill_download_items=*/true); std::vector<DownloadUIModelPtr> items_to_return; @@ -246,7 +244,8 @@ DownloadCommands::Command command) { DCHECK(command == DownloadCommands::RETRY); display_controller_->HideBubble(); - if (!download_manager_) { + content::DownloadManager* download_manager = profile_->GetDownloadManager(); + if (!download_manager) { return; } RecordDownloadRetry( @@ -281,7 +280,7 @@ download_url_params->set_download_source( download::DownloadSource::RETRY_FROM_BUBBLE); - download_manager_->DownloadUrl(std::move(download_url_params)); + download_manager->DownloadUrl(std::move(download_url_params)); } void DownloadBubbleUIController::ScheduleCancelForEphemeralWarning(
diff --git a/chrome/browser/download/bubble/download_bubble_ui_controller.h b/chrome/browser/download/bubble/download_bubble_ui_controller.h index 2a57a6b..8db427b6 100644 --- a/chrome/browser/download/bubble/download_bubble_ui_controller.h +++ b/chrome/browser/download/bubble/download_bubble_ui_controller.h
@@ -14,7 +14,6 @@ #include "components/download/content/public/all_download_item_notifier.h" #include "components/offline_items_collection/core/offline_content_aggregator.h" #include "components/offline_items_collection/core/offline_content_provider.h" -#include "content/public/browser/download_manager.h" #include "third_party/abseil-cpp/absl/types/optional.h" class Profile; @@ -47,7 +46,6 @@ bool may_show_animation); void OnDownloadItemUpdated(download::DownloadItem* item); void OnDownloadItemRemoved(download::DownloadItem* item); - void OnDownloadManagerGoingDown(); void OnOfflineItemsAdded( const OfflineContentProvider::OfflineItemList& items); void OnOfflineItemUpdated(const OfflineItem& item); @@ -102,10 +100,6 @@ DownloadBubbleUpdateService* update_service() { return update_service_; } - void set_manager_for_testing(content::DownloadManager* manager) { - download_manager_ = manager; - } - private: friend class DownloadBubbleUIControllerTest; friend class DownloadBubbleUIControllerIncognitoTest; @@ -120,7 +114,6 @@ raw_ptr<Browser, DanglingUntriaged> browser_; raw_ptr<Profile, DanglingUntriaged> profile_; raw_ptr<DownloadBubbleUpdateService> update_service_; - raw_ptr<content::DownloadManager, DanglingUntriaged> download_manager_; raw_ptr<OfflineItemModelManager, DanglingUntriaged> offline_manager_; // DownloadDisplayController and DownloadBubbleUIController have the same
diff --git a/chrome/browser/download/bubble/download_bubble_ui_controller_unittest.cc b/chrome/browser/download/bubble/download_bubble_ui_controller_unittest.cc index 20442eb..dde8288 100644 --- a/chrome/browser/download/bubble/download_bubble_ui_controller_unittest.cc +++ b/chrome/browser/download/bubble/download_bubble_ui_controller_unittest.cc
@@ -123,6 +123,8 @@ void AddModel(ModelType type) { model_types_.push_back(type); } + bool IsInitialized() const override { return true; } + MOCK_METHOD(DownloadDisplayController::ProgressInfo, GetProgressInfo, (), @@ -184,8 +186,6 @@ second_display_controller_ = std::make_unique<NiceMock<MockDownloadDisplayController>>( browser_.get(), second_controller_.get()); - controller_->set_manager_for_testing(manager_); - second_controller_->set_manager_for_testing(manager_); } void TearDown() override {
diff --git a/chrome/browser/download/bubble/download_bubble_update_service.cc b/chrome/browser/download/bubble/download_bubble_update_service.cc index a65918b..e75e55d 100644 --- a/chrome/browser/download/bubble/download_bubble_update_service.cc +++ b/chrome/browser/download/bubble/download_bubble_update_service.cc
@@ -15,6 +15,7 @@ #include "base/time/time.h" #include "chrome/browser/content_index/content_index_provider_impl.h" #include "chrome/browser/download/bubble/download_bubble_ui_controller.h" +#include "chrome/browser/download/bubble/download_bubble_update_service_factory.h" #include "chrome/browser/download/bubble/download_bubble_utils.h" #include "chrome/browser/download/bubble/download_display_controller.h" #include "chrome/browser/download/download_crx_util.h" @@ -166,19 +167,11 @@ DownloadBubbleUpdateService::DownloadBubbleUpdateService(Profile* profile) : profile_(profile), - original_profile_( - profile->IsOffTheRecord() ? profile_->GetOriginalProfile() : nullptr), - download_item_notifier_(profile->GetDownloadManager(), this), - original_download_item_notifier_( - profile->IsOffTheRecord() - ? absl::make_optional<download::AllDownloadItemNotifier>( - original_profile_->GetDownloadManager(), - this) - : absl::nullopt) { + original_profile_(profile->IsOffTheRecord() + ? profile_->GetOriginalProfile() + : nullptr) { offline_content_provider_observation_.Observe( OfflineContentAggregatorFactory::GetForKey(profile_->GetProfileKey())); - InitializeDownloadItemsCache(); - StartInitializeOfflineItemsCache(); } DownloadBubbleUpdateService::~DownloadBubbleUpdateService() = default; @@ -206,6 +199,72 @@ return cache.size() == GetNumItemsToCache(); } +void DownloadBubbleUpdateService::Initialize( + content::DownloadManager* manager) { + CHECK(manager); + CHECK(!download_item_notifier_); + + // Assume we have an original profile and it has an OTR profile. + // If the original profile's DownloadBubbleUpdateService is Initialize()'d + // already when this function is invoked on the OTR profile's + // DownloadBubbleUpdateService, we set the OTR profile's + // DownloadBubbleUpdateService's |original_download_item_notifier_| in the + // 'if' block below. If the original profile's DownloadBubbleUpdateService is + // not yet initialized when this function is invoked on the OTR profile's + // DownloadBubbleUpdateService, we will set the OTR profile's + // DownloadBubbleUpdateService's |original_download_item_notifier_| when the + // original profile's DownloadBubbleUpdateService does become Initialize()'d, + // in the 'else' block below (which will trigger re-intialization of the + // download item cache). + if (profile_->IsOffTheRecord()) { + DownloadBubbleUpdateService* original_update_service = + DownloadBubbleUpdateServiceFactory::GetForProfile(original_profile_); + content::DownloadManager* original_download_manager = + original_update_service ? original_update_service->GetDownloadManager() + : nullptr; + if (original_download_manager) { + InitializeOriginalNotifier(original_download_manager); + } + } else { + for (Profile* otr_profile : profile_->GetAllOffTheRecordProfiles()) { + DownloadBubbleUpdateServiceFactory::GetForProfile(otr_profile) + ->InitializeOriginalNotifier(manager); + } + } + download_item_notifier_ = + std::make_unique<download::AllDownloadItemNotifier>(manager, this); + // As long as we have a notifier for this profile, we can initialize the cache + // with the current profile's downloads. If we get an original manager in the + // future, we will initialize from scratch at that time. + InitializeDownloadItemsCache(); + StartInitializeOfflineItemsCache(); +} + +void DownloadBubbleUpdateService::InitializeOriginalNotifier( + content::DownloadManager* manager) { + CHECK(profile_->IsOffTheRecord()); + CHECK(manager); + if (original_download_item_notifier_) { + return; + } + original_download_item_notifier_ = + std::make_unique<download::AllDownloadItemNotifier>(manager, this); + // Reset the download items cache, now that we have an original + // DownloadManager to pull from. + if (download_item_notifier_) { + InitializeDownloadItemsCache(); + } +} + +content::DownloadManager* DownloadBubbleUpdateService::GetDownloadManager() { + return download_item_notifier_ ? download_item_notifier_->GetManager() + : nullptr; +} + +bool DownloadBubbleUpdateService::IsInitialized() const { + return download_item_notifier_ && offline_items_initialized_; +} + bool DownloadBubbleUpdateService::GetAllModelsToDisplay( std::vector<DownloadUIModelPtr>& models, bool force_backfill_download_items) { @@ -343,7 +402,8 @@ void DownloadBubbleUpdateService::OnDownloadCreated( content::DownloadManager* manager, download::DownloadItem* item) { - if (download_manager_shut_down_) { + CHECK(download_item_notifier_ || original_download_item_notifier_); + if (!download_item_notifier_) { return; } if (download_crx_util::IsExtensionDownload(*item) && @@ -364,13 +424,14 @@ void DownloadBubbleUpdateService::OnDelayedCrxDownloadCreated( const std::string& guid) { - if (download_manager_shut_down_) { + CHECK(download_item_notifier_ || original_download_item_notifier_); + if (!download_item_notifier_) { return; } // This assumes that for extension/theme downloads, the DownloadItem is // removed from the DownloadManager upon completion. download::DownloadItem* item = - download_item_notifier_.GetManager()->GetDownloadByGuid(guid); + download_item_notifier_->GetManager()->GetDownloadByGuid(guid); if (item && !item->IsDone()) { MaybeAddDownloadItemToCache(item, /*is_new=*/true); @@ -391,7 +452,8 @@ void DownloadBubbleUpdateService::OnDownloadUpdated( content::DownloadManager* manager, download::DownloadItem* item) { - if (download_manager_shut_down_) { + CHECK(download_item_notifier_ || original_download_item_notifier_); + if (!download_item_notifier_) { return; } // If the item is an extension or theme download waiting out its 2-second @@ -421,7 +483,8 @@ void DownloadBubbleUpdateService::OnDownloadRemoved( content::DownloadManager* manager, download::DownloadItem* item) { - if (download_manager_shut_down_) { + CHECK(download_item_notifier_ || original_download_item_notifier_); + if (!download_item_notifier_) { return; } bool cache_was_at_max = IsCacheAtMax(download_items_); @@ -442,21 +505,15 @@ void DownloadBubbleUpdateService::OnManagerGoingDown( content::DownloadManager* manager) { + CHECK(download_item_notifier_ || original_download_item_notifier_); // Assume that the original manager (if this is an OTR profile) may or may not // have shut down, but we still want to cease all operations when this // profile's manager shuts down. - if (manager == profile_->GetDownloadManager()) { - download_manager_shut_down_ = true; + if (download_item_notifier_ && + (manager == download_item_notifier_->GetManager())) { download_items_.clear(); download_items_iter_map_.clear(); - for (Browser* browser : chrome::FindAllBrowsersWithProfile(profile_)) { - if (browser->window() && - browser->window()->GetDownloadBubbleUIController()) { - browser->window() - ->GetDownloadBubbleUIController() - ->OnDownloadManagerGoingDown(); - } - } + download_item_notifier_.reset(); } } @@ -677,7 +734,7 @@ void DownloadBubbleUpdateService::BackfillDownloadItems( const ItemSortKey& last_key) { - if (download_manager_shut_down_) { + if (!download_item_notifier_) { return; } BackfillItemsImpl(last_key, GetAllDownloadItems(), download_items_, @@ -721,6 +778,7 @@ } void DownloadBubbleUpdateService::InitializeDownloadItemsCache() { + CHECK(download_item_notifier_); download_items_.clear(); download_items_iter_map_.clear(); for (download::DownloadItem* item : GetAllDownloadItems()) { @@ -729,6 +787,9 @@ } void DownloadBubbleUpdateService::StartInitializeOfflineItemsCache() { + if (offline_items_initialized_) { + return; + } offline_items_collection::OfflineContentProvider* provider = OfflineContentAggregatorFactory::GetForKey(profile_->GetProfileKey()); provider->GetAllItems( @@ -753,17 +814,11 @@ std::vector<download::DownloadItem*> DownloadBubbleUpdateService::GetAllDownloadItems() { std::vector<download::DownloadItem*> all_items; - content::DownloadManager* manager = download_item_notifier_.GetManager(); - if (!manager) { - return all_items; + if (download_item_notifier_) { + download_item_notifier_->GetManager()->GetAllDownloads(&all_items); } - manager->GetAllDownloads(&all_items); if (original_download_item_notifier_) { - content::DownloadManager* original_manager = - original_download_item_notifier_->GetManager(); - if (original_manager) { - original_manager->GetAllDownloads(&all_items); - } + original_download_item_notifier_->GetManager()->GetAllDownloads(&all_items); } return all_items; }
diff --git a/chrome/browser/download/bubble/download_bubble_update_service.h b/chrome/browser/download/bubble/download_bubble_update_service.h index 4c3c0bc..5c3e8a0c 100644 --- a/chrome/browser/download/bubble/download_bubble_update_service.h +++ b/chrome/browser/download/bubble/download_bubble_update_service.h
@@ -97,6 +97,24 @@ // anyway. Virtual for testing. virtual DownloadDisplayController::ProgressInfo GetProgressInfo() const; + // Initializes AllDownloadItemNotifier for the current profile, and + // initializes caches. This is called when the manager is ready, to signal + // that the DownloadBubbleUpdateService should begin tracking downloads. This + // starts initialization of both the download items and the offline items. + // Should only be called once. + void Initialize(content::DownloadManager* manager); + + // Initializes the AllDownloadItemNotifier for the original profile, if + // |profile_| is off the record. May trigger re-initialization of the download + // items cache. + void InitializeOriginalNotifier(content::DownloadManager* manager); + + // Get the DownloadManager that |download_item_notifier_| is listening to. + content::DownloadManager* GetDownloadManager(); + + // Virtual for testing. + virtual bool IsInitialized() const; + // KeyedService: void Shutdown() override; @@ -130,7 +148,12 @@ } download::AllDownloadItemNotifier& download_item_notifier_for_testing() { - return download_item_notifier_; + return *download_item_notifier_; + } + + download::AllDownloadItemNotifier& + original_download_item_notifier_for_testing() { + return *original_download_item_notifier_; } private: @@ -278,18 +301,19 @@ DownloadItemIterMap download_items_iter_map_; OfflineItemIterMap offline_items_iter_map_; - // Notifier for the current profile's DownloadManager. - download::AllDownloadItemNotifier download_item_notifier_; - // Nullopt if the profile is not OTR. If the profile is OTR, this holds a - // notifier for the original profile. - absl::optional<download::AllDownloadItemNotifier> + // Notifier for the current profile's DownloadManager. Null until initialized + // in Initialize(). + std::unique_ptr<download::AllDownloadItemNotifier> download_item_notifier_; + // Null if the profile is not OTR. Null until the original profile initiates + // a download. If the profile is OTR, this holds a notifier for the original + // profile. + std::unique_ptr<download::AllDownloadItemNotifier> original_download_item_notifier_; bool offline_items_initialized_ = false; // Holds functions queued up while offline items were being initialized. std::vector<base::OnceClosure> offline_item_callbacks_; - bool download_manager_shut_down_ = false; bool offline_content_provider_shut_down_ = false; // Set of GUIDs for extension/theme (crx) downloads that are pending notifying
diff --git a/chrome/browser/download/bubble/download_bubble_update_service_factory.cc b/chrome/browser/download/bubble/download_bubble_update_service_factory.cc index 12936b9..7a08fe4 100644 --- a/chrome/browser/download/bubble/download_bubble_update_service_factory.cc +++ b/chrome/browser/download/bubble/download_bubble_update_service_factory.cc
@@ -28,7 +28,12 @@ DownloadBubbleUpdateServiceFactory::DownloadBubbleUpdateServiceFactory() : ProfileKeyedServiceFactory( "DownloadBubbleUpdateService", - ProfileSelections::BuildForRegularAndIncognito()) { + ProfileSelections::Builder() + .WithRegular(ProfileSelection::kOwnInstance) + .WithGuest(ProfileSelection::kOffTheRecordOnly) + .WithSystem(ProfileSelection::kNone) + .WithAshInternals(ProfileSelection::kNone) + .Build()) { DependsOn(OfflineContentAggregatorFactory::GetInstance()); DependsOn(OfflineItemModelManagerFactory::GetInstance()); }
diff --git a/chrome/browser/download/bubble/download_bubble_update_service_unittest.cc b/chrome/browser/download/bubble/download_bubble_update_service_unittest.cc index e04c68a..000c713 100644 --- a/chrome/browser/download/bubble/download_bubble_update_service_unittest.cc +++ b/chrome/browser/download/bubble/download_bubble_update_service_unittest.cc
@@ -8,6 +8,7 @@ #include "base/test/scoped_feature_list.h" #include "base/time/time.h" +#include "chrome/browser/download/bubble/download_bubble_update_service_factory.h" #include "chrome/browser/download/bubble/download_display_controller.h" #include "chrome/browser/download/download_ui_model.h" #include "chrome/browser/offline_items_collection/offline_content_aggregator_factory.h" @@ -37,7 +38,7 @@ using ::testing::NiceMock; using ::testing::Return; using ::testing::ReturnRefOfCopy; -using ::testing::SetArgPointee; +using ::testing::WithArg; using DownloadState = download::DownloadItem::DownloadState; using DownloadUIModelPtrVector = std::vector<DownloadUIModel::DownloadUIModelPtr>; @@ -46,6 +47,23 @@ constexpr char kProfileName[] = "testing_profile"; constexpr char kProviderNamespace[] = "mock_namespace"; +// Helper for MockDownloadManager to properly mimic GetAllDownloads(), such that +// it appends all items in |items| to the argument of the action, which should +// be a std::vector<DownloadItem*>. +ACTION_P(AddDownloadItems, items) { + for (download::DownloadItem* item : items) { + arg0->push_back(item); + } +} + +void RemoveDownloadItemsObserver( + std::vector<std::unique_ptr<NiceMockDownloadItem>>& download_items, + download::DownloadItem::Observer& observer) { + for (auto& item : download_items) { + item->RemoveObserver(&observer); + } +} + class DownloadBubbleUpdateServiceTest : public testing::Test { public: DownloadBubbleUpdateServiceTest() @@ -59,36 +77,58 @@ const DownloadBubbleUpdateServiceTest&) = delete; ~DownloadBubbleUpdateServiceTest() override = default; + raw_ptr<NiceMock<content::MockDownloadManager>> SetUpDownloadManager( + Profile* profile) { + auto download_manager = + std::make_unique<NiceMock<content::MockDownloadManager>>(); + raw_ptr<NiceMock<content::MockDownloadManager>> manager = + download_manager.get(); + EXPECT_CALL(*download_manager, GetBrowserContext()) + .WillRepeatedly(Return(profile)); + EXPECT_CALL(*download_manager, RemoveObserver(_)).WillRepeatedly(Return()); + profile->SetDownloadManagerForTesting(std::move(download_manager)); + return manager; + } + + // Pass a null |download_manager| to avoid registering the download manager. + std::unique_ptr<KeyedService> InitUpdateService( + NiceMock<content::MockDownloadManager>* download_manager, + DownloadBubbleUpdateService* update_service, + content::BrowserContext* context) { + // Unregister the observer from the previous instance of the update service. + // See note below in TearDown() for why this is necessary. + if (update_service) { + RemoveDownloadItemsObserver( + download_items_, + update_service->download_item_notifier_for_testing()); + } + auto service = std::make_unique<DownloadBubbleUpdateService>( + Profile::FromBrowserContext(context)); + if (download_manager) { + service->Initialize(download_manager); + } + task_environment_.RunUntilIdle(); + return service; + } + void SetUp() override { ASSERT_TRUE(testing_profile_manager_.SetUp()); profile_ = testing_profile_manager_.CreateTestingProfile(kProfileName); - auto download_manager = - std::make_unique<NiceMock<content::MockDownloadManager>>(); - download_manager_ = download_manager.get(); - EXPECT_CALL(*download_manager, GetBrowserContext()) - .WillRepeatedly(Return(profile_.get())); - EXPECT_CALL(*download_manager, RemoveObserver(_)).WillRepeatedly(Return()); - profile_->SetDownloadManagerForTesting(std::move(download_manager)); + download_manager_ = SetUpDownloadManager(profile_); offline_content_provider_ = std::make_unique< NiceMock<offline_items_collection::MockOfflineContentProvider>>(); OfflineContentAggregatorFactory::GetForKey(profile_->GetProfileKey()) ->RegisterProvider(kProviderNamespace, offline_content_provider_.get()); - InitUpdateService(); - } - - void InitUpdateService() { - // Unregister the observer from the previous instance of the update service. - // See note below in TearDown() for why this is necessary. - if (update_service_) { - for (auto& item : download_items_) { - item->RemoveObserver( - &update_service_->download_item_notifier_for_testing()); - } - } - update_service_ = std::make_unique<DownloadBubbleUpdateService>(profile_); - task_environment_.RunUntilIdle(); + update_service_ = static_cast<DownloadBubbleUpdateService*>( + DownloadBubbleUpdateServiceFactory::GetInstance() + ->SetTestingFactoryAndUse( + profile_, + base::BindRepeating( + &DownloadBubbleUpdateServiceTest::InitUpdateService, + base::Unretained(this), download_manager_.get(), + update_service_))); } void TearDown() override { @@ -96,14 +136,12 @@ // notifier doesn't know what items it's observing because we have manually // added it as an observer on each item, since the MockDownloadManager does // not actually call OnDownloadCreated on it. - for (auto& item : download_items_) { - item->RemoveObserver( - &update_service_->download_item_notifier_for_testing()); + if (update_service_) { + RemoveDownloadItemsObserver( + download_items_, + update_service_->download_item_notifier_for_testing()); } - update_service_.reset(); offline_content_provider_.reset(); - download_manager_ = nullptr; - profile_ = nullptr; testing_profile_manager_.DeleteTestingProfile(kProfileName); } @@ -113,18 +151,35 @@ return *download_items_[index]; } + // Forwards to the below version. void InitDownloadItem(DownloadState state, const std::string& guid, bool is_paused, base::Time start_time = base::Time::Now(), bool is_crx = false, bool observe = true) { - size_t index = download_items_.size(); - download_items_.push_back(std::make_unique<NiceMockDownloadItem>()); - auto& item = GetDownloadItem(index); + InitDownloadItem(*download_manager_, *update_service_, download_items_, + profile_, state, guid, is_paused, start_time, is_crx, + observe); + } + + void InitDownloadItem( + NiceMock<content::MockDownloadManager>& download_manager, + DownloadBubbleUpdateService& update_service, + std::vector<std::unique_ptr<NiceMockDownloadItem>>& download_items, + Profile* profile, + DownloadState state, + const std::string& guid, + bool is_paused, + base::Time start_time = base::Time::Now(), + bool is_crx = false, + bool observe = true) { + size_t index = download_items.size(); + download_items.push_back(std::make_unique<NiceMockDownloadItem>()); + auto& item = *download_items[index]; EXPECT_CALL(item, GetId()) .WillRepeatedly( - Return(static_cast<uint32_t>(download_items_.size() + 1))); + Return(static_cast<uint32_t>(download_items.size() + 1))); EXPECT_CALL(item, GetGuid()).WillRepeatedly(ReturnRefOfCopy(guid)); EXPECT_CALL(item, GetState()).WillRepeatedly(Return(state)); EXPECT_CALL(item, GetStartTime()).WillRepeatedly(Return(start_time)); @@ -146,16 +201,16 @@ EXPECT_CALL(item, GetMimeType()) .WillRepeatedly(Return(is_crx ? "application/x-chrome-extension" : "")); std::vector<download::DownloadItem*> items; - for (size_t i = 0; i < download_items_.size(); ++i) { - items.push_back(&GetDownloadItem(i)); + for (auto& download_item : download_items) { + items.push_back(download_item.get()); } - EXPECT_CALL(*download_manager_, GetAllDownloads(_)) - .WillRepeatedly(SetArgPointee<0>(items)); - EXPECT_CALL(*download_manager_, GetDownloadByGuid(guid)) + EXPECT_CALL(download_manager, GetAllDownloads(_)) + .WillRepeatedly(WithArg<0>(AddDownloadItems(items))); + EXPECT_CALL(download_manager, GetDownloadByGuid(guid)) .WillRepeatedly(Return(&item)); - content::DownloadItemUtils::AttachInfoForTesting(&item, profile_, nullptr); + content::DownloadItemUtils::AttachInfoForTesting(&item, profile, nullptr); if (observe) { - item.AddObserver(&update_service_->download_item_notifier_for_testing()); + item.AddObserver(&update_service.download_item_notifier_for_testing()); item.NotifyObserversDownloadUpdated(); } } @@ -189,7 +244,7 @@ items.push_back(&GetDownloadItem(i)); } EXPECT_CALL(*download_manager_, GetAllDownloads(_)) - .WillRepeatedly(SetArgPointee<0>(items)); + .WillRepeatedly(WithArg<0>(AddDownloadItems(items))); } void InitOfflineItems(const std::vector<OfflineItemState>& states, @@ -220,15 +275,15 @@ base::test::ScopedFeatureList feature_list_; content::BrowserTaskEnvironment task_environment_{ base::test::TaskEnvironment::TimeSource::MOCK_TIME}; - raw_ptr<NiceMock<content::MockDownloadManager>> download_manager_; + raw_ptr<NiceMock<content::MockDownloadManager>> download_manager_ = nullptr; std::vector<std::unique_ptr<NiceMockDownloadItem>> download_items_; std::vector<offline_items_collection::OfflineItem> offline_items_; TestingProfileManager testing_profile_manager_; - raw_ptr<TestingProfile> profile_; + raw_ptr<TestingProfile> profile_ = nullptr; std::unique_ptr< NiceMock<offline_items_collection::MockOfflineContentProvider>> offline_content_provider_; - std::unique_ptr<DownloadBubbleUpdateService> update_service_; + raw_ptr<DownloadBubbleUpdateService> update_service_ = nullptr; }; TEST_F(DownloadBubbleUpdateServiceTest, PopulatesCaches) { @@ -250,7 +305,9 @@ // Recreate the update service to check that it pulls in the existing // download items upon initialization. - InitUpdateService(); + auto service = + InitUpdateService(download_manager_, update_service_, profile_); + update_service_ = static_cast<DownloadBubbleUpdateService*>(service.get()); EXPECT_TRUE(update_service_->GetAllModelsToDisplay(models)); ASSERT_EQ(models.size(), 3u); @@ -275,6 +332,11 @@ EXPECT_EQ(models[3]->GetContentId().id, "in_progress_paused_offline_item"); EXPECT_EQ(models[4]->GetContentId().id, "completed_download"); EXPECT_EQ(models[5]->GetContentId().id, "completed_offline_item"); + + // Manually clean up the second service instance to avoid UAF. + RemoveDownloadItemsObserver( + download_items_, update_service_->download_item_notifier_for_testing()); + update_service_ = nullptr; } TEST_F(DownloadBubbleUpdateServiceTest, AddsNonCrxDownloadItems) { @@ -608,4 +670,86 @@ EXPECT_EQ(progress_info.progress_percentage, 50); } +class DownloadBubbleUpdateServiceIncognitoTest + : public DownloadBubbleUpdateServiceTest { + public: + DownloadBubbleUpdateServiceIncognitoTest() = default; + DownloadBubbleUpdateServiceIncognitoTest( + const DownloadBubbleUpdateServiceIncognitoTest&) = delete; + DownloadBubbleUpdateServiceIncognitoTest& operator=( + const DownloadBubbleUpdateServiceIncognitoTest&) = delete; + ~DownloadBubbleUpdateServiceIncognitoTest() override = default; + + void SetUp() override { + DownloadBubbleUpdateServiceTest::SetUp(); + + incognito_profile_ = profile_->GetOffTheRecordProfile( + Profile::OTRProfileID::CreateUniqueForTesting(), + /*create_if_needed=*/true); + incognito_download_manager_ = SetUpDownloadManager(incognito_profile_); + // Pass nullptr for the download_manager to delay RegisterDownloadManager() + // call for the test. + incognito_update_service_ = static_cast<DownloadBubbleUpdateService*>( + DownloadBubbleUpdateServiceFactory::GetInstance() + ->SetTestingFactoryAndUse( + incognito_profile_, + base::BindRepeating( + &DownloadBubbleUpdateServiceTest::InitUpdateService, + base::Unretained(this), /*download_manager=*/nullptr, + incognito_update_service_))); + } + + void TearDown() override { + RemoveDownloadItemsObserver( + incognito_download_items_, + incognito_update_service_->download_item_notifier_for_testing()); + RemoveDownloadItemsObserver( + download_items_, incognito_update_service_ + ->original_download_item_notifier_for_testing()); + DownloadBubbleUpdateServiceTest::TearDown(); + } + + protected: + raw_ptr<Profile> incognito_profile_ = nullptr; + raw_ptr<NiceMock<content::MockDownloadManager>> incognito_download_manager_ = + nullptr; + std::vector<std::unique_ptr<NiceMockDownloadItem>> incognito_download_items_; + raw_ptr<DownloadBubbleUpdateService> incognito_update_service_ = nullptr; +}; + +// Tests that initializing an update service for an incognito profile sets both +// the download manager and the original download manager. +TEST_F(DownloadBubbleUpdateServiceIncognitoTest, InitIncognito) { + base::Time now = base::Time::Now(); + // |observe| is false because this only tests initialization. + InitDownloadItem(DownloadState::COMPLETE, "regular_profile_download", + /*is_paused=*/false, now - base::Hours(1), /*is_crx=*/false, + /*observe=*/false); + InitDownloadItem(*incognito_download_manager_, *incognito_update_service_, + incognito_download_items_, incognito_profile_, + DownloadState::COMPLETE, "incognito_profile_download", + /*is_paused=*/false, now, /*is_crx=*/false, + /*observe=*/false); + + // Initial state: Regular profile's update service has a manager, incognito's + // does not. + EXPECT_NE(update_service_->GetDownloadManager(), nullptr); + EXPECT_EQ(update_service_->GetDownloadManager(), download_manager_); + EXPECT_EQ(incognito_update_service_->GetDownloadManager(), nullptr); + + incognito_update_service_->Initialize(incognito_download_manager_); + // Regular profile's update service's manager hasn't changed. + EXPECT_EQ(update_service_->GetDownloadManager(), download_manager_); + // Incognito profile's update service's manager now set correctly. + EXPECT_EQ(incognito_update_service_->GetDownloadManager(), + incognito_download_manager_); + + // Both download items should be present after initialization. + DownloadUIModelPtrVector models; + EXPECT_TRUE(incognito_update_service_->GetAllModelsToDisplay(models)); + ASSERT_EQ(models.size(), 2u); + EXPECT_EQ(models[0]->GetContentId().id, "incognito_profile_download"); + EXPECT_EQ(models[1]->GetContentId().id, "regular_profile_download"); +} + } // namespace
diff --git a/chrome/browser/download/bubble/download_display_controller.cc b/chrome/browser/download/bubble/download_display_controller.cc index df5aa901..b1c4c58 100644 --- a/chrome/browser/download/bubble/download_display_controller.cc +++ b/chrome/browser/download/bubble/download_display_controller.cc
@@ -96,7 +96,6 @@ DownloadBubbleUIController* bubble_controller) : display_(display), browser_(browser), - download_manager_(browser_->profile()->GetDownloadManager()), bubble_controller_(bubble_controller) { bubble_controller_->SetDownloadDisplayController(this); // |display| can be null in tests. @@ -281,8 +280,12 @@ std::vector<DownloadUIModelPtr> DownloadDisplayController::UpdateButtonStateFromAllModels(bool may_retry) { std::vector<std::unique_ptr<DownloadUIModel>> all_models; - bool results_complete = - bubble_controller_->update_service()->GetAllModelsToDisplay(all_models); + DownloadBubbleUpdateService* update_service = + bubble_controller_->update_service(); + if (!update_service->IsInitialized()) { + return all_models; + } + bool results_complete = update_service->GetAllModelsToDisplay(all_models); UpdateToolbarButtonState(all_models); if (!results_complete && may_retry) { base::SequencedTaskRunner::GetCurrentDefault()->PostTask( @@ -312,7 +315,7 @@ base::Time DownloadDisplayController::GetLastCompleteTime( const std::vector<std::unique_ptr<DownloadUIModel>>& all_models) { - base::Time last_time = DownloadPrefs::FromDownloadManager(download_manager_) + base::Time last_time = DownloadPrefs::FromBrowserContext(browser_->profile()) ->GetLastCompleteTime(); for (const auto& model : all_models) { if (last_time < model->GetEndTime()) {
diff --git a/chrome/browser/download/bubble/download_display_controller.h b/chrome/browser/download/bubble/download_display_controller.h index 91ab749..8df3d37 100644 --- a/chrome/browser/download/bubble/download_display_controller.h +++ b/chrome/browser/download/bubble/download_display_controller.h
@@ -15,10 +15,6 @@ #include "components/offline_items_collection/core/offline_content_aggregator.h" #include "components/offline_items_collection/core/offline_content_provider.h" -namespace content { -class DownloadManager; -} // namespace content - class DownloadBubbleUIController; namespace base { @@ -111,10 +107,6 @@ // Returns the DownloadDisplay. Should always return a valid display. DownloadDisplay* download_display_for_testing() { return display_; } - void set_manager_for_testing(content::DownloadManager* manager) { - download_manager_ = manager; - } - private: friend class DownloadDisplayControllerTest; @@ -137,8 +129,7 @@ // button is already showing. void ShowToolbarButton(); - // Based on the information from `download_manager_`, updates the icon state - // of the `display_`. + // Updates the icon state of the `display_`. void UpdateToolbarButtonState( std::vector<std::unique_ptr<DownloadUIModel>>& all_models); // Asks `display_` to make the download icon inactive. @@ -158,7 +149,6 @@ raw_ptr<Browser> browser_; base::ScopedObservation<FullscreenController, FullscreenObserver> observation_{this}; - raw_ptr<content::DownloadManager, DanglingUntriaged> download_manager_; base::OneShotTimer icon_disappearance_timer_; base::OneShotTimer icon_inactive_timer_; IconInfo icon_info_;
diff --git a/chrome/browser/download/bubble/download_display_controller_unittest.cc b/chrome/browser/download/bubble/download_display_controller_unittest.cc index 183512bc..3bb721fa 100644 --- a/chrome/browser/download/bubble/download_display_controller_unittest.cc +++ b/chrome/browser/download/bubble/download_display_controller_unittest.cc
@@ -41,7 +41,6 @@ using ::testing::Return; using ::testing::ReturnRef; using ::testing::ReturnRefOfCopy; -using ::testing::SetArgPointee; using ::testing::StrictMock; using StrictMockDownloadItem = StrictMock<download::MockDownloadItem>; using DownloadIconState = download::DownloadIconState; @@ -169,6 +168,8 @@ } } + bool IsInitialized() const override { return true; } + MOCK_METHOD(DownloadDisplayController::ProgressInfo, GetProgressInfo, (), @@ -207,8 +208,7 @@ class DownloadDisplayControllerTest : public testing::Test { public: DownloadDisplayControllerTest() - : manager_(std::make_unique<NiceMock<content::MockDownloadManager>>()), - testing_profile_manager_(TestingBrowserProcess::GetGlobal()) { + : testing_profile_manager_(TestingBrowserProcess::GetGlobal()) { base::CommandLine::ForCurrentProcess()->AppendSwitch(switches::kNoFirstRun); } DownloadDisplayControllerTest(const DownloadDisplayControllerTest&) = delete; @@ -219,8 +219,6 @@ ASSERT_TRUE(testing_profile_manager_.SetUp()); profile_ = testing_profile_manager_.CreateTestingProfile("testing_profile"); - EXPECT_CALL(*manager_.get(), GetBrowserContext()) - .WillRepeatedly(Return(profile_.get())); DownloadCoreServiceFactory::GetInstance()->SetTestingFactory( profile_, base::BindRepeating(&BuildMockDownloadCoreService)); @@ -243,10 +241,8 @@ browser_ = std::unique_ptr<Browser>(Browser::Create(params)); bubble_controller_ = std::make_unique<DownloadBubbleUIController>( browser_.get(), mock_update_service_.get()); - bubble_controller_->set_manager_for_testing(manager_.get()); controller_ = std::make_unique<DownloadDisplayController>( display_.get(), browser_.get(), bubble_controller_.get()); - controller_->set_manager_for_testing(manager_.get()); display_->SetController(controller_.get()); } @@ -260,7 +256,6 @@ Browser* browser() { return browser_.get(); } protected: - NiceMock<content::MockDownloadManager>& manager() { return *manager_.get(); } download::MockDownloadItem& item(size_t index) { return *items_[index]; } FakeDownloadDisplay& display() { return *display_; } DownloadDisplayController& controller() { return *controller_; } @@ -308,8 +303,6 @@ if (state == DownloadState::IN_PROGRESS) { in_progress_count_++; } - EXPECT_CALL(manager(), InProgressCount()) - .WillRepeatedly(Return(in_progress_count_)); // Set actioned_on to false (it defaults to true) because the controller // will generally set this to false in OnNewItem(). DownloadItemModel(&item(index)).SetActionedOn(false); @@ -318,8 +311,6 @@ for (size_t i = 0; i < items_.size(); ++i) { items.push_back(&item(i)); } - EXPECT_CALL(*manager_.get(), GetAllDownloads(_)) - .WillRepeatedly(SetArgPointee<0>(items)); content::DownloadItemUtils::AttachInfoForTesting(&(item(index)), profile_, nullptr); mock_update_service_->AddModel( @@ -373,15 +364,13 @@ .WillRepeatedly(Return(danger_type)); if (state == DownloadState::COMPLETE) { EXPECT_CALL(item(item_index), IsDone()).WillRepeatedly(Return(true)); - DownloadPrefs::FromDownloadManager(&manager()) - ->SetLastCompleteTime(base::Time::Now()); + DownloadPrefs::FromBrowserContext(profile())->SetLastCompleteTime( + base::Time::Now()); } else { EXPECT_CALL(item(item_index), IsDone()).WillRepeatedly(Return(false)); } if (state == DownloadState::COMPLETE || in_progress_dangerous) { in_progress_count_--; - EXPECT_CALL(manager(), InProgressCount()) - .WillRepeatedly(Return(in_progress_count_)); } controller().OnUpdatedItem( state == DownloadState::COMPLETE || in_progress_dangerous, @@ -396,8 +385,6 @@ for (size_t i = 0; i < items_.size(); ++i) { items.push_back(&item(i)); } - EXPECT_CALL(*manager_.get(), GetAllDownloads(_)) - .WillRepeatedly(SetArgPointee<0>(items)); mock_update_service_->RemoveLastDownload(); } @@ -441,7 +428,6 @@ std::unique_ptr<FakeDownloadDisplay> display_; std::vector<std::unique_ptr<StrictMockDownloadItem>> items_; std::vector<OfflineItem> offline_items_; - std::unique_ptr<NiceMock<content::MockDownloadManager>> manager_; std::unique_ptr<StrictMock<MockDownloadBubbleUpdateService>> mock_update_service_; std::unique_ptr<DownloadBubbleUIController> bubble_controller_; @@ -833,8 +819,8 @@ download::DownloadItem::COMPLETE); base::Time current_time = base::Time::Now(); // Set the last complete time to more than 1 hour ago. - DownloadPrefs::FromDownloadManager(&manager()) - ->SetLastCompleteTime(current_time - base::Minutes(61)); + DownloadPrefs::FromBrowserContext(profile())->SetLastCompleteTime( + current_time - base::Minutes(61)); DownloadDisplayController controller(&display(), browser(), &bubble_controller()); @@ -848,8 +834,8 @@ download::DownloadItem::COMPLETE); base::Time current_time = base::Time::Now(); // Set the last complete time to less than 1 hour ago. - DownloadPrefs::FromDownloadManager(&manager()) - ->SetLastCompleteTime(current_time - base::Minutes(59)); + DownloadPrefs::FromBrowserContext(profile())->SetLastCompleteTime( + current_time - base::Minutes(59)); DownloadDisplayController controller(&display(), browser(), &bubble_controller()); @@ -883,8 +869,8 @@ InitialState_NewLastDownloadWithEmptyItem) { base::Time current_time = base::Time::Now(); // Set the last complete time to less than 1 hour ago. - DownloadPrefs::FromDownloadManager(&manager()) - ->SetLastCompleteTime(current_time - base::Minutes(59)); + DownloadPrefs::FromBrowserContext(profile())->SetLastCompleteTime( + current_time - base::Minutes(59)); DownloadDisplayController controller(&display(), browser(), &bubble_controller());
diff --git a/chrome/browser/download/download_core_service_factory.cc b/chrome/browser/download/download_core_service_factory.cc index eb4f33f7..1d3ea33 100644 --- a/chrome/browser/download/download_core_service_factory.cc +++ b/chrome/browser/download/download_core_service_factory.cc
@@ -10,6 +10,10 @@ #include "chrome/browser/offline_items_collection/offline_content_aggregator_factory.h" #include "chrome/browser/profiles/profile.h" +#if !BUILDFLAG(IS_ANDROID) +#include "chrome/browser/download/bubble/download_bubble_update_service_factory.h" +#endif // !BUILDFLAG(IS_ANDROID) + // static DownloadCoreService* DownloadCoreServiceFactory::GetForBrowserContext( content::BrowserContext* context) { @@ -26,6 +30,9 @@ : ProfileKeyedServiceFactory( "DownloadCoreService", ProfileSelections::BuildForRegularAndIncognito()) { +#if !BUILDFLAG(IS_ANDROID) + DependsOn(DownloadBubbleUpdateServiceFactory::GetInstance()); +#endif // !BUILDFLAG(IS_ANDROID) DependsOn(HistoryServiceFactory::GetInstance()); DependsOn(NotificationDisplayServiceFactory::GetInstance()); DependsOn(OfflineContentAggregatorFactory::GetInstance());
diff --git a/chrome/browser/download/download_ui_controller.cc b/chrome/browser/download/download_ui_controller.cc index c21f771..8ea555d 100644 --- a/chrome/browser/download/download_ui_controller.cc +++ b/chrome/browser/download/download_ui_controller.cc
@@ -32,6 +32,7 @@ #else #include "chrome/browser/download/bubble/download_bubble_prefs.h" #include "chrome/browser/download/bubble/download_bubble_ui_controller.h" +#include "chrome/browser/download/bubble/download_bubble_update_service_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_list.h" @@ -68,6 +69,16 @@ #else // BUILDFLAG(IS_ANDROID) +void InitializeDownloadBubbleUpdateService(Profile* profile, + content::DownloadManager* manager) { + DownloadBubbleUpdateService* download_bubble_update_service = + DownloadBubbleUpdateServiceFactory::GetForProfile(profile); + if (!download_bubble_update_service) { + return; + } + download_bubble_update_service->Initialize(manager); +} + class DownloadShelfUIControllerDelegate : public DownloadUIController::Delegate { public: @@ -191,24 +202,24 @@ delegate_ = std::make_unique<AndroidUIControllerDelegate>(); #elif BUILDFLAG(IS_CHROMEOS) if (!delegate_) { - if (download::IsDownloadBubbleEnabled( - Profile::FromBrowserContext(manager->GetBrowserContext()))) { - delegate_ = std::make_unique<DownloadBubbleUIControllerDelegate>( - Profile::FromBrowserContext(manager->GetBrowserContext())); + Profile* profile = + Profile::FromBrowserContext(manager->GetBrowserContext()); + if (download::IsDownloadBubbleEnabled(profile)) { + delegate_ = std::make_unique<DownloadBubbleUIControllerDelegate>(profile); + InitializeDownloadBubbleUpdateService(profile, manager); } else { - delegate_ = std::make_unique<DownloadNotificationManager>( - Profile::FromBrowserContext(manager->GetBrowserContext())); + delegate_ = std::make_unique<DownloadNotificationManager>(profile); } } #else // BUILDFLAG(IS_CHROMEOS) if (!delegate_) { - if (download::IsDownloadBubbleEnabled( - Profile::FromBrowserContext(manager->GetBrowserContext()))) { - delegate_ = std::make_unique<DownloadBubbleUIControllerDelegate>( - Profile::FromBrowserContext(manager->GetBrowserContext())); + Profile* profile = + Profile::FromBrowserContext(manager->GetBrowserContext()); + if (download::IsDownloadBubbleEnabled(profile)) { + delegate_ = std::make_unique<DownloadBubbleUIControllerDelegate>(profile); + InitializeDownloadBubbleUpdateService(profile, manager); } else { - delegate_ = std::make_unique<DownloadShelfUIControllerDelegate>( - Profile::FromBrowserContext(manager->GetBrowserContext())); + delegate_ = std::make_unique<DownloadShelfUIControllerDelegate>(profile); } } #endif // BUILDFLAG(IS_ANDROID)
diff --git a/chrome/browser/enterprise/connectors/device_trust/signals/decorators/ash/ash_signals_decorator_browsertest.cc b/chrome/browser/enterprise/connectors/device_trust/signals/decorators/ash/ash_signals_decorator_browsertest.cc index 2430822..99d674d 100644 --- a/chrome/browser/enterprise/connectors/device_trust/signals/decorators/ash/ash_signals_decorator_browsertest.cc +++ b/chrome/browser/enterprise/connectors/device_trust/signals/decorators/ash/ash_signals_decorator_browsertest.cc
@@ -185,7 +185,7 @@ ash::ProfileHelper::Get()->GetUserByProfile(profile); AshSignalsDecorator decorator(connector_, profile); - std::set<std::string> user_affiliation_ids; + base::flat_set<std::string> user_affiliation_ids; user_affiliation_ids.insert(kFakeAffilationID); ash::ChromeUserManager::Get()->SetUserAffiliation(user->GetAccountId(),
diff --git a/chrome/browser/extensions/api/passwords_private/passwords_private_event_router.h b/chrome/browser/extensions/api/passwords_private/passwords_private_event_router.h index 5548dfc..a36a6f4 100644 --- a/chrome/browser/extensions/api/passwords_private/passwords_private_event_router.h +++ b/chrome/browser/extensions/api/passwords_private/passwords_private_event_router.h
@@ -45,12 +45,6 @@ void OnPasswordExceptionsListChanged( const std::vector<api::passwords_private::ExceptionEntry>& exceptions); - // Notifies listeners after fetching a plain-text password. - // |id| the id for the password entry being shown. - // |plaintext_password| The human-readable password. - void OnPlaintextPasswordFetched(int id, - const std::string& plaintext_password); - // Notifies listeners after the passwords have been written to the export // destination. // |file_path| In case of successful export, this will describe the path
diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc index 16e14d6..55aeb88 100644 --- a/chrome/browser/extensions/api/settings_private/prefs_util.cc +++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -48,6 +48,7 @@ #include "components/safe_browsing/core/common/safe_browsing_prefs.h" #include "components/search_engines/default_search_manager.h" #include "components/services/screen_ai/buildflags/buildflags.h" +#include "components/signin/public/base/signin_pref_names.h" #include "components/spellcheck/browser/pref_names.h" #include "components/supervised_user/core/common/pref_names.h" #include "components/translate/core/browser/translate_pref_names.h"
diff --git a/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckCoordinator.java b/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckCoordinator.java index bb89a476..42c3c1f 100644 --- a/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckCoordinator.java +++ b/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckCoordinator.java
@@ -10,8 +10,6 @@ import androidx.annotation.VisibleForTesting; import androidx.lifecycle.LifecycleObserver; -import org.chromium.chrome.browser.device_reauth.DeviceAuthRequester; -import org.chromium.chrome.browser.device_reauth.ReauthenticatorBridge; import org.chromium.chrome.browser.feedback.HelpAndFeedbackLauncher; import org.chromium.chrome.browser.password_check.helper.PasswordCheckChangePasswordHelper; import org.chromium.chrome.browser.password_check.helper.PasswordCheckIconHelper; @@ -31,7 +29,6 @@ private final PasswordCheckFragmentView mFragmentView; private final SettingsLauncher mSettingsLauncher; private final PasswordAccessReauthenticationHelper mReauthenticationHelper; - private final ReauthenticatorBridge mReauthenticatorBridge; private final PasswordCheckMediator mMediator; private PropertyModel mModel; @@ -81,9 +78,6 @@ mReauthenticationHelper = new PasswordAccessReauthenticationHelper( mFragmentView.getActivity(), mFragmentView.getParentFragmentManager()); - // TODO(crbug.com/1434876): Clean up the ReauthenticatorBridge usage. - mReauthenticatorBridge = - ReauthenticatorBridge.create(DeviceAuthRequester.PASSWORD_CHECK_AUTO_PWD_CHANGE); PasswordCheckChangePasswordHelper changePasswordHelper = new PasswordCheckChangePasswordHelper(mFragmentView.getActivity(), @@ -91,8 +85,8 @@ PasswordCheckIconHelper iconHelper = new PasswordCheckIconHelper( new LargeIconBridge(Profile.getLastUsedRegularProfile()), mFragmentView.getResources().getDimensionPixelSize(R.dimen.default_favicon_size)); - mMediator = new PasswordCheckMediator(changePasswordHelper, mReauthenticationHelper, - mReauthenticatorBridge, mSettingsLauncher, iconHelper); + mMediator = new PasswordCheckMediator( + changePasswordHelper, mReauthenticationHelper, mSettingsLauncher, iconHelper); } private void launchCheckupInAccount() {
diff --git a/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckMediator.java b/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckMediator.java index 901536c8c..e9461e5 100644 --- a/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckMediator.java +++ b/chrome/browser/password_check/android/internal/java/src/org/chromium/chrome/browser/password_check/PasswordCheckMediator.java
@@ -31,7 +31,6 @@ import org.chromium.base.task.PostTask; import org.chromium.base.task.TaskTraits; -import org.chromium.chrome.browser.device_reauth.ReauthenticatorBridge; import org.chromium.chrome.browser.password_check.helper.PasswordCheckChangePasswordHelper; import org.chromium.chrome.browser.password_check.helper.PasswordCheckIconHelper; import org.chromium.chrome.browser.password_manager.PasswordCheckReferrer; @@ -56,7 +55,6 @@ private static long sStatusUpdateDelayMillis = 1000; private final PasswordAccessReauthenticationHelper mReauthenticationHelper; - private final ReauthenticatorBridge mReauthenticatorBridge; private final PasswordCheckChangePasswordHelper mChangePasswordDelegate; private PropertyModel mModel; private PasswordCheckComponentUi.Delegate mDelegate; @@ -69,13 +67,11 @@ PasswordCheckMediator(PasswordCheckChangePasswordHelper changePasswordDelegate, PasswordAccessReauthenticationHelper reauthenticationHelper, - ReauthenticatorBridge reauthenticatorBridge, SettingsLauncher settingsLauncher, - PasswordCheckIconHelper passwordCheckIconHelper) { + SettingsLauncher settingsLauncher, PasswordCheckIconHelper passwordCheckIconHelper) { mChangePasswordDelegate = changePasswordDelegate; mReauthenticationHelper = reauthenticationHelper; mSettingsLauncher = settingsLauncher; mIconHelper = passwordCheckIconHelper; - mReauthenticatorBridge = reauthenticatorBridge; } void initialize(PropertyModel model, PasswordCheckComponentUi.Delegate delegate,
diff --git a/chrome/browser/password_check/android/junit/src/org/chromium/chrome/browser/password_check/PasswordCheckControllerTest.java b/chrome/browser/password_check/android/junit/src/org/chromium/chrome/browser/password_check/PasswordCheckControllerTest.java index 48b4c38..10beffd3 100644 --- a/chrome/browser/password_check/android/junit/src/org/chromium/chrome/browser/password_check/PasswordCheckControllerTest.java +++ b/chrome/browser/password_check/android/junit/src/org/chromium/chrome/browser/password_check/PasswordCheckControllerTest.java
@@ -57,7 +57,6 @@ import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.metrics.UmaRecorderHolder; import org.chromium.base.test.BaseRobolectricTestRunner; -import org.chromium.chrome.browser.device_reauth.ReauthenticatorBridge; import org.chromium.chrome.browser.password_check.PasswordCheckProperties.ItemType; import org.chromium.chrome.browser.password_check.helper.PasswordCheckChangePasswordHelper; import org.chromium.chrome.browser.password_check.helper.PasswordCheckIconHelper; @@ -110,8 +109,6 @@ @Mock private PasswordAccessReauthenticationHelper mReauthenticationHelper; @Mock - private ReauthenticatorBridge mReauthenticatorBridge; - @Mock private SettingsLauncher mSettingsLauncher; @Mock private PasswordCheckIconHelper mIconHelper; @@ -127,8 +124,8 @@ UmaRecorderHolder.resetForTesting(); MockitoAnnotations.initMocks(this); mModel = PasswordCheckProperties.createDefaultModel(); - mMediator = new PasswordCheckMediator(mChangePasswordDelegate, mReauthenticationHelper, - mReauthenticatorBridge, mSettingsLauncher, mIconHelper); + mMediator = new PasswordCheckMediator( + mChangePasswordDelegate, mReauthenticationHelper, mSettingsLauncher, mIconHelper); PasswordCheckFactory.setPasswordCheckForTesting(mPasswordCheck); mMediator.initialize(mModel, mDelegate, PasswordCheckReferrer.PASSWORD_SETTINGS, () -> {}); PasswordCheckMediator.setStatusUpdateDelayMillis(0);
diff --git a/chrome/browser/password_manager/android/BUILD.gn b/chrome/browser/password_manager/android/BUILD.gn index fdded86c..4a311f2 100644 --- a/chrome/browser/password_manager/android/BUILD.gn +++ b/chrome/browser/password_manager/android/BUILD.gn
@@ -435,6 +435,7 @@ "password_generation_controller_impl_unittest.cc", "password_manager_error_message_delegate_unittest.cc", "password_manager_settings_service_android_impl_unittest.cc", + "password_manager_ui_util_android_unittest.cc", "password_settings_updater_android_bridge_helper_impl_unittest.cc", "password_store_android_backend_bridge_helper_impl_unittest.cc", "password_store_android_backend_unittest.cc",
diff --git a/chrome/browser/password_manager/android/password_accessory_controller_impl.cc b/chrome/browser/password_manager/android/password_accessory_controller_impl.cc index f42a723..5420773 100644 --- a/chrome/browser/password_manager/android/password_accessory_controller_impl.cc +++ b/chrome/browser/password_manager/android/password_accessory_controller_impl.cc
@@ -278,29 +278,6 @@ password_client, std::move(driver_supplier)))); } -// static -bool PasswordAccessoryControllerImpl::ShouldAcceptFocusEvent( - content::WebContents* web_contents, - password_manager::ContentPasswordManagerDriver* driver, - FocusedFieldType focused_field_type) { - // Only react to focus events that are sent for the current focused frame. - // This is used to make sure that obsolette events that come in an unexpected - // order are not processed. Example: (Frame1, focus) -> (Frame2, focus) -> - // (Frame1, unfocus) would otherwise unset all the data set for Frame2, which - // would be wrong. - if (web_contents->GetFocusedFrame() && - driver->render_frame_host() == web_contents->GetFocusedFrame()) - return true; - - // The one event that is accepted even if there is no focused frame is an - // "unfocus" event that resulted in all frames being unfocused. This can be - // used to reset the state of the accessory. - if (!web_contents->GetFocusedFrame() && - focused_field_type == FocusedFieldType::kUnknown) - return true; - return false; -} - void PasswordAccessoryControllerImpl::OnOptionSelected( autofill::AccessoryAction selected_action) { if (selected_action == autofill::AccessoryAction::USE_OTHER_PASSWORD) {
diff --git a/chrome/browser/password_manager/android/password_accessory_controller_impl.h b/chrome/browser/password_manager/android/password_accessory_controller_impl.h index 61919dc..57c0175 100644 --- a/chrome/browser/password_manager/android/password_accessory_controller_impl.h +++ b/chrome/browser/password_manager/android/password_accessory_controller_impl.h
@@ -27,10 +27,6 @@ #include "content/public/browser/web_contents_user_data.h" #include "url/gurl.h" -namespace password_manager { -class ContentPasswordManagerDriver; -} // namespace password_manager - class ManualFillingController; class AllPasswordsBottomSheetController; @@ -87,16 +83,6 @@ password_manager::PasswordManagerClient* password_client, PasswordDriverSupplierForFocusedFrame driver_supplier); - // True if the focus event was sent for the current focused frame or if it is - // a blur event and no frame is focused. This check avoids reacting to - // obsolete events that arrived in an unexpected order. - // TODO(crbug.com/968162): Introduce the concept of active frame to the - // accessory controller and move this check in the controller. - static bool ShouldAcceptFocusEvent( - content::WebContents* web_contents, - password_manager::ContentPasswordManagerDriver* driver, - autofill::mojom::FocusedFieldType focused_field_type); - // Returns true if the current site attached to `web_contents_` has a SECURE // security level. bool IsSecureSite() const;
diff --git a/chrome/browser/password_manager/android/password_generation_controller.h b/chrome/browser/password_manager/android/password_generation_controller.h index eed681f8..7c3eb22 100644 --- a/chrome/browser/password_manager/android/password_generation_controller.h +++ b/chrome/browser/password_manager/android/password_generation_controller.h
@@ -67,7 +67,8 @@ // Notifies the UI that automatic password generation is available. // A button should be displayed in the accessory bar. virtual void OnAutomaticGenerationAvailable( - const password_manager::PasswordManagerDriver* target_frame_driver, + base::WeakPtr<password_manager::PasswordManagerDriver> + target_frame_driver, const autofill::password_generation::PasswordGenerationUIData& ui_data, gfx::RectF element_bounds_in_screen_space) = 0;
diff --git a/chrome/browser/password_manager/android/password_generation_controller_impl.cc b/chrome/browser/password_manager/android/password_generation_controller_impl.cc index 41ba5feac..14c5ce1 100644 --- a/chrome/browser/password_manager/android/password_generation_controller_impl.cc +++ b/chrome/browser/password_manager/android/password_generation_controller_impl.cc
@@ -80,13 +80,17 @@ } void PasswordGenerationControllerImpl::OnAutomaticGenerationAvailable( - const password_manager::PasswordManagerDriver* target_frame_driver, + base::WeakPtr<password_manager::PasswordManagerDriver> target_frame_driver, const autofill::password_generation::PasswordGenerationUIData& ui_data, gfx::RectF element_bounds_in_screen_space) { - if (!IsActiveFrameDriver(target_frame_driver)) - return; - DCHECK(!dialog_view_); + // We can't be sure that the active frame driver would be set in the + // FocusedInputChanged by now, because there is a race condition. The roots + // of the OnAutomaticGenerationAvailable and FocusedInputChanged calls are + // the same in the renderer. So we need to set it here too. + FocusedInputChanged(autofill::mojom::FocusedFieldType::kFillablePasswordField, + std::move(target_frame_driver)); + CHECK(!dialog_view_); active_frame_driver_->GetPasswordManager() ->SetGenerationElementAndTypeForForm( active_frame_driver_.get(), ui_data.form_data.unique_renderer_id, @@ -125,6 +129,11 @@ base::WeakPtr<password_manager::PasswordManagerDriver> driver) { TRACE_EVENT0("passwords", "PasswordGenerationControllerImpl::FocusedInputChanged"); + // It's probably a duplicate notification. + if (IsActiveFrameDriver(driver.get()) && + focused_field_type == FocusedFieldType::kFillablePasswordField) { + return; + } ResetState(); if (focused_field_type == FocusedFieldType::kFillablePasswordField) active_frame_driver_ = std::move(driver);
diff --git a/chrome/browser/password_manager/android/password_generation_controller_impl.h b/chrome/browser/password_manager/android/password_generation_controller_impl.h index 6a7e60ab..4ff1426 100644 --- a/chrome/browser/password_manager/android/password_generation_controller_impl.h +++ b/chrome/browser/password_manager/android/password_generation_controller_impl.h
@@ -49,7 +49,8 @@ autofill::mojom::FocusedFieldType focused_field_type, base::WeakPtr<password_manager::PasswordManagerDriver> driver) override; void OnAutomaticGenerationAvailable( - const password_manager::PasswordManagerDriver* target_frame_driver, + base::WeakPtr<password_manager::PasswordManagerDriver> + target_frame_driver, const autofill::password_generation::PasswordGenerationUIData& ui_data, gfx::RectF element_bounds_in_screen_space) override; void ShowManualGenerationDialog(
diff --git a/chrome/browser/password_manager/android/password_generation_controller_impl_unittest.cc b/chrome/browser/password_manager/android/password_generation_controller_impl_unittest.cc index 0fbaf35..eab63d56 100644 --- a/chrome/browser/password_manager/android/password_generation_controller_impl_unittest.cc +++ b/chrome/browser/password_manager/android/password_generation_controller_impl_unittest.cc
@@ -40,6 +40,7 @@ using password_manager::MockPasswordStoreInterface; using password_manager::PasswordForm; using testing::_; +using testing::AtMost; using testing::ByMove; using testing::Eq; using testing::Mock; @@ -171,6 +172,8 @@ test_pwd_manager_client_.get()); mock_password_manager_driver_ = std::make_unique<NiceMock<MockPasswordManagerDriver>>(); + mock_another_password_manager_driver_ = + std::make_unique<NiceMock<MockPasswordManagerDriver>>(); // TODO(crbug.com/969051): Remove once kAutofillKeyboardAccessory is // enabled. @@ -183,6 +186,11 @@ .WillByDefault(Return(password_manager_.get())); ON_CALL(*mock_password_manager_driver_, GetPasswordAutofillManager()) .WillByDefault(Return(password_autofill_manager_.get())); + ON_CALL(*mock_another_password_manager_driver_, GetPasswordManager()) + .WillByDefault(Return(password_manager_.get())); + ON_CALL(*mock_another_password_manager_driver_, + GetPasswordAutofillManager()) + .WillByDefault(Return(password_autofill_manager_.get())); mock_generation_helper_ = std::make_unique<NiceMock<MockPasswordGenerationHelper>>( @@ -219,6 +227,8 @@ std::unique_ptr<NiceMock<MockPasswordManagerDriver>> mock_password_manager_driver_; + std::unique_ptr<NiceMock<MockPasswordManagerDriver>> + mock_another_password_manager_driver_; std::unique_ptr<NiceMock<MockPasswordGenerationHelper>> mock_generation_helper_; std::unique_ptr<NiceMock<MockPasswordGenerationDialogView>> mock_dialog_; @@ -243,7 +253,7 @@ OnAutomaticGenerationStatusChanged(true)); controller()->OnAutomaticGenerationAvailable( - mock_password_manager_driver_.get(), GetTestGenerationUIData1(), + mock_password_manager_driver_->AsWeakPtr(), GetTestGenerationUIData1(), gfx::RectF(100, 20)); ON_CALL(*mock_generation_helper_, GeneratePassword(_, _, _, _)) @@ -272,7 +282,7 @@ EXPECT_CALL(mock_manual_filling_controller_, OnAutomaticGenerationStatusChanged(true)); controller()->OnAutomaticGenerationAvailable( - mock_password_manager_driver_.get(), GetTestGenerationUIData1(), + mock_password_manager_driver_->AsWeakPtr(), GetTestGenerationUIData1(), gfx::RectF(100, 20)); } @@ -286,11 +296,12 @@ OnAutomaticGenerationStatusChanged(true)) .Times(2); controller()->OnAutomaticGenerationAvailable( - mock_password_manager_driver_.get(), GetTestGenerationUIData1(), + mock_password_manager_driver_->AsWeakPtr(), GetTestGenerationUIData1(), gfx::RectF(100, 20)); PasswordGenerationUIData new_ui_data = GetTestGenerationUIData2(); controller()->OnAutomaticGenerationAvailable( - mock_password_manager_driver_.get(), new_ui_data, gfx::RectF(100, 20)); + mock_password_manager_driver_->AsWeakPtr(), new_ui_data, + gfx::RectF(100, 20)); autofill::FormSignature form_signature = autofill::CalculateFormSignature(new_ui_data.form_data); @@ -323,7 +334,7 @@ EXPECT_CALL(mock_manual_filling_controller_, OnAutomaticGenerationStatusChanged(true)); controller()->OnAutomaticGenerationAvailable( - mock_password_manager_driver_.get(), GetTestGenerationUIData1(), + mock_password_manager_driver_->AsWeakPtr(), GetTestGenerationUIData1(), gfx::RectF(100, 20)); EXPECT_CALL(mock_manual_filling_controller_, @@ -387,13 +398,18 @@ } TEST_F(PasswordGenerationControllerTest, - RejectAutomaticAvailableForNonActiveFrame) { + SetActiveFrameOnAutomaticGenerationAvailable) { + // TODO(crbug.com/1421753): Refactor PasswordGenerationController so that + // OnAutomaticGenerationStatusChanged would be called only once. Right now + // it's called twice: the first call resets the manual filling controller + // status and the second one sets it according to the focused input. EXPECT_CALL(mock_manual_filling_controller_, OnAutomaticGenerationStatusChanged(_)) - .Times(0); - MockPasswordManagerDriver wrong_driver; + .Times(AtMost(2)); + controller()->OnAutomaticGenerationAvailable( - &wrong_driver, GetTestGenerationUIData2(), gfx::RectF(100, 20)); + mock_another_password_manager_driver_->AsWeakPtr(), + GetTestGenerationUIData2(), gfx::RectF(100, 20)); } TEST_F(PasswordGenerationControllerTest, @@ -438,7 +454,7 @@ EXPECT_CALL(*raw_dialog_view, Destroy()); controller()->FocusedInputChanged( FocusedFieldType::kFillableUsernameField, - base::AsWeakPtr(mock_password_manager_driver_.get())); + mock_another_password_manager_driver_->AsWeakPtr()); Mock::VerifyAndClearExpectations(raw_dialog_view); } @@ -497,7 +513,7 @@ OnAutomaticGenerationStatusChanged(false)); controller()->FocusedInputChanged( FocusedFieldType::kFillablePasswordField, - base::AsWeakPtr(mock_password_manager_driver_.get())); + mock_another_password_manager_driver_->AsWeakPtr()); EXPECT_CALL(mock_dialog_factory(), Run).Times(0); controller()->ShowManualGenerationDialog(mock_password_manager_driver_.get(), GetTestGenerationUIData1());
diff --git a/chrome/browser/password_manager/android/password_manager_ui_util_android.cc b/chrome/browser/password_manager/android/password_manager_ui_util_android.cc new file mode 100644 index 0000000..6dfc20f --- /dev/null +++ b/chrome/browser/password_manager/android/password_manager_ui_util_android.cc
@@ -0,0 +1,33 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/password_manager/android/password_manager_ui_util_android.h" + +#include "components/password_manager/content/browser/content_password_manager_driver.h" + +using autofill::mojom::FocusedFieldType; + +bool ShouldAcceptFocusEvent( + content::WebContents* web_contents, + password_manager::ContentPasswordManagerDriver* driver, + FocusedFieldType focused_field_type) { + // Only react to focus events that are sent for the current focused frame. + // This is used to make sure that obsolete events that come in an unexpected + // order are not processed. Example: (Frame1, focus) -> (Frame2, focus) -> + // (Frame1, unfocus) would otherwise unset all the data set for Frame2, which + // would be wrong. + if (web_contents->GetFocusedFrame() && + driver->render_frame_host() == web_contents->GetFocusedFrame()) { + return true; + } + + // The only event that is accepted even if there is no focused frame is an + // "unfocus" event that resulted in all frames being unfocused. This can be + // used to reset the focused state. + if (!web_contents->GetFocusedFrame() && + focused_field_type == FocusedFieldType::kUnknown) { + return true; + } + return false; +}
diff --git a/chrome/browser/password_manager/android/password_manager_ui_util_android.h b/chrome/browser/password_manager/android/password_manager_ui_util_android.h new file mode 100644 index 0000000..97eed7c2 --- /dev/null +++ b/chrome/browser/password_manager/android/password_manager_ui_util_android.h
@@ -0,0 +1,19 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_PASSWORD_MANAGER_ANDROID_PASSWORD_MANAGER_UI_UTIL_ANDROID_H_ +#define CHROME_BROWSER_PASSWORD_MANAGER_ANDROID_PASSWORD_MANAGER_UI_UTIL_ANDROID_H_ + +#include "components/password_manager/content/browser/content_password_manager_driver.h" +#include "content/public/browser/web_contents.h" + +// True if the focus event was sent for the current focused frame or if it is +// a blur event and no frame is focused. This check avoids reacting to +// obsolete events that arrived in an unexpected order. +bool ShouldAcceptFocusEvent( + content::WebContents* web_contents, + password_manager::ContentPasswordManagerDriver* driver, + autofill::mojom::FocusedFieldType focused_field_type); + +#endif // CHROME_BROWSER_PASSWORD_MANAGER_ANDROID_PASSWORD_MANAGER_UI_UTIL_ANDROID_H_
diff --git a/chrome/browser/password_manager/android/password_manager_ui_util_android_unittest.cc b/chrome/browser/password_manager/android/password_manager_ui_util_android_unittest.cc new file mode 100644 index 0000000..8fc1df37 --- /dev/null +++ b/chrome/browser/password_manager/android/password_manager_ui_util_android_unittest.cc
@@ -0,0 +1,75 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/password_manager/android/password_manager_ui_util_android.h" + +#include <memory> + +#include "chrome/test/base/chrome_render_view_host_test_harness.h" +#include "components/autofill/core/browser/test_autofill_client.h" +#include "components/password_manager/content/browser/content_password_manager_driver.h" +#include "components/password_manager/core/browser/stub_password_manager_client.h" +#include "content/public/browser/render_frame_host.h" +#include "testing/gtest/include/gtest/gtest.h" + +using autofill::mojom::FocusedFieldType; + +constexpr char kExampleSite[] = "https://example.com"; + +class PasswordManagerUIUtilAndroidTest + : public ChromeRenderViewHostTestHarness { + public: + void SetUp() override { + ChromeRenderViewHostTestHarness::SetUp(); + NavigateAndCommit(GURL(kExampleSite)); + FocusWebContentsOnMainFrame(); + + ASSERT_TRUE(web_contents()->GetFocusedFrame()); + + password_mananger_driver_ = CreatePasswordManagerDriver(main_rfh()); + } + + password_manager::ContentPasswordManagerDriver* password_mananger_driver() { + return password_mananger_driver_.get(); + } + + std::unique_ptr<password_manager::ContentPasswordManagerDriver> + CreatePasswordManagerDriver(content::RenderFrameHost* rfh) { + return std::make_unique<password_manager::ContentPasswordManagerDriver>( + rfh, &client_, &test_autofill_client_); + } + + private: + std::unique_ptr<password_manager::ContentPasswordManagerDriver> + password_mananger_driver_; + password_manager::StubPasswordManagerClient client_; + autofill::TestAutofillClient test_autofill_client_; +}; + +TEST_F(PasswordManagerUIUtilAndroidTest, ShouldAcceptFocusEvent) { + EXPECT_TRUE(ShouldAcceptFocusEvent(web_contents(), password_mananger_driver(), + FocusedFieldType::kFillablePasswordField)); +} + +TEST_F(PasswordManagerUIUtilAndroidTest, + ShouldNotAcceptFocusEventAfterFrameLostFocus) { + // Make a driver for another frame than the focused one. + content::RenderFrameHost* subframe = + content::RenderFrameHostTester::For(main_rfh())->AppendChild("subframe"); + std::unique_ptr<password_manager::ContentPasswordManagerDriver> bad_driver = + CreatePasswordManagerDriver(subframe); + + EXPECT_FALSE( + ShouldAcceptFocusEvent(web_contents(), bad_driver.get(), + FocusedFieldType::kFillablePasswordField)); +} + +TEST_F(PasswordManagerUIUtilAndroidTest, + ShouldStillAcceptFocusEventIfNoFocusedFrame) { + // Reset contents, so that no frame was focused. + SetContents(CreateTestWebContents()); + + EXPECT_TRUE(ShouldAcceptFocusEvent(web_contents(), password_mananger_driver(), + FocusedFieldType::kUnknown)); +}
diff --git a/chrome/browser/password_manager/chrome_password_manager_client.cc b/chrome/browser/password_manager/chrome_password_manager_client.cc index e746535b..2d5befc 100644 --- a/chrome/browser/password_manager/chrome_password_manager_client.cc +++ b/chrome/browser/password_manager/chrome_password_manager_client.cc
@@ -134,6 +134,7 @@ #include "chrome/browser/password_manager/android/password_accessory_controller_impl.h" #include "chrome/browser/password_manager/android/password_generation_controller.h" #include "chrome/browser/password_manager/android/password_manager_launcher_android.h" +#include "chrome/browser/password_manager/android/password_manager_ui_util_android.h" #include "chrome/browser/touch_to_fill/touch_to_fill_controller.h" #include "chrome/browser/touch_to_fill/touch_to_fill_controller_autofill_delegate.h" #include "components/messages/android/messages_feature.h" @@ -340,8 +341,8 @@ ->NotifyFocusedInputChanged(focused_field_id, focused_field_type); password_manager::ContentPasswordManagerDriver* content_driver = static_cast<password_manager::ContentPasswordManagerDriver*>(driver); - if (!PasswordAccessoryControllerImpl::ShouldAcceptFocusEvent( - web_contents(), content_driver, focused_field_type)) { + if (!ShouldAcceptFocusEvent(web_contents(), content_driver, + focused_field_type)) { return; } @@ -995,29 +996,32 @@ if (!password_manager::bad_message::CheckChildProcessSecurityPolicyForURL( rfh, ui_data.form_data.url, BadMessageReason:: - CPMD_BAD_ORIGIN_AUTOMATIC_GENERATION_STATUS_CHANGED)) + CPMD_BAD_ORIGIN_AUTOMATIC_GENERATION_STATUS_CHANGED)) { return; + } #if BUILDFLAG(IS_ANDROID) - PasswordManagerDriver* driver = driver_factory_->GetDriverForFrame(rfh); + password_manager::ContentPasswordManagerDriver* driver = + driver_factory_->GetDriverForFrame(rfh); // This method is called over Mojo via a RenderFrameHostReceiverSet; the // current target frame must be live. // TODO(crbug.com/1294378): Remove reference to nested frames once // EnablePasswordManagerWithinFencedFrame is launched. - DCHECK(driver || rfh->IsNestedWithinFencedFrame()); - if (!driver) { + CHECK(driver || rfh->IsNestedWithinFencedFrame()); + if (!driver || + !ShouldAcceptFocusEvent(web_contents(), driver, + FocusedFieldType::kFillablePasswordField)) { return; } PasswordGenerationController* generation_controller = - PasswordGenerationController::GetIfExisting(web_contents()); - DCHECK(generation_controller); + PasswordGenerationController::GetOrCreate(web_contents()); gfx::RectF element_bounds_in_screen_space = TransformToRootCoordinates( password_generation_driver_receivers_.GetCurrentTargetFrame(), ui_data.bounds); generation_controller->OnAutomaticGenerationAvailable( - driver, ui_data, element_bounds_in_screen_space); + driver->AsWeakPtr(), ui_data, element_bounds_in_screen_space); #else password_manager::ContentPasswordManagerDriver* driver = driver_factory_->GetDriverForFrame(rfh); @@ -1025,7 +1029,7 @@ // current target frame must be live. // TODO(crbug.com/1294378): Remove reference to nested frames once // EnablePasswordManagerWithinFencedFrame is launched. - DCHECK(driver || rfh->IsNestedWithinFencedFrame()); + CHECK(driver || rfh->IsNestedWithinFencedFrame()); if (!driver) return;
diff --git a/chrome/browser/privacy_guide/android/BUILD.gn b/chrome/browser/privacy_guide/android/BUILD.gn index dec43515..fd859ff 100644 --- a/chrome/browser/privacy_guide/android/BUILD.gn +++ b/chrome/browser/privacy_guide/android/BUILD.gn
@@ -125,6 +125,7 @@ "//third_party/android_deps:espresso_java", "//third_party/androidx:androidx_test_runner_java", "//third_party/hamcrest:hamcrest_core_java", + "//third_party/hamcrest:hamcrest_library_java", "//third_party/junit:junit", "//third_party/mockito:mockito_java", "//ui/android:ui_java_test_support", @@ -148,6 +149,7 @@ "java/res/layout/privacy_guide_explanation_item.xml", "java/res/layout/privacy_guide_history_sync_step.xml", "java/res/layout/privacy_guide_msbb_step.xml", + "java/res/layout/privacy_guide_sb_bottom_sheet_toolbar.xml", "java/res/layout/privacy_guide_sb_enhanced_explanation.xml", "java/res/layout/privacy_guide_sb_standard_explanation.xml", "java/res/layout/privacy_guide_sb_step.xml",
diff --git a/chrome/browser/privacy_guide/android/java/res/layout/privacy_guide_sb_bottom_sheet_toolbar.xml b/chrome/browser/privacy_guide/android/java/res/layout/privacy_guide_sb_bottom_sheet_toolbar.xml new file mode 100644 index 0000000..da8eddb8 --- /dev/null +++ b/chrome/browser/privacy_guide/android/java/res/layout/privacy_guide_sb_bottom_sheet_toolbar.xml
@@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright 2023 The Chromium Authors +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. +--> +<org.chromium.ui.widget.OptimizedFrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:paddingVertical="8dp" + android:importantForAccessibility="no" + android:src="@drawable/drag_handlebar" /> +</org.chromium.ui.widget.OptimizedFrameLayout> \ No newline at end of file
diff --git a/chrome/browser/privacy_guide/android/java/res/layout/privacy_guide_sb_enhanced_explanation.xml b/chrome/browser/privacy_guide/android/java/res/layout/privacy_guide_sb_enhanced_explanation.xml index 94e9130..4b598d5 100644 --- a/chrome/browser/privacy_guide/android/java/res/layout/privacy_guide_sb_enhanced_explanation.xml +++ b/chrome/browser/privacy_guide/android/java/res/layout/privacy_guide_sb_enhanced_explanation.xml
@@ -7,7 +7,8 @@ <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" - android:layout_height="wrap_content"> + android:layout_height="wrap_content" + android:paddingTop="@dimen/sb_bottom_sheet_toolbar_height"> <LinearLayout android:id="@+id/sb_enhanced_sheet" @@ -81,7 +82,7 @@ <!-- Extra empty space, necessary due to crbug.com/1287979. --> <View android:layout_width="match_parent" - android:layout_height="16dp" + android:layout_height="32dp" android:visibility="invisible" /> </LinearLayout>
diff --git a/chrome/browser/privacy_guide/android/java/res/layout/privacy_guide_sb_standard_explanation.xml b/chrome/browser/privacy_guide/android/java/res/layout/privacy_guide_sb_standard_explanation.xml index b8eae82..6a01bd76 100644 --- a/chrome/browser/privacy_guide/android/java/res/layout/privacy_guide_sb_standard_explanation.xml +++ b/chrome/browser/privacy_guide/android/java/res/layout/privacy_guide_sb_standard_explanation.xml
@@ -7,7 +7,8 @@ <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" - android:layout_height="wrap_content"> + android:layout_height="wrap_content" + android:paddingTop="@dimen/sb_bottom_sheet_toolbar_height"> <LinearLayout android:id="@+id/sb_standard_sheet"
diff --git a/chrome/browser/privacy_guide/android/java/res/values/dimens.xml b/chrome/browser/privacy_guide/android/java/res/values/dimens.xml index e6ed6c70..11e1f83 100644 --- a/chrome/browser/privacy_guide/android/java/res/values/dimens.xml +++ b/chrome/browser/privacy_guide/android/java/res/values/dimens.xml
@@ -13,4 +13,5 @@ <dimen name="done_step_explanation_marginBottom">8dp</dimen> <dimen name="done_step_link_button_size">20dp</dimen> <dimen name="done_step_link_button_marginHorizontal">4dp</dimen> + <dimen name="sb_bottom_sheet_toolbar_height">20dp</dimen> </resources>
diff --git a/chrome/browser/privacy_guide/android/java/src/org/chromium/chrome/browser/privacy_guide/PrivacyGuideBottomSheetView.java b/chrome/browser/privacy_guide/android/java/src/org/chromium/chrome/browser/privacy_guide/PrivacyGuideBottomSheetView.java index 6a36c20..f2a8762d 100644 --- a/chrome/browser/privacy_guide/android/java/src/org/chromium/chrome/browser/privacy_guide/PrivacyGuideBottomSheetView.java +++ b/chrome/browser/privacy_guide/android/java/src/org/chromium/chrome/browser/privacy_guide/PrivacyGuideBottomSheetView.java
@@ -13,9 +13,11 @@ /** Bottom sheet view for displaying privacy guide control explanations */ public class PrivacyGuideBottomSheetView implements BottomSheetContent { private final View mContentView; + private final View mToolbarView; - PrivacyGuideBottomSheetView(View contentView) { + PrivacyGuideBottomSheetView(View contentView, View toolbarView) { mContentView = contentView; + mToolbarView = toolbarView; } @Override @@ -26,12 +28,12 @@ @Nullable @Override public View getToolbarView() { - return null; + return mToolbarView; } @Override public int getVerticalScrollOffset() { - return 0; + return mContentView.getScrollY(); } @Override
diff --git a/chrome/browser/privacy_guide/android/java/src/org/chromium/chrome/browser/privacy_guide/SafeBrowsingFragment.java b/chrome/browser/privacy_guide/android/java/src/org/chromium/chrome/browser/privacy_guide/SafeBrowsingFragment.java index c17b394a..a36d854 100644 --- a/chrome/browser/privacy_guide/android/java/src/org/chromium/chrome/browser/privacy_guide/SafeBrowsingFragment.java +++ b/chrome/browser/privacy_guide/android/java/src/org/chromium/chrome/browser/privacy_guide/SafeBrowsingFragment.java
@@ -69,10 +69,12 @@ LayoutInflater inflater = LayoutInflater.from(getView().getContext()); if (clickedButtonId == mEnhancedProtection.getId()) { displayBottomSheet( - inflater.inflate(R.layout.privacy_guide_sb_enhanced_explanation, null)); + inflater.inflate(R.layout.privacy_guide_sb_enhanced_explanation, null), + inflater.inflate(R.layout.privacy_guide_sb_bottom_sheet_toolbar, null)); } else if (clickedButtonId == mStandardProtection.getId()) { displayBottomSheet( - inflater.inflate(R.layout.privacy_guide_sb_standard_explanation, null)); + inflater.inflate(R.layout.privacy_guide_sb_standard_explanation, null), + inflater.inflate(R.layout.privacy_guide_sb_bottom_sheet_toolbar, null)); } else { assert false : "Unknown Aux clickedButtonId " + clickedButtonId; } @@ -93,8 +95,9 @@ } } - private void displayBottomSheet(View sheetContent) { - PrivacyGuideBottomSheetView bottomSheet = new PrivacyGuideBottomSheetView(sheetContent); + private void displayBottomSheet(View sheetContent, View sheetToolbar) { + PrivacyGuideBottomSheetView bottomSheet = + new PrivacyGuideBottomSheetView(sheetContent, sheetToolbar); mBottomSheetController.requestShowContent(bottomSheet, /* animate= */ true); }
diff --git a/chrome/browser/privacy_guide/android/javatests/src/org/chromium/chrome/browser/privacy_guide/PrivacyGuideFragmentTest.java b/chrome/browser/privacy_guide/android/javatests/src/org/chromium/chrome/browser/privacy_guide/PrivacyGuideFragmentTest.java index fcd53ffa..4dd4352 100644 --- a/chrome/browser/privacy_guide/android/javatests/src/org/chromium/chrome/browser/privacy_guide/PrivacyGuideFragmentTest.java +++ b/chrome/browser/privacy_guide/android/javatests/src/org/chromium/chrome/browser/privacy_guide/PrivacyGuideFragmentTest.java
@@ -10,11 +10,15 @@ import static androidx.test.espresso.intent.Intents.intended; import static androidx.test.espresso.intent.Intents.intending; import static androidx.test.espresso.intent.matcher.IntentMatchers.anyIntent; +import static androidx.test.espresso.matcher.ViewMatchers.hasSibling; import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; +import static androidx.test.espresso.matcher.ViewMatchers.withChild; import static androidx.test.espresso.matcher.ViewMatchers.withId; +import static androidx.test.espresso.matcher.ViewMatchers.withParent; import static androidx.test.espresso.matcher.ViewMatchers.withText; import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.Matchers.allOf; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -29,8 +33,7 @@ import androidx.test.espresso.intent.Intents; import androidx.test.espresso.intent.matcher.IntentMatchers; -import androidx.test.filters.MediumTest; -import androidx.test.filters.SmallTest; +import androidx.test.filters.LargeTest; import org.junit.After; import org.junit.Before; @@ -54,11 +57,10 @@ import org.chromium.chrome.browser.settings.SettingsActivityTestRule; import org.chromium.chrome.browser.signin.services.UnifiedConsentServiceBridge; import org.chromium.chrome.browser.sync.SyncService; +import org.chromium.chrome.test.ChromeBrowserTestRule; import org.chromium.chrome.test.ChromeJUnit4ClassRunner; -import org.chromium.chrome.test.ChromeTabbedActivityTestRule; import org.chromium.chrome.test.util.ChromeRenderTestRule; import org.chromium.chrome.test.util.browser.Features; -import org.chromium.chrome.test.util.browser.signin.SigninTestRule; import org.chromium.components.content_settings.CookieControlsMode; import org.chromium.components.content_settings.PrefNames; import org.chromium.components.embedder_support.util.UrlConstants; @@ -84,17 +86,13 @@ public MockitoRule mMockitoRule = MockitoJUnit.rule(); @Rule - public final ChromeTabbedActivityTestRule mActivityTestRule = - new ChromeTabbedActivityTestRule(); + public ChromeBrowserTestRule mChromeBrowserTestRule = new ChromeBrowserTestRule(); @Rule public SettingsActivityTestRule<PrivacyGuideFragment> mSettingsActivityTestRule = new SettingsActivityTestRule<>(PrivacyGuideFragment.class); @Rule - public SigninTestRule mSigninTestRule = new SigninTestRule(); - - @Rule public ChromeRenderTestRule mRenderTestRule = ChromeRenderTestRule.Builder.withPublicCorpus() .setBugComponent(ChromeRenderTestRule.Component.UI_SETTINGS_PRIVACY) @@ -107,8 +105,7 @@ @Before public void setUp() { - mActivityTestRule.startMainActivityOnBlankPage(); - mSigninTestRule.addTestAccountThenSigninAndEnableSync(); + mChromeBrowserTestRule.addTestAccountThenSigninAndEnableSync(); mActionTester = new UserActionTester(); } @@ -248,8 +245,14 @@ .getRootView(); } + private void clickOnArrowNextToRadioButtonWithText(int textId) { + onView(allOf(withId(R.id.expand_arrow), + withParent(hasSibling(withChild(withText(textId)))))) + .perform(click()); + } + @Test - @SmallTest + @LargeTest @Feature({"RenderTest"}) public void testRenderWelcomeCard() throws IOException { launchPrivacyGuide(); @@ -257,7 +260,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"RenderTest"}) public void testRenderMSBBCard() throws IOException { launchPrivacyGuide(); @@ -266,7 +269,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"RenderTest"}) public void testRenderHistorySyncCard() throws IOException { launchPrivacyGuide(); @@ -275,7 +278,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"RenderTest"}) public void testRenderSBCard() throws IOException { launchPrivacyGuide(); @@ -284,7 +287,27 @@ } @Test - @MediumTest + @LargeTest + @Feature({"RenderTest"}) + public void testRenderSBEnhancedBottomSheet() throws IOException { + launchPrivacyGuide(); + goToSafeBrowsingCard(); + clickOnArrowNextToRadioButtonWithText(R.string.privacy_guide_safe_browsing_enhanced_title); + mRenderTestRule.render(getRootView(), "privacy_guide_sb_enhanced_sheet"); + } + + @Test + @LargeTest + @Feature({"RenderTest"}) + public void testRenderSBStandardBottomSheet() throws IOException { + launchPrivacyGuide(); + goToSafeBrowsingCard(); + clickOnArrowNextToRadioButtonWithText(R.string.privacy_guide_safe_browsing_standard_title); + mRenderTestRule.render(getRootView(), "privacy_guide_sb_standard_sheet"); + } + + @Test + @LargeTest @Feature({"RenderTest"}) public void testRenderCookiesCard() throws IOException { launchPrivacyGuide(); @@ -293,7 +316,7 @@ } @Test - @MediumTest + @LargeTest @Feature({"RenderTest"}) public void testRenderCompletionCard() throws IOException { launchPrivacyGuide(); @@ -302,7 +325,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testForwardNavigation() { launchPrivacyGuide(); @@ -321,7 +344,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testWelcomeCard_nextClickWelcomeUserAction() { launchPrivacyGuide(); @@ -332,7 +355,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testWelcomeCard_nextNavigationHistogram() { launchPrivacyGuide(); @@ -346,7 +369,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testCompletionCard_nextClickCompletionUserAction() { launchPrivacyGuide(); @@ -362,7 +385,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testCompletionCard_nextNavigationHistogram() { launchPrivacyGuide(); @@ -378,7 +401,7 @@ } @Test - @MediumTest + @LargeTest @Feature({"PrivacyGuide"}) @Features.EnableFeatures(ChromeFeatureList.PRIVACY_SANDBOX_SETTINGS_3) @Features.DisableFeatures(ChromeFeatureList.PRIVACY_SANDBOX_SETTINGS_4) @@ -392,7 +415,7 @@ } @Test - @MediumTest + @LargeTest @Feature({"PrivacyGuide"}) @Features.EnableFeatures(ChromeFeatureList.PRIVACY_SANDBOX_SETTINGS_4) @Features.DisableFeatures(ChromeFeatureList.PRIVACY_SANDBOX_SETTINGS_3) @@ -405,7 +428,7 @@ } @Test - @MediumTest + @LargeTest @Feature({"PrivacyGuide"}) public void testCompletionCard_AdPrivacyClickUserAction() { launchPrivacyGuide(); @@ -416,7 +439,7 @@ } @Test - @MediumTest + @LargeTest @Feature({"PrivacyGuide"}) public void testCompletionCard_AdPrivacyClickHistogram() { launchPrivacyGuide(); @@ -431,7 +454,7 @@ } @Test - @MediumTest + @LargeTest @Feature({"PrivacyGuide"}) public void testCompletionCard_WaaLinkNavigation() { launchPrivacyGuide(); @@ -445,7 +468,7 @@ } @Test - @MediumTest + @LargeTest @Feature({"PrivacyGuide"}) public void testCompletionCard_WaaClickUserAction() { launchPrivacyGuide(); @@ -458,7 +481,7 @@ } @Test - @MediumTest + @LargeTest @Feature({"PrivacyGuide"}) public void testCompletionCard_WaaClickHistogram() { launchPrivacyGuide(); @@ -473,7 +496,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testMSBBCard_nextClickMSBBUserAction() { launchPrivacyGuide(); @@ -483,7 +506,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testMSBBCard_nextNavigationHistogram() { launchPrivacyGuide(); @@ -498,7 +521,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testMSBBCard_offToOffSettingsStatesHistogram() { setMSBBState(false); @@ -514,7 +537,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testMSBBCard_offToOnSettingsStatesHistogram() { setMSBBState(false); @@ -531,7 +554,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testMSBBCard_onToOffSettingsStatesHistogram() { setMSBBState(true); @@ -548,7 +571,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testMSBBCard_onToOnSettingsStatesHistogram() { setMSBBState(true); @@ -564,7 +587,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testMSBBCard_nextButtonInitialMSBBStateIsSet() { launchPrivacyGuide(); @@ -579,7 +602,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testMSBBCard_backButtonInitialMSBBStateIsSet() { launchPrivacyGuide(); @@ -596,7 +619,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testHistorySyncCard_nextClickHistorySyncUserAction() { launchPrivacyGuide(); @@ -608,7 +631,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testHistorySyncCard_nextNavigationHistogram() { launchPrivacyGuide(); @@ -623,7 +646,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testHistorySyncCard_offToOffSettingsStatesHistogram() { setHistorySyncState(false); @@ -639,7 +662,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testHistorySyncCard_offToOnSettingsStatesHistogram() { setHistorySyncState(false); @@ -656,7 +679,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testHistorySyncCard_onToOffSettingsStatesHistogram() { setHistorySyncState(true); @@ -673,7 +696,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testHistorySyncCard_onToOnSettingsStatesHistogram() { setHistorySyncState(true); @@ -689,7 +712,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testHistorySyncCard_nextButtonInitialSyncStateIsSet() { launchPrivacyGuide(); @@ -704,7 +727,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testHistorySyncCard_backButtonInitialSyncStateIsSet() { launchPrivacyGuide(); @@ -721,7 +744,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testSafeBrowsingCard_nextClickSafeBrowsingUserAction() { launchPrivacyGuide(); @@ -732,7 +755,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testSafeBrowsingCard_nextNavigationHistogram() { launchPrivacyGuide(); @@ -747,7 +770,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testSafeBrowsingCard_standardToStandardSettingsStatesHistogram() { setSafeBrowsingState(SafeBrowsingState.STANDARD_PROTECTION); @@ -763,7 +786,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testSafeBrowsingCard_standardToEnhancedSettingsStatesHistogram() { setSafeBrowsingState(SafeBrowsingState.STANDARD_PROTECTION); @@ -780,7 +803,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testSafeBrowsingCard_enhancedToEnhancedSettingsStatesHistogram() { setSafeBrowsingState(SafeBrowsingState.ENHANCED_PROTECTION); @@ -796,7 +819,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testSafeBrowsingCard_enhancedToStandardSettingsStatesHistogram() { setSafeBrowsingState(SafeBrowsingState.ENHANCED_PROTECTION); @@ -813,7 +836,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testSafeBrowsingCard_nextButtonInitialSafeBrowsingStateIsSet() { launchPrivacyGuide(); @@ -828,7 +851,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testSafeBrowsingCard_backButtonInitialSafeBrowsingStateIsSet() { launchPrivacyGuide(); @@ -845,7 +868,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testCookiesCard_nextClickCookiesUserAction() { launchPrivacyGuide(); @@ -855,7 +878,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testCookiesCard_nextNavigationHistogram() { launchPrivacyGuide(); @@ -870,7 +893,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testCookiesCard_block3PIncognitoTo3PIncognitoSettingsStatesHistogram() { setCookieControlsMode(CookieControlsMode.INCOGNITO_ONLY); @@ -886,7 +909,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testCookiesCard_block3PIncognitoTo3PSettingsStatesHistogram() { setCookieControlsMode(CookieControlsMode.INCOGNITO_ONLY); @@ -903,7 +926,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testCookiesCard_block3PTo3PIncognitoSettingsStatesHistogram() { setCookieControlsMode(CookieControlsMode.BLOCK_THIRD_PARTY); @@ -920,7 +943,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testCookiesCard_block3PTo3PSettingsStatesHistogram() { setCookieControlsMode(CookieControlsMode.BLOCK_THIRD_PARTY); @@ -936,7 +959,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testCookiesCard_nextButtonInitialCookiesStateIsSet() { launchPrivacyGuide(); @@ -951,7 +974,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testHistorySyncCard_backClickHistorySyncUserAction() { launchPrivacyGuide(); @@ -966,7 +989,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testSafeBrowsingCard_backClickSafeBrowsingUserAction() { launchPrivacyGuide(); @@ -981,7 +1004,7 @@ } @Test - @SmallTest + @LargeTest @Feature({"PrivacyGuide"}) public void testCookiesCard_backClickCookiesUserAction() { launchPrivacyGuide();
diff --git a/chrome/browser/profiles/profile.cc b/chrome/browser/profiles/profile.cc index a2a5795..6cffe94 100644 --- a/chrome/browser/profiles/profile.cc +++ b/chrome/browser/profiles/profile.cc
@@ -8,7 +8,6 @@ #include "base/files/file_path.h" #include "base/functional/bind.h" -#include "base/guid.h" #include "base/memory/raw_ptr.h" #include "base/observer_list.h" #include "base/path_service.h" @@ -17,6 +16,7 @@ #include "base/time/time.h" #include "base/trace_event/typed_macros.h" #include "base/tracing/protos/chrome_track_event.pbzero.h" +#include "base/uuid.h" #include "build/build_config.h" #include "build/chromeos_buildflags.h" #include "chrome/browser/browsing_data/chrome_browsing_data_remover_constants.h" @@ -138,7 +138,7 @@ const std::string& profile_id_prefix) { return OTRProfileID(base::StringPrintf( "%s-%s", profile_id_prefix.c_str(), - base::GUID::GenerateRandomV4().AsLowercaseString().c_str())); + base::Uuid::GenerateRandomV4().AsLowercaseString().c_str())); } // static
diff --git a/chrome/browser/profiles/profile_keyed_service_browsertest.cc b/chrome/browser/profiles/profile_keyed_service_browsertest.cc index 46865b3..bf7553ea 100644 --- a/chrome/browser/profiles/profile_keyed_service_browsertest.cc +++ b/chrome/browser/profiles/profile_keyed_service_browsertest.cc
@@ -210,6 +210,7 @@ std::set<std::string> guest_otr_active_services { #if BUILDFLAG(IS_CHROMEOS_LACROS) "CleanupManagerLacros", + "DownloadBubbleUpdateService", "DownloadCoreService", #else "LiveCaptionController",
diff --git a/chrome/browser/push_messaging/budget_database.cc b/chrome/browser/push_messaging/budget_database.cc index 06a3bd9..9222ad12 100644 --- a/chrome/browser/push_messaging/budget_database.cc +++ b/chrome/browser/push_messaging/budget_database.cc
@@ -6,7 +6,6 @@ #include "base/functional/bind.h" #include "base/memory/ptr_util.h" -#include "base/metrics/histogram_macros.h" #include "base/task/sequenced_task_runner.h" #include "base/task/thread_pool.h" #include "base/time/clock.h" @@ -193,9 +192,6 @@ return; } - // Get the current SES score, to generate UMA. - double score = GetSiteEngagementScoreForOrigin(origin); - // Walk the list of budget chunks to see if the origin has enough budget. double total = 0; BudgetInfo& info = budget_map_[origin]; @@ -203,11 +199,8 @@ total += chunk.amount; if (total < amount) { - UMA_HISTOGRAM_COUNTS_100("PushMessaging.SESForNoBudgetOrigin", score); std::move(callback).Run(false /* success */); return; - } else if (total < amount * 2) { - UMA_HISTOGRAM_COUNTS_100("PushMessaging.SESForLowBudgetOrigin", score); } // Walk the chunks and remove enough budget to cover the needed amount. @@ -355,11 +348,6 @@ base::Time expiration = clock_->Now() + base::Days(kBudgetDurationInDays); budget_map_[origin].chunks.emplace_back(elapsed.InHours() * hourly_budget, expiration); - - // Any time we award engagement budget, which is done at most once an hour - // whenever any budget action is taken, record the budget. - double budget = GetBudget(origin); - UMA_HISTOGRAM_COUNTS_100("PushMessaging.BackgroundBudget", budget); } // Cleans up budget in the cache. Relies on the caller eventually writing the
diff --git a/chrome/browser/push_messaging/budget_database_unittest.cc b/chrome/browser/push_messaging/budget_database_unittest.cc index d9892f8..296a570 100644 --- a/chrome/browser/push_messaging/budget_database_unittest.cc +++ b/chrome/browser/push_messaging/budget_database_unittest.cc
@@ -245,89 +245,6 @@ ASSERT_EQ(budget, prediction_[0].budget_at); } -TEST_F(BudgetDatabaseTest, CheckBackgroundBudgetHistogram) { - base::SimpleTestClock* clock = SetClockForTesting(); - - // Set the default site engagement. - SetSiteEngagementScore(kEngagement); - - // Initialize the budget with some interesting chunks: 30 budget (full - // engagement), 15 budget (half of the engagement), 0 budget (less than an - // hour), and then after the first two expire, another 30 budget. - GetBudgetDetails(); - clock->Advance(base::Days(kDefaultExpirationInDays / 2)); - GetBudgetDetails(); - clock->Advance(base::Minutes(59)); - GetBudgetDetails(); - clock->Advance(base::Days(kDefaultExpirationInDays + 1)); - GetBudgetDetails(); - - // The BackgroundBudget UMA is recorded when budget is added to the origin. - // This can happen a maximum of once per hour so there should be two entries. - std::vector<base::Bucket> buckets = - GetHistogramTester()->GetAllSamples("PushMessaging.BackgroundBudget"); - ASSERT_EQ(2U, buckets.size()); - // First bucket is for full award, which should have 2 entries. - double full_award = kMaxDailyBudget * kEngagement / - site_engagement::SiteEngagementScore::kMaxPoints * - kDefaultExpirationInDays; - EXPECT_EQ(floor(full_award), buckets[0].min); - EXPECT_EQ(2, buckets[0].count); - // Second bucket is for 1.5 * award, which should have 1 entry. - EXPECT_EQ(floor(full_award * 1.5), buckets[1].min); - EXPECT_EQ(1, buckets[1].count); -} - -TEST_F(BudgetDatabaseTest, CheckEngagementHistograms) { - base::SimpleTestClock* clock = SetClockForTesting(); - - // Manipulate the engagement so that the budget is twice the cost of an - // action. - double cost = 2; - double engagement = 2 * cost * - site_engagement::SiteEngagementScore::kMaxPoints / - kDefaultExpirationInDays / kMaxDailyBudget; - SetSiteEngagementScore(engagement); - - // Get the budget, which will award a chunk of budget equal to engagement. - GetBudgetDetails(); - - // Now spend the budget to trigger the UMA recording the SES score. The first - // call shouldn't write any UMA. The second should write a lowSES entry, and - // the third should write a noSES entry. - ASSERT_TRUE(SpendBudget(cost)); - ASSERT_TRUE(SpendBudget(cost)); - ASSERT_FALSE(SpendBudget(cost)); - - // Advance the clock by 12 days (to guarantee a full new engagement grant) - // then change the SES score to get a different UMA entry, then spend the - // budget again. - clock->Advance(base::Days(12)); - GetBudgetDetails(); - SetSiteEngagementScore(engagement * 2); - ASSERT_TRUE(SpendBudget(cost)); - ASSERT_TRUE(SpendBudget(cost)); - ASSERT_FALSE(SpendBudget(cost)); - - // Now check the UMA. Both UMA should have 2 buckets with 1 entry each. - std::vector<base::Bucket> no_budget_buckets = - GetHistogramTester()->GetAllSamples("PushMessaging.SESForNoBudgetOrigin"); - ASSERT_EQ(2U, no_budget_buckets.size()); - EXPECT_EQ(floor(engagement), no_budget_buckets[0].min); - EXPECT_EQ(1, no_budget_buckets[0].count); - EXPECT_EQ(floor(engagement * 2), no_budget_buckets[1].min); - EXPECT_EQ(1, no_budget_buckets[1].count); - - std::vector<base::Bucket> low_budget_buckets = - GetHistogramTester()->GetAllSamples( - "PushMessaging.SESForLowBudgetOrigin"); - ASSERT_EQ(2U, low_budget_buckets.size()); - EXPECT_EQ(floor(engagement), low_budget_buckets[0].min); - EXPECT_EQ(1, low_budget_buckets[0].count); - EXPECT_EQ(floor(engagement * 2), low_budget_buckets[1].min); - EXPECT_EQ(1, low_budget_buckets[1].count); -} - TEST_F(BudgetDatabaseTest, DefaultSiteEngagementInIncognitoProfile) { TestingProfile second_profile; Profile* second_profile_incognito =
diff --git a/chrome/browser/resources/password_manager/password_manager_proxy.ts b/chrome/browser/resources/password_manager/password_manager_proxy.ts index 3816b29..ba31a1a 100644 --- a/chrome/browser/resources/password_manager/password_manager_proxy.ts +++ b/chrome/browser/resources/password_manager/password_manager_proxy.ts
@@ -249,6 +249,14 @@ Promise<chrome.passwordsPrivate.ImportResults>; /** + * Resets the PasswordImporter if it is in the CONFLICTS/FINISHED state and + * the user closes the dialog. Only when the PasswordImporter is in FINISHED + * state, |deleteFile| option is taken into account. + * @param deleteFile Whether to trigger deletion of the last imported file. + */ + resetImporter(deleteFile: boolean): Promise<void>; + + /** * Queries the status of any ongoing export. */ requestExportProgressStatus(): @@ -485,6 +493,10 @@ return chrome.passwordsPrivate.importPasswords(toStore); } + resetImporter(deleteFile: boolean) { + return chrome.passwordsPrivate.resetImporter(deleteFile); + } + requestExportProgressStatus() { return chrome.passwordsPrivate.requestExportProgressStatus(); }
diff --git a/chrome/browser/resources/password_manager/passwords_importer.html b/chrome/browser/resources/password_manager/passwords_importer.html index f1a16794..3b0ffd9 100644 --- a/chrome/browser/resources/password_manager/passwords_importer.html +++ b/chrome/browser/resources/password_manager/passwords_importer.html
@@ -83,6 +83,11 @@ padding: 8px; } + #deleteFileOption { + margin-top: 16px; + --cr-checkbox-label-padding-start: 15px; + } + @media (prefers-color-scheme: dark) { #tipBox { background: var(--google-grey-900); @@ -190,6 +195,10 @@ <div id="successTip" inner-h-t-m-l="[[getSuccessTipHtml_(results_)]]"> </div> </div> + <cr-checkbox id="deleteFileOption" + hidden="[[shouldHideDeleteFileOption_(results_)]]" + inner-h-t-m-l="[[getCheckboxLabelHtml_(results_)]]"> + </cr-checkbox> <div hidden="[[shouldHideFailuresSummary_(results_)]]"> <div id="failuresTitleRow" class="flex-centered"> <iron-icon class="error-icon" icon="cr:warning"></iron-icon>
diff --git a/chrome/browser/resources/password_manager/passwords_importer.ts b/chrome/browser/resources/password_manager/passwords_importer.ts index 50f85e6..6bb9ca18 100644 --- a/chrome/browser/resources/password_manager/passwords_importer.ts +++ b/chrome/browser/resources/password_manager/passwords_importer.ts
@@ -9,6 +9,7 @@ import 'chrome://resources/polymer/v3_0/paper-spinner/paper-spinner-lite.js'; import './site_favicon.js'; +import {CrCheckboxElement} from 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.js'; import {CrLinkRowElement} from 'chrome://resources/cr_elements/cr_link_row/cr_link_row.js'; import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js'; import {assert, assertNotReached} from 'chrome://resources/js/assert_ts.js'; @@ -47,6 +48,13 @@ static get properties() { return { + enablePasswordsImportM2_: { + type: Boolean, + value() { + return loadTimeData.getBoolean('enablePasswordsImportM2'); + }, + }, + inProgress_: { type: Boolean, value: false, @@ -103,6 +111,7 @@ isAccountStoreUser: boolean; accountEmail: string; + private enablePasswordsImportM2_: boolean; private inProgress_: boolean; private dialogState_: DialogState = DialogState.NO_DIALOG; // Refers both to syncing users with sync enabled for passwords and account @@ -171,11 +180,28 @@ // dialog is closed. } - private onCloseClick_() { + private async resetImporter() { + let deleteFile = false; + if (this.isState_(DialogState.SUCCESS) && + !this.shouldHideDeleteFileOption_()) { + // Trigger the file deletion if checkbox is ticked in SUCCESS (with no + // errors) state. + const deleteFileOption = + this.shadowRoot!.querySelector<CrCheckboxElement>( + '#deleteFileOption'); + assert(deleteFileOption); + deleteFile = deleteFileOption.checked; + } + await this.passwordManager_.resetImporter(deleteFile); + } + + private async onCloseClick_() { + await this.resetImporter(); this.closeDialog_(); } - private onViewPasswordsClick_() { + private async onViewPasswordsClick_() { + await this.resetImporter(); this.closeDialog_(); Router.getInstance().navigateTo(Page.PASSWORDS); } @@ -317,13 +343,33 @@ {attrs: ['class'], substitutions: [this.results_.fileName]}); } + private getCheckboxLabelHtml_(): TrustedHTML { + assert(this.results_); + return this.i18nAdvanced( + 'importPasswordsDeleteFileOption', + {attrs: ['class'], substitutions: [this.results_.fileName]}); + } + private shouldHideLinkRowIcon_(): boolean { return this.inProgress_ || this.showSelectFileButton_; } private shouldHideTipBox_(): boolean { // Tip box is only shown in "success" state if all passwords were imported. - // TODO(crbug/1432962): Also hide when import M2 is enabled. + // Only shown in Passwords Import M1. + if (this.enablePasswordsImportM2_) { + return true; + } + assert(this.results_); + return !!this.results_.displayedEntries.length; + } + + private shouldHideDeleteFileOption_(): boolean { + // "Delete file" checkbox is only shown in "success" state if all passwords + // were imported. + if (!this.enablePasswordsImportM2_) { + return true; + } assert(this.results_); return !!this.results_.displayedEntries.length; }
diff --git a/chrome/browser/resources/password_manager/settings_section.ts b/chrome/browser/resources/password_manager/settings_section.ts index 5796650..e2af72e1 100644 --- a/chrome/browser/resources/password_manager/settings_section.ts +++ b/chrome/browser/resources/password_manager/settings_section.ts
@@ -101,6 +101,14 @@ private setCredentialsChangedListener_: CredentialsChangedListener|null = null; + override ready() { + super.ready(); + + chrome.metricsPrivate.recordBoolean( + 'PasswordManager.OpenedAsShortcut', + window.matchMedia('(display-mode: standalone)').matches); + } + override connectedCallback() { super.connectedCallback(); this.setBlockedSitesListListener_ = blockedSites => {
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/passpoint_subpage.html b/chrome/browser/resources/settings/chromeos/internet_page/passpoint_subpage.html index b456711..69329106 100644 --- a/chrome/browser/resources/settings/chromeos/internet_page/passpoint_subpage.html +++ b/chrome/browser/resources/settings/chromeos/internet_page/passpoint_subpage.html
@@ -1,5 +1,82 @@ -<style include="settings-shared"> +<style include="settings-shared network-shared"> + /* Align the headline text on the page title. */ + #headlineLink { + margin-inline-start: 36px; + } </style> -<div> - <!-- TODO(b/266151248) add Passpoint details content. --> +<div class="settings-box first two-line"> + <localized-link id="headlineLink" class="secondary" + localized-string="$i18n{passpointHeadlineText}" + link-url="$i18nRaw{wifiPasspointLearnMoreUrl}"> + </localized-link> + <cr-button id="removeButton" on-click="onForgetTap_"> + $i18n{passpointRemoveButton} + </cr-button> </div> +<template is="dom-if" if="[[hasExpirationDate_(subscription_)]]"> + <div class="settings-box first two-line single-column" + aria-labelledby="passpointExpirationLabel passpointExpirationDate"> + <div id="passpointExpirationLabel" aria-hidden="true"> + $i18n{passpointSubscriptionExpirationLabel} + </div> + <div id="passpointExpirationDate" class="secondary" aria-hidden="true"> + [[getExpirationDate_(subscription_)]] + </div> + </div> +</template> +<div class="settings-box two-line single-column" + aria-labelledby="passpointSourceLabel passpointSourceText"> + <div id="passpointSourceLabel" aria-hidden="true"> + $i18n{passpointSourceLabel} + </div> + <div id="passpointSourceText" class="secondary" aria-hidden="true"> + [[providerName_]] + </div> +</div> +<div class="settings-box two-line single-column" + aria-labelledby="passpointTrustedCALabel passpointCertificateName"> + <div id="passpointTrustedCALabel" aria-hidden="true"> + $i18n{passpointTrustedCALabel} + </div> + <div id="passpointCertificateName" class="secondary" aria-hidden="true"> + [[certificateAuthorityName_]] + </div> +</div> +<cr-expand-button aria-label="$i18n{passpointDomainsA11yLabel}" + class="settings-box" expanded="{{domainsExpanded_}}"> + $i18n{passpointDomainsLabel} +</cr-expand-button> +<div id="passpointDomainsList" class="list-frame vertical-list"> + <iron-collapse opened="[[domainsExpanded_]]"> + <template is="dom-repeat" + items="[[getPasspointDomainsList_(subscription_)]]"> + <div class="list-item secondary"> + [[item]] + </div> + </template> + </iron-collapse> +</div> +<!-- Removal dialog triggered by the "forget" button --> +<template is="dom-if" if="[[showForgetDialog_]]" restamp> + <cr-dialog id="removalDialog" close-text="$i18n{close}" show-on-attach> + <div slot="title"> + $i18nPolymer{passpointRemovalTitle} + </div> + <div slot="body"> + <localized-link + localized-string="[[getRemovalDialogDescription_(subscription_)]]" + link-url="$i18nRaw{wifiPasspointLearnMoreUrl}"> + </localized-link> + </div> + <div slot="button-container"> + <cr-button id="removalCancelButton" class="cancel-button" + on-click="onRemovalDialogCancel_"> + $i18n{cancel} + </cr-button> + <cr-button id="removalConfirmButton" class="action-button" + on-click="onRemovalDialogConfirm_"> + $i18n{confirm} + </cr-button> + </div> + </cr-dialog> +</template>
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/passpoint_subpage.ts b/chrome/browser/resources/settings/chromeos/internet_page/passpoint_subpage.ts index f74f0b86..6596ee06 100644 --- a/chrome/browser/resources/settings/chromeos/internet_page/passpoint_subpage.ts +++ b/chrome/browser/resources/settings/chromeos/internet_page/passpoint_subpage.ts
@@ -9,15 +9,25 @@ import '../../settings_shared.css.js'; +import {MojoConnectivityProvider} from 'chrome://resources/ash/common/connectivity/mojo_connectivity_provider.js'; +import {PasspointServiceInterface, PasspointSubscription} from 'chrome://resources/ash/common/connectivity/passpoint.mojom-webui.js'; +import {MojoInterfaceProviderImpl} from 'chrome://resources/ash/common/network/mojo_interface_provider.js'; +import {App, AppType, PageHandlerInterface} from 'chrome://resources/cr_components/app_management/app_management.mojom-webui.js'; +import {BrowserProxy as AppManagementComponentBrowserProxy} from 'chrome://resources/cr_components/app_management/browser_proxy.js'; import {I18nMixin} from 'chrome://resources/cr_elements/i18n_mixin.js'; +import {CrosNetworkConfigRemote, NetworkCertificate} from 'chrome://resources/mojo/chromeos/services/network_config/public/mojom/cros_network_config.mojom-webui.js'; import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; +import {castExists} from '../assert_extras.js'; +import {routes} from '../os_settings_routes.js'; +import {RouteObserverMixin} from '../route_observer_mixin.js'; +import {Route, Router} from '../router.js'; + +import {PasspointListenerMixin} from './passpoint_listener_mixin.js'; import {getTemplate} from './passpoint_subpage.html.js'; -const SettingsPasspointSubpageElementBase = I18nMixin(PolymerElement); - -class SettingsPasspointSubpageElement extends - SettingsPasspointSubpageElementBase { +export class SettingsPasspointSubpageElement extends PasspointListenerMixin +(RouteObserverMixin(I18nMixin(PolymerElement))) { static get is() { return 'settings-passpoint-subpage' as const; } @@ -27,11 +37,181 @@ } static get properties() { - return {}; + return { + /** The identifier of the subscription for which details are shown. */ + id_: String, + + /** Passpoint subscription currently displayed. */ + subscription_: Object, + + /** ARC application that provided the subscription. */ + app_: Object, + + /** List of Certificate Authorities available. */ + certs_: Array, + + /** Certificate authority common name. */ + certificateAuthorityName_: { + type: String, + computed: 'getCertificateAuthorityName_(certs_)', + }, + + /** Name of the provider of the subscription. */ + providerName_: { + type: String, + computed: 'getProviderName_(subscription_, app_)', + }, + + /** Tell if the forget dialog should be displayed. */ + showForgetDialog_: Boolean, + + domainsExpanded_: Boolean, + }; } + private app_: App|null; + private appHandler_: PageHandlerInterface; + private certs_: NetworkCertificate[]; + private certificateAuthorityName_: string; + private domainsExpanded_: boolean; + private id_: string; + private networkConfig_: CrosNetworkConfigRemote; + private passpointService_: PasspointServiceInterface; + private providerName_: string; + private showForgetDialog_: boolean; + private subscription_: PasspointSubscription|null; + constructor() { super(); + this.networkConfig_ = + MojoInterfaceProviderImpl.getInstance().getMojoServiceRemote(); + this.passpointService_ = + MojoConnectivityProvider.getInstance().getPasspointService(); + this.appHandler_ = AppManagementComponentBrowserProxy.getInstance().handler; + } + + close(): void { + // If the page is already closed, return early to avoid navigating backward + // erroneously. + if (!this.id_) { + return; + } + + this.id_ = ''; + Router.getInstance().navigateToPreviousRoute(); + } + + + /** + * RouteObserverMixin override + */ + override currentRouteChanged(route: Route): void { + if (route !== routes.PASSPOINT_DETAIL) { + return; + } + + const queryParams = Router.getInstance().getQueryParameters(); + const id = queryParams.get('id') || ''; + if (!id) { + console.warn('No Passpoint subscription ID specified for page:' + route); + this.close(); + return; + } + this.id_ = id; + this.refresh_(); + } + + private async refresh_(): Promise<void> { + const response = + await this.passpointService_.getPasspointSubscription(this.id_); + if (!response.result) { + console.warn('No subscription found for id ' + this.id_); + this.close(); + return; + } + this.subscription_ = response.result; + this.refreshCertificates_(); + this.refreshApp_(this.subscription_); + } + + private async refreshCertificates_() { + const certs = await this.networkConfig_.getNetworkCertificates(); + this.certs_ = certs.serverCas; + } + + private async refreshApp_(subscription: PasspointSubscription) { + const response = await this.appHandler_.getApps(); + for (const app of response.apps) { + if (app.type === AppType.kArc && + app.publisherId === subscription.provisioningSource) { + this.app_ = app; + return; + } + } + } + + private getCertificateAuthorityName_() { + for (const cert of this.certs_) { + if (cert.pemOrId === this.subscription_!.trustedCa) { + return cert.issuedTo; + } + } + return this.i18n('passpointSystemCALabel'); + } + + private hasExpirationDate_(): boolean { + return this.subscription_!.expirationEpochMs > 0n; + } + + private getExpirationDate_(subscription: PasspointSubscription): string { + const date = new Date(Number(subscription.expirationEpochMs)); + return date.toLocaleDateString(); + } + + private getProviderName_(): string { + if (this.app_ && this.app_!.title !== undefined) { + return this.app_!.title; + } + return this.subscription_!.provisioningSource; + } + + private getPasspointDomainsList_(): string[] { + return this.subscription_!.domains; + } + + private getRemovalDialogDescription_() { + return this.i18n( + 'passpointRemovalDescription', this.subscription_!.friendlyName); + } + + private getRemovalDialog_(): HTMLDialogElement { + return castExists( + this.shadowRoot!.querySelector<HTMLDialogElement>('#removalDialog')); + } + + private onForgetTap_() { + this.showForgetDialog_ = true; + } + + private async onRemovalDialogConfirm_() { + this.showForgetDialog_ = false; + const response = + await this.passpointService_.deletePasspointSubscription(this.id_); + if (response.success) { + this.close(); + return; + } + } + + private onRemovalDialogCancel_() { + this.showForgetDialog_ = false; + } + + override onPasspointSubscriptionRemoved(subscription: PasspointSubscription) { + if (this.id_ === subscription.id) { + // The subscription was removed, leave the page. + this.close(); + } } }
diff --git a/chrome/browser/resources/settings/chromeos/lazy_load.ts b/chrome/browser/resources/settings/chromeos/lazy_load.ts index 1e23e8e..965091f1 100644 --- a/chrome/browser/resources/settings/chromeos/lazy_load.ts +++ b/chrome/browser/resources/settings/chromeos/lazy_load.ts
@@ -112,6 +112,7 @@ export {TimezoneSubpageElement} from './date_time_page/timezone_subpage.js'; export {CROSTINI_TYPE, GuestOsBrowserProxy, GuestOsBrowserProxyImpl, GuestOsSharedUsbDevice, PLUGIN_VM_TYPE} from './guest_os/guest_os_browser_proxy.js'; export {SettingsGuestOsSharedUsbDevicesElement} from './guest_os/guest_os_shared_usb_devices.js'; +export {SettingsPasspointSubpageElement} from './internet_page/passpoint_subpage.js'; export {TetherConnectionDialogElement} from './internet_page/tether_connection_dialog.js'; export {KeyboardShortcutBanner} from './keyboard_shortcut_banner/keyboard_shortcut_banner.js'; export {SettingsMultideviceCombinedSetupItemElement} from './multidevice_page/multidevice_combined_setup_item.js';
diff --git a/chrome/browser/segmentation_platform/segmentation_platform_config.cc b/chrome/browser/segmentation_platform/segmentation_platform_config.cc index a869551..885f535 100644 --- a/chrome/browser/segmentation_platform/segmentation_platform_config.cc +++ b/chrome/browser/segmentation_platform/segmentation_platform_config.cc
@@ -13,14 +13,12 @@ #include "chrome/browser/metrics/chrome_metrics_service_accessor.h" #include "components/segmentation_platform/embedder/default_model/cross_device_user_segment.h" #include "components/segmentation_platform/embedder/default_model/device_switcher_model.h" -#include "components/segmentation_platform/embedder/default_model/device_tier_segment.h" #include "components/segmentation_platform/embedder/default_model/feed_user_segment.h" #include "components/segmentation_platform/embedder/default_model/frequent_feature_user_model.h" #include "components/segmentation_platform/embedder/default_model/low_user_engagement_model.h" #include "components/segmentation_platform/embedder/default_model/resume_heavy_user_model.h" #include "components/segmentation_platform/embedder/default_model/search_user_model.h" #include "components/segmentation_platform/embedder/default_model/shopping_user_model.h" -#include "components/segmentation_platform/embedder/default_model/tablet_productivity_user_model.h" #include "components/segmentation_platform/internal/config_parser.h" #include "components/segmentation_platform/public/config.h" #include "components/segmentation_platform/public/constants.h" @@ -38,9 +36,11 @@ #include "components/commerce/core/commerce_feature_list.h" #include "components/commerce/core/shopping_service.h" #include "components/segmentation_platform/embedder/default_model/contextual_page_actions_model.h" +#include "components/segmentation_platform/embedder/default_model/device_tier_segment.h" #include "components/segmentation_platform/embedder/default_model/intentional_user_model.h" #include "components/segmentation_platform/embedder/default_model/power_user_segment.h" #include "components/segmentation_platform/embedder/default_model/query_tiles_model.h" +#include "components/segmentation_platform/embedder/default_model/tablet_productivity_user_model.h" #endif namespace segmentation_platform {
diff --git a/chrome/browser/signin/chrome_device_id_helper.cc b/chrome/browser/signin/chrome_device_id_helper.cc index c433a4b3..ffe0f43 100644 --- a/chrome/browser/signin/chrome_device_id_helper.cc +++ b/chrome/browser/signin/chrome_device_id_helper.cc
@@ -12,8 +12,8 @@ #if BUILDFLAG(IS_CHROMEOS_ASH) #include "base/command_line.h" -#include "base/guid.h" #include "base/logging.h" +#include "base/uuid.h" #include "chrome/browser/ash/profiles/profile_helper.h" #include "chrome/browser/browser_process.h" #include "components/prefs/pref_service.h" @@ -54,7 +54,7 @@ std::string GenerateSigninScopedDeviceId(bool for_ephemeral) { constexpr char kEphemeralUserDeviceIDPrefix[] = "t_"; - std::string guid = base::GenerateGUID(); + std::string guid = base::Uuid::GenerateRandomV4().AsLowercaseString(); return for_ephemeral ? kEphemeralUserDeviceIDPrefix + guid : guid; }
diff --git a/chrome/browser/sync/sync_ui_util.cc b/chrome/browser/sync/sync_ui_util.cc index 889f03cf..443da18 100644 --- a/chrome/browser/sync/sync_ui_util.cc +++ b/chrome/browser/sync/sync_ui_util.cc
@@ -103,7 +103,8 @@ // Check to see if sync has been disabled via the dashboard and needs to be // set up once again. - if (!service->GetUserSettings()->IsSyncRequested()) { + if (service->GetDisableReasons().Has( + syncer::SyncService::DISABLE_REASON_USER_CHOICE)) { return {SyncStatusMessageType::kSyncError, IDS_SIGNED_IN_WITH_SYNC_STOPPED_VIA_DASHBOARD, IDS_SETTINGS_EMPTY_STRING, SyncStatusActionType::kNoAction};
diff --git a/chrome/browser/touch_to_fill/payments/android/touch_to_fill_delegate_android_impl.cc b/chrome/browser/touch_to_fill/payments/android/touch_to_fill_delegate_android_impl.cc index 4b753591..46ad8dc 100644 --- a/chrome/browser/touch_to_fill/payments/android/touch_to_fill_delegate_android_impl.cc +++ b/chrome/browser/touch_to_fill/payments/android/touch_to_fill_delegate_android_impl.cc
@@ -70,9 +70,9 @@ return {TriggerOutcome::kUnsupportedFieldType, {}}; } - // Trigger only for complete forms (contining the fields for the card number + // Trigger only for complete forms (containing the fields for the card number // and the card expiration date). - if (!FormHasAllEmtyCreditCardFields(*form)) { + if (!FormHasAllCreditCardFields(*form)) { return {TriggerOutcome::kIncompleteForm, {}}; } if (optional_received_form != nullptr && @@ -284,16 +284,14 @@ } bool TouchToFillDelegateAndroidImpl::IsFormPrefilled(const FormData& form) { - return base::ranges::any_of( - form.fields.begin(), form.fields.end(), [&](const FormFieldData& field) { - AutofillField* autofill_field = manager_->GetAutofillField(form, field); - if (!autofill_field->HasExpirationDateType() && - autofill_field->Type().GetStorableType() != - ServerFieldType::CREDIT_CARD_NUMBER) { - return false; - } - return !SanitizedFieldIsEmpty(field.value); - }); + return base::ranges::any_of(form.fields, [&](const FormFieldData& field) { + AutofillField* autofill_field = manager_->GetAutofillField(form, field); + if (autofill_field->Type().GetStorableType() != + ServerFieldType::CREDIT_CARD_NUMBER) { + return false; + } + return !SanitizedFieldIsEmpty(field.value); + }); } base::WeakPtr<TouchToFillDelegateAndroidImpl>
diff --git a/chrome/browser/touch_to_fill/payments/android/touch_to_fill_delegate_android_impl.h b/chrome/browser/touch_to_fill/payments/android/touch_to_fill_delegate_android_impl.h index a3b3c28..10d04c71 100644 --- a/chrome/browser/touch_to_fill/payments/android/touch_to_fill_delegate_android_impl.h +++ b/chrome/browser/touch_to_fill/payments/android/touch_to_fill_delegate_android_impl.h
@@ -177,8 +177,9 @@ bool IsFillingCorrect(const FormStructure& submitted_form) const; // Checks if the credit card form is already filled with values. The form is - // considered to be filled if the credit card number and the expiry date - // fields are non-empty. + // considered to be filled if the credit card number field is non-empty. The + // expiration date fields are not checked because they might have arbitrary + // placeholders. // TODO(crbug.com/1331312): FormData is used here to ensure that we check the // most recent form values. FormStructure knows only about the initial values. bool IsFormPrefilled(const FormData& form);
diff --git a/chrome/browser/touch_to_fill/payments/android/touch_to_fill_delegate_android_impl_unittest.cc b/chrome/browser/touch_to_fill/payments/android/touch_to_fill_delegate_android_impl_unittest.cc index 126cca72..5a12284a 100644 --- a/chrome/browser/touch_to_fill/payments/android/touch_to_fill_delegate_android_impl_unittest.cc +++ b/chrome/browser/touch_to_fill/payments/android/touch_to_fill_delegate_android_impl_unittest.cc
@@ -294,7 +294,7 @@ } TEST_F(TouchToFillDelegateAndroidImplUnitTest, - TryToShowTouchToFillFailsForPrefilledYear) { + TryToShowTouchToFillSucceedsForPrefilledYear) { // Force the form to be parsed here to test the case, when form values are // changed after the form is added to the cache. browser_autofill_manager_->OnFormsSeen({form_}, {}); @@ -304,11 +304,11 @@ form_.fields[3].value = u"2023"; ASSERT_FALSE(touch_to_fill_delegate_->IsShowingTouchToFill()); - TryToShowTouchToFill(/*expected_success=*/false); + TryToShowTouchToFill(/*expected_success=*/true); histogram_tester_.ExpectUniqueSample( kUmaTouchToFillCreditCardTriggerOutcome, - TouchToFillCreditCardTriggerOutcome::kFormAlreadyFilled, 1); + TouchToFillCreditCardTriggerOutcome::kShown, 1); } TEST_F(TouchToFillDelegateAndroidImplUnitTest,
diff --git a/chrome/browser/ui/views/passwords/manage_passwords_details_view.cc b/chrome/browser/ui/views/passwords/manage_passwords_details_view.cc index 0bc5a953..9dcd85f 100644 --- a/chrome/browser/ui/views/passwords/manage_passwords_details_view.cc +++ b/chrome/browser/ui/views/passwords/manage_passwords_details_view.cc
@@ -347,10 +347,17 @@ base::RepeatingClosure on_back_clicked_callback) { ChromeLayoutProvider* layout_provider = ChromeLayoutProvider::Get(); auto header = std::make_unique<views::BoxLayoutView>(); - // Set the space between the icons and title similar to the default behavior - // in BubbleFrameView::Layout(). + // Set the space between the icon and title similar to the space in the row + // below to make sure the bubble title and the labels below are vertically + // aligned. In the rows below the distance between the icon and the text is + // DISTANCE_RELATED_CONTROL_HORIZONTAL. But the icon in the title has a border + // of size INSETS_VECTOR_IMAGE_BUTTON to have space for the focus ring, and + // hence this is subtracted here. header->SetBetweenChildSpacing( - layout_provider->GetInsetsMetric(views::INSETS_DIALOG_TITLE).left()); + layout_provider->GetDistanceMetric( + views::DISTANCE_RELATED_CONTROL_HORIZONTAL) - + layout_provider->GetInsetsMetric(views::INSETS_VECTOR_IMAGE_BUTTON) + .right()); auto back_button = views::CreateVectorImageButtonWithNativeTheme( on_back_clicked_callback, vector_icons::kArrowBackIcon);
diff --git a/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc b/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc index 2c059e1..d2f21d2ce 100644 --- a/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc +++ b/chrome/browser/ui/views/profiles/profile_picker_view_browsertest.cc
@@ -1624,7 +1624,7 @@ // Sync is disabled. EXPECT_NE(entry->GetGAIAId(), std::string()); - EXPECT_FALSE(sync_service->GetUserSettings()->IsSyncRequested()); + EXPECT_FALSE(sync_service->IsSyncFeatureEnabled()); EXPECT_EQ(ThemeServiceFactory::GetForProfile(profile_being_created) ->GetAutogeneratedThemeColor(), kProfileColor);
diff --git a/chrome/browser/ui/web_applications/test/isolated_web_app_test_utils.cc b/chrome/browser/ui/web_applications/test/isolated_web_app_test_utils.cc index aa4aea82..b608c590 100644 --- a/chrome/browser/ui/web_applications/test/isolated_web_app_test_utils.cc +++ b/chrome/browser/ui/web_applications/test/isolated_web_app_test_utils.cc
@@ -136,7 +136,8 @@ auto url_info = IsolatedWebAppUrlInfo::CreateFromSignedWebBundleId( web_package::SignedWebBundleId::CreateRandomForDevelopment()); WebAppProvider::GetForWebApps(profile)->scheduler().InstallIsolatedWebApp( - url_info, DevModeProxy{.proxy_url = proxy_origin}, future.GetCallback()); + url_info, DevModeProxy{.proxy_url = proxy_origin}, /*keep_alive=*/nullptr, + /*profile_keep_alive=*/nullptr, future.GetCallback()); CHECK(future.Get().has_value()) << future.Get().error();
diff --git a/chrome/browser/ui/web_applications/web_app_launch_utils.cc b/chrome/browser/ui/web_applications/web_app_launch_utils.cc index 33b440b5..bb4ec43 100644 --- a/chrome/browser/ui/web_applications/web_app_launch_utils.cc +++ b/chrome/browser/ui/web_applications/web_app_launch_utils.cc
@@ -492,8 +492,8 @@ // TODO(crbug.com/1425284): Cover other app launch paths (e.g. restore // apps). auto partition_config = content::StoragePartitionConfig::Create( - nav_params.browser->profile(), /*partition_domain=*/app_id, - /*partition_name=*/"goldfish", /*in_memory=*/false); + nav_params.browser->profile(), /*partition_domain=*/"goldfish", + /*partition_name=*/app_id, /*in_memory=*/false); auto guest_site_instance = content::SiteInstance::CreateForGuest( nav_params.browser->profile(), partition_config);
diff --git a/chrome/browser/ui/webui/BUILD.gn b/chrome/browser/ui/webui/BUILD.gn index 0ded0dc..10284775 100644 --- a/chrome/browser/ui/webui/BUILD.gn +++ b/chrome/browser/ui/webui/BUILD.gn
@@ -64,6 +64,7 @@ "//ash/webui/face_ml_app_ui", "//ash/webui/file_manager:file_manager_untrusted_ui", "//ash/webui/firmware_update_ui:firmware_update_ui", + "//ash/webui/guest_os_installer:guest_os_installer", "//ash/webui/help_app_ui", "//ash/webui/os_feedback_ui", "//ash/webui/shortcut_customization_ui",
diff --git a/chrome/browser/ui/webui/ash/login/oobe_ui.cc b/chrome/browser/ui/webui/ash/login/oobe_ui.cc index 728a8ee2..3c7e963 100644 --- a/chrome/browser/ui/webui/ash/login/oobe_ui.cc +++ b/chrome/browser/ui/webui/ash/login/oobe_ui.cc
@@ -350,6 +350,11 @@ const DisplayScaleFactor k4KDisplay = {3840, 1.5f}, kMediumDisplay = {1440, 4.f / 3}; +bool OobeUIConfig::IsWebUIEnabled(content::BrowserContext* browser_context) { + return ash::ProfileHelper::IsSigninProfile( + Profile::FromBrowserContext(browser_context)); +} + // static const char OobeUI::kAppLaunchSplashDisplay[] = "app-launch-splash"; const char OobeUI::kGaiaSigninDisplay[] = "gaia-signin";
diff --git a/chrome/browser/ui/webui/ash/login/oobe_ui.h b/chrome/browser/ui/webui/ash/login/oobe_ui.h index f133fbc..b6ca84a 100644 --- a/chrome/browser/ui/webui/ash/login/oobe_ui.h +++ b/chrome/browser/ui/webui/ash/login/oobe_ui.h
@@ -10,16 +10,19 @@ #include <string> #include <vector> +#include "ash/webui/common/chrome_os_webui_config.h" #include "base/memory/ref_counted.h" #include "base/observer_list.h" #include "base/values.h" #include "chrome/browser/ash/login/oobe_screen.h" #include "chrome/browser/ui/webui/ash/login/base_screen_handler.h" #include "chrome/browser/ui/webui/ash/login/core_oobe_handler.h" +#include "chrome/common/webui_url_constants.h" #include "chromeos/ash/services/auth_factor_config/public/mojom/auth_factor_config.mojom-forward.h" #include "chromeos/ash/services/cellular_setup/public/mojom/esim_manager.mojom-forward.h" #include "chromeos/ash/services/multidevice_setup/public/mojom/multidevice_setup.mojom-forward.h" #include "chromeos/services/network_config/public/mojom/cros_network_config.mojom-forward.h" +#include "content/public/common/url_constants.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "ui/webui/mojo_web_ui_controller.h" #include "ui/webui/resources/cr_components/color_change_listener/color_change_listener.mojom.h" @@ -37,6 +40,17 @@ class ErrorScreen; class NetworkStateInformer; class OobeDisplayChooser; +class OobeUI; + +// The WebUIConfig for chrome://oobe urls +class OobeUIConfig : public ChromeOSWebUIConfig<OobeUI> { + public: + OobeUIConfig() + : ChromeOSWebUIConfig(content::kChromeUIScheme, + chrome::kChromeUIOobeHost) {} + + bool IsWebUIEnabled(content::BrowserContext* browser_context) override; +}; // A custom WebUI that defines datasource for out-of-box-experience (OOBE) UI: // - welcome screen (setup language/keyboard/network).
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc index 9f1dfc0..fda8ec73 100644 --- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc +++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -201,8 +201,6 @@ #include "ash/webui/files_internals/url_constants.h" #include "ash/webui/firmware_update_ui/firmware_update_app_ui.h" #include "ash/webui/firmware_update_ui/url_constants.h" -#include "ash/webui/guest_os_installer/guest_os_installer_ui.h" -#include "ash/webui/guest_os_installer/url_constants.h" #include "ash/webui/help_app_ui/help_app_ui.h" #include "ash/webui/help_app_ui/url_constants.h" #include "ash/webui/media_app_ui/media_app_ui.h" @@ -231,7 +229,6 @@ #include "chrome/browser/ash/device_sync/device_sync_client_factory.h" #include "chrome/browser/ash/eche_app/eche_app_manager_factory.h" #include "chrome/browser/ash/extensions/url_constants.h" -#include "chrome/browser/ash/guest_os/public/installer_delegate_factory.h" #include "chrome/browser/ash/login/easy_unlock/easy_unlock_service.h" #include "chrome/browser/ash/login/easy_unlock/easy_unlock_service_factory.h" #include "chrome/browser/ash/login/login_pref_names.h" @@ -282,7 +279,6 @@ #include "chrome/browser/ui/webui/ash/launcher_internals/launcher_internals_ui.h" #include "chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_network_ui.h" #include "chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_start_reauth_ui.h" -#include "chrome/browser/ui/webui/ash/login/oobe_ui.h" #include "chrome/browser/ui/webui/ash/manage_mirrorsync/manage_mirrorsync_ui.h" #include "chrome/browser/ui/webui/ash/multidevice_internals/multidevice_internals_ui.h" #include "chrome/browser/ui/webui/ash/multidevice_setup/multidevice_setup_dialog.h" @@ -500,11 +496,6 @@ #if BUILDFLAG(IS_CHROMEOS_ASH) template <> -WebUIController* NewWebUI<ash::OobeUI>(WebUI* web_ui, const GURL& url) { - return new ash::OobeUI(web_ui, url); -} - -template <> WebUIController* NewWebUI<ash::TrustedProjectorUI>(WebUI* web_ui, const GURL& url) { return new ash::TrustedProjectorUI(web_ui, url, @@ -696,13 +687,6 @@ return ash::personalization_app::CreatePersonalizationAppUI(web_ui); } -template <> -WebUIController* NewWebUI<ash::GuestOSInstallerUI>(WebUI* web_ui, - const GURL& url) { - return new ash::GuestOSInstallerUI( - web_ui, url, base::BindRepeating(&guest_os::InstallerDelegateFactory)); -} - #endif // BUILDFLAG(IS_CHROMEOS_ASH) #if BUILDFLAG(ENABLE_DICE_SUPPORT) @@ -964,18 +948,10 @@ return &NewComponentUI<ash::file_manager::FileManagerUI, ChromeFileManagerUIDelegate>; } - if (url.host_piece() == ash::kChromeUIGuestOSInstallerHost) - return &NewWebUI<ash::GuestOSInstallerUI>; if (url.host_piece() == ash::kChromeUIHelpAppHost) return &NewComponentUI<ash::HelpAppUI, ash::ChromeHelpAppUIDelegate>; if (url.host_piece() == chrome::kChromeUIMobileSetupHost) return &NewWebUI<ash::cellular_setup::MobileSetupUI>; - if (url.host_piece() == chrome::kChromeUIOobeHost) { - if (ash::ProfileHelper::IsSigninProfile(profile)) { - return &NewWebUI<ash::OobeUI>; - } - return nullptr; - } if (url.host_piece() == ash::kChromeUIDiagnosticsAppHost) { return &NewWebUI<ash::DiagnosticsDialogUI>; }
diff --git a/chrome/browser/ui/webui/chromeos/chrome_web_ui_configs_chromeos.cc b/chrome/browser/ui/webui/chromeos/chrome_web_ui_configs_chromeos.cc index e068305d..c34a4a2 100644 --- a/chrome/browser/ui/webui/chromeos/chrome_web_ui_configs_chromeos.cc +++ b/chrome/browser/ui/webui/chromeos/chrome_web_ui_configs_chromeos.cc
@@ -23,9 +23,11 @@ #include "ash/webui/connectivity_diagnostics/connectivity_diagnostics_ui.h" #include "ash/webui/files_internals/files_internals_ui.h" #include "ash/webui/firmware_update_ui/firmware_update_app_ui.h" +#include "ash/webui/guest_os_installer/guest_os_installer_ui.h" #include "ash/webui/os_feedback_ui/os_feedback_ui.h" #include "ash/webui/shortcut_customization_ui/shortcut_customization_app_ui.h" #include "ash/webui/system_extensions_internals_ui/system_extensions_internals_ui.h" +#include "chrome/browser/ash/guest_os/public/installer_delegate_factory.h" #include "chrome/browser/ash/net/network_health/network_health_manager.h" #include "chrome/browser/ash/os_feedback/chrome_os_feedback_delegate.h" #include "chrome/browser/ash/web_applications/camera_app/chrome_camera_app_ui_delegate.h" @@ -55,6 +57,7 @@ #include "chrome/browser/ui/webui/ash/launcher_internals/launcher_internals_ui.h" #include "chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_network_ui.h" #include "chrome/browser/ui/webui/ash/lock_screen_reauth/lock_screen_start_reauth_ui.h" +#include "chrome/browser/ui/webui/ash/login/oobe_ui.h" #include "chrome/browser/ui/webui/ash/manage_mirrorsync/manage_mirrorsync_ui.h" #include "chrome/browser/ui/webui/ash/multidevice_internals/multidevice_internals_ui.h" #include "chrome/browser/ui/webui/ash/multidevice_setup/multidevice_setup_dialog.h" @@ -138,6 +141,18 @@ create_controller_func); } +std::unique_ptr<content::WebUIConfig> MakeGuestOSInstallerUIConfig() { + CreateWebUIControllerFunc create_controller_func = + [](content::WebUI* web_ui, + const GURL& url) -> std::unique_ptr<content::WebUIController> { + return std::make_unique<ash::GuestOSInstallerUI>( + web_ui, base::BindRepeating(&guest_os::InstallerDelegateFactory)); + }; + + return std::make_unique<ash::GuestOSInstallerUIConfig>( + create_controller_func); +} + void RegisterAshChromeWebUIConfigs() { // Add `WebUIConfig`s for Ash ChromeOS to the list here. auto& map = content::WebUIConfigMap::GetInstance(); @@ -170,6 +185,7 @@ MakeComponentConfig<ash::FilesInternalsUIConfig, ash::FilesInternalsUI, ChromeFilesInternalsUIDelegate>()); map.AddWebUIConfig(std::make_unique<ash::FirmwareUpdateAppUIConfig>()); + map.AddWebUIConfig(MakeGuestOSInstallerUIConfig()); map.AddWebUIConfig(std::make_unique<ash::HealthdInternalsUIConfig>()); map.AddWebUIConfig(std::make_unique<ash::HumanPresenceInternalsUIConfig>()); map.AddWebUIConfig(std::make_unique<ash::InternetConfigDialogUIConfig>()); @@ -188,6 +204,7 @@ map.AddWebUIConfig(std::make_unique<ash::NotificationTesterUIConfig>()); map.AddWebUIConfig( std::make_unique<ash::office_fallback::OfficeFallbackUIConfig>()); + map.AddWebUIConfig(std::make_unique<ash::OobeUIConfig>()); map.AddWebUIConfig( MakeComponentConfig<ash::OSFeedbackUIConfig, ash::OSFeedbackUI, ash::ChromeOsFeedbackDelegate>());
diff --git a/chrome/browser/ui/webui/password_manager/password_manager_ui.cc b/chrome/browser/ui/webui/password_manager/password_manager_ui.cc index a6b8476..fdb57d76 100644 --- a/chrome/browser/ui/webui/password_manager/password_manager_ui.cc +++ b/chrome/browser/ui/webui/password_manager/password_manager_ui.cc
@@ -30,6 +30,7 @@ #include "components/password_manager/content/common/web_ui_constants.h" #include "components/password_manager/core/browser/leak_detection_dialog_utils.h" #include "components/password_manager/core/common/password_manager_constants.h" +#include "components/password_manager/core/common/password_manager_features.h" #include "components/strings/grit/components_strings.h" #include "components/sync/base/features.h" #include "content/public/browser/url_data_source.h" @@ -193,6 +194,8 @@ {"importPasswordsSuccessTitle", IDS_PASSWORD_MANAGER_UI_IMPORT_SUCCESS_TITLE}, {"importPasswordsSuccessTip", IDS_PASSWORD_MANAGER_UI_IMPORT_SUCCESS_TIP}, + {"importPasswordsDeleteFileOption", + IDS_PASSWORD_MANAGER_UI_IMPORT_DELETE_FILE_OPTION}, {"importPasswordsDescriptionAccount", IDS_PASSWORD_MANAGER_UI_IMPORT_DESCRIPTION_SYNCING_USERS}, {"importPasswordsSelectFile", @@ -321,6 +324,10 @@ g_browser_process->local_state())); #endif + source->AddBoolean("enablePasswordsImportM2", + base::FeatureList::IsEnabled( + password_manager::features::kPasswordsImportM2)); + source->AddString("passwordManagerLearnMoreURL", chrome::kPasswordManagerLearnMoreURL);
diff --git a/chrome/browser/ui/webui/password_manager/sync_handler.cc b/chrome/browser/ui/webui/password_manager/sync_handler.cc index 83af939..b6d1d154 100644 --- a/chrome/browser/ui/webui/password_manager/sync_handler.cc +++ b/chrome/browser/ui/webui/password_manager/sync_handler.cc
@@ -4,6 +4,7 @@ #include "chrome/browser/ui/webui/password_manager/sync_handler.h" +#include "base/values.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/signin/identity_manager_factory.h" #include "chrome/browser/sync/sync_service_factory.h" @@ -84,6 +85,12 @@ base::Value::Dict dict; syncer::SyncService* sync_service = GetSyncService(); + // sync_service might be nullptr if SyncServiceFactory::IsSyncAllowed is + // false. + if (!sync_service) { + return dict; + } + PrefService* pref_service = profile_->GetPrefs(); syncer::UserSelectableTypeSet types = sync_service->GetUserSettings()->GetSelectedTypes();
diff --git a/chrome/browser/ui/webui/password_manager/sync_handler_unittest.cc b/chrome/browser/ui/webui/password_manager/sync_handler_unittest.cc index 8ccbb98..80023bd 100644 --- a/chrome/browser/ui/webui/password_manager/sync_handler_unittest.cc +++ b/chrome/browser/ui/webui/password_manager/sync_handler_unittest.cc
@@ -69,8 +69,6 @@ auto account_info = identity_test_env()->MakePrimaryAccountAvailable( "user@gmail.com", signin::ConsentLevel::kSync); ON_CALL(*sync_service(), HasSyncConsent).WillByDefault(Return(true)); - ON_CALL(*sync_service()->GetMockUserSettings(), IsSyncRequested) - .WillByDefault(Return(true)); ON_CALL(*sync_service()->GetMockUserSettings(), IsFirstSetupComplete()) .WillByDefault(Return(true)); ON_CALL(*sync_service(), GetAccountInfo)
diff --git a/chrome/browser/ui/webui/settings/ash/internet_section.cc b/chrome/browser/ui/webui/settings/ash/internet_section.cc index 073b8040..0474b14c 100644 --- a/chrome/browser/ui/webui/settings/ash/internet_section.cc +++ b/chrome/browser/ui/webui/settings/ash/internet_section.cc
@@ -996,7 +996,21 @@ {"hotspotConfigNotLoginErrorMessage", IDS_SETTINGS_INTERNET_HOTSPOT_CONFIG_NOT_LOGIN_ERROR_MESSAGE}, {"passpointProviderLabel", IDS_SETTINGS_INTERNET_PASSPOINT_PROVIDER}, + {"passpointRemoveButton", + IDS_SETTINGS_INTERNET_PASSPOINT_REMOVE_SUBSCRIPTION}, {"passpointSectionLabel", IDS_SETTINGS_INTERNET_PASSPOINT_SECTION_LABEL}, + {"passpointHeadlineText", IDS_SETTINGS_INTERNET_PASSPOINT_HEADLINE}, + {"passpointSubscriptionExpirationLabel", + IDS_SETTINGS_INTERNET_PASSPOINT_SUBSCRIPTION_EXPIRATION}, + {"passpointSourceLabel", IDS_SETTINGS_INTERNET_PASSPOINT_SOURCE}, + {"passpointTrustedCALabel", IDS_SETTINGS_INTERNET_PASSPOINT_TRUSTED_CA}, + {"passpointSystemCALabel", IDS_SETTINGS_INTERNET_PASSPOINT_SYSTEM_CA}, + {"passpointDomainsLabel", IDS_SETTINGS_INTERNET_PASSPOINT_DOMAINS}, + {"passpointDomainsA11yLabel", + IDS_SETTINGS_INTERNET_PASSPOINT_DOMAINS_A11Y_LABEL}, + {"passpointRemovalTitle", IDS_SETTINGS_INTERNET_PASSPOINT_REMOVAL_TITLE}, + {"passpointRemovalDescription", + IDS_SETTINGS_INTERNET_PASSPOINT_REMOVAL_DESCRIPTION}, }; html_source->AddLocalizedStrings(kLocalizedStrings);
diff --git a/chrome/browser/ui/webui/settings/people_handler_unittest.cc b/chrome/browser/ui/webui/settings/people_handler_unittest.cc index 1fc58dde..93f8179 100644 --- a/chrome/browser/ui/webui/settings/people_handler_unittest.cc +++ b/chrome/browser/ui/webui/settings/people_handler_unittest.cc
@@ -266,8 +266,6 @@ void SetDefaultExpectationsForConfigPage() { ON_CALL(*mock_sync_service_, GetDisableReasons()) .WillByDefault(Return(syncer::SyncService::DisableReasonSet())); - ON_CALL(*mock_sync_service_->GetMockUserSettings(), IsSyncRequested()) - .WillByDefault(Return(true)); ON_CALL(*mock_sync_service_->GetMockUserSettings(), GetRegisteredSelectableTypes()) .WillByDefault(Return(GetAllTypes())); @@ -413,8 +411,6 @@ CreatePeopleHandler(); ON_CALL(*mock_sync_service_, GetDisableReasons()) .WillByDefault(Return(syncer::SyncService::DisableReasonSet())); - ON_CALL(*mock_sync_service_->GetMockUserSettings(), IsSyncRequested()) - .WillByDefault(Return(true)); ON_CALL(*mock_sync_service_->GetMockUserSettings(), IsFirstSetupComplete()) .WillByDefault(Return(false)); ON_CALL(*mock_sync_service_, GetTransportState()) @@ -450,8 +446,6 @@ .WillByDefault(Return(false)); ON_CALL(*mock_sync_service_, GetDisableReasons()) .WillByDefault(Return(syncer::SyncService::DisableReasonSet())); - ON_CALL(*mock_sync_service_->GetMockUserSettings(), IsSyncRequested()) - .WillByDefault(Return(true)); // Sync engine is stopped initially, and will start up. ON_CALL(*mock_sync_service_, GetTransportState()) .WillByDefault( @@ -490,8 +484,6 @@ CreatePeopleHandler(); ON_CALL(*mock_sync_service_, GetDisableReasons()) .WillByDefault(Return(syncer::SyncService::DisableReasonSet())); - ON_CALL(*mock_sync_service_->GetMockUserSettings(), IsSyncRequested()) - .WillByDefault(Return(true)); ON_CALL(*mock_sync_service_->GetMockUserSettings(), IsFirstSetupComplete()) .WillByDefault(Return(false)); EXPECT_CALL(*mock_sync_service_, GetTransportState()) @@ -533,8 +525,6 @@ // immediately starts initializing the engine. ON_CALL(*mock_sync_service_, GetDisableReasons()) .WillByDefault(Return(syncer::SyncService::DisableReasonSet())); - ON_CALL(*mock_sync_service_->GetMockUserSettings(), IsSyncRequested()) - .WillByDefault(Return(true)); ON_CALL(*mock_sync_service_, GetTransportState()) .WillByDefault( Return(syncer::SyncService::TransportState::INITIALIZING)); @@ -565,8 +555,6 @@ // engine is already running, it just gets reconfigured. ON_CALL(*mock_sync_service_, GetDisableReasons()) .WillByDefault(Return(syncer::SyncService::DisableReasonSet())); - ON_CALL(*mock_sync_service_->GetMockUserSettings(), IsSyncRequested()) - .WillByDefault(Return(true)); ON_CALL(*mock_sync_service_, GetTransportState()) .WillByDefault( Return(syncer::SyncService::TransportState::CONFIGURING)); @@ -1132,8 +1120,6 @@ // bits being cleared. ON_CALL(*mock_sync_service_, GetDisableReasons()) .WillByDefault(Return(syncer::SyncService::DISABLE_REASON_USER_CHOICE)); - ON_CALL(*mock_sync_service_->GetMockUserSettings(), IsSyncRequested()) - .WillByDefault(Return(false)); ON_CALL(*mock_sync_service_->GetMockUserSettings(), IsFirstSetupComplete()) .WillByDefault(Return(false)); // Sync will eventually start again in transport mode. @@ -1151,8 +1137,6 @@ // immediately starts initializing the engine. ON_CALL(*mock_sync_service_, GetDisableReasons()) .WillByDefault(Return(syncer::SyncService::DisableReasonSet())); - ON_CALL(*mock_sync_service_->GetMockUserSettings(), IsSyncRequested()) - .WillByDefault(Return(true)); ON_CALL(*mock_sync_service_, GetTransportState()) .WillByDefault( Return(syncer::SyncService::TransportState::INITIALIZING)); @@ -1186,8 +1170,6 @@ // bits being cleared. ON_CALL(*mock_sync_service_, GetDisableReasons()) .WillByDefault(Return(syncer::SyncService::DISABLE_REASON_USER_CHOICE)); - ON_CALL(*mock_sync_service_->GetMockUserSettings(), IsSyncRequested()) - .WillByDefault(Return(false)); ON_CALL(*mock_sync_service_->GetMockUserSettings(), IsFirstSetupComplete()) .WillByDefault(Return(false)); // Sync will eventually start again in transport mode. @@ -1218,8 +1200,6 @@ // immediately starts initializing the engine. ON_CALL(*mock_sync_service_, GetDisableReasons()) .WillByDefault(Return(syncer::SyncService::DisableReasonSet())); - ON_CALL(*mock_sync_service_->GetMockUserSettings(), IsSyncRequested()) - .WillByDefault(Return(true)); ON_CALL(*mock_sync_service_, GetTransportState()) .WillByDefault( Return(syncer::SyncService::TransportState::INITIALIZING));
diff --git a/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_command.cc b/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_command.cc index 7b60268..4d7be92 100644 --- a/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_command.cc +++ b/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_command.cc
@@ -76,20 +76,6 @@ return dest; } -std::unique_ptr<IsolatedWebAppResponseReaderFactory> -CreateDefaultResponseReaderFactory(content::BrowserContext& browser_context) { - Profile& profile = *Profile::FromBrowserContext(&browser_context); - PrefService& pref_service = *profile.GetPrefs(); - - auto trust_checker = - std::make_unique<IsolatedWebAppTrustChecker>(pref_service); - auto validator = - std::make_unique<IsolatedWebAppValidator>(std::move(trust_checker)); - - return std::make_unique<IsolatedWebAppResponseReaderFactory>( - std::move(validator)); -} - } // namespace InstallIsolatedWebAppCommand::InstallIsolatedWebAppCommand( @@ -97,25 +83,8 @@ const IsolatedWebAppLocation& location, std::unique_ptr<content::WebContents> web_contents, std::unique_ptr<WebAppUrlLoader> url_loader, - content::BrowserContext& browser_context, - base::OnceCallback<void(base::expected<InstallIsolatedWebAppCommandSuccess, - InstallIsolatedWebAppCommandError>)> - callback) - : InstallIsolatedWebAppCommand( - url_info, - location, - std::move(web_contents), - std::move(url_loader), - browser_context, - std::move(callback), - CreateDefaultResponseReaderFactory(browser_context)) {} - -InstallIsolatedWebAppCommand::InstallIsolatedWebAppCommand( - const IsolatedWebAppUrlInfo& url_info, - const IsolatedWebAppLocation& location, - std::unique_ptr<content::WebContents> web_contents, - std::unique_ptr<WebAppUrlLoader> url_loader, - content::BrowserContext& browser_context, + std::unique_ptr<ScopedKeepAlive> keep_alive, + std::unique_ptr<ScopedProfileKeepAlive> profile_keep_alive, base::OnceCallback<void(base::expected<InstallIsolatedWebAppCommandSuccess, InstallIsolatedWebAppCommandError>)> callback, @@ -129,12 +98,15 @@ response_reader_factory_(std::move(response_reader_factory)), web_contents_(std::move(web_contents)), url_loader_(std::move(url_loader)), - browser_context_(browser_context), + keep_alive_(std::move(keep_alive)), + profile_keep_alive_(std::move(profile_keep_alive)), data_retriever_(std::make_unique<WebAppDataRetriever>()) { DETACH_FROM_SEQUENCE(sequence_checker_); - DCHECK(web_contents_ != nullptr); - DCHECK(!callback.is_null()); + CHECK(web_contents_ != nullptr); + CHECK(!callback.is_null()); + CHECK(profile_keep_alive_ == nullptr || + &profile() == profile_keep_alive_->profile()); callback_ = base::BindOnce( @@ -146,6 +118,18 @@ .Then(std::move(callback)); } +// static +std::unique_ptr<IsolatedWebAppResponseReaderFactory> +InstallIsolatedWebAppCommand::CreateDefaultResponseReaderFactory( + const PrefService& prefs) { + auto trust_checker = std::make_unique<IsolatedWebAppTrustChecker>(prefs); + auto validator = + std::make_unique<IsolatedWebAppValidator>(std::move(trust_checker)); + + return std::make_unique<IsolatedWebAppResponseReaderFactory>( + std::move(validator)); +} + void InstallIsolatedWebAppCommand::SetDataRetrieverForTesting( std::unique_ptr<WebAppDataRetriever> data_retriever) { data_retriever_ = std::move(data_retriever); @@ -172,8 +156,6 @@ std::unique_ptr<AppLock> lock) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); lock_ = std::move(lock); - const PrefService& prefs = - *Profile::FromBrowserContext(&*browser_context_)->GetPrefs(); absl::visit( base::Overloaded{ @@ -185,7 +167,7 @@ [&](const DevModeBundle& location) { DCHECK_EQ(url_info_.web_bundle_id().type(), web_package::SignedWebBundleId::Type::kEd25519PublicKey); - if (!IsIwaDevModeEnabled(prefs)) { + if (!IsIwaDevModeEnabled(prefs())) { ReportFailure(kIwaDevModeNotEnabledMessage); return; } @@ -194,7 +176,7 @@ [&](const DevModeProxy& location) { DCHECK_EQ(url_info_.web_bundle_id().type(), web_package::SignedWebBundleId::Type::kDevelopment); - if (!IsIwaDevModeEnabled(prefs)) { + if (!IsIwaDevModeEnabled(prefs())) { ReportFailure(kIwaDevModeNotEnabledMessage); return; } @@ -251,9 +233,8 @@ } void InstallIsolatedWebAppCommand::CreateStoragePartition() { - browser_context_->GetStoragePartition( - url_info_.storage_partition_config(&*browser_context_), - /*can_create=*/true); + profile().GetStoragePartition(url_info_.storage_partition_config(&profile()), + /*can_create=*/true); } void InstallIsolatedWebAppCommand::LoadUrl() { @@ -478,4 +459,14 @@ InstallIsolatedWebAppCommandSuccess{})); } +Profile& InstallIsolatedWebAppCommand::profile() { + CHECK(web_contents_); + CHECK(web_contents_->GetBrowserContext()); + return *Profile::FromBrowserContext(web_contents_->GetBrowserContext()); +} + +const PrefService& InstallIsolatedWebAppCommand::prefs() { + return *profile().GetPrefs(); +} + } // namespace web_app
diff --git a/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_command.h b/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_command.h index 74ded11..b1e8292d 100644 --- a/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_command.h +++ b/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_command.h
@@ -16,6 +16,7 @@ #include "base/strings/string_piece_forward.h" #include "base/types/expected.h" #include "base/values.h" +#include "chrome/browser/profiles/keep_alive/scoped_profile_keep_alive.h" #include "chrome/browser/web_applications/commands/web_app_command.h" #include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_location.h" #include "chrome/browser/web_applications/isolated_web_apps/isolated_web_app_response_reader_factory.h" @@ -23,14 +24,16 @@ #include "chrome/browser/web_applications/os_integration/os_integration_manager.h" #include "chrome/browser/web_applications/web_app_id.h" #include "chrome/browser/web_applications/web_app_install_info.h" +#include "components/keep_alive_registry/scoped_keep_alive.h" #include "components/webapps/browser/install_result_code.h" #include "components/webapps/browser/installable/installable_logging.h" #include "third_party/blink/public/mojom/manifest/manifest.mojom-forward.h" class GURL; +class Profile; +class PrefService; namespace content { -class BrowserContext; class WebContents; } // namespace content @@ -64,35 +67,30 @@ // re-using web contents. class InstallIsolatedWebAppCommand : public WebAppCommandTemplate<AppLock> { public: - // - // |url_info| holds the origin information of the app. It is - // randomly generated for dev-proxy and the public key of signed bundle. It is + static std::unique_ptr<IsolatedWebAppResponseReaderFactory> + CreateDefaultResponseReaderFactory(const PrefService& prefs); + + // |url_info| holds the origin information of the app. It is randomly + // generated for dev-proxy and the public key of signed bundle. It is // guarantee to be valid. // - // |location| holds information about the - // mode(dev-mod-proxy/signed-bundle) and the source. + // |location| holds information about the mode(dev-mod-proxy/signed-bundle) + // and the source. // // |callback| must be not null. // // The `id` in the application's manifest must equal "/". + // + // `response_reader_factory` should be created via + // `CreateDefaultResponseReaderFactory` and is used to create the + // `IsolatedWebAppResponseReader` for the Web Bundle. explicit InstallIsolatedWebAppCommand( const IsolatedWebAppUrlInfo& url_info, const IsolatedWebAppLocation& location, std::unique_ptr<content::WebContents> web_contents, std::unique_ptr<WebAppUrlLoader> url_loader, - content::BrowserContext& browser_context, - base::OnceCallback< - void(base::expected<InstallIsolatedWebAppCommandSuccess, - InstallIsolatedWebAppCommandError>)> callback); - - // Same constructor as above, but additionally exposes the - // `response_reader_factory` for providing a mock factory in testing. - explicit InstallIsolatedWebAppCommand( - const IsolatedWebAppUrlInfo& url_info, - const IsolatedWebAppLocation& location, - std::unique_ptr<content::WebContents> web_contents, - std::unique_ptr<WebAppUrlLoader> url_loader, - content::BrowserContext& browser_context, + std::unique_ptr<ScopedKeepAlive> keep_alive, + std::unique_ptr<ScopedProfileKeepAlive> profile_keep_alive, base::OnceCallback< void(base::expected<InstallIsolatedWebAppCommandSuccess, InstallIsolatedWebAppCommandError>)> callback, @@ -123,6 +121,9 @@ void ReportFailure(base::StringPiece message); void ReportSuccess(); + Profile& profile(); + const PrefService& prefs(); + void DownloadIcons(WebAppInstallInfo install_info); void OnGetIcons(WebAppInstallInfo install_info, IconsDownloadedResult result, @@ -166,7 +167,8 @@ std::unique_ptr<WebAppUrlLoader> url_loader_; - base::raw_ref<content::BrowserContext> browser_context_; + std::unique_ptr<ScopedKeepAlive> keep_alive_; + std::unique_ptr<ScopedProfileKeepAlive> profile_keep_alive_; std::unique_ptr<WebAppDataRetriever> data_retriever_;
diff --git a/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_command_unittest.cc b/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_command_unittest.cc index 303b7577..6546433 100644 --- a/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_command_unittest.cc +++ b/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_command_unittest.cc
@@ -315,7 +315,8 @@ return std::make_unique<InstallIsolatedWebAppCommand>( url_info, location.value(), std::move(web_contents), - std::move(url_loader), *profile(), std::move(callback), + std::move(url_loader), /*keep_alive=*/nullptr, + /*profile_keep_alive=*/nullptr, std::move(callback), std::make_unique<FakeResponseReaderFactory>(std::move(bundle_error))); }
diff --git a/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_from_command_line.cc b/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_from_command_line.cc index 7ceaa8d..68f4b7c 100644 --- a/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_from_command_line.cc +++ b/chrome/browser/web_applications/isolated_web_apps/install_isolated_web_app_from_command_line.cc
@@ -105,8 +105,11 @@ return; } + // TODO(cmfcmf): Keep alives need to be set here. provider->scheduler().InstallIsolatedWebApp( - url_info.value(), location, base::BindOnce(&ReportInstallationResult)); + url_info.value(), location, /*keep_alive=*/nullptr, + /*profile_keep_alive=*/nullptr, + base::BindOnce(&ReportInstallationResult)); } base::expected<absl::optional<IsolatedWebAppLocation>, std::string>
diff --git a/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager.cc b/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager.cc index 754db423..5d26c268 100644 --- a/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager.cc +++ b/chrome/browser/web_applications/isolated_web_apps/policy/isolated_web_app_policy_manager.cc
@@ -51,8 +51,13 @@ const IsolatedWebAppLocation& location, const IsolatedWebAppUrlInfo& url_info, WebAppCommandScheduler::InstallIsolatedWebAppCallback callback) { - provider_->scheduler().InstallIsolatedWebApp(url_info, location, - std::move(callback)); + // There is no need to keep the browser or profile alive when + // policy-installing an IWA. If the browser or profile shut down, installation + // will be re-attempted the next time they start, assuming that the policy is + // still set. + provider_->scheduler().InstallIsolatedWebApp( + url_info, location, /*keep_alive=*/nullptr, + /*profile_keep_alive=*/nullptr, std::move(callback)); } IsolatedWebAppPolicyManager::IsolatedWebAppPolicyManager(
diff --git a/chrome/browser/web_applications/policy/web_app_policy_constants.cc b/chrome/browser/web_applications/policy/web_app_policy_constants.cc index 951613c..b3d79ce8 100644 --- a/chrome/browser/web_applications/policy/web_app_policy_constants.cc +++ b/chrome/browser/web_applications/policy/web_app_policy_constants.cc
@@ -26,5 +26,6 @@ const char kAllowed[] = "allowed"; const char kBlocked[] = "blocked"; const char kRunWindowed[] = "run_windowed"; +const char kPreventClose[] = "prevent_close"; } // namespace web_app
diff --git a/chrome/browser/web_applications/policy/web_app_policy_constants.h b/chrome/browser/web_applications/policy/web_app_policy_constants.h index 7126474..692c83da 100644 --- a/chrome/browser/web_applications/policy/web_app_policy_constants.h +++ b/chrome/browser/web_applications/policy/web_app_policy_constants.h
@@ -28,6 +28,7 @@ extern const char kAllowed[]; extern const char kBlocked[]; extern const char kRunWindowed[]; +extern const char kPreventClose[]; } // namespace web_app
diff --git a/chrome/browser/web_applications/policy/web_app_policy_manager.cc b/chrome/browser/web_applications/policy/web_app_policy_manager.cc index 6a7e415..1626fe0 100644 --- a/chrome/browser/web_applications/policy/web_app_policy_manager.cc +++ b/chrome/browser/web_applications/policy/web_app_policy_manager.cc
@@ -654,6 +654,26 @@ OverrideManifest(install_url, manifest); } +bool WebAppPolicyManager::IsPreventCloseEnabled(const AppId& app_id) const { +#if BUILDFLAG(IS_CHROMEOS) + if (!base::FeatureList::IsEnabled( + features::kDesktopPWAsEnforceWebAppSettingsPolicy) || + !base::FeatureList::IsEnabled(features::kDesktopPWAsPreventClose)) { + return false; + } + + const std::string unhashed_id = + app_registrar_->GetComputedUnhashedAppId(app_id); + auto it = settings_by_url_.find(unhashed_id); + if (it != settings_by_url_.end()) { + return it->second.prevent_close; + } + return default_settings_->prevent_close; +#else + return false; +#endif // BUILDFLAG(IS_CHROMEOS) +} + void WebAppPolicyManager::OnAppsSynchronized( std::map<GURL, ExternallyManagedAppManager::InstallResult> install_results, std::map<GURL, bool> uninstall_results) { @@ -693,11 +713,26 @@ } } +#if BUILDFLAG(IS_CHROMEOS) + // The value of "prevent_close" shall only be considered if run-on-os-login + // is enforced. + if (base::FeatureList::IsEnabled( + features::kDesktopPWAsEnforceWebAppSettingsPolicy) && + base::FeatureList::IsEnabled(features::kDesktopPWAsPreventClose) && + run_on_os_login_policy == RunOnOsLoginPolicy::kRunWindowed) { + absl::optional<bool> prevent_close_value = dict.FindBoolKey(kPreventClose); + if (prevent_close_value && *prevent_close_value) { + prevent_close = true; + } + } +#endif // BUILDFLAG(IS_CHROMEOS) + return true; } void WebAppPolicyManager::WebAppSetting::ResetSettings() { run_on_os_login_policy = RunOnOsLoginPolicy::kAllowed; + prevent_close = false; } WebAppPolicyManager::CustomManifestValues::CustomManifestValues() = default;
diff --git a/chrome/browser/web_applications/policy/web_app_policy_manager.h b/chrome/browser/web_applications/policy/web_app_policy_manager.h index 27418d18..ee8d083 100644 --- a/chrome/browser/web_applications/policy/web_app_policy_manager.h +++ b/chrome/browser/web_applications/policy/web_app_policy_manager.h
@@ -107,6 +107,8 @@ void MaybeOverrideManifest(content::RenderFrameHost* frame_host, blink::mojom::ManifestPtr& manifest) const; + bool IsPreventCloseEnabled(const AppId& app_id) const; + private: friend class WebAppPolicyManagerTest; @@ -120,6 +122,7 @@ void ResetSettings(); RunOnOsLoginPolicy run_on_os_login_policy; + bool prevent_close; }; struct CustomManifestValues {
diff --git a/chrome/browser/web_applications/policy/web_app_policy_manager_unittest.cc b/chrome/browser/web_applications/policy/web_app_policy_manager_unittest.cc index cc8b071f..56960d7 100644 --- a/chrome/browser/web_applications/policy/web_app_policy_manager_unittest.cc +++ b/chrome/browser/web_applications/policy/web_app_policy_manager_unittest.cc
@@ -313,6 +313,7 @@ struct TestParam { TestLacrosParam lacros_params; test::ExternalPrefMigrationTestCases pref_migration_test_param; + bool prevent_close_enabled; }; class WebAppPolicyManagerTest : public ChromeRenderViewHostTestHarness, @@ -453,9 +454,24 @@ features::kUseWebAppDBInsteadOfExternalPrefs); break; } + + if (GetParam().prevent_close_enabled) { + enabled_features.push_back( + features::kDesktopPWAsEnforceWebAppSettingsPolicy); + enabled_features.push_back(features::kDesktopPWAsPreventClose); + } + scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features); } + void InstallPwa(const std::string& url) { + std::unique_ptr<WebAppInstallInfo> web_app_info = + std::make_unique<WebAppInstallInfo>(); + web_app_info->start_url = GURL(url); + web_app_info->manifest_id = ""; + web_app::test::InstallWebApp(profile(), std::move(web_app_info)); + } + bool ShouldSkipPWASpecificTest() { #if BUILDFLAG(IS_CHROMEOS_ASH) if (GetParam().lacros_params == TestLacrosParam::kLacrosEnabled) { @@ -521,6 +537,11 @@ unhashed_app_id); } + bool IsPreventCloseEnabled(const std::string& unhashed_app_id) { + return policy_manager().IsPreventCloseEnabled( + web_app::GenerateAppIdFromUnhashed(unhashed_app_id)); + } + void WaitForAppsToSynchronize() { base::RunLoop loop; policy_manager().SetOnAppsSynchronizedCompletedCallbackForTesting( @@ -1391,6 +1412,61 @@ app_registrar().RemoveObserver(&mock_observer); } +TEST_P(WebAppPolicyManagerTest, WebAppSettingsPreventClose) { + if (ShouldSkipPWASpecificTest()) { + return; + } + const char kWebAppSettingNoDefaultConfiguration[] = R"([ + { + "manifest_id": "https://windowed.example/", + "run_on_os_login": "run_windowed", + "prevent_close": true + }, + { + "manifest_id": "https://tabbed.example/", + "run_on_os_login": "blocked", + "prevent_close": true + }, + { + "manifest_id": "https://no-container.example/", + "run_on_os_login": "unsupported_value", + "prevent_close": true + }, + { + "manifest_id": "bad.uri", + "run_on_os_login": "allowed", + "prevent_close": true + } + ])"; + + // Make sure that WebAppRegistrar::GetComputedUnhashedAppId does not fail. + InstallPwa(kWindowedUrl); + InstallPwa(kTabbedUrl); + InstallPwa(kNoContainerUrl); + + base::RunLoop loop; + policy_manager().SetRefreshPolicySettingsCompletedCallbackForTesting( + loop.QuitClosure()); + SetWebAppSettingsListPref(kWebAppSettingNoDefaultConfiguration); + loop.Run(); + +#if BUILDFLAG(IS_CHROMEOS) + if (GetParam().prevent_close_enabled) { + EXPECT_TRUE(IsPreventCloseEnabled(kWindowedUrl)); + EXPECT_FALSE(IsPreventCloseEnabled(kTabbedUrl)); + EXPECT_FALSE(IsPreventCloseEnabled(kNoContainerUrl)); + } else { + EXPECT_FALSE(IsPreventCloseEnabled(kWindowedUrl)); + EXPECT_FALSE(IsPreventCloseEnabled(kTabbedUrl)); + EXPECT_FALSE(IsPreventCloseEnabled(kNoContainerUrl)); + } +#else + EXPECT_FALSE(IsPreventCloseEnabled(kWindowedUrl)); + EXPECT_FALSE(IsPreventCloseEnabled(kTabbedUrl)); + EXPECT_FALSE(IsPreventCloseEnabled(kNoContainerUrl)); +#endif // BUILDFLAG(IS_CHROMEOS) +} + INSTANTIATE_TEST_SUITE_P( WebAppPolicyManagerTestWithParams, WebAppPolicyManagerTest, @@ -1398,29 +1474,67 @@ #if BUILDFLAG(IS_CHROMEOS_ASH) TestParam( {TestLacrosParam::kLacrosDisabled, - test::ExternalPrefMigrationTestCases::kDisableMigrationReadPref}), + test::ExternalPrefMigrationTestCases::kDisableMigrationReadPref, + /*prevent_close_enabled=*/false}), TestParam( {TestLacrosParam::kLacrosDisabled, - test::ExternalPrefMigrationTestCases::kDisableMigrationReadDB}), + test::ExternalPrefMigrationTestCases::kDisableMigrationReadDB, + /*prevent_close_enabled=*/false}), TestParam( {TestLacrosParam::kLacrosDisabled, - test::ExternalPrefMigrationTestCases::kEnableMigrationReadPref}), - TestParam( - {TestLacrosParam::kLacrosDisabled, - test::ExternalPrefMigrationTestCases::kEnableMigrationReadDB}), + test::ExternalPrefMigrationTestCases::kEnableMigrationReadPref, + /*prevent_close_enabled=*/false}), + TestParam({TestLacrosParam::kLacrosDisabled, + test::ExternalPrefMigrationTestCases::kEnableMigrationReadDB, + /*prevent_close_enabled=*/false}), #endif // BUILDFLAG(IS_CHROMEOS_ASH) TestParam( {TestLacrosParam::kLacrosEnabled, - test::ExternalPrefMigrationTestCases::kDisableMigrationReadPref}), + test::ExternalPrefMigrationTestCases::kDisableMigrationReadPref, + /*prevent_close_enabled=*/false}), TestParam( {TestLacrosParam::kLacrosEnabled, - test::ExternalPrefMigrationTestCases::kDisableMigrationReadDB}), + test::ExternalPrefMigrationTestCases::kDisableMigrationReadDB, + /*prevent_close_enabled=*/false}), TestParam( {TestLacrosParam::kLacrosEnabled, - test::ExternalPrefMigrationTestCases::kEnableMigrationReadPref}), + test::ExternalPrefMigrationTestCases::kEnableMigrationReadPref, + /*prevent_close_enabled=*/false}), + TestParam({TestLacrosParam::kLacrosEnabled, + test::ExternalPrefMigrationTestCases::kEnableMigrationReadDB, + /*prevent_close_enabled=*/false}), +#if BUILDFLAG(IS_CHROMEOS_ASH) + TestParam( + {TestLacrosParam::kLacrosDisabled, + test::ExternalPrefMigrationTestCases::kDisableMigrationReadPref, + /*prevent_close_enabled=*/true}), + TestParam( + {TestLacrosParam::kLacrosDisabled, + test::ExternalPrefMigrationTestCases::kDisableMigrationReadDB, + /*prevent_close_enabled=*/true}), + TestParam( + {TestLacrosParam::kLacrosDisabled, + test::ExternalPrefMigrationTestCases::kEnableMigrationReadPref, + /*prevent_close_enabled=*/true}), + TestParam({TestLacrosParam::kLacrosDisabled, + test::ExternalPrefMigrationTestCases::kEnableMigrationReadDB, + /*prevent_close_enabled=*/true}), +#endif // BUILDFLAG(IS_CHROMEOS_ASH) TestParam( {TestLacrosParam::kLacrosEnabled, - test::ExternalPrefMigrationTestCases::kEnableMigrationReadDB})), + test::ExternalPrefMigrationTestCases::kDisableMigrationReadPref, + /*prevent_close_enabled=*/true}), + TestParam( + {TestLacrosParam::kLacrosEnabled, + test::ExternalPrefMigrationTestCases::kDisableMigrationReadDB, + /*prevent_close_enabled=*/true}), + TestParam( + {TestLacrosParam::kLacrosEnabled, + test::ExternalPrefMigrationTestCases::kEnableMigrationReadPref, + /*prevent_close_enabled=*/true}), + TestParam({TestLacrosParam::kLacrosEnabled, + test::ExternalPrefMigrationTestCases::kEnableMigrationReadDB, + /*prevent_close_enabled=*/true})), [](const ::testing::TestParamInfo<TestParam>& info) { std::string test_name = "Test_"; if (info.param.lacros_params == TestLacrosParam::kLacrosEnabled) @@ -1442,6 +1556,12 @@ test_name.append("EnableMigration_ReadFromDB"); break; } + + if (info.param.prevent_close_enabled) { + test_name.append("PreventCloseEnabled"); + } else { + test_name.append("PreventCloseDisabled"); + } return test_name; });
diff --git a/chrome/browser/web_applications/web_app_command_scheduler.cc b/chrome/browser/web_applications/web_app_command_scheduler.cc index 3815a1fe..5c8f245 100644 --- a/chrome/browser/web_applications/web_app_command_scheduler.cc +++ b/chrome/browser/web_applications/web_app_command_scheduler.cc
@@ -314,8 +314,13 @@ void WebAppCommandScheduler::InstallIsolatedWebApp( const IsolatedWebAppUrlInfo& url_info, const IsolatedWebAppLocation& location, + std::unique_ptr<ScopedKeepAlive> keep_alive, + std::unique_ptr<ScopedProfileKeepAlive> profile_keep_alive, InstallIsolatedWebAppCallback callback, const base::Location& call_location) { + DCHECK(profile_keep_alive == nullptr || + profile_keep_alive->profile() == &*profile_); + if (IsShuttingDown()) { InstallIsolatedWebAppCommandError error; error.message = "The profile and/or browser are shutting down."; @@ -328,7 +333,10 @@ provider_->command_manager().ScheduleCommand( std::make_unique<InstallIsolatedWebAppCommand>( url_info, location, CreateIsolatedWebAppWebContents(*profile_), - std::make_unique<WebAppUrlLoader>(), *profile_, std::move(callback)), + std::make_unique<WebAppUrlLoader>(), std::move(keep_alive), + std::move(profile_keep_alive), std::move(callback), + InstallIsolatedWebAppCommand::CreateDefaultResponseReaderFactory( + *profile_->GetPrefs())), call_location); }
diff --git a/chrome/browser/web_applications/web_app_command_scheduler.h b/chrome/browser/web_applications/web_app_command_scheduler.h index 646e43d..d88fcc9 100644 --- a/chrome/browser/web_applications/web_app_command_scheduler.h +++ b/chrome/browser/web_applications/web_app_command_scheduler.h
@@ -161,10 +161,13 @@ // Schedules a command that installs the Isolated Web App described by the // given IsolatedWebAppUrlInfo and IsolationData. - void InstallIsolatedWebApp(const IsolatedWebAppUrlInfo& url_info, - const IsolatedWebAppLocation& location, - InstallIsolatedWebAppCallback callback, - const base::Location& call_location = FROM_HERE); + void InstallIsolatedWebApp( + const IsolatedWebAppUrlInfo& url_info, + const IsolatedWebAppLocation& location, + std::unique_ptr<ScopedKeepAlive> keep_alive, + std::unique_ptr<ScopedProfileKeepAlive> profile_keep_alive, + InstallIsolatedWebAppCallback callback, + const base::Location& call_location = FROM_HERE); // Computes the browsing data size of all installed Isolated Web Apps. void GetIsolatedWebAppBrowsingData(
diff --git a/chrome/build/lacros64.pgo.txt b/chrome/build/lacros64.pgo.txt index 377aa9c..e8b6f403 100644 --- a/chrome/build/lacros64.pgo.txt +++ b/chrome/build/lacros64.pgo.txt
@@ -1 +1 @@ -chrome-chromeos-amd64-generic-main-1682048921-eeb52421ccc94b14aae3c77c91272ec82f749451.profdata +chrome-chromeos-amd64-generic-main-1682078092-766d36b3038d584ad4f93080b116939d7457f2da.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt index 90158d7..7fa39581 100644 --- a/chrome/build/mac-arm.pgo.txt +++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@ -chrome-mac-arm-main-1682048921-b0abc953971a32f4c19d1286dfd19690a33d1656.profdata +chrome-mac-arm-main-1682070945-4986223444ad51e35b587ce493edebba537cc33d.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt index 44567d6..7bcd943e 100644 --- a/chrome/build/mac.pgo.txt +++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@ -chrome-mac-main-1682034798-ee55d11b73f9a8fbf3196585be3c3fd21047507a.profdata +chrome-mac-main-1682056724-4d44d1e17fd3ccff93441b862dc627e46af0dd5f.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt index 264ca88..5bb9536a 100644 --- a/chrome/build/win32.pgo.txt +++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@ -chrome-win32-main-1682045784-8ab28d72f980bfa2955d71ec4bb020a2265861d8.profdata +chrome-win32-main-1682067136-9335a3b3dcbdf943ef113711516cd79a2a4330ae.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt index dd3ac82..906fb09 100644 --- a/chrome/build/win64.pgo.txt +++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@ -chrome-win64-main-1682045784-fe4aa10d2ec84b46d53d21afb8d4b9a1faf5a06e.profdata +chrome-win64-main-1682067136-60e9e14529c0fdb21e7529dc476fd176458a1686.profdata
diff --git a/chrome/chrome_cleaner/settings/settings.cc b/chrome/chrome_cleaner/settings/settings.cc index 7047746..bacd7de 100644 --- a/chrome/chrome_cleaner/settings/settings.cc +++ b/chrome/chrome_cleaner/settings/settings.cc
@@ -10,10 +10,10 @@ #include "base/command_line.h" #include "base/containers/cxx20_erase.h" -#include "base/guid.h" #include "base/logging.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" +#include "base/uuid.h" #include "base/win/win_util.h" #include "chrome/chrome_cleaner/buildflags.h" #include "chrome/chrome_cleaner/constants/chrome_cleaner_switches.h" @@ -121,7 +121,7 @@ std::string GetCleanerRunId(const base::CommandLine& command_line) { std::string cleanup_id = command_line.GetSwitchValueASCII(kCleanupIdSwitch); if (cleanup_id.empty()) { - cleanup_id = base::GenerateGUID(); + cleanup_id = base::Uuid::GenerateRandomV4().AsLowercaseString(); DCHECK(!cleanup_id.empty()); } return cleanup_id;
diff --git a/chrome/chrome_cleaner/test/test_native_reg_util.cc b/chrome/chrome_cleaner/test/test_native_reg_util.cc index 8fc1459..4cf62bd 100644 --- a/chrome/chrome_cleaner/test/test_native_reg_util.cc +++ b/chrome/chrome_cleaner/test/test_native_reg_util.cc
@@ -6,12 +6,12 @@ #include <string> -#include "base/guid.h" #include "base/strings/strcat.h" #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" #include "base/test/test_reg_util_win.h" #include "base/time/time.h" +#include "base/uuid.h" #include "base/win/win_util.h" #include "chrome/chrome_cleaner/os/registry.h" @@ -30,7 +30,8 @@ base::Time::Now().ToDeltaSinceWindowsEpoch(); key_path_ = base::StrCat( {kTempTestKeyPath, base::NumberToWString(timestamp.InMicroseconds()), - kTimestampDelimiter, base::ASCIIToWide(base::GenerateGUID())}); + kTimestampDelimiter, + base::ASCIIToWide(base::Uuid::GenerateRandomV4().AsLowercaseString())}); CHECK(ERROR_SUCCESS == reg_key_.Create(HKEY_LOCAL_MACHINE, key_path_.c_str(), KEY_ALL_ACCESS));
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc index 532516b..6c2a44a 100644 --- a/chrome/common/pref_names.cc +++ b/chrome/common/pref_names.cc
@@ -2903,10 +2903,6 @@ const char kBrowserShowProfilePickerOnStartup[] = "profile.show_picker_on_startup"; -// Boolean which indicates if the user is allowed to sign into Chrome on the -// next startup. -const char kSigninAllowedOnNextStartup[] = "signin.allowed_on_next_startup"; - // Boolean which indicate if signin interception is enabled. const char kSigninInterceptionEnabled[] = "signin.interception_enabled";
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h index 319dcae3..931cd84 100644 --- a/chrome/common/pref_names.h +++ b/chrome/common/pref_names.h
@@ -973,7 +973,6 @@ extern const char kBrowserProfilePickerAvailabilityOnStartup[]; extern const char kBrowserProfilePickerShown[]; extern const char kBrowserShowProfilePickerOnStartup[]; -extern const char kSigninAllowedOnNextStartup[]; extern const char kSigninInterceptionEnabled[]; #if BUILDFLAG(IS_CHROMEOS) extern const char kEchoCheckedOffers[];
diff --git a/chrome/test/data/webui/password_manager/passwords_importer_test.ts b/chrome/test/data/webui/password_manager/passwords_importer_test.ts index 659adadd..9250126 100644 --- a/chrome/test/data/webui/password_manager/passwords_importer_test.ts +++ b/chrome/test/data/webui/password_manager/passwords_importer_test.ts
@@ -59,18 +59,23 @@ assertEquals(expectedText, element?.textContent!.trim()); } -function assertButtonShouldCloseDialog( - importer: PasswordsImporterElement, dialog: HTMLElement, selector: string) { +async function closeDialogHelper( + importer: PasswordsImporterElement, + passwordManager: TestPasswordManagerProxy, dialog: HTMLElement, + selector: string, shouldDeleteFile: boolean = false) { const button = dialog.querySelector<HTMLElement>(selector); assertTrue(!!button); - // Should close the dialog. + // Should close the dialog and trigger 'passwordsPrivate.resetImporter'. button.click(); - flush(); + const deleteFile = await passwordManager.whenCalled('resetImporter'); + assertEquals(shouldDeleteFile, deleteFile); + await flushTasks(); assertFalse(!!importer.shadowRoot!.querySelector<CrDialogElement>('#dialog')); } async function assertErrorStateAndClose( - importer: PasswordsImporterElement, expectedDescription: string) { + importer: PasswordsImporterElement, + passwordManager: TestPasswordManagerProxy, expectedDescription: string) { const dialog = importer.shadowRoot!.querySelector<CrDialogElement>('#dialog'); assertTrue(!!dialog); assertTrue(dialog.open); @@ -86,7 +91,7 @@ dialog, '#selectFileButton', importer.i18n('selectFile')); assertVisibleTextContent(dialog, '#closeButton', importer.i18n('close')); - assertButtonShouldCloseDialog(importer, dialog, '#closeButton'); + await closeDialogHelper(importer, passwordManager, dialog, '#closeButton'); } suite('PasswordsImporterTest', function() { @@ -140,7 +145,7 @@ await triggerImportHelper(importer, passwordManager); }); - test('store picker dialog has correct state', function() { + test('store picker dialog has correct state', async function() { const importer = createPasswordsImporter( /*isUserSyncingPasswords=*/ false, /*isAccountStoreUser=*/ true, /*accountEmail=*/ 'test@test.com'); @@ -164,7 +169,7 @@ dialog, '#selectFileButton', importer.i18n('selectFile')); assertVisibleTextContent(dialog, '#cancelButton', importer.i18n('cancel')); - assertButtonShouldCloseDialog(importer, dialog, '#cancelButton'); + await closeDialogHelper(importer, passwordManager, dialog, '#cancelButton'); }); test('account store user can import passwords to device', async function() { @@ -199,7 +204,8 @@ await triggerImportHelper(importer, passwordManager, expectedStore); }); - test('has correct success state with no errors', async function() { + test('M1: has correct success state with no errors', async function() { + loadTimeData.overrideValues({enablePasswordsImportM2: false}); const importer = createPasswordsImporter(); passwordManager.setImportResults({ status: chrome.passwordsPrivate.ImportResultsStatus.SUCCESS, @@ -221,6 +227,9 @@ dialog, '#title', importer.i18n('importPasswordsSuccessTitle')); assertTrue(isChildVisible(dialog, '#tipBox', /*checkLightDom=*/ true)); + + assertFalse( + isChildVisible(dialog, '#deleteFileOption', /*checkLightDom=*/ true)); assertFalse( isChildVisible(dialog, '#failuresSummary', /*checkLightDom=*/ true)); @@ -236,9 +245,117 @@ assertVisibleTextContent(dialog, '#closeButton', importer.i18n('close')); - assertButtonShouldCloseDialog(importer, dialog, '#closeButton'); + await closeDialogHelper(importer, passwordManager, dialog, '#closeButton'); }); + test('M2: has correct success state with no errors', async function() { + loadTimeData.overrideValues({enablePasswordsImportM2: true}); + const importer = createPasswordsImporter(); + passwordManager.setImportResults({ + status: chrome.passwordsPrivate.ImportResultsStatus.SUCCESS, + numberImported: 42, + displayedEntries: [], + fileName: 'test.csv', + }); + + await triggerImportHelper(importer, passwordManager); + await pluralString.whenCalled('getPluralString'); + await flushTasks(); + + const dialog = + importer.shadowRoot!.querySelector<CrDialogElement>('#dialog'); + assertTrue(!!dialog); + assertTrue(dialog.open); + + assertVisibleTextContent( + dialog, '#title', importer.i18n('importPasswordsSuccessTitle')); + + assertTrue( + isChildVisible(dialog, '#deleteFileOption', /*checkLightDom=*/ true)); + + assertFalse(isChildVisible(dialog, '#tipBox', /*checkLightDom=*/ true)); + assertFalse( + isChildVisible(dialog, '#failuresSummary', /*checkLightDom=*/ true)); + + const deleteFileOption = dialog.querySelector('#deleteFileOption'); + assertTrue(!!deleteFileOption); + assertEquals( + deleteFileOption.innerHTML.toString(), + importer + .i18nAdvanced( + 'importPasswordsDeleteFileOption', + {attrs: ['class'], substitutions: ['test.csv']}) + .toString()); + + assertVisibleTextContent(dialog, '#closeButton', importer.i18n('close')); + + await closeDialogHelper(importer, passwordManager, dialog, '#closeButton'); + }); + + test( + 'M2: close button triggers file deletion with ticked checkbox', + async function() { + loadTimeData.overrideValues({enablePasswordsImportM2: true}); + const importer = createPasswordsImporter(); + passwordManager.setImportResults({ + status: chrome.passwordsPrivate.ImportResultsStatus.SUCCESS, + numberImported: 42, + displayedEntries: [], + fileName: 'test.csv', + }); + + await triggerImportHelper(importer, passwordManager); + await pluralString.whenCalled('getPluralString'); + await flushTasks(); + + const dialog = + importer.shadowRoot!.querySelector<CrDialogElement>('#dialog'); + assertTrue(!!dialog); + assertTrue(dialog.open); + + const deleteFileOption = + dialog.querySelector<HTMLElement>('#deleteFileOption'); + assertTrue(!!deleteFileOption); + deleteFileOption.click(); + flush(); + + await closeDialogHelper( + importer, passwordManager, dialog, '#closeButton', + /*shouldDeleteFile=*/ true); + }); + + test( + 'M2: view passwords triggers file deletion with ticked checkbox', + async function() { + loadTimeData.overrideValues({enablePasswordsImportM2: true}); + const importer = createPasswordsImporter(); + passwordManager.setImportResults({ + status: chrome.passwordsPrivate.ImportResultsStatus.SUCCESS, + numberImported: 42, + displayedEntries: [], + fileName: 'test.csv', + }); + + await triggerImportHelper(importer, passwordManager); + await pluralString.whenCalled('getPluralString'); + await flushTasks(); + + const dialog = + importer.shadowRoot!.querySelector<CrDialogElement>('#dialog'); + assertTrue(!!dialog); + assertTrue(dialog.open); + + const deleteFileOption = + dialog.querySelector<HTMLElement>('#deleteFileOption'); + assertTrue(!!deleteFileOption); + deleteFileOption.click(); + flush(); + + await closeDialogHelper( + importer, passwordManager, dialog, '#viewPasswordsButton', + /*shouldDeleteFile=*/ true); + }); + test('view passwords navigates to the passwords page', async function() { const importer = createPasswordsImporter(); @@ -260,14 +377,9 @@ assertVisibleTextContent( dialog, '#viewPasswordsButton', importer.i18n('viewPasswordsButton')); - const button = dialog.querySelector<HTMLElement>('#viewPasswordsButton'); - assertTrue(!!button); // Should close the dialog and navigate to PASSWORDS page. - button.click(); - flush(); - assertFalse( - !!importer.shadowRoot!.querySelector<CrDialogElement>('#dialog')); - await flushTasks(); + await closeDialogHelper( + importer, passwordManager, dialog, '#viewPasswordsButton'); assertEquals(Page.PASSWORDS, Router.getInstance().currentRoute.page); }); @@ -365,13 +477,15 @@ // Success tip should not be visible. assertFalse(isChildVisible(dialog, '#tipBox', /*checkLightDom=*/ true)); + assertFalse( + isChildVisible(dialog, '#deleteFileOption', /*checkLightDom=*/ true)); assertTrue( isChildVisible(dialog, '#failuresSummary', /*checkLightDom=*/ true)); assertVisibleTextContent(dialog, '#closeButton', importer.i18n('close')); - assertButtonShouldCloseDialog(importer, dialog, '#closeButton'); + await closeDialogHelper(importer, passwordManager, dialog, '#closeButton'); }); test('bad format error dialog is correct', async function() { @@ -385,7 +499,7 @@ await triggerImportHelper(importer, passwordManager); await assertErrorStateAndClose( - importer, + importer, passwordManager, importer .i18nAdvanced( 'importPasswordsBadFormatError', @@ -411,7 +525,7 @@ await triggerImportHelper(importer, passwordManager); await assertErrorStateAndClose( - importer, + importer, passwordManager, importer.i18nAdvanced('importPasswordsUnknownError').toString()); }); @@ -427,7 +541,7 @@ await triggerImportHelper(importer, passwordManager); await assertErrorStateAndClose( - importer, + importer, passwordManager, importer.i18nAdvanced('importPasswordsLimitExceeded').toString()); }); @@ -442,7 +556,7 @@ await triggerImportHelper(importer, passwordManager); await assertErrorStateAndClose( - importer, + importer, passwordManager, importer.i18nAdvanced('importPasswordsFileSizeExceeded').toString()); }); @@ -468,6 +582,6 @@ dialog, '#description', importer.i18n('importPasswordsAlreadyActive')); assertVisibleTextContent(dialog, '#closeButton', importer.i18n('close')); - assertButtonShouldCloseDialog(importer, dialog, '#closeButton'); + await closeDialogHelper(importer, passwordManager, dialog, '#closeButton'); }); });
diff --git a/chrome/test/data/webui/password_manager/test_password_manager_proxy.ts b/chrome/test/data/webui/password_manager/test_password_manager_proxy.ts index ae8b7530..618ec34 100644 --- a/chrome/test/data/webui/password_manager/test_password_manager_proxy.ts +++ b/chrome/test/data/webui/password_manager/test_password_manager_proxy.ts
@@ -71,6 +71,7 @@ 'recordPasswordViewInteraction', 'removeBlockedSite', 'removeSavedPassword', + 'resetImporter', 'requestCredentialsDetails', 'requestExportProgressStatus', 'requestPlaintextPassword', @@ -328,6 +329,11 @@ return Promise.resolve(this.importResults_); } + resetImporter(deleteFile: boolean) { + this.methodCalled('resetImporter', deleteFile); + return Promise.resolve(); + } + isOptedInForAccountStorage() { this.methodCalled('isOptedInForAccountStorage'); return Promise.resolve(this.data.isOptedInAccountStorage);
diff --git a/chrome/test/data/webui/settings/chromeos/BUILD.gn b/chrome/test/data/webui/settings/chromeos/BUILD.gn index 306d3c3..01f51a2 100644 --- a/chrome/test/data/webui/settings/chromeos/BUILD.gn +++ b/chrome/test/data/webui/settings/chromeos/BUILD.gn
@@ -96,6 +96,7 @@ "os_settings_page_test.js", "os_settings_search_box_test.js", "os_sync_controls_subpage_test.js", + "passpoint_subpage_test.js", "people_page_account_manager_subpage_test.js", "personalization_page_with_personalization_hub_test.js", "privacy_hub_subpage_tests.js",
diff --git a/chrome/test/data/webui/settings/chromeos/internet_page_tests.js b/chrome/test/data/webui/settings/chromeos/internet_page_tests.js index 19f5ac4c..2e497b0 100644 --- a/chrome/test/data/webui/settings/chromeos/internet_page_tests.js +++ b/chrome/test/data/webui/settings/chromeos/internet_page_tests.js
@@ -882,10 +882,20 @@ }); test('Nagivate to Passpoint detail page', async () => { + const subId = 'a_passpoint_id'; + const sub = { + id: subId, + domains: ['passpoint.example.com'], + friendlyName: 'Passpoint Example Ltd.', + provisioningSource: 'app.passpoint.example.com', + trustedCa: '', + expirationEpochMs: 0n, + }; + passpointService_.addSubscription(sub); await init(); const params = new URLSearchParams(); - params.append('id', 'a_passpoint_id'); + params.append('id', subId); // Navigate straight to Passpoint detail subpage. Router.getInstance().navigateTo(routes.PASSPOINT_DETAIL, params);
diff --git a/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js b/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js index 40efa91..6ae1da4 100644 --- a/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js +++ b/chrome/test/data/webui/settings/chromeos/os_settings_v3_browsertest.js
@@ -528,6 +528,14 @@ 'ParentalControlsPage', 'parental_controls_page/parental_controls_page_test.js' ], + [ + 'PasspointSubpage', 'passpoint_subpage_test.js', { + enabled: [ + 'ash::features::kPasspointARCSupport', + 'ash::features::kPasspointSettings', + ] + } + ], ['PeoplePage', 'os_people_page_test.js'], [ 'PeoplePageAccountManagerSubpage',
diff --git a/chrome/test/data/webui/settings/chromeos/passpoint_subpage_test.js b/chrome/test/data/webui/settings/chromeos/passpoint_subpage_test.js new file mode 100644 index 0000000..29dd04a --- /dev/null +++ b/chrome/test/data/webui/settings/chromeos/passpoint_subpage_test.js
@@ -0,0 +1,273 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'chrome://os-settings/chromeos/lazy_load.js'; + +import {Router, routes} from 'chrome://os-settings/chromeos/os_settings.js'; +import {assert} from 'chrome://resources/ash/common/assert.js'; +import {MojoConnectivityProvider} from 'chrome://resources/ash/common/connectivity/mojo_connectivity_provider.js'; +import {loadTimeData} from 'chrome://resources/ash/common/load_time_data.m.js'; +import {MojoInterfaceProviderImpl} from 'chrome://resources/ash/common/network/mojo_interface_provider.js'; +import {AppType} from 'chrome://resources/cr_components/app_management/app_management.mojom-webui.js'; +import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js'; +import {FakeNetworkConfig} from 'chrome://webui-test/chromeos/fake_network_config_mojom.js'; +import {FakePasspointService} from 'chrome://webui-test/chromeos/fake_passpoint_service_mojom.js'; +import {waitAfterNextRender} from 'chrome://webui-test/polymer_test_util.js'; + +import {setupFakeHandler} from './app_management/test_util.js'; + +suite('PasspointSubpage', () => { + /** @type {?FakeNetworkConfig} */ + let networkConfigApi_ = null; + /** @type {?FakePasspointService} */ + let passpointServiceApi_ = null; + /** @type {?FakePageHandler} */ + let fakeHandler = null; + /** @type {?SettingPasspointSubpageElement} */ + let passpointSubpage_ = null; + + const kCaHash = 'CAHASH'; + const kCaPem = 'test-pem'; + const kCaCN = 'Passpoint Example Certificate Authority'; + + /** + * @return {!Promise<unknown>} + * @private + */ + function flushAsync() { + flush(); + // Use setTimeout to wait for the next macrotask. + return new Promise(resolve => setTimeout(resolve)); + } + + /** + * @param {!PasspointSubscription} sub + * @return {!Promise<unknown>} + */ + async function init(sub) { + const serverCas = []; + serverCas.push({ + hash: kCaHash, + pemOrId: kCaPem, + issuedTo: kCaCN, + }); + networkConfigApi_.setCertificatesForTest(serverCas, []); + passpointServiceApi_.addSubscription(sub); + await waitAfterNextRender(passpointSubpage_); + + const params = new URLSearchParams(); + params.append('id', sub.id); + Router.getInstance().navigateTo(routes.PASSPOINT_DETAIL, params); + return flushAsync(); + } + + function getDomainsListItems() { + const domains = + passpointSubpage_.shadowRoot.querySelector('#passpointDomainsList'); + assertTrue(!!domains); + return domains.querySelectorAll('div.list-item'); + } + + function getExpirationDateItem() { + return passpointSubpage_.shadowRoot.querySelector( + '#passpointExpirationDate'); + } + + function getSourceText() { + const div = + passpointSubpage_.shadowRoot.querySelector('#passpointSourceText'); + assertTrue(!!div); + return div.textContent.trim(); + } + + function getCertificateName() { + const div = + passpointSubpage_.shadowRoot.querySelector('#passpointCertificateName'); + assertTrue(!!div); + return div.textContent.trim(); + } + + function getElement(id) { + const element = passpointSubpage_.shadowRoot.querySelector(`#${id}`); + assertTrue(!!element); + return element; + } + + function getRemovalDialog() { + return passpointSubpage_.shadowRoot.querySelector('#removalDialog'); + } + + suiteSetup(() => { + loadTimeData.overrideValues({ + isPasspointEnabled: true, + isPasspointSettingsEnabled: true, + }); + networkConfigApi_ = new FakeNetworkConfig(); + MojoInterfaceProviderImpl.getInstance().remote_ = networkConfigApi_; + passpointServiceApi_ = new FakePasspointService(); + MojoConnectivityProvider.getInstance().setPasspointServiceForTest( + passpointServiceApi_); + fakeHandler = setupFakeHandler(); + + // Disable animations so sub-pages open within one event loop. + testing.Test.disableAnimationsAndTransitions(); + }); + + setup(async () => { + PolymerTest.clearBody(); + passpointSubpage_ = document.createElement('settings-passpoint-subpage'); + assert(passpointSubpage_); + + networkConfigApi_.resetForTest(); + passpointServiceApi_.resetForTest(); + + document.body.appendChild(passpointSubpage_); + await waitAfterNextRender(passpointSubpage_); + }); + + teardown(function() { + passpointSubpage_.remove(); + passpointSubpage_ = null; + Router.getInstance().resetRouteForTesting(); + }); + + test('Show Passpoint subscription', async () => { + const sub = { + id: 'sub-id', + domains: ['passpoint.example.com'], + friendlyName: 'Passpoint Example Ltd.', + provisioningSource: 'app.passpoint.example.com', + trustedCa: kCaPem, + expirationEpochMs: 0n, + }; + await init(sub); + + // No app registered, package name should be displayed. + assertEquals(sub.provisioningSource, getSourceText()); + // Only one domain in the list. + const list = getDomainsListItems(); + assertEquals(1, list.length); + // No expiration time. + const item = getExpirationDateItem(); + assertFalse(!!item); + // Certificate has a common name. + assertEquals(kCaCN, getCertificateName()); + }); + + test( + 'Show Passpoint subscription with domains and expiration time', + async () => { + // Create a date 7 days ahead of now. + const date = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000); + + const sub = { + id: 'sub-id', + domains: ['passpoint.example.com', 'passpoint2.example.com'], + friendlyName: 'Passpoint Example Ltd.', + provisioningSource: 'app.passpoint.example.com', + trustedCa: kCaPem, + expirationEpochMs: BigInt(date.getTime()), + }; + await init(sub); + + // No app registered, package name should be displayed. + assertEquals(sub.provisioningSource, getSourceText()); + // Only one domain in the list. + const list = getDomainsListItems(); + assertEquals(2, list.length); + // Expiration time is displayed. + const item = getExpirationDateItem(); + assertTrue(!!item); + // Certificate has a common name. + assertEquals(kCaCN, getCertificateName()); + }); + + test('Show Passpoint subscription without certificate', async () => { + const sub = { + id: 'sub-id', + domains: ['passpoint.example.com'], + friendlyName: 'Passpoint Example Ltd.', + provisioningSource: 'app.passpoint.example.com', + expirationEpochMs: 0n, + }; + await init(sub); + + // Certificate has a common name. + assertEquals( + passpointSubpage_.i18n('passpointSystemCALabel'), getCertificateName()); + }); + + test('Show Passpoint subscription with app', async () => { + const sub = { + id: 'sub-id', + domains: ['passpoint.example.com'], + friendlyName: 'Passpoint Example Ltd.', + provisioningSource: 'app.passpoint.example.com', + trustedCa: kCaPem, + expirationEpochMs: 0n, + }; + const appTitle = 'My Passpoint App'; + const app = { + type: AppType.kArc, + title: appTitle, + publisherId: sub.provisioningSource, + }; + fakeHandler.setApps([app]); + await init(sub); + + // Only one domain in the list. + const list = getDomainsListItems(); + assertEquals(1, list.length); + // No expiration time. + const item = getExpirationDateItem(); + assertFalse(!!item); + // App name is displayed as subscription source. + assertEquals(appTitle, getSourceText()); + // Certificate has a common name. + assertEquals(kCaCN, getCertificateName()); + }); + + test('Subscription removal', async () => { + const subId = 'sub-id'; + const sub = { + id: subId, + domains: ['passpoint.example.com'], + friendlyName: 'Passpoint Example Ltd.', + provisioningSource: 'app.passpoint.example.com', + expirationEpochMs: 0n, + }; + await init(sub); + + // Trigger a remove. + const removeButton = getElement('removeButton'); + removeButton.click(); + await waitAfterNextRender(removeButton); + let dialog = getRemovalDialog(); + assertTrue(!!dialog); + + // Cancel the dialog. + const cancelButton = getElement('removalCancelButton'); + cancelButton.click(); + await waitAfterNextRender(cancelButton); + dialog = getRemovalDialog(); + assertFalse(!!dialog); + + // Trigger a remove again. + removeButton.click(); + await waitAfterNextRender(removeButton); + dialog = getRemovalDialog(); + assertTrue(!!dialog); + + // Confirm the removal. + const confirmButton = getElement('removalConfirmButton'); + confirmButton.click(); + await waitAfterNextRender(confirmButton); + + // Check the subscription is removed. + dialog = getRemovalDialog(); + assertFalse(!!dialog); + const resp = await passpointServiceApi_.getPasspointSubscription(subId); + assertEquals(null, resp.result); + }); +});
diff --git a/components/android_autofill/browser/android_autofill_manager.cc b/components/android_autofill/browser/android_autofill_manager.cc index b9d26e9..60b056e 100644 --- a/components/android_autofill/browser/android_autofill_manager.cc +++ b/components/android_autofill/browser/android_autofill_manager.cc
@@ -69,6 +69,7 @@ mojom::SubmissionSource source) { address_logger_->OnWillSubmitForm(); payments_logger_->OnWillSubmitForm(); + password_logger_->OnWillSubmitForm(); if (auto* provider = GetAutofillProvider()) provider->OnFormSubmitted(this, form, known_success, source); } @@ -246,6 +247,8 @@ address_logger_ = std::make_unique<FormEventLoggerWeblayerAndroid>("Address"); payments_logger_ = std::make_unique<FormEventLoggerWeblayerAndroid>("CreditCard"); + password_logger_ = + std::make_unique<FormEventLoggerWeblayerAndroid>("Password"); } FormEventLoggerWeblayerAndroid* AndroidAutofillManager::GetEventFormLogger( @@ -267,6 +270,7 @@ case FormType::kCreditCardForm: return payments_logger_.get(); case FormType::kPasswordForm: + return password_logger_.get(); case FormType::kUnknownFormType: return nullptr; }
diff --git a/components/android_autofill/browser/android_autofill_manager.h b/components/android_autofill/browser/android_autofill_manager.h index ebe7951..179b2b4 100644 --- a/components/android_autofill/browser/android_autofill_manager.h +++ b/components/android_autofill/browser/android_autofill_manager.h
@@ -171,6 +171,7 @@ raw_ptr<AutofillProvider> autofill_provider_for_testing_ = nullptr; std::unique_ptr<FormEventLoggerWeblayerAndroid> address_logger_; std::unique_ptr<FormEventLoggerWeblayerAndroid> payments_logger_; + std::unique_ptr<FormEventLoggerWeblayerAndroid> password_logger_; base::WeakPtrFactory<AndroidAutofillManager> weak_ptr_factory_{this}; };
diff --git a/components/autofill/content/renderer/autofill_agent.cc b/components/autofill/content/renderer/autofill_agent.cc index a663c484..1b465af 100644 --- a/components/autofill/content/renderer/autofill_agent.cc +++ b/components/autofill/content/renderer/autofill_agent.cc
@@ -1070,14 +1070,6 @@ SendFocusedInputChangedNotificationToBrowser(focused_element); } - // PasswordGenerationAgent needs to know about focus changes, even if there is - // no focused element. - if (password_generation_agent_ && - password_generation_agent_->FocusedNodeHasChanged(focused_element)) { - is_generation_popup_possibly_visible_ = true; - is_popup_possibly_visible_ = true; - } - if (!IsKeyboardAccessoryEnabled() && focus_requires_scroll_) HandleFocusChangeComplete(); @@ -1219,6 +1211,12 @@ focused_node_was_last_clicked_ = false; + if (password_generation_agent_ && + password_generation_agent_->HandleFocusChangeComplete(focused_element)) { + is_generation_popup_possibly_visible_ = true; + is_popup_possibly_visible_ = true; + } + SendPotentiallySubmittedFormToBrowser(); }
diff --git a/components/autofill/content/renderer/password_generation_agent.cc b/components/autofill/content/renderer/password_generation_agent.cc index f9b2bedf..adeea7e 100644 --- a/components/autofill/content/renderer/password_generation_agent.cc +++ b/components/autofill/content/renderer/password_generation_agent.cc
@@ -30,6 +30,7 @@ #include "third_party/blink/public/platform/web_security_origin.h" #include "third_party/blink/public/platform/web_vector.h" #include "third_party/blink/public/web/web_document.h" +#include "third_party/blink/public/web/web_form_control_element.h" #include "third_party/blink/public/web/web_form_element.h" #include "third_party/blink/public/web/web_input_element.h" #include "third_party/blink/public/web/web_local_frame.h" @@ -483,7 +484,7 @@ return true; } -bool PasswordGenerationAgent::FocusedNodeHasChanged( +bool PasswordGenerationAgent::HandleFocusChangeComplete( const blink::WebNode& node) { if (node.IsNull() || !node.IsElementNode()) { return false; @@ -619,8 +620,9 @@ // Notify `password_agent_` of text changes to the other confirmation // password fields. for (const auto& password_element : - current_generation_item_->password_elements_) + current_generation_item_->password_elements_) { password_agent_->UpdateStateForTextChange(password_element); + } } return true; } @@ -691,8 +693,10 @@ // Do not treat the password as generated, either here or in the browser. current_generation_item_->password_is_generated_ = false; current_generation_item_->password_edited_ = false; - for (WebInputElement& password : current_generation_item_->password_elements_) + for (WebInputElement& password : + current_generation_item_->password_elements_) { password.SetAutofillState(WebAutofillState::kNotFilled); + } password_generation::LogPasswordGenerationEvent( password_generation::PASSWORD_DELETED); // Clear all other password fields.
diff --git a/components/autofill/content/renderer/password_generation_agent.h b/components/autofill/content/renderer/password_generation_agent.h index 2a1aafe..826a11e 100644 --- a/components/autofill/content/renderer/password_generation_agent.h +++ b/components/autofill/content/renderer/password_generation_agent.h
@@ -69,7 +69,7 @@ bool TextDidChangeInTextField(const blink::WebInputElement& element); // Returns true if the newly focused node caused the generation UI to show. - bool FocusedNodeHasChanged(const blink::WebNode& node); + bool HandleFocusChangeComplete(const blink::WebNode& node); // Event forwarded by AutofillAgent from WebAutofillClient, informing that // the text field editing has ended, which means that the field is not
diff --git a/components/autofill/core/browser/form_data_importer_utils.cc b/components/autofill/core/browser/form_data_importer_utils.cc index db016f3..b7ff614 100644 --- a/components/autofill/core/browser/form_data_importer_utils.cc +++ b/components/autofill/core/browser/form_data_importer_utils.cc
@@ -109,13 +109,12 @@ !(is_line1_missing || is_city_missing || is_state_missing || is_zip_missing || is_zip_or_state_requirement_violated || is_line1_or_house_number_violated); - if (is_minimum_address && - base::FeatureList::IsEnabled( - features::kAutofillRequireNameForProfileImport)) { - is_minimum_address &= ValidateAndLog( - /*required=*/true, {NAME_FULL}, - AddressImportRequirement::kNameRequirementFulfilled, - AddressImportRequirement::kNameRequirementViolated); + // TODO(crbug.com/1413205): Merge this into is_minimum_address. + if (is_minimum_address && country.requires_full_name()) { + is_minimum_address &= + ValidateAndLog(/*required=*/true, {NAME_FULL}, + AddressImportRequirement::kNameRequirementFulfilled, + AddressImportRequirement::kNameRequirementViolated); } if (collect_metrics) { autofill_metrics::
diff --git a/components/autofill/core/browser/form_types.cc b/components/autofill/core/browser/form_types.cc index 71cdf9ef..f1a401f 100644 --- a/components/autofill/core/browser/form_types.cc +++ b/components/autofill/core/browser/form_types.cc
@@ -51,18 +51,16 @@ return "UnknownFormType"; } -bool FormHasAllEmtyCreditCardFields(const FormStructure& form_structure) { +bool FormHasAllCreditCardFields(const FormStructure& form_structure) { bool has_card_number_field = base::ranges::any_of( form_structure, [](const std::unique_ptr<AutofillField>& autofill_field) { return autofill_field->Type().GetStorableType() == - ServerFieldType::CREDIT_CARD_NUMBER && - SanitizedFieldIsEmpty(autofill_field->value); + ServerFieldType::CREDIT_CARD_NUMBER; }); bool has_expiration_date_field = base::ranges::any_of( form_structure, [](const std::unique_ptr<AutofillField>& autofill_field) { - return autofill_field->HasExpirationDateType() && - SanitizedFieldIsEmpty(autofill_field->value); + return autofill_field->HasExpirationDateType(); }); return has_card_number_field && has_expiration_date_field;
diff --git a/components/autofill/core/browser/form_types.h b/components/autofill/core/browser/form_types.h index 1fc41cb..379025d 100644 --- a/components/autofill/core/browser/form_types.h +++ b/components/autofill/core/browser/form_types.h
@@ -22,7 +22,7 @@ // Returns true if the form contains fields that represent the card number and // the card expiration date. -bool FormHasAllEmtyCreditCardFields(const FormStructure& form_structure); +bool FormHasAllCreditCardFields(const FormStructure& form_structure); FormType FieldTypeGroupToFormType(FieldTypeGroup field_type_group);
diff --git a/components/autofill/core/browser/form_types_unittest.cc b/components/autofill/core/browser/form_types_unittest.cc index 69827d3..0c9ba63 100644 --- a/components/autofill/core/browser/form_types_unittest.cc +++ b/components/autofill/core/browser/form_types_unittest.cc
@@ -43,22 +43,18 @@ FormStructure form_structure(form); FormStructureTestApi(&form_structure).SetFieldTypes(test_case.field_types); - EXPECT_THAT(FormHasAllEmtyCreditCardFields(form_structure), + EXPECT_THAT(FormHasAllCreditCardFields(form_structure), testing::Eq(test_case.expected_result)); } INSTANTIATE_TEST_SUITE_P( All, FormTypesTest, - testing::Values( - FormTypesTestCase{{CREDIT_CARD_NUMBER, CREDIT_CARD_EXP_MONTH, - CREDIT_CARD_EXP_2_DIGIT_YEAR}, - {u"", u"", u""}, - true}, - FormTypesTestCase{{CREDIT_CARD_NUMBER}, {u""}, false}, - FormTypesTestCase{{CREDIT_CARD_NUMBER, CREDIT_CARD_EXP_MONTH, - CREDIT_CARD_EXP_2_DIGIT_YEAR}, - {u"411111111111", u"", u""}, - false})); + testing::Values(FormTypesTestCase{{CREDIT_CARD_NUMBER, + CREDIT_CARD_EXP_MONTH, + CREDIT_CARD_EXP_2_DIGIT_YEAR}, + {u"", u"", u""}, + true}, + FormTypesTestCase{{CREDIT_CARD_NUMBER}, {u""}, false})); } // namespace autofill
diff --git a/components/autofill/core/browser/geo/autofill_country.cc b/components/autofill/core/browser/geo/autofill_country.cc index 44fd060..b8bbfe01 100644 --- a/components/autofill/core/browser/geo/autofill_country.cc +++ b/components/autofill/core/browser/geo/autofill_country.cc
@@ -10,12 +10,11 @@ #include "base/containers/contains.h" #include "base/containers/fixed_flat_map.h" #include "base/containers/fixed_flat_set.h" -#include "base/feature_list.h" + #include "base/strings/string_piece_forward.h" #include "base/strings/string_util.h" #include "components/autofill/core/browser/geo/country_data.h" #include "components/autofill/core/browser/geo/country_names.h" -#include "components/autofill/core/common/autofill_features.h" #include "components/autofill/core/common/autofill_internals/log_message.h" #include "components/autofill/core/common/logging/log_buffer.h" #include "third_party/icu/source/common/unicode/locid.h" @@ -188,6 +187,9 @@ } bool AutofillCountry::IsAddressFieldRequired(AddressField address_field) const { + if (address_field == AddressField::RECIPIENT && requires_full_name()) { + return true; + } auto* mapping_it = kRequiredFieldMapping.find(address_field); return mapping_it != kRequiredFieldMapping.end() && (required_fields_for_address_import_ & mapping_it->second);
diff --git a/components/autofill/core/browser/geo/autofill_country.h b/components/autofill/core/browser/geo/autofill_country.h index be2fa63..cb4f6835 100644 --- a/components/autofill/core/browser/geo/autofill_country.h +++ b/components/autofill/core/browser/geo/autofill_country.h
@@ -8,8 +8,10 @@ #include <string> #include "base/containers/span.h" +#include "base/feature_list.h" #include "base/strings/string_piece.h" #include "components/autofill/core/browser/geo/country_data.h" +#include "components/autofill/core/common/autofill_features.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_field.h" @@ -79,6 +81,12 @@ // the constructor. If no `locale` was provided, an empty string is returned. const std::u16string& name() const { return name_; } + // Full name is expected in a complete address for this country. + bool requires_full_name() const { + return base::FeatureList::IsEnabled( + features::kAutofillRequireNameForProfileImport); + } + // City is expected in a complete address for this country. bool requires_city() const { return (required_fields_for_address_import_ & ADDRESS_REQUIRES_CITY) != 0;
diff --git a/components/autofill/core/browser/geo/autofill_country_unittest.cc b/components/autofill/core/browser/geo/autofill_country_unittest.cc index 0c3e2947..e8050a41 100644 --- a/components/autofill/core/browser/geo/autofill_country_unittest.cc +++ b/components/autofill/core/browser/geo/autofill_country_unittest.cc
@@ -142,6 +142,26 @@ EXPECT_TRUE(country.IsAddressFieldRequired(AddressField::STREET_ADDRESS)); } +// Test the full name requirement depending on the +// kAutofillRequireNameForProfileImport feature flag. +TEST(AutofillCountryTest, IsAddressFieldRequired_RequireName) { + AutofillCountry country("US", "en_US"); + + { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndDisableFeature( + features::kAutofillRequireNameForProfileImport); + EXPECT_FALSE(country.IsAddressFieldRequired(AddressField::RECIPIENT)); + } + + { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature( + features::kAutofillRequireNameForProfileImport); + EXPECT_TRUE(country.IsAddressFieldRequired(AddressField::RECIPIENT)); + } +} + // Test mapping all country codes to country names. TEST(AutofillCountryTest, AllCountryCodesHaveCountryName) { std::set<std::string> expected_failures;
diff --git a/components/certificate_transparency/data/log_list.json b/components/certificate_transparency/data/log_list.json index b79f696..14e1fa51 100644 --- a/components/certificate_transparency/data/log_list.json +++ b/components/certificate_transparency/data/log_list.json
@@ -1,6 +1,6 @@ { - "version": "20.45", - "log_list_timestamp": "2023-04-20T12:54:44Z", + "version": "20.46", + "log_list_timestamp": "2023-04-21T12:54:37Z", "operators": [ { "name": "Google",
diff --git a/components/dom_distiller/content/renderer/distillability_agent.cc b/components/dom_distiller/content/renderer/distillability_agent.cc index aa4cf60..abea0d0 100644 --- a/components/dom_distiller/content/renderer/distillability_agent.cc +++ b/components/dom_distiller/content/renderer/distillability_agent.cc
@@ -27,15 +27,6 @@ const char* const kFilterlist[] = {"www.reddit.com", "tools.usps.com", "old.reddit.com"}; -enum RejectionBuckets { - NOT_ARTICLE = 0, - MOBILE_FRIENDLY, - FILTERED, - TOO_SHORT, - NOT_REJECTED, - REJECTION_BUCKET_BOUNDARY -}; - // Returns whether it is necessary to send updates back to the browser. // The number of updates can be from 0 to 2. See the tests in // "distillable_page_utils_browsertest.cc". @@ -150,56 +141,6 @@ long_score, long_article, filtered); } - if (!features.is_mobile_friendly) { - int score_int = std::round(score * 100); - if (score > 0) { - UMA_HISTOGRAM_COUNTS_1000("DomDistiller.DistillabilityScoreNMF.Positive", - score_int); - } else { - UMA_HISTOGRAM_COUNTS_1000("DomDistiller.DistillabilityScoreNMF.Negative", - -score_int); - } - if (distillable) { - // The long-article model is trained with pages that are - // non-mobile-friendly, and distillable (deemed by the first model), so - // only record on that type of pages. - int long_score_int = std::round(long_score * 100); - if (long_score > 0) { - UMA_HISTOGRAM_COUNTS_1000("DomDistiller.LongArticleScoreNMF.Positive", - long_score_int); - } else { - UMA_HISTOGRAM_COUNTS_1000("DomDistiller.LongArticleScoreNMF.Negative", - -long_score_int); - } - } - } - - int bucket = static_cast<unsigned>(features.is_mobile_friendly) | - (static_cast<unsigned>(distillable) << 1); - if (is_last) { - UMA_HISTOGRAM_ENUMERATION("DomDistiller.PageDistillableAfterLoading", - bucket, 4); - } else { - UMA_HISTOGRAM_ENUMERATION("DomDistiller.PageDistillableAfterParsing", - bucket, 4); - if (!distillable) { - UMA_HISTOGRAM_ENUMERATION("DomDistiller.DistillabilityRejection", - NOT_ARTICLE, REJECTION_BUCKET_BOUNDARY); - } else if (features.is_mobile_friendly) { - UMA_HISTOGRAM_ENUMERATION("DomDistiller.DistillabilityRejection", - MOBILE_FRIENDLY, REJECTION_BUCKET_BOUNDARY); - } else if (filtered) { - UMA_HISTOGRAM_ENUMERATION("DomDistiller.DistillabilityRejection", - FILTERED, REJECTION_BUCKET_BOUNDARY); - } else if (!long_article) { - UMA_HISTOGRAM_ENUMERATION("DomDistiller.DistillabilityRejection", - TOO_SHORT, REJECTION_BUCKET_BOUNDARY); - } else { - UMA_HISTOGRAM_ENUMERATION("DomDistiller.DistillabilityRejection", - NOT_REJECTED, REJECTION_BUCKET_BOUNDARY); - } - } - if (filtered) { return false; }
diff --git a/components/exo/surface.cc b/components/exo/surface.cc index 04c7ea1d..c4362f78 100644 --- a/components/exo/surface.cc +++ b/components/exo/surface.cc
@@ -591,6 +591,7 @@ if (pending_state_.clip_rect == clip_rect) { return; } + has_pending_contents_ = true; pending_state_.clip_rect = clip_rect; }
diff --git a/components/history/core/browser/history_backend_db_unittest.cc b/components/history/core/browser/history_backend_db_unittest.cc index 4d72cf8..e3fd593 100644 --- a/components/history/core/browser/history_backend_db_unittest.cc +++ b/components/history/core/browser/history_backend_db_unittest.cc
@@ -2877,5 +2877,27 @@ EXPECT_EQ(segment_id2, results2[0]->GetID()); } +TEST_F(HistoryBackendDBTest, QuerySegmentUsageReturnsZeroScoreForZeroVisits) { + CreateBackendAndDatabase(); + + const GURL url("http://www.foo.com"); + const base::Time time(base::Time::Now()); + + URLID url_id = db_->AddURL(URLRow(url)); + ASSERT_NE(0, url_id); + + SegmentID segment_id = + db_->CreateSegment(url_id, VisitSegmentDatabase::ComputeSegmentName(url)); + ASSERT_NE(0, segment_id); + ASSERT_TRUE(db_->IncreaseSegmentVisitCount(segment_id, time, 0)); + + std::vector<std::unique_ptr<PageUsageData>> results = + db_->QuerySegmentUsage(/*max_result_count=*/1, base::NullCallback()); + ASSERT_EQ(1u, results.size()); + EXPECT_EQ(url, results[0]->GetURL()); + EXPECT_EQ(segment_id, results[0]->GetID()); + EXPECT_EQ(0, results[0]->GetScore()); +} + } // namespace } // namespace history
diff --git a/components/history/core/browser/visitsegment_database.cc b/components/history/core/browser/visitsegment_database.cc index 98c5628..f7c1437d 100644 --- a/components/history/core/browser/visitsegment_database.cc +++ b/components/history/core/browser/visitsegment_database.cc
@@ -228,7 +228,9 @@ int days_ago = (now - timeslot).InDays(); // Score for this day in isolation. - float day_visits_score = 1.0f + log(static_cast<float>(visit_count)); + float day_visits_score = visit_count <= 0.0f + ? 0.0f + : 1.0f + log(static_cast<float>(visit_count)); // Recent visits count more than historical ones, so we multiply in a boost // related to how long ago this day was. // This boost is a curve that smoothly goes through these values:
diff --git a/components/metrics/demographics/demographic_metrics_provider_unittest.cc b/components/metrics/demographics/demographic_metrics_provider_unittest.cc index 08468e4..ab0ce6f 100644 --- a/components/metrics/demographics/demographic_metrics_provider_unittest.cc +++ b/components/metrics/demographics/demographic_metrics_provider_unittest.cc
@@ -35,7 +35,10 @@ SYNC_FEATURE_NOT_ENABLED, SYNC_FEATURE_ENABLED, SYNC_FEATURE_ENABLED_BUT_PAUSED, - SYNC_FEATURE_TEMPORARILY_DISABLED, + // Represents the user clearing sync data via dashboard. On all platforms + // except ChromeOS (Ash), this clears the primary account (which is basically + // SYNC_FEATURE_NOT_ENABLED). On ChromeOS Ash, Sync enters a special state. + SYNC_FEATURE_DISABLED_ON_CHROMEOS_ASH_VIA_DASHBOARD, }; // Profile client for testing that gets fake Profile information and services. @@ -68,7 +71,6 @@ // TestSyncService by default behaves as everything enabled/active. sync_service_ = std::make_unique<syncer::TestSyncService>(); - CHECK(sync_service_->GetUserSettings()->IsSyncRequested()); CHECK(sync_service_->GetDisableReasons().Empty()); CHECK_EQ(syncer::SyncService::TransportState::ACTIVE, sync_service_->GetTransportState()); @@ -79,21 +81,23 @@ // Mimic the user signing out from content are (sync paused). sync_service_->SetPersistentAuthError(); - CHECK(sync_service_->GetUserSettings()->IsSyncRequested()); CHECK(sync_service_->GetDisableReasons().Empty()); CHECK_EQ(syncer::SyncService::TransportState::PAUSED, sync_service_->GetTransportState()); break; - case SYNC_FEATURE_TEMPORARILY_DISABLED: + case SYNC_FEATURE_DISABLED_ON_CHROMEOS_ASH_VIA_DASHBOARD: sync_service_ = std::make_unique<syncer::TestSyncService>(); - // Temporarily disable sync without turning it off. - sync_service_->GetUserSettings()->ClearSyncRequested(); + sync_service_->SetDisableReasons(syncer::SyncService::DisableReasonSet( + syncer::SyncService::DISABLE_REASON_USER_CHOICE)); - CHECK(!sync_service_->GetUserSettings()->IsSyncRequested()); - CHECK(syncer::SyncService::DisableReasonSet( - syncer::SyncService::DISABLE_REASON_USER_CHOICE) == - sync_service_->GetDisableReasons()); + // On ChromeOS Ash, IsFirstSetupComplete gets cleared temporarily but + // immediately afterwards, it gets set again with + // ENGINE_INITIALIZED_WITH_AUTO_START. And yet, IsSyncFeatureEnabled() + // stays false because the user needs to manually resume sync the + // feature. + CHECK(sync_service_->GetUserSettings()->IsFirstSetupComplete()); + CHECK(!sync_service_->IsSyncFeatureEnabled()); break; } } @@ -201,12 +205,14 @@ UserDemographicsStatus::kSyncNotEnabled, 1); } -TEST(DemographicMetricsProviderTest, - ProvideSyncedUserNoisedBirthYearAndGender_SyncTemporarilyDisabled) { +TEST( + DemographicMetricsProviderTest, + ProvideSyncedUserNoisedBirthYearAndGender_SyncFeatureDisabledOnChromeOsAshViaSyncDashboard) { base::HistogramTester histogram; auto client = std::make_unique<TestProfileClient>( - /*number_of_profiles=*/1, SYNC_FEATURE_TEMPORARILY_DISABLED); + /*number_of_profiles=*/1, + SYNC_FEATURE_DISABLED_ON_CHROMEOS_ASH_VIA_DASHBOARD); // Run demographics provider. DemographicMetricsProvider provider(
diff --git a/components/page_load_metrics/browser/observers/prerender_page_load_metrics_observer.cc b/components/page_load_metrics/browser/observers/prerender_page_load_metrics_observer.cc index 7628504..d00273e 100644 --- a/components/page_load_metrics/browser/observers/prerender_page_load_metrics_observer.cc +++ b/components/page_load_metrics/browser/observers/prerender_page_load_metrics_observer.cc
@@ -461,6 +461,9 @@ case content::PrerenderTriggerType::kSpeculationRule: DCHECK(embedder_histogram_suffix_.empty()); return histogram_name + ".SpeculationRule"; + case content::PrerenderTriggerType::kSpeculationRuleFromIsolatedWorld: + DCHECK(embedder_histogram_suffix_.empty()); + return histogram_name + ".SpeculationRuleFromIsolatedWorld"; case content::PrerenderTriggerType::kEmbedder: DCHECK(!embedder_histogram_suffix_.empty()); return histogram_name + ".Embedder_" + embedder_histogram_suffix_;
diff --git a/components/password_manager/core/browser/ui/insecure_credentials_manager.cc b/components/password_manager/core/browser/ui/insecure_credentials_manager.cc index cb0067c..10d0da79 100644 --- a/components/password_manager/core/browser/ui/insecure_credentials_manager.cc +++ b/components/password_manager/core/browser/ui/insecure_credentials_manager.cc
@@ -198,6 +198,8 @@ void InsecureCredentialsManager::OnReuseCheckDone( base::ElapsedTimer timer_since_reuse_check_start, base::flat_set<std::u16string> reused_passwords) { + base::UmaHistogramTimes("PasswordManager.ReuseCheck.Time", + timer_since_reuse_check_start.Elapsed()); reused_passwords_ = std::move(reused_passwords); NotifyInsecureCredentialsChanged(); }
diff --git a/components/password_manager/core/browser/ui/insecure_credentials_manager_unittest.cc b/components/password_manager/core/browser/ui/insecure_credentials_manager_unittest.cc index 8f9444b..fb94124 100644 --- a/components/password_manager/core/browser/ui/insecure_credentials_manager_unittest.cc +++ b/components/password_manager/core/browser/ui/insecure_credentials_manager_unittest.cc
@@ -1149,10 +1149,14 @@ store().AddLogin(form2); RunUntilIdle(); provider().StartReuseCheck(); + AdvanceClock(base::Milliseconds(kDelay)); RunUntilIdle(); EXPECT_THAT(provider().GetInsecureCredentialEntries(), ElementsAre(CredentialUIEntry(form1), CredentialUIEntry(form2))); + + histogram_tester().ExpectUniqueSample("PasswordManager.ReuseCheck.Time", + kDelay, 1); } TEST_F(InsecureCredentialsManagerTest, UpdatingReusedPasswordFixesTheIssue) {
diff --git a/components/password_manager/core/browser/ui/reuse_check_utility.cc b/components/password_manager/core/browser/ui/reuse_check_utility.cc index 967d885..cf1d5536 100644 --- a/components/password_manager/core/browser/ui/reuse_check_utility.cc +++ b/components/password_manager/core/browser/ui/reuse_check_utility.cc
@@ -7,6 +7,7 @@ #include <unordered_map> +#include "base/metrics/histogram_functions.h" #include "base/strings/string_util.h" #include "components/password_manager/core/browser/affiliation/affiliation_utils.h" #include "components/password_manager/core/browser/psl_matching_helper.h" @@ -136,6 +137,11 @@ } reused_passwords.insert(password); } + + base::UmaHistogramCounts1000("PasswordManager.ReuseCheck.CheckedPasswords", + password_to_credentials.size()); + base::UmaHistogramCounts1000("PasswordManager.ReuseCheck.ReusedPasswords", + reused_passwords.size()); return reused_passwords; }
diff --git a/components/password_manager/core/browser/ui/reuse_check_utility_unittest.cc b/components/password_manager/core/browser/ui/reuse_check_utility_unittest.cc index 4cb0d57..044189b5 100644 --- a/components/password_manager/core/browser/ui/reuse_check_utility_unittest.cc +++ b/components/password_manager/core/browser/ui/reuse_check_utility_unittest.cc
@@ -5,6 +5,7 @@ #include "components/password_manager/core/browser/ui/reuse_check_utility.h" #include "base/strings/utf_string_conversions.h" +#include "base/test/metrics/histogram_tester.h" #include "components/password_manager/core/browser/affiliation/affiliation_utils.h" #include "components/password_manager/core/browser/ui/credential_ui_entry.h" #include "testing/gmock/include/gmock/gmock.h" @@ -44,12 +45,21 @@ } TEST(ReuseCheckUtilityTest, ReuseDetected) { + base::HistogramTester histogram_tester; + std::vector<CredentialUIEntry> credentials; credentials.push_back( CreateCredential(u"user1", u"password", {"https://test1.com"})); credentials.push_back( CreateCredential(u"user2", u"password", {"https://test2.com"})); + credentials.push_back( + CreateCredential(u"user", u"password2", {"https://test3.com"})); EXPECT_THAT(BulkReuseCheck(credentials, {}), ElementsAre(u"password")); + + histogram_tester.ExpectUniqueSample( + "PasswordManager.ReuseCheck.CheckedPasswords", 2, 1); + histogram_tester.ExpectUniqueSample( + "PasswordManager.ReuseCheck.ReusedPasswords", 1, 1); } TEST(ReuseCheckUtilityTest, ReuseDetectedSameWebsite) {
diff --git a/components/policy/core/common/cloud/affiliation.cc b/components/policy/core/common/cloud/affiliation.cc index 845be1b9..86e1a87 100644 --- a/components/policy/core/common/cloud/affiliation.cc +++ b/components/policy/core/common/cloud/affiliation.cc
@@ -4,6 +4,8 @@ #include "components/policy/core/common/cloud/affiliation.h" +#include "components/policy/core/common/device_local_account_type.h" + namespace policy { bool IsAffiliated(const base::flat_set<std::string>& user_ids, @@ -15,4 +17,21 @@ return false; } +bool IsUserAffiliated(const base::flat_set<std::string>& user_affiliation_ids, + const base::flat_set<std::string>& device_affiliation_ids, + base::StringPiece email) { + // An empty username means incognito user in case of Chrome OS and no + // logged-in user in case of Chrome (SigninService). Many tests use nonsense + // email addresses (e.g. 'test') so treat those as non-enterprise users. + if (email.empty() || email.find('@') == base::StringPiece::npos) { + return false; + } + + if (IsDeviceLocalAccountUser(email)) { + return true; + } + + return IsAffiliated(user_affiliation_ids, device_affiliation_ids); +} + } // namespace policy
diff --git a/components/policy/core/common/cloud/affiliation.h b/components/policy/core/common/cloud/affiliation.h index e51693b6..25d667ddc 100644 --- a/components/policy/core/common/cloud/affiliation.h +++ b/components/policy/core/common/cloud/affiliation.h
@@ -8,6 +8,7 @@ #include <string> #include "base/containers/flat_set.h" +#include "base/strings/string_piece.h" #include "components/policy/policy_export.h" namespace policy { @@ -19,6 +20,16 @@ POLICY_EXPORT bool IsAffiliated(const base::flat_set<std::string>& user_ids, const base::flat_set<std::string>& device_ids); +// TODO(peletskyi): Remove email after affiliation based implementation will +// fully work. http://crbug.com/515476 +// The function makes a decision if user with `user_affiliation_ids` and +// `email` is affiliated on the device with `device_affiliation_ids` and +// `enterprise_domain`. +POLICY_EXPORT bool IsUserAffiliated( + const base::flat_set<std::string>& user_affiliation_ids, + const base::flat_set<std::string>& device_affiliation_ids, + base::StringPiece email); + } // namespace policy #endif // COMPONENTS_POLICY_CORE_COMMON_CLOUD_AFFILIATION_H_
diff --git a/components/policy/core/common/cloud/affiliation_unittest.cc b/components/policy/core/common/cloud/affiliation_unittest.cc index 874b938..9c7959b 100644 --- a/components/policy/core/common/cloud/affiliation_unittest.cc +++ b/components/policy/core/common/cloud/affiliation_unittest.cc
@@ -57,4 +57,26 @@ EXPECT_FALSE(IsAffiliated(user_ids, device_ids)); } +TEST(CloudManagementAffiliationTest, UserAffiliated) { + base::flat_set<std::string> user_ids; + base::flat_set<std::string> device_ids; + + // Empty affiliation IDs. + EXPECT_FALSE(IsUserAffiliated(user_ids, device_ids, "user@managed.com")); + + user_ids.insert("aaaa"); // Only user affiliation IDs present. + EXPECT_FALSE(IsUserAffiliated(user_ids, device_ids, "user@managed.com")); + + device_ids.insert("bbbb"); // Device and user IDs do not overlap. + EXPECT_FALSE(IsUserAffiliated(user_ids, device_ids, "user@managed.com")); + + user_ids.insert("cccc"); // Device and user IDs do overlap. + device_ids.insert("cccc"); + EXPECT_TRUE(IsUserAffiliated(user_ids, device_ids, "user@managed.com")); + + // Invalid email overrides match of affiliation IDs. + EXPECT_FALSE(IsUserAffiliated(user_ids, device_ids, "")); + EXPECT_FALSE(IsUserAffiliated(user_ids, device_ids, "user")); +} + } // namespace policy
diff --git a/components/policy/resources/templates/policy_definitions/Miscellaneous/WebAppSettings.yaml b/components/policy/resources/templates/policy_definitions/Miscellaneous/WebAppSettings.yaml index 0db5bbfc..8424f07 100644 --- a/components/policy/resources/templates/policy_definitions/Miscellaneous/WebAppSettings.yaml +++ b/components/policy/resources/templates/policy_definitions/Miscellaneous/WebAppSettings.yaml
@@ -4,6 +4,7 @@ The <ph name="MANIFEST_ID_FIELD">manifest_id</ph> field is the Manifest ID for the Web App. See <ph name="WEB_APP_ID_REFERENCE_URL">https://developer.chrome.com/blog/pwa-manifest-id/<ex>https://developer.chrome.com/blog/pwa-manifest-id/</ex></ph> for instructions on how to determine the Manifest ID for an installed web app. The <ph name="RUN_ON_OS_LOGIN_FIELD">run_on_os_login</ph> field specifies if a web app can be run during OS login. If this field is set to <ph name="BLOCKED">blocked</ph>, the web app will not run during OS login and the user will not be able to enable this later. If this field is set to <ph name="RUN_WINDOWED">run_windowed</ph>, the web app will run during OS login and the user will not be able to disable this later. If this field is set to <ph name="ALLOWED">allowed</ph>, the user will be able to configure the web app to run at OS login. The default configuration only allows the <ph name="ALLOWED">allowed</ph> and <ph name="BLOCKED">blocked</ph> values. " + (Since M114) The <ph name="PREVENT_CLOSE_FIELD">prevent_close</ph> field specifies if a web app shall be prevented from closing in any way (e.g. by the user, task manager, web APIs). This behavior can only be enabled if <ph name="RUN_ON_OS_LOGIN_FIELD">run_on_os_login</ph> is set to <ph name="RUN_WINDOWED">run_windowed</ph>. If the app were already running, this property will only come into effect after the app is restarted. If this field is not defined, apps will be closable by users. example_value: - manifest_id: https://foo.example/index.html run_on_os_login: allowed @@ -11,6 +12,7 @@ run_on_os_login: allowed - manifest_id: https://foobar.example/index.html run_on_os_login: run_windowed + prevent_close: true - manifest_id: '*' run_on_os_login: blocked features: @@ -32,6 +34,8 @@ - blocked - run_windowed type: string + prevent_close: + type: boolean required: - manifest_id type: object
diff --git a/components/safe_browsing/core/browser/realtime/url_lookup_service_base.cc b/components/safe_browsing/core/browser/realtime/url_lookup_service_base.cc index 02ba946..8d0fd6b 100644 --- a/components/safe_browsing/core/browser/realtime/url_lookup_service_base.cc +++ b/components/safe_browsing/core/browser/realtime/url_lookup_service_base.cc
@@ -258,10 +258,12 @@ base::TimeTicks get_cache_start_time = base::TimeTicks::Now(); + absl::optional<bool> is_verdict_from_past_session; RTLookupResponse::ThreatInfo::VerdictType verdict_type = - cache_manager_ ? cache_manager_->GetCachedRealTimeUrlVerdict( - url, cached_threat_info.get()) - : RTLookupResponse::ThreatInfo::VERDICT_TYPE_UNSPECIFIED; + cache_manager_ + ? cache_manager_->GetCachedRealTimeUrlVerdict( + url, cached_threat_info.get(), &is_verdict_from_past_session) + : RTLookupResponse::ThreatInfo::VERDICT_TYPE_UNSPECIFIED; RecordSparseWithAndWithoutSuffix("SafeBrowsing.RT.GetCacheResult", GetMetricSuffix(), verdict_type); @@ -271,6 +273,12 @@ if (verdict_type == RTLookupResponse::ThreatInfo::SAFE || verdict_type == RTLookupResponse::ThreatInfo::DANGEROUS) { + if (is_verdict_from_past_session.has_value()) { + base::UmaHistogramBoolean( + "SafeBrowsing.RT.GetCacheResultIsFromPastSession", + is_verdict_from_past_session.value()); + } + auto cache_response = std::make_unique<RTLookupResponse>(); RTLookupResponse::ThreatInfo* new_threat_info = cache_response->add_threat_info();
diff --git a/components/safe_browsing/core/browser/verdict_cache_manager.cc b/components/safe_browsing/core/browser/verdict_cache_manager.cc index b804d23..d86690e 100644 --- a/components/safe_browsing/core/browser/verdict_cache_manager.cc +++ b/components/safe_browsing/core/browser/verdict_cache_manager.cc
@@ -290,7 +290,9 @@ scoped_refptr<HostContentSettingsMap> content_settings, const ContentSettingsType contents_setting_type, const char* proto_name, - MatchParams match_params) { + MatchParams match_params, + base::Time& time_initialized, + absl::optional<bool>* out_is_verdict_from_past_initialization) { DCHECK(proto_name == kVerdictProto || proto_name == kRealTimeThreatInfoProto); absl::optional<base::Value> result; @@ -341,6 +343,13 @@ PathVariantsMatchCacheExpression(paths, cache_expression_path) && match_params.ShouldMatch() && !IsCacheExpired(verdict_received_time, verdict.cache_duration_sec())) { + // We cast to an int because that is initially done for + // verdict_received_time. If we don't, then the comparison could + // incorrectly claim that the verdict was received before initialization + // simply because of the int casting. + *out_is_verdict_from_past_initialization = + verdict_received_time < + static_cast<int>(time_initialized.ToDoubleT()); max_path_depth = path_depth; result = std::move(value); } @@ -356,7 +365,9 @@ const std::string& type_key, scoped_refptr<HostContentSettingsMap> content_settings, const ContentSettingsType contents_setting_type, - const char* proto_name) { + const char* proto_name, + base::Time& time_initialized, + absl::optional<bool>* out_is_verdict_from_past_initialization) { DCHECK(proto_name == kVerdictProto || proto_name == kRealTimeThreatInfoProto); absl::optional<base::Value> most_matching_verdict; MatchParams match_params; @@ -371,13 +382,17 @@ int depth = static_cast<int>(GetHostDepth(host)); GURL url_to_check = GetUrlWithHostAndPath(host, root_path); match_params.is_exact_host = (root_host == host); + absl::optional<bool> is_verdict_from_past_initialization; absl::optional<base::Value> verdict = GetMostMatchingCachedVerdictEntryWithPathMatching<T>( url_to_check, type_key, content_settings, contents_setting_type, - proto_name, match_params); + proto_name, match_params, time_initialized, + &is_verdict_from_past_initialization); if (depth > max_path_depth && verdict && verdict->is_dict()) { max_path_depth = depth; most_matching_verdict = std::move(verdict); + *out_is_verdict_from_past_initialization = + is_verdict_from_past_initialization; } } @@ -428,6 +443,7 @@ stored_verdict_count_real_time_url_check_(absl::nullopt), content_settings_(content_settings), sync_observer_(std::move(sync_observer)) { + time_initialized_ = base::Time::Now(); if (history_service) { history_service_observation_.Observe(history_service); } @@ -540,11 +556,13 @@ std::string type_key = GetKeyOfTypeFromTriggerType(trigger_type, password_type); + absl::optional<bool> is_verdict_from_past_initialization; absl::optional<base::Value> most_matching_verdict = GetMostMatchingCachedVerdictEntryWithHostAndPathMatching< LoginReputationClientResponse>( url, type_key, content_settings_, - ContentSettingsType::PASSWORD_PROTECTION, kVerdictProto); + ContentSettingsType::PASSWORD_PROTECTION, kVerdictProto, + time_initialized_, &is_verdict_from_past_initialization); return GetVerdictTypeFromMostMatchedCachedVerdict< LoginReputationClientResponse>( @@ -678,7 +696,8 @@ RTLookupResponse::ThreatInfo::VerdictType VerdictCacheManager::GetCachedRealTimeUrlVerdict( const GURL& url, - RTLookupResponse::ThreatInfo* out_threat_info) { + RTLookupResponse::ThreatInfo* out_threat_info, + absl::optional<bool>* out_is_verdict_from_past_initialization) { if (is_shut_down_) { return RTLookupResponse::ThreatInfo::VERDICT_TYPE_UNSPECIFIED; } @@ -688,7 +707,8 @@ RTLookupResponse::ThreatInfo>( url, kRealTimeUrlCacheKey, content_settings_, ContentSettingsType::SAFE_BROWSING_URL_CHECK_DATA, - kRealTimeThreatInfoProto); + kRealTimeThreatInfoProto, time_initialized_, + out_is_verdict_from_past_initialization); return GetVerdictTypeFromMostMatchedCachedVerdict< RTLookupResponse::ThreatInfo>(kRealTimeThreatInfoProto, @@ -703,12 +723,14 @@ return safe_browsing::ClientSideDetectionType:: CLIENT_SIDE_DETECTION_TYPE_UNSPECIFIED; } + absl::optional<bool> is_verdict_from_past_initialization; absl::optional<base::Value> most_matching_verdict = GetMostMatchingCachedVerdictEntryWithHostAndPathMatching< RTLookupResponse::ThreatInfo>( url, kRealTimeUrlCacheKey, content_settings_, ContentSettingsType::SAFE_BROWSING_URL_CHECK_DATA, - kRealTimeThreatInfoProto); + kRealTimeThreatInfoProto, time_initialized_, + &is_verdict_from_past_initialization); if (!most_matching_verdict || !most_matching_verdict->is_dict()) { return safe_browsing::ClientSideDetectionType::
diff --git a/components/safe_browsing/core/browser/verdict_cache_manager.h b/components/safe_browsing/core/browser/verdict_cache_manager.h index edcc7c63..4b085a4a 100644 --- a/components/safe_browsing/core/browser/verdict_cache_manager.h +++ b/components/safe_browsing/core/browser/verdict_cache_manager.h
@@ -85,10 +85,14 @@ // Looks up |content_settings_| to find the cached verdict response. If // verdict is not available or is expired, return VERDICT_TYPE_UNSPECIFIED. // Otherwise, the most matching theat info will be copied to out_threat_info. - // Can be called on any thread. + // |out_is_verdict_from_past_initialization| represents whether the verdict + // was set before the current VerdictCacheManager instance was initialized, + // and is used only for logging. The parameter is only set if the unexpired + // cache entry was found. Can be called on any thread. RTLookupResponse::ThreatInfo::VerdictType GetCachedRealTimeUrlVerdict( const GURL& url, - RTLookupResponse::ThreatInfo* out_threat_info); + RTLookupResponse::ThreatInfo* out_threat_info, + absl::optional<bool>* out_is_verdict_from_past_initialization); safe_browsing::ClientSideDetectionType GetCachedRealTimeUrlClientSideDetectionType(const GURL& url); @@ -232,6 +236,9 @@ bool is_shut_down_ = false; + // Represents the time the VerdictCacheManager object was constructed. + base::Time time_initialized_; + base::WeakPtrFactory<VerdictCacheManager> weak_factory_{this}; static bool has_artificial_unsafe_url_;
diff --git a/components/safe_browsing/core/browser/verdict_cache_manager_unittest.cc b/components/safe_browsing/core/browser/verdict_cache_manager_unittest.cc index 74dfc433..f0c3c00 100644 --- a/components/safe_browsing/core/browser/verdict_cache_manager_unittest.cc +++ b/components/safe_browsing/core/browser/verdict_cache_manager_unittest.cc
@@ -57,6 +57,10 @@ &test_pref_service_, false /* is_off_the_record */, false /* store_last_modified */, false /* restore_session */, false /* should_record_metrics */); + InitializeVerdictCacheManager(); + } + + void InitializeVerdictCacheManager() { auto sync_observer = std::make_unique<MockSafeBrowsingSyncObserver>(); raw_sync_observer_ = sync_observer.get(); cache_manager_ = std::make_unique<VerdictCacheManager>( @@ -477,16 +481,18 @@ password_type, &actual_verdict)); RTLookupResponse::ThreatInfo actual_real_time_threat_info; + absl::optional<bool> is_verdict_from_past_initialization; // No cached SAFE_BROWSING_URL_CHECK_DATA verdict for www.example.com/. - EXPECT_EQ( - RTLookupResponse::ThreatInfo::VERDICT_TYPE_UNSPECIFIED, - cache_manager_->GetCachedRealTimeUrlVerdict( - GURL("https://www.example.com/"), &actual_real_time_threat_info)); + EXPECT_EQ(RTLookupResponse::ThreatInfo::VERDICT_TYPE_UNSPECIFIED, + cache_manager_->GetCachedRealTimeUrlVerdict( + GURL("https://www.example.com/"), &actual_real_time_threat_info, + &is_verdict_from_past_initialization)); // Has cached SAFE_BROWSING_URL_CHECK_DATA verdict for www.example.com/path. EXPECT_EQ( RTLookupResponse::ThreatInfo::DANGEROUS, cache_manager_->GetCachedRealTimeUrlVerdict( - GURL("https://www.example.com/path"), &actual_real_time_threat_info)); + GURL("https://www.example.com/path"), &actual_real_time_threat_info, + &is_verdict_from_past_initialization)); // token1 is cleaned up. EXPECT_FALSE( @@ -571,13 +577,16 @@ cache_manager_->CacheRealTimeUrlVerdict(response, base::Time::Now()); RTLookupResponse::ThreatInfo out_verdict; + absl::optional<bool> is_verdict_from_past_initialization; EXPECT_EQ(RTLookupResponse::ThreatInfo::DANGEROUS, - cache_manager_->GetCachedRealTimeUrlVerdict(url, &out_verdict)); + cache_manager_->GetCachedRealTimeUrlVerdict( + url, &out_verdict, &is_verdict_from_past_initialization)); EXPECT_EQ("www.example.com/path", out_verdict.cache_expression_using_match_type()); EXPECT_EQ(60, out_verdict.cache_duration_sec()); EXPECT_EQ(RTLookupResponse::ThreatInfo::SOCIAL_ENGINEERING, out_verdict.threat_type()); + EXPECT_FALSE(is_verdict_from_past_initialization.value()); histograms.ExpectUniqueSample( "SafeBrowsing.RT.CacheManager.RealTimeVerdictCount", /* sample */ 2, /* expected_count */ 1); @@ -608,19 +617,25 @@ cache_manager_->CacheRealTimeUrlVerdict(response, base::Time::Now()); RTLookupResponse::ThreatInfo out_verdict; + absl::optional<bool> is_verdict_from_past_initialization; EXPECT_EQ(RTLookupResponse::ThreatInfo::DANGEROUS, - cache_manager_->GetCachedRealTimeUrlVerdict(url1, &out_verdict)); + cache_manager_->GetCachedRealTimeUrlVerdict( + url1, &out_verdict, &is_verdict_from_past_initialization)); EXPECT_EQ("www.example.com/", out_verdict.cache_expression_using_match_type()); EXPECT_EQ(RTLookupResponse::ThreatInfo::SOCIAL_ENGINEERING, out_verdict.threat_type()); + EXPECT_FALSE(is_verdict_from_past_initialization.value()); + absl::optional<bool> is_verdict_from_past_initialization2; EXPECT_EQ(RTLookupResponse::ThreatInfo::DANGEROUS, - cache_manager_->GetCachedRealTimeUrlVerdict(url2, &out_verdict)); + cache_manager_->GetCachedRealTimeUrlVerdict( + url2, &out_verdict, &is_verdict_from_past_initialization2)); EXPECT_EQ("www.example.com/path", out_verdict.cache_expression_using_match_type()); EXPECT_EQ(RTLookupResponse::ThreatInfo::UNWANTED_SOFTWARE, out_verdict.threat_type()); + EXPECT_FALSE(is_verdict_from_past_initialization2.value()); } TEST_F(VerdictCacheManagerTest, @@ -635,8 +650,11 @@ cache_manager_->CacheRealTimeUrlVerdict(response, base::Time::Now()); RTLookupResponse::ThreatInfo out_verdict; + absl::optional<bool> is_verdict_from_past_initialization; EXPECT_EQ(RTLookupResponse::ThreatInfo::VERDICT_TYPE_UNSPECIFIED, - cache_manager_->GetCachedRealTimeUrlVerdict(url, &out_verdict)); + cache_manager_->GetCachedRealTimeUrlVerdict( + url, &out_verdict, &is_verdict_from_past_initialization)); + EXPECT_FALSE(is_verdict_from_past_initialization.has_value()); } TEST_F(VerdictCacheManagerTest, @@ -650,8 +668,11 @@ RTLookupResponse::ThreatInfo::EXACT_MATCH); cache_manager_->CacheRealTimeUrlVerdict(response, base::Time::Now()); RTLookupResponse::ThreatInfo out_verdict; + absl::optional<bool> is_verdict_from_past_initialization; EXPECT_EQ(RTLookupResponse::ThreatInfo::DANGEROUS, - cache_manager_->GetCachedRealTimeUrlVerdict(url, &out_verdict)); + cache_manager_->GetCachedRealTimeUrlVerdict( + url, &out_verdict, &is_verdict_from_past_initialization)); + EXPECT_FALSE(is_verdict_from_past_initialization.value()); history::URLRows deleted_urls; deleted_urls.push_back(history::URLRow(GURL("https://www.example.com/path"))); @@ -659,8 +680,11 @@ cache_manager_->RemoveContentSettingsOnURLsDeleted(false /* all_history */, deleted_urls); EXPECT_EQ(0u, cache_manager_->GetStoredRealTimeUrlCheckVerdictCount()); + absl::optional<bool> is_verdict_from_past_initialization2; EXPECT_EQ(RTLookupResponse::ThreatInfo::VERDICT_TYPE_UNSPECIFIED, - cache_manager_->GetCachedRealTimeUrlVerdict(url, &out_verdict)); + cache_manager_->GetCachedRealTimeUrlVerdict( + url, &out_verdict, &is_verdict_from_past_initialization2)); + EXPECT_FALSE(is_verdict_from_past_initialization2.has_value()); } TEST_F(VerdictCacheManagerTest, @@ -685,13 +709,16 @@ cache_manager_->CacheRealTimeUrlVerdict(response, base::Time::Now()); RTLookupResponse::ThreatInfo out_verdict; + absl::optional<bool> is_verdict_from_past_initialization; EXPECT_EQ(RTLookupResponse::ThreatInfo::SUSPICIOUS, - cache_manager_->GetCachedRealTimeUrlVerdict(url, &out_verdict)); + cache_manager_->GetCachedRealTimeUrlVerdict( + url, &out_verdict, &is_verdict_from_past_initialization)); EXPECT_EQ("www.example.com/path", out_verdict.cache_expression_using_match_type()); EXPECT_EQ(60, out_verdict.cache_duration_sec()); EXPECT_EQ(RTLookupResponse::ThreatInfo::SOCIAL_ENGINEERING, out_verdict.threat_type()); + EXPECT_FALSE(is_verdict_from_past_initialization.value()); EXPECT_EQ(static_cast<int>(safe_browsing::ClientSideDetectionType:: CLIENT_SIDE_DETECTION_TYPE_UNSPECIFIED), cache_manager_->GetCachedRealTimeUrlClientSideDetectionType(url)); @@ -742,9 +769,11 @@ RTLookupResponse::ThreatInfo::COVERING_MATCH); cache_manager_->CacheRealTimeUrlVerdict(response, base::Time::Now()); RTLookupResponse::ThreatInfo out_verdict; + absl::optional<bool> is_verdict_from_past_initialization; EXPECT_EQ(RTLookupResponse::ThreatInfo::DANGEROUS, cache_manager_->GetCachedRealTimeUrlVerdict( - GURL("https://b.example.test/path/path2"), &out_verdict)); + GURL("https://b.example.test/path/path2"), &out_verdict, + &is_verdict_from_past_initialization)); } TEST_F(VerdictCacheManagerTest, TestHostSuffixMatchingMostExactMatching) { @@ -782,14 +811,17 @@ cache_manager_->CacheRealTimeUrlVerdict(response, base::Time::Now()); RTLookupResponse::ThreatInfo out_verdict; + absl::optional<bool> is_verdict_from_past_initialization; EXPECT_EQ(RTLookupResponse::ThreatInfo::DANGEROUS, cache_manager_->GetCachedRealTimeUrlVerdict( - GURL("https://a.example.test/path1/"), &out_verdict)); + GURL("https://a.example.test/path1/"), &out_verdict, + &is_verdict_from_past_initialization)); // Since |cache_expression_exact_matching| is set to EXACT_MATCH, cache is not // found. EXPECT_EQ(RTLookupResponse::ThreatInfo::VERDICT_TYPE_UNSPECIFIED, cache_manager_->GetCachedRealTimeUrlVerdict( - GURL("https://a.example.test/path1/path2"), &out_verdict)); + GURL("https://a.example.test/path1/path2"), &out_verdict, + &is_verdict_from_past_initialization)); } TEST_F(VerdictCacheManagerTest, TestMatchingTypeNotSet) { @@ -807,9 +839,11 @@ cache_manager_->CacheRealTimeUrlVerdict(response, base::Time::Now()); RTLookupResponse::ThreatInfo out_verdict; + absl::optional<bool> is_verdict_from_past_initialization; // If |cache_expression_match_type| is not set, ignore this cache. EXPECT_EQ(RTLookupResponse::ThreatInfo::VERDICT_TYPE_UNSPECIFIED, - cache_manager_->GetCachedRealTimeUrlVerdict(url, &out_verdict)); + cache_manager_->GetCachedRealTimeUrlVerdict( + url, &out_verdict, &is_verdict_from_past_initialization)); histograms.ExpectBucketCount( "SafeBrowsing.RT.CacheManager.RealTimeVerdictCount", /* sample */ 0, /* expected_count */ 1); @@ -819,7 +853,8 @@ cache_manager_->CacheRealTimeUrlVerdict(response, base::Time::Now()); // Should be able to get the cache if |cache_expression_match_type| is set. EXPECT_EQ(RTLookupResponse::ThreatInfo::DANGEROUS, - cache_manager_->GetCachedRealTimeUrlVerdict(url, &out_verdict)); + cache_manager_->GetCachedRealTimeUrlVerdict( + url, &out_verdict, &is_verdict_from_past_initialization)); histograms.ExpectBucketCount( "SafeBrowsing.RT.CacheManager.RealTimeVerdictCount", /* sample */ 1, /* expected_count */ 1); @@ -950,8 +985,10 @@ // Call to cache_manager after shutdown should not cause a crash. cache_manager_->CacheRealTimeUrlVerdict(rt_response, base::Time::Now()); RTLookupResponse::ThreatInfo out_rt_verdict; + absl::optional<bool> is_verdict_from_past_initialization; cache_manager_->GetCachedRealTimeUrlVerdict( - GURL("https://www.example.com/path"), &out_rt_verdict); + GURL("https://www.example.com/path"), &out_rt_verdict, + &is_verdict_from_past_initialization); LoginReputationClientResponse pg_response; ReusedPasswordAccountType password_type; cache_manager_->CachePhishGuardVerdict( @@ -982,4 +1019,36 @@ EXPECT_TRUE(base::Contains(cache_results, "bbbb")); } +TEST_F(VerdictCacheManagerTest, TestIsVerdictFromPastInitialization) { + GURL url("https://www.example.com/path"); + RTLookupResponse response; + AddThreatInfoToResponse(response, RTLookupResponse::ThreatInfo::DANGEROUS, + RTLookupResponse::ThreatInfo::SOCIAL_ENGINEERING, 60, + "www.example.com/path", + RTLookupResponse::ThreatInfo::EXACT_MATCH); + cache_manager_->CacheRealTimeUrlVerdict(response, base::Time::Now()); + + // First, confirm that the verdict is not from a past initialization if it has + // just been cached. + RTLookupResponse::ThreatInfo out_verdict; + absl::optional<bool> is_verdict_from_past_initialization; + EXPECT_EQ(RTLookupResponse::ThreatInfo::DANGEROUS, + cache_manager_->GetCachedRealTimeUrlVerdict( + url, &out_verdict, &is_verdict_from_past_initialization)); + EXPECT_FALSE(is_verdict_from_past_initialization.value()); + + // Re-create the verdict cache manager after a brief delay. + task_environment_.FastForwardBy(base::Seconds(10)); + InitializeVerdictCacheManager(); + + // This time, the verdict should still be there, but it is from the past + // initialization. + RTLookupResponse::ThreatInfo out_verdict2; + absl::optional<bool> is_verdict_from_past_initialization2; + EXPECT_EQ(RTLookupResponse::ThreatInfo::DANGEROUS, + cache_manager_->GetCachedRealTimeUrlVerdict( + url, &out_verdict2, &is_verdict_from_past_initialization2)); + EXPECT_TRUE(is_verdict_from_past_initialization2.value()); +} + } // namespace safe_browsing
diff --git a/components/segmentation_platform/embedder/default_model/device_tier_segment.cc b/components/segmentation_platform/embedder/default_model/device_tier_segment.cc index 87165f92..976d051 100644 --- a/components/segmentation_platform/embedder/default_model/device_tier_segment.cc +++ b/components/segmentation_platform/embedder/default_model/device_tier_segment.cc
@@ -99,7 +99,7 @@ float score = 0; float device_ram_in_gb = inputs[0] / 1024; float device_os_version = inputs[1]; - int device_ppi = inputs[2]; + float device_ppi = inputs[2]; if ((device_ram_in_gb >= 8 && device_os_version >= 10 && device_ppi > 370) || (device_ram_in_gb >= 6 && device_os_version >= 11 && device_ppi > 370)) { score = 3;
diff --git a/components/segmentation_platform/embedder/default_model/tablet_productivity_user_model.cc b/components/segmentation_platform/embedder/default_model/tablet_productivity_user_model.cc index 4643c6a..8de1088 100644 --- a/components/segmentation_platform/embedder/default_model/tablet_productivity_user_model.cc +++ b/components/segmentation_platform/embedder/default_model/tablet_productivity_user_model.cc
@@ -159,7 +159,7 @@ int device_tier_score; float device_ram_in_gb = inputs[0] / 1024; float device_os_version = inputs[1]; - int device_ppi = inputs[2]; + float device_ppi = inputs[2]; if ((device_ram_in_gb >= 8 && device_os_version >= 10 && device_ppi > 370) || (device_ram_in_gb >= 6 && device_os_version >= 11 && device_ppi > 370)) { device_tier_score = 3; // High-Tier Device
diff --git a/components/segmentation_platform/internal/execution/processing/custom_input_processor.cc b/components/segmentation_platform/internal/execution/processing/custom_input_processor.cc index e5840a2..a65d562 100644 --- a/components/segmentation_platform/internal/execution/processing/custom_input_processor.cc +++ b/components/segmentation_platform/internal/execution/processing/custom_input_processor.cc
@@ -291,7 +291,7 @@ if (custom_input.tensor_length() != 1) { return false; } - int device_ram_in_mb = base::SysInfo::AmountOfPhysicalMemoryMB(); + float device_ram_in_mb = base::SysInfo::AmountOfPhysicalMemoryMB(); out_tensor.emplace_back(device_ram_in_mb); return true; } @@ -303,7 +303,7 @@ return false; } std::string os_version = base::SysInfo::OperatingSystemVersion(); - int device_os_version = processing::ProcessOsVersionString(os_version); + float device_os_version = processing::ProcessOsVersionString(os_version); out_tensor.emplace_back(device_os_version); return true; } @@ -315,7 +315,7 @@ return false; } #if BUILDFLAG(IS_ANDROID) - int device_ppi = CustomDeviceUtils::GetDevicePPI(); + float device_ppi = CustomDeviceUtils::GetDevicePPI(); out_tensor.emplace_back(device_ppi); return true; #else
diff --git a/components/segmentation_platform/public/proto/model_metadata.proto b/components/segmentation_platform/public/proto/model_metadata.proto index 2caf61d6..a780a48a 100644 --- a/components/segmentation_platform/public/proto/model_metadata.proto +++ b/components/segmentation_platform/public/proto/model_metadata.proto
@@ -137,18 +137,18 @@ FILL_SYNC_DEVICE_INFO = 5; // Output is a tensor of length 1 consisting device RAM in MB. - // Output type: int + // Output type: float // Output length: 1 FILL_DEVICE_RAM_MB = 6; // Output is a tensor of length 1 describing device OS level. - // Output type: int + // Output type: float // Output length: 1 FILL_DEVICE_OS_VERSION_NUMBER = 7; // Output is a tensor of length 1 giving pixels per inch for the current // device used by the user. - // Output type: int + // Output type: float // Output length: 1 FILL_DEVICE_PPI = 8; }
diff --git a/components/signin/public/base/signin_pref_names.cc b/components/signin/public/base/signin_pref_names.cc index 409d7a5..5f6f813 100644 --- a/components/signin/public/base/signin_pref_names.cc +++ b/components/signin/public/base/signin_pref_names.cc
@@ -101,4 +101,8 @@ const char kRestrictAccountsToPatterns[] = "signin.restrict_accounts_to_patterns"; +// Boolean which indicates if the user is allowed to sign into Chrome on the +// next startup. +const char kSigninAllowedOnNextStartup[] = "signin.allowed_on_next_startup"; + } // namespace prefs
diff --git a/components/signin/public/base/signin_pref_names.h b/components/signin/public/base/signin_pref_names.h index 1e9e64ec..3a00cda 100644 --- a/components/signin/public/base/signin_pref_names.h +++ b/components/signin/public/base/signin_pref_names.h
@@ -32,6 +32,7 @@ extern const char kSignedInWithCredentialProvider[]; extern const char kSigninAllowed[]; extern const char kGaiaCookieLastListAccountsData[]; +extern const char kSigninAllowedOnNextStartup[]; } // namespace prefs
diff --git a/components/sync/driver/sync_internals_util.cc b/components/sync/driver/sync_internals_util.cc index cf1f0816..c361524 100644 --- a/components/sync/driver/sync_internals_util.cc +++ b/components/sync/driver/sync_internals_util.cc
@@ -13,7 +13,6 @@ #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" -#include "build/chromeos_buildflags.h" #include "components/sync/base/time.h" #include "components/sync/driver/sync_service.h" #include "components/sync/driver/sync_token_status.h" @@ -25,10 +24,6 @@ #include "components/version_info/version_info.h" #include "url/gurl.h" -#if BUILDFLAG(IS_CHROMEOS_ASH) -#include "ash/constants/ash_features.h" -#endif - namespace syncer::sync_ui_util { namespace { @@ -314,10 +309,6 @@ section_summary->AddStringStat("User Actionable Error"); Stat<std::string>* disable_reasons = section_summary->AddStringStat("Disable Reasons"); -#if BUILDFLAG(IS_CHROMEOS_ASH) - Stat<std::string>* os_feature_state = - section_summary->AddStringStat("Chrome OS Sync Feature"); -#endif Stat<bool>* feature_enabled = section_summary->AddBoolStat("Sync Feature Enabled"); Stat<bool>* setup_in_progress = @@ -452,9 +443,6 @@ /*is_good=*/user_actionable_error == SyncService::UserActionableError::kNone); disable_reasons->Set(GetDisableReasonsString(service->GetDisableReasons())); -#if BUILDFLAG(IS_CHROMEOS_ASH) - os_feature_state->Set("Enforced Enabled"); -#endif // BUILDFLAG(IS_CHROMEOS_ASH) feature_enabled->Set(service->IsSyncFeatureEnabled()); setup_in_progress->Set(service->IsSetupInProgress()); std::string auth_error_str = service->GetAuthError().ToString();
diff --git a/components/sync/driver/sync_service_impl_unittest.cc b/components/sync/driver/sync_service_impl_unittest.cc index cb77d05..4dfacd6 100644 --- a/components/sync/driver/sync_service_impl_unittest.cc +++ b/components/sync/driver/sync_service_impl_unittest.cc
@@ -473,7 +473,6 @@ SyncPrefs sync_prefs(prefs()); - ASSERT_TRUE(sync_prefs.IsSyncRequested()); ASSERT_EQ(SyncService::DisableReasonSet(), service()->GetDisableReasons()); ASSERT_EQ(SyncService::TransportState::ACTIVE, service()->GetTransportState()); @@ -492,7 +491,6 @@ EXPECT_FALSE(service()->IsSyncFeatureEnabled()); service()->GetUserSettings()->SetSyncRequested(); - EXPECT_TRUE(sync_prefs.IsSyncRequested()); EXPECT_EQ(SyncService::DisableReasonSet(), service()->GetDisableReasons()); service()->GetUserSettings()->SetFirstSetupComplete( syncer::SyncFirstSetupCompleteSource::BASIC_FLOW); @@ -540,7 +538,7 @@ CreateService(SyncServiceImpl::MANUAL_START); InitializeForNthSync(); ASSERT_TRUE(service()->GetUserSettings()->IsFirstSetupComplete()); - ASSERT_TRUE(service()->GetUserSettings()->IsSyncRequested()); + ASSERT_EQ(SyncService::DisableReasonSet(), service()->GetDisableReasons()); ASSERT_EQ(0, component_factory()->clear_transport_data_call_count()); // Sign-out. @@ -554,11 +552,14 @@ base::RunLoop().RunUntilIdle(); // These are specific to sync-the-feature and should be cleared. EXPECT_FALSE(service()->GetUserSettings()->IsFirstSetupComplete()); - EXPECT_FALSE(service()->GetUserSettings()->IsSyncRequested()); + EXPECT_EQ( + SyncService::DisableReasonSet(SyncService::DISABLE_REASON_NOT_SIGNED_IN, + SyncService::DISABLE_REASON_USER_CHOICE), + service()->GetDisableReasons()); EXPECT_EQ(1, component_factory()->clear_transport_data_call_count()); } -TEST_F(SyncServiceImplTest, SyncRequestedSetToFalseIfStartsSignedOut) { +TEST_F(SyncServiceImplTest, DisableReasonUserChoiceIfStartsSignedOut) { // Set up bad state. SyncPrefs sync_prefs(prefs()); sync_prefs.SetSyncRequested(true); @@ -566,8 +567,8 @@ CreateService(SyncServiceImpl::MANUAL_START); service()->Initialize(); - // There's no signed-in user, so SyncRequested should have been set to false. - EXPECT_FALSE(service()->GetUserSettings()->IsSyncRequested()); + EXPECT_TRUE(service()->GetDisableReasons().Has( + SyncService::DISABLE_REASON_USER_CHOICE)); } #endif // !BUILDFLAG(IS_CHROMEOS_ASH) @@ -792,7 +793,6 @@ // Calling StopAndClear while already stopped should not crash. This may // (under some circumstances) happen when the user enables sync again but hits // the cancel button at the end of the process. - ASSERT_FALSE(service()->GetUserSettings()->IsSyncRequested()); service()->StopAndClear(); EXPECT_FALSE(service()->IsSyncFeatureEnabled()); } @@ -1068,18 +1068,16 @@ EXPECT_EQ(SyncService::TransportState::ACTIVE, service()->GetTransportState()); + // The transport should continue active even if kSyncManaged becomes true. prefs()->SetManagedPref(prefs::kSyncManaged, base::Value(true)); EXPECT_EQ(SyncService::DisableReasonSet(), service()->GetDisableReasons()); EXPECT_EQ(SyncService::TransportState::ACTIVE, service()->GetTransportState()); - // Note: If standalone transport is enabled, then setting kSyncManaged to - // false will immediately start up the engine. Otherwise, the RequestStart - // call below will trigger it. + // Setting kSyncManaged back to false should also make no difference. prefs()->SetManagedPref(prefs::kSyncManaged, base::Value(false)); - service()->GetUserSettings()->SetSyncRequested(); EXPECT_EQ(SyncService::DisableReasonSet(), service()->GetDisableReasons()); EXPECT_EQ(SyncService::TransportState::ACTIVE, service()->GetTransportState());
diff --git a/components/sync/driver/sync_user_settings.h b/components/sync/driver/sync_user_settings.h index a86eb28..7ddfb03 100644 --- a/components/sync/driver/sync_user_settings.h +++ b/components/sync/driver/sync_user_settings.h
@@ -36,17 +36,14 @@ public: virtual ~SyncUserSettings() = default; - // Whether the user wants Sync to run. This is false by default, but gets set - // to true early in the Sync setup flow, after the user has pressed "turn on - // Sync" but before they have actually confirmed the settings (that's - // IsFirstSetupComplete()). After Sync is enabled, this can get set to false - // by the Sync feature toggle in settings, or when Sync gets reset from the - // dashboard. This maps to DISABLE_REASON_USER_CHOICE. - virtual bool IsSyncRequested() const = 0; + // Indicates that the initial Sync setup has started, usually meaning that the + // user clicked on a UI to turn Sync (the Feature) on. NOTE: On ChromeOS, this + // gets set automatically, so it doesn't really mean anything. See + // |browser_defaults::kSyncAutoStarts|. virtual void SetSyncRequested() = 0; - // Whether the initial Sync setup has been completed, meaning the user has - // consented to Sync. + // Whether the initial Sync Feature setup has been completed, meaning the + // user has turned on Sync-the-Feature. // NOTE: On ChromeOS, this gets set automatically, so it doesn't really mean // anything. See |browser_defaults::kSyncAutoStarts|. virtual bool IsFirstSetupComplete() const = 0;
diff --git a/components/sync/driver/sync_user_settings_impl.h b/components/sync/driver/sync_user_settings_impl.h index ac2adfe7..0a67ca2 100644 --- a/components/sync/driver/sync_user_settings_impl.h +++ b/components/sync/driver/sync_user_settings_impl.h
@@ -33,7 +33,6 @@ ~SyncUserSettingsImpl() override; // SyncUserSettings implementation. - bool IsSyncRequested() const override; void SetSyncRequested() override; bool IsFirstSetupComplete() const override; void SetFirstSetupComplete(SyncFirstSetupCompleteSource source) override; @@ -70,6 +69,15 @@ void SetDecryptionNigoriKey(std::unique_ptr<Nigori> nigori) override; std::unique_ptr<Nigori> GetDecryptionNigoriKey() const override; + // Whether the user wants Sync to run. This is false by default, but gets set + // to true early in the Sync setup flow, after the user has pressed "turn on + // Sync" but before they have actually confirmed the settings (that's + // IsFirstSetupComplete()). After Sync is enabled, this can get set to false + // via signout (which also clears IsFirstSetupComplete) or, on ChromeOS Ash, + // when Sync gets reset from the dashboard. + // + // This maps to DISABLE_REASON_USER_CHOICE. + bool IsSyncRequested() const; void ClearSyncRequested(); void SetSyncRequestedIfNotSetExplicitly();
diff --git a/components/sync/protocol/password_specifics.proto b/components/sync/protocol/password_specifics.proto index 57c40ae..02d41f8d 100644 --- a/components/sync/protocol/password_specifics.proto +++ b/components/sync/protocol/password_specifics.proto
@@ -70,6 +70,14 @@ // Whether the issue was muted by user. optional bool is_muted = 2; + + // Whether the backend should notify the user about this issue. + // Set to true if the user hasn't already seen a client notification for + // this issue (e.g. a leak detection prompt in Chrome). The backend sending + // notifications does not reset this field. All other sources can write this + // in both `PasswordSpecificsData` and `PasswordSpecificsMetadata` and do + // so. + optional bool trigger_notification_from_backend_on_detection = 3; } optional PasswordIssue leaked_password_issue = 1; optional PasswordIssue reused_password_issue = 2;
diff --git a/components/sync/protocol/proto_visitors.h b/components/sync/protocol/proto_visitors.h index af754557..f0558010 100644 --- a/components/sync/protocol/proto_visitors.h +++ b/components/sync/protocol/proto_visitors.h
@@ -885,6 +885,7 @@ VISIT_PROTO_FIELDS(const sync_pb::PasswordIssues_PasswordIssue& proto) { VISIT(date_first_detection_windows_epoch_micros); VISIT(is_muted); + VISIT(trigger_notification_from_backend_on_detection); } VISIT_PROTO_FIELDS(const sync_pb::PasswordSpecificsData_Notes& proto) {
diff --git a/components/sync/test/sync_user_settings_mock.h b/components/sync/test/sync_user_settings_mock.h index b4651b8..5d70c797 100644 --- a/components/sync/test/sync_user_settings_mock.h +++ b/components/sync/test/sync_user_settings_mock.h
@@ -19,7 +19,6 @@ public: SyncUserSettingsMock(); ~SyncUserSettingsMock() override; - MOCK_METHOD(bool, IsSyncRequested, (), (const override)); MOCK_METHOD(void, SetSyncRequested, (), (override)); MOCK_METHOD(bool, IsFirstSetupComplete, (), (const override)); MOCK_METHOD(void,
diff --git a/components/sync/test/test_sync_user_settings.cc b/components/sync/test/test_sync_user_settings.cc index bf55b67..9038fac 100644 --- a/components/sync/test/test_sync_user_settings.cc +++ b/components/sync/test/test_sync_user_settings.cc
@@ -45,22 +45,12 @@ TestSyncUserSettings::~TestSyncUserSettings() = default; -bool TestSyncUserSettings::IsSyncRequested() const { - return !service_->HasDisableReason(SyncService::DISABLE_REASON_USER_CHOICE); -} - void TestSyncUserSettings::SetSyncRequested() { SyncService::DisableReasonSet disable_reasons = service_->GetDisableReasons(); disable_reasons.Remove(SyncService::DISABLE_REASON_USER_CHOICE); service_->SetDisableReasons(disable_reasons); } -void TestSyncUserSettings::ClearSyncRequested() { - SyncService::DisableReasonSet disable_reasons = service_->GetDisableReasons(); - disable_reasons.Put(SyncService::DISABLE_REASON_USER_CHOICE); - service_->SetDisableReasons(disable_reasons); -} - bool TestSyncUserSettings::IsFirstSetupComplete() const { return first_setup_complete_; }
diff --git a/components/sync/test/test_sync_user_settings.h b/components/sync/test/test_sync_user_settings.h index 990d54a..abd5c1e 100644 --- a/components/sync/test/test_sync_user_settings.h +++ b/components/sync/test/test_sync_user_settings.h
@@ -25,7 +25,6 @@ explicit TestSyncUserSettings(TestSyncService* service); ~TestSyncUserSettings() override; - bool IsSyncRequested() const override; void SetSyncRequested() override; bool IsFirstSetupComplete() const override; @@ -70,10 +69,6 @@ void SetDecryptionNigoriKey(std::unique_ptr<Nigori> nigori) override; std::unique_ptr<Nigori> GetDecryptionNigoriKey() const override; - // TODO(crbug.com/1219990): Remove or rename this function since there is no - // UI for the user to achieve this, with the exception of ChromeOS for the - // case where the user clears sync data via dashboard. - void ClearSyncRequested(); void SetFirstSetupComplete(); void ClearFirstSetupComplete(); void SetCustomPassphraseAllowed(bool allowed);
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn index 5086de0..f103049c 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn
@@ -1516,6 +1516,8 @@ "preloading/prerender/prerender_new_tab_handle.h", "preloading/prerender/prerender_subframe_navigation_throttle.cc", "preloading/prerender/prerender_subframe_navigation_throttle.h", + "preloading/prerender/prerender_trigger_type_impl.cc", + "preloading/prerender/prerender_trigger_type_impl.h", "preloading/prerenderer.h", "preloading/prerenderer_impl.cc", "preloading/prerenderer_impl.h",
diff --git a/content/browser/preloading/prerender/prerender_browsertest.cc b/content/browser/preloading/prerender/prerender_browsertest.cc index 9206d99..18584d66 100644 --- a/content/browser/preloading/prerender/prerender_browsertest.cc +++ b/content/browser/preloading/prerender/prerender_browsertest.cc
@@ -291,8 +291,9 @@ prerender_helper_->WaitForRequest(url, count); } - int AddPrerender(const GURL& prerendering_url) { - return prerender_helper_->AddPrerender(prerendering_url); + int AddPrerender(const GURL& prerendering_url, + int32_t world_id = ISOLATED_WORLD_ID_GLOBAL) { + return prerender_helper_->AddPrerender(prerendering_url, world_id); } void AddPrerenderAsync(const GURL& prerendering_url) { @@ -505,11 +506,10 @@ EXPECT_TRUE(WaitForLoadStop(web_contents())); } - void ExpectFinalStatusForSpeculationRule(PrerenderFinalStatus status) { + void ExpectFinalStatus(const std::string& final_status_name, + PrerenderFinalStatus status) { // Check FinalStatus in UMA. - histogram_tester().ExpectUniqueSample( - "Prerender.Experimental.PrerenderHostFinalStatus.SpeculationRule", - status, 1); + histogram_tester().ExpectUniqueSample(final_status_name, status, 1); // Check all entries in UKM to make sure that the recorded FinalStatus is // equal to `status`. At least one entry should exist. @@ -529,13 +529,21 @@ EXPECT_TRUE(final_status_entry_found); } - void ExpectFinalStatusForEmbedder(PrerenderFinalStatus status) { - // Check FinalStatus in UMA. - histogram_tester().ExpectUniqueSample( - "Prerender.Experimental.PrerenderHostFinalStatus.Embedder_" - "EmbedderSuffixForTest", - status, 1); + void ExpectFinalStatusForSpeculationRule(PrerenderFinalStatus status) { + ExpectFinalStatus( + "Prerender.Experimental.PrerenderHostFinalStatus.SpeculationRule", + status); + } + void ExpectFinalStatusForSpeculationRuleFromIsolatedWorld( + PrerenderFinalStatus status) { + ExpectFinalStatus( + "Prerender.Experimental.PrerenderHostFinalStatus." + "SpeculationRuleFromIsolatedWorld", + status); + } + + void ExpectFinalStatusForEmbedder(PrerenderFinalStatus status) { // UKM can be recorded in an initiator page and an activated page. Embedder // triggers don't have an initiator page, so UKM is not recorded anywhere // when prerendering is canceled. @@ -543,22 +551,10 @@ return; } - // Check all entries in UKM to make sure that the recorded FinalStatus is - // equal to `status`. At least one entry should exist. - bool final_status_entry_found = false; - const auto entries = ukm_recorder_->GetEntriesByName( - ukm::builders::PrerenderPageLoad::kEntryName); - for (const auto* entry : entries) { - if (ukm_recorder_->EntryHasMetric( - entry, ukm::builders::PrerenderPageLoad::kFinalStatusName)) { - final_status_entry_found = true; - ukm_recorder_->ExpectEntryMetric( - entry, ukm::builders::PrerenderPageLoad::kFinalStatusName, - static_cast<int>(status)); - } - } - - EXPECT_TRUE(final_status_entry_found); + ExpectFinalStatus( + "Prerender.Experimental.PrerenderHostFinalStatus.Embedder_" + "EmbedderSuffixForTest", + status); } const base::HistogramTester& histogram_tester() { return histogram_tester_; } @@ -2303,6 +2299,9 @@ case PrerenderTriggerType::kSpeculationRule: host_id = AddPrerender(kPrerenderingUrl); break; + case PrerenderTriggerType::kSpeculationRuleFromIsolatedWorld: + host_id = AddPrerender(kPrerenderingUrl, /*world_id=*/1); + break; case PrerenderTriggerType::kEmbedder: prerender_handle = AddEmbedderTriggeredPrerender(kPrerenderingUrl); host_id = static_cast<PrerenderHandleImpl*>(prerender_handle.get()) @@ -2338,6 +2337,7 @@ // Activation should succeed. switch (trigger_type) { case PrerenderTriggerType::kSpeculationRule: + case PrerenderTriggerType::kSpeculationRuleFromIsolatedWorld: NavigatePrimaryPage(kPrerenderingUrl); break; case PrerenderTriggerType::kEmbedder: @@ -2363,6 +2363,9 @@ case PrerenderTriggerType::kSpeculationRule: ExpectFinalStatusForSpeculationRule(expected_status); break; + case PrerenderTriggerType::kSpeculationRuleFromIsolatedWorld: + ExpectFinalStatusForSpeculationRuleFromIsolatedWorld(expected_status); + break; case PrerenderTriggerType::kEmbedder: ExpectFinalStatusForEmbedder(expected_status); break; @@ -2401,6 +2404,9 @@ case PrerenderTriggerType::kSpeculationRule: host_id = AddPrerender(kPrerenderingUrl); break; + case PrerenderTriggerType::kSpeculationRuleFromIsolatedWorld: + host_id = AddPrerender(kPrerenderingUrl, /*world_id=*/1); + break; case PrerenderTriggerType::kEmbedder: prerender_handle = AddEmbedderTriggeredPrerender(kPrerenderingUrl); host_id = static_cast<PrerenderHandleImpl*>(prerender_handle.get()) @@ -2424,6 +2430,7 @@ // Activation should succeed. switch (trigger_type) { case PrerenderTriggerType::kSpeculationRule: + case PrerenderTriggerType::kSpeculationRuleFromIsolatedWorld: NavigatePrimaryPage(kPrerenderingUrl); break; case PrerenderTriggerType::kEmbedder: @@ -2449,6 +2456,9 @@ case PrerenderTriggerType::kSpeculationRule: ExpectFinalStatusForSpeculationRule(expected_status); break; + case PrerenderTriggerType::kSpeculationRuleFromIsolatedWorld: + ExpectFinalStatusForSpeculationRuleFromIsolatedWorld(expected_status); + break; case PrerenderTriggerType::kEmbedder: ExpectFinalStatusForEmbedder(expected_status); break; @@ -2495,11 +2505,14 @@ All, PrerenderMainFrameNavigationBrowserTest, testing::Values(PrerenderTriggerType::kSpeculationRule, + PrerenderTriggerType::kSpeculationRuleFromIsolatedWorld, PrerenderTriggerType::kEmbedder), [](const testing::TestParamInfo<PrerenderTriggerType>& info) { switch (info.param) { case PrerenderTriggerType::kSpeculationRule: return "SpeculationRule"; + case PrerenderTriggerType::kSpeculationRuleFromIsolatedWorld: + return "SpeculationRuleFromIsolatedWorld"; case PrerenderTriggerType::kEmbedder: return "Embedder"; }
diff --git a/content/browser/preloading/prerender/prerender_host_registry.cc b/content/browser/preloading/prerender/prerender_host_registry.cc index 8cd03ae..d7788e8 100644 --- a/content/browser/preloading/prerender/prerender_host_registry.cc +++ b/content/browser/preloading/prerender/prerender_host_registry.cc
@@ -24,6 +24,7 @@ #include "content/browser/preloading/prerender/prerender_metrics.h" #include "content/browser/preloading/prerender/prerender_navigation_utils.h" #include "content/browser/preloading/prerender/prerender_new_tab_handle.h" +#include "content/browser/preloading/prerender/prerender_trigger_type_impl.h" #include "content/browser/renderer_host/frame_tree_node.h" #include "content/browser/renderer_host/navigation_request.h" #include "content/browser/renderer_host/render_frame_host_impl.h" @@ -448,6 +449,7 @@ blink::features::kPrerender2SequentialPrerendering)) { switch (attributes.trigger_type) { case PrerenderTriggerType::kSpeculationRule: + case PrerenderTriggerType::kSpeculationRuleFromIsolatedWorld: pending_prerenders_.push_back(frame_tree_node_id); // Start the initial prerendering navigation of the pending request in // the head of the queue if there's no running prerender. @@ -485,7 +487,7 @@ int PrerenderHostRegistry::CreateAndStartHostForNewTab( const PrerenderAttributes& attributes) { CHECK(base::FeatureList::IsEnabled(blink::features::kPrerender2InNewTab)); - CHECK_EQ(attributes.trigger_type, PrerenderTriggerType::kSpeculationRule); + CHECK(IsSpeculationRuleType(attributes.trigger_type)); std::string recorded_url = attributes.initiator_origin.has_value() ? attributes.initiator_origin.value().GetURL().spec() @@ -564,6 +566,7 @@ switch (prerender_host_by_frame_tree_node_id_[frame_tree_node_id] ->trigger_type()) { case PrerenderTriggerType::kSpeculationRule: + case PrerenderTriggerType::kSpeculationRuleFromIsolatedWorld: DestroyWhenUsingExcessiveMemory(frame_tree_node_id); break; case PrerenderTriggerType::kEmbedder: @@ -578,6 +581,7 @@ switch (prerender_host_by_frame_tree_node_id_[frame_tree_node_id] ->trigger_type()) { case PrerenderTriggerType::kSpeculationRule: + case PrerenderTriggerType::kSpeculationRuleFromIsolatedWorld: running_prerender_host_id_ = frame_tree_node_id; break; case PrerenderTriggerType::kEmbedder: @@ -668,31 +672,27 @@ return !cancelled_ids.empty(); } -void PrerenderHostRegistry::CancelHostsForTrigger( - PrerenderTriggerType trigger_type, +void PrerenderHostRegistry::CancelHostsForTriggers( + std::vector<PrerenderTriggerType> trigger_types, const PrerenderCancellationReason& reason) { TRACE_EVENT1("navigation", "PrerenderHostRegistry::CancelHostsForTrigger", - "trigger_type", trigger_type); + "trigger_type", trigger_types[0]); std::vector<int> ids_to_be_deleted; for (auto& iter : prerender_host_by_frame_tree_node_id_) { - if (iter.second->trigger_type() == trigger_type) { + if (base::Contains(trigger_types, iter.second->trigger_type())) { ids_to_be_deleted.push_back(iter.first); } } - if (base::FeatureList::IsEnabled(blink::features::kPrerender2InNewTab)) { - switch (trigger_type) { - case PrerenderTriggerType::kSpeculationRule: - for (auto& iter : prerender_new_tab_handle_by_frame_tree_node_id_) { - ids_to_be_deleted.push_back(iter.first); - } - break; - case PrerenderTriggerType::kEmbedder: - // Prerendering into a new tab can be triggered by speculation - // rules only. - break; + for (auto& iter : prerender_new_tab_handle_by_frame_tree_node_id_) { + if (base::Contains(trigger_types, iter.second->trigger_type())) { + // Prerendering into a new tab can be triggered by speculation rules + // only. + CHECK(IsSpeculationRuleType(iter.second->trigger_type())); + ids_to_be_deleted.push_back(iter.first); + } } } else { CHECK(prerender_new_tab_handle_by_frame_tree_node_id_.empty()); @@ -1116,17 +1116,21 @@ // amount of time. The timeout differs depending on the trigger type. timeout_timer_for_embedder_.Start( FROM_HERE, kTimeToLiveInBackgroundForEmbedder, - base::BindOnce(&PrerenderHostRegistry::CancelHostsForTrigger, - base::Unretained(this), PrerenderTriggerType::kEmbedder, + base::BindOnce(&PrerenderHostRegistry::CancelHostsForTriggers, + base::Unretained(this), + std::vector({PrerenderTriggerType::kEmbedder}), PrerenderCancellationReason( PrerenderFinalStatus::kTimeoutBackgrounded))); timeout_timer_for_speculation_rules_.Start( FROM_HERE, kTimeToLiveInBackgroundForSpeculationRules, - base::BindOnce(&PrerenderHostRegistry::CancelHostsForTrigger, - base::Unretained(this), - PrerenderTriggerType::kSpeculationRule, - PrerenderCancellationReason( - PrerenderFinalStatus::kTimeoutBackgrounded))); + base::BindOnce( + &PrerenderHostRegistry::CancelHostsForTriggers, + base::Unretained(this), + std::vector( + {PrerenderTriggerType::kSpeculationRule, + PrerenderTriggerType::kSpeculationRuleFromIsolatedWorld}), + PrerenderCancellationReason( + PrerenderFinalStatus::kTimeoutBackgrounded))); } else { // Stop the timer when a prerendered page gets visible to users. timeout_timer_for_embedder_.Stop(); @@ -1331,6 +1335,7 @@ switch (trigger_type) { case PrerenderTriggerType::kSpeculationRule: + case PrerenderTriggerType::kSpeculationRuleFromIsolatedWorld: // The number of prerenders triggered by speculation rules is limited to a // Finch config param. return trigger_type_count <
diff --git a/content/browser/preloading/prerender/prerender_host_registry.h b/content/browser/preloading/prerender/prerender_host_registry.h index 1c1000b..e61d809 100644 --- a/content/browser/preloading/prerender/prerender_host_registry.h +++ b/content/browser/preloading/prerender/prerender_host_registry.h
@@ -140,10 +140,6 @@ std::set<int> CancelHosts(const std::vector<int>& frame_tree_node_ids, const PrerenderCancellationReason& reason); - // Cancels the existing hosts that were triggered by `trigger_type`. - void CancelHostsForTrigger(PrerenderTriggerType trigger_type, - const PrerenderCancellationReason& reason); - // Applies CancelHost for all existing PrerenderHost. void CancelAllHosts(PrerenderFinalStatus final_status); @@ -266,6 +262,10 @@ // cancelled. int StartPrerendering(int frame_tree_node_id); + // Cancels the existing hosts that were triggered by `trigger_types`. + void CancelHostsForTriggers(std::vector<PrerenderTriggerType> trigger_types, + const PrerenderCancellationReason& reason); + // Returns whether a certain type of PrerenderTriggerType is allowed to be // added to PrerenderHostRegistry according to the limit of the given // PrerenderTriggerType.
diff --git a/content/browser/preloading/prerender/prerender_host_registry_unittest.cc b/content/browser/preloading/prerender/prerender_host_registry_unittest.cc index 5360504f..fa8dc02 100644 --- a/content/browser/preloading/prerender/prerender_host_registry_unittest.cc +++ b/content/browser/preloading/prerender/prerender_host_registry_unittest.cc
@@ -198,6 +198,7 @@ RenderFrameHostImpl* rfh) { switch (trigger_type) { case PrerenderTriggerType::kSpeculationRule: + case PrerenderTriggerType::kSpeculationRuleFromIsolatedWorld: return PrerenderAttributes( url, trigger_type, embedder_histogram_suffix, Referrer(), rfh->GetLastCommittedOrigin(), rfh->GetProcess()->GetID(),
diff --git a/content/browser/preloading/prerender/prerender_metrics.cc b/content/browser/preloading/prerender/prerender_metrics.cc index 872cc930..b9d0c88 100644 --- a/content/browser/preloading/prerender/prerender_metrics.cc +++ b/content/browser/preloading/prerender/prerender_metrics.cc
@@ -9,6 +9,7 @@ #include "base/metrics/metrics_hashes.h" #include "base/strings/string_util.h" #include "content/browser/devtools/devtools_instrumentation.h" +#include "content/browser/preloading/prerender/prerender_trigger_type_impl.h" #include "content/browser/renderer_host/frame_tree_node.h" #include "content/public/browser/prerender_trigger_type.h" #include "services/metrics/public/cpp/ukm_builders.h" @@ -61,6 +62,10 @@ case PrerenderTriggerType::kSpeculationRule: CHECK(embedder_suffix.empty()); return std::string(histogram_base_name) + ".SpeculationRule"; + case PrerenderTriggerType::kSpeculationRuleFromIsolatedWorld: + CHECK(embedder_suffix.empty()); + return std::string(histogram_base_name) + + ".SpeculationRuleFromIsolatedWorld"; case PrerenderTriggerType::kEmbedder: CHECK(!embedder_suffix.empty()); return std::string(histogram_base_name) + ".Embedder_" + embedder_suffix; @@ -215,7 +220,7 @@ if (attributes.initiator_ukm_id != ukm::kInvalidSourceId) { // `initiator_ukm_id` must be valid for the speculation rules. - CHECK_EQ(attributes.trigger_type, PrerenderTriggerType::kSpeculationRule); + CHECK(IsSpeculationRuleType(attributes.trigger_type)); ukm::builders::PrerenderPageLoad(attributes.initiator_ukm_id) .SetFinalStatus(static_cast<int>(cancellation_reason.final_status())) .Record(ukm::UkmRecorder::Get()); @@ -247,7 +252,7 @@ attributes.embedder_histogram_suffix); if (attributes.initiator_ukm_id != ukm::kInvalidSourceId) { // `initiator_ukm_id` must be valid only for the speculation rules. - CHECK_EQ(attributes.trigger_type, PrerenderTriggerType::kSpeculationRule); + CHECK(IsSpeculationRuleType(attributes.trigger_type)); ukm::builders::PrerenderPageLoad(attributes.initiator_ukm_id) .SetFinalStatus(static_cast<int>(PrerenderFinalStatus::kActivated)) .Record(ukm::UkmRecorder::Get());
diff --git a/content/browser/preloading/prerender/prerender_new_tab_handle.h b/content/browser/preloading/prerender/prerender_new_tab_handle.h index ceae2348..3b7b589 100644 --- a/content/browser/preloading/prerender/prerender_new_tab_handle.h +++ b/content/browser/preloading/prerender/prerender_new_tab_handle.h
@@ -56,6 +56,9 @@ // Returns PrerenderHost that `web_contents_` is hosting. PrerenderHost* GetPrerenderHostForTesting(); + // Returns PrerenderTriggerType. + PrerenderTriggerType trigger_type() const { return attributes_.trigger_type; } + private: PrerenderHostRegistry& GetPrerenderHostRegistry();
diff --git a/content/browser/preloading/prerender/prerender_trigger_type_impl.cc b/content/browser/preloading/prerender/prerender_trigger_type_impl.cc new file mode 100644 index 0000000..b041ac3 --- /dev/null +++ b/content/browser/preloading/prerender/prerender_trigger_type_impl.cc
@@ -0,0 +1,20 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/preloading/prerender/prerender_trigger_type_impl.h" + +namespace content { + +bool IsSpeculationRuleType(PrerenderTriggerType type) { + switch (type) { + case PrerenderTriggerType::kSpeculationRule: + [[fallthrough]]; + case PrerenderTriggerType::kSpeculationRuleFromIsolatedWorld: + return true; + case PrerenderTriggerType::kEmbedder: + return false; + } +} + +} // namespace content
diff --git a/content/browser/preloading/prerender/prerender_trigger_type_impl.h b/content/browser/preloading/prerender/prerender_trigger_type_impl.h new file mode 100644 index 0000000..69d62b2 --- /dev/null +++ b/content/browser/preloading/prerender/prerender_trigger_type_impl.h
@@ -0,0 +1,18 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_PRELOADING_PRERENDER_PRERENDER_TRIGGER_TYPE_IMPL_H_ +#define CONTENT_BROWSER_PRELOADING_PRERENDER_PRERENDER_TRIGGER_TYPE_IMPL_H_ + +#include "content/public/browser/prerender_trigger_type.h" + +namespace content { + +// Checks if the type is kSpeculationRule*. Recommends to use this function to +// keep the code robust against adding more trigger types in the future. +bool IsSpeculationRuleType(PrerenderTriggerType type); + +} // namespace content + +#endif // CONTENT_BROWSER_PRELOADING_PRERENDER_PRERENDER_TRIGGER_TYPE_IMPL_H_
diff --git a/content/browser/preloading/prerenderer_impl.cc b/content/browser/preloading/prerenderer_impl.cc index 673581d..8d0f3b3 100644 --- a/content/browser/preloading/prerenderer_impl.cc +++ b/content/browser/preloading/prerenderer_impl.cc
@@ -214,7 +214,11 @@ Referrer referrer(*(candidate->referrer)); PrerenderAttributes attributes( - candidate->url, PrerenderTriggerType::kSpeculationRule, + candidate->url, + candidate->injection_world != + blink::mojom::SpeculationInjectionWorld::kIsolated + ? PrerenderTriggerType::kSpeculationRule + : PrerenderTriggerType::kSpeculationRuleFromIsolatedWorld, /*embedder_histogram_suffix=*/"", referrer, rfhi.GetLastCommittedOrigin(), rfhi.GetProcess()->GetID(), web_contents->GetWeakPtr(), rfhi.GetFrameToken(), rfhi.GetFrameTreeNodeId(),
diff --git a/content/browser/renderer_host/input/passthrough_touch_event_queue.cc b/content/browser/renderer_host/input/passthrough_touch_event_queue.cc index 7e626cf7..7079e8b 100644 --- a/content/browser/renderer_host/input/passthrough_touch_event_queue.cc +++ b/content/browser/renderer_host/input/passthrough_touch_event_queue.cc
@@ -287,6 +287,8 @@ if (timeout_handler_) timeout_handler_->StartIfNecessary(*touch); + touch->event.GetModifiableEventLatencyMetadata().dispatched_to_renderer = + base::TimeTicks::Now(); if (wait_for_ack) outstanding_touches_.insert(*touch); client_->SendTouchEventImmediately(*touch);
diff --git a/content/browser/renderer_host/input/render_widget_host_latency_tracker.cc b/content/browser/renderer_host/input/render_widget_host_latency_tracker.cc index e706e1e5..f1aa2eb4 100644 --- a/content/browser/renderer_host/input/render_widget_host_latency_tracker.cc +++ b/content/browser/renderer_host/input/render_widget_host_latency_tracker.cc
@@ -94,7 +94,8 @@ void RenderWidgetHostLatencyTracker::OnInputEvent( const blink::WebInputEvent& event, - LatencyInfo* latency) { + LatencyInfo* latency, + ui::EventLatencyMetadata* event_latency_metadata) { DCHECK(latency); DCHECK_CURRENTLY_ON(BrowserThread::UI); @@ -135,9 +136,12 @@ ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, timestamp_original); } + base::TimeTicks begin_rwh_timestamp = base::TimeTicks::Now(); latency->AddLatencyNumberWithTraceName( ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, - GetTraceNameFromType(event.GetType())); + GetTraceNameFromType(event.GetType()), begin_rwh_timestamp); + event_latency_metadata->arrived_in_browser_main_timestamp = + begin_rwh_timestamp; if (event.GetType() == blink::WebInputEvent::Type::kGestureScrollBegin) { has_seen_first_gesture_scroll_update_ = false;
diff --git a/content/browser/renderer_host/input/render_widget_host_latency_tracker.h b/content/browser/renderer_host/input/render_widget_host_latency_tracker.h index a6acd1f..c1cca70 100644 --- a/content/browser/renderer_host/input/render_widget_host_latency_tracker.h +++ b/content/browser/renderer_host/input/render_widget_host_latency_tracker.h
@@ -35,7 +35,8 @@ // Called when an event is received by the RenderWidgetHost, prior to // that event being forwarded to the renderer (via the InputRouter). void OnInputEvent(const blink::WebInputEvent& event, - ui::LatencyInfo* latency); + ui::LatencyInfo* latency, + ui::EventLatencyMetadata* event_latency_metadata); // Populates the LatencyInfo with relevant entries for latency tracking, also // terminating latency tracking for events that did not trigger rendering and
diff --git a/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc b/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc index 38c9e17..39b431f 100644 --- a/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc +++ b/content/browser/renderer_host/input/render_widget_host_latency_tracker_unittest.cc
@@ -222,16 +222,22 @@ blink::WebMouseWheelEvent::kPhaseChanged); base::TimeTicks now = base::TimeTicks::Now(); wheel.SetTimeStamp(now); + ui::EventLatencyMetadata event_latency_metadata; ui::LatencyInfo wheel_latency(ui::SourceEventType::WHEEL); wheel_latency.AddLatencyNumberWithTimestamp( ui::INPUT_EVENT_LATENCY_FIRST_SCROLL_UPDATE_ORIGINAL_COMPONENT, now); AddFakeComponentsWithTimeStamp(*tracker(), &wheel_latency, now); AddRenderingScheduledComponent(&wheel_latency, rendering_on_main, now); - tracker()->OnInputEvent(wheel, &wheel_latency); + tracker()->OnInputEvent(wheel, &wheel_latency, &event_latency_metadata); + base::TimeTicks begin_rwh_timestamp; EXPECT_TRUE(wheel_latency.FindLatency( - ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, nullptr)); + ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, &begin_rwh_timestamp)); EXPECT_TRUE(wheel_latency.FindLatency( ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, nullptr)); + EXPECT_FALSE( + event_latency_metadata.arrived_in_browser_main_timestamp.is_null()); + EXPECT_EQ(event_latency_metadata.arrived_in_browser_main_timestamp, + begin_rwh_timestamp); tracker()->OnInputEventAck( wheel, &wheel_latency, blink::mojom::InputEventResultState::kNotConsumed); @@ -287,16 +293,22 @@ blink::WebMouseWheelEvent::kPhaseChanged); base::TimeTicks now = base::TimeTicks::Now(); wheel.SetTimeStamp(now); + ui::EventLatencyMetadata event_latency_metadata; ui::LatencyInfo wheel_latency(ui::SourceEventType::WHEEL); wheel_latency.AddLatencyNumberWithTimestamp( ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT, now); AddFakeComponentsWithTimeStamp(*tracker(), &wheel_latency, now); AddRenderingScheduledComponent(&wheel_latency, rendering_on_main, now); - tracker()->OnInputEvent(wheel, &wheel_latency); + tracker()->OnInputEvent(wheel, &wheel_latency, &event_latency_metadata); + base::TimeTicks begin_rwh_timestamp; EXPECT_TRUE(wheel_latency.FindLatency( - ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, nullptr)); + ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, &begin_rwh_timestamp)); EXPECT_TRUE(wheel_latency.FindLatency( ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, nullptr)); + EXPECT_FALSE( + event_latency_metadata.arrived_in_browser_main_timestamp.is_null()); + EXPECT_EQ(event_latency_metadata.arrived_in_browser_main_timestamp, + begin_rwh_timestamp); tracker()->OnInputEventAck( wheel, &wheel_latency, blink::mojom::InputEventResultState::kNotConsumed); @@ -350,13 +362,19 @@ base::TimeTicks now = base::TimeTicks::Now(); scroll.SetTimeStamp(now); ui::LatencyInfo scroll_latency(ui::SourceEventType::INERTIAL); + ui::EventLatencyMetadata event_latency_metadata; AddFakeComponentsWithTimeStamp(*tracker(), &scroll_latency, now); AddRenderingScheduledComponent(&scroll_latency, rendering_on_main, now); - tracker()->OnInputEvent(scroll, &scroll_latency); + tracker()->OnInputEvent(scroll, &scroll_latency, &event_latency_metadata); + base::TimeTicks begin_rwh_timestamp; EXPECT_TRUE(scroll_latency.FindLatency( - ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, nullptr)); + ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, &begin_rwh_timestamp)); EXPECT_TRUE(scroll_latency.FindLatency( ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, nullptr)); + EXPECT_FALSE( + event_latency_metadata.arrived_in_browser_main_timestamp.is_null()); + EXPECT_EQ(event_latency_metadata.arrived_in_browser_main_timestamp, + begin_rwh_timestamp); tracker()->OnInputEventAck( scroll, &scroll_latency, blink::mojom::InputEventResultState::kNotConsumed); @@ -396,13 +414,19 @@ base::TimeTicks now = base::TimeTicks::Now(); scroll.SetTimeStamp(now); ui::LatencyInfo scroll_latency; + ui::EventLatencyMetadata event_latency_metadata; AddFakeComponentsWithTimeStamp(*tracker(), &scroll_latency, now); AddRenderingScheduledComponent(&scroll_latency, rendering_on_main, now); - tracker()->OnInputEvent(scroll, &scroll_latency); + tracker()->OnInputEvent(scroll, &scroll_latency, &event_latency_metadata); + base::TimeTicks begin_rwh_timestamp; EXPECT_TRUE(scroll_latency.FindLatency( - ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, nullptr)); + ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, &begin_rwh_timestamp)); EXPECT_TRUE(scroll_latency.FindLatency( ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, nullptr)); + EXPECT_FALSE( + event_latency_metadata.arrived_in_browser_main_timestamp.is_null()); + EXPECT_EQ(event_latency_metadata.arrived_in_browser_main_timestamp, + begin_rwh_timestamp); tracker()->OnInputEventAck( scroll, &scroll_latency, blink::mojom::InputEventResultState::kNotConsumed); @@ -412,17 +436,23 @@ blink::SyntheticWebTouchEvent touch; touch.PressPoint(0, 0); touch.PressPoint(1, 1); + ui::EventLatencyMetadata event_latency_metadata; ui::LatencyInfo touch_latency(ui::SourceEventType::TOUCH); base::TimeTicks now = base::TimeTicks::Now(); touch_latency.AddLatencyNumberWithTimestamp( ui::INPUT_EVENT_LATENCY_FIRST_SCROLL_UPDATE_ORIGINAL_COMPONENT, now); AddFakeComponentsWithTimeStamp(*tracker(), &touch_latency, now); AddRenderingScheduledComponent(&touch_latency, rendering_on_main, now); - tracker()->OnInputEvent(touch, &touch_latency); + tracker()->OnInputEvent(touch, &touch_latency, &event_latency_metadata); + base::TimeTicks begin_rwh_timestamp; EXPECT_TRUE(touch_latency.FindLatency( - ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, nullptr)); + ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, &begin_rwh_timestamp)); EXPECT_TRUE(touch_latency.FindLatency( ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, nullptr)); + EXPECT_FALSE( + event_latency_metadata.arrived_in_browser_main_timestamp.is_null()); + EXPECT_EQ(event_latency_metadata.arrived_in_browser_main_timestamp, + begin_rwh_timestamp); tracker()->OnInputEventAck( touch, &touch_latency, blink::mojom::InputEventResultState::kNotConsumed); @@ -476,9 +506,10 @@ base::TimeTicks now = base::TimeTicks::Now(); scroll.SetTimeStamp(now); ui::LatencyInfo scroll_latency; + ui::EventLatencyMetadata event_latency_metadata; AddFakeComponentsWithTimeStamp(*tracker(), &scroll_latency, now); AddRenderingScheduledComponent(&scroll_latency, rendering_on_main, now); - tracker()->OnInputEvent(scroll, &scroll_latency); + tracker()->OnInputEvent(scroll, &scroll_latency, &event_latency_metadata); EXPECT_TRUE(scroll_latency.FindLatency( ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, nullptr)); EXPECT_TRUE(scroll_latency.FindLatency( @@ -493,16 +524,22 @@ touch.PressPoint(0, 0); touch.PressPoint(1, 1); ui::LatencyInfo touch_latency(ui::SourceEventType::TOUCH); + ui::EventLatencyMetadata event_latency_metadata; base::TimeTicks now = base::TimeTicks::Now(); touch_latency.AddLatencyNumberWithTimestamp( ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT, now); AddFakeComponentsWithTimeStamp(*tracker(), &touch_latency, now); AddRenderingScheduledComponent(&touch_latency, rendering_on_main, now); - tracker()->OnInputEvent(touch, &touch_latency); + tracker()->OnInputEvent(touch, &touch_latency, &event_latency_metadata); + base::TimeTicks begin_rwh_timestamp; EXPECT_TRUE(touch_latency.FindLatency( - ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, nullptr)); + ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, &begin_rwh_timestamp)); EXPECT_TRUE(touch_latency.FindLatency( ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, nullptr)); + EXPECT_FALSE( + event_latency_metadata.arrived_in_browser_main_timestamp.is_null()); + EXPECT_EQ(event_latency_metadata.arrived_in_browser_main_timestamp, + begin_rwh_timestamp); tracker()->OnInputEventAck( touch, &touch_latency, blink::mojom::InputEventResultState::kNotConsumed); @@ -558,14 +595,21 @@ const bool on_main[] = {true, false}; for (bool on_main_thread : on_main) { ui::LatencyInfo scrollbar_latency(ui::SourceEventType::SCROLLBAR); + ui::EventLatencyMetadata event_latency_metadata; AddFakeComponentsWithTimeStamp(*tracker(), &scrollbar_latency, now); scrollbar_latency.AddLatencyNumberWithTimestamp(component, now); AddRenderingScheduledComponent(&scrollbar_latency, on_main_thread, now); - tracker()->OnInputEvent(mouse_move, &scrollbar_latency); + tracker()->OnInputEvent(mouse_move, &scrollbar_latency, + &event_latency_metadata); + base::TimeTicks begin_rwh_timestamp; EXPECT_TRUE(scrollbar_latency.FindLatency( - ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, nullptr)); + ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, &begin_rwh_timestamp)); EXPECT_TRUE(scrollbar_latency.FindLatency( ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, nullptr)); + EXPECT_FALSE( + event_latency_metadata.arrived_in_browser_main_timestamp.is_null()); + EXPECT_EQ(event_latency_metadata.arrived_in_browser_main_timestamp, + begin_rwh_timestamp); tracker()->OnInputEventAck( mouse_move, &scrollbar_latency, blink::mojom::InputEventResultState::kNotConsumed); @@ -594,13 +638,14 @@ base::TimeTicks now = base::TimeTicks::Now(); scroll.SetTimeStamp(now); ui::LatencyInfo scroll_latency; + ui::EventLatencyMetadata event_latency_metadata; scroll_latency.set_source_event_type( source_device == blink::WebGestureDevice::kTouchscreen ? ui::SourceEventType::TOUCH : ui::SourceEventType::WHEEL); AddFakeComponentsWithTimeStamp(*tracker(), &scroll_latency, now); AddRenderingScheduledComponent(&scroll_latency, rendering_on_main, now); - tracker()->OnInputEvent(scroll, &scroll_latency); + tracker()->OnInputEvent(scroll, &scroll_latency, &event_latency_metadata); tracker()->OnInputEventAck( scroll, &scroll_latency, blink::mojom::InputEventResultState::kNoConsumerExists); @@ -613,11 +658,18 @@ auto scroll_begin = blink::SyntheticWebGestureEventBuilder::BuildScrollBegin( 5, -5, blink::WebGestureDevice::kTouchscreen); ui::LatencyInfo scroll_latency; + ui::EventLatencyMetadata event_latency_metadata; scroll_latency.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT); - tracker()->OnInputEvent(scroll_begin, &scroll_latency); + tracker()->OnInputEvent(scroll_begin, &scroll_latency, + &event_latency_metadata); + base::TimeTicks begin_rwh_timestamp; EXPECT_TRUE(scroll_latency.FindLatency( - ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, nullptr)); + ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, &begin_rwh_timestamp)); EXPECT_EQ(2U, scroll_latency.latency_components().size()); + EXPECT_FALSE( + event_latency_metadata.arrived_in_browser_main_timestamp.is_null()); + EXPECT_EQ(event_latency_metadata.arrived_in_browser_main_timestamp, + begin_rwh_timestamp); // The first GestureScrollUpdate should be provided with // INPUT_EVENT_LATENCY_FIRST_SCROLL_UPDATE_ORIGINAL_COMPONENT. @@ -625,15 +677,22 @@ blink::SyntheticWebGestureEventBuilder::BuildScrollUpdate( 5.f, -5.f, 0, blink::WebGestureDevice::kTouchscreen); scroll_latency = ui::LatencyInfo(); + event_latency_metadata = ui::EventLatencyMetadata(); scroll_latency.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT); - tracker()->OnInputEvent(first_scroll_update, &scroll_latency); + tracker()->OnInputEvent(first_scroll_update, &scroll_latency, + &event_latency_metadata); + begin_rwh_timestamp = base::TimeTicks(); EXPECT_TRUE(scroll_latency.FindLatency( - ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, nullptr)); + ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, &begin_rwh_timestamp)); EXPECT_TRUE(scroll_latency.FindLatency( ui::INPUT_EVENT_LATENCY_FIRST_SCROLL_UPDATE_ORIGINAL_COMPONENT, nullptr)); EXPECT_FALSE(scroll_latency.FindLatency( ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT, nullptr)); EXPECT_EQ(3U, scroll_latency.latency_components().size()); + EXPECT_FALSE( + event_latency_metadata.arrived_in_browser_main_timestamp.is_null()); + EXPECT_EQ(event_latency_metadata.arrived_in_browser_main_timestamp, + begin_rwh_timestamp); // Subsequent GestureScrollUpdates should be provided with // INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT. @@ -641,15 +700,22 @@ blink::SyntheticWebGestureEventBuilder::BuildScrollUpdate( -5.f, 5.f, 0, blink::WebGestureDevice::kTouchscreen); scroll_latency = ui::LatencyInfo(); + event_latency_metadata = ui::EventLatencyMetadata(); scroll_latency.AddLatencyNumber(ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT); - tracker()->OnInputEvent(scroll_update, &scroll_latency); + tracker()->OnInputEvent(scroll_update, &scroll_latency, + &event_latency_metadata); + begin_rwh_timestamp = base::TimeTicks(); EXPECT_TRUE(scroll_latency.FindLatency( - ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, nullptr)); + ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, &begin_rwh_timestamp)); EXPECT_FALSE(scroll_latency.FindLatency( ui::INPUT_EVENT_LATENCY_FIRST_SCROLL_UPDATE_ORIGINAL_COMPONENT, nullptr)); EXPECT_TRUE(scroll_latency.FindLatency( ui::INPUT_EVENT_LATENCY_SCROLL_UPDATE_ORIGINAL_COMPONENT, nullptr)); EXPECT_EQ(3U, scroll_latency.latency_components().size()); + EXPECT_FALSE( + event_latency_metadata.arrived_in_browser_main_timestamp.is_null()); + EXPECT_EQ(event_latency_metadata.arrived_in_browser_main_timestamp, + begin_rwh_timestamp); } TEST_F(RenderWidgetHostLatencyTrackerTest, KeyEndToEndLatency) {
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc index f6c7102..5e8403e 100644 --- a/content/browser/renderer_host/render_widget_host_impl.cc +++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -1497,7 +1497,9 @@ } MouseEventWithLatencyInfo mouse_with_latency(mouse_event, latency); - DispatchInputEventWithLatencyInfo(mouse_event, &mouse_with_latency.latency); + DispatchInputEventWithLatencyInfo( + mouse_with_latency.event, &mouse_with_latency.latency, + &mouse_with_latency.event.GetModifiableEventLatencyMetadata()); input_router_->SendMouseEvent( mouse_with_latency, base::BindOnce(&RenderWidgetHostImpl::OnMouseEventAck, weak_factory_.GetWeakPtr())); @@ -1523,7 +1525,9 @@ return; MouseWheelEventWithLatencyInfo wheel_with_latency(wheel_event, latency); - DispatchInputEventWithLatencyInfo(wheel_event, &wheel_with_latency.latency); + DispatchInputEventWithLatencyInfo( + wheel_with_latency.event, &wheel_with_latency.latency, + &wheel_with_latency.event.GetModifiableEventLatencyMetadata()); input_router_->SendWheelEvent(wheel_with_latency); } @@ -1643,8 +1647,9 @@ return; GestureEventWithLatencyInfo gesture_with_latency(gesture_event, latency); - DispatchInputEventWithLatencyInfo(gesture_event, - &gesture_with_latency.latency); + DispatchInputEventWithLatencyInfo( + gesture_with_latency.event, &gesture_with_latency.latency, + &gesture_with_latency.event.GetModifiableEventLatencyMetadata()); input_router_->SendGestureEvent(gesture_with_latency); } @@ -1662,7 +1667,9 @@ // ignored if appropriate in FilterInputEvent(). TouchEventWithLatencyInfo touch_with_latency(touch_event, latency); - DispatchInputEventWithLatencyInfo(touch_event, &touch_with_latency.latency); + DispatchInputEventWithLatencyInfo( + touch_with_latency.event, &touch_with_latency.latency, + &touch_with_latency.event.GetModifiableEventLatencyMetadata()); input_router_->SendTouchEvent(touch_with_latency); } @@ -1771,7 +1778,9 @@ NativeWebKeyboardEventWithLatencyInfo key_event_with_latency(key_event, latency); key_event_with_latency.event.is_browser_shortcut = is_shortcut; - DispatchInputEventWithLatencyInfo(key_event, &key_event_with_latency.latency); + DispatchInputEventWithLatencyInfo( + key_event_with_latency.event, &key_event_with_latency.latency, + &key_event_with_latency.event.GetModifiableEventLatencyMetadata()); // TODO(foolip): |InputRouter::SendKeyboardEvent()| may filter events, in // which the commands will be treated as belonging to the next key event. // WidgetInputHandler::SetEditCommandsForNextKeyEvent should only be sent if @@ -3245,8 +3254,9 @@ void RenderWidgetHostImpl::DispatchInputEventWithLatencyInfo( const blink::WebInputEvent& event, - ui::LatencyInfo* latency) { - latency_tracker_.OnInputEvent(event, latency); + ui::LatencyInfo* latency, + ui::EventLatencyMetadata* event_latency_metadata) { + latency_tracker_.OnInputEvent(event, latency, event_latency_metadata); AddPendingUserActivation(event); for (auto& observer : input_event_observers_) observer.OnInputEvent(event);
diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h index dd25d63..76a2bd4 100644 --- a/content/browser/renderer_host/render_widget_host_impl.h +++ b/content/browser/renderer_host/render_widget_host_impl.h
@@ -1079,8 +1079,10 @@ void OnInvalidInputEventSource() override; // Dispatch input events with latency information - void DispatchInputEventWithLatencyInfo(const blink::WebInputEvent& event, - ui::LatencyInfo* latency); + void DispatchInputEventWithLatencyInfo( + const blink::WebInputEvent& event, + ui::LatencyInfo* latency, + ui::EventLatencyMetadata* event_latency_metadata); void WindowSnapshotReachedScreen(int snapshot_id);
diff --git a/content/browser/renderer_host/render_widget_host_unittest.cc b/content/browser/renderer_host/render_widget_host_unittest.cc index 68d74a9..539a8b0 100644 --- a/content/browser/renderer_host/render_widget_host_unittest.cc +++ b/content/browser/renderer_host/render_widget_host_unittest.cc
@@ -2351,8 +2351,10 @@ NativeWebKeyboardEvent native_event(WebInputEvent::Type::kChar, 0, GetNextSimulatedEventTime()); ui::LatencyInfo latency_info = ui::LatencyInfo(); + ui::EventLatencyMetadata event_latency_metadata; EXPECT_CALL(observer, OnInputEvent(_)).Times(1); - host_->DispatchInputEventWithLatencyInfo(native_event, &latency_info); + host_->DispatchInputEventWithLatencyInfo(native_event, &latency_info, + &event_latency_metadata); // Remove InputEventObserver. host_->RemoveInputEventObserver(&observer); @@ -2360,7 +2362,9 @@ // Confirm InputEventObserver is removed. EXPECT_CALL(observer, OnInputEvent(_)).Times(0); latency_info = ui::LatencyInfo(); - host_->DispatchInputEventWithLatencyInfo(native_event, &latency_info); + event_latency_metadata = ui::EventLatencyMetadata(); + host_->DispatchInputEventWithLatencyInfo(native_event, &latency_info, + &event_latency_metadata); } #if BUILDFLAG(IS_ANDROID)
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc index 178efd7..5adf34a 100644 --- a/content/browser/renderer_host/render_widget_host_view_android.cc +++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -2112,9 +2112,20 @@ blink::mojom::InputEventResultState ack_result) { const bool event_consumed = ack_result == blink::mojom::InputEventResultState::kConsumed; + // |is_source_touch_event_set_non_blocking| defines a blocking behaviour of + // the future inputs. + const bool is_source_touch_event_set_non_blocking = + InputEventResultStateIsSetNonBlocking(ack_result); + // |was_touch_blocked| indicates whether the current event was dispatched + // blocking to the Renderer. + const bool was_touch_blocked = + ui::WebInputEventTraits::ShouldBlockEventStream(touch.event); gesture_provider_.OnTouchEventAck( touch.event.unique_touch_event_id, event_consumed, - InputEventResultStateIsSetNonBlocking(ack_result)); + is_source_touch_event_set_non_blocking, + was_touch_blocked + ? absl::make_optional(touch.event.GetEventLatencyMetadata()) + : absl::nullopt); if (touch.event.touch_start_or_first_touch_move && event_consumed && host()->delegate() && host()->delegate()->GetInputEventRouter()) { host()
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn index a92d133..a164080 100644 --- a/content/common/BUILD.gn +++ b/content/common/BUILD.gn
@@ -628,6 +628,7 @@ "//ui/color:mojom", "//ui/display/mojom", "//ui/events/mojom", + "//ui/events/mojom:event_latency_metadata_mojom", "//ui/gfx/geometry/mojom", "//ui/gfx/image/mojom", "//ui/gfx/mojom",
diff --git a/content/public/browser/prerender_trigger_type.h b/content/public/browser/prerender_trigger_type.h index 223cccb..8012409 100644 --- a/content/public/browser/prerender_trigger_type.h +++ b/content/public/browser/prerender_trigger_type.h
@@ -10,6 +10,8 @@ enum class PrerenderTriggerType { // https://wicg.github.io/nav-speculation/prerendering.html#speculation-rules kSpeculationRule, + // Same as kSpeculationRule but triggered in isolated worlds like Extensions. + kSpeculationRuleFromIsolatedWorld, // Trigger used by content embedders. kEmbedder, };
diff --git a/content/public/test/prerender_test_util.cc b/content/public/test/prerender_test_util.cc index eafbc1cc..a436d11b 100644 --- a/content/public/test/prerender_test_util.cc +++ b/content/public/test/prerender_test_util.cc
@@ -304,11 +304,12 @@ WaitForPrerenderLoadCompletion(*GetWebContents(), gurl); } -int PrerenderTestHelper::AddPrerender(const GURL& prerendering_url) { +int PrerenderTestHelper::AddPrerender(const GURL& prerendering_url, + int32_t world_id) { TRACE_EVENT("test", "PrerenderTestHelper::AddPrerender", "prerendering_url", prerendering_url); EXPECT_TRUE(content::BrowserThread::CurrentlyOn(BrowserThread::UI)); - AddPrerenderAsync(prerendering_url); + AddPrerenderAsync(prerendering_url, world_id); WaitForPrerenderLoadCompletion(prerendering_url); int host_id = GetHostForUrl(prerendering_url); @@ -316,17 +317,23 @@ return host_id; } -void PrerenderTestHelper::AddPrerenderAsync(const GURL& prerendering_url) { +void PrerenderTestHelper::AddPrerenderAsync(const GURL& prerendering_url, + int32_t world_id) { TRACE_EVENT("test", "PrerenderTestHelper::AddPrerenderAsync", "prerendering_url", prerendering_url); EXPECT_TRUE(content::BrowserThread::CurrentlyOn(BrowserThread::UI)); std::string script = JsReplace(kAddSpeculationRuleScript, prerendering_url); - // Have to use ExecuteJavaScriptForTests instead of ExecJs/EvalJs here, - // because some test pages have ContentSecurityPolicy and EvalJs cannot work - // with it. See the quick migration guide for EvalJs for more information. - GetWebContents()->GetPrimaryMainFrame()->ExecuteJavaScriptForTests( - base::UTF8ToUTF16(script), base::NullCallback()); + if (world_id == ISOLATED_WORLD_ID_GLOBAL) { + // Have to use ExecuteJavaScriptForTests instead of ExecJs/EvalJs here, + // because some test pages have ContentSecurityPolicy and EvalJs cannot work + // with it. See the quick migration guide for EvalJs for more information. + GetWebContents()->GetPrimaryMainFrame()->ExecuteJavaScriptForTests( + base::UTF8ToUTF16(script), base::NullCallback()); + } else { + GetWebContents()->GetPrimaryMainFrame()->ExecuteJavaScriptInIsolatedWorld( + base::UTF8ToUTF16(script), base::NullCallback(), world_id); + } } void PrerenderTestHelper::AddMultiplePrerenderAsync( @@ -544,6 +551,10 @@ case content::PrerenderTriggerType::kSpeculationRule: DCHECK(embedder_suffix.empty()); return std::string(histogram_base_name) + ".SpeculationRule"; + case content::PrerenderTriggerType::kSpeculationRuleFromIsolatedWorld: + DCHECK(embedder_suffix.empty()); + return std::string(histogram_base_name) + + ".SpeculationRuleFromIsolatedWorld"; case content::PrerenderTriggerType::kEmbedder: DCHECK(!embedder_suffix.empty()); return std::string(histogram_base_name) + ".Embedder_" + embedder_suffix;
diff --git a/content/public/test/prerender_test_util.h b/content/public/test/prerender_test_util.h index 2233d56d..e24a27c 100644 --- a/content/public/test/prerender_test_util.h +++ b/content/public/test/prerender_test_util.h
@@ -9,6 +9,7 @@ #include "base/test/scoped_feature_list.h" #include "content/public/browser/prerender_trigger_type.h" #include "content/public/browser/render_frame_host.h" +#include "content/public/common/isolated_world_ids.h" #include "content/public/test/browser_test_utils.h" #include "net/test/embedded_test_server/embedded_test_server.h" #include "net/test/embedded_test_server/http_request.h" @@ -127,8 +128,10 @@ // // AddPrerenderAsync() is the same as AddPrerender(), but does not wait until // the completion of prerendering. - int AddPrerender(const GURL& prerendering_url); - void AddPrerenderAsync(const GURL& prerendering_url); + int AddPrerender(const GURL& prerendering_url, + int32_t world_id = ISOLATED_WORLD_ID_GLOBAL); + void AddPrerenderAsync(const GURL& prerendering_url, + int32_t world_id = ISOLATED_WORLD_ID_GLOBAL); void AddPrerenderWithTargetHintAsync(const GURL& prerendering_url, const std::string& target_hint);
diff --git a/ios/chrome/browser/permissions/permissions_tab_helper.mm b/ios/chrome/browser/permissions/permissions_tab_helper.mm index c2e7999b..24c2aef 100644 --- a/ios/chrome/browser/permissions/permissions_tab_helper.mm +++ b/ios/chrome/browser/permissions/permissions_tab_helper.mm
@@ -32,7 +32,11 @@ OverlayResponse* response) API_AVAILABLE(ios(15.0)) { PermissionsDialogResponse* dialog_response = response ? response->GetInfo<PermissionsDialogResponse>() : nullptr; - handler(dialog_response && dialog_response->capture_allow()); + web::PermissionDecision decision = + dialog_response && dialog_response->capture_allow() + ? web::PermissionDecisionGrant + : web::PermissionDecisionDeny; + handler(decision); } } // namespace
diff --git a/ios/chrome/browser/search_engines/BUILD.gn b/ios/chrome/browser/search_engines/BUILD.gn index 58a78c6..8979c3f 100644 --- a/ios/chrome/browser/search_engines/BUILD.gn +++ b/ios/chrome/browser/search_engines/BUILD.gn
@@ -139,3 +139,42 @@ primary_script = "resources/search_engine.js" sources = [ "resources/search_engine.js" ] } + +source_set("eg_app_support+eg2") { + configs += [ + "//build/config/compiler:enable_arc", + "//build/config/ios:xctest_config", + ] + testonly = true + + sources = [ + "search_engines_app_interface.h", + "search_engines_app_interface.mm", + ] + deps = [ + "//base", + "//base/test:test_support", + "//components/search_engines", + "//ios/chrome/browser/search_engines", + "//ios/chrome/test/app:test_support", + "//ios/testing/earl_grey:eg_app_support+eg2", + "//ios/third_party/earl_grey2:app_framework+link", + "//testing/gmock", + "//testing/gtest:gtest", + ] +} + +source_set("eg_test_support+eg2") { + configs += [ + "//build/config/compiler:enable_arc", + "//build/config/ios:xctest_config", + ] + testonly = true + + sources = [ + "search_engines_app_interface.h", + "search_engines_app_interface_stub.mm", + ] + + deps = [ "//ios/testing/earl_grey:eg_test_support+eg2" ] +}
diff --git a/ios/chrome/browser/search_engines/search_engines_app_interface.h b/ios/chrome/browser/search_engines/search_engines_app_interface.h new file mode 100644 index 0000000..ccd1834086 --- /dev/null +++ b/ios/chrome/browser/search_engines/search_engines_app_interface.h
@@ -0,0 +1,30 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IOS_CHROME_BROWSER_SEARCH_ENGINES_SEARCH_ENGINES_APP_INTERFACE_H_ +#define IOS_CHROME_BROWSER_SEARCH_ENGINES_SEARCH_ENGINES_APP_INTERFACE_H_ + +#import <UIKit/UIKit.h> + +// App interface for the search engines. +@interface SearchEnginesAppInterface : NSObject + +// Returns the short name of the default search engine. ++ (NSString*)defaultSearchEngine; + +// Resets the default search engine to `defaultSearchEngine`. +// `defaultSearchEngine` should be its short name. ++ (void)setSearchEngineTo:(NSString*)defaultSearchEngine; + +// Adds a new Search engine an optionally sets it as default. ++ (void)addSearchEngineWithName:(NSString*)name + URL:(NSString*)URL + setDefault:(BOOL)setDefault; + +// Removes the search engine. ++ (void)removeSearchEngineWithName:(NSString*)name; + +@end + +#endif // IOS_CHROME_BROWSER_SEARCH_ENGINES_SEARCH_ENGINES_APP_INTERFACE_H_
diff --git a/ios/chrome/browser/search_engines/search_engines_app_interface.mm b/ios/chrome/browser/search_engines/search_engines_app_interface.mm new file mode 100644 index 0000000..765b83f --- /dev/null +++ b/ios/chrome/browser/search_engines/search_engines_app_interface.mm
@@ -0,0 +1,80 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "ios/chrome/browser/search_engines/search_engines_app_interface.h" + +#import "base/strings/sys_string_conversions.h" +#import "components/search_engines/template_url.h" +#import "components/search_engines/template_url_service.h" +#import "ios/chrome/browser/search_engines/template_url_service_factory.h" +#import "ios/chrome/test/app/chrome_test_util.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +@implementation SearchEnginesAppInterface + ++ (NSString*)defaultSearchEngine { + // Get the default Search Engine. + ChromeBrowserState* browser_state = + chrome_test_util::GetOriginalBrowserState(); + TemplateURLService* service = + ios::TemplateURLServiceFactory::GetForBrowserState(browser_state); + const TemplateURL* default_provider = service->GetDefaultSearchProvider(); + DCHECK(default_provider); + return base::SysUTF16ToNSString(default_provider->short_name()); +} + ++ (void)setSearchEngineTo:(NSString*)defaultSearchEngine { + std::u16string defaultSearchEngineString = + base::SysNSStringToUTF16(defaultSearchEngine); + // Set the search engine back to the default in case the test fails before + // cleaning it up. + ChromeBrowserState* browser_state = + chrome_test_util::GetOriginalBrowserState(); + TemplateURLService* service = + ios::TemplateURLServiceFactory::GetForBrowserState(browser_state); + std::vector<TemplateURL*> urls = service->GetTemplateURLs(); + + for (auto iter = urls.begin(); iter != urls.end(); ++iter) { + if (defaultSearchEngineString == (*iter)->short_name()) { + service->SetUserSelectedDefaultSearchProvider(*iter); + } + } +} + ++ (void)addSearchEngineWithName:(NSString*)name + URL:(NSString*)URL + setDefault:(BOOL)setDefault { + ChromeBrowserState* browser_state = + chrome_test_util::GetOriginalBrowserState(); + TemplateURLService* url_service = + ios::TemplateURLServiceFactory::GetForBrowserState(browser_state); + TemplateURLData data; + data.SetShortName(base::SysNSStringToUTF16(name)); + data.SetURL(base::SysNSStringToUTF8(URL)); + url_service->Add(std::make_unique<TemplateURL>(data)); + if (setDefault) { + [SearchEnginesAppInterface setSearchEngineTo:name]; + } +} + ++ (void)removeSearchEngineWithName:(NSString*)name { + ChromeBrowserState* browser_state = + chrome_test_util::GetOriginalBrowserState(); + TemplateURLService* url_service = + ios::TemplateURLServiceFactory::GetForBrowserState(browser_state); + std::vector<TemplateURL*> urls = url_service->GetTemplateURLs(); + std::u16string utfName = base::SysNSStringToUTF16(name); + + for (auto iter = urls.begin(); iter != urls.end(); ++iter) { + if (utfName == (*iter)->short_name()) { + url_service->Remove(*iter); + return; + } + } +} + +@end
diff --git a/ios/chrome/browser/search_engines/search_engines_app_interface_stub.mm b/ios/chrome/browser/search_engines/search_engines_app_interface_stub.mm new file mode 100644 index 0000000..e269de3 --- /dev/null +++ b/ios/chrome/browser/search_engines/search_engines_app_interface_stub.mm
@@ -0,0 +1,13 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "ios/chrome/browser/search_engines/search_engines_app_interface.h" + +#import "ios/testing/earl_grey/earl_grey_test.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +GREY_STUB_CLASS_IN_APP_MAIN_QUEUE(SearchEnginesAppInterface)
diff --git a/ios/chrome/browser/search_engines/search_engines_util.cc b/ios/chrome/browser/search_engines/search_engines_util.cc index a2fbfe6..61068d7 100644 --- a/ios/chrome/browser/search_engines/search_engines_util.cc +++ b/ios/chrome/browser/search_engines/search_engines_util.cc
@@ -32,8 +32,6 @@ std::vector<std::unique_ptr<TemplateURLData>> new_engines = TemplateURLPrepopulateData::GetPrepopulatedEngines(nullptr, &default_engine_index); - DCHECK(default_engine_index == 0); - DCHECK(new_engines[0]->prepopulate_id == kGoogleEnginePrepopulatedId); // The aim is to replace the old search engines with the new ones. // It is not possible to remove all of them, because removing the current // selected engine is not allowed.
diff --git a/ios/chrome/browser/shared/coordinator/scene/scene_controller.mm b/ios/chrome/browser/shared/coordinator/scene/scene_controller.mm index 73745aab..018373d8 100644 --- a/ios/chrome/browser/shared/coordinator/scene/scene_controller.mm +++ b/ios/chrome/browser/shared/coordinator/scene/scene_controller.mm
@@ -111,6 +111,7 @@ #import "ios/chrome/browser/ui/default_promo/default_browser_promo_non_modal_scheduler.h" #import "ios/chrome/browser/ui/first_run/orientation_limiting_navigation_controller.h" #import "ios/chrome/browser/ui/history/history_coordinator.h" +#import "ios/chrome/browser/ui/history/history_coordinator_delegate.h" #import "ios/chrome/browser/ui/incognito_interstitial/incognito_interstitial_coordinator.h" #import "ios/chrome/browser/ui/incognito_interstitial/incognito_interstitial_coordinator_delegate.h" #import "ios/chrome/browser/ui/incognito_reauth/incognito_reauth_scene_agent.h" @@ -239,6 +240,7 @@ PolicyWatcherBrowserAgentObserving, SettingsNavigationControllerDelegate, SceneUIProvider, + HistoryCoordinatorDelegate, SceneURLLoadingServiceDelegate, TabGridCoordinatorDelegate, WebStateListObserving, @@ -1405,6 +1407,7 @@ self.historyCoordinator.loadStrategy = self.currentInterface.incognito ? UrlLoadStrategy::ALWAYS_IN_INCOGNITO : UrlLoadStrategy::NORMAL; + self.historyCoordinator.delegate = self; [self.historyCoordinator start]; } @@ -3379,4 +3382,17 @@ return self.mainCoordinator.activeViewController; } +#pragma mark - HistoryCoordinatorDelegate + +- (void)closeHistoryWithCompletion:(ProceduralBlock)completion { + __weak __typeof(self) weakSelf = self; + [self.historyCoordinator dismissWithCompletion:^{ + if (completion) { + completion(); + } + [weakSelf.historyCoordinator stop]; + weakSelf.historyCoordinator = nil; + }]; +} + @end
diff --git a/ios/chrome/browser/shared/public/commands/browser_commands.h b/ios/chrome/browser/shared/public/commands/browser_commands.h index f4434b3d..7d95310d 100644 --- a/ios/chrome/browser/shared/public/commands/browser_commands.h +++ b/ios/chrome/browser/shared/public/commands/browser_commands.h
@@ -8,16 +8,10 @@ #import <Foundation/Foundation.h> #import <UIKit/UIKit.h> -@class ReadingListAddCommand; - // Protocol for commands that will generally be handled by the "current tab", // which in practice is the BrowserViewController instance displaying the tab. @protocol BrowserCommands <NSObject> -// Adds a page to the reading list using data in `command`. -// TODO(crbug.com/1272540): Remove this command. -- (void)addToReadingList:(ReadingListAddCommand*)command; - // Prepares the browser to display the overflow menu. - (void)prepareForOverflowMenuPresentation;
diff --git a/ios/chrome/browser/sync/sync_setup_service.cc b/ios/chrome/browser/sync/sync_setup_service.cc index 68ed0b1..6c4075c4 100644 --- a/ios/chrome/browser/sync/sync_setup_service.cc +++ b/ios/chrome/browser/sync/sync_setup_service.cc
@@ -116,7 +116,8 @@ } bool SyncSetupService::IsSyncRequested() const { - return sync_service_->GetUserSettings()->IsSyncRequested(); + return !sync_service_->GetDisableReasons().Has( + syncer::SyncService::DISABLE_REASON_USER_CHOICE); } bool SyncSetupService::CanSyncFeatureStart() const { @@ -127,20 +128,6 @@ return sync_service_->GetUserSettings()->IsEncryptEverythingEnabled(); } -bool SyncSetupService::IsInitialSetupOngoing() { - // Sync initial setup is considered to finished iff: - // 1. User is signed in with sync enabled and the sync setup was completed. - // OR - // 2. User is not signed in or has disabled sync. - // Otherwise we consider that the initial setup is still pending. - // Note that if the user visits the Advanced Settings during the opt-in flow, - // the Sync consent is not granted yet. In this case, IsSyncRequested() is - // set to true, indicating that the sync was requested but the initial setup - // has not been finished yet. - return IsSyncRequested() && - !sync_service_->GetUserSettings()->IsFirstSetupComplete(); -} - void SyncSetupService::PrepareForFirstSyncSetup() { if (!sync_blocker_) sync_blocker_ = sync_service_->GetSetupInProgressHandle();
diff --git a/ios/chrome/browser/sync/sync_setup_service.h b/ios/chrome/browser/sync/sync_setup_service.h index 5351412..edcdb9b 100644 --- a/ios/chrome/browser/sync/sync_setup_service.h +++ b/ios/chrome/browser/sync/sync_setup_service.h
@@ -46,7 +46,7 @@ static syncer::ModelType GetModelType(SyncableDatatype datatype); // Returns whether the user wants Sync to run. - // TODO(crbug.com/1291946): Callers should typically use CanSyncFeatureStart() + // TODO(crbug.com/1291953): Callers should typically use CanSyncFeatureStart() // or IsSyncFeatureEnabled() instead. virtual bool IsSyncRequested() const; // Returns whether Sync-the-transport can start the Sync feature. @@ -79,14 +79,6 @@ // Returns whether all sync data is being encrypted. virtual bool IsEncryptEverythingEnabled() const; - // Returns true if the initial sync setup is currently ongoing. - // Returns false if it is either finished or not started. - // This method is guaranteed not to start the sync backend so it can be - // called at start-up. - // DEPRECATED: do not use, will be removed once the internal repository - // has been fixed to not use this method. - virtual bool IsInitialSetupOngoing(); - // Pauses sync allowing the user to configure what data to sync before // actually starting to sync data with the server. virtual void PrepareForFirstSyncSetup();
diff --git a/ios/chrome/browser/sync/sync_setup_service_mock.h b/ios/chrome/browser/sync/sync_setup_service_mock.h index bc703f3..797f767 100644 --- a/ios/chrome/browser/sync/sync_setup_service_mock.h +++ b/ios/chrome/browser/sync/sync_setup_service_mock.h
@@ -27,7 +27,6 @@ MOCK_METHOD(bool, IsSyncingAllDataTypes, (), (const override)); MOCK_METHOD(bool, IsDataTypePreferred, (syncer::ModelType), (const override)); MOCK_METHOD(bool, IsDataTypeActive, (syncer::ModelType), (const override)); - MOCK_METHOD(bool, IsInitialSetupOngoing, (), (override)); MOCK_METHOD(void, PrepareForFirstSyncSetup, (), (override)); MOCK_METHOD(void, SetFirstSetupComplete,
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_mediator.h b/ios/chrome/browser/ui/bookmarks/bookmark_mediator.h index d972a93..07daf49 100644 --- a/ios/chrome/browser/ui/bookmarks/bookmark_mediator.h +++ b/ios/chrome/browser/ui/bookmarks/bookmark_mediator.h
@@ -27,11 +27,15 @@ @interface BookmarkMediator : NSObject - (instancetype)init NS_UNAVAILABLE; -- (instancetype) - initWithWithBookmarkModel:(bookmarks::BookmarkModel*)bookmarkModel - prefs:(PrefService*)prefs - authenticationService:(AuthenticationService*)authenticationService - syncSetupService:(SyncSetupService*)syncSetupService +- (instancetype)initWithWithProfileBookmarkModel: + (bookmarks::BookmarkModel*)profileBookmarkModel + accountBookmarkModel: + (bookmarks::BookmarkModel*)accountBookmarkModel + prefs:(PrefService*)prefs + authenticationService: + (AuthenticationService*)authenticationService + syncSetupService: + (SyncSetupService*)syncSetupService NS_DESIGNATED_INITIALIZER; // Registers the feature preferences.
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_mediator.mm b/ios/chrome/browser/ui/bookmarks/bookmark_mediator.mm index 8e493ec..b9f6b8d 100644 --- a/ios/chrome/browser/ui/bookmarks/bookmark_mediator.mm +++ b/ios/chrome/browser/ui/bookmarks/bookmark_mediator.mm
@@ -41,46 +41,52 @@ } // namespace -@interface BookmarkMediator () { - // Bookmark model for this mediator. - bookmarks::BookmarkModel* _bookmarkModel; +@implementation BookmarkMediator { + // Profile bookmark model for this mediator. + base::WeakPtr<bookmarks::BookmarkModel> _profileBookmarkModel; + // Account bookmark model for this mediator. + base::WeakPtr<bookmarks::BookmarkModel> _accountBookmarkModel; // Prefs model for this mediator. PrefService* _prefs; // Authentication service for this mediator. - AuthenticationService* _authenticationService; + base::WeakPtr<AuthenticationService> _authenticationService; // The setup service for this mediator. SyncSetupService* _syncSetupService; } -@end - -@implementation BookmarkMediator - + (void)registerBrowserStatePrefs:(user_prefs::PrefRegistrySyncable*)registry { registry->RegisterInt64Pref(prefs::kIosBookmarkFolderDefault, kLastUsedFolderNone); } -- (instancetype) - initWithWithBookmarkModel:(bookmarks::BookmarkModel*)bookmarkModel - prefs:(PrefService*)prefs - authenticationService:(AuthenticationService*)authenticationService - syncSetupService:(SyncSetupService*)syncSetupService { +- (instancetype)initWithWithProfileBookmarkModel: + (bookmarks::BookmarkModel*)profileBookmarkModel + accountBookmarkModel: + (bookmarks::BookmarkModel*)accountBookmarkModel + prefs:(PrefService*)prefs + authenticationService: + (AuthenticationService*)authenticationService + syncSetupService: + (SyncSetupService*)syncSetupService { self = [super init]; if (self) { - _bookmarkModel = bookmarkModel; + _profileBookmarkModel = profileBookmarkModel->AsWeakPtr(); + if (accountBookmarkModel) { + _accountBookmarkModel = accountBookmarkModel->AsWeakPtr(); + } _prefs = prefs; - _authenticationService = authenticationService; + _authenticationService = authenticationService->GetWeakPtr(); _syncSetupService = syncSetupService; } return self; } - (void)disconnect { - _bookmarkModel = nullptr; + _profileBookmarkModel = nullptr; + _accountBookmarkModel = nullptr; _prefs = nullptr; _authenticationService = nullptr; _syncSetupService = nullptr; @@ -93,8 +99,9 @@ LogLikelyInterestedDefaultBrowserUserActivity(DefaultPromoTypeAllTabs); const BookmarkNode* defaultFolder = [self folderForNewBookmarks]; - _bookmarkModel->AddNewURL(defaultFolder, defaultFolder->children().size(), - base::SysNSStringToUTF16(title), URL); + _profileBookmarkModel->AddNewURL(defaultFolder, + defaultFolder->children().size(), + base::SysNSStringToUTF16(title), URL); MDCSnackbarMessageAction* action = [[MDCSnackbarMessageAction alloc] init]; action.handler = editAction; @@ -122,9 +129,9 @@ for (URLWithTitle* urlWithTitle in URLs) { base::RecordAction(base::UserMetricsAction("BookmarkAdded")); - _bookmarkModel->AddNewURL(folder, folder->children().size(), - base::SysNSStringToUTF16(urlWithTitle.title), - urlWithTitle.URL); + _profileBookmarkModel->AddNewURL( + folder, folder->children().size(), + base::SysNSStringToUTF16(urlWithTitle.title), urlWithTitle.URL); } NSString* folderTitle = bookmark_utils_ios::TitleForBookmarkNode(folder); @@ -140,13 +147,13 @@ #pragma mark - Private - (const BookmarkNode*)folderForNewBookmarks { - const BookmarkNode* defaultFolder = _bookmarkModel->mobile_node(); + const BookmarkNode* defaultFolder = _profileBookmarkModel->mobile_node(); int64_t node_id = _prefs->GetInt64(prefs::kIosBookmarkFolderDefault); if (node_id == kLastUsedFolderNone) { node_id = defaultFolder->id(); } const BookmarkNode* result = - bookmarks::GetBookmarkNodeByID(_bookmarkModel, node_id); + bookmarks::GetBookmarkNodeByID(_profileBookmarkModel.get(), node_id); if (result) { return result;
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_mediator_unittest.mm b/ios/chrome/browser/ui/bookmarks/bookmark_mediator_unittest.mm index dd1c008..30ab1be 100644 --- a/ios/chrome/browser/ui/bookmarks/bookmark_mediator_unittest.mm +++ b/ios/chrome/browser/ui/bookmarks/bookmark_mediator_unittest.mm
@@ -54,10 +54,11 @@ sync_setup_service_ = std::make_unique<FakeSyncSetupService>(sync_service_); mediator_ = [[BookmarkMediator alloc] - initWithWithBookmarkModel:profile_bookmark_model_ - prefs:chrome_browser_state_->GetPrefs() - authenticationService:authentication_service_ - syncSetupService:sync_setup_service_.get()]; + initWithWithProfileBookmarkModel:profile_bookmark_model_ + accountBookmarkModel:nullptr + prefs:chrome_browser_state_->GetPrefs() + authenticationService:authentication_service_ + syncSetupService:sync_setup_service_.get()]; } protected:
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_utils_ios.h b/ios/chrome/browser/ui/bookmarks/bookmark_utils_ios.h index 2164e22..f795ec90 100644 --- a/ios/chrome/browser/ui/bookmarks/bookmark_utils_ios.h +++ b/ios/chrome/browser/ui/bookmarks/bookmark_utils_ios.h
@@ -166,7 +166,7 @@ // the operation wasn't successful or there's nothing to undo. MDCSnackbarMessage* DeleteBookmarksWithUndoToast( const std::set<const bookmarks::BookmarkNode*>& bookmarks, - const std::vector<bookmarks::BookmarkModel*> bookmark_models, + const std::vector<bookmarks::BookmarkModel*>& bookmark_models, ChromeBrowserState* browser_state); // Deletes all nodes in `bookmarks`.
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_utils_ios.mm b/ios/chrome/browser/ui/bookmarks/bookmark_utils_ios.mm index 94e806ec..f4a6471c 100644 --- a/ios/chrome/browser/ui/bookmarks/bookmark_utils_ios.mm +++ b/ios/chrome/browser/ui/bookmarks/bookmark_utils_ios.mm
@@ -368,7 +368,7 @@ MDCSnackbarMessage* DeleteBookmarksWithUndoToast( const std::set<const BookmarkNode*>& nodes, - const std::vector<bookmarks::BookmarkModel*> bookmark_models, + const std::vector<bookmarks::BookmarkModel*>& bookmark_models, ChromeBrowserState* browser_state) { CHECK_GT(bookmark_models.size(), 0u); size_t node_count = nodes.size();
diff --git a/ios/chrome/browser/ui/bookmarks/bookmarks_coordinator.mm b/ios/chrome/browser/ui/bookmarks/bookmarks_coordinator.mm index 2f0a492..1e52e33e 100644 --- a/ios/chrome/browser/ui/bookmarks/bookmarks_coordinator.mm +++ b/ios/chrome/browser/ui/bookmarks/bookmarks_coordinator.mm
@@ -17,6 +17,7 @@ #import "base/time/time.h" #import "components/bookmarks/browser/bookmark_model.h" #import "components/bookmarks/browser/bookmark_utils.h" +#import "ios/chrome/browser/bookmarks/account_bookmark_model_factory.h" #import "ios/chrome/browser/bookmarks/local_or_syncable_bookmark_model_factory.h" #import "ios/chrome/browser/browser_state/chrome_browser_state.h" #import "ios/chrome/browser/default_browser/utils.h" @@ -76,14 +77,7 @@ BookmarksFolderEditorCoordinatorDelegate, BookmarksFolderChooserCoordinatorDelegate, BookmarksHomeViewControllerDelegate, - UIAdaptivePresentationControllerDelegate> { - // The browser state of the current user. - ChromeBrowserState* _currentBrowserState; // weak - - // The browser state to use, might be different from _currentBrowserState if - // it is incognito. - ChromeBrowserState* _browserState; // weak -} + UIAdaptivePresentationControllerDelegate> // The type of view controller that is being presented. @property(nonatomic, assign) PresentedState currentPresentedState; @@ -98,9 +92,6 @@ @property(nonatomic, strong) UINavigationController* bookmarkNavigationController; -// The bookmark model in use. -@property(nonatomic, assign) BookmarkModel* bookmarkModel; - // A reference to the potentially presented bookmark browser. This will be // non-nil when `currentPresentedState` is BOOKMARK_BROWSER. @property(nonatomic, strong) BookmarksHomeViewController* bookmarkBrowser; @@ -130,36 +121,53 @@ @end -@implementation BookmarksCoordinator +@implementation BookmarksCoordinator { + // The browser state of the current user. + base::WeakPtr<ChromeBrowserState> _currentBrowserState; + // The browser state to use, might be different from _currentBrowserState if + // it is incognito. + base::WeakPtr<ChromeBrowserState> _browserState; + + // Profile bookmark model. + base::WeakPtr<bookmarks::BookmarkModel> _profileBookmarkModel; + // Account bookmark model. + base::WeakPtr<bookmarks::BookmarkModel> _accountBookmarkModel; +} + @synthesize applicationCommandsHandler = _applicationCommandsHandler; -@synthesize snackbarCommandsHandler = _snackbarCommandsHandler; @synthesize baseViewController = _baseViewController; -@synthesize bookmarkBrowser = _bookmarkBrowser; -@synthesize bookmarkModel = _bookmarkModel; -@synthesize bookmarkNavigationController = _bookmarkNavigationController; -@synthesize currentPresentedState = _currentPresentedState; -@synthesize delegate = _delegate; -@synthesize mediator = _mediator; +@synthesize snackbarCommandsHandler = _snackbarCommandsHandler; - (instancetype)initWithBrowser:(Browser*)browser { self = [super initWithBaseViewController:nil browser:browser]; if (self) { // Bookmarks are always opened with the main browser state, even in // incognito mode. - _currentBrowserState = browser->GetBrowserState(); - _browserState = _currentBrowserState->GetOriginalChromeBrowserState(); - _bookmarkModel = + _currentBrowserState = browser->GetBrowserState()->AsWeakPtr(); + _browserState = + _currentBrowserState->GetOriginalChromeBrowserState()->AsWeakPtr(); + _profileBookmarkModel = ios::LocalOrSyncableBookmarkModelFactory::GetForBrowserState( - _browserState); + _browserState.get()) + ->AsWeakPtr(); + BookmarkModel* accountBookmarkModel = + ios::AccountBookmarkModelFactory::GetForBrowserState( + _browserState.get()); + if (accountBookmarkModel) { + _accountBookmarkModel = accountBookmarkModel->AsWeakPtr(); + } _mediator = [[BookmarkMediator alloc] - initWithWithBookmarkModel:self.bookmarkModel - prefs:_browserState->GetPrefs() - authenticationService:AuthenticationServiceFactory:: - GetForBrowserState(_browserState) - syncSetupService:SyncSetupServiceFactory::GetForBrowserState( - _browserState)]; + initWithWithProfileBookmarkModel:_profileBookmarkModel.get() + accountBookmarkModel:_accountBookmarkModel.get() + prefs:_browserState->GetPrefs() + authenticationService:AuthenticationServiceFactory:: + GetForBrowserState( + _browserState.get()) + syncSetupService:SyncSetupServiceFactory:: + GetForBrowserState( + _browserState.get())]; _currentPresentedState = PresentedState::NONE; - DCHECK(_bookmarkModel); + DCHECK(_profileBookmarkModel); } return self; } @@ -214,7 +222,7 @@ } - (void)bookmarkURL:(const GURL&)URL title:(NSString*)title { - if (!self.bookmarkModel->loaded()) { + if (!_profileBookmarkModel->loaded()) { return; } @@ -234,12 +242,12 @@ } - (void)presentBookmarkEditorForURL:(const GURL&)URL { - if (!self.bookmarkModel->loaded()) { + if (!_profileBookmarkModel->loaded()) { return; } const BookmarkNode* bookmark = - self.bookmarkModel->GetMostRecentlyAddedUserNodeForURL(URL); + _profileBookmarkModel->GetMostRecentlyAddedUserNodeForURL(URL); if (!bookmark) { return; } @@ -247,7 +255,7 @@ } - (void)presentBookmarks { - [self presentBookmarksAtDisplayedFolderNode:self.bookmarkModel->root_node() + [self presentBookmarksAtDisplayedFolderNode:_profileBookmarkModel->root_node() selectingBookmark:nil]; } @@ -523,7 +531,7 @@ - (void)bookmark:(BookmarkAddCommand*)command { DCHECK(command.URLs.count > 0) << "URLs are missing"; - if (!self.bookmarkModel->loaded()) { + if (!_profileBookmarkModel->loaded()) { return; } @@ -532,7 +540,7 @@ DCHECK(URLWithTitle); const BookmarkNode* existingBookmark = - self.bookmarkModel->GetMostRecentlyAddedUserNodeForURL( + _profileBookmarkModel->GetMostRecentlyAddedUserNodeForURL( URLWithTitle.URL); if (existingBookmark) { @@ -548,16 +556,17 @@ } - (void)openToExternalBookmark:(BookmarkAddCommand*)command { - if (!self.bookmarkModel->loaded() || command.URLs.count != 1 || + if (!_profileBookmarkModel->loaded() || command.URLs.count != 1 || command.presentFolderChooser) { return; } const BookmarkNode* existingBookmark = - self.bookmarkModel->GetMostRecentlyAddedUserNodeForURL( + _profileBookmarkModel->GetMostRecentlyAddedUserNodeForURL( command.URLs.firstObject.URL); - [self presentBookmarksAtDisplayedFolderNode:self.bookmarkModel->mobile_node() - selectingBookmark:existingBookmark]; + [self + presentBookmarksAtDisplayedFolderNode:_profileBookmarkModel->mobile_node() + selectingBookmark:existingBookmark]; } #pragma mark - Private @@ -625,7 +634,8 @@ - (void)openURLInCurrentTab:(const GURL&)url { WebStateList* webStateList = self.browser->GetWebStateList(); if (url.SchemeIs(url::kJavaScriptScheme) && webStateList) { // bookmarklet - LoadJavaScriptURL(url, _browserState, webStateList->GetActiveWebState()); + LoadJavaScriptURL(url, _browserState.get(), + webStateList->GetActiveWebState()); return; } UrlLoadParams params = UrlLoadParams::InCurrentTab(url); @@ -662,13 +672,13 @@ self.bookmarkBrowser.snackbarCommandsHandler = self.snackbarCommandsHandler; NSArray<BookmarksHomeViewController*>* replacementViewControllers = nil; - if (self.bookmarkModel->loaded()) { + if (_profileBookmarkModel->loaded()) { // Set the root node if the model has been loaded. If the model has not been // loaded yet, the root node will be set in BookmarksHomeViewController // after the model is finished loading. self.bookmarkBrowser.displayedFolderNode = displayedFolderNode; [self.bookmarkBrowser setExternalBookmark:bookmarkNode]; - if (displayedFolderNode == self.bookmarkModel->root_node()) { + if (displayedFolderNode == _profileBookmarkModel->root_node()) { replacementViewControllers = [self.bookmarkBrowser cachedViewControllerStack]; }
diff --git a/ios/chrome/browser/ui/browser_view/browser_coordinator.mm b/ios/chrome/browser/ui/browser_view/browser_coordinator.mm index 3e32059..721e8717 100644 --- a/ios/chrome/browser/ui/browser_view/browser_coordinator.mm +++ b/ios/chrome/browser/ui/browser_view/browser_coordinator.mm
@@ -665,8 +665,6 @@ dependencies:_viewControllerDependencies]; self.tabLifecycleMediator.baseViewController = self.viewController; self.tabLifecycleMediator.delegate = self.viewController; - _viewController.readingListBrowserAgent = - ReadingListBrowserAgent::FromBrowser(self.browser); WebNavigationBrowserAgent::FromBrowser(self.browser)->SetDelegate(self);
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller.h b/ios/chrome/browser/ui/browser_view/browser_view_controller.h index 5e0d001d0..81ef6d9 100644 --- a/ios/chrome/browser/ui/browser_view/browser_view_controller.h +++ b/ios/chrome/browser/ui/browser_view/browser_view_controller.h
@@ -56,7 +56,6 @@ @protocol PopupMenuUIUpdating; class PrerenderService; @class PrimaryToolbarCoordinator; -class ReadingListBrowserAgent; @class SafeAreaProvider; @class SecondaryToolbarCoordinator; @class SideSwipeController; @@ -164,9 +163,6 @@ @property(nonatomic, weak) id<DefaultPromoNonModalPresentationDelegate> nonModalPromoPresentationDelegate; -// TODO(crbug.com/1272540): Remove this command. -@property(nonatomic) ReadingListBrowserAgent* readingListBrowserAgent; - // Whether the receiver is currently the primary BVC. - (void)setPrimary:(BOOL)primary;
diff --git a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm index 16ba19ea..b6c7141 100644 --- a/ios/chrome/browser/ui/browser_view/browser_view_controller.mm +++ b/ios/chrome/browser/ui/browser_view/browser_view_controller.mm
@@ -2747,11 +2747,6 @@ #pragma mark - BrowserCommands -// TODO(crbug.com/1272540): Remove this command after updating ios_internal. -- (void)addToReadingList:(ReadingListAddCommand*)command { - self.readingListBrowserAgent->AddURLsToReadingList(command.URLs); -} - - (void)prepareForOverflowMenuPresentation { DCHECK(self.visible || self.dismissingModal);
diff --git a/ios/chrome/browser/ui/content_suggestions/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/BUILD.gn index eb46b158..d6a9159 100644 --- a/ios/chrome/browser/ui/content_suggestions/BUILD.gn +++ b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
@@ -328,6 +328,7 @@ "//ios/chrome/browser/flags:system_flags", "//ios/chrome/browser/ntp:features", "//ios/chrome/browser/prefs:pref_names", + "//ios/chrome/browser/search_engines:eg_test_support+eg2", "//ios/chrome/browser/shared/public/features", "//ios/chrome/browser/signin:capabilities_types", "//ios/chrome/browser/signin:fake_system_identity",
diff --git a/ios/chrome/browser/ui/content_suggestions/new_tab_page_app_interface.h b/ios/chrome/browser/ui/content_suggestions/new_tab_page_app_interface.h index ca7c12b..80ca1443 100644 --- a/ios/chrome/browser/ui/content_suggestions/new_tab_page_app_interface.h +++ b/ios/chrome/browser/ui/content_suggestions/new_tab_page_app_interface.h
@@ -10,13 +10,6 @@ // App interface for the NTP. @interface NewTabPageAppInterface : NSObject -// Returns the short name of the default search engine. -+ (NSString*)defaultSearchEngine; - -// Resets the default search engine to `defaultSearchEngine`. -// `defaultSearchEngine` should be its short name. -+ (void)resetSearchEngineTo:(NSString*)defaultSearchEngine; - // Returns the width the search field is supposed to have when the collection // has `collectionWidth`. `traitCollection` is the trait collection of the view // displaying the omnibox, its Size Class is used in the computation.
diff --git a/ios/chrome/browser/ui/content_suggestions/new_tab_page_app_interface.mm b/ios/chrome/browser/ui/content_suggestions/new_tab_page_app_interface.mm index d1a5c9ed..6271b9a5 100644 --- a/ios/chrome/browser/ui/content_suggestions/new_tab_page_app_interface.mm +++ b/ios/chrome/browser/ui/content_suggestions/new_tab_page_app_interface.mm
@@ -8,8 +8,6 @@ #import "base/strings/sys_string_conversions.h" #import "base/strings/utf_string_conversions.h" #import "components/keyed_service/ios/browser_state_keyed_service_factory.h" -#import "components/search_engines/template_url.h" -#import "components/search_engines/template_url_service.h" #import "ios/chrome/browser/application_context/application_context.h" #import "ios/chrome/browser/browser_state/chrome_browser_state.h" #import "ios/chrome/browser/flags/system_flags.h" @@ -28,35 +26,6 @@ @implementation NewTabPageAppInterface -+ (NSString*)defaultSearchEngine { - // Get the default Search Engine. - ChromeBrowserState* browser_state = - chrome_test_util::GetOriginalBrowserState(); - TemplateURLService* service = - ios::TemplateURLServiceFactory::GetForBrowserState(browser_state); - const TemplateURL* default_provider = service->GetDefaultSearchProvider(); - DCHECK(default_provider); - return base::SysUTF16ToNSString(default_provider->short_name()); -} - -+ (void)resetSearchEngineTo:(NSString*)defaultSearchEngine { - std::u16string defaultSearchEngineString = - base::SysNSStringToUTF16(defaultSearchEngine); - // Set the search engine back to the default in case the test fails before - // cleaning it up. - ChromeBrowserState* browser_state = - chrome_test_util::GetOriginalBrowserState(); - TemplateURLService* service = - ios::TemplateURLServiceFactory::GetForBrowserState(browser_state); - std::vector<TemplateURL*> urls = service->GetTemplateURLs(); - - for (auto iter = urls.begin(); iter != urls.end(); ++iter) { - if (defaultSearchEngineString == (*iter)->short_name()) { - service->SetUserSelectedDefaultSearchProvider(*iter); - } - } -} - + (CGFloat)searchFieldWidthForCollectionWidth:(CGFloat)collectionWidth traitCollection: (UITraitCollection*)traitCollection {
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 40d3b64..4e7c573 100644 --- a/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm +++ b/ios/chrome/browser/ui/content_suggestions/ntp_home_egtest.mm
@@ -12,6 +12,7 @@ #import "ios/chrome/browser/flags/chrome_switches.h" #import "ios/chrome/browser/ntp/features.h" #import "ios/chrome/browser/prefs/pref_names.h" +#import "ios/chrome/browser/search_engines/search_engines_app_interface.h" #import "ios/chrome/browser/shared/public/features/features.h" #import "ios/chrome/browser/signin/capabilities_types.h" #import "ios/chrome/browser/signin/fake_system_identity.h" @@ -169,13 +170,13 @@ setBoolValue:YES forUserPref:base::SysUTF8ToNSString(feed::prefs::kArticlesListVisible)]; - self.defaultSearchEngine = [NewTabPageAppInterface defaultSearchEngine]; + self.defaultSearchEngine = [SearchEnginesAppInterface defaultSearchEngine]; } - (void)tearDown { [EarlGrey rotateDeviceToOrientation:UIDeviceOrientationPortrait error:nil]; - [NewTabPageAppInterface resetSearchEngineTo:self.defaultSearchEngine]; + [SearchEnginesAppInterface setSearchEngineTo:self.defaultSearchEngine]; [super tearDown]; }
diff --git a/ios/chrome/browser/ui/history/BUILD.gn b/ios/chrome/browser/ui/history/BUILD.gn index d7e9d4e..28fc78b9 100644 --- a/ios/chrome/browser/ui/history/BUILD.gn +++ b/ios/chrome/browser/ui/history/BUILD.gn
@@ -7,6 +7,7 @@ sources = [ "history_coordinator.h", "history_coordinator.mm", + "history_coordinator_delegate.h", "history_mediator.h", "history_mediator.mm", ]
diff --git a/ios/chrome/browser/ui/history/history_coordinator.h b/ios/chrome/browser/ui/history/history_coordinator.h index 7db51cb..6caef4c 100644 --- a/ios/chrome/browser/ui/history/history_coordinator.h +++ b/ios/chrome/browser/ui/history/history_coordinator.h
@@ -12,6 +12,7 @@ enum class UrlLoadStrategy; +@protocol HistoryCoordinatorDelegate; @protocol HistoryPresentationDelegate; // Coordinator that presents History. @@ -24,8 +25,11 @@ // Delegate used to make the Tab UI visible. @property(nonatomic, weak) id<HistoryPresentationDelegate> presentationDelegate; -// Stops this Coordinator then calls `completionHandler`. -- (void)stopWithCompletion:(ProceduralBlock)completionHandler; +// The delegate handling coordinator dismissal. +@property(nonatomic, weak) id<HistoryCoordinatorDelegate> delegate; + +// Dismisses this Coordinator then calls `completionHandler`. +- (void)dismissWithCompletion:(ProceduralBlock)completionHandler; @end
diff --git a/ios/chrome/browser/ui/history/history_coordinator.mm b/ios/chrome/browser/ui/history/history_coordinator.mm index b710d17..a7e3922 100644 --- a/ios/chrome/browser/ui/history/history_coordinator.mm +++ b/ios/chrome/browser/ui/history/history_coordinator.mm
@@ -19,6 +19,7 @@ #import "ios/chrome/browser/shared/ui/table_view/table_view_navigation_controller.h" #import "ios/chrome/browser/sync/sync_service_factory.h" #import "ios/chrome/browser/ui/history/history_clear_browsing_data_coordinator.h" +#import "ios/chrome/browser/ui/history/history_coordinator_delegate.h" #import "ios/chrome/browser/ui/history/history_mediator.h" #import "ios/chrome/browser/ui/history/history_menu_provider.h" #import "ios/chrome/browser/ui/history/history_table_view_controller.h" @@ -140,7 +141,13 @@ // Disconnect the historyTableViewController before dismissing it to avoid // it accessing stalled objects. [self.historyTableViewController detachFromBrowser]; - [self stopWithCompletion:nil]; + [self dismissWithCompletion:nil]; + + // Clear C++ objects as they may reference objects that will become + // unavailable. + _browsingHistoryDriver = nullptr; + _browsingHistoryService = nullptr; + _browsingHistoryDriverDelegate = nullptr; } - (void)dealloc { @@ -148,7 +155,7 @@ } // This method should always execute the `completionHandler`. -- (void)stopWithCompletion:(ProceduralBlock)completionHandler { +- (void)dismissWithCompletion:(ProceduralBlock)completionHandler { [self.sharingCoordinator stop]; self.sharingCoordinator = nil; @@ -187,7 +194,7 @@ #pragma mark - HistoryUIDelegate - (void)dismissHistoryWithCompletion:(ProceduralBlock)completionHandler { - [self stopWithCompletion:completionHandler]; + [self.delegate closeHistoryWithCompletion:completionHandler]; } - (void)displayPrivacySettings { @@ -289,7 +296,7 @@ // the active regular tab. - (void)onOpenedURLInNewTab { __weak __typeof(self) weakSelf = self; - [self stopWithCompletion:^{ + [self.delegate closeHistoryWithCompletion:^{ [weakSelf.presentationDelegate showActiveRegularTabFromHistory]; }]; } @@ -298,7 +305,7 @@ // the active incognito tab. - (void)onOpenedURLInNewIncognitoTab { __weak __typeof(self) weakSelf = self; - [self stopWithCompletion:^{ + [self.delegate closeHistoryWithCompletion:^{ [weakSelf.presentationDelegate showActiveIncognitoTabFromHistory]; }]; }
diff --git a/ios/chrome/browser/ui/history/history_coordinator_delegate.h b/ios/chrome/browser/ui/history/history_coordinator_delegate.h new file mode 100644 index 0000000..d501dc83 --- /dev/null +++ b/ios/chrome/browser/ui/history/history_coordinator_delegate.h
@@ -0,0 +1,20 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IOS_CHROME_BROWSER_UI_HISTORY_HISTORY_COORDINATOR_DELEGATE_H_ +#define IOS_CHROME_BROWSER_UI_HISTORY_HISTORY_COORDINATOR_DELEGATE_H_ + +#import "base/ios/block_types.h" + +// Delegate for HistoryCoordinator. +@protocol HistoryCoordinatorDelegate + +// Called when the history should be dismissed. +// `Completion` is called after the dismissal but before the coordinator +// is stopped. +- (void)closeHistoryWithCompletion:(ProceduralBlock)completion; + +@end + +#endif // IOS_CHROME_BROWSER_UI_HISTORY_HISTORY_COORDINATOR_DELEGATE_H_
diff --git a/ios/chrome/browser/ui/link_to_text/link_to_text_mediator_unittest.mm b/ios/chrome/browser/ui/link_to_text/link_to_text_mediator_unittest.mm index b1c6b2b..e6273ae 100644 --- a/ios/chrome/browser/ui/link_to_text/link_to_text_mediator_unittest.mm +++ b/ios/chrome/browser/ui/link_to_text/link_to_text_mediator_unittest.mm
@@ -162,32 +162,30 @@ std::unique_ptr<base::Value> CreateSuccessResponse( const std::string& selected_text, CGRect selection_rect) { - base::Value rect_value(base::Value::Type::DICT); - rect_value.SetDoubleKey("x", selection_rect.origin.x); - rect_value.SetDoubleKey("y", selection_rect.origin.y); - rect_value.SetDoubleKey("width", selection_rect.size.width); - rect_value.SetDoubleKey("height", selection_rect.size.height); + base::Value::Dict rect_value; + rect_value.Set("x", selection_rect.origin.x); + rect_value.Set("y", selection_rect.origin.y); + rect_value.Set("width", selection_rect.size.width); + rect_value.Set("height", selection_rect.size.height); - std::unique_ptr<base::Value> response_value = - std::make_unique<base::Value>(base::Value::Type::DICT); - response_value->SetDoubleKey( - "status", static_cast<double>(LinkGenerationOutcome::kSuccess)); - response_value->SetKey("fragment", kTestTextFragment.ToValue()); - response_value->SetStringKey("selectedText", selected_text); - response_value->SetKey("selectionRect", std::move(rect_value)); - return response_value; + base::Value::Dict response_value; + response_value.Set("status", + static_cast<double>(LinkGenerationOutcome::kSuccess)); + response_value.Set("fragment", kTestTextFragment.ToValue()); + response_value.Set("selectedText", selected_text); + response_value.Set("selectionRect", std::move(rect_value)); + return std::make_unique<base::Value>(std::move(response_value)); } void SetCanonicalUrl(base::Value* value, const std::string& canonical_url) { - value->SetStringKey("canonicalUrl", canonical_url); + value->GetDict().Set("canonicalUrl", canonical_url); } std::unique_ptr<base::Value> CreateErrorResponse( LinkGenerationOutcome outcome) { - std::unique_ptr<base::Value> response_value = - std::make_unique<base::Value>(base::Value::Type::DICT); - response_value->SetDoubleKey("status", static_cast<double>(outcome)); - return response_value; + base::Value::Dict response_value; + response_value.Set("status", static_cast<double>(outcome)); + return std::make_unique<base::Value>(std::move(response_value)); } void ValidateLinkGeneratedSuccessUkm() { @@ -403,7 +401,7 @@ std::unique_ptr<base::Value> malformed_response = std::make_unique<base::Value>(base::Value::Type::DICT); - malformed_response->SetStringKey("somethingElse", "abc"); + malformed_response->GetDict().Set("somethingElse", "abc"); SetLinkToTextResponse(malformed_response.get(), /*zoom=*/1.0); __block BOOL callback_invoked = NO;
diff --git a/ios/chrome/browser/ui/main/browser_view_wrangler_unittest.mm b/ios/chrome/browser/ui/main/browser_view_wrangler_unittest.mm index ea9b9cc..6bd8c58ad 100644 --- a/ios/chrome/browser/ui/main/browser_view_wrangler_unittest.mm +++ b/ios/chrome/browser/ui/main/browser_view_wrangler_unittest.mm
@@ -24,6 +24,8 @@ #import "ios/chrome/browser/sessions/test_session_service.h" #import "ios/chrome/browser/shared/coordinator/scene/scene_state.h" #import "ios/chrome/browser/shared/coordinator/scene/scene_state_browser_agent.h" +#import "ios/chrome/browser/signin/authentication_service_factory.h" +#import "ios/chrome/browser/signin/fake_authentication_service_delegate.h" #import "ios/chrome/browser/sync/send_tab_to_self_sync_service_factory.h" #import "ios/chrome/browser/tabs/inactive_tabs/features.h" #import "ios/chrome/browser/ui/browser_view/browser_view_controller.h" @@ -92,9 +94,15 @@ test_cbs_builder.AddTestingFactory( ios::LocalOrSyncableBookmarkModelFactory::GetInstance(), ios::LocalOrSyncableBookmarkModelFactory::GetDefaultFactory()); + test_cbs_builder.AddTestingFactory( + AuthenticationServiceFactory::GetInstance(), + AuthenticationServiceFactory::GetDefaultFactory()); chrome_browser_state_ = test_cbs_builder.Build(); + AuthenticationServiceFactory::CreateAndInitializeForBrowserState( + chrome_browser_state_.get(), + std::make_unique<FakeAuthenticationServiceDelegate>()); session_service_block_ = ^SessionServiceIOS*(id self) { return test_session_service_; };
diff --git a/ios/chrome/browser/ui/passwords/account_storage_notice/passwords_account_storage_notice_view_controller.mm b/ios/chrome/browser/ui/passwords/account_storage_notice/passwords_account_storage_notice_view_controller.mm index 5cb0a1e..10aed41 100644 --- a/ios/chrome/browser/ui/passwords/account_storage_notice/passwords_account_storage_notice_view_controller.mm +++ b/ios/chrome/browser/ui/passwords/account_storage_notice/passwords_account_storage_notice_view_controller.mm
@@ -34,19 +34,27 @@ self.actionHandler = actionHandler; self.presentationController.delegate = self; - if (@available(iOS 15, *)) { - self.modalPresentationStyle = UIModalPresentationPageSheet; - self.sheetPresentationController.preferredCornerRadius = 20; + + self.modalPresentationStyle = UIModalPresentationPageSheet; + self.sheetPresentationController.preferredCornerRadius = 20; + + if (@available(iOS 16, *)) { + self.sheetPresentationController.detents = @[ + // Add custom detent to fit content vertically. + self.preferredHeightDetent, + UISheetPresentationControllerDetent.largeDetent + ]; + } else { self.sheetPresentationController.detents = @[ UISheetPresentationControllerDetent.mediumDetent, - UISheetPresentationControllerDetent.largeDetent, + UISheetPresentationControllerDetent.largeDetent ]; - // prefersEdgeAttachedInCompactHeight just controls attaching to the bottom, - // not the sheet height. - self.sheetPresentationController.prefersEdgeAttachedInCompactHeight = YES; - } else { - self.modalPresentationStyle = UIModalPresentationFormSheet; } + + // prefersEdgeAttachedInCompactHeight just controls attaching to the bottom, + // not the sheet height. + self.sheetPresentationController.prefersEdgeAttachedInCompactHeight = YES; + return self; }
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_account_storage_egtest.mm b/ios/chrome/browser/ui/reading_list/reading_list_account_storage_egtest.mm index 4f1fa6e7..cb56c06 100644 --- a/ios/chrome/browser/ui/reading_list/reading_list_account_storage_egtest.mm +++ b/ios/chrome/browser/ui/reading_list/reading_list_account_storage_egtest.mm
@@ -18,6 +18,8 @@ #error "This file requires ARC support." #endif +using chrome_test_util::PrimarySignInButton; + // Reading List integration tests for Chrome with account storage and UI // enabled. @interface ReadingListAccountStorageTestCase : WebHttpServerChromeTestCase @@ -36,6 +38,35 @@ #pragma mark - ReadingListAccountStorageTestCase Tests +// Tests that the sign-in is re-shown after the user signs-in and then signs-out +// while the reading list screen is still shown. +// See http://crbug.com/1432611. +- (void)testPromoReshowAfterSignInAndSignOut { + FakeSystemIdentity* fakeIdentity1 = [FakeSystemIdentity fakeIdentity1]; + [SigninEarlGrey addFakeIdentity:fakeIdentity1]; + // Sign-in with identity1 with the promo. + [ReadingListEarlGreyUI openReadingList]; + [SigninEarlGreyUI + verifySigninPromoVisibleWithMode:SigninPromoViewModeSigninWithAccount]; + [[EarlGrey + selectElementWithMatcher:grey_allOf(PrimarySignInButton(), + grey_sufficientlyVisible(), nil)] + performAction:grey_tap()]; + // Verify that identity1 is signed-in and the promo is hidden. + [SigninEarlGrey verifyPrimaryAccountWithEmail:fakeIdentity1.userEmail + consent:signin::ConsentLevel::kSignin]; + [SigninEarlGreyUI verifySigninPromoNotVisible]; + // Sign-out without changing the UI and verify that the promo is shown, + // without spinner. + [SigninEarlGrey signOut]; + [SigninEarlGreyUI + verifySigninPromoVisibleWithMode:SigninPromoViewModeSigninWithAccount]; + [[EarlGrey + selectElementWithMatcher:grey_allOf(grey_accessibilityID( + kSigninPromoActivityIndicatorId), + grey_sufficientlyVisible(), nil)] + assertWithMatcher:grey_nil()]; +} // Tests to sign-in in incognito mode with the promo. // See http://crbug.com/1432747. - (void)testSignInPromoInIncognito {
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_coordinator.mm b/ios/chrome/browser/ui/reading_list/reading_list_coordinator.mm index 26a112f..ffdabb3 100644 --- a/ios/chrome/browser/ui/reading_list/reading_list_coordinator.mm +++ b/ios/chrome/browser/ui/reading_list/reading_list_coordinator.mm
@@ -559,7 +559,7 @@ // Called when a user changes the syncing state. - (void)onPrimaryAccountChanged: (const signin::PrimaryAccountChangeEvent&)event { - switch (event.GetEventTypeFor(signin::ConsentLevel::kSync)) { + switch (event.GetEventTypeFor(signin::ConsentLevel::kSignin)) { case signin::PrimaryAccountChangeEvent::Type::kSet: if (!_signinPromoViewMediator.signinInProgress) { self.shouldShowSignInPromo = NO;
diff --git a/ios/chrome/browser/ui/reading_list/reading_list_table_view_controller.mm b/ios/chrome/browser/ui/reading_list/reading_list_table_view_controller.mm index 350a0fa..13b8d5c 100644 --- a/ios/chrome/browser/ui/reading_list/reading_list_table_view_controller.mm +++ b/ios/chrome/browser/ui/reading_list/reading_list_table_view_controller.mm
@@ -349,15 +349,6 @@ return UITableViewAutomaticDimension; } -- (CGFloat)tableView:(UITableView*)tableView - heightForFooterInSection:(NSInteger)section { - if ([self.tableViewModel sectionIdentifierForSectionIndex:section] == - SectionIdentifierSignInPromo) { - return 0; - } - return UITableViewAutomaticDimension; -} - #pragma mark - TableViewURLDragDataSource - (URLInfo*)tableView:(UITableView*)tableView
diff --git a/ios/chrome/browser/ui/settings/password/password_manager_egtest.mm b/ios/chrome/browser/ui/settings/password/password_manager_egtest.mm index 0940dcf..c41c48e 100644 --- a/ios/chrome/browser/ui/settings/password/password_manager_egtest.mm +++ b/ios/chrome/browser/ui/settings/password/password_manager_egtest.mm
@@ -462,6 +462,10 @@ return YES; } +- (BOOL)notesEnabled { + return YES; +} + - (GREYElementInteraction*) interactionForSinglePasswordEntryWithDomain:(NSString*)domain username:(NSString*)username { @@ -545,16 +549,10 @@ password_manager::features::kEnablePasswordsAccountStorage); } - if ([self isRunningTest:@selector(testLayoutWithNotesDisabled)]) { - config.features_disabled.push_back(syncer::kPasswordNotesWithBackup); - } - if ([self isRunningTest:@selector(testLayoutWithNotesEnabled)] || - [self isRunningTest:@selector(testAddPasswordLayoutWithLongNotes)] || - [self isRunningTest:@selector - (testAddPasswordSaveButtonStateOnFieldChanges)] || - [self isRunningTest:@selector(testLayoutWithLongNotes)] || - [self isRunningTest:@selector(testShowHidePasswordWithNotesEnabled)]) { + if ([self notesEnabled]) { config.features_enabled.push_back(syncer::kPasswordNotesWithBackup); + } else { + config.features_disabled.push_back(syncer::kPasswordNotesWithBackup); } return config; @@ -574,6 +572,13 @@ performAction:grey_tap()]; // Inspect "password details" view. + if ([self notesEnabled]) { + [PasswordSettingsAppInterface + setUpMockReauthenticationModuleForPasswordManager]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + } + [[self interactionForSinglePasswordEntryWithDomain:@"example.com" username:@"concrete username"] performAction:grey_tap()]; @@ -590,6 +595,11 @@ // Checks that attempts to copy a password provide appropriate feedback, // both when reauthentication succeeds and when it fails. - (void)testCopyPasswordToast { + if ([self notesEnabled]) { + EARL_GREY_TEST_SKIPPED( + @"This test is not needed with notes for passwords enabled."); + } + // Saving a form is needed for using the "password details" view. SaveExamplePasswordForm(); @@ -640,13 +650,22 @@ OpenPasswordManager(); + if ([self notesEnabled]) { + [PasswordSettingsAppInterface + setUpMockReauthenticationModuleForPasswordManager]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + } + [[self interactionForSinglePasswordEntryWithDomain:@"example.com" username:@"concrete username"] performAction:grey_tap()]; - [PasswordSettingsAppInterface setUpMockReauthenticationModule]; - [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: - ReauthenticationResult::kSuccess]; + if (![self notesEnabled]) { + [PasswordSettingsAppInterface setUpMockReauthenticationModule]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + } [GetInteractionForPasswordDetailItem(ShowPasswordButton()) performAction:grey_tap()]; @@ -666,6 +685,11 @@ // Checks that an attempt to show a password provides an appropriate feedback // when reauthentication fails. - (void)testShowPasswordToastAuthFailed { + if ([self notesEnabled]) { + EARL_GREY_TEST_SKIPPED( + @"This test is not needed with notes for passwords enabled."); + } + // Saving a form is needed for using the "password details" view. SaveExamplePasswordForm(); @@ -706,6 +730,13 @@ OpenPasswordManager(); + if ([self notesEnabled]) { + [PasswordSettingsAppInterface + setUpMockReauthenticationModuleForPasswordManager]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + } + [[self interactionForSinglePasswordEntryWithDomain:@"example.com" username:@"concrete username"] performAction:grey_tap()]; @@ -732,6 +763,13 @@ OpenPasswordManager(); + if ([self notesEnabled]) { + [PasswordSettingsAppInterface + setUpMockReauthenticationModuleForPasswordManager]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + } + [[self interactionForSinglePasswordEntryWithDomain:@"example.com" username:@"concrete username"] performAction:grey_tap()]; @@ -762,13 +800,22 @@ OpenPasswordManager(); + if ([self notesEnabled]) { + [PasswordSettingsAppInterface + setUpMockReauthenticationModuleForPasswordManager]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + } + [[self interactionForSinglePasswordEntryWithDomain:@"example.com" username:@"concrete username"] performAction:grey_tap()]; - [PasswordSettingsAppInterface setUpMockReauthenticationModule]; - [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: - ReauthenticationResult::kSuccess]; + if (![self notesEnabled]) { + [PasswordSettingsAppInterface setUpMockReauthenticationModule]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + } [[EarlGrey selectElementWithMatcher:NavigationBarEditButton()] performAction:grey_tap()]; @@ -824,13 +871,22 @@ OpenPasswordManager(); + if ([self notesEnabled]) { + [PasswordSettingsAppInterface + setUpMockReauthenticationModuleForPasswordManager]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + } + [[self interactionForSinglePasswordEntryWithDomain:@"example.com" username:@"concrete username"] performAction:grey_tap()]; - [PasswordSettingsAppInterface setUpMockReauthenticationModule]; - [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: - ReauthenticationResult::kSuccess]; + if (![self notesEnabled]) { + [PasswordSettingsAppInterface setUpMockReauthenticationModule]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + } TapEdit(); @@ -896,13 +952,22 @@ OpenPasswordManager(); + if ([self notesEnabled]) { + [PasswordSettingsAppInterface + setUpMockReauthenticationModuleForPasswordManager]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + } + [[self interactionForSinglePasswordEntryWithDomain:@"example.com" username:@"concrete username"] performAction:grey_tap()]; - [PasswordSettingsAppInterface setUpMockReauthenticationModule]; - [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: - ReauthenticationResult::kSuccess]; + if (![self notesEnabled]) { + [PasswordSettingsAppInterface setUpMockReauthenticationModule]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + } [[EarlGrey selectElementWithMatcher:NavigationBarEditButton()] performAction:grey_tap()]; @@ -971,13 +1036,22 @@ OpenPasswordManager(); + if ([self notesEnabled]) { + [PasswordSettingsAppInterface + setUpMockReauthenticationModuleForPasswordManager]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + } + [[self interactionForSinglePasswordEntryWithDomain:@"example.com" username:@"concrete username"] performAction:grey_tap()]; - [PasswordSettingsAppInterface setUpMockReauthenticationModule]; - [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: - ReauthenticationResult::kSuccess]; + if (![self notesEnabled]) { + [PasswordSettingsAppInterface setUpMockReauthenticationModule]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + } [[EarlGrey selectElementWithMatcher:NavigationBarEditButton()] performAction:grey_tap()]; @@ -1133,13 +1207,22 @@ OpenPasswordManager(); + if ([self notesEnabled]) { + [PasswordSettingsAppInterface + setUpMockReauthenticationModuleForPasswordManager]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + } + [[self interactionForSinglePasswordEntryWithDomain:@"example.com" username:@"concrete username"] performAction:grey_tap()]; - [PasswordSettingsAppInterface setUpMockReauthenticationModule]; - [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: - ReauthenticationResult::kSuccess]; + if (![self notesEnabled]) { + [PasswordSettingsAppInterface setUpMockReauthenticationModule]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + } [[EarlGrey selectElementWithMatcher:NavigationBarEditButton()] performAction:grey_tap()]; @@ -1214,6 +1297,13 @@ OpenPasswordManager(); + if ([self notesEnabled]) { + [PasswordSettingsAppInterface + setUpMockReauthenticationModuleForPasswordManager]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + } + [[self interactionForSinglePasswordEntryWithDomain:@"example.com" username:@"concrete username"] performAction:grey_tap()]; @@ -1225,9 +1315,11 @@ // Make sure to capture the reauthentication module in a variable until the // end of the test, otherwise it might get deleted too soon and break the // functionality of copying and viewing passwords. - [PasswordSettingsAppInterface setUpMockReauthenticationModule]; - [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: - ReauthenticationResult::kSuccess]; + if (![self notesEnabled]) { + [PasswordSettingsAppInterface setUpMockReauthenticationModule]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + } // Tap the context menu item for copying. [[EarlGrey @@ -1264,6 +1356,13 @@ OpenPasswordManager(); + if ([self notesEnabled]) { + [PasswordSettingsAppInterface + setUpMockReauthenticationModuleForPasswordManager]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + } + [[self interactionForSinglePasswordEntryWithDomain:@"example.com" username:@"federated username"] performAction:grey_tap()]; @@ -1283,9 +1382,12 @@ assertWithMatcher:grey_nil()]; // Check that editing doesn't require reauth. - [PasswordSettingsAppInterface setUpMockReauthenticationModule]; - [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: - ReauthenticationResult::kFailure]; + if (![self notesEnabled]) { + [PasswordSettingsAppInterface setUpMockReauthenticationModule]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kFailure]; + } + [[EarlGrey selectElementWithMatcher:NavigationBarEditButton()] performAction:grey_tap()]; // Ensure delete button is present after entering editing mode. @@ -1309,6 +1411,13 @@ OpenPasswordManager(); + if ([self notesEnabled]) { + [PasswordSettingsAppInterface + setUpMockReauthenticationModuleForPasswordManager]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + } + [[self interactionForSinglePasswordEntryWithDomain:@"example.com" username:@"concrete username"] performAction:grey_tap()]; @@ -1345,6 +1454,10 @@ // Checks the order of the elements in the detail view layout for a // non-federated, non-blocked credential with notes feature disabled. - (void)testLayoutWithNotesDisabled { + if ([self notesEnabled]) { + EARL_GREY_TEST_SKIPPED(@"This test is obsolete with notes enabled."); + } + SaveExamplePasswordForm(); OpenPasswordManager(); @@ -1385,6 +1498,11 @@ // Checks the order of the elements in the detail view layout for a // non-federated, non-blocked credential with notes feature enabled. - (void)testLayoutWithNotesEnabled { + if (![self notesEnabled]) { + EARL_GREY_TEST_SKIPPED( + @"This test is obsolete with notes for passwords disabled."); + } + SaveExamplePasswordFormWithNote(); OpenPasswordManager(); @@ -1429,6 +1547,11 @@ // Checks that entering too long note while editing a password blocks the save // button and displays a footer explanation. - (void)testLayoutWithLongNotes { + if (![self notesEnabled]) { + EARL_GREY_TEST_SKIPPED( + @"This test is obsolete with notes for passwords disabled."); + } + SaveExamplePasswordFormWithNote(); OpenPasswordManager(); @@ -1521,6 +1644,13 @@ OpenPasswordManager(); + if ([self notesEnabled]) { + [PasswordSettingsAppInterface + setUpMockReauthenticationModuleForPasswordManager]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + } + [[self interactionForSinglePasswordEntryWithDomain:@"example.com" username:@"federated username"] performAction:grey_tap()]; @@ -1675,6 +1805,11 @@ // Checks that an attempt to copy a password provides appropriate feedback when // reauthentication cannot be attempted. - (void)testCopyPasswordToastNoReauth { + if ([self notesEnabled]) { + EARL_GREY_TEST_SKIPPED( + @"This test is not needed with notes for passwords enabled."); + } + // Saving a form is needed for using the "password details" view. SaveExamplePasswordForm(); @@ -1706,6 +1841,11 @@ // Checks that an attempt to view a password provides appropriate feedback when // reauthentication cannot be attempted. - (void)testShowPasswordToastNoReauth { + if ([self notesEnabled]) { + EARL_GREY_TEST_SKIPPED( + @"This test is not needed with notes for passwords enabled."); + } + // Saving a form is needed for using the "password details" view. SaveExamplePasswordForm(); @@ -1774,6 +1914,13 @@ OpenPasswordManager(); + if ([self notesEnabled]) { + [PasswordSettingsAppInterface + setUpMockReauthenticationModuleForPasswordManager]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + } + // Wait for the loading indicator to disappear, and the sections to be on // screen, before scrolling. [[EarlGrey selectElementWithMatcher:SavedPasswordsHeaderMatcher()] @@ -2114,14 +2261,23 @@ OpenPasswordManager(); + if ([self notesEnabled]) { + [PasswordSettingsAppInterface + setUpMockReauthenticationModuleForPasswordManager]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + } + [[self interactionForSinglePasswordEntryWithDomain:@"example.com" username:@"concrete username"] performAction:grey_tap()]; // Check the snackbar in case of successful reauthentication. - [PasswordSettingsAppInterface setUpMockReauthenticationModule]; - [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: - ReauthenticationResult::kSuccess]; + if (![self notesEnabled]) { + [PasswordSettingsAppInterface setUpMockReauthenticationModule]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + } TapEdit(); @@ -2166,14 +2322,23 @@ OpenPasswordManager(); + if ([self notesEnabled]) { + [PasswordSettingsAppInterface + setUpMockReauthenticationModuleForPasswordManager]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + } + [[self interactionForSinglePasswordEntryWithDomain:@"example.com" username:@"concrete username"] performAction:grey_tap()]; // Check the snackbar in case of successful reauthentication. - [PasswordSettingsAppInterface setUpMockReauthenticationModule]; - [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: - ReauthenticationResult::kSuccess]; + if (![self notesEnabled]) { + [PasswordSettingsAppInterface setUpMockReauthenticationModule]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + } TapEdit(); @@ -2241,14 +2406,23 @@ OpenPasswordManager(); + if ([self notesEnabled]) { + [PasswordSettingsAppInterface + setUpMockReauthenticationModuleForPasswordManager]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + } + [[self interactionForSinglePasswordEntryWithDomain:@"example.com" username:@"concrete username1"] performAction:grey_tap()]; // Check the snackbar in case of successful reauthentication. - [PasswordSettingsAppInterface setUpMockReauthenticationModule]; - [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: - ReauthenticationResult::kSuccess]; + if (![self notesEnabled]) { + [PasswordSettingsAppInterface setUpMockReauthenticationModule]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + } TapEdit(); @@ -2283,14 +2457,23 @@ OpenPasswordManager(); + if ([self notesEnabled]) { + [PasswordSettingsAppInterface + setUpMockReauthenticationModuleForPasswordManager]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + } + [[self interactionForSinglePasswordEntryWithDomain:@"example.com" username:@"concrete username"] performAction:grey_tap()]; // Check the snackbar in case of successful reauthentication. - [PasswordSettingsAppInterface setUpMockReauthenticationModule]; - [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: - ReauthenticationResult::kSuccess]; + if (![self notesEnabled]) { + [PasswordSettingsAppInterface setUpMockReauthenticationModule]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + } TapEdit(); @@ -2453,6 +2636,11 @@ // Checks that entering too long note while adding passwords blocks the save // button and displays a footer explanation. - (void)testAddPasswordLayoutWithLongNotes { + if (![self notesEnabled]) { + EARL_GREY_TEST_SKIPPED( + @"This test is obsolote with notes for passwords disabled."); + } + OpenPasswordManager(); [[EarlGrey selectElementWithMatcher:AddPasswordToolbarButton()] @@ -2610,6 +2798,11 @@ // from invalid to valid input in any of the fields (website, password, note), // when there are still other fields with invalid input. - (void)testAddPasswordSaveButtonStateOnFieldChanges { + if (![self notesEnabled]) { + EARL_GREY_TEST_SKIPPED( + @"This test is obsolete with notes for passwords disabled."); + } + OpenPasswordManager(); [[EarlGrey selectElementWithMatcher:AddPasswordToolbarButton()] performAction:grey_tap()]; @@ -2720,13 +2913,22 @@ [[EarlGrey selectElementWithMatcher:AddPasswordSaveButton()] performAction:grey_tap()]; + if ([self notesEnabled]) { + [PasswordSettingsAppInterface + setUpMockReauthenticationModuleForPasswordManager]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + } + [[self interactionForSinglePasswordEntryWithDomain:@"example.com" username:@"new username"] performAction:grey_tap()]; - [PasswordSettingsAppInterface setUpMockReauthenticationModule]; - [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: - ReauthenticationResult::kSuccess]; + if (![self notesEnabled]) { + [PasswordSettingsAppInterface setUpMockReauthenticationModule]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + } TapEdit(); @@ -2828,6 +3030,11 @@ } - (void)testShowHidePassword { + if ([self notesEnabled]) { + EARL_GREY_TEST_SKIPPED( + @"This test is obsolete with notes for passwords enabled."); + } + SaveExamplePasswordForm(); OpenPasswordManager(); @@ -2859,6 +3066,11 @@ // enabled since the reauthentication happens before navigating to the details // view in this scenario. - (void)testShowHidePasswordWithNotesEnabled { + if (![self notesEnabled]) { + EARL_GREY_TEST_SKIPPED( + @"This test is obsolete with notes for passwords disabled."); + } + SaveExamplePasswordForm(); OpenPasswordManager(); @@ -2900,6 +3112,13 @@ OpenPasswordManager(); + if ([self notesEnabled]) { + [PasswordSettingsAppInterface + setUpMockReauthenticationModuleForPasswordManager]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + } + // Make sure the cell is loaded properly before tapping on it. ConditionBlock condition = ^{ NSError* error = nil; @@ -3075,14 +3294,23 @@ OpenPasswordManager(); + if ([self notesEnabled]) { + [PasswordSettingsAppInterface + setUpMockReauthenticationModuleForPasswordManager]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + } + [GetInteractionForPasswordEntry(@"example1.com, 2 accounts") assertWithMatcher:grey_notNil()]; [GetInteractionForPasswordEntry(@"example1.com, 2 accounts") performAction:grey_tap()]; - [PasswordSettingsAppInterface setUpMockReauthenticationModule]; - [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: - ReauthenticationResult::kSuccess]; + if (![self notesEnabled]) { + [PasswordSettingsAppInterface setUpMockReauthenticationModule]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + } [[EarlGrey selectElementWithMatcher:NavigationBarEditButton()] performAction:grey_tap()]; @@ -3236,6 +3464,13 @@ enableSync:NO]; OpenPasswordManager(); + if ([self notesEnabled]) { + [PasswordSettingsAppInterface + setUpMockReauthenticationModuleForPasswordManager]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + } + // `passwordMatcher` includes grey_sufficientlyVisible() because there are // other invisible cells when password details is closed later. id<GREYMatcher> passwordMatcher = @@ -3252,9 +3487,11 @@ [[EarlGrey selectElementWithMatcher:passwordMatcher] performAction:grey_tap()]; - [PasswordSettingsAppInterface setUpMockReauthenticationModule]; - [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: - ReauthenticationResult::kSuccess]; + if (![self notesEnabled]) { + [PasswordSettingsAppInterface setUpMockReauthenticationModule]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + } [[EarlGrey selectElementWithMatcher:grey_accessibilityID( kMovePasswordToAccountButtonId)] @@ -3291,6 +3528,13 @@ OpenPasswordManager(); + if ([self notesEnabled]) { + [PasswordSettingsAppInterface + setUpMockReauthenticationModuleForPasswordManager]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + } + // `passwordMatcher` includes grey_sufficientlyVisible() because there are // other invisible cells when password details is closed later. id<GREYMatcher> passwordMatcher = @@ -3307,9 +3551,11 @@ [[EarlGrey selectElementWithMatcher:passwordMatcher] performAction:grey_tap()]; - [PasswordSettingsAppInterface setUpMockReauthenticationModule]; - [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: - ReauthenticationResult::kSuccess]; + if (![self notesEnabled]) { + [PasswordSettingsAppInterface setUpMockReauthenticationModule]; + [PasswordSettingsAppInterface mockReauthenticationModuleExpectedResult: + ReauthenticationResult::kSuccess]; + } [[EarlGrey selectElementWithMatcher:grey_accessibilityID( kMovePasswordToAccountButtonId)] @@ -3373,3 +3619,23 @@ } @end + +// Rerun all the tests in this file but with kPasswordNotesWithBackup disabled. +// This will be removed once that feature launches fully, but ensures +// regressions aren't introduced in the meantime. +@interface PasswordManagerNotesDisabledTestCase : PasswordManagerTestCase + +@end + +@implementation PasswordManagerNotesDisabledTestCase + +- (BOOL)notesEnabled { + return NO; +} + +// This causes the test case to actually be detected as a test case. The actual +// tests are all inherited from the parent class. +- (void)testEmpty { +} + +@end
diff --git a/ios/chrome/browser/ui/settings/settings_table_view_controller.mm b/ios/chrome/browser/ui/settings/settings_table_view_controller.mm index aa07cd7..8b799fc 100644 --- a/ios/chrome/browser/ui/settings/settings_table_view_controller.mm +++ b/ios/chrome/browser/ui/settings/settings_table_view_controller.mm
@@ -1411,7 +1411,7 @@ [[ContentSettingsTableViewController alloc] initWithBrowser:_browser]; break; case SettingsItemTypeTabs: - // TODO(crbug.com/1418021): Add metrics about tabs settings. + base::RecordAction(base::UserMetricsAction("Settings.Tabs")); [self showTabsSettings]; break; case SettingsItemTypeBandwidth:
diff --git a/ios/chrome/browser/ui/settings/tabs/inactive_tabs/inactive_tabs_settings_table_view_controller.mm b/ios/chrome/browser/ui/settings/tabs/inactive_tabs/inactive_tabs_settings_table_view_controller.mm index 140a6df5..ae79d49 100644 --- a/ios/chrome/browser/ui/settings/tabs/inactive_tabs/inactive_tabs_settings_table_view_controller.mm +++ b/ios/chrome/browser/ui/settings/tabs/inactive_tabs/inactive_tabs_settings_table_view_controller.mm
@@ -5,6 +5,8 @@ #import "ios/chrome/browser/ui/settings/tabs/inactive_tabs/inactive_tabs_settings_table_view_controller.h" #import "base/i18n/message_formatter.h" +#import "base/metrics/user_metrics.h" +#import "base/metrics/user_metrics_action.h" #import "base/notreached.h" #import "base/strings/sys_string_conversions.h" #import "base/time/time.h" @@ -176,13 +178,12 @@ #pragma mark - SettingsControllerProtocol - (void)reportDismissalUserAction { - // TODO(crbug.com/1418021): Add metrics when the user go close Inactive Tabs - // Settings. + base::RecordAction( + base::UserMetricsAction("MobileInactiveTabsSettingsClose")); } - (void)reportBackUserAction { - // TODO(crbug.com/1418021): Add metrics when the user go back from Inactive - // Tabs Settings to Tabs Settings screen. + base::RecordAction(base::UserMetricsAction("MobileInactiveTabsSettingsBack")); } @end
diff --git a/ios/chrome/browser/ui/settings/tabs/tabs_settings_mediator.mm b/ios/chrome/browser/ui/settings/tabs/tabs_settings_mediator.mm index 9e4b4dd..7082f803 100644 --- a/ios/chrome/browser/ui/settings/tabs/tabs_settings_mediator.mm +++ b/ios/chrome/browser/ui/settings/tabs/tabs_settings_mediator.mm
@@ -4,6 +4,8 @@ #import "ios/chrome/browser/ui/settings/tabs/tabs_settings_mediator.h" +#import "base/metrics/user_metrics.h" +#import "base/metrics/user_metrics_action.h" #import "components/prefs/ios/pref_observer_bridge.h" #import "components/prefs/pref_change_registrar.h" #import "components/prefs/pref_service.h" @@ -76,8 +78,7 @@ - (void)tabsSettingsTableViewControllerDidSelectInactiveTabsSettings: (TabsSettingsTableViewController*)tabsSettingsTableViewController { - // TODO(crbug.com/1418021): Add metrics when the user go to inactive tabs - // settings. + base::RecordAction(base::UserMetricsAction("Settings.Tabs.InactiveTabs")); [self.handler showInactiveTabsSettings]; }
diff --git a/ios/chrome/browser/ui/settings/tabs/tabs_settings_table_view_controller.mm b/ios/chrome/browser/ui/settings/tabs/tabs_settings_table_view_controller.mm index 967a7dd..dfe4a9e 100644 --- a/ios/chrome/browser/ui/settings/tabs/tabs_settings_table_view_controller.mm +++ b/ios/chrome/browser/ui/settings/tabs/tabs_settings_table_view_controller.mm
@@ -5,6 +5,8 @@ #import "ios/chrome/browser/ui/settings/tabs/tabs_settings_table_view_controller.h" #import "base/i18n/message_formatter.h" +#import "base/metrics/user_metrics.h" +#import "base/metrics/user_metrics_action.h" #import "base/strings/sys_string_conversions.h" #import "ios/chrome/browser/browser_state/chrome_browser_state.h" #import "ios/chrome/browser/shared/ui/table_view/cells/table_view_detail_icon_item.h" @@ -76,12 +78,11 @@ #pragma mark - SettingsControllerProtocol - (void)reportDismissalUserAction { - // TODO(crbug.com/1418021): Add metrics when the user go close Tabs Settings. + base::RecordAction(base::UserMetricsAction("MobileTabsSettingsClose")); } - (void)reportBackUserAction { - // TODO(crbug.com/1418021): Add metrics when the user go back from Tabs - // Settings to root Settings screen. + base::RecordAction(base::UserMetricsAction("MobileTabsSettingsBack")); } #pragma mark - UITableViewDelegate
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator.mm b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator.mm index ed03f47..82b9d2f 100644 --- a/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator.mm +++ b/ios/chrome/browser/ui/tab_switcher/tab_grid/tab_grid_coordinator.mm
@@ -56,6 +56,7 @@ #import "ios/chrome/browser/ui/gestures/view_controller_trait_collection_observer.h" #import "ios/chrome/browser/ui/gestures/view_revealing_vertical_pan_handler.h" #import "ios/chrome/browser/ui/history/history_coordinator.h" +#import "ios/chrome/browser/ui/history/history_coordinator_delegate.h" #import "ios/chrome/browser/ui/history/public/history_presentation_delegate.h" #import "ios/chrome/browser/ui/incognito_reauth/incognito_reauth_mediator.h" #import "ios/chrome/browser/ui/incognito_reauth/incognito_reauth_scene_agent.h" @@ -102,6 +103,7 @@ InactiveTabsCoordinatorDelegate, SceneStateObserver, SnackbarCoordinatorDelegate, + HistoryCoordinatorDelegate, TabContextMenuDelegate, TabGridMediatorDelegate, TabPresentationDelegate, @@ -333,7 +335,7 @@ } // History may be presented on top of the tab grid. if (self.historyCoordinator) { - [self.historyCoordinator stopWithCompletion:completion]; + [self closeHistoryWithCompletion:completion]; } else if (completion) { completion(); } @@ -937,6 +939,9 @@ self.inactiveTabsButtonMediator = nil; [self.inactiveTabsCoordinator stop]; self.inactiveTabsCoordinator = nil; + + [self.historyCoordinator stop]; + self.historyCoordinator = nil; } #pragma mark - TabPresentationDelegate @@ -1128,6 +1133,7 @@ self.historyCoordinator.loadStrategy = UrlLoadStrategy::ALWAYS_NEW_FOREGROUND_TAB; self.historyCoordinator.presentationDelegate = self; + self.historyCoordinator.delegate = self; [self.historyCoordinator start]; } @@ -1213,6 +1219,19 @@ [self showActiveRegularTabFromRecentTabs]; } +#pragma mark - HistoryCoordinatorDelegate + +- (void)closeHistoryWithCompletion:(ProceduralBlock)completion { + __weak __typeof(self) weakSelf = self; + [self.historyCoordinator dismissWithCompletion:^{ + if (completion) { + completion(); + } + [weakSelf.historyCoordinator stop]; + weakSelf.historyCoordinator = nil; + }]; +} + #pragma mark - TabContextMenuDelegate - (void)shareURL:(const GURL&)URL
diff --git a/ios/chrome/browser/ui/tabs/tab_strip_controller.mm b/ios/chrome/browser/ui/tabs/tab_strip_controller.mm index bdc8f67..6c668a6 100644 --- a/ios/chrome/browser/ui/tabs/tab_strip_controller.mm +++ b/ios/chrome/browser/ui/tabs/tab_strip_controller.mm
@@ -1330,7 +1330,7 @@ // Set the content size to be large enough to contain all the tabs at the // desired width, with the standard overlap, plus the new tab button. CGSize contentSize = CGSizeMake( - (_currentTabWidth * tabCount) - ([self tabOverlap] * tabCount) + + (_currentTabWidth * tabCount) - ([self tabOverlap] * (tabCount - 1)) + CGRectGetWidth([_buttonNewTab frame]) - kNewTabOverlap, tabHeight); if (CGSizeEqualToSize([_tabStripView contentSize], contentSize))
diff --git a/ios/chrome/browser/web/web_state_delegate_browser_agent.h b/ios/chrome/browser/web/web_state_delegate_browser_agent.h index 1f86314..8e44cdb 100644 --- a/ios/chrome/browser/web/web_state_delegate_browser_agent.h +++ b/ios/chrome/browser/web/web_state_delegate_browser_agent.h
@@ -93,7 +93,7 @@ base::OnceCallback<void(bool)> callback) override; web::JavaScriptDialogPresenter* GetJavaScriptDialogPresenter( web::WebState* source) override; - bool HandlePermissionsDecisionRequest( + void HandlePermissionsDecisionRequest( web::WebState* source, NSArray<NSNumber*>* permissions, web::WebStatePermissionDecisionHandler handler) override
diff --git a/ios/chrome/browser/web/web_state_delegate_browser_agent.mm b/ios/chrome/browser/web/web_state_delegate_browser_agent.mm index b1a64c3..ecebc45 100644 --- a/ios/chrome/browser/web/web_state_delegate_browser_agent.mm +++ b/ios/chrome/browser/web/web_state_delegate_browser_agent.mm
@@ -233,19 +233,17 @@ return &java_script_dialog_presenter_; } -bool WebStateDelegateBrowserAgent::HandlePermissionsDecisionRequest( +void WebStateDelegateBrowserAgent::HandlePermissionsDecisionRequest( web::WebState* source, NSArray<NSNumber*>* permissions, - web::WebStatePermissionDecisionHandler handler) { - if (@available(iOS 15.0, *)) { - if (web::features::IsMediaPermissionsControlEnabled()) { - PermissionsTabHelper::FromWebState(source) - ->PresentPermissionsDecisionDialogWithCompletionHandler(permissions, - handler); - return true; - } + web::WebStatePermissionDecisionHandler handler) API_AVAILABLE(ios(15.0)) { + if (web::features::IsMediaPermissionsControlEnabled()) { + PermissionsTabHelper::FromWebState(source) + ->PresentPermissionsDecisionDialogWithCompletionHandler(permissions, + handler); + } else { + handler(web::PermissionDecisionShowDefaultPrompt); } - return false; } void WebStateDelegateBrowserAgent::OnAuthRequired(
diff --git a/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.h b/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.h index a4b645c..d80fe78 100644 --- a/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.h +++ b/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.h
@@ -132,6 +132,12 @@ // nothing. - (void)customizeSubtitle:(UITextView*)subtitle; +// Detent that attempts to fit the preferred height of the content. Detent may +// be inactive in some size classes, so it should be used together with at +// least one other detent. +- (UISheetPresentationControllerDetent*) + preferredHeightDetent API_AVAILABLE(ios(16)); + @end #endif // IOS_CHROME_COMMON_UI_CONFIRMATION_ALERT_CONFIRMATION_ALERT_VIEW_CONTROLLER_H_
diff --git a/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.mm b/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.mm index 275ec82..690f5b12 100644 --- a/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.mm +++ b/ios/chrome/common/ui/confirmation_alert/confirmation_alert_view_controller.mm
@@ -88,6 +88,17 @@ #pragma mark - Public +- (UISheetPresentationControllerDetent*)preferredHeightDetent { + __typeof(self) __weak weakSelf = self; + auto resolver = ^CGFloat( + id<UISheetPresentationControllerDetentResolutionContext> context) { + return [weakSelf detentForPreferredHeightInContext:context]; + }; + return [UISheetPresentationControllerDetent + customDetentWithIdentifier:@"preferred_height" + resolver:resolver]; +} + - (instancetype)init { self = [super initWithNibName:nil bundle:nil]; if (self) { @@ -770,4 +781,42 @@ return tertiaryActionButton; } +- (CGFloat)detentForPreferredHeightInContext: + (id<UISheetPresentationControllerDetentResolutionContext>)context + API_AVAILABLE(ios(16)) { + // Only activate this detent in portrait orientation on iPhone. + UITraitCollection* traitCollection = context.containerTraitCollection; + if (traitCollection.horizontalSizeClass != UIUserInterfaceSizeClassCompact || + traitCollection.verticalSizeClass != UIUserInterfaceSizeClassRegular) { + return UISheetPresentationControllerDetentInactive; + } + + // Obtain container view from presentation controller directly because + // this view may not have been added to its container view yet. + UIView* containerView = self.sheetPresentationController.containerView; + + // Measure compressed height without safe area inset (detent values are + // generally expressed without safe area insets). + CGFloat fittingWidth = containerView.bounds.size.width; + CGSize fittingSize = + CGSizeMake(fittingWidth, UILayoutFittingCompressedSize.height); + CGFloat height = [self.view systemLayoutSizeFittingSize:fittingSize].height; + height -= containerView.safeAreaInsets.bottom; + + // Replace bottom margin calculated based on view's own safe area with bottom + // margin calculated based on the safe area of the container view it will + // eventually live in. This is needed in case the detent value is requested + // before the view has been added to its superview. + height -= MAX(kActionsBottomMargin, self.view.safeAreaInsets.bottom); + height += MAX(kActionsBottomMargin, containerView.safeAreaInsets.bottom); + + // Make sure detent is not larger than 75% of the maximum detent value but at + // least as large as a standard medium detent. + height = MIN(height, 0.75 * context.maximumDetentValue); + CGFloat mediumDetentHeight = [UISheetPresentationControllerDetent.mediumDetent + resolvedValueInContext:context]; + height = MAX(height, mediumDetentHeight); + return height; +} + @end
diff --git a/ios/chrome/test/earl_grey/BUILD.gn b/ios/chrome/test/earl_grey/BUILD.gn index 34d07f4..4cd47332 100644 --- a/ios/chrome/test/earl_grey/BUILD.gn +++ b/ios/chrome/test/earl_grey/BUILD.gn
@@ -97,6 +97,7 @@ "//ios/chrome/browser/passwords", "//ios/chrome/browser/passwords:eg_app_support+eg2", "//ios/chrome/browser/policy:eg_app_support+eg2", + "//ios/chrome/browser/search_engines:eg_app_support+eg2", "//ios/chrome/browser/search_engines:search_engines_util", "//ios/chrome/browser/search_engines:template_url_service_factory", "//ios/chrome/browser/sessions:restoration_agent",
diff --git a/ios/web/public/permissions/README.md b/ios/web/public/permissions/README.md index cb5ced44..5f42ed73 100644 --- a/ios/web/public/permissions/README.md +++ b/ios/web/public/permissions/README.md
@@ -1,3 +1,4 @@ -This directory contains two enums: +This directory contains three enums: - Permission specifies different data or device hardwares that the app/site needs access permissions to; currently we have camera and microphone. - PermissionState specifies whether the user has granted the site permission to use a certain type of permission. + - PermissionDecision an enumeration based on `WKPermissionDecision` to specifies the possible permission decisions for device resource access
diff --git a/ios/web/public/permissions/permissions.h b/ios/web/public/permissions/permissions.h index be34853..e0a76df 100644 --- a/ios/web/public/permissions/permissions.h +++ b/ios/web/public/permissions/permissions.h
@@ -9,9 +9,17 @@ namespace web { +// Enum based on `WKPermissionDecision` to specify the possible permission +// decisions for device resource access. +typedef NS_ENUM(NSInteger, PermissionDecision) { + PermissionDecisionShowDefaultPrompt, + PermissionDecisionGrant, + PermissionDecisionDeny, +}; + // Callback that processes user's permission for a web state to asks the user -// whether it is allowed to access certain permissions on the device. -using WebStatePermissionDecisionHandler = void (^)(BOOL allow); +// the decision to access certain permissions on the device. +using WebStatePermissionDecisionHandler = void (^)(PermissionDecision decision); // Enum specifying different data or device hardwares that the app/site needs // access permissions to.
diff --git a/ios/web/public/test/crw_fake_web_state_delegate.mm b/ios/web/public/test/crw_fake_web_state_delegate.mm index 37f4faf..cc0a68b0 100644 --- a/ios/web/public/test/crw_fake_web_state_delegate.mm +++ b/ios/web/public/test/crw_fake_web_state_delegate.mm
@@ -61,14 +61,13 @@ return nil; } -- (BOOL)webState:(web::WebState*)webState +- (void)webState:(web::WebState*)webState handlePermissions:(NSArray<NSNumber*>*)permissions - decisionHandler:(void (^)(BOOL allow))decisionHandler + decisionHandler:(web::WebStatePermissionDecisionHandler)decisionHandler API_AVAILABLE(ios(15.0)) { _webState = webState; _permissionsRequestHandled = YES; - decisionHandler(YES); - return YES; + decisionHandler(web::PermissionDecisionGrant); } - (void)webState:(web::WebState*)webState
diff --git a/ios/web/public/test/fakes/fake_web_state_delegate.h b/ios/web/public/test/fakes/fake_web_state_delegate.h index 835828b..d9f6533f 100644 --- a/ios/web/public/test/fakes/fake_web_state_delegate.h +++ b/ios/web/public/test/fakes/fake_web_state_delegate.h
@@ -87,7 +87,7 @@ NSURLProtectionSpace* protection_space, NSURLCredential* proposed_credential, AuthCallback callback) override; - bool HandlePermissionsDecisionRequest( + void HandlePermissionsDecisionRequest( WebState* source, NSArray<NSNumber*>* permissions, WebStatePermissionDecisionHandler handler) override @@ -153,10 +153,10 @@ // `HandlePermissionsDecisionRequest`. void ClearLastRequestedPermissions() { last_requested_permissions_ = nil; } - // Sets that whether permissions should be granted or denied the next time + // Sets that permission decision the for next time // `HandlePermissionsDecisionRequest` is called. - void SetShouldGrantPermissions(bool should_grant_permissions) { - should_grant_permissions_ = should_grant_permissions; + void SetPermissionDecision(PermissionDecision permission_decision) { + permission_decision_ = permission_decision; } // Sets the return value of `ShouldAllowAppLaunching`. @@ -181,7 +181,7 @@ std::unique_ptr<FakeAuthenticationRequest> last_authentication_request_; NSArray<NSNumber*>* last_requested_permissions_; bool should_allow_app_launching_ = false; - bool should_grant_permissions_ = false; + PermissionDecision permission_decision_ = PermissionDecisionDeny; }; } // namespace web
diff --git a/ios/web/public/test/fakes/fake_web_state_delegate.mm b/ios/web/public/test/fakes/fake_web_state_delegate.mm index dcbd258..20658922 100644 --- a/ios/web/public/test/fakes/fake_web_state_delegate.mm +++ b/ios/web/public/test/fakes/fake_web_state_delegate.mm
@@ -115,13 +115,12 @@ last_authentication_request_->auth_callback = std::move(callback); } -bool FakeWebStateDelegate::HandlePermissionsDecisionRequest( +void FakeWebStateDelegate::HandlePermissionsDecisionRequest( WebState* source, NSArray<NSNumber*>* permissions, WebStatePermissionDecisionHandler handler) { last_requested_permissions_ = permissions; - handler(should_grant_permissions_); - return true; + handler(permission_decision_); } } // namespace web
diff --git a/ios/web/public/web_state_delegate.h b/ios/web/public/web_state_delegate.h index 38341a58..dfbe87e 100644 --- a/ios/web/public/web_state_delegate.h +++ b/ios/web/public/web_state_delegate.h
@@ -60,13 +60,13 @@ virtual JavaScriptDialogPresenter* GetJavaScriptDialogPresenter( WebState* source); - // Returns whether the delegate is able to handle requests the user's - // permission to access `web::Permission`. + // Called when web resource requests the user's permission to access + // `web::Permission`. // - // If returned `true`, the delegate must use the `handler` function to answer - // to the permissions access request; otherwise, the delegate must NOT use the - // handler. - virtual bool HandlePermissionsDecisionRequest( + // The delegate should use the `handler` function to answer to the request to + // grant, deny media permissions or show the default prompt that asks for + // permissions. + virtual void HandlePermissionsDecisionRequest( WebState* source, NSArray<NSNumber*>* permissions, WebStatePermissionDecisionHandler handler) API_AVAILABLE(ios(15.0));
diff --git a/ios/web/public/web_state_delegate_bridge.h b/ios/web/public/web_state_delegate_bridge.h index 3925a00..1537dd71 100644 --- a/ios/web/public/web_state_delegate_bridge.h +++ b/ios/web/public/web_state_delegate_bridge.h
@@ -47,16 +47,11 @@ (web::WebState*)webState; // Called when the media permission is requested and to acquire the decision -// handler needed to process the user's decision to grant or deny media -// permissions. -// -// If the delegate doesn't implement this method or delegate returned `NO`, the -// web state would still show the default prompt that asks for permissions. If -// delegate returned `YES`, the delegate must use the `handler` function to -// answer to the permissions access request; -- (BOOL)webState:(web::WebState*)webState +// handler needed to process the user's decision to grant, deny media +// permissions or show the default prompt that asks for permissions. +- (void)webState:(web::WebState*)webState handlePermissions:(NSArray<NSNumber*>*)permissions - decisionHandler:(void (^)(BOOL allow))decisionHandler + decisionHandler:(web::WebStatePermissionDecisionHandler)decisionHandler API_AVAILABLE(ios(15.0)); // Called when a request receives an authentication challenge specified by @@ -115,7 +110,7 @@ base::OnceCallback<void(bool)> callback) override; JavaScriptDialogPresenter* GetJavaScriptDialogPresenter( WebState* source) override; - bool HandlePermissionsDecisionRequest( + void HandlePermissionsDecisionRequest( WebState* source, NSArray<NSNumber*>* permissions, WebStatePermissionDecisionHandler handler)
diff --git a/ios/web/web_state/permissions_inttest.mm b/ios/web/web_state/permissions_inttest.mm index c97c44f..fd2e13b 100644 --- a/ios/web/web_state/permissions_inttest.mm +++ b/ios/web/web_state/permissions_inttest.mm
@@ -132,7 +132,7 @@ EXPECT_CALL(observer_, PermissionStateChanged(web_state(), PermissionMicrophone)) .Times(0); - delegate_.SetShouldGrantPermissions(YES); + delegate_.SetPermissionDecision(PermissionDecisionGrant); // Initial load. test::LoadUrl(web_state(), test_server_->GetURL("/camera_only.html")); @@ -165,7 +165,7 @@ PermissionStateAllowed)); // Initial load. - delegate_.SetShouldGrantPermissions(YES); + delegate_.SetPermissionDecision(PermissionDecisionGrant); test::LoadUrl(web_state(), test_server_->GetURL("/microphone_only.html")); SpinRunLoopWithMinDelay(kWaitForPageLoadTimeout); ExpectThatLastRequestedPermissionsMatchesPermissions( @@ -197,7 +197,7 @@ PermissionStateAllowed)); // Initial load. - delegate_.SetShouldGrantPermissions(YES); + delegate_.SetPermissionDecision(PermissionDecisionGrant); test::LoadUrl(web_state(), test_server_->GetURL("/camera_and_microphone.html")); SpinRunLoopWithMinDelay(kWaitForPageLoadTimeout); @@ -224,7 +224,7 @@ PermissionStateChanged(web_state(), PermissionCamera)) .Times(0); - delegate_.SetShouldGrantPermissions(NO); + delegate_.SetPermissionDecision(PermissionDecisionDeny); test::LoadUrl(web_state(), test_server_->GetURL("/camera_only.html")); SpinRunLoopWithMinDelay(kWaitForPageLoadTimeout); ExpectThatLastRequestedPermissionsMatchesPermissions( @@ -239,7 +239,7 @@ TEST_F(PermissionsInttest, TestsThatWebStateShouldNotAlterPermissionIfNotAccessible) { if (@available(iOS 15.0, *)) { - delegate_.SetShouldGrantPermissions(NO); + delegate_.SetPermissionDecision(PermissionDecisionDeny); test::LoadUrl(web_state(), test_server_->GetURL("/camera_only.html")); SpinRunLoopWithMinDelay(kWaitForPageLoadTimeout); ExpectThatLastRequestedPermissionsMatchesPermissions( @@ -272,7 +272,7 @@ TEST_F(PermissionsInttest, TestsThatPageReloadResetsPermissionState) { if (@available(iOS 15.0, *)) { // Initial load should allow permission. - delegate_.SetShouldGrantPermissions(YES); + delegate_.SetPermissionDecision(PermissionDecisionGrant); test::LoadUrl(web_state(), test_server_->GetURL("/camera_only.html")); SpinRunLoopWithMinDelay(kWaitForPageLoadTimeout); EXPECT_EQ(web_state()->GetStateForPermission(PermissionCamera), @@ -283,7 +283,7 @@ // Reload should reset permission. Handler should be called again, and // permission state should be NotAccessible. delegate_.ClearLastRequestedPermissions(); - delegate_.SetShouldGrantPermissions(NO); + delegate_.SetPermissionDecision(PermissionDecisionDeny); web_state()->GetNavigationManager()->Reload(ReloadType::NORMAL, /*check_for_repost=*/false); SpinRunLoopWithMinDelay(kWaitForPageLoadTimeout); @@ -301,7 +301,7 @@ TEST_F(PermissionsInttest, TestsThatWebStateDoesNotPreservePermissionState) { if (@available(iOS 15.0, *)) { // Initial load should allow permission. - delegate_.SetShouldGrantPermissions(YES); + delegate_.SetPermissionDecision(PermissionDecisionGrant); test::LoadUrl(web_state(), test_server_->GetURL("/camera_only.html")); EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForPageLoadTimeout, ^bool { return web_state()->GetStateForPermission(PermissionCamera) == @@ -314,7 +314,7 @@ // called again, permission state should be NotAccessible and the observer // should NOT be invoked. delegate_.ClearLastRequestedPermissions(); - delegate_.SetShouldGrantPermissions(NO); + delegate_.SetPermissionDecision(PermissionDecisionDeny); test::LoadUrl(web_state(), test_server_->GetURL("/camera_and_microphone.html")); SpinRunLoopWithMinDelay(kWaitForPageLoadTimeout); @@ -335,7 +335,7 @@ TestsThatMovingBackwardOrForwardResetsPermissionState) { if (@available(iOS 15.0, *)) { // Initial load for both pages should allow permission. - delegate_.SetShouldGrantPermissions(YES); + delegate_.SetPermissionDecision(PermissionDecisionGrant); test::LoadUrl(web_state(), test_server_->GetURL("/microphone_only.html")); EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForPageLoadTimeout, ^bool { return web_state()->GetStateForPermission(PermissionMicrophone) == @@ -371,7 +371,7 @@ // WKMediaCaptureStateNone. The two following lines of code should be // uncommented when this is fixed. - // delegate_.SetShouldGrantPermissions(NO); + // delegate_.SetPermissionDecision(PermissionDecisionDeny); // handler_.decision = WKPermissionDecisionDeny; web_state()->GetNavigationManager()->GoBack(); EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForPageLoadTimeout, ^bool {
diff --git a/ios/web/web_state/web_state_delegate.mm b/ios/web/web_state/web_state_delegate.mm index 85ac58c..960ba59 100644 --- a/ios/web/web_state/web_state_delegate.mm +++ b/ios/web/web_state/web_state_delegate.mm
@@ -46,11 +46,11 @@ return nullptr; } -bool WebStateDelegate::HandlePermissionsDecisionRequest( +void WebStateDelegate::HandlePermissionsDecisionRequest( WebState* source, NSArray<NSNumber*>* permissions, WebStatePermissionDecisionHandler handler) { - return false; + handler(PermissionDecisionShowDefaultPrompt); } void WebStateDelegate::OnAuthRequired(WebState* source,
diff --git a/ios/web/web_state/web_state_delegate_bridge.mm b/ios/web/web_state/web_state_delegate_bridge.mm index e71c526..b58fd3e5 100644 --- a/ios/web/web_state/web_state_delegate_bridge.mm +++ b/ios/web/web_state/web_state_delegate_bridge.mm
@@ -70,17 +70,18 @@ return nullptr; } -bool WebStateDelegateBridge::HandlePermissionsDecisionRequest( +void WebStateDelegateBridge::HandlePermissionsDecisionRequest( WebState* source, NSArray<NSNumber*>* permissions, WebStatePermissionDecisionHandler handler) API_AVAILABLE(ios(15.0)) { if ([delegate_ respondsToSelector:@selector(webState: handlePermissions:decisionHandler:)]) { - return [delegate_ webState:source - handlePermissions:permissions - decisionHandler:handler]; + [delegate_ webState:source + handlePermissions:permissions + decisionHandler:handler]; + } else { + handler(PermissionDecisionShowDefaultPrompt); } - return false; } void WebStateDelegateBridge::OnAuthRequired(
diff --git a/ios/web/web_state/web_state_delegate_bridge_unittest.mm b/ios/web/web_state/web_state_delegate_bridge_unittest.mm index 9f15440..8409079 100644 --- a/ios/web/web_state/web_state_delegate_bridge_unittest.mm +++ b/ios/web/web_state/web_state_delegate_bridge_unittest.mm
@@ -142,14 +142,13 @@ __block bool callback_called = false; EXPECT_FALSE([delegate_ permissionsRequestHandled]); EXPECT_FALSE([delegate_ webState]); - bool useHandlerAnswerTheRequest = bridge_->HandlePermissionsDecisionRequest( - &fake_web_state_, @[], ^(bool allow) { - EXPECT_TRUE(allow); + bridge_->HandlePermissionsDecisionRequest( + &fake_web_state_, @[], ^(PermissionDecision decision) { + EXPECT_EQ(decision, PermissionDecisionGrant); callback_called = true; }); EXPECT_TRUE([delegate_ permissionsRequestHandled]); EXPECT_EQ(&fake_web_state_, [delegate_ webState]); - EXPECT_TRUE(useHandlerAnswerTheRequest); EXPECT_TRUE(callback_called); } } @@ -160,13 +159,16 @@ HandlePermissionsDecisionRequestWithNoDelegateMethod) { if (@available(iOS 15.0, *)) { __block bool callback_called = false; - bool useHandlerAnswerTheRequest = - empty_delegate_bridge_->HandlePermissionsDecisionRequest( - nullptr, @[], ^(bool allow) { - callback_called = true; - }); - EXPECT_FALSE(useHandlerAnswerTheRequest); - EXPECT_FALSE(callback_called); + empty_delegate_bridge_->HandlePermissionsDecisionRequest( + nullptr, @[], ^(PermissionDecision decision) { + // Default decision `PermissionDecisionShowDefaultPrompt` will be used + // when delegate doesn't implement + // `webState:handlePermissions:decisionHandler:` method to handle the + // permissions. + EXPECT_EQ(decision, PermissionDecisionShowDefaultPrompt); + callback_called = true; + }); + EXPECT_TRUE(callback_called); } }
diff --git a/ios/web/web_state/web_state_impl_realized_web_state.mm b/ios/web/web_state/web_state_impl_realized_web_state.mm index bd96aab..4c0fec2 100644 --- a/ios/web/web_state/web_state_impl_realized_web_state.mm +++ b/ios/web/web_state/web_state_impl_realized_web_state.mm
@@ -851,17 +851,24 @@ void WebStateImpl::RealizedWebState::RequestPermissionsWithDecisionHandler( NSArray<NSNumber*>* permissions, PermissionDecisionHandler web_view_decision_handler) { - bool delegate_can_handle_decision = false; if (delegate_) { WebStatePermissionDecisionHandler web_state_decision_handler = - ^(BOOL allowed) { - allowed ? web_view_decision_handler(WKPermissionDecisionGrant) - : web_view_decision_handler(WKPermissionDecisionDeny); + ^(PermissionDecision decision) { + switch (decision) { + case PermissionDecisionShowDefaultPrompt: + web_view_decision_handler(WKPermissionDecisionPrompt); + break; + case PermissionDecisionGrant: + web_view_decision_handler(WKPermissionDecisionGrant); + break; + case PermissionDecisionDeny: + web_view_decision_handler(WKPermissionDecisionDeny); + break; + } }; - delegate_can_handle_decision = delegate_->HandlePermissionsDecisionRequest( - owner_, permissions, web_state_decision_handler); - } - if (!delegate_can_handle_decision) { + delegate_->HandlePermissionsDecisionRequest(owner_, permissions, + web_state_decision_handler); + } else { web_view_decision_handler(WKPermissionDecisionPrompt); } }
diff --git a/ios/web_view/internal/cwv_web_view.mm b/ios/web_view/internal/cwv_web_view.mm index dd76614..3d3e315 100644 --- a/ios/web_view/internal/cwv_web_view.mm +++ b/ios/web_view/internal/cwv_web_view.mm
@@ -582,9 +582,9 @@ return _javaScriptDialogPresenter.get(); } -- (BOOL)webState:(web::WebState*)webState +- (void)webState:(web::WebState*)webState handlePermissions:(NSArray<NSNumber*>*)permissions - decisionHandler:(void (^)(BOOL allow))decisionHandler + decisionHandler:(web::WebStatePermissionDecisionHandler)decisionHandler API_AVAILABLE(ios(15.0)) { DCHECK(decisionHandler); CWVMediaCaptureType mediaCaptureType; @@ -609,17 +609,22 @@ requestMediaCapturePermissionForType:mediaCaptureType decisionHandler:^(CWVPermissionDecision decision) { switch (decision) { + case CWVPermissionDecisionPrompt: + decisionHandler( + web:: + PermissionDecisionShowDefaultPrompt); + break; case CWVPermissionDecisionGrant: - decisionHandler(YES); + decisionHandler( + web::PermissionDecisionGrant); break; case CWVPermissionDecisionDeny: - decisionHandler(NO); + decisionHandler(web::PermissionDecisionDeny); break; } }]; - return YES; } else { - return NO; + decisionHandler(web::PermissionDecisionShowDefaultPrompt); } }
diff --git a/ios/web_view/public/cwv_ui_delegate.h b/ios/web_view/public/cwv_ui_delegate.h index 649149c..4bd4841 100644 --- a/ios/web_view/public/cwv_ui_delegate.h +++ b/ios/web_view/public/cwv_ui_delegate.h
@@ -20,6 +20,7 @@ @class CWVNavigationAction; typedef NS_ENUM(NSInteger, CWVPermissionDecision) { + CWVPermissionDecisionPrompt, CWVPermissionDecisionGrant, CWVPermissionDecisionDeny, } API_AVAILABLE(ios(15.0));
diff --git a/ios/web_view/shell/BUILD.gn b/ios/web_view/shell/BUILD.gn index 094472a0..854bfc7 100644 --- a/ios/web_view/shell/BUILD.gn +++ b/ios/web_view/shell/BUILD.gn
@@ -2,8 +2,10 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//build/buildflag_header.gni") import("//build/config/ios/ios_sdk.gni") import("//build/config/ios/rules.gni") +import("//ios/web_view/features.gni") declare_args() { # The bundle identifier. Overriding this will affect the provisioning profile @@ -35,6 +37,11 @@ ios_web_view_shell_entitlements_path = "//build/config/ios/entitlements.plist" } +buildflag_header("buildflags") { + header = "buildflags.h" + flags = [ "IOS_WEB_VIEW_INCLUDE_CRONET=$ios_web_view_include_cronet" ] +} + ios_app_bundle("ios_web_view_shell") { info_plist = "Info.plist" @@ -54,10 +61,19 @@ configs += [ "//build/config/compiler:enable_arc" ] } +source_set("cwv_framework") { + sources = [ "cwv_framework.h" ] + + deps = [ + ":buildflags", + "//ios/web_view:web_view+link", + ] +} + source_set("shell_auth_service_interface") { sources = [ "shell_auth_service.h" ] - deps = [ "//ios/web_view:web_view+link" ] + deps = [ ":cwv_framework" ] configs += [ "//build/config/compiler:enable_arc" ] } @@ -66,8 +82,8 @@ sources = [ "shell_auth_service_fake.m" ] deps = [ + ":cwv_framework", ":shell_auth_service_interface", - "//ios/web_view:web_view+link", ] configs += [ "//build/config/compiler:enable_arc" ] @@ -76,7 +92,7 @@ source_set("shell_risk_data_loader_interface") { sources = [ "shell_risk_data_loader.h" ] - deps = [ "//ios/web_view:web_view+link" ] + deps = [ ":cwv_framework" ] configs += [ "//build/config/compiler:enable_arc" ] } @@ -85,8 +101,8 @@ sources = [ "shell_risk_data_loader_fake.m" ] deps = [ + ":cwv_framework", ":shell_risk_data_loader_interface", - "//ios/web_view:web_view+link", ] configs += [ "//build/config/compiler:enable_arc" ] @@ -96,8 +112,8 @@ sources = [ "shell_trusted_vault_provider.h" ] deps = [ + ":cwv_framework", ":shell_auth_service_interface", - "//ios/web_view:web_view+link", ] configs += [ "//build/config/compiler:enable_arc" ] @@ -107,8 +123,8 @@ sources = [ "shell_trusted_vault_provider_fake.m" ] deps = [ + ":cwv_framework", ":shell_trusted_vault_provider_interface", - "//ios/web_view:web_view+link", ] configs += [ "//build/config/compiler:enable_arc" ] @@ -128,12 +144,12 @@ ] deps = [ + ":cwv_framework", ":shell_auth_service_interface", ":shell_risk_data_loader_interface", ":shell_trusted_vault_provider_interface", "//base", "//ios/third_party/webkit", - "//ios/web_view:web_view+link", ios_web_view_shell_auth_service, ios_web_view_shell_risk_data_loader, ios_web_view_shell_trusted_vault_provider,
diff --git a/ios/web_view/shell/cwv_framework.h b/ios/web_view/shell/cwv_framework.h new file mode 100644 index 0000000..afe0ba4 --- /dev/null +++ b/ios/web_view/shell/cwv_framework.h
@@ -0,0 +1,16 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef IOS_WEB_VIEW_SHELL_CWV_FRAMEWORK_H_ +#define IOS_WEB_VIEW_SHELL_CWV_FRAMEWORK_H_ + +#import "ios/web_view/shell/buildflags.h" + +#if BUILDFLAG(IOS_WEB_VIEW_INCLUDE_CRONET) +#import <CronetChromeWebView/CronetChromeWebView.h> +#else +#import <ChromeWebView/ChromeWebView.h> +#endif + +#endif // IOS_WEB_VIEW_SHELL_CWV_FRAMEWORK_H_
diff --git a/ios/web_view/shell/shell_auth_service.h b/ios/web_view/shell/shell_auth_service.h index 23587bd..0e7a5d1 100644 --- a/ios/web_view/shell/shell_auth_service.h +++ b/ios/web_view/shell/shell_auth_service.h
@@ -4,7 +4,7 @@ #ifndef IOS_WEB_VIEW_SHELL_SHELL_AUTH_SERVICE_H_ #define IOS_WEB_VIEW_SHELL_SHELL_AUTH_SERVICE_H_ -#import <ChromeWebView/ChromeWebView.h> +#import "ios/web_view/shell/cwv_framework.h" NS_ASSUME_NONNULL_BEGIN
diff --git a/ios/web_view/shell/shell_autofill_delegate.h b/ios/web_view/shell/shell_autofill_delegate.h index 33bc4cc..0a617a6 100644 --- a/ios/web_view/shell/shell_autofill_delegate.h +++ b/ios/web_view/shell/shell_autofill_delegate.h
@@ -5,8 +5,8 @@ #ifndef IOS_WEB_VIEW_SHELL_SHELL_AUTOFILL_DELEGATE_H_ #define IOS_WEB_VIEW_SHELL_SHELL_AUTOFILL_DELEGATE_H_ -#import <ChromeWebView/ChromeWebView.h> #import <Foundation/Foundation.h> +#import "ios/web_view/shell/cwv_framework.h" NS_ASSUME_NONNULL_BEGIN
diff --git a/ios/web_view/shell/shell_translation_delegate.h b/ios/web_view/shell/shell_translation_delegate.h index 45be1c9..2bd57a1 100644 --- a/ios/web_view/shell/shell_translation_delegate.h +++ b/ios/web_view/shell/shell_translation_delegate.h
@@ -5,7 +5,8 @@ #ifndef IOS_WEB_VIEW_SHELL_SHELL_TRANSLATION_DELEGATE_H_ #define IOS_WEB_VIEW_SHELL_SHELL_TRANSLATION_DELEGATE_H_ -#import <ChromeWebView/ChromeWebView.h> +#import "ios/web_view/shell/cwv_framework.h" + #import <Foundation/Foundation.h> NS_ASSUME_NONNULL_BEGIN
diff --git a/ios/web_view/shell/shell_trusted_vault_provider.h b/ios/web_view/shell/shell_trusted_vault_provider.h index 8b24d3d..36aaa88e 100644 --- a/ios/web_view/shell/shell_trusted_vault_provider.h +++ b/ios/web_view/shell/shell_trusted_vault_provider.h
@@ -5,7 +5,7 @@ #ifndef IOS_WEB_VIEW_SHELL_SHELL_TRUSTED_VAULT_PROVIDER_H_ #define IOS_WEB_VIEW_SHELL_SHELL_TRUSTED_VAULT_PROVIDER_H_ -#import <ChromeWebView/ChromeWebView.h> +#import "ios/web_view/shell/cwv_framework.h" #import "ios/web_view/shell/shell_auth_service.h"
diff --git a/ios/web_view/shell/shell_view_controller.h b/ios/web_view/shell/shell_view_controller.h index 1a380921..f4fa8cd 100644 --- a/ios/web_view/shell/shell_view_controller.h +++ b/ios/web_view/shell/shell_view_controller.h
@@ -5,7 +5,8 @@ #ifndef IOS_WEB_VIEW_SHELL_SHELL_VIEW_CONTROLLER_H_ #define IOS_WEB_VIEW_SHELL_SHELL_VIEW_CONTROLLER_H_ -#import <ChromeWebView/ChromeWebView.h> +#import "ios/web_view/shell/cwv_framework.h" + #import <UIKit/UIKit.h> NS_ASSUME_NONNULL_BEGIN
diff --git a/ios/web_view/shell/shell_view_controller.m b/ios/web_view/shell/shell_view_controller.m index 5d00996..8f5ee7b 100644 --- a/ios/web_view/shell/shell_view_controller.m +++ b/ios/web_view/shell/shell_view_controller.m
@@ -1160,6 +1160,13 @@ [alertController addAction:[UIAlertAction + actionWithTitle:@"Show Default Prompt" + style:UIAlertActionStyleDefault + handler:^(UIAlertAction* action) { + decisionHandler(CWVPermissionDecisionPrompt); + }]]; + [alertController + addAction:[UIAlertAction actionWithTitle:@"Grant" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) {
diff --git a/net/base/network_interfaces_linux.cc b/net/base/network_interfaces_linux.cc index ff43b4e..e4f3ec5 100644 --- a/net/base/network_interfaces_linux.cc +++ b/net/base/network_interfaces_linux.cc
@@ -18,6 +18,7 @@ #include <sys/ioctl.h> #include <sys/types.h> +#include "base/feature_list.h" #include "base/files/file_path.h" #include "base/files/scoped_file.h" #include "base/strings/escape.h" @@ -26,10 +27,13 @@ #include "base/strings/string_util.h" #include "base/threading/thread_restrictions.h" #include "build/build_config.h" +#include "net/base/address_map_linux.h" #include "net/base/address_tracker_linux.h" +#include "net/base/features.h" #include "net/base/ip_endpoint.h" #include "net/base/net_errors.h" #include "net/base/network_interfaces_posix.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "url/gurl.h" #if BUILDFLAG(IS_ANDROID) @@ -238,13 +242,27 @@ network.type = internal::GetInterfaceConnectionType(network.name); return ret; } -#endif +#endif // BUILDFLAG(IS_ANDROID) - internal::AddressTrackerLinux tracker; - tracker.Init(); + const AddressMapOwnerLinux* map_owner = nullptr; + absl::optional<internal::AddressTrackerLinux> temp_tracker; +#if BUILDFLAG(IS_LINUX) + // If NetworkChangeNotifier already maintains a map owner in this process, use + // it. + if (base::FeatureList::IsEnabled(features::kAddressTrackerLinuxIsProxied)) { + map_owner = NetworkChangeNotifier::GetAddressMapOwner(); + } +#endif // BUILDFLAG(IS_LINUX) + if (!map_owner) { + // If there is no existing map_owner, create an AdressTrackerLinux and + // initialize it. + temp_tracker.emplace(); + temp_tracker->Init(); + map_owner = &temp_tracker.value(); + } return internal::GetNetworkListImpl( - networks, policy, tracker.GetOnlineLinks(), tracker.GetAddressMap(), + networks, policy, map_owner->GetOnlineLinks(), map_owner->GetAddressMap(), &internal::AddressTrackerLinux::GetInterfaceName); }
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index c304ef0..133908386 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -9696,6 +9696,21 @@ ] } ], + "PasswordNotesWithBackup": [ + { + "platforms": [ + "ios" + ], + "experiments": [ + { + "name": "Enabled", + "enable_features": [ + "PasswordNotesWithBackup" + ] + } + ] + } + ], "PasswordsGrouping": [ { "platforms": [ @@ -14458,6 +14473,22 @@ ] } ], + "WebRTC-RtcEventLogNewFormat": [ + { + "platforms": [ + "linux", + "mac", + "windows", + "chromeos", + "chromeos_lacros" + ], + "experiments": [ + { + "name": "Enabled" + } + ] + } + ], "WebRTC-RttMult": [ { "platforms": [
diff --git a/third_party/blink/common/BUILD.gn b/third_party/blink/common/BUILD.gn index b993939e..1ea03285 100644 --- a/third_party/blink/common/BUILD.gn +++ b/third_party/blink/common/BUILD.gn
@@ -326,6 +326,7 @@ "//ui/display", "//ui/display/mojom", "//ui/events:events_base", + "//ui/events/mojom:event_latency_metadata_mojom", "//ui/latency/mojom:shared_mojom_traits", ]
diff --git a/third_party/blink/common/DEPS b/third_party/blink/common/DEPS index 40bf560cf..77997ecf2 100644 --- a/third_party/blink/common/DEPS +++ b/third_party/blink/common/DEPS
@@ -41,6 +41,7 @@ "+ui/base/dragdrop/mojom", "+ui/display", "+ui/events/base_event_utils.h", + "+ui/events/mojom/event_latency_metadata_mojom_traits.h", "+ui/gfx/presentation_feedback.h", "+ui/gfx/transform.h", "+ui/gfx/geometry",
diff --git a/third_party/blink/common/input/web_coalesced_input_event_mojom_traits.cc b/third_party/blink/common/input/web_coalesced_input_event_mojom_traits.cc index cae8aad..1adbb11 100644 --- a/third_party/blink/common/input/web_coalesced_input_event_mojom_traits.cc +++ b/third_party/blink/common/input/web_coalesced_input_event_mojom_traits.cc
@@ -13,6 +13,7 @@ #include "third_party/blink/public/common/input/web_gesture_event.h" #include "third_party/blink/public/common/input/web_keyboard_event.h" #include "third_party/blink/public/common/input/web_mouse_wheel_event.h" +#include "ui/events/mojom/event_latency_metadata_mojom_traits.h" #include "ui/latency/mojom/latency_info_mojom_traits.h" namespace mojo { @@ -361,6 +362,12 @@ return false; } + ui::EventLatencyMetadata event_latency_metadata; + if (!event.ReadEventLatencyMetadata(&event_latency_metadata)) { + return false; + }; + input_event->GetModifiableEventLatencyMetadata() = + std::move(event_latency_metadata); ui::LatencyInfo latency_info; if (!event.ReadLatency(&latency_info)) return false;
diff --git a/third_party/blink/public/common/DEPS b/third_party/blink/public/common/DEPS index c2c3018..74484c759 100644 --- a/third_party/blink/public/common/DEPS +++ b/third_party/blink/public/common/DEPS
@@ -36,5 +36,6 @@ "+ui/gfx/display_color_spaces.h", "+ui/gfx/geometry", "+ui/latency/latency_info.h", + "+ui/events/event_latency_metadata.h", "+url", ]
diff --git a/third_party/blink/public/common/input/web_coalesced_input_event_mojom_traits.h b/third_party/blink/public/common/input/web_coalesced_input_event_mojom_traits.h index aa7f806..31edf22 100644 --- a/third_party/blink/public/common/input/web_coalesced_input_event_mojom_traits.h +++ b/third_party/blink/public/common/input/web_coalesced_input_event_mojom_traits.h
@@ -7,6 +7,7 @@ #include "third_party/blink/public/common/common_export.h" #include "third_party/blink/public/common/input/web_coalesced_input_event.h" +#include "third_party/blink/public/common/input/web_input_event.h" #include "third_party/blink/public/mojom/input/input_handler.mojom.h" namespace mojo { @@ -35,6 +36,11 @@ return event->latency_info(); } + static const ui::EventLatencyMetadata& event_latency_metadata( + const std::unique_ptr<blink::WebCoalescedInputEvent>& event) { + return event->Event().GetEventLatencyMetadata(); + } + static blink::mojom::KeyDataPtr key_data( const std::unique_ptr<blink::WebCoalescedInputEvent>& event); static blink::mojom::PointerDataPtr pointer_data(
diff --git a/third_party/blink/public/common/input/web_input_event.h b/third_party/blink/public/common/input/web_input_event.h index d6ee25c8..7fe9ec3 100644 --- a/third_party/blink/public/common/input/web_input_event.h +++ b/third_party/blink/public/common/input/web_input_event.h
@@ -40,6 +40,7 @@ #include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/blink/public/common/common_export.h" #include "third_party/blink/public/mojom/input/input_event.mojom-shared.h" +#include "ui/events/event_latency_metadata.h" #include "ui/events/types/event_type.h" #include "ui/gfx/geometry/point_f.h" #include "ui/gfx/geometry/vector2d_f.h" @@ -314,6 +315,13 @@ base::TimeTicks TimeStamp() const { return time_stamp_; } void SetTimeStamp(base::TimeTicks time_stamp) { time_stamp_ = time_stamp; } + const ui::EventLatencyMetadata& GetEventLatencyMetadata() const { + return event_latency_metadata_; + } + ui::EventLatencyMetadata& GetModifiableEventLatencyMetadata() { + return event_latency_metadata_; + } + void SetTargetFrameMovedRecently() { modifiers_ |= kTargetFrameMovedRecently; } @@ -357,6 +365,8 @@ base::TimeTicks time_stamp_; Type type_ = Type::kUndefined; int modifiers_ = kNoModifiers; + + ui::EventLatencyMetadata event_latency_metadata_; }; } // namespace blink
diff --git a/third_party/blink/public/mojom/BUILD.gn b/third_party/blink/public/mojom/BUILD.gn index 88fd5ab..12439f3 100644 --- a/third_party/blink/public/mojom/BUILD.gn +++ b/third_party/blink/public/mojom/BUILD.gn
@@ -298,6 +298,7 @@ "//ui/base/mojom", "//ui/display/mojom", "//ui/events/mojom", + "//ui/events/mojom:event_latency_metadata_mojom", "//ui/gfx/geometry/mojom", "//ui/gfx/mojom", "//ui/gfx/range/mojom",
diff --git a/third_party/blink/public/mojom/input/input_handler.mojom b/third_party/blink/public/mojom/input/input_handler.mojom index 020807b..7e289ec 100644 --- a/third_party/blink/public/mojom/input/input_handler.mojom +++ b/third_party/blink/public/mojom/input/input_handler.mojom
@@ -4,27 +4,28 @@ module blink.mojom; +import "cc/mojom/browser_controls_state.mojom"; import "cc/mojom/overscroll_behavior.mojom"; import "cc/mojom/touch_action.mojom"; import "mojo/public/mojom/base/string16.mojom"; import "mojo/public/mojom/base/time.mojom"; import "third_party/blink/public/mojom/input/gesture_event.mojom"; -import "third_party/blink/public/mojom/input/input_event.mojom"; +import "third_party/blink/public/mojom/input/handwriting_gesture_result.mojom"; import "third_party/blink/public/mojom/input/input_event_result.mojom"; +import "third_party/blink/public/mojom/input/input_event.mojom"; import "third_party/blink/public/mojom/input/pointer_lock_context.mojom"; import "third_party/blink/public/mojom/input/pointer_lock_result.mojom"; +import "third_party/blink/public/mojom/input/stylus_writing_gesture.mojom"; import "third_party/blink/public/mojom/input/touch_event.mojom"; import "third_party/blink/public/mojom/selection_menu/selection_menu_behavior.mojom"; import "ui/base/ime/mojom/ime_types.mojom"; -import "third_party/blink/public/mojom/input/handwriting_gesture_result.mojom"; -import "third_party/blink/public/mojom/input/stylus_writing_gesture.mojom"; -import "ui/events/mojom/event.mojom"; import "ui/events/mojom/event_constants.mojom"; +import "ui/events/mojom/event_latency_metadata.mojom"; +import "ui/events/mojom/event.mojom"; import "ui/events/mojom/scroll_granularity.mojom"; import "ui/gfx/geometry/mojom/geometry.mojom"; import "ui/gfx/range/mojom/range.mojom"; import "ui/latency/mojom/latency_info.mojom"; -import "cc/mojom/browser_controls_state.mojom"; [EnableIf=is_android] import "third_party/blink/public/mojom/input/synchronous_compositor.mojom"; @@ -169,6 +170,7 @@ int32 modifiers; mojo_base.mojom.TimeTicks timestamp; ui.mojom.LatencyInfo latency; + ui.mojom.EventLatencyMetadata event_latency_metadata; KeyData? key_data; PointerData? pointer_data; GestureData? gesture_data;
diff --git a/third_party/blink/public/mojom/speculation_rules/speculation_rules.mojom b/third_party/blink/public/mojom/speculation_rules/speculation_rules.mojom index a9cf42ec..ae63b31 100644 --- a/third_party/blink/public/mojom/speculation_rules/speculation_rules.mojom +++ b/third_party/blink/public/mojom/speculation_rules/speculation_rules.mojom
@@ -56,6 +56,14 @@ kEager, }; +// Specifies the v8 world where the JavaScript runs if the JavaScript injects +// the speculation rule. +enum SpeculationInjectionWorld { + kNone, // No v8 context, i.e. statically inserted script tags. + kMain, + kIsolated, +}; + // A single candidate: a URL, an action, a referrer, and any associated // metadata that might be needed to make a decision. // https://wicg.github.io/nav-speculation/speculation-rules.html#prefetch-candidate @@ -86,4 +94,8 @@ // The expected No-Vary-Search header if specified. // Explainer: https://github.com/WICG/nav-speculation/blob/main/no-vary-search.md#a-referrer-hint network.mojom.NoVarySearch? no_vary_search_expected; + + // The v8 world where the JavaScript runs if the speculation rule is injected + // by the JavaScript. + SpeculationInjectionWorld injection_world = kNone; };
diff --git a/third_party/blink/renderer/core/animation/animation.cc b/third_party/blink/renderer/core/animation/animation.cc index dd0ddb0..1b23fb1b 100644 --- a/third_party/blink/renderer/core/animation/animation.cc +++ b/third_party/blink/renderer/core/animation/animation.cc
@@ -941,8 +941,9 @@ // Update content timing to be based on new timeline type. This ensures that // EffectEnd() is returning a value appropriate to the new timeline. - if (content_ && timeline_) + if (content_) { content_->InvalidateNormalizedTiming(); + } reset_current_time_on_resume_ = false; @@ -952,39 +953,37 @@ keyframe_effect->Model()->SetViewTimelineIfRequired(view_timeline); } - if (timeline) { - if (!timeline->IsMonotonicallyIncreasing()) { - ApplyPendingPlaybackRate(); - AnimationTimeDelta boundary_time = - (playback_rate_ > 0) ? AnimationTimeDelta() : EffectEnd(); - switch (old_play_state) { - case kIdle: - break; + if (timeline && !timeline->IsMonotonicallyIncreasing()) { + ApplyPendingPlaybackRate(); + AnimationTimeDelta boundary_time = + (playback_rate_ > 0) ? AnimationTimeDelta() : EffectEnd(); + switch (old_play_state) { + case kIdle: + break; - case kRunning: - case kFinished: - // A non-monotonic timeline has a fixed start time at the beginning or - // end of the timeline. + case kRunning: + case kFinished: + // A non-monotonic timeline has a fixed start time at the beginning or + // end of the timeline. + start_time_ = boundary_time; + break; + + case kPaused: + if (old_current_time) { + reset_current_time_on_resume_ = true; + start_time_ = absl::nullopt; + hold_time_ = progress * EffectEnd(); + } else if (PendingInternal()) { start_time_ = boundary_time; - break; + } + break; - case kPaused: - if (old_current_time) { - reset_current_time_on_resume_ = true; - start_time_ = absl::nullopt; - hold_time_ = progress * EffectEnd(); - } else if (PendingInternal()) { - start_time_ = boundary_time; - } - break; - - default: - NOTREACHED(); - } - } else if (old_current_time && old_timeline && - !old_timeline->IsMonotonicallyIncreasing()) { - SetCurrentTimeInternal(progress * EffectEnd()); + default: + NOTREACHED(); } + } else if (old_current_time && old_timeline && + !old_timeline->IsMonotonicallyIncreasing()) { + SetCurrentTimeInternal(progress * EffectEnd()); } // 4. If the start time of animation is resolved, make the animation’s hold @@ -2293,19 +2292,32 @@ } void Animation::OnRangeUpdate() { + // Change in animation range has no effect unless using a scroll-timeline. + ScrollTimeline* scroll_timeline = DynamicTo<ScrollTimeline>(timeline_.Get()); + if (!scroll_timeline) { + return; + } + SetOutdated(); if (content_) { // Animation range affects intrinsic iteration duration, which in turn // affects iteration duration in normalized timing. content_->InvalidateNormalizedTiming(); + content_->Invalidate(); } if (start_time_) { UpdateStartTimeForViewTimeline(); } + Update(kTimingUpdateOnDemand); + + // Clamp current time to end time if finished. The |previous_current_time_| + // flag prevents current time from jumping when updating the finished state + // on an animation and not performing an explicit seek operation. + previous_current_time_ = absl::nullopt; UpdateFinishedState(UpdateType::kContinuous, NotificationType::kAsync); - SetCompositorPending(false); + SetCompositorPending(/*effect_changed=*/true); // Inform devtools of a potential change to the play state. NotifyProbe();
diff --git a/third_party/blink/renderer/core/animation/animation.h b/third_party/blink/renderer/core/animation/animation.h index e764e9ee..03af6ee 100644 --- a/third_party/blink/renderer/core/animation/animation.h +++ b/third_party/blink/renderer/core/animation/animation.h
@@ -233,6 +233,14 @@ OnRangeUpdate(); } } + virtual void SetRange(const absl::optional<TimelineOffset>& range_start, + const absl::optional<TimelineOffset>& range_end) { + if (range_start_ != range_start || range_end_ != range_end) { + range_start_ = range_start; + range_end_ = range_end; + OnRangeUpdate(); + } + } void OnRangeUpdate();
diff --git a/third_party/blink/renderer/core/animation/animation_effect.cc b/third_party/blink/renderer/core/animation/animation_effect.cc index 77d45b6c..b90b490 100644 --- a/third_party/blink/renderer/core/animation/animation_effect.cc +++ b/third_party/blink/renderer/core/animation/animation_effect.cc
@@ -65,6 +65,16 @@ InvalidateNormalizedTiming(); } +AnimationTimeDelta AnimationEffect::IntrinsicIterationDuration() const { + if (auto* animation = GetAnimation()) { + auto* timeline = animation->timeline(); + if (timeline) { + return timeline->CalculateIntrinsicIterationDuration(animation, timing_); + } + } + return AnimationTimeDelta(); +} + // Scales all timing values so that end_time == timeline_duration // https://drafts.csswg.org/web-animations-2/#time-based-animation-to-a-proportional-animation void AnimationEffect::EnsureNormalizedTiming() const {
diff --git a/third_party/blink/renderer/core/animation/animation_effect.h b/third_party/blink/renderer/core/animation/animation_effect.h index c538b15..950238a 100644 --- a/third_party/blink/renderer/core/animation/animation_effect.h +++ b/third_party/blink/renderer/core/animation/animation_effect.h
@@ -168,9 +168,7 @@ // In web-animations-1, auto is treated as "the value zero for the purpose of // timing model calculations and for the result of the duration member // returned from getComputedTiming()". - virtual AnimationTimeDelta IntrinsicIterationDuration() const { - return AnimationTimeDelta(); - } + virtual AnimationTimeDelta IntrinsicIterationDuration() const; virtual AnimationTimeDelta CalculateTimeToEffectChange( bool forwards,
diff --git a/third_party/blink/renderer/core/animation/animation_test_helpers.h b/third_party/blink/renderer/core/animation/animation_test_helpers.h index 1140b59..3f15044 100644 --- a/third_party/blink/renderer/core/animation/animation_test_helpers.h +++ b/third_party/blink/renderer/core/animation/animation_test_helpers.h
@@ -6,6 +6,7 @@ #define THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_ANIMATION_TEST_HELPERS_H_ #include "third_party/blink/renderer/bindings/core/v8/v8_typedefs.h" +#include "third_party/blink/renderer/core/animation/inert_effect.h" #include "third_party/blink/renderer/core/animation/interpolation.h" #include "third_party/blink/renderer/platform/wtf/text/string_view.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" @@ -48,6 +49,28 @@ // InvalidatableInterpolation. void EnsureInterpolatedValueCached(ActiveInterpolations*, Document&, Element*); +class TestAnimationProxy : public AnimationProxy { + public: + // AnimationProxy interface. + bool AtScrollTimelineBoundary() const override { return false; } + absl::optional<AnimationTimeDelta> TimelineDuration() const override { + return absl::nullopt; + } + AnimationTimeDelta IntrinsicIterationDuration() const override { + return AnimationTimeDelta(); + } + double PlaybackRate() const override { return playback_rate_; } + bool Paused() const override { return false; } + absl::optional<AnimationTimeDelta> InheritedTime() const override { + return AnimationTimeDelta(); + } + + void SetPlaybackRate(double rate) { playback_rate_ = rate; } + + private: + double playback_rate_ = 1; +}; + } // namespace animation_test_helpers } // namespace blink
diff --git a/third_party/blink/renderer/core/animation/animation_timeline.h b/third_party/blink/renderer/core/animation/animation_timeline.h index 42cdd0f8..579a988 100644 --- a/third_party/blink/renderer/core/animation/animation_timeline.h +++ b/third_party/blink/renderer/core/animation/animation_timeline.h
@@ -72,6 +72,14 @@ // Changing scroll-linked animation start_time initialization is under // consideration here: https://github.com/w3c/csswg-drafts/issues/2075. virtual absl::optional<base::TimeDelta> InitialStartTimeForAnimations() = 0; + + virtual AnimationTimeDelta CalculateIntrinsicIterationDuration( + const absl::optional<TimelineOffset>& rangeStart, + const absl::optional<TimelineOffset>& rangeEnd, + const Timing&) { + return AnimationTimeDelta(); + } + virtual AnimationTimeDelta CalculateIntrinsicIterationDuration( const Animation*, const Timing&) {
diff --git a/third_party/blink/renderer/core/animation/css/css_animation.cc b/third_party/blink/renderer/core/animation/css/css_animation.cc index 942a5d2..3d49efa 100644 --- a/third_party/blink/renderer/core/animation/css/css_animation.cc +++ b/third_party/blink/renderer/core/animation/css/css_animation.cc
@@ -70,6 +70,20 @@ ignore_css_range_end_ = true; } +void CSSAnimation::SetRange(const absl::optional<TimelineOffset>& range_start, + const absl::optional<TimelineOffset>& range_end) { + if (GetIgnoreCSSRangeStart() && GetIgnoreCSSRangeEnd()) { + return; + } + + const absl::optional<TimelineOffset>& adjusted_range_start = + GetIgnoreCSSRangeStart() ? GetRangeStartInternal() : range_start; + const absl::optional<TimelineOffset>& adjusted_range_end = + GetIgnoreCSSRangeEnd() ? GetRangeEndInternal() : range_end; + + Animation::SetRange(adjusted_range_start, adjusted_range_end); +} + void CSSAnimation::setStartTime(const V8CSSNumberish* start_time, ExceptionState& exception_state) { PlayStateTransitionScope scope(*this);
diff --git a/third_party/blink/renderer/core/animation/css/css_animation.h b/third_party/blink/renderer/core/animation/css/css_animation.h index 36ce2df..5954b71 100644 --- a/third_party/blink/renderer/core/animation/css/css_animation.h +++ b/third_party/blink/renderer/core/animation/css/css_animation.h
@@ -58,6 +58,12 @@ void setRangeEnd(const RangeBoundary* range_end, ExceptionState& exception_state) override; + // Conditionally updates both boundaries of the animation range. + // If the corresponding boundary has been explicitly set via WAAPI + // the new value will be ignored. + void SetRange(const absl::optional<TimelineOffset>& range_start, + const absl::optional<TimelineOffset>& range_end) override; + // When set, subsequent changes to animation-<property> no longer affect // <property>. // https://drafts.csswg.org/css-animations-2/#interaction-between-animation-play-state-and-web-animations-API
diff --git a/third_party/blink/renderer/core/animation/css/css_animations.cc b/third_party/blink/renderer/core/animation/css/css_animations.cc index b0efe43..a9d56e2 100644 --- a/third_party/blink/renderer/core/animation/css/css_animations.cc +++ b/third_party/blink/renderer/core/animation/css/css_animations.cc
@@ -97,6 +97,222 @@ namespace { +class CSSAnimationProxy : public AnimationProxy { + public: + CSSAnimationProxy(AnimationTimeline* timeline, + CSSAnimation* animation, + bool is_paused, + const absl::optional<TimelineOffset>& range_start, + const absl::optional<TimelineOffset>& range_end, + const Timing& timing); + + // AnimationProxy interface. + bool AtScrollTimelineBoundary() const override { + return at_scroll_timeline_boundary_; + } + absl::optional<AnimationTimeDelta> TimelineDuration() const override { + return timeline_duration_; + } + AnimationTimeDelta IntrinsicIterationDuration() const override { + return intrinsic_iteration_duration_; + } + double PlaybackRate() const override { return playback_rate_; } + bool Paused() const override { return is_paused_; } + absl::optional<AnimationTimeDelta> InheritedTime() const override { + return inherited_time_; + } + + private: + absl::optional<AnimationTimeDelta> CalculateInheritedTime( + AnimationTimeline* timeline, + CSSAnimation* animation, + const absl::optional<TimelineOffset>& range_start, + const absl::optional<TimelineOffset>& range_end, + const Timing& timing); + + double playback_rate_ = 1; + absl::optional<AnimationTimeDelta> inherited_time_; + AnimationTimeDelta intrinsic_iteration_duration_; + absl::optional<AnimationTimeDelta> timeline_duration_; + bool is_paused_; + bool at_scroll_timeline_boundary_ = false; +}; + +CSSAnimationProxy::CSSAnimationProxy( + AnimationTimeline* timeline, + CSSAnimation* animation, + bool is_paused, + const absl::optional<TimelineOffset>& range_start, + const absl::optional<TimelineOffset>& range_end, + const Timing& timing) + : is_paused_(is_paused) { + absl::optional<TimelineOffset> adjusted_range_start; + absl::optional<TimelineOffset> adjusted_range_end; + if (animation) { + playback_rate_ = animation->playbackRate(); + adjusted_range_start = animation->GetIgnoreCSSRangeStart() + ? animation->GetRangeStartInternal() + : range_start; + adjusted_range_end = animation->GetIgnoreCSSRangeEnd() + ? animation->GetRangeEndInternal() + : range_end; + } else { + adjusted_range_start = range_start; + adjusted_range_end = range_end; + } + + intrinsic_iteration_duration_ = + timeline ? timeline->CalculateIntrinsicIterationDuration( + adjusted_range_start, adjusted_range_end, timing) + : AnimationTimeDelta(); + + inherited_time_ = CalculateInheritedTime( + timeline, animation, adjusted_range_start, adjusted_range_end, timing); + + timeline_duration_ = timeline ? timeline->GetDuration() : absl::nullopt; + if (timeline && timeline->IsScrollTimeline() && timeline->CurrentTime()) { + AnimationTimeDelta timeline_time = timeline->CurrentTime().value(); + at_scroll_timeline_boundary_ = + timeline_time.is_zero() || + IsWithinAnimationTimeTolerance(timeline_time, + timeline_duration_.value()); + } +} + +absl::optional<AnimationTimeDelta> CSSAnimationProxy::CalculateInheritedTime( + AnimationTimeline* timeline, + CSSAnimation* animation, + const absl::optional<TimelineOffset>& range_start, + const absl::optional<TimelineOffset>& range_end, + const Timing& timing) { + absl::optional<AnimationTimeDelta> inherited_time; + + AnimationTimeline* previous_timeline = nullptr; + bool resets_current_time_on_resume = false; + + if (animation) { + // A cancelled CSS animation does not become active again due to an + // animation update. + if (!animation->UnlimitedCurrentTime() && !animation->StartTimeInternal()) { + return absl::nullopt; + } + + // In most cases, current time is preserved on an animation update. + inherited_time = animation->UnlimitedCurrentTime(); + previous_timeline = animation->timeline(); + resets_current_time_on_resume = animation->ResetsCurrentTimeOnResume(); + } + + bool range_changed = + !animation || (range_start != animation->GetRangeStartInternal() || + range_end != animation->GetRangeEndInternal()); + + if (timeline && timeline->IsScrollTimeline()) { + if (is_paused_ || ((timeline == previous_timeline) && + !resets_current_time_on_resume && !range_changed)) { + // Current time is unaffected by the update. + return inherited_time; + } + + // Running animation with an update that potentially affects the + // animation's start time. Need to compute a new value for + // inherited_time_. + double relative_offset; + if (timeline->IsViewTimeline()) { + // TODO(kevers): Support animation-range for a non-view scroll-timeline. + if (playback_rate_ >= 0) { + relative_offset = + range_start ? DynamicTo<ViewTimeline>(timeline)->ToFractionalOffset( + range_start.value()) + : 0; + } else { + relative_offset = + range_end ? DynamicTo<ViewTimeline>(timeline)->ToFractionalOffset( + range_end.value()) + : 1; + } + } else { + // A non-view scroll-timeline has its start time at 0 or end time. + // TODO(kevers): Update once non-view scroll-timeline support animation + // ranges. + relative_offset = playback_rate_ >= 0 ? 0 : 1; + } + if (timeline->CurrentTime()) { + AnimationTimeDelta pending_start_time = + timeline->GetDuration().value() * relative_offset; + return (timeline->CurrentTime().value() - pending_start_time) * + playback_rate_; + } + return absl::nullopt; + } + + if (previous_timeline && previous_timeline->IsScrollTimeline() && + previous_timeline->CurrentTime()) { + // Going from a scroll timeline to a document or null timeline. + // In this case, we preserve the current time. + double progress = previous_timeline->CurrentTime().value() / + previous_timeline->GetDuration().value(); + + AnimationTimeDelta end_time = std::max( + timing.start_delay.AsTimeValue() + + MultiplyZeroAlwaysGivesZero( + timing.iteration_duration.value_or(AnimationTimeDelta()), + timing.iteration_count) + + timing.end_delay.AsTimeValue(), + AnimationTimeDelta()); + + return progress * end_time; + } + + if (!timeline) { + // If changing from a monotonic-timeline to a null-timeline, current time + // may become null. + // TODO(https://github.com/w3c/csswg-drafts/issues/6412): Update once the + // issue is resolved. + if (previous_timeline && previous_timeline->IsMonotonicallyIncreasing() && + !is_paused_ && animation->StartTimeInternal() && + animation->CalculateAnimationPlayState() == Animation::kRunning) { + return absl::nullopt; + } + // A new animation with a null timeline will be stuck in the play or pause + // pending state. + if (!inherited_time && !animation) { + return AnimationTimeDelta(); + } + } + + // A timeline attached to a monotonic timeline that does not currently have a + // time will start in either the play or paused state. + if (timeline && timeline->IsMonotonicallyIncreasing() && !inherited_time) { + return AnimationTimeDelta(); + } + + return inherited_time; +} + +class CSSTransitionProxy : public AnimationProxy { + public: + explicit CSSTransitionProxy(absl::optional<AnimationTimeDelta> current_time) + : current_time_(current_time) {} + + // AnimationProxy interface. + bool AtScrollTimelineBoundary() const override { return false; } + absl::optional<AnimationTimeDelta> TimelineDuration() const override { + return absl::nullopt; + } + AnimationTimeDelta IntrinsicIterationDuration() const override { + return AnimationTimeDelta(); + } + double PlaybackRate() const override { return 1; } + bool Paused() const override { return false; } + absl::optional<AnimationTimeDelta> InheritedTime() const override { + return current_time_; + } + + private: + absl::optional<AnimationTimeDelta> current_time_; +}; + // A keyframe can have an offset as a fixed percent or as a // <timeline-range percent>. In the later case, we resolve as a fixed // percent, though this value can change as layout changes. Setting the @@ -1483,6 +1699,8 @@ // kUnset and kPending. NOTREACHED(); } + } else if (!animation->GetIgnoreCSSPlayState()) { + will_be_playing = !is_paused && play_state != Animation::kIdle; } else { will_be_playing = (play_state == Animation::kRunning) || (play_state == Animation::kFinished); @@ -1507,46 +1725,10 @@ is_paused != was_paused || logical_property_mapping_change || timeline != existing_animation->Timeline() || range_changed) { DCHECK(!is_animation_style_change); - absl::optional<AnimationTimeDelta> inherited_time; - absl::optional<AnimationTimeDelta> timeline_duration; - if (timeline) { - inherited_time = animation->UnlimitedCurrentTime(); - timeline_duration = timeline->GetDuration(); - - if (will_be_playing && - ((timeline != existing_animation->Timeline()) || - animation->ResetsCurrentTimeOnResume())) { - if (timeline->IsScrollTimeline()) { - inherited_time = timeline->CurrentTime(); - } else { - AnimationTimeline* previous_timeline = - existing_animation->Timeline(); - // Check to see if we are switching from a scroll timeline to a - // document timeline. - if (previous_timeline && - previous_timeline->IsScrollTimeline() && - previous_timeline->CurrentTime()) { - // We need to maintain current progress in the animation when - // switching from scroll timeline to document timeline. - double progress = previous_timeline->CurrentTime().value() / - previous_timeline->GetDuration().value(); - - AnimationTimeDelta end_time = std::max( - specified_timing.start_delay.AsTimeValue() + - MultiplyZeroAlwaysGivesZero( - specified_timing.iteration_duration.value_or( - AnimationTimeDelta()), - specified_timing.iteration_count) + - specified_timing.end_delay.AsTimeValue(), - AnimationTimeDelta()); - - inherited_time = progress * end_time; - } - } - } - } - + CSSAnimationProxy animation_proxy(timeline, animation, + !will_be_playing, range_start, + range_end, timing); update.UpdateAnimation( existing_animation_index, animation, *MakeGarbageCollected<InertEffect>( @@ -1554,8 +1736,7 @@ resolver, element, animating_element, writing_direction, parent_style, name, keyframe_timing_function.get(), composite, i, timeline), - timing, is_paused, inherited_time, timeline_duration, - animation->playbackRate()), + timing, animation_proxy), specified_timing, keyframes_rule, timeline, animation_data->PlayStateList(), range_start, range_end); if (toggle_pause_state) @@ -1565,16 +1746,11 @@ DCHECK(!is_animation_style_change); AnimationTimeline* timeline = ComputeTimeline( &element, style_timeline, update, /* existing_timeline */ nullptr); - absl::optional<AnimationTimeDelta> inherited_time = - AnimationTimeDelta(); - absl::optional<AnimationTimeDelta> timeline_duration; - if (timeline) { - timeline_duration = timeline->GetDuration(); - if (!timeline->IsMonotonicallyIncreasing()) { - inherited_time = timeline->CurrentTime(); - } - } + CSSAnimationProxy animation_proxy(timeline, /* animation */ nullptr, + is_paused, range_start, range_end, + timing); + update.StartAnimation( name, name_index, i, *MakeGarbageCollected<InertEffect>( @@ -1582,7 +1758,7 @@ writing_direction, parent_style, name, keyframe_timing_function.get(), composite, i, timeline), - timing, is_paused, inherited_time, timeline_duration, 1.0), + timing, animation_proxy), specified_timing, keyframes_rule, timeline, animation_data->PlayStateList(), range_start, range_end); } @@ -1884,17 +2060,7 @@ css_animation.setTimeline(entry.timeline); css_animation.ResetIgnoreCSSTimeline(); } - if (!css_animation.GetIgnoreCSSRangeStart() && - css_animation.GetRangeStartInternal() != entry.range_start) { - css_animation.SetRangeStartInternal(entry.range_start); - css_animation.ResetIgnoreCSSRangeStart(); - } - if (!css_animation.GetIgnoreCSSRangeEnd() && - css_animation.GetRangeEndInternal() != entry.range_end) { - css_animation.SetRangeEndInternal(entry.range_end); - css_animation.ResetIgnoreCSSRangeEnd(); - } - + css_animation.SetRange(entry.range_start, entry.range_end); running_animations_[entry.index]->Update(entry); entry.animation->Update(kTimingUpdateOnDemand); } @@ -1929,8 +2095,7 @@ if (inert_animation->Paused()) animation->pause(); animation->ResetIgnoreCSSPlayState(); - animation->SetRangeStartInternal(entry.range_start); - animation->SetRangeEndInternal(entry.range_end); + animation->SetRange(entry.range_start, entry.range_end); animation->ResetIgnoreCSSRangeStart(); animation->ResetIgnoreCSSRangeEnd(); animation->Update(kTimingUpdateOnDemand); @@ -2274,7 +2439,7 @@ property, state.before_change_style, &state.base_style, reversing_adjusted_start_value, reversing_shortening_factor, *MakeGarbageCollected<InertEffect>( - model, timing, false, AnimationTimeDelta(), absl::nullopt, 1.0)); + model, timing, CSSTransitionProxy(AnimationTimeDelta()))); DCHECK(!state.animating_element.GetElementAnimations() || !state.animating_element.GetElementAnimations() ->IsAnimationStyleChange()); @@ -2489,8 +2654,8 @@ continue; auto* inert_animation_for_sampling = MakeGarbageCollected<InertEffect>( - effect->Model(), effect->SpecifiedTiming(), false, current_time, - /* timeline_duration */ absl::nullopt, animation->playbackRate()); + effect->Model(), effect->SpecifiedTiming(), + CSSTransitionProxy(current_time)); HeapVector<Member<Interpolation>> sample; inert_animation_for_sampling->Sample(sample);
diff --git a/third_party/blink/renderer/core/animation/effect_stack_test.cc b/third_party/blink/renderer/core/animation/effect_stack_test.cc index 31fecce..0b2a7060 100644 --- a/third_party/blink/renderer/core/animation/effect_stack_test.cc +++ b/third_party/blink/renderer/core/animation/effect_stack_test.cc
@@ -76,7 +76,7 @@ Timing timing; timing.fill_mode = Timing::FillMode::BOTH; return MakeGarbageCollected<InertEffect>( - effect, timing, false, AnimationTimeDelta(), absl::nullopt, 1.0); + effect, timing, animation_test_helpers::TestAnimationProxy()); } KeyframeEffect* MakeKeyframeEffect(KeyframeEffectModelBase* effect,
diff --git a/third_party/blink/renderer/core/animation/inert_effect.cc b/third_party/blink/renderer/core/animation/inert_effect.cc index ee8bdfc..cb968ab8 100644 --- a/third_party/blink/renderer/core/animation/inert_effect.cc +++ b/third_party/blink/renderer/core/animation/inert_effect.cc
@@ -36,19 +36,18 @@ InertEffect::InertEffect(KeyframeEffectModelBase* model, const Timing& timing, - bool paused, - absl::optional<AnimationTimeDelta> inherited_time, - absl::optional<AnimationTimeDelta> timeline_duration, - double playback_rate) + const AnimationProxy& proxy) : AnimationEffect(timing), model_(model), - paused_(paused), - inherited_time_(inherited_time), - timeline_duration_(timeline_duration), - playback_rate_(playback_rate) {} + paused_(proxy.Paused()), + inherited_time_(proxy.InheritedTime()), + timeline_duration_(proxy.TimelineDuration()), + intrinsic_iteration_duration_(proxy.IntrinsicIterationDuration()), + playback_rate_(proxy.PlaybackRate()), + at_scroll_timeline_boundary_(proxy.AtScrollTimelineBoundary()) {} void InertEffect::Sample(HeapVector<Member<Interpolation>>& result) const { - UpdateInheritedTime(inherited_time_, /* at_scroll_timeline_boundary */ false, + UpdateInheritedTime(inherited_time_, at_scroll_timeline_boundary_, /* is_idle */ false, playback_rate_, kTimingUpdateOnDemand); if (!IsInEffect()) { @@ -78,6 +77,10 @@ return timeline_duration_; } +AnimationTimeDelta InertEffect::IntrinsicIterationDuration() const { + return intrinsic_iteration_duration_; +} + void InertEffect::Trace(Visitor* visitor) const { visitor->Trace(model_); AnimationEffect::Trace(visitor);
diff --git a/third_party/blink/renderer/core/animation/inert_effect.h b/third_party/blink/renderer/core/animation/inert_effect.h index 27efe66..d567baf 100644 --- a/third_party/blink/renderer/core/animation/inert_effect.h +++ b/third_party/blink/renderer/core/animation/inert_effect.h
@@ -37,17 +37,22 @@ namespace blink { +class AnimationProxy { + public: + virtual bool AtScrollTimelineBoundary() const = 0; + virtual absl::optional<AnimationTimeDelta> TimelineDuration() const = 0; + virtual AnimationTimeDelta IntrinsicIterationDuration() const = 0; + virtual double PlaybackRate() const = 0; + virtual bool Paused() const = 0; + virtual absl::optional<AnimationTimeDelta> InheritedTime() const = 0; +}; + // Lightweight subset of KeyframeEffect. // Used to transport data for deferred KeyframeEffect construction and one off // Interpolation sampling. class CORE_EXPORT InertEffect final : public AnimationEffect { public: - InertEffect(KeyframeEffectModelBase*, - const Timing&, - bool paused, - absl::optional<AnimationTimeDelta> inherited_time, - absl::optional<AnimationTimeDelta> timeline_duration, - double playback_rate); + InertEffect(KeyframeEffectModelBase*, const Timing&, const AnimationProxy&); void Sample(HeapVector<Member<Interpolation>>&) const; KeyframeEffectModelBase* Model() const { return model_.Get(); } @@ -66,6 +71,7 @@ absl::optional<AnimationTimeDelta> inherited_time, AnimationTimeDelta time_to_next_iteration) const override; absl::optional<AnimationTimeDelta> TimelineDuration() const override; + AnimationTimeDelta IntrinsicIterationDuration() const override; private: Member<KeyframeEffectModelBase> model_; @@ -73,7 +79,9 @@ absl::optional<AnimationTimeDelta> inherited_time_; absl::optional<TimelinePhase> inherited_phase_; absl::optional<AnimationTimeDelta> timeline_duration_; + AnimationTimeDelta intrinsic_iteration_duration_; double playback_rate_; + bool at_scroll_timeline_boundary_; }; template <>
diff --git a/third_party/blink/renderer/core/animation/inert_effect_test.cc b/third_party/blink/renderer/core/animation/inert_effect_test.cc index d8c158e..9c5f93c 100644 --- a/third_party/blink/renderer/core/animation/inert_effect_test.cc +++ b/third_party/blink/renderer/core/animation/inert_effect_test.cc
@@ -24,8 +24,7 @@ timing.iteration_duration = ANIMATION_TIME_DELTA_FROM_SECONDS(1000); auto* inert_effect = MakeGarbageCollected<InertEffect>( - opacity_model, timing, /* paused */ false, AnimationTimeDelta(), - /* timeline_duration */ absl::nullopt, /* playback_rate */ 1.0); + opacity_model, timing, animation_test_helpers::TestAnimationProxy()); HeapVector<Member<Interpolation>> interpolations; // Calling Sample ensures Timing is calculated. inert_effect->Sample(interpolations); @@ -39,8 +38,7 @@ timing.start_delay = Timing::Delay(ANIMATION_TIME_DELTA_FROM_SECONDS(500)); auto* inert_effect = MakeGarbageCollected<InertEffect>( - opacity_model, timing, /* paused */ false, AnimationTimeDelta(), - /* timeline_duration */ absl::nullopt, /* playback_rate */ 1.0); + opacity_model, timing, animation_test_helpers::TestAnimationProxy()); HeapVector<Member<Interpolation>> interpolations; // Calling Sample ensures Timing is calculated. inert_effect->Sample(interpolations); @@ -53,9 +51,12 @@ timing.iteration_duration = ANIMATION_TIME_DELTA_FROM_SECONDS(1000); timing.start_delay = Timing::Delay(ANIMATION_TIME_DELTA_FROM_SECONDS(500)); - auto* inert_effect = MakeGarbageCollected<InertEffect>( - opacity_model, timing, /* paused */ false, AnimationTimeDelta(), - /* timeline_duration */ absl::nullopt, /* playback_rate */ -1.0); + animation_test_helpers::TestAnimationProxy proxy; + proxy.SetPlaybackRate(-1); + + auto* inert_effect = + MakeGarbageCollected<InertEffect>(opacity_model, timing, proxy); + HeapVector<Member<Interpolation>> interpolations; // Calling Sample ensures Timing is calculated. inert_effect->Sample(interpolations); @@ -73,12 +74,10 @@ Timing timing; auto* opacity_effect = MakeGarbageCollected<InertEffect>( - opacity_model, timing, /* paused */ false, AnimationTimeDelta(), - /* timeline_duration */ absl::nullopt, /* playback_rate */ 1.0); + opacity_model, timing, animation_test_helpers::TestAnimationProxy()); auto* color_effect = MakeGarbageCollected<InertEffect>( - color_model, timing, /* paused */ false, AnimationTimeDelta(), - /* timeline_duration */ absl::nullopt, /* playback_rate */ 1.0); + color_model, timing, animation_test_helpers::TestAnimationProxy()); EXPECT_TRUE(opacity_effect->Affects(PropertyHandle(GetCSSPropertyOpacity()))); EXPECT_FALSE(opacity_effect->Affects(PropertyHandle(GetCSSPropertyColor())));
diff --git a/third_party/blink/renderer/core/animation/keyframe_effect.cc b/third_party/blink/renderer/core/animation/keyframe_effect.cc index 6e106b37..5559a5c 100644 --- a/third_party/blink/renderer/core/animation/keyframe_effect.cc +++ b/third_party/blink/renderer/core/animation/keyframe_effect.cc
@@ -40,6 +40,7 @@ #include "third_party/blink/renderer/core/animation/effect_input.h" #include "third_party/blink/renderer/core/animation/element_animations.h" #include "third_party/blink/renderer/core/animation/sampled_effect.h" +#include "third_party/blink/renderer/core/animation/timing_calculations.h" #include "third_party/blink/renderer/core/animation/timing_input.h" #include "third_party/blink/renderer/core/animation/view_timeline.h" #include "third_party/blink/renderer/core/css/parser/css_selector_parser.h" @@ -664,14 +665,16 @@ } void KeyframeEffect::ClearEffects() { - if (!sampled_effect_) + if (!sampled_effect_) { return; + } sampled_effect_->Clear(); sampled_effect_ = nullptr; if (GetAnimation()) GetAnimation()->RestartAnimationOnCompositor(); - if (!effect_target_->GetDocument().Lifecycle().InDetach()) + if (!effect_target_->GetDocument().Lifecycle().InDetach()) { effect_target_->SetNeedsAnimationStyleRecalc(); + } auto* svg_element = DynamicTo<SVGElement>(effect_target_.Get()); if (RuntimeEnabledFeatures::WebAnimationsSVGEnabled() && svg_element) svg_element->ClearWebAnimatedAttributes(); @@ -683,10 +686,11 @@ return; DCHECK(owner_); if (IsInEffect() && !owner_->EffectSuppressed() && - !owner_->ReplaceStateRemoved()) + !owner_->ReplaceStateRemoved()) { const_cast<KeyframeEffect*>(this)->ApplyEffects(); - else + } else { const_cast<KeyframeEffect*>(this)->ClearEffects(); + } } void KeyframeEffect::Attach(AnimationEffectOwner* owner) { @@ -719,16 +723,6 @@ ClearEffects(); } -AnimationTimeDelta KeyframeEffect::IntrinsicIterationDuration() const { - if (auto* animation = GetAnimation()) { - auto* timeline = animation->timeline(); - if (timeline) { - return timeline->CalculateIntrinsicIterationDuration(animation, timing_); - } - } - return AnimationTimeDelta(); -} - AnimationTimeDelta KeyframeEffect::CalculateTimeToEffectChange( bool forwards, absl::optional<AnimationTimeDelta> local_time, @@ -766,7 +760,8 @@ } return {}; case Timing::kPhaseAfter: - DCHECK_GE(local_time.value(), after_time); + DCHECK(GreaterThanOrEqualToWithinTimeTolerance(local_time.value(), + after_time)); if (forwards) { // If an animation has a positive-valued end delay, we need an // additional tick at the end time to ensure that the finished event is
diff --git a/third_party/blink/renderer/core/animation/keyframe_effect.h b/third_party/blink/renderer/core/animation/keyframe_effect.h index fbc526a..99411c93 100644 --- a/third_party/blink/renderer/core/animation/keyframe_effect.h +++ b/third_party/blink/renderer/core/animation/keyframe_effect.h
@@ -160,7 +160,6 @@ void DetachTarget(Animation*); void RefreshTarget(); void CountAnimatedProperties() const; - AnimationTimeDelta IntrinsicIterationDuration() const override; AnimationTimeDelta CalculateTimeToEffectChange( bool forwards, absl::optional<AnimationTimeDelta> inherited_time,
diff --git a/third_party/blink/renderer/core/animation/scroll_timeline.cc b/third_party/blink/renderer/core/animation/scroll_timeline.cc index 19bd395..b8aab215 100644 --- a/third_party/blink/renderer/core/animation/scroll_timeline.cc +++ b/third_party/blink/renderer/core/animation/scroll_timeline.cc
@@ -252,8 +252,7 @@ // Only run calculation for progress based scroll timelines if (duration) { - // if iteration_duration == "auto" and iterations > 0 - if (!timing.iteration_duration && timing.iteration_count > 0) { + if (timing.iteration_count > 0) { // duration represents 100% so we subtract percentage delays and divide it // by iteration count to calculate the iteration duration. double start_delay = timing.start_delay.relative_delay.value_or(0);
diff --git a/third_party/blink/renderer/core/animation/scroll_timeline.h b/third_party/blink/renderer/core/animation/scroll_timeline.h index 61ba18a8..407c1f39 100644 --- a/third_party/blink/renderer/core/animation/scroll_timeline.h +++ b/third_party/blink/renderer/core/animation/scroll_timeline.h
@@ -79,6 +79,16 @@ AnimationTimeDelta CalculateIntrinsicIterationDuration( const Animation*, const Timing&) override; + + // TODO(kevers): Support range start and end for scroll-timelines that are not + // view timelines. + AnimationTimeDelta CalculateIntrinsicIterationDuration( + const absl::optional<TimelineOffset>& rangeStart, + const absl::optional<TimelineOffset>& rangeEnd, + const Timing& timing) override { + return CalculateIntrinsicIterationDuration(nullptr, timing); + } + AnimationTimeDelta ZeroTime() override { return AnimationTimeDelta(); } void ServiceAnimations(TimingUpdateReason) override;
diff --git a/third_party/blink/renderer/core/animation/timeline_offset.cc b/third_party/blink/renderer/core/animation/timeline_offset.cc index df94924..fa30e8b 100644 --- a/third_party/blink/renderer/core/animation/timeline_offset.cc +++ b/third_party/blink/renderer/core/animation/timeline_offset.cc
@@ -108,11 +108,18 @@ // TODO(kevers): Keep track of style dependent lengths in order // to re-resolve on a style update. DCHECK(list.length()); - const auto& range_name = To<CSSIdentifierValue>(list.Item(0)); - Length offset = (list.length() == 2u) ? ResolveLength(element, &list.Item(1)) - : Length::Percent(default_percent); + NamedRange range_name = NamedRange::kNone; + Length offset = Length::Percent(default_percent); + if (list.Item(0).IsIdentifierValue()) { + range_name = To<CSSIdentifierValue>(list.Item(0)).ConvertTo<NamedRange>(); + if (list.length() == 2u) { + offset = ResolveLength(element, &list.Item(1)); + } + } else { + offset = ResolveLength(element, &list.Item(0)); + } - return TimelineOffset(range_name.ConvertTo<NamedRange>(), offset); + return TimelineOffset(range_name, offset); } /* static */
diff --git a/third_party/blink/renderer/core/animation/timing_calculations.h b/third_party/blink/renderer/core/animation/timing_calculations.h index 2235c33..d725923 100644 --- a/third_party/blink/renderer/core/animation/timing_calculations.h +++ b/third_party/blink/renderer/core/animation/timing_calculations.h
@@ -135,7 +135,8 @@ AnimationTimeDelta()); if (local_time.value() > active_after_boundary_time || (direction == Timing::AnimationDirection::kForwards && - local_time.value() == active_after_boundary_time && + IsWithinAnimationTimeTolerance(local_time.value(), + active_after_boundary_time) && !at_progress_timeline_boundary)) { return Timing::kPhaseAfter; }
diff --git a/third_party/blink/renderer/core/animation/view_timeline.cc b/third_party/blink/renderer/core/animation/view_timeline.cc index 34d4415..e285b639 100644 --- a/third_party/blink/renderer/core/animation/view_timeline.cc +++ b/third_party/blink/renderer/core/animation/view_timeline.cc
@@ -261,20 +261,23 @@ AnimationTimeDelta ViewTimeline::CalculateIntrinsicIterationDuration( const Animation* animation, const Timing& timing) { + return CalculateIntrinsicIterationDuration(animation->GetRangeStartInternal(), + animation->GetRangeEndInternal(), + timing); +} + +AnimationTimeDelta ViewTimeline::CalculateIntrinsicIterationDuration( + const absl::optional<TimelineOffset>& rangeStart, + const absl::optional<TimelineOffset>& rangeEnd, + const Timing& timing) { absl::optional<AnimationTimeDelta> duration = GetDuration(); // Only run calculation for progress based scroll timelines if (duration && timing.iteration_count > 0) { double active_interval = 1; - double start = - animation->GetRangeStartInternal() - ? ToFractionalOffset(animation->GetRangeStartInternal().value()) - : 0; - double end = - animation->GetRangeEndInternal() - ? ToFractionalOffset(animation->GetRangeEndInternal().value()) - : 1; + double start = rangeStart ? ToFractionalOffset(rangeStart.value()) : 0; + double end = rangeEnd ? ToFractionalOffset(rangeEnd.value()) : 1; active_interval -= start; active_interval -= (1 - end); @@ -361,8 +364,9 @@ start_offset_ = start_offset; end_offset_ = end_offset; - for (auto animation : GetAnimations()) + for (auto animation : GetAnimations()) { animation->InvalidateNormalizedTiming(); + } } return absl::make_optional<ScrollOffsets>(start_offset, end_offset);
diff --git a/third_party/blink/renderer/core/animation/view_timeline.h b/third_party/blink/renderer/core/animation/view_timeline.h index cf6a2c2..afd3fec 100644 --- a/third_party/blink/renderer/core/animation/view_timeline.h +++ b/third_party/blink/renderer/core/animation/view_timeline.h
@@ -44,6 +44,11 @@ const Animation*, const Timing&) override; + AnimationTimeDelta CalculateIntrinsicIterationDuration( + const absl::optional<TimelineOffset>& rangeStart, + const absl::optional<TimelineOffset>& rangeEnd, + const Timing&) override; + // IDL API implementation. Element* subject() const;
diff --git a/third_party/blink/renderer/core/css/properties/computed_style_utils.cc b/third_party/blink/renderer/core/css/properties/computed_style_utils.cc index 4c3909cc..c0164431 100644 --- a/third_party/blink/renderer/core/css/properties/computed_style_utils.cc +++ b/third_party/blink/renderer/core/css/properties/computed_style_utils.cc
@@ -2288,7 +2288,9 @@ return MakeGarbageCollected<CSSIdentifierValue>(CSSValueID::kNormal); } CSSValueList* list = CSSValueList::CreateSpaceSeparated(); - list->Append(*MakeGarbageCollected<CSSIdentifierValue>(offset->name)); + if (offset->name != TimelineOffset::NamedRange::kNone) { + list->Append(*MakeGarbageCollected<CSSIdentifierValue>(offset->name)); + } if (offset->offset != default_offset) { list->Append(*ComputedStyleUtils::ZoomAdjustedPixelValueForLength( offset->offset, style));
diff --git a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc index 3a6fa85..ff3e277 100644 --- a/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc +++ b/third_party/blink/renderer/core/css/properties/css_parsing_utils.cc
@@ -4354,16 +4354,17 @@ } CSSValueList* list = CSSValueList::CreateSpaceSeparated(); CSSValue* range_name = ConsumeTimelineRangeName(range); - if (!range_name) { - return nullptr; + if (range_name) { + list->Append(*range_name); } - list->Append(*range_name); CSSPrimitiveValue* percentage = ConsumeLengthOrPercent( range, context, CSSPrimitiveValue::ValueRange::kAll); if (percentage && - !(percentage->IsPercentage() && + !(range_name && percentage->IsPercentage() && percentage->GetValue<double>() == default_offset_percent)) { list->Append(*percentage); + } else if (!range_name) { + return nullptr; } return list; }
diff --git a/third_party/blink/renderer/core/css/properties/shorthands/shorthands_custom.cc b/third_party/blink/renderer/core/css/properties/shorthands/shorthands_custom.cc index e24b363b..e2133ea 100644 --- a/third_party/blink/renderer/core/css/properties/shorthands/shorthands_custom.cc +++ b/third_party/blink/renderer/core/css/properties/shorthands/shorthands_custom.cc
@@ -300,8 +300,10 @@ if (start_range && start_range->IsValueList() && !end_range) { CSSValueList* implied_end = CSSValueList::CreateSpaceSeparated(); const CSSValue& name = To<CSSValueList>(start_range)->First(); - implied_end->Append(name); - end_range = implied_end; + if (name.IsIdentifierValue()) { + implied_end->Append(name); + end_range = implied_end; + } } if (!start_range) {
diff --git a/third_party/blink/renderer/core/css/resolver/css_to_style_map.cc b/third_party/blink/renderer/core/css/resolver/css_to_style_map.cc index aba8cc46..b1c8289 100644 --- a/third_party/blink/renderer/core/css/resolver/css_to_style_map.cc +++ b/third_party/blink/renderer/core/css/resolver/css_to_style_map.cc
@@ -484,12 +484,19 @@ const auto& list = To<CSSValueList>(value); DCHECK_GE(list.length(), 1u); DCHECK_LE(list.length(), 2u); - const auto& range_name = To<CSSIdentifierValue>(list.Item(0)); - Length percent = (list.length() == 2u) ? StyleBuilderConverter::ConvertLength( - state, list.Item(1)) - : Length::Percent(default_percent); - return TimelineOffset(range_name.ConvertTo<TimelineOffset::NamedRange>(), - percent); + TimelineOffset::NamedRange range_name = TimelineOffset::NamedRange::kNone; + Length offset = Length::Percent(default_percent); + if (list.Item(0).IsIdentifierValue()) { + range_name = To<CSSIdentifierValue>(list.Item(0)) + .ConvertTo<TimelineOffset::NamedRange>(); + if (list.length() == 2u) { + offset = StyleBuilderConverter::ConvertLength(state, list.Item(1)); + } + } else { + offset = StyleBuilderConverter::ConvertLength(state, list.Item(0)); + } + + return TimelineOffset(range_name, offset); } } // namespace
diff --git a/third_party/blink/renderer/core/css/style_engine.cc b/third_party/blink/renderer/core/css/style_engine.cc index b390809..9ac49ac 100644 --- a/third_party/blink/renderer/core/css/style_engine.cc +++ b/third_party/blink/renderer/core/css/style_engine.cc
@@ -3133,7 +3133,7 @@ DCHECK(FlatTreeTraversal::ContainsIncludingPseudoElement( container, *layout_tree_rebuild_root_.GetRootNode())); } - RebuildLayoutTree(RebuildTransitionPseudoTree::kNo); + RebuildLayoutTree(&container); } if (container == GetDocument().documentElement()) { @@ -3210,17 +3210,27 @@ } } -void StyleEngine::RebuildLayoutTreeForTraversalRootAncestors(Element* parent) { +void StyleEngine::RebuildLayoutTreeForTraversalRootAncestors( + Element* parent, + Element* container_parent) { + bool is_container_ancestor = false; + for (auto* ancestor = parent; ancestor; ancestor = ancestor->GetReattachParent()) { - ancestor->RebuildLayoutTreeForTraversalRootAncestor(); + if (ancestor == container_parent) { + is_container_ancestor = true; + } + if (is_container_ancestor) { + ancestor->RebuildLayoutTreeForSizeContainerAncestor(); + } else { + ancestor->RebuildLayoutTreeForTraversalRootAncestor(); + } ancestor->ClearChildNeedsStyleRecalc(); ancestor->ClearChildNeedsReattachLayoutTree(); } } -void StyleEngine::RebuildLayoutTree( - RebuildTransitionPseudoTree rebuild_transition_pseudo_tree) { +void StyleEngine::RebuildLayoutTree(Element* size_container) { bool propagate_to_root = false; { DCHECK(GetDocument().documentElement()); @@ -3237,9 +3247,11 @@ root_element.RebuildLayoutTree(whitespace_attacher); } - RebuildLayoutTreeForTraversalRootAncestors( - root_element.GetReattachParent()); - if (rebuild_transition_pseudo_tree == RebuildTransitionPseudoTree::kYes) { + Element* container_parent = + size_container ? size_container->GetReattachParent() : nullptr; + RebuildLayoutTreeForTraversalRootAncestors(root_element.GetReattachParent(), + container_parent); + if (size_container == nullptr) { document_->documentElement()->RebuildTransitionPseudoLayoutTree( view_transition_names_); } @@ -3250,7 +3262,7 @@ if (propagate_to_root) { PropagateWritingModeAndDirectionToHTMLRoot(); if (NeedsLayoutTreeRebuild()) { - RebuildLayoutTree(rebuild_transition_pseudo_tree); + RebuildLayoutTree(size_container); } } } @@ -3269,7 +3281,8 @@ base::AutoReset<bool> rebuild_scope(&in_layout_tree_rebuild_, true); container.ReattachLayoutTreeChildren(base::PassKey<StyleEngine>()); - RebuildLayoutTreeForTraversalRootAncestors(&container); + RebuildLayoutTreeForTraversalRootAncestors(&container, + container.GetReattachParent()); layout_tree_rebuild_root_.Clear(); } @@ -3299,7 +3312,7 @@ if (NeedsLayoutTreeRebuild()) { TRACE_EVENT0("blink,blink_style", "Document::rebuildLayoutTree"); SCOPED_BLINK_UMA_HISTOGRAM_TIMER_HIGHRES("Style.RebuildLayoutTreeTime"); - RebuildLayoutTree(RebuildTransitionPseudoTree::kYes); + RebuildLayoutTree(); } } else { style_recalc_root_.Clear();
diff --git a/third_party/blink/renderer/core/css/style_engine.h b/third_party/blink/renderer/core/css/style_engine.h index 3730954..3b70f354 100644 --- a/third_party/blink/renderer/core/css/style_engine.h +++ b/third_party/blink/renderer/core/css/style_engine.h
@@ -585,9 +585,7 @@ void RecalcStyle(); void ClearEnsuredDescendantStyles(Element& element); - enum class RebuildTransitionPseudoTree { kYes, kNo }; - void RebuildLayoutTree( - RebuildTransitionPseudoTree rebuild_transition_pseudo_tree); + void RebuildLayoutTree(Element* size_container = nullptr); bool InRebuildLayoutTree() const { return in_layout_tree_rebuild_; } bool InDOMRemoval() const { return in_dom_removal_; } bool InContainerQueryStyleRecalc() const { @@ -799,7 +797,8 @@ // removal which caused a layout subtree to be detached. void MarkForLayoutTreeChangesAfterDetach(); - void RebuildLayoutTreeForTraversalRootAncestors(Element* parent); + void RebuildLayoutTreeForTraversalRootAncestors(Element* parent, + Element* container_parent); // Separate path for layout tree rebuild for re-attaching children of a // fieldset size query container, or a size query container which must use
diff --git a/third_party/blink/renderer/core/css/style_property_serializer.cc b/third_party/blink/renderer/core/css/style_property_serializer.cc index 1e8d43e..8786609b 100644 --- a/third_party/blink/renderer/core/css/style_property_serializer.cc +++ b/third_party/blink/renderer/core/css/style_property_serializer.cc
@@ -959,19 +959,21 @@ } DCHECK_GE(list->length(), 1u); DCHECK_LE(list->length(), 2u); - const auto& name = To<CSSIdentifierValue>(list->Item(0)); - double offset_percent = -1.0; + CSSValueID name = CSSValueID::kNormal; + double offset_percent = default_offset_percent; - if (list->length() == 1u) { - // <ident> - offset_percent = default_offset_percent; + if (list->Item(0).IsIdentifierValue()) { + name = To<CSSIdentifierValue>(list->Item(0)).GetValueID(); + if (list->length() == 2u) { + const auto& offset = To<CSSPrimitiveValue>(list->Item(1)); + offset_percent = offset.IsPercentage() ? offset.GetValue<double>() : -1.0; + } } else { - // <ident> <length-percentage> - const auto& offset = To<CSSPrimitiveValue>(list->Item(1)); + const auto& offset = To<CSSPrimitiveValue>(list->Item(0)); offset_percent = offset.IsPercentage() ? offset.GetValue<double>() : -1.0; } - return {name.GetValueID(), offset_percent}; + return {name, offset_percent}; } CSSValue* AnimationRangeShorthandValueItem(wtf_size_t index,
diff --git a/third_party/blink/renderer/core/dom/element.h b/third_party/blink/renderer/core/dom/element.h index 209e220..a8ae825b 100644 --- a/third_party/blink/renderer/core/dom/element.h +++ b/third_party/blink/renderer/core/dom/element.h
@@ -612,6 +612,9 @@ RebuildMarkerLayoutTree(whitespace_attacher); HandleSubtreeModifications(); } + void RebuildLayoutTreeForSizeContainerAncestor() { + RebuildFirstLetterLayoutTree(); + } bool NeedsRebuildChildLayoutTrees( const WhitespaceAttacher& whitespace_attacher) const { return ChildNeedsReattachLayoutTree() || NeedsWhitespaceChildrenUpdate() ||
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 38beb7c..a4df7dca 100644 --- a/third_party/blink/renderer/core/frame/local_frame_view.cc +++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -681,7 +681,11 @@ } } - To<LayoutBox>(root).LayoutSubtreeRoot(); + if (RuntimeEnabledFeatures::LayoutNewSubtreeRootEnabled()) { + To<LayoutBox>(root).LayoutSubtreeRoot(); + } else { + To<LayoutBox>(root).LayoutSubtreeRootOld(); + } return true; }
diff --git a/third_party/blink/renderer/core/frame/root_frame_viewport.cc b/third_party/blink/renderer/core/frame/root_frame_viewport.cc index 553d5b8..849e563 100644 --- a/third_party/blink/renderer/core/frame/root_frame_viewport.cc +++ b/third_party/blink/renderer/core/frame/root_frame_viewport.cc
@@ -233,7 +233,7 @@ return visible_scroll_snapport; const ComputedStyle* style = LayoutViewport().GetLayoutBox()->Style(); - LayoutRectOutsets padding( + visible_scroll_snapport.ContractEdges( MinimumValueForLength(style->ScrollPaddingTop(), visible_scroll_snapport.Height()), MinimumValueForLength(style->ScrollPaddingRight(), @@ -242,7 +242,6 @@ visible_scroll_snapport.Height()), MinimumValueForLength(style->ScrollPaddingLeft(), visible_scroll_snapport.Width())); - visible_scroll_snapport.Contract(padding); return visible_scroll_snapport; }
diff --git a/third_party/blink/renderer/core/html/portal/portal_contents.cc b/third_party/blink/renderer/core/html/portal/portal_contents.cc index 7c7c424..a45b0df0 100644 --- a/third_party/blink/renderer/core/html/portal/portal_contents.cc +++ b/third_party/blink/renderer/core/html/portal/portal_contents.cc
@@ -38,10 +38,17 @@ : document_(portal_element.GetDocument()), portal_element_(&portal_element), portal_token_(portal_token), - remote_portal_(std::move(remote_portal)), - portal_client_receiver_(this, std::move(portal_client_receiver)) { + remote_portal_(portal_element.GetDocument().GetExecutionContext()), + portal_client_receiver_( + this, + portal_element.GetDocument().GetExecutionContext()) { + remote_portal_.Bind(std::move(remote_portal), + document_->GetTaskRunner(TaskType::kInternalDefault)); remote_portal_.set_disconnect_handler(WTF::BindOnce( &PortalContents::DisconnectHandler, WrapWeakPersistent(this))); + portal_client_receiver_.Bind( + std::move(portal_client_receiver), + document_->GetTaskRunner(TaskType::kInternalDefault)); DocumentPortals::GetOrCreate(GetDocument()).RegisterPortalContents(this); } @@ -215,6 +222,8 @@ visitor->Trace(document_); visitor->Trace(portal_element_); visitor->Trace(activation_delegate_); + visitor->Trace(remote_portal_); + visitor->Trace(portal_client_receiver_); } } // namespace blink
diff --git a/third_party/blink/renderer/core/html/portal/portal_contents.h b/third_party/blink/renderer/core/html/portal/portal_contents.h index 8f5b6ff..294ba57 100644 --- a/third_party/blink/renderer/core/html/portal/portal_contents.h +++ b/third_party/blink/renderer/core/html/portal/portal_contents.h
@@ -6,8 +6,6 @@ #define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_PORTAL_PORTAL_CONTENTS_H_ #include "base/memory/scoped_refptr.h" -#include "mojo/public/cpp/bindings/associated_receiver.h" -#include "mojo/public/cpp/bindings/associated_remote.h" #include "mojo/public/cpp/bindings/pending_associated_receiver.h" #include "mojo/public/cpp/bindings/pending_associated_remote.h" #include "services/network/public/mojom/referrer_policy.mojom-shared.h" @@ -15,7 +13,8 @@ #include "third_party/blink/public/common/tokens/tokens.h" #include "third_party/blink/public/mojom/portal/portal.mojom-blink.h" #include "third_party/blink/renderer/platform/heap/garbage_collected.h" -#include "third_party/blink/renderer/platform/wtf/gc_plugin.h" +#include "third_party/blink/renderer/platform/mojo/heap_mojo_associated_receiver.h" +#include "third_party/blink/renderer/platform/mojo/heap_mojo_associated_remote.h" namespace blink { @@ -116,10 +115,9 @@ absl::optional<PortalToken> portal_token_; // Both of these will be reset once Destroy has been called. - GC_PLUGIN_IGNORE("https://crbug.com/1381979") - mojo::AssociatedRemote<mojom::blink::Portal> remote_portal_; - GC_PLUGIN_IGNORE("https://crbug.com/1381979") - mojo::AssociatedReceiver<mojom::blink::PortalClient> portal_client_receiver_; + HeapMojoAssociatedRemote<mojom::blink::Portal> remote_portal_; + HeapMojoAssociatedReceiver<mojom::blink::PortalClient, PortalContents> + portal_client_receiver_; }; } // namespace blink
diff --git a/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc b/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc index 8707bcd93..6ae7fb3 100644 --- a/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc +++ b/third_party/blink/renderer/core/intersection_observer/intersection_geometry.cc
@@ -63,10 +63,10 @@ // TODO(szager): Make sure the spec is clear that left/right margins are // resolved against width and not height. const PhysicalRect& rect = resolution_rect.value_or(expand_rect); - LayoutRectOutsets outsets(ComputeMargin(margin[0], rect.Height(), zoom), - ComputeMargin(margin[1], rect.Width(), zoom), - ComputeMargin(margin[2], rect.Height(), zoom), - ComputeMargin(margin[3], rect.Width(), zoom)); + NGPhysicalBoxStrut outsets(ComputeMargin(margin[0], rect.Height(), zoom), + ComputeMargin(margin[1], rect.Width(), zoom), + ComputeMargin(margin[2], rect.Height(), zoom), + ComputeMargin(margin[3], rect.Width(), zoom)); expand_rect.Expand(outsets); }
diff --git a/third_party/blink/renderer/core/layout/build.gni b/third_party/blink/renderer/core/layout/build.gni index df9a46c1..6a80341 100644 --- a/third_party/blink/renderer/core/layout/build.gni +++ b/third_party/blink/renderer/core/layout/build.gni
@@ -165,8 +165,6 @@ "layout_view_transition_content.h", "layout_word_break.cc", "layout_word_break.h", - "line/line_orientation_utils.cc", - "line/line_orientation_utils.h", "list_marker.cc", "list_marker.h", "map_coordinates_flags.h", @@ -720,7 +718,6 @@ "layout_theme_test.cc", "layout_video_test.cc", "layout_view_test.cc", - "line/line_orientation_utils_test.cc", "list_marker_test.cc", "map_coordinates_test.cc", "min_max_size_test.cc",
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc index 4e964f40..3a31e0e6 100644 --- a/third_party/blink/renderer/core/layout/layout_box.cc +++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -1002,6 +1002,33 @@ void LayoutBox::LayoutSubtreeRoot() { NOT_DESTROYED(); + const auto* previous_result = GetSingleCachedLayoutResult(); + DCHECK(previous_result); + auto space = previous_result->GetConstraintSpaceForCaching(); + DCHECK_EQ(space.GetWritingMode(), StyleRef().GetWritingMode()); + const NGLayoutResult* result = NGBlockNode(this).Layout(space); + GetDocument().GetFrame()->GetInputMethodController().DidLayoutSubtree(*this); + + // Even if we are a layout root, our baseline may have shifted. In this + // (rare) case, mark our containing-block for layout. + // + // NOTE: We could weaken the constraints in ObjectIsRelayoutBoundary, and use + // this technique to detect size-changes, etc if we wanted to expand this + // optimization. + const auto& previous_fragment = + To<NGPhysicalBoxFragment>(previous_result->PhysicalFragment()); + const auto& fragment = To<NGPhysicalBoxFragment>(result->PhysicalFragment()); + if (previous_fragment.FirstBaseline() != fragment.FirstBaseline() || + previous_fragment.LastBaseline() != fragment.LastBaseline()) { + if (auto* containing_block = ContainingBlock()) { + containing_block->SetNeedsLayout( + layout_invalidation_reason::kChildChanged, kMarkContainerChain); + } + } +} + +void LayoutBox::LayoutSubtreeRootOld() { + NOT_DESTROYED(); if (!IsLayoutNGObject() && GetSingleCachedLayoutResult()) { // 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
diff --git a/third_party/blink/renderer/core/layout/layout_box.h b/third_party/blink/renderer/core/layout/layout_box.h index bdb928d..8dad9d6e 100644 --- a/third_party/blink/renderer/core/layout/layout_box.h +++ b/third_party/blink/renderer/core/layout/layout_box.h
@@ -854,6 +854,7 @@ } void LayoutSubtreeRoot(); + void LayoutSubtreeRootOld(); void UpdateLayout() override; void Paint(const PaintInfo&) const override;
diff --git a/third_party/blink/renderer/core/layout/line/line_orientation_utils.cc b/third_party/blink/renderer/core/layout/line/line_orientation_utils.cc deleted file mode 100644 index 1a08eca..0000000 --- a/third_party/blink/renderer/core/layout/line/line_orientation_utils.cc +++ /dev/null
@@ -1,19 +0,0 @@ -// Copyright 2018 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "third_party/blink/renderer/core/layout/line/line_orientation_utils.h" - -namespace blink { - -LayoutRectOutsets LineOrientationLayoutRectOutsets( - const LayoutRectOutsets& outsets, - WritingMode writing_mode) { - if (!IsHorizontalWritingMode(writing_mode)) { - return LayoutRectOutsets(outsets.Left(), outsets.Bottom(), outsets.Right(), - outsets.Top()); - } - return outsets; -} - -} // namespace blink
diff --git a/third_party/blink/renderer/core/layout/line/line_orientation_utils.h b/third_party/blink/renderer/core/layout/line/line_orientation_utils.h deleted file mode 100644 index d6c4deb..0000000 --- a/third_party/blink/renderer/core/layout/line/line_orientation_utils.h +++ /dev/null
@@ -1,25 +0,0 @@ -// Copyright 2018 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LINE_LINE_ORIENTATION_UTILS_H_ -#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LINE_LINE_ORIENTATION_UTILS_H_ - -#include "third_party/blink/renderer/core/core_export.h" -#include "third_party/blink/renderer/platform/geometry/layout_rect_outsets.h" -#include "third_party/blink/renderer/platform/text/writing_mode.h" - -namespace blink { - -// Produces a new LayoutRectOutsets in line orientation -// (https://www.w3.org/TR/css-writing-modes-3/#line-orientation), whose -// - |top| is the logical 'over', -// - |right| is the logical 'line right', -// - |bottom| is the logical 'under', -// - |left| is the logical 'line left'. -CORE_EXPORT LayoutRectOutsets -LineOrientationLayoutRectOutsets(const LayoutRectOutsets&, WritingMode); - -} // namespace blink - -#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_LINE_LINE_ORIENTATION_UTILS_H_
diff --git a/third_party/blink/renderer/core/layout/line/line_orientation_utils_test.cc b/third_party/blink/renderer/core/layout/line/line_orientation_utils_test.cc deleted file mode 100644 index 4a43925..0000000 --- a/third_party/blink/renderer/core/layout/line/line_orientation_utils_test.cc +++ /dev/null
@@ -1,30 +0,0 @@ -// Copyright 2015 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "third_party/blink/renderer/core/layout/line/line_orientation_utils.h" - -#include "testing/gtest/include/gtest/gtest.h" - -namespace blink { -namespace { - -TEST(LineOrientationUtilsTest, LineOrientationLayoutRectOutsets_Horizontal) { - LayoutRectOutsets outsets(1, 2, 3, 4); - EXPECT_EQ( - LayoutRectOutsets(1, 2, 3, 4), - LineOrientationLayoutRectOutsets(outsets, WritingMode::kHorizontalTb)); -} - -TEST(LineOrientationUtilsTest, LineOrientationLayoutRectOutsets_Vertical) { - LayoutRectOutsets outsets(1, 2, 3, 4); - EXPECT_EQ( - LayoutRectOutsets(4, 3, 2, 1), - LineOrientationLayoutRectOutsets(outsets, WritingMode::kVerticalLr)); - EXPECT_EQ( - LayoutRectOutsets(4, 3, 2, 1), - LineOrientationLayoutRectOutsets(outsets, WritingMode::kVerticalRl)); -} - -} // namespace -} // namespace blink
diff --git a/third_party/blink/renderer/core/layout/ng/ng_ink_overflow.cc b/third_party/blink/renderer/core/layout/ng/ng_ink_overflow.cc index 03c95f1..fcf05e2 100644 --- a/third_party/blink/renderer/core/layout/ng/ng_ink_overflow.cc +++ b/third_party/blink/renderer/core/layout/ng/ng_ink_overflow.cc
@@ -7,7 +7,6 @@ #include "build/chromeos_buildflags.h" #include "third_party/blink/renderer/core/layout/geometry/logical_rect.h" #include "third_party/blink/renderer/core/layout/geometry/writing_mode_converter.h" -#include "third_party/blink/renderer/core/layout/line/line_orientation_utils.h" #include "third_party/blink/renderer/core/layout/ng/ng_text_decoration_offset.h" #include "third_party/blink/renderer/core/paint/text_decoration_info.h" #include "third_party/blink/renderer/core/style/computed_style.h" @@ -472,13 +471,15 @@ const WritingMode writing_mode = style.GetWritingMode(); if (ShadowList* text_shadow = style.TextShadow()) { - LayoutRectOutsets text_shadow_logical_outsets = - LineOrientationLayoutRectOutsets( - EnclosingLayoutRectOutsets( - text_shadow->RectOutsetsIncludingOriginal()), - writing_mode); - text_shadow_logical_outsets.ClampNegativeToZero(); - ink_overflow.Expand(text_shadow_logical_outsets); + NGLineBoxStrut text_shadow_logical_outsets = + NGPhysicalBoxStrut::Enclosing( + text_shadow->RectOutsetsIncludingOriginal()) + .ConvertToLineLogical({writing_mode, TextDirection::kLtr}); + ink_overflow.ExpandEdges( + text_shadow_logical_outsets.line_over.ClampNegativeToZero(), + text_shadow_logical_outsets.inline_end.ClampNegativeToZero(), + text_shadow_logical_outsets.line_under.ClampNegativeToZero(), + text_shadow_logical_outsets.inline_start.ClampNegativeToZero()); } PhysicalRect local_ink_overflow =
diff --git a/third_party/blink/renderer/core/page/scrolling/snap_coordinator.cc b/third_party/blink/renderer/core/page/scrolling/snap_coordinator.cc index e70ad0c..8f1cfd32 100644 --- a/third_party/blink/renderer/core/page/scrolling/snap_coordinator.cc +++ b/third_party/blink/renderer/core/page/scrolling/snap_coordinator.cc
@@ -12,6 +12,7 @@ #include "third_party/blink/renderer/core/layout/layout_block.h" #include "third_party/blink/renderer/core/layout/layout_box.h" #include "third_party/blink/renderer/core/layout/layout_view.h" +#include "third_party/blink/renderer/core/layout/ng/geometry/ng_box_strut.h" #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h" #include "third_party/blink/renderer/platform/geometry/length_functions.h" #include "third_party/blink/renderer/platform/instrumentation/use_counter.h" @@ -278,20 +279,19 @@ PhysicalRect container_rect(snap_container.PhysicalPaddingBoxRect()); const ComputedStyle* container_style = snap_container.Style(); - LayoutRectOutsets container_padding( - // The percentage of scroll-padding is different from that of normal - // padding, as scroll-padding resolves the percentage against - // corresponding dimension of the scrollport[1], while the normal - // padding resolves that against "width".[2,3] We use - // MinimumValueForLength here to ensure kAuto is resolved to - // LayoutUnit() which is the correct behavior for padding. - // [1] https://drafts.csswg.org/css-scroll-snap-1/#scroll-padding - // "relative to the corresponding dimension of the scroll - // container’s - // scrollport" - // [2] https://drafts.csswg.org/css-box/#padding-props - // [3] See for example LayoutBoxModelObject::ComputedCSSPadding where it - // uses |MinimumValueForLength| but against the "width". + // The percentage of scroll-padding is different from that of normal + // padding, as scroll-padding resolves the percentage against corresponding + // dimension of the scrollport[1], while the normal padding resolves that + // against "width".[2,3] We use MinimumValueForLength here to ensure kAuto + // is resolved to LayoutUnit() which is the correct behavior for padding. + // + // [1] https://drafts.csswg.org/css-scroll-snap-1/#scroll-padding + // "relative to the corresponding dimension of the scroll container’s + // scrollport" + // [2] https://drafts.csswg.org/css-box/#padding-props + // [3] See for example LayoutBoxModelObject::ComputedCSSPadding where it + // uses |MinimumValueForLength| but against the "width". + container_rect.ContractEdges( MinimumValueForLength(container_style->ScrollPaddingTop(), container_rect.Height()), MinimumValueForLength(container_style->ScrollPaddingRight(), @@ -300,7 +300,6 @@ container_rect.Height()), MinimumValueForLength(container_style->ScrollPaddingLeft(), container_rect.Width())); - container_rect.Contract(container_padding); snap_container_data.set_rect(gfx::RectF(container_rect)); if (snap_container_data.scroll_snap_type().strictness == @@ -409,7 +408,7 @@ area_rect, &snap_container, kTraverseDocumentBoundaries | kIgnoreScrollOffset); - LayoutRectOutsets area_margin( + NGPhysicalBoxStrut area_margin( area_style->ScrollMarginTop(), area_style->ScrollMarginRight(), area_style->ScrollMarginBottom(), area_style->ScrollMarginLeft()); area_rect.Expand(area_margin);
diff --git a/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.cc b/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.cc index fee4e3e..4fb44ee 100644 --- a/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.cc +++ b/third_party/blink/renderer/core/speculation_rules/document_speculation_rules.cc
@@ -507,8 +507,8 @@ rule->requires_anonymous_client_ip_when_cross_origin(), rule->target_browsing_context_name_hint().value_or( mojom::blink::SpeculationTargetHint::kNoHint), - eagerness, rule->no_vary_search_expected().Clone(), rule_set, - /*anchor=*/nullptr)); + eagerness, rule->no_vary_search_expected().Clone(), + rule->injection_world(), rule_set, /*anchor=*/nullptr)); } } }; @@ -653,7 +653,7 @@ rule->target_browsing_context_name_hint().value_or( mojom::blink::SpeculationTargetHint::kNoHint), eagerness, rule->no_vary_search_expected().Clone(), - rule_set, link); + rule->injection_world(), rule_set, link); link_candidates->push_back(std::move(candidate)); } };
diff --git a/third_party/blink/renderer/core/speculation_rules/speculation_candidate.cc b/third_party/blink/renderer/core/speculation_rules/speculation_candidate.cc index b0fe440..d5b6c66 100644 --- a/third_party/blink/renderer/core/speculation_rules/speculation_candidate.cc +++ b/third_party/blink/renderer/core/speculation_rules/speculation_candidate.cc
@@ -18,6 +18,7 @@ mojom::blink::SpeculationTargetHint target_hint, mojom::blink::SpeculationEagerness eagerness, network::mojom::blink::NoVarySearchPtr no_vary_search, + mojom::blink::SpeculationInjectionWorld injection_world, SpeculationRuleSet* rule_set, HTMLAnchorElement* anchor) : url_(url), @@ -28,6 +29,7 @@ target_hint_(target_hint), eagerness_(eagerness), no_vary_search_(std::move(no_vary_search)), + injection_world_(injection_world), rule_set_(rule_set), anchor_(anchor) { DCHECK(rule_set); @@ -45,7 +47,7 @@ mojom::blink::Referrer::New(KURL(referrer_.referrer), referrer_.referrer_policy), requires_anonymous_client_ip_when_cross_origin_, target_hint_, eagerness_, - no_vary_search_.Clone()); + no_vary_search_.Clone(), injection_world_); } } // namespace blink
diff --git a/third_party/blink/renderer/core/speculation_rules/speculation_candidate.h b/third_party/blink/renderer/core/speculation_rules/speculation_candidate.h index 6325241..8bf0a8c 100644 --- a/third_party/blink/renderer/core/speculation_rules/speculation_candidate.h +++ b/third_party/blink/renderer/core/speculation_rules/speculation_candidate.h
@@ -30,6 +30,7 @@ mojom::blink::SpeculationTargetHint target_hint, mojom::blink::SpeculationEagerness eagerness, network::mojom::blink::NoVarySearchPtr no_vary_search, + mojom::blink::SpeculationInjectionWorld injection_world, SpeculationRuleSet* rule_set, HTMLAnchorElement* anchor); virtual ~SpeculationCandidate() = default; @@ -57,6 +58,7 @@ const mojom::blink::SpeculationTargetHint target_hint_; const mojom::blink::SpeculationEagerness eagerness_; const network::mojom::blink::NoVarySearchPtr no_vary_search_; + const mojom::blink::SpeculationInjectionWorld injection_world_; const Member<SpeculationRuleSet> rule_set_; const Member<HTMLAnchorElement> anchor_; };
diff --git a/third_party/blink/renderer/core/speculation_rules/speculation_rule.cc b/third_party/blink/renderer/core/speculation_rules/speculation_rule.cc index fdf674e5..fb49ad9 100644 --- a/third_party/blink/renderer/core/speculation_rules/speculation_rule.cc +++ b/third_party/blink/renderer/core/speculation_rules/speculation_rule.cc
@@ -17,14 +17,16 @@ absl::optional<mojom::blink::SpeculationTargetHint> target_hint, absl::optional<network::mojom::ReferrerPolicy> referrer_policy, absl::optional<mojom::blink::SpeculationEagerness> eagerness, - network::mojom::blink::NoVarySearchPtr no_vary_search_expected) + network::mojom::blink::NoVarySearchPtr no_vary_search_expected, + mojom::blink::SpeculationInjectionWorld injection_world) : urls_(std::move(urls)), predicate_(predicate), requires_anonymous_client_ip_(requires_anonymous_client_ip), target_browsing_context_name_hint_(target_hint), referrer_policy_(referrer_policy), eagerness_(eagerness), - no_vary_search_expected_(std::move(no_vary_search_expected)) {} + no_vary_search_expected_(std::move(no_vary_search_expected)), + injection_world_(injection_world) {} SpeculationRule::~SpeculationRule() = default;
diff --git a/third_party/blink/renderer/core/speculation_rules/speculation_rule.h b/third_party/blink/renderer/core/speculation_rules/speculation_rule.h index 6da39da9..0baaaef5 100644 --- a/third_party/blink/renderer/core/speculation_rules/speculation_rule.h +++ b/third_party/blink/renderer/core/speculation_rules/speculation_rule.h
@@ -36,7 +36,8 @@ absl::optional<mojom::blink::SpeculationTargetHint> target_hint, absl::optional<network::mojom::ReferrerPolicy>, absl::optional<mojom::blink::SpeculationEagerness>, - network::mojom::blink::NoVarySearchPtr); + network::mojom::blink::NoVarySearchPtr, + mojom::blink::SpeculationInjectionWorld); ~SpeculationRule(); const Vector<KURL>& urls() const { return urls_; } @@ -58,6 +59,9 @@ const { return no_vary_search_expected_; } + mojom::blink::SpeculationInjectionWorld injection_world() const { + return injection_world_; + } void Trace(Visitor*) const; @@ -70,6 +74,8 @@ const absl::optional<network::mojom::ReferrerPolicy> referrer_policy_; absl::optional<mojom::blink::SpeculationEagerness> eagerness_; network::mojom::blink::NoVarySearchPtr no_vary_search_expected_; + mojom::blink::SpeculationInjectionWorld injection_world_ = + mojom::blink::SpeculationInjectionWorld::kNone; }; } // namespace blink
diff --git a/third_party/blink/renderer/core/speculation_rules/speculation_rule_set.cc b/third_party/blink/renderer/core/speculation_rules/speculation_rule_set.cc index c9670b0..5d0a4f31 100644 --- a/third_party/blink/renderer/core/speculation_rules/speculation_rule_set.cc +++ b/third_party/blink/renderer/core/speculation_rules/speculation_rule_set.cc
@@ -350,9 +350,17 @@ no_vary_search = std::move(no_vary_search_expected->get_no_vary_search()); } + const mojom::blink::SpeculationInjectionWorld world = + context->GetCurrentWorld() + ? context->GetCurrentWorld()->IsMainWorld() + ? mojom::blink::SpeculationInjectionWorld::kMain + : mojom::blink::SpeculationInjectionWorld::kIsolated + : mojom::blink::SpeculationInjectionWorld::kNone; + return MakeGarbageCollected<SpeculationRule>( std::move(urls), document_rule_predicate, requires_anonymous_client_ip, - target_hint, referrer_policy, eagerness, std::move(no_vary_search)); + target_hint, referrer_policy, eagerness, std::move(no_vary_search), + world); } } // namespace
diff --git a/third_party/blink/renderer/modules/broadcastchannel/broadcast_channel.cc b/third_party/blink/renderer/modules/broadcastchannel/broadcast_channel.cc index 09ccb79..ed26203 100644 --- a/third_party/blink/renderer/modules/broadcastchannel/broadcast_channel.cc +++ b/third_party/blink/renderer/modules/broadcastchannel/broadcast_channel.cc
@@ -157,6 +157,9 @@ void BroadcastChannel::Trace(Visitor* visitor) const { ExecutionContextLifecycleObserver::Trace(visitor); EventTargetWithInlineData::Trace(visitor); + visitor->Trace(receiver_); + visitor->Trace(remote_client_); + visitor->Trace(associated_remote_); } void BroadcastChannel::OnMessage(BlinkCloneableMessage message) { @@ -215,10 +218,13 @@ : ActiveScriptWrappable<BroadcastChannel>({}), ExecutionContextLifecycleObserver(execution_context), name_(name), + receiver_(this, execution_context), + remote_client_(execution_context), feature_handle_for_scheduler_( execution_context->GetScheduler()->RegisterFeature( SchedulingPolicy::Feature::kBroadcastChannel, - {SchedulingPolicy::DisableBackForwardCache()})) { + {SchedulingPolicy::DisableBackForwardCache()})), + associated_remote_(execution_context) { // Note: We cannot associate per-frame task runner here, but postTask // to it manually via EnqueueEvent, since the current expectation // is to receive messages even after close for which queued before @@ -242,19 +248,24 @@ // shared remote for all BroadcastChannel objects created on that thread to // ensure in-order delivery of messages to the appropriate *WorkerHost // object. + auto receiver_task_runner = + execution_context->GetTaskRunner(TaskType::kInternalDefault); + auto client_task_runner = + execution_context->GetTaskRunner(TaskType::kInternalDefault); if (receiver.is_valid() && remote.is_valid()) { - receiver_.Bind(std::move(receiver)); - remote_client_.Bind(std::move(remote)); + receiver_.Bind(std::move(receiver), receiver_task_runner); + remote_client_.Bind(std::move(remote), client_task_runner); } else if (auto* window = DynamicTo<LocalDOMWindow>(execution_context)) { LocalFrame* frame = window->GetFrame(); if (!frame) return; frame->GetRemoteNavigationAssociatedInterfaces()->GetInterface( - associated_remote_.BindNewEndpointAndPassReceiver()); + associated_remote_.BindNewEndpointAndPassReceiver( + execution_context->GetTaskRunner(TaskType::kInternalDefault))); associated_remote_->ConnectToChannel( - name_, receiver_.BindNewEndpointAndPassRemote(), - remote_client_.BindNewEndpointAndPassReceiver()); + name_, receiver_.BindNewEndpointAndPassRemote(receiver_task_runner), + remote_client_.BindNewEndpointAndPassReceiver(client_task_runner)); } else if (auto* worker_global_scope = DynamicTo<WorkerGlobalScope>(execution_context)) { if (worker_global_scope->IsClosing()) @@ -262,8 +273,9 @@ mojo::Remote<mojom::blink::BroadcastChannelProvider>& provider = GetWorkerThreadSpecificProvider(*worker_global_scope); - provider->ConnectToChannel(name_, receiver_.BindNewEndpointAndPassRemote(), - remote_client_.BindNewEndpointAndPassReceiver()); + provider->ConnectToChannel( + name_, receiver_.BindNewEndpointAndPassRemote(receiver_task_runner), + remote_client_.BindNewEndpointAndPassReceiver(client_task_runner)); } else { NOTREACHED(); }
diff --git a/third_party/blink/renderer/modules/broadcastchannel/broadcast_channel.h b/third_party/blink/renderer/modules/broadcastchannel/broadcast_channel.h index 17df1b3..76ef61af 100644 --- a/third_party/blink/renderer/modules/broadcastchannel/broadcast_channel.h +++ b/third_party/blink/renderer/modules/broadcastchannel/broadcast_channel.h
@@ -5,16 +5,15 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_BROADCASTCHANNEL_BROADCAST_CHANNEL_H_ #define THIRD_PARTY_BLINK_RENDERER_MODULES_BROADCASTCHANNEL_BROADCAST_CHANNEL_H_ -#include "mojo/public/cpp/bindings/associated_receiver.h" -#include "mojo/public/cpp/bindings/associated_remote.h" #include "third_party/blink/public/mojom/broadcastchannel/broadcast_channel.mojom-blink.h" #include "third_party/blink/renderer/bindings/core/v8/active_script_wrappable.h" #include "third_party/blink/renderer/core/dom/events/event_target.h" #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h" #include "third_party/blink/renderer/modules/modules_export.h" #include "third_party/blink/renderer/platform/heap/prefinalizer.h" +#include "third_party/blink/renderer/platform/mojo/heap_mojo_associated_receiver.h" +#include "third_party/blink/renderer/platform/mojo/heap_mojo_associated_remote.h" #include "third_party/blink/renderer/platform/scheduler/public/frame_or_worker_scheduler.h" -#include "third_party/blink/renderer/platform/wtf/gc_plugin.h" namespace blink { @@ -102,11 +101,10 @@ // BroadcastChannelClient receiver for messages sent from the browser to // this channel and BroadcastChannelClient remote for messages sent from // this channel to the browser. - GC_PLUGIN_IGNORE("https://crbug.com/1381979") - mojo::AssociatedReceiver<mojom::blink::BroadcastChannelClient> receiver_{ - this}; - GC_PLUGIN_IGNORE("https://crbug.com/1381979") - mojo::AssociatedRemote<mojom::blink::BroadcastChannelClient> remote_client_; + HeapMojoAssociatedReceiver<mojom::blink::BroadcastChannelClient, + BroadcastChannel> + receiver_; + HeapMojoAssociatedRemote<mojom::blink::BroadcastChannelClient> remote_client_; // Notifies the scheduler that a broadcast channel is active. FrameOrWorkerScheduler::SchedulingAffectingFeatureHandle @@ -117,8 +115,7 @@ // ConnectToChannel messages (with ordering preserved) to the // RenderFrameHostImpl associated with this frame. When a BroadcastChannel is // instantiated from a worker execution context, this member is not used. - GC_PLUGIN_IGNORE("https://crbug.com/1381979") - mojo::AssociatedRemote<mojom::blink::BroadcastChannelProvider> + HeapMojoAssociatedRemote<mojom::blink::BroadcastChannelProvider> associated_remote_; };
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc index e3e9203..73e2c9c 100644 --- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc +++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.cc
@@ -113,6 +113,7 @@ #include "third_party/blink/renderer/modules/peerconnection/rtc_void_request_impl.h" #include "third_party/blink/renderer/modules/peerconnection/rtc_void_request_promise_impl.h" #include "third_party/blink/renderer/modules/peerconnection/web_rtc_stats_report_callback_resolver.h" +#include "third_party/blink/renderer/platform/bindings/exception_code.h" #include "third_party/blink/renderer/platform/bindings/exception_state.h" #include "third_party/blink/renderer/platform/bindings/script_state.h" #include "third_party/blink/renderer/platform/bindings/v8_throw_exception.h" @@ -140,6 +141,10 @@ namespace blink { +BASE_FEATURE(kWebRtcLegacyGetStatsThrows, + "WebRtcLegacyGetStatsThrows", + base::FEATURE_DISABLED_BY_DEFAULT); + namespace { const char kSignalingStateClosedMessage[] = @@ -1659,7 +1664,7 @@ V8MediaStreamTrack::ToImplWithTypeCheck(isolate, legacy_selector.V8Value()); return LegacyCallbackBasedGetStats(script_state, success_callback, - selector_or_null); + selector_or_null, exception_state); } // Custom binding for spec-compliant // "getStats(optional MediaStreamTrack? selector)". null is a valid selector @@ -1681,7 +1686,8 @@ ScriptPromise RTCPeerConnection::LegacyCallbackBasedGetStats( ScriptState* script_state, V8RTCStatsCallback* success_callback, - MediaStreamTrack* selector) { + MediaStreamTrack* selector, + ExceptionState& exception_state) { ExecutionContext* context = ExecutionContext::From(script_state); auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state); ScriptPromise promise = resolver->Promise(); @@ -1694,12 +1700,20 @@ UseCounter::Count(context, WebFeature::kRTCPeerConnectionLegacyGetStatsTrial); } else { - // The deprecation trial is NOT enabled: show a deprecation warning. - // TODO(https://crbug.com/822696): In M114 add a base::Feature to control - // the throwing of an exception here. The plan is to throw in Canary/Beta in - // M114 and to throw in Stable in M117. + // The deprecation trial is NOT enabled: show a deprecation warning and + // maybe throw an exception. Deprecation::CountDeprecation( context, WebFeature::kRTCPeerConnectionGetStatsLegacyNonCompliant); + // The plan from the Intent to Deprecate is: + // - M114: Throw an exception in Canary/Beta. + // - M117: Throw in Stable. + // Which channel to throw on is controlled via the base::Feature. + if (base::FeatureList::IsEnabled(kWebRtcLegacyGetStatsThrows)) { + exception_state.ThrowDOMException( + DOMExceptionCode::kNotSupportedError, + "The callback-based getStats() method is no longer supported."); + return ScriptPromise(); + } } auto* stats_request = MakeGarbageCollected<RTCStatsRequestImpl>( GetExecutionContext(), this, success_callback, selector);
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h index 8f58dd1..dde910e 100644 --- a/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h +++ b/third_party/blink/renderer/modules/peerconnection/rtc_peer_connection.h
@@ -213,7 +213,8 @@ ScriptPromise LegacyCallbackBasedGetStats( ScriptState*, V8RTCStatsCallback* success_callback, - MediaStreamTrack* selector); + MediaStreamTrack* selector, + ExceptionState&); ScriptPromise PromiseBasedGetStats(ScriptState*, MediaStreamTrack* selector, ExceptionState&);
diff --git a/third_party/blink/renderer/modules/smart_card/smart_card_resource_manager.cc b/third_party/blink/renderer/modules/smart_card/smart_card_resource_manager.cc index 857d3f4..1c1c988 100644 --- a/third_party/blink/renderer/modules/smart_card/smart_card_resource_manager.cc +++ b/third_party/blink/renderer/modules/smart_card/smart_card_resource_manager.cc
@@ -57,7 +57,8 @@ SmartCardResourceManager::SmartCardResourceManager(NavigatorBase& navigator) : Supplement<NavigatorBase>(navigator), ExecutionContextLifecycleObserver(navigator.GetExecutionContext()), - service_(navigator.GetExecutionContext()) {} + service_(navigator.GetExecutionContext()), + receiver_(this, navigator.GetExecutionContext()) {} void SmartCardResourceManager::ContextDestroyed() { CloseServiceConnection(); @@ -121,6 +122,7 @@ void SmartCardResourceManager::Trace(Visitor* visitor) const { visitor->Trace(service_); + visitor->Trace(receiver_); visitor->Trace(get_readers_promises_); visitor->Trace(watch_for_readers_promises_); visitor->Trace(reader_cache_); @@ -237,7 +239,7 @@ WrapWeakPersistent(this))); DCHECK(!receiver_.is_bound()); service_->RegisterClient( - receiver_.BindNewEndpointAndPassRemote(), + receiver_.BindNewEndpointAndPassRemote(task_runner), WTF::BindOnce(&SmartCardResourceManager::OnServiceClientRegistered, WrapWeakPersistent(this))); }
diff --git a/third_party/blink/renderer/modules/smart_card/smart_card_resource_manager.h b/third_party/blink/renderer/modules/smart_card/smart_card_resource_manager.h index dcceebb9..34caffa 100644 --- a/third_party/blink/renderer/modules/smart_card/smart_card_resource_manager.h +++ b/third_party/blink/renderer/modules/smart_card/smart_card_resource_manager.h
@@ -5,7 +5,6 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_SMART_CARD_SMART_CARD_RESOURCE_MANAGER_H_ #define THIRD_PARTY_BLINK_RENDERER_MODULES_SMART_CARD_SMART_CARD_RESOURCE_MANAGER_H_ -#include "mojo/public/cpp/bindings/associated_receiver.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "third_party/blink/public/mojom/smart_card/smart_card.mojom-blink.h" #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h" @@ -15,6 +14,7 @@ #include "third_party/blink/renderer/platform/bindings/script_wrappable.h" #include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h" #include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_set.h" +#include "third_party/blink/renderer/platform/mojo/heap_mojo_associated_receiver.h" #include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h" #include "third_party/blink/renderer/platform/supplementable.h" #include "third_party/blink/renderer/platform/wtf/gc_plugin.h" @@ -79,9 +79,9 @@ SmartCardReaderPresenceObserver* GetOrCreatePresenceObserver(); HeapMojoRemote<mojom::blink::SmartCardService> service_; - GC_PLUGIN_IGNORE("https://crbug.com/1381979") - mojo::AssociatedReceiver<mojom::blink::SmartCardServiceClient> receiver_{ - this}; + HeapMojoAssociatedReceiver<mojom::blink::SmartCardServiceClient, + SmartCardResourceManager> + receiver_; HeapHashSet<Member<ScriptPromiseResolver>> get_readers_promises_; HeapHashSet<Member<ScriptPromiseResolver>> watch_for_readers_promises_; bool tracking_started_ = false;
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5 index edd83fac..3fb2f1db 100644 --- a/third_party/blink/renderer/platform/runtime_enabled_features.json5 +++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -2042,6 +2042,10 @@ status: "stable", }, { + name: "LayoutNewSubtreeRoot", + status: "stable", + }, + { name: "LayoutNewSVGForeignObjectEntry", status: "stable", },
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 fe2da01..12ed57ee 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
@@ -601,26 +601,28 @@ event->EventPointer()->SetTimeStamp(base::TimeTicks::Now()); } + // TODO(b/224960731): Fix tests and add + // `DCHECK(!arrived_in_browser_main_timestamp.is_null())`. + // We expect that `arrived_in_browser_main_timestamp` is always + // found, but there are a lot of tests where this component is not set. + // Currently EventMetrics knows how to handle null timestamp, so we + // don't process it here. + const base::TimeTicks arrived_in_browser_main_timestamp = + event->Event() + .GetEventLatencyMetadata() + .arrived_in_browser_main_timestamp; std::unique_ptr<cc::EventMetrics> metrics; if (event->Event().IsGestureScroll()) { const auto& gesture_event = static_cast<const WebGestureEvent&>(event->Event()); const bool is_inertial = gesture_event.InertialPhase() == WebGestureEvent::InertialPhaseState::kMomentum; - - // TODO(b/224960731): It is not recommended to use LatencyInfo. So we need - // to create a separate field with "arrived_in_browser_main" timestamp in - // WebInputEvent and use it here. - base::TimeTicks arrived_in_browser_main_timestamp; - event->latency_info().FindLatency( - ui::LatencyComponentType::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, - &(arrived_in_browser_main_timestamp)); - // TODO(b/224960731): Fix tests and add - // `DCHECK(!arrived_in_browser_main_timestamp.is_null())`. - // We expect that `INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT` is always - // found, but there are a lot of tests where this component is not set. - // Currently EventMetrics knows how to handle null timestamp, so we - // don't process it here. + //'scrolls_blocking_touch_dispatched_to_renderer' can be null. It is set + // by the Browser only if the corresponding TouchMove was blocking. + base::TimeTicks blocking_touch_dispatched_to_renderer_timestamp = + event->Event() + .GetEventLatencyMetadata() + .scrolls_blocking_touch_dispatched_to_renderer; if (gesture_event.GetType() == WebInputEvent::Type::kGestureScrollUpdate) { metrics = cc::ScrollUpdateEventMetrics::Create( @@ -632,13 +634,15 @@ gesture_event.data.scroll_update.delta_y, event->Event().TimeStamp(), arrived_in_browser_main_timestamp, base::IdType64<class ui::LatencyInfo>( - event->latency_info().trace_id())); + event->latency_info().trace_id()), + blocking_touch_dispatched_to_renderer_timestamp); has_seen_first_gesture_scroll_update_after_begin_ = true; } else { metrics = cc::ScrollEventMetrics::Create( gesture_event.GetTypeAsUiEventType(), gesture_event.GetScrollInputType(), is_inertial, - event->Event().TimeStamp(), arrived_in_browser_main_timestamp); + event->Event().TimeStamp(), arrived_in_browser_main_timestamp, + blocking_touch_dispatched_to_renderer_timestamp); has_seen_first_gesture_scroll_update_after_begin_ = false; } } else if (WebInputEvent::IsPinchGestureEventType(event->Event().GetType())) { @@ -649,7 +653,8 @@ gesture_event.GetScrollInputType(), event->Event().TimeStamp()); } else { metrics = cc::EventMetrics::Create(event->Event().GetTypeAsUiEventType(), - event->Event().TimeStamp()); + event->Event().TimeStamp(), + arrived_in_browser_main_timestamp); } if (uses_input_handler_) {
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index 67761ccd..0f49496 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -1836,15 +1836,10 @@ crbug.com/498539 http/tests/devtools/tracing/timeline-misc/timeline-bound-function.js [ Failure Pass ] -crbug.com/498539 crbug.com/794869 crbug.com/798548 crbug.com/946716 http/tests/devtools/elements/styles-4/styles-update-from-js.js [ Crash Failure Pass Timeout ] - crbug.com/889952 fast/selectors/selection-window-inactive.html [ Failure Pass ] crbug.com/1107923 inspector-protocol/debugger/wasm-streaming-url.js [ Failure Pass Timeout ] -# Script let/const redeclaration errors -crbug.com/1042162 http/tests/inspector-protocol/console/console-let-const-with-api.js [ Failure Pass ] - # Will be re-enabled and rebaselined once we remove the '--enable-file-cookies' flag. crbug.com/470482 fast/cookies/local-file-can-set-cookies.html [ Crash Failure Pass Timeout ]
diff --git a/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-range-end-computed.html b/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-range-end-computed.html index 914d496..4c96ee8 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-range-end-computed.html +++ b/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-range-end-computed.html
@@ -12,6 +12,9 @@ test_computed_value("animation-range-end", "COVER 0%", "cover 0%"); test_computed_value("animation-range-end", "COVER 100%", "cover"); test_computed_value("animation-range-end", "cover 120%"); +test_computed_value("animation-range-end", "0", "0px"); +test_computed_value("animation-range-end", "120%"); +test_computed_value("animation-range-end", "120px"); test_computed_value("animation-range-end", "cover 42%"); test_computed_value("animation-range-end", "cover -42%"); test_computed_value("animation-range-end", "contain 42%");
diff --git a/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-range-end-invalid.html b/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-range-end-invalid.html index ec28da2..459cdfd0 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-range-end-invalid.html +++ b/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-range-end-invalid.html
@@ -5,11 +5,8 @@ <script src="/css/support/parsing-testcommon.js"></script> <script> test_invalid_value("animation-range-end", "infinite"); -test_invalid_value("animation-range-end", "0"); test_invalid_value("animation-range-end", "1s 2s"); test_invalid_value("animation-range-end", "1s / 2s"); -test_invalid_value("animation-range-end", "100px"); -test_invalid_value("animation-range-end", "100%"); test_invalid_value("animation-range-end", "peek 50%"); test_invalid_value("animation-range-end", "50% contain");
diff --git a/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-range-end-valid.html b/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-range-end-valid.html index 4a248f3..aeeb2ee5 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-range-end-valid.html +++ b/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-range-end-valid.html
@@ -10,6 +10,9 @@ test_valid_value("animation-range-end", "cover 100%", "cover"); test_valid_value("animation-range-end", "cover 120%"); test_valid_value("animation-range-end", "cover 42%"); +test_valid_value("animation-range-end", "0", "0px"); +test_valid_value("animation-range-end", "120%"); +test_valid_value("animation-range-end", "120px"); test_valid_value("animation-range-end", "cover -42%"); test_valid_value("animation-range-end", "contain 42%"); test_valid_value("animation-range-end", "exit 42%");
diff --git a/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-range-shorthand.html b/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-range-shorthand.html index e5f6e866..8acf0b1c 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-range-shorthand.html +++ b/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-range-shorthand.html
@@ -40,6 +40,11 @@ test_valid_value("animation-range", "entry 50% exit 50%"); test_valid_value("animation-range", "cover 50% entry 50%, contain 50% exit 50%"); +test_valid_value("animation-range", "50% exit 50%"); +test_valid_value("animation-range", "normal 100px"); +test_valid_value("animation-range", "100px"); +test_valid_value("animation-range", "100px normal", "100px"); +test_valid_value("animation-range", "10% normal", "10%"); test_computed_value("animation-range", "normal"); test_computed_value("animation-range", "normal normal", "normal"); @@ -75,6 +80,11 @@ "cover 50% entry 50%, contain 50% exit 50%"); test_computed_value("animation-range", "entry 10em exit 20em", "entry 100px exit 200px"); +test_computed_value("animation-range", "10em exit 20em", "100px exit 200px"); +test_computed_value("animation-range", "normal 100px"); +test_computed_value("animation-range", "100px"); +test_computed_value("animation-range", "100px normal", "100px"); +test_computed_value("animation-range", "10% normal", "10%"); test_invalid_value("animation-range", "entry 50% 0s", "entry 50%"); test_invalid_value("animation-range", "0s entry 50%"); @@ -157,4 +167,12 @@ 'animation-range-start': 'exit calc(10% + 50px)', 'animation-range-end': 'exit', }); +test_shorthand_value('animation-range', '100px', { + 'animation-range-start': '100px', + 'animation-range-end': 'normal', +}); +test_shorthand_value('animation-range', '10%', { + 'animation-range-start': '10%', + 'animation-range-end': 'normal', +}); </script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-range-start-computed.html b/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-range-start-computed.html index 9750fb50..044aea2 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-range-start-computed.html +++ b/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-range-start-computed.html
@@ -13,6 +13,9 @@ test_computed_value("animation-range-start", "COVER 100%", "cover 100%"); test_computed_value("animation-range-start", "cover 120%"); test_computed_value("animation-range-start", "cover 42%"); +test_computed_value("animation-range-start", "0", "0px"); +test_computed_value("animation-range-start", "120%"); +test_computed_value("animation-range-start", "120px"); test_computed_value("animation-range-start", "cover -42%"); test_computed_value("animation-range-start", "contain 42%"); test_computed_value("animation-range-start", "exit 42%");
diff --git a/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-range-start-valid.html b/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-range-start-valid.html index d70a371..309f4cc 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-range-start-valid.html +++ b/third_party/blink/web_tests/external/wpt/css/css-animations/parsing/animation-range-start-valid.html
@@ -10,6 +10,9 @@ test_valid_value("animation-range-start", "cover 100%"); test_valid_value("animation-range-start", "cover 120%"); test_valid_value("animation-range-start", "cover 42%"); +test_valid_value("animation-range-start", "0", "0px"); +test_valid_value("animation-range-start", "120%"); +test_valid_value("animation-range-start", "120px"); test_valid_value("animation-range-start", "cover -42%"); test_valid_value("animation-range-start", "contain 42%"); test_valid_value("animation-range-start", "exit 42%");
diff --git a/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/crashtests/chrome-bug-1429955-crash.html b/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/crashtests/chrome-bug-1429955-crash.html new file mode 100644 index 0000000..bdf4002 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-contain/container-queries/crashtests/chrome-bug-1429955-crash.html
@@ -0,0 +1,5 @@ +<!DOCTYPE html> +<link rel="help" href="https://crbug.com/1429955.html"> +<div id="mc" style="display:list-item; width:0; columns:2; container-type:size;"> + <div id="abs" style="position:absolute; container-type:inline-size;">line</div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-timeline-ignored.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-timeline-ignored.tentative.html index 32cb89c..54a6257f 100644 --- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-timeline-ignored.tentative.html +++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-timeline-ignored.tentative.html
@@ -135,11 +135,11 @@ await waitForNextFrame(); assert_equals(getComputedStyle(element).width, '120px'); element.getAnimations()[0].timeline = null; - assert_equals(getComputedStyle(element).width, '0px'); + assert_equals(getComputedStyle(element).width, '120px'); // Changing the animation-timeline property should have no effect. element.style = 'animation-timeline:timeline2'; - assert_equals(getComputedStyle(element).width, '0px'); + assert_equals(getComputedStyle(element).width, '120px'); }, 'animation-timeline ignored after setting timeline with JS (null)'); </script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-timeline-named-scroll-progress-timeline.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-timeline-named-scroll-progress-timeline.tentative.html index 30461723..ed2c32d 100644 --- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-timeline-named-scroll-progress-timeline.tentative.html +++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-timeline-named-scroll-progress-timeline.tentative.html
@@ -7,6 +7,7 @@ <script src="/resources/testharnessreport.js"></script> <script src="/web-animations/testcommon.js"></script> <script src="support/testcommon.js"></script> +<script src="/scroll-animations/scroll-timelines/testcommon.js"></script> <style> @keyframes anim { from { translate: 50px; } @@ -318,17 +319,24 @@ await waitForCSSScrollTimelineStyle(); assert_equals(getComputedStyle(target).translate, '100px'); + const anim = target.getAnimations()[0]; + assert_percents_equal(anim.startTime, 0); + assert_percents_equal(anim.currentTime, 50); // This effectively removes the CSS-created ScrollTimeline on this element, // thus invoking "setting the timeline of an animation" [1] with a null- - // timeline on affected elements. This in turn causes the current time to - // become unresolved [2], ultimately resulting in no effect value. - // - // [1] https://drafts.csswg.org/web-animations-1/#setting-the-timeline - // [2] https://drafts.csswg.org/web-animations-1/#the-current-time-of-an-animation + // timeline on affected elements. This in turn runs the procedure to set the + // current time to previous progress * end time. Ultimately, this sets the + // hold time of the animation. + + // [1] https://www.w3.org/TR/web-animations-2/#setting-the-timeline + // [2] https://www.w3.org/TR/web-animations-2/ + // #setting-the-current-time-of-an-animation scroller.remove(); await waitForNextFrame(); - assert_equals(getComputedStyle(target).translate, 'none'); + assert_equals(getComputedStyle(target).translate, '100px'); + assert_equals(anim.startTime, null); + assert_times_equal(anim.currentTime, 5000); }, 'scroll-timeline-name on removed element affects subsequent siblings'); promise_test(async t => { @@ -371,13 +379,19 @@ await waitForCSSScrollTimelineStyle(); assert_equals(getComputedStyle(target).translate, '100px'); + const anim = target.getAnimations()[0]; + assert_percents_equal(anim.startTime, 0); + assert_percents_equal(anim.currentTime, 50); // See comment in the test "scroll-timeline-name on removed element ..." for // an explantation of this result. (Setting display:none is similar to // removing the element). scroller.style.display = 'none'; await waitForNextFrame(); - assert_equals(getComputedStyle(target).translate, 'none'); + assert_equals(getComputedStyle(target).translate, '100px'); + assert_equals(anim.startTime, null); + assert_times_equal(anim.currentTime, 5000); + }, 'scroll-timeline-name on element becoming display:none affects subsequent siblings'); promise_test(async t => { @@ -461,13 +475,16 @@ assert_true(!!anim, 'Failed to create animation'); assert_true(!!anim.timeline, 'Failed to create timeline'); assert_equals(getComputedStyle(target).translate, '100px'); + assert_percents_equal(anim.startTime, 0); + assert_percents_equal(anim.currentTime, 50); scroller.style.scrollTimelineName = 'timeline-B'; await waitForNextFrame(); assert_equals(anim.timeline, null, 'Failed to remove timeline'); - assert_equals(getComputedStyle(target).translate, 'none'); - + assert_equals(getComputedStyle(target).translate, '100px'); + assert_equals(anim.startTime, null); + assert_times_equal(anim.currentTime, 5000); }, 'Change in scroll-timeline-name to no longer match animation timeline updates animation.'); promise_test(async t => {
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-update-ref.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-update-ref.html new file mode 100644 index 0000000..7e375a1d --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/animation-update-ref.html
@@ -0,0 +1,55 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<head> +<meta charset="utf-8"> +<meta name="viewport" content="width=device-width, initial-scale=1"> +<title>Reference file for various tests that update an animation with a scroll timeline</title> +<script src="/web-animations/testcommon.js"></script> +</head> +<style type="text/css"> + #scroller { + border: 1px solid black; + overflow: hidden; + width: 300px; + height: 200px; + } + #target { + margin-bottom: 800px; + margin-top: 800px; + margin-left: 10px; + margin-right: 10px; + width: 100px; + height: 100px; + z-index: -1; + background-color: green; + } +</style> +<body> + <div id="scroller"> + <div id="target"></div> + </div> +</body> +<script type="text/javascript"> + document.documentElement.addEventListener('TestRendered', async () => { + runTest(); + }, { once: true }); + + async function runTest() { + // Defaults to exit 60% if using a view timeline with subject = target. + const DEFAULT_SCROLL_POS = 860; + await waitForCompositorReady(); + + const urlParams = new URLSearchParams(window.location.search); + target.style.transform = + `translateX(${urlParams.get('translate') || "0px"}`; + + scroller.scrollTop = urlParams.get('scroll') || DEFAULT_SCROLL_POS; + await waitForNextFrame(); + await waitForNextFrame(); + + // Make sure change to animation range was properly picked up. + document.documentElement.classList.remove("reftest-wait"); + } +</script> +</body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-dynamic.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-dynamic.tentative-expected.txt deleted file mode 100644 index 5714521..0000000 --- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-dynamic.tentative-expected.txt +++ /dev/null
@@ -1,19 +0,0 @@ -This is a testharness.js-based test. -PASS Switching between document and scroll timelines [immediate] -PASS Switching between document and scroll timelines [scroll] -PASS Switching pending animation from document to scroll timelines [immediate] -PASS Switching pending animation from document to scroll timelines [scroll] -PASS Changing computed value of animation-timeline changes effective timeline [immediate] -PASS Changing computed value of animation-timeline changes effective timeline [scroll] -FAIL Changing to/from animation-timeline:none [immediate] assert_equals: expected "120px" but got "0px" -FAIL Changing to/from animation-timeline:none [scroll] assert_equals: expected "120px" but got "0px" -PASS Changing scroll-timeline on preceding elements affects target element [immediate] -PASS Changing scroll-timeline on preceding elements affects target element [scroll] -PASS Reverse animation direction [immediate] -PASS Reverse animation direction [scroll] -PASS Switching timelines while paused [immediate] -PASS Switching timelines while paused [scroll] -PASS Switching timelines and pausing at the same time [immediate] -PASS Switching timelines and pausing at the same time [scroll] -Harness: the test ran to completion. -
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-dynamic.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-dynamic.tentative.html index b0880a7c..0d951e7 100644 --- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-dynamic.tentative.html +++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-dynamic.tentative.html
@@ -159,6 +159,10 @@ // DocumentTimeline applies by default. await assert_width(element, '100px'); + // Wait for the animation to be ready so that we a start time and no hold + // time. + await element.getAnimations()[0].ready; + // DocumentTimeline -> none element.style.animationTimeline = 'none'; await assert_width(element, '0px');
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-update-reversed-animation.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-update-reversed-animation.html new file mode 100644 index 0000000..93ad6916 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/scroll-timeline-update-reversed-animation.html
@@ -0,0 +1,69 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<head> +<meta charset="utf-8"> +<meta name="viewport" content="width=device-width, initial-scale=1"> +<title>Attach a scroll timeline to a reversed animation refTest</title> +<link rel="help" src="https://www.w3.org/TR/scroll-animations-1/#scroll-timeline-name"> +<link rel="match" href="./animation-update-ref.html?translate=55px&scroll=825"> +<script src="/web-animations/testcommon.js"></script> +</head> +<style type="text/css"> + @keyframes anim { + from { transform: translateX(100px) } + to { transform: translateX(0px) } + } + #scroller { + border: 1px solid black; + overflow: hidden; + width: 300px; + height: 200px; + scroll-timeline: timeline; + } + #target { + margin-bottom: 800px; + margin-top: 800px; + margin-left: 10px; + margin-right: 10px; + width: 100px; + height: 100px; + z-index: -1; + background-color: green; + animation: anim 10s linear paused; + } + #target.update { + animation-play-state: running; + animation-timeline: timeline; + animation-duration: auto; + } +</style> +<body> + <div id="scroller"> + <div id="target"></div> + </div> +</body> +<script type="text/javascript"> + document.documentElement.addEventListener('TestRendered', async () => { + runTest(); + }, { once: true }); + + async function runTest() { + await waitForCompositorReady(); + + const anim = target.getAnimations()[0]; + anim.playbackRate = -1; + await anim.ready; + + // Scroll to 55% of maximum scroll while paused. + scroller.scrollTop = 825; + await waitForNextFrame(); + + target.classList.add('update'); + await waitForNextFrame(); + + // Make sure change to animation range was properly picked up. + document.documentElement.classList.remove("reftest-wait"); + } +</script> +</body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-dynamic.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-dynamic.html index 74da8850..76a30ad 100644 --- a/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-dynamic.html +++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-dynamic.html
@@ -178,6 +178,10 @@ await scrollTop(scroller, 50); assert_equals(getComputedStyle(target).zIndex, '25'); timeline.style.display = 'none'; - assert_equals(getComputedStyle(target).zIndex, '-1'); + // Animation is held at previous current time. + assert_equals(getComputedStyle(target).zIndex, '25'); + const anim = target.getAnimations()[0]; + assert_equals(anim.startTime, null); + assert_times_equal(anim.currentTime, 250); }, 'Element with view-timeline becoming display:none'); </script>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-range-update-reversed-animation.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-range-update-reversed-animation.html new file mode 100644 index 0000000..c719916 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-range-update-reversed-animation.html
@@ -0,0 +1,69 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<head> +<meta charset="utf-8"> +<meta name="viewport" content="width=device-width, initial-scale=1"> +<title>Update timeline range on reversed animation refTest</title> +<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#named-timeline-range"> +<link rel="match" href="./animation-update-ref.html?translate=60px"> +<script src="/web-animations/testcommon.js"></script> +</head> +<style type="text/css"> + @keyframes anim { + from { transform: translateX(100px) } + to { transform: translateX(0px) } + } + #scroller { + border: 1px solid black; + overflow: hidden; + width: 300px; + height: 200px; + } + #target { + margin-bottom: 800px; + margin-top: 800px; + margin-left: 10px; + margin-right: 10px; + width: 100px; + height: 100px; + z-index: -1; + background-color: green; + animation: anim auto linear; + animation-timeline: timeline; + view-timeline: timeline; + } + #target.exit-range { + animation-range-start: exit 0%; + animation-range-end: exit 100%; + } +</style> +<body> + <div id="scroller"> + <div id="target"></div> + </div> +</body> +<script type="text/javascript"> + document.documentElement.addEventListener('TestRendered', async () => { + runTest(); + }, { once: true }); + + async function runTest() { + await waitForCompositorReady(); + + const anim = target.getAnimations()[0]; + anim.playbackRate = -1; + + // Scroll to exit 60%. + scroller.scrollTop = 860; + await waitForNextFrame(); + + // Update the animation range. + target.classList.add('exit-range'); + await waitForNextFrame(); + + // Make sure change to animation range was properly picked up. + document.documentElement.classList.remove("reftest-wait"); + } +</script> +</body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-range-update.html b/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-range-update.html new file mode 100644 index 0000000..e8e761d --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/scroll-animations/css/view-timeline-range-update.html
@@ -0,0 +1,68 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<head> +<meta charset="utf-8"> +<meta name="viewport" content="width=device-width, initial-scale=1"> +<title>Update timeline range refTest</title> +<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#named-timeline-range"> +<link rel="match" href="./animation-update-ref.html?translate=40px"> +<script src="/web-animations/testcommon.js"></script> +</head> +<style type="text/css"> + @keyframes anim { + from { transform: translateX(100px) } + to { transform: translateX(0px) } + } + #scroller { + border: 1px solid black; + overflow: hidden; + width: 300px; + height: 200px; + } + #target { + margin-bottom: 800px; + margin-top: 800px; + margin-left: 10px; + margin-right: 10px; + width: 100px; + height: 100px; + z-index: -1; + background-color: green; + animation: anim auto linear; + animation-timeline: timeline; + view-timeline: timeline; + } + #target.exit-range { + animation-range-start: exit 0%; + animation-range-end: exit 100%; + } +</style> +<body> + <div id="scroller"> + <div id="target"></div> + </div> +</body> +<script type="text/javascript"> + document.documentElement.addEventListener('TestRendered', async () => { + runTest(); + }, { once: true }); + + async function runTest() { + await waitForCompositorReady(); + + const anim = target.getAnimations()[0]; + + // Scroll to exit 60%. + scroller.scrollTop = 860; + await waitForNextFrame(); + + // Update the animation range. + target.classList.add('exit-range'); + await waitForNextFrame(); + + // Make sure change to animation range was properly picked up. + document.documentElement.classList.remove("reftest-wait"); + } +</script> +</body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/setting-timeline.tentative.html b/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/setting-timeline.tentative.html index 69b40cb..34d9af2 100644 --- a/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/setting-timeline.tentative.html +++ b/third_party/blink/web_tests/external/wpt/scroll-animations/scroll-timelines/setting-timeline.tentative.html
@@ -255,12 +255,14 @@ await animation.ready; await updateScrollPosition(scrollTimeline, 100); - assert_equals(animation.playState, 'running'); + const progress = animation.currentTime.value / 100; + const duration = animation.effect.getTiming().duration; animation.timeline = null; - assert_equals(animation.playState, 'running'); + const expectedCurrentTime = progress * duration; + assert_times_equal(animation.currentTime, expectedCurrentTime); }, 'Transitioning from a scroll timeline to a null timeline on a running ' + - 'animation preserves the play state'); + 'animation preserves current progress.'); promise_test(async t => { const keyframeEfect = new KeyframeEffect(createDiv(t),
diff --git a/third_party/blink/web_tests/external/wpt/scroll-animations/view-timelines/change-animation-range-updates-play-state.html b/third_party/blink/web_tests/external/wpt/scroll-animations/view-timelines/change-animation-range-updates-play-state.html index ecc80aea..53330d32 100644 --- a/third_party/blink/web_tests/external/wpt/scroll-animations/view-timelines/change-animation-range-updates-play-state.html +++ b/third_party/blink/web_tests/external/wpt/scroll-animations/view-timelines/change-animation-range-updates-play-state.html
@@ -55,16 +55,21 @@ anim.rangeStart = 'contain 0%'; // 700px anim.rangeEnd = 'contain 100%'; // 800px assert_equals(anim.playState, 'running'); + assert_percents_equal(anim.currentTime, 100/6); // Animation in the after phase and switches to the finished state. anim.rangeStart = 'entry 0%'; // 600px anim.rangeEnd = 'entry 100%'; // 700px assert_equals(anim.playState, 'finished'); + // Clamp to effect end when finished. + assert_percents_equal(anim.currentTime, 100/3); // Animation in the before phase and switches back to the running state. anim.rangeStart = 'exit 0%'; // 800px anim.rangeEnd = 'exit 100%'; // 900px assert_equals(anim.playState, 'running'); + assert_percents_equal(anim.currentTime, -100/6); + }, 'Changing the animation range updates the play state'); }
diff --git a/third_party/blink/web_tests/http/tests/devtools/elements/styles-4/styles-update-from-js-expected.txt b/third_party/blink/web_tests/http/tests/devtools/elements/styles-4/styles-update-from-js-expected.txt index 006af98..c3f86af 100644 --- a/third_party/blink/web_tests/http/tests/devtools/elements/styles-4/styles-update-from-js-expected.txt +++ b/third_party/blink/web_tests/http/tests/devtools/elements/styles-4/styles-update-from-js-expected.txt
@@ -9,18 +9,18 @@ element.style { () color: #daC0DE; border: 1px solid black; - border-top-color: black; - border-top-style: solid; border-top-width: 1px; - border-right-color: black; - border-right-style: solid; border-right-width: 1px; - border-bottom-color: black; - border-bottom-style: solid; border-bottom-width: 1px; - border-left-color: black; - border-left-style: solid; border-left-width: 1px; + border-top-style: solid; + border-right-style: solid; + border-bottom-style: solid; + border-left-style: solid; + border-top-color: black; + border-right-color: black; + border-bottom-color: black; + border-left-color: black; border-image-source: initial; border-image-slice: initial; border-image-width: initial; @@ -49,18 +49,18 @@ element.style { () color: rgb(192, 255, 238); border: 3px dashed green; - border-top-color: green; - border-top-style: dashed; border-top-width: 3px; - border-right-color: green; - border-right-style: dashed; border-right-width: 3px; - border-bottom-color: green; - border-bottom-style: dashed; border-bottom-width: 3px; - border-left-color: green; - border-left-style: dashed; border-left-width: 3px; + border-top-style: dashed; + border-right-style: dashed; + border-bottom-style: dashed; + border-left-style: dashed; + border-top-color: green; + border-right-color: green; + border-bottom-color: green; + border-left-color: green; border-image-source: initial; border-image-slice: initial; border-image-width: initial; @@ -87,7 +87,7 @@ ======== Inherited from div#container.red ======== [expanded] -Style Attribute { () +style attribute { () color: rgb(192, 255, 238); @@ -106,7 +106,7 @@ ======== Inherited from div#container.red ======== [expanded] -Style Attribute { () +style attribute { () color: rgb(192, 255, 238);
diff --git a/third_party/blink/web_tests/http/tests/devtools/elements/styles-4/styles-update-from-js.js b/third_party/blink/web_tests/http/tests/devtools/elements/styles-4/styles-update-from-js.js index a760ceb..fd352a4 100644 --- a/third_party/blink/web_tests/http/tests/devtools/elements/styles-4/styles-update-from-js.js +++ b/third_party/blink/web_tests/http/tests/devtools/elements/styles-4/styles-update-from-js.js
@@ -49,52 +49,43 @@ } `); - TestRunner.runTestSuite([ - function testInit(next) { - ElementsTestRunner.selectNodeAndWaitForStyles('container', next); + TestRunner.runAsyncTestSuite([ + async function testInit(next) { + await ElementsTestRunner.selectNodeAndWaitForStylesPromise('container'); }, - function testSetStyleAttribute(next) { - waitAndDumpAttributeAndStyles(next); - TestRunner.evaluateInPage('modifyStyleAttribute()'); + async function testSetStyleAttribute() { + await TestRunner.evaluateInPage('modifyStyleAttribute()'); + await waitAndDumpAttributeAndStyles(); }, - function testSetStyleCSSText(next) { - waitAndDumpAttributeAndStyles(next); - TestRunner.evaluateInPage('modifyCSSText()'); + async function testSetStyleCSSText() { + await TestRunner.evaluateInPage('modifyCSSText()'); + await waitAndDumpAttributeAndStyles(); }, - function testSetViaParsedAttributes(next) { - waitAndDumpAttributeAndStyles(next); - TestRunner.evaluateInPage('modifyParsedAttributes()'); + async function testSetViaParsedAttributes() { + await TestRunner.evaluateInPage('modifyParsedAttributes()'); + await waitAndDumpAttributeAndStyles(); }, - function testSetViaAncestorClass(next) { - ElementsTestRunner.selectNodeAndWaitForStyles('child', callback); - - function callback() { - waitAndDumpAttributeAndStyles(next, 'child'); - TestRunner.evaluateInPage('modifyContainerClass()'); - } + async function testSetViaAncestorClass() { + await ElementsTestRunner.selectNodeAndWaitForStylesPromise('child'); + await TestRunner.evaluateInPage('modifyContainerClass()'); + await waitAndDumpAttributeAndStyles('child'); }, - function testSetViaSiblingAttr(next) { - ElementsTestRunner.selectNodeAndWaitForStyles('childSibling', callback); - - function callback() { - waitAndDumpAttributeAndStyles(next, 'childSibling'); - TestRunner.evaluateInPage('modifyChildAttr()'); - } + async function testSetViaSiblingAttr() { + await ElementsTestRunner.selectNodeAndWaitForStylesPromise('childSibling'); + await TestRunner.evaluateInPage('modifyChildAttr()'); + await waitAndDumpAttributeAndStyles('childSibling'); } ]); - function waitAndDumpAttributeAndStyles(next, id) { + async function waitAndDumpAttributeAndStyles(id) { id = id || 'container'; - async function callback() { - await dumpAttributeAndStyles(id); - next(); - } - ElementsTestRunner.waitForStyles(id, callback); + await new Promise(resolve => ElementsTestRunner.waitForStyles(id, resolve)); + await dumpAttributeAndStyles(id); } async function dumpAttributeAndStyles(id) {
diff --git a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-layout/timeline-layout-expected.txt b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-layout/timeline-layout-expected.txt index 6f3cc681..a07f1fd 100644 --- a/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-layout/timeline-layout-expected.txt +++ b/third_party/blink/web_tests/http/tests/devtools/tracing/timeline-layout/timeline-layout-expected.txt
@@ -42,6 +42,44 @@ { data : { beginData : { + dirtyObjects : 3 + frame : <string> + partialLayout : false + stackTrace : <object> + totalObjects : 22 + } + endData : { + layoutRoots : [ + { + depth : 1 + nodeId : <number> + quads : [ + [ + 0 + 0 + 800 + 0 + 800 + 600 + 0 + 600 + ] + ] + } + ] + } + } + endTime : <number> + frameId : <string> + stackTrace : <object> + startTime : <number> + type : "Layout" +} +Text details for Layout: test://evaluations/0/timeline-layout.js:40:32 +Layout Properties: +{ + data : { + beginData : { dirtyObjects : 2 frame : <string> partialLayout : true @@ -76,4 +114,42 @@ type : "Layout" } Text details for Layout: test://evaluations/0/timeline-layout.js:40:32 +Layout Properties: +{ + data : { + beginData : { + dirtyObjects : 3 + frame : <string> + partialLayout : false + stackTrace : <object> + totalObjects : 22 + } + endData : { + layoutRoots : [ + { + depth : 1 + nodeId : <number> + quads : [ + [ + 0 + 0 + 800 + 0 + 800 + 600 + 0 + 600 + ] + ] + } + ] + } + } + endTime : <number> + frameId : <string> + stackTrace : <object> + startTime : <number> + type : "Layout" +} +Text details for Layout: test://evaluations/0/timeline-layout.js:40:32
diff --git a/third_party/blink/web_tests/http/tests/inspector-protocol/console/console-let-const-with-api-expected.txt b/third_party/blink/web_tests/http/tests/inspector-protocol/console/console-let-const-with-api-expected.txt index 06f4097..1d8510c3 100644 --- a/third_party/blink/web_tests/http/tests/inspector-protocol/console/console-let-const-with-api-expected.txt +++ b/third_party/blink/web_tests/http/tests/inspector-protocol/console/console-let-const-with-api-expected.txt
@@ -3,24 +3,24 @@ second 'let a = 1;' result: wasThrown = true exception message: Uncaught SyntaxError: Identifier 'a' has already been declared {"result":{"type":"number","value":42,"description":"42"}} -function $(selector, [startNode]) { [Command Line API] } -function $$(selector, [startNode]) { [Command Line API] } -function $x(xpath, [startNode]) { [Command Line API] } -function dir(value) { [Command Line API] } -function dirxml(value) { [Command Line API] } -function keys(object) { [Command Line API] } -function values(object) { [Command Line API] } -function profile(title) { [Command Line API] } -function profileEnd(title) { [Command Line API] } -function monitorEvents(object, [types]) { [Command Line API] } -function unmonitorEvents(object, [types]) { [Command Line API] } -function inspect(object) { [Command Line API] } -function copy(value) { [Command Line API] } -function clear() { [Command Line API] } -function getEventListeners(node) { [Command Line API] } -function debug(function, condition) { [Command Line API] } -function undebug(function) { [Command Line API] } -function monitor(function) { [Command Line API] } -function unmonitor(function) { [Command Line API] } -function table(data, [columns]) { [Command Line API] } +function $() { [native code] } +function $$() { [native code] } +function $x() { [native code] } +function dir() { [native code] } +function dirxml() { [native code] } +function keys() { [native code] } +function values() { [native code] } +function profile() { [native code] } +function profileEnd() { [native code] } +function monitorEvents() { [native code] } +function unmonitorEvents() { [native code] } +function inspect() { [native code] } +function copy() { [native code] } +function clear() { [native code] } +function getEventListeners() { [native code] } +function debug() { [native code] } +function undebug() { [native code] } +function monitor() { [native code] } +function unmonitor() { [native code] } +function table() { [native code] }
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml index fe5bd5d98..cd6b733 100644 --- a/tools/metrics/actions/actions.xml +++ b/tools/metrics/actions/actions.xml
@@ -19126,6 +19126,23 @@ </description> </action> +<action name="MobileInactiveTabsSettingsBack"> + <owner>alionadangla@chromium.org</owner> + <owner>lpromero@chromium.org</owner> + <description> + Reported when user goes back from Inactive Tabs Settings UI to Tabs Settings + screen. iOS only. + </description> +</action> + +<action name="MobileInactiveTabsSettingsClose"> + <owner>alionadangla@chromium.org</owner> + <owner>lpromero@chromium.org</owner> + <description> + Reported when Inactive Tabs Settings UI was dismissed. iOS only. + </description> +</action> + <action name="MobileIncognitoBiometricAuthenticationRequested"> <owner>stkhapugin@chromium.org</owner> <description> @@ -21730,6 +21747,23 @@ </description> </action> +<action name="MobileTabsSettingsBack"> + <owner>alionadangla@chromium.org</owner> + <owner>lpromero@chromium.org</owner> + <description> + Reported when user goes back from Tabs Settings UI to root Settings screen. + iOS only. + </description> +</action> + +<action name="MobileTabsSettingsClose"> + <owner>alionadangla@chromium.org</owner> + <owner>lpromero@chromium.org</owner> + <description> + Reported when Tabs Settings UI was dismissed. iOS only. + </description> +</action> + <action name="MobileTabStripCloseTab"> <obsolete>Deprecated as of 5/2015</obsolete> <owner>Please list the metric's owners. Add more owner tags as needed.</owner> @@ -29586,6 +29620,22 @@ </description> </action> +<action name="Settings.Tabs"> + <owner>alionadangla@chromium.org</owner> + <owner>lpromero@chromium.org</owner> + <description> + Reported when user navigates to Tabs Settings. iOS only. + </description> +</action> + +<action name="Settings.Tabs.InactiveTabs"> + <owner>alionadangla@chromium.org</owner> + <owner>lpromero@chromium.org</owner> + <description> + Reported when user navigates to Inactive Tabs Settings. iOS only. + </description> +</action> + <action name="Settings.VoiceSearch"> <owner>rohitrao@chromium.org</owner> <owner>sczs@chromium.org</owner>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index e6fcccb..b446a9f 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -26010,6 +26010,54 @@ <int value="3" label="Cancel"/> </enum> +<enum name="DevicePoliciesState"> + <int value="0" label="Consumer owned good key and policy"/> + <int value="1" label="Consumer owned malformed key, good policy"/> + <int value="2" label="Consumer owned no key, good policy"/> + <int value="3" label="Consumer owned good key, malformed policy"/> + <int value="4" label="Consumer owned malformed key, malformed policy"/> + <int value="5" label="Consumer owned no key, malformed policy"/> + <int value="6" label="Consumer owned good key, no policy"/> + <int value="7" label="Consumer owned malformed key, no policy"/> + <int value="8" label="Consumer owned no key, no policy"/> + <int value="9" label="Enrolled device good key and policy"/> + <int value="10" label="Enrolled device malformed key, good policy"/> + <int value="11" label="Enrolled device no key, good policy"/> + <int value="12" label="Enrolled device good key, malformed policy"/> + <int value="13" label="Enrolled device malformed key, malformed policy"/> + <int value="14" label="Enrolled device no key, malformed policy"/> + <int value="15" label="Enrolled device good key, no policy"/> + <int value="16" label="Enrolled device malformed key, no policy"/> + <int value="17" label="Enrolled device no key, no policy"/> + <int value="18" label="Legacy retail good key and policy"/> + <int value="19" label="Legacy retail malformed key, good policy"/> + <int value="20" label="Legacy retail no key, good policy"/> + <int value="21" label="Legacy retail good key, malformed policy"/> + <int value="22" label="Legacy retail malformed key, malformed policy"/> + <int value="23" label="Legacy retail no key, malformed policy"/> + <int value="24" label="Legacy retail good key, no policy"/> + <int value="25" label="Legacy retail malformed key, no policy"/> + <int value="26" label="Legacy retail no key, no policy"/> + <int value="27" label="Consumer kiosk good key and policy"/> + <int value="28" label="Consumer kiosk malformed key, good policy"/> + <int value="29" label="Consumer kiosk no key, good policy"/> + <int value="30" label="Consumer kiosk good key, malformed policy"/> + <int value="31" label="Consumer kiosk malformed key, malformed policy"/> + <int value="32" label="Consumer kiosk no key, malformed policy"/> + <int value="33" label="Consumer kiosk good key, no policy"/> + <int value="34" label="Consumer kiosk malformed key, no policy"/> + <int value="35" label="Consumer kiosk no key, no policy"/> + <int value="36" label="Unknown owner good key and policy"/> + <int value="37" label="Unknown owner malformed key, good policy"/> + <int value="38" label="Unknown owner no key, good policy"/> + <int value="39" label="Unknown owner good key, malformed policy"/> + <int value="40" label="Unknown owner malformed key, malformed policy"/> + <int value="41" label="Unknown owner no key, malformed policy"/> + <int value="42" label="Unknown owner good key, no policy"/> + <int value="43" label="Unknown owner malformed key, no policy"/> + <int value="44" label="Unknown owner no key, no policy"/> +</enum> + <enum name="DeviceSettingsFeatureFlagsMigrationStatus"> <int value="0" label="No feature flags present"/> <int value="1" label="Migration already completed previously"/> @@ -27566,21 +27614,6 @@ <int value="2" label="Mobile-friendly distillable"/> </enum> -<enum name="DistillableType2"> - <int value="0" label="Non-mobile-friendly, not distillable"/> - <int value="1" label="Mobile-friendly, not distillable"/> - <int value="2" label="Non-mobile-friendly, distillable"/> - <int value="3" label="Mobile-friendly, distillable"/> -</enum> - -<enum name="DistillRejection"> - <int value="0" label="Not an article"/> - <int value="1" label="Mobile-friendly"/> - <int value="2" label="Domain is filtered"/> - <int value="3" label="Predicted to be short"/> - <int value="4" label="Not rejected"/> -</enum> - <enum name="DlcService.InstallResult"> <int value="0" label="Unknown error"/> <int value="1" label="Success - New install"/> @@ -82612,17 +82645,6 @@ <int value="443" label="Port 443"/> </enum> -<enum name="PostMergeVerificationOutcome"> - <int value="0" label="Undefined"/> - <int value="1" label="Succeeded"/> - <int value="2" label="No accounts found"/> - <int value="3" label="Missing primary account"/> - <int value="4" label="Primary account is not the first"/> - <int value="5" label="Verification failed"/> - <int value="6" label="Connection failed"/> - <int value="7" label="Overflow"/> -</enum> - <enum name="PostOperationState"> <obsolete> Removed in M95.
diff --git a/tools/metrics/histograms/metadata/accessibility/histograms.xml b/tools/metrics/histograms/metadata/accessibility/histograms.xml index 98d31a7..78799bb 100644 --- a/tools/metrics/histograms/metadata/accessibility/histograms.xml +++ b/tools/metrics/histograms/metadata/accessibility/histograms.xml
@@ -2001,32 +2001,6 @@ </token> </histogram> -<histogram name="DomDistiller.DistillabilityRejection" enum="DistillRejection" - expires_after="M85"> - <owner>wychen@chromium.org</owner> - <summary> - The reason to reject distillability at PageDistillableAfterParsing time. - </summary> -</histogram> - -<histogram name="DomDistiller.DistillabilityScoreNMF.Negative" units="score" - expires_after="M77"> - <owner>wychen@chromium.org</owner> - <summary> - Score of distillability from AdaBoost model, non-mobile-friendly only. The - score shown here is multiplied by 100. - </summary> -</histogram> - -<histogram name="DomDistiller.DistillabilityScoreNMF.Positive" units="score" - expires_after="M77"> - <owner>wychen@chromium.org</owner> - <summary> - Score of distillability from AdaBoost model, non-mobile-friendly only. The - score shown here is multiplied by 100. - </summary> -</histogram> - <histogram name="DomDistiller.InfoBarUsage" enum="BooleanUsage" expires_after="2023-06-25"> <obsolete> @@ -2042,24 +2016,6 @@ </summary> </histogram> -<histogram name="DomDistiller.LongArticleScoreNMF.Negative" units="score" - expires_after="M85"> - <owner>wychen@chromium.org</owner> - <summary> - Score of long article from AdaBoost model, distillable and - non-mobile-friendly only. The score shown here is multiplied by 100. - </summary> -</histogram> - -<histogram name="DomDistiller.LongArticleScoreNMF.Positive" units="score" - expires_after="M85"> - <owner>wychen@chromium.org</owner> - <summary> - Score of long article from AdaBoost model, distillable and - non-mobile-friendly only. The score shown here is multiplied by 100. - </summary> -</histogram> - <histogram name="DomDistiller.MessageDismissalCondition" enum="ReaderModeMessageDismissalCondition" expires_after="2023-09-10"> <owner>twellington@chromium.org</owner> @@ -2071,26 +2027,6 @@ </summary> </histogram> -<histogram name="DomDistiller.PageDistillableAfterLoading" - enum="DistillableType2" expires_after="M85"> - <owner>wychen@chromium.org</owner> - <summary> - Records the "Distillable Type" (mobile-friendly not distillable, - mobile-friendly distillable, non-mobile-friendly not distillable, - non-mobile-friendly distillable) for each analyzed page after loading. - </summary> -</histogram> - -<histogram name="DomDistiller.PageDistillableAfterParsing" - enum="DistillableType2" expires_after="M85"> - <owner>wychen@chromium.org</owner> - <summary> - Records the "Distillable Type" (mobile-friendly not distillable, - mobile-friendly distillable, non-mobile-friendly not distillable, - non-mobile-friendly distillable) for each analyzed page after parsing. - </summary> -</histogram> - <histogram name="DomDistiller.PageHasDistilledData" enum="BooleanHasDistilledData" expires_after="M85"> <owner>kuan@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/enterprise/histograms.xml b/tools/metrics/histograms/metadata/enterprise/histograms.xml index 8a9718d..f8ca04c 100644 --- a/tools/metrics/histograms/metadata/enterprise/histograms.xml +++ b/tools/metrics/histograms/metadata/enterprise/histograms.xml
@@ -1150,7 +1150,7 @@ </histogram> <histogram name="Enterprise.Dlp.CaptureModeInitWarned" enum="BooleanWarned" - expires_after="2023-06-01"> + expires_after="2023-12-01"> <owner>aidazolic@chromium.org</owner> <owner>chromeos-dlp@google.com</owner> <summary> @@ -1354,7 +1354,7 @@ </histogram> <histogram name="Enterprise.Dlp.PrintingWarnProceeded" enum="Boolean" - expires_after="2023-06-01"> + expires_after="2023-12-01"> <owner>aidazolic@chromium.org</owner> <owner>chromeos-dlp@google.com</owner> <summary> @@ -1364,7 +1364,7 @@ </histogram> <histogram name="Enterprise.Dlp.PrintingWarnSilentProceeded" enum="Boolean" - expires_after="2023-06-01"> + expires_after="2023-12-01"> <owner>aidazolic@chromium.org</owner> <owner>chromeos-dlp@google.com</owner> <summary> @@ -1474,7 +1474,7 @@ </histogram> <histogram name="Enterprise.Dlp.ScreenShareWarned" enum="BooleanWarned" - expires_after="2023-06-01"> + expires_after="2023-12-01"> <owner>aidazolic@chromium.org</owner> <owner>chromeos-dlp@google.com</owner> <summary> @@ -1484,7 +1484,7 @@ </histogram> <histogram name="Enterprise.Dlp.ScreenShareWarnProceeded" enum="Boolean" - expires_after="2023-06-01"> + expires_after="2023-12-01"> <owner>aidazolic@chromium.org</owner> <owner>chromeos-dlp@google.com</owner> <summary> @@ -1494,7 +1494,7 @@ </histogram> <histogram name="Enterprise.Dlp.ScreenShareWarnSilentProceeded" enum="Boolean" - expires_after="2023-06-01"> + expires_after="2023-12-01"> <owner>aidazolic@chromium.org</owner> <owner>chromeos-dlp@google.com</owner> <summary> @@ -1526,7 +1526,7 @@ </histogram> <histogram name="Enterprise.Dlp.ScreenshotWarnProceeded" enum="Boolean" - expires_after="2023-06-01"> + expires_after="2023-12-01"> <owner>aidazolic@chromium.org</owner> <owner>chromeos-dlp@google.com</owner> <summary> @@ -2639,43 +2639,6 @@ <summary>Failure reason for OAuth token fetch for child user.</summary> </histogram> -<histogram name="Enterprise.UserPolicyChromeOS.DelayInitialization" units="ms" - expires_after="M82"> - <owner>mnissler@chromium.org</owner> - <summary>Initialization delay due to loading the user policy cache.</summary> -</histogram> - -<histogram name="Enterprise.UserPolicyChromeOS.InitialFetch.ClientError" - enum="EnterpriseDeviceManagementStatus" expires_after="M82"> - <owner>mnissler@chromium.org</owner> - <summary>Policy client error during initial policy fetch.</summary> -</histogram> - -<histogram - name="Enterprise.UserPolicyChromeOS.InitialFetch.DelayClientRegister" - units="ms" expires_after="M78"> - <owner>mnissler@chromium.org</owner> - <summary>Delay for registering the client with the policy server.</summary> -</histogram> - -<histogram name="Enterprise.UserPolicyChromeOS.InitialFetch.DelayOAuth2Token" - units="ms" expires_after="M77"> - <owner>mnissler@chromium.org</owner> - <summary>Delay for minting an OAuth2 acccess token.</summary> -</histogram> - -<histogram name="Enterprise.UserPolicyChromeOS.InitialFetch.DelayPolicyFetch" - units="ms" expires_after="M82"> - <owner>mnissler@chromium.org</owner> - <summary>Delay for fetching policy from the policy server.</summary> -</histogram> - -<histogram name="Enterprise.UserPolicyChromeOS.InitialFetch.DelayTotal" - units="ms" expires_after="M77"> - <owner>mnissler@chromium.org</owner> - <summary>Total delay for the initial policy fetch.</summary> -</histogram> - <histogram name="Enterprise.UserPolicyChromeOS.InitialFetch.OAuth2Error" enum="GoogleServiceAuthError" expires_after="2023-12-01"> <owner>igorcov@chromium.org</owner> @@ -2689,12 +2652,6 @@ </summary> </histogram> -<histogram name="Enterprise.UserPolicyChromeOS.InitialFetch.OAuth2NetworkError" - enum="NetErrorCodes" expires_after="M81"> - <owner>mnissler@chromium.org</owner> - <summary>Network error during OAuth2 access token fetch.</summary> -</histogram> - <histogram name="Enterprise.UserPolicyChromeOS.ReregistrationResult" enum="EnterpriseUserPolicyChromeOSReregistrationResult" expires_after="2023-12-01">
diff --git a/tools/metrics/histograms/metadata/login/histograms.xml b/tools/metrics/histograms/metadata/login/histograms.xml index b433f7f2..a885f41 100644 --- a/tools/metrics/histograms/metadata/login/histograms.xml +++ b/tools/metrics/histograms/metadata/login/histograms.xml
@@ -146,6 +146,17 @@ </summary> </histogram> +<histogram name="Login.DevicePolicyState" enum="DevicePoliciesState" + expires_after="2024-04-10"> + <owner>igorcov@chromium.org</owner> + <owner>chromeos-commercial-remote-management@chromium.org</owner> + <summary> + Reports the device ownership, the state of the device policy and the state + of the owner key. Logged only for the devices that have ownership taken as + per install attributes. Is logged at every device policy load. + </summary> +</histogram> + <histogram name="Login.FailureReason" enum="LoginFailureReason" expires_after="2023-10-08"> <owner>achuith@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/navigation/histograms.xml b/tools/metrics/histograms/metadata/navigation/histograms.xml index 25cf150..73bf4b6 100644 --- a/tools/metrics/histograms/metadata/navigation/histograms.xml +++ b/tools/metrics/histograms/metadata/navigation/histograms.xml
@@ -49,6 +49,7 @@ <variant name=".Embedder_DefaultSearchEngine"/> <variant name=".Embedder_DirectURLInput"/> <variant name=".SpeculationRule"/> + <variant name=".SpeculationRuleFromIsolatedWorld"/> </variants> <histogram name="BackForwardCache.AllSites.EvictedAfterDocumentRestoredReason"
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml index b12fb14..bfdfed7 100644 --- a/tools/metrics/histograms/metadata/others/histograms.xml +++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -8368,38 +8368,6 @@ </summary> </histogram> -<histogram name="OAuth2Login.PostMergeVerification" - enum="PostMergeVerificationOutcome" expires_after="2021-06-17"> - <obsolete> - Deprecated on 2023-03-27 in M114. The code that generated this histogram was - removed. - </obsolete> - <owner>droger@chromium.org</owner> - <owner>msarda@chromium.org</owner> - <owner>chrome-signin-team@google.com</owner> - <summary> - Outcome of Chrome OS GAIA cookie post-merge session verification process. It - measures how often /MergeSession request collided with browser session - restore process resulting in partially authenticated primary GAIA session. - </summary> -</histogram> - -<histogram name="OAuth2Login.PreMergeVerification" - enum="PostMergeVerificationOutcome" expires_after="2021-06-17"> - <obsolete> - Deprecated on 2023-03-27 in M114. The code that generated this histogram was - removed. - </obsolete> - <owner>droger@chromium.org</owner> - <owner>msarda@chromium.org</owner> - <owner>chrome-signin-team@google.com</owner> - <summary> - Outcome of Chrome OS GAIA cookie pre-merge session verification process. It - measures how often we need to perform /MergeSession request to - re-authenticated exisitng user with GAIA. - </summary> -</histogram> - <histogram name="OAuth2Login.SessionRestore" enum="GaiaSessionRestoreOutcome" expires_after="2023-08-20"> <owner>anastasiian@chromium.org</owner> @@ -9835,17 +9803,6 @@ </summary> </histogram> -<histogram name="PushMessaging.BackgroundBudget" units="units" - expires_after="2018-08-30"> - <owner>peter@chromium.org</owner> - <summary> - Whenever a Service Worker receives a push message, this records the budget - available to the service worker, which is an internal Chrome value for the - amount of background processing a service worker is allowed to do without - visibly alerting the user. Scale for the budget is 0 to 100. - </summary> -</histogram> - <histogram name="PushMessaging.CheckOriginForAbuseTime" units="ms" expires_after="2022-06-19"> <owner>knollr@chromium.org</owner> @@ -9937,24 +9894,6 @@ </summary> </histogram> -<histogram name="PushMessaging.SESForLowBudgetOrigin" units="units" - expires_after="2018-08-30"> - <owner>peter@chromium.org</owner> - <summary> - When a Service Worker hits low budget when servicing a push message, this - records what the Site Engagement Service score is at that time. - </summary> -</histogram> - -<histogram name="PushMessaging.SESForNoBudgetOrigin" units="units" - expires_after="2018-08-30"> - <owner>peter@chromium.org</owner> - <summary> - When a Service Worker hits zero budget when servicing a push message, this - records what the Site Engagement Service score is at that time. - </summary> -</histogram> - <histogram name="PushMessaging.TimeToReadPersistedMessages" units="ms" expires_after="M98"> <owner>peter@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/password/histograms.xml b/tools/metrics/histograms/metadata/password/histograms.xml index bc6d54a4..d6a525a 100644 --- a/tools/metrics/histograms/metadata/password/histograms.xml +++ b/tools/metrics/histograms/metadata/password/histograms.xml
@@ -977,7 +977,7 @@ </histogram> <histogram name="PasswordManager.BiometricAuthPwdFill.CanAuthenticate" - enum="BiometricsAvailability" expires_after="2023-04-23"> + enum="BiometricsAvailability" expires_after="M118"> <owner>ioanap@chromium.org</owner> <owner>fhorschig@chromium.org</owner> <summary> @@ -2215,6 +2215,16 @@ </summary> </histogram> +<histogram name="PasswordManager.OpenedAsShortcut" enum="Boolean" + expires_after="2023-10-15"> + <owner>vasilii@chromium.org</owner> + <owner>vsemeniuk@google.com</owner> + <summary> + Records whether Password Manager was opened as a standalone app or inside a + browser window. Recorded every time Password Manager is opened. + </summary> +</histogram> + <histogram name="PasswordManager.ParserDetectedOtpFieldWithRegex" enum="Boolean" expires_after="2023-07-01"> <owner>kolos@chromium.org</owner> @@ -3157,6 +3167,38 @@ </summary> </histogram> +<histogram name="PasswordManager.ReuseCheck.CheckedPasswords" units="passwords" + expires_after="2023-09-01"> + <owner>vsemeniuk@google.com</owner> + <owner>vasilii@chromium.org</owner> + <summary> + The number of passwords analyzed during the password reuse check. Note: this + is a number of unique password values. Recorded after reuse check is + finished. + </summary> +</histogram> + +<histogram name="PasswordManager.ReuseCheck.ReusedPasswords" units="passwords" + expires_after="2023-09-01"> + <owner>vsemeniuk@google.com</owner> + <owner>vasilii@chromium.org</owner> + <summary> + The number of reused passwords found when the password reuse check + completed. Note: this is a number of unique password values. Recorded after + reuse check is finished. + </summary> +</histogram> + +<histogram name="PasswordManager.ReuseCheck.Time" units="ms" + expires_after="2023-09-01"> + <owner>vsemeniuk@google.com</owner> + <owner>vasilii@chromium.org</owner> + <summary> + The time it took to complete the password reuse check. Recorded after reuse + check is finished. + </summary> +</histogram> + <histogram name="PasswordManager.ReusedPasswordType" enum="ReusedPasswordType" expires_after="2023-10-15"> <owner>vakh@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml index 2471440..a7a9899c 100644 --- a/tools/metrics/histograms/metadata/safe_browsing/histograms.xml +++ b/tools/metrics/histograms/metadata/safe_browsing/histograms.xml
@@ -1812,6 +1812,16 @@ </summary> </histogram> +<histogram name="SafeBrowsing.RT.GetCacheResultIsFromPastSession" + enum="Boolean" expires_after="2023-07-20"> + <owner>thefrog@chromium.org</owner> + <owner>chrome-counter-abuse-alerts@google.com</owner> + <summary> + If the real-time URL cache lookup had a cache hit, logs whether the cache + entry was defined in a previous browser session rather than the current one. + </summary> +</histogram> + <histogram name="SafeBrowsing.RT.GetToken.Time" units="ms" expires_after="2023-10-15"> <owner>xinghuilu@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/segmentation_platform/histograms.xml b/tools/metrics/histograms/metadata/segmentation_platform/histograms.xml index 14df249..21461ae 100644 --- a/tools/metrics/histograms/metadata/segmentation_platform/histograms.xml +++ b/tools/metrics/histograms/metadata/segmentation_platform/histograms.xml
@@ -36,6 +36,7 @@ <variant name="ResumeHeavyUser"/> <variant name="SearchUser"/> <variant name="ShoppingUser"/> + <variant name="TabletProductivityUser"/> </variants> <variants name="Index"> @@ -97,7 +98,7 @@ <variant name="SearchUserSegment"/> <variant name="Share"/> <variant name="ShoppingUser"/> - <variant name="TabletProductivityUserSegment"/> + <variant name="TabletProductivityUser"/> <variant name="Unknown"/> <variant name="Voice"/> </variants>
diff --git a/tools/metrics/histograms/metadata/signin/histograms.xml b/tools/metrics/histograms/metadata/signin/histograms.xml index 20ea00f..3a062aa 100644 --- a/tools/metrics/histograms/metadata/signin/histograms.xml +++ b/tools/metrics/histograms/metadata/signin/histograms.xml
@@ -512,9 +512,10 @@ <owner>msarda@chromium.org</owner> <owner>chrome-signin-team@google.com</owner> <summary> - Records whether sync was turned on/consented on a profile at least once. - This histogram is recorded upon profile load, and only for the case where - the user is fully signed out. + Records whether sync was turned on/consented on a profile at least once + since the last time all profile data was wiped out (e.g. the user chose to + clear data upon signout). This histogram is recorded upon profile load, and + only for the case where the user is fully signed out. </summary> </histogram> @@ -524,10 +525,11 @@ <owner>msarda@chromium.org</owner> <owner>chrome-signin-team@google.com</owner> <summary> - Records whether sync was turned on/consented on a profile at least once. - This histogram is recorded upon profile load, and only if sync is off - (either the user is fully signed out, or the user is signed in without - turning sync on). + Records whether sync was turned on/consented on a profile at least once + since the last time all profile data was wiped out (e.g. the user chose to + clear data upon signout). This histogram is recorded upon profile load, and + only if sync is off (either the user is fully signed out, or the user is + signed in without turning sync on). </summary> </histogram>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json index 62b3bb5..ac74cf6 100644 --- a/tools/perf/core/perfetto_binary_roller/binary_deps.json +++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -6,7 +6,7 @@ }, "win": { "hash": "5df410606c35db860d68e0c3866484f23477a266", - "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/4d46c527b1dd8a864a7f26e7549d22a0872d3dd1/trace_processor_shell.exe" + "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/4f9f0278b3d294d668cae35171e5070be160536a/trace_processor_shell.exe" }, "linux_arm": { "hash": "1d229abc94dea54ab4bb4327e78e18f942d08bf9", @@ -14,15 +14,15 @@ }, "mac": { "hash": "d6a4d3c988c9b763491d45342c7a274ab7f045dc", - "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/4d46c527b1dd8a864a7f26e7549d22a0872d3dd1/trace_processor_shell" + "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/mac/4f9f0278b3d294d668cae35171e5070be160536a/trace_processor_shell" }, "mac_arm64": { "hash": "7a4026b8718994145a52586fdec6e9447573345a", "full_remote_path": "perfetto-luci-artifacts/adbbb6c78e3a86c5e87b0338d9e42eb6b4ddbf4d/mac-arm64/trace_processor_shell" }, "linux": { - "hash": "94074b17585e392d8e021b57516e6004f3e3022f", - "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/4f9f0278b3d294d668cae35171e5070be160536a/trace_processor_shell" + "hash": "0f1eb1cbf6622d1184f5b526295855ff964d5d80", + "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/26b08771635aa44efa337d344336008dbcb65cdf/trace_processor_shell" } }, "power_profile.sql": {
diff --git a/ui/events/BUILD.gn b/ui/events/BUILD.gn index b793bb2..40bfcdd 100644 --- a/ui/events/BUILD.gn +++ b/ui/events/BUILD.gn
@@ -98,6 +98,7 @@ sources = [ "base_event_utils.cc", "base_event_utils.h", + "event_latency_metadata.h", "event_switches.cc", "event_switches.h", "events_base_export.h", @@ -496,6 +497,7 @@ "//ui/display", "//ui/gfx", "//ui/gfx/geometry", + "//ui/latency", ] defines = [ "GESTURE_DETECTION_IMPLEMENTATION" ]
diff --git a/ui/events/blink/blink_event_util.cc b/ui/events/blink/blink_event_util.cc index 9ad01f1..e7f0d58 100644 --- a/ui/events/blink/blink_event_util.cc +++ b/ui/events/blink/blink_event_util.cc
@@ -335,6 +335,8 @@ gesture.primary_unique_touch_event_id = details.primary_unique_touch_event_id(); gesture.unique_touch_event_id = unique_touch_event_id; + gesture.GetModifiableEventLatencyMetadata() = + details.GetEventLatencyMetadata(); switch (details.type()) { case ET_GESTURE_SHOW_PRESS:
diff --git a/ui/events/event_latency_metadata.h b/ui/events/event_latency_metadata.h new file mode 100644 index 0000000..b61ccf8 --- /dev/null +++ b/ui/events/event_latency_metadata.h
@@ -0,0 +1,30 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_EVENTS_EVENT_LATENCY_METADATA_H_ +#define UI_EVENTS_EVENT_LATENCY_METADATA_H_ + +#include "base/time/time.h" + +namespace ui { + +// The struct contains metadata about EventLatency events. +// There should only be POD classes in this struct to keep the metadata to a +// minimum. +struct EventLatencyMetadata { + // Time when event arrived in the BrowserMain thread. + base::TimeTicks arrived_in_browser_main_timestamp; + + // This field is used only by scroll events to understand when the related + // blocking touch move was dispatched to Renderer. If the related touch move + // wasn't blocking, this field is not set. + base::TimeTicks scrolls_blocking_touch_dispatched_to_renderer; + + // Time when event was disppatched to the Renderer from the Browser. + base::TimeTicks dispatched_to_renderer; +}; + +} // namespace ui + +#endif // UI_EVENTS_EVENT_LATENCY_METADATA_H_ \ No newline at end of file
diff --git a/ui/events/gesture_detection/filtered_gesture_provider.cc b/ui/events/gesture_detection/filtered_gesture_provider.cc index b832c1db..d497ee0 100644 --- a/ui/events/gesture_detection/filtered_gesture_provider.cc +++ b/ui/events/gesture_detection/filtered_gesture_provider.cc
@@ -62,9 +62,11 @@ void FilteredGestureProvider::OnTouchEventAck( uint32_t unique_event_id, bool event_consumed, - bool is_source_touch_event_set_blocking) { + bool is_source_touch_event_set_blocking, + const absl::optional<EventLatencyMetadata>& event_latency_metadata) { gesture_filter_.OnTouchEventAck(unique_event_id, event_consumed, - is_source_touch_event_set_blocking); + is_source_touch_event_set_blocking, + event_latency_metadata); } void FilteredGestureProvider::ResetGestureHandlingState() {
diff --git a/ui/events/gesture_detection/filtered_gesture_provider.h b/ui/events/gesture_detection/filtered_gesture_provider.h index cbe9984..f02ff45 100644 --- a/ui/events/gesture_detection/filtered_gesture_provider.h +++ b/ui/events/gesture_detection/filtered_gesture_provider.h
@@ -48,9 +48,15 @@ // To be called upon asynchronous and synchronous ack of an event that was // forwarded after a successful call to |OnTouchEvent()|. + // |event_latency_metadata| is provided only if the touch event or + // corresponding touch event was blocked before sending to the Renderer. This + // definition of blocking is not related to the value of + // |is_source_touch_event_set_blocking|. void OnTouchEventAck(uint32_t unique_event_id, bool event_consumed, - bool is_source_touch_event_set_blocking); + bool is_source_touch_event_set_blocking, + const absl::optional<EventLatencyMetadata>& + event_latency_metadata = absl::nullopt); void ResetGestureHandlingState();
diff --git a/ui/events/gesture_detection/gesture_event_data_packet.cc b/ui/events/gesture_detection/gesture_event_data_packet.cc index dfd08725..bc6f6fe 100644 --- a/ui/events/gesture_detection/gesture_event_data_packet.cc +++ b/ui/events/gesture_detection/gesture_event_data_packet.cc
@@ -122,4 +122,15 @@ } } +void GestureEventDataPacket::AddEventLatencyMetadataToGestures( + const EventLatencyMetadata& event_latency_metadata, + const base::RepeatingCallback<bool(const ui::GestureEventData&)>& filter) { + for (auto& gesture : gestures_) { + if (filter.Run(gesture)) { + gesture.details.GetModifiableEventLatencyMetadata() = + event_latency_metadata; + } + } +} + } // namespace ui
diff --git a/ui/events/gesture_detection/gesture_event_data_packet.h b/ui/events/gesture_detection/gesture_event_data_packet.h index 285c837..8719711 100644 --- a/ui/events/gesture_detection/gesture_event_data_packet.h +++ b/ui/events/gesture_detection/gesture_event_data_packet.h
@@ -7,8 +7,10 @@ #include <stddef.h> #include <stdint.h> +#include <functional> #include "base/containers/stack_container.h" +#include "base/functional/callback.h" #include "base/time/time.h" #include "ui/events/gesture_detection/gesture_detection_export.h" #include "ui/events/gesture_detection/gesture_event_data.h" @@ -66,6 +68,10 @@ AckState ack_state() { return ack_state_; } uint32_t unique_touch_event_id() const { return unique_touch_event_id_; } + void AddEventLatencyMetadataToGestures( + const EventLatencyMetadata& event_latency_metadata, + const base::RepeatingCallback<bool(const ui::GestureEventData&)>& filter); + private: GestureEventDataPacket(base::TimeTicks timestamp, GestureSource source,
diff --git a/ui/events/gesture_detection/gesture_event_data_packet_unittest.cc b/ui/events/gesture_detection/gesture_event_data_packet_unittest.cc index b4995671..c65b011 100644 --- a/ui/events/gesture_detection/gesture_event_data_packet_unittest.cc +++ b/ui/events/gesture_detection/gesture_event_data_packet_unittest.cc
@@ -134,4 +134,33 @@ EXPECT_EQ(gfx::PointF(gesture.x, gesture.y), packet.touch_location()); } +TEST_F(GestureEventDataPacketTest, AddEventLatencyMetadataToGestures) { + GestureEventDataPacket packet = GestureEventDataPacket::FromTouch( + MockMotionEvent(MotionEvent::Action::DOWN)); + packet.Push(CreateGesture(ET_GESTURE_TAP)); + packet.Push(CreateGesture(ET_GESTURE_SCROLL_UPDATE)); + packet.Push(CreateGesture(ET_GESTURE_PINCH_UPDATE)); + + EventLatencyMetadata event_latency_metadata; + event_latency_metadata.scrolls_blocking_touch_dispatched_to_renderer = + base::TimeTicks::Now(); + packet.AddEventLatencyMetadataToGestures( + event_latency_metadata, + base::BindRepeating([](const ui::GestureEventData& data) { + return data.type() == ET_GESTURE_SCROLL_UPDATE; + })); + + EXPECT_TRUE(packet.gesture(0) + .details.GetEventLatencyMetadata() + .scrolls_blocking_touch_dispatched_to_renderer.is_null()); + EXPECT_EQ( + packet.gesture(1) + .details.GetEventLatencyMetadata() + .scrolls_blocking_touch_dispatched_to_renderer, + event_latency_metadata.scrolls_blocking_touch_dispatched_to_renderer); + EXPECT_TRUE(packet.gesture(2) + .details.GetEventLatencyMetadata() + .scrolls_blocking_touch_dispatched_to_renderer.is_null()); +} + } // namespace ui
diff --git a/ui/events/gesture_detection/touch_disposition_gesture_filter.cc b/ui/events/gesture_detection/touch_disposition_gesture_filter.cc index 219d06d3..4cb988e 100644 --- a/ui/events/gesture_detection/touch_disposition_gesture_filter.cc +++ b/ui/events/gesture_detection/touch_disposition_gesture_filter.cc
@@ -9,6 +9,7 @@ #include "base/auto_reset.h" #include "base/check_op.h" #include "base/notreached.h" +#include "base/trace_event/typed_macros.h" #include "ui/events/gesture_event_details.h" namespace ui { @@ -134,6 +135,11 @@ gesture_source == GestureEventDataPacket::TOUCH_START; } +bool DoAddInputTimestampsToGesture(const GestureEventData& gesture_data) { + return gesture_data.type() == EventType::ET_GESTURE_SCROLL_UPDATE || + gesture_data.type() == EventType::ET_GESTURE_SCROLL_BEGIN; +} + } // namespace // TouchDispositionGestureFilter @@ -201,7 +207,8 @@ void TouchDispositionGestureFilter::OnTouchEventAck( uint32_t unique_touch_event_id, bool event_consumed, - bool is_source_touch_event_set_blocking) { + bool is_source_touch_event_set_blocking, + const absl::optional<EventLatencyMetadata>& event_latency_metadata) { // Spurious asynchronous acks should not trigger a crash. if (IsEmpty() || (Head().empty() && sequences_.size() == 1)) return; @@ -216,16 +223,17 @@ Tail().back().gesture_source() != GestureEventDataPacket::TOUCH_TIMEOUT) { Tail().back().Ack(event_consumed, is_source_touch_event_set_blocking); if (sequences_.size() == 1 && Tail().size() == 1) - SendAckedEvents(); + SendAckedEvents(event_latency_metadata); } else { DCHECK(!Head().empty()); DCHECK_EQ(Head().front().unique_touch_event_id(), unique_touch_event_id); Head().front().Ack(event_consumed, is_source_touch_event_set_blocking); - SendAckedEvents(); + SendAckedEvents(event_latency_metadata); } } -void TouchDispositionGestureFilter::SendAckedEvents() { +void TouchDispositionGestureFilter::SendAckedEvents( + const absl::optional<EventLatencyMetadata>& event_latency_metadata) { // Dispatch all packets corresponding to ack'ed touches, as well as // any pending timeout-based packets. bool touch_packet_for_current_ack_handled = false; @@ -256,8 +264,20 @@ // Aura, we could trigger a touch-cancel). As popping the sequence destroys // the packet, we copy the packet before popping it. touch_packet_for_current_ack_handled = true; - const GestureEventDataPacket packet = sequence.front(); + GestureEventDataPacket packet = sequence.front(); sequence.pop(); + + if (source == GestureEventDataPacket::TOUCH_MOVE && + event_latency_metadata.has_value()) { + EventLatencyMetadata gesture_event_latency_metadata; + gesture_event_latency_metadata + .scrolls_blocking_touch_dispatched_to_renderer = + event_latency_metadata->dispatched_to_renderer; + packet.AddEventLatencyMetadataToGestures( + std::move(gesture_event_latency_metadata), + base::BindRepeating(DoAddInputTimestampsToGesture)); + } + FilterAndSendPacket(packet); } DCHECK(touch_packet_for_current_ack_handled);
diff --git a/ui/events/gesture_detection/touch_disposition_gesture_filter.h b/ui/events/gesture_detection/touch_disposition_gesture_filter.h index 53bbd88..10f2bd0 100644 --- a/ui/events/gesture_detection/touch_disposition_gesture_filter.h +++ b/ui/events/gesture_detection/touch_disposition_gesture_filter.h
@@ -9,10 +9,12 @@ #include "base/containers/queue.h" #include "base/memory/raw_ptr.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "ui/events/gesture_detection/bitset_32.h" #include "ui/events/gesture_detection/gesture_detection_export.h" #include "ui/events/gesture_detection/gesture_event_data_packet.h" #include "ui/events/types/event_type.h" +#include "ui/latency/latency_info.h" namespace ui { @@ -53,9 +55,18 @@ PacketResult OnGesturePacket(const GestureEventDataPacket& packet); // OnTouchEventAck must be called upon receipt of every touch event ack. + // |event_latency_metadata| is provided only if the touch event or + // corresponding touch event was blocked before sending to the Renderer. This + // definition of blocking is not related to the value of + // |is_source_touch_event_set_blocking| since + // |is_source_touch_event_set_blocking| refers to the behavior of blocking + // future inputs, not whether the current event was dispatched blocking to the + // renderer. void OnTouchEventAck(uint32_t unique_touch_event_id, bool event_consumed, - bool is_source_touch_event_set_blocking); + bool is_source_touch_event_set_blocking, + const absl::optional<EventLatencyMetadata>& + event_latency_metadata = absl::nullopt); // Whether there are any active gesture sequences still queued in the filter. bool IsEmpty() const; @@ -101,7 +112,8 @@ void CancelFlingIfNecessary(const GestureEventDataPacket& packet); void EndScrollIfNecessary(const GestureEventDataPacket& packet); void PopGestureSequence(); - void SendAckedEvents(); + void SendAckedEvents( + const absl::optional<EventLatencyMetadata>& event_latency_metadata); GestureSequence& Head(); GestureSequence& Tail();
diff --git a/ui/events/gesture_event_details.h b/ui/events/gesture_event_details.h index ce44240..bdf8406 100644 --- a/ui/events/gesture_event_details.h +++ b/ui/events/gesture_event_details.h
@@ -9,6 +9,7 @@ #include "base/check_op.h" #include "ui/events/event_constants.h" +#include "ui/events/event_latency_metadata.h" #include "ui/events/events_base_export.h" #include "ui/events/types/event_type.h" #include "ui/events/types/scroll_types.h" @@ -185,6 +186,13 @@ data_.scale = scale; } + const EventLatencyMetadata& GetEventLatencyMetadata() const { + return input_timestamps_; + } + EventLatencyMetadata& GetModifiableEventLatencyMetadata() { + return input_timestamps_; + } + // Supports comparison over internal structures for testing. bool operator==(const GestureEventDetails& other) const { return type_ == other.type_ && @@ -259,6 +267,8 @@ // Bounding box is an axis-aligned rectangle that contains all the // enclosing rectangles of the touch-points in the gesture. gfx::RectF bounding_box_; + + EventLatencyMetadata input_timestamps_; }; } // namespace ui
diff --git a/ui/events/mojom/BUILD.gn b/ui/events/mojom/BUILD.gn index a1f910e6..47a11bf 100644 --- a/ui/events/mojom/BUILD.gn +++ b/ui/events/mojom/BUILD.gn
@@ -80,3 +80,24 @@ blink_cpp_typemaps = shared_cpp_typemaps webui_module_path = "chrome://resources/mojo/ui/events/mojom" } + +mojom("event_latency_metadata_mojom") { + generate_java = true + sources = [ "event_latency_metadata.mojom" ] + public_deps = [ "//mojo/public/mojom/base" ] + + cpp_typemaps = [ + { + types = [ + { + mojom = "ui.mojom.EventLatencyMetadata" + cpp = "::ui::EventLatencyMetadata" + }, + ] + + traits_headers = [ "event_latency_metadata_mojom_traits.h" ] + traits_sources = [ "event_latency_metadata_mojom_traits.cc" ] + traits_deps = [ "//ui/events:events_base" ] + }, + ] +}
diff --git a/ui/events/mojom/event_latency_metadata.mojom b/ui/events/mojom/event_latency_metadata.mojom new file mode 100644 index 0000000..615bdad --- /dev/null +++ b/ui/events/mojom/event_latency_metadata.mojom
@@ -0,0 +1,23 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +module ui.mojom; + +import "mojo/public/mojom/base/time.mojom"; + +// The struct contains metadata about EventLatency events. +// There should only be POD classes in this struct to keep the metadata to a +// minimum. +struct EventLatencyMetadata { + // Time when event arrived in the BrowserMain thread. + mojo_base.mojom.TimeTicks arrived_in_browser_main_timestamp; + + // This field is used only by scroll events to understand when the related + // blocking touch move was dispatched to Renderer. If the related touch move + // wasn't blocking, this field is not set. + mojo_base.mojom.TimeTicks scrolls_blocking_touch_dispatched_to_renderer; + + // Time when event was disppatched to the Renderer from the Browser. + mojo_base.mojom.TimeTicks dispatched_to_renderer; +};
diff --git a/ui/events/mojom/event_latency_metadata_mojom_traits.cc b/ui/events/mojom/event_latency_metadata_mojom_traits.cc new file mode 100644 index 0000000..9d1fa06 --- /dev/null +++ b/ui/events/mojom/event_latency_metadata_mojom_traits.cc
@@ -0,0 +1,27 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/events/mojom/event_latency_metadata_mojom_traits.h" + +namespace mojo { + +// static +bool StructTraits< + ui::mojom::EventLatencyMetadataDataView, + ui::EventLatencyMetadata>::Read(ui::mojom::EventLatencyMetadataDataView in, + ui::EventLatencyMetadata* out) { + DCHECK(out != nullptr); + + if (!in.ReadArrivedInBrowserMainTimestamp( + &out->arrived_in_browser_main_timestamp) || + !in.ReadScrollsBlockingTouchDispatchedToRenderer( + &out->scrolls_blocking_touch_dispatched_to_renderer) || + !in.ReadDispatchedToRenderer(&out->dispatched_to_renderer)) { + return false; + } + + return true; +} + +} // namespace mojo
diff --git a/ui/events/mojom/event_latency_metadata_mojom_traits.h b/ui/events/mojom/event_latency_metadata_mojom_traits.h new file mode 100644 index 0000000..04bcb4f --- /dev/null +++ b/ui/events/mojom/event_latency_metadata_mojom_traits.h
@@ -0,0 +1,38 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_EVENTS_MOJOM_EVENT_LATENCY_METADATA_MOJOM_TRAITS_H_ +#define UI_EVENTS_MOJOM_EVENT_LATENCY_METADATA_MOJOM_TRAITS_H_ + +#include "mojo/public/cpp/base/time_mojom_traits.h" +#include "mojo/public/cpp/bindings/struct_traits.h" +#include "ui/events/event_latency_metadata.h" +#include "ui/events/mojom/event_latency_metadata.mojom-shared.h" + +namespace mojo { + +template <> +struct StructTraits<ui::mojom::EventLatencyMetadataDataView, + ui::EventLatencyMetadata> { + static base::TimeTicks arrived_in_browser_main_timestamp( + const ui::EventLatencyMetadata& event_latency_metadata) { + return event_latency_metadata.arrived_in_browser_main_timestamp; + } + + static base::TimeTicks scrolls_blocking_touch_dispatched_to_renderer( + const ui::EventLatencyMetadata& event_latency_metadata) { + return event_latency_metadata.scrolls_blocking_touch_dispatched_to_renderer; + } + + static base::TimeTicks dispatched_to_renderer( + const ui::EventLatencyMetadata& event_latency_metadata) { + return event_latency_metadata.dispatched_to_renderer; + } + static bool Read(ui::mojom::EventLatencyMetadataDataView in, + ui::EventLatencyMetadata* out); +}; + +} // namespace mojo + +#endif // UI_EVENTS_MOJOM_EVENT_LATENCY_METADATA_MOJOM_TRAITS_H_ \ No newline at end of file
diff --git a/ui/file_manager/file_manager/externs/ts/state.js b/ui/file_manager/file_manager/externs/ts/state.js index f886dfd..380224b 100644 --- a/ui/file_manager/file_manager/externs/ts/state.js +++ b/ui/file_manager/file_manager/externs/ts/state.js
@@ -381,6 +381,7 @@ * folderShortcuts: !Array<!FileKey>, * androidApps: !Object<!string, !chrome.fileManagerPrivate.AndroidApp>, * bulkPinning: (chrome.fileManagerPrivate.BulkPinProgress|undefined), + * preferences: (chrome.fileManagerPrivate.Preferences|undefined), * }} */ export let State;
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager.js b/ui/file_manager/file_manager/foreground/js/file_manager.js index b26a26f..905a7b9 100644 --- a/ui/file_manager/file_manager/foreground/js/file_manager.js +++ b/ui/file_manager/file_manager/foreground/js/file_manager.js
@@ -29,6 +29,7 @@ import {ForegroundWindow} from '../../externs/foreground_window.js'; import {PropStatus} from '../../externs/ts/state.js'; import {Store} from '../../externs/ts/store.js'; +import {updatePreferences} from '../../state/actions/preferences.js'; import {updateSearch} from '../../state/actions/search.js'; import {addUiEntry, removeUiEntry} from '../../state/actions/ui_entries.js'; import {trashRootKey} from '../../state/reducers/volumes.js'; @@ -1678,6 +1679,8 @@ return; } + this.store_.dispatch(updatePreferences(prefs)); + let redraw = false; if (this.driveEnabled_ !== prefs.driveEnabled) { this.driveEnabled_ = prefs.driveEnabled;
diff --git a/ui/file_manager/file_manager/state/actions.ts b/ui/file_manager/file_manager/state/actions.ts index e54454a0..8635652 100644 --- a/ui/file_manager/file_manager/state/actions.ts +++ b/ui/file_manager/file_manager/state/actions.ts
@@ -8,6 +8,7 @@ import {ChangeDirectoryAction, ChangeFileTasksAction, ChangeSelectionAction, UpdateDirectoryContentAction} from './actions/current_directory.js'; import {AddFolderShortcutAction, RefreshFolderShortcutAction, RemoveFolderShortcutAction} from './actions/folder_shortcuts.js'; import {RefreshNavigationRootsAction, UpdateNavigationEntryAction} from './actions/navigation.js'; +import {UpdatePreferencesAction} from './actions/preferences.js'; import {SearchAction} from './actions/search.js'; import {AddUiEntryAction, RemoveUiEntryAction} from './actions/ui_entries.js'; import {AddVolumeAction, RemoveVolumeAction} from './actions/volumes.js'; @@ -25,7 +26,8 @@ AddUiEntryAction|RemoveUiEntryAction|UpdateDirectoryContentAction| UpdateMetadataAction|RefreshFolderShortcutAction|AddFolderShortcutAction| RemoveFolderShortcutAction|AddAndroidAppsAction|AddChildEntriesAction| - UpdateNavigationEntryAction|UpdateBulkPinProgressAction; + UpdateNavigationEntryAction|UpdateBulkPinProgressAction| + UpdatePreferencesAction; /** Enum to identify every Action in Files app. */ @@ -49,4 +51,5 @@ UPDATE_METADATA = 'update-metadata', ADD_CHILD_ENTRIES = 'add-child-entries', UPDATE_BULK_PIN_PROGRESS = 'update-bulk-pin-progress', + UPDATE_PREFERENCES = 'update-preferences', }
diff --git a/ui/file_manager/file_manager/state/actions/preferences.ts b/ui/file_manager/file_manager/state/actions/preferences.ts new file mode 100644 index 0000000..ee92344 --- /dev/null +++ b/ui/file_manager/file_manager/state/actions/preferences.ts
@@ -0,0 +1,30 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import {BaseAction} from '../../lib/base_store.js'; +import {ActionType} from '../actions.js'; + +/** + * Actions for Chrome Preferences. + * + * Chrome preferences store user data that is persisted to disk OR across + * profiles, this takes care of initially populating these values then keeping + * them updated on dynamic changes. + */ + +/** Action to update the chrome preferences to the store. */ +export interface UpdatePreferencesAction extends BaseAction { + type: ActionType.UPDATE_PREFERENCES; + payload: chrome.fileManagerPrivate.PreferencesChange| + chrome.fileManagerPrivate.Preferences; +} + +/** Action factory to update the user preferences to the store. */ +export function updatePreferences(payload: UpdatePreferencesAction['payload']): + UpdatePreferencesAction { + return { + type: ActionType.UPDATE_PREFERENCES, + payload, + }; +}
diff --git a/ui/file_manager/file_manager/state/reducers/bulk_pinning_unittest.ts b/ui/file_manager/file_manager/state/reducers/bulk_pinning_unittest.ts index c7c36e80..e164d123 100644 --- a/ui/file_manager/file_manager/state/reducers/bulk_pinning_unittest.ts +++ b/ui/file_manager/file_manager/state/reducers/bulk_pinning_unittest.ts
@@ -2,15 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import {assertDeepEquals} from 'chrome://webui-test/chromeos/chai_assert.js'; + import {updateBulkPinProgress} from '../actions/bulk_pinning.js'; -import {setupStore, waitDeepEquals} from '../for_tests.js'; +import {setupStore} from '../for_tests.js'; /** * Tests that bulk pin progress updates the store and overwrites existing values * on each update. */ export async function testUpdateBulkPinProgress(done: () => void) { - const bulkPinProgress: chrome.fileManagerPrivate.BulkPinProgress = { + const want: chrome.fileManagerPrivate.BulkPinProgress = { stage: chrome.fileManagerPrivate.BulkPinStage.STOPPED, freeSpaceBytes: 100, requiredSpaceBytes: 100, @@ -21,17 +23,23 @@ // Dispatch an action to update bulk pin progress. const store = setupStore(); - store.dispatch(updateBulkPinProgress(bulkPinProgress)); + store.dispatch(updateBulkPinProgress(want)); // Expect the bulk pin progress to be updated. - waitDeepEquals(store, bulkPinProgress, (state) => state.bulkPinning); + const firstState = store.getState().bulkPinning; + assertDeepEquals( + want, firstState, + `1. ${JSON.stringify(want)} != ${JSON.stringify(firstState)}`); // Dispatch another action to change the stage to `SYNCING`. - bulkPinProgress.stage = chrome.fileManagerPrivate.BulkPinStage.SYNCING; - store.dispatch(updateBulkPinProgress(bulkPinProgress)); + want.stage = chrome.fileManagerPrivate.BulkPinStage.SYNCING; + store.dispatch(updateBulkPinProgress(want)); // Expect the bulk pin progress to equal the new state. - waitDeepEquals(store, bulkPinProgress, (state) => state.bulkPinning); + const secondState = store.getState().bulkPinning; + assertDeepEquals( + want, secondState, + `2. ${JSON.stringify(want)} != ${JSON.stringify(secondState)}`); done(); }
diff --git a/ui/file_manager/file_manager/state/reducers/preferences.ts b/ui/file_manager/file_manager/state/reducers/preferences.ts new file mode 100644 index 0000000..92395441 --- /dev/null +++ b/ui/file_manager/file_manager/state/reducers/preferences.ts
@@ -0,0 +1,102 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/** + * @fileoverview Reducer for preferences. + * + * This file is checked via TS, so we suppress Closure checks. + * @suppress {checkTypes} + */ + +import {State} from '../../externs/ts/state.js'; +import {UpdatePreferencesAction} from '../actions/preferences.js'; + +/** + * Type alises to avoid writing the `chrome.fileManagerPrivate` prefix. + */ +export type Preferences = chrome.fileManagerPrivate.Preferences; +export type PreferencesChange = chrome.fileManagerPrivate.PreferencesChange; + +/** + * A type guard to see if the payload supplied is a change of preferences or the + * entire preferences object. Useful in ensuring subsequent type checks are done + * on the correct type (instead of the union type). + */ +function isPreferencesChange(payload: Preferences| + PreferencesChange): payload is PreferencesChange { + // The field `driveEnabled` is only on a `Preferences` object, so if this is + // undefined the payload is a `Preferences` object otherwise it's a + // `PreferencesChange` object. + if ((payload as Preferences).driveEnabled !== undefined) { + return false; + } + return true; +} + +/** + * Only update the existing preferences with their new values if they are + * defined. In the event of spreading the change event over the existing + * preferences, undefined values should not overwrite their existing values. + */ +function updateIfDefined( + updatedPreferences: Preferences, newPreferences: PreferencesChange, + key: keyof PreferencesChange): boolean { + if (!(key in newPreferences) || newPreferences[key] === undefined) { + return false; + } + if (updatedPreferences[key] === newPreferences[key]) { + return false; + } + // We're updating the `Preferences` original here and it doesn't type union + // well with `PreferencesChange`. Given we've done all the type validation + // above, cast them both to the `Preferences` type to ensure subsequent + // updates can work. + (updatedPreferences[key] as Preferences[keyof Preferences]) = + newPreferences[key] as Preferences[keyof Preferences]; + return true; +} + +export function updatePreferences( + currentState: State, action: UpdatePreferencesAction): State { + const preferences = action.payload; + + // This action takes two potential payloads: + // - chrome.fileManagerPrivate.Preferences + // - chrome.fileManagerPrivate.PreferencesChange + // Both of these have different type requirements. If we receive a + // `Preferences` update, just store the data directly in the store. If we + // receive a `PreferencesChange` the individual fields need to be checked to + // ensure they are different to what we have in the store AND they won't + // remove the existing data (i.e. they are not null or undefined). + if (!isPreferencesChange(preferences)) { + return { + ...currentState, + preferences, + }; + } + + const updatedPreferences = {...currentState.preferences!}; + const keysToCheck: Array<keyof PreferencesChange> = [ + 'cellularDisabled', + 'arcEnabled', + 'arcRemovableMediaAccessEnabled', + 'folderShortcuts', + 'driveFsBulkPinningEnabled', + ]; + let updated = false; + for (const key of keysToCheck) { + updated = updateIfDefined(updatedPreferences, preferences, key) || updated; + } + + // If no keys have been updated in the preference change, then send back the + // original state as nothing has changed. + if (!updated) { + return currentState; + } + + return { + ...currentState, + preferences: updatedPreferences, + }; +}
diff --git a/ui/file_manager/file_manager/state/reducers/preferences_unittest.ts b/ui/file_manager/file_manager/state/reducers/preferences_unittest.ts new file mode 100644 index 0000000..8013a6a --- /dev/null +++ b/ui/file_manager/file_manager/state/reducers/preferences_unittest.ts
@@ -0,0 +1,96 @@ +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import {assertDeepEquals} from 'chrome://webui-test/chromeos/chai_assert.js'; + +import {updatePreferences} from '../actions/preferences.js'; +import {setupStore} from '../for_tests.js'; + +import {Preferences} from './preferences.js'; + +/** + * Defines an initial state for user preferences that is used in all the tests. + */ +const INITIAL_PREFERENCES: Preferences = { + driveEnabled: false, + cellularDisabled: false, + searchSuggestEnabled: false, + use24hourClock: false, + timezone: 'GMT+10', + arcEnabled: false, + arcRemovableMediaAccessEnabled: false, + folderShortcuts: [], + trashEnabled: false, + officeFileMovedOneDrive: 0, + officeFileMovedGoogleDrive: 0, + driveFsBulkPinningEnabled: false, +}; + +/** + * Tests that bulk pin progress updates the store and overwrites existing values + * on each update. + */ +export async function testUpdatePreferences(done: () => void) { + // Dispatch an action to update user preferences. + const store = setupStore(); + store.dispatch(updatePreferences(INITIAL_PREFERENCES)); + + // Expect the preferences in the store to be updated. + const firstState = store.getState().preferences; + const want = INITIAL_PREFERENCES; + assertDeepEquals( + want, firstState, + `${JSON.stringify(want)} != ${JSON.stringify(firstState)}`); + + done(); +} + +export async function testPreferencesWithNoKeysUpdates(done: () => void) { + // Dispatch an action to update bulk pin progress. + const store = setupStore(); + store.dispatch(updatePreferences(INITIAL_PREFERENCES)); + + /** + * Test an individual preference update type. + * NOTE: Closure types defined on the fileManagerPrivate.PreferencesChange + * require that if 1 preference is being updated all others must have a key of + * undefined, however, this doesn't mimic the real world scenario of the keys + * not existing. Use `any` to ensure this can be passed through. + */ + const testPreferenceUpdate = + (preferenceUpdate: any, initialPreferences: Preferences): Preferences => { + store.dispatch(updatePreferences(preferenceUpdate)); + + const want = { + ...initialPreferences, + ...preferenceUpdate, + }; + + // Expect the preferences in the store to be updated. + const state = store.getState().preferences; + assertDeepEquals( + want, state, `${JSON.stringify(want)} != ${JSON.stringify(state)}`); + + return want; + }; + + // Verify all the `boolean` type preferences update appropriately, they are + // all initially `false` and this updates them all to `true` one by one. + let preferences = INITIAL_PREFERENCES; + const booleanPreferences = [ + 'cellularDisabled', + 'arcEnabled', + 'arcRemovableMediaAccessEnabled', + 'driveFsBulkPinningEnabled', + ]; + for (const pref of booleanPreferences) { + preferences = testPreferenceUpdate({[pref]: true}, preferences); + } + + // Folder shortcuts are an array, so test them separately. + const folderShortcutsUpdate = {folderShortcuts: ['some/shortcut']}; + testPreferenceUpdate(folderShortcutsUpdate, preferences); + + done(); +}
diff --git a/ui/file_manager/file_manager/state/reducers/root.ts b/ui/file_manager/file_manager/state/reducers/root.ts index a8f9c04d..175ce06 100644 --- a/ui/file_manager/file_manager/state/reducers/root.ts +++ b/ui/file_manager/file_manager/state/reducers/root.ts
@@ -11,6 +11,7 @@ import {changeDirectory, updateDirectoryContent, updateFileTasks, updateSelection} from './current_directory.js'; import {addFolderShortcut, refreshFolderShortcut, removeFolderShortcut} from './folder_shortcuts.js'; import {refreshNavigationRoots, updateNavigationEntry} from './navigation.js'; +import {updatePreferences} from './preferences.js'; import {search} from './search.js'; import {addUiEntry, removeUiEntry} from './ui_entries.js'; import {addVolume, removeVolume} from './volumes.js'; @@ -67,6 +68,8 @@ return addChildEntries(currentState, action); case ActionType.UPDATE_BULK_PIN_PROGRESS: return updateBulkPinning(currentState, action); + case ActionType.UPDATE_PREFERENCES: + return updatePreferences(currentState, action); default: console.error(`invalid action type: ${(action as any)?.type} action: ${ JSON.stringify(action)}`);
diff --git a/ui/file_manager/file_manager/state/store.ts b/ui/file_manager/file_manager/state/store.ts index e282f81..872a6fd 100644 --- a/ui/file_manager/file_manager/state/store.ts +++ b/ui/file_manager/file_manager/state/store.ts
@@ -59,6 +59,7 @@ folderShortcuts: [], androidApps: [], bulkPinning: undefined, + preferences: undefined, }; }
diff --git a/ui/file_manager/file_names.gni b/ui/file_manager/file_names.gni index d709114d..54e694d 100644 --- a/ui/file_manager/file_names.gni +++ b/ui/file_manager/file_names.gni
@@ -260,6 +260,7 @@ "file_manager/state/actions/current_directory.ts", "file_manager/state/actions/folder_shortcuts.ts", "file_manager/state/actions/navigation.ts", + "file_manager/state/actions/preferences.ts", "file_manager/state/actions/search.ts", "file_manager/state/actions/ui_entries.ts", "file_manager/state/actions/volumes.ts", @@ -276,6 +277,7 @@ "file_manager/state/reducers/current_directory.ts", "file_manager/state/reducers/folder_shortcuts.ts", "file_manager/state/reducers/navigation.ts", + "file_manager/state/reducers/preferences.ts", "file_manager/state/reducers/search.ts", "file_manager/state/reducers/ui_entries.ts", "file_manager/state/reducers/volumes.ts", @@ -351,6 +353,7 @@ "file_manager/state/reducers/current_directory_unittest.ts", "file_manager/state/reducers/folder_shortcuts_unittest.ts", "file_manager/state/reducers/navigation_unittest.ts", + "file_manager/state/reducers/preferences_unittest.ts", "file_manager/state/reducers/search_unittest.ts", "file_manager/state/reducers/ui_entries_unittest.ts", "file_manager/state/reducers/volumes_unittest.ts",
diff --git a/ui/latency/latency_info.cc b/ui/latency/latency_info.cc index dd76d2a..ef364fc 100644 --- a/ui/latency/latency_info.cc +++ b/ui/latency/latency_info.cc
@@ -148,11 +148,10 @@ AddLatencyNumberWithTimestampImpl(component, base::TimeTicks::Now(), nullptr); } -void LatencyInfo::AddLatencyNumberWithTraceName( - LatencyComponentType component, - const char* trace_name_str) { - AddLatencyNumberWithTimestampImpl(component, base::TimeTicks::Now(), - trace_name_str); +void LatencyInfo::AddLatencyNumberWithTraceName(LatencyComponentType component, + const char* trace_name_str, + base::TimeTicks now) { + AddLatencyNumberWithTimestampImpl(component, now, trace_name_str); } void LatencyInfo::AddLatencyNumberWithTimestamp(LatencyComponentType component,
diff --git a/ui/latency/latency_info.h b/ui/latency/latency_info.h index 64dfa11..3456e68 100644 --- a/ui/latency/latency_info.h +++ b/ui/latency/latency_info.h
@@ -130,7 +130,8 @@ // the trace event's name. // This function should only be called when adding a BEGIN component. void AddLatencyNumberWithTraceName(LatencyComponentType component, - const char* trace_name_str); + const char* trace_name_str, + base::TimeTicks now); // Modifies the current sequence number and adds a certain number of events // for a specific component.