diff --git a/DEPS b/DEPS index 1e37df3..2ded78b59 100644 --- a/DEPS +++ b/DEPS
@@ -240,7 +240,7 @@ # luci-go CIPD package version. # Make sure the revision is uploaded by infra-packagers builder. # https://ci.chromium.org/p/infra-internal/g/infra-packagers/console - 'luci_go': 'git_revision:f2aee64d8d98e4fe969d433785e969f3effe90c6', + 'luci_go': 'git_revision:299f8fef82a4bbdedf3cae74b19bd883dc3fa9a5', # This can be overridden, e.g. with custom_vars, to build clang from HEAD # instead of downloading the prebuilt pinned revision. @@ -291,19 +291,19 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. - 'src_internal_revision': 'bea2012bd2ef30585305fbc17db2600b340fcef7', + 'src_internal_revision': 'd517e9b109b54a3441b712813f69b8034e2d4676', # 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': 'a0b1007063b10045c078a5c499d921ac77d93579', + 'skia_revision': '49e39cd3cf1847f5e0464b4652735d6f0091f93d', # 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': 'e5edfbb3446fcbfb1b7f0847b0ee8154ecde9c60', + 'v8_revision': '596261193b6c808f82e6bcd0150f39129793f1f3', # 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': '5fd368aaf91c25a5fb8e4e214acb09fda36acdaa', + 'angle_revision': 'b0e0c9f617d0b4d706cbc2b135843f2f4cdb714a', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. @@ -383,7 +383,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': '5357bd73fb7c69cabd7fbb4d098c43b1e63d651a', + 'devtools_frontend_revision': '57b2c1d83fd82da2f49f0644023046ee5f964832', # 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. @@ -515,11 +515,11 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling llvm-libc # and whatever else without interference from each other. - 'compiler_rt_revision': 'd507e8c8847deb237602cb68fbff2ac75c666144', + 'compiler_rt_revision': 'a0b0e482f99c0b79b1c5330bd017157d9f23e700', # If you change this, also update the libc++ revision in # //buildtools/deps_revisions.gni. - 'libcxx_revision': '1ce2525ae355a30a61bacd896916c3416d1827b1', + 'libcxx_revision': 'c105b13e377d3fa5fdf033957f31abc1894a9968', # GN CIPD package version. 'gn_version': 'git_revision:3a4f5cea73eca32e9586e8145f97b04cbd4a1aee', @@ -1182,7 +1182,7 @@ 'packages': [ { 'package': 'chromium/chrome/android/orderfiles/arm', - 'version': 'iHbwu_4uyQYKmzOZlLMh90HZkKLSeGxmuUzaJPx0vaUC', + 'version': '_YL2zhs0P_79KiVZPdOAVqaW1SJ845RsmkF8bDL5jUsC', }, ], 'condition': 'checkout_android', @@ -1193,7 +1193,7 @@ 'packages': [ { 'package': 'chromium/chrome/android/orderfiles/arm64', - 'version': 'HLgotM0PuUufko3HiK9Hq_m6wZFYpi1wmNfCj3PtqpUC', + 'version': '_8k4P_-t3qvE5bKeqypIkQpWNVdtG4ykKrRAHTxmxpQC', }, ], 'condition': 'checkout_android', @@ -1596,7 +1596,7 @@ 'src/clank': { 'url': Var('chrome_git') + '/clank/internal/apps.git' + '@' + - '6e21ffac7e2b010713d1a7ebc0b50317309068c0', + '81a027f27278239ad842497fae52a645acb015d3', 'condition': 'checkout_android and checkout_src_internal', }, @@ -2047,7 +2047,7 @@ # Tools used when building Chrome for Chrome OS. This affects both the Simple # Chrome workflow, as well as the chromeos-chrome ebuild. 'src/third_party/chromite': { - 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'f8b47735153c5ccef3996503bd57ba270130ee4b', + 'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '3411a369f26bdab1098052dcc0805fe3e1063e4b', 'condition': 'checkout_chromeos', }, @@ -2079,7 +2079,7 @@ Var('chromium_git') + '/chromium/web-tests.git' + '@' + Var('crossbench_web_tests_revision'), 'src/third_party/depot_tools': - Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'ba3926011ba1bd617b6f368b59ec5e19647b96f6', + Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'e5797fdfad58cea9abb465291a06db8156b55177', 'src/third_party/devtools-frontend/src': Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'), @@ -2618,7 +2618,7 @@ Var('pdfium_git') + '/pdfium.git' + '@' + Var('pdfium_revision'), 'src/third_party/perfetto': - Var('chromium_git') + '/external/github.com/google/perfetto.git' + '@' + '6f026925036beba8c0911aef979234d68413524c', + Var('chromium_git') + '/external/github.com/google/perfetto.git' + '@' + '54b52ebe0a12a5672cd9544f6c1e3162ddc9d70c', 'src/base/tracing/test/data': { 'bucket': 'perfetto', @@ -2989,7 +2989,7 @@ Var('chromium_git') + '/webpagereplay.git' + '@' + Var('webpagereplay_revision'), 'src/third_party/webrtc': - Var('webrtc_git') + '/src.git' + '@' + '75c4772c989e9e7551f1186c72a27ff8c4a8a7ec', + Var('webrtc_git') + '/src.git' + '@' + '8d884426cc2fc6b44a4315ed4917035d218992d9', # 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. @@ -3122,7 +3122,7 @@ 'packages': [ { 'package': 'chromeos_internal/apps/help_app/app', - 'version': '-fDj5RPRb7WMQQYAzwPzW-5HPrynSeZqIO0TOK0dkrsC', + 'version': 'wtfqmVosUmQ9h1n7Omk1svGZt0ThuwPRGkxLCpwQABcC', }, ], 'condition': 'checkout_chromeos and checkout_src_internal', @@ -3133,7 +3133,7 @@ 'packages': [ { 'package': 'chromeos_internal/apps/media_app/app', - 'version': '5A73ffgEbpY_2bUFW_98unhkZNPdCB30OpDKNoYBH1sC', + 'version': 'QGlODNawFhhuEyC9rfuP4JKtZKVkKwLd0r3t9nfaaDsC', }, ], 'condition': 'checkout_chromeos and checkout_src_internal', @@ -3686,7 +3686,7 @@ 'src/components/optimization_guide/internal': { 'url': Var('chrome_git') + '/chrome/components/optimization_guide.git' + '@' + - 'fcc388612fe88fddb7335229bd0d70b6ec45d7bd', + '76c30fcad7ac08d84c88ddc4df6a7b5f607822f8', 'condition': 'checkout_src_internal', }, @@ -3752,7 +3752,7 @@ 'src/ios_internal': { 'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' + - '264a87f5ecb14297755d6381aace419f6cbedaed', + 'e733c5ec5c2437d1c4431c0debd400a2f4e83bd7', 'condition': 'checkout_ios and checkout_src_internal', },
diff --git a/ash/virtual_trackpad/virtual_trackpad_view.cc b/ash/virtual_trackpad/virtual_trackpad_view.cc index a70f692..8d6f7843 100644 --- a/ash/virtual_trackpad/virtual_trackpad_view.cc +++ b/ash/virtual_trackpad/virtual_trackpad_view.cc
@@ -255,7 +255,7 @@ blurred_background_ = std::make_unique<BlurredBackgroundShield>( this, SK_ColorTRANSPARENT, ColorProvider::kBackgroundBlurSigma, gfx::RoundedCornersF( - static_cast<float>(chromeos::kTopCornerRadiusWhenRestored))); + static_cast<float>(chromeos::kRoundedWindowSmallCornerRadius))); } VirtualTrackpadView::~VirtualTrackpadView() = default;
diff --git a/ash/wm/resize_shadow_controller.cc b/ash/wm/resize_shadow_controller.cc index db507a7..5904835c 100644 --- a/ash/wm/resize_shadow_controller.cc +++ b/ash/wm/resize_shadow_controller.cc
@@ -12,6 +12,7 @@ #include "ash/wm/resize_shadow.h" #include "ash/wm/window_properties.h" #include "ash/wm/window_state.h" +#include "chromeos/ui/base/chromeos_ui_constants.h" #include "chromeos/ui/base/window_properties.h" #include "chromeos/ui/base/window_state_type.h" #include "chromeos/ui/frame/frame_utils.h" @@ -20,13 +21,6 @@ #include "ui/gfx/geometry/rounded_corners_f.h" namespace ash { -namespace { - -// If window rounded corners are larger than the threshold, use resize shadow -// designed for larger rounded corners. -constexpr int kLargeRoundedCornerThreshold = 2; - -} // namespace ResizeShadowController::ResizeShadowController() = default; @@ -163,7 +157,7 @@ ash::WindowState::Get(window)->GetWindowRoundedCorners(); const int corner_radius = window_radii.upper_left(); const bool has_large_rounded_corners = - corner_radius > kLargeRoundedCornerThreshold; + corner_radius > chromeos::kRoundedWindowSmallCornerRadius; // If the `window` has a resize shadow with the requested type and the shadow // is configured for small/large rounded corners, no need to recreate it.
diff --git a/base/libcpp_hardening_test.cc b/base/libcpp_hardening_test.cc index 83c4a90..3eaed9d 100644 --- a/base/libcpp_hardening_test.cc +++ b/base/libcpp_hardening_test.cc
@@ -59,7 +59,7 @@ EXPECT_DEATH(std::ignore = vec[3], Not(ContainsRegex(".*assertion.*failed:"))); #else - GTEST_UNSUPPORTED_DEATH_TEST(vec[3], "", ); + GTEST_UNSUPPORTED_DEATH_TEST(std::ignore = vec[3], "", ); #endif // GTEST_HAS_DEATH_TEST && !GTEST_OS_LINUX_ANDROID #endif // CHECK_WILL_STREAM() }
diff --git a/base/metrics/field_trial_params.h b/base/metrics/field_trial_params.h index 0c5ab654d..8f6c4809 100644 --- a/base/metrics/field_trial_params.h +++ b/base/metrics/field_trial_params.h
@@ -235,8 +235,8 @@ // Returns the param-string for the given enum value. std::string GetName(Enum value) const { for (size_t i = 0; i < option_count; ++i) { - if (value == options[i].value) { - return options[i].name; + if (value == UNSAFE_TODO(options[i].value)) { + return UNSAFE_TODO(options[i].name); } } NOTREACHED();
diff --git a/base/metrics/histogram.cc b/base/metrics/histogram.cc index d49f5ad..c5a3de6 100644 --- a/base/metrics/histogram.cc +++ b/base/metrics/histogram.cc
@@ -808,9 +808,9 @@ LinearHistogram* histogram = static_cast<LinearHistogram*>(base_histogram); // Set range descriptions. if (descriptions_) { - for (int i = 0; descriptions_[i].description; ++i) { - histogram->bucket_description_[descriptions_[i].sample] = - descriptions_[i].description; + for (int i = 0; UNSAFE_TODO(descriptions_[i].description); ++i) { + UNSAFE_TODO(histogram->bucket_description_[descriptions_[i].sample] = + descriptions_[i].description); } } }
diff --git a/base/metrics/persistent_memory_allocator.cc b/base/metrics/persistent_memory_allocator.cc index 6c2b2f7..e79c886 100644 --- a/base/metrics/persistent_memory_allocator.cc +++ b/base/metrics/persistent_memory_allocator.cc
@@ -1350,12 +1350,14 @@ SCOPED_CRASH_KEY_STRING32( PMA, "ref_value_before", ref_is_magic_number - ? NumberToString((reference_ - 1)->load(std::memory_order_relaxed)) + ? NumberToString( + (UNSAFE_TODO(reference_ - 1))->load(std::memory_order_relaxed)) : "N/A"); SCOPED_CRASH_KEY_STRING32( PMA, "ref_value_after", ref_is_magic_number - ? NumberToString((reference_ + 1)->load(std::memory_order_relaxed)) + ? NumberToString( + (UNSAFE_TODO(reference_ + 1))->load(std::memory_order_relaxed)) : "N/A"); SCOPED_CRASH_KEY_BOOL(PMA, "ref_found", ref_found); SCOPED_CRASH_KEY_BOOL(PMA, "race_detected", race_detected);
diff --git a/base/strings/safe_sprintf.cc b/base/strings/safe_sprintf.cc index 53674c62..50863cbf 100644 --- a/base/strings/safe_sprintf.cc +++ b/base/strings/safe_sprintf.cc
@@ -164,7 +164,7 @@ // have been allocated for the |buffer_|. inline bool Out(char ch) { if (size_ >= 1 && count_ < size_) { - buffer_[count_] = ch; + UNSAFE_TODO(buffer_[count_] = ch); return IncrementCountByOne(); } // |count_| still needs to be updated, even if the buffer has been @@ -263,7 +263,8 @@ if (idx > size_) { idx = size_; } - return buffer_ + idx; + // SAFETY: idx checked against size_ above. + return UNSAFE_BUFFERS(buffer_ + idx); } // User-provided buffer that will receive the fully formatted output string. @@ -363,7 +364,9 @@ // have to discard digits in the order that we have already emitted // them. This is essentially equivalent to: // memmove(buffer_ + start, buffer_ + start + 1, size_ - start - 1) - for (char *move = buffer_ + start, *end = buffer_ + size_ - 1; + // SAFETY: start checked against size_ above. + for (char *move = UNSAFE_BUFFERS(buffer_ + start), + *end = UNSAFE_BUFFERS(buffer_ + size_ - 1); move < end; UNSAFE_TODO(++move)) { *move = UNSAFE_TODO(move[1]); } @@ -423,7 +426,8 @@ // order. We can't easily generate them in forward order, as we can't tell // the number of characters needed until we are done converting. // So, now, we reverse the string (except for the possible '-' sign). - char* front = buffer_ + start; + // SAFETY: start checked against size_ above. + char* front = UNSAFE_BUFFERS(buffer_ + start); char* back = GetInsertionPoint(); UNSAFE_TODO({ while (--back > front) {
diff --git a/build/config/clang/BUILD.gn b/build/config/clang/BUILD.gn index 5bff5c6..a1e635c 100644 --- a/build/config/clang/BUILD.gn +++ b/build/config/clang/BUILD.gn
@@ -97,7 +97,6 @@ # dynamically. plugin = "find-bad-constructs" plugin_arguments = [ - "span-ctor-from-string-literal", "raw-ref-template-as-trivial-member", "raw-span-template-as-trivial-member", "check-stack-allocated",
diff --git a/buildtools/deps_revisions.gni b/buildtools/deps_revisions.gni index caf7951..914a6e115 100644 --- a/buildtools/deps_revisions.gni +++ b/buildtools/deps_revisions.gni
@@ -5,5 +5,5 @@ declare_args() { # Used to cause full rebuilds on libc++ rolls. This should be kept in sync # with the libcxx_revision var in //DEPS. - libcxx_revision = "1ce2525ae355a30a61bacd896916c3416d1827b1" + libcxx_revision = "c105b13e377d3fa5fdf033957f31abc1894a9968" }
diff --git a/cc/animation/scroll_offset_animations_impl.cc b/cc/animation/scroll_offset_animations_impl.cc index 3e08899..25c7694 100644 --- a/cc/animation/scroll_offset_animations_impl.cc +++ b/cc/animation/scroll_offset_animations_impl.cc
@@ -273,15 +273,7 @@ ScrollOffsetAnimationsImpl::ScrollOffsetAnimationsImpl( AnimationHost* animation_host) - : animation_host_(animation_host) { - if (!features::MultiImplOnlyScrollAnimationsSupported()) { - // If MultiImplOnlyScrollAnimations is not supported only one impl-only - // scroll animation can be run at a time and it is managed through the - // singleton instantiated here. - scroll_offset_animation_ = - std::make_unique<ScrollOffsetAnimationImpl>(animation_host_); - } -} + : animation_host_(animation_host) {} ScrollOffsetAnimationsImpl::~ScrollOffsetAnimationsImpl() = default; @@ -291,21 +283,14 @@ const gfx::PointF& current_offset, float autoscroll_velocity, base::TimeDelta animation_start_offset) { - if (features::MultiImplOnlyScrollAnimationsSupported()) { - element_to_animation_map_.insert(std::pair( - element_id, - std::make_unique<ScrollOffsetAnimationImpl>(animation_host_))); - std::unique_ptr<ScrollOffsetAnimationImpl>& impl_animation = - element_to_animation_map_.at(element_id); - impl_animation->AutoScrollAnimationCreate( - element_id, target_offset, current_offset, autoscroll_velocity, - animation_start_offset); - } else { - DCHECK(scroll_offset_animation_); - scroll_offset_animation_->AutoScrollAnimationCreate( - element_id, target_offset, current_offset, autoscroll_velocity, - animation_start_offset); - } + element_to_animation_map_.insert( + std::pair(element_id, + std::make_unique<ScrollOffsetAnimationImpl>(animation_host_))); + std::unique_ptr<ScrollOffsetAnimationImpl>& impl_animation = + element_to_animation_map_.at(element_id); + impl_animation->AutoScrollAnimationCreate(element_id, target_offset, + current_offset, autoscroll_velocity, + animation_start_offset); } void ScrollOffsetAnimationsImpl::MouseWheelScrollAnimationCreate( @@ -314,7 +299,6 @@ const gfx::PointF& current_offset, base::TimeDelta delayed_by, base::TimeDelta animation_start_offset) { - if (features::MultiImplOnlyScrollAnimationsSupported()) { element_to_animation_map_.insert(std::pair( element_id, std::make_unique<ScrollOffsetAnimationImpl>(animation_host_))); @@ -323,12 +307,6 @@ impl_animation->MouseWheelScrollAnimationCreate(element_id, target_offset, current_offset, delayed_by, animation_start_offset); - } else { - DCHECK(scroll_offset_animation_); - scroll_offset_animation_->MouseWheelScrollAnimationCreate( - element_id, target_offset, current_offset, delayed_by, - animation_start_offset); - } } std::optional<gfx::PointF> @@ -338,127 +316,76 @@ base::TimeTicks frame_monotonic_time, base::TimeDelta delayed_by, ElementId element_id) { - if (features::MultiImplOnlyScrollAnimationsSupported()) { - ScrollOffsetAnimationImpl* animation = GetScrollAnimation(element_id); - DCHECK(animation); - return animation->ScrollAnimationUpdateTarget( - scroll_delta, max_scroll_offset, frame_monotonic_time, delayed_by); - } else { - DCHECK(scroll_offset_animation_); - return scroll_offset_animation_->ScrollAnimationUpdateTarget( - scroll_delta, max_scroll_offset, frame_monotonic_time, delayed_by); - } + ScrollOffsetAnimationImpl* animation = GetScrollAnimation(element_id); + DCHECK(animation); + return animation->ScrollAnimationUpdateTarget( + scroll_delta, max_scroll_offset, frame_monotonic_time, delayed_by); } void ScrollOffsetAnimationsImpl::ScrollAnimationApplyAdjustment( ElementId element_id, const gfx::Vector2dF& adjustment) { - if (features::MultiImplOnlyScrollAnimationsSupported()) { - if (ScrollOffsetAnimationImpl* animation = GetScrollAnimation(element_id)) { - animation->ScrollAnimationApplyAdjustment(element_id, adjustment); - } - } else { - DCHECK(scroll_offset_animation_); - return scroll_offset_animation_->ScrollAnimationApplyAdjustment(element_id, - adjustment); + if (ScrollOffsetAnimationImpl* animation = GetScrollAnimation(element_id)) { + animation->ScrollAnimationApplyAdjustment(element_id, adjustment); } } void ScrollOffsetAnimationsImpl::ScrollAnimationAbort(bool needs_completion, ElementId element_id) { - if (features::MultiImplOnlyScrollAnimationsSupported()) { - if (ScrollOffsetAnimationImpl* animation = GetScrollAnimation(element_id)) { - animation->ScrollAnimationAbort(needs_completion); - } - } else { - DCHECK(scroll_offset_animation_); - scroll_offset_animation_->ScrollAnimationAbort(needs_completion); + if (ScrollOffsetAnimationImpl* animation = GetScrollAnimation(element_id)) { + animation->ScrollAnimationAbort(needs_completion); } } void ScrollOffsetAnimationsImpl::HandleRemovedScrollAnimatingElements( bool commits_to_active) { - if (features::MultiImplOnlyScrollAnimationsSupported()) { - std::vector<ElementId> deleted; - for (auto& entry : element_to_animation_map_) { - ElementId element_id = entry.first; - if (!animation_host_->IsElementInPropertyTrees(element_id, - commits_to_active)) { - // We probably shouldn't need to check IsAnimating here, - // but some bots recycle AnimationHost between tests - // which seems to lead to referencing Animations with null - // KeyframeModels. Checking IsAnimating guards against this and also - // matches what was done pre-MultiImplOnlyScrollAnimationsSupported. - if (entry.second->IsAnimating()) { - entry.second->AnimatingElementRemovedByCommit(); - } - deleted.push_back(element_id); + std::vector<ElementId> deleted; + for (auto& entry : element_to_animation_map_) { + ElementId element_id = entry.first; + if (!animation_host_->IsElementInPropertyTrees(element_id, + commits_to_active)) { + // We probably shouldn't need to check IsAnimating here, + // but some bots recycle AnimationHost between tests + // which seems to lead to referencing Animations with null + // KeyframeModels. + if (entry.second->IsAnimating()) { + entry.second->AnimatingElementRemovedByCommit(); } + deleted.push_back(element_id); } + } - for (auto& entry : deleted) { - element_to_animation_map_.erase(entry); - } - } else { - DCHECK(scroll_offset_animation_); - if (scroll_offset_animation_->IsAnimating()) { - if (!animation_host_->IsElementInPropertyTrees( - scroll_offset_animation_->GetElementId(), commits_to_active)) { - scroll_offset_animation_->AnimatingElementRemovedByCommit(); - } - } + for (auto& entry : deleted) { + element_to_animation_map_.erase(entry); } } bool ScrollOffsetAnimationsImpl::ElementHasImplOnlyScrollAnimation( ElementId element_id) const { - if (features::MultiImplOnlyScrollAnimationsSupported()) { - ScrollOffsetAnimationImpl* impl_animation = GetScrollAnimation(element_id); - return impl_animation && impl_animation->IsAnimating(); - } else { - DCHECK(scroll_offset_animation_); - return scroll_offset_animation_->GetElementId() == element_id && - scroll_offset_animation_->IsAnimating(); - } + ScrollOffsetAnimationImpl* impl_animation = GetScrollAnimation(element_id); + return impl_animation && impl_animation->IsAnimating(); } bool ScrollOffsetAnimationsImpl::HasImplOnlyScrollAnimatingElement() const { - if (features::MultiImplOnlyScrollAnimationsSupported()) { - for (auto& entry : element_to_animation_map_) { - if (entry.second->IsAnimating()) { - return true; - } + for (auto& entry : element_to_animation_map_) { + if (entry.second->IsAnimating()) { + return true; } - return false; - } else { - DCHECK(scroll_offset_animation_); - return scroll_offset_animation_ && scroll_offset_animation_->IsAnimating(); } + return false; } bool ScrollOffsetAnimationsImpl::HasImplOnlyAutoScrollAnimatingElement() const { - if (features::MultiImplOnlyScrollAnimationsSupported()) { - for (auto& entry : element_to_animation_map_) { - if (entry.second->IsAutoScrolling()) { - return true; - } + for (auto& entry : element_to_animation_map_) { + if (entry.second->IsAutoScrolling()) { + return true; } - return false; - } else { - DCHECK(scroll_offset_animation_); - return scroll_offset_animation_ && - scroll_offset_animation_->IsAutoScrolling(); } -} - -ElementId ScrollOffsetAnimationsImpl::GetElementId() const { - DCHECK(!features::MultiImplOnlyScrollAnimationsSupported()); - return scroll_offset_animation_->GetElementId(); + return false; } ScrollOffsetAnimationImpl* ScrollOffsetAnimationsImpl::GetScrollAnimation( ElementId element_id) const { - DCHECK(features::MultiImplOnlyScrollAnimationsSupported()); auto iter = element_to_animation_map_.find(element_id); if (iter != element_to_animation_map_.end()) { return iter->second.get();
diff --git a/cc/animation/scroll_offset_animations_impl.h b/cc/animation/scroll_offset_animations_impl.h index e33d5e2..4007654 100644 --- a/cc/animation/scroll_offset_animations_impl.h +++ b/cc/animation/scroll_offset_animations_impl.h
@@ -154,23 +154,16 @@ bool IsAnimating() const; bool IsAutoScrolling() const; - ElementId GetElementId() const; private: // This retrieves the ScrollOffsetAnimationImpl object associated with the - // given ElementId. It is only used when MultiImplScrollAnimations is enabled. + // given ElementId. ScrollOffsetAnimationImpl* GetScrollAnimation(ElementId element_id) const; raw_ptr<AnimationHost> animation_host_; - // We have just one animation for impl-only scroll offset animations. - // I.e. only one element can have an impl-only scroll offset animation at - // any given time. - std::unique_ptr<ScrollOffsetAnimationImpl> scroll_offset_animation_; - // This maps each animating scroll container's ElementId to a - // ScrollOffsetAnimationImpl object. It is only used when - // MultiImplScrollAnimations is enabled. + // ScrollOffsetAnimationImpl object.. base::flat_map<ElementId, std::unique_ptr<ScrollOffsetAnimationImpl>> element_to_animation_map_; };
diff --git a/cc/base/features.cc b/cc/base/features.cc index 55b84cd..54b1a49 100644 --- a/cc/base/features.cc +++ b/cc/base/features.cc
@@ -157,14 +157,6 @@ "AllowLCDTextWithFilter", base::FEATURE_ENABLED_BY_DEFAULT); -BASE_FEATURE(kMultipleImplOnlyScrollAnimations, - "MultipleImplOnlyScrollAnimations", - base::FEATURE_ENABLED_BY_DEFAULT); -bool MultiImplOnlyScrollAnimationsSupported() { - return base::FeatureList::IsEnabled( - features::kMultipleImplOnlyScrollAnimations); -} - BASE_FEATURE(kRenderSurfacePixelAlignment, "RenderSurfacePixelAlignment", base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/cc/base/features.h b/cc/base/features.h index fa39b8a..340827c 100644 --- a/cc/base/features.h +++ b/cc/base/features.h
@@ -152,10 +152,6 @@ // Killswitch M135. CC_BASE_EXPORT BASE_DECLARE_FEATURE(kAllowLCDTextWithFilter); -// When enabled, impl-only scroll animations may execute concurrently. -CC_BASE_EXPORT BASE_DECLARE_FEATURE(kMultipleImplOnlyScrollAnimations); -CC_BASE_EXPORT extern bool MultiImplOnlyScrollAnimationsSupported(); - // When enabled, for a render surface with fractional translation, we'll try to // align the texels in the render surface to screen pixels to avoid blurriness // during compositing.
diff --git a/cc/input/input_handler.cc b/cc/input/input_handler.cc index 7735dc5..e3297826 100644 --- a/cc/input/input_handler.cc +++ b/cc/input/input_handler.cc
@@ -546,7 +546,6 @@ if (scroll_node && scroll_node != latched_node) { // This call to ScrollEnd marks the end of a snap animation on a ScrollNode // we are no longer latched to. - DCHECK(features::MultiImplOnlyScrollAnimationsSupported()); DCHECK(!should_snap); InsertPendingScrollendContainer(scroll_node->element_id); @@ -579,10 +578,7 @@ // Only indicate that the scroll gesture ended if scrolling actually // occurred so that we don't fire a "scrollend" event. if (did_scroll_x_for_scroll_gesture_ || did_scroll_y_for_scroll_gesture_) { - scroll_gesture_did_end_ = true; - if (features::MultiImplOnlyScrollAnimationsSupported()) { - InsertPendingScrollendContainer(latched_node->element_id); - } + InsertPendingScrollendContainer(latched_node->element_id); } end_of_scroll_cleanup(); @@ -827,7 +823,7 @@ auto* node = CurrentlyScrollingNode(); if (node && GetViewport().ShouldScroll(*node)) { return true; - } else if (features::MultiImplOnlyScrollAnimationsSupported()) { + } else { // In the snap phase of a scroll gesture, InputHandler will de-latch from // from the snapping ScrollNode (which, for viewport scrolls, is recorded as // outer viewport scrolls). While this animation is ongoing, we consider @@ -907,8 +903,7 @@ IsScrolledBy(layer_impl, currently_scroll_node)) { return InputHandler::TouchStartOrMoveEventListenerType:: kHandlerOnScrollingLayer; - } else if (features::MultiImplOnlyScrollAnimationsSupported() && - !snap_animation_data_map_.empty()) { + } else if (!snap_animation_data_map_.empty()) { // In the snap phase of a scroll gesture on a snap container, InputHandler // will de-latch from from the snapping ScrollNode. While this animation is // ongoing, we consider InputHandler to still be scrolling the node, despite @@ -1158,9 +1153,6 @@ has_pinch_zoomed_ = false; has_scrolled_by_scrollbar_ = false; - commit_data->scroll_end_data.scroll_gesture_did_end = scroll_gesture_did_end_; - scroll_gesture_did_end_ = false; - commit_data->overscroll_delta = overscroll_delta_for_main_thread_; overscroll_delta_for_main_thread_ = gfx::Vector2dF(); @@ -1174,21 +1166,11 @@ // TODO(bokan): This is wrong - if we also started a scroll this frame then // this will clear this value for that scroll. https://crbug.com/1116780. commit_data->scroll_latched_element_id = last_latched_scroller_; - if (features::MultiImplOnlyScrollAnimationsSupported()) { - commit_data->scroll_end_data.done_containers = - std::move(pending_scrollend_containers_); - if (commit_data->scroll_end_data.done_containers.contains( - last_latched_scroller_)) { - last_latched_scroller_ = ElementId(); - } - } else if (commit_data->scroll_end_data.scroll_gesture_did_end) { + commit_data->scroll_end_data.done_containers = + std::move(pending_scrollend_containers_); + if (commit_data->scroll_end_data.done_containers.contains( + last_latched_scroller_)) { last_latched_scroller_ = ElementId(); - commit_data->scroll_end_data.gesture_affects_outer_viewport_scroll = - outer_viewport_consumed_delta_; - outer_viewport_consumed_delta_ = false; - commit_data->scroll_end_data.gesture_affects_inner_viewport_scroll = - inner_viewport_consumed_delta_; - inner_viewport_consumed_delta_ = false; } } @@ -1287,16 +1269,11 @@ bool was_animating_for_snap = IsAnimatingForSnap(finished_node->element_id); ScrollNode* latched_node = CurrentlyScrollingNode(); - if (features::MultiImplOnlyScrollAnimationsSupported()) { - // With MultiImplOnlyScrollAnimationsSupported, the node that was animating - // might not be the currently scrolling node. - // The only instance in which we expect that the animating node is not the - // currently latched node is if this was a snap animation (during which we - // de-latch from the animating node). - DCHECK(finished_node == latched_node || was_animating_for_snap); - } else { - DCHECK(finished_node == latched_node); - } + // The node that was animating might not be the currently scrolling node. + // The only instance in which we expect that the animating node is not the + // currently latched node is if this was a snap animation (during which we + // de-latch from the animating node). + DCHECK(finished_node == latched_node || was_animating_for_snap); // ScrollOffsetAnimationFinished is called in two cases: // 1- smooth scrolling animation is over (IsAnimatingForSnap == false). @@ -1354,13 +1331,10 @@ // will de-latch from from the snapping ScrollNode. While this animation is // ongoing, we consider InputHandler to still be scrolling the node, despite // having de-latched from it. - if (features::MultiImplOnlyScrollAnimationsSupported()) { - for (const auto& entry : snap_animation_data_map_) { - // Empty targets means not snap-animating. - if (entry.second.animating_snap_target_ids_ != - TargetSnapAreaElementIds()) { - return true; - } + for (const auto& entry : snap_animation_data_map_) { + // Empty targets means not snap-animating. + if (entry.second.animating_snap_target_ids_ != TargetSnapAreaElementIds()) { + return true; } } @@ -1377,16 +1351,13 @@ return ActivelyScrollingType::kPrecise; } - if (features::MultiImplOnlyScrollAnimationsSupported()) { - // In the snap phase of a scroll gesture on a snap container, InputHandler - // will de-latch from from the snapping ScrollNode. While this animation is - // ongoing, we consider InputHandler to still be scrolling the node, despite - // having de-latched from it. - for (const auto& entry : snap_animation_data_map_) { - if (entry.second.animating_snap_target_ids_ != - TargetSnapAreaElementIds()) { - return ActivelyScrollingType::kAnimated; - } + // In the snap phase of a scroll gesture on a snap container, InputHandler + // will de-latch from from the snapping ScrollNode. While this animation is + // ongoing, we consider InputHandler to still be scrolling the node, despite + // having de-latched from it. + for (const auto& entry : snap_animation_data_map_) { + if (entry.second.animating_snap_target_ids_ != TargetSnapAreaElementIds()) { + return ActivelyScrollingType::kAnimated; } } @@ -1407,23 +1378,20 @@ } } - if (features::MultiImplOnlyScrollAnimationsSupported()) { - // Ensure InputHandler factors in snap animations (during which - // InputHandler de-latches from the ScrollNode) when queried about - // nodes it's scrolling which require main thread repaints. - const auto& scroll_tree = GetScrollTree(); - for (const auto& entry : snap_animation_data_map_) { - if (entry.second.animating_snap_target_ids_ == - TargetSnapAreaElementIds()) { - continue; - } - if (const ScrollNode* animating_node = - scroll_tree.FindNodeFromElementId(entry.first)) { - uint32_t repaint_reasons = - GetScrollTree().GetMainThreadRepaintReasons(*animating_node); - if (repaint_reasons != MainThreadScrollingReason::kNotScrollingOnMain) { - return true; - } + // Ensure InputHandler factors in snap animations (during which + // InputHandler de-latches from the ScrollNode) when queried about + // nodes it's scrolling which require main thread repaints. + const auto& scroll_tree = GetScrollTree(); + for (const auto& entry : snap_animation_data_map_) { + if (entry.second.animating_snap_target_ids_ == TargetSnapAreaElementIds()) { + continue; + } + if (const ScrollNode* animating_node = + scroll_tree.FindNodeFromElementId(entry.first)) { + uint32_t repaint_reasons = + GetScrollTree().GetMainThreadRepaintReasons(*animating_node); + if (repaint_reasons != MainThreadScrollingReason::kNotScrollingOnMain) { + return true; } } } @@ -2297,12 +2265,11 @@ } DCHECK(!IsAnimatingForSnap(CurrentlyScrollingNode()->element_id)); if (did_animate) { - if (features::MultiImplOnlyScrollAnimationsSupported()) { - // Forget the scroll container that is currently - // latched so that any scroll gesture that occurs during the snap - // animation will be allowed to scroll the appropriate container. - ClearCurrentlyScrollingNode(); - } + // Forget the scroll container that is currently + // latched so that any scroll gesture that occurs during the snap + // animation will be allowed to scroll the appropriate container. + ClearCurrentlyScrollingNode(); + EnsureSnapAnimationData(scroll_node->element_id); // The updated snap target will be set when the animation is completed. SetAnimatingSnapTargetsForElement(scroll_node->element_id, @@ -2450,22 +2417,19 @@ return true; } - if (features::MultiImplOnlyScrollAnimationsSupported()) { - // Ensure InputHandler factors in snap animations (during which - // InputHandler de-latches from the ScrollNode) when queried about - // nodes it's scrolling which need frame alignment. - const auto& scroll_tree = GetScrollTree(); - for (const auto& entry : snap_animation_data_map_) { - if (entry.second.animating_snap_target_ids_ == - TargetSnapAreaElementIds()) { - continue; - } - if (const ScrollNode* animating_node = - scroll_tree.FindNodeFromElementId(entry.first)) { - if (compositor_delegate_->HasScrollLinkedAnimation( - animating_node->element_id)) { - return true; - } + // Ensure InputHandler factors in snap animations (during which + // InputHandler de-latches from the ScrollNode) when queried about + // nodes it's scrolling which need frame alignment. + const auto& scroll_tree = GetScrollTree(); + for (const auto& entry : snap_animation_data_map_) { + if (entry.second.animating_snap_target_ids_ == TargetSnapAreaElementIds()) { + continue; + } + if (const ScrollNode* animating_node = + scroll_tree.FindNodeFromElementId(entry.first)) { + if (compositor_delegate_->HasScrollLinkedAnimation( + animating_node->element_id)) { + return true; } } } @@ -2511,27 +2475,19 @@ TargetSnapAreaElementIds InputHandler::GetAnimatingSnapTargetsForElement( ElementId element_id) const { - if (features::MultiImplOnlyScrollAnimationsSupported()) { - auto entry = snap_animation_data_map_.find(element_id); - if (entry != snap_animation_data_map_.end()) { - return entry->second.animating_snap_target_ids_; - } - return TargetSnapAreaElementIds(); - } else { - return scroll_animating_snap_target_ids_; + auto entry = snap_animation_data_map_.find(element_id); + if (entry != snap_animation_data_map_.end()) { + return entry->second.animating_snap_target_ids_; } + return TargetSnapAreaElementIds(); } void InputHandler::SetAnimatingSnapTargetsForElement( ElementId element_id, TargetSnapAreaElementIds target_ids) { - if (features::MultiImplOnlyScrollAnimationsSupported()) { - auto entry = snap_animation_data_map_.find(element_id); - if (entry != snap_animation_data_map_.end()) { - entry->second.animating_snap_target_ids_ = target_ids; - } - } else { - scroll_animating_snap_target_ids_ = target_ids; + auto entry = snap_animation_data_map_.find(element_id); + if (entry != snap_animation_data_map_.end()) { + entry->second.animating_snap_target_ids_ = target_ids; } } @@ -2540,11 +2496,8 @@ } void InputHandler::EnsureSnapAnimationData(ElementId element_id) { - if (features::MultiImplOnlyScrollAnimationsSupported()) { - if (!snap_animation_data_map_.contains(element_id)) { - snap_animation_data_map_.insert_or_assign(element_id, - SnapAnimationData()); - } + if (!snap_animation_data_map_.contains(element_id)) { + snap_animation_data_map_.insert_or_assign(element_id, SnapAnimationData()); } }
diff --git a/cc/input/input_handler.h b/cc/input/input_handler.h index 13609c5..1a2f6c51 100644 --- a/cc/input/input_handler.h +++ b/cc/input/input_handler.h
@@ -825,11 +825,6 @@ // over. bool deferred_scroll_end_ = false; - // Set to true when a scroll gesture being handled on the compositor has - // ended. i.e. When a GSE has arrived and any ongoing scroll animation has - // ended. - bool scroll_gesture_did_end_ = false; - // True iff some of the delta has been consumed for the current scroll // sequence on the specific axis. bool did_scroll_x_for_scroll_gesture_ = false;
diff --git a/cc/mojom/layer_selection_bound_mojom_traits.cc b/cc/mojom/layer_selection_bound_mojom_traits.cc index 14a93b3..4502d9e5 100644 --- a/cc/mojom/layer_selection_bound_mojom_traits.cc +++ b/cc/mojom/layer_selection_bound_mojom_traits.cc
@@ -11,11 +11,10 @@ cc::mojom::LayerSelectionBoundDataView, cc::LayerSelectionBound>::Read(cc::mojom::LayerSelectionBoundDataView data, cc::LayerSelectionBound* out) { - if (!data.ReadEdgeStart(&out->edge_start) || + if (!data.ReadType(&out->type) || !data.ReadEdgeStart(&out->edge_start) || !data.ReadEdgeEnd(&out->edge_end)) { return false; } - out->type = MojoSelectionBoundTypeToGfx(data.type()); out->layer_id = data.layer_id(); out->hidden = data.hidden(); return true;
diff --git a/cc/mojom/layer_selection_bound_mojom_traits.h b/cc/mojom/layer_selection_bound_mojom_traits.h index fb61faf..f806abc 100644 --- a/cc/mojom/layer_selection_bound_mojom_traits.h +++ b/cc/mojom/layer_selection_bound_mojom_traits.h
@@ -15,9 +15,8 @@ template <> struct StructTraits<cc::mojom::LayerSelectionBoundDataView, cc::LayerSelectionBound> { - static gfx::mojom::SelectionBoundType type( - const cc::LayerSelectionBound& bound) { - return GfxSelectionBoundTypeToMojo(bound.type); + static gfx::SelectionBound::Type type(const cc::LayerSelectionBound& bound) { + return bound.type; } static const gfx::Point& edge_start(const cc::LayerSelectionBound& bound) {
diff --git a/cc/trees/compositor_commit_data.h b/cc/trees/compositor_commit_data.h index 1ce658e..3544944 100644 --- a/cc/trees/compositor_commit_data.h +++ b/cc/trees/compositor_commit_data.h
@@ -100,18 +100,6 @@ struct ScrollEndInfo { ScrollEndInfo(); ~ScrollEndInfo(); - // Set to true when a scroll gesture being handled on the compositor has - // ended. - // TODO(crbug.com/372627916): This is not used when - // MultiImplOnlyScrollAnimations is enabled. Remove it when deleting the old - // code path. - bool scroll_gesture_did_end = false; - - // TODO(crbug.com/372627916): These are not used when - // MultiImplOnlyScrollAnimations is enabled. Remove them when deleting the - // old code path. - bool gesture_affects_outer_viewport_scroll = false; - bool gesture_affects_inner_viewport_scroll = false; // The set of containers for which an impl scroll has ended between this // commit and the last.
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc index 6514f7c..a33b4f80 100644 --- a/cc/trees/layer_tree_host.cc +++ b/cc/trees/layer_tree_host.cc
@@ -1051,7 +1051,7 @@ commit_data.elastic_overscroll_delta.IsZero() && !commit_data.top_controls_delta && !commit_data.bottom_controls_delta && !commit_data.browser_controls_constraint_changed && - !commit_data.scroll_end_data.scroll_gesture_did_end && + commit_data.scroll_end_data.done_containers.empty() && commit_data.is_pinch_gesture_active == is_pinch_gesture_active_from_impl_) { return; @@ -1078,7 +1078,7 @@ commit_data.page_scale_delta, commit_data.is_pinch_gesture_active, commit_data.top_controls_delta, commit_data.bottom_controls_delta, commit_data.browser_controls_constraint, - commit_data.scroll_end_data.scroll_gesture_did_end}); + !commit_data.scroll_end_data.done_containers.empty()}); SetNeedsUpdateLayers(); }
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc index 6add5b5..60ce1a7 100644 --- a/cc/trees/layer_tree_host_impl_unittest.cc +++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -2890,81 +2890,6 @@ GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds()); } -TEST_P(LayerTreeHostImplTest, SnapAnimationTargetUpdated) { - // If MultiImplOnlyScrollAnimations is enabled this test is not valid because - // that flag makes InputHandler forget the latched ScrollNode at the beginning - // of the snap animation rather than at the end. And so, processing a - // ScrollUpdate (without a corresponding ScrollBegin) after the snap animation - // has kicked off is invalid. - // TODO(crbug.com/372627916): remove this test when deleting this flag. - if (features::MultiImplOnlyScrollAnimationsSupported()) { - return; - } - LayerImpl* overflow = CreateLayerForSnapping(); - - gfx::Point pointer_position(10, 10); - gfx::Vector2dF y_delta(0, 20); - EXPECT_EQ(ScrollThread::kScrollOnImplThread, - GetInputHandler() - .ScrollBegin(BeginState(pointer_position, y_delta, - ui::ScrollInputType::kWheel) - .get(), - ui::ScrollInputType::kWheel) - .thread); - EXPECT_POINTF_EQ(gfx::PointF(0, 0), CurrentScrollOffset(overflow)); - - GetInputHandler().ScrollUpdate( - UpdateState(pointer_position, y_delta, ui::ScrollInputType::kWheel)); - EXPECT_FALSE( - GetInputHandler().animating_for_snap_for_testing(overflow->element_id())); - - viz::BeginFrameArgs begin_frame_args = - viz::CreateBeginFrameArgsForTesting(BEGINFRAME_FROM_HERE, 0, 1); - GetInputHandler().ScrollEnd(true); - base::TimeTicks start_time = base::TimeTicks() + base::Milliseconds(100); - BeginImplFrameAndAnimate(begin_frame_args, start_time); - - // Finish smooth wheel scroll animation which starts a snap animation. - BeginImplFrameAndAnimate(begin_frame_args, - start_time + base::Milliseconds(100)); - EXPECT_TRUE( - GetInputHandler().animating_for_snap_for_testing(overflow->element_id())); - EXPECT_EQ(TargetSnapAreaElementIds(), - GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds()); - - gfx::PointF current_offset = CurrentScrollOffset(overflow); - EXPECT_GT(50, current_offset.y()); - EXPECT_LT(20, current_offset.y()); - - // Update wheel scroll animation target. This should no longer be considered - // as animating a snap scroll, which should happen at the end of this - // animation. - GetInputHandler().ScrollUpdate( - AnimatedUpdateState(gfx::Point(10, 10), gfx::Vector2dF(0, -10))); - EXPECT_FALSE( - GetInputHandler().animating_for_snap_for_testing(overflow->element_id())); - // Finish the smooth scroll animation for wheel. - BeginImplFrameAndAnimate(begin_frame_args, - start_time + base::Milliseconds(150)); - - // At the end of the previous scroll animation, a new animation for the - // snapping should have started. - EXPECT_TRUE( - GetInputHandler().animating_for_snap_for_testing(overflow->element_id())); - - // Finish the snap animation. - BeginImplFrameAndAnimate(begin_frame_args, - start_time + base::Milliseconds(1000)); - - EXPECT_FALSE( - GetInputHandler().animating_for_snap_for_testing(overflow->element_id())); - // At the end of snap animation we should have updated the - // TargetSnapAreaElementIds. - EXPECT_EQ(TargetSnapAreaElementIds(ElementId(), ElementId(10)), - GetSnapContainerData(overflow)->GetTargetSnapAreaElementIds()); - EXPECT_POINTF_EQ(gfx::PointF(0, 50), CurrentScrollOffset(overflow)); -} - TEST_P(LayerTreeHostImplTest, SnapAnimationCancelledByScroll) { LayerImpl* overflow = CreateLayerForSnapping(); @@ -19391,11 +19316,6 @@ class ConcurrentImplOnlyScrollAnimationsTest : public LayerTreeHostImplTest { public: - void SetUp() override { - scoped_feature_list_.InitAndEnableFeature( - features::kMultipleImplOnlyScrollAnimations); - LayerTreeHostImplTest::SetUp(); - } gfx::PointF CreateAndTickScrollAnimations(); void CompleteScrollAnimations(); @@ -19544,8 +19464,6 @@ class ConcurrentSnapAnimationsTest : public LayerTreeHostImplTest { public: void SetUp() override { - scoped_feature_list_.InitAndEnableFeature( - features::kMultipleImplOnlyScrollAnimations); LayerTreeHostImplTest::SetUp(); gfx::Size viewport_size(100, 100); gfx::Size content_size(100, 5000);
diff --git a/chrome/android/expectations/trichrome_chrome_64_32_bundle__chrome.AndroidManifest.expected b/chrome/android/expectations/trichrome_chrome_64_32_bundle__chrome.AndroidManifest.expected index 178af93..320e44b 100644 --- a/chrome/android/expectations/trichrome_chrome_64_32_bundle__chrome.AndroidManifest.expected +++ b/chrome/android/expectations/trichrome_chrome_64_32_bundle__chrome.AndroidManifest.expected
@@ -265,7 +265,7 @@ android:excludeFromRecents="true" android:label="@string/fre_activity_label" android:launchMode="singleInstance" - android:theme="@style/Theme.BrowserUI.AlertDialog.NoActionBar.DayNight" + android:theme="@style/Theme.BrowserUI.DayNight.AlertDialog.NoActionBar" android:windowSoftInputMode="stateHidden|adjustPan"> </activity> # DIFF-ANCHOR: 67932092 <activity # DIFF-ANCHOR: 05911131
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupSuggestionMessageService.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupSuggestionMessageService.java index 6b99c43e..b5b8951c 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupSuggestionMessageService.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupSuggestionMessageService.java
@@ -24,6 +24,7 @@ import org.chromium.chrome.browser.tasks.tab_management.TabSwitcherMessageManager.MessageUpdateObserver; import org.chromium.chrome.tab_ui.R; +import java.util.ArrayList; import java.util.List; /** @@ -33,6 +34,22 @@ @NullMarked public class TabGroupSuggestionMessageService extends MessageService implements MessageUpdateObserver { + /** Callback to start the merge animation which runs upon accepting a suggestion. */ + @FunctionalInterface + public interface StartMergeAnimation { + /** + * Starts the merge animation. + * + * @param targetTabId The tab that will serve as the destination for the other tabs. + * @param tabIdsToShift The tab IDs for the tabs that will merge into the target tab. + * @param onAnimationEnd Executed after the animation has finished. + */ + void start( + @TabId int targetTabId, + List<@TabId Integer> tabIdsToShift, + Runnable onAnimationEnd); + } + /** This is the data type that this MessageService is serving to its Observer. */ public static class TabGroupSuggestionMessageData implements MessageData { private final int mNumTabs; @@ -88,6 +105,7 @@ mCurrentTabGroupModelFilterSupplier; private final Callback<@TabId Integer> mAddOnMessageAfterTabCallback; + private final StartMergeAnimation mStartMergeAnimation; private boolean mMessageCurrentlyShown; /** @@ -95,15 +113,18 @@ * @param currentTabGroupModelFilterSupplier The supplier for the current {@link * TabGroupModelFilter}. * @param onMessageAfterTabCallback A callback to be called to add a message after a tab. + * @param startMergeAnimation A callback used to start the merge animation. */ public TabGroupSuggestionMessageService( Context context, ObservableSupplier<@Nullable TabGroupModelFilter> currentTabGroupModelFilterSupplier, - Callback<@TabId Integer> onMessageAfterTabCallback) { + Callback<@TabId Integer> onMessageAfterTabCallback, + StartMergeAnimation startMergeAnimation) { super(MessageType.TAB_GROUP_SUGGESTION_MESSAGE); mContext = context; mCurrentTabGroupModelFilterSupplier = currentTabGroupModelFilterSupplier; mAddOnMessageAfterTabCallback = onMessageAfterTabCallback; + mStartMergeAnimation = startMergeAnimation; } /** @@ -135,10 +156,7 @@ new TabGroupSuggestionMessageData( tabIdsSortedByIndex.size(), mContext, - () -> - groupTabs( - tabIdsSortedByIndex, - responseListener::onSuggestionAccepted), + () -> onAcceptMessage(tabIdsSortedByIndex, responseListener), ignored -> dismissMessage(responseListener::onSuggestionDismissed)); sendAvailabilityNotification(data); mMessageCurrentlyShown = true; @@ -147,6 +165,21 @@ mAddOnMessageAfterTabCallback.onResult(lastTabId); } + private void onAcceptMessage( + List<@TabId Integer> tabIdsSortedByIndex, + SuggestionLifecycleObserver responseListener) { + Runnable onAnimationEnd = + () -> groupTabs(tabIdsSortedByIndex, responseListener::onSuggestionAccepted); + + int numTabs = tabIdsSortedByIndex.size(); + List<@TabId Integer> shiftedTabIds = new ArrayList<>(numTabs); + shiftedTabIds.addAll(tabIdsSortedByIndex.subList(1, numTabs)); + + if (numTabs > 1) { + mStartMergeAnimation.start(tabIdsSortedByIndex.get(0), shiftedTabIds, onAnimationEnd); + } + } + private void groupTabs(List<@TabId Integer> tabIds, Runnable onAcceptMessageListener) { assert !tabIds.isEmpty();
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupSuggestionMessageServiceUnitTest.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupSuggestionMessageServiceUnitTest.java index f3cf2fe4..cd954a2 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupSuggestionMessageServiceUnitTest.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabGroupSuggestionMessageServiceUnitTest.java
@@ -80,7 +80,8 @@ new TabGroupSuggestionMessageService( mContext, tabGroupModelFilterSupplier, - mAddOnMessageAfterTabCallback)); + mAddOnMessageAfterTabCallback, + (a, b, c) -> c.run())); when(mContext.getString(R.string.tab_group_suggestion_message, 2)) .thenReturn("Group 2 tabs?");
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java index de5a61d..89facb96 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListCoordinator.java
@@ -6,6 +6,8 @@ import static org.chromium.build.NullUtil.assumeNonNull; import static org.chromium.chrome.browser.tasks.tab_management.TabListModel.CardProperties.CARD_TYPE; +import static org.chromium.chrome.browser.tasks.tab_management.TabListModel.CardProperties.ModelType.TAB; +import static org.chromium.chrome.browser.tasks.tab_management.TabProperties.TAB_ID; import android.app.Activity; import android.graphics.PointF; @@ -64,6 +66,7 @@ import org.chromium.ui.base.ViewUtils; import org.chromium.ui.modaldialog.ModalDialogManager; import org.chromium.ui.modelutil.MVCListAdapter; +import org.chromium.ui.modelutil.MVCListAdapter.ListItem; import org.chromium.ui.modelutil.PropertyKey; import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.modelutil.PropertyModelChangeProcessor; @@ -73,7 +76,10 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; /** Coordinator for showing UI for a list of tabs. Can be used in GRID or STRIP modes. */ @NullMarked @@ -140,6 +146,7 @@ private final ObservableSupplier<@Nullable TabGroupModelFilter> mTabGroupModelFilterSupplier; private final ObserverList<DragObserver> mDragObserverList = new ObserverList<>(); private final TabListHighlighter mTabListHighlighter; + private final TabListMergeAnimationManager mTabListMergeAnimationManager; private boolean mIsInitialized; private @Nullable OnLayoutChangeListener mListLayoutListener; @@ -451,7 +458,7 @@ parentView, mModelList, this::runOnItemAnimatorFinished); } mTabListHighlighter = new TabListHighlighter(mModelList); - + mTabListMergeAnimationManager = new TabListMergeAnimationManager(mRecyclerView); configureRecyclerViewTouchHelpers(); } @@ -879,7 +886,7 @@ } /** - * Inserts a special {@link MVCListAdapter.ListItem} at given index of the model list. + * Inserts a special {@link ListItem} at given index of the model list. * * @see TabListMediator#addSpecialItemToModel(int, int, PropertyModel). */ @@ -888,20 +895,20 @@ } /** - * Removes a special {@link MVCListAdapter.ListItem} that has the given {@code uiType} and/or - * its {@link PropertyModel} has the given {@code itemIdentifier}. + * Removes a special {@link ListItem} that has the given {@code uiType} and/or its {@link + * PropertyModel} has the given {@code itemIdentifier}. * * @param uiType The uiType to match. * @param itemIdentifier The itemIdentifier to match. This can be obsoleted if the {@link - * org.chromium.ui.modelutil.MVCListAdapter.ListItem} does not need additional identifier. + * ListItem} does not need additional identifier. */ void removeSpecialListItem(@UiType int uiType, int itemIdentifier) { mMediator.removeSpecialItemFromModelList(uiType, itemIdentifier); } /** - * Removes a {@link MVCListAdapter.ListItem} that has the given {@code uiType} and the {@link - * PropertyModel} has the given {@link TabListEditorItemSelectionId}. + * Removes a {@link ListItem} that has the given {@code uiType} and the {@link PropertyModel} + * has the given {@link TabListEditorItemSelectionId}. * * @param uiType The uiType to match. * @param itemId The itemId to match. @@ -922,7 +929,7 @@ // PriceWelcomeMessageService.PriceWelcomeMessageProvider implementation. @Override - public int getTabIndexFromTabId(int tabId) { + public int getTabIndexFromTabId(@TabId int tabId) { return mModelList.indexFromTabId(tabId); } @@ -931,6 +938,25 @@ mModelList.get(index).model.set(TabProperties.SHOULD_SHOW_PRICE_DROP_TOOLTIP, true); } + /** + * Converts a list of tab IDs into a list of their corresponding indexes within the model list. + * Note that this method does not account for tabs in tab groups. + * + * @param tabIds A list of tab IDs to convert. + */ + public List<Integer> getCardIndexesFromTabIds(List<@TabId Integer> tabIds) { + Set<@TabId Integer> tabIdSet = new HashSet<>(tabIds); + List<Integer> indexes = new ArrayList<>(); + for (int i = 0; i < mModelList.size(); i++) { + PropertyModel model = mModelList.get(i).model; + if (model.get(CARD_TYPE) == TAB && tabIdSet.contains(model.get(TAB_ID))) { + indexes.add(i); + } + } + assert tabIds.size() == indexes.size(); + return indexes; + } + int getIndexOfNthTabCard(int index) { return mModelList.indexOfNthTabCardOrInvalid(index); } @@ -1040,6 +1066,25 @@ }); } + /** + * Triggers an animation where a set of tabs merge into a single target tab. + * + * @param targetIndex The model index of the tab that other tabs will merge into. + * @param visibleTabIndexes The model indexes for all tabs that will be merged into the target + * tab. + * @param onAnimationEnd Executed after the merge animation has finished. + */ + public void triggerMergeAnimation( + int targetIndex, List<Integer> visibleTabIndexes, Runnable onAnimationEnd) { + Runnable wrappedOnAnimationEnd = + () -> { + onAnimationEnd.run(); + configureRecyclerViewTouchHelpers(); + }; + mTabListMergeAnimationManager.playAnimation( + targetIndex, visibleTabIndexes, wrappedOnAnimationEnd); + } + /** Returns the coordinator that manages the overflow menu for tab group cards in the GTS. */ public TabListGroupMenuCoordinator getTabListGroupMenuCoordinator() { return mMediator.getTabListGroupMenuCoordinator();
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMergeAnimationManager.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMergeAnimationManager.java new file mode 100644 index 0000000..e89258d --- /dev/null +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMergeAnimationManager.java
@@ -0,0 +1,204 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser.tasks.tab_management; + +import static org.chromium.chrome.browser.tasks.tab_management.TabProperties.IS_HIGHLIGHTED; +import static org.chromium.ui.animation.AnimationListeners.onAnimationEnd; + +import android.animation.Animator; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.graphics.Rect; +import android.view.View; + +import androidx.recyclerview.widget.LinearSmoothScroller; +import androidx.recyclerview.widget.RecyclerView; +import androidx.recyclerview.widget.RecyclerView.OnScrollListener; + +import org.chromium.build.annotations.NullMarked; +import org.chromium.ui.animation.AnimationHandler; +import org.chromium.ui.interpolators.Interpolators; +import org.chromium.ui.modelutil.PropertyModel; +import org.chromium.ui.modelutil.SimpleRecyclerViewAdapter; + +import java.util.ArrayList; +import java.util.List; + +@NullMarked +public class TabListMergeAnimationManager { + private static final long MERGE_ANIMATION_DURATION_MS = 300L; + private final AnimationHandler mMergeAnimationHandler = new AnimationHandler(); + private final TabListRecyclerView mRecyclerView; + private boolean mIsAnimating; + + /** + * Plays an animation where all visible tab cards fade out and move towards a target tab card. + * + * @param recyclerView The {@link TabListRecyclerView} to run the animations on. + */ + public TabListMergeAnimationManager(TabListRecyclerView recyclerView) { + mRecyclerView = recyclerView; + } + + /** + * Plays an animation where all visible tab cards fade out and move towards a target tab card. + * + * @param targetIndex The model index of the tab card to focus on. + * @param visibleTabIndexes A list of model indexes for all currently visible tab cards. + * @param onAnimationEnd A {@link Runnable} to execute when the animation is complete. + */ + public void playAnimation( + int targetIndex, List<Integer> visibleTabIndexes, Runnable onAnimationEnd) { + if (mIsAnimating) return; + mIsAnimating = true; + mRecyclerView.setBlockTouchInput(true); + mRecyclerView.setSmoothScrolling(true); + + boolean isTargetVisible = isTargetFullyVisible(targetIndex); + + // If the target is already visible, don't scroll. + if (isTargetVisible) { + postAnimationToRecyclerView(targetIndex, visibleTabIndexes, onAnimationEnd); + return; + } + + // If the target is not visible, scroll to it before running the animation. + smoothScrollAndAnimate(targetIndex, visibleTabIndexes, onAnimationEnd); + } + + private void smoothScrollAndAnimate( + int targetIndex, List<Integer> visibleTabIndexes, Runnable onAnimationEnd) { + LinearSmoothScroller smoothScroller = + new LinearSmoothScroller(mRecyclerView.getContext()) { + @Override + protected int getVerticalSnapPreference() { + return LinearSmoothScroller.SNAP_TO_START; + } + }; + smoothScroller.setTargetPosition(targetIndex); + mRecyclerView.smoothScrollToPosition(targetIndex); + mRecyclerView.addOnScrollListener( + new OnScrollListener() { + @Override + public void onScrollStateChanged(RecyclerView recyclerView, int newState) { + super.onScrollStateChanged(recyclerView, newState); + onStateChanged( + this, newState, targetIndex, visibleTabIndexes, onAnimationEnd); + } + }); + } + + private boolean isTargetFullyVisible(int targetIndex) { + RecyclerView.ViewHolder viewHolder = + mRecyclerView.findViewHolderForAdapterPosition(targetIndex); + if (viewHolder == null) return false; + + View view = viewHolder.itemView; + if (!view.isShown()) return false; + + Rect actualPosition = new Rect(); + view.getGlobalVisibleRect(actualPosition); + + return view.getMeasuredHeight() == actualPosition.height(); + } + + private void onStateChanged( + OnScrollListener listener, + int newState, + int targetIndex, + List<Integer> visibleTabIndexes, + Runnable onAnimationEnd) { + if (newState == RecyclerView.SCROLL_STATE_IDLE) { + mRecyclerView.removeOnScrollListener(listener); + postAnimationToRecyclerView(targetIndex, visibleTabIndexes, onAnimationEnd); + } + } + + private void postAnimationToRecyclerView( + int targetIndex, List<Integer> visibleTabIndexes, Runnable onAnimationEnd) { + mRecyclerView.post( + () -> + animateMergeWith( + targetIndex, visibleTabIndexes, mRecyclerView, onAnimationEnd)); + } + + private void animateMergeWith( + int targetIndex, + List<Integer> visibleTabIndexes, + TabListRecyclerView recyclerView, + Runnable onAnimationEnd) { + RecyclerView.ViewHolder targetViewHolder = + recyclerView.findViewHolderForAdapterPosition(targetIndex); + if (targetViewHolder == null) { + cleanUp(onAnimationEnd); + return; + } + Animator animation = + buildMergeAnimation( + visibleTabIndexes, recyclerView, onAnimationEnd, targetViewHolder); + mMergeAnimationHandler.startAnimation(animation); + } + + private Animator buildMergeAnimation( + List<Integer> visibleTabIndexes, + TabListRecyclerView recyclerView, + Runnable onAnimationEnd, + RecyclerView.ViewHolder targetViewHolder) { + View targetView = targetViewHolder.itemView; + AnimatorSet animatorSet = new AnimatorSet(); + List<Animator> animators = new ArrayList<>(); + + for (int index : visibleTabIndexes) { + RecyclerView.ViewHolder viewHolder = + recyclerView.findViewHolderForAdapterPosition(index); + if (viewHolder == null) continue; + View view = viewHolder.itemView; + + animators.add(ObjectAnimator.ofFloat(view, View.ALPHA, 1f, 0f)); + + float targetTranslationX = targetView.getX() - view.getX(); + float targetTranslationY = targetView.getY() - view.getY(); + animators.add(ObjectAnimator.ofFloat(view, View.TRANSLATION_X, 0f, targetTranslationX)); + animators.add(ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, 0f, targetTranslationY)); + } + + animatorSet.playTogether(animators); + animatorSet.setDuration(MERGE_ANIMATION_DURATION_MS); + animatorSet.setInterpolator(Interpolators.ACCELERATE_INTERPOLATOR); + + animatorSet.addListener( + onAnimationEnd( + animator -> { + for (int index : visibleTabIndexes) { + cleanCardState(index); + } + cleanUp(onAnimationEnd); + })); + return animatorSet; + } + + private void cleanCardState(int index) { + SimpleRecyclerViewAdapter.ViewHolder viewHolder = + (SimpleRecyclerViewAdapter.ViewHolder) + mRecyclerView.findViewHolderForAdapterPosition(index); + if (viewHolder == null) return; + View view = viewHolder.itemView; + + view.setTranslationX(0f); + view.setTranslationY(0f); + + PropertyModel model = viewHolder.model; + if (model != null && model.containsKeyEqualTo(IS_HIGHLIGHTED, true)) { + model.set(IS_HIGHLIGHTED, false); + } + } + + private void cleanUp(Runnable onAnimationEnd) { + mRecyclerView.setBlockTouchInput(false); + mRecyclerView.setSmoothScrolling(false); + onAnimationEnd.run(); + mIsAnimating = false; + } +}
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java index 8483a033..52b8eeb 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListRecyclerView.java
@@ -47,7 +47,9 @@ @NullMarked class TabListRecyclerView extends RecyclerView implements TabListMediator.TabGridAccessibilityHelper, RunOnNextLayout { + private static final float SMOOTH_SCROLL_SPEED_FACTOR = 0.8f; private boolean mBlockTouchInput; + private boolean mIsSmoothScrolling; // Null unless item animations are disabled. private RecyclerView.@Nullable ItemAnimator mDisabledAnimatorHolder; @@ -381,4 +383,16 @@ || action == R.id.move_tab_up || action == R.id.move_tab_down; } + + @Override + public boolean fling(int velocityX, int velocityY) { + if (mIsSmoothScrolling) { + velocityY = (int) (velocityY * SMOOTH_SCROLL_SPEED_FACTOR); + } + return super.fling(velocityX, velocityY); + } + + public void setSmoothScrolling(boolean isSmoothScrolling) { + mIsSmoothScrolling = isSmoothScrolling; + } }
diff --git a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMessageManager.java b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMessageManager.java index 7638de8..6e8c880 100644 --- a/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMessageManager.java +++ b/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabSwitcherMessageManager.java
@@ -358,7 +358,8 @@ new TabGroupSuggestionMessageService( mActivity, mCurrentTabGroupModelFilterSupplier, - this::addTabGroupSuggestionMessage); + this::addTabGroupSuggestionMessage, + this::translateStartMergeAnimation); mMessageCardProviderCoordinator.subscribeMessageService( mTabGroupSuggestionMessageService); } @@ -374,6 +375,24 @@ } /** + * Triggers an animation where a set of tabs merge into a single target tab. + * + * @param targetTabId The ID of the tab that other tabs will merge into. + * @param tabIdsToMerge The IDs for all tabs that will be merged into the target tab. + * @param onAnimationEnd Executed after the merge animation has finished. + */ + private void translateStartMergeAnimation( + @TabId int targetTabId, List<@TabId Integer> tabIdsToMerge, Runnable onAnimationEnd) { + if (!mTabListCoordinatorSupplier.hasValue()) return; + + TabListCoordinator tabListCoordinator = assumeNonNull(mTabListCoordinatorSupplier.get()); + int targetTabIndex = tabListCoordinator.getTabIndexFromTabId(targetTabId); + List<Integer> tabIndexesToMerge = + tabListCoordinator.getCardIndexesFromTabIds(tabIdsToMerge); + tabListCoordinator.triggerMergeAnimation(targetTabIndex, tabIndexesToMerge, onAnimationEnd); + } + + /** * @param observer The {@link MessageUpdateObserver} to notify. */ public void addObserver(MessageUpdateObserver observer) {
diff --git a/chrome/android/features/tab_ui/tab_management_java_sources.gni b/chrome/android/features/tab_ui/tab_management_java_sources.gni index 8f1165e9..844533f 100644 --- a/chrome/android/features/tab_ui/tab_management_java_sources.gni +++ b/chrome/android/features/tab_ui/tab_management_java_sources.gni
@@ -177,6 +177,7 @@ "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListHighlighter.java", "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListItemAnimator.java", "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMediator.java", + "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListMergeAnimationManager.java", "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListModel.java", "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListNotificationHandler.java", "//chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListOnScrollListener.java",
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml index c586a7f6..37fd15d9 100644 --- a/chrome/android/java/AndroidManifest.xml +++ b/chrome/android/java/AndroidManifest.xml
@@ -603,7 +603,7 @@ android:autoRemoveFromRecents="true"> </activity> <activity android:name="org.chromium.chrome.browser.firstrun.LightweightFirstRunActivity" - android:theme="@style/Theme.BrowserUI.AlertDialog.NoActionBar.DayNight" + android:theme="@style/Theme.BrowserUI.DayNight.AlertDialog.NoActionBar" android:launchMode="singleInstance" {{ self.first_run_activity_common() }}> </activity>
diff --git a/chrome/android/java/res/values/styles.xml b/chrome/android/java/res/values/styles.xml index b73b4da..f8bff018 100644 --- a/chrome/android/java/res/values/styles.xml +++ b/chrome/android/java/res/values/styles.xml
@@ -146,7 +146,7 @@ TODO(crbug.com/41374924): Remove textAppearance when all TextViews have text style explicitly specified. --> <style name="Base.Theme.Chromium.DialogWhenLarge" - parent="Theme.BrowserUI.DialogWhenLarge.DayNight"> + parent="Theme.BrowserUI.DayNight.DialogWhenLarge"> <item name="android:windowBackground">@drawable/bg_white_dialog</item> <item name="android:textAppearance">@style/TextAppearance.TextMedium.Primary</item> <item name="android:textColorLink">@macro/default_text_color_link</item>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/TabStateStore.java b/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/TabStateStore.java index eefd8b87..4d46b26 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/TabStateStore.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/app/tabmodel/TabStateStore.java
@@ -6,9 +6,13 @@ import static org.chromium.build.NullUtil.assumeNonNull; +import android.os.SystemClock; + +import org.chromium.base.Log; import org.chromium.build.annotations.NullMarked; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.TabAssociatedApp; +import org.chromium.chrome.browser.tab.TabState; import org.chromium.chrome.browser.tab.TabStateAttributes; import org.chromium.chrome.browser.tab.TabStateAttributes.DirtinessState; import org.chromium.chrome.browser.tab.TabStateStorageService; @@ -19,6 +23,8 @@ /** Orchestrates saving of tabs to the {@link TabStateStorageService}. */ @NullMarked public class TabStateStore { + private static final String TAG = "TabStateStore"; + private final TabStateStorageService mTabStateStorageService; private final TabStateAttributes.Observer mAttributesObserver = this::onTabStateDirtinessChanged; @@ -45,6 +51,8 @@ TabStateStore.this.onTabUnregistered(tab); } }); + + loadAllTabsFromService(); } /** Cleans up observation. */ @@ -92,4 +100,32 @@ assumeNonNull(TabStateAttributes.from(tab)).removeObserver(mAttributesObserver); // TODO(https://crbug.com/430996004): Delete the tab record. } + + private void loadAllTabsFromService() { + long loadStartTime = SystemClock.elapsedRealtime(); + mTabStateStorageService.loadAllTabs((tabStates) -> onTabsLoaded(tabStates, loadStartTime)); + } + + private void onTabsLoaded(TabState[] tabStates, long loadStartTime) { + long duration = SystemClock.elapsedRealtime() - loadStartTime; + Log.i(TAG, "Loaded %d tabs in %dms", tabStates.length, duration); + + for (int i = 0; i < tabStates.length; i++) { + TabState tabState = tabStates[i]; + if (tabState.contentsState == null || tabState.contentsState.buffer().limit() <= 0) { + Log.i(TAG, " Tab %d: no state", i); + continue; + } + + WebContentsState contentsState = tabState.contentsState; + contentsState.setVersion(WebContentsState.CONTENTS_STATE_CURRENT_VERSION); + Log.i( + TAG, + " Tab %d: url: %s, title: %s, state size: %d", + i, + contentsState.getVirtualUrlFromState(), + contentsState.getDisplayTitleFromState(), + contentsState.buffer().limit()); + } + } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ClearDataDialogActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ClearDataDialogActivity.java index eb98bd9..68354d8 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ClearDataDialogActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ClearDataDialogActivity.java
@@ -9,11 +9,12 @@ import android.content.Intent; import android.os.Bundle; -import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.appcompat.app.AppCompatActivity; import org.chromium.base.IntentUtils; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; import org.chromium.chrome.R; import java.util.ArrayList; @@ -25,6 +26,7 @@ * associated with Trusted Web Activity client app, which has just been uninstalled or had its data * cleared. */ +@NullMarked public class ClearDataDialogActivity extends AppCompatActivity { private static final String EXTRA_APP_NAME = "org.chromium.chrome.extra.app_name"; private static final String EXTRA_DOMAINS = "org.chromium.chrome.extra.domains"; @@ -37,7 +39,7 @@ */ public static Intent createIntent( Context context, - String appName, + @Nullable String appName, Collection<String> linkedDomains, Collection<String> linkedOrigins, boolean appUninstalled) { @@ -91,12 +93,12 @@ } @VisibleForTesting - static List<String> getOriginsFromIntent(Intent intent) { + static @Nullable List<String> getOriginsFromIntent(Intent intent) { return IntentUtils.safeGetStringArrayListExtra(intent, EXTRA_ORIGINS); } @VisibleForTesting - static List<String> getDomainsFromIntent(Intent intent) { + static @Nullable List<String> getDomainsFromIntent(Intent intent) { return IntentUtils.safeGetStringArrayListExtra(intent, EXTRA_DOMAINS); } @@ -106,7 +108,7 @@ } @VisibleForTesting - static String getAppNameFromIntent(Intent intent) { + static @Nullable String getAppNameFromIntent(Intent intent) { return IntentUtils.safeGetStringExtra(intent, EXTRA_APP_NAME); } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/InstalledWebappBroadcastReceiver.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/InstalledWebappBroadcastReceiver.java index 8ed49d0..fc42d98 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/InstalledWebappBroadcastReceiver.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/InstalledWebappBroadcastReceiver.java
@@ -10,6 +10,7 @@ import org.chromium.base.Log; import org.chromium.base.version_info.VersionInfo; +import org.chromium.build.annotations.NullMarked; import org.chromium.chrome.browser.browserservices.permissiondelegation.PermissionUpdater; import org.chromium.chrome.browser.webapps.WebApkUninstallTracker; import org.chromium.components.embedder_support.util.Origin; @@ -42,6 +43,7 @@ * <p>Lifecycle: The lifecycle of this class is managed by Android. Thread safety: {@link * #onReceive} will be called on the UI thread. */ +@NullMarked public class InstalledWebappBroadcastReceiver extends BroadcastReceiver { private static final String TAG = "IWBroadcastReceiver"; @@ -114,6 +116,7 @@ private void clearPreferences(int uid, boolean uninstalled) { String packageName = InstalledWebappDataRegister.getPackageNameForRegisteredUid(uid); + assert packageName != null; BrowserServicesStore.removeTwaDisclosureAcceptanceForPackage(packageName); if (uninstalled) { InstalledWebappDataRegister.removePackage(uid);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/InstalledWebappDataRegister.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/InstalledWebappDataRegister.java index c87f45b..b19a7fd 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/InstalledWebappDataRegister.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/InstalledWebappDataRegister.java
@@ -67,7 +67,7 @@ * handling uninstallation or data clear, since that would require loading native libraries. */ public static void registerPackageForOrigin( - int uid, String appName, String packageName, String domain, Origin origin) { + int uid, String appName, String packageName, @Nullable String domain, Origin origin) { // Store the UID in the main Chrome Preferences. Set<String> uids = getUids(); uids.add(String.valueOf(uid)); @@ -81,7 +81,8 @@ editor.apply(); } - private static void writeToSet(SharedPreferences.Editor editor, String key, String newElement) { + private static void writeToSet( + SharedPreferences.Editor editor, String key, @Nullable String newElement) { Set<String> set = new HashSet<>(getPreferences().getStringSet(key, Collections.emptySet())); set.add(newElement); editor.putStringSet(key, set);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/InstalledWebappRegistrar.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/InstalledWebappRegistrar.java index f28403c..bb699b05 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/InstalledWebappRegistrar.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/InstalledWebappRegistrar.java
@@ -11,6 +11,8 @@ import org.chromium.base.ContextUtils; import org.chromium.base.Log; import org.chromium.base.ThreadUtils; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; import org.chromium.chrome.browser.browserservices.permissiondelegation.PermissionUpdater; import org.chromium.components.embedder_support.util.Origin; import org.chromium.components.embedder_support.util.UrlUtilities; @@ -22,6 +24,7 @@ * Records in all the appropriate places that an installed webapp (TWA or WebAPK) has successfully * been verified. */ +@NullMarked public class InstalledWebappRegistrar { private static final String TAG = "WebappRegistrar"; @@ -35,7 +38,7 @@ */ private final Set<String> mCache = new HashSet<>(); - private static InstalledWebappRegistrar sInstance; + private static @Nullable InstalledWebappRegistrar sInstance; public static InstalledWebappRegistrar getInstance() { if (sInstance == null) sInstance = new InstalledWebappRegistrar();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ManageTrustedWebActivityDataActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ManageTrustedWebActivityDataActivity.java index 5940cf5..c2fa3aa 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ManageTrustedWebActivityDataActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/ManageTrustedWebActivityDataActivity.java
@@ -6,12 +6,13 @@ import android.os.Bundle; -import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import androidx.browser.customtabs.CustomTabsSessionToken; import org.chromium.base.Log; import org.chromium.base.ResettersForTesting; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; import org.chromium.chrome.browser.browserservices.intents.SessionHolder; import org.chromium.chrome.browser.browserservices.metrics.TrustedWebActivityUmaRecorder; import org.chromium.chrome.browser.customtabs.CustomTabsConnection; @@ -23,14 +24,15 @@ * The calling app's identity is established using {@link CustomTabsSessionToken} provided in the * intent. */ +@NullMarked public class ManageTrustedWebActivityDataActivity extends AppCompatActivity { private static final String TAG = "TwaDataActivity"; - private static String sCallingPackageForTesting; + private static @Nullable String sCallingPackageForTesting; @Override - protected void onCreate(Bundle savedInstanceState) { + protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getIntent().getData() == null) { @@ -54,6 +56,7 @@ TrustedWebActivityUmaRecorder.recordOpenedSettingsViaManageSpace(); if (isWebApk) { + assert urlToLaunchSettingsFor != null; TrustedWebActivitySettingsNavigation.launchForWebApkPackageName( this, packageName, urlToLaunchSettingsFor); } else {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClient.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClient.java index 52f1f64..d00f8d6 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClient.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClient.java
@@ -4,6 +4,7 @@ package org.chromium.chrome.browser.browserservices; +import static org.chromium.build.NullUtil.assumeNonNull; import static org.chromium.chrome.browser.browserservices.permissiondelegation.InstalledWebappGeolocationBridge.EXTRA_NEW_LOCATION_ERROR_CALLBACK; import android.app.ActivityOptions; @@ -23,8 +24,6 @@ import android.os.Messenger; import android.os.RemoteException; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.browser.trusted.Token; import androidx.browser.trusted.TrustedWebActivityCallback; import androidx.browser.trusted.TrustedWebActivityService; @@ -36,6 +35,8 @@ import org.chromium.base.ResettersForTesting; import org.chromium.base.task.PostTask; import org.chromium.base.task.TaskTraits; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; import org.chromium.chrome.R; import org.chromium.chrome.browser.browserservices.TrustedWebActivityClientWrappers.Connection; import org.chromium.chrome.browser.browserservices.TrustedWebActivityClientWrappers.ConnectionPool; @@ -54,6 +55,7 @@ import java.util.Set; /** A client for calling methods on a {@link TrustedWebActivityService}. */ +@NullMarked public class TrustedWebActivityClient { private static final String TAG = "TWAClient"; @@ -78,7 +80,7 @@ private final ConnectionPool mConnectionPool; - private static TrustedWebActivityClient sInstance; + private static @Nullable TrustedWebActivityClient sInstance; /** Interface for callbacks to get a permission setting from a TWA app. */ public interface PermissionCallback { @@ -180,6 +182,7 @@ } @ContentSettingValues int settingValue = ContentSettingValues.BLOCK; + assert commandResult != null; @PermissionStatus int permissionStatus = commandResult.getInt(KEY_PERMISSION_STATUS, PermissionStatus.BLOCK); @@ -229,8 +232,9 @@ : commandResult.getBoolean(EXTRA_COMMAND_SUCCESS); PendingIntent pendingIntent = commandSuccess - ? commandResult.getParcelable( - KEY_NOTIFICATION_PERMISSION_REQUEST_PENDING_INTENT) + ? assumeNonNull(commandResult) + .getParcelable( + KEY_NOTIFICATION_PERMISSION_REQUEST_PENDING_INTENT) : null; TrustedWebActivityUmaRecorder.recordExtraCommandSuccess( COMMAND_GET_NOTIFICATION_PERMISSION_REQUEST_PENDING_INTENT, @@ -563,9 +567,9 @@ } private static @Nullable ComponentName searchVerifiedApps( - @NonNull PackageManager pm, + PackageManager pm, @Nullable Set<Token> verifiedPackages, - @NonNull List<ResolveInfo> resolveInfosForUrl) { + List<ResolveInfo> resolveInfosForUrl) { if (verifiedPackages == null || verifiedPackages.isEmpty()) return null; for (ResolveInfo info : resolveInfosForUrl) { @@ -591,7 +595,7 @@ Connection service, String commandName, Bundle args, - TrustedWebActivityCallback callback) { + @Nullable TrustedWebActivityCallback callback) { try { return service.sendExtraCommand(commandName, args, callback); } catch (Exception e) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientWrappers.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientWrappers.java index bd93d18..91a0526 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientWrappers.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivityClientWrappers.java
@@ -22,6 +22,8 @@ import org.chromium.base.task.AsyncTask; import org.chromium.base.task.PostTask; import org.chromium.base.task.TaskTraits; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; import org.chromium.chrome.browser.browserservices.TrustedWebActivityClient.ExecutionCallback; import org.chromium.components.embedder_support.util.Origin; @@ -30,6 +32,7 @@ import java.util.concurrent.Executor; /** Provides wrappers for AndroidX classes that can be mocked in tests. */ +@NullMarked public class TrustedWebActivityClientWrappers { private static final String TAG = "TWAClient"; private static final Executor UI_THREAD_EXECUTOR = @@ -55,11 +58,11 @@ int getSmallIconId() throws RemoteException; /** See implementation on {@link TrustedWebActivityServiceConnection}. */ - Bitmap getSmallIconBitmap() throws RemoteException; + @Nullable Bitmap getSmallIconBitmap() throws RemoteException; /** See implementation on {@link TrustedWebActivityServiceConnection}. */ - Bundle sendExtraCommand( - String commandName, Bundle args, TrustedWebActivityCallback callback) + @Nullable Bundle sendExtraCommand( + String commandName, Bundle args, @Nullable TrustedWebActivityCallback callback) throws RemoteException; } @@ -143,13 +146,13 @@ } @Override - public Bitmap getSmallIconBitmap() throws RemoteException { + public @Nullable Bitmap getSmallIconBitmap() throws RemoteException { return connection.getSmallIconBitmap(); } @Override - public Bundle sendExtraCommand( - String commandName, Bundle args, TrustedWebActivityCallback callback) + public @Nullable Bundle sendExtraCommand( + String commandName, Bundle args, @Nullable TrustedWebActivityCallback callback) throws RemoteException { return connection.sendExtraCommand(commandName, args, callback); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivitySettingsNavigation.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivitySettingsNavigation.java index ec8fa2ef2..a68b374 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivitySettingsNavigation.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/TrustedWebActivitySettingsNavigation.java
@@ -10,6 +10,8 @@ import android.os.Bundle; import org.chromium.base.Log; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; import org.chromium.chrome.R; import org.chromium.chrome.browser.settings.SettingsNavigationFactory; import org.chromium.chrome.browser.webapps.ChromeWebApkHost; @@ -25,6 +27,7 @@ /** * Helper functions for launching site-settings for websites associated with a Trusted Web Activity. */ +@NullMarked public class TrustedWebActivitySettingsNavigation { private static final String TAG = "TwaSettingsNavigation"; @@ -62,7 +65,7 @@ openSingleWebsitePrefs(context, webApkUrl); } - private static Integer getApplicationUid(Context context, String packageName) { + private static @Nullable Integer getApplicationUid(Context context, String packageName) { int applicationUid; try { applicationUid = context.getPackageManager().getApplicationInfo(packageName, 0).uid;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/digitalgoods/DigitalGoodsAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/digitalgoods/DigitalGoodsAdapter.java index 8b801f3..c90df764 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/digitalgoods/DigitalGoodsAdapter.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/digitalgoods/DigitalGoodsAdapter.java
@@ -11,6 +11,7 @@ import androidx.browser.trusted.TrustedWebActivityCallback; import org.chromium.base.Log; +import org.chromium.build.annotations.NullMarked; import org.chromium.chrome.browser.browserservices.TrustedWebActivityClient; import org.chromium.chrome.browser.browserservices.TrustedWebActivityClientWrappers; import org.chromium.components.embedder_support.util.Origin; @@ -24,6 +25,7 @@ * Android types and then uses the {@link TrustedWebActivityClient} to call into the Trusted Web * Activity Client. */ +@NullMarked public class DigitalGoodsAdapter { private static final String TAG = "DigitalGoods";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/digitalgoods/DigitalGoodsFactoryFactory.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/digitalgoods/DigitalGoodsFactoryFactory.java index 6c4be00..6346d9780 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/digitalgoods/DigitalGoodsFactoryFactory.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/digitalgoods/DigitalGoodsFactoryFactory.java
@@ -4,11 +4,13 @@ package org.chromium.chrome.browser.browserservices.digitalgoods; +import org.chromium.build.annotations.NullMarked; import org.chromium.content_public.browser.RenderFrameHost; import org.chromium.payments.mojom.DigitalGoodsFactory; import org.chromium.services.service_manager.InterfaceFactory; /** A factory to produce instances of the mojo {@link DigitalGoodsFactory} interface. */ +@NullMarked public class DigitalGoodsFactoryFactory implements InterfaceFactory<DigitalGoodsFactory> { private final RenderFrameHost mRenderFrameHost;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/digitalgoods/DigitalGoodsFactoryImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/digitalgoods/DigitalGoodsFactoryImpl.java index 5291449..485814e 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/digitalgoods/DigitalGoodsFactoryImpl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/digitalgoods/DigitalGoodsFactoryImpl.java
@@ -7,6 +7,8 @@ import android.app.Activity; import org.chromium.base.ResettersForTesting; +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; import org.chromium.chrome.browser.ActivityUtils; import org.chromium.chrome.browser.customtabs.CustomTabActivity; import org.chromium.chrome.browser.profiles.Profile; @@ -22,8 +24,9 @@ import org.chromium.payments.mojom.DigitalGoodsFactory.CreateDigitalGoods_Response; /** An implementation of the mojo {@link DigitalGoodsFactory} interface. */ +@NullMarked public class DigitalGoodsFactoryImpl implements DigitalGoodsFactory { - private static DigitalGoods sImplForTesting; + private static @Nullable DigitalGoods sImplForTesting; private final RenderFrameHost mRenderFrameHost; private final DigitalGoodsImpl.Delegate mDigitalGoodsDelegate;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/digitalgoods/DigitalGoodsImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/digitalgoods/DigitalGoodsImpl.java index 7238d98..6b695d4a 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/digitalgoods/DigitalGoodsImpl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/digitalgoods/DigitalGoodsImpl.java
@@ -6,8 +6,8 @@ import android.net.Uri; -import androidx.annotation.Nullable; - +import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; import org.chromium.mojo.system.MojoException; import org.chromium.payments.mojom.DigitalGoods; import org.chromium.payments.mojom.DigitalGoods.GetDetails_Response; @@ -18,14 +18,14 @@ * An implementation of the {@link DigitalGoods} mojo interface that communicates with Trusted Web * Activity clients to call Billing APIs. */ +@NullMarked public class DigitalGoodsImpl implements DigitalGoods { private final Delegate mDelegate; /** A Delegate that provides the current URL. */ public interface Delegate { /** @return The current URL or null when the frame is being destroyed. */ - @Nullable - GURL getUrl(); + @Nullable GURL getUrl(); } /** Constructs the object with a given adapter and delegate. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/digitalgoods/SiteIsolator.java b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/digitalgoods/SiteIsolator.java index cbd10bc..8553e838 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/browserservices/digitalgoods/SiteIsolator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/browserservices/digitalgoods/SiteIsolator.java
@@ -8,6 +8,7 @@ import org.jni_zero.NativeMethods; import org.chromium.build.annotations.NullMarked; +import org.chromium.build.annotations.Nullable; import org.chromium.chrome.browser.profiles.Profile; import org.chromium.url.GURL; @@ -20,12 +21,12 @@ public class SiteIsolator { private SiteIsolator() {} - public static void startIsolatingSite(Profile profile, GURL url) { + public static void startIsolatingSite(@Nullable Profile profile, @Nullable GURL url) { SiteIsolatorJni.get().startIsolatingSite(profile, url); } @NativeMethods interface Natives { - void startIsolatingSite(@JniType("Profile*") Profile profile, GURL url); + void startIsolatingSite(@JniType("Profile*") @Nullable Profile profile, @Nullable GURL url); } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedTilesMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedTilesMediator.java index cbea868..9a4309f 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedTilesMediator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedTilesMediator.java
@@ -163,14 +163,38 @@ } /** - * Updates the visibility of the Most Visited Tiles section. The section is visible if and only - * if the user has chosen to show it and there are tiles to display. + * Sets the visibility of the Most Visited Tiles (MVT) section. + * + * <p>If the `NewTabPageCustomizationForMvt` feature is disabled: The section is visible as long + * as it has content, which means there are either tiles to show or custom links are enabled + * (showing the "Add new" button). + * + * <p>If the `NewTabPageCustomizationForMvt` feature is enabled: Visibility is also controlled + * by a user accessible toggle. The section will only be visible if the user has the toggle + * turned on and the section has content. + * + * <p>Once the MVT customization feature flag is enabled by default, the code should be changed + * to: + * + * <p>boolean isMvtVisible = !ChromeFeatureList.sNewTabPageCustomizationForMvt.isEnabled() || + * NtpCustomizationConfigManager.getInstance().getPrefIsMvtToggleOn(); + * + * <p>mModel.set(IS_VISIBLE, isMvtVisible); */ void updateMvtVisibility() { - mModel.set( - IS_VISIBLE, - NtpCustomizationConfigManager.getInstance().getPrefIsMvtToggleOn() - && !mTileGroup.isEmpty()); + // The section has content if the "Add new" button is present or there are tiles. + boolean hasContent = + ChromeFeatureList.sMostVisitedTilesCustomization.isEnabled() + || (mTileGroup != null && !mTileGroup.isEmpty()); + + boolean isMvtVisible = hasContent; + if (ChromeFeatureList.sNewTabPageCustomizationForMvt.isEnabled()) { + // The toggle turns off the whole MVT section regardless the section has something to + // show. + isMvtVisible &= NtpCustomizationConfigManager.getInstance().getPrefIsMvtToggleOn(); + } + + mModel.set(IS_VISIBLE, isMvtVisible); } @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabCollectionTabModelImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabCollectionTabModelImpl.java index bbbd2520..932711bb 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabCollectionTabModelImpl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/tabmodel/TabCollectionTabModelImpl.java
@@ -1605,7 +1605,7 @@ if (isMovingOutOfGroup) { assumeNonNull(oldTabGroupId); boolean wasLastTabInGroup = - wasLastTabInGroupAndNotifyDidMoveTabOutOfGroup(tab, oldTabGroupId, finalIndex); + wasLastTabInGroupAndNotifyDidMoveTabOutOfGroup(tab, oldTabGroupId); // TODO(crbug.com/429145597): Also close the detached tab group if this is not an // undoable merge. if (wasLastTabInGroup && newTabGroupId == null) { @@ -1663,12 +1663,11 @@ * Notifies observers that a tab has moved out of a group. Returns true if the tab was the last * tab in the group. */ - private boolean wasLastTabInGroupAndNotifyDidMoveTabOutOfGroup( - Tab tab, Token oldTabGroupId, int finalIndex) { + private boolean wasLastTabInGroupAndNotifyDidMoveTabOutOfGroup(Tab tab, Token oldTabGroupId) { int prevFilterIndex = representativeIndexOf(getLastShownTabForGroup(oldTabGroupId)); boolean isLastTabInGroup = prevFilterIndex == TabList.INVALID_TAB_INDEX; if (isLastTabInGroup) { - prevFilterIndex = finalIndex; + prevFilterIndex = representativeIndexOf(tab); } for (TabGroupModelFilterObserver observer : mTabGroupObservers) { observer.didMoveTabOutOfGroup(tab, prevFilterIndex);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java index 240134b..54159c8 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/RootUiCoordinator.java
@@ -926,6 +926,8 @@ new WeakReference<Activity>(mActivity), mWindowAndroid.getInsetObserver(), this::initializeEdgeToEdgeController); + } else { + initializeEdgeToEdgeController(); } initBoardingPassDetector();
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabCollectionTabModelImplTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabCollectionTabModelImplTest.java index cf8ae478..60d2a61 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabCollectionTabModelImplTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tabmodel/TabCollectionTabModelImplTest.java
@@ -1700,13 +1700,7 @@ Tab tab2 = createTab(); Tab tab3 = createTab(); - // TODO(crbug.com/429145597): Remove this once the implementation is further along. - // Create a tab that is not in a group to act as the current tab. This is required to - // prevent TabListMediator from being created and failing a bunch of lookups for - // representative tabs that are not yet implemented. - Tab tab4 = createTab(); - - assertTabsInOrderAre(List.of(tab0, tab1, tab2, tab3, tab4)); + assertTabsInOrderAre(List.of(tab0, tab1, tab2, tab3)); CallbackHelper didRemoveTabGroupHelper = new CallbackHelper(); ThreadUtils.runOnUiThreadBlocking( @@ -1745,7 +1739,7 @@ assertEquals(groupId2, tab1.getTabGroupId()); assertEquals(4, mCollectionModel.getTabsInGroup(groupId2).size()); assertFalse(mCollectionModel.tabGroupExists(groupId1)); - assertTabsInOrderAre(List.of(tab2, tab3, tab0, tab1, tab4)); + assertTabsInOrderAre(List.of(tab2, tab3, tab0, tab1)); }); didRemoveTabGroupHelper.waitForOnly();
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java index e76537a..ed7fb67 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/StripLayoutHelperTest.java
@@ -282,7 +282,7 @@ R.style.Theme_BrowserUI_DayNight); mActivity = Robolectric.setupActivity(Activity.class); - mActivity.setTheme(org.chromium.chrome.R.style.Theme_BrowserUI); + mActivity.setTheme(R.style.Theme_BrowserUI_DayNight); when(mWindowAndroid.getActivity()).thenReturn(new WeakReference<>(mActivity)); CompositorAnimationHandler.setTestingMode(true); CompositorAnimationHandler mHandler =
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/reorder/ReorderStrategyTestBase.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/reorder/ReorderStrategyTestBase.java index bd4557e..14abd7c 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/reorder/ReorderStrategyTestBase.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/reorder/ReorderStrategyTestBase.java
@@ -23,6 +23,7 @@ import org.chromium.base.Token; import org.chromium.base.supplier.ObservableSupplierImpl; import org.chromium.base.supplier.Supplier; +import org.chromium.chrome.R; import org.chromium.chrome.browser.compositor.overlays.strip.AnimationHost; import org.chromium.chrome.browser.compositor.overlays.strip.ScrollDelegate; import org.chromium.chrome.browser.compositor.overlays.strip.StripLayoutGroupTitle; @@ -91,7 +92,7 @@ protected void setup() { mActivity = Robolectric.setupActivity(Activity.class); // StripLayoutViews need styles during initializations. - mActivity.setTheme(org.chromium.chrome.R.style.Theme_BrowserUI); + mActivity.setTheme(R.style.Theme_BrowserUI_DayNight); mModel = spy(new MockTabModel(mProfile, /* delegate= */ null)); for (int id : TAB_IDS) mModel.addTab(id); mModel.setIndex(0, TabSelectionType.FROM_USER);
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/reorder/TabStripDragHandlerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/reorder/TabStripDragHandlerTest.java index 03571eb7..19212e2 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/reorder/TabStripDragHandlerTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/compositor/overlays/strip/reorder/TabStripDragHandlerTest.java
@@ -173,7 +173,7 @@ @Before public void beforeTest() { mActivity = Robolectric.setupActivity(Activity.class); - mActivity.setTheme(org.chromium.chrome.R.style.Theme_BrowserUI); + mActivity.setTheme(R.style.Theme_BrowserUI_DayNight); mTabStripHeight = mActivity.getResources().getDimensionPixelSize(R.dimen.tab_strip_height); mPosY = mTabStripHeight - 2 * DRAG_MOVE_DISTANCE; mTabStripVisible = true;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedMediatorUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedMediatorUnitTest.java index 8ab2ac9..76458f29b 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedMediatorUnitTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/suggestions/tile/MostVisitedMediatorUnitTest.java
@@ -135,65 +135,77 @@ } /** - * Verifies that the container visibility only depends on the toggle state and whether there are - * tiles, regardless of the MVT customization feature. + * Verifies the container visibility logic when both feature flags are enabled. Visibility + * should only depend on the toggle state. */ @Test - @DisableFeatures({ChromeFeatureList.MOST_VISITED_TILES_CUSTOMIZATION}) - public void testMvtContainerOnTileCountChanged_DisableMvtCustomization() { - // Toggle is on and tile group not empty. - when(mNtpCustomizationConfigManager.getPrefIsMvtToggleOn()).thenReturn(true); - mMostVisitedSites.setTileSuggestions(JUnitTestGURLs.HTTP_URL.getSpec()); + @Features.EnableFeatures({ + ChromeFeatureList.NEW_TAB_PAGE_CUSTOMIZATION_FOR_MVT, + ChromeFeatureList.MOST_VISITED_TILES_CUSTOMIZATION + }) + public void testOnMvtToggleChanged_MvtCustomizationEnabled() { createMediator(); - mMediator.onTileCountChanged(); - Assert.assertTrue(mModel.get(IS_VISIBLE)); + verify(mNtpCustomizationConfigManager).addListener(mHomepageStateListenerCaptor.capture()); + NtpCustomizationConfigManager.HomepageStateListener listener = + mHomepageStateListenerCaptor.getValue(); - // Toggle is off and tile group not empty. - when(mNtpCustomizationConfigManager.getPrefIsMvtToggleOn()).thenReturn(false); - mMediator.onTileCountChanged(); - Assert.assertFalse(mModel.get(IS_VISIBLE)); - - mMostVisitedSites.setTileSuggestions(new ArrayList<>()); - // Toggle is on and tile group is empty. - when(mNtpCustomizationConfigManager.getPrefIsMvtToggleOn()).thenReturn(true); - mMediator.onTileCountChanged(); - Assert.assertFalse(mModel.get(IS_VISIBLE)); - - // Toggle is off and tile group is empty. - when(mNtpCustomizationConfigManager.getPrefIsMvtToggleOn()).thenReturn(false); - mMediator.onTileCountChanged(); - Assert.assertFalse(mModel.get(IS_VISIBLE)); + // Logic: isMvtVisible = isMvtToggleOn && (true || hasTiles) => isMvtToggleOn + verifyMvtSectionVisibility( + listener, + /* toggleIsOn= */ true, + /* hasTiles= */ true, + /* expectedVisibility= */ true); + verifyMvtSectionVisibility( + listener, + /* toggleIsOn= */ false, + /* hasTiles= */ true, + /* expectedVisibility= */ false); + verifyMvtSectionVisibility( + listener, + /* toggleIsOn= */ true, + /* hasTiles= */ false, + /* expectedVisibility= */ true); + verifyMvtSectionVisibility( + listener, + /* toggleIsOn= */ false, + /* hasTiles= */ false, + /* expectedVisibility= */ false); } /** - * Verifies that the container visibility only depends on the toggle state and whether there are - * tiles, regardless of the MVT customization feature. + * Verifies the container visibility logic when MVT customization is disabled. Visibility should + * depend on both the toggle state and whether there are tiles. */ @Test - @EnableFeatures({ChromeFeatureList.MOST_VISITED_TILES_CUSTOMIZATION}) - public void testMvtContainerOnTileCountChanged_EnableMvtCustomization() { - // Toggle is on and tile group not empty. - when(mNtpCustomizationConfigManager.getPrefIsMvtToggleOn()).thenReturn(true); - mMostVisitedSites.setTileSuggestions(JUnitTestGURLs.HTTP_URL.getSpec()); + @Features.EnableFeatures(ChromeFeatureList.NEW_TAB_PAGE_CUSTOMIZATION_FOR_MVT) + @Features.DisableFeatures(ChromeFeatureList.MOST_VISITED_TILES_CUSTOMIZATION) + public void testOnMvtToggleChanged_MvtCustomizationDisabled() { createMediator(); - mMediator.onTileCountChanged(); - Assert.assertTrue(mModel.get(IS_VISIBLE)); + verify(mNtpCustomizationConfigManager).addListener(mHomepageStateListenerCaptor.capture()); + NtpCustomizationConfigManager.HomepageStateListener listener = + mHomepageStateListenerCaptor.getValue(); - // Toggle is off and tile group not empty. - when(mNtpCustomizationConfigManager.getPrefIsMvtToggleOn()).thenReturn(false); - mMediator.onTileCountChanged(); - Assert.assertFalse(mModel.get(IS_VISIBLE)); - - mMostVisitedSites.setTileSuggestions(new ArrayList<>()); - // Toggle is on and tile group is empty. - when(mNtpCustomizationConfigManager.getPrefIsMvtToggleOn()).thenReturn(true); - mMediator.onTileCountChanged(); - Assert.assertFalse(mModel.get(IS_VISIBLE)); - - // Toggle is off and tile group is empty. - when(mNtpCustomizationConfigManager.getPrefIsMvtToggleOn()).thenReturn(false); - mMediator.onTileCountChanged(); - Assert.assertFalse(mModel.get(IS_VISIBLE)); + // Logic: isMvtVisible = isMvtToggleOn && (false || hasTiles) => isMvtToggleOn && hasTiles + verifyMvtSectionVisibility( + listener, + /* toggleIsOn= */ true, + /* hasTiles= */ true, + /* expectedVisibility= */ true); + verifyMvtSectionVisibility( + listener, + /* toggleIsOn= */ false, + /* hasTiles= */ true, + /* expectedVisibility= */ false); + verifyMvtSectionVisibility( + listener, + /* toggleIsOn= */ true, + /* hasTiles= */ false, + /* expectedVisibility= */ false); + verifyMvtSectionVisibility( + listener, + /* toggleIsOn= */ false, + /* hasTiles= */ false, + /* expectedVisibility= */ false); } @Test @@ -332,77 +344,167 @@ } /** - * Verifies that the container visibility only depends on the toggle state and whether there are - * tiles, regardless of the MVT customization feature. + * Verifies that the container is visible if and only if there are tiles, when both NTP and MVT + * customization features are disabled. The MVT toggle state should have no effect. */ @Test - @Features.EnableFeatures({ + @DisableFeatures({ ChromeFeatureList.NEW_TAB_PAGE_CUSTOMIZATION_FOR_MVT, ChromeFeatureList.MOST_VISITED_TILES_CUSTOMIZATION }) - public void testOnMvtToggleChanged_MvtCustomizationEnabled() { + public void testMvtContainerOnTileCountChanged_AllDisabled() { + // Logic: isMvtVisible = false || hasTiles => hasTiles createMediator(); - verify(mNtpCustomizationConfigManager).addListener(mHomepageStateListenerCaptor.capture()); - NtpCustomizationConfigManager.HomepageStateListener listener = - mHomepageStateListenerCaptor.getValue(); - mMostVisitedSites.setTileSuggestions(JUnitTestGURLs.HTTP_URL.getSpec()); - // MVT toggle on and tile group not empty. - when(mNtpCustomizationConfigManager.getPrefIsMvtToggleOn()).thenReturn(true); - listener.onMvtToggleChanged(); - Assert.assertTrue(mModel.get(IS_VISIBLE)); + verifyMvtSectionVisibility( + /* listener= */ null, + /* toggleIsOn= */ true, + /* hasTiles= */ true, + /* expectedVisibility= */ true); + verifyMvtSectionVisibility( + /* listener= */ null, + /* toggleIsOn= */ true, + /* hasTiles= */ false, + /* expectedVisibility= */ false); - // MVT toggle off and tile group not empty. - when(mNtpCustomizationConfigManager.getPrefIsMvtToggleOn()).thenReturn(false); - listener.onMvtToggleChanged(); - Assert.assertFalse(mModel.get(IS_VISIBLE)); - - mMostVisitedSites.setTileSuggestions(new ArrayList<>()); - // MVT toggle on and tile group is empty. - when(mNtpCustomizationConfigManager.getPrefIsMvtToggleOn()).thenReturn(true); - listener.onMvtToggleChanged(); - Assert.assertFalse(mModel.get(IS_VISIBLE)); - - // MVT toggle off and tile group is empty. - when(mNtpCustomizationConfigManager.getPrefIsMvtToggleOn()).thenReturn(false); - listener.onMvtToggleChanged(); - Assert.assertFalse(mModel.get(IS_VISIBLE)); + // Testing those two cases for completeness, even though it's an impossible case. + // The toggle is inaccessible and defaults to true when the MVT customization feature is + // off. + verifyMvtSectionVisibility( + /* listener= */ null, + /* toggleIsOn= */ false, + /* hasTiles= */ true, + /* expectedVisibility= */ true); + verifyMvtSectionVisibility( + /* listener= */ null, + /* toggleIsOn= */ false, + /* hasTiles= */ false, + /* expectedVisibility= */ false); } /** - * Verifies that the container visibility only depends on the toggle state and whether there are - * tiles, regardless of the MVT customization feature. + * Verifies that the container is always visible when only the MVT customization feature is + * enabled, regardless of the MVT toggle state or whether there are tiles. This is because the + * "Add shortcut" button should be visible. */ @Test - @Features.EnableFeatures(ChromeFeatureList.NEW_TAB_PAGE_CUSTOMIZATION_FOR_MVT) - @Features.DisableFeatures(ChromeFeatureList.MOST_VISITED_TILES_CUSTOMIZATION) - public void testOnMvtToggleChanged_MvtCustomizationDisabled() { + @DisableFeatures({ChromeFeatureList.NEW_TAB_PAGE_CUSTOMIZATION_FOR_MVT}) + @EnableFeatures({ChromeFeatureList.MOST_VISITED_TILES_CUSTOMIZATION}) + public void testMvtContainerOnTileCountChanged_MvtCustomizationEnabled() { + // Logic: isMvtVisible = true || hasTiles => true createMediator(); - verify(mNtpCustomizationConfigManager).addListener(mHomepageStateListenerCaptor.capture()); - NtpCustomizationConfigManager.HomepageStateListener listener = - mHomepageStateListenerCaptor.getValue(); + verifyMvtSectionVisibility( + /* listener= */ null, + /* toggleIsOn= */ true, + /* hasTiles= */ true, + /* expectedVisibility= */ true); + verifyMvtSectionVisibility( + /* listener= */ null, + /* toggleIsOn= */ true, + /* hasTiles= */ false, + /* expectedVisibility= */ true); - // MVT toggle on and tile group not empty. - when(mNtpCustomizationConfigManager.getPrefIsMvtToggleOn()).thenReturn(true); - mMostVisitedSites.setTileSuggestions(JUnitTestGURLs.HTTP_URL.getSpec()); - listener.onMvtToggleChanged(); - Assert.assertTrue(mModel.get(IS_VISIBLE)); + // Testing those two cases for completeness, even though it's an impossible case. + // The toggle is inaccessible and defaults to true when the MVT customization feature is + // off. + verifyMvtSectionVisibility( + /* listener= */ null, + /* toggleIsOn= */ false, + /* hasTiles= */ false, + /* expectedVisibility= */ true); + verifyMvtSectionVisibility( + /* listener= */ null, + /* toggleIsOn= */ false, + /* hasTiles= */ true, + /* expectedVisibility= */ true); + } - // MVT toggle off and tile group not empty. - when(mNtpCustomizationConfigManager.getPrefIsMvtToggleOn()).thenReturn(false); - listener.onMvtToggleChanged(); - Assert.assertFalse(mModel.get(IS_VISIBLE)); + /** + * Verifies that the container is visible if and only if the MVT toggle is on and there are + * tiles, when only the NTP customization for MVT feature is enabled. + */ + @Test + @EnableFeatures({ChromeFeatureList.NEW_TAB_PAGE_CUSTOMIZATION_FOR_MVT}) + @DisableFeatures({ChromeFeatureList.MOST_VISITED_TILES_CUSTOMIZATION}) + public void testMvtContainerOnTileCountChanged_NtpCustomizationEnabled() { + // Logic: isMvtVisible = isMvtToggleOn && (false || hasTiles) => isMvtToggleOn && hasTiles + createMediator(); + verifyMvtSectionVisibility( + /* listener= */ null, + /* toggleIsOn= */ true, + /* hasTiles= */ true, + /* expectedVisibility= */ true); + verifyMvtSectionVisibility( + /* listener= */ null, + /* toggleIsOn= */ false, + /* hasTiles= */ true, + /* expectedVisibility= */ false); + verifyMvtSectionVisibility( + /* listener= */ null, + /* toggleIsOn= */ true, + /* hasTiles= */ false, + /* expectedVisibility= */ false); + verifyMvtSectionVisibility( + /* listener= */ null, + /* toggleIsOn= */ false, + /* hasTiles= */ false, + /* expectedVisibility= */ false); + } - mMostVisitedSites.setTileSuggestions(new ArrayList<>()); - // MVT toggle on but tile group is empty. - when(mNtpCustomizationConfigManager.getPrefIsMvtToggleOn()).thenReturn(true); - listener.onMvtToggleChanged(); - Assert.assertFalse(mModel.get(IS_VISIBLE)); + /** + * Verifies that the container is visible if and only if the MVT toggle is on, when both NTP and + * MVT customization features are enabled. The presence of tiles should have no effect. + */ + @Test + @EnableFeatures({ + ChromeFeatureList.NEW_TAB_PAGE_CUSTOMIZATION_FOR_MVT, + ChromeFeatureList.MOST_VISITED_TILES_CUSTOMIZATION + }) + public void testMvtContainerOnTileCountChanged_AllEnabled() { + // Logic: isMvtVisible = isMvtToggleOn && (true || hasTiles) => isMvtToggleOn + createMediator(); + verifyMvtSectionVisibility( + /* listener= */ null, + /* toggleIsOn= */ true, + /* hasTiles= */ true, + /* expectedVisibility= */ true); + verifyMvtSectionVisibility( + /* listener= */ null, + /* toggleIsOn= */ false, + /* hasTiles= */ true, + /* expectedVisibility= */ false); + verifyMvtSectionVisibility( + /* listener= */ null, + /* toggleIsOn= */ true, + /* hasTiles= */ false, + /* expectedVisibility= */ true); + verifyMvtSectionVisibility( + /* listener= */ null, + /* toggleIsOn= */ false, + /* hasTiles= */ false, + /* expectedVisibility= */ false); + } - // MVT toggle off and tile group is empty. - when(mNtpCustomizationConfigManager.getPrefIsMvtToggleOn()).thenReturn(false); - listener.onMvtToggleChanged(); - Assert.assertFalse(mModel.get(IS_VISIBLE)); + private void verifyMvtSectionVisibility( + NtpCustomizationConfigManager.HomepageStateListener listener, + boolean toggleIsOn, + boolean hasTiles, + boolean expectedVisibility) { + if (hasTiles) { + mMostVisitedSites.setTileSuggestions(JUnitTestGURLs.HTTP_URL.getSpec()); + } else { + mMostVisitedSites.setTileSuggestions(new ArrayList<>()); + } + + when(mNtpCustomizationConfigManager.getPrefIsMvtToggleOn()).thenReturn(toggleIsOn); + + if (listener == null) { + mMediator.onTileCountChanged(); + } else { + listener.onMvtToggleChanged(); + } + + Assert.assertEquals(expectedVisibility, mModel.get(IS_VISIBLE)); } @Test
diff --git a/chrome/android/profiles/arm.newest.txt b/chrome/android/profiles/arm.newest.txt index 672ee8f..50e7b38 100644 --- a/chrome/android/profiles/arm.newest.txt +++ b/chrome/android/profiles/arm.newest.txt
@@ -1 +1 @@ -chromeos-chrome-arm-140.0.7324.0_pre1492793_rc-r1-merged.afdo.bz2 +chromeos-chrome-arm-140.0.7330.0_pre1494647_rc-r1-merged.afdo.bz2
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 2f73f92..bff17b5d 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd
@@ -8493,10 +8493,10 @@ Reset to default </message> </if> - <message name="IDS_NTP_COMPOSE_ENTRYPOINT" desc="The text for the compose entrypoint in the searchbox on the New Tab Page." translateable="true"> + <message name="IDS_NTP_COMPOSE_ENTRYPOINT" desc="The text for the compose entrypoint in the searchbox on the New Tab Page."> AI Mode </message> - <message name="IDS_NTP_COMPOSE_ENTRYPOINT_A11Y_LABEL" desc="The accessibility label for the compose entrypoint in the searchbox on the New Tab Page." translateable="false"> + <message name="IDS_NTP_COMPOSE_ENTRYPOINT_A11Y_LABEL" desc="The accessibility label for the compose entrypoint in the searchbox on the New Tab Page."> Ask Google AI Mode </message> <message name="IDS_NTP_COMPOSE_PLACEHOLDER_TEXT" desc="The placeholder for the input in the compose box."> @@ -8505,43 +8505,43 @@ <message name="IDS_NTP_COMPOSE_CANCEL_BUTTON_A11Y_LABEL" desc="The accessibility label for the cancel button on the compose box when there is no input."> Dismiss </message> - <message name="IDS_NTP_COMPOSE_CANCEL_BUTTON_A11Y_LABEL_INPUT" desc="The accessibility label for the cancel button on the compose box when there is input." translateable="false"> + <message name="IDS_NTP_COMPOSE_CANCEL_BUTTON_A11Y_LABEL_INPUT" desc="The accessibility label for the cancel button on the compose box when there is input."> Clear </message> <message name="IDS_NTP_COMPOSE_IMAGE_UPLOAD_BUTTON_A11Y_LABEL" desc="The accessibility label for the image upload button on the compose box."> Upload an image </message> - <message name="IDS_NTP_COMPOSE_PDF_UPLOAD_BUTTON_A11Y_LABEL" desc="The accessibility label for the file upload button on the compose box." translateable="false"> + <message name="IDS_NTP_COMPOSE_PDF_UPLOAD_BUTTON_A11Y_LABEL" desc="The accessibility label for the file upload button on the compose box."> Upload a PDF </message> <message name="IDS_NTP_COMPOSE_SUBMIT_BUTTON_A11Y_LABEL" desc="The accessibility label for the submit button on the compose box."> Send </message> - <message name="IDS_NTP_COMPOSE_DELETE_FILE_A11Y_LABEL" desc="The accessibility label for the delete button shown on hover for uploaded files." translateable="false"> + <message name="IDS_NTP_COMPOSE_DELETE_FILE_A11Y_LABEL" desc="The accessibility label for the delete button shown on hover for uploaded files."> <ph name="FILE_NAME">$1<ex>recipe.pdf</ex></ph> press enter to delete file </message> - <message name="IDS_NTP_COMPOSE_FILE_UPLOAD_STARTED_A11Y_TEXT" desc="Text for a screenreader to read aloud when the user starts uploading a file." translateable="false"> + <message name="IDS_NTP_COMPOSE_FILE_UPLOAD_STARTED_A11Y_TEXT" desc="Text for a screenreader to read aloud when the user starts uploading a file."> Uploading file </message> - <message name="IDS_NTP_COMPOSE_FILE_UPLOAD_COMPLETE_A11Y_TEXT" desc="Text for a screenreader to read aloud when the user's file upload completes successfully." translateable="false">> + <message name="IDS_NTP_COMPOSE_FILE_UPLOAD_COMPLETE_A11Y_TEXT" desc="Text for a screenreader to read aloud when the user's file upload completes successfully.">> File upload complete </message> - <message name="IDS_NTP_COMPOSE_FILE_UPLOAD_INVALID_EMPTY_SIZE" desc="The text to display when a file upload is empty." translateable="false"> + <message name="IDS_NTP_COMPOSE_FILE_UPLOAD_INVALID_EMPTY_SIZE" desc="The text to display when a file upload is empty."> Can't upload. File appears to be empty. </message> - <message name="IDS_NTP_COMPOSE_FILE_UPLOAD_INVALID_TOO_LARGE" desc="The text to display when a file upload exceeds the allowed size" translateable="false"> + <message name="IDS_NTP_COMPOSE_FILE_UPLOAD_INVALID_TOO_LARGE" desc="The text to display when a file upload exceeds the allowed size"> Can't upload. File exceeds maximum supported size. </message> - <message name="IDS_NTP_COMPOSE_FILE_UPLOAD_IMAGE_PROCESSING_ERROR" desc="The text to display when an file upload image can't be processed." translateable="false"> + <message name="IDS_NTP_COMPOSE_FILE_UPLOAD_IMAGE_PROCESSING_ERROR" desc="The text to display when an file upload image can't be processed."> Can't upload. Failed to process image file. </message> - <message name="IDS_NTP_COMPOSE_FILE_UPLOAD_VALIDATION_FAILED" desc="The generic text to display when a file upload fails a validation step." translateable="false"> + <message name="IDS_NTP_COMPOSE_FILE_UPLOAD_VALIDATION_FAILED" desc="The generic text to display when a file upload fails a validation step."> Can't upload. File upload failed validation checks. </message> - <message name="IDS_NTP_COMPOSE_FILE_UPLOAD_FAILED" translateable="false"> + <message name="IDS_NTP_COMPOSE_FILE_UPLOAD_FAILED"> Unexpected error, file upload failed. </message> - <message name="IDS_NTP_COMPOSE_FILE_UPLOAD_EXPIRED" translateable="false"> + <message name="IDS_NTP_COMPOSE_FILE_UPLOAD_EXPIRED"> File upload has expired and been removed. </message> @@ -19135,6 +19135,34 @@ </message> </if> + <!-- Protected content identifiers permission --> + <if expr="is_win"> + <message name="IDS_ALLOWED_PROTECTED_CONTENT_IDENTIFIERS_TITLE" desc="Title of the info bubble shown when a site has been allowed to use identifiers to play protected content."> + Protected content identifiers allowed + </message> + <message name="IDS_BLOCKED_PROTECTED_CONTENT_IDENTIFIERS_TITLE" desc="Title of the info bubble shown when a site has been denied to to use identifiers to play protected content."> + Protected content identifiers denied + </message> + <message name="IDS_ALLOWED_PROTECTED_CONTENT_IDENTIFIERS_MESSAGE" desc="Info bubble message and location bar icon tooltip text shown when a site has been allowed to use identifiers to play protected content."> + This site can use identifiers to play protected content. + </message> + <message name="IDS_ALLOWED_PROTECTED_CONTENT_IDENTIFIERS_BLOCK" desc="Radio button to block to use identifiers to play protected content"> + Always block <ph name="HOST">$1<ex>example.com</ex></ph> from using identifiers to play protected content + </message> + <message name="IDS_ALLOWED_PROTECTED_CONTENT_IDENTIFIERS_NO_ACTION" desc="Radio button to keep allowing to use identifiers to play protected content"> + Continue allowing this site to use identifiers to play protected content + </message> + <message name="IDS_BLOCKED_PROTECTED_CONTENT_IDENTIFIERS_MESSAGE" desc="Info bubble message and location bar icon tooltip text shown when a site has been denied to use identifiers to play protected content."> + This site has been blocked from using identifiers to play protected content. + </message> + <message name="IDS_BLOCKED_PROTECTED_CONTENT_IDENTIFIERS_UNBLOCK" desc="Radio button choice to unblock a site from using identifiers to play protected content."> + Always allow <ph name="HOST">$1<ex>mail.google.com</ex></ph> to use identifiers to play protected content + </message> + <message name="IDS_BLOCKED_PROTECTED_CONTENT_IDENTIFIERS_NO_ACTION" desc="Radio button choice to continue blocking a site from using identifiers to play protected content."> + Continue blocking this site from using identifiers to play protected content + </message> + </if> + <!-- New Tab Page browser feature promotions. --> <if expr="not is_android"> <message name="IDS_NTP_CUSTOMIZATION_PROMO" desc="The text shown in the body of the New Tab Page browser customization promotion">
diff --git a/chrome/app/generated_resources_grd/IDS_ALLOWED_PROTECTED_CONTENT_IDENTIFIERS_BLOCK.png.sha1 b/chrome/app/generated_resources_grd/IDS_ALLOWED_PROTECTED_CONTENT_IDENTIFIERS_BLOCK.png.sha1 new file mode 100644 index 0000000..98fa9fc --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_ALLOWED_PROTECTED_CONTENT_IDENTIFIERS_BLOCK.png.sha1
@@ -0,0 +1 @@ +2701d36dae02884949b651bf173232c10b723f4f \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_ALLOWED_PROTECTED_CONTENT_IDENTIFIERS_MESSAGE.png.sha1 b/chrome/app/generated_resources_grd/IDS_ALLOWED_PROTECTED_CONTENT_IDENTIFIERS_MESSAGE.png.sha1 new file mode 100644 index 0000000..5cd1cf377 --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_ALLOWED_PROTECTED_CONTENT_IDENTIFIERS_MESSAGE.png.sha1
@@ -0,0 +1 @@ +3e0bdba1c64b4ac243f2a6278e53b75f88690da5 \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_ALLOWED_PROTECTED_CONTENT_IDENTIFIERS_NO_ACTION.png.sha1 b/chrome/app/generated_resources_grd/IDS_ALLOWED_PROTECTED_CONTENT_IDENTIFIERS_NO_ACTION.png.sha1 new file mode 100644 index 0000000..d1997f22 --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_ALLOWED_PROTECTED_CONTENT_IDENTIFIERS_NO_ACTION.png.sha1
@@ -0,0 +1 @@ +63d261d84e9368c3ee4a1f21160c9dc0a12dd95c \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_ALLOWED_PROTECTED_CONTENT_IDENTIFIERS_TITLE.png.sha1 b/chrome/app/generated_resources_grd/IDS_ALLOWED_PROTECTED_CONTENT_IDENTIFIERS_TITLE.png.sha1 new file mode 100644 index 0000000..2a35803 --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_ALLOWED_PROTECTED_CONTENT_IDENTIFIERS_TITLE.png.sha1
@@ -0,0 +1 @@ +622fb846e409b1c2f1bb2d29a54fcbfe4c7a7789 \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_BLOCKED_PROTECTED_CONTENT_IDENTIFIERS_MESSAGE.png.sha1 b/chrome/app/generated_resources_grd/IDS_BLOCKED_PROTECTED_CONTENT_IDENTIFIERS_MESSAGE.png.sha1 new file mode 100644 index 0000000..dd2d706ac --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_BLOCKED_PROTECTED_CONTENT_IDENTIFIERS_MESSAGE.png.sha1
@@ -0,0 +1 @@ +5bd27618ff0e0dc8b264a894132ba8bd8439fc6a \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_BLOCKED_PROTECTED_CONTENT_IDENTIFIERS_NO_ACTION.png.sha1 b/chrome/app/generated_resources_grd/IDS_BLOCKED_PROTECTED_CONTENT_IDENTIFIERS_NO_ACTION.png.sha1 new file mode 100644 index 0000000..0354d64 --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_BLOCKED_PROTECTED_CONTENT_IDENTIFIERS_NO_ACTION.png.sha1
@@ -0,0 +1 @@ +2bb4ac29c34f7b47b8e7ccd77a1f7a79439d1821 \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_BLOCKED_PROTECTED_CONTENT_IDENTIFIERS_TITLE.png.sha1 b/chrome/app/generated_resources_grd/IDS_BLOCKED_PROTECTED_CONTENT_IDENTIFIERS_TITLE.png.sha1 new file mode 100644 index 0000000..b223d29 --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_BLOCKED_PROTECTED_CONTENT_IDENTIFIERS_TITLE.png.sha1
@@ -0,0 +1 @@ +d84e493f98184b80bc20ced86c05fcb7644bca21 \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_BLOCKED_PROTECTED_CONTENT_IDENTIFIERS_UNBLOCK.png.sha1 b/chrome/app/generated_resources_grd/IDS_BLOCKED_PROTECTED_CONTENT_IDENTIFIERS_UNBLOCK.png.sha1 new file mode 100644 index 0000000..69e453b --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_BLOCKED_PROTECTED_CONTENT_IDENTIFIERS_UNBLOCK.png.sha1
@@ -0,0 +1 @@ +21e97e8a4704e7915319a5ecec0f38bb6bcaf81c \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_CANCEL_BUTTON_A11Y_LABEL_INPUT.png.sha1 b/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_CANCEL_BUTTON_A11Y_LABEL_INPUT.png.sha1 index 8d24424..bb1ff6e 100644 --- a/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_CANCEL_BUTTON_A11Y_LABEL_INPUT.png.sha1 +++ b/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_CANCEL_BUTTON_A11Y_LABEL_INPUT.png.sha1
@@ -1 +1 @@ -cd9e862e12165dbbf9afa65a2c0e072552795dc0 \ No newline at end of file +fa07901905a3454fd8ca52b2923fa81f98083bd2 \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_DELETE_FILE_A11Y_LABEL.png.sha1 b/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_DELETE_FILE_A11Y_LABEL.png.sha1 new file mode 100644 index 0000000..bb648b31 --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_DELETE_FILE_A11Y_LABEL.png.sha1
@@ -0,0 +1 @@ +36c183aac2fe9a5973d4c388de81ea4b8d50de93 \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_ENTRYPOINT_A11Y_LABEL.png.sha1 b/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_ENTRYPOINT_A11Y_LABEL.png.sha1 index dbec228..e4d47c5 100644 --- a/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_ENTRYPOINT_A11Y_LABEL.png.sha1 +++ b/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_ENTRYPOINT_A11Y_LABEL.png.sha1
@@ -1 +1 @@ -c1dc1f0e961018917dd72ff5f21bf9646226792b \ No newline at end of file +f4a6f88046aa1b532c0a7fb012f44387f37aa399 \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_FILE_UPLOAD_COMPLETE_A11Y_TEXT.png.sha1 b/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_FILE_UPLOAD_COMPLETE_A11Y_TEXT.png.sha1 new file mode 100644 index 0000000..fc348d4 --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_FILE_UPLOAD_COMPLETE_A11Y_TEXT.png.sha1
@@ -0,0 +1 @@ +006d82e903b7936e3b62b24033d863854bb8565f \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_FILE_UPLOAD_EXPIRED.png.sha1 b/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_FILE_UPLOAD_EXPIRED.png.sha1 new file mode 100644 index 0000000..a9072cf --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_FILE_UPLOAD_EXPIRED.png.sha1
@@ -0,0 +1 @@ +9fa3cb6f07171fff1f53bcf4aaf83696f973b555 \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_FILE_UPLOAD_FAILED.png.sha1 b/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_FILE_UPLOAD_FAILED.png.sha1 new file mode 100644 index 0000000..e94b21f --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_FILE_UPLOAD_FAILED.png.sha1
@@ -0,0 +1 @@ +117794e83bd7fb5e33d8d7f6895bb2063adbc868 \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_FILE_UPLOAD_IMAGE_PROCESSING_ERROR.png.sha1 b/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_FILE_UPLOAD_IMAGE_PROCESSING_ERROR.png.sha1 new file mode 100644 index 0000000..9f00a2f --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_FILE_UPLOAD_IMAGE_PROCESSING_ERROR.png.sha1
@@ -0,0 +1 @@ +feeb6444bffc1decbbd4bfe69f65281eb3470252 \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_FILE_UPLOAD_INVALID_EMPTY_SIZE.png.sha1 b/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_FILE_UPLOAD_INVALID_EMPTY_SIZE.png.sha1 new file mode 100644 index 0000000..ce8646b9 --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_FILE_UPLOAD_INVALID_EMPTY_SIZE.png.sha1
@@ -0,0 +1 @@ +19c03e1d6dfa88805ade89390dac142f1a48b610 \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_FILE_UPLOAD_INVALID_TOO_LARGE.png.sha1 b/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_FILE_UPLOAD_INVALID_TOO_LARGE.png.sha1 new file mode 100644 index 0000000..2b38cfc9 --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_FILE_UPLOAD_INVALID_TOO_LARGE.png.sha1
@@ -0,0 +1 @@ +ed83ea27a6474a6704fa6c5fe39920f76cb6602e \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_FILE_UPLOAD_STARTED_A11Y_TEXT.png.sha1 b/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_FILE_UPLOAD_STARTED_A11Y_TEXT.png.sha1 new file mode 100644 index 0000000..bb648b31 --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_FILE_UPLOAD_STARTED_A11Y_TEXT.png.sha1
@@ -0,0 +1 @@ +36c183aac2fe9a5973d4c388de81ea4b8d50de93 \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_FILE_UPLOAD_VALIDATION_FAILED.png.sha1 b/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_FILE_UPLOAD_VALIDATION_FAILED.png.sha1 new file mode 100644 index 0000000..cfdb77e --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_FILE_UPLOAD_VALIDATION_FAILED.png.sha1
@@ -0,0 +1 @@ +b53dc7c3bc559e5cbeacead2edf356abc3670469 \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_PDF_UPLOAD_BUTTON_A11Y_LABEL.png.sha1 b/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_PDF_UPLOAD_BUTTON_A11Y_LABEL.png.sha1 new file mode 100644 index 0000000..48acedd --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_NTP_COMPOSE_PDF_UPLOAD_BUTTON_A11Y_LABEL.png.sha1
@@ -0,0 +1 @@ +cdc2473b2cf9ec8b7e21336b8b3828c290affc69 \ No newline at end of file
diff --git a/chrome/app/profiles_strings.grdp b/chrome/app/profiles_strings.grdp index 5eb04f8..b5eb128 100644 --- a/chrome/app/profiles_strings.grdp +++ b/chrome/app/profiles_strings.grdp
@@ -1173,6 +1173,10 @@ <message name="IDS_AVATAR_BUTTON_SEE_TABS_FROM_OTHER_DEVICES" desc="The avatar button label for the history sync promo. When clicked, it opens the profile menu with the sync button."> See tabs from other devices? </message> + <!-- TODO(crbug.com/435113265): Finalize the string and make it translateable. --> + <message translateable="false" name="IDS_AVATAR_BUTTON_SYNC_PROMO" desc="The avatar button label for the sync promo. When clicked, it opens the profile menu with the sync button."> + Back-up your stuff + </message> <!-- Profile Menu Sync Promo Desktop --> <message name="IDS_PROFILE_MENU_SYNC_PROMO_BROWSE_ACROSS_DEVICES_DESCRIPTION" desc="The description for the sync promo in the profile menu opened from the 'Browse across devices?' history sync opt-in identity pill."> To pick up where you left off on other devices, sync your history and tabs to <ph name="ACCOUNT_EMAIL">$1<ex>elisa.beckett@gmail.com</ex></ph>
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp index 89f01ccb..73dfbc5 100644 --- a/chrome/app/settings_strings.grdp +++ b/chrome/app/settings_strings.grdp
@@ -739,10 +739,10 @@ Remove from Chrome </message> <message name="IDS_SETTINGS_HOME_ADDRESS_REMOVE_CONFIRMATION_DIALOG_TITLE" desc="Title for the dialog that asks for confirmation to remove the selected home address from Chrome."> - Remove your home address from Chrome? + Remove your home address from Chrome autofill? </message> <message name="IDS_SETTINGS_WORK_ADDRESS_REMOVE_CONFIRMATION_DIALOG_TITLE" desc="Title for the dialog that asks for confirmation to remove the selected work address from Chrome."> - Remove your work address from Chrome? + Remove your work address from Chrome autofill? </message> <message name="IDS_SETTINGS_HOME_ADDRESS_REMOVE_CONFIRMATION_DIALOG_NOTICE" desc="Body for the dialog that asks for confirmation to remove the selected home address from Chrome."> Your home address can help you get things done across Google services, like more relevant Search results and directions in Google Maps. You can edit or delete your address in your <ph name="BEGIN_LINK"><a href="$1" target="_blank"></ph>Google Account <ph name="USERNAME">$2<ex>example@gmail.com</ex></ph><ph name="END_LINK"></a></ph>.
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_HOME_ADDRESS_REMOVE_CONFIRMATION_DIALOG_TITLE.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_HOME_ADDRESS_REMOVE_CONFIRMATION_DIALOG_TITLE.png.sha1 index ed957ec..8a87f23 100644 --- a/chrome/app/settings_strings_grdp/IDS_SETTINGS_HOME_ADDRESS_REMOVE_CONFIRMATION_DIALOG_TITLE.png.sha1 +++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_HOME_ADDRESS_REMOVE_CONFIRMATION_DIALOG_TITLE.png.sha1
@@ -1 +1 @@ -c52210b14365366f1f7bfaeeeaff65a624948188 \ No newline at end of file +124279ff5faa6ba337a905b12a345c0064fe3196 \ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_WORK_ADDRESS_REMOVE_CONFIRMATION_DIALOG_TITLE.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_WORK_ADDRESS_REMOVE_CONFIRMATION_DIALOG_TITLE.png.sha1 index 6319ee37..8f473e53 100644 --- a/chrome/app/settings_strings_grdp/IDS_SETTINGS_WORK_ADDRESS_REMOVE_CONFIRMATION_DIALOG_TITLE.png.sha1 +++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_WORK_ADDRESS_REMOVE_CONFIRMATION_DIALOG_TITLE.png.sha1
@@ -1 +1 @@ -74b3432a36bd0b53275d7b5d7a301ff74d486a09 \ No newline at end of file +10db4d5d0df46fbebc7e1d76c8e9a996e0cf3263 \ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index 619e05b4..aaffa2f 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -2028,6 +2028,7 @@ "//chrome/browser/segmentation_platform", "//chrome/browser/send_tab_to_self", "//chrome/browser/serial", + "//chrome/browser/sessions", "//chrome/browser/share", "//chrome/browser/sharing:buildflags", "//chrome/browser/signin", @@ -8167,10 +8168,8 @@ "sessions/session_data_service_factory.cc", "sessions/session_data_service_factory.h", "sessions/session_restore.cc", - "sessions/session_restore.h", "sessions/session_restore_delegate.cc", "sessions/session_restore_delegate.h", - "sessions/session_restore_observer.h", "sessions/session_restore_stats_collector.cc", "sessions/session_restore_stats_collector.h", "sessions/session_service.cc", @@ -9153,6 +9152,7 @@ "//chrome/browser/profiles:profile", "//chrome/browser/resource_coordinator", "//chrome/browser/safe_browsing:test_support", + "//chrome/browser/sessions", "//chrome/browser/ui:test_support", ] deps = [
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index 4ecce10..9b9c83f 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -5348,6 +5348,9 @@ {"use-dhcpcd10", flag_descriptions::kUseDHCPCD10Name, flag_descriptions::kUseDHCPCD10Description, kOsCrOS, FEATURE_VALUE_TYPE(ash::features::kUseDHCPCD10)}, + {"jupiter-screensaver", flag_descriptions::kJupiterScreensaverName, + flag_descriptions::kJupiterScreensaverDescription, kOsCrOS, + FEATURE_VALUE_TYPE(ash::features::kJupiterScreensaver)}, #endif // BUILDFLAG(IS_CHROMEOS) { "disable-accelerated-video-decode", @@ -10723,6 +10726,12 @@ "EnableHistorySyncOptinExpansionPill")}, #endif +#if BUILDFLAG(IS_WIN) + {"avatar-button-sync-promo", flag_descriptions::kAvatarButtonSyncPromoName, + flag_descriptions::kAvatarButtonSyncPromoDescription, kOsWin, + FEATURE_VALUE_TYPE(switches::kAvatarButtonSyncPromo)}, +#endif + #if BUILDFLAG(ENABLE_DICE_SUPPORT) && BUILDFLAG(ENABLE_EXTENSIONS) {"enable-extensions-explicit-browser-signin", flag_descriptions::kEnableExtensionsExplicitBrowserSigninName,
diff --git a/chrome/browser/accessibility/media_app/ax_media_app_untrusted_service_browsertest.cc b/chrome/browser/accessibility/media_app/ax_media_app_untrusted_service_browsertest.cc index 85f8706..a79d5f1 100644 --- a/chrome/browser/accessibility/media_app/ax_media_app_untrusted_service_browsertest.cc +++ b/chrome/browser/accessibility/media_app/ax_media_app_untrusted_service_browsertest.cc
@@ -18,6 +18,7 @@ #include "chrome/browser/accessibility/media_app/ax_media_app_service_factory.h" #include "chrome/browser/accessibility/media_app/test/fake_ax_media_app.h" #include "chrome/browser/accessibility/media_app/test/test_ax_media_app_untrusted_service.h" +#include "chrome/browser/preloading/scoped_prewarm_feature_list.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_window.h" @@ -149,6 +150,11 @@ std::unique_ptr<TestAXMediaAppUntrustedService> service_; private: + // TODO(https://crbug.com/423465927): Explore a better approach to make the + // existing tests run with the prewarm feature enabled. + ::test::ScopedPrewarmFeatureList scoped_prewarm_feature_list_{ + ::test::ScopedPrewarmFeatureList::PrewarmState::kDisabled}; + std::optional<content::ScopedAccessibilityModeOverride> mode_override_; };
diff --git a/chrome/browser/actor/OWNERS b/chrome/browser/actor/OWNERS index ffc358c1..6788002 100644 --- a/chrome/browser/actor/OWNERS +++ b/chrome/browser/actor/OWNERS
@@ -1,4 +1,5 @@ bokan@chromium.org +dtapuska@chromium.org mcnee@chromium.org mfoltz@chromium.org rodneyding@google.com
diff --git a/chrome/browser/actor/actor_test_util.cc b/chrome/browser/actor/actor_test_util.cc index 6811beb..921748ee 100644 --- a/chrome/browser/actor/actor_test_util.cc +++ b/chrome/browser/actor/actor_test_util.cc
@@ -47,7 +47,7 @@ using ::content::RenderFrameHost; using ::content::WebContents; using ::optimization_guide::DocumentIdentifierUserData; -using ::optimization_guide::proto::BrowserAction; +using ::optimization_guide::proto::Actions; using ::optimization_guide::proto::ClickAction; using ::optimization_guide::proto::Coordinate; using ::optimization_guide::proto::CreateTabAction; @@ -63,9 +63,9 @@ using tabs::TabHandle; using tabs::TabInterface; -BrowserAction MakeClick(RenderFrameHost& rfh, int content_node_id) { - BrowserAction action; - ClickAction* click = action.add_actions()->mutable_click(); +Actions MakeClick(RenderFrameHost& rfh, int content_node_id) { + Actions actions; + ClickAction* click = actions.add_actions()->mutable_click(); click->mutable_target()->set_content_node_id(content_node_id); click->mutable_target()->mutable_document_identifier()->set_serialized_token( *DocumentIdentifierUserData::GetDocumentIdentifier( @@ -77,77 +77,76 @@ content::WebContents::FromRenderFrameHost(&rfh)); click->set_tab_id(tab->GetHandle().raw_value()); - return action; + return actions; } -BrowserAction MakeClick(TabHandle tab_handle, const gfx::Point& click_point) { - BrowserAction action; - ClickAction* click = action.add_actions()->mutable_click(); +Actions MakeClick(TabHandle tab_handle, const gfx::Point& click_point) { + Actions actions; + ClickAction* click = actions.add_actions()->mutable_click(); Coordinate* coordinate = click->mutable_target()->mutable_coordinate(); coordinate->set_x(click_point.x()); coordinate->set_y(click_point.y()); click->set_click_type(ClickAction::LEFT); click->set_click_count(ClickAction::SINGLE); click->set_tab_id(tab_handle.raw_value()); - return action; + return actions; } -BrowserAction MakeHistoryBack(TabHandle tab_handle) { - BrowserAction action; - HistoryBackAction* back = action.add_actions()->mutable_back(); +Actions MakeHistoryBack(TabHandle tab_handle) { + Actions actions; + HistoryBackAction* back = actions.add_actions()->mutable_back(); back->set_tab_id(tab_handle.raw_value()); - return action; + return actions; } -BrowserAction MakeHistoryForward(TabHandle tab_handle) { - BrowserAction action; - HistoryForwardAction* forward = action.add_actions()->mutable_forward(); +Actions MakeHistoryForward(TabHandle tab_handle) { + Actions actions; + HistoryForwardAction* forward = actions.add_actions()->mutable_forward(); forward->set_tab_id(tab_handle.raw_value()); - return action; + return actions; } -BrowserAction MakeMouseMove(RenderFrameHost& rfh, int content_node_id) { - BrowserAction action; - MoveMouseAction* move = action.add_actions()->mutable_move_mouse(); +Actions MakeMouseMove(RenderFrameHost& rfh, int content_node_id) { + Actions actions; + MoveMouseAction* move = actions.add_actions()->mutable_move_mouse(); move->mutable_target()->set_content_node_id(content_node_id); move->mutable_target()->mutable_document_identifier()->set_serialized_token( *DocumentIdentifierUserData::GetDocumentIdentifier( rfh.GetGlobalFrameToken())); - return action; + return actions; } -BrowserAction MakeMouseMove(const gfx::Point& move_point) { - BrowserAction action; - MoveMouseAction* move = action.add_actions()->mutable_move_mouse(); +Actions MakeMouseMove(const gfx::Point& move_point) { + Actions actions; + MoveMouseAction* move = actions.add_actions()->mutable_move_mouse(); Coordinate* coordinate = move->mutable_target()->mutable_coordinate(); coordinate->set_x(move_point.x()); coordinate->set_y(move_point.y()); - return action; + return actions; } -BrowserAction MakeNavigate(tabs::TabHandle tab_handle, - std::string_view target_url) { - BrowserAction action; - NavigateAction* navigate = action.add_actions()->mutable_navigate(); +Actions MakeNavigate(tabs::TabHandle tab_handle, std::string_view target_url) { + Actions actions; + NavigateAction* navigate = actions.add_actions()->mutable_navigate(); navigate->mutable_url()->assign(target_url); navigate->set_tab_id(tab_handle.raw_value()); - return action; + return actions; } -BrowserAction MakeCreateTab(SessionID window_id, bool foreground) { - BrowserAction action; - CreateTabAction* create_tab = action.add_actions()->mutable_create_tab(); +Actions MakeCreateTab(SessionID window_id, bool foreground) { + Actions actions; + CreateTabAction* create_tab = actions.add_actions()->mutable_create_tab(); create_tab->set_foreground(foreground); create_tab->set_window_id(window_id.id()); - return action; + return actions; } -BrowserAction MakeType(RenderFrameHost& rfh, - int content_node_id, - std::string_view text, - bool follow_by_enter) { - BrowserAction action; - TypeAction* type_action = action.add_actions()->mutable_type(); +Actions MakeType(RenderFrameHost& rfh, + int content_node_id, + std::string_view text, + bool follow_by_enter) { + Actions actions; + TypeAction* type_action = actions.add_actions()->mutable_type(); type_action->mutable_target()->set_content_node_id(content_node_id); type_action->mutable_target() ->mutable_document_identifier() @@ -158,14 +157,14 @@ type_action->set_mode( TypeAction_TypeMode::TypeAction_TypeMode_UNKNOWN_TYPE_MODE); type_action->set_follow_by_enter(follow_by_enter); - return action; + return actions; } -BrowserAction MakeType(const gfx::Point& type_point, - std::string_view text, - bool follow_by_enter) { - BrowserAction action; - TypeAction* type_action = action.add_actions()->mutable_type(); +Actions MakeType(const gfx::Point& type_point, + std::string_view text, + bool follow_by_enter) { + Actions actions; + TypeAction* type_action = actions.add_actions()->mutable_type(); Coordinate* coordinate = type_action->mutable_target()->mutable_coordinate(); coordinate->set_x(type_point.x()); coordinate->set_y(type_point.y()); @@ -174,17 +173,17 @@ type_action->set_mode( TypeAction_TypeMode::TypeAction_TypeMode_UNKNOWN_TYPE_MODE); type_action->set_follow_by_enter(follow_by_enter); - return action; + return actions; } -BrowserAction MakeScroll(RenderFrameHost& rfh, - std::optional<int> content_node_id, - float scroll_offset_x, - float scroll_offset_y) { +Actions MakeScroll(RenderFrameHost& rfh, + std::optional<int> content_node_id, + float scroll_offset_x, + float scroll_offset_y) { CHECK(!scroll_offset_x || !scroll_offset_y) << "Scroll action supports only one axis at a time."; - BrowserAction action; - ScrollAction* scroll = action.add_actions()->mutable_scroll(); + Actions actions; + ScrollAction* scroll = actions.add_actions()->mutable_scroll(); if (content_node_id.has_value()) { scroll->mutable_target()->set_content_node_id(content_node_id.value()); @@ -212,28 +211,28 @@ scroll->set_direction(ScrollAction::UP); scroll->set_distance(-scroll_offset_y); } - return action; + return actions; } -BrowserAction MakeSelect(RenderFrameHost& rfh, - int content_node_id, - std::string_view value) { - BrowserAction action; - SelectAction* select_action = action.add_actions()->mutable_select(); +Actions MakeSelect(RenderFrameHost& rfh, + int content_node_id, + std::string_view value) { + Actions actions; + SelectAction* select_action = actions.add_actions()->mutable_select(); select_action->mutable_target()->set_content_node_id(content_node_id); select_action->mutable_target() ->mutable_document_identifier() ->set_serialized_token(*DocumentIdentifierUserData::GetDocumentIdentifier( rfh.GetGlobalFrameToken())); select_action->set_value(value); - return action; + return actions; } -BrowserAction MakeDragAndRelease(const gfx::Point& from_point, - const gfx::Point& to_point) { - BrowserAction action; +Actions MakeDragAndRelease(const gfx::Point& from_point, + const gfx::Point& to_point) { + Actions actions; DragAndReleaseAction* drag_and_release = - action.add_actions()->mutable_drag_and_release(); + actions.add_actions()->mutable_drag_and_release(); drag_and_release->mutable_from_target()->mutable_coordinate()->set_x( from_point.x()); drag_and_release->mutable_from_target()->mutable_coordinate()->set_y( @@ -242,19 +241,19 @@ to_point.x()); drag_and_release->mutable_to_target()->mutable_coordinate()->set_y( to_point.y()); - return action; + return actions; } -BrowserAction MakeWait() { - BrowserAction action; - action.add_actions()->mutable_wait(); - return action; +Actions MakeWait() { + Actions actions; + actions.add_actions()->mutable_wait(); + return actions; } -BrowserAction MakeAttemptLogin() { - BrowserAction action; - action.add_actions()->mutable_attempt_login(); - return action; +Actions MakeAttemptLogin() { + Actions actions; + actions.add_actions()->mutable_attempt_login(); + return actions; } TabHandle GetTab(content::RenderFrameHost& rfh) { @@ -264,10 +263,10 @@ return tab->GetHandle(); } -BrowserAction MakeScriptTool(content::RenderFrameHost& rfh, - const std::string& name, - const std::string& input_arguments) { - BrowserAction action; +Actions MakeScriptTool(content::RenderFrameHost& rfh, + const std::string& name, + const std::string& input_arguments) { + Actions action; auto* script_tool = action.add_actions()->mutable_script_tool(); script_tool->mutable_document_identifier()->set_serialized_token( *DocumentIdentifierUserData::GetDocumentIdentifier(
diff --git a/chrome/browser/actor/actor_test_util.h b/chrome/browser/actor/actor_test_util.h index d6d49ff6..35348c0 100644 --- a/chrome/browser/actor/actor_test_util.h +++ b/chrome/browser/actor/actor_test_util.h
@@ -49,48 +49,41 @@ ///////////////////////// // Proto action makers -optimization_guide::proto::BrowserAction MakeClick( - content::RenderFrameHost& rfh, - int content_node_id); -optimization_guide::proto::BrowserAction MakeClick( - tabs::TabHandle tab_handle, - const gfx::Point& click_point); -optimization_guide::proto::BrowserAction MakeHistoryBack( +optimization_guide::proto::Actions MakeClick(content::RenderFrameHost& rfh, + int content_node_id); +optimization_guide::proto::Actions MakeClick(tabs::TabHandle tab_handle, + const gfx::Point& click_point); +optimization_guide::proto::Actions MakeHistoryBack(tabs::TabHandle tab_handle); +optimization_guide::proto::Actions MakeHistoryForward( tabs::TabHandle tab_handle); -optimization_guide::proto::BrowserAction MakeHistoryForward( - tabs::TabHandle tab_handle); -optimization_guide::proto::BrowserAction MakeMouseMove( - content::RenderFrameHost& rfh, - int content_node_id); -optimization_guide::proto::BrowserAction MakeMouseMove( - const gfx::Point& move_point); -optimization_guide::proto::BrowserAction MakeNavigate( - tabs::TabHandle tab_handle, - std::string_view target_url); -optimization_guide::proto::BrowserAction MakeCreateTab(SessionID window_id, - bool foreground); -optimization_guide::proto::BrowserAction MakeType(content::RenderFrameHost& rfh, - int content_node_id, - std::string_view text, - bool follow_by_enter); -optimization_guide::proto::BrowserAction MakeType(const gfx::Point& type_point, - std::string_view text, - bool follow_by_enter); -optimization_guide::proto::BrowserAction MakeSelect( - content::RenderFrameHost& rfh, - int content_node_id, - std::string_view value); -optimization_guide::proto::BrowserAction MakeScroll( +optimization_guide::proto::Actions MakeMouseMove(content::RenderFrameHost& rfh, + int content_node_id); +optimization_guide::proto::Actions MakeMouseMove(const gfx::Point& move_point); +optimization_guide::proto::Actions MakeNavigate(tabs::TabHandle tab_handle, + std::string_view target_url); +optimization_guide::proto::Actions MakeCreateTab(SessionID window_id, + bool foreground); +optimization_guide::proto::Actions MakeType(content::RenderFrameHost& rfh, + int content_node_id, + std::string_view text, + bool follow_by_enter); +optimization_guide::proto::Actions MakeType(const gfx::Point& type_point, + std::string_view text, + bool follow_by_enter); +optimization_guide::proto::Actions MakeSelect(content::RenderFrameHost& rfh, + int content_node_id, + std::string_view value); +optimization_guide::proto::Actions MakeScroll( content::RenderFrameHost& rfh, std::optional<int> content_node_id, float scroll_offset_x, float scroll_offset_y); -optimization_guide::proto::BrowserAction MakeDragAndRelease( +optimization_guide::proto::Actions MakeDragAndRelease( const gfx::Point& from_point, const gfx::Point& to_point); -optimization_guide::proto::BrowserAction MakeWait(); -optimization_guide::proto::BrowserAction MakeAttemptLogin(); -optimization_guide::proto::BrowserAction MakeScriptTool( +optimization_guide::proto::Actions MakeWait(); +optimization_guide::proto::Actions MakeAttemptLogin(); +optimization_guide::proto::Actions MakeScriptTool( content::RenderFrameHost& rfh, const std::string& name, const std::string& input_arguments);
diff --git a/chrome/browser/android/DEPS b/chrome/browser/android/DEPS index f74d9b9..9d1396a 100644 --- a/chrome/browser/android/DEPS +++ b/chrome/browser/android/DEPS
@@ -24,10 +24,9 @@ ] specific_include_rules = { - # Special-case where monochrome composes chrome+webview+weblayer + # Special-case where monochrome composes chrome+webview "monochrome_entry_point\.cc": [ "+android_webview", - "+weblayer", ], # Allowed for test setup. "chrome_entry_point_for_test\.cc": [
diff --git a/chrome/browser/ash/app_restore/BUILD.gn b/chrome/browser/ash/app_restore/BUILD.gn index 3ac2159..a5d17bd 100644 --- a/chrome/browser/ash/app_restore/BUILD.gn +++ b/chrome/browser/ash/app_restore/BUILD.gn
@@ -49,6 +49,7 @@ "//base", "//chrome/browser:browser_public_dependencies", "//chrome/browser/profiles:profile", + "//chrome/browser/sessions", "//chrome/common:buildflags", "//chromeos/ash/components/dbus/resourced", "//chromeos/ash/components/scheduler_config",
diff --git a/chrome/browser/ash/arc/boot_phase_monitor/BUILD.gn b/chrome/browser/ash/arc/boot_phase_monitor/BUILD.gn index c240f935..ae090d9 100644 --- a/chrome/browser/ash/arc/boot_phase_monitor/BUILD.gn +++ b/chrome/browser/ash/arc/boot_phase_monitor/BUILD.gn
@@ -12,7 +12,10 @@ "arc_boot_phase_monitor_bridge.h", ] - public_deps = [ "//chrome/browser:browser_public_dependencies" ] + public_deps = [ + "//chrome/browser:browser_public_dependencies", + "//chrome/browser/sessions", + ] deps = [ "//base",
diff --git a/chrome/browser/ash/arc/instance_throttle/BUILD.gn b/chrome/browser/ash/arc/instance_throttle/BUILD.gn index 0ff9ab4..e5ccbc1 100644 --- a/chrome/browser/ash/arc/instance_throttle/BUILD.gn +++ b/chrome/browser/ash/arc/instance_throttle/BUILD.gn
@@ -30,7 +30,10 @@ "arcvm_kiosk_mode_throttle_observer.h", ] - public_deps = [ "//chrome/browser:browser_public_dependencies" ] + public_deps = [ + "//chrome/browser:browser_public_dependencies", + "//chrome/browser/sessions", + ] deps = [ "//ash/public/cpp",
diff --git a/chrome/browser/ash/boca/on_task/on_task_locked_session_navigation_throttle_interactive_ui_test.cc b/chrome/browser/ash/boca/on_task/on_task_locked_session_navigation_throttle_interactive_ui_test.cc index 38012d2..a9dc3179 100644 --- a/chrome/browser/ash/boca/on_task/on_task_locked_session_navigation_throttle_interactive_ui_test.cc +++ b/chrome/browser/ash/boca/on_task/on_task_locked_session_navigation_throttle_interactive_ui_test.cc
@@ -174,6 +174,11 @@ } private: + // TODO(https://crbug.com/423465927): Explore a better approach to make the + // existing tests run with the prewarm feature enabled. + test::ScopedPrewarmFeatureList scoped_prewarm_feature_list_{ + test::ScopedPrewarmFeatureList::PrewarmState::kDisabled}; + base::test::ScopedFeatureList scoped_feature_list_; std::unique_ptr<boca::OnTaskSystemWebAppManagerImpl> system_web_app_manager_; raw_ptr<FakeOnTaskNotificationsManagerDelegate> @@ -190,11 +195,6 @@ embedded_test_server()->AddDefaultHandlers(GetChromeTestDataDir()); ASSERT_TRUE(embedded_test_server()->Start()); } - private: - // TODO(https://crbug.com/423465927): Explore a better approach to make the - // existing tests run with the prewarm feature enabled. - test::ScopedPrewarmFeatureList scoped_prewarm_feature_list_{ - test::ScopedPrewarmFeatureList::PrewarmState::kDisabled}; }; IN_PROC_BROWSER_TEST_F(OnTaskLockedSessionNavigationThrottleInteractiveUITest,
diff --git a/chrome/browser/ash/floating_workspace/BUILD.gn b/chrome/browser/ash/floating_workspace/BUILD.gn index fe081ddf..2c50dd1 100644 --- a/chrome/browser/ash/floating_workspace/BUILD.gn +++ b/chrome/browser/ash/floating_workspace/BUILD.gn
@@ -32,6 +32,7 @@ "//chrome/browser/ash/login/session", "//chrome/browser/ash/profiles", "//chrome/browser/profiles:profile", + "//chrome/browser/sessions", "//chrome/browser/sync", "//chrome/browser/ui/ash/desks", "//chrome/browser/ui/ash/multi_user",
diff --git a/chrome/browser/contextual_cueing/contextual_cueing_service.cc b/chrome/browser/contextual_cueing/contextual_cueing_service.cc index a1fcd061..cdbfc35 100644 --- a/chrome/browser/contextual_cueing/contextual_cueing_service.cc +++ b/chrome/browser/contextual_cueing/contextual_cueing_service.cc
@@ -337,7 +337,7 @@ const std::vector<content::WebContents*>& web_contents_list, bool is_fre, std::optional<std::vector<std::string>> supported_tools, - bool is_focused_tab) { + const content::WebContents* focused_tab) { // Construct base request proto. optimization_guide::proto::ZeroStateSuggestionsRequest request_proto; request_proto.set_is_fre(is_fre); @@ -347,14 +347,15 @@ PopulateSupportedToolsForRequest(supported_tools, pref_service_, &request_proto); // Instantiate the one-of to indicate the request type. - if (is_focused_tab) { + if (focused_tab && web_contents_list.size() == 1) { request_proto.mutable_page_context(); } else { request_proto.mutable_page_context_list(); } return std::make_unique<ZeroStateSuggestionsRequest>( - optimization_guide_keyed_service_, request_proto, web_contents_list); + optimization_guide_keyed_service_, request_proto, web_contents_list, + focused_tab); } void ContextualCueingService:: @@ -388,7 +389,7 @@ auto* zss_request_ptr = zss_data->focused_tab_request(); if (!zss_request_ptr) { auto zss_request = MakeZeroStateSuggestionsRequest( - {web_contents}, is_fre, supported_tools, /*is_focused_tab=*/true); + {web_contents}, is_fre, supported_tools, web_contents); zss_request_ptr = zss_request.get(); zss_data->set_focused_tab_request(std::move(zss_request)); } @@ -404,9 +405,9 @@ std::vector<content::WebContents*> pinned_web_contents, bool is_fre, std::optional<std::vector<std::string>> supported_tools, + const content::WebContents* focused_tab, GlicSuggestionsCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - if (!IsZeroStateSuggestionsEnabled()) { std::move(callback).Run({}); return; @@ -425,7 +426,7 @@ #if BUILDFLAG(ENABLE_GLIC) // Initiate request for suggestions for pinned tabs. pinned_tabs_zero_state_suggestions_request_ = MakeZeroStateSuggestionsRequest( - pinned_web_contents, is_fre, supported_tools, /*is_focused_tab=*/false); + pinned_web_contents, is_fre, supported_tools, focused_tab); pinned_tabs_zero_state_suggestions_request_->AddCallback(base::BindOnce( &OnSuggestionsReceived, base::TimeTicks::Now(), std::move(callback))); #else
diff --git a/chrome/browser/contextual_cueing/contextual_cueing_service.h b/chrome/browser/contextual_cueing/contextual_cueing_service.h index 974d2bf6..225f95b 100644 --- a/chrome/browser/contextual_cueing/contextual_cueing_service.h +++ b/chrome/browser/contextual_cueing/contextual_cueing_service.h
@@ -107,6 +107,7 @@ std::vector<content::WebContents*> pinned_web_contents, bool is_fre, std::optional<std::vector<std::string>> supported_tools, + const content::WebContents* focused_tab, GlicSuggestionsCallback callback); private: @@ -128,7 +129,7 @@ const std::vector<content::WebContents*>& web_contents_list, bool is_fre, std::optional<std::vector<std::string>> supported_tools, - bool is_focused_tab); + const content::WebContents* focused_tab); // Tracker to limit the number of nudges shown over a certain duration. NudgeCapTracker recent_nudge_tracker_;
diff --git a/chrome/browser/contextual_cueing/contextual_cueing_service_browsertest.cc b/chrome/browser/contextual_cueing/contextual_cueing_service_browsertest.cc index c832d14..7c623fe 100644 --- a/chrome/browser/contextual_cueing/contextual_cueing_service_browsertest.cc +++ b/chrome/browser/contextual_cueing/contextual_cueing_service_browsertest.cc
@@ -16,6 +16,7 @@ #include "chrome/browser/optimization_guide/browser_test_util.h" #include "chrome/browser/optimization_guide/mock_optimization_guide_keyed_service.h" #include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h" +#include "chrome/browser/preloading/scoped_prewarm_feature_list.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/search_engines/template_url_service_factory.h" #include "chrome/browser/ui/browser.h" @@ -67,6 +68,12 @@ {features::kGlic, {}}, {features::kTabstripComboButton, {}}}, {}); + // Initialize `scoped_prewarm_feature_list_` after the + // `scoped_feature_list_` that will be removed in the parent class's + // destructor, so that these instances are destroyed in the reversed order. + scoped_prewarm_feature_list_ = + std::make_unique<test::ScopedPrewarmFeatureList>( + test::ScopedPrewarmFeatureList::PrewarmState::kDisabled); } void SetUpOnMainThread() override { @@ -77,6 +84,10 @@ void SetUpCommandLine(base::CommandLine* command_line) override { command_line->AppendSwitch(switches::kGlicDev); } + private: + // TODO(https://crbug.com/423465927): Explore a better approach to make the + // existing tests run with the prewarm feature enabled. + std::unique_ptr<test::ScopedPrewarmFeatureList> scoped_prewarm_feature_list_; }; // A WebContentsObserver that asks for zero state suggestions every
diff --git a/chrome/browser/contextual_cueing/mock_contextual_cueing_service.h b/chrome/browser/contextual_cueing/mock_contextual_cueing_service.h index 52dea9cb..ff3927f 100644 --- a/chrome/browser/contextual_cueing/mock_contextual_cueing_service.h +++ b/chrome/browser/contextual_cueing/mock_contextual_cueing_service.h
@@ -43,6 +43,7 @@ (std::vector<content::WebContents*>, bool, std::optional<std::vector<std::string>>, + const content::WebContents*, GlicSuggestionsCallback)); };
diff --git a/chrome/browser/contextual_cueing/zero_state_suggestions_browsertest.cc b/chrome/browser/contextual_cueing/zero_state_suggestions_browsertest.cc index baadba6..57aa4e0 100644 --- a/chrome/browser/contextual_cueing/zero_state_suggestions_browsertest.cc +++ b/chrome/browser/contextual_cueing/zero_state_suggestions_browsertest.cc
@@ -472,7 +472,7 @@ ContextualCueingServiceFactory::GetForProfile(browser()->profile()) ->GetContextualGlicZeroStateSuggestionsForPinnedTabs( {initial_web_contents, web_contents2}, /*is_fre=*/false, - /*supported_tools=*/{}, future.GetCallback()); + /*supported_tools=*/{}, initial_web_contents, future.GetCallback()); ASSERT_TRUE(future.Wait()); EXPECT_TRUE(future.Get().empty()); } @@ -503,7 +503,7 @@ ContextualCueingServiceFactory::GetForProfile(browser()->profile()) ->GetContextualGlicZeroStateSuggestionsForPinnedTabs( {initial_web_contents, web_contents2}, /*is_fre=*/false, - /*supported_tools=*/{}, future.GetCallback()); + /*supported_tools=*/{}, initial_web_contents, future.GetCallback()); EXPECT_EQ(3u, future.Get().size()); EXPECT_EQ("suggestion 1", future.Get()[0]); EXPECT_EQ("suggestion 2", future.Get()[1]); @@ -529,7 +529,7 @@ ContextualCueingServiceFactory::GetForProfile(browser()->profile()) ->GetContextualGlicZeroStateSuggestionsForPinnedTabs( {initial_web_contents, web_contents2}, /*is_fre=*/false, - /*supported_tools=*/{}, future.GetCallback()); + /*supported_tools=*/{}, nullptr, future.GetCallback()); ASSERT_TRUE(future.Wait()); EXPECT_TRUE(future.Get().empty()); @@ -564,7 +564,7 @@ ContextualCueingServiceFactory::GetForProfile(browser()->profile()) ->GetContextualGlicZeroStateSuggestionsForPinnedTabs( {initial_web_contents, web_contents2}, /*is_fre=*/false, - /*supported_tools=*/{}, future.GetCallback()); + /*supported_tools=*/{}, initial_web_contents, future.GetCallback()); ASSERT_TRUE(future.Wait()); EXPECT_EQ(3u, future.Get().size()); EXPECT_EQ("suggestion 1", future.Get()[0]);
diff --git a/chrome/browser/contextual_cueing/zero_state_suggestions_page_data.cc b/chrome/browser/contextual_cueing/zero_state_suggestions_page_data.cc index e0bb737..b4f2dcd 100644 --- a/chrome/browser/contextual_cueing/zero_state_suggestions_page_data.cc +++ b/chrome/browser/contextual_cueing/zero_state_suggestions_page_data.cc
@@ -379,6 +379,8 @@ optimization_guide::proto::ZeroStatePageContext zero_state_page_context; *zero_state_page_context.mutable_page_context() = page_context; + zero_state_page_context.set_is_focused(is_focused_); + return zero_state_page_context; }
diff --git a/chrome/browser/contextual_cueing/zero_state_suggestions_page_data.h b/chrome/browser/contextual_cueing/zero_state_suggestions_page_data.h index 0db4f51..1d422cdd 100644 --- a/chrome/browser/contextual_cueing/zero_state_suggestions_page_data.h +++ b/chrome/browser/contextual_cueing/zero_state_suggestions_page_data.h
@@ -80,6 +80,8 @@ return weak_ptr_factory_.GetWeakPtr(); } + void set_is_focused_tab() { is_focused_ = true; } + private: friend class content::PageUserData<ZeroStateSuggestionsPageData>; friend class ContextualCueingServiceTestZeroStateSuggestions; @@ -160,6 +162,7 @@ // Tracks the state for a page context request. PageContextCallbackList page_context_callbacks_; + bool is_focused_ = false; // Not owned and guaranteed to outlive `this`. raw_ptr<optimization_guide::PageContextEligibility> page_context_eligibility_; raw_ptr<OptimizationGuideKeyedService> optimization_guide_keyed_service_ =
diff --git a/chrome/browser/contextual_cueing/zero_state_suggestions_request.cc b/chrome/browser/contextual_cueing/zero_state_suggestions_request.cc index d427896..4046ef05 100644 --- a/chrome/browser/contextual_cueing/zero_state_suggestions_request.cc +++ b/chrome/browser/contextual_cueing/zero_state_suggestions_request.cc
@@ -22,7 +22,8 @@ OptimizationGuideKeyedService* optimization_guide_keyed_service, const optimization_guide::proto::ZeroStateSuggestionsRequest& pending_base_request, - const std::vector<content::WebContents*>& requested_tabs) + const std::vector<content::WebContents*>& requested_tabs, + const content::WebContents* focused_tab) : begin_time_(base::TimeTicks::Now()), pending_base_request_(pending_base_request), optimization_guide_keyed_service_(optimization_guide_keyed_service) { @@ -38,6 +39,7 @@ requested_tabs.size(), base::BindOnce(&ZeroStateSuggestionsRequest::OnAllPageContextExtracted, weak_ptr_factory_.GetWeakPtr())); + for (auto* tab : requested_tabs) { auto* zss_data = ZeroStateSuggestionsPageData::GetOrCreateForPage(tab->GetPrimaryPage()); @@ -56,6 +58,10 @@ return; } + // If we're in multitab mode, store the information about focused tab. + if (focused_tab && tab == focused_tab) { + zss_data->set_is_focused_tab(); + } // Otherwise, start grabbing the page context. zss_data->GetPageContext(barrier_callback); }
diff --git a/chrome/browser/contextual_cueing/zero_state_suggestions_request.h b/chrome/browser/contextual_cueing/zero_state_suggestions_request.h index a4994d0..00aa607 100644 --- a/chrome/browser/contextual_cueing/zero_state_suggestions_request.h +++ b/chrome/browser/contextual_cueing/zero_state_suggestions_request.h
@@ -35,7 +35,9 @@ OptimizationGuideKeyedService* optimization_guide_keyed_service, const optimization_guide::proto::ZeroStateSuggestionsRequest& pending_base_request, - const std::vector<content::WebContents*>& requested_tabs); + const std::vector<content::WebContents*>& requested_tabs, + const content::WebContents* focused_tab); + ~ZeroStateSuggestionsRequest(); // Adds a callback for this pending request that gets invoked when suggestions
diff --git a/chrome/browser/enterprise/connectors/analysis/clipboard_request_handler_unittest.cc b/chrome/browser/enterprise/connectors/analysis/clipboard_request_handler_unittest.cc index b14b8fa..0f73d9a 100644 --- a/chrome/browser/enterprise/connectors/analysis/clipboard_request_handler_unittest.cc +++ b/chrome/browser/enterprise/connectors/analysis/clipboard_request_handler_unittest.cc
@@ -80,7 +80,7 @@ std::string email() const override { return "test@user.com"; } - std::string url() const override { return kUrl; } + const GURL& url() const override { return tab_url_; } const GURL& tab_url() const override { return tab_url_; }
diff --git a/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.cc b/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.cc index 0dbd985..48e15c6 100644 --- a/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.cc +++ b/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.cc
@@ -1003,8 +1003,8 @@ return GetProfileEmail(profile_); } -std::string ContentAnalysisDelegate::url() const { - return url_.spec(); +const GURL& ContentAnalysisDelegate::url() const { + return url_; } const GURL& ContentAnalysisDelegate::tab_url() const {
diff --git a/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.h b/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.h index c3f69549..32d5ef7 100644 --- a/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.h +++ b/chrome/browser/enterprise/connectors/analysis/content_analysis_delegate.h
@@ -280,7 +280,7 @@ std::string tab_title() const override; std::string user_action_id() const override; std::string email() const override; - std::string url() const override; + const GURL& url() const override; const GURL& tab_url() const override; ContentAnalysisRequest::Reason reason() const override; google::protobuf::RepeatedPtrField<::safe_browsing::ReferrerChainEntry>
diff --git a/chrome/browser/enterprise/connectors/analysis/content_analysis_info.cc b/chrome/browser/enterprise/connectors/analysis/content_analysis_info.cc index af865377..87a5b68 100644 --- a/chrome/browser/enterprise/connectors/analysis/content_analysis_info.cc +++ b/chrome/browser/enterprise/connectors/analysis/content_analysis_info.cc
@@ -114,7 +114,7 @@ NOTREACHED(); } -std::string ContentAreaUserProvider::url() const { +const GURL& ContentAreaUserProvider::url() const { NOTREACHED(); } @@ -144,8 +144,8 @@ im_(IdentityManagerFactory::GetForProfile(Profile::FromBrowserContext( content::DownloadItemUtils::GetBrowserContext(&download_item)))) {} -std::string DownloadContentAreaUserProvider::url() const { - return url_.spec(); +const GURL& DownloadContentAreaUserProvider::url() const { + return url_; } const GURL& DownloadContentAreaUserProvider::tab_url() const {
diff --git a/chrome/browser/enterprise/connectors/analysis/content_analysis_info.h b/chrome/browser/enterprise/connectors/analysis/content_analysis_info.h index 1c5e8854..f1b35b48 100644 --- a/chrome/browser/enterprise/connectors/analysis/content_analysis_info.h +++ b/chrome/browser/enterprise/connectors/analysis/content_analysis_info.h
@@ -48,7 +48,7 @@ std::string tab_title() const override; std::string user_action_id() const override; std::string email() const override; - std::string url() const override; + const GURL& url() const override; const GURL& tab_url() const override; ContentAnalysisRequest::Reason reason() const override; google::protobuf::RepeatedPtrField<::safe_browsing::ReferrerChainEntry> @@ -71,7 +71,7 @@ const download::DownloadItem& download_item); // ContentAnalysisInfo: - std::string url() const override; + const GURL& url() const override; const GURL& tab_url() const override; signin::IdentityManager* identity_manager() const override;
diff --git a/chrome/browser/enterprise/connectors/analysis/file_transfer_analysis_delegate.cc b/chrome/browser/enterprise/connectors/analysis/file_transfer_analysis_delegate.cc index b54c1d3a..2c47fe7 100644 --- a/chrome/browser/enterprise/connectors/analysis/file_transfer_analysis_delegate.cc +++ b/chrome/browser/enterprise/connectors/analysis/file_transfer_analysis_delegate.cc
@@ -450,8 +450,8 @@ return GetProfileEmail(profile_); } -std::string FileTransferAnalysisDelegate::url() const { - return ""; +const GURL& FileTransferAnalysisDelegate::url() const { + return GURL::EmptyGURL(); } const GURL& FileTransferAnalysisDelegate::tab_url() const {
diff --git a/chrome/browser/enterprise/connectors/analysis/file_transfer_analysis_delegate.h b/chrome/browser/enterprise/connectors/analysis/file_transfer_analysis_delegate.h index 4d4b5ea5..4480294 100644 --- a/chrome/browser/enterprise/connectors/analysis/file_transfer_analysis_delegate.h +++ b/chrome/browser/enterprise/connectors/analysis/file_transfer_analysis_delegate.h
@@ -173,7 +173,7 @@ std::string tab_title() const override; std::string user_action_id() const override; std::string email() const override; - std::string url() const override; + const GURL& url() const override; const GURL& tab_url() const override; ContentAnalysisRequest::Reason reason() const override; google::protobuf::RepeatedPtrField<::safe_browsing::ReferrerChainEntry>
diff --git a/chrome/browser/enterprise/connectors/analysis/files_request_handler_unittest.cc b/chrome/browser/enterprise/connectors/analysis/files_request_handler_unittest.cc index 8653c3e..be50ac8e 100644 --- a/chrome/browser/enterprise/connectors/analysis/files_request_handler_unittest.cc +++ b/chrome/browser/enterprise/connectors/analysis/files_request_handler_unittest.cc
@@ -184,7 +184,7 @@ std::string email() const override { return "test@user.com"; } - std::string url() const override { return kTestUrl; } + const GURL& url() const override { return tab_url(); } const GURL& tab_url() const override { static GURL url(kTestUrl);
diff --git a/chrome/browser/enterprise/connectors/analysis/page_print_request_handler_unittest.cc b/chrome/browser/enterprise/connectors/analysis/page_print_request_handler_unittest.cc index aea6714..be44abeb 100644 --- a/chrome/browser/enterprise/connectors/analysis/page_print_request_handler_unittest.cc +++ b/chrome/browser/enterprise/connectors/analysis/page_print_request_handler_unittest.cc
@@ -77,7 +77,7 @@ std::string email() const override { return "test@user.com"; } - std::string url() const override { return kUrl; } + const GURL& url() const override { return url_; } const GURL& tab_url() const override { return tab_url_; } @@ -97,6 +97,7 @@ } private: + GURL url_{kUrl}; GURL tab_url_{kTabUrl}; AnalysisSettings settings_; };
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn index a0aa8d4..ec4a050 100644 --- a/chrome/browser/extensions/BUILD.gn +++ b/chrome/browser/extensions/BUILD.gn
@@ -921,6 +921,7 @@ "//chrome/browser/search/background", "//chrome/browser/search_engine_choice", "//chrome/browser/search_engines", + "//chrome/browser/sessions", "//chrome/browser/sync", "//chrome/browser/themes", "//chrome/browser/ui:ui_features",
diff --git a/chrome/browser/extensions/api/autofill_private/autofill_util.cc b/chrome/browser/extensions/api/autofill_private/autofill_util.cc index 492ec4d2..76265713 100644 --- a/chrome/browser/extensions/api/autofill_private/autofill_util.cc +++ b/chrome/browser/extensions/api/autofill_private/autofill_util.cc
@@ -330,8 +330,21 @@ PayOverTimeIssuerEntryList GeneratePayOverTimeIssuerList( const autofill::PaymentsDataManager& paydm) { - return base::ToVector(paydm.GetLinkedBnplIssuers(), - &BnplIssuerToPayOverTimeIssuerEntry); + std::vector<autofill::BnplIssuer> linked_issuers = + base::ToVector(paydm.GetLinkedBnplIssuers()); + + // Remove the issuer entry if a BNPL issuer is linked externally, due to + // missing terms of services acceptance. + linked_issuers.erase( + std::remove_if( + linked_issuers.begin(), linked_issuers.end(), + [](autofill::BnplIssuer& issuer) { + return issuer.payment_instrument()->action_required().contains( + autofill::PaymentInstrument::ActionRequired::kAcceptTos); + }), + linked_issuers.end()); + + return base::ToVector(linked_issuers, &BnplIssuerToPayOverTimeIssuerEntry); } std::optional<api::autofill_private::AccountInfo> GetAccountInfo(
diff --git a/chrome/browser/extensions/api/autofill_private/autofill_util_unittest.cc b/chrome/browser/extensions/api/autofill_private/autofill_util_unittest.cc index 1c1ff52d..a1a44cd 100644 --- a/chrome/browser/extensions/api/autofill_private/autofill_util_unittest.cc +++ b/chrome/browser/extensions/api/autofill_private/autofill_util_unittest.cc
@@ -8,11 +8,13 @@ #include "base/functional/callback_forward.h" #include "base/test/mock_callback.h" +#include "base/test/scoped_feature_list.h" #include "chrome/test/base/in_process_browser_test.h" #include "components/autofill/core/browser/data_manager/payments/test_payments_data_manager.h" #include "components/autofill/core/browser/data_manager/test_personal_data_manager.h" #include "components/autofill/core/browser/test_utils/autofill_test_utils.h" #include "components/autofill/core/common/autofill_test_utils.h" +#include "components/autofill/core/common/dense_set.h" #include "components/device_reauth/mock_device_authenticator.h" #include "content/public/test/browser_task_environment.h" #include "content/public/test/browser_test.h" @@ -32,6 +34,11 @@ return arg.metadata->is_local == is_local; } +MATCHER_P(MatchesBnplIssuer, bnpl_issuer, "") { + return arg.issuer_id == + autofill::ConvertToBnplIssuerIdString(bnpl_issuer.issuer_id()); +} + class AutofillUtilTest : public InProcessBrowserTest { public: AutofillUtilTest() = default; @@ -50,6 +57,24 @@ mock_device_authenticator_; }; +class AutofillUtilTestForBnpl : public AutofillUtilTest { + public: + AutofillUtilTestForBnpl() { + scoped_feature_list_.InitWithFeatures( + /*enabled_features=*/ + {autofill::features::kAutofillEnableBuyNowPayLater, + autofill::features::kAutofillEnableBuyNowPayLaterSyncing, + autofill::features::kAutofillEnableBuyNowPayLaterForKlarna}, + /*disabled_features=*/{}); + } + + AutofillUtilTestForBnpl(const AutofillUtilTestForBnpl&) = delete; + AutofillUtilTestForBnpl& operator=(const AutofillUtilTestForBnpl&) = delete; + + private: + base::test::ScopedFeatureList scoped_feature_list_; +}; + IN_PROC_BROWSER_TEST_F(AutofillUtilTest, GenerateIbanList) { autofill::TestPaymentsDataManager paydm; paydm.SetAutofillWalletImportEnabled(true); @@ -102,6 +127,47 @@ #endif } +IN_PROC_BROWSER_TEST_F(AutofillUtilTestForBnpl, + GeneratePayOverTimeIssuerList_IssuerLinkedExternally) { + autofill::TestPaymentsDataManager paydm; + paydm.SetAutofillWalletImportEnabled(true); + paydm.SetAutofillPaymentMethodsEnabled(true); + paydm.SetIsAutofillBnplPrefEnabled(true); + autofill::BnplIssuer issuer_affirm = autofill::test::GetTestLinkedBnplIssuer( + autofill::BnplIssuer::IssuerId::kBnplAffirm); + autofill::BnplIssuer issuer_klarna = autofill::test::GetTestLinkedBnplIssuer( + autofill::BnplIssuer::IssuerId::kBnplKlarna, + /*action_required=*/autofill::DenseSet( + {autofill::PaymentInstrument::ActionRequired::kAcceptTos})); + paydm.AddBnplIssuer(issuer_affirm); + paydm.AddBnplIssuer(issuer_klarna); + + PayOverTimeIssuerEntryList bnpl_issuer_list = + GeneratePayOverTimeIssuerList(paydm); + EXPECT_THAT(bnpl_issuer_list, + UnorderedElementsAre(MatchesBnplIssuer(issuer_affirm))); +} + +IN_PROC_BROWSER_TEST_F(AutofillUtilTestForBnpl, + GeneratePayOverTimeIssuerList_IssuerTosAccepted) { + autofill::TestPaymentsDataManager paydm; + paydm.SetAutofillWalletImportEnabled(true); + paydm.SetAutofillPaymentMethodsEnabled(true); + paydm.SetIsAutofillBnplPrefEnabled(true); + autofill::BnplIssuer issuer_affirm = autofill::test::GetTestLinkedBnplIssuer( + autofill::BnplIssuer::IssuerId::kBnplAffirm); + autofill::BnplIssuer issuer_klarna = autofill::test::GetTestLinkedBnplIssuer( + autofill::BnplIssuer::IssuerId::kBnplKlarna); + paydm.AddBnplIssuer(issuer_affirm); + paydm.AddBnplIssuer(issuer_klarna); + + PayOverTimeIssuerEntryList bnpl_issuer_list = + GeneratePayOverTimeIssuerList(paydm); + EXPECT_THAT(bnpl_issuer_list, + UnorderedElementsAre(MatchesBnplIssuer(issuer_affirm), + MatchesBnplIssuer(issuer_klarna))); +} + } // namespace } // namespace extensions::autofill_util
diff --git a/chrome/browser/extensions/process_management_browsertest.cc b/chrome/browser/extensions/process_management_browsertest.cc index 02f779c9..9b5fc7d 100644 --- a/chrome/browser/extensions/process_management_browsertest.cc +++ b/chrome/browser/extensions/process_management_browsertest.cc
@@ -12,6 +12,7 @@ #include "build/build_config.h" #include "chrome/browser/extensions/extension_apitest.h" #include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/preloading/scoped_prewarm_feature_list.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" @@ -62,6 +63,11 @@ host_resolver()->AddRule("*", "127.0.0.1"); } + // TODO(https://crbug.com/423465927): Explore a better approach to make the + // existing tests run with the prewarm feature enabled. + test::ScopedPrewarmFeatureList scoped_prewarm_feature_list_{ + test::ScopedPrewarmFeatureList::PrewarmState::kDisabled}; + base::test::ScopedFeatureList disabled_feature_list_; };
diff --git a/chrome/browser/fast_checkout/fast_checkout_client_impl.cc b/chrome/browser/fast_checkout/fast_checkout_client_impl.cc index c064da5..0be30816 100644 --- a/chrome/browser/fast_checkout/fast_checkout_client_impl.cc +++ b/chrome/browser/fast_checkout/fast_checkout_client_impl.cc
@@ -57,10 +57,9 @@ bool is_credit_card_form) { for (const std::unique_ptr<autofill::AutofillField>& field : fields) { if (IsVisibleTextField(*field) && field->value().empty() && - ((!is_credit_card_form && - autofill::IsAddressType(field->Type().GetStorableType())) || - (is_credit_card_form && - field->Type().GetStorableType() == autofill::CREDIT_CARD_NUMBER))) { + (is_credit_card_form + ? field->Type().GetStorableType() == autofill::CREDIT_CARD_NUMBER + : field->Type().GetAddressType() != autofill::UNKNOWN_TYPE)) { return field.get(); } }
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index 37b3c59..3afb9f7f 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json
@@ -1021,6 +1021,11 @@ "owners": [ "gangwu@chromium.org", "chrome-mobile-search@google.com" ], "expiry_milestone": 160 }, + { + "name": "avatar-button-sync-promo", + "owners": [ "rsult@google.com", "chrome-signin-desktop-team@google.com" ], + "expiry_milestone": 148 + }, { "name": "back-forward-cache", "owners": [ "bfcache-dev@chromium.org" ], @@ -6169,6 +6174,11 @@ "expiry_milestone": 145 }, { + "name": "jupiter-screensaver", + "owners": [ "cowmoo@google.com", "cros-p13n-eng@google.com" ], + "expiry_milestone": 145 + }, + { "name": "keyboard-lock-prompt", "owners": ["muyaoxu@google.com", "liberato@chromium.org", "openscreen-eng@google.com"], "expiry_milestone": 140
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index a6aa313..3124f8f 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -4146,6 +4146,13 @@ "See https://github.com/WICG/translation-api/blob/main/README.md"; #endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) +#if BUILDFLAG(IS_WIN) +const char kAvatarButtonSyncPromoName[] = "Avatar Sync Promo"; +const char kAvatarButtonSyncPromoDescription[] = + "Enables the avatar button sync promo for eligible users. Only available " + "on Windows."; +#endif + const char kTreatInsecureOriginAsSecureName[] = "Insecure origins treated as secure"; const char kTreatInsecureOriginAsSecureDescription[] = @@ -7203,6 +7210,10 @@ const char kImeUsEnglishModelUpdateDescription[] = "Enable updated US English IME language models for native IME"; +const char kJupiterScreensaverName[] = "Jupiter screensaver"; +const char kJupiterScreensaverDescription[] = + "Enable Jupiter screensaver on more device types."; + const char kCrosComponentsName[] = "Cros Components"; const char kCrosComponentsDescription[] = "Enable cros-component UI elements, replacing other elements.";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index 21978a7..52c0aed 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -2371,6 +2371,11 @@ extern const char kTranslationAPIDescription[]; #endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) +#if BUILDFLAG(IS_WIN) +extern const char kAvatarButtonSyncPromoName[]; +extern const char kAvatarButtonSyncPromoDescription[]; +#endif + extern const char kTreatInsecureOriginAsSecureName[]; extern const char kTreatInsecureOriginAsSecureDescription[]; @@ -4157,6 +4162,9 @@ extern const char kImeUsEnglishModelUpdateName[]; extern const char kImeUsEnglishModelUpdateDescription[]; +extern const char kJupiterScreensaverName[]; +extern const char kJupiterScreensaverDescription[]; + extern const char kCrosComponentsName[]; extern const char kCrosComponentsDescription[];
diff --git a/chrome/browser/glic/glic_zero_state_suggestions_manager.cc b/chrome/browser/glic/glic_zero_state_suggestions_manager.cc index 44f39b6..126261c 100644 --- a/chrome/browser/glic/glic_zero_state_suggestions_manager.cc +++ b/chrome/browser/glic/glic_zero_state_suggestions_manager.cc
@@ -97,9 +97,13 @@ MakePendingSuggestionsPtr(), mojom::ZeroStateSuggestionsOptions(is_first_run, supported_tools)); + FocusedTabData focused_tab_data = sharing_manager_->GetFocusedTabData(); + content::WebContents* active_web_contents = + focused_tab_data.focus() ? focused_tab_data.focus()->GetContents() + : nullptr; contextual_cueing_service_ ->GetContextualGlicZeroStateSuggestionsForPinnedTabs( - pinned_tab_data, is_first_run, supported_tools, + pinned_tab_data, is_first_run, supported_tools, active_web_contents, mojo::WrapCallbackWithDefaultInvokeIfNotRun( base::BindOnce(&GlicZeroStateSuggestionsManager:: OnZeroStateSuggestionsNotify, @@ -157,6 +161,7 @@ contextual_cueing_service_ ->GetContextualGlicZeroStateSuggestionsForPinnedTabs( pinned_tabs, is_first_run, supported_tools, + /* focused_tab=*/nullptr, mojo::WrapCallbackWithDefaultInvokeIfNotRun( base::BindOnce(&GlicZeroStateSuggestionsManager:: OnZeroStateSuggestionsFetched,
diff --git a/chrome/browser/glic/host/context/glic_sharing_manager_impl.h b/chrome/browser/glic/host/context/glic_sharing_manager_impl.h index 87f084e..249021e 100644 --- a/chrome/browser/glic/host/context/glic_sharing_manager_impl.h +++ b/chrome/browser/glic/host/context/glic_sharing_manager_impl.h
@@ -118,7 +118,7 @@ // The profile for which to manage sharing. raw_ptr<Profile> profile_; - // Enables providing sharing-related-related input to metrics. + // Enables providing sharing-related input to metrics. raw_ptr<GlicMetrics> metrics_; };
diff --git a/chrome/browser/glic/host/glic.mojom b/chrome/browser/glic/host/glic.mojom index 58b2c6f4..a83eead 100644 --- a/chrome/browser/glic/host/glic.mojom +++ b/chrome/browser/glic/host/glic.mojom
@@ -157,24 +157,6 @@ // LINT.ThenChange(//tools/metrics/histograms/metadata/glic/enums.xml:CreateTaskErrorReason) -// Reason for failure while acting in a focused tab. This MUST be kept in -// sync with the glic_api.ts enum. -// LINT.IfChange(ActInFocusedTabErrorReason) -[Stable, Extensible] -enum ActInFocusedTabErrorReason { - [Default] kUnknown = 0, - // Capturing Context after acting failed. - kGetContextFailed = 1, - // The action proto is invalid. - kInvalidActionProto = 2, - // Action target is not found. - kTargetNotFound = 3, - // Failed to start a new task. - [MinVersion=1] kFailedToStartTask = 4, -}; - -// LINT.ThenChange(//tools/metrics/histograms/metadata/glic/enums.xml:ActInFocusedTabErrorReason) - // The state of an actor task. This MUST be kept in sync with the glic_api.ts // enum. [Stable, Extensible] @@ -208,11 +190,6 @@ Screenshot screenshot; }; -union ActInFocusedTabResult { - ActInFocusedTabErrorReason error_reason; - ActInFocusedTabResponse act_in_focused_tab_response; -}; - // Information about the user profile. struct UserProfileInfo { // Profile avatar image bitmap. @@ -560,18 +537,6 @@ PerformActions(array<uint8> actions_proto) => result<mojo_base.mojom.ProtoWrapper, PerformActionsErrorReason>; - // TODO(crbug.com/411462297) DEPRECATED - new callers should use - // PerformActions. - // - // Starts an action in the currently focused tab. `action_proto` is a - // serialized proto of type optimization_guide::proto::BrowserAction. Returns - // the result of the action when it is completed along with an observation of - // the page, taken using the given `options`. - // - // It is an error to act with a paused task. - ActInFocusedTab(array<uint8> action_proto, GetTabContextOptions options) - => (ActInFocusedTabResult result); - // Immediately stops the actor from taking any further actions associated with // the given id and exits acting mode. Any in flight actions are canceled and // their callbacks will be (asynchronously) invoked. No-op if a task with the @@ -976,17 +941,6 @@ AnnotatedPageData? annotated_page_data; }; -struct ActInFocusedTabResponse { - // The context of the focused tab after the action was completed. - TabContext tab_context; - // The outcome of the action. - // Note that this is an enum ActionResultCode from chrome/common/actor.mojom. - // It would be tedious to import the actual enum here and the renderer doesn't - // need the type, so we just use an int here. It is expected that the client - // has an equivalent enum definition. See http://shortn/_gLyPxrRm6p - int32 action_result; -}; - // Information about a web page being rendered in a tab. struct WebPageData { // Main document of the page.
diff --git a/chrome/browser/glic/host/glic_actor_controller.cc b/chrome/browser/glic/host/glic_actor_controller.cc index 225c1f3d..971293b 100644 --- a/chrome/browser/glic/host/glic_actor_controller.cc +++ b/chrome/browser/glic/host/glic_actor_controller.cc
@@ -8,38 +8,19 @@ #include <cstddef> #include <memory> -#include "base/feature_list.h" #include "base/location.h" -#include "base/metrics/histogram_macros.h" -#include "base/task/sequenced_task_runner.h" #include "base/types/cxx23_to_underlying.h" #include "chrome/browser/actor/actor_keyed_service.h" #include "chrome/browser/actor/actor_task.h" #include "chrome/browser/actor/aggregated_journal.h" -#include "chrome/browser/actor/browser_action_util.h" #include "chrome/browser/actor/execution_engine.h" #include "chrome/browser/glic/host/context/glic_page_context_fetcher.h" #include "chrome/browser/glic/host/glic.mojom.h" -#include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_finder.h" #include "chrome/common/actor.mojom.h" -#include "chrome/common/actor/action_result.h" -#include "chrome/common/chrome_features.h" -#include "components/optimization_guide/proto/features/actions_data.pb.h" -#include "components/optimization_guide/proto/features/model_prototyping.pb.h" #include "components/tabs/public/tab_interface.h" -#include "third_party/abseil-cpp/absl/strings/str_format.h" namespace glic { -using ::actor::ActorKeyedService; -using ::actor::ActorTask; -using ::actor::BuildToolRequest; -using ::actor::BuildToolRequestResult; -using ::actor::TaskId; -using ::actor::ToolRequest; -using ::actor::ToolRequestList; - namespace { mojom::GetTabContextOptionsPtr ActionableOptions( @@ -52,70 +33,6 @@ return actionable_context_options; } -mojom::ActInFocusedTabResultPtr MakeActErrorResult( - mojom::ActInFocusedTabErrorReason error_reason) { - mojom::ActInFocusedTabResultPtr result = - mojom::ActInFocusedTabResult::NewErrorReason(error_reason); - UMA_HISTOGRAM_ENUMERATION("Glic.Action.ActInFocusedTabErrorReason", - result->get_error_reason()); - return result; -} - -void PostTaskForActCallback( - mojom::WebClientHandler::ActInFocusedTabCallback callback, - mojom::ActInFocusedTabErrorReason error_reason) { - mojom::ActInFocusedTabResultPtr result = MakeActErrorResult(error_reason); - - base::SequencedTaskRunner::GetCurrentDefault()->PostTask( - FROM_HERE, base::BindOnce(std::move(callback), std::move(result))); -} - -void OnFetchPageContext( - const GURL& url, - std::unique_ptr<actor::AggregatedJournal::PendingAsyncEntry> journal_entry, - actor::mojom::ActionResultCode result_code, - mojom::WebClientHandler::ActInFocusedTabCallback callback, - base::WeakPtr<actor::ExecutionEngine> execution_engine, - mojom::GetContextResultPtr tab_context_result) { - if (tab_context_result->is_error_reason()) { - journal_entry->EndEntry(tab_context_result->get_error_reason()); - mojom::ActInFocusedTabResultPtr result = MakeActErrorResult( - mojom::ActInFocusedTabErrorReason::kGetContextFailed); - std::move(callback).Run(std::move(result)); - return; - } - - if (execution_engine && - tab_context_result->get_tab_context()->annotated_page_data && - tab_context_result->get_tab_context() - ->annotated_page_data->annotated_page_content.has_value()) { - execution_engine->DidObserveContext( - tab_context_result->get_tab_context() - ->annotated_page_data->annotated_page_content.value()); - } - - mojom::ActInFocusedTabResultPtr result = - mojom::ActInFocusedTabResult::NewActInFocusedTabResponse( - mojom::ActInFocusedTabResponse::New( - std::move(tab_context_result->get_tab_context()), - base::to_underlying(result_code))); - - std::move(callback).Run(std::move(result)); -} - -void LogAddTabError(actor::mojom::ActionResultPtr result) { - if (!actor::IsOk(*result)) { - LOG(DFATAL) - << "Unexpected error when calling AddTab from GlicActorController::Act " - "(crbug.com/431239173): " - << actor::ToDebugString(*result); - } -} - -BASE_FEATURE(kGlicProvideObservationOnActionFailure, - "GlicProvideObservationOnActionFailure", - base::FEATURE_DISABLED_BY_DEFAULT); - } // namespace GlicActorController::GlicActorController(Profile* profile) : profile_(profile) { @@ -125,82 +42,6 @@ GlicActorController::~GlicActorController() = default; -void GlicActorController::Act( - const optimization_guide::proto::BrowserAction& action, - const mojom::GetTabContextOptions& options, - mojom::WebClientHandler::ActInFocusedTabCallback callback) { - auto* actor_service = actor::ActorKeyedService::Get(profile_); - CHECK(actor_service); - - actor_service->GetJournal().Log( - GURL(), TaskId(action.task_id()), actor::mojom::JournalTrack::kActor, - "GlicActInFocusedTab", - absl::StrFormat("Proto: %s", actor::ToBase64(action))); - - actor::BuildToolRequestResult result = - actor::BuildToolRequest(action, /*deprecated_fallback_tab=*/nullptr); - - if (!result.has_value()) { - actor::ActorKeyedService::Get(profile_)->GetJournal().Log( - /*url=*/GURL(), actor::TaskId(), actor::mojom::JournalTrack::kActor, - "ActImpl", - absl::StrFormat("Invalid BrowserAction proto[%d]", result.error())); - PostTaskForActCallback( - std::move(callback), - mojom::ActInFocusedTabErrorReason::kInvalidActionProto); - return; - } - - ToolRequestList& tool_requests = result.value(); - - // TODO(crbug.com/431325114): Once the front end injected CreateTabAction - // provides a task ID we can remove the GetMostRecentTask branch. - actor::ActorTask* task = - action.has_task_id() && action.task_id() != 0 - ? actor_service->GetTask(actor::TaskId(action.task_id())) - : actor_service->GetMostRecentTask(); - - // TODO(crbug.com/431239173): It's not clear what should happen if the current - // task has no observed tabs in its set yet, and the incoming actions will not - // add one (e.g. first action in a new task is Wait). This workaround - // preserves existing behavior, we'll use the currently focused tab. Long term - // the API should have support to deal with this case (Should we return an - // empty observation? Should we return an error?). - if (task && task->GetTabs().empty()) { - bool will_observe_tab = std::ranges::any_of( - tool_requests, [](const std::unique_ptr<ToolRequest>& request) { - return request->AddsTabToObservationSet(); - }); - - if (!will_observe_tab) { - actor_service->GetJournal().Log( - /*url=*/GURL(), task->id(), actor::mojom::JournalTrack::kActor, - "[Warning] No observable tab", - "Action will end without an observable tab, adding active tab."); - - // Get the most recently active browser for this profile. - Browser* browser = chrome::FindTabbedBrowser( - profile_, /*match_original_profiles=*/false); - // If no browser exists create one. - if (!browser) { - browser = Browser::Create( - Browser::CreateParams(profile_, /*user_gesture=*/false)); - } - // TODO(crbug.com/431239173): We should remove this call as the UI has - // no mechanism for reporting errors when launching on this tab. - task->AddTab(browser->GetActiveTabInterface()->GetHandle(), - base::BindOnce(&LogAddTabError)); - } - } - - ActorKeyedService::PerformActionsCallback action_callback = - base::BindOnce(&GlicActorController::OnActionFinished, GetWeakPtr(), - task->id(), options, std::move(callback)); - actor_service->PerformActions(task ? task->id() : TaskId(), - std::move(tool_requests), - std::move(action_callback)); -} - // TODO(mcnee): Determine if we need additional mechanisms, within the browser, // to stop a task. void GlicActorController::StopTask(actor::TaskId task_id) { @@ -242,72 +83,6 @@ std::move(callback)); } -void GlicActorController::OnActionFinished( - actor::TaskId task_id, - const mojom::GetTabContextOptions& options, - mojom::WebClientHandler::ActInFocusedTabCallback callback, - actor::mojom::ActionResultCode result_code, - std::optional<size_t> index_of_failed_action) const { - if (!actor::IsOk(result_code) && - !ProvideObservationOnActionFailureEnabled()) { - PostTaskForActCallback(std::move(callback), - mojom::ActInFocusedTabErrorReason::kTargetNotFound); - return; - } - - actor::ActorTask* task = - actor::ActorKeyedService::Get(profile_)->GetTask(task_id); - CHECK(task); - - actor::AggregatedJournal& journal = - actor::ActorKeyedService::Get(profile_)->GetJournal(); - if (task->GetTabs().size() != 1) { - journal.Log(GURL::EmptyGURL(), task_id, actor::mojom::JournalTrack::kActor, - "[Warning] Unexpected number of tabs", - absl::StrFormat("Expect 1 observable tab but have [%d]", - task->GetTabs().size())); - } - - tabs::TabInterface* tab = task->GetTabForObservation(); - - // TODO(https://crbug.com/398271171): Remove when the actor coordinator - // handles getting a new observation. - // TODO(https://crbug.com/402086398): Figure out if/how this can be shared - // with GlicKeyedService::GetContextFromFocusedTab(). It's not clear yet if - // the same permission checks, etc. should apply here. - if (tab) { - const GURL& url = tab->GetContents()->GetLastCommittedURL(); - auto journal_entry = journal.CreatePendingAsyncEntry( - url, task_id, actor::mojom::JournalTrack::kActor, "FetchPageContext", - "TabHandle:" + base::ToString(tab->GetHandle())); - - FetchPageContext(tab, *ActionableOptions(options), - base::BindOnce(OnFetchPageContext, url, - std::move(journal_entry), result_code, - std::move(callback), - task->GetExecutionEngine()->GetWeakPtr())); - } else { - journal.Log(GURL::EmptyGURL(), task_id, actor::mojom::JournalTrack::kActor, - "FetchPageContext", "Tab is gone"); - PostTaskForActCallback(std::move(callback), - mojom::ActInFocusedTabErrorReason::kTargetNotFound); - } -} - -// static -bool GlicActorController::ProvideObservationOnActionFailureEnabled() { - return base::FeatureList::IsEnabled(kGlicProvideObservationOnActionFailure); -} - -base::WeakPtr<const GlicActorController> GlicActorController::GetWeakPtr() - const { - return weak_ptr_factory_.GetWeakPtr(); -} - -base::WeakPtr<GlicActorController> GlicActorController::GetWeakPtr() { - return weak_ptr_factory_.GetWeakPtr(); -} - actor::ActorTask* GlicActorController::GetCurrentTask() const { return actor::ActorKeyedService::Get(profile_)->GetMostRecentTask(); }
diff --git a/chrome/browser/glic/host/glic_actor_controller.h b/chrome/browser/glic/host/glic_actor_controller.h index 11a9d4e..5884a52 100644 --- a/chrome/browser/glic/host/glic_actor_controller.h +++ b/chrome/browser/glic/host/glic_actor_controller.h
@@ -31,15 +31,6 @@ GlicActorController& operator=(const GlicActorController&) = delete; ~GlicActorController(); - // ActorKeyedService, the underlying framework, supports multi-tab actuation. - // But this class does not because it does not expose the concept of - // start/stop task. Instead it keeps track of any ongoing task, and implicitly - // creates one for Act() if one does not already exist. - // Invokes the actor to complete an action. - void Act(const optimization_guide::proto::BrowserAction& action, - const mojom::GetTabContextOptions& options, - mojom::WebClientHandler::ActInFocusedTabCallback callback); - void StopTask(actor::TaskId task_id); void PauseTask(actor::TaskId task_id); void ResumeTask( @@ -47,26 +38,10 @@ const mojom::GetTabContextOptions& context_options, glic::mojom::WebClientHandler::ResumeActorTaskCallback callback); - // TODO(crbug.com/418280472): This temporarily gates providing observations - // after action failure, to avoid confusing the client before it's updated. - static bool ProvideObservationOnActionFailureEnabled(); - private: - // Handles the result of the action, returning new page context if necessary. - void OnActionFinished( - actor::TaskId task_id, - const mojom::GetTabContextOptions& options, - mojom::WebClientHandler::ActInFocusedTabCallback callback, - actor::mojom::ActionResultCode result, - std::optional<size_t> index_of_failed_action) const; - actor::ActorTask* GetCurrentTask() const; - base::WeakPtr<const GlicActorController> GetWeakPtr() const; - base::WeakPtr<GlicActorController> GetWeakPtr(); - raw_ptr<Profile> profile_; - base::WeakPtrFactory<GlicActorController> weak_ptr_factory_{this}; }; } // namespace glic
diff --git a/chrome/browser/glic/host/glic_actor_controller_interactive_uitest.cc b/chrome/browser/glic/host/glic_actor_controller_interactive_uitest.cc index e7ca99b6..0a67cf877 100644 --- a/chrome/browser/glic/host/glic_actor_controller_interactive_uitest.cc +++ b/chrome/browser/glic/host/glic_actor_controller_interactive_uitest.cc
@@ -47,8 +47,9 @@ using ::base::test::EqualsProto; using ::content::RenderFrameHost; using ::content::WebContents; +using ::optimization_guide::proto::Actions; +using ::optimization_guide::proto::ActionsResult; using ::optimization_guide::proto::AnnotatedPageContent; -using ::optimization_guide::proto::BrowserAction; using ::optimization_guide::proto::ClickAction; using ::optimization_guide::proto::ContentAttributes; using ::optimization_guide::proto::ContentNode; @@ -64,10 +65,23 @@ "use ActivateSurface() may be skipped on machine configurations which do " "not reliably support them."; -std::string EncodeActionProto(const BrowserAction& action) { +std::string EncodeActionProto(const Actions& action) { return base::Base64Encode(action.SerializeAsString()); } +std::optional<ActionsResult> DecodeActionsResultProto( + const std::string& base64_proto) { + std::string decoded_proto; + if (!base::Base64Decode(base64_proto, &decoded_proto)) { + return std::nullopt; + } + optimization_guide::proto::ActionsResult actions_result; + if (!actions_result.ParseFromString(decoded_proto)) { + return std::nullopt; + } + return actions_result; +} + // Tests the actor framework using the Glic API surface. This tests is meant to // exercise the API and end-to-end plumbing within Chrome. These tests aim to // faithfully mimic Glic's usage of these APIs to provide some basic coverage @@ -78,8 +92,8 @@ public: using ActionProtoProvider = base::OnceCallback<std::string()>; using ExpectedErrorResult = std::variant<std::monostate, - actor::mojom::ActionResultPtr, - mojom::ActInFocusedTabErrorReason>; + actor::mojom::ActionResultCode, + mojom::PerformActionsErrorReason>; GlicActorControllerUiTest() { scoped_feature_list_.InitWithFeatures( @@ -113,7 +127,6 @@ // content node id from the APC). Prefer to use the wrappers like ClickAction, // NavigateAction, etc. auto ExecuteAction(ActionProtoProvider proto_provider, - base::Value::Dict context_options, ExpectedErrorResult expected_result = {}) { static constexpr int kResultSuccess = base::to_underlying(actor::mojom::ActionResultCode::kOk); @@ -122,18 +135,11 @@ const std::string expected_result_string = std::visit( absl::Overload{ [](std::monostate) { return std::string(kSuccessString); }, - [](const actor::mojom::ActionResultPtr& r) { - EXPECT_FALSE(actor::IsOk(*r)); - if (!GlicActorController:: - ProvideObservationOnActionFailureEnabled()) { - // Until we provide observations on failure, every - // tool failure is reported as target not found. - return base::ToString( - mojom::ActInFocusedTabErrorReason::kTargetNotFound); - } - return base::ToString(r->code); + [](actor::mojom::ActionResultCode r) { + EXPECT_FALSE(actor::IsOk(r)); + return base::ToString(r); }, - [](mojom::ActInFocusedTabErrorReason r) { + [](mojom::PerformActionsErrorReason r) { return base::ToString(r); }, }, @@ -145,30 +151,39 @@ InAnyContext(WithElement( kGlicContentsElementId, [result_out = buffer_raw, - proto_provider = std::move(proto_provider), - context_options = - std::move(context_options)](ui::TrackedElement* el) mutable { + proto_provider = + std::move(proto_provider)](ui::TrackedElement* el) mutable { content::WebContents* glic_contents = AsInstrumentedWebContents(el)->web_contents(); // Distinguish errors from the action and errors from rejecting - // actInFocusedTab by making the latter negative. + // performAction by making the latter negative. std::string script = content::JsReplace( R"js( (async () => { try { - const res = await client.browser.actInFocusedTab({ - actionProto: Uint8Array.fromBase64($1).buffer, - tabContextOptions: $2 - }); - return res.actionResult; + const res = await client.browser.performActions( + Uint8Array.fromBase64($1).buffer); + return new Uint8Array(res).toBase64(); } catch (err) { - return -err.reason; + return err.reason; } })(); )js", - std::move(proto_provider).Run(), std::move(context_options)); - *result_out = content::EvalJs(glic_contents, std::move(script)) - .ExtractInt(); + std::move(proto_provider).Run()); + content::EvalJsResult result = + content::EvalJs(glic_contents, std::move(script)); + if (result.is_string()) { + auto actions_result = + DecodeActionsResultProto(result.ExtractString()); + if (actions_result) { + *result_out = actions_result->action_result(); + } else { + *result_out = -static_cast<int>( + mojom::PerformActionsErrorReason::kInvalidProto); + } + } else { + *result_out = -result.ExtractInt(); + } })), CheckResult( [result_in = std::move(result_buffer)]() { @@ -180,7 +195,7 @@ } if (result < 0) { auto result_enum = - static_cast<mojom::ActInFocusedTabErrorReason>(-result); + static_cast<mojom::PerformActionsErrorReason>(-result); EXPECT_TRUE(mojom::IsKnownEnumValue(result_enum)); return base::ToString(result_enum); } @@ -213,7 +228,6 @@ auto CreateTabAction(actor::TaskId& task_id, SessionID window_id, bool foreground, - base::Value::Dict context_options, ExpectedErrorResult expected_result = {}) { // Window_id is passed by value since tests currently only use one window so // this allows using browser()->session_id(). Once tests are exercising @@ -221,125 +235,108 @@ // provided ref. auto create_tab_provider = base::BindLambdaForTesting([&task_id, window_id, foreground]() { - BrowserAction create_tab = - actor::MakeCreateTab(window_id, foreground); + Actions create_tab = actor::MakeCreateTab(window_id, foreground); create_tab.set_task_id(task_id.value()); return EncodeActionProto(create_tab); }); return ExecuteAction(std::move(create_tab_provider), - std::move(context_options), std::move(expected_result)); } auto ClickAction(std::string_view label, actor::TaskId& task_id, TabHandle& tab_handle, - base::Value::Dict context_options, ExpectedErrorResult expected_result = {}) { auto click_provider = base::BindLambdaForTesting([this, &task_id, &tab_handle, label]() { int32_t node_id = SearchAnnotatedPageContent(label); RenderFrameHost* frame = tab_handle.Get()->GetContents()->GetPrimaryMainFrame(); - BrowserAction action = actor::MakeClick(*frame, node_id); + Actions action = actor::MakeClick(*frame, node_id); action.set_task_id(task_id.value()); return EncodeActionProto(action); }); - return ExecuteAction(std::move(click_provider), std::move(context_options), - std::move(expected_result)); + return ExecuteAction(std::move(click_provider), std::move(expected_result)); } auto ClickAction(std::string_view label, ExpectedErrorResult expected_result = {}) { return ClickAction(label, task_id_, tab_handle_, - AnnotationsOnlyContextOptions(), std::move(expected_result)); } auto ClickAction(const gfx::Point& coordinate, actor::TaskId& task_id, TabHandle& tab_handle, - base::Value::Dict context_options, ExpectedErrorResult expected_result = {}) { auto click_provider = base::BindLambdaForTesting([&task_id, &tab_handle, coordinate]() { - BrowserAction action = actor::MakeClick(tab_handle, coordinate); + Actions action = actor::MakeClick(tab_handle, coordinate); action.set_task_id(task_id.value()); return EncodeActionProto(action); }); - return ExecuteAction(std::move(click_provider), std::move(context_options), - std::move(expected_result)); + return ExecuteAction(std::move(click_provider), std::move(expected_result)); } auto ClickAction(const gfx::Point& coordinate, ExpectedErrorResult expected_result = {}) { return ClickAction(coordinate, task_id_, tab_handle_, - AnnotationsOnlyContextOptions(), std::move(expected_result)); } auto NavigateAction(GURL url, actor::TaskId& task_id, TabHandle& tab_handle, - base::Value::Dict context_options, ExpectedErrorResult expected_result = {}) { auto navigate_provider = base::BindLambdaForTesting([&task_id, &tab_handle, url]() { - BrowserAction action = actor::MakeNavigate(tab_handle, url.spec()); + Actions action = actor::MakeNavigate(tab_handle, url.spec()); action.set_task_id(task_id.value()); return EncodeActionProto(action); }); return ExecuteAction(std::move(navigate_provider), - std::move(context_options), std::move(expected_result)); } auto NavigateAction(GURL url, ExpectedErrorResult expected_result = {}) { return NavigateAction(url, task_id_, tab_handle_, - AnnotationsOnlyContextOptions(), std::move(expected_result)); } auto HistoryAction(HistoryDirection direction, actor::TaskId& task_id, TabHandle& tab_handle, - base::Value::Dict context_options, ExpectedErrorResult expected_result = {}) { auto navigate_provider = base::BindLambdaForTesting([&task_id, &tab_handle, direction]() { - BrowserAction action = direction == HistoryDirection::kBack - ? actor::MakeHistoryBack(tab_handle) - : actor::MakeHistoryForward(tab_handle); + Actions action = direction == HistoryDirection::kBack + ? actor::MakeHistoryBack(tab_handle) + : actor::MakeHistoryForward(tab_handle); action.set_task_id(task_id.value()); return EncodeActionProto(action); }); return ExecuteAction(std::move(navigate_provider), - std::move(context_options), std::move(expected_result)); } auto HistoryAction(HistoryDirection direction, ExpectedErrorResult expected_result = {}) { return HistoryAction(direction, task_id_, tab_handle_, - AnnotationsOnlyContextOptions(), std::move(expected_result)); } auto WaitAction(actor::TaskId& task_id, - base::Value::Dict context_options, ExpectedErrorResult expected_result = {}) { auto wait_provider = base::BindLambdaForTesting([&task_id]() { - BrowserAction action = actor::MakeWait(); + Actions action = actor::MakeWait(); action.set_task_id(task_id.value()); return EncodeActionProto(action); }); - return ExecuteAction(std::move(wait_provider), std::move(context_options), - std::move(expected_result)); + return ExecuteAction(std::move(wait_provider), std::move(expected_result)); } auto WaitAction(ExpectedErrorResult expected_result = {}) { - return WaitAction(task_id_, AnnotationsOnlyContextOptions(), - std::move(expected_result)); + return WaitAction(task_id_, std::move(expected_result)); } // Starts a new task by executing an initial navigate action to `task_url` to @@ -354,8 +351,8 @@ CreateTask(task_id_), CreateTabAction(task_id_, browser()->session_id(), - /*foreground=*/true, - AnnotationsOnlyContextOptions()), + /*foreground=*/true), + WaitForWebContentsReady(new_tab_id), InAnyContext(WithElement(new_tab_id, [this](ui::TrackedElement* el) { content::WebContents* new_tab_contents = AsInstrumentedWebContents(el)->web_contents(); @@ -365,8 +362,7 @@ })), NavigateAction(task_url, task_id_, - tab_handle_, - AnnotationsOnlyContextOptions()), + tab_handle_), WaitForWebContentsReady(new_tab_id, task_url) // clang-format on ); @@ -487,16 +483,6 @@ #endif } - // Gets the context options to capture a new observation that only has the - // annotations (AnnotatedPageContent). Taking screenshots can be slow or flaky - // in the test. This is intended to be used for interim steps, *before* - // returning the final context of an action. - base::Value::Dict AnnotationsOnlyContextOptions() { - return base::Value::Dict() - .Set("annotatedPageContent", true) - .Set("viewportScreenshot", false); - } - auto InitializeWithOpenGlicWindow() { DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kCurrentActiveTabId); @@ -679,8 +665,8 @@ // the last page context had the removed frame there. ExecuteJs(kNewActorTabId, "()=>{document.getElementById('topframe').remove();}"), - ClickAction(gfx::Point(10, 10), actor::MakeResult( - actor::mojom::ActionResultCode::kFrameLocationChangedSinceObservation)) + ClickAction(gfx::Point(10, 10), + actor::mojom::ActionResultCode::kFrameLocationChangedSinceObservation) // clang-format on ); } @@ -716,8 +702,8 @@ // the last page context had the removed frame there. ExecuteJs(kNewActorTabId, "()=>{document.getElementById('topframe').remove();}"), - ClickAction(gfx::Point(10, 10), actor::MakeResult( - actor::mojom::ActionResultCode::kFrameLocationChangedSinceObservation)) + ClickAction(gfx::Point(10, 10), + actor::mojom::ActionResultCode::kFrameLocationChangedSinceObservation) // clang-format on ); } @@ -738,8 +724,7 @@ ExecuteJs(kNewActorTabId, "()=>{document.getElementById('clickable').remove();}"), ClickAction(kClickableButtonLabel, - actor::MakeResult( - actor::mojom::ActionResultCode::kElementOffscreen)) + actor::mojom::ActionResultCode::kElementOffscreen) // clang-format on ); } @@ -764,8 +749,7 @@ "document.getElementById('clickable').offsetHeight;}"), ClickAction( {15, 15}, - actor::MakeResult( - actor::mojom::ActionResultCode::kObservedTargetElementChanged)) + actor::mojom::ActionResultCode::kObservedTargetElementChanged) // clang-format on ); } @@ -805,8 +789,7 @@ RunTestSequence( InitializeWithOpenGlicWindow(), ExecuteAction(ArbitraryStringProvider(encodedProto), - UpdatedContextOptions(), - mojom::ActInFocusedTabErrorReason::kInvalidActionProto)); + mojom::PerformActionsErrorReason::kInvalidProto)); } IN_PROC_BROWSER_TEST_F(GlicActorControllerUiTest, ActionTargetNotFound) { @@ -819,7 +802,7 @@ std::numeric_limits<int32_t>::max(); RenderFrameHost* frame = tab_handle_.Get()->GetContents()->GetPrimaryMainFrame(); - BrowserAction action = actor::MakeClick(*frame, kNonExistentContentNodeId); + Actions action = actor::MakeClick(*frame, kNonExistentContentNodeId); action.set_task_id(task_id_.value()); return EncodeActionProto(action); }); @@ -827,9 +810,8 @@ RunTestSequence( InitializeWithOpenGlicWindow(), StartActorTaskInNewTab(task_url, kNewActorTabId), - ExecuteAction(std::move(click_provider), UpdatedContextOptions(), - actor::MakeResult( - actor::mojom::ActionResultCode::kInvalidDomNodeId))); + ExecuteAction(std::move(click_provider), + actor::mojom::ActionResultCode::kInvalidDomNodeId)); } IN_PROC_BROWSER_TEST_F(GlicActorControllerUiTest, HistoryTool) { @@ -866,8 +848,8 @@ WaitForJsResult(kNewActorTabId, "() => button_clicked"), CheckIsActingOnTab(kNewActorTabId, true), StopActorTask(), - ClickAction(kClickableButtonLabel, actor::MakeResult( - actor::mojom::ActionResultCode::kTaskWentAway)), + ClickAction(kClickableButtonLabel, + actor::mojom::ActionResultCode::kTaskWentAway), CheckIsActingOnTab(kNewActorTabId, false)); // clang-format on } @@ -926,7 +908,7 @@ PauseActorTask(), ClickAction(kClickableButtonLabel, - actor::MakeResult(actor::mojom::ActionResultCode::kTaskPaused)), + actor::mojom::ActionResultCode::kTaskPaused), // Unlike stopping, pausing keeps the task. CheckIsActingOnTab(kNewActorTabId, true)
diff --git a/chrome/browser/glic/host/glic_api_browsertest.cc b/chrome/browser/glic/host/glic_api_browsertest.cc index f9a8cb0..ac4a761 100644 --- a/chrome/browser/glic/host/glic_api_browsertest.cc +++ b/chrome/browser/glic/host/glic_api_browsertest.cc
@@ -64,6 +64,7 @@ #include "chrome/test/interaction/interactive_browser_test.h" #include "components/content_settings/core/common/content_settings_types.h" #include "components/metrics/metrics_service.h" +#include "components/optimization_guide/content/browser/page_content_proto_provider.h" #include "components/policy/core/common/management/management_service.h" #include "components/policy/core/common/management/scoped_management_service_override_for_testing.h" #include "components/signin/public/identity_manager/identity_manager.h" @@ -197,7 +198,8 @@ {features::kGlicPreLoadingTimeMs.name, "20"}, {features::kGlicMinLoadingTimeMs.name, "40"}, }}, - {features::kGlicScrollTo, {}}, + {features::kGlicScrollTo, + {{"glic-scroll-to-enforce-document-id", "true"}}}, {features::kGlicClosedCaptioning, {}}, {features::kGlicApiActivationGating, {}}, {mojom::features::kGlicMultiTab, {}}, @@ -390,6 +392,19 @@ "/glic/test.html"); } + std::string GetDocumentIdForTab(ui::ElementIdentifier tab_id) { + ui::TrackedElement* const element = + ui::ElementTracker::GetElementTracker()->GetElementInAnyContext( + tab_id); + CHECK(element); + content::RenderFrameHost* rfh = AsInstrumentedWebContents(element) + ->web_contents() + ->GetPrimaryMainFrame(); + return optimization_guide::DocumentIdentifierUserData:: + GetDocumentIdentifier(rfh->GetGlobalFrameToken()) + .value(); + } + std::unique_ptr<base::HistogramTester> histogram_tester; private: @@ -1061,10 +1076,9 @@ IN_PROC_BROWSER_TEST_F(GlicApiTestWithOneTabAndPreloading, MAYBE_testDeferredFocusedTabStateAtCreation) { // Navigate the first tab. - RunTestSequence( - NavigateWebContents(kFirstTab, - InProcessBrowserTest::embedded_test_server()->GetURL( - "/scrollable_page_with_content.html"))); + RunTestSequence(NavigateWebContents( + kFirstTab, InProcessBrowserTest::embedded_test_server()->GetURL( + "/scrollable_page_with_content.html"))); ExecuteJsTest(); RunTestSequence(ToggleGlicWindow(GlicWindowMode::kDetached), CheckControllerShowing(true)); @@ -1274,20 +1288,24 @@ } IN_PROC_BROWSER_TEST_F(GlicApiTestWithOneTab, testScrollToFindsText) { - ExecuteJsTest(); + ExecuteJsTest({.params = base::Value(base::Value::Dict().Set( + "documentId", GetDocumentIdForTab(kFirstTab)))}); } IN_PROC_BROWSER_TEST_F(GlicApiTestWithOneTab, testScrollToFindsTextNoTabContextPermission) { - ExecuteJsTest(); + ExecuteJsTest({.params = base::Value(base::Value::Dict().Set( + "documentId", GetDocumentIdForTab(kFirstTab)))}); } IN_PROC_BROWSER_TEST_F(GlicApiTestWithOneTab, testScrollToFailsWhenInactive) { - ExecuteJsTest(); + ExecuteJsTest({.params = base::Value(base::Value::Dict().Set( + "documentId", GetDocumentIdForTab(kFirstTab)))}); } IN_PROC_BROWSER_TEST_F(GlicApiTestWithOneTab, testScrollToNoMatchFound) { - ExecuteJsTest(); + ExecuteJsTest({.params = base::Value(base::Value::Dict().Set( + "documentId", GetDocumentIdForTab(kFirstTab)))}); } IN_PROC_BROWSER_TEST_F(GlicApiTestWithOneTab, testSetSyntheticExperimentState) {
diff --git a/chrome/browser/glic/host/glic_page_handler.cc b/chrome/browser/glic/host/glic_page_handler.cc index 3cf3da4..873bd2aed 100644 --- a/chrome/browser/glic/host/glic_page_handler.cc +++ b/chrome/browser/glic/host/glic_page_handler.cc
@@ -833,17 +833,6 @@ glic_service_->PerformActions(actions_proto, std::move(callback)); } - void ActInFocusedTab(const std::vector<uint8_t>& action_proto, - glic::mojom::GetTabContextOptionsPtr options, - ActInFocusedTabCallback callback) override { - if (!base::FeatureList::IsEnabled(features::kGlicActor)) { - receiver_.ReportBadMessage( - "ActInFocusedTab cannot be called without GlicActor enabled."); - return; - } - glic_service_->ActInFocusedTab(action_proto, *options, std::move(callback)); - } - void StopActorTask(int32_t task_id) override { if (!base::FeatureList::IsEnabled(features::kGlicActor)) { receiver_.ReportBadMessage(
diff --git a/chrome/browser/glic/public/glic_keyed_service.cc b/chrome/browser/glic/public/glic_keyed_service.cc index 90867db..a577087 100644 --- a/chrome/browser/glic/public/glic_keyed_service.cc +++ b/chrome/browser/glic/public/glic_keyed_service.cc
@@ -419,27 +419,6 @@ base::BindOnce(PerformActionsFinished, std::move(callback))); } -void GlicKeyedService::ActInFocusedTab( - const std::vector<uint8_t>& action_proto, - const mojom::GetTabContextOptions& options, - mojom::WebClientHandler::ActInFocusedTabCallback callback) { - CHECK(base::FeatureList::IsEnabled(features::kGlicActor)); - - optimization_guide::proto::BrowserAction action; - if (!action.ParseFromArray(action_proto.data(), action_proto.size())) { - mojom::ActInFocusedTabResultPtr result = - mojom::ActInFocusedTabResult::NewErrorReason( - mojom::ActInFocusedTabErrorReason::kInvalidActionProto); - - base::SequencedTaskRunner::GetCurrentDefault()->PostTask( - FROM_HERE, base::BindOnce(std::move(callback), std::move(result))); - return; - } - - CHECK(actor_controller_); - actor_controller_->Act(action, options, std::move(callback)); -} - // TODO(crbug.com/411462297): Stop/Pause/Resume task need to be routed to go // through the ActorKeyedService, rather than the deprecated ActorController // which ignores the task_id.
diff --git a/chrome/browser/glic/public/glic_keyed_service.h b/chrome/browser/glic/public/glic_keyed_service.h index 7ac5b63..d5b049d 100644 --- a/chrome/browser/glic/public/glic_keyed_service.h +++ b/chrome/browser/glic/public/glic_keyed_service.h
@@ -160,11 +160,6 @@ void PerformActions(const std::vector<uint8_t>& actions_proto, mojom::WebClientHandler::PerformActionsCallback callback); - void ActInFocusedTab( - const std::vector<uint8_t>& action_proto, - const mojom::GetTabContextOptions& options, - mojom::WebClientHandler::ActInFocusedTabCallback callback); - void StopActorTask(actor::TaskId task_id); void PauseActorTask(actor::TaskId task_id); void ResumeActorTask(
diff --git a/chrome/browser/headless/BUILD.gn b/chrome/browser/headless/BUILD.gn index eec52a5..5c3ffb08 100644 --- a/chrome/browser/headless/BUILD.gn +++ b/chrome/browser/headless/BUILD.gn
@@ -121,6 +121,7 @@ "//chrome/browser/infobars", "//chrome/browser/profiles", "//chrome/browser/ui", + "//chrome/browser/ui/views/frame:toolbar_button_provider", "//components/devtools/simple_devtools_protocol_client", "//components/headless/clipboard", "//components/headless/command_handler",
diff --git a/chrome/browser/headless/DEPS b/chrome/browser/headless/DEPS index cc74d49..5bd7c03 100644 --- a/chrome/browser/headless/DEPS +++ b/chrome/browser/headless/DEPS
@@ -10,7 +10,6 @@ ], "headless_mode_browsertest\.cc": [ "+chrome/browser/ui/views/frame/app_menu_button.h", - "+chrome/browser/ui/views/frame/toolbar_button_provider.h", ], "headless_mode_command_browsertest\.cc": [ "+pdf",
diff --git a/chrome/browser/login_detection/README.md b/chrome/browser/login_detection/README.md index 9dc83a2..6635995 100644 --- a/chrome/browser/login_detection/README.md +++ b/chrome/browser/login_detection/README.md
@@ -8,7 +8,3 @@ for login sites on platforms like Android, where Site Isolation cannot be used for all sites. The login sites are also saved in preferences. Note that the detector's heuristics are not expected to be perfect. - -In the future, the OAuth detector should be factored out into its own -login detection component under //components, so that it can also be used -on platforms such as WebLayer.
diff --git a/chrome/browser/media/webrtc/fake_desktop_media_picker_factory.cc b/chrome/browser/media/webrtc/fake_desktop_media_picker_factory.cc index 79186ab9..94ceed3 100644 --- a/chrome/browser/media/webrtc/fake_desktop_media_picker_factory.cc +++ b/chrome/browser/media/webrtc/fake_desktop_media_picker_factory.cc
@@ -6,6 +6,7 @@ #include <utility> +#include "base/compiler_specific.h" #include "base/functional/bind.h" #include "base/memory/weak_ptr.h" #include "base/task/single_thread_task_runner.h" @@ -98,7 +99,8 @@ if (current_test_ >= tests_count_) return nullptr; ++current_test_; - picker_ = new FakeDesktopMediaPicker(test_flags_ + current_test_ - 1); + picker_ = + new FakeDesktopMediaPicker(UNSAFE_TODO(test_flags_ + current_test_ - 1)); return std::unique_ptr<DesktopMediaPicker>(picker_); }
diff --git a/chrome/browser/metrics/chrome_metrics_service_client.cc b/chrome/browser/metrics/chrome_metrics_service_client.cc index b0c8ac221..38589887 100644 --- a/chrome/browser/metrics/chrome_metrics_service_client.cc +++ b/chrome/browser/metrics/chrome_metrics_service_client.cc
@@ -839,8 +839,7 @@ metrics_service_->RegisterMetricsProvider(MakeDemographicMetricsProvider( metrics::MetricsLogUploader::MetricServiceType::UMA)); - // TODO(crbug.com/40765618): Add metrics registration for WebView, iOS and - // WebLayer. + // TODO(crbug.com/40765618): Add metrics registration for WebView and iOS. metrics_service_->RegisterMetricsProvider( std::make_unique<safe_browsing::SafeBrowsingMetricsProvider>());
diff --git a/chrome/browser/net/network_request_metrics_browsertest.cc b/chrome/browser/net/network_request_metrics_browsertest.cc index a91a3d3..f0a5341 100644 --- a/chrome/browser/net/network_request_metrics_browsertest.cc +++ b/chrome/browser/net/network_request_metrics_browsertest.cc
@@ -16,6 +16,7 @@ #include "base/threading/thread_restrictions.h" #include "chrome/browser/download/download_prefs.h" #include "chrome/browser/predictors/loading_predictor_config.h" +#include "chrome/browser/preloading/scoped_prewarm_feature_list.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" @@ -324,6 +325,11 @@ base::HistogramTester* histograms() { return histograms_.get(); } private: + // TODO(https://crbug.com/423465927): Explore a better approach to make the + // existing tests run with the prewarm feature enabled. + test::ScopedPrewarmFeatureList scoped_prewarm_feature_list_{ + test::ScopedPrewarmFeatureList::PrewarmState::kDisabled}; + std::unique_ptr<net::test_server::ControllableHttpResponse> uninteresting_main_frame_response_; std::unique_ptr<net::test_server::ControllableHttpResponse>
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc b/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc index 61f5e56..d326984 100644 --- a/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc +++ b/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
@@ -325,7 +325,6 @@ content::WebContents* web_contents) { // Change this method? consider to modify the peer in // android_webview/browser/page_load_metrics/page_load_metrics_initialize.cc - // weblayer/browser/page_load_metrics_initialize.cc // as well. page_load_metrics::MetricsWebContentsObserver::CreateForWebContents( web_contents, std::make_unique<PageLoadMetricsEmbedder>(web_contents));
diff --git a/chrome/browser/performance_manager/frame_node_impl_browsertest.cc b/chrome/browser/performance_manager/frame_node_impl_browsertest.cc index 555e600..872f5bf 100644 --- a/chrome/browser/performance_manager/frame_node_impl_browsertest.cc +++ b/chrome/browser/performance_manager/frame_node_impl_browsertest.cc
@@ -11,6 +11,7 @@ #include "base/run_loop.h" #include "base/test/bind.h" #include "base/test/scoped_feature_list.h" +#include "chrome/browser/preloading/scoped_prewarm_feature_list.h" #include "chrome/browser/ui/browser.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h" @@ -55,7 +56,16 @@ /*shift=*/false, /*alt=*/false, /*command=*/false); } -using FrameNodeImplBrowserTest = InProcessBrowserTest; +class FrameNodeImplBrowserTest : public InProcessBrowserTest { + public: + ~FrameNodeImplBrowserTest() override = default; + + private: + // TODO(https://crbug.com/423465927): Explore a better approach to make the + // existing tests run with the prewarm feature enabled. + test::ScopedPrewarmFeatureList scoped_prewarm_feature_list_{ + test::ScopedPrewarmFeatureList::PrewarmState::kDisabled}; +}; class ParameterizedFrameNodeImplBrowserTest : public FrameNodeImplBrowserTest,
diff --git a/chrome/browser/permissions/permission_manager_factory.cc b/chrome/browser/permissions/permission_manager_factory.cc index ffeb2d6..05b4592 100644 --- a/chrome/browser/permissions/permission_manager_factory.cc +++ b/chrome/browser/permissions/permission_manager_factory.cc
@@ -106,8 +106,6 @@ // when adding new contexts here explaining why it can't be shared with other // Content embedders by adding it to CreateDefaultPermissionContexts(). - // TODO(crbug.com/40941384): Still in development for Android so we don't - // support it on WebLayer yet. permission_contexts[ContentSettingsType::AUTOMATIC_FULLSCREEN] = std::make_unique<permissions::AutomaticFullscreenPermissionContext>( profile); @@ -116,31 +114,21 @@ permission_contexts[ContentSettingsType::BACKGROUND_FETCH] = std::make_unique<BackgroundFetchPermissionContext>(profile); - // TODO(crbug.com/40418135): Still in development for Android so we don't - // support it on WebLayer yet. permission_contexts[ContentSettingsType::DISPLAY_CAPTURE] = std::make_unique<DisplayCapturePermissionContext>(profile); - // TODO(crbug.com/40703864): Permission is granted based on browser heuristics - // (e.g. site engagement) and is not planned for WebLayer until it supports - // installing PWAs. permission_contexts[ContentSettingsType::DURABLE_STORAGE] = std::make_unique<DurableStoragePermissionContext>(profile); - // TODO(crbug.com/40591477): Still in development so we don't support it on - // WebLayer yet. permission_contexts[ContentSettingsType::IDLE_DETECTION] = std::make_unique<IdleDetectionPermissionContext>(profile); permission_contexts[ContentSettingsType::KEYBOARD_LOCK] = std::make_unique<permissions::KeyboardLockPermissionContext>(profile); - // TODO(crbug.com/40115199): Still in development for Android so we don't - // support it on WebLayer yet. permission_contexts[ContentSettingsType::LOCAL_FONTS] = std::make_unique<LocalFontsPermissionContext>(profile); - // Depends on Chrome specific policies not available on WebLayer. permission_contexts[ContentSettingsType::MEDIASTREAM_CAMERA] = std::make_unique<MediaStreamDevicePermissionContext>( profile, ContentSettingsType::MEDIASTREAM_CAMERA); @@ -151,12 +139,9 @@ permission_contexts[ContentSettingsType::SPEAKER_SELECTION] = std::make_unique<SpeakerSelectionPermissionContext>(profile); - // TODO(crbug.com/40659287): Move once Notifications are supported on - // WebLayer. permission_contexts[ContentSettingsType::NOTIFICATIONS] = std::make_unique<NotificationPermissionContext>(profile); - // TODO(crbug.com/40697624): Move once supported on WebLayer. permission_contexts[ContentSettingsType::PERIODIC_BACKGROUND_SYNC] = std::make_unique<PeriodicBackgroundSyncPermissionContext>(profile); @@ -164,22 +149,16 @@ std::make_unique<permissions::PointerLockPermissionContext>(profile); #if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_WIN) - // We don't support Chrome OS and Windows for WebLayer yet so only the Android - // specific logic is used on WebLayer. permission_contexts[ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER] = std::make_unique<ProtectedMediaIdentifierPermissionContext>(profile); #endif // BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_WIN) - // TODO(crbug.com/40638427): Still in development so we don't support it on - // WebLayer yet. permission_contexts[ContentSettingsType::STORAGE_ACCESS] = std::make_unique<StorageAccessGrantPermissionContext>(profile); permission_contexts[ContentSettingsType::TOP_LEVEL_STORAGE_ACCESS] = std::make_unique<TopLevelStorageAccessPermissionContext>(profile); - // TODO(crbug.com/40092782): Still in development for Android so we don't - // support it on WebLayer yet. permission_contexts[ContentSettingsType::WINDOW_MANAGEMENT] = std::make_unique<permissions::WindowManagementPermissionContext>(profile);
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc index ecaa2ca..58dd494d 100644 --- a/chrome/browser/prefs/browser_prefs.cc +++ b/chrome/browser/prefs/browser_prefs.cc
@@ -1092,6 +1092,12 @@ "ash.holding_space.time_of_first_files_app_chip_press"; #endif // BUILDFLAG(IS_CHROMEOS) +// Deprecated 07/2025. +inline constexpr char kSyncPromoIdentityPillShownCount[] = + "ChromeSigninSyncPromoIdentityPillShownCount"; +inline constexpr char kSyncPromoIdentityPillUsedCount[] = + "ChromeSigninSyncPromoIdentityPillUsedCount"; + // Register local state used only for migration (clearing or moving to a new // key). void RegisterLocalStatePrefsForMigration(PrefRegistrySimple* registry) { @@ -1544,6 +1550,9 @@ // Deprecated 07/2025. registry->RegisterTimePref(kTimeOfFirstFilesAppChipPress, base::Time()); #endif // BUILDFLAG(IS_CHROMEOS) + + registry->RegisterIntegerPref(kSyncPromoIdentityPillShownCount, 0); + registry->RegisterIntegerPref(kSyncPromoIdentityPillUsedCount, 0); } } // namespace @@ -2832,6 +2841,9 @@ profile_prefs->ClearPref(kTimeOfFirstFilesAppChipPress); #endif // BUILDFLAG(IS_CHROMEOS) + profile_prefs->ClearPref(kSyncPromoIdentityPillShownCount); + profile_prefs->ClearPref(kSyncPromoIdentityPillUsedCount); + // Please don't delete the following line. It is used by PRESUBMIT.py. // END_MIGRATE_OBSOLETE_PROFILE_PREFS
diff --git a/chrome/browser/profiles/batch_upload/BUILD.gn b/chrome/browser/profiles/batch_upload/BUILD.gn index 5dc2c79a..5dabf627 100644 --- a/chrome/browser/profiles/batch_upload/BUILD.gn +++ b/chrome/browser/profiles/batch_upload/BUILD.gn
@@ -33,6 +33,7 @@ "//chrome/browser/sync", "//chrome/browser/sync:factories", "//chrome/browser/ui/profiles", + "//chrome/browser/ui/views/frame:toolbar_button_provider", "//components/signin/public/identity_manager", "//components/sync/base", "//components/sync/service", @@ -94,6 +95,7 @@ "//chrome/browser/signin", "//chrome/browser/ui", "//chrome/browser/ui/profiles", + "//chrome/browser/ui/views/frame:toolbar_button_provider", "//chrome/common", "//chrome/test:test_support_ui", "//components/signin/public/base",
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc index b9a1f20..91acd4ad 100644 --- a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc +++ b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
@@ -2546,7 +2546,6 @@ std::string image_path, int event_flags, ContextMenuNotificationObserver::MenuShownCallback callback) { - ASSERT_TRUE(embedded_test_server()->Start()); GURL image_url(embedded_test_server()->GetURL(image_path)); GURL page("data:text/html,<img src='" + image_url.spec() + "'>"); ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), page));
diff --git a/chrome/browser/resources/gaia_auth_host/authenticator.js b/chrome/browser/resources/gaia_auth_host/authenticator.js index f646508..6cc9f18 100644 --- a/chrome/browser/resources/gaia_auth_host/authenticator.js +++ b/chrome/browser/resources/gaia_auth_host/authenticator.js
@@ -457,6 +457,7 @@ this.syncTrustedVaultKeys_ = null; this.closeViewReceived_ = false; this.gaiaStartTime = null; + this.samlRedirectionInProgress = false; window.addEventListener( 'message', e => this.onMessageFromWebview_(e), false); @@ -992,7 +993,7 @@ /** * Invoked when headers are received in the main frame of the webview. It - * reads the authenticated user info from a signin header. + * reads the authenticated user info from a sign-in header. * @param {OnHeadersReceivedDetails} details * @private */ @@ -1015,6 +1016,12 @@ const header = headers[i]; const headerName = header.name.toLowerCase(); if (headerName === SIGN_IN_HEADER) { + if (this.samlRedirectionInProgress) { + console.warn( + `Authenticator: sign-in header received during ongoing SAML ' + + 'redirection, it will be ignored`) + return; + } // See go/gaia-response-headers#google-accounts-signin for the expected // format of the sign-in header fields. const headerValues = header.value.toLowerCase().split(','); @@ -1387,8 +1394,8 @@ * @private */ onIsSamlFlowChanged_(e) { - const isSamlFlow = e.detail.isSamlFlow; - if (isSamlFlow) { + this.samlRedirectionInProgress = e.detail.isSamlFlow; + if (this.samlRedirectionInProgress) { this.authFlow = AuthFlow.SAML; } }
diff --git a/chrome/browser/resources/glic/glic_api/glic_api.ts b/chrome/browser/resources/glic/glic_api/glic_api.ts index fe40320..58030dfa 100644 --- a/chrome/browser/resources/glic/glic_api/glic_api.ts +++ b/chrome/browser/resources/glic/glic_api/glic_api.ts
@@ -199,7 +199,8 @@ setMaximumNumberOfPinnedTabs?(numTabs: number): Promise<number>; /** - * @deprecated Use CreateTask and PerformActions instead. + * @deprecated Use CreateTask and PerformActions instead. This method + * is undefined in Chrome and calling it is not supported. * * Inform Chrome about an action. Chrome Takes an action based on the * action proto and returns new context based on the tab context options. @@ -217,7 +218,6 @@ * * @throws {ActInFocusedTabError} on failure. * - * @todo Not yet implemented. https://crbug.com/425681926 */ createTask?(): Promise<number>; @@ -229,7 +229,6 @@ * * The output corresponds to the ActionsResult proto. * - * @todo Not yet implemented. https://crbug.com/425681926 */ performActions?(actions: ArrayBuffer): Promise<ArrayBuffer>;
diff --git a/chrome/browser/resources/glic/glic_api_impl/conversions.ts b/chrome/browser/resources/glic/glic_api_impl/conversions.ts index d3befa0..78496d5 100644 --- a/chrome/browser/resources/glic/glic_api_impl/conversions.ts +++ b/chrome/browser/resources/glic/glic_api_impl/conversions.ts
@@ -76,9 +76,6 @@ assertNever<CheckEnumCompatibility< Omit<typeof mojom.WebClientMode, 'kUnknown'>, typeof api.WebClientMode>>(); assertNever<CheckEnumCompatibility< - typeof mojom.ActInFocusedTabErrorReason, - typeof api.ActInFocusedTabErrorReason>>(); -assertNever<CheckEnumCompatibility< typeof mojom.CaptureScreenshotErrorReason, typeof api.CaptureScreenshotErrorReason>>(); assertNever<CheckEnumCompatibility<
diff --git a/chrome/browser/resources/glic/glic_api_impl/glic_api_client.ts b/chrome/browser/resources/glic/glic_api_impl/glic_api_client.ts index 9f8df3e..bc93bf94 100644 --- a/chrome/browser/resources/glic/glic_api_impl/glic_api_client.ts +++ b/chrome/browser/resources/glic/glic_api_impl/glic_api_client.ts
@@ -2,14 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import type {ActInFocusedTabParams, ActInFocusedTabResult, AnnotatedPageData, ChromeVersion, CreateTabOptions, DraggableArea, FocusedTabData, GetPinCandidatesOptions, GlicBrowserHost, GlicBrowserHostJournal, GlicBrowserHostMetrics, GlicHostRegistry, GlicWebClient, HostCapability, Journal, Observable, ObservableValue, OpenPanelInfo, OpenSettingsOptions, PanelOpeningData, PanelState, PdfDocumentData, PinCandidate, ResizeWindowOptions, Screenshot, ScrollToParams, TabContextOptions, TabContextResult, TabData, UserProfileInfo, ViewChangedNotification, ViewChangeRequest, ZeroStateSuggestions, ZeroStateSuggestionsOptions, ZeroStateSuggestionsV2} from '../glic_api/glic_api.js'; +import type {AnnotatedPageData, ChromeVersion, CreateTabOptions, DraggableArea, FocusedTabData, GetPinCandidatesOptions, GlicBrowserHost, GlicBrowserHostJournal, GlicBrowserHostMetrics, GlicHostRegistry, GlicWebClient, HostCapability, Journal, Observable, ObservableValue, OpenPanelInfo, OpenSettingsOptions, PanelOpeningData, PanelState, PdfDocumentData, PinCandidate, ResizeWindowOptions, Screenshot, ScrollToParams, TabContextOptions, TabContextResult, TabData, UserProfileInfo, ViewChangedNotification, ViewChangeRequest, ZeroStateSuggestions, ZeroStateSuggestionsOptions, ZeroStateSuggestionsV2} from '../glic_api/glic_api.js'; import {ActorTaskState} from '../glic_api/glic_api.js'; import {ObservableValue as ObservableValueImpl, Subject} from '../observable.js'; import {replaceProperties} from './conversions.js'; import {newSenderId, PostMessageRequestReceiver, PostMessageRequestSender} from './post_message_transport.js'; import type {ResponseExtras} from './post_message_transport.js'; -import type {ActInFocusedTabResultPrivate, AnnotatedPageDataPrivate, FocusedTabDataPrivate, PdfDocumentDataPrivate, PinCandidatePrivate, RequestRequestType, RequestResponseType, RgbaImage, TabContextResultPrivate, TabDataPrivate, TransferableException, WebClientRequestTypes} from './request_types.js'; +import type {AnnotatedPageDataPrivate, FocusedTabDataPrivate, PdfDocumentDataPrivate, PinCandidatePrivate, RequestRequestType, RequestResponseType, RgbaImage, TabContextResultPrivate, TabDataPrivate, TransferableException, WebClientRequestTypes} from './request_types.js'; import {ImageAlphaType, ImageColorType, newTransferableException} from './request_types.js'; @@ -300,7 +300,6 @@ if (!state.enableActInFocusedTab) { this.createTask = undefined; this.performActions = undefined; - this.actInFocusedTab = undefined; this.stopActorTask = undefined; this.pauseActorTask = undefined; this.resumeActorTask = undefined; @@ -463,14 +462,6 @@ return result.actionsResult; } - async actInFocusedTab?(actInFocusedTabParams: ActInFocusedTabParams): - Promise<ActInFocusedTabResult> { - const context = await this.sender.requestWithResponse( - 'glicBrowserActInFocusedTab', {actInFocusedTabParams}); - return convertActInFocusedTabResultFromPrivate( - context.actInFocusedTabResult); - } - stopActorTask?(taskId?: number): void { this.sender.requestNoResponse( 'glicBrowserStopActorTask', {taskId: taskId ?? 0}); @@ -1003,10 +994,3 @@ convertAnnotatedPageDataFromPrivate(data.annotatedPageData); return replaceProperties(data, {tabData, pdfDocumentData, annotatedPageData}); } - -function convertActInFocusedTabResultFromPrivate( - data: ActInFocusedTabResultPrivate): ActInFocusedTabResult { - const tabContextResult = - convertTabContextResultFromPrivate(data.tabContextResult); - return replaceProperties(data, {tabContextResult}); -}
diff --git a/chrome/browser/resources/glic/glic_api_impl/glic_api_host.ts b/chrome/browser/resources/glic/glic_api_impl/glic_api_host.ts index e177528..9245fb5d 100644 --- a/chrome/browser/resources/glic/glic_api_impl/glic_api_host.ts +++ b/chrome/browser/resources/glic/glic_api_impl/glic_api_host.ts
@@ -19,8 +19,8 @@ import type {ActorTaskState as ActorTaskStateMojo, FocusedTabData as FocusedTabDataMojo, GetPinCandidatesOptions as GetPinCandidatesOptionsMojo, GetTabContextOptions as TabContextOptionsMojo, OpenPanelInfo as OpenPanelInfoMojo, OpenSettingsOptions as OpenSettingsOptionsMojo, PanelOpeningData as PanelOpeningDataMojo, PanelState as PanelStateMojo, PinCandidate as PinCandidateMojo, PinCandidatesObserver, ScrollToSelector as ScrollToSelectorMojo, TabContext as TabContextMojo, TabData as TabDataMojo, ViewChangeRequest as ViewChangeRequestMojo, WebClientHandlerInterface, WebClientInterface, ZeroStateSuggestionsOptions as ZeroStateSuggestionsOptionsMojo, ZeroStateSuggestionsV2 as ZeroStateSuggestionsV2Mojo} from '../glic.mojom-webui.js'; import {CurrentView as CurrentViewMojo, PinCandidatesObserverReceiver, SettingsPageField as SettingsPageFieldMojo, WebClientHandlerRemote, WebClientMode, WebClientReceiver} from '../glic.mojom-webui.js'; import type {HostCapability as HostCapabilityMojo} from '../glic.mojom-webui.js'; -import type {ActInFocusedTabParams, ActorTaskState, DraggableArea, GetPinCandidatesOptions, HostCapability, Journal, OpenSettingsOptions, PageMetadata, PanelOpeningData, PanelState, Screenshot, ScrollToParams, TabContextOptions, ViewChangedNotification, ViewChangeRequest, WebPageData, ZeroStateSuggestions, ZeroStateSuggestionsOptions, ZeroStateSuggestionsV2} from '../glic_api/glic_api.js'; -import {ActInFocusedTabErrorReason, CaptureScreenshotErrorReason, ClientView, CreateTaskErrorReason, DEFAULT_INNER_TEXT_BYTES_LIMIT, DEFAULT_PDF_SIZE_LIMIT, PerformActionsErrorReason, ScrollToErrorReason} from '../glic_api/glic_api.js'; +import type {ActorTaskState, DraggableArea, GetPinCandidatesOptions, HostCapability, Journal, OpenSettingsOptions, PageMetadata, PanelOpeningData, PanelState, Screenshot, ScrollToParams, TabContextOptions, ViewChangedNotification, ViewChangeRequest, WebPageData, ZeroStateSuggestions, ZeroStateSuggestionsOptions, ZeroStateSuggestionsV2} from '../glic_api/glic_api.js'; +import {CaptureScreenshotErrorReason, ClientView, CreateTaskErrorReason, DEFAULT_INNER_TEXT_BYTES_LIMIT, DEFAULT_PDF_SIZE_LIMIT, PerformActionsErrorReason, ScrollToErrorReason} from '../glic_api/glic_api.js'; import {ObservableValue} from '../observable.js'; import type {ObservableValueReadOnly} from '../observable.js'; import {OneShotTimer} from '../timer.js'; @@ -28,7 +28,7 @@ import {replaceProperties} from './conversions.js'; import type {PostMessageRequestHandler} from './post_message_transport.js'; import {newSenderId, PostMessageRequestReceiver, PostMessageRequestSender, ResponseExtras} from './post_message_transport.js'; -import type {ActInFocusedTabResultPrivate, AnnotatedPageDataPrivate, FocusedTabDataPrivate, HostRequestTypes, PdfDocumentDataPrivate, RequestRequestType, RequestResponseType, RgbaImage, TabContextResultPrivate, TabDataPrivate, TransferableException, WebClientInitialStatePrivate} from './request_types.js'; +import type {AnnotatedPageDataPrivate, FocusedTabDataPrivate, HostRequestTypes, PdfDocumentDataPrivate, RequestRequestType, RequestResponseType, RgbaImage, TabContextResultPrivate, TabDataPrivate, TransferableException, WebClientInitialStatePrivate} from './request_types.js'; import {ErrorWithReasonImpl, exceptionFromTransferable, ImageAlphaType, ImageColorType, requestTypeToHistogramSuffix} from './request_types.js'; export enum WebClientState { @@ -508,34 +508,6 @@ } } - async glicBrowserActInFocusedTab( - request: {actInFocusedTabParams: ActInFocusedTabParams}, - extras: ResponseExtras): - Promise<{actInFocusedTabResult: ActInFocusedTabResultPrivate}> { - const { - result: {errorReason, actInFocusedTabResponse}, - } = - await this.handler.actInFocusedTab( - byteArrayFromClient(request.actInFocusedTabParams.actionProto), - tabContextOptionsFromClient( - request.actInFocusedTabParams.tabContextOptions)); - if (!actInFocusedTabResponse) { - throw new ErrorWithReasonImpl( - 'actInFocusedTab', - (errorReason as ActInFocusedTabErrorReason | undefined) ?? - ActInFocusedTabErrorReason.UNKNOWN); - } - - const tabContextResult = - tabContextToClient(actInFocusedTabResponse.tabContext, extras); - return { - actInFocusedTabResult: { - tabContextResult: tabContextResult, - actionResult: actInFocusedTabResponse.actionResult, - }, - }; - } - glicBrowserStopActorTask(request: {taskId: number}): void { this.handler.stopActorTask(request.taskId); }
diff --git a/chrome/browser/resources/glic/glic_api_impl/request_types.ts b/chrome/browser/resources/glic/glic_api_impl/request_types.ts index d8b1f1ff..e135fed 100644 --- a/chrome/browser/resources/glic/glic_api_impl/request_types.ts +++ b/chrome/browser/resources/glic/glic_api_impl/request_types.ts
@@ -3,7 +3,7 @@ // found in the LICENSE file. import {type WebClientInitialState} from '../glic.mojom-webui.js'; -import type {ActInFocusedTabParams, ActInFocusedTabResult, ActorTaskState, AnnotatedPageData, ChromeVersion, DraggableArea, ErrorReasonTypes, ErrorWithReason, FocusedTabDataHasFocus, FocusedTabDataHasNoFocus, GetPinCandidatesOptions, HostCapability, Journal, OpenPanelInfo, OpenSettingsOptions, PageMetadata, PanelOpeningData, PanelState, PdfDocumentData, PinCandidate, Screenshot, ScrollToParams, TabContextOptions, TabContextResult, TabData, UserProfileInfo, ViewChangedNotification, ViewChangeRequest, ZeroStateSuggestions, ZeroStateSuggestionsOptions, ZeroStateSuggestionsV2} from '../glic_api/glic_api.js'; +import type {ActorTaskState, AnnotatedPageData, ChromeVersion, DraggableArea, ErrorReasonTypes, ErrorWithReason, FocusedTabDataHasFocus, FocusedTabDataHasNoFocus, GetPinCandidatesOptions, HostCapability, Journal, OpenPanelInfo, OpenSettingsOptions, PageMetadata, PanelOpeningData, PanelState, PdfDocumentData, PinCandidate, Screenshot, ScrollToParams, TabContextOptions, TabContextResult, TabData, UserProfileInfo, ViewChangedNotification, ViewChangeRequest, ZeroStateSuggestions, ZeroStateSuggestionsOptions, ZeroStateSuggestionsV2} from '../glic_api/glic_api.js'; /* This file defines messages sent over postMessage in-between the Glic WebUI @@ -115,14 +115,6 @@ actionsResult: ArrayBuffer, }, }; - glicBrowserActInFocusedTab: { - request: { - actInFocusedTabParams: ActInFocusedTabParams, - }, - response: { - actInFocusedTabResult: ActInFocusedTabResultPrivate, - }, - }; glicBrowserStopActorTask: { request: { taskId: number, @@ -483,7 +475,6 @@ GetContextFromTab: 0, GetContextForActorFromTab: 0, SetMaximumNumberOfPinnedTabs: 0, - ActInFocusedTab: 0, StopActorTask: 0, PauseActorTask: 0, ResumeActorTask: 0, @@ -662,11 +653,6 @@ annotatedPageData?: AnnotatedPageDataPrivate; } -export declare interface ActInFocusedTabResultPrivate extends - Omit<ActInFocusedTabResult, 'tabContextResult'> { - tabContextResult: TabContextResultPrivate; -} - export declare interface UserProfileInfoPrivate extends Omit<UserProfileInfo, 'avatarIcon'> { avatarIcon?: RgbaImage;
diff --git a/chrome/browser/resources/new_tab_page/composebox/composebox_proxy.ts b/chrome/browser/resources/new_tab_page/composebox/composebox_proxy.ts index eb28388..52987d0 100644 --- a/chrome/browser/resources/new_tab_page/composebox/composebox_proxy.ts +++ b/chrome/browser/resources/new_tab_page/composebox/composebox_proxy.ts
@@ -1,40 +1,52 @@ // Copyright 2025 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import {PageCallbackRouter as SearchboxPageCallbackRouter, PageHandlerRemote as SearchboxPageHandlerRemote} from '//resources/mojo/components/omnibox/browser/searchbox.mojom-webui.js'; import {PageCallbackRouter, PageHandlerFactory, PageHandlerRemote} from '../composebox.mojom-webui.js'; export interface ComposeboxProxy { handler: PageHandlerRemote; callbackRouter: PageCallbackRouter; + searchboxHandler: SearchboxPageHandlerRemote; + searchboxCallbackRouter: SearchboxPageCallbackRouter; } - export class ComposeboxProxyImpl implements ComposeboxProxy { handler: PageHandlerRemote; callbackRouter: PageCallbackRouter; - - constructor(handler: PageHandlerRemote, callbackRouter: PageCallbackRouter) { + searchboxHandler: SearchboxPageHandlerRemote; + searchboxCallbackRouter: SearchboxPageCallbackRouter; + constructor( + handler: PageHandlerRemote, callbackRouter: PageCallbackRouter, + searchboxHandler: SearchboxPageHandlerRemote, + searchboxCallbackRouter: SearchboxPageCallbackRouter) { this.handler = handler; this.callbackRouter = callbackRouter; + this.searchboxHandler = searchboxHandler; + this.searchboxCallbackRouter = searchboxCallbackRouter; } - static getInstance(): ComposeboxProxyImpl { if (instance) { return instance; } + // Composebox connection variables. const callbackRouter = new PageCallbackRouter(); const handler = new PageHandlerRemote(); const factory = PageHandlerFactory.getRemote(); + // Searchbox connection variables. + const searchboxHandler = new SearchboxPageHandlerRemote(); + const searchboxCallbackRouter = new SearchboxPageCallbackRouter(); factory.createPageHandler( callbackRouter.$.bindNewPipeAndPassRemote(), - handler.$.bindNewPipeAndPassReceiver()); - instance = new ComposeboxProxyImpl(handler, callbackRouter); + handler.$.bindNewPipeAndPassReceiver(), + searchboxCallbackRouter.$.bindNewPipeAndPassRemote(), + searchboxHandler.$.bindNewPipeAndPassReceiver()); + instance = new ComposeboxProxyImpl( + handler, callbackRouter, searchboxHandler, searchboxCallbackRouter); return instance; } - static setInstance(newInstance: ComposeboxProxy) { instance = newInstance; } } - let instance: ComposeboxProxy|null = null;
diff --git a/chrome/browser/safe_browsing/client_side_detection_host_browsertest.cc b/chrome/browser/safe_browsing/client_side_detection_host_browsertest.cc index 4903f728..2216fa9f 100644 --- a/chrome/browser/safe_browsing/client_side_detection_host_browsertest.cc +++ b/chrome/browser/safe_browsing/client_side_detection_host_browsertest.cc
@@ -31,6 +31,7 @@ #include "components/safe_browsing/core/common/safe_browsing_prefs.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/render_frame_host.h" +#include "content/public/browser/render_widget_host_view.h" #include "content/public/browser/web_contents_observer.h" #include "content/public/test/browser_test.h" #include "content/public/test/mock_navigation_handle.h" @@ -160,6 +161,66 @@ ~MockSafeBrowsingUIManager() override = default; }; +std::string set_up_client_side_model() { + flatbuffers::FlatBufferBuilder builder(1024); + std::vector<flatbuffers::Offset<flat::Hash>> hashes; + // Make sure this is sorted. + std::vector<std::string> hashes_vector = {"feature1", "feature2", "feature3", + "token one", "token two"}; + for (std::string& feature : hashes_vector) { + std::vector<uint8_t> hash_data(feature.begin(), feature.end()); + hashes.push_back(flat::CreateHashDirect(builder, &hash_data)); + } + flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<flat::Hash>>> + hashes_flat = builder.CreateVector(hashes); + + std::vector<flatbuffers::Offset<flat::ClientSideModel_::Rule>> rules; + std::vector<int32_t> rule_feature1 = {}; + std::vector<int32_t> rule_feature2 = {0}; + std::vector<int32_t> rule_feature3 = {0, 1}; + rules.push_back( + flat::ClientSideModel_::CreateRuleDirect(builder, &rule_feature1, 0.5)); + rules.push_back( + flat::ClientSideModel_::CreateRuleDirect(builder, &rule_feature2, 2)); + rules.push_back( + flat::ClientSideModel_::CreateRuleDirect(builder, &rule_feature3, 3)); + flatbuffers::Offset< + flatbuffers::Vector<flatbuffers::Offset<flat::ClientSideModel_::Rule>>> + rules_flat = builder.CreateVector(rules); + + std::vector<int32_t> page_terms_vector = {3, 4}; + flatbuffers::Offset<flatbuffers::Vector<int32_t>> page_term_flat = + builder.CreateVector(page_terms_vector); + + std::vector<uint32_t> page_words_vector = {1000U, 2000U, 3000U}; + flatbuffers::Offset<flatbuffers::Vector<uint32_t>> page_word_flat = + builder.CreateVector(page_words_vector); + + std::vector< + flatbuffers::Offset<safe_browsing::flat::TfLiteModelMetadata_::Threshold>> + thresholds_vector = {}; + flatbuffers::Offset<flat::TfLiteModelMetadata> tflite_metadata_flat = + flat::CreateTfLiteModelMetadataDirect(builder, 0, &thresholds_vector, 0, + 0); + flat::ClientSideModelBuilder csd_model_builder(builder); + csd_model_builder.add_version(123); + // The model will always trigger. + csd_model_builder.add_threshold_probability(-1); + csd_model_builder.add_hashes(hashes_flat); + csd_model_builder.add_rule(rules_flat); + csd_model_builder.add_page_term(page_term_flat); + csd_model_builder.add_page_word(page_word_flat); + csd_model_builder.add_max_words_per_term(2); + csd_model_builder.add_murmur_hash_seed(12345U); + csd_model_builder.add_max_shingles_per_page(10); + csd_model_builder.add_shingle_size(3); + csd_model_builder.add_tflite_metadata(tflite_metadata_flat); + builder.Finish(csd_model_builder.Finish()); + + return std::string(reinterpret_cast<char*>(builder.GetBufferPointer()), + builder.GetSize()); +} + } // namespace class ClientSideDetectionHostPrerenderBrowserTest @@ -194,7 +255,7 @@ } void SetUpOnMainThread() override { - set_up_client_side_model(); + flatbuffer_model_str_ = set_up_client_side_model(); host_resolver()->AddRule("*", "127.0.0.1"); ASSERT_TRUE(embedded_test_server()->Start()); } @@ -207,65 +268,6 @@ return browser()->tab_strip_model()->GetActiveWebContents(); } - void set_up_client_side_model() { - flatbuffers::FlatBufferBuilder builder(1024); - std::vector<flatbuffers::Offset<flat::Hash>> hashes; - // Make sure this is sorted. - std::vector<std::string> hashes_vector = { - "feature1", "feature2", "feature3", "token one", "token two"}; - for (std::string& feature : hashes_vector) { - std::vector<uint8_t> hash_data(feature.begin(), feature.end()); - hashes.push_back(flat::CreateHashDirect(builder, &hash_data)); - } - flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<flat::Hash>>> - hashes_flat = builder.CreateVector(hashes); - - std::vector<flatbuffers::Offset<flat::ClientSideModel_::Rule>> rules; - std::vector<int32_t> rule_feature1 = {}; - std::vector<int32_t> rule_feature2 = {0}; - std::vector<int32_t> rule_feature3 = {0, 1}; - rules.push_back( - flat::ClientSideModel_::CreateRuleDirect(builder, &rule_feature1, 0.5)); - rules.push_back( - flat::ClientSideModel_::CreateRuleDirect(builder, &rule_feature2, 2)); - rules.push_back( - flat::ClientSideModel_::CreateRuleDirect(builder, &rule_feature3, 3)); - flatbuffers::Offset< - flatbuffers::Vector<flatbuffers::Offset<flat::ClientSideModel_::Rule>>> - rules_flat = builder.CreateVector(rules); - - std::vector<int32_t> page_terms_vector = {3, 4}; - flatbuffers::Offset<flatbuffers::Vector<int32_t>> page_term_flat = - builder.CreateVector(page_terms_vector); - - std::vector<uint32_t> page_words_vector = {1000U, 2000U, 3000U}; - flatbuffers::Offset<flatbuffers::Vector<uint32_t>> page_word_flat = - builder.CreateVector(page_words_vector); - - std::vector<flatbuffers::Offset< - safe_browsing::flat::TfLiteModelMetadata_::Threshold>> - thresholds_vector = {}; - flatbuffers::Offset<flat::TfLiteModelMetadata> tflite_metadata_flat = - flat::CreateTfLiteModelMetadataDirect(builder, 0, &thresholds_vector, 0, - 0); - flat::ClientSideModelBuilder csd_model_builder(builder); - csd_model_builder.add_version(123); - // The model will always trigger. - csd_model_builder.add_threshold_probability(-1); - csd_model_builder.add_hashes(hashes_flat); - csd_model_builder.add_rule(rules_flat); - csd_model_builder.add_page_term(page_term_flat); - csd_model_builder.add_page_word(page_word_flat); - csd_model_builder.add_max_words_per_term(2); - csd_model_builder.add_murmur_hash_seed(12345U); - csd_model_builder.add_max_shingles_per_page(10); - csd_model_builder.add_shingle_size(3); - csd_model_builder.add_tflite_metadata(tflite_metadata_flat); - builder.Finish(csd_model_builder.Finish()); - flatbuffer_model_str_ = std::string( - reinterpret_cast<char*>(builder.GetBufferPointer()), builder.GetSize()); - } - std::string client_side_model() { return flatbuffer_model_str_; } protected: @@ -299,7 +301,7 @@ } void SetUpOnMainThread() override { - set_up_client_side_model(); + flatbuffer_model_str_ = set_up_client_side_model(); host_resolver()->AddRule("*", "127.0.0.1"); ASSERT_TRUE(embedded_test_server()->Start()); ExclusiveAccessTest::SetUpOnMainThread(); @@ -313,65 +315,6 @@ return browser()->tab_strip_model()->GetActiveWebContents(); } - void set_up_client_side_model() { - flatbuffers::FlatBufferBuilder builder(1024); - std::vector<flatbuffers::Offset<flat::Hash>> hashes; - // Make sure this is sorted. - std::vector<std::string> hashes_vector = { - "feature1", "feature2", "feature3", "token one", "token two"}; - for (std::string& feature : hashes_vector) { - std::vector<uint8_t> hash_data(feature.begin(), feature.end()); - hashes.push_back(flat::CreateHashDirect(builder, &hash_data)); - } - flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<flat::Hash>>> - hashes_flat = builder.CreateVector(hashes); - - std::vector<flatbuffers::Offset<flat::ClientSideModel_::Rule>> rules; - std::vector<int32_t> rule_feature1 = {}; - std::vector<int32_t> rule_feature2 = {0}; - std::vector<int32_t> rule_feature3 = {0, 1}; - rules.push_back( - flat::ClientSideModel_::CreateRuleDirect(builder, &rule_feature1, 0.5)); - rules.push_back( - flat::ClientSideModel_::CreateRuleDirect(builder, &rule_feature2, 2)); - rules.push_back( - flat::ClientSideModel_::CreateRuleDirect(builder, &rule_feature3, 3)); - flatbuffers::Offset< - flatbuffers::Vector<flatbuffers::Offset<flat::ClientSideModel_::Rule>>> - rules_flat = builder.CreateVector(rules); - - std::vector<int32_t> page_terms_vector = {3, 4}; - flatbuffers::Offset<flatbuffers::Vector<int32_t>> page_term_flat = - builder.CreateVector(page_terms_vector); - - std::vector<uint32_t> page_words_vector = {1000U, 2000U, 3000U}; - flatbuffers::Offset<flatbuffers::Vector<uint32_t>> page_word_flat = - builder.CreateVector(page_words_vector); - - std::vector<flatbuffers::Offset< - safe_browsing::flat::TfLiteModelMetadata_::Threshold>> - thresholds_vector = {}; - flatbuffers::Offset<flat::TfLiteModelMetadata> tflite_metadata_flat = - flat::CreateTfLiteModelMetadataDirect(builder, 0, &thresholds_vector, 0, - 0); - flat::ClientSideModelBuilder csd_model_builder(builder); - csd_model_builder.add_version(123); - // The model will always trigger. - csd_model_builder.add_threshold_probability(-1); - csd_model_builder.add_hashes(hashes_flat); - csd_model_builder.add_rule(rules_flat); - csd_model_builder.add_page_term(page_term_flat); - csd_model_builder.add_page_word(page_word_flat); - csd_model_builder.add_max_words_per_term(2); - csd_model_builder.add_murmur_hash_seed(12345U); - csd_model_builder.add_max_shingles_per_page(10); - csd_model_builder.add_shingle_size(3); - csd_model_builder.add_tflite_metadata(tflite_metadata_flat); - builder.Finish(csd_model_builder.Finish()); - flatbuffer_model_str_ = std::string( - reinterpret_cast<char*>(builder.GetBufferPointer()), builder.GetSize()); - } - std::string client_side_model() { return flatbuffer_model_str_; } protected: @@ -884,70 +827,11 @@ ~ClientSideDetectionHostVibrateTest() override = default; void SetUpOnMainThread() override { - set_up_client_side_model(); + flatbuffer_model_str_ = set_up_client_side_model(); host_resolver()->AddRule("*", "127.0.0.1"); ASSERT_TRUE(embedded_test_server()->Start()); } - void set_up_client_side_model() { - flatbuffers::FlatBufferBuilder builder(1024); - std::vector<flatbuffers::Offset<flat::Hash>> hashes; - // Make sure this is sorted. - std::vector<std::string> hashes_vector = { - "feature1", "feature2", "feature3", "token one", "token two"}; - for (std::string& feature : hashes_vector) { - std::vector<uint8_t> hash_data(feature.begin(), feature.end()); - hashes.push_back(flat::CreateHashDirect(builder, &hash_data)); - } - flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<flat::Hash>>> - hashes_flat = builder.CreateVector(hashes); - - std::vector<flatbuffers::Offset<flat::ClientSideModel_::Rule>> rules; - std::vector<int32_t> rule_feature1 = {}; - std::vector<int32_t> rule_feature2 = {0}; - std::vector<int32_t> rule_feature3 = {0, 1}; - rules.push_back( - flat::ClientSideModel_::CreateRuleDirect(builder, &rule_feature1, 0.5)); - rules.push_back( - flat::ClientSideModel_::CreateRuleDirect(builder, &rule_feature2, 2)); - rules.push_back( - flat::ClientSideModel_::CreateRuleDirect(builder, &rule_feature3, 3)); - flatbuffers::Offset< - flatbuffers::Vector<flatbuffers::Offset<flat::ClientSideModel_::Rule>>> - rules_flat = builder.CreateVector(rules); - - std::vector<int32_t> page_terms_vector = {3, 4}; - flatbuffers::Offset<flatbuffers::Vector<int32_t>> page_term_flat = - builder.CreateVector(page_terms_vector); - - std::vector<uint32_t> page_words_vector = {1000U, 2000U, 3000U}; - flatbuffers::Offset<flatbuffers::Vector<uint32_t>> page_word_flat = - builder.CreateVector(page_words_vector); - - std::vector<flatbuffers::Offset< - safe_browsing::flat::TfLiteModelMetadata_::Threshold>> - thresholds_vector = {}; - flatbuffers::Offset<flat::TfLiteModelMetadata> tflite_metadata_flat = - flat::CreateTfLiteModelMetadataDirect(builder, 0, &thresholds_vector, 0, - 0); - flat::ClientSideModelBuilder csd_model_builder(builder); - csd_model_builder.add_version(123); - // The model will always trigger. - csd_model_builder.add_threshold_probability(-1); - csd_model_builder.add_hashes(hashes_flat); - csd_model_builder.add_rule(rules_flat); - csd_model_builder.add_page_term(page_term_flat); - csd_model_builder.add_page_word(page_word_flat); - csd_model_builder.add_max_words_per_term(2); - csd_model_builder.add_murmur_hash_seed(12345U); - csd_model_builder.add_max_shingles_per_page(10); - csd_model_builder.add_shingle_size(3); - csd_model_builder.add_tflite_metadata(tflite_metadata_flat); - builder.Finish(csd_model_builder.Finish()); - flatbuffer_model_str_ = std::string( - reinterpret_cast<char*>(builder.GetBufferPointer()), builder.GetSize()); - } - std::string client_side_model() { return flatbuffer_model_str_; } content::WebContents* GetWebContents() { @@ -1109,4 +993,210 @@ "SBClientPhishing.ServerModelDetectsPhishing.VibrationApi", 1); } +class ClientSideDetectionHostClipboardTest + : public InProcessBrowserTest, + public testing::WithParamInterface<std::string_view> { + public: + ClientSideDetectionHostClipboardTest() { + scoped_feature_list_.InitAndEnableFeatureWithParameters( + kClientSideDetectionClipboardCopyApi, + {{kCSDClipboardCopyApiHCAcceptanceRate.name, "0.0"}}); + } + + ClientSideDetectionHostClipboardTest( + const ClientSideDetectionHostClipboardTest&) = delete; + ClientSideDetectionHostClipboardTest& operator=( + const ClientSideDetectionHostClipboardTest&) = delete; + ~ClientSideDetectionHostClipboardTest() override = default; + + void SetUpOnMainThread() override { + flatbuffer_model_str_ = set_up_client_side_model(); + host_resolver()->AddRule("*", "127.0.0.1"); + ASSERT_TRUE(embedded_test_server()->Start()); + } + + std::string client_side_model() { return flatbuffer_model_str_; } + + content::WebContents* GetWebContents() { + return browser()->tab_strip_model()->GetActiveWebContents(); + } + + // This script uses the Clipboard API to write text to the clipboard. + static constexpr char kClipboardApiScriptTemplate[] = + "navigator.clipboard.writeText($1)"; + // This script uses the (deprecated) `execCommand` method to write the + // currently-selected text to the clipboard. As a result, the script creates a + // temporary DOM element and selects text from that element. + static constexpr char kDocumentExecScriptTemplate[] = R"( + (function() { + const textSelectionArea = document.createElement("textarea"); + textSelectionArea.value = $1; + document.body.append(textSelectionArea); + textSelectionArea.select(); + document.execCommand("copy"); + document.body.removeChild(textSelectionArea); + })(); + )"; + + protected: + void TriggerClipboardCopy(std::string_view copied_text, + base::OnceClosure clipboard_copy_done) { + content::RenderFrameHost* frame = GetWebContents()->GetPrimaryMainFrame(); + frame->GetView()->Focus(); + std::string script = + content::JsReplace(GetClipboardCopyScript(), copied_text); + ASSERT_TRUE(ExecJs(frame, script)); + std::move(clipboard_copy_done).Run(); + } + + base::test::ScopedFeatureList scoped_feature_list_; + + private: + std::string_view GetClipboardCopyScript() { return GetParam(); } + + std::string flatbuffer_model_str_; +}; + +INSTANTIATE_TEST_SUITE_P( + All, + ClientSideDetectionHostClipboardTest, + ::testing::Values( + ClientSideDetectionHostClipboardTest::kClipboardApiScriptTemplate, + ClientSideDetectionHostClipboardTest::kDocumentExecScriptTemplate)); + +class ClipboardObserverWaiter : public content::WebContentsObserver { + public: + explicit ClipboardObserverWaiter(content::WebContents* web_contents) + : WebContentsObserver(web_contents) {} + + void OnTextCopiedToClipboard(content::RenderFrameHost* render_frame_host, + const std::u16string& copied_text) override { + did_copy_to_clipboard_ = true; + run_loop_.Quit(); + } + + void Wait() { + if (!did_copy_to_clipboard_) { + run_loop_.Run(); + } + } + + bool DidCopyToClipboard() { return did_copy_to_clipboard_; } + + private: + bool did_copy_to_clipboard_ = false; + base::RunLoop run_loop_; +}; + +IN_PROC_BROWSER_TEST_P(ClientSideDetectionHostClipboardTest, + ClipboardApiTriggersPreclassificationCheck) { + if (base::FeatureList::IsEnabled(kClientSideDetectionKillswitch)) { + GTEST_SKIP(); + } + SetSafeBrowsingState(browser()->profile()->GetPrefs(), + SafeBrowsingState::ENHANCED_PROTECTION); + + base::HistogramTester histogram_tester; + + FakeClientSideDetectionService fake_csd_service; + fake_csd_service.SetModel(client_side_model()); + + scoped_refptr<StrictMock<MockSafeBrowsingUIManager>> mock_ui_manager = + new StrictMock<MockSafeBrowsingUIManager>(); + + std::unique_ptr<ClientSideDetectionHost> csd_host = + ChromeClientSideDetectionHostDelegate::CreateHost( + browser()->tab_strip_model()->GetActiveWebContents()); + csd_host->set_client_side_detection_service(fake_csd_service.GetWeakPtr()); + csd_host->set_ui_manager(mock_ui_manager.get()); + fake_csd_service.SendModelToRenderers(); + + const GURL initial_url(embedded_test_server()->GetURL("/title1.html")); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); + + histogram_tester.ExpectTotalCount( + "SBClientPhishing.PreClassificationCheckResult.ClipboardCopyApi", 0); + + ClipboardObserverWaiter waiter(GetWebContents()); + ASSERT_FALSE(waiter.DidCopyToClipboard()); + + base::RunLoop run_loop; + TriggerClipboardCopy("this will be copied to the clipboard", + run_loop.QuitClosure()); + run_loop.Run(); + waiter.Wait(); + + EXPECT_TRUE(waiter.DidCopyToClipboard()); + histogram_tester.ExpectTotalCount( + "SBClientPhishing.PreClassificationCheckResult.ClipboardCopyApi", 2); +} + +IN_PROC_BROWSER_TEST_P(ClientSideDetectionHostClipboardTest, + ClipboardApiClassificationTriggersCSPPPing) { + if (base::FeatureList::IsEnabled(kClientSideDetectionKillswitch)) { + GTEST_SKIP(); + } + SetSafeBrowsingState(browser()->profile()->GetPrefs(), + SafeBrowsingState::ENHANCED_PROTECTION); + + base::HistogramTester histogram_tester; + + FakeClientSideDetectionService fake_csd_service; + fake_csd_service.SetModel(client_side_model()); + + scoped_refptr<StrictMock<MockSafeBrowsingUIManager>> mock_ui_manager = + new StrictMock<MockSafeBrowsingUIManager>(); + + std::unique_ptr<ClientSideDetectionHost> csd_host = + ChromeClientSideDetectionHostDelegate::CreateHost( + browser()->tab_strip_model()->GetActiveWebContents()); + csd_host->set_client_side_detection_service(fake_csd_service.GetWeakPtr()); + csd_host->set_ui_manager(mock_ui_manager.get()); + fake_csd_service.SendModelToRenderers(); + + base::RunLoop run_loop; + fake_csd_service.SetRequestCallback(run_loop.QuitClosure()); + + const GURL initial_url(embedded_test_server()->GetURL("/title1.html")); + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); + + histogram_tester.ExpectTotalCount( + "SBClientPhishing.PhishingDetectorResult.ClipboardCopyApi", 0); + histogram_tester.ExpectTotalCount( + "SBClientPhishing.ClientSideDetectionTypeRequest", 0); + histogram_tester.ExpectTotalCount( + "SBClientPhishing.ServerModelDetectsPhishing.ClipboardCopyApi", 0); + + // Bypass the pre-classification check because it would otherwise return + // `PreClassificationCheckResult::NO_CLASSIFY_PRIVATE_IP`. + csd_host->OnPhishingPreClassificationDone( + ClientSideDetectionType::CLIPBOARD_COPY_API, + /*should_classify=*/true, /*is_sample_ping=*/false, + /*did_match_high_confidence_allowlist=*/false); + run_loop.Run(); + + histogram_tester.ExpectTotalCount( + "SBClientPhishing.PhishingDetectorResult.ClipboardCopyApi", 1); + histogram_tester.ExpectTotalCount( + "SBClientPhishing.ClientSideDetectionTypeRequest", 1); + histogram_tester.ExpectTotalCount( + "SBClientPhishing.ServerModelDetectsPhishing.ClipboardCopyApi", 0); + + EXPECT_EQ(fake_csd_service.saved_request().model_version(), 123); + + // Expect an interstitial to be shown. + EXPECT_CALL(*mock_ui_manager, DisplayBlockingPage(_)); + + ASSERT_FALSE(fake_csd_service.saved_callback_is_null()); + std::move(fake_csd_service.saved_callback()) + .Run(initial_url, true, net::HTTP_OK, std::nullopt); + + histogram_tester.ExpectTotalCount( + "SBClientPhishing.PhishingDetectorResult.ClipboardCopyApi", 1); + histogram_tester.ExpectTotalCount( + "SBClientPhishing.ClientSideDetectionTypeRequest", 1); + histogram_tester.ExpectTotalCount( + "SBClientPhishing.ServerModelDetectsPhishing.ClipboardCopyApi", 1); +} + } // namespace safe_browsing
diff --git a/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc b/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc index dc8b8cb..a09e8c8 100644 --- a/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc +++ b/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
@@ -540,6 +540,8 @@ return "VibrationApi"; case safe_browsing::ClientSideDetectionType::FULLSCREEN_API: return "FullscreenApi"; + case safe_browsing::ClientSideDetectionType::CLIPBOARD_COPY_API: + return "ClipboardCopyApi"; } } @@ -1899,6 +1901,133 @@ "SBClientPhishing.PreClassificationCheckResult.KeyboardLockRequested", 1); } +TEST_F(ClientSideDetectionHostTest, + ClipboardCopyApiCallDoesNotProceedWithClassification) { + if (base::FeatureList::IsEnabled(kClientSideDetectionKillswitch)) { + GTEST_SKIP(); + } + + SetFeatures({}, {kClientSideDetectionClipboardCopyApi}); + SetEnhancedProtectionPrefForTests(profile()->GetPrefs(), true); + base::HistogramTester histogram_tester; + + GURL url("http://host.com/"); + database_manager_->SetAllowlistLookupDetailsForUrl(url, /*match=*/true); + ExpectPreClassificationChecks(url, &kFalse, &kFalse, nullptr, nullptr, + nullptr); + NavigateAndKeepLoading(web_contents(), url); + WaitAndCheckPreClassificationChecks(); + + // Check that the clipboard histograms haven't been recorded yet. + histogram_tester.ExpectTotalCount( + "SBClientPhishing.MatchCSDAllowlistOnClipboardCopyApi", 0); + histogram_tester.ExpectTotalCount( + "SBClientPhishing.MatchHighConfidenceAllowlist.ClipboardCopyApi", 0); + histogram_tester.ExpectTotalCount( + "SBClientPhishing.PreClassificationCheckResult.ClipboardCopyApi", 0); + + ExpectPreClassificationChecks(url, &kFalse, &kFalse, nullptr, nullptr, + nullptr); + csd_host_->OnTextCopiedToClipboard(main_rfh(), u"test"); + WaitAndCheckPreClassificationChecks(); + + // The feature to send CSP pings is disabled, so nothing will be classified + // (or included in the HC allowlist). + histogram_tester.ExpectTotalCount( + "SBClientPhishing.MatchCSDAllowlistOnClipboardCopyApi", 1); + histogram_tester.ExpectTotalCount( + "SBClientPhishing.MatchHighConfidenceAllowlist.ClipboardCopyApi", 1); + histogram_tester.ExpectBucketCount( + "SBClientPhishing.PreClassificationCheckResult.ClipboardCopyApi", + PreClassificationCheckResult::NO_CLASSIFY_ALLOWLIST_METRIC, 1); +} + +TEST_F(ClientSideDetectionHostTest, + ClipboardCopyApiCallProceedsWithClassification) { + if (base::FeatureList::IsEnabled(kClientSideDetectionKillswitch)) { + GTEST_SKIP(); + } + + SetFeatures({kClientSideDetectionClipboardCopyApi}, {}); + SetEnhancedProtectionPrefForTests(profile()->GetPrefs(), true); + base::HistogramTester histogram_tester; + + GURL url("http://host.com/"); + database_manager_->SetAllowlistLookupDetailsForUrl(url, /*match=*/true); + ExpectPreClassificationChecks(url, &kFalse, &kFalse, nullptr, nullptr, + nullptr); + NavigateAndKeepLoading(web_contents(), url); + WaitAndCheckPreClassificationChecks(); + + // Check that the clipboard histograms haven't been recorded yet. + histogram_tester.ExpectTotalCount( + "SBClientPhishing.MatchCSDAllowlistOnClipboardCopyApi", 0); + histogram_tester.ExpectTotalCount( + "SBClientPhishing.MatchHighConfidenceAllowlist.ClipboardCopyApi", 0); + histogram_tester.ExpectTotalCount( + "SBClientPhishing.PreClassificationCheckResult.ClipboardCopyApi", 0); + + ExpectPreClassificationChecks(url, &kFalse, &kFalse, nullptr, nullptr, + nullptr); + csd_host_->OnTextCopiedToClipboard(main_rfh(), u"test"); + WaitAndCheckPreClassificationChecks(); + + // The feature to send CSP pings is enabled and the host is not included in + // the HC allowlist, so normal classification will occur. + histogram_tester.ExpectTotalCount( + "SBClientPhishing.MatchCSDAllowlistOnClipboardCopyApi", 1); + histogram_tester.ExpectTotalCount( + "SBClientPhishing.MatchHighConfidenceAllowlist.ClipboardCopyApi", 1); + histogram_tester.ExpectBucketCount( + "SBClientPhishing.PreClassificationCheckResult.ClipboardCopyApi", + PreClassificationCheckResult::CLASSIFY, 1); +} + +TEST_F( + ClientSideDetectionHostTest, + ClipboardCopyApiCallDoesNotProceedWithClassificationWithHighHCAcceptanceRate) { + if (base::FeatureList::IsEnabled(kClientSideDetectionKillswitch)) { + GTEST_SKIP(); + } + + feature_list_.InitAndEnableFeatureWithParameters( + kClientSideDetectionClipboardCopyApi, + {{kCSDClipboardCopyApiHCAcceptanceRate.name, "1.0"}}); + + SetEnhancedProtectionPrefForTests(profile()->GetPrefs(), true); + base::HistogramTester histogram_tester; + + GURL url("http://host.com/"); + database_manager_->SetAllowlistLookupDetailsForUrl(url, /*match=*/true); + ExpectPreClassificationChecks(url, &kFalse, &kFalse, nullptr, nullptr, + nullptr); + NavigateAndKeepLoading(web_contents(), url); + WaitAndCheckPreClassificationChecks(); + + // Check that the clipboard histograms haven't been recorded yet. + histogram_tester.ExpectTotalCount( + "SBClientPhishing.MatchCSDAllowlistOnClipboardCopyApi", 0); + histogram_tester.ExpectTotalCount( + "SBClientPhishing.MatchHighConfidenceAllowlist.ClipboardCopyApi", 0); + histogram_tester.ExpectTotalCount( + "SBClientPhishing.PreClassificationCheckResult.ClipboardCopyApi", 0); + + ExpectPreClassificationChecks(url, &kFalse, &kFalse, nullptr, nullptr, + nullptr); + csd_host_->OnTextCopiedToClipboard(main_rfh(), u"test"); + WaitAndCheckPreClassificationChecks(); + + // The feature to send CSP pings is enabled, but the host is included in the + // HC allowlist, so classification will not occur. + histogram_tester.ExpectTotalCount( + "SBClientPhishing.MatchCSDAllowlistOnClipboardCopyApi", 1); + histogram_tester.ExpectTotalCount( + "SBClientPhishing.MatchHighConfidenceAllowlist.ClipboardCopyApi", 1); + histogram_tester.ExpectBucketCount( + "SBClientPhishing.PreClassificationCheckResult.ClipboardCopyApi", + PreClassificationCheckResult::NO_CLASSIFY_MATCH_HC_ALLOWLIST, 1); +} + class ClientSideDetectionHostNotificationTest : public ClientSideDetectionHostTest { public:
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc index ebdfa11..c2d36e7 100644 --- a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc +++ b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.cc
@@ -159,8 +159,8 @@ content_analysis_request_.set_analysis_connector(connector); } -void BinaryUploadService::Request::set_url(const std::string& url) { - content_analysis_request_.mutable_request_data()->set_url(url); +void BinaryUploadService::Request::set_url(const GURL& url) { + content_analysis_request_.mutable_request_data()->set_url(url.spec()); } void BinaryUploadService::Request::set_source(const std::string& source) {
diff --git a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h index a74ef56..60da559 100644 --- a/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h +++ b/chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h
@@ -165,7 +165,7 @@ // Methods for modifying the ContentAnalysisRequest. void set_analysis_connector( enterprise_connectors::AnalysisConnector connector); - void set_url(const std::string& url); + void set_url(const GURL& url); void set_source(const std::string& source); void set_destination(const std::string& destination); void set_csd(ClientDownloadRequest csd);
diff --git a/chrome/browser/safe_browsing/download_protection/deep_scanning_request.cc b/chrome/browser/safe_browsing/download_protection/deep_scanning_request.cc index 6a302be..d50da1e 100644 --- a/chrome/browser/safe_browsing/download_protection/deep_scanning_request.cc +++ b/chrome/browser/safe_browsing/download_protection/deep_scanning_request.cc
@@ -746,11 +746,12 @@ Profile::FromBrowserContext(metadata_->GetBrowserContext())); } -std::string DeepScanningRequest::url() const { +const GURL& DeepScanningRequest::url() const { if (metadata_->GetURL().is_valid()) { - return metadata_->GetURL().spec(); + return metadata_->GetURL(); } - return ""; + return GURL::EmptyGURL(); + ; } const GURL& DeepScanningRequest::tab_url() const { @@ -830,9 +831,12 @@ // Bypassed verdicts are given when a user continues a download after being // warned by WP, so it is considered safe here. // For obfuscated download files, deobfuscate it if the scan returns a safe - // verdict. + // verdict or result is unknown. + // TODO(crbug.com/378490429): Add support in obfuscation module for skipping + // malware scan for password protected files. if ((event_result == enterprise_connectors::EventResult::ALLOWED || - event_result == enterprise_connectors::EventResult::BYPASSED) && + event_result == enterprise_connectors::EventResult::BYPASSED || + result == DownloadCheckResult::UNKNOWN) && metadata_->IsObfuscated()) { base::ThreadPool::PostTaskAndReplyWithResult( FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
diff --git a/chrome/browser/safe_browsing/download_protection/deep_scanning_request.h b/chrome/browser/safe_browsing/download_protection/deep_scanning_request.h index 6be4ac7..ee5de82 100644 --- a/chrome/browser/safe_browsing/download_protection/deep_scanning_request.h +++ b/chrome/browser/safe_browsing/download_protection/deep_scanning_request.h
@@ -114,7 +114,7 @@ std::string tab_title() const override; std::string user_action_id() const override; std::string email() const override; - std::string url() const override; + const GURL& url() const override; const GURL& tab_url() const override; enterprise_connectors::ContentAnalysisRequest::Reason reason() const override; google::protobuf::RepeatedPtrField<::safe_browsing::ReferrerChainEntry>
diff --git a/chrome/browser/sessions/BUILD.gn b/chrome/browser/sessions/BUILD.gn new file mode 100644 index 0000000..fd1a3fab --- /dev/null +++ b/chrome/browser/sessions/BUILD.gn
@@ -0,0 +1,15 @@ +# Copyright 2025 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +source_set("sessions") { + sources = [ + "session_restore.h", + "session_restore_observer.h", + ] + public_deps = [ + "//base", + "//components/sessions", + "//ui/base:types", + ] +}
diff --git a/chrome/browser/sessions/tab_restore_service_unittest.cc b/chrome/browser/sessions/tab_restore_service_unittest.cc index 0427a73c..f8a95b23 100644 --- a/chrome/browser/sessions/tab_restore_service_unittest.cc +++ b/chrome/browser/sessions/tab_restore_service_unittest.cc
@@ -272,7 +272,7 @@ std::optional<tab_groups::TabGroupId> group = std::nullopt, std::optional<tab_groups::TabGroupVisualData> group_visual_data = std::nullopt, - absl ::optional<ExtraData> extra_data = std::nullopt) { + std::optional<ExtraData> extra_data = std::nullopt) { // Create new window / tab IDs so that these remain distinct. window_id_ = SessionID::NewUnique(); tab_id_ = SessionID::NewUnique();
diff --git a/chrome/browser/signin/bound_session_credentials/throttled_gaia_auth_fetcher.cc b/chrome/browser/signin/bound_session_credentials/throttled_gaia_auth_fetcher.cc index a540b63..01d9d794 100644 --- a/chrome/browser/signin/bound_session_credentials/throttled_gaia_auth_fetcher.cc +++ b/chrome/browser/signin/bound_session_credentials/throttled_gaia_auth_fetcher.cc
@@ -37,7 +37,7 @@ void ThrottledGaiaAuthFetcher::CreateAndStartGaiaFetcher( const std::string& body, const std::string& body_content_type, - const std::string& headers, + const net::HttpRequestHeaders& headers, const GURL& gaia_gurl, network::mojom::CredentialsMode credentials_mode, const net::NetworkTrafficAnnotationTag& traffic_annotation) { @@ -64,7 +64,7 @@ void ThrottledGaiaAuthFetcher::OnGaiaFetcherResumedOrCancelled( const std::string& body, const std::string& body_content_type, - const std::string& headers, + const net::HttpRequestHeaders& headers, const GURL& gaia_gurl, network::mojom::CredentialsMode credentials_mode, const net::NetworkTrafficAnnotationTag& traffic_annotation,
diff --git a/chrome/browser/signin/bound_session_credentials/throttled_gaia_auth_fetcher.h b/chrome/browser/signin/bound_session_credentials/throttled_gaia_auth_fetcher.h index f7c8f95..26d4be9d 100644 --- a/chrome/browser/signin/bound_session_credentials/throttled_gaia_auth_fetcher.h +++ b/chrome/browser/signin/bound_session_credentials/throttled_gaia_auth_fetcher.h
@@ -39,7 +39,7 @@ void CreateAndStartGaiaFetcher( const std::string& body, const std::string& body_content_type, - const std::string& headers, + const net::HttpRequestHeaders& headers, const GURL& gaia_gurl, network::mojom::CredentialsMode credentials_mode, const net::NetworkTrafficAnnotationTag& traffic_annotation) override; @@ -48,7 +48,7 @@ void OnGaiaFetcherResumedOrCancelled( const std::string& body, const std::string& body_content_type, - const std::string& headers, + const net::HttpRequestHeaders& headers, const GURL& gaia_gurl, network::mojom::CredentialsMode credentials_mode, const net::NetworkTrafficAnnotationTag& traffic_annotation,
diff --git a/chrome/browser/signin/signin_promo_util.cc b/chrome/browser/signin/signin_promo_util.cc index 2b35ad6..90a4a2a 100644 --- a/chrome/browser/signin/signin_promo_util.cc +++ b/chrome/browser/signin/signin_promo_util.cc
@@ -443,10 +443,17 @@ if (!ArePromotionsEnabled()) { return false; } - const int show_count = SigninPrefs(*profile_->GetPrefs()) - .GetSyncPromoIdentityPillShownCount(account.gaia); - const int used_count = SigninPrefs(*profile_->GetPrefs()) - .GetSyncPromoIdentityPillUsedCount(account.gaia); + + SigninPrefs signin_prefs(*profile_->GetPrefs()); + const int show_count = + switches::IsAvatarSyncPromoFeatureEnabled() + ? signin_prefs.GetSyncPromoIdentityPillShownCount(account.gaia) + : signin_prefs.GetHistorySyncPromoIdentityPillShownCount( + account.gaia); + const int used_count = + switches::IsAvatarSyncPromoFeatureEnabled() + ? signin_prefs.GetSyncPromoIdentityPillUsedCount(account.gaia) + : signin_prefs.GetHistorySyncPromoIdentityPillUsedCount(account.gaia); return show_count < max_shown_count_ && used_count < max_used_count_; } @@ -458,8 +465,12 @@ // promo should be shown only for signed in users). return; } - SigninPrefs(*profile_->GetPrefs()) - .IncrementSyncPromoIdentityPillShownCount(account.gaia); + + SigninPrefs signin_prefs(*profile_->GetPrefs()); + switches::IsAvatarSyncPromoFeatureEnabled() + ? signin_prefs.IncrementSyncPromoIdentityPillShownCount(account.gaia) + : signin_prefs.IncrementHistorySyncPromoIdentityPillShownCount( + account.gaia); } void SyncPromoIdentityPillManager::RecordPromoUsed() { @@ -470,8 +481,11 @@ // promo should be shown only for signed in users). return; } - SigninPrefs(*profile_->GetPrefs()) - .IncrementSyncPromoIdentityPillUsedCount(account.gaia); + SigninPrefs signin_prefs(*profile_->GetPrefs()); + switches::IsAvatarSyncPromoFeatureEnabled() + ? signin_prefs.IncrementSyncPromoIdentityPillUsedCount(account.gaia) + : signin_prefs.IncrementHistorySyncPromoIdentityPillUsedCount( + account.gaia); } bool SyncPromoIdentityPillManager::ArePromotionsEnabled() const {
diff --git a/chrome/browser/signin/signin_util.cc b/chrome/browser/signin/signin_util.cc index 594c4dc..a02cdcf 100644 --- a/chrome/browser/signin/signin_util.cc +++ b/chrome/browser/signin/signin_util.cc
@@ -26,6 +26,7 @@ #include "chrome/browser/signin/account_reconcilor_factory.h" #include "chrome/browser/signin/identity_manager_factory.h" #include "chrome/browser/sync/sync_service_factory.h" +#include "chrome/browser/ui/webui/signin/signin_utils_desktop.h" #include "chrome/common/pref_names.h" #include "chrome/common/url_constants.h" #include "chrome/grit/generated_resources.h" @@ -387,6 +388,64 @@ } return true; } + +bool ShouldShowAvatarSyncPromo(Profile* profile) { + CHECK(switches::IsAvatarSyncPromoFeatureEnabled()); + + // Do not show the promo for users that are not signed in. (E.g. Signed out, + // Signin Pending or already syncing). + if (GetSignedInState(IdentityManagerFactory::GetForProfile(profile)) != + signin_util::SignedInState::kSignedIn) { + return false; + } + + // SyncService should be usable. + syncer::SyncService* sync_service = + SyncServiceFactory::GetForProfile(profile); + if (!sync_service) { + return false; + } + if (sync_service->HasDisableReason( + syncer::SyncService::DISABLE_REASON_ENTERPRISE_POLICY)) { + return false; + } + + // A profile signed in with a managed account should not see this promo. + if (enterprise_util::UserAcceptedAccountManagement(profile)) { + return false; + } + + // Do not show the promo if there was a previously syncing account that does + // not match the currently signed in one. + signin::IdentityManager* identity_manager = + IdentityManagerFactory::GetForProfile(profile); + CoreAccountInfo core_account_info = + identity_manager->GetPrimaryAccountInfo(signin::ConsentLevel::kSignin); + if (core_account_info.IsEmpty()) { + return false; + } + PrefService* pref_service = profile->GetPrefs(); + GaiaId previously_syncing_gaia_id = + GaiaId(pref_service->GetString(prefs::kGoogleServicesLastSyncingGaiaId)); + if (IsCrossAccountError(profile, core_account_info.gaia)) { + return false; + } + + // For non-dice users, do not show the promo for users that have been signed + // for a short period of time. + // TODO(crbug.com/435113265): Confirm minimum cookie age value. + constexpr base::TimeDelta kMinimumCookieAge = base::Days(7); + if (pref_service->GetBoolean(prefs::kExplicitBrowserSignin)) { + const base::Time last_changed = base::Time::FromSecondsSinceUnixEpoch( + pref_service->GetDouble(prefs::kGaiaCookieChangedTime)); + if (last_changed.is_null() || + (base::Time::Now() - last_changed < kMinimumCookieAge)) { + return false; + } + } + + return true; +} #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) } // namespace signin_util
diff --git a/chrome/browser/signin/signin_util.h b/chrome/browser/signin/signin_util.h index 5a5d6730..88df5738 100644 --- a/chrome/browser/signin/signin_util.h +++ b/chrome/browser/signin/signin_util.h
@@ -176,6 +176,11 @@ // TODO(crbug.com/419741847): Consider using also on mobile and moving the // method as necessary. bool ShouldShowHistorySyncOptinScreen(Profile& profile); + +// The avatar sync promo is only shown to users with specific sign in states. +// Requires the feature enabling through +// `switches::IsAvatarSyncPromoFeatureEnabled()`. +bool ShouldShowAvatarSyncPromo(Profile* profile); #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) } // namespace signin_util
diff --git a/chrome/browser/signin/signin_util_unittest.cc b/chrome/browser/signin/signin_util_unittest.cc index 2b3f487b..e0212c14 100644 --- a/chrome/browser/signin/signin_util_unittest.cc +++ b/chrome/browser/signin/signin_util_unittest.cc
@@ -6,8 +6,10 @@ #include <memory> +#include "base/test/scoped_feature_list.h" #include "build/buildflag.h" #include "chrome/browser/browser_process.h" +#include "chrome/browser/enterprise/util/managed_browser_utils.h" #include "chrome/browser/prefs/browser_prefs.h" #include "chrome/browser/signin/identity_manager_factory.h" #include "chrome/browser/sync/sync_service_factory.h" @@ -41,6 +43,8 @@ const char kLegacyPolicyPrimaryAccountKeepExistingData[] = "primary_account_keep_existing_data"; +const GaiaId kSignedInGaiaId("signed_in_gaia_id"); + } // namespace class SigninUtilTest : public BrowserWithTestWindowTest { @@ -380,8 +384,11 @@ signin::IdentityManager* identity_manager = IdentityManagerFactory::GetForProfile(profile()); CHECK(identity_manager); - signin::MakePrimaryAccountAvailable(identity_manager, "test@gmail.com", - signin::ConsentLevel::kSignin); + signin::MakeAccountAvailable(identity_manager, + signin::AccountAvailabilityOptionsBuilder() + .AsPrimary(signin::ConsentLevel::kSignin) + .WithGaiaId(kSignedInGaiaId) + .Build("test@gmail.com")); } void SignInAndSetUpSyncService() { @@ -398,6 +405,24 @@ test_sync_service()->GetUserSettings()->SetSelectedTypes( /*sync_everything=*/false, syncer::UserSelectableTypeSet()); } + + void SetupForAvatarSyncPromo() { + SignInAndSetUpSyncService(); + DisableAllSyncedDataTypes(); + + // Simulate setting enough time passing for the cookie change. + profile()->GetPrefs()->SetDouble( + prefs::kGaiaCookieChangedTime, + (base::Time::Now() - base::Days(8)).InSecondsFSinceUnixEpoch()); + + // The rest of the setup should be aligned with the default profile + // initialization/signin. + } + + private: + // Use this flag to simplify test writing and not restrict to Windows only. + base::test::ScopedFeatureList scoped_feature_list_{ + switches::kAvatarButtonSyncPromoForTesting}; }; #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) @@ -410,6 +435,7 @@ ASSERT_FALSE( identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSignin)); EXPECT_FALSE(signin_util::ShouldShowHistorySyncOptinScreen(*profile())); + EXPECT_FALSE(signin_util::ShouldShowAvatarSyncPromo(profile())); } TEST_F(SigninUtilHistorySyncOptinTest, @@ -417,6 +443,7 @@ Signin(); ASSERT_FALSE(test_sync_service()); EXPECT_FALSE(signin_util::ShouldShowHistorySyncOptinScreen(*profile())); + EXPECT_FALSE(signin_util::ShouldShowAvatarSyncPromo(profile())); } TEST_F(SigninUtilHistorySyncOptinTest, @@ -428,6 +455,7 @@ test_sync_service()->SetAllowedByEnterprisePolicy(false); EXPECT_FALSE(signin_util::ShouldShowHistorySyncOptinScreen(*profile())); + EXPECT_FALSE(signin_util::ShouldShowAvatarSyncPromo(profile())); } TEST_F(SigninUtilHistorySyncOptinTest, ShouldNotShowHistorySyncOptinScreenIfUserIsAlreadyOptedIn) { @@ -444,6 +472,7 @@ syncer::UserSelectableType::kSavedTabGroups, true); EXPECT_FALSE(signin_util::ShouldShowHistorySyncOptinScreen(*profile())); + EXPECT_FALSE(signin_util::ShouldShowAvatarSyncPromo(profile())); } TEST_F(SigninUtilHistorySyncOptinTest, @@ -492,6 +521,57 @@ EXPECT_TRUE(signin_util::ShouldShowHistorySyncOptinScreen(*profile())); } +TEST_F(SigninUtilHistorySyncOptinTest, ShouldShowAvatarSyncPromo) { + SetupForAvatarSyncPromo(); + EXPECT_TRUE(signin_util::ShouldShowAvatarSyncPromo(profile())); +} + +TEST_F(SigninUtilHistorySyncOptinTest, + ShouldNotShowAvatarSyncPromoManagedProfile) { + SetupForAvatarSyncPromo(); + ASSERT_TRUE(signin_util::ShouldShowAvatarSyncPromo(profile())); + + enterprise_util::SetUserAcceptedAccountManagement(profile(), true); + EXPECT_FALSE(signin_util::ShouldShowAvatarSyncPromo(profile())); +} + +TEST_F(SigninUtilHistorySyncOptinTest, + ShouldShowAvatarSyncPromoBasedOnProfleAlreadySyncing) { + SetupForAvatarSyncPromo(); + ASSERT_TRUE(signin_util::ShouldShowAvatarSyncPromo(profile())); + + // Promo should not show if the previously syncing gaia id is different than + // the current signed in one. + const GaiaId previously_syncing_gaia_id("syncing_gaia_id"); + ASSERT_NE(previously_syncing_gaia_id, kSignedInGaiaId); + profile()->GetPrefs()->SetString(prefs::kGoogleServicesLastSyncingGaiaId, + previously_syncing_gaia_id.ToString()); + EXPECT_FALSE(signin_util::ShouldShowAvatarSyncPromo(profile())); + + // Promo can show if the gaia id match. + profile()->GetPrefs()->SetString(prefs::kGoogleServicesLastSyncingGaiaId, + kSignedInGaiaId.ToString()); + EXPECT_TRUE(signin_util::ShouldShowAvatarSyncPromo(profile())); +} + +TEST_F(SigninUtilHistorySyncOptinTest, + ShouldShowAvatarSyncPromoBasedOnGaiaCookieAge) { + SetupForAvatarSyncPromo(); + ASSERT_TRUE(signin_util::ShouldShowAvatarSyncPromo(profile())); + + // Promo should not show if the gaia cookie age is too short. + profile()->GetPrefs()->SetDouble( + prefs::kGaiaCookieChangedTime, + (base::Time::Now() - base::Days(5)).InSecondsFSinceUnixEpoch()); + EXPECT_FALSE(signin_util::ShouldShowAvatarSyncPromo(profile())); + + // Promo can show if the gaia cookie age is long enough. + profile()->GetPrefs()->SetDouble( + prefs::kGaiaCookieChangedTime, + (base::Time::Now() - base::Days(10)).InSecondsFSinceUnixEpoch()); + EXPECT_TRUE(signin_util::ShouldShowAvatarSyncPromo(profile())); +} + class SigninUtilHistorySyncOptinForManagedSettingsTest : public SigninUtilHistorySyncOptinTest, public testing::WithParamInterface<syncer::UserSelectableType> {};
diff --git a/chrome/browser/sync/sync_ui_util.cc b/chrome/browser/sync/sync_ui_util.cc index 42915150..9d7632f 100644 --- a/chrome/browser/sync/sync_ui_util.cc +++ b/chrome/browser/sync/sync_ui_util.cc
@@ -297,6 +297,7 @@ } SyncStatusLabels GetAvatarSyncErrorLabelsForSettings( + Profile* profile, AvatarSyncErrorType error) { switch (error) { case AvatarSyncErrorType::kSyncPaused: @@ -347,13 +348,17 @@ IDS_PROFILES_ACCOUNT_REMOVAL_TITLE, SyncStatusActionType::kConfirmSyncSettings}; - case AvatarSyncErrorType::kManagedUserUnrecoverableError: - return {SyncStatusMessageType::kSyncError, - IDS_SYNC_STATUS_UNRECOVERABLE_ERROR_NEEDS_SIGNOUT, - IDS_SYNC_RELOGIN_BUTTON, IDS_PROFILES_ACCOUNT_REMOVAL_TITLE, - SyncStatusActionType::kReauthenticate}; - case AvatarSyncErrorType::kUnrecoverableError: + // Managed users get different labels. + if (!ChromeSigninClientFactory::GetForProfile(profile) + ->IsClearPrimaryAccountAllowed( + IdentityManagerFactory::GetForProfile(profile) + ->HasPrimaryAccount(signin::ConsentLevel::kSync))) { + return {SyncStatusMessageType::kSyncError, + IDS_SYNC_STATUS_UNRECOVERABLE_ERROR_NEEDS_SIGNOUT, + IDS_SYNC_RELOGIN_BUTTON, IDS_PROFILES_ACCOUNT_REMOVAL_TITLE, + SyncStatusActionType::kReauthenticate}; + } return {SyncStatusMessageType::kSyncError, IDS_SYNC_STATUS_UNRECOVERABLE_ERROR, IDS_SYNC_RELOGIN_BUTTON, IDS_PROFILES_ACCOUNT_REMOVAL_TITLE, @@ -376,13 +381,6 @@ // RequiresClientUpgrade() is unrecoverable, but is treated separately // below. if (service->HasUnrecoverableError() && !service->RequiresClientUpgrade()) { - // Display different messages and buttons for managed accounts. - if (!ChromeSigninClientFactory::GetForProfile(profile) - ->IsClearPrimaryAccountAllowed( - IdentityManagerFactory::GetForProfile(profile) - ->HasPrimaryAccount(signin::ConsentLevel::kSync))) { - return AvatarSyncErrorType::kManagedUserUnrecoverableError; - } return AvatarSyncErrorType::kUnrecoverableError; } } @@ -435,7 +433,6 @@ IDS_SYNC_ERROR_TRUSTED_VAULT_USER_MENU_ERROR_DESCRIPTION, base::UTF8ToUTF16(user_email)); case AvatarSyncErrorType::kSettingsUnconfirmedError: - case AvatarSyncErrorType::kManagedUserUnrecoverableError: case AvatarSyncErrorType::kUnrecoverableError: return l10n_util::GetStringUTF16(IDS_SYNC_ERROR_USER_MENU_TITLE); }
diff --git a/chrome/browser/sync/sync_ui_util.h b/chrome/browser/sync/sync_ui_util.h index ac440096..d8a82d41 100644 --- a/chrome/browser/sync/sync_ui_util.h +++ b/chrome/browser/sync/sync_ui_util.h
@@ -56,8 +56,6 @@ // Sync errors that should be exposed to the user through the avatar button. enum AvatarSyncErrorType { - // Unrecoverable error for managed users. - kManagedUserUnrecoverableError, // Unrecoverable error for regular users. kUnrecoverableError, // Sync paused (e.g. persistent authentication error). @@ -108,7 +106,8 @@ SyncStatusLabels GetSyncStatusLabelsForSettings( const syncer::SyncService* service); -SyncStatusLabels GetAvatarSyncErrorLabelsForSettings(AvatarSyncErrorType error); +SyncStatusLabels GetAvatarSyncErrorLabelsForSettings(Profile* profile, + AvatarSyncErrorType error); // Gets the error in the sync machinery (if any) that should be exposed to the // user through the titlebar avatar button. If std::nullopt is returned, this
diff --git a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabGroupModelFilterImpl.java b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabGroupModelFilterImpl.java index 7fea7fc..8d539f15 100644 --- a/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabGroupModelFilterImpl.java +++ b/chrome/browser/tabmodel/android/java/src/org/chromium/chrome/browser/tabmodel/TabGroupModelFilterImpl.java
@@ -383,6 +383,18 @@ } } + UndoGroupMetadataImpl undoGroupMetadata = + new UndoGroupMetadataImpl( + destinationTabGroupId, + isIncognito(), + tabsIncludingDestination, + originalIndexes, + originalRootIds, + originalTabGroupIds, + destinationGroupTitle, + destinationGroupColorId, + destinationGroupTitleCollapsed); + // TODO(b/339480989): Resequence this so that we iterate over observers multiple times // and emit one event per loop to be consistent with other usages. for (TabGroupModelFilterObserver observer : mGroupFilterObserver) { @@ -396,17 +408,6 @@ // Since the undo group merge logic is unsupported when called from the tab strip, // skip notifying the UndoGroupSnackbarController observer which shows the snackbar. if (!skipUpdateTabModel) { - UndoGroupMetadataImpl undoGroupMetadata = - new UndoGroupMetadataImpl( - destinationTabGroupId, - isIncognito(), - tabsIncludingDestination, - originalIndexes, - originalRootIds, - originalTabGroupIds, - destinationGroupTitle, - destinationGroupColorId, - destinationGroupTitleCollapsed); observer.showUndoGroupSnackbar(undoGroupMetadata); } else { for (int i = 0; i < tabsIncludingDestination.size(); i++) { @@ -561,6 +562,18 @@ } } + UndoGroupMetadataImpl undoGroupMetadata = + new UndoGroupMetadataImpl( + assumeNonNull(destinationTabGroupId), + isIncognito(), + mergedTabs, + originalIndexes, + originalRootIds, + originalTabGroupIds, + destinationGroupTitle, + destinationGroupColorId, + destinationGroupTitleCollapsed); + for (TabGroupModelFilterObserver observer : mGroupFilterObserver) { if (willMergingCreateNewGroup) { observer.didCreateNewGroup(destinationTab, this); @@ -568,17 +581,6 @@ // Do not show a snackbar for new tab group creations as they launch a dialog. if (notify && !willMergingCreateNewGroup) { - UndoGroupMetadataImpl undoGroupMetadata = - new UndoGroupMetadataImpl( - assumeNonNull(destinationTabGroupId), - isIncognito(), - mergedTabs, - originalIndexes, - originalRootIds, - originalTabGroupIds, - destinationGroupTitle, - destinationGroupColorId, - destinationGroupTitleCollapsed); observer.showUndoGroupSnackbar(undoGroupMetadata); } else { for (int i = 0; i < mergedTabs.size(); i++) {
diff --git a/chrome/browser/themes/BUILD.gn b/chrome/browser/themes/BUILD.gn index aff4ad1..57844c0 100644 --- a/chrome/browser/themes/BUILD.gn +++ b/chrome/browser/themes/BUILD.gn
@@ -36,6 +36,7 @@ "//chrome/browser/search/background:constants", "//chrome/browser/ui/color:color_headers", "//chrome/browser/ui/frame", + "//chrome/browser/ui/tabs:tab_group", "//chrome/browser/ui/webui/cr_components/theme_color_picker", "//chrome/common", "//components/crx_file",
diff --git a/chrome/browser/themes/browser_theme_pack.cc b/chrome/browser/themes/browser_theme_pack.cc index f05258c..2c1482c 100644 --- a/chrome/browser/themes/browser_theme_pack.cc +++ b/chrome/browser/themes/browser_theme_pack.cc
@@ -40,6 +40,7 @@ #include "chrome/browser/themes/theme_properties.h" #include "chrome/browser/ui/color/chrome_color_id.h" #include "chrome/browser/ui/frame/window_frame_util.h" +#include "chrome/browser/ui/tabs/tab_group_theme.h" #include "chrome/common/extensions/manifest_handlers/theme_handler.h" #include "chrome/common/themes/autogenerated_theme_util.h" #include "chrome/grit/theme_resources.h" @@ -52,6 +53,7 @@ #include "ui/color/color_provider.h" #include "ui/color/color_recipe.h" #include "ui/color/color_transform.h" +#include "ui/color/dynamic_color/palette_factory.h" #include "ui/gfx/canvas.h" #include "ui/gfx/codec/png_codec.h" #include "ui/gfx/color_analysis.h" @@ -123,6 +125,7 @@ const int kDisplayPropertiesID = kMaxID - 4; const int kSourceImagesID = kMaxID - 5; const int kScaleFactorsID = kMaxID - 6; +const int kTabGroupColorPaletteShadesID = kMaxID - 7; struct PersistingImagesTable { // A non-changing integer ID meant to be saved in theme packs. This ID must @@ -210,13 +213,17 @@ base::as_byte_span(base::allow_nonunique_obj, scales))); } -struct StringToIntTable { +// Stores the string |key| which is used by themes and its corresponding +// enumeration |id|. `T` currently represents +// `TP::OverwritableByUserThemeProperty` and `tab_groups::TabGroupColorId` +template <typename T> +struct StringToIdTable { const char* const key; - TP::OverwritableByUserThemeProperty id; + T id; }; // Strings used by themes to identify tints in the JSON. -const StringToIntTable kTintTable[] = { +const StringToIdTable<TP::OverwritableByUserThemeProperty> kTintTable[] = { {"background_tab", TP::TINT_BACKGROUND_TAB}, {"buttons", TP::TINT_BUTTONS}, {"frame", TP::TINT_FRAME}, @@ -230,40 +237,41 @@ const size_t kTintTableLength = std::size(kTintTable); // Strings used by themes to identify colors in the JSON. -constexpr StringToIntTable kOverwritableColorTable[] = { - {"background_tab", TP::COLOR_TAB_BACKGROUND_INACTIVE_FRAME_ACTIVE}, - {"background_tab_inactive", - TP::COLOR_TAB_BACKGROUND_INACTIVE_FRAME_INACTIVE}, - {"background_tab_incognito", - TP::COLOR_TAB_BACKGROUND_INACTIVE_FRAME_ACTIVE_INCOGNITO}, - {"background_tab_incognito_inactive", - TP::COLOR_TAB_BACKGROUND_INACTIVE_FRAME_INACTIVE_INCOGNITO}, - {"bookmark_text", TP::COLOR_BOOKMARK_TEXT}, - {"button_background", TP::COLOR_CONTROL_BUTTON_BACKGROUND}, - {"frame", TP::COLOR_FRAME_ACTIVE}, - {"frame_inactive", TP::COLOR_FRAME_INACTIVE}, - {"frame_incognito", TP::COLOR_FRAME_ACTIVE_INCOGNITO}, - {"frame_incognito_inactive", TP::COLOR_FRAME_INACTIVE_INCOGNITO}, - {"ntp_background", TP::COLOR_NTP_BACKGROUND}, - {"ntp_header", TP::COLOR_NTP_HEADER}, - {"ntp_link", TP::COLOR_NTP_LINK}, - {"ntp_text", TP::COLOR_NTP_TEXT}, - {"omnibox_background", TP::COLOR_OMNIBOX_BACKGROUND}, - {"omnibox_text", TP::COLOR_OMNIBOX_TEXT}, - {"tab_background_text", TP::COLOR_TAB_FOREGROUND_INACTIVE_FRAME_ACTIVE}, - {"tab_background_text_inactive", - TP::COLOR_TAB_FOREGROUND_INACTIVE_FRAME_INACTIVE}, - {"tab_background_text_incognito", - TP::COLOR_TAB_FOREGROUND_INACTIVE_FRAME_ACTIVE_INCOGNITO}, - {"tab_background_text_incognito_inactive", - TP::COLOR_TAB_FOREGROUND_INACTIVE_FRAME_INACTIVE_INCOGNITO}, - {"tab_text", TP::COLOR_TAB_FOREGROUND_ACTIVE_FRAME_ACTIVE}, - {"toolbar", TP::COLOR_TOOLBAR}, - {"toolbar_button_icon", TP::COLOR_TOOLBAR_BUTTON_ICON}, - {"toolbar_text", TP::COLOR_TOOLBAR_TEXT}, +constexpr StringToIdTable<TP::OverwritableByUserThemeProperty> + kOverwritableColorTable[] = { + {"background_tab", TP::COLOR_TAB_BACKGROUND_INACTIVE_FRAME_ACTIVE}, + {"background_tab_inactive", + TP::COLOR_TAB_BACKGROUND_INACTIVE_FRAME_INACTIVE}, + {"background_tab_incognito", + TP::COLOR_TAB_BACKGROUND_INACTIVE_FRAME_ACTIVE_INCOGNITO}, + {"background_tab_incognito_inactive", + TP::COLOR_TAB_BACKGROUND_INACTIVE_FRAME_INACTIVE_INCOGNITO}, + {"bookmark_text", TP::COLOR_BOOKMARK_TEXT}, + {"button_background", TP::COLOR_CONTROL_BUTTON_BACKGROUND}, + {"frame", TP::COLOR_FRAME_ACTIVE}, + {"frame_inactive", TP::COLOR_FRAME_INACTIVE}, + {"frame_incognito", TP::COLOR_FRAME_ACTIVE_INCOGNITO}, + {"frame_incognito_inactive", TP::COLOR_FRAME_INACTIVE_INCOGNITO}, + {"ntp_background", TP::COLOR_NTP_BACKGROUND}, + {"ntp_header", TP::COLOR_NTP_HEADER}, + {"ntp_link", TP::COLOR_NTP_LINK}, + {"ntp_text", TP::COLOR_NTP_TEXT}, + {"omnibox_background", TP::COLOR_OMNIBOX_BACKGROUND}, + {"omnibox_text", TP::COLOR_OMNIBOX_TEXT}, + {"tab_background_text", TP::COLOR_TAB_FOREGROUND_INACTIVE_FRAME_ACTIVE}, + {"tab_background_text_inactive", + TP::COLOR_TAB_FOREGROUND_INACTIVE_FRAME_INACTIVE}, + {"tab_background_text_incognito", + TP::COLOR_TAB_FOREGROUND_INACTIVE_FRAME_ACTIVE_INCOGNITO}, + {"tab_background_text_incognito_inactive", + TP::COLOR_TAB_FOREGROUND_INACTIVE_FRAME_INACTIVE_INCOGNITO}, + {"tab_text", TP::COLOR_TAB_FOREGROUND_ACTIVE_FRAME_ACTIVE}, + {"toolbar", TP::COLOR_TOOLBAR}, + {"toolbar_button_icon", TP::COLOR_TOOLBAR_BUTTON_ICON}, + {"toolbar_text", TP::COLOR_TOOLBAR_TEXT}, - // /!\ If you make any changes here, you must also increment - // kThemePackVersion above, or else themes will display incorrectly. + // /!\ If you make any changes here, you must also increment + // kThemePackVersion above, or else themes will display incorrectly. }; constexpr size_t kOverwritableColorTableLength = std::size(kOverwritableColorTable); @@ -294,26 +302,41 @@ kOverwritableColorTableLength + kNonOverwritableColorTableLength; // Strings used by themes to identify display properties keys in JSON. -const StringToIntTable kDisplayProperties[] = { - {"ntp_background_alignment", TP::NTP_BACKGROUND_ALIGNMENT}, - {"ntp_background_repeat", TP::NTP_BACKGROUND_TILING}, - {"ntp_logo_alternate", TP::NTP_LOGO_ALTERNATE}, +const StringToIdTable<TP::OverwritableByUserThemeProperty> + kDisplayProperties[] = { + {"ntp_background_alignment", TP::NTP_BACKGROUND_ALIGNMENT}, + {"ntp_background_repeat", TP::NTP_BACKGROUND_TILING}, + {"ntp_logo_alternate", TP::NTP_LOGO_ALTERNATE}, - // /!\ If you make any changes here, you must also increment - // kThemePackVersion above, or else themes will display incorrectly. + // /!\ If you make any changes here, you must also increment + // kThemePackVersion above, or else themes will display incorrectly. }; const size_t kDisplayPropertiesSize = std::size(kDisplayProperties); -int GetIntForString(const std::string& key, - base::span<const StringToIntTable> table, - size_t spanification_suspected_redundant_table_length) { +const StringToIdTable<tab_groups::TabGroupColorId> kTabGroupColorIdTable[] = { + {"grey_override", tab_groups::TabGroupColorId::kGrey}, + {"blue_override", tab_groups::TabGroupColorId::kBlue}, + {"red_override", tab_groups::TabGroupColorId::kRed}, + {"yellow_override", tab_groups::TabGroupColorId::kYellow}, + {"green_override", tab_groups::TabGroupColorId::kGreen}, + {"pink_override", tab_groups::TabGroupColorId::kPink}, + {"purple_override", tab_groups::TabGroupColorId::kPurple}, + {"cyan_override", tab_groups::TabGroupColorId::kCyan}, + {"orange_override", tab_groups::TabGroupColorId::kOrange}, +}; +const size_t kTabGroupColorIdTableSize = std::size(kTabGroupColorIdTable); + +template <typename T> +int GetIdForString(const std::string& key, + base::span<const StringToIdTable<T>> table, + size_t spanification_suspected_redundant_table_length) { // TODO(crbug.com/431824301): Remove unneeded parameter once validated to be // redundant in M143. CHECK(spanification_suspected_redundant_table_length == table.size(), base::NotFatalUntil::M143); for (size_t i = 0; i < spanification_suspected_redundant_table_length; ++i) { if (base::EqualsCaseInsensitiveASCII(key, table[i].key)) { - return table[i].id; + return static_cast<int>(table[i].id); } } @@ -734,6 +757,8 @@ pack->SetColorsFromJSON(extensions::ThemeInfo::GetColors(extension)); pack->SetDisplayPropertiesFromJSON( extensions::ThemeInfo::GetDisplayProperties(extension)); + pack->SetTabGroupColorPaletteShadesFromJSON( + extensions::ThemeInfo::GetTabGroupColorPalette(extension)); // Builds the images. (Image building is dependent on tints). FilePathMap file_paths; @@ -821,6 +846,13 @@ pack->display_properties_ = reinterpret_cast<DisplayPropertyPair*>( const_cast<char*>(pointer->data())); + pointer = data_pack->GetStringView(kTabGroupColorPaletteShadesID); + if (!pointer) { + return nullptr; + } + std::memcpy(pack->tab_group_color_palette_shades_.data(), pointer->data(), + sizeof(pack->tab_group_color_palette_shades_)); + pointer = data_pack->GetStringView(kSourceImagesID); if (!pointer) { return nullptr; @@ -948,6 +980,9 @@ resources[kDisplayPropertiesID] = std::string_view(reinterpret_cast<const char*>(display_properties_.get()), sizeof(DisplayPropertyPair[kDisplayPropertiesSize])); + resources[kTabGroupColorPaletteShadesID] = std::string_view( + reinterpret_cast<const char*>(tab_group_color_palette_shades_.data()), + sizeof(tab_group_color_palette_shades_)); int source_count = 1; SourceImage* end = source_images_; @@ -1176,6 +1211,67 @@ mixer[kColorNewTabButtonBackgroundFrameActive] = {SK_ColorTRANSPARENT}; mixer[kColorNewTabButtonBackgroundFrameInactive] = {SK_ColorTRANSPARENT}; } + + for (const auto& [id, shades] : tab_group_color_palette_shades_) { + // The array |tab_group_color_palette_shades_| is populated from left to + // right, and unused entries are marked with id == -1. Stop iteration once + // an unused entry is encountered. + if (id == -1) { + break; + } + + tab_groups::TabGroupColorId tab_group_color_id = + static_cast<tab_groups::TabGroupColorId>(id); + + enum ShadeIndex { + k50, + k100, + k200, + k300, + k400, + k500, + k600, + k700, + k800, + k900, + k1000 + }; + + mixer[GetTabGroupTabStripColorId(tab_group_color_id, true)] = + ui::SelectBasedOnDarkInput(kColorTabBackgroundInactiveFrameActive, + shades[k300], shades[k600]); + + mixer[GetTabGroupTabStripColorId(tab_group_color_id, false)] = + ui::SelectBasedOnDarkInput(kColorTabBackgroundInactiveFrameInactive, + shades[k300], shades[k600]); + + mixer[GetTabGroupDialogColorId(tab_group_color_id)] = { + GetTabGroupContextMenuColorId(tab_group_color_id)}; + + mixer[GetTabGroupContextMenuColorId(tab_group_color_id)] = + ui::SelectBasedOnDarkInput(ui::kColorMenuBackground, shades[k300], + shades[k600]); + + mixer[GetSavedTabGroupForegroundColorId(tab_group_color_id)] = + ui::SelectBasedOnDarkInput(kColorBookmarkBarBackground, shades[k100], + shades[k800]); + + mixer[GetSavedTabGroupOutlineColorId(tab_group_color_id)] = + ui::SelectBasedOnDarkInput(kColorBookmarkBarBackground, shades[k300], + shades[k700]); + + mixer[GetTabGroupBookmarkColorId(tab_group_color_id)] = + ui::SelectBasedOnDarkInput(kColorBookmarkBarBackground, shades[k1000], + shades[k50]); + + mixer[GetThumbnailTabStripTabGroupColorId(tab_group_color_id, true)] = + ui::SelectBasedOnDarkInput(kColorThumbnailTabStripBackgroundActive, + shades[k300], shades[k600]); + + mixer[GetThumbnailTabStripTabGroupColorId(tab_group_color_id, false)] = + ui::SelectBasedOnDarkInput(kColorThumbnailTabStripBackgroundInactive, + shades[k300], shades[k600]); + } } // private: @@ -1353,7 +1449,8 @@ color_utils::HSL hsl = {*h, *s, *l}; MakeHSLShiftValid(&hsl); - int id = GetIntForString(key, kTintTable, kTintTableLength); + int id = GetIdForString<TP::OverwritableByUserThemeProperty>( + key, kTintTable, kTintTableLength); if (id != -1) { temp_tints[id] = hsl; } @@ -1443,8 +1540,8 @@ (*temp_colors)[TP::COLOR_NTP_HEADER] = color; } } else { - int id = GetIntForString(key, kOverwritableColorTable, - kOverwritableColorTableLength); + int id = GetIdForString<TP::OverwritableByUserThemeProperty>( + key, kOverwritableColorTable, kOverwritableColorTableLength); if (id != -1) { (*temp_colors)[id] = color; } @@ -1462,8 +1559,8 @@ std::map<int, int> temp_properties; for (const auto [key, value] : *display_properties_value) { - int property_id = - GetIntForString(key, kDisplayProperties, kDisplayPropertiesSize); + int property_id = GetIdForString<TP::OverwritableByUserThemeProperty>( + key, kDisplayProperties, kDisplayPropertiesSize); switch (property_id) { case TP::NTP_BACKGROUND_ALIGNMENT: { if (value.is_string()) { @@ -1525,6 +1622,31 @@ } } +void BrowserThemePack::SetTabGroupColorPaletteShadesFromJSON( + const base::Value::Dict* tab_group_color_palette_value) { + size_t count = 0; + for (const auto [key, value] : *tab_group_color_palette_value) { + if (!value.is_int()) { + continue; + } + + int hue = value.GetInt(); + if (hue < -1 || hue > 360) { + continue; + } + + int id = GetIdForString<tab_groups::TabGroupColorId>( + key, kTabGroupColorIdTable, kTabGroupColorIdTableSize); + if (id != -1) { + tab_group_color_palette_shades_[count].id = id; + ui::GenerateStandardShadesFromHue( + hue == -1 ? std::nullopt : std::make_optional(hue), + tab_group_color_palette_shades_[count].shades); + ++count; + } + } +} + void BrowserThemePack::AddFileAtScaleToMap(const std::string& image_name, ui::ResourceScaleFactor scale_factor, const base::FilePath& image_path,
diff --git a/chrome/browser/themes/browser_theme_pack.h b/chrome/browser/themes/browser_theme_pack.h index b709963..24e3ff1 100644 --- a/chrome/browser/themes/browser_theme_pack.h +++ b/chrome/browser/themes/browser_theme_pack.h
@@ -217,6 +217,12 @@ const base::FilePath& images_path, FilePathMap* file_paths) const; + // Transforms the JSON |tab_group_color_palette_value| into their final + // versions in the |tab_group_color_palette_shades_| array. Does nothing if + // |tab_group_color_palette_value| is nullptr. + void SetTabGroupColorPaletteShadesFromJSON( + const base::Value::Dict* tab_group_color_palette_value); + // Helper function to populate the FilePathMap. void AddFileAtScaleToMap(const std::string& image_name, ui::ResourceScaleFactor scale_factor,
diff --git a/chrome/browser/themes/browser_theme_pack_unittest.cc b/chrome/browser/themes/browser_theme_pack_unittest.cc index 557549d..6156b69 100644 --- a/chrome/browser/themes/browser_theme_pack_unittest.cc +++ b/chrome/browser/themes/browser_theme_pack_unittest.cc
@@ -11,6 +11,8 @@ #include <stddef.h> +#include <algorithm> +#include <array> #include <memory> #include <string_view> @@ -28,6 +30,7 @@ #include "chrome/common/chrome_paths.h" #include "chrome/common/themes/autogenerated_theme_util.h" #include "chrome/grit/theme_resources.h" +#include "components/tab_groups/tab_group_color.h" #include "content/public/test/browser_task_environment.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/color/color_mixer.h" @@ -35,6 +38,7 @@ #include "ui/color/color_provider_key.h" #include "ui/color/color_recipe.h" #include "ui/color/color_test_ids.h" +#include "ui/color/dynamic_color/palette_factory.h" #include "ui/gfx/color_utils.h" #include "ui/gfx/image/image.h" #include "ui/gfx/image/image_skia.h" @@ -78,6 +82,10 @@ TestFilePathMap* out_file_paths); void ParseImageNamesDictionary(const base::Value::Dict* value, TestFilePathMap* out_file_paths); + void ResetTabGroupColorPaletteShades(); + void LoadTabGroupColorPaletteShadesJSON(const std::string& json); + void LoadTabGroupColorPaletteShadesDictionary(const base::Value::Dict* value); + void VerifyTabGroupColorPaletteShades(); bool LoadRawBitmapsTo(const TestFilePathMap& out_file_paths); // This function returns void in order to be able use ASSERT_... @@ -230,6 +238,67 @@ theme_pack_->BuildSourceImagesArray(*out_file_paths); } +void BrowserThemePackTest::ResetTabGroupColorPaletteShades() { + theme_pack_->tab_group_color_palette_shades_ = + std::array<BrowserThemePack::TabGroupColorPaletteShadesPair, + BrowserThemePack::kTabGroupColorPaletteLength>{}; +} + +void BrowserThemePackTest::LoadTabGroupColorPaletteShadesJSON( + const std::string& json) { + LoadTabGroupColorPaletteShadesDictionary( + &base::JSONReader::Read(json)->GetDict()); +} + +void BrowserThemePackTest::LoadTabGroupColorPaletteShadesDictionary( + const base::Value::Dict* value) { + theme_pack_->SetTabGroupColorPaletteShadesFromJSON(value); +} + +void BrowserThemePackTest::VerifyTabGroupColorPaletteShades() { + const std::array<BrowserThemePack::TabGroupColorPaletteShadesPair, + BrowserThemePack::kTabGroupColorPaletteLength>& + tab_group_color_palette_shades = + theme_pack_->tab_group_color_palette_shades_; + + std::array<BrowserThemePack::TabGroupColorPaletteShadesPair, + BrowserThemePack::kTabGroupColorPaletteLength> + expected_tab_group_color_palette_shades{}; + + expected_tab_group_color_palette_shades[0].id = + static_cast<int>(tab_groups::TabGroupColorId::kRed); + + // Expected standard shades for hue = 40 + constexpr std::array<SkColor, ui::kGeneratedShadesCount> kExpectedShades = { + SkColorSetRGB(0xFF, 0xED, 0xE7), // Shade 50 + SkColorSetRGB(0xFF, 0xDC, 0xD0), // Shade 100 + SkColorSetRGB(0xFF, 0xC1, 0xAA), // Shade 200 + SkColorSetRGB(0xFF, 0xA6, 0x83), // Shade 300 + SkColorSetRGB(0xFF, 0x87, 0x56), // Shade 400 + SkColorSetRGB(0xFA, 0x6E, 0x2F), // Shade 500 + SkColorSetRGB(0xE4, 0x5F, 0x20), // Shade 600 + SkColorSetRGB(0xCB, 0x52, 0x19), // Shade 700 + SkColorSetRGB(0xB3, 0x46, 0x11), // Shade 800 + SkColorSetRGB(0x9F, 0x3D, 0x0E), // Shade 900 + SkColorSetRGB(0x5A, 0x3D, 0x32), // Shade 1000 + }; + + std::copy(kExpectedShades.begin(), kExpectedShades.end(), + expected_tab_group_color_palette_shades[0].shades.begin()); + + for (size_t i = 0; i < BrowserThemePack::kTabGroupColorPaletteLength; ++i) { + const auto& actual = tab_group_color_palette_shades[i]; + const auto& expected = expected_tab_group_color_palette_shades[i]; + + EXPECT_EQ(actual.id, expected.id) << "Mismatch at index " << i << " for ID"; + + for (size_t j = 0; j < ui::kGeneratedShadesCount; ++j) { + EXPECT_EQ(actual.shades[j], expected.shades[j]) + << "Mismatch at index " << i << " shade " << j; + } + } +} + bool BrowserThemePackTest::LoadRawBitmapsTo( const TestFilePathMap& out_file_paths) { return theme_pack_->LoadRawBitmapsTo(out_file_paths, &theme_pack_->images_); @@ -1237,3 +1306,141 @@ GetTestExtensionThemePath("theme_jpeg_theme_frame"), theme.get()); EXPECT_FALSE(theme->is_valid()); } + +TEST_F(BrowserThemePackTest, SetTabGroupColorPaletteShadesFromJSONTest) { + ResetTabGroupColorPaletteShades(); + + std::string tab_group_color_palette_json = + // invalid key with valid value, will be ignored. + "{ \"invalid_key\": 23, " + // valid key with invalid value, will be ignored. + " \"grey_override\": 361, " + // valid key with valid value, will be translated. + " \"red_override\": 40 } "; + LoadTabGroupColorPaletteShadesJSON(tab_group_color_palette_json); + + VerifyTabGroupColorPaletteShades(); + + ResetTabGroupColorPaletteShades(); +} + +TEST_F(BrowserThemePackTest, TabGroupColorPaletteCustomizationTest) { + // Tests whether the Tab Group-specific colorIds are set correctly based on + // the background color and theme provided. + ui::ColorProvider provider; + ui::ColorMixer& mixer = provider.AddMixer(); + + // Randomly set the background colors that influence Tab Group-specific + // ColorIds. The selected shade depends on whether the background color is + // considered dark. + mixer[kColorTabBackgroundInactiveFrameActive] = {SkColorSetRGB(20, 20, 20)}; + mixer[kColorTabBackgroundInactiveFrameInactive] = {SkColorSetRGB(34, 34, 34)}; + mixer[ui::kColorMenuBackground] = {SkColorSetRGB(15, 23, 30)}; + mixer[kColorBookmarkBarBackground] = {SkColorSetRGB(255, 250, 200)}; + mixer[kColorThumbnailTabStripBackgroundActive] = { + SkColorSetRGB(220, 230, 240)}; + mixer[kColorThumbnailTabStripBackgroundInactive] = { + SkColorSetRGB(245, 245, 245)}; + + // Customizing the red Tab Group color using hue 40. + std::string tab_group_color_palette_json = R"({ "red_override": 40 })"; + LoadTabGroupColorPaletteShadesJSON(tab_group_color_palette_json); + theme_pack().AddColorMixers(&provider, ui::ColorProviderKey()); + + // Standard shades of hue 40. + constexpr std::array<SkColor, ui::kGeneratedShadesCount> shades = { + SkColorSetRGB(0xFF, 0xED, 0xE7), // Shade 50 + SkColorSetRGB(0xFF, 0xDC, 0xD0), // Shade 100 + SkColorSetRGB(0xFF, 0xC1, 0xAA), // Shade 200 + SkColorSetRGB(0xFF, 0xA6, 0x83), // Shade 300 + SkColorSetRGB(0xFF, 0x87, 0x56), // Shade 400 + SkColorSetRGB(0xFA, 0x6E, 0x2F), // Shade 500 + SkColorSetRGB(0xE4, 0x5F, 0x20), // Shade 600 + SkColorSetRGB(0xCB, 0x52, 0x19), // Shade 700 + SkColorSetRGB(0xB3, 0x46, 0x11), // Shade 800 + SkColorSetRGB(0x9F, 0x3D, 0x0E), // Shade 900 + SkColorSetRGB(0x5A, 0x3D, 0x32), // Shade 1000 + }; + + enum ShadeIndex { + k50, + k100, + k200, + k300, + k400, + k500, + k600, + k700, + k800, + k900, + k1000 + }; + + SkColor expected; + SkColor actual; + + // kColorTabGroupTabStripFrameActiveRed + expected = color_utils::IsDark( + provider.GetColor(kColorTabBackgroundInactiveFrameActive)) + ? shades[k300] + : shades[k600]; + actual = provider.GetColor(kColorTabGroupTabStripFrameActiveRed); + EXPECT_EQ(expected, actual); + + // kColorTabGroupTabStripFrameInactiveRed + expected = color_utils::IsDark( + provider.GetColor(kColorTabBackgroundInactiveFrameInactive)) + ? shades[k300] + : shades[k600]; + actual = provider.GetColor(kColorTabGroupTabStripFrameInactiveRed); + EXPECT_EQ(expected, actual); + + // kColorTabGroupDialogRed + expected = provider.GetColor(kColorTabGroupContextMenuRed); + actual = provider.GetColor(kColorTabGroupDialogRed); + EXPECT_EQ(expected, actual); + + // kColorTabGroupContextMenuRed + expected = color_utils::IsDark(provider.GetColor(ui::kColorMenuBackground)) + ? shades[k300] + : shades[k600]; + actual = provider.GetColor(kColorTabGroupContextMenuRed); + EXPECT_EQ(expected, actual); + + // kColorSavedTabGroupForegroundRed + expected = color_utils::IsDark(provider.GetColor(kColorBookmarkBarBackground)) + ? shades[k100] + : shades[k800]; + actual = provider.GetColor(kColorSavedTabGroupForegroundRed); + EXPECT_EQ(expected, actual); + + // kColorSavedTabGroupOutlineRed + expected = color_utils::IsDark(provider.GetColor(kColorBookmarkBarBackground)) + ? shades[k300] + : shades[k700]; + actual = provider.GetColor(kColorSavedTabGroupOutlineRed); + EXPECT_EQ(expected, actual); + + // kColorTabGroupBookmarkBarRed + expected = color_utils::IsDark(provider.GetColor(kColorBookmarkBarBackground)) + ? shades[k1000] + : shades[k50]; + actual = provider.GetColor(kColorTabGroupBookmarkBarRed); + EXPECT_EQ(expected, actual); + + // kColorThumbnailTabStripTabGroupFrameActiveRed + expected = color_utils::IsDark( + provider.GetColor(kColorThumbnailTabStripBackgroundActive)) + ? shades[k300] + : shades[k600]; + actual = provider.GetColor(kColorThumbnailTabStripTabGroupFrameActiveRed); + EXPECT_EQ(expected, actual); + + // kColorThumbnailTabStripTabGroupFrameInactiveRed + expected = color_utils::IsDark( + provider.GetColor(kColorThumbnailTabStripBackgroundInactive)) + ? shades[k300] + : shades[k600]; + actual = provider.GetColor(kColorThumbnailTabStripTabGroupFrameInactiveRed); + EXPECT_EQ(expected, actual); +}
diff --git a/chrome/browser/trusted_vault/trusted_vault_encryption_keys_tab_helper_browsertest.cc b/chrome/browser/trusted_vault/trusted_vault_encryption_keys_tab_helper_browsertest.cc index 1cae61c..3497488 100644 --- a/chrome/browser/trusted_vault/trusted_vault_encryption_keys_tab_helper_browsertest.cc +++ b/chrome/browser/trusted_vault/trusted_vault_encryption_keys_tab_helper_browsertest.cc
@@ -13,6 +13,7 @@ #include "base/test/bind.h" #include "base/test/metrics/histogram_tester.h" #include "build/build_config.h" +#include "chrome/browser/preloading/scoped_prewarm_feature_list.h" #include "chrome/browser/trusted_vault/trusted_vault_service_factory.h" #include "chrome/common/chrome_paths.h" #include "chrome/test/base/chrome_test_utils.h" @@ -334,6 +335,11 @@ } private: + // TODO(https://crbug.com/423465927): Explore a better approach to make the + // existing tests run with the prewarm feature enabled. + test::ScopedPrewarmFeatureList scoped_prewarm_feature_list_{ + test::ScopedPrewarmFeatureList::PrewarmState::kDisabled}; + base::test::ScopedFeatureList feature_list_; net::EmbeddedTestServer https_server_; content::test::FencedFrameTestHelper fenced_frame_test_helper_;
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index 3531b4e..766fdd2 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn
@@ -338,6 +338,7 @@ "//chrome/browser/search_engine_choice:impl", "//chrome/browser/search_engines", "//chrome/browser/segmentation_platform", + "//chrome/browser/sessions", "//chrome/browser/share", "//chrome/browser/signin", "//chrome/browser/signin:identity_manager_provider", @@ -3938,7 +3939,6 @@ "views/frame/system_menu_model_delegate.h", "views/frame/tab_strip_region_view.cc", "views/frame/tab_strip_region_view.h", - "views/frame/toolbar_button_provider.h", "views/frame/top_container_background.cc", "views/frame/top_container_background.h", "views/frame/top_container_loading_bar.cc", @@ -4818,6 +4818,7 @@ "//chrome/browser/ui/views/bubble", "//chrome/browser/ui/views/download", "//chrome/browser/ui/views/frame:immersive_mode_controller", + "//chrome/browser/ui/views/frame:toolbar_button_provider", "//chrome/browser/ui/views/infobars", "//chrome/browser/ui/views/intent_picker:intent_picker_page_action", "//chrome/browser/ui/views/page_action",
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionHorizontalDividerTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionHorizontalDividerTest.java index d2e6879d..67cace4 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionHorizontalDividerTest.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/SuggestionHorizontalDividerTest.java
@@ -59,7 +59,7 @@ @Before public void setUp() { mActivity = Robolectric.buildActivity(Activity.class).setup().get(); - mActivity.setTheme(R.style.Theme_BrowserUI); + mActivity.setTheme(R.style.Theme_BrowserUI_DayNight); mDecoration = new SuggestionHorizontalDivider(mActivity); mShowDividerViewHolder.model = mShowDividerModel; mNoDividerViewHolder.model = mNoDividerModel;
diff --git a/chrome/browser/ui/android/tab_model/BUILD.gn b/chrome/browser/ui/android/tab_model/BUILD.gn index ed3ea42..f34dc24 100644 --- a/chrome/browser/ui/android/tab_model/BUILD.gn +++ b/chrome/browser/ui/android/tab_model/BUILD.gn
@@ -51,6 +51,7 @@ "//chrome/android:chrome_jni_headers", "//chrome/browser:browser_process", "//chrome/browser/profiles:profile", + "//chrome/browser/sessions", "//chrome/browser/sync", "//chrome/browser/ui:browser_navigator_params_headers", "//content/public/browser",
diff --git a/chrome/browser/ui/ash/shell_delegate/BUILD.gn b/chrome/browser/ui/ash/shell_delegate/BUILD.gn index beac2eb..2055c955 100644 --- a/chrome/browser/ui/ash/shell_delegate/BUILD.gn +++ b/chrome/browser/ui/ash/shell_delegate/BUILD.gn
@@ -34,6 +34,7 @@ "//chrome/browser/ash/scanner", "//chrome/browser/feedback", "//chrome/browser/profiles:profile", + "//chrome/browser/sessions", "//chrome/browser/ui:browser_list", "//chrome/browser/ui/ash/accelerator", "//chrome/browser/ui/ash/accessibility",
diff --git a/chrome/browser/ui/autofill/payments/desktop_payments_window_manager_interactive_uitest.cc b/chrome/browser/ui/autofill/payments/desktop_payments_window_manager_interactive_uitest.cc index cc848cd8..778042a5 100644 --- a/chrome/browser/ui/autofill/payments/desktop_payments_window_manager_interactive_uitest.cc +++ b/chrome/browser/ui/autofill/payments/desktop_payments_window_manager_interactive_uitest.cc
@@ -223,8 +223,9 @@ // Tests that an error dialog is shown if there is no metadata returned from the // server. +// TODO(crbug.com/435092593): Re-enable once flakiness is fixed. IN_PROC_BROWSER_TEST_F(DesktopPaymentsWindowManagerInteractiveUiTest, - InvokeUi_EmptyMetadata_ErrorDialogShown) { + DISABLED_InvokeUi_EmptyMetadata_ErrorDialogShown) { PaymentsWindowManager::Vcn3dsContext context; context.card = test::GetVirtualCard(); context.context_token = kTestContextToken; @@ -237,8 +238,9 @@ // Tests that an error dialog is shown if there is no URL to open returned from // the server. +// TODO(crbug.com/435092593): Re-enable once flakiness is fixed. IN_PROC_BROWSER_TEST_F(DesktopPaymentsWindowManagerInteractiveUiTest, - InvokeUi_EmptyUrlToOpen_ErrorDialogShown) { + DISABLED_InvokeUi_EmptyUrlToOpen_ErrorDialogShown) { PaymentsWindowManager::Vcn3dsContext context; context.card = test::GetVirtualCard(); context.context_token = kTestContextToken; @@ -255,8 +257,10 @@ // Tests that an error dialog is shown if there is no success query param name // returned from the server. -IN_PROC_BROWSER_TEST_F(DesktopPaymentsWindowManagerInteractiveUiTest, - InvokeUi_EmptySuccessQueryParamName_ErrorDialogShown) { +// TODO(crbug.com/435092593): Re-enable once flakiness is fixed. +IN_PROC_BROWSER_TEST_F( + DesktopPaymentsWindowManagerInteractiveUiTest, + DISABLED_InvokeUi_EmptySuccessQueryParamName_ErrorDialogShown) { PaymentsWindowManager::Vcn3dsContext context; context.card = test::GetVirtualCard(); context.context_token = kTestContextToken; @@ -273,8 +277,10 @@ // Tests that an error dialog is shown if there is no failure query param name // returned from the server. -IN_PROC_BROWSER_TEST_F(DesktopPaymentsWindowManagerInteractiveUiTest, - InvokeUi_EmptyFailureQueryParamName_ErrorDialogShown) { +// TODO(crbug.com/435092593): Re-enable once flakiness is fixed. +IN_PROC_BROWSER_TEST_F( + DesktopPaymentsWindowManagerInteractiveUiTest, + DISABLED_InvokeUi_EmptyFailureQueryParamName_ErrorDialogShown) { PaymentsWindowManager::Vcn3dsContext context; context.card = test::GetVirtualCard(); context.context_token = kTestContextToken; @@ -291,8 +297,10 @@ // Test that the VCN 3DS flow started and consent dialog skipped histogram // buckets are logged to when the flow starts. -IN_PROC_BROWSER_TEST_F(DesktopPaymentsWindowManagerInteractiveUiTest, - InvokeUi_Vcn3ds_FlowStartedHistogramBucketLogs) { +// TODO(crbug.com/435092593): Re-enable once flakiness is fixed. +IN_PROC_BROWSER_TEST_F( + DesktopPaymentsWindowManagerInteractiveUiTest, + DISABLED_InvokeUi_Vcn3ds_FlowStartedHistogramBucketLogs) { ShowUi("Vcn3ds_ConsentAlreadyGiven"); EXPECT_TRUE(VerifyUi()); @@ -313,8 +321,9 @@ // Test that the VCN 3DS pop-up is shown correctly, and on close an // UnmaskCardRequest is triggered with the proper fields set if the right query // params are present. +// TODO(crbug.com/435092593): Re-enable once flakiness is fixed. IN_PROC_BROWSER_TEST_F(DesktopPaymentsWindowManagerInteractiveUiTest, - InvokeUi_Vcn3ds_QueryParamsPresent) { + DISABLED_InvokeUi_Vcn3ds_QueryParamsPresent) { ShowUi("Vcn3ds_ConsentAlreadyGiven"); EXPECT_TRUE(VerifyUi()); @@ -388,9 +397,10 @@ // Tests that the VCN 3DS flow succeeded histogram bucket is logged to when a // successful flow is completed for VCN 3DS. +// TODO(crbug.com/435092593): Re-enable once flakiness is fixed. IN_PROC_BROWSER_TEST_F( DesktopPaymentsWindowManagerInteractiveUiTest, - InvokeUi_Vcn3ds_QueryParamsPresent_SuccessHistogramBucketLogs) { + DISABLED_InvokeUi_Vcn3ds_QueryParamsPresent_SuccessHistogramBucketLogs) { ShowUi("Vcn3ds_ConsentAlreadyGiven"); EXPECT_TRUE(VerifyUi()); @@ -427,9 +437,10 @@ // Tests that the VCN 3DS flow succeeded latency histogram bucket is logged to // when a successful flow is completed for VCN 3DS. +// TODO(crbug.com/435092593): Re-enable once flakiness is fixed. IN_PROC_BROWSER_TEST_F( DesktopPaymentsWindowManagerInteractiveUiTest, - InvokeUi_Vcn3ds_QueryParamsPresent_SuccessLatencyHistogramBucketLogs) { + DISABLED_InvokeUi_Vcn3ds_QueryParamsPresent_SuccessLatencyHistogramBucketLogs) { ShowUi("Vcn3ds_ConsentAlreadyGiven"); EXPECT_TRUE(VerifyUi()); @@ -450,9 +461,10 @@ // Tests that the VCN 3DS flow failure latency histogram bucket is logged to // when a failed flow is completed for VCN 3DS. +// TODO(crbug.com/435092593): Re-enable once flakiness is fixed. IN_PROC_BROWSER_TEST_F( DesktopPaymentsWindowManagerInteractiveUiTest, - InvokeUi_Vcn3ds_QueryParamsPresent_FailureLatencyHistogramBucketLogs) { + DISABLED_InvokeUi_Vcn3ds_QueryParamsPresent_FailureLatencyHistogramBucketLogs) { ShowUi("Vcn3ds_ConsentAlreadyGiven"); EXPECT_TRUE(VerifyUi()); @@ -476,8 +488,9 @@ // UnmaskCardRequest is triggered with the proper fields set if the right query // params are present. Then mock an UnmaskCardRequest failure, and check that // the requester was notified of this failure. +// TODO(crbug.com/435092593): Re-enable once flakiness is fixed. IN_PROC_BROWSER_TEST_F(DesktopPaymentsWindowManagerInteractiveUiTest, - InvokeUi_Vcn3ds_UnmaskCardRequestFailure) { + DISABLED_InvokeUi_Vcn3ds_UnmaskCardRequestFailure) { ShowUi("Vcn3ds_ConsentAlreadyGiven"); EXPECT_TRUE(VerifyUi()); @@ -527,9 +540,10 @@ // Tests that the VCN 3DS flow failed during second server call histogram bucket // is logged to when a flow fails in the second UnmaskCardRequest. +// TODO(crbug.com/435092593): Re-enable once flakiness is fixed. IN_PROC_BROWSER_TEST_F( DesktopPaymentsWindowManagerInteractiveUiTest, - InvokeUi_Vcn3ds_UnmaskCardRequestFailure_FailureHistogramBucketLogs) { + DISABLED_InvokeUi_Vcn3ds_UnmaskCardRequestFailure_FailureHistogramBucketLogs) { ShowUi("Vcn3ds_ConsentAlreadyGiven"); EXPECT_TRUE(VerifyUi()); @@ -562,8 +576,10 @@ // Test that the VCN 3DS pop-up is shown correctly, and on close an // UnmaskCardRequest is not triggered if the query params indicate the // authentication failed. -IN_PROC_BROWSER_TEST_F(DesktopPaymentsWindowManagerInteractiveUiTest, - InvokeUi_Vcn3ds_QueryParams_AuthenticationFailed) { +// TODO(crbug.com/435092593): Re-enable once flakiness is fixed. +IN_PROC_BROWSER_TEST_F( + DesktopPaymentsWindowManagerInteractiveUiTest, + DISABLED_InvokeUi_Vcn3ds_QueryParams_AuthenticationFailed) { ShowUi("Vcn3ds_ConsentAlreadyGiven"); EXPECT_TRUE(VerifyUi()); @@ -599,9 +615,10 @@ // Tests that the VCN 3DS authentication failed histogram bucket is logged to // when the authentication inside of the pop-up failed for VCN 3DS. +// TODO(crbug.com/435092593): Re-enable once flakiness is fixed. IN_PROC_BROWSER_TEST_F( DesktopPaymentsWindowManagerInteractiveUiTest, - InvokeUi_Vcn3ds_QueryParams_AuthenticationFailed_FailureHistogramBucketLogs) { + DISABLED_InvokeUi_Vcn3ds_QueryParams_AuthenticationFailed_FailureHistogramBucketLogs) { ShowUi("Vcn3ds_ConsentAlreadyGiven"); EXPECT_TRUE(VerifyUi()); @@ -627,8 +644,9 @@ // Test that the VCN 3DS pop-up is shown correctly, and on close an // UnmaskCardRequest is not triggered if there are no query params present. +// TODO(crbug.com/435092593): Re-enable once flakiness is fixed. IN_PROC_BROWSER_TEST_F(DesktopPaymentsWindowManagerInteractiveUiTest, - InvokeUi_Vcn3ds_NoQueryParamsAndPopupClosed) { + DISABLED_InvokeUi_Vcn3ds_NoQueryParamsAndPopupClosed) { ShowUi("Vcn3ds_ConsentAlreadyGiven"); EXPECT_TRUE(VerifyUi()); @@ -655,9 +673,10 @@ // Tests that the VCN 3DS flow cancelled histogram bucket is logged to when the // user closes the pop-up. +// TODO(crbug.com/435092593): Re-enable once flakiness is fixed. IN_PROC_BROWSER_TEST_F( DesktopPaymentsWindowManagerInteractiveUiTest, - InvokeUi_Vcn3ds_NoQueryParamsAndPopupClosed_CancelledHistogramBucketLogs) { + DISABLED_InvokeUi_Vcn3ds_NoQueryParamsAndPopupClosed_CancelledHistogramBucketLogs) { ShowUi("Vcn3ds_ConsentAlreadyGiven"); EXPECT_TRUE(VerifyUi()); @@ -673,8 +692,9 @@ // Test that the VCN 3DS pop-up is shown correctly, and on close an // UnmaskCardRequest is not triggered if the query params are invalid. +// TODO(crbug.com/435092593): Re-enable once flakiness is fixed. IN_PROC_BROWSER_TEST_F(DesktopPaymentsWindowManagerInteractiveUiTest, - InvokeUi_Vcn3ds_InvalidQueryParams) { + DISABLED_InvokeUi_Vcn3ds_InvalidQueryParams) { ShowUi("Vcn3ds_ConsentAlreadyGiven"); EXPECT_TRUE(VerifyUi()); @@ -711,8 +731,9 @@ // Test that the VCN 3DS pop-up is shown correctly, and when the user cancels // the progress dialog, the state of the PaymentsWindowManager in relation to // the ongoing UnmaskCardRequest is reset. +// TODO(crbug.com/435092593): Re-enable once flakiness is fixed. IN_PROC_BROWSER_TEST_F(DesktopPaymentsWindowManagerInteractiveUiTest, - InvokeUi_Vcn3ds_ProgressDialogCancelled) { + DISABLED_InvokeUi_Vcn3ds_ProgressDialogCancelled) { ShowUi("Vcn3ds_ConsentAlreadyGiven"); EXPECT_TRUE(VerifyUi()); @@ -747,9 +768,10 @@ // Tests that the VCN 3DS progress dialog cancelled histogram bucket is logged // to when the progress dialog is cancelled during the VCN 3DS flow. +// TODO(crbug.com/435092593): Re-enable once flakiness is fixed. IN_PROC_BROWSER_TEST_F( DesktopPaymentsWindowManagerInteractiveUiTest, - InvokeUi_Vcn3ds_ProgressDialogCancelled_ProgressDialogCancelledHistogramBucketLogs) { + DISABLED_InvokeUi_Vcn3ds_ProgressDialogCancelled_ProgressDialogCancelledHistogramBucketLogs) { ShowUi("Vcn3ds_ConsentAlreadyGiven"); EXPECT_TRUE(VerifyUi()); @@ -777,8 +799,9 @@ #if BUILDFLAG(IS_LINUX) // Tests that if a VCN 3DS flow is ongoing, and the original tab is set active, // the payments window manager popup's web contents are re-activated. +// TODO(crbug.com/435092593): Re-enable once flakiness is fixed. IN_PROC_BROWSER_TEST_F(DesktopPaymentsWindowManagerInteractiveUiTest, - InvokeUi_Vcn3ds_OriginalTabSetLastActive) { + DISABLED_InvokeUi_Vcn3ds_OriginalTabSetLastActive) { ShowUi("Vcn3ds_ConsentAlreadyGiven"); EXPECT_TRUE(VerifyUi()); @@ -797,8 +820,9 @@ // Test that the BNPL pop-up is shown correctly, and on close the completion // callback is triggered with a success result if the flow was successful. +// TODO(crbug.com/435092593): Re-enable once flakiness is fixed. IN_PROC_BROWSER_TEST_F(DesktopPaymentsWindowManagerInteractiveUiTest, - InvokeUi_Bnpl_Success) { + DISABLED_InvokeUi_Bnpl_Success) { ShowUi("Bnpl_Affirm"); EXPECT_TRUE(VerifyUi()); @@ -823,8 +847,9 @@ // Test that the PopupWindowShown histogram is logged if the BNPL pop-up is // shown. +// TODO(crbug.com/435092593): Re-enable once flakiness is fixed. IN_PROC_BROWSER_TEST_F(DesktopPaymentsWindowManagerInteractiveUiTest, - InvokeUi_Bnpl_PopupWindowShownLogged) { + DISABLED_InvokeUi_Bnpl_PopupWindowShownLogged) { ShowUi("Bnpl_Affirm"); histogram_tester_.ExpectUniqueSample( @@ -834,8 +859,9 @@ // Test that the BNPL PopupWindowResult histogram logs a success result if the // pop-up flow was successful. +// TODO(crbug.com/435092593): Re-enable once flakiness is fixed. IN_PROC_BROWSER_TEST_F(DesktopPaymentsWindowManagerInteractiveUiTest, - InvokeUi_Bnpl_Success_PopupWindowResultLogged) { + DISABLED_InvokeUi_Bnpl_Success_PopupWindowResultLogged) { ShowUi("Bnpl_Affirm"); // Navigate to the URL that denotes success inside of the BNPL pop-up. @@ -856,8 +882,10 @@ /*expected_bucket_count=*/1); } -IN_PROC_BROWSER_TEST_F(DesktopPaymentsWindowManagerInteractiveUiTest, - InvokeUi_Bnpl_Success_PopupWindowLatencyLogged) { +// TODO(crbug.com/435092593): Re-enable once flakiness is fixed. +IN_PROC_BROWSER_TEST_F( + DesktopPaymentsWindowManagerInteractiveUiTest, + DISABLED_InvokeUi_Bnpl_Success_PopupWindowLatencyLogged) { ShowUi("Bnpl_Affirm"); // Navigate to the URL that denotes success inside of the BNPL pop-up. @@ -878,8 +906,9 @@ // Test that the BNPL pop-up is shown correctly, and on close the completion // callback is triggered with a failure result if the flow was a failure. +// TODO(crbug.com/435092593): Re-enable once flakiness is fixed. IN_PROC_BROWSER_TEST_F(DesktopPaymentsWindowManagerInteractiveUiTest, - InvokeUi_Bnpl_Failure) { + DISABLED_InvokeUi_Bnpl_Failure) { ShowUi("Bnpl_Affirm"); EXPECT_TRUE(VerifyUi()); @@ -904,8 +933,9 @@ // Test that the BNPL PopupWindowResult histogram logs a failure result if the // pop-up flow was a failure. +// TODO(crbug.com/435092593): Re-enable once flakiness is fixed. IN_PROC_BROWSER_TEST_F(DesktopPaymentsWindowManagerInteractiveUiTest, - InvokeUi_Bnpl_Failure_PopupWindowResultLogged) { + DISABLED_InvokeUi_Bnpl_Failure_PopupWindowResultLogged) { ShowUi("Bnpl_Affirm"); // Navigate to the URL that denotes failure inside of the BNPL pop-up. @@ -926,8 +956,10 @@ /*expected_bucket_count=*/1); } -IN_PROC_BROWSER_TEST_F(DesktopPaymentsWindowManagerInteractiveUiTest, - InvokeUi_Bnpl_Failure_PopupWindowLatencyLogged) { +// TODO(crbug.com/435092593): Re-enable once flakiness is fixed. +IN_PROC_BROWSER_TEST_F( + DesktopPaymentsWindowManagerInteractiveUiTest, + DISABLED_InvokeUi_Bnpl_Failure_PopupWindowLatencyLogged) { ShowUi("Bnpl_Affirm"); // Navigate to the URL that denotes success inside of the BNPL pop-up. @@ -949,8 +981,9 @@ // Test that the BNPL pop-up is shown correctly, and on close the completion // callback is triggered with a "user closed" result if the flow was closed by // the user. +// TODO(crbug.com/435092593): Re-enable once flakiness is fixed. IN_PROC_BROWSER_TEST_F(DesktopPaymentsWindowManagerInteractiveUiTest, - InvokeUi_Bnpl_UserClosedPopup) { + DISABLED_InvokeUi_Bnpl_UserClosedPopup) { ShowUi("Bnpl_Affirm"); EXPECT_TRUE(VerifyUi()); @@ -965,8 +998,10 @@ // Test that the PopupWindowResult histogram logs a "user closed" result if the // pop-up flow was closed by the user. -IN_PROC_BROWSER_TEST_F(DesktopPaymentsWindowManagerInteractiveUiTest, - InvokeUi_Bnpl_UserClosedPopup_PopupWindowResultLogged) { +// TODO(crbug.com/435092593): Re-enable once flakiness is fixed. +IN_PROC_BROWSER_TEST_F( + DesktopPaymentsWindowManagerInteractiveUiTest, + DISABLED_InvokeUi_Bnpl_UserClosedPopup_PopupWindowResultLogged) { ShowUi("Bnpl_Affirm"); ClosePopupAndWait(); @@ -976,8 +1011,10 @@ /*expected_bucket_count=*/1); } -IN_PROC_BROWSER_TEST_F(DesktopPaymentsWindowManagerInteractiveUiTest, - InvokeUi_Bnpl_UserClosedPopup_PopupWindowLatencyLogged) { +// TODO(crbug.com/435092593): Re-enable once flakiness is fixed. +IN_PROC_BROWSER_TEST_F( + DesktopPaymentsWindowManagerInteractiveUiTest, + DISABLED_InvokeUi_Bnpl_UserClosedPopup_PopupWindowLatencyLogged) { ShowUi("Bnpl_Affirm"); ClosePopupAndWait(); @@ -989,8 +1026,9 @@ // callback is triggered with a "user closed" result if the flow was closed and // the URL contained the success URL prefix, but the success URL prefix was not // its prefix. +// TODO(crbug.com/435092593): Re-enable once flakiness is fixed. IN_PROC_BROWSER_TEST_F(DesktopPaymentsWindowManagerInteractiveUiTest, - InvokeUi_Bnpl_UrlIsNotAPrefix) { + DISABLED_InvokeUi_Bnpl_UrlIsNotAPrefix) { ShowUi("Bnpl_Affirm"); EXPECT_TRUE(VerifyUi());
diff --git a/chrome/browser/ui/browser_window/internal/android/browser_window_interface_iterator_android.cc b/chrome/browser/ui/browser_window/internal/android/browser_window_interface_iterator_android.cc index 16dc1a3..27f2301 100644 --- a/chrome/browser/ui/browser_window/internal/android/browser_window_interface_iterator_android.cc +++ b/chrome/browser/ui/browser_window/internal/android/browser_window_interface_iterator_android.cc
@@ -21,7 +21,7 @@ return {}; } -BrowserWindowInterface* GetLastActiveBrowserWindowInterface() { +BrowserWindowInterface* GetLastActiveBrowserWindowInterfaceWithAnyProfile() { // TODO(https://crbug.com/419057482): Implement this once we have an // Android variant of BrowserWindowInterface. NOTIMPLEMENTED();
diff --git a/chrome/browser/ui/browser_window/internal/browser_window_interface_iterator_non_android.cc b/chrome/browser/ui/browser_window/internal/browser_window_interface_iterator_non_android.cc index a31ee94..04ff750 100644 --- a/chrome/browser/ui/browser_window/internal/browser_window_interface_iterator_non_android.cc +++ b/chrome/browser/ui/browser_window/internal/browser_window_interface_iterator_non_android.cc
@@ -21,7 +21,7 @@ BrowserList::GetInstance()->end_browsers_ordered_by_activation()); } -BrowserWindowInterface* GetLastActiveBrowserWindowInterface() { +BrowserWindowInterface* GetLastActiveBrowserWindowInterfaceWithAnyProfile() { // TODO(crbug.com/431671448): This is implemented in terms of BrowserList to // ensure it stays in sync with other BrowserList APIs during migration. It // can be implemented directly once clients are migrated off of BrowserList.
diff --git a/chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h b/chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h index 82186fe..cc14f87 100644 --- a/chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h +++ b/chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h
@@ -22,6 +22,10 @@ // Returns the last active browser window interface. This can be nullptr if // there are no browser windows. -BrowserWindowInterface* GetLastActiveBrowserWindowInterface(); +// CAUTION: This can return a browser window with *any* profile. Please verify +// the profile. +// If you only care whether a *particular* browser is active, prefer checking +// that with `browser->GetWindow()->IsActive()`, or similar. +BrowserWindowInterface* GetLastActiveBrowserWindowInterfaceWithAnyProfile(); #endif // CHROME_BROWSER_UI_BROWSER_WINDOW_PUBLIC_BROWSER_WINDOW_INTERFACE_ITERATOR_H_
diff --git a/chrome/browser/ui/browser_window/public/browser_window_interface_iterator_browsertest.cc b/chrome/browser/ui/browser_window/public/browser_window_interface_iterator_browsertest.cc index 02de2d5..58d0277 100644 --- a/chrome/browser/ui/browser_window/public/browser_window_interface_iterator_browsertest.cc +++ b/chrome/browser/ui/browser_window/public/browser_window_interface_iterator_browsertest.cc
@@ -14,8 +14,8 @@ using BrowserWindowInterfaceIteratorBrowserTest = InProcessBrowserTest; -// Test that GetLastActiveBrowserWindowInterface returns the most recently -// activated browser. +// Test that GetLastActiveBrowserWindowInterfaceWithAnyProfile returns the most +// recently activated browser. // TODO(crbug.com/431671448): Disable test on Linux until passing. #if BUILDFLAG(IS_LINUX) #define MAYBE_GetLastActiveBrowserWindowInterface_ReturnsLastActive \ @@ -31,7 +31,7 @@ Browser* const browser1 = browser(); // Verify initial state - the default browser should be the last active. - EXPECT_EQ(GetLastActiveBrowserWindowInterface(), browser1); + EXPECT_EQ(GetLastActiveBrowserWindowInterfaceWithAnyProfile(), browser1); // Create a second browser window. Browser* const browser2 = @@ -41,11 +41,13 @@ // Activate the second browser. browser2->window()->Activate(); - EXPECT_TRUE(base::test::RunUntil( - [&] { return GetLastActiveBrowserWindowInterface() == browser2; })); + EXPECT_TRUE(base::test::RunUntil([&] { + return GetLastActiveBrowserWindowInterfaceWithAnyProfile() == browser2; + })); // Activate the first browser again. browser1->window()->Activate(); - EXPECT_TRUE(base::test::RunUntil( - [&] { return GetLastActiveBrowserWindowInterface() == browser1; })); + EXPECT_TRUE(base::test::RunUntil([&] { + return GetLastActiveBrowserWindowInterfaceWithAnyProfile() == browser1; + })); }
diff --git a/chrome/browser/ui/commerce/BUILD.gn b/chrome/browser/ui/commerce/BUILD.gn index a0e94d35..eded1bd3 100644 --- a/chrome/browser/ui/commerce/BUILD.gn +++ b/chrome/browser/ui/commerce/BUILD.gn
@@ -62,6 +62,7 @@ "//chrome/browser/ui/toasts", "//chrome/browser/ui/toasts/api:toasts", "//chrome/browser/ui/toolbar", + "//chrome/browser/ui/views/frame:toolbar_button_provider", "//chrome/browser/ui/views/side_panel", "//chrome/common:constants", "//components/bookmarks/browser",
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model.cc b/chrome/browser/ui/content_settings/content_setting_bubble_model.cc index 6f2de65f..b435e55 100644 --- a/chrome/browser/ui/content_settings/content_setting_bubble_model.cc +++ b/chrome/browser/ui/content_settings/content_setting_bubble_model.cc
@@ -299,6 +299,10 @@ {ContentSettingsType::GEOLOCATION, IDS_BLOCKED_GEOLOCATION_TITLE}, {ContentSettingsType::MIDI_SYSEX, IDS_BLOCKED_MIDI_SYSEX_TITLE}, {ContentSettingsType::SENSORS, IDS_BLOCKED_SENSORS_TITLE}, +#if BUILDFLAG(IS_WIN) + {ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER, + IDS_BLOCKED_PROTECTED_CONTENT_IDENTIFIERS_TITLE}, +#endif }; // Fields as for kBlockedTitleIDs, above. static const ContentSettingsTypeIdEntry kAccessedTitleIDs[] = { @@ -307,6 +311,10 @@ {ContentSettingsType::GEOLOCATION, IDS_ALLOWED_GEOLOCATION_TITLE}, {ContentSettingsType::MIDI_SYSEX, IDS_ALLOWED_MIDI_SYSEX_TITLE}, {ContentSettingsType::SENSORS, IDS_ALLOWED_SENSORS_TITLE}, +#if BUILDFLAG(IS_WIN) + {ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER, + IDS_ALLOWED_PROTECTED_CONTENT_IDENTIFIERS_TITLE}, +#endif }; int title_id = [&]() { @@ -343,6 +351,10 @@ base::FeatureList::IsEnabled(features::kGenericSensorExtraClasses) ? IDS_BLOCKED_SENSORS_MESSAGE : IDS_BLOCKED_MOTION_SENSORS_MESSAGE}, +#if BUILDFLAG(IS_WIN) + {ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER, + IDS_BLOCKED_PROTECTED_CONTENT_IDENTIFIERS_MESSAGE}, +#endif }; // Fields as for kBlockedMessageIDs, above. const ContentSettingsTypeIdEntry kAccessedMessageIDs[] = { @@ -355,6 +367,10 @@ base::FeatureList::IsEnabled(features::kGenericSensorExtraClasses) ? IDS_ALLOWED_SENSORS_MESSAGE : IDS_ALLOWED_MOTION_SENSORS_MESSAGE}, +#if BUILDFLAG(IS_WIN) + {ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER, + IDS_ALLOWED_PROTECTED_CONTENT_IDENTIFIERS_MESSAGE}, +#endif }; int message_id = [&]() { @@ -659,6 +675,10 @@ {ContentSettingsType::CLIPBOARD_READ_WRITE, IDS_BLOCKED_CLIPBOARD_UNBLOCK}, {ContentSettingsType::SENSORS, IDS_BLOCKED_SENSORS_UNBLOCK}, +#if BUILDFLAG(IS_WIN) + {ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER, + IDS_BLOCKED_PROTECTED_CONTENT_IDENTIFIERS_UNBLOCK}, +#endif }; // Fields as for kBlockedAllowIDs, above. static const ContentSettingsTypeIdEntry kAllowedAllowIDs[] = { @@ -668,6 +688,10 @@ {ContentSettingsType::CLIPBOARD_READ_WRITE, IDS_ALLOWED_CLIPBOARD_NO_ACTION}, {ContentSettingsType::SENSORS, IDS_ALLOWED_SENSORS_NO_ACTION}, +#if BUILDFLAG(IS_WIN) + {ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER, + IDS_ALLOWED_PROTECTED_CONTENT_IDENTIFIERS_NO_ACTION}, +#endif }; std::u16string radio_allow_label; @@ -694,6 +718,10 @@ {ContentSettingsType::CLIPBOARD_READ_WRITE, IDS_BLOCKED_CLIPBOARD_NO_ACTION}, {ContentSettingsType::SENSORS, IDS_BLOCKED_SENSORS_NO_ACTION}, +#if BUILDFLAG(IS_WIN) + {ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER, + IDS_BLOCKED_PROTECTED_CONTENT_IDENTIFIERS_NO_ACTION}, +#endif }; static const ContentSettingsTypeIdEntry kAllowedBlockIDs[] = { {ContentSettingsType::COOKIES, IDS_ALLOWED_ON_DEVICE_SITE_DATA_BLOCK}, @@ -701,6 +729,10 @@ {ContentSettingsType::MIDI_SYSEX, IDS_ALLOWED_MIDI_SYSEX_BLOCK}, {ContentSettingsType::CLIPBOARD_READ_WRITE, IDS_ALLOWED_CLIPBOARD_BLOCK}, {ContentSettingsType::SENSORS, IDS_ALLOWED_SENSORS_BLOCK}, +#if BUILDFLAG(IS_WIN) + {ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER, + IDS_ALLOWED_PROTECTED_CONTENT_IDENTIFIERS_BLOCK}, +#endif }; std::u16string radio_block_label; @@ -1920,6 +1952,9 @@ case ContentSettingsType::CLIPBOARD_READ_WRITE: case ContentSettingsType::MIDI_SYSEX: case ContentSettingsType::SENSORS: +#if BUILDFLAG(IS_WIN) + case ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER: +#endif return std::make_unique<ContentSettingSingleRadioGroup>( delegate, web_contents, content_type); case ContentSettingsType::STORAGE_ACCESS:
diff --git a/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc b/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc index bd4c42b..15824ada 100644 --- a/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc +++ b/chrome/browser/ui/content_settings/content_setting_bubble_model_unittest.cc
@@ -1491,3 +1491,87 @@ l10n_util::GetStringUTF16(IDS_ACCESSED_SMART_CARD_READER_BODY)); } #endif + +#if BUILDFLAG(IS_WIN) +TEST_F(ContentSettingBubbleModelTest, ProtectedMediaIdentifier_Allowed) { + // Arrange + WebContentsTester::For(web_contents()) + ->NavigateAndCommit(GURL("https://www.example.com")); + PageSpecificContentSettings* content_settings = + PageSpecificContentSettings::GetForFrame( + web_contents()->GetPrimaryMainFrame()); + HostContentSettingsMap* settings_map = + HostContentSettingsMapFactory::GetForProfile(profile()); + settings_map->SetDefaultContentSetting( + ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER, CONTENT_SETTING_ALLOW); + + // Act + content_settings->OnContentAllowed( + ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER); + + std::unique_ptr<ContentSettingBubbleModel> content_setting_bubble_model( + ContentSettingBubbleModel::CreateContentSettingBubbleModel( + nullptr, web_contents(), + ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER)); + const auto& bubble_content = content_setting_bubble_model->bubble_content(); + + // Assert + EXPECT_EQ(bubble_content.title, + l10n_util::GetStringUTF16( + IDS_ALLOWED_PROTECTED_CONTENT_IDENTIFIERS_TITLE)); + EXPECT_EQ(bubble_content.message, + l10n_util::GetStringUTF16( + IDS_ALLOWED_PROTECTED_CONTENT_IDENTIFIERS_MESSAGE)); + ASSERT_EQ(bubble_content.radio_group.radio_items.size(), 2U); + EXPECT_EQ(bubble_content.radio_group.radio_items[0], + l10n_util::GetStringUTF16( + IDS_ALLOWED_PROTECTED_CONTENT_IDENTIFIERS_NO_ACTION)); + EXPECT_EQ(bubble_content.radio_group.radio_items[1], + l10n_util::GetStringFUTF16( + IDS_ALLOWED_PROTECTED_CONTENT_IDENTIFIERS_BLOCK, + url_formatter::FormatUrlForSecurityDisplay( + web_contents()->GetLastCommittedURL()))); + EXPECT_EQ(bubble_content.radio_group.default_item, 0); +} + +TEST_F(ContentSettingBubbleModelTest, ProtectedMediaIdentifier_Blocked) { + // Arrange + WebContentsTester::For(web_contents()) + ->NavigateAndCommit(GURL("https://www.example.com")); + PageSpecificContentSettings* content_settings = + PageSpecificContentSettings::GetForFrame( + web_contents()->GetPrimaryMainFrame()); + HostContentSettingsMap* settings_map = + HostContentSettingsMapFactory::GetForProfile(profile()); + settings_map->SetDefaultContentSetting( + ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER, CONTENT_SETTING_ALLOW); + + // Act + content_settings->OnContentBlocked( + ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER); + + std::unique_ptr<ContentSettingBubbleModel> content_setting_bubble_model( + ContentSettingBubbleModel::CreateContentSettingBubbleModel( + nullptr, web_contents(), + ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER)); + const auto& bubble_content = content_setting_bubble_model->bubble_content(); + + // Assert + EXPECT_EQ(bubble_content.title, + l10n_util::GetStringUTF16( + IDS_BLOCKED_PROTECTED_CONTENT_IDENTIFIERS_TITLE)); + EXPECT_EQ(bubble_content.message, + l10n_util::GetStringUTF16( + IDS_BLOCKED_PROTECTED_CONTENT_IDENTIFIERS_MESSAGE)); + ASSERT_EQ(bubble_content.radio_group.radio_items.size(), 2U); + EXPECT_EQ(bubble_content.radio_group.radio_items[0], + l10n_util::GetStringFUTF16( + IDS_BLOCKED_PROTECTED_CONTENT_IDENTIFIERS_UNBLOCK, + url_formatter::FormatUrlForSecurityDisplay( + web_contents()->GetLastCommittedURL()))); + EXPECT_EQ(bubble_content.radio_group.radio_items[1], + l10n_util::GetStringUTF16( + IDS_BLOCKED_PROTECTED_CONTENT_IDENTIFIERS_NO_ACTION)); + EXPECT_EQ(bubble_content.radio_group.default_item, 0); +} +#endif // BUILDFLAG(IS_WIN)
diff --git a/chrome/browser/ui/content_settings/content_setting_image_model.cc b/chrome/browser/ui/content_settings/content_setting_image_model.cc index 67786bb9..82993d5 100644 --- a/chrome/browser/ui/content_settings/content_setting_image_model.cc +++ b/chrome/browser/ui/content_settings/content_setting_image_model.cc
@@ -46,6 +46,7 @@ #include "components/strings/grit/components_strings.h" #include "components/vector_icons/vector_icons.h" #include "content/public/browser/web_contents.h" +#include "media/base/media_switches.h" #include "net/base/schemeful_site.h" #include "services/device/public/cpp/device_features.h" #include "ui/base/l10n/l10n_util.h" @@ -291,6 +292,21 @@ bool UpdateAndGetVisibility(WebContents* web_contents) override; }; +#if BUILDFLAG(IS_WIN) +class ContentSettingProtectedMediaIdentifierImageModel + : public ContentSettingSimpleImageModel { + public: + ContentSettingProtectedMediaIdentifierImageModel(); + + ContentSettingProtectedMediaIdentifierImageModel( + const ContentSettingProtectedMediaIdentifierImageModel&) = delete; + ContentSettingProtectedMediaIdentifierImageModel& operator=( + const ContentSettingProtectedMediaIdentifierImageModel&) = delete; + + bool UpdateAndGetVisibility(WebContents* web_contents) override; +}; +#endif // BUILDFLAG(IS_WIN) + namespace { struct ContentSettingsImageDetails { @@ -400,6 +416,13 @@ *icon = &vector_icons::kSmartCardReaderIcon; return; #endif +#if BUILDFLAG(IS_WIN) + case ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER: + // TODO(crbug.com/429039234): Update corrected/new icons + *icon = blocked ? &vector_icons::kWebAssetOffIcon + : &vector_icons::kSyncSavedLocallyIcon; + return; +#endif // BUILDFLAG(IS_WIN) default: NOTREACHED(); } @@ -486,6 +509,11 @@ case ImageType::SMART_CARD: return std::make_unique<ContentSettingSmartCardImageModel>(); #endif +#if BUILDFLAG(IS_WIN) + case ImageType::PROTECTED_MEDIA_IDENTIFIER: + return std::make_unique< + ContentSettingProtectedMediaIdentifierImageModel>(); +#endif // BUILDFLAG(IS_WIN) case ImageType::NUM_IMAGE_TYPES: break; @@ -1248,6 +1276,43 @@ } } +#if BUILDFLAG(IS_WIN) +// Protected media identifiers +// ------------------------------------------------------------------- + +ContentSettingProtectedMediaIdentifierImageModel:: + ContentSettingProtectedMediaIdentifierImageModel() + : ContentSettingSimpleImageModel( + ImageType::PROTECTED_MEDIA_IDENTIFIER, + ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER, + /*image_type_should_notify_accessibility=*/true) {} + +bool ContentSettingProtectedMediaIdentifierImageModel::UpdateAndGetVisibility( + WebContents* web_contents) { + PageSpecificContentSettings* content_settings = + PageSpecificContentSettings::GetForFrame( + web_contents->GetPrimaryMainFrame()); + if (!content_settings) { + return false; + } + ContentSettingsType content_type = + ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER; + bool blocked = content_settings->IsContentBlocked(content_type); + bool allowed = content_settings->IsContentAllowed(content_type); + if (!blocked && !allowed) { + return false; + } + + SetIcon(ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER, + /*blocked=*/!allowed); + auto message_id = allowed ? IDS_ALLOWED_PROTECTED_CONTENT_IDENTIFIERS_MESSAGE + : IDS_BLOCKED_PROTECTED_CONTENT_IDENTIFIERS_MESSAGE; + set_tooltip(l10n_util::GetStringUTF16(message_id)); + set_accessibility_string_id(message_id); + return true; +} +#endif // BUILDFLAG(IS_WIN) + // Base class ------------------------------------------------------------------ gfx::Image ContentSettingImageModel::GetIcon(SkColor icon_color) const { @@ -1311,10 +1376,20 @@ #if BUILDFLAG(IS_CHROMEOS) ImageType::SMART_CARD, #endif +#if BUILDFLAG(IS_WIN) + ImageType::PROTECTED_MEDIA_IDENTIFIER, +#endif }; std::vector<std::unique_ptr<ContentSettingImageModel>> result; for (auto type : kContentSettingImageOrder) { +#if BUILDFLAG(IS_WIN) + if (type == ImageType::PROTECTED_MEDIA_IDENTIFIER && + !base::FeatureList::IsEnabled( + media::kProtectedMediaIdentifierIndicator)) { + continue; + } +#endif result.push_back(CreateForContentType(type)); }
diff --git a/chrome/browser/ui/content_settings/content_setting_image_model.h b/chrome/browser/ui/content_settings/content_setting_image_model.h index e7f6225..e31fde4 100644 --- a/chrome/browser/ui/content_settings/content_setting_image_model.h +++ b/chrome/browser/ui/content_settings/content_setting_image_model.h
@@ -56,6 +56,9 @@ #if BUILDFLAG(IS_CHROMEOS) SMART_CARD = 22, #endif +#if BUILDFLAG(IS_WIN) + PROTECTED_MEDIA_IDENTIFIER = 23, +#endif NUM_IMAGE_TYPES };
diff --git a/chrome/browser/ui/content_settings/content_setting_image_model_unittest.cc b/chrome/browser/ui/content_settings/content_setting_image_model_unittest.cc index a946704..e159d041 100644 --- a/chrome/browser/ui/content_settings/content_setting_image_model_unittest.cc +++ b/chrome/browser/ui/content_settings/content_setting_image_model_unittest.cc
@@ -814,4 +814,150 @@ } #endif // !BUILDFLAG(IS_CHROMEOS) +#if BUILDFLAG(IS_WIN) +TEST_F(ContentSettingImageModelTest, ProtectedMediaIdentifier_Allowed) { + // Arrange + PageSpecificContentSettings::CreateForWebContents( + web_contents(), + std::make_unique<PageSpecificContentSettingsDelegate>(web_contents())); + NavigateAndCommit(web_contents(), GURL("https://www.example.com")); + PageSpecificContentSettings* content_settings = + PageSpecificContentSettings::GetForFrame( + web_contents()->GetPrimaryMainFrame()); + auto content_setting_image_model = + ContentSettingImageModel::CreateForContentType( + ContentSettingImageModel::ImageType::PROTECTED_MEDIA_IDENTIFIER); + + // Guard + EXPECT_FALSE(content_setting_image_model->is_visible()); + EXPECT_TRUE(content_setting_image_model->get_tooltip().empty()); + + // Act + content_settings->OnContentAllowed( + ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER); + content_setting_image_model->Update(web_contents()); + + // Assert + EXPECT_TRUE(content_setting_image_model->is_visible()); + EXPECT_EQ(content_setting_image_model->get_tooltip(), + l10n_util::GetStringUTF16( + IDS_ALLOWED_PROTECTED_CONTENT_IDENTIFIERS_MESSAGE)); + EXPECT_EQ(content_setting_image_model->icon(), + &vector_icons::kSyncSavedLocallyIcon); +} + +TEST_F(ContentSettingImageModelTest, ProtectedMediaIdentifier_Blocked) { + // Arrange + PageSpecificContentSettings::CreateForWebContents( + web_contents(), + std::make_unique<PageSpecificContentSettingsDelegate>(web_contents())); + NavigateAndCommit(web_contents(), GURL("https://www.example.com")); + PageSpecificContentSettings* content_settings = + PageSpecificContentSettings::GetForFrame( + web_contents()->GetPrimaryMainFrame()); + auto content_setting_image_model = + ContentSettingImageModel::CreateForContentType( + ContentSettingImageModel::ImageType::PROTECTED_MEDIA_IDENTIFIER); + + // Guard + EXPECT_FALSE(content_setting_image_model->is_visible()); + EXPECT_TRUE(content_setting_image_model->get_tooltip().empty()); + + // Act + content_settings->OnContentBlocked( + ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER); + content_setting_image_model->Update(web_contents()); + + // Assert + EXPECT_TRUE(content_setting_image_model->is_visible()); + EXPECT_EQ(content_setting_image_model->get_tooltip(), + l10n_util::GetStringUTF16( + IDS_BLOCKED_PROTECTED_CONTENT_IDENTIFIERS_MESSAGE)); + EXPECT_EQ(content_setting_image_model->icon(), + &vector_icons::kWebAssetOffIcon); +} + +TEST_F(ContentSettingImageModelTest, ProtectedMediaIdentifier_Reconciled) { + // This test simulates the conflict state (allowed=1 and blocked=1 at the same + // time) by the following steps: + // a) Change PageSpecificContentSettings to Allowed or Blocked. + // b) Update HostContentSettingMap with the same state. + // c) Call HostContentSettingMap::OnContentSettingChanged() to trigger + // PageSpecificContentSettings::OnContentSettingChanged() so that it can + // reconcile the conflict state. + // d) Update ContentSettingImageModel to verify if the state is reconciled. + + // Arrange + GURL host("https://example.com/"); + const auto type = ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER; + PageSpecificContentSettings::CreateForWebContents( + web_contents(), + std::make_unique<PageSpecificContentSettingsDelegate>(web_contents())); + auto* map = HostContentSettingsMapFactory::GetForProfile( + Profile::FromBrowserContext(web_contents()->GetBrowserContext())); + NavigateAndCommit(web_contents(), host); + PageSpecificContentSettings* content_settings = + PageSpecificContentSettings::GetForFrame( + web_contents()->GetPrimaryMainFrame()); + auto content_setting_image_model = + ContentSettingImageModel::CreateForContentType( + ContentSettingImageModel::ImageType::PROTECTED_MEDIA_IDENTIFIER); + + // Guard + map->SetContentSettingDefaultScope(host, host, type, CONTENT_SETTING_ALLOW); + EXPECT_EQ(CONTENT_SETTING_ALLOW, map->GetContentSetting(host, host, type)); + EXPECT_FALSE(content_setting_image_model->is_visible()); + EXPECT_TRUE(content_setting_image_model->get_tooltip().empty()); + + // 1-1. Act: Allowed + content_settings->OnProtectedMediaIdentifierPermissionSet(host, + /*allowed=*/true); + map->SetContentSettingDefaultScope(host, host, type, CONTENT_SETTING_ALLOW); + map->OnContentSettingChanged(ContentSettingsPattern::Wildcard(), + ContentSettingsPattern::Wildcard(), + ContentSettingsTypeSet(type)); + + // 1-2. Assert: Allowed + EXPECT_EQ(CONTENT_SETTING_ALLOW, map->GetContentSetting(host, host, type)); + UpdateModelAndVerifyStates( + content_setting_image_model.get(), /* is_visible = */ true, + /* tooltip_empty = */ false, + IDS_ALLOWED_PROTECTED_CONTENT_IDENTIFIERS_MESSAGE, 0); + + // 2-1. Act: Blocked + content_settings->OnProtectedMediaIdentifierPermissionSet(host, + /*allowed=*/false); + map->SetContentSettingDefaultScope(host, host, type, CONTENT_SETTING_BLOCK); + map->OnContentSettingChanged(ContentSettingsPattern::Wildcard(), + ContentSettingsPattern::Wildcard(), + ContentSettingsTypeSet(type)); + + // 2-2. Assert: Blocked + EXPECT_EQ(CONTENT_SETTING_BLOCK, map->GetContentSetting(host, host, type)); + UpdateModelAndVerifyStates( + content_setting_image_model.get(), /* is_visible = */ true, + /* tooltip_empty = */ false, + IDS_BLOCKED_PROTECTED_CONTENT_IDENTIFIERS_MESSAGE, 0); + + // 3-1. Act: Allowed (Conflict reconciled) + content_settings->OnProtectedMediaIdentifierPermissionSet(host, + /*allowed=*/true); + map->SetContentSettingDefaultScope(host, host, type, CONTENT_SETTING_ALLOW); + // At this point, there is a conflict with status.allowed=1 and + // status.blocked=1 in PageSpecificContentSettings::OnContentSettingChanged(). + // The conflict should be reconciled. + map->OnContentSettingChanged(ContentSettingsPattern::Wildcard(), + ContentSettingsPattern::Wildcard(), + ContentSettingsTypeSet(type)); + + // 3-1. Act: Allowed (Conflict reconciled) + EXPECT_EQ(CONTENT_SETTING_ALLOW, map->GetContentSetting(host, host, type)); + UpdateModelAndVerifyStates( + content_setting_image_model.get(), /* is_visible = */ true, + /* tooltip_empty = */ false, + IDS_ALLOWED_PROTECTED_CONTENT_IDENTIFIERS_MESSAGE, 0); +} + +#endif // BUILDFLAG(IS_WIN) + } // namespace
diff --git a/chrome/browser/ui/performance_controls/BUILD.gn b/chrome/browser/ui/performance_controls/BUILD.gn index 9f4e5d9..2fed11b 100644 --- a/chrome/browser/ui/performance_controls/BUILD.gn +++ b/chrome/browser/ui/performance_controls/BUILD.gn
@@ -86,6 +86,7 @@ "//chrome/browser/ui/tabs:tab_strip_model_observer", "//chrome/browser/ui/tabs:tabs_public", "//chrome/browser/ui/user_education", + "//chrome/browser/ui/views/frame:toolbar_button_provider", "//chrome/browser/ui/views/page_action", "//chrome/common", "//chrome/common:channel_info",
diff --git a/chrome/browser/ui/promos/BUILD.gn b/chrome/browser/ui/promos/BUILD.gn index 1cfb8c55..f52118cd 100644 --- a/chrome/browser/ui/promos/BUILD.gn +++ b/chrome/browser/ui/promos/BUILD.gn
@@ -19,6 +19,7 @@ "//chrome/browser/segmentation_platform", "//chrome/browser/sync", "//chrome/browser/ui", + "//chrome/browser/ui/views/frame:toolbar_button_provider", "//components/feature_engagement/public", "//components/segmentation_platform/embedder/default_model", "//components/segmentation_platform/public",
diff --git a/chrome/browser/ui/signin/BUILD.gn b/chrome/browser/ui/signin/BUILD.gn index fc5df75f5..744fad2 100644 --- a/chrome/browser/ui/signin/BUILD.gn +++ b/chrome/browser/ui/signin/BUILD.gn
@@ -78,6 +78,7 @@ "//chrome/browser/ui/profiles:impl", "//chrome/browser/ui/signin", "//chrome/browser/ui/tabs:tab_strip", + "//chrome/browser/ui/views/frame:toolbar_button_provider", "//chrome/browser/ui/webui/signin", "//chrome/common", "//components/prefs", @@ -188,6 +189,7 @@ "//chrome/browser/ui", "//chrome/browser/ui:ui_features", "//chrome/browser/ui/browser_window", + "//chrome/browser/ui/views/frame:toolbar_button_provider", "//chrome/browser/ui/webui/signin:test_support_ui", "//chrome/test:sync_integration_test_support", "//chrome/test:test_support_ui",
diff --git a/chrome/browser/ui/tabs/BUILD.gn b/chrome/browser/ui/tabs/BUILD.gn index eab19f7d..dfab313 100644 --- a/chrome/browser/ui/tabs/BUILD.gn +++ b/chrome/browser/ui/tabs/BUILD.gn
@@ -429,6 +429,7 @@ "//chrome/browser/profiles", "//chrome/browser/profiles:profile", "//chrome/browser/search", + "//chrome/browser/sessions", "//chrome/browser/signin", "//chrome/browser/sync", "//chrome/browser/sync:factories",
diff --git a/chrome/browser/ui/tabs/tab_strip_api/BUILD.gn b/chrome/browser/ui/tabs/tab_strip_api/BUILD.gn index 9e3105f..14747f2 100644 --- a/chrome/browser/ui/tabs/tab_strip_api/BUILD.gn +++ b/chrome/browser/ui/tabs/tab_strip_api/BUILD.gn
@@ -20,6 +20,7 @@ ":browser_tests", ":impl", ":tab_strip_api", + ":unit_tests", ] } @@ -59,6 +60,10 @@ mojom = "tabs_api.mojom.TabGroupVisualData" cpp = "::tab_groups::TabGroupVisualData" }, + { + mojom = "tabs_api.mojom.SplitTabVisualData" + cpp = "::split_tabs::SplitTabVisualData" + }, ] traits_headers = [ "//chrome/browser/ui/tabs/tab_strip_api/types/node_id.h", @@ -66,11 +71,14 @@ "//chrome/browser/ui/tabs/tab_strip_api/types/position.h", "//chrome/browser/ui/tabs/tab_strip_api/types/position_traits.h", "//chrome/browser/ui/tabs/tab_strip_api/types/tab_group_visual_data_traits.h", + "//chrome/browser/ui/tabs/tab_strip_api/types/split_tab_visual_data_traits.h", "//components/tab_groups/tab_group_visual_data.h", + "//components/tabs/public/split_tab_visual_data.h", ] traits_sources = [ "//chrome/browser/ui/tabs/tab_strip_api/types/node_id_traits.cc", "//chrome/browser/ui/tabs/tab_strip_api/types/position_traits.cc", + "//chrome/browser/ui/tabs/tab_strip_api/types/split_tab_visual_data_traits.cc", "//chrome/browser/ui/tabs/tab_strip_api/types/tab_group_visual_data_traits.cc", ] traits_public_deps = [ @@ -138,10 +146,14 @@ source_set("unit_tests") { testonly = true - sources = [ "tab_strip_service_impl_unittest.cc" ] + sources = [ + "tab_strip_experiment_service_unittest.cc", + "tab_strip_service_impl_unittest.cc", + ] deps = [ ":impl", + ":mojom_experiment", ":tab_strip_api", "converters:unit_tests", "events:unit_tests",
diff --git a/chrome/browser/ui/tabs/tab_strip_api/converters/tab_converters.cc b/chrome/browser/ui/tabs/tab_strip_api/converters/tab_converters.cc index 8922029..46f1ee066c 100644 --- a/chrome/browser/ui/tabs/tab_strip_api/converters/tab_converters.cc +++ b/chrome/browser/ui/tabs/tab_strip_api/converters/tab_converters.cc
@@ -10,6 +10,9 @@ #include "base/strings/utf_string_conversions.h" #include "chrome/browser/ui/tabs/tab_utils.h" #include "components/tab_groups/tab_group_visual_data.h" +#include "components/tabs/public/split_tab_collection.h" +#include "components/tabs/public/split_tab_data.h" +#include "components/tabs/public/split_tab_visual_data.h" #include "components/tabs/public/tab_group.h" #include "components/tabs/public/tab_group_tab_collection.h" @@ -73,8 +76,17 @@ std::move(mojo_tab_group)); } case tabs::TabCollection::Type::SPLIT: { - // TODO(crbug.com/421933194): Add SplitTab. - NOTIMPLEMENTED(); + auto mojo_split_tab = tabs_api::mojom::SplitTab::New(); + mojo_split_tab->id = node_id; + const tabs::SplitTabCollection* split_collection = + static_cast<const tabs::SplitTabCollection*>(collection); + split_tabs::SplitTabData* split_data = split_collection->data(); + CHECK(split_data); + split_tabs::SplitTabVisualData* visual_data = split_data->visual_data(); + CHECK(visual_data); + mojo_split_tab->data = *visual_data; + return tabs_api::mojom::TabCollection::NewSplitTab( + std::move(mojo_split_tab)); } } NOTREACHED();
diff --git a/chrome/browser/ui/tabs/tab_strip_api/tab_strip_api_data_model.mojom b/chrome/browser/ui/tabs/tab_strip_api/tab_strip_api_data_model.mojom index 0b5fe4d..c370037b 100644 --- a/chrome/browser/ui/tabs/tab_strip_api/tab_strip_api_data_model.mojom +++ b/chrome/browser/ui/tabs/tab_strip_api/tab_strip_api_data_model.mojom
@@ -57,6 +57,21 @@ bool is_collapsed; }; +struct SplitTab { + NodeId id; + SplitTabVisualData data; +}; + +struct SplitTabVisualData { + enum Layout { + kVertical, + kHorizontal, + }; + + Layout layout; + double split_ratio; +}; + // A TabCollection is a container in the tab strip tree. Using a union enforces // that a collection must be exactly one of the types below thus preventing // inconsistent states. @@ -65,6 +80,7 @@ PinnedTabs pinned_tabs; UnpinnedTabs unpinned_tabs; TabGroup tab_group; + SplitTab split_tab; }; // The TabCollection tree has child nodes that can be a Tab or a
diff --git a/chrome/browser/ui/tabs/tab_strip_api/tab_strip_experiment_service_unittest.cc b/chrome/browser/ui/tabs/tab_strip_api/tab_strip_experiment_service_unittest.cc new file mode 100644 index 0000000..b8f0837 --- /dev/null +++ b/chrome/browser/ui/tabs/tab_strip_api/tab_strip_experiment_service_unittest.cc
@@ -0,0 +1,108 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/run_loop.h" +#include "base/strings/string_number_conversions.h" +#include "base/test/bind.h" +#include "base/test/task_environment.h" +#include "chrome/browser/ui/tabs/tab_strip_api/tab_strip_experiment_api.mojom.h" +#include "chrome/browser/ui/tabs/tab_strip_api/tab_strip_service_impl.h" +#include "chrome/browser/ui/tabs/tab_strip_api/testing/toy_tab_strip.h" +#include "chrome/browser/ui/tabs/tab_strip_api/testing/toy_tab_strip_browser_adapter.h" +#include "chrome/browser/ui/tabs/tab_strip_api/testing/toy_tab_strip_model_adapter.h" +#include "chrome/browser/ui/tabs/tab_strip_api/types/node_id.h" +#include "components/tabs/public/tab_collection.h" +#include "components/tabs/public/tab_interface.h" +#include "content/public/test/browser_task_environment.h" +#include "mojo/public/cpp/bindings/receiver.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "mojo/public/mojom/base/error.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace tabs_api { +namespace { + +class TabStripExperimentServiceImplTest : public ::testing::Test { + protected: + TabStripExperimentServiceImplTest() = default; + ~TabStripExperimentServiceImplTest() override = default; + + void SetUp() override { + tab_strip_ = std::make_unique<testing::ToyTabStrip>(); + impl_ = std::make_unique<TabStripServiceImpl>( + std::make_unique<testing::ToyTabStripBrowserAdapter>(tab_strip_.get()), + std::make_unique<testing::ToyTabStripModelAdapter>(tab_strip_.get())); + impl_->AcceptExperimental(client_.BindNewPipeAndPassReceiver()); + } + + mojo::Remote<tabs_api::mojom::TabStripExperimentService> client_; + std::unique_ptr<testing::ToyTabStrip> tab_strip_; + + private: + content::BrowserTaskEnvironment task_environment_; + std::unique_ptr<TabStripServiceImpl> impl_; +}; + +TEST_F(TabStripExperimentServiceImplTest, UpdateTabGroupVisual) { + const tab_groups::TabGroupVisualData initial_visuals( + u"old title", tab_groups::TabGroupColorId::kGrey); + const tabs::TabCollectionHandle group_handle = + tab_strip_->AddGroup(initial_visuals); + + ASSERT_EQ(u"old title", + tab_strip_->GetGroupVisualData(group_handle)->title()); + ASSERT_EQ(tab_groups::TabGroupColorId::kGrey, + tab_strip_->GetGroupVisualData(group_handle)->color()); + + std::u16string expected = u"new title"; + tabs_api::NodeId group_node_id( + NodeId::Type::kCollection, + base::NumberToString(group_handle.raw_value())); + tab_groups::TabGroupVisualData new_visuals( + expected, tab_groups::TabGroupColorId::kBlue); + + mojom::TabStripExperimentService::UpdateTabGroupVisualResult result; + bool success = + client_->UpdateTabGroupVisual(group_node_id, new_visuals, &result); + + ASSERT_TRUE(success); + ASSERT_TRUE(result.has_value()); + + const tab_groups::TabGroupVisualData* updated_visuals = + tab_strip_->GetGroupVisualData(group_handle); + ASSERT_EQ(expected, updated_visuals->title()); + ASSERT_EQ(tab_groups::TabGroupColorId::kBlue, updated_visuals->color()); +} + +TEST_F(TabStripExperimentServiceImplTest, + UpdateTabGroupVisual_InvalidNodeType) { + tabs_api::NodeId tab_node_id(NodeId::Type::kContent, "123"); + tab_groups::TabGroupVisualData new_visuals( + u"title", tab_groups::TabGroupColorId::kBlue); + + mojom::TabStripExperimentService::UpdateTabGroupVisualResult result; + bool success = + client_->UpdateTabGroupVisual(tab_node_id, new_visuals, &result); + + ASSERT_TRUE(success); + ASSERT_FALSE(result.has_value()); + ASSERT_EQ(result.error()->code, mojo_base::mojom::Code::kInvalidArgument); +} + +TEST_F(TabStripExperimentServiceImplTest, UpdateTabGroupVisual_NotFound) { + NodeId group_node_id(NodeId::Type::kCollection, "123"); + tab_groups::TabGroupVisualData new_visuals( + u"title", tab_groups::TabGroupColorId::kBlue); + + mojom::TabStripExperimentService::UpdateTabGroupVisualResult result; + bool success = + client_->UpdateTabGroupVisual(group_node_id, new_visuals, &result); + + ASSERT_TRUE(success); + ASSERT_FALSE(result.has_value()); + ASSERT_EQ(result.error()->code, mojo_base::mojom::Code::kNotFound); +} + +} // namespace +} // namespace tabs_api
diff --git a/chrome/browser/ui/tabs/tab_strip_api/testing/toy_tab_strip.cc b/chrome/browser/ui/tabs/tab_strip_api/testing/toy_tab_strip.cc index bf892bd..3ddfe43 100644 --- a/chrome/browser/ui/tabs/tab_strip_api/testing/toy_tab_strip.cc +++ b/chrome/browser/ui/tabs/tab_strip_api/testing/toy_tab_strip.cc
@@ -83,4 +83,45 @@ return id++; } +std::optional<tab_groups::TabGroupId> ToyTabStrip::GetGroupIdFor( + const tabs::TabCollectionHandle& handle) const { + for (const auto& group : groups_with_visuals_) { + if (group.handle == handle) { + return group.id; + } + } + return std::nullopt; +} + +tabs::TabCollectionHandle ToyTabStrip::AddGroup( + const tab_groups::TabGroupVisualData& visual_data) { + ToyTabGroupData new_group{tab_groups::TabGroupId::GenerateNew(), + tabs::TabCollectionHandle(GetNextId()), + visual_data}; + auto handle = new_group.handle; + groups_with_visuals_.push_back(std::move(new_group)); + return handle; +} + +const tab_groups::TabGroupVisualData* ToyTabStrip::GetGroupVisualData( + const tabs::TabCollectionHandle& handle) const { + for (const auto& group : groups_with_visuals_) { + if (group.handle == handle) { + return &group.visuals; + } + } + return nullptr; +} + +void ToyTabStrip::UpdateGroupVisuals( + const tab_groups::TabGroupId& group_id, + const tab_groups::TabGroupVisualData& new_visuals) { + for (auto& group : groups_with_visuals_) { + if (group.id == group_id) { + group.visuals = new_visuals; + return; + } + } +} + } // namespace tabs_api::testing
diff --git a/chrome/browser/ui/tabs/tab_strip_api/testing/toy_tab_strip.h b/chrome/browser/ui/tabs/tab_strip_api/testing/toy_tab_strip.h index 93208dc..08bcd59 100644 --- a/chrome/browser/ui/tabs/tab_strip_api/testing/toy_tab_strip.h +++ b/chrome/browser/ui/tabs/tab_strip_api/testing/toy_tab_strip.h
@@ -5,6 +5,8 @@ #ifndef CHROME_BROWSER_UI_TABS_TAB_STRIP_API_TESTING_TOY_TAB_STRIP_H_ #define CHROME_BROWSER_UI_TABS_TAB_STRIP_API_TESTING_TOY_TAB_STRIP_H_ +#include "components/tab_groups/tab_group_id.h" +#include "components/tab_groups/tab_group_visual_data.h" #include "components/tabs/public/tab_collection.h" #include "components/tabs/public/tab_interface.h" #include "url/gurl.h" @@ -16,11 +18,17 @@ bool active = false; }; -struct ToyTabGroup { +struct ToyTabCollection { tabs::TabCollection::Handle collection_handle; std::vector<ToyTab> tabs; }; +struct ToyTabGroupData { + tab_groups::TabGroupId id; + tabs::TabCollectionHandle handle; + tab_groups::TabGroupVisualData visuals; +}; + // A toy tab strip for integration testing. Toy tab strip is a simple // shallow tree backed by a vector of "tabs." class ToyTabStrip { @@ -38,15 +46,25 @@ void ActivateTab(tabs::TabHandle handle); tabs::TabHandle FindActiveTab(); void MoveTab(tabs::TabHandle handle, size_t to); - ToyTabGroup GetRoot(); + + std::optional<tab_groups::TabGroupId> GetGroupIdFor( + const tabs::TabCollectionHandle& handle) const; + tabs::TabCollectionHandle AddGroup( + const tab_groups::TabGroupVisualData& visual_data); + const tab_groups::TabGroupVisualData* GetGroupVisualData( + const tabs::TabCollectionHandle& handle) const; + void UpdateGroupVisuals(const tab_groups::TabGroupId& group_id, + const tab_groups::TabGroupVisualData& new_visuals); protected: // An ever incrementing id. int GetNextId(); private: - ToyTabGroup root_{tabs::TabCollection::Handle(GetNextId()), - std::vector<ToyTab>()}; + std::vector<ToyTabGroupData> groups_with_visuals_; + + ToyTabCollection root_{tabs::TabCollection::Handle(GetNextId()), + std::vector<ToyTab>()}; }; } // namespace tabs_api::testing
diff --git a/chrome/browser/ui/tabs/tab_strip_api/testing/toy_tab_strip_model_adapter.cc b/chrome/browser/ui/tabs/tab_strip_api/testing/toy_tab_strip_model_adapter.cc index 517a3a2..ea14e09 100644 --- a/chrome/browser/ui/tabs/tab_strip_api/testing/toy_tab_strip_model_adapter.cc +++ b/chrome/browser/ui/tabs/tab_strip_api/testing/toy_tab_strip_model_adapter.cc
@@ -80,16 +80,13 @@ std::optional<const tab_groups::TabGroupId> ToyTabStripModelAdapter::FindGroupIdFor( const tabs::TabCollection::Handle& collection_handle) { - // TODO(crbug.com/425390972): Integrate with the toy tabstrip for testing - // purposes. - return std::nullopt; + return tab_strip_->GetGroupIdFor(collection_handle); } void ToyTabStripModelAdapter::UpdateTabGroupVisuals( const tab_groups::TabGroupId& group, const tab_groups::TabGroupVisualData& visual_data) { - // TODO(crbug.com/433569400) Integrate with the toy tabstrip. - return; + tab_strip_->UpdateGroupVisuals(group, visual_data); } } // namespace tabs_api::testing
diff --git a/chrome/browser/ui/tabs/tab_strip_api/types/split_tab_visual_data_traits.cc b/chrome/browser/ui/tabs/tab_strip_api/types/split_tab_visual_data_traits.cc new file mode 100644 index 0000000..11114f5 --- /dev/null +++ b/chrome/browser/ui/tabs/tab_strip_api/types/split_tab_visual_data_traits.cc
@@ -0,0 +1,53 @@ +// Copyright 2025 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/ui/tabs/tab_strip_api/types/split_tab_visual_data_traits.h" + +MojoSplitLayout mojo::EnumTraits<MojoSplitLayout, NativeSplitLayout>::ToMojom( + NativeSplitLayout input) { + switch (input) { + case NativeSplitLayout::kVertical: + return MojoSplitLayout::kVertical; + case NativeSplitLayout::kHorizontal: + return MojoSplitLayout::kHorizontal; + } + NOTREACHED(); +} + +bool mojo::EnumTraits<MojoSplitLayout, NativeSplitLayout>::FromMojom( + MojoSplitLayout input, + NativeSplitLayout* out) { + switch (input) { + case MojoSplitLayout::kVertical: + *out = NativeSplitLayout::kVertical; + return true; + case MojoSplitLayout::kHorizontal: + *out = NativeSplitLayout::kHorizontal; + return true; + } + return false; +} + +NativeSplitLayout mojo::StructTraits< + MojoSplitTabVisualDataView, + NativeSplitTabVisualData>::layout(const NativeSplitTabVisualData& native) { + return native.split_layout(); +} + +double +mojo::StructTraits<MojoSplitTabVisualDataView, NativeSplitTabVisualData>:: + split_ratio(const NativeSplitTabVisualData& native) { + return native.split_ratio(); +} + +bool mojo::StructTraits<MojoSplitTabVisualDataView, NativeSplitTabVisualData>:: + Read(MojoSplitTabVisualDataView view, NativeSplitTabVisualData* out) { + NativeSplitLayout layout; + if (!view.ReadLayout(&layout)) { + return false; + } + + *out = NativeSplitTabVisualData(layout, view.split_ratio()); + return true; +}
diff --git a/chrome/browser/ui/tabs/tab_strip_api/types/split_tab_visual_data_traits.h b/chrome/browser/ui/tabs/tab_strip_api/types/split_tab_visual_data_traits.h new file mode 100644 index 0000000..883cdb9 --- /dev/null +++ b/chrome/browser/ui/tabs/tab_strip_api/types/split_tab_visual_data_traits.h
@@ -0,0 +1,36 @@ +// Copyright 2025 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_UI_TABS_TAB_STRIP_API_TYPES_SPLIT_TAB_VISUAL_DATA_TRAITS_H_ +#define CHROME_BROWSER_UI_TABS_TAB_STRIP_API_TYPES_SPLIT_TAB_VISUAL_DATA_TRAITS_H_ + +#include "chrome/browser/ui/tabs/tab_strip_api/tab_strip_api_data_model.mojom.h" +#include "components/tabs/public/split_tab_visual_data.h" +#include "mojo/public/cpp/bindings/enum_traits.h" +#include "mojo/public/cpp/bindings/struct_traits.h" + +using MojoSplitLayout = tabs_api::mojom::SplitTabVisualData_Layout; +using NativeSplitLayout = split_tabs::SplitTabLayout; + +template <> +struct mojo::EnumTraits<MojoSplitLayout, NativeSplitLayout> { + static MojoSplitLayout ToMojom(NativeSplitLayout input); + static bool FromMojom(MojoSplitLayout input, NativeSplitLayout* out); +}; + +using MojoSplitTabVisualDataView = tabs_api::mojom::SplitTabVisualDataDataView; +using NativeSplitTabVisualData = split_tabs::SplitTabVisualData; + +template <> +struct mojo::StructTraits<MojoSplitTabVisualDataView, + NativeSplitTabVisualData> { + static NativeSplitLayout layout(const NativeSplitTabVisualData& native); + static double split_ratio(const NativeSplitTabVisualData& native); + + // Decoder: + static bool Read(MojoSplitTabVisualDataView view, + NativeSplitTabVisualData* out); +}; + +#endif // CHROME_BROWSER_UI_TABS_TAB_STRIP_API_TYPES_SPLIT_TAB_VISUAL_DATA_TRAITS_H_
diff --git a/chrome/browser/ui/tabs/tab_strip_api/types/tab_strip_service_traits_unittest.cc b/chrome/browser/ui/tabs/tab_strip_api/types/tab_strip_service_traits_unittest.cc index fd2ded2c..d2e360c 100644 --- a/chrome/browser/ui/tabs/tab_strip_api/types/tab_strip_service_traits_unittest.cc +++ b/chrome/browser/ui/tabs/tab_strip_api/types/tab_strip_service_traits_unittest.cc
@@ -5,6 +5,8 @@ #include "chrome/browser/ui/tabs/tab_strip_api/tab_strip_api.mojom.h" #include "chrome/browser/ui/tabs/tab_strip_api/types/node_id_traits.h" #include "chrome/browser/ui/tabs/tab_strip_api/types/position_traits.h" +#include "components/tab_groups/tab_group_visual_data.h" +#include "components/tabs/public/split_tab_visual_data.h" #include "testing/gtest/include/gtest/gtest.h" namespace tabs_api { @@ -46,5 +48,19 @@ ASSERT_TRUE(original == deserialized); } +TEST(TabsStripServiceMojoTraitsTest, ConvertSplitTabVisualData) { + split_tabs::SplitTabVisualData original(split_tabs::SplitTabLayout::kVertical, + 0.75); + + std::vector<uint8_t> serialized = + mojom::SplitTabVisualData::Serialize(&original); + + split_tabs::SplitTabVisualData deserialized; + ASSERT_TRUE( + mojom::SplitTabVisualData::Deserialize(serialized, &deserialized)); + + ASSERT_TRUE(original == deserialized); +} + } // namespace } // namespace tabs_api
diff --git a/chrome/browser/ui/views/autofill/popup/popup_row_factory_utils.cc b/chrome/browser/ui/views/autofill/popup/popup_row_factory_utils.cc index 0d316990..5972029c 100644 --- a/chrome/browser/ui/views/autofill/popup/popup_row_factory_utils.cc +++ b/chrome/browser/ui/views/autofill/popup/popup_row_factory_utils.cc
@@ -131,6 +131,7 @@ std::unique_ptr<views::BoxLayoutView> GetBadgeView(std::u16string_view label) { return views::Builder<views::BoxLayoutView>() + .SetAccessibleName(std::u16string(label)) .AddChildren(views::Builder<views::Label>() .SetText(std::u16string(label)) .SetAccessibleName(std::u16string(label))
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_menu_delegate.cc b/chrome/browser/ui/views/bookmarks/bookmark_menu_delegate.cc index 7344dfc..58f45bf 100644 --- a/chrome/browser/ui/views/bookmarks/bookmark_menu_delegate.cc +++ b/chrome/browser/ui/views/bookmarks/bookmark_menu_delegate.cc
@@ -1229,7 +1229,12 @@ } void BookmarkMenuDelegate::BuildOtherNodeMenuHeader(MenuItemView* menu) { - CHECK(!menu->HasSubmenu() || menu->GetSubmenu()->children().empty()); + // This menu can be in an inconsistent state when dragging bookmarks, so + // enforce that it's empty before building its contents. + other_node_menu_separator_ = nullptr; + if (menu->HasSubmenu()) { + menu->RemoveAllMenuItems(); + } ui::ImageModel bookmarks_side_panel_icon = ui::ImageModel::FromVectorIcon( kBookmarksSidePanelIcon, ui::kColorMenuIcon, ui::SimpleMenuModel::kDefaultIconSize);
diff --git a/chrome/browser/ui/views/frame/BUILD.gn b/chrome/browser/ui/views/frame/BUILD.gn index 2e58fac5..eab7127 100644 --- a/chrome/browser/ui/views/frame/BUILD.gn +++ b/chrome/browser/ui/views/frame/BUILD.gn
@@ -2,10 +2,20 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -assert(!is_android, "ImmersiveModeController is Desktop only.") +assert(!is_android, + "ImmersiveModeController and ToolbarButtonProvider are Desktop only.") source_set("immersive_mode_controller") { sources = [ "immersive_mode_controller.h" ] deps = [ "//base" ] } + +source_set("toolbar_button_provider") { + sources = [ "toolbar_button_provider.h" ] + + public_deps = [ + "//chrome/browser/ui/page_action:icon_type", + "//ui/actions:actions_headers", + ] +}
diff --git a/chrome/browser/ui/views/location_bar/content_setting_image_view.cc b/chrome/browser/ui/views/location_bar/content_setting_image_view.cc index 104cdb79..888d6075 100644 --- a/chrome/browser/ui/views/location_bar/content_setting_image_view.cc +++ b/chrome/browser/ui/views/location_bar/content_setting_image_view.cc
@@ -81,6 +81,9 @@ #if BUILDFLAG(IS_CHROMEOS) case ImageType::SMART_CARD: #endif +#if BUILDFLAG(IS_WIN) + case ImageType::PROTECTED_MEDIA_IDENTIFIER: +#endif return std::nullopt; case ImageType::NUM_IMAGE_TYPES:
diff --git a/chrome/browser/ui/views/page_info/page_info_view_factory.cc b/chrome/browser/ui/views/page_info/page_info_view_factory.cc index fef3a05..1eff44c 100644 --- a/chrome/browser/ui/views/page_info/page_info_view_factory.cc +++ b/chrome/browser/ui/views/page_info/page_info_view_factory.cc
@@ -354,8 +354,8 @@ break; #if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN) case ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER: - icon = show_blocked_badge ? &vector_icons::kCertificateOffIcon - : &vector_icons::kCertificateIcon; + icon = show_blocked_badge ? &vector_icons::kWebAssetOffIcon + : &vector_icons::kSyncSavedLocallyIcon; break; #endif case ContentSettingsType::MIDI_SYSEX:
diff --git a/chrome/browser/ui/views/profiles/avatar_toolbar_button_browsertest.cc b/chrome/browser/ui/views/profiles/avatar_toolbar_button_browsertest.cc index b137beeb..db121506 100644 --- a/chrome/browser/ui/views/profiles/avatar_toolbar_button_browsertest.cc +++ b/chrome/browser/ui/views/profiles/avatar_toolbar_button_browsertest.cc
@@ -65,6 +65,7 @@ #include "components/policy/policy_constants.h" #include "components/signin/public/base/consent_level.h" #include "components/signin/public/base/signin_metrics.h" +#include "components/signin/public/base/signin_pref_names.h" #include "components/signin/public/base/signin_switches.h" #include "components/signin/public/identity_manager/account_capabilities_test_mutator.h" #include "components/signin/public/identity_manager/account_info.h" @@ -315,8 +316,18 @@ // Signs in to Chrome with `email` and set the `name` to the account name. AccountInfo Signin(const std::u16string& email, const std::u16string& name) { - return MakePrimaryAccountAvailableWithName(signin::ConsentLevel::kSignin, - email, name); + AccountInfo account_info = MakePrimaryAccountAvailableWithName( + signin::ConsentLevel::kSignin, email, name); + + // This simplifies the setup for tests that expect to show the SyncPromo. + if (switches::IsAvatarSyncPromoFeatureEnabled()) { + // Simulate setting enough time passing for the cookie change. + GetBrowser()->GetProfile()->GetPrefs()->SetDouble( + prefs::kGaiaCookieChangedTime, + (base::Time::Now() - base::Days(8)).InSecondsFSinceUnixEpoch()); + } + + return account_info; } // Make sure `image_url` is different for each new image in order for the @@ -1064,20 +1075,61 @@ }; #if BUILDFLAG(ENABLE_DICE_SUPPORT) -class AvatarToolbarButtonHistorySyncOptinBrowserTest +enum FeaturePromoType { + // Enables `switches::kEnableHistorySyncOptinExpansionPill` feature. + kHistorySyncPromo, + // Enables `switches::kAvatarButtonSyncPromoForTesting` feature, which is + // checked through `switches::IsAvatarSyncPromoFeatureEnabled()`. + kSyncPromo, +}; + +struct FeaturePromoTestParams { + FeaturePromoType feature_promo_type; + std::string feature_parameters; +}; + +// The tests relying on this base class can test both the History Sync Promo and +// the Sync Promo. It is important to ensure that one of the feature flags is +// enabled at the same time, since the features are not compatible (SyncPromo +// have a higher priority than HistorySync in `HistorySyncOptinStateProvider`). +class AvatarToolbarButtonSyncPromoBaseBrowserTest : public AvatarToolbarButtonWithInteractiveFeaturePromoBrowserTest { protected: - explicit AvatarToolbarButtonHistorySyncOptinBrowserTest( - base::FieldTrialParams feature_parameters = {}) { - feature_list_.InitAndEnableFeatureWithParameters( - switches::kEnableHistorySyncOptinExpansionPill, feature_parameters); + explicit AvatarToolbarButtonSyncPromoBaseBrowserTest( + FeaturePromoTestParams params) { + switch (params.feature_promo_type) { + case FeaturePromoType::kHistorySyncPromo: + feature_list_.InitWithFeaturesAndParameters( + /*enabled_features=*/ + {{switches::kEnableHistorySyncOptinExpansionPill, + /*params=*/{{"history-sync-optin-expansion-pill-" + "option", + params.feature_parameters}}}}, + /*disabled_features=*/{switches::kAvatarButtonSyncPromoForTesting}); + break; + case FeaturePromoType::kSyncPromo: + feature_list_.InitWithFeatures( + /*enabled_features=*/{switches::kAvatarButtonSyncPromoForTesting}, + /*disabled_features=*/{ + switches::kEnableHistorySyncOptinExpansionPill}); + break; + } } private: base::test::ScopedFeatureList feature_list_; }; -IN_PROC_BROWSER_TEST_F(AvatarToolbarButtonHistorySyncOptinBrowserTest, +class AvatarToolbarButtonSyncPromoBrowserTest + : public AvatarToolbarButtonSyncPromoBaseBrowserTest, + public testing::WithParamInterface<FeaturePromoType> { + protected: + AvatarToolbarButtonSyncPromoBrowserTest() + : AvatarToolbarButtonSyncPromoBaseBrowserTest( + {.feature_promo_type = GetParam()}) {} +}; + +IN_PROC_BROWSER_TEST_P(AvatarToolbarButtonSyncPromoBrowserTest, HistorySyncOptinNotShownIfGreetingNotShown) { AvatarToolbarButton* avatar = GetAvatarToolbarButton(browser()); // Normal state. @@ -1099,7 +1151,7 @@ // TODO(crbug.com/407964657): Merge this test with // AvatarToolbarButtonBrowserTest.SyncError once the feature is enabled by // default. -IN_PROC_BROWSER_TEST_F(AvatarToolbarButtonHistorySyncOptinBrowserTest, +IN_PROC_BROWSER_TEST_P(AvatarToolbarButtonSyncPromoBrowserTest, MAYBE_HistorySyncOptinNotShownWhenSyncEnabled) { AvatarToolbarButton* avatar = GetAvatarToolbarButton(browser()); // Normal state. @@ -1131,7 +1183,7 @@ #define MAYBE_HistorySyncOptinNotShownWhenPromotionsDisabled \ HistorySyncOptinNotShownWhenPromotionsDisabled #endif -IN_PROC_BROWSER_TEST_F(AvatarToolbarButtonHistorySyncOptinBrowserTest, +IN_PROC_BROWSER_TEST_P(AvatarToolbarButtonSyncPromoBrowserTest, MAYBE_HistorySyncOptinNotShownWhenPromotionsDisabled) { TestingBrowserProcess::GetGlobal()->local_state()->SetBoolean( prefs::kPromotionsEnabled, false); @@ -1156,7 +1208,7 @@ #define MAYBE_HistorySyncOptinNotShownWhenSyncNotAllowed \ HistorySyncOptinNotShownWhenSyncNotAllowed #endif -IN_PROC_BROWSER_TEST_F(AvatarToolbarButtonHistorySyncOptinBrowserTest, +IN_PROC_BROWSER_TEST_P(AvatarToolbarButtonSyncPromoBrowserTest, MAYBE_HistorySyncOptinNotShownWhenSyncNotAllowed) { SimulateDisableSyncByPolicyWithError(); AvatarToolbarButton* avatar = GetAvatarToolbarButton(browser()); @@ -1183,8 +1235,17 @@ }; class AvatarToolbarButtonHistorySyncOptinManagedTypeTest - : public AvatarToolbarButtonHistorySyncOptinBrowserTest, - public WithParamInterface<HistorySyncOptinSyncManagedTypeTestCase> {}; + : public AvatarToolbarButtonWithInteractiveFeaturePromoBrowserTest, + public WithParamInterface<HistorySyncOptinSyncManagedTypeTestCase> { + protected: + AvatarToolbarButtonHistorySyncOptinManagedTypeTest() { + feature_list_.InitAndEnableFeature( + switches::kEnableHistorySyncOptinExpansionPill); + } + + private: + base::test::ScopedFeatureList feature_list_; +}; // TODO(crbug.com/331746545): Check the flaky test issue on Windows. #if BUILDFLAG(IS_WIN) @@ -1251,7 +1312,7 @@ #define MAYBE_HistorySyncOptinThenPassphraseError \ HistorySyncOptinThenPassphraseError #endif -IN_PROC_BROWSER_TEST_F(AvatarToolbarButtonHistorySyncOptinBrowserTest, +IN_PROC_BROWSER_TEST_P(AvatarToolbarButtonSyncPromoBrowserTest, MAYBE_HistorySyncOptinThenPassphraseError) { AvatarToolbarButton* avatar = GetAvatarToolbarButton(browser()); // Normal state. @@ -1262,8 +1323,11 @@ IDS_AVATAR_BUTTON_GREETING, account_name)); avatar->ClearActiveStateForTesting(); // The greeting should be followed by the history sync opt-in entry point. - EXPECT_EQ(avatar->GetText(), - l10n_util::GetStringUTF16(IDS_AVATAR_BUTTON_BROWSE_ACROSS_DEVICES)); + EXPECT_EQ( + avatar->GetText(), + l10n_util::GetStringUTF16(switches::IsAvatarSyncPromoFeatureEnabled() + ? IDS_AVATAR_BUTTON_SYNC_PROMO + : IDS_AVATAR_BUTTON_BROWSE_ACROSS_DEVICES)); SimulatePassphraseError(); // The history sync opt-in entry point should be replaced by the passphrase // error message. @@ -1283,7 +1347,7 @@ #define MAYBE_HistorySyncOptinThenClientUpgradeError \ HistorySyncOptinThenClientUpgradeError #endif -IN_PROC_BROWSER_TEST_F(AvatarToolbarButtonHistorySyncOptinBrowserTest, +IN_PROC_BROWSER_TEST_P(AvatarToolbarButtonSyncPromoBrowserTest, MAYBE_HistorySyncOptinThenClientUpgradeError) { AvatarToolbarButton* avatar = GetAvatarToolbarButton(browser()); // Normal state. @@ -1294,8 +1358,11 @@ IDS_AVATAR_BUTTON_GREETING, account_name)); avatar->ClearActiveStateForTesting(); // The greeting should be followed by the history sync opt-in entry point. - EXPECT_EQ(avatar->GetText(), - l10n_util::GetStringUTF16(IDS_AVATAR_BUTTON_BROWSE_ACROSS_DEVICES)); + EXPECT_EQ( + avatar->GetText(), + l10n_util::GetStringUTF16(switches::IsAvatarSyncPromoFeatureEnabled() + ? IDS_AVATAR_BUTTON_SYNC_PROMO + : IDS_AVATAR_BUTTON_BROWSE_ACROSS_DEVICES)); SimulateUpgradeClientError(); // The history sync opt-in entry point should be replaced by the passphrase // error message. @@ -1315,7 +1382,7 @@ #define MAYBE_HistorySyncOptinThenSigninPending \ HistorySyncOptinThenSigninPending #endif -IN_PROC_BROWSER_TEST_F(AvatarToolbarButtonHistorySyncOptinBrowserTest, +IN_PROC_BROWSER_TEST_P(AvatarToolbarButtonSyncPromoBrowserTest, MAYBE_HistorySyncOptinThenSigninPending) { AvatarToolbarButton* avatar = GetAvatarToolbarButton(browser()); // Normal state. @@ -1326,8 +1393,11 @@ IDS_AVATAR_BUTTON_GREETING, account_name)); avatar->ClearActiveStateForTesting(); // The greeting should be followed by the history sync opt-in entry point. - EXPECT_EQ(avatar->GetText(), - l10n_util::GetStringUTF16(IDS_AVATAR_BUTTON_BROWSE_ACROSS_DEVICES)); + EXPECT_EQ( + avatar->GetText(), + l10n_util::GetStringUTF16(switches::IsAvatarSyncPromoFeatureEnabled() + ? IDS_AVATAR_BUTTON_SYNC_PROMO + : IDS_AVATAR_BUTTON_BROWSE_ACROSS_DEVICES)); SimulateSigninError(/*web_sign_out=*/false); // The history sync opt-in entry point should be replaced by the signin // pending message. @@ -1346,7 +1416,7 @@ #else #define MAYBE_HistorySyncOptinThenExplicitText HistorySyncOptinThenExplicitText #endif -IN_PROC_BROWSER_TEST_F(AvatarToolbarButtonHistorySyncOptinBrowserTest, +IN_PROC_BROWSER_TEST_P(AvatarToolbarButtonSyncPromoBrowserTest, MAYBE_HistorySyncOptinThenExplicitText) { AvatarToolbarButton* avatar = GetAvatarToolbarButton(browser()); // Normal state. @@ -1357,8 +1427,11 @@ IDS_AVATAR_BUTTON_GREETING, account_name)); avatar->ClearActiveStateForTesting(); // The greeting should be followed by the history sync opt-in entry point. - EXPECT_EQ(avatar->GetText(), - l10n_util::GetStringUTF16(IDS_AVATAR_BUTTON_BROWSE_ACROSS_DEVICES)); + EXPECT_EQ( + avatar->GetText(), + l10n_util::GetStringUTF16(switches::IsAvatarSyncPromoFeatureEnabled() + ? IDS_AVATAR_BUTTON_SYNC_PROMO + : IDS_AVATAR_BUTTON_BROWSE_ACROSS_DEVICES)); const std::u16string explicit_text(u"Explicit Text"); base::ScopedClosureRunner hide_callback = avatar->SetExplicitButtonState( explicit_text, /*accessibility_label=*/std::nullopt, @@ -1380,8 +1453,8 @@ #define MAYBE_HistorySyncOptinNotShownIfErrorBeforeGreetingTimesOut \ HistorySyncOptinNotShownIfErrorBeforeGreetingTimesOut #endif -IN_PROC_BROWSER_TEST_F( - AvatarToolbarButtonHistorySyncOptinBrowserTest, +IN_PROC_BROWSER_TEST_P( + AvatarToolbarButtonSyncPromoBrowserTest, MAYBE_HistorySyncOptinNotShownIfErrorBeforeGreetingTimesOut) { AvatarToolbarButton* avatar = GetAvatarToolbarButton(browser()); // Normal state. @@ -1402,22 +1475,49 @@ EXPECT_TRUE(avatar->GetText().empty()); } +INSTANTIATE_TEST_SUITE_P(, + AvatarToolbarButtonSyncPromoBrowserTest, + ValuesIn({FeaturePromoType::kHistorySyncPromo, + FeaturePromoType::kSyncPromo})); + struct HistorySyncOptinExpansionPillOptionTestCase { + FeaturePromoType promo_type; std::string feature_param; int expected_history_sync_message_id; }; class AvatarToolbarButtonHistorySyncOptinWithParamBrowserTest - : public AvatarToolbarButtonHistorySyncOptinBrowserTest, + : public AvatarToolbarButtonSyncPromoBaseBrowserTest, public WithParamInterface<HistorySyncOptinExpansionPillOptionTestCase> { public: AvatarToolbarButtonHistorySyncOptinWithParamBrowserTest() - : AvatarToolbarButtonHistorySyncOptinBrowserTest(/*feature_parameters=*/ - {{"history-sync-optin-" - "expansion-pill-" - "option", - GetParam() - .feature_param}}) { + : AvatarToolbarButtonSyncPromoBaseBrowserTest( + {.feature_promo_type = GetParam().promo_type, + .feature_parameters = GetParam().feature_param}) {} + + // For History Sync promo, since it reactivates on inactivtity, we can just + // override the LastActive time. + // For the Sync promo, since it does not react to inactivity, we sign out and + // sign back in, where the user is expected to see the promo. Unloading and + // reloading the profile would have worked as well, but would have made the + // test setup more complex. + void SimulateResettingStateToAttemptShowingSyncPromo( + int shown_count, + const std::u16string& email, + const std::u16string& account_name, + AvatarToolbarButton* avatar) { + if (switches::IsAvatarSyncPromoFeatureEnabled()) { + // Signing back in should would put the profile in a state where it may + // show the promo. + Signout(); + SigninWithImage(/*email=*/u"test@gmail.com", account_name); + avatar->ClearActiveStateForTesting(); + } else { + // Simulate inactivity for enough time to trigger the new session. + RunTestSequence(SetLastActive( + shown_count * + user_education::features::GetIdleTimeBetweenSessions())); + } } }; @@ -1533,6 +1633,11 @@ IN_PROC_BROWSER_TEST_P( AvatarToolbarButtonHistorySyncOptinWithParamBrowserTest, MAYBE_HistorySyncOptinShowsAfterGreetingAndOnInactivity) { + if (GetParam().promo_type == FeaturePromoType::kSyncPromo) { + GTEST_SKIP() << "With the SyncPromo feature, the promo is not expected to " + "be shown on inactivity."; + } + base::TimeDelta last_active_time; RunTestSequence(SetLastActive(last_active_time)); AvatarToolbarButton* avatar = GetAvatarToolbarButton(browser()); @@ -1596,7 +1701,8 @@ // Normal state. ASSERT_TRUE(avatar->GetText().empty()); const std::u16string account_name_1(u"Account name"); - SigninWithImage(/*email=*/u"test@gmail.com", account_name_1); + const std::u16string email(u"test@gmail.com"); + SigninWithImage(email, account_name_1); ASSERT_EQ(avatar->GetText(), l10n_util::GetStringFUTF16( IDS_AVATAR_BUTTON_GREETING, account_name_1)); avatar->ClearActiveStateForTesting(); @@ -1610,9 +1716,8 @@ EXPECT_TRUE(avatar->GetText().empty()); for (; shown_count < user_education::features::GetNewBadgeShowCount(); ++shown_count) { - // Simulate inactivity for enough time to trigger the new session. - RunTestSequence(SetLastActive( - shown_count * user_education::features::GetIdleTimeBetweenSessions())); + SimulateResettingStateToAttemptShowingSyncPromo(shown_count, email, + account_name_1, avatar); EXPECT_EQ( avatar->GetText(), l10n_util::GetStringUTF16(GetParam().expected_history_sync_message_id)); @@ -1620,8 +1725,8 @@ // The button comes back to the normal state. EXPECT_TRUE(avatar->GetText().empty()); } - RunTestSequence(SetLastActive( - shown_count * user_education::features::GetIdleTimeBetweenSessions())); + SimulateResettingStateToAttemptShowingSyncPromo(shown_count, email, + account_name_1, avatar); // The history sync opt-in entry point should NOT be shown after the // inactivity period if the max shown count has been reached. EXPECT_TRUE(avatar->GetText().empty()); @@ -1642,20 +1747,32 @@ const HistorySyncOptinExpansionPillOptionTestCase kHistorySyncOptinTestCases[] = { { - "browse-across-devices", - IDS_AVATAR_BUTTON_BROWSE_ACROSS_DEVICES, + .promo_type = FeaturePromoType::kSyncPromo, + .expected_history_sync_message_id = IDS_AVATAR_BUTTON_SYNC_PROMO, }, { - "sync-history", - IDS_AVATAR_BUTTON_SYNC_HISTORY, + .promo_type = FeaturePromoType::kHistorySyncPromo, + .feature_param = "browse-across-devices", + .expected_history_sync_message_id = + IDS_AVATAR_BUTTON_BROWSE_ACROSS_DEVICES, }, { - "see-tabs-from-other-devices", - IDS_AVATAR_BUTTON_SEE_TABS_FROM_OTHER_DEVICES, + .promo_type = FeaturePromoType::kHistorySyncPromo, + .feature_param = "sync-history", + .expected_history_sync_message_id = IDS_AVATAR_BUTTON_SYNC_HISTORY, }, { - "browse-across-devices-new-profile-menu-promo-variant", - IDS_AVATAR_BUTTON_BROWSE_ACROSS_DEVICES, + .promo_type = FeaturePromoType::kHistorySyncPromo, + .feature_param = "see-tabs-from-other-devices", + .expected_history_sync_message_id = + IDS_AVATAR_BUTTON_SEE_TABS_FROM_OTHER_DEVICES, + }, + { + .promo_type = FeaturePromoType::kHistorySyncPromo, + .feature_param = + "browse-across-devices-new-profile-menu-promo-variant", + .expected_history_sync_message_id = + IDS_AVATAR_BUTTON_BROWSE_ACROSS_DEVICES, }, }; @@ -1766,8 +1883,8 @@ // Normal state. ASSERT_TRUE(avatar->GetText().empty()); const std::u16string account_name(u"Account name"); - const AccountInfo account_info = - SigninWithImage(/*email=*/u"test@gmail.com", account_name); + const std::u16string email(u"test@gmail.com"); + const AccountInfo account_info = SigninWithImage(email, account_name); ASSERT_EQ(avatar->GetText(), l10n_util::GetStringFUTF16( IDS_AVATAR_BUTTON_GREETING, account_name)); avatar->ClearActiveStateForTesting(); @@ -1788,9 +1905,8 @@ avatar->ClearActiveStateForTesting(); // The button comes back to the normal state. EXPECT_TRUE(avatar->GetText().empty()); - // Simulate inactivity for enough time to trigger the new session. - RunTestSequence( - SetLastActive(user_education::features::GetIdleTimeBetweenSessions())); + SimulateResettingStateToAttemptShowingSyncPromo( + /*shown_count=*/1, email, account_name, avatar); // The history sync opt-in entry point should be shown again after the // inactivity period. EXPECT_EQ( @@ -1801,11 +1917,11 @@ histogram_tester.ExpectBucketCount( "Signin.SyncOptIn.IdentityPill.Shown", signin_metrics::AccessPoint::kHistorySyncOptinExpansionPillOnStartup, - /*expected_count=*/1); + /*expected_count=*/switches::IsAvatarSyncPromoFeatureEnabled() ? 2 : 1); histogram_tester.ExpectBucketCount( "Signin.SyncOptIn.IdentityPill.Shown", signin_metrics::AccessPoint::kHistorySyncOptinExpansionPillOnInactivity, - /*expected_count=*/1); + /*expected_count=*/switches::IsAvatarSyncPromoFeatureEnabled() ? 0 : 1); // The button action should be overridden. histogram_tester.ExpectTotalCount( "Signin.SyncOptIn.IdentityPill.DurationBeforeClick", @@ -1826,8 +1942,11 @@ EXPECT_CALL( mock_signin_ui_delegate_, ShowTurnSyncOnUI(browser()->profile(), - signin_metrics::AccessPoint:: - kHistorySyncOptinExpansionPillOnInactivity, + switches::IsAvatarSyncPromoFeatureEnabled() + ? signin_metrics::AccessPoint:: + kHistorySyncOptinExpansionPillOnStartup + : signin_metrics::AccessPoint:: + kHistorySyncOptinExpansionPillOnInactivity, signin_metrics::PromoAction::PROMO_ACTION_WITH_DEFAULT, account_info.account_id, TurnSyncOnHelper::SigninAbortedMode::KEEP_ACCOUNT, @@ -1851,7 +1970,8 @@ // Normal state. ASSERT_TRUE(avatar->GetText().empty()); const std::u16string account_name_1(u"Account name"); - SigninWithImage(/*email=*/u"test@gmail.com", account_name_1); + const std::u16string email(u"test@gmail.com"); + SigninWithImage(email, account_name_1); ASSERT_EQ(avatar->GetText(), l10n_util::GetStringFUTF16( IDS_AVATAR_BUTTON_GREETING, account_name_1)); avatar->ClearActiveStateForTesting(); @@ -1866,9 +1986,8 @@ int used_count = 1; for (; used_count < user_education::features::GetNewBadgeFeatureUsedCount(); ++used_count) { - // Simulate inactivity for enough time to trigger the new session. - RunTestSequence(SetLastActive( - used_count * user_education::features::GetIdleTimeBetweenSessions())); + SimulateResettingStateToAttemptShowingSyncPromo(used_count, email, + account_name_1, avatar); EXPECT_EQ( avatar->GetText(), l10n_util::GetStringUTF16(GetParam().expected_history_sync_message_id)); @@ -1876,8 +1995,8 @@ // The button comes back to the normal state. EXPECT_TRUE(avatar->GetText().empty()); } - RunTestSequence(SetLastActive( - used_count * user_education::features::GetIdleTimeBetweenSessions())); + SimulateResettingStateToAttemptShowingSyncPromo(used_count, email, + account_name_1, avatar); // The history sync opt-in entry point should NOT be shown after the // inactivity period if the max used count has been reached. EXPECT_TRUE(avatar->GetText().empty()); @@ -1917,8 +2036,8 @@ // Normal state. ASSERT_TRUE(avatar_1->GetText().empty()); const std::u16string account_name(u"Account name"); - const AccountInfo account_info = - SigninWithImage(/*email=*/u"test@gmail.com", account_name); + const std::u16string email(u"test@gmail.com"); + const AccountInfo account_info = SigninWithImage(email, account_name); ASSERT_EQ(avatar_1->GetText(), l10n_util::GetStringFUTF16( IDS_AVATAR_BUTTON_GREETING, account_name)); avatar_1->ClearActiveStateForTesting(); @@ -1948,15 +2067,18 @@ // The button in both browsers comes back to the normal state. EXPECT_TRUE(avatar_1->GetText().empty()); EXPECT_TRUE(avatar_2->GetText().empty()); - - // Simulate inactivity for enough time to trigger the new session. - RunTestSequence( - SetLastActive(user_education::features::GetIdleTimeBetweenSessions())); + SimulateResettingStateToAttemptShowingSyncPromo( + /*shown_count=*/1, email, account_name, avatar_1); // The history sync opt-in entry point should be shown again after the // inactivity period (in both browsers). EXPECT_EQ( avatar_1->GetText(), l10n_util::GetStringUTF16(GetParam().expected_history_sync_message_id)); + if (switches::IsAvatarSyncPromoFeatureEnabled()) { + // Since the sync promo was activated on Singing back in, we also need to + // force clear the greeing on the second avatar button. + avatar_2->ClearActiveStateForTesting(); + } EXPECT_EQ( avatar_2->GetText(), l10n_util::GetStringUTF16(GetParam().expected_history_sync_message_id)); @@ -1972,11 +2094,11 @@ histogram_tester.ExpectBucketCount( "Signin.SyncOptIn.IdentityPill.Shown", signin_metrics::AccessPoint::kHistorySyncOptinExpansionPillOnStartup, - /*expected_count=*/1); + /*expected_count=*/switches::IsAvatarSyncPromoFeatureEnabled() ? 2 : 1); histogram_tester.ExpectBucketCount( "Signin.SyncOptIn.IdentityPill.Shown", signin_metrics::AccessPoint::kHistorySyncOptinExpansionPillOnInactivity, - /*expected_count=*/1); + /*expected_count=*/switches::IsAvatarSyncPromoFeatureEnabled() ? 0 : 1); // Clicking the button on any browser should collapse the history sync opt-in // in all browsers. Click(avatar_2);
diff --git a/chrome/browser/ui/views/profiles/avatar_toolbar_button_state_manager.cc b/chrome/browser/ui/views/profiles/avatar_toolbar_button_state_manager.cc index d599ee3..8fb4864 100644 --- a/chrome/browser/ui/views/profiles/avatar_toolbar_button_state_manager.cc +++ b/chrome/browser/ui/views/profiles/avatar_toolbar_button_state_manager.cc
@@ -63,6 +63,7 @@ #include "components/signin/public/base/signin_pref_names.h" #include "components/signin/public/base/signin_prefs.h" #include "components/signin/public/base/signin_switches.h" +#include "components/signin/public/identity_manager/account_info.h" #include "components/signin/public/identity_manager/identity_manager.h" #include "components/signin/public/identity_manager/primary_account_change_event.h" #include "components/sync/base/features.h" @@ -891,6 +892,14 @@ IdentityManagerFactory::GetForProfile(&profile)); } + bool ShouldProfileShowPromo() const { + if (switches::IsAvatarSyncPromoFeatureEnabled()) { + return signin_util::ShouldShowAvatarSyncPromo(&profile_.get()); + } + + return signin_util::ShouldShowHistorySyncOptinScreen(profile_.get()); + } + void Trigger(signin_metrics::AccessPoint access_point) { if (triggered_) { return; @@ -898,7 +907,7 @@ if (!sync_promo_identity_pill_manager_.ShouldShowPromo()) { return; } - if (!signin_util::ShouldShowHistorySyncOptinScreen(profile_.get())) { + if (!ShouldProfileShowPromo()) { return; } access_point_ = access_point; @@ -940,6 +949,11 @@ } void OnNewSession() { + // Do not trigger the Sync promo on activity for this feature. + if (switches::IsAvatarSyncPromoFeatureEnabled()) { + return; + } + // NOTE: All history sync opt-in triggers for enterprise badging are // considered "on inactivity" (`kHistorySyncOptinExpansionPillOnInactivity` // access point). @@ -988,6 +1002,10 @@ identity_manager_observation_{this}; }; +// With the addition of `switches::kAvatarButtonSyncPromo` feature, this +// provider may either show a SyncPromo or a HistorySyncPromo. +// SyncPromo has a higher priority, check +// `HistorySyncOptinCoordinator::ShouldProfileShowPromo()`. class HistorySyncOptinStateProvider : public StateProvider { public: explicit HistorySyncOptinStateProvider(Browser* browser, @@ -1002,6 +1020,10 @@ bool IsActive() const override { return coordinator_->triggered(); } std::u16string GetText() const override { + if (switches::IsAvatarSyncPromoFeatureEnabled()) { + return l10n_util::GetStringUTF16(IDS_AVATAR_BUTTON_SYNC_PROMO); + } + switch (switches::kHistorySyncOptinExpansionPillOption.Get()) { case switches::HistorySyncOptinExpansionPillOption::kBrowseAcrossDevices: case switches::HistorySyncOptinExpansionPillOption:: @@ -1798,7 +1820,8 @@ #if BUILDFLAG(ENABLE_DICE_SUPPORT) if (base::FeatureList::IsEnabled( - switches::kEnableHistorySyncOptinExpansionPill)) { + switches::kEnableHistorySyncOptinExpansionPill) || + switches::IsAvatarSyncPromoFeatureEnabled()) { auto history_sync_optin_state_provider = std::make_unique<HistorySyncOptinStateProvider>( browser,
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view.cc b/chrome/browser/ui/views/profiles/profile_menu_view.cc index 663c25c..709b0fd 100644 --- a/chrome/browser/ui/views/profiles/profile_menu_view.cc +++ b/chrome/browser/ui/views/profiles/profile_menu_view.cc
@@ -113,18 +113,24 @@ namespace { -std::u16string GetSyncErrorButtonText(AvatarSyncErrorType error) { +std::u16string GetSyncErrorButtonText(Profile* profile, + AvatarSyncErrorType error) { switch (error) { - case AvatarSyncErrorType::kSyncPaused: case AvatarSyncErrorType::kUnrecoverableError: + if (!ChromeSigninClientFactory::GetForProfile(profile) + ->IsClearPrimaryAccountAllowed( + IdentityManagerFactory::GetForProfile(profile) + ->HasPrimaryAccount(signin::ConsentLevel::kSync))) { + // As opposed to the corresponding error in an unmanaged account, + // sign-out hasn't happened here yet. The button directs to the sign-out + // confirmation dialog in settings. + return l10n_util::GetStringUTF16( + IDS_SYNC_ERROR_USER_MENU_SIGNOUT_BUTTON); + } + [[fallthrough]]; + case AvatarSyncErrorType::kSyncPaused: // The user was signed out. Offer them to sign in again. return l10n_util::GetStringUTF16(IDS_SYNC_ERROR_USER_MENU_SIGNIN_BUTTON); - case AvatarSyncErrorType::kManagedUserUnrecoverableError: - // As opposed to the corresponding error in an unmanaged account - // (AvatarSyncErrorType::kUnrecoverableError), sign-out hasn't happened - // here yet. The button directs to the sign-out confirmation dialog in - // settings. - return l10n_util::GetStringUTF16(IDS_SYNC_ERROR_USER_MENU_SIGNOUT_BUTTON); case AvatarSyncErrorType::kUpgradeClientError: return l10n_util::GetStringUTF16(IDS_SYNC_ERROR_USER_MENU_UPGRADE_BUTTON); case AvatarSyncErrorType::kPassphraseError: @@ -355,12 +361,18 @@ // The logic below must be consistent with GetSyncInfoForAvatarErrorType(). switch (error) { - case AvatarSyncErrorType::kManagedUserUnrecoverableError: - chrome::ShowSettingsSubPage(&browser(), chrome::kSignOutSubPage); - break; case AvatarSyncErrorType::kUnrecoverableError: { signin::IdentityManager* identity_manager = IdentityManagerFactory::GetForProfile(&profile()); + // Managed users get directed to the sign-out confirmation dialog in + // settings. + if (!ChromeSigninClientFactory::GetForProfile(&profile()) + ->IsClearPrimaryAccountAllowed( + identity_manager->HasPrimaryAccount( + signin::ConsentLevel::kSync))) { + chrome::ShowSettingsSubPage(&browser(), chrome::kSignOutSubPage); + break; + } // This error means that the Sync engine failed to initialize. Shutdown // Sync engine by revoking sync consent. identity_manager->GetPrimaryAccountMutator()->RevokeSyncConsent( @@ -676,7 +688,7 @@ identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync))) { params.subtitle = GetAvatarSyncErrorDescription(*error, primary_account_info.email); - params.button_text = GetSyncErrorButtonText(error.value()); + params.button_text = GetSyncErrorButtonText(&profile(), error.value()); params.button_action = base::BindRepeating(&ProfileMenuView::OnSyncErrorButtonClicked, base::Unretained(this), error.value());
diff --git a/chrome/browser/ui/views/session_crashed_bubble_view.cc b/chrome/browser/ui/views/session_crashed_bubble_view.cc index 2dbad56..5c20e96 100644 --- a/chrome/browser/ui/views/session_crashed_bubble_view.cc +++ b/chrome/browser/ui/views/session_crashed_bubble_view.cc
@@ -218,9 +218,15 @@ views::BubbleDialogDelegate* SessionCrashedBubbleView::ShowBubble( Browser* browser, bool offer_uma_optin) { - views::View* anchor_view = BrowserView::GetBrowserViewForBrowser(browser) - ->toolbar_button_provider() - ->GetAppMenuButton(); + BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser); + // TODO(webium): WebUI browser does not use BrowserView. Make an WebUI anchor + // for the bubble. + if (!browser_view) { + return nullptr; + } + + views::View* anchor_view = + browser_view->toolbar_button_provider()->GetAppMenuButton(); auto bubble_delegate_unique = std::make_unique<SessionCrashedBubbleDelegate>(browser->profile());
diff --git a/chrome/browser/ui/views/toolbar/BUILD.gn b/chrome/browser/ui/views/toolbar/BUILD.gn index c3f56cab..c6c9eed 100644 --- a/chrome/browser/ui/views/toolbar/BUILD.gn +++ b/chrome/browser/ui/views/toolbar/BUILD.gn
@@ -136,6 +136,7 @@ "//chrome/browser/ui/tabs:tab_strip", "//chrome/browser/ui/tabs:tab_strip_model_observer", "//chrome/browser/ui/views/download", + "//chrome/browser/ui/views/frame:toolbar_button_provider", "//chrome/browser/ui/views/page_action", "//chrome/browser/ui/views/side_panel:side_panel_enums", "//chrome/browser/ui/web_applications",
diff --git a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_browsertest.cc b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_browsertest.cc index b864691a..bafe949 100644 --- a/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_browsertest.cc +++ b/chrome/browser/ui/views/web_apps/frame_toolbar/web_app_frame_toolbar_browsertest.cc
@@ -84,6 +84,7 @@ #include "chrome/test/base/interactive_test_utils.h" #include "chrome/test/base/ui_test_utils.h" #include "chromeos/constants/chromeos_features.h" +#include "chromeos/ui/base/chromeos_ui_constants.h" #include "chromeos/ui/frame/caption_buttons/frame_caption_button_container_view.h" #include "components/infobars/content/content_infobar_manager.h" #include "components/permissions/permission_request_manager.h" @@ -1410,8 +1411,32 @@ // the ChromeOS's frame_view to have access to the caption_button_container_ so // it cannot be run on any other platform. #if BUILDFLAG(IS_CHROMEOS) -IN_PROC_BROWSER_TEST_F(WebAppFrameToolbarBrowserTest_WindowControlsOverlay, - WindowControlsOverlayFrameViewHeight) { +class WebAppFrameToolbarBrowserTest_WindowControlsOverlay_RoundedWindows + : public WebAppFrameToolbarBrowserTest_WindowControlsOverlay, + public testing::WithParamInterface<bool> { + public: + WebAppFrameToolbarBrowserTest_WindowControlsOverlay_RoundedWindows() { + if (GetParam()) { + scoped_feature_list_.InitWithFeatures( + {chromeos::features::kFeatureManagementRoundedWindows}, {}); + } else { + scoped_feature_list_.InitWithFeatures( + {}, {chromeos::features::kFeatureManagementRoundedWindows}); + } + } + + private: + base::test::ScopedFeatureList scoped_feature_list_; +}; + +INSTANTIATE_TEST_SUITE_P( + /* no prefix */, + WebAppFrameToolbarBrowserTest_WindowControlsOverlay_RoundedWindows, + ::testing::Bool()); + +IN_PROC_BROWSER_TEST_P( + WebAppFrameToolbarBrowserTest_WindowControlsOverlay_RoundedWindows, + WindowControlsOverlayFrameViewHeight) { InstallAndLaunchWebApp(); ToggleWindowControlsOverlayAndWait(); EXPECT_TRUE(GetWindowControlOverlayVisibility()); @@ -1427,10 +1452,11 @@ // Frame view minimum height also includes radius of window to ensure correct // rounding of window. See b/294588040. - int window_radius = chromeos::features::RoundedWindowsRadius(); + int bottom_window_radius = + GetParam() ? chromeos::kRoundedWindowCornerRadius : 0; - EXPECT_EQ(frame_view_height, - caption_container_height + client_view_height + window_radius); + EXPECT_EQ(frame_view_height, caption_container_height + client_view_height + + bottom_window_radius); } #endif // BUILDFLAG(IS_CHROMEOS)
diff --git a/chrome/browser/ui/views/zoom/BUILD.gn b/chrome/browser/ui/views/zoom/BUILD.gn index b7b4e11..a5497ed 100644 --- a/chrome/browser/ui/views/zoom/BUILD.gn +++ b/chrome/browser/ui/views/zoom/BUILD.gn
@@ -17,6 +17,7 @@ ":zoom", "//chrome/browser/ui", "//chrome/browser/ui:ui_features", + "//chrome/browser/ui/views/frame:toolbar_button_provider", "//chrome/browser/ui/views/page_action", "//components/zoom", ] @@ -32,6 +33,7 @@ deps = [ "//chrome/browser/ui", "//chrome/browser/ui:ui_features", + "//chrome/browser/ui/views/frame:toolbar_button_provider", "//chrome/browser/ui/views/page_action", "//chrome/browser/ui/views/toolbar", "//chrome/test:test_support_ui",
diff --git a/chrome/browser/ui/webauthn/BUILD.gn b/chrome/browser/ui/webauthn/BUILD.gn index 63ea52c..81df5d3 100644 --- a/chrome/browser/ui/webauthn/BUILD.gn +++ b/chrome/browser/ui/webauthn/BUILD.gn
@@ -72,6 +72,7 @@ "//chrome/browser/sync:factories", "//chrome/browser/ui/passwords/bubble_controllers", "//chrome/browser/ui/tabs:tab_enums", + "//chrome/browser/ui/views/frame:toolbar_button_provider", "//chrome/browser/webauthn", "//components/autofill/content/browser", "//components/autofill/core/browser",
diff --git a/chrome/browser/ui/webid/identity_dialog_controller.cc b/chrome/browser/ui/webid/identity_dialog_controller.cc index 3747cd4..f9d7debe 100644 --- a/chrome/browser/ui/webid/identity_dialog_controller.cc +++ b/chrome/browser/ui/webid/identity_dialog_controller.cc
@@ -75,6 +75,21 @@ return AccountSelectionView::GetBrandIconIdealSize(rp_mode); } +void IdentityDialogController::ShouldShowAccountsPassiveDialog( + ShouldShowAccountsPassiveDialogCallback cb) { + // If widget mode and segmentation platform feature flag is enabled, make the + // call to segmentation platform service for a UI volume recommendation. + if (base::FeatureList::IsEnabled( + segmentation_platform::features::kSegmentationPlatformFedCmUser)) { + RequestUiVolumeRecommendation( + base::BindOnce(&IdentityDialogController:: + OnRequestUiVolumeRecommendationResultReceived, + weak_ptr_factory_.GetWeakPtr(), std::move(cb))); + return; + } + std::move(cb).Run(true); +} + bool IdentityDialogController::ShowAccountsDialog( content::RelyingPartyData rp_data, const std::vector<IdentityProviderDataPtr>& identity_provider_data, @@ -102,19 +117,6 @@ rp_data.rp_icon = favicon_driver->GetFavicon(); } - // If widget mode and segmentation platform feature flag is enabled, make the - // call to segmentation platform service for a UI volume recommendation. - if (rp_mode == blink::mojom::RpMode::kPassive && - base::FeatureList::IsEnabled( - segmentation_platform::features::kSegmentationPlatformFedCmUser)) { - RequestUiVolumeRecommendation(base::BindOnce( - &IdentityDialogController:: - OnRequestUiVolumeRecommendationResultReceived, - weak_ptr_factory_.GetWeakPtr(), rp_data, identity_provider_data, - accounts, rp_mode, new_accounts)); - return true; - } - return account_view_->Show(rp_data, identity_provider_data, accounts, rp_mode, new_accounts); } @@ -398,11 +400,7 @@ } void IdentityDialogController::OnRequestUiVolumeRecommendationResultReceived( - const content::RelyingPartyData& rp_data, - const std::vector<IdentityProviderDataPtr>& identity_provider_data, - const std::vector<IdentityRequestAccountPtr>& accounts, - blink::mojom::RpMode rp_mode, - const std::vector<IdentityRequestAccountPtr>& new_accounts, + ShouldShowAccountsPassiveDialogCallback cb, const segmentation_platform::ClassificationResult& ui_volume_recommendation) { training_request_id_ = ui_volume_recommendation.request_id; @@ -411,14 +409,13 @@ if (ui_volume_recommendation.status != segmentation_platform::PredictionStatus::kSucceeded || ui_volume_recommendation.ordered_labels[0] == "FedCmUserLoud") { - account_view_->Show(rp_data, identity_provider_data, accounts, rp_mode, - new_accounts); + std::move(cb).Run(true); return; } // TODO(crbug.com/380416872): Integrate with quiet UI. Until then, dismiss the // UI. - OnDismiss(DismissReason::kSuppressed); + std::move(cb).Run(false); } void IdentityDialogController::CollectTrainingData(UserAction user_action) {
diff --git a/chrome/browser/ui/webid/identity_dialog_controller.h b/chrome/browser/ui/webid/identity_dialog_controller.h index aa163a81..e2deb2f 100644 --- a/chrome/browser/ui/webid/identity_dialog_controller.h +++ b/chrome/browser/ui/webid/identity_dialog_controller.h
@@ -64,6 +64,8 @@ int GetBrandIconIdealSize(blink::mojom::RpMode rp_mode) override; // content::IdentityRequestDialogController + void ShouldShowAccountsPassiveDialog( + ShouldShowAccountsPassiveDialogCallback cb) override; bool ShowAccountsDialog( content::RelyingPartyData rp_data, const std::vector<IdentityProviderDataPtr>& identity_provider_data, @@ -142,11 +144,7 @@ // Called when |RequestUiVolumeRecommendation| returns a result. void OnRequestUiVolumeRecommendationResultReceived( - const content::RelyingPartyData& rp_data, - const std::vector<IdentityProviderDataPtr>& identity_provider_data, - const std::vector<IdentityRequestAccountPtr>& accounts, - blink::mojom::RpMode rp_mode, - const std::vector<IdentityRequestAccountPtr>& new_accounts, + ShouldShowAccountsPassiveDialogCallback cb, const segmentation_platform::ClassificationResult& ui_volume_recommendation);
diff --git a/chrome/browser/ui/webid/identity_dialog_controller_unittest.cc b/chrome/browser/ui/webid/identity_dialog_controller_unittest.cc index 02faeb83..864da7f 100644 --- a/chrome/browser/ui/webid/identity_dialog_controller_unittest.cc +++ b/chrome/browser/ui/webid/identity_dialog_controller_unittest.cc
@@ -32,6 +32,7 @@ #include "url/origin.h" using testing::_; +using testing::SaveArg; namespace { @@ -462,15 +463,16 @@ CreateMockSegmentationPlatformService("FedCmUserLoud", run_loop), CreateMockOptimizationGuideDecider()); - // Show should be called. - std::unique_ptr<MockAccountSelectionView> account_selection_view = - std::make_unique<MockAccountSelectionView>(); - EXPECT_CALL(*account_selection_view, Show(_, _, _, _, _)).Times(1); - controller->SetAccountSelectionViewForTesting( - std::move(account_selection_view)); + base::MockCallback< + IdentityDialogController::ShouldShowAccountsPassiveDialogCallback> + should_show_callback; + bool value = false; + EXPECT_CALL(should_show_callback, Run).WillOnce(SaveArg<0>(&value)); - ShowAccountsDialog(controller.get(), blink::mojom::RpMode::kPassive); + controller->ShouldShowAccountsPassiveDialog(should_show_callback.Get()); + run_loop.Run(); + EXPECT_EQ(true, value); } TEST_F(IdentityDialogControllerTest, SegmentationPlatformDontShowUi) { @@ -486,20 +488,16 @@ CreateMockSegmentationPlatformService("FedCmUserQuiet", run_loop), CreateMockOptimizationGuideDecider()); - // Show should not be called. - std::unique_ptr<MockAccountSelectionView> account_selection_view = - std::make_unique<MockAccountSelectionView>(); - EXPECT_CALL(*account_selection_view, Show(_, _, _, _, _)).Times(0); - controller->SetAccountSelectionViewForTesting( - std::move(account_selection_view)); + base::MockCallback< + IdentityDialogController::ShouldShowAccountsPassiveDialogCallback> + should_show_callback; + bool value = true; + EXPECT_CALL(should_show_callback, Run).WillOnce(SaveArg<0>(&value)); - // Dismiss callback should be run. - base::MockCallback<DismissCallback> dismiss_callback; - EXPECT_CALL(dismiss_callback, Run).WillOnce(testing::Return()); + controller->ShouldShowAccountsPassiveDialog(should_show_callback.Get()); - ShowAccountsDialog(controller.get(), blink::mojom::RpMode::kPassive, - dismiss_callback.Get()); run_loop.Run(); + EXPECT_EQ(false, value); } TEST_F(IdentityDialogControllerTest, @@ -529,8 +527,9 @@ CollectTrainingData(_, _, _, _, _)) .Times(1); - ShowAccountsDialog(controller.get(), blink::mojom::RpMode::kPassive); + controller->ShouldShowAccountsPassiveDialog(base::DoNothing()); run_loop.Run(); + ShowAccountsDialog(controller.get(), blink::mojom::RpMode::kPassive); // User selects an account. controller->OnAccountSelected(GURL(kIdpEtldPlusOne), accounts_[0]->id, @@ -552,8 +551,9 @@ CollectTrainingData(_, _, _, _, _)) .Times(1); - ShowAccountsDialog(controller.get(), blink::mojom::RpMode::kPassive); + controller->ShouldShowAccountsPassiveDialog(base::DoNothing()); run_loop.Run(); + ShowAccountsDialog(controller.get(), blink::mojom::RpMode::kPassive); // User closes the dialog. controller->OnDismiss( @@ -575,8 +575,9 @@ CollectTrainingData(_, _, _, _, _)) .Times(1); - ShowAccountsDialog(controller.get(), blink::mojom::RpMode::kPassive); + controller->ShouldShowAccountsPassiveDialog(base::DoNothing()); run_loop.Run(); + ShowAccountsDialog(controller.get(), blink::mojom::RpMode::kPassive); // Dialog gets dismissed for other reasons. controller->OnDismiss(IdentityDialogController::DismissReason::kOther); @@ -601,7 +602,7 @@ controller->SetAccountSelectionViewForTesting( std::make_unique<MockAccountSelectionView>()); - ShowAccountsDialog(controller.get(), blink::mojom::RpMode::kPassive); + controller->ShouldShowAccountsPassiveDialog(base::DoNothing()); // Reset the controller before running // `segmentation_platform_service_callback_`. This should not crash.
diff --git a/chrome/browser/ui/webui/new_tab_page/composebox/BUILD.gn b/chrome/browser/ui/webui/new_tab_page/composebox/BUILD.gn index 4a46f80..173ae9f 100644 --- a/chrome/browser/ui/webui/new_tab_page/composebox/BUILD.gn +++ b/chrome/browser/ui/webui/new_tab_page/composebox/BUILD.gn
@@ -18,6 +18,7 @@ "//chrome/browser:browser_process", "//chrome/browser:global_features", "//chrome/browser/ui/webui/new_tab_page/composebox/variations", + "//chrome/browser/ui/webui/searchbox", "//components/application_locale_storage", "//components/omnibox/common", "//components/omnibox/composebox", @@ -33,22 +34,8 @@ sources = [ "composebox.mojom" ] webui_module_path = "/" public_deps = [ + "//components/omnibox/browser:mojo_bindings", "//components/omnibox/composebox:mojo_bindings", "//mojo/public//mojom/base", ] } - -source_set("unit_tests") { - testonly = true - sources = [ "composebox_handler_unittest.cc" ] - deps = [ - ":composebox", - "//base/test:test_support", - "//chrome/browser/search_engines", - "//chrome/browser/ui/webui/new_tab_page/composebox/variations", - "//chrome/test:test_support", - "//components/omnibox/composebox:test_support", - "//services/network:test_support", - "//services/network/public/cpp", - ] -}
diff --git a/chrome/browser/ui/webui/new_tab_page/composebox/composebox.mojom b/chrome/browser/ui/webui/new_tab_page/composebox/composebox.mojom index 0d28d7d..f8d7fa1 100644 --- a/chrome/browser/ui/webui/new_tab_page/composebox/composebox.mojom +++ b/chrome/browser/ui/webui/new_tab_page/composebox/composebox.mojom
@@ -8,6 +8,7 @@ import "mojo/public/mojom/base/time.mojom"; import "mojo/public/mojom/base/unguessable_token.mojom"; import "components/omnibox/composebox/composebox_query.mojom"; +import "components/omnibox/browser/searchbox.mojom"; // Information about a file selected by the user in the WebUI. struct SelectedFileInfo { @@ -23,7 +24,9 @@ interface PageHandlerFactory { // The component calls this method when it is first initialized. CreatePageHandler(pending_remote<Page> page, - pending_receiver<PageHandler> handler); + pending_receiver<PageHandler> handler, + pending_remote<searchbox.mojom.Page> searchbox_page, + pending_receiver<searchbox.mojom.PageHandler> searchbox_handler); }; // Browser-side handler for requests from NTP Composebox UI.
diff --git a/chrome/browser/ui/webui/new_tab_page/composebox/composebox_handler.cc b/chrome/browser/ui/webui/new_tab_page/composebox/composebox_handler.cc index df76ae0..d4e38b49 100644 --- a/chrome/browser/ui/webui/new_tab_page/composebox/composebox_handler.cc +++ b/chrome/browser/ui/webui/new_tab_page/composebox/composebox_handler.cc
@@ -4,6 +4,7 @@ #include "chrome/browser/ui/webui/new_tab_page/composebox/composebox_handler.h" +#include "base/notreached.h" #include "base/time/time.h" #include "chrome/browser/ui/webui/new_tab_page/composebox/variations/composebox_fieldtrial.h" #include "components/omnibox/composebox/composebox_image_helper.h" @@ -12,10 +13,18 @@ ComposeboxHandler::ComposeboxHandler( mojo::PendingReceiver<composebox::mojom::PageHandler> pending_handler, mojo::PendingRemote<composebox::mojom::Page> pending_page, + mojo::PendingReceiver<searchbox::mojom::PageHandler> + pending_searchbox_handler, std::unique_ptr<ComposeboxQueryController> query_controller, std::unique_ptr<ComposeboxMetricsRecorder> metrics_recorder, - content::WebContents* web_contents) - : query_controller_(std::move(query_controller)), + Profile* profile, + content::WebContents* web_contents, + MetricsReporter* metrics_reporter) + : SearchboxHandler(std::move(pending_searchbox_handler), + profile, + web_contents, + metrics_reporter), + query_controller_(std::move(query_controller)), metrics_recorder_(std::move(metrics_recorder)), web_contents_(web_contents), page_{std::move(pending_page)}, @@ -134,3 +143,27 @@ metrics_recorder_->OnFileUploadStatusChanged(mime_type, file_upload_status, error_type); } + +void ComposeboxHandler::DeleteAutocompleteMatch(uint8_t line, const GURL& url) { + NOTREACHED(); +} + +void ComposeboxHandler::ExecuteAction(uint8_t line, + uint8_t action_index, + const GURL& url, + base::TimeTicks match_selection_timestamp, + uint8_t mouse_button, + bool alt_key, + bool ctrl_key, + bool meta_key, + bool shift_key) { + NOTREACHED(); +} + +void ComposeboxHandler::PopupElementSizeChanged(const gfx::Size& size) { + NOTREACHED(); +} + +void ComposeboxHandler::OnThumbnailRemoved() { + NOTREACHED(); +}
diff --git a/chrome/browser/ui/webui/new_tab_page/composebox/composebox_handler.h b/chrome/browser/ui/webui/new_tab_page/composebox/composebox_handler.h index db16cac..5577b65 100644 --- a/chrome/browser/ui/webui/new_tab_page/composebox/composebox_handler.h +++ b/chrome/browser/ui/webui/new_tab_page/composebox/composebox_handler.h
@@ -11,6 +11,8 @@ #include "base/memory/raw_ptr.h" #include "base/unguessable_token.h" #include "chrome/browser/ui/webui/new_tab_page/composebox/composebox.mojom.h" +#include "chrome/browser/ui/webui/searchbox/searchbox_handler.h" +#include "components/omnibox/browser/searchbox.mojom.h" #include "components/omnibox/composebox/composebox_metrics_recorder.h" #include "components/omnibox/composebox/composebox_query.mojom.h" #include "components/omnibox/composebox/composebox_query_controller.h" @@ -21,16 +23,24 @@ #include "ui/base/window_open_disposition_utils.h" #include "url/gurl.h" +class MetricsReporter; +class Profile; + class ComposeboxHandler : public composebox::mojom::PageHandler, - public ComposeboxQueryController::FileUploadStatusObserver { + public ComposeboxQueryController::FileUploadStatusObserver, + public SearchboxHandler { public: explicit ComposeboxHandler( - mojo::PendingReceiver<composebox::mojom::PageHandler> handler, + mojo::PendingReceiver<composebox::mojom::PageHandler> pending_handler, mojo::PendingRemote<composebox::mojom::Page> pending_page, + mojo::PendingReceiver<searchbox::mojom::PageHandler> + pending_searchbox_handler, std::unique_ptr<ComposeboxQueryController> query_controller, std::unique_ptr<ComposeboxMetricsRecorder> metrics_recorder, - content::WebContents* web_contents); + Profile* profile, + content::WebContents* web_contents, + MetricsReporter* metrics_reporter); ~ComposeboxHandler() override; // composebox::mojom::PageHandler: @@ -55,6 +65,20 @@ composebox_query::mojom::FileUploadStatus file_upload_status, const std::optional<FileUploadErrorType>& error_type) override; + // searchbox::mojom::PageHandler: + void DeleteAutocompleteMatch(uint8_t line, const GURL& url) override; + void ExecuteAction(uint8_t line, + uint8_t action_index, + const GURL& url, + base::TimeTicks match_selection_timestamp, + uint8_t mouse_button, + bool alt_key, + bool ctrl_key, + bool meta_key, + bool shift_key) override; + void PopupElementSizeChanged(const gfx::Size& size) override; + void OnThumbnailRemoved() override; + private: void OpenUrl(GURL url, const WindowOpenDisposition disposition);
diff --git a/chrome/browser/ui/webui/new_tab_page/composebox/composebox_handler_unittest.cc b/chrome/browser/ui/webui/new_tab_page/composebox/composebox_handler_unittest.cc index 3f86af9..ecfa58d 100644 --- a/chrome/browser/ui/webui/new_tab_page/composebox/composebox_handler_unittest.cc +++ b/chrome/browser/ui/webui/new_tab_page/composebox/composebox_handler_unittest.cc
@@ -22,6 +22,7 @@ #include "chrome/browser/search_engines/template_url_service_factory.h" #include "chrome/browser/ui/webui/new_tab_page/composebox/composebox.mojom.h" #include "chrome/browser/ui/webui/new_tab_page/composebox/variations/composebox_fieldtrial.h" +#include "chrome/browser/ui/webui/searchbox/searchbox_test_utils.h" #include "chrome/test/base/chrome_render_view_host_test_harness.h" #include "chrome/test/base/testing_profile.h" #include "components/omnibox/composebox/composebox_query.mojom.h" @@ -172,9 +173,12 @@ metrics_recorder_ = metrics_recorder_ptr.get(); handler_ = std::make_unique<ComposeboxHandler>( mojo::PendingReceiver<composebox::mojom::PageHandler>(), - mock_page_.BindAndGetRemote(), std::move(query_controller_ptr), - std::move(metrics_recorder_ptr), web_contents()); + mock_page_.BindAndGetRemote(), + mojo::PendingReceiver<searchbox::mojom::PageHandler>(), + std::move(query_controller_ptr), std::move(metrics_recorder_ptr), + profile(), web_contents(), /*metrics_reporter=*/nullptr); + handler_->SetPage(mock_searchbox_page_.BindAndGetRemote()); // Set all the feature params here to keep the test consistent if future // default values are changed. scoped_config_.Get().enabled = true; @@ -242,6 +246,7 @@ protected: testing::NiceMock<MockPage> mock_page_; + testing::NiceMock<MockSearchboxPage> mock_searchbox_page_; private: ntp_composebox::ScopedFeatureConfigForTesting scoped_config_;
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc index bdd3821c..e7f0079d 100644 --- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc +++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.cc
@@ -936,11 +936,16 @@ void NewTabPageUI::CreatePageHandler( mojo::PendingRemote<composebox::mojom::Page> pending_page, - mojo::PendingReceiver<composebox::mojom::PageHandler> - pending_page_handler) { + mojo::PendingReceiver<composebox::mojom::PageHandler> pending_page_handler, + mojo::PendingRemote<searchbox::mojom::Page> pending_searchbox_page, + mojo::PendingReceiver<searchbox::mojom::PageHandler> + pending_searchbox_handler) { DCHECK(pending_page.is_valid()); + MetricsReporterService* service = + MetricsReporterService::GetFromWebContents(web_ui()->GetWebContents()); composebox_handler_ = std::make_unique<ComposeboxHandler>( std::move(pending_page_handler), std::move(pending_page), + std::move(pending_searchbox_handler), std::make_unique<ComposeboxQueryController>( IdentityManagerFactory::GetForProfile(profile_), g_browser_process->shared_url_loader_factory(), chrome::GetChannel(), @@ -948,8 +953,11 @@ TemplateURLServiceFactory::GetForProfile(profile_), profile_->GetVariationsClient(), ntp_composebox::kSendLnsSurfaceParam.Get()), - std::make_unique<ComposeboxMetricsRecorder>("NewTabPage."), - web_contents()); + std::make_unique<ComposeboxMetricsRecorder>("NewTabPage."), profile_, + web_contents(), service->metrics_reporter()); + + // TODO(crbug.com/435288212): Move searchbox mojom to use factory pattern. + composebox_handler_->SetPage(std::move(pending_searchbox_page)); } void NewTabPageUI::CreateHelpBubbleHandler(
diff --git a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h index 6417eb9..b2e30916 100644 --- a/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h +++ b/chrome/browser/ui/webui/new_tab_page/new_tab_page_ui.h
@@ -34,6 +34,7 @@ #include "chrome/browser/themes/theme_service_observer.h" #include "chrome/browser/ui/webui/customize_buttons/customize_buttons.mojom.h" #include "chrome/browser/ui/webui/new_tab_page/composebox/composebox.mojom.h" +#include "chrome/browser/ui/webui/new_tab_page/composebox/composebox_handler.h" #include "chrome/browser/ui/webui/new_tab_page/new_tab_page.mojom.h" #include "chrome/common/webui_url_constants.h" #include "components/omnibox/browser/searchbox.mojom-forward.h" @@ -271,7 +272,10 @@ void CreatePageHandler( mojo::PendingRemote<composebox::mojom::Page> pending_page, mojo::PendingReceiver<composebox::mojom::PageHandler> - pending_page_handler) override; + pending_page_handler, + mojo::PendingRemote<searchbox::mojom::Page> pending_searchbox_page, + mojo::PendingReceiver<searchbox::mojom::PageHandler> + pending_searchbox_handler) override; // help_bubble::mojom::HelpBubbleHandlerFactory: void CreateHelpBubbleHandler( @@ -317,7 +321,7 @@ std::unique_ptr<MostVisitedHandler> most_visited_page_handler_; mojo::Receiver<most_visited::mojom::MostVisitedPageHandlerFactory> most_visited_page_factory_receiver_; - std::unique_ptr<composebox::mojom::PageHandler> composebox_handler_; + std::unique_ptr<ComposeboxHandler> composebox_handler_; mojo::Receiver<composebox::mojom::PageHandlerFactory> composebox_page_factory_receiver_; std::unique_ptr<BrowserCommandHandler> promo_browser_command_handler_;
diff --git a/chrome/browser/ui/webui/settings/people_handler.cc b/chrome/browser/ui/webui/settings/people_handler.cc index 9917140c..f9184b2 100644 --- a/chrome/browser/ui/webui/settings/people_handler.cc +++ b/chrome/browser/ui/webui/settings/people_handler.cc
@@ -1207,7 +1207,8 @@ if (error.has_value() && (error.value() != AvatarSyncErrorType::kSyncPaused || identity_manager->HasPrimaryAccount(signin::ConsentLevel::kSync))) { - status_labels = GetAvatarSyncErrorLabelsForSettings(error.value()); + status_labels = + GetAvatarSyncErrorLabelsForSettings(profile_, error.value()); } else { status_labels = GetSyncStatusLabelsForSettings( SyncServiceFactory::GetForProfile(profile_));
diff --git a/chrome/browser/webauthn/enclave_authenticator_browsertest.cc b/chrome/browser/webauthn/enclave_authenticator_browsertest.cc index bf2e825..b6cb868 100644 --- a/chrome/browser/webauthn/enclave_authenticator_browsertest.cc +++ b/chrome/browser/webauthn/enclave_authenticator_browsertest.cc
@@ -60,6 +60,7 @@ #include "chrome/grit/generated_resources.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h" +#include "components/device_event_log/device_event_log.h" #include "components/password_manager/core/browser/password_form.h" #include "components/password_manager/core/browser/password_store/password_store_interface.h" #include "components/password_manager/core/common/password_manager_pref_names.h" @@ -604,6 +605,13 @@ } } +std::string GetDeviceLog() { + return device_event_log::GetAsString( + device_event_log::NEWEST_FIRST, /*format=*/"level", + /*types=*/"fido", + /*max_level=*/device_event_log::LOG_LEVEL_EVENT, /*max_events=*/0); +} + bool IsMechanismEnclaveCredential( const AuthenticatorRequestDialogModel::Mechanism& mechanism) { if (std::holds_alternative< @@ -1013,6 +1021,9 @@ std::string script_result; ASSERT_TRUE(message_queue.WaitForMessage(&script_result)); EXPECT_EQ(script_result, "\"webauthn: OK\""); + + // Ensure the security domain secret is redacted from logs. + EXPECT_THAT(GetDeviceLog(), testing::HasSubstr("\"secret\": \"[redacted]\"")); } IN_PROC_BROWSER_TEST_F(EnclaveAuthenticatorBrowserTest, NonWebauthnRequest) { @@ -1115,6 +1126,9 @@ EXPECT_TRUE(enabled); EXPECT_EQ(first, "none"); EXPECT_EQ(second, "none"); + + // Ensure the PRF is redacted from logs. + EXPECT_THAT(GetDeviceLog(), testing::HasSubstr("\"prf\": \"[redacted]\"")); } IN_PROC_BROWSER_TEST_F(EnclaveAuthenticatorBrowserTest, GetAssertionWithPrf) { @@ -4316,6 +4330,10 @@ histogram_tester.ExpectBucketCount( "WebAuthentication.GPM.GetAssertion.LargeBlobSucceeded.Read", /*sample=*/true, /*expected_count=*/1); + + // Ensure the large blob is redacted from logs. + EXPECT_THAT(GetDeviceLog(), + testing::HasSubstr("\"largeBlob\": \"[redacted]\"")); } // Disable large blob for GPM feature flag.
diff --git a/chrome/build/android-arm64.pgo.txt b/chrome/build/android-arm64.pgo.txt index 1c7fa595..21908ed2 100644 --- a/chrome/build/android-arm64.pgo.txt +++ b/chrome/build/android-arm64.pgo.txt
@@ -1 +1 @@ -chrome-android64-main-1753967455-be9c9760411dfc3d4c6976b8fb574ae13d918038-c37fb944c0fb279b5e786ac8928aee6c40091e42.profdata +chrome-android64-main-1753973788-7db82161dfec2d907d39857c36d723867fcfe6c8-afe5dc16ead263366ceb21b058108bc68f136fb5.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt index 5b27369f..143775c1 100644 --- a/chrome/build/mac-arm.pgo.txt +++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@ -chrome-mac-arm-main-1753963144-d9303566806c341cb4f1082a15d49a979da92378-b9a9162f6894732631419bbbefb8aab83c8d7b09.profdata +chrome-mac-arm-main-1753970342-bbb8b0680ac026c6b127cce2366b047dc63bb84a-7c032cc8481334090bfff54dc49a7ede92881074.profdata
diff --git a/chrome/build/win-arm64.pgo.txt b/chrome/build/win-arm64.pgo.txt index 7a162eb..365c0e6f 100644 --- a/chrome/build/win-arm64.pgo.txt +++ b/chrome/build/win-arm64.pgo.txt
@@ -1 +1 @@ -chrome-win-arm64-main-1753768535-374b090ad1f93de889070329e94fc1fa1c2659c0-6520cd593d02c2f408281c056adc3338a55ddd6a.profdata +chrome-win-arm64-main-1753963144-9ed34f1746056a57c6817b5669919a28f72b2391-b9a9162f6894732631419bbbefb8aab83c8d7b09.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt index f2901c7..27d7f09 100644 --- a/chrome/build/win32.pgo.txt +++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@ -chrome-win32-main-1753952371-0ccdb251cc5c17f7a2c15fbcf9e0474d9c56e7bb-d1c819d6c21826e577fd4f967533e47476f9301e.profdata +chrome-win32-main-1753963144-61a943c402b72d8d202b35dfd28177003134806b-b9a9162f6894732631419bbbefb8aab83c8d7b09.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt index bc8ecf2..127306e 100644 --- a/chrome/build/win64.pgo.txt +++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@ -chrome-win64-main-1753941130-5d4f4031488d1220c875f87ec9cd3ee74cefd50b-d35efb71121894cb4164ece1695b956c6eb6ba57.profdata +chrome-win64-main-1753952371-58ca9e21a99ff7fd186d79a8e40af6a08f677980-d1c819d6c21826e577fd4f967533e47476f9301e.profdata
diff --git a/chrome/common/extensions/api/autofill_private.idl b/chrome/common/extensions/api/autofill_private.idl index 5e200638..3eaf103 100644 --- a/chrome/common/extensions/api/autofill_private.idl +++ b/chrome/common/extensions/api/autofill_private.idl
@@ -149,6 +149,8 @@ NATIONAL_ID_CARD_EXPIRATION_DATE, NATIONAL_ID_CARD_ISSUE_DATE, NATIONAL_ID_CARD_ISSUING_COUNTRY, + KNOWN_TRAVELER_NUMBER, + KNOWN_TRAVELER_NUMBER_EXPIRATION_DATE, ADDRESS_HOME_ZIP_PREFIX, ADDRESS_HOME_ZIP_SUFFIX, MAX_VALID_FIELD_TYPE
diff --git a/chrome/common/safe_browsing/mach_o_image_reader_mac.cc b/chrome/common/safe_browsing/mach_o_image_reader_mac.cc index 528fc36a..a0f461d 100644 --- a/chrome/common/safe_browsing/mach_o_image_reader_mac.cc +++ b/chrome/common/safe_browsing/mach_o_image_reader_mac.cc
@@ -36,7 +36,7 @@ ByteSlice Slice(size_t at, size_t size) { if (!RangeCheck(at, size)) return ByteSlice(); - return ByteSlice(data_ + at, size); + return ByteSlice(UNSAFE_TODO(data_ + at), size); } // Casts an offset to a specific type.
diff --git a/chrome/enterprise_companion/installer_win.cc b/chrome/enterprise_companion/installer_win.cc index 9b38e3eb..a654aca6 100644 --- a/chrome/enterprise_companion/installer_win.cc +++ b/chrome/enterprise_companion/installer_win.cc
@@ -51,6 +51,7 @@ // if it does not exist. if (base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_WRITE | base::File::FLAG_WIN_EXCLUSIVE_WRITE | + base::File::FLAG_WIN_EXCLUSIVE_READ | base::File::FLAG_WIN_SHARE_DELETE); file.IsValid() || file.error_details() == base::File::FILE_ERROR_NOT_FOUND) {
diff --git a/chrome/services/speech/speech_recognition_recognizer_impl.cc b/chrome/services/speech/speech_recognition_recognizer_impl.cc index af16e5c..8c60f9a1 100644 --- a/chrome/services/speech/speech_recognition_recognizer_impl.cc +++ b/chrome/services/speech/speech_recognition_recognizer_impl.cc
@@ -81,22 +81,16 @@ const soda::chrome::SodaRecognitionResult& result = response.recognition_result(); - const bool is_final = - result.result_type() == soda::chrome::SodaRecognitionResult::FINAL; + auto speech_recognition_result = media::SpeechRecognitionResult( + result.hypothesis(0), + result.result_type() == soda::chrome::SodaRecognitionResult::FINAL); - auto speech_recognition_result = - media::SpeechRecognitionResult(result.hypothesis(0), is_final); - - // TODO(crbug.com/413823334): Check if we can add `TimingInformation` to non - // final `SpeechRecognitionResults`, if this proves to be useful downstream. - if (is_final && result.has_timing_metrics()) { - const auto& timing_metrics = result.timing_metrics(); - + if (result.has_timing_metrics()) { speech_recognition_result.timing_information = media::TimingInformation(); speech_recognition_result.timing_information->audio_start_time = - base::Microseconds(timing_metrics.audio_start_time_usec()); + base::Microseconds(result.timing_metrics().audio_start_time_usec()); speech_recognition_result.timing_information->audio_end_time = - base::Microseconds(timing_metrics.event_end_time_usec()); + base::Microseconds(result.timing_metrics().event_end_time_usec()); } DCHECK(result.hypothesis_size()); @@ -213,7 +207,7 @@ if (!client_remote_.is_bound()) return; - if (event.is_final && event.timing_information.has_value()) { + if (event.timing_information.has_value()) { using SpeechTimestamp = SpeechTimestampEstimator::SpeechTimestamp; auto& timing_info = event.timing_information.value(); @@ -231,9 +225,16 @@ // // Correctly handling this scenario would add a fair bit more complexity, // which might not be warranted at this time. - auto media_timestamps = timestamp_estimator_->TakeTimestampsInRange( - SpeechTimestamp(timing_info.audio_start_time), - SpeechTimestamp(timing_info.audio_end_time)); + std::vector<media::MediaTimestampRange> media_timestamps; + if (event.is_final) { + media_timestamps = timestamp_estimator_->TakeTimestampsInRange( + SpeechTimestamp(timing_info.audio_start_time), + SpeechTimestamp(timing_info.audio_end_time)); + } else { + media_timestamps = timestamp_estimator_->PeekTimestampsInRange( + SpeechTimestamp(timing_info.audio_start_time), + SpeechTimestamp(timing_info.audio_end_time)); + } timing_info.originating_media_timestamps = std::move(media_timestamps); }
diff --git a/chrome/services/speech/speech_recognition_recognizer_impl.h b/chrome/services/speech/speech_recognition_recognizer_impl.h index 0110961..c5bd59e0 100644 --- a/chrome/services/speech/speech_recognition_recognizer_impl.h +++ b/chrome/services/speech/speech_recognition_recognizer_impl.h
@@ -143,7 +143,6 @@ media::mojom::SpeechRecognitionOptionsPtr options_; - protected: bool mask_offensive_words() { return mask_offensive_words_; } private:
diff --git a/chrome/services/speech/speech_recognition_recognizer_impl_unittest.cc b/chrome/services/speech/speech_recognition_recognizer_impl_unittest.cc index a511803c3..5fff8c4 100644 --- a/chrome/services/speech/speech_recognition_recognizer_impl_unittest.cc +++ b/chrome/services/speech/speech_recognition_recognizer_impl_unittest.cc
@@ -34,20 +34,26 @@ // media::mojom::SpeechRecognitionRecognizerClient: void OnSpeechRecognitionRecognitionEvent( const media::SpeechRecognitionResult& result, - OnSpeechRecognitionRecognitionEventCallback reply) override {} + OnSpeechRecognitionRecognitionEventCallback reply) override { + last_received_result_ = result; + std::move(reply).Run(true); + if (run_loop_) { + run_loop_->Quit(); + } + } void OnSpeechRecognitionStopped() override {} void OnSpeechRecognitionError() override {} void OnLanguageIdentificationEvent( media::mojom::LanguageIdentificationEventPtr event) override {} - void SetUp() override {} - - std::unique_ptr<SpeechRecognitionRecognizerImpl> CreateRecognizer( - media::mojom::SpeechRecognitionOptionsPtr options) { - return std::make_unique<SpeechRecognitionRecognizerImpl>( + void CreateRecognizer(media::mojom::SpeechRecognitionOptionsPtr options) { + recognizer_ = std::make_unique<SpeechRecognitionRecognizerImpl>( receiver_.BindNewPipeAndPassRemote(), std::move(options), base::FilePath(), config_paths_, kPrimaryLanguageName, /*mask_offensive_words=*/true); + auto soda_client = std::make_unique<NiceMock<::soda::MockSodaClient>>(); + soda_client_ = soda_client.get(); + recognizer_->SetSodaClientForTesting(std::move(soda_client)); } media::mojom::SpeechRecognitionOptionsPtr CreateOptions( @@ -59,7 +65,7 @@ options->enable_formatting = false; options->recognizer_client_type = media::mojom::RecognizerClientType::kLiveCaption; - options->skip_continuously_empty_audio = true; + options->skip_continuously_empty_audio = false; if (recognition_context) { options->recognition_context = std::move(*recognition_context); } @@ -70,23 +76,43 @@ return config_paths_; } - private: + protected: + void WaitForRecognitionEvent() { + run_loop_ = std::make_unique<base::RunLoop>(); + run_loop_->Run(); + run_loop_.reset(); + } + + // Sends a dummy audio buffer to the recognizer to populate the timestamp + // estimator. + void SendAudio(SpeechRecognitionRecognizerImpl* recognizer, + base::TimeDelta duration, + base::TimeDelta media_start_pts) { + auto audio_buffer = media::mojom::AudioDataS16::New(); + audio_buffer->sample_rate = 16000; + audio_buffer->channel_count = 1; + audio_buffer->frame_count = duration.InSeconds() * 16000; + audio_buffer->data.resize(audio_buffer->frame_count, 0); + recognizer->SendAudioToSpeechRecognitionService(std::move(audio_buffer), + media_start_pts); + } + + base::test::SingleThreadTaskEnvironment task_environment_; mojo::Receiver<media::mojom::SpeechRecognitionRecognizerClient> receiver_{ this}; base::flat_map<std::string, base::FilePath> config_paths_; - base::test::SingleThreadTaskEnvironment task_environment_; + media::SpeechRecognitionResult last_received_result_; + std::unique_ptr<base::RunLoop> run_loop_; + std::unique_ptr<SpeechRecognitionRecognizerImpl> recognizer_; + raw_ptr<NiceMock<::soda::MockSodaClient>> soda_client_ = nullptr; }; TEST_F(SpeechRecognitionRecognizerImplTest, OnLanguagePackInstalledTest) { - auto recognizer = CreateRecognizer(CreateOptions()); - auto soda_client = std::make_unique<NiceMock<::soda::MockSodaClient>>(); - auto* soda_client_ptr = soda_client.get(); - recognizer->SetSodaClientForTesting(std::move(soda_client)); + CreateRecognizer(CreateOptions()); + EXPECT_CALL(*soda_client_, Reset(_, _, _)); + recognizer_->OnLanguagePackInstalled(config_paths()); - EXPECT_CALL(*soda_client_ptr, Reset(_, _, _)); - recognizer->OnLanguagePackInstalled(config_paths()); - - auto* config = recognizer->GetExtendedSodaConfigMsgForTesting(); + auto* config = recognizer_->GetExtendedSodaConfigMsgForTesting(); EXPECT_EQ(soda::chrome::ExtendedSodaConfigMsg::CAPTION, config->recognition_mode()); EXPECT_FALSE(config->enable_formatting()); @@ -98,14 +124,12 @@ std::vector<media::SpeechRecognitionPhrase> phrases; phrases.emplace_back("test phrase", 2.0); - auto recognizer = CreateRecognizer( + CreateRecognizer( CreateOptions(media::SpeechRecognitionRecognitionContext(phrases))); - recognizer->SetSodaClientForTesting( - std::make_unique<NiceMock<::soda::MockSodaClient>>()); - recognizer->OnLanguagePackInstalled(config_paths()); + recognizer_->OnLanguagePackInstalled(config_paths()); auto context = - recognizer->GetExtendedSodaConfigMsgForTesting()->recognition_context(); + recognizer_->GetExtendedSodaConfigMsgForTesting()->recognition_context(); EXPECT_EQ(1, context.context().size()); auto context_input = context.context().Get(0); EXPECT_EQ("android-speech-api-generic-phrases", context_input.name()); @@ -116,15 +140,94 @@ } TEST_F(SpeechRecognitionRecognizerImplTest, UpdateRecognitionContextTest) { - auto recognizer = CreateRecognizer(CreateOptions()); - auto soda_client = std::make_unique<NiceMock<::soda::MockSodaClient>>(); - auto* soda_client_ptr = soda_client.get(); - recognizer->SetSodaClientForTesting(std::move(soda_client)); - + CreateRecognizer(CreateOptions()); media::SpeechRecognitionRecognitionContext context; context.phrases.emplace_back("test phrase", 2.0); - EXPECT_CALL(*soda_client_ptr, UpdateRecognitionContext(_)); - recognizer->UpdateRecognitionContext(context); + EXPECT_CALL(*soda_client_, UpdateRecognitionContext(_)); + recognizer_->UpdateRecognitionContext(context); +} + +TEST_F(SpeechRecognitionRecognizerImplTest, + PopulatesTimestampsForFinalResults) { + CreateRecognizer(CreateOptions()); + + // 1. Populate the timestamp estimator. + // Audio from media time [10s, 12s) corresponds to speech time [0s, 2s). + SendAudio(recognizer_.get(), base::Seconds(2), base::Seconds(10)); + + // 2. Create a final recognition result for speech time [0s, 1.5s). + media::SpeechRecognitionResult result; + result.transcription = "hello world"; + result.is_final = true; + result.timing_information = media::TimingInformation(); + result.timing_information->audio_start_time = base::Seconds(0); + result.timing_information->audio_end_time = base::Milliseconds(1500); + + // 3. Trigger the event handler to receive the result. + recognizer_->recognition_event_callback().Run(std::move(result)); + WaitForRecognitionEvent(); + + // 4. Verify the timestamps on the received result. + ASSERT_TRUE(last_received_result_.timing_information.has_value()); + const auto& timestamps = + last_received_result_.timing_information->originating_media_timestamps; + ASSERT_TRUE(timestamps.has_value()); + ASSERT_EQ(timestamps->size(), 1u); + // Should correspond to media time [10s, 11.5s). + EXPECT_EQ((*timestamps)[0].start, base::Seconds(10)); + EXPECT_EQ((*timestamps)[0].end, base::Seconds(10) + base::Milliseconds(1500)); +} + +TEST_F(SpeechRecognitionRecognizerImplTest, + PopulatesTimestampsForNonFinalResults) { + CreateRecognizer(CreateOptions()); + + // 1. Populate the timestamp estimator. + // Audio from media time [20s, 25s) corresponds to speech time [0s, 5s). + SendAudio(recognizer_.get(), base::Seconds(5), base::Seconds(20)); + + // 2. Create a non-final recognition result for speech time [1s, 3s). + media::SpeechRecognitionResult result; + result.transcription = "testing"; + result.is_final = false; + result.timing_information = media::TimingInformation(); + result.timing_information->audio_start_time = base::Seconds(1); + result.timing_information->audio_end_time = base::Seconds(3); + + // 3. Trigger the event handler to receive the result. + recognizer_->recognition_event_callback().Run(std::move(result)); + WaitForRecognitionEvent(); + + // 4. Verify the timestamps on the received result. + ASSERT_TRUE(last_received_result_.timing_information.has_value()); + const auto& timestamps = + last_received_result_.timing_information->originating_media_timestamps; + ASSERT_TRUE(timestamps.has_value()); + ASSERT_EQ(timestamps->size(), 1u); + // Should correspond to media time [21s, 23s). + EXPECT_EQ((*timestamps)[0].start, base::Seconds(21)); + EXPECT_EQ((*timestamps)[0].end, base::Seconds(23)); + + // 5. Verify the estimator's state was NOT changed. + // We can do this by sending a final result for the full range [0s, 5s) + // and checking that it returns the full media range [20s, 25s). + // If Peek had mutated the state, this would fail. + media::SpeechRecognitionResult final_result; + final_result.is_final = true; + final_result.timing_information = media::TimingInformation(); + final_result.timing_information->audio_start_time = base::Seconds(0); + final_result.timing_information->audio_end_time = base::Seconds(5); + + recognizer_->recognition_event_callback().Run(std::move(final_result)); + WaitForRecognitionEvent(); + + ASSERT_TRUE(last_received_result_.timing_information.has_value()); + const auto& final_timestamps = + last_received_result_.timing_information->originating_media_timestamps; + ASSERT_TRUE(final_timestamps.has_value()); + ASSERT_EQ(final_timestamps->size(), 1u); + EXPECT_EQ((*final_timestamps)[0].start, base::Seconds(20)); + EXPECT_EQ((*final_timestamps)[0].end, base::Seconds(25)); } } // namespace speech
diff --git a/chrome/services/speech/speech_timestamp_estimator.cc b/chrome/services/speech/speech_timestamp_estimator.cc index e0cba85..c893c4b 100644 --- a/chrome/services/speech/speech_timestamp_estimator.cc +++ b/chrome/services/speech/speech_timestamp_estimator.cc
@@ -122,59 +122,62 @@ running_silence_duration_.reset(); } -void SpeechTimestampEstimator::PopFrontUntil(SpeechTimestamp end_timestamp) { - CHECK(!playback_chunks_.empty()); +void SpeechTimestampEstimator::PopFrontUntil( + base::circular_deque<PlaybackChunk>& chunks, + SpeechTimestamp end_timestamp) { + CHECK(!chunks.empty()); CHECK_LE(end_timestamp, current_speech_time_); - CHECK_EQ(playback_chunks_.back().SpeechEnd(), current_speech_time_); + CHECK_EQ(chunks.back().SpeechEnd(), current_speech_time_); // Remove all chunks that have ended before `end_timestamp`. - while (playback_chunks_.front().SpeechEnd() < end_timestamp) { - playback_chunks_.pop_front(); + while (chunks.front().SpeechEnd() < end_timestamp) { + chunks.pop_front(); } // We should always have a leftover chunk, even if `end_timestamp` is equal to // `current_speech_time_`. - CHECK(!playback_chunks_.empty()); + CHECK(!chunks.empty()); // Partially discard the front chunk until `end_timestamp` (exclusively), // if it starts before `end_timestamp`. - PlaybackChunk& front_chunk = playback_chunks_.front(); + PlaybackChunk& front_chunk = chunks.front(); if (front_chunk.speech_start < end_timestamp) { PlaybackDuration duration = CalculateDuration(front_chunk.speech_start, end_timestamp); - front_chunk.TrimStart(duration); } // We should always have a leftover chunk, potentially with 0 duration, since // we popped [0, `end_timestamp`), not [0, `end_timestamp`]. - CHECK(!playback_chunks_.empty()); + CHECK(!chunks.empty()); } -std::vector<PlaybackChunk> SpeechTimestampEstimator::TakeFrontUntil( +std::vector<SpeechTimestampEstimator::PlaybackChunk> +SpeechTimestampEstimator::TakeFrontUntil( + base::circular_deque<PlaybackChunk>& chunks, SpeechTimestamp end_timestamp) { - CHECK(!playback_chunks_.empty()); + CHECK(!chunks.empty()); CHECK_LE(end_timestamp, current_speech_time_); - CHECK_EQ(playback_chunks_.back().SpeechEnd(), current_speech_time_); + CHECK_EQ(chunks.back().SpeechEnd(), current_speech_time_); std::vector<PlaybackChunk> results; // Take all complete chunks before `end_timestamp`. - while (playback_chunks_.front().SpeechEnd() < end_timestamp) { + while (chunks.front().SpeechEnd() < end_timestamp) { // Don't save chunks with no duration. - if (!playback_chunks_.front().playback_duration->is_zero()) { - results.push_back(std::move(playback_chunks_.front())); + if (!chunks.front().playback_duration->is_zero()) { + results.push_back(std::move(chunks.front())); } - playback_chunks_.pop_front(); + chunks.pop_front(); } // We should always have a leftover chunk, even if `end_timestamp` is equal to // `current_speech_time_`. - CHECK(!playback_chunks_.empty()); + CHECK(!chunks.empty()); // Split the front chunk at `end_timestamp`, and save the results before // `end_timestamp`. - PlaybackChunk& front_chunk = playback_chunks_.front(); + PlaybackChunk& front_chunk = chunks.front(); if (front_chunk.speech_start < end_timestamp) { PlaybackDuration duration = CalculateDuration(front_chunk.speech_start, end_timestamp); @@ -188,7 +191,7 @@ // We should always have a leftover chunk, potentially with 0 duration, since // we took [0, `end_timestamp`), not [0, `end_timestamp`]. - CHECK(!playback_chunks_.empty()); + CHECK(!chunks.empty()); return results; } @@ -213,16 +216,50 @@ } // Trim the front of the queue, from [0, `start`). - PopFrontUntil(start); + PopFrontUntil(playback_chunks_, start); // `playback_chunks_` no longer contain any timestamps before `start`. // Take all playback chunks from [0, `end`), which is now [`start`, `end`). - auto playbacks = TakeFrontUntil(end); + auto playbacks = TakeFrontUntil(playback_chunks_, end); // We should always have one chunk leftover (sometimes with 0 duration), // since we removed [0, `end`), not [0, `end`]. CHECK(!playback_chunks_.empty()); + return ConvertToMediaRanges(playbacks); +} + +MediaRanges SpeechTimestampEstimator::PeekTimestampsInRange( + SpeechTimestamp start, + SpeechTimestamp end) { + CHECK_LT(start, end); + + if (playback_chunks_.empty()) { + return MediaRanges(); + } + + // Clamp inputs. + constexpr auto kSpeechTimeZero = SpeechTimestamp(base::Seconds(0)); + start = std::clamp(start, kSpeechTimeZero, current_speech_time_); + end = std::clamp(end, kSpeechTimeZero, current_speech_time_); + + // Clamping values can collapse them to the same point. + if (start == end) { + return MediaRanges(); + } + + // Create a copy of the chunks to avoid modifying the original state. + auto chunks_copy = playback_chunks_; + + // Operate on the copy. + PopFrontUntil(chunks_copy, start); + auto playbacks = TakeFrontUntil(chunks_copy, end); + + return ConvertToMediaRanges(playbacks); +} + +MediaRanges SpeechTimestampEstimator::ConvertToMediaRanges( + const std::vector<PlaybackChunk>& playbacks) { MediaRanges results; results.reserve(playbacks.size());
diff --git a/chrome/services/speech/speech_timestamp_estimator.h b/chrome/services/speech/speech_timestamp_estimator.h index 6337eb4..1b2e7b1 100644 --- a/chrome/services/speech/speech_timestamp_estimator.h +++ b/chrome/services/speech/speech_timestamp_estimator.h
@@ -94,17 +94,29 @@ [[nodiscard]] MediaRanges TakeTimestampsInRange(SpeechTimestamp start, SpeechTimestamp end); + // Similar to `TakeTimestampsInRange`, but this method will only peek at the + // current timestamps, and will not discard any data. + [[nodiscard]] + MediaRanges PeekTimestampsInRange(SpeechTimestamp start, SpeechTimestamp end); + private: + // Given a vector of `PlaybackChunk`s, returns a `MediaRanges` vector with the + // media start and end presentation timestamps. + MediaRanges ConvertToMediaRanges(const std::vector<PlaybackChunk>& playbacks); + // Moves forward the last media timestamp by `running_silence_duration_`. void AdjustLastMediaTimestampForSilence(SpeechTimestamp current_speech_time); - // Removes all `playback_chunks_` in the [0, `end_timestamp`) range, splitting - // partial chunks if necessary. - void PopFrontUntil(SpeechTimestamp end_timestamp); + // Removes all chunks in the given `deque` that are in the [0, + // `end_timestamp`) range, splitting partial chunks if necessary. + void PopFrontUntil(base::circular_deque<PlaybackChunk>& chunks, + SpeechTimestamp end_timestamp); - // Returns all `playback_chunks_` in the [0, `end_timestamp`) range, splitting - // partial chunks if necessary. - std::vector<PlaybackChunk> TakeFrontUntil(SpeechTimestamp end_timestamp); + // Returns all chunks in the given `deque` that are in the [0, + // `end_timestamp`) range, splitting partial chunks if necessary. + std::vector<PlaybackChunk> TakeFrontUntil( + base::circular_deque<PlaybackChunk>& chunks, + SpeechTimestamp end_timestamp); // Tracks how much silence was skipped since the last call to // AddPlaybackStart() or AddDuration().
diff --git a/chrome/services/speech/speech_timestamp_estimator_unittest.cc b/chrome/services/speech/speech_timestamp_estimator_unittest.cc index 131c4fea..7d62775 100644 --- a/chrome/services/speech/speech_timestamp_estimator_unittest.cc +++ b/chrome/services/speech/speech_timestamp_estimator_unittest.cc
@@ -44,7 +44,7 @@ SpeechTimestampEstimatorTest() = default; ~SpeechTimestampEstimatorTest() override = default; - // Helper functions to allow the direct use of `base::TimeDelta`; + // Helper functions for taking the timestamps. MediaRanges TakeRange(SpeechTimestampRange range) { return estimator_.TakeTimestampsInRange(range.first, range.second); } @@ -52,6 +52,9 @@ // Completely empties out `estimator_`. return TakeRange(SpeechSecondsRange(0, base::Days(1).InSeconds())); } + MediaRanges PeekRange(SpeechTimestampRange range) { + return estimator_.PeekTimestampsInRange(range.first, range.second); + } void AppendDuration(base::TimeDelta duration) { estimator_.AppendDuration(PlaybackDuration(duration)); @@ -436,5 +439,41 @@ {MediaSecondsRange(100, 110), MediaSecondsRange(510, 520)}); } +TEST_F(SpeechTimestampEstimatorTest, PeekTimestampsInRange) { + // Add [100s, 110s). Speech time: [0, 10) + AddNewPlayback(base::Seconds(100)); + AppendDuration(base::Seconds(10)); + + // Add [140s, 145s). Speech time: [10, 15) + AddNewPlayback(base::Seconds(140)); + AppendDuration(base::Seconds(5)); + + // Add [100s, 110s). Speech time: [15, 25) + AddNewPlayback(base::Seconds(100)); + AppendDuration(base::Seconds(10)); + + // 1. Peek into a range. + // Speech range [1, 21) should correspond to media ranges: + // [101, 110), [140, 145), [100, 106) + MediaRanges expected_ranges = {MediaSecondsRange(101, 110), + MediaSecondsRange(140, 145), + MediaSecondsRange(100, 106)}; + VerifyRanges(PeekRange(SpeechSecondsRange(1, 21)), expected_ranges); + + // 2. Verify that peeking again yields the exact same result. + // This is the key difference from TakeRange. + VerifyRanges(PeekRange(SpeechSecondsRange(1, 21)), expected_ranges); + + // 3. Take the same range to prove the state was indeed unchanged. + // The result of taking should be identical to the result of peeking. + VerifyRanges(TakeRange(SpeechSecondsRange(1, 21)), expected_ranges); + + // 4. Now that we've *taken* the range [1, 21), the estimator is modified. + // Peeking into the full range should now only return the final chunk. + // Speech range [21, 25) -> Media range [106, 110) + VerifyRanges(PeekRange(SpeechSecondsRange(0, 100)), + {MediaSecondsRange(106, 110)}); +} + } // namespace } // namespace speech
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index 546f3e8..ded37f7 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -2570,6 +2570,7 @@ "//components/cookie_config", "//components/country_codes", "//components/data_sharing/public", + "//components/device_event_log", "//components/dom_distiller/content/browser", "//components/dom_distiller/content/browser:test_support", "//components/dom_distiller/content/renderer", @@ -3947,6 +3948,7 @@ "//chrome/browser/contextual_cueing:impl", "//chrome/browser/infobars:test_support", "//chrome/browser/resource_coordinator", + "//chrome/browser/sessions", "//chrome/browser/supervised_user", "//chrome/browser/ui:browser_tab_strip", "//chrome/browser/ui/extensions", @@ -8195,6 +8197,7 @@ "../browser/ui/webui/metrics_reporter/metrics_reporter_unittest.cc", "../browser/ui/webui/metrics_reporter/mock_metrics_reporter.cc", "../browser/ui/webui/metrics_reporter/mock_metrics_reporter.h", + "../browser/ui/webui/new_tab_page/composebox/composebox_handler_unittest.cc", "../browser/ui/webui/new_tab_page/new_tab_page_handler_unittest.cc", "../browser/ui/webui/new_tab_page/untrusted_source_unittest.cc", "../browser/ui/webui/ntp_microsoft_auth/ntp_microsoft_auth_page_handler_unittest.cc", @@ -8322,7 +8325,8 @@ "//chrome/browser/ui/webui/access_code_cast:unit_tests", "//chrome/browser/ui/webui/app_management:unit_tests", "//chrome/browser/ui/webui/new_tab_footer:unit_tests", - "//chrome/browser/ui/webui/new_tab_page/composebox:unit_tests", + "//chrome/browser/ui/webui/new_tab_page/composebox", + "//chrome/browser/ui/webui/new_tab_page/composebox/variations", "//chrome/browser/ui/webui/new_tab_page/ntp_promo:unit_tests", "//chrome/browser/ui/webui/privacy_sandbox:unit_tests", "//chrome/browser/ui/webui/settings", @@ -8332,8 +8336,10 @@ "//chrome/utility:unit_tests", "//components/autofill/core/browser", "//components/data_sharing:test_support", + "//components/omnibox/composebox:test_support", "//components/passage_embeddings", "//components/passage_embeddings:test_support", + "//components/tab_groups", "//components/webapps/isolated_web_apps", ] @@ -10204,6 +10210,9 @@ "../browser/sessions/session_service_unittest.cc", "../browser/sessions/tab_loader_unittest.cc", ] + + deps += [ "//chrome/browser/sessions" ] + if (is_chromeos) { deps += [ "//chromeos/components/kiosk:test_support" ] } @@ -10801,6 +10810,7 @@ public_deps = [ "//chrome/browser:test_support_ui", "//chrome/browser/ui/exclusive_access", + "//chrome/browser/ui/views/frame:toolbar_button_provider", "//chrome/browser/ui/views/side_panel", "//components/supervised_user/core/browser", "//components/webui/chrome_urls", @@ -11281,6 +11291,7 @@ "//chrome/browser/profiles/keep_alive", "//chrome/browser/search", "//chrome/browser/search_engines", + "//chrome/browser/sessions", "//chrome/browser/signin", "//chrome/browser/signin/e2e_tests:test_support", "//chrome/browser/status_icons",
diff --git a/chrome/test/chromedriver/chrome/device_manager.cc b/chrome/test/chromedriver/chrome/device_manager.cc index 26baafe..29f7b40 100644 --- a/chrome/test/chromedriver/chrome/device_manager.cc +++ b/chrome/test/chromedriver/chrome/device_manager.cc
@@ -90,13 +90,6 @@ // |args| as the executable name, and not an argument (in other words, // args[0] is effectively ignored as a command line switch). known_exec_name = "webview"; - } else if (package.find("weblayer") != std::string::npos) { - command_line_file = "/data/local/tmp/weblayer-command-line"; - // This name isn't really important, what is important is that it's - // non-empty. If empty, it means weblayer treats the the first value of - // |args| as the executable name, and not an argument (in other words, - // args[0] is effectively ignored as a command line switch). - known_exec_name = "weblayer_shell"; } if (!use_running_app) { @@ -128,8 +121,7 @@ return Status(kUnknownError, "known package " + package + " does not accept activity/process"); } else if (activity.empty()) { - return Status(kUnknownError, - "WebView/WebLayer apps require activity name"); + return Status(kUnknownError, "WebView apps require activity name"); } if (!command_line_file.empty()) { @@ -197,18 +189,11 @@ // their PID, which Chrome DevTools accepts and we also should. std::string webview_pattern = base::StringPrintf("@webview_devtools_remote_.*%d", pid); - std::string weblayer_pattern = - base::StringPrintf("@weblayer_devtools_remote_.*%d", pid); status = adb_->GetSocketByPattern(serial_, webview_pattern, &socket_name); if (status.IsError()) { - status = - adb_->GetSocketByPattern(serial_, weblayer_pattern, &socket_name); - } - if (status.IsError()) { if (socket_name.empty()) { status.AddDetails( - "make sure the app has its WebView/WebLayer " - "configured for debugging"); + "make sure the app has its WebView configured for debugging"); } return status; }
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py index 2112b3d..c30c704f 100755 --- a/chrome/test/chromedriver/test/run_py_tests.py +++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -9043,11 +9043,8 @@ """ % self._url_prefix, 'utf-8') self._https_server.SetDataForPath('/fedcm.html', script_content) - # Disable SegmentationPlatformFedCmUser for chromedriver tests since it is - # possible for segmentation platform to suppress the UI. self.chrome_switches = ['host-resolver-rules=MAP *:443 127.0.0.1:%s' % port, - 'enable-experimental-web-platform-features', - 'disable-features=SegmentationPlatformFedCmUser'] + 'enable-experimental-web-platform-features'] self._driver = self.CreateDriver( accept_insecure_certs=True, chrome_switches=self.chrome_switches)
diff --git a/chrome/test/data/webui/glic/api_test.ts b/chrome/test/data/webui/glic/api_test.ts index a3ced296..14c238e 100644 --- a/chrome/test/data/webui/glic/api_test.ts +++ b/chrome/test/data/webui/glic/api_test.ts
@@ -875,15 +875,21 @@ assertTrue(!!this.host.scrollTo); assertTrue(!!this.host.setTabContextPermissionState); await this.host.setTabContextPermissionState(true); - await this.host.scrollTo( - {selector: {exactText: {text: 'Test Page'}}, highlight: true}); + await this.host.scrollTo({ + selector: {exactText: {text: 'Test Page'}}, + highlight: true, + documentId: this.testParams.documentId, + }); } async testScrollToFindsTextNoTabContextPermission() { assertTrue(!!this.host.scrollTo); try { - await this.host.scrollTo( - {selector: {exactText: {text: 'Abracadabra'}}, highlight: true}); + await this.host.scrollTo({ + selector: {exactText: {text: 'Abracadabra'}}, + highlight: true, + documentId: this.testParams.documentId, + }); } catch (e) { assertEquals( ScrollToErrorReason.TAB_CONTEXT_PERMISSION_DISABLED, @@ -898,8 +904,11 @@ assertTrue(!!this.host.closePanel); await this.closePanelAndWaitUntilInactive(); try { - await this.host.scrollTo( - {selector: {exactText: {text: 'Abracadabra'}}, highlight: true}); + await this.host.scrollTo({ + selector: {exactText: {text: 'Abracadabra'}}, + highlight: true, + documentId: this.testParams.documentId, + }); } catch (e) { assertEquals( ScrollToErrorReason.NOT_SUPPORTED, (e as ScrollToError).reason); @@ -913,8 +922,11 @@ assertTrue(!!this.host.setTabContextPermissionState); await this.host.setTabContextPermissionState(true); try { - await this.host.scrollTo( - {selector: {exactText: {text: 'Abracadabra'}}, highlight: true}); + await this.host.scrollTo({ + selector: {exactText: {text: 'Abracadabra'}}, + highlight: true, + documentId: this.testParams.documentId, + }); } catch (e) { assertEquals( ScrollToErrorReason.NO_MATCH_FOUND, (e as ScrollToError).reason);
diff --git a/chrome/test/data/webui/new_tab_page/app_test.ts b/chrome/test/data/webui/new_tab_page/app_test.ts index 88a537b4..096cba0 100644 --- a/chrome/test/data/webui/new_tab_page/app_test.ts +++ b/chrome/test/data/webui/new_tab_page/app_test.ts
@@ -17,6 +17,7 @@ import {loadTimeData} from 'chrome://resources/js/load_time_data.js'; import {isMac} from 'chrome://resources/js/platform.js'; import {PromiseResolver} from 'chrome://resources/js/promise_resolver.js'; +import {PageCallbackRouter as SearchboxPageCallbackRouter, PageHandlerRemote as SearchboxPageHandlerRemote} from 'chrome://resources/mojo/components/omnibox/browser/searchbox.mojom-webui.js'; import {assertDeepEquals, assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js'; import type {MetricsTracker} from 'chrome://webui-test/metrics_test_support.js'; import {fakeMetricsPrivate} from 'chrome://webui-test/metrics_test_support.js'; @@ -88,8 +89,10 @@ composeboxHandler = installMock( ComposeboxPageHandlerRemote, - mock => ComposeboxProxyImpl.setInstance( - new ComposeboxProxyImpl(mock, new ComposeboxPageCallbackRouter()))); + mock => ComposeboxProxyImpl.setInstance(new ComposeboxProxyImpl( + mock, new ComposeboxPageCallbackRouter(), + new SearchboxPageHandlerRemote(), + new SearchboxPageCallbackRouter()))); app = document.createElement('ntp-app'); document.body.appendChild(app);
diff --git a/chrome/test/data/webui/new_tab_page/composebox/composebox_test.ts b/chrome/test/data/webui/new_tab_page/composebox/composebox_test.ts index ae954d6..ef9a4ab 100644 --- a/chrome/test/data/webui/new_tab_page/composebox/composebox_test.ts +++ b/chrome/test/data/webui/new_tab_page/composebox/composebox_test.ts
@@ -8,6 +8,7 @@ import {ComposeboxElement, ComposeboxProxyImpl} from 'chrome://new-tab-page/lazy_load.js'; import {$$} from 'chrome://new-tab-page/new_tab_page.js'; import {loadTimeData} from 'chrome://resources/js/load_time_data.js'; +import {PageCallbackRouter as SearchboxPageCallbackRouter, PageHandlerRemote as SearchboxPageHandlerRemote} from 'chrome://resources/mojo/components/omnibox/browser/searchbox.mojom-webui.js'; import {assertDeepEquals, assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js'; import type {MetricsTracker} from 'chrome://webui-test/metrics_test_support.js'; import {fakeMetricsPrivate} from 'chrome://webui-test/metrics_test_support.js'; @@ -33,8 +34,9 @@ document.body.innerHTML = window.trustedTypes!.emptyHTML; handler = installMock( PageHandlerRemote, - mock => ComposeboxProxyImpl.setInstance( - new ComposeboxProxyImpl(mock, new PageCallbackRouter()))); + mock => ComposeboxProxyImpl.setInstance(new ComposeboxProxyImpl( + mock, new PageCallbackRouter(), new SearchboxPageHandlerRemote(), + new SearchboxPageCallbackRouter()))); callbackRouterRemote = ComposeboxProxyImpl.getInstance() .callbackRouter.$.bindNewPipeAndPassRemote(); metrics = fakeMetricsPrivate();
diff --git a/chrome/updater/test/data/enterprise/win/google/test_gold.adml b/chrome/updater/test/data/enterprise/win/google/test_gold.adml index 4ec490c..f671fd4 100644 --- a/chrome/updater/test/data/enterprise/win/google/test_gold.adml +++ b/chrome/updater/test/data/enterprise/win/google/test_gold.adml Binary files differ
diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM index d385a53e..ebb3a1a 100644 --- a/chromeos/CHROMEOS_LKGM +++ b/chromeos/CHROMEOS_LKGM
@@ -1 +1 @@ -16366.0.0-1070641 \ No newline at end of file +16367.0.0-1070647 \ No newline at end of file
diff --git a/chromeos/ash/experiences/arc/chrome_feature_flags/arc_chrome_feature_flags_bridge.cc b/chromeos/ash/experiences/arc/chrome_feature_flags/arc_chrome_feature_flags_bridge.cc index ab97972..5a9970a8 100644 --- a/chromeos/ash/experiences/arc/chrome_feature_flags/arc_chrome_feature_flags_bridge.cc +++ b/chromeos/ash/experiences/arc/chrome_feature_flags/arc_chrome_feature_flags_bridge.cc
@@ -13,6 +13,7 @@ #include "chromeos/ash/experiences/arc/session/arc_bridge_service.h" #include "chromeos/ash/experiences/arc/session/arc_service_manager.h" #include "chromeos/constants/chromeos_features.h" +#include "chromeos/ui/base/chromeos_ui_constants.h" namespace arc { @@ -80,7 +81,7 @@ flags->touchscreen_emulation = true; flags->rounded_window_compat_strategy = mojom::RoundedWindowCompatStrategy::kLeftRightBottomGesture; - flags->rounded_window_radius = chromeos::features::RoundedWindowsRadius(); + flags->rounded_window_radius = chromeos::kRoundedWindowCornerRadius; flags->enable_pip_double_tap = true; flags->render_arc_notifications_by_chrome = ash::features::IsRenderArcNotificationsByChromeEnabled();
diff --git a/chromeos/ash/experiences/arc/chrome_feature_flags/arc_chrome_feature_flags_bridge_unittest.cc b/chromeos/ash/experiences/arc/chrome_feature_flags/arc_chrome_feature_flags_bridge_unittest.cc index 0e9f4bb..c87ebc6 100644 --- a/chromeos/ash/experiences/arc/chrome_feature_flags/arc_chrome_feature_flags_bridge_unittest.cc +++ b/chromeos/ash/experiences/arc/chrome_feature_flags/arc_chrome_feature_flags_bridge_unittest.cc
@@ -12,6 +12,7 @@ #include "chromeos/ash/experiences/arc/session/arc_service_manager.h" #include "chromeos/ash/experiences/arc/test/fake_chrome_feature_flags_instance.h" #include "chromeos/constants/chromeos_features.h" +#include "chromeos/ui/base/chromeos_ui_constants.h" #include "components/user_prefs/test/test_browser_context_with_prefs.h" #include "content/public/test/browser_task_environment.h" #include "testing/gtest/include/gtest/gtest.h" @@ -76,21 +77,10 @@ mojom::RoundedWindowCompatStrategy::kLeftRightBottomGesture); } -TEST_F(ArcChromeFeatureFlagsBridgeTest, NotifyRoundedWindows_Enabled) { - scoped_feature_list()->InitWithFeaturesAndParameters( - {{chromeos::features::kFeatureManagementRoundedWindows, {}}, - {chromeos::features::kRoundedWindows, - {{chromeos::features::kRoundedWindowsRadius, "8"}}}}, - {}); +TEST_F(ArcChromeFeatureFlagsBridgeTest, RoundedWindowsRadius) { Connect(); - EXPECT_EQ(instance()->flags_called_value()->rounded_window_radius, 8); -} - -TEST_F(ArcChromeFeatureFlagsBridgeTest, NotifyRoundedWindows_Disabled) { - scoped_feature_list()->InitAndDisableFeature( - chromeos::features::kRoundedWindows); - Connect(); - EXPECT_EQ(instance()->flags_called_value()->rounded_window_radius, 0); + EXPECT_EQ(instance()->flags_called_value()->rounded_window_radius, + chromeos::kRoundedWindowCornerRadius); } TEST_F(ArcChromeFeatureFlagsBridgeTest, NotifyResizeCompat_Enabled) {
diff --git a/chromeos/constants/chromeos_features.cc b/chromeos/constants/chromeos_features.cc index c62e912c..1442db8f 100644 --- a/chromeos/constants/chromeos_features.cc +++ b/chromeos/constants/chromeos_features.cc
@@ -355,10 +355,6 @@ "MicrosoftOneDriveIntegrationForEnterprise", base::FEATURE_ENABLED_BY_DEFAULT); -BASE_FEATURE(kRoundedWindows, - "RoundedWindows", - base::FEATURE_ENABLED_BY_DEFAULT); - // Enables CloudFileSystem for FileSystemProvider extensions. BASE_FEATURE(kFileSystemProviderCloudFileSystem, "FileSystemProviderCloudFileSystem", @@ -391,8 +387,6 @@ "WebAppManifestProtocolHandlerSupport", base::FEATURE_DISABLED_BY_DEFAULT); -const char kRoundedWindowsRadius[] = "window_radius"; - bool IsApnPoliciesEnabled() { return base::FeatureList::IsEnabled(kApnPolicies); } @@ -577,8 +571,7 @@ bool IsRoundedWindowsEnabled() { static bool is_enabled = - base::FeatureList::IsEnabled(kFeatureManagementRoundedWindows) && - base::FeatureList::IsEnabled(kRoundedWindows); + base::FeatureList::IsEnabled(kFeatureManagementRoundedWindows); return is_enabled; } @@ -591,16 +584,6 @@ return base::FeatureList::IsEnabled(kFeatureManagementHistoryEmbedding); } -int RoundedWindowsRadius() { - if (!IsRoundedWindowsEnabled()) { - return 0; - } - - return base::GetFieldTrialParamByFeatureAsInt(kRoundedWindows, - kRoundedWindowsRadius, - /*default_value=*/12); -} - bool IsWebAppManifestProtocolHandlerSupportEnabled() { return base::FeatureList::IsEnabled(kWebAppManifestProtocolHandlerSupport); }
diff --git a/chromeos/constants/chromeos_features.h b/chromeos/constants/chromeos_features.h index 1d11ac9..8d1fa5ad7 100644 --- a/chromeos/constants/chromeos_features.h +++ b/chromeos/constants/chromeos_features.h
@@ -112,7 +112,6 @@ BASE_DECLARE_FEATURE(kUploadOfficeToCloudForEnterprise); COMPONENT_EXPORT(CHROMEOS_CONSTANTS) BASE_DECLARE_FEATURE(kUploadOfficeToCloudSync); -COMPONENT_EXPORT(CHROMEOS_CONSTANTS) BASE_DECLARE_FEATURE(kRoundedWindows); COMPONENT_EXPORT(CHROMEOS_CONSTANTS) BASE_DECLARE_FEATURE(kMicrosoftOneDriveIntegrationForEnterprise); COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
diff --git a/chromeos/ui/base/chromeos_ui_constants.h b/chromeos/ui/base/chromeos_ui_constants.h index f7c8e27..3620f8a 100644 --- a/chromeos/ui/base/chromeos_ui_constants.h +++ b/chromeos/ui/base/chromeos_ui_constants.h
@@ -10,8 +10,10 @@ namespace chromeos { -// Radius of the header's top corners when the window is restored. -inline constexpr int kTopCornerRadiusWhenRestored = 2; +// Radius of the window corner radius when the window is restored. +// See `chromeos::GetWindowRoundedCorners()` for the context. +inline constexpr int kRoundedWindowCornerRadius = 12; +inline constexpr int kRoundedWindowSmallCornerRadius = 2; // Rounded corner radius for Pip window. inline constexpr int kPipRoundedCornerRadius = 12;
diff --git a/chromeos/ui/frame/frame_utils.cc b/chromeos/ui/frame/frame_utils.cc index e82dbed..6383e034 100644 --- a/chromeos/ui/frame/frame_utils.cc +++ b/chromeos/ui/frame/frame_utils.cc
@@ -114,8 +114,8 @@ gfx::RoundedCornersF GetWindowRoundedCorners() { const int corner_radius = features::IsRoundedWindowsEnabled() - ? features::RoundedWindowsRadius() - : kTopCornerRadiusWhenRestored; + ? kRoundedWindowCornerRadius + : kRoundedWindowSmallCornerRadius; const bool rounded_bottom_corners = features::IsRoundedWindowsEnabled(); return gfx::RoundedCornersF(corner_radius, corner_radius,
diff --git a/clank b/clank index 6e21ffac..81a027f 160000 --- a/clank +++ b/clank
@@ -1 +1 @@ -Subproject commit 6e21ffac7e2b010713d1a7ebc0b50317309068c0 +Subproject commit 81a027f27278239ad842497fae52a645acb015d3
diff --git a/components/autofill/core/browser/data_manager/payments/payments_data_manager.cc b/components/autofill/core/browser/data_manager/payments/payments_data_manager.cc index 2acb8ba..c8341e66 100644 --- a/components/autofill/core/browser/data_manager/payments/payments_data_manager.cc +++ b/components/autofill/core/browser/data_manager/payments/payments_data_manager.cc
@@ -1009,6 +1009,10 @@ // BUILDFLAG(IS_CHROMEOS) } +bool PaymentsDataManager::IsAutofillBnplPrefEnabled() const { + return prefs::IsAutofillBnplEnabled(pref_service_); +} + void PaymentsDataManager::NotifyObservers() { if (!HasPendingPaymentQueries()) { for (Observer& o : observers_) { @@ -1057,10 +1061,6 @@ features::kAutofillEnableCardBenefitsSync); } -bool PaymentsDataManager::IsAutofillBnplPrefEnabled() const { - return prefs::IsAutofillBnplEnabled(pref_service_); -} - bool PaymentsDataManager::IsAutofillPaymentMethodsEnabled() const { return prefs::IsAutofillPaymentMethodsEnabled(pref_service_); } @@ -2042,6 +2042,10 @@ return prefs::IsFacilitatedPaymentsEwalletEnabled(pref_service_); } +bool PaymentsDataManager::IsFacilitatedPaymentsA2AUserPrefEnabled() const { + return prefs::IsFacilitatedPaymentsA2AEnabled(pref_service_); +} + void PaymentsDataManager::SetFacilitatedPaymentsA2ATriggeredOnce(bool enabled) { prefs::SetFacilitatedPaymentsA2ATriggeredOnce(pref_service_, enabled); } @@ -2262,6 +2266,29 @@ return; } + DenseSet<PaymentInstrument::ActionRequired> action_required = + DenseSet<PaymentInstrument::ActionRequired>(); + + // Sets values for `BnplIssuer::action_required` when the list is not empty + // and flag 'kAutofillEnableBuyNowPayLaterForExternallyLinkedKlarna` is + // enabled. + // Note: `action_required_size()` is checked first so that the experiment + // groups only contain users having nonempty`action_required` info. + if (payment_instrument.action_required_size() > 0 && + base::FeatureList::IsEnabled( + features::kAutofillEnableBuyNowPayLaterForExternallyLinkedKlarna)) { + for (int action_required_sync : payment_instrument.action_required()) { + switch (action_required_sync) { + case sync_pb::PaymentInstrument_ActionRequired_ACTION_REQUIRED_UNKNOWN: + action_required.insert(PaymentInstrument::ActionRequired::kUnknown); + break; + case sync_pb::PaymentInstrument_ActionRequired_ACCEPT_TOS: + action_required.insert(PaymentInstrument::ActionRequired::kAcceptTos); + break; + } + } + } + // `IsBnplIssuerSupported` is already called to filter out any unknown // issuer IDs that might be returned by the payment server. This ensures that // only issuer IDs with a corresponding BnplIssuer::IssuerId enum value are @@ -2270,7 +2297,7 @@ linked_bnpl_issuers_.emplace_back( payment_instrument.instrument_id(), ConvertToBnplIssuerIdEnum(bnpl_issuer_details.issuer_id()), - std::move(eligible_price_ranges)); + std::move(eligible_price_ranges), std::move(action_required)); } void PaymentsDataManager::CacheIfEwalletPaymentInstrument(
diff --git a/components/autofill/core/browser/data_manager/payments/payments_data_manager.h b/components/autofill/core/browser/data_manager/payments/payments_data_manager.h index 4d49e4d..474349f 100644 --- a/components/autofill/core/browser/data_manager/payments/payments_data_manager.h +++ b/components/autofill/core/browser/data_manager/payments/payments_data_manager.h
@@ -520,6 +520,9 @@ // Returns the value of the FacilitatedPaymentsEwallet user pref. bool IsFacilitatedPaymentsEwalletUserPrefEnabled() const; + // Returns the value of the FacilitatedPaymentsA2AEnabled user pref. + bool IsFacilitatedPaymentsA2AUserPrefEnabled() const; + // Sets the FacilitatedPaymentsA2ATriggeredOnce user pref value to `enabled`. void SetFacilitatedPaymentsA2ATriggeredOnce(bool enabled); @@ -574,6 +577,9 @@ void SetPrefService(PrefService* pref_service); + // Returns the value of the AutofillBnplEnabled pref. + virtual bool IsAutofillBnplPrefEnabled() const; + void NotifyObservers(); CreditCard* GetMutableCreditCardByGUID(const std::string& guid); @@ -649,9 +655,6 @@ // Returns whether Autofill card benefit suggestion labels should be blocked. bool ShouldBlockCardBenefitSuggestionLabels() const; - // Returns the value of the AutofillBnplEnabled pref. - virtual bool IsAutofillBnplPrefEnabled() const; - // Checks whether any new card art url is synced. If so, attempt to fetch the // image based on the url. void ProcessCardArtUrlChanges();
diff --git a/components/autofill/core/browser/data_manager/payments/payments_data_manager_unittest.cc b/components/autofill/core/browser/data_manager/payments/payments_data_manager_unittest.cc index 562fbe8b..5ea4d4d 100644 --- a/components/autofill/core/browser/data_manager/payments/payments_data_manager_unittest.cc +++ b/components/autofill/core/browser/data_manager/payments/payments_data_manager_unittest.cc
@@ -2381,7 +2381,7 @@ test::CreatePaymentInstrumentWithLinkedBnplIssuer( 1234L, std::string(kBnplAffirmIssuerId), "USD", /*min_price_in_micros=*/0, - /*max_price_in_micros=*/35000000); + /*max_price_in_micros=*/35'000'000); ASSERT_TRUE( GetServerDataTable()->SetPaymentInstruments({payment_instrument})); @@ -2411,12 +2411,12 @@ test::CreatePaymentInstrumentWithLinkedBnplIssuer( 1234L, std::string(kBnplAffirmIssuerId), "USD", /*min_price_in_micros=*/0, - /*max_price_in_micros=*/35000000); + /*max_price_in_micros=*/35'000'000); sync_pb::PaymentInstrument payment_instrument_2 = test::CreatePaymentInstrumentWithLinkedBnplIssuer( 2345L, std::string(kBnplZipIssuerId), "USD", /*min_price_in_micros=*/0, - /*max_price_in_micros=*/35000000); + /*max_price_in_micros=*/35'000'000); ASSERT_TRUE(GetServerDataTable()->SetPaymentInstruments( {payment_instrument_1, payment_instrument_2})); @@ -2442,7 +2442,7 @@ test::CreatePaymentInstrumentWithLinkedBnplIssuer( 1234L, "unsupported_issuer_id", "USD", /*min_price_in_micros=*/0, - /*max_price_in_micros=*/35000000); + /*max_price_in_micros=*/35'000'000); ASSERT_TRUE( GetServerDataTable()->SetPaymentInstruments({payment_instrument_1})); @@ -2464,8 +2464,8 @@ int64_t instrument_id = 1234L; std::string issuer_id = std::string(kBnplAffirmIssuerId); std::string currency = "USD"; - uint64_t min_price_in_micros = 5000000; - uint64_t max_price_in_micros = 35000000; + uint64_t min_price_in_micros = 5'000'000; + uint64_t max_price_in_micros = 35'000'000; sync_pb::PaymentInstrument payment_instrument = test::CreatePaymentInstrumentWithLinkedBnplIssuer( instrument_id, issuer_id, currency, min_price_in_micros, @@ -2504,8 +2504,8 @@ int64_t instrument_id = 1234L; std::string issuer_id = std::string(kBnplAffirmIssuerId); std::string currency = "USD"; - uint64_t min_price_in_micros = 50000000; - uint64_t max_price_in_micros = 35000000; + uint64_t min_price_in_micros = 50'000'000; + uint64_t max_price_in_micros = 35'000'000; sync_pb::PaymentInstrument payment_instrument = test::CreatePaymentInstrumentWithLinkedBnplIssuer( instrument_id, issuer_id, currency, min_price_in_micros, @@ -2542,7 +2542,8 @@ {test::CreatePaymentInstrumentWithLinkedBnplIssuer( /*instrument_id=*/1234L, /*issuer_id=*/std::string(kBnplAffirmIssuerId), /*currency=*/"CAD", - /*min_price_in_micros=*/5000000, /*max_price_in_micros=*/35000000)})); + /*min_price_in_micros=*/5'000'000, + /*max_price_in_micros=*/35'000'000)})); // `Refresh()` must be called to ensure that the linked BNPL issuer payment // instruments are loaded again from the WebDatabase. @@ -2554,6 +2555,79 @@ EXPECT_TRUE(payments_data_manager().GetLinkedBnplIssuers().empty()); } +// Tests that `action_required` is not set for BNPL issuers if flag +// `AutofillEnableBuyNowPayLaterForExternallyLinkedKlarna` is disabled. +TEST_F(PaymentsDataManagerTest, + GetLinkedBnplIssuers_IssuerLinkedExternally_FlagDisabled) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitWithFeatures( + /*enabled_features=*/{features::kAutofillEnableBuyNowPayLaterSyncing, + features::kAutofillEnableBuyNowPayLaterForKlarna}, + /*disabled_features=*/{ + features::kAutofillEnableBuyNowPayLaterForExternallyLinkedKlarna}); + sync_pb::PaymentInstrument payment_instrument = + test::CreatePaymentInstrumentWithLinkedBnplIssuer( + /*instrument_id=*/1234L, std::string(kBnplKlarnaIssuerId), "USD", + /*min_price_in_micros=*/0, + /*max_price_in_micros=*/35'000'000, + /*actions_required=*/ + {sync_pb::PaymentInstrument_ActionRequired_ACCEPT_TOS}); + ASSERT_TRUE( + GetServerDataTable()->SetPaymentInstruments({payment_instrument})); + payments_data_manager().Refresh(); + WaitForOnPaymentsDataChanged(); + + base::span<const BnplIssuer> linked_bnpl_issuers = + payments_data_manager().GetLinkedBnplIssuers(); + + ASSERT_EQ(linked_bnpl_issuers.size(), 1U); + EXPECT_EQ( + linked_bnpl_issuers[0], + BnplIssuer( + /*instrument_id=*/1234L, BnplIssuer::IssuerId::kBnplKlarna, + /*eligible_price_ranges=*/ + {BnplIssuer::EligiblePriceRange("USD", /*price_lower_bound=*/0, + /*price_upper_bound=*/35'000'000)}, + /*action_required=*/DenseSet<PaymentInstrument::ActionRequired>())); +} + +// Tests that `action_required` is set for BNPL issuers if flag +// `AutofillEnableBuyNowPayLaterForExternallyLinkedKlarna` is enabled. +TEST_F(PaymentsDataManagerTest, GetLinkedBnplIssuers_IssuerLinkedExternally) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitWithFeatures( + /*enabled_features=*/ + {features::kAutofillEnableBuyNowPayLaterSyncing, + features::kAutofillEnableBuyNowPayLaterForKlarna, + features::kAutofillEnableBuyNowPayLaterForExternallyLinkedKlarna}, + /*disabled_features=*/{}); + sync_pb::PaymentInstrument payment_instrument = + test::CreatePaymentInstrumentWithLinkedBnplIssuer( + /*instrument_id=*/1234L, std::string(kBnplKlarnaIssuerId), "USD", + /*min_price_in_micros=*/0, + /*max_price_in_micros=*/35'000'000, + /*actions_required=*/ + {sync_pb::PaymentInstrument_ActionRequired_ACCEPT_TOS}); + ASSERT_TRUE( + GetServerDataTable()->SetPaymentInstruments({payment_instrument})); + payments_data_manager().Refresh(); + WaitForOnPaymentsDataChanged(); + + base::span<const BnplIssuer> linked_bnpl_issuers = + payments_data_manager().GetLinkedBnplIssuers(); + + ASSERT_EQ(linked_bnpl_issuers.size(), 1U); + EXPECT_EQ( + linked_bnpl_issuers[0], + BnplIssuer( + /*instrument_id=*/1234L, BnplIssuer::IssuerId::kBnplKlarna, + /*eligible_price_ranges=*/ + {BnplIssuer::EligiblePriceRange("USD", /*price_lower_bound=*/0, + /*price_upper_bound=*/35'000'000)}, + /*action_required=*/ + DenseSet({PaymentInstrument::ActionRequired::kAcceptTos}))); +} + #endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || // BUILDFLAG(IS_CHROMEOS)
diff --git a/components/autofill/core/browser/data_manager/payments/test_payments_data_manager.cc b/components/autofill/core/browser/data_manager/payments/test_payments_data_manager.cc index 8f6cf49..ca60924 100644 --- a/components/autofill/core/browser/data_manager/payments/test_payments_data_manager.cc +++ b/components/autofill/core/browser/data_manager/payments/test_payments_data_manager.cc
@@ -256,6 +256,13 @@ return false; } +bool TestPaymentsDataManager::IsAutofillBnplPrefEnabled() const { + if (autofill_bnpl_enabled_.has_value()) { + return autofill_bnpl_enabled_.value(); + } + return PaymentsDataManager::IsAutofillBnplPrefEnabled(); +} + CoreAccountInfo TestPaymentsDataManager::GetAccountInfoForPaymentsServer() const { return account_info_;
diff --git a/components/autofill/core/browser/data_manager/payments/test_payments_data_manager.h b/components/autofill/core/browser/data_manager/payments/test_payments_data_manager.h index 0e3488e..224bef9 100644 --- a/components/autofill/core/browser/data_manager/payments/test_payments_data_manager.h +++ b/components/autofill/core/browser/data_manager/payments/test_payments_data_manager.h
@@ -61,6 +61,7 @@ const CreditCard& imported_credit_card) override; bool IsPaymentCvcStorageEnabled() override; bool IsSyncFeatureEnabledForPaymentsServerMetrics() const override; + bool IsAutofillBnplPrefEnabled() const override; CoreAccountInfo GetAccountInfoForPaymentsServer() const override; // Clears |local_credit_cards_| and |server_credit_cards_|. @@ -88,6 +89,10 @@ payments_cvc_storage_enabled_ = enabled; } + void SetIsAutofillBnplPrefEnabled(bool enabled) { + autofill_bnpl_enabled_ = enabled; + } + // Adds a card to `server_credit_cards_`. This test class treats masked and // full server cards equally, relying on their preset RecordType to // differentiate them. @@ -142,6 +147,7 @@ std::optional<bool> payments_wallet_sync_transport_enabled_; std::optional<bool> payment_methods_mandatory_reauth_enabled_; std::optional<bool> payments_cvc_storage_enabled_; + std::optional<bool> autofill_bnpl_enabled_; CoreAccountInfo account_info_; std::unique_ptr<TestAutofillImageFetcher> owned_image_fetcher_; };
diff --git a/components/autofill/core/browser/data_model/addresses/address.cc b/components/autofill/core/browser/data_model/addresses/address.cc index 40e5057..5ac0343 100644 --- a/components/autofill/core/browser/data_model/addresses/address.cc +++ b/components/autofill/core/browser/data_model/addresses/address.cc
@@ -200,7 +200,7 @@ return base::ASCIIToUTF16(country_code); } - FieldType storable_type = type.GetStorableType(); + FieldType storable_type = type.GetAddressType(); if (storable_type == ADDRESS_HOME_COUNTRY && !country_code.empty()) return AutofillCountry(country_code, locale).name(); @@ -235,7 +235,7 @@ return !country_code.empty(); } - FieldType storable_type = type.GetStorableType(); + FieldType storable_type = type.GetAddressType(); if (storable_type == ADDRESS_HOME_COUNTRY && !value.empty()) { std::string country_code = CountryNames::GetInstance()->GetCountryCodeForLocalizedCountryName(
diff --git a/components/autofill/core/browser/data_model/addresses/autofill_i18n_api.cc b/components/autofill/core/browser/data_model/addresses/autofill_i18n_api.cc index abf02a9..e265257 100644 --- a/components/autofill/core/browser/data_model/addresses/autofill_i18n_api.cc +++ b/components/autofill/core/browser/data_model/addresses/autofill_i18n_api.cc
@@ -231,6 +231,8 @@ case NATIONAL_ID_CARD_EXPIRATION_DATE: case NATIONAL_ID_CARD_ISSUE_DATE: case NATIONAL_ID_CARD_ISSUING_COUNTRY: + case KNOWN_TRAVELER_NUMBER: + case KNOWN_TRAVELER_NUMBER_EXPIRATION_DATE: case MAX_VALID_FIELD_TYPE: return nullptr; }
diff --git a/components/autofill/core/browser/data_model/addresses/autofill_profile.cc b/components/autofill/core/browser/data_model/addresses/autofill_profile.cc index ccd7f744..1365952 100644 --- a/components/autofill/core/browser/data_model/addresses/autofill_profile.cc +++ b/components/autofill/core/browser/data_model/addresses/autofill_profile.cc
@@ -74,9 +74,8 @@ constexpr char kAddressComponentsDefaultLocality[] = "en-US"; -// Like |AutofillType::GetStorableType()|, but also returns |NAME_FULL| for -// first, middle, and last name field types, and groups phone number types -// similarly. +// Returns `NAME_FULL` for first, middle, and last name field types, and groups +// phone number types similarly. FieldType GetStorableTypeCollapsingGroupsForPartialType(FieldType type) { if (GroupTypeOfFieldType(type) == FieldTypeGroup::kName) { return NAME_FULL; @@ -552,7 +551,7 @@ // When adding field types, ensure that they don't need to be added here and // update the last checked value. - static_assert(FieldType::MAX_VALID_FIELD_TYPE == 203, + static_assert(FieldType::MAX_VALID_FIELD_TYPE == 204, "New field type needs to be reviewed for inclusion in the " "profile comparison logic."); @@ -1058,7 +1057,7 @@ std::u16string AutofillProfile::GetInfo(const AutofillType& type, const std::string& app_locale) const { - const FormGroup* form_group = FormGroupForType(type.GetStorableType()); + const FormGroup* form_group = FormGroupForType(type.GetAddressType()); if (!form_group) { return std::u16string(); } @@ -1070,7 +1069,7 @@ const std::u16string& value, const std::string& app_locale, VerificationStatus status) { - FormGroup* form_group = MutableFormGroupForType(type.GetStorableType()); + FormGroup* form_group = MutableFormGroupForType(type.GetAddressType()); if (!form_group) { return false; }
diff --git a/components/autofill/core/browser/data_model/addresses/contact_info.cc b/components/autofill/core/browser/data_model/addresses/contact_info.cc index fdcf660..99f18dd 100644 --- a/components/autofill/core/browser/data_model/addresses/contact_info.cc +++ b/components/autofill/core/browser/data_model/addresses/contact_info.cc
@@ -219,32 +219,28 @@ std::u16string NameInfo::GetInfo(const AutofillType& type, const std::string& app_locale) const { - return GetRawInfo(type.GetStorableType()); + return GetRawInfo(type.GetAddressType()); } bool NameInfo::SetInfoWithVerificationStatus(const AutofillType& type, const std::u16string& value, const std::string& app_locale, VerificationStatus status) { - if (type.GetStorableType() == NAME_FULL || - (type.GetStorableType() == ALTERNATIVE_FULL_NAME && - base::FeatureList::IsEnabled( - features::kAutofillSupportPhoneticNameForJP))) { + const FieldType ft = type.GetAddressType(); + if (ft == NAME_FULL || (ft == ALTERNATIVE_FULL_NAME && + base::FeatureList::IsEnabled( + features::kAutofillSupportPhoneticNameForJP))) { // If the set string is token equivalent to the old one, the value can // just be updated, otherwise create a new name record and complete it in // the end. // TODO(crbug.com/40266145): Move this logic to the data model. - AreStringTokenEquivalent(value, - GetRootForType(type.GetStorableType()) - ->GetValueForType(type.GetStorableType())) - ? GetRootForType(type.GetStorableType()) - ->SetValueForType(type.GetStorableType(), value, status) - : GetRootForType(type.GetStorableType()) - ->SetValueForType(type.GetStorableType(), value, status, - /*invalidate_child_nodes=*/true); + AreStringTokenEquivalent(value, GetRootForType(ft)->GetValueForType(ft)) + ? GetRootForType(ft)->SetValueForType(ft, value, status) + : GetRootForType(ft)->SetValueForType(ft, value, status, + /*invalidate_child_nodes=*/true); return true; } - SetRawInfoWithVerificationStatus(type.GetStorableType(), value, status); + SetRawInfoWithVerificationStatus(ft, value, status); return true; } @@ -284,7 +280,7 @@ std::u16string EmailInfo::GetInfo(const AutofillType& type, const std::string& app_locale) const { - return GetRawInfo(type.GetStorableType()); + return GetRawInfo(type.GetAddressType()); } std::u16string EmailInfo::GetRawInfo(FieldType type) const { @@ -306,7 +302,7 @@ const std::u16string& value, const std::string& app_locale, const VerificationStatus status) { - SetRawInfoWithVerificationStatus(type.GetStorableType(), value, status); + SetRawInfoWithVerificationStatus(type.GetAddressType(), value, status); return true; } @@ -342,7 +338,7 @@ std::u16string CompanyInfo::GetInfo(const AutofillType& type, const std::string& app_locale) const { - return GetRawInfo(type.GetStorableType()); + return GetRawInfo(type.GetAddressType()); } std::u16string CompanyInfo::GetRawInfo(FieldType type) const { @@ -361,7 +357,7 @@ const std::u16string& value, const std::string& app_locale, const VerificationStatus status) { - SetRawInfoWithVerificationStatus(type.GetStorableType(), value, status); + SetRawInfoWithVerificationStatus(type.GetAddressType(), value, status); return true; }
diff --git a/components/autofill/core/browser/data_model/addresses/phone_number.cc b/components/autofill/core/browser/data_model/addresses/phone_number.cc index f65a8b6..31f81e2 100644 --- a/components/autofill/core/browser/data_model/addresses/phone_number.cc +++ b/components/autofill/core/browser/data_model/addresses/phone_number.cc
@@ -174,7 +174,7 @@ // If the phone cannot be normalized, returns the stored value verbatim. std::u16string PhoneNumber::GetInfo(const AutofillType& autofill_type, const std::string& app_locale) const { - FieldType type = autofill_type.GetStorableType(); + FieldType type = autofill_type.GetAddressType(); UpdateCacheIfNeeded(app_locale); // When the phone number autofill has stored cannot be normalized, it @@ -273,7 +273,7 @@ const std::u16string& value, const std::string& app_locale, VerificationStatus status) { - SetRawInfoWithVerificationStatus(type.GetStorableType(), value, status); + SetRawInfoWithVerificationStatus(type.GetAddressType(), value, status); if (number_.empty()) { return true;
diff --git a/components/autofill/core/browser/data_model/payments/bnpl_issuer.cc b/components/autofill/core/browser/data_model/payments/bnpl_issuer.cc index ed0b9877..f29cba0 100644 --- a/components/autofill/core/browser/data_model/payments/bnpl_issuer.cc +++ b/components/autofill/core/browser/data_model/payments/bnpl_issuer.cc
@@ -37,17 +37,22 @@ bool operator==(const BnplIssuer& a, const BnplIssuer& b) = default; -BnplIssuer::BnplIssuer(std::optional<int64_t> instrument_id, - BnplIssuer::IssuerId issuer_id, - std::vector<EligiblePriceRange> eligible_price_ranges) +BnplIssuer::BnplIssuer( + std::optional<int64_t> instrument_id, + BnplIssuer::IssuerId issuer_id, + std::vector<EligiblePriceRange> eligible_price_ranges, + DenseSet<PaymentInstrument::ActionRequired> action_required) : issuer_id_(std::move(issuer_id)), payment_instrument_( instrument_id.has_value() ? std::make_optional<PaymentInstrument>( instrument_id.value(), - u"", - GURL(), - DenseSet({PaymentInstrument::PaymentRail::kCardNumber})) + /*nickname=*/u"", + /*display_icon_url=*/GURL(), + /*supported_rails=*/ + DenseSet({PaymentInstrument::PaymentRail::kCardNumber}), + /*is_fido_enrolled=*/false, + std::move(action_required)) : std::nullopt), eligible_price_ranges_(std::move(eligible_price_ranges)) {}
diff --git a/components/autofill/core/browser/data_model/payments/bnpl_issuer.h b/components/autofill/core/browser/data_model/payments/bnpl_issuer.h index e000485..0575642 100644 --- a/components/autofill/core/browser/data_model/payments/bnpl_issuer.h +++ b/components/autofill/core/browser/data_model/payments/bnpl_issuer.h
@@ -73,10 +73,13 @@ // `instrument_id` is present for linked issuers, and nullopt for unlinked // issuers. `issuer_id` is the unique identifier of this specfiic issuer. // `eligible_price_ranges` is a list of currencies mapped to their price - // ranges, in micros. + // ranges, in micros. 'action_required' is the additional steps needed to + // use this issuer. BnplIssuer(std::optional<int64_t> instrument_id, IssuerId issuer_id, - std::vector<EligiblePriceRange> eligible_price_ranges); + std::vector<EligiblePriceRange> eligible_price_ranges, + DenseSet<PaymentInstrument::ActionRequired> action_required = + DenseSet<PaymentInstrument::ActionRequired>()); BnplIssuer(const BnplIssuer&); BnplIssuer& operator=(const BnplIssuer&); BnplIssuer(BnplIssuer&&);
diff --git a/components/autofill/core/browser/data_quality/addresses/profile_token_quality.cc b/components/autofill/core/browser/data_quality/addresses/profile_token_quality.cc index 33faae1..5514f13 100644 --- a/components/autofill/core/browser/data_quality/addresses/profile_token_quality.cc +++ b/components/autofill/core/browser/data_quality/addresses/profile_token_quality.cc
@@ -269,7 +269,7 @@ DCHECK(!base::Contains(other_profiles, profile_->guid(), [](const AutofillProfile* p) { return p->guid(); })); - const FieldType type = field.Type().GetStorableType(); + const FieldType type = field.Type().GetAddressType(); if (field.is_autofilled()) { // The filled value was accepted without editing. return AutofillProfile::kDatabaseStoredTypes.contains(type)
diff --git a/components/autofill/core/browser/field_type_utils.cc b/components/autofill/core/browser/field_type_utils.cc index faa8f9d9..20c7c11 100644 --- a/components/autofill/core/browser/field_type_utils.cc +++ b/components/autofill/core/browser/field_type_utils.cc
@@ -188,6 +188,8 @@ case NATIONAL_ID_CARD_EXPIRATION_DATE: case NATIONAL_ID_CARD_ISSUE_DATE: case NATIONAL_ID_CARD_ISSUING_COUNTRY: + case KNOWN_TRAVELER_NUMBER: + case KNOWN_TRAVELER_NUMBER_EXPIRATION_DATE: case MAX_VALID_FIELD_TYPE: return false; } @@ -313,6 +315,8 @@ case DRIVERS_LICENSE_NUMBER: case NATIONAL_ID_CARD_NUMBER: case NATIONAL_ID_CARD_ISSUING_COUNTRY: + case KNOWN_TRAVELER_NUMBER: + case KNOWN_TRAVELER_NUMBER_EXPIRATION_DATE: case MAX_VALID_FIELD_TYPE: return false; case CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR: @@ -449,6 +453,8 @@ case NATIONAL_ID_CARD_EXPIRATION_DATE: case NATIONAL_ID_CARD_ISSUE_DATE: case NATIONAL_ID_CARD_ISSUING_COUNTRY: + case KNOWN_TRAVELER_NUMBER: + case KNOWN_TRAVELER_NUMBER_EXPIRATION_DATE: case ADDRESS_HOME_ZIP_PREFIX: case ADDRESS_HOME_ZIP_SUFFIX: case MAX_VALID_FIELD_TYPE:
diff --git a/components/autofill/core/browser/field_types.cc b/components/autofill/core/browser/field_types.cc index b7468bf6..dbcbd8c 100644 --- a/components/autofill/core/browser/field_types.cc +++ b/components/autofill/core/browser/field_types.cc
@@ -173,6 +173,9 @@ {"NATIONAL_ID_CARD_EXPIRATION_DATE", NATIONAL_ID_CARD_EXPIRATION_DATE}, {"NATIONAL_ID_CARD_ISSUE_DATE", NATIONAL_ID_CARD_ISSUE_DATE}, {"NATIONAL_ID_CARD_ISSUING_COUNTRY", NATIONAL_ID_CARD_ISSUING_COUNTRY}, + {"KNOWN_TRAVELER_NUMBER", KNOWN_TRAVELER_NUMBER}, + {"KNOWN_TRAVELER_NUMBER_EXPIRATION_DATE", + KNOWN_TRAVELER_NUMBER_EXPIRATION_DATE}, {"EMAIL_OR_LOYALTY_MEMBERSHIP_ID", EMAIL_OR_LOYALTY_MEMBERSHIP_ID}}); bool IsFillableFieldType(FieldType field_type) { @@ -301,6 +304,8 @@ case NATIONAL_ID_CARD_EXPIRATION_DATE: case NATIONAL_ID_CARD_ISSUE_DATE: case NATIONAL_ID_CARD_ISSUING_COUNTRY: + case KNOWN_TRAVELER_NUMBER: + case KNOWN_TRAVELER_NUMBER_EXPIRATION_DATE: return true; // Not fillable credential fields. @@ -398,6 +403,8 @@ case NATIONAL_ID_CARD_EXPIRATION_DATE: case NATIONAL_ID_CARD_ISSUE_DATE: case NATIONAL_ID_CARD_ISSUING_COUNTRY: + case KNOWN_TRAVELER_NUMBER: + case KNOWN_TRAVELER_NUMBER_EXPIRATION_DATE: return ""; case NUMERIC_QUANTITY: return "Numeric quantity";
diff --git a/components/autofill/core/browser/field_types.h b/components/autofill/core/browser/field_types.h index 332ae7e05..ed4591e4 100644 --- a/components/autofill/core/browser/field_types.h +++ b/components/autofill/core/browser/field_types.h
@@ -524,7 +524,12 @@ NATIONAL_ID_CARD_ISSUE_DATE = 192, NATIONAL_ID_CARD_ISSUING_COUNTRY = 193, - // Types 194 to 200 are not used on the client yet, but will likely be added + // Types corresponding to the "Known traveler" entity from + // components/autofill/core/browser/data_model/autofill_ai/entity_schema.json. + KNOWN_TRAVELER_NUMBER = 194, + KNOWN_TRAVELER_NUMBER_EXPIRATION_DATE = 203, + + // Types 195 to 200 are not used on the client yet, but will likely be added // in the future. // ADDRESS_HOME_ZIP = ADDRESS_HOME_ZIP_PREFIX + separator + @@ -543,7 +548,7 @@ // If the newly added type is a storable type of AutofillProfile, update // AutofillProfile.StorableTypes in // tools/metrics/histograms/metadata/autofill/histograms.xml. - MAX_VALID_FIELD_TYPE = 203, + MAX_VALID_FIELD_TYPE = 204, }; // LINT.ThenChange(//chrome/common/extensions/api/autofill_private.idl) @@ -652,7 +657,7 @@ (187 <= t && t <= 188) || // Types for date of birth, gender, and flight reservation are not // used yet, but will likely be added in the future. - (194 <= t && t <= 200); + (195 <= t && t <= 200); }; return is_invalid(raw_value) ? fallback_value : static_cast<FieldType>(raw_value); // nocheck @@ -815,6 +820,8 @@ case NATIONAL_ID_CARD_ISSUE_DATE: case NATIONAL_ID_CARD_EXPIRATION_DATE: case NATIONAL_ID_CARD_ISSUING_COUNTRY: + case KNOWN_TRAVELER_NUMBER: + case KNOWN_TRAVELER_NUMBER_EXPIRATION_DATE: return FieldTypeGroup::kAutofillAi; case PASSWORD:
diff --git a/components/autofill/core/browser/field_types_unittest.cc b/components/autofill/core/browser/field_types_unittest.cc index 9f2ce86..fd1e594 100644 --- a/components/autofill/core/browser/field_types_unittest.cc +++ b/components/autofill/core/browser/field_types_unittest.cc
@@ -147,6 +147,8 @@ NATIONAL_ID_CARD_EXPIRATION_DATE, NATIONAL_ID_CARD_ISSUE_DATE, NATIONAL_ID_CARD_ISSUING_COUNTRY, + KNOWN_TRAVELER_NUMBER, + KNOWN_TRAVELER_NUMBER_EXPIRATION_DATE, ADDRESS_HOME_ZIP_PREFIX, ADDRESS_HOME_ZIP_SUFFIX, };
diff --git a/components/autofill/core/browser/filling/addresses/field_filling_address_util.cc b/components/autofill/core/browser/filling/addresses/field_filling_address_util.cc index d671d8d9..c118c985 100644 --- a/components/autofill/core/browser/filling/addresses/field_filling_address_util.cc +++ b/components/autofill/core/browser/filling/addresses/field_filling_address_util.cc
@@ -180,28 +180,29 @@ // into the input `field`. std::u16string GetValueForProfileForInput(const AutofillProfile& profile, const std::string& app_locale, - const AutofillType& field_type, + const AutofillType& autofill_type, const FormFieldData& field_data, std::string* failure_to_fill) { - const std::u16string value = profile.GetInfo(field_type, app_locale); + const std::u16string value = profile.GetInfo(autofill_type, app_locale); if (value.empty()) { return {}; } - if (field_type.group() == FieldTypeGroup::kPhone) { + const FieldType field_type = autofill_type.GetAddressType(); + if (GroupTypeOfFieldType(field_type) == FieldTypeGroup::kPhone) { return GetPhoneNumberValueForInput( field_data.max_length(), value, profile.GetInfo(PHONE_HOME_CITY_AND_NUMBER, app_locale)); } - if (field_type.GetStorableType() == ADDRESS_HOME_STREET_ADDRESS) { + if (field_type == ADDRESS_HOME_STREET_ADDRESS) { return GetStreetAddressForInput(value, profile.language_code(), field_data.form_control_type()); } - if (field_type.GetStorableType() == ADDRESS_HOME_STATE) { + if (field_type == ADDRESS_HOME_STATE) { return GetStateTextForInput( value, data_util::GetCountryCodeWithFallback(profile, app_locale), field_data.max_length(), failure_to_fill); } - if (IsAlternativeNameType(field_type.GetStorableType())) { + if (IsAlternativeNameType(field_type)) { return GetAlternativeNameForInput(value, profile.GetAddressCountryCode(), field_data); } @@ -241,21 +242,22 @@ std::pair<std::u16string, FieldType> GetFillingValueAndTypeForProfile( const AutofillProfile& profile, const std::string& app_locale, - const AutofillType& field_type, + const AutofillType& autofill_type, const FormFieldData& field_data, AddressNormalizer* address_normalizer, std::string* failure_to_fill) { - CHECK(IsAddressType(field_type.GetStorableType())); + const FieldType field_type = autofill_type.GetAddressType(); + CHECK_NE(field_type, UNKNOWN_TYPE); std::u16string value = GetValueForProfileForInput( - profile, app_locale, field_type, field_data, failure_to_fill); + profile, app_locale, autofill_type, field_data, failure_to_fill); if (field_data.IsSelectElement() && !value.empty()) { value = GetValueForProfileSelectControl( - profile, value, app_locale, field_data.options(), - field_type.GetStorableType(), address_normalizer, failure_to_fill); + profile, value, app_locale, field_data.options(), field_type, + address_normalizer, failure_to_fill); } - return {value, field_type.GetStorableType()}; + return {value, field_type}; } std::u16string GetPhoneNumberValueForInput(
diff --git a/components/autofill/core/browser/form_import/form_data_importer.cc b/components/autofill/core/browser/form_import/form_data_importer.cc index ff769c3..fda1e02aa7 100644 --- a/components/autofill/core/browser/form_import/form_data_importer.cc +++ b/components/autofill/core/browser/form_import/form_data_importer.cc
@@ -444,7 +444,7 @@ // Relevant sections for address fields. std::map<Section, std::vector<const AutofillField*>> section_fields; for (const auto& field : form) { - if (IsAddressType(field->Type().GetStorableType())) { + if (field->Type().GetAddressType() != UNKNOWN_TYPE) { section_fields[field->section()].push_back(field.get()); } } @@ -588,10 +588,10 @@ continue; } - FieldType field_type = field->Type().GetStorableType(); + FieldType field_type = field->Type().GetAddressType(); // Only address types are relevant in this function, other types are treated // in different flows. - if (!IsAddressType(field_type)) { + if (field_type == UNKNOWN_TYPE) { continue; } has_address_related_fields = true;
diff --git a/components/autofill/core/browser/form_parsing/address_field_parser_ng.cc b/components/autofill/core/browser/form_parsing/address_field_parser_ng.cc index 9856299..ea820135 100644 --- a/components/autofill/core/browser/form_parsing/address_field_parser_ng.cc +++ b/components/autofill/core/browser/form_parsing/address_field_parser_ng.cc
@@ -691,6 +691,8 @@ case NATIONAL_ID_CARD_EXPIRATION_DATE: case NATIONAL_ID_CARD_ISSUE_DATE: case NATIONAL_ID_CARD_ISSUING_COUNTRY: + case KNOWN_TRAVELER_NUMBER: + case KNOWN_TRAVELER_NUMBER_EXPIRATION_DATE: case ADDRESS_HOME_ZIP_PREFIX: case ADDRESS_HOME_ZIP_SUFFIX: case MAX_VALID_FIELD_TYPE:
diff --git a/components/autofill/core/browser/form_structure_rationalization_engine_unittest.cc b/components/autofill/core/browser/form_structure_rationalization_engine_unittest.cc index 01cf41ae..c091b11 100644 --- a/components/autofill/core/browser/form_structure_rationalization_engine_unittest.cc +++ b/components/autofill/core/browser/form_structure_rationalization_engine_unittest.cc
@@ -17,6 +17,7 @@ namespace autofill::rationalization { namespace { +using ::testing::Contains; using ::testing::ElementsAre; BASE_FEATURE(kTestFeatureForFormStructureRationalizationEngine, @@ -45,18 +46,23 @@ f->set_label(t.label); f->SetTypeTo(AutofillType(t.field_type), AutofillPredictionSource::kHeuristics); - DCHECK_EQ(f->Type().GetStorableType(), t.field_type); + DCHECK(f->Type().GetTypes().contains(t.field_type)); + DCHECK_EQ(f->Type().GetTypes(), FieldTypeSet({t.field_type})); } return result; } -std::vector<FieldType> GetTypes( +std::vector<FieldTypeSet> GetTypes( const std::vector<std::unique_ptr<AutofillField>>& fields) { - std::vector<FieldType> server_types; - std::ranges::transform( - fields, std::back_inserter(server_types), - [](const auto& field) { return field->Type().GetStorableType(); }); - return server_types; + std::vector<FieldTypeSet> types; + for (const auto& field : fields) { + types.push_back(field->Type().GetTypes()); + } + return types; +} + +auto FieldTypesAre(auto... types) { + return ElementsAre(FieldTypeSet{types}...); } PatternFile GetPatternFile() { @@ -107,7 +113,7 @@ EXPECT_EQ(rule.environment_condition->feature, &kTestFeatureForFormStructureRationalizationEngine); EXPECT_THAT(rule.environment_condition->country_list, - testing::ElementsAre(GeoIpCountryCode("MX"))); + ElementsAre(GeoIpCountryCode("MX"))); EXPECT_EQ(rule.trigger_field.location, FieldLocation::kTriggerField); EXPECT_EQ(rule.trigger_field.possible_overall_types, @@ -207,7 +213,7 @@ AutofillField field; // Unknown type. - ASSERT_EQ(field.Type().GetStorableType(), UNKNOWN_TYPE); + ASSERT_EQ(field.Type().GetAddressType(), UNKNOWN_TYPE); EXPECT_TRUE(IsFieldConditionFulfilledIgnoringLocation( kMXContext, no_possible_types_required, field)); EXPECT_FALSE(IsFieldConditionFulfilledIgnoringLocation( @@ -215,7 +221,7 @@ // Non-matching type. field.set_heuristic_type(GetActiveHeuristicSource(), NAME_FIRST); - ASSERT_EQ(field.Type().GetStorableType(), NAME_FIRST); + ASSERT_EQ(field.Type().GetAddressType(), NAME_FIRST); EXPECT_TRUE(IsFieldConditionFulfilledIgnoringLocation( kMXContext, no_possible_types_required, field)); EXPECT_FALSE(IsFieldConditionFulfilledIgnoringLocation( @@ -223,7 +229,7 @@ // Matching type. field.set_heuristic_type(GetActiveHeuristicSource(), ADDRESS_HOME_LINE1); - ASSERT_EQ(field.Type().GetStorableType(), ADDRESS_HOME_LINE1); + ASSERT_EQ(field.Type().GetAddressType(), ADDRESS_HOME_LINE1); EXPECT_TRUE(IsFieldConditionFulfilledIgnoringLocation( kMXContext, no_possible_types_required, field)); EXPECT_TRUE(IsFieldConditionFulfilledIgnoringLocation( @@ -307,10 +313,10 @@ EXPECT_THAT( GetTypes(fields), - ElementsAre(NAME_FIRST, NAME_LAST, COMPANY_NAME, - /*changed*/ ADDRESS_HOME_STREET_ADDRESS, - /*changed*/ ADDRESS_HOME_DEPENDENT_LOCALITY, ADDRESS_HOME_ZIP, - ADDRESS_HOME_CITY, ADDRESS_HOME_STATE)); + FieldTypesAre(NAME_FIRST, NAME_LAST, COMPANY_NAME, + /*changed*/ ADDRESS_HOME_STREET_ADDRESS, + /*changed*/ ADDRESS_HOME_DEPENDENT_LOCALITY, + ADDRESS_HOME_ZIP, ADDRESS_HOME_CITY, ADDRESS_HOME_STATE)); } // Test that no actions are applied if the trigger field does not exist. @@ -336,9 +342,9 @@ EXPECT_THAT( GetTypes(fields), - ElementsAre(NAME_FIRST, NAME_LAST, COMPANY_NAME, ADDRESS_HOME_LINE1, - /*ADDRESS_HOME_LINE2,*/ ADDRESS_HOME_ZIP, ADDRESS_HOME_CITY, - ADDRESS_HOME_STATE)); + FieldTypesAre(NAME_FIRST, NAME_LAST, COMPANY_NAME, ADDRESS_HOME_LINE1, + /*ADDRESS_HOME_LINE2,*/ ADDRESS_HOME_ZIP, ADDRESS_HOME_CITY, + ADDRESS_HOME_STATE)); } // Test that no actions are applied if the additional condition field does not @@ -365,9 +371,9 @@ EXPECT_THAT( GetTypes(fields), - ElementsAre(NAME_FIRST, NAME_LAST, COMPANY_NAME, /*ADDRESS_HOME_LINE1,*/ - ADDRESS_HOME_LINE2, ADDRESS_HOME_ZIP, ADDRESS_HOME_CITY, - ADDRESS_HOME_STATE)); + FieldTypesAre(NAME_FIRST, NAME_LAST, COMPANY_NAME, /*ADDRESS_HOME_LINE1,*/ + ADDRESS_HOME_LINE2, ADDRESS_HOME_ZIP, ADDRESS_HOME_CITY, + ADDRESS_HOME_STATE)); } // Test that no actions are applied if the additional condition asks for @@ -396,9 +402,9 @@ internal::ApplyRuleIfApplicable(kMXContext, CreateTestRule(), fields); EXPECT_THAT(GetTypes(fields), - ElementsAre(NAME_FIRST, NAME_LAST, ADDRESS_HOME_LINE1, - COMPANY_NAME, ADDRESS_HOME_LINE2, ADDRESS_HOME_ZIP, - ADDRESS_HOME_CITY, ADDRESS_HOME_STATE)); + FieldTypesAre(NAME_FIRST, NAME_LAST, ADDRESS_HOME_LINE1, + COMPANY_NAME, ADDRESS_HOME_LINE2, ADDRESS_HOME_ZIP, + ADDRESS_HOME_CITY, ADDRESS_HOME_STATE)); } // Test that the kLastClassifiedPredecessor can skip unclassified predecessors. @@ -427,10 +433,10 @@ EXPECT_THAT( GetTypes(fields), - ElementsAre(NAME_FIRST, NAME_LAST, COMPANY_NAME, - /*changed*/ ADDRESS_HOME_STREET_ADDRESS, UNKNOWN_TYPE, - /*changed*/ ADDRESS_HOME_DEPENDENT_LOCALITY, ADDRESS_HOME_ZIP, - ADDRESS_HOME_CITY, ADDRESS_HOME_STATE)); + FieldTypesAre(NAME_FIRST, NAME_LAST, COMPANY_NAME, + /*changed*/ ADDRESS_HOME_STREET_ADDRESS, UNKNOWN_TYPE, + /*changed*/ ADDRESS_HOME_DEPENDENT_LOCALITY, + ADDRESS_HOME_ZIP, ADDRESS_HOME_CITY, ADDRESS_HOME_STATE)); } // Test that the actions are applied if all conditions are met. @@ -452,10 +458,10 @@ ApplyRationalizationEngineRules(kDEContext, fields, nullptr); EXPECT_THAT(GetTypes(fields), - ElementsAre(NAME_FIRST, NAME_LAST, - /*changed*/ ADDRESS_HOME_LINE1, - /*changed*/ ADDRESS_HOME_LINE2, ADDRESS_HOME_ZIP, - ADDRESS_HOME_CITY)); + FieldTypesAre(NAME_FIRST, NAME_LAST, + /*changed*/ ADDRESS_HOME_LINE1, + /*changed*/ ADDRESS_HOME_LINE2, ADDRESS_HOME_ZIP, + ADDRESS_HOME_CITY)); } // Test that a house number field not followed by an apartment is treated @@ -478,9 +484,9 @@ ApplyRationalizationEngineRules(kPLContext, fields, nullptr); EXPECT_THAT(GetTypes(fields), - ElementsAre(NAME_FIRST, NAME_LAST, ADDRESS_HOME_STREET_NAME, - /*changed*/ ADDRESS_HOME_HOUSE_NUMBER_AND_APT, - ADDRESS_HOME_ZIP, ADDRESS_HOME_CITY)); + FieldTypesAre(NAME_FIRST, NAME_LAST, ADDRESS_HOME_STREET_NAME, + /*changed*/ ADDRESS_HOME_HOUSE_NUMBER_AND_APT, + ADDRESS_HOME_ZIP, ADDRESS_HOME_CITY)); } // Test that the actions are not applied since there is apartment related field @@ -504,9 +510,9 @@ ApplyRationalizationEngineRules(kPLContext, fields, nullptr); EXPECT_THAT(GetTypes(fields), - ElementsAre(NAME_FIRST, NAME_LAST, ADDRESS_HOME_STREET_NAME, - ADDRESS_HOME_HOUSE_NUMBER, ADDRESS_HOME_APT_NUM, - ADDRESS_HOME_ZIP, ADDRESS_HOME_CITY)); + FieldTypesAre(NAME_FIRST, NAME_LAST, ADDRESS_HOME_STREET_NAME, + ADDRESS_HOME_HOUSE_NUMBER, ADDRESS_HOME_APT_NUM, + ADDRESS_HOME_ZIP, ADDRESS_HOME_CITY)); } // Test that the actions are applied if there is no next field after @@ -527,8 +533,8 @@ ApplyRationalizationEngineRules(kPLContext, fields, nullptr); EXPECT_THAT(GetTypes(fields), - ElementsAre(NAME_FIRST, NAME_LAST, ADDRESS_HOME_STREET_NAME, - /*changed*/ ADDRESS_HOME_HOUSE_NUMBER_AND_APT)); + FieldTypesAre(NAME_FIRST, NAME_LAST, ADDRESS_HOME_STREET_NAME, + /*changed*/ ADDRESS_HOME_HOUSE_NUMBER_AND_APT)); } // Verifies that fields classified as ADDRESS_HOME_LINE1 without a following @@ -550,8 +556,8 @@ ApplyRationalizationEngineRules(kPLContext, fields, nullptr); EXPECT_THAT(GetTypes(fields), - ElementsAre(NAME_FIRST, NAME_LAST, ADDRESS_HOME_STREET_ADDRESS, - /*changed*/ ADDRESS_HOME_ZIP)); + FieldTypesAre(NAME_FIRST, NAME_LAST, ADDRESS_HOME_STREET_ADDRESS, + /*changed*/ ADDRESS_HOME_ZIP)); } // Verifies that fields classified as ADDRESS_HOME_LINE1 with a following @@ -574,8 +580,8 @@ ApplyRationalizationEngineRules(kITContext, fields, nullptr); EXPECT_THAT(GetTypes(fields), - ElementsAre(NAME_FIRST, NAME_LAST, ADDRESS_HOME_LINE1, - /*changed*/ ADDRESS_HOME_LINE2, ADDRESS_HOME_ZIP)); + FieldTypesAre(NAME_FIRST, NAME_LAST, ADDRESS_HOME_LINE1, + /*changed*/ ADDRESS_HOME_LINE2, ADDRESS_HOME_ZIP)); } // Verifies that fields classified as ADDRESS_HOME_LINE1 without a following @@ -598,8 +604,8 @@ EXPECT_THAT( GetTypes(fields), - ElementsAre(NAME_FIRST, NAME_LAST, - /*changed*/ ADDRESS_HOME_STREET_ADDRESS, ADDRESS_HOME_ZIP)); + FieldTypesAre(NAME_FIRST, NAME_LAST, + /*changed*/ ADDRESS_HOME_STREET_ADDRESS, ADDRESS_HOME_ZIP)); } // Test that a house number field not followed by an apartment is treated @@ -622,9 +628,9 @@ ApplyRationalizationEngineRules(kNLContext, fields, nullptr); EXPECT_THAT(GetTypes(fields), - ElementsAre(NAME_FIRST, NAME_LAST, ADDRESS_HOME_STREET_NAME, - /*changed*/ ADDRESS_HOME_HOUSE_NUMBER_AND_APT, - ADDRESS_HOME_ZIP, ADDRESS_HOME_CITY)); + FieldTypesAre(NAME_FIRST, NAME_LAST, ADDRESS_HOME_STREET_NAME, + /*changed*/ ADDRESS_HOME_HOUSE_NUMBER_AND_APT, + ADDRESS_HOME_ZIP, ADDRESS_HOME_CITY)); } // Test that the actions are not applied since there is apartment related field @@ -648,9 +654,9 @@ ApplyRationalizationEngineRules(kNLContext, fields, nullptr); EXPECT_THAT(GetTypes(fields), - ElementsAre(NAME_FIRST, NAME_LAST, ADDRESS_HOME_STREET_NAME, - ADDRESS_HOME_HOUSE_NUMBER, ADDRESS_HOME_APT_NUM, - ADDRESS_HOME_ZIP, ADDRESS_HOME_CITY)); + FieldTypesAre(NAME_FIRST, NAME_LAST, ADDRESS_HOME_STREET_NAME, + ADDRESS_HOME_HOUSE_NUMBER, ADDRESS_HOME_APT_NUM, + ADDRESS_HOME_ZIP, ADDRESS_HOME_CITY)); } // Test that the actions are applied if there is no next field after @@ -671,8 +677,8 @@ ApplyRationalizationEngineRules(kNLContext, fields, nullptr); EXPECT_THAT(GetTypes(fields), - ElementsAre(NAME_FIRST, NAME_LAST, ADDRESS_HOME_STREET_NAME, - /*changed*/ ADDRESS_HOME_HOUSE_NUMBER_AND_APT)); + FieldTypesAre(NAME_FIRST, NAME_LAST, ADDRESS_HOME_STREET_NAME, + /*changed*/ ADDRESS_HOME_HOUSE_NUMBER_AND_APT)); } // Tests that in India, if there is landmark field detected, but there is no @@ -696,10 +702,11 @@ ParsingContext kINContext(kIN, LanguageCode("en"), GetPatternFile()); ApplyRationalizationEngineRules(kINContext, fields, nullptr); - EXPECT_THAT(GetTypes(fields), - ElementsAre(NAME_FIRST, NAME_LAST, - /*changed*/ ADDRESS_HOME_STREET_LOCATION_AND_LOCALITY, - ADDRESS_HOME_LANDMARK, ADDRESS_HOME_CITY)); + EXPECT_THAT( + GetTypes(fields), + FieldTypesAre(NAME_FIRST, NAME_LAST, + /*changed*/ ADDRESS_HOME_STREET_LOCATION_AND_LOCALITY, + ADDRESS_HOME_LANDMARK, ADDRESS_HOME_CITY)); } // Tests that in India, if there is only one street address related field, it is @@ -721,10 +728,10 @@ ParsingContext kINContext(kIN, LanguageCode("en"), GetPatternFile()); ApplyRationalizationEngineRules(kINContext, fields, nullptr); - EXPECT_THAT( - GetTypes(fields), - ElementsAre(NAME_FIRST, NAME_LAST, - /*changed*/ ADDRESS_HOME_STREET_ADDRESS, ADDRESS_HOME_CITY)); + EXPECT_THAT(GetTypes(fields), + FieldTypesAre(NAME_FIRST, NAME_LAST, + /*changed*/ ADDRESS_HOME_STREET_ADDRESS, + ADDRESS_HOME_CITY)); } // Tests that in India, if there is locality field detected, but there is no @@ -748,10 +755,11 @@ ParsingContext kINContext(kIN, LanguageCode("en"), GetPatternFile()); ApplyRationalizationEngineRules(kINContext, fields, nullptr); - EXPECT_THAT(GetTypes(fields), - ElementsAre(NAME_FIRST, NAME_LAST, - /*changed*/ ADDRESS_HOME_STREET_LOCATION_AND_LANDMARK, - ADDRESS_HOME_DEPENDENT_LOCALITY, ADDRESS_HOME_CITY)); + EXPECT_THAT( + GetTypes(fields), + FieldTypesAre(NAME_FIRST, NAME_LAST, + /*changed*/ ADDRESS_HOME_STREET_LOCATION_AND_LANDMARK, + ADDRESS_HOME_DEPENDENT_LOCALITY, ADDRESS_HOME_CITY)); } // Tests that in India, if there is street location field detected, but there is @@ -778,9 +786,9 @@ EXPECT_THAT( GetTypes(fields), - ElementsAre(NAME_FIRST, NAME_LAST, ADDRESS_HOME_STREET_LOCATION, - /*changed*/ ADDRESS_HOME_DEPENDENT_LOCALITY_AND_LANDMARK, - ADDRESS_HOME_CITY)); + FieldTypesAre(NAME_FIRST, NAME_LAST, ADDRESS_HOME_STREET_LOCATION, + /*changed*/ ADDRESS_HOME_DEPENDENT_LOCALITY_AND_LANDMARK, + ADDRESS_HOME_CITY)); } } // namespace
diff --git a/components/autofill/core/browser/form_structure_rationalizer.cc b/components/autofill/core/browser/form_structure_rationalizer.cc index 545d2b9..d5df691 100644 --- a/components/autofill/core/browser/form_structure_rationalizer.cc +++ b/components/autofill/core/browser/form_structure_rationalizer.cc
@@ -64,7 +64,7 @@ // This phone number rationalization marks all but the first phone number as // `set_only_fill_when_focused(true)`. Since it doesn't change the types, it // intentionally uses the rationalized `Type()` (over the `ComputedType()`). - FieldType current_field_type = field->Type().GetStorableType(); + const FieldType current_field_type = field->Type().GetAddressType(); switch (current_field_type) { case PHONE_HOME_NUMBER: found_number_field = field; @@ -162,7 +162,7 @@ // |only_fill_when_focused| field to true. for (AutofillField* field : fields) { // As above, using the rationalized `Type()` is intentional. - FieldType current_field_type = field->Type().GetStorableType(); + const FieldType current_field_type = field->Type().GetAddressType(); switch (current_field_type) { case PHONE_HOME_NUMBER: case PHONE_HOME_NUMBER_PREFIX: @@ -807,13 +807,13 @@ return; } for (auto field = fields_->begin() + 1; field != fields_->end(); ++field) { - if ((*field)->ComputedType().GetStorableType() != ADDRESS_HOME_LINE2) { + if ((*field)->ComputedType().GetAddressType() != ADDRESS_HOME_LINE2) { continue; } // Rationalize a preceding street address belonging to the same section // unless it's a server override. AutofillField& previous_field = **(field - 1); - if (previous_field.ComputedType().GetStorableType() != + if (previous_field.ComputedType().GetAddressType() != ADDRESS_HOME_STREET_ADDRESS || previous_field.section() != (*field)->section() || previous_field.server_type_prediction_is_override()) { @@ -835,7 +835,7 @@ } for (auto field = fields_->begin(); field != fields_->end() - 1; ++field) { const bool first_is_between_streets = - (*field)->ComputedType().GetStorableType() == + (*field)->ComputedType().GetAddressType() == ADDRESS_HOME_BETWEEN_STREETS; if (!first_is_between_streets) { continue; @@ -845,9 +845,9 @@ // unless it's a server override. AutofillField& next_field = **(field + 1); const bool second_is_between_streets_1_or_2 = - next_field.ComputedType().GetStorableType() == + next_field.ComputedType().GetAddressType() == ADDRESS_HOME_BETWEEN_STREETS_1 || - next_field.ComputedType().GetStorableType() == + next_field.ComputedType().GetAddressType() == ADDRESS_HOME_BETWEEN_STREETS_2; if (!second_is_between_streets_1_or_2) { continue; @@ -873,7 +873,7 @@ // If the type is changed, logs to `log_manager`. auto change_type_and_log = [&](AutofillField& field, FieldType new_type) { - FieldType current_type = field.ComputedType().GetStorableType(); + FieldType current_type = field.ComputedType().GetAddressType(); if (current_type == new_type) { return; } @@ -889,7 +889,7 @@ // Indicates whether the previous field was a phone country code. bool preceding_phone_country_code = false; for (const std::unique_ptr<AutofillField>& field : *fields_) { - FieldType type = field->ComputedType().GetStorableType(); + FieldType type = field->ComputedType().GetAddressType(); if (type == PHONE_HOME_CITY_AND_NUMBER || type == PHONE_HOME_CITY_AND_NUMBER_WITHOUT_TRUNK_PREFIX) { change_type_and_log(*field, @@ -921,8 +921,8 @@ // Group ADDRESS_HOME_STREET_ADDRESS `fields_` by section. std::map<Section, std::vector<AutofillField*>> street_address_fields; for (const std::unique_ptr<AutofillField>& field : *fields_) { - if (field->IsFocusable() && field->ComputedType().GetStorableType() == - ADDRESS_HOME_STREET_ADDRESS) { + if (field->IsFocusable() && + field->ComputedType().GetAddressType() == ADDRESS_HOME_STREET_ADDRESS) { street_address_fields[field->section()].push_back(field.get()); } } @@ -955,7 +955,7 @@ // [Ref: https://en.wikipedia.org/wiki/List_of_postal_codes] constexpr size_t kMaxZipCodePartLength = 5; auto has_zip_type = [](const std::unique_ptr<AutofillField>& field) { - FieldType type = field->Type().GetStorableType(); + FieldType type = field->ComputedType().GetAddressType(); return field->is_visible() && (type == ADDRESS_HOME_ZIP || type == ADDRESS_HOME_ZIP_SUFFIX); }; @@ -1041,12 +1041,12 @@ PHONE_HOME_CITY_AND_NUMBER_WITHOUT_TRUNK_PREFIX}; if (std::ranges::any_of(*fields_, [&](const auto& field) { return kRelevantPhoneTypes.contains( - field->ComputedType().GetStorableType()); + field->ComputedType().GetAddressType()); })) { return; } for (const std::unique_ptr<AutofillField>& field : *fields_) { - if (field->ComputedType().GetStorableType() == PHONE_HOME_COUNTRY_CODE) { + if (field->ComputedType().GetAddressType() == PHONE_HOME_COUNTRY_CODE) { field->SetTypeTo(AutofillType(UNKNOWN_TYPE), AutofillPredictionSource::kRationalization); LOG_AF(log_manager)
diff --git a/components/autofill/core/browser/foundations/browser_autofill_manager.cc b/components/autofill/core/browser/foundations/browser_autofill_manager.cc index 3c3d455..887f860 100644 --- a/components/autofill/core/browser/foundations/browser_autofill_manager.cc +++ b/components/autofill/core/browser/foundations/browser_autofill_manager.cc
@@ -2878,7 +2878,7 @@ FieldTypeSet field_types = [&]() -> FieldTypeSet { if (current_suggestion_type == SuggestionType::kAddressFieldByFieldFilling) { - return {trigger_autofill_field.Type().GetStorableType()}; + return {trigger_autofill_field.Type().GetAddressType()}; } // If the FormData and FormStructure do not have the same size, we assume // as a fallback that all fields are fillable. @@ -2893,7 +2893,7 @@ for (size_t i = 0; i < form_structure.field_count(); ++i) { if (auto it = skip_reasons.find(form_structure.field(i)->global_id()); it == skip_reasons.end() || it->second.empty()) { - field_types.insert(form_structure.field(i)->Type().GetStorableType()); + field_types.insert(form_structure.field(i)->Type().GetAddressType()); } } return field_types; @@ -2901,7 +2901,7 @@ return GetSuggestionsForProfiles( client(), field_types, trigger_field, - trigger_autofill_field.Type().GetStorableType(), current_suggestion_type, + trigger_autofill_field.Type().GetAddressType(), current_suggestion_type, std::move(plus_address_email_override)); }
diff --git a/components/autofill/core/browser/foundations/browser_autofill_manager_unittest.cc b/components/autofill/core/browser/foundations/browser_autofill_manager_unittest.cc index d601ef1..e0c7399 100644 --- a/components/autofill/core/browser/foundations/browser_autofill_manager_unittest.cc +++ b/components/autofill/core/browser/foundations/browser_autofill_manager_unittest.cc
@@ -1942,7 +1942,7 @@ AutofillField* autofill_field = form_structure->field(0); ASSERT_TRUE(autofill_field); ASSERT_TRUE(firstname_field.global_id() == autofill_field->global_id()); - autofill_field->set_autofilled_type(autofill_field->Type().GetStorableType()); + autofill_field->set_autofilled_type(autofill_field->Type().GetAddressType()); OnAskForValuesToFill(form, firstname_field); // Test that we sent the right values to the external delegate. @@ -3403,7 +3403,7 @@ ASSERT_TRUE(autofill_field); ASSERT_TRUE(field.global_id() == autofill_field->global_id()); field.set_is_autofilled(true); - autofill_field->set_autofilled_type(autofill_field->Type().GetStorableType()); + autofill_field->set_autofilled_type(autofill_field->Type().GetAddressType()); field.set_value(u"Elvis"); OnAskForValuesToFill(form, field); // Test that we sent the right values to the external delegate. @@ -3601,7 +3601,7 @@ CreditCard bnpl_virtual_card = test::GetVirtualCard(); bnpl_virtual_card.set_issuer_id( - autofill::ConvertToBnplIssuerIdString(BnplIssuer::IssuerId::kBnplAffirm)); + ConvertToBnplIssuerIdString(BnplIssuer::IssuerId::kBnplAffirm)); bnpl_virtual_card.set_is_bnpl_card(/*is_bnpl_card=*/true); TestPaymentsDataManager& test_paydm = static_cast<TestPaymentsDataManager&>( @@ -3677,8 +3677,7 @@ features::kAutofillEnableBuyNowPayLaterSyncing); BnplIssuer issuer = test::GetTestLinkedBnplIssuer(); CreditCard credit_card = test::GetVirtualCard(); - credit_card.set_issuer_id( - autofill::ConvertToBnplIssuerIdString(issuer.issuer_id())); + credit_card.set_issuer_id(ConvertToBnplIssuerIdString(issuer.issuer_id())); credit_card.set_is_bnpl_card(/*is_bnpl_card=*/true); test_api(client().GetPersonalDataManager().payments_data_manager()) .AddBnplIssuer(issuer); @@ -4568,7 +4567,7 @@ ADDRESS_HOME_LINE2, ADDRESS_HOME_CITY}; for (size_t i = 0; i < server_types.size(); ++i) { EXPECT_EQ(overall_types[i], - form_structure->field(i)->Type().GetStorableType()); + form_structure->field(i)->Type().GetAddressType()); } // Simulate form submission. @@ -4593,7 +4592,7 @@ bool type_changed = autofill_field_ptr->parseable_label() == u"Address" ? true : false; expected_events.push_back(RationalizationFieldLogEvent{ - .field_type = autofill_field_ptr->Type().GetStorableType(), + .field_type = autofill_field_ptr->Type().GetAddressType(), .section_id = 1, .type_changed = type_changed, }); @@ -8183,7 +8182,7 @@ *form_structure, [&pdm_profile](const std::unique_ptr<AutofillField>& field) { return pdm_profile->token_quality() - .GetObservationTypesForFieldType(field->Type().GetStorableType()) + .GetObservationTypesForFieldType(field->Type().GetAddressType()) .empty(); })); // Submit the form and expect observations for all of the form's types. This @@ -8196,7 +8195,7 @@ *form_structure, [&pdm_profile](const std::unique_ptr<AutofillField>& field) { return pdm_profile->token_quality() - .GetObservationTypesForFieldType(field->Type().GetStorableType()) + .GetObservationTypesForFieldType(field->Type().GetAddressType()) .empty(); })); }
diff --git a/components/autofill/core/browser/metrics/autofill_metrics_utils.cc b/components/autofill/core/browser/metrics/autofill_metrics_utils.cc index 18d38f3..84e2f2a1 100644 --- a/components/autofill/core/browser/metrics/autofill_metrics_utils.cc +++ b/components/autofill/core/browser/metrics/autofill_metrics_utils.cc
@@ -57,8 +57,8 @@ DenseSet<FieldType> postal_address_field_types; for (const auto& field : form.fields()) { if (field->Type().group() == FieldTypeGroup::kAddress && - field->Type().GetStorableType() != ADDRESS_HOME_COUNTRY) { - postal_address_field_types.insert(field->Type().GetStorableType()); + field->Type().GetAddressType() != ADDRESS_HOME_COUNTRY) { + postal_address_field_types.insert(field->Type().GetAddressType()); } } return postal_address_field_types.size() >= 3 &&
diff --git a/components/autofill/core/browser/metrics/form_events/address_form_event_logger.cc b/components/autofill/core/browser/metrics/form_events/address_form_event_logger.cc index e99a15a..93990833c 100644 --- a/components/autofill/core/browser/metrics/form_events/address_form_event_logger.cc +++ b/components/autofill/core/browser/metrics/form_events/address_form_event_logger.cc
@@ -170,7 +170,7 @@ base::RecordAction( base::UserMetricsAction("Autofill_FilledProfileSuggestion")); - FieldType field_type = field.Type().GetStorableType(); + FieldType field_type = field.Type().GetAddressType(); field_types_with_shown_suggestions_.erase(field_type); field_types_with_accepted_suggestions_.insert(field_type);
diff --git a/components/autofill/core/browser/metrics/prediction_quality_metrics.cc b/components/autofill/core/browser/metrics/prediction_quality_metrics.cc index dfba60e..cad7463d 100644 --- a/components/autofill/core/browser/metrics/prediction_quality_metrics.cc +++ b/components/autofill/core/browser/metrics/prediction_quality_metrics.cc
@@ -420,6 +420,8 @@ case NATIONAL_ID_CARD_EXPIRATION_DATE: case NATIONAL_ID_CARD_ISSUE_DATE: case NATIONAL_ID_CARD_ISSUING_COUNTRY: + case KNOWN_TRAVELER_NUMBER: + case KNOWN_TRAVELER_NUMBER_EXPIRATION_DATE: NOTREACHED() << field_type << " type is not in that group."; } break;
diff --git a/components/autofill/core/browser/metrics/profile_token_quality_metrics.cc b/components/autofill/core/browser/metrics/profile_token_quality_metrics.cc index bd1d72c..25aeab67 100644 --- a/components/autofill/core/browser/metrics/profile_token_quality_metrics.cc +++ b/components/autofill/core/browser/metrics/profile_token_quality_metrics.cc
@@ -183,7 +183,7 @@ if (const AutofillProfile* profile = adm.GetProfileByGUID(*field->autofill_source_profile_guid())) { profiles_used.insert(profile); - FieldType field_type = field->Type().GetStorableType(); + FieldType field_type = field->Type().GetAddressType(); base::UmaHistogramExactLinear( base::StrCat({kHistogramPrefix, "ObservationCountBeforeSubmission.", FieldTypeToStringView(field_type)}), @@ -212,7 +212,7 @@ if (const AutofillProfile* profile = adm.GetProfileByGUID(*field->autofill_source_profile_guid())) { FieldTypeSet relevant_types = GetMetricRelevantTypes(*profile); - FieldType field_type = field->Type().GetStorableType(); + FieldType field_type = field->Type().GetAddressType(); if (!relevant_types.contains(field_type)) { continue; }
diff --git a/components/autofill/core/browser/metrics/quality_metrics.cc b/components/autofill/core/browser/metrics/quality_metrics.cc index ac7b5d26..04fb8e6 100644 --- a/components/autofill/core/browser/metrics/quality_metrics.cc +++ b/components/autofill/core/browser/metrics/quality_metrics.cc
@@ -149,7 +149,7 @@ return; } for (const std::unique_ptr<AutofillField>& field : form) { - if (IsAlternativeNameType(field->Type().GetStorableType()) && + if (IsAlternativeNameType(field->Type().GetAddressType()) && !field->value().empty()) { base::UmaHistogramEnumeration( "Autofill.SubmittedAlternativeNameFieldValueCharacterSet",
diff --git a/components/autofill/core/browser/test_utils/autofill_test_utils.cc b/components/autofill/core/browser/test_utils/autofill_test_utils.cc index 5f535be..717cddb 100644 --- a/components/autofill/core/browser/test_utils/autofill_test_utils.cc +++ b/components/autofill/core/browser/test_utils/autofill_test_utils.cc
@@ -1312,7 +1312,8 @@ std::string issuer_id, std::string currency, uint64_t min_price_in_micros, - uint64_t max_price_in_micros) { + uint64_t max_price_in_micros, + std::vector<sync_pb::PaymentInstrument_ActionRequired> actions_required) { sync_pb::PaymentInstrument payment_instrument; payment_instrument.set_instrument_id(instrument_id); payment_instrument.add_supported_rails( @@ -1327,16 +1328,25 @@ eligible_price_range->set_min_price_in_micros(min_price_in_micros); eligible_price_range->set_max_price_in_micros(max_price_in_micros); eligible_price_range->set_currency(std::move(currency)); + + for (auto& action_required : actions_required) { + payment_instrument.add_action_required(action_required); + } + return payment_instrument; } -BnplIssuer GetTestLinkedBnplIssuer(autofill::BnplIssuer::IssuerId issuer_id) { +BnplIssuer GetTestLinkedBnplIssuer( + autofill::BnplIssuer::IssuerId issuer_id, + DenseSet<PaymentInstrument::ActionRequired> action_required) { std::vector<BnplIssuer::EligiblePriceRange> eligible_price_ranges; // Currency: USD, price lower bound: $50, price upper bound: $200. eligible_price_ranges.emplace_back(/*currency=*/"USD", /*price_lower_bound=*/50'000'000, /*price_upper_bound=*/200'000'000); - return BnplIssuer(12345, issuer_id, std::move(eligible_price_ranges)); + return BnplIssuer( + /*instrument_id=*/12345, issuer_id, std::move(eligible_price_ranges), + std::move(action_required)); } BnplIssuer GetTestUnlinkedBnplIssuer() {
diff --git a/components/autofill/core/browser/test_utils/autofill_test_utils.h b/components/autofill/core/browser/test_utils/autofill_test_utils.h index d08bd69..3a0a5557 100644 --- a/components/autofill/core/browser/test_utils/autofill_test_utils.h +++ b/components/autofill/core/browser/test_utils/autofill_test_utils.h
@@ -503,12 +503,16 @@ std::string issuer_id, std::string currency, uint64_t min_price_in_micros, - uint64_t max_price_in_micros); + uint64_t max_price_in_micros, + std::vector<sync_pb::PaymentInstrument_ActionRequired> actions_required = + {}); // Returns a linked BNPL issuer with fake data. BnplIssuer GetTestLinkedBnplIssuer( autofill::BnplIssuer::IssuerId issuer_id = - autofill::BnplIssuer::IssuerId::kBnplAffirm); + autofill::BnplIssuer::IssuerId::kBnplAffirm, + DenseSet<PaymentInstrument::ActionRequired> action_required = + DenseSet<PaymentInstrument::ActionRequired>()); // Returns an unlinked BNPL issuer with fake data. BnplIssuer GetTestUnlinkedBnplIssuer();
diff --git a/components/autofill/core/browser/ui/addresses/autofill_address_util.cc b/components/autofill/core/browser/ui/addresses/autofill_address_util.cc index b9284db5..64d1feb 100644 --- a/components/autofill/core/browser/ui/addresses/autofill_address_util.cc +++ b/components/autofill/core/browser/ui/addresses/autofill_address_util.cc
@@ -429,6 +429,8 @@ case NATIONAL_ID_CARD_EXPIRATION_DATE: case NATIONAL_ID_CARD_ISSUE_DATE: case NATIONAL_ID_CARD_ISSUING_COUNTRY: + case KNOWN_TRAVELER_NUMBER: + case KNOWN_TRAVELER_NUMBER_EXPIRATION_DATE: case MAX_VALID_FIELD_TYPE: case DELIVERY_INSTRUCTIONS: case ADDRESS_HOME_SUBPREMISE:
diff --git a/components/autofill/core/browser/webdata/addresses/autofill_profile_sync_util.cc b/components/autofill/core/browser/webdata/addresses/autofill_profile_sync_util.cc index 9ffa2900..2f06dd1d 100644 --- a/components/autofill/core/browser/webdata/addresses/autofill_profile_sync_util.cc +++ b/components/autofill/core/browser/webdata/addresses/autofill_profile_sync_util.cc
@@ -669,7 +669,7 @@ // When adding field types, ensure that they don't need to be added here and // update the last checked value. // TODO(crbug.com/359768803): Handle alternative names here. - static_assert(FieldType::MAX_VALID_FIELD_TYPE == 203, + static_assert(FieldType::MAX_VALID_FIELD_TYPE == 204, "New field type needs to be reviewed for inclusion in sync"); // The profile may be in a legacy state. By calling |FinalizeAfterImport()|
diff --git a/components/autofill/core/common/autofill_prefs.cc b/components/autofill/core/common/autofill_prefs.cc index 09126d4..ec28c02 100644 --- a/components/autofill/core/common/autofill_prefs.cc +++ b/components/autofill/core/common/autofill_prefs.cc
@@ -334,6 +334,15 @@ #endif // BUILDFLAG(IS_ANDROID) } +bool IsFacilitatedPaymentsA2AEnabled(const PrefService* prefs) { +#if BUILDFLAG(IS_ANDROID) + return prefs->GetBoolean(kFacilitatedPaymentsA2AEnabled); +#else + // Default to false on other platforms as the feature is Android-only. + return false; +#endif // BUILDFLAG(IS_ANDROID) +} + void SetFacilitatedPaymentsA2ATriggeredOnce(PrefService* prefs, bool value) { #if BUILDFLAG(IS_ANDROID) prefs->SetBoolean(kFacilitatedPaymentsA2ATriggeredOnce, value);
diff --git a/components/autofill/core/common/autofill_prefs.h b/components/autofill/core/common/autofill_prefs.h index 16a3671..039b6387c 100644 --- a/components/autofill/core/common/autofill_prefs.h +++ b/components/autofill/core/common/autofill_prefs.h
@@ -240,6 +240,8 @@ bool IsFacilitatedPaymentsPixAccountLinkingEnabled(const PrefService* prefs); +bool IsFacilitatedPaymentsA2AEnabled(const PrefService* prefs); + void SetFacilitatedPaymentsA2ATriggeredOnce(PrefService* prefs, bool value); #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || \
diff --git a/components/browser_ui/theme/android/BUILD.gn b/components/browser_ui/theme/android/BUILD.gn index ac0ea497..f2a895e 100644 --- a/components/browser_ui/theme/android/BUILD.gn +++ b/components/browser_ui/theme/android/BUILD.gn
@@ -6,15 +6,13 @@ jinja_template_resources("theme_template_resources") { res_dir = "templates/res" - resources = [ - "templates/res/values-night/themes.xml", - "templates/res/values/themes.xml", - ] + resources = [ "templates/res/values/themes.xml" ] } android_resources("java_resources") { sources = [ "java/res/values-night/colors.xml", + "java/res/values-night/themes.xml", "java/res/values/attrs.xml", "java/res/values/colors.xml", ]
diff --git a/components/browser_ui/theme/android/java/res/values-night/themes.xml b/components/browser_ui/theme/android/java/res/values-night/themes.xml new file mode 100644 index 0000000..7573f85 --- /dev/null +++ b/components/browser_ui/theme/android/java/res/values-night/themes.xml
@@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright 2025 The Chromium Authors +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. +--> + +<resources> + <style name="Theme.BrowserUI.DayNight" parent="Theme.BrowserUI.Dark"/> + <style name="Theme.BrowserUI.DayNight.DialogWhenLarge" parent="Theme.BrowserUI.Dark.DialogWhenLarge"/> + <style name="Theme.BrowserUI.DayNight.AlertDialog.NoActionBar" parent="Theme.BrowserUI.Dark.AlertDialog.NoActionBar"/> +</resources>
diff --git a/components/browser_ui/theme/android/templates/res/values-night/themes.xml b/components/browser_ui/theme/android/templates/res/values-night/themes.xml deleted file mode 100644 index 98173c6..0000000 --- a/components/browser_ui/theme/android/templates/res/values-night/themes.xml +++ /dev/null
@@ -1,52 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -Copyright 2025 The Chromium Authors -Use of this source code is governed by a BSD-style license that can be -found in the LICENSE file. ---> - -<!-- -Note: This is a jinja2 template, processed at build time into the final resources. ---> - -<resources> - <!-- Colors should be mirrored by Theme.BrowserUI.DialogWhenLarge.DayNight and - Theme.BrowserUI.AlertDialog.NoActionBar.DayNight. --> - <style name="Theme.BrowserUI.DayNight" parent="Theme.BrowserUI"> - {% macro common_color_night_attributes() %} - <!-- Color palettes --> - <item name="colorPrimary">@color/baseline_primary_80</item> - <item name="colorPrimaryInverse">@color/baseline_primary_40</item> - <item name="colorOnPrimary">@color/baseline_primary_20</item> - <item name="colorPrimaryContainer">@color/baseline_primary_30</item> - <item name="colorOnPrimaryContainer">@color/baseline_primary_90</item> - <item name="colorSecondaryContainer">@color/baseline_secondary_30</item> - <item name="colorOnSecondaryContainer">@color/baseline_secondary_90</item> - <item name="android:colorBackground">@color/baseline_neutral_10</item> - <item name="colorOnBackground">@color/baseline_neutral_90</item> - <item name="colorSurface">@color/gm3_baseline_surface_dark</item> - <item name="colorOnSurface">@color/baseline_neutral_90</item> - <item name="colorSurfaceVariant">@color/baseline_neutral_variant_30</item> - <item name="colorOnSurfaceVariant">@color/baseline_neutral_variant_80</item> - <item name="colorOnSurfaceInverse">@color/baseline_neutral_20</item> - <item name="colorSurfaceBright">@color/gm3_baseline_surface_bright_dark</item> - <item name="colorSurfaceDim">@color/gm3_baseline_surface_dim_dark</item> - <item name="colorSurfaceContainerLow">@color/gm3_baseline_surface_container_low_dark</item> - <item name="colorSurfaceContainer">@color/gm3_baseline_surface_container_dark</item> - <item name="colorSurfaceContainerHigh">@color/gm3_baseline_surface_container_high_dark</item> - <item name="colorSurfaceContainerHighest">@color/gm3_baseline_surface_container_highest_dark</item> - <item name="colorOutline">@color/baseline_neutral_variant_60</item> - <item name="colorOutlineVariant">@color/baseline_neutral_variant_30</item> - <item name="colorError">@color/baseline_error_80</item> - {% endmacro %} - {{ common_color_night_attributes() }} - </style> - - <style name="Theme.BrowserUI.DialogWhenLarge.DayNight" parent="Theme.BrowserUI.DialogWhenLarge"> - {{ common_color_night_attributes() }} - </style> - - <style name="Theme.BrowserUI.AlertDialog.NoActionBar.DayNight" parent="Theme.BrowserUI.AlertDialog.NoActionBar"> - {{ common_color_night_attributes() }} - </style> -</resources>
diff --git a/components/browser_ui/theme/android/templates/res/values/themes.xml b/components/browser_ui/theme/android/templates/res/values/themes.xml index ab6bd5e..0d43fe5 100644 --- a/components/browser_ui/theme/android/templates/res/values/themes.xml +++ b/components/browser_ui/theme/android/templates/res/values/themes.xml
@@ -10,12 +10,13 @@ --> <resources xmlns:tools="http://schemas.android.com/tools"> + <!-- Light themes --> <!-- Colors should be mirrored by Theme.BrowserUI.DialogWhenLarge and Theme.BrowserUI.AlertDialog.NoActionBar. --> - <style name="Base.V21.Theme.BrowserUI" parent="Theme.Material3.DayNight.NoActionBar"> + <style name="Theme.BrowserUI.Light" parent="Theme.Material3.Light.NoActionBar"> <item name="dynamicColorThemeOverlay">@style/ThemeOverlay.BrowserUI.DynamicColors</item> - {% macro common_color_attributes() %} + {% macro color_palette_light_attributes() %} <!-- Color palettes --> <item name="colorPrimary">@color/baseline_primary_40</item> <item name="colorPrimaryDark">@android:color/black</item> @@ -42,7 +43,10 @@ <item name="colorOutline">@color/baseline_neutral_variant_50</item> <item name="colorOutlineVariant">@color/baseline_neutral_variant_80</item> <item name="colorError">@color/baseline_error_40</item> + {% endmacro %} + {{ color_palette_light_attributes() }} + {% macro other_color_attributes() %} <!-- Text colors--> <item name="android:textColorPrimary">@color/default_text_color_list</item> <item name="android:textColorSecondary">@color/default_text_color_secondary_list</item> @@ -72,8 +76,9 @@ <item name="globalLinkTextColor">?attr/colorPrimary</item> <item name="globalClickableSpanColor">?attr/colorPrimary</item> {% endmacro %} - {{ common_color_attributes() }} + {{ other_color_attributes() }} + {% macro misc_attributes() %} <!-- Switches style workaround for UI that might use SwitchPreferenceCompat while not using the theme for SettingsActivity (e.g. PageInfoView) --> <item name="switchPreferenceCompatStyle">@style/SwitchPreference</item> @@ -89,10 +94,6 @@ <item name="radioButtonStyle">@style/Widget.BrowserUI.RadioButton</item> <item name="materialSwitchStyle">@style/Widget.BrowserUI.Switch</item> - <!-- Window Properties --> - <item name="android:windowBackground">@macro/default_bg_color</item> - <item name="android:windowSplashScreenBackground" tools:targetApi="31">@color/splash_screen_bg</item> - <!-- Status bar color --> <item name="android:statusBarColor">@android:color/black</item> <item name="android:windowLightStatusBar">false</item> @@ -139,68 +140,43 @@ <!-- Close button dimensions. --> <item name="closeButtonWidth">@dimen/close_button_width</item> <item name="closeButtonHeight">@dimen/close_button_height</item> + {% endmacro %} + {{ misc_attributes() }} + + {% macro full_window_attributes() %} + <!-- Window Properties --> + <item name="android:windowBackground">@macro/default_bg_color</item> + <item name="android:windowSplashScreenBackground" tools:targetApi="31">@color/splash_screen_bg</item> + {% endmacro %} + {{ full_window_attributes() }} </style> + <style name="Theme.BrowserUI.DayNight" parent="Theme.BrowserUI.Light" /> - <style name="Base.V31.Theme.BrowserUI" parent="Base.V21.Theme.BrowserUI" /> - <style name="Base.Theme.BrowserUI" parent="Base.V31.Theme.BrowserUI" /> - <style name="Theme.BrowserUI" parent="Base.Theme.BrowserUI" /> - <!-- Overridden by night mode. --> - <style name="Theme.BrowserUI.DayNight" parent="Theme.BrowserUI" /> - - <!-- Colors should be mirrored by Base.V21.Theme.BrowserUI. --> - <style name="Theme.BrowserUI.DialogWhenLarge" parent="Theme.Material3.DayNight.DialogWhenLarge"> + <!-- Colors should be mirrored by Theme.BrowserUI --> + <style name="Theme.BrowserUI.Light.DialogWhenLarge" parent="Theme.Material3.Light.DialogWhenLarge"> <item name="dynamicColorThemeOverlay">@style/ThemeOverlay.BrowserUI.DynamicColors</item> - {{ common_color_attributes() }} - - <!-- Widgets style override--> - <item name="materialSwitchStyle">@style/Widget.BrowserUI.Switch</item> - <!-- Switches style workaround for UI that might use SwitchPreferenceCompat while not - using the theme for SettingsActivity (e.g. PageInfoView) --> - <item name="switchPreferenceCompatStyle">@style/SwitchPreference</item> - - <item name="android:elegantTextHeight">false</item> - <item name="defaultFontFamily">sans-serif</item> - <item name="defaultMediumFontFamily">sans-serif-medium</item> - - <item name="android:itemTextAppearance">@style/TextAppearance.TextLarge.Primary</item> - <item name="android:spinnerItemStyle">@style/Widget.BrowserUI.SpinnerItem</item> - <item name="android:spinnerDropDownItemStyle">@style/Widget.BrowserUI.SpinnerDropDownItemStyle</item> - - <item name="android:windowOptOutEdgeToEdgeEnforcement" tools:targetApi="35">true</item> - - <!-- Toolbar button dimensions. --> - <item name="toolbarButtonWidth">@dimen/toolbar_button_width</item> - <item name="toolbarButtonHeight">@dimen/toolbar_button_height</item> - <item name="toolbarButtonMarginHorizontal">@dimen/toolbar_button_margin_horizontal</item> - <item name="toolbarButtonMarginVertical">@dimen/toolbar_button_margin_vertical</item> - - <!-- Close button dimensions. --> - <item name="closeButtonWidth">@dimen/close_button_width</item> - <item name="closeButtonHeight">@dimen/close_button_height</item> + {{ color_palette_light_attributes() }} + {{ other_color_attributes() }} + {{ misc_attributes() }} </style> - <!-- Overridden by night mode. --> - <style name="Theme.BrowserUI.DialogWhenLarge.DayNight" parent="Theme.BrowserUI.DialogWhenLarge"/> + <style name="Theme.BrowserUI.DayNight.DialogWhenLarge" parent="Theme.BrowserUI.Light.DialogWhenLarge"/> <!-- Unlike |ThemeOverlay.BrowserUI.AlertDialog|, this is a complete theme that can be used as - an activity theme on its own. In addition to mirroring |Base.V21.Theme.BrowserUI|, this - should include all the attributes that |ThemeOverlay.BrowserUI.AlertDialog| has. + an activity theme on its own. In addition to mirroring |Theme.BrowserUI|, this should + include all the attributes that |ThemeOverlay.BrowserUI.AlertDialog| has. --> - <style name="Theme.BrowserUI.AlertDialog.NoActionBar" parent="Theme.Material3.DayNight.Dialog.Alert"> + <style name="Theme.BrowserUI.Light.AlertDialog.NoActionBar" parent="Theme.Material3.Light.Dialog.Alert"> <item name="dynamicColorThemeOverlay">@style/ThemeOverlay.BrowserUI.DynamicColors</item> - {{ common_color_attributes() }} + {{ color_palette_light_attributes() }} + {{ other_color_attributes() }} + {{ misc_attributes() }} + {% macro alert_dialog_attributes() %} <item name="android:windowBackground">@drawable/dialog_bg_no_shadow</item> <item name="android:windowTitleStyle">@style/TextAppearance.AlertDialogTitleStyle</item> - <!-- Overriding AppCompat values --> - <item name="spinnerStyle">@style/SpinnerStyle</item> - <item name="materialSwitchStyle">@style/Widget.BrowserUI.Switch</item> - <!-- Switches style workaround for UI that might use SwitchPreferenceCompat while not - using the theme for SettingsActivity (e.g. PageInfoView) --> - <item name="switchPreferenceCompatStyle">@style/SwitchPreference</item> - <!-- Depending on if the support library or framework is inflating the dialog, a different layout is used, that names this style slightly differently. WebView will use the framework version for the @@ -212,30 +188,59 @@ <!-- NoActionBar --> <item name="windowNoTitle">true</item> <item name="windowActionBar">false</item> - - <item name="android:elegantTextHeight">false</item> - <item name="defaultFontFamily">sans-serif</item> - <item name="defaultMediumFontFamily">sans-serif-medium</item> - - <item name="android:itemTextAppearance">@style/TextAppearance.TextLarge.Primary</item> - <item name="android:spinnerItemStyle">@style/Widget.BrowserUI.SpinnerItem</item> - <item name="android:spinnerDropDownItemStyle">@style/Widget.BrowserUI.SpinnerDropDownItemStyle</item> - - <item name="android:windowOptOutEdgeToEdgeEnforcement" tools:targetApi="35">true</item> - - <!-- Toolbar button dimensions. --> - <item name="toolbarButtonWidth">@dimen/toolbar_button_width</item> - <item name="toolbarButtonHeight">@dimen/toolbar_button_height</item> - <item name="toolbarButtonMarginHorizontal">@dimen/toolbar_button_margin_horizontal</item> - <item name="toolbarButtonMarginVertical">@dimen/toolbar_button_margin_vertical</item> - - <!-- Close button dimensions. --> - <item name="closeButtonWidth">@dimen/close_button_width</item> - <item name="closeButtonHeight">@dimen/close_button_height</item> + {% endmacro %} + {{ alert_dialog_attributes() }} </style> - <!-- Overridden by night mode. --> - <style name="Theme.BrowserUI.AlertDialog.NoActionBar.DayNight" parent="Theme.BrowserUI.AlertDialog.NoActionBar"/> + <style name="Theme.BrowserUI.DayNight.AlertDialog.NoActionBar" parent="Theme.BrowserUI.Light.AlertDialog.NoActionBar"/> + <!-- Dark themes --> + <style name="Theme.BrowserUI.Dark" parent="Theme.Material3.Dark.NoActionBar"> + {% macro color_palette_dark_attributes() %} + <!-- Color palettes --> + <item name="colorPrimary">@color/baseline_primary_80</item> + <item name="colorPrimaryInverse">@color/baseline_primary_40</item> + <item name="colorOnPrimary">@color/baseline_primary_20</item> + <item name="colorPrimaryContainer">@color/baseline_primary_30</item> + <item name="colorOnPrimaryContainer">@color/baseline_primary_90</item> + <item name="colorSecondaryContainer">@color/baseline_secondary_30</item> + <item name="colorOnSecondaryContainer">@color/baseline_secondary_90</item> + <item name="android:colorBackground">@color/baseline_neutral_10</item> + <item name="colorOnBackground">@color/baseline_neutral_90</item> + <item name="colorSurface">@color/gm3_baseline_surface_dark</item> + <item name="colorOnSurface">@color/baseline_neutral_90</item> + <item name="colorSurfaceVariant">@color/baseline_neutral_variant_30</item> + <item name="colorOnSurfaceVariant">@color/baseline_neutral_variant_80</item> + <item name="colorOnSurfaceInverse">@color/baseline_neutral_20</item> + <item name="colorSurfaceBright">@color/gm3_baseline_surface_bright_dark</item> + <item name="colorSurfaceDim">@color/gm3_baseline_surface_dim_dark</item> + <item name="colorSurfaceContainerLow">@color/gm3_baseline_surface_container_low_dark</item> + <item name="colorSurfaceContainer">@color/gm3_baseline_surface_container_dark</item> + <item name="colorSurfaceContainerHigh">@color/gm3_baseline_surface_container_high_dark</item> + <item name="colorSurfaceContainerHighest">@color/gm3_baseline_surface_container_highest_dark</item> + <item name="colorOutline">@color/baseline_neutral_variant_60</item> + <item name="colorOutlineVariant">@color/baseline_neutral_variant_30</item> + <item name="colorError">@color/baseline_error_80</item> + {% endmacro %} + {{ color_palette_dark_attributes() }} + {{ other_color_attributes() }} + {{ misc_attributes() }} + {{ full_window_attributes() }} + </style> + + <style name="Theme.BrowserUI.Dark.DialogWhenLarge" parent="Theme.Material3.Dark.DialogWhenLarge"> + {{ color_palette_dark_attributes() }} + {{ other_color_attributes() }} + {{ misc_attributes() }} + </style> + + <style name="Theme.BrowserUI.Dark.AlertDialog.NoActionBar" parent="Theme.Material3.Dark.Dialog.Alert"> + {{ color_palette_dark_attributes() }} + {{ other_color_attributes() }} + {{ misc_attributes() }} + {{ alert_dialog_attributes() }} + </style> + + <!-- Misc. themes --> <!-- This theme is used instead of android:style/Theme.NoDisplay so that it has the required attributes in case the context ends up being used to inflate views. --> <style name="Theme.BrowserUI.NoDisplay" parent="Theme.BrowserUI.DayNight"> @@ -261,6 +266,7 @@ <item name="android:windowOptOutEdgeToEdgeEnforcement" tools:targetApi="35">false</item> </style> + <!-- Theme overlays --> <style name="ThemeOverlay.BrowserUI.ElegantTextHeight" parent=""> <item name="android:elegantTextHeight">true</item> </style>
diff --git a/components/content_settings/browser/page_specific_content_settings.cc b/components/content_settings/browser/page_specific_content_settings.cc index 8789b8a29..0cf92d9 100644 --- a/components/content_settings/browser/page_specific_content_settings.cc +++ b/components/content_settings/browser/page_specific_content_settings.cc
@@ -875,6 +875,9 @@ content_type == ContentSettingsType::CLIPBOARD_READ_WRITE || content_type == ContentSettingsType::SENSORS || content_type == ContentSettingsType::GEOLOCATION || +#if BUILDFLAG(IS_WIN) + content_type == ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER || +#endif content_type == ContentSettingsType::NOTIFICATIONS) { const auto& it = content_settings_status_.find(content_type); if (it != content_settings_status_.end()) { @@ -901,6 +904,9 @@ content_type != ContentSettingsType::CLIPBOARD_READ_WRITE && content_type != ContentSettingsType::SENSORS && content_type != ContentSettingsType::GEOLOCATION && +#if BUILDFLAG(IS_WIN) + content_type != ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER && +#endif content_type != ContentSettingsType::NOTIFICATIONS) { return false; } @@ -1415,6 +1421,9 @@ case ContentSettingsType::ADS: case ContentSettingsType::SOUND: case ContentSettingsType::CLIPBOARD_READ_WRITE: +#if BUILDFLAG(IS_WIN) + case ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER: +#endif case ContentSettingsType::SENSORS: { ContentSetting setting = map_->GetContentSetting(current_url, current_url, content_type);
diff --git a/components/content_settings/browser/page_specific_content_settings_unittest.cc b/components/content_settings/browser/page_specific_content_settings_unittest.cc index 2649247..c4eec2e 100644 --- a/components/content_settings/browser/page_specific_content_settings_unittest.cc +++ b/components/content_settings/browser/page_specific_content_settings_unittest.cc
@@ -1545,6 +1545,32 @@ } #endif +#if BUILDFLAG(IS_WIN) +TEST_F(PageSpecificContentSettingsTest, ProtectedMediaIdentifier) { + MockPageSpecificContentSettingsDelegate* mock_delegate = + InstallMockDelegate(); + NavigateAndCommit(GURL("http://google.com")); + + PageSpecificContentSettings* pscs = PageSpecificContentSettings::GetForFrame( + web_contents()->GetPrimaryMainFrame()); + ASSERT_NE(pscs, nullptr); + + // Allowed + EXPECT_CALL(*mock_delegate, + OnContentAllowed(ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER)) + .Times(1); + pscs->OnProtectedMediaIdentifierPermissionSet( + web_contents()->GetLastCommittedURL(), /*allowed=*/true); + + // Blocked + EXPECT_CALL(*mock_delegate, + OnContentBlocked(ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER)) + .Times(1); + pscs->OnProtectedMediaIdentifierPermissionSet( + web_contents()->GetLastCommittedURL(), /*allowed=*/false); +} +#endif // BUILDFLAG(IS_WIN) + class PageSpecificContentSettingsIframeTest : public PageSpecificContentSettingsTest { public:
diff --git a/components/download/internal/common/base_file.cc b/components/download/internal/common/base_file.cc index 23323c5..cb46b44 100644 --- a/components/download/internal/common/base_file.cc +++ b/components/download/internal/common/base_file.cc
@@ -157,15 +157,23 @@ return Open(hash_so_far, bytes_wasted); } -DownloadInterruptReason BaseFile::AppendDataToFile(const char* data, - size_t data_len) { +DownloadInterruptReason BaseFile::AppendDataToFile( + base::span<const uint8_t> data) { DCHECK(!is_sparse_file_); - return WriteDataToFile(bytes_so_far_, data, data_len); + return WriteDataToFile(bytes_so_far_, data); } -DownloadInterruptReason BaseFile::WriteDataToFile(int64_t offset, - const char* data, - size_t data_len) { +DownloadInterruptReason BaseFile::AppendDataToFile(const char* data, + size_t data_len) { + UNSAFE_BUFFERS( + // SAFETY TODO(https://crbug.com/435230896): get rid of this. + return AppendDataToFile( + base::span(reinterpret_cast<const uint8_t*>(data), data_len));) +} + +DownloadInterruptReason BaseFile::WriteDataToFile( + int64_t offset, + base::span<const uint8_t> data) { // NOTE(benwells): The above DCHECK won't be present in release builds, // so we log any occurences to see how common this error is in the wild. if (detached_) @@ -177,8 +185,9 @@ } // TODO(phajdan.jr): get rid of this check. - if (data_len == 0) + if (data.size() == 0) { return DOWNLOAD_INTERRUPT_REASON_NONE; + } // Use nestable async event instead of sync event so that all the writes // belong to the same download will be grouped together. @@ -192,39 +201,46 @@ } // Writes to the file. - int64_t len = base::saturated_cast<int64_t>(data_len); - const char* current_data = data; + base::span<const uint8_t> current_data = data; int64_t current_offset = offset; - while (len > 0) { + while (!current_data.empty()) { // |write_result| may be less than |len|, and return an error on the next // write call when the disk is unavaliable. - int write_result = file_.Write(current_offset, current_data, len); - DCHECK_NE(0, write_result); - - // Report errors on file writes. - if (write_result < 0) + std::optional<size_t> write_result = + file_.Write(current_offset, current_data); + if (!write_result.has_value()) { return LogSystemError("Write", logging::GetLastSystemErrorCode()); + } + + DCHECK_NE(0u, *write_result); // Update status. - DCHECK_LE(write_result, len); - len -= write_result; - current_data += write_result; - current_offset += write_result; - bytes_so_far_ += write_result; + bytes_so_far_ += *write_result; + current_offset += *write_result; + current_data = current_data.subspan(*write_result); } CONDITIONAL_TRACE(NESTABLE_ASYNC_END1("download", "DownloadFileWrite", - download_id_, "bytes", data_len)); + download_id_, "bytes", data.size())); if (secure_hash_) - secure_hash_->Update(data, data_len); + secure_hash_->Update(data); return DOWNLOAD_INTERRUPT_REASON_NONE; } +DownloadInterruptReason BaseFile::WriteDataToFile(int64_t offset, + const char* data, + size_t data_len) { + UNSAFE_BUFFERS( + // SAFETY TODO(https://crbug.com/435230896): get rid of this. + return WriteDataToFile( + offset, + base::span(reinterpret_cast<const uint8_t*>(data), data_len));) +} + bool BaseFile::ValidateDataInFile(int64_t offset, - const char* data, - size_t data_len) { + base::span<const uint8_t> data) { if (!file_.IsValid()) return false; @@ -233,15 +249,27 @@ if (offset > bytes_so_far_) return false; - if (data_len <= 0) + if (data.size() == 0) { return true; + } - auto buffer = base::HeapArray<char>::Uninit(data_len); - int bytes_read = file_.Read(offset, buffer.data(), buffer.size()); - if (bytes_read < 0 || static_cast<size_t>(bytes_read) < data_len) + auto buffer = base::HeapArray<uint8_t>::Uninit(data.size()); + std::optional<size_t> bytes_read = file_.Read(offset, buffer.as_span()); + if (!bytes_read.has_value() || bytes_read.value() < data.size()) { return false; + } - return memcmp(data, buffer.data(), buffer.size()) == 0; + return base::span(buffer) == data; +} + +bool BaseFile::ValidateDataInFile(int64_t offset, + const char* data, + size_t data_len) { + UNSAFE_BUFFERS( + // SAFETY TODO(https://crbug.com/435230896): get rid of this. + return ValidateDataInFile( + offset, + base::span(reinterpret_cast<const uint8_t*>(data), data_len));) } DownloadInterruptReason BaseFile::Rename(const base::FilePath& new_path) {
diff --git a/components/download/public/common/base_file.h b/components/download/public/common/base_file.h index 2818f0b..84110b5b 100644 --- a/components/download/public/common/base_file.h +++ b/components/download/public/common/base_file.h
@@ -120,16 +120,20 @@ // Write a new chunk of data to the file. Returns a DownloadInterruptReason // indicating the result of the operation. Works only if |is_sparse_file| is // false. + DownloadInterruptReason AppendDataToFile(base::span<const uint8_t> data); DownloadInterruptReason AppendDataToFile(const char* data, size_t data_len); // Write a new chunk of data to the file. Returns a DownloadInterruptReason // indicating the result of the operation. DownloadInterruptReason WriteDataToFile(int64_t offset, + base::span<const uint8_t> data); + DownloadInterruptReason WriteDataToFile(int64_t offset, const char* data, size_t data_len); // Validates that the content starting from |offset| matches that of |data| // with the given length. + bool ValidateDataInFile(int64_t offset, base::span<const uint8_t> data); bool ValidateDataInFile(int64_t offset, const char* data, size_t data_len); // Rename the download file. Returns a DownloadInterruptReason indicating the
diff --git a/components/enterprise/connectors/core/content_analysis_info_base.h b/components/enterprise/connectors/core/content_analysis_info_base.h index 473f20f..ebac5c7 100644 --- a/components/enterprise/connectors/core/content_analysis_info_base.h +++ b/components/enterprise/connectors/core/content_analysis_info_base.h
@@ -37,7 +37,7 @@ virtual std::string tab_title() const = 0; virtual std::string user_action_id() const = 0; virtual std::string email() const = 0; - virtual std::string url() const = 0; + virtual const GURL& url() const = 0; virtual const GURL& tab_url() const = 0; virtual ContentAnalysisRequest::Reason reason() const = 0; virtual google::protobuf::RepeatedPtrField<
diff --git a/components/exo/client_controlled_shell_surface_unittest.cc b/components/exo/client_controlled_shell_surface_unittest.cc index a1eb58d..f352c87 100644 --- a/components/exo/client_controlled_shell_surface_unittest.cc +++ b/components/exo/client_controlled_shell_surface_unittest.cc
@@ -607,7 +607,7 @@ ui::Shadow* shadow = wm::ShadowController::GetShadowForWindow(window); ASSERT_TRUE(shadow); EXPECT_EQ(shadow->rounded_corner_radius_for_testing(), - chromeos::kTopCornerRadiusWhenRestored); + chromeos::kRoundedWindowSmallCornerRadius); shell_surface->SetPip(); root_surface->Commit(); @@ -622,7 +622,7 @@ ASSERT_TRUE(shadow); EXPECT_EQ(shadow->rounded_corner_radius_for_testing(), - chromeos::kTopCornerRadiusWhenRestored); + chromeos::kRoundedWindowSmallCornerRadius); } namespace {
diff --git a/components/facilitated_payments/core/browser/payment_link_manager.cc b/components/facilitated_payments/core/browser/payment_link_manager.cc index a30d3fd3..5e3b8a8 100644 --- a/components/facilitated_payments/core/browser/payment_link_manager.cc +++ b/components/facilitated_payments/core/browser/payment_link_manager.cc
@@ -180,10 +180,20 @@ } bool PaymentLinkManager::CanTriggerAppPaymentFlow(const GURL& page_url) { - return optimization_guide_decider_->CanApplyOptimization( - page_url, optimization_guide::proto::A2A_MERCHANT_ALLOWLIST, - /*optimization_metadata=*/nullptr) == - optimization_guide::OptimizationGuideDecision::kTrue; + if (optimization_guide_decider_->CanApplyOptimization( + page_url, optimization_guide::proto::A2A_MERCHANT_ALLOWLIST, + /*optimization_metadata=*/nullptr) != + optimization_guide::OptimizationGuideDecision::kTrue) { + return false; + } + + if (!client_->GetPaymentsDataManager()) { + // Payments data manager can be null only in tests. + return false; + } + + return client_->GetPaymentsDataManager() + ->IsFacilitatedPaymentsA2AUserPrefEnabled(); } void PaymentLinkManager::Reset() {
diff --git a/components/facilitated_payments/core/browser/payment_link_manager_unittest.cc b/components/facilitated_payments/core/browser/payment_link_manager_unittest.cc index 84863d38..d9933c3 100644 --- a/components/facilitated_payments/core/browser/payment_link_manager_unittest.cc +++ b/components/facilitated_payments/core/browser/payment_link_manager_unittest.cc
@@ -302,10 +302,9 @@ GURL supported_payment_link( "shopeepay://shopeepay.com.my?code=https://shopeepay.com.my/" "281011051692389958586862838?merchant=Walmart&amount=101¤cy=usd"); + ON_CALL(client_, GetPaymentsDataManager) + .WillByDefault(testing::Return(nullptr)); - EXPECT_CALL(client_, GetPaymentsDataManager) - .Times(1) - .WillOnce(testing::Return(nullptr)); EXPECT_CALL(client_, ShowPaymentLinkPrompt).Times(0); payment_link_manager_->TriggerPaymentLinkPushPayment( @@ -1717,4 +1716,37 @@ autofill::prefs::kFacilitatedPaymentsA2ATriggeredOnce)); } +// A2A payment prompt is not shown if the user has opted out of the A2A flow. +TEST_F(PaymentLinkManagerTestForA2AFlow, + UserOptedOut_A2APaymentPromptNotShown) { + feature_list_.InitAndEnableFeature( + payments::facilitated::kFacilitatedPaymentsEnableA2APayment); + GURL supported_payment_link( + "https://www.itmx.co.th/facilitated-payment/prompt-pay?path=fake_path"); + ON_CALL(*mock_facilitated_payments_app_info_list_, Size) + .WillByDefault(testing::Return(2)); + + // Test that when `kFacilitatedPaymentsA2AEnabled` pref is true, + // `ShowPaymentLinkPrompt` is invoked. + pref_service_.get()->SetBoolean( + autofill::prefs::kFacilitatedPaymentsA2AEnabled, true); + + EXPECT_CALL(client_, ShowPaymentLinkPrompt).Times(1); + + payment_link_manager_->TriggerPaymentLinkPushPayment( + supported_payment_link, GURL("https://www.example.com"), + ukm::UkmRecorder::GetNewSourceID()); + + // Test that when `kFacilitatedPaymentsA2AEnabled` pref is false, + // `ShowPaymentLinkPrompt` is not invoked. + pref_service_.get()->SetBoolean( + autofill::prefs::kFacilitatedPaymentsA2AEnabled, false); + + EXPECT_CALL(client_, ShowPaymentLinkPrompt).Times(0); + + payment_link_manager_->TriggerPaymentLinkPushPayment( + supported_payment_link, GURL("https://www.example.com"), + ukm::UkmRecorder::GetNewSourceID()); +} + } // namespace payments::facilitated
diff --git a/components/feature_engagement/public/feature_constants.cc b/components/feature_engagement/public/feature_constants.cc index 2934dfd..abd53e4 100644 --- a/components/feature_engagement/public/feature_constants.cc +++ b/components/feature_engagement/public/feature_constants.cc
@@ -805,6 +805,10 @@ "IPH_iOSHomepageCustomizationNewBadge", base::FEATURE_ENABLED_BY_DEFAULT); +BASE_FEATURE(kIPHiOSOneTimeDefaultBrowserNotificationFeature, + "IPH_iOSOneTimeDefaultBrowserNotification", + base::FEATURE_ENABLED_BY_DEFAULT); + // Non-FET feature. BASE_FEATURE(kDefaultBrowserEligibilitySlidingWindow, "DefaultBrowserEligibilitySlidingWindow",
diff --git a/components/feature_engagement/public/feature_constants.h b/components/feature_engagement/public/feature_constants.h index bf4a230..714db028 100644 --- a/components/feature_engagement/public/feature_constants.h +++ b/components/feature_engagement/public/feature_constants.h
@@ -345,6 +345,8 @@ FEATURE_CONSTANTS_DECLARE_FEATURE(kIPHIOSPageActionMenu); FEATURE_CONSTANTS_DECLARE_FEATURE(kIPHiOSHomepageLensNewBadge); FEATURE_CONSTANTS_DECLARE_FEATURE(kIPHiOSHomepageCustomizationNewBadge); +FEATURE_CONSTANTS_DECLARE_FEATURE( + kIPHiOSOneTimeDefaultBrowserNotificationFeature); // A feature flag to enable and parametrize the sliding window of time for a // user's eligibility to be shown a default browser promo. This is not an FET
diff --git a/components/feature_engagement/public/feature_list.cc b/components/feature_engagement/public/feature_list.cc index 3d53bff..874368e 100644 --- a/components/feature_engagement/public/feature_list.cc +++ b/components/feature_engagement/public/feature_list.cc
@@ -186,6 +186,7 @@ &kIPHiOSSharedTabGroupForeground, &kIPHiOSDefaultBrowserBannerPromoFeature, &kIPHiOSDefaultBrowserOffCyclePromoFeature, + &kIPHiOSOneTimeDefaultBrowserNotificationFeature, &kIPHiOSReminderNotificationsOverflowMenuBubbleFeature, &kIPHiOSReminderNotificationsOverflowMenuNewBadgeFeature, &kIPHiOSSettingsInOverflowMenuBubbleFeature,
diff --git a/components/feature_engagement/public/feature_list.h b/components/feature_engagement/public/feature_list.h index cdebacd..c675fd9 100644 --- a/components/feature_engagement/public/feature_list.h +++ b/components/feature_engagement/public/feature_list.h
@@ -328,6 +328,8 @@ "IPH_iOSDefaultBrowserBannerPromoFeature"); DEFINE_VARIATION_PARAM(kIPHiOSDefaultBrowserOffCyclePromoFeature, "IPH_iOSDefaultBrowserOffCyclePromo"); +DEFINE_VARIATION_PARAM(kIPHiOSOneTimeDefaultBrowserNotificationFeature, + "IPH_iOSOneTimeDefaultBrowserNotification"); DEFINE_VARIATION_PARAM(kIPHiOSReminderNotificationsOverflowMenuBubbleFeature, "IPH_iOSReminderNotificationsOverflowMenuBubbleFeature"); DEFINE_VARIATION_PARAM(
diff --git a/components/feature_engagement/public/ios_promo_feature_configuration.cc b/components/feature_engagement/public/ios_promo_feature_configuration.cc index 0652656..ff142b5 100644 --- a/components/feature_engagement/public/ios_promo_feature_configuration.cc +++ b/components/feature_engagement/public/ios_promo_feature_configuration.cc
@@ -569,6 +569,21 @@ return config; } + if (kIPHiOSOneTimeDefaultBrowserNotificationFeature.name == feature->name) { + FeatureConfig config; + config.valid = true; + config.availability = Comparator(ANY, 0); + config.session_rate = Comparator(ANY, 0); + config.storage_type = StorageType::DEVICE; + config.trigger = + EventConfig("one_time_default_browser_notification_trigger", + Comparator(EQUAL, 0), feature_engagement::kMaxStoragePeriod, + feature_engagement::kMaxStoragePeriod); + config.event_configs.insert(EventConfig( + "default_browser_promos_group_trigger", Comparator(EQUAL, 0), 14, 360)); + return config; + } + return std::nullopt; } } // namespace
diff --git a/components/google/core/common/BUILD.gn b/components/google/core/common/BUILD.gn index df7eef8..e976732 100644 --- a/components/google/core/common/BUILD.gn +++ b/components/google/core/common/BUILD.gn
@@ -30,6 +30,7 @@ deps = [ ":common", "//base", + "//base/test:test_support", "//testing/gtest", ] }
diff --git a/components/google/core/common/google_util.cc b/components/google/core/common/google_util.cc index 1ae7ee5f..dc38e01 100644 --- a/components/google/core/common/google_util.cc +++ b/components/google/core/common/google_util.cc
@@ -13,6 +13,7 @@ #include "base/command_line.h" #include "base/containers/fixed_flat_set.h" +#include "base/feature_list.h" #include "base/no_destructor.h" #include "base/strings/strcat.h" #include "base/strings/string_number_conversions.h" @@ -145,6 +146,10 @@ } // namespace +BASE_FEATURE(kIsViewerGoogleSearchUrl, + "IsViewerGoogleSearchUrl", + base::FEATURE_ENABLED_BY_DEFAULT); + // Global functions ----------------------------------------------------------- const char kGoogleHomepageURL[] = "https://www.google.com/"; @@ -271,7 +276,12 @@ // Make sure the path is a known search path. std::string_view path(url.path_piece()); bool is_home_page_base = IsPathHomePageBase(path); - if (!is_home_page_base && path != "/search" && path != "/imgres") { + bool is_search_url = + is_home_page_base || path == "/search" || path == "/imgres"; + if (base::FeatureList::IsEnabled(kIsViewerGoogleSearchUrl)) { + is_search_url |= path == "/viewer" || base::StartsWith(path, "/viewer/"); + } + if (!is_search_url) { return false; }
diff --git a/components/google/core/common/google_util.h b/components/google/core/common/google_util.h index be404bb..c1734c2 100644 --- a/components/google/core/common/google_util.h +++ b/components/google/core/common/google_util.h
@@ -11,11 +11,17 @@ #include <string_view> #include <vector> +#include "base/feature_list.h" + class GURL; // This namespace provides various helpers around handling Google-related URLs. namespace google_util { +// TODO(crbug.com/435419641): Remove this feature flag once it is stable. +// Feature flag to consider /viewer/ URLs as search URL. +BASE_DECLARE_FEATURE(kIsViewerGoogleSearchUrl); + // The Google Search mode of a page. This corresponds to the tab (e.g. web // result, image results, video results, etc.) the user is on. Used in UKM // logging so don't remove or reorder values. Update |GoogleSearchMode| in
diff --git a/components/google/core/common/google_util_unittest.cc b/components/google/core/common/google_util_unittest.cc index c26b17d..983d793 100644 --- a/components/google/core/common/google_util_unittest.cc +++ b/components/google/core/common/google_util_unittest.cc
@@ -6,6 +6,7 @@ #include "base/command_line.h" #include "base/strings/strcat.h" +#include "base/test/scoped_feature_list.h" #include "components/google/core/common/google_switches.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" @@ -132,6 +133,8 @@ } TEST(GoogleUtilTest, GoodSearches) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature(google_util::kIsViewerGoogleSearchUrl); constexpr struct { const char* before_query_param; const char* after_query_param; @@ -156,6 +159,9 @@ {"www.google.com/webhp?name=bob#", "=something"}, {"www.google.com/webhp?name=bob#age=24&", "=thing"}, + // Viewing a google search card is still considered a search + {"www.google.com/viewer/places?", "=something"}, + {"www.google.com/#", "=something"}, {"www.google.com/#name=bob&", "=something"}, {"www.google.com/?name=bob#", "=something"}, @@ -245,6 +251,13 @@ } } +TEST(GoogleUtilTest, GoodSearchesWithoutViewer) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndDisableFeature(google_util::kIsViewerGoogleSearchUrl); + EXPECT_FALSE(IsSearch("https://www.google.com/viewer/places?q=something")); + EXPECT_TRUE(IsSearch("https://www.google.com/search?q=something")); +} + TEST(GoogleUtilTest, GoogleDomains) { // Test some good Google domains (valid TLDs). EXPECT_TRUE(IsGoogleDomainUrl(GURL("http://www.google.com"),
diff --git a/components/metrics/dwa/dwa_service.cc b/components/metrics/dwa/dwa_service.cc index f8a6091c..6466c865 100644 --- a/components/metrics/dwa/dwa_service.cc +++ b/components/metrics/dwa/dwa_service.cc
@@ -4,13 +4,17 @@ #include "components/metrics/dwa/dwa_service.h" +#include <algorithm> #include <memory> #include <string> #include <string_view> #include "base/containers/fixed_flat_set.h" #include "base/i18n/timezone.h" +#include "base/json/json_writer.h" +#include "base/metrics/metrics_hashes.h" #include "base/rand_util.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/version.h" #include "components/metrics/dwa/dwa_pref_names.h" @@ -22,6 +26,23 @@ namespace metrics::dwa { +namespace { + +// TODO(crbug.com/411369489): Encrypt private metric report. Current +// implementation only serializes the report and moves the serialized report +// into the encrypted field without actually encrypting it. +::private_metrics::EncryptedPrivateMetricReport EncryptPrivateMetricReport( + const ::private_metrics::PrivateMetricReport& report) { + std::string serialized_log; + report.SerializeToString(&serialized_log); + + ::private_metrics::EncryptedPrivateMetricReport encrypted_report; + *encrypted_report.mutable_encrypted_report() = std::move(serialized_log); + return encrypted_report; +} + +} // namespace + // Set of countries in the European Economic Area. Used by // RecordCoarseSystemInformation to set geo_designation fields in // CoarseSystemInfo. These will need to be manually updated using @@ -119,7 +140,7 @@ return; } - BuildAndStoreLog(reason); + BuildDwaReportAndStoreLog(reason); reporting_service_.unsent_log_store()->TrimAndPersistUnsentLogs(true); } @@ -229,16 +250,78 @@ return client_id; } +// static +uint64_t DwaService::HashCoarseSystemInfo( + const ::dwa::CoarseSystemInfo& coarse_system_info) { + return base::HashMetricName(base::JoinString( + {base::NumberToString(coarse_system_info.channel()), + base::NumberToString(coarse_system_info.platform()), + base::NumberToString(coarse_system_info.geo_designation()), + base::NumberToString(coarse_system_info.client_age()), + base::NumberToString(coarse_system_info.milestone_prefix_trimmed()), + base::NumberToString(coarse_system_info.is_ukm_enabled())}, + "-")); +} + +// static +std::optional<uint64_t> DwaService::HashRepeatedFieldTrials( + const google::protobuf::RepeatedPtrField< + ::metrics::SystemProfileProto::FieldTrial>& repeated_field_trials) { + std::vector<std::pair<uint32_t, uint32_t>> field_trials_vector; + field_trials_vector.reserve(repeated_field_trials.size()); + for (const auto& field_trials : repeated_field_trials) { + field_trials_vector.emplace_back(field_trials.name_id(), + field_trials.group_id()); + } + + std::sort(field_trials_vector.begin(), field_trials_vector.end()); + + base::Value::List value_list; + for (const auto& field_trials : field_trials_vector) { + base::Value::List field_trial_pair; + field_trial_pair.Append(base::NumberToString(field_trials.first)); + field_trial_pair.Append(base::NumberToString(field_trials.second)); + value_list.Append(std::move(field_trial_pair)); + } + + auto serialized_json = base::WriteJson(value_list); + if (!serialized_json.has_value()) { + return std::nullopt; + } + return base::HashMetricName(serialized_json.value()); +} + +// static +std::vector<uint64_t> DwaService::BuildKAnonymityBuckets( + const ::dwa::DeidentifiedWebAnalyticsEvent& dwa_event) { + auto coarse_system_info_hash = + HashCoarseSystemInfo(dwa_event.coarse_system_info()); + auto field_trials_hash = HashRepeatedFieldTrials(dwa_event.field_trials()); + + if (!field_trials_hash.has_value()) { + return std::vector<uint64_t>(); + } + + std::vector<uint64_t> k_anonymity_buckets; + k_anonymity_buckets.push_back(base::HashMetricName( + base::JoinString({base::NumberToString(coarse_system_info_hash), + base::NumberToString(dwa_event.event_hash()), + base::NumberToString(field_trials_hash.value())}, + "-"))); + return k_anonymity_buckets; +} + void DwaService::RotateLog() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (!reporting_service_.unsent_log_store()->has_unsent_logs()) { - BuildAndStoreLog(metrics::MetricsLogsEventManager::CreateReason::kPeriodic); + BuildDwaReportAndStoreLog( + metrics::MetricsLogsEventManager::CreateReason::kPeriodic); } reporting_service_.Start(); scheduler_->RotationFinished(); } -void DwaService::BuildAndStoreLog( +void DwaService::BuildDwaReportAndStoreLog( metrics::MetricsLogsEventManager::CreateReason reason) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); // There are no new events, so no new logs should be created. @@ -266,6 +349,51 @@ reason); } +void DwaService::BuildPrivateMetricReportAndStoreLog( + metrics::MetricsLogsEventManager::CreateReason reason) { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + // There are no new events, so no new logs should be created. + if (!recorder_->HasEntries()) { + return; + } + + ::private_metrics::PrivateMetricReport report; + report.set_ephemeral_id(GetEphemeralClientId(*pref_service_)); + + std::vector<::dwa::DeidentifiedWebAnalyticsEvent> dwa_events = + recorder_->TakeDwaEvents(); + + for (auto& dwa_event : dwa_events) { + RecordCoarseSystemInformation(*client_, *pref_service_, + dwa_event.mutable_coarse_system_info()); + + auto k_anonymity_buckets = BuildKAnonymityBuckets(dwa_event); + // Since there are no k-anonymity buckets, the k-anonymity filter cannot be + // enforced. As such, the bucket should be dropped. + // TODO(crbug.com/432764678): Add UMA metric when dwa_event is dropped due + // to empty k-anonymity buckets. + if (k_anonymity_buckets.empty()) { + continue; + } + + auto* event = report.add_events(); + event->mutable_k_anonymity_buckets()->Add( + std::make_move_iterator(k_anonymity_buckets.begin()), + std::make_move_iterator(k_anonymity_buckets.end())); + *event->mutable_dwa_event() = std::move(dwa_event); + } + + ::private_metrics::EncryptedPrivateMetricReport encrypted_report = + EncryptPrivateMetricReport(report); + + std::string serialized_log; + encrypted_report.SerializeToString(&serialized_log); + + LogMetadata metadata; + reporting_service_.unsent_log_store()->StoreLog(serialized_log, metadata, + reason); +} + // static void DwaService::RegisterPrefs(PrefRegistrySimple* registry) { registry->RegisterUint64Pref(prefs::kDwaClientId, 0u);
diff --git a/components/metrics/dwa/dwa_service.h b/components/metrics/dwa/dwa_service.h index 07dca76b..092333d 100644 --- a/components/metrics/dwa/dwa_service.h +++ b/components/metrics/dwa/dwa_service.h
@@ -7,6 +7,8 @@ #include <cstdint> #include <memory> +#include <optional> +#include <vector> #include "base/memory/weak_ptr.h" #include "base/sequence_checker.h" @@ -18,6 +20,7 @@ #include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_service.h" #include "third_party/metrics_proto/dwa/deidentified_web_analytics.pb.h" +#include "third_party/metrics_proto/private_metrics/private_metrics.pb.h" namespace metrics::dwa { @@ -54,6 +57,39 @@ // would mean undercounting for k-anonymity). static uint64_t GetEphemeralClientId(PrefService& local_state); + // Computes a persistent hash for the given `coarse_system_info`. + static uint64_t HashCoarseSystemInfo( + const ::dwa::CoarseSystemInfo& coarse_system_info); + + // Computes a persistent hash for a repeated list of field trials names and + // groups. An empty optional is returned if `repeated_field_trials` cannot be + // serialized into a value. + static std::optional<uint64_t> HashRepeatedFieldTrials( + const google::protobuf::RepeatedPtrField< + ::metrics::SystemProfileProto::FieldTrial>& repeated_field_trials); + + // Builds the k-anonymity buckets for the `k_anonymity_buckets` field in + // PrivateMetricReport protocol buffer. Each event may contain multiple + // buckets that need to pass the k-anonymity filter. Buckets may contain + // quasi-identifiers. We treat the k-anonymity bucket + // values as opaque and do not attempt to interpret them. An empty vector is + // returned and dropped from being reported if there is an error in building + // k-anonymity buckets for `dwa_event` as there would be no way to enforce the + // k-anonymity filter without the k-anonymity buckets. For `dwa_event`, the + // combination of `dwa_event.coarse_system_info`, `dwa_event.event_hash`, and + // `dwa_event.field_trials` builds the first k-anbonymity bucket because the + // combination describes an user invoking an action. We want to verify there + // is a sufficient number of users who perform this action before allowing the + // `dwa_event` past the k-anonymity filter. Similarly, + // `dwa_event.content_metrics.content_hash` builds the second k-anonymity + // bucket because we want to confirm that the subresource's eTLD+1 is a domain + // with which a substantial number of users have interacted with. + // TODO(crbug.com/418025635): After we remove client-side aggregation of DWA + // events, we should also include `content_hash` as a k-anonymity bucket. This + // should be completed prior to 100% rollout of private metrics. + static std::vector<uint64_t> BuildKAnonymityBuckets( + const ::dwa::DeidentifiedWebAnalyticsEvent& dwa_event); + // Register prefs from `dwa_pref_names.h`. static void RegisterPrefs(PrefRegistrySimple* registry); @@ -67,7 +103,13 @@ // Constructs a new DeidentifiedWebAnalyticsReport from available data and // stores it in |unsent_log_store_|. - void BuildAndStoreLog(metrics::MetricsLogsEventManager::CreateReason reason); + void BuildDwaReportAndStoreLog( + metrics::MetricsLogsEventManager::CreateReason reason); + + // Constructs a new PrivateMetricReport from available data and + // stores it in `unsent_log_store_`. + void BuildPrivateMetricReportAndStoreLog( + metrics::MetricsLogsEventManager::CreateReason reason); // Retrieves the storage parameters to control the reporting service. static UnsentLogStore::UnsentLogStoreLimits GetLogStoreLimits();
diff --git a/components/metrics/dwa/dwa_service_unittest.cc b/components/metrics/dwa/dwa_service_unittest.cc index 58beb2a..d259395c 100644 --- a/components/metrics/dwa/dwa_service_unittest.cc +++ b/components/metrics/dwa/dwa_service_unittest.cc
@@ -64,6 +64,8 @@ void RecordTestMetric() { ::dwa::DwaEntryBuilder builder("Kangaroo.Jumped"); builder.SetContent("https://adtech.com"); + builder.AddToStudiesOfInterest("test_trial_1"); + builder.AddToStudiesOfInterest("test_trial_2"); builder.SetMetric("Length", 5); builder.Record(DwaRecorder::Get()); } @@ -164,6 +166,127 @@ EXPECT_EQ(client_id_day2, client_id_day2_later); } +TEST_F(DwaServiceTest, HashCoarseSystemInfoCreatesPersistentHash) { + ::dwa::CoarseSystemInfo coarse_system_info_1; + coarse_system_info_1.set_channel(::dwa::CoarseSystemInfo::CHANNEL_STABLE); + coarse_system_info_1.set_platform(::dwa::CoarseSystemInfo::PLATFORM_WINDOWS); + coarse_system_info_1.set_geo_designation( + ::dwa::CoarseSystemInfo::GEO_DESIGNATION_ROW); + coarse_system_info_1.set_client_age( + ::dwa::CoarseSystemInfo::CLIENT_AGE_RECENT); + coarse_system_info_1.set_milestone_prefix_trimmed(8); + coarse_system_info_1.set_is_ukm_enabled(true); + EXPECT_THAT(DwaService::HashCoarseSystemInfo(coarse_system_info_1), + testing::Eq(5379665033289076337u)); + + ::dwa::CoarseSystemInfo coarse_system_info_2; + coarse_system_info_2.set_channel(::dwa::CoarseSystemInfo::CHANNEL_STABLE); + coarse_system_info_2.set_platform(::dwa::CoarseSystemInfo::PLATFORM_WINDOWS); + coarse_system_info_2.set_geo_designation( + ::dwa::CoarseSystemInfo::GEO_DESIGNATION_ROW); + coarse_system_info_2.set_client_age( + ::dwa::CoarseSystemInfo::CLIENT_AGE_RECENT); + coarse_system_info_2.set_milestone_prefix_trimmed(9); + coarse_system_info_2.set_is_ukm_enabled(true); + EXPECT_THAT(DwaService::HashCoarseSystemInfo(coarse_system_info_2), + testing::Eq(150860663309450601u)); + + ::dwa::CoarseSystemInfo coarse_system_info_3; + coarse_system_info_3.set_channel(::dwa::CoarseSystemInfo::CHANNEL_STABLE); + coarse_system_info_3.set_platform(::dwa::CoarseSystemInfo::PLATFORM_LINUX); + coarse_system_info_3.set_geo_designation( + ::dwa::CoarseSystemInfo::GEO_DESIGNATION_EEA); + coarse_system_info_3.set_client_age( + ::dwa::CoarseSystemInfo::CLIENT_AGE_NOT_RECENT); + coarse_system_info_3.set_milestone_prefix_trimmed(3); + coarse_system_info_3.set_is_ukm_enabled(true); + EXPECT_THAT(DwaService::HashCoarseSystemInfo(coarse_system_info_3), + testing::Eq(5124987072588276635u)); +} + +TEST_F(DwaServiceTest, HashRepeatedFieldTrialsCreatesPersistentHash) { + ::metrics::SystemProfileProto::FieldTrial field_trial_1; + field_trial_1.set_name_id(0x11111111); + field_trial_1.set_group_id(0x22222222); + ::metrics::SystemProfileProto::FieldTrial field_trial_2; + field_trial_2.set_name_id(0x11111111); + field_trial_2.set_group_id(0x66666666); + ::metrics::SystemProfileProto::FieldTrial field_trial_3; + field_trial_3.set_name_id(0x33333333); + field_trial_3.set_group_id(0x44444444); + ::metrics::SystemProfileProto::FieldTrial field_trial_4; + field_trial_4.set_name_id(0x55555555); + field_trial_4.set_group_id(0x66666666); + + uint64_t expected_result_from_field_trial_1_and_field_trial_3 = + 784123498318573506u; + + struct { + std::vector<::metrics::SystemProfileProto::FieldTrial> input; + std::optional<uint64_t> expected_output; + } test_cases[] = { + {std::vector<::metrics::SystemProfileProto::FieldTrial>{field_trial_1, + field_trial_3}, + expected_result_from_field_trial_1_and_field_trial_3}, + {std::vector<::metrics::SystemProfileProto::FieldTrial>{field_trial_3, + field_trial_1}, + expected_result_from_field_trial_1_and_field_trial_3}, + {std::vector<::metrics::SystemProfileProto::FieldTrial>{field_trial_2, + field_trial_3}, + 10506435849301764974u}, + {std::vector<::metrics::SystemProfileProto::FieldTrial>{ + field_trial_1, field_trial_3, field_trial_4}, + 13321506181621468176u}, + }; + + for (const auto& test_case : test_cases) { + google::protobuf::RepeatedPtrField< + ::metrics::SystemProfileProto::FieldTrial> + repeated_ptr_field; + repeated_ptr_field.Add(std::make_move_iterator(test_case.input.begin()), + std::make_move_iterator(test_case.input.end())); + + EXPECT_THAT(DwaService::HashRepeatedFieldTrials(repeated_ptr_field), + testing::Eq(test_case.expected_output)); + } +} + +TEST_F(DwaServiceTest, BuildsKAnonymityBuckets) { + base::FieldTrialList::CreateFieldTrial("test_trial_1", "test_group_2") + ->Activate(); + base::FieldTrialList::CreateFieldTrial("test_trial_2", "test_group_1") + ->Activate(); + DwaRecorder::Get()->EnableRecording(); + + // Records a test metric and generate a vector of k_anonymity_buckets values. + RecordTestMetric(); + EXPECT_TRUE(DwaRecorder::Get()->HasEntries()); + + auto dwa_events = DwaRecorder::Get()->TakeDwaEvents(); + EXPECT_FALSE(dwa_events.empty()); + ASSERT_EQ(dwa_events.size(), 1u); + + auto k_anonymity_buckets = + DwaService::BuildKAnonymityBuckets(dwa_events.at(0)); + EXPECT_FALSE(k_anonymity_buckets.empty()); + ASSERT_EQ(k_anonymity_buckets.size(), 1u); + auto previous_bucket_value = k_anonymity_buckets.at(0); + + // Records another test metric and validate the two vector of + // k_anonymity_buckets values match. + RecordTestMetric(); + EXPECT_TRUE(DwaRecorder::Get()->HasEntries()); + + dwa_events = DwaRecorder::Get()->TakeDwaEvents(); + EXPECT_FALSE(dwa_events.empty()); + ASSERT_EQ(dwa_events.size(), 1u); + + k_anonymity_buckets = DwaService::BuildKAnonymityBuckets(dwa_events.at(0)); + EXPECT_FALSE(k_anonymity_buckets.empty()); + ASSERT_EQ(k_anonymity_buckets.size(), 1u); + ASSERT_EQ(previous_bucket_value, k_anonymity_buckets.at(0)); +} + TEST_F(DwaServiceEnvironmentTest, Flush) { DwaService service(&client_, &prefs_); histogram_tester_.ExpectTotalCount(kDwaInitSequenceHistogramName,
diff --git a/components/metrics/file_metrics_provider_unittest.cc b/components/metrics/file_metrics_provider_unittest.cc index 89e0d70..948d4686 100644 --- a/components/metrics/file_metrics_provider_unittest.cc +++ b/components/metrics/file_metrics_provider_unittest.cc
@@ -333,7 +333,7 @@ const base::FilePath& path) { DCHECK_LT(0U, filter_actions_remaining_); --filter_actions_remaining_; - return *filter_actions_++; + return UNSAFE_TODO(*filter_actions_++); } base::test::TaskEnvironment task_environment_;
diff --git a/components/omnibox/browser/autocomplete_match.h b/components/omnibox/browser/autocomplete_match.h index c2855c7..314f749 100644 --- a/components/omnibox/browser/autocomplete_match.h +++ b/components/omnibox/browser/autocomplete_match.h
@@ -162,7 +162,13 @@ kVerbatimProvider, // Matches that come from the verbatim provider, which // does not include the verbatim SWYT match. kHistoryEmbeddingAnswer, // Matches with type `HISTORY_EMBEDDINGS_ANSWER`. - kAiMode, // Matches that activate to the DSE's AI Mode + kAiMode, // Matches that activate the DSE's AI Mode. AIM suggestions' URLs + // are discerned by a query param `udm=50`. But deduping doesn't + // consider extra query params; `google.com/?q=query&udm=50` and + // `google.com/?q=query` would usually be deduped. `kAiMode` allows + // matches with `udm=50` in their suggest template to not be deduped + // with matches without it. But this does not apply to `udm=50` in + // the actual match URL; nor to udm values other than 50. }; // AutocompleteMatch ----------------------------------------------------------
diff --git a/components/omnibox/browser/autocomplete_result.cc b/components/omnibox/browser/autocomplete_result.cc index 01ec346..281ad48c 100644 --- a/components/omnibox/browser/autocomplete_result.cc +++ b/components/omnibox/browser/autocomplete_result.cc
@@ -1562,7 +1562,9 @@ type = AutocompleteMatchDedupeType::kVerbatimProvider; } else if (match.type == ACMatchType::CALCULATOR) { type = AutocompleteMatchDedupeType::kCalculator; - } else if (match.IsSearchAimSuggestion()) { + } else if (match.IsSearchAimSuggestion() && + omnibox_feature_configs::AiModeEchoMatch::Get() + .do_not_dedupe_aim_suggestions) { type = AutocompleteMatchDedupeType::kAiMode; } else { type = AutocompleteMatchDedupeType::kNormal;
diff --git a/components/omnibox/common/omnibox_feature_configs.cc b/components/omnibox/common/omnibox_feature_configs.cc index 53da05e..04d5816 100644 --- a/components/omnibox/common/omnibox_feature_configs.cc +++ b/components/omnibox/common/omnibox_feature_configs.cc
@@ -38,6 +38,17 @@ .Get(); } +BASE_FEATURE(AiModeEchoMatch::kAiModeEchoMatch, + "AiModeEchoMatch", + base::FEATURE_ENABLED_BY_DEFAULT); +AiModeEchoMatch::AiModeEchoMatch() { + enabled = base::FeatureList::IsEnabled(kAiModeEchoMatch); + do_not_dedupe_aim_suggestions = + base::FeatureParam<bool>(&kAiModeEchoMatch, "DoNotDedupeAimSuggestions", + do_not_dedupe_aim_suggestions) + .Get(); +} + BASE_FEATURE(ContextualSearch::kContextualSuggestionsAblateOthersWhenPresent, "ContextualSuggestionsAblateOthersWhenPresent", base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/components/omnibox/common/omnibox_feature_configs.h b/components/omnibox/common/omnibox_feature_configs.h index 71fcd9d2f..cd4bf477 100644 --- a/components/omnibox/common/omnibox_feature_configs.h +++ b/components/omnibox/common/omnibox_feature_configs.h
@@ -118,6 +118,25 @@ size_t num_non_calc_inputs; }; +// Chromium-side tweaks to accommodate AI mode echo matches from the server. +// Disabling this won't actually disable AI mode echo matches altogether; that's +// controlled server side. This just changes client side behavior to allow them +// to work well. +struct AiModeEchoMatch : Config<AiModeEchoMatch> { + DECLARE_FEATURE(kAiModeEchoMatch); + + AiModeEchoMatch(); + + bool enabled; + + // Deduping doesn't consider extra query params like `udm=50`. + // `google.com/?q=query&udm=50` and `google.com/?q=query` would usually be + // deduped. This param makes `udm=50` in the match's suggest template a + // differentiating signal in deduping. Does not apply to `udm=50` in normal + // URLs. Does not apply to e.g. `udm=49` in the suggest template. + bool do_not_dedupe_aim_suggestions = true; +}; + // A config struct for features related to contextual search in omnibox. struct ContextualSearch : Config<ContextualSearch> { ContextualSearch();
diff --git a/components/optimization_guide/core/optimization_guide_constants.cc b/components/optimization_guide/core/optimization_guide_constants.cc index 3911ccb..f9281bac 100644 --- a/components/optimization_guide/core/optimization_guide_constants.cc +++ b/components/optimization_guide/core/optimization_guide_constants.cc
@@ -36,9 +36,6 @@ kOldOptimizationGuidePredictionModelMetadataStore[] = FILE_PATH_LITERAL("optimization_guide_model_metadata_store"); -const base::FilePath::CharType kOldOptimizationGuidePredictionModelDownloads[] = - FILE_PATH_LITERAL("optimization_guide_prediction_model_downloads"); - const base::FilePath::CharType kOptimizationGuideModelStoreDirPrefix[] = FILE_PATH_LITERAL("optimization_guide_model_store");
diff --git a/components/optimization_guide/core/optimization_guide_constants.h b/components/optimization_guide/core/optimization_guide_constants.h index 9eb1329..e04ff893 100644 --- a/components/optimization_guide/core/optimization_guide_constants.h +++ b/components/optimization_guide/core/optimization_guide_constants.h
@@ -53,12 +53,6 @@ extern const base::FilePath::CharType kOldOptimizationGuidePredictionModelMetadataStore[]; -// The folder where the old prediction model downloads are stored. This is per -// profile. -COMPONENT_EXPORT(OPTIMIZATION_GUIDE_FEATURES) -extern const base::FilePath::CharType - kOldOptimizationGuidePredictionModelDownloads[]; - // The prefix for the folder where models are stored by the new install-wide // model store. COMPONENT_EXPORT(OPTIMIZATION_GUIDE_FEATURES)
diff --git a/components/optimization_guide/internal b/components/optimization_guide/internal index fcc3886..76c30fc 160000 --- a/components/optimization_guide/internal +++ b/components/optimization_guide/internal
@@ -1 +1 @@ -Subproject commit fcc388612fe88fddb7335229bd0d70b6ec45d7bd +Subproject commit 76c30fcad7ac08d84c88ddc4df6a7b5f607822f8
diff --git a/components/policy/core/common/BUILD.gn b/components/policy/core/common/BUILD.gn index acbd662..6f637fb9 100644 --- a/components/policy/core/common/BUILD.gn +++ b/components/policy/core/common/BUILD.gn
@@ -375,6 +375,8 @@ if (is_ios) { sources += [ + "management/platform_management_status_provider_ios.h", + "management/platform_management_status_provider_ios.mm", "policy_loader_ios.h", "policy_loader_ios.mm", ]
diff --git a/components/policy/core/common/management/platform_management_service.cc b/components/policy/core/common/management/platform_management_service.cc index b923440..9d6131d6 100644 --- a/components/policy/core/common/management/platform_management_service.cc +++ b/components/policy/core/common/management/platform_management_service.cc
@@ -14,6 +14,8 @@ #include "components/policy/core/common/management/platform_management_status_provider_mac.h" #elif BUILDFLAG(IS_WIN) #include "components/policy/core/common/management/platform_management_status_provider_win.h" +#elif BUILDFLAG(IS_IOS) +#include "components/policy/core/common/management/platform_management_status_provider_ios.h" #endif namespace policy { @@ -23,13 +25,15 @@ GetPlatformManagementSatusProviders() { std::vector<std::unique_ptr<ManagementStatusProvider>> providers; #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) - providers.emplace_back(std::make_unique<DomainEnrollmentStatusProvider>()); - providers.emplace_back( + providers.push_back(std::make_unique<DomainEnrollmentStatusProvider>()); + providers.push_back( std::make_unique<EnterpriseMDMManagementStatusProvider>()); #endif #if BUILDFLAG(IS_WIN) - providers.emplace_back( - std::make_unique<AzureActiveDirectoryStatusProvider>()); + providers.push_back(std::make_unique<AzureActiveDirectoryStatusProvider>()); +#endif +#if BUILDFLAG(IS_IOS) + providers.push_back(std::make_unique<DeviceManagementStatusProvider>()); #endif return providers; }
diff --git a/components/policy/core/common/management/platform_management_service.h b/components/policy/core/common/management/platform_management_service.h index 944c749..927b04b 100644 --- a/components/policy/core/common/management/platform_management_service.h +++ b/components/policy/core/common/management/platform_management_service.h
@@ -15,7 +15,7 @@ // This class gives information related to the OS and device's management // state. -// For more imformation please read +// For more information please read // //components/policy/core/common/management/management_service.md class POLICY_EXPORT PlatformManagementService : public ManagementService { public:
diff --git a/components/policy/core/common/management/platform_management_status_provider_ios.h b/components/policy/core/common/management/platform_management_status_provider_ios.h new file mode 100644 index 0000000..dd9a5a3 --- /dev/null +++ b/components/policy/core/common/management/platform_management_status_provider_ios.h
@@ -0,0 +1,35 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_POLICY_CORE_COMMON_MANAGEMENT_PLATFORM_MANAGEMENT_STATUS_PROVIDER_IOS_H_ +#define COMPONENTS_POLICY_CORE_COMMON_MANAGEMENT_PLATFORM_MANAGEMENT_STATUS_PROVIDER_IOS_H_ + +#include "base/enterprise_util.h" +#include "components/policy/core/common/management/management_service.h" +#include "components/policy/policy_export.h" + +namespace policy { + +// This is used to determine if the device is managed. +// If "com.apple.configuration.managed" is found in the user defaults, the +// device is considered managed. +class POLICY_EXPORT DeviceManagementStatusProvider final + : public ManagementStatusProvider { + public: + DeviceManagementStatusProvider(); + ~DeviceManagementStatusProvider() final; + + DeviceManagementStatusProvider(const DeviceManagementStatusProvider&) = + delete; + DeviceManagementStatusProvider& operator=( + const DeviceManagementStatusProvider&) = delete; + + protected: + // ManagementStatusProvider impl + EnterpriseManagementAuthority FetchAuthority() final; +}; + +} // namespace policy + +#endif // COMPONENTS_POLICY_CORE_COMMON_MANAGEMENT_PLATFORM_MANAGEMENT_STATUS_PROVIDER_IOS_H_
diff --git a/components/policy/core/common/management/platform_management_status_provider_ios.mm b/components/policy/core/common/management/platform_management_status_provider_ios.mm new file mode 100644 index 0000000..baa8715 --- /dev/null +++ b/components/policy/core/common/management/platform_management_status_provider_ios.mm
@@ -0,0 +1,27 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "components/policy/core/common/management/platform_management_status_provider_ios.h" + +#import <Foundation/Foundation.h> + +#import "base/enterprise_util.h" +#import "components/policy/core/common/policy_loader_ios_constants.h" +#import "components/policy/core/common/policy_pref_names.h" + +namespace policy { + +DeviceManagementStatusProvider::DeviceManagementStatusProvider() = default; + +DeviceManagementStatusProvider::~DeviceManagementStatusProvider() = default; + +EnterpriseManagementAuthority DeviceManagementStatusProvider::FetchAuthority() { + BOOL isManagedDevice = + [[NSUserDefaults standardUserDefaults] + dictionaryForKey:kPolicyLoaderIOSConfigurationKey] != nil; + return isManagedDevice ? EnterpriseManagementAuthority::CLOUD + : EnterpriseManagementAuthority::NONE; +} + +} // namespace policy
diff --git a/components/policy/core/common/schema.cc b/components/policy/core/common/schema.cc index 85a699b5..6b48133 100644 --- a/components/policy/core/common/schema.cc +++ b/components/policy/core/common/schema.cc
@@ -502,37 +502,37 @@ const SchemaNode* schema(int index) const { DCHECK_GE(index, 0); - return schema_data_.schema_nodes + index; + return UNSAFE_TODO(schema_data_.schema_nodes + index); } const PropertiesNode* properties(int index) const { DCHECK_GE(index, 0); - return schema_data_.properties_nodes + index; + return UNSAFE_TODO(schema_data_.properties_nodes + index); } const PropertyNode* property(int index) const { DCHECK_GE(index, 0); - return schema_data_.property_nodes + index; + return UNSAFE_TODO(schema_data_.property_nodes + index); } const RestrictionNode* restriction(int index) const { DCHECK_GE(index, 0); - return schema_data_.restriction_nodes + index; + return UNSAFE_TODO(schema_data_.restriction_nodes + index); } const char* const* required_property(int index) const { DCHECK_GE(index, 0); - return schema_data_.required_properties + index; + return UNSAFE_TODO(schema_data_.required_properties + index); } const int* int_enums(int index) const { DCHECK_GE(index, 0); - return schema_data_.int_enums + index; + return UNSAFE_TODO(schema_data_.int_enums + index); } const char* const* string_enums(int index) const { DCHECK_GE(index, 0); - return schema_data_.string_enums + index; + return UNSAFE_TODO(schema_data_.string_enums + index); } // Compiles regular expression |pattern|. The result is cached and will be @@ -1140,7 +1140,7 @@ void Schema::Iterator::Advance() { DCHECK(it_); - ++it_; + UNSAFE_TODO(++it_); // Should be UNSAFE_BUFFER_USAGE. } const char* Schema::Iterator::key() const {
diff --git a/components/regional_capabilities/BUILD.gn b/components/regional_capabilities/BUILD.gn index 587a366..9e11e4a 100644 --- a/components/regional_capabilities/BUILD.gn +++ b/components/regional_capabilities/BUILD.gn
@@ -38,6 +38,7 @@ "//third_party/abseil-cpp:absl", "//third_party/search_engines_data:prepopulated_engines", "//third_party/search_engines_data:regional_settings", + "//ui/base", ] if (is_android) { @@ -122,5 +123,6 @@ "//components/metrics", "//components/sync_preferences:test_support", "//testing/gtest", + "//ui/base", ] }
diff --git a/components/regional_capabilities/DEPS b/components/regional_capabilities/DEPS index 384f04b7..cc4d822 100644 --- a/components/regional_capabilities/DEPS +++ b/components/regional_capabilities/DEPS
@@ -3,6 +3,7 @@ "+components/keyed_service/core", "+components/prefs", "+components/pref_registry", + "+ui/base/device_form_factor.h", "+third_party/jni_zero", "+components/regional_capabilities/access", "+third_party/search_engines_data",
diff --git a/components/regional_capabilities/program_settings.cc b/components/regional_capabilities/program_settings.cc index 9edbebac..ecc8e14 100644 --- a/components/regional_capabilities/program_settings.cc +++ b/components/regional_capabilities/program_settings.cc
@@ -4,21 +4,44 @@ #include "components/regional_capabilities/program_settings.h" +#include "base/notreached.h" +#include "components/country_codes/country_codes.h" +#include "components/regional_capabilities/eea_countries_ids.h" + namespace regional_capabilities { +namespace { + +constexpr country_codes::CountryId kTaiyakiCountry("JP"); + +} const ProgramSettings kWaffleSettings{ + .program = Program::kWaffle, .search_engine_list_type = SearchEngineListType::kShuffled, .can_show_search_engine_choice_screen = true, }; const ProgramSettings kTaiyakiSettings{ + .program = Program::kTaiyaki, .search_engine_list_type = SearchEngineListType::kShuffled, .can_show_search_engine_choice_screen = true, }; const ProgramSettings kDefaultSettings{ + .program = Program::kDefault, .search_engine_list_type = SearchEngineListType::kTopFive, .can_show_search_engine_choice_screen = false, }; +bool IsInProgramRegion(Program program, country_codes::CountryId country_id) { + switch (program) { + case Program::kTaiyaki: + return country_id == kTaiyakiCountry; + case Program::kWaffle: + return kEeaChoiceCountriesIds.contains(country_id); + case Program::kDefault: + NOTREACHED(); + } +} + } // namespace regional_capabilities
diff --git a/components/regional_capabilities/program_settings.h b/components/regional_capabilities/program_settings.h index 99854ac..c865308f 100644 --- a/components/regional_capabilities/program_settings.h +++ b/components/regional_capabilities/program_settings.h
@@ -5,8 +5,18 @@ #ifndef COMPONENTS_REGIONAL_CAPABILITIES_PROGRAM_SETTINGS_H_ #define COMPONENTS_REGIONAL_CAPABILITIES_PROGRAM_SETTINGS_H_ +namespace country_codes { +class CountryId; +} + namespace regional_capabilities { +enum class Program { + kTaiyaki, + kWaffle, + kDefault, +}; + // Describes how search engines should be listed. enum class SearchEngineListType { // The top 5 (at most) engines of the current country's list should be used, @@ -18,6 +28,7 @@ // Describes how features should adjust themselves based on the program. struct ProgramSettings { + Program program; SearchEngineListType search_engine_list_type; bool can_show_search_engine_choice_screen; }; @@ -26,6 +37,9 @@ extern const ProgramSettings kTaiyakiSettings; extern const ProgramSettings kDefaultSettings; +bool IsInProgramRegion(Program program, + country_codes::CountryId profile_country); + } // namespace regional_capabilities #endif // COMPONENTS_REGIONAL_CAPABILITIES_PROGRAM_SETTINGS_H_
diff --git a/components/regional_capabilities/regional_capabilities_service.cc b/components/regional_capabilities/regional_capabilities_service.cc index 8a4208e..693de949e 100644 --- a/components/regional_capabilities/regional_capabilities_service.cc +++ b/components/regional_capabilities/regional_capabilities_service.cc
@@ -27,6 +27,7 @@ #include "regional_capabilities_metrics.h" #include "third_party/abseil-cpp/absl/functional/overload.h" #include "third_party/search_engines_data/resources/definitions/prepopulated_engines.h" +#include "ui/base/device_form_factor.h" #if BUILDFLAG(IS_ANDROID) #include "base/android/scoped_java_ref.h" @@ -163,16 +164,26 @@ } const ProgramSettings* CountryIdToProgram(CountryId country_id) { -#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS) - if (base::FeatureList::IsEnabled(switches::kTaiyaki)) { - // Not final logic. - // TODO(crbug.com/423882950): Update logic for iOS. - // TODO(crbug.com/423883216): Update logic for Android. - return &kTaiyakiSettings; +#if BUILDFLAG(IS_IOS) + // TODO(crbug.com/423883216): Update logic to support Android. + if (IsInProgramRegion(Program::kTaiyaki, country_id)) { + switch (ui::GetDeviceFormFactor()) { + case ui::DEVICE_FORM_FACTOR_PHONE: + case ui::DEVICE_FORM_FACTOR_FOLDABLE: + if (base::FeatureList::IsEnabled(switches::kTaiyaki)) { + return &kTaiyakiSettings; + } + break; + case ui::DEVICE_FORM_FACTOR_DESKTOP: + case ui::DEVICE_FORM_FACTOR_TABLET: + case ui::DEVICE_FORM_FACTOR_TV: + case ui::DEVICE_FORM_FACTOR_AUTOMOTIVE: + break; + } } -#endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS) +#endif // BUILDFLAG(IS_IOS) - if (regional_capabilities::IsEeaCountry(country_id)) { + if (IsInProgramRegion(Program::kWaffle, country_id)) { return &kWaffleSettings; } @@ -253,7 +264,7 @@ // override. // TODO(crbug.com/328040066): Introduce granular program settings APIs and // deprecate `IsInEeaCountry()` in favour of these. - return &GetActiveProgramSettings() == &kWaffleSettings; + return GetActiveProgramSettings().program == Program::kWaffle; } CountryIdHolder RegionalCapabilitiesService::GetCountryId() { @@ -336,6 +347,10 @@ country_id_cache_.reset(); } +Program RegionalCapabilitiesService::GetActiveProgramForTesting() { + return GetActiveProgramSettings().program; +} + CountryId RegionalCapabilitiesService::GetPersistedCountryId() { // Prefer `prefs::kCountryID` if available and valid, otherwise fallback to // `prefs::kCountryIDAtInstall`.
diff --git a/components/regional_capabilities/regional_capabilities_service.h b/components/regional_capabilities/regional_capabilities_service.h index 1804ab7..1cc6d1a 100644 --- a/components/regional_capabilities/regional_capabilities_service.h +++ b/components/regional_capabilities/regional_capabilities_service.h
@@ -112,6 +112,8 @@ // in tests. void ClearCountryIdCacheForTesting(); + Program GetActiveProgramForTesting(); + #if BUILDFLAG(IS_ANDROID) // -- JNI Interface --------------------------------------------------------- @@ -129,9 +131,6 @@ #endif private: - FRIEND_TEST_ALL_PREFIXES(RegionalCapabilitiesServiceTest, - GetActiveProgram_CommandLineOverride); - // Returns how features should adjust themselves based on the active country // or program. const ProgramSettings& GetActiveProgramSettings();
diff --git a/components/regional_capabilities/regional_capabilities_service_unittest.cc b/components/regional_capabilities/regional_capabilities_service_unittest.cc index 7a5a2e10..ea7be84 100644 --- a/components/regional_capabilities/regional_capabilities_service_unittest.cc +++ b/components/regional_capabilities/regional_capabilities_service_unittest.cc
@@ -8,6 +8,7 @@ #include <optional> #include "base/check_deref.h" +#include "base/containers/span.h" #include "base/memory/weak_ptr.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" @@ -22,6 +23,7 @@ #include "regional_capabilities_metrics.h" #include "regional_capabilities_service.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/base/device_form_factor.h" using ::country_codes::CountryId; @@ -79,7 +81,20 @@ return service.GetCountryId().GetForTesting(); } -} // namespace +// Helper function to concatenate multiple `std::vector`s, intended for the +// parameterized test params. +template <typename Vec, typename... Vecs> +Vec Concatenate(const Vec& first, const Vecs&... rest) { + Vec result; + // Reserve space in the result vector to avoid multiple reallocations. + result.reserve(first.size() + (rest.size() + ... + 0)); + + // Insert the first vector, then a fold expression for the rest. + result.insert(result.end(), first.begin(), first.end()); + (result.insert(result.end(), rest.begin(), rest.end()), ...); + + return result; +} class RegionalCapabilitiesServiceTest : public ::testing::Test { public: @@ -156,6 +171,118 @@ base::HistogramTester histogram_tester_; }; +template <typename T> +class RegionalCapabilitiesServiceTestWithParam + : public RegionalCapabilitiesServiceTest, + public testing::WithParamInterface<T> { + public: + static std::string GetTestName(const testing::TestParamInfo<T>& info) { + return info.param.test_name; + } +}; + +struct ActiveProgramFromOverrideTestParam { + std::string test_name; + std::string country_override; + Program expected_program; +}; + +using RegionalCapabilitiesServiceActiveProgramFromOverrideTest = + RegionalCapabilitiesServiceTestWithParam< + ActiveProgramFromOverrideTestParam>; + +TEST_P(RegionalCapabilitiesServiceActiveProgramFromOverrideTest, Run) { + std::unique_ptr<RegionalCapabilitiesService> service = InitService(); + + SetCommandLineCountry(GetParam().country_override); + EXPECT_EQ(GetParam().expected_program, service->GetActiveProgramForTesting()); +} + +const std::vector<ActiveProgramFromOverrideTestParam> + kActiveProgramFromOverrideCommonTestCases = { + ActiveProgramFromOverrideTestParam{ + .test_name = "fr_to_waffle", + .country_override = "FR", + .expected_program = Program::kWaffle, + }, + ActiveProgramFromOverrideTestParam{ + .test_name = "us_to_default", + .country_override = "US", + .expected_program = Program::kDefault, + }, + ActiveProgramFromOverrideTestParam{ + .test_name = "err_to_default", + .country_override = "??", + .expected_program = Program::kDefault, + }, + ActiveProgramFromOverrideTestParam{ + .test_name = "default_eea_list", + .country_override = switches::kDefaultListCountryOverride, + .expected_program = Program::kWaffle, + }, + ActiveProgramFromOverrideTestParam{ + .test_name = "full_eea_list", + .country_override = switches::kEeaListCountryOverride, + .expected_program = Program::kWaffle, + }, + +}; + +INSTANTIATE_TEST_SUITE_P( + , + RegionalCapabilitiesServiceActiveProgramFromOverrideTest, + ::testing::ValuesIn( + Concatenate(kActiveProgramFromOverrideCommonTestCases, + std::vector<ActiveProgramFromOverrideTestParam>{ + ActiveProgramFromOverrideTestParam{ + .test_name = "jp_to_default", + .country_override = "JP", + .expected_program = Program::kDefault, + }, + })), + &RegionalCapabilitiesServiceActiveProgramFromOverrideTest::GetTestName); + +#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS) +bool IsIPhone() { +#if BUILDFLAG(IS_IOS) + return ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_PHONE; +#else + return false; +#endif +} + +class RegionalCapabilitiesServiceActiveProgramFromOverrideTaiyakiForcedTest + : public RegionalCapabilitiesServiceTestWithParam< + ActiveProgramFromOverrideTestParam> { + private: + base::test::ScopedFeatureList scoped_feature_list_{switches::kTaiyaki}; +}; + +TEST_P(RegionalCapabilitiesServiceActiveProgramFromOverrideTaiyakiForcedTest, + Run) { + std::unique_ptr<RegionalCapabilitiesService> service = InitService(); + + SetCommandLineCountry(GetParam().country_override); + EXPECT_EQ(GetParam().expected_program, service->GetActiveProgramForTesting()); +} + +INSTANTIATE_TEST_SUITE_P( + , + RegionalCapabilitiesServiceActiveProgramFromOverrideTaiyakiForcedTest, + ::testing::ValuesIn( + Concatenate(kActiveProgramFromOverrideCommonTestCases, + std::vector<ActiveProgramFromOverrideTestParam>{ + ActiveProgramFromOverrideTestParam{ + .test_name = "jp_to_taiyaki", + .country_override = "JP", + .expected_program = IsIPhone() ? Program::kTaiyaki + : Program::kDefault, + }, + })), + &RegionalCapabilitiesServiceActiveProgramFromOverrideTaiyakiForcedTest:: + GetTestName); +#endif + TEST_F(RegionalCapabilitiesServiceTest, GetCountryIdCommandLineOverride) { // The command line value bypasses the country ID cache and does not // require recreating the service. @@ -185,27 +312,6 @@ "RegionalCapabilities.LoadedCountrySource", 0); } -TEST_F(RegionalCapabilitiesServiceTest, GetActiveProgram_CommandLineOverride) { - // The command line value bypasses the country ID cache and does not - // require recreating the service. - std::unique_ptr<RegionalCapabilitiesService> service = InitService(); - - SetCommandLineCountry("FR"); - EXPECT_EQ(&service->GetActiveProgramSettings(), &kWaffleSettings); - - SetCommandLineCountry("US"); - EXPECT_EQ(&service->GetActiveProgramSettings(), &kDefaultSettings); - - SetCommandLineCountry("??"); - EXPECT_EQ(&service->GetActiveProgramSettings(), &kDefaultSettings); - - SetCommandLineCountry(switches::kDefaultListCountryOverride); - EXPECT_EQ(&service->GetActiveProgramSettings(), &kWaffleSettings); - - SetCommandLineCountry(switches::kEeaListCountryOverride); - EXPECT_EQ(&service->GetActiveProgramSettings(), &kWaffleSettings); -} - TEST_F(RegionalCapabilitiesServiceTest, GetCountryId_FetchedSync) { const auto kFallbackCountryId = CountryId("FR"); @@ -629,4 +735,5 @@ EXPECT_TRUE(service->IsInEeaCountry()); } +} // namespace } // namespace regional_capabilities
diff --git a/components/safe_browsing/content/browser/client_side_detection_host.cc b/components/safe_browsing/content/browser/client_side_detection_host.cc index fcf842f..a4f323c2 100644 --- a/components/safe_browsing/content/browser/client_side_detection_host.cc +++ b/components/safe_browsing/content/browser/client_side_detection_host.cc
@@ -129,6 +129,8 @@ return "VibrationApi"; case safe_browsing::ClientSideDetectionType::FULLSCREEN_API: return "FullscreenApi"; + case safe_browsing::ClientSideDetectionType::CLIPBOARD_COPY_API: + return "ClipboardCopyApi"; } } @@ -150,6 +152,8 @@ return safe_browsing::mojom::ClientSideDetectionType::kVibrationApi; case safe_browsing::ClientSideDetectionType::FULLSCREEN_API: return safe_browsing::mojom::ClientSideDetectionType::kFullscreen; + case safe_browsing::ClientSideDetectionType::CLIPBOARD_COPY_API: + return safe_browsing::mojom::ClientSideDetectionType::kClipboardCopyApi; case safe_browsing::ClientSideDetectionType:: CLIENT_SIDE_DETECTION_TYPE_UNSPECIFIED: default: @@ -496,6 +500,12 @@ "SBClientPhishing.MatchCSDAllowlistOnFullscreenApi", match_allowlist); } + if (phishing_detection_request_type_ == + ClientSideDetectionType::CLIPBOARD_COPY_API) { + base::UmaHistogramBoolean( + "SBClientPhishing.MatchCSDAllowlistOnClipboardCopyApi", + match_allowlist); + } // This check is also for logging purposes although the CSD allowlist // could be matched or not checked at all. Once it completes, // preclassification check will continue. @@ -557,11 +567,14 @@ } if (phishing_detection_request_type_ == - ClientSideDetectionType::FULLSCREEN_API) { - // The purpose of triggering preclassification for fullscreen API to have + ClientSideDetectionType::FULLSCREEN_API || + (phishing_detection_request_type_ == + ClientSideDetectionType::CLIPBOARD_COPY_API && + !base::FeatureList::IsEnabled(kClientSideDetectionClipboardCopyApi))) { + // The purpose of triggering preclassification for these APIs is to have // an initial assessment on how often we'll be hitting the allowlist and - // triggering the classification. We will not go further than checking for - // this metric for now. + // triggering the classification. We will not go further than checking + // for this metric, unless otherwise specified by feature flags. DontClassifyForPhishing( PreClassificationCheckResult::NO_CLASSIFY_ALLOWLIST_METRIC); } @@ -579,7 +592,7 @@ /*is_from_cache=*/true, ClientSideDetectionType::TRIGGER_MODELS, did_match_high_confidence_allowlist_, url_, is_phishing, /*response_code=*/std::nullopt, - /*IntelligentScanVerdict=*/std::nullopt); + /*intelligent_scan_verdict=*/std::nullopt); DontClassifyForPhishing( PreClassificationCheckResult::NO_CLASSIFY_RESULT_FROM_CACHE); } @@ -639,6 +652,11 @@ case ClientSideDetectionType::TRIGGER_MODELS: return base::RandDouble() <= probability_for_accepting_hc_allowlist_trigger_; + case ClientSideDetectionType::CLIPBOARD_COPY_API: + return base::FeatureList::IsEnabled( + kClientSideDetectionClipboardCopyApi) && + (base::RandDouble() < + kCSDClipboardCopyApiHCAcceptanceRate.Get()); default: return false; } @@ -906,6 +924,19 @@ } } +void ClientSideDetectionHost::OnTextCopiedToClipboard( + content::RenderFrameHost* render_frame_host, + const std::u16string& copied_text) { + if (!IsEnhancedProtectionEnabled(*delegate_->GetPrefs())) { + return; + } + + if (!HasDonePreclassificationCheckOnSameURL( + ClientSideDetectionType::CLIPBOARD_COPY_API)) { + MaybeStartPreClassification(ClientSideDetectionType::CLIPBOARD_COPY_API); + } +} + bool ClientSideDetectionHost::HasDonePreclassificationCheckOnSameURL( ClientSideDetectionType client_side_detection_type) { content::RenderFrameHost* rfh = web_contents()->GetPrimaryMainFrame();
diff --git a/components/safe_browsing/content/browser/client_side_detection_host.h b/components/safe_browsing/content/browser/client_side_detection_host.h index b6f758ab..e48662e7 100644 --- a/components/safe_browsing/content/browser/client_side_detection_host.h +++ b/components/safe_browsing/content/browser/client_side_detection_host.h
@@ -169,6 +169,8 @@ void VibrationRequested() override; void DidToggleFullscreenModeForTab(bool entered_fullscreen, bool will_cause_resize) override; + void OnTextCopiedToClipboard(content::RenderFrameHost* render_frame_host, + const std::u16string& copied_text) override; // permissions::PermissionRequestManager::Observer methods: void OnPromptAdded() override; @@ -251,6 +253,10 @@ AsyncCheckTrackerTriggersClassificationRequestOnAllowlistMatch); FRIEND_TEST_ALL_PREFIXES(ClientSideDetectionHostScamDetectionTest, KeyboardLockRequestTriggersOnDeviceLLM); + FRIEND_TEST_ALL_PREFIXES(ClientSideDetectionHostClipboardTest, + ClipboardApiTriggersPreclassificationCheck); + FRIEND_TEST_ALL_PREFIXES(ClientSideDetectionHostClipboardTest, + ClipboardApiClassificationTriggersCSPPPing); // Helper function to create preclassification check once requirements are // met.
diff --git a/components/safe_browsing/content/common/safe_browsing.mojom b/components/safe_browsing/content/common/safe_browsing.mojom index 3748848..585bf35 100644 --- a/components/safe_browsing/content/common/safe_browsing.mojom +++ b/components/safe_browsing/content/common/safe_browsing.mojom
@@ -148,6 +148,8 @@ kFullscreen, // Password protection request triggered. kPasswordProtection, + // Clipboard copy API request triggered. + kClipboardCopyApi, }; // Interface for starting phishing classification. This is scoped to a
diff --git a/components/safe_browsing/content/renderer/phishing_classifier/phishing_classifier_delegate.cc b/components/safe_browsing/content/renderer/phishing_classifier/phishing_classifier_delegate.cc index c236ada..f6ed527 100644 --- a/components/safe_browsing/content/renderer/phishing_classifier/phishing_classifier_delegate.cc +++ b/components/safe_browsing/content/renderer/phishing_classifier/phishing_classifier_delegate.cc
@@ -72,6 +72,8 @@ return "FullscreenApi"; case safe_browsing::mojom::ClientSideDetectionType::kPasswordProtection: return "PasswordProtection"; + case safe_browsing::mojom::ClientSideDetectionType::kClipboardCopyApi: + return "ClipboardCopyApi"; } }
diff --git a/components/safe_browsing/core/common/features.cc b/components/safe_browsing/core/common/features.cc index 0dffe6c..0d9fa17 100644 --- a/components/safe_browsing/core/common/features.cc +++ b/components/safe_browsing/core/common/features.cc
@@ -42,6 +42,14 @@ "ClientSideDetectionBrandAndIntentForScamDetection", base::FEATURE_ENABLED_BY_DEFAULT); +BASE_FEATURE(kClientSideDetectionClipboardCopyApi, + "ClientSideDetectionClipboardCopyApi", + base::FEATURE_DISABLED_BY_DEFAULT); + +const base::FeatureParam<double> kCSDClipboardCopyApiHCAcceptanceRate{ + &kClientSideDetectionClipboardCopyApi, "HCAcceptanceRate", + /*default_value=*/0.0}; + BASE_FEATURE(kClientSideDetectionDebuggingMetadataCache, "ClientSideDetectionDebuggingMetadataCache", base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/components/safe_browsing/core/common/features.h b/components/safe_browsing/core/common/features.h index 3f9d5c5..bbba2e4 100644 --- a/components/safe_browsing/core/common/features.h +++ b/components/safe_browsing/core/common/features.h
@@ -37,6 +37,12 @@ // on-device model LLM. BASE_DECLARE_FEATURE(kClientSideDetectionBrandAndIntentForScamDetection); +// Expand CSPP beyond phishing and trigger when clipboard copy API is called on +// the page. +BASE_DECLARE_FEATURE(kClientSideDetectionClipboardCopyApi); + +extern const base::FeatureParam<double> kCSDClipboardCopyApiHCAcceptanceRate; + BASE_DECLARE_FEATURE(kClientSideDetectionDebuggingMetadataCache); // Extract only the visual features during the phishing classifier.
diff --git a/components/safe_browsing/core/common/proto/csd.proto b/components/safe_browsing/core/common/proto/csd.proto index 6a505dd1..3dd238ec 100644 --- a/components/safe_browsing/core/common/proto/csd.proto +++ b/components/safe_browsing/core/common/proto/csd.proto
@@ -1316,6 +1316,8 @@ VIBRATION_API = 6; // Fullscreen API request triggered. FULLSCREEN_API = 7; + // Clipboard copy API request triggered. + CLIPBOARD_COPY_API = 8; } message ClientDownloadResponse {
diff --git a/components/saved_tab_groups/internal/versioning_message_controller_impl.cc b/components/saved_tab_groups/internal/versioning_message_controller_impl.cc index 9c2b8b6..b7a5baa 100644 --- a/components/saved_tab_groups/internal/versioning_message_controller_impl.cc +++ b/components/saved_tab_groups/internal/versioning_message_controller_impl.cc
@@ -9,6 +9,7 @@ #include "components/prefs/pref_service.h" #include "components/saved_tab_groups/public/pref_names.h" #include "components/saved_tab_groups/public/tab_group_sync_service.h" +#include "components/saved_tab_groups/public/utils.h" namespace tab_groups { namespace { @@ -121,6 +122,14 @@ if (had_any_shared_tab_groups) { pref_service_->SetBoolean( prefs::kEligibleForVersionOutOfDatePersistentMessage, true); + + // For desktop, instant message should be shown if there are any shared + // tab groups in the previous session regardless of whether they were + // open or closed. + if (!AreLocalIdsPersisted()) { + pref_service_->SetBoolean( + prefs::kEligibleForVersionOutOfDateInstantMessage, true); + } } // Always reset the 'updated' message eligibility when out of date. @@ -190,6 +199,23 @@ void VersioningMessageControllerImpl::ShouldShowMessageUiAsync( MessageType message_type, base::OnceCallback<void(bool)> callback) { + // The VERSION_UPDATED_MESSAGE requires waiting for tab groups to be + // downloaded. We handle it separately by queueing the callback. + if (message_type == MessageType::VERSION_UPDATED_MESSAGE) { + // If the check has already completed and final state of the eligibility is + // already determined, we can respond immediately with the final state. + if (processed_version_updated_callbacks_) { + std::move(callback).Run(ShouldShowMessageUi(message_type)); + } else { + // Otherwise, queue the callback. It will be run later by either + // OnInitialized or OnTabGroupAdded. + pending_version_updated_callbacks_.push_back(std::move(callback)); + } + return; + } + + // For all other message types, queue callbacks if init isn't complete, + // otherwise invoke them right away. if (!is_initialized_) { pending_callbacks_.push_back(base::BindOnce( &VersioningMessageControllerImpl::ShouldShowMessageUiAsync, @@ -235,10 +261,69 @@ is_initialized_ = true; ComputePrefsOnStartup(); + // Attempt to resolve any pending VERSION_UPDATED_MESSAGE callbacks. This + // handles cases where conditions are already met (or definitively not met) + // at startup. + MaybeResolvePendingVersionUpdatedCallbacks(); + + // Run pending callbacks for other message types that were waiting for init. for (auto& callback : pending_callbacks_) { std::move(callback).Run(); } pending_callbacks_.clear(); } +void VersioningMessageControllerImpl::OnTabGroupAdded( + const SavedTabGroup& group, + TriggerSource source) { + // We only care about the first shared group that appears. + if (processed_version_updated_callbacks_ || !group.is_shared_tab_group()) { + return; + } + + // A shared group has been added. Re-evaluate the conditions to see if we + // can resolve the pending callbacks now. + MaybeResolvePendingVersionUpdatedCallbacks(); +} + +void VersioningMessageControllerImpl:: + MaybeResolvePendingVersionUpdatedCallbacks() { + if (processed_version_updated_callbacks_) { + return; + } + + // The message can only be shown if the version is up to date and the user is + // eligible (they have seen an out-of-date message before). + const bool can_ever_show = + GetVersionState() == VersionState::kUpToDate && + pref_service_->GetBoolean(prefs::kEligibleForVersionUpdatedMessage); + + bool should_resolve = false; + bool resolution_value = false; + + if (!can_ever_show) { + // If base conditions aren't met, we can definitively resolve with `false`. + should_resolve = true; + resolution_value = false; + } else if (HasCurrentSharedTabGroups(tab_group_sync_service_)) { + // If base conditions *are* met and we have shared groups, resolve with + // `true`. + should_resolve = true; + resolution_value = true; + } + + // If `should_resolve` is false, it means conditions are plausible but we are + // still waiting for a shared tab group to be added. We'll wait for another + // call from OnTabGroupAdded. + if (!should_resolve) { + return; + } + + for (auto& callback : pending_version_updated_callbacks_) { + std::move(callback).Run(resolution_value); + } + pending_version_updated_callbacks_.clear(); + processed_version_updated_callbacks_ = true; +} + } // namespace tab_groups
diff --git a/components/saved_tab_groups/internal/versioning_message_controller_impl.h b/components/saved_tab_groups/internal/versioning_message_controller_impl.h index 6e8c84b..ef40b4aa 100644 --- a/components/saved_tab_groups/internal/versioning_message_controller_impl.h +++ b/components/saved_tab_groups/internal/versioning_message_controller_impl.h
@@ -38,9 +38,12 @@ // TabGroupSyncService::Observer implementation. void OnInitialized() override; + void OnTabGroupAdded(const SavedTabGroup& group, + TriggerSource source) override; private: void ComputePrefsOnStartup(); + void MaybeResolvePendingVersionUpdatedCallbacks(); raw_ptr<PrefService> pref_service_; raw_ptr<TabGroupSyncService> tab_group_sync_service_; @@ -51,6 +54,12 @@ // Whether the TabGroupSyncService has been initialized. bool is_initialized_ = false; + // Callbacks for VERSION_UPDATED_MESSAGE waiting for a final state + // determination. + std::vector<base::OnceCallback<void(bool)>> + pending_version_updated_callbacks_; + bool processed_version_updated_callbacks_ = false; + base::WeakPtrFactory<VersioningMessageControllerImpl> weak_ptr_factory_{this}; };
diff --git a/components/saved_tab_groups/internal/versioning_message_controller_impl_unittest.cc b/components/saved_tab_groups/internal/versioning_message_controller_impl_unittest.cc index a6a33a3..5435dc5 100644 --- a/components/saved_tab_groups/internal/versioning_message_controller_impl_unittest.cc +++ b/components/saved_tab_groups/internal/versioning_message_controller_impl_unittest.cc
@@ -15,6 +15,7 @@ #include "components/prefs/pref_registry_simple.h" #include "components/prefs/testing_pref_service.h" #include "components/saved_tab_groups/public/pref_names.h" +#include "components/saved_tab_groups/public/utils.h" #include "components/saved_tab_groups/public/versioning_message_controller.h" #include "components/saved_tab_groups/test_support/mock_tab_group_sync_service.h" #include "components/saved_tab_groups/test_support/saved_tab_group_test_utils.h" @@ -69,6 +70,14 @@ testing::Return(std::vector<const SavedTabGroup*>({&tab_group1_}))); } + void MimicTabGroupAdded(bool is_shared_group) { + if (is_shared_group) { + tab_group1_.SetCollaborationId(CollaborationId("collaboration_id")); + } + SetTabGroupSyncServiceCurrentExpectation(is_shared_group); + controller_->OnTabGroupAdded(tab_group1_, TriggerSource::REMOTE); + } + void InitializeController() { controller_ = std::make_unique<VersioningMessageControllerImpl>( &pref_service_, &mock_tab_group_sync_service_); @@ -220,8 +229,13 @@ SetTabGroupSyncServiceExpectation(/*had_shared_tab_groups=*/true, /*had_open_shared_tab_groups=*/false); InitializeController(); - EXPECT_FALSE( - ShouldShowMessageUi(MessageType::VERSION_OUT_OF_DATE_INSTANT_MESSAGE)); + if (AreLocalIdsPersisted()) { + EXPECT_FALSE( + ShouldShowMessageUi(MessageType::VERSION_OUT_OF_DATE_INSTANT_MESSAGE)); + } else { + EXPECT_TRUE( + ShouldShowMessageUi(MessageType::VERSION_OUT_OF_DATE_INSTANT_MESSAGE)); + } EXPECT_TRUE( ShouldShowMessageUi(MessageType::VERSION_OUT_OF_DATE_PERSISTENT_MESSAGE)); EXPECT_FALSE(ShouldShowMessageUi(MessageType::VERSION_UPDATED_MESSAGE)); @@ -331,6 +345,97 @@ EXPECT_TRUE(callback_called); } +TEST_F(VersioningMessageControllerImplTest, + VersionUpdatedCallback_WaitsForGroup) { + // Setup: Eligible for updated message, but no shared groups yet. + SetupFeatureList(/*version_up_to_date=*/true); + SetPref(prefs::kHasShownAnyVersionOutOfDateMessage, true); + SetTabGroupSyncServiceCurrentExpectation(/*has_shared_tab_groups*/ false); + InitializeController(); + + base::RunLoop run_loop; + bool result = false; + controller_->ShouldShowMessageUiAsync( + MessageType::VERSION_UPDATED_MESSAGE, + base::BindOnce( + [](base::RunLoop* run_loop, bool* result, bool received_value) { + *result = received_value; + run_loop->Quit(); + }, + &run_loop, &result)); + + // The callback should not have been called yet because we are waiting for a + // shared group to be added. + task_environment_.RunUntilIdle(); + + // Simulate a shared group being added. + MimicTabGroupAdded(/*is_shared_group=*/true); + + // Now the callback should be called with true, quitting the run loop. + run_loop.Run(); + EXPECT_TRUE(result); +} + +TEST_F(VersioningMessageControllerImplTest, + VersionUpdatedCallback_ResolvesImmediatelyIfIneligible) { + // Setup: Not eligible for updated message because version is out of date. + SetupFeatureList(/*version_up_to_date=*/false); + SetPref(prefs::kHasShownAnyVersionOutOfDateMessage, true); + SetTabGroupSyncServiceCurrentExpectation(/*has_shared_tab_groups*/ false); + InitializeController(); + + // The callback should be called immediately with false. + EXPECT_FALSE( + RunShouldShowMessageUiAsync(MessageType::VERSION_UPDATED_MESSAGE)); +} + +TEST_F(VersioningMessageControllerImplTest, + VersionUpdatedCallback_ResolvesImmediatelyIfGroupExists) { + // Setup: Eligible and a shared group already exists. + SetupFeatureList(/*version_up_to_date=*/true); + SetPref(prefs::kHasShownAnyVersionOutOfDateMessage, true); + SetTabGroupSyncServiceCurrentExpectation(/*has_shared_tab_groups*/ true); + InitializeController(); + + // The callback should be called immediately with true. + EXPECT_TRUE( + RunShouldShowMessageUiAsync(MessageType::VERSION_UPDATED_MESSAGE)); +} + +TEST_F(VersioningMessageControllerImplTest, + VersionUpdatedCallback_MultipleCallbacksAreQueued) { + // Setup: Eligible for updated message, but no shared groups yet. + SetupFeatureList(/*version_up_to_date=*/true); + SetPref(prefs::kHasShownAnyVersionOutOfDateMessage, true); + SetTabGroupSyncServiceCurrentExpectation(/*has_shared_tab_groups*/ false); + InitializeController(); + + bool first_callback_called = false; + bool second_callback_called = false; + + controller_->ShouldShowMessageUiAsync( + MessageType::VERSION_UPDATED_MESSAGE, + base::BindOnce([](bool* called, bool /*result*/) { *called = true; }, + &first_callback_called)); + controller_->ShouldShowMessageUiAsync( + MessageType::VERSION_UPDATED_MESSAGE, + base::BindOnce([](bool* called, bool /*result*/) { *called = true; }, + &second_callback_called)); + + // No callbacks should have been called yet. + task_environment_.RunUntilIdle(); + EXPECT_FALSE(first_callback_called); + EXPECT_FALSE(second_callback_called); + + // Simulate a shared group being added. + MimicTabGroupAdded(/*is_shared_group=*/true); + + // Both callbacks should now be called. + task_environment_.RunUntilIdle(); + EXPECT_TRUE(first_callback_called); + EXPECT_TRUE(second_callback_called); +} + TEST_F(VersioningMessageControllerImplTest, MultipleRestarts) { { // Start with version up-to-date.
diff --git a/components/saved_tab_groups/public/versioning_message_controller.h b/components/saved_tab_groups/public/versioning_message_controller.h index f44987f0..c523f23 100644 --- a/components/saved_tab_groups/public/versioning_message_controller.h +++ b/components/saved_tab_groups/public/versioning_message_controller.h
@@ -54,6 +54,11 @@ // callback is called synchronously. // See comments on MessageType for when the UI should inform this class about // display / dismissed events. + // + // NOTE: For MessageType::VERSION_UPDATED_MESSAGE, this method has a special + // behavior. The callback may be deferred until a shared tab group has been + // downloaded. If no shared tab group is ever added during the session, the + // callback may not be invoked at all. virtual void ShouldShowMessageUiAsync( MessageType message_type, base::OnceCallback<void(bool)> callback) = 0;
diff --git a/components/signin/core/browser/signin_metrics_service.cc b/components/signin/core/browser/signin_metrics_service.cc index dcaf4bb5..7fb300f 100644 --- a/components/signin/core/browser/signin_metrics_service.cc +++ b/components/signin/core/browser/signin_metrics_service.cc
@@ -308,8 +308,31 @@ switch (event_details.GetEventTypeFor(signin::ConsentLevel::kSync)) { case signin::PrimaryAccountChangeEvent::Type::kNone: - case signin::PrimaryAccountChangeEvent::Type::kSet: break; + case signin::PrimaryAccountChangeEvent::Type::kSet: { + std::optional<signin_metrics::AccessPoint> access_point = + event_details.GetSetPrimaryAccountAccessPoint(); + CHECK(access_point.has_value()); + if (access_point == signin_metrics::AccessPoint:: + kHistorySyncOptinExpansionPillOnStartup || + access_point == signin_metrics::AccessPoint:: + kHistorySyncOptinExpansionPillOnInactivity) { + SigninPrefs signin_prefs(pref_service_.get()); + const CoreAccountInfo& account = + event_details.GetCurrentState().primary_account; + base::UmaHistogramExactLinear( + "Signin.SyncOptIn.IdentityPill.SyncAtShowCount", + switches::IsAvatarSyncPromoFeatureEnabled() + ? signin_prefs.GetSyncPromoIdentityPillShownCount(account.gaia) + : signin_prefs.GetHistorySyncPromoIdentityPillShownCount( + account.gaia), + // Arbitrary number that is higher than the possible show count that + // the promo can reach + // (`user_education::features::GetNewBadgeShowCount()`: 10). + /*exclusive_max=*/30); + } + break; + } case signin::PrimaryAccountChangeEvent::Type::kCleared: if (pref_service_->HasPrefPath(kSyncPausedStartTimePref)) { RecordPendingResolutionTime(
diff --git a/components/signin/core/browser/signin_metrics_service_unittest.cc b/components/signin/core/browser/signin_metrics_service_unittest.cc index 81912e7e..63f4f35 100644 --- a/components/signin/core/browser/signin_metrics_service_unittest.cc +++ b/components/signin/core/browser/signin_metrics_service_unittest.cc
@@ -6,6 +6,7 @@ #include "base/json/values_util.h" #include "base/test/metrics/histogram_tester.h" +#include "base/test/scoped_feature_list.h" #include "base/test/task_environment.h" #include "components/prefs/pref_service.h" #include "components/prefs/testing_pref_service.h" @@ -84,9 +85,14 @@ void Signout() { identity_test_environment_.ClearPrimaryAccount(); } - void EnableSync(const std::string& email) { - identity_test_environment_.MakePrimaryAccountAvailable( - email, signin::ConsentLevel::kSync); + void EnableSync(const std::string& email, + signin_metrics::AccessPoint access_point = + signin_metrics::AccessPoint::kSettings) { + identity_test_environment_.MakeAccountAvailable( + signin::AccountAvailabilityOptionsBuilder() + .AsPrimary(signin::ConsentLevel::kSync) + .WithAccessPoint(access_point) + .Build(email)); } AccountInfo WebSignin(const std::string& email) { @@ -759,3 +765,47 @@ EXPECT_THAT(histogram_tester.GetTotalCountsForPrefix("Signin.SyncPaused"), base::HistogramTester::CountsMap()); } + +TEST_F(SigninMetricsServiceTest, HistorySyncPromoMetricLogging) { + base::HistogramTester histogram_tester; + base::test::ScopedFeatureList scoped_feature_list{ + switches::kAvatarButtonSyncPromoForTesting}; + + CreateSigninMetricsService(); + + const std::string email("test@gmail.com"); + AccountInfo account = Signin(email); + SigninPrefs signin_prefs(pref_service()); + signin_prefs.IncrementSyncPromoIdentityPillShownCount(account.gaia); + signin_prefs.IncrementSyncPromoIdentityPillShownCount(account.gaia); + + EnableSync( + email, + signin_metrics::AccessPoint::kHistorySyncOptinExpansionPillOnStartup); + histogram_tester.ExpectBucketCount( + "Signin.SyncOptIn.IdentityPill.SyncAtShowCount", + signin_prefs.GetSyncPromoIdentityPillShownCount(account.gaia), 1); +} + +TEST_F(SigninMetricsServiceTest, + HistorySyncPromoMetricLoggingWithSyncPromoOff) { + base::HistogramTester histogram_tester; + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndDisableFeature( + switches::kAvatarButtonSyncPromoForTesting); + + CreateSigninMetricsService(); + + const std::string email("test@gmail.com"); + AccountInfo account = Signin(email); + SigninPrefs signin_prefs(pref_service()); + signin_prefs.IncrementHistorySyncPromoIdentityPillShownCount(account.gaia); + signin_prefs.IncrementHistorySyncPromoIdentityPillShownCount(account.gaia); + + EnableSync( + email, + signin_metrics::AccessPoint::kHistorySyncOptinExpansionPillOnStartup); + histogram_tester.ExpectBucketCount( + "Signin.SyncOptIn.IdentityPill.SyncAtShowCount", + signin_prefs.GetHistorySyncPromoIdentityPillShownCount(account.gaia), 1); +}
diff --git a/components/signin/internal/identity_manager/BUILD.gn b/components/signin/internal/identity_manager/BUILD.gn index 3a0b539..a4ce0d8 100644 --- a/components/signin/internal/identity_manager/BUILD.gn +++ b/components/signin/internal/identity_manager/BUILD.gn
@@ -169,6 +169,7 @@ "//base/test:test_support", "//components/image_fetcher/core", "//components/image_fetcher/core:test_support", + "//components/os_crypt/async/browser:key_provider_interface", "//components/os_crypt/async/browser:test_support", "//components/prefs", "//components/prefs:test_support",
diff --git a/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate_unittest.cc b/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate_unittest.cc index 548137248..3217bb7 100644 --- a/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate_unittest.cc +++ b/components/signin/internal/identity_manager/mutable_profile_oauth2_token_service_delegate_unittest.cc
@@ -24,7 +24,10 @@ #include "base/test/task_environment.h" #include "base/test/test_future.h" #include "base/time/time.h" +#include "components/os_crypt/async/browser/key_provider.h" +#include "components/os_crypt/async/browser/os_crypt_async.h" #include "components/os_crypt/async/browser/test_utils.h" +#include "components/os_crypt/async/common/algorithm.mojom.h" #include "components/prefs/scoped_user_pref_update.h" #include "components/signin/internal/identity_manager/fake_profile_oauth2_token_service_delegate.h" #include "components/signin/internal/identity_manager/mock_profile_oauth2_token_service_observer.h" @@ -45,6 +48,7 @@ #include "components/unexportable_keys/fake_unexportable_key_service.h" #include "components/webdata/common/web_data_service_base.h" #include "components/webdata/common/web_database_service.h" +#include "crypto/kdf.h" #include "google_apis/gaia/core_account_id.h" #include "google_apis/gaia/gaia_constants.h" #include "google_apis/gaia/gaia_id.h" @@ -119,6 +123,31 @@ AccountMoveDecision::kCannotMoveInsertWithoutRefreshToken}, }; +// A test key provider that takes a name and produces a deterministic key based +// on that name. +class TestKeyProvider : public os_crypt_async::KeyProvider { + public: + explicit TestKeyProvider(const std::string& name, bool use_for_encryption) + : name_(name), use_for_encryption_(use_for_encryption) {} + + private: + void GetKey(KeyCallback callback) final { + std::move(callback).Run( + name_, os_crypt_async::Encryptor::Key( + crypto::kdf::Hkdf< + os_crypt_async::Encryptor::Key::kAES256GCMKeySize>( + crypto::hash::kSha256, base::as_byte_span(name_), + /*salt=*/{}, /*info=*/{}), + os_crypt_async::mojom::Algorithm::kAES256GCM)); + } + + bool UseForEncryption() final { return use_for_encryption_; } + bool IsCompatibleWithOsCryptSync() final { return false; } + + const std::string name_; + const bool use_for_encryption_; +}; + } // namespace class MutableProfileOAuth2TokenServiceDelegateTest @@ -170,13 +199,18 @@ base::RunLoop().RunUntilIdle(); } - void LoadTokenDatabase() { + // Supply an `os_crypt_override` if the caller wishes to override the default + // one. The `os_crypt_override` must remain valid until the + // `UnloadTokenDatabase` call is made. + void LoadTokenDatabase( + os_crypt_async::OSCryptAsync* os_crypt_override = nullptr) { scoped_refptr<WebDatabaseService> web_database = new WebDatabaseService( temp_dir_.GetPath().AppendASCII(kTestTokenDatabase), base::SingleThreadTaskRunner::GetCurrentDefault(), base::SingleThreadTaskRunner::GetCurrentDefault()); web_database->AddTable(std::make_unique<TokenServiceTable>()); - web_database->LoadDatabase(os_crypt_.get()); + web_database->LoadDatabase(os_crypt_override ? os_crypt_override + : os_crypt_.get()); token_web_data_ = new TokenWebData( web_database, base::SingleThreadTaskRunner::GetCurrentDefault()); token_web_data_->Init(base::NullCallback()); @@ -1545,21 +1579,32 @@ // cleanup at the end of the scope of the test. auto SetUpTestAndReturnScopedCleanup = [this](bool new_encryption_enabled, bool expect_reencrypt, - std::string_view key_prefix, base::HistogramBase::Count32 expected_writes) -> base::ScopedClosureRunner { - auto histograms = std::make_unique<base::HistogramTester>(); - auto features = std::make_unique<base::test::ScopedFeatureList>(); - features->InitWithFeatureState(features::kUseNewEncryptionKeyForWebData, - new_encryption_enabled); + std::vector<std::pair<size_t, std::unique_ptr<os_crypt_async::KeyProvider>>> + providers; + providers.emplace_back( + /*precedence=*/5u, + std::make_unique<TestKeyProvider>("v1", /*use_for_encryption=*/true)); + // If `new_encryption_enabled` is true then v2 will be used for encryption + // and should trigger a re-encrypt of data previous encrypted to v1 key. + providers.emplace_back( + /*precedence=*/10u, + std::make_unique<TestKeyProvider>( + "v2", /*use_for_encryption=*/new_encryption_enabled)); + const std::string expected_prefix = new_encryption_enabled ? "v2" : "v1"; - LoadTokenDatabase(); + auto os_crypt = + std::make_unique<os_crypt_async::OSCryptAsync>(std::move(providers)); + auto histograms = std::make_unique<base::HistogramTester>(); + + LoadTokenDatabase(os_crypt.get()); InitializeOAuth2ServiceDelegate( signin::AccountConsistencyMethod::kDisabled); return base::ScopedClosureRunner(base::BindLambdaForTesting( - [this, key_prefix, expect_reencrypt, expected_writes, - histograms = std::move(histograms), feature = std::move(features)]() { + [this, expected_prefix, expect_reencrypt, expected_writes, + histograms = std::move(histograms), os_crypt = std::move(os_crypt)]() { UnloadTokenDatabase(); { // The APIs available via WebData always return plaintext data so @@ -1574,7 +1619,7 @@ ASSERT_TRUE(s.Step()); std::string encrypted_data; ASSERT_TRUE(s.ColumnBlobAsString(0, &encrypted_data)); - EXPECT_TRUE(base::StartsWith(encrypted_data, key_prefix, + EXPECT_TRUE(base::StartsWith(encrypted_data, expected_prefix, base::CompareCase::SENSITIVE)); // Should only be one row, the "invalid-token" should be deleted by // the time the database is unloaded, and never re-encrypted. @@ -1594,7 +1639,6 @@ // to set up the database. auto cleanup = SetUpTestAndReturnScopedCleanup( /*new_encryption_enabled=*/false, /*expect_reencrypt=*/false, - os_crypt_async::kOsCryptSyncCompatibleTestKeyPrefix, /*expected_writes=*/2); // Verify DB is clean. @@ -1623,7 +1667,7 @@ // re-encrypted to the database, as expected. auto cleanup = SetUpTestAndReturnScopedCleanup( /*new_encryption_enabled=*/true, /*expect_reencrypt=*/true, - os_crypt_async::kDefaultTestKeyPrefix, /*expected_writes=*/2); + /*expected_writes=*/2); // Add another invalid token. This will be not re-encrypted, and removed // during LoadAllCredentialsIntoMemory. @@ -1646,7 +1690,7 @@ // no writes are needed. auto cleanup = SetUpTestAndReturnScopedCleanup( /*new_encryption_enabled=*/true, /*expect_reencrypt=*/false, - os_crypt_async::kDefaultTestKeyPrefix, /*expected_writes=*/0); + /*expected_writes=*/0); ResetObserverCounts(); oauth2_service_delegate_->LoadCredentials(primary_account); @@ -1664,7 +1708,6 @@ // key. auto cleanup = SetUpTestAndReturnScopedCleanup( /*new_encryption_enabled=*/false, /*expect_reencrypt=*/true, - os_crypt_async::kOsCryptSyncCompatibleTestKeyPrefix, /*expected_writes=*/1); ResetObserverCounts();
diff --git a/components/signin/public/base/signin_prefs.cc b/components/signin/public/base/signin_prefs.cc index 5210a140..e34cb37 100644 --- a/components/signin/public/base/signin_prefs.cc +++ b/components/signin/public/base/signin_prefs.cc
@@ -82,15 +82,27 @@ constexpr char kBookmarksExplicitBrowserSigninEnabled[] = "BookmarksExplicitBrowserSigninEnabled"; +// History Sync promo on the avatar button. +// +// Number of times the history sync promo was shown in the identity pill (avata +// toolbar button). +constexpr std::string_view kHistorySyncPromoIdentityPillShownCount = + "ChromeSigninSyncPromoIdentityPillShownCount"; +// Number of times the history sync promo was used (clicked) in the identity +// pill (avatar toolbar button). +constexpr std::string_view kHistorySyncPromoIdentityPillUsedCount = + "ChromeSigninSyncPromoIdentityPillUsedCount"; + +// Sync promo on the avatar button. +// // Number of times the sync promo was shown in the identity pill (avatar toolbar // button). constexpr std::string_view kSyncPromoIdentityPillShownCount = - "ChromeSigninSyncPromoIdentityPillShownCount"; - + "SyncPromoIdentityPillShownCount"; // Number of times the sync promo was used (clicked) in the identity pill // (avatar toolbar button). constexpr std::string_view kSyncPromoIdentityPillUsedCount = - "ChromeSigninSyncPromoIdentityPillUsedCount"; + "SyncPromoIdentityPillUsedCount"; // Number of times the Bookmark Batch Upload promo was dismissed. constexpr std::string_view kBookmarkBatchUploadPromoDismissCount = @@ -110,8 +122,6 @@ registry->RegisterDictionaryPref(kSigninAccountPrefs); registry->RegisterIntegerPref(prefs::kHistorySyncSuccessiveDeclineCount, 0); registry->RegisterInt64Pref(prefs::kHistorySyncLastDeclinedTimestamp, 0); - registry->RegisterIntegerPref(kSyncPromoIdentityPillShownCount, 0); - registry->RegisterIntegerPref(kSyncPromoIdentityPillUsedCount, 0); } bool SigninPrefs::HasAccountPrefs(const GaiaId& gaia_id) const { @@ -294,6 +304,26 @@ kBookmarksExplicitBrowserSigninEnabled); } +void SigninPrefs::IncrementHistorySyncPromoIdentityPillShownCount( + const GaiaId& gaia_id) { + IncrementIntPrefForAccount(gaia_id, kHistorySyncPromoIdentityPillShownCount); +} + +int SigninPrefs::GetHistorySyncPromoIdentityPillShownCount( + const GaiaId& gaia_id) const { + return GetIntPrefForAccount(gaia_id, kHistorySyncPromoIdentityPillShownCount); +} + +void SigninPrefs::IncrementHistorySyncPromoIdentityPillUsedCount( + const GaiaId& gaia_id) { + IncrementIntPrefForAccount(gaia_id, kHistorySyncPromoIdentityPillUsedCount); +} + +int SigninPrefs::GetHistorySyncPromoIdentityPillUsedCount( + const GaiaId& gaia_id) const { + return GetIntPrefForAccount(gaia_id, kHistorySyncPromoIdentityPillUsedCount); +} + void SigninPrefs::IncrementSyncPromoIdentityPillShownCount( const GaiaId& gaia_id) { IncrementIntPrefForAccount(gaia_id, kSyncPromoIdentityPillShownCount);
diff --git a/components/signin/public/base/signin_prefs.h b/components/signin/public/base/signin_prefs.h index f336a82ae..d41cfd0 100644 --- a/components/signin/public/base/signin_prefs.h +++ b/components/signin/public/base/signin_prefs.h
@@ -116,9 +116,14 @@ void SetBookmarksExplicitBrowserSignin(const GaiaId& gaia_id, bool enabled); bool GetBookmarksExplicitBrowserSignin(const GaiaId& gaia_id) const; + // History Sync experiment promo on the avatar button. + void IncrementHistorySyncPromoIdentityPillShownCount(const GaiaId& gaia_id); + int GetHistorySyncPromoIdentityPillShownCount(const GaiaId& gaia_id) const; + void IncrementHistorySyncPromoIdentityPillUsedCount(const GaiaId& gaia_id); + int GetHistorySyncPromoIdentityPillUsedCount(const GaiaId& gaia_id) const; + // Sync promo on the avatar button. void IncrementSyncPromoIdentityPillShownCount(const GaiaId& gaia_id); int GetSyncPromoIdentityPillShownCount(const GaiaId& gaia_id) const; - void IncrementSyncPromoIdentityPillUsedCount(const GaiaId& gaia_id); int GetSyncPromoIdentityPillUsedCount(const GaiaId& gaia_id) const;
diff --git a/components/signin/public/base/signin_switches.cc b/components/signin/public/base/signin_switches.cc index 8621fba9..ae2a7e5e 100644 --- a/components/signin/public/base/signin_switches.cc +++ b/components/signin/public/base/signin_switches.cc
@@ -9,6 +9,10 @@ #include "components/prefs/pref_service.h" #include "components/signin/public/base/signin_pref_names.h" +#if BUILDFLAG(IS_WIN) +#include "base/win/windows_version.h" +#endif + namespace switches { // All switches in alphabetical order. @@ -360,6 +364,31 @@ base::FEATURE_DISABLED_BY_DEFAULT); #endif +#if BUILDFLAG(IS_WIN) +// Enables expanding the Avatar Pill to show a sync promo. Expected to be used +// by Windows users only. +BASE_FEATURE(kAvatarButtonSyncPromo, + "AvatarButtonSyncPromo", + base::FEATURE_DISABLED_BY_DEFAULT); +#endif +// Convenient testing flag for `kAvatarButtonSyncPromo` on all platforms. +BASE_FEATURE(kAvatarButtonSyncPromoForTesting, + "AvatarButtonSyncPromoForTesting", + base::FEATURE_DISABLED_BY_DEFAULT); + +bool IsAvatarSyncPromoFeatureEnabled() { + if (base::FeatureList::IsEnabled( + switches::kAvatarButtonSyncPromoForTesting)) { + return true; + } +#if BUILDFLAG(IS_WIN) + return (base::win::GetVersion() <= base::win::Version::WIN10_22H2) && + base::FeatureList::IsEnabled(switches::kAvatarButtonSyncPromo); +#else + return false; +#endif +} + } // namespace switches #if BUILDFLAG(IS_CHROMEOS)
diff --git a/components/signin/public/base/signin_switches.h b/components/signin/public/base/signin_switches.h index fd54d695..4ac442647 100644 --- a/components/signin/public/base/signin_switches.h +++ b/components/signin/public/base/signin_switches.h
@@ -266,6 +266,16 @@ BASE_DECLARE_FEATURE(kEnforceManagementDisclaimer); #endif +#if BUILDFLAG(IS_WIN) +COMPONENT_EXPORT(SIGNIN_SWITCHES) +BASE_DECLARE_FEATURE(kAvatarButtonSyncPromo); +#endif +COMPONENT_EXPORT(SIGNIN_SWITCHES) +BASE_DECLARE_FEATURE(kAvatarButtonSyncPromoForTesting); + +COMPONENT_EXPORT(SIGNIN_SWITCHES) +bool IsAvatarSyncPromoFeatureEnabled(); + } // namespace switches // TODO(crbug.com/337879458): Move switches below into the switches namespace.
diff --git a/components/user_data_importer/content/stable_portability_data_importer_unittest.cc b/components/user_data_importer/content/stable_portability_data_importer_unittest.cc index 961e1b4..db364dbd 100644 --- a/components/user_data_importer/content/stable_portability_data_importer_unittest.cc +++ b/components/user_data_importer/content/stable_portability_data_importer_unittest.cc
@@ -78,14 +78,56 @@ return true; } +MATCHER_P4(IsUrlBookmarkWithinTimeRange, title, url, min_time, max_time, "") { + if (!testing::ExplainMatchResult(IsUrlBookmark(title, url), arg, + result_listener)) { + return false; + } + if (arg->date_added() < min_time || arg->date_added() > max_time) { + *result_listener << "which has creation time " << arg->date_added() + << " not in range [" << min_time << ", " << max_time + << "]"; + return false; + } + return true; +} + +MATCHER_P4(IsReadingListEntryWithinTimeRange, + title, + url, + min_time, + max_time, + "") { + if (arg->Title() != title) { + *result_listener << "which has title " << arg->Title(); + return false; + } + if (arg->URL() != url) { + *result_listener << "which has url " << arg->URL(); + return false; + } + base::Time creation_time = + base::Time::UnixEpoch() + base::Microseconds(arg->CreationTime()); + if (creation_time < min_time || creation_time > max_time) { + *result_listener << "which has creation time " << creation_time + << " not in range [" << min_time << ", " << max_time + << "]"; + return false; + } + return true; +} + class StablePortabilityDataImporterTest : public testing::Test { public: StablePortabilityDataImporterTest() : receiver_(&fake_utility_parser_) {} protected: void SetUp() override { - CHECK(history_dir_.CreateUniqueTempDir()); bookmark_model_ = bookmarks::TestBookmarkClient::CreateModel(); + mojo::PendingRemote<user_data_importer::mojom::BookmarkHtmlParser> + pending_remote{receiver_.BindNewPipeAndPassRemote()}; + auto parser = base::MakeRefCounted<ContentBookmarkParser>(); + parser->SetServiceForTesting(std::move(pending_remote)); auto storage = std::make_unique<FakeReadingListModelStorage>(); auto* storage_raw = storage.get(); @@ -94,6 +136,14 @@ syncer::WipeModelUponSyncDisabledBehavior::kNever, base::DefaultClock::GetInstance()); storage_raw->TriggerLoadCompletion(); + + CHECK(history_dir_.CreateUniqueTempDir()); + history_service_ = + history::CreateHistoryService(history_dir_.GetPath(), true); + + importer_ = std::make_unique<StablePortabilityDataImporter>( + history_service_.get(), bookmark_model_.get(), + reading_list_model_.get(), std::move(parser)); } void TearDown() override { task_environment_.RunUntilIdle(); } @@ -153,7 +203,7 @@ base::File file(bookmarks_file, base::File::FLAG_OPEN | base::File::FLAG_READ); - importer()->ImportBookmarks( + importer_->ImportBookmarks( std::move(file), // Use of Unretained below is safe because the RunUntil loop below // guarantees this outlives the tasks. @@ -172,7 +222,7 @@ base::File file(reading_list_file, base::File::FLAG_OPEN | base::File::FLAG_READ); - importer()->ImportReadingList( + importer_->ImportReadingList( std::move(file), // Use of Unretained below is safe because the RunUntil loop below // guarantees this outlives the tasks. @@ -188,7 +238,7 @@ PrepareCallbacks(); const size_t default_batch_size = 10; - importer()->ImportHistory( + importer_->ImportHistory( history_file, // Use of Unretained below is safe because the RunUntil loop below // guarantees this outlives the tasks. @@ -214,31 +264,7 @@ return future.Take(); } - void InitializeHistoryService() { CreateImporterAndService(true); } - private: - StablePortabilityDataImporter* importer() { - if (!importer_) { - CreateImporterAndService(/*create_history_db=*/false); - } - return importer_.get(); - } - - // We need to perform a lazy initialization of the importer because the - // history database is created lazily. This is necessary to ensure that each - // test starts with a clean slate. - void CreateImporterAndService(bool create_history_db) { - history_service_ = history::CreateHistoryService(history_dir_.GetPath(), - create_history_db); - mojo::PendingRemote<user_data_importer::mojom::BookmarkHtmlParser> - pending_remote{receiver_.BindNewPipeAndPassRemote()}; - auto parser = base::MakeRefCounted<ContentBookmarkParser>(); - parser->SetServiceForTesting(std::move(pending_remote)); - importer_ = std::make_unique<StablePortabilityDataImporter>( - history_service_.get(), bookmark_model_.get(), - reading_list_model_.get(), std::move(parser)); - } - void OnBookmarksConsumed(int number_imported) { bookmarks_callback_called_ = true; number_bookmarks_imported_ = number_imported; @@ -279,6 +305,7 @@ // Tests parsing a simple HTML file with two bookmarks. TEST_F(StablePortabilityDataImporterTest, Bookmarks_Basic) { + const base::Time import_start_time = base::Time::Now(); ImportBookmarks(R"( <!DOCTYPE NETSCAPE-Bookmark-file-1> <!--This is an automatically generated file. @@ -289,6 +316,7 @@ <DT><A HREF="https://www.chromium.org/">Chromium</A> </DL>)"); EXPECT_EQ(GetNumberOfBookmarksImported(), 2); + const base::Time import_end_time = base::Time::Now(); const bookmarks::BookmarkNode* other_node = GetOtherBookmarkNode(); EXPECT_THAT( @@ -298,15 +326,16 @@ ElementsAre( IsUrlBookmark(u"Google", GURL("https://www.google.com/"), base::Time::FromSecondsSinceUnixEpoch(904914000)), - // No timestamp maps to current time. - IsUrlBookmark(u"Chromium", GURL("https://www.chromium.org/"), - base::Time::Now()))))); - + // No timestamp maps to current time, within the import time. + IsUrlBookmarkWithinTimeRange( + u"Chromium", GURL("https://www.chromium.org/"), + import_start_time, import_end_time))))); EXPECT_EQ(GetReadingListModel().size(), 0u); } // Identical to the above test, but without the top-level <DL> tag enclosing it. TEST_F(StablePortabilityDataImporterTest, Bookmarks_NoTopLevelDL) { + const base::Time import_start_time = base::Time::Now(); ImportBookmarks( R"(<!DOCTYPE NETSCAPE-Bookmark-file-1> <!--This is an automatically generated file. @@ -314,6 +343,7 @@ Do Not Edit! --> <DT><A HREF="https://www.google.com/" ADD_DATE="904914000">Google</A> <DT><A HREF="https://www.chromium.org/">Chromium</A>)"); + const base::Time import_end_time = base::Time::Now(); EXPECT_EQ(GetNumberOfBookmarksImported(), 2); const bookmarks::BookmarkNode* other_node = GetOtherBookmarkNode(); @@ -324,9 +354,9 @@ ElementsAre( IsUrlBookmark(u"Google", GURL("https://www.google.com/"), base::Time::FromSecondsSinceUnixEpoch(904914000)), - // No timestamp maps to current time. - IsUrlBookmark(u"Chromium", GURL("https://www.chromium.org/"), - base::Time::Now()))))); + IsUrlBookmarkWithinTimeRange( + u"Chromium", GURL("https://www.chromium.org/"), + import_start_time, import_end_time))))); EXPECT_EQ(GetReadingListModel().size(), 0u); } @@ -382,6 +412,7 @@ // Tests parsing a simple HTML file with three reading list items. TEST_F(StablePortabilityDataImporterTest, ReadingList) { + const base::Time import_start_time = base::Time::Now(); ImportReadingList( R"(<!DOCTYPE NETSCAPE-Bookmark-file-1> <!--This is an automatically generated file. @@ -393,6 +424,7 @@ <DT><A HREF="https://en.wikipedia.org/wiki/Brian_Wilson" ADD_DATE="-868878000">Brian Wilson</A> </DL><p> </DL>)"); + const base::Time import_end_time = base::Time::Now(); EXPECT_EQ(GetNumberOfReadingListImported(), 3); EXPECT_EQ(GetReadingListModel().size(), 3u); @@ -401,51 +433,33 @@ const bookmarks::BookmarkNode* other_node = GetOtherBookmarkNode(); EXPECT_THAT(other_node->children(), ElementsAre()); - EXPECT_THAT( - GetReadingListModel().GetKeys(), - UnorderedElementsAre(GURL("https://www.google.com/"), - GURL("https://en.wikipedia.org/wiki/The_Beach_Boys"), - GURL("https://en.wikipedia.org/wiki/Brian_Wilson"))); + std::vector<const ReadingListEntry*> entries; + for (const auto& gurl : GetReadingListModel().GetKeys()) { + entries.push_back(GetReadingListModel().GetEntryByURL(gurl).get()); + } - const ReadingListEntry* entry1 = - GetReadingListModel() - .GetEntryByURL(GURL("https://www.google.com/")) - .get(); - ASSERT_TRUE(entry1); - EXPECT_EQ(entry1->Title(), "Google"); - // TODO(crbug.com/431203204): Implement actually importing the creation time. - // Then the expectation should become - // `base::Time::FromSecondsSinceUnixEpoch(904914000)`. - EXPECT_EQ( - base::Time::UnixEpoch() + base::Microseconds(entry1->CreationTime()), - base::Time::Now()); - - const ReadingListEntry* entry2 = - GetReadingListModel() - .GetEntryByURL(GURL("https://en.wikipedia.org/wiki/The_Beach_Boys")) - .get(); - ASSERT_TRUE(entry2); - EXPECT_EQ(entry2->Title(), "The Beach Boys"); - // No timestamp maps to current time. - EXPECT_EQ( - base::Time::UnixEpoch() + base::Microseconds(entry2->CreationTime()), - base::Time::Now()); - - const ReadingListEntry* entry3 = - GetReadingListModel() - .GetEntryByURL(GURL("https://en.wikipedia.org/wiki/Brian_Wilson")) - .get(); - ASSERT_TRUE(entry3); - EXPECT_EQ(entry3->Title(), "Brian Wilson"); - // Invalid timestamp maps to current time. - EXPECT_EQ( - base::Time::UnixEpoch() + base::Microseconds(entry3->CreationTime()), - base::Time::Now()); + EXPECT_THAT(entries, + UnorderedElementsAre( + // TODO(crbug.com/431203204): Implement actually importing the + // creation time. Then the expectation should become + // `base::Time::FromSecondsSinceUnixEpoch(904914000)`. + IsReadingListEntryWithinTimeRange( + "Google", GURL("https://www.google.com/"), + import_start_time, import_end_time), + IsReadingListEntryWithinTimeRange( + "The Beach Boys", + GURL("https://en.wikipedia.org/wiki/The_Beach_Boys"), + import_start_time, import_end_time), + IsReadingListEntryWithinTimeRange( + "Brian Wilson", + GURL("https://en.wikipedia.org/wiki/Brian_Wilson"), + import_start_time, import_end_time))); } // Tests parsing an HTML with several not valid formats. The parser should still // try to parse as many items as possible. TEST_F(StablePortabilityDataImporterTest, Bookmarks_MiscJunk) { + const base::Time import_start_time = base::Time::Now(); ImportBookmarks(R"( <!DOCTYPE NETSCAPE-Bookmark-file-1> <!--This is an automatically generated file. @@ -467,7 +481,7 @@ ISLIVEPREVIEW="true" PREVIEWSIZE="100 x 100" </DL>)"); - + const base::Time import_end_time = base::Time::Now(); EXPECT_EQ(GetNumberOfBookmarksImported(), 2); const bookmarks::BookmarkNode* other_node = GetOtherBookmarkNode(); @@ -481,21 +495,19 @@ // The folder contains a mix of invalid and valid // entries. Ensure the valid ones are preserved. ElementsAre( - IsUrlBookmark(u"Chromium", - GURL("https://www.chromium.org/"), - // No timestamp maps to current time. - base::Time::Now()), - IsUrlBookmark( + IsUrlBookmarkWithinTimeRange( + u"Chromium", GURL("https://www.chromium.org/"), + import_start_time, import_end_time), + IsUrlBookmarkWithinTimeRange( u"Example", GURL("https://www.example.org/"), - // Invalid timestamp maps to current time. - base::Time::Now()) + import_start_time, import_end_time) + // <A>Google Reader</A> was skipped for lack of URL. )))))); } // Tests parsing a simple JSON file with two history entries. TEST_F(StablePortabilityDataImporterTest, History_Basic) { - InitializeHistoryService(); const char kHistoryJson[] = R"({ "metadata": { "data_type": "history_visits" @@ -528,16 +540,16 @@ expected_row1.set_title(u"Google"); expected_row1.set_visit_count(5); expected_row1.set_typed_count(2); - expected_row1.set_last_visit( - base::Time::UnixEpoch() + base::Microseconds(1674205200000000)); + expected_row1.set_last_visit(base::Time::UnixEpoch() + + base::Microseconds(1674205200000000)); history::URLResult expected_row2; expected_row2.set_url(GURL("https://www.chromium.org/")); expected_row2.set_title(u"Chromium"); expected_row2.set_visit_count(1); expected_row2.set_typed_count(0); - expected_row2.set_last_visit( - base::Time::UnixEpoch() + base::Microseconds(1674205260000000)); + expected_row2.set_last_visit(base::Time::UnixEpoch() + + base::Microseconds(1674205260000000)); EXPECT_THAT(results, UnorderedElementsAre(URLResultEq(expected_row1), URLResultEq(expected_row2))); @@ -545,7 +557,6 @@ // Tests parsing an invalid JSON file. TEST_F(StablePortabilityDataImporterTest, History_InvalidJson) { - InitializeHistoryService(); const char kHistoryJson[] = R"({ "metadata": { "data_type": "history_visits" @@ -561,7 +572,6 @@ // Tests parsing a valid JSON file with no history entries. TEST_F(StablePortabilityDataImporterTest, History_NoEntries) { - InitializeHistoryService(); const char kHistoryJson[] = R"({ "metadata": { "data_type": "history_visits" @@ -574,7 +584,6 @@ // Tests parsing a large JSON file that is processed in chunks. TEST_F(StablePortabilityDataImporterTest, History_LargeFileInChunks) { - InitializeHistoryService(); const int num_visits = 15; std::vector<std::string> visits; for (int i = 0; i < num_visits; ++i) { @@ -598,9 +607,9 @@ std::vector<testing::Matcher<history::URLResult>> matchers; for (int i = 0; i < num_visits; ++i) { history::URLResult expected_row; - expected_row.set_url(GURL(absl::StrFormat("https://www.example.com/%d", i))); - expected_row.set_title( - base::UTF8ToUTF16(absl::StrFormat("Title %d", i))); + expected_row.set_url( + GURL(absl::StrFormat("https://www.example.com/%d", i))); + expected_row.set_title(base::UTF8ToUTF16(absl::StrFormat("Title %d", i))); expected_row.set_visit_count(1); expected_row.set_typed_count(0); expected_row.set_last_visit(base::Time::UnixEpoch() +
diff --git a/components/variations/service/variations_field_trial_creator_base.cc b/components/variations/service/variations_field_trial_creator_base.cc index f161aa3..bacba54 100644 --- a/components/variations/service/variations_field_trial_creator_base.cc +++ b/components/variations/service/variations_field_trial_creator_base.cc
@@ -2,11 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifdef UNSAFE_BUFFERS_BUILD -// TODO(crbug.com/390223051): Remove C-library calls to fix the errors. -#pragma allow_unsafe_libc_calls -#endif - #include "components/variations/service/variations_field_trial_creator_base.h" #include <stddef.h> @@ -84,14 +79,6 @@ base::UmaHistogramEnumeration("Variations.SeedUsage", usage); } -// If an invalid command-line to force field trials was specified, exit the -// browser with a helpful error message, so that the user can correct their -// mistake. -void ExitWithMessage(const std::string& message) { - puts(message.c_str()); - exit(1); -} - // Retrieves the value of the policy converted to the RestrictionPolicyValues. RestrictionPolicy GetVariationPolicyRestriction(PrefService* local_state) { int value = local_state->GetInteger(prefs::kVariationsRestrictionsByPolicy); @@ -263,7 +250,7 @@ switch (result) { case VariationsIdsProvider::ForceIdsResult::INVALID_SWITCH_ENTRY: - ExitWithMessage(base::StringPrintf("Invalid --%s list specified.", + client_->ExitWithMessage(base::StringPrintf("Invalid --%s list specified.", switches::kForceVariationIds)); break; case VariationsIdsProvider::ForceIdsResult::INVALID_VECTOR_ENTRY: @@ -279,7 +266,7 @@ bool success = http_header_provider->ForceDisableVariationIds( command_line->GetSwitchValueASCII(switches::kForceDisableVariationIds)); if (!success) { - ExitWithMessage(base::StringPrintf("Invalid --%s list specified.", + client_->ExitWithMessage(base::StringPrintf("Invalid --%s list specified.", switches::kForceDisableVariationIds)); } @@ -303,7 +290,7 @@ } #else if (command_line->HasSwitch(switches::kEnableFieldTrialTestingConfig)) { - ExitWithMessage( + client_->ExitWithMessage( base::StringPrintf("--%s was passed, but the field trial testing " "config was excluded from the build.", switches::kEnableFieldTrialTestingConfig)); @@ -742,7 +729,7 @@ file_deserializer.Deserialize(&error_code, &error_message); if (!json_contents) { - ExitWithMessage(base::StringPrintf("Failed to load \"%s\" %s (%i)", + client_->ExitWithMessage(base::StringPrintf("Failed to load \"%s\" %s (%i)", json_seed_path.AsUTF8Unsafe().c_str(), error_message.c_str(), error_code)); } @@ -753,13 +740,13 @@ json_contents->GetDict().Find(prefs::kVariationsSeedSignature); if (!seed_data || !seed_data->is_string()) { - ExitWithMessage( + client_->ExitWithMessage( base::StrCat({"Missing or invalid seed data in contents of \"", json_seed_path.AsUTF8Unsafe(), "\""})); } if (!seed_signature || !seed_signature->is_string()) { - ExitWithMessage( + client_->ExitWithMessage( base::StrCat({"Missing or invalid seed signature in contents of \"", json_seed_path.AsUTF8Unsafe(), "\""})); } @@ -772,7 +759,7 @@ // Override Local State seed prefs. std::string decoded_seed; if (!base::Base64Decode(seed_data->GetString(), &decoded_seed)) { - ExitWithMessage( + client_->ExitWithMessage( base::StrCat({"Failed to decode seed data in contents of \"", json_seed_path.AsUTF8Unsafe(), "\""})); }
diff --git a/components/variations/service/variations_service_client.cc b/components/variations/service/variations_service_client.cc index 89bdf04..54f1e08 100644 --- a/components/variations/service/variations_service_client.cc +++ b/components/variations/service/variations_service_client.cc
@@ -4,6 +4,13 @@ #include "components/variations/service/variations_service_client.h" +#ifdef UNSAFE_BUFFERS_BUILD +// TODO(crbug.com/390223051): Remove C-library calls to fix the errors. +#pragma allow_unsafe_libc_calls +#endif + +#include <cstdio> +#include <cstdlib> #include "base/command_line.h" #include "base/files/file_path.h" #include "base/logging.h" @@ -90,4 +97,9 @@ return nullptr; } +void VariationsServiceClient::ExitWithMessage(const std::string& message) { + puts(message.c_str()); + exit(1); +} + } // namespace variations
diff --git a/components/variations/service/variations_service_client.h b/components/variations/service/variations_service_client.h index 07d091e..ea9d8f1 100644 --- a/components/variations/service/variations_service_client.h +++ b/components/variations/service/variations_service_client.h
@@ -64,6 +64,10 @@ // returns nullptr. virtual std::unique_ptr<SeedResponse> TakeSeedFromNativeVariationsSeedStore(); + // If an invalid command-line was specified by the user, flag an error to the + // user and exit the process. + virtual void ExitWithMessage(const std::string& message); + // Returns whether the client is enterprise. // TODO(manukh): crbug.com/1003025. This is inconsistent with UMA which // analyzes brand_code to determine if the client is an enterprise user:
diff --git a/components/vector_icons/BUILD.gn b/components/vector_icons/BUILD.gn index 1d1f167..1d22a3f0 100644 --- a/components/vector_icons/BUILD.gn +++ b/components/vector_icons/BUILD.gn
@@ -244,6 +244,7 @@ "sync_chrome_refresh.icon", "sync_off_chrome_refresh.icon", "sync_problem_chrome_refresh.icon", + "sync_saved_locally.icon", "tab_search.icon", "tenancy.icon", "thumb_down.icon", @@ -277,6 +278,7 @@ "vr_headset_off_chrome_refresh.icon", "warning.icon", "warning_outline.icon", + "web_asset_off.icon", "work.icon", ]
diff --git a/components/vector_icons/sync_saved_locally.icon b/components/vector_icons/sync_saved_locally.icon new file mode 100644 index 0000000..55e1e33 --- /dev/null +++ b/components/vector_icons/sync_saved_locally.icon
@@ -0,0 +1,39 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +CANVAS_DIMENSIONS, 20, +FILL_RULE_NONZERO, +R_MOVE_TO, 9.11, 11.63, +R_LINE_TO, 4.6, -4.61, +R_LINE_TO, -1.14, -1.12, +R_LINE_TO, -3.46, 3.46, +R_LINE_TO, -1.69, -1.69, +LINE_TO, 6.27, 8.79, +CLOSE, +MOVE_TO, 1, 17, +R_V_LINE_TO, -1.5, +R_H_LINE_TO, 18, +V_LINE_TO, 17, +CLOSE, +R_MOVE_TO, 2.5, -2.5, +R_CUBIC_TO, -0.41, 0, -0.77, -0.15, -1.06, -0.44, +ARC_TO, 1.44, 1.44, 0, 0, 1, 2, 13, +V_LINE_TO, 4.5, +R_CUBIC_TO, 0, -0.41, 0.15, -0.77, 0.44, -1.06, +ARC_TO, 1.44, 1.44, 0, 0, 1, 3.5, 3, +R_H_LINE_TO, 13, +R_CUBIC_TO, 0.41, 0, 0.77, 0.15, 1.06, 0.44, +R_CUBIC_TO, 0.29, 0.29, 0.44, 0.65, 0.44, 1.06, +V_LINE_TO, 13, +R_CUBIC_TO, 0, 0.41, -0.15, 0.77, -0.44, 1.06, +R_ARC_TO, 1.44, 1.44, 0, 0, 1, -1.06, 0.44, +CLOSE, +R_MOVE_TO, 0, -1.5, +R_H_LINE_TO, 13, +V_LINE_TO, 4.5, +R_H_LINE_TO, -13, +CLOSE, +R_MOVE_TO, 0, 0, +V_LINE_TO, 4.5, +CLOSE
diff --git a/components/vector_icons/web_asset_off.icon b/components/vector_icons/web_asset_off.icon new file mode 100644 index 0000000..d80667a --- /dev/null +++ b/components/vector_icons/web_asset_off.icon
@@ -0,0 +1,37 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +CANVAS_DIMENSIONS, 20, +FILL_RULE_NONZERO, +MOVE_TO, 16.73, 18.83, +LINE_TO, 13.88, 16, +H_LINE_TO, 3.5, +R_CUBIC_TO, -0.41, 0, -0.77, -0.15, -1.06, -0.44, +ARC_TO, 1.45, 1.45, 0, 0, 1, 2, 14.5, +V_LINE_TO, 5.49, +R_CUBIC_TO, 0, -0.41, 0.15, -0.76, 0.46, -1.05, +ARC_TO, 1.55, 1.55, 0, 0, 1, 3.56, 4, +R_H_LINE_TO, 0.23, +R_V_LINE_TO, 1.9, +LINE_TO, 1.17, 3.27, +LINE_TO, 2.23, 2.23, +R_LINE_TO, 15.54, 15.54, +CLOSE, +MOVE_TO, 3.5, 14.5, +R_H_LINE_TO, 8.88, +LINE_TO, 4.88, 7, +H_LINE_TO, 3.5, +CLOSE, +R_MOVE_TO, 14.13, 1, +LINE_TO, 16.5, 14.38, +V_LINE_TO, 7, +H_LINE_TO, 9.13, +R_LINE_TO, -3, -3, +H_LINE_TO, 16.5, +R_CUBIC_TO, 0.41, 0, 0.77, 0.15, 1.06, 0.44, +R_CUBIC_TO, 0.29, 0.29, 0.44, 0.65, 0.44, 1.06, +R_V_LINE_TO, 9, +R_CUBIC_TO, 0, 0.19, -0.04, 0.38, -0.1, 0.56, +R_CUBIC_TO, -0.07, 0.18, -0.16, 0.33, -0.27, 0.44, +CLOSE
diff --git a/components/visitedlink/common/visitedlink_common.h b/components/visitedlink/common/visitedlink_common.h index 5b9741406..42c440e 100644 --- a/components/visitedlink/common/visitedlink_common.h +++ b/components/visitedlink/common/visitedlink_common.h
@@ -11,6 +11,7 @@ #include <string_view> #include <vector> +#include "base/compiler_specific.h" #include "base/memory/raw_ptr.h" #include "components/visitedlink/core/visited_link.h" @@ -144,7 +145,7 @@ Fingerprint FingerprintAt(int32_t table_offset) const { if (!hash_table_) return null_fingerprint_; - return hash_table_[table_offset]; + return UNSAFE_TODO(hash_table_[table_offset]); } // Computes the fingerprint of the given canonical URL. It is static so the
diff --git a/components/webdata/common/web_database_service.cc b/components/webdata/common/web_database_service.cc index d53ed24d..38a3f72 100644 --- a/components/webdata/common/web_database_service.cc +++ b/components/webdata/common/web_database_service.cc
@@ -9,7 +9,6 @@ #include <utility> #include "base/check.h" -#include "base/feature_list.h" #include "base/functional/bind.h" #include "base/location.h" #include "base/task/sequenced_task_runner.h" @@ -20,20 +19,6 @@ #include "components/webdata/common/web_data_service_consumer.h" #include "components/webdata/common/web_database_backend.h" -namespace features { - -// If enabled, then an Encryptor will be requested that is not always backwards -// compatible with OSCrypt Sync. On some platforms, this might mean a key is -// used that is stored more securely, such as using App-Bound encryption on -// Windows. -// If this feature is enabled, any data stored by `WebDatabaseService` is not -// guaranteed to be retrievable if OSCrypt Async is not used. -BASE_FEATURE(kUseNewEncryptionKeyForWebData, - "UseNewEncryptionKeyForWebData", - base::FEATURE_ENABLED_BY_DEFAULT); - -} // namespace features - // Receives messages from the backend on the DB sequence, posts them to // WebDatabaseService on the UI sequence. class WebDatabaseService::BackendDelegate @@ -97,14 +82,8 @@ } void WebDatabaseService::LoadDatabase(os_crypt_async::OSCryptAsync* os_crypt) { - const auto option = - base::FeatureList::IsEnabled(features::kUseNewEncryptionKeyForWebData) - ? os_crypt_async::Encryptor::Option::kNone - : os_crypt_async::Encryptor::Option::kEncryptSyncCompat; - // TODO(crbug.com/40267945): Place kEncryptSyncCompat behind base::Feature and - // then remove it. os_crypt->GetInstance( - base::BindOnce(&WebDatabaseService::CompleteLoadDatabase, this), option); + base::BindOnce(&WebDatabaseService::CompleteLoadDatabase, this)); } void WebDatabaseService::ShutdownDatabase() {
diff --git a/components/webdata/common/web_database_service.h b/components/webdata/common/web_database_service.h index b690cc7..297710d 100644 --- a/components/webdata/common/web_database_service.h +++ b/components/webdata/common/web_database_service.h
@@ -12,7 +12,6 @@ #include <memory> #include "base/compiler_specific.h" -#include "base/feature_list.h" #include "base/files/file_path.h" #include "base/functional/callback_forward.h" #include "base/memory/ref_counted.h" @@ -38,10 +37,6 @@ class WDTypedResult; -namespace features { -WEBDATA_EXPORT BASE_DECLARE_FEATURE(kUseNewEncryptionKeyForWebData); -} // namespace features - //////////////////////////////////////////////////////////////////////////////// // // WebDatabaseService defines the interface to a generic data repository
diff --git a/components/zucchini/disassembler_elf.cc b/components/zucchini/disassembler_elf.cc index 5040f42..8293d6e3 100644 --- a/components/zucchini/disassembler_elf.cc +++ b/components/zucchini/disassembler_elf.cc
@@ -283,11 +283,12 @@ elf::Elf32_Half string_section_id = header_->e_shstrndx; if (string_section_id >= sections_count_) return false; - size_t section_names_size = sections_[string_section_id].sh_size; + size_t section_names_size = UNSAFE_TODO(sections_[string_section_id]).sh_size; if (section_names_size > 0) { // If nonempty, then last byte of string section must be null. const char* section_names = nullptr; - source = BufferSource(image_, sections_[string_section_id].sh_offset); + source = BufferSource(image_, + UNSAFE_TODO(sections_[string_section_id].sh_offset)); section_names = source.GetArray<char>(section_names_size); if (!section_names || UNSAFE_TODO(section_names[section_names_size - 1]) != '\0') { @@ -300,7 +301,8 @@ // Visits |segments_| to get estimate on |offset_bound|. for (const typename Traits::Elf_Phdr* segment = segments_; - segment != segments_ + segments_count_; UNSAFE_TODO(++segment)) { + segment != UNSAFE_TODO(segments_ + segments_count_); + UNSAFE_TODO(++segment)) { // |image_.covers()| is a sufficient check except when size_t is 32 bit and // parsing ELF64. In such cases a value-in-range check is needed on the // segment. This fixes crbug/1035603. @@ -321,7 +323,7 @@ section_judgements_.reserve(sections_count_); for (int i = 0; i < sections_count_; ++i) { - const typename Traits::Elf_Shdr* section = §ions_[i]; + const typename Traits::Elf_Shdr* section = UNSAFE_TODO(§ions_[i]); int judgement = JudgeSection<Traits>(image_.size(), section); section_judgements_.push_back(judgement); if ((judgement & SECTION_BIT_SAFE) == 0) @@ -355,7 +357,7 @@ DCHECK(reloc_section_dims_.empty()); DCHECK(exec_headers_.empty()); for (elf::Elf32_Half i = 0; i < sections_count_; ++i) { - const typename Traits::Elf_Shdr* section = sections_ + i; + const typename Traits::Elf_Shdr* section = UNSAFE_TODO(sections_ + i); if ((section_judgements_[i] & SECTION_BIT_MAYBE_USEFUL_FOR_POINTERS) != 0) { if (IsRelocSection<Traits>(*section)) reloc_section_dims_.emplace_back(*section);
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc index bd9ca49..c4d4a29 100644 --- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc +++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -155,7 +155,7 @@ class DumpAccessibilityTreeTestExceptUIA : public DumpAccessibilityTreeTest {}; -#if !BUILDFLAG(IS_ANDROID) +#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) // Material Design accessibility tests use third_party components. class DumpAccessibilityTreeWithMaterialDesignTest : public DumpAccessibilityTreeTest { @@ -309,7 +309,7 @@ ::testing::ValuesIn(DumpAccessibilityTestBase::TreeTestPasses()), DumpAccessibilityTreeTestPassToString()); -#if !BUILDFLAG(IS_ANDROID) +#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) INSTANTIATE_TEST_SUITE_P( All, DumpAccessibilityTreeWithMaterialDesignTest, @@ -4694,7 +4694,7 @@ RunCSSTest(FILE_PATH_LITERAL("interactivity-inert.html")); } -#if !BUILDFLAG(IS_ANDROID) +#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) IN_PROC_BROWSER_TEST_P(DumpAccessibilityTreeWithMaterialDesignTest, MaterialDesignButtons) { RunMaterialDesignTest(FILE_PATH_LITERAL("buttons.html")); @@ -4795,7 +4795,7 @@ RunMaterialDesignTest(FILE_PATH_LITERAL("version-info.html")); } -#endif // BUILDFLAG(IS_ANDROID) +#endif // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS) class DumpAccessibilityTreeWithCarouselTest : public DumpAccessibilityTreeTest { void SetUpCommandLine(base::CommandLine* command_line) override { DumpAccessibilityTreeTest::SetUpCommandLine(command_line);
diff --git a/content/browser/loader/navigation_url_loader_delegate.h b/content/browser/loader/navigation_url_loader_delegate.h index 3c5d68a..ef3285a1 100644 --- a/content/browser/loader/navigation_url_loader_delegate.h +++ b/content/browser/loader/navigation_url_loader_delegate.h
@@ -107,6 +107,16 @@ CreateNavigationEarlyHintsManagerParams( const network::mojom::EarlyHints& early_hints) = 0; + // Only for testing purpose (https://crbug.com/434182226). + // In non-test cases, use `OnRequestRedirected()` instead, and this method + // must do nothing and return `false`. + // + // Called when `NavigationURLLoaderImpl::OnReceiveRedirect()` is called. + // When the return value is `true` (which is only allowed in tests), + // `head->parsed_headers` is cleared to enforce and test the async + // `ParseHeaders()` path. + virtual bool ShouldClearParsedHeadersOnTestReceiveRedirect() = 0; + protected: NavigationURLLoaderDelegate() {} virtual ~NavigationURLLoaderDelegate() {}
diff --git a/content/browser/loader/navigation_url_loader_impl.cc b/content/browser/loader/navigation_url_loader_impl.cc index 02fed1e..882c11b 100644 --- a/content/browser/loader/navigation_url_loader_impl.cc +++ b/content/browser/loader/navigation_url_loader_impl.cc
@@ -10,6 +10,7 @@ #include <string_view> #include <utility> +#include "base/check_is_test.h" #include "base/command_line.h" #include "base/containers/contains.h" #include "base/debug/crash_logging.h" @@ -775,6 +776,8 @@ } received_response_ = false; head_update_params_ = ResponseHeadUpdateParams(); + loader_holder_.OnExclusiveTaskStarted( + LoaderHolder::ExclusiveTaskType::kInterceptor); MaybeStartLoader(/*next_interceptor_index=*/0, /*interceptor_result=*/std::nullopt); } @@ -785,6 +788,11 @@ DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(started_); + if (loader_holder_.ShouldCancelExclusiveTask( + LoaderHolder::ExclusiveTaskType::kInterceptor)) { + return; + } + if (interceptor_result) { subresource_loader_params_ = std::move(interceptor_result->subresource_loader_params); @@ -826,6 +834,9 @@ void NavigationURLLoaderImpl::StartInterceptedRequest( scoped_refptr<network::SharedURLLoaderFactory> single_request_factory) { + loader_holder_.OnExclusiveTaskCompleted( + LoaderHolder::ExclusiveTaskType::kInterceptor); + std::vector<std::unique_ptr<blink::URLLoaderThrottle>> additional_throttles; // Intercepted requests need MimeSniffingThrottle to do mime sniffing. // Non-intercepted requests usually go through the regular network @@ -854,38 +865,194 @@ NavigationURLLoaderImpl::LoaderHolder::~LoaderHolder() = default; -void NavigationURLLoaderImpl::LoaderHolder::Reset() { +void NavigationURLLoaderImpl::LoaderHolder::ResetInternal() { + CheckState(); + response_loader_receiver_.reset(); url_loader_.reset(); + modified_headers_on_redirect_.reset(); + + state_ = State::kNone; + CheckState(); +} + +void NavigationURLLoaderImpl::LoaderHolder::Reset() { + switch (exclusive_task_state_) { + case ExclusiveTaskState::kNoExclusiveTask: + break; + case ExclusiveTaskState::kHasExclusiveTask: + // If there can be any possible exclusive tasks, the (possibly indirect) + // caller of `Reset()` should check `HasExclusiveTask()` and call + // `ResetForFailure()` and make the loading fail instead, if any exclusive + // tasks. This can't be done here, because we have to cancel the whole + // loading (including the new operation that triggers `Reset()`), not only + // cancalling the exclusive tasks. + // TODO(https://crbug.com/434182226): Remove DUMP_WILL_BE_. + DUMP_WILL_BE_NOTREACHED(); + break; + case ExclusiveTaskState::kCancelExclusiveTask: + // It's harmless to reach here, because the issues related to exclusive + // tasks should be already handled when transitioned + // `kCancelExclusiveTask` (i.e. by the caller of `ResetForFailure()`). + // TODO(https://crbug.com/434182226): Remove DUMP_WILL_BE_. + DUMP_WILL_BE_NOTREACHED(); + break; + } + + ResetInternal(); +} + +void NavigationURLLoaderImpl::LoaderHolder::ResetForFailure() { + exclusive_task_state_ = ExclusiveTaskState::kCancelExclusiveTask; + ResetInternal(); +} + +void NavigationURLLoaderImpl::LoaderHolder::OnExclusiveTaskStarted( + ExclusiveTaskType exclusive_task_type) { + switch (exclusive_task_state_) { + case ExclusiveTaskState::kNoExclusiveTask: + exclusive_task_state_ = ExclusiveTaskState::kHasExclusiveTask; + current_exclusive_task_type_ = exclusive_task_type; + break; + case ExclusiveTaskState::kHasExclusiveTask: + // exclusive tasks shouldn't be started while there is already another + // exclusive task. + // TODO(https://crbug.com/434182226): Remove DUMP_WILL_BE_. + DUMP_WILL_BE_NOTREACHED(); + break; + case ExclusiveTaskState::kCancelExclusiveTask: + // exclusive tasks shouldn't be started if exclusive task is to be + // cancelled. + // TODO(https://crbug.com/434182226): Remove DUMP_WILL_BE_. + DUMP_WILL_BE_NOTREACHED(); + break; + } +} + +void NavigationURLLoaderImpl::LoaderHolder::OnExclusiveTaskCompleted( + ExclusiveTaskType exclusive_task_type) { + switch (exclusive_task_state_) { + case ExclusiveTaskState::kHasExclusiveTask: + // TODO(https://crbug.com/434182226): Remove DUMP_WILL_BE_. + DUMP_WILL_BE_CHECK(current_exclusive_task_type_); + DUMP_WILL_BE_CHECK_EQ(*current_exclusive_task_type_, exclusive_task_type); + exclusive_task_state_ = ExclusiveTaskState::kNoExclusiveTask; + current_exclusive_task_type_.reset(); + break; + case ExclusiveTaskState::kNoExclusiveTask: + // TODO(https://crbug.com/434182226): Remove DUMP_WILL_BE_. + DUMP_WILL_BE_NOTREACHED(); + break; + case ExclusiveTaskState::kCancelExclusiveTask: + // TODO(https://crbug.com/434182226): Remove DUMP_WILL_BE_. + DUMP_WILL_BE_NOTREACHED(); + break; + } +} + +bool NavigationURLLoaderImpl::LoaderHolder::HasExclusiveTask() const { + switch (exclusive_task_state_) { + case ExclusiveTaskState::kNoExclusiveTask: + return false; + case ExclusiveTaskState::kHasExclusiveTask: + case ExclusiveTaskState::kCancelExclusiveTask: + return true; + } +} + +bool NavigationURLLoaderImpl::LoaderHolder::ShouldCancelExclusiveTask( + ExclusiveTaskType exclusive_task_type) const { + switch (exclusive_task_state_) { + case ExclusiveTaskState::kNoExclusiveTask: + // TODO(https://crbug.com/434182226): Remove DUMP_WILL_BE_. + DUMP_WILL_BE_NOTREACHED(); + return false; + case ExclusiveTaskState::kHasExclusiveTask: + // TODO(https://crbug.com/434182226): Remove DUMP_WILL_BE_. + DUMP_WILL_BE_CHECK(current_exclusive_task_type_); + DUMP_WILL_BE_CHECK_EQ(*current_exclusive_task_type_, exclusive_task_type); + return false; + case ExclusiveTaskState::kCancelExclusiveTask: + // TODO(https://crbug.com/434182226): Remove DUMP_WILL_BE_. + DUMP_WILL_BE_CHECK(current_exclusive_task_type_); + DUMP_WILL_BE_CHECK_EQ(*current_exclusive_task_type_, exclusive_task_type); + return true; + } } void NavigationURLLoaderImpl::LoaderHolder::BindReceiver( mojo::PendingReceiver<network::mojom::URLLoaderClient> pending_receiver, scoped_refptr<base::SequencedTaskRunner> task_runner) { + // TODO(https://crbug.com/434182226): Remove DUMP_WILL_BE_. + DUMP_WILL_BE_CHECK(!modified_headers_on_redirect_); + DUMP_WILL_BE_CHECK_EQ(state_, State::kLoadingViaLoader); + CheckState(); + response_loader_receiver_.reset(); response_loader_receiver_.Bind(std::move(pending_receiver), std::move(task_runner)); url_loader_.reset(); + + state_ = State::kLoadingViaReceiver; + CheckState(); } void NavigationURLLoaderImpl::LoaderHolder::SetLoader( std::unique_ptr<blink::ThrottlingURLLoader> url_loader) { + // TODO(https://crbug.com/434182226): Remove DUMP_WILL_BE_. + DUMP_WILL_BE_CHECK(!modified_headers_on_redirect_); + DUMP_WILL_BE_CHECK_EQ(state_, State::kNone); + CheckState(); + url_loader_ = std::move(url_loader); + + state_ = State::kLoadingViaLoader; + CheckState(); } network::mojom::URLLoaderClientEndpointsPtr NavigationURLLoaderImpl::LoaderHolder::Unbind() { + CheckState(); + if (url_loader_) { + // TODO(https://crbug.com/434182226): Turn this to `CHECK()`. + DUMP_WILL_BE_CHECK_EQ(state_, State::kLoadingViaLoader); + state_ = State::kUnbound; // Even after this point `url_loader_` should be alive and accessed via // `url_loader()`. // TODO(https://crbug.com/40251638): Clean up this behavior if needed. return url_loader_->Unbind(); } else { + // TODO(https://crbug.com/434182226): Turn this to `CHECK()`. + DUMP_WILL_BE_CHECK_EQ(state_, State::kLoadingViaReceiver); + state_ = State::kUnbound; return network::mojom::URLLoaderClientEndpoints::New( std::move(response_url_loader_), response_loader_receiver_.Unbind()); } } +void NavigationURLLoaderImpl::LoaderHolder::CheckState() const { + // TODO(https://crbug.com/434182226): Turn `DUMP_WILL_BE_CHECK()`s to + // `CHECK()`. + switch (state_) { + case State::kNone: + DUMP_WILL_BE_CHECK(!response_loader_receiver_.is_bound()); + DUMP_WILL_BE_CHECK(!url_loader_); + break; + case State::kLoadingViaLoader: + DUMP_WILL_BE_CHECK(!response_loader_receiver_.is_bound()); + DUMP_WILL_BE_CHECK(url_loader_); + break; + case State::kLoadingViaReceiver: + DUMP_WILL_BE_CHECK(response_loader_receiver_.is_bound()); + DUMP_WILL_BE_CHECK(!url_loader_); + break; + case State::kUnbound: + // `LoaderHolder` shouldn't be touched after `Unbind()`. + DUMP_WILL_BE_NOTREACHED(); + } +} + NavigationURLLoaderImpl::LoaderHolder::ModifiedHeadersOnRedirect:: ModifiedHeadersOnRedirect( std::vector<std::string> removed_headers, @@ -902,6 +1069,8 @@ std::vector<std::string> removed_headers, net::HttpRequestHeaders modified_headers, net::HttpRequestHeaders modified_cors_exempt_headers) { + // TODO(https://crbug.com/434182226): Remove DUMP_WILL_BE_. + DUMP_WILL_BE_CHECK(!modified_headers_on_redirect_); modified_headers_on_redirect_.emplace( std::move(removed_headers), std::move(modified_headers), std::move(modified_cors_exempt_headers)); @@ -915,7 +1084,6 @@ resource_request, modified_headers_on_redirect_->removed_headers_, modified_headers_on_redirect_->modified_headers_, modified_headers_on_redirect_->modified_cors_exempt_headers_); - modified_headers_on_redirect_.reset(); } Reset(); } @@ -927,6 +1095,7 @@ std::move(modified_headers_on_redirect_->removed_headers_), std::move(modified_headers_on_redirect_->modified_headers_), std::move(modified_headers_on_redirect_->modified_cors_exempt_headers_)); + modified_headers_on_redirect_.reset(); } bool NavigationURLLoaderImpl::LoaderHolder::receiver_is_bound_for_check() @@ -936,15 +1105,26 @@ void NavigationURLLoaderImpl::StartNonInterceptedRequest( ResponseHeadUpdateParams head_update_params) { + loader_holder_.OnExclusiveTaskCompleted( + LoaderHolder::ExclusiveTaskType::kInterceptor); + // If we already have the default `url_loader_` we must come here after a // redirect. No interceptors wanted to intercept the redirected request, // so let the loader just follow the redirect. if (loader_holder_.url_loader()) { DCHECK(!redirect_info_.new_url.is_empty()); + // TODO(https://crbug.com/434182226): Turn this to `CHECK()`. + DUMP_WILL_BE_CHECK_EQ(loader_holder_.state(), + LoaderHolder::State::kLoadingViaLoader); loader_holder_.FollowRedirect(); return; } + // The previous loader should be already reset at + // `NavigationURLLoaderImpl::Restart()` and we start a new loader below. + // TODO(https://crbug.com/434182226): Turn this to `CHECK()`. + DUMP_WILL_BE_CHECK_EQ(loader_holder_.state(), LoaderHolder::State::kNone); + head_update_params_ = std::move(head_update_params); scoped_refptr<network::SharedURLLoaderFactory> factory; if (network::IsURLHandledByNetworkService(resource_request_->url)) { @@ -1107,6 +1287,8 @@ "navigation", "NavigationURLLoaderImpl::CreateThrottlingLoaderAndStart", TRACE_ID_LOCAL(this), TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); + // TODO(https://crbug.com/434182226): Turn this to `CHECK()`. + DUMP_WILL_BE_CHECK_EQ(loader_holder_.state(), LoaderHolder::State::kNone); CHECK(!loader_holder_.url_loader()); std::vector<std::unique_ptr<blink::URLLoaderThrottle>> throttles = @@ -1180,6 +1362,8 @@ mojo::ScopedDataPipeConsumerHandle response_body, std::optional<mojo_base::BigBuffer> cached_metadata) { DCHECK(!cached_metadata); + // TODO(https://crbug.com/434182226): Remove DUMP_WILL_BE_. + DUMP_WILL_BE_CHECK(!loader_holder_.HasExclusiveTask()); LogQueueTimeHistogram("Navigation.QueueTime.OnReceiveResponse", resource_request_->is_outermost_main_frame); @@ -1324,12 +1508,15 @@ weak_factory_.GetWeakPtr(), std::move(url_loader_client_endpoints), std::move(response_body_), global_request_id_, is_download); - ParseHeaders(url_, std::move(head), std::move(on_receive_response)); + ParseHeaders(url_, std::move(head), std::move(on_receive_response), + /*clear_parsed_headers_for_testing=*/false); } void NavigationURLLoaderImpl::OnReceiveRedirect( const net::RedirectInfo& redirect_info, network::mojom::URLResponseHeadPtr head) { + // TODO(https://crbug.com/434182226): Remove DUMP_WILL_BE_. + DUMP_WILL_BE_CHECK(!loader_holder_.HasExclusiveTask()); LogQueueTimeHistogram("Navigation.QueueTime.OnReceiveRedirect", resource_request_->is_outermost_main_frame); net::Error error = net::OK; @@ -1350,12 +1537,18 @@ } if (error != net::OK) { if (loader_holder_.url_loader()) { + // TODO(https://crbug.com/434182226): Turn this to `CHECK()`. + DUMP_WILL_BE_CHECK_EQ(loader_holder_.state(), + LoaderHolder::State::kLoadingViaLoader); // Call CancelWithError instead of OnComplete so that if there is an // intercepting URLLoaderFactory (created through the embedder's // ContentBrowserClient::WillCreateURLLoaderFactory) it gets notified. loader_holder_.url_loader()->CancelWithError( error, std::string_view(base::NumberToString(error))); } else { + // TODO(https://crbug.com/434182226): Turn this to `CHECK()`. + DUMP_WILL_BE_CHECK_EQ(loader_holder_.state(), + LoaderHolder::State::kLoadingViaReceiver); // TODO(crbug.com/40118809): Make sure ResetWithReason() is called // on the original `url_loader_`. OnComplete(network::URLLoaderCompletionStatus(error)); @@ -1370,10 +1563,19 @@ GURL previous_url = url_; url_ = redirect_info.new_url; + loader_holder_.OnExclusiveTaskStarted( + LoaderHolder::ExclusiveTaskType::kRedirect); + auto on_receive_redirect = base::BindOnce(&NavigationURLLoaderImpl::NotifyRequestRedirected, weak_factory_.GetWeakPtr(), redirect_info); - ParseHeaders(previous_url, std::move(head), std::move(on_receive_redirect)); + const bool clear_parsed_headers_for_testing = + delegate_->ShouldClearParsedHeadersOnTestReceiveRedirect(); + if (clear_parsed_headers_for_testing) { + CHECK_IS_TEST(); + } + ParseHeaders(previous_url, std::move(head), std::move(on_receive_redirect), + clear_parsed_headers_for_testing); } void NavigationURLLoaderImpl::OnUploadProgress( @@ -1408,13 +1610,20 @@ // Note: Despite having received a response, the HTTP_NOT_MODIFIED(304) ones // are ignored using OnComplete(net::ERR_ABORTED). No interceptor must // be used in this case. - if (!received_response_) { + // + // We also skip interceptors and force the loading to fail when there are + // exclusive tasks, because we can't gracefully cancel the exclusive tasks and + // switch to the interceptor-induced redirects. + if (!received_response_ && !loader_holder_.HasExclusiveTask()) { auto response = network::mojom::URLResponseHead::New(); if (MaybeCreateLoaderForResponse(status, &response)) { return; } } + // Cancel all loading operations to avoid further URLLoaderClient calls. + loader_holder_.ResetForFailure(); + status_ = status; GetUIThreadTaskRunner({})->PostTask( FROM_HERE, base::BindOnce(&NavigationURLLoaderImpl::NotifyRequestFailed, @@ -1591,6 +1800,18 @@ mojo::PendingReceiver<network::mojom::URLLoaderClient> response_client_receiver; bool skip_other_interceptors = false; + // The `MaybeCreateLoaderForResponse()` call here seems to have been + // implicitly assuming the url_loader is non-null since before, because + // `SignedExchangeRequestHandler::MaybeCreateLoaderForResponse()` requires a + // non-null url_loader. This should hold because: + // - `MaybeCreateLoaderForResponse()` is called from the URLLoaderClient + // override methods, so the loading is ongoing. + // - `default_loader_used_` is true here, so the state can't be + // `kLoadingViaReceiver` and thus it should be `kLoadingViaLoader`. + // TODO(https://crbug.com/434182226): Turn this to `CHECK()`. + DUMP_WILL_BE_CHECK_EQ(loader_holder_.state(), + LoaderHolder::State::kLoadingViaLoader); + if (interceptor->MaybeCreateLoaderForResponse( status, *resource_request_, response, &response_body_, loader_holder_.response_url_loader(), &response_client_receiver, @@ -1664,7 +1885,8 @@ void NavigationURLLoaderImpl::ParseHeaders( const GURL& url, network::mojom::URLResponseHeadPtr head, - base::OnceCallback<void(network::mojom::URLResponseHeadPtr)> continuation) { + base::OnceCallback<void(network::mojom::URLResponseHeadPtr)> continuation, + bool clear_parsed_headers_for_testing) { // As an optimization, when we know the parsed headers will be empty, we can // skip the network process roundtrip. // TODO(arthursonzogni): If there are any performance issues, consider @@ -1683,6 +1905,11 @@ network::PopulateParsedHeaders(head->headers.get(), url); } + if (clear_parsed_headers_for_testing) { + CHECK_IS_TEST(); + head->parsed_headers.reset(); + } + // The main path: // -------------- // The ParsedHeaders are already provided. No more work needed. @@ -1967,6 +2194,13 @@ DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK(!redirect_info_.new_url.is_empty()); + if (loader_holder_.ShouldCancelExclusiveTask( + LoaderHolder::ExclusiveTaskType::kRedirect)) { + return; + } + loader_holder_.OnExclusiveTaskCompleted( + LoaderHolder::ExclusiveTaskType::kRedirect); + // Don't send Accept: application/signed-exchange for fallback redirects. // This is also applied to `resource_request_->headers` via // `net::RedirectUtil::UpdateHttpRequest()`. @@ -2065,6 +2299,10 @@ timeout_timer_.Stop(); } +void NavigationURLLoaderImpl::TriggerTimeoutForTesting() { + timeout_timer_.FireNow(); +} + void NavigationURLLoaderImpl::NotifyResponseStarted( network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints, mojo::ScopedDataPipeConsumerHandle response_body, @@ -2101,6 +2339,12 @@ net::RedirectInfo redirect_info, network::mojom::URLResponseHeadPtr response_head) { DCHECK_CURRENTLY_ON(BrowserThread::UI); + + if (loader_holder_.ShouldCancelExclusiveTask( + LoaderHolder::ExclusiveTaskType::kRedirect)) { + return; + } + delegate_->OnRequestRedirected( redirect_info, resource_request_->trusted_params->isolation_info
diff --git a/content/browser/loader/navigation_url_loader_impl.h b/content/browser/loader/navigation_url_loader_impl.h index a767363..d301a47ed 100644 --- a/content/browser/loader/navigation_url_loader_impl.h +++ b/content/browser/loader/navigation_url_loader_impl.h
@@ -122,6 +122,7 @@ bool SetNavigationTimeout(base::TimeDelta timeout) override; void CancelNavigationTimeout() override; + void TriggerTimeoutForTesting(); const network::ResourceRequest& GetResourceRequestForTesting() const; private: @@ -239,10 +240,14 @@ const NavigationRequestInfo& request_info, scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory); - void ParseHeaders(const GURL& url, - network::mojom::URLResponseHeadPtr head, - base::OnceCallback<void(network::mojom::URLResponseHeadPtr)> - continuation); + // When `clear_parsed_headers_for_testing` is true (which is only allowed in + // tests), `head->parsed_headers` is cleared to enforce and test the async + // `ParseHeaders()` path. https://crbug.com/434182226 + void ParseHeaders( + const GURL& url, + network::mojom::URLResponseHeadPtr head, + base::OnceCallback<void(network::mojom::URLResponseHeadPtr)> continuation, + bool clear_parsed_headers_for_testing); void NotifyResponseStarted( network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints, @@ -354,6 +359,41 @@ explicit LoaderHolder(network::mojom::URLLoaderClient* receiver); ~LoaderHolder(); + // Right now, `State` is used only for `DUMP_WILL_BE_CHECK()`ing and the + // other underlying members (e.g. `url_loader_`) should be used to check + // state-dependent conditions. + // TODO(https://crbug.com/434182226): Once the `DUMP_WILL_BE_CHECK()`s stick + // and are turned into `CHECK()`s, `State` must match with `url_loader_` + // etc. and thus should be used also for non-CHECK purposes. + enum class State { + // Neither of the loader or receiver is active right now. + // This can be a transient state when processing a redirect (after + // resetting the previous redirect leg and just before starting the next + // leg). + kNone, + + // The loading is ongoing and the `NavigationURLLoaderImpl`'s + // `URLLoaderClient` methods are called via `url_loader_` (the primary + // cases). + // The `URLLoaderClient` methods are called directly (synchronously) by + // `blink::ThrottlingURLLoader`. + kLoadingViaLoader, + + // The loading is ongoing and the `NavigationURLLoaderImpl`'s + // `URLLoaderClient` methods are called via + // `response_loader_receiver_` (for `MaybeCreateLoaderForResponse()`). + // The `URLLoaderClient` methods are called via mojo. + kLoadingViaReceiver, + + // All loading via `NavigationURLLoaderImpl` is done and further + // operation on `LoaderHolder` or calls to `NavigationURLLoaderImpl`'s + // `URLLoaderClient` methods shouldn't be made, except for some operations + // directly through `url_loader()`. + kUnbound, + }; + + State state() const { return state_; } + blink::ThrottlingURLLoader* url_loader() const { return url_loader_.get(); } mojo::PendingRemote<network::mojom::URLLoader>* response_url_loader() { return &response_url_loader_; @@ -361,24 +401,39 @@ // Cancel the current loading, if any. // Any associated pending operations should be cancelled. - // TODO(https://crbug.com/434182226): Still some known pending operations - // are not cancelled. Actually cancel them. + // Transitions to `State::kNone`. + // + // Note: The "exclusive tasks" (see `ExclusiveTaskState` below) can't be + // gracefully cancelled here and thus `Reset()` should be called only when + // there should always be no exclusive tasks. See also `ResetForFailure()`. void Reset(); + // When the caller wants to start a new loading operation while there can be + // existing exclusive tasks, the caller should check `HasExclusiveTask()`, + // and if there are exclusive tasks, call `ResetForFailure()` and fail the + // entire loading instead. + // + // This is similar to `Reset()`, but also instructs exclusive tasks to be + // cancelled. + void ResetForFailure(); + // For starting loading via `url_loader` (transitioning from `kNone` to // `kLoadingViaLoader`). THe caller should actually start the loading by // calling `url_loader->Start()`. + // Transitions to `State::kLoadingViaLoader`. void SetLoader(std::unique_ptr<blink::ThrottlingURLLoader> url_loader); // Switches to loading via `pending_receiver` (transitioning from // `kLoadingViaLoader` to `kLoadingViaReceiver`). The caller might already // call `url_loader()->Unbind()` etc. + // Transitions to `State::kLoadingViaReceiver`. void BindReceiver( mojo::PendingReceiver<network::mojom::URLLoaderClient> pending_receiver, scoped_refptr<base::SequencedTaskRunner> task_runner); // Unbind the endpoints from ``NavigationURLLoaderImpl`` to // `URLLoaderClientEndpointsPtr` (transitioning to `kUnbound`). + // Transitions to `State::kUnbound`. [[nodiscard]] network::mojom::URLLoaderClientEndpointsPtr Unbind(); // Redirect handling: the expected sequence is: @@ -410,11 +465,40 @@ // `URLLoader::ResetForFollowRedirect()` if needed. void ResetForFollowRedirect(network::ResourceRequest& resource_request); + // See the `ExclusiveTaskState` comment below. + // TODO(https://crbug.com/434182226): Add more exclusive tasks handing. + enum ExclusiveTaskType { + // From `OnReceiveRedirect()` until `FollowRedirect()`. + // This contains two possible async tasks: + // - Waiting for `network.mojom.NetworkService::ParseHeaders()` and + // - Waiting for `NavigationURLLoaderDelegate`: from + // `OnRequestRedirected()` until + // `NavigationURLLoaderImpl::FollowRedirect()` is called. + kRedirect, + + // Waiting for `NavigationLoaderInterceptor::MaybeCreateLoader()`. + // From `NavigationURLLoaderImpl::Restart()` + // Until `NavigationURLLoaderImpl::StartNonInterceptedRequest()` or + // `NavigationURLLoaderImpl::StartInterceptedRequest()`. + kInterceptor, + }; + void OnExclusiveTaskStarted(ExclusiveTaskType exclusive_task_type); + void OnExclusiveTaskCompleted(ExclusiveTaskType exclusive_task_type); + bool HasExclusiveTask() const; + // Should be called only during an exclusive task. + bool ShouldCancelExclusiveTask(ExclusiveTaskType exclusive_task_type) const; + bool receiver_is_bound_for_check() const; private: + void ResetInternal(); + void CheckState() const; + + State state_ = State::kNone; + // `NavigationURLLoaderImpl`'s `URLLoaderClient` methods are called either // via `url_loader_` or `response_loader_receiver_`. + // See also the comment at `State` above for details. std::unique_ptr<blink::ThrottlingURLLoader> url_loader_; mojo::Receiver<network::mojom::URLLoaderClient> response_loader_receiver_; @@ -442,6 +526,46 @@ net::HttpRequestHeaders modified_cors_exempt_headers_; }; std::optional<ModifiedHeadersOnRedirect> modified_headers_on_redirect_; + + // `NavigationURLLoaderImpl` can be waiting for a certain (possibly async) + // "exclusive task" and can't start a new request nor receive + // URLLoaderClient method calls until the task completes. For example, + // `NavigationURLLoaderImpl`, `NavigationLoaderInterceptor` and + // `NavigationRequest` are going through checks before sending an initial or + // redirected request and in the middle of updating the request and other + // states. + // + // Not all arbitrary async operations are considered exclusive tasks here. + // For example, waiting for URLLoaderClient calls from `url_loader_` or + // `response_loader_receiver_` aren't considered exclusive tasks, because + // e.g. we can cancel the `url_loader_`, issue a synthetic redirect response + // as if it would be received from `url_loader_` and continue on the + // synthetic redirect. + // + // Original design doc: + // https://docs.google.com/document/d/1xCq9l9mYc3WE1adspyaX7FnPArxKDyz8BcRGTlpQIu8/edit?usp=sharing + enum ExclusiveTaskState { + kNoExclusiveTask, + + // There is an exclusive task, and therefore: + // - No URLLoaderClient methods should be called (except for unexpected + // `OnComplete()` or error events like timeout). + // - No actions that could initiate a new request/redirect etc. are + // allowed. When attempting such actions, `NavigationURLLoaderImpl` + // should call `ResetForFailure()` and make the navigation fail + // immediately. + // + // There should be at most one exclusive task at a time. + kHasExclusiveTask, + + // After `ResetForFailure()` is called, navigation fails and existing + // exclusive tasks should be cancelled. + kCancelExclusiveTask, + }; + + ExclusiveTaskState exclusive_task_state_ = + ExclusiveTaskState::kNoExclusiveTask; + std::optional<ExclusiveTaskType> current_exclusive_task_type_; }; LoaderHolder loader_holder_{this};
diff --git a/content/browser/loader/navigation_url_loader_impl_unittest.cc b/content/browser/loader/navigation_url_loader_impl_unittest.cc index 1ecb592..40da4ee 100644 --- a/content/browser/loader/navigation_url_loader_impl_unittest.cc +++ b/content/browser/loader/navigation_url_loader_impl_unittest.cc
@@ -126,9 +126,14 @@ blink::NavigationDownloadPolicy(), bool is_main_frame = true, bool upgrade_if_insecure = false, - bool is_ad_tagged = false) { + bool is_ad_tagged = false, + std::vector<std::unique_ptr<NavigationLoaderInterceptor>> interceptors = + {}) { // NavigationURLLoader assumes that the corresponding FrameTreeNode has an // associated NavigationRequest. + // NOTE: This also creates and starts another `NavigationURLLoaderImpl` + // (`NavigationRequest::loader_`) but it's not the `NavigationURLLoaderImpl` + // being tested (=the return value of this method). pending_navigation_ = NavigationSimulator::CreateBrowserInitiated( GURL("https://example.com"), web_contents_.get()); pending_navigation_->Start(); @@ -195,7 +200,6 @@ false /* shared_storage_writable */, is_ad_tagged /* is_ad_tagged */, false /* force_no_https_upgrade */)); - std::vector<std::unique_ptr<NavigationLoaderInterceptor>> interceptors; return std::make_unique<NavigationURLLoaderImpl>( browser_context_.get(), browser_context_->GetDefaultStoragePartition(), @@ -579,6 +583,746 @@ true); } +namespace { + +// A `NavigationLoaderInterceptor` to test operations during an interceptor is +// running (between `MaybeCreateLoader()` call and its callback invocation, i.e. +// between `WaitUntilMaybeCreateLoader()` and `Unblock()`). +class TestAsyncInterceptor final : public NavigationLoaderInterceptor { + public: + TestAsyncInterceptor() { ResetRunLoop(); } + ~TestAsyncInterceptor() override = default; + + // Waits until the start of `MaybeCreateLoader()`. + void WaitUntilMaybeCreateLoader() { run_loop_->Run(); } + + // Finishes `MaybeCreateLoader()` without intercepting the request. + void Unblock() { + // Allow `WaitUntilMaybeCreateLoader()` for the next redirect request. + ResetRunLoop(); + std::move(loader_callback_).Run(std::nullopt); + } + + private: + void MaybeCreateLoader( + const network::ResourceRequest& tentative_resource_request, + BrowserContext* browser_context, + LoaderCallback loader_callback, + FallbackCallback fallback_callback) override { + run_loop_->Quit(); + loader_callback_ = std::move(loader_callback); + } + + bool MaybeCreateLoaderForResponse( + const network::URLLoaderCompletionStatus& status, + const network::ResourceRequest& request, + network::mojom::URLResponseHeadPtr* response_head, + mojo::ScopedDataPipeConsumerHandle* response_body, + mojo::PendingRemote<network::mojom::URLLoader>* loader, + mojo::PendingReceiver<network::mojom::URLLoaderClient>* client_receiver, + blink::ThrottlingURLLoader* url_loader, + bool* skip_other_interceptors) override { + return false; + } + + void ResetRunLoop() { run_loop_ = std::make_unique<base::RunLoop>(); } + + std::unique_ptr<base::RunLoop> run_loop_; + LoaderCallback loader_callback_; +}; + +// A `NavigationLoaderInterceptor` to test `MaybeCreateLoaderForResponse()` +// triggering redirects. +class TestResponseInterceptor final : public NavigationLoaderInterceptor { + public: + explicit TestResponseInterceptor(const GURL& redirect_url) + : redirect_url_(redirect_url) {} + ~TestResponseInterceptor() override = default; + + int response_count() const { return response_count_; } + void set_should_redirect(bool should_redirect) { + should_redirect_ = should_redirect; + } + + private: + void MaybeCreateLoader( + const network::ResourceRequest& tentative_resource_request, + BrowserContext* browser_context, + LoaderCallback callback, + FallbackCallback fallback_callback) override { + std::move(callback).Run(std::nullopt); + } + + bool MaybeCreateLoaderForResponse( + const network::URLLoaderCompletionStatus& status, + const network::ResourceRequest& request, + network::mojom::URLResponseHeadPtr* response_head, + mojo::ScopedDataPipeConsumerHandle* response_body, + mojo::PendingRemote<network::mojom::URLLoader>* loader, + mojo::PendingReceiver<network::mojom::URLLoaderClient>* client_receiver, + blink::ThrottlingURLLoader* url_loader, + bool* skip_other_interceptors) override { + if (!should_redirect_) { + return false; + } + + ++response_count_; + + mojo::Remote<network::mojom::URLLoaderClient> client; + *client_receiver = client.BindNewPipeAndPassReceiver(); + + // Create an artificial redirect back to the fallback URL. + auto new_response_head = network::mojom::URLResponseHead::New(); + net::RedirectInfo redirect_info = net::RedirectInfo::ComputeRedirectInfo( + request.method, request.url, request.site_for_cookies, + request.update_first_party_url_on_redirect + ? net::RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT + : net::RedirectInfo::FirstPartyURLPolicy::NEVER_CHANGE_URL, + request.referrer_policy, request.referrer.spec(), + request.request_initiator, net::HTTP_TEMPORARY_REDIRECT, redirect_url_, + /*referrer_policy_header=*/std::nullopt, + /*insecure_scheme_was_upgraded=*/false); + client->OnReceiveRedirect(redirect_info, std::move(new_response_head)); + return true; + } + + const GURL redirect_url_; + int response_count_ = 0; + bool should_redirect_ = true; +}; + +// This sets the timeout timer but doesn't expect the timer is fired +// automatically. If needed, the timer should be fired explicitly e.g. via +// `TriggerTimeoutForTesting()`. +void SetLargeNavigationTimeout(NavigationURLLoaderImpl& loader) { + loader.SetNavigationTimeout(base::Seconds(1000)); +} + +} // namespace + +// Timeout case (failure) while waiting for the response from URLLoader. +TEST_F(NavigationURLLoaderImplTest, TimeoutDuringURLLoader) { + ASSERT_TRUE(http_test_server_.Start()); + const GURL final_url = http_test_server_.GetURL("/echo"); + TestNavigationURLLoaderDelegate delegate; + auto loader = CreateTestLoader(final_url, std::string(), "GET", &delegate); + loader->Start(); + SetLargeNavigationTimeout(*loader); + + // Simulate timeout during the async operation. + // `delegate` shouldn't be notified synchronously. + static_cast<NavigationURLLoaderImpl*>(loader.get()) + ->TriggerTimeoutForTesting(); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 0); + EXPECT_EQ(delegate.on_request_handled_counter(), 0); + + // Wait for the failure due to the timeout is notified. + // + // Note [*1]: + // In the expected non-test scenario, the operations below should occur + // between `TriggerTimeoutForTesting()` and `WaitForRequestFailed()` (which + // should be quite rare though), because `NavigationRequest` destroys + // `NavigationURLLoaderImpl` upon `OnRequestFailed()`. + // In tests, `WaitForRequestFailed()` is called before the operations below to + // avoid race conditions, but we can consider as if the `OnRequestFailed()` + // waited here is received after the operations below, because + // `OnRequestFailed()` can delay as a pending posted task without interfering + // with other parts of `NavigationURLLoaderImpl`. + delegate.WaitForRequestFailed(); + EXPECT_EQ(net::ERR_TIMED_OUT, delegate.net_error()); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 0); + EXPECT_EQ(delegate.on_request_handled_counter(), 1); + + // Check that no further loading should occur. + task_environment_->RunUntilIdle(); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 0); + EXPECT_EQ(delegate.on_request_handled_counter(), 1); +} + +// Timeout + MaybeCreateLoaderForResponse() + redirect case (failure) while +// waiting for the response from URLLoader. +TEST_F(NavigationURLLoaderImplTest, RedirectDuringURLLoader) { + ASSERT_TRUE(http_test_server_.Start()); + const GURL final_url = http_test_server_.GetURL("/echo"); + const GURL interceptor_url = http_test_server_.GetURL("/foo"); + TestNavigationURLLoaderDelegate delegate; + auto response_interceptor = + std::make_unique<TestResponseInterceptor>(interceptor_url); + auto* response_interceptor_ptr = response_interceptor.get(); + std::vector<std::unique_ptr<NavigationLoaderInterceptor>> interceptors; + interceptors.push_back(std::move(response_interceptor)); + auto loader = + CreateTestLoader(final_url, std::string(), "GET", &delegate, + blink::NavigationDownloadPolicy(), + /*is_main_frame=*/true, + /*upgrade_if_insecure=*/false, + /*is_ad_tagged=*/false, std::move(interceptors)); + loader->Start(); + SetLargeNavigationTimeout(*loader); + + // Simulate timeout during the async operation. + // `delegate` shouldn't be notified synchronously. + static_cast<NavigationURLLoaderImpl*>(loader.get()) + ->TriggerTimeoutForTesting(); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 0); + EXPECT_EQ(delegate.on_request_handled_counter(), 0); + + // Interceptor's MaybeCreateLoaderForResponse() is processed synchronously, + // which triggers `OnReceiveRedirect()` asynchronously. + ASSERT_EQ(response_interceptor_ptr->response_count(), 1); + + // Note [*2]: + // Avoid `MaybeCreateLoaderForResponse()` intercepting the response below, to + // simplify the expectation a bit. The main thing to test is possibly + // triggering `MaybeCreateLoaderForResponse()` from `OnComplete()` (i.e. from + // `TriggerTimeoutForTesting()` above), not the response possibly received + // (unexpectedly) below after the timeout. + response_interceptor_ptr->set_should_redirect(false); + + // Wait for the redirect due to the timeout + interceptor is notified. + delegate.WaitForRequestRedirected(); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 1); + EXPECT_EQ(delegate.on_request_handled_counter(), 0); + + // Check that no further loading should occur. + task_environment_->RunUntilIdle(); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 1); + EXPECT_EQ(delegate.on_request_handled_counter(), 0); +} + +// Timeout case (failure) with async `OnRequestRedirected()` -> +// `FollowRedirect()`. +TEST_F(NavigationURLLoaderImplTest, TimeoutDuringFollowRedirect) { + ASSERT_TRUE(http_test_server_.Start()); + const GURL redirect_url = http_test_server_.GetURL("/redirect301-to-echo"); + TestNavigationURLLoaderDelegate delegate; + auto loader = CreateTestLoader(redirect_url, std::string(), "GET", &delegate); + loader->Start(); + SetLargeNavigationTimeout(*loader); + + // Wait for the async operation starts. + delegate.WaitForRequestRedirected(); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 1); + EXPECT_EQ(delegate.on_request_handled_counter(), 0); + + // Simulate timeout during the async operation. + // `delegate` shouldn't be notified synchronously. + static_cast<NavigationURLLoaderImpl*>(loader.get()) + ->TriggerTimeoutForTesting(); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 1); + EXPECT_EQ(delegate.on_request_handled_counter(), 0); + + // Wait for the failure due to the timeout is notified (See Note [*1] above). + delegate.WaitForRequestFailed(); + EXPECT_EQ(net::ERR_TIMED_OUT, delegate.net_error()); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 1); + EXPECT_EQ(delegate.on_request_handled_counter(), 1); + + // Finish the async operation. + loader->FollowRedirect({}, {}, {}); + + // Check that no further loading should occur. + task_environment_->RunUntilIdle(); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 1); + EXPECT_EQ(delegate.on_request_handled_counter(), 1); +} + +// Timeout + MaybeCreateLoaderForResponse() + redirect case (failure) with async +// `OnRequestRedirected()` -> `FollowRedirect()`. +TEST_F(NavigationURLLoaderImplTest, RedirectDuringFollowRedirect) { + ASSERT_TRUE(http_test_server_.Start()); + const GURL redirect_url = http_test_server_.GetURL("/redirect301-to-echo"); + const GURL interceptor_url = http_test_server_.GetURL("/foo"); + TestNavigationURLLoaderDelegate delegate; + auto response_interceptor = + std::make_unique<TestResponseInterceptor>(interceptor_url); + auto* response_interceptor_ptr = response_interceptor.get(); + std::vector<std::unique_ptr<NavigationLoaderInterceptor>> interceptors; + interceptors.push_back(std::move(response_interceptor)); + auto loader = + CreateTestLoader(redirect_url, std::string(), "GET", &delegate, + blink::NavigationDownloadPolicy(), + /*is_main_frame=*/true, + /*upgrade_if_insecure=*/false, + /*is_ad_tagged=*/false, std::move(interceptors)); + loader->Start(); + SetLargeNavigationTimeout(*loader); + + // Wait for the async operation starts. + delegate.WaitForRequestRedirected(); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 1); + EXPECT_EQ(delegate.on_request_handled_counter(), 0); + + // Simulate timeout during the async operation. + // `delegate` shouldn't be notified synchronously. + static_cast<NavigationURLLoaderImpl*>(loader.get()) + ->TriggerTimeoutForTesting(); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 1); + EXPECT_EQ(delegate.on_request_handled_counter(), 0); + + // `MaybeCreateLoaderForResponse()` shouldn't be called during an exclusive + // task. + ASSERT_EQ(response_interceptor_ptr->response_count(), 0); + + // See Note [*2] above. + response_interceptor_ptr->set_should_redirect(false); + + // Wait for the failure due to the timeout is notified (See Note [*1] above). + delegate.WaitForRequestFailed(); + EXPECT_EQ(net::ERR_TIMED_OUT, delegate.net_error()); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 1); + EXPECT_EQ(delegate.on_request_handled_counter(), 1); + + // Finish the async operation. + loader->FollowRedirect({}, {}, {}); + + // Check that no further loading should occur. + task_environment_->RunUntilIdle(); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 1); + EXPECT_EQ(delegate.on_request_handled_counter(), 1); +} + +// Successful case with async `ParseHeaders()`. +TEST_F(NavigationURLLoaderImplTest, ForceAsyncParseHeaders) { + ASSERT_TRUE(http_test_server_.Start()); + const GURL redirect_url = http_test_server_.GetURL("/redirect301-to-echo"); + const GURL final_url = http_test_server_.GetURL("/echo"); + TestNavigationURLLoaderDelegate delegate; + auto loader = CreateTestLoader(redirect_url, std::string(), "GET", &delegate); + loader->Start(); + SetLargeNavigationTimeout(*loader); + + // Force the async ParseHeaders() path for redirects. + delegate.set_clear_parsed_headers_on_redirect(true); + + // Wait for the async operation starts. + delegate.WaitForOnReceiveRedirect(); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 0); + EXPECT_EQ(delegate.on_request_handled_counter(), 0); + + // Finish the async operation (`ParseHeaders()` automatically completes while + // running the task queue) and wait for redirect/response received after the + // async `ParseHeaders()`. + delegate.WaitForRequestRedirected(); + EXPECT_EQ(delegate.redirect_info().new_url, final_url); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 1); + EXPECT_EQ(delegate.on_request_handled_counter(), 0); + + loader->FollowRedirect({}, {}, {}); + delegate.WaitForResponseStarted(); + EXPECT_EQ(net::OK, delegate.net_error()); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 1); + EXPECT_EQ(delegate.on_request_handled_counter(), 1); +} + +// Timouet case (failure) with async `ParseHeaders()`. +TEST_F(NavigationURLLoaderImplTest, TimeoutDuringParseHeaders) { + ASSERT_TRUE(http_test_server_.Start()); + const GURL redirect_url = http_test_server_.GetURL("/redirect301-to-echo"); + TestNavigationURLLoaderDelegate delegate; + auto loader = CreateTestLoader(redirect_url, std::string(), "GET", &delegate); + loader->Start(); + SetLargeNavigationTimeout(*loader); + + // Force the async ParseHeaders() path for redirects. + delegate.set_clear_parsed_headers_on_redirect(true); + + // Wait for the async operation starts. + delegate.WaitForOnReceiveRedirect(); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 0); + EXPECT_EQ(delegate.on_request_handled_counter(), 0); + + // Simulate timeout during the async operation. + static_cast<NavigationURLLoaderImpl*>(loader.get()) + ->TriggerTimeoutForTesting(); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 0); + EXPECT_EQ(delegate.on_request_handled_counter(), 0); + + // Wait for the failure due to the timeout is notified (See Note [*1] above). + delegate.WaitForRequestFailed(); + EXPECT_EQ(net::ERR_TIMED_OUT, delegate.net_error()); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 0); + EXPECT_EQ(delegate.on_request_handled_counter(), 1); + + // Finish the async operation (`ParseHeaders()` automatically completes while + // running the task queue). No further loading should occur. + task_environment_->RunUntilIdle(); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 0); + EXPECT_EQ(delegate.on_request_handled_counter(), 1); +} + +// Timeout + MaybeCreateLoaderForResponse() + redirect case (failure) with async +// `ParseHeaders()`. +TEST_F(NavigationURLLoaderImplTest, RedirectDuringParseHeaders) { + ASSERT_TRUE(http_test_server_.Start()); + const GURL redirect_url = http_test_server_.GetURL("/redirect301-to-echo"); + const GURL interceptor_url = http_test_server_.GetURL("/foo"); + TestNavigationURLLoaderDelegate delegate; + auto response_interceptor = + std::make_unique<TestResponseInterceptor>(interceptor_url); + auto* response_interceptor_ptr = response_interceptor.get(); + std::vector<std::unique_ptr<NavigationLoaderInterceptor>> interceptors; + interceptors.push_back(std::move(response_interceptor)); + auto loader = + CreateTestLoader(redirect_url, std::string(), "GET", &delegate, + blink::NavigationDownloadPolicy(), + /*is_main_frame=*/true, + /*upgrade_if_insecure=*/false, + /*is_ad_tagged=*/false, std::move(interceptors)); + loader->Start(); + SetLargeNavigationTimeout(*loader); + + // Force the async ParseHeaders() path for redirects. + delegate.set_clear_parsed_headers_on_redirect(true); + + // Wait for the async operation starts. + delegate.WaitForOnReceiveRedirect(); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 0); + EXPECT_EQ(delegate.on_request_handled_counter(), 0); + EXPECT_EQ(response_interceptor_ptr->response_count(), 0); + + // Simulate timeout during the async operation. + // `delegate` shouldn't be notified synchronously. + static_cast<NavigationURLLoaderImpl*>(loader.get()) + ->TriggerTimeoutForTesting(); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 0); + EXPECT_EQ(delegate.on_request_handled_counter(), 0); + + // `MaybeCreateLoaderForResponse()` shouldn't be called during an exclusive + // task. + ASSERT_EQ(response_interceptor_ptr->response_count(), 0); + + // Wait for the failure due to the timeout is notified (See Note [*1] above). + delegate.WaitForRequestFailed(); + EXPECT_EQ(net::ERR_TIMED_OUT, delegate.net_error()); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 0); + EXPECT_EQ(delegate.on_request_handled_counter(), 1); + + // Finish the async operation (`ParseHeaders()` automatically completes while + // running the task queue). No further loading should occur. + task_environment_->RunUntilIdle(); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 0); + EXPECT_EQ(delegate.on_request_handled_counter(), 1); +} + +// Successful case with an async interceptor (initial request). +TEST_F(NavigationURLLoaderImplTest, ForceAsyncInterceptor) { + ASSERT_TRUE(http_test_server_.Start()); + const GURL final_url = http_test_server_.GetURL("/echo"); + TestNavigationURLLoaderDelegate delegate; + auto async_interceptor = std::make_unique<TestAsyncInterceptor>(); + auto* async_interceptor_ptr = async_interceptor.get(); + std::vector<std::unique_ptr<NavigationLoaderInterceptor>> interceptors; + interceptors.push_back(std::move(async_interceptor)); + auto loader = + CreateTestLoader(final_url, std::string(), "GET", &delegate, + blink::NavigationDownloadPolicy(), + /*is_main_frame=*/true, + /*upgrade_if_insecure=*/false, + /*is_ad_tagged=*/false, std::move(interceptors)); + loader->Start(); + SetLargeNavigationTimeout(*loader); + + // Wait for the async operation starts. + async_interceptor_ptr->WaitUntilMaybeCreateLoader(); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 0); + EXPECT_EQ(delegate.on_request_handled_counter(), 0); + + // Finish the async operation. + async_interceptor_ptr->Unblock(); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 0); + EXPECT_EQ(delegate.on_request_handled_counter(), 0); + + // Wait for response received after the interceptor. + delegate.WaitForResponseStarted(); + EXPECT_EQ(net::OK, delegate.net_error()); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 0); + EXPECT_EQ(delegate.on_request_handled_counter(), 1); +} + +// Timeout case (failure) with an async interceptor (initial request). +TEST_F(NavigationURLLoaderImplTest, TimeoutDuringAsyncInterceptor) { + ASSERT_TRUE(http_test_server_.Start()); + const GURL final_url = http_test_server_.GetURL("/echo"); + TestNavigationURLLoaderDelegate delegate; + auto async_interceptor = std::make_unique<TestAsyncInterceptor>(); + auto* async_interceptor_ptr = async_interceptor.get(); + std::vector<std::unique_ptr<NavigationLoaderInterceptor>> interceptors; + interceptors.push_back(std::move(async_interceptor)); + auto loader = + CreateTestLoader(final_url, std::string(), "GET", &delegate, + blink::NavigationDownloadPolicy(), + /*is_main_frame=*/true, + /*upgrade_if_insecure=*/false, + /*is_ad_tagged=*/false, std::move(interceptors)); + loader->Start(); + SetLargeNavigationTimeout(*loader); + + // Wait for the async operation starts. + async_interceptor_ptr->WaitUntilMaybeCreateLoader(); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 0); + EXPECT_EQ(delegate.on_request_handled_counter(), 0); + + // Simulate timeout during the async operation. + // `delegate` shouldn't be notified synchronously. + static_cast<NavigationURLLoaderImpl*>(loader.get()) + ->TriggerTimeoutForTesting(); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 0); + EXPECT_EQ(delegate.on_request_handled_counter(), 0); + + // Wait for the failure due to the timeout is notified (See Note [*1] above). + delegate.WaitForRequestFailed(); + EXPECT_EQ(net::ERR_TIMED_OUT, delegate.net_error()); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 0); + EXPECT_EQ(delegate.on_request_handled_counter(), 1); + + // Finish the async operation. + async_interceptor_ptr->Unblock(); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 0); + EXPECT_EQ(delegate.on_request_handled_counter(), 1); + + // Check that no further loading should occur. + task_environment_->RunUntilIdle(); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 0); + EXPECT_EQ(delegate.on_request_handled_counter(), 1); +} + +// Timeout + MaybeCreateLoaderForResponse() + redirect case (failure) with an +// async interceptor (initial request). +TEST_F(NavigationURLLoaderImplTest, RedirectDuringAsyncInterceptor) { + ASSERT_TRUE(http_test_server_.Start()); + const GURL final_url = http_test_server_.GetURL("/echo"); + const GURL interceptor_url = http_test_server_.GetURL("/foo"); + TestNavigationURLLoaderDelegate delegate; + auto response_interceptor = + std::make_unique<TestResponseInterceptor>(interceptor_url); + auto* response_interceptor_ptr = response_interceptor.get(); + auto async_interceptor = std::make_unique<TestAsyncInterceptor>(); + auto* async_interceptor_ptr = async_interceptor.get(); + std::vector<std::unique_ptr<NavigationLoaderInterceptor>> interceptors; + interceptors.push_back(std::move(response_interceptor)); + interceptors.push_back(std::move(async_interceptor)); + auto loader = + CreateTestLoader(final_url, std::string(), "GET", &delegate, + blink::NavigationDownloadPolicy(), + /*is_main_frame=*/true, + /*upgrade_if_insecure=*/false, + /*is_ad_tagged=*/false, std::move(interceptors)); + loader->Start(); + SetLargeNavigationTimeout(*loader); + + // Wait for the async operation starts. + async_interceptor_ptr->WaitUntilMaybeCreateLoader(); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 0); + EXPECT_EQ(delegate.on_request_handled_counter(), 0); + + // Simulate timeout during the async operation. + // `delegate` shouldn't be notified synchronously. + static_cast<NavigationURLLoaderImpl*>(loader.get()) + ->TriggerTimeoutForTesting(); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 0); + EXPECT_EQ(delegate.on_request_handled_counter(), 0); + + // Interceptor's MaybeCreateLoaderForResponse() isn't processed here, because + // `default_loader_used_` is false. Therefore falling back to the same + // scenario as the `TimeoutDuringAsyncInterceptor` test above. + // Anyway, `MaybeCreateLoaderForResponse()` shouldn't be called during an + // exclusive task. + ASSERT_EQ(response_interceptor_ptr->response_count(), 0); + + // See Note [*2] above. + response_interceptor_ptr->set_should_redirect(false); + + // Wait for the failure due to the timeout is notified (See Note [*1] above). + delegate.WaitForRequestFailed(); + EXPECT_EQ(net::ERR_TIMED_OUT, delegate.net_error()); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 0); + EXPECT_EQ(delegate.on_request_handled_counter(), 1); + + // Finish the async operation. + async_interceptor_ptr->Unblock(); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 0); + EXPECT_EQ(delegate.on_request_handled_counter(), 1); + + // Check that no further loading should occur. + task_environment_->RunUntilIdle(); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 0); + EXPECT_EQ(delegate.on_request_handled_counter(), 1); +} + +// Successful case with an async interceptor (redirected request). +TEST_F(NavigationURLLoaderImplTest, ForceAsyncInterceptorForRedirect) { + ASSERT_TRUE(http_test_server_.Start()); + const GURL redirect_url = http_test_server_.GetURL("/redirect301-to-echo"); + const GURL final_url = http_test_server_.GetURL("/echo"); + TestNavigationURLLoaderDelegate delegate; + auto async_interceptor = std::make_unique<TestAsyncInterceptor>(); + auto* async_interceptor_ptr = async_interceptor.get(); + std::vector<std::unique_ptr<NavigationLoaderInterceptor>> interceptors; + interceptors.push_back(std::move(async_interceptor)); + auto loader = + CreateTestLoader(redirect_url, std::string(), "GET", &delegate, + blink::NavigationDownloadPolicy(), + /*is_main_frame=*/true, + /*upgrade_if_insecure=*/false, + /*is_ad_tagged=*/false, std::move(interceptors)); + loader->Start(); + SetLargeNavigationTimeout(*loader); + + // Process the initial request. + async_interceptor_ptr->WaitUntilMaybeCreateLoader(); + async_interceptor_ptr->Unblock(); + delegate.WaitForRequestRedirected(); + loader->FollowRedirect({}, {}, {}); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 1); + EXPECT_EQ(delegate.on_request_handled_counter(), 0); + + // Wait for the async operation starts. + // This processes the redirected request until the async interceptor starts. + async_interceptor_ptr->WaitUntilMaybeCreateLoader(); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 1); + EXPECT_EQ(delegate.on_request_handled_counter(), 0); + + // Finish the async operation. + async_interceptor_ptr->Unblock(); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 1); + EXPECT_EQ(delegate.on_request_handled_counter(), 0); + + // Wait for response received after the interceptor. + delegate.WaitForResponseStarted(); + EXPECT_EQ(net::OK, delegate.net_error()); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 1); + EXPECT_EQ(delegate.on_request_handled_counter(), 1); +} + +// Timeout case (failure) with an async interceptor (redirect request). +TEST_F(NavigationURLLoaderImplTest, TimeoutDuringAsyncInterceptorForRedirect) { + ASSERT_TRUE(http_test_server_.Start()); + const GURL redirect_url = http_test_server_.GetURL("/redirect301-to-echo"); + const GURL final_url = http_test_server_.GetURL("/echo"); + TestNavigationURLLoaderDelegate delegate; + auto async_interceptor = std::make_unique<TestAsyncInterceptor>(); + auto* async_interceptor_ptr = async_interceptor.get(); + std::vector<std::unique_ptr<NavigationLoaderInterceptor>> interceptors; + interceptors.push_back(std::move(async_interceptor)); + auto loader = + CreateTestLoader(redirect_url, std::string(), "GET", &delegate, + blink::NavigationDownloadPolicy(), + /*is_main_frame=*/true, + /*upgrade_if_insecure=*/false, + /*is_ad_tagged=*/false, std::move(interceptors)); + loader->Start(); + SetLargeNavigationTimeout(*loader); + + // Process the initial request. + async_interceptor_ptr->WaitUntilMaybeCreateLoader(); + async_interceptor_ptr->Unblock(); + delegate.WaitForRequestRedirected(); + loader->FollowRedirect({}, {}, {}); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 1); + EXPECT_EQ(delegate.on_request_handled_counter(), 0); + + // Wait for the async operation starts. + // This processes the redirected request until the async interceptor starts. + async_interceptor_ptr->WaitUntilMaybeCreateLoader(); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 1); + EXPECT_EQ(delegate.on_request_handled_counter(), 0); + + // Simulate timeout during the async operation. + // `delegate` shouldn't be notified synchronously. + static_cast<NavigationURLLoaderImpl*>(loader.get()) + ->TriggerTimeoutForTesting(); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 1); + EXPECT_EQ(delegate.on_request_handled_counter(), 0); + + // Wait for the failure due to the timeout is notified (See Note [*1] above). + delegate.WaitForRequestFailed(); + EXPECT_EQ(net::ERR_TIMED_OUT, delegate.net_error()); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 1); + EXPECT_EQ(delegate.on_request_handled_counter(), 1); + + // Finish the async operation. + async_interceptor_ptr->Unblock(); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 1); + EXPECT_EQ(delegate.on_request_handled_counter(), 1); + + // Check that no further loading should occur. + task_environment_->RunUntilIdle(); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 1); + EXPECT_EQ(delegate.on_request_handled_counter(), 1); +} + +// Timeout + MaybeCreateLoaderForResponse() + redirect case (failure) with an +// async interceptor (redirect request). +TEST_F(NavigationURLLoaderImplTest, RedirectDuringAsyncInterceptorForRedirect) { + ASSERT_TRUE(http_test_server_.Start()); + const GURL redirect_url = http_test_server_.GetURL("/redirect301-to-echo"); + const GURL final_url = http_test_server_.GetURL("/echo"); + const GURL interceptor_url = http_test_server_.GetURL("/foo"); + TestNavigationURLLoaderDelegate delegate; + auto response_interceptor = + std::make_unique<TestResponseInterceptor>(interceptor_url); + auto* response_interceptor_ptr = response_interceptor.get(); + auto async_interceptor = std::make_unique<TestAsyncInterceptor>(); + auto* async_interceptor_ptr = async_interceptor.get(); + std::vector<std::unique_ptr<NavigationLoaderInterceptor>> interceptors; + interceptors.push_back(std::move(response_interceptor)); + interceptors.push_back(std::move(async_interceptor)); + auto loader = + CreateTestLoader(redirect_url, std::string(), "GET", &delegate, + blink::NavigationDownloadPolicy(), + /*is_main_frame=*/true, + /*upgrade_if_insecure=*/false, + /*is_ad_tagged=*/false, std::move(interceptors)); + loader->Start(); + SetLargeNavigationTimeout(*loader); + + // Process the initial request. + async_interceptor_ptr->WaitUntilMaybeCreateLoader(); + async_interceptor_ptr->Unblock(); + delegate.WaitForRequestRedirected(); + loader->FollowRedirect({}, {}, {}); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 1); + EXPECT_EQ(delegate.on_request_handled_counter(), 0); + + // Wait for the async operation starts. + // This processes the redirected request until the async interceptor starts. + async_interceptor_ptr->WaitUntilMaybeCreateLoader(); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 1); + EXPECT_EQ(delegate.on_request_handled_counter(), 0); + EXPECT_EQ(response_interceptor_ptr->response_count(), 0); + + // Simulate timeout during the async operation. + // `delegate` shouldn't be notified synchronously. + static_cast<NavigationURLLoaderImpl*>(loader.get()) + ->TriggerTimeoutForTesting(); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 1); + EXPECT_EQ(delegate.on_request_handled_counter(), 0); + + // `MaybeCreateLoaderForResponse()` shouldn't be called during an exclusive + // task. + ASSERT_EQ(response_interceptor_ptr->response_count(), 0); + + // See Note [*2] above. + response_interceptor_ptr->set_should_redirect(false); + + // Wait for the failure due to the timeout is notified (See Note [*1] above). + delegate.WaitForRequestFailed(); + EXPECT_EQ(net::ERR_TIMED_OUT, delegate.net_error()); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 1); + EXPECT_EQ(delegate.on_request_handled_counter(), 1); + + // Finish the async operation. + async_interceptor_ptr->Unblock(); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 1); + EXPECT_EQ(delegate.on_request_handled_counter(), 1); + + // Check that no further loading should occur. + task_environment_->RunUntilIdle(); + EXPECT_EQ(delegate.on_redirect_handled_counter(), 1); + EXPECT_EQ(delegate.on_request_handled_counter(), 1); +} + TEST_F(NavigationURLLoaderImplTest, RedirectModifiedHeaders) { ASSERT_TRUE(http_test_server_.Start());
diff --git a/content/browser/media/key_system_support_impl.cc b/content/browser/media/key_system_support_impl.cc index c0017ef2..5e84d2c 100644 --- a/content/browser/media/key_system_support_impl.cc +++ b/content/browser/media/key_system_support_impl.cc
@@ -113,19 +113,28 @@ // PROTECTED_MEDIA_IDENTIFIER. #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN) || \ BUILDFLAG(IS_FUCHSIA) - render_frame_host() - .GetBrowserContext() - ->GetPermissionController() - ->RequestPermissionFromCurrentDocument( - &render_frame_host(), - PermissionRequestDescription( + // Don't call RequestPermissionFromCurrentDocument API that requests + // permission right away since `is_protected_identifier_allowed_` flag is used + // only when deciding whether we allow or disallow hardware secure capability + // check. Instead whether or not to call GetPermissionStatusForCurrentDocument + // API will be decided in KeySystemConfigSelector::SelectConfigInternal(). + // TODO(crbug.com/435220187): Add a unit test that would fail if it uses + // RequestPermissionFromCurrentDocument instead of + // GetPermissionForCurrentDocument. + auto status = + render_frame_host() + .GetBrowserContext() + ->GetPermissionController() + ->GetPermissionStatusForCurrentDocument( content::PermissionDescriptorUtil:: CreatePermissionDescriptorForPermissionType( blink::PermissionType::PROTECTED_MEDIA_IDENTIFIER), - render_frame_host().HasTransientUserActivation()), - base::BindOnce(&KeySystemSupportImpl:: - OnProtectedMediaIdentifierPermissionInitialized, - weak_ptr_factory_.GetWeakPtr())); + &render_frame_host()); + is_protected_identifier_allowed_ = + status == blink::mojom::PermissionStatus::GRANTED; + are_permissions_initialized_ = true; + SetUpPermissionListeners(); + ObserveKeySystemCapabilities(); #else are_permissions_initialized_ = true; SetUpPermissionListeners(); @@ -165,18 +174,6 @@ preference_watcher_receiver_.BindNewPipeAndPassRemote()); } -void KeySystemSupportImpl::OnProtectedMediaIdentifierPermissionInitialized( - blink::mojom::PermissionStatus status) { - DCHECK(!are_permissions_initialized_); - - are_permissions_initialized_ = true; - is_protected_identifier_allowed_ = - status == blink::mojom::PermissionStatus::GRANTED; - - SetUpPermissionListeners(); - ObserveKeySystemCapabilities(); -} - void KeySystemSupportImpl::OnProtectedMediaIdentifierPermissionUpdated( blink::mojom::PermissionStatus status) { const bool is_protected_identifier_allowed =
diff --git a/content/browser/media/key_system_support_impl.h b/content/browser/media/key_system_support_impl.h index 50eddb5..9aa81bf 100644 --- a/content/browser/media/key_system_support_impl.h +++ b/content/browser/media/key_system_support_impl.h
@@ -68,10 +68,6 @@ // Sets up permission listeners for updates. void SetUpPermissionListeners(); - // Initializes `is_protected_identifier_allowed_` with `status`. - void OnProtectedMediaIdentifierPermissionInitialized( - blink::mojom::PermissionStatus status); - // Updates `is_protected_identifier_allowed_` with `status`. void OnProtectedMediaIdentifierPermissionUpdated( blink::mojom::PermissionStatus status);
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc index 2c74d7e..7265a78 100644 --- a/content/browser/renderer_host/navigation_request.cc +++ b/content/browser/renderer_host/navigation_request.cc
@@ -3438,6 +3438,12 @@ return previous_page_ukm_source_id_; } +bool NavigationRequest::ShouldClearParsedHeadersOnTestReceiveRedirect() { + // This method is only for testing and thus must do nothing and return `false` + // here. + return false; +} + void NavigationRequest::OnRequestRedirected( const net::RedirectInfo& redirect_info, const net::NetworkAnonymizationKey& network_anonymization_key,
diff --git a/content/browser/renderer_host/navigation_request.h b/content/browser/renderer_host/navigation_request.h index 162f166..45cb8b4 100644 --- a/content/browser/renderer_host/navigation_request.h +++ b/content/browser/renderer_host/navigation_request.h
@@ -1801,6 +1801,7 @@ std::optional<NavigationEarlyHintsManagerParams> CreateNavigationEarlyHintsManagerParams( const network::mojom::EarlyHints& early_hints) override; + bool ShouldClearParsedHeadersOnTestReceiveRedirect() override; // Selecting a `RenderFrameHost` to commit a navigation may occasionally fail. // When this happens, the navigation will bind a closure to continue the
diff --git a/content/browser/webid/digital_credentials/digital_identity_request_impl.cc b/content/browser/webid/digital_credentials/digital_identity_request_impl.cc index 4e18e90..faa34e2 100644 --- a/content/browser/webid/digital_credentials/digital_identity_request_impl.cc +++ b/content/browser/webid/digital_credentials/digital_identity_request_impl.cc
@@ -40,6 +40,7 @@ using base::Value; constexpr char kPreviewProtocol[] = "preview"; +constexpr char kOpenid4vpProtocolPrefix[] = "openid4vp"; constexpr char kMdlDocumentType[] = "org.iso.18013.5.1.mDL"; @@ -63,12 +64,6 @@ constexpr char kDigitalIdentityLowRiskDialogParamValue[] = "low_risk"; constexpr char kDigitalIdentityHighRiskDialogParamValue[] = "high_risk"; -base::flat_set<std::string_view> GetOpenid4vpProtocolIdentifiers() { - base::flat_set<std::string_view> ids = {"openid4vp", "openid4vp1.0", - "openid4vp-v1-unsigned"}; - return ids; -} - // Returns entry if `dict` has a list with a single dict element for key // `list_key`. const Value::Dict* FindSingleElementListEntry(const Value::Dict& dict, @@ -279,7 +274,7 @@ return false; } - if (GetOpenid4vpProtocolIdentifiers().contains(protocol)) { + if (protocol.starts_with(kOpenid4vpProtocolPrefix)) { return CanRequestCredentialBypassInterstitialForOpenid4vpProtocol(request); } return protocol == kPreviewProtocol &&
diff --git a/content/browser/webid/federated_auth_request_impl.cc b/content/browser/webid/federated_auth_request_impl.cc index 3b74d5f..aa5b141aa 100644 --- a/content/browser/webid/federated_auth_request_impl.cc +++ b/content/browser/webid/federated_auth_request_impl.cc
@@ -1154,6 +1154,14 @@ return; } } else { + if (rp_mode_ == RpMode::kPassive) { + request_dialog_controller_->ShouldShowAccountsPassiveDialog( + base::BindOnce(&FederatedAuthRequestImpl:: + OnShouldShowAccountsPassiveDialogResult, + weak_ptr_factory_.GetWeakPtr(), + did_succeed_for_at_least_one_idp)); + return; + } if (!request_dialog_controller_->ShowAccountsDialog( CreateRpData(), idp_data_for_display_, accounts_, rp_mode_, new_accounts_, @@ -1169,6 +1177,36 @@ return; } } + AfterAccountsDialogShown(did_succeed_for_at_least_one_idp); +} + +void FederatedAuthRequestImpl::OnShouldShowAccountsPassiveDialogResult( + bool did_succeed_for_at_least_one_idp, + bool should_show) { + if (!should_show) { + OnDialogDismissed( + IdentityRequestDialogController::DismissReason::kSuppressed); + return; + } + if (!request_dialog_controller_->ShowAccountsDialog( + CreateRpData(), idp_data_for_display_, accounts_, rp_mode_, + new_accounts_, + base::BindOnce(&FederatedAuthRequestImpl::OnAccountSelected, + weak_ptr_factory_.GetWeakPtr()), + base::BindRepeating(&FederatedAuthRequestImpl::LoginToIdP, + weak_ptr_factory_.GetWeakPtr(), + /*can_append_hints=*/false), + base::BindOnce(&FederatedAuthRequestImpl::OnDialogDismissed, + weak_ptr_factory_.GetWeakPtr()), + base::BindOnce(&FederatedAuthRequestImpl::OnAccountsDisplayed, + weak_ptr_factory_.GetWeakPtr()))) { + return; + } + AfterAccountsDialogShown(did_succeed_for_at_least_one_idp); +} + +void FederatedAuthRequestImpl::AfterAccountsDialogShown( + bool did_succeed_for_at_least_one_idp) { did_show_ui_ = true; devtools_instrumentation::DidShowFedCmDialog(render_frame_host()); @@ -1254,6 +1292,7 @@ weak_ptr_factory_.GetWeakPtr()))) { return; } + // TODO(crbug.com/435216589): Should we call AfterAccountsDialogShown here? } void FederatedAuthRequestImpl::OnAccountsDisplayed() {
diff --git a/content/browser/webid/federated_auth_request_impl.h b/content/browser/webid/federated_auth_request_impl.h index 53860d2f..7cf0f6f 100644 --- a/content/browser/webid/federated_auth_request_impl.h +++ b/content/browser/webid/federated_auth_request_impl.h
@@ -314,6 +314,14 @@ std::vector<blink::mojom::IdentityProviderRequestOptionsPtr>& providers); void MaybeShowAccountsDialog(); + void OnShouldShowAccountsPassiveDialogResult( + bool did_succeed_for_at_least_one_idp, + bool should_show); + // To be called immediately after ShowAccountsDialog for correct devtools + // integration and metrics reporting. + // `did_succeed_for_at_least_one_idp` needs to be passed as a parameter + // because `fetch_data_` has been cleared at this point. + void AfterAccountsDialogShown(bool did_succeed_for_at_least_one_idp); void ShowModalDialog(DialogType dialog_type, const GURL& idp_config_url, const GURL& url_to_show);
diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h index 17cf4554..fbba4e5 100644 --- a/content/public/browser/web_contents.h +++ b/content/public/browser/web_contents.h
@@ -55,7 +55,7 @@ #include "third_party/blink/public/mojom/picture_in_picture_window_options/picture_in_picture_window_options.mojom.h" #include "third_party/perfetto/include/perfetto/tracing/traced_value_forward.h" #include "third_party/skia/include/core/SkColor.h" -#include "ui/accessibility/ax_enums.mojom-shared.h" +#include "ui/accessibility/ax_enums.mojom-forward.h" #include "ui/accessibility/ax_mode.h" #include "ui/accessibility/platform/inspect/ax_api_type.h" #include "ui/color/color_provider_key.h"
diff --git a/content/public/browser/webid/identity_request_dialog_controller.cc b/content/public/browser/webid/identity_request_dialog_controller.cc index f11d0d8..018f4a9 100644 --- a/content/public/browser/webid/identity_request_dialog_controller.cc +++ b/content/public/browser/webid/identity_request_dialog_controller.cc
@@ -65,6 +65,11 @@ is_interception_enabled_ = enabled; } +void IdentityRequestDialogController::ShouldShowAccountsPassiveDialog( + ShouldShowAccountsPassiveDialogCallback cb) { + std::move(cb).Run(true); +} + bool IdentityRequestDialogController::ShowAccountsDialog( content::RelyingPartyData rp_data, const std::vector<scoped_refptr<content::IdentityProviderData>>& idp_list,
diff --git a/content/public/browser/webid/identity_request_dialog_controller.h b/content/public/browser/webid/identity_request_dialog_controller.h index f147f7b..213c026 100644 --- a/content/public/browser/webid/identity_request_dialog_controller.h +++ b/content/public/browser/webid/identity_request_dialog_controller.h
@@ -164,6 +164,8 @@ // GENERATED_JAVA_CLASS_NAME_OVERRIDE: IdentityRequestDialogLinkType enum class LinkType { PRIVACY_POLICY, TERMS_OF_SERVICE }; + using ShouldShowAccountsPassiveDialogCallback = + base::OnceCallback<void(bool)>; using AccountSelectionCallback = base::OnceCallback<void(const GURL& idp_config_url, const std::string& /*account_id*/, @@ -198,6 +200,12 @@ // When this is true, the dialog should not be immediately auto-accepted. virtual void SetIsInterceptionEnabled(bool enabled); + // Computes whether to show the dialog. Will be called before + // ShowAccountsDialog, but only in passive mode. If false is passed to the + // callback, the request will be cancelled. + virtual void ShouldShowAccountsPassiveDialog( + ShouldShowAccountsPassiveDialogCallback cb); + // Shows and accounts selections for the given IDP. The `on_selected` callback // is called with the selected account id or empty string otherwise. // `new_accounts` are the accounts that were just logged in, which should
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc index 537f378..ab3dee1 100644 --- a/content/public/common/content_features.cc +++ b/content/public/common/content_features.cc
@@ -1129,7 +1129,7 @@ // SiteInstance. BASE_FEATURE(kDefaultSiteInstanceGroups, "DefaultSiteInstanceGroups", - base::FEATURE_DISABLED_BY_DEFAULT); + base::FEATURE_ENABLED_BY_DEFAULT); // Controls whether to isolate sites of documents that specify an eligible // Cross-Origin-Opener-Policy header. Note that this is only intended to be @@ -1595,11 +1595,7 @@ BASE_FEATURE(kPwaNavigationCapturing, "PwaNavigationCapturing", -#if BUILDFLAG(IS_CHROMEOS) - base::FEATURE_DISABLED_BY_DEFAULT); -#else base::FEATURE_ENABLED_BY_DEFAULT); -#endif const base::FeatureParam<CapturingState>::Option kNavigationCapturingParams[] = {{CapturingState::kDefaultOn, "on_by_default"},
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc index 4aba989f..7e54042 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc
@@ -5208,7 +5208,8 @@ RendererNavigationMetricsManager::Instance().ProcessNavigationCommit( navigation_state->commit_params().navigation_metrics_token, navigation_state->common_params().url, - navigation_state->common_params().actual_navigation_start); + navigation_state->common_params().actual_navigation_start, + navigation_state->commit_params().commit_sent, IsMainFrame()); // Add any new code above the ProcessNavigationCommit call. }
diff --git a/content/renderer/renderer_navigation_metrics_manager.cc b/content/renderer/renderer_navigation_metrics_manager.cc index b6df2bb..e452e4e 100644 --- a/content/renderer/renderer_navigation_metrics_manager.cc +++ b/content/renderer/renderer_navigation_metrics_manager.cc
@@ -29,6 +29,57 @@ "EnableRendererNavigationTimeline", base::FEATURE_ENABLED_BY_DEFAULT); +// Used to record how ready a renderer process is for an incoming +// CommitNavigation IPC. Please keep in sync with "RendererProcessReadiness" in +// tools/metrics/histograms/metadata/navigation/enums.xml. These values are +// persisted to logs. Entries should not be renumbered and numeric values should +// never be reused. +// +// LINT.IfChange(RendererProcessReadiness) +enum class RendererProcessReadiness { + // The commit IPC was sent before the process was ready. This implies that + // we should've started the renderer process earlier. + kProcessNotReady = 0, + // Renderer was ready, but the preparatory work of creating the + // view/proxy/frame objects wasn't complete before the commit IPC was sent. + // This implies that the speculative RenderFrameHost should've been created + // earlier. + kViewProxyFrameNotReady = 1, + // Renderer was ready to process the commit IPC right away. + kReadyToProcessCommitIPC = 2, + + kMaxValue = kReadyToProcessCommitIPC +}; +// LINT.ThenChange(//tools/metrics/histograms/metadata/navigation/enums.xml:RendererProcessReadiness) + +void RecordProcessReadiness(bool is_main_frame, + RendererProcessReadiness readiness) { + base::UmaHistogramEnumeration("Navigation.Renderer.ProcessReadiness", + readiness); + if (is_main_frame) { + base::UmaHistogramEnumeration( + "Navigation.Renderer.ProcessReadiness.MainFrameOnly", readiness); + } + + std::string readiness_description; + switch (readiness) { + case RendererProcessReadiness::kProcessNotReady: + readiness_description = "Process not ready"; + break; + case RendererProcessReadiness::kViewProxyFrameNotReady: + readiness_description = "View/proxy/frame not ready"; + break; + case RendererProcessReadiness::kReadyToProcessCommitIPC: + readiness_description = "Ready to process commit IPC"; + break; + } + // Also emit an instant trace event to expose process readiness in traces. + TRACE_EVENT_INSTANT( + "navigation", + "RendererNavigationMetricsManager::RecordTraceEventsAndMetrics", + "ProcessReadiness", readiness_description); +} + } // namespace RendererNavigationMetricsManager& RendererNavigationMetricsManager::Instance() { @@ -289,6 +340,33 @@ timeline.create_frame_event->end); } + // Record a metric to measure how ready the renderer process is to process a + // navigation commit IPC. + + if (timeline.commit_sent < process_ready_time) { + // The commit IPC was sent before the process was ready. This implies that + // we should've started the renderer process earlier. + RecordProcessReadiness(timeline.is_main_frame, + RendererProcessReadiness::kProcessNotReady); + } else if (timeline.create_frame_event && + timeline.commit_sent < timeline.create_frame_event->end) { + // Renderer was ready, but the prerequisite work of creating the + // view/proxy/frame objects wasn't completed before the commit IPC was sent. + // This implies that the speculative RenderFrameHost should've been created + // earlier. Note that CreateFrame's end time is used here because it is the + // last view/proxy/frame creation IPC to be processed; not having + // CreateFrame for this navigation implies there also was no view or proxy + // creation, and the navigation commit wasn't blocked waiting for any of + // them. + RecordProcessReadiness(timeline.is_main_frame, + RendererProcessReadiness::kViewProxyFrameNotReady); + } else { + // Renderer was ready to process the commit IPC without waiting for process + // startup or prerequisite frame/proxy/view creation IPCs. + RecordProcessReadiness(timeline.is_main_frame, + RendererProcessReadiness::kReadyToProcessCommitIPC); + } + log_trace_event_and_uma("CommitToDidCommit", timeline.commit_start, timeline.commit_end); } @@ -296,7 +374,9 @@ void RendererNavigationMetricsManager::ProcessNavigationCommit( const base::UnguessableToken& navigation_metrics_token, const GURL& url, - const base::TimeTicks& navigation_start_time) { + const base::TimeTicks& navigation_start_time, + const base::TimeTicks& commit_sent_time, + bool is_main_frame) { if (!base::FeatureList::IsEnabled(kEnableRendererNavigationTimeline)) { return; } @@ -311,7 +391,9 @@ auto& timeline = it->second; timeline.navigation_start = navigation_start_time; + timeline.commit_sent = commit_sent_time; timeline.commit_end = base::TimeTicks().Now(); + timeline.is_main_frame = is_main_frame; RecordTraceEventsAndMetrics(timeline, url); // Remove the timeline from the map and cancel the cleanup timer.
diff --git a/content/renderer/renderer_navigation_metrics_manager.h b/content/renderer/renderer_navigation_metrics_manager.h index 34d733b9e..519fda3 100644 --- a/content/renderer/renderer_navigation_metrics_manager.h +++ b/content/renderer/renderer_navigation_metrics_manager.h
@@ -87,6 +87,10 @@ // an initial blank document. std::optional<TimelineEvent> create_frame_event; + // The time at which the CommitNavigation IPC was sent to this renderer + // process from the browser process. + base::TimeTicks commit_sent; + // The time at which this navigation started processing the CommitNavigation // IPC. base::TimeTicks commit_start; @@ -107,6 +111,12 @@ // the first navigation begins, to give a sense of how often a process // is ready to go when it's needed for a navigation. bool is_first_navigation_in_this_process; + + // Whether this navigation was in a main frame, as defined by + // RenderFrameImpl::IsMainFrame(). Useful for recording metrics for main + // frames only. Note that this is not limited to outermost or primary main + // frames. + bool is_main_frame; }; // The following methods are called to add timestamps for processing the @@ -163,11 +173,15 @@ // This is a signal for this class to generate trace events and metrics using // all the timestamps collected so far for it. `navigation_start_time` // identifies the time at which this navigation was started, possibly in - // another process. + // another process. `commit_sent_time` is the time at which the + // CommitNavigation IPC was sent by the browser process to this renderer + // process. void ProcessNavigationCommit( const base::UnguessableToken& navigation_metrics_token, const GURL& url, - const base::TimeTicks& navigation_start_time); + const base::TimeTicks& navigation_start_time, + const base::TimeTicks& commit_sent_time, + bool is_main_frame); private: ~RendererNavigationMetricsManager();
diff --git a/content/test/test_navigation_url_loader_delegate.cc b/content/test/test_navigation_url_loader_delegate.cc index 7e11e26..6e0e6a39 100644 --- a/content/test/test_navigation_url_loader_delegate.cc +++ b/content/test/test_navigation_url_loader_delegate.cc
@@ -15,11 +15,16 @@ namespace content { -TestNavigationURLLoaderDelegate::TestNavigationURLLoaderDelegate() - : net_error_(0), on_request_handled_counter_(0) {} +TestNavigationURLLoaderDelegate::TestNavigationURLLoaderDelegate() = default; TestNavigationURLLoaderDelegate::~TestNavigationURLLoaderDelegate() {} +void TestNavigationURLLoaderDelegate::WaitForOnReceiveRedirect() { + on_receive_redirect_ = std::make_unique<base::RunLoop>(); + on_receive_redirect_->Run(); + on_receive_redirect_.reset(); +} + void TestNavigationURLLoaderDelegate::WaitForRequestRedirected() { request_redirected_ = std::make_unique<base::RunLoop>(); request_redirected_->Run(); @@ -38,14 +43,24 @@ request_failed_.reset(); } +bool TestNavigationURLLoaderDelegate:: + ShouldClearParsedHeadersOnTestReceiveRedirect() { + if (on_receive_redirect_) { + on_receive_redirect_->Quit(); + } + return clear_parsed_headers_on_redirect_; +} + void TestNavigationURLLoaderDelegate::OnRequestRedirected( const net::RedirectInfo& redirect_info, const net::NetworkAnonymizationKey& network_anonymization_key, network::mojom::URLResponseHeadPtr response_head) { redirect_info_ = redirect_info; redirect_response_ = std::move(response_head); - ASSERT_TRUE(request_redirected_); - request_redirected_->Quit(); + ++on_redirect_handled_counter_; + if (request_redirected_) { + request_redirected_->Quit(); + } } void TestNavigationURLLoaderDelegate::OnResponseStarted(
diff --git a/content/test/test_navigation_url_loader_delegate.h b/content/test/test_navigation_url_loader_delegate.h index f57d10b..b730243 100644 --- a/content/test/test_navigation_url_loader_delegate.h +++ b/content/test/test_navigation_url_loader_delegate.h
@@ -35,15 +35,24 @@ int net_error() const { return net_error_; } const net::SSLInfo& ssl_info() const { return ssl_info_; } int on_request_handled_counter() const { return on_request_handled_counter_; } + int on_redirect_handled_counter() const { + return on_redirect_handled_counter_; + } // Waits for various navigation events. // Note: if the event already happened, the functions will hang. // TODO(clamy): Make the functions not hang if they are called after the // event happened. + void WaitForOnReceiveRedirect(); void WaitForRequestRedirected(); void WaitForResponseStarted(); void WaitForRequestFailed(); + void set_clear_parsed_headers_on_redirect( + bool clear_parsed_headers_on_redirect) { + clear_parsed_headers_on_redirect_ = clear_parsed_headers_on_redirect; + } + // NavigationURLLoaderDelegate implementation. void OnRequestRedirected( const net::RedirectInfo& redirect_info, @@ -63,16 +72,23 @@ std::optional<NavigationEarlyHintsManagerParams> CreateNavigationEarlyHintsManagerParams( const network::mojom::EarlyHints& early_hints) override; + bool ShouldClearParsedHeadersOnTestReceiveRedirect() override; private: net::RedirectInfo redirect_info_; network::mojom::URLResponseHeadPtr redirect_response_; network::mojom::URLResponseHeadPtr response_head_; mojo::ScopedDataPipeConsumerHandle response_body_; - int net_error_; + int net_error_ = 0; net::SSLInfo ssl_info_; - int on_request_handled_counter_; + int on_request_handled_counter_ = 0; + int on_redirect_handled_counter_ = 0; + // See `NavigationURLLoaderImpl::ParseHeaders()` and + // `OnReceiveRedirect()`. + bool clear_parsed_headers_on_redirect_ = false; + + std::unique_ptr<base::RunLoop> on_receive_redirect_; std::unique_ptr<base::RunLoop> request_redirected_; std::unique_ptr<base::RunLoop> response_started_; std::unique_ptr<base::RunLoop> request_failed_;
diff --git a/device/fido/enclave/enclave_protocol_utils.cc b/device/fido/enclave/enclave_protocol_utils.cc index 51883e1..f607cf3 100644 --- a/device/fido/enclave/enclave_protocol_utils.cc +++ b/device/fido/enclave/enclave_protocol_utils.cc
@@ -232,6 +232,91 @@ return ret; } +// Redacts `path` from `cbor` using the semantics described below for +// `RedactCbor`. Mutates `cbor` in place. +void RedactPath(cbor::Value* cbor, base::span<const char*> path) { + if (cbor->is_array()) { + // Mutate all the elements in the array. + cbor::Value::ArrayValue& array = + const_cast<cbor::Value::ArrayValue&>(cbor->GetArray()); + for (cbor::Value& value : array) { + RedactPath(&value, path); + } + return; + } + if (!cbor->is_map()) { + // Only maps and arrays are supported. + return; + } + cbor::Value::MapValue& map = + const_cast<cbor::Value::MapValue&>(cbor->GetMap()); + const char* field = path.take_first_elem(); + const auto it = map.find(cbor::Value(field)); + if (it == map.end()) { + // Could not find some part of the path, bail out. + return; + } + if (path.empty()) { + // Found the leaf, replace the map value regardless of its type. + it->second = cbor::Value("[redacted]"); + return; + } + RedactPath(&it->second, path); +} + +// Redacts `paths_to_redact` from `cbor` by finding the corresponding keys and +// replacing them by the cbor string "redacted". Nested paths should correspond +// to nested maps under the same key name. The redaction is applied to all array +// elements for a matching key. +// If a path is not found, a clone of `cbor` is returned. +// +// Example: +// +// Given a `cbor` value... +// { +// characters: [ +// { +// name: "Reimu", +// occupation: ["Shrine maiden"] +// }, +// { +// name: "Marisa", +// occupation: ["Witch", "Troublemaker"] +// } +// ] +// } +// +// ...and a `paths_to_redact` value... +// +// [ +// ["characters", "occupation"], +// ["characters", "date-of-birth"], +// ] +// +// ...the returned cbor will be: +// +// { +// characters: [ +// { +// name: "Reimu", +// occupation: "redacted" +// }, +// { +// name: "Marisa", +// occupation: "redacted" +// } +// ] +// } +cbor::Value RedactCbor( + const cbor::Value& cbor, + base::span<const std::vector<const char*>> paths_to_redact) { + cbor::Value response = cbor.Clone(); + for (std::vector<const char*> field_to_redact : paths_to_redact) { + RedactPath(&response, field_to_redact); + } + return response; +} + } // namespace ErrorResponse::ErrorResponse(std::string error) @@ -667,4 +752,17 @@ std::move(complete_callback))); } +cbor::Value RedactEnclaveRequest(const cbor::Value& cbor) { + const std::array redacted_fields = {std::vector{"secret"}}; + return RedactCbor(cbor, redacted_fields); +} + +cbor::Value RedactEnclaveResponse(const cbor::Value& cbor) { + const std::array redacted_fields = { + std::vector{"ok", "ok", "largeBlob"}, + std::vector{"ok", "ok", "prf"}, + }; + return RedactCbor(cbor, redacted_fields); +} + } // namespace device::enclave
diff --git a/device/fido/enclave/enclave_protocol_utils.h b/device/fido/enclave/enclave_protocol_utils.h index 41772175..e435f04 100644 --- a/device/fido/enclave/enclave_protocol_utils.h +++ b/device/fido/enclave/enclave_protocol_utils.h
@@ -125,6 +125,16 @@ base::OnceCallback<void(std::optional<std::vector<uint8_t>>)> complete_callback); +// Returns a copy of the enclave `request` with its sensitive information (such +// as end-to-end encryption secrets) removed. +cbor::Value COMPONENT_EXPORT(DEVICE_FIDO) + RedactEnclaveRequest(const cbor::Value& request); + +// Returns a copy of the enclave `response` with its sensitive information (such +// as prf outputs) removed. +cbor::Value COMPONENT_EXPORT(DEVICE_FIDO) + RedactEnclaveResponse(const cbor::Value& response); + } // namespace enclave } // namespace device
diff --git a/device/fido/enclave/enclave_protocol_utils_unittest.cc b/device/fido/enclave/enclave_protocol_utils_unittest.cc index cb7e04c..bf22f49 100644 --- a/device/fido/enclave/enclave_protocol_utils_unittest.cc +++ b/device/fido/enclave/enclave_protocol_utils_unittest.cc
@@ -19,9 +19,11 @@ #include "base/values.h" #include "components/cbor/reader.h" #include "components/cbor/values.h" +#include "components/cbor/writer.h" #include "components/device_event_log/device_event_log.h" #include "components/sync/protocol/webauthn_credential_specifics.pb.h" #include "device/fido/ctap_make_credential_request.h" +#include "device/fido/enclave/constants.h" #include "device/fido/fido_parsing_utils.h" #include "device/fido/fido_transport_protocol.h" #include "device/fido/json_request.h" @@ -94,6 +96,18 @@ constexpr char kMakeCredentialHexResponse[] = "81A1626F6BA3677075625F6B657944050607086776657273696F6E0169656E637279707465" "644401020304"; + +// An example response with the top-level "ok" key, dummy large blob and PRF +// values. +constexpr char kCompleteGetAssertionHexResponse[] = + "A1626F6B81A1626F6BA3637072661904D268726573706F6E7365A3697369676E6174757265" + "A2646461746184185318691867186E6474797065664275666665726A7573657248616E646C" + "65A2646461746182186118626474797065664275666665727161757468656E74696361746F" + "7244617461A2646461746198251118941822188D18A818FD18BD18EE18FD1826181B18D718" + "B61859185C18FD187018A50D187018C61840187B18CF01183D18E9186D184E18FB1718DE01" + "000000183B647479706566427566666572696C61726765426C6F62A264726561641904D264" + "73697A6501"; + constexpr int32_t kWrappedSecretVersion = 952; struct BadResponseTestCase { @@ -247,8 +261,11 @@ std::vector<uint8_t> wrapped_secret() { return wrapped_secret_; } + std::vector<uint8_t> secret() { return secret_; } + private: const std::vector<uint8_t> wrapped_secret_ = {1, 2, 3, 4, 5}; + const std::vector<uint8_t> secret_ = {6, 7, 8, 9, 0}; std::vector<uint8_t> device_id_; std::vector<uint8_t> user_id_; std::vector<uint8_t> encrypted_passkey_; @@ -647,5 +664,54 @@ testing::ElementsAre(1, 2, 3)); } +TEST_F(EnclaveProtocolUtilsTest, RedactEnclaveRequest) { + auto entity = PasskeyEntity(); + entity.set_rp_id(kRpId); + std::optional<base::Value> parsed_json = + base::JSONReader::Read(kGetAssertionRequestJson); + EXPECT_TRUE(parsed_json); + auto json_request = + base::MakeRefCounted<JSONRequest>(std::move(*parsed_json)); + cbor::Value request_cbor = BuildGetAssertionCommand( + std::move(entity), json_request, kClientDataJson, + /*claimed_pin=*/nullptr, /*wrapped_secret=*/std::nullopt, secret()); + cbor::Value redacted = RedactEnclaveRequest(request_cbor); + ASSERT_TRUE(redacted.is_map()); + const auto& redacted_map = redacted.GetMap(); + const auto& redacted_value = + redacted_map.find(cbor::Value(kRequestSecretKey))->second; + EXPECT_EQ(redacted_value.GetString(), "[redacted]"); +} + +TEST_F(EnclaveProtocolUtilsTest, RedactErroneousEnclaveRequest) { + cbor::Value request = cbor::Value("not a valid request"); + EXPECT_EQ(cbor::Writer::Write(RedactEnclaveRequest(request)), + cbor::Writer::Write(request)); +} + +TEST_F(EnclaveProtocolUtilsTest, RedactEnclaveResponse) { + std::vector<uint8_t> response_serialized; + CHECK(base::HexStringToBytes(kCompleteGetAssertionHexResponse, + &response_serialized)); + cbor::Value response_cbor = cbor::Reader::Read(response_serialized).value(); + + const cbor::Value redacted = RedactEnclaveResponse(response_cbor); + const cbor::Value::MapValue& redacted_outer_map = + redacted.GetMap().find(cbor::Value("ok"))->second.GetArray()[0].GetMap(); + const cbor::Value::MapValue& redacted_map = + redacted_outer_map.find(cbor::Value("ok"))->second.GetMap(); + const auto& large_blob_value = + redacted_map.find(cbor::Value("largeBlob"))->second; + EXPECT_EQ(large_blob_value.GetString(), "[redacted]"); + const auto& prf_value = redacted_map.find(cbor::Value("prf"))->second; + EXPECT_EQ(prf_value.GetString(), "[redacted]"); +} + +TEST_F(EnclaveProtocolUtilsTest, RedactErroneousEnclaveResponse) { + cbor::Value response = cbor::Value("not a valid response"); + EXPECT_EQ(cbor::Writer::Write(RedactEnclaveResponse(response)), + cbor::Writer::Write(response)); +} + } // namespace enclave } // namespace device
diff --git a/device/fido/enclave/transact.cc b/device/fido/enclave/transact.cc index 874a04f..f64a4440 100644 --- a/device/fido/enclave/transact.cc +++ b/device/fido/enclave/transact.cc
@@ -89,7 +89,9 @@ return; } - FIDO_LOG(EVENT) << "<- " << cbor::DiagnosticWriter::Write(request_); + FIDO_LOG(EVENT) << "<- " + << cbor::DiagnosticWriter::Write( + RedactEnclaveRequest(request_)); BuildCommandRequestBody( std::move(request_), std::move(signing_callback_), *handshake_hash_, base::BindOnce(&Transaction::RequestReady, scoped_refptr(this))); @@ -111,7 +113,9 @@ break; } - FIDO_LOG(EVENT) << "-> " << cbor::DiagnosticWriter::Write(*response); + FIDO_LOG(EVENT) << "-> " + << cbor::DiagnosticWriter::Write( + RedactEnclaveResponse(*response)); if (!response->is_map()) { RecordTransactionResult(EnclaveTransactionResult::kParseFailure); std::move(callback_).Run(base::unexpected(TransactError::kOther));
diff --git a/docs/orderfile.md b/docs/orderfile.md index 9c5d327f..3453e41 100644 --- a/docs/orderfile.md +++ b/docs/orderfile.md
@@ -14,7 +14,7 @@ ## Generating Orderfiles Manually -To generate an orderfile you can run the `orderfile_generator_backend.py` +To generate an orderfile you can run the `generate_orderfile_full.py` script. You will need an Android device connected with [adb](https://developer.android.com/tools/adb) to generate the orderfile as the generation pipeline will need to run benchmarks on a device. @@ -22,7 +22,7 @@ Example: ``` -tools/cygprofile/orderfile_generator_backend.py --target-arch=arm64 --use-remoteexec +tools/cygprofile/generate_orderfile_full.py --target-arch=arm64 --use-remoteexec ``` You can specify the architecture (arm or arm64) with `--target-arch`. For quick @@ -78,7 +78,7 @@ ## Orderfile Pipeline -The `orderfile_generator_backend.py` script runs several key steps: +The `generate_orderfile_full.py` script runs several key steps: 1. **Build and install Chrome with orderfile instrumentation.** This uses the [`-finstrument-function-entry-bare`](https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-finstrument-function-entry-bare)
diff --git a/google_apis/gaia/gaia_auth_fetcher.cc b/google_apis/gaia/gaia_auth_fetcher.cc index d18521e2..da54b4a 100644 --- a/google_apis/gaia/gaia_auth_fetcher.cc +++ b/google_apis/gaia/gaia_auth_fetcher.cc
@@ -136,24 +136,6 @@ return GaiaAuthConsumer::ReAuthProofTokenStatus::kUnknownError; } -std::string CreateMultiBearerAuthorizationHeader( - const std::vector<gaia::MultiloginAccountAuthCredentials>& accounts) { - std::vector<std::string> authorization_header_parts; - std::ranges::transform( - accounts, std::back_inserter(authorization_header_parts), - [](const auto& account) { - return base::StrCat({account.token, ":", account.gaia_id.ToString()}); - }); - - return "Authorization: MultiBearer " + - base::JoinString(authorization_header_parts, ","); -} - -std::string CreateMultiOAuthAuthorizationHeader( - const std::vector<gaia::MultiloginAccountAuthCredentials>& accounts) { - return "Authorization: MultiOAuth " + gaia::CreateMultiOAuthHeader(accounts); -} - } // namespace namespace gaia { @@ -245,7 +227,7 @@ void GaiaAuthFetcher::CreateAndStartGaiaFetcher( const std::string& body, const std::string& body_content_type, - const std::string& headers, + const net::HttpRequestHeaders& request_headers, const GURL& gaia_gurl, network::mojom::CredentialsMode credentials_mode, const net::NetworkTrafficAnnotationTag& traffic_annotation) { @@ -272,8 +254,7 @@ if (!body.empty()) resource_request->method = "POST"; - if (!headers.empty()) - resource_request->headers.AddHeadersFromString(headers); + resource_request->headers = request_headers; resource_request->credentials_mode = credentials_mode; @@ -287,7 +268,7 @@ url_loader_->SetAllowHttpErrorResults(true); VLOG(2) << "Gaia fetcher URL: " << gaia_gurl.spec(); - VLOG(2) << "Gaia fetcher headers: " << headers; + VLOG(2) << "Gaia fetcher headers: " << request_headers.ToString(); VLOG(2) << "Gaia fetcher body: " << body; // Fetchers are sometimes cancelled because a network change was detected, @@ -386,7 +367,7 @@ } })"); CreateAndStartGaiaFetcher( - request_body_, kFormEncodedContentType, std::string(), + request_body_, kFormEncodedContentType, net::HttpRequestHeaders(), oauth2_revoke_gurl_, google_apis::GetOmitCredentialsModeForGaiaRequests(), traffic_annotation); } @@ -409,10 +390,10 @@ VLOG(1) << "Starting OAuth token pair fetch"; - std::string user_agent_full_version_list_header; + net::HttpRequestHeaders headers; if (!user_agent_full_version_list.empty()) { - user_agent_full_version_list_header = base::StrCat( - {"Sec-CH-UA-Full-Version-List: ", user_agent_full_version_list}); + headers.SetHeader("Sec-CH-UA-Full-Version-List", + user_agent_full_version_list); } request_body_ = @@ -448,7 +429,7 @@ })"); CreateAndStartGaiaFetcher( request_body_, kFormEncodedContentType, - user_agent_full_version_list_header, oauth2_token_gurl_, + headers, oauth2_token_gurl_, google_apis::GetOmitCredentialsModeForGaiaRequests(), traffic_annotation); } @@ -482,9 +463,11 @@ } } })"); + net::HttpRequestHeaders headers; + headers.SetHeader("Origin", "https://www.google.com"); CreateAndStartGaiaFetcher( " ", // To force an HTTP POST. - kFormEncodedContentType, "Origin: https://www.google.com", + kFormEncodedContentType, headers, list_accounts_gurl_, network::mojom::CredentialsMode::kInclude, traffic_annotation); } @@ -503,9 +486,21 @@ accounts, [](const gaia::MultiloginAccountAuthCredentials& account) { return !account.token_binding_assertion.empty(); }); - std::string authorization_header = - has_binding_assertion ? CreateMultiOAuthAuthorizationHeader(accounts) - : CreateMultiBearerAuthorizationHeader(accounts); + net::HttpRequestHeaders headers; + if (has_binding_assertion) { + headers.SetHeader("Authorization", + "MultiOAuth " + gaia::CreateMultiOAuthHeader(accounts)); + } else { + std::vector<std::string> authorization_header_parts; + std::ranges::transform( + accounts, std::back_inserter(authorization_header_parts), + [](const auto& account) { + return base::StrCat({account.token, ":", account.gaia_id.ToString()}); + }); + headers.SetHeader( + "Authorization", + "MultiBearer " + base::JoinString(authorization_header_parts, ",")); + } std::string source_string = base::EscapeUrlEncodedData(source_, true); std::string parameters = base::StringPrintf( @@ -549,7 +544,7 @@ } })"); CreateAndStartGaiaFetcher(" ", // Non-empty to force a POST - kFormEncodedContentType, authorization_header, + kFormEncodedContentType, headers, oauth_multilogin_gurl_.Resolve(parameters), network::mojom::CredentialsMode::kInclude, traffic_annotation); @@ -586,7 +581,7 @@ } })"); CreateAndStartGaiaFetcher( - std::string(), std::string(), std::string(), logout_gurl_, + std::string(), std::string(), net::HttpRequestHeaders(), logout_gurl_, network::mojom::CredentialsMode::kInclude, traffic_annotation); } @@ -603,9 +598,9 @@ DCHECK(write_success); // Create the Authorization header. - std::string auth_header = "Bearer " + child_oauth_access_token; - std::string headers = "Authorization: " + auth_header + "\r\n" + - "Content-Type: " + kJsonContentType; + net::HttpRequestHeaders headers; + headers.SetHeader("Authorization", "Bearer " + child_oauth_access_token); + headers.SetHeader("Content-Type", kJsonContentType); // Create the traffic annotation. net::NetworkTrafficAnnotationTag traffic_annotation( @@ -682,7 +677,7 @@ } })"); CreateAndStartGaiaFetcher( - std::string(), std::string(), std::string(), + std::string(), std::string(), net::HttpRequestHeaders(), get_check_connection_info_url_, google_apis::GetOmitCredentialsModeForGaiaRequests(), traffic_annotation); }
diff --git a/google_apis/gaia/gaia_auth_fetcher.h b/google_apis/gaia/gaia_auth_fetcher.h index a7ac6f1..92fa674 100644 --- a/google_apis/gaia/gaia_auth_fetcher.h +++ b/google_apis/gaia/gaia_auth_fetcher.h
@@ -19,6 +19,7 @@ #include "google_apis/gaia/gaia_auth_util.h" #include "google_apis/gaia/google_service_auth_error.h" #include "google_apis/gaia/oauth_multilogin_result.h" +#include "net/http/http_request_headers.h" #include "net/base/net_errors.h" #include "net/traffic_annotation/network_traffic_annotation.h" #include "services/network/public/mojom/fetch_api.mojom.h" @@ -166,8 +167,8 @@ protected: // Creates and starts |url_loader_|, used to make all Gaia request. |body| is // used as the body of the POST request sent to GAIA. |body_content_type| is - // the body content type to set, but only used if |body| is set. Any strings - // listed in |headers| are added as extra HTTP headers in the request. + // the body content type to set, but only used if |body| is set. + // |request_headers| are added as extra HTTP headers in the request. // // |credentials_mode| are passed to directly to // network::SimpleURLLoader::Create() when creating the SimpleURLLoader. @@ -177,7 +178,7 @@ virtual void CreateAndStartGaiaFetcher( const std::string& body, const std::string& body_content_type, - const std::string& headers, + const net::HttpRequestHeaders& request_headers, const GURL& gaia_gurl, network::mojom::CredentialsMode credentials_mode, const net::NetworkTrafficAnnotationTag& traffic_annotation);
diff --git a/google_apis/gaia/gaia_auth_fetcher_unittest.cc b/google_apis/gaia/gaia_auth_fetcher_unittest.cc index 3ff17a1..6becb12 100644 --- a/google_apis/gaia/gaia_auth_fetcher_unittest.cc +++ b/google_apis/gaia/gaia_auth_fetcher_unittest.cc
@@ -190,7 +190,7 @@ void CreateAndStartGaiaFetcherForTesting( const std::string& body, - const std::string& headers, + const net::HttpRequestHeaders& headers, const GURL& gaia_gurl, network::mojom::CredentialsMode credentials_mode, const net::NetworkTrafficAnnotationTag& traffic_annotation) { @@ -555,7 +555,7 @@ TestGaiaAuthFetcher auth(&consumer, GetURLLoaderFactory()); auth.CreateAndStartGaiaFetcherForTesting( - /*body=*/"", /*headers=*/"", + /*body=*/"", net::HttpRequestHeaders(), GaiaUrls::GetInstance()->ListAccountsURLWithSource( GaiaConstants::kChromeSource), network::mojom::CredentialsMode::kInclude, TRAFFIC_ANNOTATION_FOR_TESTS); @@ -573,7 +573,7 @@ TestGaiaAuthFetcher auth(&consumer, GetURLLoaderFactory()); auth.CreateAndStartGaiaFetcherForTesting( - /*body=*/"", /*headers=*/"", + /*body=*/"", net::HttpRequestHeaders(), GaiaUrls::GetInstance()->LogOutURLWithSource( GaiaConstants::kChromeSource), network::mojom::CredentialsMode::kInclude, TRAFFIC_ANNOTATION_FOR_TESTS); @@ -590,7 +590,7 @@ TestGaiaAuthFetcher auth(&consumer, GetURLLoaderFactory()); auth.CreateAndStartGaiaFetcherForTesting( - /*body=*/"", /*headers=*/"", + /*body=*/"", net::HttpRequestHeaders(), GaiaUrls::GetInstance()->LogOutURLWithSource( GaiaConstants::kChromeSource), network::mojom::CredentialsMode::kInclude, TRAFFIC_ANNOTATION_FOR_TESTS); @@ -606,7 +606,7 @@ TestGaiaAuthFetcher auth(&consumer, GetURLLoaderFactory()); auth.CreateAndStartGaiaFetcherForTesting( - /*body=*/"", /*headers=*/"", + /*body=*/"", net::HttpRequestHeaders(), GaiaUrls::GetInstance()->GetCheckConnectionInfoURLWithSource( GaiaConstants::kChromeSource), network::mojom::CredentialsMode::kInclude, TRAFFIC_ANNOTATION_FOR_TESTS); @@ -622,7 +622,7 @@ TestGaiaAuthFetcher auth(&consumer, GetURLLoaderFactory()); auth.CreateAndStartGaiaFetcherForTesting( - /*body=*/"", /*headers=*/"", GaiaUrls::GetInstance()->oauth2_revoke_url(), + /*body=*/"", net::HttpRequestHeaders(), GaiaUrls::GetInstance()->oauth2_revoke_url(), network::mojom::CredentialsMode::kInclude, TRAFFIC_ANNOTATION_FOR_TESTS); auth.TestOnURLLoadCompleteInternal(net::OK, net::HTTP_OK, data); } @@ -636,7 +636,7 @@ TestGaiaAuthFetcher auth(&consumer, GetURLLoaderFactory()); auth.CreateAndStartGaiaFetcherForTesting( - /*body=*/"", /*headers=*/"", GaiaUrls::GetInstance()->oauth2_revoke_url(), + /*body=*/"", net::HttpRequestHeaders(), GaiaUrls::GetInstance()->oauth2_revoke_url(), network::mojom::CredentialsMode::kInclude, TRAFFIC_ANNOTATION_FOR_TESTS); auth.TestOnURLLoadCompleteInternal(net::ERR_ABORTED); } @@ -650,7 +650,7 @@ TestGaiaAuthFetcher auth(&consumer, GetURLLoaderFactory()); auth.CreateAndStartGaiaFetcherForTesting( - /*body=*/"", /*headers=*/"", GaiaUrls::GetInstance()->oauth2_revoke_url(), + /*body=*/"", net::HttpRequestHeaders(), GaiaUrls::GetInstance()->oauth2_revoke_url(), network::mojom::CredentialsMode::kInclude, TRAFFIC_ANNOTATION_FOR_TESTS); auth.TestOnURLLoadCompleteInternal(net::ERR_CERT_CONTAINS_ERRORS); } @@ -664,7 +664,7 @@ TestGaiaAuthFetcher auth(&consumer, GetURLLoaderFactory()); auth.CreateAndStartGaiaFetcherForTesting( - /*body=*/"", /*headers=*/"", GaiaUrls::GetInstance()->oauth2_revoke_url(), + /*body=*/"", net::HttpRequestHeaders(), GaiaUrls::GetInstance()->oauth2_revoke_url(), network::mojom::CredentialsMode::kInclude, TRAFFIC_ANNOTATION_FOR_TESTS); auth.TestOnURLLoadCompleteInternal(net::ERR_TIMED_OUT); } @@ -679,7 +679,7 @@ TestGaiaAuthFetcher auth(&consumer, GetURLLoaderFactory()); auth.CreateAndStartGaiaFetcherForTesting( - /*body=*/"", /*headers=*/"", GaiaUrls::GetInstance()->oauth2_revoke_url(), + /*body=*/"", net::HttpRequestHeaders(), GaiaUrls::GetInstance()->oauth2_revoke_url(), network::mojom::CredentialsMode::kInclude, TRAFFIC_ANNOTATION_FOR_TESTS); auth.TestOnURLLoadCompleteInternal(net::OK, net::HTTP_BAD_REQUEST, data); } @@ -694,7 +694,7 @@ TestGaiaAuthFetcher auth(&consumer, GetURLLoaderFactory()); auth.CreateAndStartGaiaFetcherForTesting( - /*body=*/"", /*headers=*/"", GaiaUrls::GetInstance()->oauth2_revoke_url(), + /*body=*/"", net::HttpRequestHeaders(), GaiaUrls::GetInstance()->oauth2_revoke_url(), network::mojom::CredentialsMode::kInclude, TRAFFIC_ANNOTATION_FOR_TESTS); auth.TestOnURLLoadCompleteInternal(net::OK, net::HTTP_BAD_REQUEST, data); } @@ -709,7 +709,7 @@ TestGaiaAuthFetcher auth(&consumer, GetURLLoaderFactory()); auth.CreateAndStartGaiaFetcherForTesting( - /*body=*/"", /*headers=*/"", GaiaUrls::GetInstance()->oauth2_revoke_url(), + /*body=*/"", net::HttpRequestHeaders(), GaiaUrls::GetInstance()->oauth2_revoke_url(), network::mojom::CredentialsMode::kInclude, TRAFFIC_ANNOTATION_FOR_TESTS); auth.TestOnURLLoadCompleteInternal(net::OK, net::HTTP_INTERNAL_SERVER_ERROR, data); @@ -722,7 +722,7 @@ TestGaiaAuthFetcher auth(&consumer, GetURLLoaderFactory()); auth.CreateAndStartGaiaFetcherForTesting( - /*body=*/"", /*headers=*/"", GaiaUrls::GetInstance()->reauth_api_url(), + /*body=*/"", net::HttpRequestHeaders(), GaiaUrls::GetInstance()->reauth_api_url(), google_apis::GetOmitCredentialsModeForGaiaRequests(), TRAFFIC_ANNOTATION_FOR_TESTS); auth.TestOnURLLoadCompleteInternal(net::OK, net::HTTP_OK, data); @@ -738,7 +738,7 @@ TestGaiaAuthFetcher auth(&consumer, GetURLLoaderFactory()); auth.CreateAndStartGaiaFetcherForTesting( - /*body=*/"", /*headers=*/"", GaiaUrls::GetInstance()->reauth_api_url(), + /*body=*/"", net::HttpRequestHeaders(), GaiaUrls::GetInstance()->reauth_api_url(), google_apis::GetOmitCredentialsModeForGaiaRequests(), TRAFFIC_ANNOTATION_FOR_TESTS); auth.TestOnURLLoadCompleteInternal(net::OK, net::HTTP_BAD_REQUEST, data); @@ -754,7 +754,7 @@ TestGaiaAuthFetcher auth(&consumer, GetURLLoaderFactory()); auth.CreateAndStartGaiaFetcherForTesting( - /*body=*/"", /*headers=*/"", GaiaUrls::GetInstance()->reauth_api_url(), + /*body=*/"", net::HttpRequestHeaders(), GaiaUrls::GetInstance()->reauth_api_url(), google_apis::GetOmitCredentialsModeForGaiaRequests(), TRAFFIC_ANNOTATION_FOR_TESTS); auth.TestOnURLLoadCompleteInternal(net::OK, net::HTTP_BAD_REQUEST, data); @@ -771,7 +771,7 @@ TestGaiaAuthFetcher auth(&consumer, GetURLLoaderFactory()); auth.CreateAndStartGaiaFetcherForTesting( - /*body=*/"", /*headers=*/"", GaiaUrls::GetInstance()->reauth_api_url(), + /*body=*/"", net::HttpRequestHeaders(), GaiaUrls::GetInstance()->reauth_api_url(), google_apis::GetOmitCredentialsModeForGaiaRequests(), TRAFFIC_ANNOTATION_FOR_TESTS); auth.TestOnURLLoadCompleteInternal(net::OK, net::HTTP_FORBIDDEN, data); @@ -787,7 +787,7 @@ TestGaiaAuthFetcher auth(&consumer, GetURLLoaderFactory()); auth.CreateAndStartGaiaFetcherForTesting( - /*body=*/"", /*headers=*/"", GaiaUrls::GetInstance()->reauth_api_url(), + /*body=*/"", net::HttpRequestHeaders(), GaiaUrls::GetInstance()->reauth_api_url(), google_apis::GetOmitCredentialsModeForGaiaRequests(), TRAFFIC_ANNOTATION_FOR_TESTS); auth.TestOnURLLoadCompleteInternal(net::OK, net::HTTP_FORBIDDEN, data); @@ -803,7 +803,7 @@ TestGaiaAuthFetcher auth(&consumer, GetURLLoaderFactory()); auth.CreateAndStartGaiaFetcherForTesting( - /*body=*/"", /*headers=*/"", GaiaUrls::GetInstance()->reauth_api_url(), + /*body=*/"", net::HttpRequestHeaders(), GaiaUrls::GetInstance()->reauth_api_url(), google_apis::GetOmitCredentialsModeForGaiaRequests(), TRAFFIC_ANNOTATION_FOR_TESTS); auth.TestOnURLLoadCompleteInternal(net::OK, net::HTTP_FORBIDDEN, data);
diff --git a/gpu/command_buffer/client/client_font_manager.cc b/gpu/command_buffer/client/client_font_manager.cc index a6ae14e..45432d7 100644 --- a/gpu/command_buffer/client/client_font_manager.cc +++ b/gpu/command_buffer/client/client_font_manager.cc
@@ -35,7 +35,7 @@ return; UNSAFE_TODO(memcpy(memory_, input, bytes)); - memory_ += bytes; + UNSAFE_TODO(memory_ += bytes); bytes_written_ += bytes; } @@ -48,7 +48,7 @@ size_t padding = base::bits::AlignUp(memory, alignment) - memory; DCHECK_LE(bytes_written_ + size + padding, memory_size_); - memory_ += padding; + UNSAFE_TODO(memory_ += padding); bytes_written_ += padding; }
diff --git a/gpu/command_buffer/client/cmd_buffer_helper.cc b/gpu/command_buffer/client/cmd_buffer_helper.cc index d0fc4ab..ccda45b1 100644 --- a/gpu/command_buffer/client/cmd_buffer_helper.cc +++ b/gpu/command_buffer/client/cmd_buffer_helper.cc
@@ -9,6 +9,8 @@ #include <stdint.h> #include <algorithm> + +#include "base/compiler_specific.h" #include "base/logging.h" #include "base/strings/stringprintf.h" #include "base/time/time.h" @@ -317,7 +319,7 @@ int32_t num_entries = total_entry_count_ - put_; while (num_entries > 0) { int32_t num_to_skip = std::min(CommandHeader::kMaxSize, num_entries); - cmd::Noop::Set(&entries_[put_], num_to_skip); + cmd::Noop::Set(UNSAFE_TODO(&entries_[put_]), num_to_skip); put_ += num_to_skip; num_entries -= num_to_skip; }
diff --git a/gpu/command_buffer/client/cmd_buffer_helper.h b/gpu/command_buffer/client/cmd_buffer_helper.h index fdfe2da1..a7060f7 100644 --- a/gpu/command_buffer/client/cmd_buffer_helper.h +++ b/gpu/command_buffer/client/cmd_buffer_helper.h
@@ -157,7 +157,7 @@ DCHECK_LE(entries, immediate_entry_count_); // Allocate space and advance put_. - CommandBufferEntry* space = &entries_[put_]; + CommandBufferEntry* space = UNSAFE_TODO(&entries_[put_]); put_ += entries; immediate_entry_count_ -= entries;
diff --git a/gpu/command_buffer/client/query_tracker.cc b/gpu/command_buffer/client/query_tracker.cc index 807331d..79ce22cc 100644 --- a/gpu/command_buffer/client/query_tracker.cc +++ b/gpu/command_buffer/client/query_tracker.cc
@@ -7,13 +7,14 @@ #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> #include <GLES2/gl2extchromium.h> - #include <limits.h> #include <stddef.h> #include <stdint.h> + #include <vector> #include "base/atomicops.h" +#include "base/compiler_specific.h" #include "base/containers/circular_deque.h" #include "base/numerics/safe_conversions.h" #include "gpu/command_buffer/client/gles2_cmd_helper.h" @@ -33,7 +34,7 @@ void QuerySyncManager::Bucket::FreePendingSyncs() { std::erase_if(pending_syncs, [this](const PendingSync& pending) { - QuerySync* sync = this->syncs + pending.index; + QuerySync* sync = UNSAFE_TODO(this->syncs + pending.index); if (base::subtle::Acquire_Load(&sync->process_count) == pending.submit_count) { this->in_use_query_syncs[pending.index] = false;
diff --git a/gpu/command_buffer/client/query_tracker.h b/gpu/command_buffer/client/query_tracker.h index b31e5d87..f17db78 100644 --- a/gpu/command_buffer/client/query_tracker.h +++ b/gpu/command_buffer/client/query_tracker.h
@@ -56,7 +56,7 @@ struct QueryInfo { QueryInfo(Bucket* bucket, uint32_t index) - : bucket(bucket), sync(bucket->syncs + index) {} + : bucket(bucket), sync(UNSAFE_TODO(bucket->syncs + index)) {} QueryInfo() = default; uint32_t index() const { return sync - bucket->syncs.get(); }
diff --git a/gpu/command_buffer/service/command_buffer_service.cc b/gpu/command_buffer/service/command_buffer_service.cc index 93b8229..5a2928a6 100644 --- a/gpu/command_buffer/service/command_buffer_service.cc +++ b/gpu/command_buffer/service/command_buffer_service.cc
@@ -10,6 +10,7 @@ #include <limits> #include <memory> +#include "base/compiler_specific.h" #include "base/logging.h" #include "base/memory/page_size.h" #include "base/trace_event/memory_dump_manager.h" @@ -262,9 +263,9 @@ while (put_offset_ != state_.get_offset) { int num_entries = end - state_.get_offset; int entries_processed = 0; - error::Error error = handler->DoCommands(GetCommandBufferSliceSize(), - buffer_ + state_.get_offset, - num_entries, &entries_processed); + error::Error error = handler->DoCommands( + GetCommandBufferSliceSize(), UNSAFE_TODO(buffer_ + state_.get_offset), + num_entries, &entries_processed); state_.get_offset += entries_processed; DCHECK_LE(state_.get_offset, num_entries_);
diff --git a/gpu/command_buffer/service/service_font_manager.cc b/gpu/command_buffer/service/service_font_manager.cc index 8b7feab..59848a0 100644 --- a/gpu/command_buffer/service/service_font_manager.cc +++ b/gpu/command_buffer/service/service_font_manager.cc
@@ -15,6 +15,7 @@ #include <type_traits> #include "base/bits.h" +#include "base/compiler_specific.h" #include "base/debug/dump_without_crashing.h" #include "base/logging.h" #include "base/memory/raw_ptr.h" @@ -43,7 +44,7 @@ memcpy(val, const_cast<const uint8_t*>(memory_.get()), sizeof(T)); - memory_ += sizeof(T); + UNSAFE_TODO(memory_ += sizeof(T)); bytes_read_ += sizeof(T); return true; } @@ -62,7 +63,7 @@ return false; bytes_read_ += size; - memory_ += size; + UNSAFE_TODO(memory_ += size); return true; } @@ -82,7 +83,7 @@ return false; } - memory_ += padding; + UNSAFE_TODO(memory_ += padding); bytes_read_ += padding; return true; }
diff --git a/gpu/ipc/service/context_url.cc b/gpu/ipc/service/context_url.cc index de8b33d..a7e9879 100644 --- a/gpu/ipc/service/context_url.cc +++ b/gpu/ipc/service/context_url.cc
@@ -31,9 +31,9 @@ last_url_hash = active_url.hash(); - // Note that the url is intentionally excluded from WebView and WebLayer + // Note that the url is intentionally excluded from WebView // crash dumps using an allowlist for privacy reasons. See - // kWebViewCrashKeyAllowList and kWebLayerCrashKeyAllowList. + // kWebViewCrashKeyAllowList. { static crash_reporter::CrashKeyString<1024> crash_key("gpu-url-chunk"); crash_key.Set(active_url.url().possibly_invalid_spec());
diff --git a/infra/config/generated/builder-owners/~unowned.txt b/infra/config/generated/builder-owners/~unowned.txt index bb271cd..a317307 100644 --- a/infra/config/generated/builder-owners/~unowned.txt +++ b/infra/config/generated/builder-owners/~unowned.txt
@@ -22,7 +22,6 @@ ci/android-device-flasher ci/android-fieldtrial-rel ci/android-perfetto-rel -ci/android-pie-x86-fyi-rel ci/android-rust-arm32-rel ci/android-rust-arm64-dbg ci/android-rust-arm64-rel @@ -74,7 +73,6 @@ ci/mac-rust-x64-dbg ci/mac-ubsan-fyi-rel ci/metadata-exporter -ci/rts-model-packager ci/rts-suite-analysis ci/win-annotator-rel ci/win-fieldtrial-rel
diff --git a/infra/config/generated/builders/ci/android-pie-x86-fyi-rel/gn-args.json b/infra/config/generated/builders/ci/android-pie-x86-fyi-rel/gn-args.json deleted file mode 100644 index bec7c3d3..0000000 --- a/infra/config/generated/builders/ci/android-pie-x86-fyi-rel/gn-args.json +++ /dev/null
@@ -1,20 +0,0 @@ -{ - "gn_args": { - "android_static_analysis": "off", - "dcheck_always_on": false, - "debuggable_apks": false, - "ffmpeg_branding": "Chrome", - "is_component_build": false, - "is_debug": false, - "proprietary_codecs": true, - "strip_debug_info": true, - "symbol_level": 1, - "system_webview_package_name": "com.google.android.apps.chrome", - "system_webview_shell_package_name": "org.chromium.my_webview_shell", - "target_cpu": "x86", - "target_os": "android", - "use_reclient": false, - "use_remoteexec": true, - "use_siso": true - } -} \ No newline at end of file
diff --git a/infra/config/generated/builders/ci/android-pie-x86-fyi-rel/properties.json b/infra/config/generated/builders/ci/android-pie-x86-fyi-rel/properties.json deleted file mode 100644 index 260bb3d..0000000 --- a/infra/config/generated/builders/ci/android-pie-x86-fyi-rel/properties.json +++ /dev/null
@@ -1,75 +0,0 @@ -{ - "$build/chromium_tests_builder_config": { - "builder_config": { - "additional_exclusions": [ - "infra/config/generated/builders/ci/android-pie-x86-fyi-rel/gn-args.json" - ], - "builder_db": { - "entries": [ - { - "builder_id": { - "bucket": "ci", - "builder": "android-pie-x86-fyi-rel", - "project": "chromium" - }, - "builder_spec": { - "build_gs_bucket": "chromium-android-archive", - "builder_group": "chromium.android.fyi", - "execution_mode": "COMPILE_AND_TEST", - "legacy_android_config": { - "config": "base_config" - }, - "legacy_chromium_config": { - "apply_configs": [ - "mb" - ], - "build_config": "Release", - "config": "main_builder", - "target_arch": "intel", - "target_bits": 32, - "target_platform": "android" - }, - "legacy_gclient_config": { - "apply_configs": [ - "android", - "chromium_with_telemetry_dependencies", - "enable_wpr_tests" - ], - "config": "chromium" - } - } - } - ] - }, - "builder_ids": [ - { - "bucket": "ci", - "builder": "android-pie-x86-fyi-rel", - "project": "chromium" - } - ], - "targets_spec_directory": "src/infra/config/generated/builders/ci/android-pie-x86-fyi-rel/targets" - } - }, - "$build/siso": { - "configs": [ - "builder" - ], - "enable_cloud_monitoring": true, - "enable_cloud_profiler": true, - "enable_cloud_trace": true, - "experiments": [], - "metrics_project": "chromium-reclient-metrics", - "project": "rbe-chromium-trusted", - "remote_jobs": 250 - }, - "$recipe_engine/resultdb/test_presentation": { - "column_keys": [], - "grouping_keys": [ - "status", - "v.test_suite" - ] - }, - "builder_group": "chromium.android.fyi", - "recipe": "chromium" -} \ No newline at end of file
diff --git a/infra/config/generated/builders/ci/android-pie-x86-fyi-rel/shadow-properties.json b/infra/config/generated/builders/ci/android-pie-x86-fyi-rel/shadow-properties.json deleted file mode 100644 index 78dedff8..0000000 --- a/infra/config/generated/builders/ci/android-pie-x86-fyi-rel/shadow-properties.json +++ /dev/null
@@ -1,14 +0,0 @@ -{ - "$build/siso": { - "configs": [ - "builder" - ], - "enable_cloud_monitoring": true, - "enable_cloud_profiler": true, - "enable_cloud_trace": true, - "experiments": [], - "metrics_project": "chromium-reclient-metrics", - "project": "rbe-chromium-untrusted", - "remote_jobs": 250 - } -} \ No newline at end of file
diff --git a/infra/config/generated/builders/ci/android-pie-x86-fyi-rel/targets/chromium.android.fyi.json b/infra/config/generated/builders/ci/android-pie-x86-fyi-rel/targets/chromium.android.fyi.json deleted file mode 100644 index 32e8eed..0000000 --- a/infra/config/generated/builders/ci/android-pie-x86-fyi-rel/targets/chromium.android.fyi.json +++ /dev/null
@@ -1,3994 +0,0 @@ -{ - "android-pie-x86-fyi-rel": { - "additional_compile_targets": [ - "chrome_nocompile_tests" - ], - "gtest_tests": [ - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "absl_hardening_tests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "absl_hardening_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "absl_hardening_tests", - "test_id_prefix": "ninja://third_party/abseil-cpp:absl_hardening_tests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gtest_filter=-ImportantSitesUtilBrowserTest.DSENotConsideredImportantInRegularMode", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "android_browsertests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "android_browsertests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "8", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 9 - }, - "test": "android_browsertests", - "test_id_prefix": "ninja://chrome/test:android_browsertests/" - }, - { - "args": [ - "--test-launcher-batch-limit=1", - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "android_sync_integration_tests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "android_sync_integration_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 2 - }, - "test": "android_sync_integration_tests", - "test_id_prefix": "ninja://chrome/test:android_sync_integration_tests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "android_webview_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "android_webview_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "android_webview_unittests", - "test_id_prefix": "ninja://android_webview/test:android_webview_unittests/" - }, - { - "args": [ - "-v", - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb" - ], - "isolate_profile_data": true, - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "name": "angle_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "angle_unittests", - "test_id_prefix": "ninja://third_party/angle/src/tests:angle_unittests/", - "use_isolated_scripts_api": true - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "base_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "base_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "base_unittests", - "test_id_prefix": "ninja://base:base_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "blink_common_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "blink_common_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "blink_common_unittests", - "test_id_prefix": "ninja://third_party/blink/common:blink_common_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "blink_heap_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "blink_heap_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "blink_heap_unittests", - "test_id_prefix": "ninja://third_party/blink/renderer/platform/heap:blink_heap_unittests/" - }, - { - "args": [ - "--git-revision=${got_revision}", - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "blink_platform_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "blink_platform_unittests", - "precommit_args": [ - "--gerrit-issue=${patch_issue}", - "--gerrit-patchset=${patch_set}", - "--buildbucket-id=${buildbucket_build_id}" - ], - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "blink_platform_unittests", - "test_id_prefix": "ninja://third_party/blink/renderer/platform:blink_platform_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "boringssl_crypto_tests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "boringssl_crypto_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "boringssl_crypto_tests", - "test_id_prefix": "ninja://third_party/boringssl:boringssl_crypto_tests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "boringssl_ssl_tests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "boringssl_ssl_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "boringssl_ssl_tests", - "test_id_prefix": "ninja://third_party/boringssl:boringssl_ssl_tests/" - }, - { - "args": [ - "--gtest_filter=-*UsingRealWebcam*", - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "capture_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "capture_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "capture_unittests", - "test_id_prefix": "ninja://media/capture:capture_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "cast_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "cast_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "cast_unittests", - "test_id_prefix": "ninja://media/cast:cast_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator.cc_unittests.filter", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "cc_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "cc_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "cc_unittests", - "test_id_prefix": "ninja://cc:cc_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "chrome_public_smoke_test" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "chrome_public_smoke_test", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "chrome_public_smoke_test", - "test_id_prefix": "ninja://chrome/android:chrome_public_smoke_test/" - }, - { - "args": [ - "--git-revision=${got_revision}", - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator_p.chrome_public_test_apk.filter", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "chrome_public_test_apk" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "chrome_public_test_apk", - "precommit_args": [ - "--gerrit-issue=${patch_issue}", - "--gerrit-patchset=${patch_set}", - "--buildbucket-id=${buildbucket_build_id}" - ], - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "8", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 75 - }, - "test": "chrome_public_test_apk", - "test_id_prefix": "ninja://chrome/android:chrome_public_test_apk/" - }, - { - "args": [ - "--git-revision=${got_revision}", - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "chrome_public_unit_test_apk" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "chrome_public_unit_test_apk", - "precommit_args": [ - "--gerrit-issue=${patch_issue}", - "--gerrit-patchset=${patch_set}", - "--buildbucket-id=${buildbucket_build_id}" - ], - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 4 - }, - "test": "chrome_public_unit_test_apk", - "test_id_prefix": "ninja://chrome/android:chrome_public_unit_test_apk/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "components_browsertests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "components_browsertests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 4 - }, - "test": "components_browsertests", - "test_id_prefix": "ninja://components:components_browsertests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "components_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "components_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 6 - }, - "test": "components_unittests", - "test_id_prefix": "ninja://components:components_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator_p.content_browsertests.filter", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "content_browsertests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "content_browsertests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "8", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 75 - }, - "test": "content_browsertests", - "test_id_prefix": "ninja://content/test:content_browsertests/" - }, - { - "args": [ - "--gtest_filter=QuicConnectionMigrationTest.*", - "--emulator-enable-network", - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "content_browsertests_with_emulator_network" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "content_browsertests_with_emulator_network", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "idempotent": false, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "content_browsertests", - "test_id_prefix": "ninja://content/test:content_browsertests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gtest_filter=-org.chromium.content.browser.input.ImeInputModeTest.testShowAndHideInputMode*", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "content_shell_test_apk" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "content_shell_test_apk", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "8", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 6 - }, - "test": "content_shell_test_apk", - "test_id_prefix": "ninja://content/shell/android:content_shell_test_apk/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "content_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "content_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 3 - }, - "test": "content_unittests", - "test_id_prefix": "ninja://content/test:content_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "crashpad_tests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "crashpad_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "crashpad_tests", - "test_id_prefix": "ninja://third_party/crashpad/crashpad:crashpad_tests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "crypto_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "crypto_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "crypto_unittests", - "test_id_prefix": "ninja://crypto:crypto_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "device_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "device_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "device_unittests", - "test_id_prefix": "ninja://device:device_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "display_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "display_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "display_unittests", - "test_id_prefix": "ninja://ui/display:display_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "ci_only": true, - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "env_chromium_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "env_chromium_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "env_chromium_unittests", - "test_id_prefix": "ninja://third_party/leveldatabase:env_chromium_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "events_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "events_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "events_unittests", - "test_id_prefix": "ninja://ui/events:events_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "fuzzing_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "fuzzing_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "fuzzing_unittests", - "test_id_prefix": "ninja://testing/libfuzzer/tests:fuzzing_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "gcm_unit_tests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "gcm_unit_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "gcm_unit_tests", - "test_id_prefix": "ninja://google_apis/gcm:gcm_unit_tests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "gfx_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "gfx_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "gfx_unittests", - "test_id_prefix": "ninja://ui/gfx:gfx_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "gin_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "gin_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "gin_unittests", - "test_id_prefix": "ninja://gin:gin_unittests/" - }, - { - "args": [ - "--use-cmd-decoder=validating", - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator_o_p.gl_tests.filter", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "gl_tests_validating" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "gl_tests_validating", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "gl_tests", - "test_id_prefix": "ninja://gpu:gl_tests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "gl_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "gl_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "gl_unittests", - "test_id_prefix": "ninja://ui/gl:gl_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "google_apis_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "google_apis_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "google_apis_unittests", - "test_id_prefix": "ninja://google_apis:google_apis_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "gpu_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "gpu_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "gpu_unittests", - "test_id_prefix": "ninja://gpu:gpu_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "gwp_asan_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "gwp_asan_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "gwp_asan_unittests", - "test_id_prefix": "ninja://components/gwp_asan:gwp_asan_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "ipc_tests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "ipc_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "ipc_tests", - "test_id_prefix": "ninja://ipc:ipc_tests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "latency_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "latency_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "latency_unittests", - "test_id_prefix": "ninja://ui/latency:latency_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "ci_only": true, - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "leveldb_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "leveldb_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "leveldb_unittests", - "test_id_prefix": "ninja://third_party/leveldatabase:leveldb_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "libjingle_xmpp_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "libjingle_xmpp_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "libjingle_xmpp_unittests", - "test_id_prefix": "ninja://third_party/libjingle_xmpp:libjingle_xmpp_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "liburlpattern_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "liburlpattern_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "liburlpattern_unittests", - "test_id_prefix": "ninja://third_party/liburlpattern:liburlpattern_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "media_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "media_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "media_unittests", - "test_id_prefix": "ninja://media:media_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "midi_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "midi_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "midi_unittests", - "test_id_prefix": "ninja://media/midi:midi_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "mojo_test_apk" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "mojo_test_apk", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "mojo_test_apk", - "test_id_prefix": "ninja://mojo/public/java/system:mojo_test_apk/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "mojo_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "mojo_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "mojo_unittests", - "test_id_prefix": "ninja://mojo:mojo_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "monochrome_public_bundle_smoke_test" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "monochrome_public_bundle_smoke_test", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "monochrome_public_bundle_smoke_test", - "test_id_prefix": "ninja://chrome/android:monochrome_public_bundle_smoke_test/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "monochrome_public_smoke_test" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "monochrome_public_smoke_test", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "monochrome_public_smoke_test", - "test_id_prefix": "ninja://chrome/android:monochrome_public_smoke_test/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator.net_unittests.filter", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "net_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "net_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 3 - }, - "test": "net_unittests", - "test_id_prefix": "ninja://net:net_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "sandbox_linux_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "sandbox_linux_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "sandbox_linux_unittests", - "test_id_prefix": "ninja://sandbox/linux:sandbox_linux_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gtest_filter=-PacLibraryTest.ActualPacMyIpAddress*", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "services_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "services_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 3 - }, - "test": "services_unittests", - "test_id_prefix": "ninja://services:services_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "shell_dialogs_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "shell_dialogs_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "shell_dialogs_unittests", - "test_id_prefix": "ninja://ui/shell_dialogs:shell_dialogs_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "skia_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "skia_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "skia_unittests", - "test_id_prefix": "ninja://skia:skia_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "sql_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "sql_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "sql_unittests", - "test_id_prefix": "ninja://sql:sql_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "storage_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "storage_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "storage_unittests", - "test_id_prefix": "ninja://storage:storage_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "system_webview_shell_layout_test_apk" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "system_webview_shell_layout_test_apk", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "system_webview_shell_layout_test_apk", - "test_id_prefix": "ninja://android_webview/tools/system_webview_shell:system_webview_shell_layout_test_apk/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "ui_android_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "ui_android_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "ui_android_unittests", - "test_id_prefix": "ninja://ui/android:ui_android_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "ui_base_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "ui_base_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "ui_base_unittests", - "test_id_prefix": "ninja://ui/base:ui_base_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "ui_touch_selection_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "ui_touch_selection_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "ui_touch_selection_unittests", - "test_id_prefix": "ninja://ui/touch_selection:ui_touch_selection_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "unit_tests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "unit_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 2 - }, - "test": "unit_tests", - "test_id_prefix": "ninja://chrome/test:unit_tests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "url_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "url_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "url_unittests", - "test_id_prefix": "ninja://url:url_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "video_encode_accelerator_tests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "video_encode_accelerator_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "video_encode_accelerator_tests", - "test_id_prefix": "ninja://media/gpu/test:video_encode_accelerator_tests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "viz_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "viz_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "viz_unittests", - "test_id_prefix": "ninja://components/viz:viz_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "webkit_unit_tests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "webkit_unit_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 6 - }, - "test": "blink_unittests", - "test_id_prefix": "ninja://third_party/blink/renderer/controller:blink_unittests/" - }, - { - "args": [ - "--store-tombstones", - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "webview_cts_tests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "webview_cts_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "cipd_packages": [ - { - "cipd_package": "chromium/android_webview/tools/cts_archive", - "location": "android_webview/tools/cts_archive/cipd", - "revision": "8BpUBTnmt5bH3GiqPKpmTWTP-Ie2X1TuUgf4F0IsgVgC" - } - ], - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 2 - }, - "test": "webview_cts_tests", - "test_id_prefix": "ninja://android_webview/test:webview_cts_tests/" - }, - { - "args": [ - "--webview-process-mode=multiple", - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator.webview_instrumentation_test_apk.filter", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "webview_instrumentation_test_apk_multiple_process_mode" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "webview_instrumentation_test_apk_multiple_process_mode", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 15 - }, - "test": "webview_instrumentation_test_apk", - "test_id_prefix": "ninja://android_webview/test:webview_instrumentation_test_apk/" - }, - { - "args": [ - "--webview-process-mode=single", - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator.webview_instrumentation_test_apk.filter", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "webview_instrumentation_test_apk_single_process_mode" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "webview_instrumentation_test_apk_single_process_mode", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 9 - }, - "test": "webview_instrumentation_test_apk", - "test_id_prefix": "ninja://android_webview/test:webview_instrumentation_test_apk/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "webview_ui_test_app_test_apk" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "webview_ui_test_app_test_apk", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "webview_ui_test_app_test_apk", - "test_id_prefix": "ninja://android_webview/tools/automated_ui_tests:webview_ui_test_app_test_apk/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "wtf_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "wtf_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "wtf_unittests", - "test_id_prefix": "ninja://third_party/blink/renderer/platform/wtf:wtf_unittests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--gs-results-bucket=chromium-result-details", - "--recover-devices" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--bucket", - "chromium-result-details", - "--test-name", - "zlib_unittests" - ], - "script": "//build/android/pylib/results/presentation/test_results_presentation.py" - }, - "name": "zlib_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "zlib_unittests", - "test_id_prefix": "ninja://third_party/zlib:zlib_unittests/" - } - ], - "isolated_scripts": [ - { - "isolate_profile_data": true, - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "name": "android_webview_junit_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "8", - "cpu": "x86-64", - "os": "Ubuntu-22.04", - "pool": "chromium.tests" - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "android_webview_junit_tests", - "test_id_prefix": "ninja://android_webview/test:android_webview_junit_tests/" - }, - { - "isolate_profile_data": true, - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "name": "base_junit_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "8", - "cpu": "x86-64", - "os": "Ubuntu-22.04", - "pool": "chromium.tests" - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "base_junit_tests", - "test_id_prefix": "ninja://base:base_junit_tests/" - }, - { - "isolate_profile_data": true, - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "name": "build_junit_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "8", - "cpu": "x86-64", - "os": "Ubuntu-22.04", - "pool": "chromium.tests" - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "build_junit_tests", - "test_id_prefix": "ninja://build/android:build_junit_tests/" - }, - { - "isolate_profile_data": true, - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "name": "chrome_java_test_pagecontroller_junit_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "8", - "cpu": "x86-64", - "os": "Ubuntu-22.04", - "pool": "chromium.tests" - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "chrome_java_test_pagecontroller_junit_tests", - "test_id_prefix": "ninja://chrome/test/android:chrome_java_test_pagecontroller_junit_tests/" - }, - { - "isolate_profile_data": true, - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "name": "chrome_junit_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "8", - "cpu": "x86-64", - "os": "Ubuntu-22.04", - "pool": "chromium.tests" - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "chrome_junit_tests", - "test_id_prefix": "ninja://chrome/android:chrome_junit_tests/" - }, - { - "isolate_profile_data": true, - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "name": "components_junit_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "8", - "cpu": "x86-64", - "os": "Ubuntu-22.04", - "pool": "chromium.tests" - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "components_junit_tests", - "test_id_prefix": "ninja://components:components_junit_tests/" - }, - { - "args": [ - "--gtest-benchmark-name=components_perftests", - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb" - ], - "isolate_profile_data": true, - "merge": { - "args": [ - "--smoke-test-mode" - ], - "script": "//tools/perf/process_perf_results.py" - }, - "name": "components_perftests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "components_perftests", - "test_id_prefix": "ninja://components:components_perftests/" - }, - { - "isolate_profile_data": true, - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "name": "content_junit_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "8", - "cpu": "x86-64", - "os": "Ubuntu-22.04", - "pool": "chromium.tests" - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "content_junit_tests", - "test_id_prefix": "ninja://content/public/android:content_junit_tests/" - }, - { - "isolate_profile_data": true, - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "name": "device_junit_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "8", - "cpu": "x86-64", - "os": "Ubuntu-22.04", - "pool": "chromium.tests" - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "device_junit_tests", - "test_id_prefix": "ninja://device:device_junit_tests/" - }, - { - "isolate_profile_data": true, - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "name": "junit_unit_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "8", - "cpu": "x86-64", - "os": "Ubuntu-22.04", - "pool": "chromium.tests" - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "junit_unit_tests", - "test_id_prefix": "ninja://testing/android/junit:junit_unit_tests/" - }, - { - "isolate_profile_data": true, - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "name": "keyboard_accessory_junit_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "8", - "cpu": "x86-64", - "os": "Ubuntu-22.04", - "pool": "chromium.tests" - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "keyboard_accessory_junit_tests", - "test_id_prefix": "ninja://chrome/android/features/keyboard_accessory:keyboard_accessory_junit_tests/" - }, - { - "isolate_profile_data": true, - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "name": "media_base_junit_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "8", - "cpu": "x86-64", - "os": "Ubuntu-22.04", - "pool": "chromium.tests" - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "media_base_junit_tests", - "test_id_prefix": "ninja://media/base/android:media_base_junit_tests/" - }, - { - "isolate_profile_data": true, - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "name": "module_installer_junit_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "8", - "cpu": "x86-64", - "os": "Ubuntu-22.04", - "pool": "chromium.tests" - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "module_installer_junit_tests", - "test_id_prefix": "ninja://components/module_installer/android:module_installer_junit_tests/" - }, - { - "args": [ - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb" - ], - "isolate_profile_data": true, - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "name": "monochrome_public_apk_checker", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_os_flavor": null, - "device_playstore_version": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "monochrome_public_apk_checker", - "test_id_prefix": "ninja://chrome/android/monochrome:monochrome_public_apk_checker/" - }, - { - "isolate_profile_data": true, - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "name": "net_junit_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "8", - "cpu": "x86-64", - "os": "Ubuntu-22.04", - "pool": "chromium.tests" - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "net_junit_tests", - "test_id_prefix": "ninja://net/android:net_junit_tests/" - }, - { - "isolate_profile_data": true, - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "name": "paint_preview_junit_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "8", - "cpu": "x86-64", - "os": "Ubuntu-22.04", - "pool": "chromium.tests" - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "paint_preview_junit_tests", - "test_id_prefix": "ninja://components/paint_preview/player/android:paint_preview_junit_tests/" - }, - { - "isolate_profile_data": true, - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "name": "password_check_junit_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "8", - "cpu": "x86-64", - "os": "Ubuntu-22.04", - "pool": "chromium.tests" - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "password_check_junit_tests", - "test_id_prefix": "ninja://chrome/browser/password_check/android:password_check_junit_tests/" - }, - { - "isolate_profile_data": true, - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "name": "password_manager_junit_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "8", - "cpu": "x86-64", - "os": "Ubuntu-22.04", - "pool": "chromium.tests" - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "password_manager_junit_tests", - "test_id_prefix": "ninja://chrome/browser/password_manager/android:password_manager_junit_tests/" - }, - { - "isolate_profile_data": true, - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "name": "services_junit_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "8", - "cpu": "x86-64", - "os": "Ubuntu-22.04", - "pool": "chromium.tests" - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "services_junit_tests", - "test_id_prefix": "ninja://services:services_junit_tests/" - }, - { - "args": [ - "BrowserMinidumpTest", - "--browser=android-chromium", - "-v", - "--passthrough", - "--retry-limit=2", - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb" - ], - "isolate_profile_data": true, - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "name": "telemetry_chromium_minidump_unittests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "telemetry_perf_unittests_android_chrome", - "test_id_prefix": "ninja://chrome/test:telemetry_perf_unittests_android_chrome/" - }, - { - "args": [ - "--extra-browser-args=--enable-crashpad", - "--avd-config=../../tools/android/avd/proto/android_28_google_apis_x86.textpb", - "--browser=android-chromium" - ], - "isolate_profile_data": true, - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "name": "telemetry_perf_unittests_android_chrome", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "4", - "cpu": "x86-64", - "device_os": null, - "device_type": null, - "os": "Ubuntu-22.04", - "pool": "chromium.tests.avd" - }, - "idempotent": false, - "named_caches": [ - { - "name": "android_28_google_apis_x86", - "path": ".android_emulator/android_28_google_apis_x86" - } - ], - "optional_dimensions": { - "60": { - "caches": "android_28_google_apis_x86" - } - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com", - "shards": 12 - }, - "test": "telemetry_perf_unittests_android_chrome", - "test_id_prefix": "ninja://chrome/test:telemetry_perf_unittests_android_chrome/" - }, - { - "isolate_profile_data": true, - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "name": "touch_to_fill_junit_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "8", - "cpu": "x86-64", - "os": "Ubuntu-22.04", - "pool": "chromium.tests" - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "touch_to_fill_junit_tests", - "test_id_prefix": "ninja://chrome/browser/touch_to_fill/password_manager/android:touch_to_fill_junit_tests/" - }, - { - "isolate_profile_data": true, - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "name": "ui_junit_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "8", - "cpu": "x86-64", - "os": "Ubuntu-22.04", - "pool": "chromium.tests" - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "ui_junit_tests", - "test_id_prefix": "ninja://ui:ui_junit_tests/" - }, - { - "isolate_profile_data": true, - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "name": "webapk_client_junit_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "8", - "cpu": "x86-64", - "os": "Ubuntu-22.04", - "pool": "chromium.tests" - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "webapk_client_junit_tests", - "test_id_prefix": "ninja://chrome/android/webapk/libs/client:webapk_client_junit_tests/" - }, - { - "isolate_profile_data": true, - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "name": "webapk_shell_apk_h2o_junit_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "8", - "cpu": "x86-64", - "os": "Ubuntu-22.04", - "pool": "chromium.tests" - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "webapk_shell_apk_h2o_junit_tests", - "test_id_prefix": "ninja://chrome/android/webapk/shell_apk:webapk_shell_apk_h2o_junit_tests/" - }, - { - "isolate_profile_data": true, - "merge": { - "script": "//testing/merge_scripts/standard_isolated_script_merge.py" - }, - "name": "webapk_shell_apk_junit_tests", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "swarming": { - "dimensions": { - "cores": "8", - "cpu": "x86-64", - "os": "Ubuntu-22.04", - "pool": "chromium.tests" - }, - "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" - }, - "test": "webapk_shell_apk_junit_tests", - "test_id_prefix": "ninja://chrome/android/webapk/shell_apk:webapk_shell_apk_junit_tests/" - } - ], - "scripts": [ - { - "name": "check_network_annotations", - "resultdb": { - "enable": true, - "has_native_resultdb_integration": true - }, - "script": "check_network_annotations.py" - } - ] - } -} \ No newline at end of file
diff --git a/infra/config/generated/builders/ci/android-x86-code-coverage/targets/chromium.coverage.json b/infra/config/generated/builders/ci/android-x86-code-coverage/targets/chromium.coverage.json index 21bbb9e..f4549fdd 100644 --- a/infra/config/generated/builders/ci/android-x86-code-coverage/targets/chromium.coverage.json +++ b/infra/config/generated/builders/ci/android-x86-code-coverage/targets/chromium.coverage.json
@@ -721,7 +721,7 @@ "--git-revision=${got_revision}", "--use-persistent-shell", "--avd-config=../../tools/android/avd/proto/android_29_google_apis_x86.textpb", - "--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator_o.chrome_public_test_apk.filter", + "--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator_10.chrome_public_test_apk.filter", "--gs-results-bucket=chromium-result-details", "--recover-devices" ],
diff --git a/infra/config/generated/builders/ci/chromeos-x64-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json b/infra/config/generated/builders/ci/chromeos-x64-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json index 341199e..91da401 100644 --- a/infra/config/generated/builders/ci/chromeos-x64-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json +++ b/infra/config/generated/builders/ci/chromeos-x64-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json
@@ -2,8 +2,16 @@ "chromeos-x64-libfuzzer-asan-rel-tests": { "gtest_tests": [ { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, "name": "fuzzing_unittests", - "swarming": {}, + "swarming": { + "dimensions": { + "os": "Ubuntu-22.04" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, "test": "fuzzing_unittests", "test_id_prefix": "ninja://testing/libfuzzer/tests:fuzzing_unittests/" }
diff --git a/infra/config/generated/builders/ci/linux-x64-libfuzzer-asan-dbg-tests/targets/chromium.fuzz.json b/infra/config/generated/builders/ci/linux-x64-libfuzzer-asan-dbg-tests/targets/chromium.fuzz.json index 6d9e60f8..dfdd0b3 100644 --- a/infra/config/generated/builders/ci/linux-x64-libfuzzer-asan-dbg-tests/targets/chromium.fuzz.json +++ b/infra/config/generated/builders/ci/linux-x64-libfuzzer-asan-dbg-tests/targets/chromium.fuzz.json
@@ -2,8 +2,16 @@ "linux-x64-libfuzzer-asan-dbg-tests": { "gtest_tests": [ { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, "name": "fuzzing_unittests", - "swarming": {}, + "swarming": { + "dimensions": { + "os": "Ubuntu-22.04" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, "test": "fuzzing_unittests", "test_id_prefix": "ninja://testing/libfuzzer/tests:fuzzing_unittests/" }
diff --git a/infra/config/generated/builders/ci/linux-x64-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json b/infra/config/generated/builders/ci/linux-x64-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json index d037bbc9..28c9e6b 100644 --- a/infra/config/generated/builders/ci/linux-x64-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json +++ b/infra/config/generated/builders/ci/linux-x64-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json
@@ -2,8 +2,16 @@ "linux-x64-libfuzzer-asan-rel-tests": { "gtest_tests": [ { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, "name": "fuzzing_unittests", - "swarming": {}, + "swarming": { + "dimensions": { + "os": "Ubuntu-22.04" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, "test": "fuzzing_unittests", "test_id_prefix": "ninja://testing/libfuzzer/tests:fuzzing_unittests/" }
diff --git a/infra/config/generated/builders/ci/linux-x64-libfuzzer-msan-rel-tests/targets/chromium.fuzz.json b/infra/config/generated/builders/ci/linux-x64-libfuzzer-msan-rel-tests/targets/chromium.fuzz.json index 01f3382..e5b8c96 100644 --- a/infra/config/generated/builders/ci/linux-x64-libfuzzer-msan-rel-tests/targets/chromium.fuzz.json +++ b/infra/config/generated/builders/ci/linux-x64-libfuzzer-msan-rel-tests/targets/chromium.fuzz.json
@@ -2,8 +2,16 @@ "linux-x64-libfuzzer-msan-rel-tests": { "gtest_tests": [ { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, "name": "fuzzing_unittests", - "swarming": {}, + "swarming": { + "dimensions": { + "os": "Ubuntu-22.04" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, "test": "fuzzing_unittests", "test_id_prefix": "ninja://testing/libfuzzer/tests:fuzzing_unittests/" }
diff --git a/infra/config/generated/builders/ci/linux-x64-libfuzzer-ubsan-rel-tests/targets/chromium.fuzz.json b/infra/config/generated/builders/ci/linux-x64-libfuzzer-ubsan-rel-tests/targets/chromium.fuzz.json index 338b921..be9b255a 100644 --- a/infra/config/generated/builders/ci/linux-x64-libfuzzer-ubsan-rel-tests/targets/chromium.fuzz.json +++ b/infra/config/generated/builders/ci/linux-x64-libfuzzer-ubsan-rel-tests/targets/chromium.fuzz.json
@@ -2,8 +2,16 @@ "linux-x64-libfuzzer-ubsan-rel-tests": { "gtest_tests": [ { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, "name": "fuzzing_unittests", - "swarming": {}, + "swarming": { + "dimensions": { + "os": "Ubuntu-22.04" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, "test": "fuzzing_unittests", "test_id_prefix": "ninja://testing/libfuzzer/tests:fuzzing_unittests/" }
diff --git a/infra/config/generated/builders/ci/linux-x86-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json b/infra/config/generated/builders/ci/linux-x86-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json index 80b2d9e..347e550 100644 --- a/infra/config/generated/builders/ci/linux-x86-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json +++ b/infra/config/generated/builders/ci/linux-x86-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json
@@ -2,8 +2,16 @@ "linux-x86-libfuzzer-asan-rel-tests": { "gtest_tests": [ { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, "name": "fuzzing_unittests", - "swarming": {}, + "swarming": { + "dimensions": { + "os": "Ubuntu-22.04" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, "test": "fuzzing_unittests", "test_id_prefix": "ninja://testing/libfuzzer/tests:fuzzing_unittests/" }
diff --git a/infra/config/generated/builders/ci/mac-arm64-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json b/infra/config/generated/builders/ci/mac-arm64-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json index 5f1fb41..25cf81f 100644 --- a/infra/config/generated/builders/ci/mac-arm64-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json +++ b/infra/config/generated/builders/ci/mac-arm64-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json
@@ -2,8 +2,17 @@ "mac-arm64-libfuzzer-asan-rel-tests": { "gtest_tests": [ { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, "name": "fuzzing_unittests", - "swarming": {}, + "swarming": { + "dimensions": { + "cpu": "arm64", + "os": "Mac-15" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, "test": "fuzzing_unittests", "test_id_prefix": "ninja://testing/libfuzzer/tests:fuzzing_unittests/" }
diff --git a/infra/config/generated/builders/ci/win-x64-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json b/infra/config/generated/builders/ci/win-x64-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json index f37afd1..d93dfed 100644 --- a/infra/config/generated/builders/ci/win-x64-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json +++ b/infra/config/generated/builders/ci/win-x64-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json
@@ -2,8 +2,16 @@ "win-x64-libfuzzer-asan-rel-tests": { "gtest_tests": [ { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, "name": "fuzzing_unittests", - "swarming": {}, + "swarming": { + "dimensions": { + "os": "Windows-10" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, "test": "fuzzing_unittests", "test_id_prefix": "ninja://testing/libfuzzer/tests:fuzzing_unittests/" }
diff --git a/infra/config/generated/builders/gn_args_locations.json b/infra/config/generated/builders/gn_args_locations.json index 059230f..69fe283 100644 --- a/infra/config/generated/builders/gn_args_locations.json +++ b/infra/config/generated/builders/gn_args_locations.json
@@ -88,8 +88,7 @@ "android-15-x64-fyi-rel": "ci/android-15-x64-fyi-rel/gn-args.json", "android-16-x64-fyi-rel": "ci/android-16-x64-fyi-rel/gn-args.json", "android-annotator-rel": "ci/android-annotator-rel/gn-args.json", - "android-cronet-asan-x86-rel": "ci/android-cronet-asan-x86-rel/gn-args.json", - "android-pie-x86-fyi-rel": "ci/android-pie-x86-fyi-rel/gn-args.json" + "android-cronet-asan-x86-rel": "ci/android-cronet-asan-x86-rel/gn-args.json" }, "chromium.angle": { "android-angle-chromium-arm64-builder": "ci/android-angle-chromium-arm64-builder/gn-args.json",
diff --git a/infra/config/generated/builders/try/android-x86-code-coverage/targets/chromium.coverage.json b/infra/config/generated/builders/try/android-x86-code-coverage/targets/chromium.coverage.json index 21bbb9e..f4549fdd 100644 --- a/infra/config/generated/builders/try/android-x86-code-coverage/targets/chromium.coverage.json +++ b/infra/config/generated/builders/try/android-x86-code-coverage/targets/chromium.coverage.json
@@ -721,7 +721,7 @@ "--git-revision=${got_revision}", "--use-persistent-shell", "--avd-config=../../tools/android/avd/proto/android_29_google_apis_x86.textpb", - "--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator_o.chrome_public_test_apk.filter", + "--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator_10.chrome_public_test_apk.filter", "--gs-results-bucket=chromium-result-details", "--recover-devices" ],
diff --git a/infra/config/generated/builders/try/chromeos-x64-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json b/infra/config/generated/builders/try/chromeos-x64-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json index 341199e..91da401 100644 --- a/infra/config/generated/builders/try/chromeos-x64-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json +++ b/infra/config/generated/builders/try/chromeos-x64-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json
@@ -2,8 +2,16 @@ "chromeos-x64-libfuzzer-asan-rel-tests": { "gtest_tests": [ { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, "name": "fuzzing_unittests", - "swarming": {}, + "swarming": { + "dimensions": { + "os": "Ubuntu-22.04" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, "test": "fuzzing_unittests", "test_id_prefix": "ninja://testing/libfuzzer/tests:fuzzing_unittests/" }
diff --git a/infra/config/generated/builders/try/linux-x64-libfuzzer-asan-dbg-tests/targets/chromium.fuzz.json b/infra/config/generated/builders/try/linux-x64-libfuzzer-asan-dbg-tests/targets/chromium.fuzz.json index 6d9e60f8..dfdd0b3 100644 --- a/infra/config/generated/builders/try/linux-x64-libfuzzer-asan-dbg-tests/targets/chromium.fuzz.json +++ b/infra/config/generated/builders/try/linux-x64-libfuzzer-asan-dbg-tests/targets/chromium.fuzz.json
@@ -2,8 +2,16 @@ "linux-x64-libfuzzer-asan-dbg-tests": { "gtest_tests": [ { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, "name": "fuzzing_unittests", - "swarming": {}, + "swarming": { + "dimensions": { + "os": "Ubuntu-22.04" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, "test": "fuzzing_unittests", "test_id_prefix": "ninja://testing/libfuzzer/tests:fuzzing_unittests/" }
diff --git a/infra/config/generated/builders/try/linux-x64-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json b/infra/config/generated/builders/try/linux-x64-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json index d037bbc9..28c9e6b 100644 --- a/infra/config/generated/builders/try/linux-x64-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json +++ b/infra/config/generated/builders/try/linux-x64-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json
@@ -2,8 +2,16 @@ "linux-x64-libfuzzer-asan-rel-tests": { "gtest_tests": [ { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, "name": "fuzzing_unittests", - "swarming": {}, + "swarming": { + "dimensions": { + "os": "Ubuntu-22.04" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, "test": "fuzzing_unittests", "test_id_prefix": "ninja://testing/libfuzzer/tests:fuzzing_unittests/" }
diff --git a/infra/config/generated/builders/try/linux-x64-libfuzzer-msan-rel-tests/targets/chromium.fuzz.json b/infra/config/generated/builders/try/linux-x64-libfuzzer-msan-rel-tests/targets/chromium.fuzz.json index 01f3382..e5b8c96 100644 --- a/infra/config/generated/builders/try/linux-x64-libfuzzer-msan-rel-tests/targets/chromium.fuzz.json +++ b/infra/config/generated/builders/try/linux-x64-libfuzzer-msan-rel-tests/targets/chromium.fuzz.json
@@ -2,8 +2,16 @@ "linux-x64-libfuzzer-msan-rel-tests": { "gtest_tests": [ { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, "name": "fuzzing_unittests", - "swarming": {}, + "swarming": { + "dimensions": { + "os": "Ubuntu-22.04" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, "test": "fuzzing_unittests", "test_id_prefix": "ninja://testing/libfuzzer/tests:fuzzing_unittests/" }
diff --git a/infra/config/generated/builders/try/linux-x64-libfuzzer-ubsan-rel-tests/targets/chromium.fuzz.json b/infra/config/generated/builders/try/linux-x64-libfuzzer-ubsan-rel-tests/targets/chromium.fuzz.json index 338b921..be9b255a 100644 --- a/infra/config/generated/builders/try/linux-x64-libfuzzer-ubsan-rel-tests/targets/chromium.fuzz.json +++ b/infra/config/generated/builders/try/linux-x64-libfuzzer-ubsan-rel-tests/targets/chromium.fuzz.json
@@ -2,8 +2,16 @@ "linux-x64-libfuzzer-ubsan-rel-tests": { "gtest_tests": [ { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, "name": "fuzzing_unittests", - "swarming": {}, + "swarming": { + "dimensions": { + "os": "Ubuntu-22.04" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, "test": "fuzzing_unittests", "test_id_prefix": "ninja://testing/libfuzzer/tests:fuzzing_unittests/" }
diff --git a/infra/config/generated/builders/try/linux-x86-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json b/infra/config/generated/builders/try/linux-x86-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json index 80b2d9e..347e550 100644 --- a/infra/config/generated/builders/try/linux-x86-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json +++ b/infra/config/generated/builders/try/linux-x86-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json
@@ -2,8 +2,16 @@ "linux-x86-libfuzzer-asan-rel-tests": { "gtest_tests": [ { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, "name": "fuzzing_unittests", - "swarming": {}, + "swarming": { + "dimensions": { + "os": "Ubuntu-22.04" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, "test": "fuzzing_unittests", "test_id_prefix": "ninja://testing/libfuzzer/tests:fuzzing_unittests/" }
diff --git a/infra/config/generated/builders/try/mac-arm64-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json b/infra/config/generated/builders/try/mac-arm64-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json index 5f1fb41..25cf81f 100644 --- a/infra/config/generated/builders/try/mac-arm64-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json +++ b/infra/config/generated/builders/try/mac-arm64-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json
@@ -2,8 +2,17 @@ "mac-arm64-libfuzzer-asan-rel-tests": { "gtest_tests": [ { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, "name": "fuzzing_unittests", - "swarming": {}, + "swarming": { + "dimensions": { + "cpu": "arm64", + "os": "Mac-15" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, "test": "fuzzing_unittests", "test_id_prefix": "ninja://testing/libfuzzer/tests:fuzzing_unittests/" }
diff --git a/infra/config/generated/builders/try/win-x64-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json b/infra/config/generated/builders/try/win-x64-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json index f37afd1..d93dfed 100644 --- a/infra/config/generated/builders/try/win-x64-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json +++ b/infra/config/generated/builders/try/win-x64-libfuzzer-asan-rel-tests/targets/chromium.fuzz.json
@@ -2,8 +2,16 @@ "win-x64-libfuzzer-asan-rel-tests": { "gtest_tests": [ { + "merge": { + "script": "//testing/merge_scripts/standard_gtest_merge.py" + }, "name": "fuzzing_unittests", - "swarming": {}, + "swarming": { + "dimensions": { + "os": "Windows-10" + }, + "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com" + }, "test": "fuzzing_unittests", "test_id_prefix": "ninja://testing/libfuzzer/tests:fuzzing_unittests/" }
diff --git a/infra/config/generated/health-specs/health-specs.json b/infra/config/generated/health-specs/health-specs.json index 6373a4c..4aaa6f7 100644 --- a/infra/config/generated/health-specs/health-specs.json +++ b/infra/config/generated/health-specs/health-specs.json
@@ -7721,26 +7721,6 @@ } ] }, - "android-pie-x86-fyi-rel": { - "problem_specs": [ - { - "name": "Unhealthy", - "period_days": 7, - "score": 5, - "thresholds": { - "_default": "_default" - } - }, - { - "name": "Low Value", - "period_days": 90, - "score": 1, - "thresholds": { - "_default": "_default" - } - } - ] - }, "android-rust-arm32-rel": { "problem_specs": [ { @@ -11737,26 +11717,6 @@ } ] }, - "rts-model-packager": { - "problem_specs": [ - { - "name": "Unhealthy", - "period_days": 7, - "score": 5, - "thresholds": { - "_default": "_default" - } - }, - { - "name": "Low Value", - "period_days": 90, - "score": 1, - "thresholds": { - "_default": "_default" - } - } - ] - }, "rts-suite-analysis": { "problem_specs": [ {
diff --git a/infra/config/generated/luci/cr-buildbucket.cfg b/infra/config/generated/luci/cr-buildbucket.cfg index 955dfbb..f1f5616d 100644 --- a/infra/config/generated/luci/cr-buildbucket.cfg +++ b/infra/config/generated/luci/cr-buildbucket.cfg
@@ -43827,116 +43827,6 @@ } } builders { - name: "android-pie-x86-fyi-rel" - swarming_host: "chromium-swarm.appspot.com" - dimensions: "builderless:1" - dimensions: "cores:8" - dimensions: "cpu:x86-64" - dimensions: "free_space:standard" - dimensions: "os:Ubuntu-22.04" - dimensions: "pool:luci.chromium.ci" - dimensions: "ssd:0" - exe { - cipd_package: "infra/chromium/bootstrapper/${platform}" - cipd_version: "latest" - cmd: "bootstrapper" - } - properties: - '{' - ' "$bootstrap/exe": {' - ' "exe": {' - ' "cipd_package": "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build",' - ' "cipd_version": "refs/heads/main",' - ' "cmd": [' - ' "luciexe"' - ' ]' - ' }' - ' },' - ' "$bootstrap/properties": {' - ' "properties_file": "infra/config/generated/builders/ci/android-pie-x86-fyi-rel/properties.json",' - ' "shadow_properties_file": "infra/config/generated/builders/ci/android-pie-x86-fyi-rel/shadow-properties.json",' - ' "top_level_project": {' - ' "ref": "refs/heads/main",' - ' "repo": {' - ' "host": "chromium.googlesource.com",' - ' "project": "chromium/src"' - ' }' - ' }' - ' },' - ' "builder_group": "chromium.android.fyi",' - ' "led_builder_is_bootstrapped": true,' - ' "recipe": "chromium"' - '}' - priority: 35 - execution_timeout_secs: 10800 - build_numbers: YES - service_account: "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com" - experiments { - key: "chromium.use_per_builder_build_dir_name" - value: 100 - } - experiments { - key: "luci.recipes.use_python3" - value: 100 - } - resultdb { - enable: true - bq_exports { - project: "chrome-luci-data" - dataset: "chromium" - table: "ci_test_results" - test_results {} - } - bq_exports { - project: "chrome-luci-data" - dataset: "chromium" - table: "gpu_ci_test_results" - test_results { - predicate { - test_id_regexp: "ninja://(chrome|content)/test:telemetry_gpu_integration_test[^/]*/.+" - } - } - } - bq_exports { - project: "chrome-luci-data" - dataset: "chromium" - table: "blink_web_tests_ci_test_results" - test_results { - predicate { - test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*_wpt_tests/.+)|(ninja://[^/]*headless_shell_wpt/.+)" - } - } - } - history_options { - use_invocation_timestamp: true - } - } - shadow_builder_adjustments { - service_account: "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com" - pool: "luci.chromium.try" - dimensions: "free_space:" - dimensions: "pool:luci.chromium.try" - } - custom_metric_definitions { - name: "/chrome/infra/browser/builds/cached_count" - predicates: "has(build.output.properties.is_cached)" - predicates: "string(build.output.properties.is_cached) == \"true\"" - } - custom_metric_definitions { - name: "/chrome/infra/browser/builds/ran_tests_retry_shard_count" - predicates: "has(build.output.properties.ran_tests_retry_shard)" - } - custom_metric_definitions { - name: "/chrome/infra/browser/builds/ran_tests_without_patch_count" - predicates: "has(build.output.properties.ran_tests_without_patch)" - } - custom_metric_definitions { - name: "/chrome/infra/browser/builds/uncached_count" - predicates: "has(build.output.properties.is_cached)" - predicates: "string(build.output.properties.is_cached) == \"false\"" - } - } - builders { name: "android-rust-arm32-rel" swarming_host: "chromium-swarm.appspot.com" dimensions: "builderless:1" @@ -65799,99 +65689,6 @@ } } builders { - name: "rts-model-packager" - swarming_host: "chromium-swarm.appspot.com" - dimensions: "builder:rts-model-packager" - dimensions: "cpu:x86-64" - dimensions: "os:Ubuntu-22.04" - dimensions: "pool:luci.chromium.ci" - exe { - cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" - cipd_version: "refs/heads/main" - cmd: "luciexe" - } - properties: - '{' - ' "$recipe_engine/resultdb/test_presentation": {' - ' "column_keys": [],' - ' "grouping_keys": [' - ' "status",' - ' "v.test_suite"' - ' ]' - ' },' - ' "builder_group": "chromium.infra",' - ' "recipe": "chromium_rts/create_model"' - '}' - execution_timeout_secs: 36000 - build_numbers: YES - service_account: "chromium-cipd-builder@chops-service-accounts.iam.gserviceaccount.com" - experiments { - key: "chromium.use_per_builder_build_dir_name" - value: 100 - } - experiments { - key: "luci.recipes.use_python3" - value: 100 - } - resultdb { - enable: true - bq_exports { - project: "chrome-luci-data" - dataset: "chromium" - table: "ci_test_results" - test_results {} - } - bq_exports { - project: "chrome-luci-data" - dataset: "chromium" - table: "gpu_ci_test_results" - test_results { - predicate { - test_id_regexp: "ninja://(chrome|content)/test:telemetry_gpu_integration_test[^/]*/.+" - } - } - } - bq_exports { - project: "chrome-luci-data" - dataset: "chromium" - table: "blink_web_tests_ci_test_results" - test_results { - predicate { - test_id_regexp: "(ninja://[^/]*blink_web_tests/.+)|(ninja://[^/]*_wpt_tests/.+)|(ninja://[^/]*headless_shell_wpt/.+)" - } - } - } - history_options { - use_invocation_timestamp: true - } - } - shadow_builder_adjustments { - service_account: "chromium-cipd-try-builder@chops-service-accounts.iam.gserviceaccount.com" - pool: "luci.chromium.try" - dimensions: "builder:" - dimensions: "builderless:1" - dimensions: "pool:luci.chromium.try" - } - custom_metric_definitions { - name: "/chrome/infra/browser/builds/cached_count" - predicates: "has(build.output.properties.is_cached)" - predicates: "string(build.output.properties.is_cached) == \"true\"" - } - custom_metric_definitions { - name: "/chrome/infra/browser/builds/ran_tests_retry_shard_count" - predicates: "has(build.output.properties.ran_tests_retry_shard)" - } - custom_metric_definitions { - name: "/chrome/infra/browser/builds/ran_tests_without_patch_count" - predicates: "has(build.output.properties.ran_tests_without_patch)" - } - custom_metric_definitions { - name: "/chrome/infra/browser/builds/uncached_count" - predicates: "has(build.output.properties.is_cached)" - predicates: "string(build.output.properties.is_cached) == \"false\"" - } - } - builders { name: "rts-suite-analysis" swarming_host: "chromium-swarm.appspot.com" dimensions: "builder:rts-suite-analysis"
diff --git a/infra/config/generated/luci/luci-milo.cfg b/infra/config/generated/luci/luci-milo.cfg index e5461ff..4483cd30 100644 --- a/infra/config/generated/luci/luci-milo.cfg +++ b/infra/config/generated/luci/luci-milo.cfg
@@ -9941,11 +9941,6 @@ short_name: "11" } builders { - name: "buildbucket/luci.chromium.ci/android-pie-x86-fyi-rel" - category: "emulator|x86|rel" - short_name: "P" - } - builders { name: "buildbucket/luci.chromium.ci/android-annotator-rel" category: "network|traffic|annotations" short_name: "and" @@ -18178,11 +18173,6 @@ category: "packager|android" short_name: "sdk" } - builders { - name: "buildbucket/luci.chromium.ci/rts-model-packager" - category: "packager|rts" - short_name: "create-model" - } header { oncalls { name: "Chromium"
diff --git a/infra/config/generated/luci/luci-notify.cfg b/infra/config/generated/luci/luci-notify.cfg index a412bc7..a7265d6 100644 --- a/infra/config/generated/luci/luci-notify.cfg +++ b/infra/config/generated/luci/luci-notify.cfg
@@ -4059,19 +4059,6 @@ } notifiers { notifications { - on_occurrence: FAILURE - on_occurrence: INFRA_FAILURE - email { - recipients: "chrome-dev-infra-auto+alerts@google.com" - } - } - builders { - bucket: "ci" - name: "rts-model-packager" - } -} -notifiers { - notifications { on_new_status: FAILURE email { recipients: "chiav@chromium.org"
diff --git a/infra/config/generated/luci/luci-scheduler.cfg b/infra/config/generated/luci/luci-scheduler.cfg index a5f4ab0..3b05898 100644 --- a/infra/config/generated/luci/luci-scheduler.cfg +++ b/infra/config/generated/luci/luci-scheduler.cfg
@@ -5612,16 +5612,6 @@ } } job { - id: "rts-model-packager" - realm: "ci" - schedule: "0 9 * * *" - buildbucket { - server: "cr-buildbucket.appspot.com" - bucket: "ci" - builder: "rts-model-packager" - } -} -job { id: "rts-suite-analysis" realm: "ci" schedule: "0 9 * * *"
diff --git a/infra/config/lib/builder_exemptions.star b/infra/config/lib/builder_exemptions.star index 408dad3..2a83afea 100644 --- a/infra/config/lib/builder_exemptions.star +++ b/infra/config/lib/builder_exemptions.star
@@ -283,7 +283,6 @@ "mac-swangle-chromium-x64", "mac12-arm64-rel-tests", "mac13-arm64-rel-tests", - "rts-model-packager", "rts-suite-analysis", "win-angle-chromium-x64-builder", "win-angle-chromium-x86-builder", @@ -723,7 +722,6 @@ "mac-rust-x64-dbg", "mac-ubsan-fyi-rel", "metadata-exporter", - "rts-model-packager", "rts-suite-analysis", "win-annotator-rel", "win-build-perf-developer",
diff --git a/infra/config/subprojects/chromium/ci/chromium.android.fyi.star b/infra/config/subprojects/chromium/ci/chromium.android.fyi.star index 06e249b..fb5fec3 100644 --- a/infra/config/subprojects/chromium/ci/chromium.android.fyi.star +++ b/infra/config/subprojects/chromium/ci/chromium.android.fyi.star
@@ -166,200 +166,6 @@ contact_team_email = "chrome-product-engprod@google.com", ) -# TODO(crbug.com/1022533#c40): Remove this builder once there are no associated -# disabled tests. -ci.builder( - name = "android-pie-x86-fyi-rel", - # Set to an empty list to avoid chromium-gitiles-trigger triggering new - # builds. Also we don't set any `schedule` since this builder is for - # reference only and should not run any new builds. - triggered_by = [], - builder_spec = builder_config.builder_spec( - gclient_config = builder_config.gclient_config( - config = "chromium", - apply_configs = [ - "android", - # This is necessary due to this builder running the - # telemetry_perf_unittests suite. - "chromium_with_telemetry_dependencies", - "enable_wpr_tests", - ], - ), - chromium_config = builder_config.chromium_config( - config = "main_builder", - apply_configs = ["mb"], - build_config = builder_config.build_config.RELEASE, - target_arch = builder_config.target_arch.INTEL, - target_bits = 32, - target_platform = builder_config.target_platform.ANDROID, - ), - android_config = builder_config.android_config(config = "base_config"), - build_gs_bucket = "chromium-android-archive", - ), - gn_args = gn_args.config( - configs = [ - "android_builder", - "release_builder", - "remoteexec", - "minimal_symbols", - "x86", - "strip_debug_info", - "android_fastbuild", - "webview_monochrome", - "webview_shell", - ], - ), - targets = targets.bundle( - targets = [ - targets.bundle( - targets = [ - "android_pie_emulator_gtests", - "pie_isolated_scripts", - ], - ), - "chromium_android_scripts", - ], - additional_compile_targets = [ - "chrome_nocompile_tests", - ], - mixins = [ - "has_native_resultdb_integration", - "isolate_profile_data", - "pie-x86-emulator", - "emulator-4-cores", - "linux-jammy", - "x86-64", - ], - per_test_modifications = { - "android_browsertests": targets.mixin( - args = [ - # https://crbug.com/1034001 - "--gtest_filter=-ImportantSitesUtilBrowserTest.DSENotConsideredImportantInRegularMode", - ], - swarming = targets.swarming( - dimensions = { - # crbug.com/1292221 - "cores": "8", - }, - shards = 9, - ), - ), - "android_sync_integration_tests": targets.mixin( - swarming = targets.swarming( - shards = 2, - ), - ), - "cc_unittests": targets.mixin( - args = [ - # https://crbug.com/1039860 - "--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator.cc_unittests.filter", - ], - ), - "chrome_public_test_apk": targets.mixin( - args = [ - # https://crbug.com/1046059 - "--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator_p.chrome_public_test_apk.filter", - ], - swarming = targets.swarming( - dimensions = { - "cores": "8", - }, - # See https://crbug.com/1230192, runs of 40-60 minutes at 20 shards. - shards = 75, - ), - ), - "components_browsertests": targets.mixin( - swarming = targets.swarming( - shards = 4, - ), - ), - "content_browsertests": targets.mixin( - args = [ - "--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator_p.content_browsertests.filter", - ], - swarming = targets.swarming( - dimensions = { - # use 8-core to shorten runtime - "cores": "8", - }, - shards = 75, - ), - ), - # If you change this, make similar changes in android-x86-code-coverage - "content_shell_crash_test": targets.remove( - reason = "crbug.com/1084353", - ), - "content_shell_test_apk": targets.mixin( - args = [ - "--gtest_filter=-org.chromium.content.browser.input.ImeInputModeTest.testShowAndHideInputMode*", - ], - swarming = targets.swarming( - dimensions = { - # use 8-core to shorten runtime - "cores": "8", - }, - shards = 6, - ), - ), - "gl_tests_validating": targets.mixin( - args = [ - "--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator_o_p.gl_tests.filter", - ], - ), - "net_unittests": targets.mixin( - # crbug.com/1046060 - args = [ - "--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator.net_unittests.filter", - ], - ), - "perfetto_unittests": targets.remove( - reason = "TODO(crbug.com/41440830): Fix permission issue when creating tmp files", - ), - "services_unittests": targets.mixin( - args = [ - # TODO(crbug.com/40203477): Fix the failed tests - "--gtest_filter=-PacLibraryTest.ActualPacMyIpAddress*", - ], - swarming = targets.swarming( - shards = 3, - ), - ), - "telemetry_perf_unittests_android_chrome": targets.mixin( - # For whatever reason, automatic browser selection on this bot chooses - # webview instead of the full browser, so explicitly specify it here. - args = [ - "--browser=android-chromium", - ], - ), - "webview_instrumentation_test_apk_multiple_process_mode": targets.mixin( - args = [ - "--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator.webview_instrumentation_test_apk.filter", - ], - swarming = targets.swarming( - # crbug.com/1294924 - shards = 15, - ), - ), - "webview_instrumentation_test_apk_single_process_mode": targets.mixin( - args = [ - "--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator.webview_instrumentation_test_apk.filter", - ], - swarming = targets.swarming( - # crbug.com/1294924 - shards = 9, - ), - ), - }, - ), - targets_settings = targets.settings( - os_type = targets.os_type.ANDROID, - ), - console_view_entry = consoles.console_view_entry( - category = "emulator|x86|rel", - short_name = "P", - ), -) - ci.builder( name = "android-10-x86-fyi-rel", description_html = "Run chromium tests on Android 10 emulators.",
diff --git a/infra/config/subprojects/chromium/ci/chromium.coverage.star b/infra/config/subprojects/chromium/ci/chromium.coverage.star index 9327c12..ddebc8ef 100644 --- a/infra/config/subprojects/chromium/ci/chromium.coverage.star +++ b/infra/config/subprojects/chromium/ci/chromium.coverage.star
@@ -309,7 +309,7 @@ # Keep this same as android-10-x86-rel "chrome_public_test_apk": targets.mixin( args = [ - "--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator_o.chrome_public_test_apk.filter", + "--test-launcher-filter-file=../../testing/buildbot/filters/android.emulator_10.chrome_public_test_apk.filter", ], swarming = targets.swarming( dimensions = {
diff --git a/infra/config/subprojects/chromium/ci/chromium.fuzz.star b/infra/config/subprojects/chromium/ci/chromium.fuzz.star index e663dd3..accebee 100644 --- a/infra/config/subprojects/chromium/ci/chromium.fuzz.star +++ b/infra/config/subprojects/chromium/ci/chromium.fuzz.star
@@ -231,6 +231,7 @@ target_bits = None, target_platform = None, target_arch = None, + swarming_mixins = None, builderless = True, sanitizer = None, branch_selector = None, @@ -322,7 +323,10 @@ # Use the builderless machine pool. builderless = True, # Build and run fuzzing unit tests. - targets = targets.bundle(targets = ["fuzzing_unittests"]), + targets = targets.bundle( + targets = ["fuzzing_unittests"], + mixins = ["chromium-tester-service-account"] + swarming_mixins, + ), # TODO(https://crbug.com/432407787): Add to a gardening rotation # once the bots are proven green enough. gardener_rotations = args.ignore_default(None), @@ -342,6 +346,7 @@ ] + gn_extra_configs return libfuzzer_builder( + swarming_mixins = ["linux-jammy"], target_platform = target_platform, gn_extra_configs = gn_configs, **kwargs @@ -1100,6 +1105,7 @@ cpu = cpu.ARM64, target_arch = builder_config.target_arch.ARM, console_short_name = "mac-arm64-asan", + swarming_mixins = ["mac_15_arm64"], # Even if we don't actively fuzz this build configuration yet, it is useful # to test that things nominally work and do not regress. test_builder_name = "mac-arm64-libfuzzer-asan-rel-tests", @@ -1130,6 +1136,7 @@ max_concurrent_invocations = 3 if settings.is_main else None, sanitizer = "asan", siso_remote_jobs = siso.remote_jobs.HIGH_JOBS_FOR_CI, + swarming_mixins = ["win10-any"], test_builder_name = "win-x64-libfuzzer-asan-rel-tests", use_component_build = False, )
diff --git a/infra/config/subprojects/chromium/ci/chromium.infra.star b/infra/config/subprojects/chromium/ci/chromium.infra.star index a0d7090c..a6a162e1 100644 --- a/infra/config/subprojects/chromium/ci/chromium.infra.star +++ b/infra/config/subprojects/chromium/ci/chromium.infra.star
@@ -357,27 +357,6 @@ }, ) -packager_builder( - name = "rts-model-packager", - executable = "recipe:chromium_rts/create_model", - schedule = "0 9 * * *", # at 1AM or 2AM PT (depending on DST), once a day. - triggered_by = [], - builderless = False, - cores = None, - console_view_entry = consoles.console_view_entry( - category = "packager|rts", - short_name = "create-model", - ), - execution_timeout = 10 * time.hour, - notifies = [ - luci.notifier( - name = "rts-model-packager-notifier", - notify_emails = ["chrome-dev-infra-auto+alerts@google.com"], - on_occurrence = ["FAILURE", "INFRA_FAILURE"], - ), - ], -) - ci.builder( name = "android-device-flasher", executable = "recipe:android/device_flasher",
diff --git a/internal b/internal index bea2012..d517e9b 160000 --- a/internal +++ b/internal
@@ -1 +1 @@ -Subproject commit bea2012bd2ef30585305fbc17db2600b340fcef7 +Subproject commit d517e9b109b54a3441b712813f69b8034e2d4676
diff --git a/ios/chrome/app/BUILD.gn b/ios/chrome/app/BUILD.gn index 3202c59..fadba8b 100644 --- a/ios/chrome/app/BUILD.gn +++ b/ios/chrome/app/BUILD.gn
@@ -431,6 +431,7 @@ "//components/metrics", "//components/optimization_guide/core:features", "//components/password_manager/core/common", + "//components/policy/core/common", "//components/prefs", "//components/prefs/ios", "//components/previous_session_info", @@ -491,6 +492,7 @@ "//ios/chrome/browser/ntp/ui_bundled:feature_flags", "//ios/chrome/browser/omaha/model", "//ios/chrome/browser/passwords/model", + "//ios/chrome/browser/policy/model", "//ios/chrome/browser/promos_manager/model:factory", "//ios/chrome/browser/push_notification/model:push_notification_delegate", "//ios/chrome/browser/push_notification/model:push_notification_service",
diff --git a/ios/chrome/app/main_controller.mm b/ios/chrome/app/main_controller.mm index 9ebb6db..ee0c6f1b 100644 --- a/ios/chrome/app/main_controller.mm +++ b/ios/chrome/app/main_controller.mm
@@ -37,6 +37,7 @@ #import "components/metrics/metrics_service.h" #import "components/password_manager/core/common/password_manager_features.h" #import "components/password_manager/core/common/passwords_directory_util_ios.h" +#import "components/policy/core/common/management/management_service.h" #import "components/prefs/ios/pref_observer_bridge.h" #import "components/prefs/pref_change_registrar.h" #import "components/prefs/scoped_user_pref_update.h" @@ -100,6 +101,7 @@ #import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_feature.h" #import "ios/chrome/browser/omaha/model/omaha_service.h" #import "ios/chrome/browser/passwords/model/password_manager_util_ios.h" +#import "ios/chrome/browser/policy/model/management_service_ios_factory.h" #import "ios/chrome/browser/saved_tab_groups/model/tab_group_sync_service_factory.h" #import "ios/chrome/browser/screenshot/model/screenshot_metrics_recorder.h" #import "ios/chrome/browser/search_engines/model/search_engines_util.h" @@ -1510,11 +1512,9 @@ } - (void)logIfEnterpriseManagedDevice { - NSString* managedKey = @"com.apple.configuration.managed"; - BOOL isManagedDevice = [[NSUserDefaults standardUserDefaults] - dictionaryForKey:managedKey] != nil; - - base::UmaHistogramBoolean("EnterpriseCheck.IsManaged2", isManagedDevice); + base::UmaHistogramBoolean( + "EnterpriseCheck.IsManaged2", + policy::ManagementServiceIOSFactory::GetForPlatform()->IsManaged()); } - (void)startFreeMemoryMonitoring {
diff --git a/ios/chrome/app/strings/ios_chromium_strings.grd b/ios/chrome/app/strings/ios_chromium_strings.grd index 649c7cd..560b652 100644 --- a/ios/chrome/app/strings/ios_chromium_strings.grd +++ b/ios/chrome/app/strings/ios_chromium_strings.grd
@@ -1,4 +1,4 @@ -<?xml version='1.0' encoding='UTF-8'?> +<?xml version='1.0' encoding='utf-8'?> <grit base_dir="." latest_public_release="0" current_release="1" source_lang_id="en" enc_check="möl"> <!-- This file contains definition of resources that will be translated for each @@ -339,6 +339,9 @@ <message name="IDS_IOS_DEFAULT_BROWSER_VIDEO_PROMO_APP_SETTINGS" desc="Chromium version of Text in the default browser video the app settings. [iOS only]"> Chromium settings </message> + <message name="IDS_IOS_DEFAULT_BROWSER_VIDEO_PROMO_DEFAULT_APPS_SUBTITLE_TEXT" desc="Subtitle for the default browser video promo. 'Default Apps' must use the same translation that iOS uses for the 'Default Apps' item in the iOS settings. [iOS only]"> + Open Default Apps settings in iOS, then tap on "Browser App" and select Chromium. + </message> <message name="IDS_IOS_DEFAULT_BROWSER_VIDEO_PROMO_SUBTITLE_TEXT" desc="Chromium version of the subtitle for the default browser video promo. [iOS only]"> Open Chromium settings in iOS, then tap on "Default Browser App" and select Chromium. </message>
diff --git a/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_DEFAULT_BROWSER_VIDEO_PROMO_DEFAULT_APPS_SUBTITLE_TEXT.png.sha1 b/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_DEFAULT_BROWSER_VIDEO_PROMO_DEFAULT_APPS_SUBTITLE_TEXT.png.sha1 new file mode 100644 index 0000000..aea5101 --- /dev/null +++ b/ios/chrome/app/strings/ios_chromium_strings_grd/IDS_IOS_DEFAULT_BROWSER_VIDEO_PROMO_DEFAULT_APPS_SUBTITLE_TEXT.png.sha1
@@ -0,0 +1 @@ +5d985ace177d2414be24830070c02468dc42f650 \ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_google_chrome_strings.grd b/ios/chrome/app/strings/ios_google_chrome_strings.grd index a537e27c..a9c5144 100644 --- a/ios/chrome/app/strings/ios_google_chrome_strings.grd +++ b/ios/chrome/app/strings/ios_google_chrome_strings.grd
@@ -1,4 +1,4 @@ -<?xml version='1.0' encoding='UTF-8'?> +<?xml version='1.0' encoding='utf-8'?> <grit base_dir="." latest_public_release="0" current_release="1" source_lang_id="en" enc_check="möl"> <!-- This file contains definition of resources that will be translated for each @@ -339,6 +339,9 @@ <message name="IDS_IOS_DEFAULT_BROWSER_VIDEO_PROMO_APP_SETTINGS" desc="Text in the default browser video the app settings. [iOS only]"> Chrome settings </message> + <message name="IDS_IOS_DEFAULT_BROWSER_VIDEO_PROMO_DEFAULT_APPS_SUBTITLE_TEXT" desc="Subtitle for the default browser video promo. 'Default Apps' must use the same translation that iOS uses for the 'Default Apps' item in the iOS settings. [iOS only]"> + Open Default Apps settings in iOS, then tap on "Browser App" and select Chrome. + </message> <message name="IDS_IOS_DEFAULT_BROWSER_VIDEO_PROMO_SUBTITLE_TEXT" desc="Subtitle for the default browser video promo. [iOS only]"> Open Chrome settings in iOS, then tap on "Default Browser App" and select Chrome. </message>
diff --git a/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_DEFAULT_BROWSER_VIDEO_PROMO_DEFAULT_APPS_SUBTITLE_TEXT.png.sha1 b/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_DEFAULT_BROWSER_VIDEO_PROMO_DEFAULT_APPS_SUBTITLE_TEXT.png.sha1 new file mode 100644 index 0000000..5319653 --- /dev/null +++ b/ios/chrome/app/strings/ios_google_chrome_strings_grd/IDS_IOS_DEFAULT_BROWSER_VIDEO_PROMO_DEFAULT_APPS_SUBTITLE_TEXT.png.sha1
@@ -0,0 +1 @@ +0001587548cb2cf63751a7ac9dfd50cb45a1ea24 \ No newline at end of file
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd index 1b66f5a..da8a1a3 100644 --- a/ios/chrome/app/strings/ios_strings.grd +++ b/ios/chrome/app/strings/ios_strings.grd
@@ -1838,6 +1838,9 @@ <message name="IDS_IOS_DEFAULT_BROWSER_TAILORED_BUILT_FOR_IPADOS_TITLE" desc="Text of title for 'Built for iPadOS' tailored default browser promo [iPadOS only]"> Built for iPadOS </message> + <message name="IDS_IOS_DEFAULT_BROWSER_VIDEO_PROMO_BROWSER_APP" desc="Text in the default browser promo animation mimicking the iOS Default Apps settings page. [iOS only]" meaning="The specific Browser App string appearing in iOS Default Apps settings"> + Browser app + </message> <message name="IDS_IOS_DEFAULT_BROWSER_VIDEO_PROMO_DEFAULT_BROWSER_APP" desc="Text in the default browser video promo indicating the default browser app in the chrome settings to set a default browser. [iOS only]"> Default browser app </message>
diff --git a/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_DEFAULT_BROWSER_VIDEO_PROMO_BROWSER_APP.png.sha1 b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_DEFAULT_BROWSER_VIDEO_PROMO_BROWSER_APP.png.sha1 new file mode 100644 index 0000000..4401e6f --- /dev/null +++ b/ios/chrome/app/strings/ios_strings_grd/IDS_IOS_DEFAULT_BROWSER_VIDEO_PROMO_BROWSER_APP.png.sha1
@@ -0,0 +1 @@ +98ed7c5a7402e6a40e1128d948f422090d426155 \ No newline at end of file
diff --git a/ios/chrome/browser/download/model/auto_deletion/BUILD.gn b/ios/chrome/browser/download/model/auto_deletion/BUILD.gn index 7fd4c339..240cc1a5 100644 --- a/ios/chrome/browser/download/model/auto_deletion/BUILD.gn +++ b/ios/chrome/browser/download/model/auto_deletion/BUILD.gn
@@ -1,5 +1,7 @@ source_set("auto_deletion") { sources = [ + "auto_deletion_histograms.h", + "auto_deletion_histograms.mm", "auto_deletion_service.h", "auto_deletion_service.mm", "scheduled_file.h",
diff --git a/ios/chrome/browser/download/model/auto_deletion/auto_deletion_histograms.h b/ios/chrome/browser/download/model/auto_deletion/auto_deletion_histograms.h new file mode 100644 index 0000000..5fed958 --- /dev/null +++ b/ios/chrome/browser/download/model/auto_deletion/auto_deletion_histograms.h
@@ -0,0 +1,22 @@ +// Copyright 2025 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_DOWNLOAD_MODEL_AUTO_DELETION_AUTO_DELETION_HISTOGRAMS_H_ +#define IOS_CHROME_BROWSER_DOWNLOAD_MODEL_AUTO_DELETION_AUTO_DELETION_HISTOGRAMS_H_ + +// UMA histogram names. +extern const char kAutoDeletionServiceActionsHistogram[]; + +// Enum for the IOS.AutoDeletion.ServiceActions histogram. Keep in sync with +// "AutoDeletionServiceActionsType" in tools/metrics/histograms/enums.xml +// LINT.IfChange +enum class AutoDeletionServiceActions { + kFileScheduledForAutoDeletion = 0, + kScheduledFileIdentifiedForRemoval = 1, + kScheduledFileRemovedFromDevice = 2, + kMaxValue = kScheduledFileRemovedFromDevice, +}; +// LINT.ThenChange(/tools/metrics/histograms/enums.xml) + +#endif // IOS_CHROME_BROWSER_DOWNLOAD_MODEL_AUTO_DELETION_AUTO_DELETION_HISTOGRAMS_H_
diff --git a/ios/chrome/browser/download/model/auto_deletion/auto_deletion_histograms.mm b/ios/chrome/browser/download/model/auto_deletion/auto_deletion_histograms.mm new file mode 100644 index 0000000..a89dc53 --- /dev/null +++ b/ios/chrome/browser/download/model/auto_deletion/auto_deletion_histograms.mm
@@ -0,0 +1,8 @@ +// Copyright 2025 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/download/model/auto_deletion/auto_deletion_histograms.h" + +const char kAutoDeletionServiceActionsHistogram[] = + "IOS.AutoDeletion.ServiceActions";
diff --git a/ios/chrome/browser/download/model/auto_deletion/auto_deletion_service.mm b/ios/chrome/browser/download/model/auto_deletion/auto_deletion_service.mm index 13a8275..90f6e36 100644 --- a/ios/chrome/browser/download/model/auto_deletion/auto_deletion_service.mm +++ b/ios/chrome/browser/download/model/auto_deletion/auto_deletion_service.mm
@@ -9,6 +9,7 @@ #import "base/files/file_util.h" #import "base/functional/bind.h" #import "base/hash/md5.h" +#import "base/metrics/histogram_functions.h" #import "base/strings/sys_string_conversions.h" #import "base/task/task_traits.h" #import "base/task/thread_pool.h" @@ -17,6 +18,7 @@ #import "components/prefs/pref_registry_simple.h" #import "components/prefs/pref_service.h" #import "components/prefs/scoped_user_pref_update.h" +#import "ios/chrome/browser/download/model/auto_deletion/auto_deletion_histograms.h" #import "ios/chrome/browser/download/model/auto_deletion/scheduled_file.h" #import "ios/chrome/browser/shared/model/application_context/application_context.h" #import "ios/chrome/browser/shared/model/prefs/pref_names.h" @@ -42,6 +44,9 @@ // Delete the files from the file system. std::string buffer; for (const auto& file : files_to_delete) { + base::UmaHistogramEnumeration( + kAutoDeletionServiceActionsHistogram, + AutoDeletionServiceActions::kScheduledFileIdentifiedForRemoval); NSString* filename = base::apple::FilePathToNSString(file.filepath().BaseName()); NSFileManager* manager = [NSFileManager defaultManager]; @@ -53,12 +58,25 @@ NSString* path = URL.absoluteURL.path; if (![manager fileExistsAtPath:path]) { + // TODO(crbug.com/433728890): Log failure type to histogram. continue; } + const std::string hash = HashDownloadData(base::as_byte_span(buffer)); - if (hash == file.hash()) { - [manager removeItemAtPath:path error:nil]; + if (hash != file.hash()) { + // TODO(crbug.com/433728890): Log failure type to histogram. + return; } + + NSError* error; + [manager removeItemAtPath:path error:&error]; + if (error) { + // TODO(crbug.com/433728890): Log failure type to histogram. + return; + } + base::UmaHistogramEnumeration( + kAutoDeletionServiceActionsHistogram, + AutoDeletionServiceActions::kScheduledFileRemovedFromDevice); } } @@ -146,6 +164,9 @@ HashDownloadData(base::apple::NSDataToSpan(details.file_content)), base::Time::Now()); scheduler_.ScheduleFile(file); + base::UmaHistogramEnumeration( + kAutoDeletionServiceActionsHistogram, + AutoDeletionServiceActions::kFileScheduledForAutoDeletion); } tasks_awaiting_scheduling_.erase(iterator); }
diff --git a/ios/chrome/browser/intelligence/bwg/model/bwg_configuration.h b/ios/chrome/browser/intelligence/bwg/model/bwg_configuration.h index 25def43e..0d82d4a 100644 --- a/ios/chrome/browser/intelligence/bwg/model/bwg_configuration.h +++ b/ios/chrome/browser/intelligence/bwg/model/bwg_configuration.h
@@ -16,6 +16,8 @@ namespace ios::provider { enum class BWGLocationPermissionState; enum class BWGPageContextState; +enum class BWGPageContextComputationState; +enum class BWGPageContextAttachmentState; } // namespace ios::provider namespace optimization_guide::proto { @@ -41,10 +43,19 @@ @property(nonatomic, assign) ios::provider::BWGLocationPermissionState BWGLocationPermissionState; +// TODO(crbug.com/434662294): Remove when migration is complete. // The state of the BWG PageContext. @property(nonatomic, assign) ios::provider::BWGPageContextState BWGPageContextState; +// The state of the BWG PageContext computation. +@property(nonatomic, assign) ios::provider::BWGPageContextComputationState + BWGPageContextComputationState; + +// The state of the BWG PageContext attachment. +@property(nonatomic, assign) + ios::provider::BWGPageContextAttachmentState BWGPageContextAttachmentState; + // The favicon of the attached page. Uses a default icon if it's unavailable. @property(nonatomic, strong) UIImage* favicon;
diff --git a/ios/chrome/browser/intelligence/page_action_menu/ui/page_action_menu_view_controller.mm b/ios/chrome/browser/intelligence/page_action_menu/ui/page_action_menu_view_controller.mm index e7c103c..bcf4a1f 100644 --- a/ios/chrome/browser/intelligence/page_action_menu/ui/page_action_menu_view_controller.mm +++ b/ios/chrome/browser/intelligence/page_action_menu/ui/page_action_menu_view_controller.mm
@@ -100,7 +100,7 @@ // Add blurred background. UIBlurEffect* blurEffect = - [UIBlurEffect effectWithStyle:UIBlurEffectStyleRegular]; + [UIBlurEffect effectWithStyle:UIBlurEffectStyleSystemThickMaterial]; UIVisualEffectView* blurEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect]; blurEffectView.translatesAutoresizingMaskIntoConstraints = NO;
diff --git a/ios/chrome/browser/lens_overlay/coordinator/fake_chrome_lens_overlay.mm b/ios/chrome/browser/lens_overlay/coordinator/fake_chrome_lens_overlay.mm index eac841a..881ee17 100644 --- a/ios/chrome/browser/lens_overlay/coordinator/fake_chrome_lens_overlay.mm +++ b/ios/chrome/browser/lens_overlay/coordinator/fake_chrome_lens_overlay.mm
@@ -76,6 +76,14 @@ // NO-OP } +- (void)setHUDViewHidden:(BOOL)hidden { + // NO-OP +} + +- (void)setGuidanceViewHidden:(BOOL)hidden { + // NO-OP +} + - (void)disableFlyoutMenu:(BOOL)disable { // NO-OP }
diff --git a/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.mm b/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.mm index 28d7fc100..5f7feb41 100644 --- a/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.mm +++ b/ios/chrome/browser/lens_overlay/coordinator/lens_overlay_coordinator.mm
@@ -440,6 +440,11 @@ [_selectionViewController updateGuidanceViewVisibility:NO animated:YES]; } + if (_entrypoint == LensOverlayEntrypoint::kFREPromo) { + [_selectionViewController setHUDViewHidden:YES]; + [_selectionViewController setGuidanceViewHidden:YES]; + } + [_metricsRecorder setLensOverlayInForeground:YES]; [self showRestorationWindowIfNeeded]; @@ -1552,6 +1557,10 @@ return; } + if (_entrypoint == LensOverlayEntrypoint::kFREPromo) { + [_selectionViewController setHUDViewHidden:NO]; + } + __weak __typeof(self) weakSelf = self; SheetDimensionState restoredSheetState =
diff --git a/ios/chrome/browser/optimization_guide/model/optimization_guide_service.mm b/ios/chrome/browser/optimization_guide/model/optimization_guide_service.mm index 4cf22d3..8320a15 100644 --- a/ios/chrome/browser/optimization_guide/model/optimization_guide_service.mm +++ b/ios/chrome/browser/optimization_guide/model/optimization_guide_service.mm
@@ -5,7 +5,6 @@ #import "ios/chrome/browser/optimization_guide/model/optimization_guide_service.h" #import "base/apple/bundle_locations.h" -#import "base/files/file_util.h" #import "base/functional/bind.h" #import "base/functional/callback.h" #import "base/metrics/histogram_functions.h" @@ -22,7 +21,6 @@ #import "components/optimization_guide/core/hints/top_host_provider.h" #import "components/optimization_guide/core/model_execution/model_execution_manager.h" #import "components/optimization_guide/core/model_execution/on_device_model_service_controller.h" -#import "components/optimization_guide/core/optimization_guide_constants.h" #import "components/optimization_guide/core/optimization_guide_features.h" #import "components/optimization_guide/core/optimization_guide_logger.h" #import "components/optimization_guide/core/optimization_guide_util.h" @@ -56,18 +54,6 @@ using ::optimization_guide::OnDeviceModelComponentStateManager; #endif // BUILDFLAG(BUILD_WITH_INTERNAL_OPTIMIZATION_GUIDE) -// Deletes old store paths that were written in incorrect locations. -void DeleteOldStorePaths(const base::FilePath& profile_path) { - // Added 11/2023 - // - // Delete the old profile-wide model download store path, since - // the install-wide model store is enabled now. - base::ThreadPool::PostTask( - FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT}, - base::GetDeletePathRecursivelyCallback(profile_path.Append( - optimization_guide::kOldOptimizationGuidePredictionModelDownloads))); -} - #if BUILDFLAG(BUILD_WITH_INTERNAL_OPTIMIZATION_GUIDE) class OnDeviceModelComponentStateManagerDelegate : public OnDeviceModelComponentStateManager::Delegate { @@ -206,14 +192,6 @@ #endif // BUILDFLAG(BUILD_WITH_INTERNAL_OPTIMIZATION_GUIDE) } - // Some previous paths were written in incorrect locations. Delete the - // old paths. - // - // TODO(crbug.com/40842340): Remove this code in 05/2023 since it should be - // assumed that all clients that had the previous path have had their previous - // stores deleted. - DeleteOldStorePaths(profile_path); - OPTIMIZATION_GUIDE_LOG( optimization_guide_common::mojom::LogSource::SERVICE_AND_SETTINGS, optimization_guide_logger_,
diff --git a/ios/chrome/browser/policy/model/management_service_ios_factory.h b/ios/chrome/browser/policy/model/management_service_ios_factory.h index 7b05e82e..bd8f6652 100644 --- a/ios/chrome/browser/policy/model/management_service_ios_factory.h +++ b/ios/chrome/browser/policy/model/management_service_ios_factory.h
@@ -13,11 +13,13 @@ namespace policy { class ManagementServiceIOS; +class ManagementService; class ManagementServiceIOSFactory : public ProfileKeyedServiceFactoryIOS { public: static ManagementServiceIOSFactory* GetInstance(); static ManagementServiceIOS* GetForProfile(ProfileIOS* profile); + static ManagementService* GetForPlatform(); private: friend class base::NoDestructor<ManagementServiceIOSFactory>;
diff --git a/ios/chrome/browser/policy/model/management_service_ios_factory.mm b/ios/chrome/browser/policy/model/management_service_ios_factory.mm index c8402fe..c3bd5f8 100644 --- a/ios/chrome/browser/policy/model/management_service_ios_factory.mm +++ b/ios/chrome/browser/policy/model/management_service_ios_factory.mm
@@ -4,6 +4,7 @@ #import "ios/chrome/browser/policy/model/management_service_ios_factory.h" +#import "components/policy/core/common/management/platform_management_service.h" #import "ios/chrome/browser/policy/model/management_service_ios.h" #import "ios/chrome/browser/shared/model/profile/profile_ios.h" @@ -22,6 +23,11 @@ profile, /*create=*/true); } +// static +ManagementService* ManagementServiceIOSFactory::GetForPlatform() { + return PlatformManagementService::GetInstance(); +} + ManagementServiceIOSFactory::ManagementServiceIOSFactory() : ProfileKeyedServiceFactoryIOS("ManagementServiceIOS", ProfileSelection::kOwnInstanceInIncognito) {
diff --git a/ios/chrome/browser/policy/model/policy_util.h b/ios/chrome/browser/policy/model/policy_util.h index 7293e31d..daf758c 100644 --- a/ios/chrome/browser/policy/model/policy_util.h +++ b/ios/chrome/browser/policy/model/policy_util.h
@@ -36,10 +36,6 @@ // policy data in the App Configuration from the platform. bool HasPlatformPolicies(); -// Returns whether the application is managed through MDM. This -// checks the key set in the NSUserDefaults by iOS. -bool IsApplicationManagedByMDM(); - // Returns true if IncognitoModeAvailability policy is set by enterprise or // custodian. bool IsIncognitoPolicyApplied(PrefService* pref_service);
diff --git a/ios/chrome/browser/policy/model/policy_util.mm b/ios/chrome/browser/policy/model/policy_util.mm index b021465e..28946da 100644 --- a/ios/chrome/browser/policy/model/policy_util.mm +++ b/ios/chrome/browser/policy/model/policy_util.mm
@@ -4,6 +4,8 @@ #import "ios/chrome/browser/policy/model/policy_util.h" +#import <Foundation/Foundation.h> + #import "components/policy/core/common/policy_loader_ios_constants.h" #import "components/policy/core/common/policy_pref_names.h" #import "components/prefs/pref_service.h" @@ -13,11 +15,6 @@ dictionaryForKey:kPolicyLoaderIOSConfigurationKey] count] > 0; } -bool IsApplicationManagedByMDM() { - return [[NSUserDefaults standardUserDefaults] - dictionaryForKey:kPolicyLoaderIOSConfigurationKey] != nil; -} - bool IsIncognitoPolicyApplied(PrefService* pref_service) { if (!pref_service) { return NO;
diff --git a/ios/chrome/browser/policy/model/policy_util_unittest.mm b/ios/chrome/browser/policy/model/policy_util_unittest.mm index 08d1352..9218042 100644 --- a/ios/chrome/browser/policy/model/policy_util_unittest.mm +++ b/ios/chrome/browser/policy/model/policy_util_unittest.mm
@@ -4,6 +4,7 @@ #import "ios/chrome/browser/policy/model/policy_util.h" +#import "components/policy/core/common/management/platform_management_service.h" #import "components/policy/core/common/policy_loader_ios_constants.h" #import "testing/platform_test.h" @@ -14,7 +15,7 @@ TEST_F(PolicyUtilTest, ReturnsFalseWhenNoApplicationConfigFromPlatform) { NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults]; [userDefaults removeObjectForKey:kPolicyLoaderIOSConfigurationKey]; - EXPECT_FALSE(IsApplicationManagedByMDM()); + EXPECT_FALSE(policy::PlatformManagementService::GetInstance()->IsManaged()); EXPECT_FALSE(HasPlatformPolicies()); } @@ -23,7 +24,7 @@ TEST_F(PolicyUtilTest, ReturnsFalseWhenEmptyApplicationConfigFromPlatform) { NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults]; [userDefaults setObject:@{} forKey:kPolicyLoaderIOSConfigurationKey]; - EXPECT_TRUE(IsApplicationManagedByMDM()); + EXPECT_TRUE(policy::PlatformManagementService::GetInstance()->IsManaged()); EXPECT_FALSE(HasPlatformPolicies()); [userDefaults removeObjectForKey:kPolicyLoaderIOSConfigurationKey]; } @@ -34,7 +35,7 @@ NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults]; NSDictionary* dict = @{@"key" : @"value"}; [userDefaults setObject:dict forKey:kPolicyLoaderIOSConfigurationKey]; - EXPECT_TRUE(IsApplicationManagedByMDM()); + EXPECT_TRUE(policy::PlatformManagementService::GetInstance()->IsManaged()); EXPECT_TRUE(HasPlatformPolicies()); [userDefaults removeObjectForKey:kPolicyLoaderIOSConfigurationKey]; }
diff --git a/ios/chrome/browser/reader_mode/coordinator/BUILD.gn b/ios/chrome/browser/reader_mode/coordinator/BUILD.gn index 59b7c8c..1a9d534 100644 --- a/ios/chrome/browser/reader_mode/coordinator/BUILD.gn +++ b/ios/chrome/browser/reader_mode/coordinator/BUILD.gn
@@ -11,6 +11,7 @@ ] deps = [ ":options", + "//ios/chrome/browser/dom_distiller/model", "//ios/chrome/browser/intelligence/bwg/model:bwg_service", "//ios/chrome/browser/intelligence/bwg/model:bwg_service_factory", "//ios/chrome/browser/intelligence/bwg/model:tab_helper", @@ -65,3 +66,21 @@ "//ios/chrome/browser/shared/public/commands", ] } + +source_set("unit_tests") { + testonly = true + sources = [ "reader_mode_mediator_unittest.mm" ] + deps = [ + ":coordinator", + "//base/test:test_support", + "//components/dom_distiller/core", + "//components/dom_distiller/core/mojom", + "//ios/chrome/browser/dom_distiller/model", + "//ios/chrome/browser/intelligence/bwg/model:bwg_service", + "//ios/chrome/browser/intelligence/bwg/model:bwg_service_factory", + "//ios/chrome/browser/shared/model/browser/test:test_support", + "//ios/chrome/browser/shared/model/profile/test", + "//ios/web/public/test", + "//testing/gtest", + ] +}
diff --git a/ios/chrome/browser/reader_mode/coordinator/reader_mode_coordinator.mm b/ios/chrome/browser/reader_mode/coordinator/reader_mode_coordinator.mm index 37f7956..99aad65 100644 --- a/ios/chrome/browser/reader_mode/coordinator/reader_mode_coordinator.mm +++ b/ios/chrome/browser/reader_mode/coordinator/reader_mode_coordinator.mm
@@ -4,6 +4,9 @@ #import "ios/chrome/browser/reader_mode/coordinator/reader_mode_coordinator.h" +#import "components/dom_distiller/core/distilled_page_prefs.h" +#import "ios/chrome/browser/dom_distiller/model/distiller_service.h" +#import "ios/chrome/browser/dom_distiller/model/distiller_service_factory.h" #import "ios/chrome/browser/intelligence/bwg/model/bwg_service.h" #import "ios/chrome/browser/intelligence/bwg/model/bwg_service_factory.h" #import "ios/chrome/browser/reader_mode/coordinator/reader_mode_mediator.h" @@ -35,10 +38,17 @@ _viewController = [[ReaderModeViewController alloc] init]; ProfileIOS* profile = self.browser->GetProfile(); BwgService* BWGService = BwgServiceFactory::GetForProfile(profile); + DistillerService* distiller_service = + DistillerServiceFactory::GetForProfile(self.browser->GetProfile()); + dom_distiller::DistilledPagePrefs* distilledPagePrefs = + distiller_service ? distiller_service->GetDistilledPagePrefs() : nullptr; _mediator = [[ReaderModeMediator alloc] initWithWebStateList:self.browser->GetWebStateList() - BWGService:BWGService]; + BWGService:BWGService + distilledPagePrefs:distilledPagePrefs]; _mediator.consumer = _viewController; + _viewController.mutator = _mediator; + [self.baseViewController addChildViewController:_viewController]; [_viewController moveToParentViewController:self.baseViewController animated:animated]; // Start handling Reader mode options commands.
diff --git a/ios/chrome/browser/reader_mode/coordinator/reader_mode_mediator.h b/ios/chrome/browser/reader_mode/coordinator/reader_mode_mediator.h index 0ac4756b..a39315e 100644 --- a/ios/chrome/browser/reader_mode/coordinator/reader_mode_mediator.h +++ b/ios/chrome/browser/reader_mode/coordinator/reader_mode_mediator.h
@@ -8,19 +8,27 @@ #import <Foundation/Foundation.h> #import "base/memory/raw_ptr.h" +#import "ios/chrome/browser/reader_mode/ui/reader_mode_mutator.h" #import "ios/chrome/browser/shared/model/web_state_list/web_state_list.h" class BwgService; @protocol ReaderModeConsumer; +namespace dom_distiller { +class DistilledPagePrefs; +} // Mediator for the Reader mode UI. -@interface ReaderModeMediator : NSObject +@interface ReaderModeMediator : NSObject <ReaderModeMutator> @property(nonatomic, weak) id<ReaderModeConsumer> consumer; +@property(nonatomic, readonly) + dom_distiller::DistilledPagePrefs* distilledPagePrefs; // Designated initializer. `webStateList` must not be null. - (instancetype)initWithWebStateList:(WebStateList*)webStateList BWGService:(BwgService*)BWGService + distilledPagePrefs: + (dom_distiller::DistilledPagePrefs*)distilledPagePrefs NS_DESIGNATED_INITIALIZER; - (instancetype)init NS_UNAVAILABLE;
diff --git a/ios/chrome/browser/reader_mode/coordinator/reader_mode_mediator.mm b/ios/chrome/browser/reader_mode/coordinator/reader_mode_mediator.mm index 02fac48..23eb6347 100644 --- a/ios/chrome/browser/reader_mode/coordinator/reader_mode_mediator.mm +++ b/ios/chrome/browser/reader_mode/coordinator/reader_mode_mediator.mm
@@ -4,10 +4,13 @@ #import "ios/chrome/browser/reader_mode/coordinator/reader_mode_mediator.h" +#import "components/dom_distiller/core/distilled_page_prefs.h" #import "ios/chrome/browser/intelligence/bwg/model/bwg_service.h" #import "ios/chrome/browser/intelligence/bwg/model/bwg_tab_helper.h" #import "ios/chrome/browser/reader_mode/model/reader_mode_tab_helper.h" #import "ios/chrome/browser/reader_mode/ui/reader_mode_consumer.h" +#import "ios/chrome/browser/shared/model/browser/browser.h" +#import "ios/chrome/browser/shared/model/profile/profile_ios.h" #import "ios/chrome/browser/shared/model/web_state_list/web_state_list_observer_bridge.h" #import "ios/web/public/web_state.h" @@ -17,18 +20,22 @@ @implementation ReaderModeMediator { raw_ptr<WebStateList> _webStateList; raw_ptr<BwgService> _BWGService; + raw_ptr<dom_distiller::DistilledPagePrefs> _distilledPagePrefs; std::unique_ptr<WebStateListObserverBridge> _webStateListObserverBridge; } #pragma mark - Initialization - (instancetype)initWithWebStateList:(WebStateList*)webStateList - BWGService:(BwgService*)BWGService { + BWGService:(BwgService*)BWGService + distilledPagePrefs: + (dom_distiller::DistilledPagePrefs*)distilledPagePrefs { self = [super init]; if (self) { CHECK(webStateList); _webStateList = webStateList; _BWGService = BWGService; + _distilledPagePrefs = distilledPagePrefs; _webStateListObserverBridge = std::make_unique<WebStateListObserverBridge>(self); _webStateList->AddObserver(_webStateListObserverBridge.get()); @@ -38,6 +45,18 @@ #pragma mark - Properties +- (dom_distiller::DistilledPagePrefs*)distilledPagePrefs { + return _distilledPagePrefs; +} + +#pragma mark - ReaderModeMutator + +- (void)setDefaultTheme:(dom_distiller::mojom::Theme)theme { + if (_distilledPagePrefs) { + _distilledPagePrefs->SetDefaultTheme(theme); + } +} + - (void)setConsumer:(id<ReaderModeConsumer>)consumer { CHECK(consumer); _consumer = consumer;
diff --git a/ios/chrome/browser/reader_mode/coordinator/reader_mode_mediator_unittest.mm b/ios/chrome/browser/reader_mode/coordinator/reader_mode_mediator_unittest.mm new file mode 100644 index 0000000..067a2c9 --- /dev/null +++ b/ios/chrome/browser/reader_mode/coordinator/reader_mode_mediator_unittest.mm
@@ -0,0 +1,50 @@ +// Copyright 2025 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/reader_mode/coordinator/reader_mode_mediator.h" + +#import "base/test/task_environment.h" +#import "components/dom_distiller/core/distilled_page_prefs.h" +#import "components/dom_distiller/core/mojom/distilled_page_prefs.mojom.h" +#import "ios/chrome/browser/dom_distiller/model/distiller_service.h" +#import "ios/chrome/browser/dom_distiller/model/distiller_service_factory.h" +#import "ios/chrome/browser/intelligence/bwg/model/bwg_service.h" +#import "ios/chrome/browser/intelligence/bwg/model/bwg_service_factory.h" +#import "ios/chrome/browser/shared/model/browser/test/test_browser.h" +#import "ios/chrome/browser/shared/model/profile/test/test_profile_ios.h" +#import "ios/web/public/test/web_task_environment.h" +#import "testing/gtest/include/gtest/gtest.h" +#import "testing/platform_test.h" + +class ReaderModeMediatorTest : public PlatformTest { + public: + ReaderModeMediatorTest() { + profile_ = TestProfileIOS::Builder().Build(); + browser_ = std::make_unique<TestBrowser>(profile_.get()); + DistillerService* distiller_service = + DistillerServiceFactory::GetForProfile(profile_.get()); + mediator_ = [[ReaderModeMediator alloc] + initWithWebStateList:browser_->GetWebStateList() + BWGService:BwgServiceFactory::GetForProfile(profile_.get()) + distilledPagePrefs:distiller_service->GetDistilledPagePrefs()]; + } + + void TearDown() override { [mediator_ disconnect]; } + + protected: + web::WebTaskEnvironment task_environment_; + std::unique_ptr<TestProfileIOS> profile_; + std::unique_ptr<TestBrowser> browser_; + ReaderModeMediator* mediator_; +}; + +TEST_F(ReaderModeMediatorTest, SetDefaultTheme) { + [mediator_ setDefaultTheme:dom_distiller::mojom::Theme::kDark]; + EXPECT_EQ(mediator_.distilledPagePrefs->GetTheme(), + dom_distiller::mojom::Theme::kDark); + + [mediator_ setDefaultTheme:dom_distiller::mojom::Theme::kLight]; + EXPECT_EQ(mediator_.distilledPagePrefs->GetTheme(), + dom_distiller::mojom::Theme::kLight); +}
diff --git a/ios/chrome/browser/reader_mode/ui/BUILD.gn b/ios/chrome/browser/reader_mode/ui/BUILD.gn index b71a120..7f802659 100644 --- a/ios/chrome/browser/reader_mode/ui/BUILD.gn +++ b/ios/chrome/browser/reader_mode/ui/BUILD.gn
@@ -5,11 +5,13 @@ source_set("ui") { sources = [ "reader_mode_consumer.h", + "reader_mode_mutator.h", "reader_mode_view_controller.h", "reader_mode_view_controller.mm", ] deps = [ ":constants", + "//components/dom_distiller/core/mojom", "//ios/chrome/app/strings:ios_strings_grit", "//ios/chrome/browser/shared/ui/util", "//ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid:tabs_closure_animation",
diff --git a/ios/chrome/browser/reader_mode/ui/reader_mode_mutator.h b/ios/chrome/browser/reader_mode/ui/reader_mode_mutator.h new file mode 100644 index 0000000..821e5a5 --- /dev/null +++ b/ios/chrome/browser/reader_mode/ui/reader_mode_mutator.h
@@ -0,0 +1,22 @@ +// Copyright 2025 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_READER_MODE_UI_READER_MODE_MUTATOR_H_ +#define IOS_CHROME_BROWSER_READER_MODE_UI_READER_MODE_MUTATOR_H_ + +#import <UIKit/UIKit.h> + +namespace dom_distiller::mojom { +enum class Theme; +} // namespace dom_distiller::mojom + +// Mutator for the Reader Mode. +@protocol ReaderModeMutator + +// Sets the default theme for the distilled page. +- (void)setDefaultTheme:(dom_distiller::mojom::Theme)theme; + +@end + +#endif // IOS_CHROME_BROWSER_READER_MODE_UI_READER_MODE_MUTATOR_H_
diff --git a/ios/chrome/browser/reader_mode/ui/reader_mode_view_controller.h b/ios/chrome/browser/reader_mode/ui/reader_mode_view_controller.h index 008783c1..8dd0c98db 100644 --- a/ios/chrome/browser/reader_mode/ui/reader_mode_view_controller.h +++ b/ios/chrome/browser/reader_mode/ui/reader_mode_view_controller.h
@@ -9,9 +9,13 @@ #import "ios/chrome/browser/reader_mode/ui/reader_mode_consumer.h" +@protocol ReaderModeMutator; + // View controller for displaying the Reader mode content. @interface ReaderModeViewController : UIViewController <ReaderModeConsumer> +@property(nonatomic, weak) id<ReaderModeMutator> mutator; + // Adds `self` as child view controller of `parent` and does the appropriate // calls to `willMoveToParentViewController:` and // `didMoveToParentViewController:`. If `animated` then an animation will be
diff --git a/ios/chrome/browser/reader_mode/ui/reader_mode_view_controller.mm b/ios/chrome/browser/reader_mode/ui/reader_mode_view_controller.mm index dc5e358..c579536 100644 --- a/ios/chrome/browser/reader_mode/ui/reader_mode_view_controller.mm +++ b/ios/chrome/browser/reader_mode/ui/reader_mode_view_controller.mm
@@ -4,7 +4,9 @@ #import "ios/chrome/browser/reader_mode/ui/reader_mode_view_controller.h" +#import "components/dom_distiller/core/mojom/distilled_page_prefs.mojom.h" #import "ios/chrome/browser/reader_mode/ui/constants.h" +#import "ios/chrome/browser/reader_mode/ui/reader_mode_mutator.h" #import "ios/chrome/browser/shared/ui/util/named_guide.h" #import "ios/chrome/browser/tab_switcher/ui_bundled/tab_grid/grid/tabs_closure_animation.h" #import "ios/chrome/common/ui/util/constraints_ui_util.h" @@ -24,6 +26,21 @@ [super viewDidLoad]; self.view.translatesAutoresizingMaskIntoConstraints = NO; self.view.accessibilityIdentifier = kReaderModeViewAccessibilityIdentifier; + + if (@available(iOS 17.0, *)) { + __weak __typeof(self) weakSelf = self; + id handler = ^(id<UITraitEnvironment> traitEnvironment, + UITraitCollection* previousCollection) { + [weakSelf updateTheme]; + }; + [self registerForTraitChanges:@[ UITraitUserInterfaceStyle.class ] + withHandler:handler]; + } +} + +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + [self updateTheme]; } #pragma mark - Public @@ -72,6 +89,19 @@ } } +#if !defined(__IPHONE_17_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_17_0 +- (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection { + [super traitCollectionDidChange:previousTraitCollection]; + if (@available(iOS 17.0, *)) { + return; + } + if (self.traitCollection.userInterfaceStyle != + previousTraitCollection.userInterfaceStyle) { + [self updateTheme]; + } +} +#endif + #pragma mark - ReaderModeConsumer - (void)setContentView:(UIView*)contentView { @@ -90,6 +120,14 @@ #pragma mark - Private +- (void)updateTheme { + if (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) { + [self.mutator setDefaultTheme:dom_distiller::mojom::Theme::kDark]; + } else { + [self.mutator setDefaultTheme:dom_distiller::mojom::Theme::kLight]; + } +} + // First restores user interaction in `self.view`. In case of dismissal, removes // the view and view controller from their hierarchy. Then calls // `didMoveToParentViewController:` and frees `_tabsClosureAnimation`.
diff --git a/ios/chrome/browser/safari_data_import/coordinator/safari_data_import_import_coordinator.mm b/ios/chrome/browser/safari_data_import/coordinator/safari_data_import_import_coordinator.mm index 92b2e1f..ef5c827f 100644 --- a/ios/chrome/browser/safari_data_import/coordinator/safari_data_import_import_coordinator.mm +++ b/ios/chrome/browser/safari_data_import/coordinator/safari_data_import_import_coordinator.mm
@@ -248,16 +248,18 @@ [self.mediator conflictingPasswords]; CHECK(passwordConflicts); if (passwordConflicts.count == 0) { - [self transitionToNextImportStage]; [self.mediator importItems]; return; } /// Wraps the password conflict view in a navigation controller to display /// navigation bar and toolbar. - UINavigationController* wrapper = [[UINavigationController alloc] - initWithRootViewController: + SafariDataImportPasswordConflictResolutionViewController* + conflictResolutionViewController = [[SafariDataImportPasswordConflictResolutionViewController alloc] - initWithPasswordConflicts:passwordConflicts]]; + initWithPasswordConflicts:passwordConflicts]; + conflictResolutionViewController.mutator = self.mediator; + UINavigationController* wrapper = [[UINavigationController alloc] + initWithRootViewController:conflictResolutionViewController]; wrapper.toolbarHidden = NO; [self presentViewController:wrapper]; }
diff --git a/ios/chrome/browser/safari_data_import/coordinator/safari_data_import_import_mediator.h b/ios/chrome/browser/safari_data_import/coordinator/safari_data_import_import_mediator.h index a4ea76f..f72bffa 100644 --- a/ios/chrome/browser/safari_data_import/coordinator/safari_data_import_import_mediator.h +++ b/ios/chrome/browser/safari_data_import/coordinator/safari_data_import_import_mediator.h
@@ -10,6 +10,7 @@ #import <memory> #import "components/password_manager/core/browser/ui/saved_passwords_presenter.h" +#import "ios/chrome/browser/safari_data_import/ui/safari_data_import_password_conflict_mutator.h" namespace autofill { class PaymentsDataManager; @@ -27,7 +28,9 @@ /// Mediator for the safari data import screen. Handles stages of importing a /// .zip file generated from Safari data to Chrome. -@interface SafariDataImportImportMediator : NSObject <UIDocumentPickerDelegate> +@interface SafariDataImportImportMediator + : NSObject <SafariDataImportPasswordConflictMutator, + UIDocumentPickerDelegate> /// Transition handler for import stage. This needs to be set before selecting a /// file. @@ -54,8 +57,8 @@ /// Resets the mediator to the state before any file is selected or processed. - (void)reset; -/// Imports the items that are ready for import. Should only be invoked when -/// items are ready. +/// Imports the items that are ready for import, and increments the import stage +/// . Should only be invoked when items are ready. - (void)importItems; /// List of password conflicts with the information retrieved from the source
diff --git a/ios/chrome/browser/safari_data_import/coordinator/safari_data_import_import_mediator.mm b/ios/chrome/browser/safari_data_import/coordinator/safari_data_import_import_mediator.mm index 8dd40802..73f6ca5 100644 --- a/ios/chrome/browser/safari_data_import/coordinator/safari_data_import_import_mediator.mm +++ b/ios/chrome/browser/safari_data_import/coordinator/safari_data_import_import_mediator.mm
@@ -69,7 +69,7 @@ } - (void)importItems { - _importer->CompleteImport(/*selected_password_ids=*/std::vector<int>()); + [self continueToImportPasswords:[NSArray array]]; } - (NSArray<PasswordImportItem*>*)conflictingPasswords { @@ -88,6 +88,17 @@ _disconnected = YES; } +#pragma mark - SafariDataImportPasswordConflictMutator + +- (void)continueToImportPasswords:(NSArray<NSNumber*>*)passwordIdentifiers { + std::vector<int> selected_password_ids; + for (NSNumber* identifier in passwordIdentifiers) { + selected_password_ids.push_back([identifier intValue]); + } + [self.importStageTransitionHandler transitionToNextImportStage]; + _importer->CompleteImport(selected_password_ids); +} + #pragma mark - UIDocumentPickerDelegate - (void)documentPicker:(UIDocumentPickerViewController*)controller
diff --git a/ios/chrome/browser/safari_data_import/ui/BUILD.gn b/ios/chrome/browser/safari_data_import/ui/BUILD.gn index f93c11d..eedca004 100644 --- a/ios/chrome/browser/safari_data_import/ui/BUILD.gn +++ b/ios/chrome/browser/safari_data_import/ui/BUILD.gn
@@ -11,6 +11,7 @@ "safari_data_import_import_stage_transition_handler.h", "safari_data_import_import_view_controller.h", "safari_data_import_import_view_controller.mm", + "safari_data_import_password_conflict_mutator.h", "safari_data_import_password_conflict_resolution_view_controller.h", "safari_data_import_password_conflict_resolution_view_controller.mm", "safari_data_item_table_view.h",
diff --git a/ios/chrome/browser/safari_data_import/ui/safari_data_import_password_conflict_mutator.h b/ios/chrome/browser/safari_data_import/ui/safari_data_import_password_conflict_mutator.h new file mode 100644 index 0000000..0ac05fd --- /dev/null +++ b/ios/chrome/browser/safari_data_import/ui/safari_data_import_password_conflict_mutator.h
@@ -0,0 +1,20 @@ +// Copyright 2025 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_SAFARI_DATA_IMPORT_UI_SAFARI_DATA_IMPORT_PASSWORD_CONFLICT_MUTATOR_H_ +#define IOS_CHROME_BROWSER_SAFARI_DATA_IMPORT_UI_SAFARI_DATA_IMPORT_PASSWORD_CONFLICT_MUTATOR_H_ + +#import <Foundation/Foundation.h> + +@class PasswordImportItem; + +/// A protocol for the mediator to handle password import conflicts. +@protocol SafariDataImportPasswordConflictMutator + +/// Continues to import the given passwords with identifiers. +- (void)continueToImportPasswords:(NSArray<NSNumber*>*)passwordIdentifiers; + +@end + +#endif // IOS_CHROME_BROWSER_SAFARI_DATA_IMPORT_UI_SAFARI_DATA_IMPORT_PASSWORD_CONFLICT_MUTATOR_H_
diff --git a/ios/chrome/browser/safari_data_import/ui/safari_data_import_password_conflict_resolution_view_controller.h b/ios/chrome/browser/safari_data_import/ui/safari_data_import_password_conflict_resolution_view_controller.h index ec8d68a..81e10b5 100644 --- a/ios/chrome/browser/safari_data_import/ui/safari_data_import_password_conflict_resolution_view_controller.h +++ b/ios/chrome/browser/safari_data_import/ui/safari_data_import_password_conflict_resolution_view_controller.h
@@ -8,12 +8,17 @@ #import "ios/chrome/browser/shared/ui/table_view/chrome_table_view_controller.h" @class PasswordImportItem; +@protocol SafariDataImportImportStageTransitionHandler; +@protocol SafariDataImportPasswordConflictMutator; /// View controller listing password conflicts introduced by Safari data import /// and allowing the user to resolve them. @interface SafariDataImportPasswordConflictResolutionViewController : ChromeTableViewController +/// Mutator object to handle conflict resolution decision. +@property(nonatomic, weak) id<SafariDataImportPasswordConflictMutator> mutator; + - (instancetype)initWithPasswordConflicts: (NSArray<PasswordImportItem*>*)passwords NS_DESIGNATED_INITIALIZER; - (instancetype)initWithStyle:(UITableViewStyle)style NS_UNAVAILABLE;
diff --git a/ios/chrome/browser/safari_data_import/ui/safari_data_import_password_conflict_resolution_view_controller.mm b/ios/chrome/browser/safari_data_import/ui/safari_data_import_password_conflict_resolution_view_controller.mm index def8934..98e4948 100644 --- a/ios/chrome/browser/safari_data_import/ui/safari_data_import_password_conflict_resolution_view_controller.mm +++ b/ios/chrome/browser/safari_data_import/ui/safari_data_import_password_conflict_resolution_view_controller.mm
@@ -7,6 +7,8 @@ #import "base/check_op.h" #import "ios/chrome/browser/safari_data_import/public/password_import_item.h" #import "ios/chrome/browser/safari_data_import/public/utils.h" +#import "ios/chrome/browser/safari_data_import/ui/safari_data_import_import_stage_transition_handler.h" +#import "ios/chrome/browser/safari_data_import/ui/safari_data_import_password_conflict_mutator.h" #import "ios/chrome/browser/shared/ui/symbols/symbols.h" #import "ios/chrome/browser/shared/ui/table_view/cells/table_view_attributed_string_header_footer_item.h" #import "ios/chrome/browser/shared/ui/table_view/cells/table_view_url_item.h" @@ -122,8 +124,12 @@ } - (void)didTapContinueButton { - /// TODO(crbug.com/420703283): Actually import passwords with conflict - /// resolution decisions. + NSMutableArray<NSNumber*>* passwordIdentifiers = [NSMutableArray array]; + for (NSIndexPath* indexPath in [self.tableView indexPathsForSelectedRows]) { + [passwordIdentifiers + addObject:[_dataSource itemIdentifierForIndexPath:indexPath]]; + } + [self.mutator continueToImportPasswords:passwordIdentifiers]; [self.presentingViewController dismissViewControllerAnimated:YES completion:nil]; } @@ -171,8 +177,8 @@ indexPath.item); /// Populate cell with information. PasswordImportItem* item = _passwordConflicts[identifier.intValue]; - cell.titleLabel.text = item.username; - cell.URLLabel.text = item.url; + cell.titleLabel.text = item.url; + cell.URLLabel.text = item.username; if (item.faviconAttributes) { [cell.faviconView configureWithAttributes:item.faviconAttributes]; } else {
diff --git a/ios/chrome/browser/signin/model/BUILD.gn b/ios/chrome/browser/signin/model/BUILD.gn index 4d030a3..f680d7f 100644 --- a/ios/chrome/browser/signin/model/BUILD.gn +++ b/ios/chrome/browser/signin/model/BUILD.gn
@@ -65,6 +65,7 @@ "//components/image_fetcher/core", "//components/image_fetcher/ios", "//components/metrics", + "//components/policy/core/common", "//components/policy/core/common:common_constants", "//components/pref_registry", "//components/prefs", @@ -138,6 +139,7 @@ ":system_identity_util", "//base", "//components/browser_sync", + "//components/policy/core/common", "//components/pref_registry", "//components/prefs", "//components/signin/ios/browser:features",
diff --git a/ios/chrome/browser/signin/model/account_profile_mapper.mm b/ios/chrome/browser/signin/model/account_profile_mapper.mm index b793251..0992943d 100644 --- a/ios/chrome/browser/signin/model/account_profile_mapper.mm +++ b/ios/chrome/browser/signin/model/account_profile_mapper.mm
@@ -14,6 +14,7 @@ #import "base/metrics/user_metrics.h" #import "base/metrics/user_metrics_action.h" #import "base/strings/sys_string_conversions.h" +#import "components/policy/core/common/management/platform_management_service.h" #import "components/prefs/pref_service.h" #import "components/signin/core/browser/account_management_type_metrics_recorder.h" #import "google_apis/gaia/gaia_id.h" @@ -990,7 +991,7 @@ base::UmaHistogramEnumeration( "Signin.IOSAccountsOnDeviceManagementTypesSummary", account_types_summary); - if (IsApplicationManagedByMDM()) { + if (policy::PlatformManagementService::GetInstance()->IsManaged()) { base::UmaHistogramEnumeration( "Signin.IOSAccountsOnDeviceManagementTypesSummary.ManagedDevice", account_types_summary);
diff --git a/ios/chrome/browser/signin/model/authentication_service.mm b/ios/chrome/browser/signin/model/authentication_service.mm index d368746..354e963 100644 --- a/ios/chrome/browser/signin/model/authentication_service.mm +++ b/ios/chrome/browser/signin/model/authentication_service.mm
@@ -15,6 +15,7 @@ #import "base/strings/sys_string_conversions.h" #import "base/task/single_thread_task_runner.h" #import "components/browser_sync/sync_to_signin_migration.h" +#import "components/policy/core/common/management/platform_management_service.h" #import "components/pref_registry/pref_registry_syncable.h" #import "components/prefs/pref_service.h" #import "components/signin/ios/browser/features.h" @@ -340,7 +341,7 @@ // 2. The app management configuration key is present. // Note: data will be cleared from the time of sign-in in this case. return HasPrimaryIdentityManaged(signin::ConsentLevel::kSignin) && - !IsApplicationManagedByMDM(); + !policy::PlatformManagementService::GetInstance()->IsManaged(); } id<SystemIdentity> AuthenticationService::GetPrimaryIdentity(
diff --git a/ios/chrome/browser/signin/model/gaia_auth_fetcher_ios.h b/ios/chrome/browser/signin/model/gaia_auth_fetcher_ios.h index dda09f59..1f0abda1 100644 --- a/ios/chrome/browser/signin/model/gaia_auth_fetcher_ios.h +++ b/ios/chrome/browser/signin/model/gaia_auth_fetcher_ios.h
@@ -50,7 +50,7 @@ void CreateAndStartGaiaFetcher( const std::string& body, const std::string& body_content_type, - const std::string& headers, + const net::HttpRequestHeaders& headers, const GURL& gaia_gurl, network::mojom::CredentialsMode credentials_mode, const net::NetworkTrafficAnnotationTag& traffic_annotation) override;
diff --git a/ios/chrome/browser/signin/model/gaia_auth_fetcher_ios.mm b/ios/chrome/browser/signin/model/gaia_auth_fetcher_ios.mm index 8f04d6f..8848e02 100644 --- a/ios/chrome/browser/signin/model/gaia_auth_fetcher_ios.mm +++ b/ios/chrome/browser/signin/model/gaia_auth_fetcher_ios.mm
@@ -27,7 +27,7 @@ void GaiaAuthFetcherIOS::CreateAndStartGaiaFetcher( const std::string& body, const std::string& body_content_type, - const std::string& headers, + const net::HttpRequestHeaders& headers, const GURL& gaia_gurl, network::mojom::CredentialsMode credentials_mode, const net::NetworkTrafficAnnotationTag& traffic_annotation) { @@ -46,7 +46,7 @@ } DVLOG(2) << "Gaia fetcher URL: " << gaia_gurl.spec(); - DVLOG(2) << "Gaia fetcher headers: " << headers; + DVLOG(2) << "Gaia fetcher headers: " << headers.ToString(); DVLOG(2) << "Gaia fetcher body: " << body; // The fetch requires cookies and WKWebView is being used. The only way to do @@ -54,7 +54,8 @@ // WKWebView. SetPendingFetch(true); bool should_use_xml_http_request = IsMultiloginUrl(gaia_gurl); - bridge_->Fetch(gaia_gurl, headers, body, should_use_xml_http_request); + bridge_->Fetch(gaia_gurl, headers.ToString(), body, + should_use_xml_http_request); } void GaiaAuthFetcherIOS::OnFetchComplete(const GURL& url,
diff --git a/ios/chrome/browser/tips_notifications/model/BUILD.gn b/ios/chrome/browser/tips_notifications/model/BUILD.gn index d0cc08fc..830a119 100644 --- a/ios/chrome/browser/tips_notifications/model/BUILD.gn +++ b/ios/chrome/browser/tips_notifications/model/BUILD.gn
@@ -78,6 +78,7 @@ ":utils", "//base", "//base/test:test_support", + "//components/feature_engagement/public", "//components/feature_engagement/test:test_support", "//components/password_manager/core/common", "//components/safe_browsing/core/common:safe_browsing_prefs",
diff --git a/ios/chrome/browser/tips_notifications/model/tips_notification_client.h b/ios/chrome/browser/tips_notifications/model/tips_notification_client.h index 700c8b2..5fbff14 100644 --- a/ios/chrome/browser/tips_notifications/model/tips_notification_client.h +++ b/ios/chrome/browser/tips_notifications/model/tips_notification_client.h
@@ -120,8 +120,8 @@ // Classifies the user and sets the `user_type`, if possible. void ClassifyUser(); - // Returns whether any identities/accounts exist on the device. - bool HasIdentitiesOnDevice(ProfileIOS* profile) const; + // Returns the profile for an active foreground Browser, if there is one. + ProfileIOS* GetActiveForegroundProfile() const; // Stores whether Tips notifications are permitted. bool permitted_ = false; @@ -144,6 +144,11 @@ // settings. std::optional<TipsNotificationType> forced_type_; + // If set, previous notification request(s) will be cleared and the given + // type will be sent one time with a 24 hour trigger. This will be used in + // conjunction with the kIOSOneTimeDefaultBrowserNotification feature. + std::optional<TipsNotificationType> one_time_type_; + // Observes changes to permitted pref. PrefChangeRegistrar pref_change_registrar_;
diff --git a/ios/chrome/browser/tips_notifications/model/tips_notification_client.mm b/ios/chrome/browser/tips_notifications/model/tips_notification_client.mm index c9c2c2e..bafda764 100644 --- a/ios/chrome/browser/tips_notifications/model/tips_notification_client.mm +++ b/ios/chrome/browser/tips_notifications/model/tips_notification_client.mm
@@ -10,9 +10,11 @@ #import "base/strings/sys_string_conversions.h" #import "base/task/bind_post_task.h" #import "base/time/time.h" +#import "components/feature_engagement/public/tracker.h" #import "components/prefs/pref_registry_simple.h" #import "components/prefs/pref_service.h" #import "components/signin/public/identity_manager/identity_manager.h" +#import "ios/chrome/browser/feature_engagement/model/tracker_factory.h" #import "ios/chrome/browser/push_notification/model/constants.h" #import "ios/chrome/browser/push_notification/model/push_notification_client.h" #import "ios/chrome/browser/push_notification/model/push_notification_service.h" @@ -36,6 +38,9 @@ // The amount of time used to determine if the user should be classified. const base::TimeDelta kClassifyUserRecency = base::Hours(2); +// The trigger time used for the one time default browser notification. +const base::TimeDelta OneTimeNotificationTriggerDelta = base::Hours(24); + // Returns the first notification from `requests` whose identifier matches // `identifier`. UNNotificationRequest* NotificationWithIdentifier( @@ -251,6 +256,21 @@ void TipsNotificationClient::OnPendingRequestFound( UNNotificationRequest* request) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + + // Check for the one-time default browser notification. + if (base::FeatureList::IsEnabled(kIOSOneTimeDefaultBrowserNotification)) { + ProfileIOS* profile = GetActiveForegroundProfile(); + if (profile) { + TipsNotificationType type = TipsNotificationType::kDefaultBrowser; + std::unique_ptr<TipsNotificationCriteria> criteria = + std::make_unique<TipsNotificationCriteria>(profile, local_state_, + CanSendReactivation()); + if (criteria->ShouldSendNotification(type)) { + one_time_type_ = type; + } + } + } + if (!request) { MaybeLogTriggeredNotification(); MaybeLogDismissedNotification(); @@ -262,10 +282,10 @@ MaybeLogDismissedNotification(); interacted_type_ = std::nullopt; + std::optional<TipsNotificationType> type = ParseTipsNotificationType(request); + if (CanSendReactivation()) { ClearAllRequestedNotifications(); - std::optional<TipsNotificationType> type = - ParseTipsNotificationType(request); if (type.has_value()) { MarkNotificationTypeNotSent(type.value()); // Increment the Reactivation canceled count. @@ -276,6 +296,16 @@ } MaybeRequestNotification(base::DoNothing()); } + + if (one_time_type_.has_value()) { + // If a pending request is found, clear it to prioritize the one-time + // notification. + ClearAllRequestedNotifications(); + if (type.has_value()) { + MarkNotificationTypeNotSent(type.value()); + } + MaybeRequestNotification(base::DoNothing()); + } } void TipsNotificationClient::MaybeRequestNotification( @@ -286,12 +316,11 @@ return; } - Browser* browser = GetActiveForegroundBrowser(); - if (!browser) { + ProfileIOS* profile = GetActiveForegroundProfile(); + if (!profile) { std::move(completion).Run(); return; } - ProfileIOS* profile = browser->GetProfile(); if (forced_type_.has_value()) { RequestNotification(forced_type_.value(), profile->GetProfileName(), @@ -299,6 +328,25 @@ return; } + if (one_time_type_.has_value()) { + if (one_time_type_ == TipsNotificationType::kDefaultBrowser) { + // The FET's feature should be triggered. + feature_engagement::Tracker* tracker = + feature_engagement::TrackerFactory::GetForProfile(profile); + if (tracker->ShouldTriggerHelpUI( + feature_engagement:: + kIPHiOSOneTimeDefaultBrowserNotificationFeature)) { + RequestNotification(one_time_type_.value(), profile->GetProfileName(), + std::move(completion)); + tracker->Dismissed(feature_engagement:: + kIPHiOSOneTimeDefaultBrowserNotificationFeature); + tracker->NotifyEvent("default_browser_promos_group_trigger"); + } + } + one_time_type_ = std::nullopt; + return; + } + int sent_bitfield = local_state_->GetInteger(kTipsNotificationsSentPref); int enabled_bitfield = TipsNotificationsEnabledBitfield(); @@ -344,6 +392,9 @@ base::TimeDelta trigger_delta = TipsNotificationTriggerDelta( CanSendReactivation(), user_type_, notification_type); + if (one_time_type_.has_value()) { + trigger_delta = std::min(trigger_delta, OneTimeNotificationTriggerDelta); + } if (IsNotificationCollisionManagementEnabled()) { ScheduledNotificationRequest request = { @@ -560,3 +611,11 @@ SetTipsNotificationUserType(local_state_, user_type_); base::UmaHistogramEnumeration("IOS.Notifications.Tips.UserType", user_type_); } + +ProfileIOS* TipsNotificationClient::GetActiveForegroundProfile() const { + Browser* browser = GetActiveForegroundBrowser(); + if (!browser) { + return nullptr; + } + return browser->GetProfile(); +}
diff --git a/ios/chrome/browser/tips_notifications/model/tips_notification_client_unittest.mm b/ios/chrome/browser/tips_notifications/model/tips_notification_client_unittest.mm index 68262e4..63e95fa8 100644 --- a/ios/chrome/browser/tips_notifications/model/tips_notification_client_unittest.mm +++ b/ios/chrome/browser/tips_notifications/model/tips_notification_client_unittest.mm
@@ -10,6 +10,7 @@ #import "base/test/scoped_feature_list.h" #import "base/test/scoped_mock_clock_override.h" #import "base/threading/thread_restrictions.h" +#import "components/feature_engagement/public/tracker.h" #import "components/prefs/scoped_user_pref_update.h" #import "components/safe_browsing/core/common/safe_browsing_prefs.h" #import "ios/chrome/browser/authentication/ui_bundled/signin_presenter.h" @@ -17,6 +18,7 @@ #import "ios/chrome/browser/default_browser/model/promo_source.h" #import "ios/chrome/browser/default_browser/model/utils.h" #import "ios/chrome/browser/default_browser/model/utils_test_support.h" +#import "ios/chrome/browser/feature_engagement/model/tracker_factory.h" #import "ios/chrome/browser/first_run/model/first_run.h" #import "ios/chrome/browser/push_notification/model/constants.h" #import "ios/chrome/browser/push_notification/model/push_notification_util.h" @@ -87,6 +89,14 @@ ScopedDictPrefUpdate update(GetApplicationContext()->GetLocalState(), prefs::kAppLevelPushNotificationPermissions); update->Set(kTipsNotificationKey, true); + + // Wait for the tracker to initialize. + tracker_ = + feature_engagement::TrackerFactory::GetForProfile(profile_.get()); + base::RunLoop run_loop; + tracker_->AddOnInitializedCallback( + base::IgnoreArgs<bool>(run_loop.QuitClosure())); + run_loop.Run(); } // Sets up a mock notification center, so notification requests can be @@ -287,6 +297,7 @@ const base::HistogramTester histogram_tester_; IOSChromeScopedTestingLocalState scoped_testing_local_state_; TestProfileManagerIOS profile_manager_; + raw_ptr<feature_engagement::Tracker> tracker_; id mock_scene_state_; UNNotificationResponse* mock_notification_response_; std::unique_ptr<TestBrowser> browser_; @@ -757,3 +768,49 @@ histogram_tester_.ExpectUniqueSample("IOS.Notifications.Tips.Interaction", TipsNotificationType::kLensOverlay, 1); } + +// Tests that the client can request a one-time default browser notification. +TEST_F(TipsNotificationClientTest, OneTimeDefaultBrowserNotification) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature(kIOSOneTimeDefaultBrowserNotification); + SetFalseChromeLikelyDefaultBrowser(); + RecordDefaultBrowserPromoLastAction(IOSDefaultBrowserPromoAction::kCancel); + + StubGetPendingRequests(nil); + ExpectNotificationRequest(TipsNotificationType::kDefaultBrowser); + + SimulateForegroundingApp(); + + EXPECT_OCMOCK_VERIFY(mock_notification_center_); + + // Verify that it sends the next available notification afterwards. + ExpectNotificationRequest(TipsNotificationType::kEnhancedSafeBrowsing); + SimulateForegroundingApp(); + EXPECT_OCMOCK_VERIFY(mock_notification_center_); +} + +// Tests that a pending notification is cleared for the one-time default +// browser notification. +TEST_F(TipsNotificationClientTest, + OneTimeDefaultBrowserNotificationClearsPending) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature(kIOSOneTimeDefaultBrowserNotification); + SetFalseChromeLikelyDefaultBrowser(); + + UNNotificationRequest* request = [UNNotificationRequest + requestWithIdentifier:kTipsNotificationId + content:ContentForTipsNotificationType( + TipsNotificationType::kWhatsNew, false, + GetProfileName()) + trigger:nil]; + StubGetPendingRequests(@[ request ]); + ExpectNotificationRequest(TipsNotificationType::kDefaultBrowser); + OCMExpect([mock_notification_center_ + removePendingNotificationRequestsWithIdentifiers:@[ + kTipsNotificationId + ]]); + + SimulateForegroundingApp(); + + EXPECT_OCMOCK_VERIFY(mock_notification_center_); +}
diff --git a/ios/chrome/browser/tips_notifications/model/tips_notification_criteria.mm b/ios/chrome/browser/tips_notifications/model/tips_notification_criteria.mm index cc1efd7..eb8661a4 100644 --- a/ios/chrome/browser/tips_notifications/model/tips_notification_criteria.mm +++ b/ios/chrome/browser/tips_notifications/model/tips_notification_criteria.mm
@@ -118,7 +118,21 @@ } bool TipsNotificationCriteria::ShouldSendDefaultBrowser() { - return !IsChromeLikelyDefaultBrowser() && !DefaultBrowserPromoCanceled(); + if (IsChromeLikelyDefaultBrowser()) { + return false; + } + if (base::FeatureList::IsEnabled(kIOSOneTimeDefaultBrowserNotification)) { + // For kIOSOneTimeDefaultBrowserNotification, the logic simply checks if + // the user has not seen the promo in the last 14 days. + feature_engagement::Tracker* tracker = + feature_engagement::TrackerFactory::GetForProfile(profile_); + bool would_trigger = tracker->WouldTriggerHelpUI( + feature_engagement::kIPHiOSOneTimeDefaultBrowserNotificationFeature); + if (would_trigger) { + return true; + } + } + return !DefaultBrowserPromoCanceled(); } bool TipsNotificationCriteria::ShouldSendWhatsNew() {
diff --git a/ios/chrome/browser/tips_notifications/model/tips_notification_criteria_unittest.mm b/ios/chrome/browser/tips_notifications/model/tips_notification_criteria_unittest.mm index 53f38e2..91c267a 100644 --- a/ios/chrome/browser/tips_notifications/model/tips_notification_criteria_unittest.mm +++ b/ios/chrome/browser/tips_notifications/model/tips_notification_criteria_unittest.mm
@@ -139,6 +139,7 @@ // has previously canceled the promo. TEST_F(TipsNotificationCriteriaTest, TestShouldSendDefaultBrowser_PromoCanceled) { + feature_list_.InitAndDisableFeature(kIOSOneTimeDefaultBrowserNotification); SetFalseChromeLikelyDefaultBrowser(); RecordDefaultBrowserPromoLastAction(IOSDefaultBrowserPromoAction::kCancel); EXPECT_FALSE( @@ -149,6 +150,7 @@ // previously dismissed the promo. TEST_F(TipsNotificationCriteriaTest, TestShouldSendDefaultBrowser_PromoDismissed) { + feature_list_.InitAndDisableFeature(kIOSOneTimeDefaultBrowserNotification); SetFalseChromeLikelyDefaultBrowser(); RecordDefaultBrowserPromoLastAction(IOSDefaultBrowserPromoAction::kDismiss); EXPECT_TRUE( @@ -159,6 +161,7 @@ // previously tapped "Remind Me Later". TEST_F(TipsNotificationCriteriaTest, TestShouldSendDefaultBrowser_PromoRemindMeLater) { + feature_list_.InitAndDisableFeature(kIOSOneTimeDefaultBrowserNotification); SetFalseChromeLikelyDefaultBrowser(); RecordDefaultBrowserPromoLastAction( IOSDefaultBrowserPromoAction::kRemindMeLater); @@ -169,11 +172,44 @@ // Tests that the Default Browser notification should be sent when the user has // not interacted with the promo and Chrome is not the default. TEST_F(TipsNotificationCriteriaTest, TestShouldSendDefaultBrowser_NoAction) { + feature_list_.InitAndDisableFeature(kIOSOneTimeDefaultBrowserNotification); SetFalseChromeLikelyDefaultBrowser(); EXPECT_TRUE( criteria_->ShouldSendNotification(TipsNotificationType::kDefaultBrowser)); } +// Tests that the Default Browser notification should not be sent if a default +// browser promo has been seen in the last 2 weeks. +TEST_F(TipsNotificationCriteriaTest, + TestShouldSendDefaultBrowser_ShouldNotTriggerOneTime) { + feature_list_.InitAndEnableFeature(kIOSOneTimeDefaultBrowserNotification); + SetFalseChromeLikelyDefaultBrowser(); + RecordDefaultBrowserPromoLastAction(IOSDefaultBrowserPromoAction::kCancel); + EXPECT_CALL( + *mock_tracker_, + WouldTriggerHelpUI(testing::Ref( + feature_engagement::kIPHiOSOneTimeDefaultBrowserNotificationFeature))) + .WillOnce(testing::Return(false)); + EXPECT_FALSE( + criteria_->ShouldSendNotification(TipsNotificationType::kDefaultBrowser)); +} + +// Tests that the Default Browser notification should be sent if a default +// browser promo has not been seen in the last 2 weeks. +TEST_F(TipsNotificationCriteriaTest, + TestShouldSendDefaultBrowser_ShouldTriggerOneTime) { + feature_list_.InitAndEnableFeature(kIOSOneTimeDefaultBrowserNotification); + SetFalseChromeLikelyDefaultBrowser(); + RecordDefaultBrowserPromoLastAction(IOSDefaultBrowserPromoAction::kCancel); + EXPECT_CALL( + *mock_tracker_, + WouldTriggerHelpUI(testing::Ref( + feature_engagement::kIPHiOSOneTimeDefaultBrowserNotificationFeature))) + .WillOnce(testing::Return(true)); + EXPECT_TRUE( + criteria_->ShouldSendNotification(TipsNotificationType::kDefaultBrowser)); +} + #pragma mark - ShouldSendSignin // Tests that the Sign-in notification should be sent when sign-in is allowed
diff --git a/ios/chrome/test/BUILD.gn b/ios/chrome/test/BUILD.gn index e97fafe..cd8bc629 100644 --- a/ios/chrome/test/BUILD.gn +++ b/ios/chrome/test/BUILD.gn
@@ -408,6 +408,7 @@ "//ios/chrome/browser/promos_manager/ui_bundled:unit_tests", "//ios/chrome/browser/push_notification/model:unit_tests", "//ios/chrome/browser/push_notification/ui_bundled:unit_tests", + "//ios/chrome/browser/reader_mode/coordinator:unit_tests", "//ios/chrome/browser/reader_mode/model:unit_tests", "//ios/chrome/browser/reading_list/model:unit_tests", "//ios/chrome/browser/reading_list/ui_bundled:unit_tests",
diff --git a/ios/chrome/test/providers/lens/test_lens_overlay_controller.mm b/ios/chrome/test/providers/lens/test_lens_overlay_controller.mm index 4af75652..2794040 100644 --- a/ios/chrome/test/providers/lens/test_lens_overlay_controller.mm +++ b/ios/chrome/test/providers/lens/test_lens_overlay_controller.mm
@@ -53,6 +53,14 @@ // NO-OP } +- (void)setHUDViewHidden:(BOOL)hidden { + // NO-OP +} + +- (void)setGuidanceViewHidden:(BOOL)hidden { + // NO-OP +} + - (void)disableFlyoutMenu:(BOOL)disable { // NO-OP }
diff --git a/ios/public/provider/chrome/browser/bwg/bwg_api.h b/ios/public/provider/chrome/browser/bwg/bwg_api.h index 749b3a94..223cbe5 100644 --- a/ios/public/provider/chrome/browser/bwg/bwg_api.h +++ b/ios/public/provider/chrome/browser/bwg/bwg_api.h
@@ -38,6 +38,7 @@ kEnterpriseDisabled, }; +// TODO(crbug.com/434662294): Remove when migration is complete. // Enum representing the PageContext state of the BWG experience. // This needs to stay in sync with GCRGeminiPageState (and its SDK counterpart). enum class BWGPageContextState { @@ -59,6 +60,42 @@ kUserDisabled, }; +// Enum representing the page context computation state of the BWG experience. +// This needs to stay in sync with GCRGeminiPageContextComputationState (and its +// SDK counterpart). +enum class BWGPageContextComputationState { + // The state of the page context is unknown; this likely means that it was not + // set. + kUnknown, + // The page context was successfully created. + kSuccess, + // The page context should have been included, but was not gathered + // successfully. + kError, + // The page contains protected content which should not be used for Gemini, + // and should not be sent to any server or stored. + kProtected, + // The page contains blocked content that could be used for Gemini, but will + // likely be rejected due to its content. + kBlocked, +}; + +// Enum representing the page context attachment state of the BWG experience. +// This needs to stay in sync with GCRGeminiPageContextAttachmentState (and its +// SDK counterpart). +enum class BWGPageContextAttachmentState { + // The attach state is unknown. + kUnknown, + // Page context should be attached. + kAttached, + // Page context should be detached. + kDetached, + // Page context attachment is disabled by the user. + kUserDisabled, + // Page context attachment is disabled by an enterprise policy. + kEnterpriseDisabled, +}; + // Creates request body data using a prompt and page context. std::string CreateRequestBody( std::string prompt,
diff --git a/ios/public/provider/chrome/browser/lens/lens_overlay_api.h b/ios/public/provider/chrome/browser/lens/lens_overlay_api.h index d82e74a..62226bd 100644 --- a/ios/public/provider/chrome/browser/lens/lens_overlay_api.h +++ b/ios/public/provider/chrome/browser/lens/lens_overlay_api.h
@@ -101,6 +101,12 @@ // Updates the visibility of the top icons. - (void)setTopIconsHidden:(BOOL)hidden; +// Updates the visibility of the HUD view. +- (void)setHUDViewHidden:(BOOL)hidden; + +// Updates the visibility of the guidance view. +- (void)setGuidanceViewHidden:(BOOL)hidden; + // Disables flyout menus from displaying. - (void)disableFlyoutMenu:(BOOL)disable;
diff --git a/ios/testing/earl_grey/base_earl_grey_test_case.mm b/ios/testing/earl_grey/base_earl_grey_test_case.mm index 84956ed..776156a 100644 --- a/ios/testing/earl_grey/base_earl_grey_test_case.mm +++ b/ios/testing/earl_grey/base_earl_grey_test_case.mm
@@ -16,11 +16,6 @@ #import "ios/testing/earl_grey/coverage_utils.h" #import "ios/testing/earl_grey/earl_grey_test.h" #import "ios/testing/earl_grey/system_alert_handler.h" -#import "ui/display/screen.h" - -#if DCHECK_IS_ON() -#import "ui/display/screen_base.h" -#endif namespace { @@ -28,7 +23,6 @@ // ensure that +setUpForTestCase is called exactly once per unique XCTestCase // and is reset in +tearDown. bool g_needs_set_up_for_test_case = true; -std::unique_ptr<display::ScopedNativeScreen> g_screen; } // namespace @implementation BaseEarlGreyTestCase @@ -67,8 +61,6 @@ - (void)setUp { [super setUp]; - g_screen = std::make_unique<display::ScopedNativeScreen>(); - // Before starting a new test, relaunch the app and wipe the profile. AppLaunchConfiguration config = [self appConfigurationForTestCase]; if ([BaseEarlGreyTestCase forceRestartAndWipe]) { @@ -96,17 +88,7 @@ } + (void)tearDown { -#if DCHECK_IS_ON() - // Make sure that all display observers are removed at the end of each - // test. - if (display::Screen::HasScreen()) { - display::ScreenBase* screen = - static_cast<display::ScreenBase*>(display::Screen::GetScreen()); - DCHECK(!screen->HasDisplayObservers()); - } -#endif g_needs_set_up_for_test_case = true; - g_screen.reset(); [super tearDown]; }
diff --git a/ios/web_view/internal/signin/web_view_gaia_auth_fetcher.h b/ios/web_view/internal/signin/web_view_gaia_auth_fetcher.h index b11a326..a47cfa1b 100644 --- a/ios/web_view/internal/signin/web_view_gaia_auth_fetcher.h +++ b/ios/web_view/internal/signin/web_view_gaia_auth_fetcher.h
@@ -24,7 +24,7 @@ void CreateAndStartGaiaFetcher( const std::string& body, const std::string& body_content_type, - const std::string& headers, + const net::HttpRequestHeaders& headers, const GURL& gaia_gurl, network::mojom::CredentialsMode credentials_mode, const net::NetworkTrafficAnnotationTag& traffic_annotation) override;
diff --git a/ios/web_view/internal/signin/web_view_gaia_auth_fetcher.mm b/ios/web_view/internal/signin/web_view_gaia_auth_fetcher.mm index c5f1f08e..52d21712 100644 --- a/ios/web_view/internal/signin/web_view_gaia_auth_fetcher.mm +++ b/ios/web_view/internal/signin/web_view_gaia_auth_fetcher.mm
@@ -20,7 +20,7 @@ void WebViewGaiaAuthFetcher::CreateAndStartGaiaFetcher( const std::string& body, const std::string& body_content_type, - const std::string& headers, + const net::HttpRequestHeaders& headers, const GURL& gaia_gurl, network::mojom::CredentialsMode credentials_mode, const net::NetworkTrafficAnnotationTag& traffic_annotation) {
diff --git a/ios_internal b/ios_internal index 264a87f..e733c5e 160000 --- a/ios_internal +++ b/ios_internal
@@ -1 +1 @@ -Subproject commit 264a87f5ecb14297755d6381aace419f6cbedaed +Subproject commit e733c5ec5c2437d1c4431c0debd400a2f4e83bd7
diff --git a/ipc/ipc_sync_channel_unittest.cc b/ipc/ipc_sync_channel_unittest.cc index c70afc1..a85b732 100644 --- a/ipc/ipc_sync_channel_unittest.cc +++ b/ipc/ipc_sync_channel_unittest.cc
@@ -1178,9 +1178,11 @@ peer_(peer) {} void OnDoServerTask() { - events_[3]->Signal(); - events_[2]->Wait(); - events_[0]->Signal(); + UNSAFE_TODO({ + events_[3]->Signal(); + events_[2]->Wait(); + events_[0]->Signal(); + }); SendMessageToClient(); } @@ -1239,9 +1241,11 @@ } void OnDoClient2Task() { - events_[3]->Wait(); - events_[1]->Signal(); - events_[2]->Signal(); + UNSAFE_TODO({ + events_[3]->Wait(); + events_[1]->Signal(); + events_[2]->Signal(); + }); DCHECK(received_msg_ == false); Message* message = new SyncChannelTestMsg_NoArgs; @@ -1308,8 +1312,10 @@ FROM_HERE, base::BindOnce(&RestrictedDispatchDeadlockClient2::OnDoClient2Task, base::Unretained(peer_))); - events_[0]->Wait(); - events_[1]->Wait(); + UNSAFE_TODO({ + events_[0]->Wait(); + events_[1]->Wait(); + }); DCHECK(received_msg_ == false); Message* message = new SyncChannelTestMsg_NoArgs;
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc index 508c455..8f93145 100644 --- a/media/base/media_switches.cc +++ b/media/base/media_switches.cc
@@ -1061,6 +1061,14 @@ "HardwareSecureDecryptionAv1", base::FEATURE_DISABLED_BY_DEFAULT); +// Enables showing permission indicator in the omnibox when a site is allowed or +// denied to to use protected content IDs to play protected content. +#if BUILDFLAG(IS_WIN) +BASE_FEATURE(kProtectedMediaIdentifierIndicator, + "ProtectedMediaIdentifierIndicator", + base::FEATURE_ENABLED_BY_DEFAULT); +#endif + // Enables handling of hardware media keys for controlling media. BASE_FEATURE(kHardwareMediaKeyHandling, "HardwareMediaKeyHandling", @@ -1795,7 +1803,7 @@ // Allows per-site special processing for media links. BASE_FEATURE(kMediaLinkHelpers, "MediaLinkHelpers", - base::FEATURE_DISABLED_BY_DEFAULT); + base::FEATURE_ENABLED_BY_DEFAULT); bool IsChromeWideEchoCancellationEnabled() { #if BUILDFLAG(CHROME_WIDE_ECHO_CANCELLATION)
diff --git a/media/base/media_switches.h b/media/base/media_switches.h index 0d3b42e1..4c4ebe97 100644 --- a/media/base/media_switches.h +++ b/media/base/media_switches.h
@@ -283,6 +283,9 @@ MEDIA_EXPORT extern const base::FeatureParam<bool> kHardwareSecureDecryptionFallbackOnHardwareContextReset; MEDIA_EXPORT BASE_DECLARE_FEATURE(kHardwareSecureDecryptionAv1); +#if BUILDFLAG(IS_WIN) +MEDIA_EXPORT BASE_DECLARE_FEATURE(kProtectedMediaIdentifierIndicator); +#endif MEDIA_EXPORT BASE_DECLARE_FEATURE(kInternalMediaSession); MEDIA_EXPORT BASE_DECLARE_FEATURE(kOnDeviceWebSpeech); MEDIA_EXPORT BASE_DECLARE_FEATURE(kLiveCaption);
diff --git a/media/cast/encoding/audio_encoder_unittest.cc b/media/cast/encoding/audio_encoder_unittest.cc index 385a704..595d9b9 100644 --- a/media/cast/encoding/audio_encoder_unittest.cc +++ b/media/cast/encoding/audio_encoder_unittest.cc
@@ -11,6 +11,7 @@ #include <sstream> #include <string> +#include "base/compiler_specific.h" #include "base/functional/bind.h" #include "base/functional/callback_helpers.h" #include "base/logging.h" @@ -100,7 +101,7 @@ if (i > 0) { out << ", "; } - out << durations_in_ms[i]; + out << UNSAFE_TODO(durations_in_ms[i]); } return out.str(); } @@ -129,9 +130,10 @@ const base::TimeDelta frame_duration = audio_encoder_->GetFrameDuration(); for (size_t i = 0; i < scenario.num_durations; ++i) { - const bool simulate_missing_data = scenario.durations_in_ms[i] < 0; - const base::TimeDelta duration = - base::Milliseconds(std::abs(scenario.durations_in_ms[i])); + const bool simulate_missing_data = + UNSAFE_TODO(scenario.durations_in_ms[i]) < 0; + const base::TimeDelta duration = base::Milliseconds( + std::abs(UNSAFE_TODO(scenario.durations_in_ms[i]))); receiver_->SetCaptureTimeBounds(NowTicks() - frame_duration, NowTicks() + duration); if (!simulate_missing_data) {
diff --git a/media/filters/hls_rendition_impl.cc b/media/filters/hls_rendition_impl.cc index dca6d00..6b02bb4 100644 --- a/media/filters/hls_rendition_impl.cc +++ b/media/filters/hls_rendition_impl.cc
@@ -144,53 +144,108 @@ return; } - // If media time comes before the last loaded range, then a seek probably - // failed, and we should raise an error. - if (std::get<0>(ranges.back()) > media_time) { - HlsDemuxerStatus error = HlsDemuxerStatus::Codes::kInvalidLoadedRanges; - rendition_host_->Quit(std::move(error) - .WithData("timestamp", media_time) - .WithData("range_start", ranges.back().first) - .WithData("range_end", ranges.back().second)); + if (ranges.contains(0, media_time)) { + auto end_of_range_ts = ranges.end(0); + auto end_of_buffer_ts = std::get<1>(ranges.back()); + auto ideal_buffer_duration = GetIdealBufferSize(); + auto denominator_rate = playback_rate ? playback_rate : 1.0; + auto range_duration = (end_of_range_ts - media_time) / denominator_rate; + auto buffer_duration = (end_of_buffer_ts - media_time) / denominator_rate; + + // Regardless of the gaps ahead, we just don't have enough data at all, and + // we need more. + if (buffer_duration < ideal_buffer_duration) { + TryFillingBuffers(std::move(time_remaining_cb), media_time); + return; + } + + // If there are gaps in the loaded content, we might have to issue a seek + // really soon to skip them. We want to do this after `media_time` has + // run into that gap in order to calculate the best seek offset, so we issue + // a delay here for as much time as is needed to hit the end of this + // particular range. + if (range_duration < ideal_buffer_duration) { + // wait to check state again when `media_time` is at the gap coming up. + std::move(time_remaining_cb).Run(range_duration); + return; + } + + // Why do today what can be put off until tomorrow? Handle the gap later. + // Our buffers are in good shape. This is a good chance to clear out old + // data and then, for live content, see if the manifest is in need of an + // update. We then want to delay until the buffer is ~halfway full. + ClearOldSegments(media_time); + auto delay_time = range_duration - (ideal_buffer_duration / 2); + + if (IsLive()) { + MaybeFetchManifestUpdates(std::move(time_remaining_cb), delay_time); + return; + } + std::move(time_remaining_cb).Run(delay_time); return; } - auto end_of_buffer_ts = std::get<1>(ranges.back()); + constexpr base::TimeDelta kMinimumSpottyBufferSeek = base::Milliseconds(17); + constexpr base::TimeDelta kMaximumGapSkipDistance = base::Seconds(2); + // When `media_time` is after the first buffer, it's because of a gap in + // playback. The criteria for finding a seek spot are as follows: + // 1) it must be within `kMaximumGapSkipDistance` of the end of buffer #1. + // 2) it must be in a range having a duration >= kMinimumSpottyBufferSeek + // 3) it must be the first range meeting these criteria + if (media_time > ranges.end(0)) { + for (size_t i = 1; i < ranges.size(); i++) { + auto distance = ranges.start(i) - ranges.end(0); + if (distance > kMaximumGapSkipDistance) { + HlsDemuxerStatus error = { + HlsDemuxerStatus::Codes::kInvalidLoadedRanges, + "Unable to seek past gap in buffered ranges - gap too large"}; + rendition_host_->Quit(std::move(error) + .WithData("media_time", media_time) + .WithData("ranges", ranges)); + return; + } - // We need data ASAP, because the media time is outside the loaded ranges - // due to a seek. - if (media_time > end_of_buffer_ts) { + auto duration = ranges.end(i) - ranges.start(i); + if (duration > kMinimumSpottyBufferSeek) { + // Respond with a notice to not keep checking state. We will clear data + // and request a seek to the start of the new range found. + engine_host_->Remove(role_, base::TimeDelta(), ranges.end(i - 1)); + ranges = engine_host_->GetBufferedRanges(role_); + if (ranges.empty()) { + rendition_host_->Quit({HlsDemuxerStatus::Codes::kInvalidLoadedRanges, + "Unloading disjoint buffered ranges failed"}); + return; + } + + engine_host_->RequestSeek(ranges.start(0)); + std::move(time_remaining_cb).Run(kNoTimestamp); + return; + } + } + + // There was no loaded range that starts more than `kMaximumGapSkipDistance` + // into the future, which means we're simultaneously in a state where there + // is a gap but also where we desperately need more data. If we fill the + // buffers more, the engine will re-check state immediately and we can + // hopefully find a buffer to seek into. TryFillingBuffers(std::move(time_remaining_cb), media_time); return; } - // Paused content should just pretend that it will be resumed at 1x playback - // speed, because that is by far the most common. - auto denominator_rate = playback_rate ? playback_rate : 1.0; - auto buffer_duration = (end_of_buffer_ts - media_time) / denominator_rate; - auto ideal_buffer_duration = GetIdealBufferSize(); - - if (buffer_duration < ideal_buffer_duration) { - // There is a buffer, but it's not as big as we would like it to be. - TryFillingBuffers(std::move(time_remaining_cb), media_time); + // If media time is before the first range, we might be able to seek to it + // if it's close enough. + if (ranges.start(0) - media_time < kMaximumGapSkipDistance) { + engine_host_->RequestSeek(ranges.start(0)); + std::move(time_remaining_cb).Run(kNoTimestamp); return; } - // Our buffers are in good shape. This is a good chance to clear out old - // data and then, for live content, see if the manifest is in need of an - // update. - ClearOldSegments(media_time); - - // We want to delay until the buffer is ~halfway full. - auto delay_time = buffer_duration - (ideal_buffer_duration / 2); - - if (IsLive()) { - // Use this time to consider updating the manifest. - MaybeFetchManifestUpdates(std::move(time_remaining_cb), delay_time); - return; - } - - std::move(time_remaining_cb).Run(delay_time); + // The only loaded range is far into the future, which is a weird place to + // be, and we don't have any way to recover from it. + HlsDemuxerStatus error = HlsDemuxerStatus::Codes::kInvalidLoadedRanges; + rendition_host_->Quit(std::move(error) + .WithData("media_time", media_time) + .WithData("ranges", ranges)); } void HlsRenditionImpl::TryFillingBuffers(ManifestDemuxer::DelayCallback delay, @@ -507,9 +562,12 @@ auto ranges = engine_host_->GetBufferedRanges(role_); media_log_->SetProperty<MediaLogProperty::kHlsBufferedRanges>(ranges); - if (ranges.size() && ranges.contains(ranges.size() - 1, required_time)) { - std::move(cb).Run(); - return; + if (ranges.size()) { + if (required_time >= ranges.start(0) && + required_time <= std::get<1>(ranges.back())) { + std::move(cb).Run(); + return; + } } // If the last range doesn't contain the timestamp, keep parsing until it
diff --git a/media/filters/hls_rendition_impl_unittest.cc b/media/filters/hls_rendition_impl_unittest.cc index 4de4eff..cf8f245 100644 --- a/media/filters/hls_rendition_impl_unittest.cc +++ b/media/filters/hls_rendition_impl_unittest.cc
@@ -855,4 +855,123 @@ task_environment_.RunUntilIdle(); } +TEST_F(HlsRenditionImplUnittest, TestCanPlayWhenThereIsAGap) { + auto rendition = MakeVodRendition(kDiscontinuous); + ASSERT_NE(rendition, nullptr); + std::string content = "123"; + Ranges<base::TimeDelta> empty_range; + Ranges<base::TimeDelta> split_range; + split_range.Add(base::Seconds(0), base::Milliseconds(998)); + split_range.Add(base::Seconds(3), base::Milliseconds(3999)); + EXPECT_CALL(*mock_mdeh_, GetBufferedRanges("test")) + .WillOnce(Return(empty_range)) // CheckState #1 + .WillOnce(Return(split_range)); // OnSegmentData #1 + RespondToUrl("https://example.com/bip00.ts", content); + RequireAppend(base::as_byte_span(content)); + rendition->CheckState(base::Seconds(0), 0.0, BindCheck0Sec()); +} + +TEST_F(HlsRenditionImplUnittest, TestCantSkipOverLargeGaps) { + auto rendition = MakeVodRendition(kDiscontinuous); + ASSERT_NE(rendition, nullptr); + std::string content = "123"; + Ranges<base::TimeDelta> split_range; + split_range.Add(base::Seconds(0), base::Milliseconds(998)); + split_range.Add(base::Seconds(3), base::Milliseconds(3999)); + EXPECT_CALL(*mock_mdeh_, GetBufferedRanges("test")) + .WillOnce(Return(split_range)); + EXPECT_CALL(*mock_hrh_, Quit(_)); + rendition->CheckState(base::Milliseconds(999), 0.0, BindCheckStateNoExpect()); +} + +TEST_F(HlsRenditionImplUnittest, TestCantSkipIntoTinyRangeMiddle) { + auto rendition = MakeVodRendition(kDiscontinuous); + ASSERT_NE(rendition, nullptr); + std::string content = "123"; + Ranges<base::TimeDelta> split_range; + Ranges<base::TimeDelta> truncated; + split_range.Add(base::Milliseconds(10), base::Milliseconds(80)); + split_range.Add(base::Milliseconds(100), base::Milliseconds(102)); + split_range.Add(base::Milliseconds(104), base::Milliseconds(112)); + split_range.Add(base::Milliseconds(114), base::Milliseconds(130)); + split_range.Add(base::Milliseconds(132), base::Milliseconds(190)); + truncated.Add(base::Milliseconds(132), base::Milliseconds(190)); + EXPECT_CALL(*mock_mdeh_, GetBufferedRanges("test")) + .WillOnce(Return(split_range)) + .WillOnce(Return(truncated)); + + EXPECT_CALL(*mock_mdeh_, + Remove(_, base::Seconds(0), base::Milliseconds(130))); + EXPECT_CALL(*mock_mdeh_, RequestSeek(base::Milliseconds(132))); + rendition->CheckState(base::Milliseconds(90), 0.0, + BindCheckState(kNoTimestamp)); +} + +TEST_F(HlsRenditionImplUnittest, TestSkipsAheadIfBehind) { + auto rendition = MakeVodRendition(kDiscontinuous); + ASSERT_NE(rendition, nullptr); + std::string content = "123"; + Ranges<base::TimeDelta> split_range; + split_range.Add(base::Milliseconds(100), base::Milliseconds(102)); + split_range.Add(base::Milliseconds(104), base::Milliseconds(112)); + split_range.Add(base::Milliseconds(114), base::Milliseconds(130)); + split_range.Add(base::Milliseconds(132), base::Milliseconds(190)); + EXPECT_CALL(*mock_mdeh_, GetBufferedRanges("test")) + .WillRepeatedly(Return(split_range)); + + EXPECT_CALL(*mock_mdeh_, RequestSeek(base::Milliseconds(100))); + rendition->CheckState(base::Milliseconds(0), 1, BindCheckState(kNoTimestamp)); +} + +TEST_F(HlsRenditionImplUnittest, TestCantSkipIntoTheFarFuture) { + auto rendition = MakeVodRendition(kDiscontinuous); + ASSERT_NE(rendition, nullptr); + std::string content = "123"; + Ranges<base::TimeDelta> split_range; + split_range.Add(base::Milliseconds(3100), base::Milliseconds(3102)); + split_range.Add(base::Milliseconds(3104), base::Milliseconds(3112)); + split_range.Add(base::Milliseconds(3114), base::Milliseconds(3130)); + split_range.Add(base::Milliseconds(3132), base::Milliseconds(3190)); + EXPECT_CALL(*mock_mdeh_, GetBufferedRanges("test")) + .WillOnce(Return(split_range)); + + EXPECT_CALL(*mock_hrh_, Quit(_)); + rendition->CheckState(base::Milliseconds(0), 1, BindCheckStateNoExpect()); +} + +TEST_F(HlsRenditionImplUnittest, TestWillDelayUntilRangeWhenBufferFull) { + auto rendition = MakeVodRendition(kDiscontinuous); + ASSERT_NE(rendition, nullptr); + Ranges<base::TimeDelta> split_range; + split_range.Add(base::Seconds(0), base::Seconds(2)); + split_range.Add(base::Seconds(3), base::Seconds(20)); + EXPECT_CALL(*mock_mdeh_, GetBufferedRanges("test")) + .WillRepeatedly(Return(split_range)); + + // The delay is set until the end of the first loaded range, because it is + // closer than the "ideal buffer size" + rendition->CheckState(base::Seconds(1), 1, BindCheckState(base::Seconds(1))); +} + +TEST_F(HlsRenditionImplUnittest, TestRemoveOldDataForSkipRemovesAllBuffers) { + auto rendition = MakeVodRendition(kDiscontinuous); + ASSERT_NE(rendition, nullptr); + std::string content = "123"; + Ranges<base::TimeDelta> split_range; + Ranges<base::TimeDelta> truncated; + split_range.Add(base::Milliseconds(10), base::Milliseconds(80)); + split_range.Add(base::Milliseconds(100), base::Milliseconds(102)); + split_range.Add(base::Milliseconds(104), base::Milliseconds(112)); + split_range.Add(base::Milliseconds(114), base::Milliseconds(130)); + split_range.Add(base::Milliseconds(132), base::Milliseconds(190)); + EXPECT_CALL(*mock_mdeh_, GetBufferedRanges("test")) + .WillOnce(Return(split_range)) + .WillOnce(Return(truncated)); + + EXPECT_CALL(*mock_mdeh_, + Remove(_, base::Seconds(0), base::Milliseconds(130))); + EXPECT_CALL(*mock_hrh_, Quit(_)); + rendition->CheckState(base::Milliseconds(90), 0.0, BindCheckStateNoExpect()); +} + } // namespace media
diff --git a/media/filters/memory_data_source.cc b/media/filters/memory_data_source.cc index 23364e7..0ae31c5 100644 --- a/media/filters/memory_data_source.cc +++ b/media/filters/memory_data_source.cc
@@ -12,6 +12,7 @@ #include <algorithm> #include "base/check.h" +#include "base/compiler_specific.h" #include "base/functional/callback.h" namespace media { @@ -44,7 +45,8 @@ if (clamped_size > 0) { DCHECK(data); - memcpy(data, data_ + base::checked_cast<size_t>(position), clamped_size); + memcpy(data, UNSAFE_TODO(data_ + base::checked_cast<size_t>(position)), + clamped_size); } std::move(read_cb).Run(clamped_size);
diff --git a/media/mojo/mojom/speech_recognition_result_mojom_traits.cc b/media/mojo/mojom/speech_recognition_result_mojom_traits.cc index ac77e65ee..ad9c4a67 100644 --- a/media/mojo/mojom/speech_recognition_result_mojom_traits.cc +++ b/media/mojo/mojom/speech_recognition_result_mojom_traits.cc
@@ -104,10 +104,6 @@ return false; } - // Timing information is provided only for final results. - if (!data.is_final() && timing_information.has_value()) - return false; - out->transcription = std::move(transcription); out->is_final = data.is_final(); out->timing_information = std::move(timing_information);
diff --git a/media/mojo/mojom/speech_recognition_result_mojom_traits_unittest.cc b/media/mojo/mojom/speech_recognition_result_mojom_traits_unittest.cc index 0b8720ff..87117da 100644 --- a/media/mojo/mojom/speech_recognition_result_mojom_traits_unittest.cc +++ b/media/mojo/mojom/speech_recognition_result_mojom_traits_unittest.cc
@@ -52,17 +52,22 @@ TEST(SpeechRecognitionResultStructTraitsTest, PartialResultWithTimingInformation) { - media::SpeechRecognitionResult invalid_result("hello world", false); - invalid_result.timing_information = media::TimingInformation(); - invalid_result.timing_information->audio_start_time = kZeroTime; - invalid_result.timing_information->audio_end_time = base::Seconds(1); - std::vector<uint8_t> invalid_data = - media::mojom::SpeechRecognitionResult::Serialize(&invalid_result); - media::SpeechRecognitionResult invalid_output; + media::SpeechRecognitionResult partial_result("hello world", false); + partial_result.timing_information = media::TimingInformation(); + partial_result.timing_information->audio_start_time = base::Seconds(1); + partial_result.timing_information->audio_end_time = base::Seconds(2); + partial_result.timing_information->originating_media_timestamps = + std::vector<media::MediaTimestampRange>(); + partial_result.timing_information->originating_media_timestamps->push_back( + {.start = base::Seconds(10), .end = base::Seconds(11)}); - // Partial results shouldn't have timing information. - EXPECT_FALSE(media::mojom::SpeechRecognitionResult::Deserialize( - std::move(invalid_data), &invalid_output)); + std::vector<uint8_t> data = + media::mojom::SpeechRecognitionResult::Serialize(&partial_result); + media::SpeechRecognitionResult output; + + EXPECT_TRUE(media::mojom::SpeechRecognitionResult::Deserialize( + std::move(data), &output)); + EXPECT_EQ(partial_result, output); } TEST(SpeechRecognitionResultStructTraitsTest, WithInvalidHypothesisParts) {
diff --git a/net/extras/preload_data/decoder.cc b/net/extras/preload_data/decoder.cc index e536d51..fc415ca 100644 --- a/net/extras/preload_data/decoder.cc +++ b/net/extras/preload_data/decoder.cc
@@ -20,7 +20,7 @@ if (current_byte_index_ >= num_bytes_) { return false; } - current_byte_ = bytes_[current_byte_index_++]; + current_byte_ = UNSAFE_TODO(bytes_[current_byte_index_++]); num_bits_used_ = 0; } @@ -137,7 +137,7 @@ return false; } current_byte_index_ = offset / 8; - current_byte_ = bytes_[current_byte_index_++]; + current_byte_ = UNSAFE_TODO(bytes_[current_byte_index_++]); num_bits_used_ = offset % 8; return true; } @@ -148,7 +148,7 @@ bool PreloadDecoder::HuffmanDecoder::Decode(PreloadDecoder::BitReader* reader, char* out) const { - const uint8_t* current = &tree_[tree_bytes_ - 2]; + const uint8_t* current = UNSAFE_TODO(&tree_[tree_bytes_ - 2]); for (;;) { bool bit; @@ -168,7 +168,7 @@ return false; } - current = &tree_[offset]; + current = UNSAFE_TODO(&tree_[offset]); } }
diff --git a/sandbox/linux/syscall_broker/broker_permission_list.cc b/sandbox/linux/syscall_broker/broker_permission_list.cc index 2627608..cc93c02 100644 --- a/sandbox/linux/syscall_broker/broker_permission_list.cc +++ b/sandbox/linux/syscall_broker/broker_permission_list.cc
@@ -12,6 +12,8 @@ #include <string> #include <vector> +#include "base/compiler_specific.h" + namespace sandbox { namespace syscall_broker { @@ -37,8 +39,8 @@ const char* requested_filename, int requested_mode) const { for (size_t i = 0; i < num_of_permissions_; i++) { - const char* ret = - permissions_array_[i].CheckAccess(requested_filename, requested_mode); + const char* ret = UNSAFE_TODO(permissions_array_[i]) + .CheckAccess(requested_filename, requested_mode); if (ret) { return ret; } @@ -51,7 +53,8 @@ int requested_flags) const { for (size_t i = 0; i < num_of_permissions_; i++) { std::pair<const char*, bool> ret = - permissions_array_[i].CheckOpen(requested_filename, requested_flags); + UNSAFE_TODO(permissions_array_[i]) + .CheckOpen(requested_filename, requested_flags); if (ret.first) { return ret; } @@ -62,8 +65,8 @@ const char* BrokerPermissionList::GetFileNameIfAllowedToStat( const char* requested_filename) const { for (size_t i = 0; i < num_of_permissions_; i++) { - const char* ret = - permissions_array_[i].CheckStatWithIntermediates(requested_filename); + const char* ret = UNSAFE_TODO(permissions_array_[i]) + .CheckStatWithIntermediates(requested_filename); if (ret) { return ret; } @@ -76,8 +79,8 @@ uint32_t mask) const { for (size_t i = 0; i < num_of_permissions_; i++) { const char* ret = - permissions_array_[i].CheckInotifyAddWatchWithIntermediates( - requested_filename, mask); + UNSAFE_TODO(permissions_array_[i]) + .CheckInotifyAddWatchWithIntermediates(requested_filename, mask); if (ret) { return ret; }
diff --git a/services/device/usb/fake_usb_device_handle.cc b/services/device/usb/fake_usb_device_handle.cc index 12fdb68b..9130ecd 100644 --- a/services/device/usb/fake_usb_device_handle.cc +++ b/services/device/usb/fake_usb_device_handle.cc
@@ -78,10 +78,11 @@ return; } - if (data_[position_++] % 2) { + if (UNSAFE_TODO(data_[position_++]) % 2) { size_t bytes_transferred = 0; if (position_ + 2 <= size_) { - bytes_transferred = data_[position_] | data_[position_ + 1] << 8; + bytes_transferred = UNSAFE_TODO(data_[position_]) | + UNSAFE_TODO(data_[position_ + 1]) << 8; position_ += 2; bytes_transferred = std::min(bytes_transferred, buffer->size()); bytes_transferred = std::min(bytes_transferred, size_ - position_);
diff --git a/services/metrics/public/cpp/ukm_recorder.h b/services/metrics/public/cpp/ukm_recorder.h index 5065ede..46686c2 100644 --- a/services/metrics/public/cpp/ukm_recorder.h +++ b/services/metrics/public/cpp/ukm_recorder.h
@@ -53,10 +53,6 @@ class NotificationContentDetectionUkmUtil; } -namespace weblayer { -class BackgroundSyncDelegateImpl; -} - namespace ukm { class DelegatingUkmRecorder; @@ -232,7 +228,6 @@ SourceIdType type); private: - friend weblayer::BackgroundSyncDelegateImpl; friend DelegatingUkmRecorder; friend TestRecordingHelper; friend UkmBackgroundRecorderService;
diff --git a/services/on_device_model/ml/on_device_model_executor.cc b/services/on_device_model/ml/on_device_model_executor.cc index 346d1a8..3b9821e6 100644 --- a/services/on_device_model/ml/on_device_model_executor.cc +++ b/services/on_device_model/ml/on_device_model_executor.cc
@@ -302,11 +302,6 @@ return base::unexpected( on_device_model::ServiceDisconnectReason::kFailedToLoadLibrary); } - if (!on_device_model::IsCpuCapable() && - ml::IsGpuBlocked(chrome_ml_->api(), /*log_histogram=*/false)) { - return base::unexpected( - on_device_model::ServiceDisconnectReason::kGpuBlocked); - } return base::ok(); }
diff --git a/services/webnn/webnn_context_impl.cc b/services/webnn/webnn_context_impl.cc index f039484..df1168b 100644 --- a/services/webnn/webnn_context_impl.cc +++ b/services/webnn/webnn_context_impl.cc
@@ -125,12 +125,6 @@ mojo_base::BigBuffer tensor_data, mojom::WebNNContext::CreateTensorCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - // Must use CreateTensorFromMailbox for WebGPU Interop. - if (tensor_info->usage.Has(MLTensorUsageFlags::kWebGpuInterop)) { - receiver_.ReportBadMessage(kBadMessageInvalidTensor); - return; - } - if (!ValidateTensor(properties_, tensor_info->descriptor).has_value()) { receiver_.ReportBadMessage(kBadMessageInvalidTensor); return;
diff --git a/testing/perf/cbb_ref_info/edge/dev/Windows.json b/testing/perf/cbb_ref_info/edge/dev/Windows.json index f44de4b..ca87c41b 100644 --- a/testing/perf/cbb_ref_info/edge/dev/Windows.json +++ b/testing/perf/cbb_ref_info/edge/dev/Windows.json
@@ -2,5 +2,5 @@ "browser": "Edge", "channel": "Dev", "platform": "Windows", - "version": "140.0.3430.1" + "version": "140.0.3456.0" }
diff --git a/testing/perf/cbb_ref_info/edge/stable/Windows.json b/testing/perf/cbb_ref_info/edge/stable/Windows.json index c1c2db7a..c66d483c 100644 --- a/testing/perf/cbb_ref_info/edge/stable/Windows.json +++ b/testing/perf/cbb_ref_info/edge/stable/Windows.json
@@ -2,5 +2,5 @@ "browser": "Edge", "channel": "Stable", "platform": "Windows", - "version": "138.0.3351.83" + "version": "138.0.3351.109" }
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index 750eac0..5eac219 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -3023,6 +3023,21 @@ ] } ], + "AvatarButtonSyncPromoWindows": [ + { + "platforms": [ + "windows" + ], + "experiments": [ + { + "name": "Enabled", + "enable_features": [ + "AvatarButtonSyncPromo" + ] + } + ] + } + ], "AvoidDuplicateDelayBeginFrame": [ { "platforms": [ @@ -5717,6 +5732,24 @@ ] } ], + "ClientSideDetectionClipboardCopyApi": [ + { + "platforms": [ + "chromeos", + "linux", + "mac", + "windows" + ], + "experiments": [ + { + "name": "Enabled", + "enable_features": [ + "ClientSideDetectionClipboardCopyApi" + ] + } + ] + } + ], "ClientSideDetectionLlamaForcedTriggerInfoForScamDetection": [ { "platforms": [ @@ -7451,6 +7484,28 @@ ] } ], + "DSEPrewarm": [ + { + "platforms": [ + "chromeos", + "linux", + "mac", + "windows" + ], + "experiments": [ + { + "name": "Enabled_20260624_Dogfood", + "params": { + "url": "https://www.google.com/", + "zero_suggest_trigger": "true" + }, + "enable_features": [ + "Prewarm" + ] + } + ] + } + ], "DTCAntivirusSignalEnabled": [ { "platforms": [ @@ -15706,7 +15761,7 @@ { "name": "Enabled", "params": { - "ConfigParam": "CgIIAw==" + "ConfigParam": "CgIIAxI8CAEQARoXCAAQ4MZbGMAMIMAMKCgyB2ltYWdlLyoiGwiAhK9fEhQucGRmLGFwcGxpY2F0aW9uL3BkZigB" }, "enable_features": [ "NtpComposebox" @@ -16609,24 +16664,6 @@ ] } ], - "PWANavigationCapturingV2ChromeOS": [ - { - "platforms": [ - "chromeos" - ], - "experiments": [ - { - "name": "EnabledSettingOffByDefault", - "params": { - "link_capturing_state": "reimpl_default_off" - }, - "enable_features": [ - "PwaNavigationCapturing" - ] - } - ] - } - ], "PWANavigationCapturingV2WindowMacLinux": [ { "platforms": [
diff --git a/third_party/angle b/third_party/angle index 5fd368a..b0e0c9f6 160000 --- a/third_party/angle +++ b/third_party/angle
@@ -1 +1 @@ -Subproject commit 5fd368aaf91c25a5fb8e4e214acb09fda36acdaa +Subproject commit b0e0c9f617d0b4d706cbc2b135843f2f4cdb714a
diff --git a/third_party/blink/renderer/bindings/core/v8/script_promise_tester.h b/third_party/blink/renderer/bindings/core/v8/script_promise_tester.h index af984db..5135345e 100644 --- a/third_party/blink/renderer/bindings/core/v8/script_promise_tester.h +++ b/third_party/blink/renderer/bindings/core/v8/script_promise_tester.h
@@ -5,6 +5,7 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_SCRIPT_PROMISE_TESTER_H_ #define THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_SCRIPT_PROMISE_TESTER_H_ +#include "base/memory/stack_allocated.h" #include "base/memory/weak_ptr.h" #include "third_party/blink/renderer/bindings/core/v8/script_promise.h" #include "third_party/blink/renderer/bindings/core/v8/script_value.h" @@ -106,7 +107,7 @@ } private: - GC_PLUGIN_IGNORE("Pointer to on-stack class is valid here.") + STACK_ALLOCATED_IGNORE("Pointer to on-stack class is valid here.") base::WeakPtr<ScriptPromiseTester> owner_; State target_state_; };
diff --git a/third_party/blink/renderer/core/animation/animation_trigger.cc b/third_party/blink/renderer/core/animation/animation_trigger.cc index b15020a..e51e388 100644 --- a/third_party/blink/renderer/core/animation/animation_trigger.cc +++ b/third_party/blink/renderer/core/animation/animation_trigger.cc
@@ -472,6 +472,14 @@ } } +void AnimationTrigger::RemoveAnimations() { + HeapHashSet<WeakMember<Animation>> animations; + animations_.swap(animations); + for (Animation* animation : animations) { + removeAnimation(animation); + } +} + void AnimationTrigger::UpdateAnimations( AnimationTrigger::UpdateType update_type) { DCHECK_NE(update_type, UpdateType::kNone);
diff --git a/third_party/blink/renderer/core/animation/animation_trigger.h b/third_party/blink/renderer/core/animation/animation_trigger.h index 6872112..e5451d2 100644 --- a/third_party/blink/renderer/core/animation/animation_trigger.h +++ b/third_party/blink/renderer/core/animation/animation_trigger.h
@@ -125,6 +125,7 @@ void addAnimation(Animation* animation, ExceptionState& exception_state); void removeAnimation(Animation* animation); + void RemoveAnimations(); void Update(); void UpdateInternal(State old_state, State new_state);
diff --git a/third_party/blink/renderer/core/animation/animation_trigger_test.cc b/third_party/blink/renderer/core/animation/animation_trigger_test.cc index 63375a8..3756b4a0 100644 --- a/third_party/blink/renderer/core/animation/animation_trigger_test.cc +++ b/third_party/blink/renderer/core/animation/animation_trigger_test.cc
@@ -17,177 +17,161 @@ namespace blink { namespace { -// void ExpectRelativeErrorWithinEpsilon(double expected, double observed) { -// EXPECT_NEAR(1.0, observed / expected, -// std::numeric_limits<double>::epsilon()); -// } +void ExpectRelativeErrorWithinEpsilon(double expected, double observed) { + EXPECT_NEAR(1.0, observed / expected, std::numeric_limits<double>::epsilon()); +} } // namespace -// TODO(crbug.com/429392773): CSSAnimation Triggers are no longer computed -// during CSSAnimations::CalculateAnimationUpdate. Instead, because triggers -// need to be visible to the entire document, they will be computed and -// propagated up the fragment tree, similar to anchor-name. Update and -// re-enable these tests when that logic is implemented. +class AnimationTriggerTest : public PaintTestConfigurations, + public RenderingTest { + public: + AnimationTrigger::RangeBoundary* MakeRangeOffsetBoundary( + std::optional<V8TimelineRange::Enum> range, + std::optional<int> pct) { + TimelineRangeOffset* offset = MakeGarbageCollected<TimelineRangeOffset>(); + if (range) { + offset->setRangeName(V8TimelineRange(*range)); + } + if (pct) { + offset->setOffset( + CSSNumericValue::FromCSSValue(*CSSNumericLiteralValue::Create( + *pct, CSSNumericLiteralValue::UnitType::kPercentage))); + } + return MakeGarbageCollected<AnimationTrigger::RangeBoundary>(offset); + } +}; -// class AnimationTriggerTest : public PaintTestConfigurations, -// public RenderingTest { -// public: -// AnimationTrigger::RangeBoundary* MakeRangeOffsetBoundary( -// std::optional<V8TimelineRange::Enum> range, -// std::optional<int> pct) { -// TimelineRangeOffset* offset = -// MakeGarbageCollected<TimelineRangeOffset>(); if (range) { -// offset->setRangeName(V8TimelineRange(*range)); -// } -// if (pct) { -// offset->setOffset( -// CSSNumericValue::FromCSSValue(*CSSNumericLiteralValue::Create( -// *pct, CSSNumericLiteralValue::UnitType::kPercentage))); -// } -// return MakeGarbageCollected<AnimationTrigger::RangeBoundary>(offset); -// } -// }; +INSTANTIATE_PAINT_TEST_SUITE_P(AnimationTriggerTest); -// INSTANTIATE_PAINT_TEST_SUITE_P(AnimationTriggerTest); +TEST_P(AnimationTriggerTest, ComputeBoundariesTest) { + using RangeBoundary = AnimationTrigger::RangeBoundary; + using TriggerBoundaries = AnimationTrigger::TriggerBoundaries; + SetBodyInnerHTML(R"HTML( + <style> + @keyframes anim { + from { opacity: 0; } + to { opacity: 1; } + } + #scroller { + overflow-y: scroll; width: 100px; height: 100px; + } + #target { + animation: anim 1s both; + width: 100px; height: 50px; background: blue; + timeline-trigger: --trigger view(); + animation-trigger: --trigger; + } + #spacer { width: 200px; height: 200px; } + </style> + <div id ='scroller'> + <div id ='spacer'></div> + <div id ='target'></div> + <div id ='spacer'></div> + </div> + )HTML"); -// TEST_P(AnimationTriggerTest, ComputeBoundariesTest) { -// using RangeBoundary = AnimationTrigger::RangeBoundary; -// using TriggerBoundaries = AnimationTrigger::TriggerBoundaries; -// SetBodyInnerHTML(R"HTML( -// <style> -// @keyframes anim { -// from { opacity: 0; } -// to { opacity: 1; } -// } -// #scroller { -// overflow-y: scroll; width: 100px; height: 100px; -// } -// #target { -// animation: anim 1s both; -// width: 100px; height: 50px; background: blue; -// animation-trigger: view(); -// } -// #spacer { width: 200px; height: 200px; } -// </style> -// <div id ='scroller'> -// <div id ='spacer'></div> -// <div id ='target'></div> -// <div id ='spacer'></div> -// </div> -// )HTML"); + Element* target = GetDocument().getElementById(AtomicString("target")); + AnimationTrigger* trigger = target->NamedTriggers()->begin()->value.Get(); -// Element* target = GetDocument().getElementById(AtomicString("target")); -// ElementAnimations* animations = target->GetElementAnimations(); -// CSSAnimation* animation = -// DynamicTo<CSSAnimation>((*animations->Animations().begin()).key.Get()); -// AnimationTrigger* trigger = animation->GetTrigger(); + UpdateAllLifecyclePhasesForTest(); -// UpdateAllLifecyclePhasesForTest(); + ScrollTimeline& timeline = *To<ScrollTimeline>(trigger->timeline()); + Element& timeline_source = *To<Element>(timeline.ComputeResolvedSource()); -// ScrollTimeline& timeline = *To<ScrollTimeline>(trigger->timeline()); -// Element& timeline_source = *To<Element>(timeline.ComputeResolvedSource()); + RangeBoundary* cover_10 = + MakeRangeOffsetBoundary(V8TimelineRange::Enum::kCover, 10); + RangeBoundary* cover_90 = + MakeRangeOffsetBoundary(V8TimelineRange::Enum::kCover, 90); + RangeBoundary* contain_20 = + MakeRangeOffsetBoundary(V8TimelineRange::Enum::kContain, 20); + RangeBoundary* contain_80 = + MakeRangeOffsetBoundary(V8TimelineRange::Enum::kContain, 80); + AnimationTrigger::RangeBoundary* normal = + MakeGarbageCollected<RangeBoundary>("normal"); + AnimationTrigger::RangeBoundary* auto_offset = + MakeGarbageCollected<RangeBoundary>("auto"); -// RangeBoundary* cover_10 = -// MakeRangeOffsetBoundary(V8TimelineRange::Enum::kCover, 10); -// RangeBoundary* cover_90 = -// MakeRangeOffsetBoundary(V8TimelineRange::Enum::kCover, 90); -// RangeBoundary* contain_20 = -// MakeRangeOffsetBoundary(V8TimelineRange::Enum::kContain, 20); -// RangeBoundary* contain_80 = -// MakeRangeOffsetBoundary(V8TimelineRange::Enum::kContain, 80); -// AnimationTrigger::RangeBoundary* normal = -// MakeGarbageCollected<RangeBoundary>("normal"); -// AnimationTrigger::RangeBoundary* auto_offset = -// MakeGarbageCollected<RangeBoundary>("auto"); + // cover 0% -> 100px; + // cover 100% -> 250px; + // contain 0% -> 150px; + // contain 100% -> 200px; + double cover_0_px = 100; + double cover_100_px = 250; + double cover_10_px = cover_0_px + 0.1 * (cover_100_px - cover_0_px); + double cover_90_px = cover_0_px + 0.9 * (cover_100_px - cover_0_px); + double contain_0_px = 150; + double contain_100_px = 200; + double contain_20_px = contain_0_px + 0.2 * (contain_100_px - contain_0_px); + double contain_80_px = contain_0_px + 0.8 * (contain_100_px - contain_0_px); -// // cover 0% -> 100px; -// // cover 100% -> 250px; -// // contain 0% -> 150px; -// // contain 100% -> 200px; -// double cover_0_px = 100; -// double cover_100_px = 250; -// double cover_10_px = cover_0_px + 0.1 * (cover_100_px - cover_0_px); -// double cover_90_px = cover_0_px + 0.9 * (cover_100_px - cover_0_px); -// double contain_0_px = 150; -// double contain_100_px = 200; -// double contain_20_px = contain_0_px + 0.2 * (contain_100_px - -// contain_0_px); double contain_80_px = contain_0_px + 0.8 * (contain_100_px -// - contain_0_px); + // cover 10% cover 90% auto auto. + trigger->setRangeBoundariesForTest(cover_10, cover_90, auto_offset, + auto_offset); + double dummy_offset = 0; + TriggerBoundaries boundaries = trigger->ComputeTriggerBoundaries( + dummy_offset, timeline_source, timeline); + ExpectRelativeErrorWithinEpsilon(boundaries.start, cover_10_px); + ExpectRelativeErrorWithinEpsilon(boundaries.end, cover_90_px); + ExpectRelativeErrorWithinEpsilon(boundaries.exit_start, cover_10_px); + ExpectRelativeErrorWithinEpsilon(boundaries.exit_end, cover_90_px); -// // cover 10% cover 90% auto auto. -// trigger->setRangeBoundariesForTest(cover_10, cover_90, auto_offset, -// auto_offset); -// double dummy_offset = 0; -// TriggerBoundaries boundaries = trigger->ComputeTriggerBoundaries( -// dummy_offset, timeline_source, timeline); -// ExpectRelativeErrorWithinEpsilon(boundaries.start, cover_10_px); -// ExpectRelativeErrorWithinEpsilon(boundaries.end, cover_90_px); -// ExpectRelativeErrorWithinEpsilon(boundaries.exit_start, cover_10_px); -// ExpectRelativeErrorWithinEpsilon(boundaries.exit_end, cover_90_px); + // contain 20% contain 80% auto normal. + trigger->setRangeBoundariesForTest(contain_20, contain_80, auto_offset, + normal); + boundaries = trigger->ComputeTriggerBoundaries(dummy_offset, timeline_source, + timeline); + ExpectRelativeErrorWithinEpsilon(boundaries.start, contain_20_px); + ExpectRelativeErrorWithinEpsilon(boundaries.end, contain_80_px); + ExpectRelativeErrorWithinEpsilon(boundaries.exit_start, contain_20_px); + ExpectRelativeErrorWithinEpsilon(boundaries.exit_end, cover_100_px); -// // contain 20% contain 80% auto normal. -// trigger->setRangeBoundariesForTest(contain_20, contain_80, auto_offset, -// normal); -// boundaries = trigger->ComputeTriggerBoundaries(dummy_offset, -// timeline_source, -// timeline); -// ExpectRelativeErrorWithinEpsilon(boundaries.start, contain_20_px); -// ExpectRelativeErrorWithinEpsilon(boundaries.end, contain_80_px); -// ExpectRelativeErrorWithinEpsilon(boundaries.exit_start, contain_20_px); -// ExpectRelativeErrorWithinEpsilon(boundaries.exit_end, cover_100_px); + // cover 10% cover 90% normal auto. + trigger->setRangeBoundariesForTest(cover_10, cover_90, normal, auto_offset); + boundaries = trigger->ComputeTriggerBoundaries(dummy_offset, timeline_source, + timeline); + ExpectRelativeErrorWithinEpsilon(boundaries.start, cover_10_px); + ExpectRelativeErrorWithinEpsilon(boundaries.end, cover_90_px); + ExpectRelativeErrorWithinEpsilon(boundaries.exit_start, cover_0_px); + ExpectRelativeErrorWithinEpsilon(boundaries.exit_end, cover_90_px); -// // cover 10% cover 90% normal auto. -// trigger->setRangeBoundariesForTest(cover_10, cover_90, normal, -// auto_offset); boundaries = trigger->ComputeTriggerBoundaries(dummy_offset, -// timeline_source, -// timeline); -// ExpectRelativeErrorWithinEpsilon(boundaries.start, cover_10_px); -// ExpectRelativeErrorWithinEpsilon(boundaries.end, cover_90_px); -// ExpectRelativeErrorWithinEpsilon(boundaries.exit_start, cover_0_px); -// ExpectRelativeErrorWithinEpsilon(boundaries.exit_end, cover_90_px); + // contain 20% contain 80% normal normal. + trigger->setRangeBoundariesForTest(contain_20, contain_80, normal, normal); + boundaries = trigger->ComputeTriggerBoundaries(dummy_offset, timeline_source, + timeline); + ExpectRelativeErrorWithinEpsilon(boundaries.start, contain_20_px); + ExpectRelativeErrorWithinEpsilon(boundaries.end, contain_80_px); + ExpectRelativeErrorWithinEpsilon(boundaries.exit_start, cover_0_px); + ExpectRelativeErrorWithinEpsilon(boundaries.exit_end, cover_100_px); -// // contain 20% contain 80% normal normal. -// trigger->setRangeBoundariesForTest(contain_20, contain_80, normal, normal); -// boundaries = trigger->ComputeTriggerBoundaries(dummy_offset, -// timeline_source, -// timeline); -// ExpectRelativeErrorWithinEpsilon(boundaries.start, contain_20_px); -// ExpectRelativeErrorWithinEpsilon(boundaries.end, contain_80_px); -// ExpectRelativeErrorWithinEpsilon(boundaries.exit_start, cover_0_px); -// ExpectRelativeErrorWithinEpsilon(boundaries.exit_end, cover_100_px); + // contain 20% contain 80% cover 10% normal. + trigger->setRangeBoundariesForTest(contain_20, contain_80, cover_10, normal); + boundaries = trigger->ComputeTriggerBoundaries(dummy_offset, timeline_source, + timeline); + ExpectRelativeErrorWithinEpsilon(boundaries.start, contain_20_px); + ExpectRelativeErrorWithinEpsilon(boundaries.end, contain_80_px); + ExpectRelativeErrorWithinEpsilon(boundaries.exit_start, cover_10_px); + ExpectRelativeErrorWithinEpsilon(boundaries.exit_end, cover_100_px); -// // contain 20% contain 80% cover 10% normal. -// trigger->setRangeBoundariesForTest(contain_20, contain_80, cover_10, -// normal); boundaries = trigger->ComputeTriggerBoundaries(dummy_offset, -// timeline_source, -// timeline); -// ExpectRelativeErrorWithinEpsilon(boundaries.start, contain_20_px); -// ExpectRelativeErrorWithinEpsilon(boundaries.end, contain_80_px); -// ExpectRelativeErrorWithinEpsilon(boundaries.exit_start, cover_10_px); -// ExpectRelativeErrorWithinEpsilon(boundaries.exit_end, cover_100_px); + // contain 20% contain 80% cover 10% auto. + trigger->setRangeBoundariesForTest(contain_20, contain_80, cover_10, + auto_offset); + boundaries = trigger->ComputeTriggerBoundaries(dummy_offset, timeline_source, + timeline); + ExpectRelativeErrorWithinEpsilon(boundaries.start, contain_20_px); + ExpectRelativeErrorWithinEpsilon(boundaries.end, contain_80_px); + ExpectRelativeErrorWithinEpsilon(boundaries.exit_start, cover_10_px); + ExpectRelativeErrorWithinEpsilon(boundaries.exit_end, contain_80_px); -// // contain 20% contain 80% cover 10% auto. -// trigger->setRangeBoundariesForTest(contain_20, contain_80, cover_10, -// auto_offset); -// boundaries = trigger->ComputeTriggerBoundaries(dummy_offset, -// timeline_source, -// timeline); -// ExpectRelativeErrorWithinEpsilon(boundaries.start, contain_20_px); -// ExpectRelativeErrorWithinEpsilon(boundaries.end, contain_80_px); -// ExpectRelativeErrorWithinEpsilon(boundaries.exit_start, cover_10_px); -// ExpectRelativeErrorWithinEpsilon(boundaries.exit_end, contain_80_px); - -// // contain 20% contain 80% cover 10% cover 90%. -// trigger->setRangeBoundariesForTest(contain_20, contain_80, cover_10, -// cover_90); -// boundaries = trigger->ComputeTriggerBoundaries(dummy_offset, -// timeline_source, -// timeline); -// ExpectRelativeErrorWithinEpsilon(boundaries.start, contain_20_px); -// ExpectRelativeErrorWithinEpsilon(boundaries.end, contain_80_px); -// ExpectRelativeErrorWithinEpsilon(boundaries.exit_start, cover_10_px); -// ExpectRelativeErrorWithinEpsilon(boundaries.exit_end, cover_90_px); -// } + // contain 20% contain 80% cover 10% cover 90%. + trigger->setRangeBoundariesForTest(contain_20, contain_80, cover_10, + cover_90); + boundaries = trigger->ComputeTriggerBoundaries(dummy_offset, timeline_source, + timeline); + ExpectRelativeErrorWithinEpsilon(boundaries.start, contain_20_px); + ExpectRelativeErrorWithinEpsilon(boundaries.end, contain_80_px); + ExpectRelativeErrorWithinEpsilon(boundaries.exit_start, cover_10_px); + ExpectRelativeErrorWithinEpsilon(boundaries.exit_end, cover_90_px); +} } // namespace blink
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 3b4042d..0c55db6 100644 --- a/third_party/blink/renderer/core/animation/css/css_animations.cc +++ b/third_party/blink/renderer/core/animation/css/css_animations.cc
@@ -82,12 +82,15 @@ #include "third_party/blink/renderer/core/dom/element.h" #include "third_party/blink/renderer/core/dom/events/event_path.h" #include "third_party/blink/renderer/core/dom/layout_tree_builder_traversal.h" +#include "third_party/blink/renderer/core/dom/named_animation_trigger_map.h" #include "third_party/blink/renderer/core/dom/pseudo_element.h" #include "third_party/blink/renderer/core/dom/shadow_root.h" #include "third_party/blink/renderer/core/events/animation_event.h" #include "third_party/blink/renderer/core/events/transition_event.h" #include "third_party/blink/renderer/core/frame/web_feature.h" #include "third_party/blink/renderer/core/layout/layout_object.h" +#include "third_party/blink/renderer/core/layout/layout_object_inlines.h" +#include "third_party/blink/renderer/core/layout/physical_box_fragment.h" #include "third_party/blink/renderer/core/paint/paint_layer.h" #include "third_party/blink/renderer/core/probe/core_probes.h" #include "third_party/blink/renderer/core/style_property_shorthand.h" @@ -1176,6 +1179,83 @@ return changed_timelines; } +// static +void CSSAnimations::UpdateNamedTriggers( + const ComputedStyleBuilder& style_builder, + Element& element) { + NamedAnimationTriggerMap* existing_trigger_map = element.NamedTriggers(); + NamedAnimationTriggerMap new_trigger_map; + + bool is_layout_needed = false; + + if (const CSSAnimationData* data = style_builder.Animations()) { + const HeapVector<Member<const ScopedCSSName>>& trigger_names = + data->TimelineTriggerNameList(); + + const CSSAnimationUpdate& update = *GetPendingAnimationUpdate(element); + + for (wtf_size_t i = 0; i < trigger_names.size(); i++) { + Member<const ScopedCSSName> name = trigger_names[i]; + if (!name) { + continue; + } + + AnimationTrigger* existing_trigger = element.NamedTrigger(name); + AnimationTrigger* new_trigger = CSSAnimations::ComputeTimelineTrigger( + data, i, update, style_builder.EffectiveZoom(), &element, + existing_trigger); + + new_trigger_map.Set(name, new_trigger); + + if (new_trigger == existing_trigger) { + continue; + } + + if (existing_trigger) { + // If the previous trigger is now obsolete, disassociate it from its + // animations. + existing_trigger->RemoveAnimations(); + } + + // Make sure that the new trigger is propagated throughout the tree. + is_layout_needed = true; + } + } + + if (existing_trigger_map) { + for (const auto& entry : *existing_trigger_map) { + AnimationTrigger* trigger = entry.value; + const ScopedCSSName* name = entry.key; + if (new_trigger_map.Contains(name)) { + continue; + } + + // TODO: When the trigger map contains triggers from the subtree, ensure + // this cleanup affects only triggers owned by this element. Otherwise, + // we'd be disassociating triggers from the subtree. + + // NOTE: This is only okay as long as script has no way to + // access CSS triggers. If it becomes possible to reference a CSS + // trigger via script, we'll need a way to distinguish between + // animations that were attached to a trigger via CSS and animations + // that were attached to the trigger via script. We only want to remove + // the former here. + trigger->RemoveAnimations(); + + // Make sure the rest of the DOM knows this name is now obsolete. + is_layout_needed = true; + } + } + + element.SetNamedTriggers(std::move(new_trigger_map)); + + if (is_layout_needed) { + if (LayoutBox* box = element.GetLayoutBox()) { + box->SetNeedsLayout(layout_invalidation_reason::kStyleChange); + } + } +} + template <> const CSSScrollTimelineMap* CSSAnimations::GetExistingTimelines<CSSScrollTimelineMap>( @@ -1522,87 +1602,81 @@ return !existing_range_offset->offset() && !new_range_offset->offset(); } -// TODO(crbug.com/429392773): CSSAnimation Triggers are no longer computed -// during CSSAnimations::CalculateAnimationUpdate. Instead, because triggers -// need to be visible to the entire document, they will be computed and -// propagated up the fragment tree, similar to anchor-name. As such, these -// methods aren't used at the moment. Uncomment them when they are actually -// used. +bool AnimationTriggerRangeBoundariesUnchanged( + AnimationTrigger* trigger, + const AnimationTrigger::RangeBoundary* new_range_start, + const AnimationTrigger::RangeBoundary* new_range_end, + const AnimationTrigger::RangeBoundary* new_exit_range_start, + const AnimationTrigger::RangeBoundary* new_exit_range_end) { + DCHECK(trigger); + return AnimationTriggerBoundariesMatch(trigger->rangeStart(nullptr), + new_range_start) && + AnimationTriggerBoundariesMatch(trigger->rangeEnd(nullptr), + new_range_end) && + AnimationTriggerBoundariesMatch(trigger->exitRangeStart(nullptr), + new_exit_range_start) && + AnimationTriggerBoundariesMatch(trigger->exitRangeEnd(nullptr), + new_exit_range_end); +} -// bool AnimationTriggerRangeBoundariesUnchanged( -// AnimationTrigger* trigger, -// const AnimationTrigger::RangeBoundary* new_range_start, -// const AnimationTrigger::RangeBoundary* new_range_end, -// const AnimationTrigger::RangeBoundary* new_exit_range_start, -// const AnimationTrigger::RangeBoundary* new_exit_range_end) { -// DCHECK(trigger); -// return AnimationTriggerBoundariesMatch(trigger->rangeStart(nullptr), -// new_range_start) && -// AnimationTriggerBoundariesMatch(trigger->rangeEnd(nullptr), -// new_range_end) && -// AnimationTriggerBoundariesMatch(trigger->exitRangeStart(nullptr), -// new_exit_range_start) && -// AnimationTriggerBoundariesMatch(trigger->exitRangeEnd(nullptr), -// new_exit_range_end); -// } +AnimationTrigger* CSSAnimations::ComputeTimelineTrigger( + const CSSAnimationData* data, + wtf_size_t animation_index, + const CSSAnimationUpdate& update, + float zoom, + Element* element, + AnimationTrigger* existing_trigger) { + AnimationTimeline* existing_timeline = + (existing_trigger ? existing_trigger->GetTimelineInternal() : nullptr); + AnimationTimeline* new_timeline = + animation_index < data->TimelineTriggerTimelineList().size() + ? ComputeTimeline(element, + data->GetTimelineTriggerTimeline(animation_index), + update, existing_timeline) + : nullptr; + if (!new_timeline) { + new_timeline = &element->GetDocument().Timeline(); + } + EAnimationTriggerBehavior behavior = CSSAnimationData::GetRepeated( + data->TimelineTriggerBehaviorList(), animation_index); + V8AnimationTriggerBehavior new_behavior = + AnimationTrigger::ToV8TriggerBehavior(behavior); -// AnimationTrigger* CSSAnimations::ComputeTimelineTrigger( -// Element* element, -// const CSSAnimationData* data, -// wtf_size_t animation_index, -// const CSSAnimationUpdate& update, -// AnimationTrigger* existing_trigger, -// float zoom) { -// const StyleTimeline& style_trigger_timeline = -// data->GetTimelineTriggerTimeline(animation_index); -// AnimationTimeline* existing_timeline = -// (existing_trigger ? existing_trigger->GetTimelineInternal() : nullptr); -// AnimationTimeline* new_timeline = ComputeTimeline( -// element, style_trigger_timeline, update, existing_timeline); -// if (!new_timeline) { -// new_timeline = &element->GetDocument().Timeline(); -// } -// EAnimationTriggerBehavior behavior = CSSAnimationData::GetRepeated( -// data->TimelineTriggerBehaviorList(), animation_index); -// V8AnimationTriggerBehavior new_behavior = -// AnimationTrigger::ToV8TriggerBehavior(behavior); + const std::optional<TimelineOffset>& new_start_offset = + CSSAnimationData::GetRepeated(data->TimelineTriggerRangeStartList(), + animation_index); + const std::optional<TimelineOffset>& new_end_offset = + CSSAnimationData::GetRepeated(data->TimelineTriggerRangeEndList(), + animation_index); + const TimelineOffsetOrAuto& new_exit_start_offset = + CSSAnimationData::GetRepeated(data->TimelineTriggerExitRangeStartList(), + animation_index); + const TimelineOffsetOrAuto& new_exit_end_offset = + CSSAnimationData::GetRepeated(data->TimelineTriggerExitRangeEndList(), + animation_index); -// const std::optional<TimelineOffset>& new_start_offset = -// CSSAnimationData::GetRepeated(data->TimelineTriggerRangeStartList(), -// animation_index); -// const std::optional<TimelineOffset>& new_end_offset = -// CSSAnimationData::GetRepeated(data->TimelineTriggerRangeEndList(), -// animation_index); -// const TimelineOffsetOrAuto& new_exit_start_offset = -// CSSAnimationData::GetRepeated(data->TimelineTriggerExitRangeStartList(), -// animation_index); -// const TimelineOffsetOrAuto& new_exit_end_offset = -// CSSAnimationData::GetRepeated(data->TimelineTriggerExitRangeEndList(), -// animation_index); + Animation::RangeBoundary* new_range_start = + Animation::ToRangeBoundary(new_start_offset, zoom); + Animation::RangeBoundary* new_range_end = + Animation::ToRangeBoundary(new_end_offset, zoom); + Animation::RangeBoundary* new_exit_range_start = + Animation::ToRangeBoundary(new_exit_start_offset, zoom); + Animation::RangeBoundary* new_exit_range_end = + Animation::ToRangeBoundary(new_exit_end_offset, zoom); -// Animation::RangeBoundary* new_range_start = -// Animation::ToRangeBoundary(new_start_offset, zoom); -// Animation::RangeBoundary* new_range_end = -// Animation::ToRangeBoundary(new_end_offset, zoom); -// Animation::RangeBoundary* new_exit_range_start = -// Animation::ToRangeBoundary(new_exit_start_offset, zoom); -// Animation::RangeBoundary* new_exit_range_end = -// Animation::ToRangeBoundary(new_exit_end_offset, zoom); + bool need_new_trigger = !existing_trigger || + existing_timeline != new_timeline || + existing_trigger->behavior() != new_behavior || + !AnimationTriggerRangeBoundariesUnchanged( + existing_trigger, new_range_start, new_range_end, + new_exit_range_start, new_exit_range_end); -// bool need_new_trigger = !existing_trigger || -// existing_timeline != new_timeline || -// existing_trigger->behavior() != new_behavior || -// !AnimationTriggerRangeBoundariesUnchanged( -// existing_trigger, new_range_start, -// new_range_end, new_exit_range_start, -// new_exit_range_end); - -// return need_new_trigger -// ? MakeGarbageCollected<AnimationTrigger>( -// new_timeline, new_behavior, new_range_start, -// new_range_end, new_exit_range_start, new_exit_range_end) -// : existing_trigger; -// } + return need_new_trigger + ? MakeGarbageCollected<AnimationTrigger>( + new_timeline, new_behavior, new_range_start, new_range_end, + new_exit_range_start, new_exit_range_end) + : existing_trigger; +} CSSAnimations::CSSAnimations() = default;
diff --git a/third_party/blink/renderer/core/animation/css/css_animations.h b/third_party/blink/renderer/core/animation/css/css_animations.h index d7ed1e3a..177a393 100644 --- a/third_party/blink/renderer/core/animation/css/css_animations.h +++ b/third_party/blink/renderer/core/animation/css/css_animations.h
@@ -133,6 +133,9 @@ CSSAnimationUpdate&, ComputedStyleBuilder&); + static void UpdateNamedTriggers(const ComputedStyleBuilder& style_builder, + Element& element); + const CSSAnimationUpdate& PendingUpdate() const { return pending_update_; } void SetPendingUpdate(const CSSAnimationUpdate& update) { ClearPendingUpdate(); @@ -465,14 +468,13 @@ TransitionUpdateState& state, const PropertyHandle& transitioning_property); - // TODO(crbug.com/429392773): Uncomment and use this function. - // static AnimationTrigger* ComputeTimelineTrigger( - // Element* element, - // const CSSAnimationData* data, - // wtf_size_t animation_index, - // const CSSAnimationUpdate& update, - // AnimationTrigger* existing_trigger, - // float zoom); + static AnimationTrigger* ComputeTimelineTrigger( + const CSSAnimationData* data, + wtf_size_t animation_index, + const CSSAnimationUpdate& update, + float zoom, + Element* element, + AnimationTrigger* existing_trigger); class AnimationEventDelegate final : public AnimationEffect::EventDelegate { public:
diff --git a/third_party/blink/renderer/core/animation/css/css_animations_test.cc b/third_party/blink/renderer/core/animation/css/css_animations_test.cc index 66b312d..cb74700a 100644 --- a/third_party/blink/renderer/core/animation/css/css_animations_test.cc +++ b/third_party/blink/renderer/core/animation/css/css_animations_test.cc
@@ -18,6 +18,7 @@ #include "third_party/blink/renderer/core/dom/dom_token_list.h" #include "third_party/blink/renderer/core/dom/pseudo_element.h" #include "third_party/blink/renderer/core/layout/layout_object.h" +#include "third_party/blink/renderer/core/layout/layout_object_inlines.h" #include "third_party/blink/renderer/core/page/page_animator.h" #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h" #include "third_party/blink/renderer/platform/animation/compositor_animation.h" @@ -1281,825 +1282,845 @@ EXPECT_TRUE(trigger_names2.value().Contains(AtomicString("--trigger4"))); } -// TODO(crbug.com/429392773): CSSAnimation Triggers are no longer computed -// during CSSAnimations::CalculateAnimationUpdate. Instead, because triggers -// need to be visible to the entire document, they will be computed and -// propagated up the fragment tree, similar to anchor-name. Update and -// re-enable these tests when that logic is implemented. +void VerifyTriggerRangeBoundary( + const AnimationTrigger::RangeBoundary* actual, + const AnimationTrigger::RangeBoundary* expected) { + if (expected->IsString()) { + EXPECT_EQ(actual->GetAsString(), expected->GetAsString()); + } else { + TimelineRangeOffset* expected_offset = expected->GetAsTimelineRangeOffset(); + TimelineRangeOffset* actual_offset = actual->GetAsTimelineRangeOffset(); + if (expected_offset->hasRangeName()) { + EXPECT_EQ(expected_offset->rangeName(), actual_offset->rangeName()); + } -// void VerifyTriggerRangeBoundary( -// const AnimationTrigger::RangeBoundary* actual, -// const AnimationTrigger::RangeBoundary* expected) { -// if (expected->IsString()) { -// EXPECT_EQ(actual->GetAsString(), expected->GetAsString()); -// } else { -// TimelineRangeOffset* expected_offset = -// expected->GetAsTimelineRangeOffset(); TimelineRangeOffset* actual_offset -// = actual->GetAsTimelineRangeOffset(); if -// (expected_offset->hasRangeName()) { -// EXPECT_EQ(expected_offset->rangeName(), actual_offset->rangeName()); -// } + if (expected_offset->hasOffset()) { + EXPECT_TRUE(expected_offset->offset()->Equals(*actual_offset->offset())); + } + } +} -// if (expected_offset->hasOffset()) { -// EXPECT_TRUE(expected_offset->offset()->Equals(*actual_offset->offset())); -// } -// } -// } +class CSSAnimationsTriggerTest : public CSSAnimationsTest { + public: + using Behavior = AnimationTrigger::Behavior; -// class CSSAnimationsTriggerTest : public CSSAnimationsTest { -// public: -// using Behavior = AnimationTrigger::Behavior; + void TestAnimationTrigger( + AnimationTrigger* trigger, + AnimationTrigger::Behavior expected_behavior, + std::optional<bool> expect_view_timeline, + AnimationTrigger::RangeBoundary* expected_start, + AnimationTrigger::RangeBoundary* expected_end, + AnimationTrigger::RangeBoundary* expected_exit_start, + AnimationTrigger::RangeBoundary* expected_exit_end); -// void TestAnimationTrigger( -// AnimationTrigger* trigger, -// AnimationTrigger::Behavior expected_behavior, -// std::optional<bool> expect_view_timeline, -// AnimationTrigger::RangeBoundary* expected_start, -// AnimationTrigger::RangeBoundary* expected_end, -// AnimationTrigger::RangeBoundary* expected_exit_start, -// AnimationTrigger::RangeBoundary* expected_exit_end); + void TestRangeStartChange( + Element* target, + AtomicString new_class, + bool expect_same, + const AnimationTrigger::RangeBoundary* expected_bounday); -// void TestRangeStartChange( -// Element* target, -// Animation* animation, -// AtomicString new_class, -// bool expect_same, -// const AnimationTrigger::RangeBoundary* expected_bounday); + AnimationTrigger* TestAssociatedTrigger(Element& target, + AtomicString trigger_name) { + AnimationTrigger* trigger_in_target = GetTrigger(target); + EXPECT_NE(trigger_in_target, nullptr); + const ScopedCSSName* name_in_target = GetTriggerName(target); + EXPECT_NE(name_in_target, nullptr); + EXPECT_EQ(name_in_target->GetName(), trigger_name); -// AnimationTrigger::RangeBoundary* MakeRangeOffsetBoundary( -// std::optional<V8TimelineRange::Enum> range, -// std::optional<int> pct) { -// TimelineRangeOffset* offset = -// MakeGarbageCollected<TimelineRangeOffset>(); if (range) { -// offset->setRangeName(V8TimelineRange(*range)); -// } -// if (pct) { -// offset->setOffset( -// CSSNumericValue::FromCSSValue(*CSSNumericLiteralValue::Create( -// *pct, CSSNumericLiteralValue::UnitType::kPercentage))); -// } -// return MakeGarbageCollected<AnimationTrigger::RangeBoundary>(offset); -// } -// }; + return trigger_in_target; + } -// INSTANTIATE_PAINT_TEST_SUITE_P(CSSAnimationsTriggerTest); + const ScopedCSSName* GetTriggerName(Element& element) { + return element.NamedTriggers()->begin()->key; + } -// void CSSAnimationsTriggerTest::TestAnimationTrigger( -// AnimationTrigger* trigger, -// AnimationTrigger::Behavior expected_behavior, -// std::optional<bool> expect_view_timeline, -// AnimationTrigger::RangeBoundary* expected_start, -// AnimationTrigger::RangeBoundary* expected_end, -// AnimationTrigger::RangeBoundary* expected_exit_start, -// AnimationTrigger::RangeBoundary* expected_exit_end) { -// EXPECT_NE(trigger, nullptr); -// EXPECT_EQ(trigger->behavior(), expected_behavior); + AnimationTrigger* GetTrigger(Element& element) { + return element.NamedTriggers()->begin()->value; + } -// AnimationTimeline* timeline = trigger->timeline(); -// if (!expect_view_timeline.has_value()) { -// EXPECT_EQ(timeline, &GetDocument().Timeline()); -// } else if (expect_view_timeline.value() == false) { -// EXPECT_TRUE(timeline->IsScrollTimeline()); -// } else { -// EXPECT_TRUE(timeline->IsViewTimeline()); -// } + AnimationTrigger::RangeBoundary* MakeRangeOffsetBoundary( + std::optional<V8TimelineRange::Enum> range, + std::optional<int> pct) { + TimelineRangeOffset* offset = MakeGarbageCollected<TimelineRangeOffset>(); + if (range) { + offset->setRangeName(V8TimelineRange(*range)); + } + if (pct) { + offset->setOffset( + CSSNumericValue::FromCSSValue(*CSSNumericLiteralValue::Create( + *pct, CSSNumericLiteralValue::UnitType::kPercentage))); + } + return MakeGarbageCollected<AnimationTrigger::RangeBoundary>(offset); + } +}; -// const AnimationTrigger::RangeBoundary* range_start = -// trigger->rangeStart(nullptr); -// VerifyTriggerRangeBoundary(range_start, expected_start); +INSTANTIATE_PAINT_TEST_SUITE_P(CSSAnimationsTriggerTest); -// const AnimationTrigger::RangeBoundary* range_end = -// trigger->rangeEnd(nullptr); VerifyTriggerRangeBoundary(range_end, -// expected_end); +void CSSAnimationsTriggerTest::TestAnimationTrigger( + AnimationTrigger* trigger, + AnimationTrigger::Behavior expected_behavior, + std::optional<bool> expect_view_timeline, + AnimationTrigger::RangeBoundary* expected_start, + AnimationTrigger::RangeBoundary* expected_end, + AnimationTrigger::RangeBoundary* expected_exit_start, + AnimationTrigger::RangeBoundary* expected_exit_end) { + EXPECT_NE(trigger, nullptr); + EXPECT_EQ(trigger->behavior(), expected_behavior); -// const AnimationTrigger::RangeBoundary* exit_range_start = -// trigger->exitRangeStart(nullptr); -// VerifyTriggerRangeBoundary(exit_range_start, expected_exit_start); + AnimationTimeline* timeline = trigger->timeline(); + if (!expect_view_timeline.has_value()) { + EXPECT_EQ(timeline, &GetDocument().Timeline()); + } else if (expect_view_timeline.value() == false) { + EXPECT_TRUE(timeline->IsScrollTimeline()); + } else { + EXPECT_TRUE(timeline->IsViewTimeline()); + } -// const AnimationTrigger::RangeBoundary* exit_range_end = -// trigger->exitRangeEnd(nullptr); -// VerifyTriggerRangeBoundary(exit_range_end, expected_exit_end); -// } + const AnimationTrigger::RangeBoundary* range_start = + trigger->rangeStart(nullptr); + VerifyTriggerRangeBoundary(range_start, expected_start); -// TEST_P(CSSAnimationsTriggerTest, AnimationTriggerOnceOnly) { -// SetBodyInnerHTML(R"HTML( -// <style> -// @keyframes myAnim { -// from { transform: scaleX(1); } -// to { transform: scaleX(5); } -// } -// .subject { -// height: 50px; -// width: 50px; -// animation: myAnim linear 0.5s forwards; -// animation-trigger: once; -// } -// .scroller { -// overflow-y: scroll; -// height: 500px; -// width: 500px; -// border: solid 1px; -// position: relative; -// } -// #space { -// width: 50px; -// height: 600px; -// } -// </style> -// <div id="scroller" class="scroller"> -// <div id="space"></div> -// <div id="target" class="subject"></div> -// <div id="space"></div> -// </div> -// )HTML"); + const AnimationTrigger::RangeBoundary* range_end = trigger->rangeEnd(nullptr); + VerifyTriggerRangeBoundary(range_end, expected_end); -// Element* target = GetDocument().getElementById(AtomicString("target")); -// ElementAnimations* animations = target->GetElementAnimations(); -// CSSAnimation* animation = -// DynamicTo<CSSAnimation>((*animations->Animations().begin()).key.Get()); + const AnimationTrigger::RangeBoundary* exit_range_start = + trigger->exitRangeStart(nullptr); + VerifyTriggerRangeBoundary(exit_range_start, expected_exit_start); -// AnimationTrigger* trigger = animation->GetTrigger(); + const AnimationTrigger::RangeBoundary* exit_range_end = + trigger->exitRangeEnd(nullptr); + VerifyTriggerRangeBoundary(exit_range_end, expected_exit_end); +} -// AnimationTrigger::RangeBoundary* normal = -// MakeGarbageCollected<AnimationTrigger::RangeBoundary>("normal"); -// AnimationTrigger::RangeBoundary* auto_offset = -// MakeGarbageCollected<AnimationTrigger::RangeBoundary>("auto"); -// TestAnimationTrigger(trigger, -// V8AnimationTriggerBehavior(Behavior::Enum::kOnce), -// /* expect_view_timeline */ std::nullopt, normal, -// normal, auto_offset, auto_offset); -// } +TEST_P(CSSAnimationsTriggerTest, AnimationTriggerOnceOnly) { + SetBodyInnerHTML(R"HTML( + <style> + @keyframes myAnim { + from { transform: scaleX(1); } + to { transform: scaleX(5); } + } + .subject { + height: 50px; + width: 50px; + animation: myAnim linear 0.5s forwards; + timeline-trigger: --trigger once; + animation-trigger: --trigger; + } + .scroller { + overflow-y: scroll; + height: 500px; + width: 500px; + border: solid 1px; + position: relative; + } + #space { + width: 50px; + height: 600px; + } + </style> + <div id="scroller" class="scroller"> + <div id="space"></div> + <div id="target" class="subject"></div> + <div id="space"></div> + </div> + )HTML"); -// TEST_P(CSSAnimationsTriggerTest, AnimationTriggerViewOnly) { -// SetBodyInnerHTML(R"HTML( -// <style> -// @keyframes myAnim { -// from { transform: scaleX(1); } -// to { transform: scaleX(5); } -// } -// .subject { -// height: 50px; -// width: 50px; -// animation: myAnim linear 0.5s forwards; -// animation-trigger: view(); -// } -// .scroller { -// overflow-y: scroll; -// height: 500px; -// width: 500px; -// border: solid 1px; -// position: relative; -// } -// #space { -// width: 50px; -// height: 600px; -// } -// </style> -// <div id="scroller" class="scroller"> -// <div id="space"></div> -// <div id="target" class="subject"></div> -// <div id="space"></div> -// </div> -// )HTML"); + Element* target = GetDocument().getElementById(AtomicString("target")); -// Element* target = GetDocument().getElementById(AtomicString("target")); -// ElementAnimations* animations = target->GetElementAnimations(); -// CSSAnimation* animation = -// DynamicTo<CSSAnimation>((*animations->Animations().begin()).key.Get()); + AnimationTrigger* trigger = + TestAssociatedTrigger(*target, AtomicString("--trigger")); -// AnimationTrigger* trigger = animation->GetTrigger(); -// AnimationTrigger::RangeBoundary* normal = -// MakeGarbageCollected<AnimationTrigger::RangeBoundary>("normal"); -// AnimationTrigger::RangeBoundary* auto_offset = -// MakeGarbageCollected<AnimationTrigger::RangeBoundary>("auto"); -// TestAnimationTrigger(trigger, -// V8AnimationTriggerBehavior(Behavior::Enum::kOnce), -// /* expect_view_timeline */ true, normal, normal, -// auto_offset, auto_offset); -// } + AnimationTrigger::RangeBoundary* normal = + MakeGarbageCollected<AnimationTrigger::RangeBoundary>("normal"); + AnimationTrigger::RangeBoundary* auto_offset = + MakeGarbageCollected<AnimationTrigger::RangeBoundary>("auto"); + TestAnimationTrigger(trigger, + V8AnimationTriggerBehavior(Behavior::Enum::kOnce), + /* expect_view_timeline */ std::nullopt, normal, normal, + auto_offset, auto_offset); +} -// TEST_P(CSSAnimationsTriggerTest, AnimationTriggerScrollOnce) { -// SetBodyInnerHTML(R"HTML( -// <style> -// @keyframes myAnim { -// from { transform: scaleX(1); } -// to { transform: scaleX(5); } -// } -// .subject { -// height: 50px; -// width: 50px; -// animation: myAnim linear 0.5s forwards; -// animation-trigger: scroll() once 25% 75%; -// } -// .scroller { -// overflow-y: scroll; -// height: 500px; -// width: 500px; -// border: solid 1px; -// position: relative; -// } -// #space { -// width: 50px; -// height: 600px; -// } -// </style> -// <div id="scroller" class="scroller"> -// <div id="space"></div> -// <div id="target" class="subject"></div> -// <div id="space"></div> -// </div> -// )HTML"); +TEST_P(CSSAnimationsTriggerTest, AnimationTriggerViewOnly) { + SetBodyInnerHTML(R"HTML( + <style> + @keyframes myAnim { + from { transform: scaleX(1); } + to { transform: scaleX(5); } + } + .subject { + height: 50px; + width: 50px; + animation: myAnim linear 0.5s forwards; + timeline-trigger: --trigger view(); + animation-trigger: --trigger; + } + .scroller { + overflow-y: scroll; + height: 500px; + width: 500px; + border: solid 1px; + position: relative; + } + #space { + width: 50px; + height: 600px; + } + </style> + <div id="scroller" class="scroller"> + <div id="space"></div> + <div id="target" class="subject"></div> + <div id="space"></div> + </div> + )HTML"); -// Element* target = GetDocument().getElementById(AtomicString("target")); -// ElementAnimations* animations = target->GetElementAnimations(); -// CSSAnimation* animation = -// DynamicTo<CSSAnimation>((*animations->Animations().begin()).key.Get()); + Element* target = GetDocument().getElementById(AtomicString("target")); -// AnimationTrigger* trigger = animation->GetTrigger(); + AnimationTrigger* trigger = + TestAssociatedTrigger(*target, AtomicString("--trigger")); -// AnimationTrigger::RangeBoundary* pct25 = -// MakeRangeOffsetBoundary(std::nullopt, 25); -// AnimationTrigger::RangeBoundary* pct75 = -// MakeRangeOffsetBoundary(std::nullopt, 75); -// AnimationTrigger::RangeBoundary* auto_offset = -// MakeGarbageCollected<AnimationTrigger::RangeBoundary>("auto"); + AnimationTrigger::RangeBoundary* normal = + MakeGarbageCollected<AnimationTrigger::RangeBoundary>("normal"); + AnimationTrigger::RangeBoundary* auto_offset = + MakeGarbageCollected<AnimationTrigger::RangeBoundary>("auto"); + TestAnimationTrigger(trigger, + V8AnimationTriggerBehavior(Behavior::Enum::kOnce), + /* expect_view_timeline */ true, normal, normal, + auto_offset, auto_offset); +} -// TestAnimationTrigger( -// trigger, V8AnimationTriggerBehavior(Behavior::Enum::kOnce), -// /* expect_view_timeline */ false, pct25, pct75, auto_offset, -// auto_offset); -// } +TEST_P(CSSAnimationsTriggerTest, AnimationTriggerScrollOnce) { + SetBodyInnerHTML(R"HTML( + <style> + @keyframes myAnim { + from { transform: scaleX(1); } + to { transform: scaleX(5); } + } + .subject { + height: 50px; + width: 50px; + animation: myAnim linear 0.5s forwards; + timeline-trigger: --trigger scroll() once 25% 75%; + animation-trigger: --trigger; + } + .scroller { + overflow-y: scroll; + height: 500px; + width: 500px; + border: solid 1px; + position: relative; + } + #space { + width: 50px; + height: 600px; + } + </style> + <div id="scroller" class="scroller"> + <div id="space"></div> + <div id="target" class="subject"></div> + <div id="space"></div> + </div> + )HTML"); -// TEST_P(CSSAnimationsTriggerTest, AnimationTriggerViewAlternate) { -// SetBodyInnerHTML(R"HTML( -// <style> -// @keyframes myAnim { -// from { transform: scaleX(1); } -// to { transform: scaleX(5); } -// } -// .subject { -// height: 50px; -// width: 50px; -// animation: myAnim linear 0.5s forwards; -// animation-trigger: view() alternate contain 10% contain 90%; -// } -// .scroller { -// overflow-y: scroll; -// height: 500px; -// width: 500px; -// border: solid 1px; -// position: relative; -// } -// #space { -// width: 50px; -// height: 600px; -// } -// </style> -// <div id="scroller" class="scroller"> -// <div id="space"></div> -// <div id="target" class="subject"></div> -// <div id="space"></div> -// </div> -// )HTML"); + Element* target = GetDocument().getElementById(AtomicString("target")); -// Element* target = GetDocument().getElementById(AtomicString("target")); + AnimationTrigger* trigger = + TestAssociatedTrigger(*target, AtomicString("--trigger")); -// ElementAnimations* animations = target->GetElementAnimations(); -// CSSAnimation* animation = -// DynamicTo<CSSAnimation>((*animations->Animations().begin()).key.Get()); + AnimationTrigger::RangeBoundary* pct25 = + MakeRangeOffsetBoundary(std::nullopt, 25); + AnimationTrigger::RangeBoundary* pct75 = + MakeRangeOffsetBoundary(std::nullopt, 75); + AnimationTrigger::RangeBoundary* auto_offset = + MakeGarbageCollected<AnimationTrigger::RangeBoundary>("auto"); -// AnimationTrigger* trigger = animation->GetTrigger(); -// EXPECT_NE(trigger, nullptr); + TestAnimationTrigger( + trigger, V8AnimationTriggerBehavior(Behavior::Enum::kOnce), + /* expect_view_timeline */ false, pct25, pct75, auto_offset, auto_offset); +} -// AnimationTrigger::RangeBoundary* contain10 = -// MakeRangeOffsetBoundary(V8TimelineRange::Enum::kContain, 10); -// AnimationTrigger::RangeBoundary* contain90 = -// MakeRangeOffsetBoundary(V8TimelineRange::Enum::kContain, 90); -// AnimationTrigger::RangeBoundary* auto_offset = -// MakeGarbageCollected<AnimationTrigger::RangeBoundary>("auto"); +TEST_P(CSSAnimationsTriggerTest, AnimationTriggerViewAlternate) { + SetBodyInnerHTML(R"HTML( + <style> + @keyframes myAnim { + from { transform: scaleX(1); } + to { transform: scaleX(5); } + } + .subject { + height: 50px; + width: 50px; + animation: myAnim linear 0.5s forwards; + timeline-trigger: --trigger view() alternate contain 10% contain 90%; + animation-trigger: --trigger; + } + .scroller { + overflow-y: scroll; + height: 500px; + width: 500px; + border: solid 1px; + position: relative; + } + #space { + width: 50px; + height: 600px; + } + </style> + <div id="scroller" class="scroller"> + <div id="space"></div> + <div id="target" class="subject"></div> + <div id="space"></div> + </div> + )HTML"); -// TestAnimationTrigger(trigger, -// V8AnimationTriggerBehavior(Behavior::Enum::kAlternate), -// /* expect_view_timeline */ true, contain10, contain90, -// auto_offset, auto_offset); -// } + Element* target = GetDocument().getElementById(AtomicString("target")); -// TEST_P(CSSAnimationsTriggerTest, AnimationTriggerViewRepeat) { -// SetBodyInnerHTML(R"HTML( -// <style> -// @keyframes myAnim { -// from { transform: scaleX(1); } -// to { transform: scaleX(5); } -// } -// .subject { -// height: 50px; -// width: 50px; -// animation: myAnim linear 0.5s forwards; -// animation-trigger: view() repeat contain 10% contain 90% cover 1% -// cover 99%; -// } -// .scroller { -// overflow-y: scroll; -// height: 500px; -// width: 500px; -// border: solid 1px; -// position: relative; -// } -// #space { -// width: 50px; -// height: 600px; -// } -// </style> -// <div id="scroller" class="scroller"> -// <div id="space"></div> -// <div id="target" class="subject"></div> -// <div id="space"></div> -// </div> -// )HTML"); + AnimationTrigger* trigger = + TestAssociatedTrigger(*target, AtomicString("--trigger")); -// Element* target = GetDocument().getElementById(AtomicString("target")); + AnimationTrigger::RangeBoundary* contain10 = + MakeRangeOffsetBoundary(V8TimelineRange::Enum::kContain, 10); + AnimationTrigger::RangeBoundary* contain90 = + MakeRangeOffsetBoundary(V8TimelineRange::Enum::kContain, 90); + AnimationTrigger::RangeBoundary* auto_offset = + MakeGarbageCollected<AnimationTrigger::RangeBoundary>("auto"); -// ElementAnimations* animations = target->GetElementAnimations(); -// CSSAnimation* animation = -// DynamicTo<CSSAnimation>((*animations->Animations().begin()).key.Get()); + TestAnimationTrigger(trigger, + V8AnimationTriggerBehavior(Behavior::Enum::kAlternate), + /* expect_view_timeline */ true, contain10, contain90, + auto_offset, auto_offset); +} -// AnimationTrigger* trigger = animation->GetTrigger(); -// EXPECT_NE(trigger, nullptr); +TEST_P(CSSAnimationsTriggerTest, AnimationTriggerViewRepeat) { + SetBodyInnerHTML(R"HTML( + <style> + @keyframes myAnim { + from { transform: scaleX(1); } + to { transform: scaleX(5); } + } + .subject { + height: 50px; + width: 50px; + animation: myAnim linear 0.5s forwards; + timeline-trigger: --trigger view() repeat contain 10% contain 90% + cover 1% cover 99%; + animation-trigger: --trigger; + } + .scroller { + overflow-y: scroll; + height: 500px; + width: 500px; + border: solid 1px; + position: relative; + } + #space { + width: 50px; + height: 600px; + } + </style> + <div id="scroller" class="scroller"> + <div id="space"></div> + <div id="target" class="subject"></div> + <div id="space"></div> + </div> + )HTML"); -// AnimationTrigger::RangeBoundary* contain10 = -// MakeRangeOffsetBoundary(V8TimelineRange::Enum::kContain, 10); -// AnimationTrigger::RangeBoundary* contain90 = -// MakeRangeOffsetBoundary(V8TimelineRange::Enum::kContain, 90); -// AnimationTrigger::RangeBoundary* cover1 = -// MakeRangeOffsetBoundary(V8TimelineRange::Enum::kCover, 1); -// AnimationTrigger::RangeBoundary* cover99 = -// MakeRangeOffsetBoundary(V8TimelineRange::Enum::kCover, 99); + Element* target = GetDocument().getElementById(AtomicString("target")); -// TestAnimationTrigger(trigger, -// V8AnimationTriggerBehavior(Behavior::Enum::kRepeat), -// true, contain10, contain90, cover1, cover99); -// } + AnimationTrigger* trigger = + TestAssociatedTrigger(*target, AtomicString("--trigger")); -// TEST_P(CSSAnimationsTriggerTest, AnimationTriggerNamedTimeline) { -// SetBodyInnerHTML(R"HTML( -// <style> -// @keyframes myAnim { -// from { transform: scaleX(1); } -// to { transform: scaleX(5); } -// } -// .subject { -// height: 50px; -// width: 50px; -// view-timeline: --viewtimeline; -// } -// #target { -// animation: myAnim linear 0.5s forwards; -// animation-trigger: --viewtimeline repeat contain 10% contain 90%; -// } -// .scroller { -// overflow-y: scroll; -// height: 500px; -// width: 500px; -// border: solid 1px; -// position: relative; -// } -// #space { -// width: 50px; -// height: 600px; -// } -// #wrapper { -// timeline-scope: --viewtimeline; -// } -// </style> -// <div id="wrapper"> -// <div id="scroller" class="scroller"> -// <div id="space"></div> -// <div class="subject"></div> -// <div id="space"></div> -// </div> -// <div id="target"></div> -// </div> -// )HTML"); + AnimationTrigger::RangeBoundary* contain10 = + MakeRangeOffsetBoundary(V8TimelineRange::Enum::kContain, 10); + AnimationTrigger::RangeBoundary* contain90 = + MakeRangeOffsetBoundary(V8TimelineRange::Enum::kContain, 90); + AnimationTrigger::RangeBoundary* cover1 = + MakeRangeOffsetBoundary(V8TimelineRange::Enum::kCover, 1); + AnimationTrigger::RangeBoundary* cover99 = + MakeRangeOffsetBoundary(V8TimelineRange::Enum::kCover, 99); -// Element* target = GetDocument().getElementById(AtomicString("target")); + TestAnimationTrigger(trigger, + V8AnimationTriggerBehavior(Behavior::Enum::kRepeat), + true, contain10, contain90, cover1, cover99); +} -// ElementAnimations* animations = target->GetElementAnimations(); -// CSSAnimation* animation = -// DynamicTo<CSSAnimation>((*animations->Animations().begin()).key.Get()); +TEST_P(CSSAnimationsTriggerTest, AnimationTriggerNamedTimeline) { + SetBodyInnerHTML(R"HTML( + <style> + @keyframes myAnim { + from { transform: scaleX(1); } + to { transform: scaleX(5); } + } + .subject { + height: 50px; + width: 50px; + view-timeline: --viewtimeline; + } + #target { + animation: myAnim linear 0.5s forwards; + timeline-trigger: --trigger --viewtimeline repeat contain 10% contain 90%; + animation-trigger: --trigger; + } + .scroller { + overflow-y: scroll; + height: 500px; + width: 500px; + border: solid 1px; + position: relative; + } + #space { + width: 50px; + height: 600px; + } + #wrapper { + timeline-scope: --viewtimeline; + } + </style> + <div id="wrapper"> + <div id="scroller" class="scroller"> + <div id="space"></div> + <div class="subject"></div> + <div id="space"></div> + </div> + <div id="target"></div> + </div> + )HTML"); -// AnimationTrigger* trigger = animation->GetTrigger(); -// EXPECT_NE(trigger, nullptr); + Element* target = GetDocument().getElementById(AtomicString("target")); -// EXPECT_EQ(trigger->behavior(), V8AnimationTriggerBehavior::Enum::kRepeat); + AnimationTrigger* trigger = GetTrigger(*target); -// EXPECT_FALSE(trigger->GetTimelineInternal()->IsScrollTimeline()); -// EXPECT_TRUE(trigger->timeline()->IsViewTimeline()); -// } + EXPECT_EQ(trigger->behavior(), V8AnimationTriggerBehavior::Enum::kRepeat); + + EXPECT_FALSE(trigger->GetTimelineInternal()->IsScrollTimeline()); + EXPECT_TRUE(trigger->timeline()->IsViewTimeline()); +} + +TEST_P(CSSAnimationsTriggerTest, AnimationTriggerChangeTimeline) { + SetBodyInnerHTML(R"HTML( + <style> + @keyframes stretch { + from { transform: scaleX(1); } + to { transform: scaleX(5); } + } + .subject { + height: 50px; + width: 50px; + view-timeline-name: --viewtimeline; + } + #target { + animation: stretch linear 0.5s forwards; + } + .view_trigger { + timeline-trigger: --trigger --viewtimeline repeat contain 10% contain 90%; + } + .scroll_trigger { + timeline-trigger: --trigger --scrolltimeline repeat contain 10% contain 90%; + } + .scroller { + overflow-y: scroll; + height: 500px; + width: 500px; + border: solid 1px; + position: relative; + scroll-timeline-name: --scrolltimeline; + } + #space { + width: 50px; + height: 600px; + } + #wrapper { + timeline-scope: --scrolltimeline, --viewtimeline; + } + </style> + <div id="wrapper"> + <div id="scroller" class="scroller"> + <div id="space"></div> + <div class="subject"></div> + <div id="space"></div> + </div> + <div id="target"></div> + </div> + )HTML"); -// TEST_P(CSSAnimationsTriggerTest, AnimationTriggerChangeTimeline) { -// SetBodyInnerHTML(R"HTML( -// <style> -// @keyframes stretch { -// from { transform: scaleX(1); } -// to { transform: scaleX(5); } -// } -// .subject { -// height: 50px; -// width: 50px; -// } -// #target { -// animation: stretch linear 0.5s forwards; -// } -// .view_trigger { -// animation-trigger: view() repeat contain 10% contain 90%; -// } -// .scroll_trigger { -// animation-trigger: --scrolltimeline repeat contain 10% contain 90%; -// } -// .scroller { -// overflow-y: scroll; -// height: 500px; -// width: 500px; -// border: solid 1px; -// position: relative; -// scroll-timeline-name: --scrolltimeline; -// } -// #space { -// width: 50px; -// height: 600px; -// } -// #wrapper { -// timeline-scope: --scrolltimeline; -// } -// </style> -// <div id="wrapper"> -// <div id="scroller" class="scroller"> -// <div id="space"></div> -// <div class="subject"></div> -// <div id="space"></div> -// </div> -// <div id="target"></div> -// </div> -// )HTML"); + Element* target = GetDocument().getElementById(AtomicString("target")); -// Element* target = GetDocument().getElementById(AtomicString("target")); -// UpdateAllLifecyclePhasesForTest(); + target->setAttribute(html_names::kClassAttr, AtomicString("view_trigger")); + UpdateAllLifecyclePhasesForTest(); + AnimationTrigger* view_trigger = GetTrigger(*target); -// ElementAnimations* animations = target->GetElementAnimations(); -// CSSAnimation* animation = -// DynamicTo<CSSAnimation>((*animations->Animations().begin()).key.Get()); + EXPECT_NE(view_trigger->timeline(), nullptr); + EXPECT_TRUE(view_trigger->timeline()->IsViewTimeline()); -// AnimationTrigger* trigger = animation->GetTrigger(); -// EXPECT_NE(trigger, nullptr); + target->setAttribute(html_names::kClassAttr, AtomicString("scroll_trigger")); + UpdateAllLifecyclePhasesForTest(); + AnimationTrigger* scroll_trigger = GetTrigger(*target); -// EXPECT_EQ(trigger->timeline(), &GetDocument().Timeline()); + EXPECT_NE(view_trigger, scroll_trigger); + EXPECT_NE(scroll_trigger->GetTimelineInternal(), nullptr); + EXPECT_FALSE(scroll_trigger->GetTimelineInternal()->IsScrollTimeline()); + EXPECT_FALSE(scroll_trigger->timeline()->IsViewTimeline()); + EXPECT_TRUE(scroll_trigger->timeline()->IsScrollTimeline()); +} -// target->setAttribute(html_names::kClassAttr, AtomicString("view_trigger")); -// UpdateAllLifecyclePhasesForTest(); +TEST_P(CSSAnimationsTriggerTest, AnimationTriggerChangeBehavior) { + SetBodyInnerHTML(R"HTML( + <style> + @keyframes stretch { + from { transform: scaleX(1); } + to { transform: scaleX(5); } + } + #target { + height: 50px; + width: 50px; + animation: stretch 1s; + } + .repeat_trigger { + timeline-trigger: --trigger view() repeat contain 10% contain 90%; + } + .once_trigger { + timeline-trigger: --trigger view() once contain 10% contain 90%; + } + .scroller { + overflow-y: scroll; + height: 500px; + width: 500px; + border: solid 1px; + position: relative; + } + #space { + width: 50px; + height: 600px; + } + </style> + <div id="wrapper"> + <div id="scroller" class="scroller"> + <div id="space"></div> + <div id="target"></div> + <div id="space"></div> + </div> + </div> + )HTML"); + Element* target = GetDocument().getElementById(AtomicString("target")); -// AnimationTrigger* view_trigger = animation->GetTrigger(); -// EXPECT_NE(trigger, view_trigger); -// EXPECT_NE(view_trigger->timeline(), nullptr); -// EXPECT_TRUE(view_trigger->timeline()->IsViewTimeline()); + target->classList().Add(AtomicString("repeat_trigger")); + UpdateAllLifecyclePhasesForTest(); -// target->setAttribute(html_names::kClassAttr, -// AtomicString("scroll_trigger")); UpdateAllLifecyclePhasesForTest(); + AnimationTrigger* repeat_trigger = + TestAssociatedTrigger(*target, AtomicString("--trigger")); + EXPECT_EQ(repeat_trigger->behavior(), + AnimationTrigger::Behavior::Enum::kRepeat); -// AnimationTrigger* scroll_trigger = animation->GetTrigger(); -// EXPECT_NE(view_trigger, scroll_trigger); -// EXPECT_NE(scroll_trigger->GetTimelineInternal(), nullptr); -// EXPECT_FALSE(scroll_trigger->GetTimelineInternal()->IsScrollTimeline()); -// EXPECT_FALSE(scroll_trigger->timeline()->IsViewTimeline()); -// EXPECT_TRUE(scroll_trigger->timeline()->IsScrollTimeline()); -// } + target->classList().Remove(AtomicString("repeat_trigger")); + target->classList().Add(AtomicString("once_trigger")); + UpdateAllLifecyclePhasesForTest(); -// TEST_P(CSSAnimationsTriggerTest, AnimationTriggerChangeBehavior) { -// SetBodyInnerHTML(R"HTML( -// <style> -// @keyframes stretch { -// from { transform: scaleX(1); } -// to { transform: scaleX(5); } -// } -// .subject { -// height: 50px; -// width: 50px; -// } -// #target { -// animation: stretch linear 0.5s forwards; -// } -// .repeat_trigger { -// animation-trigger: view() repeat contain 10% contain 90%; -// } -// .once_trigger { -// animation-trigger: view() once contain 10% contain 90%; -// } -// .scroller { -// overflow-y: scroll; -// height: 500px; -// width: 500px; -// border: solid 1px; -// position: relative; -// } -// #space { -// width: 50px; -// height: 600px; -// } -// </style> -// <div id="wrapper"> -// <div id="scroller" class="scroller"> -// <div id="space"></div> -// <div class="subject"></div> -// <div id="space"></div> -// </div> -// <div id="target"></div> -// </div> -// )HTML"); + AnimationTrigger* once_trigger = + TestAssociatedTrigger(*target, AtomicString("--trigger")); + EXPECT_NE(once_trigger, repeat_trigger); + EXPECT_EQ(once_trigger->behavior(), AnimationTrigger::Behavior::Enum::kOnce); +} -// Element* target = GetDocument().getElementById(AtomicString("target")); +void CSSAnimationsTriggerTest::TestRangeStartChange( + Element* target, + AtomicString new_class, + bool expect_same, + const AnimationTrigger::RangeBoundary* expected_boundary) { + AnimationTrigger* old_trigger = GetTrigger(*target); + target->setAttribute(html_names::kClassAttr, new_class); + UpdateAllLifecyclePhasesForTest(); + AnimationTrigger* new_trigger = GetTrigger(*target); + if (expect_same) { + EXPECT_EQ(old_trigger, new_trigger); + } else { + EXPECT_NE(old_trigger, new_trigger); + } + VerifyTriggerRangeBoundary(new_trigger->rangeStart(nullptr), + expected_boundary); +} -// ElementAnimations* animations = target->GetElementAnimations(); -// CSSAnimation* animation = -// DynamicTo<CSSAnimation>((*animations->Animations().begin()).key.Get()); +TEST_P(CSSAnimationsTriggerTest, AnimationTriggerChangeRangeStart) { + SetBodyInnerHTML(R"HTML( + <style> + @keyframes stretch { + from { transform: scaleX(1); } + to { transform: scaleX(5); } + } -// AnimationTrigger* trigger = animation->GetTrigger(); -// EXPECT_NE(trigger, nullptr); + #target { + height: 50px; + width: 50px; + animation: stretch linear 0.5s forwards; + } + .normal_trigger { + timeline-trigger: --normal-trigger view() repeat; + } + .normal_trigger2 { + timeline-trigger: --normal-trigger view() repeat; + } + .contain10_trigger { + timeline-trigger: --contain10-trigger view() once contain 10%; + } + .contain10_trigger2 { + timeline-trigger: --contain10-trigger view() once contain 10%; + } + .contain90_trigger { + timeline-trigger: --contain90-trigger view() once contain 90%; + } + .cover90_trigger { + timeline-trigger: --cover90-trigger view() once cover 90%; + } -// EXPECT_EQ(trigger->timeline(), &GetDocument().Timeline()); + .scroller { + overflow-y: scroll; + height: 500px; + width: 500px; + border: solid 1px; + position: relative; + } + #space { + width: 50px; + height: 600px; + } + </style> + <div id="wrapper"> + <div id="scroller" class="scroller"> + <div id="space"></div> + <div id="target" class="normal_trigger"></div> + <div id="space"></div> + </div> + </div> + )HTML"); -// target->classList().Add(AtomicString("repeat_trigger")); -// UpdateAllLifecyclePhasesForTest(); + Element* target = GetDocument().getElementById(AtomicString("target")); -// AnimationTrigger* repeat_trigger = animation->GetTrigger(); -// EXPECT_NE(trigger, repeat_trigger); -// EXPECT_EQ(repeat_trigger->behavior(), -// AnimationTrigger::Behavior::Enum::kRepeat); + const AnimationTrigger::RangeBoundary* normal = + MakeGarbageCollected<AnimationTrigger::RangeBoundary>(String("normal")); + TestRangeStartChange(target, AtomicString("normal_trigger2"), + /* expect_same */ true, normal); + AnimationTrigger::RangeBoundary* contain10 = + MakeRangeOffsetBoundary(V8TimelineRange::Enum::kContain, 10); + TestRangeStartChange(target, AtomicString("contain10_trigger"), + /* expect_same */ false, contain10); + TestRangeStartChange(target, AtomicString("contain10_trigger2"), + /* expect_same */ true, contain10); -// target->classList().Remove(AtomicString("repeat_trigger")); -// target->classList().Add(AtomicString("once_trigger")); -// UpdateAllLifecyclePhasesForTest(); + AnimationTrigger::RangeBoundary* contain90 = + MakeRangeOffsetBoundary(V8TimelineRange::Enum::kContain, 90); + TestRangeStartChange(target, AtomicString("contain90_trigger"), + /* expect_same */ false, contain90); -// AnimationTrigger* once_trigger = animation->GetTrigger(); -// EXPECT_NE(once_trigger, repeat_trigger); -// EXPECT_EQ(once_trigger->behavior(), -// AnimationTrigger::Behavior::Enum::kOnce); -// } + AnimationTrigger::RangeBoundary* cover90 = + MakeRangeOffsetBoundary(V8TimelineRange::Enum::kCover, 90); + TestRangeStartChange(target, AtomicString("cover90_trigger"), + /* expect_same */ false, cover90); +} -// void CSSAnimationsTriggerTest::TestRangeStartChange( -// Element* target, -// Animation* animation, -// AtomicString new_class, -// bool expect_same, -// const AnimationTrigger::RangeBoundary* expected_boundary) { -// CSSAnimation* css_animation = DynamicTo<CSSAnimation>(animation); -// AnimationTrigger* old_trigger = css_animation->GetTrigger(); -// target->setAttribute(html_names::kClassAttr, new_class); -// UpdateAllLifecyclePhasesForTest(); -// AnimationTrigger* new_trigger = css_animation->GetTrigger(); -// if (expect_same) { -// EXPECT_EQ(old_trigger, new_trigger); -// } else { -// EXPECT_NE(old_trigger, new_trigger); -// } -// VerifyTriggerRangeBoundary(new_trigger->rangeStart(nullptr), -// expected_boundary); -// } +TEST_P(CSSAnimationsTriggerTest, NonTriggerChange) { + SetBodyInnerHTML(R"HTML( + <style> + @keyframes stretch { + from { transform: scaleX(1); } + to { transform: scaleX(5); } + } + .subject50x50 { + height: 50px; + width: 50px; + } + .subject100x100 { + height: 100px; + width: 100px; + } + .target { + height: 10px; + width: 10px; + animation: stretch linear 0.5s forwards; + timeline-trigger: --trigger view() once contain 10% contain 90%; + animation-trigger: --trigger; + } + .scroll_tl { + animation-timeline: scroll(); + } + .view_tl { + animation-timeline: view(); + } + .range_contain { + animation-range: contain 10% contain 90%; + } + .range_cover { + animation-range: cover 1% cover 99%; + } + .scroller { + overflow-y: scroll; + height: 500px; + width: 500px; + border: solid 1px; + position: relative; + } + #space { + width: 50px; + height: 600px; + } + </style> + <div id="wrapper"> + <div id="scroller" class="scroller"> + <div id="space"></div> + <div id="target" class="target subject50x50"></div> + <div id="space"></div> + </div> + </div> + )HTML"); -// TEST_P(CSSAnimationsTriggerTest, AnimationTriggerChangeRangeStart) { -// SetBodyInnerHTML(R"HTML( -// <style> -// @keyframes stretch { -// from { transform: scaleX(1); } -// to { transform: scaleX(5); } -// } -// .subject { -// height: 50px; -// width: 50px; -// } -// #target { -// animation: stretch linear 0.5s forwards; -// } -// .normal_trigger { -// animation-trigger: view() repeat; -// } -// .normal_trigger2 { -// animation-trigger: view() repeat; -// } -// .contain10_trigger { -// animation-trigger: view() once contain 10%; -// } -// .contain10_trigger2 { -// animation-trigger: view() once contain 10%; -// } -// .contain90_trigger { -// animation-trigger: view() once contain 90%; -// } -// .cover90_trigger { -// animation-trigger: view() once cover 90%; -// } -// .scroller { -// overflow-y: scroll; -// height: 500px; -// width: 500px; -// border: solid 1px; -// position: relative; -// } -// #space { -// width: 50px; -// height: 600px; -// } -// </style> -// <div id="wrapper"> -// <div id="scroller" class="scroller"> -// <div id="space"></div> -// <div id="target" class="subject"></div> -// <div id="space"></div> -// </div> -// </div> -// )HTML"); + Element* target = GetDocument().getElementById(AtomicString("target")); + ElementAnimations* animations = target->GetElementAnimations(); + CSSAnimation* animation = + DynamicTo<CSSAnimation>((*animations->Animations().begin()).key.Get()); -// Element* target = GetDocument().getElementById(AtomicString("target")); -// ElementAnimations* animations = target->GetElementAnimations(); -// CSSAnimation* animation = -// DynamicTo<CSSAnimation>((*animations->Animations().begin()).key.Get()); + AnimationTrigger* original_trigger = GetTrigger(*target); + EXPECT_NE(original_trigger, nullptr); + EXPECT_TRUE(original_trigger->timeline()->IsViewTimeline()); -// AnimationTrigger* trigger = animation->GetTrigger(); -// EXPECT_NE(trigger, nullptr); -// EXPECT_EQ(trigger->timeline(), &GetDocument().Timeline()); + target->classList().Add(AtomicString("subject100x100")); + UpdateAllLifecyclePhasesForTest(); + EXPECT_EQ(original_trigger, GetTrigger(*target)); -// const AnimationTrigger::RangeBoundary* normal = -// MakeGarbageCollected<AnimationTrigger::RangeBoundary>(String("normal")); -// TestRangeStartChange(target, animation, AtomicString("normal_trigger"), -// /* expect_same */ false, normal); -// TestRangeStartChange(target, animation, AtomicString("normal_trigger2"), -// /* expect_same */ true, normal); -// AnimationTrigger::RangeBoundary* contain10 = -// MakeRangeOffsetBoundary(V8TimelineRange::Enum::kContain, 10); -// TestRangeStartChange(target, animation, AtomicString("contain10_trigger"), -// /* expect_same */ false, contain10); -// TestRangeStartChange(target, animation, AtomicString("contain10_trigger2"), -// /* expect_same */ true, contain10); + EXPECT_FALSE(animation->timeline()->IsScrollTimeline()); + target->classList().Add(AtomicString("scroll_tl")); + UpdateAllLifecyclePhasesForTest(); + EXPECT_EQ(original_trigger, GetTrigger(*target)); + EXPECT_TRUE(animation->timeline()->IsScrollTimeline()); -// AnimationTrigger::RangeBoundary* contain90 = -// MakeRangeOffsetBoundary(V8TimelineRange::Enum::kContain, 90); -// TestRangeStartChange(target, animation, AtomicString("contain90_trigger"), -// /* expect_same */ false, contain90); + EXPECT_FALSE(animation->timeline()->IsViewTimeline()); + target->classList().Remove(AtomicString("scroll_tl")); + target->classList().Add(AtomicString("view_tl")); + UpdateAllLifecyclePhasesForTest(); + EXPECT_EQ(original_trigger, GetTrigger(*target)); + EXPECT_TRUE(animation->timeline()->IsViewTimeline()); -// AnimationTrigger::RangeBoundary* cover90 = -// MakeRangeOffsetBoundary(V8TimelineRange::Enum::kCover, 90); -// TestRangeStartChange(target, animation, AtomicString("cover90_trigger"), -// /* expect_same */ false, cover90); -// } + const AnimationTrigger::RangeBoundary* normal = + MakeGarbageCollected<AnimationTrigger::RangeBoundary>(String("normal")); + VerifyTriggerRangeBoundary(animation->rangeStart(), normal); + target->classList().Add(AtomicString("range_contain")); + UpdateAllLifecyclePhasesForTest(); + EXPECT_EQ(original_trigger, GetTrigger(*target)); + AnimationTrigger::RangeBoundary* contain10 = + MakeRangeOffsetBoundary(V8TimelineRange::Enum::kContain, 10); + VerifyTriggerRangeBoundary(animation->rangeStart(), contain10); -// TEST_P(CSSAnimationsTriggerTest, NonTriggerChange) { -// SetBodyInnerHTML(R"HTML( -// <style> -// @keyframes stretch { -// from { transform: scaleX(1); } -// to { transform: scaleX(5); } -// } -// .subject50x50 { -// height: 50px; -// width: 50px; -// } -// .subject100x100 { -// height: 100px; -// width: 100px; -// } -// .target { -// height: 10px; -// width: 10px; -// animation: stretch linear 0.5s forwards; -// animation-trigger: view() once contain 10% contain 90%; -// } -// .scroll_tl { -// animation-timeline: scroll(); -// } -// .view_tl { -// animation-timeline: view(); -// } -// .range_contain { -// animation-range: contain 10% contain 90%; -// } -// .range_cover { -// animation-range: cover 1% cover 99%; -// } -// .scroller { -// overflow-y: scroll; -// height: 500px; -// width: 500px; -// border: solid 1px; -// position: relative; -// } -// #space { -// width: 50px; -// height: 600px; -// } -// </style> -// <div id="wrapper"> -// <div id="scroller" class="scroller"> -// <div id="space"></div> -// <div id="target" class="target subject50x50"></div> -// <div id="space"></div> -// </div> -// </div> -// )HTML"); + target->classList().Remove(AtomicString("range_contain")); + target->classList().Add(AtomicString("range_cover")); + UpdateAllLifecyclePhasesForTest(); + EXPECT_EQ(original_trigger, GetTrigger(*target)); + AnimationTrigger::RangeBoundary* cover1 = + MakeRangeOffsetBoundary(V8TimelineRange::Enum::kCover, 1); + VerifyTriggerRangeBoundary(animation->rangeStart(), cover1); +} -// Element* target = GetDocument().getElementById(AtomicString("target")); -// ElementAnimations* animations = target->GetElementAnimations(); -// CSSAnimation* animation = -// DynamicTo<CSSAnimation>((*animations->Animations().begin()).key.Get()); +TEST_P(CSSAnimationsTriggerTest, DeviceScaleFactor) { + using RangeBoundary = AnimationTrigger::RangeBoundary; -// AnimationTrigger* original_trigger = animation->GetTrigger(); -// EXPECT_NE(original_trigger, nullptr); -// EXPECT_TRUE(original_trigger->timeline()->IsViewTimeline()); + GetFrame().SetLayoutZoomFactor(2.0f); -// target->classList().Add(AtomicString("subject100x100")); -// UpdateAllLifecyclePhasesForTest(); -// EXPECT_EQ(original_trigger, animation->GetTrigger()); + SetBodyInnerHTML(R"HTML( + <style> + @keyframes stretch { + from { transform: scaleX(1); } + to { transform: scaleX(5); } + } + .target { + height: 10px; + width: 10px; + animation: stretch linear 0.5s forwards; + timeline-trigger: --trigger view() once 100px 300px; + animation-trigger: --trigger; -// EXPECT_FALSE(animation->timeline()->IsScrollTimeline()); -// target->classList().Add(AtomicString("scroll_tl")); -// UpdateAllLifecyclePhasesForTest(); -// EXPECT_EQ(original_trigger, animation->GetTrigger()); -// EXPECT_TRUE(animation->timeline()->IsScrollTimeline()); + } + </style> + <div id="target" class="target"></div> + )HTML"); + UpdateAllLifecyclePhasesForTest(); -// EXPECT_FALSE(animation->timeline()->IsViewTimeline()); -// target->classList().Remove(AtomicString("scroll_tl")); -// target->classList().Add(AtomicString("view_tl")); -// UpdateAllLifecyclePhasesForTest(); -// EXPECT_EQ(original_trigger, animation->GetTrigger()); -// EXPECT_TRUE(animation->timeline()->IsViewTimeline()); + Element* target = GetDocument().getElementById(AtomicString("target")); -// const AnimationTrigger::RangeBoundary* normal = -// MakeGarbageCollected<AnimationTrigger::RangeBoundary>(String("normal")); -// VerifyTriggerRangeBoundary(animation->rangeStart(), normal); -// target->classList().Add(AtomicString("range_contain")); -// UpdateAllLifecyclePhasesForTest(); -// EXPECT_EQ(original_trigger, animation->GetTrigger()); -// AnimationTrigger::RangeBoundary* contain10 = -// MakeRangeOffsetBoundary(V8TimelineRange::Enum::kContain, 10); -// VerifyTriggerRangeBoundary(animation->rangeStart(), contain10); + AnimationTrigger* trigger = GetTrigger(*target); + const RangeBoundary* range_start = trigger->rangeStart(nullptr); + const RangeBoundary* range_end = trigger->rangeEnd(nullptr); -// target->classList().Remove(AtomicString("range_contain")); -// target->classList().Add(AtomicString("range_cover")); -// UpdateAllLifecyclePhasesForTest(); -// EXPECT_EQ(original_trigger, animation->GetTrigger()); -// AnimationTrigger::RangeBoundary* cover1 = -// MakeRangeOffsetBoundary(V8TimelineRange::Enum::kCover, 1); -// VerifyTriggerRangeBoundary(animation->rangeStart(), cover1); -// } + EXPECT_TRUE(range_start->IsTimelineRangeOffset()); + EXPECT_TRUE(range_end->IsTimelineRangeOffset()); -// TEST_P(CSSAnimationsTriggerTest, DeviceScaleFactor) { -// using RangeBoundary = AnimationTrigger::RangeBoundary; + TimelineRangeOffset* start_offset = range_start->GetAsTimelineRangeOffset(); + TimelineRangeOffset* end_offset = range_end->GetAsTimelineRangeOffset(); -// GetFrame().SetLayoutZoomFactor(2.0f); + CSSPrimitiveValue* value_100px = + CSSNumericLiteralValue::Create(100, CSSPrimitiveValue::UnitType::kPixels); + CSSNumericValue* offset_100px = CSSNumericValue::FromCSSValue(*value_100px); + EXPECT_TRUE(start_offset->offset()->Equals(*offset_100px)); -// SetBodyInnerHTML(R"HTML( -// <style> -// @keyframes stretch { -// from { transform: scaleX(1); } -// to { transform: scaleX(5); } -// } -// .target { -// height: 10px; -// width: 10px; -// animation: stretch linear 0.5s forwards; -// animation-trigger: view() once 100px 300px; -// } -// </style> -// <div id="target" class="target"></div> -// )HTML"); -// UpdateAllLifecyclePhasesForTest(); + CSSPrimitiveValue* value_300px = + CSSNumericLiteralValue::Create(300, CSSPrimitiveValue::UnitType::kPixels); + CSSNumericValue* offset_300px = CSSNumericValue::FromCSSValue(*value_300px); + EXPECT_TRUE(end_offset->offset()->Equals(*offset_300px)); +} -// Element* target = GetDocument().getElementById(AtomicString("target")); -// ElementAnimations* animations = target->GetElementAnimations(); -// CSSAnimation* animation = -// DynamicTo<CSSAnimation>((*animations->Animations().begin()).key.Get()); +TEST_P(CSSAnimationsTriggerTest, ChangeTriggerName) { + SetBodyInnerHTML(R"HTML( + <style> + @keyframes stretch { + from { transform: scaleX(1); } + to { transform: scaleX(5); } + } + #target { + height: 50px; + width: 50px; + animation: stretch 1s; + } + .trigger1 { + timeline-trigger: --trigger1 view() once contain 10% contain 90%; + } + .trigger2 { + timeline-trigger: --trigger2 view() once contain 10% contain 90%; + } + .scroller { + overflow-y: scroll; + height: 500px; + width: 500px; + border: solid 1px; + position: relative; + } + #space { + width: 50px; + height: 600px; + } + </style> + <div id="wrapper"> + <div id="scroller" class="scroller"> + <div id="space"></div> + <div id="target"></div> + <div id="space"></div> + </div> + </div> + )HTML"); + Element* target = GetDocument().getElementById(AtomicString("target")); -// AnimationTrigger* trigger = animation->GetTrigger(); -// const RangeBoundary* range_start = trigger->rangeStart(nullptr); -// const RangeBoundary* range_end = trigger->rangeEnd(nullptr); + target->classList().Add(AtomicString("trigger1")); + UpdateAllLifecyclePhasesForTest(); -// EXPECT_TRUE(range_start->IsTimelineRangeOffset()); -// EXPECT_TRUE(range_end->IsTimelineRangeOffset()); + const ScopedCSSName* name1 = target->NamedTriggers()->begin()->key.Get(); + AnimationTrigger* trigger1 = + TestAssociatedTrigger(*target, AtomicString("--trigger1")); -// TimelineRangeOffset* start_offset = -// range_start->GetAsTimelineRangeOffset(); TimelineRangeOffset* end_offset = -// range_end->GetAsTimelineRangeOffset(); + target->classList().Remove(AtomicString("trigger1")); + target->classList().Add(AtomicString("trigger2")); + UpdateAllLifecyclePhasesForTest(); -// CSSPrimitiveValue* value_100px = -// CSSNumericLiteralValue::Create(100, -// CSSPrimitiveValue::UnitType::kPixels); -// CSSNumericValue* offset_100px = -// CSSNumericValue::FromCSSValue(*value_100px); -// EXPECT_TRUE(start_offset->offset()->Equals(*offset_100px)); + const ScopedCSSName* name2 = target->NamedTriggers()->begin()->key.Get(); + AnimationTrigger* trigger2 = + TestAssociatedTrigger(*target, AtomicString("--trigger2")); + EXPECT_NE(trigger1, trigger2); -// CSSPrimitiveValue* value_300px = -// CSSNumericLiteralValue::Create(300, -// CSSPrimitiveValue::UnitType::kPixels); -// CSSNumericValue* offset_300px = -// CSSNumericValue::FromCSSValue(*value_300px); -// EXPECT_TRUE(end_offset->offset()->Equals(*offset_300px)); -// } + // TODO(crbug.com/429392773): test that the animation is removed from + // trigger1. + EXPECT_TRUE(target->NamedTriggers()->Contains(name2)); + EXPECT_FALSE(target->NamedTriggers()->Contains(name1)); +} } // namespace blink
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver.cc b/third_party/blink/renderer/core/css/resolver/style_resolver.cc index 89146e8..f54412a 100644 --- a/third_party/blink/renderer/core/css/resolver/style_resolver.cc +++ b/third_party/blink/renderer/core/css/resolver/style_resolver.cc
@@ -1400,6 +1400,7 @@ ApplyAnchorData(state); ApplyInertness(state); + ApplyTriggerData(state); IncrementResolvedStyleCounters(style_request, GetDocument()); if (InvalidationTracingFlag::IsEnabled()) [[unlikely]] { @@ -3654,4 +3655,8 @@ return position_try_rule; } +void StyleResolver::ApplyTriggerData(StyleResolverState& state) { + CSSAnimations::UpdateNamedTriggers(state.StyleBuilder(), state.GetElement()); +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/css/resolver/style_resolver.h b/third_party/blink/renderer/core/css/resolver/style_resolver.h index 5ce1ad5..a71ae67 100644 --- a/third_party/blink/renderer/core/css/resolver/style_resolver.h +++ b/third_party/blink/renderer/core/css/resolver/style_resolver.h
@@ -288,6 +288,8 @@ return computed_style_bytes_used_; } + void ApplyTriggerData(StyleResolverState& state); + void Trace(Visitor*) const; private:
diff --git a/third_party/blink/renderer/core/dom/build.gni b/third_party/blink/renderer/core/dom/build.gni index 916786e1..f8e31f9 100644 --- a/third_party/blink/renderer/core/dom/build.gni +++ b/third_party/blink/renderer/core/dom/build.gni
@@ -107,6 +107,8 @@ "dom_time_stamp.h", "dom_token_list.cc", "dom_token_list.h", + "element_animation_trigger_data.cc", + "element_animation_trigger_data.h", "element_data.cc", "element_data.h", "element_rare_data_field.h", @@ -192,6 +194,7 @@ "mutation_record.h", "name_node_list.cc", "name_node_list.h", + "named_animation_trigger_map.h", "named_node_map.cc", "named_node_map.h", "names_map.cc",
diff --git a/third_party/blink/renderer/core/dom/document.h b/third_party/blink/renderer/core/dom/document.h index 49b3b9c..3fc7a7e 100644 --- a/third_party/blink/renderer/core/dom/document.h +++ b/third_party/blink/renderer/core/dom/document.h
@@ -39,6 +39,7 @@ #include "base/dcheck_is_on.h" #include "base/gtest_prod_util.h" #include "base/memory/scoped_refptr.h" +#include "base/memory/stack_allocated.h" #include "base/time/time.h" #include "base/timer/elapsed_timer.h" #include "base/uuid.h" @@ -95,7 +96,6 @@ #include "third_party/blink/renderer/platform/timer.h" #include "third_party/blink/renderer/platform/weborigin/kurl.h" #include "third_party/blink/renderer/platform/wtf/casting.h" -#include "third_party/blink/renderer/platform/wtf/gc_plugin.h" #include "third_party/blink/renderer/platform/wtf/hash_set.h" #include "third_party/perfetto/include/perfetto/tracing/traced_value_forward.h" @@ -2808,21 +2808,21 @@ // the stack and cleared upon leaving its allocated scope. Hence it // is acceptable not to trace it -- should a conservative GC occur, // the cache object's references will be traced by a stack walk. - GC_PLUGIN_IGNORE("https://crbug.com/461878") + STACK_ALLOCATED_IGNORE("https://crbug.com/461878") NthIndexCache* nth_index_cache_ = nullptr; // This is an untraced pointer to the cache-scoped object that is first // allocated on the stack. It is set upon the first object being allocated // on the stack, and cleared upon leaving its allocated scope. The object's // references will be traced by a stack walk. - GC_PLUGIN_IGNORE("https://crbug.com/669058") + STACK_ALLOCATED_IGNORE("https://crbug.com/669058") CheckPseudoHasCacheScope* check_pseudo_has_cache_scope_ = nullptr; // This is an untraced pointer to the first stack-allocated scoping object // that defers invalidation of the node list caches. It is set upon the first // object being allocated on the stack, and cleared upon leaving its // allocated scope. The object's references will be traced by a stack walk. - GC_PLUGIN_IGNORE("https://crbug.com/40874584") + STACK_ALLOCATED_IGNORE("https://crbug.com/40874584") InvalidateNodeListCachesScope* invalidate_node_list_caches_scope_ = nullptr; bool in_pseudo_has_checking_ = false;
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc index 009b66a..4f63e93 100644 --- a/third_party/blink/renderer/core/dom/element.cc +++ b/third_party/blink/renderer/core/dom/element.cc
@@ -12421,4 +12421,33 @@ SanitizerAPI::SanitizeSafeInternal(this, options, exception_state); } +void Element::SetNamedTriggers(NamedAnimationTriggerMap&& named_triggers) { + EnsureElementRareData().EnsureAnimationTriggerData().SetNamedTriggers( + named_triggers); +} + +NamedAnimationTriggerMap* Element::NamedTriggers() const { + ElementRareDataVector* data = GetElementRareData(); + if (!data) { + return nullptr; + } + + ElementAnimationTriggerData* trigger_data = data->AnimationTriggerData(); + if (!trigger_data) { + return nullptr; + } + + return &trigger_data->NamedTriggers(); +} + +AnimationTrigger* Element::NamedTrigger(const ScopedCSSName* name) const { + NamedAnimationTriggerMap* trigger_map = NamedTriggers(); + if (!trigger_map) { + return nullptr; + } + + auto it = trigger_map->find(name); + return it == trigger_map->end() ? nullptr : it->value.Get(); +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/dom/element.h b/third_party/blink/renderer/core/dom/element.h index f698e10..26ea2fbe 100644 --- a/third_party/blink/renderer/core/dom/element.h +++ b/third_party/blink/renderer/core/dom/element.h
@@ -47,6 +47,7 @@ #include "third_party/blink/renderer/core/dom/element_rare_data_field.h" #include "third_party/blink/renderer/core/dom/events/simulated_click_options.h" #include "third_party/blink/renderer/core/dom/focusgroup_flags.h" +#include "third_party/blink/renderer/core/dom/named_animation_trigger_map.h" #include "third_party/blink/renderer/core/dom/names_map.h" #include "third_party/blink/renderer/core/dom/node.h" #include "third_party/blink/renderer/core/dom/whitespace_attacher.h" @@ -74,6 +75,7 @@ class AnchorElementObserver; class AnchorPositionScrollData; +class AnimationTrigger; class AriaNotificationOptions; class Attr; class Attribute; @@ -120,6 +122,7 @@ class ResizeObserverSize; class ScrollIntoViewOptions; class CheckVisibilityOptions; +class ScopedCSSName; class ScrollMarkerGroupData; class ScrollMarkerPseudoElement; class ScrollToOptions; @@ -1773,6 +1776,10 @@ bool HasTabIndexWasSetExplicitly() const; + void SetNamedTriggers(NamedAnimationTriggerMap&& named_triggers); + NamedAnimationTriggerMap* NamedTriggers() const; + AnimationTrigger* NamedTrigger(const ScopedCSSName* name) const; + protected: bool HasElementData() const { return static_cast<bool>(element_data_); } const ElementData* GetElementData() const { return element_data_.Get(); }
diff --git a/third_party/blink/renderer/core/dom/element_animation_trigger_data.cc b/third_party/blink/renderer/core/dom/element_animation_trigger_data.cc new file mode 100644 index 0000000..889891e0 --- /dev/null +++ b/third_party/blink/renderer/core/dom/element_animation_trigger_data.cc
@@ -0,0 +1,23 @@ +// Copyright 2025 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/dom/element_animation_trigger_data.h" + +namespace blink { + +void ElementAnimationTriggerData::SetNamedTriggers( + NamedAnimationTriggerMap& named_triggers) { + named_triggers_ = std::move(named_triggers); +} + +NamedAnimationTriggerMap& ElementAnimationTriggerData::NamedTriggers() { + return named_triggers_; +} + +void ElementAnimationTriggerData::Trace(Visitor* visitor) const { + visitor->Trace(named_triggers_); + ElementRareDataField::Trace(visitor); +} + +} // namespace blink
diff --git a/third_party/blink/renderer/core/dom/element_animation_trigger_data.h b/third_party/blink/renderer/core/dom/element_animation_trigger_data.h new file mode 100644 index 0000000..17f90faa --- /dev/null +++ b/third_party/blink/renderer/core/dom/element_animation_trigger_data.h
@@ -0,0 +1,34 @@ +// Copyright 2025 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_DOM_ELEMENT_ANIMATION_TRIGGER_DATA_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_DOM_ELEMENT_ANIMATION_TRIGGER_DATA_H_ + +#include "third_party/blink/renderer/core/animation/animation_trigger.h" +#include "third_party/blink/renderer/core/dom/element.h" +#include "third_party/blink/renderer/core/style/scoped_css_name.h" +#include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h" +#include "third_party/blink/renderer/platform/heap/garbage_collected.h" +#include "third_party/blink/renderer/platform/heap/member.h" + +namespace blink { + +class ElementAnimationTriggerData + : public GarbageCollected<ElementAnimationTriggerData>, + public ElementRareDataField { + public: + void SetNamedTriggers(NamedAnimationTriggerMap& named_triggers); + NamedAnimationTriggerMap& NamedTriggers(); + + void Trace(Visitor*) const override; + + private: + // A map of all AnimationTriggers declared by CSS on the associated element or + // within its subtree. + NamedAnimationTriggerMap named_triggers_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_DOM_ELEMENT_ANIMATION_TRIGGER_DATA_H_
diff --git a/third_party/blink/renderer/core/dom/element_rare_data_vector.cc b/third_party/blink/renderer/core/dom/element_rare_data_vector.cc index a632263..b3154410 100644 --- a/third_party/blink/renderer/core/dom/element_rare_data_vector.cc +++ b/third_party/blink/renderer/core/dom/element_rare_data_vector.cc
@@ -539,6 +539,17 @@ SetField(FieldId::kCustomElementRegistry, registry); } +ElementAnimationTriggerData* ElementRareDataVector::AnimationTriggerData() { + return static_cast<ElementAnimationTriggerData*>( + GetField(FieldId::kAnimationTriggerData)); +} + +ElementAnimationTriggerData& +ElementRareDataVector::EnsureAnimationTriggerData() { + return EnsureField<ElementAnimationTriggerData>( + FieldId::kAnimationTriggerData); +} + void ElementRareDataVector::IncrementImplicitlyAnchoredElementCount() { EnsureWrappedField<wtf_size_t>(FieldId::kImplicitlyAnchoredElementCount)++; }
diff --git a/third_party/blink/renderer/core/dom/element_rare_data_vector.h b/third_party/blink/renderer/core/dom/element_rare_data_vector.h index 11ab4e18..f9b9745d 100644 --- a/third_party/blink/renderer/core/dom/element_rare_data_vector.h +++ b/third_party/blink/renderer/core/dom/element_rare_data_vector.h
@@ -7,6 +7,7 @@ #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/dom/css_pseudo_element.h" +#include "third_party/blink/renderer/core/dom/element_animation_trigger_data.h" #include "third_party/blink/renderer/core/dom/element_rare_data_field.h" #include "third_party/blink/renderer/core/dom/explicitly_set_attr_elements_map.h" #include "third_party/blink/renderer/core/dom/focusgroup_flags.h" @@ -94,8 +95,9 @@ kExplicitlySetElementsForAttr = 35, kCSSPseudoElementData = 36, kCustomElementRegistry = 37, + kAnimationTriggerData = 38, - kNumFields = 38, + kNumFields = 39, }; ElementRareDataField* GetField(FieldId field_id) const; @@ -326,6 +328,9 @@ CustomElementRegistry* GetCustomElementRegistry() const; void SetCustomElementRegistry(CustomElementRegistry* registry); + ElementAnimationTriggerData* AnimationTriggerData(); + ElementAnimationTriggerData& EnsureAnimationTriggerData(); + void IncrementImplicitlyAnchoredElementCount(); void DecrementImplicitlyAnchoredElementCount(); bool HasImplicitlyAnchoredElement() const;
diff --git a/third_party/blink/renderer/core/dom/named_animation_trigger_map.h b/third_party/blink/renderer/core/dom/named_animation_trigger_map.h new file mode 100644 index 0000000..9e6c858b --- /dev/null +++ b/third_party/blink/renderer/core/dom/named_animation_trigger_map.h
@@ -0,0 +1,23 @@ +// Copyright 2025 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_DOM_NAMED_ANIMATION_TRIGGER_MAP_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_DOM_NAMED_ANIMATION_TRIGGER_MAP_H_ + +#include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h" +#include "third_party/blink/renderer/platform/heap/member.h" + +namespace blink { + +class AnimationTrigger; +class ScopedCSSName; + +// This maps a named animation trigger to the corresponding trigger object +// within. +using NamedAnimationTriggerMap = + HeapHashMap<Member<const ScopedCSSName>, Member<AnimationTrigger>>; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_DOM_NAMED_ANIMATION_TRIGGER_MAP_H_
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc index 4f44b74..7569ce3 100644 --- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc +++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
@@ -1422,44 +1422,6 @@ } } -void WebFrameWidgetImpl::SendEndOfScrollEventsDeprecated( - bool affects_outer_viewport, - bool affects_inner_viewport, - cc::ElementId scroll_latched_element_id) { - Node* target_node = View()->FindNodeFromScrollableCompositorElementId( - scroll_latched_element_id); - if (!target_node) { - return; - } - if (ScrollableArea* scrollable_area = - ScrollableArea::GetForScrolling(target_node->GetLayoutBox())) { - scrollable_area->UpdateSnappedTargetsAndEnqueueScrollSnapChange(); - scrollable_area->SetImplSnapStrategy(nullptr); - } - - if (auto* viewport_position_tracker = - AnchorElementViewportPositionTracker::MaybeGetOrCreateFor( - target_node->GetDocument())) { - viewport_position_tracker->OnScrollEnd(); - } - - if (RuntimeEnabledFeatures::ScrollEndEventsEnabled()) { - Node* document_node = View()->MainFrameImpl() - ? View()->MainFrameImpl()->GetDocument() - : nullptr; - if (affects_inner_viewport) { - target_node->GetDocument().EnqueueVisualViewportScrollEndEvent(); - } - // A scroll gesture that causes the browser controls to show/hide would be - // associated with the document but may not have actually caused the - // document/outer viewport to scroll. In this case the document should - // not receive a scrollend event. - if (affects_outer_viewport || target_node != document_node) { - target_node->GetDocument().EnqueueScrollEndEventForNode(target_node); - } - } -} - void WebFrameWidgetImpl::SendEndOfScrollEvents( const cc::CompositorCommitData& commit_data) { HeapHashSet<Member<AnchorElementViewportPositionTracker>> handled_trackers; @@ -1548,25 +1510,8 @@ NotifyLatchedScrollMarkerGroup(commit_data); } - // TODO(bokan): If a scroll ended and a new one began in the same Blink frame - // (e.g. during a long running main thread task), this will erroneously - // dispatch the scroll end to the latter (still-scrolling) element. - // https://crbug.com/1116780. - // With MultiImplyOnlyScrollAnimations support, a non-latched scroll - // container might have finished its snap animation, so we don't check that we - // have a latched id. - if (::features::MultiImplOnlyScrollAnimationsSupported()) { - if (commit_data.scroll_end_data.done_containers.size()) { - SendEndOfScrollEvents(commit_data); - } - } else { - if (commit_data.scroll_latched_element_id != cc::ElementId() && - commit_data.scroll_end_data.scroll_gesture_did_end) { - SendEndOfScrollEventsDeprecated( - commit_data.scroll_end_data.gesture_affects_outer_viewport_scroll, - commit_data.scroll_end_data.gesture_affects_inner_viewport_scroll, - commit_data.scroll_latched_element_id); - } + if (commit_data.scroll_end_data.done_containers.size()) { + SendEndOfScrollEvents(commit_data); } }
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.h b/third_party/blink/renderer/core/frame/web_frame_widget_impl.h index d10d1c06..fa1c274 100644 --- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.h +++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
@@ -958,13 +958,6 @@ void SendOverscrollEventFromImplSide(const gfx::Vector2dF& overscroll_delta, cc::ElementId scroll_latched_element_id); - // TODO(crbug.com/372627916): This function is not used when - // MultipleImplOnlyScrollAnimations is enabled. It should be considered - // deprecated and should be deleted when the MultipleImplOnlyScrollAnimations - // code path is the only existing code path. - void SendEndOfScrollEventsDeprecated(bool affects_outer_viewport, - bool affects_inner_viewport, - cc::ElementId scroll_latched_element_id); void SendEndOfScrollEvents(const cc::CompositorCommitData& commit_data); void SendScrollSnapChangingEventIfNeeded( const cc::CompositorCommitData& commit_data);
diff --git a/third_party/blink/renderer/core/html/forms/html_button_element.cc b/third_party/blink/renderer/core/html/forms/html_button_element.cc index f3c25f3..022735a 100644 --- a/third_party/blink/renderer/core/html/forms/html_button_element.cc +++ b/third_party/blink/renderer/core/html/forms/html_button_element.cc
@@ -83,15 +83,15 @@ const AtomicString& HTMLButtonElement::FormControlTypeAsString() const { switch (type_) { case Type::kButton: { - DEFINE_STATIC_LOCAL(const AtomicString, button, ("button")); + DEFINE_STATIC_LOCAL(const AtomicString, button, (keywords::kButton)); return button; } case Type::kSubmit: { - DEFINE_STATIC_LOCAL(const AtomicString, submit, ("submit")); + DEFINE_STATIC_LOCAL(const AtomicString, submit, (keywords::kSubmit)); return submit; } case Type::kReset: { - DEFINE_STATIC_LOCAL(const AtomicString, reset, ("reset")); + DEFINE_STATIC_LOCAL(const AtomicString, reset, (keywords::kReset)); return reset; } } @@ -112,11 +112,11 @@ // static std::optional<HTMLButtonElement::Type> HTMLButtonElement::TypeFromString( const AtomicString& string) { - if (EqualIgnoringASCIICase(string, "reset")) { + if (EqualIgnoringASCIICase(string, keywords::kReset)) { return kReset; - } else if (EqualIgnoringASCIICase(string, "button")) { + } else if (EqualIgnoringASCIICase(string, keywords::kButton)) { return kButton; - } else if (EqualIgnoringASCIICase(string, "submit")) { + } else if (EqualIgnoringASCIICase(string, keywords::kSubmit)) { return kSubmit; } else { return std::nullopt; @@ -214,8 +214,9 @@ } } +// static CommandEventType HTMLButtonElement::GetCommandEventType( - const AtomicString& action) const { + const AtomicString& action) { if (action.IsNull() || action.empty()) { return CommandEventType::kNone; } @@ -320,51 +321,39 @@ void HTMLButtonElement::DefaultEventHandler(Event& event) { if (event.type() == event_type_names::kDOMActivate) { - bool potentialCommand = (FastHasAttribute(html_names::kCommandforAttr) || - FastHasAttribute(html_names::kCommandAttr)); - if (!IsDisabledFormControl()) { - if (Form() && type_ == kButton) { - if (!EqualIgnoringASCIICase(FastGetAttribute(html_names::kTypeAttr), - "button")) { - DCHECK(type_ == kButton); - AddConsoleMessage(mojom::blink::ConsoleMessageSource::kOther, - mojom::blink::ConsoleMessageLevel::kWarning, - "Buttons associated with forms that include " - "command or commandfor attributes are " - "ambiguous, and require a type=button attribute. " - "No action will be taken."); - return; - } + if (auto* form = Form(); + form && !IsDisabledFormControl()) { + bool has_command_attr = FastHasAttribute(html_names::kCommandforAttr) || + FastHasAttribute(html_names::kCommandAttr); + if (has_command_attr && type_ == kButton && + !EqualIgnoringASCIICase(FastGetAttribute(html_names::kTypeAttr), + keywords::kButton)) { + AddConsoleMessage(mojom::blink::ConsoleMessageSource::kOther, + mojom::blink::ConsoleMessageLevel::kWarning, + "Buttons associated with forms that include " + "command or commandfor attributes are " + "ambiguous, and require a type=button attribute. " + "No action will be taken."); + return; } - - if (Form() && type_ == kSubmit) { - if (!EqualIgnoringASCIICase(FastGetAttribute(html_names::kTypeAttr), - "submit") && - potentialCommand) { - DCHECK(type_ == kSubmit); - AddConsoleMessage(mojom::blink::ConsoleMessageSource::kOther, - mojom::blink::ConsoleMessageLevel::kWarning, - "Buttons associated with forms that include " - "command or commandfor attributes are " - "ambiguous, and require a type=button attribute. " - "No action will be taken."); - return; - } else if (potentialCommand) { - DCHECK(FastHasAttribute(html_names::kTypeAttr)); + if (type_ == kSubmit) { + if (has_command_attr && + EqualIgnoringASCIICase(FastGetAttribute(html_names::kTypeAttr), + keywords::kSubmit)) { AddConsoleMessage( mojom::blink::ConsoleMessageSource::kOther, mojom::blink::ConsoleMessageLevel::kWarning, - "Buttons with an explicit type=submit will always submit a form, " - "so command or commandfor attributes will be ignored."); + "Buttons with an explicit type=submit will always submit a " + "form, so command or commandfor attributes will be ignored."); } - Form()->PrepareForSubmission(&event, this); + form->PrepareForSubmission(&event, this); event.SetDefaultHandled(); return; } - if (Form() && type_ == kReset) { - Form()->reset(); + if (type_ == kReset) { + form->reset(); event.SetDefaultHandled(); - if (potentialCommand) { + if (has_command_attr) { AddConsoleMessage( mojom::blink::ConsoleMessageSource::kOther, mojom::blink::ConsoleMessageLevel::kWarning, @@ -376,8 +365,8 @@ } // Buttons with a commandfor will dispatch a CommandEvent on the - // invoker, and run HandleCommandInternal to perform default logic. - if (auto* command_target = commandForElement()) { + // invoker, and run `HandleCommandInternal` to perform default logic. + if (Element* command_target = commandForElement()) { // commandfor & popovertarget shouldn't be combined, so warn. if (FastHasAttribute(html_names::kPopovertargetAttr)) { AddConsoleMessage( @@ -385,22 +374,18 @@ mojom::blink::ConsoleMessageLevel::kWarning, "popovertarget is ignored on elements with commandfor."); } - auto action = GetCommandEventType(FastGetAttribute(html_names::kCommandAttr)); bool is_valid_builtin = command_target->IsValidBuiltinCommand(*this, action); - bool should_dispatch = - is_valid_builtin || action == CommandEventType::kCustom; - if (should_dispatch) { - Event* commandEvent = + if (is_valid_builtin || action == CommandEventType::kCustom) { + Event* command_event = CommandEvent::Create(event_type_names::kCommand, command(), this); - command_target->DispatchEvent(*commandEvent); - if (is_valid_builtin && !commandEvent->defaultPrevented()) { + command_target->DispatchEvent(*command_event); + if (is_valid_builtin && !command_event->defaultPrevented()) { command_target->HandleCommandInternal(*this, action); } } - return; } } @@ -408,7 +393,6 @@ if (HandleKeyboardActivation(event)) { return; } - HTMLFormControlElement::DefaultEventHandler(event); }
diff --git a/third_party/blink/renderer/core/html/forms/html_button_element.h b/third_party/blink/renderer/core/html/forms/html_button_element.h index c053390..bc2c7c43 100644 --- a/third_party/blink/renderer/core/html/forms/html_button_element.h +++ b/third_party/blink/renderer/core/html/forms/html_button_element.h
@@ -53,7 +53,7 @@ Element* commandForElement() const; AtomicString command() const; void setCommand(const AtomicString& type); - CommandEventType GetCommandEventType(const AtomicString& type) const; + static CommandEventType GetCommandEventType(const AtomicString& type); // Override for inertness in order to make customizable <select> button inert. // TODO(crbug.com/1511354): Replace this with interactivity:inert in
diff --git a/third_party/blink/renderer/core/html/html_geolocation_element.cc b/third_party/blink/renderer/core/html/html_geolocation_element.cc index 33f74c8..769082e 100644 --- a/third_party/blink/renderer/core/html/html_geolocation_element.cc +++ b/third_party/blink/renderer/core/html/html_geolocation_element.cc
@@ -11,7 +11,7 @@ namespace blink { HTMLGeolocationElement::HTMLGeolocationElement(Document& document) - : HTMLPermissionElement(document) { + : HTMLPermissionElement(document, html_names::kGeolocationTag) { CHECK(RuntimeEnabledFeatures::GeolocationElementEnabled( document.GetExecutionContext())); setType(AtomicString("geolocation"));
diff --git a/third_party/blink/renderer/core/html/html_permission_element.cc b/third_party/blink/renderer/core/html/html_permission_element.cc index e933f34..6750dc3 100644 --- a/third_party/blink/renderer/core/html/html_permission_element.cc +++ b/third_party/blink/renderer/core/html/html_permission_element.cc
@@ -363,8 +363,10 @@ return !ParsePermissionDescriptorsFromString(type).empty(); } -HTMLPermissionElement::HTMLPermissionElement(Document& document) - : HTMLElement(html_names::kPermissionTag, document), +HTMLPermissionElement::HTMLPermissionElement( + Document& document, + std::optional<QualifiedName> tag_name) + : HTMLElement(tag_name.value_or(html_names::kPermissionTag), document), ScrollSnapshotClient(GetDocument().GetFrame()), permission_service_(document.GetExecutionContext()), embedded_permission_control_receiver_(this,
diff --git a/third_party/blink/renderer/core/html/html_permission_element.h b/third_party/blink/renderer/core/html/html_permission_element.h index 4a521e7..ec44143 100644 --- a/third_party/blink/renderer/core/html/html_permission_element.h +++ b/third_party/blink/renderer/core/html/html_permission_element.h
@@ -46,7 +46,8 @@ public: static bool isTypeSupported(const AtomicString& type); - explicit HTMLPermissionElement(Document&); + explicit HTMLPermissionElement(Document&, + std::optional<QualifiedName> = std::nullopt); ~HTMLPermissionElement() override;
diff --git a/third_party/blink/renderer/core/html/keywords.json5 b/third_party/blink/renderer/core/html/keywords.json5 index d5877367..3b5a8fc 100644 --- a/third_party/blink/renderer/core/html/keywords.json5 +++ b/third_party/blink/renderer/core/html/keywords.json5
@@ -244,5 +244,7 @@ // ElementInternals.type // https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/ElementInternalsType/explainer.md "button", + "reset", + "submit", ], }
diff --git a/third_party/blink/renderer/core/layout/inline/logical_line_builder.cc b/third_party/blink/renderer/core/layout/inline/logical_line_builder.cc index b66e081d..5026d07 100644 --- a/third_party/blink/renderer/core/layout/inline/logical_line_builder.cc +++ b/third_party/blink/renderer/core/layout/inline/logical_line_builder.cc
@@ -25,6 +25,72 @@ namespace blink { +namespace { + +bool CanUseItemForNeedsPaint(const InlineItem& item) { + switch (item.Type()) { + case InlineItem::kBlockInInline: + case InlineItem::kCloseTag: + case InlineItem::kFloating: + case InlineItem::kOutOfFlowPositioned: + case InlineItem::kListMarker: + case InlineItem::kBidiControl: + case InlineItem::kOpenRubyColumn: + case InlineItem::kCloseRubyColumn: + case InlineItem::kRubyLinePlaceholder: + return false; + + case InlineItem::kControl: + case InlineItem::kText: + if (!item.Length()) { + return false; + } + break; + + case InlineItem::kAtomicInline: + case InlineItem::kOpenTag: + case InlineItem::kInitialLetterBox: + break; + } + return item.TextType() == TextItemType::kNormal && item.GetLayoutObject(); +} + +const LayoutObject& LayoutObjectForLineClampEllipsis( + const InlineNode& node, + const InlineItemResults& line_items, + const InlineItemTextIndex& line_start) { + for (const auto& item_result : base::Reversed(line_items)) { + const auto& item = *item_result.item; + if (!CanUseItemForNeedsPaint(item)) { + continue; + } + if ((item.Type() == InlineItem::kText || + item.Type() == InlineItem::kControl) && + !item_result.Length()) { + continue; + } + return *item.GetLayoutObject(); + } + + // If we haven't found any useful layout object in the line's previous + // results (for example, because the ellipsis displaced this entire line), + // we try to find layout objects in previous lines. This is needed so, if + // the height of previous lines change, the ellipsis gets repainted. + auto items_prefix = + base::span<const Member<InlineItem>>(node.ItemsData(false).items) + .first(line_start.item_index); + for (const auto& item : base::Reversed(items_prefix)) { + if (CanUseItemForNeedsPaint(*item)) { + return *item->GetLayoutObject(); + } + } + + // If we weren't able to find anything, we fallback to the inline root. + return *node.GetLayoutBlockFlow(); +} + +} // namespace + LogicalLineBuilder::LogicalLineBuilder(InlineNode node, const ConstraintSpace& constraint_space, const InlineBreakToken* break_token, @@ -88,8 +154,11 @@ const ShapeResultView* shape_result_view = ShapeResultView::Create(ellipsis_data->shape_result); FontHeight text_metrics = ellipsis_data->text_metrics; + const LayoutObject& corresponding_layout_object = + LayoutObjectForLineClampEllipsis(node_, *line_items, + line_info->Start()); - line_box->AddChild(*node_.GetLayoutBlockFlow(), + line_box->AddChild(corresponding_layout_object, StyleVariant::kStandardEllipsis, shape_result_view, ellipsis_data->text, LogicalRect(LayoutUnit(), -text_metrics.ascent,
diff --git a/third_party/blink/renderer/core/lcp_critical_path_predictor/lcp_script_observer.h b/third_party/blink/renderer/core/lcp_critical_path_predictor/lcp_script_observer.h index 8a892d1..b22fe2b 100644 --- a/third_party/blink/renderer/core/lcp_critical_path_predictor/lcp_script_observer.h +++ b/third_party/blink/renderer/core/lcp_critical_path_predictor/lcp_script_observer.h
@@ -8,6 +8,7 @@ #include <stdint.h> #include "base/feature_list.h" +#include "base/memory/stack_allocated.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/core_probes_inl.h" @@ -41,10 +42,12 @@ void Did(const probe::CallFunction&); private: - GC_PLUGIN_IGNORE("probe references should be valid within will/did callbacks") + STACK_ALLOCATED_IGNORE( + "probe references should be valid within will/did callbacks") Vector<const probe::ExecuteScript*> stack_script_probes_; - GC_PLUGIN_IGNORE("probe references should be valid within will/did callbacks") + STACK_ALLOCATED_IGNORE( + "probe references should be valid within will/did callbacks") Vector<const probe::CallFunction*> stack_function_probes_; Member<LocalFrame> local_root_;
diff --git a/third_party/blink/renderer/core/view_transition/view_transition.cc b/third_party/blink/renderer/core/view_transition/view_transition.cc index a10c9623..514667b 100644 --- a/third_party/blink/renderer/core/view_transition/view_transition.cc +++ b/third_party/blink/renderer/core/view_transition/view_transition.cc
@@ -637,10 +637,6 @@ if (style_tracker_->HasActiveAnimations()) break; - CHECK_NE(creation_type_, CreationType::kForSnapshot); - CHECK(script_delegate_); - script_delegate_->DidFinishAnimating(); - // Post a task to run the next state (cleanup) outside of the current // lifecycle update. document_->GetTaskRunner(TaskType::kMiscPlatformAPI) @@ -667,6 +663,11 @@ LogIfDocumentElementChanged(); style_tracker_ = nullptr; + + CHECK_NE(creation_type_, CreationType::kForSnapshot); + CHECK(script_delegate_); + script_delegate_->DidFinishAnimating(); + process_next_state = AdvanceTo(State::kFinished); DCHECK(IsTerminalState(state_)); break;
diff --git a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc index 6e4735e0..0bb41f7 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_node_object.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_node_object.cc
@@ -3293,13 +3293,12 @@ // the HTML spec invokers commandfor functionality first, and only // popovertarget after, if commandfor was not executed. if (auto* button = DynamicTo<HTMLButtonElement>(element)) { - const AtomicString& action = - button->FastGetAttribute(html_names::kCommandAttr); - CommandEventType type = button->GetCommandEventType(action); if (HTMLElement* command_for = DynamicTo<HTMLElement>(button->commandForElement())) { - bool is_valid_popover_command = - command_for->IsValidBuiltinPopoverCommand(*button, type); + const AtomicString& action = + button->FastGetAttribute(html_names::kCommandAttr); + bool is_valid_popover_command = command_for->IsValidBuiltinPopoverCommand( + *button, HTMLButtonElement::GetCommandEventType(action)); bool is_child = button->IsDescendantOrShadowDescendantOf(command_for); // Buttons for popovers should indicate the expanded/collapsed state. if (is_valid_popover_command && !is_child) {
diff --git a/third_party/blink/renderer/modules/accessibility/ax_object.cc b/third_party/blink/renderer/modules/accessibility/ax_object.cc index fdc00056..f3c148a 100644 --- a/third_party/blink/renderer/modules/accessibility/ax_object.cc +++ b/third_party/blink/renderer/modules/accessibility/ax_object.cc
@@ -2765,15 +2765,15 @@ return nullptr; } - const AtomicString& action = button_element->FastGetAttribute(html_names::kCommandAttr); - CommandEventType type = button_element->GetCommandEventType(action); - if (command_for->popoverOpen()) { // A button with commandfor might point to an open popover, but the command // might be unrelated - for example `show-modal`. Commands that aren't related // to the showing or hiding of popovers should not establish a details relation // in these cases. - if (!command_for->IsValidBuiltinPopoverCommand(*button_element, type)) { + const AtomicString& action = + button_element->FastGetAttribute(html_names::kCommandAttr); + if (!command_for->IsValidBuiltinPopoverCommand( + *button_element, HTMLButtonElement::GetCommandEventType(action))) { return nullptr; }
diff --git a/third_party/blink/renderer/platform/graphics/dark_mode_filter.cc b/third_party/blink/renderer/platform/graphics/dark_mode_filter.cc index 1f47749..1929af7 100644 --- a/third_party/blink/renderer/platform/graphics/dark_mode_filter.cc +++ b/third_party/blink/renderer/platform/graphics/dark_mode_filter.cc
@@ -207,11 +207,6 @@ DCHECK(flags); DCHECK_NE(GetDarkModeImagePolicy(), DarkModeImagePolicy::kFilterNone); - if (GetDarkModeImagePolicy() == DarkModeImagePolicy::kFilterAll) { - flags->setColorFilter(GetImageFilter()); - return; - } - // Raster-side dark mode path - Just set the dark mode on flags and dark // mode will be applied at compositor side during rasterization. if (ShouldUseRasterSidePath(image)) { @@ -232,8 +227,6 @@ DarkModeImagePolicy image_policy = GetDarkModeImagePolicy(); if (image_policy == DarkModeImagePolicy::kFilterNone) return false; - if (image_policy == DarkModeImagePolicy::kFilterAll) - return true; // kIcon: Do not consider images being drawn into bigger rect as these // images are not meant for icons or representing smaller widgets. These
diff --git a/third_party/blink/renderer/platform/graphics/dark_mode_settings.h b/third_party/blink/renderer/platform/graphics/dark_mode_settings.h index 607ce6a9..19b3796 100644 --- a/third_party/blink/renderer/platform/graphics/dark_mode_settings.h +++ b/third_party/blink/renderer/platform/graphics/dark_mode_settings.h
@@ -18,13 +18,13 @@ kLast = kInvertLightnessLAB, // Last enum value. }; +// This enum will be removed soon to make dark mode simpler. enum class DarkModeImagePolicy { - kFilterAll, // Apply dark-mode filter to all images. - kFilterNone, // Never apply dark-mode filter to any images. kFilterSmart, // Apply dark-mode based on image content. + kFilterNone, // Never apply dark-mode filter to any images. - kFirst = kFilterAll, // First enum value. - kLast = kFilterSmart, // Last enum value. + kFirst = kFilterSmart, // First enum value. + kLast = kFilterNone, // Last enum value. }; enum class DarkModeImageClassifierPolicy {
diff --git a/third_party/blink/renderer/platform/wtf/gc_plugin.h b/third_party/blink/renderer/platform/wtf/gc_plugin.h index fe5bfe1..33006c1b 100644 --- a/third_party/blink/renderer/platform/wtf/gc_plugin.h +++ b/third_party/blink/renderer/platform/wtf/gc_plugin.h
@@ -14,9 +14,8 @@ // // Developer note: this macro must be kept in sync with the definition of // STACK_ALLOCATED_IGNORE in /base/memory/stack_allocated.h. -#define GC_PLUGIN_IGNORE(reason) \ - __attribute__((annotate("blink_gc_plugin_ignore"), \ - annotate("stack_allocated_ignore"))) +#define GC_PLUGIN_IGNORE(reason) \ + __attribute__((annotate("blink_gc_plugin_ignore"))) // GC_PLUGIN_IGNORE_FILE is used to make the Blink GC plugin ignore a whole // file. All classes, fields, methods and variables in that file will be skipped // by the plugin. Any incorrect usages in the file will not be reported by the
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index ec75187..7927981 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -8698,6 +8698,7 @@ crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/block-ellipsis-019.tentative.html [ Failure ] crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/block-ellipsis-020.tentative.html [ Failure ] crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/block-ellipsis-021.tentative.html [ Failure ] +crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/block-ellipsis-repaint-003.html [ Failure ] crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/webkit-line-clamp-036.html [ Failure ] crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/webkit-line-clamp-051.html [ Failure ] crbug.com/40336192 external/wpt/css/css-overflow/line-clamp/webkit-line-clamp-052.html [ Failure ] @@ -8721,6 +8722,7 @@ crbug.com/40336192 virtual/css-line-clamp-line-breaking-ellipsis/external/wpt/css/css-overflow/line-clamp/block-ellipsis-019.tentative.html [ Pass ] crbug.com/40336192 virtual/css-line-clamp-line-breaking-ellipsis/external/wpt/css/css-overflow/line-clamp/block-ellipsis-020.tentative.html [ Pass ] crbug.com/40336192 virtual/css-line-clamp-line-breaking-ellipsis/external/wpt/css/css-overflow/line-clamp/block-ellipsis-021.tentative.html [ Pass ] +crbug.com/40336192 virtual/css-line-clamp-line-breaking-ellipsis/external/wpt/css/css-overflow/line-clamp/block-ellipsis-repaint-003.html [ Pass ] crbug.com/40336192 virtual/css-line-clamp-line-breaking-ellipsis/external/wpt/css/css-overflow/line-clamp/webkit-line-clamp-036.html [ Pass ] crbug.com/40336192 virtual/css-line-clamp-line-breaking-ellipsis/external/wpt/css/css-overflow/line-clamp/webkit-line-clamp-051.html [ Pass ] crbug.com/40336192 virtual/css-line-clamp-line-breaking-ellipsis/external/wpt/css/css-overflow/line-clamp/webkit-line-clamp-052.html [ Pass ] @@ -8874,6 +8876,10 @@ crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/block-ellipsis-010.html [ Failure ] crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/block-ellipsis-015.html [ Failure ] crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/block-ellipsis-022.html [ Failure ] +crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/block-ellipsis-repaint-001.html [ Failure ] +crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/block-ellipsis-repaint-002.html [ Failure ] +crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/block-ellipsis-repaint-003.html [ Failure ] +crbug.com/40336192 virtual/disable-css-line-clamp/external/wpt/css/css-overflow/line-clamp/block-ellipsis-repaint-004.html [ Failure ] # backdrop-filter-mirror-edge crbug.com/904592 external/wpt/css/filter-effects/backdrop-filter-svg-blur.html [ Failure ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites index a3c8d94..3f7f1ed 100644 --- a/third_party/blink/web_tests/VirtualTestSuites +++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -609,26 +609,6 @@ "expires": "Jul 1, 2026" }, { - "prefix": "dark-mode-images-filter-all", - "owners": [ - "peconn@chromium.org", - "pdr@chromium.org" - ], - "platforms": [ - "Linux", - "Mac", - "Win" - ], - "bases": [ - "dark-mode/images" - ], - "args": [ - "--blink-settings=preferredColorScheme=0,forceDarkModeEnabled=true", - "--dark-mode-settings=ImagePolicy=0" - ], - "expires": "Jul 1, 2026" - }, - { "prefix": "dark-mode-images-filter-none", "owners": [ "peconn@chromium.org",
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/line-clamp/block-ellipsis-repaint-001.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/line-clamp/block-ellipsis-repaint-001.html new file mode 100644 index 0000000..b8ba9872 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/line-clamp/block-ellipsis-repaint-001.html
@@ -0,0 +1,40 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<meta charset="utf-8"> +<title>CSS Overflow: line-clamp ellipsis repaint tests</title> +<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#block-ellipsis"> +<link rel="match" href="reference/block-ellipsis-repaint-001-ref.html"> +<meta name="assert" content="This test makes sure that, if the line with the ellipsis changes so it breaks at a different point, the ellipsis gets repainted accordingly."> +<script src="/common/reftest-wait.js"></script> +<style> +.clamp { + line-clamp: 1; + border: 1px solid black; + padding: 4px; + background-color: yellow; +} +#atomic { + display: inline-block; + background-color: orange; + width: 2em; +} +</style> + +<p>The ellipsis should appear right after the end of the first line.</p> + +<div class="clamp"> + <span id="atomic"></span> Some text here <br> + This line gets clamped +</div> + +<script> + window.onload = () => { + requestAnimationFrame(() => { + requestAnimationFrame(() => { + document.getElementById("atomic").style.display = "none"; + takeScreenshot(); + }); + }); + }; +</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/line-clamp/block-ellipsis-repaint-002.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/line-clamp/block-ellipsis-repaint-002.html new file mode 100644 index 0000000..321d7ef --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/line-clamp/block-ellipsis-repaint-002.html
@@ -0,0 +1,37 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<meta charset="utf-8"> +<title>CSS Overflow: line-clamp ellipsis repaint tests</title> +<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#block-ellipsis"> +<link rel="match" href="reference/block-ellipsis-repaint-002-ref.html"> +<meta name="assert" content="This test makes sure that, if the line with the ellipsis gets moved vertically because the height of previous lines grows, the ellipsis moves correspondingly; and that this happens even when the height of the line-clamp container does not change."> +<script src="/common/reftest-wait.js"></script> +<style> +.clamp { + line-clamp: 2; + border: 1px solid black; + padding: 4px; + background-color: yellow; + height: 3.5lh; +} +</style> + +<p>The ellipsis should appear right after the end of the second line.</p> + +<div class="clamp"> + <span id="line1">Line 1</span> <br> + Line 2 <br> + Line 3 +</div> + +<script> + window.onload = () => { + requestAnimationFrame(() => { + requestAnimationFrame(() => { + document.getElementById("line1").style.lineHeight = "2lh"; + takeScreenshot(); + }); + }); + }; +</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/line-clamp/block-ellipsis-repaint-003.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/line-clamp/block-ellipsis-repaint-003.html new file mode 100644 index 0000000..cf902c6 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/line-clamp/block-ellipsis-repaint-003.html
@@ -0,0 +1,59 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<meta charset="utf-8"> +<title>CSS Overflow: line-clamp ellipsis repaint tests</title> +<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#block-ellipsis"> +<link rel="match" href="reference/block-ellipsis-repaint-003-ref.html"> +<meta name="assert" content="This test makes sure that, if the line with the ellipsis gets moved vertically because the height of previous lines grows, the ellipsis moves correspondingly; and that this happens even when the height of the line-clamp container does not change. This is the case even if the ellipsis completely displaces the line it's in."> +<script src="/common/reftest-wait.js"></script> +<style> +.clamp { + line-clamp: 2; + border: 1px solid black; + padding: 4px; + background-color: yellow; + height: 3lh; + width: 400px; + + position: relative; + z-index: 0; +} +#abspos { + position: absolute; + top: calc(1lh + 4px); + left: 4px; + height: 1lh; + width: 2em; + + background-color: pink; + z-index: -1; +} +#line1 { + line-height: 2lh; +} +#wide-atomic { + display: inline-block; + width: 500px; +} +</style> + +<p>The ellipsis should appear inside the pink box.</p> + +<div class="clamp"> + <div id="abspos"></div> + <span id="line1">Line 1</span> <br> + <span id="wide-atomic"></span> + Line 2 +</div> + +<script> + window.onload = () => { + requestAnimationFrame(() => { + requestAnimationFrame(() => { + document.getElementById("line1").style.lineHeight = "initial"; + takeScreenshot(); + }); + }); + }; +</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/line-clamp/block-ellipsis-repaint-004.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/line-clamp/block-ellipsis-repaint-004.html new file mode 100644 index 0000000..23c32d4 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/line-clamp/block-ellipsis-repaint-004.html
@@ -0,0 +1,46 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<meta charset="utf-8"> +<title>CSS Overflow: line-clamp ellipsis repaint tests</title> +<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#block-ellipsis"> +<link rel="match" href="reference/block-ellipsis-repaint-004-ref.html"> +<meta name="assert" content="This test makes sure that, if the inline root containing the ellipsis changes the styles that affect the ellipsis, the ellipsis will indeed change; and that this will happen even if nothing else needs to be repainted."> +<script src="/common/reftest-wait.js"></script> +<style> +#clamp { + line-clamp: 3; + border: 1px solid black; + padding: 4px; + background-color: yellow; + + font: 16px/0 serif; + + & span { + font: 16px/16px serif; + } +} +</style> + +<p>The ellipsis should be bold and twice as large as the rest of the text.</p> + +<div id="clamp"> + <span> + Line 1 <br> + Line 2 <br> + Line 3 <br> + Line 4 + </span> +</div> + +<script> + window.onload = () => { + requestAnimationFrame(() => { + requestAnimationFrame(() => { + document.getElementById("clamp").style.fontSize = "32px"; + document.getElementById("clamp").style.fontWeight = "bold"; + takeScreenshot(); + }); + }); + }; +</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/line-clamp/reference/block-ellipsis-repaint-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/line-clamp/reference/block-ellipsis-repaint-001-ref.html new file mode 100644 index 0000000..8a80598 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/line-clamp/reference/block-ellipsis-repaint-001-ref.html
@@ -0,0 +1,14 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Reference</title> +<style> +.clamp { + border: 1px solid black; + padding: 4px; + background-color: yellow; +} +</style> + +<p>The ellipsis should appear right after the end of the first line.</p> + +<div class="clamp">Some text here…</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/line-clamp/reference/block-ellipsis-repaint-002-ref.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/line-clamp/reference/block-ellipsis-repaint-002-ref.html new file mode 100644 index 0000000..b26e597 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/line-clamp/reference/block-ellipsis-repaint-002-ref.html
@@ -0,0 +1,21 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Reference</title> +<style> +.clamp { + border: 1px solid black; + padding: 4px; + background-color: yellow; + height: 3.5lh; +} +#line1 { + line-height: 2lh; +} +</style> + +<p>The ellipsis should appear right after the end of the second line.</p> + +<div class="clamp"> + <span id="line1">Line 1</span> <br> + Line 2… +</div> \ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/line-clamp/reference/block-ellipsis-repaint-003-ref.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/line-clamp/reference/block-ellipsis-repaint-003-ref.html new file mode 100644 index 0000000..95c319f3 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/line-clamp/reference/block-ellipsis-repaint-003-ref.html
@@ -0,0 +1,24 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Reference</title> +<style> +.clamp { + border: 1px solid black; + padding: 4px; + background-color: yellow; + height: 3lh; + width: 400px; +} +#ellipsis { + display: inline-block; + width: 2em; + background-color: pink; +} +</style> + +<p>The ellipsis should appear inside the pink box.</p> + +<div class="clamp"> + <span id="line1">Line 1</span> <br> + <span id="ellipsis">…</span> +</div> \ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/css/css-overflow/line-clamp/reference/block-ellipsis-repaint-004-ref.html b/third_party/blink/web_tests/external/wpt/css/css-overflow/line-clamp/reference/block-ellipsis-repaint-004-ref.html new file mode 100644 index 0000000..ec5756f --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-overflow/line-clamp/reference/block-ellipsis-repaint-004-ref.html
@@ -0,0 +1,23 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Reference</title> +<style> +#clamp { + border: 1px solid black; + padding: 4px; + background-color: yellow; + + font: 16px/16px serif; +} +.ellipsis { + font: bold 32px/0 serif; +} +</style> + +<p>The ellipsis should be bold and twice as large as the rest of the text.</p> + +<div id="clamp"> + Line 1 <br> + Line 2 <br> + Line 3<span class="ellipsis">…</span> +</div> \ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/css/css-view-transitions/finished-promise-defers-cleanup.html b/third_party/blink/web_tests/external/wpt/css/css-view-transitions/finished-promise-defers-cleanup.html index 8515edf..73b986e 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-view-transitions/finished-promise-defers-cleanup.html +++ b/third_party/blink/web_tests/external/wpt/css/css-view-transitions/finished-promise-defers-cleanup.html
@@ -14,7 +14,7 @@ view-transition-name: target; } ::view-transition-group(*) { - animation-duration: 1ms; + animation-duration: 0ms; } ::view-transition-old(target) { background-color: rgb(0, 255, 0); @@ -29,20 +29,31 @@ const transition = document.startViewTransition(() => {}); + // When we're ready, we request a new animation frame (in which the vt should be finished), + // and we post a task to run right after the update the rendering to verify that the view + // transition pseudos are still there. Wait until that happens. + await transition.ready.then(async () => { + await new Promise(resolve => { + requestAnimationFrame(() => { + t.step_timeout(() => { + assert_equals(getComputedStyle(document.documentElement, "::view-transition").display, "block"); + assert_equals(getComputedStyle(document.documentElement, "::view-transition").position, "fixed"); + resolve(); + }, 0); + }); + }); + }); + + // From here, we expect that the finished promise will run before any new rAFs. + let success = true; + requestAnimationFrame(() => success = false); + + // When the finished promise runs, verify that everything is cleaned up and that we didn't + // have a rAF between the timeout function and here. transition.finished.then(t.step_func(() => { - // At this point, the transition should not be fully cleaned up yet. - // The pseudo-elements should still be present. Verified as view-transition being block-level. - assert_equals(getComputedStyle(document.documentElement, "::view-transition").display, "block"); - assert_equals(getComputedStyle(document.documentElement, "::view-transition").position, "fixed"); + assert_true(success); + assert_equals(getComputedStyle(document.documentElement, "::view-transition").display, "inline"); + assert_equals(getComputedStyle(document.documentElement, "::view-transition").position, "static"); })); - - await transition.finished; - - await new Promise(requestAnimationFrame); - - // After the next frame, the cleanup should have happened. Verified as view-transition being inline-levle. - assert_equals(getComputedStyle(document.documentElement, "::view-transition").display, "inline"); - assert_equals(getComputedStyle(document.documentElement, "::view-transition").position, "static"); - }, "View transition cleanup is deferred until after the frame where `finished` promise resolves"); </script>
diff --git a/third_party/blink/web_tests/fast/forms/button/button-command-console-messages-expected.txt b/third_party/blink/web_tests/fast/forms/button/button-command-console-messages-expected.txt new file mode 100644 index 0000000..0904e2b1 --- /dev/null +++ b/third_party/blink/web_tests/fast/forms/button/button-command-console-messages-expected.txt
@@ -0,0 +1,5 @@ +CONSOLE WARNING: Buttons associated with forms that include command or commandfor attributes are ambiguous, and require a type=button attribute. No action will be taken. +CONSOLE WARNING: Buttons associated with forms that include command or commandfor attributes are ambiguous, and require a type=button attribute. No action will be taken. +CONSOLE WARNING: Buttons with an explicit type=submit will always submit a form, so command or commandfor attributes will be ignored. +CONSOLE WARNING: Buttons with an explicit type=reset will always reset a form, so command or commandfor attributes will be ignored. +
diff --git a/third_party/blink/web_tests/fast/forms/button/button-command-console-messages.html b/third_party/blink/web_tests/fast/forms/button/button-command-console-messages.html new file mode 100644 index 0000000..9faaad78 --- /dev/null +++ b/third_party/blink/web_tests/fast/forms/button/button-command-console-messages.html
@@ -0,0 +1,50 @@ +<!DOCTYPE html> +<html> +<body> + +<form id="testForm"> + <!-- Button with command attribute but ambiguous type (should generate console warning) --> + <button id="ambiguousButton" command="test-command"></button> + + <!-- Button with commandfor attribute but ambiguous type (should generate console warning) --> + <button id="ambiguousButtonFor" commandfor="test-target"></button> + + <!-- Button with explicit type=submit and command attribute (should generate console warning) --> + <button id="submitWithCommand" type="submit" command="test-command"></button> + + <!-- Button with explicit type=reset and commandfor attribute (should generate console warning) --> + <button id="resetWithCommandFor" type="reset" commandfor="test-target"></button> + + <!-- Button with explicit type=button and command attribute (should NOT generate console warning) --> + <button id="buttonWithCommand" type="button" command="test-command"></button> +</form> +<div id="test-target"></div> + +<script> + // Add event listener to prevent default form submission behavior. + document.getElementById('testForm').addEventListener('submit', function(e) { + e.preventDefault(); + }); + + // Button with command attribute but no explicit type=button should generate console warning. + document.getElementById('ambiguousButton').click(); + + // Button with commandfor attribute but no explicit type=button should generate console warning. + document.getElementById('ambiguousButtonFor').click(); + + // Button with type=submit and command attribute should generate console warning. + document.getElementById('submitWithCommand').click(); + + // Button with type=reset and commandfor attribute should generate console warning. + document.getElementById('resetWithCommandFor').click(); + + // Button with type=button and command attribute should NOT generate console warning. + document.getElementById('buttonWithCommand').click(); +</script> + +<script> + testRunner.dumpAsText(); +</script> + +</body> +</html>
diff --git a/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/dark-mode-images-filter-all/dark-mode/images/crossfade-expected.png b/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/dark-mode-images-filter-all/dark-mode/images/crossfade-expected.png deleted file mode 100644 index bcf7ed0b..0000000 --- a/third_party/blink/web_tests/platform/mac-mac13-arm64/virtual/dark-mode-images-filter-all/dark-mode/images/crossfade-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/dark-mode-images-filter-all/dark-mode/images/crossfade-expected.png b/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/dark-mode-images-filter-all/dark-mode/images/crossfade-expected.png deleted file mode 100644 index 683f9437..0000000 --- a/third_party/blink/web_tests/platform/mac-mac15-arm64/virtual/dark-mode-images-filter-all/dark-mode/images/crossfade-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/dark-mode-images-filter-all/dark-mode/images/crossfade-expected.png b/third_party/blink/web_tests/platform/mac/virtual/dark-mode-images-filter-all/dark-mode/images/crossfade-expected.png deleted file mode 100644 index 42515b1..0000000 --- a/third_party/blink/web_tests/platform/mac/virtual/dark-mode-images-filter-all/dark-mode/images/crossfade-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/blink/web_tests/platform/win11-arm64/virtual/dark-mode-images-filter-all/dark-mode/images/crossfade-expected.png b/third_party/blink/web_tests/platform/win11-arm64/virtual/dark-mode-images-filter-all/dark-mode/images/crossfade-expected.png deleted file mode 100644 index 122eed1..0000000 --- a/third_party/blink/web_tests/platform/win11-arm64/virtual/dark-mode-images-filter-all/dark-mode/images/crossfade-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/DIR_METADATA b/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/DIR_METADATA deleted file mode 100644 index ae69a28..0000000 --- a/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/DIR_METADATA +++ /dev/null
@@ -1,7 +0,0 @@ -monorail: { - component: "Blink>Paint" -} -team_email: "paint-dev@chromium.org" -buganizer_public: { - component_id: 1456440 -} \ No newline at end of file
diff --git a/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/README.txt b/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/README.txt deleted file mode 100644 index 091e87a..0000000 --- a/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/README.txt +++ /dev/null
@@ -1,4 +0,0 @@ -# This suite runs the tests in web_tests/dark-mode/images with -# --blink-settings=forceDarkModeEnabled=true and -# --dark-mode-settings=ImagePolicy=0 -# See the virtual_test_suites() method in tools/blinkpy/web_tests/port/base.py.
diff --git a/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/crossfade-expected.png b/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/crossfade-expected.png deleted file mode 100644 index 4d9386e..0000000 --- a/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/crossfade-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/desaturate-before-inversion-expected.png b/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/desaturate-before-inversion-expected.png deleted file mode 100644 index 7f2aa79..0000000 --- a/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/desaturate-before-inversion-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/gradient-expected.png b/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/gradient-expected.png deleted file mode 100644 index 5ff8edf..0000000 --- a/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/gradient-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/image-expected.png b/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/image-expected.png deleted file mode 100644 index f74d3ecf..0000000 --- a/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/image-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/none-repeat-image-as-background-expected.html b/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/none-repeat-image-as-background-expected.html deleted file mode 100644 index 3417368..0000000 --- a/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/none-repeat-image-as-background-expected.html +++ /dev/null
@@ -1,7 +0,0 @@ -<!DOCTYPE html> -<head> - <meta name="color-scheme" content="only light"> -</head> -<body style="background: #121212;"> -<div style="width: 200px; height: 199px; background: black"></div> -</body>
diff --git a/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/opt-out-background-image-expected.html b/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/opt-out-background-image-expected.html deleted file mode 100644 index c194959..0000000 --- a/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/opt-out-background-image-expected.html +++ /dev/null
@@ -1,5 +0,0 @@ -<!DOCTYPE html> -<meta name="color-scheme" content="only light"> -<body style="background: #121212;"> - <div style="width: 200px; height: 199px; background: white"></div> -</body>
diff --git a/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/opt-out-svg-gradient-expected.png b/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/opt-out-svg-gradient-expected.png deleted file mode 100644 index c11a456..0000000 --- a/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/opt-out-svg-gradient-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/pattern-expected.png b/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/pattern-expected.png deleted file mode 100644 index a9c3b1ab..0000000 --- a/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/pattern-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/repeat-image-as-background-expected.html b/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/repeat-image-as-background-expected.html deleted file mode 100644 index 7b1c85e..0000000 --- a/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/repeat-image-as-background-expected.html +++ /dev/null
@@ -1,7 +0,0 @@ -<!DOCTYPE html> -<head> - <meta name="color-scheme" content="only light"> -</head> -<body style="background: #121212;"> -<div style="width: 400px; height: 400px; background: black"></div> -</body>
diff --git a/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/shadow-expected.png b/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/shadow-expected.png deleted file mode 100644 index 7fe58b8..0000000 --- a/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/shadow-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/svg-expected.png b/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/svg-expected.png deleted file mode 100644 index 374b58f..0000000 --- a/third_party/blink/web_tests/virtual/dark-mode-images-filter-all/dark-mode/images/svg-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/bspatch/BUILD.gn b/third_party/bspatch/BUILD.gn index 7c4bfa3..1d88f3c1 100644 --- a/third_party/bspatch/BUILD.gn +++ b/third_party/bspatch/BUILD.gn
@@ -2,6 +2,8 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//third_party/bspatch/visibility.gni") + source_set("bspatch") { sources = [ "mbspatch.cc",
diff --git a/third_party/bspatch/visibility.gni b/third_party/bspatch/visibility.gni new file mode 100644 index 0000000..937b436c --- /dev/null +++ b/third_party/bspatch/visibility.gni
@@ -0,0 +1,6 @@ +# bspatch is infrequently updated and rarely used. New additions to the below +# list should be scrutinized. +visibility = [ + "//chrome/installer/setup/*", + "//third_party/bspatch", +]
diff --git a/third_party/chromite b/third_party/chromite index f8b4773..3411a36 160000 --- a/third_party/chromite +++ b/third_party/chromite
@@ -1 +1 @@ -Subproject commit f8b47735153c5ccef3996503bd57ba270130ee4b +Subproject commit 3411a369f26bdab1098052dcc0805fe3e1063e4b
diff --git a/third_party/compiler-rt/src b/third_party/compiler-rt/src index d507e8c..a0b0e48 160000 --- a/third_party/compiler-rt/src +++ b/third_party/compiler-rt/src
@@ -1 +1 @@ -Subproject commit d507e8c8847deb237602cb68fbff2ac75c666144 +Subproject commit a0b0e482f99c0b79b1c5330bd017157d9f23e700
diff --git a/third_party/depot_tools b/third_party/depot_tools index ba39260..e5797fd 160000 --- a/third_party/depot_tools +++ b/third_party/depot_tools
@@ -1 +1 @@ -Subproject commit ba3926011ba1bd617b6f368b59ec5e19647b96f6 +Subproject commit e5797fdfad58cea9abb465291a06db8156b55177
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src index 5357bd7..57b2c1d 160000 --- a/third_party/devtools-frontend/src +++ b/third_party/devtools-frontend/src
@@ -1 +1 @@ -Subproject commit 5357bd73fb7c69cabd7fbb4d098c43b1e63d651a +Subproject commit 57b2c1d83fd82da2f49f0644023046ee5f964832
diff --git a/third_party/libc++/src b/third_party/libc++/src index 1ce2525..c105b13 160000 --- a/third_party/libc++/src +++ b/third_party/libc++/src
@@ -1 +1 @@ -Subproject commit 1ce2525ae355a30a61bacd896916c3416d1827b1 +Subproject commit c105b13e377d3fa5fdf033957f31abc1894a9968
diff --git a/third_party/omnibox_proto/BUILD.gn b/third_party/omnibox_proto/BUILD.gn index ba64314e..00c8caae 100644 --- a/third_party/omnibox_proto/BUILD.gn +++ b/third_party/omnibox_proto/BUILD.gn
@@ -9,6 +9,7 @@ } _proto_files = [ + "aim_eligibility_response.proto", "answer_data.proto", "answer_type.proto", "chrome_aim_entry_point.proto",
diff --git a/third_party/omnibox_proto/README.chromium b/third_party/omnibox_proto/README.chromium index 06401c1..fc243e9 100644 --- a/third_party/omnibox_proto/README.chromium +++ b/third_party/omnibox_proto/README.chromium
@@ -1,8 +1,8 @@ Name: Omnibox Protos Short Name: omnibox_proto URL: This is the canonical public repository -Version: 788916415 -Date: 2025-07-30 +Version: 789118594 +Date: 2025-07-31 License: BSD-3-Clause License File: LICENSE Shipped: yes
diff --git a/third_party/omnibox_proto/aim_eligibility_response.proto b/third_party/omnibox_proto/aim_eligibility_response.proto new file mode 100644 index 0000000..f14d5eb1 --- /dev/null +++ b/third_party/omnibox_proto/aim_eligibility_response.proto
@@ -0,0 +1,17 @@ +// Copyright 2025 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +syntax = 'proto2'; + +option optimize_for = LITE_RUNTIME; +option java_package = 'org.chromium.components.omnibox'; +option java_outer_classname = 'AimEligibilityResponseProto'; + +package omnibox; + +// AIM Eligibility Response. +// Next ID: 2 +message AimEligibilityResponse { + optional bool is_eligible = 1; +}
diff --git a/third_party/perfetto b/third_party/perfetto index 6f02692..54b52eb 160000 --- a/third_party/perfetto +++ b/third_party/perfetto
@@ -1 +1 @@ -Subproject commit 6f026925036beba8c0911aef979234d68413524c +Subproject commit 54b52ebe0a12a5672cd9544f6c1e3162ddc9d70c
diff --git a/third_party/skia b/third_party/skia index a0b1007..49e39cd 160000 --- a/third_party/skia +++ b/third_party/skia
@@ -1 +1 @@ -Subproject commit a0b1007063b10045c078a5c499d921ac77d93579 +Subproject commit 49e39cd3cf1847f5e0464b4652735d6f0091f93d
diff --git a/third_party/webrtc b/third_party/webrtc index 75c4772..8d88442 160000 --- a/third_party/webrtc +++ b/third_party/webrtc
@@ -1 +1 @@ -Subproject commit 75c4772c989e9e7551f1186c72a27ff8c4a8a7ec +Subproject commit 8d884426cc2fc6b44a4315ed4917035d218992d9
diff --git a/tools/clang/plugins/FindBadConstructsAction.cpp b/tools/clang/plugins/FindBadConstructsAction.cpp index 4483bb3..46989a9 100644 --- a/tools/clang/plugins/FindBadConstructsAction.cpp +++ b/tools/clang/plugins/FindBadConstructsAction.cpp
@@ -71,10 +71,6 @@ options_.check_ipc = true; } else if (arg == "check-layout-object-methods") { options_.check_layout_object_methods = true; - } else if (arg == "raw-ref-template-as-trivial-member") { - // TODO(crbug.com/394919686): Remove once plugin is rolled and GN updated. - } else if (arg == "raw-span-template-as-trivial-member") { - // TODO(crbug.com/394919686): Remove once plugin is rolled and GN updated. } else if (arg == "check-stack-allocated") { options_.check_stack_allocated = true; } else if (arg == "check-ptrs-to-non-string-literals") { @@ -95,8 +91,6 @@ options_.check_span_fields = true; } else if (arg == "enable-match-profiling") { options_.enable_match_profiling = true; - } else if (arg == "span-ctor-from-string-literal") { - // TODO(crbug.com/394919686): Remove once plugin is rolled and GN updated. } else { llvm::errs() << "Unknown clang plugin argument: " << arg << "\n"; return false;
diff --git a/tools/clang/plugins/tests/missing_ctor.flags b/tools/clang/plugins/tests/missing_ctor.flags deleted file mode 100644 index e50201a6..0000000 --- a/tools/clang/plugins/tests/missing_ctor.flags +++ /dev/null
@@ -1 +0,0 @@ --Xclang -plugin-arg-find-bad-constructs -Xclang raw-ref-template-as-trivial-member -Xclang -plugin-arg-find-bad-constructs -Xclang raw-span-template-as-trivial-member
diff --git a/tools/clang/plugins/tests/span_from_string_literal.flags b/tools/clang/plugins/tests/span_from_string_literal.flags deleted file mode 100644 index 7d784f9..0000000 --- a/tools/clang/plugins/tests/span_from_string_literal.flags +++ /dev/null
@@ -1 +0,0 @@ --Xclang -plugin-arg-find-bad-constructs -Xclang span-ctor-from-string-literal
diff --git a/tools/clang/unsafe_pragma_rewriter/extract_failures.py b/tools/clang/unsafe_pragma_rewriter/extract_failures.py index 82b529c..487042e 100755 --- a/tools/clang/unsafe_pragma_rewriter/extract_failures.py +++ b/tools/clang/unsafe_pragma_rewriter/extract_failures.py
@@ -14,12 +14,12 @@ # Regex to match lines like '../../ipc/ipc_logging.cc:265:5: error:' # It captures the path and filename. error_pattern = re.compile( - r"^(?P<filepath>[^:]+):\d+:\d+:.*\s*(error|warning):") + rb"^(?P<filepath>[^:]+):\d+:\d+:.*\s*(error|warning):") for line in log_content.splitlines(): match = error_pattern.match(line) if match: - filepath = match.group('filepath') + filepath = match.group('filepath').decode('ascii') failed_files.add(filepath) return failed_files @@ -27,7 +27,7 @@ def main(): """Reads the compile log from standard input, extracts failed files, and prints them one per line to standard output.""" - log_content = sys.stdin.read() + log_content = sys.stdin.buffer.read() failed_files = extract_failed_files(log_content) for filename in sorted(list(failed_files)): print(filename.removeprefix("../../"))
diff --git a/tools/clang/unsafe_pragma_rewriter/extract_sources.py b/tools/clang/unsafe_pragma_rewriter/extract_sources.py index 85f5a075..be0a8f03 100755 --- a/tools/clang/unsafe_pragma_rewriter/extract_sources.py +++ b/tools/clang/unsafe_pragma_rewriter/extract_sources.py
@@ -14,12 +14,12 @@ # Regex to match lines like: # '[1688/67026] 11.01s ... clang++ ... -c ./../ipc/ipc_logging.cc' # It captures the path and filename. - pattern = re.compile(r"^\[\d+/\d+\] .*clang\+\+ .* -c ([^ ]+) -o [^ ]+") + pattern = re.compile(rb"^\[\d+/\d+\] .*clang\+\+ .* -c ([^ ]+) -o [^ ]+") for line in log_content.splitlines(): match = pattern.match(line) if match: - filepath = match.group(1) + filepath = match.group(1).decode('ascii') tried_files.add(filepath) return tried_files @@ -27,7 +27,7 @@ def main(): """Reads the compile log from standard input, extracts tried files, and prints them one per line to standard output.""" - log_content = sys.stdin.read() + log_content = sys.stdin.buffer.read() tried_files = extract_tried_files(log_content) for filename in sorted(list(tried_files)): print(filename.removeprefix("../../"))
diff --git a/tools/clang/unsafe_pragma_rewriter/remove_unsafe_pragma.py b/tools/clang/unsafe_pragma_rewriter/remove_unsafe_pragma.py index 8d503178..6d5339b 100755 --- a/tools/clang/unsafe_pragma_rewriter/remove_unsafe_pragma.py +++ b/tools/clang/unsafe_pragma_rewriter/remove_unsafe_pragma.py
@@ -19,7 +19,7 @@ def process_file(filepath): try: - with open(filepath, 'r', encoding='utf-8') as f: + with open(filepath, 'r') as f: lines = f.readlines() except Exception as e: print(f"Error: Could not read file {filepath}: {e}", file=sys.stderr) @@ -65,7 +65,7 @@ if len(lines_to_write) != original_line_count: try: - with open(filepath, 'w', encoding='utf-8') as f: + with open(filepath, 'w') as f: f.writelines(lines_to_write) except Exception as e: print(f"Error: Could not write to file {filepath}: {e}", file=sys.stderr)
diff --git a/tools/clang/unsafe_pragma_rewriter/rewrite_directory.py b/tools/clang/unsafe_pragma_rewriter/rewrite_directory.py index 1b5f245..42b9b26 100755 --- a/tools/clang/unsafe_pragma_rewriter/rewrite_directory.py +++ b/tools/clang/unsafe_pragma_rewriter/rewrite_directory.py
@@ -62,8 +62,13 @@ tmpdir = tempfile.mkdtemp(None, "unsafe_pragma_rewriter.") print(f"Temporary files will be written to {tmpdir}\n") - grep_cmd = ["git", "grep", "-l", "^#pragma allow_unsafe_", directory] - grep = subprocess.check_output(grep_cmd, text=True).strip() + try: + grep_cmd = ["git", "grep", "-l", "^#pragma allow_unsafe_", directory] + grep = subprocess.check_output(grep_cmd, text=True).strip() + except Exception as e: + print("No candidates found") + sys.exit(1) + grep_lines = grep.splitlines() if grep else [] source_files = [x for x in grep_lines if re.match(r".*\.cc$", x)] if not source_files: @@ -166,9 +171,14 @@ check=True) if source_files: - needs_header = subprocess.check_output( - ["git", "grep", "-l", "UNSAFE_TODO"] + source_files, text=True).strip() - needs_header_files = needs_header.splitlines() if needs_header else [] + try: + needs_header_cmd = ["git", "grep", "-l", "UNSAFE_TODO"] + source_files + needs_header = subprocess.check_output(needs_header_cmd, + text=True).strip() + needs_header_files = needs_header.splitlines() if needs_header else [] + except Exception as e: + needs_header_files = [] + if needs_header_files: subprocess.run( ["tools/add_header.py", "--header", '"base/compiler_specific.h"'] +
diff --git a/tools/cygprofile/orderfile_generator_backend.py b/tools/cygprofile/generate_orderfile_full.py similarity index 95% rename from tools/cygprofile/orderfile_generator_backend.py rename to tools/cygprofile/generate_orderfile_full.py index 26adb8a4..39fcc4c 100755 --- a/tools/cygprofile/orderfile_generator_backend.py +++ b/tools/cygprofile/generate_orderfile_full.py
@@ -2,18 +2,16 @@ # Copyright 2013 The Chromium Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. - """ A utility to generate an orderfile. The orderfile is used by the linker to order symbols such that they are placed consecutively. See //docs/orderfile.md. Example usage: - tools/cygprofile/orderfile_generator_backend.py --use-remoteexec \ + tools/cygprofile/generate_orderfile_full.py --use-remoteexec \ --target-arch=arm64 """ - import argparse import ast import csv @@ -454,8 +452,8 @@ use_debug_location: (bool) Whether to use the debug location. use_new_cloud: (bool) Whether to use the new workflow and modify DEPS. """ - bucket = (self._CLOUD_STORAGE_BUCKET_FOR_DEBUG if use_debug_location - else self._CLOUD_STORAGE_BUCKET) + bucket = (self._CLOUD_STORAGE_BUCKET_FOR_DEBUG + if use_debug_location else self._CLOUD_STORAGE_BUCKET) extension = _GetFileExtension(filename) cmd = [self._UPLOAD_TO_CLOUD_COMMAND, '--bucket', bucket] if extension: @@ -589,7 +587,6 @@ return devices[0] - def __init__(self, options, orderfile_updater_class): self._options = options self._native_library_build_variant = NativeLibraryBuildVariant.TRICHROME @@ -727,7 +724,7 @@ 'Outlined function found in instrumented function, very likely ' 'something has gone very wrong!') self._output_data['offsets_kib'] = processor.SymbolsSize( - ordered_symbols) / 1024 + ordered_symbols) / 1024 with open(self._GetUnpatchedOrderfileFilename(), 'w') as orderfile: orderfile.write('\n'.join(ordered_symbols)) @@ -769,8 +766,8 @@ def _RecordHash(self, file_name): """Records the hash of the file into the output_data dictionary.""" - self._output_data[os.path.basename(file_name) + '.sha1'] = _GenerateHash( - file_name) + self._output_data[os.path.basename(file_name) + + '.sha1'] = _GenerateHash(file_name) def _SaveFileLocally(self, file_name, file_sha1): """Saves the file to a temporary location and prints the sha1sum.""" @@ -830,10 +827,12 @@ def UploadReadyOrderfiles(self): self._step_recorder.BeginStep('Upload Ready Orderfiles') - for file_name in [self._GetUnpatchedOrderfileFilename(), - self._GetPathToOrderfile()]: - self._orderfile_updater.UploadToCloudStorage( - file_name, use_debug_location=False) + for file_name in [ + self._GetUnpatchedOrderfileFilename(), + self._GetPathToOrderfile() + ]: + self._orderfile_updater.UploadToCloudStorage(file_name, + use_debug_location=False) def _WebViewStartupBenchmark(self, apk: str): """Runs system_health.webview_startup benchmark. @@ -910,8 +909,8 @@ # story run, so this average (reported as 'avg') is exactly the value # of that one sample. Each story is run multiple times, so this loop # will accumulate into a list all values for all runs of each story. - results.setdefault(row['name'], {}).setdefault( - row['stories'], []).append(row['avg']) + results.setdefault(row['name'], {}).setdefault(row['stories'], + []).append(row['avg']) if not results: raise Exception('Could not find relevant results') @@ -966,7 +965,6 @@ finally: shutil.rmtree(out_dir) - def RunBenchmark(self, out_directory: pathlib.Path, no_orderfile: bool = False) -> Dict: @@ -997,15 +995,13 @@ open(orderfile_path, 'w').close() # Build APK to be installed on the device. - self._compiler.CompileChromeApk(instrumented=False, - force_relink=True) + self._compiler.CompileChromeApk(instrumented=False, force_relink=True) benchmark_results[_RESULTS_KEY_SPEEDOMETER] = self._PerformanceBenchmark( self._compiler.chrome_apk_path) benchmark_results['orderfile.memory_mobile'] = ( self._NativeCodeMemoryBenchmark(self._compiler.chrome_apk_path)) if self._options.profile_webview_startup: - self._compiler.CompileWebViewApk(instrumented=False, - force_relink=True) + self._compiler.CompileWebViewApk(instrumented=False, force_relink=True) self._profiler.InstallAndSetWebViewProvider( self._compiler.webview_installer_path) benchmark_results[ @@ -1122,16 +1118,22 @@ def CreateArgumentParser(): """Creates and returns the argument parser.""" parser = argparse.ArgumentParser() - parser.add_argument('--no-benchmark', action='store_false', dest='benchmark', - default=True, help='Disables running benchmarks.') + parser.add_argument('--no-benchmark', + action='store_false', + dest='benchmark', + default=True, + help='Disables running benchmarks.') parser.add_argument( - '--buildbot', action='store_true', + '--buildbot', + action='store_true', help='If true, the script expects to be run on a buildbot') + parser.add_argument('--device', + default=None, + type=str, + help='Device serial number on which to run profiling.') parser.add_argument( - '--device', default=None, type=str, - help='Device serial number on which to run profiling.') - parser.add_argument( - '--verify', action='store_true', + '--verify', + action='store_true', help='If true, the script only verifies the current orderfile') parser.add_argument('--target-arch', action='store', @@ -1139,7 +1141,9 @@ default='arm', choices=list(_ARCH_GN_ARGS.keys()), help='The target architecture for which to build.') - parser.add_argument('--output-json', action='store', dest='json_file', + parser.add_argument('--output-json', + action='store', + dest='json_file', help='Location to save stats in json format') parser.add_argument( '--skip-profile', @@ -1179,11 +1183,15 @@ default=False, help='Use the webview startup benchmark profiles to ' 'generate the orderfile.') - parser.add_argument('--pregenerated-profiles', default=None, type=str, + parser.add_argument('--pregenerated-profiles', + default=None, + type=str, help=('Pregenerated profiles to use instead of running ' 'profile step. Cannot be used with ' '--skip-profiles.')) - parser.add_argument('--profile-save-dir', default=None, type=str, + parser.add_argument('--profile-save-dir', + default=None, + type=str, help=('Directory to save any profiles created. These can ' 'be used with --pregenerated-profiles. Cannot be ' 'used with --skip-profiles.')) @@ -1260,8 +1268,7 @@ except Exception: logging.exception('Generator failure') finally: - json_output = json.dumps(generator.GetReportingData(), - indent=2) + '\n' + json_output = json.dumps(generator.GetReportingData(), indent=2) + '\n' if options.json_file: with open(options.json_file, 'w') as f: f.write(json_output)
diff --git a/tools/cygprofile/orderfile_generator_backend_unittest.py b/tools/cygprofile/generate_orderfile_full_unittest.py similarity index 82% rename from tools/cygprofile/orderfile_generator_backend_unittest.py rename to tools/cygprofile/generate_orderfile_full_unittest.py index 9883093d..45d55a0 100755 --- a/tools/cygprofile/orderfile_generator_backend_unittest.py +++ b/tools/cygprofile/generate_orderfile_full_unittest.py
@@ -7,12 +7,14 @@ import tempfile import unittest -import orderfile_generator_backend +import generate_orderfile_full + class TestOrderfileGenerator(unittest.TestCase): + def testStepRecorder(self): """Checks that the step recorder records step timings correctly.""" - step_recorder = orderfile_generator_backend.StepRecorder() + step_recorder = generate_orderfile_full.StepRecorder() self.assertFalse(step_recorder.ErrorRecorded()) step_recorder.BeginStep('foo') self.assertFalse(step_recorder.ErrorRecorded()) @@ -27,8 +29,9 @@ self.assertTrue(step_recorder.ErrorRecorded()) def testGetFileExtension(self): - self.assertEqual('zip', - orderfile_generator_backend._GetFileExtension('/foo/bar/baz.blub.zip')) + self.assertEqual( + 'zip', + generate_orderfile_full._GetFileExtension('/foo/bar/baz.blub.zip')) def testGenerateHash(self): try: @@ -36,7 +39,7 @@ filename = handle.name handle.write('foo') self.assertEqual('0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33', - orderfile_generator_backend._GenerateHash(filename)) + generate_orderfile_full._GenerateHash(filename)) finally: if filename: os.unlink(filename)
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml index 1505fa9..aaa728e 100644 --- a/tools/metrics/actions/actions.xml +++ b/tools/metrics/actions/actions.xml
@@ -50156,6 +50156,8 @@ label="For iOSLensOverlayEntrypoint IPH feature"/> <suffix name="iOSLensOverlayEscapeHatchTip" label="For iOSLensOverlayEscapeHatchTip IPH feature"/> + <suffix name="iOSOneTimeDefaultBrowserNotification" + label="For iOSOneTimeDefaultBrowserNotification feature"/> <suffix name="iOSOverflowMenuCustomization" label="For iOSOverflowMenuCustomization feature"/> <suffix name="IOSPageActionMenu" label="For IOSPageActionMenu IPH"/>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml index 2c3602b..616d1f0 100644 --- a/tools/metrics/histograms/enums.xml +++ b/tools/metrics/histograms/enums.xml
@@ -781,6 +781,12 @@ <int value="50" label="Unrecognized"/> </enum> +<enum name="AutoDeletionServiceActionsType"> + <int value="0" label="FileScheduledForAutoDeletion"/> + <int value="1" label="ScheduledFileIdentifiedForRemoval"/> + <int value="2" label="ScheduledFileRemovedFromDevice"/> +</enum> + <enum name="AutoScreenBrightnessAlsReaderStatus"> <int value="0" label="Success"/> <int value="1" label="InProgress"/> @@ -8733,6 +8739,7 @@ <int value="-1974183367" label="GlanceablesV2:enabled"/> <int value="-1973722176" label="VoiceSearchOnLocalNtp:enabled"/> <int value="-1973559591" label="AppDiscoveryForOobe:enabled"/> + <int value="-1973106081" label="JupiterScreensaver:disabled"/> <int value="-1972383451" label="disable-pinch"/> <int value="-1972312724" label="OfflinePagesLoadSignalCollecting:enabled"/> <int value="-1972219399" label="NTPSaveToOffline:enabled"/> @@ -9929,6 +9936,7 @@ <int value="-1532645183" label="ReleaseNotesNotification:enabled"/> <int value="-1532501905" label="MediaPlaybackWhileNotVisiblePermissionPolicy:disabled"/> + <int value="-1532138473" label="AvatarButtonSyncPromo:enabled"/> <int value="-1532100164" label="WebViewMeasureScreenCoverage:enabled"/> <int value="-1532035450" label="DragTabsInTabletMode:disabled"/> <int value="-1532014193" label="disable-encryption-migration"/> @@ -13194,6 +13202,7 @@ <int value="-336090046" label="OmniboxNumNtpZpsRecentSearches:disabled"/> <int value="-335331552" label="TabGroupsForTablets:disabled"/> <int value="-334873793" label="MediaSessionNotification:enabled"/> + <int value="-334654963" label="JupiterScreensaver:enabled"/> <int value="-332010491" label="RetailCoupons:enabled"/> <int value="-331952590" label="OmniboxTriggerForPrerender2:enabled"/> <int value="-331137136" @@ -14233,6 +14242,7 @@ <int value="46201270" label="Taiyaki:disabled"/> <int value="46334141" label="FeatureNotificationGuideSkipCheckForLowEngagedUsers:enabled"/> + <int value="46893781" label="AvatarButtonSyncPromo:disabled"/> <int value="48159177" label="reduced-referrer-granularity"/> <int value="48619644" label="MostVisitedTilesVisualDeduplication:enabled"/> <int value="48666910" label="ClipboardHistoryLongpress:disabled"/>
diff --git a/tools/metrics/histograms/metadata/autofill/enums.xml b/tools/metrics/histograms/metadata/autofill/enums.xml index a4d488f..909c910 100644 --- a/tools/metrics/histograms/metadata/autofill/enums.xml +++ b/tools/metrics/histograms/metadata/autofill/enums.xml
@@ -903,6 +903,9 @@ value edited"/> <int value="12353" label="NATIONAL_ID_CARD_ISSUING_COUNTRY: Autofilled value accepted"/> + <int value="12416" + label="KNOWN_TRAVELER_NUMBER: Not autofilled or autofilled value edited"/> + <int value="12417" label="KNOWN_TRAVELER_NUMBER: Autofilled value accepted"/> <int value="12864" label="ADDRESS_HOME_ZIP_PREFIX: Not autofilled or autofilled value edited"/> @@ -913,6 +916,11 @@ edited"/> <int value="12929" label="ADDRESS_HOME_ZIP_SUFFIX: Autofilled value accepted"/> + <int value="12992" + label="KNOWN_TRAVELER_NUMBER_EXPIRATION_DATE: Not autofilled or + autofilled value edited"/> + <int value="12993" + label="KNOWN_TRAVELER_NUMBER_EXPIRATION_DATE: Autofilled value accepted"/> </enum> <enum name="AutofillDeveloperEngagement"> @@ -1230,10 +1238,14 @@ <int value="3073" label="NATIONAL_ID_CARD_ISSUE_DATE: accepted"/> <int value="3088" label="NATIONAL_ID_CARD_ISSUING_COUNTRY: edited"/> <int value="3089" label="NATIONAL_ID_CARD_ISSUING_COUNTRY: accepted"/> + <int value="3104" label="KNOWN_TRAVELER_NUMBER: edited"/> + <int value="3105" label="KNOWN_TRAVELER_NUMBER: accepted"/> <int value="3216" label="ADDRESS_HOME_ZIP_PREFIX: edited"/> <int value="3217" label="ADDRESS_HOME_ZIP_PREFIX: accepted"/> <int value="3232" label="ADDRESS_HOME_ZIP_SUFFIX: edited"/> <int value="3233" label="ADDRESS_HOME_ZIP_SUFFIX: accepted"/> + <int value="3248" label="KNOWN_TRAVELER_NUMBER_EXPIRATION_DATE: edited"/> + <int value="3249" label="KNOWN_TRAVELER_NUMBER_EXPIRATION_DATE: accepted"/> </enum> <enum name="AutofillEmailOrLoyaltyCardAcceptanceMetricValue"> @@ -2096,8 +2108,10 @@ <int value="191" label="NATIONAL_ID_CARD_EXPIRATION_DATE"/> <int value="192" label="NATIONAL_ID_CARD_ISSUE_DATE"/> <int value="193" label="NATIONAL_ID_CARD_ISSUING_COUNTRY"/> + <int value="194" label="KNOWN_TRAVELER_NUMBER"/> <int value="201" label="ADDRESS_HOME_ZIP_PREFIX"/> <int value="202" label="ADDRESS_HOME_ZIP_SUFFIX"/> + <int value="203" label="KNOWN_TRAVELER_NUMBER_EXPIRATION_DATE"/> </enum> <enum name="AutofillFilledCardInformationBubbleFieldClicked"> @@ -3761,10 +3775,14 @@ <int value="769" label="NATIONAL_ID_CARD_ISSUE_DATE: Accepted"/> <int value="772" label="NATIONAL_ID_CARD_ISSUING_COUNTRY: Ignored"/> <int value="773" label="NATIONAL_ID_CARD_ISSUING_COUNTRY: Accepted"/> + <int value="776" label="KNOWN_TRAVELER_NUMBER: Ignored"/> + <int value="777" label="KNOWN_TRAVELER_NUMBER: Accepted"/> <int value="804" label="ADDRESS_HOME_ZIP_PREFIX: Ignored"/> <int value="805" label="ADDRESS_HOME_ZIP_PREFIX: Accepted"/> <int value="808" label="ADDRESS_HOME_ZIP_SUFFIX: Ignored"/> <int value="809" label="ADDRESS_HOME_ZIP_SUFFIX: Accepted"/> + <int value="812" label="KNOWN_TRAVELER_NUMBER_EXPIRATION_DATE: Ignored"/> + <int value="813" label="KNOWN_TRAVELER_NUMBER_EXPIRATION_DATE: Accepted"/> </enum> <!-- LINT.IfChange(FormSubmissionOutcomeIOS) -->
diff --git a/tools/metrics/histograms/metadata/autofill/histograms.xml b/tools/metrics/histograms/metadata/autofill/histograms.xml index 1fcf60b..1c3e8a7b 100644 --- a/tools/metrics/histograms/metadata/autofill/histograms.xml +++ b/tools/metrics/histograms/metadata/autofill/histograms.xml
@@ -490,6 +490,8 @@ <variant name="EMAIL_OR_LOYALTY_MEMBERSHIP_ID"/> <variant name="EMPTY_TYPE"/> <variant name="IBAN_VALUE"/> + <variant name="KNOWN_TRAVELER_NUMBER"/> + <variant name="KNOWN_TRAVELER_NUMBER_EXPIRATION_DATE"/> <variant name="LOYALTY_MEMBERSHIP_ID"/> <variant name="LOYALTY_MEMBERSHIP_PROGRAM"/> <variant name="LOYALTY_MEMBERSHIP_PROVIDER"/>
diff --git a/tools/metrics/histograms/metadata/feature_engagement/histograms.xml b/tools/metrics/histograms/metadata/feature_engagement/histograms.xml index 3509a1d..cf8bab0 100644 --- a/tools/metrics/histograms/metadata/feature_engagement/histograms.xml +++ b/tools/metrics/histograms/metadata/feature_engagement/histograms.xml
@@ -309,6 +309,8 @@ summary="IOS Lens overlay toolbar entrypoint"/> <variant name="IPH_iOSLensOverlayEscapeHatchTip" summary="IOS Lens overlay Escape Hatch"/> + <variant name="IPH_iOSOneTimeDefaultBrowserNotification" + summary="scheduling the One Time Default Browser notification on iOS"/> <variant name="IPH_iOSOverflowMenuCustomization" summary="overflow menu customization"/> <variant name="IPH_iOSPageActionMenu"
diff --git a/tools/metrics/histograms/metadata/glic/enums.xml b/tools/metrics/histograms/metadata/glic/enums.xml index 17cc1bd..836fe1a 100644 --- a/tools/metrics/histograms/metadata/glic/enums.xml +++ b/tools/metrics/histograms/metadata/glic/enums.xml
@@ -26,18 +26,6 @@ <enums> -<!-- LINT.IfChange(ActInFocusedTabErrorReason) --> - -<enum name="ActInFocusedTabErrorReason"> - <int value="0" label="Unknown"/> - <int value="1" label="Get context failed"/> - <int value="2" label="Invalid action proto"/> - <int value="3" label="Target not found"/> - <int value="4" label="Failed to start task"/> -</enum> - -<!-- LINT.ThenChange(//chrome/browser/glic/host/glic.mojom:ActInFocusedTabErrorReason) --> - <!-- LINT.IfChange(ActiveTabSharingState) --> <enum name="ActiveTabSharingState">
diff --git a/tools/metrics/histograms/metadata/glic/histograms.xml b/tools/metrics/histograms/metadata/glic/histograms.xml index f50680b..745297d 100644 --- a/tools/metrics/histograms/metadata/glic/histograms.xml +++ b/tools/metrics/histograms/metadata/glic/histograms.xml
@@ -22,17 +22,6 @@ <histograms> -<histogram name="Glic.Action.ActInFocusedTabErrorReason" - enum="ActInFocusedTabErrorReason" expires_after="2026-01-15"> - <owner>carlosk@chromium.org</owner> - <owner>harringtond@chromium.org</owner> - <owner>bokan@chromium.org</owner> - <summary> - Recorded whenever the glic web client receives an error reason from the - browser when attempting to act. - </summary> -</histogram> - <histogram name="Glic.Api.FetchZeroStateSuggestionsLatency" units="ms" expires_after="2026-01-15"> <owner>sophiechang@chromium.org</owner> @@ -64,7 +53,6 @@ <!-- LINT.IfChange(ApiRequestType) --> <token key="ApiRequestType"> - <variant name="ActInFocusedTab"/> <variant name="AttachPanel"/> <variant name="CaptureScreenshot"/> <variant name="ClosePanel"/>
diff --git a/tools/metrics/histograms/metadata/ios/histograms.xml b/tools/metrics/histograms/metadata/ios/histograms.xml index 55ce3ff8..4754dd8 100644 --- a/tools/metrics/histograms/metadata/ios/histograms.xml +++ b/tools/metrics/histograms/metadata/ios/histograms.xml
@@ -235,6 +235,17 @@ </summary> </histogram> +<histogram name="IOS.AutoDeletion.ServiceActions" + enum="AutoDeletionServiceActionsType" expires_after="2026-08-01"> + <owner>danieltwhite@google.com</owner> + <owner>bling-fundamentals@google.com</owner> + <summary> + This event is recorded each time the AutoDeletionService successfully + completes an action. The enumeration indicates which action was successfully + completed. + </summary> +</histogram> + <histogram name="IOS.BackgroundRefresh.BGTaskSchedulerError" enum="BGTaskSchedulerErrorType" expires_after="2025-12-06"> <owner>fedegermi@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/navigation/enums.xml b/tools/metrics/histograms/metadata/navigation/enums.xml index cc56e8d..38fe902 100644 --- a/tools/metrics/histograms/metadata/navigation/enums.xml +++ b/tools/metrics/histograms/metadata/navigation/enums.xml
@@ -1921,6 +1921,23 @@ </int> </enum> +<!-- LINT.IfChange(RendererProcessReadiness) --> + +<enum name="RendererProcessReadiness"> + <int value="0" label="Process not ready"> + This implies that the renderer process should have been started earlier. + </int> + <int value="1" + label="Process ready, but view/proxy/frame objects not yet created"> + This implies that the speculative RenderFrameHost should have been created + earlier. + </int> + <int value="2" + label="Process ready, and all view/proxy/frame objects created"/> +</enum> + +<!-- LINT.ThenChange(//content/renderer/renderer_navigation_metrics_manager.cc:RendererProcessReadiness) --> + <enum name="ThirdPartyCookieBlockState"> <int value="0" label="Cookies allowed"/> <int value="1" label="Third-party cookies blocked"/>
diff --git a/tools/metrics/histograms/metadata/navigation/histograms.xml b/tools/metrics/histograms/metadata/navigation/histograms.xml index 9381c2c..d929708d 100644 --- a/tools/metrics/histograms/metadata/navigation/histograms.xml +++ b/tools/metrics/histograms/metadata/navigation/histograms.xml
@@ -2001,6 +2001,34 @@ </summary> </histogram> +<histogram name="Navigation.Renderer.ProcessReadiness" + enum="RendererProcessReadiness" expires_after="2026-07-23"> + <owner>alexmos@chromium.org</owner> + <owner>creis@chromium.org</owner> + <summary> + Identifies how ready the renderer process is for processing a + CommitNavigation IPC by recording whether the process was ready for + processing IPC when CommitNavigation was sent, and if so, whether all + prerequisite view/proxy/frame objects were already created. Recorded in the + renderer at the end of committing a navigation. + </summary> +</histogram> + +<histogram name="Navigation.Renderer.ProcessReadiness.MainFrameOnly" + enum="RendererProcessReadiness" expires_after="2026-07-23"> + <owner>alexmos@chromium.org</owner> + <owner>creis@chromium.org</owner> + <summary> + Identifies how ready the renderer process is for processing a main frame + CommitNavigation IPC by recording whether the process was ready for + processing IPC when CommitNavigation was sent, and if so, whether all + prerequisite view/proxy/frame objects were already created. Recorded in the + renderer at the end of committing navigation. Similar to + Navigation.Renderer.ProcessReadiness, but recorded for main frames only. + Note that this is not limited to outermost or primary main frames. + </summary> +</histogram> + <histogram name="Navigation.Renderer.Timeline.{DurationFromTo}.Duration" units="ms" expires_after="2026-06-20"> <owner>alexmos@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/sb_client/histograms.xml b/tools/metrics/histograms/metadata/sb_client/histograms.xml index 6c8bb9f..2c7abaae 100644 --- a/tools/metrics/histograms/metadata/sb_client/histograms.xml +++ b/tools/metrics/histograms/metadata/sb_client/histograms.xml
@@ -24,6 +24,7 @@ <variants name="ClassifierRequestType"> <variant name="" summary="all request types"/> + <variant name=".ClipboardCopyApi" summary="Clipboard copy API"/> <variant name=".ForceRequest" summary="Force request"/> <variant name=".FullscreenApi" summary="Fullscreen API"/> <variant name=".KeyboardLockRequested" summary="Keyboard lock requested"/> @@ -43,6 +44,7 @@ <variants name="ClientSideDetectionRequestType"> <variant name="" summary="all request types"/> + <variant name=".ClipboardCopyApi" summary="Clipboard copy API"/> <variant name=".ForceRequest" summary="Force request"/> <variant name=".FullscreenApi" summary="Fullscreen API"/> <variant name=".KeyboardLockRequested" summary="Keyboard lock requested"/> @@ -721,6 +723,16 @@ <token key="RequestType" variants="ClientSideDetectionRequestType"/> </histogram> +<histogram name="SBClientPhishing.MatchCSDAllowlistOnClipboardCopyApi" + enum="BooleanMatched" expires_after="2026-07-24"> + <owner>liu@chromium.org</owner> + <owner>chrome-counter-abuse-alerts@google.com</owner> + <summary> + Records the CSD allowlist match result when a clipboard copy API is + triggered on the page. + </summary> +</histogram> + <histogram name="SBClientPhishing.MatchCSDAllowlistOnFullscreenApi" enum="BooleanMatched" expires_after="2026-01-29"> <owner>andysjlim@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/signin/histograms.xml b/tools/metrics/histograms/metadata/signin/histograms.xml index 7bc0ec7d..2bae28c2 100644 --- a/tools/metrics/histograms/metadata/signin/histograms.xml +++ b/tools/metrics/histograms/metadata/signin/histograms.xml
@@ -3299,6 +3299,18 @@ </summary> </histogram> +<histogram name="Signin.SyncOptIn.IdentityPill.SyncAtShowCount" + units="ShowCount" expires_after="2026-01-12"> + <owner>ernn@google.com</owner> + <owner>rsult@google.com</owner> + <owner>chrome-signin-team-desktop@google.com</owner> + <summary> + Records the count at which the Sync promo on the Identity Pill was shown + when the user has successfuly intereacted with it (E.g. successfully turned + sync). Recorded when that interaction was succesfully made. + </summary> +</histogram> + <histogram name="Signin.SyncOptIn.Offered" enum="SigninAccessPoint" expires_after="2026-01-12"> <owner>ernn@google.com</owner>
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json index 9d671b6d..0724efc2 100644 --- a/tools/perf/core/perfetto_binary_roller/binary_deps.json +++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -5,8 +5,8 @@ "full_remote_path": "perfetto-luci-artifacts/361808c959b026d558c9948602ea38bf25d8981e/linux-arm64/trace_processor_shell" }, "win": { - "hash": "adf7ea443061f0ae742c1fd46f148372af0e8457", - "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/3c2d2b9c28cf2de964bfd18a4944d4527e21e4c4/trace_processor_shell.exe" + "hash": "4dc903aa8fb3f223d0bdf98cafcf0149b189bcbb", + "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/6f026925036beba8c0911aef979234d68413524c/trace_processor_shell.exe" }, "linux_arm": { "hash": "ab1a0d9236a63649044414663b6ace711253648f", @@ -21,8 +21,8 @@ "full_remote_path": "perfetto-luci-artifacts/v51.2/mac-arm64/trace_processor_shell" }, "linux": { - "hash": "7498759c2a13312a77908ee844f471bf79f8893d", - "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/3c2d2b9c28cf2de964bfd18a4944d4527e21e4c4/trace_processor_shell" + "hash": "92ff72aaea465f79d9be3182243a50e872d1e3d9", + "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/3c5467716aacaae68cb7c752779b82e8d92b3e42/trace_processor_shell" } }, "power_profile.sql": {
diff --git a/tools/typescript/definitions/autofill_private.d.ts b/tools/typescript/definitions/autofill_private.d.ts index cc782f46..22f207db 100644 --- a/tools/typescript/definitions/autofill_private.d.ts +++ b/tools/typescript/definitions/autofill_private.d.ts
@@ -145,6 +145,8 @@ NATIONAL_ID_CARD_EXPIRATION_DATE, NATIONAL_ID_CARD_ISSUE_DATE, NATIONAL_ID_CARD_ISSUING_COUNTRY, + KNOWN_TRAVELER_NUMBER, + KNOWN_TRAVELER_NUMBER_EXPIRATION_DATE, ADDRESS_HOME_ZIP_PREFIX, ADDRESS_HOME_ZIP_SUFFIX, }
diff --git a/ui/android/java/src/org/chromium/ui/listmenu/OWNERS b/ui/android/java/src/org/chromium/ui/listmenu/OWNERS index b887596..713bf98 100644 --- a/ui/android/java/src/org/chromium/ui/listmenu/OWNERS +++ b/ui/android/java/src/org/chromium/ui/listmenu/OWNERS
@@ -1,2 +1,2 @@ lazzzis@google.com -twellington@chromium.org +jhimawan@google.com
diff --git a/ui/android/javatests/src/org/chromium/ui/listmenu/OWNERS b/ui/android/javatests/src/org/chromium/ui/listmenu/OWNERS new file mode 100644 index 0000000..96ebe9c --- /dev/null +++ b/ui/android/javatests/src/org/chromium/ui/listmenu/OWNERS
@@ -0,0 +1 @@ +file://ui/android/java/src/org/chromium/ui/listmenu/OWNERS
diff --git a/ui/base/resource/data_pack.h b/ui/base/resource/data_pack.h index 9fb984b..38e7cb15f 100644 --- a/ui/base/resource/data_pack.h +++ b/ui/base/resource/data_pack.h
@@ -18,6 +18,7 @@ #include <string_view> #include <vector> +#include "base/compiler_specific.h" #include "base/component_export.h" #include "base/files/file.h" #include "base/files/memory_mapped_file.h" @@ -96,7 +97,7 @@ const ResourceData& operator*() { return *resource_data_; } Iterator& operator++() { - ++entry_; + UNSAFE_TODO(++entry_); // Should be UNSAFE_BUFFER_USAGE, too. UpdateResourceData(); return *this; } @@ -210,10 +211,10 @@ // Return Entry or Alias by index of resource or alias table. const Entry* GetEntryByResourceTableIndex(size_t index) const { - return &resource_table_[index]; + return UNSAFE_TODO(&resource_table_[index]); } const Alias* GetAliasByAliasTableIndex(size_t index) const { - return &alias_table_[index]; + return UNSAFE_TODO(&alias_table_[index]); } // Return the size of the alias table. size_t GetAliasTableSize() const { return alias_count_; }
diff --git a/ui/gfx/mojom/BUILD.gn b/ui/gfx/mojom/BUILD.gn index 1e223e3..501a47f 100644 --- a/ui/gfx/mojom/BUILD.gn +++ b/ui/gfx/mojom/BUILD.gn
@@ -155,6 +155,10 @@ mojom = "gfx.mojom.SelectionBound" cpp = "::gfx::SelectionBound" }, + { + mojom = "gfx.mojom.SelectionBoundType" + cpp = "::gfx::SelectionBound::Type" + }, ] traits_headers = [ "selection_bound_mojom_traits.h" ] traits_public_deps = [ "//ui/gfx/geometry/mojom:mojom_traits" ]
diff --git a/ui/gfx/mojom/selection_bound_mojom_traits.h b/ui/gfx/mojom/selection_bound_mojom_traits.h index 30fa2ba5..80fde79 100644 --- a/ui/gfx/mojom/selection_bound_mojom_traits.h +++ b/ui/gfx/mojom/selection_bound_mojom_traits.h
@@ -12,48 +12,52 @@ namespace mojo { -namespace { - -gfx::mojom::SelectionBoundType GfxSelectionBoundTypeToMojo( - gfx::SelectionBound::Type type) { - switch (type) { - case gfx::SelectionBound::LEFT: - return gfx::mojom::SelectionBoundType::LEFT; - case gfx::SelectionBound::RIGHT: - return gfx::mojom::SelectionBoundType::RIGHT; - case gfx::SelectionBound::CENTER: - return gfx::mojom::SelectionBoundType::CENTER; - case gfx::SelectionBound::HIDDEN: - return gfx::mojom::SelectionBoundType::HIDDEN; - case gfx::SelectionBound::EMPTY: - return gfx::mojom::SelectionBoundType::EMPTY; +template <> +struct EnumTraits<gfx::mojom::SelectionBoundType, gfx::SelectionBound::Type> { + static gfx::mojom::SelectionBoundType ToMojom( + gfx::SelectionBound::Type input) { + switch (input) { + case gfx::SelectionBound::LEFT: + return gfx::mojom::SelectionBoundType::LEFT; + case gfx::SelectionBound::RIGHT: + return gfx::mojom::SelectionBoundType::RIGHT; + case gfx::SelectionBound::CENTER: + return gfx::mojom::SelectionBoundType::CENTER; + case gfx::SelectionBound::HIDDEN: + return gfx::mojom::SelectionBoundType::HIDDEN; + case gfx::SelectionBound::EMPTY: + return gfx::mojom::SelectionBoundType::EMPTY; + } + NOTREACHED(); } - NOTREACHED(); -} -gfx::SelectionBound::Type MojoSelectionBoundTypeToGfx( - gfx::mojom::SelectionBoundType type) { - switch (type) { - case gfx::mojom::SelectionBoundType::LEFT: - return gfx::SelectionBound::LEFT; - case gfx::mojom::SelectionBoundType::RIGHT: - return gfx::SelectionBound::RIGHT; - case gfx::mojom::SelectionBoundType::CENTER: - return gfx::SelectionBound::CENTER; - case gfx::mojom::SelectionBoundType::HIDDEN: - return gfx::SelectionBound::HIDDEN; - case gfx::mojom::SelectionBoundType::EMPTY: - return gfx::SelectionBound::EMPTY; + static bool FromMojom(gfx::mojom::SelectionBoundType input, + gfx::SelectionBound::Type* out) { + switch (input) { + case gfx::mojom::SelectionBoundType::LEFT: + *out = gfx::SelectionBound::LEFT; + return true; + case gfx::mojom::SelectionBoundType::RIGHT: + *out = gfx::SelectionBound::RIGHT; + return true; + case gfx::mojom::SelectionBoundType::CENTER: + *out = gfx::SelectionBound::CENTER; + return true; + case gfx::mojom::SelectionBoundType::HIDDEN: + *out = gfx::SelectionBound::HIDDEN; + return true; + case gfx::mojom::SelectionBoundType::EMPTY: + *out = gfx::SelectionBound::EMPTY; + return true; + } + NOTREACHED(); } - NOTREACHED(); -} - -} +}; template <> struct StructTraits<gfx::mojom::SelectionBoundDataView, gfx::SelectionBound> { - static gfx::mojom::SelectionBoundType type(const gfx::SelectionBound& input) { - return GfxSelectionBoundTypeToMojo(input.type()); + static gfx::SelectionBound::Type type(const gfx::SelectionBound& input) { + return input.type(); } static gfx::PointF edge_start(const gfx::SelectionBound& input) { @@ -78,17 +82,20 @@ static bool Read(gfx::mojom::SelectionBoundDataView data, gfx::SelectionBound* out) { + gfx::SelectionBound::Type type; gfx::PointF edge_start; gfx::PointF edge_end; gfx::PointF visible_edge_start; gfx::PointF visible_edge_end; - if (!data.ReadEdgeStart(&edge_start) || !data.ReadEdgeEnd(&edge_end) || + if (!data.ReadType(&type) || !data.ReadEdgeStart(&edge_start) || + !data.ReadEdgeEnd(&edge_end) || !data.ReadVisibleEdgeStart(&visible_edge_start) || - !data.ReadVisibleEdgeEnd(&visible_edge_end)) + !data.ReadVisibleEdgeEnd(&visible_edge_end)) { return false; + } out->SetEdge(edge_start, edge_end); out->SetVisibleEdge(visible_edge_start, visible_edge_end); - out->set_type(MojoSelectionBoundTypeToGfx(data.type())); + out->set_type(type); out->set_visible(data.visible()); return true; }
diff --git a/ui/gfx/render_text_unittest.cc b/ui/gfx/render_text_unittest.cc index b26414fd..127ad25d 100644 --- a/ui/gfx/render_text_unittest.cc +++ b/ui/gfx/render_text_unittest.cc
@@ -419,7 +419,7 @@ << stride_; for (int y = top; y < top + height; ++y) { for (int x = left; x < left + width; ++x) { - SkColor buffer_color = buffer_[x + y * stride_]; + SkColor buffer_color = UNSAFE_TODO(buffer_[x + y * stride_]); EXPECT_EQ(color, buffer_color) << string_ << " at " << x << ", " << y; } } @@ -431,7 +431,7 @@ int top, int width, int height) const { - SkColor buffer_color = buffer_[left + top * stride_]; + SkColor buffer_color = UNSAFE_TODO(buffer_[left + top * stride_]); EnsureSolidRect(buffer_color, left, top, width, height); }
diff --git a/ui/gfx/x/xproto_types.cc b/ui/gfx/x/xproto_types.cc index a7495e0..6eb8061 100644 --- a/ui/gfx/x/xproto_types.cc +++ b/ui/gfx/x/xproto_types.cc
@@ -88,7 +88,7 @@ } int ReadBuffer::TakeFd() { - return *fds++; + return UNSAFE_TODO(*fds++); } WriteBuffer::WriteBuffer() = default;
diff --git a/ui/ozone/platform/flatland/client_native_pixmap_factory_flatland.cc b/ui/ozone/platform/flatland/client_native_pixmap_factory_flatland.cc index 23e7e23..a00c362 100644 --- a/ui/ozone/platform/flatland/client_native_pixmap_factory_flatland.cc +++ b/ui/ozone/platform/flatland/client_native_pixmap_factory_flatland.cc
@@ -2,9 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/memory/raw_ptr.h" - - #include "ui/ozone/platform/flatland/client_native_pixmap_factory_flatland.h" #include <lib/zx/vmar.h> @@ -14,9 +11,11 @@ #include <vector> #include "base/check_op.h" +#include "base/compiler_specific.h" #include "base/fuchsia/fuchsia_logging.h" #include "base/fuchsia/koid.h" #include "base/memory/ptr_util.h" +#include "base/memory/raw_ptr.h" #include "base/numerics/safe_conversions.h" #include "base/sequence_checker.h" #include "base/system/sys_info.h" @@ -115,7 +114,7 @@ void* GetMemoryAddress(size_t plane) const override { DCHECK_LT(plane, handle_.planes.size()); DCHECK(mapping_); - return mapping_ + handle_.planes[plane].offset; + return UNSAFE_TODO(mapping_ + handle_.planes[plane].offset); } int GetStride(size_t plane) const override {
diff --git a/ui/resources/ui_lottie_resources.grd b/ui/resources/ui_lottie_resources.grd index 178cf0c..d4952f6 100644 --- a/ui/resources/ui_lottie_resources.grd +++ b/ui/resources/ui_lottie_resources.grd
@@ -10,6 +10,7 @@ <structures> <if expr="use_aura"> <structure type="lottie" name="IDR_AURA_CURSOR_PTR_LOTTIE" file="vector/common/pointers/left_ptr.json" compress="gzip" /> + <structure type="lottie" name="IDR_AURA_CURSOR_ALIAS_LOTTIE" file="vector/common/pointers/alias.json" compress="gzip" /> <structure type="lottie" name="IDR_AURA_CURSOR_IBEAM_LOTTIE" file="vector/common/pointers/xterm.json" compress="gzip" /> <structure type="lottie" name="IDR_AURA_CURSOR_THROBBER_LOTTIE" file="vector/common/pointers/throbber.json" compress="gzip" /> </if>
diff --git a/ui/resources/vector/common/pointers/alias.json b/ui/resources/vector/common/pointers/alias.json new file mode 100644 index 0000000..2a70d60 --- /dev/null +++ b/ui/resources/vector/common/pointers/alias.json
@@ -0,0 +1 @@ +{"v":"5.9.3","fr":60,"ip":0,"op":120,"w":25,"h":25,"nm":"22","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"22 Outlines","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[12.5,12.5,0],"ix":2,"l":2},"a":{"a":0,"k":[12.5,12.5,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":25,"nm":"Drop Shadow","np":8,"mn":"ADBE Drop Shadow","ix":1,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":51,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":171,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":1,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]},{"ty":25,"nm":"Drop Shadow 2","np":8,"mn":"ADBE Drop Shadow","ix":2,"en":1,"ef":[{"ty":2,"nm":"Shadow Color","mn":"ADBE Drop Shadow-0001","ix":1,"v":{"a":0,"k":[0,0,0,1],"ix":1}},{"ty":0,"nm":"Opacity","mn":"ADBE Drop Shadow-0002","ix":2,"v":{"a":0,"k":51,"ix":2}},{"ty":0,"nm":"Direction","mn":"ADBE Drop Shadow-0003","ix":3,"v":{"a":0,"k":171,"ix":3}},{"ty":0,"nm":"Distance","mn":"ADBE Drop Shadow-0004","ix":4,"v":{"a":0,"k":0,"ix":4}},{"ty":0,"nm":"Softness","mn":"ADBE Drop Shadow-0005","ix":5,"v":{"a":0,"k":1,"ix":5}},{"ty":7,"nm":"Shadow Only","mn":"ADBE Drop Shadow-0006","ix":6,"v":{"a":0,"k":0,"ix":6}}]}],"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.781,0.775],[0,0],[0,0],[0.009,-0.016],[0.941,0],[0.573,1.17],[0,0],[0,0],[-1.81,0],[-0.574,-1.19],[0,0],[-0.798,-0.493],[0,0],[2.09,0],[-1.448,-2.955],[0,0],[-2.066,0]],"o":[[0,0],[0,0],[-0.008,-0.016],[-0.404,0.726],[-1.302,0],[0,0],[0,0],[-0.791,-1.626],[1.321,0],[0,0],[0.956,0.108],[0,0],[-0.908,-1.883],[-3.291,0],[0,0],[0.909,1.856],[1.242,0]],"v":[[5.551,5.387],[4.605,3.413],[4.604,3.413],[4.564,3.411],[2.449,4.656],[-0.615,2.743],[-0.618,2.738],[-2.549,-1.135],[-0.346,-4.656],[2.755,-2.708],[3.13,-1.928],[5.79,-1.007],[4.557,-3.577],[-0.346,-6.656],[-4.342,-0.251],[-2.411,3.624],[2.449,6.656]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[9.917,9.75],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[1.414,2.932],[0,0],[2.09,0],[0.786,-0.776],[0,0],[-0.947,0],[-0.574,-1.191],[0,0],[0,0],[0,0],[1.781,0],[0.573,1.17],[0,0],[0,0],[0.81,0.513],[0,0],[-2.066,0]],"o":[[0,0],[-0.908,-1.883],[-1.248,0],[0,0],[0.411,-0.728],[1.321,0],[0,0],[0,0],[0,0],[0.773,1.605],[-1.302,0],[0,0],[0,0],[-0.977,-0.111],[0,0],[0.909,1.855],[3.256,0]],"v":[[4.374,0.321],[2.502,-3.576],[-2.401,-6.656],[-5.519,-5.385],[-4.534,-3.41],[-2.401,-4.656],[0.7,-2.709],[0.701,-2.708],[2.571,1.187],[2.572,1.19],[0.395,4.656],[-2.669,2.744],[-2.672,2.738],[-3.076,1.927],[-5.788,0.971],[-4.465,3.624],[0.395,6.656]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[14.721,15.437],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.065,0.127],[0.549,0],[0.142,-0.624],[-0.538,0],[-0.237,0.444]],"o":[[-0.419,-0.322],[-0.72,0],[0.413,0.315],[0.56,0],[0.06,-0.112]],"v":[[1.492,-0.306],[0.003,-0.812],[-1.412,0.319],[0.05,0.813],[1.302,0.06]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[1.222,-0.089],[0,0],[0,0],[-0.921,0],[0.454,0.941],[0,0],[0,0]],"o":[[0,0],[0,0],[0.405,0.827],[1.044,0],[0,0],[0,0],[-0.842,0.735]],"v":[[0.459,4.798],[0.631,5.143],[0.634,5.149],[2.8,6.5],[4.076,4.468],[4.074,4.464],[3.605,3.487]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[-1.244,0.086],[0,0],[0,0],[0.938,0],[-0.464,-0.961],[0,0]],"o":[[0,0],[0,0],[-0.408,-0.845],[-1.069,0],[0,0],[0.849,-0.745]],"v":[[-0.392,-4.798],[-0.545,-5.117],[-0.546,-5.118],[-2.747,-6.5],[-4.052,-4.421],[-3.579,-3.474]],"c":true},"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ind":3,"ty":"sh","ix":4,"ks":{"a":0,"k":{"i":[[0.092,0.363],[0.124,0.254],[0,0],[-4.03,0],[-1.075,-2.229],[0,0],[0,0],[-0.067,-0.139],[0,0],[0,0],[3.992,0],[1.077,2.197],[0,0]],"o":[[-0.154,-0.232],[0,0],[-1.772,-3.62],[2.474,0],[0,0],[0,0],[0.076,0.133],[0,0],[0,0],[1.734,3.597],[-2.447,0],[0,0],[-0.174,-0.356]],"v":[[-5.288,1.953],[-5.707,1.224],[-7.641,-2.655],[-2.747,-10.5],[3.058,-6.854],[3.058,-6.853],[5.592,-1.575],[5.807,-1.167],[5.808,-1.165],[7.679,2.731],[2.8,10.5],[-2.957,6.911],[-4.891,3.033]],"c":true},"ix":2},"nm":"Path 4","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.999998863071,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[12.317,12.593],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":6,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":120,"st":0,"ct":1,"bm":0}],"markers":[]} \ No newline at end of file
diff --git a/ui/wm/core/cursor_util.cc b/ui/wm/core/cursor_util.cc index a632f595..66d6fbe 100644 --- a/ui/wm/core/cursor_util.cc +++ b/ui/wm/core/cursor_util.cc
@@ -335,7 +335,7 @@ {26, 23}}}, {{CursorType::kCell, IDR_AURA_CURSOR_CELL, {11, 11}, {24, 23}}}, {{CursorType::kContextMenu, IDR_AURA_CURSOR_CONTEXT_MENU, {4, 4}, {8, 9}}}, - {{CursorType::kAlias, IDR_AURA_CURSOR_ALIAS, {8, 6}, {15, 11}}}, + {{CursorType::kAlias, IDR_AURA_CURSOR_ALIAS_LOTTIE, {6, 4}, {6, 4}}}, {{CursorType::kProgress, IDR_AURA_CURSOR_THROBBER_LOTTIE, {12, 12}, @@ -466,7 +466,7 @@ IDR_AURA_CURSOR_BIG_CONTEXT_MENU, {11, 11}, {22, 22}}}, - {{CursorType::kAlias, IDR_AURA_CURSOR_BIG_ALIAS, {19, 11}, {38, 22}}}, + {{CursorType::kAlias, IDR_AURA_CURSOR_ALIAS_LOTTIE, {6, 4}, {6, 4}}}, {{CursorType::kProgress, IDR_AURA_CURSOR_THROBBER_LOTTIE, {12, 12},
diff --git a/v8 b/v8 index e5edfbb..5962611 160000 --- a/v8 +++ b/v8
@@ -1 +1 @@ -Subproject commit e5edfbb3446fcbfb1b7f0847b0ee8154ecde9c60 +Subproject commit 596261193b6c808f82e6bcd0150f39129793f1f3