diff --git a/DEPS b/DEPS index cc22b80..d3713a70 100644 --- a/DEPS +++ b/DEPS
@@ -312,7 +312,7 @@ # 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': '9333e227108653e751321a2420f88ae6dc2156ae', + 'skia_revision': '28172a4e03af8430d1bb6497116baa4b260ad4eb', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling V8 # and whatever else without interference from each other. @@ -320,7 +320,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling ANGLE # and whatever else without interference from each other. - 'angle_revision': 'a326d25100d1d09038de1d63d1d0676a59b24ffe', + 'angle_revision': '8807d22fb5c5cb62eb4c8233abfb0d5c6af1e999', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling SwiftShader # and whatever else without interference from each other. @@ -328,7 +328,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling PDFium # and whatever else without interference from each other. - 'pdfium_revision': '65f4269f6accf48306adb7fff10c07d72d56b1ea', + 'pdfium_revision': 'fcfdf1c3e22ed9d0f2341d596297393cc7f5ad53', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling BoringSSL # and whatever else without interference from each other. @@ -376,7 +376,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling catapult # and whatever else without interference from each other. - 'catapult_revision': '87c9af3b7c6495ab0a0ba86a3eb6a58ff84eca85', + 'catapult_revision': '0f9739da7f76bbb5bc9c108986880d5cfb9b9cf6', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling CrossBench # and whatever else without interference from each other. @@ -392,7 +392,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling fuzztest # and whatever else without interference from each other. - 'fuzztest_revision': '362a279f0ad018574278e72c4e98e2b99d3991bb', + 'fuzztest_revision': 'f67a6fe9ca3afc4d71997e75e8d87c571f0c30cb', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling domato # and whatever else without interference from each other. @@ -400,7 +400,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': 'c804bd56a0e5089b60ad36855f181564fa114789', + 'devtools_frontend_revision': 'e27f62c91b96b3f59a6322ee587f80bfa2d9ed96', # 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. @@ -424,7 +424,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. - 'dawn_revision': 'a1b78e4d9d2b8ac509af227aff8e3004d2ace7f1', + 'dawn_revision': '0e9fde4e362e8e402c7ca3cc3d241775eb80bdb8', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling feed # and whatever else without interference from each other. @@ -1209,7 +1209,7 @@ 'packages': [ { 'package': 'chromium/chrome/android/orderfiles/arm64', - 'version': 'n9uxQVPbVcuNo7WKh6cF8GTEwiNOlYW3zLSw8rgKd_QC', + 'version': 'QGPr-BhnUCDY5mxLT01_JynDLbtCIlHQnwYzf-BRbVkC', }, ], 'condition': 'checkout_android and non_git_source', @@ -1220,7 +1220,7 @@ 'packages': [ { 'package': 'chromium/android_webview/tools/orderfiles/arm', - 'version': '2KZam7lqGCqeftaNyhjwDqNjREkejFscftF1ea37wU8C', + 'version': 'p7wwUEovnHoqzaX0O8qupMY55URGa3eG-FptDRcL1EcC', }, ], 'condition': 'checkout_android and non_git_source', @@ -1231,7 +1231,7 @@ 'packages': [ { 'package': 'chromium/android_webview/tools/orderfiles/arm64', - 'version': 'nqPFthO4wBvep4dy8M9cXT9h8T-W3Gopeu9XjNrwbDUC', + 'version': 'GKpAn0wNEFST1SoKqXhbR-c_gBJgaNDs6_BKbqFixh4C', }, ], 'condition': 'checkout_android and non_git_source', @@ -1616,7 +1616,7 @@ 'packages': [ { 'package': 'chromium/chrome/test/data/variations/cipd', - 'version': 'PyUZ0c5MMik-Oj9I33AyoTvyVFi-foaJNWZTSzvAxm8C', + 'version': 'zfX9ru6NDRRxsBzgWZgQLzi8j93t4iEuvOnKcUMuGwgC', }, ], 'condition': 'non_git_source', @@ -1728,7 +1728,7 @@ 'packages': [ { 'package': 'chromium/third_party/androidx', - 'version': 'yjDOkdvC492M3KA9EiBGFBmpTkSe0XvVXtHqQqB1gZYC', + 'version': '_wBJ2hBwWyOSj4dkL0Ttoxv3fb4wX0ZrTBMEDITnWXwC', }, ], 'condition': 'checkout_android and non_git_source', @@ -2076,7 +2076,7 @@ Var('chromium_git') + '/external/github.com/jk-jeon/dragonbox.git' + '@' + 'beeeef91cf6fef89a4d4ba5e95d47ca64ccb3a44', 'src/third_party/eigen3/src': - Var('chromium_git') + '/external/gitlab.com/libeigen/eigen.git' + '@' + 'f64d1e0acc2c2c33e325b8dd7b2b4673de2b9f14', + Var('chromium_git') + '/external/gitlab.com/libeigen/eigen.git' + '@' + '25dba492e30e19dce3d4bd4cd38af2f5c88c387c', 'src/third_party/emoji-metadata/src': { 'url': Var('chromium_git') + '/external/github.com/googlefonts/emoji-metadata' + '@' + '045f146fca682a836e01cd265171312bfb300e06', @@ -2591,7 +2591,7 @@ Var('chromium_git') + '/external/github.com/cisco/openh264' + '@' + '652bdb7719f30b52b08e506645a7322ff1b2cc6f', 'src/third_party/openscreen/src': - Var('chromium_git') + '/openscreen' + '@' + 'd55bd311eb9a970a8e4fb25cbe5ae016d4d3d812', + Var('chromium_git') + '/openscreen' + '@' + '89fa62bbbe45c5cc87c6a5e84ff3f2d03a3656c8', 'src/third_party/openxr/src': { 'url': Var('chromium_git') + '/external/github.com/KhronosGroup/OpenXR-SDK' + '@' + '75c53b6e853dc12c7b3c771edc9c9c841b15faaa', @@ -2617,7 +2617,7 @@ Var('pdfium_git') + '/pdfium.git' + '@' + Var('pdfium_revision'), 'src/third_party/perfetto': - Var('chromium_git') + '/external/github.com/google/perfetto.git' + '@' + 'dcb5e98f9f6e9ba61a95b0a3fef56671b0b6ba83', + Var('chromium_git') + '/external/github.com/google/perfetto.git' + '@' + 'fa49f16ad958de750da23698f12aebb9f1ae662f', 'src/base/tracing/test/data': { 'bucket': 'perfetto', @@ -2958,16 +2958,16 @@ 'dep_type': 'cipd', }, - 'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@92727ef4b277d71d38659e8679bd9f3c59651401', + 'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@dd5ab0120a15c8373e8cfa6278b23918dbe09dbf', 'src/third_party/glslang/src': '{chromium_git}/external/github.com/KhronosGroup/glslang@e966816ab28ab7cb448d5b33270b43c941b343d4', 'src/third_party/spirv-cross/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Cross@b8fcf307f1f347089e3c46eb4451d27f32ebc8d3', 'src/third_party/spirv-headers/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Headers@f88a2d766840fc825af1fc065977953ba1fa4a91', - 'src/third_party/spirv-tools/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Tools@1c8c845843ca77f70a862fc94d371d36fe703498', + 'src/third_party/spirv-tools/src': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Tools@4972c69eb50255b314fc0925ca757c4417e6b6c0', 'src/third_party/vulkan-headers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Headers@ad9ce1235e88dc09287e19171dfac384db8ec32c', 'src/third_party/vulkan-loader/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Loader@e0e501b0ba42df7b3af023470ad068c48a3ac4de', 'src/third_party/vulkan-tools/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Tools@7f423e2b242c154e6ace85c804c65462a7d41870', 'src/third_party/vulkan-utility-libraries/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Utility-Libraries@738ec97a3f659dd6469bff3c4078ef981b0a343f', - 'src/third_party/vulkan-validation-layers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@cbfe559d89689bd5e471a39560e137043fb0dc36', + 'src/third_party/vulkan-validation-layers/src': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@9076c341487013d4650377e164fd4816779f501d', 'src/third_party/vulkan_memory_allocator': Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + 'cb0597213b0fcb999caa9ed08c2f88dc45eb7d50', @@ -3010,7 +3010,7 @@ Var('chromium_git') + '/webpagereplay.git' + '@' + Var('webpagereplay_revision'), 'src/third_party/webrtc': - Var('webrtc_git') + '/src.git' + '@' + '1b08071e7a4747ae7614aa7898d0d393bbe297a0', + Var('webrtc_git') + '/src.git' + '@' + '6f90f4ff7a7fb27e8e6785bf40c5ca46b6254162', # 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. @@ -3143,7 +3143,7 @@ 'packages': [ { 'package': 'chromeos_internal/apps/boca_receiver_app/app', - 'version': 'ewkWY5vLV2opHtR3R6VR4lNryszwqOS8Ix9eBjhk37wC', + 'version': 'atSa_2QV5PquyB5HeJjF2Rk3RBANWrNylmz0RG2wArkC', }, ], 'condition': 'checkout_chromeos and checkout_src_internal', @@ -3154,7 +3154,7 @@ 'packages': [ { 'package': 'chromeos_internal/apps/boca_app/app', - 'version': 'SXqInJgBvQOHSfho4F_yUGidrbytjcpMIl4uiVkTfMMC', + 'version': '4FLStbpu96N3F6zLCl-tj_9xA0MuSG4FwTz6f-BUCf0C', }, ], 'condition': 'checkout_chromeos and checkout_src_internal', @@ -3236,7 +3236,7 @@ 'packages': [ { 'package': 'chromium/third_party/android_deps/autorolled', - 'version': 'cA8nRMj8uhS3aXj_1a5gxG7mzG-iKRo3ndtBCv5qszEC', + 'version': '9DXI8FnmXkL2tti6GWrRX5mKZzOJzOVKFbiQLuE4ThYC', }, ], 'condition': 'checkout_android and non_git_source', @@ -3721,7 +3721,7 @@ 'src/components/optimization_guide/internal': { 'url': Var('chrome_git') + '/chrome/components/optimization_guide.git' + '@' + - '2e698554fb2f4d1d3e5a8d6cfce58886a6d43654', + 'a7d5c8b5113456c2a50f4af09f30f50acd27c707', 'condition': 'checkout_src_internal', }, @@ -3793,7 +3793,7 @@ 'src/ios_internal': { 'url': Var('chrome_git') + '/chrome/ios_internal.git' + '@' + - 'f0f28e17f585b2fc159c8f571456d38592803f0d', + 'dc307889eca75f759981b901f51303bd1703a003', 'condition': 'checkout_ios and checkout_src_internal', },
diff --git a/base/threading/platform_thread.h b/base/threading/platform_thread.h index 676368f..c9cbddd4 100644 --- a/base/threading/platform_thread.h +++ b/base/threading/platform_thread.h
@@ -12,10 +12,13 @@ #include <stddef.h> #include <array> +#include <cstdint> #include <limits> #include <optional> #include <ostream> +#include <string> #include <type_traits> +#include <vector> #include "base/base_export.h" #include "base/memory/raw_ptr.h"
diff --git a/cc/trees/draw_properties_unittest.cc b/cc/trees/draw_properties_unittest.cc index a3548f1..7bbcf25 100644 --- a/cc/trees/draw_properties_unittest.cc +++ b/cc/trees/draw_properties_unittest.cc
@@ -65,9 +65,10 @@ if (layer_impl->layer_tree_impl() ->property_trees() ->scroll_tree_mutable() - .SetScrollOffsetDeltaForTesting(layer_impl->element_id(), delta)) + .SetScrollOffsetDeltaForTesting(layer_impl->element_id(), delta)) { layer_impl->layer_tree_impl()->DidUpdateScrollOffset( layer_impl->element_id(), /*pushed_from_main_or_pending_tree=*/false); + } } static float MaximumAnimationToScreenScale(LayerImpl* layer_impl) { @@ -101,8 +102,9 @@ // impl-side pending tree, and updates pending tree draw properties. void Commit(float device_scale_factor = 1.0f) { UpdateMainDrawProperties(device_scale_factor); - if (!host_impl()->pending_tree()) + if (!host_impl()->pending_tree()) { host_impl()->CreatePendingTree(); + } host()->CommitToPendingTree(); // TODO(crbug.com/40617417) This call should be handled by // FakeLayerTreeHost instead of manually pushing the properties from the @@ -125,8 +127,9 @@ bool UpdateLayerListContains(int id) const { for (const auto& layer : update_layer_list_) { - if (layer->id() == id) + if (layer->id() == id) { return true; + } } return false; } @@ -2035,14 +2038,18 @@ const gfx::RectF& mapped_rect) { gfx::Transform inverse = map_transform.GetCheckedInverse(); bool clipped = false; - if (!clipped) + if (!clipped) { MathUtil::ProjectPoint(inverse, mapped_rect.top_right(), &clipped); - if (!clipped) + } + if (!clipped) { MathUtil::ProjectPoint(inverse, mapped_rect.origin(), &clipped); - if (!clipped) + } + if (!clipped) { MathUtil::ProjectPoint(inverse, mapped_rect.bottom_right(), &clipped); - if (!clipped) + } + if (!clipped) { MathUtil::ProjectPoint(inverse, mapped_rect.bottom_left(), &clipped); + } return clipped; } @@ -5771,6 +5778,24 @@ gfx::Vector2dF(-20, -20), GetImpl(anchored.get())->ScreenSpaceTransform().To2dTranslation()); } + +TEST_F(DrawPropertiesAnchorPositionScrollTest, Cycle) { + CreateRoot(); + scoped_refptr<Layer> anchored1 = CreateAnchored(root_.get(), {}); + scoped_refptr<Layer> anchored2 = + CreateAnchored(root_.get(), {anchored1->element_id()}); + + // Create a cycle: anchored1 -> anchored2 -> anchored1 + auto& data1 = + GetPropertyTrees(anchored1.get()) + ->transform_tree_mutable() + .EnsureAnchorPositionScrollData(anchored1->transform_tree_index()); + data1.adjustment_container_ids = {anchored2->element_id()}; + + // This should not crash (stack overflow). + UpdateMainDrawProperties(); +} + class AnimationScaleFactorTrackingLayerImpl : public LayerImpl { public: static std::unique_ptr<AnimationScaleFactorTrackingLayerImpl> Create( @@ -8615,10 +8640,9 @@ gfx::LinearGradient gradient_mask; }; -class DrawPropertiesWithLayerTreeTest : - public DrawPropertiesTestWithLayerTree, - public testing::WithParamInterface<MaskFilterTestCase> { -}; +class DrawPropertiesWithLayerTreeTest + : public DrawPropertiesTestWithLayerTree, + public testing::WithParamInterface<MaskFilterTestCase> {}; // In layer tree mode, not using impl-side PropertyTreeBuilder. TEST_P(DrawPropertiesWithLayerTreeTest, MaskFilterOnRenderSurface) { @@ -8632,8 +8656,9 @@ const MaskFilterTestCase test_case = GetParam(); gfx::LinearGradient gradient_mask = test_case.gradient_mask; - if (!gradient_mask.IsEmpty()) + if (!gradient_mask.IsEmpty()) { gradient_mask.AddStep(50, 0x50); + } scoped_refptr<Layer> root = Layer::Create(); host()->SetRootLayer(root); @@ -8689,21 +8714,26 @@ UpdateMainDrawProperties(); CommitAndActivate(); - EXPECT_NE(test_case.rounded_corners.IsEmpty(), + EXPECT_NE( + test_case.rounded_corners.IsEmpty(), GetRenderSurfaceImpl(child_1)->mask_filter_info().HasRoundedCorners()); - EXPECT_NE(test_case.rounded_corners.IsEmpty(), + EXPECT_NE( + test_case.rounded_corners.IsEmpty(), GetRenderSurfaceImpl(child_2)->mask_filter_info().HasRoundedCorners()); - EXPECT_NE(test_case.rounded_corners.IsEmpty(), + EXPECT_NE( + test_case.rounded_corners.IsEmpty(), GetRenderSurfaceImpl(child_3)->mask_filter_info().HasRoundedCorners()); - EXPECT_NE(test_case.gradient_mask.IsEmpty(), + EXPECT_NE( + test_case.gradient_mask.IsEmpty(), GetRenderSurfaceImpl(child_1)->mask_filter_info().HasGradientMask()); - EXPECT_NE(test_case.gradient_mask.IsEmpty(), + EXPECT_NE( + test_case.gradient_mask.IsEmpty(), GetRenderSurfaceImpl(child_2)->mask_filter_info().HasGradientMask()); - EXPECT_NE(test_case.gradient_mask.IsEmpty(), + EXPECT_NE( + test_case.gradient_mask.IsEmpty(), GetRenderSurfaceImpl(child_3)->mask_filter_info().HasGradientMask()); - } - +} INSTANTIATE_TEST_SUITE_P( DrawPropertiesWithLayerTreeTests, @@ -8711,7 +8741,8 @@ testing::ValuesIn<MaskFilterTestCase>({ {"WithRoundedCorners", gfx::RoundedCornersF(10.f), gfx::LinearGradient::GetEmpty()}, - {"WithGradientMask", gfx::RoundedCornersF(0.f), gfx::LinearGradient(45)}, + {"WithGradientMask", gfx::RoundedCornersF(0.f), + gfx::LinearGradient(45)}, {"WithRoundedCornersAndGradientMask", gfx::RoundedCornersF(10.f), gfx::LinearGradient(45)}, }),
diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h index ac28015..6055bc8 100644 --- a/cc/trees/layer_tree_host.h +++ b/cc/trees/layer_tree_host.h
@@ -1142,7 +1142,6 @@ // Layer id to Layer map. std::unordered_map<int, raw_ptr<Layer, CtnExperimental>> layer_id_map_; - // This is for layer tree mode only. std::unordered_map<ElementId, raw_ptr<Layer, CtnExperimental>, ElementIdHash> element_layers_map_;
diff --git a/cc/trees/property_tree.cc b/cc/trees/property_tree.cc index b8a3cc8..da53d6c 100644 --- a/cc/trees/property_tree.cc +++ b/cc/trees/property_tree.cc
@@ -631,7 +631,13 @@ gfx::Vector2dF TransformTree::AnchorPositionOffset( TransformNode* node, int max_updated_node_id, - UpdateTransformsData* update_data) { + UpdateTransformsData* update_data, + base::flat_set<int>& visited) { + if (visited.contains(node->id)) { + return gfx::Vector2dF(); + } + visited.insert(node->id); + const AnchorPositionScrollData* data = GetAnchorPositionScrollData(node->id); if (!data) return gfx::Vector2dF(); @@ -665,7 +671,7 @@ // because AnchorPositionOffset() is the opposite of // blink::AnchorPositionScrollData::AccmulatedOffset(). accumulated_offset -= AnchorPositionOffset( - container_transform, max_updated_node_id, update_data); + container_transform, max_updated_node_id, update_data, visited); } if (container_transform_id > max_updated_node_id) { // The adjustment depends on a later transform node that may contain @@ -834,7 +840,9 @@ transform.Translate(StickyPositionOffset(node)); if (node->anchor_position_scroll_data_id >= 0) { - transform.Translate(AnchorPositionOffset(node, node->id - 1, update_data)); + base::flat_set<int> visited; + transform.Translate( + AnchorPositionOffset(node, node->id - 1, update_data, visited)); // Make sure the damage rect is tracked. node->SetTransformChanged(DamageReason::kUntracked); }
diff --git a/cc/trees/property_tree.h b/cc/trees/property_tree.h index 3c48c276..1ff0494 100644 --- a/cc/trees/property_tree.h +++ b/cc/trees/property_tree.h
@@ -104,23 +104,23 @@ } T* FindNodeFromElementId(ElementId id) { auto iterator = element_id_to_node_index_.find(id); - if (iterator == element_id_to_node_index_.end()) + if (iterator == element_id_to_node_index_.end()) { return nullptr; + } return Node(iterator->second); } const T* FindNodeFromElementId(ElementId id) const { auto iterator = element_id_to_node_index_.find(id); - if (iterator == element_id_to_node_index_.end()) + if (iterator == element_id_to_node_index_.end()) { return nullptr; + } return Node(iterator->second); } void clear(); size_t size() const { return nodes_.size(); } - void set_needs_update(bool needs_update) { - needs_update_ = needs_update; - } + void set_needs_update(bool needs_update) { needs_update_ = needs_update; } bool needs_update() const { return needs_update_; } std::vector<T>& nodes() { return nodes_; } @@ -358,7 +358,8 @@ gfx::Vector2dF StickyPositionOffset(TransformNode* node); gfx::Vector2dF AnchorPositionOffset(TransformNode* node, int max_updated_node_id, - UpdateTransformsData* update_data); + UpdateTransformsData* update_data, + base::flat_set<int>& visited); void UpdateLocalTransform(TransformNode* node, const ViewportPropertyIds* viewport_property_ids, UpdateTransformsData* update_data); @@ -649,8 +650,9 @@ // Returns true if the scroll offset is changed. bool SetScrollOffset(ElementId id, const gfx::PointF& scroll_offset); void SetScrollOffsetClobberActiveValue(ElementId id) { - if (auto* synced_offset = GetSyncedScrollOffset(id)) + if (auto* synced_offset = GetSyncedScrollOffset(id)) { synced_offset->set_clobber_active_value(); + } } bool SetElasticOverscroll(const ScrollNode& scroll_node,
diff --git a/chrome/VERSION b/chrome/VERSION index 257d705..9a278040 100644 --- a/chrome/VERSION +++ b/chrome/VERSION
@@ -1,4 +1,4 @@ MAJOR=147 MINOR=0 -BUILD=7709 +BUILD=7710 PATCH=0
diff --git a/chrome/android/java/res/layout/history_item_view.xml b/chrome/android/java/res/layout/history_item_view.xml index 3df0a64aa..76df6e2 100644 --- a/chrome/android/java/res/layout/history_item_view.xml +++ b/chrome/android/java/res/layout/history_item_view.xml
@@ -7,6 +7,28 @@ <org.chromium.chrome.browser.history.HistoryItemView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" android:background="@drawable/list_item_rounded_background_selector" android:layout_width="match_parent" - android:layout_height="wrap_content" /> \ No newline at end of file + android:layout_height="wrap_content"> + + <FrameLayout + android:id="@+id/spark_container" + android:layout_width="@dimen/history_item_spark_size" + android:layout_height="@dimen/history_item_spark_size" + android:layout_gravity="start|top" + android:layout_marginStart="@dimen/history_item_spark_margin_start" + android:layout_marginTop="@dimen/history_item_spark_margin_top" + android:visibility="gone"> + <View + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@drawable/oval_surface_0" /> + <org.chromium.ui.widget.ChromeImageView + android:layout_width="match_parent" + android:layout_height="match_parent" + android:padding="@dimen/history_item_spark_padding" + android:src="@drawable/ic_spark_24dp" + app:tint="@macro/default_icon_color_accent1" /> + </FrameLayout> +</org.chromium.chrome.browser.history.HistoryItemView>
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml index e22b13f..f6e08e82 100644 --- a/chrome/android/java/res/values/dimens.xml +++ b/chrome/android/java/res/values/dimens.xml
@@ -293,6 +293,16 @@ <!-- 18dp padding is added to each side of the remove button to limit the image width to 20dp (56dp - 2 * 18dp). --> <dimen name="history_item_remove_button_lateral_padding">18dp</dimen> <dimen name="history_item_leading_padding">2dp</dimen> + <dimen name="history_item_spark_size">18dp</dimen> + <!-- + Calculated to align the spark center with the favicon bottom-right corner. + Favicon right edge = list_item_default_margin(16dp) + list_item_start_icon_width(36dp) = 52dp. + Spark margin start = 52dp - (history_item_spark_size(18dp) / 2) = 43dp. + --> + <dimen name="history_item_spark_margin_start">43dp</dimen> + <!-- Calculated using list_item_min_height(64dp) / 2. --> + <dimen name="history_item_spark_margin_top">32dp</dimen> + <dimen name="history_item_spark_padding">4dp</dimen> <!-- Context Menu Dimensions --> <!-- Prevent the chip from reaching the edges of the screen for long translations. -->
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryItemView.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryItemView.java index 1ee6ce0..894bcbb 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryItemView.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryItemView.java
@@ -52,6 +52,7 @@ private boolean mIsItemRemoved; private BooleanSupplier mShowSourceApp; private ChipView mChipView; + private View mSparkContainer; public HistoryItemView(Context context, AttributeSet attrs) { super(context, attrs); @@ -88,6 +89,12 @@ mChipView = findViewById(R.id.chip); mChipView.getPrimaryTextView().setEllipsize(TextUtils.TruncateAt.END); + + mSparkContainer = findViewById(R.id.spark_container); + // Ensure the spark is drawn on top of the favicon and its background. This is needed + // because the content of xml is added first, then the content from code is added next (on + // top of the xml children). + mSparkContainer.bringToFront(); } @Override @@ -127,6 +134,22 @@ AppCompatResources.getColorStateList( getContext(), R.color.default_text_color_list)); } + updateSparkVisibility(); + } + + @Override + protected void updateView(boolean animate) { + super.updateView(animate); + updateSparkVisibility(); + } + + private void updateSparkVisibility() { + HistoryItem item = getItem(); + // The spark should be shown only if the item is not selected (checked), blocked, and is an + // actor visit. + boolean showSpark = + item != null && item.isActorVisit() && !item.wasBlockedVisit() && !isChecked(); + mSparkContainer.setVisibility(showSpark ? View.VISIBLE : View.GONE); } @Initializer @@ -218,7 +241,13 @@ }); } + @VisibleForTesting View getRemoveButtonForTests() { return mRemoveButton; } + + @VisibleForTesting + View getSparkContainerForTests() { + return mSparkContainer; + } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomSheetManager.java b/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomSheetManager.java index 1f72db8..07a2164e 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomSheetManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ui/BottomSheetManager.java
@@ -175,11 +175,11 @@ @Override public void onBottomControlsHeightChanged( int bottomControlsHeight, int bottomControlsMinHeight) { - mSheetController.setBottomControlsHeight(bottomControlsHeight); + mSheetController.setBottomControlsOffset(bottomControlsHeight); } }; mBrowserControlsVisibilityManager.addObserver(mBrowserControlsObserver); - mSheetController.setBottomControlsHeight( + mSheetController.setBottomControlsOffset( controlsVisibilityManager.getBottomControlsHeight()); mOmniboxFocusObserver =
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/history/HistoryUiTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/history/HistoryUiTest.java index 2ea373e1..b9a711a2 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/history/HistoryUiTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/history/HistoryUiTest.java
@@ -261,6 +261,66 @@ @Test @SmallTest + public void testSparkVisibility() { + // Use a timestamp older than the ones in setUp() to ensure they appear after Item 1 and 2. + long timestamp = mItem2.getTimestamp() - 1000; + + // Item with spark (actor visit, not blocked) + HistoryItem actorItem = + StubbedHistoryProvider.createHistoryItem( + 0, timestamp, /* blockedVisit= */ false, /* isActorVisit= */ true); + + // Item without spark (not actor visit) + HistoryItem nonActorItem = + StubbedHistoryProvider.createHistoryItem( + 1, timestamp - 1, /* blockedVisit= */ false, /* isActorVisit= */ false); + + // Item without spark (actor visit, but blocked) + HistoryItem blockedActorItem = + StubbedHistoryProvider.createHistoryItem( + 2, timestamp - 2, /* blockedVisit= */ true, /* isActorVisit= */ true); + + mHistoryProvider.addItem(actorItem); + mHistoryProvider.addItem(nonActorItem); + mHistoryProvider.addItem(blockedActorItem); + + mAdapter.startLoadingItems(); + RobolectricUtil.runAllBackgroundAndUi(); + + // Recalculate height and layout to ensure all items are visible. + mRecyclerView.measure(0, 0); + mHeight = mRecyclerView.getMeasuredHeight(); + layoutRecyclerView(); + + // The items are added after the initial ones in setUp(). + // setUp() adds 2 items. They are at position 2 and 3. + // New items are at 4, 5, 6 because they have older timestamps. + Assert.assertEquals(7, mAdapter.getItemCount()); + + HistoryItemView actorView = (HistoryItemView) getItemView(4); + Assert.assertEquals(actorItem, actorView.getItem()); + Assert.assertEquals(View.VISIBLE, actorView.getSparkContainerForTests().getVisibility()); + + // Select the actor visit item and check that the spark is hidden. + toggleItemSelection(4); + Assert.assertEquals(View.GONE, actorView.getSparkContainerForTests().getVisibility()); + + // Unselect the actor visit item and check that the spark is shown again. + toggleItemSelection(4); + Assert.assertEquals(View.VISIBLE, actorView.getSparkContainerForTests().getVisibility()); + + HistoryItemView nonActorView = (HistoryItemView) getItemView(5); + Assert.assertEquals(nonActorItem, nonActorView.getItem()); + Assert.assertEquals(View.GONE, nonActorView.getSparkContainerForTests().getVisibility()); + + HistoryItemView blockedActorView = (HistoryItemView) getItemView(6); + Assert.assertEquals(blockedActorItem, blockedActorView.getItem()); + Assert.assertEquals( + View.GONE, blockedActorView.getSparkContainerForTests().getVisibility()); + } + + @Test + @SmallTest public void testRemove_AllItems() throws Exception { toggleItemSelection(2); toggleItemSelection(3);
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index fa39c7807..a43c3b5a 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd
@@ -19866,10 +19866,10 @@ <!-- Chrome-specific PDF strings --> <if expr="enable_glic and enable_pdf"> - <message name="IDS_PDF_GLIC_SUMMARIZE" translateable="false" desc="Button label for the button to summarize PDFs."> + <message name="IDS_PDF_GLIC_SUMMARIZE" desc="Button label for the button to summarize PDFs."> Summarize </message> - <message name="IDS_PDF_GLIC_SUMMARIZE_TOOLTIP" translateable="false" desc="Tooltip for the button to summarize PDFs with Gemini."> + <message name="IDS_PDF_GLIC_SUMMARIZE_TOOLTIP" desc="Tooltip for the button to summarize PDFs with Gemini."> Summarize with Gemini </message> </if>
diff --git a/chrome/app/generated_resources_grd/IDS_PDF_GLIC_SUMMARIZE.png.sha1 b/chrome/app/generated_resources_grd/IDS_PDF_GLIC_SUMMARIZE.png.sha1 new file mode 100644 index 0000000..2028fd5 --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_PDF_GLIC_SUMMARIZE.png.sha1
@@ -0,0 +1 @@ +287446831535446f69bc0d6c8023091a7114c422 \ No newline at end of file
diff --git a/chrome/app/generated_resources_grd/IDS_PDF_GLIC_SUMMARIZE_TOOLTIP.png.sha1 b/chrome/app/generated_resources_grd/IDS_PDF_GLIC_SUMMARIZE_TOOLTIP.png.sha1 new file mode 100644 index 0000000..f084de5 --- /dev/null +++ b/chrome/app/generated_resources_grd/IDS_PDF_GLIC_SUMMARIZE_TOOLTIP.png.sha1
@@ -0,0 +1 @@ +a22102e1ff9904b2321497ec1926b751f9fe7a8b \ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn index 9575497..7a5e5447 100644 --- a/chrome/browser/BUILD.gn +++ b/chrome/browser/BUILD.gn
@@ -1933,6 +1933,7 @@ "//components/activity_reporter", "//components/activity_reporter:buildflags", "//components/browser_apis/tab_strip:mojom", + "//components/browser_apis/ui_controllers/toolbar:mojom", "//components/content_settings/browser/ui", "//components/enterprise/connectors/core", "//components/enterprise/connectors/core:cloud_content_scanning",
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS index 2dfa669..492ad5f 100644 --- a/chrome/browser/DEPS +++ b/chrome/browser/DEPS
@@ -70,6 +70,7 @@ "+components/breadcrumbs/core", "+components/browser_apis/browser_controls", "+components/browser_apis/tab_strip", + "+components/browser_apis/ui_controllers", "+components/browser_sync", "+components/browser_ui/accessibility", "+components/browser_ui/desktop_windowing/android",
diff --git a/chrome/browser/ash/system/input_device_settings.cc b/chrome/browser/ash/system/input_device_settings.cc index 7c95a9c7..011d7f8 100644 --- a/chrome/browser/ash/system/input_device_settings.cc +++ b/chrome/browser/ash/system/input_device_settings.cc
@@ -466,7 +466,8 @@ // static bool InputDeviceSettings::ForceKeyboardDrivenUINavigation() { if (policy::EnrollmentRequisitionManager::IsMeetDevice() || - policy::EnrollmentRequisitionManager::IsSharkRequisition()) { + policy::EnrollmentRequisitionManager::IsSharkRequisition() || + policy::EnrollmentRequisitionManager::IsSquidDevice()) { return true; }
diff --git a/chrome/browser/autocomplete/chrome_aim_eligibility_service_browsertest.cc b/chrome/browser/autocomplete/chrome_aim_eligibility_service_browsertest.cc index 06e854d..b43ecac 100644 --- a/chrome/browser/autocomplete/chrome_aim_eligibility_service_browsertest.cc +++ b/chrome/browser/autocomplete/chrome_aim_eligibility_service_browsertest.cc
@@ -1626,8 +1626,14 @@ } #endif // !BUILDFLAG(IS_CHROMEOS) +#if BUILDFLAG(IS_CHROMEOS) +// TODO(crbug.com/488467253): Fix and re-enable this test for CrOS. +#define MAYBE_RefreshesOnPersistentError DISABLED_RefreshesOnPersistentError +#else +#define MAYBE_RefreshesOnPersistentError RefreshesOnPersistentError +#endif IN_PROC_BROWSER_TEST_F(ChromeAimEligibilityServiceOAuthBrowserTest, - RefreshesOnPersistentError) { + MAYBE_RefreshesOnPersistentError) { base::HistogramTester histogram_tester; omnibox::AimEligibilityResponse response;
diff --git a/chrome/browser/chrome_browser_interface_binders_webui_parts_desktop.cc b/chrome/browser/chrome_browser_interface_binders_webui_parts_desktop.cc index 75b255922..6c9d9a8 100644 --- a/chrome/browser/chrome_browser_interface_binders_webui_parts_desktop.cc +++ b/chrome/browser/chrome_browser_interface_binders_webui_parts_desktop.cc
@@ -93,6 +93,7 @@ #include "chrome/common/chrome_features.h" #include "components/autofill/core/browser/ml_model/logging/autofill_ml_internals.mojom.h" #include "components/browser_apis/browser_controls/browser_controls_api.mojom.h" +#include "components/browser_apis/ui_controllers/toolbar/toolbar_ui_api.mojom.h" #include "components/commerce/core/mojom/shopping_service.mojom.h" // nogncheck crbug.com/1125897 #include "components/contextual_tasks/public/features.h" #include "components/data_sharing/public/features.h" @@ -681,6 +682,7 @@ if (features::IsWebUIToolbarEnabled()) { registry.ForWebUI<WebUIToolbarUI>() .Add<browser_controls_api::mojom::BrowserControlsService>() + .Add<toolbar_ui_api::mojom::ToolbarUIService>() .Add<tracked_element::mojom::TrackedElementHandler>(); }
diff --git a/chrome/browser/content_settings/host_content_settings_map_factory.cc b/chrome/browser/content_settings/host_content_settings_map_factory.cc index 9bcba12..8bb9e8f 100644 --- a/chrome/browser/content_settings/host_content_settings_map_factory.cc +++ b/chrome/browser/content_settings/host_content_settings_map_factory.cc
@@ -26,11 +26,11 @@ #include "extensions/buildflags/buildflags.h" #include "ui/webui/webui_allowlist_provider.h" -#if BUILDFLAG(ENABLE_EXTENSIONS) +#if BUILDFLAG(ENABLE_EXTENSIONS_CORE) #include "base/trace_event/trace_event.h" #include "extensions/browser/api/content_settings/content_settings_custom_extension_provider.h" // nogncheck #include "extensions/browser/api/content_settings/content_settings_service.h" // nogncheck -#endif // BUILDFLAG(ENABLE_EXTENSIONS) +#endif // BUILDFLAG(ENABLE_EXTENSIONS_CORE) #if BUILDFLAG(IS_ANDROID) #include "chrome/browser/content_settings/javascript_optimizer_provider_android.h" @@ -70,7 +70,7 @@ DependsOn(TemplateURLServiceFactory::GetInstance()); #endif DependsOn(OneTimePermissionsTrackerFactory::GetInstance()); -#if BUILDFLAG(ENABLE_EXTENSIONS) +#if BUILDFLAG(ENABLE_EXTENSIONS_CORE) DependsOn(extensions::ContentSettingsService::GetFactoryInstance()); #endif #if BUILDFLAG(IS_CHROMEOS) @@ -137,7 +137,7 @@ std::move(component_extension_provider)); #endif // BUILDFLAG(IS_CHROMEOS) -#if BUILDFLAG(ENABLE_EXTENSIONS) +#if BUILDFLAG(ENABLE_EXTENSIONS_CORE) // These must be registered before before the HostSettings are passed over to // the IOThread. Simplest to do this on construction. settings_map->RegisterProvider( @@ -150,7 +150,7 @@ // the case where profile->IsOffTheRecord() is true? And what is the // interaction with profile->IsGuestSession()? false)); -#endif // BUILDFLAG(ENABLE_EXTENSIONS) +#endif // BUILDFLAG(ENABLE_EXTENSIONS_CORE) supervised_user::FamilyLinkSettingsService* family_link_settings_service = supervised_user::FamilyLinkSettingsServiceFactory::GetForKey(
diff --git a/chrome/browser/extensions/BUILD.gn b/chrome/browser/extensions/BUILD.gn index 3e62df2d..c81428c 100644 --- a/chrome/browser/extensions/BUILD.gn +++ b/chrome/browser/extensions/BUILD.gn
@@ -595,6 +595,7 @@ "//extensions/browser", "//extensions/browser:crx_installer", "//extensions/browser:keepalive", + "//extensions/browser/api/content_settings", "//extensions/browser/api/storage:settings_namespace", "//extensions/browser/api/storage:settings_observer", "//extensions/browser/api/system_display:display_info_provider", @@ -1058,7 +1059,6 @@ "//device/fido", "//extensions:extensions_resources", "//extensions/browser/api:extensions_api_client", - "//extensions/browser/api/content_settings", "//extensions/common:mojom", "//extensions/common/api", "//extensions/strings",
diff --git a/chrome/browser/extensions/api/BUILD.gn b/chrome/browser/extensions/api/BUILD.gn index 4331010..02e7063 100644 --- a/chrome/browser/extensions/api/BUILD.gn +++ b/chrome/browser/extensions/api/BUILD.gn
@@ -31,6 +31,7 @@ "//chrome/browser/extensions/api/activity_log_private", "//chrome/browser/extensions/api/browsing_data", "//chrome/browser/extensions/api/commands", + "//chrome/browser/extensions/api/content_settings", "//chrome/browser/extensions/api/context_menus", "//chrome/browser/extensions/api/cookies", "//chrome/browser/extensions/api/debugger", @@ -72,7 +73,6 @@ "//chrome/browser/extensions/api/autofill_private", "//chrome/browser/extensions/api/braille_display_private", "//chrome/browser/extensions/api/command_line_private", - "//chrome/browser/extensions/api/content_settings", "//chrome/browser/extensions/api/experimental_actor", "//chrome/browser/extensions/api/experimental_ai_data", "//chrome/browser/extensions/api/idltest",
diff --git a/chrome/browser/extensions/api/content_settings/BUILD.gn b/chrome/browser/extensions/api/content_settings/BUILD.gn index 9d44256..18994d6d 100644 --- a/chrome/browser/extensions/api/content_settings/BUILD.gn +++ b/chrome/browser/extensions/api/content_settings/BUILD.gn
@@ -4,8 +4,8 @@ import("//extensions/buildflags/buildflags.gni") -assert(enable_extensions, - "Cannot depend on extensions because enable_extensions=false.") +assert(enable_extensions_core, + "Cannot depend on extensions because enable_extensions_core=false.") source_set("content_settings") { sources = [
diff --git a/chrome/browser/extensions/api/content_settings/content_settings_apitest.cc b/chrome/browser/extensions/api/content_settings/content_settings_apitest.cc index 4f084909..a8ad2a3 100644 --- a/chrome/browser/extensions/api/content_settings/content_settings_apitest.cc +++ b/chrome/browser/extensions/api/content_settings/content_settings_apitest.cc
@@ -23,15 +23,12 @@ #include "chrome/browser/profiles/keep_alive/profile_keep_alive_types.h" #include "chrome/browser/profiles/keep_alive/scoped_profile_keep_alive.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/browser.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" #include "components/content_settings/core/browser/content_settings_uma_util.h" #include "components/content_settings/core/browser/cookie_settings.h" #include "components/content_settings/core/browser/host_content_settings_map.h" #include "components/content_settings/core/common/content_settings.h" -#include "components/keep_alive_registry/keep_alive_types.h" -#include "components/keep_alive_registry/scoped_keep_alive.h" #include "components/permissions/permission_manager.h" #include "components/prefs/pref_service.h" #include "content/public/common/content_switches.h" @@ -48,6 +45,11 @@ #include "net/base/schemeful_site.h" #include "net/dns/mock_host_resolver.h" +#if !BUILDFLAG(IS_ANDROID) +#include "components/keep_alive_registry/keep_alive_types.h" +#include "components/keep_alive_registry/scoped_keep_alive.h" +#endif + #if BUILDFLAG(ENABLE_PLUGINS) #include "content/public/browser/plugin_service.h" #endif @@ -73,24 +75,28 @@ // The browser might get closed later (and therefore be destroyed), so we // save the profile. profile_ = profile(); - +#if !BUILDFLAG(IS_ANDROID) // Closing the last browser window also releases a KeepAlive. Make // sure it's not the last one, so the message loop doesn't quit // unexpectedly. keep_alive_ = std::make_unique<ScopedKeepAlive>( KeepAliveOrigin::BROWSER, KeepAliveRestartOption::DISABLED); +#endif + profile_keep_alive_ = std::make_unique<ScopedProfileKeepAlive>( profile_, ProfileKeepAliveOrigin::kBrowserWindow); } void TearDownOnMainThread() override { profile_keep_alive_.reset(); +#if !BUILDFLAG(IS_ANDROID) // BrowserProcess::Shutdown() needs to be called in a message loop, so we // post a task to release the keep alive, then run the message loop. base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( FROM_HERE, base::BindOnce(&std::unique_ptr<ScopedKeepAlive>::reset, base::Unretained(&keep_alive_), nullptr)); content::RunAllPendingInMessageLoop(); +#endif ExtensionApiTest::TearDownOnMainThread(); } @@ -273,7 +279,11 @@ private: raw_ptr<Profile, AcrossTasksDanglingUntriaged> profile_ = nullptr; + +#if !BUILDFLAG(IS_ANDROID) + // KeepAlive is not supported nor required on Android. std::unique_ptr<ScopedKeepAlive> keep_alive_; +#endif std::unique_ptr<ScopedProfileKeepAlive> profile_keep_alive_; }; @@ -313,9 +323,14 @@ const ExtensionContentSettingsApiTestWithContextType&) = delete; }; +// Android only supports MV3 and later, therefore don't need to test for +// persistent background context. +#if !BUILDFLAG(IS_ANDROID) INSTANTIATE_TEST_SUITE_P(PersistentBackground, ExtensionContentSettingsApiTestWithContextType, ::testing::Values(ContextType::kPersistentBackground)); +#endif + INSTANTIATE_TEST_SUITE_P(ServiceWorker, ExtensionContentSettingsApiTestWithContextType, ::testing::Values(ContextType::kServiceWorker));
diff --git a/chrome/browser/extensions/api/tabs/tabs_apitest.cc b/chrome/browser/extensions/api/tabs/tabs_apitest.cc index 10c5882f..28009eb 100644 --- a/chrome/browser/extensions/api/tabs/tabs_apitest.cc +++ b/chrome/browser/extensions/api/tabs/tabs_apitest.cc
@@ -283,6 +283,68 @@ ASSERT_TRUE(RunExtensionTest("tabs/reload")) << message_; } +// Tests various behaviors of highlighting tabs using chrome.tabs.update(), +// including that highlighting is additive, tabs can be unhighlighted, and +// that extensions cannot unhighlight all tabs in a window. +IN_PROC_BROWSER_TEST_F(ExtensionApiTabTest, UpdateHighlighted) { + constexpr char kManifest[] = R"({ + "name": "Update Highlighted", + "version": "1.0", + "manifest_version": 3, + "background": {"service_worker": "background.js"} + })"; + + constexpr char kBackgroundJs[] = R"( + chrome.test.runTests([ + async function highlightingTabs() { + // Open multiple tabs. + const win = await chrome.windows.create( + {url: ['about:blank', 'about:blank', 'about:blank']}); + const tabs = await chrome.tabs.query({windowId: win.id}); + chrome.test.assertEq(3, tabs.length); + // Set the initial state. Only highlight the first tab. + // This is necessary because on desktop android, multiple tabs are + // highlighted in the newly-created window. + await chrome.tabs.update(tabs[0].id, {highlighted: true}); + await chrome.tabs.update(tabs[1].id, {highlighted: false}); + await chrome.tabs.update(tabs[2].id, {highlighted: false}); + + let highlightedTabs = + await chrome.tabs.query({windowId: win.id, highlighted: true}); + chrome.test.assertEq(1, highlightedTabs.length); + + chrome.test.assertEq(tabs[0].id, highlightedTabs[0].id); + + // Highlight a different tab. Both tabs should be highlighted. + await chrome.tabs.update(tabs[2].id, {highlighted: true}); + highlightedTabs = + await chrome.tabs.query({windowId: win.id, highlighted: true}); + chrome.test.assertEq(2, highlightedTabs.length); + let highlightedIds = highlightedTabs.map(t => t.id); + chrome.test.assertEq(highlightedIds.sort(), + [tabs[0].id, tabs[2].id].sort()); + + // Unhighlight the first tab. Only tabs[2] should be highlighted now. + await chrome.tabs.update(tabs[0].id, {highlighted: false}); + highlightedTabs = + await chrome.tabs.query({windowId: win.id, highlighted: true}); + chrome.test.assertEq(1, highlightedTabs.length); + chrome.test.assertEq(tabs[2].id, highlightedTabs[0].id); + + chrome.test.succeed(); + } + ]); + )"; + + extensions::TestExtensionDir test_dir; + test_dir.WriteManifest(kManifest); + test_dir.WriteFile(FILE_PATH_LITERAL("background.js"), kBackgroundJs); + + extensions::ResultCatcher catcher; + ASSERT_TRUE(LoadExtension(test_dir.UnpackedPath())); + EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); +} + class ExtensionApiCaptureTest : public ExtensionApiTabTest { public: ExtensionApiCaptureTest() = default;
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc index 479113e..3d3bf704 100644 --- a/chrome/browser/flags/android/chrome_feature_list.cc +++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -263,6 +263,7 @@ &kAvoidDoubleMultiwindowChanges, &kBlockIntentsWhileLocked, &kBookmarkPaneAndroid, + &kBottomSheetAsBrowserControls, &kBrowserControlsDebugging, &kBrowserControlsEarlyResize, &kBrowserControlsPersistsOnCvh, @@ -592,6 +593,7 @@ BASE_FEATURE(kAvoidDoubleMultiwindowChanges, base::FEATURE_DISABLED_BY_DEFAULT); BASE_FEATURE(kBlockIntentsWhileLocked, base::FEATURE_DISABLED_BY_DEFAULT); BASE_FEATURE(kBookmarkPaneAndroid, base::FEATURE_DISABLED_BY_DEFAULT); +BASE_FEATURE(kBottomSheetAsBrowserControls, base::FEATURE_ENABLED_BY_DEFAULT); BASE_FEATURE(kBrowserControlsDebugging, base::FEATURE_DISABLED_BY_DEFAULT); BASE_FEATURE(kBrowserControlsEarlyResize, base::FEATURE_DISABLED_BY_DEFAULT); BASE_FEATURE(kBrowserControlsPersistsOnCvh, base::FEATURE_ENABLED_BY_DEFAULT);
diff --git a/chrome/browser/flags/android/chrome_feature_list.h b/chrome/browser/flags/android/chrome_feature_list.h index 2f14ec0c..97eb9578 100644 --- a/chrome/browser/flags/android/chrome_feature_list.h +++ b/chrome/browser/flags/android/chrome_feature_list.h
@@ -81,6 +81,7 @@ BASE_DECLARE_FEATURE(kBackgroundThreadPool); BASE_DECLARE_FEATURE(kBlockIntentsWhileLocked); BASE_DECLARE_FEATURE(kBookmarkPaneAndroid); +BASE_DECLARE_FEATURE(kBottomSheetAsBrowserControls); BASE_DECLARE_FEATURE(kBrowserControlsDebugging); BASE_DECLARE_FEATURE(kBrowserControlsEarlyResize); BASE_DECLARE_FEATURE(kBrowserControlsPersistsOnCvh);
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java index 58f5147..12e182b3 100644 --- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java +++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -298,6 +298,7 @@ public static final String BACK_FORWARD_CACHE = "BackForwardCache"; public static final String BLOCK_INTENTS_WHILE_LOCKED = "BlockIntentsWhileLocked"; public static final String BOOKMARK_PANE_ANDROID = "BookmarkPaneAndroid"; + public static final String BOTTOM_SHEET_AS_BROWSER_CONTROLS = "BottomSheetAsBrowserControls"; public static final String BROWSER_CONTROLS_DEBUGGING = "BrowserControlsDebugging"; public static final String BROWSER_CONTROLS_EARLY_RESIZE = "BrowserControlsEarlyResize"; public static final String BROWSER_CONTROLS_PERSISTS_ON_CVH = "BrowserControlsPersistsOnCvh"; @@ -799,6 +800,8 @@ newCachedFlag(BLOCK_INTENTS_WHILE_LOCKED, false); public static final CachedFlag sBookmarkPaneAndroid = newCachedFlag(BOOKMARK_PANE_ANDROID, false); + public static final CachedFlag sBottomSheetAsBrowserControls = + newCachedFlag(BOTTOM_SHEET_AS_BROWSER_CONTROLS, true); public static final CachedFlag sBrowserControlsDebugging = newCachedFlag(BROWSER_CONTROLS_DEBUGGING, false); public static final CachedFlag sCacheIsMultiInstanceApi31Enabled = @@ -1152,6 +1155,7 @@ sBackgroundThreadPoolFieldTrial, sBlockIntentsWhileLocked, sBookmarkPaneAndroid, + sBottomSheetAsBrowserControls, sBrowserControlsDebugging, sCacheIsMultiInstanceApi31Enabled, sCctAdaptiveButton,
diff --git a/chrome/browser/resources/contextual_tasks/app.ts b/chrome/browser/resources/contextual_tasks/app.ts index 96dad18c..be7a3f19 100644 --- a/chrome/browser/resources/contextual_tasks/app.ts +++ b/chrome/browser/resources/contextual_tasks/app.ts
@@ -67,9 +67,6 @@ // <if expr="not is_android"> composebox: ContextualTasksComposeboxElement, // </if> - // <if expr="is_android"> - composebox: HTMLElement, - // </if> composeboxHeaderWrapper: HTMLElement, composeboxHeader: HTMLElement, flexCenterContainer: HTMLElement, @@ -563,7 +560,9 @@ }); }; + // <if expr="not is_android"> restartAnimations(this.$.composebox); + // </if> restartAnimations(this.$.composeboxHeaderWrapper); if (this.$.nameShimmer) {
diff --git a/chrome/browser/resources/guest_view_shared/slim_web_view.ts b/chrome/browser/resources/guest_view_shared/slim_web_view.ts index 4344d18..a1883cc3 100644 --- a/chrome/browser/resources/guest_view_shared/slim_web_view.ts +++ b/chrome/browser/resources/guest_view_shared/slim_web_view.ts
@@ -13,12 +13,6 @@ import {getHtml} from './slim_web_view.html.js'; import {BrowserProxyImpl, PermissionResponseAction} from './slim_web_view_browser_proxy.js'; -export interface SlimWebViewElement { - $: { - input: HTMLElement, - }; -} - const GUEST_INSTANCE_ID_PENDING: number = 0; export class ExitEvent extends Event {
diff --git a/chrome/browser/resources/history/side_bar.ts b/chrome/browser/resources/history/side_bar.ts index 43fbcaa..d94ed24 100644 --- a/chrome/browser/resources/history/side_bar.ts +++ b/chrome/browser/resources/history/side_bar.ts
@@ -29,9 +29,9 @@ export interface HistorySideBarElement { $: { - 'history': HTMLAnchorElement, - 'menu': CrMenuSelectorElement, - 'syncedTabs': HTMLElement, + history: HTMLAnchorElement, + menu: CrMenuSelectorElement, + syncedTabs: HTMLElement, }; }
diff --git a/chrome/browser/resources/history/synced_device_manager.css b/chrome/browser/resources/history/synced_device_manager.css index 8ecbcf4..47b0495 100644 --- a/chrome/browser/resources/history/synced_device_manager.css +++ b/chrome/browser/resources/history/synced_device_manager.css
@@ -47,11 +47,11 @@ } } -#no-synced-tabs { +#noSyncedTabs { height: 100%; } -#sign-in-guide { +#signInGuide { align-items: center; display: flex; flex-direction: column;
diff --git a/chrome/browser/resources/history/synced_device_manager.html.ts b/chrome/browser/resources/history/synced_device_manager.html.ts index 2df51aab..e20649d 100644 --- a/chrome/browser/resources/history/synced_device_manager.html.ts +++ b/chrome/browser/resources/history/synced_device_manager.html.ts
@@ -28,12 +28,12 @@ </history-synced-device-card> `)} </div> -<div id="no-synced-tabs" class="centered-message" +<div id="noSyncedTabs" class="centered-message" ?hidden="${!this.showNoSyncedMessage_()}"> ${this.noSyncedTabsMessage_()} </div> -<div id="sign-in-guide" +<div id="signInGuide" ?hidden="${!this.showSignInGuide_() || this.replaceSyncPromosWithSignInPromos_}"> <div id="sync-promo-illustration"></div>
diff --git a/chrome/browser/resources/history/synced_device_manager.ts b/chrome/browser/resources/history/synced_device_manager.ts index 3e128b13..0503ed7 100644 --- a/chrome/browser/resources/history/synced_device_manager.ts +++ b/chrome/browser/resources/history/synced_device_manager.ts
@@ -47,9 +47,9 @@ export interface HistorySyncedDeviceManagerElement { $: { - 'menu': CrLazyRenderLitElement<CrActionMenuElement>, - 'no-synced-tabs': HTMLElement, - 'sign-in-guide': HTMLElement, + menu: CrLazyRenderLitElement<CrActionMenuElement>, + noSyncedTabs: HTMLElement, + signInGuide: HTMLElement, }; }
diff --git a/chrome/browser/resources/omnibox/BUILD.gn b/chrome/browser/resources/omnibox/BUILD.gn index 5c2ce13..a9b17c7 100644 --- a/chrome/browser/resources/omnibox/BUILD.gn +++ b/chrome/browser/resources/omnibox/BUILD.gn
@@ -52,7 +52,7 @@ ] ts_tsconfig_base = "tsconfig_base.json" - ts_definitions = [ "css.d.ts" ] + ts_definitions = [ "//tools/typescript/definitions/pending.d.ts" ] ts_deps = [ "//ui/webui/resources/js:build_ts", "//ui/webui/resources/mojo:build_ts",
diff --git a/chrome/browser/resources/omnibox/css.d.ts b/chrome/browser/resources/omnibox/css.d.ts deleted file mode 100644 index 09bc2d2..0000000 --- a/chrome/browser/resources/omnibox/css.d.ts +++ /dev/null
@@ -1,9 +0,0 @@ -// Copyright 2024 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// See https://github.com/microsoft/TypeScript/issues/46135. -declare module '*.css' { - const _default: CSSStyleSheet; - export default _default; -}
diff --git a/chrome/browser/resources/omnibox_popup/aim_app.css b/chrome/browser/resources/omnibox_popup/aim_app.css index 75ee3d34..6b10f274 100644 --- a/chrome/browser/resources/omnibox_popup/aim_app.css +++ b/chrome/browser/resources/omnibox_popup/aim_app.css
@@ -99,14 +99,11 @@ --cr-icon-button-fill-color: var(--cr-composebox-voice-icon-fill-color); } -cr-composebox[searchbox-layout-mode='TallBottomContext']::part(voice-icon) { +cr-composebox[searchbox-layout-mode='TallBottomContext']::part(voice-icon), +cr-composebox[submit-enabled_][searchbox-layout-mode='TallBottomContext']:not([show-dropdown_])::part(submit) { bottom: 6px; } -cr-composebox[show-submit_][submit-enabled_][searchbox-layout-mode='TallBottomContext']:not([show-dropdown_])::part(submit) { - bottom: 6px; -} - cr-composebox[searchbox-layout-mode='TallTopContext'] { --cr-composebox-submit-button-margin-inline-start: 0; }
diff --git a/chrome/browser/resources/omnibox_popup/aim_app.html.ts b/chrome/browser/resources/omnibox_popup/aim_app.html.ts index be4be1e..f17b6b9 100644 --- a/chrome/browser/resources/omnibox_popup/aim_app.html.ts +++ b/chrome/browser/resources/omnibox_popup/aim_app.html.ts
@@ -19,6 +19,7 @@ @close-composebox="${this.onCloseComposebox_}" @composebox-submit="${this.onComposeboxSubmit_}" .showMenuOnClick="${false}" + .shouldShowGhostFiles="${true}" entrypoint-name="Omnibox"> </cr-composebox> </div>
diff --git a/chrome/browser/resources/on_device_internals/app.ts b/chrome/browser/resources/on_device_internals/app.ts index c04d406c..4740a443 100644 --- a/chrome/browser/resources/on_device_internals/app.ts +++ b/chrome/browser/resources/on_device_internals/app.ts
@@ -17,7 +17,7 @@ export interface OnDeviceInternalsAppElement { $: { - 'tabs': CrTabsElement, + tabs: CrTabsElement, }; }
diff --git a/chrome/browser/resources/skills/card.ts b/chrome/browser/resources/skills/card.ts index 341c5c59..e3e55e7 100644 --- a/chrome/browser/resources/skills/card.ts +++ b/chrome/browser/resources/skills/card.ts
@@ -29,7 +29,6 @@ export interface SkillCardElement { $: { - cardBody: HTMLElement, name: HTMLElement, icon: HTMLElement, menu: CrActionMenuElement,
diff --git a/chrome/browser/resources/user_education_internals/user_education_internals.html.ts b/chrome/browser/resources/user_education_internals/user_education_internals.html.ts index 9392498..4701933f 100644 --- a/chrome/browser/resources/user_education_internals/user_education_internals.html.ts +++ b/chrome/browser/resources/user_education_internals/user_education_internals.html.ts
@@ -136,7 +136,7 @@ </div> <div id="whatsNew"> <h2>What's New</h2> - <if expr="is_chromeos == False"> + <if expr="not is_chromeos"> <div class="whats-new-section"> <h3>Version Override</h3> <p>
diff --git a/chrome/browser/resources/user_education_internals/user_education_internals.ts b/chrome/browser/resources/user_education_internals/user_education_internals.ts index bc25567..fe2b4c864 100644 --- a/chrome/browser/resources/user_education_internals/user_education_internals.ts +++ b/chrome/browser/resources/user_education_internals/user_education_internals.ts
@@ -16,7 +16,10 @@ import './user_education_internals_card.js'; import './user_education_whats_new_internals_card.js'; +// <if expr="not is_chromeos"> import type {CrInputElement} from '//resources/cr_elements/cr_input/cr_input.js'; +// </if> + import {ColorChangeUpdater} from 'chrome://resources/cr_components/color_change_listener/colors_css_updater.js'; import {HelpBubbleMixinLit} from 'chrome://resources/cr_components/help_bubble/help_bubble_mixin_lit.js'; import type {CrMenuSelectorElement} from 'chrome://resources/cr_elements/cr_menu_selector/cr_menu_selector.js'; @@ -35,7 +38,9 @@ content: HTMLElement, errorMessageToast: CrToastElement, menu: CrMenuSelectorElement, + // <if expr="not is_chromeos"> whatsNewVersionOverride: CrInputElement, + // </if> }; } @@ -385,6 +390,7 @@ this.ntpPromoPreferencesExpanded_ = e.detail.value; } + // <if expr="not is_chromeos"> protected onLaunchWhatsNewStagingClick_() { this.handler_.launchWhatsNewStaging(); } @@ -401,6 +407,7 @@ this.handler_.updateWhatsNewVersionOverride(versionOverride); this.whatsNewVersionToRequest_ = versionOverride; } + // </if> } declare global {
diff --git a/chrome/browser/resources/webui_toolbar/BUILD.gn b/chrome/browser/resources/webui_toolbar/BUILD.gn index 1cc48d0..9a8bf99 100644 --- a/chrome/browser/resources/webui_toolbar/BUILD.gn +++ b/chrome/browser/resources/webui_toolbar/BUILD.gn
@@ -66,10 +66,14 @@ mojo_files_deps = [ "//components/browser_apis/browser_controls:mojom_data_model_ts__generator", "//components/browser_apis/browser_controls:mojom_ts__generator", + "//components/browser_apis/ui_controllers/toolbar:mojom_data_model_ts__generator", + "//components/browser_apis/ui_controllers/toolbar:mojom_ts__generator", ] mojo_files = [ "$root_gen_dir/components/browser_apis/browser_controls/browser_controls_api.mojom-webui.ts", "$root_gen_dir/components/browser_apis/browser_controls/browser_controls_api_data_model.mojom-webui.ts", + "$root_gen_dir/components/browser_apis/ui_controllers/toolbar/toolbar_ui_api.mojom-webui.ts", + "$root_gen_dir/components/browser_apis/ui_controllers/toolbar/toolbar_ui_api_data_model.mojom-webui.ts", ] }
diff --git a/chrome/browser/resources/webui_toolbar/app.ts b/chrome/browser/resources/webui_toolbar/app.ts index f861c23..c740441 100644 --- a/chrome/browser/resources/webui_toolbar/app.ts +++ b/chrome/browser/resources/webui_toolbar/app.ts
@@ -13,10 +13,10 @@ import {getCss} from './app.css.js'; import {getHtml} from './app.html.js'; -import {SplitTabActiveLocation} from './browser_controls_api_data_model.mojom-webui.js'; import {BrowserProxyImpl, INVALID_NAVIGATION_CONTROLS_STATE_LISTENER_HANDLE} from './browser_proxy.js'; import type {BrowserProxy, NavigationControlsState, NavigationControlsStateListenerHandle} from './browser_proxy.js'; import {MetricsRecorder} from './metrics_recorder.js'; +import {SplitTabActiveLocation} from './toolbar_ui_api_data_model.mojom-webui.js'; export class ToolbarAppElement extends CrLitElement { static get is() { @@ -166,7 +166,7 @@ promises.push(splitTabs.updateComplete); } Promise.all(promises).then(() => { - this.browserProxy_.handler.onPageInitialized(); + this.browserProxy_.toolbarUIHandler.onPageInitialized(); }); } }
diff --git a/chrome/browser/resources/webui_toolbar/browser_proxy.ts b/chrome/browser/resources/webui_toolbar/browser_proxy.ts index d49546f..32794288 100644 --- a/chrome/browser/resources/webui_toolbar/browser_proxy.ts +++ b/chrome/browser/resources/webui_toolbar/browser_proxy.ts
@@ -4,10 +4,21 @@ import '//resources/js/cr.js'; -import {BrowserControlsObserverCallbackRouter, BrowserControlsService} from './browser_controls_api.mojom-webui.js'; +import {BrowserControlsService} from './browser_controls_api.mojom-webui.js'; import type {BrowserControlsServiceInterface} from './browser_controls_api.mojom-webui.js'; -import {ClickDispositionFlag, ContextMenuType} from './browser_controls_api_data_model.mojom-webui.js'; -import type {NavigationControlsState, ReloadControlState} from './browser_controls_api_data_model.mojom-webui.js'; +import {ClickDispositionFlag} from './browser_controls_api_data_model.mojom-webui.js'; +import { + ToolbarUIObserverCallbackRouter, + ToolbarUIService, +} from './toolbar_ui_api.mojom-webui.js'; +import type {ToolbarUIServiceInterface} from './toolbar_ui_api.mojom-webui.js'; +import { + ContextMenuType, +} from './toolbar_ui_api_data_model.mojom-webui.js'; +import type { + NavigationControlsState, + ReloadControlState, +} from './toolbar_ui_api_data_model.mojom-webui.js'; export { ClickDispositionFlag, @@ -26,7 +37,8 @@ NavigationControlsStateListenerHandle = -1; export interface BrowserProxy { - handler: BrowserControlsServiceInterface; + browserControlsHandler: BrowserControlsServiceInterface; + toolbarUIHandler: ToolbarUIServiceInterface; /** * Records a value in a histogram. @@ -45,12 +57,14 @@ } export class BrowserProxyImpl implements BrowserProxy { - private callbackRouter: BrowserControlsObserverCallbackRouter; - handler: BrowserControlsServiceInterface; + private callbackRouter: ToolbarUIObserverCallbackRouter; + browserControlsHandler: BrowserControlsServiceInterface; + toolbarUIHandler: ToolbarUIServiceInterface; private constructor() { - this.callbackRouter = new BrowserControlsObserverCallbackRouter(); - this.handler = BrowserControlsService.getRemote(); + this.callbackRouter = new ToolbarUIObserverCallbackRouter(); + this.browserControlsHandler = BrowserControlsService.getRemote(); + this.toolbarUIHandler = ToolbarUIService.getRemote(); } /** @@ -68,7 +82,7 @@ const handle = this.callbackRouter.onNavigationControlsStateChanged.addListener( listener); - this.handler.bind().then(fence => { + this.toolbarUIHandler.bind().then(fence => { listener(fence.state); this.callbackRouter.$.bindHandle(fence.updateStream.handle); });
diff --git a/chrome/browser/resources/webui_toolbar/reload_button.ts b/chrome/browser/resources/webui_toolbar/reload_button.ts index 9adb00a..f58c56a 100644 --- a/chrome/browser/resources/webui_toolbar/reload_button.ts +++ b/chrome/browser/resources/webui_toolbar/reload_button.ts
@@ -143,7 +143,7 @@ // as true, so that it won't be treated as a normal click. this.isLongPressed_ = true; if (this.state.isDevtoolsConnected) { - BrowserProxyImpl.getInstance().handler.showContextMenu( + BrowserProxyImpl.getInstance().toolbarUIHandler.showContextMenu( ContextMenuType.kReload, this.contextMenuPosition(), MenuSourceType.kLongPress); } @@ -207,11 +207,11 @@ clearTimeout(this.longPressTimer_); if (this.state.isNavigationLoading) { - BrowserProxyImpl.getInstance().handler.stopLoad(); + BrowserProxyImpl.getInstance().browserControlsHandler.stopLoad(); } else { // If the shift or ctrl key is pressed, we should reload with cache // bypassed. - BrowserProxyImpl.getInstance().handler.reloadFromClick( + BrowserProxyImpl.getInstance().browserControlsHandler.reloadFromClick( /*bypass_cache=*/ e.shiftKey || e.ctrlKey, this.generateFlags(e)); } @@ -223,7 +223,7 @@ protected onContextmenu_(e: PointerEvent) { if (this.state.isDevtoolsConnected) { - BrowserProxyImpl.getInstance().handler.showContextMenu( + BrowserProxyImpl.getInstance().toolbarUIHandler.showContextMenu( ContextMenuType.kReload, this.contextMenuPosition(), MenuSourceType.kMouse); }
diff --git a/chrome/browser/resources/webui_toolbar/split_tabs_button.ts b/chrome/browser/resources/webui_toolbar/split_tabs_button.ts index b2f30ba8..ccc6b64 100644 --- a/chrome/browser/resources/webui_toolbar/split_tabs_button.ts +++ b/chrome/browser/resources/webui_toolbar/split_tabs_button.ts
@@ -10,12 +10,13 @@ import {CrLitElement} from '//resources/lit/v3_0/lit.rollup.js'; import type {PropertyValues} from '//resources/lit/v3_0/lit.rollup.js'; -import {ContextMenuType, SplitTabActiveLocation} from './browser_controls_api_data_model.mojom-webui.js'; -import type {SplitTabsControlState} from './browser_controls_api_data_model.mojom-webui.js'; +import {SplitTabActiveLocation} from './toolbar_ui_api_data_model.mojom-webui.js'; +import {ContextMenuType} from './toolbar_ui_api_data_model.mojom-webui.js'; import {type BrowserProxy, BrowserProxyImpl} from './browser_proxy.js'; import {getCss} from './split_tabs_button.css.js'; import {getHtml} from './split_tabs_button.html.js'; import {getClickSourceType, getContextMenuPosition, getContextMenuSourceType} from './toolbar_button.js'; +import type {SplitTabsControlState} from './toolbar_ui_api_data_model.mojom-webui.js'; export class SplitTabsButtonElement extends CrLitElement { static get is() { @@ -88,18 +89,18 @@ protected onClick(e: Event) { if (this.state.isCurrentTabSplit) { // If already split, show the action menu. - this.browserProxy_.handler.showContextMenu( + this.browserProxy_.toolbarUIHandler.showContextMenu( ContextMenuType.kSplitTabsAction, this.menuPosition(), getClickSourceType(e)); } else { // If not split, enters split view. - this.browserProxy_.handler.splitActiveTab(); + this.browserProxy_.browserControlsHandler.splitActiveTab(); } } protected onContextmenu(e: MouseEvent) { e.preventDefault(); - this.browserProxy_.handler.showContextMenu( + this.browserProxy_.toolbarUIHandler.showContextMenu( ContextMenuType.kSplitTabsContext, this.menuPosition(), getContextMenuSourceType(e)); }
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 d63abd5a1..8e51211 100644 --- a/chrome/browser/safe_browsing/client_side_detection_host_browsertest.cc +++ b/chrome/browser/safe_browsing/client_side_detection_host_browsertest.cc
@@ -173,40 +173,6 @@ ~MockSafeBrowsingUIManager() override = default; }; -// This class waits for page load state that ClientSideDetectionHost observes as -// a WebContentsObserver. ClientSideDetectionHost observes the two functions -// listed before starting the preclassification check. -class PaintObserverWaiter : public content::WebContentsObserver { - public: - explicit PaintObserverWaiter(content::WebContents* web_contents) - : WebContentsObserver(web_contents) {} - - void DidFirstVisuallyNonEmptyPaint() override { - did_paint_ = true; - if (did_fcp_) { - run_loop_.Quit(); - } - } - - void OnFirstContentfulPaintInPrimaryMainFrame() override { - did_fcp_ = true; - if (did_paint_) { - run_loop_.Quit(); - } - } - - void Wait() { - if (!did_paint_ || !did_fcp_) { - run_loop_.Run(); - } - } - - private: - bool did_paint_ = false; - bool did_fcp_ = false; - base::RunLoop run_loop_; -}; - std::string set_up_client_side_model() { flatbuffers::FlatBufferBuilder builder(1024); std::vector<flatbuffers::Offset<flat::Hash>> hashes; @@ -379,13 +345,7 @@ fake_csd_service.SendModelToRenderers(); GURL page_url(embedded_test_server()->GetURL("/safe_browsing/malware.html")); - if (base::FeatureList::IsEnabled(kClientSideDetectionNewObservers)) { - PaintObserverWaiter paint_waiter(GetWebContents()); - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), page_url)); - paint_waiter.Wait(); - } else { - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), page_url)); - } + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), page_url)); base::RunLoop run_loop; fake_csd_service.SetRequestCallback(run_loop.QuitClosure()); @@ -433,13 +393,7 @@ fake_csd_service.SendModelToRenderers(); GURL page_url(embedded_test_server()->GetURL("/safe_browsing/malware.html")); - if (base::FeatureList::IsEnabled(kClientSideDetectionNewObservers)) { - PaintObserverWaiter paint_waiter(GetWebContents()); - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), page_url)); - paint_waiter.Wait(); - } else { - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), page_url)); - } + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), page_url)); base::RunLoop run_loop; fake_csd_service.SetRequestCallback(run_loop.QuitClosure()); @@ -503,25 +457,13 @@ fake_csd_service.SetRequestCallback(run_loop.QuitClosure()); const GURL initial_url(embedded_test_server()->GetURL("/title1.html")); - if (base::FeatureList::IsEnabled(kClientSideDetectionNewObservers)) { - PaintObserverWaiter paint_waiter(GetWebContents()); - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); - paint_waiter.Wait(); - } else { - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); - } + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); // Prerender then activate a phishing page. const GURL prerender_url = embedded_test_server()->GetURL("/safe_browsing/malware.html"); prerender_helper().AddPrerender(prerender_url); - if (base::FeatureList::IsEnabled(kClientSideDetectionNewObservers)) { - PaintObserverWaiter paint_waiter(GetWebContents()); - prerender_helper().NavigatePrimaryPage(prerender_url); - paint_waiter.Wait(); - } else { - prerender_helper().NavigatePrimaryPage(prerender_url); - } + prerender_helper().NavigatePrimaryPage(prerender_url); // Bypass the pre-classification checks. csd_host->OnPhishingPreClassificationDone( @@ -655,25 +597,13 @@ fake_csd_service.SetRequestCallback(run_loop.QuitClosure()); const GURL initial_url(embedded_test_server()->GetURL("/title1.html")); - if (base::FeatureList::IsEnabled(kClientSideDetectionNewObservers)) { - PaintObserverWaiter paint_waiter(GetWebContents()); - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); - paint_waiter.Wait(); - } else { - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); - } + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); // Prerender then activate a phishing page. const GURL prerender_url = embedded_test_server()->GetURL("/safe_browsing/malware.html"); prerender_helper().AddPrerender(prerender_url); - if (base::FeatureList::IsEnabled(kClientSideDetectionNewObservers)) { - PaintObserverWaiter paint_waiter(GetWebContents()); - prerender_helper().NavigatePrimaryPage(prerender_url); - paint_waiter.Wait(); - } else { - prerender_helper().NavigatePrimaryPage(prerender_url); - } + prerender_helper().NavigatePrimaryPage(prerender_url); // Bypass the pre-classification checks. csd_host->OnPhishingPreClassificationDone( @@ -734,25 +664,13 @@ fake_csd_service.SetRequestCallback(run_loop.QuitClosure()); const GURL initial_url(embedded_test_server()->GetURL("/title1.html")); - if (base::FeatureList::IsEnabled(kClientSideDetectionNewObservers)) { - PaintObserverWaiter paint_waiter(GetWebContents()); - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); - paint_waiter.Wait(); - } else { - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); - } + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); // Prerender then activate a phishing page. const GURL prerender_url = embedded_test_server()->GetURL("/safe_browsing/malware.html"); prerender_helper().AddPrerender(prerender_url); - if (base::FeatureList::IsEnabled(kClientSideDetectionNewObservers)) { - PaintObserverWaiter paint_waiter(GetWebContents()); - prerender_helper().NavigatePrimaryPage(prerender_url); - paint_waiter.Wait(); - } else { - prerender_helper().NavigatePrimaryPage(prerender_url); - } + prerender_helper().NavigatePrimaryPage(prerender_url); feature_cache_map->Clear(); @@ -809,13 +727,7 @@ fake_csd_service.SendModelToRenderers(); const GURL initial_url(embedded_test_server()->GetURL("/title1.html")); - if (base::FeatureList::IsEnabled(kClientSideDetectionNewObservers)) { - PaintObserverWaiter paint_waiter(GetWebContents()); - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); - paint_waiter.Wait(); - } else { - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); - } + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); // TODO(andysjlim): Navigating to initial page alongside the first page logs // the histogram twice. Figure out why. @@ -861,13 +773,7 @@ fake_csd_service.SendModelToRenderers(); const GURL initial_url(embedded_test_server()->GetURL("/title1.html")); - if (base::FeatureList::IsEnabled(kClientSideDetectionNewObservers)) { - PaintObserverWaiter paint_waiter(GetWebContents()); - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); - paint_waiter.Wait(); - } else { - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); - } + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); // Navigating to initial page logs the histogram twice. histogram_tester.ExpectTotalCount( @@ -916,22 +822,10 @@ fake_csd_service.SetRequestCallback(run_loop.QuitClosure()); const GURL initial_url(embedded_test_server()->GetURL("/title1.html")); - if (base::FeatureList::IsEnabled(kClientSideDetectionNewObservers)) { - PaintObserverWaiter paint_waiter(GetWebContents()); - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); - paint_waiter.Wait(); - } else { - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); - } + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); prerender_helper().AddPrerender(initial_url); - if (base::FeatureList::IsEnabled(kClientSideDetectionNewObservers)) { - PaintObserverWaiter paint_waiter(GetWebContents()); - prerender_helper().NavigatePrimaryPage(initial_url); - paint_waiter.Wait(); - } else { - prerender_helper().NavigatePrimaryPage(initial_url); - } + prerender_helper().NavigatePrimaryPage(initial_url); EnterActiveTabFullscreen(); ASSERT_TRUE(RequestKeyboardLock(/*esc_key_locked=*/true)); @@ -998,22 +892,10 @@ fake_csd_service.SetRequestCallback(run_loop.QuitClosure()); const GURL initial_url(embedded_test_server()->GetURL("/title1.html")); - if (base::FeatureList::IsEnabled(kClientSideDetectionNewObservers)) { - PaintObserverWaiter paint_waiter(GetWebContents()); - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); - paint_waiter.Wait(); - } else { - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); - } + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); prerender_helper().AddPrerender(initial_url); - if (base::FeatureList::IsEnabled(kClientSideDetectionNewObservers)) { - PaintObserverWaiter paint_waiter(GetWebContents()); - prerender_helper().NavigatePrimaryPage(initial_url); - paint_waiter.Wait(); - } else { - prerender_helper().NavigatePrimaryPage(initial_url); - } + prerender_helper().NavigatePrimaryPage(initial_url); RequestToLockPointer(true, false); ASSERT_TRUE(GetExclusiveAccessManager() @@ -1132,13 +1014,7 @@ fake_csd_service.SendModelToRenderers(); const GURL initial_url(embedded_test_server()->GetURL("/title1.html")); - if (base::FeatureList::IsEnabled(kClientSideDetectionNewObservers)) { - PaintObserverWaiter paint_waiter(GetWebContents()); - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); - paint_waiter.Wait(); - } else { - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); - } + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); // TODO(andysjlim): Navigating to initial page alongside the first page logs // the histogram twice. Figure out why. @@ -1200,13 +1076,7 @@ fake_csd_service.SetRequestCallback(run_loop.QuitClosure()); const GURL initial_url(embedded_test_server()->GetURL("/title1.html")); - if (base::FeatureList::IsEnabled(kClientSideDetectionNewObservers)) { - PaintObserverWaiter paint_waiter(GetWebContents()); - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); - paint_waiter.Wait(); - } else { - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); - } + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); // Bypass the pre-classification check because it would otherwise return // "NO_CLASSIFY_PRIVATE_IP". @@ -1362,13 +1232,7 @@ fake_csd_service.SendModelToRenderers(); const GURL initial_url(embedded_test_server()->GetURL("/title1.html")); - if (base::FeatureList::IsEnabled(kClientSideDetectionNewObservers)) { - PaintObserverWaiter paint_waiter(GetWebContents()); - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); - paint_waiter.Wait(); - } else { - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); - } + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); histogram_tester.ExpectTotalCount( "SBClientPhishing.PreClassificationCheckResult.ClipboardCopyApi", 0); @@ -1418,13 +1282,7 @@ fake_csd_service.SetRequestCallback(csd_request_run_loop.QuitClosure()); const GURL initial_url(embedded_test_server()->GetURL("/title1.html")); - if (base::FeatureList::IsEnabled(kClientSideDetectionNewObservers)) { - PaintObserverWaiter paint_waiter(GetWebContents()); - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); - paint_waiter.Wait(); - } else { - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); - } + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); histogram_tester.ExpectTotalCount( "SBClientPhishing.PhishingDetectorResult.ClipboardCopyApi", 0); @@ -1528,13 +1386,7 @@ base::HistogramTester histogram_tester; const GURL initial_url(embedded_test_server()->GetURL("/title1.html")); - if (base::FeatureList::IsEnabled(kClientSideDetectionNewObservers)) { - PaintObserverWaiter paint_waiter(GetWebContents()); - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); - paint_waiter.Wait(); - } else { - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); - } + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); histogram_tester.ExpectTotalCount( "SBClientPhishing.PreClassificationCheckResult.ClipboardCopyApi", 0); @@ -1568,13 +1420,7 @@ base::HistogramTester histogram_tester; const GURL initial_url(embedded_test_server()->GetURL("/title1.html")); - if (base::FeatureList::IsEnabled(kClientSideDetectionNewObservers)) { - PaintObserverWaiter paint_waiter(GetWebContents()); - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); - paint_waiter.Wait(); - } else { - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); - } + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); histogram_tester.ExpectTotalCount( "SBClientPhishing.PreClassificationCheckResult.ClipboardCopyApi", 0); @@ -1641,13 +1487,7 @@ GURL NavigateToCreditCardForm() { const GURL url(embedded_test_server()->GetURL( "/autofill/autofill_creditcard_form.html")); - if (base::FeatureList::IsEnabled(kClientSideDetectionNewObservers)) { - PaintObserverWaiter paint_waiter(GetWebContents()); - EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); - paint_waiter.Wait(); - } else { - EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); - } + EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); return url; } @@ -1849,22 +1689,9 @@ fake_csd_service.SetRequestCallback(run_loop.QuitClosure()); const GURL initial_url(embedded_test_server()->GetURL("/title1.html")); - if (base::FeatureList::IsEnabled(kClientSideDetectionNewObservers)) { - PaintObserverWaiter paint_waiter(GetWebContents()); - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); - paint_waiter.Wait(); - } else { - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); - } - + ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url)); prerender_helper().AddPrerender(initial_url); - if (base::FeatureList::IsEnabled(kClientSideDetectionNewObservers)) { - PaintObserverWaiter paint_waiter(GetWebContents()); - prerender_helper().NavigatePrimaryPage(initial_url); - paint_waiter.Wait(); - } else { - prerender_helper().NavigatePrimaryPage(initial_url); - } + prerender_helper().NavigatePrimaryPage(initial_url); csd_host->OnPhishingPreClassificationDone( ClientSideDetectionType::FORCE_REQUEST, /*should_classify=*/true,
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 68f6ba93..67d9757 100644 --- a/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc +++ b/chrome/browser/safe_browsing/client_side_detection_host_unittest.cc
@@ -79,7 +79,6 @@ #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/navigation_entry.h" -#include "content/public/browser/navigation_handle.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/web_contents.h" #include "content/public/test/browser_test_utils.h" @@ -126,6 +125,16 @@ const bool kFalse = false; const bool kTrue = true; +std::unique_ptr<content::NavigationSimulator> NavigateAndKeepLoading( + content::WebContents* web_contents, + const GURL& url) { + auto navigation = + content::NavigationSimulator::CreateBrowserInitiated(url, web_contents); + navigation->SetKeepLoading(true); + navigation->Commit(); + return navigation; +} + void WaitUntilHighConfidenceAllowlistCheckDone() { base::StatisticsRecorder::HistogramWaiter( "SBClientPhishing.MatchHighConfidenceAllowlist") @@ -576,30 +585,11 @@ } } - void NotifyClientSideDetectionObservers() { - content::WebContentsTester::For(web_contents()) - ->TestDidFirstVisuallyNonEmptyPaint(); - if (csd_host_) { - csd_host_->OnFirstContentfulPaintInPrimaryMainFrame(); - } - } - - void NavigateAndCommit(const GURL& safe_url, - bool reverse_callback_order = false) { + void NavigateAndCommit(const GURL& safe_url) { controller().LoadURL(safe_url, content::Referrer(), ui::PAGE_TRANSITION_LINK, std::string()); + content::WebContentsTester::For(web_contents())->CommitPendingNavigation(); - if (base::FeatureList::IsEnabled(kClientSideDetectionNewObservers)) { - if (!reverse_callback_order) { - NotifyClientSideDetectionObservers(); - } else { - if (csd_host_) { - csd_host_->OnFirstContentfulPaintInPrimaryMainFrame(); - } - content::WebContentsTester::For(web_contents()) - ->TestDidFirstVisuallyNonEmptyPaint(); - } - } } void AdvanceTimeTickClock(base::TimeDelta delta) { clock_.Advance(delta); } @@ -1142,41 +1132,7 @@ database_manager_->SetAllowlistLookupDetailsForUrl(url, false); ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse, &kFalse); - NavigateAndCommit(url); - WaitAndCheckPreClassificationChecks(); - - fake_phishing_detector_.CheckMessage(&url); - - histogram_tester.ExpectBucketCount( - "SBClientPhishing.PreClassificationCheckResult", - PreClassificationCheckResult::CLASSIFY, 1); - histogram_tester.ExpectBucketCount( - "SBClientPhishing.PreClassificationCheckResult.TriggerModel", - PreClassificationCheckResult::CLASSIFY, 1); - histogram_tester.ExpectBucketCount( - "SBClientPhishing.OnDeviceModelSessionAliveOnNewPreclassification", false, - 1); - histogram_tester.ExpectBucketCount( - "SBClientPhishing.IntelligentScanOngoingOnNewPreclassification", false, - 1); -} - -TEST_F(ClientSideDetectionHostTest, - TestPreClassificationCheckPassAlternateObserverOrder) { - if (base::FeatureList::IsEnabled(kClientSideDetectionKillswitch)) { - GTEST_SKIP(); - } - - base::HistogramTester histogram_tester; - - // Navigate the tab to a page. We should see a StartPhishingDetection IPC. - GURL url("http://host.com/"); - database_manager_->SetAllowlistLookupDetailsForUrl(url, false); - ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse, - &kFalse); - - NavigateAndCommit(url, /*reverse_callback_order=*/true); - + NavigateAndKeepLoading(web_contents(), url); WaitAndCheckPreClassificationChecks(); fake_phishing_detector_.CheckMessage(&url); @@ -1205,7 +1161,7 @@ database_manager_->SetAllowlistLookupDetailsForUrl(url, false); ExpectPreClassificationChecks(url, &kFalse, &kTrue, nullptr, nullptr, &kFalse); - NavigateAndCommit(url); + NavigateAndKeepLoading(web_contents(), url); WaitAndCheckPreClassificationChecks(); } @@ -1222,7 +1178,7 @@ database_manager_->SetAllowlistLookupDetailsForUrl(url, /*match=*/true); ExpectPreClassificationChecks(url, &kFalse, &kFalse, nullptr, nullptr, nullptr); - NavigateAndCommit(url); + NavigateAndKeepLoading(web_contents(), url); WaitAndCheckPreClassificationChecks(); histogram_tester.ExpectTotalCount( @@ -1245,7 +1201,7 @@ database_manager_->SetAllowlistLookupDetailsForUrl(url, /*match=*/true); ExpectPreClassificationChecks(url, &kFalse, &kFalse, nullptr, nullptr, nullptr); - NavigateAndCommit(url); + NavigateAndKeepLoading(web_contents(), url); WaitAndCheckPreClassificationChecks(); histogram_tester.ExpectTotalCount( @@ -1272,9 +1228,6 @@ ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse, &kFalse); navigation->Commit(); - if (base::FeatureList::IsEnabled(kClientSideDetectionNewObservers)) { - NotifyClientSideDetectionObservers(); - } WaitAndCheckPreClassificationChecks(); fake_phishing_detector_.CheckMessage(&url); @@ -1290,7 +1243,7 @@ database_manager_->SetAllowlistLookupDetailsForUrl(url1, false); ExpectPreClassificationChecks(url1, &kFalse, &kFalse, &kFalse, &kFalse, &kFalse); - NavigateAndCommit(url1); + NavigateAndKeepLoading(web_contents(), url1); WaitAndCheckPreClassificationChecks(); fake_phishing_detector_.CheckMessage(&url1); @@ -1299,7 +1252,7 @@ database_manager_->SetAllowlistLookupDetailsForUrl(url2, false); ExpectPreClassificationChecks(url2, &kFalse, &kFalse, &kFalse, &kFalse, &kFalse); - NavigateAndCommit(url2); + NavigateAndKeepLoading(web_contents(), url2); WaitAndCheckPreClassificationChecks(); fake_phishing_detector_.CheckMessage(&url2); @@ -1315,11 +1268,11 @@ // preclassification check and continue loading, so that url2 can cancel it. GURL url1("http://host1.com/"); database_manager_->SetAllowlistLookupDetailsForUrl(url1, false); - NavigateAndCommit(url1); + NavigateAndKeepLoading(web_contents(), url1); GURL url2("http://host2.com/"); database_manager_->SetAllowlistLookupDetailsForUrl(url2, false); - NavigateAndCommit(url2); + NavigateAndKeepLoading(web_contents(), url2); // Navigating to a second page will cancel the preclassification check of the // first page. @@ -1400,9 +1353,6 @@ content::NavigationSimulator::CreateBrowserInitiated(url, web_contents()); navigation->Fail(net::ERR_FAILED); navigation->CommitErrorPage(); - if (base::FeatureList::IsEnabled(kClientSideDetectionNewObservers)) { - NotifyClientSideDetectionObservers(); - } WaitAndCheckPreClassificationChecks(); histogram_tester.ExpectUniqueSample( @@ -1428,7 +1378,7 @@ ExpectPreClassificationChecks(url, &kFalse, nullptr, nullptr, nullptr, &kFalse); - NavigateAndCommit(url); + content::WebContentsTester::For(web_contents())->NavigateAndCommit(url); WaitAndCheckPreClassificationChecks(); fake_phishing_detector_.CheckMessage(nullptr); @@ -1446,7 +1396,7 @@ database_manager_->SetAllowlistLookupDetailsForUrl(url, false); ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kTrue, &kFalse); - NavigateAndCommit(url); + NavigateAndKeepLoading(web_contents(), url); WaitAndCheckPreClassificationChecks(); fake_phishing_detector_.CheckMessage(nullptr); @@ -1461,7 +1411,7 @@ database_manager_->SetAllowlistLookupDetailsForUrl(url, false); ExpectPreClassificationChecks(url, &kFalse, &kFalse, &kFalse, &kFalse, &kFalse); - NavigateAndCommit(url); + NavigateAndKeepLoading(web_contents(), url); WaitAndCheckPreClassificationChecks(); fake_phishing_detector_.CheckMessage(&url); @@ -1476,7 +1426,7 @@ GURL url("file://host.com/"); ExpectPreClassificationChecks(url, &kFalse, nullptr, nullptr, nullptr, &kFalse); - NavigateAndCommit(url); + NavigateAndKeepLoading(web_contents(), url); WaitAndCheckPreClassificationChecks(); fake_phishing_detector_.CheckMessage(nullptr); @@ -1498,7 +1448,7 @@ EXPECT_CALL(*ui_manager_.get(), DisplayBlockingPage(_)) .WillOnce(SaveArg<0>(&resource)); - NavigateAndCommit(url); + NavigateAndKeepLoading(web_contents(), url); WaitAndCheckPreClassificationChecks(); EXPECT_EQ(url, resource.url); EXPECT_EQ(url, resource.original_url); @@ -1520,7 +1470,7 @@ ExpectPreClassificationChecks(url, &kFalse, nullptr, nullptr, nullptr, &kFalse); - NavigateAndCommit(url); + NavigateAndKeepLoading(web_contents(), url); WaitAndCheckPreClassificationChecks(); fake_phishing_detector_.CheckMessage(nullptr); @@ -2088,7 +2038,7 @@ database_manager_->SetAllowlistLookupDetailsForUrl(url, /*match=*/true); ExpectPreClassificationChecks(url, &kFalse, &kFalse, nullptr, nullptr, nullptr); - NavigateAndCommit(url); + NavigateAndKeepLoading(web_contents(), url); WaitAndCheckPreClassificationChecks(); // Check that the clipboard histograms haven't been recorded yet. @@ -2132,7 +2082,7 @@ database_manager_->SetAllowlistLookupDetailsForUrl(url, /*match=*/true); ExpectPreClassificationChecks(url, &kFalse, &kFalse, nullptr, nullptr, nullptr); - NavigateAndCommit(url); + NavigateAndKeepLoading(web_contents(), url); WaitAndCheckPreClassificationChecks(); // Check that the clipboard histograms haven't been recorded yet. @@ -2178,7 +2128,7 @@ database_manager_->SetAllowlistLookupDetailsForUrl(url, /*match=*/true); ExpectPreClassificationChecks(url, &kFalse, &kFalse, nullptr, nullptr, nullptr); - NavigateAndCommit(url); + NavigateAndKeepLoading(web_contents(), url); WaitAndCheckPreClassificationChecks(); // Check that the clipboard histograms haven't been recorded yet. @@ -2219,7 +2169,7 @@ database_manager_->SetAllowlistLookupDetailsForUrl(url, /*match=*/true); ExpectPreClassificationChecks(url, &kFalse, &kFalse, nullptr, nullptr, nullptr); - NavigateAndCommit(url); + NavigateAndKeepLoading(web_contents(), url); WaitAndCheckPreClassificationChecks(); // Check that the clipboard histograms haven't been recorded yet. @@ -3813,7 +3763,7 @@ ExpectPreClassificationChecks(url, &kFalse, nullptr, nullptr, nullptr, &kFalse); EXPECT_CALL(*database_manager_.get(), CheckCsdAllowlistUrl(url, _)).Times(0); - NavigateAndCommit(url); + NavigateAndKeepLoading(web_contents(), url); WaitAndCheckPreClassificationChecks(); fake_phishing_detector_.CheckMessage(&url); } @@ -3829,7 +3779,7 @@ ExpectPreClassificationChecks(url, &kFalse, nullptr, nullptr, nullptr, &kFalse); EXPECT_CALL(*csd_service_, GetValidCachedResult(url, NotNull())).Times(0); - NavigateAndCommit(url); + NavigateAndKeepLoading(web_contents(), url); WaitAndCheckPreClassificationChecks(); fake_phishing_detector_.CheckMessage(&url); } @@ -3845,7 +3795,7 @@ ExpectPreClassificationChecks(url, &kFalse, nullptr, nullptr, nullptr, &kFalse); EXPECT_CALL(*csd_service_, AtPhishingReportLimit()).Times(0); - NavigateAndCommit(url); + NavigateAndKeepLoading(web_contents(), url); WaitAndCheckPreClassificationChecks(); fake_phishing_detector_.CheckMessage(&url); }
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/FuseboxSessionState.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/FuseboxSessionState.java index 897e710d..9b76365 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/FuseboxSessionState.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/FuseboxSessionState.java
@@ -10,6 +10,8 @@ import org.chromium.base.UserDataHost; import org.chromium.build.annotations.NullMarked; import org.chromium.build.annotations.Nullable; +import org.chromium.chrome.browser.omnibox.fusebox.ComposeboxQueryControllerBridge; +import org.chromium.chrome.browser.omnibox.fusebox.FuseboxAttachmentModelList; import org.chromium.chrome.browser.profiles.Profile; import org.chromium.components.embedder_support.util.UrlUtilities; import org.chromium.components.omnibox.AutocompleteInput; @@ -31,6 +33,8 @@ private AutocompleteInput mAutocompleteInput = new AutocompleteInput(); private @Nullable Profile mProfile; + private @Nullable ComposeboxQueryControllerBridge mComposeBoxQueryControllerBridge; + private @Nullable FuseboxAttachmentModelList mFuseboxAttachmentModelList; private boolean mIsActive; /** @@ -77,8 +81,13 @@ * @param input The initial AutocompleteInput for this session. */ @VisibleForTesting - public FuseboxSessionState(AutocompleteInput input) { + public FuseboxSessionState( + AutocompleteInput input, + @Nullable ComposeboxQueryControllerBridge composeboxQueryControllerBridge, + @Nullable FuseboxAttachmentModelList fuseboxAttachmentModelList) { mAutocompleteInput = input; + mComposeBoxQueryControllerBridge = composeboxQueryControllerBridge; + mFuseboxAttachmentModelList = fuseboxAttachmentModelList; } /** @@ -120,7 +129,37 @@ * session. */ public void setProfile(@Nullable Profile profile) { + if (mProfile == profile) return; + + // Profile has changed. This typically means either + // - profile was applied and we want to construct session objects, or + // - profile was removed and we want to destroy them. + // Technically this also supports profile swap, but that scenario shouldn't ever happen. mProfile = profile; + + if (mFuseboxAttachmentModelList != null) { + mFuseboxAttachmentModelList.destroy(); + mFuseboxAttachmentModelList = null; + } + + if (mComposeBoxQueryControllerBridge != null) { + mComposeBoxQueryControllerBridge.destroy(); + mComposeBoxQueryControllerBridge = null; + } + + // Abort now if we're not creating session controllers. + if (mProfile == null || !mIsActive) return; + + mComposeBoxQueryControllerBridge = + ComposeboxQueryControllerBridge.createForProfile(mProfile); + + if (mComposeBoxQueryControllerBridge != null) { + // Composebox Controller may not be instantiated if locale or policies prohibit AIM. + // Create attachments list only if allowed. + mFuseboxAttachmentModelList = new FuseboxAttachmentModelList(); + mFuseboxAttachmentModelList.setComposeboxQueryControllerBridge( + mComposeBoxQueryControllerBridge); + } } /** @@ -141,4 +180,18 @@ public AutocompleteInput getAutocompleteInput() { return mAutocompleteInput; } + + /** + * @return The current {@link ComposeboxQueryControllerBridge} for this session. + */ + public @Nullable ComposeboxQueryControllerBridge getComposeboxQueryControllerBridge() { + return mComposeBoxQueryControllerBridge; + } + + /** + * @return The current {@link FuseboxAttachmentModelList} for this session. + */ + public @Nullable FuseboxAttachmentModelList getFuseboxAttachmentModelList() { + return mFuseboxAttachmentModelList; + } }
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java index 9242548..50fa86e9 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediator.java
@@ -1876,6 +1876,13 @@ TextView tv = (TextView) view; return tv.getSelectionStart() == tv.getSelectionEnd() && tv.getSelectionEnd() == tv.getText().length(); + } else if (keyCode == KeyEvent.KEYCODE_DEL) { + if (mCurrentInput != null + && !TextUtils.isEmpty(mCurrentInput.getKeyword()) + && TextUtils.isEmpty(mUrlCoordinator.getTextWithoutAutocomplete())) { + mCurrentInput.setKeyword(null); + return true; + } } return false; }
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediatorTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediatorTest.java index 8da17a70..ec6d30f 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediatorTest.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/LocationBarMediatorTest.java
@@ -74,6 +74,7 @@ import org.chromium.chrome.browser.lens.LensController; import org.chromium.chrome.browser.locale.LocaleManager; import org.chromium.chrome.browser.multiwindow.MultiInstanceManager; +import org.chromium.chrome.browser.omnibox.fusebox.ComposeboxQueryControllerBridge; import org.chromium.chrome.browser.omnibox.fusebox.FuseboxCoordinator; import org.chromium.chrome.browser.omnibox.fusebox.FuseboxCoordinator.FuseboxState; import org.chromium.chrome.browser.omnibox.geo.GeolocationHeader; @@ -203,6 +204,7 @@ @Mock private AppBannerManager.Natives mAppBannerManagerJni; @Mock private NewTabPageDelegate mNewTabPageDelegate; @Mock private FuseboxCoordinator mFuseboxCoordinator; + @Mock private ComposeboxQueryControllerBridge mComposeboxBridge; @Captor private ArgumentCaptor<Runnable> mRunnableCaptor; @Captor private ArgumentCaptor<LoadUrlParams> mLoadUrlParamsCaptor; @@ -273,6 +275,8 @@ doReturn(mFuseboxStateSupplier).when(mFuseboxCoordinator).getFuseboxStateSupplier(); doReturn("").when(mUrlCoordinator).getTextWithAutocomplete(); + ComposeboxQueryControllerBridge.setInstanceForTesting(mComposeboxBridge); + AppBannerManagerJni.setInstanceForTesting(mAppBannerManagerJni); doReturn(mAppBannerManager) .when(mAppBannerManagerJni) @@ -765,6 +769,50 @@ } @Test + public void testOnKey_del_clearsKeyword() { + mMediator.onFinishNativeInitialization(); + mProfileSupplier.set(mProfile); + AutocompleteInput input = new AutocompleteInput(); + input.setKeyword("keyword"); + mMediator.beginInput(input); + + doReturn("").when(mUrlCoordinator).getTextWithoutAutocomplete(); + doReturn(KeyEvent.ACTION_DOWN).when(mKeyEvent).getAction(); + + assertTrue(mMediator.onKey(mView, KeyEvent.KEYCODE_DEL, mKeyEvent)); + assertNull(input.getKeyword()); + } + + @Test + public void testOnKey_del_withText() { + mMediator.onFinishNativeInitialization(); + mProfileSupplier.set(mProfile); + AutocompleteInput input = new AutocompleteInput(); + input.setKeyword("keyword"); + mMediator.beginInput(input); + + doReturn("text").when(mUrlCoordinator).getTextWithoutAutocomplete(); + doReturn(KeyEvent.ACTION_DOWN).when(mKeyEvent).getAction(); + + assertFalse(mMediator.onKey(mView, KeyEvent.KEYCODE_DEL, mKeyEvent)); + assertEquals("keyword", input.getKeyword()); + } + + @Test + public void testOnKey_del_noKeyword() { + mMediator.onFinishNativeInitialization(); + mProfileSupplier.set(mProfile); + AutocompleteInput input = new AutocompleteInput(); + mMediator.beginInput(input); + + doReturn("").when(mUrlCoordinator).getTextWithoutAutocomplete(); + doReturn(KeyEvent.ACTION_DOWN).when(mKeyEvent).getAction(); + + assertFalse(mMediator.onKey(mView, KeyEvent.KEYCODE_DEL, mKeyEvent)); + assertNull(input.getKeyword()); + } + + @Test public void testOnKey_escape() { doReturn(KeyEvent.ACTION_DOWN).when(mKeyEvent).getAction(); assertTrue(mMediator.handleEscPress());
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/OmniboxMetrics.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/OmniboxMetrics.java index 59dc3145..e50e6d8 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/OmniboxMetrics.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/OmniboxMetrics.java
@@ -499,15 +499,6 @@ break; default: - // May trigger if nev PageClassifications were added to - // third_party/metrics_proto/omnibox_event.proto file, - // but have not been reflected here. If that's the case, file a bug for the - // author of the new PageClassification. - // Last supported value: OTHER_ON_CCT. - assert false - : "b/40221519: Invalid page classification: " - + pageClass - + ". Please re-open bug, and attach captured stack trace."; break; }
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java index 455a357..a569e079 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
@@ -290,7 +290,8 @@ public boolean onKeyDown(int keyCode, KeyEvent event) { return ((KeyNavigationUtil.isEnter(event) || KeyNavigationUtil.isGoAnyDirection(event) - || KeyNavigationUtil.isTabNavigation(event)) + || KeyNavigationUtil.isTabNavigation(event) + || event.getKeyCode() == KeyEvent.KEYCODE_DEL) && (mKeyDownListener != null && mKeyDownListener.onKey(this, keyCode, event))) || super_onKeyDown(keyCode, event);
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarUnitTest.java index 5135993a..2daaa69 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarUnitTest.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBarUnitTest.java
@@ -751,7 +751,8 @@ KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_LEFT, - KeyEvent.KEYCODE_DPAD_RIGHT); + KeyEvent.KEYCODE_DPAD_RIGHT, + KeyEvent.KEYCODE_DEL); var listener = mock(View.OnKeyListener.class); mUrlBar.setKeyDownListener(listener); @@ -788,7 +789,8 @@ KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_NUMPAD_ENTER, KeyEvent.KEYCODE_DPAD_UP, - KeyEvent.KEYCODE_DPAD_DOWN); + KeyEvent.KEYCODE_DPAD_DOWN, + KeyEvent.KEYCODE_DEL); var listener = mock(View.OnKeyListener.class); mUrlBar.setKeyDownListener(listener);
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/ComposeboxQueryControllerBridge.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/ComposeboxQueryControllerBridge.java index 7cfdefd28..f4b1d65 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/ComposeboxQueryControllerBridge.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/ComposeboxQueryControllerBridge.java
@@ -4,11 +4,14 @@ package org.chromium.chrome.browser.omnibox.fusebox; +import androidx.annotation.VisibleForTesting; + import org.jni_zero.CalledByNative; import org.jni_zero.JniType; import org.jni_zero.NativeMethods; import org.chromium.base.Callback; +import org.chromium.base.ResettersForTesting; import org.chromium.base.supplier.MonotonicObservableSupplier; import org.chromium.base.supplier.ObservableSuppliers; import org.chromium.base.supplier.SettableMonotonicObservableSupplier; @@ -16,12 +19,13 @@ import org.chromium.build.annotations.Nullable; import org.chromium.chrome.browser.profiles.Profile; import org.chromium.chrome.browser.tab.Tab; -import org.chromium.components.contextual_search.FileUploadStatus; +import org.chromium.components.contextual_search.ContextUploadStatus; import org.chromium.components.contextual_search.InputState; import org.chromium.content_public.browser.WebContents; import org.chromium.url.GURL; import java.nio.ByteBuffer; +import java.util.Optional; /** * Bridge for native Composebox query controller functionality, allowing for management of a @@ -30,6 +34,9 @@ @SuppressWarnings("unused") @NullMarked public class ComposeboxQueryControllerBridge { + /** Instance to be used for testing - null value permitted to signify no controller. */ + @SuppressWarnings("NullableOptional") + private static @Nullable Optional<ComposeboxQueryControllerBridge> sInstanceForTesting; /** Observer for file upload status changes. */ interface FileUploadObserver { @@ -37,7 +44,7 @@ * @param token Unique string identifier for the file. * @param status The status of the file's upload. */ - void onFileUploadStatusChanged(String token, @FileUploadStatus int status); + void onFileUploadStatusChanged(String token, @ContextUploadStatus int status); } private long mNativeInstance; @@ -49,6 +56,8 @@ /** Create a new ComposeboxQueryControllerBridge using the given profile. */ public static @Nullable ComposeboxQueryControllerBridge createForProfile(Profile profile) { + if (sInstanceForTesting != null) return sInstanceForTesting.orElse(null); + ComposeboxQueryControllerBridge javaInstance = new ComposeboxQueryControllerBridge(); long nativeInstance = ComposeboxQueryControllerBridgeJni.get().init(profile, javaInstance); if (nativeInstance == 0L) return null; @@ -119,11 +128,11 @@ .addTabContextFromCache(mNativeInstance, tabId); } - void getAimUrl(GURL url, Callback<GURL> callback) { + public void getAimUrl(GURL url, Callback<GURL> callback) { ComposeboxQueryControllerBridgeJni.get().getAimUrl(mNativeInstance, url, callback); } - void getImageGenerationUrl(GURL url, Callback<GURL> callback) { + public void getImageGenerationUrl(GURL url, Callback<GURL> callback) { ComposeboxQueryControllerBridgeJni.get() .getImageGenerationUrl(mNativeInstance, url, callback); } @@ -167,8 +176,19 @@ return mInputStateSupplier; } + @VisibleForTesting + public static void setInstanceForTesting(@Nullable ComposeboxQueryControllerBridge instance) { + sInstanceForTesting = Optional.ofNullable(instance); + ResettersForTesting.register(ComposeboxQueryControllerBridge::resetInstanceForTesting); + } + + @VisibleForTesting + public static void resetInstanceForTesting() { + sInstanceForTesting = null; + } + @CalledByNative - void onFileUploadStatusChanged(String token, @FileUploadStatus int fileUploadStatus) { + void onFileUploadStatusChanged(String token, @ContextUploadStatus int fileUploadStatus) { if (mFileUploadObserver != null) { mFileUploadObserver.onFileUploadStatusChanged(token, fileUploadStatus); }
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxAttachmentModelList.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxAttachmentModelList.java index 1bb31326..0b44901b 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxAttachmentModelList.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxAttachmentModelList.java
@@ -19,7 +19,7 @@ import org.chromium.chrome.browser.omnibox.fusebox.ComposeboxQueryControllerBridge.FileUploadObserver; import org.chromium.chrome.browser.omnibox.fusebox.FuseboxAttachmentRecyclerViewAdapter.FuseboxAttachmentType; import org.chromium.chrome.browser.ui.theme.BrandedColorScheme; -import org.chromium.components.contextual_search.FileUploadStatus; +import org.chromium.components.contextual_search.ContextUploadStatus; import org.chromium.components.omnibox.OmniboxFeatures; import org.chromium.ui.modelutil.ListObservable.ListObserver; import org.chromium.ui.modelutil.MVCListAdapter.ListItem; @@ -196,7 +196,7 @@ } /** Release all resources and mark this instance ready for recycling. */ - void destroy() { + public void destroy() { setComposeboxQueryControllerBridge(null); mAttachmentUploadFailedListener = null; } @@ -364,15 +364,15 @@ } @Override - public void onFileUploadStatusChanged(String token, @FileUploadStatus int status) { + public void onFileUploadStatusChanged(String token, @ContextUploadStatus int status) { if (TextUtils.isEmpty(token)) return; FuseboxAttachment pendingAttachment = findAttachmentWithToken(token); if (pendingAttachment == null) return; switch (status) { - case FileUploadStatus.VALIDATION_FAILED: - case FileUploadStatus.UPLOAD_FAILED: - case FileUploadStatus.UPLOAD_EXPIRED: + case ContextUploadStatus.VALIDATION_FAILED: + case ContextUploadStatus.UPLOAD_FAILED: + case ContextUploadStatus.UPLOAD_EXPIRED: if (pendingAttachment.retryUpload( assumeNonNull(mComposeboxQueryControllerBridge))) { break; @@ -381,7 +381,7 @@ pendingAttachment.setUploadIsComplete(); remove(pendingAttachment, /* isFailure= */ true); break; - case FileUploadStatus.UPLOAD_SUCCESSFUL: + case ContextUploadStatus.UPLOAD_SUCCESSFUL: pendingAttachment.setUploadIsComplete(); int index = indexOf(pendingAttachment); mModelList.update(index, pendingAttachment);
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxAttachmentModelListUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxAttachmentModelListUnitTest.java index d91a6cf..ffd2422 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxAttachmentModelListUnitTest.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxAttachmentModelListUnitTest.java
@@ -37,7 +37,7 @@ import org.chromium.chrome.browser.omnibox.fusebox.FuseboxAttachmentRecyclerViewAdapter.FuseboxAttachmentType; import org.chromium.chrome.browser.omnibox.fusebox.FuseboxMetrics.FuseboxAttachmentButtonType; import org.chromium.chrome.browser.tab.Tab; -import org.chromium.components.contextual_search.FileUploadStatus; +import org.chromium.components.contextual_search.ContextUploadStatus; import org.chromium.components.omnibox.OmniboxFeatures; import org.chromium.content_public.browser.RenderWidgetHostView; import org.chromium.content_public.browser.WebContents; @@ -341,7 +341,7 @@ assertFalse(attachment.isUploadComplete()); mFuseboxAttachmentModelList.onFileUploadStatusChanged( - "uploaded-token", FileUploadStatus.UPLOAD_SUCCESSFUL); + "uploaded-token", ContextUploadStatus.UPLOAD_SUCCESSFUL); assertTrue(attachment.isUploadComplete()); verifyNoMoreInteractions(mComposeboxQueryControllerBridge); } @@ -392,9 +392,9 @@ assertFalse(uploadedAttachment.isUploadComplete()); mFuseboxAttachmentModelList.onFileUploadStatusChanged( - "pretokenized-token", FileUploadStatus.UPLOAD_SUCCESSFUL); + "pretokenized-token", ContextUploadStatus.UPLOAD_SUCCESSFUL); mFuseboxAttachmentModelList.onFileUploadStatusChanged( - "uploaded-token", FileUploadStatus.UPLOAD_FAILED); + "uploaded-token", ContextUploadStatus.UPLOAD_FAILED); assertTrue(uploadFailedNotified.get()); assertTrue(preTokenizedAttachment.isUploadComplete()); @@ -521,11 +521,11 @@ when(mComposeboxQueryControllerBridge.addTabContext(tab)).thenReturn("token2"); mFuseboxAttachmentModelList.onFileUploadStatusChanged( - "token", FileUploadStatus.VALIDATION_FAILED); + "token", ContextUploadStatus.VALIDATION_FAILED); assertEquals("token2", tabAttachment.getToken()); mFuseboxAttachmentModelList.onFileUploadStatusChanged( - "token2", FileUploadStatus.VALIDATION_FAILED); + "token2", ContextUploadStatus.VALIDATION_FAILED); assertTrue(mFuseboxAttachmentModelList.isEmpty()); } @@ -620,7 +620,7 @@ FuseboxAttachment attachment = createTestAttachment("test"); mFuseboxAttachmentModelList.add(attachment); mFuseboxAttachmentModelList.onFileUploadStatusChanged( - "token1", FileUploadStatus.UPLOAD_SUCCESSFUL); + "token1", ContextUploadStatus.UPLOAD_SUCCESSFUL); } }
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxCoordinator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxCoordinator.java index f0d67f7..3af33598 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxCoordinator.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxCoordinator.java
@@ -48,7 +48,6 @@ import org.chromium.ui.widget.AnchoredPopupWindow.HorizontalOrientation; import org.chromium.ui.widget.RectProvider; import org.chromium.ui.widget.ViewRectProvider; -import org.chromium.url.GURL; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -75,7 +74,6 @@ private final FuseboxAttachmentModelList mModelList; private final MonotonicObservableSupplier<TabModelSelector> mTabModelSelectorSupplier; private @Nullable FuseboxMediator mMediator; - private @Nullable ComposeboxQueryControllerBridge mComposeboxQueryControllerBridge; private @Nullable AutocompleteInput mInput; private boolean mDefaultSearchEngineIsGoogle = true; private TemplateUrlService mTemplateUrlService; @@ -177,17 +175,6 @@ mMediator = null; } - if (mComposeboxQueryControllerBridge != null) { - mComposeboxQueryControllerBridge.destroy(); - } - mComposeboxQueryControllerBridge = - ComposeboxQueryControllerBridge.createForProfile(profile); - AutocompleteController.getForProfile(profile) - .setComposeboxQueryControllerBridge(mComposeboxQueryControllerBridge); - if (mComposeboxQueryControllerBridge == null) return; - - // Set the bridge for the model list to enable tight coupling. - mModelList.setComposeboxQueryControllerBridge(mComposeboxQueryControllerBridge); mMediator = new FuseboxMediator( mContext, @@ -195,15 +182,12 @@ mWindowAndroid, mModel, assumeNonNull(mViewHolder), - mModelList, mTabModelSelectorSupplier, - mComposeboxQueryControllerBridge, mFuseboxStateSupplier, mSnackbarManager); if (mLastBrandedColorScheme != null) { mMediator.updateVisualsForState(mLastBrandedColorScheme); } - mModelList.setAttachmentUploadFailedListener(mMediator::onAttachmentUploadFailed); } public void destroy() { @@ -217,10 +201,6 @@ mTemplateUrlService.removeObserver(this); } mModelList.destroy(); - if (mComposeboxQueryControllerBridge != null) { - mComposeboxQueryControllerBridge.destroy(); - mComposeboxQueryControllerBridge = null; - } if (mViewportRectProvider != null) { mViewportRectProvider.destroy(); } @@ -244,6 +224,9 @@ * through the endInput() (valid -> valid). This is the case for tab switching. */ public void beginInput(FuseboxSessionState session) { + // Abort early if there is no composebox. + if (session.getComposeboxQueryControllerBridge() == null) return; + // We can't do inclusive check due to missing `isPhone()` case in `DeviceInfo`. // Additionally these values may change at runtime, e.g. if the user starts Chrome on phone // and moves to Android Auto. @@ -271,12 +254,12 @@ return; } - // Erase all attachments to avoid leaking them to other sessions when user switches tabs. - // TODO(crbug.com/474616308): remove when proper session persistence is available. - mModelList.clear(); + // TODO(crbug.com/474616308): move to FuseboxSessionState. + AutocompleteController.getForProfile(assumeNonNull(session.getProfile())) + .setComposeboxQueryControllerBridge(session.getComposeboxQueryControllerBridge()); mInput = session.getAutocompleteInput(); - mMediator.beginInput(mInput); + mMediator.beginInput(session); FuseboxMetrics.notifyOmniboxSessionStarted(); } @@ -301,24 +284,6 @@ } } - /** Returns the URL associated with the current AIM session. */ - public void getAimUrl(GURL url, Callback<GURL> callback) { - if (mMediator == null) { - callback.onResult(GURL.emptyGURL()); - return; - } - mMediator.getAimUrl(url, callback); - } - - /** Returns the URL associated with the current image generation session. */ - public void getImageGenerationUrl(GURL url, Callback<GURL> callback) { - if (mMediator == null) { - callback.onResult(GURL.emptyGURL()); - return; - } - mMediator.getImageGenerationUrl(url, callback); - } - public PropertyModel getModelForTesting() { return mModel; }
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxCoordinatorUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxCoordinatorUnitTest.java index db5c389a..152a00b 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxCoordinatorUnitTest.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxCoordinatorUnitTest.java
@@ -10,7 +10,6 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.lenient; @@ -83,7 +82,7 @@ @Mock private AutocompleteController mAutocompleteController; @Mock private AutocompleteController.Natives mControllerJniMock; - @Mock private ComposeboxQueryControllerBridge.Natives mComposeboxController; + @Mock private ComposeboxQueryControllerBridge mComposebox; @Mock private FuseboxMediator mMediator; @Mock private TabModelSelector mTabModelSelector; @Mock private TabModel mTabModel; @@ -110,8 +109,6 @@ @Before public void setUp() { - ComposeboxQueryControllerBridgeJni.setInstanceForTesting(mComposeboxController); - AutocompleteControllerJni.setInstanceForTesting(mControllerJniMock); lenient().doReturn(mAutocompleteController).when(mControllerJniMock).getForProfile(any()); @@ -151,7 +148,7 @@ } private FuseboxSessionState createSession() { - return new FuseboxSessionState(mAutocompleteInput); + return new FuseboxSessionState(mAutocompleteInput, mComposebox, null); } @After @@ -166,9 +163,6 @@ // Start with a default state. mCoordinator.setMediatorForTesting(null); - doReturn(/* nativeInstance= */ 1L) - .when(mComposeboxController) - .init(any(Profile.class), any(ComposeboxQueryControllerBridge.class)); mProfileSupplier.set(mProfile); assertNotNull(mCoordinator.getMediatorForTesting()); assertNotEquals(mMediator, mCoordinator.getMediatorForTesting()); @@ -178,13 +172,12 @@ @EnableFeatures(OmniboxFeatureList.OMNIBOX_MULTIMODAL_INPUT) public void testOnProfileAvailable_featureEnabled_noBridge() { // Start with a default state. - mCoordinator.setMediatorForTesting(null); - - doReturn(/* nativeInstance= */ 0L) - .when(mComposeboxController) - .init(any(Profile.class), any(ComposeboxQueryControllerBridge.class)); + mCoordinator.endInput(); + mComposebox = null; + clearInvocations(mMediator); mProfileSupplier.set(mProfile); - assertNull(mCoordinator.getMediatorForTesting()); + mCoordinator.beginInput(createSession()); + verify(mMediator, never()).beginInput(any()); } @Test @@ -194,8 +187,6 @@ mCoordinator.setMediatorForTesting(null); mProfileSupplier.set(mProfile); - verify(mComposeboxController, never()) - .init(any(Profile.class), any(ComposeboxQueryControllerBridge.class)); assertNull(mCoordinator.getMediatorForTesting()); } @@ -205,9 +196,6 @@ // Start with a default state. mCoordinator.setMediatorForTesting(null); - doReturn(/* nativeInstance= */ 1L) - .when(mComposeboxController) - .init(any(Profile.class), any(ComposeboxQueryControllerBridge.class)); mProfileSupplier.set(mProfile); assertNotNull(mCoordinator.getMediatorForTesting()); assertNotEquals(mMediator, mCoordinator.getMediatorForTesting()); @@ -243,7 +231,7 @@ // Mediator set by setUp(). mCoordinator.beginInput(createSession()); - verify(mMediator).beginInput(mAutocompleteInput); + verify(mMediator).beginInput(any()); mCoordinator.endInput(); verify(mMediator).endInput(); @@ -279,7 +267,7 @@ mCoordinator.beginInput(createSession()); boolean shouldBeVisible = supportedPageClassifications.contains(pageClass); - verify(mMediator, times(shouldBeVisible ? 1 : 0)).beginInput(mAutocompleteInput); + verify(mMediator, times(shouldBeVisible ? 1 : 0)).beginInput(any()); mCoordinator.endInput(); } @@ -299,27 +287,24 @@ @Test @EnableFeatures(OmniboxFeatureList.OMNIBOX_MULTIMODAL_INPUT) public void testNtpAiModeButtonPress() { - doReturn(/* nativeInstance= */ 1L) - .when(mComposeboxController) - .init(any(Profile.class), any(ComposeboxQueryControllerBridge.class)); mProfileSupplier.set(mProfile); RobolectricUtil.runAllBackgroundAndUi(); mAutocompleteInput.setRequestType(AutocompleteRequestType.AI_MODE); mCoordinator.beginInput(createSession()); - verify(mMediator).beginInput(mAutocompleteInput); + verify(mMediator).beginInput(any()); } @Test @EnableFeatures({OmniboxFeatureList.OMNIBOX_MULTIMODAL_INPUT}) public void createImageButtonVisibility_regularProfile() { - doReturn(/* nativeInstance= */ 1L) - .when(mComposeboxController) - .init(any(Profile.class), any(ComposeboxQueryControllerBridge.class)); - doReturn(true).when(mComposeboxController).isCreateImagesEligible(anyLong()); - + // Restart session with different eligibility. + mCoordinator.endInput(); + doReturn(true).when(mComposebox).isCreateImagesEligible(); OmniboxFeatures.sShowImageGenerationButtonInIncognito.setForTesting(false); mProfileSupplier.set(mProfile); + mCoordinator.beginInput(createSession()); + assertTrue( mCoordinator .getModelForTesting()
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxMediator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxMediator.java index 6c9c541..d881db6 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxMediator.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxMediator.java
@@ -32,7 +32,9 @@ import org.chromium.build.annotations.Nullable; import org.chromium.chrome.browser.feature_engagement.TrackerFactory; import org.chromium.chrome.browser.flags.ChromeFeatureList; +import org.chromium.chrome.browser.omnibox.FuseboxSessionState; import org.chromium.chrome.browser.omnibox.R; +import org.chromium.chrome.browser.omnibox.fusebox.FuseboxAttachmentModelList.FuseboxAttachmentChangeListener; import org.chromium.chrome.browser.omnibox.fusebox.FuseboxAttachmentRecyclerViewAdapter.FuseboxAttachmentType; import org.chromium.chrome.browser.omnibox.fusebox.FuseboxCoordinator.FuseboxState; import org.chromium.chrome.browser.omnibox.fusebox.FuseboxMetrics.AiModeActivationSource; @@ -61,7 +63,6 @@ import org.chromium.ui.modelutil.MVCListAdapter; import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.permissions.AndroidPermissionDelegate; -import org.chromium.url.GURL; import java.io.ByteArrayOutputStream; import java.util.ArrayList; @@ -71,21 +72,24 @@ /** Mediator for the Fusebox component. */ @NullMarked -public class FuseboxMediator { +public class FuseboxMediator implements FuseboxAttachmentChangeListener { private final Context mContext; private final Profile mProfile; private final WindowAndroid mWindowAndroid; private final AndroidPermissionDelegate mPermissionDelegate; private final PropertyModel mModel; private final FuseboxPopup mPopup; - private final FuseboxAttachmentModelList mModelList; + private final FuseboxViewHolder mViewHolder; private final MonotonicObservableSupplier<TabModelSelector> mTabModelSelectorSupplier; - private final ComposeboxQueryControllerBridge mComposeboxQueryControllerBridge; private final SettableNonNullObservableSupplier<@FuseboxState Integer> mFuseboxStateSupplier; private final Callback<@AutocompleteRequestType Integer> mOnAutocompleteRequestTypeChanged = this::onAutocompleteRequestTypeChanged; private final SnackbarManager mSnackbarManager; private final Snackbar mAttachmentUploadFailedSnackbar; + private @BrandedColorScheme int mBrandedColorScheme = BrandedColorScheme.APP_DEFAULT; + private @Nullable AutocompleteInput mInput; + private @Nullable FuseboxAttachmentModelList mModelList; + private @Nullable ComposeboxQueryControllerBridge mComposeboxQueryControllerBridge; private final ListObserver<Void> mListObserver = new ListObserver<>() { @Override @@ -98,7 +102,6 @@ onAttachmentsChanged(); } }; - private @Nullable AutocompleteInput mInput; FuseboxMediator( Context context, @@ -106,9 +109,7 @@ WindowAndroid windowAndroid, PropertyModel model, FuseboxViewHolder viewHolder, - FuseboxAttachmentModelList modelList, MonotonicObservableSupplier<TabModelSelector> tabModelSelectorSupplier, - ComposeboxQueryControllerBridge composeBoxQueryControllerBridge, SettableNonNullObservableSupplier<@FuseboxState Integer> fuseboxStateSupplier, SnackbarManager snackbarManager) { mContext = context; @@ -117,9 +118,8 @@ mPermissionDelegate = windowAndroid; mModel = model; mPopup = viewHolder.popup; - mModelList = modelList; + mViewHolder = viewHolder; mTabModelSelectorSupplier = tabModelSelectorSupplier; - mComposeboxQueryControllerBridge = composeBoxQueryControllerBridge; mFuseboxStateSupplier = fuseboxStateSupplier; mSnackbarManager = snackbarManager; @@ -146,8 +146,23 @@ mModel.set( FuseboxProperties.POPUP_ATTACH_TAB_PICKER_VISIBLE, ChromeFeatureList.sChromeItemPickerUi.isEnabled()); - mModel.set(FuseboxProperties.POPUP_ATTACH_CAMERA_VISIBLE, true); - mModel.set(FuseboxProperties.POPUP_ATTACH_GALLERY_VISIBLE, true); + } + + public void destroy() { + endInput(); + } + + @EnsuresNonNullIf( + value = {"mInput", "mModelList", "mComposeboxQueryControllerBridge"}, + result = true) + private boolean isInInputSession() { + return mInput != null && mModelList != null && mComposeboxQueryControllerBridge != null; + } + + private void setController(@Nullable ComposeboxQueryControllerBridge controller) { + mComposeboxQueryControllerBridge = controller; + if (mComposeboxQueryControllerBridge == null) return; + mModel.set( FuseboxProperties.POPUP_ATTACH_FILE_VISIBLE, mComposeboxQueryControllerBridge.isPdfUploadEligible()); @@ -157,39 +172,57 @@ FuseboxProperties.POPUP_TOOL_CREATE_IMAGE_VISIBLE, mComposeboxQueryControllerBridge.isCreateImagesEligible() && (OmniboxFeatures.sShowImageGenerationButtonInIncognito.getValue() - || !profile.isIncognitoBranded())); - - mModelList.addObserver(mListObserver); - onAttachmentsChanged(); + || !mProfile.isIncognitoBranded())); } - public void destroy() { - mModelList.removeObserver(mListObserver); - endInput(); - } + private void setModelList(@Nullable FuseboxAttachmentModelList modelList) { + if (mModelList == modelList) return; - @EnsuresNonNullIf("mInput") - private boolean isInInputSession() { - return mInput != null; + if (mModelList != null) { + mModelList.removeObserver(mListObserver); + mModelList.removeAttachmentChangeListener(this); + mModelList.setAttachmentUploadFailedListener(null); + } + + mModelList = modelList; + + if (mModelList != null) { + var adapter = mModelList.getAdapter(); + mViewHolder.attachmentsView.setAdapter(adapter); + mModel.set(FuseboxProperties.ADAPTER, adapter); + mModelList.setAttachmentUploadFailedListener(this::onAttachmentUploadFailed); + mModelList.updateVisualsForState(mBrandedColorScheme); + mModelList.addAttachmentChangeListener(this); + mModelList.addObserver(mListObserver); + onAttachmentsChanged(); + } else { + // need a safe fallback. + mViewHolder.attachmentsView.setAdapter(null); + mModel.set(FuseboxProperties.ADAPTER, null); + mModel.set(FuseboxProperties.ATTACHMENTS_VISIBLE, false); + } } /** * Called when the user begins interacting with the Omnibox. * - * @param input The input state for the new session. The input may be replaced without going + * @param session The input state for the new session. The input may be replaced without going * through the endInput() (valid -> valid). This is the case for tab switching. */ - /* package */ void beginInput(AutocompleteInput input) { - setAutocompleteInput(input); + /* package */ void beginInput(FuseboxSessionState session) { + setAutocompleteInput(session.getAutocompleteInput()); + setController(session.getComposeboxQueryControllerBridge()); + setModelList(session.getFuseboxAttachmentModelList()); setToolbarVisible(true); } /** Called when the user stops interacting with the Omnibox. */ /* package */ void endInput() { - mModelList.clear(); mPopup.dismiss(); setToolbarVisible(false); setAutocompleteInput(null); + setController(null); + setModelList(null); } private void setAutocompleteInput(@Nullable AutocompleteInput input) { @@ -228,7 +261,9 @@ /** Apply a variant of the branded color scheme to Fusebox UI elements */ /*package */ void updateVisualsForState(@BrandedColorScheme int brandedColorScheme) { + mBrandedColorScheme = brandedColorScheme; mModel.set(FuseboxProperties.COLOR_SCHEME, brandedColorScheme); + if (mModelList == null) return; mModelList.updateVisualsForState(brandedColorScheme); } @@ -304,25 +339,9 @@ && mInput.getRequestType() == AutocompleteRequestType.SEARCH); } - /** - * @param url The search URL to get the AIM analog of. - * @param callback The callback to run with the URL for the AIM service. - */ - void getAimUrl(GURL url, Callback<GURL> callback) { - mComposeboxQueryControllerBridge.getAimUrl(url, callback); - } - - /** - * @param url The search URL to get the Image generator analog of. - * @param callback The callback to run with the URL for the image generation service. - */ - void getImageGenerationUrl(GURL url, Callback<GURL> callback) { - mComposeboxQueryControllerBridge.getImageGenerationUrl(url, callback); - } - @VisibleForTesting void onToggleAttachmentsPopup() { - if (mPopup.isShowing()) { + if (!isInInputSession() || mPopup.isShowing()) { mPopup.dismiss(); } else { updateModelForCurrentTab(); @@ -337,6 +356,7 @@ } private void updateModelForCurrentTab() { + if (!isInInputSession()) return; var tabSelector = mTabModelSelectorSupplier.get(); var shouldShowCurrentTab = tabSelector != null @@ -370,6 +390,7 @@ } private void onAddCurrentTab(Tab tab) { + if (!isInInputSession()) return; FuseboxMetrics.notifyAttachmentButtonUsed(FuseboxAttachmentButtonType.CURRENT_TAB); maybeActivateAiMode(AiModeActivationSource.IMPLICIT); @@ -417,6 +438,7 @@ } private void onAttachmentsChanged() { + if (!isInInputSession()) return; mModel.set(FuseboxProperties.ATTACHMENTS_VISIBLE, !mModelList.isEmpty()); mModel.set( FuseboxProperties.POPUP_TOOL_CREATE_IMAGE_ENABLED, @@ -425,6 +447,7 @@ } private boolean areAttachmentsCompatibleWithCreateImage() { + if (!isInInputSession()) return false; int imageCount = 0; for (MVCListAdapter.ListItem listItem : mModelList) { if (listItem.type == FuseboxAttachmentType.ATTACHMENT_FILE) { @@ -443,6 +466,7 @@ @VisibleForTesting void onTabPickerClicked() { mPopup.dismiss(); + if (!isInInputSession()) return; FuseboxMetrics.notifyAttachmentButtonUsed(FuseboxAttachmentButtonType.TAB_PICKER); if (isMaxAttachmentCountReached(FuseboxAttachmentType.ATTACHMENT_TAB)) return; @@ -468,6 +492,7 @@ } void onTabPickerResult(int resultCode, @Nullable Intent data) { + if (!isInInputSession()) return; if (resultCode != Activity.RESULT_OK || data == null || data.getExtras() == null) return; ArrayList<Integer> tabIds = data.getIntegerArrayListExtra(ChromeItemPickerExtras.EXTRA_ATTACHMENT_TAB_IDS); @@ -491,6 +516,7 @@ */ @VisibleForTesting public void updateCurrentlyAttachedTabs(Set<Integer> newlySelectedTabIds) { + if (!isInInputSession()) return; TabModelSelector tabModelSelector = mTabModelSelectorSupplier.get(); if (tabModelSelector == null) return; @@ -630,6 +656,7 @@ mWindowAndroid.showCancelableIntent( intent, (resultCode, data) -> { + if (!isInInputSession()) return; if (resultCode != Activity.RESULT_OK || data == null) return; try (var batchToken = mModelList.beginBatchEdit()) { @@ -665,6 +692,7 @@ i, (resultCode, data) -> { if (resultCode != Activity.RESULT_OK || data == null) return; + if (!isInInputSession()) return; try (var batchToken = mModelList.beginBatchEdit()) { var uris = extractUrisFromResult(data);
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxMediatorUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxMediatorUnitTest.java index 1a01e62..d722b6d 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxMediatorUnitTest.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/fusebox/FuseboxMediatorUnitTest.java
@@ -61,6 +61,7 @@ import org.chromium.base.test.RobolectricUtil; import org.chromium.build.annotations.Nullable; import org.chromium.chrome.browser.feature_engagement.TrackerFactory; +import org.chromium.chrome.browser.omnibox.FuseboxSessionState; import org.chromium.chrome.browser.omnibox.R; import org.chromium.chrome.browser.omnibox.fusebox.FuseboxAttachmentRecyclerViewAdapter.FuseboxAttachmentType; import org.chromium.chrome.browser.omnibox.fusebox.FuseboxCoordinator.FuseboxState; @@ -186,12 +187,14 @@ mWindowAndroid, mModel, mViewHolder, - mAttachments, mTabModelSelectorSupplier, - mComposeboxQueryControllerBridge, mFuseboxStateSupplier, mSnackbarManager); - mMediator.beginInput(mInput); + mMediator.beginInput(createSession()); + } + + private FuseboxSessionState createSession() { + return new FuseboxSessionState(mInput, mComposeboxQueryControllerBridge, mAttachments); } private void addTabAttachment(Tab tab) {
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java index d49a47d..f1a35ca 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediator.java
@@ -118,6 +118,7 @@ mDeferredIMEWindowInsetApplicationCallback; private final OmniboxSuggestionsDropdownEmbedder mEmbedder; private @Nullable AutocompleteInput mAutocompleteInput; + private @Nullable FuseboxSessionState mSessionState; private final boolean mForcePhoneStyleOmnibox; private final Callback<@ControlsPosition Integer> mToolbarPositionChangedCallback = this::onToolbarPositionChanged; @@ -439,9 +440,10 @@ * through the endInput() (valid -> valid). This is the case for tab switching. */ void beginInput(FuseboxSessionState session) { - boolean alreadyInInput = mAutocompleteInput != null; + boolean alreadyInInput = mSessionState != null; cancelAutocompleteRequests(); setAutocompleteInput(session.getAutocompleteInput()); + mSessionState = session; if (!alreadyInInput) { // Propagate the information about omnibox session state change to all the processors @@ -495,7 +497,7 @@ */ void endInput() { // Session already inactive - stop. - if (mAutocompleteInput == null) return; + if (!isInInputSession()) return; // Propagate the information about omnibox session state change to all the processors first. // Processors need this for accounting purposes. @@ -536,6 +538,7 @@ // a consequence the omnibox is unfocused). clearSuggestions(); setAutocompleteInput(null); + mSessionState = null; } private void setAutocompleteInput(@Nullable AutocompleteInput input) { @@ -1172,6 +1175,8 @@ long inputStart, boolean openInNewTab, boolean openInNewWindow) { + if (!isInInputSession()) return; + try (TraceEvent e = TraceEvent.scoped("AutocompleteMediator.loadUrlFromOmniboxMatch")) { OmniboxMetrics.recordFocusToOpenTime( System.currentTimeMillis() @@ -1217,11 +1222,13 @@ finalTransition); }; - switch (assumeNonNull(mAutocompleteInput).getRequestType()) { + switch (mAutocompleteInput.getRequestType()) { case AutocompleteRequestType.AI_MODE -> - mFuseboxCoordinator.getAimUrl(url, onUrlReady); + assumeNonNull(mSessionState.getComposeboxQueryControllerBridge()) + .getAimUrl(url, onUrlReady); case AutocompleteRequestType.IMAGE_GENERATION -> - mFuseboxCoordinator.getImageGenerationUrl(url, onUrlReady); + assumeNonNull(mSessionState.getComposeboxQueryControllerBridge()) + .getImageGenerationUrl(url, onUrlReady); default -> onUrlReady.onResult(url); } } @@ -1694,9 +1701,11 @@ * @return Whether there is currently an active omnibox session. An active session is defined by * the presence of an {@link AutocompleteInput} and the activity window having focus. */ - @EnsuresNonNullIf("mAutocompleteInput") + @EnsuresNonNullIf( + value = {"mAutocompleteInput", "mSessionState"}, + result = true) private boolean isInInputSession() { - return mAutocompleteInput != null && mActivityWindowFocused; + return mSessionState != null && mAutocompleteInput != null && mActivityWindowFocused; } @Override
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediatorUnitTest.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediatorUnitTest.java index b9d240ad..da91c90 100644 --- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediatorUnitTest.java +++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediatorUnitTest.java
@@ -65,6 +65,7 @@ import org.chromium.chrome.browser.omnibox.NewTabPageDelegate; import org.chromium.chrome.browser.omnibox.OmniboxMetrics; import org.chromium.chrome.browser.omnibox.UrlBarEditingTextStateProvider; +import org.chromium.chrome.browser.omnibox.fusebox.ComposeboxQueryControllerBridge; import org.chromium.chrome.browser.omnibox.fusebox.FuseboxCoordinator; import org.chromium.chrome.browser.omnibox.fusebox.FuseboxCoordinator.FuseboxState; import org.chromium.chrome.browser.omnibox.styles.OmniboxResourceProvider; @@ -139,6 +140,7 @@ private @Mock DeferredIMEWindowInsetApplicationCallback mDeferredImeCallback; private @Mock FuseboxCoordinator mFuseboxCoordinator; private @Mock PreloadingFeatureMap mPreloadingFeatureMap; + private @Mock ComposeboxQueryControllerBridge mComposeboxQueryControllerBridge; private @Captor ArgumentCaptor<OmniboxLoadUrlParams> mOmniboxLoadUrlParamsCaptor; private @Mock CachedZeroSuggestionsManager.OverridesForTesting mMockCachedZeroSuggestionsManager; @@ -279,7 +281,7 @@ autocompleteInput.setPageUrl(url); autocompleteInput.setPageTitle(title); autocompleteInput.setPageClassification(pageClassification); - return new FuseboxSessionState(autocompleteInput); + return new FuseboxSessionState(autocompleteInput, mComposeboxQueryControllerBridge, null); } private FuseboxSessionState createEmptySession() { @@ -1685,7 +1687,7 @@ ((Callback<GURL>) invocation.getArgument(1)).onResult(url); return null; }) - .when(mFuseboxCoordinator) + .when(mComposeboxQueryControllerBridge) .getAimUrl(any(), any()); AutocompleteMatch defaultMatch = @@ -1730,7 +1732,7 @@ ((Callback<GURL>) invocation.getArgument(1)).onResult(url2); return null; }) - .when(mFuseboxCoordinator) + .when(mComposeboxQueryControllerBridge) .getImageGenerationUrl(any(), any()); AutocompleteMatch defaultMatch =
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarOverlayMediator.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarOverlayMediator.java index 524746b..2d993c6 100644 --- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarOverlayMediator.java +++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarOverlayMediator.java
@@ -583,7 +583,8 @@ private void applyContentOffsetToModel(float contentOffset) { if (BrowserControlsUtils.isTopControlsRefactorOffsetEnabled() - && getControlsPosition() == ControlsPosition.TOP) { + && getControlsPosition() == ControlsPosition.TOP + && !mIsVisibilityManuallyControlled) { contentOffset = INVALID_CONTENT_OFFSET; } mModel.set(TopToolbarOverlayProperties.LEGACY_CONTENT_OFFSET, contentOffset);
diff --git a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarOverlayMediatorTest.java b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarOverlayMediatorTest.java index 3d3b33f9..1c56953 100644 --- a/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarOverlayMediatorTest.java +++ b/chrome/browser/ui/android/toolbar/java/src/org/chromium/chrome/browser/toolbar/top/TopToolbarOverlayMediatorTest.java
@@ -538,6 +538,42 @@ ChromeFeatureList.TOP_CONTROLS_REFACTOR, ChromeFeatureList.TOP_CONTROLS_REFACTOR_V2 }) + public void testContentOffset_topControlsRefactorEnabled_manuallyControlled() { + mMediator.setVisibilityManuallyControlledForTesting(true); + + int height = 150; + doReturn(height).when(mBrowserControlsStateProvider).getTopControlsHeight(); + doReturn(ControlsPosition.TOP).when(mBrowserControlsStateProvider).getControlsPosition(); + mBrowserControlsObserverCaptor.getValue().onControlsPositionChanged(ControlsPosition.TOP); + + // When requestNewFrame is false, applyContentOffsetToModel receives getTopControlsHeight(). + mBrowserControlsObserverCaptor + .getValue() + .onControlsOffsetChanged( + 0, 0, false, 0, 0, false, /* requestNewFrame= */ false, false); + assertEquals( + (float) height, + mModel.get(TopToolbarOverlayProperties.LEGACY_CONTENT_OFFSET), + MathUtils.EPSILON); + + // When requestNewFrame is true, applyContentOffsetToModel receives getContentOffset(). + int contentOffset = 200; + doReturn(contentOffset).when(mBrowserControlsStateProvider).getContentOffset(); + mBrowserControlsObserverCaptor + .getValue() + .onControlsOffsetChanged( + 0, 0, false, 0, 0, false, /* requestNewFrame= */ true, false); + assertEquals( + (float) contentOffset, + mModel.get(TopToolbarOverlayProperties.LEGACY_CONTENT_OFFSET), + MathUtils.EPSILON); + } + + @Test + @EnableFeatures({ + ChromeFeatureList.TOP_CONTROLS_REFACTOR, + ChromeFeatureList.TOP_CONTROLS_REFACTOR_V2 + }) public void testContentOffset_topControlsRefactorEnabled_ControlsAtBottom() { float height = 700.0f; mMediator.setViewportHeight(height);
diff --git a/chrome/browser/ui/views/tabs/dragging/tab_drag_controller_interactive_uitest.cc b/chrome/browser/ui/views/tabs/dragging/tab_drag_controller_interactive_uitest.cc index 22a7dd83..f68709b 100644 --- a/chrome/browser/ui/views/tabs/dragging/tab_drag_controller_interactive_uitest.cc +++ b/chrome/browser/ui/views/tabs/dragging/tab_drag_controller_interactive_uitest.cc
@@ -3804,10 +3804,10 @@ } // Creates a browser with four tabs. The first two tabs belong in Tab Group 1. -// Dragging the collapsed group header of Tab Group 1 will result in Tab Group 1 -// expanding. +// Dragging the collapsed group header of Tab Group 1 will not expand the +// group. IN_PROC_BROWSER_TEST_P(DetachToBrowserTabDragControllerTest, - DragCollapsedGroupHeaderExpandsGroup) { + DragCollapsedGroupHeaderRemainsCollapsed) { ASSERT_TRUE(browser()->tab_strip_model()->SupportsTabGroups()); TabStrip* tab_strip = GetTabStripForBrowser(browser()); @@ -3825,14 +3825,14 @@ ASSERT_EQ(4, model->count()); ASSERT_EQ(2u, group_model->GetTabGroup(group)->ListTabs().length()); - // Drag group1, this should expand the group. + // Drag group1, group should remain collapsed. ASSERT_TRUE(PressInputAtCenter(tab_strip->group_header(group))); ASSERT_TRUE(DragInputToCenter(tab_strip->tab_at(1))); ASSERT_TRUE(TabDragController::IsActive()); - EXPECT_FALSE(model->IsGroupCollapsed(group)); + EXPECT_TRUE(model->IsGroupCollapsed(group)); ASSERT_TRUE(ReleaseInput()); StopAnimating(tab_strip); - EXPECT_FALSE(model->IsGroupCollapsed(group)); + EXPECT_TRUE(model->IsGroupCollapsed(group)); } #if BUILDFLAG(IS_MAC)
diff --git a/chrome/browser/ui/views/tabs/tab_group_header_interactive_uitest.cc b/chrome/browser/ui/views/tabs/tab_group_header_interactive_uitest.cc index 52f45d8..7b7b2220 100644 --- a/chrome/browser/ui/views/tabs/tab_group_header_interactive_uitest.cc +++ b/chrome/browser/ui/views/tabs/tab_group_header_interactive_uitest.cc
@@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/test/bind.h" #include "chrome/browser/data_sharing/data_sharing_service_factory.h" #include "chrome/browser/tab_group_sync/tab_group_sync_service_factory.h" #include "chrome/browser/ui/browser_element_identifiers.h" @@ -29,6 +30,7 @@ #include "ui/gfx/codec/png_codec.h" #include "ui/gfx/image/image_skia_operations.h" #include "ui/gfx/image/image_unittest_util.h" +#include "ui/views/interaction/element_tracker_views.h" #include "ui/views/interaction/polling_view_observer.h" class TabGroupHeaderInteractiveUiTest @@ -123,3 +125,28 @@ ->ShouldShowAttentionIndicator()); })); } + +IN_PROC_BROWSER_TEST_F(TabGroupHeaderInteractiveUiTest, DragCollapsedGroup) { + CreateTabGroup({CreateTab()}); + + RunTestSequence( + WaitForShow(kTabGroupHeaderElementId), FinishTabstripAnimations(), + PollViewProperty(kTabGroupCollapsedState, kTabGroupHeaderElementId, + &TabGroupHeader::is_collapsed_for_testing), + // Collapse the group + MoveMouseTo(kTabGroupHeaderElementId), ClickMouse(ui_controls::LEFT), + WaitForState(kTabGroupCollapsedState, true), FinishTabstripAnimations(), + // Drag the group header. We drag it a bit to the right. + MoveMouseTo(kTabGroupHeaderElementId), + DragMouseTo(kTabGroupHeaderElementId, + base::BindLambdaForTesting([](ui::TrackedElement* el) { + return el->AsA<views::TrackedElementViews>() + ->view() + ->GetBoundsInScreen() + .CenterPoint() + + gfx::Vector2d(50, 0); + })), + // Verify it is still collapsed + CheckViewProperty(kTabGroupHeaderElementId, + &TabGroupHeader::is_collapsed_for_testing, true)); +}
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc index 670e7c95..be0b9d0 100644 --- a/chrome/browser/ui/views/tabs/tab_strip.cc +++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -1505,6 +1505,13 @@ // If the tab that is about to be selected is in a collapsed group, // automatically expand the group. if (IsGroupCollapsed(new_group)) { + // If the group is being dragged, do not expand it. + if (drag_context_->GetDragController() && + drag_context_->GetDragController()->group_header_id() == + new_group) { + continue; + } + ToggleTabGroupCollapsedState( new_group, ToggleTabGroupCollapsedStateOrigin::kTabsSelected); }
diff --git a/chrome/browser/ui/views/toolbar/DEPS b/chrome/browser/ui/views/toolbar/DEPS new file mode 100644 index 0000000..f503dd6 --- /dev/null +++ b/chrome/browser/ui/views/toolbar/DEPS
@@ -0,0 +1,3 @@ +include_rules = [ + "+components/browser_apis/ui_controllers/toolbar", +]
diff --git a/chrome/browser/ui/views/toolbar/webui_reload_control.cc b/chrome/browser/ui/views/toolbar/webui_reload_control.cc index 71c37d2..e0c1e1a 100644 --- a/chrome/browser/ui/views/toolbar/webui_reload_control.cc +++ b/chrome/browser/ui/views/toolbar/webui_reload_control.cc
@@ -8,6 +8,7 @@ #include "chrome/browser/ui/views/toolbar/webui_toolbar_web_view.h" #include "chrome/browser/ui/webui/webui_toolbar/webui_toolbar_ui.h" #include "chrome/grit/generated_resources.h" +#include "components/browser_apis/ui_controllers/toolbar/toolbar_ui_api_data_model.mojom.h" #include "ui/base/window_open_disposition_utils.h" #include "ui/views/controls/menu/menu_runner.h" #include "ui/views/widget/widget.h" @@ -95,7 +96,7 @@ } void WebUIReloadControl::UpdateState() { - auto state = browser_controls_api::mojom::ReloadControlState::New(); + auto state = toolbar_ui_api::mojom::ReloadControlState::New(); state->is_devtools_connected = is_dev_tools_connected_; state->is_navigation_loading = (mode_ == ReloadControl::Mode::kStop); state->is_context_menu_visible = menu_runner_->IsRunning();
diff --git a/chrome/browser/ui/views/toolbar/webui_split_tabs_control.cc b/chrome/browser/ui/views/toolbar/webui_split_tabs_control.cc index 8cd97a0..9e16224 100644 --- a/chrome/browser/ui/views/toolbar/webui_split_tabs_control.cc +++ b/chrome/browser/ui/views/toolbar/webui_split_tabs_control.cc
@@ -20,6 +20,7 @@ #include "chrome/browser/ui/webui/webui_toolbar/webui_toolbar_ui.h" #include "chrome/common/pref_names.h" #include "chrome/grit/generated_resources.h" +#include "components/browser_apis/ui_controllers/toolbar/toolbar_ui_api_data_model.mojom.h" #include "components/prefs/pref_service.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_ui_data_source.h" @@ -57,13 +58,12 @@ } void WebUISplitTabsControl::HandleContextMenu( - browser_controls_api::mojom::ContextMenuType menu_type, + toolbar_ui_api::mojom::ContextMenuType menu_type, const gfx::Point& screen_location, ui::mojom::MenuSourceType source_type) { BrowserWindowInterface* browser = toolbar_view_->browser_; current_menu_type_ = menu_type; - if (menu_type == - browser_controls_api::mojom::ContextMenuType::kSplitTabsAction) { + if (menu_type == toolbar_ui_api::mojom::ContextMenuType::kSplitTabsAction) { // Only show "Separate Views" menu if actually in split. auto* tab_strip_model = browser->GetTabStripModel(); if (!tab_strip_model || !tab_strip_model->GetActiveTab() || @@ -77,7 +77,7 @@ tab_strip_model, SplitTabMenuModel::MenuSource::kToolbarButton); RunMenuAt(screen_location.x(), screen_location.y(), source_type); } else if (menu_type == - browser_controls_api::mojom::ContextMenuType::kSplitTabsContext) { + toolbar_ui_api::mojom::ContextMenuType::kSplitTabsContext) { Browser* actual_browser = chrome::FindBrowserWithWindow(browser->GetWindow()->GetNativeWindow()); if (actual_browser) { @@ -124,7 +124,7 @@ } void WebUISplitTabsControl::UpdateVisibility( - const browser_controls_api::mojom::SplitTabsControlState* state) { + const toolbar_ui_api::mojom::SplitTabsControlState* state) { bool should_be_visible = state->is_pinned || state->is_current_tab_split; if (should_be_visible != is_visible_) { @@ -134,13 +134,13 @@ } void WebUISplitTabsControl::UpdateState() { - auto state = browser_controls_api::mojom::SplitTabsControlState::New(); + auto state = toolbar_ui_api::mojom::SplitTabsControlState::New(); auto s = webui_toolbar::ComputeTabSplitStatus(toolbar_view_->browser_); state->is_current_tab_split = s.is_split; state->location = s.location; state->is_pinned = webui_toolbar::IsButtonPinned( toolbar_view_->browser_, - browser_controls_api::mojom::ToolbarButtonType::kSplitTabs); + toolbar_ui_api::mojom::ToolbarButtonType::kSplitTabs); state->is_context_menu_visible = menu_runner_ && menu_runner_->IsRunning(); UpdateVisibility(state.get()); toolbar_view_->OnSplitTabsControlStateChanged(std::move(state));
diff --git a/chrome/browser/ui/views/toolbar/webui_split_tabs_control.h b/chrome/browser/ui/views/toolbar/webui_split_tabs_control.h index d42527e..53edf034 100644 --- a/chrome/browser/ui/views/toolbar/webui_split_tabs_control.h +++ b/chrome/browser/ui/views/toolbar/webui_split_tabs_control.h
@@ -9,9 +9,10 @@ #include "chrome/browser/ui/tabs/tab_strip_model_observer.h" #include "chrome/browser/ui/webui/webui_toolbar/utils/split_tabs_utils.h" #include "components/browser_apis/browser_controls/browser_controls_api.mojom.h" +#include "components/browser_apis/ui_controllers/toolbar/toolbar_ui_api_data_model.mojom.h" #include "components/prefs/pref_member.h" #include "ui/base/models/menu_model.h" -#include "ui/base/mojom/menu_source_type.mojom-forward.h" +#include "ui/base/mojom/menu_source_type.mojom.h" #include "ui/views/controls/menu/menu_runner.h" class WebUIToolbarWebView; @@ -33,7 +34,7 @@ bool IsVisible() const; // Handles context menu requests from the WebUI. - void HandleContextMenu(browser_controls_api::mojom::ContextMenuType menu_type, + void HandleContextMenu(toolbar_ui_api::mojom::ContextMenuType menu_type, const gfx::Point& screen_location, ui::mojom::MenuSourceType source); @@ -51,7 +52,7 @@ CheckSplitTabsButtonSourceType); void UpdateVisibility( - const browser_controls_api::mojom::SplitTabsControlState* state); + const toolbar_ui_api::mojom::SplitTabsControlState* state); void UpdateState(); void RunMenuAt(int x, int y, ui::mojom::MenuSourceType source_type); @@ -59,8 +60,8 @@ BooleanPrefMember pin_state_; bool is_visible_ = false; - browser_controls_api::mojom::ContextMenuType current_menu_type_ = - browser_controls_api::mojom::ContextMenuType::kUnspecified; + toolbar_ui_api::mojom::ContextMenuType current_menu_type_ = + toolbar_ui_api::mojom::ContextMenuType::kUnspecified; ui::mojom::MenuSourceType last_source_type_for_testing_ = ui::mojom::MenuSourceType::kNone;
diff --git a/chrome/browser/ui/views/toolbar/webui_toolbar_web_view.cc b/chrome/browser/ui/views/toolbar/webui_toolbar_web_view.cc index 8b4dec3d..ccf3302 100644 --- a/chrome/browser/ui/views/toolbar/webui_toolbar_web_view.cc +++ b/chrome/browser/ui/views/toolbar/webui_toolbar_web_view.cc
@@ -136,9 +136,9 @@ base::Unretained(this)))) { base::trace_event::EmitNamedTrigger("webui-toolbar-constructor"); last_queued_state_.split_tabs_control_state = - browser_controls_api::mojom::SplitTabsControlState::New(); + toolbar_ui_api::mojom::SplitTabsControlState::New(); last_queued_state_.reload_control_state = - browser_controls_api::mojom::ReloadControlState::New(); + toolbar_ui_api::mojom::ReloadControlState::New(); last_queued_state_.layout_constants_version = 0; if (auto* manager = InitialWebUIWindowMetricsManager::From(browser_)) { manager->OnReloadButtonCreated(); @@ -218,7 +218,7 @@ } void WebUIToolbarWebView::HandleContextMenu( - browser_controls_api::mojom::ContextMenuType menu_type, + toolbar_ui_api::mojom::ContextMenuType menu_type, gfx::Point viewport_coordinate_css_pixels, ui::mojom::MenuSourceType source) { CHECK(web_view_); @@ -234,14 +234,14 @@ .OffsetFromOrigin(); switch (menu_type) { - case browser_controls_api::mojom::ContextMenuType::kReload: + case toolbar_ui_api::mojom::ContextMenuType::kReload: reload_control_.HandleContextMenu(GetWidget(), screen_location, source); break; - case browser_controls_api::mojom::ContextMenuType::kSplitTabsAction: - case browser_controls_api::mojom::ContextMenuType::kSplitTabsContext: + case toolbar_ui_api::mojom::ContextMenuType::kSplitTabsAction: + case toolbar_ui_api::mojom::ContextMenuType::kSplitTabsContext: split_tabs_control_.HandleContextMenu(menu_type, screen_location, source); break; - case browser_controls_api::mojom::ContextMenuType::kUnspecified: + case toolbar_ui_api::mojom::ContextMenuType::kUnspecified: NOTREACHED() << "Unexpected ClickDispositionFlag::kUnspecified."; } } @@ -261,25 +261,28 @@ return &reload_control_; } -browser_controls_api::BrowserControlsService::Delegate* -WebUIToolbarWebView::GetDelegate() { +browser_controls_api::BrowserControlsService::BrowserControlsServiceDelegate* +WebUIToolbarWebView::GetBrowserControlsDelegate() { return this; } -std::unique_ptr<browser_controls_api::NavigationControlsStateFetcher> +toolbar_ui_api::ToolbarUIService::ToolbarUIServiceDelegate* +WebUIToolbarWebView::GetToolbarUIServiceDelegate() { + return this; +} + +std::unique_ptr<toolbar_ui_api::NavigationControlsStateFetcher> WebUIToolbarWebView::GetNavigationControlsStateFetcher() { - return std::make_unique< - browser_controls_api::NavigationControlsStateFetcherImpl>( + return std::make_unique<toolbar_ui_api::NavigationControlsStateFetcherImpl>( base::BindRepeating(&WebUIToolbarWebView::GetNavigationControlsState, base::Unretained(this))); } -browser_controls_api::mojom::NavigationControlsStatePtr +toolbar_ui_api::mojom::NavigationControlsStatePtr WebUIToolbarWebView::GetNavigationControlsState() { return last_queued_state_.Clone(); } - void WebUIToolbarWebView::DidStartNavigation( content::NavigationHandle* navigation_handle) { if (!navigation_handle->IsInPrimaryMainFrame()) { @@ -419,7 +422,7 @@ } void WebUIToolbarWebView::OnReloadControlStateChanged( - browser_controls_api::mojom::ReloadControlStatePtr state) { + toolbar_ui_api::mojom::ReloadControlStatePtr state) { if (*state != *last_queued_state_.reload_control_state) { last_queued_state_.reload_control_state = std::move(state); PostPushNavigationState(); @@ -427,7 +430,7 @@ } void WebUIToolbarWebView::OnSplitTabsControlStateChanged( - browser_controls_api::mojom::SplitTabsControlStatePtr state) { + toolbar_ui_api::mojom::SplitTabsControlStatePtr state) { if (*state != *last_queued_state_.split_tabs_control_state) { last_queued_state_.split_tabs_control_state = std::move(state); PostPushNavigationState();
diff --git a/chrome/browser/ui/views/toolbar/webui_toolbar_web_view.h b/chrome/browser/ui/views/toolbar/webui_toolbar_web_view.h index 151a5f92..27eafa10 100644 --- a/chrome/browser/ui/views/toolbar/webui_toolbar_web_view.h +++ b/chrome/browser/ui/views/toolbar/webui_toolbar_web_view.h
@@ -13,8 +13,11 @@ #include "chrome/browser/ui/views/toolbar/webui_split_tabs_control.h" #include "chrome/browser/ui/webui/webui_toolbar/adapters/navigation_controls_state_fetcher.h" #include "chrome/browser/ui/webui/webui_toolbar/browser_controls_service.h" +#include "chrome/browser/ui/webui/webui_toolbar/toolbar_ui_service.h" #include "chrome/browser/ui/webui/webui_toolbar/webui_toolbar_ui.h" #include "chrome/common/webui_url_constants.h" +#include "components/browser_apis/ui_controllers/toolbar/toolbar_ui_api.mojom.h" +#include "components/browser_apis/ui_controllers/toolbar/toolbar_ui_api_data_model.mojom.h" #include "content/public/browser/web_contents_delegate.h" #include "content/public/browser/web_contents_observer.h" #include "ui/base/metadata/metadata_header_macros.h" @@ -33,7 +36,9 @@ class WebUIToolbarWebView : public views::View, public content::WebContentsObserver, - public browser_controls_api::BrowserControlsService::Delegate, + public toolbar_ui_api::ToolbarUIService::ToolbarUIServiceDelegate, + public browser_controls_api::BrowserControlsService:: + BrowserControlsServiceDelegate, public WebUIToolbarUI::DependencyProvider { METADATA_HEADER(WebUIToolbarWebView, views::View) @@ -52,16 +57,20 @@ WebUILocationBar* GetLocationBar() { return location_bar_.get(); } // WebUIToolbarUI::DependencyProvider: - browser_controls_api::BrowserControlsService::Delegate* GetDelegate() - override; - std::unique_ptr<browser_controls_api::NavigationControlsStateFetcher> + browser_controls_api::BrowserControlsService::BrowserControlsServiceDelegate* + GetBrowserControlsDelegate() override; + toolbar_ui_api::ToolbarUIService::ToolbarUIServiceDelegate* + GetToolbarUIServiceDelegate() override; + std::unique_ptr<toolbar_ui_api::NavigationControlsStateFetcher> GetNavigationControlsStateFetcher() override; - // BrowserControlsService::BrowserControlsServiceDelegate: - void HandleContextMenu(browser_controls_api::mojom::ContextMenuType menu_type, + // ToolbarUIService::ToolbarUIServiceDelegate: + void HandleContextMenu(toolbar_ui_api::mojom::ContextMenuType menu_type, gfx::Point viewport_coordinate_css_pixels, ui::mojom::MenuSourceType source) override; void OnPageInitialized() override; + + // BrowserControlsService::BrowserControlsServiceDelegate: void PermitLaunchUrl() override; // views::View: @@ -93,7 +102,7 @@ friend WebUIReloadControl; friend WebUISplitTabsControl; - browser_controls_api::mojom::NavigationControlsStatePtr + toolbar_ui_api::mojom::NavigationControlsStatePtr GetNavigationControlsState(); // Reloads the WebUI toolbar to recover from crashes or unresponsiveness. @@ -118,14 +127,14 @@ // Called by friended controls to push state. void OnReloadControlStateChanged( - browser_controls_api::mojom::ReloadControlStatePtr state); + toolbar_ui_api::mojom::ReloadControlStatePtr state); void OnSplitTabsControlStateChanged( - browser_controls_api::mojom::SplitTabsControlStatePtr state); + toolbar_ui_api::mojom::SplitTabsControlStatePtr state); void OnTouchUiChanged(); void PostPushNavigationState(); void PushNavigationState(uint64_t state_generation); - browser_controls_api::mojom::NavigationControlsState last_queued_state_; + toolbar_ui_api::mojom::NavigationControlsState last_queued_state_; uint64_t current_state_generation_ = 0; InitializationState initialization_state_ =
diff --git a/chrome/browser/ui/views/toolbar/webui_toolbar_web_view_browsertest.cc b/chrome/browser/ui/views/toolbar/webui_toolbar_web_view_browsertest.cc index 3d7b791..c8d887f 100644 --- a/chrome/browser/ui/views/toolbar/webui_toolbar_web_view_browsertest.cc +++ b/chrome/browser/ui/views/toolbar/webui_toolbar_web_view_browsertest.cc
@@ -38,6 +38,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/browser_apis/ui_controllers/toolbar/toolbar_ui_api_data_model.mojom.h" #include "components/prefs/pref_service.h" #include "components/viz/common/frame_sinks/copy_output_result.h" #include "content/public/browser/javascript_dialog_manager.h" @@ -338,7 +339,7 @@ // Show reload button context menu. webui_toolbar_view->GetReloadControl()->SetDevToolsStatus(true); webui_toolbar_view->HandleContextMenu( - browser_controls_api::mojom::ContextMenuType::kReload, + toolbar_ui_api::mojom::ContextMenuType::kReload, element->GetScreenBounds().bottom_right(), ui::mojom::MenuSourceType::kMouse); @@ -391,7 +392,7 @@ // Show context menu. split_tabs_control->HandleContextMenu( - browser_controls_api::mojom::ContextMenuType::kSplitTabsContext, + toolbar_ui_api::mojom::ContextMenuType::kSplitTabsContext, element->GetScreenBounds().bottom_right(), ui::mojom::MenuSourceType::kMouse);
diff --git a/chrome/browser/ui/waap/initial_webui_browsertest.cc b/chrome/browser/ui/waap/initial_webui_browsertest.cc index dbc67be..9c870ed 100644 --- a/chrome/browser/ui/waap/initial_webui_browsertest.cc +++ b/chrome/browser/ui/waap/initial_webui_browsertest.cc
@@ -53,19 +53,23 @@ // This might blow up in the future. We are implicitly assuming that the // delegate isn't going to be used in this test. - browser_controls_api::BrowserControlsService::Delegate* GetDelegate() - override { + browser_controls_api::BrowserControlsService::BrowserControlsServiceDelegate* + GetBrowserControlsDelegate() override { return nullptr; } - std::unique_ptr<browser_controls_api::NavigationControlsStateFetcher> + toolbar_ui_api::ToolbarUIService::ToolbarUIServiceDelegate* + GetToolbarUIServiceDelegate() override { + return nullptr; + } + + std::unique_ptr<toolbar_ui_api::NavigationControlsStateFetcher> GetNavigationControlsStateFetcher() override { - return std::make_unique< - browser_controls_api::NavigationControlsStateFetcherImpl>( + return std::make_unique<toolbar_ui_api::NavigationControlsStateFetcherImpl>( base::BindLambdaForTesting([]() { - return browser_controls_api::mojom::NavigationControlsState::New( - browser_controls_api::mojom::ReloadControlState::New(), - browser_controls_api::mojom::SplitTabsControlState::New(), + return toolbar_ui_api::mojom::NavigationControlsState::New( + toolbar_ui_api::mojom::ReloadControlState::New(), + toolbar_ui_api::mojom::SplitTabsControlState::New(), /*layout_constants_version=*/0); })); }
diff --git a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.cc b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.cc index f176641..f1716f9 100644 --- a/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.cc +++ b/chrome/browser/ui/webui/side_panel/read_anything/read_anything_untrusted_page_handler.cc
@@ -110,6 +110,7 @@ void StartDistillation(dom_distiller::DomDistillerService* service, content::WebContents* contents) { + start_time_ = base::TimeTicks::Now(); // If existing distillation request, cancel it. This removes delegate as // observer of previous request and allow it to observe new request. viewer_handle_.reset(); @@ -125,6 +126,10 @@ // dom_distiller::ViewRequestDelegate: void OnArticleReady( const dom_distiller::DistilledArticleProto* article_proto) override { + CHECK(!start_time_.is_null()); + base::UmaHistogramMediumTimes( + "Accessibility.ReadAnything.TimeFromStartDistillationToOnArticleReady", + base::TimeTicks::Now() - start_time_); handler_->ProcessDistilledArticle(article_proto); viewer_handle_.reset(); } @@ -137,6 +142,7 @@ private: raw_ptr<ReadAnythingUntrustedPageHandler> handler_; std::unique_ptr<dom_distiller::ViewerHandle> viewer_handle_; + base::TimeTicks start_time_; }; namespace {
diff --git a/chrome/browser/ui/webui/webui_toolbar/BUILD.gn b/chrome/browser/ui/webui/webui_toolbar/BUILD.gn index cf522691..b368bd9 100644 --- a/chrome/browser/ui/webui/webui_toolbar/BUILD.gn +++ b/chrome/browser/ui/webui/webui_toolbar/BUILD.gn
@@ -11,6 +11,7 @@ source_set("webui_toolbar") { sources = [ "browser_controls_service.h", + "toolbar_ui_service.h", "webui_toolbar_ui.h", ] public_deps = [ @@ -19,6 +20,7 @@ "//chrome/common:constants", "//components/browser_apis/browser_controls:mojom", "//components/browser_apis/browser_controls:mojom_data_model", + "//components/browser_apis/ui_controllers/toolbar:mojom", "//content/public/browser", "//ui/webui/resources/js/tracked_element:mojo_bindings", "//ui/webui/tracked_element", @@ -34,6 +36,7 @@ source_set("impl") { sources = [ "browser_controls_service.cc", + "toolbar_ui_service.cc", "webui_toolbar_layout_css_helper.cc", "webui_toolbar_layout_css_helper.h", "webui_toolbar_ui.cc", @@ -70,6 +73,7 @@ testonly = true sources = [ "browser_controls_service_unittest.cc", + "toolbar_ui_service_unittest.cc", "webui_toolbar_layout_css_helper_unittest.cc", "webui_toolbar_ui_unittest.cc", ] @@ -89,6 +93,7 @@ "//chrome/test:test_support", "//components/browser_apis/browser_controls:mojom", "//components/browser_apis/browser_controls:mojom_data_model", + "//components/browser_apis/ui_controllers/toolbar:mojom", "//content/test:test_support", "//testing/gmock", "//testing/gtest",
diff --git a/chrome/browser/ui/webui/webui_toolbar/DEPS b/chrome/browser/ui/webui/webui_toolbar/DEPS index b4ebea21..1bed516fa 100644 --- a/chrome/browser/ui/webui/webui_toolbar/DEPS +++ b/chrome/browser/ui/webui/webui_toolbar/DEPS
@@ -1,3 +1,4 @@ include_rules = [ "+components/browser_apis/browser_controls", + "+components/browser_apis/ui_controllers/toolbar", ] \ No newline at end of file
diff --git a/chrome/browser/ui/webui/webui_toolbar/adapters/BUILD.gn b/chrome/browser/ui/webui/webui_toolbar/adapters/BUILD.gn index e97f3636..181b942f 100644 --- a/chrome/browser/ui/webui/webui_toolbar/adapters/BUILD.gn +++ b/chrome/browser/ui/webui/webui_toolbar/adapters/BUILD.gn
@@ -11,6 +11,7 @@ "//chrome/browser/ui/webui/webui_toolbar/utils", "//components/browser_apis/browser_controls:mojom", "//components/browser_apis/browser_controls:mojom_data_model", + "//components/browser_apis/ui_controllers/toolbar:mojom_data_model", "//ui/base:types", ] } @@ -30,5 +31,6 @@ "//chrome/browser/ui/tabs", "//chrome/browser/ui/webui/webui_toolbar/utils", "//components/browser_apis/browser_controls:mojom_data_model", + "//components/browser_apis/ui_controllers/toolbar:mojom_data_model", ] }
diff --git a/chrome/browser/ui/webui/webui_toolbar/adapters/browser_controls_adapter.h b/chrome/browser/ui/webui/webui_toolbar/adapters/browser_controls_adapter.h index ed8fc74..dd99820a 100644 --- a/chrome/browser/ui/webui/webui_toolbar/adapters/browser_controls_adapter.h +++ b/chrome/browser/ui/webui/webui_toolbar/adapters/browser_controls_adapter.h
@@ -23,7 +23,8 @@ virtual void CreateNewSplitTab() = 0; // These should probably be pulled to their own adapter. virtual webui_toolbar::TabSplitStatus ComputeSplitTabStatus() = 0; - virtual bool IsButtonPinned(mojom::ToolbarButtonType type) = 0; + virtual bool IsButtonPinned( + toolbar_ui_api::mojom::ToolbarButtonType type) = 0; }; } // namespace browser_controls_api
diff --git a/chrome/browser/ui/webui/webui_toolbar/adapters/browser_controls_adapter_impl.cc b/chrome/browser/ui/webui/webui_toolbar/adapters/browser_controls_adapter_impl.cc index 1614780d..bbc611a 100644 --- a/chrome/browser/ui/webui/webui_toolbar/adapters/browser_controls_adapter_impl.cc +++ b/chrome/browser/ui/webui/webui_toolbar/adapters/browser_controls_adapter_impl.cc
@@ -43,7 +43,8 @@ return webui_toolbar::ComputeTabSplitStatus(&browser_.get()); } -bool BrowserControlsAdapterImpl::IsButtonPinned(mojom::ToolbarButtonType type) { +bool BrowserControlsAdapterImpl::IsButtonPinned( + toolbar_ui_api::mojom::ToolbarButtonType type) { return webui_toolbar::IsButtonPinned(&browser_.get(), type); }
diff --git a/chrome/browser/ui/webui/webui_toolbar/adapters/browser_controls_adapter_impl.h b/chrome/browser/ui/webui/webui_toolbar/adapters/browser_controls_adapter_impl.h index 5687a7f..6e52975 100644 --- a/chrome/browser/ui/webui/webui_toolbar/adapters/browser_controls_adapter_impl.h +++ b/chrome/browser/ui/webui/webui_toolbar/adapters/browser_controls_adapter_impl.h
@@ -28,7 +28,7 @@ void Stop() override; void CreateNewSplitTab() override; webui_toolbar::TabSplitStatus ComputeSplitTabStatus() override; - bool IsButtonPinned(mojom::ToolbarButtonType type) override; + bool IsButtonPinned(toolbar_ui_api::mojom::ToolbarButtonType type) override; private: // Not owned.
diff --git a/chrome/browser/ui/webui/webui_toolbar/adapters/navigation_controls_state_fetcher.h b/chrome/browser/ui/webui/webui_toolbar/adapters/navigation_controls_state_fetcher.h index aaa3e98..1d11a3d 100644 --- a/chrome/browser/ui/webui/webui_toolbar/adapters/navigation_controls_state_fetcher.h +++ b/chrome/browser/ui/webui/webui_toolbar/adapters/navigation_controls_state_fetcher.h
@@ -5,9 +5,9 @@ #ifndef CHROME_BROWSER_UI_WEBUI_WEBUI_TOOLBAR_ADAPTERS_NAVIGATION_CONTROLS_STATE_FETCHER_H_ #define CHROME_BROWSER_UI_WEBUI_WEBUI_TOOLBAR_ADAPTERS_NAVIGATION_CONTROLS_STATE_FETCHER_H_ -#include "components/browser_apis/browser_controls/browser_controls_api.mojom.h" +#include "components/browser_apis/ui_controllers/toolbar/toolbar_ui_api_data_model.mojom.h" -namespace browser_controls_api { +namespace toolbar_ui_api { // An adapter which fetches and combines the current toolbar control states. class NavigationControlsStateFetcher { @@ -17,6 +17,6 @@ virtual mojom::NavigationControlsStatePtr GetNavigationControlsState() = 0; }; -} // namespace browser_controls_api +} // namespace toolbar_ui_api #endif // CHROME_BROWSER_UI_WEBUI_WEBUI_TOOLBAR_ADAPTERS_NAVIGATION_CONTROLS_STATE_FETCHER_H_
diff --git a/chrome/browser/ui/webui/webui_toolbar/adapters/navigation_controls_state_fetcher_impl.cc b/chrome/browser/ui/webui/webui_toolbar/adapters/navigation_controls_state_fetcher_impl.cc index 13143b3d..ba707731 100644 --- a/chrome/browser/ui/webui/webui_toolbar/adapters/navigation_controls_state_fetcher_impl.cc +++ b/chrome/browser/ui/webui/webui_toolbar/adapters/navigation_controls_state_fetcher_impl.cc
@@ -4,7 +4,7 @@ #include "chrome/browser/ui/webui/webui_toolbar/adapters/navigation_controls_state_fetcher_impl.h" -namespace browser_controls_api { +namespace toolbar_ui_api { NavigationControlsStateFetcherImpl::NavigationControlsStateFetcherImpl( CallbackType state_fetcher) @@ -18,4 +18,4 @@ return state_fetcher_.Run(); } -} // namespace browser_controls_api +} // namespace toolbar_ui_api
diff --git a/chrome/browser/ui/webui/webui_toolbar/adapters/navigation_controls_state_fetcher_impl.h b/chrome/browser/ui/webui/webui_toolbar/adapters/navigation_controls_state_fetcher_impl.h index 182b46cc..aea8b53 100644 --- a/chrome/browser/ui/webui/webui_toolbar/adapters/navigation_controls_state_fetcher_impl.h +++ b/chrome/browser/ui/webui/webui_toolbar/adapters/navigation_controls_state_fetcher_impl.h
@@ -5,9 +5,10 @@ #ifndef CHROME_BROWSER_UI_WEBUI_WEBUI_TOOLBAR_ADAPTERS_NAVIGATION_CONTROLS_STATE_FETCHER_IMPL_H_ #define CHROME_BROWSER_UI_WEBUI_WEBUI_TOOLBAR_ADAPTERS_NAVIGATION_CONTROLS_STATE_FETCHER_IMPL_H_ +#include "base/functional/callback.h" #include "chrome/browser/ui/webui/webui_toolbar/adapters/navigation_controls_state_fetcher.h" -namespace browser_controls_api { +namespace toolbar_ui_api { // State fetcher using a simple repeating callback. class NavigationControlsStateFetcherImpl @@ -25,6 +26,6 @@ CallbackType state_fetcher_; }; -} // namespace browser_controls_api +} // namespace toolbar_ui_api #endif // CHROME_BROWSER_UI_WEBUI_WEBUI_TOOLBAR_ADAPTERS_NAVIGATION_CONTROLS_STATE_FETCHER_IMPL_H_
diff --git a/chrome/browser/ui/webui/webui_toolbar/browser_controls_service.cc b/chrome/browser/ui/webui/webui_toolbar/browser_controls_service.cc index 4a99fe2b..c955014 100644 --- a/chrome/browser/ui/webui/webui_toolbar/browser_controls_service.cc +++ b/chrome/browser/ui/webui/webui_toolbar/browser_controls_service.cc
@@ -9,22 +9,15 @@ #include "base/check.h" #include "base/functional/bind.h" -#include "base/memory/weak_ptr.h" #include "base/metrics/histogram_functions.h" #include "base/time/time.h" #include "chrome/browser/ui/webui/metrics_reporter/metrics_reporter.h" -#include "chrome/browser/ui/webui/webui_toolbar/adapters/browser_controls_adapter.h" -#include "chrome/browser/ui/webui/webui_toolbar/utils/split_tabs_utils.h" #include "components/browser_apis/browser_controls/browser_controls_api.mojom.h" -#include "content/public/browser/context_menu_params.h" #include "ui/base/window_open_disposition_utils.h" +#include "ui/events/event_constants.h" namespace { // Measurement marks. -constexpr char kChangeVisibleModeToLoadingStartMark[] = - "BrowserControls.ChangeVisibleModeToLoading.Start"; -constexpr char kChangeVisibleModeToNotLoadingStartMark[] = - "BrowserControls.ChangeVisibleModeToNotLoading.Start"; constexpr char kInputMouseReleaseStartMark[] = "ReloadButton.Input.MouseRelease.Start"; @@ -67,32 +60,17 @@ BrowserControlsService::BrowserControlsService( mojo::PendingReceiver<mojom::BrowserControlsService> service, std::unique_ptr<BrowserControlsAdapter> browser_adapter, - std::unique_ptr<NavigationControlsStateFetcher> state_fetcher, MetricsReporter* metrics_reporter, - BrowserControlsService::Delegate* delegate) + BrowserControlsServiceDelegate* delegate) : service_(this, std::move(service)), browser_adapter_(std::move(browser_adapter)), - state_fetcher_(std::move(state_fetcher)), metrics_reporter_(metrics_reporter), delegate_(delegate) { CHECK(browser_adapter_); - CHECK(metrics_reporter); } BrowserControlsService::~BrowserControlsService() = default; -void BrowserControlsService::Bind(BindCallback callback) { - auto result = browser_controls_api::mojom::InitialState::New(); - result->state = state_fetcher_->GetNavigationControlsState(); - - mojo::Remote<browser_controls_api::mojom::BrowserControlsObserver> observer; - result->update_stream = observer.BindNewPipeAndPassReceiver(); - - observers_.Add(std::move(observer)); - - std::move(callback).Run(std::move(result)); -} - void BrowserControlsService::ReloadFromClick( bool bypass_cache, const std::vector<browser_controls_api::mojom::ClickDispositionFlag>& @@ -138,41 +116,14 @@ // TODO(crbug.com/448794588): Handle KeyPress events. } -void BrowserControlsService::ShowContextMenu( - browser_controls_api::mojom::ContextMenuType menu_type, - const gfx::Point& viewport_coordinate_css_pixels, - ui::mojom::MenuSourceType source) { - if (delegate_) { - delegate_->HandleContextMenu(menu_type, viewport_coordinate_css_pixels, - source); - } -} - -void BrowserControlsService::OnPageInitialized() { - if (delegate_) { - delegate_->OnPageInitialized(); - } -} - -void BrowserControlsService::OnNavigationControlsStateChanged( - const browser_controls_api::mojom::NavigationControlsStatePtr& state) { - auto* mark = state->reload_control_state->is_navigation_loading - ? kChangeVisibleModeToLoadingStartMark - : kChangeVisibleModeToNotLoadingStartMark; - metrics_reporter_->Mark(mark); - - for (auto& observer : observers_) { - observer->OnNavigationControlsStateChanged(state.Clone()); - } -} - void BrowserControlsService::SplitActiveTab() { // We only reach here if the frontend decided we need to CREATE a split. // We don't need to check IsActiveTabInSplit() or handle the menu here. browser_adapter_->CreateNewSplitTab(); } -void BrowserControlsService::SetDelegate(Delegate* delegate) { +void BrowserControlsService::SetDelegate( + BrowserControlsServiceDelegate* delegate) { delegate_ = delegate; }
diff --git a/chrome/browser/ui/webui/webui_toolbar/browser_controls_service.h b/chrome/browser/ui/webui/webui_toolbar/browser_controls_service.h index 3b95b98..380c1b14 100644 --- a/chrome/browser/ui/webui/webui_toolbar/browser_controls_service.h +++ b/chrome/browser/ui/webui/webui_toolbar/browser_controls_service.h
@@ -9,17 +9,10 @@ #include "base/memory/raw_ptr.h" #include "base/memory/weak_ptr.h" -#include "chrome/browser/ui/tabs/split_tab_menu_model.h" #include "chrome/browser/ui/webui/webui_toolbar/adapters/browser_controls_adapter.h" -#include "chrome/browser/ui/webui/webui_toolbar/adapters/navigation_controls_state_fetcher.h" #include "components/browser_apis/browser_controls/browser_controls_api.mojom.h" #include "mojo/public/cpp/bindings/pending_receiver.h" -#include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/receiver.h" -#include "mojo/public/cpp/bindings/remote_set.h" -#include "ui/base/models/menu_model.h" -#include "ui/base/mojom/menu_source_type.mojom-shared.h" -#include "ui/views/controls/menu/menu_runner.h" class MetricsReporter; @@ -28,47 +21,32 @@ class BrowserControlsService : public browser_controls_api::mojom::BrowserControlsService { public: - class Delegate { + class BrowserControlsServiceDelegate { public: - virtual ~Delegate() = default; - - virtual void HandleContextMenu( - browser_controls_api::mojom::ContextMenuType menu_type, - gfx::Point viewport_coordinate_css_pixels, - ui::mojom::MenuSourceType source) = 0; - virtual void OnPageInitialized() = 0; + virtual ~BrowserControlsServiceDelegate() = default; virtual void PermitLaunchUrl() = 0; }; BrowserControlsService( mojo::PendingReceiver<mojom::BrowserControlsService> service, std::unique_ptr<BrowserControlsAdapter> browser_adapter, - std::unique_ptr<NavigationControlsStateFetcher> state_fetcher, MetricsReporter* metrics_reporter, - Delegate* delegate); + BrowserControlsServiceDelegate* delegate); BrowserControlsService(const BrowserControlsService&) = delete; BrowserControlsService& operator=(const BrowserControlsService&) = delete; ~BrowserControlsService() override; - void SetDelegate(Delegate* delegate); + void SetDelegate(BrowserControlsServiceDelegate* delegate); // browser_controls_api::mojom::BrowserControlsService: - void Bind(BindCallback callback) override; void ReloadFromClick( bool bypass_cache, const std::vector<mojom::ClickDispositionFlag>& click_flags) override; void StopLoad() override; - void ShowContextMenu(mojom::ContextMenuType menu_type, - const gfx::Point& viewport_coordinate_css_pixels, - ui::mojom::MenuSourceType source) override; - void OnPageInitialized() override; void SplitActiveTab() override; - void OnNavigationControlsStateChanged( - const browser_controls_api::mojom::NavigationControlsStatePtr& state); - private: // Callback for `MetricsReporter::Measure()`. Records the resulting // base::TimeDelta to the given UMA histogram and clears the start mark. @@ -77,15 +55,11 @@ base::TimeDelta duration); mojo::Receiver<browser_controls_api::mojom::BrowserControlsService> service_; - mojo::RemoteSet<browser_controls_api::mojom::BrowserControlsObserver> - observers_; - std::unique_ptr<BrowserControlsAdapter> browser_adapter_; - std::unique_ptr<NavigationControlsStateFetcher> state_fetcher_; // Not owned. raw_ptr<MetricsReporter> metrics_reporter_; - raw_ptr<Delegate> delegate_; + raw_ptr<BrowserControlsServiceDelegate> delegate_; // Must be the last member. base::WeakPtrFactory<BrowserControlsService> weak_ptr_factory_{this};
diff --git a/chrome/browser/ui/webui/webui_toolbar/browser_controls_service_unittest.cc b/chrome/browser/ui/webui/webui_toolbar/browser_controls_service_unittest.cc index 64441451..c2fb4e4a 100644 --- a/chrome/browser/ui/webui/webui_toolbar/browser_controls_service_unittest.cc +++ b/chrome/browser/ui/webui/webui_toolbar/browser_controls_service_unittest.cc
@@ -7,8 +7,7 @@ #include <memory> #include <utility> -#include "base/metrics/histogram_samples.h" -#include "base/metrics/statistics_recorder.h" +#include "base/memory/raw_ptr.h" #include "base/test/bind.h" #include "base/test/gmock_callback_support.h" #include "base/test/metrics/histogram_tester.h" @@ -19,20 +18,14 @@ #include "chrome/browser/ui/browser_window/test/mock_browser_window_interface.h" #include "chrome/browser/ui/webui/metrics_reporter/metrics_reporter_service.h" #include "chrome/browser/ui/webui/metrics_reporter/mock_metrics_reporter.h" -#include "chrome/browser/ui/webui/webui_toolbar/adapters/navigation_controls_state_fetcher_impl.h" #include "chrome/browser/ui/webui/webui_toolbar/testing/toy_browser.h" #include "chrome/browser/ui/webui/webui_toolbar/webui_toolbar_test_utils.h" -#include "chrome/common/pref_names.h" #include "chrome/test/base/testing_profile.h" #include "components/browser_apis/browser_controls/browser_controls_api.mojom.h" -#include "components/prefs/pref_service.h" -#include "content/public/browser/context_menu_params.h" -#include "content/public/browser/web_contents_delegate.h" #include "content/public/test/browser_task_environment.h" #include "content/public/test/test_renderer_host.h" #include "content/public/test/web_contents_tester.h" -#include "mojo/public/cpp/bindings/receiver.h" -#include "mojo/public/cpp/bindings/remote.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/window_open_disposition.h" @@ -43,15 +36,10 @@ using ::testing::_; using ::testing::Eq; -using ::testing::InvokeArgument; using ::testing::Return; using testing::ToyBrowser; // Measurement marks. -constexpr char kChangeVisibleModeToLoadingStartMark[] = - "BrowserControls.ChangeVisibleModeToLoading.Start"; -constexpr char kChangeVisibleModeToNotLoadingStartMark[] = - "BrowserControls.ChangeVisibleModeToNotLoading.Start"; constexpr char kInputMouseReleaseStartMark[] = "ReloadButton.Input.MouseRelease.Start"; @@ -61,55 +49,14 @@ constexpr char kInputToStopMouseReleaseHistogram[] = "InitialWebUI.ReloadButton.InputToStop.MouseRelease"; -class MockWebWebUIToolbarDelegate : public BrowserControlsService::Delegate { +class MockBrowserControlsServiceDelegate + : public BrowserControlsService::BrowserControlsServiceDelegate { public: - MockWebWebUIToolbarDelegate() = default; + MockBrowserControlsServiceDelegate() = default; - MOCK_METHOD(void, - HandleContextMenu, - (mojom::ContextMenuType, gfx::Point, ui::mojom::MenuSourceType), - (override)); - MOCK_METHOD(void, OnPageInitialized, (), (override)); MOCK_METHOD(void, PermitLaunchUrl, (), (override)); }; -class Observer : public browser_controls_api::mojom::BrowserControlsObserver { - public: - explicit Observer(BrowserControlsService* service) { - base::RunLoop run_loop; - service->Bind(base::BindLambdaForTesting( - [&](base::expected<browser_controls_api::mojom::InitialStatePtr, - mojo_base::mojom::ErrorPtr> result) { - ASSERT_TRUE(result.has_value()); - state = std::move(result.value()->state); - receiver_.Bind(std::move(result.value()->update_stream)); - run_loop.Quit(); - })); - run_loop.Run(); - - FlushForTesting(); - } - ~Observer() override = default; - - Observer(const Observer&) = delete; - Observer& operator=(const Observer&) = delete; - - void OnNavigationControlsStateChanged( - mojom::NavigationControlsStatePtr changed) override { - state = std::move(changed); - } - - void FlushForTesting() { receiver_.FlushForTesting(); } - - // Easily accessible for testing. Start with nullopt to easily differentiate - // between uninitialized and unset. - mojom::NavigationControlsStatePtr state; - - private: - mojo::Receiver<browser_controls_api::mojom::BrowserControlsObserver> - receiver_{this}; -}; - // This is really an integration test. We provide a faked environment so that we // can have an easily predictable sealed environment to exercise the // interactions between our service and dependencies. To validate our @@ -117,15 +64,9 @@ class BrowserControlsServiceTest : public ::testing::Test { public: void SetUp() override { - auto fetcher = std::make_unique<NavigationControlsStateFetcherImpl>( - base::BindLambdaForTesting( - [&] { return navigation_controls_state().Clone(); })); service_ = std::make_unique<BrowserControlsService>( mojo::PendingReceiver<mojom::BrowserControlsService>(), - toy_browser_.GetAdapter(), std::move(fetcher), &metrics_reporter_, - &delegate_); - observer_ = std::make_unique<Observer>(service_.get()); - observer_->FlushForTesting(); + toy_browser_.GetAdapter(), &metrics_reporter_, &delegate_); } void TearDown() override { service_.reset(); } @@ -143,32 +84,18 @@ return metrics_reporter_; } - mojom::NavigationControlsStatePtr& navigation_controls_state() { - return navigation_controls_state_; - } - // Updates the service with the current navigation control state. - void PushNavigationControlsStateUpdate() { - service_->OnNavigationControlsStateChanged( - navigation_controls_state_.Clone()); - observer()->FlushForTesting(); - } - ToyBrowser& toy_browser() { return toy_browser_; } BrowserControlsService& service() { return *service_; } - Observer* observer() { return observer_.get(); } base::HistogramTester& histogram_tester() { return histogram_tester_; } - MockWebWebUIToolbarDelegate& delegate() { return delegate_; } + MockBrowserControlsServiceDelegate& delegate() { return delegate_; } private: content::BrowserTaskEnvironment task_environment_; testing::ToyBrowser toy_browser_; ::testing::NiceMock<MockMetricsReporter> metrics_reporter_; std::unique_ptr<BrowserControlsService> service_; - std::unique_ptr<Observer> observer_; base::HistogramTester histogram_tester_; - MockWebWebUIToolbarDelegate delegate_; - mojom::NavigationControlsStatePtr navigation_controls_state_ = - CreateValidNavigationControlsState(); + MockBrowserControlsServiceDelegate delegate_; }; // Test suite for Reload-related tests. @@ -234,112 +161,6 @@ histogram_tester().ExpectUniqueTimeSample(kInputToStopMouseReleaseHistogram, duration, 1); } - -// Tests that calling ShowContextMenu() opens the context menu. -TEST_F(BrowserControlsServiceTest, TestShowContextMenu) { - EXPECT_CALL(delegate(), - HandleContextMenu(::testing::_, ::testing::_, ::testing::_)); - - service().ShowContextMenu(mojom::ContextMenuType::kReload, gfx::Point(1, 2), - ui::mojom::MenuSourceType::kMouse); -} - -// Tests that calling OnNavigationControlsStateChanged() calls the page with the -// correct state and records metrics when loading. -TEST_F(BrowserControlsServiceTest, TestOnNavigationStatusChangedLoading) { - EXPECT_CALL(mock_metrics_reporter(), - Mark(kChangeVisibleModeToLoadingStartMark)) - .Times(1); - ASSERT_FALSE(observer()->state->reload_control_state->is_navigation_loading); - - navigation_controls_state()->reload_control_state->is_navigation_loading = - true; - PushNavigationControlsStateUpdate(); - - ASSERT_TRUE(observer()->state->reload_control_state->is_navigation_loading); -} - -// Tests that calling OnNavigationControlsStateChanged() calls the page with the -// correct state and records metrics when not loading. -TEST_F(BrowserControlsServiceTest, TestOnNavigationStatusChangedNotLoading) { - EXPECT_CALL(mock_metrics_reporter(), - Mark(kChangeVisibleModeToNotLoadingStartMark)) - .Times(1); - - navigation_controls_state()->reload_control_state->is_navigation_loading = - false; - PushNavigationControlsStateUpdate(); - - ASSERT_FALSE(observer()->state->reload_control_state->is_navigation_loading); -} - -// Tests that calling OnNavigationControlsStateChanged() calls the page with the -// correct state. -TEST_F(BrowserControlsServiceTest, TestOnDevToolsStatusChangedToConnected) { - ASSERT_FALSE(observer()->state->reload_control_state->is_devtools_connected); - - navigation_controls_state()->reload_control_state->is_devtools_connected = - true; - PushNavigationControlsStateUpdate(); - - ASSERT_TRUE(observer()->state->reload_control_state->is_devtools_connected); -} - -// Tests that multiple observers receive updates. -TEST_F(BrowserControlsServiceTest, MultipleObserversReceiveUpdates) { - Observer observer2(&service()); - - EXPECT_CALL(mock_metrics_reporter(), - Mark(kChangeVisibleModeToLoadingStartMark)) - .Times(1); - - ASSERT_FALSE(observer()->state->reload_control_state->is_navigation_loading); - ASSERT_FALSE(observer2.state->reload_control_state->is_navigation_loading); - - navigation_controls_state()->reload_control_state->is_navigation_loading = - true; - PushNavigationControlsStateUpdate(); - observer2.FlushForTesting(); - - ASSERT_TRUE(observer()->state->reload_control_state->is_navigation_loading); - ASSERT_TRUE(observer2.state->reload_control_state->is_navigation_loading); -} - -// Test suite for SplitTabs-related tests. -using BrowserControlsServiceSplitTabsTest = BrowserControlsServiceTest; - -// Tests that OnNavigationControlsStateChanged calls the page with the correct -// state. -TEST_F(BrowserControlsServiceSplitTabsTest, TestOnTabSplitStatusChanged) { - navigation_controls_state()->split_tabs_control_state->is_current_tab_split = - true; - navigation_controls_state()->split_tabs_control_state->location = - browser_controls_api::mojom::SplitTabActiveLocation::kStart; - PushNavigationControlsStateUpdate(); - - ASSERT_TRUE( - observer()->state->split_tabs_control_state->is_current_tab_split); - ASSERT_EQ(mojom::SplitTabActiveLocation::kStart, - observer()->state->split_tabs_control_state->location); -} - -// Tests that OnNavigationControlsStateChanged calls the page with the correct -// state. -TEST_F(BrowserControlsServiceSplitTabsTest, - TestOnSplitTabsButtonPinStateChanged) { - navigation_controls_state()->split_tabs_control_state->is_pinned = true; - PushNavigationControlsStateUpdate(); - - ASSERT_TRUE(observer()->state->split_tabs_control_state->is_pinned); -} - -// Tests that OnPageInitialized calls the delegate. -TEST_F(BrowserControlsServiceSplitTabsTest, TestOnPageInitializedDelegates) { - // Delegate OnPageInitialized should be called. - EXPECT_CALL(delegate(), OnPageInitialized()).Times(1); - - service().OnPageInitialized(); -} - } // namespace + } // namespace browser_controls_api
diff --git a/chrome/browser/ui/webui/webui_toolbar/testing/BUILD.gn b/chrome/browser/ui/webui/webui_toolbar/testing/BUILD.gn index 9dccb76e..3c05fa8 100644 --- a/chrome/browser/ui/webui/webui_toolbar/testing/BUILD.gn +++ b/chrome/browser/ui/webui/webui_toolbar/testing/BUILD.gn
@@ -14,6 +14,7 @@ "//chrome/browser/ui/webui/webui_toolbar/adapters", "//chrome/browser/ui/webui/webui_toolbar/utils", "//components/browser_apis/browser_controls:mojom_data_model", + "//components/browser_apis/ui_controllers/toolbar:mojom_data_model", "//ui/base:types", ] }
diff --git a/chrome/browser/ui/webui/webui_toolbar/testing/toy_browser.cc b/chrome/browser/ui/webui/webui_toolbar/testing/toy_browser.cc index e52112b..d590672 100644 --- a/chrome/browser/ui/webui/webui_toolbar/testing/toy_browser.cc +++ b/chrome/browser/ui/webui/webui_toolbar/testing/toy_browser.cc
@@ -39,7 +39,7 @@ return status; } - bool IsButtonPinned(mojom::ToolbarButtonType type) override { + bool IsButtonPinned(toolbar_ui_api::mojom::ToolbarButtonType type) override { return toy_browser_->IsButtonPinned(type); } @@ -51,18 +51,19 @@ return std::make_unique<ToyBrowserControlsAdapter>(this); } -void ToyBrowser::PinButton(mojom::ToolbarButtonType type) { +void ToyBrowser::PinButton(toolbar_ui_api::mojom::ToolbarButtonType type) { pinned_buttons_.insert(type); } -void ToyBrowser::UnpinButton(mojom::ToolbarButtonType type) { +void ToyBrowser::UnpinButton(toolbar_ui_api::mojom::ToolbarButtonType type) { auto found = pinned_buttons_.find(type); if (found != pinned_buttons_.end()) { pinned_buttons_.erase(found); } } -bool ToyBrowser::IsButtonPinned(mojom::ToolbarButtonType type) const { +bool ToyBrowser::IsButtonPinned( + toolbar_ui_api::mojom::ToolbarButtonType type) const { return pinned_buttons_.contains(type); }
diff --git a/chrome/browser/ui/webui/webui_toolbar/testing/toy_browser.h b/chrome/browser/ui/webui/webui_toolbar/testing/toy_browser.h index 9e2b083..7b7c5eb 100644 --- a/chrome/browser/ui/webui/webui_toolbar/testing/toy_browser.h +++ b/chrome/browser/ui/webui/webui_toolbar/testing/toy_browser.h
@@ -11,6 +11,7 @@ #include "chrome/app/chrome_command_ids.h" #include "components/browser_apis/browser_controls/browser_controls_api_data_model.mojom.h" +#include "components/browser_apis/ui_controllers/toolbar/toolbar_ui_api_data_model.mojom.h" #include "ui/base/window_open_disposition.h" namespace browser_controls_api { @@ -42,16 +43,16 @@ } // Noop if the pin state doesn't change. - void PinButton(mojom::ToolbarButtonType type); - void UnpinButton(mojom::ToolbarButtonType type); - bool IsButtonPinned(mojom::ToolbarButtonType type) const; + void PinButton(toolbar_ui_api::mojom::ToolbarButtonType type); + void UnpinButton(toolbar_ui_api::mojom::ToolbarButtonType type); + bool IsButtonPinned(toolbar_ui_api::mojom::ToolbarButtonType type) const; bool is_split_tab() const { return is_split_tab_; } private: friend class ToyBrowserControlsAdapter; std::vector<ToyBrowserCommand> received_commands_; - std::set<mojom::ToolbarButtonType> pinned_buttons_; + std::set<toolbar_ui_api::mojom::ToolbarButtonType> pinned_buttons_; // True when split tab is created. This state currently sticks, with no way // to unset it. bool is_split_tab_ = false;
diff --git a/chrome/browser/ui/webui/webui_toolbar/toolbar_ui_service.cc b/chrome/browser/ui/webui/webui_toolbar/toolbar_ui_service.cc new file mode 100644 index 0000000..9a575b2 --- /dev/null +++ b/chrome/browser/ui/webui/webui_toolbar/toolbar_ui_service.cc
@@ -0,0 +1,84 @@ +// Copyright 2026 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/webui/webui_toolbar/toolbar_ui_service.h" + +#include <utility> + +#include "base/check.h" +#include "base/functional/bind.h" +#include "base/types/expected.h" +#include "chrome/browser/ui/webui/metrics_reporter/metrics_reporter.h" +#include "chrome/browser/ui/webui/webui_toolbar/adapters/navigation_controls_state_fetcher.h" +#include "components/browser_apis/ui_controllers/toolbar/toolbar_ui_api.mojom.h" + +namespace { +// Measurement marks. +constexpr char kChangeVisibleModeToLoadingStartMark[] = + "ToolbarUI.ChangeVisibleModeToLoading.Start"; +constexpr char kChangeVisibleModeToNotLoadingStartMark[] = + "ToolbarUI.ChangeVisibleModeToNotLoading.Start"; +} // namespace + +namespace toolbar_ui_api { + +ToolbarUIService::ToolbarUIService( + mojo::PendingReceiver<toolbar_ui_api::mojom::ToolbarUIService> service, + std::unique_ptr<NavigationControlsStateFetcher> state_fetcher, + MetricsReporter* metrics_reporter, + ToolbarUIServiceDelegate* delegate) + : service_(this, std::move(service)), + state_fetcher_(std::move(state_fetcher)), + metrics_reporter_(metrics_reporter), + delegate_(delegate) { + CHECK(state_fetcher_); +} + +ToolbarUIService::~ToolbarUIService() = default; + +void ToolbarUIService::SetDelegate(ToolbarUIServiceDelegate* delegate) { + delegate_ = delegate; +} + +void ToolbarUIService::OnNavigationControlsStateChanged( + const mojom::NavigationControlsStatePtr& state) { + auto* mark = state->reload_control_state->is_navigation_loading + ? kChangeVisibleModeToLoadingStartMark + : kChangeVisibleModeToNotLoadingStartMark; + metrics_reporter_->Mark(mark); + + for (auto& observer : observers_) { + observer->OnNavigationControlsStateChanged(state.Clone()); + } +} + +void ToolbarUIService::Bind(BindCallback callback) { + auto result = toolbar_ui_api::mojom::InitialState::New(); + result->state = state_fetcher_->GetNavigationControlsState(); + + mojo::Remote<toolbar_ui_api::mojom::ToolbarUIObserver> observer; + result->update_stream = observer.BindNewPipeAndPassReceiver(); + + observers_.Add(std::move(observer)); + + std::move(callback).Run(std::move(result)); +} + +void ToolbarUIService::ShowContextMenu( + toolbar_ui_api::mojom::ContextMenuType menu_type, + const gfx::Point& viewport_coordinate_css_pixels, + ui::mojom::MenuSourceType source) { + if (delegate_) { + delegate_->HandleContextMenu(menu_type, viewport_coordinate_css_pixels, + source); + } +} + +void ToolbarUIService::OnPageInitialized() { + if (delegate_) { + delegate_->OnPageInitialized(); + } +} + +} // namespace toolbar_ui_api
diff --git a/chrome/browser/ui/webui/webui_toolbar/toolbar_ui_service.h b/chrome/browser/ui/webui/webui_toolbar/toolbar_ui_service.h new file mode 100644 index 0000000..53402990 --- /dev/null +++ b/chrome/browser/ui/webui/webui_toolbar/toolbar_ui_service.h
@@ -0,0 +1,74 @@ +// Copyright 2026 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_WEBUI_WEBUI_TOOLBAR_TOOLBAR_UI_SERVICE_H_ +#define CHROME_BROWSER_UI_WEBUI_WEBUI_TOOLBAR_TOOLBAR_UI_SERVICE_H_ + +#include "base/memory/raw_ptr.h" +#include "base/memory/weak_ptr.h" +#include "chrome/browser/ui/webui/webui_toolbar/adapters/navigation_controls_state_fetcher.h" +#include "components/browser_apis/ui_controllers/toolbar/toolbar_ui_api.mojom.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "mojo/public/cpp/bindings/receiver.h" +#include "mojo/public/cpp/bindings/remote_set.h" +#include "ui/base/mojom/menu_source_type.mojom-shared.h" +#include "ui/base/pointer/touch_ui_controller.h" + +class MetricsReporter; + +namespace toolbar_ui_api { + +class ToolbarUIService : public toolbar_ui_api::mojom::ToolbarUIService { + public: + class ToolbarUIServiceDelegate { + public: + virtual ~ToolbarUIServiceDelegate() = default; + virtual void HandleContextMenu( + toolbar_ui_api::mojom::ContextMenuType menu_type, + gfx::Point viewport_coordinate_css_pixels, + ui::mojom::MenuSourceType source) = 0; + virtual void OnPageInitialized() = 0; + }; + + ToolbarUIService( + mojo::PendingReceiver<toolbar_ui_api::mojom::ToolbarUIService> service, + std::unique_ptr<NavigationControlsStateFetcher> state_fetcher, + MetricsReporter* metrics_reporter, + ToolbarUIServiceDelegate* delegate); + + ToolbarUIService(const ToolbarUIService&) = delete; + ToolbarUIService& operator=(const ToolbarUIService&) = delete; + + ~ToolbarUIService() override; + + void SetDelegate(ToolbarUIServiceDelegate* delegate); + + void OnNavigationControlsStateChanged( + const mojom::NavigationControlsStatePtr& state); + + // toolbar_ui_api::mojom::ToolbarUIService: + void Bind(BindCallback callback) override; + void ShowContextMenu(toolbar_ui_api::mojom::ContextMenuType menu_type, + const gfx::Point& viewport_coordinate_css_pixels, + ui::mojom::MenuSourceType source) override; + void OnPageInitialized() override; + + private: + mojo::Receiver<toolbar_ui_api::mojom::ToolbarUIService> service_; + mojo::RemoteSet<toolbar_ui_api::mojom::ToolbarUIObserver> observers_; + + std::unique_ptr<NavigationControlsStateFetcher> state_fetcher_; + + // Not owned. + raw_ptr<MetricsReporter> metrics_reporter_; + raw_ptr<ToolbarUIServiceDelegate> delegate_; + + // Must be the last member. + base::WeakPtrFactory<ToolbarUIService> weak_ptr_factory_{this}; +}; + +} // namespace toolbar_ui_api + +#endif // CHROME_BROWSER_UI_WEBUI_WEBUI_TOOLBAR_TOOLBAR_UI_SERVICE_H_
diff --git a/chrome/browser/ui/webui/webui_toolbar/toolbar_ui_service_unittest.cc b/chrome/browser/ui/webui/webui_toolbar/toolbar_ui_service_unittest.cc new file mode 100644 index 0000000..fe2b02e --- /dev/null +++ b/chrome/browser/ui/webui/webui_toolbar/toolbar_ui_service_unittest.cc
@@ -0,0 +1,247 @@ +// Copyright 2026 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/webui/webui_toolbar/toolbar_ui_service.h" + +#include <memory> +#include <utility> + +#include "base/test/bind.h" +#include "base/test/gmock_callback_support.h" +#include "base/types/expected.h" +#include "chrome/browser/ui/browser_window/test/mock_browser_window_interface.h" +#include "chrome/browser/ui/webui/metrics_reporter/metrics_reporter_service.h" +#include "chrome/browser/ui/webui/metrics_reporter/mock_metrics_reporter.h" +#include "chrome/browser/ui/webui/webui_toolbar/adapters/navigation_controls_state_fetcher_impl.h" +#include "chrome/browser/ui/webui/webui_toolbar/webui_toolbar_test_utils.h" +#include "chrome/test/base/testing_profile.h" +#include "components/browser_apis/ui_controllers/toolbar/toolbar_ui_api.mojom.h" +#include "content/public/test/browser_task_environment.h" +#include "content/public/test/test_renderer_host.h" +#include "content/public/test/web_contents_tester.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/receiver.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace toolbar_ui_api { + +namespace { + +using ::testing::_; +using ::testing::Return; + +// Measurement marks. +constexpr char kChangeVisibleModeToLoadingStartMark[] = + "ToolbarUI.ChangeVisibleModeToLoading.Start"; +constexpr char kChangeVisibleModeToNotLoadingStartMark[] = + "ToolbarUI.ChangeVisibleModeToNotLoading.Start"; + +class MockToolbarUIServiceDelegate + : public ToolbarUIService::ToolbarUIServiceDelegate { + public: + MOCK_METHOD(void, + HandleContextMenu, + (mojom::ContextMenuType type, + gfx::Point location, + ui::mojom::MenuSourceType source), + (override)); + MOCK_METHOD(void, OnPageInitialized, (), (override)); +}; + +class Observer : public mojom::ToolbarUIObserver { + public: + explicit Observer(ToolbarUIService* service) { + base::RunLoop run_loop; + service->Bind(base::BindLambdaForTesting( + [&](base::expected<mojom::InitialStatePtr, mojo_base::mojom::ErrorPtr> + result) { + ASSERT_TRUE(result.has_value()); + state = std::move(result.value()->state); + receiver_.Bind(std::move(result.value()->update_stream)); + run_loop.Quit(); + })); + run_loop.Run(); + + FlushForTesting(); + } + + ~Observer() override = default; + + Observer(const Observer&) = delete; + Observer& operator=(const Observer&) = delete; + + void OnNavigationControlsStateChanged( + mojom::NavigationControlsStatePtr changed) override { + state = std::move(changed); + } + + void FlushForTesting() { receiver_.FlushForTesting(); } + + // Easily accessible for testing. Start with nullopt to easily differentiate + // between uninitialized and unset. + mojom::NavigationControlsStatePtr state; + + private: + mojo::Receiver<mojom::ToolbarUIObserver> receiver_{this}; +}; + +// This is really an integration test. We provide a faked environment so that we +// can have an easily predictable sealed environment to exercise the +// interactions between our service and dependencies. To validate our +// integration with "real" browser services, we should utilize browser tests. +class ToolbarUIServiceTest : public ::testing::Test { + public: + void SetUp() override { + auto fetcher = std::make_unique<NavigationControlsStateFetcherImpl>( + base::BindLambdaForTesting( + [&] { return navigation_controls_state().Clone(); })); + service_ = std::make_unique<ToolbarUIService>( + mojo::PendingReceiver<mojom::ToolbarUIService>(), std::move(fetcher), + &metrics_reporter_, &delegate_); + observer_ = std::make_unique<Observer>(service_.get()); + observer_->FlushForTesting(); + } + + void TearDown() override { service_.reset(); } + + protected: + ::testing::NiceMock<MockMetricsReporter>& mock_metrics_reporter() { + return metrics_reporter_; + } + + mojom::NavigationControlsStatePtr& navigation_controls_state() { + return navigation_controls_state_; + } + + // Updates the service with the current navigation control state. + void PushNavigationControlsStateUpdate() { + service_->OnNavigationControlsStateChanged( + navigation_controls_state_.Clone()); + observer()->FlushForTesting(); + } + + ToolbarUIService& service() { return *service_; } + Observer* observer() { return observer_.get(); } + MockToolbarUIServiceDelegate& delegate() { return delegate_; } + + private: + content::BrowserTaskEnvironment task_environment_; + ::testing::NiceMock<MockMetricsReporter> metrics_reporter_; + std::unique_ptr<ToolbarUIService> service_; + std::unique_ptr<Observer> observer_; + MockToolbarUIServiceDelegate delegate_; + mojom::NavigationControlsStatePtr navigation_controls_state_ = + CreateValidNavigationControlsState(); +}; + +// Tests that calling ShowContextMenu() opens the context menu. +TEST_F(ToolbarUIServiceTest, TestShowContextMenu) { + EXPECT_CALL(delegate(), + HandleContextMenu(::testing::_, ::testing::_, ::testing::_)); + + service().ShowContextMenu(mojom::ContextMenuType::kReload, gfx::Point(1, 2), + ui::mojom::MenuSourceType::kMouse); +} + +// Tests that calling OnNavigationControlsStateChanged() calls the page with the +// correct state and records metrics when loading. +TEST_F(ToolbarUIServiceTest, TestOnNavigationStatusChangedLoading) { + EXPECT_CALL(mock_metrics_reporter(), + Mark(kChangeVisibleModeToLoadingStartMark)) + .Times(1); + ASSERT_FALSE(observer()->state->reload_control_state->is_navigation_loading); + + navigation_controls_state()->reload_control_state->is_navigation_loading = + true; + PushNavigationControlsStateUpdate(); + + ASSERT_TRUE(observer()->state->reload_control_state->is_navigation_loading); +} + +// Tests that calling OnNavigationControlsStateChanged() calls the page with the +// correct state and records metrics when not loading. +TEST_F(ToolbarUIServiceTest, TestOnNavigationStatusChangedNotLoading) { + EXPECT_CALL(mock_metrics_reporter(), + Mark(kChangeVisibleModeToNotLoadingStartMark)) + .Times(1); + + navigation_controls_state()->reload_control_state->is_navigation_loading = + false; + PushNavigationControlsStateUpdate(); + + ASSERT_FALSE(observer()->state->reload_control_state->is_navigation_loading); +} + +// Tests that calling OnNavigationControlsStateChanged() calls the page with the +// correct state. +TEST_F(ToolbarUIServiceTest, TestOnDevToolsStatusChangedToConnected) { + ASSERT_FALSE(observer()->state->reload_control_state->is_devtools_connected); + + navigation_controls_state()->reload_control_state->is_devtools_connected = + true; + PushNavigationControlsStateUpdate(); + + ASSERT_TRUE(observer()->state->reload_control_state->is_devtools_connected); +} + +// Tests that multiple observers receive updates. +TEST_F(ToolbarUIServiceTest, MultipleObserversReceiveUpdates) { + Observer observer2(&service()); + + EXPECT_CALL(mock_metrics_reporter(), + Mark(kChangeVisibleModeToLoadingStartMark)) + .Times(1); + + ASSERT_FALSE(observer()->state->reload_control_state->is_navigation_loading); + ASSERT_FALSE(observer2.state->reload_control_state->is_navigation_loading); + + navigation_controls_state()->reload_control_state->is_navigation_loading = + true; + PushNavigationControlsStateUpdate(); + observer2.FlushForTesting(); + + ASSERT_TRUE(observer()->state->reload_control_state->is_navigation_loading); + ASSERT_TRUE(observer2.state->reload_control_state->is_navigation_loading); +} + +// Test suite for SplitTabs-related tests. +using ToolbarUIServiceSplitTabsTest = ToolbarUIServiceTest; + +// Tests that OnNavigationControlsStateChanged calls the page with the correct +// state. +TEST_F(ToolbarUIServiceSplitTabsTest, TestOnTabSplitStatusChanged) { + navigation_controls_state()->split_tabs_control_state->is_current_tab_split = + true; + navigation_controls_state()->split_tabs_control_state->location = + toolbar_ui_api::mojom::SplitTabActiveLocation::kStart; + PushNavigationControlsStateUpdate(); + + ASSERT_TRUE( + observer()->state->split_tabs_control_state->is_current_tab_split); + ASSERT_EQ(mojom::SplitTabActiveLocation::kStart, + observer()->state->split_tabs_control_state->location); +} + +// Tests that OnNavigationControlsStateChanged calls the page with the correct +// state. +TEST_F(ToolbarUIServiceSplitTabsTest, TestOnSplitTabsButtonPinStateChanged) { + navigation_controls_state()->split_tabs_control_state->is_pinned = true; + PushNavigationControlsStateUpdate(); + + ASSERT_TRUE(observer()->state->split_tabs_control_state->is_pinned); +} + +// Tests that OnPageInitialized calls the delegate. +TEST_F(ToolbarUIServiceSplitTabsTest, TestOnPageInitializedDelegates) { + // Delegate OnPageInitialized should be called. + EXPECT_CALL(delegate(), OnPageInitialized()).Times(1); + + service().OnPageInitialized(); +} + +} // namespace + +} // namespace toolbar_ui_api
diff --git a/chrome/browser/ui/webui/webui_toolbar/utils/BUILD.gn b/chrome/browser/ui/webui/webui_toolbar/utils/BUILD.gn index cfee35b..b12d3ccb 100644 --- a/chrome/browser/ui/webui/webui_toolbar/utils/BUILD.gn +++ b/chrome/browser/ui/webui/webui_toolbar/utils/BUILD.gn
@@ -12,6 +12,7 @@ "//chrome/browser/ui/tabs", "//chrome/browser/ui/tabs:tab_strip", "//components/browser_apis/browser_controls:mojom_data_model", + "//components/browser_apis/ui_controllers/toolbar:mojom_data_model", "//ui/webui", ] }
diff --git a/chrome/browser/ui/webui/webui_toolbar/utils/split_tabs_utils.cc b/chrome/browser/ui/webui/webui_toolbar/utils/split_tabs_utils.cc index a79b8478..eef5b04e 100644 --- a/chrome/browser/ui/webui/webui_toolbar/utils/split_tabs_utils.cc +++ b/chrome/browser/ui/webui/webui_toolbar/utils/split_tabs_utils.cc
@@ -14,6 +14,7 @@ #include "chrome/browser/ui/ui_features.h" #include "chrome/common/pref_names.h" #include "chrome/grit/generated_resources.h" +#include "components/browser_apis/ui_controllers/toolbar/toolbar_ui_api_data_model.mojom.h" #include "components/prefs/pref_service.h" #include "content/public/browser/web_ui_data_source.h" #include "ui/base/l10n/l10n_util.h" @@ -38,20 +39,17 @@ switch (location) { case split_tabs::SplitTabActiveLocation::kStart: - status.location = - browser_controls_api::mojom::SplitTabActiveLocation::kStart; + status.location = toolbar_ui_api::mojom::SplitTabActiveLocation::kStart; break; case split_tabs::SplitTabActiveLocation::kEnd: - status.location = - browser_controls_api::mojom::SplitTabActiveLocation::kEnd; + status.location = toolbar_ui_api::mojom::SplitTabActiveLocation::kEnd; break; case split_tabs::SplitTabActiveLocation::kTop: - status.location = - browser_controls_api::mojom::SplitTabActiveLocation::kTop; + status.location = toolbar_ui_api::mojom::SplitTabActiveLocation::kTop; break; case split_tabs::SplitTabActiveLocation::kBottom: status.location = - browser_controls_api::mojom::SplitTabActiveLocation::kBottom; + toolbar_ui_api::mojom::SplitTabActiveLocation::kBottom; break; } } @@ -60,9 +58,9 @@ } bool IsButtonPinned(BrowserWindowInterface* browser_interface, - browser_controls_api::mojom::ToolbarButtonType type) { + toolbar_ui_api::mojom::ToolbarButtonType type) { switch (type) { - case browser_controls_api::mojom::ToolbarButtonType::kSplitTabs: + case toolbar_ui_api::mojom::ToolbarButtonType::kSplitTabs: return browser_interface->GetProfile()->GetPrefs()->GetBoolean( prefs::kPinSplitTabButton); default:
diff --git a/chrome/browser/ui/webui/webui_toolbar/utils/split_tabs_utils.h b/chrome/browser/ui/webui/webui_toolbar/utils/split_tabs_utils.h index f90fd27..5cb77f42 100644 --- a/chrome/browser/ui/webui/webui_toolbar/utils/split_tabs_utils.h +++ b/chrome/browser/ui/webui/webui_toolbar/utils/split_tabs_utils.h
@@ -6,6 +6,7 @@ #define CHROME_BROWSER_UI_WEBUI_WEBUI_TOOLBAR_UTILS_SPLIT_TABS_UTILS_H_ #include "components/browser_apis/browser_controls/browser_controls_api_data_model.mojom.h" +#include "components/browser_apis/ui_controllers/toolbar/toolbar_ui_api_data_model.mojom.h" class BrowserWindowInterface; @@ -18,8 +19,8 @@ // Represents the split state of the active tab. struct TabSplitStatus { bool is_split = false; - browser_controls_api::mojom::SplitTabActiveLocation location = - browser_controls_api::mojom::SplitTabActiveLocation::kStart; + toolbar_ui_api::mojom::SplitTabActiveLocation location = + toolbar_ui_api::mojom::SplitTabActiveLocation::kStart; bool operator==(const TabSplitStatus& other) const = default; }; @@ -29,7 +30,7 @@ // Gets the pin state from user prefs. bool IsButtonPinned(BrowserWindowInterface* browser_interface, - browser_controls_api::mojom::ToolbarButtonType type); + toolbar_ui_api::mojom::ToolbarButtonType type); // Populates the WebUI data source with split tabs specific strings and initial // state.
diff --git a/chrome/browser/ui/webui/webui_toolbar/webui_toolbar_test_utils.cc b/chrome/browser/ui/webui/webui_toolbar/webui_toolbar_test_utils.cc index 0fb5f6a0..bed7c48b 100644 --- a/chrome/browser/ui/webui/webui_toolbar/webui_toolbar_test_utils.cc +++ b/chrome/browser/ui/webui/webui_toolbar/webui_toolbar_test_utils.cc
@@ -7,14 +7,13 @@ MockReloadButtonPage::MockReloadButtonPage() = default; MockReloadButtonPage::~MockReloadButtonPage() = default; -mojo::PendingRemote<browser_controls_api::mojom::BrowserControlsObserver> +mojo::PendingRemote<toolbar_ui_api::mojom::ToolbarUIObserver> MockReloadButtonPage::BindAndGetRemote() { return receiver_.BindNewPipeAndPassRemote(); } void MockReloadButtonPage::Bind( - mojo::PendingReceiver<browser_controls_api::mojom::BrowserControlsObserver> - receiver) { + mojo::PendingReceiver<toolbar_ui_api::mojom::ToolbarUIObserver> receiver) { receiver_.Bind(std::move(receiver)); } @@ -22,14 +21,19 @@ receiver_.FlushForTesting(); } -MockWebWebUIToolbarDelegate::MockWebWebUIToolbarDelegate() = default; -MockWebWebUIToolbarDelegate::~MockWebWebUIToolbarDelegate() = default; +MockToolbarUIServiceDelegate::MockToolbarUIServiceDelegate() = default; +MockToolbarUIServiceDelegate::~MockToolbarUIServiceDelegate() = default; -browser_controls_api::mojom::NavigationControlsStatePtr +MockBrowserControlsServiceDelegate::MockBrowserControlsServiceDelegate() = + default; +MockBrowserControlsServiceDelegate::~MockBrowserControlsServiceDelegate() = + default; + +toolbar_ui_api::mojom::NavigationControlsStatePtr CreateValidNavigationControlsState() { - return browser_controls_api::mojom::NavigationControlsState::New( - browser_controls_api::mojom::ReloadControlState::New(), - browser_controls_api::mojom::SplitTabsControlState::New(), + return toolbar_ui_api::mojom::NavigationControlsState::New( + toolbar_ui_api::mojom::ReloadControlState::New(), + toolbar_ui_api::mojom::SplitTabsControlState::New(), /*layout_constants_version=*/0); }
diff --git a/chrome/browser/ui/webui/webui_toolbar/webui_toolbar_test_utils.h b/chrome/browser/ui/webui/webui_toolbar/webui_toolbar_test_utils.h index c782a9cb..ead49d1 100644 --- a/chrome/browser/ui/webui/webui_toolbar/webui_toolbar_test_utils.h +++ b/chrome/browser/ui/webui/webui_toolbar/webui_toolbar_test_utils.h
@@ -7,17 +7,18 @@ #include "chrome/browser/command_updater.h" #include "chrome/browser/ui/webui/webui_toolbar/browser_controls_service.h" +#include "chrome/browser/ui/webui/webui_toolbar/toolbar_ui_service.h" #include "components/browser_apis/browser_controls/browser_controls_api.mojom.h" -#include "components/browser_apis/browser_controls/browser_controls_api_data_model.mojom.h" +#include "components/browser_apis/ui_controllers/toolbar/toolbar_ui_api.mojom.h" +#include "components/browser_apis/ui_controllers/toolbar/toolbar_ui_api_data_model.mojom.h" #include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/receiver.h" #include "testing/gmock/include/gmock/gmock.h" #include "ui/base/window_open_disposition.h" // Mock implementation of the -// browser_controls_api::mojom::BrowserControlsObserver interface. -class MockReloadButtonPage - : public browser_controls_api::mojom::BrowserControlsObserver { +// toolbar_ui_api::mojom::ToolbarUIObserver interface. +class MockReloadButtonPage : public toolbar_ui_api::mojom::ToolbarUIObserver { public: MockReloadButtonPage(); ~MockReloadButtonPage() override; @@ -26,43 +27,53 @@ MockReloadButtonPage& operator=(const MockReloadButtonPage&) = delete; // Returns a PendingRemote to this mock implementation. - mojo::PendingRemote<browser_controls_api::mojom::BrowserControlsObserver> + mojo::PendingRemote<toolbar_ui_api::mojom::ToolbarUIObserver> BindAndGetRemote(); - void Bind(mojo::PendingReceiver< - browser_controls_api::mojom::BrowserControlsObserver> receiver); + void Bind( + mojo::PendingReceiver<toolbar_ui_api::mojom::ToolbarUIObserver> receiver); void FlushForTesting(); - // browser_controls_api::mojom::BrowserControlsObserver: + // toolbar_ui_api::mojom::ToolbarUIObserver: MOCK_METHOD(void, OnNavigationControlsStateChanged, - (browser_controls_api::mojom::NavigationControlsStatePtr state), + (toolbar_ui_api::mojom::NavigationControlsStatePtr state), (override)); private: - mojo::Receiver<browser_controls_api::mojom::BrowserControlsObserver> - receiver_{this}; + mojo::Receiver<toolbar_ui_api::mojom::ToolbarUIObserver> receiver_{this}; }; -class MockWebWebUIToolbarDelegate - : public browser_controls_api::BrowserControlsService::Delegate { +class MockToolbarUIServiceDelegate + : public toolbar_ui_api::ToolbarUIService::ToolbarUIServiceDelegate { public: - MockWebWebUIToolbarDelegate(); - ~MockWebWebUIToolbarDelegate() override; + MockToolbarUIServiceDelegate(); + ~MockToolbarUIServiceDelegate() override; + // ToolbarUIService::ToolbarUIServiceDelegate: MOCK_METHOD(void, HandleContextMenu, - (browser_controls_api::mojom::ContextMenuType, + (toolbar_ui_api::mojom::ContextMenuType, gfx::Point, ui::mojom::MenuSourceType), (override)); MOCK_METHOD(void, OnPageInitialized, (), (override)); +}; + +class MockBrowserControlsServiceDelegate + : public browser_controls_api::BrowserControlsService:: + BrowserControlsServiceDelegate { + public: + MockBrowserControlsServiceDelegate(); + ~MockBrowserControlsServiceDelegate() override; + + // BrowserControlsService::BrowserControlsServiceDelegate: MOCK_METHOD(void, PermitLaunchUrl, (), (override)); }; // Helper to create a valid NavigationControlsState with initialized fields. -browser_controls_api::mojom::NavigationControlsStatePtr +toolbar_ui_api::mojom::NavigationControlsStatePtr CreateValidNavigationControlsState(); // Mock implementation of CommandUpdater for testing.
diff --git a/chrome/browser/ui/webui/webui_toolbar/webui_toolbar_ui.cc b/chrome/browser/ui/webui/webui_toolbar/webui_toolbar_ui.cc index 9df6fde..b42a443 100644 --- a/chrome/browser/ui/webui/webui_toolbar/webui_toolbar_ui.cc +++ b/chrome/browser/ui/webui/webui_toolbar/webui_toolbar_ui.cc
@@ -6,7 +6,9 @@ #include <memory> #include <utility> +#include <vector> +#include "base/check.h" #include "base/strings/strcat.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser_command_controller.h" @@ -26,6 +28,7 @@ #include "chrome/browser/ui/webui/webui_embedding_context.h" #include "chrome/browser/ui/webui/webui_toolbar/adapters/browser_controls_adapter_impl.h" #include "chrome/browser/ui/webui/webui_toolbar/browser_controls_service.h" +#include "chrome/browser/ui/webui/webui_toolbar/toolbar_ui_service.h" #include "chrome/browser/ui/webui/webui_toolbar/utils/split_tabs_utils.h" #include "chrome/browser/ui/webui/webui_toolbar/webui_toolbar_layout_css_helper.h" #include "chrome/common/chrome_features.h" @@ -34,6 +37,7 @@ #include "chrome/grit/webui_toolbar_resources.h" #include "chrome/grit/webui_toolbar_resources_map.h" #include "components/browser_apis/browser_controls/browser_controls_api.mojom.h" +#include "components/browser_apis/ui_controllers/toolbar/toolbar_ui_api.mojom.h" #include "content/public/browser/render_widget_host.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_ui.h" @@ -125,9 +129,28 @@ std::move(receiver), std::make_unique<browser_controls_api::BrowserControlsAdapterImpl>( webui::GetBrowserWindowInterface(web_contents), command_updater), - dependency_provider_->GetNavigationControlsStateFetcher(), metrics_service->metrics_reporter(), - dependency_provider_->GetDelegate()); + dependency_provider_->GetBrowserControlsDelegate()); +} + +void WebUIToolbarUI::BindInterface( + mojo::PendingReceiver<toolbar_ui_api::mojom::ToolbarUIService> receiver) { + CHECK(dependency_provider_) + << "Dependency provider is not set, make sure to call Init() first"; + + auto* web_contents = web_ui()->GetWebContents(); + MetricsReporterService* metrics_service = + MetricsReporterService::GetFromWebContents(web_contents); + + // If this CHECK() starts hitting, it could be due to races with browser + // shutdown, similar to issues seen in the past (e.g., b/478033216#comment4). + CHECK(metrics_service) << "Metrics service missing from web contents"; + + toolbar_ui_service_ = std::make_unique<toolbar_ui_api::ToolbarUIService>( + std::move(receiver), + dependency_provider_->GetNavigationControlsStateFetcher(), + metrics_service->metrics_reporter(), + dependency_provider_->GetToolbarUIServiceDelegate()); } void WebUIToolbarUI::BindInterface( @@ -146,10 +169,9 @@ } void WebUIToolbarUI::OnNavigationControlsStateChanged( - browser_controls_api::mojom::NavigationControlsStatePtr state) { - if (browser_controls_service_) { - browser_controls_service_->OnNavigationControlsStateChanged( - std::move(state)); + toolbar_ui_api::mojom::NavigationControlsStatePtr state) { + if (toolbar_ui_service_) { + toolbar_ui_service_->OnNavigationControlsStateChanged(std::move(state)); } } @@ -158,12 +180,11 @@ << "Out of order initialization, the browser control service has already " "been instantiated."; - dependency_provider_ = dependency_provider; -} + CHECK(!toolbar_ui_service_) + << "Out of order initialization, the toolbar UI service has already " + "been instantiated."; -browser_controls_api::BrowserControlsService* -WebUIToolbarUI::browser_controls_service_for_testing() { - return browser_controls_service_.get(); + dependency_provider_ = dependency_provider; } CommandUpdater* WebUIToolbarUI::GetCommandUpdater() const {
diff --git a/chrome/browser/ui/webui/webui_toolbar/webui_toolbar_ui.h b/chrome/browser/ui/webui/webui_toolbar/webui_toolbar_ui.h index 62f005c..22f899b 100644 --- a/chrome/browser/ui/webui/webui_toolbar/webui_toolbar_ui.h +++ b/chrome/browser/ui/webui/webui_toolbar/webui_toolbar_ui.h
@@ -6,13 +6,18 @@ #define CHROME_BROWSER_UI_WEBUI_WEBUI_TOOLBAR_WEBUI_TOOLBAR_UI_H_ #include <memory> +#include <string_view> +#include <vector> +#include "base/gtest_prod_util.h" +#include "base/memory/raw_ptr.h" #include "chrome/browser/ui/webui/top_chrome/top_chrome_web_ui_controller.h" #include "chrome/browser/ui/webui/top_chrome/top_chrome_webui_config.h" -#include "chrome/browser/ui/webui/webui_toolbar/adapters/navigation_controls_state_fetcher.h" #include "chrome/browser/ui/webui/webui_toolbar/browser_controls_service.h" +#include "chrome/browser/ui/webui/webui_toolbar/toolbar_ui_service.h" #include "components/browser_apis/browser_controls/browser_controls_api.mojom-forward.h" #include "components/browser_apis/browser_controls/browser_controls_api_data_model.mojom.h" +#include "components/browser_apis/ui_controllers/toolbar/toolbar_ui_api.mojom.h" #include "content/public/browser/webui_config.h" #include "content/public/common/url_constants.h" #include "mojo/public/cpp/bindings/pending_receiver.h" @@ -28,7 +33,7 @@ namespace gfx { class FontList; } // namespace gfx - // + class CommandUpdater; // The webui controller for the webui toolbar. This class has a two part @@ -39,10 +44,12 @@ // Provides dependencies to this controller during init. class DependencyProvider { public: - virtual browser_controls_api::BrowserControlsService::Delegate* - GetDelegate() = 0; - virtual std::unique_ptr< - browser_controls_api::NavigationControlsStateFetcher> + virtual browser_controls_api::BrowserControlsService:: + BrowserControlsServiceDelegate* + GetBrowserControlsDelegate() = 0; + virtual toolbar_ui_api::ToolbarUIService::ToolbarUIServiceDelegate* + GetToolbarUIServiceDelegate() = 0; + virtual std::unique_ptr<toolbar_ui_api::NavigationControlsStateFetcher> GetNavigationControlsStateFetcher() = 0; }; @@ -58,17 +65,18 @@ receiver); void BindInterface( + mojo::PendingReceiver<toolbar_ui_api::mojom::ToolbarUIService> receiver); + + void BindInterface( mojo::PendingReceiver<tracked_element::mojom::TrackedElementHandler> receiver); void OnNavigationControlsStateChanged( - browser_controls_api::mojom::NavigationControlsStatePtr state); + toolbar_ui_api::mojom::NavigationControlsStatePtr state); // The |depdency_provider| is expected to outlive this class. void Init(DependencyProvider* dependency_provider); - browser_controls_api::BrowserControlsService* - browser_controls_service_for_testing(); // TopChromeWebUIController: // The controller uses `requesting_origin` to: @@ -96,6 +104,15 @@ content::WebUIDataSource* source); private: + FRIEND_TEST_ALL_PREFIXES(WebUIToolbarUITest, + BindInterfaceBrowserControlsService); + FRIEND_TEST_ALL_PREFIXES(WebUIToolbarUITest, BindInterfaceToolbarUIService); + FRIEND_TEST_ALL_PREFIXES(WebUIToolbarUITest, CreateBrowserControlsService); + FRIEND_TEST_ALL_PREFIXES(WebUIToolbarUITest, CreateToolbarUIService); + FRIEND_TEST_ALL_PREFIXES(WebUIToolbarUITest, + CreateBrowserControlsService_NullCommandUpdater); + FRIEND_TEST_ALL_PREFIXES(WebUIToolbarUITest, + CreateToolbarUIService_NullCommandUpdater); CommandUpdater* GetCommandUpdater() const; // Returns the list of known element identifiers. These elements are HTML @@ -105,6 +122,7 @@ std::unique_ptr<browser_controls_api::BrowserControlsService> browser_controls_service_; + std::unique_ptr<toolbar_ui_api::ToolbarUIService> toolbar_ui_service_; std::unique_ptr<ui::TrackedElementHandler> tracked_element_handler_; raw_ptr<DependencyProvider> dependency_provider_;
diff --git a/chrome/browser/ui/webui/webui_toolbar/webui_toolbar_ui_browsertest.cc b/chrome/browser/ui/webui/webui_toolbar/webui_toolbar_ui_browsertest.cc index 6c0714e..3ad0745 100644 --- a/chrome/browser/ui/webui/webui_toolbar/webui_toolbar_ui_browsertest.cc +++ b/chrome/browser/ui/webui/webui_toolbar/webui_toolbar_ui_browsertest.cc
@@ -7,6 +7,7 @@ #include <memory> #include <utility> +#include "base/run_loop.h" #include "chrome/browser/ui/webui/theme_colors_source_manager.h" #include "chrome/browser/ui/webui/theme_colors_source_manager_factory.h" #include "chrome/browser/ui/webui/webui_embedding_context.h" @@ -31,22 +32,22 @@ namespace { // Helper class to manage mojo remote to the WebUIToolbarUI. -class MockBrowserControlsServiceConnection { +class ToolbarUIServiceConnectionManager { public: - explicit MockBrowserControlsServiceConnection(WebUIToolbarUI* ui) { + explicit ToolbarUIServiceConnectionManager(WebUIToolbarUI* ui) { ui->BindInterface(service_remote_.BindNewPipeAndPassReceiver()); } // Not movable or copyable. - MockBrowserControlsServiceConnection( - const MockBrowserControlsServiceConnection&) = delete; - MockBrowserControlsServiceConnection& operator=( - const MockBrowserControlsServiceConnection&) = delete; + ToolbarUIServiceConnectionManager(const ToolbarUIServiceConnectionManager&) = + delete; + ToolbarUIServiceConnectionManager& operator=( + const ToolbarUIServiceConnectionManager&) = delete; void RegisterObserver() { service_remote_->Bind(base::BindOnce( - [](MockBrowserControlsServiceConnection* self, - base::expected<browser_controls_api::mojom::InitialStatePtr, + [](ToolbarUIServiceConnectionManager* self, + base::expected<toolbar_ui_api::mojom::InitialStatePtr, mojo_base::mojom::ErrorPtr> result) { ASSERT_TRUE(result.has_value()); self->mock_observer_.Bind(std::move(result.value()->update_stream)); @@ -64,21 +65,52 @@ private: testing::StrictMock<MockReloadButtonPage> mock_observer_; + mojo::Remote<toolbar_ui_api::mojom::ToolbarUIService> service_remote_; +}; + +// Helper class to manage mojo remote to the WebUIToolbarUI. +class BrowserControlsServiceConnectionManager { + public: + explicit BrowserControlsServiceConnectionManager(WebUIToolbarUI* ui) { + ui->BindInterface(service_remote_.BindNewPipeAndPassReceiver()); + } + + // Not movable or copyable. + BrowserControlsServiceConnectionManager( + const BrowserControlsServiceConnectionManager&) = delete; + BrowserControlsServiceConnectionManager& operator=( + const BrowserControlsServiceConnectionManager&) = delete; + + void FlushForTesting() { service_remote_.FlushForTesting(); } + + bool is_bound() { return service_remote_.is_bound(); } + bool is_connected() { return service_remote_.is_connected(); } + + private: mojo::Remote<browser_controls_api::mojom::BrowserControlsService> service_remote_; }; class BrowserControlsDelegate - : public browser_controls_api::BrowserControlsService::Delegate { + : public browser_controls_api::BrowserControlsService:: + BrowserControlsServiceDelegate { public: BrowserControlsDelegate() = default; ~BrowserControlsDelegate() override = default; - void HandleContextMenu(browser_controls_api::mojom::ContextMenuType menu_type, + void PermitLaunchUrl() override {} +}; + +class ToolbarUIDelegate + : public toolbar_ui_api::ToolbarUIService::ToolbarUIServiceDelegate { + public: + ToolbarUIDelegate() = default; + ~ToolbarUIDelegate() override = default; + + void HandleContextMenu(toolbar_ui_api::mojom::ContextMenuType menu_type, gfx::Point viewport_coordinate_css_pixels, ui::mojom::MenuSourceType source) override {} void OnPageInitialized() override {} - void PermitLaunchUrl() override {} }; // Test fixture for WebUIToolbarUI. These tests test the connectivity between @@ -119,14 +151,17 @@ } // WebUIToolbarUI::DependencyProvider: - browser_controls_api::BrowserControlsService::Delegate* GetDelegate() - override { - return &delegate_; + browser_controls_api::BrowserControlsService::BrowserControlsServiceDelegate* + GetBrowserControlsDelegate() override { + return &browser_controls_delegate_; } - std::unique_ptr<browser_controls_api::NavigationControlsStateFetcher> + toolbar_ui_api::ToolbarUIService::ToolbarUIServiceDelegate* + GetToolbarUIServiceDelegate() override { + return &toolbar_ui_delegate_; + } + std::unique_ptr<toolbar_ui_api::NavigationControlsStateFetcher> GetNavigationControlsStateFetcher() override { - return std::make_unique< - browser_controls_api::NavigationControlsStateFetcherImpl>( + return std::make_unique<toolbar_ui_api::NavigationControlsStateFetcherImpl>( base::BindRepeating( [&] { return CreateValidNavigationControlsState(); })); } @@ -136,7 +171,8 @@ private: base::test::ScopedFeatureList feature_list_; - BrowserControlsDelegate delegate_; + BrowserControlsDelegate browser_controls_delegate_; + ToolbarUIDelegate toolbar_ui_delegate_; std::unique_ptr<content::TestWebUI> web_ui_; std::unique_ptr<WebUIToolbarUI> ui_; }; @@ -144,20 +180,19 @@ // Tests that OnNavigationControlsStateChanged calls the browser controls // observer with the correct parameters. IN_PROC_BROWSER_TEST_F(WebUIToolbarUIBrowserTest, SetReloadButtonState) { - MockBrowserControlsServiceConnection connection(ui()); + ToolbarUIServiceConnectionManager connection(ui()); auto state = CreateValidNavigationControlsState(); state->reload_control_state->is_navigation_loading = true; connection.RegisterObserver(); - EXPECT_CALL(connection.mock_observer(), - OnNavigationControlsStateChanged(testing::Pointee(testing::Field( - &browser_controls_api::mojom::NavigationControlsState:: - reload_control_state, - testing::Pointee(testing::Field( - &browser_controls_api::mojom::ReloadControlState:: - is_navigation_loading, - true)))))) + EXPECT_CALL( + connection.mock_observer(), + OnNavigationControlsStateChanged(testing::Pointee(testing::Field( + &toolbar_ui_api::mojom::NavigationControlsState::reload_control_state, + testing::Pointee(testing::Field( + &toolbar_ui_api::mojom::ReloadControlState::is_navigation_loading, + true)))))) .Times(1); ui()->OnNavigationControlsStateChanged(std::move(state)); connection.mock_observer().FlushForTesting(); @@ -165,18 +200,35 @@ // Tests that the BindInterface method for BrowserControlsService works // correctly. -IN_PROC_BROWSER_TEST_F(WebUIToolbarUIBrowserTest, BindService) { - MockBrowserControlsServiceConnection connection(ui()); +IN_PROC_BROWSER_TEST_F(WebUIToolbarUIBrowserTest, BindBrowserControlsService) { + BrowserControlsServiceConnectionManager connection(ui()); + + EXPECT_TRUE(connection.is_bound()); + EXPECT_TRUE(connection.is_connected()); +} + +// Tests that the BindInterface method for ToolbarUIService works +// correctly. +IN_PROC_BROWSER_TEST_F(WebUIToolbarUIBrowserTest, BindToolbarUIService) { + ToolbarUIServiceConnectionManager connection(ui()); EXPECT_TRUE(connection.is_bound()); EXPECT_TRUE(connection.is_connected()); } // Tests that connecting to the service instantiates the BrowserControlsService. -IN_PROC_BROWSER_TEST_F(WebUIToolbarUIBrowserTest, CreateService) { - MockBrowserControlsServiceConnection connection(ui()); +IN_PROC_BROWSER_TEST_F(WebUIToolbarUIBrowserTest, + CreateBrowserControlsService) { + BrowserControlsServiceConnectionManager connection(ui()); - EXPECT_THAT(ui()->browser_controls_service_for_testing(), testing::NotNull()); + EXPECT_TRUE(connection.is_connected()); +} + +// Tests that connecting to the service instantiates the ToolbarUIService. +IN_PROC_BROWSER_TEST_F(WebUIToolbarUIBrowserTest, CreateToolbarUIService) { + ToolbarUIServiceConnectionManager connection(ui()); + + EXPECT_TRUE(connection.is_connected()); } // Tests that Service creation handles a null CommandUpdater gracefully. @@ -192,7 +244,7 @@ web_ui()->set_web_contents(dummy_content.get()); - MockBrowserControlsServiceConnection connection(ui()); + BrowserControlsServiceConnectionManager connection(ui()); // This line is necessary, because there is a defect in mojo's is_connected() // which erroneously return true without this, even if the remote is not // actually connected.
diff --git a/chrome/build/android-arm32.pgo.txt b/chrome/build/android-arm32.pgo.txt index e7ba1327..12bf721 100644 --- a/chrome/build/android-arm32.pgo.txt +++ b/chrome/build/android-arm32.pgo.txt
@@ -1 +1 @@ -chrome-android32-main-1772215198-55b1e814606ae26f17dd80d83724051e0fd912ff-c1d741b15fbbb33be4ab55ba150d481080e6c83b.profdata +chrome-android32-main-1772253278-aa6de317dad5d5d4e7914bfa50fcfe200bb5aa81-3633b670e86af329be8ecfe3d73ba9f927f48bb3.profdata
diff --git a/chrome/build/android-desktop-x64.pgo.txt b/chrome/build/android-desktop-x64.pgo.txt index 195300cf..aa05835 100644 --- a/chrome/build/android-desktop-x64.pgo.txt +++ b/chrome/build/android-desktop-x64.pgo.txt
@@ -1 +1 @@ -chrome-android-desktop-x64-main-1772215198-03242c792579d32531d5810e0404f84e64aea45d-c1d741b15fbbb33be4ab55ba150d481080e6c83b.profdata +chrome-android-desktop-x64-main-1772236775-7b6cfce113f300048aa89b8b79a6a9f09e515b2c-a0f393d920ee0ffde8f21326fd8e77fbfdd17956.profdata
diff --git a/chrome/build/linux.pgo.txt b/chrome/build/linux.pgo.txt index ce0832b..57fa5d2 100644 --- a/chrome/build/linux.pgo.txt +++ b/chrome/build/linux.pgo.txt
@@ -1 +1 @@ -chrome-linux-main-1772215198-bdc4b2f2b7c274e320a7cf09bd275900cc667a9c-c1d741b15fbbb33be4ab55ba150d481080e6c83b.profdata +chrome-linux-main-1772236775-97328bc5e6d19ce7c22898fbc27c22e23ee0a12b-a0f393d920ee0ffde8f21326fd8e77fbfdd17956.profdata
diff --git a/chrome/build/mac-arm.pgo.txt b/chrome/build/mac-arm.pgo.txt index 51576f4f..ffe2470 100644 --- a/chrome/build/mac-arm.pgo.txt +++ b/chrome/build/mac-arm.pgo.txt
@@ -1 +1 @@ -chrome-mac-arm-main-1772229576-5f973b7e08b34e8902f3d9d5706aee7d97435c5a-b2513bbf80c7e1a417bfc15796db5bce80c98a37.profdata +chrome-mac-arm-main-1772253278-9d63e83bbdb4157781b1affe4f08a318ab6a3ac8-3633b670e86af329be8ecfe3d73ba9f927f48bb3.profdata
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt index f6e7e525..a341d00d 100644 --- a/chrome/build/mac.pgo.txt +++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@ -chrome-mac-main-1772193450-1dc8f8da3202b18663507f2c9cf5a32ee404348b-4df9eebf17daee51c0decb393c6aac2cc86bf7c9.profdata +chrome-mac-main-1772253278-6d849fb6b91517f8151995fcdcec3b7f65c2c27a-3633b670e86af329be8ecfe3d73ba9f927f48bb3.profdata
diff --git a/chrome/build/win-arm64.pgo.txt b/chrome/build/win-arm64.pgo.txt index b6e494b41..fada1b9 100644 --- a/chrome/build/win-arm64.pgo.txt +++ b/chrome/build/win-arm64.pgo.txt
@@ -1 +1 @@ -chrome-win-arm64-main-1772215198-d27adaf22adcf9dc47b7b2ade2442bbce753b9ae-c1d741b15fbbb33be4ab55ba150d481080e6c83b.profdata +chrome-win-arm64-main-1772253278-d9c5e62094cf84e551a3605bcb00c73b7a0f7723-3633b670e86af329be8ecfe3d73ba9f927f48bb3.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt index a36a266..8ca1cf1 100644 --- a/chrome/build/win32.pgo.txt +++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@ -chrome-win32-main-1772215198-1ac559f8ef7c73841d5dbf73bf8821988c8fdd2f-c1d741b15fbbb33be4ab55ba150d481080e6c83b.profdata +chrome-win32-main-1772247500-aff4241956ae836e92b1c6d60727d8cc6dfb8980-da952433df6bb86b50d03dd9d54ad399b1b90ffc.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt index 95684d7..9d0b1ed 100644 --- a/chrome/build/win64.pgo.txt +++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@ -chrome-win64-main-1772215198-3d16dd670474a2977dc3cf78b91b275bad70451a-c1d741b15fbbb33be4ab55ba150d481080e6c83b.profdata +chrome-win64-main-1772247500-0b6f95fb586cd21e01acf13cd6d68e853d63cf35-da952433df6bb86b50d03dd9d54ad399b1b90ffc.profdata
diff --git a/chrome/common/extensions/api/_permission_features.json b/chrome/common/extensions/api/_permission_features.json index 7dc23a3..b43fe9e 100644 --- a/chrome/common/extensions/api/_permission_features.json +++ b/chrome/common/extensions/api/_permission_features.json
@@ -191,9 +191,7 @@ }, "contentSettings": { "channel": "stable", - "extension_types": ["extension", "legacy_packaged_app"], - // "desktop_android" is not supported. - "platforms": ["chromeos", "linux", "mac", "win"] + "extension_types": ["extension", "legacy_packaged_app"] }, "contextMenus": { "channel": "stable",
diff --git a/chrome/common/extensions/api/api_sources.gni b/chrome/common/extensions/api/api_sources.gni index 1bb3d09f..1d45b2e1 100644 --- a/chrome/common/extensions/api/api_sources.gni +++ b/chrome/common/extensions/api/api_sources.gni
@@ -14,6 +14,7 @@ schema_sources_ = [ "activity_log_private.json", "bookmarks.json", + "content_settings.json", "context_menus.json", "cookies.json", "debugger.json", @@ -81,7 +82,6 @@ "bookmark_manager_private.json", "braille_display_private.idl", "command_line_private.json", - "content_settings.json", "crash_report_private.idl", "enterprise_reporting_private.idl", "proxy_override_rules_private.json",
diff --git a/chrome/renderer/safe_browsing/phishing_classifier_delegate_browsertest.cc b/chrome/renderer/safe_browsing/phishing_classifier_delegate_browsertest.cc index 33c854a..5adbda1 100644 --- a/chrome/renderer/safe_browsing/phishing_classifier_delegate_browsertest.cc +++ b/chrome/renderer/safe_browsing/phishing_classifier_delegate_browsertest.cc
@@ -14,9 +14,7 @@ #include "base/memory/raw_ptr.h" #include "base/memory/ref_counted_memory.h" #include "base/memory/scoped_refptr.h" -#include "base/run_loop.h" #include "base/strings/utf_string_conversions.h" -#include "base/test/gmock_callback_support.h" #include "base/test/scoped_feature_list.h" #include "chrome/test/base/chrome_render_view_test.h" #include "chrome/test/base/chrome_unit_test_suite.h" @@ -220,11 +218,9 @@ const auto page_text = MakeRefPtrString(u"dummy"); const auto page_text2 = MakeRefPtrString(u"dummy2"); { - base::RunLoop run_loop; - EXPECT_CALL(*classifier_, BeginClassification(_)) - .WillOnce(base::test::RunOnceClosure(run_loop.QuitClosure())); + InSequence s; + EXPECT_CALL(*classifier_, BeginClassification(_)); delegate_->PageCaptured(page_text, false); - run_loop.Run(); Mock::VerifyAndClearExpectations(classifier_); } @@ -235,37 +231,20 @@ // Start phishing detection without a fresh page text should still classify, // because the top level URL still match. - { - base::RunLoop run_loop; - EXPECT_CALL(*classifier_, BeginClassification(_)) - .WillOnce(base::test::RunOnceClosure(run_loop.QuitClosure())); - OnStartPhishingDetection(url); - delegate_->PageCaptured(page_text, false); - run_loop.Run(); - Mock::VerifyAndClearExpectations(classifier_); - } + EXPECT_CALL(*classifier_, BeginClassification(_)); + OnStartPhishingDetection(url); + delegate_->PageCaptured(page_text, false); + Mock::VerifyAndClearExpectations(classifier_); - // Even if the page text is captured first, it shouldn't matter since the - // browser request will start the classification. - { - base::RunLoop run_loop; - EXPECT_CALL(*classifier_, BeginClassification(_)) - .WillOnce(base::test::RunOnceClosure(run_loop.QuitClosure())); - delegate_->PageCaptured(page_text, false); - OnStartPhishingDetection(url); - run_loop.Run(); - Mock::VerifyAndClearExpectations(classifier_); - } + EXPECT_CALL(*classifier_, BeginClassification(_)); + OnStartPhishingDetection(url); + delegate_->PageCaptured(page_text, false); + Mock::VerifyAndClearExpectations(classifier_); - { - base::RunLoop run_loop; - EXPECT_CALL(*classifier_, BeginClassification(_)) - .WillOnce(base::test::RunOnceClosure(run_loop.QuitClosure())); - OnStartPhishingDetection(url); - delegate_->PageCaptured(page_text, false); - run_loop.Run(); - Mock::VerifyAndClearExpectations(classifier_); - } + EXPECT_CALL(*classifier_, BeginClassification(_)); + OnStartPhishingDetection(url); + delegate_->PageCaptured(page_text, false); + Mock::VerifyAndClearExpectations(classifier_); // Now load a new toplevel page, which should trigger another classification. EXPECT_CALL(*classifier_, CancelPendingClassification()); @@ -276,11 +255,9 @@ OnStartPhishingDetection(new_url); { - base::RunLoop run_loop; - EXPECT_CALL(*classifier_, BeginClassification(_)) - .WillOnce(base::test::RunOnceClosure(run_loop.QuitClosure())); + InSequence s; + EXPECT_CALL(*classifier_, BeginClassification(_)); delegate_->PageCaptured(page_text2, false); - run_loop.Run(); Mock::VerifyAndClearExpectations(classifier_); } @@ -389,20 +366,14 @@ // Now set a scorer, which should cause a classifier to be created, and // classification will happen again because the scorer is set within timeout. - base::RunLoop run_loop; - EXPECT_CALL(*classifier_, BeginClassification(_)) - .WillOnce(base::test::RunOnceClosure(run_loop.QuitClosure())); + EXPECT_CALL(*classifier_, BeginClassification(_)); SetScorer(/*model_version=*/1); - run_loop.Run(); Mock::VerifyAndClearExpectations(classifier_); // Manually start a classification, so that when a new scorer is set, it // should cancel. - base::RunLoop run_loop2; - EXPECT_CALL(*classifier_, BeginClassification(_)) - .WillOnce(base::test::RunOnceClosure(run_loop2.QuitClosure())); + EXPECT_CALL(*classifier_, BeginClassification(_)); OnStartPhishingDetection(url2); - run_loop2.Run(); // If we set a new scorer while a classification is going on the // classification should be cancelled. @@ -432,24 +403,16 @@ OnStartPhishingDetection(url); delegate_->PageCaptured(page_text, false); - task_environment_.RunUntilIdle(); - // Now set a scorer, which should cause a classifier to be created, and // classification will happen again because the scorer is set within timeout. - base::RunLoop run_loop; - EXPECT_CALL(*classifier_, BeginClassification(_)) - .WillOnce(base::test::RunOnceClosure(run_loop.QuitClosure())); + EXPECT_CALL(*classifier_, BeginClassification(_)); SetScorer(/*model_version=*/1); - run_loop.Run(); Mock::VerifyAndClearExpectations(classifier_); // Manually start a classification, so that when a new scorer is set, it // should cancel. - base::RunLoop run_loop2; - EXPECT_CALL(*classifier_, BeginClassification(_)) - .WillOnce(base::test::RunOnceClosure(run_loop2.QuitClosure())); + EXPECT_CALL(*classifier_, BeginClassification(_)); OnStartPhishingDetection(url); - run_loop2.Run(); // If we set a new scorer while a classification is going on the // classification should be cancelled. @@ -492,11 +455,8 @@ // Manually start a classification, so that when a new scorer is set, it // should cancel. - base::RunLoop run_loop; - EXPECT_CALL(*classifier_, BeginClassification(_)) - .WillOnce(base::test::RunOnceClosure(run_loop.QuitClosure())); + EXPECT_CALL(*classifier_, BeginClassification(_)); OnStartPhishingDetection(url2); - run_loop.Run(); // If we set a new scorer while a classification is going on the // classification should be cancelled. @@ -537,11 +497,8 @@ Mock::VerifyAndClearExpectations(classifier_); // Manually start a classification - base::RunLoop run_loop; - EXPECT_CALL(*classifier_, BeginClassification(_)) - .WillOnce(base::test::RunOnceClosure(run_loop.QuitClosure())); + EXPECT_CALL(*classifier_, BeginClassification(_)); OnStartPhishingDetection(url); - run_loop.Run(); // If we set a new scorer while a classification is going on the // classification should be cancelled. @@ -594,11 +551,8 @@ Mock::VerifyAndClearExpectations(classifier_); // Now simulate the StartPhishingDetection IPC. We expect classification // to begin. - base::RunLoop run_loop; - EXPECT_CALL(*classifier_, BeginClassification(_)) - .WillOnce(base::test::RunOnceClosure(run_loop.QuitClosure())); + EXPECT_CALL(*classifier_, BeginClassification(_)); OnStartPhishingDetection(url); - run_loop.Run(); Mock::VerifyAndClearExpectations(classifier_); // Now try again, but this time we will navigate the page away before @@ -632,30 +586,18 @@ Mock::VerifyAndClearExpectations(classifier_); EXPECT_CALL(*classifier_, CancelPendingClassification()); - // Now the redirecting URL HTML has been loaded. GURL redir_url("http://host4.com/redir"); LoadHTMLWithUrlOverride("123", redir_url.spec().c_str()); Mock::VerifyAndClearExpectations(classifier_); - // Although the redirecting URL HTML has already loaded, browser requested - // from the original URL, but it shouldn't trigger anything, if with new - // observers. - if (!base::FeatureList::IsEnabled(kClientSideDetectionNewObservers)) { - EXPECT_CALL(*classifier_, BeginClassification(_)); - } + EXPECT_CALL(*classifier_, BeginClassification(_)); OnStartPhishingDetection(url4); page_text = MakeRefPtrString(u"123"); { - base::RunLoop run_loop2; - EXPECT_CALL(*classifier_, BeginClassification(_)) - .WillOnce(base::test::RunOnceClosure(run_loop2.QuitClosure())); - // The below essentially called OnStartPhishingDetection by replacing the - // URL. + InSequence s; + EXPECT_CALL(*classifier_, BeginClassification(_)); SimulateRedirection(redir_url); - // Page has finally captured for the redirecting URL. With the layout - // complete, it will start classification on landing page. delegate_->PageCaptured(page_text, false); - run_loop2.Run(); Mock::VerifyAndClearExpectations(classifier_); } @@ -681,11 +623,9 @@ // Once the non-preliminary capture happens, classification should begin. { - base::RunLoop run_loop; - EXPECT_CALL(*classifier_, BeginClassification(_)) - .WillOnce(base::test::RunOnceClosure(run_loop.QuitClosure())); + InSequence s; + EXPECT_CALL(*classifier_, BeginClassification(_)); delegate_->PageCaptured(page_text, false); - run_loop.Run(); Mock::VerifyAndClearExpectations(classifier_); } @@ -709,11 +649,9 @@ Mock::VerifyAndClearExpectations(classifier_); OnStartPhishingDetection(url); { - base::RunLoop run_loop; - EXPECT_CALL(*classifier_, BeginClassification(_)) - .WillOnce(base::test::RunOnceClosure(run_loop.QuitClosure())); + InSequence s; + EXPECT_CALL(*classifier_, BeginClassification(_)); delegate_->PageCaptured(page_text, false); - run_loop.Run(); Mock::VerifyAndClearExpectations(classifier_); } @@ -740,11 +678,9 @@ Mock::VerifyAndClearExpectations(classifier_); OnStartPhishingDetection(url); { - base::RunLoop run_loop; - EXPECT_CALL(*classifier_, BeginClassification(_)) - .WillOnce(base::test::RunOnceClosure(run_loop.QuitClosure())); + InSequence s; + EXPECT_CALL(*classifier_, BeginClassification(_)); delegate_->PageCaptured(page_text, false); - run_loop.Run(); Mock::VerifyAndClearExpectations(classifier_); }
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn index 9e8cf73..4520fa09 100644 --- a/chrome/test/BUILD.gn +++ b/chrome/test/BUILD.gn
@@ -1608,6 +1608,7 @@ "../browser/extensions/api/alarms/alarms_apitest.cc", "../browser/extensions/api/bookmarks/bookmarks_apitest.cc", "../browser/extensions/api/browsing_data/browsing_data_test.cc", + "../browser/extensions/api/content_settings/content_settings_apitest.cc", "../browser/extensions/api/context_menus/context_menu_apitest.cc", "../browser/extensions/api/context_menus/extension_context_menu_browsertest.cc", "../browser/extensions/api/cookies/cookies_apitest.cc", @@ -1743,6 +1744,7 @@ "//chrome/browser/extensions", "//chrome/browser/extensions:test_support", "//chrome/browser/extensions/api/bookmarks/test:test_support", + "//chrome/browser/permissions", "//chrome/browser/prefetch", "//chrome/browser/prefs", "//chrome/browser/prefs:util", @@ -4865,7 +4867,6 @@ "../browser/extensions/api/bluetooth/bluetooth_private_apitest.cc", "../browser/extensions/api/braille_display_private/braille_display_private_apitest.cc", "../browser/extensions/api/command_line_private/command_line_private_apitest.cc", - "../browser/extensions/api/content_settings/content_settings_apitest.cc", "../browser/extensions/api/declarative/declarative_apitest.cc", "../browser/extensions/api/extension_action/browser_action_apitest.cc", "../browser/extensions/api/extension_action/browser_action_browsertest.cc",
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/browser/history/StubbedHistoryProvider.java b/chrome/test/android/javatests/src/org/chromium/chrome/browser/history/StubbedHistoryProvider.java index 9732c20..83933579 100644 --- a/chrome/test/android/javatests/src/org/chromium/chrome/browser/history/StubbedHistoryProvider.java +++ b/chrome/test/android/javatests/src/org/chromium/chrome/browser/history/StubbedHistoryProvider.java
@@ -170,6 +170,18 @@ } public static HistoryItem createHistoryItem(int which, long timestamp) { + return createHistoryItem(which, timestamp, which == 5, false); + } + + /** + * @param which Which history item to create. + * @param timestamp The timestamp for the item. + * @param blockedVisit Whether the visit was blocked. + * @param isActorVisit Whether the visit was actor initiated. + * @return A new HistoryItem. + */ + public static HistoryItem createHistoryItem( + int which, long timestamp, boolean blockedVisit, boolean isActorVisit) { long[] nativeTimestamps = {timestamp * 1000}; String appId = null; if (which == 0) { @@ -180,8 +192,8 @@ appId, timestamp, nativeTimestamps, - false, - false); + blockedVisit, + isActorVisit); } else if (which == 1) { return new HistoryItem( JUnitTestGURLs.EXAMPLE_URL, @@ -190,8 +202,8 @@ appId, timestamp, nativeTimestamps, - false, - false); + blockedVisit, + isActorVisit); } else if (which == 2) { return new HistoryItem( JUnitTestGURLs.URL_1, @@ -200,8 +212,8 @@ appId, timestamp, nativeTimestamps, - false, - false); + blockedVisit, + isActorVisit); } else if (which == 3) { return new HistoryItem( JUnitTestGURLs.URL_2, @@ -210,8 +222,8 @@ appId, timestamp, nativeTimestamps, - false, - false); + blockedVisit, + isActorVisit); } else if (which == 4) { return new HistoryItem( JUnitTestGURLs.URL_3, @@ -220,8 +232,8 @@ appId, timestamp, nativeTimestamps, - false, - false); + blockedVisit, + isActorVisit); } else if (which == 5) { return new HistoryItem( JUnitTestGURLs.INITIAL_URL, @@ -230,8 +242,8 @@ appId, timestamp, nativeTimestamps, - true, - false); + blockedVisit, + isActorVisit); } else { return null; }
diff --git a/chrome/test/data/webui/history/history_synced_tabs_test.ts b/chrome/test/data/webui/history/history_synced_tabs_test.ts index e89f771d..a5a7225 100644 --- a/chrome/test/data/webui/history/history_synced_tabs_test.ts +++ b/chrome/test/data/webui/history/history_synced_tabs_test.ts
@@ -28,9 +28,9 @@ function assertNoSyncedTabsMessageShown( manager: HistorySyncedDeviceManagerElement, stringID: string) { - assertFalse(manager.$['no-synced-tabs'].hidden); + assertFalse(manager.$.noSyncedTabs.hidden); const message = loadTimeData.getString(stringID); - assertNotEquals(-1, manager.$['no-synced-tabs'].textContent.indexOf(message)); + assertNotEquals(-1, manager.$.noSyncedTabs.textContent.indexOf(message)); } suite('<history-synced-device-manager>', function() { @@ -267,14 +267,14 @@ historySync: SyncState.TURNED_OFF, }); await microtasksFinished(); - assertFalse(element.$['sign-in-guide'].hidden); + assertFalse(element.$.signInGuide.hidden); webUIListenerCallback('history-identity-state-changed', { signIn: HistorySignInState.SIGNED_IN, tabsSync: SyncState.TURNED_ON, historySync: SyncState.TURNED_OFF, }); await microtasksFinished(); - assertTrue(element.$['sign-in-guide'].hidden); + assertTrue(element.$.signInGuide.hidden); }); // </if> @@ -288,7 +288,7 @@ }); element.clearSyncedDevicesForTest(); await microtasksFinished(); - assertTrue(element.$['no-synced-tabs'].hidden); + assertTrue(element.$.noSyncedTabs.hidden); let cards = getCards(element); assertEquals(0, cards.length); @@ -318,7 +318,7 @@ cards = getCards(element); assertEquals(1, cards.length); // If there are any synced tabs, hide the 'no synced tabs' message. - assertTrue(element.$['no-synced-tabs'].hidden); + assertTrue(element.$.noSyncedTabs.hidden); webUIListenerCallback('history-identity-state-changed', { signIn: HistorySignInState.SIGNED_OUT, @@ -327,7 +327,7 @@ }); await microtasksFinished(); // When user signs out, don't show the message. - assertTrue(element.$['no-synced-tabs'].hidden); + assertTrue(element.$.noSyncedTabs.hidden); }); test('hide sign in promo in guest mode', async () => { @@ -341,7 +341,7 @@ historySync: SyncState.TURNED_OFF, }); await microtasksFinished(); - assertTrue(element.$['sign-in-guide'].hidden); + assertTrue(element.$.signInGuide.hidden); }); test('hide sign-in promo if sign-in is disabled', async function() { @@ -355,7 +355,7 @@ guestSession: false, }); await microtasksFinished(); - assertTrue(element.$['sign-in-guide'].hidden); + assertTrue(element.$.signInGuide.hidden); }); test('no synced tabs message displays on load', async () => { @@ -436,7 +436,7 @@ }); await microtasksFinished(); // Should not be visible with kReplaceSyncPromosWithSignInPromos enabled. - assertFalse(isChildVisible(element, '#sign-in-guide')); + assertFalse(isChildVisible(element, '#signInGuide')); // The other states promo elements should not be visible. assertFalse(isChildVisible(element, '#signed-in-sync-history-promo-desc')); assertFalse(isChildVisible(element, '#verify-its-you-button')); @@ -502,7 +502,7 @@ await microtasksFinished(); // The 'no synced tabs' message should be shown. - assertTrue(isChildVisible(element, '#no-synced-tabs')); + assertTrue(isChildVisible(element, '#noSyncedTabs')); // The promo elements are not shown assertFalse(isChildVisible(element, '#history-sync-optin'));
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 43763db..390d9c7 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
@@ -27,6 +27,8 @@ const ADD_FILE_CONTEXT_FN = 'addFileContext'; const ADD_TAB_CONTEXT_FN = 'addTabContext'; const FAKE_TOKEN_STRING = '00000000000000001234567890ABCDEF'; +const FAKE_TOKEN_STRING_2 = '00000000000000001234567890ABCDEE'; + const CONTEXT_ADDED_NTP = 'ContextualSearch.ContextAdded.ContextAddedMethod.NewTabPage'; @@ -160,6 +162,31 @@ }); } + async function addTab() { + searchboxHandler.setPromiseResolveFor( + ADD_TAB_CONTEXT_FN, FAKE_TOKEN_STRING); + + // Assert no files. + assertFalse(!!$$<HTMLElement>(composeboxElement, '#carousel')); + + const contextMenuButton = $$(composeboxElement, '#contextEntrypoint'); + assertTrue(!!contextMenuButton); + const sampleTabTitle = 'Sample Tab'; + contextMenuButton.dispatchEvent(new CustomEvent('add-tab-context', { + detail: {id: 1, title: sampleTabTitle}, + bubbles: true, + composed: true, + })); + + await searchboxHandler.whenCalled(ADD_TAB_CONTEXT_FN); + await microtasksFinished(); + const files = composeboxElement.$.carousel.files; + assertEquals(files.length, 1); + assertEquals(files[0]!.type, 'tab'); + assertEquals(files[0]!.name, sampleTabTitle); + return FAKE_TOKEN_STRING; + } + function getInputForFileType(fileType: string): HTMLInputElement { return fileType === 'application/pdf' ? composeboxElement.$.fileInputs.$.fileInput : @@ -2091,7 +2118,7 @@ // Check that only one files were added. assertEquals(1, searchboxHandler.getCallCount(ADD_FILE_CONTEXT_FN)); - // Check that the "too many files" metric was recorded (Enum value 1). + // Check that the 'too many files' metric was recorded (Enum value 1). assertEquals( 1, metrics.count( @@ -2674,27 +2701,7 @@ test('add tab context', async () => { createComposeboxElement(); - searchboxHandler.setPromiseResolveFor( - ADD_TAB_CONTEXT_FN, {low: BigInt(1), high: BigInt(2)}); - - // Assert no files. - assertFalse(!!$$<HTMLElement>(composeboxElement, '#carousel')); - - const contextMenuButton = $$(composeboxElement, '#contextEntrypoint'); - assertTrue(!!contextMenuButton); - const sampleTabTitle = 'Sample Tab'; - contextMenuButton.dispatchEvent(new CustomEvent('add-tab-context', { - detail: {id: 1, title: sampleTabTitle}, - bubbles: true, - composed: true, - })); - - await searchboxHandler.whenCalled(ADD_TAB_CONTEXT_FN); - await microtasksFinished(); - const files = composeboxElement.$.carousel.files; - assertEquals(files.length, 1); - assertEquals(files[0]!.type, 'tab'); - assertEquals(files[0]!.name, sampleTabTitle); + await addTab(); }); test('add tab context fails', async () => { @@ -2919,4 +2926,57 @@ assertEquals(searchboxHandler.getCallCount('queryAutocomplete'), 1); assertEquals(searchboxHandler.getCallCount('stopAutocomplete'), 0); }); + + test('when flag enabled, adds tab context of ghost file', async () => { + createComposeboxElement(); + document.body.appendChild(composeboxElement); + composeboxElement.shouldShowGhostFiles = true; + + await addTab(); + + await composeboxElement.updateComplete; + await microtasksFinished(); + + assertTrue( + composeboxElement.getNumOfFilesForTesting() === 1, + 'Tab should be added'); + + const bad_token = FAKE_TOKEN_STRING_2; + searchboxCallbackRouterRemote.onContextualInputStatusChanged( + bad_token, + FileUploadStatus.kUploadSuccessful, + null, + ); + await composeboxElement.updateComplete; + await microtasksFinished(); + assertTrue( + composeboxElement.getNumOfFilesForTesting() === 2, + 'Ghost file should be added'); + }); + + test('does not add tab context of ghost file', async () => { + createComposeboxElement(); + document.body.appendChild(composeboxElement); + composeboxElement.shouldShowGhostFiles = false; + + await addTab(); + await composeboxElement.updateComplete; + await microtasksFinished(); + + + assertTrue( + composeboxElement.getNumOfFilesForTesting() === 1, + 'Tab should be added'); + const bad_token = FAKE_TOKEN_STRING_2; + searchboxCallbackRouterRemote.onContextualInputStatusChanged( + bad_token, + FileUploadStatus.kUploadSuccessful, + null, + ); + await composeboxElement.updateComplete; + await microtasksFinished(); + assertTrue( + composeboxElement.getNumOfFilesForTesting() === 1, + 'Ghost file should not be added'); + }); });
diff --git a/components/browser_apis/browser_controls/BUILD.gn b/components/browser_apis/browser_controls/BUILD.gn index b1700a67..a841067b8 100644 --- a/components/browser_apis/browser_controls/BUILD.gn +++ b/components/browser_apis/browser_controls/BUILD.gn
@@ -20,9 +20,6 @@ public_deps = [ ":mojom_data_model", "//mojo/public/mojom/base", - "//ui/base/mojom:ui_base_types", - "//ui/gfx/geometry/mojom", - "//url/mojom:url_mojom_gurl", ] # Generate TypeScript bindings for WebUI.
diff --git a/components/browser_apis/browser_controls/browser_controls_api.mojom b/components/browser_apis/browser_controls/browser_controls_api.mojom index 95802cf..a8884c0 100644 --- a/components/browser_apis/browser_controls/browser_controls_api.mojom +++ b/components/browser_apis/browser_controls/browser_controls_api.mojom
@@ -5,48 +5,14 @@ module browser_controls_api.mojom; import "components/browser_apis/browser_controls/browser_controls_api_data_model.mojom"; -import "ui/base/mojom/menu_source_type.mojom"; -import "ui/gfx/geometry/mojom/geometry.mojom"; -import "mojo/public/mojom/base/error.mojom"; - -// Called from C++ side of chrome://webui-toolbar.top-chrome -// (Browser -> Renderer) -interface BrowserControlsObserver { - // Observes events for navigation controls (e.g. back, forward, reload - // buttons). |state| contains the state for all navigation controls - // so that they can update together to avoid visual flicker. - OnNavigationControlsStateChanged(NavigationControlsState state); -}; - -struct InitialState { - NavigationControlsState state; - - pending_receiver<BrowserControlsObserver> update_stream; -}; // BrowserControlsService defines methods to be called from the WebUI to the // browser-side. This interface consolidates various browser control actions. interface BrowserControlsService { - // Binds an observer to the service. This is a synchronization point between - // the client and the browser. All subsequent updates after the initial - // data will be associated receiver. - Bind() => result<InitialState, mojo_base.mojom.Error>; - // Stops the current page from loading. If the page is not in a loading // state, it is a no-op. StopLoad(); - // Displays the context menu with the provided menu type at the provided - // offset in CSS pixels relative to the WebUIToolbarWebView's viewport origin. - // The source indicates what triggered showing this menu (mouse, keyboard, - // etc). - ShowContextMenu(ContextMenuType menu_type, - gfx.mojom.Point viewport_coordinate_css_pixels, - ui.mojom.MenuSourceType source); - - // Called when the page finishes initialization. - OnPageInitialized(); - // Reloads the current page. If the page is already in a loading state, // it will trigger another reload, and abort the previous one. // If `bypass_cache` is set to true, the page will be reloaded while
diff --git a/components/browser_apis/browser_controls/browser_controls_api_data_model.mojom b/components/browser_apis/browser_controls/browser_controls_api_data_model.mojom index 31fa55a..31cf4b2 100644 --- a/components/browser_apis/browser_controls/browser_controls_api_data_model.mojom +++ b/components/browser_apis/browser_controls/browser_controls_api_data_model.mojom
@@ -12,44 +12,3 @@ kAltKeyDown = 2, kMetaKeyDown = 3, }; - -// The context menu types that can be opened with BrowserControlsService's -// ShowContextMenu. -enum ContextMenuType { - kUnspecified = 0, - kReload = 1, - kSplitTabsAction = 2, - kSplitTabsContext = 3, -}; - -struct ReloadControlState { - bool is_devtools_connected; - bool is_navigation_loading; - bool is_context_menu_visible; -}; - -enum SplitTabActiveLocation { - kStart, - kEnd, - kTop, - kBottom, -}; - -enum ToolbarButtonType { - kSplitTabs, -}; - -struct SplitTabsControlState { - bool is_current_tab_split; - SplitTabActiveLocation location; - bool is_pinned; - bool is_context_menu_visible; -}; - -struct NavigationControlsState { - ReloadControlState reload_control_state; - SplitTabsControlState split_tabs_control_state; - // This is incremented every time a settings change may change the layout - // constants. - int32 layout_constants_version = 0; -};
diff --git a/components/browser_apis/ui_controllers/toolbar/BUILD.gn b/components/browser_apis/ui_controllers/toolbar/BUILD.gn new file mode 100644 index 0000000..6edab3c --- /dev/null +++ b/components/browser_apis/ui_controllers/toolbar/BUILD.gn
@@ -0,0 +1,33 @@ +# Copyright 2026 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//mojo/public/tools/bindings/mojom.gni") + +# Generate Mojo bindings for toolbar_ui_api_data_model.mojom. +mojom("mojom_data_model") { + sources = [ "toolbar_ui_api_data_model.mojom" ] + + # Generate TypeScript bindings for WebUI. + webui_module_path = "/" +} + +# Generate Mojo bindings for toolbar_ui_api.mojom. +mojom("mojom") { + sources = [ "toolbar_ui_api.mojom" ] + + # Dependencies on other mojom targets. + public_deps = [ + ":mojom_data_model", + "//mojo/public/mojom/base", + "//ui/base/mojom:ui_base_types", + "//ui/gfx/geometry/mojom", + ] + + # Generate TypeScript bindings for WebUI. + webui_module_path = "/" +} + +source_set("unit_tests") { + testonly = true +}
diff --git a/components/browser_apis/ui_controllers/toolbar/OWNERS b/components/browser_apis/ui_controllers/toolbar/OWNERS new file mode 100644 index 0000000..92d932cf --- /dev/null +++ b/components/browser_apis/ui_controllers/toolbar/OWNERS
@@ -0,0 +1,5 @@ +per-file *.mojom=set noparent +per-file *.mojom=file://ipc/SECURITY_OWNERS + +pauljensen@google.com +xtlsheep@google.com
diff --git a/components/browser_apis/ui_controllers/toolbar/toolbar_ui_api.mojom b/components/browser_apis/ui_controllers/toolbar/toolbar_ui_api.mojom new file mode 100644 index 0000000..58281f3 --- /dev/null +++ b/components/browser_apis/ui_controllers/toolbar/toolbar_ui_api.mojom
@@ -0,0 +1,43 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +module toolbar_ui_api.mojom; + +import "components/browser_apis/ui_controllers/toolbar/toolbar_ui_api_data_model.mojom"; +import "ui/base/mojom/menu_source_type.mojom"; +import "ui/gfx/geometry/mojom/geometry.mojom"; +import "mojo/public/mojom/base/error.mojom"; + +// Used by Toolbar UI to observe navigation controls state changes. +interface ToolbarUIObserver { + // Observes events for navigation controls (e.g. back, forward, reload + // buttons). |state| contains the state for all navigation controls + // so that they can update together to avoid visual flicker. + OnNavigationControlsStateChanged(NavigationControlsState state); +}; + +struct InitialState { + NavigationControlsState state; + pending_receiver<ToolbarUIObserver> update_stream; +}; + +// ToolbarUIService defines methods to be called from the WebUI to the +// browser-side to drive UI logic. +interface ToolbarUIService { + // Binds an observer to the service. This is a synchronization point between + // the client and the browser. All subsequent updates after the initial + // data will be associated receiver. + Bind() => result<InitialState, mojo_base.mojom.Error>; + + // Displays the context menu with the provided menu type at the provided + // offset in CSS pixels relative to the WebUIToolbarWebView's viewport origin. + // The source indicates what triggered showing this menu (mouse, keyboard, + // etc). + ShowContextMenu(ContextMenuType menu_type, + gfx.mojom.Point viewport_coordinate_css_pixels, + ui.mojom.MenuSourceType source); + + // Called when the page finishes initialization. + OnPageInitialized(); +};
diff --git a/components/browser_apis/ui_controllers/toolbar/toolbar_ui_api_data_model.mojom b/components/browser_apis/ui_controllers/toolbar/toolbar_ui_api_data_model.mojom new file mode 100644 index 0000000..ef5ee3e --- /dev/null +++ b/components/browser_apis/ui_controllers/toolbar/toolbar_ui_api_data_model.mojom
@@ -0,0 +1,46 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +module toolbar_ui_api.mojom; + +// The context menu types that can be opened with ToolbarUIService's +// ShowContextMenu. +enum ContextMenuType { + kUnspecified = 0, + kReload = 1, + kSplitTabsAction = 2, + kSplitTabsContext = 3, +}; + +enum SplitTabActiveLocation { + kStart, + kEnd, + kTop, + kBottom, +}; + +enum ToolbarButtonType { + kSplitTabs, +}; + +struct ReloadControlState { + bool is_devtools_connected; + bool is_navigation_loading; + bool is_context_menu_visible; +}; + +struct SplitTabsControlState { + bool is_current_tab_split; + SplitTabActiveLocation location; + bool is_pinned; + bool is_context_menu_visible; +}; + +struct NavigationControlsState { + ReloadControlState reload_control_state; + SplitTabsControlState split_tabs_control_state; + // This is incremented every time a settings change may change the layout + // constants. + int32 layout_constants_version = 0; +};
diff --git a/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerImpl.java b/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerImpl.java index ee2739f..bce37e6 100644 --- a/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerImpl.java +++ b/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerImpl.java
@@ -118,7 +118,7 @@ private @Nullable BottomSheetContent mContentWhenSuppressed; private int mAppHeaderHeight; - private int mBottomControlsHeight; + private int mBottomControlsOffset; private boolean mIsAnchoredToBottomControls; /** @@ -232,7 +232,7 @@ mAlwaysFullWidth, mEdgeToEdgeBottomInsetSupplier, mAppHeaderHeight, - mBottomControlsHeight); + mBottomControlsOffset); // Initialize the queue with a comparator that checks content priority. mContentQueue = @@ -348,9 +348,9 @@ } @Override - public void setBottomControlsHeight(int bottomControlsHeight) { - if (mBottomControlsHeight == bottomControlsHeight) return; - mBottomControlsHeight = bottomControlsHeight; + public void setBottomControlsOffset(int bottomControlsOffset) { + if (mBottomControlsOffset == bottomControlsOffset) return; + mBottomControlsOffset = bottomControlsOffset; var scrimManager = mScrimManagerSupplier.get(); if (scrimManager != null) { // Set the appropriate offset for the current scrim state. @@ -745,7 +745,7 @@ // the bottom controls. mIsAnchoredToBottomControls = true; mBottomSheetContainer.setZ(0.0f); - mBottomSheet.setBottomMargin(mBottomControlsHeight); + mBottomSheet.setBottomMargin(mBottomControlsOffset); } }
diff --git a/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerImplUnitTest.java b/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerImplUnitTest.java index a2f0e0ff..416c3a8df 100644 --- a/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerImplUnitTest.java +++ b/components/browser_ui/bottomsheet/android/internal/java/src/org/chromium/components/browser_ui/bottomsheet/BottomSheetControllerImplUnitTest.java
@@ -148,10 +148,10 @@ } @Test - public void testBottomControlsHeight() { + public void testBottomControlsOffset() { mController.runSheetInitializerForTesting(); doReturn(true).when(mBottomSheet).isSheetOpen(); - mController.setBottomControlsHeight(100); + mController.setBottomControlsOffset(100); verify(mBottomSheet).setBottomMargin(100);
diff --git a/components/browser_ui/bottomsheet/android/java/src/org/chromium/components/browser_ui/bottomsheet/ManagedBottomSheetController.java b/components/browser_ui/bottomsheet/android/java/src/org/chromium/components/browser_ui/bottomsheet/ManagedBottomSheetController.java index 25e1803..2e4854a 100644 --- a/components/browser_ui/bottomsheet/android/java/src/org/chromium/components/browser_ui/bottomsheet/ManagedBottomSheetController.java +++ b/components/browser_ui/bottomsheet/android/java/src/org/chromium/components/browser_ui/bottomsheet/ManagedBottomSheetController.java
@@ -51,8 +51,8 @@ */ void setBrowserControlsHiddenRatio(float ratio); - /** Set the current height of the bottom controls. */ - void setBottomControlsHeight(int bottomControlsHeight); + /** Set the current offset of the bottom controls. */ + void setBottomControlsOffset(int bottomControlsOffset); /** Clean up any state maintained by the controller. */ void destroy();
diff --git a/components/contextual_search/contextual_search_types.h b/components/contextual_search/contextual_search_types.h index 55a8510a..86eb83d 100644 --- a/components/contextual_search/contextual_search_types.h +++ b/components/contextual_search/contextual_search_types.h
@@ -23,7 +23,7 @@ // Upload status of a file. // GENERATED_JAVA_ENUM_PACKAGE: org.chromium.components.contextual_search -enum class FileUploadStatus { +enum class ContextUploadStatus { // Not uploaded. kNotUploaded = 0, // File being processed. @@ -44,8 +44,10 @@ kUploadReplaced = 8, }; +using FileUploadStatus = ContextUploadStatus; + // For upload error notifications and metrics. -enum class FileUploadErrorType { +enum class ContextUploadErrorType { // Unknown. kUnknown = 0, // Browser error before/during request, not covered by validation. @@ -62,6 +64,8 @@ kImageProcessingError = 6, }; +using FileUploadErrorType = ContextUploadErrorType; + // Struct containing file information for a file upload. struct FileInfo { public: @@ -93,13 +97,13 @@ // The upload status of the file. // Do not modify this field directly. - contextual_search::FileUploadStatus upload_status = - contextual_search::FileUploadStatus::kNotUploaded; + contextual_search::ContextUploadStatus upload_status = + contextual_search::ContextUploadStatus::kNotUploaded; // The error type if the upload failed. // Do not modify this field directly. - contextual_search::FileUploadErrorType upload_error_type = - contextual_search::FileUploadErrorType::kUnknown; + contextual_search::ContextUploadErrorType upload_error_type = + contextual_search::ContextUploadErrorType::kUnknown; // If populated, the url of the tab corresponding to this uploaded file. std::optional<GURL> tab_url;
diff --git a/components/contextual_search/internal/composebox_query_controller.cc b/components/contextual_search/internal/composebox_query_controller.cc index f68c812a..df6a0fed 100644 --- a/components/contextual_search/internal/composebox_query_controller.cc +++ b/components/contextual_search/internal/composebox_query_controller.cc
@@ -512,8 +512,8 @@ // Add the "cvst" lns mode param to the url. if (is_aim_search) { - search_url_request_info->additional_params.insert( - {kLnsModeQueryParameterKey, kLnsModeQueryParameterValue}); + search_url_request_info->additional_params[kLnsModeQueryParameterKey] = + kLnsModeQueryParameterValue; } // Get the encoded visual search interaction log data.
diff --git a/components/contextual_search/internal/composebox_query_controller_unittest.cc b/components/contextual_search/internal/composebox_query_controller_unittest.cc index 481f0fb..2dd1501 100644 --- a/components/contextual_search/internal/composebox_query_controller_unittest.cc +++ b/components/contextual_search/internal/composebox_query_controller_unittest.cc
@@ -4570,6 +4570,46 @@ } TEST_F(ComposeboxQueryControllerTest, + CreateSearchUrl_AimSearch_OverwritesLnsModeIfPresent) { + // Act: Start the session. + controller().InitializeIfNeeded(); + + // Assert: Validate cluster info request and state changes. + WaitForClusterInfo(); + + // Act: Start the file upload flow. + const base::UnguessableToken file_token = base::UnguessableToken::Create(); + StartPdfFileUploadFlow(file_token, + /*file_data=*/std::vector<uint8_t>()); + + // Assert: Validate file upload request and status changes. + WaitForFileUpload(file_token, lens::MimeType::kPdf); + + // Act: Create the destination URL for the query. + std::unique_ptr<CreateSearchUrlRequestInfo> search_url_request_info = + std::make_unique<CreateSearchUrlRequestInfo>(); + search_url_request_info->query_text = "hello"; + search_url_request_info->search_url_type = + ComposeboxQueryController::SearchUrlType::kAim; + search_url_request_info->query_start_time = kTestQueryStartTime; + search_url_request_info->file_tokens.push_back(file_token); + // Add an existing lns_mode param with a different value. + search_url_request_info->additional_params[kLnsModeQueryParameterKey] = + "old_value"; + + base::test::TestFuture<GURL> url_future; + controller().CreateSearchUrl(std::move(search_url_request_info), + url_future.GetCallback()); + GURL aim_url = url_future.Take(); + + // Assert: lns_mode param is present and updated. + std::string lns_mode_value; + EXPECT_TRUE(net::GetValueForKeyInQuery(aim_url, kLnsModeQueryParameterKey, + &lns_mode_value)); + EXPECT_EQ(lns_mode_value, "cvst"); +} + +TEST_F(ComposeboxQueryControllerTest, CreateAddedInputs_IncludesFilesWithoutLensUsageIntent) { // Act: Start the session. controller().InitializeIfNeeded();
diff --git a/components/optimization_guide/internal b/components/optimization_guide/internal index 2e69855..a7d5c8b 160000 --- a/components/optimization_guide/internal +++ b/components/optimization_guide/internal
@@ -1 +1 @@ -Subproject commit 2e698554fb2f4d1d3e5a8d6cfce58886a6d43654 +Subproject commit a7d5c8b5113456c2a50f4af09f30f50acd27c707
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 c311eb2..d680a5a5 100644 --- a/components/safe_browsing/content/browser/client_side_detection_host.cc +++ b/components/safe_browsing/content/browser/client_side_detection_host.cc
@@ -946,35 +946,10 @@ // ping back but only cancel the showing of the interstitial. weak_factory_.InvalidateWeakPtrs(); - if (base::FeatureList::IsEnabled(kClientSideDetectionNewObservers)) { - did_first_visually_non_empty_paint_ = false; - on_first_contentful_paint_ = false; - trigger_model_request_sent_as_force_request_ = false; - return; - } - trigger_model_request_sent_as_force_request_ = false; MaybeStartPreClassification(ClientSideDetectionType::TRIGGER_MODELS); } -void ClientSideDetectionHost::DidFirstVisuallyNonEmptyPaint() { - if (base::FeatureList::IsEnabled(kClientSideDetectionNewObservers)) { - did_first_visually_non_empty_paint_ = true; - if (on_first_contentful_paint_) { - MaybeStartPreClassification(ClientSideDetectionType::TRIGGER_MODELS); - } - } -} - -void ClientSideDetectionHost::OnFirstContentfulPaintInPrimaryMainFrame() { - if (base::FeatureList::IsEnabled(kClientSideDetectionNewObservers)) { - on_first_contentful_paint_ = true; - if (did_first_visually_non_empty_paint_) { - MaybeStartPreClassification(ClientSideDetectionType::TRIGGER_MODELS); - } - } -} - void ClientSideDetectionHost::OnPromptAdded() { if (!IsEnhancedProtectionEnabled(*delegate_->GetPrefs())) { return; @@ -2227,11 +2202,6 @@ ? "ConditionalImageResize.Enabled" : "ConditionalImageResize.Control"); - verdict->mutable_population()->add_finch_active_groups( - base::FeatureList::IsEnabled(kClientSideDetectionNewObservers) - ? "ClientSideDetectionNewObservers.Enabled" - : "ClientSideDetectionNewObservers.Control"); - raw_ptr<VerdictCacheManager> cache_manager = delegate_->GetCacheManager(); if (cache_manager) { ChromeUserPopulation::PageLoadToken token =
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 0cd1906f..7a20e8f 100644 --- a/components/safe_browsing/content/browser/client_side_detection_host.h +++ b/components/safe_browsing/content/browser/client_side_detection_host.h
@@ -153,8 +153,6 @@ void VibrationRequested() override; void OnTextCopiedToClipboard(content::RenderFrameHost* render_frame_host, const std::u16string& copied_text) override; - void DidFirstVisuallyNonEmptyPaint() override; - void OnFirstContentfulPaintInPrimaryMainFrame() override; // permissions::PermissionRequestManager::Observer methods: void OnPromptAdded() override; @@ -549,18 +547,6 @@ // fullscreen. GURL last_fullscreen_url_; - // `did_first_visually_non_empty_paint_` becomes true after the first paint - // that is not the background color. `on_first_contentful_paint_` becomes - // true after the browser renders the first content from the DOM (e.g., - // text or an image). - // - // Client-side detection for TRIGGER_MODELS will only start after both events - // have occurred. This ensures that classification doesn't begin before the - // page has meaningfully rendered. These flags are reset on each new main - // frame navigation. - bool did_first_visually_non_empty_paint_ = false; - bool on_first_contentful_paint_ = false; - // Records the start time of when image embedding started. base::TimeTicks image_embedding_start_time_; raw_ptr<const base::TickClock> tick_clock_;
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 de5f83b..16d0392 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
@@ -139,29 +139,9 @@ classifier_->SetClientSideDetectionType(request_type); RecordEvent(SBPhishingClassifierEvent::kPhishingDetectionRequested); - if (base::FeatureList::IsEnabled(kClientSideDetectionNewObservers)) { - // Browser request has come in, but renderer has not fully loaded, so leave - // it up for renderer load to start the classification. - if (!renderer_layout_finished_) { - return; - } - - if (request_type_ == mojom::ClientSideDetectionType::kImageEmbeddingMatch || - request_type_ == mojom::ClientSideDetectionType::kTriggerModels) { - base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask( - FROM_HERE, - base::BindOnce(&PhishingClassifierDelegate::MaybeStartClassification, - weak_factory_.GetWeakPtr()), - base::Seconds(kCsdClassificationDelay.Get())); - } else { - MaybeStartClassification(); - } - - } else { - // Start classifying the current page if all conditions are met. - // See MaybeStartClassification() for details. - MaybeStartClassification(); - } + // Start classifying the current page if all conditions are met. + // See MaybeStartClassification() for details. + MaybeStartClassification(); } void PhishingClassifierDelegate::DidCommitProvisionalLoad( @@ -169,7 +149,6 @@ blink::WebLocalFrame* frame = render_frame()->GetWebFrame(); // A new page is starting to load, so cancel classificaiton. CancelPendingClassification(CancelClassificationReason::kNavigateAway); - renderer_layout_finished_ = false; if (!frame->Parent()) last_main_frame_transition_ = transition; } @@ -181,70 +160,24 @@ void PhishingClassifierDelegate::PageCaptured( scoped_refptr<const base::RefCountedString16> page_text, bool preliminary_capture) { - if (!base::FeatureList::IsEnabled(kClientSideDetectionNewObservers)) { - RecordEvent(SBPhishingClassifierEvent::kPageTextCaptured); + RecordEvent(SBPhishingClassifierEvent::kPageTextCaptured); - if (preliminary_capture) { - return; - } - - // Note: Currently, if the url hasn't changed, we won't restart - // classification in this case. We may want to adjust this. - - last_finished_load_url_ = - render_frame()->GetWebFrame()->GetDocument().Url(); - - GURL stripped_last_load_url(StripRef(last_finished_load_url_)); - // Check if toplevel URL has changed. - if (stripped_last_load_url == StripRef(last_url_sent_to_classifier_)) { - return; - } - - MaybeStartClassification(); - } else { - // This is true if layout_type == kWebMeaningfulLayout::kFinishedParsing. - // We are looking for kWebMeaningfulLayout::kFinishedLoading only. - // PageCaptured is not called for any other cases of kWebMeaningfulLayout. - if (preliminary_capture) { - return; - } - renderer_layout_finished_ = true; - RecordEvent( - SBPhishingClassifierEvent::kPhishingClassifierPageFinishedLoading); - // Note: Currently, if the url hasn't changed, we won't restart - // classification in this case. We may want to adjust this. - last_finished_load_url_ = - render_frame()->GetWebFrame()->GetDocument().Url(); - - // Browser side has not made a request yet, so no need to try to start the - // classification. - if (!is_phishing_detection_running_) { - return; - } - - GURL stripped_last_load_url(StripRef(last_finished_load_url_)); - // If we're classifying at the moment and there's a new finished load on the - // page, do not attempt to start a new classification. - if (is_classifying_ && - stripped_last_load_url == StripRef(last_url_sent_to_classifier_)) { - RecordEvent( - SBPhishingClassifierEvent:: - kPhishingClassifierPageFinishedLoadingAgainDuringClassification); - return; - } - - if (request_type_ == mojom::ClientSideDetectionType::kTriggerModels || - request_type_ == mojom::ClientSideDetectionType::kImageEmbeddingMatch) { - base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask( - FROM_HERE, - base::BindOnce(&PhishingClassifierDelegate::MaybeStartClassification, - weak_factory_.GetWeakPtr()), - base::Seconds(kCsdClassificationDelay.Get())); - - } else { - MaybeStartClassification(); - } + if (preliminary_capture) { + return; } + + // Note: Currently, if the url hasn't changed, we won't restart + // classification in this case. We may want to adjust this. + + last_finished_load_url_ = render_frame()->GetWebFrame()->GetDocument().Url(); + + GURL stripped_last_load_url(StripRef(last_finished_load_url_)); + // Check if toplevel URL has changed. + if (stripped_last_load_url == StripRef(last_url_sent_to_classifier_)) { + return; + } + + MaybeStartClassification(); } void PhishingClassifierDelegate::CancelPendingClassification(
diff --git a/components/safe_browsing/content/renderer/phishing_classifier/phishing_classifier_delegate.h b/components/safe_browsing/content/renderer/phishing_classifier/phishing_classifier_delegate.h index d690cfb..d45fedab 100644 --- a/components/safe_browsing/content/renderer/phishing_classifier/phishing_classifier_delegate.h +++ b/components/safe_browsing/content/renderer/phishing_classifier/phishing_classifier_delegate.h
@@ -51,12 +51,7 @@ kPhishingClasifierCallbackEmptyOnCompletion = 8, // Phishing classification request responded. kPhishingClassifierRequestResponded = 9, - // Renderer frame layout has finished loading as - // WebMeaningfulLayout::kFinishedLoading. - kPhishingClassifierPageFinishedLoading = 10, - // Renderer frame layout has finished loading during classification. - kPhishingClassifierPageFinishedLoadingAgainDuringClassification = 11, - kMaxValue = kPhishingClassifierPageFinishedLoadingAgainDuringClassification, + kMaxValue = kPhishingClassifierRequestResponded, }; class PhishingClassifierDelegate : public content::RenderFrameObserver, @@ -179,10 +174,6 @@ // set to false whenever phishing detection has finished. bool is_phishing_detection_running_ = false; - // Set to true when PageText is captured. Set to false when there is a new - // frame loading. This is used as a signal to start the classification. - bool renderer_layout_finished_ = false; - // Set to true when we want to classify for the page, but classifier was not // ready. It is set to false whenever |is_phishing_detection_running_| is set // to true, classification is happening, completed, or cancelled.
diff --git a/components/safe_browsing/core/common/features.cc b/components/safe_browsing/core/common/features.cc index e2fb332..1156893 100644 --- a/components/safe_browsing/core/common/features.cc +++ b/components/safe_browsing/core/common/features.cc
@@ -133,11 +133,6 @@ BASE_FEATURE(kClientSideDetectionLlamaForcedTriggerInfoForScamDetection, base::FEATURE_ENABLED_BY_DEFAULT); -BASE_FEATURE(kClientSideDetectionNewObservers, - base::FEATURE_DISABLED_BY_DEFAULT); -constexpr base::FeatureParam<double> kCsdClassificationDelay{ - &kClientSideDetectionNewObservers, "ClassificationDelay", 0.0}; - #if BUILDFLAG(IS_ANDROID) BASE_FEATURE(kClientSideDetectionOnDeviceModelLazyDownloadAndroid, base::FEATURE_DISABLED_BY_DEFAULT);
diff --git a/components/safe_browsing/core/common/features.h b/components/safe_browsing/core/common/features.h index 454249330..061b070 100644 --- a/components/safe_browsing/core/common/features.h +++ b/components/safe_browsing/core/common/features.h
@@ -120,11 +120,6 @@ BASE_DECLARE_FEATURE( kClientSideDetectionLlamaForcedTriggerInfoForScamDetection); -// The observers that trigger the image classification have been tweaked with a -// more defined page loading state check. -BASE_DECLARE_FEATURE(kClientSideDetectionNewObservers); -extern const base::FeatureParam<double> kCsdClassificationDelay; - #if BUILDFLAG(IS_ANDROID) // Instead of starting model download on startup, do it lazily during inference. BASE_DECLARE_FEATURE(kClientSideDetectionOnDeviceModelLazyDownloadAndroid);
diff --git a/components/services/storage/dom_storage/session_storage_impl.cc b/components/services/storage/dom_storage/session_storage_impl.cc index ad3ccbb..4d5320d1 100644 --- a/components/services/storage/dom_storage/session_storage_impl.cc +++ b/components/services/storage/dom_storage/session_storage_impl.cc
@@ -174,8 +174,6 @@ } void SessionStorageImpl::CreateNamespace(const std::string& namespace_id) { - CHECK_NE(connection_state_, CONNECTION_IN_PROGRESS, - base::NotFatalUntil::M146); if (namespaces_.find(namespace_id) != namespaces_.end()) return; @@ -187,8 +185,6 @@ const std::string& clone_from_namespace_id, const std::string& clone_to_namespace_id, mojom::SessionStorageCloneType clone_type) { - CHECK_NE(connection_state_, CONNECTION_IN_PROGRESS, - base::NotFatalUntil::M146); if (namespaces_.find(clone_to_namespace_id) != namespaces_.end()) { // Non-immediate clones expect to be paired with a |Clone| from the mojo // namespace object. If that clone has already happened, then we don't need @@ -224,8 +220,8 @@ clone_to_namespace_id); } else if (metadata_.namespace_storage_key_map().contains( clone_from_namespace_id)) { - CHECK_EQ(connection_state_, CONNECTION_FINISHED, - base::NotFatalUntil::M146); + CHECK_EQ(connection_state_, CONNECTION_FINISHED); + // The namespace exists on disk but is not in-use, so do the appropriate // metadata operations to clone the namespace and set up the new object. auto source_namespace_entry = @@ -254,8 +250,6 @@ void SessionStorageImpl::DeleteNamespace(const std::string& namespace_id, bool should_persist) { - CHECK_NE(connection_state_, CONNECTION_IN_PROGRESS, - base::NotFatalUntil::M146); auto namespace_it = namespaces_.find(namespace_id); // If the namespace has pending clones, do the clone now before destroying it. if (namespace_it != namespaces_.end()) { @@ -556,6 +550,8 @@ scoped_refptr<DomStorageDatabase::SharedMapLocator> SessionStorageImpl::RegisterNewAreaMap(const std::string& namespace_id, const blink::StorageKey& storage_key) { + CHECK_EQ(connection_state_, CONNECTION_FINISHED); + scoped_refptr<DomStorageDatabase::SharedMapLocator> map_entry = metadata_.RegisterNewMap(namespace_id, storage_key); if (database_) { @@ -595,6 +591,14 @@ commit_error_count_ = 0; return; } + + if (connection_state_ != CONNECTION_FINISHED) { + // Previous commit errors deleted and recreated the database below. Ignore + // additional errors from the old database while waiting for the new + // database to open. + return; + } + commit_error_count_++; if (commit_error_count_ > kSessionStorageCommitErrorThreshold) { if (tried_to_recover_from_commit_errors_) { @@ -635,8 +639,8 @@ } } - CHECK_EQ(connection_state_, CONNECTION_FINISHED, base::NotFatalUntil::M146); - DCHECK_EQ(connection_state_, CONNECTION_FINISHED); + CHECK_EQ(connection_state_, CONNECTION_FINISHED); + auto source_namespace_entry = metadata_.GetOrCreateNamespaceEntry(source_namespace_id); auto namespace_entry = metadata_.GetOrCreateNamespaceEntry(new_namespace_id); @@ -677,7 +681,7 @@ void SessionStorageImpl::DeleteNamespacesFromMetadataAndDatabase( std::vector<std::string> namespace_ids) { - CHECK_EQ(connection_state_, CONNECTION_FINISHED, base::NotFatalUntil::M146); + CHECK_EQ(connection_state_, CONNECTION_FINISHED); // Remove each namespace from `metadata_`. std::vector<DomStorageDatabase::MapLocator> maps_to_delete; @@ -724,8 +728,7 @@ } void SessionStorageImpl::InitiateConnection(bool in_memory_only) { - CHECK_EQ(connection_state_, CONNECTION_IN_PROGRESS, - base::NotFatalUntil::M146); + CHECK_EQ(connection_state_, CONNECTION_IN_PROGRESS); if (backing_mode_ != BackingMode::kNoDisk && !in_memory_only && !storage_partition_directory_.empty()) { @@ -784,8 +787,7 @@ } void SessionStorageImpl::OnConnectionFinished() { - CHECK(!database_ || connection_state_ == CONNECTION_IN_PROGRESS, - base::NotFatalUntil::M146); + CHECK_EQ(connection_state_, CONNECTION_IN_PROGRESS); // If connection was opened successfully, reset tried_to_recreate_during_open_ // to enable recreating the database on future errors.
diff --git a/components/services/storage/dom_storage/session_storage_impl.h b/components/services/storage/dom_storage/session_storage_impl.h index dde7757..48b0aa1 100644 --- a/components/services/storage/dom_storage/session_storage_impl.h +++ b/components/services/storage/dom_storage/session_storage_impl.h
@@ -165,7 +165,9 @@ // Initiates connecting to the database if no connection is in progress yet. void RunWhenConnected(base::OnceClosure callback); - // Part of our asynchronous directory opening called from RunWhenConnected(). + // Part of asynchronous database opening called from `RunWhenConnected()`. If + // opening the database on disk fails twice, falls back to in memory. If + // opening the database in memory fails, runs without a database. void InitiateConnection(bool in_memory_only = false); void OnDatabaseOpened(DbStatus status); void OnGotDatabaseMetadata( @@ -207,6 +209,7 @@ mojo::Receiver<mojom::SessionStorageControl> receiver_; + // `database_` is null after failing to open repeatedly. std::unique_ptr<AsyncDomStorageDatabase> database_; // This can be true even if the profile is not in-memory, since we attempt // to create an in-memory DB if on-disk fails. This variable has no meaning
diff --git a/components/update_client/background_downloader_win.cc b/components/update_client/background_downloader_win.cc index 3adbb99..227b7bd 100644 --- a/components/update_client/background_downloader_win.cc +++ b/components/update_client/background_downloader_win.cc
@@ -909,29 +909,14 @@ void BackgroundDownloader::CleanupStaleDownloads() { DCHECK_CALLED_ON_VALID_SEQUENCE(com_sequence_checker_); - EnumerateDownloadDirs( - base::StrCat({prod_id_, kDownloadDirectoryPrefixMatcher}), - [](const base::FilePath& dir) { - const base::Time now = base::Time::Now(); - base::File::Info info; - if (base::GetFileInfo(dir, &info) && - info.creation_time + base::Days(kPurgeStaleJobsAfterDays) < now) { - RetryFileOperation(&base::DeletePathRecursively, dir); - } - }); -} -void BackgroundDownloader::EnumerateDownloadDirs( - const base::FilePath::StringType& matcher, - base::FunctionRef<void(const base::FilePath& dir)> callback) { - DCHECK_CALLED_ON_VALID_SEQUENCE(com_sequence_checker_); base::FilePath dir; - if (base::GetSecureTempDirectory(&dir)) { - base::FileEnumerator(dir, - /*recursive=*/false, base::FileEnumerator::DIRECTORIES, - matcher) - .ForEach(callback); + if (!base::GetSecureTempDirectory(&dir)) { + return; } + CleanupDirectoriesOlderThan( + dir, base::StrCat({prod_id_, kDownloadDirectoryPrefixMatcher}), + base::Days(kPurgeStaleJobsAfterDays)); } } // namespace update_client
diff --git a/components/update_client/background_downloader_win.h b/components/update_client/background_downloader_win.h index 0f645f2..596869b 100644 --- a/components/update_client/background_downloader_win.h +++ b/components/update_client/background_downloader_win.h
@@ -118,11 +118,6 @@ // Perform a best-effort cleanup up downloads that are too old. void CleanupStaleDownloads(); - // Enumerate the writable temporary directories matching |matcher|. - void EnumerateDownloadDirs( - const base::FilePath::StringType& matcher, - base::FunctionRef<void(const base::FilePath& dir)> callback); - // This sequence checker is bound to the main sequence. SEQUENCE_CHECKER(sequence_checker_); SEQUENCE_CHECKER(com_sequence_checker_);
diff --git a/components/update_client/background_downloader_win_unittest.cc b/components/update_client/background_downloader_win_unittest.cc index b986d72..1cde5f38 100644 --- a/components/update_client/background_downloader_win_unittest.cc +++ b/components/update_client/background_downloader_win_unittest.cc
@@ -11,6 +11,7 @@ #include "base/memory/scoped_refptr.h" #include "base/test/task_environment.h" #include "base/win/windows_types.h" +#include "components/update_client/utils.h" #include "testing/gtest/include/gtest/gtest.h" namespace update_client { @@ -35,9 +36,9 @@ }; void BackgroundDownloaderWinTest::TearDown() { - downloader_->EnumerateDownloadDirs( - kTestDirMatcher, - [](const base::FilePath& dir) { base::DeletePathRecursively(dir); }); + base::FilePath dir; + ASSERT_TRUE(base::GetSecureTempDirectory(&dir)); + CleanupDirectoriesOlderThan(dir, kTestDirMatcher, base::Seconds(0)); } TEST_F(BackgroundDownloaderWinTest, CleansStaleDownloads) {
diff --git a/components/update_client/update_client.cc b/components/update_client/update_client.cc index a58f918..b19f9c0a 100644 --- a/components/update_client/update_client.cc +++ b/components/update_client/update_client.cc
@@ -235,16 +235,6 @@ is_stopped_ = true; - // In the current implementation it is sufficient to cancel the pending - // tasks only. The tasks that are run by the update engine will stop - // making progress naturally, as the main task runner stops running task - // actions. Upon the browser shutdown, the resources employed by the active - // tasks will leak, as the operating system kills the thread associated with - // the update engine task runner. Further refactoring may be needed in this - // area, to cancel the running tasks by canceling the current action update. - // This behavior would be expected, correct, and result in no resource leaks - // in all cases, in shutdown or not. - // // Cancel the pending tasks. These tasks are safe to cancel and delete since // they have not picked up by the update engine, and not shared with any // task runner yet. @@ -253,6 +243,14 @@ task_queue_.pop_front(); task->Cancel(); } + + // Also cancel active tasks to trigger downloader cleanup. Otherwise, upon the + // browser shutdown, the resources employed by the active tasks will leak, as + // the operating system kills the thread associated with the update engine + // task runner. + for (auto& task : tasks_) { + task->Cancel(); + } } void UpdateClientImpl::SendPing(const CrxComponent& crx_component,
diff --git a/components/update_client/update_client_unittest.cc b/components/update_client/update_client_unittest.cc index 0d0d6e8..76bc84a 100644 --- a/components/update_client/update_client_unittest.cc +++ b/components/update_client/update_client_unittest.cc
@@ -16,6 +16,7 @@ #include "base/check_deref.h" #include "base/containers/flat_map.h" #include "base/containers/to_vector.h" +#include "base/files/file_enumerator.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/functional/bind.h" @@ -5773,15 +5774,6 @@ MockObserver observer(update_client); { - InSequence seq; - EXPECT_CALL(observer, OnEvent(Truly([](const CrxUpdateItem& item) { - return item.id == "jebgalgnebhfojomionfpkfelancnnkf" && - item.state == ComponentState::kChecking; - }))); - EXPECT_CALL(observer, OnEvent(Truly([](const CrxUpdateItem& item) { - return item.id == "jebgalgnebhfojomionfpkfelancnnkf" && - item.state == ComponentState::kUpToDate; - }))); } std::vector<CrxUpdateItem> items; @@ -5790,8 +5782,7 @@ .WillRepeatedly( [&items](const CrxUpdateItem& item) { items.push_back(item); }); - // Do two `CheckForUpdate` calls, expect the second call to be cancelled, - // because `Stop` cancels the queued up subsequent call. + // Do two `CheckForUpdate` calls, expect both calls to be cancelled. base::RepeatingClosure barrier_quit_closure = BarrierClosure(2, runloop_.QuitClosure()); const std::string id = "jebgalgnebhfojomionfpkfelancnnkf"; @@ -5799,20 +5790,16 @@ id, base::BindOnce(&DataCallbackMock::Callback), base::BindRepeating(&MockCrxStateChangeReceiver::Receive, receiver), /*is_foreground=*/true, - ExpectErrorThenQuit(barrier_quit_closure, Error::NONE)); + ExpectErrorThenQuit(barrier_quit_closure, Error::UPDATE_CANCELED)); update_client->CheckForUpdate( id, base::BindOnce(&DataCallbackMock::Callback), base::BindRepeating(&MockCrxStateChangeReceiver::Receive, receiver), /*is_foreground=*/true, ExpectErrorThenQuit(barrier_quit_closure, Error::UPDATE_CANCELED)); - update_client->Stop(); EXPECT_TRUE(update_client->IsUpdating(id)); + update_client->Stop(); runloop_.Run(); - EXPECT_EQ(items.size(), 2u); - EXPECT_EQ(items[0].state, ComponentState::kChecking); - EXPECT_EQ(items[0].id, "jebgalgnebhfojomionfpkfelancnnkf"); - EXPECT_EQ(items[1].state, ComponentState::kUpToDate); - EXPECT_EQ(items[1].id, "jebgalgnebhfojomionfpkfelancnnkf"); + EXPECT_TRUE(items.empty()); } TEST_F(UpdateClientTest, CheckForUpdate_Errors) { @@ -6599,4 +6586,173 @@ EXPECT_EQ("jebgalgnebhfojomionfpkfelancnnkf", items[2].id); } +// Tests cancellation of an active download when `UpdateClient::Stop` is called. +TEST_F(UpdateClientTest, Install_StopCancelsActiveDownload) { + base::FilePath temp_dir; +#if BUILDFLAG(IS_WIN) + ASSERT_TRUE(base::GetSecureTempDirectory(&temp_dir)); +#else // BUILDFLAG(IS_WIN) + ASSERT_TRUE(base::GetTempDir(&temp_dir)); +#endif // BUILDFLAG(IS_WIN) + + base::FileEnumerator( + temp_dir, /*recursive=*/false, base::FileEnumerator::DIRECTORIES, + base::StrCat({UTF8ToStringType(config()->GetProdId()), + FILE_PATH_LITERAL("_chrome_url_fetcher_")})) + .ForEach([](const base::FilePath& path) { + ASSERT_TRUE(RetryFileOperation(&base::DeletePathRecursively, path)); + }); + + class DataCallbackMock { + public: + static void Callback( + const std::vector<std::string>& ids, + base::OnceCallback< + void(const std::vector<std::optional<CrxComponent>>&)> callback) { + CrxComponent crx; + crx.app_id = "jebgalgnebhfojomionfpkfelancnnkf"; + crx.name = "test_jebg"; + crx.pk_hash = base::ToVector(jebg_hash); + crx.version = base::Version("0.0"); + crx.installer = base::MakeRefCounted<TestInstaller>(); + crx.crx_format_requirement = crx_file::VerifierFormat::CRX3; + std::move(callback).Run({crx}); + } + }; + + MockUpdateCheckerFactory< + MockUpdateCheckerImpl<UpdateCheckerOptionsOneCrxInstall>> + mock_update_checker_factory; + + class MockCrxDownloader : public CrxDownloader { + public: + MockCrxDownloader() = default; + + private: + ~MockCrxDownloader() override = default; + + base::OnceClosure DoStartDownload(const GURL& url) override { + DownloadMetrics download_metrics; + base::FilePath path; + Result result; + if (url.GetPath() == "/download/jebgalgnebhfojomionfpkfelancnnkf.crx") { + download_metrics.url = url; + download_metrics.downloader = DownloadMetrics::kNone; + download_metrics.error = 0; + download_metrics.downloaded_bytes = 1015; + download_metrics.total_bytes = 1015; + download_metrics.download_time_ms = 1000; + + EXPECT_TRUE(MakeTestFile( + GetTestFilePath("jebgalgnebhfojomionfpkfelancnnkf.crx"), &path)); + + result.error = 0; + result.response = path; + } else { + ADD_FAILURE(); + } + + base::SequencedTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadProgress, + base::Unretained(this), + download_metrics.downloaded_bytes, + download_metrics.total_bytes)); + + base::SequencedTaskRunner::GetCurrentDefault()->PostTask( + FROM_HERE, base::BindOnce(&MockCrxDownloader::OnDownloadComplete, + base::Unretained(this), true, result, + download_metrics)); + + EXPECT_CALL(cancel_callback_, Run()).Times(testing::AtLeast(1)); + return cancel_callback_.Get(); + } + + base::MockCallback<base::OnceClosure> cancel_callback_; + }; + + class MockPingManager : public MockPingManagerImpl { + public: + explicit MockPingManager(scoped_refptr<Configurator> config) + : MockPingManagerImpl(config) {} + + protected: + ~MockPingManager() override { + // Verify the ping data shows that the active task was cancelled. + const auto ping_data = MockPingManagerImpl::terminal_ping_data(); + EXPECT_EQ(1u, ping_data.size()); + EXPECT_EQ("jebgalgnebhfojomionfpkfelancnnkf", ping_data[0].id); + EXPECT_EQ(base::Version("0.0"), ping_data[0].previous_version); + EXPECT_EQ(base::Version("1.0"), ping_data[0].next_version); + EXPECT_EQ(ErrorCategory::kService, ping_data[0].error_category); + EXPECT_EQ(static_cast<int>(ServiceError::CANCELLED), + ping_data[0].error_code); + } + }; + + SetMockCrxDownloader<MockCrxDownloader>(); + scoped_refptr<UpdateClient> update_client = + base::MakeRefCounted<UpdateClientImpl>( + config(), base::MakeRefCounted<MockPingManager>(config()), + mock_update_checker_factory.GetFactory()); + + MockObserver observer(update_client); + { + InSequence seq; + EXPECT_CALL(observer, OnEvent(Truly([](const CrxUpdateItem& item) { + return item.id == "jebgalgnebhfojomionfpkfelancnnkf" && + item.state == ComponentState::kChecking; + }))); + EXPECT_CALL(observer, OnEvent(Truly([](const CrxUpdateItem& item) { + return item.id == "jebgalgnebhfojomionfpkfelancnnkf" && + item.state == ComponentState::kCanUpdate; + }))); + EXPECT_CALL(observer, OnEvent(Truly([](const CrxUpdateItem& item) { + return item.id == "jebgalgnebhfojomionfpkfelancnnkf" && + item.state == ComponentState::kDownloading; + }))) + .Times(AtLeast(1)) + .WillRepeatedly([&update_client] { + // Call `Stop` during the download. + update_client->Stop(); + }); + EXPECT_CALL(observer, OnEvent(Truly([](const CrxUpdateItem& item) { + return item.id == "jebgalgnebhfojomionfpkfelancnnkf" && + item.state == ComponentState::kUpdateError; + }))); + } + + std::vector<CrxUpdateItem> items; + auto receiver = base::MakeRefCounted<MockCrxStateChangeReceiver>(); + EXPECT_CALL(*receiver, Receive(_)) + .WillRepeatedly( + [&items](const CrxUpdateItem& item) { items.push_back(item); }); + + update_client->Install( + std::string("jebgalgnebhfojomionfpkfelancnnkf"), + base::BindOnce(&DataCallbackMock::Callback), + base::BindRepeating(&MockCrxStateChangeReceiver::Receive, receiver), + ExpectErrorThenQuit(runloop_, Error::NONE)); + runloop_.Run(); + + EXPECT_EQ(5u, items.size()); + EXPECT_EQ(ComponentState::kChecking, items[0].state); + EXPECT_EQ("jebgalgnebhfojomionfpkfelancnnkf", items[0].id); + EXPECT_EQ(ComponentState::kCanUpdate, items[1].state); + EXPECT_EQ("jebgalgnebhfojomionfpkfelancnnkf", items[1].id); + EXPECT_EQ(ComponentState::kDownloading, items[2].state); + EXPECT_EQ("jebgalgnebhfojomionfpkfelancnnkf", items[2].id); + EXPECT_EQ(ComponentState::kDownloading, items[3].state); + EXPECT_EQ("jebgalgnebhfojomionfpkfelancnnkf", items[3].id); + EXPECT_EQ(ComponentState::kUpdateError, items[4].state); + EXPECT_EQ("jebgalgnebhfojomionfpkfelancnnkf", items[4].id); + + base::FileEnumerator( + temp_dir, /*recursive=*/false, base::FileEnumerator::DIRECTORIES, + base::StrCat({UTF8ToStringType(config()->GetProdId()), + FILE_PATH_LITERAL("_chrome_url_fetcher_")})) + .ForEach([](const base::FilePath& path) { + ADD_FAILURE() << "Unexpected left over directory found: " << path; + }); +} + } // namespace update_client
diff --git a/components/update_client/utils.cc b/components/update_client/utils.cc index f49ea3f..666474d 100644 --- a/components/update_client/utils.cc +++ b/components/update_client/utils.cc
@@ -19,6 +19,7 @@ #include "base/containers/heap_array.h" #include "base/containers/span.h" #include "base/files/file.h" +#include "base/files/file_enumerator.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/files/memory_mapped_file.h" @@ -237,4 +238,22 @@ } #endif // BUILDFLAG(IS_WIN) +void CleanupDirectoriesOlderThan(const base::FilePath& dir, + const base::FilePath::StringType& matcher, + base::TimeDelta older_than) { + // Enumerate the directories matching `matcher`. + base::FileEnumerator(dir, + /*recursive=*/false, base::FileEnumerator::DIRECTORIES, + matcher) + .ForEach([&](const base::FilePath& dir) { + base::File::Info info; + + // Delete the directories older than `older_than`. + if (base::GetFileInfo(dir, &info) && + ((info.creation_time + older_than) < base::Time::Now())) { + RetryFileOperation(&base::DeletePathRecursively, dir); + } + }); +} + } // namespace update_client
diff --git a/components/update_client/utils.h b/components/update_client/utils.h index 7237742..9f9534579 100644 --- a/components/update_client/utils.h +++ b/components/update_client/utils.h
@@ -126,6 +126,12 @@ } #endif // BUILDFLAG(IS_WIN) +// Perform a best-effort cleanup up of directories under `dir` that match +// `matcher` and are `older_than`. +void CleanupDirectoriesOlderThan(const base::FilePath& dir, + const base::FilePath::StringType& matcher, + base::TimeDelta older_than); + } // namespace update_client #endif // COMPONENTS_UPDATE_CLIENT_UTILS_H_
diff --git a/components/update_client/utils_unittest.cc b/components/update_client/utils_unittest.cc index 0ea25984..8222dd3f 100644 --- a/components/update_client/utils_unittest.cc +++ b/components/update_client/utils_unittest.cc
@@ -24,7 +24,11 @@ #include "url/gurl.h" #if BUILDFLAG(IS_WIN) +#include <windows.h> + #include <shlobj.h> + +#include "base/win/windows_types.h" #endif // BUILDFLAG(IS_WIN) #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA) @@ -33,6 +37,16 @@ namespace update_client { +namespace { +constexpr base::FilePath::CharType kTestDirPrefix[] = + FILE_PATH_LITERAL("_utils_unittest_"); +constexpr base::FilePath::CharType kTestDirMatcher[] = + FILE_PATH_LITERAL("_utils_unittest_*"); +constexpr base::FilePath::CharType kTestDownloadFilename[] = + FILE_PATH_LITERAL("test_file.txt"); +constexpr char kTestDownloadContent[] = "Hello, World!"; +} // namespace + TEST(UpdateClientUtils, VerifyFileHash256) { EXPECT_TRUE(VerifyFileHash256( GetTestFilePath("jebgalgnebhfojomionfpkfelancnnkf.crx"), @@ -229,6 +243,52 @@ ASSERT_TRUE(RetryFileOperation(&base::DeletePathRecursively, tempdir)); } +#if BUILDFLAG(IS_WIN) +TEST(UpdateClientUtils, CleanupDirectoriesOlderThan) { + base::FilePath download_dir_path; + ASSERT_TRUE(base::CreateNewTempDirectory(kTestDirPrefix, &download_dir_path)); + ASSERT_TRUE(base::WriteFile(download_dir_path.Append(kTestDownloadFilename), + kTestDownloadContent)); + + // Manipulate the creation time of the directory to be older than 3 days. + FILETIME creation_filetime = + (base::Time::NowFromSystemTime() - base::Days(5)).ToFileTime(); + base::File download_dir(download_dir_path, + base::File::FLAG_OPEN | + base::File::FLAG_WIN_BACKUP_SEMANTICS | + base::File::FLAG_WRITE_ATTRIBUTES); + ASSERT_TRUE(download_dir.IsValid()); + ASSERT_TRUE(::SetFileTime(download_dir.GetPlatformFile(), &creation_filetime, + NULL, NULL)); + download_dir.Close(); + + CleanupDirectoriesOlderThan(download_dir_path.DirName(), kTestDirMatcher, + base::Days(3)); + + EXPECT_FALSE(base::DirectoryExists(download_dir_path)) + << "download_dir_path: " << download_dir_path; +} +#endif // BUILDFLAG(IS_WIN) + +TEST(UpdateClientUtils, RetainsRecentDownloads) { + base::FilePath download_dir_path; + ASSERT_TRUE(base::CreateNewTempDirectory(kTestDirPrefix, &download_dir_path)); + ASSERT_TRUE(base::WriteFile(download_dir_path.Append(kTestDownloadFilename), + kTestDownloadContent)); + + base::FilePath temp_dir; +#if BUILDFLAG(IS_WIN) + ASSERT_TRUE(base::GetSecureTempDirectory(&temp_dir)); +#else // BUILDFLAG(IS_WIN) + ASSERT_TRUE(base::GetTempDir(&temp_dir)); +#endif // BUILDFLAG(IS_WIN) + CleanupDirectoriesOlderThan(temp_dir, kTestDirMatcher, base::Days(3)); + + EXPECT_TRUE(base::DirectoryExists(download_dir_path)); + ASSERT_TRUE( + RetryFileOperation(&base::DeletePathRecursively, download_dir_path)); +} + struct UpdateClientUtilsUTF8StringTypeTestCase { const base::FilePath::StringType stringtype; const std::string utf8;
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc index f523d47d..2e11149 100644 --- a/content/browser/renderer_host/navigation_request.cc +++ b/content/browser/renderer_host/navigation_request.cc
@@ -2136,13 +2136,13 @@ if (NeedsUrlLoader() && common_params_->url.SchemeIsHTTPOrHTTPS()) { if (GetContentClient()->browser()->ShouldPreconnectNavigation( frame_tree_node_->current_frame_host()) && - IsAllowedByConnectionAllowlist()) { + IsAllowedByConnectionAllowlist(/*is_redirect=*/false)) { auto* storage_partition = frame_tree_node_->current_frame_host()->GetStoragePartition(); // Initiator frame's `network_restriction_id` is not passed because the // preconnection has already been checked against the connection-allowlist - // by the `IsAllowedByConnectionAllowlist()` call above. + // by the `IsAllowedByConnectionAllowlist(false)` call above. storage_partition->GetNetworkContext()->PreconnectSockets( 1, common_params_->url, network::mojom::CredentialsMode::kInclude, GetIsolationInfo().network_anonymization_key(), @@ -2889,7 +2889,7 @@ } // Connection Allowlist: check whether navigation to the url is allowed. - if (!IsAllowedByConnectionAllowlist()) { + if (!IsAllowedByConnectionAllowlist(/*is_redirect=*/false)) { // Create a navigation handle so that the correct error code can be set on // it by OnRequestFailedInternal(). StartNavigation(); @@ -3550,6 +3550,19 @@ perfetto::Flow::FromPointer(this)); ScopedCrashKeys crash_keys(*this); + if (!was_redirected_ && + !IsAllowedByConnectionAllowlist(/*is_redirect=*/true)) { + auto completion_status = + network::URLLoaderCompletionStatus(net::ERR_UNSAFE_REDIRECT); + error_navigation_trigger_ = ErrorNavigationTrigger::kRedirectNotAllowed; + OnRequestFailedInternal(completion_status, false /* skip_throttles */, + std::nullopt /* error_page_content */, + false /* collapse_frame */); + // DO NOT ADD CODE after this. The previous call to OnRequestFailedInternal + // has destroyed the NavigationRequest. + return; + } + // Sanity check - this can only be set at commit time. DCHECK(!auth_challenge_info_); @@ -7326,13 +7339,15 @@ SetExpectedProcess(post_redirect_process); } -bool NavigationRequest::IsAllowedByConnectionAllowlist() { +bool NavigationRequest::IsAllowedByConnectionAllowlist(bool is_redirect) { if (!base::FeatureList::IsEnabled(network::features::kConnectionAllowlists)) { return true; } - // Determine the PolicyContainerPolicies to use based on navigation type. - const PolicyContainerPolicies* policies = nullptr; + if (is_redirect && connection_allowlists_blocks_redirect_) { + // TODO(crbug.com/447954811): Implement reporting. + return false; + } // If it is renderer-initiated, initiator_frame_token_ will be set and // connection allowlist should be checked unless it is a same-document @@ -7377,6 +7392,9 @@ navigation_state->policy_container_host(); } } + + // Determine the PolicyContainerPolicies to use based on navigation type. + const PolicyContainerPolicies* policies = nullptr; if (initiator_policy_container_host) { policies = &initiator_policy_container_host->policies(); } @@ -7385,8 +7403,18 @@ return true; } - return network::ConnectionAllowlistMatchesUrl( - policies->connection_allowlists.enforced.value(), common_params_->url); + if (network::ConnectionAllowlistMatchesUrl( + policies->connection_allowlists.enforced.value(), + common_params_->url)) { + // Default-block any server-side redirects. + // TODO(crbug.com/447954811): Implement allowing server-side redirects + // based on an attribute. Consider not having a bool on the + // NavigationRequest as part of that change. + connection_allowlists_blocks_redirect_ = true; + return true; + } + + return false; } bool NavigationRequest::IsAllowedByCSPDirective(
diff --git a/content/browser/renderer_host/navigation_request.h b/content/browser/renderer_host/navigation_request.h index 6711f66..523395ed 100644 --- a/content/browser/renderer_host/navigation_request.h +++ b/content/browser/renderer_host/navigation_request.h
@@ -1969,8 +1969,12 @@ void CommitPageActivation(); // Checks whether this navigation is allowed based on the connection - // allowlist header, if present. - bool IsAllowedByConnectionAllowlist(); + // allowlist header, if present. This method can have two side effects: + // - If a CA is configured to send reports and the request violates the CA, + // a report will be sent. + // - If CA is checked, the navigation request's + // connection_allowlists_blocks_redirect_ will be set accordingly. + bool IsAllowedByConnectionAllowlist(bool is_redirect); // Checks if the specified CSP context's relevant CSP directive // allows the navigation. This is called to perform the frame-src check. @@ -3441,6 +3445,11 @@ // request. bool did_encounter_cross_origin_redirect_ = false; + // This field is checked to see if server-side redirects should be blocked. + // It is only used if Connection allowlists were consulted when this + // navigation started. + bool connection_allowlists_blocks_redirect_ = false; + // A scoped reference on the ViewTransition resources generated for this // navigation. This is set after we received the cached results from the old // Document's renderer. If the navigation commits, the resources are
diff --git a/content/browser/webid/request_service.cc b/content/browser/webid/request_service.cc index 27deca42..a512502 100644 --- a/content/browser/webid/request_service.cc +++ b/content/browser/webid/request_service.cc
@@ -225,23 +225,18 @@ void RequestService::BindReceiver( mojo::PendingReceiver<blink::mojom::FederatedAuthRequest> pending_receiver) { - if (receiver_.is_bound()) { - // This should only happen with a compromised renderer. - // TODO(crbug.com/40810039): Call ReportBadMessage. - return; - } - receiver_.Bind(std::move(pending_receiver)); + receivers_.Add(this, std::move(pending_receiver)); } void RequestService::ReportBadMessage(const char* message) { - receiver_.ReportBadMessage(message); + receivers_.ReportBadMessage(message); } void RequestService::ResetAndDeleteThisForTesting() { - // Resetting the receiver_ before we destruct the objects means that + // Resetting the receivers_ before we destruct the objects means that // callbacks won't be called. This matches DocumentService::ResetAndDeleteThis // and is what our tests expect. - receiver_.reset(); + receivers_.Clear(); DeleteForCurrentDocument(&render_frame_host()); }
diff --git a/content/browser/webid/request_service.h b/content/browser/webid/request_service.h index 31d68cd..2a140aac 100644 --- a/content/browser/webid/request_service.h +++ b/content/browser/webid/request_service.h
@@ -682,7 +682,7 @@ // Can be set to true in tests. bool force_allow_redirect_to_for_testing_{false}; - mojo::Receiver<blink::mojom::FederatedAuthRequest> receiver_{this}; + mojo::ReceiverSet<blink::mojom::FederatedAuthRequest> receivers_; base::WeakPtrFactory<RequestService> weak_ptr_factory_{this}; };
diff --git a/extensions/browser/api/BUILD.gn b/extensions/browser/api/BUILD.gn index ad3f563..a1caf57 100644 --- a/extensions/browser/api/BUILD.gn +++ b/extensions/browser/api/BUILD.gn
@@ -31,6 +31,7 @@ # TODO(https://crbug.com/356905053): Continue moving more APIs here. public_deps = [ "//extensions/browser/api/alarms", + "//extensions/browser/api/content_settings", "//extensions/browser/api/declarative_net_request", "//extensions/browser/api/dns", "//extensions/browser/api/file_handlers", @@ -64,7 +65,6 @@ "//extensions/browser/api/bluetooth", "//extensions/browser/api/bluetooth_low_energy", "//extensions/browser/api/bluetooth_socket", - "//extensions/browser/api/content_settings", "//extensions/browser/api/feedback_private", "//extensions/browser/api/file_system", "//extensions/browser/api/mime_handler_private",
diff --git a/extensions/browser/api/api_browser_context_keyed_service_factories.cc b/extensions/browser/api/api_browser_context_keyed_service_factories.cc index bd241c0..52117749 100644 --- a/extensions/browser/api/api_browser_context_keyed_service_factories.cc +++ b/extensions/browser/api/api_browser_context_keyed_service_factories.cc
@@ -7,6 +7,7 @@ #include "build/build_config.h" #include "build/chromeos_buildflags.h" #include "extensions/browser/api/alarms/alarm_manager.h" +#include "extensions/browser/api/content_settings/content_settings_service.h" #include "extensions/browser/api/declarative/rules_registry_service.h" #include "extensions/browser/api/declarative_net_request/rules_monitor_service.h" #include "extensions/browser/api/idle/idle_manager_factory.h" @@ -35,7 +36,6 @@ #include "extensions/browser/api/bluetooth_low_energy/bluetooth_low_energy_notify_session.h" #include "extensions/browser/api/bluetooth_socket/bluetooth_api_socket.h" #include "extensions/browser/api/bluetooth_socket/bluetooth_socket_event_dispatcher.h" -#include "extensions/browser/api/content_settings/content_settings_service.h" // nogncheck #include "extensions/browser/api/feedback_private/feedback_private_api.h" #include "extensions/browser/api/hid/hid_connection_resource.h" #include "extensions/browser/api/hid/hid_device_manager.h" @@ -72,6 +72,7 @@ void EnsureApiBrowserContextKeyedServiceFactoriesBuilt() { AlarmManager::GetFactoryInstance(); + ContentSettingsService::GetFactoryInstance(); declarative_net_request::RulesMonitorService::GetFactoryInstance(); IdleManagerFactory::GetInstance(); ManagementAPI::GetFactoryInstance(); @@ -124,7 +125,6 @@ #if BUILDFLAG(IS_CHROMEOS) ClipboardAPI::GetFactoryInstance(); #endif - ContentSettingsService::GetFactoryInstance(); FeedbackPrivateAPI::GetFactoryInstance(); HidDeviceManager::GetFactoryInstance(); #if BUILDFLAG(IS_CHROMEOS)
diff --git a/extensions/common/api/content_scripts.webidl b/extensions/common/api/content_scripts.webidl new file mode 100644 index 0000000..b325e022 --- /dev/null +++ b/extensions/common/api/content_scripts.webidl
@@ -0,0 +1,80 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +[ExternalExtensionType="extensionTypes.RunAt"] +typedef object ExtensionTypesRunAt; + +[ExternalExtensionType="extensionTypes.ExecutionWorld"] +typedef object ExtensionTypesExecutionWorld; + +// Describes a content script to be injected into a web page. +dictionary ContentScript { + // Specifies which pages this content script will be injected into. See + // <a href="develop/concepts/match-patterns">Match Patterns</a> for more + // details on the syntax of these strings. + required sequence<DOMString> matches; + + // Excludes pages that this content script would otherwise be injected into. + // See <a href="develop/concepts/match-patterns">Match Patterns</a> for more + // details on the syntax of these strings. + sequence<DOMString> exclude_matches; + + // The list of CSS files to be injected into matching pages. These are + // injected in the order they appear in this array, before any DOM is + // constructed or displayed for the page. + sequence<DOMString> css; + + // The list of JavaScript files to be injected into matching pages. These + // are injected in the order they appear in this array. + sequence<DOMString> js; + + // If specified true, it will inject into all frames, even if the frame is + // not the top-most frame in the tab. Each frame is checked independently + // for URL requirements; it will not inject into child frames if the URL + // requirements are not met. Defaults to false, meaning that only the top + // frame is matched. + boolean all_frames; + + // Whether the script should inject into any frames where the URL belongs to + // a scheme that would never match a specified Match Pattern, including + // about:, data:, blob:, and filesystem: schemes. In these cases, in order + // to determine if the script should inject, the origin of the URL is + // checked. If the origin is `null` (as is the case for data: URLs), then + // the "initiator" or "creator" origin is used (i.e., the origin of the + // frame that created or navigated this frame). Note that this may not + // be the parent frame, if the frame was navigated by another frame in the + // document hierarchy. + boolean match_origin_as_fallback; + + // Whether the script should inject into an about:blank frame where the + // parent or opener frame matches one of the patterns declared in matches. + // Defaults to false. + boolean match_about_blank; + + // Applied after matches to include only those URLs that also match this + // glob. Intended to emulate the + // <a href="http://wiki.greasespot.net/Metadata_Block#.40include">@include + // </a> Greasemonkey keyword. + sequence<DOMString> include_globs; + + // Applied after matches to exclude URLs that match this glob. Intended to + // emulate the + // <a href="https://wiki.greasespot.net/Metadata_Block#.40exclude">@exclude + // </a> Greasemonkey keyword. + sequence<DOMString> exclude_globs; + + // Specifies when JavaScript files are injected into the web page. The + // preferred and default value is <code>document_idle</code>. + ExtensionTypesRunAt run_at; + + // The JavaScript "world" to run the script in. Defaults to + // <code>ISOLATED</code>. Only available in Manifest V3 extensions. + ExtensionTypesExecutionWorld world; +}; + +// Stub namespace for the "content_scripts" manifest key. +[generate_error_messages, Namespace=contentScripts] +partial dictionary ExtensionManifest { + sequence<ContentScript> content_scripts; +};
diff --git a/extensions/common/api/schema.gni b/extensions/common/api/schema.gni index ff23310..412efe9 100644 --- a/extensions/common/api/schema.gni +++ b/extensions/common/api/schema.gni
@@ -40,7 +40,7 @@ # parsing. extensions_types_only_schema_files_ = [ "chrome_url_overrides.webidl", - "content_scripts.idl", + "content_scripts.webidl", "cross_origin_isolation.webidl", "extensions_manifest_types.json", "incognito.json",
diff --git a/ios/chrome/browser/ntp/ui_bundled/incognito/BUILD.gn b/ios/chrome/browser/ntp/ui_bundled/incognito/BUILD.gn index 3b3e9be..a66805d 100644 --- a/ios/chrome/browser/ntp/ui_bundled/incognito/BUILD.gn +++ b/ios/chrome/browser/ntp/ui_bundled/incognito/BUILD.gn
@@ -20,7 +20,6 @@ "//ios/chrome/browser/shared/ui/symbols", "//ios/chrome/browser/shared/ui/util", "//ios/chrome/browser/toolbar/legacy/ui_bundled/public:constants", - "//ios/chrome/browser/url_loading/model", "//ios/chrome/common:string_util", "//ios/chrome/common/ui/colors", "//ios/chrome/common/ui/util",
diff --git a/ios/chrome/browser/ntp/ui_bundled/incognito/incognito_view_controller.h b/ios/chrome/browser/ntp/ui_bundled/incognito/incognito_view_controller.h index 22dc1a9..fa2f56e 100644 --- a/ios/chrome/browser/ntp/ui_bundled/incognito/incognito_view_controller.h +++ b/ios/chrome/browser/ntp/ui_bundled/incognito/incognito_view_controller.h
@@ -7,16 +7,12 @@ #import <UIKit/UIKit.h> -class UrlLoadingBrowserAgent; +@protocol NewTabPageURLLoaderDelegate; @interface IncognitoViewController : UIViewController -// Init with the given loader object. `loader` may be nil, but isn't -// retained so it must outlive this controller. -// TODO(crbug.com/40228520): View controllers should not have access to -// model-layer objects. Create a mediator to connect model-layer class -// `UrlLoadingBrowserAgent` to the view controller. -- (instancetype)initWithUrlLoader:(UrlLoadingBrowserAgent*)URLLoader; +// Delegate to load URLs in the current tab. +@property(nonatomic, weak) id<NewTabPageURLLoaderDelegate> URLLoaderDelegate; @end
diff --git a/ios/chrome/browser/ntp/ui_bundled/incognito/incognito_view_controller.mm b/ios/chrome/browser/ntp/ui_bundled/incognito/incognito_view_controller.mm index 380e99a7..b60af2f 100644 --- a/ios/chrome/browser/ntp/ui_bundled/incognito/incognito_view_controller.mm +++ b/ios/chrome/browser/ntp/ui_bundled/incognito/incognito_view_controller.mm
@@ -4,42 +4,25 @@ #import "ios/chrome/browser/ntp/ui_bundled/incognito/incognito_view_controller.h" -#import "base/memory/raw_ptr.h" #import "ios/chrome/browser/ntp/ui_bundled/incognito/incognito_view.h" #import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_constants.h" #import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_url_loader_delegate.h" -#import "ios/chrome/browser/url_loading/model/url_loading_browser_agent.h" -#import "ios/chrome/browser/url_loading/model/url_loading_params.h" #import "ios/chrome/common/ui/colors/semantic_color_names.h" -@interface IncognitoViewController () <NewTabPageURLLoaderDelegate> +@interface IncognitoViewController () // The scrollview containing the actual views. @property(nonatomic, strong) UIScrollView* incognitoView; @end -@implementation IncognitoViewController { - // The UrlLoadingService associated with this view. - // TODO(crbug.com/40228520): View controllers should not have access to - // model-layer objects. Create a mediator to connect model-layer class - // `UrlLoadingBrowserAgent` to the view controller. - raw_ptr<UrlLoadingBrowserAgent, DanglingUntriaged> _URLLoader; // weak -} - -- (instancetype)initWithUrlLoader:(UrlLoadingBrowserAgent*)URLLoader { - self = [super init]; - if (self) { - _URLLoader = URLLoader; - } - return self; -} +@implementation IncognitoViewController - (void)viewDidLoad { self.overrideUserInterfaceStyle = UIUserInterfaceStyleDark; IncognitoView* view = [[IncognitoView alloc] initWithFrame:self.view.bounds]; - view.URLLoaderDelegate = self; + view.URLLoaderDelegate = self.URLLoaderDelegate; self.incognitoView = view; self.incognitoView.accessibilityIdentifier = kNTPIncognitoViewIdentifier; [self.incognitoView setAutoresizingMask:UIViewAutoresizingFlexibleHeight | @@ -52,10 +35,4 @@ [_incognitoView setDelegate:nil]; } -#pragma mark - NewTabPageURLLoaderDelegate - -- (void)loadURLInTab:(const GURL&)URL { - _URLLoader->Load(UrlLoadParams::InCurrentTab(URL)); -} - @end
diff --git a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_coordinator.mm b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_coordinator.mm index dd8dc034..5186a3ec 100644 --- a/ios/chrome/browser/ntp/ui_bundled/new_tab_page_coordinator.mm +++ b/ios/chrome/browser/ntp/ui_bundled/new_tab_page_coordinator.mm
@@ -81,6 +81,7 @@ #import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_component_factory_protocol.h" #import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_constants.h" #import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_content_delegate.h" +#import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_url_loader_delegate.h" #import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_controller_delegate.h" #import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_coordinator+Testing.h" #import "ios/chrome/browser/ntp/ui_bundled/new_tab_page_delegate.h" @@ -138,6 +139,7 @@ #import "ios/chrome/browser/toolbar/legacy/ui_bundled/public/fakebox_focuser.h" #import "ios/chrome/browser/toolbar/tab_group/coordinator/tab_group_indicator_coordinator.h" #import "ios/chrome/browser/url_loading/model/url_loading_browser_agent.h" +#import "ios/chrome/browser/url_loading/model/url_loading_params.h" #import "ios/chrome/browser/web/model/web_navigation_util.h" #import "ios/chrome/common/NSString+Chromium.h" #import "ios/chrome/common/material_timing.h" @@ -167,6 +169,7 @@ NewTabPageDelegate, NewTabPageHeaderCommands, NewTabPageActionsDelegate, + NewTabPageURLLoaderDelegate, OverscrollActionsControllerDelegate, ProfileStateObserver, SceneStateObserver, @@ -342,10 +345,8 @@ // Configures incognito NTP if user is in incognito mode. if (self.isOffTheRecord) { DCHECK(!self.incognitoViewController); - UrlLoadingBrowserAgent* URLLoader = - UrlLoadingBrowserAgent::FromBrowser(self.browser); - self.incognitoViewController = - [[IncognitoViewController alloc] initWithUrlLoader:URLLoader]; + self.incognitoViewController = [[IncognitoViewController alloc] init]; + self.incognitoViewController.URLLoaderDelegate = self; self.started = YES; return; } @@ -1279,6 +1280,13 @@ return self.authService->SigninEnabled(); } +#pragma mark - NewTabPageURLLoaderDelegate + +- (void)loadURLInTab:(const GURL&)URL { + UrlLoadingBrowserAgent::FromBrowser(self.browser) + ->Load(UrlLoadParams::InCurrentTab(URL)); +} + #pragma mark - NewTabPageActionsDelegate - (void)recentTabTileOpenedAtIndex:(NSUInteger)index {
diff --git a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1 index f8136709..8d91c72 100644 --- a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1 +++ b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@ -68701c367aa9c320e7d81bd4e09acc57791faba2 \ No newline at end of file +ff73c142ad02fd3d3e499fbad34afca4c8dda1a9 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1 b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1 index 0e49fd5..e3668de 100644 --- a/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1 +++ b/ios/google_internal/frameworks/ChromeExtensionKeychainInternal.framework.dSYM.ios_asan.zip.sha1
@@ -1 +1 @@ -9e25cb428caffc24e780a0bd10171625e2f5a2e2 \ No newline at end of file +be2f17f2274057fed8a2f7a0ef742a5c4ff0e16f \ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1 index a0669e34..65ae807 100644 --- a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1 +++ b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@ -46f584355ef136803b0a79ed179424374a8864a3 \ No newline at end of file +3fab3fd65e3305950e1c6ee4c3b945e401b4728b \ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1 b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1 index 56fbdab0..fa428b95 100644 --- a/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1 +++ b/ios/google_internal/frameworks/ChromeInternal.framework.dSYM.ios_asan.zip.sha1
@@ -1 +1 @@ -527a7ddc5b411293fc2616f4f9f4e93b2c8c6305 \ No newline at end of file +e18be4059bf75a990670e7f1f1c3e763bad9dd1e \ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1 b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1 index ef9df33b..5650149e 100644 --- a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1 +++ b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios.zip.sha1
@@ -1 +1 @@ -65e6b9a378e530dcaa6007c7262f23e9c7f2ded5 \ No newline at end of file +6b19308c728ed7e4b750c31e28109cf726601b2a \ No newline at end of file
diff --git a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1 b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1 index a444666..7222d56 100644 --- a/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1 +++ b/ios/google_internal/frameworks/ChromeSSOInternal.framework.dSYM.ios_asan.zip.sha1
@@ -1 +1 @@ -8289701351e488778e30461aefcbd7efccf028d8 \ No newline at end of file +7a6a556468286fd44cfafd5e9dd2884c411b64f9 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1 index 288e2f2..3ee098e 100644 --- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -f92626cd7c815774f1c0fdc3d9f41b4969fac617 \ No newline at end of file +ee91cbb3974338dc518cd74d42320904d0a39199 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1 index 1da0948..40dcf78 100644 --- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@ -1b342133f12e4d73539e9aef65b02840bc67842d \ No newline at end of file +937030f1a64ede920d669d7b6085a98bdc088c21 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1 index 6e8fddd..b35a3fd 100644 --- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -4845b05f59e543329814f86a9b360e7dc530e1e9 \ No newline at end of file +e51f23200cff6267d28c80381edb13a5952a6638 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1 index 13c52e2..8d65b5ff 100644 --- a/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_extension_keychain_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@ -7cd3c3c7ed9a8c60aeae8062de8e36a075acdd3a \ No newline at end of file +cf6d86339d042e9f6ae38ad3bd5a83959441b8ec \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 index 79ad84d..3744a014 100644 --- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -c510940a763880cf27d018e53453cc4d87b9c1d8 \ No newline at end of file +3d1c4d8fcfd6dc2a6c7ade79f66acee00c2a7644 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1 index 30cb02a..0e9c8ac 100644 --- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@ -5ca84f66f945e9d154c7eff4fc34f048e360da24 \ No newline at end of file +f4901ce56ec35ae4fd61a54ccf269d9ca58dddba \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 index af3908da..dcf4809 100644 --- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -3e94ac42450ebd2153cbc711478fafd73472d85c \ No newline at end of file +d0cf978dfde173a0b959f5bd06b15390f74cd1e8 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1 index 2dbffb7..77cf252 100644 --- a/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@ -cab9a4189a1179699cbbd9bb2114b16efef08971 \ No newline at end of file +18127472fef224904204d9ac77e13393be3fa8a1 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 index 18be966..e75fbbe 100644 --- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -638e9e919f4d8ec8797b2aaa60bfe03dcae3490d \ No newline at end of file +f2bad34fffe979b65023b9e407c4f9ee801cd418 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1 index 3da7453..97c52d2 100644 --- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@ -7b224a217461772817290f684321ff5f83858eb3 \ No newline at end of file +b19eacee68fd66a5ae93d22b7c003144c899f9c1 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 index b26e970e..7d99e55 100644 --- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -5b7c3f7977cc5ff61754182f9eaa358f78188ac1 \ No newline at end of file +a67d32e123afdb3919b76643a05ef418f2e907af \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1 index 4439415..cf081792 100644 --- a/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_sso_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@ -1655c86126c8b3511259ce445d969eeb07340fc2 \ No newline at end of file +978ffdf7df764c5d96d7b5bf30594970993e4a8a \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1 index 905a824..2aee7d63 100644 --- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -89af12397b5485913c0581123969c217f6551802 \ No newline at end of file +2c42885111ff287b51cbaddd521d288a209e26ce \ No newline at end of file
diff --git a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1 index 713043c..6564010 100644 --- a/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/chrome_test_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -14b50c45e7caed7acaf0ffe83e3015de6533711e \ No newline at end of file +69019f1d27be648e2dd61e0067f5741cbf043c5b \ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 index a6a9a164..e4f9371 100644 --- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -90d1ffa1ff30a8e034286ba9663ef5a65bcd9493 \ No newline at end of file +bae26756df481255e26cbf764ef3191e50e027ab \ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios_asan.zip.sha1 index 3b88356..b7de425 100644 --- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios_asan.zip.sha1 +++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@ -5013fc7c435afd6f0aac4368352bded387eccc56 \ No newline at end of file +91e7e94e5b1860b51290b209658002c56abf06eb \ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 index 35e55485..3086585 100644 --- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -f7382912f988440a469a6bb6a377a270078815d8 \ No newline at end of file +fbfafc4db346cd436c35f193b15d3b4db4fb9225 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator_asan.zip.sha1 index 4a94fe7..e3fefbc 100644 --- a/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator_asan.zip.sha1 +++ b/ios/google_internal/frameworks/remoting_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@ -8658494d2f61216126c9cb105927545544762ecc \ No newline at end of file +d284dbab60e07544c3e68e26240eac27b8a26915 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 index 2471110a..f35d02d 100644 --- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1 +++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios.zip.sha1
@@ -1 +1 @@ -96eccf6889832a26cdcb74a3f59f3f107c460c4f \ No newline at end of file +a1f7f584f65cabfe96c905b89d87ac267b44b9a4 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1 index a9e7969..179c7f5 100644 --- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1 +++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.ios_asan.zip.sha1
@@ -1 +1 @@ -debc34dc3416a785eacf029374ab23e2d9965a74 \ No newline at end of file +613f8e20bfa03e3bedceff00488a4a1b7ddfb350 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 index 7679cc2..5c44d56c81 100644 --- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1 +++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator.zip.sha1
@@ -1 +1 @@ -4d6f383e5d4c1dad8817aca1a1d90d7690e1a652 \ No newline at end of file +80c93cb8b1052db15cce13f7d8eded5ec55b7f82 \ No newline at end of file
diff --git a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1 b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1 index 0d8c851..a90da16 100644 --- a/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1 +++ b/ios/google_internal/frameworks/web_view_shell_internal_dynamic_framework.iossimulator_asan.zip.sha1
@@ -1 +1 @@ -fbe523f06e30096f97f554cebc361e8dc2c3c4fe \ No newline at end of file +0191c04ab31fd65b1e5fb014dec669d8bf09882a \ No newline at end of file
diff --git a/ios/web/js_features/crash_keys/resources/crash_keys.ts b/ios/web/js_features/crash_keys/resources/crash_keys.ts index b21e40dd..b962eb20 100644 --- a/ios/web/js_features/crash_keys/resources/crash_keys.ts +++ b/ios/web/js_features/crash_keys/resources/crash_keys.ts
@@ -27,10 +27,11 @@ } /** - * Returns a copy of the current crash keys. + * Returns the internal map of crash keys as an object so it can be passed + * across the JS->native bridge. */ -export function getCrashKeys(): Map<string, string> { - return new Map(getInternalMap()); +export function getCrashKeys(): Object { + return Object.fromEntries(getInternalMap()); } /**
diff --git a/ios/web/js_features/crash_keys/resources/crash_keys_test.ts b/ios/web/js_features/crash_keys/resources/crash_keys_test.ts index 71e4f1e6..4e8bf356 100644 --- a/ios/web/js_features/crash_keys/resources/crash_keys_test.ts +++ b/ios/web/js_features/crash_keys/resources/crash_keys_test.ts
@@ -13,8 +13,7 @@ gCrWeb.registerApi(crash_keys_tests); -crash_keys_tests.addFunction( - 'getCrashKeys', () => Object.fromEntries(getCrashKeys())); +crash_keys_tests.addFunction('getCrashKeys', getCrashKeys); crash_keys_tests.addFunction('setCrashKey', setCrashKey); crash_keys_tests.addFunction('clearCrashKey', clearCrashKey); crash_keys_tests.addFunction('clearAllCrashKeys', clearAllCrashKeys);
diff --git a/ios/web/js_features/window_error/ios_javascript_error_report.h b/ios/web/js_features/window_error/ios_javascript_error_report.h index 85a8f37d..0b59ddc 100644 --- a/ios/web/js_features/window_error/ios_javascript_error_report.h +++ b/ios/web/js_features/window_error/ios_javascript_error_report.h
@@ -5,6 +5,7 @@ #ifndef IOS_WEB_JS_FEATURES_WINDOW_ERROR_IOS_JAVASCRIPT_ERROR_REPORT_H_ #define IOS_WEB_JS_FEATURES_WINDOW_ERROR_IOS_JAVASCRIPT_ERROR_REPORT_H_ +#include <map> #include <optional> #include <string> @@ -62,6 +63,9 @@ // This can be important information for debugging since the full URL is not // available in `page_url` std::optional<std::string> page_url_file_extension; + + // The crash keys set in the JavaScript code before this error occurred. + std::optional<std::map<std::string, std::string>> crash_keys; }; #endif // IOS_WEB_JS_FEATURES_WINDOW_ERROR_IOS_JAVASCRIPT_ERROR_REPORT_H_
diff --git a/ios/web/js_features/window_error/script_error_details.h b/ios/web/js_features/window_error/script_error_details.h index e872fa7..6fc37ce9 100644 --- a/ios/web/js_features/window_error/script_error_details.h +++ b/ios/web/js_features/window_error/script_error_details.h
@@ -5,6 +5,7 @@ #ifndef IOS_WEB_JS_FEATURES_WINDOW_ERROR_SCRIPT_ERROR_DETAILS_H_ #define IOS_WEB_JS_FEATURES_WINDOW_ERROR_SCRIPT_ERROR_DETAILS_H_ +#import <map> #import <string> #import "url/gurl.h" @@ -35,6 +36,9 @@ // Whether or not this error occurred in the main frame. bool is_main_frame; + + // The crash keys set in the JavaScript code before this error occurred. + std::map<std::string, std::string> crash_keys; }; #endif // IOS_WEB_JS_FEATURES_WINDOW_ERROR_SCRIPT_ERROR_DETAILS_H_
diff --git a/ios/web/js_features/window_error/script_error_message_handler_java_script_feature.mm b/ios/web/js_features/window_error/script_error_message_handler_java_script_feature.mm index a4c5e60f..849310d 100644 --- a/ios/web/js_features/window_error/script_error_message_handler_java_script_feature.mm +++ b/ios/web/js_features/window_error/script_error_message_handler_java_script_feature.mm
@@ -23,6 +23,8 @@ static const char kScriptMessageResponseLineNumberKey[] = "line_number"; static const char kScriptMessageResponseMessageKey[] = "message"; static const char kScriptMessageResponseStackKey[] = "stack"; +static const char kScriptMessageResponseCrashKeys[] = "crashKeys"; + } // namespace namespace web { @@ -82,6 +84,14 @@ details.url = script_message.request_url().value(); } + const base::DictValue* crash_keys = + script_dict->FindDict(kScriptMessageResponseCrashKeys); + if (crash_keys) { + for (auto ck = crash_keys->begin(); ck != crash_keys->end(); ++ck) { + details.crash_keys.insert({ck->first, ck->second.GetString()}); + } + } + if (log_message && base::FeatureList::IsEnabled(features::kLogCrWebJavaScriptErrors)) { WebJsErrorReportProcessor::FromBrowserState(web_state->GetBrowserState())
diff --git a/ios/web/js_features/window_error/web_js_error_report_processor.mm b/ios/web/js_features/window_error/web_js_error_report_processor.mm index dfd92c77..23ef242 100644 --- a/ios/web/js_features/window_error/web_js_error_report_processor.mm +++ b/ios/web/js_features/window_error/web_js_error_report_processor.mm
@@ -196,6 +196,7 @@ report.api = details.api; report.error_message = details.message; report.stack_trace = RedactStack(details.stack); + report.crash_keys = details.crash_keys; report.page_url = details.url.GetWithEmptyPath().spec(); std::string filename = details.url.ExtractFileName(); @@ -295,6 +296,13 @@ base::NumberToString(error_report.error_code.value()); } + if (error_report.crash_keys) { + for (auto ck = error_report.crash_keys->begin(); + ck != error_report.crash_keys->end(); ++ck) { + params[base::StrCat({"JS_", ck->first})] = ck->second; + } + } + AddExperimentIds(params); const GURL url(base::StrCat(
diff --git a/ios/web/js_messaging/java_script_feature_util_impl.mm b/ios/web/js_messaging/java_script_feature_util_impl.mm index 96bd5c77..7b84c9a 100644 --- a/ios/web/js_messaging/java_script_feature_util_impl.mm +++ b/ios/web/js_messaging/java_script_feature_util_impl.mm
@@ -55,8 +55,13 @@ // {error_message} // {api}:{line_number} // {stack} + // {crash_keys} // {url} // {kMainFrameDescription|kIframeDescription} + std::string crash_keys_str; + for (const auto [key, value] : error_details.crash_keys) { + crash_keys_str += "\n " + key + ": " + value; + } const char* frame_description = error_details.is_main_frame ? kMainFrameDescription : kIframeDescription; @@ -64,7 +69,10 @@ << error_details.message << "\n" << error_details.api << ":" << error_details.line_number << "\n " << error_details.stack << "\n " - << error_details.url.spec() << "\n " << frame_description; + << "Crash Keys:" + << (crash_keys_str.empty() ? "None" : crash_keys_str) + << "\n " << error_details.url.spec() << "\n " + << frame_description; if (base::FeatureList::IsEnabled(features::kAssertOnJavaScriptErrors)) { CHECK(false) << "JavaScript error occurred with " "kAssertOnJavaScriptErrors enabled.";
diff --git a/ios/web/public/js_messaging/BUILD.gn b/ios/web/public/js_messaging/BUILD.gn index fb44e629..de9a930 100644 --- a/ios/web/public/js_messaging/BUILD.gn +++ b/ios/web/public/js_messaging/BUILD.gn
@@ -55,7 +55,10 @@ compile_ts("error_reporting") { sources = [ "resources/error_reporting.ts" ] - deps = [ ":util_scripts" ] + deps = [ + ":util_scripts", + "//ios/web/js_features/crash_keys:crash_keys_js", + ] } # `compile_ts` and `optimize_js` targets are defined separately here instead of
diff --git a/ios/web/public/js_messaging/resources/error_reporting.ts b/ios/web/public/js_messaging/resources/error_reporting.ts index a7f648f..5073639 100644 --- a/ios/web/public/js_messaging/resources/error_reporting.ts +++ b/ios/web/public/js_messaging/resources/error_reporting.ts
@@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import {clearAllCrashKeys, getCrashKeys} from '//ios/web/js_features/crash_keys/resources/crash_keys.js'; import {sendWebKitMessage} from '//ios/web/public/js_messaging/resources/utils.js'; /** @@ -24,9 +25,14 @@ } } if (errorMessage && errorStack) { - sendWebKitMessage( - 'WindowErrorResultHandler', - {'message': errorMessage, 'stack': errorStack, 'api': apiName}); + const crashKeys = getCrashKeys(); + clearAllCrashKeys(); + sendWebKitMessage('WindowErrorResultHandler', { + 'message': errorMessage, + 'stack': errorStack, + 'api': apiName, + 'crashKeys': crashKeys, + }); } } return undefined;
diff --git a/ios_internal b/ios_internal index f0f28e1..dc307889 160000 --- a/ios_internal +++ b/ios_internal
@@ -1 +1 @@ -Subproject commit f0f28e17f585b2fc159c8f571456d38592803f0d +Subproject commit dc307889eca75f759981b901f51303bd1703a003
diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn index 22c2ed5..64bb10c9 100644 --- a/media/gpu/BUILD.gn +++ b/media/gpu/BUILD.gn
@@ -772,6 +772,20 @@ } } +if (is_win) { + fuzzer_test("media_d3d12_video_encode_delegate_fuzzer") { + sources = [ "windows/d3d12_video_encode_delegate_fuzzer.cc" ] + deps = [ + ":gpu", + "//base", + "//media:test_support", + "//media/base/win:test_support", + "//testing/gmock", + "//testing/gtest", + ] + } +} + fuzzer_test("media_vp9_decoder_fuzzer") { sources = [ "vp9_decoder_fuzzer.cc" ] deps = [
diff --git a/media/gpu/h265_builder.cc b/media/gpu/h265_builder.cc index 37a2731..ba6415f 100644 --- a/media/gpu/h265_builder.cc +++ b/media/gpu/h265_builder.cc
@@ -25,8 +25,9 @@ builder.AppendBits(1, profile_tier_level.general_frame_only_constraint_flag); CHECK_LT(profile_tier_level.general_profile_idc, 4); - // Check general_profile_compatibility_flag[ 2 ] == 0 - CHECK(!(profile_tier_level.general_profile_compatibility_flags & 1 << 29)); + // We are not using the encoder for still image encoding, so the + // general_one_picture_only_constraint_flag should always be set to 0. In + // that case simply appending 43 zero bits is fine. builder.AppendBits(43, 0); // general_reserved_zero_43bits builder.AppendBits(1, 0); // general_inbld_flag }
diff --git a/media/gpu/windows/d3d12_video_encode_av1_delegate.cc b/media/gpu/windows/d3d12_video_encode_av1_delegate.cc index 708e2a44..9c2d0e3 100644 --- a/media/gpu/windows/d3d12_video_encode_av1_delegate.cc +++ b/media/gpu/windows/d3d12_video_encode_av1_delegate.cc
@@ -110,7 +110,7 @@ return sequence_header; } -AV1BitstreamBuilder::FrameHeader FillAV1BuilderFrameHeader( +std::optional<AV1BitstreamBuilder::FrameHeader> FillAV1BuilderFrameHeader( const D3D12VideoEncodeAV1Delegate::PictureControlFlags& picture_ctrl, const D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_CODEC_DATA& pic_params, const AV1BitstreamBuilder::SequenceHeader& sequence_header) { @@ -209,7 +209,7 @@ lr_unit_shift = 0; break; default: - NOTREACHED(); + return std::nullopt; } // Check if either restoration_u_tile_size or resotration_v_tile_size is // equal to resotration_y_tile_size, if so, lr_uv_shift is 0; otherwise, @@ -1211,8 +1211,14 @@ DVLOG(4) << PrintPostEncodeValues(post_encode_values); - auto frame_header = FillAV1BuilderFrameHeader(picture_ctrl_, picture_params_, - sequence_header_); + auto frame_header_or_error = FillAV1BuilderFrameHeader( + picture_ctrl_, picture_params_, sequence_header_); + if (!frame_header_or_error.has_value()) { + return {EncoderStatus::Codes::kEncoderHardwareDriverError, + "D3D12VideoEncodeAV1Delegate: invalid restoration tile size."}; + } + AV1BitstreamBuilder::FrameHeader frame_header = + std::move(frame_header_or_error).value(); if (!UpdateFrameHeaderPostEncode(enc_caps_.post_value_flags, post_encode_values, frame_header)) { return {EncoderStatus::Codes::kEncoderHardwareDriverError,
diff --git a/media/gpu/windows/d3d12_video_encode_delegate.cc b/media/gpu/windows/d3d12_video_encode_delegate.cc index 6c2c1eba..9f82d91 100644 --- a/media/gpu/windows/d3d12_video_encode_delegate.cc +++ b/media/gpu/windows/d3d12_video_encode_delegate.cc
@@ -558,6 +558,10 @@ return std::move(size_or_error).error(); } size_t size = std::move(size_or_error).value(); + if (size > bitstream_buffer.size()) { + return {EncoderStatus::Codes::kEncoderHardwareDriverError, + "Encoded bitstream exceeds output buffer size"}; + } D3D12_RANGE written_range{}; metadata.Commit(&written_range); EncoderStatus status = @@ -583,8 +587,11 @@ DXGI_FORMAT format, size_t max_num_ref_frames, bool use_texture_array) { - CHECK_GT(max_num_ref_frames, 0u); - CHECK_LE(max_num_ref_frames, kMaxDpbSize); + if (max_num_ref_frames == 0 || max_num_ref_frames > kMaxDpbSize) { + LOG(ERROR) << "Invalid max reference frames number: " << max_num_ref_frames + << " (should be between 1 and " << kMaxDpbSize << ")"; + return false; + } size_ = max_num_ref_frames; // We reserve one space in extra for the current frame.
diff --git a/media/gpu/windows/d3d12_video_encode_delegate_fuzzer.cc b/media/gpu/windows/d3d12_video_encode_delegate_fuzzer.cc new file mode 100644 index 0000000..c24f86a --- /dev/null +++ b/media/gpu/windows/d3d12_video_encode_delegate_fuzzer.cc
@@ -0,0 +1,1192 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/gpu/windows/d3d12_video_encode_delegate.h" + +#include <fuzzer/FuzzedDataProvider.h> +#include <stddef.h> +#include <stdint.h> + +#include <algorithm> +#include <limits> +#include <memory> +#include <vector> + +#include "base/containers/span.h" +#include "base/functional/bind.h" +#include "base/memory/scoped_refptr.h" +#include "base/memory/unsafe_shared_memory_region.h" +#include "build/buildflag.h" +#include "gpu/config/gpu_driver_bug_workarounds.h" +#include "media/base/bitrate.h" +#include "media/base/bitstream_buffer.h" +#include "media/base/video_codecs.h" +#include "media/base/video_encoder.h" +#include "media/base/video_types.h" +#include "media/base/win/d3d12_mocks.h" +#include "media/base/win/d3d12_video_mocks.h" +#include "media/gpu/windows/d3d12_video_encode_av1_delegate.h" +#include "media/gpu/windows/d3d12_video_encode_h264_delegate.h" +#include "media/gpu/windows/d3d12_video_helpers.h" +#include "media/gpu/windows/format_utils.h" +#include "media/media_buildflags.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "third_party/microsoft_dxheaders/src/include/directx/d3dx12_core.h" +#include "ui/gfx/color_space.h" +#include "ui/gfx/geometry/size.h" + +#if BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER) +#include "media/gpu/windows/d3d12_video_encode_h265_delegate.h" +#endif // BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER) + +namespace media { + +namespace { + +using ::testing::_; +using ::testing::NiceMock; +using ::testing::Return; + +constexpr uint32_t kMinFrameSize = 16; +constexpr uint32_t kMaxFrameSize = 1920; +constexpr size_t kMinPayloadSize = 1024; +constexpr size_t kMaxPayloadSize = 4096; + +struct Av1CodecSupportConfig { + uint32_t supported_interpolation_filters = + 1u << D3D12_VIDEO_ENCODER_AV1_INTERPOLATION_FILTERS_EIGHTTAP; + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAGS supported_feature_flags = + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_NONE; + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAGS required_feature_flags = + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_NONE; + std::array<uint32_t, 2> supported_tx_modes = { + D3D12_VIDEO_ENCODER_AV1_TX_MODE_FLAG_SELECT, + D3D12_VIDEO_ENCODER_AV1_TX_MODE_FLAG_SELECT, + }; + std::array<std::array<D3D12_VIDEO_ENCODER_AV1_RESTORATION_SUPPORT_FLAGS, 3>, + 3> + supported_restoration_params = {}; + D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES_FLAGS post_encode_flags = + D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES_FLAG_NONE; +}; + +struct HevcCodecSupportConfig { + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAGS support_flags = + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_NONE; + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE min_luma_cu_size = + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_8x8; + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE max_luma_cu_size = + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_64x64; + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE min_luma_tu_size = + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_4x4; + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE max_luma_tu_size = + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_32x32; + uint8_t max_transform_hierarchy_depth_inter = 0; + uint8_t max_transform_hierarchy_depth_intra = 0; +}; + +struct HevcEncoderSupportConfig { + D3D12_VIDEO_ENCODER_SUPPORT_FLAGS support_flags = + D3D12_VIDEO_ENCODER_SUPPORT_FLAG_GENERAL_SUPPORT_OK; + D3D12_VIDEO_ENCODER_VALIDATION_FLAGS validation_flags = + D3D12_VIDEO_ENCODER_VALIDATION_FLAG_NONE; + D3D12_VIDEO_ENCODER_PROFILE_HEVC suggested_profile = + D3D12_VIDEO_ENCODER_PROFILE_HEVC_MAIN; + D3D12_VIDEO_ENCODER_LEVELS_HEVC suggested_level = + D3D12_VIDEO_ENCODER_LEVELS_HEVC_31; + D3D12_VIDEO_ENCODER_TIER_HEVC suggested_tier = + D3D12_VIDEO_ENCODER_TIER_HEVC_MAIN; + uint32_t subregion_block_pixels_size = 16; +}; + +struct H264PictureControlSupportConfig { + uint8_t max_long_term_references = 0; + uint8_t max_dpb_capacity = 0; +}; + +struct H264CodecConfigurationSupportConfig { + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_H264_FLAGS support_flags = + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_H264_FLAG_NONE; + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264_SLICES_DEBLOCKING_MODE_FLAGS + deblocking_modes = + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264_SLICES_DEBLOCKING_MODE_FLAG_NONE; +}; + +Av1CodecSupportConfig ConsumeAv1CodecSupportConfig( + FuzzedDataProvider& provider) { + Av1CodecSupportConfig config; + config.supported_interpolation_filters = + provider.ConsumeIntegralInRange<uint32_t>( + 1u << D3D12_VIDEO_ENCODER_AV1_INTERPOLATION_FILTERS_EIGHTTAP, + (1u << D3D12_VIDEO_ENCODER_AV1_INTERPOLATION_FILTERS_SWITCHABLE)); + for (auto& mode : config.supported_tx_modes) { + mode = provider.ConsumeBool() + ? D3D12_VIDEO_ENCODER_AV1_TX_MODE_FLAG_SELECT + : D3D12_VIDEO_ENCODER_AV1_TX_MODE_FLAG_LARGEST; + } + const D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAGS base_features = + static_cast<D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAGS>( + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_CDEF_FILTERING | + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_ORDER_HINT_TOOLS | + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_LOOP_RESTORATION_FILTER | + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_PALETTE_ENCODING | + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_INTRA_BLOCK_COPY); + if (provider.ConsumeBool()) { + config.required_feature_flags = + provider.ConsumeBool() + ? base_features + : static_cast<D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAGS>(base_features & + ~1u); + } + if (provider.ConsumeBool()) { + config.supported_feature_flags = base_features; + } else if (config.required_feature_flags != + D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_NONE) { + config.supported_feature_flags = + provider.ConsumeBool() ? config.required_feature_flags + : D3D12_VIDEO_ENCODER_AV1_FEATURE_FLAG_NONE; + } + for (auto& type_params : config.supported_restoration_params) { + for (auto& plane_params : type_params) { + uint32_t mask = 0; + if (provider.ConsumeBool()) { + mask |= D3D12_VIDEO_ENCODER_AV1_RESTORATION_SUPPORT_FLAG_256x256; + } + if (provider.ConsumeBool()) { + mask |= D3D12_VIDEO_ENCODER_AV1_RESTORATION_SUPPORT_FLAG_128x128; + } + if (provider.ConsumeBool()) { + mask |= D3D12_VIDEO_ENCODER_AV1_RESTORATION_SUPPORT_FLAG_64x64; + } + if (provider.ConsumeBool()) { + mask |= D3D12_VIDEO_ENCODER_AV1_RESTORATION_SUPPORT_FLAG_32x32; + } + plane_params = + static_cast<D3D12_VIDEO_ENCODER_AV1_RESTORATION_SUPPORT_FLAGS>(mask); + } + } + static constexpr std::array kPostEncodeFlags = { + D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES_FLAG_QUANTIZATION, + D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES_FLAG_QUANTIZATION_DELTA, + D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES_FLAG_LOOP_FILTER, + D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES_FLAG_LOOP_FILTER_DELTA, + D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES_FLAG_CDEF_DATA, + D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES_FLAG_CONTEXT_UPDATE_TILE_ID, + D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES_FLAG_COMPOUND_PREDICTION_MODE, + D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES_FLAG_PRIMARY_REF_FRAME, + D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES_FLAG_REFERENCE_INDICES, + }; + for (const auto& flag : kPostEncodeFlags) { + if (provider.ConsumeBool()) { + config.post_encode_flags |= flag; + } + } + return config; +} + +HevcCodecSupportConfig ConsumeHevcCodecSupportConfig( + FuzzedDataProvider& provider) { + HevcCodecSupportConfig config; + static constexpr std::array kCuSizes = { + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_8x8, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_16x16, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_32x32, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_CUSIZE_64x64, + }; + static constexpr std::array kTuSizes = { + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_4x4, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_8x8, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_16x16, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_HEVC_TUSIZE_32x32, + }; + auto min_cu = provider.PickValueInArray(kCuSizes); + auto max_cu = provider.PickValueInArray(kCuSizes); + if (min_cu > max_cu) { + std::swap(min_cu, max_cu); + } + config.min_luma_cu_size = min_cu; + config.max_luma_cu_size = max_cu; + + auto min_tu = provider.PickValueInArray(kTuSizes); + auto max_tu = provider.PickValueInArray(kTuSizes); + if (min_tu > max_tu) { + std::swap(min_tu, max_tu); + } + config.min_luma_tu_size = min_tu; + config.max_luma_tu_size = max_tu; + + config.max_transform_hierarchy_depth_inter = + provider.ConsumeIntegral<uint8_t>(); + config.max_transform_hierarchy_depth_intra = + provider.ConsumeIntegral<uint8_t>(); + + static constexpr std::array kSupportFlags = { + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_BFRAME_LTR_COMBINED_SUPPORT, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_INTRA_SLICE_CONSTRAINED_ENCODING_SUPPORT, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_CONSTRAINED_INTRAPREDICTION_SUPPORT, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_SAO_FILTER_SUPPORT, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_ASYMETRIC_MOTION_PARTITION_SUPPORT, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_ASYMETRIC_MOTION_PARTITION_REQUIRED, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_TRANSFORM_SKIP_SUPPORT, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_DISABLING_LOOP_FILTER_ACROSS_SLICES_SUPPORT, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_P_FRAMES_IMPLEMENTED_AS_LOW_DELAY_B_FRAMES, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_NUM_REF_IDX_ACTIVE_OVERRIDE_FLAG_SLICE_SUPPORT, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_TRANSFORM_SKIP_ROTATION_ENABLED_SUPPORT, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_TRANSFORM_SKIP_ROTATION_ENABLED_REQUIRED, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_TRANSFORM_SKIP_CONTEXT_ENABLED_SUPPORT, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_TRANSFORM_SKIP_CONTEXT_ENABLED_REQUIRED, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_IMPLICIT_RDPCM_ENABLED_SUPPORT, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_IMPLICIT_RDPCM_ENABLED_REQUIRED, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_EXPLICIT_RDPCM_ENABLED_SUPPORT, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_EXPLICIT_RDPCM_ENABLED_REQUIRED, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_EXTENDED_PRECISION_PROCESSING_SUPPORT, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_EXTENDED_PRECISION_PROCESSING_REQUIRED, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_INTRA_SMOOTHING_DISABLED_SUPPORT, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_INTRA_SMOOTHING_DISABLED_REQUIRED, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_HIGH_PRECISION_OFFSETS_ENABLED_SUPPORT, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_HIGH_PRECISION_OFFSETS_ENABLED_REQUIRED, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_PERSISTENT_RICE_ADAPTATION_ENABLED_SUPPORT, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_PERSISTENT_RICE_ADAPTATION_ENABLED_REQUIRED, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_CABAC_BYPASS_ALIGNMENT_ENABLED_SUPPORT, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_CABAC_BYPASS_ALIGNMENT_ENABLED_REQUIRED, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_CROSS_COMPONENT_PREDICTION_ENABLED_FLAG_SUPPORT, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_CROSS_COMPONENT_PREDICTION_ENABLED_FLAG_REQUIRED, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_CHROMA_QP_OFFSET_LIST_ENABLED_FLAG_SUPPORT, + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_HEVC_FLAG_CHROMA_QP_OFFSET_LIST_ENABLED_FLAG_REQUIRED, + }; + for (auto flag : kSupportFlags) { + if (provider.ConsumeBool()) { + config.support_flags |= flag; + } + } + + return config; +} + +HevcEncoderSupportConfig ConsumeHevcEncoderSupportConfig( + FuzzedDataProvider& provider) { + HevcEncoderSupportConfig config; + config.support_flags = D3D12_VIDEO_ENCODER_SUPPORT_FLAG_GENERAL_SUPPORT_OK; + if (provider.ConsumeBool()) { + config.support_flags |= + D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_RECONFIGURATION_AVAILABLE; + } + if (provider.ConsumeBool()) { + config.support_flags |= + D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RESOLUTION_RECONFIGURATION_AVAILABLE; + } + if (provider.ConsumeBool()) { + config.support_flags |= + D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_VBV_SIZE_CONFIG_AVAILABLE; + } + if (provider.ConsumeBool()) { + config.support_flags |= + D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_FRAME_ANALYSIS_AVAILABLE; + } + if (provider.ConsumeBool()) { + config.support_flags |= + D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RECONSTRUCTED_FRAMES_REQUIRE_TEXTURE_ARRAYS; + } + if (provider.ConsumeBool()) { + config.support_flags |= + D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_DELTA_QP_AVAILABLE; + } + if (provider.ConsumeBool()) { + config.support_flags |= + D3D12_VIDEO_ENCODER_SUPPORT_FLAG_SUBREGION_LAYOUT_RECONFIGURATION_AVAILABLE; + } + if (provider.ConsumeBool()) { + config.support_flags |= + D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_ADJUSTABLE_QP_RANGE_AVAILABLE; + } + if (provider.ConsumeBool()) { + config.support_flags |= + D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_INITIAL_QP_AVAILABLE; + } + if (provider.ConsumeBool()) { + config.support_flags |= + D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_MAX_FRAME_SIZE_AVAILABLE; + } + if (provider.ConsumeBool()) { + config.support_flags |= + D3D12_VIDEO_ENCODER_SUPPORT_FLAG_SEQUENCE_GOP_RECONFIGURATION_AVAILABLE; + } + if (provider.ConsumeBool()) { + config.support_flags |= + D3D12_VIDEO_ENCODER_SUPPORT_FLAG_MOTION_ESTIMATION_PRECISION_MODE_LIMIT_AVAILABLE; + } + if (provider.ConsumeBool()) { + config.support_flags |= + D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_EXTENSION1_SUPPORT; + } + if (provider.ConsumeBool()) { + config.support_flags |= + D3D12_VIDEO_ENCODER_SUPPORT_FLAG_RATE_CONTROL_QUALITY_VS_SPEED_AVAILABLE; + } + if (provider.ConsumeBool()) { + config.support_flags |= + D3D12_VIDEO_ENCODER_SUPPORT_FLAG_READABLE_RECONSTRUCTED_PICTURE_LAYOUT_AVAILABLE; + } + config.suggested_profile = provider.ConsumeBool() + ? D3D12_VIDEO_ENCODER_PROFILE_HEVC_MAIN + : D3D12_VIDEO_ENCODER_PROFILE_HEVC_MAIN10; + config.suggested_level = provider.ConsumeBool() + ? D3D12_VIDEO_ENCODER_LEVELS_HEVC_1 + : D3D12_VIDEO_ENCODER_LEVELS_HEVC_31; + config.subregion_block_pixels_size = + provider.ConsumeIntegralInRange<uint32_t>( + std::numeric_limits<uint32_t>::min(), + std::numeric_limits<uint32_t>::max()); + return config; +} + +H264PictureControlSupportConfig ConsumeH264PictureControlSupportConfig( + FuzzedDataProvider& provider) { + H264PictureControlSupportConfig config; + config.max_long_term_references = provider.ConsumeIntegral<uint8_t>(); + config.max_dpb_capacity = provider.ConsumeIntegral<uint8_t>(); + return config; +} + +H264CodecConfigurationSupportConfig ConsumeH264CodecConfigurationSupportConfig( + FuzzedDataProvider& provider) { + H264CodecConfigurationSupportConfig config; + if (provider.ConsumeBool()) { + config.support_flags |= + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_H264_FLAG_CABAC_ENCODING_SUPPORT; + } + if (provider.ConsumeBool()) { + config.support_flags |= + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT_H264_FLAG_INTRA_SLICE_CONSTRAINED_ENCODING_SUPPORT; + } + if (provider.ConsumeBool()) { + config.deblocking_modes |= + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264_SLICES_DEBLOCKING_MODE_FLAG_NONE; + } + if (provider.ConsumeBool()) { + config.deblocking_modes |= + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264_SLICES_DEBLOCKING_MODE_FLAG_1_DISABLE_ALL_SLICE_BLOCK_EDGES; + } + if (provider.ConsumeBool()) { + config.deblocking_modes |= + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264_SLICES_DEBLOCKING_MODE_FLAG_0_ALL_LUMA_CHROMA_SLICE_BLOCK_EDGES_ALWAYS_FILTERED; + } + if (provider.ConsumeBool()) { + config.deblocking_modes |= + D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION_H264_SLICES_DEBLOCKING_MODE_FLAG_2_DISABLE_SLICE_BOUNDARIES_BLOCKS; + } + return config; +} + +D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES ConsumeAv1PostEncodeValues( + FuzzedDataProvider& provider) { + D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES values = {}; + + values.CDEF.CdefBits = provider.ConsumeIntegralInRange<uint64_t>( + std::numeric_limits<uint64_t>::min(), + std::numeric_limits<uint64_t>::max()); + values.CDEF.CdefDampingMinus3 = provider.ConsumeIntegralInRange<uint64_t>( + std::numeric_limits<uint64_t>::min(), + std::numeric_limits<uint64_t>::max()); + for (auto& value : values.CDEF.CdefYPriStrength) { + value = provider.ConsumeIntegralInRange<uint64_t>( + std::numeric_limits<uint64_t>::min(), + std::numeric_limits<uint64_t>::max()); + } + for (auto& value : values.CDEF.CdefYSecStrength) { + value = provider.ConsumeIntegralInRange<uint64_t>( + std::numeric_limits<uint64_t>::min(), + std::numeric_limits<uint64_t>::max()); + } + for (auto& value : values.CDEF.CdefUVPriStrength) { + value = provider.ConsumeIntegralInRange<uint64_t>( + std::numeric_limits<uint64_t>::min(), + std::numeric_limits<uint64_t>::max()); + } + for (auto& value : values.CDEF.CdefUVSecStrength) { + value = provider.ConsumeIntegralInRange<uint64_t>( + std::numeric_limits<uint64_t>::min(), + std::numeric_limits<uint64_t>::max()); + } + + for (auto& level : values.LoopFilter.LoopFilterLevel) { + level = provider.ConsumeIntegralInRange<uint64_t>( + std::numeric_limits<uint64_t>::min(), + std::numeric_limits<uint64_t>::max()); + } + values.LoopFilter.LoopFilterLevelU = + provider.ConsumeIntegralInRange<uint64_t>( + std::numeric_limits<uint64_t>::min(), + std::numeric_limits<uint64_t>::max()); + values.LoopFilter.LoopFilterLevelV = + provider.ConsumeIntegralInRange<uint64_t>( + std::numeric_limits<uint64_t>::min(), + std::numeric_limits<uint64_t>::max()); + values.LoopFilter.LoopFilterSharpnessLevel = + provider.ConsumeIntegralInRange<uint64_t>( + std::numeric_limits<uint64_t>::min(), + std::numeric_limits<uint64_t>::max()); + values.LoopFilter.LoopFilterDeltaEnabled = + provider.ConsumeIntegralInRange<uint64_t>( + std::numeric_limits<uint64_t>::min(), + std::numeric_limits<uint64_t>::max()); + values.LoopFilter.UpdateRefDelta = provider.ConsumeIntegralInRange<uint64_t>( + std::numeric_limits<uint64_t>::min(), + std::numeric_limits<uint64_t>::max()); + values.LoopFilter.UpdateModeDelta = provider.ConsumeIntegralInRange<uint64_t>( + std::numeric_limits<uint64_t>::min(), + std::numeric_limits<uint64_t>::max()); + for (auto& delta : values.LoopFilter.RefDeltas) { + delta = provider.ConsumeIntegralInRange<int64_t>( + std::numeric_limits<int64_t>::min(), + std::numeric_limits<int64_t>::max()); + } + for (auto& delta : values.LoopFilter.ModeDeltas) { + delta = provider.ConsumeIntegralInRange<int64_t>( + std::numeric_limits<int64_t>::min(), + std::numeric_limits<int64_t>::max()); + } + + values.LoopFilterDelta.DeltaLFPresent = + provider.ConsumeIntegralInRange<uint64_t>( + std::numeric_limits<uint64_t>::min(), + std::numeric_limits<uint64_t>::max()); + values.LoopFilterDelta.DeltaLFMulti = + provider.ConsumeIntegralInRange<uint64_t>( + std::numeric_limits<uint64_t>::min(), + std::numeric_limits<uint64_t>::max()); + values.LoopFilterDelta.DeltaLFRes = provider.ConsumeIntegralInRange<uint64_t>( + std::numeric_limits<uint64_t>::min(), + std::numeric_limits<uint64_t>::max()); + + values.Quantization.BaseQIndex = provider.ConsumeIntegralInRange<uint64_t>( + std::numeric_limits<uint64_t>::min(), + std::numeric_limits<uint64_t>::max()); + values.Quantization.YDCDeltaQ = provider.ConsumeIntegralInRange<int64_t>( + std::numeric_limits<int64_t>::min(), std::numeric_limits<int64_t>::max()); + values.Quantization.UDCDeltaQ = provider.ConsumeIntegralInRange<int64_t>( + std::numeric_limits<int64_t>::min(), std::numeric_limits<int64_t>::max()); + values.Quantization.UACDeltaQ = provider.ConsumeIntegralInRange<int64_t>( + std::numeric_limits<int64_t>::min(), std::numeric_limits<int64_t>::max()); + values.Quantization.VDCDeltaQ = provider.ConsumeIntegralInRange<int64_t>( + std::numeric_limits<int64_t>::min(), std::numeric_limits<int64_t>::max()); + values.Quantization.VACDeltaQ = provider.ConsumeIntegralInRange<int64_t>( + std::numeric_limits<int64_t>::min(), std::numeric_limits<int64_t>::max()); + values.Quantization.UsingQMatrix = provider.ConsumeIntegralInRange<uint64_t>( + std::numeric_limits<uint64_t>::min(), + std::numeric_limits<uint64_t>::max()); + values.Quantization.QMY = provider.ConsumeIntegralInRange<uint64_t>( + std::numeric_limits<uint64_t>::min(), + std::numeric_limits<uint64_t>::max()); + values.Quantization.QMU = provider.ConsumeIntegralInRange<uint64_t>( + std::numeric_limits<uint64_t>::min(), + std::numeric_limits<uint64_t>::max()); + values.Quantization.QMV = provider.ConsumeIntegralInRange<uint64_t>( + std::numeric_limits<uint64_t>::min(), + std::numeric_limits<uint64_t>::max()); + + values.QuantizationDelta.DeltaQPresent = + provider.ConsumeIntegralInRange<uint64_t>( + std::numeric_limits<uint64_t>::min(), + std::numeric_limits<uint64_t>::max()); + values.QuantizationDelta.DeltaQRes = + provider.ConsumeIntegralInRange<uint64_t>( + std::numeric_limits<uint64_t>::min(), + std::numeric_limits<uint64_t>::max()); + + if (provider.ConsumeBool()) { + values.SegmentationConfig.NumSegments = + provider.ConsumeIntegralInRange<uint64_t>( + std::numeric_limits<uint64_t>::min(), + std::numeric_limits<uint64_t>::max()); + } else { + values.SegmentationConfig.NumSegments = 0; + } + values.SegmentationConfig.UpdateMap = + provider.ConsumeIntegralInRange<uint64_t>( + std::numeric_limits<uint64_t>::min(), + std::numeric_limits<uint64_t>::max()); + values.SegmentationConfig.TemporalUpdate = + provider.ConsumeIntegralInRange<uint64_t>( + std::numeric_limits<uint64_t>::min(), + std::numeric_limits<uint64_t>::max()); + values.SegmentationConfig.UpdateData = + provider.ConsumeIntegralInRange<uint64_t>( + std::numeric_limits<uint64_t>::min(), + std::numeric_limits<uint64_t>::max()); + for (auto& segment : values.SegmentationConfig.SegmentsData) { + segment.EnabledFeatures = provider.ConsumeIntegralInRange<uint64_t>( + std::numeric_limits<uint64_t>::min(), + std::numeric_limits<uint64_t>::max()); + for (auto& value : segment.FeatureValue) { + value = provider.ConsumeIntegralInRange<int64_t>( + std::numeric_limits<int64_t>::min(), + std::numeric_limits<int64_t>::max()); + } + } + + values.PrimaryRefFrame = provider.ConsumeIntegralInRange<uint64_t>( + std::numeric_limits<uint64_t>::min(), + std::numeric_limits<uint64_t>::max()); + for (auto& index : values.ReferenceIndices) { + index = provider.ConsumeIntegralInRange<uint64_t>( + std::numeric_limits<uint64_t>::min(), + std::numeric_limits<uint64_t>::max()); + } + values.CompoundPredictionType = provider.ConsumeIntegralInRange<uint64_t>( + std::numeric_limits<uint64_t>::min(), + std::numeric_limits<uint64_t>::max()); + + return values; +} + +class FuzzerVideoEncoderWrapper : public D3D12VideoEncoderWrapper { + public: + FuzzerVideoEncoderWrapper( + size_t payload_size, + D3D12_VIDEO_ENCODER_CODEC codec, + std::optional<D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES> + av1_post_encode_values) + : D3D12VideoEncoderWrapper(nullptr, nullptr), + payload_size_(payload_size), + metadata_resource_(MakeComPtr<NiceMock<D3D12ResourceMock>>()) { + size_t metadata_size = sizeof(D3D12_VIDEO_ENCODER_OUTPUT_METADATA); + if (codec == D3D12_VIDEO_ENCODER_CODEC_AV1) { + metadata_size += + sizeof(D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA) + + sizeof( + D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_TILES) + + sizeof(D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES); + } + metadata_bytes_.resize(metadata_size); + D3D12_VIDEO_ENCODER_OUTPUT_METADATA metadata = {}; + metadata.EncodedBitstreamWrittenBytesCount = payload_size_; + metadata.WrittenSubregionsCount = 1; + base::span(metadata_bytes_) + .first(sizeof(metadata)) + .copy_from(base::byte_span_from_ref(metadata)); + + if (codec == D3D12_VIDEO_ENCODER_CODEC_AV1) { + size_t offset = sizeof(D3D12_VIDEO_ENCODER_OUTPUT_METADATA); + D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA subregion = {}; + subregion.bSize = static_cast<UINT64>(payload_size_); + base::span(metadata_bytes_) + .subspan(offset, sizeof(subregion)) + .copy_from(base::byte_span_from_ref(subregion)); + offset += sizeof(D3D12_VIDEO_ENCODER_FRAME_SUBREGION_METADATA); + + D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_TILES + tiles = {}; + base::span(metadata_bytes_) + .subspan(offset, sizeof(tiles)) + .copy_from(base::byte_span_from_ref(tiles)); + offset += sizeof( + D3D12_VIDEO_ENCODER_AV1_PICTURE_CONTROL_SUBREGIONS_LAYOUT_DATA_TILES); + + D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES post_encode_values = {}; + if (av1_post_encode_values.has_value()) { + post_encode_values = av1_post_encode_values.value(); + } + base::span(metadata_bytes_) + .subspan(offset, sizeof(post_encode_values)) + .copy_from(base::byte_span_from_ref(post_encode_values)); + } + + D3D12_RESOURCE_DESC metadata_desc = CD3DX12_RESOURCE_DESC::Buffer( + static_cast<UINT64>(metadata_bytes_.size())); + ON_CALL(*metadata_resource_.Get(), GetDesc) + .WillByDefault(Return(metadata_desc)); + ON_CALL(*metadata_resource_.Get(), Map) + .WillByDefault([this](UINT, const D3D12_RANGE*, void** data) { + *data = metadata_bytes_.data(); + return S_OK; + }); + ON_CALL(*metadata_resource_.Get(), Unmap) + .WillByDefault([](UINT, const D3D12_RANGE*) {}); + } + + bool Initialize(uint32_t) override { return true; } + bool Wait(D3D12FenceAndValue) override { return true; } + EncoderStatus Encode( + const D3D12_VIDEO_ENCODER_ENCODEFRAME_INPUT_ARGUMENTS&, + const D3D12_VIDEO_ENCODER_RECONSTRUCTED_PICTURE&) override { + return EncoderStatus::Codes::kOk; + } + + EncoderStatus::Or<ScopedD3D12ResourceMap> GetEncoderOutputMetadata() + const override { + ScopedD3D12ResourceMap map; + if (!map.Map(metadata_resource_.Get(), 0, nullptr)) { + return EncoderStatus::Codes::kEncoderInitializationError; + } + return map; + } + + EncoderStatus ReadbackBitstream(base::span<uint8_t> data) const override { + std::fill(data.begin(), data.end(), 0); + return EncoderStatus::Codes::kOk; + } + + private: + size_t payload_size_ = 0; + mutable std::vector<uint8_t> metadata_bytes_; + Microsoft::WRL::ComPtr<D3D12ResourceMock> metadata_resource_; +}; + +class FuzzerVideoProcessorWrapper : public D3D12VideoProcessorWrapper { + public: + explicit FuzzerVideoProcessorWrapper( + Microsoft::WRL::ComPtr<ID3D12VideoDevice> device) + : D3D12VideoProcessorWrapper(std::move(device)), + fence_(MakeComPtr<NiceMock<D3D12FenceMock>>()) {} + + bool Init() override { return true; } + bool Wait(D3D12FenceAndValue) override { return true; } + + D3D12FenceAndValue ProcessFrames(ID3D12Resource*, + UINT, + const gfx::ColorSpace&, + const gfx::Rect&, + ID3D12Resource*, + UINT, + const gfx::ColorSpace&, + const gfx::Rect&) override { + return {fence_, 1}; + } + + private: + Microsoft::WRL::ComPtr<ID3D12Fence> fence_; +}; + +std::unique_ptr<D3D12VideoEncoderWrapper> CreateFuzzerVideoEncoderWrapper( + size_t payload_size, + ID3D12VideoDevice*, + D3D12_VIDEO_ENCODER_CODEC codec, + const D3D12_VIDEO_ENCODER_PROFILE_DESC&, + const D3D12_VIDEO_ENCODER_LEVEL_SETTING&, + DXGI_FORMAT, + const D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION&, + const D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC&) { + return std::make_unique<FuzzerVideoEncoderWrapper>(payload_size, codec, + std::nullopt); +} + +std::unique_ptr<D3D12VideoEncoderWrapper> CreateFuzzerVideoEncoderWrapperAV1( + size_t payload_size, + D3D12_VIDEO_ENCODER_AV1_POST_ENCODE_VALUES post_encode_values, + ID3D12VideoDevice*, + D3D12_VIDEO_ENCODER_CODEC codec, + const D3D12_VIDEO_ENCODER_PROFILE_DESC&, + const D3D12_VIDEO_ENCODER_LEVEL_SETTING&, + DXGI_FORMAT, + const D3D12_VIDEO_ENCODER_CODEC_CONFIGURATION&, + const D3D12_VIDEO_ENCODER_PICTURE_RESOLUTION_DESC&) { + return std::make_unique<FuzzerVideoEncoderWrapper>(payload_size, codec, + post_encode_values); +} + +std::unique_ptr<D3D12VideoProcessorWrapper> CreateFuzzerVideoProcessorWrapper( + Microsoft::WRL::ComPtr<ID3D12VideoDevice>&& video_device) { + return std::make_unique<FuzzerVideoProcessorWrapper>(std::move(video_device)); +} + +gfx::Size ConsumeFrameSize(FuzzedDataProvider& provider) { + uint32_t width = + provider.ConsumeIntegralInRange<uint32_t>(kMinFrameSize, kMaxFrameSize); + uint32_t height = + provider.ConsumeIntegralInRange<uint32_t>(kMinFrameSize, kMaxFrameSize); + // 4:2:0 subsampling requires frame size to be even. + width &= ~1u; + height &= ~1u; + width = std::max(width, kMinFrameSize); + height = std::max(height, kMinFrameSize); + return gfx::Size(width, height); +} + +Microsoft::WRL::ComPtr<D3D12ResourceMock> CreateInputResource( + const gfx::Size& size, + DXGI_FORMAT format) { + auto resource = MakeComPtr<NiceMock<D3D12ResourceMock>>(); + D3D12_RESOURCE_DESC desc = + CD3DX12_RESOURCE_DESC::Tex2D(format, size.width(), size.height(), 1, 1); + ON_CALL(*resource.Get(), GetDesc).WillByDefault(Return(desc)); + return resource; +} + +void ConfigureDeviceCommon( + Microsoft::WRL::ComPtr<D3D12DeviceMock> device, + Microsoft::WRL::ComPtr<D3D12VideoDevice3Mock> video_device) { + ON_CALL(*video_device.Get(), QueryInterface(IID_ID3D12Device, _)) + .WillByDefault(SetComPointeeAndReturnOk<1>(device.Get())); + ON_CALL(*video_device.Get(), QueryInterface(IID_ID3D12VideoDevice1, _)) + .WillByDefault(SetComPointeeAndReturnOk<1>(video_device.Get())); + + ON_CALL(*device.Get(), CreateCommittedResource) + .WillByDefault([](const D3D12_HEAP_PROPERTIES*, D3D12_HEAP_FLAGS, + const D3D12_RESOURCE_DESC* desc, D3D12_RESOURCE_STATES, + const D3D12_CLEAR_VALUE*, REFIID, void** ppv) { + auto resource = MakeComPtr<NiceMock<D3D12ResourceMock>>(); + D3D12_RESOURCE_DESC copy = *desc; + ON_CALL(*resource.Get(), GetDesc).WillByDefault(Return(copy)); + resource->AddRef(); + *ppv = resource.Get(); + return S_OK; + }); +} + +void ConfigureVideoDeviceForH264( + D3D12VideoDevice3Mock* video_device, + const H264PictureControlSupportConfig& picture_support_config, + const H264CodecConfigurationSupportConfig& codec_support_config) { + ON_CALL(*video_device, CheckFeatureSupport) + .WillByDefault([picture_support_config, codec_support_config]( + D3D12_FEATURE_VIDEO feature, void* data, UINT) { + switch (feature) { + case D3D12_FEATURE_VIDEO_ENCODER_CODEC: { + auto* codec = + static_cast<D3D12_FEATURE_DATA_VIDEO_ENCODER_CODEC*>(data); + codec->IsSupported = codec->Codec == D3D12_VIDEO_ENCODER_CODEC_H264; + return S_OK; + } + case D3D12_FEATURE_VIDEO_ENCODER_PROFILE_LEVEL: { + auto* profile_level = + static_cast<D3D12_FEATURE_DATA_VIDEO_ENCODER_PROFILE_LEVEL*>( + data); + profile_level->IsSupported = true; + if (profile_level->MinSupportedLevel.pH264LevelSetting) { + *profile_level->MinSupportedLevel.pH264LevelSetting = + D3D12_VIDEO_ENCODER_LEVELS_H264_1; + } + if (profile_level->MaxSupportedLevel.pH264LevelSetting) { + *profile_level->MaxSupportedLevel.pH264LevelSetting = + D3D12_VIDEO_ENCODER_LEVELS_H264_31; + } + return S_OK; + } + case D3D12_FEATURE_VIDEO_ENCODER_INPUT_FORMAT: { + auto* input_format = + static_cast<D3D12_FEATURE_DATA_VIDEO_ENCODER_INPUT_FORMAT*>( + data); + input_format->IsSupported = true; + return S_OK; + } + case D3D12_FEATURE_VIDEO_ENCODER_CODEC_PICTURE_CONTROL_SUPPORT: { + auto* picture_control = static_cast< + D3D12_FEATURE_DATA_VIDEO_ENCODER_CODEC_PICTURE_CONTROL_SUPPORT*>( + data); + picture_control->IsSupported = true; + if (picture_control->PictureSupport.pH264Support) { + picture_control->PictureSupport.pH264Support + ->MaxLongTermReferences = + picture_support_config.max_long_term_references; + picture_control->PictureSupport.pH264Support->MaxDPBCapacity = + picture_support_config.max_dpb_capacity; + } + return S_OK; + } + case D3D12_FEATURE_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT: { + auto* config = static_cast< + D3D12_FEATURE_DATA_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT*>( + data); + config->IsSupported = true; + if (config->CodecSupportLimits.pH264Support) { + config->CodecSupportLimits.pH264Support->SupportFlags = + codec_support_config.support_flags; + config->CodecSupportLimits.pH264Support + ->DisableDeblockingFilterSupportedModes = + codec_support_config.deblocking_modes; + } + return S_OK; + } + case D3D12_FEATURE_VIDEO_ENCODER_SUPPORT: { + auto* support = + static_cast<D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT*>(data); + support->SupportFlags = + D3D12_VIDEO_ENCODER_SUPPORT_FLAG_GENERAL_SUPPORT_OK; + support->ValidationFlags = D3D12_VIDEO_ENCODER_VALIDATION_FLAG_NONE; + if (support->SuggestedProfile.pH264Profile) { + *support->SuggestedProfile.pH264Profile = + D3D12_VIDEO_ENCODER_PROFILE_H264_MAIN; + } + if (support->SuggestedLevel.pH264LevelSetting) { + *support->SuggestedLevel.pH264LevelSetting = + D3D12_VIDEO_ENCODER_LEVELS_H264_31; + } + return S_OK; + } + case D3D12_FEATURE_VIDEO_ENCODER_RATE_CONTROL_MODE: { + auto* rate_control = static_cast< + D3D12_FEATURE_DATA_VIDEO_ENCODER_RATE_CONTROL_MODE*>(data); + rate_control->IsSupported = true; + return S_OK; + } + default: + return E_INVALIDARG; + } + }); +} + +void ConfigureVideoDeviceForAV1(D3D12VideoDevice3Mock* video_device, + const Av1CodecSupportConfig& support_config) { + ON_CALL(*video_device, CheckFeatureSupport) + .WillByDefault([support_config](D3D12_FEATURE_VIDEO feature, void* data, + UINT) { + switch (feature) { + case D3D12_FEATURE_VIDEO_ENCODER_CODEC: { + auto* codec = + static_cast<D3D12_FEATURE_DATA_VIDEO_ENCODER_CODEC*>(data); + codec->IsSupported = codec->Codec == D3D12_VIDEO_ENCODER_CODEC_AV1; + return S_OK; + } + case D3D12_FEATURE_VIDEO_ENCODER_PROFILE_LEVEL: { + auto* profile_level = + static_cast<D3D12_FEATURE_DATA_VIDEO_ENCODER_PROFILE_LEVEL*>( + data); + profile_level->IsSupported = true; + return S_OK; + } + case D3D12_FEATURE_VIDEO_ENCODER_INPUT_FORMAT: { + auto* input_format = + static_cast<D3D12_FEATURE_DATA_VIDEO_ENCODER_INPUT_FORMAT*>( + data); + input_format->IsSupported = true; + return S_OK; + } + case D3D12_FEATURE_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT: { + auto* config_support = static_cast< + D3D12_FEATURE_DATA_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT*>( + data); + config_support->IsSupported = true; + if (config_support->CodecSupportLimits.pAV1Support) { + auto* av1 = config_support->CodecSupportLimits.pAV1Support; + av1->SupportedInterpolationFilters = static_cast< + D3D12_VIDEO_ENCODER_AV1_INTERPOLATION_FILTERS_FLAGS>( + support_config.supported_interpolation_filters); + av1->SupportedFeatureFlags = + support_config.supported_feature_flags; + av1->RequiredFeatureFlags = support_config.required_feature_flags; + av1->PostEncodeValuesFlags = support_config.post_encode_flags; + const size_t tx_mode_count = + std::min(std::size(av1->SupportedTxModes), + support_config.supported_tx_modes.size()); + auto src_tx_modes = base::span(support_config.supported_tx_modes); + auto src_tx_mode_it = src_tx_modes.begin(); + size_t tx_index = 0; + for (auto& mode : av1->SupportedTxModes) { + if (tx_index < tx_mode_count) { + mode = static_cast<D3D12_VIDEO_ENCODER_AV1_TX_MODE_FLAGS>( + *src_tx_mode_it); + ++src_tx_mode_it; + } else { + mode = D3D12_VIDEO_ENCODER_AV1_TX_MODE_FLAG_SELECT; + } + ++tx_index; + } + auto src_restoration = + base::span(support_config.supported_restoration_params); + auto src_restoration_it = src_restoration.begin(); + for (auto& type_params : av1->SupportedRestorationParams) { + auto src_plane_it = src_restoration_it->begin(); + for (auto& plane_params : type_params) { + plane_params = *src_plane_it; + ++src_plane_it; + } + ++src_restoration_it; + } + } + return S_OK; + } + case D3D12_FEATURE_VIDEO_ENCODER_SUPPORT1: { + auto* support = + static_cast<D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT1*>(data); + support->SupportFlags = + D3D12_VIDEO_ENCODER_SUPPORT_FLAG_GENERAL_SUPPORT_OK; + support->ValidationFlags = D3D12_VIDEO_ENCODER_VALIDATION_FLAG_NONE; + if (support->SuggestedProfile.pAV1Profile) { + *support->SuggestedProfile.pAV1Profile = + D3D12_VIDEO_ENCODER_AV1_PROFILE_MAIN; + } + if (support->SuggestedLevel.pAV1LevelSetting) { + support->SuggestedLevel.pAV1LevelSetting->Level = + D3D12_VIDEO_ENCODER_AV1_LEVELS_3_1; + support->SuggestedLevel.pAV1LevelSetting->Tier = + D3D12_VIDEO_ENCODER_AV1_TIER_MAIN; + } + if (support->pResolutionDependentSupport) { + support->pResolutionDependentSupport[0].SubregionBlockPixelsSize = + 16; + } + return S_OK; + } + case D3D12_FEATURE_VIDEO_ENCODER_RATE_CONTROL_MODE: { + auto* rate_control = static_cast< + D3D12_FEATURE_DATA_VIDEO_ENCODER_RATE_CONTROL_MODE*>(data); + rate_control->IsSupported = true; + return S_OK; + } + default: + return E_INVALIDARG; + } + }); +} + +#if BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER) +void ConfigureVideoDeviceForH265( + D3D12VideoDevice3Mock* video_device, + const HevcCodecSupportConfig& support_config, + const HevcEncoderSupportConfig& encoder_config) { + ON_CALL(*video_device, CheckFeatureSupport) + .WillByDefault([support_config, encoder_config]( + D3D12_FEATURE_VIDEO feature, void* data, UINT) { + switch (feature) { + case D3D12_FEATURE_VIDEO_ENCODER_CODEC: { + auto* codec = + static_cast<D3D12_FEATURE_DATA_VIDEO_ENCODER_CODEC*>(data); + codec->IsSupported = codec->Codec == D3D12_VIDEO_ENCODER_CODEC_HEVC; + return S_OK; + } + case D3D12_FEATURE_VIDEO_ENCODER_PROFILE_LEVEL: { + auto* profile_level = + static_cast<D3D12_FEATURE_DATA_VIDEO_ENCODER_PROFILE_LEVEL*>( + data); + profile_level->IsSupported = true; + if (profile_level->MinSupportedLevel.pHEVCLevelSetting) { + *profile_level->MinSupportedLevel.pHEVCLevelSetting = { + D3D12_VIDEO_ENCODER_LEVELS_HEVC_1, + D3D12_VIDEO_ENCODER_TIER_HEVC_MAIN}; + } + if (profile_level->MaxSupportedLevel.pHEVCLevelSetting) { + *profile_level->MaxSupportedLevel.pHEVCLevelSetting = { + D3D12_VIDEO_ENCODER_LEVELS_HEVC_31, + D3D12_VIDEO_ENCODER_TIER_HEVC_MAIN}; + } + return S_OK; + } + case D3D12_FEATURE_VIDEO_ENCODER_INPUT_FORMAT: { + auto* input_format = + static_cast<D3D12_FEATURE_DATA_VIDEO_ENCODER_INPUT_FORMAT*>( + data); + input_format->IsSupported = true; + return S_OK; + } + case D3D12_FEATURE_VIDEO_ENCODER_CODEC_PICTURE_CONTROL_SUPPORT: { + auto* picture_control = static_cast< + D3D12_FEATURE_DATA_VIDEO_ENCODER_CODEC_PICTURE_CONTROL_SUPPORT*>( + data); + picture_control->IsSupported = true; + if (picture_control->PictureSupport.pHEVCSupport) { + picture_control->PictureSupport.pHEVCSupport + ->MaxLongTermReferences = 1; + picture_control->PictureSupport.pHEVCSupport->MaxDPBCapacity = 16; + } + return S_OK; + } + case D3D12_FEATURE_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT: { + auto* config = static_cast< + D3D12_FEATURE_DATA_VIDEO_ENCODER_CODEC_CONFIGURATION_SUPPORT*>( + data); + config->IsSupported = true; + if (config->CodecSupportLimits.pHEVCSupport) { + *config->CodecSupportLimits.pHEVCSupport = { + .SupportFlags = support_config.support_flags, + .MinLumaCodingUnitSize = support_config.min_luma_cu_size, + .MaxLumaCodingUnitSize = support_config.max_luma_cu_size, + .MinLumaTransformUnitSize = support_config.min_luma_tu_size, + .MaxLumaTransformUnitSize = support_config.max_luma_tu_size, + .max_transform_hierarchy_depth_inter = + support_config.max_transform_hierarchy_depth_inter, + .max_transform_hierarchy_depth_intra = + support_config.max_transform_hierarchy_depth_intra, + }; + } + return S_OK; + } + case D3D12_FEATURE_VIDEO_ENCODER_SUPPORT: { + auto* support = + static_cast<D3D12_FEATURE_DATA_VIDEO_ENCODER_SUPPORT*>(data); + support->SupportFlags = encoder_config.support_flags; + support->ValidationFlags = encoder_config.validation_flags; + if (support->SuggestedProfile.pHEVCProfile) { + *support->SuggestedProfile.pHEVCProfile = + encoder_config.suggested_profile; + } + if (support->SuggestedLevel.pHEVCLevelSetting) { + *support->SuggestedLevel.pHEVCLevelSetting = { + encoder_config.suggested_level, + encoder_config.suggested_tier}; + } + if (support->pResolutionDependentSupport) { + support->pResolutionDependentSupport[0].SubregionBlockPixelsSize = + encoder_config.subregion_block_pixels_size; + } + return S_OK; + } + case D3D12_FEATURE_VIDEO_ENCODER_RATE_CONTROL_MODE: { + auto* rate_control = static_cast< + D3D12_FEATURE_DATA_VIDEO_ENCODER_RATE_CONTROL_MODE*>(data); + rate_control->IsSupported = true; + return S_OK; + } + default: + return E_INVALIDARG; + } + }); +} +#endif // BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER) + +VideoEncodeAccelerator::Config BuildConfig(VideoCodecProfile profile, + VideoPixelFormat format, + const gfx::Size& size, + uint32_t bitrate, + uint32_t framerate, + bool is_screen) { + VideoEncodeAccelerator::Config config; + config.input_format = format; + config.input_visible_size = size; + config.output_profile = profile; + config.bitrate = Bitrate::ConstantBitrate(bitrate); + config.framerate = framerate; + config.storage_type = VideoEncodeAccelerator::Config::StorageType::kShmem; + config.content_type = + is_screen ? VideoEncodeAccelerator::Config::ContentType::kDisplay + : VideoEncodeAccelerator::Config::ContentType::kCamera; + config.manual_reference_buffer_control = is_screen; + config.gop_length = framerate * 2; + return config; +} + +} // namespace + +int RunD3D12VideoEncodeDelegateFuzzer(FuzzedDataProvider& provider) { + auto device = MakeComPtr<NiceMock<D3D12DeviceMock>>(); + auto video_device = MakeComPtr<NiceMock<D3D12VideoDevice3Mock>>(); + ConfigureDeviceCommon(device, video_device); + + size_t payload_size = + provider.ConsumeIntegralInRange<size_t>(kMinPayloadSize, kMaxPayloadSize); + auto processor_factory = + base::BindRepeating(&CreateFuzzerVideoProcessorWrapper); + + gfx::Size frame_size = ConsumeFrameSize(provider); + uint32_t bitrate = provider.ConsumeIntegralInRange<uint32_t>(10000, 2000000); + uint32_t framerate = provider.ConsumeIntegralInRange<uint32_t>(1, 60); + bool is_screen = provider.ConsumeBool(); + + enum class CodecChoice { kH264, kH265, kAV1 }; + CodecChoice codec = + provider.ConsumeBool() ? CodecChoice::kH264 : CodecChoice::kAV1; +#if BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER) + if (provider.ConsumeBool()) { + codec = CodecChoice::kH265; + } +#endif // BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER) + + std::unique_ptr<D3D12VideoEncodeDelegate> delegate; + VideoEncodeAccelerator::Config config; + VideoPixelFormat input_format = PIXEL_FORMAT_NV12; + + switch (codec) { + case CodecChoice::kH264: { + auto h264_picture_support_config = + ConsumeH264PictureControlSupportConfig(provider); + auto h264_codec_support_config = + ConsumeH264CodecConfigurationSupportConfig(provider); + ConfigureVideoDeviceForH264(video_device.Get(), + h264_picture_support_config, + h264_codec_support_config); + bool use_high10 = provider.ConsumeBool(); + VideoCodecProfile profile = + use_high10 ? H264PROFILE_HIGH10PROFILE : H264PROFILE_MAIN; + input_format = use_high10 ? PIXEL_FORMAT_P010LE : PIXEL_FORMAT_NV12; + config = BuildConfig(profile, input_format, frame_size, bitrate, + framerate, is_screen); + delegate = std::make_unique<D3D12VideoEncodeH264Delegate>( + video_device, gpu::GpuDriverBugWorkarounds{}); + auto encoder_factory = + base::BindRepeating(&CreateFuzzerVideoEncoderWrapper, payload_size); + delegate->SetFactoriesForTesting(encoder_factory, processor_factory); + break; + } +#if BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER) + case CodecChoice::kH265: { + auto hevc_support_config = ConsumeHevcCodecSupportConfig(provider); + auto hevc_encoder_support_config = + ConsumeHevcEncoderSupportConfig(provider); + ConfigureVideoDeviceForH265(video_device.Get(), hevc_support_config, + hevc_encoder_support_config); + bool use_main10 = provider.ConsumeBool(); + VideoCodecProfile profile = + use_main10 ? HEVCPROFILE_MAIN10 : HEVCPROFILE_MAIN; + input_format = use_main10 ? PIXEL_FORMAT_P010LE : PIXEL_FORMAT_NV12; + config = BuildConfig(profile, input_format, frame_size, bitrate, + framerate, is_screen); + delegate = std::make_unique<D3D12VideoEncodeH265Delegate>( + video_device, gpu::GpuDriverBugWorkarounds{}); + auto encoder_factory = + base::BindRepeating(&CreateFuzzerVideoEncoderWrapper, payload_size); + delegate->SetFactoriesForTesting(encoder_factory, processor_factory); + break; + } +#endif // BUILDFLAG(ENABLE_HEVC_PARSER_AND_HW_DECODER) + case CodecChoice::kAV1: { + auto av1_support_config = ConsumeAv1CodecSupportConfig(provider); + ConfigureVideoDeviceForAV1(video_device.Get(), av1_support_config); + input_format = PIXEL_FORMAT_NV12; + config = BuildConfig(AV1PROFILE_PROFILE_MAIN, input_format, frame_size, + bitrate, framerate, is_screen); + delegate = std::make_unique<D3D12VideoEncodeAV1Delegate>( + video_device, gpu::GpuDriverBugWorkarounds{}); + auto post_encode_values = ConsumeAv1PostEncodeValues(provider); + auto encoder_factory = + base::BindRepeating(&CreateFuzzerVideoEncoderWrapperAV1, payload_size, + post_encode_values); + delegate->SetFactoriesForTesting(encoder_factory, processor_factory); + break; + } + } + + if (!delegate->Initialize(config).is_ok()) { + return 0; + } + + DXGI_FORMAT dxgi_format = VideoPixelFormatToDxgiFormat(input_format); + auto input_resource = CreateInputResource(frame_size, dxgi_format); + D3D12PictureBuffer picture_buffer(input_resource, 0, {}); + + auto shared_memory = base::UnsafeSharedMemoryRegion::Create(payload_size); + BitstreamBuffer bitstream_buffer(provider.ConsumeIntegral<int32_t>(), + shared_memory.Duplicate(), payload_size); + + VideoEncoder::EncodeOptions options(provider.ConsumeBool()); + if (provider.ConsumeBool()) { + options.quantizer = provider.ConsumeIntegralInRange<int>(1, 51); + } + if (provider.ConsumeBool()) { + size_t max_refs = delegate->GetMaxNumOfManualRefBuffers(); + if (max_refs > 0) { + size_t ref_count = provider.ConsumeIntegralInRange<size_t>(0, max_refs); + for (size_t i = 0; i < ref_count; ++i) { + options.reference_buffers.push_back( + static_cast<uint8_t>(provider.ConsumeIntegralInRange<int>( + 0, static_cast<int>(max_refs - 1)))); + } + if (provider.ConsumeBool()) { + options.update_buffer = static_cast<uint8_t>( + provider.ConsumeIntegralInRange<int>(0, max_refs - 1)); + } + } + } + + gfx::ColorSpace color_space = gfx::ColorSpace::CreateREC709(); + delegate->Encode(picture_buffer, color_space, bitstream_buffer, options); + + return 0; +} + +} // namespace media + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + FuzzedDataProvider provider(data, size); + return media::RunD3D12VideoEncodeDelegateFuzzer(provider); +}
diff --git a/media/gpu/windows/d3d12_video_encode_h265_delegate.cc b/media/gpu/windows/d3d12_video_encode_h265_delegate.cc index 3419679b..3592d00 100644 --- a/media/gpu/windows/d3d12_video_encode_h265_delegate.cc +++ b/media/gpu/windows/d3d12_video_encode_h265_delegate.cc
@@ -402,18 +402,18 @@ // Rate control. int qp = -1; if (software_rate_controller_) { - software_rate_controller_ - ->temporal_layers(metadata_.svc_generic->temporal_idx) + size_t temporal_idx = + metadata_.svc_generic ? metadata_.svc_generic->temporal_idx : 0; + software_rate_controller_->temporal_layers(temporal_idx) .ShrinkHRDBuffer(rate_controller_timestamp_); if (is_keyframe) { software_rate_controller_->EstimateIntraFrameQP( rate_controller_timestamp_); } else { software_rate_controller_->EstimateInterFrameQP( - metadata_.svc_generic->temporal_idx, rate_controller_timestamp_); + temporal_idx, rate_controller_timestamp_); } - qp = software_rate_controller_ - ->temporal_layers(metadata_.svc_generic->temporal_idx) + qp = software_rate_controller_->temporal_layers(temporal_idx) .curr_frame_qp(); } else if (options.quantizer.has_value()) { qp = options.quantizer.value(); @@ -645,6 +645,14 @@ if (!status.is_ok()) { return status; } + if (!std::has_single_bit( + resolution_support_limits_.SubregionBlockPixelsSize)) { + return { + EncoderStatus::Codes::kEncoderUnsupportedConfig, + base::StringPrintf( + "D3D12VideoEncoder reported invalid SubregionBlockPixelsSize %u", + resolution_support_limits_.SubregionBlockPixelsSize)}; + } encoder_support_flags_ = support.SupportFlags; h265_level_ = suggested_level;
diff --git a/mojo/public/rust/bindings/message.rs b/mojo/public/rust/bindings/message.rs index aaa3e90..85c4b83 100644 --- a/mojo/public/rust/bindings/message.rs +++ b/mojo/public/rust/bindings/message.rs
@@ -40,7 +40,7 @@ pub fn from_raw(msg: &RawMojoMessage) -> ParsingResult<Self> { // FOR_RELEASE: Make sure any MojoErrors are handled gracefully. let (raw_bytes, handles) = msg.read_data().unwrap(); - let (remaining_bytes, header) = MessageHeader::deserialize(&raw_bytes)?; + let (remaining_bytes, header) = MessageHeader::deserialize(raw_bytes)?; // FOR_RELEASE: Hopefully once we make our MojomMessage type better we // can avoid calling to_vec here. let payload = remaining_bytes.to_vec();
diff --git a/mojo/public/rust/bindings/test/tests.rs b/mojo/public/rust/bindings/test/tests.rs index 6196401dd..113c4dca 100644 --- a/mojo/public/rust/bindings/test/tests.rs +++ b/mojo/public/rust/bindings/test/tests.rs
@@ -390,7 +390,7 @@ let _receiver = pending_receiver.bind_with_options( SaturatingMathService {}, None, - Some(Box::new(move || (quit_loop)())), + Some(Box::new(quit_loop)), ); drop(pending_remote); @@ -402,7 +402,7 @@ // Test Receiver disconnect handler let (pending_remote, pending_receiver) = PendingRemote::<dyn MathService>::new_pipe().unwrap(); - let _remote = pending_remote.bind_with_options(None, Some(Box::new(move || (quit_loop)()))); + let _remote = pending_remote.bind_with_options(None, Some(Box::new(quit_loop))); drop(pending_receiver); run_loop.run();
diff --git a/net/cert/x509_util_unittest.cc b/net/cert/x509_util_unittest.cc index faab5ea..92c3dd0f 100644 --- a/net/cert/x509_util_unittest.cc +++ b/net/cert/x509_util_unittest.cc
@@ -781,8 +781,8 @@ } { // Last component is too big. - const uint8_t oid[] = {0x1, 0x2, 0x3, 0x82, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; + const uint8_t oid[] = {0x1, 0x2, 0x3, 0x82, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x0}; auto last = LastOidComponentFromBase(oid, base); EXPECT_EQ(last, std::nullopt); }
diff --git a/remoting/base/BUILD.gn b/remoting/base/BUILD.gn index 0af1540..bd25e510 100644 --- a/remoting/base/BUILD.gn +++ b/remoting/base/BUILD.gn
@@ -46,6 +46,8 @@ "auto_thread.h", "auto_thread_task_runner.cc", "auto_thread_task_runner.h", + "branding.cc", + "branding.h", "buffered_socket_writer.cc", "buffered_socket_writer.h", "capabilities.cc", @@ -135,6 +137,8 @@ "url_loader_network_service_observer.h", "url_request_context_getter.cc", "url_request_context_getter.h", + "username.cc", + "username.h", "util.cc", "util.h", "vlog_net_log.cc",
diff --git a/remoting/base/branding.cc b/remoting/base/branding.cc new file mode 100644 index 0000000..c3beac2 --- /dev/null +++ b/remoting/base/branding.cc
@@ -0,0 +1,87 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "remoting/base/branding.h" + +#include "base/base_paths.h" +#include "base/files/file_path.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "build/build_config.h" + +#if BUILDFLAG(IS_LINUX) +#include <unistd.h> + +#include "remoting/base/file_path_util_linux.h" +#include "remoting/base/username.h" +#endif + +namespace { + +// TODO(lambroslambrou): The default locations should depend on whether Chrome +// branding is enabled - this means also modifying the Python daemon script. +// The actual location of the files is ultimately determined by the service +// daemon and native messaging host - these defaults are only used in case the +// command-line switches are absent. +#if BUILDFLAG(IS_WIN) +#ifdef OFFICIAL_BUILD +const base::FilePath::CharType kConfigDir[] = + FILE_PATH_LITERAL("Google\\Chrome Remote Desktop"); +#else +const base::FilePath::CharType kConfigDir[] = FILE_PATH_LITERAL("Chromoting"); +#endif +#elif BUILDFLAG(IS_APPLE) +const base::FilePath::CharType kConfigDir[] = + FILE_PATH_LITERAL("Chrome Remote Desktop"); +#elif !BUILDFLAG(IS_LINUX) +const base::FilePath::CharType kConfigDir[] = + FILE_PATH_LITERAL(".config/chrome-remote-desktop"); +#endif + +#if !BUILDFLAG(IS_LINUX) +base::FilePath GetConfigDirWithPrefix(int prefix_path_key) { + base::FilePath app_data_dir; + base::PathService::Get(prefix_path_key, &app_data_dir); + if (app_data_dir.empty()) { + LOG(ERROR) << "Failed to get path for key: " << prefix_path_key; + return {}; + } + return app_data_dir.Append(kConfigDir); +} +#endif + +} // namespace + +namespace remoting { + +#if BUILDFLAG(IS_WIN) +const wchar_t kWindowsServiceName[] = L"chromoting"; +#endif + +base::FilePath GetConfigDir() { +#if BUILDFLAG(IS_LINUX) + if (getuid() == /*root*/ 0 || GetUsername() == GetNetworkProcessUsername()) { + // Processes run as root: + // daemon process, + // elevated native messaging host (for managing multi-process host) + // Processes run as network user: network process + return GetMultiProcessHostGlobalConfigDir(); + } else { + // Other processes: + // single-process host, + // desktop process, + // user launched processes (e.g. remoting-webauthn), + // unelevated native messaging host (for managing single-process host) + return GetPerUserConfigDir(); + } +#elif BUILDFLAG(IS_WIN) + return GetConfigDirWithPrefix(base::DIR_COMMON_APP_DATA); +#elif BUILDFLAG(IS_APPLE) + return GetConfigDirWithPrefix(base::DIR_APP_DATA); +#else + return GetConfigDirWithPrefix(base::DIR_HOME); +#endif +} + +} // namespace remoting
diff --git a/remoting/base/branding.h b/remoting/base/branding.h new file mode 100644 index 0000000..93822ff --- /dev/null +++ b/remoting/base/branding.h
@@ -0,0 +1,24 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef REMOTING_BASE_BRANDING_H_ +#define REMOTING_BASE_BRANDING_H_ + +#include "base/files/file_path.h" +#include "build/build_config.h" + +namespace remoting { + +#if BUILDFLAG(IS_WIN) +// Windows chromoting service name. +extern const wchar_t kWindowsServiceName[]; +#endif + +// Returns the a directory for storing chromoting config files. Depending on the +// platform, different users may get different config directories. +base::FilePath GetConfigDir(); + +} // namespace remoting + +#endif // REMOTING_BASE_BRANDING_H_
diff --git a/remoting/base/crash/crashpad_database_manager.cc b/remoting/base/crash/crashpad_database_manager.cc index 2839ffe..8564f47 100644 --- a/remoting/base/crash/crashpad_database_manager.cc +++ b/remoting/base/crash/crashpad_database_manager.cc
@@ -13,6 +13,7 @@ #include "base/i18n/time_formatting.h" #include "base/path_service.h" #include "base/strings/string_number_conversions.h" +#include "remoting/base/branding.h" #include "remoting/base/file_path_util_linux.h" #include "third_party/crashpad/crashpad/client/crash_report_database.h" #include "third_party/crashpad/crashpad/client/settings.h" @@ -48,7 +49,9 @@ #if BUILDFLAG(IS_WIN) base::PathService::Get(base::BasePathKey::DIR_ASSETS, &database_path); #else - database_path = GetConfigDirectoryPath(); + // TODO: crbug.com/475611769 - fix multi-process Linux host. The current + // implementation will create one crash database per Linux user. + database_path = GetConfigDir(); #endif return database_path.Append(kChromotingCrashpadDatabasePath); }
diff --git a/remoting/base/file_host_settings_linux.cc b/remoting/base/file_host_settings_linux.cc index 5da7a4da..87d0edd 100644 --- a/remoting/base/file_host_settings_linux.cc +++ b/remoting/base/file_host_settings_linux.cc
@@ -4,13 +4,14 @@ #include "remoting/base/file_host_settings.h" +#include "remoting/base/branding.h" #include "remoting/base/file_path_util_linux.h" namespace remoting { base::FilePath FileHostSettings::GetSettingsFilePath() { - return (base::FilePath( - GetConfigDirectoryPath().Append(GetHostHash() + ".settings.json"))); + return ( + base::FilePath(GetConfigDir().Append(GetHostHash() + ".settings.json"))); } } // namespace remoting
diff --git a/remoting/base/file_path_util_linux.cc b/remoting/base/file_path_util_linux.cc index 5b17b36..b24cb3e 100644 --- a/remoting/base/file_path_util_linux.cc +++ b/remoting/base/file_path_util_linux.cc
@@ -4,7 +4,8 @@ #include "remoting/base/file_path_util_linux.h" -#include "base/base_paths.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/path_service.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" @@ -12,16 +13,22 @@ #include "net/base/network_interfaces.h" namespace remoting { - -base::FilePath GetConfigDirectoryPath() { - base::FilePath homedir; - base::PathService::Get(base::DIR_HOME, &homedir); - return homedir.Append(".config/chrome-remote-desktop"); -} +namespace { +const base::FilePath::CharType kConfigDir[] = + FILE_PATH_LITERAL("chrome-remote-desktop"); +} // namespace std::string GetHostHash() { return "host#" + base::HexEncodeLower(crypto::obsolete::Md5::Hash(net::GetHostName())); } +base::FilePath GetMultiProcessHostGlobalConfigDir() { + return base::FilePath("/etc").Append(kConfigDir); +} + +base::FilePath GetPerUserConfigDir() { + return base::GetHomeDir().Append(".config").Append(kConfigDir); +} + } // namespace remoting
diff --git a/remoting/base/file_path_util_linux.h b/remoting/base/file_path_util_linux.h index a986b45..5ee8df8 100644 --- a/remoting/base/file_path_util_linux.h +++ b/remoting/base/file_path_util_linux.h
@@ -11,13 +11,26 @@ namespace remoting { -// Returns the path to the directory that store host configurations. -base::FilePath GetConfigDirectoryPath(); - // Returns a string that can be used to construct a host config file name, e.g. // "host#1234567890aabbccddeeff1234567890". +// DEPRECATED: This should only be used for the single-process host for +// compatibility reasons. New config/setting files should not have the host hash +// in the filename. std::string GetHostHash(); +// Returns the directory where the host config file for the multi-process host +// is located. Note that only processes run as root will have access to files in +// the directory. +base::FilePath GetMultiProcessHostGlobalConfigDir(); + +// Returns the per-user chromoting config directory. +// On the single-process host, this is where the host config file is located, +// i.e. this is what `GetConfigDir()` returns. +// On the multi-process host, this will return a path in the home directory of +// the user that the process is run as, so this is generally only useful for the +// desktop process, which is always run as the login user. +base::FilePath GetPerUserConfigDir(); + } // namespace remoting #endif // REMOTING_BASE_FILE_PATH_UTIL_LINUX_H_
diff --git a/remoting/host/base/username.cc b/remoting/base/username.cc similarity index 88% rename from remoting/host/base/username.cc rename to remoting/base/username.cc index f783b19..f7e5cb0 100644 --- a/remoting/host/base/username.cc +++ b/remoting/base/username.cc
@@ -1,13 +1,14 @@ -// Copyright 2013 The Chromium Authors +// Copyright 2026 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "remoting/host/base/username.h" +#include "remoting/base/username.h" #include <vector> #include "base/logging.h" #include "base/notimplemented.h" +#include "base/strings/cstring_view.h" #include "build/build_config.h" #if BUILDFLAG(IS_POSIX) @@ -85,4 +86,14 @@ #endif // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID) } +#if BUILDFLAG(IS_LINUX) + +base::cstring_view GetNetworkProcessUsername() { + // Should be in sync with CRD_NETWORK_USER in + // //remoting/host/installer/linux/debian/postinst + return "_crd_network"; +} + +#endif // BUILDFLAG(IS_LINUX) + } // namespace remoting
diff --git a/remoting/base/username.h b/remoting/base/username.h new file mode 100644 index 0000000..2843966 --- /dev/null +++ b/remoting/base/username.h
@@ -0,0 +1,26 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef REMOTING_BASE_USERNAME_H_ +#define REMOTING_BASE_USERNAME_H_ + +#include <string> + +#include "base/strings/cstring_view.h" +#include "build/build_config.h" + +namespace remoting { + +// Returns the username associated with this process, or the empty string on +// error or if not implemented. +std::string GetUsername(); + +#if BUILDFLAG(IS_LINUX) +// Returns the username that the network process is run as. +base::cstring_view GetNetworkProcessUsername(); +#endif // BUILDFLAG(IS_LINUX) + +} // namespace remoting + +#endif // REMOTING_BASE_USERNAME_H_
diff --git a/remoting/codec/audio_encoder_opus.cc b/remoting/codec/audio_encoder_opus.cc index 1167daa..249cd73 100644 --- a/remoting/codec/audio_encoder_opus.cc +++ b/remoting/codec/audio_encoder_opus.cc
@@ -28,6 +28,11 @@ constexpr AudioPacket::SamplingRate kOpusSamplingRate = AudioPacket::SAMPLING_RATE_48000; +// If not using `kOpusSampleRate`, the only other input sample rate we accept is +// 44.1kHz. +constexpr AudioPacket::SamplingRate kAltSamplingRate = + AudioPacket::SAMPLING_RATE_44100; + // Opus supports frame sizes of 2.5, 5, 10, 20, 40 and 60 ms. We use 20 ms // frames to balance latency and efficiency. constexpr base::TimeDelta kFrameDuration = base::Milliseconds(20); @@ -40,18 +45,122 @@ constexpr AudioPacket::BytesPerSample kBytesPerSample = AudioPacket::BYTES_PER_SAMPLE_2; +// Size in frames of the resampler's fill requests. +constexpr size_t kResamplerRequestSize = + media::SincResampler::kDefaultRequestSize; + constexpr bool IsSupportedSampleRate(int rate) { return rate == 44100 || rate == 48000; } } // namespace +class ResamplerFifoImpl : public AudioEncoderOpus::ResamplerFifo { + public: + explicit ResamplerFifoImpl(size_t size_in_frames, size_t channels) + : chunk_size_(kResamplerRequestSize * channels), + storage_buffer_( + base::AlignedUninit<int16_t>(size_in_frames * channels, + media::AudioBus::kChannelAlignment)), + crossover_buffer_( + base::AlignedUninit<int16_t>(chunk_size_, + media::AudioBus::kChannelAlignment)) {} + ~ResamplerFifoImpl() override = default; + + void AddNewSamples(base::span<const int16_t> samples) override { + CHECK(new_samples_.empty()); + new_samples_ = samples; + } + + [[nodiscard]] base::span<const int16_t> TakeChunk() override { + CHECK_GE(remaining_samples(), chunk_size_); + + if (saved_samples_.empty()) { + return new_samples_.take_first(chunk_size_); + } + + if (saved_samples_.size() > chunk_size_) { + return saved_samples_.take_first(chunk_size_); + } + + // If we're here, we have to combine some of the previous samples with the + // new ones. To return a continuous span, we have to make a temporary copy + // into `crossover_buffer_`. + const size_t new_samples_needed = chunk_size_ - saved_samples_.size(); + auto [saved_dest, new_dest] = + base::span(crossover_buffer_).split_at(saved_samples_.size()); + + saved_dest.copy_from_nonoverlapping(saved_samples_); + saved_samples_ = {}; + + new_dest.copy_from_nonoverlapping( + new_samples_.take_first(new_samples_needed)); + + // Return merged samples. + return crossover_buffer_; + } + + void SaveNewSamples() override { + CHECK_LE(remaining_samples(), storage_buffer_.size()); + + // No other saved samples. Copy directly. + if (saved_samples_.empty()) { + auto save_dest = storage_buffer_.first(new_samples_.size()); + save_dest.copy_from_nonoverlapping(new_samples_); + new_samples_ = {}; + saved_samples_ = save_dest; + return; + } + + // There are some saved samples remaining. Copy them to the front of + // `storage_buffer_` first, and then append the new samples. + const size_t total_size = remaining_samples(); + auto storage = storage_buffer_.first(total_size); + auto [previous_dest, new_dest] = storage.split_at(saved_samples_.size()); + + previous_dest.copy_from(saved_samples_); + new_dest.copy_from_nonoverlapping(new_samples_); + + new_samples_ = {}; + saved_samples_ = storage; + } + + size_t remaining_samples() const override { + return saved_samples_.size() + new_samples_.size(); + } + + size_t GetChunkSizeForTesting() const override { return chunk_size_; } + + private: + const size_t chunk_size_; + + // Location where `new_samples_` are copied, during `SaveNewSamples()`; + base::AlignedHeapArray<int16_t> storage_buffer_; + + // Temporary location where `saved_samples_` and `new_samples_` are stored, + // when there aren't enough saved samples to completely fill one chunk. + base::AlignedHeapArray<int16_t> crossover_buffer_; + + // Portion of `storage_buffer_` which contains unused samples. + base::raw_span<const int16_t> saved_samples_; + + // Points towards external, unowned memory. + base::raw_span<const int16_t> new_samples_; +}; + AudioEncoderOpus::AudioEncoderOpus() = default; AudioEncoderOpus::~AudioEncoderOpus() { DestroyEncoder(); } +// static +std::unique_ptr<AudioEncoderOpus::ResamplerFifo> +AudioEncoderOpus::GetEmptyFifoForTesting(size_t size_in_frames, + size_t channels) { + return std::make_unique<ResamplerFifoImpl>(size_in_frames, channels); +} + void AudioEncoderOpus::InitEncoder() { DCHECK(!encoder_); int error; @@ -64,30 +173,31 @@ opus_encoder_ctl(encoder_.get(), OPUS_SET_BITRATE(kOutputBitrateBps)); - frame_size_ = - media::AudioTimestampHelper::TimeToFrames(kFrameDuration, sampling_rate_); + needs_resampling_ = sampling_rate_ != kOpusSamplingRate; - if (sampling_rate_ != kOpusSamplingRate) { - size_t total_samples = - base::CheckMul(kOpusFrameCount, channels_).ValueOrDie<size_t>(); - resample_buffer_ = base::AlignedUninit<int16_t>( - total_samples, media::AudioBus::kChannelAlignment); - // TODO(sergeyu): Figure out the right buffer size to use per packet instead - // of using media::SincResampler::kDefaultRequestSize. + // Drop any previous samples. + leftover_encoder_samples_ = {}; + + if (needs_resampling_) { + CHECK_EQ(sampling_rate_, kAltSamplingRate); resampler_ = std::make_unique<media::MultiChannelResampler>( channels_, sampling_rate_ / double{kOpusSamplingRate}, - media::SincResampler::kDefaultRequestSize, + kResamplerRequestSize, base::BindRepeating(&AudioEncoderOpus::FetchBytesToResample, base::Unretained(this))); + + const size_t min_input_frames_needed = + resampler_->GetMaxInputFramesRequested(kOpusFrameCount); + + resampling_samples_needed_ = min_input_frames_needed * channels_; + + resampler_fifo_ = std::make_unique<ResamplerFifoImpl>( + resampling_samples_needed_, channels_); resampler_bus_ = media::AudioBus::Create(channels_, kOpusFrameCount); } - // Drop leftover data because it's for different sampling rate. - leftover_frames_ = 0; - leftover_samples_size_in_frames_ = - frame_size_ + media::SincResampler::kDefaultRequestSize; - leftover_samples_.reset( - new int16_t[leftover_samples_size_in_frames_ * channels_]); + encoder_samples_needed_ = kOpusFrameCount * channels_; + encoder_input_ = base::AlignedUninit<int16_t>(encoder_samples_needed_); } void AudioEncoderOpus::DestroyEncoder() { @@ -123,17 +233,12 @@ void AudioEncoderOpus::FetchBytesToResample(int resampler_frame_delay, media::AudioBus* audio_bus) { - DCHECK(resampling_data_); - int samples_left = (resampling_data_size_ - resampling_data_pos_) / - kBytesPerSample / channels_; - DCHECK_LE(audio_bus->frames(), samples_left); + CHECK(needs_resampling_); + CHECK_GE(resampler_fifo_->remaining_samples(), + static_cast<size_t>(audio_bus->frames() * channels_)); static_assert(kBytesPerSample == 2, "FromInterleaved expects 2 bytes."); audio_bus->FromInterleaved<media::SignedInt16SampleTypeTraits>( - reinterpret_cast<const int16_t*>( - UNSAFE_TODO(resampling_data_ + resampling_data_pos_)), - audio_bus->frames()); - resampling_data_pos_ += audio_bus->frames() * kBytesPerSample * channels_; - DCHECK_LE(resampling_data_pos_, static_cast<int>(resampling_data_size_)); + resampler_fifo_->TakeChunk()); } int AudioEncoderOpus::GetBitrate() { @@ -151,90 +256,91 @@ return nullptr; } - int frames_in_packet = packet->data(0).size() / kBytesPerSample / channels_; - const int16_t* next_sample = - UNSAFE_TODO(reinterpret_cast<const int16_t*>(packet->data(0).data())); + base::span<const uint8_t> byte_input = base::as_byte_span(packet->data(0)); + CHECK_EQ(byte_input.size() % kBytesPerSample, 0u); + CHECK(base::IsAligned(byte_input.data(), sizeof(int16_t))); + // SAFETY: This data is coming from an external source, but we've CHECK'ed + // that there are the right number of bytes, and that they have the proper + // alignment. + base::span<const int16_t> input_samples = UNSAFE_BUFFERS( + base::span(reinterpret_cast<const int16_t*>(byte_input.data()), + byte_input.size() / sizeof(int16_t))); + + if (needs_resampling_) { + return EncodeInternalWithResampling(input_samples); + } + + return EncodeInternal(input_samples); +} + +bool AudioEncoderOpus::EncodeData(base::span<const int16_t> samples, + AudioPacket* destination) { + CHECK_EQ(samples.size(), encoder_samples_needed_); + + // Initialize output buffer. + std::string* data = destination->add_data(); + data->resize(encoder_samples_needed_ * kBytesPerSample); + + // Encode. + unsigned char* buffer = reinterpret_cast<unsigned char*>(std::data(*data)); + int result = opus_encode(encoder_, samples.data(), kOpusFrameCount, buffer, + data->length()); + if (result < 0) { + LOG(ERROR) << "opus_encode() failed with error code: " << result; + return false; + } + + CHECK_LE(result, static_cast<int>(data->length())); + data->resize(result); + return true; +} + +std::unique_ptr<AudioPacket> AudioEncoderOpus::CreatePacket() { // Create a new packet of encoded data. auto encoded_packet = std::make_unique<AudioPacket>(); encoded_packet->set_encoding(AudioPacket::ENCODING_OPUS); encoded_packet->set_sampling_rate(kOpusSamplingRate); encoded_packet->set_channels(channels_); + return encoded_packet; +} - const int prefetch_frames = - resampler_.get() ? media::SincResampler::kDefaultRequestSize : 0; - int frames_wanted = frame_size_ + prefetch_frames; +std::unique_ptr<AudioPacket> AudioEncoderOpus::EncodeInternal( + base::span<const int16_t> input_samples) { + CHECK(!needs_resampling_); - while (leftover_frames_ + frames_in_packet >= frames_wanted) { - const int16_t* pcm_buffer = nullptr; + // Create a new packet of encoded data. + auto encoded_packet = CreatePacket(); - // Combine the packet with the leftover samples, if any. - if (leftover_frames_ > 0) { - pcm_buffer = leftover_samples_.get(); - const int frames_to_copy = frames_wanted - leftover_frames_; - UNSAFE_TODO(memcpy(leftover_samples_.get() + leftover_frames_ * channels_, - next_sample, - frames_to_copy * kBytesPerSample * channels_)); - } else { - pcm_buffer = next_sample; + while (leftover_encoder_samples_.size() + input_samples.size() >= + encoder_samples_needed_) { + // If there are no leftover frames, encode directly. + if (leftover_encoder_samples_.empty()) { + EncodeData(input_samples.take_first(encoder_samples_needed_), + encoded_packet.get()); + continue; } - // Resample data if necessary. - int frames_consumed = 0; - if (resampler_.get()) { - resampling_data_ = reinterpret_cast<const char*>(pcm_buffer); - resampling_data_pos_ = 0; - resampling_data_size_ = frames_wanted * channels_ * kBytesPerSample; - resampler_->Resample(kOpusFrameCount, resampler_bus_.get()); - resampling_data_ = nullptr; - frames_consumed = resampling_data_pos_ / channels_ / kBytesPerSample; + // Fill `encoder_input_` completely. + const size_t free_space_size = + encoder_samples_needed_ - leftover_encoder_samples_.size(); - static_assert(kBytesPerSample == 2, "ToInterleaved expects 2 bytes."); - resampler_bus_->ToInterleaved<media::SignedInt16SampleTypeTraits>( - resample_buffer_); - pcm_buffer = resample_buffer_.data(); - } else { - frames_consumed = frame_size_; - } + encoder_input_.subspan(leftover_encoder_samples_.size()) + .copy_from_nonoverlapping(input_samples.take_first(free_space_size)); - // Initialize output buffer. - std::string* data = encoded_packet->add_data(); - data->resize(kOpusFrameCount * kBytesPerSample * channels_); - - // Encode. - unsigned char* buffer = reinterpret_cast<unsigned char*>(std::data(*data)); - int result = opus_encode(encoder_, pcm_buffer, kOpusFrameCount, buffer, - data->length()); - if (result < 0) { - LOG(ERROR) << "opus_encode() failed with error code: " << result; - return nullptr; - } - - DCHECK_LE(result, static_cast<int>(data->length())); - data->resize(result); - - // Cleanup leftover buffer. - if (frames_consumed >= leftover_frames_) { - frames_consumed -= leftover_frames_; - leftover_frames_ = 0; - UNSAFE_TODO(next_sample += frames_consumed * channels_); - frames_in_packet -= frames_consumed; - } else { - leftover_frames_ -= frames_consumed; - UNSAFE_TODO(memmove(leftover_samples_.get(), - leftover_samples_.get() + frames_consumed * channels_, - leftover_frames_ * channels_ * kBytesPerSample)); - } + // Encode the samples. All clean leftovers, as they already encoded. + EncodeData(encoder_input_, encoded_packet.get()); + leftover_encoder_samples_ = {}; } - // Store the leftover samples. - if (frames_in_packet > 0) { - DCHECK_LE(leftover_frames_ + frames_in_packet, - leftover_samples_size_in_frames_); - UNSAFE_TODO(memmove(leftover_samples_.get() + leftover_frames_ * channels_, - next_sample, - frames_in_packet * kBytesPerSample * channels_)); - leftover_frames_ += frames_in_packet; + // Copy unused samples into `encoder_input_`. + if (!input_samples.empty()) { + CHECK_LT(input_samples.size(), encoder_samples_needed_); + const size_t used_space = leftover_encoder_samples_.size(); + encoder_input_.subspan(used_space, input_samples.size()) + .copy_from_nonoverlapping(input_samples); + leftover_encoder_samples_ = + encoder_input_.first(used_space + input_samples.size()); } // Return nullptr if there's nothing in the packet. @@ -245,4 +351,38 @@ return encoded_packet; } +std::unique_ptr<AudioPacket> AudioEncoderOpus::EncodeInternalWithResampling( + base::span<const int16_t> input_samples) { + CHECK(needs_resampling_); + CHECK(resampler_fifo_); + + // We always encode the full `encoder_samples_needed_` samples in + // `encoder_input_` at once. Leftover samples are stored in `resampling_fifo_` + // instead, at their original `kAltSamplingRate`. + CHECK(leftover_encoder_samples_.empty()); + + // Create a new packet of encoded data. + auto encoded_packet = CreatePacket(); + + // Add a reference to the incoming samples without copying them. + resampler_fifo_->AddNewSamples(input_samples); + + while (resampler_fifo_->remaining_samples() >= resampling_samples_needed_) { + resampler_->Resample(kOpusFrameCount, resampler_bus_.get()); + resampler_bus_->ToInterleaved<media::SignedInt16SampleTypeTraits>( + encoder_input_); + EncodeData(encoder_input_, encoded_packet.get()); + } + + // Save unused samples. + resampler_fifo_->SaveNewSamples(); + + // Return nullptr if there's nothing in the packet. + if (encoded_packet->data_size() == 0) { + return nullptr; + } + + return encoded_packet; +} + } // namespace remoting
diff --git a/remoting/codec/audio_encoder_opus.h b/remoting/codec/audio_encoder_opus.h index b048334..d0b90fe 100644 --- a/remoting/codec/audio_encoder_opus.h +++ b/remoting/codec/audio_encoder_opus.h
@@ -7,6 +7,7 @@ #include "base/memory/aligned_memory.h" #include "base/memory/raw_ptr.h" +#include "base/memory/raw_span.h" #include "remoting/codec/audio_encoder.h" #include "remoting/proto/audio.pb.h" @@ -23,6 +24,28 @@ class AudioEncoderOpus : public AudioEncoder { public: + // Helper class which segments samples into chunks, while minimizing copies. + // Exposed as an interface here for ease of testing. + class ResamplerFifo { + public: + virtual ~ResamplerFifo() = default; + + // Add samples to the FIFO, without copying them. + virtual void AddNewSamples(base::span<const int16_t> samples) = 0; + + // Copies unused samples added by `AddNewSamples()` to internal storage. + virtual void SaveNewSamples() = 0; + + // Consumes samples from the FIFO. + virtual base::span<const int16_t> TakeChunk() = 0; + + // Returns the number of samples currently in the FIFO, saved or not. + virtual size_t remaining_samples() const = 0; + + // Returns the size of each chunk returned by `TakeChunk()`. + virtual size_t GetChunkSizeForTesting() const = 0; + }; + AudioEncoderOpus(); AudioEncoderOpus(const AudioEncoderOpus&) = delete; @@ -35,32 +58,53 @@ std::unique_ptr<AudioPacket> packet) override; int GetBitrate() override; + static std::unique_ptr<ResamplerFifo> GetEmptyFifoForTesting( + size_t size_in_frames, + size_t channels); + private: void InitEncoder(); void DestroyEncoder(); bool ResetForPacket(AudioPacket* packet); + std::unique_ptr<AudioPacket> CreatePacket(); + + std::unique_ptr<AudioPacket> EncodeInternal(base::span<const int16_t> data); + std::unique_ptr<AudioPacket> EncodeInternalWithResampling( + base::span<const int16_t> data); + void FetchBytesToResample(int resampler_frame_delay, media::AudioBus* audio_bus); + bool EncodeData(base::span<const int16_t> data, AudioPacket* destination); + + bool needs_resampling_ = false; + + // Holds samples (always at 48kHz) that have not yet been encoded. + base::AlignedHeapArray<int16_t> encoder_input_; + + // The portion of `encoder_input_` which contains samples that have not yet + // been encoded. + // Unused when `needs_resampling_` is true, since extra samples will be stored + // in `resampler_fifo_` instead. + base::raw_span<int16_t> leftover_encoder_samples_; + + // Number of samples needed to encode a single "Opus frame". + size_t encoder_samples_needed_ = 0; + + // Manages samples which have not been resampled yet. + std::unique_ptr<ResamplerFifo> resampler_fifo_; + + // The minimum number of samples needed to guarantee to have enough for + // one resample call. + size_t resampling_samples_needed_ = 0; + int sampling_rate_ = 0; AudioPacket::Channels channels_ = AudioPacket::CHANNELS_STEREO; raw_ptr<OpusEncoder, DanglingUntriaged> encoder_ = nullptr; - int frame_size_ = 0; std::unique_ptr<media::MultiChannelResampler> resampler_; - base::AlignedHeapArray<int16_t> resample_buffer_; std::unique_ptr<media::AudioBus> resampler_bus_; - - // Used to pass packet to the FetchBytesToResampler() callback. - const char* resampling_data_ = nullptr; - int resampling_data_size_ = 0; - int resampling_data_pos_ = 0; - - // Left-over unencoded samples from the previous AudioPacket. - std::unique_ptr<int16_t[]> leftover_samples_; - int leftover_samples_size_in_frames_ = 0; - int leftover_frames_ = 0; }; } // namespace remoting
diff --git a/remoting/codec/audio_encoder_opus_unittest.cc b/remoting/codec/audio_encoder_opus_unittest.cc index 0e50ab86..2a242aad 100644 --- a/remoting/codec/audio_encoder_opus_unittest.cc +++ b/remoting/codec/audio_encoder_opus_unittest.cc
@@ -24,8 +24,6 @@ // Maximum value that can be encoded in a 16-bit signed sample. const int kMaxSampleValue = 32767; -const int kChannels = 2; - // Phase shift between left and right channels. const double kChannelPhaseShift = 2 * std::numbers::pi / 3; @@ -71,31 +69,34 @@ std::unique_ptr<AudioPacket> CreatePacket(int samples, AudioPacket::SamplingRate rate, double frequency_hz, - int pos) { - std::vector<int16_t> data(samples * kChannels); + int pos, + int channels) { + std::vector<int16_t> data(samples * channels); for (int i = 0; i < samples; ++i) { - data[i * kChannels] = GetSampleValue(rate, frequency_hz, i + pos, 0); - data[i * kChannels + 1] = GetSampleValue(rate, frequency_hz, i + pos, 1); + for (int j = 0; j < channels; ++j) { + data[i * channels + j] = GetSampleValue(rate, frequency_hz, i + pos, j); + } } std::unique_ptr<AudioPacket> packet(new AudioPacket()); - packet->add_data(reinterpret_cast<char*>(&(data[0])), - samples * kChannels * sizeof(int16_t)); + packet->add_data(reinterpret_cast<char*>(data.data()), + samples * channels * sizeof(int16_t)); packet->set_encoding(AudioPacket::ENCODING_RAW); packet->set_sampling_rate(rate); packet->set_bytes_per_sample(AudioPacket::BYTES_PER_SAMPLE_2); - packet->set_channels(AudioPacket::CHANNELS_STEREO); + packet->set_channels(static_cast<AudioPacket::Channels>(channels)); return packet; } // Decoded data is normally shifted in phase relative to the original signal. // This function returns the approximate shift in samples by finding the first // point when signal goes from negative to positive. - double EstimateSignalShift(const std::vector<int16_t>& received_data) { + double EstimateSignalShift(const std::vector<int16_t>& received_data, + int channels) { for (size_t i = kSkippedFirstSamples; - i < received_data.size() / kChannels - 1; i++) { - int16_t this_sample = received_data[i * kChannels]; - int16_t next_sample = received_data[(i + 1) * kChannels]; + i < received_data.size() / channels - 1; i++) { + int16_t this_sample = received_data[i * channels]; + int16_t next_sample = received_data[(i + 1) * channels]; if (this_sample < 0 && next_sample > 0) { return i + static_cast<double>(-this_sample) / (next_sample - this_sample); @@ -110,17 +111,17 @@ void ValidateReceivedData(int samples, AudioPacket::SamplingRate rate, double frequency_hz, - const std::vector<int16_t>& received_data) { - double shift = EstimateSignalShift(received_data); + const std::vector<int16_t>& received_data, + int channels) { + double shift = EstimateSignalShift(received_data, channels); double diff_sqare_sum = 0; - for (size_t i = kSkippedFirstSamples; i < received_data.size() / kChannels; + for (size_t i = kSkippedFirstSamples; i < received_data.size() / channels; i++) { - double d = received_data[i * kChannels] - - GetSampleValue(rate, frequency_hz, i - shift, 0); - diff_sqare_sum += d * d; - d = received_data[i * kChannels + 1] - - GetSampleValue(rate, frequency_hz, i - shift, 1); - diff_sqare_sum += d * d; + for (int j = 0; j < channels; ++j) { + double d = received_data[i * channels + j] - + GetSampleValue(rate, frequency_hz, i - shift, j); + diff_sqare_sum += d * d; + } } double deviation = std::sqrt(diff_sqare_sum / received_data.size()) / kMaxSampleValue; @@ -130,7 +131,8 @@ void TestEncodeDecode(int packet_size, double frequency_hz, - AudioPacket::SamplingRate rate) { + AudioPacket::SamplingRate rate, + int channels = 2) { const int kTotalTestSamples = 24000; encoder_ = std::make_unique<AudioEncoderOpus>(); @@ -140,7 +142,7 @@ int pos = 0; for (; pos < kTotalTestSamples; pos += packet_size) { std::unique_ptr<AudioPacket> source_packet = - CreatePacket(packet_size, rate, frequency_hz, pos); + CreatePacket(packet_size, rate, frequency_hz, pos, channels); std::unique_ptr<AudioPacket> encoded = encoder_->Encode(std::move(source_packet)); if (encoded.get()) { @@ -159,11 +161,11 @@ // Verify that at most kMaxLatencyMs worth of samples is buffered inside // |encoder_| and |decoder_|. - EXPECT_GE(static_cast<int>(received_data.size()) / kChannels, + EXPECT_GE(static_cast<int>(received_data.size()) / channels, pos - rate * kMaxLatencyMs / 1000); ValidateReceivedData(packet_size, kDefaultSamplingRate, frequency_hz, - received_data); + received_data, channels); } protected: @@ -191,4 +193,207 @@ TestEncodeDecode(5000, 3000, AudioPacket::SAMPLING_RATE_44100); } +TEST_F(OpusAudioEncoderTest, Mono) { + TestEncodeDecode(2000, 3000, AudioPacket::SAMPLING_RATE_48000, 1); +} + +TEST_F(OpusAudioEncoderTest, DynamicConfigChange) { + encoder_ = std::make_unique<AudioEncoderOpus>(); + decoder_ = std::make_unique<AudioDecoderOpus>(); + + auto test_config = [&](int samples, AudioPacket::SamplingRate rate, + int channels) { + std::unique_ptr<AudioPacket> source_packet = + CreatePacket(samples, rate, 3000, 0, channels); + std::unique_ptr<AudioPacket> encoded = + encoder_->Encode(std::move(source_packet)); + // It might take multiple packets to get output due to buffering, + // but here we just want to ensure it doesn't crash and eventually + // produces something or handles the reset. + if (encoded) { + std::unique_ptr<AudioPacket> decoded = + decoder_->Decode(std::move(encoded)); + EXPECT_EQ(kDefaultSamplingRate, decoded->sampling_rate()); + EXPECT_EQ(channels, decoded->channels()); + } + }; + + // Switch between various configs. + test_config(2000, AudioPacket::SAMPLING_RATE_48000, 2); + test_config(2000, AudioPacket::SAMPLING_RATE_44100, 2); + test_config(2000, AudioPacket::SAMPLING_RATE_48000, 1); + test_config(2000, AudioPacket::SAMPLING_RATE_44100, 1); +} + +TEST_F(OpusAudioEncoderTest, UnsupportedParameters) { + encoder_ = std::make_unique<AudioEncoderOpus>(); + + // 3 channels (Unsupported). + auto packet = + CreatePacket(2000, AudioPacket::SAMPLING_RATE_48000, 3000, 0, 3); + EXPECT_FALSE(encoder_->Encode(std::move(packet))); + + // Unsupported sampling rate. + packet = CreatePacket(2000, static_cast<AudioPacket::SamplingRate>(8000), + 3000, 0, 2); + EXPECT_FALSE(encoder_->Encode(std::move(packet))); +} + +TEST_F(OpusAudioEncoderTest, SmallPackets) { + encoder_ = std::make_unique<AudioEncoderOpus>(); + + // Send 10ms of 48kHz audio (480 samples). Opus frame is 20ms. + auto packet = CreatePacket(480, AudioPacket::SAMPLING_RATE_48000, 3000, 0, 2); + auto encoded = encoder_->Encode(std::move(packet)); + EXPECT_FALSE(encoded); // Should be buffered. + + // Send another 10ms. + packet = CreatePacket(480, AudioPacket::SAMPLING_RATE_48000, 3000, 480, 2); + encoded = encoder_->Encode(std::move(packet)); + EXPECT_TRUE(encoded); // Now we should have a full 20ms frame. +} + +TEST_F(OpusAudioEncoderTest, SmallPacketsWithResampling) { + encoder_ = std::make_unique<AudioEncoderOpus>(); + + // Send 10ms of 44.1kHz audio (441 samples). Opus frame is 20ms. + auto packet = CreatePacket(441, AudioPacket::SAMPLING_RATE_44100, 3000, 0, 2); + auto encoded = encoder_->Encode(std::move(packet)); + EXPECT_FALSE(encoded); // Should be buffered. + + // Send another 30ms (1323 samples). Total 40ms. + packet = CreatePacket(1323, AudioPacket::SAMPLING_RATE_44100, 3000, 441, 2); + encoded = encoder_->Encode(std::move(packet)); + EXPECT_TRUE(encoded); + // We should have at least one encoded chunk. + EXPECT_GE(encoded->data_size(), 1); +} + +TEST_F(OpusAudioEncoderTest, GetBitrate) { + encoder_ = std::make_unique<AudioEncoderOpus>(); + EXPECT_EQ(encoder_->GetBitrate(), 160 * 1024); +} + +// Makes sure that the FIFO returns spans from the new samples, without copying +// data. +TEST_F(OpusAudioEncoderTest, ResamplerFifo_TakeChunkDirect) { + auto fifo = AudioEncoderOpus::GetEmptyFifoForTesting(2048, 2); + ASSERT_TRUE(fifo); + const size_t chunk_size = fifo->GetChunkSizeForTesting(); + + std::vector<int16_t> samples(chunk_size, 42); + fifo->AddNewSamples(samples); + EXPECT_EQ(fifo->remaining_samples(), chunk_size); + + auto chunk = fifo->TakeChunk(); + // Make sure we haven't copied any data internally. + EXPECT_EQ(chunk.data(), samples.data()); + + EXPECT_EQ(chunk.size(), chunk_size); + EXPECT_EQ(chunk[0], 42); + EXPECT_EQ(fifo->remaining_samples(), 0u); + + fifo.reset(); +} + +// Make sure that `SaveNewSamples()` copies new samples to internal storage. +TEST_F(OpusAudioEncoderTest, ResamplerFifo_SaveNewSamples) { + auto fifo = AudioEncoderOpus::GetEmptyFifoForTesting(2048, 2); + const size_t chunk_size = fifo->GetChunkSizeForTesting(); + + { + std::vector<int16_t> data = {1, 2, 3, 4}; + fifo->AddNewSamples(data); + EXPECT_EQ(fifo->remaining_samples(), 4u); + + // This copies `data` into the FIFO's internal storage. + fifo->SaveNewSamples(); + } + // `data` is now destroyed. + + EXPECT_EQ(fifo->remaining_samples(), 4u); + + // Verify the data is still there by completing a chunk and taking it. + std::vector<int16_t> part2(chunk_size - 4, 7); + fifo->AddNewSamples(part2); + auto chunk = fifo->TakeChunk(); + EXPECT_EQ(chunk.size(), chunk_size); + EXPECT_EQ(chunk[0], 1); + EXPECT_EQ(chunk[3], 4); + EXPECT_EQ(chunk[4], 7); + + fifo.reset(); +} + +// Make sure that we can receive a mix of saved and new samples. +TEST_F(OpusAudioEncoderTest, ResamplerFifo_TakeChunkCrossover) { + auto fifo = AudioEncoderOpus::GetEmptyFifoForTesting(2048, 2); + const size_t chunk_size = fifo->GetChunkSizeForTesting(); + + // Add some samples and compact. + std::vector<int16_t> samples_to_save = {1, 2, 3, 4}; + fifo->AddNewSamples(samples_to_save); + fifo->SaveNewSamples(); + + // Add more samples to complete a chunk. + std::vector<int16_t> large_chunks(chunk_size * 2, 7); + fifo->AddNewSamples(large_chunks); + + // TakeChunk should now use the crossover buffer. + auto chunk = fifo->TakeChunk(); + EXPECT_EQ(chunk.size(), chunk_size); + EXPECT_EQ(chunk[0], 1); + EXPECT_EQ(chunk[3], 4); + EXPECT_EQ(chunk[4], 7); + + // Make sure we can pull remaining samples from the remaining `large_chunks`. + auto chunk_from_new_samples = fifo->TakeChunk(); + EXPECT_EQ(chunk_from_new_samples.size(), chunk_size); + + fifo.reset(); +} + +TEST_F(OpusAudioEncoderTest, ResamplerFifo_TakeChunkFromSavedSamples) { + auto fifo = AudioEncoderOpus::GetEmptyFifoForTesting(2048, 2); + const size_t chunk_size = fifo->GetChunkSizeForTesting(); + + // Add more than a chunk and compact. + std::vector<int16_t> data(chunk_size + 10, 42); + fifo->AddNewSamples(data); + fifo->SaveNewSamples(); + + // TakeChunk should pull from the compacted buffer. + auto chunk = fifo->TakeChunk(); + EXPECT_EQ(chunk.size(), chunk_size); + EXPECT_EQ(fifo->remaining_samples(), 10u); + + fifo.reset(); +} + +// Makes sure that +TEST_F(OpusAudioEncoderTest, ResamplerFifo_SaveLargeChunks) { + const size_t kFifoSize = 2048; + auto fifo = AudioEncoderOpus::GetEmptyFifoForTesting(kFifoSize, 2); + + // Completely fill the FIFO. + std::vector<int16_t> max_capacity(kFifoSize, 42); + fifo->AddNewSamples(max_capacity); + fifo->SaveNewSamples(); + + // Add more samples that can fit + std::vector<int16_t> huge_chunk(kFifoSize * 2, 42); + fifo->AddNewSamples(huge_chunk); + + EXPECT_GT(fifo->remaining_samples(), kFifoSize); + + while (fifo->remaining_samples() > kFifoSize) { + std::ignore = fifo->TakeChunk(); + } + + // Make sure we can save the data after consuming enough of it. + fifo->SaveNewSamples(); + + fifo.reset(); +} + } // namespace remoting
diff --git a/remoting/host/BUILD.gn b/remoting/host/BUILD.gn index 99b46ae..7c11604 100644 --- a/remoting/host/BUILD.gn +++ b/remoting/host/BUILD.gn
@@ -154,6 +154,7 @@ "//base", "//components/named_mojo_ipc_server", "//mojo/public/cpp/platform", + "//remoting/base", "//remoting/host/base", ] } @@ -342,8 +343,6 @@ "backoff_timer.cc", "backoff_timer.h", "basic_desktop_environment.cc", - "branding.cc", - "branding.h", "chromoting_host.cc", "chromoting_host.h", "chromoting_host_context.cc",
diff --git a/remoting/host/base/BUILD.gn b/remoting/host/base/BUILD.gn index eb702a8..da85642c 100644 --- a/remoting/host/base/BUILD.gn +++ b/remoting/host/base/BUILD.gn
@@ -20,7 +20,6 @@ "screen_controls.h", "screen_resolution.h", "switches.h", - "username.h", ] sources = [ @@ -30,7 +29,6 @@ "process_util.cc", "screen_resolution.cc", "switches.cc", - "username.cc", ] public_deps = [ "//remoting/base:logging" ]
diff --git a/remoting/host/base/username.h b/remoting/host/base/username.h deleted file mode 100644 index 207dd45..0000000 --- a/remoting/host/base/username.h +++ /dev/null
@@ -1,18 +0,0 @@ -// 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. - -#ifndef REMOTING_HOST_BASE_USERNAME_H_ -#define REMOTING_HOST_BASE_USERNAME_H_ - -#include <string> - -namespace remoting { - -// Returns the username associated with this process, or the empty string on -// error or if not implemented. -std::string GetUsername(); - -} // namespace remoting - -#endif // REMOTING_HOST_BASE_USERNAME_H_
diff --git a/remoting/host/branding.cc b/remoting/host/branding.cc deleted file mode 100644 index 69f7b7ad..0000000 --- a/remoting/host/branding.cc +++ /dev/null
@@ -1,55 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "remoting/host/branding.h" - -#include "base/base_paths.h" -#include "base/path_service.h" -#include "build/build_config.h" - -namespace { - -// TODO(lambroslambrou): The default locations should depend on whether Chrome -// branding is enabled - this means also modifying the Python daemon script. -// The actual location of the files is ultimately determined by the service -// daemon and native messaging host - these defaults are only used in case the -// command-line switches are absent. -#if BUILDFLAG(IS_WIN) -#ifdef OFFICIAL_BUILD -const base::FilePath::CharType kConfigDir[] = - FILE_PATH_LITERAL("Google\\Chrome Remote Desktop"); -#else -const base::FilePath::CharType kConfigDir[] = FILE_PATH_LITERAL("Chromoting"); -#endif -#elif BUILDFLAG(IS_APPLE) -const base::FilePath::CharType kConfigDir[] = - FILE_PATH_LITERAL("Chrome Remote Desktop"); -#else -const base::FilePath::CharType kConfigDir[] = - FILE_PATH_LITERAL(".config/chrome-remote-desktop"); -#endif - -} // namespace - -namespace remoting { - -#if BUILDFLAG(IS_WIN) -const wchar_t kWindowsServiceName[] = L"chromoting"; -#endif - -base::FilePath GetConfigDir() { - base::FilePath app_data_dir; - -#if BUILDFLAG(IS_WIN) - base::PathService::Get(base::DIR_COMMON_APP_DATA, &app_data_dir); -#elif BUILDFLAG(IS_APPLE) - base::PathService::Get(base::DIR_APP_DATA, &app_data_dir); -#else - base::PathService::Get(base::DIR_HOME, &app_data_dir); -#endif - - return app_data_dir.Append(kConfigDir); -} - -} // namespace remoting
diff --git a/remoting/host/branding.h b/remoting/host/branding.h deleted file mode 100644 index 694592a0..0000000 --- a/remoting/host/branding.h +++ /dev/null
@@ -1,23 +0,0 @@ -// Copyright 2012 The Chromium Authors -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef REMOTING_HOST_BRANDING_H_ -#define REMOTING_HOST_BRANDING_H_ - -#include "base/files/file_path.h" -#include "build/build_config.h" - -namespace remoting { - -#if BUILDFLAG(IS_WIN) -// Windows chromoting service name. -extern const wchar_t kWindowsServiceName[]; -#endif - -// Returns the location of the host configuration directory. -base::FilePath GetConfigDir(); - -} // namespace remoting - -#endif // REMOTING_HOST_BRANDING_H_
diff --git a/remoting/host/daemon_process.cc b/remoting/host/daemon_process.cc index a3d5399..bad55b4 100644 --- a/remoting/host/daemon_process.cc +++ b/remoting/host/daemon_process.cc
@@ -19,10 +19,10 @@ #include "base/task/thread_pool/thread_pool_instance.h" #include "mojo/public/cpp/bindings/pending_associated_receiver.h" #include "remoting/base/auto_thread_task_runner.h" +#include "remoting/base/branding.h" #include "remoting/base/constants.h" #include "remoting/host/base/host_exit_codes.h" #include "remoting/host/base/screen_resolution.h" -#include "remoting/host/branding.h" #include "remoting/host/config_file_watcher.h" #include "remoting/host/desktop_session.h" #include "remoting/host/host_event_logger.h"
diff --git a/remoting/host/daemon_process_linux.cc b/remoting/host/daemon_process_linux.cc index 0f8defb..8e72590 100644 --- a/remoting/host/daemon_process_linux.cc +++ b/remoting/host/daemon_process_linux.cc
@@ -34,12 +34,13 @@ #include "mojo/public/cpp/system/message_pipe.h" #include "remoting/base/auto_thread.h" #include "remoting/base/auto_thread_task_runner.h" +#include "remoting/base/branding.h" #include "remoting/base/crash/crash_reporting_breakpad.h" #include "remoting/base/logging.h" +#include "remoting/base/username.h" #include "remoting/host/base/host_exit_codes.h" #include "remoting/host/base/screen_resolution.h" #include "remoting/host/base/switches.h" -#include "remoting/host/branding.h" #include "remoting/host/chromoting_host_services_server.h" #include "remoting/host/host_config.h" #include "remoting/host/host_main.h" @@ -282,6 +283,9 @@ auto daemon_process = std::make_unique<DaemonProcessLinux>( caller_task_runner, io_task_runner, std::move(stopped_callback)); + // TODO: crbug.com/475611769 - set ACL on the pairing registry directory for + // the network user. + daemon_process->StartDesktopSessionFactory(); // Finishes configuring the Daemon process and launches the network process.
diff --git a/remoting/host/daemon_process_win.cc b/remoting/host/daemon_process_win.cc index 9b4396d8..edab598f 100644 --- a/remoting/host/daemon_process_win.cc +++ b/remoting/host/daemon_process_win.cc
@@ -35,13 +35,13 @@ #include "mojo/public/cpp/system/message_pipe.h" #include "remoting/base/auto_thread.h" #include "remoting/base/auto_thread_task_runner.h" +#include "remoting/base/branding.h" #include "remoting/base/crash/crash_reporting_breakpad.h" #include "remoting/base/logging.h" #include "remoting/base/scoped_sc_handle_win.h" #include "remoting/host/base/host_exit_codes.h" #include "remoting/host/base/screen_resolution.h" #include "remoting/host/base/switches.h" -#include "remoting/host/branding.h" #include "remoting/host/chromoting_host_services_server.h" #include "remoting/host/crash/minidump_handler.h" #include "remoting/host/desktop_session_win.h"
diff --git a/remoting/host/ipc_constants.cc b/remoting/host/ipc_constants.cc index e20d5ad..935e03e7 100644 --- a/remoting/host/ipc_constants.cc +++ b/remoting/host/ipc_constants.cc
@@ -11,7 +11,7 @@ #include "build/build_config.h" #include "components/named_mojo_ipc_server/named_mojo_ipc_util.h" #include "mojo/public/cpp/platform/named_platform_channel.h" -#include "remoting/host/base/username.h" +#include "remoting/base/username.h" namespace remoting { @@ -139,12 +139,6 @@ return *server_name; } -base::cstring_view GetNetworkProcessUsername() { - // Should be in sync with CRD_NETWORK_USER in - // //remoting/host/installer/linux/debian/postinst - return "_crd_network"; -} - #endif // BUILDFLAG(IS_LINUX) } // namespace remoting
diff --git a/remoting/host/ipc_constants.h b/remoting/host/ipc_constants.h index b1604af7..31c3693 100644 --- a/remoting/host/ipc_constants.h +++ b/remoting/host/ipc_constants.h
@@ -46,9 +46,6 @@ // Returns the server name for the login session reporter. const mojo::NamedPlatformChannel::ServerName& GetLoginSessionReporterServerName(); - -// Returns the username that the network process is run as. -base::cstring_view GetNetworkProcessUsername(); #endif // BUILDFLAG(IS_LINUX) } // namespace remoting
diff --git a/remoting/host/linux/gnome_remote_desktop_session.cc b/remoting/host/linux/gnome_remote_desktop_session.cc index 0e37787..9f78905 100644 --- a/remoting/host/linux/gnome_remote_desktop_session.cc +++ b/remoting/host/linux/gnome_remote_desktop_session.cc
@@ -19,6 +19,7 @@ #include "base/strings/string_split.h" #include "base/task/sequenced_task_runner.h" #include "base/types/expected.h" +#include "remoting/base/branding.h" #include "remoting/base/file_path_util_linux.h" #include "remoting/base/logging.h" #include "remoting/host/base/switches.h" @@ -44,7 +45,7 @@ base::FilePath GetDisplayLayoutFilePath() { return (base::FilePath( - GetConfigDirectoryPath().Append(GetHostHash() + ".display_layout.pb"))); + GetConfigDir().Append(GetHostHash() + ".display_layout.pb"))); } std::unique_ptr<protocol::VideoLayout> CreateDefaultLayout() {
diff --git a/remoting/host/mac/host_service_main.cc b/remoting/host/mac/host_service_main.cc index 2ea07d3..b9f6ad5 100644 --- a/remoting/host/mac/host_service_main.cc +++ b/remoting/host/mac/host_service_main.cc
@@ -26,9 +26,9 @@ #include "base/threading/platform_thread.h" #include "base/time/time.h" #include "remoting/base/logging.h" +#include "remoting/base/username.h" #include "remoting/host/base/host_exit_codes.h" #include "remoting/host/base/switches.h" -#include "remoting/host/base/username.h" #include "remoting/host/mac/constants_mac.h" #include "remoting/host/version.h"
diff --git a/remoting/host/pairing_registry_delegate_linux.cc b/remoting/host/pairing_registry_delegate_linux.cc index 4e1d3c8..f11252e 100644 --- a/remoting/host/pairing_registry_delegate_linux.cc +++ b/remoting/host/pairing_registry_delegate_linux.cc
@@ -20,7 +20,7 @@ #include "base/logging.h" #include "base/strings/stringprintf.h" #include "base/values.h" -#include "remoting/host/branding.h" +#include "remoting/base/branding.h" namespace {
diff --git a/remoting/host/pam_authorization_factory_posix.cc b/remoting/host/pam_authorization_factory_posix.cc index 6f70a87..109f8c1 100644 --- a/remoting/host/pam_authorization_factory_posix.cc +++ b/remoting/host/pam_authorization_factory_posix.cc
@@ -14,7 +14,7 @@ #include "base/functional/bind.h" #include "base/functional/callback.h" #include "remoting/base/logging.h" -#include "remoting/host/base/username.h" +#include "remoting/base/username.h" #include "remoting/host/pam_utils.h" #include "remoting/protocol/channel_authenticator.h"
diff --git a/remoting/host/remoting_me2me_host.cc b/remoting/host/remoting_me2me_host.cc index 4c323cce..24eefc7 100644 --- a/remoting/host/remoting_me2me_host.cc +++ b/remoting/host/remoting_me2me_host.cc
@@ -54,6 +54,7 @@ #include "net/base/network_change_notifier.h" #include "remoting/base/authentication_method.h" #include "remoting/base/auto_thread_task_runner.h" +#include "remoting/base/branding.h" #include "remoting/base/cloud_session_authz_service_client_factory.h" #include "remoting/base/corp_session_authz_service_client_factory.h" #include "remoting/base/cpu_utils.h" @@ -69,12 +70,11 @@ #include "remoting/base/rsa_key_pair.h" #include "remoting/base/service_urls.h" #include "remoting/base/session_policies.h" +#include "remoting/base/username.h" #include "remoting/host/base/desktop_environment_options.h" #include "remoting/host/base/host_exit_codes.h" #include "remoting/host/base/switches.h" -#include "remoting/host/base/username.h" #include "remoting/host/basic_desktop_environment.h" -#include "remoting/host/branding.h" #include "remoting/host/chromoting_host.h" #include "remoting/host/chromoting_host_context.h" #include "remoting/host/cloud_heartbeat_service_client.h"
diff --git a/remoting/host/setup/daemon_controller_delegate_linux.cc b/remoting/host/setup/daemon_controller_delegate_linux.cc index 7612609..15147d5 100644 --- a/remoting/host/setup/daemon_controller_delegate_linux.cc +++ b/remoting/host/setup/daemon_controller_delegate_linux.cc
@@ -26,6 +26,7 @@ #include "base/strings/string_util.h" #include "base/values.h" #include "build/build_config.h" +#include "remoting/base/branding.h" #include "remoting/base/file_path_util_linux.h" #include "remoting/host/host_config.h" #include "remoting/host/usage_stats_consent.h" @@ -53,7 +54,7 @@ return current_process->GetSwitchValuePath(kHostConfigSwitchName); } std::string filename = GetHostHash() + ".json"; - return GetConfigDirectoryPath().Append(filename); + return GetConfigDir().Append(filename); } bool GetScriptPath(base::FilePath* result) {
diff --git a/remoting/host/setup/daemon_controller_delegate_win.cc b/remoting/host/setup/daemon_controller_delegate_win.cc index 4c3912e..10be7a2 100644 --- a/remoting/host/setup/daemon_controller_delegate_win.cc +++ b/remoting/host/setup/daemon_controller_delegate_win.cc
@@ -18,9 +18,9 @@ #include "base/memory/ptr_util.h" #include "base/values.h" #include "base/win/scoped_bstr.h" +#include "remoting/base/branding.h" #include "remoting/base/is_google_email.h" #include "remoting/base/scoped_sc_handle_win.h" -#include "remoting/host/branding.h" #include "remoting/host/host_config.h" #include "remoting/host/usage_stats_consent.h" #include "remoting/host/win/security_descriptor.h"
diff --git a/remoting/host/usage_stats_consent_linux.cc b/remoting/host/usage_stats_consent_linux.cc index a14efd1..d335acf4 100644 --- a/remoting/host/usage_stats_consent_linux.cc +++ b/remoting/host/usage_stats_consent_linux.cc
@@ -13,6 +13,7 @@ #include "base/logging.h" #include "base/notimplemented.h" #include "base/values.h" +#include "remoting/base/branding.h" #include "remoting/base/file_path_util_linux.h" #include "remoting/base/is_google_email.h" #include "remoting/host/config_file_watcher.h" @@ -25,7 +26,7 @@ *allowed = false; std::string filename = GetHostHash() + ".json"; - base::FilePath config_path = GetConfigDirectoryPath().Append(filename); + base::FilePath config_path = GetConfigDir().Append(filename); std::optional<base::DictValue> config(HostConfigFromJsonFile(config_path)); if (!config.has_value()) { LOG(ERROR) << "No host config file found.";
diff --git a/remoting/host/win/host_service.cc b/remoting/host/win/host_service.cc index 9a58ebb..af144fdc 100644 --- a/remoting/host/win/host_service.cc +++ b/remoting/host/win/host_service.cc
@@ -27,11 +27,11 @@ #include "base/win/message_window.h" #include "base/win/scoped_com_initializer.h" #include "remoting/base/auto_thread.h" +#include "remoting/base/branding.h" #include "remoting/base/cpu_utils.h" #include "remoting/base/logging.h" #include "remoting/base/scoped_sc_handle_win.h" #include "remoting/host/base/host_exit_codes.h" -#include "remoting/host/branding.h" #include "remoting/host/daemon_process.h" #include "remoting/host/win/com_security.h" #include "remoting/host/win/core_resource.h"
diff --git a/sandbox/win/src/parallel_launch_test.cc b/sandbox/win/src/parallel_launch_test.cc index ed091be..3b39718 100644 --- a/sandbox/win/src/parallel_launch_test.cc +++ b/sandbox/win/src/parallel_launch_test.cc
@@ -86,7 +86,8 @@ static_cast<BrokerServicesBase*>(broker)->SetBrokerServicesDelegateForTesting( std::unique_ptr<BrokerServicesDelegate>(delegate)); - base::CommandLine cmd_line = sandbox::CreateCommandLineForTesting("wait"); + base::CommandLine cmd_line = + WaitCommandTestRunner::CreateCommandLineForTesting(); auto policy = broker->CreatePolicy(); EXPECT_EQ(SBOX_ALL_OK, policy->GetConfig()->SetTokenLevel(USER_INTERACTIVE, @@ -159,7 +160,8 @@ static_cast<BrokerServicesBase*>(broker)->SetBrokerServicesDelegateForTesting( std::unique_ptr<BrokerServicesDelegate>(delegate)); - base::CommandLine cmd_line = sandbox::CreateCommandLineForTesting("wait"); + base::CommandLine cmd_line = + WaitCommandTestRunner::CreateCommandLineForTesting(); base::RunLoop run_loop; int launches_remaining_count = 2;
diff --git a/sandbox/win/src/policy_target_test.cc b/sandbox/win/src/policy_target_test.cc index d2bfd5c7..e3e2312 100644 --- a/sandbox/win/src/policy_target_test.cc +++ b/sandbox/win/src/policy_target_test.cc
@@ -6,34 +6,87 @@ #include <string_view> -#include "base/compiler_specific.h" #include "base/environment.h" #include "base/memory/read_only_shared_memory_region.h" #include "base/memory/writable_shared_memory_region.h" #include "base/scoped_environment_variable_override.h" #include "base/strings/string_number_conversions.h" -#include "base/strings/string_util.h" -#include "base/test/task_environment.h" -#include "base/test/test_future.h" -#include "base/win/scoped_process_information.h" +#include "base/win/scoped_handle.h" +#include "base/win/security_descriptor.h" #include "base/win/windows_handle_util.h" #include "sandbox/win/src/broker_services.h" #include "sandbox/win/src/sandbox.h" #include "sandbox/win/src/sandbox_factory.h" #include "sandbox/win/src/sandbox_policy.h" #include "sandbox/win/src/target_services.h" +#include "sandbox/win/src/window.h" #include "sandbox/win/tests/common/controller.h" #include "testing/gtest/include/gtest/gtest.h" namespace sandbox { +namespace { + +SBOX_TEST_COMMAND(SharedMemoryCommand) { + if (args.size() < 2) { + return SBOX_TEST_FIRST_ERROR; + } + size_t raw_handle; + if (!base::StringToSizeT(args[0], &raw_handle)) { + return SBOX_TEST_SECOND_ERROR; + } + // First extract the handle to the platform-native ScopedHandle. + base::win::ScopedHandle scoped_handle(reinterpret_cast<HANDLE>(raw_handle)); + if (!scoped_handle.is_valid()) { + return SBOX_TEST_THIRD_ERROR; + } + + auto test_contents = base::as_byte_span(args[1]); + // Then convert to the low-level chromium region. + base::subtle::PlatformSharedMemoryRegion platform_region = + base::subtle::PlatformSharedMemoryRegion::Take( + std::move(scoped_handle), + base::subtle::PlatformSharedMemoryRegion::Mode::kReadOnly, + test_contents.size(), base::UnguessableToken::Create()); + // Finally wrap the low-level region in the shared memory API. + base::ReadOnlySharedMemoryRegion region = + base::ReadOnlySharedMemoryRegion::Deserialize(std::move(platform_region)); + if (!region.IsValid()) { + return SBOX_TEST_FOURTH_ERROR; + } + base::ReadOnlySharedMemoryMapping view = region.Map(); + if (!view.IsValid()) { + return SBOX_TEST_FIFTH_ERROR; + } + auto contents = base::span(view); + if (contents != test_contents) { + return SBOX_TEST_SIXTH_ERROR; + } + return SBOX_TEST_SUCCEEDED; +} + +// Tests that environment is filtered correctly. +SBOX_TEST_COMMAND(FilterEnvironmentCommand) { + auto env = base::Environment::Create(); + // "TMP" should never be filtered. See `CreateFilteredEnvironment`. + if (!env->HasVar("TMP")) { + return SBOX_TEST_FIRST_ERROR; + } + if (env->HasVar("SBOX_TEST_ENV")) { + return SBOX_TEST_SECOND_ERROR; + } + return SBOX_TEST_SUCCEEDED; +} + +} // namespace + #define BINDNTDLL(name) \ name##Function name = reinterpret_cast<name##Function>( \ ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), #name)) // Reverts to self and verify that SetInformationToken was faked. Returns // SBOX_TEST_SUCCEEDED if faked and SBOX_TEST_FAILED if not faked. -SBOX_TESTS_COMMAND int PolicyTargetTest_token(int argc, wchar_t** argv) { +SBOX_TEST_COMMAND(OpenTokenCommand) { HANDLE thread_token; // Get the thread token, using impersonation. if (!::OpenThreadToken(GetCurrentThread(), @@ -56,7 +109,7 @@ // Stores the high privilege token on a static variable, change impersonation // again to that one and verify that we are not interfering anymore with // RevertToSelf. -SBOX_TESTS_COMMAND int PolicyTargetTest_steal(int argc, wchar_t** argv) { +SBOX_TEST_COMMAND(StealTokenCommand) { static HANDLE thread_token; if (!SandboxFactory::GetTargetServices()->GetState()->RevertedToSelf()) { if (!::OpenThreadToken(GetCurrentThread(), @@ -68,7 +121,7 @@ return ::GetLastError(); // See if we fake the call again. - int ret = PolicyTargetTest_token(argc, argv); + int ret = OpenTokenCommandImpl(args); ::CloseHandle(thread_token); return ret; } @@ -76,344 +129,253 @@ } // Opens the thread token with and without impersonation. -SBOX_TESTS_COMMAND int PolicyTargetTest_token2(int argc, wchar_t** argv) { +SBOX_TEST_COMMAND(OpenToken2Command) { HANDLE thread_token; // Get the thread token, using impersonation. if (!::OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE | TOKEN_DUPLICATE, false, - &thread_token)) + &thread_token)) { return ::GetLastError(); + } ::CloseHandle(thread_token); // Get the thread token, without impersonation. if (!OpenThreadToken(GetCurrentThread(), TOKEN_IMPERSONATE | TOKEN_DUPLICATE, - true, &thread_token)) + true, &thread_token)) { return ::GetLastError(); + } ::CloseHandle(thread_token); return SBOX_TEST_SUCCEEDED; } // Opens the thread token with and without impersonation, using // NtOpenThreadTokenEX. -SBOX_TESTS_COMMAND int PolicyTargetTest_token3(int argc, wchar_t** argv) { +SBOX_TEST_COMMAND(OpenToken3Command) { BINDNTDLL(NtOpenThreadTokenEx); - if (!NtOpenThreadTokenEx) + if (!NtOpenThreadTokenEx) { return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; - + } HANDLE thread_token; // Get the thread token, using impersonation. NTSTATUS status = NtOpenThreadTokenEx(GetCurrentThread(), TOKEN_IMPERSONATE | TOKEN_DUPLICATE, false, 0, &thread_token); - if (status == STATUS_NO_TOKEN) + if (status == STATUS_NO_TOKEN) { return ERROR_NO_TOKEN; - if (!NT_SUCCESS(status)) + } + if (!NT_SUCCESS(status)) { return SBOX_TEST_FAILED; - + } ::CloseHandle(thread_token); // Get the thread token, without impersonation. status = NtOpenThreadTokenEx(GetCurrentThread(), TOKEN_IMPERSONATE | TOKEN_DUPLICATE, true, 0, &thread_token); - if (!NT_SUCCESS(status)) + if (!NT_SUCCESS(status)) { return SBOX_TEST_FAILED; - + } ::CloseHandle(thread_token); return SBOX_TEST_SUCCEEDED; } // Tests that we can open the current thread. -SBOX_TESTS_COMMAND int PolicyTargetTest_thread(int argc, wchar_t** argv) { +SBOX_TEST_COMMAND(OpenThreadCommand) { DWORD thread_id = ::GetCurrentThreadId(); - HANDLE thread = ::OpenThread(SYNCHRONIZE, false, thread_id); - if (!thread) - return ::GetLastError(); - if (!::CloseHandle(thread)) - return ::GetLastError(); - - return SBOX_TEST_SUCCEEDED; + base::win::ScopedHandle thread(::OpenThread(SYNCHRONIZE, false, thread_id)); + return thread.is_valid() ? SBOX_TEST_SUCCEEDED : ::GetLastError(); } // New thread entry point: do nothing. DWORD WINAPI PolicyTargetTest_thread_main(void* param) { - ::Sleep(INFINITE); return 0; } // Tests that we can create a new thread, and open it. -SBOX_TESTS_COMMAND int PolicyTargetTest_thread2(int argc, wchar_t** argv) { +SBOX_TEST_COMMAND(CreateThreadCommand) { // Use default values to create a new thread. DWORD thread_id; - HANDLE thread = ::CreateThread(nullptr, 0, &PolicyTargetTest_thread_main, 0, - 0, &thread_id); - if (!thread) + base::win::ScopedHandle thread(::CreateThread( + nullptr, 0, &PolicyTargetTest_thread_main, 0, 0, &thread_id)); + if (!thread.is_valid()) { return ::GetLastError(); - if (!::CloseHandle(thread)) - return ::GetLastError(); + } - thread = ::OpenThread(SYNCHRONIZE, false, thread_id); - if (!thread) - return ::GetLastError(); - - if (!::CloseHandle(thread)) - return ::GetLastError(); - - return SBOX_TEST_SUCCEEDED; + base::win::ScopedHandle thread2(::OpenThread(SYNCHRONIZE, false, thread_id)); + return thread2.is_valid() ? SBOX_TEST_SUCCEEDED : ::GetLastError(); } // Tests that we can call CreateProcess. -SBOX_TESTS_COMMAND int PolicyTargetTest_process(int argc, wchar_t** argv) { +SBOX_TEST_COMMAND(CreateProcessCommand) { // Use default values to create a new process. - STARTUPINFO startup_info = {0}; - startup_info.cb = sizeof(startup_info); - PROCESS_INFORMATION temp_process_info = {}; + STARTUPINFO startup_info = {}; + PROCESS_INFORMATION proc_info = {}; // Note: CreateProcessW() can write to its lpCommandLine, don't pass a // raw string literal. - std::wstring writable_cmdline_str(L"foo.exe"); - if (!::CreateProcessW(L"foo.exe", &writable_cmdline_str[0], nullptr, nullptr, + std::wstring cmd_line(L"foo.exe"); + if (!::CreateProcessW(L"foo.exe", std::data(cmd_line), nullptr, nullptr, false, 0, nullptr, nullptr, &startup_info, - &temp_process_info)) + &proc_info)) { return SBOX_TEST_SUCCEEDED; - base::win::ScopedProcessInformation process_info(temp_process_info); + } + ::CloseHandle(proc_info.hProcess); + ::CloseHandle(proc_info.hThread); return SBOX_TEST_FAILED; } -// Tests that environment is filtered correctly. -SBOX_TESTS_COMMAND int PolicyTargetTest_filterEnvironment(int argc, - wchar_t** argv) { - auto env = base::Environment::Create(); - // "TMP" should never be filtered. See `CreateFilteredEnvironment`. - if (!env->HasVar("TMP")) { +SBOX_TEST_COMMAND(CheckDesktopNameCommand) { + if (args.empty()) { return SBOX_TEST_FIRST_ERROR; } - if (env->HasVar("SBOX_TEST_ENV")) { + + HDESK desktop = ::GetThreadDesktop(::GetCurrentThreadId()); + if (!desktop) { return SBOX_TEST_SECOND_ERROR; } + + HWINSTA winsta = + args[0].contains(L'\\') ? ::GetProcessWindowStation() : nullptr; + + if (GetFullDesktopName(winsta, desktop) != args[0]) { + return SBOX_TEST_THIRD_ERROR; + } return SBOX_TEST_SUCCEEDED; } TEST(PolicyTargetTest, SetInformationThread) { - TestRunner runner; + OpenTokenCommandTestRunner runner; runner.SetTestState(BEFORE_REVERT); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_token")); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest()); - TestRunner runner1; + OpenTokenCommandTestRunner runner1; runner1.SetTestState(AFTER_REVERT); - EXPECT_EQ(ERROR_NO_TOKEN, runner1.RunTest(L"PolicyTargetTest_token")); + EXPECT_EQ(ERROR_NO_TOKEN, runner1.RunTest()); - TestRunner runner2; + StealTokenCommandTestRunner runner2; runner2.SetTestState(EVERY_STATE); - EXPECT_EQ(SBOX_TEST_FAILED, runner2.RunTest(L"PolicyTargetTest_steal")); + EXPECT_EQ(SBOX_TEST_FAILED, runner2.RunTest()); } TEST(PolicyTargetTest, OpenThreadToken) { - TestRunner runner; + OpenToken2CommandTestRunner runner; runner.SetTestState(BEFORE_REVERT); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_token2")); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest()); - TestRunner runner2; + OpenToken2CommandTestRunner runner2; runner2.SetTestState(AFTER_REVERT); - EXPECT_EQ(ERROR_NO_TOKEN, runner2.RunTest(L"PolicyTargetTest_token2")); + EXPECT_EQ(ERROR_NO_TOKEN, runner2.RunTest()); } TEST(PolicyTargetTest, OpenThreadTokenEx) { - TestRunner runner; + OpenToken3CommandTestRunner runner; runner.SetTestState(BEFORE_REVERT); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_token3")); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest()); - TestRunner runner2; + OpenToken3CommandTestRunner runner2; runner2.SetTestState(AFTER_REVERT); - EXPECT_EQ(ERROR_NO_TOKEN, runner2.RunTest(L"PolicyTargetTest_token3")); + EXPECT_EQ(ERROR_NO_TOKEN, runner2.RunTest()); } TEST(PolicyTargetTest, OpenThread) { - TestRunner runner; - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_thread")) + OpenThreadCommandTestRunner runner; + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest()) << "Opens the current thread"; - TestRunner runner2; - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner2.RunTest(L"PolicyTargetTest_thread2")) + CreateThreadCommandTestRunner runner2; + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner2.RunTest()) << "Creates a new thread and opens it"; } TEST(PolicyTargetTest, OpenProcess) { - TestRunner runner; - EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest(L"PolicyTargetTest_process")) - << "Opens a process"; + CreateProcessCommandTestRunner runner; + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest()) << "Opens a process"; } // Sets the desktop for the current thread to be one with a null DACL, then // launches a sandboxed app. Validates that the sandboxed app has access to the // desktop. TEST(PolicyTargetTest, InheritedDesktopPolicy) { - base::test::TaskEnvironment task_environment; // Create a desktop with a null dacl - which should allow access to // everything. SECURITY_ATTRIBUTES attributes = {}; attributes.nLength = sizeof(SECURITY_ATTRIBUTES); - SECURITY_DESCRIPTOR security_desc = {}; - ::InitializeSecurityDescriptor(&security_desc, SECURITY_DESCRIPTOR_REVISION); - ::SetSecurityDescriptorDacl(&security_desc, true, nullptr, false); + base::win::SecurityDescriptor sd; + sd.set_dacl(*base::win::AccessControlList::FromPACL(nullptr)); + SECURITY_DESCRIPTOR security_desc = sd.ToAbsolute(); attributes.lpSecurityDescriptor = &security_desc; - HDESK null_dacl_desktop_handle = CreateDesktop( + HDESK null_dacl_desktop_handle = ::CreateDesktop( L"null_dacl_desktop", nullptr, nullptr, 0, GENERIC_ALL, &attributes); EXPECT_TRUE(null_dacl_desktop_handle); + BrokerServices* broker = GetBroker(); + ASSERT_TRUE(broker); // Switch to the null dacl desktop and run the test. HDESK old_desktop = ::GetThreadDesktop(::GetCurrentThreadId()); EXPECT_TRUE(null_dacl_desktop_handle); EXPECT_TRUE(::SetThreadDesktop(null_dacl_desktop_handle)); - BrokerServices* broker = GetBroker(); - // Precreate the desktop. EXPECT_EQ(SBOX_ALL_OK, broker->CreateAlternateDesktop(Desktop::kAlternateDesktop)); - - ASSERT_TRUE(broker); - - base::CommandLine cmd_line = sandbox::CreateCommandLineForTesting("wait"); - - // Launch the app. - ResultCode result = SBOX_ALL_OK; - DWORD last_error = ERROR_SUCCESS; - base::win::ScopedProcessInformation target; - - auto policy = broker->CreatePolicy(); - policy->GetConfig()->SetDesktop(Desktop::kAlternateDesktop); - EXPECT_EQ(SBOX_ALL_OK, policy->GetConfig()->SetTokenLevel(USER_INTERACTIVE, - USER_LOCKDOWN)); - base::test::TestFuture<base::win::ScopedProcessInformation, DWORD, ResultCode> - test_future; - broker->SpawnTargetAsync(cmd_line, std::move(policy), - test_future.GetCallback()); - std::tie(target, last_error, result) = test_future.Take(); - EXPECT_EQ(SBOX_ALL_OK, result); - - // Run the process for some time to make sure it doesn't crash on launch - EXPECT_EQ(1u, ::ResumeThread(target.thread_handle())); - EXPECT_EQ(static_cast<DWORD>(WAIT_TIMEOUT), - ::WaitForSingleObject(target.process_handle(), 2000)); - - EXPECT_TRUE(::TerminateProcess(target.process_handle(), 0)); - ::WaitForSingleObject(target.process_handle(), INFINITE); - - // Close the desktop handle. - broker->DestroyDesktops(); - // Close the null dacl desktop. EXPECT_TRUE(::SetThreadDesktop(old_desktop)); EXPECT_TRUE(::CloseDesktop(null_dacl_desktop_handle)); + + CheckDesktopNameCommandTestRunner runner; + runner.SetTestState(BEFORE_INIT); + runner.GetConfig()->SetDesktop(Desktop::kAlternateDesktop); + std::wstring desktop_name = + broker->GetDesktopName(Desktop::kAlternateDesktop); + EXPECT_EQ(runner.RunTest(desktop_name), SBOX_TEST_SUCCEEDED); + + // Close the desktop handle. + broker->DestroyDesktops(); } -// Launches the app in the sandbox and ask it to wait in an -// infinite loop. Waits for 2 seconds and then check if the -// desktop associated with the app thread is not the same as the -// current desktop. +// Launches the app in the sandbox and check it was assigned the alternative +// desktop on the current window station. TEST(PolicyTargetTest, DesktopPolicy) { - base::test::TaskEnvironment task_environment; - BrokerServices* broker = GetBroker(); + CheckDesktopNameCommandTestRunner runner; + runner.SetTestState(BEFORE_INIT); + BrokerServices* broker = runner.broker(); + ASSERT_TRUE(broker); // Precreate the desktop. EXPECT_EQ(SBOX_ALL_OK, broker->CreateAlternateDesktop(Desktop::kAlternateDesktop)); - ASSERT_TRUE(broker); - - base::CommandLine cmd_line = sandbox::CreateCommandLineForTesting("wait"); - - // Launch the app. - ResultCode result = SBOX_ALL_OK; - DWORD last_error = ERROR_SUCCESS; - base::win::ScopedProcessInformation target; - - auto policy = broker->CreatePolicy(); - policy->GetConfig()->SetDesktop(Desktop::kAlternateDesktop); - EXPECT_EQ(SBOX_ALL_OK, policy->GetConfig()->SetTokenLevel(USER_INTERACTIVE, - USER_LOCKDOWN)); + runner.GetConfig()->SetDesktop(Desktop::kAlternateDesktop); // Keep the desktop name to test against later (note - it was precreated). std::wstring desktop_name = broker->GetDesktopName(Desktop::kAlternateDesktop); - base::test::TestFuture<base::win::ScopedProcessInformation, DWORD, ResultCode> - test_future; - broker->SpawnTargetAsync(cmd_line, std::move(policy), - test_future.GetCallback()); - std::tie(target, last_error, result) = test_future.Take(); - - EXPECT_EQ(SBOX_ALL_OK, result); - - EXPECT_EQ(1u, ::ResumeThread(target.thread_handle())); - - EXPECT_EQ(static_cast<DWORD>(WAIT_TIMEOUT), - ::WaitForSingleObject(target.process_handle(), 2000)); - - EXPECT_NE(::GetThreadDesktop(target.thread_id()), - ::GetThreadDesktop(::GetCurrentThreadId())); - - HDESK desk = ::OpenDesktop(desktop_name.c_str(), 0, false, DESKTOP_ENUMERATE); - EXPECT_TRUE(desk); - EXPECT_TRUE(::CloseDesktop(desk)); - EXPECT_TRUE(::TerminateProcess(target.process_handle(), 0)); - - ::WaitForSingleObject(target.process_handle(), INFINITE); - + EXPECT_EQ(runner.RunTest(desktop_name), SBOX_TEST_SUCCEEDED); // Close the desktop handle. broker->DestroyDesktops(); - // Make sure the desktop does not exist anymore. - desk = ::OpenDesktop(desktop_name.c_str(), 0, false, DESKTOP_ENUMERATE); + HDESK desk = ::OpenDesktop(desktop_name.c_str(), 0, false, DESKTOP_ENUMERATE); EXPECT_FALSE(desk); } -// Launches the app in the sandbox and ask it to wait in an -// infinite loop. Waits for 2 seconds and then check if the -// winstation associated with the app thread is not the same as the -// current desktop. +// Launches the app in the sandbox and check it was assigned the alternative +// desktop and window station. TEST(PolicyTargetTest, WinstaPolicy) { - base::test::TaskEnvironment task_environment; + CheckDesktopNameCommandTestRunner runner; + runner.SetTestState(BEFORE_INIT); BrokerServices* broker = GetBroker(); + ASSERT_TRUE(broker); // Precreate the desktop. EXPECT_EQ(SBOX_ALL_OK, broker->CreateAlternateDesktop(Desktop::kAlternateWinstation)); - ASSERT_TRUE(broker); - - base::CommandLine cmd_line = sandbox::CreateCommandLineForTesting("wait"); - - // Launch the app. - ResultCode result = SBOX_ALL_OK; - base::win::ScopedProcessInformation target; - - auto policy = broker->CreatePolicy(); - policy->GetConfig()->SetDesktop(Desktop::kAlternateWinstation); - EXPECT_EQ(SBOX_ALL_OK, policy->GetConfig()->SetTokenLevel(USER_INTERACTIVE, - USER_LOCKDOWN)); - DWORD last_error = ERROR_SUCCESS; + runner.GetConfig()->SetDesktop(Desktop::kAlternateWinstation); // Keep the desktop name for later (note - it was precreated). std::wstring desktop_name = broker->GetDesktopName(Desktop::kAlternateWinstation); - base::test::TestFuture<base::win::ScopedProcessInformation, DWORD, ResultCode> - test_future; - broker->SpawnTargetAsync(cmd_line, std::move(policy), - test_future.GetCallback()); - std::tie(target, last_error, result) = test_future.Take(); - - EXPECT_EQ(SBOX_ALL_OK, result); - - EXPECT_EQ(1u, ::ResumeThread(target.thread_handle())); - - EXPECT_EQ(static_cast<DWORD>(WAIT_TIMEOUT), - ::WaitForSingleObject(target.process_handle(), 2000)); - - EXPECT_NE(::GetThreadDesktop(target.thread_id()), - ::GetThreadDesktop(::GetCurrentThreadId())); - - ASSERT_FALSE(desktop_name.empty()); // Make sure there is a backslash, for the window station name. - EXPECT_NE(desktop_name.find_first_of(L'\\'), std::wstring::npos); + ASSERT_TRUE(desktop_name.contains(L'\\')); + EXPECT_EQ(runner.RunTest(desktop_name), SBOX_TEST_SUCCEEDED); // Isolate the desktop name. desktop_name = desktop_name.substr(desktop_name.find_first_of(L'\\') + 1); @@ -421,10 +383,6 @@ HDESK desk = ::OpenDesktop(desktop_name.c_str(), 0, false, DESKTOP_ENUMERATE); // This should fail if the desktop is really on another window station. EXPECT_FALSE(desk); - EXPECT_TRUE(::TerminateProcess(target.process_handle(), 0)); - - ::WaitForSingleObject(target.process_handle(), INFINITE); - // Close the desktop handle. broker->DestroyDesktops(); } @@ -466,60 +424,28 @@ // Launches the app in the sandbox and share a handle with it. The app should // be able to use the handle. TEST(PolicyTargetTest, ShareHandleTest) { - base::test::TaskEnvironment task_environment; - BrokerServices* broker = GetBroker(); - ASSERT_TRUE(broker); - - std::string_view contents = "Hello World"; + std::wstring_view contents = L"Hello World"; + auto contents_span = base::as_byte_span(contents); base::WritableSharedMemoryRegion writable_region = - base::WritableSharedMemoryRegion::Create(contents.size()); + base::WritableSharedMemoryRegion::Create(contents_span.size()); ASSERT_TRUE(writable_region.IsValid()); base::WritableSharedMemoryMapping writable_mapping = writable_region.Map(); ASSERT_TRUE(writable_mapping.IsValid()); - UNSAFE_TODO( - memcpy(writable_mapping.memory(), contents.data(), contents.size())); - - // Get the path to the sandboxed app. - wchar_t prog_name[MAX_PATH]; - GetModuleFileNameW(nullptr, prog_name, MAX_PATH); + std::ranges::copy(contents_span, base::span(writable_mapping).begin()); base::ReadOnlySharedMemoryRegion read_only_region = base::WritableSharedMemoryRegion::ConvertToReadOnly( std::move(writable_region)); ASSERT_TRUE(read_only_region.IsValid()); - auto policy = broker->CreatePolicy(); - policy->AddHandleToShare(read_only_region.GetPlatformHandle()); - - base::CommandLine cmd_line = - sandbox::CreateCommandLineForTesting("shared_memory_handle"); - auto handle_str = base::NumberToString( - base::win::HandleToUint32(read_only_region.GetPlatformHandle())); - cmd_line.AppendArg(handle_str); - - // Launch the app. - ResultCode result = SBOX_ALL_OK; - base::win::ScopedProcessInformation target; - - EXPECT_EQ(SBOX_ALL_OK, policy->GetConfig()->SetTokenLevel(USER_INTERACTIVE, - USER_LOCKDOWN)); - DWORD last_error = ERROR_SUCCESS; - base::test::TestFuture<base::win::ScopedProcessInformation, DWORD, ResultCode> - test_future; - broker->SpawnTargetAsync(cmd_line, std::move(policy), - test_future.GetCallback()); - std::tie(target, last_error, result) = test_future.Take(); - - EXPECT_EQ(SBOX_ALL_OK, result); - - EXPECT_EQ(1u, ::ResumeThread(target.thread_handle())); - - EXPECT_EQ(static_cast<DWORD>(WAIT_TIMEOUT), - ::WaitForSingleObject(target.process_handle(), 2000)); - - EXPECT_TRUE(::TerminateProcess(target.process_handle(), 0)); - - ::WaitForSingleObject(target.process_handle(), INFINITE); + SharedMemoryCommandTestRunner runner(JobLevel::kLockdown, USER_INTERACTIVE, + USER_LOCKDOWN); + runner.SetTestState(BEFORE_INIT); + runner.GetPolicy()->AddHandleToShare(read_only_region.GetPlatformHandle()); + EXPECT_EQ(runner.RunTest( + base::win::HandleToUint32(read_only_region.GetPlatformHandle()), + contents), + SBOX_TEST_SUCCEEDED); } // Test if shared policies can be created by the broker. @@ -568,23 +494,20 @@ TEST(PolicyTargetTest, FilterEnvironment) { base::ScopedEnvironmentVariableOverride scoped_env("SBOX_TEST_ENV", "FOO"); { - TestRunner runner; - runner.GetPolicy()->GetConfig()->SetFilterEnvironment(/*filter=*/true); - EXPECT_EQ(SBOX_TEST_SUCCEEDED, - runner.RunTest(L"PolicyTargetTest_filterEnvironment")); + FilterEnvironmentCommandTestRunner runner; + runner.GetConfig()->SetFilterEnvironment(/*filter=*/true); + EXPECT_EQ(SBOX_TEST_SUCCEEDED, runner.RunTest()); } { - TestRunner runner; - runner.GetPolicy()->GetConfig()->SetFilterEnvironment(/*filter=*/false); - EXPECT_EQ(SBOX_TEST_SECOND_ERROR, - runner.RunTest(L"PolicyTargetTest_filterEnvironment")); + FilterEnvironmentCommandTestRunner runner; + runner.GetConfig()->SetFilterEnvironment(/*filter=*/false); + EXPECT_EQ(SBOX_TEST_SECOND_ERROR, runner.RunTest()); } } TEST(PolicyTargetDeathTest, SharePseudoHandle) { - TestRunner runner; - auto* policy = runner.GetPolicy(); - EXPECT_DEATH(policy->AddHandleToShare(::GetCurrentThread()), ""); + SharedMemoryCommandTestRunner runner; + EXPECT_DEATH(runner.GetPolicy()->AddHandleToShare(::GetCurrentThread()), ""); } } // namespace sandbox
diff --git a/sandbox/win/tests/common/controller.cc b/sandbox/win/tests/common/controller.cc index f40e337..8f33947 100644 --- a/sandbox/win/tests/common/controller.cc +++ b/sandbox/win/tests/common/controller.cc
@@ -4,14 +4,16 @@ #include "sandbox/win/tests/common/controller.h" +#include <windows.h> + #include <memory> #include <string> #include <string_view> #include "base/check.h" +#include "base/check_op.h" +#include "base/functional/bind.h" #include "base/functional/callback.h" -#include "base/memory/platform_shared_memory_region.h" -#include "base/memory/read_only_shared_memory_region.h" #include "base/no_destructor.h" #include "base/notreached.h" #include "base/path_service.h" @@ -27,9 +29,6 @@ #include "base/test/test_future.h" #include "base/test/test_timeouts.h" #include "base/time/time.h" -#include "base/unguessable_token.h" -#include "base/win/scoped_handle.h" -#include "base/win/scoped_process_information.h" #include "base/win/windows_version.h" #include "sandbox/win/src/app_container.h" #include "sandbox/win/src/sandbox_factory.h" @@ -84,73 +83,11 @@ } }; -int SharedMemoryCommand(base::span<const std::wstring> args) { - if (args.empty()) { - return SBOX_TEST_INVALID_PARAMETER; - } - size_t raw_handle; - if (!base::StringToSizeT(args[0], &raw_handle)) { - return SBOX_TEST_INVALID_PARAMETER; - } - // First extract the handle to the platform-native ScopedHandle. - base::win::ScopedHandle scoped_handle(reinterpret_cast<HANDLE>(raw_handle)); - if (!scoped_handle.is_valid()) { - return SBOX_TEST_INVALID_PARAMETER; - } - - std::string_view test_contents = "Hello World"; - // Then convert to the low-level chromium region. - base::subtle::PlatformSharedMemoryRegion platform_region = - base::subtle::PlatformSharedMemoryRegion::Take( - std::move(scoped_handle), - base::subtle::PlatformSharedMemoryRegion::Mode::kReadOnly, - test_contents.size(), base::UnguessableToken::Create()); - // Finally wrap the low-level region in the shared memory API. - base::ReadOnlySharedMemoryRegion region = - base::ReadOnlySharedMemoryRegion::Deserialize(std::move(platform_region)); - if (!region.IsValid()) { - return SBOX_TEST_INVALID_PARAMETER; - } - base::ReadOnlySharedMemoryMapping view = region.Map(); - if (!view.IsValid()) { - return SBOX_TEST_INVALID_PARAMETER; - } - const std::string contents(view.GetMemoryAsSpan<char>().data()); - if (contents != test_contents) { - return SBOX_TEST_INVALID_PARAMETER; - } - Sleep(INFINITE); - return SBOX_TEST_TIMED_OUT; -} - constexpr char kChildSwitch[] = "child"; constexpr char kNoSandboxSwitch[] = "no-sandbox"; constexpr char kStateSwitch[] = "state"; constexpr char kCommandSwitch[] = "cmd"; - -base::CommandLine CreateCommandLine(std::string_view command, - base::span<const std::string> args, - SboxTestsState state, - bool no_sandbox) { - // Get the path to the sandboxed process. - base::FilePath prog_name; - CHECK(base::PathService::Get(base::FILE_EXE, &prog_name)); - base::CommandLine cmd_line(prog_name); - cmd_line.AppendSwitch(kChildSwitch); - if (no_sandbox) { - cmd_line.AppendSwitch(kNoSandboxSwitch); - } - DCHECK_LE(MAX_STATE, 10); - cmd_line.AppendSwitchASCII(kStateSwitch, - base::NumberToString(static_cast<int>(state))); - cmd_line.AppendSwitchUTF8(kCommandSwitch, command); - cmd_line.AppendArg("--"); - for (const auto& arg : args) { - cmd_line.AppendArg(arg); - } - - return cmd_line; -} +constexpr char kLegacySwitch[] = "legacy"; std::wstring MakePathToSysBase(std::wstring_view name, std::wstring_view sysname, @@ -175,8 +112,50 @@ return MakePathToSysBase(name, L"SysWOW64", is_obj_man_path); } +int RunLegacyCommand(CommandFunction func, + base::span<const std::wstring> args) { + std::vector<const wchar_t*> argv; + for (const auto& arg : args) { + argv.push_back(&arg[0]); + } + return func(static_cast<int>(argv.size()), std::data(argv)); +} + +base::RepeatingCallback<int(base::span<const std::wstring>)> BindCommand( + const std::string& command_name, + bool legacy_command) { + HMODULE module; + if (!::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | + GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + reinterpret_cast<wchar_t*>(&DispatchCall), + &module)) { + return {}; + } + + FARPROC func = ::GetProcAddress(module, command_name.c_str()); + if (!func) { + return {}; + } + + if (legacy_command) { + return base::BindRepeating(RunLegacyCommand, + reinterpret_cast<CommandFunction>(func)); + } else { + return base::BindRepeating(reinterpret_cast<CommandFunctionArgs>(func)); + } +} + } // namespace +namespace internal { + +template <> +std::string ToString(const std::wstring& value) { + return base::WideToUTF8(value); +} + +} // namespace internal + std::wstring MakePathToSys(std::wstring_view name, bool is_obj_man_path) { return (base::win::OSInfo::GetInstance()->IsWowX86OnAMD64()) ? MakePathToSysWow64(name, is_obj_man_path) @@ -220,56 +199,66 @@ return instance; } -TestRunner::TestRunner(JobLevel job_level, - TokenLevel startup_token, - TokenLevel main_token) { - broker_ = nullptr; - timeout_ = TestTimeouts::test_launcher_timeout(); +// static +base::CommandLine TestRunnerBase::CreateCommandLine( + std::string_view command, + base::span<const std::string> args, + SboxTestsState state, + bool no_sandbox, + bool legacy_command) { + // Get the path to the sandboxed process. + base::FilePath prog_name; + CHECK(base::PathService::Get(base::FILE_EXE, &prog_name)); + base::CommandLine cmd_line(prog_name); + cmd_line.AppendSwitch(kChildSwitch); + if (no_sandbox) { + cmd_line.AppendSwitch(kNoSandboxSwitch); + } + if (legacy_command) { + cmd_line.AppendSwitch(kLegacySwitch); + } + DCHECK_LE(MAX_STATE, 10); + cmd_line.AppendSwitchASCII(kStateSwitch, + base::NumberToString(static_cast<int>(state))); + cmd_line.AppendSwitchUTF8(kCommandSwitch, command); + cmd_line.AppendArg("--"); + for (const auto& arg : args) { + cmd_line.AppendArg(arg); + } - broker_ = GetBroker(); - if (!broker_) { - return; - } - policy_ = broker_->CreatePolicy(); - if (!policy_) { - return; - } - auto result = policy_->GetConfig()->SetJobLevel(job_level, 0); - if (result != SBOX_ALL_OK) { - return; - } - result = policy_->GetConfig()->SetTokenLevel(startup_token, main_token); - if (result != SBOX_ALL_OK) { - return; - } - is_init_ = true; + return cmd_line; } -TestRunner::TestRunner() - : TestRunner(JobLevel::kLockdown, - USER_RESTRICTED_SAME_ACCESS, - USER_LOCKDOWN) {} +TestRunnerBase::TestRunnerBase(JobLevel job_level, + TokenLevel startup_token, + TokenLevel main_token) { + timeout_ = TestTimeouts::test_launcher_timeout(); + broker_ = GetBroker(); + CHECK(broker_); + policy_ = broker_->CreatePolicy(); + CHECK(policy_); + CHECK_EQ(SBOX_ALL_OK, policy_->GetConfig()->SetJobLevel(job_level, 0)); + CHECK_EQ(SBOX_ALL_OK, + policy_->GetConfig()->SetTokenLevel(startup_token, main_token)); +} -TargetPolicy* TestRunner::GetPolicy() { +TestRunnerBase::~TestRunnerBase() = default; + +TargetPolicy* TestRunnerBase::GetPolicy() { return policy_.get(); } -TestRunner::~TestRunner() { - if (target_process_.IsValid() && kill_on_destruction_) { - target_process_.Terminate(0, /*wait=*/false); - } +TargetConfig* TestRunnerBase::GetConfig() { + return GetPolicy()->GetConfig(); } -bool TestRunner::WaitForAllTargets() { +bool TestRunnerBase::WaitForAllTargets() { TargetTracker::WaitForAllTargets(); return true; } -bool TestRunner::AllowFileAccess(FileSemantics semantics, - std::wstring_view pattern) { - if (!is_init_) { - return false; - } +bool TestRunnerBase::AllowFileAccess(FileSemantics semantics, + std::wstring_view pattern) { if (policy_->GetConfig()->IsConfigured()) { return false; } @@ -277,11 +266,8 @@ policy_->GetConfig()->AllowFileAccess(semantics, pattern)); } -bool TestRunner::AddRuleSys32(FileSemantics semantics, - std::wstring_view pattern) { - if (!is_init_) { - return false; - } +bool TestRunnerBase::AddRuleSys32(FileSemantics semantics, + std::wstring_view pattern) { std::wstring win32_path = MakePathToSys32(pattern, false); if (win32_path.empty()) { return false; @@ -300,40 +286,10 @@ return AllowFileAccess(semantics, win32_path.c_str()); } -// TODO(forshaw): This is to support old code which passes and entire command -// line. Remove once the new API is implemented and all the old tests have been -// updated. -int TestRunner::RunTest(std::wstring_view command) { - // Note: To use the `CommandLine` class we add a fake program and the switch - // terminator so that it doesn't sort switch arguments which can change the - // ordering. - std::wstring dummy_cmd_line = L"dummy -- "; - dummy_cmd_line += command; - auto cmd_line = base::CommandLine::FromString(dummy_cmd_line); - auto cmd_args = cmd_line.GetArgs(); - if (cmd_args.empty()) { - return SBOX_TEST_FAILED_TO_RUN_TEST; - } - std::vector<std::string> args; - for (size_t i = 1; i < cmd_args.size(); ++i) { - args.emplace_back(base::WideToUTF8(cmd_args[i])); - } - return RunTestInternal(base::WideToUTF8(cmd_args[0]), args); -} - -int TestRunner::RunTestInternal(std::string_view command, - base::span<const std::string> args) { - if (!is_init_) { - return SBOX_TEST_FAILED_TO_RUN_TEST; - } - // For simplicity TestRunner supports only one process per instance. - if (target_process_.IsValid()) { - if (target_process_.IsRunning()) { - return SBOX_TEST_FAILED_TO_RUN_TEST; - } - target_process_.Close(); - } - +base::Process TestRunnerBase::CreateTestProcess( + std::string_view command, + base::span<const std::string> args, + bool legacy_command) { if (disable_csrss_) { auto* config = policy_->GetConfig(); if (config->GetAppContainer() == nullptr) { @@ -342,26 +298,14 @@ } // Launch the sandboxed process - auto cmd_line = CreateCommandLine(command, args, state_, no_sandbox_); - base::Process process = no_sandbox_ ? base::LaunchProcess(cmd_line, {}) - : LaunchSandboxProcess(cmd_line); + auto cmd_line = + CreateCommandLine(command, args, state_, no_sandbox_, legacy_command); + return no_sandbox_ ? base::LaunchProcess(cmd_line, {}) + : LaunchSandboxProcess(cmd_line); +} - if (!process.IsValid()) { - return SBOX_TEST_FAILED_TO_RUN_TEST; - } - - // For an asynchronous run we don't bother waiting. - if (is_async_) { - target_process_ = std::move(process); - return SBOX_TEST_SUCCEEDED; - } - - base::TimeDelta timeout = timeout_; - if (::IsDebuggerPresent()) { - // Don't kill the target process on a time-out while we are debugging. - timeout = base::TimeDelta::Max(); - } - +int TestRunnerBase::WaitForResult(const base::Process& process) const { + auto timeout = ::IsDebuggerPresent() ? base::TimeDelta::Max() : timeout_; int exit_code = SBOX_TEST_SUCCEEDED; if (!process.WaitForExitWithTimeout(timeout, &exit_code)) { return SBOX_TEST_TIMED_OUT; @@ -370,7 +314,7 @@ return exit_code; } -base::Process TestRunner::LaunchSandboxProcess( +base::Process TestRunnerBase::LaunchSandboxProcess( const base::CommandLine& cmd_line) { ResultCode result = SBOX_ALL_OK; DWORD last_error = ERROR_SUCCESS; @@ -399,12 +343,12 @@ return base::Process(proc_info.TakeProcessHandle()); } -void TestRunner::SetTimeout(DWORD timeout_ms) { +void TestRunnerBase::SetTimeout(DWORD timeout_ms) { SetTimeout(timeout_ms == INFINITE ? base::TimeDelta::Max() : base::Milliseconds(timeout_ms)); } -void TestRunner::SetTimeout(base::TimeDelta timeout) { +void TestRunnerBase::SetTimeout(base::TimeDelta timeout) { // We do not take -ve timeouts. DCHECK(timeout >= base::TimeDelta()); // We need millisecond DWORDS but also cannot take exactly INFINITE, @@ -413,11 +357,48 @@ timeout_ = timeout; } -DWORD TestRunner::timeout_ms() { - if (timeout_.is_inf()) { - return INFINITE; +TestRunner::~TestRunner() { + if (target_process_.IsValid() && kill_on_destruction_) { + target_process_.Terminate(0, /*wait=*/false); } - return static_cast<DWORD>(timeout_.InMilliseconds()); +} + +int TestRunner::RunTest(std::wstring_view command) { + // For simplicity TestRunner supports only one process per instance. + if (target_process_.IsValid()) { + if (target_process_.IsRunning()) { + return SBOX_TEST_FAILED_TO_RUN_TEST; + } + target_process_.Close(); + } + + // Note: To use the `CommandLine` class we add a fake program and the switch + // terminator so that it doesn't sort switch arguments which can change the + // ordering. + std::wstring dummy_cmd_line = L"dummy -- "; + dummy_cmd_line += command; + auto cmd_line = base::CommandLine::FromString(dummy_cmd_line); + auto cmd_args = cmd_line.GetArgs(); + if (cmd_args.empty()) { + return SBOX_TEST_FAILED_TO_RUN_TEST; + } + std::vector<std::string> args; + for (size_t i = 1; i < cmd_args.size(); ++i) { + args.emplace_back(base::WideToUTF8(cmd_args[i])); + } + auto process = CreateTestProcess(base::WideToUTF8(cmd_args[0]), args, + /*legacy_command=*/true); + if (!process.IsValid()) { + return SBOX_TEST_FAILED_TO_RUN_TEST; + } + + // For an asynchronous run we don't bother waiting. + if (is_async_) { + target_process_ = std::move(process); + return SBOX_TEST_SUCCEEDED; + } + + return WaitForResult(process); } bool IsChildProcessForTesting() { @@ -447,21 +428,15 @@ } auto args = cmd_line->GetArgs(); // We hard code two tests to avoid dispatch failures. - if (command_name == "wait") { + if (command_name == sandbox::WaitCommandTestRunner::type::kTestName) { Sleep(INFINITE); return SBOX_TEST_TIMED_OUT; } - if (command_name == "ping") { + if (command_name == PingCommandTestRunner::type::kTestName) { return SBOX_TEST_PING_OK; } - // If the caller shared a shared memory handle with us attempt to open it - // in read only mode and sleep infinitely if we succeed. - if (command_name == "shared_memory_handle") { - return SharedMemoryCommand(args); - } - int state_value; if (!base::StringToInt(cmd_line->GetSwitchValueASCII(kStateSwitch), &state_value)) { @@ -480,20 +455,14 @@ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; } - CommandFunction command = reinterpret_cast<CommandFunction>( - ::GetProcAddress(module, command_name.c_str())); + auto command = BindCommand(command_name, cmd_line->HasSwitch(kLegacySwitch)); if (!command) { return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; } - std::vector<wchar_t*> argv; - for (auto& arg : args) { - argv.push_back(&arg[0]); - } - int argc = static_cast<int>(argv.size()); if (BEFORE_INIT == state) { - return command(argc, std::data(argv)); + return command.Run(args); } else if (EVERY_STATE == state) { - command(argc, std::data(argv)); + command.Run(args); } TargetServices* target = SandboxFactory::GetTargetServices(); if (target) { @@ -501,9 +470,9 @@ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; } if (BEFORE_REVERT == state) { - return command(argc, std::data(argv)); + return command.Run(args); } else if (EVERY_STATE == state) { - command(argc, std::data(argv)); + command.Run(args); } #if defined(ADDRESS_SANITIZER) || CHECK_WILL_STREAM() // Bind and leak dbghelp.dll before the token is lowered, otherwise some @@ -519,11 +488,7 @@ return SBOX_TEST_FAILED_TO_EXECUTE_COMMAND; } - return command(argc, std::data(argv)); -} - -base::CommandLine CreateCommandLineForTesting(std::string_view command) { - return CreateCommandLine(command, {}, MIN_STATE, /*no_sandbox=*/false); + return command.Run(args); } } // namespace sandbox
diff --git a/sandbox/win/tests/common/controller.h b/sandbox/win/tests/common/controller.h index 380e627d..e0a12011 100644 --- a/sandbox/win/tests/common/controller.h +++ b/sandbox/win/tests/common/controller.h
@@ -5,21 +5,45 @@ #ifndef SANDBOX_WIN_TESTS_COMMON_CONTROLLER_H_ #define SANDBOX_WIN_TESTS_COMMON_CONTROLLER_H_ +#include <concepts> #include <string> #include <string_view> +#include <utility> #include "base/command_line.h" +#include "base/compiler_specific.h" #include "base/containers/span.h" #include "base/dcheck_is_on.h" #include "base/memory/raw_ptr.h" #include "base/process/process.h" +#include "base/strings/to_string.h" #include "base/time/time.h" +#include "base/win/scoped_process_information.h" #include "base/win/windows_types.h" #include "sandbox/win/src/sandbox_policy.h" #include "sandbox/win/src/win_utils.h" namespace sandbox { +namespace internal { + +// base::ToString can't handle passing std::wstring so implement our own wrapper +// to handle this special case. +template <typename T> +std::string ToString(const T& value) { + return base::ToString(value); +} + +template <> +std::string ToString(const std::wstring& value); + +template <typename T> +concept TestNameDefinition = requires { + { T::kTestName } -> std::convertible_to<std::string_view>; +}; + +} // namespace internal + // See winerror.h for details. #define SEVERITY_INFO_FLAGS 0x40000000 #define SEVERITY_ERROR_FLAGS 0xC0000000 @@ -90,19 +114,32 @@ #define SBOX_TESTS_API __declspec(dllexport) #define SBOX_TESTS_COMMAND extern "C" SBOX_TESTS_API +#define SBOX_TEST_DECLARE_COMMAND(name) \ + struct name##Def { \ + static constexpr std::string_view kTestName = #name "Impl"; \ + }; \ + using name##TestRunner = GenericTestRunner<name##Def> + +#define SBOX_TEST_DEFINE_COMMAND(name) \ + SBOX_TESTS_COMMAND int name##Impl(base::span<const std::wstring> args) + +// Declare a command runner type and its implementation. +#define SBOX_TEST_COMMAND(name) \ + SBOX_TEST_DECLARE_COMMAND(name); \ + SBOX_TEST_DEFINE_COMMAND(name) + extern "C" { -typedef int (*CommandFunction)(int argc, wchar_t **argv); +typedef int (*CommandFunction)(int argc, const wchar_t** argv); } +typedef int (*CommandFunctionArgs)(base::span<const std::wstring>); + // Class to facilitate the launch of a test inside the sandbox. -class TestRunner { +class TestRunnerBase { public: - TestRunner(JobLevel job_level, TokenLevel startup_token, - TokenLevel main_token); - - TestRunner(); - - ~TestRunner(); + TestRunnerBase(const TestRunnerBase&) = delete; + TestRunnerBase& operator=(const TestRunnerBase&) = delete; + virtual ~TestRunnerBase(); // Adds a filesystem rules with the path of a file in system32. The function // appends "pattern" to "system32" and then call AddRule. Return true if the @@ -113,17 +150,10 @@ // succeeds. bool AllowFileAccess(FileSemantics semantics, std::wstring_view pattern); - // Starts a child process in the sandbox and ask it to run `command`. - // Return a SboxTestResult. - int RunTest(std::wstring_view command); - // Sets the timeout value for the child to run the command and return. void SetTimeout(DWORD timeout_ms); void SetTimeout(base::TimeDelta timeout); - // Sets TestRunner to return without waiting for the process to exit. - void SetAsynchronous(bool is_async) { is_async_ = is_async; } - // Sets whether TestRunner sandboxes the child process. ("--no-sandbox") void SetUnsandboxed(bool is_no_sandbox) { no_sandbox_ = is_no_sandbox; } @@ -134,43 +164,131 @@ // Sets the desired state for the test to run. void SetTestState(SboxTestsState desired_state) { state_ = desired_state; } - // Sets a flag whether the process should be killed when the TestRunner is - // destroyed. - void SetKillOnDestruction(bool value) { kill_on_destruction_ = value; } - - // Returns the pointers to the policy object. It can be used to modify + // Returns the pointer to the policy object. It can be used to modify // the policy manually. TargetPolicy* GetPolicy(); + // Returns the pointer to the config object. It can be used to modify + // the config manually. + TargetConfig* GetConfig(); + BrokerServices* broker() { return broker_; } + // Blocks until the number of tracked processes returns to zero. + bool WaitForAllTargets(); + + protected: + static base::CommandLine CreateCommandLine(std::string_view command, + base::span<const std::string> args, + SboxTestsState state, + bool no_sandbox, + bool legacy_command); + + TestRunnerBase(JobLevel job_level, + TokenLevel startup_token, + TokenLevel main_token); + + base::Process CreateTestProcess(std::string_view command, + base::span<const std::string> args, + bool legacy_command = false); + + int WaitForResult(const base::Process& process) const; + + private: + base::Process LaunchSandboxProcess(const base::CommandLine& cmd_line); + + raw_ptr<BrokerServices> broker_; + std::unique_ptr<TargetPolicy> policy_; + base::TimeDelta timeout_; + SboxTestsState state_ = AFTER_REVERT; + bool no_sandbox_ = false; + bool disable_csrss_ = true; +}; + +template <internal::TestNameDefinition Test> +class GenericTestRunner final : public TestRunnerBase { + public: + using type = Test; + + GenericTestRunner() + : TestRunnerBase(JobLevel::kLockdown, + USER_RESTRICTED_SAME_ACCESS, + USER_LOCKDOWN) {} + + GenericTestRunner(JobLevel job_level, + TokenLevel startup_token, + TokenLevel main_token) + : TestRunnerBase(job_level, startup_token, main_token) {} + + static base::CommandLine CreateCommandLineForTesting() { + return CreateCommandLine(Test::kTestName, {}, BEFORE_INIT, + /*no_sandbox=*/false, + /*legacy_command=*/false); + } + + // Starts a child process in the sandbox and ask it to run the callback + // command with optional arguments asynchronously. Return a running process + // object. + template <typename... Args> + base::Process RunTestAsync(Args&&... args) { + std::vector<std::string> args_vector = {internal::ToString(args)...}; + return CreateTestProcess(Test::kTestName, args_vector); + } + + // Starts a child process in the sandbox and ask it to run the callback + // command with optional arguments. Return a SboxTestResult. + template <typename... Args> + int RunTest(Args&&... args) { + base::Process process = RunTestAsync(std::forward<Args>(args)...); + if (!process.IsValid()) { + return SBOX_TEST_FAILED_TO_RUN_TEST; + } + return WaitForResult(process); + } +}; + +// TODO(forshaw): This is to support old code which passes and entire command +// line. Remove once the new API is implemented and all the old tests have been +// updated. +class TestRunner final : public TestRunnerBase { + public: + TestRunner() + : TestRunnerBase(JobLevel::kLockdown, + USER_RESTRICTED_SAME_ACCESS, + USER_LOCKDOWN) {} + TestRunner(JobLevel job_level, + TokenLevel startup_token, + TokenLevel main_token) + : TestRunnerBase(job_level, startup_token, main_token) {} + ~TestRunner() override; + + // Sets TestRunner to return without waiting for the process to exit. + void SetAsynchronous(bool is_async) { is_async_ = is_async; } + + // Sets a flag whether the process should be killed when the TestRunner is + // destroyed. + void SetKillOnDestruction(bool value) { kill_on_destruction_ = value; } + // Returns the process handle for an asynchronous test. base::ProcessHandle process() { return target_process_.Handle(); } // Returns the process ID for an asynchronous test. base::ProcessId process_id() { return target_process_.Pid(); } - // Blocks until the number of tracked processes returns to zero. - bool WaitForAllTargets(); + // Starts a child process in the sandbox and ask it to run `command`. + // Return a SboxTestResult. + int RunTest(std::wstring_view command); private: - DWORD timeout_ms(); - int RunTestInternal(std::string_view command, - base::span<const std::string> args); - base::Process LaunchSandboxProcess(const base::CommandLine& cmd_line); - - raw_ptr<BrokerServices> broker_; - std::unique_ptr<TargetPolicy> policy_; - base::TimeDelta timeout_; - SboxTestsState state_ = AFTER_REVERT; - bool is_init_ = false; bool is_async_ = false; - bool no_sandbox_ = false; - bool disable_csrss_ = true; bool kill_on_destruction_ = true; base::Process target_process_; }; +// Declare built-in test commands. +SBOX_TEST_DECLARE_COMMAND(WaitCommand); +SBOX_TEST_DECLARE_COMMAND(PingCommand); + // Returns the broker services. BrokerServices* GetBroker(); @@ -184,9 +302,6 @@ // Runs the given test on the target process. int DispatchCall(); -// Create a command line object for directly calling `SpawnTargetAsync`. -base::CommandLine CreateCommandLineForTesting(std::string_view command); - } // namespace sandbox #endif // SANDBOX_WIN_TESTS_COMMON_CONTROLLER_H_
diff --git a/sandbox/win/tests/validation_tests/suite.cc b/sandbox/win/tests/validation_tests/suite.cc index b6ec078..32d2e8f 100644 --- a/sandbox/win/tests/validation_tests/suite.cc +++ b/sandbox/win/tests/validation_tests/suite.cc
@@ -88,8 +88,8 @@ // Tests if the suite is working properly. TEST(ValidationSuite, TestSuite) { - TestRunner runner; - ASSERT_EQ(SBOX_TEST_PING_OK, runner.RunTest(L"ping")); + PingCommandTestRunner runner; + ASSERT_EQ(SBOX_TEST_PING_OK, runner.RunTest()); } // Tests if the file system is correctly protected by the sandbox.
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json index b28dd3ec..8d0c90f 100644 --- a/testing/variations/fieldtrial_testing_config.json +++ b/testing/variations/fieldtrial_testing_config.json
@@ -6172,28 +6172,6 @@ ] } ], - "ClientSideDetectionNewObservers": [ - { - "platforms": [ - "android", - "chromeos", - "linux", - "mac", - "windows" - ], - "experiments": [ - { - "name": "Enabled", - "params": { - "ClassificationDelay": "0.0" - }, - "enable_features": [ - "ClientSideDetectionNewObservers" - ] - } - ] - } - ], "ClientSideDetectionOnDeviceModelLazyDownloadAndroid": [ { "platforms": [ @@ -19306,6 +19284,21 @@ ] } ], + "PdfSaveToDrive_ChromeOS": [ + { + "platforms": [ + "chromeos" + ], + "experiments": [ + { + "name": "Enabled", + "enable_features": [ + "PdfSaveToDrive" + ] + } + ] + } + ], "PdfUseShowSaveFilePicker": [ { "platforms": [
diff --git a/third_party/android_deps/autorolled/VERSION.txt b/third_party/android_deps/autorolled/VERSION.txt index b64ce9e..1dbbe40 100644 --- a/third_party/android_deps/autorolled/VERSION.txt +++ b/third_party/android_deps/autorolled/VERSION.txt
@@ -1 +1 @@ -b5b60068c40971c.bc7f3b9ff45a220 \ No newline at end of file +5f2ae8ba61d6331.0dc852e54189258 \ No newline at end of file
diff --git a/third_party/android_deps/autorolled/bill_of_materials.json b/third_party/android_deps/autorolled/bill_of_materials.json index 72ee347..94c0f20 100644 --- a/third_party/android_deps/autorolled/bill_of_materials.json +++ b/third_party/android_deps/autorolled/bill_of_materials.json
@@ -1437,7 +1437,7 @@ { "name": "error_prone_annotations", "group": "com.google.errorprone", - "version": "2.47.0" + "version": "2.48.0" }, { "name": "firebase-annotations", @@ -1537,7 +1537,7 @@ { "name": "protobuf-javalite", "group": "com.google.protobuf", - "version": "4.34.0-RC2" + "version": "4.34.0" }, { "name": "protobuf-lite",
diff --git a/third_party/android_deps/autorolled/build.gradle b/third_party/android_deps/autorolled/build.gradle index a85f219..73347de 100644 --- a/third_party/android_deps/autorolled/build.gradle +++ b/third_party/android_deps/autorolled/build.gradle
@@ -301,7 +301,7 @@ versionCache['com.google.code.findbugs:jsr305'] = '3.0.2' versionCache['com.google.code.gson:gson'] = '2.13.2' versionCache['com.google.errorprone:error_prone_annotation'] = '2.41.0' -versionCache['com.google.errorprone:error_prone_annotations'] = '2.47.0' +versionCache['com.google.errorprone:error_prone_annotations'] = '2.48.0' versionCache['com.google.firebase:firebase-annotations'] = '17.0.0' versionCache['com.google.firebase:firebase-common'] = '22.0.1' versionCache['com.google.firebase:firebase-components'] = '19.0.0' @@ -321,7 +321,7 @@ versionCache['com.google.mlkit:common'] = '18.11.0' versionCache['com.google.mlkit:genai-common'] = '1.0.0-beta3' versionCache['com.google.mlkit:genai-prompt'] = '1.0.0-beta1' -versionCache['com.google.protobuf:protobuf-javalite'] = '4.34.0-RC2' +versionCache['com.google.protobuf:protobuf-javalite'] = '4.34.0' versionCache['com.google.protobuf:protobuf-lite'] = '3.0.1' versionCache['com.google.testparameterinjector:test-parameter-injector'] = '1.18' versionCache['com.googlecode.java-diff-utils:diffutils'] = '1.3.0'
diff --git a/third_party/android_deps/autorolled/committed/libs/com_google_errorprone_error_prone_annotations/README.chromium b/third_party/android_deps/autorolled/committed/libs/com_google_errorprone_error_prone_annotations/README.chromium index c037234..a229616a 100644 --- a/third_party/android_deps/autorolled/committed/libs/com_google_errorprone_error_prone_annotations/README.chromium +++ b/third_party/android_deps/autorolled/committed/libs/com_google_errorprone_error_prone_annotations/README.chromium
@@ -1,7 +1,7 @@ Name: error-prone annotations Short Name: error_prone_annotations -URL: https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_annotations/2.47.0/error_prone_annotations-2.47.0.jar -Version: 2.47.0 +URL: https://repo.maven.apache.org/maven2/com/google/errorprone/error_prone_annotations/2.48.0/error_prone_annotations-2.48.0.jar +Version: 2.48.0 Update Mechanism: Autoroll License: Apache-2.0 License File: LICENSE
diff --git a/third_party/android_deps/autorolled/committed/libs/com_google_protobuf_protobuf_javalite/README.chromium b/third_party/android_deps/autorolled/committed/libs/com_google_protobuf_protobuf_javalite/README.chromium index 27af6088..bd1b5f74 100644 --- a/third_party/android_deps/autorolled/committed/libs/com_google_protobuf_protobuf_javalite/README.chromium +++ b/third_party/android_deps/autorolled/committed/libs/com_google_protobuf_protobuf_javalite/README.chromium
@@ -1,7 +1,7 @@ Name: Protocol Buffers [Lite] Short Name: protobuf-javalite -URL: https://repo.maven.apache.org/maven2/com/google/protobuf/protobuf-javalite/4.34.0-RC2/protobuf-javalite-4.34.0-RC2.jar -Version: 4.34.0-RC2 +URL: https://repo.maven.apache.org/maven2/com/google/protobuf/protobuf-javalite/4.34.0/protobuf-javalite-4.34.0.jar +Version: 4.34.0 Update Mechanism: Autoroll License: BSD-3-Clause License File: LICENSE
diff --git a/third_party/androidx/build.gradle b/third_party/androidx/build.gradle index d4382c7..aafed27 100644 --- a/third_party/androidx/build.gradle +++ b/third_party/androidx/build.gradle
@@ -323,7 +323,7 @@ google() maven { // This URL is generated by the fetch_all_androidx.py script. - url 'https://androidx.dev/snapshots/builds/14950004/artifacts/repository' + url 'https://androidx.dev/snapshots/builds/14954281/artifacts/repository' } mavenCentral() }
diff --git a/third_party/androidx/committed/libs/androidx_activity_activity/README.chromium b/third_party/androidx/committed/libs/androidx_activity_activity/README.chromium index 203d739..493111ab 100644 --- a/third_party/androidx/committed/libs/androidx_activity_activity/README.chromium +++ b/third_party/androidx/committed/libs/androidx_activity_activity/README.chromium
@@ -1,6 +1,6 @@ Name: Activity Short Name: activity -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/activity/activity/1.13.0-SNAPSHOT/activity-1.13.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/activity/activity/1.13.0-SNAPSHOT/activity-1.13.0-20260228.045313-1.aar Version: 1.13.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_activity_activity_compose/README.chromium b/third_party/androidx/committed/libs/androidx_activity_activity_compose/README.chromium index 9dc3460..392539fa 100644 --- a/third_party/androidx/committed/libs/androidx_activity_activity_compose/README.chromium +++ b/third_party/androidx/committed/libs/androidx_activity_activity_compose/README.chromium
@@ -1,6 +1,6 @@ Name: Activity Compose Short Name: activity-compose -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/activity/activity-compose/1.13.0-SNAPSHOT/activity-compose-1.13.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/activity/activity-compose/1.13.0-SNAPSHOT/activity-compose-1.13.0-20260228.045313-1.aar Version: 1.13.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_activity_activity_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_activity_activity_ktx/README.chromium index f7e5010d..748e91a6 100644 --- a/third_party/androidx/committed/libs/androidx_activity_activity_ktx/README.chromium +++ b/third_party/androidx/committed/libs/androidx_activity_activity_ktx/README.chromium
@@ -1,6 +1,6 @@ Name: Activity Kotlin Extensions Short Name: activity-ktx -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/activity/activity-ktx/1.13.0-SNAPSHOT/activity-ktx-1.13.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/activity/activity-ktx/1.13.0-SNAPSHOT/activity-ktx-1.13.0-20260228.045313-1.aar Version: 1.13.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_annotation_annotation_experimental/README.chromium b/third_party/androidx/committed/libs/androidx_annotation_annotation_experimental/README.chromium index 0ee88cfb..ccd961dd 100644 --- a/third_party/androidx/committed/libs/androidx_annotation_annotation_experimental/README.chromium +++ b/third_party/androidx/committed/libs/androidx_annotation_annotation_experimental/README.chromium
@@ -1,6 +1,6 @@ Name: Experimental annotation Short Name: annotation-experimental -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/annotation/annotation-experimental/1.6.0-SNAPSHOT/annotation-experimental-1.6.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/annotation/annotation-experimental/1.6.0-SNAPSHOT/annotation-experimental-1.6.0-20260228.045313-1.aar Version: 1.6.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_annotation_annotation_jvm/README.chromium b/third_party/androidx/committed/libs/androidx_annotation_annotation_jvm/README.chromium index 2cfdd2fa..dbcd044e 100644 --- a/third_party/androidx/committed/libs/androidx_annotation_annotation_jvm/README.chromium +++ b/third_party/androidx/committed/libs/androidx_annotation_annotation_jvm/README.chromium
@@ -1,6 +1,6 @@ Name: Annotation Short Name: annotation-jvm -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/annotation/annotation-jvm/1.10.0-SNAPSHOT/annotation-jvm-1.10.0-20260227.122158-1.jar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/annotation/annotation-jvm/1.10.0-SNAPSHOT/annotation-jvm-1.10.0-20260228.045313-1.jar Version: 1.10.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_appcompat_appcompat/README.chromium b/third_party/androidx/committed/libs/androidx_appcompat_appcompat/README.chromium index a90be23..4ca0833e 100644 --- a/third_party/androidx/committed/libs/androidx_appcompat_appcompat/README.chromium +++ b/third_party/androidx/committed/libs/androidx_appcompat_appcompat/README.chromium
@@ -1,6 +1,6 @@ Name: AppCompat Short Name: appcompat -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/appcompat/appcompat/1.8.0-SNAPSHOT/appcompat-1.8.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/appcompat/appcompat/1.8.0-SNAPSHOT/appcompat-1.8.0-20260228.045313-1.aar Version: 1.8.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_appcompat_appcompat_resources/README.chromium b/third_party/androidx/committed/libs/androidx_appcompat_appcompat_resources/README.chromium index 84b3cbb..f4e4057d 100644 --- a/third_party/androidx/committed/libs/androidx_appcompat_appcompat_resources/README.chromium +++ b/third_party/androidx/committed/libs/androidx_appcompat_appcompat_resources/README.chromium
@@ -1,6 +1,6 @@ Name: AppCompat Resources Short Name: appcompat-resources -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/appcompat/appcompat-resources/1.8.0-SNAPSHOT/appcompat-resources-1.8.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/appcompat/appcompat-resources/1.8.0-SNAPSHOT/appcompat-resources-1.8.0-20260228.045313-1.aar Version: 1.8.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_appsearch_appsearch/README.chromium b/third_party/androidx/committed/libs/androidx_appsearch_appsearch/README.chromium index 7591f1e0..4975584 100644 --- a/third_party/androidx/committed/libs/androidx_appsearch_appsearch/README.chromium +++ b/third_party/androidx/committed/libs/androidx_appsearch_appsearch/README.chromium
@@ -1,6 +1,6 @@ Name: AppSearch Short Name: appsearch -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/appsearch/appsearch/1.2.0-SNAPSHOT/appsearch-1.2.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/appsearch/appsearch/1.2.0-SNAPSHOT/appsearch-1.2.0-20260228.045313-1.aar Version: 1.2.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_appsearch_appsearch_builtin_types/README.chromium b/third_party/androidx/committed/libs/androidx_appsearch_appsearch_builtin_types/README.chromium index 76a8a4f..78e020c0 100644 --- a/third_party/androidx/committed/libs/androidx_appsearch_appsearch_builtin_types/README.chromium +++ b/third_party/androidx/committed/libs/androidx_appsearch_appsearch_builtin_types/README.chromium
@@ -1,6 +1,6 @@ Name: AppSearch Builtin Types Short Name: appsearch-builtin-types -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/appsearch/appsearch-builtin-types/1.2.0-SNAPSHOT/appsearch-builtin-types-1.2.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/appsearch/appsearch-builtin-types/1.2.0-SNAPSHOT/appsearch-builtin-types-1.2.0-20260228.045313-1.aar Version: 1.2.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_appsearch_appsearch_platform_storage/README.chromium b/third_party/androidx/committed/libs/androidx_appsearch_appsearch_platform_storage/README.chromium index 93390d1..2448667 100644 --- a/third_party/androidx/committed/libs/androidx_appsearch_appsearch_platform_storage/README.chromium +++ b/third_party/androidx/committed/libs/androidx_appsearch_appsearch_platform_storage/README.chromium
@@ -1,6 +1,6 @@ Name: AppSearch Platform Storage Short Name: appsearch-platform-storage -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/appsearch/appsearch-platform-storage/1.2.0-SNAPSHOT/appsearch-platform-storage-1.2.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/appsearch/appsearch-platform-storage/1.2.0-SNAPSHOT/appsearch-platform-storage-1.2.0-20260228.045313-1.aar Version: 1.2.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_arch_core_core_common/README.chromium b/third_party/androidx/committed/libs/androidx_arch_core_core_common/README.chromium index e6ceb77..1bc1310 100644 --- a/third_party/androidx/committed/libs/androidx_arch_core_core_common/README.chromium +++ b/third_party/androidx/committed/libs/androidx_arch_core_core_common/README.chromium
@@ -1,6 +1,6 @@ Name: Arch-Common Short Name: core-common -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/arch/core/core-common/2.3.0-SNAPSHOT/core-common-2.3.0-20260227.122158-1.jar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/arch/core/core-common/2.3.0-SNAPSHOT/core-common-2.3.0-20260228.045313-1.jar Version: 2.3.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_arch_core_core_runtime/README.chromium b/third_party/androidx/committed/libs/androidx_arch_core_core_runtime/README.chromium index db8ac64..d605a8a 100644 --- a/third_party/androidx/committed/libs/androidx_arch_core_core_runtime/README.chromium +++ b/third_party/androidx/committed/libs/androidx_arch_core_core_runtime/README.chromium
@@ -1,6 +1,6 @@ Name: Arch-Runtime Short Name: core-runtime -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/arch/core/core-runtime/2.3.0-SNAPSHOT/core-runtime-2.3.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/arch/core/core-runtime/2.3.0-SNAPSHOT/core-runtime-2.3.0-20260228.045313-1.aar Version: 2.3.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_autofill_autofill/README.chromium b/third_party/androidx/committed/libs/androidx_autofill_autofill/README.chromium index 7d30ef3b..d5b37d34 100644 --- a/third_party/androidx/committed/libs/androidx_autofill_autofill/README.chromium +++ b/third_party/androidx/committed/libs/androidx_autofill_autofill/README.chromium
@@ -1,6 +1,6 @@ Name: Autofill Short Name: autofill -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/autofill/autofill/1.4.0-SNAPSHOT/autofill-1.4.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/autofill/autofill/1.4.0-SNAPSHOT/autofill-1.4.0-20260228.045313-1.aar Version: 1.4.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_common/README.chromium b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_common/README.chromium index 38ba6e5..7670098 100644 --- a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_common/README.chromium +++ b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_common/README.chromium
@@ -1,6 +1,6 @@ Name: Benchmark - Common Short Name: benchmark-common -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/benchmark/benchmark-common/1.5.0-SNAPSHOT/benchmark-common-1.5.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/benchmark/benchmark-common/1.5.0-SNAPSHOT/benchmark-common-1.5.0-20260228.045313-1.aar Version: 1.5.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_junit4/README.chromium b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_junit4/README.chromium index 377274a..f448b38 100644 --- a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_junit4/README.chromium +++ b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_junit4/README.chromium
@@ -1,6 +1,6 @@ Name: Benchmark - JUnit4 Short Name: benchmark-junit4 -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/benchmark/benchmark-junit4/1.5.0-SNAPSHOT/benchmark-junit4-1.5.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/benchmark/benchmark-junit4/1.5.0-SNAPSHOT/benchmark-junit4-1.5.0-20260228.045313-1.aar Version: 1.5.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_macro/README.chromium b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_macro/README.chromium index d9fc6b6..ede9833 100644 --- a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_macro/README.chromium +++ b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_macro/README.chromium
@@ -1,6 +1,6 @@ Name: Benchmark - Macrobenchmark Short Name: benchmark-macro -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/benchmark/benchmark-macro/1.5.0-SNAPSHOT/benchmark-macro-1.5.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/benchmark/benchmark-macro/1.5.0-SNAPSHOT/benchmark-macro-1.5.0-20260228.045313-1.aar Version: 1.5.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_macro_junit4/README.chromium b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_macro_junit4/README.chromium index 996df69..bb0411e 100644 --- a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_macro_junit4/README.chromium +++ b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_macro_junit4/README.chromium
@@ -1,6 +1,6 @@ Name: Benchmark - Macrobenchmark JUnit4 Short Name: benchmark-macro-junit4 -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/benchmark/benchmark-macro-junit4/1.5.0-SNAPSHOT/benchmark-macro-junit4-1.5.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/benchmark/benchmark-macro-junit4/1.5.0-SNAPSHOT/benchmark-macro-junit4-1.5.0-20260228.045313-1.aar Version: 1.5.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_traceprocessor_android/README.chromium b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_traceprocessor_android/README.chromium index 3e2fbccd..859913c 100644 --- a/third_party/androidx/committed/libs/androidx_benchmark_benchmark_traceprocessor_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_benchmark_benchmark_traceprocessor_android/README.chromium
@@ -1,6 +1,6 @@ Name: Benchmark TraceProcessor Short Name: benchmark-traceprocessor-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/benchmark/benchmark-traceprocessor-android/1.5.0-SNAPSHOT/benchmark-traceprocessor-android-1.5.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/benchmark/benchmark-traceprocessor-android/1.5.0-SNAPSHOT/benchmark-traceprocessor-android-1.5.0-20260228.045313-1.aar Version: 1.5.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_biometric_biometric/README.chromium b/third_party/androidx/committed/libs/androidx_biometric_biometric/README.chromium index 8e440cbb..306750a 100644 --- a/third_party/androidx/committed/libs/androidx_biometric_biometric/README.chromium +++ b/third_party/androidx/committed/libs/androidx_biometric_biometric/README.chromium
@@ -1,6 +1,6 @@ Name: Biometric Short Name: biometric -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/biometric/biometric/1.4.0-SNAPSHOT/biometric-1.4.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/biometric/biometric/1.4.0-SNAPSHOT/biometric-1.4.0-20260228.045313-1.aar Version: 1.4.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_browser_browser/README.chromium b/third_party/androidx/committed/libs/androidx_browser_browser/README.chromium index 13d72ad20..ad5454d 100644 --- a/third_party/androidx/committed/libs/androidx_browser_browser/README.chromium +++ b/third_party/androidx/committed/libs/androidx_browser_browser/README.chromium
@@ -1,6 +1,6 @@ Name: Browser Short Name: browser -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/browser/browser/1.10.0-SNAPSHOT/browser-1.10.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/browser/browser/1.10.0-SNAPSHOT/browser-1.10.0-20260228.045313-1.aar Version: 1.10.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_cardview_cardview/README.chromium b/third_party/androidx/committed/libs/androidx_cardview_cardview/README.chromium index 20c9eb6..ab08c30 100644 --- a/third_party/androidx/committed/libs/androidx_cardview_cardview/README.chromium +++ b/third_party/androidx/committed/libs/androidx_cardview_cardview/README.chromium
@@ -1,6 +1,6 @@ Name: CardView Short Name: cardview -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/cardview/cardview/1.1.0-SNAPSHOT/cardview-1.1.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/cardview/cardview/1.1.0-SNAPSHOT/cardview-1.1.0-20260228.045313-1.aar Version: 1.1.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_collection_collection_jvm/README.chromium b/third_party/androidx/committed/libs/androidx_collection_collection_jvm/README.chromium index da42ea0..356cf076 100644 --- a/third_party/androidx/committed/libs/androidx_collection_collection_jvm/README.chromium +++ b/third_party/androidx/committed/libs/androidx_collection_collection_jvm/README.chromium
@@ -1,6 +1,6 @@ Name: collections Short Name: collection-jvm -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/collection/collection-jvm/1.7.0-SNAPSHOT/collection-jvm-1.7.0-20260227.122158-1.jar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/collection/collection-jvm/1.7.0-SNAPSHOT/collection-jvm-1.7.0-20260228.045313-1.jar Version: 1.7.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_collection_collection_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_collection_collection_ktx/README.chromium index 3acb277..47edfa8 100644 --- a/third_party/androidx/committed/libs/androidx_collection_collection_ktx/README.chromium +++ b/third_party/androidx/committed/libs/androidx_collection_collection_ktx/README.chromium
@@ -1,6 +1,6 @@ Name: Collections Kotlin Extensions Short Name: collection-ktx -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/collection/collection-ktx/1.7.0-SNAPSHOT/collection-ktx-1.7.0-20260227.122158-1.jar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/collection/collection-ktx/1.7.0-SNAPSHOT/collection-ktx-1.7.0-20260228.045313-1.jar Version: 1.7.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_animation_animation_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_animation_animation_android/README.chromium index 1668dcd..9fc8186a 100644 --- a/third_party/androidx/committed/libs/androidx_compose_animation_animation_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_compose_animation_animation_android/README.chromium
@@ -1,6 +1,6 @@ Name: Compose Animation Short Name: animation-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/compose/animation/animation-android/1.11.0-SNAPSHOT/animation-android-1.11.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/compose/animation/animation-android/1.11.0-SNAPSHOT/animation-android-1.11.0-20260228.045313-1.aar Version: 1.11.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_animation_animation_core_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_animation_animation_core_android/README.chromium index 2f6ad2d..89754907 100644 --- a/third_party/androidx/committed/libs/androidx_compose_animation_animation_core_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_compose_animation_animation_core_android/README.chromium
@@ -1,6 +1,6 @@ Name: Compose Animation Core Short Name: animation-core-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/compose/animation/animation-core-android/1.11.0-SNAPSHOT/animation-core-android-1.11.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/compose/animation/animation-core-android/1.11.0-SNAPSHOT/animation-core-android-1.11.0-20260228.045313-1.aar Version: 1.11.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_foundation_foundation_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_foundation_foundation_android/README.chromium index 3d4a433d..d73075a 100644 --- a/third_party/androidx/committed/libs/androidx_compose_foundation_foundation_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_compose_foundation_foundation_android/README.chromium
@@ -1,6 +1,6 @@ Name: Compose Foundation Short Name: foundation-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/compose/foundation/foundation-android/1.11.0-SNAPSHOT/foundation-android-1.11.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/compose/foundation/foundation-android/1.11.0-SNAPSHOT/foundation-android-1.11.0-20260228.045313-1.aar Version: 1.11.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_foundation_foundation_layout_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_foundation_foundation_layout_android/README.chromium index 9fd03c46..c48ebcc 100644 --- a/third_party/androidx/committed/libs/androidx_compose_foundation_foundation_layout_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_compose_foundation_foundation_layout_android/README.chromium
@@ -1,6 +1,6 @@ Name: Compose Layouts Short Name: foundation-layout-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/compose/foundation/foundation-layout-android/1.11.0-SNAPSHOT/foundation-layout-android-1.11.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/compose/foundation/foundation-layout-android/1.11.0-SNAPSHOT/foundation-layout-android-1.11.0-20260228.045313-1.aar Version: 1.11.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_material3_material3_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_material3_material3_android/README.chromium index 1e2ac5f..dea727e 100644 --- a/third_party/androidx/committed/libs/androidx_compose_material3_material3_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_compose_material3_material3_android/README.chromium
@@ -1,6 +1,6 @@ Name: Compose Material3 Components Short Name: material3-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/compose/material3/material3-android/1.5.0-SNAPSHOT/material3-android-1.5.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/compose/material3/material3-android/1.5.0-SNAPSHOT/material3-android-1.5.0-20260228.045313-1.aar Version: 1.5.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_material_material_ripple_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_material_material_ripple_android/README.chromium index 689c0da..94d319f 100644 --- a/third_party/androidx/committed/libs/androidx_compose_material_material_ripple_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_compose_material_material_ripple_android/README.chromium
@@ -1,6 +1,6 @@ Name: Compose Material Ripple Short Name: material-ripple-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/compose/material/material-ripple-android/1.11.0-SNAPSHOT/material-ripple-android-1.11.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/compose/material/material-ripple-android/1.11.0-SNAPSHOT/material-ripple-android-1.11.0-20260228.045313-1.aar Version: 1.11.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_android/README.chromium index a7118e6..a49c339 100644 --- a/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_android/README.chromium
@@ -1,6 +1,6 @@ Name: Compose Runtime Short Name: runtime-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/compose/runtime/runtime-android/1.11.0-SNAPSHOT/runtime-android-1.11.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/compose/runtime/runtime-android/1.11.0-SNAPSHOT/runtime-android-1.11.0-20260228.045313-1.aar Version: 1.11.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_annotation_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_annotation_android/README.chromium index c0b7f16..fab59f4 100644 --- a/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_annotation_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_annotation_android/README.chromium
@@ -1,6 +1,6 @@ Name: Compose Runtime Annotation Short Name: runtime-annotation-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/compose/runtime/runtime-annotation-android/1.11.0-SNAPSHOT/runtime-annotation-android-1.11.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/compose/runtime/runtime-annotation-android/1.11.0-SNAPSHOT/runtime-annotation-android-1.11.0-20260228.045313-1.aar Version: 1.11.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_retain_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_retain_android/README.chromium index 7697627f..f23368e 100644 --- a/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_retain_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_retain_android/README.chromium
@@ -1,6 +1,6 @@ Name: Compose Runtime Retain Short Name: runtime-retain-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/compose/runtime/runtime-retain-android/1.11.0-SNAPSHOT/runtime-retain-android-1.11.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/compose/runtime/runtime-retain-android/1.11.0-SNAPSHOT/runtime-retain-android-1.11.0-20260228.045313-1.aar Version: 1.11.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_saveable_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_saveable_android/README.chromium index 91bb68f0..7c62d07 100644 --- a/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_saveable_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_compose_runtime_runtime_saveable_android/README.chromium
@@ -1,6 +1,6 @@ Name: Compose Saveable Short Name: runtime-saveable-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/compose/runtime/runtime-saveable-android/1.11.0-SNAPSHOT/runtime-saveable-android-1.11.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/compose/runtime/runtime-saveable-android/1.11.0-SNAPSHOT/runtime-saveable-android-1.11.0-20260228.045313-1.aar Version: 1.11.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_android/README.chromium index 7063795..10ced93f 100644 --- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_android/README.chromium
@@ -1,6 +1,6 @@ Name: Compose UI Short Name: ui-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/compose/ui/ui-android/1.11.0-SNAPSHOT/ui-android-1.11.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/compose/ui/ui-android/1.11.0-SNAPSHOT/ui-android-1.11.0-20260228.045313-1.aar Version: 1.11.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_geometry_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_geometry_android/README.chromium index d4822ad..3483c38 100644 --- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_geometry_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_geometry_android/README.chromium
@@ -1,6 +1,6 @@ Name: Compose Geometry Short Name: ui-geometry-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/compose/ui/ui-geometry-android/1.11.0-SNAPSHOT/ui-geometry-android-1.11.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/compose/ui/ui-geometry-android/1.11.0-SNAPSHOT/ui-geometry-android-1.11.0-20260228.045313-1.aar Version: 1.11.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_graphics_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_graphics_android/README.chromium index 66652560..cc9f219 100644 --- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_graphics_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_graphics_android/README.chromium
@@ -1,6 +1,6 @@ Name: Compose Graphics Short Name: ui-graphics-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/compose/ui/ui-graphics-android/1.11.0-SNAPSHOT/ui-graphics-android-1.11.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/compose/ui/ui-graphics-android/1.11.0-SNAPSHOT/ui-graphics-android-1.11.0-20260228.045313-1.aar Version: 1.11.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_android/README.chromium index c03b53f1..f7a6a92 100644 --- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_android/README.chromium
@@ -1,6 +1,6 @@ Name: Compose Testing Short Name: ui-test-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/compose/ui/ui-test-android/1.11.0-SNAPSHOT/ui-test-android-1.11.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/compose/ui/ui-test-android/1.11.0-SNAPSHOT/ui-test-android-1.11.0-20260228.045313-1.aar Version: 1.11.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_junit4_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_junit4_android/README.chromium index ed93d65..c0c5b026 100644 --- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_junit4_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_junit4_android/README.chromium
@@ -1,6 +1,6 @@ Name: Compose Testing for JUnit4 Short Name: ui-test-junit4-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/compose/ui/ui-test-junit4-android/1.11.0-SNAPSHOT/ui-test-junit4-android-1.11.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/compose/ui/ui-test-junit4-android/1.11.0-SNAPSHOT/ui-test-junit4-android-1.11.0-20260228.045313-1.aar Version: 1.11.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_manifest/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_manifest/README.chromium index 96d3903..2bb0892 100644 --- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_manifest/README.chromium +++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_test_manifest/README.chromium
@@ -1,6 +1,6 @@ Name: Compose Testing manifest dependency Short Name: ui-test-manifest -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/compose/ui/ui-test-manifest/1.11.0-SNAPSHOT/ui-test-manifest-1.11.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/compose/ui/ui-test-manifest/1.11.0-SNAPSHOT/ui-test-manifest-1.11.0-20260228.045313-1.aar Version: 1.11.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_text_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_text_android/README.chromium index f223d8d..d7ae82a 100644 --- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_text_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_text_android/README.chromium
@@ -1,6 +1,6 @@ Name: Compose UI Text Short Name: ui-text-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/compose/ui/ui-text-android/1.11.0-SNAPSHOT/ui-text-android-1.11.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/compose/ui/ui-text-android/1.11.0-SNAPSHOT/ui-text-android-1.11.0-20260228.045313-1.aar Version: 1.11.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_text_google_fonts/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_text_google_fonts/README.chromium index 7855d4f..ac8532b 100644 --- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_text_google_fonts/README.chromium +++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_text_google_fonts/README.chromium
@@ -1,6 +1,6 @@ Name: Compose Google Fonts integration Short Name: ui-text-google-fonts -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/compose/ui/ui-text-google-fonts/1.11.0-SNAPSHOT/ui-text-google-fonts-1.11.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/compose/ui/ui-text-google-fonts/1.11.0-SNAPSHOT/ui-text-google-fonts-1.11.0-20260228.045313-1.aar Version: 1.11.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_unit_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_unit_android/README.chromium index b2ffbbd..7adf65c 100644 --- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_unit_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_unit_android/README.chromium
@@ -1,6 +1,6 @@ Name: Compose Unit Short Name: ui-unit-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/compose/ui/ui-unit-android/1.11.0-SNAPSHOT/ui-unit-android-1.11.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/compose/ui/ui-unit-android/1.11.0-SNAPSHOT/ui-unit-android-1.11.0-20260228.045313-1.aar Version: 1.11.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_compose_ui_ui_util_android/README.chromium b/third_party/androidx/committed/libs/androidx_compose_ui_ui_util_android/README.chromium index 1470789..5054894 100644 --- a/third_party/androidx/committed/libs/androidx_compose_ui_ui_util_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_compose_ui_ui_util_android/README.chromium
@@ -1,6 +1,6 @@ Name: Compose Util Short Name: ui-util-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/compose/ui/ui-util-android/1.11.0-SNAPSHOT/ui-util-android-1.11.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/compose/ui/ui-util-android/1.11.0-SNAPSHOT/ui-util-android-1.11.0-20260228.045313-1.aar Version: 1.11.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_constraintlayout_constraintlayout/README.chromium b/third_party/androidx/committed/libs/androidx_constraintlayout_constraintlayout/README.chromium index 3ddc83b..350ead0 100644 --- a/third_party/androidx/committed/libs/androidx_constraintlayout_constraintlayout/README.chromium +++ b/third_party/androidx/committed/libs/androidx_constraintlayout_constraintlayout/README.chromium
@@ -1,6 +1,6 @@ Name: ConstraintLayout Short Name: constraintlayout -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/constraintlayout/constraintlayout/2.3.0-SNAPSHOT/constraintlayout-2.3.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/constraintlayout/constraintlayout/2.3.0-SNAPSHOT/constraintlayout-2.3.0-20260228.045313-1.aar Version: 2.3.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_constraintlayout_constraintlayout_core/README.chromium b/third_party/androidx/committed/libs/androidx_constraintlayout_constraintlayout_core/README.chromium index afa1dca..3c44543 100644 --- a/third_party/androidx/committed/libs/androidx_constraintlayout_constraintlayout_core/README.chromium +++ b/third_party/androidx/committed/libs/androidx_constraintlayout_constraintlayout_core/README.chromium
@@ -1,6 +1,6 @@ Name: ConstraintLayout Core Short Name: constraintlayout-core -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/constraintlayout/constraintlayout-core/1.2.0-SNAPSHOT/constraintlayout-core-1.2.0-20260227.122158-1.jar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/constraintlayout/constraintlayout-core/1.2.0-SNAPSHOT/constraintlayout-core-1.2.0-20260228.045313-1.jar Version: 1.2.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_core_core/README.chromium b/third_party/androidx/committed/libs/androidx_core_core/README.chromium index caae503b..3d3b7fb 100644 --- a/third_party/androidx/committed/libs/androidx_core_core/README.chromium +++ b/third_party/androidx/committed/libs/androidx_core_core/README.chromium
@@ -1,6 +1,6 @@ Name: Core Short Name: core -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/core/core/1.18.0-SNAPSHOT/core-1.18.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/core/core/1.18.0-SNAPSHOT/core-1.18.0-20260228.045313-1.aar Version: 1.18.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_core_core_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_core_core_ktx/README.chromium index efefdb91..5c0b23c0d 100644 --- a/third_party/androidx/committed/libs/androidx_core_core_ktx/README.chromium +++ b/third_party/androidx/committed/libs/androidx_core_core_ktx/README.chromium
@@ -1,6 +1,6 @@ Name: Core Kotlin Extensions Short Name: core-ktx -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/core/core-ktx/1.18.0-SNAPSHOT/core-ktx-1.18.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/core/core-ktx/1.18.0-SNAPSHOT/core-ktx-1.18.0-20260228.045313-1.aar Version: 1.18.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_core_core_pip/README.chromium b/third_party/androidx/committed/libs/androidx_core_core_pip/README.chromium index 56e74468..69ee525 100644 --- a/third_party/androidx/committed/libs/androidx_core_core_pip/README.chromium +++ b/third_party/androidx/committed/libs/androidx_core_core_pip/README.chromium
@@ -1,6 +1,6 @@ Name: androidx.core:core-pip Short Name: core-pip -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/core/core-pip/1.0.0-SNAPSHOT/core-pip-1.0.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/core/core-pip/1.0.0-SNAPSHOT/core-pip-1.0.0-20260228.045313-1.aar Version: 1.0.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_core_core_viewtree/README.chromium b/third_party/androidx/committed/libs/androidx_core_core_viewtree/README.chromium index f647233..ac2083c 100644 --- a/third_party/androidx/committed/libs/androidx_core_core_viewtree/README.chromium +++ b/third_party/androidx/committed/libs/androidx_core_core_viewtree/README.chromium
@@ -1,6 +1,6 @@ Name: androidx.core:core-viewtree Short Name: core-viewtree -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/core/core-viewtree/1.1.0-SNAPSHOT/core-viewtree-1.1.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/core/core-viewtree/1.1.0-SNAPSHOT/core-viewtree-1.1.0-20260228.045313-1.aar Version: 1.1.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_credentials_credentials/README.chromium b/third_party/androidx/committed/libs/androidx_credentials_credentials/README.chromium index dd447113..718feab4 100644 --- a/third_party/androidx/committed/libs/androidx_credentials_credentials/README.chromium +++ b/third_party/androidx/committed/libs/androidx_credentials_credentials/README.chromium
@@ -1,6 +1,6 @@ Name: Credentials Short Name: credentials -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/credentials/credentials/1.6.0-SNAPSHOT/credentials-1.6.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/credentials/credentials/1.6.0-SNAPSHOT/credentials-1.6.0-20260228.045313-1.aar Version: 1.6.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_credentials_credentials_play_services_auth/README.chromium b/third_party/androidx/committed/libs/androidx_credentials_credentials_play_services_auth/README.chromium index a511858..214ef58 100644 --- a/third_party/androidx/committed/libs/androidx_credentials_credentials_play_services_auth/README.chromium +++ b/third_party/androidx/committed/libs/androidx_credentials_credentials_play_services_auth/README.chromium
@@ -1,6 +1,6 @@ Name: Credentials Play Services Auth Short Name: credentials-play-services-auth -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/credentials/credentials-play-services-auth/1.6.0-SNAPSHOT/credentials-play-services-auth-1.6.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/credentials/credentials-play-services-auth/1.6.0-SNAPSHOT/credentials-play-services-auth-1.6.0-20260228.045313-1.aar Version: 1.6.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_credentials_registry_registry_provider/README.chromium b/third_party/androidx/committed/libs/androidx_credentials_registry_registry_provider/README.chromium index 2463da5..fd8aecc2 100644 --- a/third_party/androidx/committed/libs/androidx_credentials_registry_registry_provider/README.chromium +++ b/third_party/androidx/committed/libs/androidx_credentials_registry_registry_provider/README.chromium
@@ -1,6 +1,6 @@ Name: androidx.credentials.registry:registry-provider Short Name: registry-provider -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/credentials/registry/registry-provider/1.0.0-SNAPSHOT/registry-provider-1.0.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/credentials/registry/registry-provider/1.0.0-SNAPSHOT/registry-provider-1.0.0-20260228.045313-1.aar Version: 1.0.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_credentials_registry_registry_provider_play_services/README.chromium b/third_party/androidx/committed/libs/androidx_credentials_registry_registry_provider_play_services/README.chromium index ce83cc9f..5ca0919 100644 --- a/third_party/androidx/committed/libs/androidx_credentials_registry_registry_provider_play_services/README.chromium +++ b/third_party/androidx/committed/libs/androidx_credentials_registry_registry_provider_play_services/README.chromium
@@ -1,6 +1,6 @@ Name: androidx.credentials.registry:registry-provider-play-services Short Name: registry-provider-play-services -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/credentials/registry/registry-provider-play-services/1.0.0-SNAPSHOT/registry-provider-play-services-1.0.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/credentials/registry/registry-provider-play-services/1.0.0-SNAPSHOT/registry-provider-play-services-1.0.0-20260228.045313-1.aar Version: 1.0.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_cursoradapter_cursoradapter/README.chromium b/third_party/androidx/committed/libs/androidx_cursoradapter_cursoradapter/README.chromium index fe8d9088..e5cc1f42 100644 --- a/third_party/androidx/committed/libs/androidx_cursoradapter_cursoradapter/README.chromium +++ b/third_party/androidx/committed/libs/androidx_cursoradapter_cursoradapter/README.chromium
@@ -1,6 +1,6 @@ Name: Cursor Adapter Short Name: cursoradapter -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/cursoradapter/cursoradapter/1.1.0-SNAPSHOT/cursoradapter-1.1.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/cursoradapter/cursoradapter/1.1.0-SNAPSHOT/cursoradapter-1.1.0-20260228.045313-1.aar Version: 1.1.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_datastore_datastore_android/README.chromium b/third_party/androidx/committed/libs/androidx_datastore_datastore_android/README.chromium index 16ab248..6024d19 100644 --- a/third_party/androidx/committed/libs/androidx_datastore_datastore_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_datastore_datastore_android/README.chromium
@@ -1,6 +1,6 @@ Name: DataStore Short Name: datastore-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/datastore/datastore-android/1.3.0-SNAPSHOT/datastore-android-1.3.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/datastore/datastore-android/1.3.0-SNAPSHOT/datastore-android-1.3.0-20260228.045313-1.aar Version: 1.3.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_datastore_datastore_core_android/README.chromium b/third_party/androidx/committed/libs/androidx_datastore_datastore_core_android/README.chromium index cc6f0efa..96eefed 100644 --- a/third_party/androidx/committed/libs/androidx_datastore_datastore_core_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_datastore_datastore_core_android/README.chromium
@@ -1,6 +1,6 @@ Name: DataStore Core Short Name: datastore-core-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/datastore/datastore-core-android/1.3.0-SNAPSHOT/datastore-core-android-1.3.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/datastore/datastore-core-android/1.3.0-SNAPSHOT/datastore-core-android-1.3.0-20260228.045313-1.aar Version: 1.3.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_datastore_datastore_core_okio_jvm/README.chromium b/third_party/androidx/committed/libs/androidx_datastore_datastore_core_okio_jvm/README.chromium index dc14eae2..2fb4a98c 100644 --- a/third_party/androidx/committed/libs/androidx_datastore_datastore_core_okio_jvm/README.chromium +++ b/third_party/androidx/committed/libs/androidx_datastore_datastore_core_okio_jvm/README.chromium
@@ -1,6 +1,6 @@ Name: DataStore Core Okio Short Name: datastore-core-okio-jvm -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/datastore/datastore-core-okio-jvm/1.3.0-SNAPSHOT/datastore-core-okio-jvm-1.3.0-20260227.122158-1.jar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/datastore/datastore-core-okio-jvm/1.3.0-SNAPSHOT/datastore-core-okio-jvm-1.3.0-20260228.045313-1.jar Version: 1.3.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_android/README.chromium b/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_android/README.chromium index 34c41b6..c2c000ad 100644 --- a/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_android/README.chromium
@@ -1,6 +1,6 @@ Name: Preferences DataStore Short Name: datastore-preferences-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/datastore/datastore-preferences-android/1.3.0-SNAPSHOT/datastore-preferences-android-1.3.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/datastore/datastore-preferences-android/1.3.0-SNAPSHOT/datastore-preferences-android-1.3.0-20260228.045313-1.aar Version: 1.3.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_core_android/README.chromium b/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_core_android/README.chromium index 037aeb5c..f4c4f461 100644 --- a/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_core_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_core_android/README.chromium
@@ -1,6 +1,6 @@ Name: Preferences DataStore Core Short Name: datastore-preferences-core-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/datastore/datastore-preferences-core-android/1.3.0-SNAPSHOT/datastore-preferences-core-android-1.3.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/datastore/datastore-preferences-core-android/1.3.0-SNAPSHOT/datastore-preferences-core-android-1.3.0-20260228.045313-1.aar Version: 1.3.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_external_protobuf/README.chromium b/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_external_protobuf/README.chromium index 62024a5..bfde8b8d 100644 --- a/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_external_protobuf/README.chromium +++ b/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_external_protobuf/README.chromium
@@ -1,6 +1,6 @@ Name: Preferences External Protobuf Short Name: datastore-preferences-external-protobuf -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/datastore/datastore-preferences-external-protobuf/1.3.0-SNAPSHOT/datastore-preferences-external-protobuf-1.3.0-20260227.122158-1.jar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/datastore/datastore-preferences-external-protobuf/1.3.0-SNAPSHOT/datastore-preferences-external-protobuf-1.3.0-20260228.045313-1.jar Version: 1.3.0-SNAPSHOT Update Mechanism: Autoroll License: BSD-3-Clause
diff --git a/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_proto/README.chromium b/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_proto/README.chromium index a329370a..79ab4f5 100644 --- a/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_proto/README.chromium +++ b/third_party/androidx/committed/libs/androidx_datastore_datastore_preferences_proto/README.chromium
@@ -1,6 +1,6 @@ Name: Preferences DataStore Proto Short Name: datastore-preferences-proto -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/datastore/datastore-preferences-proto/1.3.0-SNAPSHOT/datastore-preferences-proto-1.3.0-20260227.122158-1.jar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/datastore/datastore-preferences-proto/1.3.0-SNAPSHOT/datastore-preferences-proto-1.3.0-20260228.045313-1.jar Version: 1.3.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_drawerlayout_drawerlayout/README.chromium b/third_party/androidx/committed/libs/androidx_drawerlayout_drawerlayout/README.chromium index 1d0e26d9..dde5a7a37 100644 --- a/third_party/androidx/committed/libs/androidx_drawerlayout_drawerlayout/README.chromium +++ b/third_party/androidx/committed/libs/androidx_drawerlayout_drawerlayout/README.chromium
@@ -1,6 +1,6 @@ Name: Drawer Layout Short Name: drawerlayout -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/drawerlayout/drawerlayout/1.3.0-SNAPSHOT/drawerlayout-1.3.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/drawerlayout/drawerlayout/1.3.0-SNAPSHOT/drawerlayout-1.3.0-20260228.045313-1.aar Version: 1.3.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_fragment_fragment/README.chromium b/third_party/androidx/committed/libs/androidx_fragment_fragment/README.chromium index 473d8ff..d8056f83 100644 --- a/third_party/androidx/committed/libs/androidx_fragment_fragment/README.chromium +++ b/third_party/androidx/committed/libs/androidx_fragment_fragment/README.chromium
@@ -1,6 +1,6 @@ Name: fragment Short Name: fragment -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/fragment/fragment/1.9.0-SNAPSHOT/fragment-1.9.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/fragment/fragment/1.9.0-SNAPSHOT/fragment-1.9.0-20260228.045313-1.aar Version: 1.9.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_fragment_fragment_compose/README.chromium b/third_party/androidx/committed/libs/androidx_fragment_fragment_compose/README.chromium index cb194aa5..f763c20 100644 --- a/third_party/androidx/committed/libs/androidx_fragment_fragment_compose/README.chromium +++ b/third_party/androidx/committed/libs/androidx_fragment_fragment_compose/README.chromium
@@ -1,6 +1,6 @@ Name: Fragment Compose Short Name: fragment-compose -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/fragment/fragment-compose/1.9.0-SNAPSHOT/fragment-compose-1.9.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/fragment/fragment-compose/1.9.0-SNAPSHOT/fragment-compose-1.9.0-20260228.045313-1.aar Version: 1.9.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_fragment_fragment_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_fragment_fragment_ktx/README.chromium index 8faf422..ad53655 100644 --- a/third_party/androidx/committed/libs/androidx_fragment_fragment_ktx/README.chromium +++ b/third_party/androidx/committed/libs/androidx_fragment_fragment_ktx/README.chromium
@@ -1,6 +1,6 @@ Name: Fragment Kotlin Extensions Short Name: fragment-ktx -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/fragment/fragment-ktx/1.9.0-SNAPSHOT/fragment-ktx-1.9.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/fragment/fragment-ktx/1.9.0-SNAPSHOT/fragment-ktx-1.9.0-20260228.045313-1.aar Version: 1.9.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_fragment_fragment_testing/README.chromium b/third_party/androidx/committed/libs/androidx_fragment_fragment_testing/README.chromium index b98ad64..bc0a72f 100644 --- a/third_party/androidx/committed/libs/androidx_fragment_fragment_testing/README.chromium +++ b/third_party/androidx/committed/libs/androidx_fragment_fragment_testing/README.chromium
@@ -1,6 +1,6 @@ Name: Fragment Testing Extensions Short Name: fragment-testing -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/fragment/fragment-testing/1.9.0-SNAPSHOT/fragment-testing-1.9.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/fragment/fragment-testing/1.9.0-SNAPSHOT/fragment-testing-1.9.0-20260228.045313-1.aar Version: 1.9.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_fragment_fragment_testing_manifest/README.chromium b/third_party/androidx/committed/libs/androidx_fragment_fragment_testing_manifest/README.chromium index 0c90116..9042eaf 100644 --- a/third_party/androidx/committed/libs/androidx_fragment_fragment_testing_manifest/README.chromium +++ b/third_party/androidx/committed/libs/androidx_fragment_fragment_testing_manifest/README.chromium
@@ -1,6 +1,6 @@ Name: Fragment Testing Manifest dependency Short Name: fragment-testing-manifest -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/fragment/fragment-testing-manifest/1.9.0-SNAPSHOT/fragment-testing-manifest-1.9.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/fragment/fragment-testing-manifest/1.9.0-SNAPSHOT/fragment-testing-manifest-1.9.0-20260228.045313-1.aar Version: 1.9.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_graphics_graphics_path/README.chromium b/third_party/androidx/committed/libs/androidx_graphics_graphics_path/README.chromium index ed01997..1b5e113b 100644 --- a/third_party/androidx/committed/libs/androidx_graphics_graphics_path/README.chromium +++ b/third_party/androidx/committed/libs/androidx_graphics_graphics_path/README.chromium
@@ -1,6 +1,6 @@ Name: Android Graphics Path Short Name: graphics-path -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/graphics/graphics-path/1.1.0-SNAPSHOT/graphics-path-1.1.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/graphics/graphics-path/1.1.0-SNAPSHOT/graphics-path-1.1.0-20260228.045313-1.aar Version: 1.1.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_interpolator_interpolator/README.chromium b/third_party/androidx/committed/libs/androidx_interpolator_interpolator/README.chromium index 169aa59..63c96de 100644 --- a/third_party/androidx/committed/libs/androidx_interpolator_interpolator/README.chromium +++ b/third_party/androidx/committed/libs/androidx_interpolator_interpolator/README.chromium
@@ -1,6 +1,6 @@ Name: Interpolators Short Name: interpolator -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/interpolator/interpolator/1.1.0-SNAPSHOT/interpolator-1.1.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/interpolator/interpolator/1.1.0-SNAPSHOT/interpolator-1.1.0-20260228.045313-1.aar Version: 1.1.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_leanback_leanback/README.chromium b/third_party/androidx/committed/libs/androidx_leanback_leanback/README.chromium index b5073d75..3b65f4c 100644 --- a/third_party/androidx/committed/libs/androidx_leanback_leanback/README.chromium +++ b/third_party/androidx/committed/libs/androidx_leanback_leanback/README.chromium
@@ -1,6 +1,6 @@ Name: Leanback Short Name: leanback -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/leanback/leanback/1.3.0-SNAPSHOT/leanback-1.3.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/leanback/leanback/1.3.0-SNAPSHOT/leanback-1.3.0-20260228.045313-1.aar Version: 1.3.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_leanback_leanback_grid/README.chromium b/third_party/androidx/committed/libs/androidx_leanback_leanback_grid/README.chromium index 32a5abe1..653e224 100644 --- a/third_party/androidx/committed/libs/androidx_leanback_leanback_grid/README.chromium +++ b/third_party/androidx/committed/libs/androidx_leanback_leanback_grid/README.chromium
@@ -1,6 +1,6 @@ Name: Leanback Grid Short Name: leanback-grid -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/leanback/leanback-grid/1.1.0-SNAPSHOT/leanback-grid-1.1.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/leanback/leanback-grid/1.1.0-SNAPSHOT/leanback-grid-1.1.0-20260228.045313-1.aar Version: 1.1.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_common_java8/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_common_java8/README.chromium index d0fbfe5..a5c188b 100644 --- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_common_java8/README.chromium +++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_common_java8/README.chromium
@@ -1,6 +1,6 @@ Name: Lifecycle-Common for Java 8 Short Name: lifecycle-common-java8 -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/lifecycle/lifecycle-common-java8/2.11.0-SNAPSHOT/lifecycle-common-java8-2.11.0-20260227.122158-1.jar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/lifecycle/lifecycle-common-java8/2.11.0-SNAPSHOT/lifecycle-common-java8-2.11.0-20260228.045313-1.jar Version: 2.11.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_common_jvm/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_common_jvm/README.chromium index 4772469..64b35f4 100644 --- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_common_jvm/README.chromium +++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_common_jvm/README.chromium
@@ -1,6 +1,6 @@ Name: Lifecycle-Common Short Name: lifecycle-common-jvm -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/lifecycle/lifecycle-common-jvm/2.11.0-SNAPSHOT/lifecycle-common-jvm-2.11.0-20260227.122158-1.jar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/lifecycle/lifecycle-common-jvm/2.11.0-SNAPSHOT/lifecycle-common-jvm-2.11.0-20260228.045313-1.jar Version: 2.11.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata/README.chromium index 202c6cba..681af8b 100644 --- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata/README.chromium +++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata/README.chromium
@@ -1,6 +1,6 @@ Name: Lifecycle LiveData Short Name: lifecycle-livedata -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/lifecycle/lifecycle-livedata/2.11.0-SNAPSHOT/lifecycle-livedata-2.11.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/lifecycle/lifecycle-livedata/2.11.0-SNAPSHOT/lifecycle-livedata-2.11.0-20260228.045313-1.aar Version: 2.11.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_core/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_core/README.chromium index 583b971f..c237bd6 100644 --- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_core/README.chromium +++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_core/README.chromium
@@ -1,6 +1,6 @@ Name: Lifecycle LiveData Core Short Name: lifecycle-livedata-core -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/lifecycle/lifecycle-livedata-core/2.11.0-SNAPSHOT/lifecycle-livedata-core-2.11.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/lifecycle/lifecycle-livedata-core/2.11.0-SNAPSHOT/lifecycle-livedata-core-2.11.0-20260228.045313-1.aar Version: 2.11.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_core_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_core_ktx/README.chromium index b35ccd6..adc9ca3 100644 --- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_core_ktx/README.chromium +++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_core_ktx/README.chromium
@@ -1,6 +1,6 @@ Name: LiveData Core Kotlin Extensions Short Name: lifecycle-livedata-core-ktx -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/lifecycle/lifecycle-livedata-core-ktx/2.11.0-SNAPSHOT/lifecycle-livedata-core-ktx-2.11.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/lifecycle/lifecycle-livedata-core-ktx/2.11.0-SNAPSHOT/lifecycle-livedata-core-ktx-2.11.0-20260228.045313-1.aar Version: 2.11.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_ktx/README.chromium index 8f24fa2..ab75327 100644 --- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_ktx/README.chromium +++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_livedata_ktx/README.chromium
@@ -1,6 +1,6 @@ Name: LiveData Kotlin Extensions Short Name: lifecycle-livedata-ktx -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/lifecycle/lifecycle-livedata-ktx/2.11.0-SNAPSHOT/lifecycle-livedata-ktx-2.11.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/lifecycle/lifecycle-livedata-ktx/2.11.0-SNAPSHOT/lifecycle-livedata-ktx-2.11.0-20260228.045313-1.aar Version: 2.11.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_process/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_process/README.chromium index a2b65c1d..f5638c7a9 100644 --- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_process/README.chromium +++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_process/README.chromium
@@ -1,6 +1,6 @@ Name: Lifecycle Process Short Name: lifecycle-process -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/lifecycle/lifecycle-process/2.11.0-SNAPSHOT/lifecycle-process-2.11.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/lifecycle/lifecycle-process/2.11.0-SNAPSHOT/lifecycle-process-2.11.0-20260228.045313-1.aar Version: 2.11.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_android/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_android/README.chromium index 1b28cca..535f0eb 100644 --- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_android/README.chromium
@@ -1,6 +1,6 @@ Name: Lifecycle Runtime Short Name: lifecycle-runtime-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/lifecycle/lifecycle-runtime-android/2.11.0-SNAPSHOT/lifecycle-runtime-android-2.11.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/lifecycle/lifecycle-runtime-android/2.11.0-SNAPSHOT/lifecycle-runtime-android-2.11.0-20260228.045313-1.aar Version: 2.11.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_compose_android/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_compose_android/README.chromium index ad1a4b52..d9a89df6 100644 --- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_compose_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_compose_android/README.chromium
@@ -1,6 +1,6 @@ Name: Lifecycle Runtime Compose Short Name: lifecycle-runtime-compose-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/lifecycle/lifecycle-runtime-compose-android/2.11.0-SNAPSHOT/lifecycle-runtime-compose-android-2.11.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/lifecycle/lifecycle-runtime-compose-android/2.11.0-SNAPSHOT/lifecycle-runtime-compose-android-2.11.0-20260228.045313-1.aar Version: 2.11.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_ktx_android/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_ktx_android/README.chromium index f5e3cc9..6301658 100644 --- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_ktx_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_runtime_ktx_android/README.chromium
@@ -1,6 +1,6 @@ Name: Lifecycle Kotlin Extensions Short Name: lifecycle-runtime-ktx-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/lifecycle/lifecycle-runtime-ktx-android/2.11.0-SNAPSHOT/lifecycle-runtime-ktx-android-2.11.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/lifecycle/lifecycle-runtime-ktx-android/2.11.0-SNAPSHOT/lifecycle-runtime-ktx-android-2.11.0-20260228.045313-1.aar Version: 2.11.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_service/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_service/README.chromium index 517e968..8a7adb56 100644 --- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_service/README.chromium +++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_service/README.chromium
@@ -1,6 +1,6 @@ Name: Lifecycle Service Short Name: lifecycle-service -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/lifecycle/lifecycle-service/2.11.0-SNAPSHOT/lifecycle-service-2.11.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/lifecycle/lifecycle-service/2.11.0-SNAPSHOT/lifecycle-service-2.11.0-20260228.045313-1.aar Version: 2.11.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_android/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_android/README.chromium index 4928c26..bb95e37 100644 --- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_android/README.chromium
@@ -1,6 +1,6 @@ Name: Lifecycle ViewModel Short Name: lifecycle-viewmodel-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/lifecycle/lifecycle-viewmodel-android/2.11.0-SNAPSHOT/lifecycle-viewmodel-android-2.11.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/lifecycle/lifecycle-viewmodel-android/2.11.0-SNAPSHOT/lifecycle-viewmodel-android-2.11.0-20260228.045313-1.aar Version: 2.11.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_compose_android/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_compose_android/README.chromium index f9836a0..0d8d9b3 100644 --- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_compose_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_compose_android/README.chromium
@@ -1,6 +1,6 @@ Name: Lifecycle ViewModel Compose Short Name: lifecycle-viewmodel-compose-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/lifecycle/lifecycle-viewmodel-compose-android/2.11.0-SNAPSHOT/lifecycle-viewmodel-compose-android-2.11.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/lifecycle/lifecycle-viewmodel-compose-android/2.11.0-SNAPSHOT/lifecycle-viewmodel-compose-android-2.11.0-20260228.045313-1.aar Version: 2.11.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_ktx/README.chromium index a3f585f..4fb2963 100644 --- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_ktx/README.chromium +++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_ktx/README.chromium
@@ -1,6 +1,6 @@ Name: Lifecycle ViewModel Kotlin Extensions Short Name: lifecycle-viewmodel-ktx -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/lifecycle/lifecycle-viewmodel-ktx/2.11.0-SNAPSHOT/lifecycle-viewmodel-ktx-2.11.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/lifecycle/lifecycle-viewmodel-ktx/2.11.0-SNAPSHOT/lifecycle-viewmodel-ktx-2.11.0-20260228.045313-1.aar Version: 2.11.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_savedstate_android/README.chromium b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_savedstate_android/README.chromium index 5bbcbaa3..5d82b251 100644 --- a/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_savedstate_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_lifecycle_lifecycle_viewmodel_savedstate_android/README.chromium
@@ -1,6 +1,6 @@ Name: Lifecycle ViewModel with SavedState Short Name: lifecycle-viewmodel-savedstate-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/lifecycle/lifecycle-viewmodel-savedstate-android/2.11.0-SNAPSHOT/lifecycle-viewmodel-savedstate-android-2.11.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/lifecycle/lifecycle-viewmodel-savedstate-android/2.11.0-SNAPSHOT/lifecycle-viewmodel-savedstate-android-2.11.0-20260228.045313-1.aar Version: 2.11.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_loader_loader/README.chromium b/third_party/androidx/committed/libs/androidx_loader_loader/README.chromium index ce136979..2100716 100644 --- a/third_party/androidx/committed/libs/androidx_loader_loader/README.chromium +++ b/third_party/androidx/committed/libs/androidx_loader_loader/README.chromium
@@ -1,6 +1,6 @@ Name: loader Short Name: loader -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/loader/loader/1.2.0-SNAPSHOT/loader-1.2.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/loader/loader/1.2.0-SNAPSHOT/loader-1.2.0-20260228.045313-1.aar Version: 1.2.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_media_media/README.chromium b/third_party/androidx/committed/libs/androidx_media_media/README.chromium index c1f0040..62c711af 100644 --- a/third_party/androidx/committed/libs/androidx_media_media/README.chromium +++ b/third_party/androidx/committed/libs/androidx_media_media/README.chromium
@@ -1,6 +1,6 @@ Name: Media Short Name: media -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/media/media/1.8.0-SNAPSHOT/media-1.8.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/media/media/1.8.0-SNAPSHOT/media-1.8.0-20260228.045313-1.aar Version: 1.8.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_mediarouter_mediarouter/README.chromium b/third_party/androidx/committed/libs/androidx_mediarouter_mediarouter/README.chromium index dedc432..363338b 100644 --- a/third_party/androidx/committed/libs/androidx_mediarouter_mediarouter/README.chromium +++ b/third_party/androidx/committed/libs/androidx_mediarouter_mediarouter/README.chromium
@@ -1,6 +1,6 @@ Name: MediaRouter Short Name: mediarouter -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/mediarouter/mediarouter/1.9.0-SNAPSHOT/mediarouter-1.9.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/mediarouter/mediarouter/1.9.0-SNAPSHOT/mediarouter-1.9.0-20260228.045313-1.aar Version: 1.9.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_navigation_navigation_common_android/README.chromium b/third_party/androidx/committed/libs/androidx_navigation_navigation_common_android/README.chromium index 8aa9f12..7d1f8047 100644 --- a/third_party/androidx/committed/libs/androidx_navigation_navigation_common_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_navigation_navigation_common_android/README.chromium
@@ -1,6 +1,6 @@ Name: Navigation Common Short Name: navigation-common-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/navigation/navigation-common-android/2.10.0-SNAPSHOT/navigation-common-android-2.10.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/navigation/navigation-common-android/2.10.0-SNAPSHOT/navigation-common-android-2.10.0-20260228.045313-1.aar Version: 2.10.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_navigation_navigation_compose_android/README.chromium b/third_party/androidx/committed/libs/androidx_navigation_navigation_compose_android/README.chromium index cb089de..2b2df070 100644 --- a/third_party/androidx/committed/libs/androidx_navigation_navigation_compose_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_navigation_navigation_compose_android/README.chromium
@@ -1,6 +1,6 @@ Name: Compose Navigation Short Name: navigation-compose-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/navigation/navigation-compose-android/2.10.0-SNAPSHOT/navigation-compose-android-2.10.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/navigation/navigation-compose-android/2.10.0-SNAPSHOT/navigation-compose-android-2.10.0-20260228.045313-1.aar Version: 2.10.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_navigation_navigation_runtime_android/README.chromium b/third_party/androidx/committed/libs/androidx_navigation_navigation_runtime_android/README.chromium index 1d4ec806..1c753f3a 100644 --- a/third_party/androidx/committed/libs/androidx_navigation_navigation_runtime_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_navigation_navigation_runtime_android/README.chromium
@@ -1,6 +1,6 @@ Name: Navigation Runtime Short Name: navigation-runtime-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/navigation/navigation-runtime-android/2.10.0-SNAPSHOT/navigation-runtime-android-2.10.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/navigation/navigation-runtime-android/2.10.0-SNAPSHOT/navigation-runtime-android-2.10.0-20260228.045313-1.aar Version: 2.10.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_android/README.chromium b/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_android/README.chromium index f8950dc..aaeff09f 100644 --- a/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_android/README.chromium
@@ -1,6 +1,6 @@ Name: Navigation Event Short Name: navigationevent-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/navigationevent/navigationevent-android/1.1.0-SNAPSHOT/navigationevent-android-1.1.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/navigationevent/navigationevent-android/1.1.0-SNAPSHOT/navigationevent-android-1.1.0-20260228.045313-1.aar Version: 1.1.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_compose_android/README.chromium b/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_compose_android/README.chromium index 966de4c..2b98662 100644 --- a/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_compose_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_navigationevent_navigationevent_compose_android/README.chromium
@@ -1,6 +1,6 @@ Name: NavigationEvent Compose Short Name: navigationevent-compose-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/navigationevent/navigationevent-compose-android/1.1.0-SNAPSHOT/navigationevent-compose-android-1.1.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/navigationevent/navigationevent-compose-android/1.1.0-SNAPSHOT/navigationevent-compose-android-1.1.0-20260228.045313-1.aar Version: 1.1.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_paging_paging_common_android/README.chromium b/third_party/androidx/committed/libs/androidx_paging_paging_common_android/README.chromium index 2515271..e8c4f0d 100644 --- a/third_party/androidx/committed/libs/androidx_paging_paging_common_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_paging_paging_common_android/README.chromium
@@ -1,6 +1,6 @@ Name: Paging-Common Short Name: paging-common-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/paging/paging-common-android/3.5.0-SNAPSHOT/paging-common-android-3.5.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/paging/paging-common-android/3.5.0-SNAPSHOT/paging-common-android-3.5.0-20260228.045313-1.aar Version: 3.5.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_paging_paging_common_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_paging_paging_common_ktx/README.chromium index 7c9e09e..35427a5 100644 --- a/third_party/androidx/committed/libs/androidx_paging_paging_common_ktx/README.chromium +++ b/third_party/androidx/committed/libs/androidx_paging_paging_common_ktx/README.chromium
@@ -1,6 +1,6 @@ Name: Paging-Common Kotlin Extensions Short Name: paging-common-ktx -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/paging/paging-common-ktx/3.5.0-SNAPSHOT/paging-common-ktx-3.5.0-20260227.122158-1.jar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/paging/paging-common-ktx/3.5.0-SNAPSHOT/paging-common-ktx-3.5.0-20260228.045313-1.jar Version: 3.5.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_paging_paging_compose_android/README.chromium b/third_party/androidx/committed/libs/androidx_paging_paging_compose_android/README.chromium index 12a54d7..9c09d95c 100644 --- a/third_party/androidx/committed/libs/androidx_paging_paging_compose_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_paging_paging_compose_android/README.chromium
@@ -1,6 +1,6 @@ Name: Paging-Compose Short Name: paging-compose-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/paging/paging-compose-android/3.5.0-SNAPSHOT/paging-compose-android-3.5.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/paging/paging-compose-android/3.5.0-SNAPSHOT/paging-compose-android-3.5.0-20260228.045313-1.aar Version: 3.5.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_paging_paging_runtime/README.chromium b/third_party/androidx/committed/libs/androidx_paging_paging_runtime/README.chromium index 2c36aaa5..71f395ff 100644 --- a/third_party/androidx/committed/libs/androidx_paging_paging_runtime/README.chromium +++ b/third_party/androidx/committed/libs/androidx_paging_paging_runtime/README.chromium
@@ -1,6 +1,6 @@ Name: Paging-Runtime Short Name: paging-runtime -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/paging/paging-runtime/3.5.0-SNAPSHOT/paging-runtime-3.5.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/paging/paging-runtime/3.5.0-SNAPSHOT/paging-runtime-3.5.0-20260228.045313-1.aar Version: 3.5.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_palette_palette/README.chromium b/third_party/androidx/committed/libs/androidx_palette_palette/README.chromium index f4c68ea..7dc4700 100644 --- a/third_party/androidx/committed/libs/androidx_palette_palette/README.chromium +++ b/third_party/androidx/committed/libs/androidx_palette_palette/README.chromium
@@ -1,6 +1,6 @@ Name: Palette Short Name: palette -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/palette/palette/1.1.0-SNAPSHOT/palette-1.1.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/palette/palette/1.1.0-SNAPSHOT/palette-1.1.0-20260228.045313-1.aar Version: 1.1.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_pdf_pdf_document_service/README.chromium b/third_party/androidx/committed/libs/androidx_pdf_pdf_document_service/README.chromium index 0f321ea..2cd47c6 100644 --- a/third_party/androidx/committed/libs/androidx_pdf_pdf_document_service/README.chromium +++ b/third_party/androidx/committed/libs/androidx_pdf_pdf_document_service/README.chromium
@@ -1,6 +1,6 @@ Name: androidx.pdf:pdf-document-service Short Name: pdf-document-service -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/pdf/pdf-document-service/1.0.0-SNAPSHOT/pdf-document-service-1.0.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/pdf/pdf-document-service/1.0.0-SNAPSHOT/pdf-document-service-1.0.0-20260228.045313-1.aar Version: 1.0.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_pdf_pdf_viewer/README.chromium b/third_party/androidx/committed/libs/androidx_pdf_pdf_viewer/README.chromium index cef4650..82998ef 100644 --- a/third_party/androidx/committed/libs/androidx_pdf_pdf_viewer/README.chromium +++ b/third_party/androidx/committed/libs/androidx_pdf_pdf_viewer/README.chromium
@@ -1,6 +1,6 @@ Name: androidx.pdf:pdf-viewer Short Name: pdf-viewer -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/pdf/pdf-viewer/1.0.0-SNAPSHOT/pdf-viewer-1.0.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/pdf/pdf-viewer/1.0.0-SNAPSHOT/pdf-viewer-1.0.0-20260228.045313-1.aar Version: 1.0.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_pdf_pdf_viewer_fragment/README.chromium b/third_party/androidx/committed/libs/androidx_pdf_pdf_viewer_fragment/README.chromium index e544f63..b9350af 100644 --- a/third_party/androidx/committed/libs/androidx_pdf_pdf_viewer_fragment/README.chromium +++ b/third_party/androidx/committed/libs/androidx_pdf_pdf_viewer_fragment/README.chromium
@@ -1,6 +1,6 @@ Name: androidx.pdf:pdf-viewer-fragment Short Name: pdf-viewer-fragment -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/pdf/pdf-viewer-fragment/1.0.0-SNAPSHOT/pdf-viewer-fragment-1.0.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/pdf/pdf-viewer-fragment/1.0.0-SNAPSHOT/pdf-viewer-fragment-1.0.0-20260228.045313-1.aar Version: 1.0.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_preference_preference/README.chromium b/third_party/androidx/committed/libs/androidx_preference_preference/README.chromium index aa69d26..3d16d0e 100644 --- a/third_party/androidx/committed/libs/androidx_preference_preference/README.chromium +++ b/third_party/androidx/committed/libs/androidx_preference_preference/README.chromium
@@ -1,6 +1,6 @@ Name: Preference Short Name: preference -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/preference/preference/1.3.0-SNAPSHOT/preference-1.3.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/preference/preference/1.3.0-SNAPSHOT/preference-1.3.0-20260228.045313-1.aar Version: 1.3.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_profileinstaller_profileinstaller/README.chromium b/third_party/androidx/committed/libs/androidx_profileinstaller_profileinstaller/README.chromium index ac15252f..9f41d31 100644 --- a/third_party/androidx/committed/libs/androidx_profileinstaller_profileinstaller/README.chromium +++ b/third_party/androidx/committed/libs/androidx_profileinstaller_profileinstaller/README.chromium
@@ -1,6 +1,6 @@ Name: Profile Installer Short Name: profileinstaller -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/profileinstaller/profileinstaller/1.5.0-SNAPSHOT/profileinstaller-1.5.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/profileinstaller/profileinstaller/1.5.0-SNAPSHOT/profileinstaller-1.5.0-20260228.045313-1.aar Version: 1.5.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_recyclerview_recyclerview/README.chromium b/third_party/androidx/committed/libs/androidx_recyclerview_recyclerview/README.chromium index 00f0870..b0d1245 100644 --- a/third_party/androidx/committed/libs/androidx_recyclerview_recyclerview/README.chromium +++ b/third_party/androidx/committed/libs/androidx_recyclerview_recyclerview/README.chromium
@@ -1,6 +1,6 @@ Name: RecyclerView Short Name: recyclerview -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/recyclerview/recyclerview/1.5.0-SNAPSHOT/recyclerview-1.5.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/recyclerview/recyclerview/1.5.0-SNAPSHOT/recyclerview-1.5.0-20260228.045313-1.aar Version: 1.5.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_resourceinspection_resourceinspection_annotation/README.chromium b/third_party/androidx/committed/libs/androidx_resourceinspection_resourceinspection_annotation/README.chromium index dca27a3..d924f06 100644 --- a/third_party/androidx/committed/libs/androidx_resourceinspection_resourceinspection_annotation/README.chromium +++ b/third_party/androidx/committed/libs/androidx_resourceinspection_resourceinspection_annotation/README.chromium
@@ -1,6 +1,6 @@ Name: Resource Inspection - Annotations Short Name: resourceinspection-annotation -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/resourceinspection/resourceinspection-annotation/1.1.0-SNAPSHOT/resourceinspection-annotation-1.1.0-20260227.122158-1.jar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/resourceinspection/resourceinspection-annotation/1.1.0-SNAPSHOT/resourceinspection-annotation-1.1.0-20260228.045313-1.jar Version: 1.1.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_savedstate_savedstate_android/README.chromium b/third_party/androidx/committed/libs/androidx_savedstate_savedstate_android/README.chromium index fbd24699..483c69c 100644 --- a/third_party/androidx/committed/libs/androidx_savedstate_savedstate_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_savedstate_savedstate_android/README.chromium
@@ -1,6 +1,6 @@ Name: Saved State Short Name: savedstate-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/savedstate/savedstate-android/1.5.0-SNAPSHOT/savedstate-android-1.5.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/savedstate/savedstate-android/1.5.0-SNAPSHOT/savedstate-android-1.5.0-20260228.045313-1.aar Version: 1.5.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_savedstate_savedstate_compose_android/README.chromium b/third_party/androidx/committed/libs/androidx_savedstate_savedstate_compose_android/README.chromium index aeb7bcb..86fe907 100644 --- a/third_party/androidx/committed/libs/androidx_savedstate_savedstate_compose_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_savedstate_savedstate_compose_android/README.chromium
@@ -1,6 +1,6 @@ Name: Saved State Compose Short Name: savedstate-compose-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/savedstate/savedstate-compose-android/1.5.0-SNAPSHOT/savedstate-compose-android-1.5.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/savedstate/savedstate-compose-android/1.5.0-SNAPSHOT/savedstate-compose-android-1.5.0-20260228.045313-1.aar Version: 1.5.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_savedstate_savedstate_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_savedstate_savedstate_ktx/README.chromium index 6d9a013..2d084eb 100644 --- a/third_party/androidx/committed/libs/androidx_savedstate_savedstate_ktx/README.chromium +++ b/third_party/androidx/committed/libs/androidx_savedstate_savedstate_ktx/README.chromium
@@ -1,6 +1,6 @@ Name: SavedState Kotlin Extensions Short Name: savedstate-ktx -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/savedstate/savedstate-ktx/1.5.0-SNAPSHOT/savedstate-ktx-1.5.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/savedstate/savedstate-ktx/1.5.0-SNAPSHOT/savedstate-ktx-1.5.0-20260228.045313-1.aar Version: 1.5.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_slidingpanelayout_slidingpanelayout/README.chromium b/third_party/androidx/committed/libs/androidx_slidingpanelayout_slidingpanelayout/README.chromium index aa87749a..ca7adaa7 100644 --- a/third_party/androidx/committed/libs/androidx_slidingpanelayout_slidingpanelayout/README.chromium +++ b/third_party/androidx/committed/libs/androidx_slidingpanelayout_slidingpanelayout/README.chromium
@@ -1,6 +1,6 @@ Name: Sliding Pane Layout Short Name: slidingpanelayout -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/slidingpanelayout/slidingpanelayout/1.3.0-SNAPSHOT/slidingpanelayout-1.3.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/slidingpanelayout/slidingpanelayout/1.3.0-SNAPSHOT/slidingpanelayout-1.3.0-20260228.045313-1.aar Version: 1.3.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_sqlite_sqlite_android/README.chromium b/third_party/androidx/committed/libs/androidx_sqlite_sqlite_android/README.chromium index 504761f..6c87446 100644 --- a/third_party/androidx/committed/libs/androidx_sqlite_sqlite_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_sqlite_sqlite_android/README.chromium
@@ -1,6 +1,6 @@ Name: SQLite Short Name: sqlite-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/sqlite/sqlite-android/2.7.0-SNAPSHOT/sqlite-android-2.7.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/sqlite/sqlite-android/2.7.0-SNAPSHOT/sqlite-android-2.7.0-20260228.045313-1.aar Version: 2.7.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_sqlite_sqlite_framework_android/README.chromium b/third_party/androidx/committed/libs/androidx_sqlite_sqlite_framework_android/README.chromium index cbf1064..804e7d978 100644 --- a/third_party/androidx/committed/libs/androidx_sqlite_sqlite_framework_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_sqlite_sqlite_framework_android/README.chromium
@@ -1,6 +1,6 @@ Name: SQLite Framework Integration Short Name: sqlite-framework-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/sqlite/sqlite-framework-android/2.7.0-SNAPSHOT/sqlite-framework-android-2.7.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/sqlite/sqlite-framework-android/2.7.0-SNAPSHOT/sqlite-framework-android-2.7.0-20260228.045313-1.aar Version: 2.7.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_test_uiautomator_uiautomator/README.chromium b/third_party/androidx/committed/libs/androidx_test_uiautomator_uiautomator/README.chromium index 433e14d..de7ace30 100644 --- a/third_party/androidx/committed/libs/androidx_test_uiautomator_uiautomator/README.chromium +++ b/third_party/androidx/committed/libs/androidx_test_uiautomator_uiautomator/README.chromium
@@ -1,6 +1,6 @@ Name: UIAutomator Short Name: uiautomator -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/test/uiautomator/uiautomator/2.4.0-SNAPSHOT/uiautomator-2.4.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/test/uiautomator/uiautomator/2.4.0-SNAPSHOT/uiautomator-2.4.0-20260228.045313-1.aar Version: 2.4.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_test_uiautomator_uiautomator_shell_android/README.chromium b/third_party/androidx/committed/libs/androidx_test_uiautomator_uiautomator_shell_android/README.chromium index b5545532..ff367ece 100644 --- a/third_party/androidx/committed/libs/androidx_test_uiautomator_uiautomator_shell_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_test_uiautomator_uiautomator_shell_android/README.chromium
@@ -1,6 +1,6 @@ Name: Shell Short Name: uiautomator-shell-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/test/uiautomator/uiautomator-shell-android/2.4.0-SNAPSHOT/uiautomator-shell-android-2.4.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/test/uiautomator/uiautomator-shell-android/2.4.0-SNAPSHOT/uiautomator-shell-android-2.4.0-20260228.045313-1.aar Version: 2.4.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_tracing_tracing_android/README.chromium b/third_party/androidx/committed/libs/androidx_tracing_tracing_android/README.chromium index 5366c8b2..2e4d49f4 100644 --- a/third_party/androidx/committed/libs/androidx_tracing_tracing_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_tracing_tracing_android/README.chromium
@@ -1,6 +1,6 @@ Name: Tracing Short Name: tracing-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/tracing/tracing-android/2.0.0-SNAPSHOT/tracing-android-2.0.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/tracing/tracing-android/2.0.0-SNAPSHOT/tracing-android-2.0.0-20260228.045313-1.aar Version: 2.0.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_tracing_tracing_ktx/README.chromium b/third_party/androidx/committed/libs/androidx_tracing_tracing_ktx/README.chromium index 9f1c1772..13228ea9 100644 --- a/third_party/androidx/committed/libs/androidx_tracing_tracing_ktx/README.chromium +++ b/third_party/androidx/committed/libs/androidx_tracing_tracing_ktx/README.chromium
@@ -1,6 +1,6 @@ Name: Tracing Kotlin Extensions Short Name: tracing-ktx -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/tracing/tracing-ktx/2.0.0-SNAPSHOT/tracing-ktx-2.0.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/tracing/tracing-ktx/2.0.0-SNAPSHOT/tracing-ktx-2.0.0-20260228.045313-1.aar Version: 2.0.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_viewpager2_viewpager2/README.chromium b/third_party/androidx/committed/libs/androidx_viewpager2_viewpager2/README.chromium index 080b835..9a07c5d 100644 --- a/third_party/androidx/committed/libs/androidx_viewpager2_viewpager2/README.chromium +++ b/third_party/androidx/committed/libs/androidx_viewpager2_viewpager2/README.chromium
@@ -1,6 +1,6 @@ Name: ViewPager2 Short Name: viewpager2 -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/viewpager2/viewpager2/1.2.0-SNAPSHOT/viewpager2-1.2.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/viewpager2/viewpager2/1.2.0-SNAPSHOT/viewpager2-1.2.0-20260228.045313-1.aar Version: 1.2.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_webkit_webkit/README.chromium b/third_party/androidx/committed/libs/androidx_webkit_webkit/README.chromium index d9a1d07..d5a2dd06 100644 --- a/third_party/androidx/committed/libs/androidx_webkit_webkit/README.chromium +++ b/third_party/androidx/committed/libs/androidx_webkit_webkit/README.chromium
@@ -1,6 +1,6 @@ Name: Webkit Short Name: webkit -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/webkit/webkit/1.16.0-SNAPSHOT/webkit-1.16.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/webkit/webkit/1.16.0-SNAPSHOT/webkit-1.16.0-20260228.045313-1.aar Version: 1.16.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_window_sidecar_sidecar/README.chromium b/third_party/androidx/committed/libs/androidx_window_sidecar_sidecar/README.chromium index 439f4d7..63edbb21 100644 --- a/third_party/androidx/committed/libs/androidx_window_sidecar_sidecar/README.chromium +++ b/third_party/androidx/committed/libs/androidx_window_sidecar_sidecar/README.chromium
@@ -1,6 +1,6 @@ Name: WindowManager Sidecar Short Name: sidecar -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/window/sidecar/sidecar/1.0.0-SNAPSHOT/sidecar-1.0.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/window/sidecar/sidecar/1.0.0-SNAPSHOT/sidecar-1.0.0-20260228.045313-1.aar Version: 1.0.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_window_window/README.chromium b/third_party/androidx/committed/libs/androidx_window_window/README.chromium index 9f35f70c..80f9c3b 100644 --- a/third_party/androidx/committed/libs/androidx_window_window/README.chromium +++ b/third_party/androidx/committed/libs/androidx_window_window/README.chromium
@@ -1,6 +1,6 @@ Name: WindowManager Short Name: window -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/window/window/1.6.0-SNAPSHOT/window-1.6.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/window/window/1.6.0-SNAPSHOT/window-1.6.0-20260228.045313-1.aar Version: 1.6.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_window_window_core_android/README.chromium b/third_party/androidx/committed/libs/androidx_window_window_core_android/README.chromium index 5fa652ae..a51ab27 100644 --- a/third_party/androidx/committed/libs/androidx_window_window_core_android/README.chromium +++ b/third_party/androidx/committed/libs/androidx_window_window_core_android/README.chromium
@@ -1,6 +1,6 @@ Name: WindowManager Core Short Name: window-core-android -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/window/window-core-android/1.6.0-SNAPSHOT/window-core-android-1.6.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/window/window-core-android/1.6.0-SNAPSHOT/window-core-android-1.6.0-20260228.045313-1.aar Version: 1.6.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_work_work_multiprocess/README.chromium b/third_party/androidx/committed/libs/androidx_work_work_multiprocess/README.chromium index 8803c47..b8b4913 100644 --- a/third_party/androidx/committed/libs/androidx_work_work_multiprocess/README.chromium +++ b/third_party/androidx/committed/libs/androidx_work_work_multiprocess/README.chromium
@@ -1,6 +1,6 @@ Name: WorkManager Multiprocess Short Name: work-multiprocess -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/work/work-multiprocess/2.12.0-SNAPSHOT/work-multiprocess-2.12.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/work/work-multiprocess/2.12.0-SNAPSHOT/work-multiprocess-2.12.0-20260228.045313-1.aar Version: 2.12.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/androidx/committed/libs/androidx_work_work_runtime/README.chromium b/third_party/androidx/committed/libs/androidx_work_work_runtime/README.chromium index c165a64..5ce76a6 100644 --- a/third_party/androidx/committed/libs/androidx_work_work_runtime/README.chromium +++ b/third_party/androidx/committed/libs/androidx_work_work_runtime/README.chromium
@@ -1,6 +1,6 @@ Name: WorkManager Runtime Short Name: work-runtime -URL: https://androidx.dev/snapshots/builds/14950004/artifacts/repository/androidx/work/work-runtime/2.12.0-SNAPSHOT/work-runtime-2.12.0-20260227.122158-1.aar +URL: https://androidx.dev/snapshots/builds/14954281/artifacts/repository/androidx/work/work-runtime/2.12.0-SNAPSHOT/work-runtime-2.12.0-20260228.045313-1.aar Version: 2.12.0-SNAPSHOT Update Mechanism: Autoroll License: Apache-2.0
diff --git a/third_party/angle b/third_party/angle index a326d25..8807d22 160000 --- a/third_party/angle +++ b/third_party/angle
@@ -1 +1 @@ -Subproject commit a326d25100d1d09038de1d63d1d0676a59b24ffe +Subproject commit 8807d22fb5c5cb62eb4c8233abfb0d5c6af1e999
diff --git a/third_party/blink/renderer/core/animation/view_timeline.cc b/third_party/blink/renderer/core/animation/view_timeline.cc index c766211e..b09dc68 100644 --- a/third_party/blink/renderer/core/animation/view_timeline.cc +++ b/third_party/blink/renderer/core/animation/view_timeline.cc
@@ -400,7 +400,7 @@ return; } - StickyPositionScrollingConstraints* constraints = + StickyPositionScrollingConstraints constraints = sticky_container->StickyConstraints(); if (!constraints) { return; @@ -409,7 +409,7 @@ const PhysicalAxis axis = orientation == kHorizontalScroll ? PhysicalAxis::kHorizontal : PhysicalAxis::kVertical; - const auto* axis_data = constraints->AxisData(axis); + const auto* axis_data = constraints.AxisData(axis); if (!axis_data) { return; }
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc index ae0410b..282839e7 100644 --- a/third_party/blink/renderer/core/dom/element.cc +++ b/third_party/blink/renderer/core/dom/element.cc
@@ -12420,23 +12420,20 @@ // losing focus (not any ancestors), and then SetFocused(true) is called on // the element gaining focus. Because the ancestor chain is not automatically // notified, this function must walk the ancestors manually. -void Element::HandleInterestForHoverOrFocus(InterestSource source, - bool recursive_call) { +void Element::HandleInterestForHoverOrFocus(InterestSource source) { DCHECK(RuntimeEnabledFeatures::HTMLInterestForAttributeEnabled()); if (!IsInTreeScope() || !GetDocument().IsActive()) { return; } - // We manually "bubble" all calls to this function to all ancestors. - if (!recursive_call) { - for (auto& node : FlatTreeTraversal::InclusiveAncestorsOf(*this)) { - if (Element* element = DynamicTo<Element>(node)) { - element->HandleInterestForHoverOrFocus(source, - /*recursive_call*/ true); - } + for (Node& node : FlatTreeTraversal::InclusiveAncestorsOf(*this)) { + if (Element* element = DynamicTo<Element>(node)) { + element->ScheduleInterestChangesIfNeeded(source); } - return; } +} +void Element::ScheduleInterestChangesIfNeeded(InterestSource source) { + DCHECK(RuntimeEnabledFeatures::HTMLInterestForAttributeEnabled()); InvokerData* invoker_data = GetInvokerData(); Element* upstream_invoker = SourceInterestInvoker(); InvokerData* upstream_data =
diff --git a/third_party/blink/renderer/core/dom/element.h b/third_party/blink/renderer/core/dom/element.h index 51dbbdb..0f463227 100644 --- a/third_party/blink/renderer/core/dom/element.h +++ b/third_party/blink/renderer/core/dom/element.h
@@ -2571,8 +2571,8 @@ kFocus, kBlur, }; - void HandleInterestForHoverOrFocus(InterestSource source, - bool recursive_call = false); + void HandleInterestForHoverOrFocus(InterestSource source); + void ScheduleInterestChangesIfNeeded(InterestSource source); // Highlight pseudos inherit all properties from the corresponding highlight // in the parent, but virtually all existing content uses universal rules
diff --git a/third_party/blink/renderer/core/dom/element_test.cc b/third_party/blink/renderer/core/dom/element_test.cc index 4ab103c..36bfabd 100644 --- a/third_party/blink/renderer/core/dom/element_test.cc +++ b/third_party/blink/renderer/core/dom/element_test.cc
@@ -1056,8 +1056,8 @@ TEST_F(ElementTest, ParseFocusgroupAttrNoMemoryToken) { Document& document = GetDocument(); SetBodyContent(R"HTML( - <div id=a focusgroup="toolbar nomemory"></div> - <div id=b focusgroup="listbox inline nomemory"></div> + <div id=a focusgroup="toolbar no-memory"></div> + <div id=b focusgroup="listbox inline no-memory"></div> )HTML"); auto* a = document.getElementById(AtomicString("a")); @@ -1225,7 +1225,7 @@ static_cast<FocusgroupFlags>(FocusgroupFlags::kBlock | FocusgroupFlags::kNoMemory)}; EXPECT_EQ( - "toolbar:(block|nomemory)", + "toolbar:(block|no-memory)", focusgroup::FocusgroupDataToStringForTesting(toolbar_no_memory_data)); }
diff --git a/third_party/blink/renderer/core/dom/events/event_target.h b/third_party/blink/renderer/core/dom/events/event_target.h index 2fb4b367..a73bcb1 100644 --- a/third_party/blink/renderer/core/dom/events/event_target.h +++ b/third_party/blink/renderer/core/dom/events/event_target.h
@@ -246,6 +246,7 @@ DEFINE_ATTRIBUTE_EVENT_LISTENER(click, kClick) DEFINE_ATTRIBUTE_EVENT_LISTENER(close, kClose) DEFINE_ATTRIBUTE_EVENT_LISTENER(command, kCommand) + DEFINE_ATTRIBUTE_EVENT_LISTENER(complete, kComplete) DEFINE_ATTRIBUTE_EVENT_LISTENER(contentvisibilityautostatechange, kContentvisibilityautostatechange) DEFINE_ATTRIBUTE_EVENT_LISTENER(contextmenu, kContextmenu)
diff --git a/third_party/blink/renderer/core/dom/focusgroup_flags.cc b/third_party/blink/renderer/core/dom/focusgroup_flags.cc index 2748883..c69d8b9 100644 --- a/third_party/blink/renderer/core/dom/focusgroup_flags.cc +++ b/third_party/blink/renderer/core/dom/focusgroup_flags.cc
@@ -67,7 +67,7 @@ {"flow", FocusgroupFlags::kRowFlow | FocusgroupFlags::kColFlow}, {"row-flow", FocusgroupFlags::kRowFlow}, {"col-flow", FocusgroupFlags::kColFlow}, - {"nomemory", FocusgroupFlags::kNoMemory}, + {"no-memory", FocusgroupFlags::kNoMemory}, }; // Returns true if a flag contains a modifier only meaningful for grid
diff --git a/third_party/blink/renderer/core/dom/focusgroup_flags_test.cc b/third_party/blink/renderer/core/dom/focusgroup_flags_test.cc index 05c1b0f..5c893f9 100644 --- a/third_party/blink/renderer/core/dom/focusgroup_flags_test.cc +++ b/third_party/blink/renderer/core/dom/focusgroup_flags_test.cc
@@ -436,24 +436,4 @@ EXPECT_TRUE(messages[0].contains("nowrap")); } -TEST_F(FocusgroupFlagsTest, NomemoryModifierSetsFlag) { - ScopedFocusgroupForTest focusgroup_scope(true); - - auto* element = MakeGarbageCollected<HTMLDivElement>(GetDocument()); - GetDocument().body()->appendChild(element); - - ClearConsoleMessages(); - FocusgroupData result = - ParseFocusgroup(element, AtomicString("toolbar nomemory")); - - EXPECT_EQ(result.behavior, FocusgroupBehavior::kToolbar); - EXPECT_TRUE(result.flags & FocusgroupFlags::kNoMemory); - // Should also have default axes. - EXPECT_TRUE(result.flags & FocusgroupFlags::kInline); - EXPECT_TRUE(result.flags & FocusgroupFlags::kBlock); - - auto messages = CopyConsoleMessages(); - EXPECT_EQ(messages.size(), 0u); -} - } // namespace blink::focusgroup
diff --git a/third_party/blink/renderer/core/dom/global_event_handlers.idl b/third_party/blink/renderer/core/dom/global_event_handlers.idl index 4836481..b5702c5 100644 --- a/third_party/blink/renderer/core/dom/global_event_handlers.idl +++ b/third_party/blink/renderer/core/dom/global_event_handlers.idl
@@ -41,6 +41,7 @@ attribute EventHandler onclick; attribute EventHandler onclose; attribute EventHandler oncommand; + [RuntimeEnabled=LoginElement] attribute EventHandler oncomplete; attribute EventHandler oncontentvisibilityautostatechange; attribute EventHandler oncontextlost; attribute EventHandler oncontextmenu;
diff --git a/third_party/blink/renderer/core/frame/remote_frame_view.cc b/third_party/blink/renderer/core/frame/remote_frame_view.cc index 33931bcc..54ddd4a 100644 --- a/third_party/blink/renderer/core/frame/remote_frame_view.cc +++ b/third_party/blink/renderer/core/frame/remote_frame_view.cc
@@ -384,7 +384,7 @@ context.Restore(); } - if (GetFrame().GetCcLayer()) { + if (GetFrame().GetCcLayer() && !paint_info.IsPrivacyPreserving()) { RecordForeignLayer( context, owner_layout_object, DisplayItem::kForeignLayerRemoteFrame, GetFrame().GetCcLayer(), FrameRect().origin() + paint_offset);
diff --git a/third_party/blink/renderer/core/html/DEPS b/third_party/blink/renderer/core/html/DEPS index 98e5fa90..a641970e 100644 --- a/third_party/blink/renderer/core/html/DEPS +++ b/third_party/blink/renderer/core/html/DEPS
@@ -17,6 +17,16 @@ "html_anchor_element.cc" : [ "+base/command_line.h" ], + "html_login_element.cc" : [ + "+services/network/public/mojom/permissions_policy/permissions_policy_feature.mojom-blink.h", + ], + "html_login_element_test.cc" : [ + "+base/run_loop.h", + "+mojo/public/cpp/bindings/receiver.h", + "+services/network/public/mojom/permissions_policy/permissions_policy_feature.mojom-blink.h", + "+services/network/public/cpp/permissions_policy/permissions_policy.h", + "+services/network/public/cpp/permissions_policy/permissions_policy_declaration.h", + ], "html_meta_element.h": [ "+services/network/public/cpp/client_hints.h", ],
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.cc b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.cc index c5dbd8b..4413d8a 100644 --- a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.cc +++ b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.cc
@@ -194,52 +194,9 @@ return true; } -std::optional<cc::PaintRecord> CanvasRenderingContext::GetElementPaintRecord( - Element* element, - std::optional<CullRect> cull_rect, - const String& func_name, - ExceptionState& exception_state) { - if (!IsDrawElementImageEligible(element, func_name, exception_state)) { - return std::nullopt; - } - - PaintRecordBuilder builder; - LayoutBox* layout_box = element->GetLayoutBox(); - // All drawn elements should have their own stacking contexts. - CHECK(layout_box->HasLayer()); - CHECK(layout_box->IsStacked()); - PaintLayer* layer = layout_box->EnclosingLayer(); - - if (!cull_rect) { - auto box_rect = - gfx::Rect(ToCeiledSize(layer->GetLayoutBox()->StitchedSize())); - cull_rect.emplace(box_rect); - } - - OverriddenCullRectScope cull_rect_scope(*layer, *cull_rect, - /*disable_expansion*/ true); - - PaintLayerPainter paint_layer_painter = PaintLayerPainter(*layer); - paint_layer_painter.Paint( - builder.Context(), - PaintFlag::kPrivacyPreserving | PaintFlag::kOmitCompositingInfo); - - // Use the drawn element's local property tree state to start drawing, but - // then modify this to include effects and clips between the drawn element - // and the canvas element. This will exclude transforms above the local - // border box state (e.g., css transform is ignored), but will include effects - // (e.g., css filter is not ignored). - PropertyTreeState property_tree_state = layer->GetLayoutBox() - ->FirstFragment() - .LocalBorderBoxProperties() - .Unalias(); - HTMLCanvasElement* canvas_element = static_cast<HTMLCanvasElement*>(Host()); - const auto& canvas_fragment = canvas_element->GetLayoutBox()->FirstFragment(); - property_tree_state.SetEffect(canvas_fragment.ContentsEffect().Unalias()); - property_tree_state.SetClip(canvas_fragment.ContentsClip().Unalias()); - - cc::PaintRecord paint_record = builder.EndRecording(property_tree_state); - return paint_record; +std::optional<CanvasChildPaintRecord> +CanvasRenderingContext::GetChildPaintRecord(Element* element) { + return Host()->GetCanvasChildPaintRecord(element->GetDomNodeId()); } scoped_refptr<StaticBitmapImage> CanvasRenderingContext::GetElementImage( @@ -255,25 +212,25 @@ element->GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( DocumentUpdateReason::kCanvasDrawElementImage); - // Element size in physical coordinates. - gfx::SizeF box_size; - if (element->GetLayoutBox()) { - box_size = gfx::SizeF(element->GetLayoutBox()->StitchedSize()); - } - gfx::RectF src_rect(box_size); - std::optional<CullRect> cull_rect; - if (sx && sy && swidth && sheight) { - float dpr = element->ComputedStyleRef().EffectiveZoom(); - src_rect = gfx::RectF(*sx * dpr, *sy * dpr, *swidth * dpr, *sheight * dpr); - cull_rect.emplace(gfx::ToEnclosingRect(src_rect)); + if (!IsDrawElementImageEligible(element, func_name, exception_state)) { + return nullptr; } - std::optional<cc::PaintRecord> paint_record = - GetElementPaintRecord(element, cull_rect, func_name, exception_state); - if (!paint_record) { + std::optional<CanvasChildPaintRecord> child_paint_record = + GetChildPaintRecord(element); + if (!child_paint_record) { + exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError, + "No cached paint record for element."); return nullptr; } + // Element size in physical coordinates. + gfx::RectF src_rect(child_paint_record->box_size); + if (sx && sy && swidth && sheight) { + float dpr = child_paint_record->scale; + src_rect = gfx::RectF(*sx * dpr, *sy * dpr, *swidth * dpr, *sheight * dpr); + } + HTMLCanvasElement* canvas_element = static_cast<HTMLCanvasElement*>(Host()); // The default destination size for GetElementImage is the source content @@ -303,7 +260,7 @@ SkiaPaintCanvas skia_paint_canvas(surface->getCanvas()); skia_paint_canvas.scale(canvas_scale.x(), canvas_scale.y()); skia_paint_canvas.translate(-src_rect.x(), -src_rect.y()); - skia_paint_canvas.drawPicture(*paint_record); + skia_paint_canvas.drawPicture(child_paint_record->record); return UnacceleratedStaticBitmapImage::Create(surface->makeImageSnapshot()); }
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h index b100d82..af36d47 100644 --- a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h +++ b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h
@@ -70,7 +70,6 @@ namespace blink { class ComputedStyle; -class CullRect; class Document; class Element; class ExceptionState; @@ -344,11 +343,7 @@ const String& func_name, ExceptionState& exception_state); - std::optional<cc::PaintRecord> GetElementPaintRecord( - Element*, - std::optional<CullRect> cull_rect, - const String& func_name, - ExceptionState&); + std::optional<CanvasChildPaintRecord> GetChildPaintRecord(Element* element); std::optional<cc::PaintRecord> empty_recording_;
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.h b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.h index 605614fa..9d49f8d 100644 --- a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.h +++ b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.h
@@ -16,6 +16,7 @@ #include "third_party/blink/renderer/core/html/canvas/ukm_parameters.h" #include "third_party/blink/renderer/platform/bindings/exception_state.h" #include "third_party/blink/renderer/platform/bindings/v8_external_memory_accounter.h" +#include "third_party/blink/renderer/platform/graphics/canvas_child_paint_record.h" #include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h" #include "third_party/blink/renderer/platform/heap/garbage_collected.h" #include "third_party/blink/renderer/platform/text/text_direction.h" @@ -151,6 +152,11 @@ virtual void DiscardResources() = 0; + virtual std::optional<CanvasChildPaintRecord> GetCanvasChildPaintRecord( + DOMNodeId child_id) const { + return std::nullopt; + } + protected: ~CanvasRenderingContextHost() override;
diff --git a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc index 6436e5c..f5293b64 100644 --- a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc +++ b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
@@ -109,6 +109,7 @@ #include "third_party/blink/renderer/platform/graphics/canvas_resource.h" #include "third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher.h" #include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h" +#include "third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h" #include "third_party/blink/renderer/platform/graphics/gpu/shared_context_rate_limiter.h" #include "third_party/blink/renderer/platform/graphics/gpu/shared_gpu_context.h" #include "third_party/blink/renderer/platform/graphics/graphics_context.h" @@ -1682,6 +1683,16 @@ dirty_rect_ = gfx::Rect(); } +std::optional<CanvasChildPaintRecord> +HTMLCanvasElement::GetCanvasChildPaintRecord(DOMNodeId child_id) const { + if (auto* view = GetDocument().View()) { + if (auto* pac = view->GetPaintArtifactCompositor()) { + return pac->GetCanvasChildPaintRecord(child_id); + } + } + return std::nullopt; +} + void HTMLCanvasElement::UpdateSuspendOffscreenCanvasAnimation() { if (!GetPage()) { return;
diff --git a/third_party/blink/renderer/core/html/canvas/html_canvas_element.h b/third_party/blink/renderer/core/html/canvas/html_canvas_element.h index bfd65c3..939f18b 100644 --- a/third_party/blink/renderer/core/html/canvas/html_canvas_element.h +++ b/third_party/blink/renderer/core/html/canvas/html_canvas_element.h
@@ -189,6 +189,9 @@ void DiscardResources() override; + std::optional<CanvasChildPaintRecord> GetCanvasChildPaintRecord( + DOMNodeId child_id) const override; + TextDirection GetTextDirection(const ComputedStyle*) override; const LayoutLocale* GetLocale() const override;
diff --git a/third_party/blink/renderer/core/html/forms/html_form_element.h b/third_party/blink/renderer/core/html/forms/html_form_element.h index 25725b7..1a26df0 100644 --- a/third_party/blink/renderer/core/html/forms/html_form_element.h +++ b/third_party/blink/renderer/core/html/forms/html_form_element.h
@@ -260,6 +260,7 @@ CHECK(!tool_name.IsNull() && !tool_description.IsNull()); } String ComputeInputSchema() override; + Element* FormElement() const override { return form_; } void ExecuteTool( String input_arguments, base::OnceCallback<void(McpToolCallbackResult)> done_callback) override;
diff --git a/third_party/blink/renderer/core/html/html_attribute_names.json5 b/third_party/blink/renderer/core/html/html_attribute_names.json5 index 6614107..012bbe4 100644 --- a/third_party/blink/renderer/core/html/html_attribute_names.json5 +++ b/third_party/blink/renderer/core/html/html_attribute_names.json5
@@ -52,6 +52,7 @@ "cite", "class", "classid", + "clientid", "clear", "closedby", "code", @@ -59,6 +60,7 @@ "codetype", "color", "cols", + "configurl", "colspan", "command", "commandfor", @@ -85,6 +87,7 @@ "dir", "direction", "dirname", + "domainhint", "disabled", "disablepictureinpicture", "disableremoteplayback", @@ -98,6 +101,7 @@ "exportparts", "face", "fetchpriority", + "fields", "filter", "focusgroup", "focusgroupstart", @@ -145,6 +149,7 @@ "link", "list", "loading", + "loginhint", "longdesc", "loop", "low", @@ -311,6 +316,7 @@ "overscrollcontainer", "parseparts", "part", + "params", "pattern", "placeholder", "playsinline",
diff --git a/third_party/blink/renderer/core/html/html_credential_element.cc b/third_party/blink/renderer/core/html/html_credential_element.cc index b8537fe1..ed0242b 100644 --- a/third_party/blink/renderer/core/html/html_credential_element.cc +++ b/third_party/blink/renderer/core/html/html_credential_element.cc
@@ -4,11 +4,111 @@ #include "third_party/blink/renderer/core/html/html_credential_element.h" +#include "third_party/blink/renderer/core/dom/document.h" +#include "third_party/blink/renderer/core/execution_context/execution_context.h" +#include "third_party/blink/renderer/core/frame/csp/content_security_policy.h" #include "third_party/blink/renderer/core/html_names.h" +#include "third_party/blink/renderer/core/inspector/console_message.h" +#include "third_party/blink/renderer/platform/json/json_parser.h" +#include "third_party/blink/renderer/platform/json/json_values.h" +#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h" +#include "third_party/blink/renderer/platform/wtf/text/string_concatenate.h" namespace blink { HTMLCredentialElement::HTMLCredentialElement(Document& document) : HTMLElement(html_names::kCredentialTag, document) {} +bool HTMLCredentialElement::IsURLAttribute(const Attribute& attribute) const { + return attribute.GetName() == html_names::kConfigurlAttr || + HTMLElement::IsURLAttribute(attribute); +} + +void HTMLCredentialElement::ParseAttribute( + const AttributeModificationParams& params) { + if (params.name == html_names::kParamsAttr && !params.new_value.IsNull()) { + JSONParseError parse_error; + if (!ParseJSON(params.new_value, &parse_error)) { + GetExecutionContext()->AddConsoleMessage( + MakeGarbageCollected<ConsoleMessage>( + mojom::blink::ConsoleMessageSource::kOther, + mojom::blink::ConsoleMessageLevel::kError, + StrCat({"credential params attribute was invalid JSON: ", + parse_error.message, " (line ", + String::Number(parse_error.line), ", col ", + String::Number(parse_error.column), ")"}))); + } + } else if (params.name == html_names::kConfigurlAttr && + !params.new_value.IsNull()) { + KURL config_url = GetDocument().CompleteURL(params.new_value); + if (params.new_value.empty() || !config_url.IsValid()) { + GetExecutionContext()->AddConsoleMessage( + MakeGarbageCollected<ConsoleMessage>( + mojom::blink::ConsoleMessageSource::kJavaScript, + mojom::blink::ConsoleMessageLevel::kError, + StrCat({"credential configurl attribute was an invalid URL: ", + params.new_value}))); + } + } + HTMLElement::ParseAttribute(params); +} + +mojom::blink::IdentityProviderRequestOptionsPtr +HTMLCredentialElement::GetFederatedRequestOptions() const { + String type = FastGetAttribute(html_names::kTypeAttr); + if (type != "federated") { + return nullptr; + } + + KURL config_url = GetNonEmptyURLAttribute(html_names::kConfigurlAttr); + if (config_url.IsEmpty() || !config_url.IsValid()) { + return nullptr; + } + + if (!GetExecutionContext()->GetContentSecurityPolicy()->AllowConnectToSource( + config_url, config_url, ResourceRequest::RedirectStatus::kNoRedirect, + ReportingDisposition::kSuppressReporting, + ContentSecurityPolicy::CheckHeaderType::kCheckAll)) { + GetExecutionContext()->AddConsoleMessage( + MakeGarbageCollected<ConsoleMessage>( + mojom::blink::ConsoleMessageSource::kJavaScript, + mojom::blink::ConsoleMessageLevel::kError, + StrCat({"Refused to connect to '", config_url.ElidedString(), + "' because it violates the document's Content Security " + "Policy."}))); + return nullptr; + } + + auto options = mojom::blink::IdentityProviderRequestOptions::New(); + options->config = mojom::blink::IdentityProviderConfig::New(); + options->config->config_url = std::move(config_url); + options->config->client_id = FastGetAttribute(html_names::kClientidAttr); + if (options->config->client_id.IsNull()) { + options->config->client_id = g_empty_string; + } + + // Initialize non-nullable fields to satisfy mojom validation. + options->nonce = g_empty_string; + options->login_hint = FastGetAttribute(html_names::kLoginhintAttr); + options->domain_hint = FastGetAttribute(html_names::kDomainhintAttr); + if (options->login_hint.IsNull()) { + options->login_hint = g_empty_string; + } + if (options->domain_hint.IsNull()) { + options->domain_hint = g_empty_string; + } + + String fields_attr = FastGetAttribute(html_names::kFieldsAttr); + if (!fields_attr.IsNull()) { + options->fields = fields_attr.Split(','); + } + + String params_attr = FastGetAttribute(html_names::kParamsAttr); + if (!params_attr.IsNull() && ParseJSON(params_attr, nullptr)) { + options->params_json = params_attr; + } + + return options; +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/html/html_credential_element.h b/third_party/blink/renderer/core/html/html_credential_element.h index 46fd1ad..2b66ffd3 100644 --- a/third_party/blink/renderer/core/html/html_credential_element.h +++ b/third_party/blink/renderer/core/html/html_credential_element.h
@@ -5,16 +5,31 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_HTML_HTML_CREDENTIAL_ELEMENT_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_HTML_CREDENTIAL_ELEMENT_H_ +#include "third_party/blink/public/mojom/webid/federated_auth_request.mojom-blink.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/html/html_element.h" namespace blink { +// <credential> element is used to provide configuration for Federated +// Credential Management (FedCM) requests when used as a child of a <login> +// element. +// See https://github.com/fedidcg/login-element for the explainer. class CORE_EXPORT HTMLCredentialElement : public HTMLElement { DEFINE_WRAPPERTYPEINFO(); public: explicit HTMLCredentialElement(Document&); + + // Returns the FederatedAuthRequest options derived from the element's + // attributes (e.g. configURL, clientID, etc.). Returns null if the element is + // not a valid credential configuration. + mojom::blink::IdentityProviderRequestOptionsPtr GetFederatedRequestOptions() + const; + + private: + void ParseAttribute(const AttributeModificationParams&) override; + bool IsURLAttribute(const Attribute&) const override; }; } // namespace blink
diff --git a/third_party/blink/renderer/core/html/html_credential_element.idl b/third_party/blink/renderer/core/html/html_credential_element.idl index a1e2259..55ee2209 100644 --- a/third_party/blink/renderer/core/html/html_credential_element.idl +++ b/third_party/blink/renderer/core/html/html_credential_element.idl
@@ -7,4 +7,11 @@ Exposed=Window, RuntimeEnabled=LoginElement ] interface HTMLCredentialElement : HTMLElement { + [CEReactions, Reflect] attribute DOMString type; + [CEReactions, Reflect] attribute DOMString clientId; + [CEReactions, Reflect, URL] attribute USVString configURL; + [CEReactions, Reflect] attribute DOMString loginHint; + [CEReactions, Reflect] attribute DOMString domainHint; + [CEReactions, Reflect] attribute DOMString fields; + [CEReactions, Reflect] attribute DOMString params; };
diff --git a/third_party/blink/renderer/core/html/html_credential_element_test.cc b/third_party/blink/renderer/core/html/html_credential_element_test.cc index 6a0e026..7b6a0d8 100644 --- a/third_party/blink/renderer/core/html/html_credential_element_test.cc +++ b/third_party/blink/renderer/core/html/html_credential_element_test.cc
@@ -4,11 +4,21 @@ #include "third_party/blink/renderer/core/html/html_credential_element.h" +#include "services/network/public/cpp/permissions_policy/permissions_policy.h" +#include "services/network/public/mojom/permissions_policy/permissions_policy_feature.mojom-blink.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/renderer/core/dom/document.h" +#include "third_party/blink/renderer/core/execution_context/execution_context.h" +#include "third_party/blink/renderer/core/execution_context/security_context.h" +#include "third_party/blink/renderer/core/frame/csp/content_security_policy.h" #include "third_party/blink/renderer/core/html_names.h" +#include "third_party/blink/renderer/core/inspector/console_message.h" +#include "third_party/blink/renderer/core/inspector/console_message_storage.h" +#include "third_party/blink/renderer/core/page/page.h" #include "third_party/blink/renderer/core/testing/page_test_base.h" #include "third_party/blink/renderer/platform/heap/garbage_collected.h" +#include "third_party/blink/renderer/platform/network/http_parsers.h" +#include "third_party/blink/renderer/platform/weborigin/security_origin.h" namespace blink { @@ -20,4 +30,110 @@ EXPECT_EQ(element->localName(), "credential"); } +TEST_F(HTMLCredentialElementTest, GetFederatedRequestOptions_Success) { + NavigateTo(KURL("https://example.com")); + auto* element = MakeGarbageCollected<HTMLCredentialElement>(GetDocument()); + element->setAttribute(html_names::kTypeAttr, AtomicString("federated")); + element->setAttribute(html_names::kConfigurlAttr, + AtomicString("https://idp.com/config.json")); + element->setAttribute(html_names::kClientidAttr, AtomicString("client123")); + + auto options = element->GetFederatedRequestOptions(); + ASSERT_TRUE(options); + EXPECT_EQ(options->config->config_url, "https://idp.com/config.json"); + EXPECT_EQ(options->config->client_id, "client123"); +} + +TEST_F(HTMLCredentialElementTest, GetFederatedRequestOptions_BlockedByCSP) { + NavigateTo(KURL("https://example.com")); + ExecutionContext* context = GetDocument().GetExecutionContext(); + + // Set up CSP to block the IDP. + context->GetContentSecurityPolicy()->AddPolicies(ParseContentSecurityPolicies( + "connect-src https://allowed.com", + network::mojom::blink::ContentSecurityPolicyType::kEnforce, + network::mojom::blink::ContentSecurityPolicySource::kHTTP, + *(context->GetSecurityOrigin()))); + + ConsoleMessageStorage& storage = GetPage().GetConsoleMessageStorage(); + wtf_size_t initial_size = storage.size(); + + auto* element = MakeGarbageCollected<HTMLCredentialElement>(GetDocument()); + GetDocument().body()->AppendChild(element); + + element->setAttribute(html_names::kTypeAttr, AtomicString("federated")); + element->setAttribute(html_names::kConfigurlAttr, + AtomicString("https://blocked.com/config.json")); + element->setAttribute(html_names::kClientidAttr, AtomicString("client123")); + + // Should return nullptr because of CSP violation. + EXPECT_FALSE(element->GetFederatedRequestOptions()); + + // Check that a console message was emitted immediately. + ASSERT_EQ(storage.size(), initial_size + 1); + ASSERT_GT(storage.size(), 0u); + EXPECT_TRUE(storage.at(storage.size() - 1) + ->Message() + .contains("Refused to connect to")); + + // Ensure no additional message is emitted. + EXPECT_EQ(storage.size(), initial_size + 1); +} + +TEST_F(HTMLCredentialElementTest, ParamsAttributeValidation) { + NavigateTo(KURL("https://example.com")); + auto* element = MakeGarbageCollected<HTMLCredentialElement>(GetDocument()); + element->setAttribute(html_names::kTypeAttr, AtomicString("federated")); + element->setAttribute(html_names::kConfigurlAttr, + AtomicString("https://idp.com/config.json")); + + ConsoleMessageStorage& storage = GetPage().GetConsoleMessageStorage(); + wtf_size_t initial_size = storage.size(); + + // Set invalid JSON in params. + element->setAttribute(html_names::kParamsAttr, + AtomicString("{invalid: json}")); + + // Check that a console message was emitted. + EXPECT_EQ(storage.size(), initial_size + 1); + EXPECT_TRUE( + storage.at(storage.size() - 1)->Message().contains("invalid JSON")); + + // Call GetFederatedRequestOptions and ensure it returns options but with + // params_json unset (null). + auto options = element->GetFederatedRequestOptions(); + ASSERT_TRUE(options); + EXPECT_TRUE(options->params_json.IsNull()); + EXPECT_EQ(storage.size(), initial_size + 1); + + // Set valid JSON. + element->setAttribute(html_names::kParamsAttr, + AtomicString("{\"valid\": \"json\"}")); + options = element->GetFederatedRequestOptions(); + ASSERT_TRUE(options); + EXPECT_EQ(options->params_json, "{\"valid\": \"json\"}"); + // No new error message should be added. + EXPECT_EQ(storage.size(), initial_size + 1); +} + +TEST_F(HTMLCredentialElementTest, ConfigUrlValidation) { + NavigateTo(KURL("https://example.com")); + auto* element = MakeGarbageCollected<HTMLCredentialElement>(GetDocument()); + element->setAttribute(html_names::kTypeAttr, AtomicString("federated")); + + ConsoleMessageStorage& storage = GetPage().GetConsoleMessageStorage(); + wtf_size_t initial_size = storage.size(); + + // Set invalid URL in configurl. + element->setAttribute(html_names::kConfigurlAttr, AtomicString("https://[")); + + // Check that a console message was emitted. + EXPECT_EQ(storage.size(), initial_size + 1); + EXPECT_TRUE( + storage.at(storage.size() - 1)->Message().contains("invalid URL")); + + // Should return nullptr because of invalid URL. + EXPECT_FALSE(element->GetFederatedRequestOptions()); +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/html/html_login_element.cc b/third_party/blink/renderer/core/html/html_login_element.cc index 3f61b9a..a355aa2 100644 --- a/third_party/blink/renderer/core/html/html_login_element.cc +++ b/third_party/blink/renderer/core/html/html_login_element.cc
@@ -4,11 +4,211 @@ #include "third_party/blink/renderer/core/html/html_login_element.h" +#include "services/network/public/mojom/permissions_policy/permissions_policy_feature.mojom-blink.h" +#include "third_party/blink/public/mojom/webid/federated_auth_request.mojom-blink.h" +#include "third_party/blink/public/platform/browser_interface_broker_proxy.h" +#include "third_party/blink/public/platform/platform.h" +#include "third_party/blink/public/platform/web_v8_value_converter.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h" +#include "third_party/blink/renderer/core/dom/document.h" +#include "third_party/blink/renderer/core/dom/element_traversal.h" +#include "third_party/blink/renderer/core/dom/events/event.h" +#include "third_party/blink/renderer/core/dom/node_traversal.h" +#include "third_party/blink/renderer/core/event_type_names.h" +#include "third_party/blink/renderer/core/events/keyboard_event.h" +#include "third_party/blink/renderer/core/events/mouse_event.h" +#include "third_party/blink/renderer/core/execution_context/execution_context.h" +#include "third_party/blink/renderer/core/frame/local_dom_window.h" +#include "third_party/blink/renderer/core/frame/web_feature.h" +#include "third_party/blink/renderer/core/html/html_credential_element.h" #include "third_party/blink/renderer/core/html_names.h" +#include "third_party/blink/renderer/core/inspector/console_message.h" +#include "third_party/blink/renderer/platform/heap/persistent.h" +#include "third_party/blink/renderer/platform/instrumentation/use_counter.h" +#include "third_party/blink/renderer/platform/wtf/functional.h" namespace blink { +namespace { + +bool IsEnterKeyKeydownEvent(Event& event) { + auto* keyboard_event = DynamicTo<KeyboardEvent>(event); + if (!keyboard_event) { + return false; + } + return keyboard_event->key() == "Enter"; +} + +bool IsLoginClick(Event& event) { + auto* mouse_event = DynamicTo<MouseEvent>(event); + if (!mouse_event || event.type() != event_type_names::kClick) { + // TODO(crbug.com/477699742): should we handle touch and pointer events too? + return false; + } + return mouse_event->button() == 0; +} + +} // namespace + HTMLLoginElement::HTMLLoginElement(Document& document) - : HTMLElement(html_names::kLoginTag, document) {} + : HTMLElement(html_names::kLoginTag, document), + federated_auth_request_(document.GetExecutionContext()) {} + +ScriptValue HTMLLoginElement::credential(ScriptState* script_state) const { + if (!credential_) { + return ScriptValue(); + } + + // TODO(crbug.com/477699742): consider storing a v8::Local<v8::Value> as + // member rather than a base::Value so that we don't have to recompute this + // every time on demand. + std::unique_ptr<WebV8ValueConverter> converter = + Platform::Current()->CreateWebV8ValueConverter(); + v8::Local<v8::Value> v8_value = + converter->ToV8Value(*credential_, script_state->GetContext()); + return ScriptValue(script_state->GetIsolate(), v8_value); +} + +void HTMLLoginElement::NotifyCredentialReceived(base::Value token) { + DCHECK(isConnected()); + credential_ = std::move(token); + DispatchEvent(*Event::Create(event_type_names::kComplete)); +} + +Vector<mojom::blink::IdentityProviderRequestOptionsPtr> +HTMLLoginElement::GetFederatedRequestOptions() const { + Vector<mojom::blink::IdentityProviderRequestOptionsPtr> options_list; + for (HTMLCredentialElement& credential : + Traversal<HTMLCredentialElement>::ChildrenOf(*this)) { + if (auto options = credential.GetFederatedRequestOptions()) { + options_list.push_back(std::move(options)); + } + } + return options_list; +} + +FocusableState HTMLLoginElement::IsFocusableState( + UpdateBehavior update_behavior) const { + // TODO(crbug.com/477699742): if you tab through the document, will the login + // element be focused even if empty? + if (!GetFederatedRequestOptions().empty()) { + return FocusableState::kFocusable; + } + return HTMLElement::IsFocusableState(update_behavior); +} + +bool HTMLLoginElement::ShouldHaveFocusAppearance() const { + return (IsFocused() && !GetFederatedRequestOptions().empty()) || + HTMLElement::ShouldHaveFocusAppearance(); +} + +bool HTMLLoginElement::WillRespondToMouseClickEvents() { + return !GetFederatedRequestOptions().empty(); +} + +bool HTMLLoginElement::IsInteractiveContent() const { + return true; +} + +Node::InsertionNotificationRequest HTMLLoginElement::InsertedInto( + ContainerNode& insertion_point) { + Node::InsertionNotificationRequest result = + HTMLElement::InsertedInto(insertion_point); + if (!insertion_point.isConnected()) { + return result; + } + + if (!GetExecutionContext()->IsSecureContext()) { + GetExecutionContext()->AddConsoleMessage( + MakeGarbageCollected<ConsoleMessage>( + mojom::blink::ConsoleMessageSource::kJavaScript, + mojom::blink::ConsoleMessageLevel::kError, + "The <login> element can only be used in a secure context.")); + } + + return result; +} + +void HTMLLoginElement::DefaultEventHandler(Event& event) { + if (!isConnected()) { + HTMLElement::DefaultEventHandler(event); + return; + } + + bool is_click = IsLoginClick(event); + bool is_enter = IsFocused() && IsEnterKeyKeydownEvent(event); + + if (!is_click && !is_enter) { + HTMLElement::DefaultEventHandler(event); + return; + } + + if (!GetExecutionContext()->IsSecureContext()) { + HTMLElement::DefaultEventHandler(event); + return; + } + + if (!GetExecutionContext()->IsFeatureEnabled( + network::mojom::PermissionsPolicyFeature::kIdentityCredentialsGet)) { + GetExecutionContext()->AddConsoleMessage(MakeGarbageCollected< + ConsoleMessage>( + mojom::blink::ConsoleMessageSource::kJavaScript, + mojom::blink::ConsoleMessageLevel::kError, + "The 'identity-credentials-get' permissions policy is not enabled.")); + HTMLElement::DefaultEventHandler(event); + return; + } + + Vector<mojom::blink::IdentityProviderGetParametersPtr> idp_get_params; + + for (HTMLCredentialElement& credential : + Traversal<HTMLCredentialElement>::ChildrenOf(*this)) { + auto options = credential.GetFederatedRequestOptions(); + if (!options) { + continue; + } + + auto get_params = mojom::blink::IdentityProviderGetParameters::New(); + get_params->providers.push_back(std::move(options)); + get_params->mode = mojom::blink::RpMode::kActive; + idp_get_params.push_back(std::move(get_params)); + } + + if (idp_get_params.empty()) { + HTMLElement::DefaultEventHandler(event); + return; + } + + if (!federated_auth_request_.is_bound()) { + GetExecutionContext()->GetBrowserInterfaceBroker().GetInterface( + federated_auth_request_.BindNewPipeAndPassReceiver( + GetExecutionContext()->GetTaskRunner(TaskType::kInternalDefault))); + } + + federated_auth_request_->RequestToken( + std::move(idp_get_params), + mojom::blink::CredentialMediationRequirement::kRequired, + blink::BindOnce(&HTMLLoginElement::OnRequestTokenResponse, + WrapWeakPersistent(this))); + event.SetDefaultHandled(); + + HTMLElement::DefaultEventHandler(event); +} + +void HTMLLoginElement::OnRequestTokenResponse( + mojom::blink::RequestTokenStatus status, + const std::optional<KURL>& selected_identity_provider_config_url, + std::optional<base::Value> token, + mojom::blink::TokenErrorPtr error, + bool is_auto_selected) { + if (status == mojom::blink::RequestTokenStatus::kSuccess && token) { + NotifyCredentialReceived(std::move(*token)); + } +} + +void HTMLLoginElement::Trace(Visitor* visitor) const { + visitor->Trace(federated_auth_request_); + HTMLElement::Trace(visitor); +} } // namespace blink
diff --git a/third_party/blink/renderer/core/html/html_login_element.h b/third_party/blink/renderer/core/html/html_login_element.h index e9b8798..bf02f579 100644 --- a/third_party/blink/renderer/core/html/html_login_element.h +++ b/third_party/blink/renderer/core/html/html_login_element.h
@@ -5,16 +5,57 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_HTML_HTML_LOGIN_ELEMENT_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_HTML_LOGIN_ELEMENT_H_ +#include "base/values.h" +#include "third_party/blink/public/mojom/webid/federated_auth_request.mojom-blink.h" +#include "third_party/blink/renderer/bindings/core/v8/script_value.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/html/html_element.h" +#include "third_party/blink/renderer/platform/mojo/heap_mojo_remote.h" namespace blink { +// <login> element is used to trigger Federated Credential Management (FedCM) +// requests when clicked. It uses its <credential> children to determine the +// identity providers to use for the request. +// See https://github.com/fedidcg/login-element for the explainer. class CORE_EXPORT HTMLLoginElement : public HTMLElement { DEFINE_WRAPPERTYPEINFO(); public: explicit HTMLLoginElement(Document&); + + ScriptValue credential(ScriptState*) const; + + Vector<mojom::blink::IdentityProviderRequestOptionsPtr> + GetFederatedRequestOptions() const; + + DEFINE_ATTRIBUTE_EVENT_LISTENER(complete, kComplete) + + void Trace(Visitor*) const override; + + private: + FocusableState IsFocusableState(UpdateBehavior) const override; + bool ShouldHaveFocusAppearance() const override; + + bool WillRespondToMouseClickEvents() override; + bool IsInteractiveContent() const override; + + void DefaultEventHandler(Event&) override; + + InsertionNotificationRequest InsertedInto(ContainerNode&) override; + + void NotifyCredentialReceived(base::Value token); + + void OnRequestTokenResponse( + mojom::blink::RequestTokenStatus status, + const std::optional<KURL>& selected_identity_provider_config_url, + std::optional<base::Value> token, + mojom::blink::TokenErrorPtr error, + bool is_auto_selected); + + std::optional<base::Value> credential_; + + HeapMojoRemote<mojom::blink::FederatedAuthRequest> federated_auth_request_; }; } // namespace blink
diff --git a/third_party/blink/renderer/core/html/html_login_element.idl b/third_party/blink/renderer/core/html/html_login_element.idl index e1f9cfc..4537542 100644 --- a/third_party/blink/renderer/core/html/html_login_element.idl +++ b/third_party/blink/renderer/core/html/html_login_element.idl
@@ -6,4 +6,5 @@ Exposed=Window, RuntimeEnabled=LoginElement ] interface HTMLLoginElement : HTMLElement { + [CallWith=ScriptState] readonly attribute any credential; };
diff --git a/third_party/blink/renderer/core/html/html_login_element_test.cc b/third_party/blink/renderer/core/html/html_login_element_test.cc index c586d51e..ac5520d8d 100644 --- a/third_party/blink/renderer/core/html/html_login_element_test.cc +++ b/third_party/blink/renderer/core/html/html_login_element_test.cc
@@ -4,20 +4,478 @@ #include "third_party/blink/renderer/core/html/html_login_element.h" +#include "base/run_loop.h" +#include "base/values.h" +#include "mojo/public/cpp/bindings/receiver.h" +#include "services/network/public/cpp/permissions_policy/permissions_policy.h" +#include "services/network/public/mojom/permissions_policy/permissions_policy_feature.mojom-blink.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/public/mojom/webid/federated_auth_request.mojom-blink.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_keyboard_event_init.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_mouse_event_init.h" +#include "third_party/blink/renderer/bindings/core/v8/v8_pointer_event_init.h" #include "third_party/blink/renderer/core/dom/document.h" +#include "third_party/blink/renderer/core/dom/events/native_event_listener.h" +#include "third_party/blink/renderer/core/event_type_names.h" +#include "third_party/blink/renderer/core/events/keyboard_event.h" +#include "third_party/blink/renderer/core/events/mouse_event.h" +#include "third_party/blink/renderer/core/events/pointer_event.h" +#include "third_party/blink/renderer/core/execution_context/execution_context.h" +#include "third_party/blink/renderer/core/execution_context/security_context.h" +#include "third_party/blink/renderer/core/frame/csp/content_security_policy.h" +#include "third_party/blink/renderer/core/frame/local_frame.h" +#include "third_party/blink/renderer/core/html/html_credential_element.h" #include "third_party/blink/renderer/core/html_names.h" +#include "third_party/blink/renderer/core/inspector/console_message.h" +#include "third_party/blink/renderer/core/inspector/console_message_storage.h" +#include "third_party/blink/renderer/core/page/focus_controller.h" +#include "third_party/blink/renderer/core/page/page.h" #include "third_party/blink/renderer/core/testing/page_test_base.h" #include "third_party/blink/renderer/platform/heap/garbage_collected.h" +#include "third_party/blink/renderer/platform/network/http_parsers.h" +#include "third_party/blink/renderer/platform/testing/testing_platform_support.h" +#include "third_party/blink/renderer/platform/weborigin/security_origin.h" namespace blink { -class HTMLLoginElementTest : public PageTestBase {}; +class QuitListener : public NativeEventListener { + public: + explicit QuitListener(base::OnceClosure quit_closure) + : quit_closure_(std::move(quit_closure)) {} -TEST_F(HTMLLoginElementTest, TagName) { + void Invoke(ExecutionContext*, Event*) override { + if (quit_closure_) { + std::move(quit_closure_).Run(); + } + } + + private: + base::OnceClosure quit_closure_; +}; + +class MockFederatedAuthRequest : public mojom::blink::FederatedAuthRequest { + public: + MockFederatedAuthRequest() = default; + + MOCK_METHOD(void, + RequestToken, + (Vector<mojom::blink::IdentityProviderGetParametersPtr>, + mojom::blink::CredentialMediationRequirement, + RequestTokenCallback), + (override)); + MOCK_METHOD(void, + RequestUserInfo, + (mojom::blink::IdentityProviderConfigPtr, + RequestUserInfoCallback), + (override)); + MOCK_METHOD(void, CancelTokenRequest, (), (override)); + MOCK_METHOD(void, + ResolveTokenRequest, + (const String&, + mojom::blink::FedCmRedirectMethod, + const std::optional<KURL>&, + const String&, + base::Value, + ResolveTokenRequestCallback), + (override)); + MOCK_METHOD(void, + SetIdpSigninStatus, + (const scoped_refptr<const SecurityOrigin>&, + mojom::blink::IdpSigninStatus, + mojom::blink::LoginStatusOptionsPtr, + SetIdpSigninStatusCallback), + (override)); + MOCK_METHOD(void, + RegisterIdP, + (const KURL&, RegisterIdPCallback), + (override)); + MOCK_METHOD(void, + UnregisterIdP, + (const KURL&, UnregisterIdPCallback), + (override)); + MOCK_METHOD(void, CloseModalDialogView, (), (override)); + MOCK_METHOD(void, + Disconnect, + (mojom::blink::IdentityCredentialDisconnectOptionsPtr, + DisconnectCallback), + (override)); + MOCK_METHOD(void, + PreventSilentAccess, + (PreventSilentAccessCallback), + (override)); +}; + +class HTMLLoginElementClickTest : public PageTestBase { + public: + void SetUp() override { + EnablePlatform(); + PageTestBase::SetUp(); + GetFrame().GetBrowserInterfaceBroker().SetBinderForTesting( + mojom::blink::FederatedAuthRequest::Name_, + BindRepeating( + [](mojo::Receiver<mojom::blink::FederatedAuthRequest>* receiver, + mojo::ScopedMessagePipeHandle handle) { + receiver->Bind( + mojo::PendingReceiver<mojom::blink::FederatedAuthRequest>( + std::move(handle))); + }, + Unretained(&receiver_))); + } + + void TearDown() override { + GetFrame().GetBrowserInterfaceBroker().SetBinderForTesting( + mojom::blink::FederatedAuthRequest::Name_, {}); + PageTestBase::TearDown(); + } + + protected: + testing::NiceMock<MockFederatedAuthRequest> mock_federated_auth_request_; + mojo::Receiver<mojom::blink::FederatedAuthRequest> receiver_{ + &mock_federated_auth_request_}; +}; + +TEST_F(HTMLLoginElementClickTest, TagName) { auto* element = MakeGarbageCollected<HTMLLoginElement>(GetDocument()); EXPECT_EQ(element->tagName(), "LOGIN"); EXPECT_EQ(element->localName(), "login"); } +TEST_F(HTMLLoginElementClickTest, ClickInitiatesFedCm) { + // Set a secure origin. + NavigateTo(KURL("https://example.com")); + + auto* login = MakeGarbageCollected<HTMLLoginElement>(GetDocument()); + GetDocument().body()->AppendChild(login); + + auto* credential = MakeGarbageCollected<HTMLCredentialElement>(GetDocument()); + credential->setAttribute(html_names::kTypeAttr, AtomicString("federated")); + credential->setAttribute(html_names::kConfigurlAttr, + AtomicString("https://example.com/fedcm.json")); + credential->setAttribute(html_names::kClientidAttr, + AtomicString("client123")); + login->AppendChild(credential); + + EXPECT_CALL(mock_federated_auth_request_, RequestToken) + .WillOnce([](Vector<mojom::blink::IdentityProviderGetParametersPtr> + idp_get_params, + mojom::blink::CredentialMediationRequirement mediation, + MockFederatedAuthRequest::RequestTokenCallback callback) { + ASSERT_EQ(idp_get_params.size(), 1u); + ASSERT_EQ(idp_get_params[0]->providers.size(), 1u); + EXPECT_EQ(idp_get_params[0]->providers[0]->config->config_url, + "https://example.com/fedcm.json"); + EXPECT_EQ(idp_get_params[0]->providers[0]->config->client_id, + "client123"); + std::move(callback).Run(mojom::blink::RequestTokenStatus::kSuccess, + std::nullopt, base::Value("dummy-token"), + nullptr, false); + }); + + // Simulate click on the login element. + base::RunLoop run_loop; + auto* listener = MakeGarbageCollected<QuitListener>(run_loop.QuitClosure()); + login->addEventListener(event_type_names::kComplete, listener); + login->click(); + + run_loop.Run(); + + // Verify that the login element received the credential. + ScriptState* script_state = + ToScriptStateForMainWorld(GetDocument().GetFrame()); + ScriptState::Scope scope(script_state); + ScriptValue credential_value = login->credential(script_state); + EXPECT_TRUE(credential_value.V8Value()->IsString()); + EXPECT_EQ(ToCoreString(script_state->GetIsolate(), + credential_value.V8Value().As<v8::String>()), + "dummy-token"); +} + +TEST_F(HTMLLoginElementClickTest, ClickInitiatesFedCmWithSimpleToken) { + // Set a secure origin. + NavigateTo(KURL("https://example.com")); + + auto* login = MakeGarbageCollected<HTMLLoginElement>(GetDocument()); + GetDocument().body()->AppendChild(login); + + auto* credential = MakeGarbageCollected<HTMLCredentialElement>(GetDocument()); + credential->setAttribute(html_names::kTypeAttr, AtomicString("federated")); + credential->setAttribute(html_names::kConfigurlAttr, + AtomicString("https://example.com/fedcm.json")); + credential->setAttribute(html_names::kClientidAttr, + AtomicString("client123")); + login->AppendChild(credential); + + EXPECT_CALL(mock_federated_auth_request_, RequestToken) + .WillOnce([](Vector<mojom::blink::IdentityProviderGetParametersPtr> + idp_get_params, + mojom::blink::CredentialMediationRequirement mediation, + MockFederatedAuthRequest::RequestTokenCallback callback) { + std::move(callback).Run(mojom::blink::RequestTokenStatus::kSuccess, + std::nullopt, base::Value(123), nullptr, false); + }); + + // Simulate click on the login element. + base::RunLoop run_loop; + auto* listener = MakeGarbageCollected<QuitListener>(run_loop.QuitClosure()); + login->addEventListener(event_type_names::kComplete, listener); + login->click(); + + run_loop.Run(); + + // Verify that the login element received the simple credential. + ScriptState* script_state = + ToScriptStateForMainWorld(GetDocument().GetFrame()); + ScriptState::Scope scope(script_state); + ScriptValue credential_value = login->credential(script_state); + EXPECT_TRUE(credential_value.V8Value()->IsNumber()); + EXPECT_EQ(credential_value.V8Value().As<v8::Number>()->Value(), 123); +} + +TEST_F(HTMLLoginElementClickTest, NonLeftClickDoesNotInitiateFedCm) { + // Set a secure origin. + NavigateTo(KURL("https://example.com")); + + auto* login = MakeGarbageCollected<HTMLLoginElement>(GetDocument()); + GetDocument().body()->AppendChild(login); + + auto* credential = MakeGarbageCollected<HTMLCredentialElement>(GetDocument()); + credential->setAttribute(html_names::kTypeAttr, AtomicString("federated")); + credential->setAttribute(html_names::kConfigurlAttr, + AtomicString("https://example.com/fedcm.json")); + credential->setAttribute(html_names::kClientidAttr, + AtomicString("client123")); + login->AppendChild(credential); + + EXPECT_CALL(mock_federated_auth_request_, RequestToken).Times(0); + + // Simulate a right-click. + ScriptState* script_state = + ToScriptStateForMainWorld(GetDocument().GetFrame()); + ScriptState::Scope scope(script_state); + + PointerEventInit* init = PointerEventInit::Create(); + init->setButton(2); // Right button + init->setPointerId(1); + PointerEvent* event = PointerEvent::Create(event_type_names::kClick, init); + login->DispatchEvent(*event); +} + +TEST_F(HTMLLoginElementClickTest, EnterKeyInitiatesFedCm) { + // Set a secure origin. + NavigateTo(KURL("https://example.com")); + + auto* login = MakeGarbageCollected<HTMLLoginElement>(GetDocument()); + login->setAttribute(html_names::kTabindexAttr, AtomicString("0")); + GetDocument().body()->AppendChild(login); + login->Focus(); + ASSERT_EQ(GetDocument().FocusedElement(), login); + + auto* credential = MakeGarbageCollected<HTMLCredentialElement>(GetDocument()); + credential->setAttribute(html_names::kTypeAttr, AtomicString("federated")); + credential->setAttribute(html_names::kConfigurlAttr, + AtomicString("https://example.com/fedcm.json")); + credential->setAttribute(html_names::kClientidAttr, + AtomicString("client123")); + login->AppendChild(credential); + + EXPECT_CALL(mock_federated_auth_request_, RequestToken) + .WillOnce([](Vector<mojom::blink::IdentityProviderGetParametersPtr> + idp_get_params, + mojom::blink::CredentialMediationRequirement mediation, + MockFederatedAuthRequest::RequestTokenCallback callback) { + std::move(callback).Run(mojom::blink::RequestTokenStatus::kSuccess, + std::nullopt, base::Value("dummy-token"), + nullptr, false); + }); + + // Simulate Enter keydown. + ScriptState* script_state = + ToScriptStateForMainWorld(GetDocument().GetFrame()); + ScriptState::Scope scope(script_state); + + KeyboardEventInit* init = KeyboardEventInit::Create(); + init->setKey("Enter"); + KeyboardEvent* event = + KeyboardEvent::Create(script_state, event_type_names::kKeydown, init); + base::RunLoop run_loop; + auto* listener = MakeGarbageCollected<QuitListener>(run_loop.QuitClosure()); + login->addEventListener(event_type_names::kComplete, listener); + login->DispatchEvent(*event); + + run_loop.Run(); +} + +TEST_F(HTMLLoginElementClickTest, InsecureContextDoesNotInitiateFedCm) { + // Navigate to an insecure origin. + NavigateTo(KURL("http://example.com")); + + ConsoleMessageStorage& storage = GetPage().GetConsoleMessageStorage(); + wtf_size_t initial_size = storage.size(); + + auto* login = MakeGarbageCollected<HTMLLoginElement>(GetDocument()); + GetDocument().body()->AppendChild(login); + + // Check that a console message was emitted immediately on insertion. + EXPECT_EQ(storage.size(), initial_size + 1); + EXPECT_TRUE( + storage.at(storage.size() - 1)->Message().contains("secure context")); + + auto* credential = MakeGarbageCollected<HTMLCredentialElement>(GetDocument()); + credential->setAttribute(html_names::kTypeAttr, AtomicString("federated")); + credential->setAttribute(html_names::kConfigurlAttr, + AtomicString("https://example.com/fedcm.json")); + credential->setAttribute(html_names::kClientidAttr, + AtomicString("client123")); + login->AppendChild(credential); + + EXPECT_CALL(mock_federated_auth_request_, RequestToken).Times(0); + + // Simulate click. + login->click(); + + // Ensure no additional message is emitted on click. + EXPECT_EQ(storage.size(), initial_size + 1); +} + +TEST_F(HTMLLoginElementClickTest, + PermissionsPolicyDisabledDoesNotInitiateFedCm) { + // Set a secure origin but disable the permissions policy. + NavigateTo(KURL("https://example.com")); + + ExecutionContext* context = GetDocument().GetExecutionContext(); + context->GetSecurityContext().SetPermissionsPolicy( + network::PermissionsPolicy::CreateFromParsedPolicy( + {}, context->GetSecurityOrigin()->ToUrlOrigin())); + + ConsoleMessageStorage& storage = GetPage().GetConsoleMessageStorage(); + wtf_size_t initial_size = storage.size(); + + auto* login = MakeGarbageCollected<HTMLLoginElement>(GetDocument()); + GetDocument().body()->AppendChild(login); + + // No console message on insertion for permissions policy. + EXPECT_EQ(storage.size(), initial_size); + + auto* credential = MakeGarbageCollected<HTMLCredentialElement>(GetDocument()); + credential->setAttribute(html_names::kTypeAttr, AtomicString("federated")); + credential->setAttribute(html_names::kConfigurlAttr, + AtomicString("https://example.com/fedcm.json")); + credential->setAttribute(html_names::kClientidAttr, + AtomicString("client123")); + login->AppendChild(credential); + + EXPECT_CALL(mock_federated_auth_request_, RequestToken).Times(0); + + // Simulate click. + login->click(); + + // Check that a console message was emitted on click. + EXPECT_EQ(storage.size(), initial_size + 1); + EXPECT_TRUE(storage.at(storage.size() - 1) + ->Message() + .contains("permissions policy is not enabled")); +} + +TEST_F(HTMLLoginElementClickTest, CSPConnectSrcBlocksIDP) { + // Set a secure origin. + NavigateTo(KURL("https://example.com")); + + ExecutionContext* context = GetDocument().GetExecutionContext(); + + // Set up CSP to block one of the IDPs. + context->GetContentSecurityPolicy()->AddPolicies(ParseContentSecurityPolicies( + "connect-src https://allowed.com", + network::mojom::blink::ContentSecurityPolicyType::kEnforce, + network::mojom::blink::ContentSecurityPolicySource::kHTTP, + *(context->GetSecurityOrigin()))); + + auto* login = MakeGarbageCollected<HTMLLoginElement>(GetDocument()); + GetDocument().body()->AppendChild(login); + + // Add an allowed IDP. + auto* credential1 = + MakeGarbageCollected<HTMLCredentialElement>(GetDocument()); + credential1->setAttribute(html_names::kTypeAttr, AtomicString("federated")); + credential1->setAttribute(html_names::kConfigurlAttr, + AtomicString("https://allowed.com/fedcm.json")); + credential1->setAttribute(html_names::kClientidAttr, AtomicString("123")); + login->AppendChild(credential1); + + // Add a blocked IDP. + auto* credential2 = + MakeGarbageCollected<HTMLCredentialElement>(GetDocument()); + credential2->setAttribute(html_names::kTypeAttr, AtomicString("federated")); + credential2->setAttribute(html_names::kConfigurlAttr, + AtomicString("https://blocked.com/fedcm.json")); + credential2->setAttribute(html_names::kClientidAttr, AtomicString("456")); + login->AppendChild(credential2); + + EXPECT_CALL(mock_federated_auth_request_, RequestToken) + .WillOnce([](Vector<mojom::blink::IdentityProviderGetParametersPtr> + idp_get_params, + mojom::blink::CredentialMediationRequirement mediation, + MockFederatedAuthRequest::RequestTokenCallback callback) { + // Verify that only the allowed IDP was included in the request. + ASSERT_EQ(idp_get_params.size(), 1u); + EXPECT_EQ(idp_get_params[0]->providers[0]->config->config_url, + "https://allowed.com/fedcm.json"); + std::move(callback).Run(mojom::blink::RequestTokenStatus::kSuccess, + std::nullopt, base::Value("dummy-token"), + nullptr, false); + }); + + // Simulate click. + base::RunLoop run_loop; + auto* listener = MakeGarbageCollected<QuitListener>(run_loop.QuitClosure()); + login->addEventListener(event_type_names::kComplete, listener); + login->click(); + + run_loop.Run(); +} + +TEST_F(HTMLLoginElementClickTest, IsFocusable) { + auto* login = MakeGarbageCollected<HTMLLoginElement>(GetDocument()); + GetDocument().body()->AppendChild(login); + + // Initially not focusable without credentials. + EXPECT_FALSE(login->IsFocusable()); + + auto* credential = MakeGarbageCollected<HTMLCredentialElement>(GetDocument()); + credential->setAttribute(html_names::kTypeAttr, AtomicString("federated")); + credential->setAttribute(html_names::kConfigurlAttr, + AtomicString("https://example.com/fedcm.json")); + credential->setAttribute(html_names::kClientidAttr, + AtomicString("client123")); + login->AppendChild(credential); + + // Focusable after adding a valid credential. + EXPECT_TRUE(login->IsFocusable()); +} + +TEST_F(HTMLLoginElementClickTest, ClickWithInvalidParamsDoesNotInitiateFedCm) { + // Set a secure origin. + NavigateTo(KURL("https://example.com")); + + auto* login = MakeGarbageCollected<HTMLLoginElement>(GetDocument()); + GetDocument().body()->AppendChild(login); + + auto* credential = MakeGarbageCollected<HTMLCredentialElement>(GetDocument()); + credential->setAttribute(html_names::kTypeAttr, AtomicString("federated")); + credential->setAttribute(html_names::kConfigurlAttr, + AtomicString("https://example.com/fedcm.json")); + credential->setAttribute(html_names::kClientidAttr, + AtomicString("client123")); + // Set invalid JSON in params. + credential->setAttribute(html_names::kParamsAttr, + AtomicString("{invalid: json}")); + login->AppendChild(credential); + + EXPECT_CALL(mock_federated_auth_request_, RequestToken).Times(0); + + // Simulate click. + login->click(); +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/layout/fragment_builder.cc b/third_party/blink/renderer/core/layout/fragment_builder.cc index a5d7f58..52be88b 100644 --- a/third_party/blink/renderer/core/layout/fragment_builder.cc +++ b/third_party/blink/renderer/core/layout/fragment_builder.cc
@@ -31,16 +31,6 @@ node.Style().GetPosition()); } -PhysicalAxes StickyConstrainedAxes(const ComputedStyle& style) { - PhysicalAxes axes = kPhysicalAxesNone; - if (!style.Top().IsAuto() || !style.Bottom().IsAuto()) { - axes |= kPhysicalAxesVertical; - } - if (!style.Left().IsAuto() || !style.Right().IsAuto()) { - axes |= kPhysicalAxesHorizontal; - } - return axes; -} } // namespace AnchorMap::SetOptions FragmentBuilder::AnchorOptionsForChild( @@ -143,7 +133,8 @@ const PhysicalAxes scrollable_axes = GetOverflowScrollAxes(); if (child.HasStickyConstrainedPosition()) { - const PhysicalAxes axes = StickyConstrainedAxes(child.Style()); + const PhysicalAxes axes = + LayoutBoxModelObject::StickyConstrainedAxes(child.Style()); const PhysicalAxes consumed = scrollable_axes & axes; const PhysicalAxes pending = axes ^ consumed;
diff --git a/third_party/blink/renderer/core/layout/geometry/axis.h b/third_party/blink/renderer/core/layout/geometry/axis.h index 4c4e582..6fd2e81 100644 --- a/third_party/blink/renderer/core/layout/geometry/axis.h +++ b/third_party/blink/renderer/core/layout/geometry/axis.h
@@ -17,8 +17,14 @@ enum class LogicalAxis : uint8_t { kInline = 0b01, kBlock = 0b10 }; enum class PhysicalAxis : uint8_t { kHorizontal = 0b01, kVertical = 0b10 }; -using PhysicalAxes = base::StrongAlias<class PhysicalAxesTag, uint8_t>; -using LogicalAxes = base::StrongAlias<class LogicalAxesTag, uint8_t>; +struct PhysicalAxes : public base::StrongAlias<class PhysicalAxesTag, uint8_t> { + using StrongAlias::StrongAlias; + explicit constexpr operator bool() const { return value() != 0; } +}; +struct LogicalAxes : public base::StrongAlias<class LogicalAxesTag, uint8_t> { + using StrongAlias::StrongAlias; + explicit constexpr operator bool() const { return value() != 0; } +}; inline constexpr LogicalAxes operator|(LogicalAxes a, LogicalAxes b) { return LogicalAxes(a.value() | b.value());
diff --git a/third_party/blink/renderer/core/layout/layout_box_model_object.cc b/third_party/blink/renderer/core/layout/layout_box_model_object.cc index cdbd0ed..b5451571 100644 --- a/third_party/blink/renderer/core/layout/layout_box_model_object.cc +++ b/third_party/blink/renderer/core/layout/layout_box_model_object.cc
@@ -180,10 +180,18 @@ Parent()->SetNeedsLayout(layout_invalidation_reason::kChildChanged, kMarkContainerChain); - // Clear our sticky constraints if we are no longer sticky. - if (Layer() && old_style->HasStickyConstrainedPosition() && - !StyleRef().HasStickyConstrainedPosition()) { - SetStickyConstraints(nullptr); + if (Layer() && old_style->HasStickyConstrainedPosition()) { + // Clear our sticky constraints if we are no longer sticky. + if (!StyleRef().HasStickyConstrainedPosition()) { + ClearStickyConstraints(kPhysicalAxesBoth); + } else { + // When still sticky, clear out axes that no longer exist. + const PhysicalAxes old_axes = StickyConstrainedAxes(*old_style); + const PhysicalAxes new_axes = StickyConstrainedAxes(StyleRef()); + if (const PhysicalAxes remove_axes = old_axes ^ (old_axes & new_axes)) { + ClearStickyConstraints(remove_axes); + } + } } PaintLayerType type = LayerTypeRequired(); @@ -519,14 +527,24 @@ return ContainingBlock(); } -StickyPositionScrollingConstraints* -LayoutBoxModelObject::ComputeStickyPositionConstraints() const { +StickyConstraintsData LayoutBoxModelObject::ComputeStickyPositionConstraints( + const PaintLayer& scroll_container_layer, + PhysicalAxes scroll_axes) const { NOT_DESTROYED(); DCHECK(StyleRef().HasStickyConstrainedPosition()); bool is_fixed_to_view = false; - const auto* scroll_container_layer = - Layer()->ContainingScrollContainerLayer(&is_fixed_to_view); + { + // Walk up layout / paint layer tree to find if a fixed to view element + // exists between this element and `scroll_container_layer`. + const PaintLayer* walk = Layer(); + while (walk && walk != &scroll_container_layer && !is_fixed_to_view) { + walk = walk->ContainingScrollContainerLayer(&is_fixed_to_view); + } + // We should reach `scroll_container_layer` unless we hit a fixed to view + // ancestor (in which case we stop early). + CHECK(walk == &scroll_container_layer || is_fixed_to_view); + } // Skip anonymous containing blocks except for anonymous fieldset content box. LayoutBlock* sticky_container = StickyContainer(); @@ -538,7 +556,7 @@ sticky_container = sticky_container->ContainingBlock(); } - const auto* scroll_container = scroll_container_layer->GetLayoutBox(); + const auto* scroll_container = scroll_container_layer.GetLayoutBox(); DCHECK(scroll_container); const PhysicalOffset scroll_container_border_offset( scroll_container->BorderLeft(), scroll_container->BorderTop()); @@ -626,10 +644,16 @@ const PhysicalRect constraining_rect = scroll_container->ComputeStickyConstrainingRect(); - auto compute_axis_data = [&](PhysicalAxis axis, const Length& min_length, - const Length& max_length, - LayoutUnit available_size, - LayoutUnit sticky_box_size, bool is_flipped) { + auto compute_axis_data = + [&](PhysicalAxis axis, const Length& min_length, const Length& max_length, + LayoutUnit available_size, LayoutUnit sticky_box_size, + bool is_flipped) -> StickyPositionScrollingConstraints::PerAxisData* { + const PhysicalAxes axes = axis == PhysicalAxis::kHorizontal + ? kPhysicalAxesHorizontal + : kPhysicalAxesVertical; + if (!(axes & scroll_axes)) { + return nullptr; + } std::optional<LayoutUnit> min_inset; std::optional<LayoutUnit> max_inset; @@ -658,7 +682,7 @@ axis, scroll_container_relative_containing_block_rect, scroll_container_relative_sticky_box_rect, constraining_rect, nearest_sticky_layer_shifting_sticky_box, - nearest_sticky_layer_shifting_containing_block, scroll_container_layer, + nearest_sticky_layer_shifting_containing_block, &scroll_container_layer, is_fixed_to_view, min_inset, max_inset); }; @@ -666,21 +690,60 @@ const WritingDirectionMode sticky_container_writing_direction = sticky_container->StyleRef().GetWritingDirection(); - return MakeGarbageCollected<StickyPositionScrollingConstraints>( + return StickyConstraintsData{ compute_axis_data(PhysicalAxis::kHorizontal, style.Left(), style.Right(), constraining_rect.size.width, sticky_box_rect.Width(), sticky_container_writing_direction.IsFlippedX()), compute_axis_data(PhysicalAxis::kVertical, style.Top(), style.Bottom(), constraining_rect.size.height, sticky_box_rect.Height(), - sticky_container_writing_direction.IsFlippedY())); + sticky_container_writing_direction.IsFlippedY())}; +} + +void LayoutBoxModelObject::SetStickyConstraints( + StickyConstraintsData constraints) { + NOT_DESTROYED(); + + if (GetMutableForPainting().FirstFragment().SetStickyConstraints( + constraints)) { + SetNeedsPaintPropertyUpdate(); + } +} + +void LayoutBoxModelObject::ClearStickyConstraints(PhysicalAxes axes_to_clear) { + NOT_DESTROYED(); + if (GetMutableForPainting().FirstFragment().ClearStickyConstraints( + axes_to_clear)) { + SetNeedsPaintPropertyUpdate(); + } +} + +PhysicalAxes LayoutBoxModelObject::StickyConstrainedAxes( + const ComputedStyle& style) { + if (style.GetPosition() != EPosition::kSticky) { + return kPhysicalAxesNone; + } + PhysicalAxes axes = kPhysicalAxesNone; + if (!style.Top().IsAuto() || !style.Bottom().IsAuto()) { + axes |= kPhysicalAxesVertical; + } + if (!style.Left().IsAuto() || !style.Right().IsAuto()) { + axes |= kPhysicalAxesHorizontal; + } + // TODO(crbug.com/481019005): When disabled, this forces both sticky axes to + // propagate upwards if either axis is active. This preserves existing + // compositor behavior and can be removed once the compositor supports + // multiple scroll container parents. + if (!RuntimeEnabledFeatures::SingleAxisScrollContainersEnabled() && axes) { + axes = kPhysicalAxesBoth; + } + return axes; } PhysicalOffset LayoutBoxModelObject::StickyPositionOffset() const { NOT_DESTROYED(); // TODO(chrishtr): StickyPositionOffset depends data updated after layout at // present, but there are callsites within Layout for it. - auto* constraints = StickyConstraints(); - return constraints ? constraints->StickyOffset() : PhysicalOffset(); + return StickyConstraints().StickyOffset(); } PhysicalOffset LayoutBoxModelObject::OffsetFromContainerInternal(
diff --git a/third_party/blink/renderer/core/layout/layout_box_model_object.h b/third_party/blink/renderer/core/layout/layout_box_model_object.h index 197f1ec6..3123e13b 100644 --- a/third_party/blink/renderer/core/layout/layout_box_model_object.h +++ b/third_party/blink/renderer/core/layout/layout_box_model_object.h
@@ -29,6 +29,7 @@ #include "third_party/blink/renderer/core/layout/background_bleed_avoidance.h" #include "third_party/blink/renderer/core/layout/content_change_type.h" #include "third_party/blink/renderer/core/layout/layout_object.h" +#include "third_party/blink/renderer/core/page/scrolling/sticky_position_scrolling_constraints.h" #include "third_party/blink/renderer/platform/text/writing_mode_utils.h" namespace blink { @@ -120,20 +121,27 @@ void DestroyLayer(); // Computes the sticky constraints for this object. - StickyPositionScrollingConstraints* ComputeStickyPositionConstraints() const; + StickyConstraintsData ComputeStickyPositionConstraints( + const PaintLayer& scroll_container_layer, + PhysicalAxes scroll_axes) const; PhysicalOffset StickyPositionOffset() const; virtual LayoutBlock* StickyContainer() const; - StickyPositionScrollingConstraints* StickyConstraints() const { + StickyPositionScrollingConstraints StickyConstraints() const { NOT_DESTROYED(); return FirstFragment().StickyConstraints(); } - void SetStickyConstraints(StickyPositionScrollingConstraints* constraints) { + bool HasStickyConstraints() const { NOT_DESTROYED(); - GetMutableForPainting().FirstFragment().SetStickyConstraints(constraints); - SetNeedsPaintPropertyUpdate(); + return FirstFragment().HasStickyConstraints(); } + void SetStickyConstraints(StickyConstraintsData constraints); + void ClearStickyConstraints(PhysicalAxes axes_to_clear); + + // Determines which physical axes are actively constrained by sticky + // positioning. + static PhysicalAxes StickyConstrainedAxes(const ComputedStyle& style); // IE extensions. Used to calculate offsetWidth/Height. virtual LayoutUnit OffsetLeft(const Element*) const = 0;
diff --git a/third_party/blink/renderer/core/layout/layout_box_model_object_test.cc b/third_party/blink/renderer/core/layout/layout_box_model_object_test.cc index 0eceeec..53d2842 100644 --- a/third_party/blink/renderer/core/layout/layout_box_model_object_test.cc +++ b/third_party/blink/renderer/core/layout/layout_box_model_object_test.cc
@@ -225,23 +225,23 @@ ASSERT_EQ(scroller->Layer(), sticky->Layer()->ContainingScrollContainerLayer()); - const auto* constraints = sticky->StickyConstraints(); + const auto constraints = sticky->StickyConstraints(); ASSERT_TRUE(constraints); EXPECT_TRUE(HasStickyLayer(scrollable_area, sticky)); - ASSERT_EQ(0.f, constraints->TopInset()->ToFloat()); + ASSERT_EQ(0.f, constraints.TopInset()->ToFloat()); // The coordinates of the constraint rects should all be with respect to the // unscrolled scroller. ASSERT_EQ(gfx::Rect(15, 115, 170, 370), ToEnclosingRect( - constraints->ScrollContainerRelativeContainingBlockRect())); + constraints.ScrollContainerRelativeContainingBlockRect())); ASSERT_EQ( gfx::Rect(15, 115, 100, 100), - ToEnclosingRect(constraints->ScrollContainerRelativeStickyBoxRect())); + ToEnclosingRect(constraints.ScrollContainerRelativeStickyBoxRect())); // The sticky constraining rect also doesn't include the border offset. ASSERT_EQ(gfx::Rect(0, 0, 400, 100), - ToEnclosingRect(constraints->ConstrainingRect())); + ToEnclosingRect(constraints.ConstrainingRect())); } // Verifies that the sticky constraints are correctly computed in right to left. @@ -267,7 +267,7 @@ ASSERT_EQ(scroller->Layer(), sticky->Layer()->ContainingScrollContainerLayer()); - const auto* constraints = sticky->StickyConstraints(); + const auto constraints = sticky->StickyConstraints(); ASSERT_TRUE(constraints); EXPECT_TRUE(HasStickyLayer(scrollable_area, sticky)); @@ -275,14 +275,14 @@ // unscrolled scroller. ASSERT_EQ(gfx::Rect(215, 115, 170, 370), ToEnclosingRect( - constraints->ScrollContainerRelativeContainingBlockRect())); + constraints.ScrollContainerRelativeContainingBlockRect())); ASSERT_EQ( gfx::Rect(285, 115, 100, 100), - ToEnclosingRect(constraints->ScrollContainerRelativeStickyBoxRect())); + ToEnclosingRect(constraints.ScrollContainerRelativeStickyBoxRect())); // The sticky constraining rect also doesn't include the border offset. ASSERT_EQ(gfx::Rect(0, 0, 400, 100), - ToEnclosingRect(constraints->ConstrainingRect())); + ToEnclosingRect(constraints.ConstrainingRect())); } // Verifies that the sticky constraints are correctly computed for inline. @@ -317,21 +317,21 @@ EXPECT_EQ(scroller->Layer(), sticky->Layer()->ContainingScrollContainerLayer()); - const auto* constraints = sticky->StickyConstraints(); + const auto constraints = sticky->StickyConstraints(); ASSERT_TRUE(constraints); EXPECT_TRUE(HasStickyLayer(scrollable_area, sticky)); - EXPECT_EQ(10.f, constraints->TopInset()->ToFloat()); + EXPECT_EQ(10.f, constraints.TopInset()->ToFloat()); // The coordinates of the constraint rects should all be with respect to the // unscrolled scroller. ASSERT_EQ(gfx::Rect(0, 100, 200, 400), ToEnclosingRect( - constraints->ScrollContainerRelativeContainingBlockRect())); + constraints.ScrollContainerRelativeContainingBlockRect())); ASSERT_EQ( gfx::Rect(0, 100, 10, 10), - ToEnclosingRect(constraints->ScrollContainerRelativeStickyBoxRect())); + ToEnclosingRect(constraints.ScrollContainerRelativeStickyBoxRect())); ASSERT_EQ(gfx::Rect(0, 0, 100, 100), - ToEnclosingRect(constraints->ConstrainingRect())); + ToEnclosingRect(constraints.ConstrainingRect())); } // Verifies that the sticky constraints are correctly computed for sticky with @@ -373,21 +373,21 @@ EXPECT_EQ(scroller->Layer(), sticky->Layer()->ContainingScrollContainerLayer()); - const auto* constraints = sticky->StickyConstraints(); + const auto constraints = sticky->StickyConstraints(); ASSERT_TRUE(constraints); EXPECT_TRUE(HasStickyLayer(scrollable_area, sticky)); - EXPECT_EQ(10.f, constraints->TopInset()->ToFloat()); + EXPECT_EQ(10.f, constraints.TopInset()->ToFloat()); // The coordinates of the constraint rects should all be with respect to the // unscrolled scroller, and not corrected for scroll origin. ASSERT_EQ(gfx::Rect(-100, 100, 200, 400), ToEnclosingRect( - constraints->ScrollContainerRelativeContainingBlockRect())); + constraints.ScrollContainerRelativeContainingBlockRect())); ASSERT_EQ( gfx::Rect(90, 100, 10, 10), - ToEnclosingRect(constraints->ScrollContainerRelativeStickyBoxRect())); + ToEnclosingRect(constraints.ScrollContainerRelativeStickyBoxRect())); ASSERT_EQ(gfx::Rect(-2100, 0, 100, 100), - ToEnclosingRect(constraints->ConstrainingRect())); + ToEnclosingRect(constraints.ConstrainingRect())); } // Verifies that the sticky constraints are not affected by transforms @@ -413,19 +413,19 @@ ASSERT_EQ(scroller->Layer(), sticky->Layer()->ContainingScrollContainerLayer()); - const auto* constraints = sticky->StickyConstraints(); + const auto constraints = sticky->StickyConstraints(); ASSERT_TRUE(constraints); EXPECT_TRUE(HasStickyLayer(scrollable_area, sticky)); - ASSERT_EQ(0.f, constraints->TopInset()->ToFloat()); + ASSERT_EQ(0.f, constraints.TopInset()->ToFloat()); // The coordinates of the constraint rects should all be with respect to the // unscrolled scroller. ASSERT_EQ(gfx::Rect(15, 115, 170, 370), ToEnclosingRect( - constraints->ScrollContainerRelativeContainingBlockRect())); + constraints.ScrollContainerRelativeContainingBlockRect())); ASSERT_EQ( gfx::Rect(15, 115, 100, 100), - ToEnclosingRect(constraints->ScrollContainerRelativeStickyBoxRect())); + ToEnclosingRect(constraints.ScrollContainerRelativeStickyBoxRect())); } // Verifies that the sticky constraints are correctly computed. @@ -450,23 +450,23 @@ ASSERT_EQ(scroller->Layer(), sticky->Layer()->ContainingScrollContainerLayer()); - const auto* constraints = sticky->StickyConstraints(); + const auto constraints = sticky->StickyConstraints(); ASSERT_TRUE(constraints); EXPECT_TRUE(HasStickyLayer(scrollable_area, sticky)); - ASSERT_EQ(0.f, constraints->TopInset()->ToFloat()); + ASSERT_EQ(0.f, constraints.TopInset()->ToFloat()); if (RuntimeEnabledFeatures::LayoutIgnoreMarginsForStickyEnabled()) { ASSERT_EQ(gfx::Rect(25, 125, 200, 350), ToEnclosingRect( - constraints->ScrollContainerRelativeContainingBlockRect())); + constraints.ScrollContainerRelativeContainingBlockRect())); } else { ASSERT_EQ(gfx::Rect(25, 145, 200, 330), ToEnclosingRect( - constraints->ScrollContainerRelativeContainingBlockRect())); + constraints.ScrollContainerRelativeContainingBlockRect())); } ASSERT_EQ( gfx::Rect(25, 145, 100, 100), - ToEnclosingRect(constraints->ScrollContainerRelativeStickyBoxRect())); + ToEnclosingRect(constraints.ScrollContainerRelativeStickyBoxRect())); } // Verifies that the sticky constraints are correct when the sticky position @@ -490,15 +490,15 @@ ASSERT_EQ(scroller->Layer(), sticky->Layer()->ContainingScrollContainerLayer()); - const auto* constraints = sticky->StickyConstraints(); + const auto constraints = sticky->StickyConstraints(); ASSERT_TRUE(constraints); EXPECT_TRUE(HasStickyLayer(scrollable_area, sticky)); ASSERT_EQ(gfx::Rect(0, 0, 400, 1100), ToEnclosingRect( - constraints->ScrollContainerRelativeContainingBlockRect())); + constraints.ScrollContainerRelativeContainingBlockRect())); ASSERT_EQ( gfx::Rect(0, 0, 100, 100), - ToEnclosingRect(constraints->ScrollContainerRelativeStickyBoxRect())); + ToEnclosingRect(constraints.ScrollContainerRelativeStickyBoxRect())); } // Verifies that the sticky constraints are correct when the sticky position @@ -525,16 +525,16 @@ ASSERT_EQ(scroller->Layer(), sticky->Layer()->ContainingScrollContainerLayer()); - const auto* constraints = sticky->StickyConstraints(); + const auto constraints = sticky->StickyConstraints(); ASSERT_TRUE(constraints); EXPECT_TRUE(HasStickyLayer(scrollable_area, sticky)); ASSERT_EQ(gfx::Rect(15, 115, 170, 370), ToEnclosingRect( - constraints->ScrollContainerRelativeContainingBlockRect())); + constraints.ScrollContainerRelativeContainingBlockRect())); ASSERT_EQ( gfx::Rect(15, 165, 100, 100), - ToEnclosingRect(constraints->ScrollContainerRelativeStickyBoxRect())); + ToEnclosingRect(constraints.ScrollContainerRelativeStickyBoxRect())); } TEST_P(LayoutBoxModelObjectTest, StickyPositionTableContainers) { @@ -554,16 +554,16 @@ PaintLayerScrollableArea* scrollable_area = scroller->GetScrollableArea(); auto* sticky = GetLayoutBoxModelObjectByElementId("sticky"); - const auto* constraints = sticky->StickyConstraints(); + const auto constraints = sticky->StickyConstraints(); ASSERT_TRUE(constraints); EXPECT_TRUE(HasStickyLayer(scrollable_area, sticky)); ASSERT_EQ(gfx::Rect(0, 0, 50, 100), ToEnclosingRect( - constraints->ScrollContainerRelativeContainingBlockRect())); + constraints.ScrollContainerRelativeContainingBlockRect())); ASSERT_EQ( gfx::Rect(0, 50, 50, 50), - ToEnclosingRect(constraints->ScrollContainerRelativeStickyBoxRect())); + ToEnclosingRect(constraints.ScrollContainerRelativeStickyBoxRect())); } // Tests that when a non-layer changes size it invalidates the constraints for @@ -590,19 +590,19 @@ auto* sticky = GetLayoutBoxModelObjectByElementId("sticky"); auto* target = GetLayoutBoxModelObjectByElementId("target"); - const auto* constraints = sticky->StickyConstraints(); + const auto constraints = sticky->StickyConstraints(); ASSERT_TRUE(constraints); EXPECT_TRUE(HasStickyLayer(scrollable_area, sticky)); EXPECT_EQ(25.f, - constraints->ScrollContainerRelativeStickyBoxRect().X().ToFloat()); + constraints.ScrollContainerRelativeStickyBoxRect().X().ToFloat()); To<HTMLElement>(target->GetNode())->classList().Add(AtomicString("hide")); // After updating layout we should have the updated position. GetDocument().View()->UpdateLifecycleToLayoutClean( DocumentUpdateReason::kTest); EXPECT_EQ(50.f, sticky->StickyConstraints() - ->ScrollContainerRelativeStickyBoxRect() + .ScrollContainerRelativeStickyBoxRect() .X() .ToFloat()); } @@ -732,33 +732,33 @@ ASSERT_TRUE( HasStickyLayer(scrollable_area, sticky_outer_div->GetLayoutBox())); - auto* outer_div_constraints = + auto outer_div_constraints = sticky_outer_div->GetLayoutObject().StickyConstraints(); ASSERT_TRUE(outer_div_constraints); ASSERT_TRUE(HasStickyLayer(scrollable_area, sticky_outer_inline)); - auto* outer_inline_constraints = sticky_outer_inline->StickyConstraints(); + auto outer_inline_constraints = sticky_outer_inline->StickyConstraints(); ASSERT_TRUE(outer_inline_constraints); ASSERT_FALSE(HasStickyLayer(scrollable_area, unanchored_sticky)); EXPECT_FALSE(unanchored_sticky->StickyConstraints()); ASSERT_TRUE(HasStickyLayer(scrollable_area, sticky_inner_inline)); - auto* inner_inline_constraints = sticky_inner_inline->StickyConstraints(); + auto inner_inline_constraints = sticky_inner_inline->StickyConstraints(); ASSERT_TRUE(inner_inline_constraints); // The outer block element trivially has no sticky-box shifting ancestor. - EXPECT_FALSE(outer_div_constraints->NearestStickyLayerShiftingStickyBox()); + EXPECT_FALSE(outer_div_constraints.NearestStickyLayerShiftingStickyBox()); // Neither does the outer inline element, as its parent element is also its // containing block. - EXPECT_FALSE(outer_inline_constraints->NearestStickyLayerShiftingStickyBox()); + EXPECT_FALSE(outer_inline_constraints.NearestStickyLayerShiftingStickyBox()); // However the inner inline element does have a sticky-box shifting ancestor, // as its containing block is the ancestor block element, above its ancestor // sticky element. EXPECT_EQ(sticky_outer_inline, - inner_inline_constraints->NearestStickyLayerShiftingStickyBox()); + inner_inline_constraints.NearestStickyLayerShiftingStickyBox()); } // Verifies that the correct containing-block shifting ancestor is found when @@ -797,29 +797,29 @@ EXPECT_TRUE(scroller->StickyConstraints()); ASSERT_TRUE(HasStickyLayer(scrollable_area, sticky_parent)); - auto* parent_constraints = sticky_parent->StickyConstraints(); + auto parent_constraints = sticky_parent->StickyConstraints(); ASSERT_TRUE(parent_constraints); ASSERT_TRUE(HasStickyLayer(scrollable_area, sticky_child)); - auto* child_constraints = sticky_child->StickyConstraints(); + auto child_constraints = sticky_child->StickyConstraints(); ASSERT_TRUE(child_constraints); ASSERT_TRUE(HasStickyLayer(scrollable_area, sticky_nested_child)); - auto* nested_child_constraints = sticky_nested_child->StickyConstraints(); + auto nested_child_constraints = sticky_nested_child->StickyConstraints(); ASSERT_TRUE(nested_child_constraints); // The outer <div> should not detect the scroller as its containing-block // shifting ancestor. - EXPECT_FALSE(parent_constraints->NearestStickyLayerShiftingContainingBlock()); + EXPECT_FALSE(parent_constraints.NearestStickyLayerShiftingContainingBlock()); // Both inner children should detect the parent <div> as their // containing-block shifting ancestor. They skip past unanchored sticky // because it will never have a non-zero offset. EXPECT_EQ(sticky_parent, - child_constraints->NearestStickyLayerShiftingContainingBlock()); + child_constraints.NearestStickyLayerShiftingContainingBlock()); EXPECT_EQ( sticky_parent, - nested_child_constraints->NearestStickyLayerShiftingContainingBlock()); + nested_child_constraints.NearestStickyLayerShiftingContainingBlock()); } // Verifies that the correct containing-block shifting ancestor is found when @@ -849,14 +849,13 @@ EXPECT_TRUE(sticky_parent->StickyConstraints()); ASSERT_TRUE(HasStickyLayer(scrollable_area, sticky_grandchild)); - auto* grandchild_constraints = sticky_grandchild->StickyConstraints(); + auto grandchild_constraints = sticky_grandchild->StickyConstraints(); ASSERT_TRUE(grandchild_constraints); // The grandchild sticky should detect the parent as its containing-block // shifting ancestor. - EXPECT_EQ( - sticky_parent, - grandchild_constraints->NearestStickyLayerShiftingContainingBlock()); + EXPECT_EQ(sticky_parent, + grandchild_constraints.NearestStickyLayerShiftingContainingBlock()); } // Verifies that the correct containing-block shifting ancestor is found when @@ -886,13 +885,13 @@ EXPECT_TRUE(sticky_outer->StickyConstraints()); ASSERT_TRUE(HasStickyLayer(scrollable_area, sticky_th)); - auto* th_constraints = sticky_th->StickyConstraints(); + auto th_constraints = sticky_th->StickyConstraints(); ASSERT_TRUE(th_constraints); // The table cell should detect the outer <div> as its containing-block // shifting ancestor. EXPECT_EQ(sticky_outer, - th_constraints->NearestStickyLayerShiftingContainingBlock()); + th_constraints.NearestStickyLayerShiftingContainingBlock()); } // Verifies that the calculated position:sticky offsets are correct when we have @@ -1254,16 +1253,16 @@ ASSERT_TRUE(HasStickyLayer(view_scrollable_area, inner_sticky_top)); // innerSticky* should not detect the outer one as any sort of ancestor. - auto* inner_constraints_top = inner_sticky_top->StickyConstraints(); + auto inner_constraints_top = inner_sticky_top->StickyConstraints(); ASSERT_TRUE(inner_constraints_top); - EXPECT_FALSE(inner_constraints_top->NearestStickyLayerShiftingStickyBox()); + EXPECT_FALSE(inner_constraints_top.NearestStickyLayerShiftingStickyBox()); EXPECT_FALSE( - inner_constraints_top->NearestStickyLayerShiftingContainingBlock()); - auto* inner_constraints_bottom = inner_sticky_bottom->StickyConstraints(); + inner_constraints_top.NearestStickyLayerShiftingContainingBlock()); + auto inner_constraints_bottom = inner_sticky_bottom->StickyConstraints(); ASSERT_TRUE(inner_constraints_bottom); - EXPECT_FALSE(inner_constraints_bottom->NearestStickyLayerShiftingStickyBox()); + EXPECT_FALSE(inner_constraints_bottom.NearestStickyLayerShiftingStickyBox()); EXPECT_FALSE( - inner_constraints_bottom->NearestStickyLayerShiftingContainingBlock()); + inner_constraints_bottom.NearestStickyLayerShiftingContainingBlock()); // Scroll the scroller down. scroller_scrollable_area->ScrollToAbsolutePositionForTest( @@ -1520,10 +1519,10 @@ )HTML"); auto* sticky = GetLayoutBoxByElementId("sticky"); - auto* constraints = sticky->StickyConstraints(); + auto constraints = sticky->StickyConstraints(); ASSERT_TRUE(constraints); EXPECT_EQ(&GetLayoutView(), - &constraints->ContainingScrollContainerLayer()->GetLayoutObject()); + &constraints.ContainingScrollContainerLayer()->GetLayoutObject()); GetDocument().body()->setAttribute(html_names::kStyleAttr, AtomicString("overflow: hidden")); @@ -1531,14 +1530,14 @@ constraints = sticky->StickyConstraints(); ASSERT_TRUE(constraints); EXPECT_EQ(GetDocument().body()->GetLayoutObject(), - &constraints->ContainingScrollContainerLayer()->GetLayoutObject()); + &constraints.ContainingScrollContainerLayer()->GetLayoutObject()); GetDocument().body()->setAttribute(html_names::kStyleAttr, g_empty_atom); UpdateAllLifecyclePhasesForTest(); constraints = sticky->StickyConstraints(); ASSERT_TRUE(constraints); EXPECT_EQ(&GetLayoutView(), - &constraints->ContainingScrollContainerLayer()->GetLayoutObject()); + &constraints.ContainingScrollContainerLayer()->GetLayoutObject()); } TEST_P(LayoutBoxModelObjectTest, RemoveStickyUnderContain) { @@ -1646,10 +1645,10 @@ ASSERT_TRUE(body->StickyConstraints()); ASSERT_TRUE(container->StickyConstraints()); - auto* child_constraints = child->StickyConstraints(); + auto child_constraints = child->StickyConstraints(); ASSERT_TRUE(child_constraints); EXPECT_EQ(container, - child_constraints->NearestStickyLayerShiftingContainingBlock()); + child_constraints.NearestStickyLayerShiftingContainingBlock()); GetLayoutView().GetScrollableArea()->ScrollToAbsolutePositionForTest( gfx::PointF(0, 50)); @@ -1664,7 +1663,7 @@ child_constraints = child->StickyConstraints(); ASSERT_TRUE(child_constraints); EXPECT_EQ(body, - child_constraints->NearestStickyLayerShiftingContainingBlock()); + child_constraints.NearestStickyLayerShiftingContainingBlock()); // This should not crash. GetLayoutView().GetScrollableArea()->ScrollToAbsolutePositionForTest(
diff --git a/third_party/blink/renderer/core/layout/physical_fragment_test.cc b/third_party/blink/renderer/core/layout/physical_fragment_test.cc index 6f10fc27..b402fd2 100644 --- a/third_party/blink/renderer/core/layout/physical_fragment_test.cc +++ b/third_party/blink/renderer/core/layout/physical_fragment_test.cc
@@ -111,11 +111,11 @@ ExpectStickyDescendant( "inner", "sticky-y", - {.consumed = kPhysicalAxesVertical, .pending = kPhysicalAxesNone}); + {.consumed = kPhysicalAxesBoth, .pending = kPhysicalAxesNone}); ExpectStickyDescendant( "inner", "sticky-x", - {.consumed = kPhysicalAxesHorizontal, .pending = kPhysicalAxesNone}); + {.consumed = kPhysicalAxesBoth, .pending = kPhysicalAxesNone}); ExpectStickyDescendant( "inner", "sticky-both",
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.cc index 3665a78..7edf889 100644 --- a/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.cc +++ b/third_party/blink/renderer/core/layout/svg/layout_svg_foreign_object.cc
@@ -6,6 +6,7 @@ #include "third_party/blink/renderer/core/layout/block_node.h" #include "third_party/blink/renderer/core/layout/constraint_space_builder.h" +#include "third_party/blink/renderer/core/layout/geometry/axis.h" #include "third_party/blink/renderer/core/layout/hit_test_result.h" #include "third_party/blink/renderer/core/layout/layout_result.h" #include "third_party/blink/renderer/core/layout/svg/svg_layout_info.h" @@ -152,7 +153,7 @@ for (const auto& item : content_result->GetPhysicalFragment().StickyDescendants()) { if (auto* pending = item.GetIfPending()) { - pending->SetStickyConstraints(nullptr); + pending->ClearStickyConstraints(kPhysicalAxesBoth); } }
diff --git a/third_party/blink/renderer/core/page/scrolling/sticky_position_scrolling_constraints.cc b/third_party/blink/renderer/core/page/scrolling/sticky_position_scrolling_constraints.cc index ff123dd..cc4d89a 100644 --- a/third_party/blink/renderer/core/page/scrolling/sticky_position_scrolling_constraints.cc +++ b/third_party/blink/renderer/core/page/scrolling/sticky_position_scrolling_constraints.cc
@@ -13,17 +13,12 @@ namespace blink { namespace { -BoxEdge RectToLayoutUnitSegment(PhysicalAxis axis, const PhysicalRect& rect) { +BoxEdge RectToBoxEdge(PhysicalAxis axis, const PhysicalRect& rect) { return axis == PhysicalAxis::kHorizontal ? BoxEdge(rect.X(), rect.Width()) : BoxEdge(rect.Y(), rect.Height()); } } // namespace -StickyPositionScrollingConstraints::StickyPositionScrollingConstraints( - PerAxisData* x_data, - PerAxisData* y_data) - : x_data_(x_data), y_data_(y_data) {} - StickyPositionScrollingConstraints::PerAxisData::PerAxisData( PhysicalAxis axis, const PhysicalRect& containing_block, @@ -39,10 +34,10 @@ min_inset(min_inset), max_inset(max_inset), scroll_container_relative_containing_block_range( - RectToLayoutUnitSegment(axis, containing_block)), + RectToBoxEdge(axis, containing_block)), scroll_container_relative_sticky_box_range( - RectToLayoutUnitSegment(axis, sticky_box)), - constraining_range(RectToLayoutUnitSegment(axis, constraining)), + RectToBoxEdge(axis, sticky_box)), + constraining_range(RectToBoxEdge(axis, constraining)), nearest_sticky_layer_shifting_sticky_box( nearest_sticky_layer_shifting_sticky_box), nearest_sticky_layer_shifting_containing_block( @@ -131,12 +126,13 @@ } void StickyPositionScrollingConstraints::ComputeStickyOffset( - const gfx::PointF& scroll_position) { - if (x_data_) { + const gfx::PointF& scroll_position, + PhysicalAxes scroll_axes) { + if (x_data_ && (scroll_axes & kPhysicalAxesHorizontal)) { x_data_->ComputeOffset(scroll_position.x()); } - if (y_data_) { + if (y_data_ && (scroll_axes & kPhysicalAxesVertical)) { y_data_->ComputeOffset(scroll_position.y()); } } @@ -176,11 +172,11 @@ if (!nearest_sticky_layer_shifting_sticky_box) { return LayoutUnit(); } - const auto* constraints = + const auto constraints = nearest_sticky_layer_shifting_sticky_box->StickyConstraints(); DCHECK(constraints); - if (const auto* ancestor_data = constraints->AxisData(axis)) { + if (const auto* ancestor_data = constraints.AxisData(axis)) { return ancestor_data->total_sticky_box_sticky_offset; } return LayoutUnit(); @@ -192,40 +188,18 @@ if (!nearest_sticky_layer_shifting_containing_block) { return LayoutUnit(); } - const auto* constraints = + const auto constraints = nearest_sticky_layer_shifting_containing_block->StickyConstraints(); DCHECK(constraints); - if (const auto* ancestor_data = constraints->AxisData(axis)) { + if (const auto* ancestor_data = constraints.AxisData(axis)) { return ancestor_data->total_containing_block_sticky_offset; } return LayoutUnit(); } -void StickyPositionScrollingConstraints::Trace(Visitor* visitor) const { - visitor->Trace(x_data_); - visitor->Trace(y_data_); -} - const StickyPositionScrollingConstraints::PerAxisData* StickyPositionScrollingConstraints::PreferredAxisData() const { - // Prefer the axis that has a valid scroll container layer. - bool x_valid = x_data_ && x_data_->containing_scroll_container_layer; - bool y_valid = y_data_ && y_data_->containing_scroll_container_layer; - if (x_valid != y_valid) { - return x_valid ? x_data_ : y_data_; - } - - // If exactly one axis is actually constrained, prefer it. - const bool x_has_insets = - x_data_ && (x_data_->min_inset || x_data_->max_inset); - const bool y_has_insets = - y_data_ && (y_data_->min_inset || y_data_->max_inset); - if (x_has_insets != y_has_insets) { - return x_has_insets ? x_data_ : y_data_; - } - - // Otherwise default to horizontal-first determinism. return x_data_ ? x_data_ : y_data_; }
diff --git a/third_party/blink/renderer/core/page/scrolling/sticky_position_scrolling_constraints.h b/third_party/blink/renderer/core/page/scrolling/sticky_position_scrolling_constraints.h index 16e1b3d1..4160c576 100644 --- a/third_party/blink/renderer/core/page/scrolling/sticky_position_scrolling_constraints.h +++ b/third_party/blink/renderer/core/page/scrolling/sticky_position_scrolling_constraints.h
@@ -74,15 +74,10 @@ // already being shifted by its ancestor. To correctly handle such situations we // apply more complicated logic which is explained in the implementation of // |ComputeStickyOffset|. -struct CORE_EXPORT StickyPositionScrollingConstraints final - : public GarbageCollected<StickyPositionScrollingConstraints> { - public: - // Computes the sticky offset for a given scroll position of the containing - // scroll container. When the scroll position changed in a ScrollableArea, - // this method must be called for all affected sticky objects in pre-tree - // order. - void ComputeStickyOffset(const gfx::PointF& scroll_position); +struct CORE_EXPORT StickyPositionScrollingConstraints final { + STACK_ALLOCATED(); + public: // The containing block rect and sticky box rect are the basic components // for calculating the sticky offset to apply after a scroll. Consider the // following setup: @@ -198,13 +193,21 @@ LayoutUnit AncestorContainingBlockOffset() const; }; - StickyPositionScrollingConstraints(PerAxisData* x_data, PerAxisData* y_data); + StickyPositionScrollingConstraints() = default; + StickyPositionScrollingConstraints(PerAxisData* x_data, PerAxisData* y_data) + : x_data_(x_data), y_data_(y_data) {} + + explicit operator bool() const { return x_data_ || y_data_; } + + // Computes the sticky offset for a given scroll position of the containing + // scroll container. When the scroll position changed in a ScrollableArea, + // this method must be called for all affected sticky objects in pre-tree + // order. + void ComputeStickyOffset(const gfx::PointF& scroll_position, + PhysicalAxes scroll_axes); const PerAxisData* AxisData(PhysicalAxis axis) const { - return (axis == PhysicalAxis::kHorizontal) ? x_data_.Get() : y_data_.Get(); - } - PerAxisData* AxisData(PhysicalAxis axis) { - return (axis == PhysicalAxis::kHorizontal) ? x_data_.Get() : y_data_.Get(); + return (axis == PhysicalAxis::kHorizontal) ? x_data_ : y_data_; } bool HasScrollDependentOffset() const; @@ -269,23 +272,28 @@ return nullptr; } - void Trace(Visitor* visitor) const; - private: const PerAxisData* PreferredAxisData() const; // Safely builds a 2D rect from 1D ranges, falling back to 0 if an axis is // missing. PhysicalRect BuildRect(const BoxEdge* x, const BoxEdge* y) const { - DCHECK(x && y) - << "2D geometric reconstruction requires both axes to be populated."; return PhysicalRect(x ? x->offset : LayoutUnit(), y ? y->offset : LayoutUnit(), x ? x->size : LayoutUnit(), y ? y->size : LayoutUnit()); } - Member<PerAxisData> x_data_; - Member<PerAxisData> y_data_; + PerAxisData* x_data_ = nullptr; + PerAxisData* y_data_ = nullptr; +}; + +// Per-axis sticky constraints update payload. +struct CORE_EXPORT StickyConstraintsData { + STACK_ALLOCATED(); + + public: + StickyPositionScrollingConstraints::PerAxisData* x_data = nullptr; + StickyPositionScrollingConstraints::PerAxisData* y_data = nullptr; }; } // namespace blink
diff --git a/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc b/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc index 3bafcd7..243ecc3b 100644 --- a/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc +++ b/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc
@@ -286,8 +286,22 @@ // We check for |HasOverflow| instead of |ScrollsOverflow| to ensure sticky // position elements are composited under overflow: hidden, which can still // have smooth scroll animations. - if (const auto* constraints = layer.GetLayoutObject().StickyConstraints()) { - if (constraints->HasScrollDependentOffset()) { + auto constraints = layer.GetLayoutObject().StickyConstraints(); + if (constraints.HasScrollDependentOffset()) { + const auto* x_data = constraints.AxisData(PhysicalAxis::kHorizontal); + const auto* y_data = constraints.AxisData(PhysicalAxis::kVertical); + const auto* x_layer = + x_data ? x_data->containing_scroll_container_layer.Get() : nullptr; + const auto* y_layer = + y_data ? y_data->containing_scroll_container_layer.Get() : nullptr; + + bool has_multiple_scrollers = x_layer && y_layer && x_layer != y_layer; + CHECK(RuntimeEnabledFeatures::SingleAxisScrollContainersEnabled() || + !has_multiple_scrollers); + + if (!has_multiple_scrollers) { + // TODO(crbug.com/481019005): Implement compositor support for multiple + // scroll container parents. Fall back to main-thread scrolling for now. reasons |= CompositingReason::kStickyPosition; } } @@ -347,7 +361,8 @@ CompositingReasons reasons = CompositingReason::kNone; auto* element = DynamicTo<Element>(object.GetNode()); - if (element && RuntimeEnabledFeatures::CanvasDrawElementEnabled()) { + if (element && IsA<LayoutBox>(object) && + RuntimeEnabledFeatures::CanvasDrawElementEnabled()) { if (element->IsInCanvasSubtree()) [[unlikely]] { auto* canvas_parent = DynamicTo<HTMLCanvasElement>(element->parentElement());
diff --git a/third_party/blink/renderer/core/paint/fragment_data.cc b/third_party/blink/renderer/core/paint/fragment_data.cc index 15ff311..d4124887 100644 --- a/third_party/blink/renderer/core/paint/fragment_data.cc +++ b/third_party/blink/renderer/core/paint/fragment_data.cc
@@ -25,14 +25,16 @@ void FragmentData::RareData::SetLayer(PaintLayer* new_layer) { if (layer && layer != new_layer) { layer->Destroy(); - sticky_constraints = nullptr; + x_sticky_constraints = nullptr; + y_sticky_constraints = nullptr; } layer = new_layer; } void FragmentData::RareData::Trace(Visitor* visitor) const { visitor->Trace(layer); - visitor->Trace(sticky_constraints); + visitor->Trace(x_sticky_constraints); + visitor->Trace(y_sticky_constraints); visitor->Trace(additional_fragments); visitor->Trace(paint_properties); visitor->Trace(local_border_box_properties);
diff --git a/third_party/blink/renderer/core/paint/fragment_data.h b/third_party/blink/renderer/core/paint/fragment_data.h index 7a2ec831..40643d4 100644 --- a/third_party/blink/renderer/core/paint/fragment_data.h +++ b/third_party/blink/renderer/core/paint/fragment_data.h
@@ -8,7 +8,9 @@ #include <optional> #include "third_party/blink/renderer/core/core_export.h" +#include "third_party/blink/renderer/core/layout/geometry/axis.h" #include "third_party/blink/renderer/core/layout/geometry/physical_rect.h" +#include "third_party/blink/renderer/core/page/scrolling/sticky_position_scrolling_constraints.h" #include "third_party/blink/renderer/core/paint/object_paint_properties.h" #include "third_party/blink/renderer/platform/graphics/paint/cull_rect.h" #include "third_party/blink/renderer/platform/graphics/paint/property_tree_state.h" @@ -49,15 +51,54 @@ } void SetLayer(PaintLayer*); - StickyPositionScrollingConstraints* StickyConstraints() const { + StickyPositionScrollingConstraints StickyConstraints() const { AssertIsFirst(); - return rare_data_ ? rare_data_->sticky_constraints.Get() : nullptr; + return rare_data_ ? StickyPositionScrollingConstraints( + rare_data_->x_sticky_constraints.Get(), + rare_data_->y_sticky_constraints.Get()) + : StickyPositionScrollingConstraints(); } - void SetStickyConstraints(StickyPositionScrollingConstraints* constraints) { + + bool HasStickyConstraints() const { AssertIsFirst(); - if (!rare_data_ && !constraints) - return; - EnsureRareData().sticky_constraints = constraints; + return rare_data_ && (rare_data_->x_sticky_constraints || + rare_data_->y_sticky_constraints); + } + + bool SetStickyConstraints(StickyConstraintsData constraints) { + AssertIsFirst(); + DCHECK(constraints.x_data || constraints.y_data); + bool has_changed = false; + auto& rare_data = EnsureRareData(); + if (constraints.x_data) { + has_changed = true; + rare_data.x_sticky_constraints = constraints.x_data; + } + if (constraints.y_data) { + has_changed = true; + rare_data.y_sticky_constraints = constraints.y_data; + } + return has_changed; + } + + bool ClearStickyConstraints(PhysicalAxes axes_to_clear) { + AssertIsFirst(); + + bool has_changed = false; + if (!rare_data_) { + return has_changed; + } + if ((axes_to_clear & kPhysicalAxesHorizontal) && + rare_data_->x_sticky_constraints) { + has_changed = true; + rare_data_->x_sticky_constraints = nullptr; + } + if ((axes_to_clear & kPhysicalAxesVertical) && + rare_data_->y_sticky_constraints) { + has_changed = true; + rare_data_->y_sticky_constraints = nullptr; + } + return has_changed; } // Holds references to the paint property nodes created by this object. @@ -178,7 +219,10 @@ // avoid separate data structure for them. They are only to be accessed in // the first fragment. Member<PaintLayer> layer; - Member<StickyPositionScrollingConstraints> sticky_constraints; + Member<StickyPositionScrollingConstraints::PerAxisData> + x_sticky_constraints; + Member<StickyPositionScrollingConstraints::PerAxisData> + y_sticky_constraints; HeapVector<Member<FragmentData>> additional_fragments; // Fragment specific data.
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc index 6add3a3..ebeb6b6 100644 --- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc +++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
@@ -2413,10 +2413,12 @@ for (const auto& fragment : GetLayoutBox()->PhysicalFragments()) { for (const auto& item : fragment.StickyDescendants()) { if (auto* sticky_descendant = item.GetIfConsumed()) { - auto* constraints = - sticky_descendant->ComputeStickyPositionConstraints(); - constraints->ComputeStickyOffset(ScrollPosition()); - sticky_descendant->SetStickyConstraints(constraints); + StickyConstraintsData data = + sticky_descendant->ComputeStickyPositionConstraints( + *Layer(), item.ConsumedAxes()); + sticky_descendant->SetStickyConstraints(data); + sticky_descendant->StickyConstraints().ComputeStickyOffset( + ScrollPosition(), item.ConsumedAxes()); } } } @@ -2451,8 +2453,8 @@ if (auto* sticky_descendant = item.GetIfConsumed()) { sticky_descendant->SetNeedsPaintPropertyUpdate(); DCHECK(sticky_descendant->StickyConstraints()); - sticky_descendant->StickyConstraints()->ComputeStickyOffset( - ScrollPosition()); + sticky_descendant->StickyConstraints().ComputeStickyOffset( + ScrollPosition(), item.ConsumedAxes()); } } }
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc index d7c4110..becbdcb 100644 --- a/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc +++ b/third_party/blink/renderer/core/paint/paint_property_tree_builder.cc
@@ -539,10 +539,10 @@ } static bool NeedsStickyTranslation(const LayoutObject& object) { - if (!object.IsBoxModelObject()) - return false; - - return To<LayoutBoxModelObject>(object).StickyConstraints(); + if (const auto* box_model = DynamicTo<LayoutBoxModelObject>(object)) { + return box_model->HasStickyConstraints(); + } + return false; } static bool NeedsAnchorPositionScrollTranslation(const LayoutObject& object) { @@ -853,10 +853,10 @@ context_.should_flatten_inherited_transform; if (state.direct_compositing_reasons) { - const auto* layout_constraint = box_model.StickyConstraints(); + const auto layout_constraint = box_model.StickyConstraints(); DCHECK(layout_constraint); const auto* scroll_container_properties = - layout_constraint->ContainingScrollContainerLayer() + layout_constraint.ContainingScrollContainerLayer() ->GetLayoutObject() .FirstFragment() .PaintProperties(); @@ -871,29 +871,29 @@ if (scroll_container_scrolls) { auto constraint = std::make_unique<CompositorStickyConstraint>(); constraint->is_anchored_left = - layout_constraint->LeftInset().has_value(); + layout_constraint.LeftInset().has_value(); constraint->is_anchored_right = - layout_constraint->RightInset().has_value(); + layout_constraint.RightInset().has_value(); constraint->is_anchored_top = - layout_constraint->TopInset().has_value(); + layout_constraint.TopInset().has_value(); constraint->is_anchored_bottom = - layout_constraint->BottomInset().has_value(); + layout_constraint.BottomInset().has_value(); constraint->left_offset = - layout_constraint->LeftInset().value_or(LayoutUnit()).ToFloat(); + layout_constraint.LeftInset().value_or(LayoutUnit()).ToFloat(); constraint->right_offset = - layout_constraint->RightInset().value_or(LayoutUnit()).ToFloat(); + layout_constraint.RightInset().value_or(LayoutUnit()).ToFloat(); constraint->top_offset = - layout_constraint->TopInset().value_or(LayoutUnit()).ToFloat(); + layout_constraint.TopInset().value_or(LayoutUnit()).ToFloat(); constraint->bottom_offset = - layout_constraint->BottomInset().value_or(LayoutUnit()).ToFloat(); + layout_constraint.BottomInset().value_or(LayoutUnit()).ToFloat(); constraint->constraint_box_rect = - gfx::RectF(layout_constraint->ConstrainingRect()); + gfx::RectF(layout_constraint.ConstrainingRect()); constraint->scroll_container_relative_sticky_box_rect = gfx::RectF( - layout_constraint->ScrollContainerRelativeStickyBoxRect()); + layout_constraint.ScrollContainerRelativeStickyBoxRect()); constraint->scroll_container_relative_containing_block_rect = gfx::RectF(layout_constraint - ->ScrollContainerRelativeContainingBlockRect()); + .ScrollContainerRelativeContainingBlockRect()); constraint->pixel_snap_offset = gfx::Vector2dF(pixel_snap_offset); // gfx::Vector2dF rounds differently than PhysicalOffset at @@ -916,7 +916,7 @@ gfx::Vector2dF(adjustment_left, adjustment_top); if (const LayoutBoxModelObject* sticky_box_shifting_ancestor = - layout_constraint->NearestStickyLayerShiftingStickyBox()) { + layout_constraint.NearestStickyLayerShiftingStickyBox()) { constraint->nearest_element_shifting_sticky_box = CompositorElementIdFromUniqueObjectId( sticky_box_shifting_ancestor->UniqueId(), @@ -924,7 +924,7 @@ } if (const LayoutBoxModelObject* containing_block_shifting_ancestor = layout_constraint - ->NearestStickyLayerShiftingContainingBlock()) { + .NearestStickyLayerShiftingContainingBlock()) { constraint->nearest_element_shifting_containing_block = CompositorElementIdFromUniqueObjectId( containing_block_shifting_ancestor->UniqueId(), @@ -1944,8 +1944,13 @@ if (state.direct_compositing_reasons & CompositingReason::kCanvasChild) { - state.canvas_child_id = CompositorElementIdFromDOMNodeId( - object_.GetNode()->GetDomNodeId()); + CHECK(IsA<LayoutBox>(object_)); + auto& canvas_fragment = object_.Parent()->FirstFragment(); + state.canvas_child_state = { + object_.GetNode()->GetDomNodeId(), + gfx::SizeF(DynamicTo<LayoutBox>(object_)->StitchedSize()), + object_.StyleRef().EffectiveZoom(), + canvas_fragment.ContentsEffect(), canvas_fragment.ContentsClip()}; } } else { // The effect node CompositorElementId is used to uniquely identify
diff --git a/third_party/blink/renderer/core/script_tools/model_context.cc b/third_party/blink/renderer/core/script_tools/model_context.cc index 76e506c2..66bfce9 100644 --- a/third_party/blink/renderer/core/script_tools/model_context.cc +++ b/third_party/blink/renderer/core/script_tools/model_context.cc
@@ -163,7 +163,7 @@ void ModelContext::ForEachScriptTool( base::FunctionRef<void(const mojom::blink::ScriptTool&)> func) const { for (const ToolData* tool_data : ListTools()) { - func(*tool_data->script_tool_); + func(tool_data->ScriptTool()); } } @@ -193,11 +193,11 @@ WebDocument::ScriptToolDeclaration* tool_declaration) const { auto it = tool_map_.find(name); if (it != tool_map_.end()) { - tool_declaration->description = it->value->script_tool_->description; - tool_declaration->input_schema = it->value->script_tool_->input_schema; - if (it->value->script_tool_->annotations) { - tool_declaration->read_only = - it->value->script_tool_->annotations->read_only; + const mojom::blink::ScriptTool& script_tool = it->value->ScriptTool(); + tool_declaration->description = script_tool.description; + tool_declaration->input_schema = script_tool.input_schema; + if (script_tool.annotations) { + tool_declaration->read_only = script_tool.annotations->read_only; } } } @@ -238,15 +238,14 @@ } std::optional<uint32_t> execution_id; - if (it->value->v8_tool_function_) { - execution_id = - ExecuteV8Tool(it->value->v8_tool_function_, name, input_arguments, - signal, std::move(tool_executed_cb)); + if (V8ToolFunction* v8_tool_function = it->value->GetV8ToolFunction()) { + execution_id = ExecuteV8Tool(v8_tool_function, name, input_arguments, + signal, std::move(tool_executed_cb)); } else { // TODO(479598776): Add support for tracking execution of // declarative tools, so that they can be cancelled. // TODO(481899636): Add signal support for declarative tools. - ExecuteDeclarativeTool(it->value->declarative_tool_, input_arguments, + ExecuteDeclarativeTool(it->value->DeclarativeTool(), input_arguments, std::move(tool_executed_cb)); } @@ -450,8 +449,6 @@ } } - auto* tool_data = MakeGarbageCollected<ToolData>(); - auto script_tool = mojom::blink::ScriptTool::New(); script_tool->name = params->name(); script_tool->description = params->description(); @@ -462,28 +459,28 @@ script_tool->annotations->read_only = params->annotations()->readOnlyHint(); } - tool_data->script_tool_ = std::move(script_tool); - tool_data->v8_tool_function_ = params->execute(); - tool_data->source_location_ = - CaptureSourceLocation(ExecutionContext::From(script_state)); + auto* tool_data = MakeGarbageCollected<ToolData>( + base::PassKey<ModelContext>(), std::move(script_tool), + /*v8_tool_function=*/params->execute(), + CaptureSourceLocation(ExecutionContext::From(script_state))); - tool_map_.insert(params->name(), std::move(tool_data)); + tool_map_.insert(params->name(), tool_data); OnToolsChanged(); UseCounter::Count(document_, WebFeature::kModelContextRegisterTool); return true; } -void ModelContext::RegisterDeclarativeTool(String name, - String description, - DeclarativeWebMCPTool* tool) { +void ModelContext::RegisterDeclarativeTool( + String name, + String description, + DeclarativeWebMCPTool* declarative_tool) { auto script_tool = mojom::blink::ScriptTool::New(); - auto* tool_data = MakeGarbageCollected<ToolData>(); script_tool->name = name; script_tool->description = description; script_tool->input_schema = "{}"; // For now - tool_data->script_tool_ = std::move(script_tool); - tool_data->declarative_tool_ = tool; + auto* tool_data = MakeGarbageCollected<ToolData>( + base::PassKey<ModelContext>(), std::move(script_tool), declarative_tool); tool_map_.insert(name, std::move(tool_data)); OnToolsChanged(); UseCounter::Count(document_, @@ -531,11 +528,7 @@ CHECK(tool_data); // Always update the input schema of declarative tools, // since the DOM might have changed. - if (DeclarativeWebMCPTool* declarative_tool = - tool_data->declarative_tool_) { - tool_data->script_tool_->input_schema = - declarative_tool->ComputeInputSchema(); - } + tool_data->RefreshDeclarativeInputSchema(); tools.push_back(tool_data); } @@ -566,10 +559,20 @@ return source_location_; } +Element* ModelContext::ToolData::BackingFormElement() const { + return declarative_tool_ ? declarative_tool_->FormElement() : nullptr; +} + void ModelContext::ToolData::Trace(Visitor* visitor) const { visitor->Trace(v8_tool_function_); visitor->Trace(declarative_tool_); visitor->Trace(source_location_); } +void ModelContext::ToolData::RefreshDeclarativeInputSchema() { + if (declarative_tool_) { + script_tool_->input_schema = declarative_tool_->ComputeInputSchema(); + } +} + } // namespace blink
diff --git a/third_party/blink/renderer/core/script_tools/model_context.h b/third_party/blink/renderer/core/script_tools/model_context.h index 51f706b7..9272fc94 100644 --- a/third_party/blink/renderer/core/script_tools/model_context.h +++ b/third_party/blink/renderer/core/script_tools/model_context.h
@@ -9,6 +9,7 @@ #include "base/functional/callback.h" #include "base/functional/callback_forward.h" +#include "base/types/pass_key.h" #include "third_party/blink/public/mojom/content_extraction/script_tools.mojom-blink.h" #include "third_party/blink/public/web/web_document.h" #include "third_party/blink/renderer/bindings/core/v8/v8_model_context.h" @@ -24,6 +25,7 @@ namespace blink { class AbortSignal; +class Element; class SourceLocation; class DeclarativeWebMCPTool : public GarbageCollectedMixin { @@ -40,6 +42,9 @@ // Returns the input json-schema associated with the tool. virtual String ComputeInputSchema() = 0; + + // The <form> backing this declarative tool. + virtual Element* FormElement() const = 0; }; class CORE_EXPORT ModelContext : public ScriptWrappable { @@ -93,17 +98,44 @@ class CORE_EXPORT ToolData : public GarbageCollected<ToolData> { public: + // Creates a JS-backed tool. + ToolData(base::PassKey<ModelContext>, + mojo::StructPtr<mojom::blink::ScriptTool> script_tool, + V8ToolFunction* v8_tool_function, + SourceLocation* source_location) + : script_tool_(std::move(script_tool)), + v8_tool_function_(v8_tool_function), + source_location_(source_location) {} + + // Creates a declarative (<form>-backed) tool. + ToolData(base::PassKey<ModelContext>, + mojo::StructPtr<mojom::blink::ScriptTool> script_tool, + DeclarativeWebMCPTool* declarative_tool) + : script_tool_(std::move(script_tool)), + declarative_tool_(declarative_tool) {} + const String& Name() const; + const mojom::blink::ScriptTool& ScriptTool() const { return *script_tool_; } + // If this is a JS-provided tool, returns the source location // of the call to registerTool(). Otherwise, returns nullptr. SourceLocation* GetSourceLocation() const; + // If this is a declarative tool, returns the <form> element + // that provided this tool. Otherwise, returns nullptr. + Element* BackingFormElement() const; + void Trace(Visitor* visitor) const; private: friend class ModelContext; + V8ToolFunction* GetV8ToolFunction() const { return v8_tool_function_; } + DeclarativeWebMCPTool* DeclarativeTool() const { return declarative_tool_; } + + void RefreshDeclarativeInputSchema(); + mojo::StructPtr<mojom::blink::ScriptTool> script_tool_; // A JS-provided MCP tool: Member<V8ToolFunction> v8_tool_function_;
diff --git a/third_party/blink/renderer/core/script_tools/model_context_test.cc b/third_party/blink/renderer/core/script_tools/model_context_test.cc index e5e3cdf..5926c595 100644 --- a/third_party/blink/renderer/core/script_tools/model_context_test.cc +++ b/third_party/blink/renderer/core/script_tools/model_context_test.cc
@@ -1053,6 +1053,7 @@ done_callback) override {} String ComputeInputSchema() override { return "{}"; } + Element* FormElement() const override { return nullptr; } void Trace(Visitor* visitor) const override {} }; @@ -1171,4 +1172,38 @@ EXPECT_EQ(8u, tools[1]->GetSourceLocation()->LineNumber()); } +TEST_F(ModelContextTest, BackingFormElement) { + SimRequest main_resource("https://example.com/", "text/html"); + LoadURL("https://example.com/"); + + main_resource.Complete(R"(<!DOCTYPE html> + <form + id=book-table + toolname=book-table + tooldescription="Book a table"> + </form> + <form + id=leave-feedback + toolname=leave-feedback + tooldescription="leave-feedback"> + </form> + )"); + + auto* model_context = + ModelContextSupplement::modelContext(*Window().navigator()); + ASSERT_TRUE(model_context); + + HeapVector<Member<const ModelContext::ToolData>> tools = + model_context->ListTools(); + ASSERT_EQ(2u, tools.size()); + + EXPECT_EQ("book-table", tools[0]->Name()); + ASSERT_TRUE(tools[0]->BackingFormElement()); + EXPECT_EQ("book-table", tools[0]->BackingFormElement()->GetIdAttribute()); + + EXPECT_EQ("leave-feedback", tools[1]->Name()); + ASSERT_TRUE(tools[1]->BackingFormElement()); + EXPECT_EQ("leave-feedback", tools[1]->BackingFormElement()->GetIdAttribute()); +} + } // namespace blink
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc index db036af..16d4166a 100644 --- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc +++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.cc
@@ -86,6 +86,7 @@ #include "third_party/blink/renderer/core/layout/layout_replaced.h" #include "third_party/blink/renderer/core/layout/layout_theme.h" #include "third_party/blink/renderer/core/layout/map_coordinates_flags.h" +#include "third_party/blink/renderer/core/paint/paint_layer.h" #include "third_party/blink/renderer/core/scroll/scroll_alignment.h" #include "third_party/blink/renderer/core/scroll/scroll_into_view_util.h" #include "third_party/blink/renderer/core/style/computed_style.h" @@ -887,29 +888,28 @@ return nullptr; } + if (!IsDrawElementImageEligible(element, "DrawElementImage", + exception_state)) { + return nullptr; + } + TRACE_EVENT0("blink", "DrawElementImage"); - element->GetDocument().View()->UpdateAllLifecyclePhasesExceptPaint( - DocumentUpdateReason::kCanvasDrawElementImage); - - // Element size in physical coordinates. - gfx::SizeF box_size; - if (element->GetLayoutBox()) { - box_size = gfx::SizeF(element->GetLayoutBox()->StitchedSize()); - } - gfx::RectF src_rect(box_size); - std::optional<CullRect> cull_rect; - if (sx && sy && swidth && sheight) { - float dpr = element->ComputedStyleRef().EffectiveZoom(); - src_rect = gfx::RectF(*sx * dpr, *sy * dpr, *swidth * dpr, *sheight * dpr); - cull_rect.emplace(gfx::ToEnclosingRect(src_rect)); - } - - std::optional<cc::PaintRecord> paint_record = GetElementPaintRecord( - element, cull_rect, "drawElementImage()", exception_state); - if (!paint_record) { + std::optional<CanvasChildPaintRecord> child_paint_record = + GetChildPaintRecord(element); + if (!child_paint_record) { + exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError, + "No cached paint record for element."); return nullptr; } + float dpr = child_paint_record->scale; + gfx::SizeF box_size = child_paint_record->box_size; + cc::PaintRecord paint_record = std::move(child_paint_record->record); + + gfx::RectF src_rect(box_size); + if (sx && sy && swidth && sheight) { + src_rect = gfx::RectF(*sx * dpr, *sy * dpr, *swidth * dpr, *sheight * dpr); + } // The filter needs to be resolved before calling Draw, because it // immediately checks IsFilterResolved() and uses a null canvas if not. @@ -989,7 +989,7 @@ c->clipRect(SkRect::MakeXYWH(src_rect.x(), src_rect.y(), src_rect.width(), src_rect.height())); - c->drawPicture(paint_record.value(), + c->drawPicture(std::move(paint_record), // use a save at the beginning of the record to keep // transforms local: true);
diff --git a/third_party/blink/renderer/modules/webaudio/audio_handler.cc b/third_party/blink/renderer/modules/webaudio/audio_handler.cc index 346a396..e65a4fd 100644 --- a/third_party/blink/renderer/modules/webaudio/audio_handler.cc +++ b/third_party/blink/renderer/modules/webaudio/audio_handler.cc
@@ -564,13 +564,13 @@ channel_interpretation_ = new_channel_interpretation_; } -void AudioHandler::SendLogMessage(const char* const function_name, +void AudioHandler::SendLogMessage(const String& function_name, const String& message) { WebRtcLogMessage( - UNSAFE_TODO(String::Format("[WA]AH::%s %s [type=%s, this=0x%" PRIXPTR "]", - function_name, message.Utf8().c_str(), - NodeTypeName().Utf8().c_str(), - reinterpret_cast<uintptr_t>(this))) + String::Format("[WA]AH::%s %s [type=%s, this=0x%" PRIXPTR "]", + function_name.Utf8().c_str(), message.Utf8().c_str(), + NodeTypeName().Utf8().c_str(), + reinterpret_cast<uintptr_t>(this)) .Utf8()); }
diff --git a/third_party/blink/renderer/modules/webaudio/audio_handler.h b/third_party/blink/renderer/modules/webaudio/audio_handler.h index 83c53ed3..79f6e3b4 100644 --- a/third_party/blink/renderer/modules/webaudio/audio_handler.h +++ b/third_party/blink/renderer/modules/webaudio/audio_handler.h
@@ -296,7 +296,7 @@ void SetNodeType(NodeType); // https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/media/capture/README.md#logs - void SendLogMessage(const char* const function_name, const String& message); + void SendLogMessage(const String& function_name, const String& message); bool is_initialized_ = false; NodeType node_type_ = NodeType::kNodeTypeUnknown;
diff --git a/third_party/blink/renderer/modules/webaudio/audio_node.cc b/third_party/blink/renderer/modules/webaudio/audio_node.cc index 79b1781..156ead4 100644 --- a/third_party/blink/renderer/modules/webaudio/audio_node.cc +++ b/third_party/blink/renderer/modules/webaudio/audio_node.cc
@@ -621,10 +621,10 @@ DCHECK_EQ(number_of_outputs, connected_params_.size()); } -void AudioNode::SendLogMessage(const char* const function_name, +void AudioNode::SendLogMessage(const String& function_name, const String& message) { - WebRtcLogMessage(UNSAFE_TODO(String::Format("[WA]AN::%s %s", function_name, - message.Utf8().c_str())) + WebRtcLogMessage(String::Format("[WA]AN::%s %s", function_name.Utf8().c_str(), + message.Utf8().c_str()) .Utf8()); }
diff --git a/third_party/blink/renderer/modules/webaudio/audio_node.h b/third_party/blink/renderer/modules/webaudio/audio_node.h index 4e6d61a5..ae63d6b3 100644 --- a/third_party/blink/renderer/modules/webaudio/audio_node.h +++ b/third_party/blink/renderer/modules/webaudio/audio_node.h
@@ -140,7 +140,7 @@ virtual void ConnectToDestinationReady() {} // https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/media/capture/README.md#logs - void SendLogMessage(const char* const function_name, const String& message); + void SendLogMessage(const String& function_name, const String& message); Member<BaseAudioContext> context_; scoped_refptr<DeferredTaskHandler> deferred_task_handler_;
diff --git a/third_party/blink/renderer/modules/webaudio/media_stream_audio_destination_handler.cc b/third_party/blink/renderer/modules/webaudio/media_stream_audio_destination_handler.cc index 7b4f363..103071a8 100644 --- a/third_party/blink/renderer/modules/webaudio/media_stream_audio_destination_handler.cc +++ b/third_party/blink/renderer/modules/webaudio/media_stream_audio_destination_handler.cc
@@ -180,13 +180,13 @@ } void MediaStreamAudioDestinationHandler::SendLogMessage( - const char* const function_name, + const String& function_name, const String& message) { - WebRtcLogMessage( - UNSAFE_TODO(String::Format("[WA]MSADH::%s %s [this=0x%" PRIXPTR "]", - function_name, message.Utf8().c_str(), - reinterpret_cast<uintptr_t>(this))) - .Utf8()); + WebRtcLogMessage(String::Format("[WA]MSADH::%s %s [this=0x%" PRIXPTR "]", + function_name.Utf8().c_str(), + message.Utf8().c_str(), + reinterpret_cast<uintptr_t>(this)) + .Utf8()); } void MediaStreamAudioDestinationHandler::SetConsumer(
diff --git a/third_party/blink/renderer/modules/webaudio/media_stream_audio_destination_handler.h b/third_party/blink/renderer/modules/webaudio/media_stream_audio_destination_handler.h index f4172b7..6d16a3a 100644 --- a/third_party/blink/renderer/modules/webaudio/media_stream_audio_destination_handler.h +++ b/third_party/blink/renderer/modules/webaudio/media_stream_audio_destination_handler.h
@@ -73,7 +73,7 @@ void ConsumeAudio(const AudioBus* const bus, int number_of_frames); // https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/media/capture/README.md#logs - void SendLogMessage(const char* const function_name, const String& message); + void SendLogMessage(const String& function_name, const String& message); base::Lock consumer_lock_; // `destination_consumer_` is owned by the node's MediaStreamSource.
diff --git a/third_party/blink/renderer/modules/webaudio/media_stream_audio_destination_node.cc b/third_party/blink/renderer/modules/webaudio/media_stream_audio_destination_node.cc index 2acb017..4e55131 100644 --- a/third_party/blink/renderer/modules/webaudio/media_stream_audio_destination_node.cc +++ b/third_party/blink/renderer/modules/webaudio/media_stream_audio_destination_node.cc
@@ -88,13 +88,14 @@ MediaStreamAudioDestinationHandler::Create( *this, number_of_channels, audio_source_ptr)); SendLogMessage( - __func__, UNSAFE_TODO(String::Format( - "({context.state=%s}, {context.sampleRate=%.0f}, " - "{number_of_channels=%u}, {handler=0x%" PRIXPTR - "}, [this=0x%" PRIXPTR "])", - context.state().AsCStr(), context.sampleRate(), - number_of_channels, reinterpret_cast<uintptr_t>(&Handler()), - reinterpret_cast<uintptr_t>(this)))); + __func__, + String::Format("({context.state=%s}, {context.sampleRate=%.0f}, " + "{number_of_channels=%u}, {handler=0x%" PRIXPTR + "}, [this=0x%" PRIXPTR "])", + context.state().AsString().Utf8().c_str(), + context.sampleRate(), number_of_channels, + reinterpret_cast<uintptr_t>(&Handler()), + reinterpret_cast<uintptr_t>(this))); } MediaStreamAudioDestinationNode* MediaStreamAudioDestinationNode::Create( @@ -167,10 +168,11 @@ } void MediaStreamAudioDestinationNode::SendLogMessage( - const char* const function_name, + const String& function_name, const String& message) { - WebRtcLogMessage(UNSAFE_TODO(String::Format("[WA]MSADN::%s %s", function_name, - message.Utf8().c_str())) + WebRtcLogMessage(String::Format("[WA]MSADN::%s %s", + function_name.Utf8().c_str(), + message.Utf8().c_str()) .Utf8()); }
diff --git a/third_party/blink/renderer/modules/webaudio/media_stream_audio_destination_node.h b/third_party/blink/renderer/modules/webaudio/media_stream_audio_destination_node.h index 4a2b948..0e160c7 100644 --- a/third_party/blink/renderer/modules/webaudio/media_stream_audio_destination_node.h +++ b/third_party/blink/renderer/modules/webaudio/media_stream_audio_destination_node.h
@@ -73,7 +73,7 @@ MediaStreamAudioDestinationHandler& GetOwnHandler() const; // https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/media/capture/README.md#logs - void SendLogMessage(const char* const function_name, const String& message); + void SendLogMessage(const String& function_name, const String& message); Member<MediaStreamSource> source_; Member<MediaStream> stream_;
diff --git a/third_party/blink/renderer/modules/webaudio/media_stream_audio_source_handler.cc b/third_party/blink/renderer/modules/webaudio/media_stream_audio_source_handler.cc index 37d57d3..cacf07aa 100644 --- a/third_party/blink/renderer/modules/webaudio/media_stream_audio_source_handler.cc +++ b/third_party/blink/renderer/modules/webaudio/media_stream_audio_source_handler.cc
@@ -126,14 +126,13 @@ } } -void MediaStreamAudioSourceHandler::SendLogMessage( - const char* const function_name, - const String& message) { - WebRtcLogMessage( - UNSAFE_TODO(String::Format("[WA]MSASH::%s %s [this=0x%" PRIXPTR "]", - function_name, message.Utf8().c_str(), - reinterpret_cast<uintptr_t>(this))) - .Utf8()); +void MediaStreamAudioSourceHandler::SendLogMessage(const String& function_name, + const String& message) { + WebRtcLogMessage(String::Format("[WA]MSASH::%s %s [this=0x%" PRIXPTR "]", + function_name.Utf8().c_str(), + message.Utf8().c_str(), + reinterpret_cast<uintptr_t>(this)) + .Utf8()); } } // namespace blink
diff --git a/third_party/blink/renderer/modules/webaudio/media_stream_audio_source_handler.h b/third_party/blink/renderer/modules/webaudio/media_stream_audio_source_handler.h index fc627a5..2c901744 100644 --- a/third_party/blink/renderer/modules/webaudio/media_stream_audio_source_handler.h +++ b/third_party/blink/renderer/modules/webaudio/media_stream_audio_source_handler.h
@@ -40,7 +40,7 @@ bool PropagatesSilence() const override { return false; } // https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/media/capture/README.md#logs - void SendLogMessage(const char* const function_name, const String& message); + void SendLogMessage(const String& function_name, const String& message); std::unique_ptr<AudioSourceProvider> audio_source_provider_;
diff --git a/third_party/blink/renderer/modules/webaudio/media_stream_audio_source_node.cc b/third_party/blink/renderer/modules/webaudio/media_stream_audio_source_node.cc index 9aa16ac4..80090eef 100644 --- a/third_party/blink/renderer/modules/webaudio/media_stream_audio_source_node.cc +++ b/third_party/blink/renderer/modules/webaudio/media_stream_audio_source_node.cc
@@ -151,11 +151,12 @@ return static_cast<MediaStreamAudioSourceHandler&>(Handler()); } -void MediaStreamAudioSourceNode::SendLogMessage(const char* const function_name, +void MediaStreamAudioSourceNode::SendLogMessage(const String& function_name, const String& message) { - WebRtcLogMessage(UNSAFE_TODO( - String::Format("[WA]MSASN::%s %s", function_name, message.Utf8().c_str()) - .Utf8())); + WebRtcLogMessage(String::Format("[WA]MSASN::%s %s", + function_name.Utf8().c_str(), + message.Utf8().c_str()) + .Utf8()); } } // namespace blink
diff --git a/third_party/blink/renderer/modules/webaudio/media_stream_audio_source_node.h b/third_party/blink/renderer/modules/webaudio/media_stream_audio_source_node.h index 84c742a..d4135fb 100644 --- a/third_party/blink/renderer/modules/webaudio/media_stream_audio_source_node.h +++ b/third_party/blink/renderer/modules/webaudio/media_stream_audio_source_node.h
@@ -79,7 +79,7 @@ MediaStreamAudioSourceHandler& GetMediaStreamAudioSourceHandler() const; // https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/media/capture/README.md#logs - void SendLogMessage(const char* const function_name, const String& message); + void SendLogMessage(const String& function_name, const String& message); Member<MediaStreamTrack> audio_track_; Member<MediaStream> media_stream_;
diff --git a/third_party/blink/renderer/modules/webaudio/realtime_audio_destination_handler.cc b/third_party/blink/renderer/modules/webaudio/realtime_audio_destination_handler.cc index 9a5eda5..1cc00b0 100644 --- a/third_party/blink/renderer/modules/webaudio/realtime_audio_destination_handler.cc +++ b/third_party/blink/renderer/modules/webaudio/realtime_audio_destination_handler.cc
@@ -541,14 +541,13 @@ } void RealtimeAudioDestinationHandler::SendLogMessage( - const char* const function_name, + const String& function_name, const String& message) const { - WebRtcLogMessage( - UNSAFE_TODO(String::Format("[WA]RADH::%s %s (sink_descriptor_=%s)", - function_name, message.Utf8().c_str(), - sink_descriptor_.SinkId().Utf8().c_str())) - .Utf8() - .c_str()); + WebRtcLogMessage(String::Format("[WA]RADH::%s %s (sink_descriptor_=%s)", + function_name.Utf8().c_str(), + message.Utf8().c_str(), + sink_descriptor_.SinkId().Utf8().c_str()) + .Utf8()); } } // namespace blink
diff --git a/third_party/blink/renderer/modules/webaudio/realtime_audio_destination_handler.h b/third_party/blink/renderer/modules/webaudio/realtime_audio_destination_handler.h index e5234be..b182108 100644 --- a/third_party/blink/renderer/modules/webaudio/realtime_audio_destination_handler.h +++ b/third_party/blink/renderer/modules/webaudio/realtime_audio_destination_handler.h
@@ -127,8 +127,7 @@ } // https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/media/capture/README.md#logs - void SendLogMessage(const char* const function_name, - const String& message) const; + void SendLogMessage(const String& function_name, const String& message) const; // Stores a sink descriptor for sink transition. WebAudioSinkDescriptor sink_descriptor_;
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn index 90b57dd..4de9c105 100644 --- a/third_party/blink/renderer/platform/BUILD.gn +++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -889,6 +889,7 @@ "graphics/box_reflection.h", "graphics/canvas_2d_color_params.cc", "graphics/canvas_2d_color_params.h", + "graphics/canvas_child_paint_record.h", "graphics/canvas_deferred_paint_record.cc", "graphics/canvas_deferred_paint_record.h", "graphics/canvas_hibernation_handler.cc",
diff --git a/third_party/blink/renderer/platform/audio/audio_destination.cc b/third_party/blink/renderer/platform/audio/audio_destination.cc index 1cac049..917f12ff 100644 --- a/third_party/blink/renderer/platform/audio/audio_destination.cc +++ b/third_party/blink/renderer/platform/audio/audio_destination.cc
@@ -68,7 +68,7 @@ // to play audio via Web Audio API. constexpr uint32_t kFIFOSize = 128 * 128; -const char* DeviceStateToString(AudioDestination::DeviceState state) { +const String DeviceStateToString(AudioDestination::DeviceState state) { switch (state) { case AudioDestination::kRunning: return "running"; @@ -443,9 +443,11 @@ callback_buffer_size_)); SendLogMessage(__func__, String::Format("=> (device sample rate=%.0f Hz)", web_audio_device_->SampleRate())); - SendLogMessage(__func__, UNSAFE_TODO(String::Format( - "Output buffer bypass: %s", - is_output_buffer_bypassed_ ? "yes" : "no"))); + if (is_output_buffer_bypassed_) { + SendLogMessage(__func__, "Output buffer bypass: yes"); + } else { + SendLogMessage(__func__, "Output buffer bypass: no"); + } TRACE_EVENT1("webaudio", "AudioDestination::AudioDestination", "sink information", @@ -681,12 +683,13 @@ frames_elapsed_ += previous_platform_destination->FramesElapsed(); } -void AudioDestination::SendLogMessage(const char* const function_name, +void AudioDestination::SendLogMessage(const String& function_name, const String& message) const { - WebRtcLogMessage(UNSAFE_TODO( - String::Format("[WA]AD::%s %s [state=%s]", function_name, - message.Utf8().c_str(), DeviceStateToString(device_state_)) - .Utf8())); + WebRtcLogMessage( + String::Format("[WA]AD::%s %s [state=%s]", function_name.Utf8().c_str(), + message.Utf8().c_str(), + DeviceStateToString(device_state_).Utf8().c_str()) + .Utf8()); } } // namespace blink
diff --git a/third_party/blink/renderer/platform/audio/audio_destination.h b/third_party/blink/renderer/platform/audio/audio_destination.h index 36babd5..b31c8ed 100644 --- a/third_party/blink/renderer/platform/audio/audio_destination.h +++ b/third_party/blink/renderer/platform/audio/audio_destination.h
@@ -202,8 +202,7 @@ void PullFromCallback(AudioBus* destination_bus, base::TimeDelta delay); // https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/media/capture/README.md#logs - void SendLogMessage(const char* const function_name, - const String& message) const; + void SendLogMessage(const String& function_name, const String& message) const; // Accessed by the main thread. std::unique_ptr<WebAudioDevice> web_audio_device_;
diff --git a/third_party/blink/renderer/platform/graphics/canvas_child_paint_record.h b/third_party/blink/renderer/platform/graphics/canvas_child_paint_record.h new file mode 100644 index 0000000..8d57e14 --- /dev/null +++ b/third_party/blink/renderer/platform/graphics/canvas_child_paint_record.h
@@ -0,0 +1,22 @@ +// Copyright 2026 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_PLATFORM_GRAPHICS_CANVAS_CHILD_PAINT_RECORD_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CANVAS_CHILD_PAINT_RECORD_H_ + +#include "cc/paint/paint_record.h" +#include "third_party/blink/renderer/platform/platform_export.h" +#include "ui/gfx/geometry/size_f.h" + +namespace blink { + +struct PLATFORM_EXPORT CanvasChildPaintRecord { + float scale = 1.f; + gfx::SizeF box_size; + cc::PaintRecord record; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GRAPHICS_CANVAS_CHILD_PAINT_RECORD_H_
diff --git a/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc b/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc index 254b16f..a5cc0ae3 100644 --- a/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc +++ b/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc
@@ -96,7 +96,8 @@ } void ContentLayerClientImpl::UpdateCcPictureLayer( - const PendingLayer& pending_layer) { + const PendingLayer& pending_layer, + PropertyTreeState property_state_for_paint) { const auto& paint_chunks = pending_layer.Chunks(); CHECK_EQ(cc_picture_layer_->client(), this); #if EXPENSIVE_DCHECKS_ARE_ON() @@ -136,6 +137,10 @@ paint_chunks[0].id.client_id)); } + canvas_child_box_size_ = layer_state.Effect().CanvasChildBoxSize(); + canvas_child_effective_zoom_ = + layer_state.Effect().CanvasChildEffectiveZoom(); + // Note: cc::Layer API assumes the layer bounds start at (0, 0), but the // bounding box of a paint chunk does not necessarily start at (0, 0) (and // could even be negative). Internally the generated layer translates the @@ -168,7 +173,7 @@ auto previous_display_list = std::move(cc_display_item_list_); cc_display_item_list_ = base::MakeRefCounted<cc::DisplayItemList>(); PaintChunksToCcLayer::ConvertInto( - paint_chunks, layer_state, layer_offset, + paint_chunks, property_state_for_paint, layer_offset, base::OptionalToPtr(raster_under_invalidation_params), *cc_display_item_list_); @@ -237,6 +242,20 @@ cc_picture_layer_->SetNeedsDisplayRect(rect); } +CanvasChildPaintRecord ContentLayerClientImpl::GetCanvasChildPaintRecord() + const { + gfx::Vector2dF offset = cc_picture_layer_->offset_to_transform_parent(); + if (offset.IsZero()) { + return {canvas_child_effective_zoom_, canvas_child_box_size_, + cc_display_item_list_->paint_op_buffer().DeepCopyAsRecord()}; + } + auto result = sk_make_sp<cc::PaintOpBuffer>(); + result->push<cc::TranslateOp>(offset.x(), offset.y()); + *result += cc_display_item_list_->paint_op_buffer(); + return {canvas_child_effective_zoom_, canvas_child_box_size_, + result->ReleaseAsRecord()}; +} + size_t ContentLayerClientImpl::ApproximateUnsharedMemoryUsage() const { return sizeof(*this) + raster_invalidator_->ApproximateUnsharedMemoryUsage() - sizeof(raster_invalidator_);
diff --git a/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.h b/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.h index 1fc81eb2..7e70338 100644 --- a/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.h +++ b/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.h
@@ -8,6 +8,7 @@ #include "base/dcheck_is_on.h" #include "cc/layers/content_layer_client.h" #include "cc/layers/picture_layer.h" +#include "third_party/blink/renderer/platform/graphics/canvas_child_paint_record.h" #include "third_party/blink/renderer/platform/graphics/compositing/layers_as_json.h" #include "third_party/blink/renderer/platform/graphics/paint/raster_invalidator.h" #include "third_party/blink/renderer/platform/heap/garbage_collected.h" @@ -44,12 +45,15 @@ cc::Layer& Layer() const { return *cc_picture_layer_.get(); } - void UpdateCcPictureLayer(const PendingLayer&); + void UpdateCcPictureLayer(const PendingLayer&, + PropertyTreeState property_state_for_paint); bool HasRasterInducingScroll() const; RasterInvalidator& GetRasterInvalidator() { return *raster_invalidator_; } + CanvasChildPaintRecord GetCanvasChildPaintRecord() const; + size_t ApproximateUnsharedMemoryUsage() const; private: @@ -59,6 +63,8 @@ scoped_refptr<cc::PictureLayer> cc_picture_layer_; scoped_refptr<cc::DisplayItemList> cc_display_item_list_; Member<RasterInvalidator> raster_invalidator_; + gfx::SizeF canvas_child_box_size_; + float canvas_child_effective_zoom_; // Used during UpdateCcPictureLayer(). bool has_empty_invalidations_ = false;
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc index 3d23450..2696f9c 100644 --- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc +++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.cc
@@ -105,6 +105,17 @@ } } +std::optional<CanvasChildPaintRecord> +PaintArtifactCompositor::GetCanvasChildPaintRecord(DOMNodeId child_id) const { + auto it = canvas_child_layer_map_.find(child_id); + if (it == canvas_child_layer_map_.end()) { + return std::nullopt; + } + auto& pending_layer = + pending_layers_[canvas_child_layer_map_.find(child_id)->value]; + return pending_layer.GetCanvasChildPaintRecord(); +} + void PaintArtifactCompositor::WillBeRemovedFromFrame() { root_layer_->RemoveAllChildren(); } @@ -512,6 +523,18 @@ return false; } +// When a child element of <canvas> is rendered via drawElementImage, its paint +// must be recorded using the canvas element's content clip and effect state. +PropertyTreeState GetPropertyTreeStateForPaint( + const PropertyTreeState& layer_state) { + PropertyTreeState result = layer_state; + if (layer_state.Effect().HasCanvasChildState()) { + result.SetClip(layer_state.Effect().CanvasChildContentClip()); + result.SetEffect(layer_state.Effect().CanvasChildContentEffect()); + } + return result; +} + } // namespace void PaintArtifactCompositor::SetNeedsUpdateInternal(UpdateType update_type) { @@ -1021,6 +1044,7 @@ wtf_size_t old_size = pending_layers_.size(); OldPendingLayerMatcher old_pending_layer_matcher(std::move(pending_layers_)); + canvas_child_layer_map_.clear(); CHECK(painted_scroll_translations_.empty()); // Make compositing decisions, storing the result in |pending_layers_|. @@ -1059,13 +1083,18 @@ cc::LayerSelection layer_selection; HashSet<int> layers_having_text; HashSet<int> layers_having_video; - for (auto& pending_layer : pending_layers_) { + for (wtf_size_t i = 0; i < pending_layers_.size(); i++) { + auto& pending_layer = pending_layers_[i]; + const auto& property_state = pending_layer.GetPropertyTreeState(); + PropertyTreeState property_state_for_paint = + GetPropertyTreeStateForPaint(property_state); + pending_layer.UpdateCompositedLayer( - old_pending_layer_matcher.Find(pending_layer), layer_selection, - tracks_raster_invalidations_, root_layer_->layer_tree_host()); + old_pending_layer_matcher.Find(pending_layer), property_state_for_paint, + layer_selection, tracks_raster_invalidations_, + root_layer_->layer_tree_host()); cc::Layer& layer = pending_layer.CcLayer(); - const auto& property_state = pending_layer.GetPropertyTreeState(); const auto& transform = property_state.Transform(); const auto& clip = property_state.Clip(); const auto& effect = property_state.Effect(); @@ -1120,7 +1149,11 @@ layer.SetEffectTreeIndex(effect_id); bool backface_hidden = transform.IsBackfaceHidden(); layer.SetShouldCheckBackfaceVisibility(backface_hidden); - layer.SetCanvasChildId(effect.CanvasChildId()); + if (effect.CanvasChildId()) { + canvas_child_layer_map_.Set(effect.CanvasChildId(), i); + layer.SetCanvasChildId( + CompositorElementIdFromDOMNodeId(effect.CanvasChildId())); + } if (layer.subtree_property_changed()) root_layer_->SetNeedsCommit(); @@ -1208,8 +1241,10 @@ case UpdateType::kRepaint: { cc::LayerSelection layer_selection; for (auto& pending_layer : pending_layers_) { - pending_layer.UpdateCompositedLayerForRepaint(repainted_artifact, - layer_selection); + PropertyTreeState property_state_for_paint = + GetPropertyTreeStateForPaint(pending_layer.GetPropertyTreeState()); + pending_layer.UpdateCompositedLayerForRepaint( + repainted_artifact, property_state_for_paint, layer_selection); } root_layer_->layer_tree_host()->RegisterSelection(layer_selection); UpdateDebugInfo();
diff --git a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h index b5ab345..6bcbffb 100644 --- a/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h +++ b/third_party/blink/renderer/platform/graphics/compositing/paint_artifact_compositor.h
@@ -217,6 +217,9 @@ void SetTracksRasterInvalidations(bool); + std::optional<CanvasChildPaintRecord> GetCanvasChildPaintRecord( + DOMNodeId child_id) const; + // Called when the local frame view that owns this compositor is // going to be removed from its frame. void WillBeRemovedFromFrame(); @@ -335,6 +338,7 @@ class OldPendingLayerMatcher; PendingLayers pending_layers_; + HashMap<DOMNodeId, wtf_size_t> canvas_child_layer_map_; class Layerizer;
diff --git a/third_party/blink/renderer/platform/graphics/compositing/pending_layer.cc b/third_party/blink/renderer/platform/graphics/compositing/pending_layer.cc index fa01d24a..5ec4714 100644 --- a/third_party/blink/renderer/platform/graphics/compositing/pending_layer.cc +++ b/third_party/blink/renderer/platform/graphics/compositing/pending_layer.cc
@@ -637,8 +637,10 @@ cc_layer_ = std::move(scrollbar_layer); } -void PendingLayer::UpdateContentLayer(PendingLayer* old_pending_layer, - bool tracks_raster_invalidations) { +void PendingLayer::UpdateContentLayer( + PendingLayer* old_pending_layer, + PropertyTreeState property_state_for_paint, + bool tracks_raster_invalidations) { DCHECK(!ChunkRequiresOwnLayer()); DCHECK(!cc_layer_); DCHECK(!content_layer_client_); @@ -651,7 +653,7 @@ content_layer_client_->GetRasterInvalidator().SetTracksRasterInvalidations( tracks_raster_invalidations); } - content_layer_client_->UpdateCcPictureLayer(*this); + content_layer_client_->UpdateCcPictureLayer(*this, property_state_for_paint); } void PendingLayer::UpdateSolidColorLayer(PendingLayer* old_pending_layer) { @@ -702,10 +704,12 @@ return chunks_[solid_color_chunk_index_].background_color.color; } -void PendingLayer::UpdateCompositedLayer(PendingLayer* old_pending_layer, - cc::LayerSelection& layer_selection, - bool tracks_raster_invalidations, - cc::LayerTreeHost* layer_tree_host) { +void PendingLayer::UpdateCompositedLayer( + PendingLayer* old_pending_layer, + PropertyTreeState property_state_for_paint, + cc::LayerSelection& layer_selection, + bool tracks_raster_invalidations, + cc::LayerTreeHost* layer_tree_host) { // This is used during PaintArifactCompositor::CollectPendingLayers() only. non_composited_scroll_translations_.clear(); @@ -724,7 +728,8 @@ if (UsesSolidColorLayer()) { UpdateSolidColorLayer(old_pending_layer); } else { - UpdateContentLayer(old_pending_layer, tracks_raster_invalidations); + UpdateContentLayer(old_pending_layer, property_state_for_paint, + tracks_raster_invalidations); } break; } @@ -742,6 +747,7 @@ void PendingLayer::UpdateCompositedLayerForRepaint( const PaintArtifact& repainted_artifact, + PropertyTreeState property_state_for_paint, cc::LayerSelection& layer_selection) { // Essentially replace the paint chunks of the pending layer with the // repainted chunks in |repainted_artifact|. The pending layer's paint @@ -778,7 +784,8 @@ content_layer_client_->GetRasterInvalidator().SetOldPaintArtifact( Chunks().GetPaintArtifact()); } else { - content_layer_client_->UpdateCcPictureLayer(*this); + content_layer_client_->UpdateCcPictureLayer(*this, + property_state_for_paint); } } } @@ -854,6 +861,14 @@ return background_color; } +std::optional<CanvasChildPaintRecord> PendingLayer::GetCanvasChildPaintRecord() + const { + if (!content_layer_client_) { + return std::nullopt; + } + return content_layer_client_->GetCanvasChildPaintRecord(); +} + bool PendingLayer::HasVideo() const { return Chunks().size() == 1 && FirstPaintChunk().size() == 1 && FirstDisplayItem().GetType() == DisplayItem::kForeignLayerVideo;
diff --git a/third_party/blink/renderer/platform/graphics/compositing/pending_layer.h b/third_party/blink/renderer/platform/graphics/compositing/pending_layer.h index 8784a88..d6f286b3 100644 --- a/third_party/blink/renderer/platform/graphics/compositing/pending_layer.h +++ b/third_party/blink/renderer/platform/graphics/compositing/pending_layer.h
@@ -81,6 +81,8 @@ chunks_.SetPaintArtifact(paint_artifact); } + std::optional<CanvasChildPaintRecord> GetCanvasChildPaintRecord() const; + using IsCompositedScrollFunction = PropertyTreeState::IsCompositedScrollFunction; @@ -160,14 +162,17 @@ // one in |old_pending_layer|, and updates the layer according to the current // contents and properties of this PendingLayer. void UpdateCompositedLayer(PendingLayer* old_pending_layer, + PropertyTreeState property_state_for_paint, cc::LayerSelection&, bool tracks_raster_invalidations, cc::LayerTreeHost*); // A lighter version of UpdateCompositedLayer(). Called when the existing // composited layer has only repainted since the last update - void UpdateCompositedLayerForRepaint(const PaintArtifact& repainted_artifact, - cc::LayerSelection&); + void UpdateCompositedLayerForRepaint( + const PaintArtifact& repainted_artifact, + PropertyTreeState property_state_for_paint, + cc::LayerSelection&); // Another lighter version of UpdateCompositedLayers(). Called after // raster-inducing scrolls that don't need repaint or PaintArtifactCompositor @@ -211,6 +216,7 @@ void UpdateScrollHitTestLayer(PendingLayer* old_pending_layer); void UpdateScrollbarLayer(PendingLayer* old_pending_layer); void UpdateContentLayer(PendingLayer* old_pending_layer, + PropertyTreeState property_state_for_paint, bool tracks_raster_invalidations); void UpdateSolidColorLayer(PendingLayer* old_pending_layer);
diff --git a/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.cc b/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.cc index 6ffbfd86..4bf51a18 100644 --- a/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.cc +++ b/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.cc
@@ -60,7 +60,7 @@ view_transition_element_resource_id != other.view_transition_element_resource_id || restriction_target_id != other.restriction_target_id || - canvas_child_id != other.canvas_child_id || + canvas_child_state != other.canvas_child_state || self_or_ancestor_participates_in_view_transition != other.self_or_ancestor_participates_in_view_transition || needs_effect_for_2d_scale_transform != @@ -119,6 +119,12 @@ void EffectPaintPropertyNode::State::Trace(Visitor* visitor) const { visitor->Trace(local_transform_space); visitor->Trace(output_clip); + visitor->Trace(canvas_child_state); +} + +void EffectPaintPropertyNode::CanvasChildState::Trace(Visitor* visitor) const { + visitor->Trace(content_effect); + visitor->Trace(content_clip); } EffectPaintPropertyNode::EffectPaintPropertyNode(RootTag) @@ -223,6 +229,18 @@ return state_.filter_info->operations.MapRect(input_rect); } +const EffectPaintPropertyNode& +EffectPaintPropertyNode::CanvasChildContentEffect() const { + CHECK(HasCanvasChildState()); + return state_.canvas_child_state.content_effect->Unalias(); +} + +const ClipPaintPropertyNode& EffectPaintPropertyNode::CanvasChildContentClip() + const { + CHECK(HasCanvasChildState()); + return state_.canvas_child_state.content_clip->Unalias(); +} + std::unique_ptr<JSONObject> EffectPaintPropertyNode::ToJSON() const { auto json = EffectPaintPropertyNodeOrAlias::ToJSON(); json->SetString("localTransformSpace",
diff --git a/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h b/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h index 4248d638..3b6fafec8 100644 --- a/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h +++ b/third_party/blink/renderer/platform/graphics/paint/effect_paint_property_node.h
@@ -20,6 +20,7 @@ namespace blink { +class ClipPaintPropertyNode; class ClipPaintPropertyNodeOrAlias; class PropertyTreeState; class TransformPaintPropertyNodeOrAlias; @@ -101,6 +102,23 @@ USING_FAST_MALLOC(BackdropFilterInfo); }; + // Used to associate this effect with a direct child of a canvas element for + // DrawElementImage. + struct PLATFORM_EXPORT CanvasChildState { + DISALLOW_NEW(); + + public: + bool operator==(const CanvasChildState&) const = default; + + DOMNodeId id = kInvalidDOMNodeId; + gfx::SizeF box_size; + float effective_zoom = 1.f; + Member<const EffectPaintPropertyNodeOrAlias> content_effect; + Member<const ClipPaintPropertyNodeOrAlias> content_clip; + + void Trace(Visitor*) const; + }; + // To make it less verbose and more readable to construct and update a node, // a struct with default values is used to represent the state. struct PLATFORM_EXPORT State { @@ -135,9 +153,7 @@ // Used to associate this effect node with its originating Element. RestrictionTargetId restriction_target_id; - // Used to associate this effect with a direct child of a canvas element - // for DrawElementImage. - CompositorElementId canvas_child_id; + CanvasChildState canvas_child_state; // When set, the affected elements should avoid doing clipping for // optimization purposes (like off-screen clipping). This is set by view @@ -364,10 +380,23 @@ return state_.restriction_target_id; } - const CompositorElementId& CanvasChildId() const { - return state_.canvas_child_id; + bool HasCanvasChildState() const { + return state_.canvas_child_state.id != kInvalidDOMNodeId; } + DOMNodeId CanvasChildId() const { return state_.canvas_child_state.id; } + + gfx::SizeF CanvasChildBoxSize() const { + return state_.canvas_child_state.box_size; + } + + float CanvasChildEffectiveZoom() const { + return state_.canvas_child_state.effective_zoom; + } + + const EffectPaintPropertyNode& CanvasChildContentEffect() const; + const ClipPaintPropertyNode& CanvasChildContentClip() const; + bool SelfOrAncestorParticipatesInViewTransition() const { return state_.self_or_ancestor_participates_in_view_transition; }
diff --git a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py index 3b1efb41..d371bf6c 100755 --- a/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py +++ b/third_party/blink/tools/blinkpy/presubmit/audit_non_blink_usage.py
@@ -1598,6 +1598,17 @@ }, { 'paths': [ + 'third_party/blink/renderer/core/html/html_credential_element.cc', + 'third_party/blink/renderer/core/html/html_credential_element.h', + 'third_party/blink/renderer/core/html/html_login_element.cc', + 'third_party/blink/renderer/core/html/html_login_element.h', + ], + 'allowed': [ + 'base::Value', + ], + }, + { + 'paths': [ 'third_party/blink/renderer/core/css/style_color.cc', 'third_party/blink/renderer/core/html/html_capability_element_base.cc', 'third_party/blink/renderer/core/paint/box_border_painter.cc',
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations index 0508c70..4468442 100644 --- a/third_party/blink/web_tests/TestExpectations +++ b/third_party/blink/web_tests/TestExpectations
@@ -800,7 +800,10 @@ ### ====== HTML-in-Canvas crbug.com/477575513 wpt_internal/html/canvas/drawElementImage/css-backdrop-filter-and-mask.html [ Failure ] -crbug.com/477575513 wpt_internal/html/canvas/drawElementImage/nested-webgl-canvas.html [ Failure ] +crbug.com/477575513 wpt_internal/html/canvas/drawElementImage/nested-webgl-canvas.html [ Skip Timeout ] +crbug.com/435609887 wpt_internal/html/canvas/drawElementImage/nested-canvas.html [ Skip Timeout ] +crbug.com/435609887 wpt_internal/html/canvas/drawElementImage/nested-div-canvas.html [ Skip Timeout ] +crbug.com/480074850 virtual/canvas-draw-element-in-subtree/draw-element-grandchild.html [ Failure Skip ] # WPT speculative-parsing tests failing crbug.com/1144176 external/wpt/html/syntax/speculative-parsing/generated/document-write/link-rel-alternate-stylesheet.tentative.sub.html [ Crash Failure ] @@ -2870,7 +2873,9 @@ crbug.com/476344902 virtual/webui-browser/external/wpt/html/browsers/windows/nested-browsing-contexts/window-top.html [ Failure ] # ====== New tests from wpt-importer added here ====== -external/wpt/webdriver/tests/bidi/emulation/set_network_conditions/user_contexts.py [ Failure ] +crbug.com/488373052 external/wpt/css/css-values/attr-namespace-non-existing.html [ Failure ] +crbug.com/488373052 external/wpt/css/css-values/attr-namespace-valid.xhtml [ Failure ] +crbug.com/488362582 external/wpt/webdriver/tests/bidi/emulation/set_network_conditions/user_contexts.py [ Failure ] crbug.com/488130147 [ Win ] external/wpt/WebCryptoAPI/encrypt_decrypt/aes_cbc.https.any.html [ Pass Timeout ] crbug.com/488130147 [ Win ] external/wpt/WebCryptoAPI/sign_verify/ecdsa.https.any.html [ Pass Timeout ] crbug.com/488171521 external/wpt/navigation-api/navigate-event/navigate-history-traversal-during-onnavigate-should-reject.html [ Timeout ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites index c2d679ff..5f9e1bc2 100644 --- a/third_party/blink/web_tests/VirtualTestSuites +++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -6269,5 +6269,20 @@ "--enable-features=WebAudioConfigurableRenderQuantum" ], "expires": "never" + }, + { + "prefix": "single-axis-scroll-containers", + "platforms": ["Linux", "Mac", "Win", "Android"], + "bases": [ + "external/wpt/css/css-position/sticky/position-sticky-single-axis-basic.html", + "external/wpt/css/css-position/sticky/position-sticky-single-axis-dynamic.html", + "external/wpt/css/css-position/sticky/position-sticky-single-axis-nested.html" + ], + "exclusive_tests": "ALL", + "args": ["--enable-blink-features=SingleAxisScrollContainers"], + "owners": [ + "freedebreuil@google.com" + ], + "expires": "Jan 1, 2027" } ]
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json index 710a2e4..4ce8e9f6 100644 --- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json +++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_8.json
@@ -77821,6 +77821,19 @@ {} ] ], + "align-out-of-flow-only-content.html": [ + "cd7750b8cdd29b341f5bf3663a512305fd6e0763", + [ + null, + [ + [ + "/css/css-align/abspos/align-out-of-flow-only-content-ref.html", + "==" + ] + ], + {} + ] + ], "align-self-static-position-001.html": [ "1ff73ef116d9d59786bf91c73c546a3f0f7e89df", [ @@ -161658,6 +161671,19 @@ {} ] ], + "custom-highlight-painting-line-wrap-001.html": [ + "8a9bbeb1c1ef87e5289f741f69bcdf7d94f5a7f3", + [ + null, + [ + [ + "/css/css-highlight-api/painting/custom-highlight-painting-line-wrap-001-ref.html", + "==" + ] + ], + {} + ] + ], "custom-highlight-painting-overlapping-highlights-001.html": [ "1129d369832bf53db7e9fc0ae87a1ecd5c19a0af", [ @@ -270732,6 +270758,45 @@ {} ] ], + "attr-namespace-non-existing.html": [ + "c1abc431e655ed37566264e42b66f20632ee2bb2", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square.xht", + "==" + ] + ], + {} + ] + ], + "attr-namespace-valid.xhtml": [ + "f5fdc184942b20cf98c2d92c08459944ba2f086a", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square.xht", + "==" + ] + ], + {} + ] + ], + "attr-namespace-wildcard.html": [ + "f9be5a7d8f46b6fdadac10751423f5b7d95cb733", + [ + null, + [ + [ + "/css/reference/ref-filled-green-100px-square.xht", + "==" + ] + ], + {} + ] + ], "attr-notype-fallback.html": [ "b88f3de4b571136c53c74486e6b63682bbd3291c", [ @@ -281883,6 +281948,19 @@ {} ] ], + "svg-stroke-width.html": [ + "ffc70ccad80f9497309ee0cabd445356bde95967", + [ + null, + [ + [ + "/css/css-viewport/zoom/svg-stroke-width-ref.html", + "==" + ] + ], + {} + ] + ], "svg-transform.html": [ "e0adf0725e86b43d2b73e7121704631012236988", [ @@ -325919,7 +325997,7 @@ ] ], "select-explicit-size.html": [ - "71c77d266d94feb2fd80837e3aa47bdfe2348cd9", + "970c1ebbc8561daad21c88c72d94f431e2b204bb", [ null, [ @@ -325928,7 +326006,23 @@ "==" ] ], - {} + { + "fuzzy": [ + [ + null, + [ + [ + 0, + 2 + ], + [ + 0, + 1 + ] + ] + ] + ] + } ] ], "select-font-size.html": [ @@ -360520,6 +360614,10 @@ "fd7093f98748709d0f6d03c9dc9993a7a1447446", [] ], + "align-out-of-flow-only-content-ref.html": [ + "7c69ac3bca6ef7190b7d2fb5c1ab0416bbf4ffa4", + [] + ], "align-self-static-position-001-ref.html": [ "94d0b43b2127080e961be8a7277865e2b6013d88", [] @@ -382846,6 +382944,10 @@ "88f3d0f3d65cf46cc56fae34166d6c38db170eb2", [] ], + "custom-highlight-painting-line-wrap-001-ref.html": [ + "f60a246c908470e1585050ac57eafd3187a3d733", + [] + ], "custom-highlight-painting-overlapping-highlights-001-ref.html": [ "3c08ad55aefe3cbb6670250b7c069e897f805977", [] @@ -406688,6 +406790,10 @@ "f5f349517c114b76e6a59b8eca4b5041a4e00e4f", [] ], + "svg-stroke-width-ref.html": [ + "14980cb77c5a2b324a1ba888fe72b04f0a7a3c0e", + [] + ], "svg-transform-ref.html": [ "abaed2accea09b8d4d6b790bab554ca83ac630ba", [] @@ -544824,6 +544930,20 @@ {} ] ], + "select-as-flex-item-child-with-overflow.html": [ + "394eb00e954141bcfe357772ae9ad278b9886144", + [ + null, + {} + ] + ], + "select-as-flex-item-with-overflow.html": [ + "d53c5309ef384d7e31d4c9cb044835238b332fff", + [ + null, + {} + ] + ], "shrinking-column-flexbox.html": [ "680dc7eb7f36e508437fab444660d58d03f00bfe", [ @@ -574684,7 +574804,7 @@ ] ], "svg-computed-style.html": [ - "fe7b69ac1544f4c3836133c915b5e8a747d5daf2", + "03df4792fb299ce3623a01820e44d16bb5209af8", [ null, {} @@ -588068,6 +588188,13 @@ } ] ], + "Range-extractContents-dynamic-end.html": [ + "71fcc9d7a4072dda74efa29f131600fad4e74622", + [ + null, + {} + ] + ], "Range-extractContents.html": [ "88f8fa55f8652fc189b4205f83f0b413e4fb7503", [ @@ -685356,7 +685483,7 @@ "focusgroup": { "tentative": { "ax-role-inference-children.html": [ - "04e3a7d819a21473c2d90c91cd5d251ad93095ed", + "a26b108d1b2dd380b3f7afa24b759206e0d4c95a", [ null, { @@ -685401,7 +685528,7 @@ ], "backward-navigation": { "does-not-move-when-on-focusgroup-root.html": [ - "97de0f7245df17bb3dbecbfadf826ccc9e25442a", + "00c7d5ca4a53caf88c82d172c8803725d8fbd797", [ null, { @@ -685410,7 +685537,7 @@ ] ], "does-not-move-when-on-non-focusgroup-item.html": [ - "bce137775608e3dd46ade6357ca3c80acb002303", + "c1f6dcb48b7aab579bbac29651412b2471946b3f", [ null, { @@ -685419,7 +685546,7 @@ ] ], "does-not-move-when-only-one-item-and-wraps.html": [ - "b31da027c9c19387a5c97f3f1733c87a27db2dee", + "d43146266b19f77a8182971b2600a3f1a6913d68", [ null, { @@ -685428,7 +685555,7 @@ ] ], "does-not-move-when-only-one-item.html": [ - "4bf74c3d452e8979fe90dd33f881711ffb716f99", + "1a54a0a0b68b72661951a5623245676dc324fc6f", [ null, { @@ -685437,7 +685564,7 @@ ] ], "does-not-move-when-outside-focusgroup.html": [ - "8bbe959f90fd5206cf158095a45889278761169c", + "3818bebfff0a55d5d9de4e053dada8a9351cd15d", [ null, { @@ -685446,7 +685573,7 @@ ] ], "does-not-wrap-when-not-supported.html": [ - "ea243d55da7eb231c82532ce2851387cddbd9b73", + "27c5bd3eff97d436cc8082e8e54a82aa1bcb38b7", [ null, { @@ -685502,7 +685629,7 @@ ] }, "moves-to-previous-item-and-skips-focusable-item.html": [ - "979ec15ae5cde4d624129b42f00f1d89cd850bde", + "cf52044e23cefef35d11894b79ede46bdf04739d", [ null, { @@ -685511,7 +685638,7 @@ ] ], "moves-to-previous-item.html": [ - "bb9def8eefa771c13187f970b900e5476b46b438", + "fcc10ff46ebd2ceb4aac83475664302e871a33e3", [ null, { @@ -685520,7 +685647,7 @@ ] ], "skips-non-focusgroup-subtree.html": [ - "316fb9fb83b264b0523c41887c0a1f68b895cecb", + "06f2dea058a0ea8413156b38ee71756a72908a72", [ null, { @@ -685529,7 +685656,7 @@ ] ], "skips-root-focusgroup-complex-case.html": [ - "cf628ce988447882797576a0dc0bbc57a90458d5", + "1c800423a9cb440a2cb02c6b1a32a22fc399e626", [ null, { @@ -685538,7 +685665,7 @@ ] ], "skips-root-focusgroup.html": [ - "c3f27db4b01bc70a50e5a92bad6ff8341144d1a7", + "d87cc55af63a5fb0e784258719d490ebb193278a", [ null, { @@ -685594,7 +685721,7 @@ ] }, "wraps-successfully-complex-case.html": [ - "6129ac6625b4230b99d973116368418fc5475ceb", + "d6454b059a1d4d6de3a442b10c7257a66db7539b", [ null, { @@ -685603,7 +685730,7 @@ ] ], "wraps-successfully.html": [ - "600daad1bf93f3317b8633aac4b2ce5e695d40dd", + "97d765a8e73c754dfc0893b00c508568056ba368", [ null, { @@ -685622,7 +685749,7 @@ ] ], "behavior-tokens-comprehensive.html": [ - "b2a6841333a93427c0768bacba9d35a7d8c35d14", + "0af5c4882f1485f0228a5eabe170d713bf42bbf6", [ null, { @@ -685632,7 +685759,7 @@ ], "descendant-navigation": { "deeply-nested-items.html": [ - "d66a9632d861ce30676179c899eedad0d45cebc0", + "0bb26224812d8ca407f41ace654e1e2d6b6613f1", [ null, { @@ -685641,7 +685768,7 @@ ] ], "mixed-content-navigation.html": [ - "a64820c8e229459e3a909dc9f170866c2794bfc0", + "dfce06e08a5069f34fe9814dfb916048a02a7d63", [ null, { @@ -685659,7 +685786,7 @@ ] ], "various-element-types.html": [ - "c1a7301a788b7c46dd79990946b36d38369a4030", + "0255cb31c62a5137f134ca7597a9f98b7fae18d7", [ null, { @@ -685668,7 +685795,7 @@ ] ], "wrapping-with-descendants.html": [ - "a1f1422c5461a087147db142efb3a32e7ca4b4eb", + "7e02373616f90749cd49881048922f1b72eecba5", [ null, { @@ -685679,7 +685806,7 @@ }, "forward-navigation": { "does-not-move-when-on-focusgroup-root.html": [ - "a02151294768fad460d5240ee7c29227a4638e5b", + "f2ebe21afacd5c321ed1bb5517b83356e8b11ec1", [ null, { @@ -685688,7 +685815,7 @@ ] ], "does-not-move-when-on-non-item.html": [ - "f6d8ac2aa5343e56e7d8a07b312bf1e32dab043b", + "0c58bff90f705f198a73ed895a40ecc24ce12d02", [ null, { @@ -685697,7 +685824,7 @@ ] ], "does-not-move-when-only-one-item-and-wraps.html": [ - "34cd12360def9ba795cfe8bd98a516c838e9b0ca", + "290412894357da33a6f4d4648b12824e32f85f56", [ null, { @@ -685706,7 +685833,7 @@ ] ], "does-not-move-when-only-one-item.html": [ - "4e3dbf0b0a61e0b17edb8b05d50e061279935f0a", + "9373b00cd0d8b1488d4210f9d2ca56216b386b04", [ null, { @@ -685715,7 +685842,7 @@ ] ], "does-not-move-when-outside-focusgroup.html": [ - "446a170db144fc1abf4f33eba3c10bdc2475f9f4", + "59f06ec18db72705513e6870a55e9efc827e5714", [ null, { @@ -685724,7 +685851,7 @@ ] ], "does-not-wrap-when-not-supported.html": [ - "e2a79ebd33700a6cb296b1580770531dbe74175f", + "e453d095428b4625ffef71a8a8bb516537d14d16", [ null, { @@ -685771,7 +685898,7 @@ ] ], "moves-to-next-item-and-skips-non-focusable.html": [ - "221d1c36ed097898cdfe65cc6f99a087e07993be", + "8417daece39e7cd82dcee7fb86c9456b7ff4b883", [ null, { @@ -685780,7 +685907,7 @@ ] ], "moves-to-next-item.html": [ - "0072d8eb5f454ca7bd1ac6a1231d410f7f7db184", + "c43cbce2c11c3899a545dac8c87e12e6ece67d92", [ null, { @@ -685789,7 +685916,7 @@ ] ], "nested-focusgroup-is-item-of-parent.html": [ - "25912f34d410624496e17e256e4c2193576b0185", + "b1269b6b696d8896d438d1c508c592939fcf3cfc", [ null, { @@ -685798,7 +685925,7 @@ ] ], "rtl-direction-reverses-inline.html": [ - "8d093342343963e3d704e015ad3d477d61596c6b", + "5fb8ab16c832d5e360e6d55799f418965c501244", [ null, { @@ -685836,7 +685963,7 @@ ] }, "vertical-writing-mode.html": [ - "0b25ea98c79097f2854ffae7fe98bb417b5b9caf", + "b376f12be446fd635ba49c737bbc85e75cc8e5f1", [ null, { @@ -685845,7 +685972,7 @@ ] ], "wraps-successfully.html": [ - "5e558a6851a982e2f5f01b454d5ade16fbd0991e", + "0d6cf9ba8881c61aec4883e9be52891ab4abb9e5", [ null, { @@ -686030,7 +686157,7 @@ }, "opt-out-barriers": { "complex-nested-opt-out.html": [ - "413f8662e4e4870a7546e71aa465d76af6071974", + "bd3b3c1bd42ffa62833ae2a071adeed63fcd0f16", [ null, { @@ -686039,7 +686166,7 @@ ] ], "none-creates-barriers.html": [ - "b169ac6977e5aff3d3dbe02aa4f55e7c4bac2002", + "26cbfda22044323e84e45f6a4d566116880258be", [ null, { @@ -686133,7 +686260,7 @@ ] ], "arrow-key-handler-nested-focusgroup.html": [ - "74e28106b45d07dc2282ca2b920f3afc9a450d3f", + "15c8dc822d03df659b5a6aaeabcf514c1bc67f25", [ null, { @@ -686151,7 +686278,7 @@ ] ], "arrow-key-handler-scrollable-container.html": [ - "6b69441b481293f2a3af1ec9ad4725deddcd30dd", + "6e61344a70491ce553af92726c8c2100fada4107", [ null, { @@ -686160,7 +686287,7 @@ ] ], "arrow-key-handler-tab-escape.html": [ - "fbe52722328e5387bb1957a30a345bffb61d7891", + "1e4beb3fe923baa60605b8c9a23f7cf96fe14dff", [ null, { @@ -686187,7 +686314,7 @@ ] ], "basic-tab-behavior.html": [ - "e090232618781ccdc0ef0a90df180f8a1ae924fe", + "f53054652b279b1833164757bdb28d608582b4ac", [ null, { @@ -686196,7 +686323,7 @@ ] ], "dynamic-changes.html": [ - "77b92f5002589094b60dea686fe56f979721c4a3", + "c5b17847d778b2d8f91b268c1ce29bb0dbd861ce", [ null, { @@ -686214,7 +686341,7 @@ ] ], "focusgroup-segments.html": [ - "53f47d248dffcd89353149637194e6be806bbdf6", + "b72196cafd3a0d20a9a5c97b33af0dd08bf7e23a", [ null, { @@ -686223,7 +686350,7 @@ ] ], "guaranteed-tab-stop-priority.html": [ - "2bbf50ee0ba19a2ee39c648c9e557f238d79e699", + "5bc85e639174230ecaf820a7bba365305b311825", [ null, { @@ -686232,7 +686359,7 @@ ] ], "memory-behavior.html": [ - "d04706e94b45b93adc34627280211225105fa10c", + "269afcef98cd4152980ded369a52b876ef38ef89", [ null, { @@ -686241,7 +686368,7 @@ ] ], "nested-focusgroups.html": [ - "15ea05eda65803a02d11c3ed6250543c731ae079", + "6ca62a35e57747ed9d19eb2999e7e124f1dad00c", [ null, { @@ -686261,7 +686388,7 @@ ] ], "shadow-nested-scope.html": [ - "35b4b68d04c29eb08caabf673b09a11c01507cff", + "cf4c0771b255790ea4ff909a7da9a59f332fd6ce", [ null, { @@ -696516,7 +696643,7 @@ ] ], "switch-picker-appearance.html": [ - "272ebae2186cc67487a428f230181dffbe4fb549", + "9abb8d6132fb3fb0a271c99d12e75ffedd10fbe3", [ null, {
diff --git a/third_party/blink/web_tests/external/wpt/connection-allowlist/tentative/navigation-redirect.sub.window.js b/third_party/blink/web_tests/external/wpt/connection-allowlist/tentative/navigation-redirect.sub.window.js new file mode 100644 index 0000000..8479ac3 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/connection-allowlist/tentative/navigation-redirect.sub.window.js
@@ -0,0 +1,65 @@ +// META: script=/common/get-host-info.sub.js +// +// The following tests assume the policy `Connection-Allowlist: (response-origin)` has been set. +// Redirects from a connection-allowlisted URL should be blocked by default. + +const port = get_host_info().HTTP_PORT_ELIDED; +const SUCCESS = true; +const FAILURE = false; + +function redirect_test(origin, target_origin, expectation) { + promise_test(async t => { + const iframe = document.createElement("iframe"); + let received_message = false; + const handler = (e) => { + if (e.data === "loaded") { + received_message = true; + } + }; + window.addEventListener("message", handler); + t.add_cleanup(() => window.removeEventListener("message", handler)); + + const p = new Promise((resolve) => { + iframe.onload = () => { + // If onload fires, it might be the success page or an error page. + // We wait a short bit to ensure any postMessage has time to arrive. + step_timeout(() => resolve(), 50); + }; + iframe.onerror = () => resolve(); + }); + + const target_url = target_origin + "/connection-allowlist/tentative/resources/post-message.html"; + iframe.src = origin + "/common/redirect.py?status=302&location=" + encodeURIComponent(target_url); + document.body.appendChild(iframe); + await p; + document.body.removeChild(iframe); + + if (expectation === SUCCESS) { + assert_true(received_message, `Redirect from ${origin} to ${target_origin} should have succeeded.`); + } else { + assert_false(received_message, `Redirect from ${origin} to ${target_origin} should have failed.`); + } + }, `Redirect from ${origin} to ${target_origin} should ${expectation === SUCCESS ? "succeed" : "fail"}.`); +} + +// We're loading this page from `http://{{hosts[][]}}`. +// The connection allowlist header is `Connection-Allowlist: (response-origin)`. +// Thus, only `http://{{hosts[][]}}` is allowlisted for navigations. + +// Redirect from an allowlisted origin (same-origin): +// origin: http://{{hosts[][]}} (allowed by allowlist) +// target: http://{{hosts[][]}} (also allowed) +// This should FAIL because redirects are default-blocked for allowlisted navigations. +redirect_test("http://{{hosts[][]}}" + port, "http://{{hosts[][]}}" + port, FAILURE); + +// Redirect from an allowlisted origin to a different origin: +// origin: http://{{hosts[][]}} (allowed by allowlist) +// target: http://{{hosts[alt][]}} (not allowed) +// This should FAIL. +redirect_test("http://{{hosts[][]}}" + port, "http://{{hosts[alt][]}}" + port, FAILURE); + +// Initial navigation to a non-allowlisted origin: +// origin: http://{{hosts[alt][]}} (not allowed) +// This is blocked before the redirect even happens. +redirect_test("http://{{hosts[alt][]}}" + port, "http://{{hosts[][]}}" + port, FAILURE); +
diff --git a/third_party/blink/web_tests/external/wpt/connection-allowlist/tentative/navigation-redirect.sub.window.js.headers b/third_party/blink/web_tests/external/wpt/connection-allowlist/tentative/navigation-redirect.sub.window.js.headers new file mode 100644 index 0000000..b1a058fb --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/connection-allowlist/tentative/navigation-redirect.sub.window.js.headers
@@ -0,0 +1 @@ +Connection-Allowlist: (response-origin)
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/abspos/align-out-of-flow-only-content-ref.html b/third_party/blink/web_tests/external/wpt/css/css-align/abspos/align-out-of-flow-only-content-ref.html new file mode 100644 index 0000000..7c69ac3 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-align/abspos/align-out-of-flow-only-content-ref.html
@@ -0,0 +1,28 @@ +<!DOCTYPE html> +<style> +.container { + background: blue; + position: relative; + width: 100px; + height: 100px; + display: inline-block; + margin-right: 5px; +} + +.child { + width: 50px; + height: 50px; + position: relative; + background: green; + bottom: -100px; +} +</style> +<div class="container"> + <div class="child"></div> +</div> +<div class="container"> + <div class="child"></div> +</div> +<div class="container"> + <div class="child"></div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-align/abspos/align-out-of-flow-only-content.html b/third_party/blink/web_tests/external/wpt/css/css-align/abspos/align-out-of-flow-only-content.html new file mode 100644 index 0000000..cd7750b --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-align/abspos/align-out-of-flow-only-content.html
@@ -0,0 +1,39 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-align/#align-justify-content"> +<link rel="match" href="align-out-of-flow-only-content-ref.html"> +<meta name="assert" content="Ensures that single, absolutely positioned elements take align-content into account."> +<style> +.container { + background: blue; + position: relative; + width: 100px; + height: 100px; + display: inline-block; + margin-right: 5px; + align-content: end; +} + +.abs { + width: 50px; + height: 50px; + position: absolute; + background: green; +} + +.positioned-inline { + left: 0; +} + +.positioned-block { + bottom: -50px; +} +</style> +<div class="container"> + <div class="abs"></div> +</div> +<div class="container"> + <div class="abs positioned-block"></div> +</div> +<div class="container"> + <div class="abs positioned-inline"></div> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/select-as-flex-item-child-with-overflow.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/select-as-flex-item-child-with-overflow.html new file mode 100644 index 0000000..394eb00e --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/select-as-flex-item-child-with-overflow.html
@@ -0,0 +1,37 @@ +<!DOCTYPE html> +<title>select as flex item's child with overflowing content</title> +<link rel="help" href="http://www.w3.org/TR/css-flexbox-1/" /> +<link rel="stylesheet" href="/fonts/ahem.css"> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<style> +.flexBox { + display: flex; + flex-direction: column; + overflow: scroll; + width: 100px; +} + +select { + font-family: Ahem; + width: 100%; +} +</style> +<p>Test passes if the flex box is not scrollable.</p> + +<div class=flexBox> + <div> + <select> + <option>this is a long long long long text this is a long long long long text</option> + </select> + </div> +</div> +<script> + test(function(t) + { + const flexBox = document.querySelector('.flexBox'); + assert_less_than_equal(flexBox.scrollWidth, 100); + }, "select content (option) should not trigger scrollable overflow."); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/select-as-flex-item-with-overflow.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/select-as-flex-item-with-overflow.html new file mode 100644 index 0000000..d53c530 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/select-as-flex-item-with-overflow.html
@@ -0,0 +1,34 @@ +<!DOCTYPE html> +<title>select as flex item with overflowing content</title> +<link rel="help" href="http://www.w3.org/TR/css-flexbox-1/" /> +<link rel="stylesheet" href="/fonts/ahem.css"> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<style> +.flexBox { + display: flex; + flex-direction: column; + width: 100px; + overflow: scroll; +} + +select { + font-family: Ahem; +} +</style> +<p>Test passes if the flex box is not scrollable.</p> + +<div class=flexBox> + <select> + <option>this is a long long long long text</option> + </select> +</div> +<script> + test(function(t) + { + const flexBox = document.querySelector('.flexBox'); + assert_less_than_equal(flexBox.scrollWidth, 100); + }, "select content (option) should not trigger scrollable overflow."); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-line-wrap-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-line-wrap-001-ref.html new file mode 100644 index 0000000..f60a246c --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-line-wrap-001-ref.html
@@ -0,0 +1,14 @@ +<!DOCTYPE html> +<meta charset="UTF-8"> +<link rel="stylesheet" type="text/css" href="/fonts/ahem.css"> +<style> + div { + font: 16px/1 Ahem; + width: 100px; + } + span { + background-color: green; + } +</style> +<body> +<div><span>one two three four five six seven eight nine ten</span></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-line-wrap-001.html b/third_party/blink/web_tests/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-line-wrap-001.html new file mode 100644 index 0000000..8a9bbeb1 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-highlight-api/painting/custom-highlight-painting-line-wrap-001.html
@@ -0,0 +1,25 @@ +<!DOCTYPE html> +<meta charset="UTF-8"> +<title>CSS Highlight API Test: no trailing whitespace painted at line wrap</title> +<link rel="help" href="https://drafts.csswg.org/css-highlight-api-1/"> +<link rel="match" href="custom-highlight-painting-line-wrap-001-ref.html"> +<link rel="stylesheet" type="text/css" href="/fonts/ahem.css"> +<meta name="assert" value="Highlight backgrounds must not extend over trailing whitespace at line breaks."> +<style> + div { + font: 16px/1 Ahem; + width: 100px; + } + ::highlight(test) { + background-color: green; + } +</style> +<body> +<div id="d">one two three four five six seven eight nine ten</div> +<script> + const text = document.getElementById("d").firstChild; + const r = new Range(); + r.setStart(text, 0); + r.setEnd(text, text.length); + CSS.highlights.set("test", new Highlight(r)); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-position/sticky/position-sticky-single-axis-basic.html b/third_party/blink/web_tests/external/wpt/css/css-position/sticky/position-sticky-single-axis-basic.html new file mode 100644 index 0000000..b432af8 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-position/sticky/position-sticky-single-axis-basic.html
@@ -0,0 +1,62 @@ +<!DOCTYPE html> +<title>Single-axis sticky constraints: Basic and Clip</title> +<link rel="help" href="https://drafts.csswg.org/css-position-3/#sticky-pos"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + .scroller-y { overflow-y: scroll; overflow-x: visible; width: 100px; height: 100px; margin-bottom: 20px; } + .scroller-x { overflow-x: scroll; overflow-y: visible; width: 100px; height: 200px; } + .clip-y { overflow-x: scroll; overflow-y: clip; width: 100px; height: 200px; } + .sticky { position: sticky; top: 10px; left: 20px; width: 10px; height: 10px; background: green; } + .spacer { width: 400px; height: 400px; } +</style> + +<!-- Standard independent scrollers. --> +<div id="outer-basic" class="scroller-y"> + <div id="inner-basic" class="scroller-x"> + <div id="sticky-basic" class="sticky"></div> + <div class="spacer"></div> + </div> +</div> + +<!-- Scroller with overflow: clip on one axis. --> +<div id="outer-clip" class="scroller-y"> + <div id="inner-clip" class="clip-y"> + <div id="sticky-clip" class="sticky" style="left: 10px;"></div> + <div class="spacer"></div> + </div> +</div> + +<script> +test(() => { + let outer = document.getElementById("outer-basic"); + let inner = document.getElementById("inner-basic"); + let sticky = document.getElementById("sticky-basic"); + + // Scroll both axes. The sticky element tracks 'inner' for the X axis and 'outer' for the Y axis. + inner.scrollLeft = 50; + outer.scrollTop = 60; + + let rOuter = outer.getBoundingClientRect(); + let rSticky = sticky.getBoundingClientRect(); + + assert_equals(rSticky.left - rOuter.left, 20, "Left constraint applies to inner scroller"); + assert_equals(rSticky.top - rOuter.top, 10, "Top constraint applies to outer scroller"); +}, "Constraints apply independently across nested single-axis scrollers"); + +test(() => { + let outer = document.getElementById("outer-clip"); + let inner = document.getElementById("inner-clip"); + let sticky = document.getElementById("sticky-clip"); + + // Scroll both axes. Because 'inner' is overflow-y: clip, the sticky element skips it and attaches to 'outer' for the Y axis. + inner.scrollLeft = 50; + outer.scrollTop = 60; + + let rOuter = outer.getBoundingClientRect(); + let rSticky = sticky.getBoundingClientRect(); + + assert_equals(rSticky.left - rOuter.left, 10, "Left constraint applies to inner scroller"); + assert_equals(rSticky.top - rOuter.top, 10, "Top constraint skips clip and applies to outer"); +}, "Constraints correctly skip axes that are overflow: clip"); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-position/sticky/position-sticky-single-axis-dynamic.html b/third_party/blink/web_tests/external/wpt/css/css-position/sticky/position-sticky-single-axis-dynamic.html new file mode 100644 index 0000000..b8f753c --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-position/sticky/position-sticky-single-axis-dynamic.html
@@ -0,0 +1,61 @@ +<!DOCTYPE html> +<title>Single-axis sticky constraints: Dynamic updates</title> +<link rel="help" href="https://drafts.csswg.org/css-position-3/#sticky-pos"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + #outer { overflow-y: scroll; overflow-x: visible; width: 100px; height: 100px; } + #inner { overflow-x: scroll; overflow-y: visible; width: 100px; height: 200px; } + + /* Only 'top' is set initially. */ + #sticky { position: sticky; top: 10px; width: 10px; height: 10px; background: green; } + .spacer { width: 400px; height: 400px; } +</style> + +<div id="outer"> + <div id="inner"> + <div id="sticky"></div> + <div class="spacer"></div> + </div> +</div> + +<script> +test(() => { + let outer = document.getElementById("outer"); + let inner = document.getElementById("inner"); + let sticky = document.getElementById("sticky"); + + // Scroll the X axis. Without a 'left' constraint, the element scrolls with the content. + inner.scrollLeft = 50; + + let rOuter = outer.getBoundingClientRect(); + let rSticky = sticky.getBoundingClientRect(); + assert_equals(rSticky.left - rOuter.left, -50, "Content scrolled left naturally (no constraint yet)"); + + // Scroll the Y axis. The 'top' constraint is active, so the element sticks. + outer.scrollTop = 60; + + rOuter = outer.getBoundingClientRect(); + rSticky = sticky.getBoundingClientRect(); + assert_equals(rSticky.top - rOuter.top, 10, "Sticky top is applied after Y scroll"); + + // Dynamically add a 'left' constraint. This merges the new X constraint with the existing Y constraint. + sticky.style.left = "20px"; + + rOuter = outer.getBoundingClientRect(); + rSticky = sticky.getBoundingClientRect(); + + assert_equals(rSticky.left - rOuter.left, 20, "Sticky left is merged and applied"); + assert_equals(rSticky.top - rOuter.top, 10, "Sticky top is preserved after merge"); + + // Dynamically remove the 'top' constraint. This forces a layout and verifies + // that the stale Y-axis constraint is not leaked/merged into the new fragment. + sticky.style.top = "auto"; + + rOuter = outer.getBoundingClientRect(); + rSticky = sticky.getBoundingClientRect(); + + assert_equals(rSticky.left - rOuter.left, 20, "Sticky left remains applied after Y is removed"); + assert_equals(rSticky.top - rOuter.top, -60, "Sticky top is removed and element scrolls naturally"); +}, "Sequential scrolling and dynamic constraint updates merge and remove correctly"); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-position/sticky/position-sticky-single-axis-nested.html b/third_party/blink/web_tests/external/wpt/css/css-position/sticky/position-sticky-single-axis-nested.html new file mode 100644 index 0000000..2c66bcfe --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-position/sticky/position-sticky-single-axis-nested.html
@@ -0,0 +1,46 @@ +<!DOCTYPE html> +<title>Single-axis sticky constraints: Nested elements</title> +<link rel="help" href="https://drafts.csswg.org/css-position-3/#sticky-pos"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + #outer { overflow-y: scroll; overflow-x: visible; width: 200px; height: 200px; } + #inner { overflow-x: scroll; overflow-y: visible; width: 200px; height: 1000px; } + #sticky-outer { position: sticky; top: 10px; left: 20px; width: 100px; height: 60px; background: lightblue; } + #sticky-inner { position: sticky; top: 25px; left: 40px; width: 50px; height: 20px; background: blue; } + .spacer { width: 1000px; height: 1000px; } +</style> + +<div id="outer"> + <div id="inner"> + <div id="sticky-outer"> + <div id="sticky-inner"></div> + </div> + <div class="spacer"></div> + </div> +</div> + +<script> +test(() => { + let outer = document.getElementById("outer"); + let inner = document.getElementById("inner"); + let stickyOuter = document.getElementById("sticky-outer"); + let stickyInner = document.getElementById("sticky-inner"); + + // Scroll both axes to activate sticky offsets. + inner.scrollLeft = 50; + outer.scrollTop = 60; + + let rOuter = outer.getBoundingClientRect(); + let rStickyOuter = stickyOuter.getBoundingClientRect(); + let rStickyInner = stickyInner.getBoundingClientRect(); + + // Verify the outer sticky element resolves against its own constraints. + assert_equals(rStickyOuter.left - rOuter.left, 20, "Outer sticky left"); + assert_equals(rStickyOuter.top - rOuter.top, 10, "Outer sticky top"); + + // Verify the inner sticky element accumulates offsets from the outer sticky element. + assert_equals(rStickyInner.left - rOuter.left, 40, "Inner sticky left accumulates"); + assert_equals(rStickyInner.top - rOuter.top, 25, "Inner sticky top accumulates"); +}, "Nested sticky elements accumulate offsets correctly on independent axes"); +</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/attr-namespace-non-existing.html b/third_party/blink/web_tests/external/wpt/css/css-values/attr-namespace-non-existing.html new file mode 100644 index 0000000..c1abc43 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-values/attr-namespace-non-existing.html
@@ -0,0 +1,17 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-values-5/#attr-notation"> +<link rel="match" href="../reference/ref-filled-green-100px-square.xht"> +<style> +.a { + background: red; + height: 50px; + width: 100px; +} + +.attr { + background: attr(foo|bar type(*), green); +} +</style> +<p>Test passes if there is a filled green square and <strong>no red</strong>.</p> +<div class="a attr" bar="red"></div> +<div class="a attr"></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/attr-namespace-valid.xhtml b/third_party/blink/web_tests/external/wpt/css/css-values/attr-namespace-valid.xhtml new file mode 100644 index 0000000..f5fdc18 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-values/attr-namespace-valid.xhtml
@@ -0,0 +1,24 @@ +<!DOCTYPE xhtml> +<html xmlns="http://www.w3.org/1999/xhtml" xmlns:color="http://www.example.com/"> +<head> +<link rel="help" href="https://drafts.csswg.org/css-values-5/#attr-notation"></link> +<link rel="match" href="../reference/ref-filled-green-100px-square.xht"></link> + <style> + @namespace color "http://www.example.com/"; + + div { + background-color: red; + } + + .a { + width: 100px; + height: 100px; + background-color: attr(color|myAttr type(*), red); + } + </style> +</head> +<body> + <p>Test passes if there is a filled green square and <strong>no red</strong>.</p> + <div class="a" color:myAttr="green"></div> +</body> +</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/attr-namespace-wildcard.html b/third_party/blink/web_tests/external/wpt/css/css-values/attr-namespace-wildcard.html new file mode 100644 index 0000000..f9be5a7d --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-values/attr-namespace-wildcard.html
@@ -0,0 +1,17 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-values-5/#attr-notation"> +<link rel="match" href="../reference/ref-filled-green-100px-square.xht"> +<meta name="assert" content="Wildcard not supported in attr() function"> +<style> +.a { + background: green; + height: 100px; + width: 100px; +} + +.attr { + background: attr(*|bar type(*)); +} +</style> +<p>Test passes if there is a filled green square and <strong>no red</strong>.</p> +<div class="a attr" bar="red"></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-viewport/zoom/svg-computed-style.html b/third_party/blink/web_tests/external/wpt/css/css-viewport/zoom/svg-computed-style.html index fe7b69a..03df479 100644 --- a/third_party/blink/web_tests/external/wpt/css/css-viewport/zoom/svg-computed-style.html +++ b/third_party/blink/web_tests/external/wpt/css/css-viewport/zoom/svg-computed-style.html
@@ -85,6 +85,10 @@ "value": "2px", "otherValues": ["4px"] }, + "stroke-width": { + "value": "2px", + "otherValues": ["4px"] + }, "width": { "value": "9px", "otherValues": ["19px"]
diff --git a/third_party/blink/web_tests/external/wpt/css/css-viewport/zoom/svg-stroke-width-ref.html b/third_party/blink/web_tests/external/wpt/css/css-viewport/zoom/svg-stroke-width-ref.html new file mode 100644 index 0000000..14980cb --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-viewport/zoom/svg-stroke-width-ref.html
@@ -0,0 +1,17 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS zoom does not scale SVG stroke-width when defined on svg element - reference</title> +<style> +body { margin: 0; } +</style> +<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 100 100" + fill="none" stroke="black" stroke-width="2"> + <rect x="10" y="10" width="80" height="80"/> +</svg> + +<div> + <svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 100 100" + fill="none" stroke="black" stroke-width="2"> + <rect x="10" y="10" width="80" height="80"/> + </svg> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-viewport/zoom/svg-stroke-width.html b/third_party/blink/web_tests/external/wpt/css/css-viewport/zoom/svg-stroke-width.html new file mode 100644 index 0000000..ffc70cc --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-viewport/zoom/svg-stroke-width.html
@@ -0,0 +1,22 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS zoom does not scale SVG stroke-width when defined on svg element</title> +<link rel="help" href="https://drafts.csswg.org/css-viewport/#zoom-property"> +<link rel="help" href="https://drafts.fxtf.org/fill-stroke-3/#propdef-stroke-width"> +<link rel="match" href="svg-stroke-width-ref.html"> +<style> +body { margin: 0; } +.zoom { zoom: 2; } +</style> + +<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100" + fill="none" stroke="black" stroke-width="2" style="zoom: 2;"> + <rect x="10" y="10" width="80" height="80"/> +</svg> + +<div class="zoom"> + <svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100" + fill="none" stroke="black" stroke-width="2"> + <rect x="10" y="10" width="80" height="80"/> + </svg> +</div>
diff --git a/third_party/blink/web_tests/external/wpt/html/interaction/focus/focusgroup/tentative/forward-navigation/nested-focusgroup-is-item-of-parent.html b/third_party/blink/web_tests/external/wpt/html/interaction/focus/focusgroup/tentative/forward-navigation/nested-focusgroup-is-item-of-parent.html index b1269b6..25912f34 100644 --- a/third_party/blink/web_tests/external/wpt/html/interaction/focus/focusgroup/tentative/forward-navigation/nested-focusgroup-is-item-of-parent.html +++ b/third_party/blink/web_tests/external/wpt/html/interaction/focus/focusgroup/tentative/forward-navigation/nested-focusgroup-is-item-of-parent.html
@@ -14,11 +14,11 @@ <button id=before tabindex=0>before</button> -<div id=outer focusgroup="toolbar nomemory"> +<div id=outer focusgroup="toolbar no-memory"> <button id=btn1>btn1</button> <button id=btn2>btn2</button> <button id=btn3>btn3</button> - <div id=inner focusgroup="toolbar nomemory" tabindex=0> + <div id=inner focusgroup="toolbar no-memory" tabindex=0> <button id=inner_btn1>inner btn1</button> <button id=inner_btn2>inner btn2</button> <button id=inner_btn3>inner btn3</button>
diff --git a/third_party/blink/web_tests/external/wpt/html/interaction/focus/focusgroup/tentative/sequential-navigation/arrow-key-handler-tab-escape.html b/third_party/blink/web_tests/external/wpt/html/interaction/focus/focusgroup/tentative/sequential-navigation/arrow-key-handler-tab-escape.html index 1e4beb3f..fbe52722 100644 --- a/third_party/blink/web_tests/external/wpt/html/interaction/focus/focusgroup/tentative/sequential-navigation/arrow-key-handler-tab-escape.html +++ b/third_party/blink/web_tests/external/wpt/html/interaction/focus/focusgroup/tentative/sequential-navigation/arrow-key-handler-tab-escape.html
@@ -14,7 +14,7 @@ <div id=before tabindex=0>Before focusgroup</div> -<div id=toolbar focusgroup="toolbar nomemory"> +<div id=toolbar focusgroup="toolbar no-memory"> <button id=bold type="button">Bold</button> <!-- Native text input is a native arrow key handler for both axes. --> <input id=search type="text" value="Search" />
diff --git a/third_party/blink/web_tests/external/wpt/html/interaction/focus/focusgroup/tentative/sequential-navigation/basic-tab-behavior.html b/third_party/blink/web_tests/external/wpt/html/interaction/focus/focusgroup/tentative/sequential-navigation/basic-tab-behavior.html index f530546..e090232 100644 --- a/third_party/blink/web_tests/external/wpt/html/interaction/focus/focusgroup/tentative/sequential-navigation/basic-tab-behavior.html +++ b/third_party/blink/web_tests/external/wpt/html/interaction/focus/focusgroup/tentative/sequential-navigation/basic-tab-behavior.html
@@ -14,7 +14,7 @@ <!-- Basic focusgroup for entry/exit testing --> <div id=before tabindex=0>Before focusgroup</div> -<div id=focusgroup1 focusgroup="toolbar nomemory"> +<div id=focusgroup1 focusgroup="toolbar no-memory"> <span id=item1 tabindex=0>Item 1</span> <span id=item2 tabindex=0>Item 2</span> <span id=item3 tabindex=0>Item 3</span>
diff --git a/third_party/blink/web_tests/external/wpt/html/interaction/focus/focusgroup/tentative/sequential-navigation/dynamic-changes.html b/third_party/blink/web_tests/external/wpt/html/interaction/focus/focusgroup/tentative/sequential-navigation/dynamic-changes.html index c5b17847..77b92f5 100644 --- a/third_party/blink/web_tests/external/wpt/html/interaction/focus/focusgroup/tentative/sequential-navigation/dynamic-changes.html +++ b/third_party/blink/web_tests/external/wpt/html/interaction/focus/focusgroup/tentative/sequential-navigation/dynamic-changes.html
@@ -14,7 +14,7 @@ <!-- Test focusgroupstart changes during navigation --> <div id=before1 tabindex=0>Before test 1</div> -<div id=fg1 focusgroup="toolbar nomemory"> +<div id=fg1 focusgroup="toolbar no-memory"> <span id=item1 tabindex=0>Item 1</span> <span id=item2 tabindex=0>Item 2</span> </div> @@ -24,7 +24,7 @@ <!-- Test disabled element changes --> <div id=before2 tabindex=0>Before test 2</div> -<div id=fg2 focusgroup="toolbar nomemory"> +<div id=fg2 focusgroup="toolbar no-memory"> <button id=btn1>Button 1</button> <button id=btn2 disabled>Button 2</button> </div>
diff --git a/third_party/blink/web_tests/external/wpt/html/interaction/focus/focusgroup/tentative/sequential-navigation/focusgroup-segments.html b/third_party/blink/web_tests/external/wpt/html/interaction/focus/focusgroup/tentative/sequential-navigation/focusgroup-segments.html index b72196c..c34aa9d 100644 --- a/third_party/blink/web_tests/external/wpt/html/interaction/focus/focusgroup/tentative/sequential-navigation/focusgroup-segments.html +++ b/third_party/blink/web_tests/external/wpt/html/interaction/focus/focusgroup/tentative/sequential-navigation/focusgroup-segments.html
@@ -29,7 +29,7 @@ <!-- Test complex nested opt-out --> <div id=before2 tabindex=0>Before complex</div> -<div id=complex-focusgroup focusgroup="toolbar nomemory"> +<div id=complex-focusgroup focusgroup="toolbar no-memory"> <button id=item1 focusgroupstart>Item 1 (priority)</button> <div id=nested-opt-out focusgroup="none"> <button id=opted-out-1>Opted out 1</button>
diff --git a/third_party/blink/web_tests/external/wpt/html/interaction/focus/focusgroup/tentative/sequential-navigation/guaranteed-tab-stop-priority.html b/third_party/blink/web_tests/external/wpt/html/interaction/focus/focusgroup/tentative/sequential-navigation/guaranteed-tab-stop-priority.html index 5bc85e63..2bbf50e 100644 --- a/third_party/blink/web_tests/external/wpt/html/interaction/focus/focusgroup/tentative/sequential-navigation/guaranteed-tab-stop-priority.html +++ b/third_party/blink/web_tests/external/wpt/html/interaction/focus/focusgroup/tentative/sequential-navigation/guaranteed-tab-stop-priority.html
@@ -14,7 +14,7 @@ <!-- Priority Tier 1: focusgroupstart attribute --> <div id=before1 tabindex=0>Before test 1</div> -<div id=fg1 focusgroup="toolbar nomemory"> +<div id=fg1 focusgroup="toolbar no-memory"> <span id=item1 tabindex=0>Item 1 (no priority)</span> <span id=item2 tabindex=0 focusgroupstart>Item 2 (has priority)</span> <span id=item3 tabindex=0>Item 3 (no priority)</span> @@ -25,7 +25,7 @@ <!-- Priority Tier 2: First element in tree order when no priority attribute --> <div id=before2 tabindex=0>Before test 2</div> -<div id=fg2 focusgroup="toolbar nomemory"> +<div id=fg2 focusgroup="toolbar no-memory"> <span id=first tabindex=0>First (no priority)</span> <span id=second tabindex=0>Second (no priority)</span> </div> @@ -35,7 +35,7 @@ <!-- Priority Tier 3: First element with priority attribute when multiple have it --> <div id=before3 tabindex=0>Before test 3</div> -<div id=fg3 focusgroup="toolbar nomemory"> +<div id=fg3 focusgroup="toolbar no-memory"> <span id=early1 tabindex=0 focusgroupstart>Early (has priority)</span> <span id=early2 tabindex=0 focusgroupstart>Later (has priority)</span> </div> @@ -45,7 +45,7 @@ <!-- Test with buttons (natively focusable) --> <div id=before4 tabindex=0>Before test 4</div> -<div id=fg4 focusgroup="toolbar nomemory"> +<div id=fg4 focusgroup="toolbar no-memory"> <button id=btn1>Button 1</button> <button id=btn2 focusgroupstart>Button 2 (has priority)</button> <button id=btn3>Button 3</button>
diff --git a/third_party/blink/web_tests/external/wpt/html/interaction/focus/focusgroup/tentative/sequential-navigation/memory-behavior.html b/third_party/blink/web_tests/external/wpt/html/interaction/focus/focusgroup/tentative/sequential-navigation/memory-behavior.html index 269afcef..d04706e9 100644 --- a/third_party/blink/web_tests/external/wpt/html/interaction/focus/focusgroup/tentative/sequential-navigation/memory-behavior.html +++ b/third_party/blink/web_tests/external/wpt/html/interaction/focus/focusgroup/tentative/sequential-navigation/memory-behavior.html
@@ -23,7 +23,7 @@ <div id=between tabindex=0>Between focusgroups</div> <!-- Test focusgroup with no-memory --> -<div id=no-memory-focusgroup focusgroup="toolbar nomemory"> +<div id=no-memory-focusgroup focusgroup="toolbar no-memory"> <button id=no-memory-item1>Item 1</button> <button id=no-memory-item2 focusgroupstart>Item 2 (priority)</button> <button id=no-memory-item3>Item 3</button>
diff --git a/third_party/blink/web_tests/external/wpt/html/interaction/focus/focusgroup/tentative/sequential-navigation/nested-focusgroups.html b/third_party/blink/web_tests/external/wpt/html/interaction/focus/focusgroup/tentative/sequential-navigation/nested-focusgroups.html index 6ca62a35..15ea05e 100644 --- a/third_party/blink/web_tests/external/wpt/html/interaction/focus/focusgroup/tentative/sequential-navigation/nested-focusgroups.html +++ b/third_party/blink/web_tests/external/wpt/html/interaction/focus/focusgroup/tentative/sequential-navigation/nested-focusgroups.html
@@ -16,7 +16,7 @@ <div id=outer focusgroup="toolbar"> <span id=outer1 tabindex=0 focusgroupstart>Outer 1 (priority)</span> - <div id=inner focusgroup="toolbar nomemory"> + <div id=inner focusgroup="toolbar no-memory"> <span id=inner1 tabindex=0 focusgroupstart>Inner 1 (priority)</span> <span id=inner2 tabindex=0>Inner 2</span> </div>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-explicit-size.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-explicit-size.html index 71c77d2..970c1ebb 100644 --- a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-explicit-size.html +++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-select-element/customizable-select/select-explicit-size.html
@@ -2,7 +2,7 @@ <!-- Tests that select respects explicit size --> <link rel=author href="mailto:pkotwicz@chromium.org"> <link rel="match" href="select-explicit-size-ref.html"> - +<meta name="fuzzy" content="maxDifference=0-2; totalPixels=0-1"> <style> select { width:400px;
diff --git a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt index 10bf8f3..3cc299f 100644 --- a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt +++ b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-navigated-expected.txt
@@ -142,6 +142,7 @@ PASS oldChildWindow.onclick is newChildWindow.onclick PASS oldChildWindow.onclose is newChildWindow.onclose PASS oldChildWindow.oncommand is newChildWindow.oncommand +PASS oldChildWindow.oncomplete is newChildWindow.oncomplete PASS oldChildWindow.oncontentvisibilityautostatechange is newChildWindow.oncontentvisibilityautostatechange PASS oldChildWindow.oncontextlost is newChildWindow.oncontextlost PASS oldChildWindow.oncontextmenu is newChildWindow.oncontextmenu
diff --git a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt index 936de40..92e6909 100644 --- a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt +++ b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-and-gced-expected.txt
@@ -81,6 +81,7 @@ PASS childWindow.onclick is null PASS childWindow.onclose is null PASS childWindow.oncommand is null +PASS childWindow.oncomplete is null PASS childWindow.oncontentvisibilityautostatechange is null PASS childWindow.oncontextlost is null PASS childWindow.oncontextmenu is null
diff --git a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt index c853ce4..757ea368 100644 --- a/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt +++ b/third_party/blink/web_tests/fast/dom/Window/property-access-on-cached-window-after-frame-removed-expected.txt
@@ -81,6 +81,7 @@ PASS childWindow.onclick is null PASS childWindow.onclose is null PASS childWindow.oncommand is null +PASS childWindow.oncomplete is null PASS childWindow.oncontentvisibilityautostatechange is null PASS childWindow.oncontextlost is null PASS childWindow.oncontextmenu is null
diff --git a/third_party/blink/web_tests/fast/forms/number/number-appearance-vertical.html b/third_party/blink/web_tests/fast/forms/number/number-appearance-vertical.html index 9b87351..9db27c6 100644 --- a/third_party/blink/web_tests/fast/forms/number/number-appearance-vertical.html +++ b/third_party/blink/web_tests/fast/forms/number/number-appearance-vertical.html
@@ -1,5 +1,6 @@ <!DOCTYPE html> <meta name=fuzzy content="maxDifference=0-3; totalPixels=0-100"> +<script src="../../../resources/run-after-layout-and-paint.js"></script> <style> div { display: inline-block; @@ -45,6 +46,9 @@ <script> if (window.internals) { + internals.setIsCursorVisible(document, true); + internals.settings.setPrimaryHoverType('hover'); + internals.settings.setAvailableHoverTypes('hover'); for (let input of document.querySelectorAll('input')) { internals.setPseudoClassState(input, ':hover', true); } @@ -53,6 +57,14 @@ // A spinbutton should be showing on the top (left) input.focus(); } +if (window.testRunner) { + testRunner.waitUntilDone(); + runAfterLayoutAndPaint(function() { + runAfterLayoutAndPaint(function() { + testRunner.notifyDone(); + }); + }); +} </script> </body> </html>
diff --git a/third_party/blink/web_tests/virtual/single-axis-scroll-containers/README.md b/third_party/blink/web_tests/virtual/single-axis-scroll-containers/README.md new file mode 100644 index 0000000..a903fc2 --- /dev/null +++ b/third_party/blink/web_tests/virtual/single-axis-scroll-containers/README.md
@@ -0,0 +1,5 @@ +# SingleAxisScrollContainers + +Adds support for independent horizontal and vertical overflow behaviors. Elements can now scroll on one axis (e.g., overflow-x: scroll) while keeping the other axis clipped or visible (e.g., overflow-y: clip). + +This loosens the restriction that forced the non-scrolling axis into auto or hidden, allowing more flexible layouts.
diff --git a/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt b/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt index 48660bda..3f959678 100644 --- a/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt +++ b/third_party/blink/web_tests/webexposed/element-instance-property-listing-expected.txt
@@ -213,6 +213,7 @@ property onclick property onclose property oncommand + property oncomplete property oncontentvisibilityautostatechange property oncontextlost property oncontextmenu @@ -589,6 +590,13 @@ property vAlign property width html element credential + property clientId + property configURL + property domainHint + property fields + property loginHint + property params + property type html element data property value html element datalist @@ -912,6 +920,7 @@ html element listing property width html element login + property credential html element main html element map property areas @@ -1543,6 +1552,7 @@ property onclick property onclose property oncommand + property oncomplete property oncontentvisibilityautostatechange property oncontextlost property oncontextmenu
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt index c2edee1a..db45087 100644 --- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt +++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -2043,6 +2043,7 @@ getter onclick getter onclose getter oncommand + getter oncomplete getter oncontentvisibilityautostatechange getter oncontextlost getter oncontextmenu @@ -2269,6 +2270,7 @@ setter onclick setter onclose setter oncommand + setter oncomplete setter oncontentvisibilityautostatechange setter oncontextlost setter oncontextmenu @@ -3853,7 +3855,21 @@ method namedItem interface HTMLCredentialElement : HTMLElement attribute @@toStringTag + getter clientId + getter configURL + getter domainHint + getter fields + getter loginHint + getter params + getter type method constructor + setter clientId + setter configURL + setter domainHint + setter fields + setter loginHint + setter params + setter type interface HTMLDListElement : HTMLElement attribute @@toStringTag getter compact @@ -3945,6 +3961,7 @@ getter onclick getter onclose getter oncommand + getter oncomplete getter oncontentvisibilityautostatechange getter oncontextlost getter oncontextmenu @@ -4084,6 +4101,7 @@ setter onclick setter onclose setter oncommand + setter oncomplete setter oncontentvisibilityautostatechange setter oncontextlost setter oncontextmenu @@ -4703,6 +4721,7 @@ setter type interface HTMLLoginElement : HTMLElement attribute @@toStringTag + getter credential method constructor interface HTMLMapElement : HTMLElement attribute @@toStringTag @@ -6086,6 +6105,7 @@ getter onclick getter onclose getter oncommand + getter oncomplete getter oncontentvisibilityautostatechange getter oncontextlost getter oncontextmenu @@ -6200,6 +6220,7 @@ setter onclick setter onclose setter oncommand + setter oncomplete setter oncontentvisibilityautostatechange setter oncontextlost setter oncontextmenu @@ -8655,6 +8676,7 @@ getter onclick getter onclose getter oncommand + getter oncomplete getter oncontentvisibilityautostatechange getter oncontextlost getter oncontextmenu @@ -8771,6 +8793,7 @@ setter onclick setter onclose setter oncommand + setter oncomplete setter oncontentvisibilityautostatechange setter oncontextlost setter oncontextmenu @@ -13517,6 +13540,7 @@ getter onclick getter onclose getter oncommand + getter oncomplete getter oncontentvisibilityautostatechange getter oncontextlost getter oncontextmenu @@ -13751,6 +13775,7 @@ setter onclick setter onclose setter oncommand + setter oncomplete setter oncontentvisibilityautostatechange setter oncontextlost setter oncontextmenu
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/anchor-positioning.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/anchor-positioning.html index 17f91517..8a28be7 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/anchor-positioning.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/anchor-positioning.html
@@ -1,7 +1,9 @@ <!DOCTYPE html> +<html class="reftest-wait"> <title>Canvas.drawElementImage with out-of-canvas anchored element</title> <link rel="help" href="https://github.com/WICG/html-in-canvas"> <link rel="match" href="anchor-positioning-ref.html"> +<script src="/common/reftest-wait.js"></script> <style> #canvas { width: 200px; @@ -37,6 +39,8 @@ var context = canvas.getContext('2d'); let matrix = context.drawElementImage(anchor, 50, 50); anchor.style.transform = matrix.toString(); + takeScreenshot(); } -onload = () => runTest(); +onload = () => requestAnimationFrame(() => setTimeout(runTest)); </script> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/basic-rect-zoom.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/basic-rect-zoom.html index 5b640ad..39055a9 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/basic-rect-zoom.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/basic-rect-zoom.html
@@ -1,9 +1,10 @@ <!DOCTYPE html> -<html> +<html class="reftest-wait"> <title>Canvas.drawElementImage: green rect when zoomed</title> <link rel="help" href="https://github.com/WICG/html-in-canvas"> <link rel="author" href="mailto:schenney@chromium.org"> <link rel="match" href="basic-rect-ref.html"> +<script src="/common/reftest-wait.js"></script> <style> body { zoom: 200%; @@ -31,9 +32,10 @@ canvas.width = rectangle.width * devicePixelRatio; canvas.height = rectangle.height * devicePixelRatio; canvas.getContext("2d").drawElementImage(child, 20 * devicePixelRatio, 30 * devicePixelRatio); + takeScreenshot(); } -onload = () => runTest(); +onload = () => requestAnimationFrame(() => setTimeout(runTest)); </script> </html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/basic-rect.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/basic-rect.html index 3a980fb..43ee72b 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/basic-rect.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/basic-rect.html
@@ -1,9 +1,10 @@ <!DOCTYPE html> -<html> +<html class="reftest-wait"> <title>Canvas.drawElementImage: green rect</title> <link rel="help" href="https://github.com/WICG/html-in-canvas"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="basic-rect-ref.html"> +<script src="/common/reftest-wait.js"></script> <style> #child { width: 100px; @@ -22,9 +23,10 @@ <script> function runTest() { canvas.getContext("2d").drawElementImage(child, 20, 30); + takeScreenshot(); } -onload = () => runTest(); +onload = () => requestAnimationFrame(() => setTimeout(runTest)); </script> </html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/broken-image-ref.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/broken-image-ref.html new file mode 100644 index 0000000..5e4b6203 --- /dev/null +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/broken-image-ref.html
@@ -0,0 +1,31 @@ +<!DOCTYPE html> +<title>Canvas.drawElementImage: green rect with broken image (ref)</title> +<link rel="help" href="https://github.com/WICG/html-in-canvas"> +<link rel="author" href="mailto:szager@chromium.org"> +<style> +#child { + will-change: transform; + width: 100px; + height: 100px; + background: green; + + position: relative; + left: 20px; + top: 30px; +} +#canvas { + width: 200px; + height: 200px; + background: grey; +} +img { + width: 100px; + height: 100px; +} +</style> + +<div id=canvas> + <div id=child> + <img id=image src="resoces/red-green-animated.gif"/> + </div> +</div>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/clips-overflow.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/clips-overflow.html index 882b76a..f2451150 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/clips-overflow.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/clips-overflow.html
@@ -29,10 +29,10 @@ <script> function runTest() { canvas.getContext("2d").drawElementImage(child, 20, 30); - requestAnimationFrame(takeScreenshot); + takeScreenshot(); } -onload = () => runTest(); +onload = () => requestAnimationFrame(() => setTimeout(runTest)); </script> </html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/compositing-op-basic.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/compositing-op-basic.html index 09ceea01..e1865ca5 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/compositing-op-basic.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/compositing-op-basic.html
@@ -1,9 +1,10 @@ <!DOCTYPE html> -<html> +<html class="reftest-wait"> <title>Canvas.drawElementImage: compositing op green rect</title> <link rel="help" href="https://github.com/WICG/html-in-canvas"> <link rel="author" href="mailto:schenney@chromium.org"> <link rel="match" href="compositing-op-basic-ref.html"> +<script src="/common/reftest-wait.js"></script> <style> #child { width: 100px; @@ -26,9 +27,10 @@ context.fillRect(70, 80, 110, 100); context.globalCompositeOperation = "source-atop"; context.drawElementImage(child, 20, 30); + takeScreenshot(); } -onload = () => runTest(); +onload = () => requestAnimationFrame(() => setTimeout(runTest)); </script> </html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/compositing-op-non-opaque-element.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/compositing-op-non-opaque-element.html index be6190e..a4ff6fd 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/compositing-op-non-opaque-element.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/compositing-op-non-opaque-element.html
@@ -1,9 +1,10 @@ <!DOCTYPE html> -<html> +<html class="reftest-wait"> <title>Canvas.drawElementImage: non opaque element with compositing op</title> <link rel="help" href="https://github.com/WICG/html-in-canvas"> <link rel="author" href="mailto:schenney@chromium.org"> <link rel="match" href="compositing-op-non-opaque-element-ref.html"> +<script src="/common/reftest-wait.js"></script> <style> #child { width: 100px; @@ -37,9 +38,10 @@ context.fillRect(10, 10, 180, 180); context.globalCompositeOperation = "destination-out"; context.drawElementImage(child, 20, 30); + takeScreenshot(); } -onload = () => runTest(); +onload = () => requestAnimationFrame(() => setTimeout(runTest)); </script> </html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/containment.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/containment.html index 76946b0..91d8d83 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/containment.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/containment.html
@@ -1,5 +1,4 @@ <!DOCTYPE html> -<html class=reftest-wait> <title>Canvas.drawElementImage: styles for children of canvas</title> <link rel="help" href="https://github.com/WICG/html-in-canvas"> <link rel="author" href="mailto:chrishtr@chromium.org"> @@ -36,5 +35,5 @@ assert_equals(getComputedStyle(grandchild1)[a], grandchild_asserts[a]); assert_equals(getComputedStyle(grandchild2)[a], grandchild_asserts[a]); } - }, 'canvas child containment'); +}, 'canvas child containment'); </script>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/crash-on-load.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/crash-on-load.html index 8aa1930..9ed71fc 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/crash-on-load.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/crash-on-load.html
@@ -1,9 +1,10 @@ <!DOCTYPE html> -<html> +<html class="reftest-wait"> <title>Canvas.drawElementImage: drawElementImage with missing images doesn't crash</title> <link rel="help" href="https://github.com/WICG/html-in-canvas"> <link rel="author" href="mailto:schenney@chromium.org"> -<link rel="match" href="basic-rect-ref.html"> +<link rel="match" href="broken-image-ref.html"> +<script src="/common/reftest-wait.js"></script> <style> #child { width: 100px; @@ -26,7 +27,8 @@ <script> function runTest() { canvas.getContext("2d").drawElementImage(child, 20, 30); + takeScreenshot(); } -runTest(); +onload = () => requestAnimationFrame(() => setTimeout(runTest)); </script> -</html> \ No newline at end of file +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-backdrop-filter-and-mask.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-backdrop-filter-and-mask.html index dd25700..7b66227 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-backdrop-filter-and-mask.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-backdrop-filter-and-mask.html
@@ -1,10 +1,12 @@ <!DOCTYPE html> +<html class="reftest-wait"> <head> <title>drawElementImage paints CSS backdrop filter with mask image</title> <link rel="help" href="https://github.com/WICG/html-in-canvas"> <link rel="author" href="mailto:schenney@chromium.org"> <link rel="match" href="css-backdrop-filter-and-mask-ref.html"> <meta name="assert" value="A div with a CSS backdrop-filter and mask paints correctly."> + <script src="/common/reftest-wait.js"></script> <style> canvas { background: red; @@ -30,9 +32,12 @@ </canvas> <script> - window.onload = () => { + function runTest() { var context = canvas.getContext("2d"); context.drawElementImage(child, 0, 0); + takeScreenshot(); } + onload = () => requestAnimationFrame(() => setTimeout(runTest)); </script> -</body> \ No newline at end of file +</body> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-backdrop-filter-on-div.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-backdrop-filter-on-div.html index 3066a18..24cb54c 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-backdrop-filter-on-div.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-backdrop-filter-on-div.html
@@ -1,10 +1,12 @@ <!DOCTYPE html> +<html class="reftest-wait"> <head> <title>drawElementImage paints CSS backdrop filter within bounds</title> <link rel="help" href="https://github.com/WICG/html-in-canvas"> <link rel="author" href="mailto:schenney@chromium.org"> <link rel="match" href="css-backdrop-filter-on-div-ref.html"> <meta name="assert" value="A div with a CSS backdrop-filter paints with the filter in the correct area."> + <script src="/common/reftest-wait.js"></script> <style> canvas { background: red; @@ -33,9 +35,12 @@ </canvas> <script> - window.onload = () => { + function runTest() { var context = canvas.getContext("2d"); context.drawElementImage(child, 0, 0); + takeScreenshot(); } + onload = () => requestAnimationFrame(() => setTimeout(runTest)); </script> -</body> \ No newline at end of file +</body> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-blur-filter-on-div.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-blur-filter-on-div.html index b843c85..9fc2185c 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-blur-filter-on-div.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-blur-filter-on-div.html
@@ -1,9 +1,11 @@ <!DOCTYPE html> +<html class="reftest-wait"> <head> <title>drawElementImage paints CSS blur filters</title> <link rel="help" href="https://github.com/WICG/html-in-canvas"> <link rel="match" href="css-blur-filter-on-div-ref.html"> <meta name="assert" value="A div with a CSS blur filter paints with the filter."> + <script src="/common/reftest-wait.js"></script> <style> div { width: 100px; @@ -19,9 +21,12 @@ </canvas> <script> - window.onload = () => { + function runTest() { var context = canvas.getContext("2d"); context.drawElementImage(child, 0, 0); + takeScreenshot(); } - </script> + onload = () => requestAnimationFrame(() => setTimeout(runTest)); + </script> </body> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-clip-path-on-div.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-clip-path-on-div.html index cca8fee..3910bdb3 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-clip-path-on-div.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-clip-path-on-div.html
@@ -1,10 +1,12 @@ <!DOCTYPE html> +<html class="reftest-wait"> <head> <title>drawElementImage paints CSS clip-path</title> <link rel="help" href="https://github.com/WICG/html-in-canvas"> <link rel="match" href="css-clip-path-on-div-ref.html"> <meta name="assert" value="A div with a CSS clip-path paints clipped."> <meta name="fuzzy" content="maxDifference=0-1;totalPixels=0-2"> + <script src="/common/reftest-wait.js"></script> <style> div { width: 100px; @@ -20,9 +22,12 @@ </canvas> <script> - window.onload = () => { + function runTest() { var context = canvas.getContext("2d"); context.drawElementImage(child, 0, 0); + takeScreenshot(); } + onload = () => requestAnimationFrame(() => setTimeout(runTest)); </script> </body> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-filter-on-div.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-filter-on-div.html index a0b9bb1..942424b 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-filter-on-div.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-filter-on-div.html
@@ -1,10 +1,12 @@ <!DOCTYPE html> +<html class="reftest-wait"> <head> <title>drawElementImage paints CSS filters</title> <link rel="help" href="https://github.com/WICG/html-in-canvas"> <link rel="author" href="mailto:schenney@chromium.org"> <link rel="match" href="css-filter-on-div-ref.html"> <meta name="assert" value="A div with a CSS grayscale filter paints with the filter."> + <script src="/common/reftest-wait.js"></script> <style> canvas { background: red; @@ -23,9 +25,12 @@ </canvas> <script> - window.onload = () => { + function runTest() { var context = canvas.getContext("2d"); context.drawElementImage(child, 0, 0); + takeScreenshot(); } + onload = () => requestAnimationFrame(() => setTimeout(runTest)); </script> </body> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-filter-reference-on-div.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-filter-reference-on-div.html index 3f6d8f3..d988e73 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-filter-reference-on-div.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-filter-reference-on-div.html
@@ -1,10 +1,12 @@ <!DOCTYPE html> +<html class="reftest-wait"> <head> <title>drawElementImage paints CSS reference filters</title> <link rel="help" href="https://github.com/WICG/html-in-canvas"> <link rel="author" href="mailto:schenney@chromium.org"> <link rel="match" href="css-filter-reference-on-div-ref.html"> <meta name="assert" value="A div with a CSS filter referencing SVG paints with the filter."> + <script src="/common/reftest-wait.js"></script> <style> canvas { background: red; @@ -29,9 +31,12 @@ </svg> <script> - window.onload = () => { - var context = canvas.getContext("2d"); - context.drawElementImage(child, 0, 0); + function runTest() { + var context = canvas.getContext("2d"); + context.drawElementImage(child, 0, 0); + takeScreenshot(); } + onload = () => requestAnimationFrame(() => setTimeout(runTest)); </script> </body> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-mask-on-div.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-mask-on-div.html index 15d3fd00..061ab85 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-mask-on-div.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-mask-on-div.html
@@ -1,10 +1,12 @@ <!DOCTYPE html> +<html class="reftest-wait"> <head> <title>drawElementImage paints CSS mask</title> <link rel="help" href="https://github.com/WICG/html-in-canvas"> <link rel="match" href="css-mask-on-div-ref.html"> <meta name="assert" value="A div with mask paints with the mask."> <meta name="fuzzy" content="maxDifference=0-1;totalPixels=0-10000"> + <script src="/common/reftest-wait.js"></script> <style> div { width: 100px; @@ -20,9 +22,12 @@ </canvas> <script> - window.onload = () => { + function runTest() { var context = canvas.getContext("2d"); context.drawElementImage(child, 0, 0); + takeScreenshot(); } + onload = () => requestAnimationFrame(() => setTimeout(runTest)); </script> </body> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-opacity-on-div.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-opacity-on-div.html index ec578f4..8e301abd 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-opacity-on-div.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-opacity-on-div.html
@@ -1,10 +1,12 @@ <!DOCTYPE html> +<html class="reftest-wait"> <head> <title>drawElementImage paints CSS opacity</title> <link rel="help" href="https://github.com/WICG/html-in-canvas"> <link rel="match" href="css-opacity-on-div-ref.html"> <meta name="assert" value="A div with opacity paints with the opacity."> <meta name="fuzzy" content="maxDifference=0-1;totalPixels=0-10000"> + <script src="/common/reftest-wait.js"></script> <style> div { width: 100px; @@ -20,9 +22,12 @@ </canvas> <script> - window.onload = () => { + function runTest() { var context = canvas.getContext("2d"); context.drawElementImage(child, 0, 0); + takeScreenshot(); } + onload = () => requestAnimationFrame(() => setTimeout(runTest)); </script> </body> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-transform-on-div.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-transform-on-div.html index 0b9cd2db..8dab736 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-transform-on-div.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/css-transform-on-div.html
@@ -1,10 +1,12 @@ <!DOCTYPE html> +<html class="reftest-wait"> <head> <title>drawElementImage ignores CSS transforms</title> <link rel="help" href="https://github.com/WICG/html-in-canvas"> <link rel="author" href="mailto:schenney@chromium.org"> <link rel="match" href="css-transform-on-div-ref.html"> <meta name="assert" value="A div with a CSS transform paints without the transform."> + <script src="/common/reftest-wait.js"></script> <style> canvas { background: black; @@ -23,9 +25,12 @@ </canvas> <script> - window.onload = () => { + function runTest() { var context = canvas.getContext("2d"); context.drawElementImage(child, 0, 0); + takeScreenshot(); } + window.onload = () => requestAnimationFrame(() => setTimeout(runTest)); </script> </body> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/draw-element-image-get-image-data.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/draw-element-image-get-image-data.html index 0384433..87e15949 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/draw-element-image-get-image-data.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/draw-element-image-get-image-data.html
@@ -20,6 +20,8 @@ <script> promise_test(async () => { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); let ctx = canvas.getContext('2d'); ctx.fillStyle = 'rgb(3, 4, 5)'; ctx.fillRect(0, 0, 200, 200);
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/draw-element-image-returned-matrix.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/draw-element-image-returned-matrix.html index 7484c257..4954fcc 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/draw-element-image-returned-matrix.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/draw-element-image-returned-matrix.html
@@ -1,56 +1,66 @@ <!DOCTYPE html> -<html> <title>drawElementImage should return a matrix transforming the element's layout location to the drawn location</title> <link rel="help" href="https://github.com/WICG/html-in-canvas"> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> <canvas id="canvas" style="width: 100px; height: 100px;" width="100" height="100" layoutsubtree> - <div id="element" style="width: 10px; height: 10px;"></div> + <div id="element" style="width: 10px; height: 10px;">hello</div> </canvas> <script> - test(() => { +async function waitOneFrame() { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); +} + +function runTest() { + promise_test(async function() { + await waitOneFrame(); let draw_transform = canvas.getContext('2d').drawElementImage(element, 0, 0); let expected = new DOMMatrix(); assert_array_equals(draw_transform.toFloat64Array(), expected.toFloat64Array()); }, 'Draw transform at the origin should be an identity matrix'); - test(() => { + promise_test(async function() { + await waitOneFrame(); let draw_transform = canvas.getContext('2d').drawElementImage(element, 17, -9); let expected = new DOMMatrix().translateSelf(17, -9); assert_array_equals(draw_transform.toFloat64Array(), expected.toFloat64Array()); }, 'Draw transform should account for x and y'); - test((t) => { + promise_test(async function(t) { t.add_cleanup(() => { element.style.transformOrigin = ''; }); element.style.transformOrigin = '0 0'; + await waitOneFrame(); let draw_transform = canvas.getContext('2d').drawElementImage(element, 0, 0, 20, 5); let expected = new DOMMatrix().scaleSelf(2, 0.5); assert_array_equals(draw_transform.toFloat64Array(), expected.toFloat64Array()); }, 'Draw transform should account for width and height scale'); - test((t) => { + promise_test(async function(t) { t.add_cleanup(() => { element.style.transform = ''; }); element.style.transform = 'rotate(45deg)'; + await waitOneFrame(); let draw_transform = canvas.getContext('2d').drawElementImage(element, 0, 0); let expected = new DOMMatrix(); assert_array_equals(draw_transform.toFloat64Array(), expected.toFloat64Array()); }, 'Draw transform should not include transform on the element'); - test((t) => { + promise_test(async function(t) { t.add_cleanup(() => { canvas.getContext('2d').reset(); }); canvas.getContext('2d').translate(42, 17); + await waitOneFrame(); let draw_transform = canvas.getContext('2d').drawElementImage(element, 0, 0); let expected = new DOMMatrix().translateSelf(42, 17); assert_array_equals(draw_transform.toFloat64Array(), expected.toFloat64Array()); }, 'Draw transform should account for the current transform matrix with translation'); - test((t) => { + promise_test(async function(t) { t.add_cleanup(() => { canvas.style.zoom = ''; }); @@ -60,24 +70,26 @@ assert_array_equals(draw_transform.toFloat64Array(), expected.toFloat64Array()); }, 'Draw transform should not be affected by zoom on the canvas'); - test((t) => { + promise_test(async function(t) { t.add_cleanup(() => { element.style.transformOrigin = ''; canvas.getContext('2d').reset(); }); element.style.transformOrigin = '0 0'; + await waitOneFrame(); canvas.getContext('2d').rotate(Math.PI / 4); let draw_transform = canvas.getContext('2d').drawElementImage(element, 0, 0); let expected = new DOMMatrix().rotateSelf(45); assert_array_approx_equals(draw_transform.toFloat64Array(), expected.toFloat64Array(), 0.001); }, 'Draw transform should account for the current transform matrix with rotation'); - test((t) => { + promise_test(async function(t) { t.add_cleanup(() => { element.style.transformOrigin = ''; canvas.getContext('2d').reset(); }); element.style.transformOrigin = '5px 5px'; + await waitOneFrame(); canvas.getContext('2d').rotate(Math.PI / 4); let draw_transform = canvas.getContext('2d').drawElementImage(element, 0, 0); let expected = new DOMMatrix() @@ -87,7 +99,7 @@ assert_array_approx_equals(draw_transform.toFloat64Array(), expected.toFloat64Array(), 0.001); }, 'Draw transform should account for transform-origin'); - test((t) => { + promise_test(async function(t) { t.add_cleanup(() => { canvas.width = 100; canvas.height = 100; @@ -99,7 +111,7 @@ assert_array_equals(draw_transform.toFloat64Array(), expected.toFloat64Array()); }, 'Draw transform should account for canvas pixel scale'); - test((t) => { + promise_test(async function(t) { t.add_cleanup(() => { canvas.width = 100; canvas.height = 100; @@ -113,6 +125,7 @@ canvas.getContext('2d').translate(42, 17); canvas.getContext('2d').rotate(Math.PI / 4); element.style.transformOrigin = '5px 5px'; + element.style.transformOrigin = '5px 5px'; let draw_transform = canvas.getContext('2d').drawElementImage(element, 13, -9); let expected = new DOMMatrix() .translateSelf(-5, -5) @@ -124,4 +137,7 @@ .translateSelf(5, 5); assert_array_approx_equals(draw_transform.toFloat64Array(), expected.toFloat64Array(), 0.001); }, 'Draw transform should account for canvas pixel scale, zoom, transform-origin, CTM, and x and y'); +} + +window.onload = runTest; </script>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/draw-element-image-scale-variant.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/draw-element-image-scale-variant.html index da654e70..42f5abc 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/draw-element-image-scale-variant.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/draw-element-image-scale-variant.html
@@ -26,7 +26,7 @@ requestAnimationFrame(takeScreenshot); } -onload = () => runTest(); +onload = () => requestAnimationFrame(() => setTimeout(runTest)); </script> </html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/drawing-display-none-fails.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/drawing-display-none-fails.html index ed773c6e..e030cfb 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/drawing-display-none-fails.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/drawing-display-none-fails.html
@@ -13,9 +13,11 @@ </canvas> <script> - test(function() { + promise_test(async function() { canvas_2d_div.getBoundingClientRect(); canvas_2d_div.style.display = 'none'; + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); const context_2d = canvas_2d.getContext('2d'); assert_throws_js( TypeError, @@ -24,9 +26,11 @@ ); }, 'drawElementImage with display:none element'); - test(function() { + promise_test(async function() { canvas_3d_div.getBoundingClientRect(); canvas_3d_div.style.display = 'none'; + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); const context_gl = canvas_3d.getContext('webgl2'); const tex = context_gl.createTexture(); context_gl.bindTexture(context_gl.TEXTURE_2D, tex);
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/element-shadow.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/element-shadow.html index 51d3345a..e1b5b01 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/element-shadow.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/element-shadow.html
@@ -26,7 +26,7 @@ requestAnimationFrame(takeScreenshot); } -onload = () => runTest(); +onload = () => requestAnimationFrame(() => setTimeout(runTest)); </script> </html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/error-conditions.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/error-conditions.html index 62e87d4..c51f578 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/error-conditions.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/error-conditions.html
@@ -29,6 +29,9 @@ <script> promise_test(async () => { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); + assert_throws_js(TypeError, () => canvas.getContext("2d").drawElementImage(grandchild, 20, 30), "Can't draw non-direct children.");
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/filtered-basic.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/filtered-basic.html index 27030f5a..1157dcc 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/filtered-basic.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/filtered-basic.html
@@ -1,9 +1,10 @@ <!DOCTYPE html> -<html> +<html class="reftest-wait"> <title>Canvas.drawElementImage: filtered green rect</title> <link rel="help" href="https://github.com/WICG/html-in-canvas"> <link rel="author" href="mailto:schenney@chromium.org"> <link rel="match" href="filtered-basic-ref.html"> +<script src="/common/reftest-wait.js"></script> <style> #child { width: 100px; @@ -24,9 +25,10 @@ var context = canvas.getContext("2d"); context.filter = "blur(5px)"; context.drawElementImage(child, 20, 30); + takeScreenshot(); } -onload = () => runTest(); +onload = () => requestAnimationFrame(() => setTimeout(runTest)); </script> </html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/fireOnEveryPaint-css-animation.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/fireOnEveryPaint-css-animation.html index 9dd1364e..214a6a7 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/fireOnEveryPaint-css-animation.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/fireOnEveryPaint-css-animation.html
@@ -43,6 +43,8 @@ canvas.getContext('2d').drawElementImage(child, 0, 0); } }); + // Wait a frame to cache initial paint + await waitForOneFrame(); resizeObserver.observe(canvas, {fireOnEveryPaint: true}); // 1. Wait a frame and assert that the initial observation occurred.
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/get-element-transform.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/get-element-transform.html index 7b67281..77faeb8 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/get-element-transform.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/get-element-transform.html
@@ -119,3 +119,4 @@ assert_array_approx_equals(element_transform.toFloat64Array(), expected.toFloat64Array(), 0.001); }, 'Element transform should account for canvas pixel scale, transform-origin, and a complex draw transform'); </script> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/global-alpha-basic.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/global-alpha-basic.html index 034d507..438a448 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/global-alpha-basic.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/global-alpha-basic.html
@@ -1,10 +1,11 @@ <!DOCTYPE html> -<html> +<html class="reftest-wait"> <title>Canvas.drawElementImage: green rect with alpha</title> <link rel="help" href="https://github.com/WICG/html-in-canvas"> <link rel="author" href="mailto:schenney@chromium.org"> <link rel="match" href="global-alpha-basic-ref.html"> <meta name=fuzzy content="1;2500"> +<script src="/common/reftest-wait.js"></script> <style> #child { width: 100px; @@ -27,9 +28,10 @@ context.fillRect(70, 80, 110, 100); context.globalAlpha = 0.5; context.drawElementImage(child, 20, 30); + takeScreenshot(); } -onload = () => runTest(); +onload = () => requestAnimationFrame(() => setTimeout(runTest)); </script> </html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/hit-test/selection.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/hit-test/selection.html index 37569b5..3523132f8 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/hit-test/selection.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/hit-test/selection.html
@@ -19,10 +19,12 @@ </canvas> <script> - const ctx = canvas.getContext('2d'); - ctx.drawElementImage(target, 0, 0); - promise_test(async () => { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); + const ctx = canvas.getContext('2d'); + ctx.drawElementImage(target, 0, 0); + let doubleClickEventFired = false; canvas.addEventListener('dblclick', () => { doubleClickEventFired = true; @@ -47,3 +49,4 @@ '#target text should be selected by double-click.'); }, 'Double-clicking on canvas should be able to modify selection.'); </script> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/iframe-local.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/iframe-local.html index 5e50885..a904ac03 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/iframe-local.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/iframe-local.html
@@ -1,9 +1,10 @@ <!DOCTYPE html> -<html> +<html class="reftest-wait"> <title>Canvas.drawElementImage: same-origin iframe test</title> <link rel="help" href="https://github.com/WICG/html-in-canvas"> <link rel="author" href="mailto:schenney@chromium.org"> <link rel="match" href="basic-rect-ref.html"> +<script src="/common/reftest-wait.js"></script> <style> #child { width: 100px; @@ -30,9 +31,10 @@ <script> function runTest() { canvas.getContext("2d").drawElementImage(child, 20, 30); + takeScreenshot(); } -onload = () => runTest(); +onload = () => requestAnimationFrame(() => setTimeout(runTest)); </script> </html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/ink-overflow.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/ink-overflow.html index c1bff0c..cec6f5d 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/ink-overflow.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/ink-overflow.html
@@ -68,6 +68,7 @@ Promise.all(Array.from(document.querySelectorAll("canvas")).map(async cvs => { await resizeToPixelGrid(cvs); + await new Promise(setTimeout); const target = cvs.firstElementChild; const targetStyle = getComputedStyle(target);
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/intersection-observer-visibility.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/intersection-observer-visibility.html index 2ce75d2..c99286a 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/intersection-observer-visibility.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/intersection-observer-visibility.html
@@ -10,6 +10,8 @@ <script> promise_test(async function(t) { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); canvas.getContext('2d').drawElementImage(target, 0, 0); await new Promise(r => requestAnimationFrame(r));
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/moved-nested-canvas.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/moved-nested-canvas.html index 2bfa117..180ef74 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/moved-nested-canvas.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/moved-nested-canvas.html
@@ -1,7 +1,9 @@ <!DOCTYPE html> +<html class="reftest-wait"> <title>drawElementImage should support rendering a nested canvas that was moved under the canvas</title> <link rel="help" href="https://github.com/WICG/html-in-canvas"> <link rel="match" href="nested-canvas-ref.html"> +<script src="/common/reftest-wait.js"></script> <canvas id="canvas_a" layoutsubtree="true" width="100" height="100"> <div id="div" style="width: 100px; height: 100px; background: radial-gradient(lightgreen, green);"></div> @@ -9,7 +11,16 @@ <canvas id="canvas_b" layoutsubtree="true" width="100" height="100" style="background: red;"> </canvas> <script> +async function runTest() { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); canvas_a.getContext('2d').drawElementImage(div, 0, 0, 100, 100); canvas_b.appendChild(canvas_a); + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); canvas_b.getContext('2d').drawElementImage(canvas_a, 0, 0, 100, 100); + takeScreenshot(); +} +onload = () => runTest(); </script> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/nested-canvas.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/nested-canvas.html index c325aa78..5d95362 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/nested-canvas.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/nested-canvas.html
@@ -1,7 +1,9 @@ <!DOCTYPE html> +<html class="reftest-wait"> <title>drawElementImage should support rendering a nested canvas</title> <link rel="help" href="https://github.com/WICG/html-in-canvas"> <link rel="match" href="nested-canvas-ref.html"> +<script src="/common/reftest-wait.js"></script> <canvas id="canvas" layoutsubtree="true" width="100" height="100" style="background: red;"> <canvas id="nested_canvas" layoutsubtree="true" width="100" height="100"> @@ -9,6 +11,15 @@ </canvas> </canvas> <script> +async function runTest() { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); nested_canvas.getContext('2d').drawElementImage(inner_div, 0, 0, 100, 100); + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); canvas.getContext('2d').drawElementImage(nested_canvas, 0, 0, 100, 100); + takeScreenshot(); +} +onload = () => runTest(); </script> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/nested-div-canvas.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/nested-div-canvas.html index fa893e7b..da66600 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/nested-div-canvas.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/nested-div-canvas.html
@@ -1,7 +1,9 @@ <!DOCTYPE html> +<html class="reftest-wait"> <title>drawElementImage should support rendering a div containing a canvas, all under a canvas</title> <link rel="help" href="https://github.com/WICG/html-in-canvas"> <link rel="match" href="nested-canvas-ref.html"> +<script src="/common/reftest-wait.js"></script> <canvas id="canvas" layoutsubtree="true" width="100" height="100" style="background: red;"> <div id="nested_div" style="width: 100px; height: 100px;"> @@ -11,6 +13,15 @@ </div> </canvas> <script> +async function runTest() { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); nested_canvas.getContext('2d').drawElementImage(inner_div, 0, 0, 100, 100); + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); canvas.getContext('2d').drawElementImage(nested_div, 0, 0, 100, 100); + takeScreenshot(); +} +onload = () => runTest(); </script> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/nested-webgl-canvas.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/nested-webgl-canvas.html index 2f7f495..f5cdf69 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/nested-webgl-canvas.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/nested-webgl-canvas.html
@@ -1,7 +1,9 @@ <!DOCTYPE html> +<html class="reftest-wait"> <title>drawElementImage should support rendering a nested webgl canvas</title> <link rel="help" href="https://github.com/WICG/html-in-canvas"> <link rel="match" href="nested-canvas-ref.html"> +<script src="/common/reftest-wait.js"></script> <canvas id="webgl_canvas" layoutsubtree="true" width="100" height="100" style="background: red;"> <canvas id="nested_webgl_canvas" layoutsubtree="true" width="100" height="100"> @@ -10,7 +12,7 @@ </canvas> <script> function drawWebGLElementImage(canvas, child) { - const gl = canvas.getContext('webgl2'); + const gl = canvas.getContext('webgl2', {preserveDrawingBuffer:true}); const vsSrc = `#version 300 es in vec2 a_pos; in vec2 a_uv; @@ -72,6 +74,15 @@ gl.drawArrays(gl.TRIANGLES, 0, 6); } +async function runTest() { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); drawWebGLElementImage(nested_webgl_canvas, inner_div); + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); drawWebGLElementImage(webgl_canvas, nested_webgl_canvas); + takeScreenshot(); +} +onload = () => runTest(); </script> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/non-opaque-element.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/non-opaque-element.html index f8371a45..f4c6ca0 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/non-opaque-element.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/non-opaque-element.html
@@ -1,9 +1,10 @@ <!DOCTYPE html> -<html> +<html class="reftest-wait"> <title>Canvas.drawElementImage: div with transparent background</title> <link rel="help" href="https://github.com/WICG/html-in-canvas"> <link rel="author" href="mailto:schenney@chromium.org"> <link rel="match" href="non-opaque-element-ref.html"> +<script src="/common/reftest-wait.js"></script> <style> #child { width: 100px; @@ -33,9 +34,10 @@ <script> function runTest() { canvas.getContext("2d").drawElementImage(child, 20, 30); + takeScreenshot(); } -onload = () => runTest(); +onload = () => requestAnimationFrame(() => setTimeout(runTest)); </script> </html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/percent-sizing.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/percent-sizing.html index fdf4dc72..b1e59f48 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/percent-sizing.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/percent-sizing.html
@@ -1,9 +1,10 @@ <!DOCTYPE html> -<html> +<html class="reftest-wait"> <title>Canvas.drawElementImage: percentage sizing of canvas children</title> <link rel="help" href="https://github.com/WICG/html-in-canvas"> <link rel="author" href="mailto:schenney@chromium.org"> <link rel="match" href="basic-rect-ref.html"> +<script src="/common/reftest-wait.js"></script> <style> #child { width: 50%; @@ -31,9 +32,10 @@ <script> function runTest() { canvas.getContext("2d").drawElementImage(child, 20, 30); + takeScreenshot(); } -onload = () => runTest(); +onload = () => requestAnimationFrame(() => setTimeout(runTest)); </script> </html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/background-png-images-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/background-png-images-ignored.https.sub.html index 2904503..a890121 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/background-png-images-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/background-png-images-ignored.https.sub.html
@@ -37,24 +37,24 @@ </canvas> <script> - window.onload = () => { - test(function(t) { - var context = canvas.getContext("2d"); - context.drawElementImage(child, 0, 0); +window.onload = () => { + promise_test(async function(t) { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); + var context = canvas.getContext("2d"); + context.drawElementImage(child, 0, 0); - pixel = context.getImageData(50, 50, 1, 1).data; - assert_equals(pixel[0], 255, "Same origin should draw"); - assert_equals(pixel[1], 0, "Same origin should draw"); - assert_equals(pixel[2], 0, "Same origin should draw"); + pixel = context.getImageData(50, 50, 1, 1).data; + assert_equals(pixel[0], 255, "Same origin should draw"); + assert_equals(pixel[1], 0, "Same origin should draw"); + assert_equals(pixel[2], 0, "Same origin should draw"); - pixel = context.getImageData(50, 150, 1, 1).data; - assert_equals(pixel[0], 0, "Cross origin should not draw"); - assert_equals(pixel[1], 128, "Cross origin should not draw"); - assert_equals(pixel[2], 0, "Cross origin should not draw"); - - t.done(); - }); - } + pixel = context.getImageData(50, 150, 1, 1).data; + assert_equals(pixel[0], 0, "Cross origin should not draw"); + assert_equals(pixel[1], 128, "Cross origin should not draw"); + assert_equals(pixel[2], 0, "Cross origin should not draw"); + }); +}; </script> </body> -</html> \ No newline at end of file +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/background-svg-images-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/background-svg-images-ignored.https.sub.html index 022d9cf2..0591e40 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/background-svg-images-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/background-svg-images-ignored.https.sub.html
@@ -37,24 +37,24 @@ </canvas> <script> - window.onload = () => { - test(function(t) { - var context = canvas.getContext("2d"); - context.drawElementImage(child, 0, 0); +window.onload = () => { + promise_test(async function(t) { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); + var context = canvas.getContext("2d"); + context.drawElementImage(child, 0, 0); - pixel = context.getImageData(50, 50, 1, 1).data; - assert_equals(pixel[0], 255, "Same origin should draw"); - assert_equals(pixel[1], 0, "Same origin should draw"); - assert_equals(pixel[2], 0, "Same origin should draw"); + pixel = context.getImageData(50, 50, 1, 1).data; + assert_equals(pixel[0], 255, "Same origin should draw"); + assert_equals(pixel[1], 0, "Same origin should draw"); + assert_equals(pixel[2], 0, "Same origin should draw"); - pixel = context.getImageData(50, 150, 1, 1).data; - assert_equals(pixel[0], 0, "Cross origin should not draw"); - assert_equals(pixel[1], 128, "Cross origin should not draw"); - assert_equals(pixel[2], 0, "Cross origin should not draw"); - - t.done(); - }); - } + pixel = context.getImageData(50, 150, 1, 1).data; + assert_equals(pixel[0], 0, "Cross origin should not draw"); + assert_equals(pixel[1], 128, "Cross origin should not draw"); + assert_equals(pixel[2], 0, "Cross origin should not draw"); + }); +} </script> </body> -</html> \ No newline at end of file +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/border-png-images-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/border-png-images-ignored.https.sub.html index 895e558..617c8a9 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/border-png-images-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/border-png-images-ignored.https.sub.html
@@ -47,7 +47,9 @@ <script> window.onload = () => { - test(function(t) { + promise_test(async function(t) { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); var context = canvas.getContext("2d"); context.drawElementImage(child, 0, 0); });
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/border-svg-images-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/border-svg-images-ignored.https.sub.html index b2865fd8..f4ba83d2 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/border-svg-images-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/border-svg-images-ignored.https.sub.html
@@ -47,7 +47,9 @@ <script> window.onload = () => { - test(function(t) { + promise_test(async function(t) { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); var context = canvas.getContext("2d"); context.drawElementImage(child, 0, 0); });
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/box-mask-png-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/box-mask-png-ignored.https.sub.html index 31444de..bc3ce5c 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/box-mask-png-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/box-mask-png-ignored.https.sub.html
@@ -40,7 +40,9 @@ <script> window.onload = () => { - test(function(t) { + promise_test(async function(t) { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); var context = canvas.getContext("2d"); context.drawElementImage(child, 0, 0); @@ -64,10 +66,8 @@ assert_equals(pixel[0], 0, "Cross origin should not draw"); assert_equals(pixel[1], 128, "Cross origin should not draw"); assert_equals(pixel[2], 0, "Cross origin should not draw"); - - t.done(); }); } </script> </body> -</html> \ No newline at end of file +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/box-mask-svg-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/box-mask-svg-ignored.https.sub.html index 6d1f65a..aa1b186 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/box-mask-svg-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/box-mask-svg-ignored.https.sub.html
@@ -40,7 +40,9 @@ <script> window.onload = () => { - test(function(t) { + promise_test(async function(t) { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); var context = canvas.getContext("2d"); context.drawElementImage(child, 0, 0); @@ -64,10 +66,8 @@ assert_equals(pixel[0], 0, "Cross origin should not draw"); assert_equals(pixel[1], 128, "Cross origin should not draw"); assert_equals(pixel[2], 0, "Cross origin should not draw"); - - t.done(); }); } </script> </body> -</html> \ No newline at end of file +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/editing-markers-ignored-ref.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/editing-markers-ignored-ref.html index e14e7e7..093c188 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/editing-markers-ignored-ref.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/editing-markers-ignored-ref.html
@@ -1,6 +1,8 @@ <!DOCTYPE html> +<html class="reftest-wait"> <meta charset="UTF-8"> <title>drawElementImage does not draw editing markers - ref</title> +<script src="/common/reftest-wait.js"></script> <style> #child { width: 100px; @@ -17,9 +19,13 @@ </div> </canvas> <script> - function runTest() { + async function runTest() { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); canvas.getContext("2d").drawElementImage(child, 0, 0); + takeScreenshot(); } onload = () => runTest(); -</script> \ No newline at end of file +</script> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/editing-markers-ignored.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/editing-markers-ignored.html index 065e7b3..2685be4a 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/editing-markers-ignored.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/editing-markers-ignored.html
@@ -1,4 +1,5 @@ <!DOCTYPE html> +<html class="reftest-wait"> <meta charset="UTF-8"> <title>drawElementImage does not draw editing markers</title> <link rel="help" href="https://github.com/WICG/html-in-canvas"> @@ -6,6 +7,7 @@ <link rel="match" href="editing-markers-ignored-ref.html"> <meta name="assert" value="When preserving privacy for readback, do not draw markers for editing."> <script src="/wpt_internal/css/support/markers.js"></script> +<script src="/common/reftest-wait.js"></script> <style> #child { width: 100px; @@ -22,7 +24,7 @@ </div> </canvas> <script> - function runTest() { + async function runTest() { if (typeof internals !== 'undefined') { // To run this test from content_shell you can use // "--expose-internals-for-testing" command flag. @@ -39,8 +41,12 @@ "pink", "thin", "darkred"); } + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); canvas.getContext("2d").drawElementImage(child, 0, 0); + takeScreenshot(); } onload = () => runTest(); </script> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/embed-image-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/embed-image-ignored.https.sub.html index 4893bc90..6ecf24f 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/embed-image-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/embed-image-ignored.https.sub.html
@@ -33,7 +33,9 @@ </canvas> <script> window.onload = function() { - test(function(t) { + promise_test(async function(t) { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); var context = canvas.getContext("2d"); context.drawElementImage(child, 0, 0); @@ -46,8 +48,6 @@ assert_equals(pixel[0], 0, "Cross origin should not draw"); assert_equals(pixel[1], 128, "Cross origin should not draw"); assert_equals(pixel[2], 0, "Cross origin should not draw"); - - t.done(); }); }; </script>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-multi-filters-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-multi-filters-ignored.https.sub.html index e564fc38..5181cf6f 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-multi-filters-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-multi-filters-ignored.https.sub.html
@@ -38,7 +38,9 @@ <script> window.onload = () => { - test(function(t) { + promise_test(async function(t) { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); var context = canvas.getContext("2d", { willReadFrequently: true }); context.drawElementImage(document.getElementById("child-same"), 0, 0); context.drawElementImage(document.getElementById("child-cross"), 0, 100); @@ -53,10 +55,8 @@ assert_equals(pixel[0], 0, "Cross origin should not draw"); assert_equals(pixel[1], 128, "Cross origin should not draw"); assert_equals(pixel[2], 0, "Cross origin should not draw"); - - t.done(); }); } </script> </body> -</html> \ No newline at end of file +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-ref-external-svg-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-ref-external-svg-ignored.https.sub.html index 325c297..b1c9f92 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-ref-external-svg-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-ref-external-svg-ignored.https.sub.html
@@ -28,7 +28,9 @@ <script> window.onload = () => { - test(function(t) { + promise_test(async function(t) { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); var context = canvas.getContext("2d", { willReadFrequently: true }); context.drawElementImage(document.getElementById("child-same"), 0, 0); context.drawElementImage(document.getElementById("child-cross"), 0, 100); @@ -43,10 +45,8 @@ assert_equals(pixel[0], 0, "Cross origin should not draw"); assert_equals(pixel[1], 128, "Cross origin should not draw"); assert_equals(pixel[2], 0, "Cross origin should not draw"); - - t.done(); }); } </script> </body> -</html> \ No newline at end of file +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-ref-svg-feimage-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-ref-svg-feimage-ignored.https.sub.html index 6b9cb32..dfc00dd6 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-ref-svg-feimage-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-ref-svg-feimage-ignored.https.sub.html
@@ -37,7 +37,9 @@ <script> window.onload = () => { - test(function(t) { + promise_test(async function(t) { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); var context = canvas.getContext("2d", { willReadFrequently: true }); context.drawElementImage(document.getElementById("child-same"), 0, 0); context.drawElementImage(document.getElementById("child-cross"), 0, 100); @@ -52,10 +54,8 @@ assert_equals(pixel[0], 0, "Cross origin should not draw"); assert_equals(pixel[1], 128, "Cross origin should not draw"); assert_equals(pixel[2], 0, "Cross origin should not draw"); - - t.done(); }); } </script> </body> -</html> \ No newline at end of file +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-ref-svg-feimage-svg-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-ref-svg-feimage-svg-ignored.https.sub.html index 02a6c0ef..709befd 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-ref-svg-feimage-svg-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-ref-svg-feimage-svg-ignored.https.sub.html
@@ -37,7 +37,9 @@ <script> window.onload = () => { - test(function(t) { + promise_test(async function(t) { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); var context = canvas.getContext("2d", { willReadFrequently: true }); context.drawElementImage(document.getElementById("child-same"), 0, 0); context.drawElementImage(document.getElementById("child-cross"), 0, 100); @@ -52,10 +54,8 @@ assert_equals(pixel[0], 0, "Cross origin should not draw"); assert_equals(pixel[1], 128, "Cross origin should not draw"); assert_equals(pixel[2], 0, "Cross origin should not draw"); - - t.done(); }); } </script> </body> -</html> \ No newline at end of file +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-ref-svg-mixed-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-ref-svg-mixed-ignored.https.sub.html index 4461c8fb..e3dd9d2 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-ref-svg-mixed-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-div-ref-svg-mixed-ignored.https.sub.html
@@ -40,7 +40,9 @@ <script> window.onload = () => { - test(function(t) { + promise_test(async function(t) { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); var context = canvas.getContext("2d", { willReadFrequently: true }); context.drawElementImage(document.getElementById("child-same"), 0, 0); context.drawElementImage(document.getElementById("child-cross"), 0, 100); @@ -55,10 +57,8 @@ assert_equals(pixel[0], 0, "Cross origin should not draw"); assert_equals(pixel[1], 128, "Cross origin should not draw"); assert_equals(pixel[2], 0, "Cross origin should not draw"); - - t.done(); }); } </script> </body> -</html> \ No newline at end of file +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-svg-ref-svg-feimage-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-svg-ref-svg-feimage-ignored.https.sub.html index 7d60ee49..b4b2f6a 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-svg-ref-svg-feimage-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-svg-ref-svg-feimage-ignored.https.sub.html
@@ -23,7 +23,9 @@ <script> window.onload = () => { - test(function(t) { + promise_test(async function(t) { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); var context = canvas.getContext("2d", { willReadFrequently: true }); context.drawElementImage(document.getElementById("child"), 0, 0); @@ -37,7 +39,7 @@ assert_equals(pixel[1], 128, "Cross origin should not draw, green channel"); assert_equals(pixel[2], 0, "Cross origin should not draw, blue channel"); - t.done(); + }); } </script>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-svg-ref-svg-feimage-svg-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-svg-ref-svg-feimage-svg-ignored.https.sub.html index ae22496..e4fe27f 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-svg-ref-svg-feimage-svg-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/filtered-svg-ref-svg-feimage-svg-ignored.https.sub.html
@@ -23,7 +23,9 @@ <script> window.onload = () => { - test(function(t) { + promise_test(async function(t) { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); var context = canvas.getContext("2d", { willReadFrequently: true }); context.drawElementImage(document.getElementById("child"), 0, 0); @@ -36,10 +38,8 @@ assert_equals(pixel[0], 0, "Cross origin should not draw, red channel"); assert_equals(pixel[1], 128, "Cross origin should not draw, green channel"); assert_equals(pixel[2], 0, "Cross origin should not draw, blue channel"); - - t.done(); }); } </script> </body> -</html> \ No newline at end of file +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/fragment-mask-png-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/fragment-mask-png-ignored.https.sub.html index e1a7ec7..d12cfeb 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/fragment-mask-png-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/fragment-mask-png-ignored.https.sub.html
@@ -31,7 +31,9 @@ <script> window.onload = () => { - test(function(t) { + promise_test(async function(t) { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); var context = canvas.getContext("2d"); context.drawElementImage(child, 0, 0); @@ -55,10 +57,8 @@ assert_equals(pixel[0], 0, "Cross origin should not draw"); assert_equals(pixel[1], 128, "Cross origin should not draw"); assert_equals(pixel[2], 0, "Cross origin should not draw"); - - t.done(); }); } </script> </body> -</html> \ No newline at end of file +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/fragment-mask-svg-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/fragment-mask-svg-ignored.https.sub.html index 7821020..34dc69d7 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/fragment-mask-svg-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/fragment-mask-svg-ignored.https.sub.html
@@ -31,7 +31,9 @@ <script> window.onload = () => { - test(function(t) { + promise_test(async function(t) { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); var context = canvas.getContext("2d"); context.drawElementImage(child, 0, 0); @@ -55,10 +57,8 @@ assert_equals(pixel[0], 0, "Cross origin should not draw"); assert_equals(pixel[1], 128, "Cross origin should not draw"); assert_equals(pixel[2], 0, "Cross origin should not draw"); - - t.done(); }); } </script> </body> -</html> \ No newline at end of file +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/glic-markers-ignored-ref.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/glic-markers-ignored-ref.html index f767d3b3..c34cabc 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/glic-markers-ignored-ref.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/glic-markers-ignored-ref.html
@@ -1,6 +1,8 @@ <!DOCTYPE html> +<html class="reftest-wait"> <meta charset="UTF-8"> <title>drawElementImage does not draw GLIC markers - ref</title> +<script src="/common/reftest-wait.js"></script> <style> #child { width: 100px; @@ -17,9 +19,13 @@ </div> </canvas> <script> - function runTest() { + async function runTest() { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); canvas.getContext("2d").drawElementImage(child, 0, 0); + takeScreenshot(); } - onload = () => runTest(); -</script> \ No newline at end of file + onload = runTest; +</script> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/glic-markers-ignored.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/glic-markers-ignored.html index 220f762..0d687af 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/glic-markers-ignored.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/glic-markers-ignored.html
@@ -24,17 +24,18 @@ </div> </canvas> <script> - function runTest() { + async function runTest() { if (typeof internals !== 'undefined') { // To run this test from content_shell you can use // "--expose-internals-for-testing" command flag. const range = createRangeForTextOnly(editable, 0, 18); internals.setMarker(document, range, "glic"); } - setTimeout(() => { - canvas.getContext("2d").drawElementImage(child, 0, 0); - takeScreenshot(); - }, 500); + await new Promise(resolve => setTimeout(resolve, 500)); + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); + canvas.getContext("2d").drawElementImage(child, 0, 0); + takeScreenshot(); } onload = () => runTest();
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/grammar-ignored-overlay-path-ref.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/grammar-ignored-overlay-path-ref.html index e16812d..4c2a455c 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/grammar-ignored-overlay-path-ref.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/grammar-ignored-overlay-path-ref.html
@@ -1,6 +1,8 @@ <!DOCTYPE html> +<html class="reftest-wait"> <meta charset="UTF-8"> <title>drawElementImage does not draw grammar with custom highlights - ref</title> +<script src="/common/reftest-wait.js"></script> <style> #child { width: 100px; @@ -21,14 +23,18 @@ </div> </canvas> <script> - function runTest() { + async function runTest() { let r = new Range(); r.setStart(document.body, 0); r.setEnd(document.body, 1); CSS.highlights.set("example-highlight", new Highlight(r)); + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); canvas.getContext("2d").drawElementImage(child, 0, 0); + takeScreenshot(); } onload = () => runTest(); -</script> \ No newline at end of file +</script> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/grammar-ignored-overlay-path.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/grammar-ignored-overlay-path.html index b83b912..0433e4a 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/grammar-ignored-overlay-path.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/grammar-ignored-overlay-path.html
@@ -1,4 +1,5 @@ <!DOCTYPE html> +<html class="reftest-wait"> <meta charset="UTF-8"> <title>drawElementImage does not draw grammar with custom highlights</title> <link rel="help" href="https://github.com/WICG/html-in-canvas"> @@ -6,6 +7,7 @@ <link rel="match" href="grammar-ignored-overlay-path-ref.html"> <meta name="assert" value="When preserving privacy for readback, do not draw grammar markers when using the highlight overlay painting path (triggered by the custom highlight)."> <script src="/wpt_internal/css/support/markers.js"></script> +<script src="/common/reftest-wait.js"></script> <style> #child { width: 100px; @@ -30,7 +32,7 @@ </div> </canvas> <script> - function runTest() { + async function runTest() { addGrammarMarker(editable, 5, 10) let r = new Range(); @@ -38,8 +40,12 @@ r.setEnd(document.body, 1); CSS.highlights.set("example-highlight", new Highlight(r)); + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); canvas.getContext("2d").drawElementImage(child, 0, 0); + takeScreenshot(); } onload = () => runTest(); -</script> \ No newline at end of file +</script> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/html-images-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/html-images-ignored.https.sub.html index 9b4d61f8..700715ac 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/html-images-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/html-images-ignored.https.sub.html
@@ -31,7 +31,9 @@ </canvas> <script> window.onload = function() { - test(function(t) { + promise_test(async function(t) { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); var context = canvas.getContext("2d"); context.drawElementImage(child, 0, 0); @@ -44,8 +46,6 @@ assert_equals(pixel[0], 0, "Cross origin should not draw"); assert_equals(pixel[1], 128, "Cross origin should not draw"); assert_equals(pixel[2], 0, "Cross origin should not draw"); - - t.done(); }); }; </script>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/iframes-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/iframes-ignored.https.sub.html index 3c92ff5..e52980b 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/iframes-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/iframes-ignored.https.sub.html
@@ -35,7 +35,9 @@ function pendingIframeLoaded() { if (!--sPendingIframesToLoad) { - test(function(t) { + promise_test(async function(t) { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); var context = canvas.getContext("2d"); context.drawElementImage(child, 0, 0); @@ -48,8 +50,6 @@ assert_equals(pixel[0], 0, "Cross origin should not draw"); assert_equals(pixel[1], 128, "Cross origin should not draw"); assert_equals(pixel[2], 0, "Cross origin should not draw"); - - t.done(); }); } } @@ -58,4 +58,4 @@ crossOrigin.onload = pendingIframeLoaded; </script> </body> -</html> \ No newline at end of file +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/object-image-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/object-image-ignored.https.sub.html index d98c670..6b990d4 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/object-image-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/object-image-ignored.https.sub.html
@@ -33,7 +33,9 @@ </canvas> <script> window.onload = function() { - test(function(t) { + promise_test(async function(t) { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); var context = canvas.getContext("2d"); context.drawElementImage(child, 0, 0); @@ -46,8 +48,6 @@ assert_equals(pixel[0], 0, "Cross origin should not draw"); assert_equals(pixel[1], 128, "Cross origin should not draw"); assert_equals(pixel[2], 0, "Cross origin should not draw"); - - t.done(); }); }; </script>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/picture-images-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/picture-images-ignored.https.sub.html index 9d4529fa..07b287b7 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/picture-images-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/picture-images-ignored.https.sub.html
@@ -39,7 +39,9 @@ </canvas> <script> window.onload = function() { - test(function(t) { + promise_test(async function(t) { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); var context = canvas.getContext("2d"); context.drawElementImage(child, 0, 0); @@ -52,8 +54,6 @@ assert_equals(pixel[0], 0, "Cross origin should not draw"); assert_equals(pixel[1], 128, "Cross origin should not draw"); assert_equals(pixel[2], 0, "Cross origin should not draw"); - - t.done(); }); }; </script>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/search-text-painted-overlay-path.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/search-text-painted-overlay-path.html index 4629529d..97c6068 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/search-text-painted-overlay-path.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/search-text-painted-overlay-path.html
@@ -1,4 +1,5 @@ <!DOCTYPE html> +<html class="reftest-wait"> <meta charset="UTF-8"> <title>drawElementImage draws serach-text with custom highlights</title> <link rel="help" href="https://github.com/WICG/html-in-canvas"> @@ -6,6 +7,7 @@ <link rel="match" href="search-text-painted-overlay-path-ref.html"> <meta name="assert" value="When preserving privacy for readback, still draw search-text markers when using the highlight overlay painting path (triggered by the custom highlight)."> <script src="/wpt_internal/css/support/markers.js"></script> +<script src="/common/reftest-wait.js"></script> <style> #child { width: 100px; @@ -30,7 +32,7 @@ </div> </canvas> <script> - function runTest() { + async function runTest() { addSearchTextMarker(editable, 5, 11) let r = new Range(); @@ -38,8 +40,13 @@ r.setEnd(document.body, 1); CSS.highlights.set("example-highlight", new Highlight(r)); + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); + canvas.getContext("2d").drawElementImage(child, 0, 0); + takeScreenshot(); } - onload = () => runTest(); -</script> \ No newline at end of file + onload = runTest; +</script> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/selection-alone-with-decorations-painted.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/selection-alone-with-decorations-painted.html index 81929865..4765c53 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/selection-alone-with-decorations-painted.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/selection-alone-with-decorations-painted.html
@@ -1,10 +1,12 @@ <!DOCTYPE html> +<html class="reftest-wait"> <meta charset="UTF-8"> <title>drawElementImage draws selection</title> <link rel="help" href="https://drafts.csswg.org/css-highlight-api-1/"> <link rel="match" href="selection-alone-with-decorations-painted-ref.html"> <meta name="assert" value="When preserving privacy for readback, draw selection when it has decorations and shadows."> <script src="/wpt_internal/css/support/markers.js"></script> +<script src="/common/reftest-wait.js"></script> <style> :focus { outline: none; @@ -30,11 +32,16 @@ </div> </canvas> <script> - function runTest() { + async function runTest() { setSelection(editable, 5, 11) + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); + canvas.getContext("2d").drawElementImage(child, 0, 0); + takeScreenshot(); } - onload = () => runTest(); -</script> \ No newline at end of file + onload = runTest; +</script> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/selection-painted-overlay-path.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/selection-painted-overlay-path.html index 6d8904a4..2acd7b26 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/selection-painted-overlay-path.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/selection-painted-overlay-path.html
@@ -1,4 +1,5 @@ <!DOCTYPE html> +<html class="reftest-wait"> <meta charset="UTF-8"> <title>drawElementImage draws selection with custom highlights</title> <link rel="help" href="https://github.com/WICG/html-in-canvas"> @@ -6,6 +7,7 @@ <link rel="match" href="selection-painted-overlay-path-ref.html"> <meta name="assert" value="When preserving privacy for readback, do draw selection when using the highlight overlay painting path (triggered by the custom highlight)."> <script src="/wpt_internal/css/support/markers.js"></script> +<script src="/common/reftest-wait.js"></script> <style> #child { width: 100px; @@ -35,7 +37,7 @@ </div> </canvas> <script> - function runTest() { + async function runTest() { setSelection(editable, 5, 11) let r = new Range(); @@ -43,8 +45,13 @@ r.setEnd(editable, 1); CSS.highlights.set("example-highlight", new Highlight(r)); + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); + canvas.getContext("2d").drawElementImage(child, 0, 0); + takeScreenshot(); } - onload = () => runTest(); -</script> \ No newline at end of file + onload = runTest; +</script> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/shape-image-png-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/shape-image-png-ignored.https.sub.html index 5913288..4ce5385 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/shape-image-png-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/shape-image-png-ignored.https.sub.html
@@ -50,7 +50,9 @@ </canvas> <script> window.onload = () => { - test(function(t) { + promise_test(async function(t) { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); var context = canvas.getContext("2d", { willReadFrequently: true }); context.drawElementImage(document.getElementById("child"), 0, 0); @@ -100,10 +102,8 @@ assert_equals(pixel[0], 255, "Cross origin should not draw, red channel"); assert_equals(pixel[1], 0, "Cross origin should not draw, green channel"); assert_equals(pixel[2], 0, "Cross origin should not draw, blue channel"); - - t.done(); }); } </script> </body> -</html> \ No newline at end of file +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/shape-image-svg-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/shape-image-svg-ignored.https.sub.html index 502e926f..a1add57a 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/shape-image-svg-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/shape-image-svg-ignored.https.sub.html
@@ -52,7 +52,9 @@ </canvas> <script> window.onload = () => { - test(function(t) { + promise_test(async function(t) { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); var context = canvas.getContext("2d", { willReadFrequently: true }); context.drawElementImage(document.getElementById("child"), 0, 0); @@ -102,10 +104,8 @@ assert_equals(pixel[0], 255, "Cross origin should not draw, red channel"); assert_equals(pixel[1], 0, "Cross origin should not draw, green channel"); assert_equals(pixel[2], 0, "Cross origin should not draw, blue channel"); - - t.done(); }); } </script> </body> -</html> \ No newline at end of file +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/spelling-ignored-fast-path-ref.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/spelling-ignored-fast-path-ref.html index 89920f8..2d3f334 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/spelling-ignored-fast-path-ref.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/spelling-ignored-fast-path-ref.html
@@ -1,6 +1,8 @@ <!DOCTYPE html> +<html class="reftest-wait"> <meta charset="UTF-8"> <title>drawElementImage does not draw spelling - ref</title> +<script src="/common/reftest-wait.js"></script> <style> #child { width: 100px; @@ -17,9 +19,13 @@ </div> </canvas> <script> - function runTest() { + async function runTest() { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); canvas.getContext("2d").drawElementImage(child, 0, 0); + takeScreenshot(); } onload = () => runTest(); -</script> \ No newline at end of file +</script> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/spelling-ignored-fast-path.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/spelling-ignored-fast-path.html index ea7d6af..1b9f2b3 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/spelling-ignored-fast-path.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/spelling-ignored-fast-path.html
@@ -1,4 +1,5 @@ <!DOCTYPE html> +<html class="reftest-wait"> <meta charset="UTF-8"> <title>drawElementImage does not draw spelling</title> <link rel="help" href="https://github.com/WICG/html-in-canvas"> @@ -6,6 +7,7 @@ <link rel="match" href="spelling-ignored-fast-path-ref.html"> <meta name="assert" value="When preserving privacy for readback, do not draw spelling markers when using the fast path."> <script src="/wpt_internal/css/support/markers.js"></script> +<script src="/common/reftest-wait.js"></script> <style> #child { width: 100px; @@ -22,11 +24,15 @@ </div> </canvas> <script> - function runTest() { + async function runTest() { addSpellingMarker(editable, 5, 10) + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); canvas.getContext("2d").drawElementImage(child, 0, 0); + takeScreenshot(); } onload = () => runTest(); -</script> \ No newline at end of file +</script> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/spelling-ignored-overlay-path-ref.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/spelling-ignored-overlay-path-ref.html index b51bfcb7..a289621 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/spelling-ignored-overlay-path-ref.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/spelling-ignored-overlay-path-ref.html
@@ -1,6 +1,8 @@ <!DOCTYPE html> +<html class="reftest-wait"> <meta charset="UTF-8"> <title>drawElementImage does not draw spelling with custom highlights - ref</title> +<script src="/common/reftest-wait.js"></script> <style> #child { width: 100px; @@ -21,14 +23,18 @@ </div> </canvas> <script> - function runTest() { + async function runTest() { let r = new Range(); r.setStart(document.body, 0); r.setEnd(document.body, 1); CSS.highlights.set("example-highlight", new Highlight(r)); + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); canvas.getContext("2d").drawElementImage(child, 0, 0); + takeScreenshot(); } onload = () => runTest(); -</script> \ No newline at end of file +</script> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/spelling-ignored-overlay-path.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/spelling-ignored-overlay-path.html index 57fa4397..dd2e79e 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/spelling-ignored-overlay-path.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/spelling-ignored-overlay-path.html
@@ -1,4 +1,5 @@ <!DOCTYPE html> +<html class="reftest-wait"> <meta charset="UTF-8"> <title>drawElementImage does not draw spelling with custom highlights</title> <link rel="help" href="https://github.com/WICG/html-in-canvas"> @@ -6,6 +7,7 @@ <link rel="match" href="spelling-ignored-overlay-path-ref.html"> <meta name="assert" value="When preserving privacy for readback, do not draw spelling markers when using the highlight overlay painting path (triggered by the custom highlight)."> <script src="/wpt_internal/css/support/markers.js"></script> +<script src="/common/reftest-wait.js"></script> <style> #child { width: 100px; @@ -30,7 +32,7 @@ </div> </canvas> <script> - function runTest() { + async function runTest() { addSpellingMarker(editable, 5, 10) let r = new Range(); @@ -38,8 +40,12 @@ r.setEnd(document.body, 1); CSS.highlights.set("example-highlight", new Highlight(r)); + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); canvas.getContext("2d").drawElementImage(child, 0, 0); + takeScreenshot(); } onload = () => runTest(); -</script> \ No newline at end of file +</script> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/subframe-cross-origin-frame.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/subframe-cross-origin-frame.https.sub.html index f83e3e8f..156d5174 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/subframe-cross-origin-frame.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/subframe-cross-origin-frame.https.sub.html
@@ -9,10 +9,10 @@ <script src='/resources/testharnessreport.js'></script> </head> <body> - <iframe id=crossOrigin width=100 height=100 src="https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/privacy/support/subframe-with-canvas-iframe.https.sub.html"></iframe> + <iframe id=crossOrigin width=100 height=200 src="https://{{hosts[alt][www]}}:{{ports[h2][0]}}/wpt_internal/html/canvas/drawElementImage/privacy/support/subframe-with-canvas-iframe.https.sub.html"></iframe> <script> fetch_tests_from_window(crossOrigin.contentWindow); </script> </body> -</html> \ No newline at end of file +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/support/subframe-with-canvas-background-png-images.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/support/subframe-with-canvas-background-png-images.https.sub.html index 3f71be8..6121e7be 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/support/subframe-with-canvas-background-png-images.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/support/subframe-with-canvas-background-png-images.https.sub.html
@@ -38,7 +38,9 @@ <script> window.onload = () => { - test(function(t) { + promise_test(async function(t) { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); var context = canvas.getContext("2d", { willReadFrequently: true }); context.drawElementImage(child, 0, 0); @@ -51,10 +53,8 @@ assert_equals(pixel[0], 0, "Cross origin should not draw"); assert_equals(pixel[1], 128, "Cross origin should not draw"); assert_equals(pixel[2], 0, "Cross origin should not draw"); - - t.done(); }); } </script> </body> -</html> \ No newline at end of file +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/support/subframe-with-canvas-html-images.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/support/subframe-with-canvas-html-images.https.sub.html index 3251815..07d19af2 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/support/subframe-with-canvas-html-images.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/support/subframe-with-canvas-html-images.https.sub.html
@@ -31,7 +31,9 @@ </canvas> <script> window.onload = function() { - test(function(t) { + promise_test(async function(t) { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); var context = canvas.getContext("2d"); context.drawElementImage(child, 0, 0);
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/support/subframe-with-canvas-iframe.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/support/subframe-with-canvas-iframe.https.sub.html index 8511d09..ed8320e 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/support/subframe-with-canvas-iframe.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/support/subframe-with-canvas-iframe.https.sub.html
@@ -39,7 +39,9 @@ function pendingIframeLoaded() { if (!--sPendingIframesToLoad) { - test(function(t) { + promise_test(async function(t) { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); var context = canvas.getContext("2d", { willReadFrequently: true }); context.drawElementImage(child, 0, 0); @@ -52,8 +54,6 @@ assert_equals(pixel[0], 255, "Same origin should draw"); assert_equals(pixel[1], 0, "Same origin should draw"); assert_equals(pixel[2], 0, "Same origin should draw"); - - t.done(); }); } } @@ -62,4 +62,4 @@ crossOrigin.onload = pendingIframeLoaded; </script> </body> -</html> \ No newline at end of file +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/svg-images-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/svg-images-ignored.https.sub.html index feba6cf8..0ad3efe 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/svg-images-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/svg-images-ignored.https.sub.html
@@ -16,7 +16,9 @@ <script> window.onload = function() { - test(function(t) { + promise_test(async function(t) { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); var context = canvas.getContext("2d"); context.drawElementImage(child, 0, 0); @@ -29,8 +31,6 @@ assert_equals(pixel[0], 0, "Cross origin should not draw"); assert_equals(pixel[1], 128, "Cross origin should not draw"); assert_equals(pixel[2], 0, "Cross origin should not draw"); - - t.done(); }); }; </script>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/svg-use-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/svg-use-ignored.https.sub.html index 6e845619..fcd3986 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/svg-use-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/svg-use-ignored.https.sub.html
@@ -19,10 +19,16 @@ <script> window.onload = () => { - test(function(t) { + promise_test(async function(t) { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); var context = canvas.getContext("2d", { willReadFrequently: true }); context.drawElementImage(document.getElementById("child-same"), 0, 0); - context.drawElementImage(document.getElementById("child-cross"), 0, 100); + assert_throws_dom( + "InvalidStateError", + () => context.drawElementImage(document.getElementById("child-cross"), 0, 100), + 'drawElementImage cannot draw cross-origin svg `use`.' + ); pixel = context.getImageData(50, 50, 1, 1).data; assert_equals(pixel[0], 0, "Same origin should draw red"); @@ -34,10 +40,8 @@ assert_equals(pixel[0], 0, "Cross origin should not draw red"); assert_equals(pixel[1], 0, "Cross origin should not draw green"); assert_equals(pixel[2], 0, "Cross origin should not draw blue"); - - t.done(); }); } </script> </body> -</html> \ No newline at end of file +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/target-text-ignored-overlay-path-ref.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/target-text-ignored-overlay-path-ref.html index 4e493dfe..5215401 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/target-text-ignored-overlay-path-ref.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/target-text-ignored-overlay-path-ref.html
@@ -1,6 +1,8 @@ <!DOCTYPE html> +<html class="reftest-wait"> <meta charset="UTF-8"> <title>drawElementImage does not draw target-text with custom highlights - ref</title> +<script src="/common/reftest-wait.js"></script> <style> #child { width: 100px; @@ -21,14 +23,18 @@ </div> </canvas> <script> - function runTest() { + async function runTest() { let r = new Range(); r.setStart(document.body, 0); r.setEnd(document.body, 1); CSS.highlights.set("example-highlight", new Highlight(r)); + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); canvas.getContext("2d").drawElementImage(child, 0, 0); + takeScreenshot(); } onload = () => runTest(); -</script> \ No newline at end of file +</script> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/target-text-ignored-overlay-path.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/target-text-ignored-overlay-path.html index 4ea9039..9a875a4 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/target-text-ignored-overlay-path.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/target-text-ignored-overlay-path.html
@@ -1,10 +1,12 @@ <!DOCTYPE html> +<html class="reftest-wait"> <meta charset="UTF-8"> <title>drawElementImage does not draw target-text with custom highlights</title> <link rel="help" href="https://github.com/WICG/html-in-canvas"> <link rel="author" href="mailto:schenney@chromium.org"> <link rel="match" href="target-text-ignored-overlay-path-ref.html"> <meta name="assert" value="When preserving privacy for readback, do not draw target text markers when using the highlight overlay painting path (triggered by the custom highlight)."> +<script src="/common/reftest-wait.js"></script> <style> #child { width: 100px; @@ -29,7 +31,7 @@ </div> </canvas> <script> - function runTest() { + async function runTest() { window.location.hash = "#:~:text=Match"; let r = new Range(); @@ -37,8 +39,12 @@ r.setEnd(document.body, 1); CSS.highlights.set("example-highlight", new Highlight(r)); + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); canvas.getContext("2d").drawElementImage(child, 0, 0); + takeScreenshot(); } onload = () => runTest(); -</script> \ No newline at end of file +</script> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/video-ignored.https.sub.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/video-ignored.https.sub.html index 7de33a7..18950da 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/video-ignored.https.sub.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/privacy/video-ignored.https.sub.html
@@ -40,7 +40,9 @@ <script> onload = () => { - test(function(t) { + promise_test(async function(t) { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); var context = canvas.getContext("2d"); context.drawElementImage(child, 0, 0); @@ -53,10 +55,8 @@ assert_equals(pixel[0], 0, "Cross origin should not draw"); assert_equals(pixel[1], 128, "Cross origin should not draw"); assert_equals(pixel[2], 0, "Cross origin should not draw"); - - t.done(); }); } </script> </body> -</html> \ No newline at end of file +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/scale.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/scale.html index 7d9969e..6ccaa2f 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/scale.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/scale.html
@@ -59,6 +59,7 @@ Promise.all(Array.from(document.querySelectorAll("canvas")).map(async cvs => { await resizeToPixelGrid(cvs); + await new Promise(setTimeout); const target = cvs.firstElementChild; const ctx = cvs.getContext("2d"); if (target.hasAttribute("xscale") && target.hasAttribute("yscale")) {
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/shadow-basic.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/shadow-basic.html index c01078d..9914bab 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/shadow-basic.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/shadow-basic.html
@@ -1,9 +1,10 @@ <!DOCTYPE html> -<html> +<html class="reftest-wait"> <title>Canvas.drawElementImage: green rect with shadow</title> <link rel="help" href="https://github.com/WICG/html-in-canvas"> <link rel="author" href="mailto:schenney@chromium.org"> <link rel="match" href="shadow-basic-ref.html"> +<script src="/common/reftest-wait.js"></script> <meta name=fuzzy content="0-2;0-1990"> <style> #child { @@ -28,9 +29,10 @@ context.shadowOffsetX = 20; context.shadowOffsetY = 10; context.drawElementImage(child, 20, 30); + takeScreenshot(); } -onload = () => runTest(); +onload = () => requestAnimationFrame(() => setTimeout(runTest)); </script> </html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/shadow-non-opaque-element.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/shadow-non-opaque-element.html index f019f466..48d80172 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/shadow-non-opaque-element.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/shadow-non-opaque-element.html
@@ -1,10 +1,11 @@ <!DOCTYPE html> -<html> +<html class="reftest-wait"> <title>Canvas.drawElementImage: non opaque element with compositing op</title> <link rel="help" href="https://github.com/WICG/html-in-canvas"> <link rel="author" href="mailto:schenney@chromium.org"> <link rel="match" href="shadow-non-opaque-element-ref.html"> <meta name=fuzzy content="0-2;0-2185"> +<script src="/common/reftest-wait.js"></script> <style> #child { width: 100px; @@ -39,9 +40,10 @@ context.shadowOffsetX = 20; context.shadowOffsetY = 10; context.drawElementImage(child, 20, 30); + takeScreenshot(); } -onload = () => runTest(); +onload = () => requestAnimationFrame(() => setTimeout(runTest)); </script> </html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/svg-foreign-object.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/svg-foreign-object.html index 8118271..f9d9e629 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/svg-foreign-object.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/svg-foreign-object.html
@@ -1,9 +1,10 @@ <!DOCTYPE html> -<html> +<html class="reftest-wait"> <title>Canvas.drawElementImage: SVG foreignObject green rect</title> <link rel="help" href="https://github.com/WICG/html-in-canvas"> <link rel="author" href="mailto:schenney@chromium.org"> <link rel="match" href="basic-rect-ref.html"> +<script src="/common/reftest-wait.js"></script> <style> #canvas { background: grey; @@ -21,9 +22,10 @@ <script> function runTest() { canvas.getContext("2d").drawElementImage(child, 0, 0); + takeScreenshot(); } -onload = () => runTest(); +onload = () => requestAnimationFrame(() => setTimeout(runTest)); </script> </html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/svg-rect.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/svg-rect.html index 894be01..78eba35 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/svg-rect.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/svg-rect.html
@@ -1,9 +1,10 @@ <!DOCTYPE html> -<html> +<html class="reftest-wait"> <title>Canvas.drawElementImage: SVG green rect</title> <link rel="help" href="https://github.com/WICG/html-in-canvas"> <link rel="author" href="mailto:schenney@chromium.org"> <link rel="match" href="basic-rect-ref.html"> +<script src="/common/reftest-wait.js"></script> <style> #canvas { background: grey; @@ -19,9 +20,10 @@ <script> function runTest() { canvas.getContext("2d").drawElementImage(child, 20, 30); + takeScreenshot(); } -onload = () => runTest(); +onload = () => requestAnimationFrame(() => setTimeout(runTest)); </script> </html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/tex-element-image-2d-basic.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/tex-element-image-2d-basic.html index 43888a3d..dee85e6a 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/tex-element-image-2d-basic.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/tex-element-image-2d-basic.html
@@ -188,7 +188,7 @@ }, "verify the texElementImage2D works as expected"); } - onload = () => runTest(); + onload = () => requestAnimationFrame(() => setTimeout(runTest)); </script> </body> </html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/top-layer-not-painted-in-canvas.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/top-layer-not-painted-in-canvas.html index 75dbe7a..d538b2b7 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/top-layer-not-painted-in-canvas.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/top-layer-not-painted-in-canvas.html
@@ -27,12 +27,15 @@ </canvas> <script> -onload = () => { +async function runTest() { + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); dialog.showModal(); - requestAnimationFrame(() => { - canvas.getContext('2d').drawElementImage(target, 100, 0); - requestAnimationFrame(takeScreenshot); - }); + await new Promise(requestAnimationFrame); + await new Promise(setTimeout); + canvas.getContext('2d').drawElementImage(target, 100, 0); + takeScreenshot(); } +onload = () => runTest(); </script> </html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/webgl-css-transform-on-div.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/webgl-css-transform-on-div.html index 06e9fa5..a4c29e4 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/webgl-css-transform-on-div.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/webgl-css-transform-on-div.html
@@ -1,9 +1,11 @@ <!DOCTYPE html> +<html class="reftest-wait"> <head> <title>texElementImage2D ignores CSS transforms</title> <link rel="help" href="https://github.com/WICG/html-in-canvas"> <link rel="match" href="css-transform-on-div-ref.html"> <meta name="assert" value="A div with a CSS transform paints without the transform."> + <script src="/common/reftest-wait.js"></script> <style> canvas { background: black; @@ -29,9 +31,11 @@ const canvas = document.querySelector("canvas"); await resizeToPixelGrid(canvas); +await new Promise(setTimeout); const prog = new SimpleGLProgram(canvas.getContext("webgl2")); const target = canvas.firstElementChild; prog.render(target); takeScreenshot(); </script> </body> +</html>
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/webgl-ink-overflow.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/webgl-ink-overflow.html index 7349e24..0384f6c0 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/webgl-ink-overflow.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/webgl-ink-overflow.html
@@ -62,6 +62,7 @@ Promise.all(Array.from(document.querySelectorAll("canvas")).map(async canvas => { await resizeToPixelGrid(canvas); + await new Promise(setTimeout); const prog = new SimpleGLProgram(canvas.getContext("webgl2")); const target = canvas.firstElementChild; const targetStyle = getComputedStyle(target);
diff --git a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/webgl-scaling.html b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/webgl-scaling.html index 0db1097c..f52a015 100644 --- a/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/webgl-scaling.html +++ b/third_party/blink/web_tests/wpt_internal/html/canvas/drawElementImage/webgl-scaling.html
@@ -60,6 +60,7 @@ Promise.all(Array.from(document.querySelectorAll("canvas")).map(async canvas => { await resizeToPixelGrid(canvas); + await new Promise(setTimeout); const prog = new SimpleGLProgram(canvas.getContext("webgl2")); const target = canvas.firstElementChild; if (target.hasAttribute("xscale") && target.hasAttribute("yscale")) {
diff --git a/third_party/catapult b/third_party/catapult index 87c9af3..0f9739d 160000 --- a/third_party/catapult +++ b/third_party/catapult
@@ -1 +1 @@ -Subproject commit 87c9af3b7c6495ab0a0ba86a3eb6a58ff84eca85 +Subproject commit 0f9739da7f76bbb5bc9c108986880d5cfb9b9cf6
diff --git a/third_party/dawn b/third_party/dawn index a1b78e4d..0e9fde4 160000 --- a/third_party/dawn +++ b/third_party/dawn
@@ -1 +1 @@ -Subproject commit a1b78e4d9d2b8ac509af227aff8e3004d2ace7f1 +Subproject commit 0e9fde4e362e8e402c7ca3cc3d241775eb80bdb8
diff --git a/third_party/devtools-frontend/src b/third_party/devtools-frontend/src index c804bd5..e27f62c 160000 --- a/third_party/devtools-frontend/src +++ b/third_party/devtools-frontend/src
@@ -1 +1 @@ -Subproject commit c804bd56a0e5089b60ad36855f181564fa114789 +Subproject commit e27f62c91b96b3f59a6322ee587f80bfa2d9ed96
diff --git a/third_party/eigen3/src b/third_party/eigen3/src index f64d1e0a..25dba49 160000 --- a/third_party/eigen3/src +++ b/third_party/eigen3/src
@@ -1 +1 @@ -Subproject commit f64d1e0acc2c2c33e325b8dd7b2b4673de2b9f14 +Subproject commit 25dba492e30e19dce3d4bd4cd38af2f5c88c387c
diff --git a/third_party/fuzztest/src b/third_party/fuzztest/src index 362a279..f67a6fe 160000 --- a/third_party/fuzztest/src +++ b/third_party/fuzztest/src
@@ -1 +1 @@ -Subproject commit 362a279f0ad018574278e72c4e98e2b99d3991bb +Subproject commit f67a6fe9ca3afc4d71997e75e8d87c571f0c30cb
diff --git a/third_party/openscreen/src b/third_party/openscreen/src index d55bd31..89fa62b 160000 --- a/third_party/openscreen/src +++ b/third_party/openscreen/src
@@ -1 +1 @@ -Subproject commit d55bd311eb9a970a8e4fb25cbe5ae016d4d3d812 +Subproject commit 89fa62bbbe45c5cc87c6a5e84ff3f2d03a3656c8
diff --git a/third_party/pdfium b/third_party/pdfium index 65f4269..fcfdf1c 160000 --- a/third_party/pdfium +++ b/third_party/pdfium
@@ -1 +1 @@ -Subproject commit 65f4269f6accf48306adb7fff10c07d72d56b1ea +Subproject commit fcfdf1c3e22ed9d0f2341d596297393cc7f5ad53
diff --git a/third_party/perfetto b/third_party/perfetto index dcb5e98..fa49f16 160000 --- a/third_party/perfetto +++ b/third_party/perfetto
@@ -1 +1 @@ -Subproject commit dcb5e98f9f6e9ba61a95b0a3fef56671b0b6ba83 +Subproject commit fa49f16ad958de750da23698f12aebb9f1ae662f
diff --git a/third_party/skia b/third_party/skia index 9333e22..28172a4 160000 --- a/third_party/skia +++ b/third_party/skia
@@ -1 +1 @@ -Subproject commit 9333e227108653e751321a2420f88ae6dc2156ae +Subproject commit 28172a4e03af8430d1bb6497116baa4b260ad4eb
diff --git a/third_party/spirv-tools/src b/third_party/spirv-tools/src index 1c8c845..4972c69 160000 --- a/third_party/spirv-tools/src +++ b/third_party/spirv-tools/src
@@ -1 +1 @@ -Subproject commit 1c8c845843ca77f70a862fc94d371d36fe703498 +Subproject commit 4972c69eb50255b314fc0925ca757c4417e6b6c0
diff --git a/third_party/vulkan-deps b/third_party/vulkan-deps index 92727ef..dd5ab01 160000 --- a/third_party/vulkan-deps +++ b/third_party/vulkan-deps
@@ -1 +1 @@ -Subproject commit 92727ef4b277d71d38659e8679bd9f3c59651401 +Subproject commit dd5ab0120a15c8373e8cfa6278b23918dbe09dbf
diff --git a/third_party/vulkan-validation-layers/src b/third_party/vulkan-validation-layers/src index cbfe559..9076c34 160000 --- a/third_party/vulkan-validation-layers/src +++ b/third_party/vulkan-validation-layers/src
@@ -1 +1 @@ -Subproject commit cbfe559d89689bd5e471a39560e137043fb0dc36 +Subproject commit 9076c341487013d4650377e164fd4816779f501d
diff --git a/third_party/webrtc b/third_party/webrtc index 1b08071..6f90f4f 160000 --- a/third_party/webrtc +++ b/third_party/webrtc
@@ -1 +1 @@ -Subproject commit 1b08071e7a4747ae7614aa7898d0d393bbe297a0 +Subproject commit 6f90f4ff7a7fb27e8e6785bf40c5ca46b6254162
diff --git a/tools/autotest/main.py b/tools/autotest/main.py index d16d747..c33c3fa 100644 --- a/tools/autotest/main.py +++ b/tools/autotest/main.py
@@ -54,14 +54,31 @@ help=__doc__, context_settings=dict(ignore_unknown_options=True, allow_interspersed_args=True, + allow_extra_args=True, help_option_names=['-h', '--help'])) @autotest_options -@click.argument('files', nargs=-1) @click.pass_context @telemetry.tracer.start_as_current_span('chromium.tools.autotest.main') def main(ctx, **kwargs) -> int: - # Capture "extras" (arguments click didn't recognize) - kwargs['extras'] = ctx.args + + files_to_test = [] + extras = [] + + parsing_files = True + for arg in ctx.args: + if len(files_to_test) == 0: + parsing_files = True + + if arg.startswith('-'): + parsing_files = False + + if parsing_files: + files_to_test.append(arg) + else: + extras.append(arg) + + kwargs['files'] = tuple(files_to_test) + kwargs['extras'] = extras config: AutotestConfig = AutotestConfig(**kwargs)
diff --git a/extensions/common/api/content_scripts.idl b/tools/json_schema_compiler/test/converted_schemas/content_scripts.idl similarity index 100% rename from extensions/common/api/content_scripts.idl rename to tools/json_schema_compiler/test/converted_schemas/content_scripts.idl
diff --git a/tools/json_schema_compiler/test/converted_schemas/content_scripts.webidl b/tools/json_schema_compiler/test/converted_schemas/content_scripts.webidl new file mode 100644 index 0000000..b325e022 --- /dev/null +++ b/tools/json_schema_compiler/test/converted_schemas/content_scripts.webidl
@@ -0,0 +1,80 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +[ExternalExtensionType="extensionTypes.RunAt"] +typedef object ExtensionTypesRunAt; + +[ExternalExtensionType="extensionTypes.ExecutionWorld"] +typedef object ExtensionTypesExecutionWorld; + +// Describes a content script to be injected into a web page. +dictionary ContentScript { + // Specifies which pages this content script will be injected into. See + // <a href="develop/concepts/match-patterns">Match Patterns</a> for more + // details on the syntax of these strings. + required sequence<DOMString> matches; + + // Excludes pages that this content script would otherwise be injected into. + // See <a href="develop/concepts/match-patterns">Match Patterns</a> for more + // details on the syntax of these strings. + sequence<DOMString> exclude_matches; + + // The list of CSS files to be injected into matching pages. These are + // injected in the order they appear in this array, before any DOM is + // constructed or displayed for the page. + sequence<DOMString> css; + + // The list of JavaScript files to be injected into matching pages. These + // are injected in the order they appear in this array. + sequence<DOMString> js; + + // If specified true, it will inject into all frames, even if the frame is + // not the top-most frame in the tab. Each frame is checked independently + // for URL requirements; it will not inject into child frames if the URL + // requirements are not met. Defaults to false, meaning that only the top + // frame is matched. + boolean all_frames; + + // Whether the script should inject into any frames where the URL belongs to + // a scheme that would never match a specified Match Pattern, including + // about:, data:, blob:, and filesystem: schemes. In these cases, in order + // to determine if the script should inject, the origin of the URL is + // checked. If the origin is `null` (as is the case for data: URLs), then + // the "initiator" or "creator" origin is used (i.e., the origin of the + // frame that created or navigated this frame). Note that this may not + // be the parent frame, if the frame was navigated by another frame in the + // document hierarchy. + boolean match_origin_as_fallback; + + // Whether the script should inject into an about:blank frame where the + // parent or opener frame matches one of the patterns declared in matches. + // Defaults to false. + boolean match_about_blank; + + // Applied after matches to include only those URLs that also match this + // glob. Intended to emulate the + // <a href="http://wiki.greasespot.net/Metadata_Block#.40include">@include + // </a> Greasemonkey keyword. + sequence<DOMString> include_globs; + + // Applied after matches to exclude URLs that match this glob. Intended to + // emulate the + // <a href="https://wiki.greasespot.net/Metadata_Block#.40exclude">@exclude + // </a> Greasemonkey keyword. + sequence<DOMString> exclude_globs; + + // Specifies when JavaScript files are injected into the web page. The + // preferred and default value is <code>document_idle</code>. + ExtensionTypesRunAt run_at; + + // The JavaScript "world" to run the script in. Defaults to + // <code>ISOLATED</code>. Only available in Manifest V3 extensions. + ExtensionTypesExecutionWorld world; +}; + +// Stub namespace for the "content_scripts" manifest key. +[generate_error_messages, Namespace=contentScripts] +partial dictionary ExtensionManifest { + sequence<ContentScript> content_scripts; +};
diff --git a/tools/json_schema_compiler/web_idl_diff_tool_test.py b/tools/json_schema_compiler/web_idl_diff_tool_test.py index 2e48eee..ad6dd95 100755 --- a/tools/json_schema_compiler/web_idl_diff_tool_test.py +++ b/tools/json_schema_compiler/web_idl_diff_tool_test.py
@@ -60,6 +60,7 @@ ('power.idl', 'power.webidl'), ('serial.idl', 'serial.webidl'), ('bluetooth_private.idl', 'bluetooth_private.webidl'), + ('content_scripts.idl', 'content_scripts.webidl'), ] # LoadAndReturnUnifiedDiff expects file paths relative to the repo root. converted_schema_path = 'tools/json_schema_compiler/test/converted_schemas/'
diff --git a/tools/metrics/histograms/metadata/accessibility/histograms.xml b/tools/metrics/histograms/metadata/accessibility/histograms.xml index a9e09f22..ce21f4a 100644 --- a/tools/metrics/histograms/metadata/accessibility/histograms.xml +++ b/tools/metrics/histograms/metadata/accessibility/histograms.xml
@@ -3402,6 +3402,18 @@ </summary> </histogram> +<histogram + name="Accessibility.ReadAnything.TimeFromStartDistillationToOnArticleReady" + units="ms" expires_after="2027-03-01"> + <owner>martinglopez@google.com</owner> + <owner>chrome-irm-devs@google.com</owner> + <summary> + Records the time it takes for the Readability distiller to complete a + distillation process. This starts when StartDistillation is called and ends + when OnArticleReady is called. + </summary> +</histogram> + <histogram name="Accessibility.ReadAnything.TimeFrom{Flow}StartedTo{Method}" units="ms" expires_after="2026-08-02"> <owner>lwinston@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/sb_client/enums.xml b/tools/metrics/histograms/metadata/sb_client/enums.xml index aac92b12..10c357e 100644 --- a/tools/metrics/histograms/metadata/sb_client/enums.xml +++ b/tools/metrics/histograms/metadata/sb_client/enums.xml
@@ -457,10 +457,6 @@ label="Phishing classifier callback is empty on classification completion"/> <int value="9" label="Phishing classification request is responded"/> - <int value="10" label="Renderer page layout has finished loading"/> - <int value="11" - label="Renderer page layout has finished loading again in middle of - classification"/> </enum> <enum name="TailoredWarningType">
diff --git a/tools/perf/core/perfetto_binary_roller/binary_deps.json b/tools/perf/core/perfetto_binary_roller/binary_deps.json index e0bc4371..d18b90a 100644 --- a/tools/perf/core/perfetto_binary_roller/binary_deps.json +++ b/tools/perf/core/perfetto_binary_roller/binary_deps.json
@@ -6,7 +6,7 @@ }, "win": { "hash": "e31aeb7477b718b8a4817e024588f8996bb627b1", - "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/ca4508e8f261cdaf3d5b48c9381a504b99b2669a/trace_processor_shell.exe" + "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/win/fa49f16ad958de750da23698f12aebb9f1ae662f/trace_processor_shell.exe" }, "linux_arm": { "hash": "46d798c1864490cbb2ee053d6eda436184470e69", @@ -22,7 +22,7 @@ }, "linux": { "hash": "d5d0a855a03ac3281a75d252c868dbcb97d9ada7", - "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/dcb5e98f9f6e9ba61a95b0a3fef56671b0b6ba83/trace_processor_shell" + "full_remote_path": "chromium-telemetry/perfetto_binaries/trace_processor_shell/linux/fa49f16ad958de750da23698f12aebb9f1ae662f/trace_processor_shell" } }, "power_profile.sql": {
diff --git a/tools/typescript/definitions/pending.d.ts b/tools/typescript/definitions/pending.d.ts index cb3b3f7b..5752a05 100644 --- a/tools/typescript/definitions/pending.d.ts +++ b/tools/typescript/definitions/pending.d.ts
@@ -155,3 +155,9 @@ locales: string|string[], options?: DurationFormatOptions): string[], }; } + +// See https://github.com/microsoft/TypeScript/issues/46135. +declare module '*.css' { + const _default: CSSStyleSheet; + export default _default; +}
diff --git a/ui/views/win/fullscreen_handler.cc b/ui/views/win/fullscreen_handler.cc index b51df0e8..4a26b26 100644 --- a/ui/views/win/fullscreen_handler.cc +++ b/ui/views/win/fullscreen_handler.cc
@@ -65,18 +65,18 @@ // Save current window state if not already fullscreen. if (!fullscreen_) { - saved_window_info_.style = GetWindowLong(hwnd_, GWL_STYLE); - saved_window_info_.ex_style = GetWindowLong(hwnd_, GWL_EXSTYLE); + saved_window_info_.style = ::GetWindowLong(hwnd_, GWL_STYLE); + saved_window_info_.ex_style = ::GetWindowLong(hwnd_, GWL_EXSTYLE); // Store the original window rect, DPI, and monitor info to detect changes // and more accurately restore window placements when exiting fullscreen. ::GetWindowRect(hwnd_, &saved_window_info_.rect); saved_window_info_.dpi = display::win::GetScreenWin()->GetDPIForHWND(hwnd_); saved_window_info_.monitor = - MonitorFromWindow(hwnd_, MONITOR_DEFAULTTONEAREST); + ::MonitorFromWindow(hwnd_, MONITOR_DEFAULTTONEAREST); saved_window_info_.monitor_info.cbSize = sizeof(saved_window_info_.monitor_info); - GetMonitorInfo(saved_window_info_.monitor, - &saved_window_info_.monitor_info); + ::GetMonitorInfo(saved_window_info_.monitor, + &saved_window_info_.monitor_info); } fullscreen_ = fullscreen; @@ -84,9 +84,9 @@ auto ref = weak_ptr_factory_.GetWeakPtr(); if (fullscreen_) { // Set new window style and size. - SetWindowLong(hwnd_, GWL_STYLE, - saved_window_info_.style & ~(WS_CAPTION | WS_THICKFRAME)); - SetWindowLong( + ::SetWindowLong(hwnd_, GWL_STYLE, + saved_window_info_.style & ~(WS_CAPTION | WS_THICKFRAME)); + ::SetWindowLong( hwnd_, GWL_EXSTYLE, saved_window_info_.ex_style & ~(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE)); @@ -98,29 +98,29 @@ gfx::Rect window_rect = screen_win_display.screen_rect(); if (target_display_id == display::kInvalidDisplayId || screen_win_display.display().id() != target_display_id) { - HMONITOR monitor = MonitorFromWindow(hwnd_, MONITOR_DEFAULTTONEAREST); + HMONITOR monitor = ::MonitorFromWindow(hwnd_, MONITOR_DEFAULTTONEAREST); MONITORINFO monitor_info; monitor_info.cbSize = sizeof(monitor_info); - GetMonitorInfo(monitor, &monitor_info); + ::GetMonitorInfo(monitor, &monitor_info); window_rect = gfx::Rect(monitor_info.rcMonitor); } - SetWindowPos(hwnd_, nullptr, window_rect.x(), window_rect.y(), - window_rect.width(), window_rect.height(), - SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); + ::SetWindowPos(hwnd_, nullptr, window_rect.x(), window_rect.y(), + window_rect.width(), window_rect.height(), + SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); } else { // Restore the window style and bounds saved prior to entering fullscreen. // Use WS_VISIBLE for windows shown after SetFullscreen: crbug.com/1062251. // Making multiple window adjustments here is ugly, but if SetWindowPos() // doesn't redraw, the taskbar won't be repainted. - SetWindowLong(hwnd_, GWL_STYLE, saved_window_info_.style | WS_VISIBLE); - SetWindowLong(hwnd_, GWL_EXSTYLE, saved_window_info_.ex_style); + ::SetWindowLong(hwnd_, GWL_STYLE, saved_window_info_.style | WS_VISIBLE); + ::SetWindowLong(hwnd_, GWL_EXSTYLE, saved_window_info_.ex_style); gfx::Rect window_rect(saved_window_info_.rect); HMONITOR monitor = - MonitorFromRect(&saved_window_info_.rect, MONITOR_DEFAULTTONEAREST); + ::MonitorFromRect(&saved_window_info_.rect, MONITOR_DEFAULTTONEAREST); MONITORINFO monitor_info; monitor_info.cbSize = sizeof(monitor_info); - GetMonitorInfo(monitor, &monitor_info); + ::GetMonitorInfo(monitor, &monitor_info); // Adjust the window bounds to restore, if displays were disconnected, // virtually rearranged, or otherwise changed metrics during fullscreen. if (monitor != saved_window_info_.monitor || @@ -131,9 +131,9 @@ const int fullscreen_dpi = display::win::GetScreenWin()->GetDPIForHWND(hwnd_); - SetWindowPos(hwnd_, nullptr, window_rect.x(), window_rect.y(), - window_rect.width(), window_rect.height(), - SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); + ::SetWindowPos(hwnd_, nullptr, window_rect.x(), window_rect.y(), + window_rect.width(), window_rect.height(), + SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); const int final_dpi = display::win::GetScreenWin()->GetDPIForHWND(hwnd_); if (final_dpi != saved_window_info_.dpi || final_dpi != fullscreen_dpi) { @@ -150,9 +150,9 @@ window_rect.set_size(gfx::ToCeiledSize(size)); window_rect.AdjustToFit(gfx::Rect(monitor_info.rcWork)); } - SetWindowPos(hwnd_, nullptr, window_rect.x(), window_rect.y(), - window_rect.width(), window_rect.height(), - SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); + ::SetWindowPos(hwnd_, nullptr, window_rect.x(), window_rect.y(), + window_rect.width(), window_rect.height(), + SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); } } if (!ref) {
diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc index 9735afc..6a4f780 100644 --- a/ui/views/win/hwnd_message_handler.cc +++ b/ui/views/win/hwnd_message_handler.cc
@@ -204,9 +204,9 @@ LPARAM l_param) { if (n_code == HC_ACTION && w_param == VK_ESCAPE) { int value = TRUE; - DwmSetWindowAttribute(instance_->host_->hwnd(), - DWMWA_TRANSITIONS_FORCEDISABLED, &value, - sizeof(value)); + ::DwmSetWindowAttribute(instance_->host_->hwnd(), + DWMWA_TRANSITIONS_FORCEDISABLED, &value, + sizeof(value)); if (instance_->hide_on_escape_) { instance_->host_->Hide(); } @@ -217,7 +217,7 @@ // Called from OnNCActivate. BOOL CALLBACK EnumChildWindowsForRedraw(HWND hwnd, LPARAM lparam) { DWORD process_id; - GetWindowThreadProcessId(hwnd, &process_id); + ::GetWindowThreadProcessId(hwnd, &process_id); UINT flags = RDW_INVALIDATE | RDW_NOCHILDREN | RDW_FRAME; if (process_id == GetCurrentProcessId()) { flags |= RDW_UPDATENOW; @@ -356,7 +356,7 @@ hwnd_(owner_->hwnd()), should_lock_(owner_->IsVisible() && !owner->HasChildRenderingWindow() && ::IsWindow(hwnd_) && !owner_->IsHeadless() && - (!(GetWindowLong(hwnd_, GWL_STYLE) & WS_CAPTION))) { + (!(::GetWindowLong(hwnd_, GWL_STYLE) & WS_CAPTION))) { if (should_lock_) { owner_->LockUpdates(); } @@ -474,7 +474,7 @@ } void HWNDMessageHandler::Close() { - if (!IsWindow(hwnd())) { + if (!::IsWindow(hwnd())) { return; // No need to do anything. } @@ -506,22 +506,22 @@ // we need to check to see if we're still a window before trying to destroy // ourself. waiting_for_close_now_ = false; - if (IsWindow(hwnd())) { - DestroyWindow(hwnd()); + if (::IsWindow(hwnd())) { + ::DestroyWindow(hwnd()); } } gfx::Rect HWNDMessageHandler::GetWindowBoundsInScreen() const { RECT r; - GetWindowRect(hwnd(), &r); + ::GetWindowRect(hwnd(), &r); return gfx::Rect(r); } gfx::Rect HWNDMessageHandler::GetClientAreaBoundsInScreen() const { RECT r; - GetClientRect(hwnd(), &r); + ::GetClientRect(hwnd(), &r); POINT point = {r.left, r.top}; - ClientToScreen(hwnd(), &point); + ::ClientToScreen(hwnd(), &point); return gfx::Rect(point.x, point.y, r.right - r.left, r.bottom - r.top); } @@ -560,7 +560,7 @@ // GetWindowPlacement can return misleading position if a normalized // window was resized using Aero Snap feature (see comment 9 in bug // 36421). As a workaround, using GetWindowRect for normalized windows. - succeeded = GetWindowRect(hwnd(), &wp.rcNormalPosition) != 0; + succeeded = ::GetWindowRect(hwnd(), &wp.rcNormalPosition) != 0; DCHECK(succeeded); *bounds = gfx::Rect(wp.rcNormalPosition); @@ -568,8 +568,8 @@ MONITORINFO mi; mi.cbSize = sizeof(mi); succeeded = - GetMonitorInfo(MonitorFromWindow(hwnd(), MONITOR_DEFAULTTONEAREST), - &mi) != 0; + ::GetMonitorInfo( + ::MonitorFromWindow(hwnd(), MONITOR_DEFAULTTONEAREST), &mi) != 0; DCHECK(succeeded); *bounds = gfx::Rect(wp.rcNormalPosition); @@ -598,17 +598,17 @@ } void HWNDMessageHandler::SetParentOrOwner(HWND new_parent) { - LONG style = GetWindowLong(hwnd(), GWL_STYLE); + LONG style = ::GetWindowLong(hwnd(), GWL_STYLE); if (style & WS_CHILD) { // This is a child window. // TODO(crbug.com/40284685): allows setting NULL parent since WinAPI permits // it. It will require updating window styles. See // https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setparent#remarks. DCHECK(new_parent); - SetParent(hwnd(), new_parent); + ::SetParent(hwnd(), new_parent); } else { - SetWindowLongPtr(hwnd(), GWLP_HWNDPARENT, - reinterpret_cast<LONG_PTR>(new_parent)); + ::SetWindowLongPtr(hwnd(), GWLP_HWNDPARENT, + reinterpret_cast<LONG_PTR>(new_parent)); } } @@ -638,7 +638,7 @@ if (it != window_owner_map.end()) { for (HWND owned_child : it->second) { // The window could have been destroyed between EnumWindows and now. - if (IsWindow(owned_child)) { + if (::IsWindow(owned_child)) { owned_windows.push_back(owned_child); // Queue the current child for BFS exploration. to_process.push(owned_child); @@ -666,13 +666,13 @@ } void HWNDMessageHandler::SetSize(const gfx::Size& size) { - SetWindowPos(hwnd(), nullptr, 0, 0, size.width(), size.height(), - SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE); + ::SetWindowPos(hwnd(), nullptr, 0, 0, size.width(), size.height(), + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE); } void HWNDMessageHandler::CenterWindow(const gfx::Size& size) { - HWND parent = GetParent(hwnd()); - if (!IsWindow(parent)) { + HWND parent = ::GetParent(hwnd()); + if (!::IsWindow(parent)) { parent = ::GetWindow(hwnd(), GW_OWNER); } gfx::CenterAndSizeWindow(parent, hwnd(), size); @@ -714,7 +714,7 @@ placement.length = sizeof(WINDOWPLACEMENT); placement.showCmd = SW_SHOWMAXIMIZED; placement.rcNormalPosition = pixel_restore_bounds.ToRECT(); - SetWindowPlacement(hwnd(), &placement); + ::SetWindowPlacement(hwnd(), &placement); native_show_state = SW_SHOWMAXIMIZED; } else { const bool is_maximized_or_arranged = @@ -738,8 +738,8 @@ native_show_state = SW_SHOWMINIMIZED; break; case ui::mojom::WindowShowState::kNormal: - if ((GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_TRANSPARENT) || - (GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_NOACTIVATE)) { + if ((::GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_TRANSPARENT) || + (::GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_NOACTIVATE)) { native_show_state = is_maximized_or_arranged ? SW_SHOWNA : SW_SHOWNOACTIVATE; } else { @@ -756,7 +756,7 @@ break; } - ShowWindow(hwnd(), native_show_state); + ::ShowWindow(hwnd(), native_show_state); // When launched from certain programs like bash and Windows Live // Messenger, show_state is set to SW_HIDE, so we need to correct that // condition. We don't just change show_state to SW_SHOWNORMAL because @@ -765,7 +765,7 @@ // ignored (!!#@@#!). Instead, we call ShowWindow again in this case. if (native_show_state == SW_HIDE) { native_show_state = SW_SHOWNORMAL; - ShowWindow(hwnd(), native_show_state); + ::ShowWindow(hwnd(), native_show_state); } } @@ -784,14 +784,14 @@ } void HWNDMessageHandler::Hide() { - if (IsWindow(hwnd())) { + if (::IsWindow(hwnd())) { // NOTE: Be careful not to activate any windows here (for example, calling // ShowWindow(SW_HIDE) will automatically activate another window). This // code can be called while a window is being deactivated, and activating // another window will screw up the activation that is already in progress. - SetWindowPos(hwnd(), nullptr, 0, 0, 0, 0, - SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOMOVE | - SWP_NOREPOSITION | SWP_NOSIZE | SWP_NOZORDER); + ::SetWindowPos(hwnd(), nullptr, 0, 0, 0, 0, + SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOMOVE | + SWP_NOREPOSITION | SWP_NOSIZE | SWP_NOZORDER); } } @@ -823,7 +823,7 @@ } ::SetWindowPos(hwnd(), HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); - SetForegroundWindow(hwnd()); + ::SetForegroundWindow(hwnd()); } void HWNDMessageHandler::Deactivate() { @@ -863,7 +863,7 @@ } bool HWNDMessageHandler::IsAlwaysOnTop() const { - return (GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_TOPMOST) != 0; + return (::GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_TOPMOST) != 0; } bool HWNDMessageHandler::IsHeadless() const { @@ -872,15 +872,15 @@ bool HWNDMessageHandler::RunMoveLoop(const gfx::Vector2d& drag_offset, bool hide_on_escape) { - ReleaseCapture(); + ::ReleaseCapture(); MoveLoopMouseWatcher watcher(msg_handler_weak_factory_.GetWeakPtr(), hide_on_escape); // In Aura, we handle touch events asynchronously. So we need to allow nested // tasks while in windows move loop. base::CurrentThread::ScopedAllowApplicationTasksInNativeNestedLoop allow; - SendMessage(hwnd(), WM_SYSCOMMAND, SC_MOVE | 0x0002, - static_cast<LPARAM>(GetMessagePos())); + ::SendMessage(hwnd(), WM_SYSCOMMAND, SC_MOVE | 0x0002, + static_cast<LPARAM>(::GetMessagePos())); // Windows doesn't appear to offer a way to determine whether the user // canceled the move or not. We assume if the user released the mouse it was // successful. @@ -888,14 +888,14 @@ } void HWNDMessageHandler::EndMoveLoop() { - SendMessage(hwnd(), WM_CANCELMODE, 0, 0); + ::SendMessage(hwnd(), WM_CANCELMODE, 0, 0); } void HWNDMessageHandler::SendFrameChanged() { - SetWindowPos(hwnd(), nullptr, 0, 0, 0, 0, - SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOMOVE | - SWP_NOOWNERZORDER | SWP_NOREPOSITION | SWP_NOSENDCHANGING | - SWP_NOSIZE | SWP_NOZORDER); + ::SetWindowPos(hwnd(), nullptr, 0, 0, 0, 0, + SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOCOPYBITS | + SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOREPOSITION | + SWP_NOSENDCHANGING | SWP_NOSIZE | SWP_NOZORDER); } void HWNDMessageHandler::FlashFrame(bool flash) { @@ -909,7 +909,7 @@ } else { fwi.dwFlags = FLASHW_STOP; } - FlashWindowEx(&fwi); + ::FlashWindowEx(&fwi); } void HWNDMessageHandler::ClearNativeFocus() { @@ -945,23 +945,23 @@ void HWNDMessageHandler::SetVisibilityChangedAnimationsEnabled(bool enabled) { int dwm_value = enabled ? FALSE : TRUE; - DwmSetWindowAttribute(hwnd(), DWMWA_TRANSITIONS_FORCEDISABLED, &dwm_value, - sizeof(dwm_value)); + ::DwmSetWindowAttribute(hwnd(), DWMWA_TRANSITIONS_FORCEDISABLED, &dwm_value, + sizeof(dwm_value)); } bool HWNDMessageHandler::SetTitle(const std::u16string& title) { std::wstring current_title; - auto len_with_null = static_cast<size_t>(GetWindowTextLength(hwnd())) + 1; + auto len_with_null = static_cast<size_t>(::GetWindowTextLength(hwnd())) + 1; if (len_with_null == 1 && title.length() == 0) { return false; } if (len_with_null - 1 == title.length() && - GetWindowText(hwnd(), base::WriteInto(¤t_title, len_with_null), - len_with_null) && + ::GetWindowText(hwnd(), base::WriteInto(¤t_title, len_with_null), + static_cast<int>(len_with_null)) && current_title == base::AsWStringView(title)) { return false; } - SetWindowText(hwnd(), base::as_wcstr(title)); + ::SetWindowText(hwnd(), base::as_wcstr(title)); return true; } @@ -1000,25 +1000,25 @@ if (!window_icon.isNull()) { base::win::ScopedGDIObject<HICON> previous_icon = std::move(window_icon_); window_icon_ = IconUtil::CreateHICONFromSkBitmapSizedTo( - *window_icon.bitmap(), GetSystemMetrics(SM_CXSMICON), - GetSystemMetrics(SM_CYSMICON)); - SendMessage(hwnd(), WM_SETICON, ICON_SMALL, - reinterpret_cast<LPARAM>(window_icon_.get())); + *window_icon.bitmap(), ::GetSystemMetrics(SM_CXSMICON), + ::GetSystemMetrics(SM_CYSMICON)); + ::SendMessage(hwnd(), WM_SETICON, ICON_SMALL, + reinterpret_cast<LPARAM>(window_icon_.get())); } if (!app_icon.isNull()) { base::win::ScopedGDIObject<HICON> previous_icon = std::move(app_icon_); app_icon_ = IconUtil::CreateHICONFromSkBitmapSizedTo( - *app_icon.bitmap(), GetSystemMetrics(SM_CXICON), - GetSystemMetrics(SM_CYICON)); - SendMessage(hwnd(), WM_SETICON, ICON_BIG, - reinterpret_cast<LPARAM>(app_icon_.get())); + *app_icon.bitmap(), ::GetSystemMetrics(SM_CXICON), + ::GetSystemMetrics(SM_CYICON)); + ::SendMessage(hwnd(), WM_SETICON, ICON_BIG, + reinterpret_cast<LPARAM>(app_icon_.get())); } } void HWNDMessageHandler::SetFullscreen(bool fullscreen, int64_t target_display_id) { // Erase any prior reference to this window in the fullscreen window map. - HMONITOR monitor = MonitorFromWindow(hwnd(), MONITOR_DEFAULTTOPRIMARY); + HMONITOR monitor = ::MonitorFromWindow(hwnd(), MONITOR_DEFAULTTOPRIMARY); FullscreenWindowMonitorMap::iterator iter = fullscreen_monitor_map_.Get().find(monitor); if (iter != fullscreen_monitor_map_.Get().end()) { @@ -1034,7 +1034,8 @@ // Add an entry in the fullscreen window map if the window is now fullscreen. if (fullscreen) { - HMONITOR new_monitor = MonitorFromWindow(hwnd(), MONITOR_DEFAULTTOPRIMARY); + HMONITOR new_monitor = + ::MonitorFromWindow(hwnd(), MONITOR_DEFAULTTOPRIMARY); (fullscreen_monitor_map_.Get())[new_monitor] = this; } // If we are out of fullscreen and there was a pending DWM transition for the @@ -1055,7 +1056,7 @@ // When the aspect ratio is set, size the window to adhere to it. This keeps // the same origin point as the original window. RECT window_rect; - if (GetWindowRect(hwnd(), &window_rect)) { + if (::GetWindowRect(hwnd(), &window_rect)) { gfx::Rect rect(window_rect); SizeWindowToAspectRatio(WMSZ_BOTTOMRIGHT, &rect); @@ -1064,7 +1065,7 @@ } void HWNDMessageHandler::SizeConstraintsChanged() { - LONG style = GetWindowLong(hwnd(), GWL_STYLE); + LONG style = ::GetWindowLong(hwnd(), GWL_STYLE); // Key style considerations: // - WS_THICKFRAME: Enables resizing. Cannot be used with translucent @@ -1091,7 +1092,7 @@ set_style_func(WS_MAXIMIZEBOX, can_maximize); set_style_func(WS_MINIMIZEBOX, delegate_->CanMinimize()); - SetWindowLong(hwnd(), GWL_STYLE, style); + ::SetWindowLong(hwnd(), GWL_STYLE, style); SendFrameChanged(); } @@ -1178,7 +1179,7 @@ msg_handled_ = old_msg_handled; if (!processed) { - result = DefWindowProc(window, message, w_param, l_param); + result = ::DefWindowProc(window, message, w_param, l_param); // DefWindowProc() may have destroyed the window and/or us in a nested // message loop. if (!ref || !::IsWindow(window)) { @@ -1316,7 +1317,7 @@ void HWNDMessageHandler::ApplyPinchZoomScale(float scale) { POINT cursor_pos = GetCursorPos(); - ScreenToClient(hwnd(), &cursor_pos); + ::ScreenToClient(hwnd(), &cursor_pos); ui::GestureEventDetails event_details(ui::EventType::kGesturePinchUpdate); event_details.set_device_type(ui::GestureDeviceType::DEVICE_TOUCHPAD); @@ -1329,7 +1330,7 @@ void HWNDMessageHandler::ApplyPinchZoomBegin() { POINT cursor_pos = GetCursorPos(); - ScreenToClient(hwnd(), &cursor_pos); + ::ScreenToClient(hwnd(), &cursor_pos); ui::GestureEventDetails event_details(ui::EventType::kGesturePinchBegin); event_details.set_device_type(ui::GestureDeviceType::DEVICE_TOUCHPAD); @@ -1341,7 +1342,7 @@ void HWNDMessageHandler::ApplyPinchZoomEnd() { POINT cursor_pos = GetCursorPos(); - ScreenToClient(hwnd(), &cursor_pos); + ::ScreenToClient(hwnd(), &cursor_pos); ui::GestureEventDetails event_details(ui::EventType::kGesturePinchEnd); event_details.set_device_type(ui::GestureDeviceType::DEVICE_TOUCHPAD); @@ -1361,7 +1362,7 @@ POINT root_location = GetCursorPos(); POINT location = {root_location.x, root_location.y}; - ScreenToClient(hwnd(), &location); + ::ScreenToClient(hwnd(), &location); gfx::Point cursor_location(location); gfx::Point cursor_root_location(root_location); @@ -1472,19 +1473,19 @@ void HWNDMessageHandler::OnAppbarAutohideEdgesChanged() { // This triggers querying WM_NCCALCSIZE again. RECT client; - GetWindowRect(hwnd(), &client); + ::GetWindowRect(hwnd(), &client); // Add SWP_NOZORDER and SWP_NOACTIVATE flags to SetWindowPos to preserve the // correct Z-order after restarting maximized browsers. Without these flags, // SetWindowPos would always bring the current window to the top. - SetWindowPos(hwnd(), nullptr, client.left, client.top, - client.right - client.left, client.bottom - client.top, - SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOACTIVATE); + ::SetWindowPos(hwnd(), nullptr, client.left, client.top, + client.right - client.left, client.bottom - client.top, + SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOACTIVATE); } void HWNDMessageHandler::SetInitialFocus() { - if (!(GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_TRANSPARENT) && - !(GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_NOACTIVATE)) { + if (!(::GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_TRANSPARENT) && + !(::GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_NOACTIVATE)) { // The window does not get keyboard messages unless we focus it. // Headless windows don't get native focus, so just pretend we grabbed one. if (IsHeadless()) { @@ -1546,10 +1547,10 @@ // active is on the same monitor as the fullscreen window. if (!active) { if (IsFullscreen() && ::IsWindow(window_gaining_or_losing_activation)) { - HMONITOR active_window_monitor = MonitorFromWindow( + HMONITOR active_window_monitor = ::MonitorFromWindow( window_gaining_or_losing_activation, MONITOR_DEFAULTTOPRIMARY); HMONITOR fullscreen_window_monitor = - MonitorFromWindow(hwnd(), MONITOR_DEFAULTTOPRIMARY); + ::MonitorFromWindow(hwnd(), MONITOR_DEFAULTTOPRIMARY); if (active_window_monitor == fullscreen_window_monitor) { OnBackgroundFullscreen(); @@ -1559,8 +1560,8 @@ // Restore the bounds of the window to fullscreen. DCHECK(IsFullscreen()); MONITORINFO monitor_info = {sizeof(monitor_info)}; - GetMonitorInfo(MonitorFromWindow(hwnd(), MONITOR_DEFAULTTOPRIMARY), - &monitor_info); + ::GetMonitorInfo(::MonitorFromWindow(hwnd(), MONITOR_DEFAULTTOPRIMARY), + &monitor_info); SetBoundsInternal(gfx::Rect(monitor_info.rcMonitor), false); // Inform the taskbar that this window is now a fullscreen window so it go // behind the window in the Z-Order. The taskbar heuristics to detect @@ -1593,7 +1594,7 @@ void HWNDMessageHandler::ExecuteSystemMenuCommand(int command) { if (command) { - SendMessage(hwnd(), WM_SYSCOMMAND, static_cast<WPARAM>(command), 0); + ::SendMessage(hwnd(), WM_SYSCOMMAND, static_cast<WPARAM>(command), 0); } } @@ -1642,7 +1643,7 @@ bool HWNDMessageHandler::GetClientAreaInsets(gfx::Insets* insets, HMONITOR monitor) const { int frame_thickness = ui::GetResizableFrameThicknessFromMonitorInPixels( - monitor, GetWindowLong(hwnd(), GWL_STYLE) & WS_CAPTION); + monitor, ::GetWindowLong(hwnd(), GWL_STYLE) & WS_CAPTION); if (delegate_->GetClientAreaInsets(insets, frame_thickness)) { return true; } @@ -1689,17 +1690,17 @@ GetWindowRgn(hwnd(), current_rgn.get()); RECT window_rect; - GetWindowRect(hwnd(), &window_rect); + ::GetWindowRect(hwnd(), &window_rect); base::win::ScopedGDIObject<HRGN> new_region; if (custom_window_region_.is_valid()) { new_region.reset(CreateRectRgn(0, 0, 0, 0)); CombineRgn(new_region.get(), custom_window_region_.get(), nullptr, RGN_COPY); } else if (IsMaximized()) { - HMONITOR monitor = MonitorFromWindow(hwnd(), MONITOR_DEFAULTTONEAREST); + HMONITOR monitor = ::MonitorFromWindow(hwnd(), MONITOR_DEFAULTTONEAREST); MONITORINFO mi; mi.cbSize = sizeof mi; - GetMonitorInfo(monitor, &mi); + ::GetMonitorInfo(monitor, &mi); RECT work_rect = mi.rcWork; OffsetRect(&work_rect, static_cast<int>(-window_rect.left), static_cast<int>(-window_rect.top)); @@ -1730,7 +1731,7 @@ // The Widget and HWND can be destroyed in the call to DefWindowProc, so use // the WeakPtrFactory to avoid unlocking (and crashing) after destruction. base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr()); - LRESULT result = DefWindowProc(hwnd(), message, w_param, l_param); + LRESULT result = ::DefWindowProc(hwnd(), message, w_param, l_param); if (!ref) { lock.CancelUnlockOperation(); } @@ -1739,15 +1740,15 @@ void HWNDMessageHandler::LockUpdates() { if (++lock_updates_count_ == 1) { - SetWindowLong(hwnd(), GWL_STYLE, - GetWindowLong(hwnd(), GWL_STYLE) & ~WS_VISIBLE); + ::SetWindowLong(hwnd(), GWL_STYLE, + ::GetWindowLong(hwnd(), GWL_STYLE) & ~WS_VISIBLE); } } void HWNDMessageHandler::UnlockUpdates() { if (--lock_updates_count_ <= 0) { - SetWindowLong(hwnd(), GWL_STYLE, - GetWindowLong(hwnd(), GWL_STYLE) | WS_VISIBLE); + ::SetWindowLong(hwnd(), GWL_STYLE, + ::GetWindowLong(hwnd(), GWL_STYLE) | WS_VISIBLE); lock_updates_count_ = 0; } } @@ -1766,7 +1767,7 @@ base::Milliseconds(500)); return; } - InvalidateRect(hwnd(), nullptr, FALSE); + ::InvalidateRect(hwnd(), nullptr, FALSE); } bool HWNDMessageHandler::IsFrameSystemDrawn() const { @@ -1840,8 +1841,8 @@ if (base::win::GetVersion() >= base::win::Version::WIN11 && use_rounded_corner_) { DWM_WINDOW_CORNER_PREFERENCE corner_pref = DWMWCP_ROUND; - DwmSetWindowAttribute(hwnd(), DWMWA_WINDOW_CORNER_PREFERENCE, &corner_pref, - sizeof(corner_pref)); + ::DwmSetWindowAttribute(hwnd(), DWMWA_WINDOW_CORNER_PREFERENCE, + &corner_pref, sizeof(corner_pref)); } fullscreen_handler_->set_hwnd(hwnd()); @@ -1850,17 +1851,17 @@ // This message initializes the window so that focus border are shown for // windows. - SendMessage(hwnd(), WM_CHANGEUISTATE, MAKELPARAM(UIS_CLEAR, UISF_HIDEFOCUS), - 0); + ::SendMessage(hwnd(), WM_CHANGEUISTATE, MAKELPARAM(UIS_CLEAR, UISF_HIDEFOCUS), + 0); if (!delegate_->HasFrame()) { - SetWindowLong(hwnd(), GWL_STYLE, - GetWindowLong(hwnd(), GWL_STYLE) & ~WS_CAPTION); + ::SetWindowLong(hwnd(), GWL_STYLE, + ::GetWindowLong(hwnd(), GWL_STYLE) & ~WS_CAPTION); SendFrameChanged(); } // Get access to a modifiable copy of the system menu. - GetSystemMenu(hwnd(), false); + ::GetSystemMenu(hwnd(), false); // We need to allow the delegate to size its contents since the window may not // receive a size notification when its initial bounds are specified at window @@ -2029,7 +2030,7 @@ // minimize/maximize/close buttons. needs_dwm_frame_clear_ = false; RECT client_rect; - GetClientRect(hwnd(), &client_rect); + ::GetClientRect(hwnd(), &client_rect); base::win::ScopedGDIObject<HBRUSH> brush(CreateSolidBrush(0)); // The DC and GetClientRect operate in client area coordinates. RECT rect = {0, 0, client_rect.right, insets.top()}; @@ -2066,8 +2067,8 @@ // view reports its size as the client size. if (delegate_->WidgetSizeIsClientSize()) { RECT client_rect, window_rect; - GetClientRect(hwnd(), &client_rect); - GetWindowRect(hwnd(), &window_rect); + ::GetClientRect(hwnd(), &client_rect); + ::GetWindowRect(hwnd(), &window_rect); CR_DEFLATE_RECT(&window_rect, &client_rect); min_window_size.Enlarge(window_rect.right - window_rect.left, window_rect.bottom - window_rect.top); @@ -2083,10 +2084,10 @@ minmax_info->ptMinTrackSize.y = min_window_size.height(); if (max_window_size.width() || max_window_size.height()) { if (!max_window_size.width()) { - max_window_size.set_width(GetSystemMetrics(SM_CXMAXTRACK)); + max_window_size.set_width(::GetSystemMetrics(SM_CXMAXTRACK)); } if (!max_window_size.height()) { - max_window_size.set_height(GetSystemMetrics(SM_CYMAXTRACK)); + max_window_size.set_height(::GetSystemMetrics(SM_CYMAXTRACK)); } minmax_info->ptMaxTrackSize.x = max_window_size.width(); minmax_info->ptMaxTrackSize.y = max_window_size.height(); @@ -2244,7 +2245,7 @@ } return MA_NOACTIVATEANDEAT; } - if (GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_NOACTIVATE) { + if (::GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_NOACTIVATE) { return MA_NOACTIVATE; } SetMsgHandled(FALSE); @@ -2352,7 +2353,7 @@ POINT cursor_pos = {0}; ::GetCursorPos(&cursor_pos); - ScreenToClient(hwnd(), &cursor_pos); + ::ScreenToClient(hwnd(), &cursor_pos); ui::MouseEvent event( ui::EventType::kMouseMoved, gfx::PointF(cursor_pos.x, cursor_pos.y), gfx::PointF(cursor_pos.x, cursor_pos.y), ui::EventTimeForNow(), @@ -2441,7 +2442,7 @@ // See http://code.google.com/p/chromium/issues/detail?id=900 if (is_first_nccalc_) { is_first_nccalc_ = false; - if (GetWindowLong(hwnd(), GWL_STYLE) & WS_CAPTION) { + if (::GetWindowLong(hwnd(), GWL_STYLE) & WS_CAPTION) { SetMsgHandled(FALSE); return 0; } @@ -2451,7 +2452,7 @@ mode ? &(reinterpret_cast<NCCALCSIZE_PARAMS*>(l_param)->rgrc[0]) : reinterpret_cast<RECT*>(l_param); - HMONITOR monitor = MonitorFromWindow(hwnd(), MONITOR_DEFAULTTONULL); + HMONITOR monitor = ::MonitorFromWindow(hwnd(), MONITOR_DEFAULTTONULL); if (!monitor) { // We might end up here if the window was previously minimized and the // user clicks on the taskbar button to restore it in the previous @@ -2586,14 +2587,14 @@ // Otherwise, we let Windows do all the native frame non-client handling for // us. - LRESULT hit_test_code = - DefWindowProc(hwnd(), WM_NCHITTEST, 0, MAKELPARAM(point.x(), point.y())); + LRESULT hit_test_code = ::DefWindowProc(hwnd(), WM_NCHITTEST, 0, + MAKELPARAM(point.x(), point.y())); return hit_test_code; } void HWNDMessageHandler::OnNCPaint(HRGN rgn) { RECT window_rect; - GetWindowRect(hwnd(), &window_rect); + ::GetWindowRect(hwnd(), &window_rect); RECT dirty_region; // A value of 1 indicates paint all. if (!rgn || rgn == reinterpret_cast<HRGN>(1)) { @@ -2721,7 +2722,7 @@ // isn't needed if we've just cleared the whole client area outside the // child window above. RECT cr; - if (GetClientRect(hwnd(), &cr)) { + if (::GetClientRect(hwnd(), &cr)) { // GetClientRect() always returns a rect with top/left at 0. const gfx::Size client_area = gfx::Rect(cr).size(); @@ -2841,7 +2842,7 @@ } void HWNDMessageHandler::OnSettingChange(UINT flags, const wchar_t* section) { - if (!GetParent(hwnd()) && (flags == SPI_SETWORKAREA)) { + if (!::GetParent(hwnd()) && (flags == SPI_SETWORKAREA)) { // Fire a dummy SetWindowPos() call, so we'll trip the code in // OnWindowPosChanging() below that notices work area changes. ::SetWindowPos(hwnd(), nullptr, 0, 0, 0, 0, @@ -2930,8 +2931,8 @@ if (window_bounds_change && !IsVisible()) { // Circumvent ScopedRedrawLocks and force visibility before entering a // resize or move modal loop to get continuous sizing/moving feedback. - SetWindowLong(hwnd(), GWL_STYLE, - GetWindowLong(hwnd(), GWL_STYLE) | WS_VISIBLE); + ::SetWindowLong(hwnd(), GWL_STYLE, + ::GetWindowLong(hwnd(), GWL_STYLE) | WS_VISIBLE); } } @@ -2978,8 +2979,8 @@ // situation. base::CurrentThread::ScopedAllowApplicationTasksInNativeNestedLoop allow; // If the delegate can't handle it, the system implementation will be called. - DefWindowProc(hwnd(), WM_SYSCOMMAND, notification_code, - MAKELPARAM(point.x(), point.y())); + ::DefWindowProc(hwnd(), WM_SYSCOMMAND, notification_code, + MAKELPARAM(point.x(), point.y())); if (is_mouse_menu && ref) { handling_mouse_menu_ = false; } @@ -3021,7 +3022,7 @@ window_pos->flags &= static_cast<unsigned int>(~(SWP_SHOWWINDOW | SWP_HIDEWINDOW)); } - } else if (!GetParent(hwnd())) { + } else if (!::GetParent(hwnd())) { RECT window_rect; const bool have_new_window_rect = !(window_pos->flags & SWP_NOMOVE) && !(window_pos->flags & SWP_NOSIZE); @@ -3039,7 +3040,7 @@ HMONITOR monitor; gfx::Rect monitor_rect, work_area; - if ((have_new_window_rect || GetWindowRect(hwnd(), &window_rect)) && + if ((have_new_window_rect || ::GetWindowRect(hwnd(), &window_rect)) && GetMonitorAndRects(window_rect, &monitor, &monitor_rect, &work_area)) { bool work_area_changed = (monitor_rect == last_monitor_rect_) && (work_area != last_work_area_); @@ -3128,7 +3129,7 @@ RECT window_rect; gfx::Size old_size; - if (GetWindowRect(hwnd(), &window_rect)) { + if (::GetWindowRect(hwnd(), &window_rect)) { old_size = gfx::Rect(window_rect).size(); } gfx::Size new_size = gfx::Size(window_pos->cx, window_pos->cy); @@ -3144,8 +3145,8 @@ // It's possible that if Aero snap is being entered then the window size // won't actually change. Post a message to ensure swaps will be re-enabled // in that case. - PostMessage(hwnd(), WM_WINDOWSIZINGFINISHED, ++current_window_size_message_, - 0); + ::PostMessage(hwnd(), WM_WINDOWSIZINGFINISHED, + ++current_window_size_message_, 0); // Copying the old bits can sometimes cause a flash of black when // resizing. See https://crbug.com/739724 if (is_translucent_) { @@ -3255,7 +3256,7 @@ // messages if it thinks the touch point is in non-client space. if (message != WM_MOUSEWHEEL && message != WM_MOUSEHWHEEL && ui::IsMouseEventFromTouch(message)) { - LRESULT hittest = SendMessage(hwnd(), WM_NCHITTEST, 0, l_param); + LRESULT hittest = ::SendMessage(hwnd(), WM_NCHITTEST, 0, l_param); // Always DefWindowProc on the titlebar. We could let the event fall through // and the special handling in HandleMouseInputForCaption would take care of // this, but in the touch case Windows does a better job. @@ -3294,12 +3295,12 @@ // TODO(pkasting): Maybe handle this in DesktopWindowTreeHostWin, where we // handle alt-space, or in the frame itself. is_right_mouse_pressed_on_caption_ = false; - ReleaseCapture(); + ::ReleaseCapture(); // |point| is in window coordinates, but WM_NCHITTEST and TrackPopupMenu() // expect screen coordinates. POINT screen_point = CR_POINT_INITIALIZER_FROM_LPARAM(l_param); MapWindowPoints(hwnd(), HWND_DESKTOP, &screen_point, 1); - w_param = static_cast<WPARAM>(SendMessage( + w_param = static_cast<WPARAM>(::SendMessage( hwnd(), WM_NCHITTEST, 0, MAKELPARAM(screen_point.x, screen_point.y))); if (w_param == HTCAPTION || w_param == HTSYSMENU) { ShowSystemMenuAtScreenPixelLocation(hwnd(), gfx::Point(screen_point)); @@ -3520,7 +3521,7 @@ } POINT client_point = pointer_info.ptPixelLocationRaw; - ScreenToClient(hwnd(), &client_point); + ::ScreenToClient(hwnd(), &client_point); gfx::Point touch_point = gfx::Point(client_point.x, client_point.y); ui::EventType event_type = GetTouchEventType(pointer_flags); const base::TimeTicks event_time = ui::EventTimeForNow(); @@ -3575,7 +3576,7 @@ // Messages on HTCAPTION should be DefWindowProc'ed, as we let Windows // take care of dragging the window and double-tapping to maximize. const bool on_titlebar = - SendMessage(hwnd(), WM_NCHITTEST, 0, l_param) == HTCAPTION; + ::SendMessage(hwnd(), WM_NCHITTEST, 0, l_param) == HTCAPTION; // Unlike above, we must mark both WM_POINTERUP and WM_NCPOINTERUP as // handled, in order for the custom caption buttons to work correctly. if (event_type == ui::EventType::kTouchReleased && !on_titlebar) { @@ -3592,7 +3593,7 @@ UINT32 pointer_id, POINTER_PEN_INFO pointer_pen_info) { POINT client_point = pointer_pen_info.pointerInfo.ptPixelLocationRaw; - ScreenToClient(hwnd(), &client_point); + ::ScreenToClient(hwnd(), &client_point); gfx::Point point = gfx::Point(client_point.x, client_point.y); std::unique_ptr<ui::Event> event = pen_processor_.GenerateEvent( @@ -3769,7 +3770,7 @@ if (delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN) { DefWindowProcWithRedrawLock(WM_NCLBUTTONDOWN, HTCAPTION, l_param); } else { - DefWindowProc(hwnd(), WM_NCLBUTTONDOWN, HTCAPTION, l_param); + ::DefWindowProc(hwnd(), WM_NCLBUTTONDOWN, HTCAPTION, l_param); } } break; @@ -3795,9 +3796,9 @@ bool force_size_changed) { gfx::Size old_size = GetClientAreaBounds().size(); - SetWindowPos(hwnd(), nullptr, bounds_in_pixels.x(), bounds_in_pixels.y(), - bounds_in_pixels.width(), bounds_in_pixels.height(), - SWP_NOACTIVATE | SWP_NOZORDER); + ::SetWindowPos(hwnd(), nullptr, bounds_in_pixels.x(), bounds_in_pixels.y(), + bounds_in_pixels.width(), bounds_in_pixels.height(), + SWP_NOACTIVATE | SWP_NOZORDER); // If HWND size is not changed, we will not receive standard size change // notifications. If |force_size_changed| is |true|, we should pretend size is @@ -3815,7 +3816,7 @@ void HWNDMessageHandler::CheckAndHandleBackgroundFullscreenOnMonitor( HWND window) { - HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTOPRIMARY); + HMONITOR monitor = ::MonitorFromWindow(window, MONITOR_DEFAULTTOPRIMARY); FullscreenWindowMonitorMap::iterator iter = fullscreen_monitor_map_.Get().find(monitor); @@ -3832,8 +3833,8 @@ // Reduce the bounds of the window by 1px to ensure that Windows does // not treat this like a fullscreen window. MONITORINFO monitor_info = {sizeof(monitor_info)}; - GetMonitorInfo(MonitorFromWindow(hwnd(), MONITOR_DEFAULTTOPRIMARY), - &monitor_info); + ::GetMonitorInfo(::MonitorFromWindow(hwnd(), MONITOR_DEFAULTTOPRIMARY), + &monitor_info); gfx::Rect shrunk_rect(monitor_info.rcMonitor); shrunk_rect.set_height(shrunk_rect.height() - 1); background_fullscreen_hack_ = true; @@ -3890,7 +3891,7 @@ } void HWNDMessageHandler::UpdateFullscreenMonitorMap() { - HMONITOR hmonitor = MonitorFromWindow(hwnd(), MONITOR_DEFAULTTONULL); + HMONITOR hmonitor = ::MonitorFromWindow(hwnd(), MONITOR_DEFAULTTONULL); if (!hmonitor) { // A null `hmonitor` indicates that the monitor where the current window // resides has been disconnected. Remove the HMONITOR corresponding to the @@ -3942,13 +3943,13 @@ DCHECK(monitor); DCHECK(monitor_rect); DCHECK(work_area); - *monitor = MonitorFromRect(&rect, MONITOR_DEFAULTTONULL); + *monitor = ::MonitorFromRect(&rect, MONITOR_DEFAULTTONULL); if (!*monitor) { return false; } MONITORINFO monitor_info = {0}; monitor_info.cbSize = sizeof(monitor_info); - GetMonitorInfo(*monitor, &monitor_info); + ::GetMonitorInfo(*monitor, &monitor_info); *monitor_rect = gfx::Rect(monitor_info.rcMonitor); *work_area = gfx::Rect(monitor_info.rcWork); return true;
diff --git a/ui/views/win/scoped_fullscreen_visibility.cc b/ui/views/win/scoped_fullscreen_visibility.cc index 479a809..10b01508 100644 --- a/ui/views/win/scoped_fullscreen_visibility.cc +++ b/ui/views/win/scoped_fullscreen_visibility.cc
@@ -27,9 +27,9 @@ // ShowWindow(SW_HIDE) will automatically activate another window). This // code can be called while a window is being deactivated, and activating // another window will screw up the activation that is already in progress. - SetWindowPos(hwnd_, nullptr, 0, 0, 0, 0, - SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOMOVE | - SWP_NOREPOSITION | SWP_NOSIZE | SWP_NOZORDER); + ::SetWindowPos(hwnd_, nullptr, 0, 0, 0, 0, + SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOMOVE | + SWP_NOREPOSITION | SWP_NOSIZE | SWP_NOZORDER); } } @@ -38,7 +38,7 @@ CHECK(it != full_screen_windows_->end()); if (--it->second == 0) { full_screen_windows_->erase(it); - ShowWindow(hwnd_, SW_SHOW); + ::ShowWindow(hwnd_, SW_SHOW); } if (full_screen_windows_->empty()) { delete full_screen_windows_;
diff --git a/ui/webui/resources/cr_components/composebox/composebox.ts b/ui/webui/resources/cr_components/composebox/composebox.ts index 8b82ea8..96a7b2f8 100644 --- a/ui/webui/resources/cr_components/composebox/composebox.ts +++ b/ui/webui/resources/cr_components/composebox/composebox.ts
@@ -261,6 +261,7 @@ reflect: true, }, isFollowupQuery: {type: Boolean}, + shouldShowGhostFiles: {type: Boolean}, }; } @@ -285,6 +286,12 @@ accessor disableVoiceSearchAnimation: boolean = false; protected accessor tabSuggestions_: TabInfo[] = []; accessor lensButtonDisabled: boolean = false; + // Set this to true in parent component if it is desired + // to show files that are not in the file map when + // file status is updated from backend. Ghost files will be + // shown as image chip with spinner in file carousel. + accessor shouldShowGhostFiles: boolean = false; + protected composeboxNoFlickerSuggestionsFix_: boolean = loadTimeData.getBoolean('composeboxNoFlickerSuggestionsFix'); // If isCollapsible is set to true, the composebox will be a pill shape until @@ -737,6 +744,11 @@ this.updateInputPlaceholder_(); } + protected addToPendingUploads_(token: UnguessableToken) { + this.pendingUploads_.add(token); + this.fileUploadsComplete = false; + } + protected computeCancelButtonTitle_() { return this.input_.trim().length > 0 || this.files_.size > 0 ? this.i18n('composeboxCancelButtonTitleInput') : @@ -909,6 +921,9 @@ ComposeboxContextAddedMethod.CONTEXT_MENU, this.composeboxSource_); } + // Start file upload flow from frontend. This contrasts with + // `onFileContextAdded_`, which is for the file upload flow that is started + // from the backend. private async addFileContext_(files: File[]) { const composeboxFiles: Map<UnguessableToken, ComposeboxFile> = new Map(); for (const file of files) { @@ -1753,8 +1768,7 @@ // `NotUploaded`, `UploadStarted` come before and after `kProcessing` // respectively, so we only need to add to `pendingUploads_` when in a // type of processing state. - this.pendingUploads_.add(file.uuid); - this.fileUploadsComplete = this.pendingUploads_.size === 0; + this.addToPendingUploads_(file.uuid); } // Fetch contextual suggestions for processingSuggestSignalsReady @@ -1866,6 +1880,10 @@ return this.input_; } + getNumOfFilesForTesting(): number { + return this.files_.size; + } + private selectFirstMatch() { if (this.result_?.matches.length) { this.$.matches.selectFirst(); @@ -1908,10 +1926,15 @@ } } + // This function is called when backend starts a file upload flow, whether + // through `addFileFromAttachment_`, `addFileContextFromBrowser`, etc. This + // contrasts with the workflows where the frontend starts a file upload flow + // (`addFileContext_`). private onFileContextAdded_(file: ComposeboxFile) { const newFiles = new Map(this.files_); newFiles.set(file.uuid, file); this.files_ = newFiles; + this.addToPendingUploads_(file.uuid); } private handleProcessFilesError_(error: ProcessFilesError) { @@ -2008,20 +2031,25 @@ } this.files_ = new Map([...this.files_]); } else { - // File is unknown but its status is known. Add it to file carousel - // while we wait for its file details to be known. - if (this.entrypointName === 'Omnibox') { + // File is unknown but its status is known. Show this if + // ghost/unknown files in frontend are allowed to be in + // carousel. + if (this.shouldShowGhostFiles) { file = { uuid: token, name: '', objectUrl: null, dataUrl: null, type: '', - status: status, + // Override this since first upload status is this or processing. + // Need this or processing in order to show tab spinner. + status: FileUploadStatus.kUploadStarted, url: null, tabId: null, isDeletable: true, }; + // Update pending uploads in 'composebox.ts' to disable + // submit button. this.onFileContextAdded_(file); } }
diff --git a/ui/webui/resources/cr_elements/BUILD.gn b/ui/webui/resources/cr_elements/BUILD.gn index cd766aa..87d5db6 100644 --- a/ui/webui/resources/cr_elements/BUILD.gn +++ b/ui/webui/resources/cr_elements/BUILD.gn
@@ -88,8 +88,7 @@ } if ((!is_android && !is_ios) || is_desktop_android) { - web_component_files += [ "cr_a11y_announcer/cr_a11y_announcer.ts" ] - + static_files = [ "cr_a11y_announcer/cr_a11y_announcer.css" ] ts_files += [ "cr_radio_button/cr_radio_button_mixin_lit.ts", "cr_search_field/cr_search_field_mixin_lit.ts", @@ -99,6 +98,8 @@ # Web components files that either # - don't have a corresponding .html file or # - have a checked-in *.html.ts wrapper file instead of auto-generated + "cr_a11y_announcer/cr_a11y_announcer.html.ts", + "cr_a11y_announcer/cr_a11y_announcer.ts", "cr_collapse/cr_collapse.html.ts", "cr_collapse/cr_collapse.ts", "cr_drawer/cr_drawer.html.ts",
diff --git a/ui/webui/resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.css b/ui/webui/resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.css new file mode 100644 index 0000000..de42a14 --- /dev/null +++ b/ui/webui/resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.css
@@ -0,0 +1,11 @@ +/* Copyright 2026 The Chromium Authors + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +:host { + clip: rect(0 0 0 0); + height: 1px; + overflow: hidden; + position: fixed; + width: 1px; +}
diff --git a/ui/webui/resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.html b/ui/webui/resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.html deleted file mode 100644 index b0a892d..0000000 --- a/ui/webui/resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.html +++ /dev/null
@@ -1,12 +0,0 @@ -<style> - :host { - clip: rect(0 0 0 0); - height: 1px; - overflow: hidden; - position: fixed; - width: 1px; - } -</style> - -<div id="messages" role="alert" aria-live="polite" aria-relevant="additions"> -</div>
diff --git a/ui/webui/resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.html.ts b/ui/webui/resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.html.ts new file mode 100644 index 0000000..cacc0a6a --- /dev/null +++ b/ui/webui/resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.html.ts
@@ -0,0 +1,11 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import {getTrustedHTML} from '//resources/js/static_types.js'; + +export function getTemplate() { + return getTrustedHTML` +<div id="messages" role="alert" aria-live="polite" aria-relevant="additions"> +</div>`; +}
diff --git a/ui/webui/resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.ts b/ui/webui/resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.ts index 723cbc5..7cd09b1 100644 --- a/ui/webui/resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.ts +++ b/ui/webui/resources/cr_elements/cr_a11y_announcer/cr_a11y_announcer.ts
@@ -5,6 +5,7 @@ import {assert} from '//resources/js/assert.js'; import {CustomElement} from '//resources/js/custom_element.js'; +import sheet from './cr_a11y_announcer.css' with {type : 'css'}; import {getTemplate} from './cr_a11y_announcer.html.js'; /** @@ -62,6 +63,11 @@ private currentTimeout_: number|null = null; private messages_: string[] = []; + constructor() { + super(); + this.shadowRoot!.adoptedStyleSheets = [sheet]; + } + disconnectedCallback() { if (this.currentTimeout_ !== null) { clearTimeout(this.currentTimeout_);
diff --git a/ui/webui/resources/cr_elements/cr_icon/cr_iconset.html.ts b/ui/webui/resources/cr_elements/cr_icon/cr_iconset.html.ts index b24f1db..acdac3a 100644 --- a/ui/webui/resources/cr_elements/cr_icon/cr_iconset.html.ts +++ b/ui/webui/resources/cr_elements/cr_icon/cr_iconset.html.ts
@@ -10,8 +10,7 @@ return html` <svg id="baseSvg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${this.size} ${this.size}" - preserveAspectRatio="xMidYMid meet" focusable="false" - style="pointer-events: none; display: block; width: 100%; height: 100%;"> + preserveAspectRatio="xMidYMid meet" focusable="false"> </svg> <slot></slot> `;
diff --git a/ui/webui/resources/cr_elements/cr_icon/cr_iconset.ts b/ui/webui/resources/cr_elements/cr_icon/cr_iconset.ts index 48c962b..271d9395 100644 --- a/ui/webui/resources/cr_elements/cr_icon/cr_iconset.ts +++ b/ui/webui/resources/cr_elements/cr_icon/cr_iconset.ts
@@ -123,6 +123,10 @@ if (contentViewBox) { svgClone.setAttribute('viewBox', contentViewBox); } + svgClone.style.display = 'block'; + svgClone.style.height = '100%'; + svgClone.style.width = '100%'; + svgClone.style.pointerEvents = 'none'; svgClone.appendChild(content); return svgClone; }
diff --git a/ui/webui/resources/tools/bundle_js.py b/ui/webui/resources/tools/bundle_js.py index a935a1d..68841782 100755 --- a/ui/webui/resources/tools/bundle_js.py +++ b/ui/webui/resources/tools/bundle_js.py
@@ -174,6 +174,8 @@ '[name].rollup.js', '--sourcemap', '--sourcemapExcludeSources', + '--importAttributesKey', + 'with', '--config', rollup_config_file, ])
diff --git a/ui/webui/resources/tools/bundle_js_test.py b/ui/webui/resources/tools/bundle_js_test.py index 7adc745..744d068f 100755 --- a/ui/webui/resources/tools/bundle_js_test.py +++ b/ui/webui/resources/tools/bundle_js_test.py
@@ -241,6 +241,20 @@ depfile_d = self._read_out_file('depfile.d') self._check_dep_file(['src/bar.js'], depfile_d) + def testSimpleBundleExcludesCss(self): + args = [ + '--host', + 'fake-host', + '--js_module_in_files', + 'foo_with_css.js', + ] + self._run_bundle(args) + + self._check_bundle_output('foo_with_css_excludes_css.rollup.js', + 'foo_with_css.rollup.js') + depfile_d = self._read_out_file('depfile.d') + self._check_dep_file(['src/foo.js'], depfile_d) + self.assertNotIn('src/foo.css', depfile_d) if __name__ == '__main__': unittest.main()
diff --git a/ui/webui/resources/tools/rollup_plugin.mjs b/ui/webui/resources/tools/rollup_plugin.mjs index 1362a0a8..169a0d1e 100644 --- a/ui/webui/resources/tools/rollup_plugin.mjs +++ b/ui/webui/resources/tools/rollup_plugin.mjs
@@ -40,12 +40,13 @@ * @param {string} urlSrcPath The path that corresponds to the URL prefix. * @param {!Array<string>} excludes List of paths that should be excluded from * bundling. + * @param {boolean} isCss Whether the file is a CSS file. * @return {string} The path to |source|. If |source| does not map to * |urlSrcPath|, returns an empty string. If |source| maps to a location * in |urlSrcPath| but is listed in |excludes|, returns the URL * corresponding to |source|. Otherwise, returns the full path for |source|. */ -function getPathForUrl(source, origin, urlPrefix, urlSrcPath, excludes) { +function getPathForUrl(source, origin, urlPrefix, urlSrcPath, excludes, isCss) { if (source === urlPrefix) { // Handle case where 'urlPrefix` matches the entire `source` URL and // therefore is not just a prefix, but a complete URL. @@ -74,7 +75,8 @@ return ''; } - if (excludes.includes(urlPrefix + pathFromUrl) || + if (isCss || + excludes.includes(urlPrefix + pathFromUrl) || excludes.includes(schemeRelativeUrl + pathFromUrl)) { return urlPrefix + pathFromUrl; } @@ -116,13 +118,16 @@ `Invalid path (missing file extension) was found: ${source}`); } + const isCss = path.extname(source) === '.css'; + // Normalize origin paths to use forward slashes. if (origin) { origin = normalizeSlashes(origin); } for (const [url, path] of urlsToPaths) { - const resultPath = getPathForUrl(source, origin, url, path, excludes); + const resultPath = + getPathForUrl(source, origin, url, path, excludes, isCss); if (resultPath.includes('://') || resultPath.startsWith('//')) { return {id: resultPath, external: 'absolute'}; } else if (resultPath) { @@ -132,7 +137,7 @@ // Check if the URL is an external path that isn't mapped. if (source.includes('://') || source.startsWith('//')) { - if (excludes.includes(source)) { + if (isCss || excludes.includes(source)) { return {id: source, external: 'absolute'}; } else { this.error(`Invalid absolute path: ${source} is not in |excludes| ` + @@ -149,7 +154,7 @@ combinePaths(origin, source); if (fullSourcePath.startsWith(rootPath)) { const pathFromRoot = relativePath(rootPath, fullSourcePath); - if (excludes.includes(pathFromRoot)) { + if (isCss || excludes.includes(pathFromRoot)) { const url = new URL(pathFromRoot, hostUrl); return source.startsWith('/') ? {id: url.href, external: 'absolute'} :
diff --git a/ui/webui/resources/tools/tests/bundle_js/expected/foo_with_css_excludes_css.rollup.js b/ui/webui/resources/tools/tests/bundle_js/expected/foo_with_css_excludes_css.rollup.js new file mode 100644 index 0000000..2e891cf --- /dev/null +++ b/ui/webui/resources/tools/tests/bundle_js/expected/foo_with_css_excludes_css.rollup.js
@@ -0,0 +1,40 @@ +import sheet from './foo.css' with { type: 'css' }; + +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +alert('Hello from resources/foo_resource.js'); + +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +alert('Hello from external/bar/bar.js'); + +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +alert('Hello from external/foo/foo.js'); + +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +alert('Hello from external/baz/baz.js'); + +// Copyright 2023 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + + +alert('Hello from src/foo.js'); + +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + + +document.getElementById('foo').shadowRoot.adoptedStyleSheets = [sheet]; +//# sourceMappingURL=foo_with_css.rollup.js.map
diff --git a/ui/webui/resources/tools/tests/bundle_js/src/foo_with_css.js b/ui/webui/resources/tools/tests/bundle_js/src/foo_with_css.js new file mode 100644 index 0000000..19edfbb6 --- /dev/null +++ b/ui/webui/resources/tools/tests/bundle_js/src/foo_with_css.js
@@ -0,0 +1,9 @@ +// Copyright 2026 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import './foo.js'; + +import sheet from './foo.css' with {type : 'css'}; + +document.getElementById('foo').shadowRoot.adoptedStyleSheets = [sheet];
diff --git a/ui/webui/webui_util.cc b/ui/webui/webui_util.cc index 9b9876ea..1cc3ea3 100644 --- a/ui/webui/webui_util.cc +++ b/ui/webui/webui_util.cc
@@ -65,8 +65,10 @@ source->OverrideContentSecurityPolicy( network::mojom::CSPDirectiveName::FontSrc, base::StringPrintf("font-src %s://resources 'self';", scheme.c_str())); - // unsafe-inline is required for Polymer. Allow styles to be imported from - // //resources and //theme. + // unsafe-inline is required for Polymer and for CSS shims, which + // require <style> tags directly in the main .html file. Also + // required by some Lit elements that set style= in HTML. + // Allow styles to be imported from //resources and //theme. source->OverrideContentSecurityPolicy( network::mojom::CSPDirectiveName::StyleSrc, base::StringPrintf(